Currently there's no reliable way to know when an agent finishes a task, and agentd ps only shows 'running' with no distinction between actively working, idle, or wedged.
Combine two improvements into one:
1. Structured completion signal: Add a first-class 'task finished' / 'waiting for input' state to agent status. This lets orchestrating agents (like ava) know when to dispatch the next task without polling logs and pattern-matching on text.
2. Activity indicators in agentd ps: Show last message timestamp, current task ID (if any), and a meaningful activity state (working / idle / waiting-for-input / stuck) in agentd ps output. This eliminates the need to pull full logs just to check if an agent is alive.
These are overlapping implementations — both require richer state tracking in the agent lifecycle. The completion signal is the higher-leverage piece; activity indicators are a natural extension of the same state.
Clean investigation summary (previous comment had shell-quoting corruption):
agentd status sd-coder --mode persistent --json currently reports status=running, systemd=active, summary=null.~/.local/state/agentd-agents/sessions/sd-coder.jsonl has no new events after 2026-04-15T21:24:37Z; latest typed event is type=custom, custom_type=agent_start.~0% CPU, only tee child), which is consistent with waiting on FIFO/input, not actively working.~/.local/share/omni/agentd.db, table agents) for sd-coder is status=running, summary=NULL, updated_at=2026-04-15 21:24:37 UTC.Root cause:
sendPersistentAgent sets status to running immediately.processTraceEvent path that calls updateAgentIdle on agent_complete.mergeSystemdStatus maps (systemd=Active, dbStatus!=Idle) to Running, so stale running persists.So status can remain running even when the persistent agent is effectively waiting for input.
Fresh check (2026-04-15 20:40 EDT):
agentd status sd-coder --mode persistent --json still reports status=running, systemd=active.~/.local/share/omni/agentd.db remains status=running, updated_at=2026-04-15 21:24:37 UTC.~/.local/state/agentd-agents/sessions/sd-coder.jsonl) last typed event is still custom/agent_start at 2026-04-15T21:24:37Z; no infer/complete events after that.621980)wchan=futex_wait_queue/home/ben/.local/state/agentd-agents/sd-coder.fifotee child processInterpretation: process is alive and waiting on FIFO input (idle/waiting), but status is stuck as running.
Implemented fix for persistent status sticking at running when the agent is actually waiting for input.
What changed (Omni/Agentd/Daemon.hs):
hydratePersistentAgent.~/.local/state/agentd-agents/sessions/<agent>.jsonl) and infer latest activity from trace events:agent_start, infer_start, infer_end, tool_call, tool_resultagent_complete, agent_errorrunning:running, and there are no session events for >30s => report idle.agent_start (>120s old) => report idle.running is fresher than session events (to avoid flapping right after send).Tests added/updated:
session status parser finds latest idle eventreconcile keeps fresher DB running statusreconcile uses newer session statusreconcile marks stale running-without-events idlereconcile marks stale lone agent_start idleVerification run:
typecheck.sh Omni/Agentd/Daemon.hslint Omni/Agentd/Daemon.hsbild --test Omni/Agentd/Daemon.hstypecheck.sh Omni/Agentd.hsbild --test Omni/Agentd.hsManual behavior check with patched binary:
Omni/Ide/run.sh Omni/Agentd.hs status sd-coder --mode persistent --json now returns "status":"idle" with "systemd":"active" (expected: active service, waiting for input).
Investigation for sd-coder status mismatch (running vs actually idle/waiting):
Evidence:
ExitType=main Restart=on-failure RestartMode=normal NotifyAccess=none RestartUSec=30s RestartSteps=0 RestartMaxDelayUSec=infinity RestartUSecNext=30s TimeoutStartUSec=1min 30s TimeoutStopUSec=10s TimeoutAbortUSec=10s TimeoutStartFailureMode=terminate TimeoutStopFailureMode=terminate RuntimeMaxUSec=infinity RuntimeRandomizedExtraUSec=0 WatchdogUSec=0 WatchdogTimestampMonotonic=0 RootDirectoryStartOnly=no RemainAfterExit=no GuessMainPID=yes MainPID=621980 ControlPID=0 FileDescriptorStoreMax=0 NFileDescriptorStore=0 FileDescriptorStorePreserve=restart StatusErrno=0 Result=success ReloadResult=success CleanResult=success LiveMountResult=success UID=[not set] GID=[not set] NRestarts=0 OOMPolicy=stop ReloadSignal=1 ExecMainStartTimestamp=Wed 2026-04-15 10:44:30 EDT ExecMainStartTimestampMonotonic=3006157750727 ExecMainExitTimestampMonotonic=0 ExecMainHandoffTimestamp=Wed 2026-04-15 10:44:30 EDT ExecMainHandoffTimestampMonotonic=3006157776943 ExecMainPID=621980 ExecMainCode=0 ExecMainStatus=0 ExecStart={ path=/home/ben/.config/agentd-agents/agentd-agent-exec ; argv[]=/home/ben/.config/agentd-agents/agentd-agent-exec ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 } ExecStartEx={ path=/home/ben/.config/agentd-agents/agentd-agent-exec ; argv[]=/home/ben/.config/agentd-agents/agentd-agent-exec ; flags= ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 } Slice=app-agentd\x2dagent.slice ControlGroup=/user.slice/user-1000.slice/user@1000.service/app.slice/app-agentd\x2dagent.slice/agentd-agent@sd-coder.service ControlGroupId=27777738 MemoryCurrent=27451392 MemoryPeak=34541568 MemorySwapCurrent=0 MemorySwapPeak=0 MemoryZSwapCurrent=0 MemoryAvailable=68767391744 EffectiveMemoryMax=135031595008 EffectiveMemoryHigh=135031595008 CPUUsageNSec=3571743000 TasksCurrent=7 EffectiveTasksMax=154399 IPIngressBytes=[no data] IPIngressPackets=[no data] IPEgressBytes=[no data] IPEgressPackets=[no data] IOReadBytes=[not set] IOReadOperations=[not set] IOWriteBytes=[not set] IOWriteOperations=[not set] Delegate=no CPUAccounting=yes CPUWeight=[not set] StartupCPUWeight=[not set] CPUShares=[not set] StartupCPUShares=[not set] CPUQuotaPerSecUSec=infinity CPUQuotaPeriodUSec=infinity IOAccounting=no IOWeight=[not set] StartupIOWeight=[not set] BlockIOAccounting=no BlockIOWeight=[not set] StartupBlockIOWeight=[not set] MemoryAccounting=yes DefaultMemoryLow=0 DefaultStartupMemoryLow=0 DefaultMemoryMin=0 MemoryMin=0 MemoryLow=0 StartupMemoryLow=0 MemoryHigh=infinity StartupMemoryHigh=infinity MemoryMax=infinity StartupMemoryMax=infinity MemorySwapMax=infinity StartupMemorySwapMax=infinity MemoryZSwapMax=infinity StartupMemoryZSwapMax=infinity MemoryZSwapWriteback=yes MemoryLimit=infinity DevicePolicy=auto TasksAccounting=yes TasksMax=154399 IPAccounting=no ManagedOOMSwap=auto ManagedOOMMemoryPressure=auto ManagedOOMMemoryPressureLimit=0 ManagedOOMMemoryPressureDurationUSec=[not set] ManagedOOMPreference=none MemoryPressureWatch=auto MemoryPressureThresholdUSec=200ms CoredumpReceive=no Environment=HOME=/home/ben PATH=/usr/local/bin:/run/current-system/sw/bin:/usr/bin:/bin:/home/ben/.nix-profile/bin AGENTD_AGENT_NAME=sd-coder EnvironmentFiles=/home/ben/.config/agentd-agents/sd-coder.env (ignore_errors=no) UMask=0022 LimitCPU=infinity LimitCPUSoft=infinity LimitFSIZE=infinity LimitFSIZESoft=infinity LimitDATA=infinity LimitDATASoft=infinity LimitSTACK=infinity LimitSTACKSoft=8388608 LimitCORE=infinity LimitCORESoft=infinity LimitRSS=infinity LimitRSSSoft=infinity LimitNOFILE=524288 LimitNOFILESoft=1024 LimitAS=infinity LimitASSoft=infinity LimitNPROC=514666 LimitNPROCSoft=514666 LimitMEMLOCK=8388608 LimitMEMLOCKSoft=8388608 LimitLOCKS=infinity LimitLOCKSSoft=infinity LimitSIGPENDING=514666 LimitSIGPENDINGSoft=514666 LimitMSGQUEUE=819200 LimitMSGQUEUESoft=819200 LimitNICE=0 LimitNICESoft=0 LimitRTPRIO=0 LimitRTPRIOSoft=0 LimitRTTIME=infinity LimitRTTIMESoft=infinity WorkingDirectory=!/home/ben RootEphemeral=no OOMScoreAdjust=200 CoredumpFilter=0x33 Nice=0 IOSchedulingClass=2 IOSchedulingPriority=4 CPUSchedulingPolicy=0 CPUSchedulingPriority=0 CPUAffinityFromNUMA=no NUMAPolicy=n/a TimerSlackNSec=50000 CPUSchedulingResetOnFork=no NonBlocking=no StandardInput=null StandardOutput=journal StandardError=journal TTYReset=no TTYVHangup=no TTYVTDisallocate=no SyslogPriority=30 SyslogIdentifier=agentd-agent-sd-coder SyslogLevelPrefix=yes SyslogLevel=6 SyslogFacility=3 LogLevelMax=-1 LogRateLimitIntervalUSec=0 LogRateLimitBurst=0 SecureBits=0 CapabilityBoundingSet=cap_chown cap_dac_override cap_dac_read_search cap_fowner cap_fsetid cap_kill cap_setgid cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config cap_mknod cap_lease cap_audit_write cap_audit_control cap_setfcap cap_mac_override cap_mac_admin cap_syslog cap_wake_alarm cap_block_suspend cap_audit_read cap_perfmon cap_bpf cap_checkpoint_restore DynamicUser=no SetLoginEnvironment=no RemoveIPC=no PrivateTmp=no PrivateTmpEx=no PrivateDevices=no ProtectClock=no ProtectKernelTunables=no ProtectKernelModules=no ProtectKernelLogs=no ProtectControlGroups=no ProtectControlGroupsEx=no PrivateNetwork=no PrivateUsers=no PrivateUsersEx=no PrivateMounts=no PrivateIPC=no PrivatePIDs=no ProtectHome=no ProtectSystem=no SameProcessGroup=no UtmpMode=init IgnoreSIGPIPE=yes NoNewPrivileges=no SystemCallErrorNumber=2147483646 LockPersonality=no RuntimeDirectoryPreserve=no RuntimeDirectoryMode=0755 StateDirectoryMode=0755 CacheDirectoryMode=0755 LogsDirectoryMode=0755 ConfigurationDirectoryMode=0755 TimeoutCleanUSec=infinity MemoryDenyWriteExecute=no RestrictRealtime=no RestrictSUIDSGID=no RestrictNamespaces=no MountAPIVFS=no BindLogSockets=no KeyringMode=inherit ProtectProc=default ProcSubset=all ProtectHostname=no MemoryKSM=no RootImagePolicy=root=verity+signed+encrypted+unprotected+absent:usr=verity+signed+encrypted+unprotected+absent:home=encrypted+unprotected+absent:srv=encrypted+unprotected+absent:tmp=encrypted+unprotected+absent:var=encrypted+unprotected+absent MountImagePolicy=root=verity+signed+encrypted+unprotected+absent:usr=verity+signed+encrypted+unprotected+absent:home=encrypted+unprotected+absent:srv=encrypted+unprotected+absent:tmp=encrypted+unprotected+absent:var=encrypted+unprotected+absent ExtensionImagePolicy=root=verity+signed+encrypted+unprotected+absent:usr=verity+signed+encrypted+unprotected+absent:home=encrypted+unprotected+absent:srv=encrypted+unprotected+absent:tmp=encrypted+unprotected+absent:var=encrypted+unprotected+absent KillMode=control-group KillSignal=15 RestartKillSignal=15 FinalKillSignal=9 SendSIGKILL=yes SendSIGHUP=no WatchdogSignal=6 Id=agentd-agent@sd-coder.service Names=agentd-agent@sd-coder.service Requires="app-agentd\\x2dagent.slice" basic.target Conflicts=shutdown.target Before=shutdown.target After=basic.target "app-agentd\\x2dagent.slice" -.mount network-online.target WantsMountsFor=/home/ben Description=Agentd Agent (sd-coder) LoadState=loaded ActiveState=active FreezerState=running SubState=running FragmentPath=/home/ben/.config/systemd/user/agentd-agent@.service UnitFileState=disabled UnitFilePreset=ignored StateChangeTimestamp=Wed 2026-04-15 10:44:30 EDT StateChangeTimestampMonotonic=3006157751858 InactiveExitTimestamp=Wed 2026-04-15 10:44:30 EDT InactiveExitTimestampMonotonic=3006157751858 ActiveEnterTimestamp=Wed 2026-04-15 10:44:30 EDT ActiveEnterTimestampMonotonic=3006157751858 ActiveExitTimestamp=Wed 2026-04-15 10:44:20 EDT ActiveExitTimestampMonotonic=3006147661215 InactiveEnterTimestamp=Wed 2026-04-15 10:44:30 EDT InactiveEnterTimestampMonotonic=3006157746828 CanStart=yes CanStop=yes CanReload=no CanIsolate=no CanFreeze=yes CanLiveMount=no StopWhenUnneeded=no RefuseManualStart=no RefuseManualStop=no AllowIsolate=no DefaultDependencies=yes SurviveFinalKillSignal=no OnSuccessJobMode=fail OnFailureJobMode=replace IgnoreOnIsolate=no NeedDaemonReload=no JobTimeoutUSec=infinity JobRunningTimeoutUSec=infinity JobTimeoutAction=none ConditionResult=yes AssertResult=yes ConditionTimestamp=Wed 2026-04-15 10:44:30 EDT ConditionTimestampMonotonic=3006157748110 AssertTimestamp=Wed 2026-04-15 10:44:30 EDT AssertTimestampMonotonic=3006157748147 Transient=no Perpetual=no StartLimitIntervalUSec=10s StartLimitBurst=5 StartLimitAction=none FailureAction=none SuccessAction=none InvocationID=12f8215bd1f5424e881544d1f6f3b65b CollectMode=inactive DebugInvocation=no => , , MainPID alive.
Root cause in code:
This exactly matches t-767 scope (structured completion + activity state).