@chorus-aidlc/chorus-openclaw-plugin 0.10.0 → 0.11.1
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/dist/connection-state.d.ts +35 -0
- package/dist/connection-state.d.ts.map +1 -0
- package/dist/connection-state.js +52 -0
- package/dist/connection-state.js.map +1 -0
- package/dist/control-handler.d.ts +73 -0
- package/dist/control-handler.d.ts.map +1 -0
- package/dist/control-handler.js +135 -0
- package/dist/control-handler.js.map +1 -0
- package/dist/daemon-client.d.ts +203 -0
- package/dist/daemon-client.d.ts.map +1 -0
- package/dist/daemon-client.js +469 -0
- package/dist/daemon-client.js.map +1 -0
- package/dist/daemon-rest-client.d.ts +86 -0
- package/dist/daemon-rest-client.d.ts.map +1 -0
- package/dist/daemon-rest-client.js +196 -0
- package/dist/daemon-rest-client.js.map +1 -0
- package/dist/event-router.d.ts +31 -6
- package/dist/event-router.d.ts.map +1 -1
- package/dist/event-router.js +58 -27
- package/dist/event-router.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +106 -7
- package/dist/index.js.map +1 -1
- package/dist/lineage.d.ts +44 -0
- package/dist/lineage.d.ts.map +1 -0
- package/dist/lineage.js +116 -0
- package/dist/lineage.js.map +1 -0
- package/dist/mcp-registration.d.ts.map +1 -1
- package/dist/mcp-registration.js +5 -4
- package/dist/mcp-registration.js.map +1 -1
- package/dist/sse-listener.d.ts +34 -0
- package/dist/sse-listener.d.ts.map +1 -1
- package/dist/sse-listener.js +78 -4
- package/dist/sse-listener.js.map +1 -1
- package/dist/wake.d.ts +20 -0
- package/dist/wake.d.ts.map +1 -1
- package/dist/wake.js +56 -0
- package/dist/wake.js.map +1 -1
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/chorus/SKILL.md +39 -8
- package/skills/code-reviewer/SKILL.md +127 -0
- package/skills/develop/SKILL.md +3 -1
- package/skills/idea/SKILL.md +1 -1
- package/skills/openspec-aware/SKILL.md +1 -1
- package/skills/proposal/SKILL.md +1 -1
- package/skills/proposal-reviewer/SKILL.md +1 -1
- package/skills/quick-dev/SKILL.md +1 -1
- package/skills/review/SKILL.md +5 -5
- package/skills/task-reviewer/SKILL.md +1 -1
- package/skills/yolo/SKILL.md +26 -1
- package/src/connection-state.ts +66 -0
- package/src/control-handler.ts +219 -0
- package/src/daemon-client.ts +622 -0
- package/src/daemon-rest-client.ts +312 -0
- package/src/event-router.ts +103 -33
- package/src/index.ts +113 -8
- package/src/lineage.ts +157 -0
- package/src/mcp-registration.ts +6 -19
- package/src/openclaw-sdk.d.ts +232 -1
- package/src/sse-listener.ts +117 -5
- package/src/wake.ts +69 -26
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
// packages/openclaw-plugin/src/daemon-client.ts
|
|
2
|
+
// The OpenClaw in-process host's bidirectional daemon behavior — the in-process
|
|
3
|
+
// analog of the chorus CLI host (cli/waker.mjs + cli/daemon.mjs), re-mapped from a
|
|
4
|
+
// spawned `claude` subprocess to a single `runtime.agent.runEmbeddedAgent(...)` call.
|
|
5
|
+
//
|
|
6
|
+
// Re-mapping table (verified against ../openclaw, 2026-06-20):
|
|
7
|
+
// | Concern | CLI host | this client |
|
|
8
|
+
// | run an agent | spawn `claude` subprocess | runEmbeddedAgent(params) in-process |
|
|
9
|
+
// | observe messages | parse stream-json lines | onAssistantMessageStart/onBlockReply |
|
|
10
|
+
// | | | /onToolResult callbacks (params.ts) |
|
|
11
|
+
// | mid-run interrupt | SIGINT→SIGKILL the proc group | AbortController.abort() → abortSignal |
|
|
12
|
+
// | crash vs user-abort | non-zero exit code | promise rejects / result.meta.aborted|
|
|
13
|
+
// | resume same session | `claude --resume <directIdea>` | sessionKey from business key → |
|
|
14
|
+
// | | | getSessionEntry resolves sessionId |
|
|
15
|
+
// The server-facing payloads are IDENTICAL across hosts (that is why they live in the
|
|
16
|
+
// shared daemon-rest-client). This module owns only the host-side concerns: the
|
|
17
|
+
// run wrapper, the abort/execution registries, the transcript content filter, the
|
|
18
|
+
// session mapping, and the at-most-once turn dispatch.
|
|
19
|
+
//
|
|
20
|
+
// VERIFIED runEmbeddedAgent surface (../openclaw):
|
|
21
|
+
// - abortSignal?: AbortSignal params.ts:167
|
|
22
|
+
// - onAssistantMessageStart?: () => void|Promise<void> params.ts:190 (ZERO-arg)
|
|
23
|
+
// - onBlockReply?: (BlockReplyPayload{text?,isReasoning?}) params.ts:191 / payloads.ts:1-11
|
|
24
|
+
// - onToolResult?: (ReplyPayload) params.ts:201 (tool internals — NOT transcript)
|
|
25
|
+
// - onReasoningStream?: (...) params.ts:195 (thinking — NOT transcript)
|
|
26
|
+
// - result.meta.aborted?: boolean types.ts:140 (user-abort marker)
|
|
27
|
+
// - getSessionEntry({sessionKey,agentId}) → SessionEntry? store.ts:210
|
|
28
|
+
// - resolveSessionFilePath(sessionId, entry?, opts?) paths.ts:267
|
|
29
|
+
const NOOP_LOGGER = { info() { }, warn() { }, error() { } };
|
|
30
|
+
/** Resource kinds the server's DaemonExecution accepts (mirrors waker.mjs). */
|
|
31
|
+
const EXECUTION_ENTITY_TYPES = new Set(["task", "idea", "proposal", "document", "daemon_session"]);
|
|
32
|
+
/**
|
|
33
|
+
* Extract finalized assistant VISIBLE text from an `onBlockReply` payload, or null to
|
|
34
|
+
* skip. Mirrors the CLI host's stream-json filter (upload-hooks.mjs
|
|
35
|
+
* `extractTranscriptText`): keep only finalized assistant text; DROP reasoning/thinking
|
|
36
|
+
* (`isReasoning`) — verified the flag exists at ../openclaw/src/agents/
|
|
37
|
+
* embedded-agent-payloads.ts:7 and is set true for reasoning blocks
|
|
38
|
+
* (embedded-agent-subscribe.handlers.messages.ts:915). Tool internals never reach here
|
|
39
|
+
* (they arrive on `onToolResult`, which the client does not post). Never throws.
|
|
40
|
+
*/
|
|
41
|
+
export function extractBlockReplyText(payload) {
|
|
42
|
+
if (!payload || typeof payload !== "object")
|
|
43
|
+
return null;
|
|
44
|
+
// Reasoning/thinking blocks are internal — not user-visible transcript.
|
|
45
|
+
if (payload.isReasoning)
|
|
46
|
+
return null;
|
|
47
|
+
const text = typeof payload.text === "string" ? payload.text : "";
|
|
48
|
+
if (!text.trim())
|
|
49
|
+
return null;
|
|
50
|
+
return { role: "assistant", text };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The OpenClaw in-process daemon client. One instance per plugin process; the entry
|
|
54
|
+
* injects the shared REST client + the host run-context resolver + the re-dispatch
|
|
55
|
+
* hook, and wires this client's `controlHooks` into the control handler.
|
|
56
|
+
*/
|
|
57
|
+
export class OpenClawDaemonClient {
|
|
58
|
+
restClient;
|
|
59
|
+
resolveRunContext;
|
|
60
|
+
redispatch;
|
|
61
|
+
buildTurnPrompt;
|
|
62
|
+
logger;
|
|
63
|
+
/** AbortController per in-flight run, keyed `entityType:entityUuid`. */
|
|
64
|
+
aborts = new Map();
|
|
65
|
+
/** Execution snapshot source — one entry per running/queued resource. */
|
|
66
|
+
executions = new Map();
|
|
67
|
+
/**
|
|
68
|
+
* At-most-once turn dedup, keyed `turn:<uuid>`. Shared by the live deliver_turn
|
|
69
|
+
* path and the reconnect backfill so a turn observed by either runs at most once
|
|
70
|
+
* (mirrors the CLI host's `seen` set; backfill.mjs).
|
|
71
|
+
*/
|
|
72
|
+
seenTurns = new Set();
|
|
73
|
+
runCounter = 0;
|
|
74
|
+
constructor(opts) {
|
|
75
|
+
this.restClient = opts.restClient;
|
|
76
|
+
this.resolveRunContext = opts.resolveRunContext;
|
|
77
|
+
this.redispatch = opts.redispatch;
|
|
78
|
+
this.buildTurnPrompt = opts.buildTurnPrompt;
|
|
79
|
+
this.logger = opts.logger ?? NOOP_LOGGER;
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Control hooks — wired into the T3 control handler (ControlBehaviorHooks).
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
/**
|
|
85
|
+
* The behavior hooks the control handler routes verified commands to. The handler
|
|
86
|
+
* owns the double-check (own connection + held entity); these implement the actual
|
|
87
|
+
* abort / re-dispatch / pending-turns-sweep.
|
|
88
|
+
*/
|
|
89
|
+
get controlHooks() {
|
|
90
|
+
return {
|
|
91
|
+
isEntityRunning: (entityType, entityUuid) => this.aborts.has(execKey(entityType, entityUuid)),
|
|
92
|
+
onInterrupt: (entityType, entityUuid) => this.interrupt(entityType, entityUuid),
|
|
93
|
+
onResume: (entityType, entityUuid) => this.resume(entityType, entityUuid),
|
|
94
|
+
onDeliverTurn: (turnUuid) => {
|
|
95
|
+
// Fire-and-forget: the control handler is synchronous; the sweep is async.
|
|
96
|
+
void this.deliverTurn(turnUuid);
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Abort the matching in-flight run (true mid-run stop via abortSignal) and mark it
|
|
102
|
+
* `interrupting` so the run's settle path reports reason=user (not crash). No-op when
|
|
103
|
+
* no run is registered (the handler's Check 2 already gates this, but we re-check so a
|
|
104
|
+
* direct caller is safe). Never throws.
|
|
105
|
+
*/
|
|
106
|
+
interrupt(entityType, entityUuid) {
|
|
107
|
+
const key = execKey(entityType, entityUuid);
|
|
108
|
+
const entry = this.aborts.get(key);
|
|
109
|
+
if (!entry) {
|
|
110
|
+
this.logger.info(`[Chorus] interrupt: no in-flight run for ${key}; ignoring`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
entry.interrupting = true;
|
|
114
|
+
try {
|
|
115
|
+
entry.controller.abort();
|
|
116
|
+
this.logger.info(`[Chorus] interrupt: aborted in-flight run for ${key}`);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
this.logger.warn(`[Chorus] interrupt: abort() failed for ${key}: ${err}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Re-dispatch a wake for the entity to continue the SAME session (the run is gone;
|
|
124
|
+
* resolveRunContext re-derives the same sessionKey → getSessionEntry resolves the
|
|
125
|
+
* existing sessionId). The control handler has no prompt; we synthesize a minimal
|
|
126
|
+
* resume prompt. Continues under the same business key (directIdea/entity).
|
|
127
|
+
*/
|
|
128
|
+
resume(entityType, entityUuid) {
|
|
129
|
+
this.logger.info(`[Chorus] resume: re-dispatching wake for ${entityType}:${entityUuid}`);
|
|
130
|
+
this.redispatch({
|
|
131
|
+
prompt: `[Chorus] Your previous run for this ${entityType} was interrupted by a human and is now being resumed. ` +
|
|
132
|
+
`Continue where you left off (entityType: ${entityType}, entityUuid: ${entityUuid}).`,
|
|
133
|
+
contextKey: `chorus:resume:${entityUuid}`,
|
|
134
|
+
entityType,
|
|
135
|
+
entityUuid,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Read connection-scoped pending turns and run the unstarted human_instruction turn.
|
|
140
|
+
* With a `turnUuid` run PRECISELY that one (live deliver_turn); without one sweep all
|
|
141
|
+
* (reconnect backfill). Idempotent via the shared `seenTurns` set keyed `turn:<uuid>`.
|
|
142
|
+
* Never throws into the caller. Mirrors backfill.mjs `backfillPendingTurns`.
|
|
143
|
+
*/
|
|
144
|
+
async deliverTurn(onlyTurnUuid) {
|
|
145
|
+
const result = await this.restClient.readPendingTurns();
|
|
146
|
+
if (!result.ok || !result.data) {
|
|
147
|
+
// Nothing to read yet (skipped) or a logged failure — nothing to dispatch.
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
let dispatched = 0;
|
|
151
|
+
for (const turn of result.data.turns) {
|
|
152
|
+
if (!turn || typeof turn.turnUuid !== "string")
|
|
153
|
+
continue;
|
|
154
|
+
// Single-turn precision: when a uuid was announced, run ONLY it.
|
|
155
|
+
if (onlyTurnUuid && turn.turnUuid !== onlyTurnUuid)
|
|
156
|
+
continue;
|
|
157
|
+
const seenKey = `turn:${turn.turnUuid}`;
|
|
158
|
+
if (this.seenTurns.has(seenKey))
|
|
159
|
+
continue;
|
|
160
|
+
this.seenTurns.add(seenKey);
|
|
161
|
+
dispatched++;
|
|
162
|
+
// Reconstruct the execution entity DIRECTLY from the turn's own ids (mirrors
|
|
163
|
+
// cli/event-router.mjs dispatchPendingTurn): an idea-anchored conversation
|
|
164
|
+
// reports against the real idea (`idea:<directIdeaUuid>`), an ad-hoc conversation
|
|
165
|
+
// against itself (`daemon_session:<sessionId>`). entityType MUST be set — without
|
|
166
|
+
// it entityOf() returns null, so the run reports no execution row (invisible in
|
|
167
|
+
// the UI) and registers no AbortController (uninterruptible). entityUuid aligns
|
|
168
|
+
// with the session business key so the report anchor, the OpenClaw session, and
|
|
169
|
+
// the per-session UI match key are all the same value.
|
|
170
|
+
const directIdeaUuid = typeof turn.directIdeaUuid === "string" ? turn.directIdeaUuid : null;
|
|
171
|
+
this.redispatch({
|
|
172
|
+
prompt: this.buildTurnPrompt(turn),
|
|
173
|
+
contextKey: `chorus:deliver_turn:${turn.turnUuid}`,
|
|
174
|
+
entityType: directIdeaUuid ? "idea" : "daemon_session",
|
|
175
|
+
entityUuid: directIdeaUuid ?? turn.sessionId,
|
|
176
|
+
directIdeaUuid,
|
|
177
|
+
turnUuid: turn.turnUuid,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (dispatched > 0) {
|
|
181
|
+
const scope = onlyTurnUuid ? `turn ${onlyTurnUuid}` : `${dispatched} pending turn(s)`;
|
|
182
|
+
this.logger.info(`[Chorus] deliver: re-derived ${scope} from the turn table`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** Reconnect backfill: full connection-scoped pending-turns sweep (no single-turn filter). */
|
|
186
|
+
async onReconnect() {
|
|
187
|
+
await this.deliverTurn();
|
|
188
|
+
}
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// The wake run wrapper.
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
/**
|
|
193
|
+
* Run one wake via runEmbeddedAgent with full daemon reporting. Never throws — a
|
|
194
|
+
* wake failure is logged + reported (crash), never propagated (the SSE service must
|
|
195
|
+
* stay alive). Fire-and-forget from the caller's perspective; returns a promise the
|
|
196
|
+
* tests can await.
|
|
197
|
+
*
|
|
198
|
+
* Lifecycle (mirrors waker.mjs):
|
|
199
|
+
* 1. resolve run context (drop on null);
|
|
200
|
+
* 2. derive sessionKey from the business key → getSessionEntry → sessionId/sessionFile;
|
|
201
|
+
* 3. register the AbortController + mark the execution running → turnAdvance(running)
|
|
202
|
+
* + execution-state snapshot;
|
|
203
|
+
* 4. run with transcript callbacks (onBlockReply → post {role:"assistant", text});
|
|
204
|
+
* 5. on settle → turnAdvance(ended) + snapshot; classify interrupt(user) vs crash;
|
|
205
|
+
* 6. finally → deregister the controller + drop the execution row + snapshot.
|
|
206
|
+
*/
|
|
207
|
+
async runWake(req) {
|
|
208
|
+
const { prompt, contextKey } = req;
|
|
209
|
+
const entity = entityOf(req);
|
|
210
|
+
const key = entity ? execKey(entity.entityType, entity.entityUuid) : null;
|
|
211
|
+
const rootIdeaUuid = req.rootIdeaUuid ?? null;
|
|
212
|
+
// Session anchor = the business key: directIdeaUuid when present, else the entity
|
|
213
|
+
// uuid (quick task / standalone doc / ad-hoc daemon_session). This is the `sessionId`
|
|
214
|
+
// used in ALL reports (turn-advance/transcript) — NOT the OpenClaw sessionKey, which
|
|
215
|
+
// is the agent's queue key. Two distinct identifiers (see deriveSessionId / sessionKey).
|
|
216
|
+
const reportSessionId = deriveSessionId(req);
|
|
217
|
+
if (!reportSessionId) {
|
|
218
|
+
this.logger.warn(`[Chorus] Wake DROPPED — no session id (no directIdea/entity) for contextKey=${contextKey}`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
let ctx;
|
|
222
|
+
try {
|
|
223
|
+
ctx = this.resolveRunContext();
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
this.logger.warn(`[Chorus] Wake DROPPED — run-context resolution failed (${contextKey}): ${err}`);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (!ctx) {
|
|
230
|
+
this.logger.warn(`[Chorus] Wake DROPPED — no resolvable session/agent runtime on this host (${contextKey}).`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
// Resolve the EXISTING session for the business key so the wake continues the same
|
|
234
|
+
// conversation (resume / deliver_turn re-enter it). The OpenClaw sessionKey is
|
|
235
|
+
// derived deterministically from the business key, so a later resume re-derives the
|
|
236
|
+
// same key and getSessionEntry returns the same sessionId/sessionFile.
|
|
237
|
+
const sessionKey = deriveSessionKey(reportSessionId, ctx.sessionKey);
|
|
238
|
+
let sessionId;
|
|
239
|
+
let sessionFile;
|
|
240
|
+
try {
|
|
241
|
+
const sessionEntry = ctx.agent.session.getSessionEntry({ sessionKey, agentId: ctx.agentId });
|
|
242
|
+
// No existing OpenClaw session for this key (the FIRST wake on the business key):
|
|
243
|
+
// open a NEW session whose id is the business key itself. The business key
|
|
244
|
+
// (directIdeaUuid / entityUuid / ad-hoc sessionId) is always a uuid, which
|
|
245
|
+
// satisfies OpenClaw's SAFE_SESSION_ID_RE (`^[a-z0-9][a-z0-9._-]{0,127}$`).
|
|
246
|
+
// The run id (`nextRunId`) is NOT a valid session id — it embeds the colon-laden
|
|
247
|
+
// contextKey, which `resolveSessionFilePath` rejects with "Invalid session ID" —
|
|
248
|
+
// so it must never be used as the session id. Reusing the business key here also
|
|
249
|
+
// keeps continuity: a later resume re-derives the SAME sessionKey, and once this
|
|
250
|
+
// first run has persisted the session, getSessionEntry returns this same id.
|
|
251
|
+
sessionId = sessionEntry?.sessionId ?? reportSessionId;
|
|
252
|
+
sessionFile = ctx.agent.session.resolveSessionFilePath(sessionId, sessionEntry?.sessionFile ? { sessionFile: sessionEntry.sessionFile } : undefined, { agentId: ctx.agentId });
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
this.logger.warn(`[Chorus] Wake DROPPED — session resolution failed (${contextKey}): ${err}`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const controller = new AbortController();
|
|
259
|
+
const abortEntry = { controller, interrupting: false };
|
|
260
|
+
if (key)
|
|
261
|
+
this.aborts.set(key, abortEntry);
|
|
262
|
+
// Mark RUNNING + report turn-advance(running) + execution snapshot.
|
|
263
|
+
if (entity && key) {
|
|
264
|
+
this.executions.set(key, {
|
|
265
|
+
entityType: entity.entityType,
|
|
266
|
+
entityUuid: entity.entityUuid,
|
|
267
|
+
rootIdeaUuid,
|
|
268
|
+
status: "running",
|
|
269
|
+
startedAt: new Date().toISOString(),
|
|
270
|
+
});
|
|
271
|
+
this.emitExecutionSnapshot();
|
|
272
|
+
}
|
|
273
|
+
let advancedToRunning = false;
|
|
274
|
+
await this.advanceTurn(reportSessionId, "running", entity);
|
|
275
|
+
advancedToRunning = true;
|
|
276
|
+
const runId = this.nextRunId(contextKey);
|
|
277
|
+
this.logger.info(`[Chorus] Waking agent via embedded run (sessionKey=${sessionKey}, sessionId=${reportSessionId}, ` +
|
|
278
|
+
`model=${ctx.modelRef ? `${ctx.modelRef.provider}/${ctx.modelRef.model}` : "host-default"}, contextKey=${contextKey})`);
|
|
279
|
+
let aborted = false;
|
|
280
|
+
let crashed = false;
|
|
281
|
+
try {
|
|
282
|
+
const result = await ctx.agent.runEmbeddedAgent({
|
|
283
|
+
sessionId,
|
|
284
|
+
sessionKey,
|
|
285
|
+
agentId: ctx.agentId,
|
|
286
|
+
trigger: "manual",
|
|
287
|
+
sessionFile,
|
|
288
|
+
...(ctx.agentDir ? { agentDir: ctx.agentDir } : {}),
|
|
289
|
+
workspaceDir: ctx.workspaceDir,
|
|
290
|
+
config: ctx.config,
|
|
291
|
+
prompt,
|
|
292
|
+
timeoutMs: ctx.timeoutMs,
|
|
293
|
+
runId,
|
|
294
|
+
disableMessageTool: true,
|
|
295
|
+
abortSignal: controller.signal,
|
|
296
|
+
// Streaming transcript: post ONLY finalized assistant visible text. Reasoning
|
|
297
|
+
// (onReasoningStream / isReasoning blocks) and tool internals (onToolResult) are
|
|
298
|
+
// intentionally NOT posted (match the CLI host's stream-json filter).
|
|
299
|
+
onBlockReply: (payload) => {
|
|
300
|
+
const msg = extractBlockReplyText(payload);
|
|
301
|
+
if (msg)
|
|
302
|
+
void this.postTranscript(reportSessionId, [msg]);
|
|
303
|
+
},
|
|
304
|
+
...(ctx.modelRef ? { provider: ctx.modelRef.provider, model: ctx.modelRef.model } : {}),
|
|
305
|
+
});
|
|
306
|
+
// result.meta.aborted distinguishes a clean abort from a normal completion
|
|
307
|
+
// (types.ts:140). An abort flagged here OR an interrupt requested → user-abort.
|
|
308
|
+
aborted = result?.meta?.aborted === true || abortEntry.interrupting;
|
|
309
|
+
}
|
|
310
|
+
catch (err) {
|
|
311
|
+
// The run rejected. If an interrupt was requested, it's a user abort; otherwise
|
|
312
|
+
// it's an unexpected crash.
|
|
313
|
+
if (abortEntry.interrupting || controller.signal.aborted) {
|
|
314
|
+
aborted = true;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
crashed = true;
|
|
318
|
+
}
|
|
319
|
+
this.logger.warn(`[Chorus] Wake turn ${crashed ? "crashed" : "aborted"} (sessionId=${reportSessionId}, ${contextKey}): ` +
|
|
320
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
// Deregister FIRST so a stale controller can never abort a later run for the
|
|
324
|
+
// same entity (spec: "A settled run deregisters its controller").
|
|
325
|
+
if (key)
|
|
326
|
+
this.aborts.delete(key);
|
|
327
|
+
}
|
|
328
|
+
// Turn lifecycle: advance running→ended regardless of outcome (a turn ends whether
|
|
329
|
+
// clean, aborted, or crashed). Guarded on advancedToRunning so a never-started wake
|
|
330
|
+
// never attempts an illegal pending→ended transition.
|
|
331
|
+
if (advancedToRunning) {
|
|
332
|
+
await this.advanceTurn(reportSessionId, "ended", entity);
|
|
333
|
+
}
|
|
334
|
+
// Interrupt-vs-crash reporting (entity-keyed — only for a reportable resource).
|
|
335
|
+
if (entity) {
|
|
336
|
+
if (aborted) {
|
|
337
|
+
await this.report(entity, "user");
|
|
338
|
+
}
|
|
339
|
+
else if (crashed) {
|
|
340
|
+
await this.report(entity, "crash");
|
|
341
|
+
}
|
|
342
|
+
// A clean completion reports nothing (mirrors waker.mjs).
|
|
343
|
+
}
|
|
344
|
+
// Drop the execution row + emit a fresh snapshot (absence == ended server-side).
|
|
345
|
+
if (key && this.executions.delete(key)) {
|
|
346
|
+
this.emitExecutionSnapshot();
|
|
347
|
+
}
|
|
348
|
+
this.logger.info(`[Chorus] Wake complete (sessionId=${reportSessionId}, contextKey=${contextKey}, ` +
|
|
349
|
+
`outcome=${aborted ? "interrupted" : crashed ? "crashed" : "completed"})`);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Mark a resource QUEUED and emit a snapshot. Called before a wake actually runs
|
|
353
|
+
* (e.g. when it sits behind a same-session run) so the server sees it waiting. The
|
|
354
|
+
* running transition in `runWake` overwrites it. Never throws.
|
|
355
|
+
*/
|
|
356
|
+
markQueued(req) {
|
|
357
|
+
const entity = entityOf(req);
|
|
358
|
+
if (!entity)
|
|
359
|
+
return;
|
|
360
|
+
const key = execKey(entity.entityType, entity.entityUuid);
|
|
361
|
+
const existing = this.executions.get(key);
|
|
362
|
+
// Don't downgrade a running resource to queued if a duplicate dispatch arrives.
|
|
363
|
+
if (existing && existing.status === "running")
|
|
364
|
+
return;
|
|
365
|
+
this.executions.set(key, {
|
|
366
|
+
entityType: entity.entityType,
|
|
367
|
+
entityUuid: entity.entityUuid,
|
|
368
|
+
rootIdeaUuid: req.rootIdeaUuid ?? null,
|
|
369
|
+
status: "queued",
|
|
370
|
+
startedAt: existing?.startedAt ?? null,
|
|
371
|
+
});
|
|
372
|
+
this.emitExecutionSnapshot();
|
|
373
|
+
}
|
|
374
|
+
// ---------------------------------------------------------------------------
|
|
375
|
+
// Internal reporting helpers — never throw into the wake path.
|
|
376
|
+
// ---------------------------------------------------------------------------
|
|
377
|
+
buildExecutionSnapshot() {
|
|
378
|
+
return [...this.executions.values()].map((e) => ({
|
|
379
|
+
entityType: e.entityType,
|
|
380
|
+
entityUuid: e.entityUuid,
|
|
381
|
+
rootIdeaUuid: e.rootIdeaUuid,
|
|
382
|
+
status: e.status,
|
|
383
|
+
startedAt: e.startedAt,
|
|
384
|
+
}));
|
|
385
|
+
}
|
|
386
|
+
/** Fire-and-forget execution-state snapshot. Never throws. */
|
|
387
|
+
emitExecutionSnapshot() {
|
|
388
|
+
void this.restClient.executionState({ executions: this.buildExecutionSnapshot() });
|
|
389
|
+
}
|
|
390
|
+
async advanceTurn(sessionId, status, entity) {
|
|
391
|
+
try {
|
|
392
|
+
await this.restClient.turnAdvance({
|
|
393
|
+
sessionId,
|
|
394
|
+
status,
|
|
395
|
+
entityType: entity?.entityType ?? null,
|
|
396
|
+
entityUuid: entity?.entityUuid ?? null,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
catch (err) {
|
|
400
|
+
this.logger.warn(`[Chorus] advanceTurn failed for session ${sessionId} → ${status}: ${err}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async postTranscript(sessionId, messages) {
|
|
404
|
+
try {
|
|
405
|
+
await this.restClient.transcript({ sessionId, messages });
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
this.logger.warn(`[Chorus] transcript post failed for session ${sessionId}: ${err}`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async report(entity, reason) {
|
|
412
|
+
try {
|
|
413
|
+
await this.restClient.reportInterrupt({
|
|
414
|
+
entityType: entity.entityType,
|
|
415
|
+
entityUuid: entity.entityUuid,
|
|
416
|
+
reason,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
catch (err) {
|
|
420
|
+
this.logger.warn(`[Chorus] reportInterrupt failed for ${entity.entityType}:${entity.entityUuid} (${reason}): ${err}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
nextRunId(contextKey) {
|
|
424
|
+
this.runCounter += 1;
|
|
425
|
+
return `chorus-wake-${this.runCounter}-${contextKey}`;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// ---------------------------------------------------------------------------
|
|
429
|
+
// Pure helpers (exported for unit testing).
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
/** Registry key for an entity. */
|
|
432
|
+
export function execKey(entityType, entityUuid) {
|
|
433
|
+
return `${entityType}:${entityUuid}`;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* The reportable resource for a wake — `{ entityType, entityUuid }` — or null when it
|
|
437
|
+
* has no recognized target (mirrors waker.mjs `#entityOf`).
|
|
438
|
+
*/
|
|
439
|
+
export function entityOf(req) {
|
|
440
|
+
const { entityType, entityUuid } = req;
|
|
441
|
+
if (typeof entityType === "string" &&
|
|
442
|
+
typeof entityUuid === "string" &&
|
|
443
|
+
entityUuid.length > 0 &&
|
|
444
|
+
EXECUTION_ENTITY_TYPES.has(entityType)) {
|
|
445
|
+
return { entityType, entityUuid };
|
|
446
|
+
}
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* The session BUSINESS KEY used in all reports (turn-advance/transcript): the
|
|
451
|
+
* directIdeaUuid when the entity has an idea ancestor, else the entity uuid. Mirrors
|
|
452
|
+
* the CLI host's `sessionId = directIdeaUuid ?? notification.entityUuid` (waker.mjs:286).
|
|
453
|
+
*/
|
|
454
|
+
export function deriveSessionId(req) {
|
|
455
|
+
return req.directIdeaUuid ?? req.entityUuid ?? null;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Derive the deterministic OpenClaw `sessionKey` for a wake from its business key. The
|
|
459
|
+
* OpenClaw session store is keyed by `sessionKey` (the agent queue key), NOT by the
|
|
460
|
+
* Chorus business id — so to make `resume`/`deliver_turn` continue the SAME OpenClaw
|
|
461
|
+
* session we must derive the SAME key from the SAME business id every time. We namespace
|
|
462
|
+
* the business id under the host's main-agent key so a Chorus wake gets its own stable
|
|
463
|
+
* lane that re-resolves identically across runs (the in-process analog of
|
|
464
|
+
* `claude --resume <directIdeaUuid>`, where the disk transcript is the stable anchor).
|
|
465
|
+
*/
|
|
466
|
+
export function deriveSessionKey(businessKey, mainSessionKey) {
|
|
467
|
+
return `${mainSessionKey}:chorus:${businessKey}`;
|
|
468
|
+
}
|
|
469
|
+
//# sourceMappingURL=daemon-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-client.js","sourceRoot":"","sources":["../src/daemon-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export interface DaemonRestLogger {
|
|
2
|
+
info: (msg: string) => void;
|
|
3
|
+
warn: (msg: string) => void;
|
|
4
|
+
error: (msg: string) => void;
|
|
5
|
+
}
|
|
6
|
+
/** A single transcript message — only `role` + visible `text` (no internals). */
|
|
7
|
+
export interface DaemonTranscriptMessage {
|
|
8
|
+
role: "user" | "assistant";
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
/** One execution-snapshot row, in the server's exact `execution-state` shape. */
|
|
12
|
+
export interface DaemonExecutionRow {
|
|
13
|
+
entityType: string;
|
|
14
|
+
entityUuid: string;
|
|
15
|
+
rootIdeaUuid: string | null;
|
|
16
|
+
status: "running" | "queued";
|
|
17
|
+
startedAt: string | null;
|
|
18
|
+
}
|
|
19
|
+
/** One unstarted (pending) turn read back from the turn table. */
|
|
20
|
+
export interface DaemonPendingTurn {
|
|
21
|
+
turnUuid: string;
|
|
22
|
+
sessionId: string;
|
|
23
|
+
directIdeaUuid: string | null;
|
|
24
|
+
trigger: string;
|
|
25
|
+
promptText: string | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Structured result of every client call. Mirrors the CLI client's
|
|
29
|
+
* `DaemonRestResult`: `ok` is true only on a 2xx (and, for reads, a well-formed
|
|
30
|
+
* body); `error` carries the (also-logged) failure cause; `skipped` marks an
|
|
31
|
+
* intentional non-call (e.g. no connection uuid yet); `data` holds parsed read
|
|
32
|
+
* payloads.
|
|
33
|
+
*/
|
|
34
|
+
export interface DaemonRestResult<TData = unknown> {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
status: number | null;
|
|
37
|
+
error?: string;
|
|
38
|
+
skipped?: boolean;
|
|
39
|
+
data?: TData;
|
|
40
|
+
}
|
|
41
|
+
export interface CreateDaemonRestClientOptions {
|
|
42
|
+
/** Chorus base URL (a trailing slash is normalized away). */
|
|
43
|
+
url: string;
|
|
44
|
+
/** `cho_` agent API key for Bearer auth. */
|
|
45
|
+
apiKey: string;
|
|
46
|
+
/**
|
|
47
|
+
* The daemon's registered connection uuid (learned from the SSE handshake),
|
|
48
|
+
* read LAZILY on every call so construction order does not matter. The
|
|
49
|
+
* connection-scoped operations (turnAdvance, executionState, reportInterrupt,
|
|
50
|
+
* readPendingTurns) require it; a null value skips the call (logged where the
|
|
51
|
+
* skip is unexpected, silent where it is a normal early state).
|
|
52
|
+
*/
|
|
53
|
+
getConnectionUuid?: () => string | null;
|
|
54
|
+
/** Injectable for tests (defaults to global fetch). */
|
|
55
|
+
fetchImpl?: typeof fetch;
|
|
56
|
+
logger?: DaemonRestLogger;
|
|
57
|
+
}
|
|
58
|
+
export interface DaemonRestClient {
|
|
59
|
+
turnAdvance(p: {
|
|
60
|
+
sessionId: string;
|
|
61
|
+
status: "running" | "ended";
|
|
62
|
+
entityType?: string | null;
|
|
63
|
+
entityUuid?: string | null;
|
|
64
|
+
}): Promise<DaemonRestResult>;
|
|
65
|
+
transcript(p: {
|
|
66
|
+
sessionId: string;
|
|
67
|
+
messages: DaemonTranscriptMessage[];
|
|
68
|
+
}): Promise<DaemonRestResult>;
|
|
69
|
+
executionState(p: {
|
|
70
|
+
executions: DaemonExecutionRow[];
|
|
71
|
+
}): Promise<DaemonRestResult>;
|
|
72
|
+
reportInterrupt(p: {
|
|
73
|
+
entityType: string;
|
|
74
|
+
entityUuid: string;
|
|
75
|
+
reason: "user" | "crash";
|
|
76
|
+
}): Promise<DaemonRestResult>;
|
|
77
|
+
readPendingTurns(): Promise<DaemonRestResult<{
|
|
78
|
+
turns: DaemonPendingTurn[];
|
|
79
|
+
}>>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build the shared daemon REST client. Inputs are entirely host-agnostic, which is
|
|
83
|
+
* exactly why the same surface serves both daemon hosts.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createDaemonRestClient(opts: CreateDaemonRestClientOptions): DaemonRestClient;
|
|
86
|
+
//# sourceMappingURL=daemon-rest-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-rest-client.d.ts","sourceRoot":"","sources":["../src/daemon-rest-client.ts"],"names":[],"mappings":"AA2CA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAID,iFAAiF;AACjF,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO;IAC/C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,6BAA6B;IAC5C,6DAA6D;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACxC,uDAAuD;IACvD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,CAAC,EAAE;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;QAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC9B,UAAU,CAAC,CAAC,EAAE;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,uBAAuB,EAAE,CAAC;KACrC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC9B,cAAc,CAAC,CAAC,EAAE;QAAE,UAAU,EAAE,kBAAkB,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnF,eAAe,CAAC,CAAC,EAAE;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC9B,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,CAAC;QAAE,KAAK,EAAE,iBAAiB,EAAE,CAAA;KAAE,CAAC,CAAC,CAAC;CAC/E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,6BAA6B,GAAG,gBAAgB,CAmL5F"}
|