ai-foreman 1.7.29 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/adapters/claude.d.ts +23 -12
- package/dist/adapters/claude.js +231 -26
- package/dist/adapters/codex.d.ts +19 -7
- package/dist/adapters/codex.js +202 -19
- package/dist/adapters/recovering.d.ts +2 -0
- package/dist/adapters/recovering.js +5 -1
- package/dist/adapters/structured.d.ts +71 -0
- package/dist/adapters/structured.js +213 -0
- package/dist/adapters/types.d.ts +85 -9
- package/dist/agentRun.d.ts +2 -0
- package/dist/agentRun.js +2 -1
- package/dist/boundedTurnEvidence.d.ts +18 -0
- package/dist/boundedTurnEvidence.js +76 -0
- package/dist/branch/currentGuard.d.ts +2 -0
- package/dist/branch/currentGuard.js +2 -0
- package/dist/branch/git.js +3 -8
- package/dist/branch/planner.js +1 -4
- package/dist/branch/runner.d.ts +2 -1
- package/dist/branch/runner.js +10 -7
- package/dist/buildAdmission.js +4 -0
- package/dist/buildApproval.d.ts +15 -0
- package/dist/buildApproval.js +28 -9
- package/dist/buildAssignment.d.ts +1 -0
- package/dist/buildAssignment.js +82 -23
- package/dist/buildInterventions.d.ts +2 -2
- package/dist/buildInterventions.js +11 -4
- package/dist/buildLaunchGate.js +3 -2
- package/dist/buildRuns.d.ts +1 -0
- package/dist/buildRuns.js +6 -1
- package/dist/builderGuidanceFollowup.js +17 -6
- package/dist/cli/doctor.js +3 -1
- package/dist/cli/manager.js +22 -20
- package/dist/cli/start.js +85 -39
- package/dist/cli/tickets.d.ts +0 -8
- package/dist/cli/tickets.js +50 -60
- package/dist/continuity.d.ts +10 -16
- package/dist/continuity.js +223 -208
- package/dist/continuityOrigin.d.ts +13 -0
- package/dist/continuityOrigin.js +91 -0
- package/dist/dispatchNotSent.d.ts +4 -0
- package/dist/dispatchNotSent.js +10 -0
- package/dist/foreman.d.ts +15 -16
- package/dist/foreman.js +70 -278
- package/dist/gitExclude.d.ts +16 -0
- package/dist/gitExclude.js +46 -0
- package/dist/graph/boundary.d.ts +1 -1
- package/dist/graph/boundary.js +1 -1
- package/dist/graph/host.js +8 -19
- package/dist/graph/manager.d.ts +1 -1
- package/dist/graph/manager.js +1 -1
- package/dist/graph/preparation.d.ts +38 -0
- package/dist/graph/preparation.js +7 -6
- package/dist/graph/semantic.d.ts +1 -0
- package/dist/graph/semantic.js +4 -1
- package/dist/graph/session.js +7 -2
- package/dist/graph/turn.d.ts +3 -4
- package/dist/graph/turn.js +29 -41
- package/dist/handoffProtocol.js +2 -2
- package/dist/handoffs.d.ts +5 -3
- package/dist/handoffs.js +92 -88
- package/dist/hostGraphApplicability.d.ts +2 -1
- package/dist/hostGraphApplicability.js +17 -19
- package/dist/hostQaAugmentation.js +21 -4
- package/dist/hostQaComparison.d.ts +21 -2
- package/dist/hostQaComparison.js +43 -29
- package/dist/hostSourcePreservation.d.ts +38 -0
- package/dist/hostSourcePreservation.js +72 -0
- package/dist/humanDecision.d.ts +7 -0
- package/dist/humanDecision.js +11 -0
- package/dist/interviews.d.ts +7 -0
- package/dist/interviews.js +18 -1
- package/dist/legacyCommunicationDisplay.d.ts +2 -0
- package/dist/legacyCommunicationDisplay.js +2 -0
- package/dist/managerPacket.js +3 -3
- package/dist/markers.d.ts +4 -3
- package/dist/markers.js +4 -20
- package/dist/observability.js +3 -8
- package/dist/probeIpc.d.ts +14 -0
- package/dist/probeIpc.js +29 -0
- package/dist/qaBuildGate.d.ts +21 -0
- package/dist/qaBuildGate.js +60 -13
- package/dist/qaBuilderCoverage.js +22 -13
- package/dist/qaBuilderEvidence.d.ts +16 -1
- package/dist/qaBuilderEvidence.js +48 -8
- package/dist/qaContractCoverage.d.ts +2 -2
- package/dist/qaContractCoverage.js +4 -9
- package/dist/qaContractDelivery.js +34 -20
- package/dist/qaCoverageAssembly.d.ts +22 -0
- package/dist/qaCoverageAssembly.js +16 -0
- package/dist/qaDeliveryJournal.d.ts +2 -0
- package/dist/qaDeliveryJournal.js +19 -0
- package/dist/qaFailureDelivery.js +97 -85
- package/dist/qaHostTransformation.d.ts +26 -0
- package/dist/qaHostTransformation.js +100 -0
- package/dist/qaPreparation.d.ts +8 -20
- package/dist/qaPreparation.js +153 -254
- package/dist/qaPreparationInputs.js +19 -0
- package/dist/qaPreparationStore.d.ts +11 -0
- package/dist/qaPreparationStore.js +44 -4
- package/dist/qaPrerequisites.d.ts +2 -1
- package/dist/qaPrerequisites.js +21 -1
- package/dist/qaProtocolV2.d.ts +15 -0
- package/dist/qaRecovery.d.ts +0 -3
- package/dist/qaRecovery.js +21 -27
- package/dist/qaReview.js +241 -406
- package/dist/qaReviewEvidence.d.ts +7 -3
- package/dist/qaReviewEvidence.js +15 -4
- package/dist/qaRuntime.js +1 -1
- package/dist/qaSnapshot.js +76 -21
- package/dist/qaStructuredResponse.d.ts +18 -0
- package/dist/qaStructuredResponse.js +81 -0
- package/dist/qaTimeline.js +2 -2
- package/dist/qaVerificationContract.d.ts +1 -2
- package/dist/qaVerificationContract.js +24 -35
- package/dist/readinessGate.js +5 -4
- package/dist/recoveryOwners.d.ts +32 -0
- package/dist/recoveryOwners.js +87 -0
- package/dist/runtimePreflight.d.ts +11 -0
- package/dist/runtimePreflight.js +77 -0
- package/dist/runtimeReadiness.js +23 -15
- package/dist/sources/sourceRegistry.d.ts +0 -3
- package/dist/sources/sourceRegistry.js +0 -42
- package/dist/statusReporter.d.ts +2 -0
- package/dist/statusReporter.js +2 -0
- package/dist/structuredCutover.d.ts +31 -0
- package/dist/structuredCutover.js +510 -0
- package/dist/structuredCutoverFiles.d.ts +10 -0
- package/dist/structuredCutoverFiles.js +181 -0
- package/dist/structuredDispatchAuthority.d.ts +22 -0
- package/dist/structuredDispatchAuthority.js +110 -0
- package/dist/structuredDispatchRecord.d.ts +29 -0
- package/dist/structuredDispatchRecord.js +36 -0
- package/dist/structuredLiveBudget.d.ts +2 -0
- package/dist/structuredLiveBudget.js +23 -0
- package/dist/structuredOperation.d.ts +18 -0
- package/dist/structuredOperation.js +279 -0
- package/dist/supervisedStart.js +35 -21
- package/dist/ticketPlanning.d.ts +1 -376
- package/dist/ticketPlanning.js +2 -49
- package/dist/ticketPopulation.d.ts +0 -3
- package/dist/ticketPopulation.js +4 -21
- package/dist/ticketPopulationPageAuthority.d.ts +13 -0
- package/dist/ticketPopulationPageAuthority.js +30 -0
- package/dist/ticketPopulationPaging.d.ts +27 -0
- package/dist/ticketPopulationPaging.js +127 -0
- package/dist/tickets/renderMarkdown.d.ts +14 -0
- package/dist/tickets/renderMarkdown.js +39 -5
- package/dist/tickets/ticketLoader.js +10 -0
- package/dist/tickets/ticketSchema.d.ts +107 -0
- package/dist/tickets/ticketSchema.js +4 -1
- package/dist/workflowDb.d.ts +116 -3
- package/dist/workflowDb.js +852 -61
- package/dist/workflowReader.d.ts +1 -0
- package/dist/workflowReader.js +9 -0
- package/package.json +23 -7
package/dist/adapters/codex.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ProviderTurnPurpose } from "../providerPhase.js";
|
|
2
1
|
import type { BuilderAdapter, BuilderAdapterOptions, BuilderEvent, CompactResult, ContextUsage, NativeAutoCompactionPolicy, NativeCompaction, ProviderSessionUsage, ProviderSettingSwitch, TurnResult } from "./types.js";
|
|
3
2
|
import type { ProviderSessionRefV1, SessionAvailabilityV1 } from "rafi-spec";
|
|
3
|
+
import type { ProviderTurnPolicy } from "./types.js";
|
|
4
4
|
export interface CodexLineResult {
|
|
5
5
|
events: BuilderEvent[];
|
|
6
6
|
sessionId?: string;
|
|
@@ -30,6 +30,12 @@ export declare class CodexAdapter implements BuilderAdapter {
|
|
|
30
30
|
private closed;
|
|
31
31
|
private closePromise?;
|
|
32
32
|
private activeText;
|
|
33
|
+
private structuredCollector?;
|
|
34
|
+
private turnStreamedBytes;
|
|
35
|
+
private streamStartedAt;
|
|
36
|
+
private streamActivityAt;
|
|
37
|
+
private streamDiagnosticAt;
|
|
38
|
+
private turnInFlight;
|
|
33
39
|
private usage?;
|
|
34
40
|
private usageRevision;
|
|
35
41
|
private providerSessionUsage?;
|
|
@@ -82,12 +88,7 @@ export declare class CodexAdapter implements BuilderAdapter {
|
|
|
82
88
|
buildAppServerArgs(): string[];
|
|
83
89
|
/** Deprecated fixture helper. Runtime execution uses only app-server. */
|
|
84
90
|
buildArgs(instruction: string): string[];
|
|
85
|
-
sendTurn(instruction: string, policy?:
|
|
86
|
-
handback?: boolean;
|
|
87
|
-
responseOnly?: boolean;
|
|
88
|
-
logicalActionId?: string;
|
|
89
|
-
purpose?: ProviderTurnPurpose;
|
|
90
|
-
}): Promise<TurnResult>;
|
|
91
|
+
sendTurn(instruction: string, policy?: ProviderTurnPolicy): Promise<TurnResult>;
|
|
91
92
|
private sendTurnInternal;
|
|
92
93
|
compact(): Promise<CompactResult>;
|
|
93
94
|
private compactInternal;
|
|
@@ -119,9 +120,20 @@ export declare class CodexAdapter implements BuilderAdapter {
|
|
|
119
120
|
private handle;
|
|
120
121
|
private observationContext;
|
|
121
122
|
private waitFor;
|
|
123
|
+
/** Ask the app-server to stop a native turn; true only once its terminal is observed. */
|
|
124
|
+
private interruptNativeTurn;
|
|
125
|
+
/** Streamed output is visible progress; it is never treated as a result. */
|
|
126
|
+
private observeAgentMessageDelta;
|
|
127
|
+
/**
|
|
128
|
+
* Bounded interruption evidence for structured turns under .foreman (Rafi's
|
|
129
|
+
* git-excluded log directory). Diagnostic only: never read back as output.
|
|
130
|
+
*/
|
|
131
|
+
private writeStreamDiagnostic;
|
|
122
132
|
private traceLifecycle;
|
|
123
133
|
private touchWaiters;
|
|
124
134
|
private providerIdleTimeoutMs;
|
|
135
|
+
/** Read-only QA may reach the network only with the operator's --qa-network grant. */
|
|
136
|
+
private qaNetworkAccess;
|
|
125
137
|
private effectiveNetworkAccess;
|
|
126
138
|
private effectiveApprovalPolicy;
|
|
127
139
|
/** Fail closed; approval is for this operation only, never a session cache or policy amendment. */
|
package/dist/adapters/codex.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { inheritedStructuredLiveBudget } from "../structuredLiveBudget.js";
|
|
1
2
|
import { OperationDeadline } from "../util/deadline.js";
|
|
2
3
|
import { spawn } from "node:child_process";
|
|
3
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
5
6
|
import { join } from "node:path";
|
|
6
7
|
import { createInterface } from "node:readline";
|
|
7
8
|
import { loadSkill } from "special-agents";
|
|
@@ -11,6 +12,9 @@ import { canonicalSessionPath, createProviderSessionRef, validateProviderSession
|
|
|
11
12
|
import { SessionUnavailableError, sessionUnavailableResult } from "./sessionFailure.js";
|
|
12
13
|
import { isFastModeUnavailable, reportFastModeUnavailable } from "./fastMode.js";
|
|
13
14
|
import { handoffSystemInstructions } from "../handoffProtocol.js";
|
|
15
|
+
import { providerControlOperationV2, providerWireContract, renderProviderWireInstruction, initializationOutput, validateStructuredCandidate, selectedDispatchTupleV2, structuredDigest } from "rafi-spec";
|
|
16
|
+
import { assertActiveProviderDispatch, withEphemeralInitializationDispatch } from "../structuredDispatchAuthority.js";
|
|
17
|
+
import { CodexRunawayOutputError, CodexTurnCollector, structuredReceipt } from "./structured.js";
|
|
14
18
|
/** Compatibility parser for recorded pre-app-server JSONL fixtures. */
|
|
15
19
|
export function parseCodexLine(raw) {
|
|
16
20
|
const type = raw.type;
|
|
@@ -46,6 +50,12 @@ export class CodexAdapter {
|
|
|
46
50
|
closed = false;
|
|
47
51
|
closePromise;
|
|
48
52
|
activeText = [];
|
|
53
|
+
structuredCollector;
|
|
54
|
+
turnStreamedBytes = 0;
|
|
55
|
+
streamStartedAt = 0;
|
|
56
|
+
streamActivityAt = 0;
|
|
57
|
+
streamDiagnosticAt = 0;
|
|
58
|
+
turnInFlight = false;
|
|
49
59
|
usage;
|
|
50
60
|
usageRevision = 0;
|
|
51
61
|
providerSessionUsage;
|
|
@@ -117,6 +127,7 @@ export class CodexAdapter {
|
|
|
117
127
|
}
|
|
118
128
|
constructor(opts) {
|
|
119
129
|
this.opts = opts;
|
|
130
|
+
inheritedStructuredLiveBudget("codex");
|
|
120
131
|
this._sessionId = opts.resumeSessionRef?.sessionId ?? opts.resumeSessionId;
|
|
121
132
|
this._sessionRef = opts.resumeSessionRef;
|
|
122
133
|
}
|
|
@@ -140,6 +151,7 @@ export class CodexAdapter {
|
|
|
140
151
|
buildInstruction(instruction) {
|
|
141
152
|
return [this.opts.systemPromptAppend,
|
|
142
153
|
this.opts.sessionRole === "builder" ? `Builder runtime contract: shell network ${this.effectiveNetworkAccess() ? "explicitly approved for this build" : "disabled"}; sandbox escalation ${this.effectiveApprovalPolicy() === "on-request" ? "requires separate approval for each exact operation" : "disabled"}. Web tools do not establish shell network access. If acquisition needs unavailable capabilities, request input before running acquisition, or use a local source/dependency bundle with verified provenance. A ticket answer does not change runtime permissions.` : undefined,
|
|
154
|
+
this.qaNetworkAccess() ? "QA runtime contract: shell network explicitly approved for this review (--qa-network); you may run live checks against operator-provided local or remote services. Workspace stays read-only." : undefined,
|
|
143
155
|
this.buildSkillsAppendix(), instruction]
|
|
144
156
|
.filter((part) => Boolean(part)).join("\n\n");
|
|
145
157
|
}
|
|
@@ -163,9 +175,23 @@ export class CodexAdapter {
|
|
|
163
175
|
return args;
|
|
164
176
|
}
|
|
165
177
|
async sendTurn(instruction, policy) {
|
|
178
|
+
if (this.turnInFlight)
|
|
179
|
+
throw new Error("a turn is already in progress");
|
|
180
|
+
if (policy?.output)
|
|
181
|
+
assertActiveProviderDispatch(policy, instruction, "codex", this.sessionRef());
|
|
182
|
+
if (policy?.output) {
|
|
183
|
+
const selected = policy.wireContract ?? providerWireContract(policy.output, "codex", "default", { requireRegistered: true });
|
|
184
|
+
if (selected.provider !== "codex" || selected.canonical.schemaDigest !== policy.output.schemaDigest)
|
|
185
|
+
throw new Error("Codex received a mismatched provider wire contract");
|
|
186
|
+
if (policy.renderedWireContractDigest && policy.renderedWireContractDigest !== selected.descriptorDigest)
|
|
187
|
+
throw new Error("Codex received mismatched rendered wire guidance");
|
|
188
|
+
instruction = renderProviderWireInstruction(instruction, selected);
|
|
189
|
+
policy = { ...policy, wireContract: selected, renderedWireContractDigest: selected.descriptorDigest };
|
|
190
|
+
}
|
|
191
|
+
this.turnInFlight = true;
|
|
166
192
|
const prepared = this.instructionWithPendingQaReminder(instruction);
|
|
167
193
|
instruction = prepared.instruction;
|
|
168
|
-
const turnId = randomUUID();
|
|
194
|
+
const turnId = policy?.dispatchId ?? randomUUID();
|
|
169
195
|
this.activeProviderTurnId = turnId;
|
|
170
196
|
const observer = this.opts.observer;
|
|
171
197
|
if (!observer) {
|
|
@@ -187,6 +213,8 @@ export class CodexAdapter {
|
|
|
187
213
|
finally {
|
|
188
214
|
this.activeProviderTurnId = undefined;
|
|
189
215
|
this.activeNativeTurnId = undefined;
|
|
216
|
+
this.structuredCollector = undefined;
|
|
217
|
+
this.turnInFlight = false;
|
|
190
218
|
}
|
|
191
219
|
}
|
|
192
220
|
const context = this.observationContext();
|
|
@@ -218,9 +246,11 @@ export class CodexAdapter {
|
|
|
218
246
|
this.activeProviderTurnId = undefined;
|
|
219
247
|
this.activeNativeTurnId = undefined;
|
|
220
248
|
this.activeProviderTurnSpanId = undefined;
|
|
249
|
+
this.structuredCollector = undefined;
|
|
250
|
+
this.turnInFlight = false;
|
|
221
251
|
}
|
|
222
252
|
}
|
|
223
|
-
async sendTurnInternal(instruction, policy) {
|
|
253
|
+
async sendTurnInternal(instruction, policy, runawayRetries = 0) {
|
|
224
254
|
if (this.closed)
|
|
225
255
|
throw new Error("builder is closed");
|
|
226
256
|
this.eventQueue.push({ kind: "activity", state: "starting Codex turn", provider: "codex", model: this.opts.model });
|
|
@@ -233,12 +263,29 @@ export class CodexAdapter {
|
|
|
233
263
|
const toolsBefore = this.observedToolCalls;
|
|
234
264
|
this.turnSawTool = false;
|
|
235
265
|
this.activeText = [];
|
|
266
|
+
this.turnStreamedBytes = 0;
|
|
267
|
+
this.streamStartedAt = 0;
|
|
268
|
+
this.streamActivityAt = 0;
|
|
269
|
+
this.streamDiagnosticAt = 0;
|
|
236
270
|
this.lastTurnTokens = undefined;
|
|
237
271
|
this.turnUsageBaseline = this.providerSessionUsage ? { ...this.providerSessionUsage } : undefined;
|
|
238
|
-
const
|
|
239
|
-
|
|
272
|
+
const deadline = new OperationDeadline("Codex active turn", this.opts.turnDeadlineMs ?? 3_600_000);
|
|
273
|
+
let idleTimer;
|
|
274
|
+
const clearIdle = () => { if (idleTimer)
|
|
275
|
+
clearTimeout(idleTimer); idleTimer = undefined; };
|
|
276
|
+
const resetIdle = () => {
|
|
277
|
+
clearIdle();
|
|
278
|
+
idleTimer = setTimeout(() => collector?.fail(new Error("Codex native turn idle deadline expired; execution outcome requires reconciliation")), this.providerIdleTimeoutMs());
|
|
279
|
+
idleTimer.unref();
|
|
280
|
+
};
|
|
281
|
+
const wire = policy?.output ? policy.wireContract ?? providerWireContract(policy.output, "codex", "default", { requireRegistered: true }) : undefined;
|
|
282
|
+
const collector = policy?.output ? this.structuredCollector = new CodexTurnCollector(this._sessionId, true, resetIdle, wire.maximumResponseBytes) : undefined;
|
|
283
|
+
const completion = collector ? collector.completion : this.waitFor("turn/completed", (params) => params.threadId === this._sessionId, this.providerIdleTimeoutMs(), true);
|
|
284
|
+
void completion.then(clearIdle, clearIdle);
|
|
240
285
|
turnStartDispatched = true;
|
|
286
|
+
const readOnlyTurn = Boolean(policy?.responseOnly || policy?.purpose && policy.purpose !== "implementation" || this.opts.sandboxMode === "read-only" || this.opts.sessionRole === "qa");
|
|
241
287
|
const startParams = {
|
|
288
|
+
...(wire ? { outputSchema: wire.wireSchema } : {}),
|
|
242
289
|
threadId: this._sessionId,
|
|
243
290
|
input: [{ type: "text", text: providerInstruction, text_elements: [] }],
|
|
244
291
|
cwd: this.opts.cwd,
|
|
@@ -246,41 +293,82 @@ export class CodexAdapter {
|
|
|
246
293
|
effort: this.opts.effort ?? null,
|
|
247
294
|
...(this.opts.fast ? { serviceTier: this.fastTier ?? "default" } : {}),
|
|
248
295
|
approvalPolicy: policy?.responseOnly || policy?.purpose && policy.purpose !== "implementation" ? "never" : this.effectiveApprovalPolicy(),
|
|
249
|
-
sandboxPolicy:
|
|
250
|
-
? { type: "readOnly", networkAccess:
|
|
296
|
+
sandboxPolicy: readOnlyTurn
|
|
297
|
+
? { type: "readOnly", networkAccess: this.qaNetworkAccess() }
|
|
251
298
|
: { type: "workspaceWrite", writableRoots: [this.opts.cwd], networkAccess: this.effectiveNetworkAccess() },
|
|
252
299
|
};
|
|
253
300
|
let started;
|
|
254
301
|
try {
|
|
255
|
-
started = await this.request("turn/start", startParams);
|
|
302
|
+
started = await deadline.run(() => this.request("turn/start", startParams));
|
|
256
303
|
}
|
|
257
304
|
catch (error) {
|
|
258
|
-
if (!this.fastTier || !isFastModeUnavailable(String(error)) || this.turnSawTool || this.activeText.length)
|
|
305
|
+
if (policy?.output || !this.fastTier || !isFastModeUnavailable(String(error)) || this.turnSawTool || this.activeText.length)
|
|
259
306
|
throw error;
|
|
260
307
|
this.disableFastMode();
|
|
261
308
|
started = await this.request("turn/start", { ...startParams, serviceTier: "default" });
|
|
262
309
|
}
|
|
263
310
|
this.activeNativeTurnId = String(started.turn?.id ?? "") || undefined;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
311
|
+
collector?.bind(this.activeNativeTurnId ?? "");
|
|
312
|
+
let params;
|
|
313
|
+
try {
|
|
314
|
+
params = await deadline.run(() => completion);
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
// Runaway output is a definite provider rejection, not an unknown
|
|
318
|
+
// outcome. A read-only turn cannot change the workspace, so after the
|
|
319
|
+
// native turn is confirmed stopped it may be asked once more, inside
|
|
320
|
+
// the same sealed host dispatch. Writable turns are never retried.
|
|
321
|
+
const nativeTurnId = this.activeNativeTurnId;
|
|
322
|
+
if (error instanceof CodexRunawayOutputError && runawayRetries < 1 && readOnlyTurn && nativeTurnId && await this.interruptNativeTurn(nativeTurnId)) {
|
|
323
|
+
this.writeStreamDiagnostic("interrupted", `${error.message}; retrying once`);
|
|
324
|
+
this.traceLifecycle("runaway-output-retry", { elapsedMs: 0 });
|
|
325
|
+
this.eventQueue.push({ kind: "retry", provider: "codex", reason: error.message, attempt: 1, maximum: 1, managedBy: "rafi", retryId: nativeTurnId });
|
|
326
|
+
return this.sendTurnInternal(instruction, policy, runawayRetries + 1);
|
|
327
|
+
}
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
const turn = collector ? params : params.turn;
|
|
331
|
+
const responseOnlyViolation = Boolean(policy?.responseOnly && (collector ? collector.tools : this.observedToolCalls !== toolsBefore));
|
|
332
|
+
const failed = turn?.status !== "completed" || responseOnlyViolation;
|
|
268
333
|
const error = turn?.error;
|
|
269
|
-
|
|
270
|
-
|
|
334
|
+
let final;
|
|
335
|
+
let invalidFinal;
|
|
336
|
+
try {
|
|
337
|
+
final = collector?.final();
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
invalidFinal = String(error);
|
|
341
|
+
}
|
|
342
|
+
if (this.streamDiagnosticAt)
|
|
343
|
+
this.writeStreamDiagnostic(failed || invalidFinal ? "interrupted" : "completed", failed ? `Codex terminal ${String(turn?.status)}` : invalidFinal);
|
|
344
|
+
const text = final?.text ?? this.activeText.join("\n");
|
|
345
|
+
if (!policy?.output && failed && !responseOnlyViolation && this.fastTier && !this.turnSawTool && !text && isFastModeUnavailable(String(error?.message ?? ""))) {
|
|
271
346
|
this.disableFastMode();
|
|
272
347
|
return this.sendTurnInternal(instruction, policy);
|
|
273
348
|
}
|
|
274
|
-
|
|
349
|
+
let result = {
|
|
275
350
|
text: failed ? normalizeRuntimeErrorText("codex", String(responseOnlyViolation ? "Response-only correction used tools; its result cannot authorize completion" : error?.message ?? text), null, "app-server turn") : text,
|
|
276
351
|
isError: failed, numTurns: 1, costUsd: 0, costAuthoritative: false,
|
|
277
352
|
turnId: this.activeProviderTurnId, hostInstruction: instruction, providerInstruction,
|
|
278
353
|
rawResponse: text, cleanedResponse: failed ? normalizeRuntimeErrorText("codex", String(responseOnlyViolation ? "Response-only correction used tools; its result cannot authorize completion" : error?.message ?? text), null, "app-server turn") : text,
|
|
279
354
|
providerMetadata: { provider: "codex", sessionId: this._sessionId, sessionRef: this._sessionRef },
|
|
355
|
+
...(policy?.selectedDispatch ? { selectedDispatch: policy.selectedDispatch } : {}),
|
|
280
356
|
...(this.lastTurnTokens ?? {}),
|
|
281
357
|
};
|
|
282
358
|
result.usage = { scope: "turn-delta", inputTokens: result.inputTokens, outputTokens: result.outputTokens,
|
|
283
359
|
...(result.inputTokens !== undefined || result.outputTokens !== undefined ? { totalTokens: (result.inputTokens ?? 0) + (result.outputTokens ?? 0) } : {}) };
|
|
360
|
+
if (policy?.output) {
|
|
361
|
+
const selectedDispatch = policy.selectedDispatch;
|
|
362
|
+
if (!selectedDispatch || !this._sessionId)
|
|
363
|
+
throw new Error("Structured terminal lost its sealed dispatch tuple or session");
|
|
364
|
+
result = { ...result, selectedDispatch, structured: failed ? { state: "rejected", reason: responseOnlyViolation ? "tool-policy-violation" : "execution-not-completed", detail: `Codex terminal ${String(turn?.status)}`, dispatchId: result.turnId }
|
|
365
|
+
: invalidFinal ? { state: "rejected", reason: "ambiguous-result", detail: invalidFinal, dispatchId: result.turnId }
|
|
366
|
+
: !wire || !policy.selectedDispatch
|
|
367
|
+
? { state: "rejected", reason: "invalid-contract", detail: "Codex structured turn has no sealed descriptor or tuple", dispatchId: result.turnId }
|
|
368
|
+
: structuredReceipt(policy.output, "codex", final?.itemId ? text : undefined, { dispatchId: result.turnId, sessionId: this._sessionId, nativeTurnId: this.activeNativeTurnId, nativeItemId: final?.itemId }, "json-text", wire, policy.selectedDispatch) };
|
|
369
|
+
if (result.structured.state === "rejected")
|
|
370
|
+
result.isError = true;
|
|
371
|
+
}
|
|
284
372
|
this.eventQueue.push({ kind: "turn-complete", result, turnId: result.turnId });
|
|
285
373
|
return result;
|
|
286
374
|
}
|
|
@@ -298,6 +386,8 @@ export class CodexAdapter {
|
|
|
298
386
|
return result;
|
|
299
387
|
}
|
|
300
388
|
const detail = [error instanceof Error ? error.message : String(error), this.stderr].filter(Boolean).join("\n");
|
|
389
|
+
if (this.turnStreamedBytes)
|
|
390
|
+
this.writeStreamDiagnostic("interrupted", error instanceof Error ? error.message : String(error));
|
|
301
391
|
const text = normalizeRuntimeErrorText("codex", detail, this.process?.exitCode ?? null, "builder turn");
|
|
302
392
|
this.disconnect(new Error(text));
|
|
303
393
|
const resumed = Boolean(this.opts.resumeSessionRef);
|
|
@@ -379,6 +469,7 @@ export class CodexAdapter {
|
|
|
379
469
|
}, hardTimeoutMs, false, controller.signal);
|
|
380
470
|
this.manualCompactionInFlight = true;
|
|
381
471
|
try {
|
|
472
|
+
providerControlOperationV2("codex", "thread/compact/start");
|
|
382
473
|
const acknowledgementFailure = this.request("thread/compact/start", { threadId: this._sessionId }).then(() => { this.traceLifecycle("compaction-acknowledged"); return new Promise(() => { }); }, error => {
|
|
383
474
|
// A missing acknowledgement is not failure evidence. Explicit
|
|
384
475
|
// completion plus fresh occupancy is sufficient to reconcile it.
|
|
@@ -391,6 +482,13 @@ export class CodexAdapter {
|
|
|
391
482
|
finally {
|
|
392
483
|
this.manualCompactionInFlight = false;
|
|
393
484
|
}
|
|
485
|
+
// Manual completion must invalidate delivery receipts just like automatic
|
|
486
|
+
// compaction. Continuity accounts for this requested compaction separately.
|
|
487
|
+
this.nativeCompactionSequence++;
|
|
488
|
+
if (this.qaContractReminder) {
|
|
489
|
+
this.qaContractReminderPending = true;
|
|
490
|
+
this.qaReminderEvent("compaction-observed");
|
|
491
|
+
}
|
|
394
492
|
this.eventQueue.push({ kind: "session-transition", transition: "compacted" });
|
|
395
493
|
this.traceLifecycle("compaction-completed", { elapsedMs: performance.now() - began });
|
|
396
494
|
return { ok: true };
|
|
@@ -437,11 +535,28 @@ export class CodexAdapter {
|
|
|
437
535
|
if (this.opts.allowAutoCompactionSetupTurn === false)
|
|
438
536
|
return;
|
|
439
537
|
const toolsBefore = this.observedToolCalls;
|
|
440
|
-
const
|
|
538
|
+
const descriptor = providerWireContract(initializationOutput, "codex");
|
|
539
|
+
const instruction = renderProviderWireInstruction("Rafi internal initialization only. Do not call tools or modify files. Submit the native session-initialization result with completed payload {ready:true} and null continuity.", descriptor);
|
|
540
|
+
const session = this.sessionRef();
|
|
541
|
+
if (!session)
|
|
542
|
+
throw new Error("Ephemeral Codex initialization requires a prepared session");
|
|
543
|
+
const id = randomUUID();
|
|
544
|
+
const tuple = selectedDispatchTupleV2({ operation: initializationOutput.operation, schemaVersion: initializationOutput.schemaVersion, schemaDigest: initializationOutput.schemaDigest,
|
|
545
|
+
provider: "codex", profile: descriptor.profile, variant: descriptor.variant, descriptorDigest: descriptor.descriptorDigest,
|
|
546
|
+
wireSchemaDigest: descriptor.wireSchemaDigest, guidanceDigest: descriptor.guidanceDigest, normalizerId: descriptor.normalizerId,
|
|
547
|
+
normalizerVersion: descriptor.normalizerVersion, maximumSchemaBytes: descriptor.maximumSchemaBytes, maximumResponseBytes: descriptor.maximumResponseBytes,
|
|
548
|
+
maximumPageBytes: descriptor.maximumPageBytes ?? null, instructionDigest: structuredDigest(instruction), actionId: id, dispatchId: id, sessionId: session.sessionId, generation: session.generation });
|
|
549
|
+
const setup = await withEphemeralInitializationDispatch(this.opts.configRoot ?? this.opts.cwd, tuple, dispatchCapability => this.sendTurn(instruction, { purpose: "initialization", responseOnly: true, output: initializationOutput, dispatchId: id, logicalActionId: id, wireContract: descriptor,
|
|
550
|
+
renderedWireContractDigest: descriptor.descriptorDigest, selectedDispatch: tuple, dispatchCapability }), session);
|
|
441
551
|
if (setup.isError)
|
|
442
552
|
throw new Error(`Codex automatic-compaction setup failed: ${setup.text.slice(0, 240)}`);
|
|
443
553
|
if (this.observedToolCalls !== toolsBefore)
|
|
444
554
|
throw new Error("Codex automatic-compaction setup unexpectedly called a tool");
|
|
555
|
+
if (!setup.selectedDispatch)
|
|
556
|
+
throw new Error("Codex initialization returned no sealed dispatch tuple");
|
|
557
|
+
const accepted = validateStructuredCandidate(initializationOutput, setup.structured, providerWireContract(initializationOutput, "codex"), setup.selectedDispatch);
|
|
558
|
+
if (accepted.result.kind !== "completed" || !accepted.result.payload.ready)
|
|
559
|
+
throw new Error("Codex initialization did not confirm readiness");
|
|
445
560
|
}
|
|
446
561
|
const maximum = this.usage?.maximum;
|
|
447
562
|
if (!maximum || !Number.isFinite(maximum) || maximum <= 0) {
|
|
@@ -527,6 +642,8 @@ export class CodexAdapter {
|
|
|
527
642
|
if (this.closed)
|
|
528
643
|
return;
|
|
529
644
|
this.closed = true;
|
|
645
|
+
if (this.turnInFlight && this.turnStreamedBytes)
|
|
646
|
+
this.writeStreamDiagnostic("interrupted", "adapter closed during an active turn");
|
|
530
647
|
const child = this.process;
|
|
531
648
|
let onExit;
|
|
532
649
|
const exited = child && child.exitCode === null ? new Promise(resolve => { onExit = resolve; child.once("exit", resolve); }) : Promise.resolve();
|
|
@@ -716,7 +833,17 @@ export class CodexAdapter {
|
|
|
716
833
|
if (!message.method)
|
|
717
834
|
return;
|
|
718
835
|
const params = message.params ?? {};
|
|
719
|
-
|
|
836
|
+
this.structuredCollector?.ingest(message.method, params);
|
|
837
|
+
// Native receipts and tool observations must belong to the same dispatch.
|
|
838
|
+
// Foreign notifications remain unable to affect completion or evidence.
|
|
839
|
+
if (this.structuredCollector && (params.threadId && params.threadId !== this._sessionId || this.activeNativeTurnId && params.turnId && params.turnId !== this.activeNativeTurnId))
|
|
840
|
+
return;
|
|
841
|
+
if (message.method === "item/agentMessage/delta") {
|
|
842
|
+
if (!this.turnInFlight || params.threadId && this._sessionId && params.threadId !== this._sessionId)
|
|
843
|
+
return;
|
|
844
|
+
this.observeAgentMessageDelta(params);
|
|
845
|
+
}
|
|
846
|
+
else if (message.method === "item/started") {
|
|
720
847
|
const item = params.item;
|
|
721
848
|
if (item)
|
|
722
849
|
this.eventQueue.push({ kind: "provider-item", provider: "codex", lifecycle: "started", itemType: String(item.type ?? "unknown"), payload: exactProviderItem(item), payloadCompleteness: "complete", providerTurnId: this.activeProviderTurnId, eventId: randomUUID(), observedAt: new Date().toISOString() });
|
|
@@ -904,6 +1031,54 @@ export class CodexAdapter {
|
|
|
904
1031
|
}
|
|
905
1032
|
});
|
|
906
1033
|
}
|
|
1034
|
+
/** Ask the app-server to stop a native turn; true only once its terminal is observed. */
|
|
1035
|
+
async interruptNativeTurn(nativeTurnId) {
|
|
1036
|
+
const stopped = this.waitFor("turn/completed", params => params.threadId === this._sessionId && params.turn?.id === nativeTurnId, 15_000)
|
|
1037
|
+
.then(() => true, () => false);
|
|
1038
|
+
try {
|
|
1039
|
+
await new OperationDeadline("Codex turn interrupt", 10_000).run(() => this.request("turn/interrupt", { threadId: this._sessionId, turnId: nativeTurnId }));
|
|
1040
|
+
}
|
|
1041
|
+
catch (error) {
|
|
1042
|
+
this.traceLifecycle("turn-interrupt-failed", { method: error instanceof Error ? error.message.slice(0, 200) : "unknown" });
|
|
1043
|
+
}
|
|
1044
|
+
return stopped;
|
|
1045
|
+
}
|
|
1046
|
+
/** Streamed output is visible progress; it is never treated as a result. */
|
|
1047
|
+
observeAgentMessageDelta(params) {
|
|
1048
|
+
if (typeof params.delta !== "string")
|
|
1049
|
+
return;
|
|
1050
|
+
this.turnStreamedBytes += Buffer.byteLength(params.delta);
|
|
1051
|
+
const now = Date.now();
|
|
1052
|
+
if (!this.streamStartedAt)
|
|
1053
|
+
this.streamStartedAt = now;
|
|
1054
|
+
if (now - this.streamActivityAt >= 1_000) {
|
|
1055
|
+
this.streamActivityAt = now;
|
|
1056
|
+
this.eventQueue.push({ kind: "activity", provider: "codex", state: "writing response", detail: `${formatStreamBytes(this.turnStreamedBytes)} streamed`, transient: true });
|
|
1057
|
+
}
|
|
1058
|
+
if (now - (this.streamDiagnosticAt || this.streamStartedAt) >= 2_000)
|
|
1059
|
+
this.writeStreamDiagnostic("streaming");
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Bounded interruption evidence for structured turns under .foreman (Rafi's
|
|
1063
|
+
* git-excluded log directory). Diagnostic only: never read back as output.
|
|
1064
|
+
*/
|
|
1065
|
+
writeStreamDiagnostic(state, detail) {
|
|
1066
|
+
const collector = this.structuredCollector, nativeTurnId = this.activeNativeTurnId, threadId = this._sessionId;
|
|
1067
|
+
if (!collector || !nativeTurnId || !threadId)
|
|
1068
|
+
return;
|
|
1069
|
+
this.streamDiagnosticAt = Date.now();
|
|
1070
|
+
try {
|
|
1071
|
+
const safe = (value) => value.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 120);
|
|
1072
|
+
const dir = join(this.opts.configRoot ?? this.opts.cwd, ".foreman", "provider-streams");
|
|
1073
|
+
mkdirSync(dir, { recursive: true });
|
|
1074
|
+
const path = join(dir, `codex-${safe(threadId)}-${safe(nativeTurnId)}.json`);
|
|
1075
|
+
const body = { version: 1, provider: "codex", state, threadId, nativeTurnId, dispatchId: this.activeProviderTurnId, updatedAt: new Date().toISOString(),
|
|
1076
|
+
...(detail ? { detail: Buffer.from(detail).subarray(0, 2_000).toString() } : {}), ...collector.streamDiagnostic() };
|
|
1077
|
+
writeFileSync(`${path}.tmp`, JSON.stringify(body, null, 2));
|
|
1078
|
+
renameSync(`${path}.tmp`, path);
|
|
1079
|
+
}
|
|
1080
|
+
catch { /* diagnostics cannot change execution */ }
|
|
1081
|
+
}
|
|
907
1082
|
traceLifecycle(phase, data = {}) {
|
|
908
1083
|
try {
|
|
909
1084
|
this.opts.onLifecycleTrace?.({ phase, at: new Date().toISOString(), sessionId: this._sessionId, ...data });
|
|
@@ -916,9 +1091,13 @@ export class CodexAdapter {
|
|
|
916
1091
|
waiter.touch?.();
|
|
917
1092
|
}
|
|
918
1093
|
providerIdleTimeoutMs() {
|
|
919
|
-
const configured = this.opts.providerIdleTimeoutMs;
|
|
1094
|
+
const configured = this.opts.providerIdleTimeoutMs ?? Number(process.env.RAFI_PROVIDER_IDLE_TIMEOUT_MS);
|
|
920
1095
|
return Number.isFinite(configured) && configured > 0 ? configured : DEFAULT_PROVIDER_IDLE_TIMEOUT_MS;
|
|
921
1096
|
}
|
|
1097
|
+
/** Read-only QA may reach the network only with the operator's --qa-network grant. */
|
|
1098
|
+
qaNetworkAccess() {
|
|
1099
|
+
return this.opts.sessionRole === "qa" && this.opts.networkAccess === true;
|
|
1100
|
+
}
|
|
922
1101
|
effectiveNetworkAccess() {
|
|
923
1102
|
return this.opts.sandboxMode !== "read-only" && this.opts.sessionRole !== "qa" && this.opts.networkAccess === true;
|
|
924
1103
|
}
|
|
@@ -973,6 +1152,7 @@ export class CodexAdapter {
|
|
|
973
1152
|
}
|
|
974
1153
|
}
|
|
975
1154
|
disconnect(error) {
|
|
1155
|
+
this.structuredCollector?.fail(error);
|
|
976
1156
|
this.connectionGeneration++;
|
|
977
1157
|
for (const controller of this.approvalRequests.values())
|
|
978
1158
|
controller.abort();
|
|
@@ -1088,3 +1268,6 @@ function loadSkillMarkdown(cwd, skill) {
|
|
|
1088
1268
|
function cumulativeDelta(before, after) {
|
|
1089
1269
|
return before !== undefined && after !== undefined && after >= before ? after - before : undefined;
|
|
1090
1270
|
}
|
|
1271
|
+
function formatStreamBytes(bytes) {
|
|
1272
|
+
return bytes < 1024 ? `${bytes} B` : bytes < 1_048_576 ? `${(bytes / 1024).toFixed(1)} KB` : `${(bytes / 1_048_576).toFixed(1)} MB`;
|
|
1273
|
+
}
|
|
@@ -18,6 +18,8 @@ export interface RecoveringAdapterOptions {
|
|
|
18
18
|
/** Adds interactive recovery around structured runtime turn failures. */
|
|
19
19
|
export declare class RecoveringAdapter implements BuilderAdapter {
|
|
20
20
|
private readonly opts;
|
|
21
|
+
postConfirmedDispatch(reference: import("../adapters/types.js").ConfirmedDispatchReference): Promise<TurnResult | void>;
|
|
22
|
+
structuredDispatchRequirement(): import("../adapters/types.js").StructuredDispatchRequirement | undefined;
|
|
21
23
|
private adapter;
|
|
22
24
|
private runtime;
|
|
23
25
|
private readonly eventQueue;
|
|
@@ -5,6 +5,8 @@ import { randomUUID } from "node:crypto";
|
|
|
5
5
|
/** Adds interactive recovery around structured runtime turn failures. */
|
|
6
6
|
export class RecoveringAdapter {
|
|
7
7
|
opts;
|
|
8
|
+
postConfirmedDispatch(reference) { return this.adapter.postConfirmedDispatch?.(reference) ?? Promise.resolve(); }
|
|
9
|
+
structuredDispatchRequirement() { return this.adapter.structuredDispatchRequirement?.(); }
|
|
8
10
|
adapter;
|
|
9
11
|
runtime;
|
|
10
12
|
eventQueue = new AsyncQueue();
|
|
@@ -45,7 +47,9 @@ export class RecoveringAdapter {
|
|
|
45
47
|
this.eventQueue.push({ kind: "error", message: `failed to persist scoped provider session binding: ${error instanceof Error ? error.message : String(error)}` });
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
|
|
50
|
+
// Structured operations have one recovery owner above the adapter. Never
|
|
51
|
+
// hide a second actual dispatch behind the same request/receipt identity.
|
|
52
|
+
if (policy?.output || policy?.handback || !this.opts.enabled || !result.isError || !result.failure)
|
|
49
53
|
return result;
|
|
50
54
|
// The host cannot know whether a missing exact session received this
|
|
51
55
|
// instruction. Never replay it or silently switch providers.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { providerWireContract, type OutputContract, type SelectedDispatchTupleV2, type StructuredResponse } from "rafi-spec";
|
|
2
|
+
export declare function structuredReceipt(contract: OutputContract, provider: "claude" | "codex", value: unknown, identity: {
|
|
3
|
+
dispatchId: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
nativeTurnId?: string;
|
|
6
|
+
nativeItemId?: string;
|
|
7
|
+
}, representation: "json-text" | "decoded-json", selected: ReturnType<typeof providerWireContract>, tuple: SelectedDispatchTupleV2): StructuredResponse;
|
|
8
|
+
/** Pretty-printed JSON never needs this much consecutive whitespace. */
|
|
9
|
+
export declare const CODEX_RUNAWAY_WHITESPACE_BYTES = 2048;
|
|
10
|
+
/** The provider is emitting a runaway whitespace sequence; its output can never be accepted. */
|
|
11
|
+
export declare class CodexRunawayOutputError extends Error {
|
|
12
|
+
readonly itemId: string;
|
|
13
|
+
readonly whitespaceBytes: number;
|
|
14
|
+
constructor(itemId: string, whitespaceBytes: number);
|
|
15
|
+
}
|
|
16
|
+
/** Bounded, non-authoritative view of one streamed agent message. */
|
|
17
|
+
export interface CodexStreamItemDiagnostic {
|
|
18
|
+
itemId: string;
|
|
19
|
+
bytes: number;
|
|
20
|
+
deltas: number;
|
|
21
|
+
completed: boolean;
|
|
22
|
+
whitespaceRun: number;
|
|
23
|
+
head: string;
|
|
24
|
+
tail: string;
|
|
25
|
+
textRetained: boolean;
|
|
26
|
+
startedAt: string;
|
|
27
|
+
lastDeltaAt: string;
|
|
28
|
+
}
|
|
29
|
+
export interface CodexStreamDiagnostic {
|
|
30
|
+
authoritative: false;
|
|
31
|
+
streamedBytes: number;
|
|
32
|
+
deltas: number;
|
|
33
|
+
items: CodexStreamItemDiagnostic[];
|
|
34
|
+
}
|
|
35
|
+
/** Holds early notifications until turn/start supplies the exact turn identity. */
|
|
36
|
+
export declare class CodexTurnCollector {
|
|
37
|
+
private readonly threadId;
|
|
38
|
+
private readonly requirePhase;
|
|
39
|
+
private readonly onProgress?;
|
|
40
|
+
private readonly maximumResponseBytes;
|
|
41
|
+
private turnId?;
|
|
42
|
+
private early;
|
|
43
|
+
private bytes;
|
|
44
|
+
private finals;
|
|
45
|
+
private streams;
|
|
46
|
+
private streamedBytes;
|
|
47
|
+
private streamedDeltas;
|
|
48
|
+
private earlyDeltaBytes;
|
|
49
|
+
private terminal?;
|
|
50
|
+
private error?;
|
|
51
|
+
tools: boolean;
|
|
52
|
+
private resolve;
|
|
53
|
+
private reject;
|
|
54
|
+
readonly completion: Promise<Record<string, unknown>>;
|
|
55
|
+
constructor(threadId: string, requirePhase: boolean, onProgress?: (() => void) | undefined, maximumResponseBytes?: number);
|
|
56
|
+
bind(turnId: string): void;
|
|
57
|
+
ingest(method: string, params: Record<string, unknown>): void;
|
|
58
|
+
/**
|
|
59
|
+
* Streamed text is bounded before completion, but never becomes a result:
|
|
60
|
+
* only a completed final item is accepted and charged against the budget.
|
|
61
|
+
*/
|
|
62
|
+
private ingestDelta;
|
|
63
|
+
streamDiagnostic(): CodexStreamDiagnostic;
|
|
64
|
+
fail(error: Error): void;
|
|
65
|
+
private invalidate;
|
|
66
|
+
private settle;
|
|
67
|
+
final(): {
|
|
68
|
+
text: string;
|
|
69
|
+
itemId?: string;
|
|
70
|
+
};
|
|
71
|
+
}
|