@veewo/claw 0.1.99 → 0.2.2
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/cli.js +470 -128
- package/dist/cli.js.map +1 -1
- package/dist/codex-driver.js +4 -0
- package/dist/codex-driver.js.map +1 -1
- package/dist/invocation-host.d.ts +1 -1
- package/dist/invocation-host.js +2 -1
- package/dist/invocation-host.js.map +1 -1
- package/dist/opencode-runner.js +2 -2
- package/dist/opencode-runner.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { spawn, spawnSync } from "node:child_process";
|
|
8
|
-
import { buildDirectWorkflowGuidance, DEFAULT_MAX_TASKS_TO_KEEP, checkProjectProtocol, ClawError, buildPlanWorkflowGuidance, shouldUsePlanHostIntegration, buildMemoryIndex, buildSessionStartDefaultPrompt, buildSessionStartRecoveredPrompt, editPlan, ensureProjectProtocol, enforceTaskRetention, findTaskDirectory, runDailyMaintenance, ingestTruth, initProject, getTemplateTaskDoneChoices, resolvePlanTemplateFile, resolveProjectContext, resolveWorkflowProjectContext, resolveSessionWorkflowContext, deleteSessionWorkflow, sweepExpiredSessionWorkflows, resolveSessionBoundPlan, resolveContext, resolveSeedPlanTemplate, searchMemoryAsync, warmProjectMemoryEmbedding, showPlan, createSubplan, switchTask, tryCaptureKnowledgeStop, claimKnowledgeFinalizationJob,
|
|
8
|
+
import { buildDirectWorkflowGuidance, buildKnowledgeDelegateDispatch, buildKnowledgeAssignmentTemplate, buildKnowledgeWriterAssignments, DEFAULT_MAX_TASKS_TO_KEEP, checkProjectProtocol, ClawError, buildPlanWorkflowGuidance, shouldUsePlanHostIntegration, buildMemoryIndex, buildSessionStartDefaultPrompt, buildSessionStartRecoveredPrompt, editPlan, ensureProjectProtocol, enforceTaskRetention, findTaskDirectory, runDailyMaintenance, ingestTruth, initProject, getTemplateTaskDoneChoices, resolvePlanTemplateFile, resolvePlanEffectiveConfig, resolveProjectContext, resolveWorkflowProjectContext, resolveSessionWorkflowContext, deleteSessionWorkflow, sweepExpiredSessionWorkflows, resolveSessionBoundPlan, resolveContext, resolveSeedPlanTemplate, searchMemoryAsync, warmProjectMemoryEmbedding, showPlan, createSubplan, switchTask, tryCaptureKnowledgeStop, claimKnowledgeFinalizationJob, doneKnowledgeFinalizationJob, readKnowledgeFinalizationJob, waitForKnowledgeFinalizationJobReady, listRetryableKnowledgeFinalizationJobs, normalizeTruthMarkdownEncoding, recordKnowledgeFinalizationResult, unbindSession, writePlan, } from "@veewo/claw-core";
|
|
9
9
|
import { buildCodexDriverEnvelope } from "./codex-driver.js";
|
|
10
10
|
import { checkCodexRuntime, resolveCodexSdkEntryPath } from "./codex-runtime.js";
|
|
11
11
|
import { extractLatestFinalAssistantMessage, extractTaskDoneConclusions, } from "./codex-transcript.js";
|
|
@@ -25,6 +25,7 @@ const TOP_LEVEL_COMMANDS = [
|
|
|
25
25
|
{ name: "subplan create [options]", summary: "Create a subplan nested under a parent task item." },
|
|
26
26
|
{ name: "switch-task --from <task> --to <task>", summary: "Switch the active task, carrying inherited context." },
|
|
27
27
|
{ name: "search [<query>] [options]", summary: "Recall project memory, truth, ADR, and declared docs." },
|
|
28
|
+
{ name: "knowledge <subcommand> [options]", summary: "Knowledge finalization lifecycle: wait, claim, done." },
|
|
28
29
|
{ name: "truth ingest [options]", summary: "Ingest a truth document under .claw/truth." },
|
|
29
30
|
{ name: "hook <event-name>", summary: "Emit host hook output (e.g. SessionStart)." },
|
|
30
31
|
];
|
|
@@ -311,6 +312,43 @@ const COMMAND_HELP = {
|
|
|
311
312
|
},
|
|
312
313
|
},
|
|
313
314
|
},
|
|
315
|
+
knowledge: {
|
|
316
|
+
usage: ["{script} knowledge <subcommand> [options]"],
|
|
317
|
+
description: "Session-bound lifecycle commands for a queued knowledge finalization job.",
|
|
318
|
+
subcommands: {
|
|
319
|
+
wait: {
|
|
320
|
+
usage: ["{script} knowledge wait --project-root <path> --finalize-id <id> [--timeout-ms <n>]"],
|
|
321
|
+
description: "Wait for Stop capture to create a knowledge finalization job. This command does not create or inspect session bindings.",
|
|
322
|
+
summary: "Wait until the finalization job exists.",
|
|
323
|
+
options: [
|
|
324
|
+
{ flag: "--project-root <path>", detail: "(required) Project that owns the pending finalization." },
|
|
325
|
+
{ flag: "--finalize-id <id>", detail: "(required) Stable finalization id returned by plan done." },
|
|
326
|
+
{ flag: "--timeout-ms <n>", detail: "Maximum wait in milliseconds (default 300000)." },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
claim: {
|
|
330
|
+
usage: ["{script} knowledge claim --job <path>"],
|
|
331
|
+
description: "Claim a queued or retryable job after its executor session has been bound.",
|
|
332
|
+
summary: "Claim a session-bound finalization job.",
|
|
333
|
+
options: [{ flag: "--job <path>", detail: "(required) Finalization job JSON path." }],
|
|
334
|
+
},
|
|
335
|
+
done: {
|
|
336
|
+
usage: [
|
|
337
|
+
"{script} knowledge done --job <path> --claim-token <token> --status succeeded --result <text>",
|
|
338
|
+
"{script} knowledge done --job <path> --claim-token <token> --status failed --error <text>",
|
|
339
|
+
],
|
|
340
|
+
description: "Persist the terminal result for a claimed knowledge finalization job.",
|
|
341
|
+
summary: "Complete a claimed finalization job.",
|
|
342
|
+
options: [
|
|
343
|
+
{ flag: "--job <path>", detail: "(required) Finalization job JSON path." },
|
|
344
|
+
{ flag: "--claim-token <token>", detail: "(required) Token returned by knowledge claim." },
|
|
345
|
+
{ flag: "--status succeeded|failed", detail: "(required) Terminal execution status." },
|
|
346
|
+
{ flag: "--result <text>", detail: "Required when status is succeeded." },
|
|
347
|
+
{ flag: "--error <text>", detail: "Required when status is failed." },
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
},
|
|
314
352
|
truth: {
|
|
315
353
|
usage: ["{script} truth <subcommand> [options]"],
|
|
316
354
|
description: "Truth document ingestion under .claw/truth.",
|
|
@@ -453,6 +491,9 @@ async function main() {
|
|
|
453
491
|
case "search":
|
|
454
492
|
await runSearch(args);
|
|
455
493
|
return;
|
|
494
|
+
case "knowledge":
|
|
495
|
+
runKnowledge(args);
|
|
496
|
+
return;
|
|
456
497
|
case "direct":
|
|
457
498
|
runDirect(args, effectiveHost);
|
|
458
499
|
return;
|
|
@@ -471,6 +512,18 @@ async function main() {
|
|
|
471
512
|
case "internal-knowledge-finalize":
|
|
472
513
|
await runInternalKnowledgeFinalize(args);
|
|
473
514
|
return;
|
|
515
|
+
case "internal-knowledge-capture":
|
|
516
|
+
await runInternalKnowledgeCapture(args, effectiveHost);
|
|
517
|
+
return;
|
|
518
|
+
case "internal-knowledge-complete":
|
|
519
|
+
runInternalKnowledgeComplete(args);
|
|
520
|
+
return;
|
|
521
|
+
case "internal-knowledge-claim":
|
|
522
|
+
runInternalKnowledgeClaim(args);
|
|
523
|
+
return;
|
|
524
|
+
case "internal-knowledge-fail":
|
|
525
|
+
runInternalKnowledgeFail(args);
|
|
526
|
+
return;
|
|
474
527
|
case "internal-embedding-warmup":
|
|
475
528
|
await runInternalEmbeddingWarmup(args);
|
|
476
529
|
return;
|
|
@@ -513,6 +566,87 @@ function runSession(args) {
|
|
|
513
566
|
removed: deleteSessionWorkflow(ownerSessionKey),
|
|
514
567
|
});
|
|
515
568
|
}
|
|
569
|
+
function runKnowledge(args) {
|
|
570
|
+
const subcommand = args.shift();
|
|
571
|
+
switch (subcommand) {
|
|
572
|
+
case "wait": {
|
|
573
|
+
const projectRoot = path.resolve(readRequiredFlag(args, "--project-root"));
|
|
574
|
+
const finalizeId = readRequiredFlag(args, "--finalize-id");
|
|
575
|
+
const timeoutMs = readOptionalNumber(args, "--timeout-ms") ?? 300_000;
|
|
576
|
+
assertNoRemainingArgs(args, "knowledge wait");
|
|
577
|
+
const { jobPath, job } = waitForKnowledgeFinalizationJobReady({
|
|
578
|
+
project: resolveProjectContext(projectRoot),
|
|
579
|
+
finalizeId,
|
|
580
|
+
timeoutMs,
|
|
581
|
+
});
|
|
582
|
+
printJson({
|
|
583
|
+
ok: true,
|
|
584
|
+
command: "knowledge.wait",
|
|
585
|
+
finalizeId: job.finalizeId,
|
|
586
|
+
status: job.status,
|
|
587
|
+
jobPath,
|
|
588
|
+
});
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
case "claim": {
|
|
592
|
+
const jobPath = readRequiredFlag(args, "--job");
|
|
593
|
+
assertNoRemainingArgs(args, "knowledge claim");
|
|
594
|
+
const job = claimKnowledgeFinalizationJob(jobPath);
|
|
595
|
+
const assignments = job ? buildKnowledgeWriterAssignments(job) : [];
|
|
596
|
+
const templatePath = job
|
|
597
|
+
? path.join(path.dirname(jobPath), `${job.finalizeId}.assignments.json`)
|
|
598
|
+
: undefined;
|
|
599
|
+
if (job && templatePath) {
|
|
600
|
+
fs.writeFileSync(templatePath, `${JSON.stringify(buildKnowledgeAssignmentTemplate({
|
|
601
|
+
assignments,
|
|
602
|
+
finalizeId: job.finalizeId,
|
|
603
|
+
version: CLI_VERSION,
|
|
604
|
+
}), null, 2)}\n`, "utf-8");
|
|
605
|
+
}
|
|
606
|
+
printJson({
|
|
607
|
+
ok: true,
|
|
608
|
+
command: "knowledge.claim",
|
|
609
|
+
claimed: Boolean(job),
|
|
610
|
+
...(job ? {
|
|
611
|
+
finalizeId: job.finalizeId,
|
|
612
|
+
claimToken: job.claimToken,
|
|
613
|
+
projectRoot: job.projectRoot,
|
|
614
|
+
writer: job.writer ?? null,
|
|
615
|
+
planPath: job.planPath,
|
|
616
|
+
reportPath: job.reportPath,
|
|
617
|
+
assignments,
|
|
618
|
+
templatePath,
|
|
619
|
+
} : {}),
|
|
620
|
+
});
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
case "done": {
|
|
624
|
+
const jobPath = readRequiredFlag(args, "--job");
|
|
625
|
+
const claimToken = readRequiredFlag(args, "--claim-token");
|
|
626
|
+
const status = readRequiredFlag(args, "--status");
|
|
627
|
+
const result = readOptionalFlag(args, "--result");
|
|
628
|
+
const error = readOptionalFlag(args, "--error");
|
|
629
|
+
assertNoRemainingArgs(args, "knowledge done");
|
|
630
|
+
if (status === "succeeded") {
|
|
631
|
+
if (result === undefined) {
|
|
632
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", "knowledge done --status succeeded requires --result.");
|
|
633
|
+
}
|
|
634
|
+
completeKnowledgeFinalizationJob(jobPath, result, claimToken);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (status === "failed") {
|
|
638
|
+
if (!error) {
|
|
639
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", "knowledge done --status failed requires --error.");
|
|
640
|
+
}
|
|
641
|
+
failKnowledgeFinalizationJob(jobPath, error, claimToken);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", `Unsupported knowledge done status "${status}".`);
|
|
645
|
+
}
|
|
646
|
+
default:
|
|
647
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", `Unknown knowledge subcommand "${subcommand ?? ""}".`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
516
650
|
async function runPlan(args, effectiveHost) {
|
|
517
651
|
const subcommand = args.shift();
|
|
518
652
|
switch (subcommand) {
|
|
@@ -551,6 +685,19 @@ async function runPlan(args, effectiveHost) {
|
|
|
551
685
|
}
|
|
552
686
|
const ownerSessionKey = resolveOwnerSessionKey() ?? undefined;
|
|
553
687
|
const entersEndState = requestsPlanEndState(operations);
|
|
688
|
+
const current = entersEndState
|
|
689
|
+
? showPlan({ cwd: process.cwd(), ...target, ownerSessionKey })
|
|
690
|
+
: undefined;
|
|
691
|
+
const project = entersEndState ? tryResolveHookProject(process.cwd()) : null;
|
|
692
|
+
const effectiveWriter = current && project
|
|
693
|
+
? resolvePlanEffectiveConfig(project.projectConfig, current.plan)?.knowledgeWriter
|
|
694
|
+
: undefined;
|
|
695
|
+
if (current
|
|
696
|
+
&& !current.plan.parentPlan
|
|
697
|
+
&& effectiveWriter?.executionPolicy === "subagent"
|
|
698
|
+
&& effectiveHost !== "codex") {
|
|
699
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", 'knowledgeWriter.executionPolicy "subagent" is supported only by the Codex host.', { host: effectiveHost ?? null });
|
|
700
|
+
}
|
|
554
701
|
const queuePlanEndFinalization = entersEndState
|
|
555
702
|
? preparePlanEndFinalization(process.cwd(), ownerSessionKey)
|
|
556
703
|
: undefined;
|
|
@@ -563,7 +710,20 @@ async function runPlan(args, effectiveHost) {
|
|
|
563
710
|
ownerSessionKey,
|
|
564
711
|
});
|
|
565
712
|
const completionRefresh = queuePlanEndFinalization?.(result.taskName);
|
|
566
|
-
|
|
713
|
+
const knowledgeDispatch = (current
|
|
714
|
+
&& project
|
|
715
|
+
&& effectiveHost === "codex"
|
|
716
|
+
&& !current.plan.parentPlan
|
|
717
|
+
&& effectiveWriter?.executionPolicy === "subagent"
|
|
718
|
+
&& result.knowledgeFinalizeId)
|
|
719
|
+
? buildKnowledgeDispatch({
|
|
720
|
+
projectRoot: project.projectRoot,
|
|
721
|
+
taskName: result.taskName,
|
|
722
|
+
finalizeId: result.knowledgeFinalizeId,
|
|
723
|
+
writer: effectiveWriter,
|
|
724
|
+
})
|
|
725
|
+
: undefined;
|
|
726
|
+
printJson(compactPlanCommandResult("plan.edit", result, effectiveHost, completionRefresh, false, knowledgeDispatch));
|
|
567
727
|
if (result.operationChain?.status === "partial")
|
|
568
728
|
process.exitCode = 1;
|
|
569
729
|
return;
|
|
@@ -631,6 +791,20 @@ async function runPlan(args, effectiveHost) {
|
|
|
631
791
|
const target = readPlanMutationTarget(args);
|
|
632
792
|
assertNoRemainingArgs(args, "plan done");
|
|
633
793
|
const ownerSessionKey = resolveOwnerSessionKey() ?? undefined;
|
|
794
|
+
const current = showPlan({
|
|
795
|
+
cwd: process.cwd(),
|
|
796
|
+
...target,
|
|
797
|
+
ownerSessionKey,
|
|
798
|
+
});
|
|
799
|
+
const project = tryResolveHookProject(process.cwd());
|
|
800
|
+
const effectiveWriter = project
|
|
801
|
+
? resolvePlanEffectiveConfig(project.projectConfig, current.plan)?.knowledgeWriter
|
|
802
|
+
: undefined;
|
|
803
|
+
if (!current.plan.parentPlan
|
|
804
|
+
&& effectiveWriter?.executionPolicy === "subagent"
|
|
805
|
+
&& effectiveHost !== "codex") {
|
|
806
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", 'knowledgeWriter.executionPolicy "subagent" is supported only by the Codex host.', { host: effectiveHost ?? null });
|
|
807
|
+
}
|
|
634
808
|
const queuePlanEndFinalization = preparePlanEndFinalization(process.cwd(), ownerSessionKey);
|
|
635
809
|
const result = await editPlan({
|
|
636
810
|
cwd: process.cwd(),
|
|
@@ -641,7 +815,18 @@ async function runPlan(args, effectiveHost) {
|
|
|
641
815
|
ownerSessionKey,
|
|
642
816
|
});
|
|
643
817
|
const completionRefresh = queuePlanEndFinalization?.(result.taskName);
|
|
644
|
-
|
|
818
|
+
const knowledgeDispatch = (effectiveHost === "codex"
|
|
819
|
+
&& !current.plan.parentPlan
|
|
820
|
+
&& effectiveWriter?.executionPolicy === "subagent"
|
|
821
|
+
&& result.knowledgeFinalizeId)
|
|
822
|
+
? buildKnowledgeDispatch({
|
|
823
|
+
projectRoot: project.projectRoot,
|
|
824
|
+
taskName: result.taskName,
|
|
825
|
+
finalizeId: result.knowledgeFinalizeId,
|
|
826
|
+
writer: effectiveWriter,
|
|
827
|
+
})
|
|
828
|
+
: undefined;
|
|
829
|
+
printJson(compactPlanCommandResult("plan.done", result, effectiveHost, completionRefresh, false, knowledgeDispatch));
|
|
645
830
|
return;
|
|
646
831
|
}
|
|
647
832
|
case "show": {
|
|
@@ -1198,6 +1383,11 @@ async function runHook(args, effectiveHost) {
|
|
|
1198
1383
|
});
|
|
1199
1384
|
}
|
|
1200
1385
|
async function runStopHook(effectiveHost) {
|
|
1386
|
+
// Cindy owns task-completion closeout in its Host worker path. The CLI sidecar
|
|
1387
|
+
// must not enqueue a job that it cannot execute with a Cindy session runner.
|
|
1388
|
+
if (effectiveHost === "cindy") {
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1201
1391
|
if (process.env.CLAW_KNOWLEDGE_FINALIZER === "1") {
|
|
1202
1392
|
return;
|
|
1203
1393
|
}
|
|
@@ -1226,14 +1416,164 @@ async function runStopHook(effectiveHost) {
|
|
|
1226
1416
|
host: effectiveHost,
|
|
1227
1417
|
taskConclusions: transcriptPath ? extractTaskDoneConclusions(transcriptPath, turnId) : [],
|
|
1228
1418
|
});
|
|
1229
|
-
|
|
1419
|
+
// Current named hosts own their runner in their adapters. Keep the CLI
|
|
1420
|
+
// launcher only for jobs written by pre-adapter releases with no host.
|
|
1421
|
+
if (result.ok && result.jobPath && !effectiveHost && process.env.CLAW_KNOWLEDGE_FINALIZER_DISABLE_LAUNCH !== "1") {
|
|
1230
1422
|
launchKnowledgeFinalizationWorker(result.jobPath, project.projectRoot);
|
|
1231
1423
|
}
|
|
1424
|
+
if (process.env.CLAW_KNOWLEDGE_CAPTURE_RESULT === "1") {
|
|
1425
|
+
printJson(result);
|
|
1426
|
+
}
|
|
1232
1427
|
}
|
|
1233
1428
|
catch {
|
|
1234
1429
|
// Knowledge capture is a fail-open sidecar and must never block Stop.
|
|
1235
1430
|
}
|
|
1236
1431
|
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Machine-facing report capture for hosts whose final-message hook is owned by
|
|
1434
|
+
* the adapter rather than by the CLI. Unlike `hook auto-doc`, this only
|
|
1435
|
+
* persists the report/job hand-off; it never chooses or launches a writer.
|
|
1436
|
+
*/
|
|
1437
|
+
async function runInternalKnowledgeCapture(args, effectiveHost) {
|
|
1438
|
+
assertNoRemainingArgs(args, "internal-knowledge-capture");
|
|
1439
|
+
const payload = await readStdinJson();
|
|
1440
|
+
const hookCwd = resolveHookCwd(payload);
|
|
1441
|
+
const sessionId = resolveOwnerSessionKey(payload);
|
|
1442
|
+
const turnId = readHookString(payload, "turn_id");
|
|
1443
|
+
const message = readHookString(payload, "message");
|
|
1444
|
+
if (!hookCwd || !sessionId || !turnId || !message || !containsClawDir(hookCwd)) {
|
|
1445
|
+
printJson({ ok: true, captured: false });
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
try {
|
|
1449
|
+
const project = resolveProjectContext(hookCwd);
|
|
1450
|
+
const result = tryCaptureKnowledgeStop({
|
|
1451
|
+
project,
|
|
1452
|
+
sessionId,
|
|
1453
|
+
turnId,
|
|
1454
|
+
message,
|
|
1455
|
+
host: effectiveHost === "cindy" ? "cindy" : effectiveHost,
|
|
1456
|
+
});
|
|
1457
|
+
printJson(result);
|
|
1458
|
+
}
|
|
1459
|
+
catch (error) {
|
|
1460
|
+
// Capture is a non-blocking sidecar. Return structured failure so the
|
|
1461
|
+
// owning adapter can surface/retry it without affecting the assistant turn.
|
|
1462
|
+
printJson({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
/** Record a successful adapter-owned writer run without invoking a host SDK. */
|
|
1466
|
+
function runInternalKnowledgeComplete(args) {
|
|
1467
|
+
const jobPath = readRequiredFlag(args, "--job");
|
|
1468
|
+
const result = readRequiredFlag(args, "--result");
|
|
1469
|
+
assertNoRemainingArgs(args, "internal-knowledge-complete");
|
|
1470
|
+
const running = ensureLegacyKnowledgeClaim(jobPath);
|
|
1471
|
+
if (!running?.claimToken) {
|
|
1472
|
+
printJson({ ok: true, completed: false, reason: "job is not claimable" });
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
completeKnowledgeFinalizationJob(jobPath, result, running.claimToken);
|
|
1476
|
+
}
|
|
1477
|
+
function runInternalKnowledgeClaim(args) {
|
|
1478
|
+
const jobPath = readRequiredFlag(args, "--job");
|
|
1479
|
+
assertNoRemainingArgs(args, "internal-knowledge-claim");
|
|
1480
|
+
const job = ensureLegacyKnowledgeClaim(jobPath);
|
|
1481
|
+
printJson({
|
|
1482
|
+
ok: true,
|
|
1483
|
+
claimed: Boolean(job),
|
|
1484
|
+
...(job ? {
|
|
1485
|
+
finalizeId: job.finalizeId,
|
|
1486
|
+
claimToken: job.claimToken,
|
|
1487
|
+
} : {}),
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
function runInternalKnowledgeFail(args) {
|
|
1491
|
+
const jobPath = readRequiredFlag(args, "--job");
|
|
1492
|
+
const message = readRequiredFlag(args, "--message");
|
|
1493
|
+
assertNoRemainingArgs(args, "internal-knowledge-fail");
|
|
1494
|
+
const job = ensureLegacyKnowledgeClaim(jobPath);
|
|
1495
|
+
if (!job?.claimToken) {
|
|
1496
|
+
printJson({ ok: true, failed: false, reason: "job is not claimable" });
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
failKnowledgeFinalizationJob(jobPath, message, job.claimToken);
|
|
1500
|
+
}
|
|
1501
|
+
function ensureLegacyKnowledgeClaim(jobPath) {
|
|
1502
|
+
const job = readKnowledgeFinalizationJob(jobPath);
|
|
1503
|
+
if (job.status === "succeeded" || job.status === "failed" && job.attempts >= 3) {
|
|
1504
|
+
return null;
|
|
1505
|
+
}
|
|
1506
|
+
if (job.status === "running") {
|
|
1507
|
+
return job;
|
|
1508
|
+
}
|
|
1509
|
+
return claimKnowledgeFinalizationJob(jobPath);
|
|
1510
|
+
}
|
|
1511
|
+
function completeKnowledgeFinalizationJob(jobPath, result, claimToken) {
|
|
1512
|
+
const running = readKnowledgeFinalizationJob(jobPath);
|
|
1513
|
+
if (running.status === "succeeded") {
|
|
1514
|
+
const terminal = doneKnowledgeFinalizationJob({
|
|
1515
|
+
jobPath,
|
|
1516
|
+
claimToken,
|
|
1517
|
+
status: "succeeded",
|
|
1518
|
+
result,
|
|
1519
|
+
});
|
|
1520
|
+
removeKnowledgeAssignmentTemplate(jobPath, running.finalizeId);
|
|
1521
|
+
printJson({ ok: true, completed: true, alreadyDone: terminal.alreadyDone, finalizeId: running.finalizeId });
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
if (running.status !== "running") {
|
|
1525
|
+
throw new Error("Knowledge finalization job must be claimed before successful completion.");
|
|
1526
|
+
}
|
|
1527
|
+
if (running.claimToken !== claimToken) {
|
|
1528
|
+
throw new Error("Knowledge finalization completion does not match the active claim.");
|
|
1529
|
+
}
|
|
1530
|
+
const project = resolveProjectContext(running.projectRoot);
|
|
1531
|
+
const finishedAt = new Date().toISOString();
|
|
1532
|
+
const truthEncoding = normalizeTruthMarkdownEncoding(project);
|
|
1533
|
+
recordKnowledgeFinalizationResult(project, running.reportPath, {
|
|
1534
|
+
schemaVersion: 1,
|
|
1535
|
+
entryType: "knowledge_finalization",
|
|
1536
|
+
finalizeId: running.finalizeId,
|
|
1537
|
+
taskName: running.taskName,
|
|
1538
|
+
recordedAt: finishedAt,
|
|
1539
|
+
status: "succeeded",
|
|
1540
|
+
result,
|
|
1541
|
+
attempts: running.attempts,
|
|
1542
|
+
...(running.host !== undefined ? { host: running.host } : {}),
|
|
1543
|
+
truthEncoding,
|
|
1544
|
+
});
|
|
1545
|
+
const terminal = doneKnowledgeFinalizationJob({
|
|
1546
|
+
jobPath,
|
|
1547
|
+
claimToken,
|
|
1548
|
+
status: "succeeded",
|
|
1549
|
+
result,
|
|
1550
|
+
finishedAt,
|
|
1551
|
+
patch: { truthEncoding },
|
|
1552
|
+
});
|
|
1553
|
+
removeKnowledgeAssignmentTemplate(jobPath, running.finalizeId);
|
|
1554
|
+
queueCompletionRefresh({
|
|
1555
|
+
cwd: running.projectRoot,
|
|
1556
|
+
taskName: running.taskName,
|
|
1557
|
+
includeTaskRetention: false,
|
|
1558
|
+
includeTaskMemory: false,
|
|
1559
|
+
statusLabel: `knowledge-${running.finalizeId.slice(0, 12)}`,
|
|
1560
|
+
skipGitNexusRefresh: true,
|
|
1561
|
+
});
|
|
1562
|
+
printJson({ ok: true, completed: true, alreadyDone: terminal.alreadyDone, finalizeId: running.finalizeId });
|
|
1563
|
+
}
|
|
1564
|
+
function failKnowledgeFinalizationJob(jobPath, message, claimToken) {
|
|
1565
|
+
const terminal = doneKnowledgeFinalizationJob({
|
|
1566
|
+
jobPath,
|
|
1567
|
+
claimToken,
|
|
1568
|
+
status: "failed",
|
|
1569
|
+
error: message,
|
|
1570
|
+
});
|
|
1571
|
+
removeKnowledgeAssignmentTemplate(jobPath, terminal.job.finalizeId);
|
|
1572
|
+
printJson({ ok: true, failed: true, alreadyDone: terminal.alreadyDone, finalizeId: terminal.job.finalizeId });
|
|
1573
|
+
}
|
|
1574
|
+
function removeKnowledgeAssignmentTemplate(jobPath, finalizeId) {
|
|
1575
|
+
fs.rmSync(path.join(path.dirname(jobPath), `${finalizeId}.assignments.json`), { force: true });
|
|
1576
|
+
}
|
|
1237
1577
|
async function runInternalEmbeddingWarmup(args) {
|
|
1238
1578
|
const cwd = readRequiredFlag(args, "--cwd");
|
|
1239
1579
|
assertNoRemainingArgs(args, "internal-embedding-warmup");
|
|
@@ -1245,110 +1585,76 @@ async function runInternalEmbeddingWarmup(args) {
|
|
|
1245
1585
|
}
|
|
1246
1586
|
async function runInternalKnowledgeFinalize(args) {
|
|
1247
1587
|
const jobPath = readRequiredFlag(args, "--job");
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1588
|
+
const queued = readKnowledgeFinalizationJob(jobPath);
|
|
1589
|
+
assertNoRemainingArgs(args, "internal-knowledge-finalize");
|
|
1590
|
+
if ((queued.writer?.executionPolicy ?? "background") !== "background") {
|
|
1250
1591
|
return;
|
|
1251
1592
|
}
|
|
1252
1593
|
try {
|
|
1253
|
-
const
|
|
1254
|
-
const
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
:
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
queueCompletionRefresh({
|
|
1267
|
-
cwd: running.projectRoot,
|
|
1268
|
-
taskName: running.taskName,
|
|
1269
|
-
includeTaskRetention: false,
|
|
1270
|
-
includeTaskMemory: false,
|
|
1271
|
-
statusLabel: `knowledge-${running.finalizeId.slice(0, 12)}`,
|
|
1272
|
-
skipGitNexusRefresh: true,
|
|
1273
|
-
});
|
|
1274
|
-
const finishedAt = new Date().toISOString();
|
|
1275
|
-
recordKnowledgeFinalizationResult(project, running.reportPath, {
|
|
1276
|
-
schemaVersion: 1,
|
|
1277
|
-
entryType: "knowledge_finalization",
|
|
1278
|
-
finalizeId: running.finalizeId,
|
|
1279
|
-
taskName: running.taskName,
|
|
1280
|
-
recordedAt: finishedAt,
|
|
1281
|
-
status: "succeeded",
|
|
1282
|
-
result: writerRun.finalResponse,
|
|
1283
|
-
attempts: running.attempts,
|
|
1284
|
-
...(running.host !== undefined ? { host: running.host } : {}),
|
|
1594
|
+
const writerRun = await runKnowledgeDelegateForJob(queued);
|
|
1595
|
+
const terminal = readKnowledgeFinalizationJob(jobPath);
|
|
1596
|
+
if (terminal.status !== "succeeded" && terminal.status !== "failed") {
|
|
1597
|
+
throw new Error("Knowledge delegate returned without acknowledging a terminal result.");
|
|
1598
|
+
}
|
|
1599
|
+
if (writerRun.threadId) {
|
|
1600
|
+
assertCompletedKnowledgeWriterSession(writerRun.threadId);
|
|
1601
|
+
}
|
|
1602
|
+
printJson({
|
|
1603
|
+
ok: terminal.status === "succeeded",
|
|
1604
|
+
command: "internal-knowledge-finalize",
|
|
1605
|
+
finalizeId: terminal.finalizeId,
|
|
1606
|
+
status: terminal.status,
|
|
1285
1607
|
...(writerRun.threadId ? { threadId: writerRun.threadId } : {}),
|
|
1286
|
-
...(knowledgeGovernance ? { knowledgeGovernance } : {}),
|
|
1287
|
-
truthEncoding,
|
|
1288
|
-
});
|
|
1289
|
-
writeKnowledgeFinalizationJob(jobPath, {
|
|
1290
|
-
...running,
|
|
1291
|
-
status: "succeeded",
|
|
1292
|
-
finishedAt,
|
|
1293
|
-
...(writerRun.threadId ? { sdkThreadId: writerRun.threadId } : {}),
|
|
1294
|
-
...(writerRun.threadIds ? { sdkThreadIds: writerRun.threadIds } : {}),
|
|
1295
|
-
finalResponse: writerRun.finalResponse,
|
|
1296
|
-
...(knowledgeGovernance ? { knowledgeGovernance } : {}),
|
|
1297
|
-
truthEncoding,
|
|
1298
1608
|
});
|
|
1299
1609
|
}
|
|
1300
1610
|
catch (error) {
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1611
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1612
|
+
let failed = readKnowledgeFinalizationJob(jobPath);
|
|
1613
|
+
if (failed.status === "running" && failed.claimToken) {
|
|
1614
|
+
failed = doneKnowledgeFinalizationJob({
|
|
1615
|
+
jobPath,
|
|
1616
|
+
claimToken: failed.claimToken,
|
|
1617
|
+
status: "failed",
|
|
1618
|
+
error: message,
|
|
1619
|
+
}).job;
|
|
1620
|
+
}
|
|
1621
|
+
else if (failed.status === "queued") {
|
|
1622
|
+
const supervisorClaim = claimKnowledgeFinalizationJob(jobPath);
|
|
1623
|
+
if (supervisorClaim?.claimToken) {
|
|
1624
|
+
failed = doneKnowledgeFinalizationJob({
|
|
1625
|
+
jobPath,
|
|
1626
|
+
claimToken: supervisorClaim.claimToken,
|
|
1627
|
+
status: "failed",
|
|
1628
|
+
error: message,
|
|
1629
|
+
}).job;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
if (failed.status === "failed") {
|
|
1633
|
+
removeKnowledgeAssignmentTemplate(jobPath, failed.finalizeId);
|
|
1634
|
+
}
|
|
1308
1635
|
if (failed.attempts < 3 && process.env.CLAW_KNOWLEDGE_FINALIZER_DISABLE_RETRY !== "1") {
|
|
1309
1636
|
launchKnowledgeFinalizationWorker(jobPath, failed.projectRoot);
|
|
1310
1637
|
}
|
|
1311
1638
|
}
|
|
1312
1639
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
assertCompletedKnowledgeWriterSession(result.threadId ?? null);
|
|
1331
|
-
}
|
|
1332
|
-
results.push({
|
|
1333
|
-
finalResponse: result.finalResponse,
|
|
1334
|
-
...(result.threadId ? { threadId: result.threadId } : {}),
|
|
1335
|
-
});
|
|
1336
|
-
continue;
|
|
1337
|
-
}
|
|
1338
|
-
if (running.host !== undefined && running.host !== null && running.host !== "codex") {
|
|
1339
|
-
throw new Error(`Unsupported knowledge finalization job host "${String(running.host)}".`);
|
|
1340
|
-
}
|
|
1341
|
-
results.push(await runCodexSdkWriter(running, skill));
|
|
1640
|
+
async function runKnowledgeDelegateForJob(running) {
|
|
1641
|
+
const dispatch = buildKnowledgeDelegateDispatch({
|
|
1642
|
+
policy: "background",
|
|
1643
|
+
projectRoot: running.projectRoot,
|
|
1644
|
+
taskName: running.taskName,
|
|
1645
|
+
finalizeId: running.finalizeId,
|
|
1646
|
+
writer: running.writer,
|
|
1647
|
+
});
|
|
1648
|
+
if (running.host === "opencode") {
|
|
1649
|
+
return runOpencodeKnowledgeWriter({
|
|
1650
|
+
prompt: dispatch.prompt,
|
|
1651
|
+
projectRoot: running.projectRoot,
|
|
1652
|
+
writer: running.writer ?? null,
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
if (running.host !== undefined && running.host !== null && running.host !== "codex") {
|
|
1656
|
+
throw new Error(`Unsupported knowledge finalization job host "${String(running.host)}".`);
|
|
1342
1657
|
}
|
|
1343
|
-
const threadIds = results.flatMap((result) => result.threadId ? [result.threadId] : []);
|
|
1344
|
-
const last = results.at(-1);
|
|
1345
|
-
return {
|
|
1346
|
-
finalResponse: results.map((result) => result.finalResponse).join("\n\n"),
|
|
1347
|
-
...(last?.threadId ? { threadId: last.threadId } : {}),
|
|
1348
|
-
...(threadIds.length > 0 ? { threadIds } : {}),
|
|
1349
|
-
};
|
|
1350
|
-
}
|
|
1351
|
-
async function runCodexSdkWriter(running, skill) {
|
|
1352
1658
|
const sdk = await import(pathToFileURL(resolveCodexSdkEntryPath()).href);
|
|
1353
1659
|
const Codex = sdk.Codex;
|
|
1354
1660
|
const codex = new Codex({
|
|
@@ -1368,10 +1674,7 @@ async function runCodexSdkWriter(running, skill) {
|
|
|
1368
1674
|
? { modelReasoningEffort: writer.reasoningEffort }
|
|
1369
1675
|
: {}),
|
|
1370
1676
|
});
|
|
1371
|
-
const turn = await thread.run(
|
|
1372
|
-
if (isBuiltInKnowledgeWriterSkill(skill)) {
|
|
1373
|
-
assertCompletedKnowledgeWriterSession(thread.id);
|
|
1374
|
-
}
|
|
1677
|
+
const turn = await thread.run(dispatch.prompt);
|
|
1375
1678
|
return {
|
|
1376
1679
|
finalResponse: turn.finalResponse,
|
|
1377
1680
|
...(thread.id ? { threadId: thread.id } : {}),
|
|
@@ -1416,28 +1719,11 @@ function knowledgeFinalizerEnvironment() {
|
|
|
1416
1719
|
}
|
|
1417
1720
|
}
|
|
1418
1721
|
env.CLAW_KNOWLEDGE_FINALIZER = "1";
|
|
1722
|
+
delete env.CLAW_SESSION_ID;
|
|
1723
|
+
delete env.CODEX_THREAD_ID;
|
|
1724
|
+
delete env.CODEX_SESSION_ID;
|
|
1419
1725
|
return env;
|
|
1420
1726
|
}
|
|
1421
|
-
function buildKnowledgeWriterPrompt(job, writerSkill) {
|
|
1422
|
-
return [
|
|
1423
|
-
`Apply the ${writerSkill} skill's documentation-governance rules to the supplied materials and update the governed project documentation. Work unattended; do not request review or confirmation. Use task status to distinguish completed work from pending or blocked intent, and never present requirements or intentions as results. Skip and report ambiguous or unsafe changes.`,
|
|
1424
|
-
"Materials:",
|
|
1425
|
-
`- ${job.planPath}`,
|
|
1426
|
-
`- ${job.reportPath}`,
|
|
1427
|
-
`Finalization id: ${job.finalizeId}`,
|
|
1428
|
-
"Interpret inputs by content, regardless of filename or schema. Do not reference or link to the supplied materials in governed documentation; they are transient and will be destroyed after finalization. Do not modify inputs, delegate, reimplement, or rerun tests.",
|
|
1429
|
-
].join("\n");
|
|
1430
|
-
}
|
|
1431
|
-
function resolveKnowledgeWriterSkills(job) {
|
|
1432
|
-
const configured = job.writer?.externalSkills?.map((skill) => skill.trim()).filter(Boolean);
|
|
1433
|
-
return configured && configured.length > 0 ? configured : [BUILT_IN_KNOWLEDGE_WRITER_SKILL];
|
|
1434
|
-
}
|
|
1435
|
-
function isBuiltInKnowledgeWriterSkill(skill) {
|
|
1436
|
-
return skill === BUILT_IN_KNOWLEDGE_WRITER_SKILL;
|
|
1437
|
-
}
|
|
1438
|
-
function usesBuiltInKnowledgeWriter(job) {
|
|
1439
|
-
return resolveKnowledgeWriterSkills(job).every(isBuiltInKnowledgeWriterSkill);
|
|
1440
|
-
}
|
|
1441
1727
|
function launchKnowledgeFinalizationWorker(jobPath, cwd) {
|
|
1442
1728
|
if (process.platform === "win32") {
|
|
1443
1729
|
const launcherScript = [
|
|
@@ -1484,11 +1770,15 @@ async function runSessionStartHook(effectiveHost) {
|
|
|
1484
1770
|
try {
|
|
1485
1771
|
const context = await runContextCommand([], hookCwd, ownerSessionKey, effectiveHost);
|
|
1486
1772
|
const contextProject = asJsonRecord(context.project);
|
|
1773
|
+
const retryableJobs = contextProject?.scope !== "session" && !context.error
|
|
1774
|
+
? listRetryableKnowledgeFinalizationJobs(resolveProjectContext(hookCwd))
|
|
1775
|
+
: [];
|
|
1487
1776
|
if (contextProject?.scope !== "session"
|
|
1488
1777
|
&& !context.error
|
|
1778
|
+
&& effectiveHost !== "cindy"
|
|
1489
1779
|
&& process.env.CLAW_KNOWLEDGE_FINALIZER_DISABLE_LAUNCH !== "1") {
|
|
1490
1780
|
const project = resolveProjectContext(hookCwd);
|
|
1491
|
-
for (const jobPath of
|
|
1781
|
+
for (const jobPath of retryableJobs) {
|
|
1492
1782
|
try {
|
|
1493
1783
|
launchKnowledgeFinalizationWorker(jobPath, project.projectRoot);
|
|
1494
1784
|
}
|
|
@@ -1505,6 +1795,7 @@ async function runSessionStartHook(effectiveHost) {
|
|
|
1505
1795
|
hookSpecificOutput: {
|
|
1506
1796
|
hookEventName: "SessionStart",
|
|
1507
1797
|
additionalContext,
|
|
1798
|
+
...(effectiveHost === "cindy" ? { knowledgeJobs: retryableJobs } : {}),
|
|
1508
1799
|
},
|
|
1509
1800
|
})}\n`);
|
|
1510
1801
|
}
|
|
@@ -1578,6 +1869,9 @@ function safeResolveTempDir() {
|
|
|
1578
1869
|
}
|
|
1579
1870
|
}
|
|
1580
1871
|
function buildSessionStartAdditionalContext(context, sessionCwd, effectiveHost) {
|
|
1872
|
+
if (effectiveHost === "cindy") {
|
|
1873
|
+
return buildCindySessionStartContext(context, sessionCwd);
|
|
1874
|
+
}
|
|
1581
1875
|
const versionSyncPrompt = buildVersionSyncPrompt(context);
|
|
1582
1876
|
const searchGuidance = buildContextSearchGuidance(context);
|
|
1583
1877
|
const runtimeErrorPrompt = buildCodexRuntimeErrorPrompt(context);
|
|
@@ -1613,6 +1907,40 @@ function buildSessionStartAdditionalContext(context, sessionCwd, effectiveHost)
|
|
|
1613
1907
|
const promptWithSearch = searchGuidance ? `${promptWithVersion}\n${searchGuidance}` : promptWithVersion;
|
|
1614
1908
|
return runtimeErrorPrompt ? `${runtimeErrorPrompt}\n\n${promptWithSearch}` : promptWithSearch;
|
|
1615
1909
|
}
|
|
1910
|
+
/**
|
|
1911
|
+
* Cindy Agents use the Ghost Tool gateway, not shell commands or Host actions.
|
|
1912
|
+
* Keep startup recovery to the actionable plan snapshot only; the worker owns
|
|
1913
|
+
* session identity, host selection, and any future Goal projection.
|
|
1914
|
+
*/
|
|
1915
|
+
function buildCindySessionStartContext(context, sessionCwd) {
|
|
1916
|
+
const activeWorkflow = asJsonRecord(context.activeWorkflow);
|
|
1917
|
+
if (activeWorkflow) {
|
|
1918
|
+
const taskName = typeof activeWorkflow.taskName === "string" ? activeWorkflow.taskName.trim() : "current task";
|
|
1919
|
+
const planStatus = typeof activeWorkflow.planStatus === "string" ? activeWorkflow.planStatus.trim() : "unknown";
|
|
1920
|
+
const planSummary = typeof activeWorkflow.planSummary === "string" ? activeWorkflow.planSummary.trim() : "";
|
|
1921
|
+
const nextTask = asJsonRecord(activeWorkflow.workflowGuidance)?.nextTask;
|
|
1922
|
+
const nextTaskTitle = asJsonRecord(nextTask)?.title;
|
|
1923
|
+
const lines = [
|
|
1924
|
+
"claw-kit recovered a Cindy workflow.",
|
|
1925
|
+
`- task: ${taskName}`,
|
|
1926
|
+
`- plan status: ${planStatus}`,
|
|
1927
|
+
...(planSummary ? [`- plan summary: ${planSummary}`] : []),
|
|
1928
|
+
...(typeof nextTaskTitle === "string" && nextTaskTitle.trim() ? [`- next task: ${nextTaskTitle.trim()}`] : []),
|
|
1929
|
+
"Use the claw-kit Ghost tools to inspect or advance this workflow. Do not run claw shell commands or manage host/session/Goal state yourself.",
|
|
1930
|
+
];
|
|
1931
|
+
return lines.join("\n");
|
|
1932
|
+
}
|
|
1933
|
+
const project = asJsonRecord(context.project);
|
|
1934
|
+
if (!project)
|
|
1935
|
+
return null;
|
|
1936
|
+
const projectName = typeof project.projectName === "string" && project.projectName.trim()
|
|
1937
|
+
? project.projectName.trim()
|
|
1938
|
+
: path.basename(String(project.projectRoot ?? sessionCwd ?? "project"));
|
|
1939
|
+
return [
|
|
1940
|
+
`claw-kit is ready for the Cindy workspace: ${projectName}.`,
|
|
1941
|
+
"Use the claw-kit Ghost tools when this work needs a plan; do not invoke claw shell commands directly.",
|
|
1942
|
+
].join("\n");
|
|
1943
|
+
}
|
|
1616
1944
|
function buildCodexRuntimeErrorPrompt(context) {
|
|
1617
1945
|
const error = asJsonRecord(context.error);
|
|
1618
1946
|
if (error?.code !== "CODEX_SDK_RUNTIME_MISSING") {
|
|
@@ -1674,8 +2002,9 @@ function buildVersionSyncPrompt(context) {
|
|
|
1674
2002
|
return {
|
|
1675
2003
|
placement: "suffix",
|
|
1676
2004
|
lines: [
|
|
1677
|
-
`
|
|
1678
|
-
|
|
2005
|
+
`A newer claw-kit version is available: installed CLI ${cliVersion}, published latest ${latestPublishedVersion}.`,
|
|
2006
|
+
"Tell the user in their language that the current claw-kit installation is out of date and must be updated before they can continue using claw-kit. Ask whether they want to update now, then wait for their answer.",
|
|
2007
|
+
`After the user confirms, use ${updateSkill} to update the claw-kit CLI and the current host plugin surface, then continue the original task.`,
|
|
1679
2008
|
],
|
|
1680
2009
|
};
|
|
1681
2010
|
}
|
|
@@ -1820,6 +2149,7 @@ async function runSubplan(args, effectiveHost) {
|
|
|
1820
2149
|
}
|
|
1821
2150
|
function resolveOwnerSessionKey(payload) {
|
|
1822
2151
|
const envCandidates = [
|
|
2152
|
+
process.env.CLAW_SESSION_ID,
|
|
1823
2153
|
process.env.CODEX_THREAD_ID,
|
|
1824
2154
|
process.env.CODEX_SESSION_ID,
|
|
1825
2155
|
];
|
|
@@ -1880,13 +2210,20 @@ function readJson(filePath) {
|
|
|
1880
2210
|
function stripBom(content) {
|
|
1881
2211
|
return content.charCodeAt(0) === 0xfeff ? content.slice(1) : content;
|
|
1882
2212
|
}
|
|
1883
|
-
function
|
|
2213
|
+
function buildKnowledgeDispatch(input) {
|
|
2214
|
+
return buildKnowledgeDelegateDispatch({
|
|
2215
|
+
policy: "subagent",
|
|
2216
|
+
...input,
|
|
2217
|
+
});
|
|
2218
|
+
}
|
|
2219
|
+
function compactPlanCommandResult(command, result, effectiveHost, completionRefresh, forceProjectionSync = false, knowledgeDispatch) {
|
|
1884
2220
|
const archivedPlanPath = completionRefresh?.taskRetention.archivedCurrentTask?.taskName === result.taskName &&
|
|
1885
2221
|
completionRefresh.taskRetention.archivedCurrentTask.archivedPlanPath
|
|
1886
2222
|
? completionRefresh.taskRetention.archivedCurrentTask.archivedPlanPath
|
|
1887
2223
|
: undefined;
|
|
1888
2224
|
const resolvedPlanPath = archivedPlanPath ?? result.planPath;
|
|
1889
2225
|
const codexResult = effectiveHost === "codex";
|
|
2226
|
+
const cindyResult = effectiveHost === "cindy";
|
|
1890
2227
|
const hostActions = codexResult ? buildHostActions(result, { forceProjectionSync, actionIdPrefix: command === "plan.sync" ? `plan.sync:${createHash("sha256").update(result.planPath).digest("hex").slice(0, 16)}` : undefined }) : [];
|
|
1891
2228
|
const nextsteps = codexResult
|
|
1892
2229
|
&& result.planStatus === "end.completed"
|
|
@@ -1917,16 +2254,17 @@ function compactPlanCommandResult(command, result, effectiveHost, completionRefr
|
|
|
1917
2254
|
...(archivedPlanPath ? { archivedPlanPath } : {}),
|
|
1918
2255
|
planStatus: result.planStatus,
|
|
1919
2256
|
...(achievement ? { achievement } : {}),
|
|
2257
|
+
...(knowledgeDispatch ? { knowledgeDispatch } : {}),
|
|
1920
2258
|
...(!codexResult && result.previousPlanStatus ? { previousPlanStatus: result.previousPlanStatus } : {}),
|
|
1921
2259
|
...(hostActions.length ? { hostActions } : {}),
|
|
1922
2260
|
...(!codexResult && result.changedTaskIds?.length ? { changedTaskIds: result.changedTaskIds } : {}),
|
|
1923
2261
|
...(!codexResult && result.appendedTaskIds?.length ? { appendedTaskIds: result.appendedTaskIds } : {}),
|
|
1924
2262
|
...(codexResult ? { stage: result.workflowGuidance.stage } : {}),
|
|
1925
|
-
...(!codexResult || result.planStatus === "end.completed"
|
|
2263
|
+
...((!codexResult && !cindyResult) || result.planStatus === "end.completed" && !cindyResult
|
|
1926
2264
|
? { nextsteps }
|
|
1927
2265
|
: {}),
|
|
1928
2266
|
...(result.workflowGuidance.nextTask ? { nextTask: result.workflowGuidance.nextTask } : {}),
|
|
1929
|
-
...(result.workflowGuidance.notes?.trim() && !codexResult
|
|
2267
|
+
...(result.workflowGuidance.notes?.trim() && !codexResult && !cindyResult
|
|
1930
2268
|
? { notes: result.workflowGuidance.notes }
|
|
1931
2269
|
: {}),
|
|
1932
2270
|
...(result.workflowGuidance.commandHints?.length
|
|
@@ -1941,9 +2279,13 @@ function compactPlanCommandResult(command, result, effectiveHost, completionRefr
|
|
|
1941
2279
|
failedOperation: result.operationChain.failedOperation,
|
|
1942
2280
|
}
|
|
1943
2281
|
: {}),
|
|
1944
|
-
...(!codexResult && result.workflowGuidance.goalMode ? { goalMode: result.workflowGuidance.goalMode } : {}),
|
|
1945
|
-
...(!codexResult && result.workflowGuidance.goalTool ? { goalTool: result.workflowGuidance.goalTool } : {}),
|
|
2282
|
+
...(!codexResult && !cindyResult && result.workflowGuidance.goalMode ? { goalMode: result.workflowGuidance.goalMode } : {}),
|
|
2283
|
+
...(!codexResult && !cindyResult && result.workflowGuidance.goalTool ? { goalTool: result.workflowGuidance.goalTool } : {}),
|
|
1946
2284
|
...(includePlan && result.plan ? { plan: result.plan } : {}),
|
|
2285
|
+
// Cindy's Ghost card is a Host-owned projection. It needs the
|
|
2286
|
+
// canonical task list to render its expandable Todo view, but that
|
|
2287
|
+
// view stays out of Agent guidance in the Cindy adapter.
|
|
2288
|
+
...(cindyResult ? { planView: result.planView } : {}),
|
|
1947
2289
|
...(result.planReview
|
|
1948
2290
|
? {
|
|
1949
2291
|
planReview: {
|
|
@@ -3305,7 +3647,7 @@ function printTopLevelUsage() {
|
|
|
3305
3647
|
"Global flags:",
|
|
3306
3648
|
" -h, --help Show help (use `claw help <command>` for command details).",
|
|
3307
3649
|
" -v, --version Print the CLI version.",
|
|
3308
|
-
" --host <host> Select host-specific output projection (codex or
|
|
3650
|
+
" --host <host> Select host-specific output projection (codex, opencode, or cindy).",
|
|
3309
3651
|
"",
|
|
3310
3652
|
"Run `claw help <command>` or `claw help <command> <subcommand>` for detailed help.",
|
|
3311
3653
|
];
|