@xfey/tutti 0.1.118 → 0.1.120

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 (110) hide show
  1. package/dist/approvals/decision-command.d.ts +2 -2
  2. package/dist/collaboration-state/clarification-snapshot.d.ts +9 -9
  3. package/dist/collaboration-state/task-compile-context.d.ts +1 -1
  4. package/dist/control-plane/workflows/openai.d.ts +3 -4
  5. package/dist/control-plane/workflows/openai.js +3 -2
  6. package/dist/providers/openai/app-server/invocation-coordinator.d.ts +1 -0
  7. package/dist/providers/openai/app-server/invocation-coordinator.js +4 -1
  8. package/dist/providers/openai/app-server/invocation-deadline.d.ts +3 -0
  9. package/dist/providers/openai/app-server/invocation-deadline.js +15 -0
  10. package/dist/providers/openai/app-server/read-only-procedure.d.ts +4 -2
  11. package/dist/providers/openai/app-server/read-only-procedure.js +16 -0
  12. package/dist/providers/openai/app-server/workspace-write-run.d.ts +4 -2
  13. package/dist/providers/openai/app-server/workspace-write-run.js +16 -0
  14. package/dist/providers/openai/chat-assistant.d.ts +3 -4
  15. package/dist/providers/openai/chat-assistant.js +2 -1
  16. package/dist/providers/openai/execution-access.d.ts +17 -0
  17. package/dist/providers/openai/execution-access.js +13 -0
  18. package/dist/providers/openai/sdk-procedure-runner.d.ts +2 -2
  19. package/dist/providers/openai/sdk-procedure-runner.js +17 -0
  20. package/dist/run-pipeline/artifact-applicability.d.ts +3 -4
  21. package/dist/run-pipeline/artifact-applicability.js +2 -1
  22. package/dist/run-pipeline/openai.d.ts +3 -4
  23. package/dist/run-pipeline/openai.js +3 -2
  24. package/dist/run-pipeline/task-run-invocation.js +6 -12
  25. package/dist/server-shell/cli/cli.js +3 -0
  26. package/dist/server-shell/cli/host-lifecycle.js +1 -0
  27. package/dist/server-shell/cli/host-server-runtime.d.ts +1 -0
  28. package/dist/server-shell/cli/host-server-runtime.js +83 -34
  29. package/dist/server-shell/cli/launch-command.js +1 -1
  30. package/dist/server-shell/cli/launch.d.ts +7 -3
  31. package/dist/server-shell/cli/launch.js +23 -4
  32. package/dist/server-shell/cli/machine-local.d.ts +9 -0
  33. package/dist/server-shell/cli/machine-local.js +60 -6
  34. package/dist/server-shell/cli/project-resolver.d.ts +2 -2
  35. package/dist/server-shell/cli/project-resolver.js +6 -3
  36. package/dist/server-shell/cli/relay-registration.js +38 -1
  37. package/dist/server-shell/http/routes/local-control.d.ts +1 -1
  38. package/dist/server-shell/http/routes/local-control.js +2 -46
  39. package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.d.ts +47 -137
  40. package/dist/server-shell/http/routes/project-api/openapi.d.ts +47 -137
  41. package/dist/server-shell/http/routes/project-api/schemas.d.ts +25 -136
  42. package/dist/server-shell/http/routes/project-api/schemas.js +4 -58
  43. package/dist/server-shell/http/routes/project-api/types.d.ts +4 -0
  44. package/dist/server-shell/local-console/managed-project-routes.d.ts +9 -0
  45. package/dist/server-shell/local-console/managed-project-routes.js +120 -0
  46. package/dist/server-shell/local-console/managed-project-service.d.ts +151 -0
  47. package/dist/server-shell/local-console/managed-project-service.js +163 -0
  48. package/dist/server-shell/local-console/operation-coordinator.d.ts +1 -1
  49. package/dist/server-shell/local-console/package-update-hosts.js +2 -1
  50. package/dist/server-shell/local-console/project-service.d.ts +5 -1
  51. package/dist/server-shell/local-console/project-service.js +63 -14
  52. package/dist/server-shell/local-console/server.js +22 -2
  53. package/dist/server-shell/managed-access/execution-binding.d.ts +9 -0
  54. package/dist/server-shell/managed-access/execution-binding.js +22 -0
  55. package/dist/server-shell/managed-access/existing-project.d.ts +15 -0
  56. package/dist/server-shell/managed-access/existing-project.js +41 -0
  57. package/dist/server-shell/managed-access/model-control-client.d.ts +66 -0
  58. package/dist/server-shell/managed-access/model-control-client.js +114 -0
  59. package/dist/server-shell/managed-access/private-store.d.ts +19 -0
  60. package/dist/server-shell/managed-access/private-store.js +275 -0
  61. package/dist/server-shell/managed-access/project-provider.d.ts +18 -0
  62. package/dist/server-shell/managed-access/project-provider.js +30 -0
  63. package/dist/server-shell/managed-access/records.d.ts +108 -0
  64. package/dist/server-shell/managed-access/records.js +73 -0
  65. package/dist/server-shell/managed-access/runners.d.ts +11 -0
  66. package/dist/server-shell/managed-access/runners.js +103 -0
  67. package/dist/server-shell/managed-access/runtime-records.d.ts +110 -0
  68. package/dist/server-shell/managed-access/runtime-records.js +65 -0
  69. package/dist/server-shell/managed-access/runtime.d.ts +48 -0
  70. package/dist/server-shell/managed-access/runtime.js +301 -0
  71. package/dist/server-shell/managed-access/state.d.ts +54 -0
  72. package/dist/server-shell/managed-access/state.js +287 -0
  73. package/dist/server-shell/managed-access/transport.d.ts +48 -0
  74. package/dist/server-shell/managed-access/transport.js +38 -0
  75. package/dist/server-shell/managed-access/workspace.d.ts +10 -0
  76. package/dist/server-shell/managed-access/workspace.js +69 -0
  77. package/dist/server-shell/smoke/e2e-client.d.ts +8 -3
  78. package/dist/server-shell/smoke/e2e-client.js +11 -11
  79. package/node_modules/@tutti/relay-client/dist/host-control.d.ts +4 -1
  80. package/node_modules/@tutti/relay-client/dist/host-control.js +26 -3
  81. package/node_modules/@tutti/relay-client/dist/index.d.ts +1 -0
  82. package/node_modules/@tutti/relay-client/dist/index.js +1 -0
  83. package/node_modules/@tutti/relay-client/dist/project-authority.d.ts +75 -0
  84. package/node_modules/@tutti/relay-client/dist/project-authority.js +140 -0
  85. package/node_modules/@tutti/shared/dist/ids/index.d.ts +7 -0
  86. package/node_modules/@tutti/shared/dist/ids/index.js +7 -0
  87. package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +1 -0
  88. package/node_modules/@tutti/shared/dist/schemas/api/index.js +1 -0
  89. package/node_modules/@tutti/shared/dist/schemas/api/managed-account.d.ts +35 -0
  90. package/node_modules/@tutti/shared/dist/schemas/api/managed-account.js +41 -0
  91. package/node_modules/@tutti/shared/dist/schemas/api/provider-config.d.ts +8 -4
  92. package/node_modules/@tutti/shared/dist/schemas/api/provider-config.js +6 -0
  93. package/node_modules/@tutti/shared/dist/schemas/api/runtime-events.d.ts +4 -0
  94. package/node_modules/@tutti/shared/dist/schemas/internal/creation-validation.d.ts +28 -0
  95. package/node_modules/@tutti/shared/dist/schemas/internal/creation-validation.js +42 -0
  96. package/node_modules/@tutti/shared/dist/schemas/internal/index.d.ts +5 -0
  97. package/node_modules/@tutti/shared/dist/schemas/internal/index.js +5 -0
  98. package/node_modules/@tutti/shared/dist/schemas/internal/managed-account.d.ts +12 -0
  99. package/node_modules/@tutti/shared/dist/schemas/internal/managed-account.js +13 -0
  100. package/node_modules/@tutti/shared/dist/schemas/internal/managed-models.d.ts +17 -0
  101. package/node_modules/@tutti/shared/dist/schemas/internal/managed-models.js +17 -0
  102. package/node_modules/@tutti/shared/dist/schemas/internal/managed-runtime.d.ts +116 -0
  103. package/node_modules/@tutti/shared/dist/schemas/internal/managed-runtime.js +87 -0
  104. package/node_modules/@tutti/shared/dist/schemas/internal/project-authority.d.ts +120 -0
  105. package/node_modules/@tutti/shared/dist/schemas/internal/project-authority.js +110 -0
  106. package/package.json +1 -1
  107. package/web/assets/{homepage-motion-scene-DYlc2u04.js → homepage-motion-scene-DyMFsYmT.js} +1 -1
  108. package/web/assets/index-DSlecDXC.js +70 -0
  109. package/web/index.html +1 -1
  110. package/web/assets/index-07ujy_xz.js +0 -70
@@ -24,9 +24,9 @@ export declare function executeApprovalDecisionCommand(input: {
24
24
  } | {
25
25
  state: "pending";
26
26
  request: {
27
+ expires_at?: string;
27
28
  task_id?: string;
28
29
  activity_ref?: string;
29
- expires_at?: string;
30
30
  reason?: string;
31
31
  file_change?: {
32
32
  paths?: string[];
@@ -68,9 +68,9 @@ export declare function executeApprovalDecisionCommand(input: {
68
68
  } | {
69
69
  state: "resolved";
70
70
  request: {
71
+ expires_at?: string;
71
72
  task_id?: string;
72
73
  activity_ref?: string;
73
- expires_at?: string;
74
74
  reason?: string;
75
75
  file_change?: {
76
76
  paths?: string[];
@@ -26,9 +26,6 @@ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, r
26
26
  target: {
27
27
  fallback_context?: {
28
28
  snapshot: {
29
- role?: string;
30
- accessible_name?: string;
31
- text_excerpt?: string;
32
29
  state?: {
33
30
  expanded?: boolean;
34
31
  selected?: boolean;
@@ -36,6 +33,9 @@ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, r
36
33
  pressed?: boolean;
37
34
  disabled?: boolean;
38
35
  };
36
+ role?: string;
37
+ accessible_name?: string;
38
+ text_excerpt?: string;
39
39
  tag_name: string;
40
40
  element_kind: "content" | "control" | "container";
41
41
  context: {
@@ -62,9 +62,6 @@ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, r
62
62
  };
63
63
  };
64
64
  snapshot: {
65
- role?: string;
66
- accessible_name?: string;
67
- text_excerpt?: string;
68
65
  state?: {
69
66
  expanded?: boolean;
70
67
  selected?: boolean;
@@ -72,6 +69,9 @@ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, r
72
69
  pressed?: boolean;
73
70
  disabled?: boolean;
74
71
  };
72
+ role?: string;
73
+ accessible_name?: string;
74
+ text_excerpt?: string;
75
75
  tag_name: string;
76
76
  element_kind: "content" | "control" | "container";
77
77
  context: {
@@ -144,7 +144,7 @@ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, r
144
144
  title: string;
145
145
  workflow_ref: string;
146
146
  tasks: {
147
- status: "failed" | "done" | "pending" | "todo" | "running";
147
+ status: "failed" | "pending" | "done" | "todo" | "running";
148
148
  task_id: string;
149
149
  title: string;
150
150
  summary: string;
@@ -162,9 +162,9 @@ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, r
162
162
  };
163
163
  reply_context?: {
164
164
  status: "resolved";
165
+ truncated: boolean;
165
166
  role: "human" | "system" | "tutti" | "operator";
166
167
  message_id: string;
167
- truncated: boolean;
168
168
  text: string;
169
169
  external_reference_sources: string[];
170
170
  } | {
@@ -210,6 +210,7 @@ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, r
210
210
  feedback_key: string;
211
211
  };
212
212
  };
213
+ created_at: string;
213
214
  scope: {
214
215
  kind: "main_chat";
215
216
  } | {
@@ -233,7 +234,6 @@ export declare function readSubmittedClarificationMessages(db: SqliteDatabase, r
233
234
  kind: "human" | "system" | "agent";
234
235
  };
235
236
  body: string;
236
- created_at: string;
237
237
  message_kind: "user_text" | "agent_text" | "system_notice" | "clarification_request" | "clarification_card";
238
238
  }[];
239
239
  //# sourceMappingURL=clarification-snapshot.d.ts.map
@@ -24,7 +24,7 @@ export declare function completeTaskCompileContext(db: SqliteDatabase, input: Co
24
24
  task_changes: string[];
25
25
  receipts: {
26
26
  compiled_tasks?: {
27
- status: "failed" | "done" | "pending" | "todo" | "running";
27
+ status: "failed" | "pending" | "done" | "todo" | "running";
28
28
  task_id: string;
29
29
  title: string;
30
30
  summary: string;
@@ -1,18 +1,17 @@
1
1
  import type { ProjectId } from "@tutti/shared/ids";
2
2
  import type { ProviderUsageRecorder } from "../../provider-usage/index.js";
3
3
  import { type OpenAiStructuredOutputClient } from "../../providers/openai/sdk-procedure-runner.js";
4
- import { type OpenAiProviderConfig } from "../../providers/openai/provider-config.js";
4
+ import { type OpenAiCredentialAccess, type OpenAiExecutionConfig } from "../../providers/openai/execution-access.js";
5
5
  import type { ProcedureWorkflowRunner, ProcedureWorkflowRunnerResolution, TaskCompilePromptInput, TaskCompileWorkflowInput } from "./types.js";
6
6
  import type { CodexAppServerAgentContextRuntime } from "../../providers/openai/app-server/skills.js";
7
7
  import type { CodexAppServerInvocationCoordinator } from "../../providers/openai/app-server/invocation-coordinator.js";
8
8
  import type { CodexAppServerLifecycleIncident } from "../../providers/openai/app-server/invocation-lifecycle.js";
9
9
  export type OpenAiProcedureWorkflowRunnerOptions = {
10
- config: OpenAiProviderConfig;
10
+ config: OpenAiExecutionConfig;
11
11
  client: OpenAiStructuredOutputClient;
12
12
  recordUsage?: ProviderUsageRecorder;
13
13
  promptsRoot?: string;
14
- readOnlyAppServer?: {
15
- apiKey: string;
14
+ readOnlyAppServer?: OpenAiCredentialAccess & {
16
15
  workspaceRoot: string;
17
16
  codexCommand?: string;
18
17
  requestTimeoutMs?: number;
@@ -6,7 +6,8 @@ import { clarificationAnswerRole } from "@tutti/shared/utils";
6
6
  import { renderPromptTemplate } from "../../prompt-templates/index.js";
7
7
  import { createOpenAiStructuredOutputClient, runOpenAiStructuredOutput, } from "../../providers/openai/sdk-procedure-runner.js";
8
8
  import { readOpenAiApiKeyCredential } from "../../providers/openai/credential-store.js";
9
- import { resolveOpenAiProviderConfig, } from "../../providers/openai/provider-config.js";
9
+ import { resolveOpenAiProviderConfig } from "../../providers/openai/provider-config.js";
10
+ import { openAiCredentialAccess, } from "../../providers/openai/execution-access.js";
10
11
  import { SCRATCHPAD_REFRESH_OUTPUT_SCHEMA, TASK_COMPILE_OUTPUT_SCHEMA, PROJECT_CONTEXT_BOOTSTRAP_OUTPUT_SCHEMA, CONTEXT_SYNC_OUTPUT_SCHEMA, PROJECT_BRIEF_REFRESH_OUTPUT_SCHEMA, FOLLOW_UP_CHECK_OUTPUT_SCHEMA, REFERENCE_FILE_SUMMARY_OUTPUT_SCHEMA, isContextSyncWorkflowOutput, isFollowUpCheckStructuredOutput, isProjectBriefRefreshWorkflowOutput, isProjectContextBootstrapWorkflowOutput, isReferenceFileSummaryStructuredOutput, isScratchpadRefreshStructuredOutput, isTaskCompileStructuredOutput, } from "./structured-output.js";
11
12
  import { runCodexAppServerReadOnlyProcedure, } from "../../providers/openai/app-server/read-only-procedure.js";
12
13
  import { CODEX_APP_SERVER_VERSION } from "../../providers/openai/codex-app-server.js";
@@ -17,7 +18,7 @@ function readOnlyAppServerOptions(options, prompt, outputSchema, skillSelectionN
17
18
  throw new Error("Codex app-server read-only Procedure adapter is not configured");
18
19
  }
19
20
  return {
20
- apiKey: options.readOnlyAppServer.apiKey,
21
+ ...openAiCredentialAccess(options.readOnlyAppServer),
21
22
  workspaceRoot: options.readOnlyAppServer.workspaceRoot,
22
23
  prompt,
23
24
  outputSchema,
@@ -1,4 +1,5 @@
1
1
  export type CodexAppServerInvocationLease = {
2
+ isClosing?: () => boolean;
2
3
  setCloser: (closer: () => Promise<unknown>) => void;
3
4
  release: () => void;
4
5
  };
@@ -48,6 +48,8 @@ export class CodexAppServerInvocationCoordinator {
48
48
  }
49
49
  async closeActive() {
50
50
  const active = this.active;
51
+ if (active !== null)
52
+ active.closing = true;
51
53
  if (active?.closer !== undefined) {
52
54
  await active.closer();
53
55
  }
@@ -59,9 +61,10 @@ export class CodexAppServerInvocationCoordinator {
59
61
  return new Promise((resolve) => this.idleWaiters.add(resolve));
60
62
  }
61
63
  activate() {
62
- const admission = { released: false };
64
+ const admission = { released: false, closing: false };
63
65
  this.active = admission;
64
66
  return {
67
+ isClosing: () => admission.closing || admission.released,
65
68
  setCloser: (closer) => {
66
69
  if (!admission.released) {
67
70
  admission.closer = closer;
@@ -0,0 +1,3 @@
1
+ import type { CodexAppServerInvocation } from "./invocation-lifecycle.js";
2
+ export declare function armInvocationDeadline(invocation: CodexAppServerInvocation, deadline: number | undefined): () => void;
3
+ //# sourceMappingURL=invocation-deadline.d.ts.map
@@ -0,0 +1,15 @@
1
+ // Managed access has an overall deadline in addition to per-RPC and per-turn
2
+ // timeouts. Close the actual process tree; do not abandon a Promise that can keep
3
+ // writing after its model credential lease has been returned.
4
+ export function armInvocationDeadline(invocation, deadline) {
5
+ if (deadline === undefined)
6
+ return () => { };
7
+ const timer = setTimeout(() => {
8
+ void invocation.close().catch(() => {
9
+ // The adapter's finally awaits the same close and records lifecycle failure.
10
+ });
11
+ }, Math.max(1, deadline - Date.now()));
12
+ timer.unref();
13
+ return () => clearTimeout(timer);
14
+ }
15
+ //# sourceMappingURL=invocation-deadline.js.map
@@ -1,3 +1,4 @@
1
+ import { type OpenAiCredentialAccess } from "../execution-access.js";
1
2
  import { type ProviderInvocationProgressObserver } from "../../invocation-progress.js";
2
3
  import { type CodexAppServerLifecycleIncident } from "./invocation-lifecycle.js";
3
4
  import { type CodexAppServerInvocationCoordinator, type CodexAppServerInvocationLease } from "./invocation-coordinator.js";
@@ -5,9 +6,8 @@ import { type CodexAppServerRuntimeTelemetryOptions } from "./runtime-telemetry.
5
6
  import { type CodexAppServerAgentContextRuntime, type CodexAppServerSkillSelectionName } from "./skills.js";
6
7
  import { type OpenAiTokenUsage } from "../token-usage.js";
7
8
  export type CodexAppServerWebSearchMode = "disabled" | "cached" | "live";
8
- export type CodexAppServerReadOnlyProcedureInput<TOutput> = {
9
+ export type CodexAppServerReadOnlyProcedureInput<TOutput> = OpenAiCredentialAccess & {
9
10
  onProgress?: ProviderInvocationProgressObserver;
10
- apiKey: string;
11
11
  apiBaseUrl?: string;
12
12
  defaultModel?: string;
13
13
  workspaceRoot: string;
@@ -44,6 +44,8 @@ export type CodexAppServerReadOnlyProcedureInput<TOutput> = {
44
44
  invocationCoordinator?: CodexAppServerInvocationCoordinator;
45
45
  /** @internal Set only by the coordinator wrapper. */
46
46
  invocationLease?: CodexAppServerInvocationLease;
47
+ /** @internal Overall deadline supplied by the managed credential wrapper. */
48
+ invocationDeadline?: number;
47
49
  };
48
50
  export type CodexAppServerReadOnlyProcedureResult<TOutput> = {
49
51
  output: TOutput;
@@ -3,6 +3,8 @@ import { mkdirSync, mkdtempSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { dirname, join } from "node:path";
5
5
  import { discoverCodexAppServerSkills } from "./skill-discovery.js";
6
+ import { managedInvocationBudget } from "../execution-access.js";
7
+ import { armInvocationDeadline } from "./invocation-deadline.js";
6
8
  import { reportProviderInvocationProgress, } from "../../invocation-progress.js";
7
9
  import { buildCodexAppServerProcessPlan } from "../codex-app-server.js";
8
10
  import { DEFAULT_OPENAI_MODEL } from "../model-config.js";
@@ -135,6 +137,18 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
135
137
  lease.release();
136
138
  }
137
139
  }
140
+ if (options.invocationLease?.isClosing?.()) {
141
+ throw new CodexAppServerReadOnlyProcedureError("admission_unavailable", "Codex app-server admission is closed.");
142
+ }
143
+ if (options.credentialLease !== undefined) {
144
+ const { credentialLease, ...invocation } = options;
145
+ const budget = managedInvocationBudget(options.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS);
146
+ return credentialLease(budget, (apiKey) => runCodexAppServerReadOnlyProcedure({
147
+ ...invocation,
148
+ apiKey,
149
+ invocationDeadline: Date.now() + budget,
150
+ }));
151
+ }
138
152
  reportProviderInvocationProgress(options.onProgress, { stage: "provider_started" });
139
153
  let skillInputs;
140
154
  let contextRuntimeHint;
@@ -223,6 +237,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
223
237
  : { onIncident: options.onLifecycleIncident }),
224
238
  });
225
239
  options.invocationLease?.setCloser(() => invocation.close());
240
+ const disarmDeadline = armInvocationDeadline(invocation, options.invocationDeadline);
226
241
  const sensitiveValues = [
227
242
  options.apiKey,
228
243
  tempRoot,
@@ -446,6 +461,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
446
461
  }
447
462
  }
448
463
  finally {
464
+ disarmDeadline();
449
465
  runtimeObserver?.flush();
450
466
  try {
451
467
  await invocation.close();
@@ -1,4 +1,5 @@
1
1
  import type { ActivityRef, TaskId } from "@tutti/shared/ids";
2
+ import { type OpenAiCredentialAccess } from "../execution-access.js";
2
3
  import type { CodexAppServerApprovalPolicy } from "../codex-app-server.js";
3
4
  import { type CodexAppServerJsonRpcError, type CodexAppServerJsonRpcServerRequest } from "./json-rpc.js";
4
5
  import { type CodexAppServerLifecycleIncident } from "./invocation-lifecycle.js";
@@ -27,8 +28,7 @@ export type CodexAppServerApprovalHandler = (input: {
27
28
  sensitiveValues: readonly string[];
28
29
  };
29
30
  }) => Promise<CodexAppServerApprovalResponse>;
30
- export type CodexAppServerWorkspaceWriteRunInput<TOutput> = {
31
- apiKey: string;
31
+ export type CodexAppServerWorkspaceWriteRunInput<TOutput> = OpenAiCredentialAccess & {
32
32
  apiBaseUrl?: string;
33
33
  defaultModel?: string;
34
34
  workspaceRoot: string;
@@ -70,6 +70,8 @@ export type CodexAppServerWorkspaceWriteRunInput<TOutput> = {
70
70
  invocationCoordinator?: CodexAppServerInvocationCoordinator;
71
71
  /** @internal Set only by the coordinator wrapper. */
72
72
  invocationLease?: CodexAppServerInvocationLease;
73
+ /** @internal Overall deadline supplied by the managed credential wrapper. */
74
+ invocationDeadline?: number;
73
75
  };
74
76
  export type CodexAppServerWorkspaceWriteRunResult<TOutput> = {
75
77
  output: TOutput;
@@ -3,6 +3,8 @@ import { mkdirSync, mkdtempSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { dirname, join } from "node:path";
5
5
  import { discoverCodexAppServerSkills } from "./skill-discovery.js";
6
+ import { managedInvocationBudget } from "../execution-access.js";
7
+ import { armInvocationDeadline } from "./invocation-deadline.js";
6
8
  import { buildCodexAppServerProcessPlan } from "../codex-app-server.js";
7
9
  import { DEFAULT_OPENAI_MODEL } from "../model-config.js";
8
10
  import { CodexAppServerJsonRpcClient, CodexAppServerProtocolError, } from "./json-rpc.js";
@@ -170,6 +172,18 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
170
172
  lease.release();
171
173
  }
172
174
  }
175
+ if (options.invocationLease?.isClosing?.()) {
176
+ throw new CodexAppServerWorkspaceWriteRunError("admission_unavailable", "Codex app-server admission is closed.");
177
+ }
178
+ if (options.credentialLease !== undefined) {
179
+ const { credentialLease, ...invocation } = options;
180
+ const budget = managedInvocationBudget(options.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS);
181
+ return credentialLease(budget, (apiKey) => runCodexAppServerWorkspaceWriteRun({
182
+ ...invocation,
183
+ apiKey,
184
+ invocationDeadline: Date.now() + budget,
185
+ }));
186
+ }
173
187
  let skillInputs;
174
188
  let contextRuntimeHint;
175
189
  try {
@@ -251,6 +265,7 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
251
265
  : { onIncident: options.onLifecycleIncident }),
252
266
  });
253
267
  options.invocationLease?.setCloser(() => invocation.close());
268
+ const disarmDeadline = armInvocationDeadline(invocation, options.invocationDeadline);
254
269
  const sensitiveValues = [
255
270
  options.apiKey,
256
271
  tempRoot,
@@ -482,6 +497,7 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
482
497
  }
483
498
  }
484
499
  finally {
500
+ disarmDeadline();
485
501
  runtimeObserver?.flush();
486
502
  try {
487
503
  await invocation.close();
@@ -1,13 +1,12 @@
1
1
  import type { ProjectId } from "@tutti/shared/ids";
2
2
  import { type ReadOnlyChatAssistantModel } from "../../chat-assistant/index.js";
3
- import { type OpenAiProviderConfig } from "./provider-config.js";
3
+ import { type OpenAiCredentialAccess, type OpenAiExecutionConfig } from "./execution-access.js";
4
4
  import type { CodexAppServerAgentContextRuntime } from "./app-server/skills.js";
5
5
  import type { CodexAppServerInvocationCoordinator } from "./app-server/invocation-coordinator.js";
6
6
  import type { CodexAppServerLifecycleIncident } from "./app-server/invocation-lifecycle.js";
7
7
  import type { ProviderUsageRecorder } from "../../provider-usage/index.js";
8
- export type OpenAiReadOnlyChatAssistantModelOptions = {
9
- config: OpenAiProviderConfig;
10
- apiKey: string;
8
+ export type OpenAiReadOnlyChatAssistantModelOptions = OpenAiCredentialAccess & {
9
+ config: OpenAiExecutionConfig;
11
10
  workspaceRoot: string;
12
11
  tuttiHome?: string;
13
12
  userSkillsRoot?: string;
@@ -1,6 +1,7 @@
1
1
  import { CHAT_ASSISTANT_OUTPUT_SCHEMA, isChatAssistantOutput, } from "../../chat-assistant/index.js";
2
2
  import { readOpenAiApiKeyCredential } from "./credential-store.js";
3
3
  import { resolveOpenAiProviderConfig } from "./provider-config.js";
4
+ import { openAiCredentialAccess, } from "./execution-access.js";
4
5
  import { CodexAppServerReadOnlyProcedureError, runCodexAppServerReadOnlyProcedure, } from "./app-server/read-only-procedure.js";
5
6
  import { reportProviderInvocationProgress } from "../invocation-progress.js";
6
7
  import { getProjectUserSkillsRoot } from "../../skills/index.js";
@@ -9,7 +10,7 @@ export function createOpenAiReadOnlyChatAssistantModel(options) {
9
10
  async answer(input) {
10
11
  const result = await runCodexAppServerReadOnlyProcedure({
11
12
  ...(input.onProgress === undefined ? {} : { onProgress: input.onProgress }),
12
- apiKey: options.apiKey,
13
+ ...openAiCredentialAccess(options),
13
14
  workspaceRoot: options.workspaceRoot,
14
15
  prompt: input.prompt,
15
16
  outputSchema: CHAT_ASSISTANT_OUTPUT_SCHEMA,
@@ -0,0 +1,17 @@
1
+ export type OpenAiExecutionConfig = {
2
+ default_model: string;
3
+ api_base_url?: string;
4
+ organization_id?: string;
5
+ openai_project_id?: string;
6
+ };
7
+ export type OpenAiCredentialLease = <T>(maximumDurationMs: number, invoke: (apiKey: string) => Promise<T>) => Promise<T>;
8
+ export type OpenAiCredentialAccess = {
9
+ apiKey: string;
10
+ credentialLease?: never;
11
+ } | {
12
+ apiKey?: never;
13
+ credentialLease: OpenAiCredentialLease;
14
+ };
15
+ export declare function openAiCredentialAccess(access: OpenAiCredentialAccess): OpenAiCredentialAccess;
16
+ export declare function managedInvocationBudget(turnTimeoutMs: number): number;
17
+ //# sourceMappingURL=execution-access.d.ts.map
@@ -0,0 +1,13 @@
1
+ export function openAiCredentialAccess(access) {
2
+ return access.credentialLease === undefined
3
+ ? { apiKey: access.apiKey }
4
+ : { credentialLease: access.credentialLease };
5
+ }
6
+ // A bounded allowance for login/discovery/archive in addition to the business turn.
7
+ // The adapter also installs a wall-clock deadline that closes the process tree.
8
+ export function managedInvocationBudget(turnTimeoutMs) {
9
+ if (!Number.isSafeInteger(turnTimeoutMs) || turnTimeoutMs < 1 || turnTimeoutMs > 1_200_000)
10
+ throw new Error("managed_invocation_timeout_invalid");
11
+ return turnTimeoutMs + 120_000;
12
+ }
13
+ //# sourceMappingURL=execution-access.js.map
@@ -1,3 +1,4 @@
1
+ import type { OpenAiCredentialAccess } from "./execution-access.js";
1
2
  import { type OpenAiCredentialValidationDiagnostic, type OpenAiCredentialValidationFailureReason } from "./credential-validation.js";
2
3
  import { type OpenAiTokenUsage } from "./token-usage.js";
3
4
  export type OpenAiJsonSchema = {
@@ -26,8 +27,7 @@ export type OpenAiStructuredOutputClient = {
26
27
  }) => Promise<unknown>;
27
28
  };
28
29
  };
29
- export type OpenAiStructuredOutputClientInput = {
30
- apiKey: string;
30
+ export type OpenAiStructuredOutputClientInput = OpenAiCredentialAccess & {
31
31
  apiBaseUrl?: string;
32
32
  organizationId?: string;
33
33
  openAiProjectId?: string;
@@ -26,6 +26,23 @@ function isRecord(value) {
26
26
  return typeof value === "object" && value !== null && !Array.isArray(value);
27
27
  }
28
28
  export function createOpenAiStructuredOutputClient(input) {
29
+ if (input.credentialLease !== undefined) {
30
+ const { credentialLease, ...connection } = input;
31
+ return {
32
+ responses: {
33
+ create: (body, options) => {
34
+ const timeout = options?.timeout ?? DEFAULT_TIMEOUT_MS;
35
+ if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 1_200_000)
36
+ return Promise.reject(new Error("managed_invocation_timeout_invalid"));
37
+ return credentialLease(timeout, (apiKey) => createOpenAiStructuredOutputClient({ ...connection, apiKey }).responses.create(body, {
38
+ ...options,
39
+ timeout,
40
+ maxRetries: 0,
41
+ }));
42
+ },
43
+ },
44
+ };
45
+ }
29
46
  const clientOptions = {
30
47
  apiKey: input.apiKey,
31
48
  maxRetries: 0,
@@ -2,7 +2,7 @@ import type { ActivityRef, TaskId } from "@tutti/shared/ids";
2
2
  import { type CandidateArtifactValidationFailure, type CandidateArtifactValidationResult, type ValidateCandidateArtifactOptions } from "../artifacts/index.js";
3
3
  import type { ProviderUsageRecorder } from "../provider-usage/index.js";
4
4
  import { type OpenAiStructuredOutputClient } from "../providers/openai/sdk-procedure-runner.js";
5
- import type { OpenAiProviderConfig } from "../providers/openai/provider-config.js";
5
+ import { type OpenAiCredentialAccess, type OpenAiExecutionConfig } from "../providers/openai/execution-access.js";
6
6
  import type { RunDiffSummary } from "../workspace-ops/index.js";
7
7
  export type ArtifactApplicabilityDecision = {
8
8
  decision: "artifact_required" | "not_applicable";
@@ -30,12 +30,11 @@ export type ArtifactCandidateGateResult = {
30
30
  correctable: boolean;
31
31
  failure: CandidateArtifactValidationFailure;
32
32
  };
33
- export type RunArtifactCandidateGateOptions = {
33
+ export type RunArtifactCandidateGateOptions = OpenAiCredentialAccess & {
34
34
  repoRoot: string;
35
35
  task: ArtifactApplicabilityJudgeInput["task"];
36
36
  diff: RunDiffSummary;
37
- config: OpenAiProviderConfig;
38
- apiKey: string;
37
+ config: OpenAiExecutionConfig;
39
38
  promptsRoot?: string;
40
39
  baseEnv?: NodeJS.ProcessEnv;
41
40
  client?: OpenAiStructuredOutputClient;
@@ -4,6 +4,7 @@ import { redactAndTruncateText } from "@tutti/shared/utils";
4
4
  import { ARTIFACT_MANIFEST_PATH, validateCandidateArtifact, } from "../artifacts/index.js";
5
5
  import { renderPromptTemplate } from "../prompt-templates/index.js";
6
6
  import { createOpenAiStructuredOutputClient, OpenAiStructuredOutputError, runOpenAiStructuredOutput, } from "../providers/openai/sdk-procedure-runner.js";
7
+ import { openAiCredentialAccess, } from "../providers/openai/execution-access.js";
7
8
  const ARTIFACT_APPLICABILITY_OUTPUT_SCHEMA = {
8
9
  type: "object",
9
10
  required: ["decision", "reason"],
@@ -135,7 +136,7 @@ function deterministicApplicability(options) {
135
136
  function createDefaultJudge(options) {
136
137
  const client = options.client ??
137
138
  createOpenAiStructuredOutputClient({
138
- apiKey: options.apiKey,
139
+ ...openAiCredentialAccess(options),
139
140
  ...(options.config.api_base_url === undefined
140
141
  ? {}
141
142
  : { apiBaseUrl: options.config.api_base_url }),
@@ -6,15 +6,14 @@ import type { ProviderUsageRecorder } from "../provider-usage/index.js";
6
6
  import type { OpenAiStructuredOutputClient } from "../providers/openai/sdk-procedure-runner.js";
7
7
  import type { CandidateArtifactValidationResult, ValidateCandidateArtifactOptions } from "../artifacts/index.js";
8
8
  import { type ArtifactApplicabilityJudge } from "./artifact-applicability.js";
9
- import { type OpenAiProviderConfig } from "../providers/openai/provider-config.js";
9
+ import { type OpenAiCredentialAccess, type OpenAiExecutionConfig } from "../providers/openai/execution-access.js";
10
10
  import { type CodexLinuxSandboxReadiness } from "../providers/openai/app-server/linux-sandbox-readiness.js";
11
11
  import type { CodexAppServerAgentContextRuntime } from "../providers/openai/app-server/skills.js";
12
12
  import type { CodexAppServerInvocationCoordinator } from "../providers/openai/app-server/invocation-coordinator.js";
13
13
  import type { CodexAppServerLifecycleIncident } from "../providers/openai/app-server/invocation-lifecycle.js";
14
14
  export { TASK_RUN_OUTPUT_SCHEMA } from "./task-run-output.js";
15
- export type OpenAiRunPipelineRunnerOptions = {
16
- config: OpenAiProviderConfig;
17
- apiKey: string;
15
+ export type OpenAiRunPipelineRunnerOptions = OpenAiCredentialAccess & {
16
+ config: OpenAiExecutionConfig;
18
17
  workspaceRoot: string;
19
18
  promptsRoot?: string;
20
19
  tuttiHome?: string;
@@ -3,7 +3,8 @@ import { runArtifactCandidateGate, } from "./artifact-applicability.js";
3
3
  import { checkSkipFields, checkStatus, checksSummary, completedWithoutRepoChanges, docsStatusForChangedPaths, failedBeforeChecks, failedFinishedResult, promotionNotAttempted, promotionNotPromoted, successfulRunSummary, } from "./result-projections.js";
4
4
  import { runWorkspaceWriteTask } from "./task-run-invocation.js";
5
5
  import { readOpenAiApiKeyCredential } from "../providers/openai/credential-store.js";
6
- import { resolveOpenAiProviderConfig, } from "../providers/openai/provider-config.js";
6
+ import { resolveOpenAiProviderConfig } from "../providers/openai/provider-config.js";
7
+ import { openAiCredentialAccess, } from "../providers/openai/execution-access.js";
7
8
  import { CodexAppServerWorkspaceWriteRunError } from "../providers/openai/app-server/workspace-write-run.js";
8
9
  import { inspectCodexLinuxSandboxReadiness, } from "../providers/openai/app-server/linux-sandbox-readiness.js";
9
10
  import { getProjectUserSkillsRoot } from "../skills/index.js";
@@ -287,7 +288,7 @@ async function runTaskAttempt(options, input) {
287
288
  },
288
289
  diff,
289
290
  config: options.config,
290
- apiKey: options.apiKey,
291
+ ...openAiCredentialAccess(options),
291
292
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
292
293
  ...(options.baseEnv === undefined ? {} : { baseEnv: options.baseEnv }),
293
294
  ...(options.artifactApplicabilityClient === undefined
@@ -1,3 +1,4 @@
1
+ import { openAiCredentialAccess } from "../providers/openai/execution-access.js";
1
2
  import { renderPromptTemplate } from "../prompt-templates/index.js";
2
3
  import { CODEX_APP_SERVER_VERSION } from "../providers/openai/codex-app-server.js";
3
4
  import { CodexAppServerWorkspaceWriteRunError, runCodexAppServerWorkspaceWriteRun, } from "../providers/openai/app-server/workspace-write-run.js";
@@ -71,9 +72,7 @@ function createRecoveryPrompt(input) {
71
72
  const needsCanonicalContext = input.session === undefined || input.session.metadata.thread_id === undefined;
72
73
  return renderPromptTemplate({
73
74
  templateKey: "runs.task_recovery",
74
- ...(input.options.promptsRoot === undefined
75
- ? {}
76
- : { promptsRoot: input.options.promptsRoot }),
75
+ ...(input.options.promptsRoot === undefined ? {} : { promptsRoot: input.options.promptsRoot }),
77
76
  variables: {
78
77
  recovery_input_json: {
79
78
  ...(needsCanonicalContext
@@ -148,7 +147,7 @@ function openTaskSession(options, input) {
148
147
  async function runTaskTurn(input) {
149
148
  let activeSession = input.session;
150
149
  const invoke = () => runCodexAppServerWorkspaceWriteRun({
151
- apiKey: input.options.apiKey,
150
+ ...openAiCredentialAccess(input.options),
152
151
  workspaceRoot: input.options.workspaceRoot,
153
152
  prompt: input.prompt,
154
153
  sandboxMode: input.sandboxMode,
@@ -164,9 +163,7 @@ async function runTaskTurn(input) {
164
163
  ...(input.skillSelectionName === undefined
165
164
  ? {}
166
165
  : { skillSelectionName: input.skillSelectionName }),
167
- ...(input.approvalHandler === undefined
168
- ? {}
169
- : { approvalHandler: input.approvalHandler }),
166
+ ...(input.approvalHandler === undefined ? {} : { approvalHandler: input.approvalHandler }),
170
167
  ...(input.options.config.api_base_url === undefined
171
168
  ? {}
172
169
  : { apiBaseUrl: input.options.config.api_base_url }),
@@ -274,9 +271,7 @@ function outputRecoveryEvidence(error) {
274
271
  return {
275
272
  previous_agent_output: error.details.agent_output ?? "",
276
273
  provider_output_failure: {
277
- kind: error.code === "output_parse_failed"
278
- ? "malformed_or_missing_json"
279
- : "schema_mismatch",
274
+ kind: error.code === "output_parse_failed" ? "malformed_or_missing_json" : "schema_mismatch",
280
275
  diagnostic: error.message,
281
276
  },
282
277
  };
@@ -298,8 +293,7 @@ function gateRecoveryEvidence(input) {
298
293
  };
299
294
  }
300
295
  function recoveryEvidenceForRun(input) {
301
- if (input.pipelineInput.run_lineage.kind !== "retry" ||
302
- input.session === undefined) {
296
+ if (input.pipelineInput.run_lineage.kind !== "retry" || input.session === undefined) {
303
297
  return input.current;
304
298
  }
305
299
  const persisted = readCodexSessionRecoveryEvidence(input.session);
@@ -192,6 +192,9 @@ async function runProviderSetupCommand(target) {
192
192
  const project = resolveExistingProjectContext({
193
193
  ...(target === undefined ? {} : { target }),
194
194
  });
195
+ if (project.provider_config.status === "managed") {
196
+ throw new LaunchError("provider_configuration_required", "This project uses Tutti managed access.", "Manage its trial authorization in your Tutti account; BYOK setup cannot replace it.");
197
+ }
195
198
  const result = await runProviderSetupTui({
196
199
  tuttiHome: project.tutti_home,
197
200
  projectId: project.project_id,
@@ -352,6 +352,7 @@ export async function startLaunchProject(options) {
352
352
  let relay;
353
353
  try {
354
354
  relay = await connectionManager.start();
355
+ host.startRecovery();
355
356
  }
356
357
  catch (error) {
357
358
  const launchError = error instanceof LaunchError || error instanceof RelayHostControlClientError
@@ -24,6 +24,7 @@ export type HostServerHandle = {
24
24
  integration_reference_attachment: (command: TrustedReferenceAttachmentIngestionCommand) => Promise<IntegrationDeliveryDisposition>;
25
25
  integration_managed_reference: (command: TrustedManagedReferenceIngestionCommand) => Promise<IntegrationManagedReferenceDisposition>;
26
26
  attachRelayConnectionManager: (manager: HostRelayConnectionManager) => void;
27
+ startRecovery: () => void;
27
28
  closed: Promise<void>;
28
29
  close: () => Promise<void>;
29
30
  };