@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.
Files changed (66) hide show
  1. package/dist/collaboration-state/index.d.ts +2 -0
  2. package/dist/collaboration-state/index.js +1 -0
  3. package/dist/collaboration-state/messages.js +15 -0
  4. package/dist/collaboration-state/run-recording.js +18 -1
  5. package/dist/collaboration-state/run-results.js +6 -0
  6. package/dist/collaboration-state/task-retry.d.ts +16 -0
  7. package/dist/collaboration-state/task-retry.js +134 -0
  8. package/dist/control-plane/command-recovery.js +9 -1
  9. package/dist/control-plane/index.d.ts +3 -2
  10. package/dist/control-plane/index.js +14 -0
  11. package/dist/control-plane/run-retry.d.ts +19 -0
  12. package/dist/control-plane/run-retry.js +103 -0
  13. package/dist/control-plane/run-start.d.ts +2 -1
  14. package/dist/control-plane/run-start.js +3 -0
  15. package/dist/control-plane/types.d.ts +12 -0
  16. package/dist/control-plane/workflows/openai.d.ts +1 -0
  17. package/dist/control-plane/workflows/openai.js +86 -15
  18. package/dist/control-plane/worklist-terminal-feedback.js +3 -0
  19. package/dist/prompt-templates/index.d.ts +1 -1
  20. package/dist/prompt-templates/index.js +4 -3
  21. package/dist/providers/openai/app-server/durable-session-smoke-cli.d.ts +2 -0
  22. package/dist/providers/openai/app-server/durable-session-smoke-cli.js +229 -0
  23. package/dist/providers/openai/app-server/read-only-procedure.d.ts +10 -0
  24. package/dist/providers/openai/app-server/read-only-procedure.js +42 -13
  25. package/dist/providers/openai/app-server/session-store.d.ts +74 -0
  26. package/dist/providers/openai/app-server/session-store.js +305 -0
  27. package/dist/providers/openai/app-server/workspace-write-run.d.ts +17 -4
  28. package/dist/providers/openai/app-server/workspace-write-run.js +92 -28
  29. package/dist/providers/openai/codex-app-server.d.ts +1 -0
  30. package/dist/providers/openai/codex-app-server.js +2 -0
  31. package/dist/run-pipeline/openai.d.ts +1 -0
  32. package/dist/run-pipeline/openai.js +15 -43
  33. package/dist/run-pipeline/task-run-invocation.js +354 -93
  34. package/dist/server-shell/http/routes/project-api/execution-routes.js +32 -2
  35. package/dist/server-shell/http/routes/project-api/invalidations.d.ts +2 -1
  36. package/dist/server-shell/http/routes/project-api/invalidations.js +13 -0
  37. package/dist/server-shell/http/routes/project-api/openapi-execution-routes.d.ts +21 -0
  38. package/dist/server-shell/http/routes/project-api/openapi-execution-routes.js +12 -1
  39. package/dist/server-shell/http/routes/project-api/openapi.d.ts +7 -0
  40. package/node_modules/@tutti/shared/dist/schemas/api/clarifications.d.ts +1 -0
  41. package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +1 -1
  42. package/node_modules/@tutti/shared/dist/schemas/api/index.js +1 -1
  43. package/node_modules/@tutti/shared/dist/schemas/api/messages.d.ts +5 -0
  44. package/node_modules/@tutti/shared/dist/schemas/api/messages.js +1 -0
  45. package/node_modules/@tutti/shared/dist/schemas/api/runtime-events.d.ts +1 -0
  46. package/node_modules/@tutti/shared/dist/schemas/api/types.d.ts +4 -1
  47. package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.d.ts +1 -0
  48. package/node_modules/@tutti/shared/dist/schemas/api/work-items.d.ts +4 -0
  49. package/node_modules/@tutti/shared/dist/schemas/api/work-items.js +3 -0
  50. package/node_modules/@tutti/shared/dist/schemas/api/workspace-commands.d.ts +33 -0
  51. package/node_modules/@tutti/shared/dist/schemas/api/workspace-commands.js +35 -1
  52. package/package.json +1 -1
  53. package/prompts/runs/README.md +11 -16
  54. package/prompts/runs/task-execute.md +86 -0
  55. package/prompts/runs/task-finalize.md +11 -0
  56. package/prompts/runs/task-recovery.md +15 -0
  57. package/prompts/runs/task-result-contract.md +64 -0
  58. package/web/assets/{homepage-motion-scene-CLTkixIk.js → homepage-motion-scene-BFo4r4nz.js} +1 -1
  59. package/web/assets/index-CNIl4TF8.js +69 -0
  60. package/web/assets/index-quWLFJgD.css +1 -0
  61. package/web/index.html +2 -2
  62. package/prompts/runs/task-continuation.md +0 -201
  63. package/prompts/runs/task-retry.md +0 -209
  64. package/prompts/runs/task-run.md +0 -188
  65. package/web/assets/index-BTNYElB-.js +0 -69
  66. package/web/assets/index-Deu_eF5K.css +0 -1
@@ -272,6 +272,9 @@ export function emitTaskFailedResultMessage(input) {
272
272
  : { latest_run_result_id: item.latest_run_result_id }),
273
273
  ...(input.activityRef === undefined ? {} : { activity_ref: input.activityRef }),
274
274
  ...(errorCode === undefined ? {} : { error_code: errorCode }),
275
+ ...(entry.detail?.detail.result.retry_action === undefined
276
+ ? {}
277
+ : { retry_action: entry.detail.detail.result.retry_action }),
275
278
  };
276
279
  return publishTaskResultMessage(input, result);
277
280
  }
@@ -1,4 +1,4 @@
1
- export type PromptTemplateKey = "procedures.project_context_bootstrap" | "procedures.context_sync" | "procedures.project_brief_refresh" | "procedures.scratchpad_refresh" | "procedures.task_compile" | "procedures.reference_file_summary" | "procedures.follow_up_check" | "runs.task_run" | "runs.task_continuation" | "runs.task_retry" | "artifacts.applicability" | "chat.assistant" | "codex.no_write_smoke";
1
+ export type PromptTemplateKey = "procedures.project_context_bootstrap" | "procedures.context_sync" | "procedures.project_brief_refresh" | "procedures.scratchpad_refresh" | "procedures.task_compile" | "procedures.reference_file_summary" | "procedures.follow_up_check" | "runs.task_execute" | "runs.task_finalize" | "runs.task_recovery" | "runs.task_result_contract" | "artifacts.applicability" | "chat.assistant" | "codex.no_write_smoke";
2
2
  export type RenderPromptTemplateOptions = {
3
3
  templateKey: PromptTemplateKey;
4
4
  variables?: Record<string, unknown>;
@@ -19,9 +19,10 @@ const TEMPLATE_REGISTRY = {
19
19
  "procedures.task_compile": "procedures/task-compile.md",
20
20
  "procedures.reference_file_summary": "procedures/reference-file-summary.md",
21
21
  "procedures.follow_up_check": "procedures/follow-up-check.md",
22
- "runs.task_run": "runs/task-run.md",
23
- "runs.task_continuation": "runs/task-continuation.md",
24
- "runs.task_retry": "runs/task-retry.md",
22
+ "runs.task_execute": "runs/task-execute.md",
23
+ "runs.task_finalize": "runs/task-finalize.md",
24
+ "runs.task_recovery": "runs/task-recovery.md",
25
+ "runs.task_result_contract": "runs/task-result-contract.md",
25
26
  "artifacts.applicability": "artifacts/applicability.md",
26
27
  "chat.assistant": "chat-assistant.md",
27
28
  "codex.no_write_smoke": "codex/no-write-smoke.md",
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=durable-session-smoke-cli.d.ts.map
@@ -0,0 +1,229 @@
1
+ import { createHash } from "node:crypto";
2
+ import { execFileSync } from "node:child_process";
3
+ import { existsSync, lstatSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, } from "node:fs";
4
+ import { homedir, tmpdir } from "node:os";
5
+ import { join, relative } from "node:path";
6
+ import { createProjectId, createTaskId } from "@tutti/shared/ids";
7
+ import { CODEX_APP_SERVER_VERSION } from "../codex-app-server.js";
8
+ import { DEFAULT_OPENAI_MODEL } from "../model-config.js";
9
+ import { runCodexAppServerReadOnlyProcedure } from "./read-only-procedure.js";
10
+ import { resolveCodexAppServerSmokeProviderInput } from "./smoke-provider-input.js";
11
+ import { acquireCodexTaskSessionLock, createProviderFingerprint, markCodexTaskSessionArchived, openCodexTaskSession, recordCodexTaskSessionThread, } from "./session-store.js";
12
+ import { runCodexAppServerWorkspaceWriteRun } from "./workspace-write-run.js";
13
+ const WRITE_SENTINEL = "TUTTI_DURABLE_WRITE";
14
+ const RESULT_SCHEMA = {
15
+ type: "object",
16
+ additionalProperties: false,
17
+ properties: {
18
+ sentinel: { type: "string", enum: [WRITE_SENTINEL] },
19
+ file_present: { type: "boolean" },
20
+ },
21
+ required: ["sentinel", "file_present"],
22
+ };
23
+ function isResult(value) {
24
+ return (typeof value === "object" &&
25
+ value !== null &&
26
+ !Array.isArray(value) &&
27
+ value.sentinel === WRITE_SENTINEL &&
28
+ value.file_present === true);
29
+ }
30
+ function mode(path) {
31
+ return statSync(path).mode & 0o777;
32
+ }
33
+ function requireMode(path, expected) {
34
+ const actual = mode(path);
35
+ if (actual !== expected) {
36
+ throw new Error(`Durable session path has mode ${actual.toString(8)}; expected ${expected.toString(8)}.`);
37
+ }
38
+ }
39
+ function fingerprintPath(path) {
40
+ if (!existsSync(path)) {
41
+ return "missing";
42
+ }
43
+ const hash = createHash("sha256");
44
+ const visit = (current) => {
45
+ const entry = lstatSync(current);
46
+ hash.update(JSON.stringify({
47
+ path: relative(path, current),
48
+ mode: entry.mode & 0o777,
49
+ size: entry.size,
50
+ modified_ms: entry.mtimeMs,
51
+ type: entry.isDirectory() ? "directory" : entry.isSymbolicLink() ? "symlink" : "file",
52
+ }));
53
+ if (!entry.isDirectory()) {
54
+ return;
55
+ }
56
+ for (const child of readdirSync(current).sort()) {
57
+ visit(join(current, child));
58
+ }
59
+ };
60
+ visit(path);
61
+ return hash.digest("hex");
62
+ }
63
+ function userCodexConfigurationFingerprint() {
64
+ const root = join(homedir(), ".codex");
65
+ const hash = createHash("sha256");
66
+ for (const relativePath of ["auth.json", "config.toml", "plugins", "skills"]) {
67
+ hash.update(relativePath);
68
+ hash.update(fingerprintPath(join(root, relativePath)));
69
+ }
70
+ return hash.digest("hex");
71
+ }
72
+ async function withSessionLock(session, run) {
73
+ const release = acquireCodexTaskSessionLock(session);
74
+ try {
75
+ return await run();
76
+ }
77
+ finally {
78
+ release();
79
+ }
80
+ }
81
+ async function main() {
82
+ const provider = resolveCodexAppServerSmokeProviderInput();
83
+ const model = provider.defaultModel ?? DEFAULT_OPENAI_MODEL;
84
+ const smokeRoot = mkdtempSync(join(tmpdir(), "tutti-codex-durable-session-smoke-"));
85
+ const tuttiHome = join(smokeRoot, "tutti-home");
86
+ const workspaceRoot = join(smokeRoot, "repo");
87
+ const projectId = createProjectId();
88
+ const taskId = createTaskId();
89
+ const userCodexBefore = userCodexConfigurationFingerprint();
90
+ let session = openCodexTaskSession({
91
+ tuttiHome,
92
+ projectId,
93
+ taskId,
94
+ compatibility: {
95
+ codexVersion: CODEX_APP_SERVER_VERSION,
96
+ model,
97
+ providerFingerprint: createProviderFingerprint({
98
+ providerKind: "openai",
99
+ ...(provider.apiBaseUrl === undefined ? {} : { apiBaseUrl: provider.apiBaseUrl }),
100
+ }),
101
+ },
102
+ });
103
+ try {
104
+ execFileSync("git", ["init", workspaceRoot], { stdio: "ignore" });
105
+ execFileSync("git", [
106
+ "-C",
107
+ workspaceRoot,
108
+ "config",
109
+ "user.email",
110
+ "tutti-smoke@example.invalid",
111
+ ]);
112
+ execFileSync("git", ["-C", workspaceRoot, "config", "user.name", "Tutti Smoke"]);
113
+ const common = {
114
+ apiKey: provider.apiKey,
115
+ ...(provider.apiBaseUrl === undefined ? {} : { apiBaseUrl: provider.apiBaseUrl }),
116
+ defaultModel: model,
117
+ workspaceRoot,
118
+ networkAccess: false,
119
+ strictLeakCheck: true,
120
+ };
121
+ const execute = await withSessionLock(session, async () => await runCodexAppServerWorkspaceWriteRun({
122
+ ...common,
123
+ approvalPolicy: "never",
124
+ prompt: `Create durable-smoke.txt in the workspace root with exactly one line: ${WRITE_SENTINEL}. ` +
125
+ "Do not modify any other file. Then briefly report completion.",
126
+ session: {
127
+ codexHome: session.codexHome,
128
+ onThreadReady: (threadId) => {
129
+ session = recordCodexTaskSessionThread(session, threadId);
130
+ },
131
+ },
132
+ }));
133
+ if (readFileSync(join(workspaceRoot, "durable-smoke.txt"), "utf8") !== `${WRITE_SENTINEL}\n`) {
134
+ throw new Error("Workspace-write turn did not create the exact durable smoke fixture.");
135
+ }
136
+ const threadId = session.metadata.thread_id;
137
+ if (threadId === undefined) {
138
+ throw new Error("Workspace-write turn did not persist a durable thread id.");
139
+ }
140
+ const finalize = await withSessionLock(session, async () => await runCodexAppServerReadOnlyProcedure({
141
+ ...common,
142
+ prompt: "Read durable-smoke.txt and return the supplied JSON shape. " +
143
+ `Use sentinel ${WRITE_SENTINEL} and set file_present to true.`,
144
+ outputSchema: RESULT_SCHEMA,
145
+ validateOutput: isResult,
146
+ session: {
147
+ codexHome: session.codexHome,
148
+ threadId,
149
+ archiveAfterTurn: () => true,
150
+ onThreadArchived: () => {
151
+ session = markCodexTaskSessionArchived(session);
152
+ },
153
+ },
154
+ }));
155
+ if (session.metadata.lifecycle !== "archived") {
156
+ throw new Error("Durable smoke thread was not archived after the read-only turn.");
157
+ }
158
+ const recovery = await withSessionLock(session, async () => await runCodexAppServerReadOnlyProcedure({
159
+ ...common,
160
+ prompt: "After resuming this archived thread, read durable-smoke.txt and return the supplied " +
161
+ `JSON shape with sentinel ${WRITE_SENTINEL} and file_present true.`,
162
+ outputSchema: RESULT_SCHEMA,
163
+ validateOutput: isResult,
164
+ session: {
165
+ codexHome: session.codexHome,
166
+ threadId,
167
+ unarchive: true,
168
+ onThreadReady: (threadId) => {
169
+ session = recordCodexTaskSessionThread(session, threadId);
170
+ },
171
+ archiveAfterTurn: () => true,
172
+ onThreadArchived: () => {
173
+ session = markCodexTaskSessionArchived(session);
174
+ },
175
+ },
176
+ }));
177
+ const authPath = join(session.codexHome, "auth.json");
178
+ const sessionIndexPath = join(tuttiHome, "projects", projectId, "codex-sessions", "task-index", `${taskId}.json`);
179
+ requireMode(session.root, 0o700);
180
+ requireMode(session.codexHome, 0o700);
181
+ requireMode(session.metadataPath, 0o600);
182
+ requireMode(sessionIndexPath, 0o600);
183
+ requireMode(authPath, 0o600);
184
+ const codexHomeEntries = readdirSync(session.codexHome);
185
+ const repoSessionPaths = readdirSync(workspaceRoot).filter((entry) => entry === ".codex" || entry === "codex-sessions" || entry === "auth.json");
186
+ if (repoSessionPaths.length > 0) {
187
+ throw new Error("Durable session state was written into the target repository.");
188
+ }
189
+ if (userCodexConfigurationFingerprint() !== userCodexBefore) {
190
+ throw new Error("The durable smoke changed user-level Codex configuration.");
191
+ }
192
+ if (execute.thread_id !== finalize.thread_id || finalize.thread_id !== recovery.thread_id) {
193
+ throw new Error("Durable smoke turns did not resume the same app-server thread.");
194
+ }
195
+ console.log(JSON.stringify({
196
+ provider_source: provider.source,
197
+ status: "passed",
198
+ codex_version: CODEX_APP_SERVER_VERSION,
199
+ model,
200
+ same_thread_across_processes: true,
201
+ per_turn_output_schema: true,
202
+ workspace_write_then_read_only: true,
203
+ archive_then_unarchive: true,
204
+ final_lifecycle: session.metadata.lifecycle,
205
+ private_session_root_mode: mode(session.root).toString(8),
206
+ private_codex_home_mode: mode(session.codexHome).toString(8),
207
+ private_metadata_mode: mode(session.metadataPath).toString(8),
208
+ private_auth_json_mode: mode(authPath).toString(8),
209
+ auth_json_in_private_home: true,
210
+ plugin_cache_in_private_home: existsSync(join(session.codexHome, ".tmp", "plugins")),
211
+ state_store_in_private_home: codexHomeEntries.some((entry) => /^state_\d+\.sqlite$/u.test(entry)),
212
+ shell_snapshots_in_private_home: existsSync(join(session.codexHome, "shell_snapshots")),
213
+ user_codex_configuration_unchanged: true,
214
+ repo_session_files: 0,
215
+ }, null, 2));
216
+ }
217
+ finally {
218
+ rmSync(smokeRoot, { recursive: true, force: true });
219
+ }
220
+ }
221
+ main().catch((error) => {
222
+ const message = error instanceof Error ? error.message : String(error);
223
+ console.error(JSON.stringify({
224
+ status: "failed",
225
+ message,
226
+ }, null, 2));
227
+ process.exitCode = 1;
228
+ });
229
+ //# sourceMappingURL=durable-session-smoke-cli.js.map
@@ -14,6 +14,15 @@ export type CodexAppServerReadOnlyProcedureInput<TOutput> = {
14
14
  prompt: string;
15
15
  outputSchema: unknown;
16
16
  validateOutput: (value: unknown) => value is TOutput;
17
+ session?: {
18
+ codexHome: string;
19
+ threadId?: string;
20
+ unarchive?: boolean;
21
+ onThreadReady?: (threadId: string) => void | Promise<void>;
22
+ archiveAfterTurn?: (output: TOutput) => boolean;
23
+ onThreadArchived?: () => void | Promise<void>;
24
+ onThreadArchiveFailure?: () => void | Promise<void>;
25
+ };
17
26
  networkAccess?: boolean;
18
27
  webSearchMode?: CodexAppServerWebSearchMode;
19
28
  skillSelectionName?: CodexAppServerSkillSelectionName;
@@ -34,6 +43,7 @@ export type CodexAppServerReadOnlyProcedureInput<TOutput> = {
34
43
  };
35
44
  export type CodexAppServerReadOnlyProcedureResult<TOutput> = {
36
45
  output: TOutput;
46
+ thread_id: string;
37
47
  model: string;
38
48
  turn_status: "completed";
39
49
  notification_count: number;
@@ -128,7 +128,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
128
128
  }
129
129
  const model = options.defaultModel ?? DEFAULT_OPENAI_MODEL;
130
130
  const tempRoot = mkdtempSync(join(tmpdir(), "tutti-codex-readonly-procedure-"));
131
- const codexHome = join(tempRoot, "codex-home");
131
+ const codexHome = options.session?.codexHome ?? join(tempRoot, "codex-home");
132
132
  mkdirSync(codexHome, { recursive: true, mode: 0o700 });
133
133
  const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
134
134
  const turnTimeoutMs = options.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS;
@@ -210,21 +210,40 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
210
210
  if (!isApiKeyAccount(loginResponse)) {
211
211
  throw new CodexAppServerReadOnlyProcedureError("auth_failed", "Codex app-server did not accept API key auth.");
212
212
  }
213
- const threadStartResponse = await client.request("thread/start", {
214
- cwd: appServerCwd,
215
- model,
216
- modelProvider,
217
- approvalPolicy: "never",
218
- sandbox: "read-only",
219
- config: buildReadOnlyThreadConfig(options),
220
- ephemeral: true,
221
- experimentalRawEvents: false,
222
- persistExtendedHistory: false,
223
- }, requestTimeoutMs);
224
- const threadId = readThreadId(threadStartResponse);
213
+ let threadId;
214
+ if (options.session?.threadId === undefined) {
215
+ const threadStartResponse = await client.request("thread/start", {
216
+ cwd: appServerCwd,
217
+ model,
218
+ modelProvider,
219
+ approvalPolicy: "never",
220
+ sandbox: "read-only",
221
+ config: buildReadOnlyThreadConfig(options),
222
+ ephemeral: options.session === undefined,
223
+ experimentalRawEvents: false,
224
+ persistExtendedHistory: false,
225
+ }, requestTimeoutMs);
226
+ threadId = readThreadId(threadStartResponse);
227
+ }
228
+ else {
229
+ if (options.session.unarchive === true) {
230
+ await client.request("thread/unarchive", { threadId: options.session.threadId }, requestTimeoutMs);
231
+ }
232
+ const threadResumeResponse = await client.request("thread/resume", {
233
+ threadId: options.session.threadId,
234
+ cwd: appServerCwd,
235
+ model,
236
+ modelProvider,
237
+ approvalPolicy: "never",
238
+ sandbox: "read-only",
239
+ config: buildReadOnlyThreadConfig(options),
240
+ }, requestTimeoutMs);
241
+ threadId = readThreadId(threadResumeResponse) ?? options.session.threadId;
242
+ }
225
243
  if (threadId === null) {
226
244
  throw new CodexAppServerReadOnlyProcedureError("thread_start_failed", "Codex app-server did not return a thread id.");
227
245
  }
246
+ await options.session?.onThreadReady?.(threadId);
228
247
  await client.request("turn/start", {
229
248
  threadId,
230
249
  input: buildCodexAppServerTurnInput({
@@ -308,8 +327,18 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
308
327
  throw new CodexAppServerReadOnlyProcedureError("secret_leak_detected", "Codex app-server output contained a sensitive runtime value.");
309
328
  }
310
329
  const usage = extractCodexAppServerTokenUsage(client.notifications);
330
+ if (options.session?.archiveAfterTurn?.(parsed) === true) {
331
+ try {
332
+ await client.request("thread/archive", { threadId }, requestTimeoutMs);
333
+ await options.session.onThreadArchived?.();
334
+ }
335
+ catch {
336
+ await options.session.onThreadArchiveFailure?.();
337
+ }
338
+ }
311
339
  procedureResult = {
312
340
  output: parsed,
341
+ thread_id: threadId,
313
342
  model,
314
343
  turn_status: "completed",
315
344
  notification_count: client.notifications.length,
@@ -0,0 +1,74 @@
1
+ import type { ActivityRef, ProjectId, TaskId } from "@tutti/shared/ids";
2
+ export type CodexSessionLifecycle = "active" | "archived" | "incompatible";
3
+ export type CodexSessionKind = "formal_task_run" | "task_compile" | "project_context_bootstrap" | "context_sync";
4
+ export type CodexSessionMetadata = {
5
+ version: 1;
6
+ session_ref: string;
7
+ kind: CodexSessionKind;
8
+ owner_ref: string;
9
+ thread_id?: string;
10
+ codex_version: string;
11
+ model: string;
12
+ provider_fingerprint: string;
13
+ lifecycle: CodexSessionLifecycle;
14
+ created_at: string;
15
+ last_used_at: string;
16
+ };
17
+ export type CodexDurableSession = {
18
+ root: string;
19
+ codexHome: string;
20
+ metadataPath: string;
21
+ metadata: CodexSessionMetadata;
22
+ disposition: "created" | "resumed" | "replaced_incompatible";
23
+ };
24
+ export type CodexTaskSession = CodexDurableSession;
25
+ export type CodexTaskSessionCompatibility = {
26
+ codexVersion: string;
27
+ model: string;
28
+ providerFingerprint: string;
29
+ };
30
+ export declare function createProviderFingerprint(input: {
31
+ providerKind: "openai";
32
+ apiBaseUrl?: string;
33
+ }): string;
34
+ export declare function openCodexDurableSession(input: {
35
+ tuttiHome: string;
36
+ projectId: ProjectId;
37
+ kind: CodexSessionKind;
38
+ ownerRef: string;
39
+ compatibility: CodexTaskSessionCompatibility;
40
+ now?: Date;
41
+ }): CodexDurableSession;
42
+ export declare function openCodexTaskSession(input: {
43
+ tuttiHome: string;
44
+ projectId: ProjectId;
45
+ taskId: TaskId;
46
+ compatibility: CodexTaskSessionCompatibility;
47
+ now?: Date;
48
+ }): CodexTaskSession;
49
+ export type CodexSessionRecoveryEvidence = {
50
+ version: 1;
51
+ owner_activity_ref: ActivityRef;
52
+ updated_at: string;
53
+ truncated: boolean;
54
+ evidence: Record<string, unknown>;
55
+ };
56
+ export declare function writeCodexSessionRecoveryEvidence(input: {
57
+ session: CodexDurableSession;
58
+ ownerActivityRef: ActivityRef;
59
+ evidence: Record<string, unknown>;
60
+ now?: Date;
61
+ }): CodexSessionRecoveryEvidence;
62
+ export declare function readCodexSessionRecoveryEvidence(session: CodexDurableSession): CodexSessionRecoveryEvidence | null;
63
+ export declare function updateCodexTaskSession(session: CodexDurableSession, update: (metadata: CodexSessionMetadata) => CodexSessionMetadata): CodexDurableSession;
64
+ export declare function recordCodexTaskSessionThread(session: CodexDurableSession, threadId: string, now?: Date): CodexDurableSession;
65
+ export declare function markCodexTaskSessionArchived(session: CodexDurableSession, now?: Date): CodexDurableSession;
66
+ export type CodexSessionArchiveIncident = {
67
+ version: 1;
68
+ kind: "thread_archive_failed";
69
+ occurred_at: string;
70
+ };
71
+ export declare function recordCodexSessionArchiveFailure(session: CodexDurableSession, now?: Date): CodexSessionArchiveIncident;
72
+ export declare function readCodexSessionArchiveIncident(session: CodexDurableSession): CodexSessionArchiveIncident | null;
73
+ export declare function acquireCodexTaskSessionLock(session: CodexDurableSession): () => void;
74
+ //# sourceMappingURL=session-store.d.ts.map