@bybrawe/opencode-loop 0.5.35 → 0.5.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.37
4
+
5
+ Dedicated Goal tool-isolation release.
6
+
7
+ - Detect a persisted dedicated @bybrawe/opencode-goal Goal before any experimental `opencode_loop_goal_*` lifecycle tool can mutate Loop Goal state.
8
+ - Fail closed when a dedicated Goal owns the session and explicitly direct the model to `opencode_goal_progress`, `opencode_goal_wait_for_user`, `opencode_goal_blocked`, or `opencode_goal_complete` instead of returning a misleading experimental-Goal result.
9
+ - Mark the Loop lifecycle tools as experimental `/loop-goal`-only in their model-visible descriptions, preventing the exact blocker-tool collision reproduced by the long Loto Diyarı session.
10
+ - Keep modular source and the committed single-file stable bundle byte-for-byte synchronized with the Bun build output.
11
+ - Add coexistence smoke coverage for blocked, progress, and completion collisions; exact feature-head CI, Dedicated Goal Coexistence, and Single-file Bundle Gate pass across the required host/platform lanes.
12
+
13
+ ## 0.5.36
14
+
15
+ Empty-turn fail-safe release.
16
+
17
+ - Distinguish a completed assistant message from a meaningful completed Loop turn instead of treating a timestamp-only blank reply as success.
18
+ - Count non-empty assistant text and tool/file/patch/artifact activity anywhere in the logical run as meaningful work, including tool-only runs with a blank final assistant tail.
19
+ - Refund a completed-but-empty logical run so it does not consume `runCount`, `--max-runs`, or normal success finalization/checkpoint behavior.
20
+ - Persist consecutive empty-turn state across reloads, retry one empty completion with bounded delay, then pause safely after the second consecutive empty turn with a visible warning instead of injecting prompts forever.
21
+ - Recover stale host `busy` state for already-completed empty turns so the fail-safe can finalize them rather than leaving the Loop stuck between busy and due states.
22
+ - Keep release metadata automation compatible with the README's `Current stable release` marker so future release prep does not fail on the documented format.
23
+ - Add focused regressions for blank/whitespace replies, tool activity, max-run refund/re-enable, bounded retry, fail-safe pause, successful streak reset, and generated-bundle parity; full Ubuntu/Windows CI, real-host, Goal coexistence, lifecycle, and single-file bundle gates are green on the feature head.
24
+
3
25
  ## 0.5.35
4
26
 
5
27
  - Treat OpenCode `session.status=retry` as authoritative provider ownership: retry and unreadable status can no longer age into idle and cause an overlapping Loop turn; a genuinely completed current assistant message can still release stale retry safely.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  OpenCode Loop adds `/loop`, scheduled prompt/command/shell jobs, compact scheduling, verification/checkpoints, and the `opencode-loopd` background daemon.
6
6
 
7
- > **Current stable release: `0.5.35`.**
7
+ > **Current stable release: `0.5.37`.**
8
8
 
9
9
  ## Install or update
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bybrawe/opencode-loop",
3
- "version": "0.5.35",
3
+ "version": "0.5.37",
4
4
  "description": "Claude Code/Codex style /loop and experimental goal mode for OpenCode: heartbeat scheduler, idle-safe loops, scheduled commands, compact scheduling, verification, checkpoints, and persistent coding goals.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -21,8 +21,8 @@
21
21
  "build:plugin": "bun build src/source/v1.js --outfile=src/index.js --target=bun --format=esm --external=@opencode-ai/plugin/tool",
22
22
  "build:plugin:npm": "npm run build:plugin",
23
23
  "prepack": "node --check src/index.js",
24
- "check": "node --check src/source/v1.js && node --check src/source/core/args.js && node --check src/source/core/continuation.js && node --check src/source/core/schedule-syntax.js && node --check src/source/core/state.js && node --check src/source/core/jobs.js && node --check src/source/core/process.js && node --check src/source/opencode/sdk.js && node --check src/source/opencode/session-context.js && node --check src/source/opencode/command-router.js && node --check src/source/opencode/goal-commands.js && node --check src/source/opencode/loop-commands.js && node --check src/source/opencode/loop-registration.js && node --check src/source/runtime/session-activity.js && node --check src/source/runtime/session-status.js && node --check src/source/runtime/schedule-policy.js && node --check src/source/runtime/scheduler-diagnostics.js && node --check src/source/runtime/companion-goal.js && node --check src/source/runtime/loop-diagnostics.js && node --check src/source/runtime/compaction.js && node --check src/source/runtime/action-dispatch.js && node --check src/source/runtime/network-recovery.js && node --check src/source/runtime/terminal-guard.js && node --check src/source/runtime/run-finalization.js && node --check src/source/runtime/run-admission.js && node --check src/source/runtime/executor.js && node --check src/source/runtime/scheduler.js && node --check src/source/runtime/goal-prompt.js && node --check src/source/runtime/goal-report.js && node --check src/source/runtime/goal-evidence.js && node --check src/source/runtime/goal-runtime.js && node --check src/source/runtime/goal-policy.js && node --check src/source/runtime/goal-steering.js && node --check src/source/runtime/job-workspace.js && node --check src/source/opencode2/prompt-runtime.js && node --check src/source/opencode2/diagnostics.js && node --check src/source/opencode2/logging.js && node --check src/source/legacy-v1.js && node --check src/index.js && node --check scripts/install-node.mjs && node --check scripts/install-with-goals.mjs && node --check scripts/loopd.mjs && node --check scripts/install-test.mjs && node --check scripts/goal-companion-test.mjs && node --check scripts/loopd-test.mjs && node --check scripts/smoke-test.mjs && node --check scripts/host-adapter-contract-test.mjs && node --check scripts/command-router-test.mjs && node --check scripts/goal-command-handlers-test.mjs && node --check scripts/loop-command-handlers-test.mjs && node --check scripts/loop-registration-test.mjs && node --check scripts/schedule-syntax-test.mjs && node --check scripts/schedule-policy-test.mjs && node --check scripts/session-activity-test.mjs && node --check scripts/session-status-test.mjs && node --check scripts/session-status-idle-recovery-test.mjs && node --check scripts/network-recovery-test.mjs && node --check scripts/compaction-runtime-test.mjs && node --check scripts/executor-runtime-test.mjs && node --check scripts/scheduler-runtime-test.mjs && node --check scripts/goal-runtime-test.mjs && node --check scripts/goal-policy-test.mjs && node --check scripts/goal-steering-test.mjs && node --check scripts/job-workspace-test.mjs && node --check scripts/v2-prompt-runtime-test.mjs && node --check scripts/v2-prompt-interval-test.mjs && node --check scripts/v2-command-runtime-test.mjs && node --check scripts/v2-command-adapter-test.mjs && node --check scripts/v2-diagnostics-test.mjs && node --check scripts/v2-logging-test.mjs && node --check scripts/comprehensive-watchdog.mjs && node --check scripts/comprehensive-test.mjs && node --check scripts/host-loop-canary.mjs && node --check scripts/host-goal-steering-canary.mjs && node --check scripts/publish-workflow-test.mjs",
25
- "test": "node scripts/publish-workflow-test.mjs && node scripts/command-router-test.mjs && node scripts/goal-command-handlers-test.mjs && node scripts/loop-command-handlers-test.mjs && node scripts/loop-registration-test.mjs && node scripts/schedule-syntax-test.mjs && node scripts/schedule-policy-test.mjs && node scripts/session-activity-test.mjs && node scripts/session-status-test.mjs && node scripts/session-status-idle-recovery-test.mjs && node scripts/network-recovery-test.mjs && node scripts/compaction-runtime-test.mjs && node scripts/action-dispatch-test.mjs && node scripts/run-finalization-test.mjs && node scripts/run-admission-test.mjs && node scripts/executor-runtime-test.mjs && node scripts/scheduler-runtime-test.mjs && node scripts/goal-runtime-test.mjs && node scripts/goal-policy-test.mjs && node scripts/goal-steering-test.mjs && node scripts/job-workspace-test.mjs && node scripts/v2-prompt-runtime-test.mjs && node scripts/v2-prompt-interval-test.mjs && node scripts/v2-command-runtime-test.mjs && node scripts/v2-command-adapter-test.mjs && node scripts/v2-diagnostics-test.mjs && node scripts/v2-logging-test.mjs && node scripts/install-test.mjs && node scripts/goal-companion-test.mjs && node scripts/loopd-test.mjs && node scripts/smoke-test.mjs && node scripts/host-adapter-contract-test.mjs && node scripts/comprehensive-watchdog.mjs",
24
+ "check": "node --check src/source/v1.js && node --check src/source/core/args.js && node --check src/source/core/continuation.js && node --check src/source/core/schedule-syntax.js && node --check src/source/core/state.js && node --check src/source/core/jobs.js && node --check src/source/core/process.js && node --check src/source/opencode/sdk.js && node --check src/source/opencode/session-context.js && node --check src/source/opencode/command-router.js && node --check src/source/opencode/goal-commands.js && node --check src/source/opencode/loop-commands.js && node --check src/source/opencode/loop-registration.js && node --check src/source/runtime/session-activity.js && node --check src/source/runtime/session-status.js && node --check src/source/runtime/schedule-policy.js && node --check src/source/runtime/scheduler-diagnostics.js && node --check src/source/runtime/companion-goal.js && node --check src/source/runtime/loop-diagnostics.js && node --check src/source/runtime/compaction.js && node --check src/source/runtime/action-dispatch.js && node --check src/source/runtime/network-recovery.js && node --check src/source/runtime/empty-turn.js && node --check src/source/runtime/terminal-guard.js && node --check src/source/runtime/run-finalization.js && node --check src/source/runtime/run-admission.js && node --check src/source/runtime/executor.js && node --check src/source/runtime/scheduler.js && node --check src/source/runtime/goal-prompt.js && node --check src/source/runtime/goal-report.js && node --check src/source/runtime/goal-evidence.js && node --check src/source/runtime/goal-runtime.js && node --check src/source/runtime/goal-policy.js && node --check src/source/runtime/goal-steering.js && node --check src/source/runtime/job-workspace.js && node --check src/source/opencode2/prompt-runtime.js && node --check src/source/opencode2/diagnostics.js && node --check src/source/opencode2/logging.js && node --check src/source/legacy-v1.js && node --check src/index.js && node --check scripts/install-node.mjs && node --check scripts/install-with-goals.mjs && node --check scripts/loopd.mjs && node --check scripts/install-test.mjs && node --check scripts/goal-companion-test.mjs && node --check scripts/loopd-test.mjs && node --check scripts/smoke-test.mjs && node --check scripts/host-adapter-contract-test.mjs && node --check scripts/command-router-test.mjs && node --check scripts/goal-command-handlers-test.mjs && node --check scripts/loop-command-handlers-test.mjs && node --check scripts/loop-registration-test.mjs && node --check scripts/schedule-syntax-test.mjs && node --check scripts/schedule-policy-test.mjs && node --check scripts/session-activity-test.mjs && node --check scripts/session-status-test.mjs && node --check scripts/session-status-idle-recovery-test.mjs && node --check scripts/empty-turn-recovery-test.mjs && node --check scripts/network-recovery-test.mjs && node --check scripts/compaction-runtime-test.mjs && node --check scripts/executor-runtime-test.mjs && node --check scripts/scheduler-runtime-test.mjs && node --check scripts/goal-runtime-test.mjs && node --check scripts/goal-policy-test.mjs && node --check scripts/goal-steering-test.mjs && node --check scripts/job-workspace-test.mjs && node --check scripts/v2-prompt-runtime-test.mjs && node --check scripts/v2-prompt-interval-test.mjs && node --check scripts/v2-command-runtime-test.mjs && node --check scripts/v2-command-adapter-test.mjs && node --check scripts/v2-diagnostics-test.mjs && node --check scripts/v2-logging-test.mjs && node --check scripts/comprehensive-watchdog.mjs && node --check scripts/comprehensive-test.mjs && node --check scripts/host-loop-canary.mjs && node --check scripts/host-goal-steering-canary.mjs && node --check scripts/publish-workflow-test.mjs",
25
+ "test": "node scripts/publish-workflow-test.mjs && node scripts/command-router-test.mjs && node scripts/goal-command-handlers-test.mjs && node scripts/loop-command-handlers-test.mjs && node scripts/loop-registration-test.mjs && node scripts/schedule-syntax-test.mjs && node scripts/schedule-policy-test.mjs && node scripts/session-activity-test.mjs && node scripts/session-status-test.mjs && node scripts/session-status-idle-recovery-test.mjs && node scripts/empty-turn-recovery-test.mjs && node scripts/network-recovery-test.mjs && node scripts/compaction-runtime-test.mjs && node scripts/action-dispatch-test.mjs && node scripts/run-finalization-test.mjs && node scripts/run-admission-test.mjs && node scripts/executor-runtime-test.mjs && node scripts/scheduler-runtime-test.mjs && node scripts/goal-runtime-test.mjs && node scripts/goal-policy-test.mjs && node scripts/goal-steering-test.mjs && node scripts/job-workspace-test.mjs && node scripts/v2-prompt-runtime-test.mjs && node scripts/v2-prompt-interval-test.mjs && node scripts/v2-command-runtime-test.mjs && node scripts/v2-command-adapter-test.mjs && node scripts/v2-diagnostics-test.mjs && node scripts/v2-logging-test.mjs && node scripts/install-test.mjs && node scripts/goal-companion-test.mjs && node scripts/loopd-test.mjs && node scripts/smoke-test.mjs && node scripts/host-adapter-contract-test.mjs && node scripts/comprehensive-watchdog.mjs",
26
26
  "canary:host": "node scripts/host-loop-canary.mjs && node scripts/host-goal-steering-canary.mjs",
27
27
  "install:global": "node scripts/install-with-goals.mjs",
28
28
  "pack:zip": "node scripts/make-zip.mjs"
@@ -0,0 +1,150 @@
1
+ import assert from "node:assert/strict"
2
+ import { activeRunCompletionFromMessages, assistantMessageHasMeaningfulActivity } from "../src/source/opencode/host.js"
3
+ import { createSessionStatusRuntime } from "../src/source/runtime/session-status.js"
4
+ import { createLoopExecutor } from "../src/source/runtime/executor.js"
5
+ import { refundEmptyAssistantTurn, clearEmptyAssistantTurnStreak } from "../src/source/runtime/empty-turn.js"
6
+ import { clearSessionActivity } from "../src/source/runtime/session-activity.js"
7
+
8
+ const blank = { info: { role: "assistant", time: { created: 120, completed: 130 } }, parts: [] }
9
+ const whitespace = { info: { role: "assistant", time: { created: 120, completed: 130 } }, parts: [{ type: "text", text: " \n" }] }
10
+ const text = { info: { role: "assistant", time: { created: 120, completed: 130 } }, parts: [{ type: "text", text: "done" }] }
11
+ const tool = { info: { role: "assistant", time: { created: 120, completed: 130 } }, parts: [{ type: "tool", callID: "call-1", state: { status: "completed" } }] }
12
+ assert.equal(assistantMessageHasMeaningfulActivity(blank), false)
13
+ assert.equal(assistantMessageHasMeaningfulActivity(whitespace), false)
14
+ assert.equal(assistantMessageHasMeaningfulActivity(text), true)
15
+ assert.equal(assistantMessageHasMeaningfulActivity(tool), true)
16
+
17
+ let hostMessages = [blank]
18
+ const hostClient = { session: { messages: async () => ({ data: hostMessages }) } }
19
+ assert.equal(await activeRunCompletionFromMessages("/repo", hostClient, "host-empty", { startedAt: 100 }), "empty")
20
+ hostMessages = [tool, { info: { role: "assistant", time: { created: 140, completed: 150 } }, parts: [] }]
21
+ assert.equal(await activeRunCompletionFromMessages("/repo", hostClient, "host-tool-then-blank", { startedAt: 100 }), "completed", "tool activity earlier in the same logical run must prevent a false empty classification")
22
+ hostMessages = [text]
23
+ assert.equal(await activeRunCompletionFromMessages("/repo", hostClient, "host-text", { startedAt: 100 }), "completed")
24
+
25
+ const pureJob = { id: "pure", enabled: false, paused: false, runCount: 1, maxRuns: 1, lastRunAt: 50 }
26
+ let pure = refundEmptyAssistantTurn(pureJob, { job: { runCount: 1 }, previousLastRunAt: 10, disabledByMaxRuns: true }, 200)
27
+ assert.equal(pure.job.runCount, 0)
28
+ assert.equal(pure.job.enabled, true)
29
+ assert.equal(pure.job.lastRunAt, 10)
30
+ assert.equal(pure.job.runNowRequestedAt, 200)
31
+ assert.equal(pure.job.emptyTurnCount, 1)
32
+ assert.equal(pure.paused, false)
33
+ pure = refundEmptyAssistantTurn(pure.job, { job: { runCount: 1 }, previousLastRunAt: 10, disabledByMaxRuns: true }, 300)
34
+ assert.equal(pure.paused, true)
35
+ assert.equal(pure.job.paused, true)
36
+ assert.equal(pure.job.runNowRequestedAt, undefined)
37
+ clearEmptyAssistantTurnStreak(pure.job)
38
+ assert.equal(pure.job.emptyTurnCount, 0)
39
+ assert.equal(pure.job.lastFailureReason, undefined)
40
+
41
+ const statusLogs = []
42
+ const statusActive = new Map([["status-empty", { jobId: "j", job: { id: "j" }, startedAt: 100 }]])
43
+ const statusRuntime = createSessionStatusRuntime({
44
+ activeRuns: statusActive,
45
+ now: () => 10_000,
46
+ sessionStatusCacheMs: 0,
47
+ activeRunCompletionFromMessages: async () => "empty",
48
+ appendLoopLog: async (...args) => statusLogs.push(args),
49
+ })
50
+ const statusClient = { session: { status: async () => ({ data: { "status-empty": { type: "busy" } } }) } }
51
+ assert.equal(await statusRuntime.sessionStatusType(statusClient, "status-empty", "/repo"), "idle", "a host-busy tail that is already an empty completed assistant turn must settle so the empty-turn guard can run")
52
+ assert.ok(statusLogs.some((entry) => entry[1] === "status-message-empty-recovery"))
53
+ clearSessionActivity("status-empty")
54
+
55
+ let clock = 1_000
56
+ let outcome = "empty"
57
+ const states = new Map()
58
+ const schedules = []
59
+ const toasts = []
60
+ const notifications = []
61
+ const checkpoints = []
62
+ const key = (directory, sessionID) => String(directory) + ":" + String(sessionID)
63
+ const clone = (value) => JSON.parse(JSON.stringify(value))
64
+ const workspace = {
65
+ buildPrompt: async () => "continue",
66
+ ensureBranch: async (_directory, job) => job,
67
+ watchChanged: async () => false,
68
+ untilReached: async () => false,
69
+ createCheckpoint: async (...args) => checkpoints.push(args),
70
+ }
71
+ const goalPolicy = {
72
+ runGoalChecks: async (_directory, _sessionID, job) => job,
73
+ applyGoalNoProgressGuard: async (_directory, _client, _sessionID, job) => job,
74
+ }
75
+ const scheduler = {
76
+ rememberSession: () => {},
77
+ scheduleDueWork: async (...args) => schedules.push(args),
78
+ }
79
+ const executor = createLoopExecutor({
80
+ workspace,
81
+ goalPolicy,
82
+ scheduler,
83
+ now: () => clock,
84
+ readState: async (directory, sessionID) => clone(states.get(key(directory, sessionID)) || { jobs: [] }),
85
+ writeState: async (directory, sessionID, state) => states.set(key(directory, sessionID), clone(state)),
86
+ appendLoopLog: async () => {},
87
+ runShellCommand: async () => ({ code: 0, stdout: "", stderr: "" }),
88
+ notifyJob: async (...args) => notifications.push(args),
89
+ toast: async (...args) => toasts.push(args),
90
+ fireSdk: () => Promise.resolve({}),
91
+ compactSession: async () => true,
92
+ activeRunCompletionFromMessages: async () => outcome,
93
+ busyRetryMs: 5_000,
94
+ })
95
+ const client = { session: { status: async () => ({ data: {} }), prompt: async () => ({ data: {} }), abort: async () => ({ data: {} }) } }
96
+ const directory = "/repo"
97
+ const sessionID = "empty-run"
98
+ states.set(key(directory, sessionID), { jobs: [{
99
+ id: "job", name: "job", action: "devam et", enabled: true, paused: false, intervalMs: 0, runCount: 0, maxRuns: 1, maxRuntimeMs: 0, timeoutMs: 0,
100
+ }] })
101
+
102
+ await executor.maybeRunDueJobs(directory, client, sessionID)
103
+ assert.equal(states.get(key(directory, sessionID)).jobs[0].runCount, 1)
104
+ assert.equal(states.get(key(directory, sessionID)).jobs[0].enabled, false, "max-runs is provisionally consumed at dispatch")
105
+ clock += 100
106
+ assert.equal(await executor.finalizeActiveRun(directory, client, sessionID), true)
107
+ let persisted = states.get(key(directory, sessionID)).jobs[0]
108
+ assert.equal(persisted.runCount, 0, "empty completed turn must refund runCount")
109
+ assert.equal(persisted.enabled, true, "empty max-runs attempt must be re-enabled")
110
+ assert.equal(persisted.emptyTurnCount, 1)
111
+ assert.equal(persisted.paused, false)
112
+ assert.equal(checkpoints.length, 0, "empty turn must not run successful finalization/checkpoint work")
113
+ assert.ok(schedules.some((entry) => entry[2] === sessionID && entry[3] === 5_000), "first empty turn retries with bounded delay")
114
+
115
+ executor.markSessionStatus(sessionID, "idle", clock)
116
+ await executor.maybeRunDueJobs(directory, client, sessionID)
117
+ clock += 100
118
+ assert.equal(await executor.finalizeActiveRun(directory, client, sessionID), true)
119
+ persisted = states.get(key(directory, sessionID)).jobs[0]
120
+ assert.equal(persisted.runCount, 0)
121
+ assert.equal(persisted.emptyTurnCount, 2)
122
+ assert.equal(persisted.paused, true, "second consecutive empty completed turn must fail safe instead of looping forever")
123
+ assert.ok(notifications.some((entry) => entry[1]?.id === "job" && entry[2] === "empty_turn"))
124
+ assert.ok(toasts.some((entry) => /paused after 2 consecutive completed assistant turns/i.test(String(entry[1]))))
125
+
126
+ const recoverySession = "empty-then-success"
127
+ outcome = "empty"
128
+ states.set(key(directory, recoverySession), { jobs: [{
129
+ id: "recover", name: "recover", action: "continue", enabled: true, paused: false, intervalMs: 0, runCount: 0, maxRuns: 0, maxRuntimeMs: 0, timeoutMs: 0,
130
+ }] })
131
+ await executor.maybeRunDueJobs(directory, client, recoverySession)
132
+ clock += 100
133
+ await executor.finalizeActiveRun(directory, client, recoverySession)
134
+ assert.equal(states.get(key(directory, recoverySession)).jobs[0].emptyTurnCount, 1)
135
+ executor.markSessionStatus(recoverySession, "idle", clock)
136
+ outcome = "completed"
137
+ await executor.maybeRunDueJobs(directory, client, recoverySession)
138
+ clock += 100
139
+ await executor.finalizeActiveRun(directory, client, recoverySession)
140
+ const recovered = states.get(key(directory, recoverySession)).jobs[0]
141
+ assert.equal(recovered.emptyTurnCount, 0, "a meaningful completed turn resets the consecutive empty streak")
142
+ assert.equal(recovered.lastFailureReason, undefined)
143
+ assert.equal(recovered.runCount, 1)
144
+ assert.ok(checkpoints.some((entry) => entry[2]?.id === "recover"), "meaningful completion still follows normal finalization")
145
+
146
+ executor.disposeSession(sessionID)
147
+ executor.disposeSession(recoverySession)
148
+ clearSessionActivity(sessionID)
149
+ clearSessionActivity(recoverySession)
150
+ console.log("empty-turn recovery tests passed")
@@ -61,7 +61,7 @@ assert.equal(compactTuiCommandName("other"), undefined)
61
61
  "/tmp", messagesClient([{ info: { role: "assistant", time: { created: 50, completed: 90 } } }]), "old", { startedAt: 100 },
62
62
  ), "incomplete")
63
63
  assert.equal(await activeRunCompletionFromMessages(
64
- "/tmp", messagesClient([{ info: { role: "assistant", time: { created: 110, completed: 120 } } }]), "done", { startedAt: 100 },
64
+ "/tmp", messagesClient([{ info: { role: "assistant", text: "done", time: { created: 110, completed: 120 } } }]), "done", { startedAt: 100 },
65
65
  ), "completed")
66
66
  }
67
67
 
@@ -118,7 +118,7 @@ assert.equal(compactTuiCommandName("other"), undefined)
118
118
  session: {
119
119
  messages: async (args) => {
120
120
  calls.push(["messages", args])
121
- return { data: [{ info: { role: "assistant", time: { created: 10, completed: 20 } } }] }
121
+ return { data: [{ info: { role: "assistant", text: "adapter completed", time: { created: 10, completed: 20 } } }] }
122
122
  },
123
123
  },
124
124
  }