TaskManager Live Monitor
Cloudflare Pages의 TaskManager 화면은 임의 명령 작성 도구가 아니라 실기기 TaskManager 플로우 검증 콘솔이다.
Dashboard:
Worker endpoint:
https://frosty-bird-d72c.silogood.workers.dev
wss://frosty-bird-d72c.silogood.workers.dev/ws?role=ui&deviceId=A1-board-001
wss://frosty-bird-d72c.silogood.workers.dev/ws?role=device&deviceId=A1-board-001
명령 등록:
POST https://frosty-bird-d72c.silogood.workers.dev/api/commands?deviceId=A1-board-001
DeviceAgent polling:
GET https://frosty-bird-d72c.silogood.workers.dev/api/device/commands?deviceId=A1-board-001
Telemetry:
POST https://frosty-bird-d72c.silogood.workers.dev/api/events
UI 범위
Dashboard의 사용 흐름은 세 단계로 고정한다.
- 테스트 프로그램 선택
- 테스트 실행
- 결과 관측
사용자는 method, queue, payload를 직접 조립하지 않는다. 화면은 미리 정의한 테스트 프로그램만 Worker 명령 큐에 등록한다. DeviceAgent는 polling으로 명령을 가져가고, TaskMonitorCommandBridge가 TaskManager submitTask 또는 workflow submit 경로로 넘긴다.
복합 workflow의 화면 흐름은 UI 하드코딩 목록이 아니라 command payload의 subTasks에서 만든다. Worker는 command_request audit에 payload를 보존하고, DeviceAgent ack가 payload를 생략해도 기존 subTasks를 유지한다. Dashboard는 이 subTasks 정의와 TaskManager의 WORKFLOW_STEP_STARTED/WORKFLOW_STEP_COMPLETED 이벤트를 currentStepIndex로 매칭해서 단계별 처리 상태를 보여준다.
현재 테스트 프로그램:
| 표시명 | 목적 | 대표 경로 |
|---|---|---|
| 안방 청정 → 거실 청정 → 복귀 | 순차 workflow가 단계별로 관리되는지 확인 | submitWorkflow → TaskManager workflow |
| 진행 중 인터럽트 | workflow 실행 중 취소/복귀 제어 명령이 중간에 인입되는지 확인 | submitWorkflow → cancelQueue → returnToStation |
| 정상 흐름 | 단일 명령이 등록, 실행, 완료되는지 확인 | setEyeLedColor → routine queue |
| 동시 인입 | 여러 명령을 연속 등록했을 때 큐와 상태가 깨지지 않는지 확인 | routine/movement queue |
| 큐 압박 | 같은 queue에 명령을 몰아 넣었을 때 상태 관리가 되는지 확인 | movement queue |
| 인터랙션 스케줄 등록 | 예약 데이터가 schedule queue를 통해 DeviceAgent 스케줄 저장 경로로 들어가는지 확인 | scheduleiot(command=add) → schedule queue |
| 인터랙션 스케줄 실행 | 등록된 인터랙션 스케줄 수행이 TaskManager interaction queue와 완료 이벤트를 타는지 확인 | interSchedule(action=1) → InterScheduleManager |
용어 설명
Dashboard와 테스트 문서에서는 영문 테스트 용어 대신 아래 한글 표현을 기본으로 쓴다. 내부 API 값은 호환성을 위해 happy, burst, pressure, failure 같은 원본 키를 유지한다.
| 표시명 | 내부 값 | 의미 |
|---|---|---|
| 정상 흐름 | happy |
명령 1개가 TaskManager에 등록되고 실행, 완료까지 가는 기본 성공 케이스다. |
| 동시 인입 | burst |
여러 출처나 성격의 명령이 짧은 시간에 들어와도 큐와 상태 관리가 깨지지 않는지 보는 케이스다. |
| 큐 압박 | pressure |
같은 큐에 여러 태스크를 몰아 넣어 대기/실행 상태와 이상 감지 기준을 확인하는 케이스다. |
| 실패 흐름 | failure |
차단되거나 실패한 명령이 요약, 타임라인, 명령 요청 기록에 남는지 확인하는 케이스다. |
| 진행 중 인터럽트 | interrupt_return_to_station |
실행 중인 workflow에 cancelQueue와 returnToStation을 중간 명령으로 넣어 TaskManager 제어 흐름과 모니터 표시를 확인하는 케이스다. |
| 실기기 검증 | deviceFlow |
Worker 명령 큐, DeviceAgent polling, TaskManager submit, telemetry 보고가 실제 보드에서 이어지는지 확인한다. |
| 상시 반복 명령 | routine |
setEyeLedColor처럼 계속 들어오는 LED/PUI 상태 표시성 명령이다. 핵심 이동/청정 테스트와 섞이지 않도록 별도 집계한다. |
| 스케줄 명령 | schedule |
scheduleiot, addSchedule, setActiveSchedule처럼 스케줄 DB/정책을 바꾸는 명령이다. |
| 인터랙션 스케줄 실행 | interaction |
시스템 예약 수행 시점에 interSchedule(action=1, scheduleId=...)로 등록되는 장시간 실행 task다. |
| 명령 요청 기록 | command_request |
UI/Cloud에서 기기로 보내려는 명령을 감사 로그처럼 남긴 기록이다. DISPATCHED는 Worker가 등록했다는 뜻이고, HANDLED는 DeviceAgent가 받아 TaskManager에 넘겼다는 뜻이다. |
| 워크플로우 단계 | WORKFLOW_STEP_* |
복합 작업 내부의 각 단계 시작/완료 이벤트다. currentStepMethod, currentStepIndex, workflowName으로 화면에서 구분한다. |
이벤트 계약
Task event:
{
"type": "task_event",
"deviceId": "A1-board-001",
"taskId": "bedroom_livingroom_clean_return-12",
"taskMethod": "submitWorkflow",
"source": "IOT",
"state": "RUNNING",
"queue": "workflow",
"eventName": "WORKFLOW_STEP_COMPLETED",
"currentStepMethod": "setAirCleanerOperation",
"currentStepIndex": 3,
"workflowName": "bedroom_livingroom_clean_return",
"traceId": "dispatch-bedroom_livingroom_return-1783699716006"
}
Command request:
{
"type": "command_request",
"deviceId": "A1-board-001",
"requestId": "dispatch-bedroom_livingroom_return-1783699716006",
"source": "IOT",
"method": "submitWorkflow",
"queue": "workflow",
"status": "HANDLED",
"taskCount": 5,
"safety": {
"requiresToken": false,
"allowlisted": true
}
}
Interrupt command request:
{
"type": "command_request",
"deviceId": "A1-board-001",
"requestId": "interrupt-returnToStation-1783784529278",
"source": "IOT",
"method": "returnToStation",
"queue": "movement",
"status": "HANDLED",
"taskId": "returnToStation-3",
"interrupt": {
"scenarioKind": "interrupt_return_to_station",
"triggerAfterStep": 1,
"action": "returnToStation",
"targetQueue": "movement",
"reason": "취소 이후 스테이션 복귀를 새 movement task로 등록"
}
}
Interaction schedule command request:
{
"type": "command_request",
"deviceId": "A1-board-001",
"requestId": "interaction-run-901001-1783784529278",
"source": "SCHEDULE",
"method": "interSchedule",
"queue": "interaction",
"status": "HANDLED",
"payload": {
"action": "1",
"scheduleId": "901001",
"completionTarget": "interaction.completed",
"timeoutMs": 1200000
}
}
DeviceAgent 설정
Runtime properties:
persist.sys.deviceagent.taskmonitor.enable=true
persist.sys.deviceagent.taskmonitor.endpoint=https://frosty-bird-d72c.silogood.workers.dev/api/events
persist.sys.deviceagent.taskmonitor.device_id=A1-board-001
persist.sys.deviceagent.taskmonitor.command.enable=true
persist.sys.deviceagent.taskmonitor.command_endpoint=https://frosty-bird-d72c.silogood.workers.dev/api/device/commands
persist.sys.deviceagent.taskmonitor.command_interval_ms=1000
DeviceAgent는 TaskMonitorReporter로 heartbeat/task event를 전송한다. TaskMonitorCommandPoller는 Worker pending queue를 polling하고, 반환된 command를 TaskMonitorCommandBridge로 넘긴 뒤 처리 결과를 command_request ack로 보고한다.
웹에서 “실제 전송”한 명령은 Worker가 dryRun=false로 pending queue에 넣고, DeviceAgent bridge가 dryRun=false, executorDryRun=false를 함께 TaskManager submit bundle로 전달한다. 따라서 실제 executor가 연결된 기기에서는 TaskManager가 기존 DeviceAgent manager 함수까지 호출한다. 하드웨어가 빠진 보드에서는 명령 등록과 대기/실패/타임아웃/모니터 표시까지만 검증한다.
음성 Agent 연동 위치
Task Monitor가 내리는 command와 음성 Agent가 내리는 command는 최종적으로 같은 TaskManager ingress를 사용한다. 차이는 입력 주체와 plan 생성 위치다.
| 입력 주체 | 생성 위치 | DeviceAgent 진입 | 상태 회수 |
|---|---|---|---|
| Task Monitor UI | Worker predefined scenario | polling command -> submitTask/submitWorkflow |
Worker telemetry dashboard |
| Cloud A2A/음성 Agent | Cloud planner device_task_requests[] |
온디바이스 bridge -> submitTask/submitWorkflow |
onTaskEvent -> MaumAi -> Cloud task_event |
2026-07-23 A1-board-005 실기기에서는 TaskManager dry-run 기준으로 아래 계약이 확인됐다.
submitTask
taskMethod=setAirCleanerOperation
forceTaskManager=true
source=CLOUD
contract_version=a2a-task-orchestration-v1
cloud_workflow_id=wf_device_e2e_001
cloud_step_id=step_clean_living_room
cloud_plan_id=plan_device_e2e_cleaning
cloud_output_key=living_room_cleaning
executorDryRun=true
DeviceAgent는 이 task를 COMPLETED로 종료했고, 같은 trace 값을 Worker telemetry에 유지했다. 따라서 Worker pending queue, DeviceAgent polling, TaskManager workflow 실행, monitor event 보고, trace 보존은 통과다.
최신 확인 run:
deviceId: A1-board-005
requestId: dispatch-a2a-callback-probe-1784741807996
source: CLOUD
method: submitWorkflow
workflowName: cloud_a2a_callback_probe
taskId: cloud_a2a_callback_probe-1
event count: 8
terminal event: COMPLETED
2026-07-23 추가 확인 run:
deviceId: A1-board-005
requestId: dispatch-a2a-step-metadata-probe-1784743420469
source: CLOUD
method: submitWorkflow
workflowName: cloud_a2a_step_metadata_probe
taskId: cloud_a2a_step_metadata_probe-1
cloud_workflow_id: wf_codex_step_metadata_probe
cloud_plan_id: plan_codex_step_metadata_probe
step 0: setEyeLedColor, cloud_step_id=step_led_001, cloud_output_key=led_result
step 1: setLlmTts, cloud_step_id=step_tts_001, cloud_output_key=tts_result
terminal event: COMPLETED
이 run의 의미는 단순히 workflow가 끝났다는 것이 아니다. Cloud가 만든 plan step id와 output key가 DeviceAgent TaskManager 내부 step 실행 중에도 보존되고, TaskMonitorReporter가 같은 값을 Worker telemetry로 올렸다는 점이 핵심이다. 따라서 Monitor 화면은 currentStepIndex/currentStepMethod만 보지 말고, Cloud 연계 검증에서는 아래 trace 필드를 함께 확인해야 한다.
| 필드 | 확인 이유 |
|---|---|
cloud_workflow_id / cloudWorkflowId |
Cloud workflow와 SoC native task를 같은 실행 단위로 묶는다. |
cloud_plan_id / cloudPlanId |
어떤 Cloud plan에서 내려온 작업인지 추적한다. |
cloud_step_id / cloudStepId |
재플래닝이나 다음 step resume 시 완료된 step을 식별한다. |
cloud_output_key / cloudOutputKey |
이전 step 결과를 다음 step 입력으로 연결할 때 사용한다. |
현재 Worker telemetry는 이 필드들을 정상 수집한다. 별도 이슈는 MaumAi가 배포 Cloud Lambda로 같은 event를 forward할 때 발생하는 500이며, Monitor 수집 경로와는 분리해서 봐야 한다.
주의할 점은 /api/device/commands가 pending command를 소비하는 polling endpoint라는 점이다. 사람이 상태를 볼 때는 이 endpoint를 직접 호출하지 말고 /api/snapshot?deviceId=A1-board-005를 사용한다.
DeviceAgent callback fan-out도 확인됐다. logcat 기준 MainApi.onModuleCallbackLoop가 method:onTaskEvent, callerclassname:com.skmagic.ondeviceai.agent.service.DeviceCommunicator로 callback을 보냈고, MaumAi는 DeviceCommunicator - Device Device Callback method : onTaskEvent로 STARTED, WORKFLOW_STEP_STARTED, WORKFLOW_STEP_COMPLETED, COMPLETED event를 수신했다.
현재 남은 실패 지점은 MaumAi가 배포 Cloud task_event endpoint로 forward한 뒤 받는 HTTP 500이다. logcat 기준 응답 body는 {"error": "'NoneType' object is not subscriptable"}다. 로컬 Lambda는 task_event 직접 envelope와 Task Monitor style event를 처리하도록 수정 및 테스트됐으므로, app-api-dev 배포본을 최신 package로 동기화해야 closed loop가 완성된다.
다음 검증은 Cloud 배포 후 같은 request id 패턴으로 workflow를 재등록하고, 아래 문자열을 같은 window에서 확인한다.
adb -s 192.168.123.116:5555 logcat -c
# Worker /api/commands 로 새 workflow 등록
adb -s 192.168.123.116:5555 logcat -d | rg -n \
"requestId|workflowName|onTaskEvent|DeviceCommunicator|Task event API|failed to forward|NoneType"
명령 게이트웨이 안전 조건
- Dashboard는 정해진 테스트 프로그램만
/api/commands에 등록한다. - Worker는 allowlisted method만 device pending queue에 넣는다.
- allowlist는 DeviceAgent
TaskPolicyRegistry와TaskExecutorBootstrap기준으로 실제 executor-backed 명령만 포함한다. - 허용되지 않은 method는
BLOCKEDcommand request로 기록한다. - 등록된 command는 DeviceAgent polling으로 1회 소비된다.
- DeviceAgent ack는
HANDLED또는FAILED로 Worker snapshot에 반영된다.
현재 웹 전송 허용 범위:
| 범주 | 대표 method |
|---|---|
| workflow/control | submitWorkflow, cancelTask, clearPendingTasks, cancelQueue |
| 이동 | setMoveTo, setMoving, stopMovement, returnToStation |
| 청정 | getAirCleanerOperation, setAirCleanerOperation, setStatusClean, startSimpleAirClear, startBasicAirClear, startFixedAirClear, startAllAirClear, startSelectiveAirClear, startAirSensorAirClear, stopAirClear, pauseAirClear, resumeAirClear, returnAirClearToStation, returnCleaningToStation, stopCleaning, ampStop |
| 상태/설정 | getBatteryInfo, getFirmwareVersion, getMainState, getDeviceStatus, setMainState, setDeviceStatus, setLauncherScreen, setConfig, getConfig, getConfiguration |
| AI/PUI | setChangeLlmStatus, setLlmTts, stopLlm, stopTts, setEyeLedColor, startEyeLedScene |
| 스케줄/인터랙션 | addSchedule, editSchedule, deleteSchedule, scheduleiot, setActiveSchedule, puiEditSchedule, callschedule, getschedule, getUpComingSchedule, setScheduleExecuted, gpsLocNearBy, interSchedule |
| 업데이트 | OTAUpdateArmResult, OTAUpdateMcuResult, setFirmwareUpdateStatus |
setFanLevel, setLlmStatus처럼 legacy 코드에는 흔적이 있지만 현재 TaskManager executor-backed catalog에 없는 명령은 실기기 전송에서 차단한다. 이런 명령을 테스트하려면 먼저 DeviceAgent executor/policy에 실제 완료 기준까지 연결한 뒤 allowlist에 추가한다.
검증 기준
실기기 검증은 아래 증거가 동시에 있어야 통과로 본다.
- Worker
/api/commands응답이 allowlisted command를 등록한다. - Worker snapshot의 command request가
DISPATCHED에서HANDLED로 바뀐다. - DeviceAgent logcat에
TaskMonitorCommandPoller handled commands:1또는 workflow 처리 로그가 남는다. - TaskManager event listener에서
STARTED,WORKFLOW_STEP_STARTED,WORKFLOW_STEP_COMPLETED,COMPLETED가 보고된다. - 스케줄 등록 테스트는
scheduleiot(command=add)가schedulequeue로 들어가고, DeviceAgent가HANDLEDack를 올리면 통과로 본다. - 인터랙션 스케줄 실행 테스트는
interSchedule(action=1, scheduleId=...)가interactionqueue에 들어가고,InterScheduleManagerstatus가interaction.completed로 변환되어COMPLETEDtask event가 올라와야 완료 통과로 본다. - 복합 workflow는
currentStepMethod,currentStepIndex,workflowName이 포함된 telemetry를 올린다. - 진행 중 인터럽트는 workflow
HANDLED,cancelQueueHANDLED,returnToStationHANDLED가 모두 남고,interrupt.scenarioKind=interrupt_return_to_station메타데이터가 보존되어야 한다. - Dashboard의 작업 흐름은
command_request.payload.subTasks기준으로안방 이동 -> 안방 청정 -> 거실 이동 -> 거실 청정 -> 스테이션 복귀같은 subtask 정의를 표시하고, 각 단계의 시작/완료 이벤트를 상태로 반영한다. - 완료 후
currentTaskCount가 0이어도, 같은 request/task의COMPLETED이벤트가 있으면 정상 완료로 판단한다.
현재 의사결정
웹 화면에서 직접 task를 생성하거나 payload를 조합하는 기능은 범위에서 제외한다. 이 화면은 정해진 테스트 프로그램을 실기기 TaskManager 경로에 태우고, 큐/상태/단계/완료 여부를 관측하는 용도로 유지한다.
현재 판정은 TaskManager 관리 검증이다. 실제 이동, 청정, 복귀 actuator 함수가 물리적으로 끝까지 수행됐다는 의미로 표시하지 않는다. 향후 모든 실제 함수가 연결된 기기에서는 같은 subtask payload와 telemetry 계약을 유지한 채, executor/actuator 완료 신호를 별도 evidence로 추가한다.
진행 중 인터럽트 시나리오도 현재는 실제 이동 중단을 물리적으로 검증하지 않는다. 현재 검증 범위는 Worker pending queue, DeviceAgent polling, TaskManager control command 처리, command ack, dashboard 표시가 이어지는지다. 실제 actuator 완료/중단 callback이 붙으면 같은 interrupt 메타데이터를 기준으로 DEVICE_CONFIRMED, FAILED_BY_DEVICE, CANCELLED_BY_DEVICE 같은 물리 결과 evidence를 추가한다.
인터랙션 스케줄은 사용자가 웹에서 임의 payload를 만드는 기능이 아니라, “시스템 예약 수행 시점에 자동으로 TaskManager에 등록되는 흐름”을 검증하는 테스트 프로그램으로 둔다. 웹의 등록/실행 버튼은 업체 설명과 단말 검증을 위한 테스트 harness이며, 제품 동작에서는 SKMLauncher/스케줄 엔진이 같은 scheduleId를 DeviceAgent interSchedule로 넘기는 것이 정상 경로다.