@chorus-aidlc/chorus-openclaw-plugin 0.16.2 → 0.17.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/commands.js +5 -11
- package/dist/commands.js.map +1 -1
- package/dist/daemon-client.d.ts.map +1 -1
- package/dist/daemon-client.js +7 -4
- package/dist/daemon-client.js.map +1 -1
- package/dist/daemon-rest-client.d.ts +4 -1
- package/dist/daemon-rest-client.d.ts.map +1 -1
- package/dist/daemon-rest-client.js +31 -7
- package/dist/daemon-rest-client.js.map +1 -1
- package/dist/event-router.d.ts +9 -0
- package/dist/event-router.d.ts.map +1 -1
- package/dist/event-router.js +13 -2
- package/dist/event-router.js.map +1 -1
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/chorus/SKILL.md +2 -1
- package/skills/chorus-cli/SKILL.md +57 -0
- package/skills/code-reviewer/SKILL.md +1 -1
- package/skills/develop/SKILL.md +2 -2
- package/skills/docs/SKILL.md +1 -1
- package/skills/idea/SKILL.md +2 -1
- package/skills/openspec-aware/SKILL.md +90 -45
- package/skills/orchestrate/SKILL.md +129 -0
- package/skills/proposal/SKILL.md +2 -2
- package/skills/proposal-reviewer/SKILL.md +1 -1
- package/skills/quick-dev/SKILL.md +1 -1
- package/skills/review/SKILL.md +1 -1
- package/skills/task-reviewer/SKILL.md +1 -1
- package/skills/yolo/SKILL.md +3 -3
- package/src/commands.ts +21 -21
- package/src/daemon-client.ts +8 -4
- package/src/daemon-rest-client.ts +37 -9
- package/src/event-router.ts +13 -2
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
// The two files are kept in lock-step by the spec's "single source of truth for
|
|
15
15
|
// the payload shapes" requirement; a drift would be caught by T5 (live e2e).
|
|
16
16
|
//
|
|
17
|
-
// The
|
|
18
|
-
// cli/daemon-rest-client.mjs
|
|
17
|
+
// The operations and payload fields this client supports (kept aligned with
|
|
18
|
+
// cli/daemon-rest-client.mjs and accepted by src/app/api/daemon/*/route.ts):
|
|
19
19
|
// turnAdvance → POST /api/daemon/turn-advance
|
|
20
|
-
// { connectionUuid, sessionId, status,
|
|
20
|
+
// { connectionUuid, sessionId, status, turnUuid?,
|
|
21
|
+
// entityType?, entityUuid?, interruptedReason?,
|
|
22
|
+
// transcriptRelayError?, usage? }
|
|
21
23
|
// transcript → POST /api/daemon/transcript
|
|
22
24
|
// { sessionId, messages: [{ role, text }] }
|
|
23
25
|
// executionState → POST /api/daemon/execution-state
|
|
@@ -27,6 +29,8 @@
|
|
|
27
29
|
// startedAt|null }] }
|
|
28
30
|
// reportInterrupt → POST /api/daemon/report-interrupt
|
|
29
31
|
// { connectionUuid, entityType, entityUuid, reason }
|
|
32
|
+
// heartbeat → POST /api/daemon/connection-heartbeat
|
|
33
|
+
// { connectionUuid, connectedAt }
|
|
30
34
|
// readPendingTurns → GET /api/daemon/pending-turns?connectionUuid=…
|
|
31
35
|
// → { turns: [{ turnUuid, sessionId, directIdeaUuid, trigger,
|
|
32
36
|
// promptText }] }
|
|
@@ -129,6 +133,7 @@ export interface CreateDaemonRestClientOptions {
|
|
|
129
133
|
export interface DaemonRestClient {
|
|
130
134
|
turnAdvance(p: {
|
|
131
135
|
sessionId: string;
|
|
136
|
+
turnUuid?: string | null;
|
|
132
137
|
status: "running" | "ended" | "interrupted";
|
|
133
138
|
entityType?: string | null;
|
|
134
139
|
entityUuid?: string | null;
|
|
@@ -136,7 +141,7 @@ export interface DaemonRestClient {
|
|
|
136
141
|
transcriptRelayError?: string | null;
|
|
137
142
|
// Per-turn token usage (daemon-token-usage); sent only on a terminal edge.
|
|
138
143
|
usage?: TokenUsage | null;
|
|
139
|
-
}): Promise<DaemonRestResult
|
|
144
|
+
}): Promise<DaemonRestResult<{ turnUuid: string }>>;
|
|
140
145
|
transcript(p: {
|
|
141
146
|
sessionId: string;
|
|
142
147
|
messages: DaemonTranscriptMessage[];
|
|
@@ -172,13 +177,14 @@ export function createDaemonRestClient(opts: CreateDaemonRestClientOptions): Dae
|
|
|
172
177
|
* IDENTICAL across all four POST endpoints; only the `op` label and the path
|
|
173
178
|
* differ. Never throws — returns a structured {@link DaemonRestResult}.
|
|
174
179
|
*/
|
|
175
|
-
async function post(
|
|
180
|
+
async function post<TData = unknown>(
|
|
176
181
|
op: string,
|
|
177
182
|
path: string,
|
|
178
183
|
body: unknown,
|
|
179
184
|
successLog?: string,
|
|
180
185
|
context = "",
|
|
181
|
-
|
|
186
|
+
readData = false,
|
|
187
|
+
): Promise<DaemonRestResult<TData>> {
|
|
182
188
|
let response: Response;
|
|
183
189
|
try {
|
|
184
190
|
response = await fetchImpl(`${url}${path}`, {
|
|
@@ -198,8 +204,17 @@ export function createDaemonRestClient(opts: CreateDaemonRestClientOptions): Dae
|
|
|
198
204
|
logger.warn(`[Chorus] ${error}`);
|
|
199
205
|
return { ok: false, status: response.status, error };
|
|
200
206
|
}
|
|
207
|
+
let data: TData | undefined;
|
|
208
|
+
if (readData) {
|
|
209
|
+
try {
|
|
210
|
+
const parsed = (await response.json()) as { data?: TData } | null;
|
|
211
|
+
data = parsed && typeof parsed === "object" ? parsed.data : undefined;
|
|
212
|
+
} catch (err) {
|
|
213
|
+
logger.warn(`[Chorus] ${op} response correlation unavailable: ${err}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
201
216
|
if (successLog) logger.info(`[Chorus] ${successLog}`);
|
|
202
|
-
return { ok: true, status: response.status };
|
|
217
|
+
return { ok: true, status: response.status, ...(data !== undefined ? { data } : {}) };
|
|
203
218
|
}
|
|
204
219
|
|
|
205
220
|
return {
|
|
@@ -211,7 +226,7 @@ export function createDaemonRestClient(opts: CreateDaemonRestClientOptions): Dae
|
|
|
211
226
|
* normalized `usage` (daemon-token-usage) — byte-for-byte the CLI client's shape.
|
|
212
227
|
* Requires the connectionUuid.
|
|
213
228
|
*/
|
|
214
|
-
async turnAdvance({ sessionId, status, entityType, entityUuid, interruptedReason, transcriptRelayError, usage }) {
|
|
229
|
+
async turnAdvance({ sessionId, turnUuid, status, entityType, entityUuid, interruptedReason, transcriptRelayError, usage }) {
|
|
215
230
|
const connectionUuid = getConnectionUuid();
|
|
216
231
|
if (!connectionUuid) {
|
|
217
232
|
const error = `cannot advance turn for session ${sessionId} → ${status} — no connection uuid yet`;
|
|
@@ -225,6 +240,7 @@ export function createDaemonRestClient(opts: CreateDaemonRestClientOptions): Dae
|
|
|
225
240
|
connectionUuid,
|
|
226
241
|
sessionId,
|
|
227
242
|
status,
|
|
243
|
+
...(turnUuid ? { turnUuid } : {}),
|
|
228
244
|
// Only sent when BOTH are present, so the server never gets a partial linkage.
|
|
229
245
|
...(entityType && entityUuid ? { entityType, entityUuid } : {}),
|
|
230
246
|
// Only meaningful alongside status=interrupted; never sent otherwise.
|
|
@@ -234,12 +250,24 @@ export function createDaemonRestClient(opts: CreateDaemonRestClientOptions): Dae
|
|
|
234
250
|
// The whole normalized TokenUsage object, nested under `usage`, only on a terminal edge.
|
|
235
251
|
...(usage && isTerminal ? { usage } : {}),
|
|
236
252
|
};
|
|
237
|
-
|
|
253
|
+
const result = await post<{ turn?: { uuid?: unknown } }>(
|
|
238
254
|
"turn-advance",
|
|
239
255
|
"/api/daemon/turn-advance",
|
|
240
256
|
body,
|
|
241
257
|
`advanced turn for session ${sessionId} → ${status}`,
|
|
258
|
+
"",
|
|
259
|
+
status === "running",
|
|
242
260
|
);
|
|
261
|
+
const resolvedTurnUuid =
|
|
262
|
+
typeof result.data?.turn?.uuid === "string" ? result.data.turn.uuid : null;
|
|
263
|
+
if (status !== "running") {
|
|
264
|
+
const { data: _rawData, ...baseResult } = result;
|
|
265
|
+
return baseResult;
|
|
266
|
+
}
|
|
267
|
+
const { data: _rawData, ...baseResult } = result;
|
|
268
|
+
return resolvedTurnUuid
|
|
269
|
+
? { ...baseResult, data: { turnUuid: resolvedTurnUuid } }
|
|
270
|
+
: baseResult;
|
|
243
271
|
},
|
|
244
272
|
|
|
245
273
|
/**
|
package/src/event-router.ts
CHANGED
|
@@ -332,12 +332,23 @@ export class ChorusEventRouter {
|
|
|
332
332
|
);
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
+
/**
|
|
336
|
+
* An idea was assigned to this agent. The assignment ALREADY set the assignee, so there is
|
|
337
|
+
* nothing to claim — chorus_claim_idea would throw (AlreadyClaimedError, and a hard "Cannot
|
|
338
|
+
* claim an elaborated Idea" for the elaborated-backfill case). Do NOT emit a claim
|
|
339
|
+
* instruction; tell the already-assigned agent to review and advance from the idea's CURRENT
|
|
340
|
+
* stage, stopping at the human proposal/verify gates, and name the assigner via
|
|
341
|
+
* n.actorName / n.actorType. Mirrors the daemon's cli/prompts.mjs `idea_claimed` case
|
|
342
|
+
* (plugin voice; no headless preamble) — KEEP THE TWO WORDINGS IN SYNC.
|
|
343
|
+
*/
|
|
335
344
|
private handleIdeaClaimed(n: NotificationDetail, attr: WakeAttribution): void {
|
|
336
345
|
const mentionGuidance = this.buildMentionGuidance(n, "idea");
|
|
337
346
|
|
|
338
347
|
this.wakeWithHandoff(
|
|
339
|
-
`[Chorus] Idea '${n.entityTitle}'
|
|
340
|
-
`Use chorus_get_idea to review
|
|
348
|
+
`[Chorus] Idea '${n.entityTitle}' was assigned to you by ${n.actorName} (${n.actorType}) (ideaUuid: ${n.entityUuid}, projectUuid: ${n.projectUuid}). ` +
|
|
349
|
+
`You are now the assignee — no need to claim it. Use chorus_get_idea to review it, then advance it from its CURRENT stage: ` +
|
|
350
|
+
`if it is still elaborating, run or continue elaboration; if it is already elaborated, author the proposal. ` +
|
|
351
|
+
`Stop at the human proposal/verify gates and never merge automatically.\n` +
|
|
341
352
|
mentionGuidance,
|
|
342
353
|
this.contextKeyFor("idea_claimed", n.entityUuid),
|
|
343
354
|
n,
|