@xfey/tutti 0.1.94 → 0.1.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collaboration-state/index.d.ts +2 -0
- package/dist/collaboration-state/index.js +1 -0
- package/dist/collaboration-state/messages.js +15 -0
- package/dist/collaboration-state/run-recording.js +18 -1
- package/dist/collaboration-state/run-results.js +6 -0
- package/dist/collaboration-state/task-retry.d.ts +16 -0
- package/dist/collaboration-state/task-retry.js +134 -0
- package/dist/control-plane/command-recovery.js +9 -1
- package/dist/control-plane/index.d.ts +3 -2
- package/dist/control-plane/index.js +14 -0
- package/dist/control-plane/run-retry.d.ts +19 -0
- package/dist/control-plane/run-retry.js +103 -0
- package/dist/control-plane/run-start.d.ts +2 -1
- package/dist/control-plane/run-start.js +3 -0
- package/dist/control-plane/types.d.ts +12 -0
- package/dist/control-plane/workflows/openai.d.ts +1 -0
- package/dist/control-plane/workflows/openai.js +86 -15
- package/dist/control-plane/worklist-terminal-feedback.js +3 -0
- package/dist/prompt-templates/index.d.ts +1 -1
- package/dist/prompt-templates/index.js +4 -3
- package/dist/providers/openai/app-server/durable-session-smoke-cli.d.ts +2 -0
- package/dist/providers/openai/app-server/durable-session-smoke-cli.js +229 -0
- package/dist/providers/openai/app-server/read-only-procedure.d.ts +10 -0
- package/dist/providers/openai/app-server/read-only-procedure.js +42 -13
- package/dist/providers/openai/app-server/session-store.d.ts +74 -0
- package/dist/providers/openai/app-server/session-store.js +305 -0
- package/dist/providers/openai/app-server/workspace-write-run.d.ts +17 -4
- package/dist/providers/openai/app-server/workspace-write-run.js +92 -28
- package/dist/providers/openai/codex-app-server.d.ts +1 -0
- package/dist/providers/openai/codex-app-server.js +2 -0
- package/dist/run-pipeline/openai.d.ts +1 -0
- package/dist/run-pipeline/openai.js +15 -43
- package/dist/run-pipeline/task-run-invocation.js +354 -93
- package/dist/server-shell/http/routes/project-api/execution-routes.js +32 -2
- package/dist/server-shell/http/routes/project-api/invalidations.d.ts +2 -1
- package/dist/server-shell/http/routes/project-api/invalidations.js +13 -0
- package/dist/server-shell/http/routes/project-api/openapi-execution-routes.d.ts +21 -0
- package/dist/server-shell/http/routes/project-api/openapi-execution-routes.js +12 -1
- package/dist/server-shell/http/routes/project-api/openapi.d.ts +7 -0
- package/node_modules/@tutti/shared/dist/schemas/api/clarifications.d.ts +1 -0
- package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +1 -1
- package/node_modules/@tutti/shared/dist/schemas/api/index.js +1 -1
- package/node_modules/@tutti/shared/dist/schemas/api/messages.d.ts +5 -0
- package/node_modules/@tutti/shared/dist/schemas/api/messages.js +1 -0
- package/node_modules/@tutti/shared/dist/schemas/api/runtime-events.d.ts +1 -0
- package/node_modules/@tutti/shared/dist/schemas/api/types.d.ts +4 -1
- package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.d.ts +1 -0
- package/node_modules/@tutti/shared/dist/schemas/api/work-items.d.ts +4 -0
- package/node_modules/@tutti/shared/dist/schemas/api/work-items.js +3 -0
- package/node_modules/@tutti/shared/dist/schemas/api/workspace-commands.d.ts +33 -0
- package/node_modules/@tutti/shared/dist/schemas/api/workspace-commands.js +35 -1
- package/package.json +1 -1
- package/prompts/runs/README.md +11 -16
- package/prompts/runs/task-execute.md +86 -0
- package/prompts/runs/task-finalize.md +11 -0
- package/prompts/runs/task-recovery.md +15 -0
- package/prompts/runs/task-result-contract.md +64 -0
- package/web/assets/{homepage-motion-scene-CLTkixIk.js → homepage-motion-scene-BFo4r4nz.js} +1 -1
- package/web/assets/index-CNIl4TF8.js +69 -0
- package/web/assets/index-quWLFJgD.css +1 -0
- package/web/index.html +2 -2
- package/prompts/runs/task-continuation.md +0 -201
- package/prompts/runs/task-retry.md +0 -209
- package/prompts/runs/task-run.md +0 -188
- package/web/assets/index-BTNYElB-.js +0 -69
- package/web/assets/index-Deu_eF5K.css +0 -1
|
@@ -17,17 +17,18 @@ function summarizeProviderError(error) {
|
|
|
17
17
|
error.message.includes("invalid_json_schema") ||
|
|
18
18
|
error.message.includes("Invalid schema") ||
|
|
19
19
|
error.message.includes("response_format");
|
|
20
|
+
const turnStateUnknown = error.details.turn_start_state === "start_requested";
|
|
20
21
|
const errorCode = looksLikeSchemaError
|
|
21
22
|
? "codex_app_server_output_schema_invalid"
|
|
22
|
-
:
|
|
23
|
+
: turnStateUnknown
|
|
24
|
+
? "codex_app_server_turn_state_unknown"
|
|
25
|
+
: `codex_app_server_${error.code}`;
|
|
23
26
|
return failedBeforeChecks({
|
|
24
27
|
summary: `Provider execution failed before checks. (${errorCode})`,
|
|
25
28
|
errorCode,
|
|
26
29
|
retryable: !looksLikeSchemaError &&
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
error.code === "turn_failed" ||
|
|
30
|
-
error.code === "thread_start_failed"),
|
|
30
|
+
error.details.turn_start_state === "not_started" &&
|
|
31
|
+
(error.code === "app_server_protocol_error" || error.code === "thread_start_failed"),
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
function summarizeWorkspaceOpsError(error, changedPaths) {
|
|
@@ -153,12 +154,12 @@ function loggedSyncStage(options, input, stage, run, completedFields = () => ({}
|
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
const MAX_SELF_CORRECTION_RETRIES = 1;
|
|
156
|
-
const MAX_PROVIDER_TRANSIENT_RETRIES = 1;
|
|
157
157
|
function runPipelineResultPromotion(result) {
|
|
158
158
|
return "promotion" in result ? result.promotion : undefined;
|
|
159
159
|
}
|
|
160
160
|
function buildCorrectionContext(input) {
|
|
161
161
|
const correction = {
|
|
162
|
+
previous_provider_result: input.previousProviderResult,
|
|
162
163
|
summary: input.result.summary,
|
|
163
164
|
reason_code: input.reasonCode,
|
|
164
165
|
guidance: input.guidance,
|
|
@@ -189,23 +190,12 @@ function selfCorrectionInput(input, nextAttempt, correctionContext) {
|
|
|
189
190
|
return {
|
|
190
191
|
...input,
|
|
191
192
|
run_lineage: {
|
|
192
|
-
|
|
193
|
+
...input.run_lineage,
|
|
193
194
|
attempt: nextAttempt,
|
|
194
|
-
parent_activity_ref: input.activity_ref,
|
|
195
|
-
root_activity_ref: input.run_lineage.root_activity_ref ?? input.activity_ref,
|
|
196
195
|
},
|
|
197
196
|
correction_context: correctionContext,
|
|
198
197
|
};
|
|
199
198
|
}
|
|
200
|
-
function providerTransientRetryInput(input) {
|
|
201
|
-
return {
|
|
202
|
-
...input,
|
|
203
|
-
run_lineage: {
|
|
204
|
-
...input.run_lineage,
|
|
205
|
-
attempt: input.run_lineage.attempt + 1,
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
199
|
function appendSelfCorrectionExhaustedSummary(input) {
|
|
210
200
|
if (input.attemptedRetries <= 0) {
|
|
211
201
|
return input.result;
|
|
@@ -218,12 +208,6 @@ function appendSelfCorrectionExhaustedSummary(input) {
|
|
|
218
208
|
function isSelfCorrectionRequest(result) {
|
|
219
209
|
return "request_kind" in result && result.request_kind === "self_correction";
|
|
220
210
|
}
|
|
221
|
-
function isProviderTransientRunFailure(result) {
|
|
222
|
-
return (!isSelfCorrectionRequest(result) &&
|
|
223
|
-
result.result_kind === "failed" &&
|
|
224
|
-
result.retryable &&
|
|
225
|
-
result.error_code.startsWith("codex_app_server_"));
|
|
226
|
-
}
|
|
227
211
|
async function runTaskAttempt(options, input) {
|
|
228
212
|
let changedPaths;
|
|
229
213
|
try {
|
|
@@ -289,8 +273,9 @@ async function runTaskAttempt(options, input) {
|
|
|
289
273
|
});
|
|
290
274
|
return requestSelfCorrection({
|
|
291
275
|
result,
|
|
276
|
+
previousProviderResult: taskOutput,
|
|
292
277
|
reasonCode: "no_candidate_changes",
|
|
293
|
-
guidance: "Produce a concrete candidate diff for the
|
|
278
|
+
guidance: "Produce a concrete candidate diff for the current task goal and scope.",
|
|
294
279
|
});
|
|
295
280
|
}
|
|
296
281
|
const artifactGate = await loggedAsyncStage(options, input, "artifact_candidate_validate", () => runArtifactCandidateGate({
|
|
@@ -336,6 +321,7 @@ async function runTaskAttempt(options, input) {
|
|
|
336
321
|
}
|
|
337
322
|
return requestSelfCorrection({
|
|
338
323
|
result: artifactResult,
|
|
324
|
+
previousProviderResult: taskOutput,
|
|
339
325
|
reasonCode: artifactGate.failure.reason_code,
|
|
340
326
|
guidance: artifactGate.failure.guidance,
|
|
341
327
|
diagnostic: artifactGate.failure.diagnostic,
|
|
@@ -361,6 +347,7 @@ async function runTaskAttempt(options, input) {
|
|
|
361
347
|
});
|
|
362
348
|
return requestSelfCorrection({
|
|
363
349
|
result,
|
|
350
|
+
previousProviderResult: taskOutput,
|
|
364
351
|
reasonCode: "checks_failed",
|
|
365
352
|
guidance: "Use the failed checks summary to fix the candidate so the task can pass Tutti checks.",
|
|
366
353
|
});
|
|
@@ -378,8 +365,9 @@ async function runTaskAttempt(options, input) {
|
|
|
378
365
|
});
|
|
379
366
|
return requestSelfCorrection({
|
|
380
367
|
result,
|
|
368
|
+
previousProviderResult: taskOutput,
|
|
381
369
|
reasonCode: "no_candidate_changes",
|
|
382
|
-
guidance: "Produce a concrete final candidate diff for the
|
|
370
|
+
guidance: "Produce a concrete final candidate diff for the current task goal and scope.",
|
|
383
371
|
});
|
|
384
372
|
}
|
|
385
373
|
const checkpoint = loggedSyncStage(options, input, "accepted_checkpoint", () => createAcceptedCheckpoint({
|
|
@@ -444,25 +432,8 @@ export function createOpenAiRunPipelineRunner(options) {
|
|
|
444
432
|
async runTask(input) {
|
|
445
433
|
let currentInput = input;
|
|
446
434
|
let selfCorrectionRetries = 0;
|
|
447
|
-
let providerTransientRetries = 0;
|
|
448
435
|
for (;;) {
|
|
449
436
|
const result = await runTaskAttempt(options, currentInput);
|
|
450
|
-
if (isProviderTransientRunFailure(result) &&
|
|
451
|
-
providerTransientRetries < MAX_PROVIDER_TRANSIENT_RETRIES) {
|
|
452
|
-
providerTransientRetries += 1;
|
|
453
|
-
input.on_stage?.({
|
|
454
|
-
stage: "provider_retry",
|
|
455
|
-
summary: "retrying Codex task after a transient provider failure.",
|
|
456
|
-
});
|
|
457
|
-
logRunPipelineStage(options, currentInput, {
|
|
458
|
-
stage: "provider_retry",
|
|
459
|
-
next_attempt: currentInput.run_lineage.attempt + 1,
|
|
460
|
-
error_code: result.error_code,
|
|
461
|
-
}, "Run pipeline scheduling provider transient retry");
|
|
462
|
-
currentInput = providerTransientRetryInput(currentInput);
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
providerTransientRetries = 0;
|
|
466
437
|
if (!isSelfCorrectionRequest(result)) {
|
|
467
438
|
return result;
|
|
468
439
|
}
|
|
@@ -512,6 +483,7 @@ export function resolveProjectOpenAiRunPipelineRunner(options) {
|
|
|
512
483
|
apiKey: credential.secret,
|
|
513
484
|
workspaceRoot: options.workspaceRoot,
|
|
514
485
|
tuttiHome: options.tuttiHome,
|
|
486
|
+
projectId: options.projectId,
|
|
515
487
|
userSkillsRoot: getProjectUserSkillsRoot(options.tuttiHome, options.projectId),
|
|
516
488
|
...(options.agentContext === undefined ? {} : { agentContext: options.agentContext }),
|
|
517
489
|
...(options.approvalManager === undefined
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { renderPromptTemplate } from "../prompt-templates/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { CODEX_APP_SERVER_VERSION } from "../providers/openai/codex-app-server.js";
|
|
3
|
+
import { CodexAppServerWorkspaceWriteRunError, runCodexAppServerWorkspaceWriteRun, } from "../providers/openai/app-server/workspace-write-run.js";
|
|
4
|
+
import { acquireCodexTaskSessionLock, createProviderFingerprint, markCodexTaskSessionArchived, openCodexTaskSession, readCodexSessionRecoveryEvidence, recordCodexSessionArchiveFailure, recordCodexTaskSessionThread, writeCodexSessionRecoveryEvidence, } from "../providers/openai/app-server/session-store.js";
|
|
3
5
|
import { TASK_RUN_OUTPUT_SCHEMA, isInitialTaskRunOutput, normalizeTaskRunOutput, } from "./task-run-output.js";
|
|
4
6
|
function taskContractRunInput(input) {
|
|
5
7
|
return {
|
|
@@ -28,57 +30,77 @@ function taskContractRunInput(input) {
|
|
|
28
30
|
: { recent_references: input.recent_references }),
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
|
-
function
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
...taskContractRunInput(input),
|
|
37
|
-
...(input.continuation_context === undefined
|
|
38
|
-
? {}
|
|
39
|
-
: { continuation: input.continuation_context }),
|
|
40
|
-
};
|
|
33
|
+
function renderResultContract(options) {
|
|
34
|
+
return renderPromptTemplate({
|
|
35
|
+
templateKey: "runs.task_result_contract",
|
|
36
|
+
...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
|
|
37
|
+
});
|
|
41
38
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
function createExecutePrompt(options, input, session) {
|
|
40
|
+
let executionInput;
|
|
41
|
+
if (input.run_lineage.kind === "continuation") {
|
|
42
|
+
if (input.continuation_context === undefined) {
|
|
43
|
+
throw new CodexAppServerWorkspaceWriteRunError("session_unavailable", "Task continuation context is unavailable.");
|
|
44
|
+
}
|
|
45
|
+
executionInput = {
|
|
46
|
+
continuation: input.continuation_context,
|
|
47
|
+
...(session === undefined ||
|
|
48
|
+
session.disposition !== "resumed" ||
|
|
49
|
+
session.metadata.thread_id === undefined
|
|
50
|
+
? { task_context: taskContractRunInput(input) }
|
|
51
|
+
: {}),
|
|
52
|
+
};
|
|
45
53
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
else {
|
|
55
|
+
executionInput = taskContractRunInput(input);
|
|
56
|
+
}
|
|
57
|
+
return renderPromptTemplate({
|
|
58
|
+
templateKey: "runs.task_execute",
|
|
59
|
+
...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
|
|
60
|
+
variables: { execution_input_json: executionInput },
|
|
61
|
+
});
|
|
50
62
|
}
|
|
51
|
-
function
|
|
52
|
-
const templateKey = input.run_lineage.kind === "continuation"
|
|
53
|
-
? "runs.task_continuation"
|
|
54
|
-
: input.run_lineage.kind === "retry"
|
|
55
|
-
? "runs.task_retry"
|
|
56
|
-
: "runs.task_run";
|
|
63
|
+
function createFinalizePrompt(options) {
|
|
57
64
|
return renderPromptTemplate({
|
|
58
|
-
templateKey,
|
|
65
|
+
templateKey: "runs.task_finalize",
|
|
59
66
|
...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
|
|
67
|
+
variables: { result_contract: renderResultContract(options) },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function createRecoveryPrompt(input) {
|
|
71
|
+
const needsCanonicalContext = input.session === undefined || input.session.metadata.thread_id === undefined;
|
|
72
|
+
return renderPromptTemplate({
|
|
73
|
+
templateKey: "runs.task_recovery",
|
|
74
|
+
...(input.options.promptsRoot === undefined
|
|
75
|
+
? {}
|
|
76
|
+
: { promptsRoot: input.options.promptsRoot }),
|
|
60
77
|
variables: {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
recovery_input_json: {
|
|
79
|
+
...(needsCanonicalContext
|
|
80
|
+
? { task_context: taskContractRunInput(input.pipelineInput) }
|
|
81
|
+
: {}),
|
|
82
|
+
recovery: input.recovery,
|
|
83
|
+
},
|
|
84
|
+
result_contract: renderResultContract(input.options),
|
|
66
85
|
},
|
|
67
86
|
});
|
|
68
87
|
}
|
|
69
|
-
function usageSource(input) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return "follow_up_run";
|
|
73
|
-
case "retry":
|
|
74
|
-
return "pipeline_self_correction";
|
|
75
|
-
case "initial":
|
|
76
|
-
return "workspace_write_run";
|
|
88
|
+
function usageSource(input, phase) {
|
|
89
|
+
if (phase === "finalize") {
|
|
90
|
+
return "task_result_finalize";
|
|
77
91
|
}
|
|
92
|
+
if (phase === "recovery") {
|
|
93
|
+
return "task_result_recovery";
|
|
94
|
+
}
|
|
95
|
+
return input.run_lineage.kind === "continuation"
|
|
96
|
+
? "follow_up_execute"
|
|
97
|
+
: input.run_lineage.kind === "retry"
|
|
98
|
+
? "manual_retry_execute"
|
|
99
|
+
: "workspace_write_execute";
|
|
78
100
|
}
|
|
79
|
-
|
|
101
|
+
function createApprovalHandler(options, input) {
|
|
80
102
|
const manager = options.approvalManager;
|
|
81
|
-
|
|
103
|
+
return manager === undefined
|
|
82
104
|
? undefined
|
|
83
105
|
: (approvalInput) => manager.awaitApproval({
|
|
84
106
|
request: approvalInput.request,
|
|
@@ -97,69 +119,308 @@ export async function runWorkspaceWriteTask(options, input) {
|
|
|
97
119
|
},
|
|
98
120
|
timeoutMs: approvalInput.context.turnTimeoutMs,
|
|
99
121
|
});
|
|
100
|
-
|
|
122
|
+
}
|
|
123
|
+
function openTaskSession(options, input) {
|
|
124
|
+
if (options.tuttiHome === undefined || options.projectId === undefined) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
101
127
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
},
|
|
115
|
-
...(approvalHandler === undefined ? {} : { approvalHandler }),
|
|
116
|
-
...(options.config.api_base_url === undefined
|
|
117
|
-
? {}
|
|
118
|
-
: { apiBaseUrl: options.config.api_base_url }),
|
|
119
|
-
...(options.tuttiHome === undefined ? {} : { tuttiHome: options.tuttiHome }),
|
|
120
|
-
...(options.userSkillsRoot === undefined ? {} : { userSkillsRoot: options.userSkillsRoot }),
|
|
121
|
-
...(options.builtInSkillsRoot === undefined
|
|
122
|
-
? {}
|
|
123
|
-
: { builtInSkillsRoot: options.builtInSkillsRoot }),
|
|
124
|
-
...(options.agentContext === undefined ? {} : { agentContext: options.agentContext }),
|
|
125
|
-
...(options.codexCommand === undefined ? {} : { codexCommand: options.codexCommand }),
|
|
126
|
-
...(options.requestTimeoutMs === undefined
|
|
127
|
-
? {}
|
|
128
|
-
: { requestTimeoutMs: options.requestTimeoutMs }),
|
|
129
|
-
...(options.turnTimeoutMs === undefined ? {} : { turnTimeoutMs: options.turnTimeoutMs }),
|
|
130
|
-
...(options.baseEnv === undefined ? {} : { baseEnv: options.baseEnv }),
|
|
131
|
-
...(options.invocationCoordinator === undefined
|
|
132
|
-
? {}
|
|
133
|
-
: { invocationCoordinator: options.invocationCoordinator }),
|
|
134
|
-
...(options.onLifecycleIncident === undefined
|
|
135
|
-
? {}
|
|
136
|
-
: { onLifecycleIncident: options.onLifecycleIncident }),
|
|
137
|
-
runtimeTelemetry: {
|
|
138
|
-
activityRef: input.activity_ref,
|
|
139
|
-
stageId: "provider_task_run",
|
|
140
|
-
workspaceRoot: options.workspaceRoot,
|
|
141
|
-
onSnapshot: (snapshot) => input.on_stage?.({
|
|
142
|
-
stage: "provider_task_run",
|
|
143
|
-
summary: "running Codex task implementation.",
|
|
144
|
-
runtime: snapshot,
|
|
128
|
+
return openCodexTaskSession({
|
|
129
|
+
tuttiHome: options.tuttiHome,
|
|
130
|
+
projectId: options.projectId,
|
|
131
|
+
taskId: input.task.id,
|
|
132
|
+
compatibility: {
|
|
133
|
+
codexVersion: CODEX_APP_SERVER_VERSION,
|
|
134
|
+
model: options.config.default_model,
|
|
135
|
+
providerFingerprint: createProviderFingerprint({
|
|
136
|
+
providerKind: "openai",
|
|
137
|
+
...(options.config.api_base_url === undefined
|
|
138
|
+
? {}
|
|
139
|
+
: { apiBaseUrl: options.config.api_base_url }),
|
|
145
140
|
}),
|
|
146
141
|
},
|
|
147
142
|
});
|
|
148
143
|
}
|
|
149
|
-
|
|
150
|
-
|
|
144
|
+
catch {
|
|
145
|
+
throw new CodexAppServerWorkspaceWriteRunError("session_unavailable", "The durable Codex task session is unavailable.");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function runTaskTurn(input) {
|
|
149
|
+
let activeSession = input.session;
|
|
150
|
+
const invoke = () => runCodexAppServerWorkspaceWriteRun({
|
|
151
|
+
apiKey: input.options.apiKey,
|
|
152
|
+
workspaceRoot: input.options.workspaceRoot,
|
|
153
|
+
prompt: input.prompt,
|
|
154
|
+
sandboxMode: input.sandboxMode,
|
|
155
|
+
networkAccess: input.networkAccess,
|
|
156
|
+
defaultModel: input.options.config.default_model,
|
|
157
|
+
approvalContext: {
|
|
158
|
+
workspaceRoot: input.options.workspaceRoot,
|
|
159
|
+
taskId: input.pipelineInput.task.id,
|
|
160
|
+
activityRef: input.pipelineInput.activity_ref,
|
|
161
|
+
},
|
|
162
|
+
...(input.outputSchema === undefined ? {} : { outputSchema: input.outputSchema }),
|
|
163
|
+
...(input.validateOutput === undefined ? {} : { validateOutput: input.validateOutput }),
|
|
164
|
+
...(input.skillSelectionName === undefined
|
|
165
|
+
? {}
|
|
166
|
+
: { skillSelectionName: input.skillSelectionName }),
|
|
167
|
+
...(input.approvalHandler === undefined
|
|
168
|
+
? {}
|
|
169
|
+
: { approvalHandler: input.approvalHandler }),
|
|
170
|
+
...(input.options.config.api_base_url === undefined
|
|
171
|
+
? {}
|
|
172
|
+
: { apiBaseUrl: input.options.config.api_base_url }),
|
|
173
|
+
...(input.options.tuttiHome === undefined ? {} : { tuttiHome: input.options.tuttiHome }),
|
|
174
|
+
...(input.options.userSkillsRoot === undefined
|
|
175
|
+
? {}
|
|
176
|
+
: { userSkillsRoot: input.options.userSkillsRoot }),
|
|
177
|
+
...(input.options.builtInSkillsRoot === undefined
|
|
178
|
+
? {}
|
|
179
|
+
: { builtInSkillsRoot: input.options.builtInSkillsRoot }),
|
|
180
|
+
...(input.options.agentContext === undefined
|
|
181
|
+
? {}
|
|
182
|
+
: { agentContext: input.options.agentContext }),
|
|
183
|
+
...(input.options.codexCommand === undefined
|
|
184
|
+
? {}
|
|
185
|
+
: { codexCommand: input.options.codexCommand }),
|
|
186
|
+
...(input.options.requestTimeoutMs === undefined
|
|
187
|
+
? {}
|
|
188
|
+
: { requestTimeoutMs: input.options.requestTimeoutMs }),
|
|
189
|
+
...(input.options.turnTimeoutMs === undefined
|
|
190
|
+
? {}
|
|
191
|
+
: { turnTimeoutMs: input.options.turnTimeoutMs }),
|
|
192
|
+
...(input.options.baseEnv === undefined ? {} : { baseEnv: input.options.baseEnv }),
|
|
193
|
+
...(input.options.invocationCoordinator === undefined
|
|
194
|
+
? {}
|
|
195
|
+
: { invocationCoordinator: input.options.invocationCoordinator }),
|
|
196
|
+
...(input.options.onLifecycleIncident === undefined
|
|
197
|
+
? {}
|
|
198
|
+
: { onLifecycleIncident: input.options.onLifecycleIncident }),
|
|
199
|
+
...(activeSession === undefined
|
|
200
|
+
? {}
|
|
201
|
+
: {
|
|
202
|
+
session: {
|
|
203
|
+
codexHome: activeSession.codexHome,
|
|
204
|
+
...(activeSession.metadata.thread_id === undefined
|
|
205
|
+
? {}
|
|
206
|
+
: { threadId: activeSession.metadata.thread_id }),
|
|
207
|
+
...(activeSession.metadata.lifecycle === "archived" ? { unarchive: true } : {}),
|
|
208
|
+
onThreadReady: (threadId) => {
|
|
209
|
+
const updated = recordCodexTaskSessionThread(activeSession, threadId);
|
|
210
|
+
activeSession = updated;
|
|
211
|
+
input.setSession(updated);
|
|
212
|
+
},
|
|
213
|
+
...(input.archiveAfterTurn === undefined
|
|
214
|
+
? {}
|
|
215
|
+
: {
|
|
216
|
+
archiveAfterTurn: input.archiveAfterTurn,
|
|
217
|
+
onThreadArchived: () => {
|
|
218
|
+
const updated = markCodexTaskSessionArchived(activeSession);
|
|
219
|
+
activeSession = updated;
|
|
220
|
+
input.setSession(updated);
|
|
221
|
+
},
|
|
222
|
+
onThreadArchiveFailure: () => {
|
|
223
|
+
recordCodexSessionArchiveFailure(activeSession);
|
|
224
|
+
},
|
|
225
|
+
}),
|
|
226
|
+
},
|
|
227
|
+
}),
|
|
228
|
+
runtimeTelemetry: {
|
|
229
|
+
activityRef: input.pipelineInput.activity_ref,
|
|
230
|
+
stageId: `provider_task_${input.phase}`,
|
|
231
|
+
workspaceRoot: input.options.workspaceRoot,
|
|
232
|
+
onSnapshot: (snapshot) => input.pipelineInput.on_stage?.({
|
|
233
|
+
stage: `provider_task_${input.phase}`,
|
|
234
|
+
summary: input.phase === "execute"
|
|
235
|
+
? "running Codex task implementation."
|
|
236
|
+
: input.phase === "finalize"
|
|
237
|
+
? "preparing the task result."
|
|
238
|
+
: "recovering the task result.",
|
|
239
|
+
runtime: snapshot,
|
|
240
|
+
}),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
let result;
|
|
244
|
+
try {
|
|
245
|
+
result = await invoke();
|
|
151
246
|
}
|
|
152
|
-
|
|
153
|
-
|
|
247
|
+
catch (error) {
|
|
248
|
+
const canRetryBeforeTurn = error instanceof CodexAppServerWorkspaceWriteRunError &&
|
|
249
|
+
error.details.turn_start_state === "not_started" &&
|
|
250
|
+
(error.code === "app_server_protocol_error" || error.code === "thread_start_failed");
|
|
251
|
+
if (!canRetryBeforeTurn) {
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
input.pipelineInput.on_stage?.({
|
|
255
|
+
stage: "provider_retry",
|
|
256
|
+
summary: "retrying the Codex connection before the task turn started.",
|
|
257
|
+
});
|
|
258
|
+
result = await invoke();
|
|
259
|
+
}
|
|
260
|
+
if (input.options.recordUsage !== undefined && result.usage !== undefined) {
|
|
261
|
+
input.options.recordUsage({
|
|
154
262
|
provider: "openai",
|
|
155
|
-
source: usageSource(input),
|
|
263
|
+
source: usageSource(input.pipelineInput, input.phase),
|
|
156
264
|
model: result.model,
|
|
157
265
|
usage: result.usage,
|
|
158
|
-
activity_ref: input.activity_ref,
|
|
159
|
-
task_id: input.task.id,
|
|
160
|
-
run_lineage_kind: input.run_lineage.kind,
|
|
266
|
+
activity_ref: input.pipelineInput.activity_ref,
|
|
267
|
+
task_id: input.pipelineInput.task.id,
|
|
268
|
+
run_lineage_kind: input.pipelineInput.run_lineage.kind,
|
|
161
269
|
});
|
|
162
270
|
}
|
|
163
|
-
return
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
function outputRecoveryEvidence(error) {
|
|
274
|
+
return {
|
|
275
|
+
previous_agent_output: error.details.agent_output ?? "",
|
|
276
|
+
provider_output_failure: {
|
|
277
|
+
kind: error.code === "output_parse_failed"
|
|
278
|
+
? "malformed_or_missing_json"
|
|
279
|
+
: "schema_mismatch",
|
|
280
|
+
diagnostic: error.message,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function taskResultEndsCurrentLineage(output) {
|
|
285
|
+
return !("needs_human" in output.result);
|
|
286
|
+
}
|
|
287
|
+
function gateRecoveryEvidence(input) {
|
|
288
|
+
const context = input.correction_context;
|
|
289
|
+
return {
|
|
290
|
+
previous_provider_result: context.previous_provider_result,
|
|
291
|
+
gate_failure: {
|
|
292
|
+
summary: context.summary,
|
|
293
|
+
reason_code: context.reason_code,
|
|
294
|
+
guidance: context.guidance,
|
|
295
|
+
diagnostic: context.diagnostic,
|
|
296
|
+
promotion_failure: context.promotion_failure,
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function recoveryEvidenceForRun(input) {
|
|
301
|
+
if (input.pipelineInput.run_lineage.kind !== "retry" ||
|
|
302
|
+
input.session === undefined) {
|
|
303
|
+
return input.current;
|
|
304
|
+
}
|
|
305
|
+
const persisted = readCodexSessionRecoveryEvidence(input.session);
|
|
306
|
+
if (persisted === null ||
|
|
307
|
+
persisted.owner_activity_ref !== input.pipelineInput.run_lineage.parent_activity_ref) {
|
|
308
|
+
return input.current;
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
...input.current,
|
|
312
|
+
previous_run_recovery_evidence: persisted.evidence,
|
|
313
|
+
previous_run_recovery_evidence_truncated: persisted.truncated,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function persistRecoveryEvidence(input) {
|
|
317
|
+
if (input.session === undefined) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
writeCodexSessionRecoveryEvidence({
|
|
321
|
+
session: input.session,
|
|
322
|
+
ownerActivityRef: input.pipelineInput.activity_ref,
|
|
323
|
+
evidence: input.evidence,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
export async function runWorkspaceWriteTask(options, input) {
|
|
327
|
+
let session = openTaskSession(options, input);
|
|
328
|
+
const releaseSessionLock = session === undefined ? undefined : acquireCodexTaskSessionLock(session);
|
|
329
|
+
const setSession = (updated) => {
|
|
330
|
+
session = updated;
|
|
331
|
+
};
|
|
332
|
+
const approvalHandler = createApprovalHandler(options, input);
|
|
333
|
+
const skillSelectionName = input.run_lineage.kind === "continuation" ? "follow_up_run" : "workspace_write_run";
|
|
334
|
+
try {
|
|
335
|
+
let structuredResult;
|
|
336
|
+
if (input.correction_context !== undefined) {
|
|
337
|
+
const recovery = recoveryEvidenceForRun({
|
|
338
|
+
pipelineInput: input,
|
|
339
|
+
session,
|
|
340
|
+
current: gateRecoveryEvidence(input),
|
|
341
|
+
});
|
|
342
|
+
persistRecoveryEvidence({ pipelineInput: input, session, evidence: recovery });
|
|
343
|
+
structuredResult = await runTaskTurn({
|
|
344
|
+
options,
|
|
345
|
+
pipelineInput: input,
|
|
346
|
+
prompt: createRecoveryPrompt({
|
|
347
|
+
options,
|
|
348
|
+
pipelineInput: input,
|
|
349
|
+
session,
|
|
350
|
+
recovery,
|
|
351
|
+
}),
|
|
352
|
+
phase: "recovery",
|
|
353
|
+
sandboxMode: "workspace-write",
|
|
354
|
+
networkAccess: true,
|
|
355
|
+
session,
|
|
356
|
+
setSession,
|
|
357
|
+
skillSelectionName,
|
|
358
|
+
...(approvalHandler === undefined ? {} : { approvalHandler }),
|
|
359
|
+
outputSchema: TASK_RUN_OUTPUT_SCHEMA,
|
|
360
|
+
validateOutput: (value) => isInitialTaskRunOutput(value),
|
|
361
|
+
archiveAfterTurn: taskResultEndsCurrentLineage,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
await runTaskTurn({
|
|
366
|
+
options,
|
|
367
|
+
pipelineInput: input,
|
|
368
|
+
prompt: createExecutePrompt(options, input, session),
|
|
369
|
+
phase: "execute",
|
|
370
|
+
sandboxMode: "workspace-write",
|
|
371
|
+
networkAccess: true,
|
|
372
|
+
session,
|
|
373
|
+
setSession,
|
|
374
|
+
skillSelectionName,
|
|
375
|
+
...(approvalHandler === undefined ? {} : { approvalHandler }),
|
|
376
|
+
});
|
|
377
|
+
try {
|
|
378
|
+
structuredResult = await runTaskTurn({
|
|
379
|
+
options,
|
|
380
|
+
pipelineInput: input,
|
|
381
|
+
prompt: createFinalizePrompt(options),
|
|
382
|
+
phase: "finalize",
|
|
383
|
+
sandboxMode: "read-only",
|
|
384
|
+
networkAccess: false,
|
|
385
|
+
session,
|
|
386
|
+
setSession,
|
|
387
|
+
outputSchema: TASK_RUN_OUTPUT_SCHEMA,
|
|
388
|
+
validateOutput: (value) => isInitialTaskRunOutput(value),
|
|
389
|
+
archiveAfterTurn: taskResultEndsCurrentLineage,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
if (!(error instanceof CodexAppServerWorkspaceWriteRunError) ||
|
|
394
|
+
(error.code !== "output_parse_failed" && error.code !== "output_validation_failed")) {
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
const recovery = outputRecoveryEvidence(error);
|
|
398
|
+
persistRecoveryEvidence({ pipelineInput: input, session, evidence: recovery });
|
|
399
|
+
structuredResult = await runTaskTurn({
|
|
400
|
+
options,
|
|
401
|
+
pipelineInput: input,
|
|
402
|
+
prompt: createRecoveryPrompt({
|
|
403
|
+
options,
|
|
404
|
+
pipelineInput: input,
|
|
405
|
+
session,
|
|
406
|
+
recovery,
|
|
407
|
+
}),
|
|
408
|
+
phase: "recovery",
|
|
409
|
+
sandboxMode: "read-only",
|
|
410
|
+
networkAccess: false,
|
|
411
|
+
session,
|
|
412
|
+
setSession,
|
|
413
|
+
outputSchema: TASK_RUN_OUTPUT_SCHEMA,
|
|
414
|
+
validateOutput: (value) => isInitialTaskRunOutput(value),
|
|
415
|
+
archiveAfterTurn: taskResultEndsCurrentLineage,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return normalizeTaskRunOutput(structuredResult.output);
|
|
420
|
+
}
|
|
421
|
+
finally {
|
|
422
|
+
options.approvalManager?.clearPending();
|
|
423
|
+
releaseSessionLock?.();
|
|
424
|
+
}
|
|
164
425
|
}
|
|
165
426
|
//# sourceMappingURL=task-run-invocation.js.map
|