@astrosheep/keiyaku 2.9.8 → 2.9.10
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 +50 -182
- package/build/.tsbuildinfo +1 -1
- package/build/agents/harness/control-types.js +18 -1
- package/build/agents/harness/index.js +2 -0
- package/build/agents/harness/outcome-error.js +81 -0
- package/build/agents/harness/projection.js +10 -7
- package/build/agents/harness/router.js +5 -1
- package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
- package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
- package/build/agents/providers/claude-agent-sdk/session.js +19 -5
- package/build/agents/providers/codex-app-server/adapter.js +11 -6
- package/build/agents/providers/codex-app-server/events.js +67 -6
- package/build/agents/providers/codex-app-server/registration.js +11 -1
- package/build/agents/providers/opencode-sdk/adapter.js +8 -1
- package/build/agents/providers/opencode-sdk/events.js +24 -2
- package/build/agents/providers/opencode-sdk/registration.js +11 -1
- package/build/agents/providers/pi/checkpoint.js +9 -1
- package/build/agents/providers/pi/events.js +2 -0
- package/build/agents/providers/pi/registration.js +14 -1
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/akuma/meta.js +3 -3
- package/build/cli/commands/akuma/catalog.js +2 -2
- package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
- package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
- package/build/cli/commands/akuma.js +1 -1
- package/build/cli/commands/contract/amend/handler.js +5 -4
- package/build/cli/commands/contract/arc/handler.js +5 -4
- package/build/cli/commands/contract/audit/handler.js +2 -2
- package/build/cli/commands/contract/bind/handler.js +12 -3
- package/build/cli/commands/contract/bind/meta.js +45 -15
- package/build/cli/commands/contract/forfeit/handler.js +9 -5
- package/build/cli/commands/contract/log/handler.js +2 -2
- package/build/cli/commands/contract/petition/handler.js +34 -7
- package/build/cli/commands/contract/renew/handler.js +7 -7
- package/build/cli/commands/contract/renew/meta.js +3 -3
- package/build/cli/commands/metadata.js +10 -6
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -0
- package/build/cli/commands/projection/history/handler.js +21 -0
- package/build/cli/commands/projection/history/meta.js +9 -0
- package/build/cli/commands/projection/shared.js +3 -4
- package/build/cli/commands/projection/status/handler.js +3 -3
- package/build/cli/commands/projection/wait/handler.js +20 -11
- package/build/cli/commands/task/add/handler.js +14 -5
- package/build/cli/commands/task/add/meta.js +2 -2
- package/build/cli/commands/task/catalog.js +4 -0
- package/build/cli/commands/task/done/handler.js +1 -4
- package/build/cli/commands/task/done/meta.js +1 -1
- package/build/cli/commands/task/drop/meta.js +1 -1
- package/build/cli/commands/task/hold/handler.js +3 -0
- package/build/cli/commands/task/hold/meta.js +8 -0
- package/build/cli/commands/task/log/handler.js +1 -1
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/resume/handler.js +3 -0
- package/build/cli/commands/task/resume/meta.js +8 -0
- package/build/cli/commands/task/shared.js +39 -14
- package/build/cli/commands/task/show/handler.js +1 -1
- package/build/cli/commands/task/show/meta.js +1 -1
- package/build/cli/commands/task/start/meta.js +1 -1
- package/build/cli/commands/task/stop/handler.js +1 -4
- package/build/cli/commands/task/stop/meta.js +1 -1
- package/build/cli/commands/task/update/handler.js +11 -3
- package/build/cli/commands/task/update/meta.js +3 -3
- package/build/cli/completion.js +3 -2
- package/build/cli/flags.js +51 -14
- package/build/cli/help.js +4 -1
- package/build/cli/index.js +40 -25
- package/build/cli/parse-flags.js +40 -7
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +2 -3
- package/build/cli/parse.js +40 -6
- package/build/cli/projection-address.js +12 -10
- package/build/cli/render/amendment-warning.js +8 -0
- package/build/cli/render/arc.js +6 -2
- package/build/cli/render/audit.js +400 -81
- package/build/cli/render/kanshi.js +242 -79
- package/build/cli/render/lifecycle.js +56 -0
- package/build/cli/render/misc.js +24 -28
- package/build/cli/render/petition.js +5 -0
- package/build/cli/render/projection-activity.js +65 -29
- package/build/cli/render/projection-history.js +64 -0
- package/build/cli/render/shared.js +23 -9
- package/build/cli/render/status.js +13 -35
- package/build/cli/render/task.js +51 -26
- package/build/cli/render/terminal-failure.js +3 -1
- package/build/cli/render/wait.js +45 -69
- package/build/cli/subagent-guard.js +1 -1
- package/build/cli/types.js +1 -1
- package/build/config/env-keys.js +3 -1
- package/build/config/env.js +24 -1
- package/build/config/settings/schema.js +3 -1
- package/build/core/address-carrier.js +89 -0
- package/build/core/addressing.js +255 -196
- package/build/core/amend.js +115 -38
- package/build/core/amendment-criteria.js +17 -0
- package/build/core/amendment-warning.js +10 -0
- package/build/core/arc.js +184 -83
- package/build/core/audit/candidate.js +110 -9
- package/build/core/audit/coordinates.js +120 -65
- package/build/core/audit/evidence.js +19 -23
- package/build/core/audit/facade.js +34 -11
- package/build/core/audit/report.js +5 -5
- package/build/core/bind-reconciliation.js +390 -0
- package/build/core/bind-workspace.js +12 -20
- package/build/core/bind.js +242 -273
- package/build/core/call/call.js +1 -0
- package/build/core/call/context.js +46 -71
- package/build/core/call/prompt.js +6 -3
- package/build/core/context.js +49 -35
- package/build/core/contract-carrier-runtime.js +408 -0
- package/build/core/contract-carrier.js +270 -0
- package/build/core/contract-view.js +33 -27
- package/build/core/contract.js +5 -1
- package/build/core/derived-replay.js +207 -0
- package/build/core/draft.js +125 -57
- package/build/core/forfeit.js +113 -56
- package/build/core/identifier-slug.js +63 -13
- package/build/core/ids.js +4 -4
- package/build/core/lifecycle-history.js +35 -0
- package/build/core/lifecycle-recovery.js +117 -0
- package/build/core/lifecycle-runner.js +377 -0
- package/build/core/log.js +37 -11
- package/build/core/outcome-base.js +181 -0
- package/build/core/process-group.js +139 -0
- package/build/core/projection/generation/database.js +82 -7
- package/build/core/projection/generation/model.js +9 -36
- package/build/core/projection/generation/projection-generation-continuation.js +10 -22
- package/build/core/projection/generation/projection-generation-execution.js +13 -6
- package/build/core/projection/index.js +5 -4
- package/build/core/projection/projection-activity.js +29 -8
- package/build/core/projection/projection-alias.js +23 -10
- package/build/core/projection/projection-core.js +21 -7
- package/build/core/projection/projection-execution-observer.js +91 -39
- package/build/core/projection/projection-history.js +474 -0
- package/build/core/projection/projection-runner-lock.js +12 -0
- package/build/core/projection/projection-status-observation.js +365 -0
- package/build/core/projection/projection-status.js +35 -337
- package/build/core/projection/projection-terminal-failure.js +13 -8
- package/build/core/projection/projection-wait.js +7 -2
- package/build/core/projection/tell/launch-store.js +244 -0
- package/build/core/projection/tell/model.js +3 -3
- package/build/core/projection/tell/store.js +70 -118
- package/build/core/registry.js +22 -87
- package/build/core/renew-build.js +424 -0
- package/build/core/renew-plan.js +239 -0
- package/build/core/renew-rewrite.js +77 -0
- package/build/core/renew.js +295 -520
- package/build/core/repository-ledger/accepted-fold-read.js +19 -0
- package/build/core/repository-ledger/accepted-tail.js +116 -0
- package/build/core/repository-ledger/admission.js +38 -0
- package/build/core/repository-ledger/authoritative-transaction.js +1 -0
- package/build/core/repository-ledger/claim-evidence.js +147 -0
- package/build/core/repository-ledger/codec.js +297 -0
- package/build/core/repository-ledger/current-state-store.js +853 -0
- package/build/core/repository-ledger/fold-repository.js +375 -0
- package/build/core/repository-ledger/fold.js +248 -0
- package/build/core/repository-ledger/git-object-store.js +243 -0
- package/build/core/repository-ledger/history-read.js +15 -0
- package/build/core/repository-ledger/incremental-read.js +153 -0
- package/build/core/repository-ledger/index.js +15 -0
- package/build/core/repository-ledger/inventory-plan.js +75 -0
- package/build/core/repository-ledger/inventory.js +310 -0
- package/build/core/repository-ledger/publication-admission.js +125 -0
- package/build/core/repository-ledger/publication.js +353 -0
- package/build/core/repository-ledger/read-model.js +469 -0
- package/build/core/repository-ledger/transaction-membership.js +78 -0
- package/build/core/repository-ledger/traversal.js +404 -0
- package/build/core/repository-ledger/write-transaction.js +339 -0
- package/build/core/seal.js +168 -161
- package/build/core/settlement/claim-delivery.js +42 -215
- package/build/core/settlement/claim.js +235 -153
- package/build/core/settlement/index.js +4 -4
- package/build/core/settlement/petition-claim-gates.js +52 -33
- package/build/core/settlement/petition-forfeit.js +12 -5
- package/build/core/settlement/petition-head-guard.js +8 -5
- package/build/core/settlement/petition-preview.js +68 -56
- package/build/core/settlement/petition-window.js +50 -0
- package/build/core/settlement/petition.js +252 -131
- package/build/core/settlement/queue-read-model.js +72 -95
- package/build/core/settlement/queue-seat-allocation.js +35 -126
- package/build/core/settlement/queue.js +1 -1
- package/build/core/settlement/settlement.js +172 -80
- package/build/core/settlement/verdict.js +112 -66
- package/build/core/settlement/verification-coordination.js +305 -0
- package/build/core/settlement/verification-supervisor.js +275 -0
- package/build/core/settlement/verification.js +398 -31
- package/build/core/status/board.js +125 -127
- package/build/core/status/lifecycle.js +7 -80
- package/build/core/structured-query.js +23 -0
- package/build/core/target-ref.js +1 -1
- package/build/core/task/board.js +59 -24
- package/build/core/task/commands.js +181 -33
- package/build/core/task/coordinate.js +56 -0
- package/build/core/task/document.js +25 -9
- package/build/core/task/index.js +7 -4
- package/build/core/task/query.js +17 -27
- package/build/core/task/settlement-git.js +164 -179
- package/build/core/task/settlement-policy.js +15 -11
- package/build/core/task/source-board.js +31 -13
- package/build/core/task/task-bind-preparation.js +193 -0
- package/build/core/task/task-contract.js +102 -193
- package/build/core/task/task-file-transaction.js +171 -0
- package/build/core/task/task-git-runtime.js +76 -14
- package/build/core/task/task-git-store.js +68 -169
- package/build/core/task/task-store-repository.js +13 -6
- package/build/core/task/task-worktree.js +6 -33
- package/build/core/task/task.js +1 -1
- package/build/core/task/tree.js +3 -3
- package/build/core/task/validation.js +4 -3
- package/build/core/transcripts.js +93 -1
- package/build/core/worktree-bootstrap.js +44 -36
- package/build/core/worktree-path.js +210 -77
- package/build/flow-error.js +7 -7
- package/build/generated/version.js +2 -2
- package/build/git/core.js +76 -13
- package/build/git/refs.js +168 -46
- package/build/git/streaming-batch.js +73 -13
- package/package.json +1 -1
- package/skills/keiyaku/SKILL.md +1 -1
- package/skills/keiyaku-akuma/SKILL.md +3 -3
- package/skills/keiyaku-task/SKILL.md +4 -2
- package/skills/keiyaku-workflow/SKILL.md +88 -203
- package/build/core/entry.js +0 -305
- package/build/core/ledger-batch.js +0 -194
- package/build/core/ledger.js +0 -84
- package/build/core/projection/leash.js +0 -87
- package/build/core/ref-log.js +0 -119
- package/build/core/renew-session.js +0 -128
- package/build/core/status/ledger-batch.js +0 -1
- package/build/core/status/reconciliation.js +0 -137
|
@@ -1 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Adapters call this only with durable admissions their native event proved.
|
|
3
|
+
* A duplicate tell coordinate is ambiguous and intentionally yields no receipt.
|
|
4
|
+
*/
|
|
5
|
+
export function exactLaunchInputReceipts(stage, fence, admissions) {
|
|
6
|
+
const counts = new Map();
|
|
7
|
+
for (const admission of admissions) {
|
|
8
|
+
for (const tellId of admission.tellIds)
|
|
9
|
+
counts.set(tellId, (counts.get(tellId) ?? 0) + 1);
|
|
10
|
+
}
|
|
11
|
+
if ([...counts.values()].some((count) => count !== 1))
|
|
12
|
+
return [];
|
|
13
|
+
return [...counts.keys()].sort().map((tellId) => ({
|
|
14
|
+
stage,
|
|
15
|
+
correlation: { kind: "exact", tellId },
|
|
16
|
+
launchInput: { fence },
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { activityEventBodySchema, AGENT_EVENT_TEXT_MAX_CHARS, AGENT_UNKNOWN_PAYLOAD_MAX_CHARS, } from "./events.js";
|
|
2
2
|
export { EXEC_EVENT_BUFFER_HARD_CAP } from "./event-channel.js";
|
|
3
3
|
export { createAbortError } from "./runtime.js";
|
|
4
|
+
export { exactLaunchInputReceipts } from "./control-types.js";
|
|
4
5
|
export { createProviderFailure, isProviderTerminalError, ProviderTerminalError, toProviderTerminalError, } from "./provider-adapter.js";
|
|
5
6
|
export { restoreOutcome } from "./outcome.js";
|
|
7
|
+
export { OutcomeErrorValidationError, parseCurrentOutcomeError, } from "./outcome-error.js";
|
|
6
8
|
export { call, revive } from "./projection.js";
|
|
7
9
|
export { isRecoverableSessionEnvelope } from "./session.js";
|
|
8
10
|
export { OUTCOME_ERROR_VERSION, SubagentFailure } from "../types.js";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { OUTCOME_ERROR_VERSION, SUBAGENT_FAILURE_CODES, } from "../types.js";
|
|
2
|
+
const OUTCOME_ERROR_CODES = new Set(SUBAGENT_FAILURE_CODES);
|
|
3
|
+
const OUTCOME_ERROR_KEYS = new Set([
|
|
4
|
+
"version",
|
|
5
|
+
"code",
|
|
6
|
+
"message",
|
|
7
|
+
"timeoutMs",
|
|
8
|
+
"exitCode",
|
|
9
|
+
"publicCause",
|
|
10
|
+
]);
|
|
11
|
+
export class OutcomeErrorValidationError extends Error {
|
|
12
|
+
incompatible;
|
|
13
|
+
constructor(message, incompatible = false) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.incompatible = incompatible;
|
|
16
|
+
this.name = "OutcomeErrorValidationError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function isPlainObject(value) {
|
|
20
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21
|
+
return false;
|
|
22
|
+
const prototype = Object.getPrototypeOf(value);
|
|
23
|
+
return prototype === Object.prototype || prototype === null;
|
|
24
|
+
}
|
|
25
|
+
function isJsonCompatible(value, ancestors) {
|
|
26
|
+
if (value === null || typeof value === "boolean" || typeof value === "string")
|
|
27
|
+
return true;
|
|
28
|
+
if (typeof value === "number")
|
|
29
|
+
return Number.isFinite(value);
|
|
30
|
+
if (typeof value !== "object")
|
|
31
|
+
return false;
|
|
32
|
+
if (ancestors.has(value))
|
|
33
|
+
return false;
|
|
34
|
+
ancestors.add(value);
|
|
35
|
+
try {
|
|
36
|
+
if (Array.isArray(value))
|
|
37
|
+
return value.every((item) => isJsonCompatible(item, ancestors));
|
|
38
|
+
return isPlainObject(value) && Object.values(value).every((item) => isJsonCompatible(item, ancestors));
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
ancestors.delete(value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Parses the one current persisted OutcomeError schema at the harness boundary. */
|
|
45
|
+
export function parseCurrentOutcomeError(value, location) {
|
|
46
|
+
const error = isPlainObject(value) ? value : undefined;
|
|
47
|
+
const version = error?.version;
|
|
48
|
+
if (!error || version !== OUTCOME_ERROR_VERSION) {
|
|
49
|
+
throw new OutcomeErrorValidationError(`${location} has incompatible version ${version === undefined ? "missing" : String(version)}; expected ${OUTCOME_ERROR_VERSION}`, true);
|
|
50
|
+
}
|
|
51
|
+
if (Object.keys(error).some((key) => !OUTCOME_ERROR_KEYS.has(key))) {
|
|
52
|
+
throw new OutcomeErrorValidationError(`${location} is not the current closed OutcomeError shape`);
|
|
53
|
+
}
|
|
54
|
+
if (typeof error.code !== "string" || !OUTCOME_ERROR_CODES.has(error.code)) {
|
|
55
|
+
throw new OutcomeErrorValidationError(`${location}.code is not a recognized subagent failure code`);
|
|
56
|
+
}
|
|
57
|
+
if (typeof error.message !== "string") {
|
|
58
|
+
throw new OutcomeErrorValidationError(`${location}.message must be a string`);
|
|
59
|
+
}
|
|
60
|
+
if (error.timeoutMs !== null && (typeof error.timeoutMs !== "number"
|
|
61
|
+
|| !Number.isSafeInteger(error.timeoutMs)
|
|
62
|
+
|| error.timeoutMs < 0)) {
|
|
63
|
+
throw new OutcomeErrorValidationError(`${location}.timeoutMs must be null or a non-negative safe integer`);
|
|
64
|
+
}
|
|
65
|
+
if (error.exitCode !== null && (typeof error.exitCode !== "number"
|
|
66
|
+
|| !Number.isSafeInteger(error.exitCode))) {
|
|
67
|
+
throw new OutcomeErrorValidationError(`${location}.exitCode must be null or a safe integer`);
|
|
68
|
+
}
|
|
69
|
+
if (error.publicCause !== undefined && (!isPlainObject(error.publicCause)
|
|
70
|
+
|| !isJsonCompatible(error.publicCause, new Set()))) {
|
|
71
|
+
throw new OutcomeErrorValidationError(`${location}.publicCause must be a JSON-compatible harness failure cause`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
version: OUTCOME_ERROR_VERSION,
|
|
75
|
+
code: error.code,
|
|
76
|
+
message: error.message,
|
|
77
|
+
timeoutMs: error.timeoutMs,
|
|
78
|
+
exitCode: error.exitCode,
|
|
79
|
+
...(error.publicCause === undefined ? {} : { publicCause: error.publicCause }),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -91,6 +91,7 @@ function createProjectionState(terms, reviveTerms, options) {
|
|
|
91
91
|
forceWaiters: new Set(),
|
|
92
92
|
onLive() { },
|
|
93
93
|
observer: options?.observer,
|
|
94
|
+
launchTellIds: options?.launchTellIds,
|
|
94
95
|
};
|
|
95
96
|
if (terms.signal) {
|
|
96
97
|
const listener = () => {
|
|
@@ -239,12 +240,14 @@ async function settleProjection(shared) {
|
|
|
239
240
|
}, shared.terms.idleTimeoutMs);
|
|
240
241
|
idleTimeout.unref?.();
|
|
241
242
|
};
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
243
|
+
if (shared.terms.executionTimeoutMs > 0) {
|
|
244
|
+
executionTimeout = setTimeout(() => {
|
|
245
|
+
policyBudgetExceeded = true;
|
|
246
|
+
requestPolicyBudgetCleanup(shared, createAbortError(`${shared.terms.provider} execution policy budget expired`));
|
|
247
|
+
resolvePolicyBudget();
|
|
248
|
+
}, shared.terms.executionTimeoutMs);
|
|
249
|
+
executionTimeout.unref?.();
|
|
250
|
+
}
|
|
248
251
|
const requestLiveAbort = () => {
|
|
249
252
|
if (shared.explicitlyAborted)
|
|
250
253
|
return;
|
|
@@ -259,7 +262,7 @@ async function settleProjection(shared) {
|
|
|
259
262
|
const live = await startLiveProjection(shared.terms, shared.lifecycleAbort.signal, shared.reviveTerms, refreshIdleTimeout, shared.observer, (admission) => {
|
|
260
263
|
recordSessionAdmission(state, admission.session);
|
|
261
264
|
refreshIdleTimeout();
|
|
262
|
-
});
|
|
265
|
+
}, shared.launchTellIds);
|
|
263
266
|
liveDriver = live;
|
|
264
267
|
shared.onLive(live);
|
|
265
268
|
resolveForceWaiters(shared, live);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getProviderRegistration } from "../providers/registry.js";
|
|
2
|
-
export async function startLiveProjection(terms, signal, reviveTerms, onIngress, observer, onSessionAdmission) {
|
|
2
|
+
export async function startLiveProjection(terms, signal, reviveTerms, onIngress, observer, onSessionAdmission, launchTellIds) {
|
|
3
3
|
const onSyncCheckpoint = observer
|
|
4
4
|
? (control, checkpoint) => {
|
|
5
5
|
observer.onCheckpoint(control, checkpoint);
|
|
@@ -13,6 +13,9 @@ export async function startLiveProjection(terms, signal, reviveTerms, onIngress,
|
|
|
13
13
|
}
|
|
14
14
|
: undefined;
|
|
15
15
|
const registration = getProviderRegistration(terms.provider);
|
|
16
|
+
if (observer && registration.tellReceiptCapabilities) {
|
|
17
|
+
await observer.onTellReceiptCapabilities(registration.tellReceiptCapabilities);
|
|
18
|
+
}
|
|
16
19
|
return registration.startLive({
|
|
17
20
|
terms,
|
|
18
21
|
signal,
|
|
@@ -21,6 +24,7 @@ export async function startLiveProjection(terms, signal, reviveTerms, onIngress,
|
|
|
21
24
|
onDeliveryOpportunity,
|
|
22
25
|
onSessionAdmission,
|
|
23
26
|
onIngress,
|
|
27
|
+
launchTellIds,
|
|
24
28
|
observer,
|
|
25
29
|
});
|
|
26
30
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { appendDebugLog } from "../../../telemetry/debug-log.js";
|
|
2
2
|
import { SUBAGENT_SESSION_VERSION } from "../../session.js";
|
|
3
3
|
import { createProjectionDriver } from "../../harness/execution-handle.js";
|
|
4
|
+
import { exactLaunchInputReceipts } from "../../harness/control-types.js";
|
|
4
5
|
import { createProviderFailure } from "../../harness/provider-adapter.js";
|
|
5
6
|
import { buildSubagentEnv, createAbortError, emitProviderEvidence, linkAbortSignal, sha256Utf8 } from "../../harness/runtime.js";
|
|
6
7
|
import { claudeIngressDisposition, emitClaudeThoughts, emitClaudeToolResults, emitClaudeToolStarts, emitClaudeUsage, extractAssistantMessage, extractAssistantMessageId, extractAssistantMessageModel, extractOverloadFallbackWarning, extractResultText, extractResumeToken, isClaudeApiRetryEvent, isClaudeModelRefusalFallbackEvent, isClaudeModelRefusalNoFallbackEvent, mapClaudeApiRetryWarning, } from "./events.js";
|
|
@@ -93,6 +94,7 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
|
93
94
|
const submitted = inputStream.push(toClaudeUserMessage(message.text));
|
|
94
95
|
// submitted = NEXT source next() after this tell is yielded (not the yield pull itself).
|
|
95
96
|
await submitted.waitSubmitted();
|
|
97
|
+
options.onTellReceipt?.({ stage: "seen", correlation: { kind: "exact", tellId: message.id } });
|
|
96
98
|
emitTurnStarted?.();
|
|
97
99
|
// 落账:记录提交时越过的 result ordinal(generation fence 落点)。
|
|
98
100
|
const submittedAfterResultOrdinal = completedResultOrdinal;
|
|
@@ -143,10 +145,19 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
|
143
145
|
const unlinkOuterAbort = linkAbortSignal(options.signal, queryAbortController, () => undefined);
|
|
144
146
|
try {
|
|
145
147
|
const initialMessage = toClaudeUserMessage(prompt);
|
|
146
|
-
inputStream = createPushableUserMessageStream((message) => {
|
|
147
|
-
if (
|
|
148
|
+
inputStream = createPushableUserMessageStream(async (message) => {
|
|
149
|
+
if (message === initialMessage) {
|
|
150
|
+
// Claude's source pull is the provider-native launch-input fact.
|
|
151
|
+
// The adapter records its coordinate before asking heart to reduce
|
|
152
|
+
// the exact attributed receipt.
|
|
153
|
+
const fence = { kind: "ordinal", ordinal: 0 };
|
|
154
|
+
await options.onLaunchAdmission?.({ fence, tellIds: options.launchTellIds ?? [] });
|
|
155
|
+
const admissions = (options.listLaunchAdmissions?.() ?? []).filter((admission) => (admission.fence.kind === "ordinal" && admission.fence.ordinal === fence.ordinal));
|
|
156
|
+
for (const receipt of exactLaunchInputReceipts("seen", fence, admissions))
|
|
157
|
+
options.onTellReceipt?.(receipt);
|
|
148
158
|
return;
|
|
149
|
-
|
|
159
|
+
}
|
|
160
|
+
if (!options.onEvidence)
|
|
150
161
|
return;
|
|
151
162
|
const content = message.message.content;
|
|
152
163
|
const text = typeof content === "string"
|
|
@@ -286,7 +297,6 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
|
286
297
|
bumpResultOrdinal();
|
|
287
298
|
const checkpoint = {
|
|
288
299
|
fence: { kind: "ordinal", ordinal: completedResultOrdinal },
|
|
289
|
-
consumptionEvidence: true,
|
|
290
300
|
};
|
|
291
301
|
// Record the native result before the synchronous pump can
|
|
292
302
|
// consume any tell fenced to an earlier result.
|
|
@@ -300,6 +310,10 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
|
300
310
|
}
|
|
301
311
|
// Sync checkpoint: claim inbox tells and deliver them via steer control.
|
|
302
312
|
options.onSyncCheckpoint?.({ enqueue }, checkpoint);
|
|
313
|
+
const admissions = (options.listLaunchAdmissions?.() ?? []).filter((admission) => (admission.fence.kind === "ordinal" && admission.fence.ordinal < checkpoint.fence.ordinal));
|
|
314
|
+
for (const receipt of exactLaunchInputReceipts("consumed", checkpoint.fence, admissions))
|
|
315
|
+
options.onTellReceipt?.(receipt);
|
|
316
|
+
options.onTellReceipt?.({ stage: "consumed", correlation: { kind: "fence", fence: checkpoint.fence } });
|
|
303
317
|
// Defer end so concurrent enqueue after this result can register openSteerCount.
|
|
304
318
|
queueMicrotask(() => maybeEndInputStream());
|
|
305
319
|
break;
|
|
@@ -179,7 +179,8 @@ export const claudeAgentSdkRegistration = {
|
|
|
179
179
|
return loader ? { ...terms, loader } : terms;
|
|
180
180
|
},
|
|
181
181
|
nativePolicyOwnership: () => null,
|
|
182
|
-
|
|
182
|
+
tellReceiptCapabilities: { seen: "exact", consumed: "fence" },
|
|
183
|
+
startLive: async ({ terms, signal, reviveTerms, onSyncCheckpoint, onSessionAdmission, onIngress, launchTellIds, observer }) => {
|
|
183
184
|
if (terms.provider !== CLAUDE_AGENT_SDK_PROVIDER) {
|
|
184
185
|
throw new Error(`startLive expected ${CLAUDE_AGENT_SDK_PROVIDER}`);
|
|
185
186
|
}
|
|
@@ -196,6 +197,15 @@ export const claudeAgentSdkRegistration = {
|
|
|
196
197
|
disallowedTools: claudeTerms.disallowedTools,
|
|
197
198
|
systemPrompt: claudeTerms.systemPrompt,
|
|
198
199
|
onSyncCheckpoint,
|
|
200
|
+
onTellReceipt: observer ? (receipt) => {
|
|
201
|
+
try {
|
|
202
|
+
observer.onTellReceipt(receipt);
|
|
203
|
+
}
|
|
204
|
+
catch { /* Receipt persistence cannot alter provider control. */ }
|
|
205
|
+
} : undefined,
|
|
206
|
+
launchTellIds,
|
|
207
|
+
onLaunchAdmission: observer ? (admission) => observer.onLaunchAdmission(admission) : undefined,
|
|
208
|
+
listLaunchAdmissions: observer ? () => observer.listLaunchAdmissions() : undefined,
|
|
199
209
|
onEvidence: claudeTerms.onEvidence,
|
|
200
210
|
onSessionAdmission,
|
|
201
211
|
onIngress,
|
|
@@ -64,10 +64,20 @@ export function createPushableUserMessageStream(onYield) {
|
|
|
64
64
|
pendingYieldAck.settleResolve();
|
|
65
65
|
pendingYieldAck = undefined;
|
|
66
66
|
};
|
|
67
|
-
const yieldQueued = (entry) => {
|
|
67
|
+
const yieldQueued = async (entry) => {
|
|
68
68
|
pullCount += 1;
|
|
69
69
|
pendingYieldAck = entry.ack;
|
|
70
|
-
|
|
70
|
+
try {
|
|
71
|
+
await onYield?.(entry.message);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
// A source-pull rejection means the SDK never received this item. Reject
|
|
75
|
+
// its post-yield acknowledgement before surfacing the provider failure.
|
|
76
|
+
entry.ack.settleReject(error);
|
|
77
|
+
if (pendingYieldAck === entry.ack)
|
|
78
|
+
pendingYieldAck = undefined;
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
71
81
|
return { value: entry.message, done: false };
|
|
72
82
|
};
|
|
73
83
|
return {
|
|
@@ -88,8 +98,12 @@ export function createPushableUserMessageStream(onYield) {
|
|
|
88
98
|
// Consumer is already inside next() (previous yield already acked at entry).
|
|
89
99
|
pullCount += 1;
|
|
90
100
|
pendingYieldAck = ack;
|
|
91
|
-
onYield?.(message)
|
|
92
|
-
|
|
101
|
+
void Promise.resolve(onYield?.(message)).then(() => waiter.resolve({ value: message, done: false }), (error) => {
|
|
102
|
+
ack.settleReject(error);
|
|
103
|
+
if (pendingYieldAck === ack)
|
|
104
|
+
pendingYieldAck = undefined;
|
|
105
|
+
waiter.reject(error);
|
|
106
|
+
});
|
|
93
107
|
return { waitSubmitted: () => ack.wait() };
|
|
94
108
|
}
|
|
95
109
|
queue.push({ message, ack });
|
|
@@ -128,7 +142,7 @@ export function createPushableUserMessageStream(onYield) {
|
|
|
128
142
|
// Per-yield submitted fence: next entry acks the previously yielded message.
|
|
129
143
|
acknowledgePreviousYield();
|
|
130
144
|
if (queue.length > 0) {
|
|
131
|
-
return
|
|
145
|
+
return yieldQueued(queue.shift());
|
|
132
146
|
}
|
|
133
147
|
if (failure !== undefined) {
|
|
134
148
|
return Promise.reject(failure);
|
|
@@ -5,7 +5,7 @@ import { createProviderFailure } from "../../harness/provider-adapter.js";
|
|
|
5
5
|
import { awaitProviderAbortSettlement, buildSubagentEnv, createAbortError, emitProviderEvidence, coerceString, } from "../../harness/runtime.js";
|
|
6
6
|
import { CODEX_APP_SERVER_PROVIDER } from "./identity.js";
|
|
7
7
|
import { CodexAppServerProcess } from "./session.js";
|
|
8
|
-
import { createCodexAppServerTurnMonitor, createCodexAppServerTurnState, recordTurnStartResponse, } from "./events.js";
|
|
8
|
+
import { createCodexAppServerTurnMonitor, createCodexAppServerTurnState, codexAppServerFailureMessage, codexAppServerTurnFailureCause, recordTurnStartResponse, } from "./events.js";
|
|
9
9
|
export { CODEX_APP_SERVER_PROVIDER } from "./identity.js";
|
|
10
10
|
/** Identity-only opacity boundary for codex-app-server turn/start effort. */
|
|
11
11
|
function toCodexAppServerEffort(effort) {
|
|
@@ -84,7 +84,7 @@ export async function startCodexAppServer(prompt, cwd, options = {}) {
|
|
|
84
84
|
appendDebugLog(`startCodexAppServer: executable=${executable} model=${options.model ?? "default"} mode=${resumeThreadId ? "resume" : "fresh"}`, { cwd, section: CODEX_APP_SERVER_PROVIDER });
|
|
85
85
|
const submitSteer = async (tell, turnId) => {
|
|
86
86
|
// turn/steer response {turnId} = enqueue acceptance (submitted), not consumption.
|
|
87
|
-
// clientUserMessageId = tell.id for later
|
|
87
|
+
// clientUserMessageId = tell.id for later exact seen proof via userMessage item clientId.
|
|
88
88
|
// tell.text is already core-rendered; send byte-for-byte — never parse or rewrite.
|
|
89
89
|
const ack = await server.request("turn/steer", {
|
|
90
90
|
threadId: activeThreadId,
|
|
@@ -111,6 +111,7 @@ export async function startCodexAppServer(prompt, cwd, options = {}) {
|
|
|
111
111
|
const startTellTurn = async (tell) => {
|
|
112
112
|
const started = await server.request("turn/start", {
|
|
113
113
|
threadId: activeThreadId,
|
|
114
|
+
clientUserMessageId: tell.id,
|
|
114
115
|
input: [{ type: "text", text: tell.text }],
|
|
115
116
|
...(options.model ? { model: options.model } : {}),
|
|
116
117
|
...(tell.effort ? { effort: toCodexAppServerEffort(tell.effort) } : {}),
|
|
@@ -249,7 +250,7 @@ export async function startCodexAppServer(prompt, cwd, options = {}) {
|
|
|
249
250
|
onEvidence: options.onEvidence,
|
|
250
251
|
onEvidenceDiagnostic: (text) => builder.emit({ type: "diagnostic", text }),
|
|
251
252
|
});
|
|
252
|
-
const { turnCompleted } = createCodexAppServerTurnMonitor({
|
|
253
|
+
const { turnCompleted, reconcileLaunchAdmissions } = createCodexAppServerTurnMonitor({
|
|
253
254
|
server,
|
|
254
255
|
builder,
|
|
255
256
|
state: turnState,
|
|
@@ -257,6 +258,8 @@ export async function startCodexAppServer(prompt, cwd, options = {}) {
|
|
|
257
258
|
onEvidence: options.onEvidence,
|
|
258
259
|
onSyncCheckpoint: options.onSyncCheckpoint,
|
|
259
260
|
onDeliveryOpportunity: options.onDeliveryOpportunity,
|
|
261
|
+
onTellReceipt: options.onTellReceipt,
|
|
262
|
+
listLaunchAdmissions: options.listLaunchAdmissions,
|
|
260
263
|
enqueue,
|
|
261
264
|
drainPendingTells: () => { void drainPendingTells(); },
|
|
262
265
|
rejectPendingTells,
|
|
@@ -346,6 +349,8 @@ export async function startCodexAppServer(prompt, cwd, options = {}) {
|
|
|
346
349
|
? (turnStart.turn ?? undefined)
|
|
347
350
|
: undefined;
|
|
348
351
|
const turnId = recordTurnStartResponse(turnState, turn?.id);
|
|
352
|
+
await options.onLaunchAdmission?.({ fence: { kind: "turn", turnId }, tellIds: options.launchTellIds ?? [] });
|
|
353
|
+
reconcileLaunchAdmissions();
|
|
349
354
|
if (options.onEvidence)
|
|
350
355
|
emitProviderEvidence(options.onEvidence, { type: "checkpoint", provider: CODEX_APP_SERVER_PROVIDER, fence: { kind: "turn", turnId }, source: "response" }, builderDiagnostic);
|
|
351
356
|
const completed = await turnCompleted;
|
|
@@ -354,9 +359,9 @@ export async function startCodexAppServer(prompt, cwd, options = {}) {
|
|
|
354
359
|
throw createAbortError(`${CODEX_APP_SERVER_PROVIDER} turn interrupted`);
|
|
355
360
|
}
|
|
356
361
|
if (completed.status === "failed") {
|
|
357
|
-
const
|
|
358
|
-
throw createProviderFailure(
|
|
359
|
-
...(
|
|
362
|
+
const publicCause = codexAppServerTurnFailureCause(completed.turn) ?? turnState.failureCause;
|
|
363
|
+
throw createProviderFailure(codexAppServerFailureMessage(publicCause), {
|
|
364
|
+
...(publicCause ? { publicCause } : {}),
|
|
360
365
|
});
|
|
361
366
|
}
|
|
362
367
|
if (completed.status !== "completed") {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { boundActivityExcerpt, diffstatFromUnifiedPatch } from "../../harness/activity-values.js";
|
|
2
2
|
import { AGENT_PLAN_ITEM_TEXT_MAX_CHARS, AGENT_PLAN_MAX_ITEMS, AGENT_THOUGHT_TEXT_MAX_CHARS, } from "../../harness/events.js";
|
|
3
|
+
import { exactLaunchInputReceipts } from "../../harness/control-types.js";
|
|
3
4
|
import { createProviderFailure } from "../../harness/provider-adapter.js";
|
|
4
5
|
import { createAbortError, emitProviderEvidence, coerceString } from "../../harness/runtime.js";
|
|
5
6
|
import { CODEX_APP_SERVER_PROVIDER } from "./identity.js";
|
|
@@ -174,7 +175,7 @@ export function createCodexAppServerTurnState() {
|
|
|
174
175
|
lastAgentMessage: "",
|
|
175
176
|
};
|
|
176
177
|
}
|
|
177
|
-
/** Admit
|
|
178
|
+
/** Admit positive native exhaustion evidence for the typed activity warning. */
|
|
178
179
|
export function codexAppServerRateLimitCause(params) {
|
|
179
180
|
const rateLimits = appServerRecord(appServerRecord(params)?.rateLimits);
|
|
180
181
|
if (!rateLimits)
|
|
@@ -182,11 +183,39 @@ export function codexAppServerRateLimitCause(params) {
|
|
|
182
183
|
for (const value of Object.values(rateLimits)) {
|
|
183
184
|
const window = appServerRecord(value);
|
|
184
185
|
if (typeof window?.usedPercent === "number" && window.usedPercent >= 100) {
|
|
185
|
-
return {
|
|
186
|
+
return { code: "rate_limit_exhausted", message: "rate limit exhausted" };
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
return undefined;
|
|
189
190
|
}
|
|
191
|
+
export function codexAppServerTurnFailureCause(turn) {
|
|
192
|
+
const error = appServerRecord(turn.error);
|
|
193
|
+
const httpStatus = [error?.httpStatus, error?.status, error?.statusCode, turn.httpStatus, turn.statusCode]
|
|
194
|
+
.find((value) => typeof value === "number" && Number.isSafeInteger(value));
|
|
195
|
+
const code = coerceString(error?.code) ?? coerceString(turn.errorCode);
|
|
196
|
+
const message = coerceString(error?.message) ?? coerceString(turn.error) ?? coerceString(turn.message);
|
|
197
|
+
return httpStatus === undefined && code === undefined && message === undefined
|
|
198
|
+
? undefined
|
|
199
|
+
: {
|
|
200
|
+
...(httpStatus === undefined ? {} : { httpStatus }),
|
|
201
|
+
...(code === undefined ? {} : { code }),
|
|
202
|
+
...(message === undefined ? {} : { message }),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export function codexAppServerFailureMessage(cause) {
|
|
206
|
+
if (!cause)
|
|
207
|
+
return `${CODEX_APP_SERVER_PROVIDER} turn failed`;
|
|
208
|
+
const httpStatus = typeof cause.httpStatus === "number" ? cause.httpStatus : undefined;
|
|
209
|
+
const code = typeof cause.code === "string" ? cause.code : undefined;
|
|
210
|
+
const message = typeof cause.message === "string" ? cause.message : undefined;
|
|
211
|
+
const label = [
|
|
212
|
+
httpStatus === undefined ? undefined : `HTTP ${httpStatus}`,
|
|
213
|
+
code,
|
|
214
|
+
].filter((part) => part !== undefined).join(" ");
|
|
215
|
+
if (label && message)
|
|
216
|
+
return `${label}: ${message}`;
|
|
217
|
+
return label || message || `${CODEX_APP_SERVER_PROVIDER} turn failed`;
|
|
218
|
+
}
|
|
190
219
|
export function recordActiveTurnId(state, candidate, source) {
|
|
191
220
|
const turnId = coerceString(candidate);
|
|
192
221
|
if (!turnId)
|
|
@@ -211,7 +240,24 @@ export function recordTurnStartResponse(state, candidate) {
|
|
|
211
240
|
return recordActiveTurnId(state, turnId, "response");
|
|
212
241
|
}
|
|
213
242
|
export function createCodexAppServerTurnMonitor(options) {
|
|
214
|
-
const { server, builder, state, builderDiagnostic, onEvidence, onSyncCheckpoint, onDeliveryOpportunity, enqueue, drainPendingTells, rejectPendingTells, setFailActiveTurn, pendingTellCount, isDrainingTells, setFinishAfterTellDrain, } = options;
|
|
243
|
+
const { server, builder, state, builderDiagnostic, onEvidence, onSyncCheckpoint, onDeliveryOpportunity, onTellReceipt, listLaunchAdmissions, enqueue, drainPendingTells, rejectPendingTells, setFailActiveTurn, pendingTellCount, isDrainingTells, setFinishAfterTellDrain, } = options;
|
|
244
|
+
const startedLaunchFences = new Map();
|
|
245
|
+
const completedLaunchFences = new Map();
|
|
246
|
+
const emitLaunchReceipts = (stage, fence) => {
|
|
247
|
+
const admissions = (listLaunchAdmissions?.() ?? []).filter((admission) => (admission.fence.kind === "turn" && fence.kind === "turn"
|
|
248
|
+
&& admission.fence.turnId === fence.turnId));
|
|
249
|
+
for (const receipt of exactLaunchInputReceipts(stage, fence, admissions))
|
|
250
|
+
onTellReceipt?.(receipt);
|
|
251
|
+
};
|
|
252
|
+
// A notification may arrive between the native response and durable launch
|
|
253
|
+
// admission. Retain only explicit native turn fences and replay them after
|
|
254
|
+
// the adapter has recorded the coordinate; no timing inference is involved.
|
|
255
|
+
const reconcileLaunchAdmissions = () => {
|
|
256
|
+
for (const fence of startedLaunchFences.values())
|
|
257
|
+
emitLaunchReceipts("seen", fence);
|
|
258
|
+
for (const fence of completedLaunchFences.values())
|
|
259
|
+
emitLaunchReceipts("consumed", fence);
|
|
260
|
+
};
|
|
215
261
|
const turnCompleted = new Promise((resolve, reject) => {
|
|
216
262
|
let unsubNotifications = () => { };
|
|
217
263
|
let unsubServerRequest = () => { };
|
|
@@ -277,6 +323,11 @@ export function createCodexAppServerTurnMonitor(options) {
|
|
|
277
323
|
builder.emit({ type: "message", id, text });
|
|
278
324
|
}
|
|
279
325
|
}
|
|
326
|
+
else if (itemType === "userMessage") {
|
|
327
|
+
const tellId = coerceString(item.clientId);
|
|
328
|
+
if (tellId)
|
|
329
|
+
onTellReceipt?.({ stage: "seen", correlation: { kind: "exact", tellId } });
|
|
330
|
+
}
|
|
280
331
|
else if (itemType === "reasoning") {
|
|
281
332
|
const text = extractReasoningSummary(item);
|
|
282
333
|
if (text) {
|
|
@@ -316,6 +367,11 @@ export function createCodexAppServerTurnMonitor(options) {
|
|
|
316
367
|
const itemType = coerceString(item.type);
|
|
317
368
|
if (!id || !itemType)
|
|
318
369
|
throw new Error(`${CODEX_APP_SERVER_PROVIDER} item/started lacked required item id or type`);
|
|
370
|
+
if (itemType === "userMessage") {
|
|
371
|
+
const tellId = coerceString(item.clientId);
|
|
372
|
+
if (tellId)
|
|
373
|
+
onTellReceipt?.({ stage: "seen", correlation: { kind: "exact", tellId } });
|
|
374
|
+
}
|
|
319
375
|
if (isCodexAppServerActivityItemType(itemType)) {
|
|
320
376
|
builder.emit({
|
|
321
377
|
type: "activity",
|
|
@@ -331,7 +387,7 @@ export function createCodexAppServerTurnMonitor(options) {
|
|
|
331
387
|
const turnId = recordActiveTurnId(state, turn?.id, "notification");
|
|
332
388
|
builder.emit({ type: "turn", phase: "started" });
|
|
333
389
|
state.activeTurnAcceptingSteer = true;
|
|
334
|
-
const checkpoint = { fence: { kind: "turn", turnId }
|
|
390
|
+
const checkpoint = { fence: { kind: "turn", turnId } };
|
|
335
391
|
if (onEvidence) {
|
|
336
392
|
emitProviderEvidence(onEvidence, {
|
|
337
393
|
type: "checkpoint",
|
|
@@ -343,6 +399,8 @@ export function createCodexAppServerTurnMonitor(options) {
|
|
|
343
399
|
// turn/start may respond before the server has installed its active
|
|
344
400
|
// turn. The native notification is the first steer-safe boundary.
|
|
345
401
|
onSyncCheckpoint?.({ enqueue }, checkpoint);
|
|
402
|
+
startedLaunchFences.set(turnId, checkpoint.fence);
|
|
403
|
+
emitLaunchReceipts("seen", checkpoint.fence);
|
|
346
404
|
void drainPendingTells();
|
|
347
405
|
}
|
|
348
406
|
if (notification.method === "account/rateLimits/updated") {
|
|
@@ -370,7 +428,7 @@ export function createCodexAppServerTurnMonitor(options) {
|
|
|
370
428
|
state.activeTurnAcceptingSteer = false;
|
|
371
429
|
state.activeTurnId = undefined;
|
|
372
430
|
state.activeTurnSource = undefined;
|
|
373
|
-
const checkpoint = { fence: { kind: "turn", turnId }
|
|
431
|
+
const checkpoint = { fence: { kind: "turn", turnId } };
|
|
374
432
|
if (onEvidence) {
|
|
375
433
|
emitProviderEvidence(onEvidence, {
|
|
376
434
|
type: "checkpoint",
|
|
@@ -380,6 +438,9 @@ export function createCodexAppServerTurnMonitor(options) {
|
|
|
380
438
|
}, builderDiagnostic);
|
|
381
439
|
}
|
|
382
440
|
onSyncCheckpoint?.({ enqueue }, checkpoint);
|
|
441
|
+
completedLaunchFences.set(turnId, checkpoint.fence);
|
|
442
|
+
emitLaunchReceipts("consumed", checkpoint.fence);
|
|
443
|
+
onTellReceipt?.({ stage: "consumed", correlation: { kind: "fence", fence: checkpoint.fence } });
|
|
383
444
|
void drainPendingTells();
|
|
384
445
|
if (pendingTellCount() > 0 || isDrainingTells()) {
|
|
385
446
|
setFinishAfterTellDrain(() => {
|
|
@@ -427,5 +488,5 @@ export function createCodexAppServerTurnMonitor(options) {
|
|
|
427
488
|
// Abort can settle this before initialization reaches the await below.
|
|
428
489
|
// Attach a handler immediately while preserving the original awaited error.
|
|
429
490
|
turnCompleted.catch(() => undefined);
|
|
430
|
-
return { turnCompleted };
|
|
491
|
+
return { turnCompleted, reconcileLaunchAdmissions };
|
|
431
492
|
}
|
|
@@ -212,7 +212,8 @@ export const codexAppServerRegistration = {
|
|
|
212
212
|
webSearch: Object.freeze(webSearch),
|
|
213
213
|
});
|
|
214
214
|
},
|
|
215
|
-
|
|
215
|
+
tellReceiptCapabilities: { seen: "exact", consumed: "fence" },
|
|
216
|
+
startLive: async ({ terms, signal, reviveTerms, onSyncCheckpoint, onDeliveryOpportunity, onSessionAdmission, onIngress, launchTellIds, observer, }) => {
|
|
216
217
|
if (terms.provider !== CODEX_APP_SERVER_PROVIDER) {
|
|
217
218
|
throw new Error(`startLive expected ${CODEX_APP_SERVER_PROVIDER}`);
|
|
218
219
|
}
|
|
@@ -230,6 +231,15 @@ export const codexAppServerRegistration = {
|
|
|
230
231
|
developerInstructions: codexTerms.developerInstructions,
|
|
231
232
|
onSyncCheckpoint,
|
|
232
233
|
onDeliveryOpportunity,
|
|
234
|
+
onTellReceipt: observer ? (receipt) => {
|
|
235
|
+
try {
|
|
236
|
+
observer.onTellReceipt(receipt);
|
|
237
|
+
}
|
|
238
|
+
catch { /* Receipt persistence cannot alter provider control. */ }
|
|
239
|
+
} : undefined,
|
|
240
|
+
launchTellIds,
|
|
241
|
+
onLaunchAdmission: observer ? (admission) => observer.onLaunchAdmission(admission) : undefined,
|
|
242
|
+
listLaunchAdmissions: observer ? () => observer.listLaunchAdmissions() : undefined,
|
|
233
243
|
onEvidence: codexTerms.onEvidence,
|
|
234
244
|
onSessionAdmission,
|
|
235
245
|
onIngress,
|
|
@@ -280,6 +280,8 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
280
280
|
onEvidence: options.onEvidence,
|
|
281
281
|
onSyncCheckpoint: options.onSyncCheckpoint,
|
|
282
282
|
onDeliveryOpportunity: options.onDeliveryOpportunity,
|
|
283
|
+
onTellReceipt: options.onTellReceipt,
|
|
284
|
+
listLaunchAdmissions: options.listLaunchAdmissions,
|
|
283
285
|
executionSignal: combined.signal,
|
|
284
286
|
getSessionId: () => activeSessionId,
|
|
285
287
|
client: sdk.client,
|
|
@@ -313,7 +315,12 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
313
315
|
// The acquisition itself lives inside streamTask, so local shutdown can
|
|
314
316
|
// always settle it even when the SDK never resolves events().
|
|
315
317
|
await eventsResultTask;
|
|
316
|
-
await promptSteer(sdk.client, prompt);
|
|
318
|
+
const launchAcknowledgement = await promptSteer(sdk.client, prompt);
|
|
319
|
+
await options.onLaunchAdmission?.({
|
|
320
|
+
fence: launchAcknowledgement.fence,
|
|
321
|
+
tellIds: options.launchTellIds ?? [],
|
|
322
|
+
});
|
|
323
|
+
eventController?.reconcileLaunchAdmissions();
|
|
317
324
|
await waitIdle();
|
|
318
325
|
if (gracefulInterruptRequested) {
|
|
319
326
|
await gracefulProviderSettlement;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { exactLaunchInputReceipts } from "../../harness/control-types.js";
|
|
1
2
|
import { activityCallFromNamedTool } from "../../harness/activity-values.js";
|
|
2
3
|
import { AGENT_THOUGHT_TEXT_MAX_CHARS, } from "../../harness/events.js";
|
|
3
4
|
import { createProviderFailure } from "../../harness/provider-adapter.js";
|
|
@@ -117,7 +118,7 @@ function extractFinalAssistantMessage(messages) {
|
|
|
117
118
|
return messages.find((message) => message.type === "assistant");
|
|
118
119
|
}
|
|
119
120
|
export function createOpencodeEventController(options) {
|
|
120
|
-
const { builder, control, idleMonitorIngress, idleMonitorTerminal, onEvidence, onSyncCheckpoint, onDeliveryOpportunity, getSessionId, client, } = options;
|
|
121
|
+
const { builder, control, idleMonitorIngress, idleMonitorTerminal, onEvidence, onSyncCheckpoint, onDeliveryOpportunity, onTellReceipt, listLaunchAdmissions, getSessionId, client, } = options;
|
|
121
122
|
const recordedEventTypes = [];
|
|
122
123
|
let totalEventCount = 0;
|
|
123
124
|
let currentTurnAdmitted = false;
|
|
@@ -125,6 +126,8 @@ export function createOpencodeEventController(options) {
|
|
|
125
126
|
let lastTurnFailure;
|
|
126
127
|
let lastSeenSeq = 0;
|
|
127
128
|
let turnOpen = false;
|
|
129
|
+
const promptedFences = [];
|
|
130
|
+
const emittedLaunchReceiptKeys = new Set();
|
|
128
131
|
const toolCalls = new Map();
|
|
129
132
|
const requestDelivery = (steerControl) => {
|
|
130
133
|
try {
|
|
@@ -134,6 +137,21 @@ export function createOpencodeEventController(options) {
|
|
|
134
137
|
// Delivery observation cannot alter provider control flow.
|
|
135
138
|
}
|
|
136
139
|
};
|
|
140
|
+
const reconcileLaunchAdmissions = () => {
|
|
141
|
+
for (const fence of promptedFences) {
|
|
142
|
+
const admissions = (listLaunchAdmissions?.() ?? []).filter((admission) => (admission.fence.kind === "ordinal" && fence.kind === "ordinal"
|
|
143
|
+
&& admission.fence.ordinal <= fence.ordinal));
|
|
144
|
+
for (const receipt of exactLaunchInputReceipts("seen", fence, admissions)) {
|
|
145
|
+
if (fence.kind !== "ordinal" || receipt.correlation.kind !== "exact")
|
|
146
|
+
continue;
|
|
147
|
+
const key = `${receipt.stage}:${fence.ordinal}:${receipt.correlation.tellId}`;
|
|
148
|
+
if (emittedLaunchReceiptKeys.has(key))
|
|
149
|
+
continue;
|
|
150
|
+
emittedLaunchReceiptKeys.add(key);
|
|
151
|
+
onTellReceipt?.(receipt);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
137
155
|
const openTurn = () => {
|
|
138
156
|
if (turnOpen)
|
|
139
157
|
builder.emit({ type: "turn", phase: "completed" });
|
|
@@ -169,11 +187,14 @@ export function createOpencodeEventController(options) {
|
|
|
169
187
|
currentTurnFailure = undefined;
|
|
170
188
|
openTurn();
|
|
171
189
|
if (seq !== undefined) {
|
|
172
|
-
const checkpoint = { fence: { kind: "ordinal", ordinal: seq }
|
|
190
|
+
const checkpoint = { fence: { kind: "ordinal", ordinal: seq } };
|
|
173
191
|
if (onEvidence) {
|
|
174
192
|
emitProviderEvidence(onEvidence, { type: "checkpoint", provider: OPENCODE_SDK_PROVIDER, fence: checkpoint.fence, source: "notification" }, (text) => builder.emit({ type: "diagnostic", text }));
|
|
175
193
|
}
|
|
176
194
|
onSyncCheckpoint?.(control, checkpoint);
|
|
195
|
+
promptedFences.push(checkpoint.fence);
|
|
196
|
+
reconcileLaunchAdmissions();
|
|
197
|
+
onTellReceipt?.({ stage: "seen", correlation: { kind: "fence", fence: checkpoint.fence } });
|
|
177
198
|
}
|
|
178
199
|
break;
|
|
179
200
|
}
|
|
@@ -346,6 +367,7 @@ export function createOpencodeEventController(options) {
|
|
|
346
367
|
drainHistory,
|
|
347
368
|
closeTurn,
|
|
348
369
|
lastSeenSeq: () => lastSeenSeq,
|
|
370
|
+
reconcileLaunchAdmissions,
|
|
349
371
|
lastTurnFailure: () => lastTurnFailure,
|
|
350
372
|
recordedEventTypes: () => recordedEventTypes,
|
|
351
373
|
totalEventCount: () => totalEventCount,
|