@enderfga/claw-orchestrator 5.1.0 → 6.0.0
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/README.md +26 -26
- package/dist/bin/cli.js +107 -1
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/acp-server.d.ts +5 -5
- package/dist/src/acp-server.js +3 -3
- package/dist/src/acp-server.js.map +1 -1
- package/dist/src/autoloop/dispatcher.d.ts +22 -0
- package/dist/src/autoloop/dispatcher.js +71 -13
- package/dist/src/autoloop/dispatcher.js.map +1 -1
- package/dist/src/autoloop/messages.d.ts +10 -0
- package/dist/src/autoloop/messages.js.map +1 -1
- package/dist/src/autoloop/runner.js +6 -0
- package/dist/src/autoloop/runner.js.map +1 -1
- package/dist/src/constants.d.ts +0 -6
- package/dist/src/constants.js +0 -6
- package/dist/src/constants.js.map +1 -1
- package/dist/src/council.d.ts +15 -0
- package/dist/src/council.js +48 -35
- package/dist/src/council.js.map +1 -1
- package/dist/src/dashboard/index.html +191 -6
- package/dist/src/embedded-server.js +132 -9
- package/dist/src/embedded-server.js.map +1 -1
- package/dist/src/fanout.d.ts +30 -1
- package/dist/src/fanout.js +32 -3
- package/dist/src/fanout.js.map +1 -1
- package/dist/src/index.js +359 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/agent-step.d.ts +59 -0
- package/dist/src/kernel/agent-step.js +100 -0
- package/dist/src/kernel/agent-step.js.map +1 -0
- package/dist/src/kernel/conditions.d.ts +11 -0
- package/dist/src/kernel/conditions.js +24 -0
- package/dist/src/kernel/conditions.js.map +1 -0
- package/dist/src/kernel/engine.d.ts +319 -0
- package/dist/src/kernel/engine.js +1047 -0
- package/dist/src/kernel/engine.js.map +1 -0
- package/dist/src/kernel/exec.d.ts +43 -0
- package/dist/src/kernel/exec.js +112 -0
- package/dist/src/kernel/exec.js.map +1 -0
- package/dist/src/kernel/file-lock.d.ts +50 -0
- package/dist/src/kernel/file-lock.js +135 -0
- package/dist/src/kernel/file-lock.js.map +1 -0
- package/dist/src/kernel/nodes/agent.d.ts +4 -0
- package/dist/src/kernel/nodes/agent.js +35 -0
- package/dist/src/kernel/nodes/agent.js.map +1 -0
- package/dist/src/kernel/nodes/autoloop.d.ts +78 -0
- package/dist/src/kernel/nodes/autoloop.js +75 -0
- package/dist/src/kernel/nodes/autoloop.js.map +1 -0
- package/dist/src/kernel/nodes/council.d.ts +12 -0
- package/dist/src/kernel/nodes/council.js +88 -0
- package/dist/src/kernel/nodes/council.js.map +1 -0
- package/dist/src/kernel/nodes/fanout.d.ts +11 -0
- package/dist/src/kernel/nodes/fanout.js +63 -0
- package/dist/src/kernel/nodes/fanout.js.map +1 -0
- package/dist/src/kernel/nodes/human-gate.d.ts +4 -0
- package/dist/src/kernel/nodes/human-gate.js +7 -0
- package/dist/src/kernel/nodes/human-gate.js.map +1 -0
- package/dist/src/kernel/nodes/index.d.ts +12 -0
- package/dist/src/kernel/nodes/index.js +21 -0
- package/dist/src/kernel/nodes/index.js.map +1 -0
- package/dist/src/kernel/nodes/router.d.ts +4 -0
- package/dist/src/kernel/nodes/router.js +12 -0
- package/dist/src/kernel/nodes/router.js.map +1 -0
- package/dist/src/kernel/nodes/subflow.d.ts +13 -0
- package/dist/src/kernel/nodes/subflow.js +38 -0
- package/dist/src/kernel/nodes/subflow.js.map +1 -0
- package/dist/src/kernel/nodes/ultraapp.d.ts +60 -0
- package/dist/src/kernel/nodes/ultraapp.js +62 -0
- package/dist/src/kernel/nodes/ultraapp.js.map +1 -0
- package/dist/src/kernel/nodes/verifier.d.ts +14 -0
- package/dist/src/kernel/nodes/verifier.js +84 -0
- package/dist/src/kernel/nodes/verifier.js.map +1 -0
- package/dist/src/kernel/projections.d.ts +42 -0
- package/dist/src/kernel/projections.js +133 -0
- package/dist/src/kernel/projections.js.map +1 -0
- package/dist/src/kernel/repo.d.ts +13 -0
- package/dist/src/kernel/repo.js +64 -0
- package/dist/src/kernel/repo.js.map +1 -0
- package/dist/src/kernel/secrets.d.ts +25 -0
- package/dist/src/kernel/secrets.js +48 -0
- package/dist/src/kernel/secrets.js.map +1 -0
- package/dist/src/kernel/store.d.ts +225 -0
- package/dist/src/kernel/store.js +838 -0
- package/dist/src/kernel/store.js.map +1 -0
- package/dist/src/kernel/templates/index.d.ts +140 -0
- package/dist/src/kernel/templates/index.js +266 -0
- package/dist/src/kernel/templates/index.js.map +1 -0
- package/dist/src/kernel/types.d.ts +326 -0
- package/dist/src/kernel/types.js +19 -0
- package/dist/src/kernel/types.js.map +1 -0
- package/dist/src/run-ledger.d.ts +57 -3
- package/dist/src/run-ledger.js +45 -2
- package/dist/src/run-ledger.js.map +1 -1
- package/dist/src/session-manager.d.ts +176 -129
- package/dist/src/session-manager.js +652 -603
- package/dist/src/session-manager.js.map +1 -1
- package/dist/src/types.d.ts +33 -3
- package/dist/src/ultraapp/build.d.ts +117 -3
- package/dist/src/ultraapp/build.js +319 -3
- package/dist/src/ultraapp/build.js.map +1 -1
- package/dist/src/ultraapp/contract.d.ts +52 -0
- package/dist/src/ultraapp/contract.js +83 -0
- package/dist/src/ultraapp/contract.js.map +1 -0
- package/dist/src/ultraapp/conventions.js +9 -2
- package/dist/src/ultraapp/conventions.js.map +1 -1
- package/dist/src/ultraapp/fix-on-failure.d.ts +21 -2
- package/dist/src/ultraapp/fix-on-failure.js +46 -62
- package/dist/src/ultraapp/fix-on-failure.js.map +1 -1
- package/dist/src/ultraapp/manager.d.ts +107 -2
- package/dist/src/ultraapp/manager.js +305 -86
- package/dist/src/ultraapp/manager.js.map +1 -1
- package/dist/src/verify/baseline.d.ts +73 -0
- package/dist/src/verify/baseline.js +186 -0
- package/dist/src/verify/baseline.js.map +1 -0
- package/dist/src/verify/contract.d.ts +116 -0
- package/dist/src/verify/contract.js +142 -0
- package/dist/src/verify/contract.js.map +1 -0
- package/dist/src/verify/evidence.d.ts +61 -0
- package/dist/src/verify/evidence.js +133 -0
- package/dist/src/verify/evidence.js.map +1 -0
- package/dist/src/verify/runner.d.ts +63 -0
- package/dist/src/verify/runner.js +317 -0
- package/dist/src/verify/runner.js.map +1 -0
- package/openclaw.plugin.json +8 -0
- package/package.json +2 -2
- package/skills/SKILL.md +120 -79
- package/skills/references/acp.md +17 -17
- package/skills/references/autoloop.md +139 -65
- package/skills/references/claude-cli-tracking.md +4 -4
- package/skills/references/cli.md +101 -59
- package/skills/references/council.md +109 -37
- package/skills/references/dashboard.md +34 -6
- package/skills/references/getting-started.md +13 -13
- package/skills/references/inbox.md +4 -4
- package/skills/references/mcp.md +39 -34
- package/skills/references/multi-engine.md +51 -47
- package/skills/references/observability.md +88 -28
- package/skills/references/openai-compat.md +39 -39
- package/skills/references/sessions.md +43 -25
- package/skills/references/tools.md +402 -309
- package/skills/references/ultra.md +45 -45
- package/skills/references/ultraapp.md +126 -50
- package/skills/references/verification.md +187 -0
- package/skills/references/workflow.md +362 -0
- package/dist/src/ultraapp/fix-on-failure-session.d.ts +0 -23
- package/dist/src/ultraapp/fix-on-failure-session.js +0 -51
- package/dist/src/ultraapp/fix-on-failure-session.js.map +0 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy mode shapes, projected from a kernel run.
|
|
3
|
+
*
|
|
4
|
+
* Before 6.0.0 each mode kept its results in its own `Map` on SessionManager,
|
|
5
|
+
* evicted after 30 minutes, gone on restart. The shapes those maps held —
|
|
6
|
+
* `CouncilSession`, `FanoutSession`, `UltraplanResult`, `UltrareviewResult`,
|
|
7
|
+
* `AutoloopState` — are the public contract of the `*_status` tools, so they
|
|
8
|
+
* survive; what changes is where they come from. They are now derived from the
|
|
9
|
+
* durable run record, which is why a fan-out's results are still there tomorrow.
|
|
10
|
+
*
|
|
11
|
+
* Pure functions, no I/O beyond the caller-supplied text, so the mapping is
|
|
12
|
+
* testable without starting anything.
|
|
13
|
+
*/
|
|
14
|
+
/** Node id used by every single-node legacy workflow, so projections know where to look. */
|
|
15
|
+
export const LEGACY_NODE = 'main';
|
|
16
|
+
function node(record, id = LEGACY_NODE) {
|
|
17
|
+
return record.nodes[id];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The node's declaration, used as the fallback for fields the node has not
|
|
21
|
+
* published yet. A run polled the instant it starts has no `data`, and reading
|
|
22
|
+
* the task out of the spec beats reporting the workflow's name as the task.
|
|
23
|
+
*/
|
|
24
|
+
function spec(record, id = LEGACY_NODE) {
|
|
25
|
+
return record.spec?.nodes?.find((n) => n.id === id);
|
|
26
|
+
}
|
|
27
|
+
/** Terminal states that mean "it ran to the end", whatever the verdict was. */
|
|
28
|
+
function isDone(state) {
|
|
29
|
+
return state === 'completed' || state === 'failed' || state === 'cancelled';
|
|
30
|
+
}
|
|
31
|
+
export function toCouncilSession(record) {
|
|
32
|
+
const data = node(record)?.data;
|
|
33
|
+
const declared = spec(record);
|
|
34
|
+
return {
|
|
35
|
+
id: record.runId,
|
|
36
|
+
task: data?.task ?? declared?.task ?? record.workflow,
|
|
37
|
+
config: {
|
|
38
|
+
agents: data?.agents ?? declared?.agents ?? [],
|
|
39
|
+
maxRounds: data?.maxRounds ?? declared?.maxRounds ?? 0,
|
|
40
|
+
projectDir: data?.projectDir ?? record.cwd,
|
|
41
|
+
},
|
|
42
|
+
responses: data?.responses ?? [],
|
|
43
|
+
// The council's own status is preserved when it reported one; otherwise the
|
|
44
|
+
// run's state stands in. A run that was cancelled or failed before the
|
|
45
|
+
// council spoke has no council status to report, and inventing `consensus`
|
|
46
|
+
// for it would be the exact kind of claim this release removes.
|
|
47
|
+
status: data?.councilStatus ?? (record.state === 'running' ? 'running' : 'error'),
|
|
48
|
+
startTime: record.createdAt,
|
|
49
|
+
endTime: record.endedAt,
|
|
50
|
+
finalSummary: data?.finalSummary,
|
|
51
|
+
compactContext: data?.compactContext,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function toFanoutSession(record) {
|
|
55
|
+
const data = node(record)?.data;
|
|
56
|
+
const declared = spec(record);
|
|
57
|
+
const status = record.state === 'cancelled'
|
|
58
|
+
? 'aborted'
|
|
59
|
+
: record.state === 'failed'
|
|
60
|
+
? 'error'
|
|
61
|
+
: isDone(record.state)
|
|
62
|
+
? 'done'
|
|
63
|
+
: 'running';
|
|
64
|
+
return {
|
|
65
|
+
id: record.runId,
|
|
66
|
+
status,
|
|
67
|
+
task: data?.task ?? declared?.prompt ?? record.workflow,
|
|
68
|
+
agentCount: data?.agentCount ?? declared?.agents?.length ?? 0,
|
|
69
|
+
startedAt: record.createdAt,
|
|
70
|
+
finishedAt: record.endedAt,
|
|
71
|
+
results: data?.results ?? [],
|
|
72
|
+
synthesis: data?.synthesis,
|
|
73
|
+
error: record.error,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// ─── Ultraplan ──────────────────────────────────────────────────────────────
|
|
77
|
+
/**
|
|
78
|
+
* An engine that answers with a bare error string counts as failed even though
|
|
79
|
+
* the turn technically completed. Kept verbatim from the previous
|
|
80
|
+
* implementation so behaviour does not shift under existing callers.
|
|
81
|
+
*/
|
|
82
|
+
const LOOKS_LIKE_ERROR = /^(Error|not logged in|authentication|auth failed|permission denied)/i;
|
|
83
|
+
export function toUltraplanResult(record, plan) {
|
|
84
|
+
const text = (plan ?? '').trim();
|
|
85
|
+
const failed = record.state === 'failed' || record.state === 'cancelled';
|
|
86
|
+
const empty = isDone(record.state) && (!text || LOOKS_LIKE_ERROR.test(text));
|
|
87
|
+
const status = !isDone(record.state) ? 'running' : failed || empty ? 'error' : 'completed';
|
|
88
|
+
return {
|
|
89
|
+
id: record.runId,
|
|
90
|
+
status,
|
|
91
|
+
sessionName: `${record.runId}-${LEGACY_NODE}-a1`,
|
|
92
|
+
startTime: record.createdAt,
|
|
93
|
+
endTime: record.endedAt,
|
|
94
|
+
plan: status === 'completed' ? text : undefined,
|
|
95
|
+
error: status === 'error' ? (record.error ?? text ?? 'Empty response from engine') : undefined,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// ─── Ultrareview ────────────────────────────────────────────────────────────
|
|
99
|
+
export function toUltrareviewResult(record, findings) {
|
|
100
|
+
const data = node(record)?.data;
|
|
101
|
+
const declared = spec(record);
|
|
102
|
+
const status = record.state === 'failed' || record.state === 'cancelled'
|
|
103
|
+
? 'error'
|
|
104
|
+
: isDone(record.state)
|
|
105
|
+
? 'completed'
|
|
106
|
+
: 'running';
|
|
107
|
+
return {
|
|
108
|
+
id: record.runId,
|
|
109
|
+
status,
|
|
110
|
+
// Kept for the UltrareviewResult contract; it now holds the run id, which is
|
|
111
|
+
// also the fan-out id, because they are the same run.
|
|
112
|
+
councilId: record.runId,
|
|
113
|
+
agentCount: data?.agentCount ?? declared?.agents?.length ?? 0,
|
|
114
|
+
startTime: record.createdAt,
|
|
115
|
+
endTime: record.endedAt,
|
|
116
|
+
findings: status === 'completed' ? findings : undefined,
|
|
117
|
+
error: status === 'error' ? record.error : undefined,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** Join per-agent results into the review text, when no synthesis pass ran. */
|
|
121
|
+
export function joinFindings(data) {
|
|
122
|
+
if (!data)
|
|
123
|
+
return undefined;
|
|
124
|
+
if (data.synthesis)
|
|
125
|
+
return data.synthesis;
|
|
126
|
+
const ok = data.results.filter((r) => r.ok && r.output);
|
|
127
|
+
if (ok.length === 0)
|
|
128
|
+
return undefined;
|
|
129
|
+
return ok.map((r) => `## ${r.agent}\n\n${r.output}`).join('\n\n---\n\n');
|
|
130
|
+
}
|
|
131
|
+
/** Engine label used when a legacy caller supplied none. */
|
|
132
|
+
export const DEFAULT_ENGINE = 'claude';
|
|
133
|
+
//# sourceMappingURL=projections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projections.js","sourceRoot":"","sources":["../../../src/kernel/projections.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository fingerprinting for ledger rows.
|
|
3
|
+
*
|
|
4
|
+
* Only what can be read off a manifest file. The point of stamping `repoLang` is
|
|
5
|
+
* to make the ledger groupable later ("which engine is better at TypeScript test
|
|
6
|
+
* failures"), and a guessed label would poison exactly the comparison it is meant
|
|
7
|
+
* to enable — so anything not evidenced by a manifest stays undefined.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Detect the primary language of a checkout. A JS manifest plus a tsconfig reads
|
|
11
|
+
* as TypeScript, since that is what the code in it actually is.
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectRepoLang(cwd: string): string | undefined;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository fingerprinting for ledger rows.
|
|
3
|
+
*
|
|
4
|
+
* Only what can be read off a manifest file. The point of stamping `repoLang` is
|
|
5
|
+
* to make the ledger groupable later ("which engine is better at TypeScript test
|
|
6
|
+
* failures"), and a guessed label would poison exactly the comparison it is meant
|
|
7
|
+
* to enable — so anything not evidenced by a manifest stays undefined.
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
/** Manifest → language, in precedence order. First hit wins. */
|
|
12
|
+
const MANIFESTS = [
|
|
13
|
+
['package.json', 'javascript'],
|
|
14
|
+
['tsconfig.json', 'typescript'],
|
|
15
|
+
['pyproject.toml', 'python'],
|
|
16
|
+
['setup.py', 'python'],
|
|
17
|
+
['requirements.txt', 'python'],
|
|
18
|
+
['go.mod', 'go'],
|
|
19
|
+
['Cargo.toml', 'rust'],
|
|
20
|
+
['pom.xml', 'java'],
|
|
21
|
+
['build.gradle', 'java'],
|
|
22
|
+
['build.gradle.kts', 'kotlin'],
|
|
23
|
+
['Gemfile', 'ruby'],
|
|
24
|
+
['composer.json', 'php'],
|
|
25
|
+
['CMakeLists.txt', 'cpp'],
|
|
26
|
+
['Package.swift', 'swift'],
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Detect the primary language of a checkout. A JS manifest plus a tsconfig reads
|
|
30
|
+
* as TypeScript, since that is what the code in it actually is.
|
|
31
|
+
*/
|
|
32
|
+
export function detectRepoLang(cwd) {
|
|
33
|
+
if (!cwd)
|
|
34
|
+
return undefined;
|
|
35
|
+
let found;
|
|
36
|
+
for (const [file, lang] of MANIFESTS) {
|
|
37
|
+
let exists = false;
|
|
38
|
+
try {
|
|
39
|
+
exists = fs.existsSync(path.join(cwd, file));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
exists = false;
|
|
43
|
+
}
|
|
44
|
+
if (!exists)
|
|
45
|
+
continue;
|
|
46
|
+
if (lang === 'typescript')
|
|
47
|
+
return 'typescript';
|
|
48
|
+
found ??= lang;
|
|
49
|
+
}
|
|
50
|
+
if (found === 'javascript') {
|
|
51
|
+
// package.json with a "types"/"typescript" signal but no tsconfig at the root.
|
|
52
|
+
try {
|
|
53
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
54
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
55
|
+
if ('typescript' in deps)
|
|
56
|
+
return 'typescript';
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Unreadable manifest — keep the weaker answer rather than inventing one.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return found;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.js","sourceRoot":"","sources":["../../../src/kernel/repo.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,gEAAgE;AAChE,MAAM,SAAS,GAA4B;IACzC,CAAC,cAAc,EAAE,YAAY,CAAC;IAC9B,CAAC,eAAe,EAAE,YAAY,CAAC;IAC/B,CAAC,gBAAgB,EAAE,QAAQ,CAAC;IAC5B,CAAC,UAAU,EAAE,QAAQ,CAAC;IACtB,CAAC,kBAAkB,EAAE,QAAQ,CAAC;IAC9B,CAAC,QAAQ,EAAE,IAAI,CAAC;IAChB,CAAC,YAAY,EAAE,MAAM,CAAC;IACtB,CAAC,SAAS,EAAE,MAAM,CAAC;IACnB,CAAC,cAAc,EAAE,MAAM,CAAC;IACxB,CAAC,kBAAkB,EAAE,QAAQ,CAAC;IAC9B,CAAC,SAAS,EAAE,MAAM,CAAC;IACnB,CAAC,eAAe,EAAE,KAAK,CAAC;IACxB,CAAC,gBAAgB,EAAE,KAAK,CAAC;IACzB,CAAC,eAAe,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,IAAI,KAAyB,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACrC,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,IAAI,IAAI,KAAK,YAAY;YAAE,OAAO,YAAY,CAAC;QAC/C,KAAK,KAAK,IAAI,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;QAC3B,+EAA+E;QAC/E,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAA4B,CAAC;YAC3G,MAAM,IAAI,GAAG,EAAE,GAAI,GAAG,CAAC,YAAuB,EAAE,GAAI,GAAG,CAAC,eAA0B,EAAE,CAAC;YACrF,IAAI,YAAY,IAAI,IAAI;gBAAE,OAAO,YAAY,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret references.
|
|
3
|
+
*
|
|
4
|
+
* Custom-engine configs carry credentials, so they are never written to a run's
|
|
5
|
+
* spec and never accepted over HTTP. That left a real gap: a run started with a
|
|
6
|
+
* custom engine could not be resumed after a crash through the dashboard or any
|
|
7
|
+
* remote caller, because the material it needed could not be supplied.
|
|
8
|
+
*
|
|
9
|
+
* A reference closes it without putting anything on the wire. The caller names a
|
|
10
|
+
* secret; the server resolves the name from its own environment. The name is not
|
|
11
|
+
* sensitive, the value never leaves the host, and a name that resolves to
|
|
12
|
+
* nothing is an error rather than a silent start with no credentials.
|
|
13
|
+
*/
|
|
14
|
+
/** Environment variable a reference resolves to. */
|
|
15
|
+
export declare function secretEnvVar(ref: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a reference to a custom-engine config.
|
|
18
|
+
*
|
|
19
|
+
* Throws when the name is unknown: starting without the credentials the caller
|
|
20
|
+
* asked for would fail later, further from the cause, and possibly after doing
|
|
21
|
+
* work.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveSecretRef(ref: string, env?: NodeJS.ProcessEnv): unknown;
|
|
24
|
+
/** Resolve a `{ role: ref }` map. Absent entries stay absent. */
|
|
25
|
+
export declare function resolveSecretRefs(refs: Record<string, string | undefined> | undefined, env?: NodeJS.ProcessEnv): Record<string, unknown>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret references.
|
|
3
|
+
*
|
|
4
|
+
* Custom-engine configs carry credentials, so they are never written to a run's
|
|
5
|
+
* spec and never accepted over HTTP. That left a real gap: a run started with a
|
|
6
|
+
* custom engine could not be resumed after a crash through the dashboard or any
|
|
7
|
+
* remote caller, because the material it needed could not be supplied.
|
|
8
|
+
*
|
|
9
|
+
* A reference closes it without putting anything on the wire. The caller names a
|
|
10
|
+
* secret; the server resolves the name from its own environment. The name is not
|
|
11
|
+
* sensitive, the value never leaves the host, and a name that resolves to
|
|
12
|
+
* nothing is an error rather than a silent start with no credentials.
|
|
13
|
+
*/
|
|
14
|
+
const PREFIX = 'CLAWO_CUSTOM_ENGINE_';
|
|
15
|
+
/** Environment variable a reference resolves to. */
|
|
16
|
+
export function secretEnvVar(ref) {
|
|
17
|
+
return `${PREFIX}${ref.replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a reference to a custom-engine config.
|
|
21
|
+
*
|
|
22
|
+
* Throws when the name is unknown: starting without the credentials the caller
|
|
23
|
+
* asked for would fail later, further from the cause, and possibly after doing
|
|
24
|
+
* work.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveSecretRef(ref, env = process.env) {
|
|
27
|
+
const key = secretEnvVar(ref);
|
|
28
|
+
const raw = env[key];
|
|
29
|
+
if (!raw) {
|
|
30
|
+
throw new Error(`Unknown secret reference '${ref}': set ${key} on the orchestrator host to the JSON custom-engine config`);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(raw);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
throw new Error(`Secret reference '${ref}' (${key}) is not valid JSON: ${err.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Resolve a `{ role: ref }` map. Absent entries stay absent. */
|
|
40
|
+
export function resolveSecretRefs(refs, env = process.env) {
|
|
41
|
+
const out = {};
|
|
42
|
+
for (const [role, ref] of Object.entries(refs ?? {})) {
|
|
43
|
+
if (ref)
|
|
44
|
+
out[role] = resolveSecretRef(ref, env);
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../../src/kernel/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,GAAG,sBAAsB,CAAC;AAEtC,oDAAoD;AACpD,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;AACxE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,MAAyB,OAAO,CAAC,GAAG;IAChF,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,6BAA6B,GAAG,UAAU,GAAG,4DAA4D,CAC1G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,MAAM,GAAG,wBAAyB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACrG,CAAC;AACH,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,iBAAiB,CAC/B,IAAoD,EACpD,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QACrD,IAAI,GAAG;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable run store.
|
|
3
|
+
*
|
|
4
|
+
* Layout, one directory per run under `~/.claw-orchestrator/wf/<runId>/`:
|
|
5
|
+
*
|
|
6
|
+
* spec.json the WorkflowSpec, written once at creation, never mutated
|
|
7
|
+
* run.json the mutable checkpoint, rewritten atomically on every transition
|
|
8
|
+
* events.jsonl append-only audit + stream source
|
|
9
|
+
* nodes/<id>/ per-node artifacts
|
|
10
|
+
* evidence/<id>/ evidence bundles (see verify/evidence.ts)
|
|
11
|
+
*
|
|
12
|
+
* Splitting the immutable spec from the mutable checkpoint is what carries crash
|
|
13
|
+
* recovery: if `run.json` is missing or half-written, the state is rebuilt by
|
|
14
|
+
* replaying `events.jsonl` against `spec.json`. The atomic rewrite makes that
|
|
15
|
+
* path rare; the replay makes it usually survivable.
|
|
16
|
+
*
|
|
17
|
+
* The checkpoint and the events it describes are written by the same
|
|
18
|
+
* transaction, so they cannot disagree: a commit that reports `committed` has
|
|
19
|
+
* both, and one that does not has neither. That was not always true — the event
|
|
20
|
+
* append used to swallow its own errors while the replay treated the same log as
|
|
21
|
+
* authoritative, which is a contradiction sitting in the recovery path. The
|
|
22
|
+
* replay is now a plain fallback for a lost `run.json`; a run that lost both it
|
|
23
|
+
* and the log is unrecoverable, and `loadRun` returns undefined rather than
|
|
24
|
+
* inventing a state.
|
|
25
|
+
*
|
|
26
|
+
* There is exactly one way to write to a run — `commit()` — and it is not
|
|
27
|
+
* optional: the checkpoint writer and the event appender are module-private, and
|
|
28
|
+
* a batch is published by a single atomic directory rename rather than by a
|
|
29
|
+
* sequence of writes that can each fail on their own. `atomicWriteJson` replaces
|
|
30
|
+
* four near-identical implementations that had grown across the codebase
|
|
31
|
+
* (`session-manager.ts` sync + async variants, `ultraapp/store.ts`,
|
|
32
|
+
* `ultraapp/patcher.ts`); the tmp-name shape is kept from the ultraapp one,
|
|
33
|
+
* whose comment records the CI bug it fixed: a reader catching a plain
|
|
34
|
+
* `writeFile` mid-flight and parsing a truncated object.
|
|
35
|
+
*/
|
|
36
|
+
import type { Logger } from '../logger.js';
|
|
37
|
+
import type { KernelEvent, RunRecord, RunState, WorkflowSpec } from './types.js';
|
|
38
|
+
export declare function wfDir(): string;
|
|
39
|
+
export declare function isValidRunId(runId: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a run's directory. Throws on an invalid id rather than returning a
|
|
42
|
+
* path, because this is the chokepoint every other path derives from — checking
|
|
43
|
+
* here means no caller can forget to.
|
|
44
|
+
*/
|
|
45
|
+
export declare function runDir(runId: string): string;
|
|
46
|
+
export declare function nodeDir(runId: string, nodeId: string): string;
|
|
47
|
+
/** Write JSON so a concurrent reader sees either the old file or the new one, never a partial. */
|
|
48
|
+
export declare function atomicWriteJson(file: string, value: unknown): void;
|
|
49
|
+
export declare function runExists(runId: string): boolean;
|
|
50
|
+
export declare function sanitizeForDisk<T>(value: T): T;
|
|
51
|
+
export declare function loadSpec(runId: string): WorkflowSpec | undefined;
|
|
52
|
+
export declare function readEvents(runId: string, limit?: number): KernelEvent[];
|
|
53
|
+
/**
|
|
54
|
+
* Rebuild a run's state from its spec plus its event log. Used when `run.json`
|
|
55
|
+
* is absent or unparseable — the belt to the atomic rewrite's braces.
|
|
56
|
+
*/
|
|
57
|
+
export declare function replayRun(runId: string, spec: WorkflowSpec): RunRecord | undefined;
|
|
58
|
+
/** Load a run, preferring the checkpoint and falling back to an event replay. */
|
|
59
|
+
export declare function loadRun(runId: string): RunRecord | undefined;
|
|
60
|
+
export declare function listRunIds(): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Remove a run's directory. Only ever called from an explicit user delete —
|
|
63
|
+
* nothing in the kernel prunes runs on its own, for the same reason the ledger
|
|
64
|
+
* does not: deleting someone's history is not ours to decide.
|
|
65
|
+
*/
|
|
66
|
+
export declare function deleteRunDir(runId: string, logger?: Logger): void;
|
|
67
|
+
/**
|
|
68
|
+
* Full text a node produced, preferring the artifact file over the record's
|
|
69
|
+
* preview. `NodeRecord.output` is capped so a checkpoint stays small, but the
|
|
70
|
+
* whole point of some nodes is their text — an ultraplan whose plan is silently
|
|
71
|
+
* cut at 4 kB has lost the deliverable.
|
|
72
|
+
*/
|
|
73
|
+
export declare function readNodeOutput(runId: string, nodeId: string): string | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Where a node artifact will live, without writing it.
|
|
76
|
+
*
|
|
77
|
+
* Pure, because the checkpoint that references an artifact and the artifact
|
|
78
|
+
* itself are written in the same {@link commit}: the caller needs the path
|
|
79
|
+
* before the write, to put it in the record it is committing.
|
|
80
|
+
*/
|
|
81
|
+
export declare function nodeArtifactPath(runId: string, nodeId: string, name: string): string;
|
|
82
|
+
export declare const LEASE_TTL_MS = 60000;
|
|
83
|
+
/** How often a live owner refreshes its claim, independently of any work it is doing. */
|
|
84
|
+
export declare const LEASE_HEARTBEAT_MS = 15000;
|
|
85
|
+
/**
|
|
86
|
+
* The capability to change a run.
|
|
87
|
+
*
|
|
88
|
+
* Held by the executing kernel, passed to every write, and impossible to forge
|
|
89
|
+
* from a run id alone — which is what makes `commit` a boundary rather than a
|
|
90
|
+
* convention.
|
|
91
|
+
*/
|
|
92
|
+
export interface RunGuard {
|
|
93
|
+
runId: string;
|
|
94
|
+
incarnationId: string;
|
|
95
|
+
ownerId: string;
|
|
96
|
+
acquisitionId: string;
|
|
97
|
+
fence: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Who holds a run, as recorded on disk.
|
|
101
|
+
*
|
|
102
|
+
* `ownerId` is the identity, not `pid`. Two `RunKernel` instances in one process
|
|
103
|
+
* — two SessionManagers is not a strange thing to have — share a pid, so
|
|
104
|
+
* treating the pid as the owner let both of them execute the same run and call
|
|
105
|
+
* it re-entrancy. The pid is kept because it is what tells us whether the holder
|
|
106
|
+
* is still alive; it is not what tells us whether the holder is us.
|
|
107
|
+
*/
|
|
108
|
+
export interface RunLease extends RunGuard {
|
|
109
|
+
pid: number;
|
|
110
|
+
host: string;
|
|
111
|
+
acquiredAt: string;
|
|
112
|
+
renewedAt: string;
|
|
113
|
+
}
|
|
114
|
+
/** What one `commit` may change. Everything durable about a run is in here. */
|
|
115
|
+
export interface CommitBatch {
|
|
116
|
+
/** The new checkpoint. */
|
|
117
|
+
record?: RunRecord;
|
|
118
|
+
/** Appended to `events.jsonl`, in order. */
|
|
119
|
+
events?: KernelEvent[];
|
|
120
|
+
/** Node artifact files, published together with the record that references them. */
|
|
121
|
+
artifacts?: Array<{
|
|
122
|
+
nodeId: string;
|
|
123
|
+
name: string;
|
|
124
|
+
body: string;
|
|
125
|
+
}>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Why a write did or did not happen.
|
|
129
|
+
*
|
|
130
|
+
* Three outcomes rather than a boolean, because the two failures call for
|
|
131
|
+
* opposite responses: `superseded` means stop permanently, `blocked` means this
|
|
132
|
+
* attempt did not get through and the run is still ours.
|
|
133
|
+
*/
|
|
134
|
+
export type CommitOutcome = 'committed' | 'superseded' | 'blocked';
|
|
135
|
+
export interface CommitResult {
|
|
136
|
+
outcome: CommitOutcome;
|
|
137
|
+
reason?: string;
|
|
138
|
+
}
|
|
139
|
+
export type ReleaseOutcome = 'released' | 'not-ours' | 'blocked';
|
|
140
|
+
export declare function readLease(runId: string): RunLease | undefined;
|
|
141
|
+
/**
|
|
142
|
+
* True when a lease no longer represents a live owner.
|
|
143
|
+
*
|
|
144
|
+
* On the same host the pid is the authority and the heartbeat is not consulted:
|
|
145
|
+
* a process running one long node makes no checkpoints, and judging it dead for
|
|
146
|
+
* that reason is how two owners end up running the same work. The heartbeat is
|
|
147
|
+
* the fallback for a holder we cannot ask about — another machine, or a pid
|
|
148
|
+
* whose meaning we cannot trust across hosts.
|
|
149
|
+
*/
|
|
150
|
+
export declare function leaseIsStale(lease: RunLease | undefined, now?: number): boolean;
|
|
151
|
+
/** Apply anything a crashed owner committed but did not finish writing. */
|
|
152
|
+
export declare function recoverPending(runId: string, logger?: Logger): boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Create a run and claim it, atomically.
|
|
155
|
+
*
|
|
156
|
+
* The non-recursive `mkdir` IS the claim: it fails with EEXIST for everyone but
|
|
157
|
+
* the first caller, so exactly one process can create a given run id. The
|
|
158
|
+
* previous version asked `runExists()` and then created with
|
|
159
|
+
* `{ recursive: true }`, which is a check-then-write race and lost it routinely
|
|
160
|
+
* — two processes creating the same id 80 times had both "succeed" 76 times,
|
|
161
|
+
* leaving one workflow executing while the other's `spec.json` sat on disk, and
|
|
162
|
+
* a lease belonging to an incarnation that had already been overwritten.
|
|
163
|
+
*/
|
|
164
|
+
export declare function createAndAcquire(runId: string, spec: WorkflowSpec, ownerId: string): RunGuard;
|
|
165
|
+
/**
|
|
166
|
+
* Claim an existing run and receive the capability to write to it.
|
|
167
|
+
*
|
|
168
|
+
* Refused when someone else's claim is still live. Allowed for the same owner —
|
|
169
|
+
* but the guard it returns is a NEW one, and the previous guard is dead from
|
|
170
|
+
* that moment: re-acquiring is a fresh claim, not a renewal of the old one, so
|
|
171
|
+
* anything still holding the old guard stops being able to write.
|
|
172
|
+
*/
|
|
173
|
+
export declare function acquireLease(runId: string, ownerId: string): RunGuard;
|
|
174
|
+
/** Heartbeat. Best-effort: losing a renewal must not break the run. */
|
|
175
|
+
export declare function renewLease(guard: RunGuard): void;
|
|
176
|
+
/**
|
|
177
|
+
* The one way to change anything durable about a run.
|
|
178
|
+
*
|
|
179
|
+
* The guard is verified and the transaction is published inside a single
|
|
180
|
+
* critical section, so a holder that has been superseded cannot land a write
|
|
181
|
+
* between the check and the change.
|
|
182
|
+
*
|
|
183
|
+
* Callers pass a record they have already produced by copying and mutating,
|
|
184
|
+
* never the record they are still using: `commit` persists what it is given, and
|
|
185
|
+
* the caller adopts it only on `committed`. That is what keeps a refused write
|
|
186
|
+
* from leaving a run *in memory* claiming a state the disk rejected.
|
|
187
|
+
*/
|
|
188
|
+
export declare function commit(guard: RunGuard, batch: CommitBatch, logger?: Logger): CommitResult;
|
|
189
|
+
/**
|
|
190
|
+
* Release the claim. The incarnation file is deliberately NOT removed — the
|
|
191
|
+
* fence must keep increasing for as long as this creation of the run exists.
|
|
192
|
+
*
|
|
193
|
+
* `blocked` is not `not-ours`: an owner that is standing down and cannot take
|
|
194
|
+
* the lock still has to hand the run back, so the caller retries rather than
|
|
195
|
+
* leaving a lease behind that no one can take over.
|
|
196
|
+
*/
|
|
197
|
+
export declare function releaseLease(guard: RunGuard): ReleaseOutcome;
|
|
198
|
+
export interface RunSummary {
|
|
199
|
+
runId: string;
|
|
200
|
+
workflow: string;
|
|
201
|
+
state: RunState;
|
|
202
|
+
outcome: RunRecord['outcome'];
|
|
203
|
+
cwd: string;
|
|
204
|
+
createdAt: string;
|
|
205
|
+
updatedAt: string;
|
|
206
|
+
endedAt?: string;
|
|
207
|
+
currentNode?: string;
|
|
208
|
+
evidenceId?: string;
|
|
209
|
+
error?: string;
|
|
210
|
+
costUsd?: number;
|
|
211
|
+
}
|
|
212
|
+
export interface ListRunsQuery {
|
|
213
|
+
workflow?: string;
|
|
214
|
+
state?: RunState;
|
|
215
|
+
limit?: number;
|
|
216
|
+
}
|
|
217
|
+
export declare function summarize(record: RunRecord): RunSummary;
|
|
218
|
+
/**
|
|
219
|
+
* Every run on this machine, newest first — the cross-process listing the three
|
|
220
|
+
* mode-specific enumerations each did differently (council regex-scraped its own
|
|
221
|
+
* markdown transcripts, autoloop read a JSONL registry, ultraapp walked a store
|
|
222
|
+
* directory). No separate index file is needed here because every run lives
|
|
223
|
+
* under one root, so the directory *is* the index.
|
|
224
|
+
*/
|
|
225
|
+
export declare function listRuns(query?: ListRunsQuery): RunSummary[];
|