@enderfga/claw-orchestrator 7.12.1 → 7.14.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 +14 -11
- package/configs/engines/README.md +20 -0
- package/configs/engines/dsh.json +17 -0
- package/configs/engines/kimi-code.json +17 -0
- package/dist/bin/cli.js +123 -34
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/acp-server.js +4 -2
- package/dist/src/acp-server.js.map +1 -1
- package/dist/src/autoloop/dispatcher.d.ts +19 -0
- package/dist/src/autoloop/dispatcher.js +57 -0
- package/dist/src/autoloop/dispatcher.js.map +1 -1
- package/dist/src/autoloop/runner.d.ts +102 -2
- package/dist/src/autoloop/runner.js +367 -44
- package/dist/src/autoloop/runner.js.map +1 -1
- package/dist/src/autoloop/types.d.ts +17 -0
- package/dist/src/autoloop/types.js.map +1 -1
- package/dist/src/dashboard/index.html +79 -73
- package/dist/src/embedded-server.js +35 -7
- package/dist/src/embedded-server.js.map +1 -1
- package/dist/src/engine-presets.d.ts +7 -3
- package/dist/src/engine-presets.js +39 -6
- package/dist/src/engine-presets.js.map +1 -1
- package/dist/src/engine-spawn.d.ts +8 -1
- package/dist/src/engine-spawn.js +13 -3
- package/dist/src/engine-spawn.js.map +1 -1
- package/dist/src/index.js +78 -71
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/agent-step.d.ts +3 -0
- package/dist/src/kernel/agent-step.js.map +1 -1
- package/dist/src/kernel/engine.d.ts +5 -2
- package/dist/src/kernel/engine.js +11 -12
- package/dist/src/kernel/engine.js.map +1 -1
- package/dist/src/kernel/nodes/autoloop.d.ts +32 -2
- package/dist/src/kernel/nodes/autoloop.js +122 -15
- package/dist/src/kernel/nodes/autoloop.js.map +1 -1
- package/dist/src/kernel/nodes/goal.d.ts +94 -0
- package/dist/src/kernel/nodes/goal.js +386 -0
- package/dist/src/kernel/nodes/goal.js.map +1 -0
- package/dist/src/kernel/nodes/index.d.ts +2 -1
- package/dist/src/kernel/nodes/index.js +3 -1
- package/dist/src/kernel/nodes/index.js.map +1 -1
- package/dist/src/kernel/templates/index.d.ts +27 -3
- package/dist/src/kernel/templates/index.js +78 -1
- package/dist/src/kernel/templates/index.js.map +1 -1
- package/dist/src/kernel/types.d.ts +41 -2
- package/dist/src/kernel/types.js.map +1 -1
- package/dist/src/persistent-acp-session.d.ts +76 -0
- package/dist/src/persistent-acp-session.js +409 -0
- package/dist/src/persistent-acp-session.js.map +1 -0
- package/dist/src/persistent-agy-session.js +1 -0
- package/dist/src/persistent-agy-session.js.map +1 -1
- package/dist/src/persistent-codex-app-session.d.ts +55 -26
- package/dist/src/persistent-codex-app-session.js +109 -24
- package/dist/src/persistent-codex-app-session.js.map +1 -1
- package/dist/src/persistent-codex-session.js +1 -0
- package/dist/src/persistent-codex-session.js.map +1 -1
- package/dist/src/persistent-cursor-session.js +1 -0
- package/dist/src/persistent-cursor-session.js.map +1 -1
- package/dist/src/persistent-custom-session.js +2 -0
- package/dist/src/persistent-custom-session.js.map +1 -1
- package/dist/src/persistent-gemini-session.js +1 -0
- package/dist/src/persistent-gemini-session.js.map +1 -1
- package/dist/src/persistent-grok-session.js +1 -0
- package/dist/src/persistent-grok-session.js.map +1 -1
- package/dist/src/persistent-opencode-session.js +1 -0
- package/dist/src/persistent-opencode-session.js.map +1 -1
- package/dist/src/persistent-session.js +1 -0
- package/dist/src/persistent-session.js.map +1 -1
- package/dist/src/sanitize.d.ts +10 -0
- package/dist/src/sanitize.js +26 -0
- package/dist/src/sanitize.js.map +1 -1
- package/dist/src/session-manager.d.ts +25 -20
- package/dist/src/session-manager.js +92 -43
- package/dist/src/session-manager.js.map +1 -1
- package/dist/src/types.d.ts +35 -1
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/skills/SKILL.md +8 -5
- package/skills/references/acp.md +4 -0
- package/skills/references/autoloop.md +27 -2
- package/skills/references/claude-cli-tracking.md +3 -1
- package/skills/references/cli.md +79 -39
- package/skills/references/dashboard.md +5 -1
- package/skills/references/multi-engine.md +39 -10
- package/skills/references/tools.md +77 -65
- package/skills/references/workflow.md +87 -13
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
import type { NodeContext, NodeResult } from '../engine.js';
|
|
16
16
|
import type { NodeSpec, RunRecord } from '../types.js';
|
|
17
17
|
import type { AutoloopState } from '../../autoloop/types.js';
|
|
18
|
+
import { type AutoloopInputSnapshot, type InFlightChat, type PendingChatEntry } from '../../autoloop/runner.js';
|
|
19
|
+
export declare const AUTOLOOP_RESTORE_MALFORMED = "Autoloop resume refused: stored chat checkpoint is malformed";
|
|
20
|
+
export declare const AUTOLOOP_RESTORE_UNPERSISTED = "Autoloop resume refused: pending chat could not be checkpointed";
|
|
18
21
|
/** Whatever SessionManager hands back from booting the loop. */
|
|
19
22
|
export interface AutoloopHandle {
|
|
20
23
|
runner: {
|
|
@@ -22,6 +25,12 @@ export interface AutoloopHandle {
|
|
|
22
25
|
stop(): void;
|
|
23
26
|
on(event: 'state', listener: () => void): unknown;
|
|
24
27
|
off(event: 'state', listener: () => void): unknown;
|
|
28
|
+
/** The accepted-chat snapshot the next checkpoint should carry. */
|
|
29
|
+
checkpointInput?(): AutoloopInputSnapshot;
|
|
30
|
+
/** Before ready: record a restored in-flight marker as a notice. */
|
|
31
|
+
reconcileRestoredInput?(): void;
|
|
32
|
+
/** After ready: queue or park restored pending chat. */
|
|
33
|
+
releaseRestoredInput?(): void;
|
|
25
34
|
};
|
|
26
35
|
dispatcher: {
|
|
27
36
|
sessionNames: {
|
|
@@ -32,11 +41,21 @@ export interface AutoloopHandle {
|
|
|
32
41
|
}): Promise<void>;
|
|
33
42
|
};
|
|
34
43
|
ledgerDir: string;
|
|
44
|
+
/** Called synchronously by `RunKernel.cancel`. */
|
|
45
|
+
abort?(): void;
|
|
46
|
+
/**
|
|
47
|
+
* After the initial publish and notice reconciliation, before `ready`.
|
|
48
|
+
* SessionManager uses this for committed-iteration recovery so it cannot
|
|
49
|
+
* send before the publisher gate. Failure rejects the start the same way
|
|
50
|
+
* a refused initial publish does.
|
|
51
|
+
*/
|
|
52
|
+
afterPublishReconcile?(): Promise<void>;
|
|
35
53
|
}
|
|
36
54
|
export interface AutoloopNodeDeps {
|
|
37
55
|
/** `secrets` carries what the spec deliberately does not — custom-engine configs. */
|
|
38
56
|
boot(config: Record<string, unknown>, secrets: Record<string, unknown>, opts: {
|
|
39
57
|
freshStart: boolean;
|
|
58
|
+
restoredInput?: AutoloopInputSnapshot;
|
|
40
59
|
}): Promise<AutoloopHandle>;
|
|
41
60
|
/**
|
|
42
61
|
* Resolve/reject the deferred the caller is waiting on.
|
|
@@ -56,9 +75,10 @@ export interface AutoloopNodeDeps {
|
|
|
56
75
|
/**
|
|
57
76
|
* Hand back a way to refresh the checkpoint mid-run. The loop's role
|
|
58
77
|
* selection changes when `spawn_subagents` picks engines, and that has to
|
|
59
|
-
* reach the record — it used to be written to the registry file.
|
|
78
|
+
* reach the record — it used to be written to the registry file. Returns
|
|
79
|
+
* true only when the refreshed payload was committed.
|
|
60
80
|
*/
|
|
61
|
-
registerPublisher(runId: string, publish: () =>
|
|
81
|
+
registerPublisher(runId: string, publish: () => boolean): void;
|
|
62
82
|
unregisterPublisher(runId: string): void;
|
|
63
83
|
/** Extra payload to publish alongside the state (currently the role selection). */
|
|
64
84
|
extra(runId: string): Record<string, unknown>;
|
|
@@ -69,7 +89,17 @@ export interface AutoloopNodeData {
|
|
|
69
89
|
plannerSession: string;
|
|
70
90
|
/** Engines/models actually in use, as chosen by `spawn_subagents`. */
|
|
71
91
|
roleSelection?: Record<string, unknown>;
|
|
92
|
+
/** Accepted chat not yet handed to the dispatcher, in order. */
|
|
93
|
+
pendingChat?: ReadonlyArray<PendingChatEntry>;
|
|
94
|
+
/** The accepted chat whose physical send may have started. */
|
|
95
|
+
inFlightChat?: InFlightChat | null;
|
|
72
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Validate the accepted-chat fields of a stored node payload. Absent fields
|
|
99
|
+
* restore as empty; anything malformed or over the acceptance bounds is
|
|
100
|
+
* refused with one fixed error.
|
|
101
|
+
*/
|
|
102
|
+
export declare function validateStoredInput(data: unknown): AutoloopInputSnapshot;
|
|
73
103
|
export declare function makeAutoloopExecutor(deps: AutoloopNodeDeps): (node: NodeSpec, ctx: NodeContext) => Promise<NodeResult>;
|
|
74
104
|
/**
|
|
75
105
|
* Rebuild an `AutoloopState` for a run that is not live in this process.
|
|
@@ -12,35 +12,137 @@
|
|
|
12
12
|
* closed over itself. The node spec carries only JSON, because it is
|
|
13
13
|
* checkpointed with the run.
|
|
14
14
|
*/
|
|
15
|
+
import { MAX_PENDING_CHAT, MAX_PENDING_CHAT_BYTES, pendingChatBytes, } from '../../autoloop/runner.js';
|
|
16
|
+
export const AUTOLOOP_RESTORE_MALFORMED = 'Autoloop resume refused: stored chat checkpoint is malformed';
|
|
17
|
+
export const AUTOLOOP_RESTORE_UNPERSISTED = 'Autoloop resume refused: pending chat could not be checkpointed';
|
|
18
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
19
|
+
const isIsoTimestamp = (value) => {
|
|
20
|
+
if (typeof value !== 'string')
|
|
21
|
+
return false;
|
|
22
|
+
const ms = Date.parse(value);
|
|
23
|
+
return Number.isFinite(ms) && new Date(ms).toISOString() === value;
|
|
24
|
+
};
|
|
25
|
+
const isCount = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
26
|
+
/**
|
|
27
|
+
* Validate the accepted-chat fields of a stored node payload. Absent fields
|
|
28
|
+
* restore as empty; anything malformed or over the acceptance bounds is
|
|
29
|
+
* refused with one fixed error.
|
|
30
|
+
*/
|
|
31
|
+
export function validateStoredInput(data) {
|
|
32
|
+
const refuse = () => {
|
|
33
|
+
throw new Error(AUTOLOOP_RESTORE_MALFORMED);
|
|
34
|
+
};
|
|
35
|
+
if (data === undefined || data === null)
|
|
36
|
+
return { pendingChat: [], inFlightChat: null };
|
|
37
|
+
if (!isRecord(data))
|
|
38
|
+
return refuse();
|
|
39
|
+
const pendingChat = [];
|
|
40
|
+
if (data.pendingChat !== undefined) {
|
|
41
|
+
if (!Array.isArray(data.pendingChat) || data.pendingChat.length > MAX_PENDING_CHAT)
|
|
42
|
+
return refuse();
|
|
43
|
+
const ids = new Set();
|
|
44
|
+
for (const raw of data.pendingChat) {
|
|
45
|
+
if (!isRecord(raw))
|
|
46
|
+
return refuse();
|
|
47
|
+
const { msg_id, iter, ts, text } = raw;
|
|
48
|
+
if (typeof msg_id !== 'string' || msg_id.length === 0 || ids.has(msg_id))
|
|
49
|
+
return refuse();
|
|
50
|
+
if (!isCount(iter) || !isIsoTimestamp(ts) || typeof text !== 'string')
|
|
51
|
+
return refuse();
|
|
52
|
+
ids.add(msg_id);
|
|
53
|
+
pendingChat.push({ msg_id, iter, ts, text });
|
|
54
|
+
}
|
|
55
|
+
if (pendingChatBytes(pendingChat) > MAX_PENDING_CHAT_BYTES)
|
|
56
|
+
return refuse();
|
|
57
|
+
}
|
|
58
|
+
let inFlightChat = null;
|
|
59
|
+
if (data.inFlightChat !== undefined && data.inFlightChat !== null) {
|
|
60
|
+
const raw = data.inFlightChat;
|
|
61
|
+
if (!isRecord(raw) || typeof raw.msg_id !== 'string' || raw.msg_id.length === 0)
|
|
62
|
+
return refuse();
|
|
63
|
+
if (!isIsoTimestamp(raw.handed_at))
|
|
64
|
+
return refuse();
|
|
65
|
+
inFlightChat = { msg_id: raw.msg_id, handed_at: raw.handed_at };
|
|
66
|
+
}
|
|
67
|
+
let unconfirmed;
|
|
68
|
+
const state = data.state;
|
|
69
|
+
if (isRecord(state) && state.unconfirmed_chat !== undefined) {
|
|
70
|
+
const raw = state.unconfirmed_chat;
|
|
71
|
+
if (!isRecord(raw) || !isCount(raw.count) || !isIsoTimestamp(raw.last_at))
|
|
72
|
+
return refuse();
|
|
73
|
+
if (typeof raw.history_pending !== 'boolean')
|
|
74
|
+
return refuse();
|
|
75
|
+
unconfirmed = { count: raw.count, last_at: raw.last_at, history_pending: raw.history_pending };
|
|
76
|
+
}
|
|
77
|
+
return { pendingChat, inFlightChat, ...(unconfirmed ? { unconfirmed } : {}) };
|
|
78
|
+
}
|
|
79
|
+
function hasObligations(input) {
|
|
80
|
+
return Boolean(input && (input.pendingChat.length > 0 || input.inFlightChat || input.unconfirmed?.history_pending));
|
|
81
|
+
}
|
|
82
|
+
function nodePayload(handle, extra) {
|
|
83
|
+
const input = handle.runner.checkpointInput?.();
|
|
84
|
+
const state = { ...handle.runner.state };
|
|
85
|
+
if (input?.unconfirmed)
|
|
86
|
+
state.unconfirmed_chat = input.unconfirmed;
|
|
87
|
+
else
|
|
88
|
+
delete state.unconfirmed_chat;
|
|
89
|
+
return {
|
|
90
|
+
state,
|
|
91
|
+
plannerSession: handle.dispatcher.sessionNames.planner,
|
|
92
|
+
...extra,
|
|
93
|
+
...(input && input.pendingChat.length > 0 ? { pendingChat: input.pendingChat } : {}),
|
|
94
|
+
...(input?.inFlightChat ? { inFlightChat: input.inFlightChat } : {}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
15
97
|
export function makeAutoloopExecutor(deps) {
|
|
16
98
|
return async function executeAutoloopNode(node, ctx) {
|
|
17
99
|
const spec = node;
|
|
100
|
+
const readyKey = ctx.tag ?? ctx.runId;
|
|
18
101
|
let handle;
|
|
102
|
+
let restoredInput;
|
|
19
103
|
try {
|
|
20
104
|
// Kernel commits visits += 1 before execute; first visit in the run is fresh.
|
|
21
105
|
const freshStart = ctx.record.nodes[node.id]?.visits === 1;
|
|
22
|
-
|
|
106
|
+
if (!freshStart)
|
|
107
|
+
restoredInput = validateStoredInput(ctx.record.nodes[node.id]?.data);
|
|
108
|
+
handle = await deps.boot(spec.config, ctx.secrets, { freshStart, restoredInput });
|
|
23
109
|
}
|
|
24
110
|
catch (err) {
|
|
25
|
-
deps.ready(
|
|
111
|
+
deps.ready(readyKey, err);
|
|
26
112
|
return { ok: false, error: err.message };
|
|
27
113
|
}
|
|
28
114
|
ctx.setHandle(handle);
|
|
29
|
-
const publish = () => ctx.publish(
|
|
30
|
-
state: handle.runner.state,
|
|
31
|
-
plannerSession: handle.dispatcher.sessionNames.planner,
|
|
32
|
-
...deps.extra(ctx.runId),
|
|
33
|
-
});
|
|
115
|
+
const publish = () => ctx.publish(nodePayload(handle, deps.extra(ctx.runId)));
|
|
34
116
|
deps.registerPublisher(ctx.runId, publish);
|
|
35
|
-
publish()
|
|
117
|
+
if (!publish() && hasObligations(restoredInput)) {
|
|
118
|
+
// Restored chat that cannot be checkpointed must not be delivered.
|
|
119
|
+
deps.unregisterPublisher(ctx.runId);
|
|
120
|
+
handle.runner.stop();
|
|
121
|
+
await handle.dispatcher.shutdown('start-failed').catch(() => undefined);
|
|
122
|
+
deps.ready(readyKey, new Error(AUTOLOOP_RESTORE_UNPERSISTED));
|
|
123
|
+
return { ok: false, error: AUTOLOOP_RESTORE_UNPERSISTED };
|
|
124
|
+
}
|
|
36
125
|
// The runner emits state after it writes pause fields (send timeout and
|
|
37
126
|
// operator pause). Re-publish that snapshot so a non-owning autoloop_status
|
|
38
127
|
// can read it while this process still owns the live handle.
|
|
39
128
|
handle.runner.on('state', publish);
|
|
40
|
-
|
|
129
|
+
handle.runner.reconcileRestoredInput?.();
|
|
130
|
+
try {
|
|
131
|
+
await handle.afterPublishReconcile?.();
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
handle.runner.off('state', publish);
|
|
135
|
+
deps.unregisterPublisher(ctx.runId);
|
|
136
|
+
handle.runner.stop();
|
|
137
|
+
await handle.dispatcher.shutdown('start-failed').catch(() => undefined);
|
|
138
|
+
deps.ready(readyKey, err);
|
|
139
|
+
return { ok: false, error: err.message };
|
|
140
|
+
}
|
|
141
|
+
deps.ready(readyKey, {
|
|
41
142
|
plannerSession: handle.dispatcher.sessionNames.planner,
|
|
42
143
|
state: handle.runner.state,
|
|
43
144
|
});
|
|
145
|
+
handle.runner.releaseRestoredInput?.();
|
|
44
146
|
try {
|
|
45
147
|
await deps.waitForExit(handle, ctx.signal);
|
|
46
148
|
}
|
|
@@ -54,11 +156,7 @@ export function makeAutoloopExecutor(deps) {
|
|
|
54
156
|
ok: status !== 'crashed',
|
|
55
157
|
output: `autoloop ${status} at iter ${handle.runner.state.iter}`,
|
|
56
158
|
error: status === 'crashed' ? (handle.runner.state.status_reason ?? 'autoloop crashed') : undefined,
|
|
57
|
-
data:
|
|
58
|
-
state: handle.runner.state,
|
|
59
|
-
plannerSession: handle.dispatcher.sessionNames.planner,
|
|
60
|
-
...deps.extra(ctx.runId),
|
|
61
|
-
},
|
|
159
|
+
data: nodePayload(handle, deps.extra(ctx.runId)),
|
|
62
160
|
};
|
|
63
161
|
};
|
|
64
162
|
}
|
|
@@ -75,8 +173,17 @@ export function autoloopStateFromRecord(record) {
|
|
|
75
173
|
if (!data?.state)
|
|
76
174
|
return undefined;
|
|
77
175
|
const terminal = record.state === 'completed' || record.state === 'failed' || record.state === 'cancelled';
|
|
78
|
-
|
|
176
|
+
const state = terminal && data.state.status !== 'terminated' && data.state.status !== 'crashed'
|
|
79
177
|
? { ...data.state, status: 'terminated', status_reason: data.state.status_reason ?? `run ${record.state}` }
|
|
80
178
|
: data.state;
|
|
179
|
+
const inFlight = data.inFlightChat;
|
|
180
|
+
if (!isRecord(inFlight) || typeof inFlight.handed_at !== 'string')
|
|
181
|
+
return state;
|
|
182
|
+
// A stored marker is a delivery nobody has confirmed yet.
|
|
183
|
+
const prev = state.unconfirmed_chat;
|
|
184
|
+
return {
|
|
185
|
+
...state,
|
|
186
|
+
unconfirmed_chat: { count: prev?.count ?? 0, last_at: prev?.last_at ?? inFlight.handed_at, history_pending: true },
|
|
187
|
+
};
|
|
81
188
|
}
|
|
82
189
|
//# sourceMappingURL=autoloop.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoloop.js","sourceRoot":"","sources":["../../../../src/kernel/nodes/autoloop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"autoloop.js","sourceRoot":"","sources":["../../../../src/kernel/nodes/autoloop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `goal` node — a Codex `/goal` run on one `codex-app` thread.
|
|
3
|
+
*
|
|
4
|
+
* Codex's app-server runs a goal itself: `thread/goal/set` starts its turn loop,
|
|
5
|
+
* which keeps going until the goal is `complete`, out of budget, blocked, or
|
|
6
|
+
* stopped by a usage limit. What the kernel adds is what a long unattended run
|
|
7
|
+
* needs around that loop:
|
|
8
|
+
*
|
|
9
|
+
* - **Reattach, never restart.** The thread id is checkpointed as soon as the
|
|
10
|
+
* session opens. A resumed run hands it to `thread/resume`, and a resume
|
|
11
|
+
* that comes back on a different thread fails the attempt instead of quietly
|
|
12
|
+
* starting the objective over.
|
|
13
|
+
* - **Resume what stalls, within a bound.** A usage-limited goal, a paused
|
|
14
|
+
* one, or an active goal with no turn and no news for `stallMs` is set back
|
|
15
|
+
* to `active` after a backoff. Every attempt counts before it is made, and
|
|
16
|
+
* past `capacityRetry.max` the node fails for good.
|
|
17
|
+
* - **Repair on the same thread.** Revisited after a red verifier, the node
|
|
18
|
+
* sets a new objective on the thread it already has — the failing checks
|
|
19
|
+
* first, then the original objective — so the agent keeps its context.
|
|
20
|
+
* - **Steer live.** Text from `workflow_steer` goes into the running turn via
|
|
21
|
+
* `turn/steer`; with no turn running it waits for the next one.
|
|
22
|
+
*/
|
|
23
|
+
import type { NodeContext, NodeResult } from '../engine.js';
|
|
24
|
+
import type { NodeSpec } from '../types.js';
|
|
25
|
+
export type GoalStatus = 'active' | 'paused' | 'blocked' | 'usageLimited' | 'budgetLimited' | 'complete';
|
|
26
|
+
export interface GoalSnapshot {
|
|
27
|
+
objective: string;
|
|
28
|
+
status: GoalStatus;
|
|
29
|
+
tokenBudget: number | null;
|
|
30
|
+
tokensUsed: number;
|
|
31
|
+
timeUsedSeconds: number;
|
|
32
|
+
}
|
|
33
|
+
/** The `codex-app` surface this node drives. `PersistentCodexAppServerSession` implements it. */
|
|
34
|
+
export interface GoalSession {
|
|
35
|
+
readonly codexThreadId: string | undefined;
|
|
36
|
+
readonly goal: GoalSnapshot | null;
|
|
37
|
+
readonly lastError: {
|
|
38
|
+
message: string;
|
|
39
|
+
code: string | null;
|
|
40
|
+
willRetry: boolean;
|
|
41
|
+
at: string;
|
|
42
|
+
} | null;
|
|
43
|
+
readonly turnActive: boolean;
|
|
44
|
+
setGoal(p: {
|
|
45
|
+
objective?: string;
|
|
46
|
+
status?: 'active' | 'paused';
|
|
47
|
+
tokenBudget?: number | null;
|
|
48
|
+
}): Promise<GoalSnapshot>;
|
|
49
|
+
getGoal(): Promise<GoalSnapshot | null>;
|
|
50
|
+
clearGoal(): Promise<void>;
|
|
51
|
+
steer(text: string): Promise<{
|
|
52
|
+
steered: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
interrupt(): Promise<{
|
|
55
|
+
interrupted: boolean;
|
|
56
|
+
}>;
|
|
57
|
+
on(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
58
|
+
off(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
59
|
+
}
|
|
60
|
+
/** What the node checkpoints. Readable by `workflow_status` while the goal runs. */
|
|
61
|
+
export interface GoalNodeData {
|
|
62
|
+
threadId: string;
|
|
63
|
+
objective: string;
|
|
64
|
+
status: GoalStatus | 'unset';
|
|
65
|
+
tokensUsed: number;
|
|
66
|
+
timeUsedSeconds: number;
|
|
67
|
+
/** Resume attempts made after a usage limit, a pause or a stall. */
|
|
68
|
+
resumes: number;
|
|
69
|
+
/** The agent's last reply, at most 4,000 characters from its end. */
|
|
70
|
+
lastMessage?: string;
|
|
71
|
+
/** The thread's spend so far, priced from codex's cumulative token totals. */
|
|
72
|
+
costUsd?: number;
|
|
73
|
+
/**
|
|
74
|
+
* How much of `costUsd` has been reported to the run. A result reports the
|
|
75
|
+
* difference, so spend from an attempt that never returned — the process was
|
|
76
|
+
* killed — is counted by the attempt that finally does.
|
|
77
|
+
*/
|
|
78
|
+
reportedCostUsd?: number;
|
|
79
|
+
lastError?: {
|
|
80
|
+
message: string;
|
|
81
|
+
code: string | null;
|
|
82
|
+
at: string;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** Codex refuses a goal objective longer than this many characters. */
|
|
86
|
+
export declare const GOAL_OBJECTIVE_MAX_CHARS = 4000;
|
|
87
|
+
/**
|
|
88
|
+
* The objective a revisit sets: what the agent must hear first (steers, the
|
|
89
|
+
* failing checks), then the original objective. The original always survives
|
|
90
|
+
* whole; when the two do not fit in Codex's limit the preface is cut, and the
|
|
91
|
+
* full check output stays in the run's evidence.
|
|
92
|
+
*/
|
|
93
|
+
export declare function composeObjective(preface: string[], objective: string): string;
|
|
94
|
+
export declare function executeGoalNode(node: NodeSpec, ctx: NodeContext): Promise<NodeResult>;
|