@xfey/tutti 0.1.119 → 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-B6lCCHBq.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-DepsYMLl.js +0 -70
@@ -0,0 +1,103 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { createOpenAiProcedureWorkflowRunner, } from "../../control-plane/workflows/openai.js";
3
+ import { createOpenAiReadOnlyChatAssistantModel, } from "../../providers/openai/chat-assistant.js";
4
+ import { createOpenAiStructuredOutputClient } from "../../providers/openai/sdk-procedure-runner.js";
5
+ import { inspectCodexLinuxSandboxReadiness } from "../../providers/openai/app-server/linux-sandbox-readiness.js";
6
+ import { createOpenAiRunPipelineRunner, } from "../../run-pipeline/openai.js";
7
+ import { getProjectUserSkillsRoot } from "../../skills/index.js";
8
+ import { bindManagedExecution } from "./execution-binding.js";
9
+ import { failedBeforeChecks } from "../../run-pipeline/result-projections.js";
10
+ import { ManagedLocalStateError } from "./private-store.js";
11
+ import { ManagedControlError } from "./model-control-client.js";
12
+ import { NativeAuthorityTransportError } from "@tutti/relay-client";
13
+ // Shell composition only. The provider adapters know nothing about account,
14
+ // delegation, New API, or private native journals.
15
+ export function createManagedWorkflowRunner(runtime, options) {
16
+ async function resolve(ref) {
17
+ const access = await bindManagedExecution(runtime, { kind: "workflow", ref });
18
+ return createOpenAiProcedureWorkflowRunner({
19
+ config: access.config,
20
+ client: createOpenAiStructuredOutputClient({
21
+ credentialLease: access.credentialLease,
22
+ apiBaseUrl: access.config.api_base_url,
23
+ }),
24
+ ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
25
+ ...(options.recordUsage === undefined ? {} : { recordUsage: options.recordUsage }),
26
+ readOnlyAppServer: {
27
+ ...options,
28
+ credentialLease: access.credentialLease,
29
+ userSkillsRoot: getProjectUserSkillsRoot(options.tuttiHome, options.projectId),
30
+ },
31
+ });
32
+ }
33
+ return {
34
+ async runScratchpadRefresh(input, context) {
35
+ return (await resolve(context.workflow_ref)).runScratchpadRefresh(input, context);
36
+ },
37
+ async runTaskCompile(input, context) {
38
+ return (await resolve(input.workflow_ref)).runTaskCompile(input, context);
39
+ },
40
+ async runProjectContextBootstrap(input, context) {
41
+ const runner = await resolve(context?.workflow_ref ?? randomUUID());
42
+ return runner.runProjectContextBootstrap(input, context);
43
+ },
44
+ async runContextSync(input, context) {
45
+ return (await resolve(input.workflow_ref)).runContextSync(input, context);
46
+ },
47
+ async runReferenceFileSummary(input, context) {
48
+ // One workflow drains multiple targets concurrently. Do not make two files
49
+ // share a model-session lease, or expose filesystem paths as owner refs.
50
+ const ref = createHash("sha256")
51
+ .update(JSON.stringify([input.workflow_ref, input.file.path, input.file.blob_oid]))
52
+ .digest("hex");
53
+ return (await resolve(ref)).runReferenceFileSummary(input, context);
54
+ },
55
+ async runProjectBriefRefresh(input, context) {
56
+ return (await resolve(context?.workflow_ref ?? randomUUID())).runProjectBriefRefresh(input, context);
57
+ },
58
+ async runFollowUpCheck(input, context) {
59
+ return (await resolve(input.workflow_ref)).runFollowUpCheck(input, context);
60
+ },
61
+ };
62
+ }
63
+ export function createManagedRunPipeline(runtime, options) {
64
+ return {
65
+ async runTask(input) {
66
+ let access;
67
+ try {
68
+ access = await bindManagedExecution(runtime, { kind: "task", ref: input.task.id });
69
+ }
70
+ catch (error) {
71
+ const known = error instanceof ManagedLocalStateError ||
72
+ error instanceof ManagedControlError ||
73
+ error instanceof NativeAuthorityTransportError;
74
+ return failedBeforeChecks({
75
+ summary: "Tutti trial access is unavailable. Check project authorization and trial allowance before retrying.",
76
+ errorCode: known ? error.code : "managed_selection_unavailable",
77
+ retryable: true,
78
+ });
79
+ }
80
+ return createOpenAiRunPipelineRunner({
81
+ ...options,
82
+ ...access,
83
+ userSkillsRoot: getProjectUserSkillsRoot(options.tuttiHome, options.projectId),
84
+ ...(process.platform === "linux"
85
+ ? { codexSandboxReadiness: () => inspectCodexLinuxSandboxReadiness() }
86
+ : {}),
87
+ }).runTask(input);
88
+ },
89
+ };
90
+ }
91
+ export function createManagedChatModel(runtime, options) {
92
+ return {
93
+ async answer(input) {
94
+ const access = await bindManagedExecution(runtime, { kind: "chat", ref: randomUUID() });
95
+ return createOpenAiReadOnlyChatAssistantModel({
96
+ ...options,
97
+ ...access,
98
+ userSkillsRoot: getProjectUserSkillsRoot(options.tuttiHome, options.projectId),
99
+ }).answer(input);
100
+ },
101
+ };
102
+ }
103
+ //# sourceMappingURL=runners.js.map
@@ -0,0 +1,110 @@
1
+ import { type Static } from "@sinclair/typebox";
2
+ export declare const ModelSessionOwnerSchema: import("@sinclair/typebox").TObject<{
3
+ kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"chat">, import("@sinclair/typebox").TLiteral<"workflow">, import("@sinclair/typebox").TLiteral<"task">]>;
4
+ ref: import("@sinclair/typebox").TString;
5
+ }>;
6
+ export type ModelSessionOwner = Static<typeof ModelSessionOwnerSchema>;
7
+ declare const SessionSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
8
+ state: import("@sinclair/typebox").TLiteral<"ready">;
9
+ version: import("@sinclair/typebox").TLiteral<1>;
10
+ project_id: import("@sinclair/typebox").TString;
11
+ delegation_ref: import("@sinclair/typebox").TString;
12
+ grant_ref: import("@sinclair/typebox").TString;
13
+ installation_ref: import("@sinclair/typebox").TString;
14
+ tier: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"light">, import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"max">]>;
15
+ owner: import("@sinclair/typebox").TObject<{
16
+ kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"chat">, import("@sinclair/typebox").TLiteral<"workflow">, import("@sinclair/typebox").TLiteral<"task">]>;
17
+ ref: import("@sinclair/typebox").TString;
18
+ }>;
19
+ session_ref: import("@sinclair/typebox").TString;
20
+ route_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
21
+ provider_identity: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
22
+ model: import("@sinclair/typebox").TString;
23
+ base_url: import("@sinclair/typebox").TString;
24
+ }>>;
25
+ }>, import("@sinclair/typebox").TObject<{
26
+ state: import("@sinclair/typebox").TLiteral<"issuing">;
27
+ request_ref: import("@sinclair/typebox").TString;
28
+ version: import("@sinclair/typebox").TLiteral<1>;
29
+ project_id: import("@sinclair/typebox").TString;
30
+ delegation_ref: import("@sinclair/typebox").TString;
31
+ grant_ref: import("@sinclair/typebox").TString;
32
+ installation_ref: import("@sinclair/typebox").TString;
33
+ tier: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"light">, import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"max">]>;
34
+ owner: import("@sinclair/typebox").TObject<{
35
+ kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"chat">, import("@sinclair/typebox").TLiteral<"workflow">, import("@sinclair/typebox").TLiteral<"task">]>;
36
+ ref: import("@sinclair/typebox").TString;
37
+ }>;
38
+ session_ref: import("@sinclair/typebox").TString;
39
+ route_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
40
+ provider_identity: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
41
+ model: import("@sinclair/typebox").TString;
42
+ base_url: import("@sinclair/typebox").TString;
43
+ }>>;
44
+ }>, import("@sinclair/typebox").TObject<{
45
+ state: import("@sinclair/typebox").TLiteral<"active">;
46
+ request_ref: import("@sinclair/typebox").TString;
47
+ credential: import("@sinclair/typebox").TObject<{
48
+ credential_ref: import("@sinclair/typebox").TString;
49
+ session_ref: import("@sinclair/typebox").TString;
50
+ device_ref: import("@sinclair/typebox").TString;
51
+ state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"creating">, import("@sinclair/typebox").TLiteral<"active">, import("@sinclair/typebox").TLiteral<"expired">, import("@sinclair/typebox").TLiteral<"revoking">, import("@sinclair/typebox").TLiteral<"revoked">]>;
52
+ expires_at: import("@sinclair/typebox").TInteger;
53
+ quota_cap: import("@sinclair/typebox").TInteger;
54
+ }>;
55
+ provider: import("@sinclair/typebox").TObject<{
56
+ base_url: import("@sinclair/typebox").TString;
57
+ model: import("@sinclair/typebox").TString;
58
+ api_key: import("@sinclair/typebox").TString;
59
+ }>;
60
+ version: import("@sinclair/typebox").TLiteral<1>;
61
+ project_id: import("@sinclair/typebox").TString;
62
+ delegation_ref: import("@sinclair/typebox").TString;
63
+ grant_ref: import("@sinclair/typebox").TString;
64
+ installation_ref: import("@sinclair/typebox").TString;
65
+ tier: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"light">, import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"max">]>;
66
+ owner: import("@sinclair/typebox").TObject<{
67
+ kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"chat">, import("@sinclair/typebox").TLiteral<"workflow">, import("@sinclair/typebox").TLiteral<"task">]>;
68
+ ref: import("@sinclair/typebox").TString;
69
+ }>;
70
+ session_ref: import("@sinclair/typebox").TString;
71
+ route_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
72
+ provider_identity: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
73
+ model: import("@sinclair/typebox").TString;
74
+ base_url: import("@sinclair/typebox").TString;
75
+ }>>;
76
+ }>, import("@sinclair/typebox").TObject<{
77
+ state: import("@sinclair/typebox").TLiteral<"revoking">;
78
+ request_ref: import("@sinclair/typebox").TString;
79
+ credential: import("@sinclair/typebox").TObject<{
80
+ credential_ref: import("@sinclair/typebox").TString;
81
+ session_ref: import("@sinclair/typebox").TString;
82
+ device_ref: import("@sinclair/typebox").TString;
83
+ state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"creating">, import("@sinclair/typebox").TLiteral<"active">, import("@sinclair/typebox").TLiteral<"expired">, import("@sinclair/typebox").TLiteral<"revoking">, import("@sinclair/typebox").TLiteral<"revoked">]>;
84
+ expires_at: import("@sinclair/typebox").TInteger;
85
+ quota_cap: import("@sinclair/typebox").TInteger;
86
+ }>;
87
+ version: import("@sinclair/typebox").TLiteral<1>;
88
+ project_id: import("@sinclair/typebox").TString;
89
+ delegation_ref: import("@sinclair/typebox").TString;
90
+ grant_ref: import("@sinclair/typebox").TString;
91
+ installation_ref: import("@sinclair/typebox").TString;
92
+ tier: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"light">, import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"max">]>;
93
+ owner: import("@sinclair/typebox").TObject<{
94
+ kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"chat">, import("@sinclair/typebox").TLiteral<"workflow">, import("@sinclair/typebox").TLiteral<"task">]>;
95
+ ref: import("@sinclair/typebox").TString;
96
+ }>;
97
+ session_ref: import("@sinclair/typebox").TString;
98
+ route_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
99
+ provider_identity: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
100
+ model: import("@sinclair/typebox").TString;
101
+ base_url: import("@sinclair/typebox").TString;
102
+ }>>;
103
+ }>]>;
104
+ export type NativeModelSession = Static<typeof SessionSchema>;
105
+ export declare function parseNativeModelSession(value: unknown): NativeModelSession;
106
+ export declare function readyModelSession(row: NativeModelSession): NativeModelSession & {
107
+ state: "ready";
108
+ };
109
+ export {};
110
+ //# sourceMappingURL=runtime-records.d.ts.map
@@ -0,0 +1,65 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { Value } from "@sinclair/typebox/value";
3
+ import { ProjectIdSchema, IdempotencyKeySchema } from "@tutti/shared/schemas/domain";
4
+ import { HostDelegationRefSchema, HostInstallationRefSchema, ProjectModelGrantRefSchema, ManagedTierSchema, ManagedRuntimeSessionRefSchema, ManagedRouteRefSchema, ManagedCredentialProjectionSchema, ManagedProviderCredentialSchema, } from "@tutti/shared/schemas/internal";
5
+ import { ManagedLocalStateError } from "./private-store.js";
6
+ export const ModelSessionOwnerSchema = Type.Object({
7
+ kind: Type.Union([Type.Literal("chat"), Type.Literal("workflow"), Type.Literal("task")]),
8
+ ref: Type.String({ pattern: "^[A-Za-z0-9_-]{1,150}$" }),
9
+ }, { additionalProperties: false });
10
+ const Common = {
11
+ version: Type.Literal(1),
12
+ project_id: ProjectIdSchema,
13
+ delegation_ref: HostDelegationRefSchema,
14
+ grant_ref: ProjectModelGrantRefSchema,
15
+ installation_ref: HostInstallationRefSchema,
16
+ tier: ManagedTierSchema,
17
+ owner: ModelSessionOwnerSchema,
18
+ session_ref: ManagedRuntimeSessionRefSchema,
19
+ route_ref: Type.Optional(ManagedRouteRefSchema),
20
+ provider_identity: Type.Optional(Type.Object({
21
+ model: Type.String({ minLength: 1, maxLength: 100 }),
22
+ base_url: Type.String({ minLength: 1, maxLength: 500 }),
23
+ }, { additionalProperties: false })),
24
+ };
25
+ const SessionSchema = Type.Union([
26
+ Type.Object({ ...Common, state: Type.Literal("ready") }, { additionalProperties: false }),
27
+ Type.Object({ ...Common, state: Type.Literal("issuing"), request_ref: IdempotencyKeySchema }, { additionalProperties: false }),
28
+ Type.Object({
29
+ ...Common,
30
+ state: Type.Literal("active"),
31
+ request_ref: IdempotencyKeySchema,
32
+ credential: ManagedCredentialProjectionSchema,
33
+ provider: ManagedProviderCredentialSchema,
34
+ }, { additionalProperties: false }),
35
+ Type.Object({
36
+ ...Common,
37
+ state: Type.Literal("revoking"),
38
+ request_ref: IdempotencyKeySchema,
39
+ credential: ManagedCredentialProjectionSchema,
40
+ }, { additionalProperties: false }),
41
+ ]);
42
+ export function parseNativeModelSession(value) {
43
+ if (!Value.Check(SessionSchema, value))
44
+ throw new ManagedLocalStateError("managed_model_session_invalid");
45
+ if ((value.state === "active" || value.state === "revoking") &&
46
+ value.credential.session_ref !== value.session_ref)
47
+ throw new ManagedLocalStateError("managed_model_session_invalid");
48
+ return value;
49
+ }
50
+ export function readyModelSession(row) {
51
+ return {
52
+ version: row.version,
53
+ project_id: row.project_id,
54
+ delegation_ref: row.delegation_ref,
55
+ grant_ref: row.grant_ref,
56
+ installation_ref: row.installation_ref,
57
+ tier: row.tier,
58
+ owner: row.owner,
59
+ session_ref: row.session_ref,
60
+ state: "ready",
61
+ ...(row.route_ref === undefined ? {} : { route_ref: row.route_ref }),
62
+ ...(row.provider_identity === undefined ? {} : { provider_identity: row.provider_identity }),
63
+ };
64
+ }
65
+ //# sourceMappingURL=runtime-records.js.map
@@ -0,0 +1,48 @@
1
+ import type { ManagedProviderCredential } from "@tutti/shared/schemas/internal";
2
+ import { type ModelSessionOwner } from "./runtime-records.js";
3
+ export type ManagedRuntimeObservation = {
4
+ stage: "credential_ready" | "credential_revoked" | "credential_revoke_pending" | "managed_selection_failed" | "managed_invocation_failed";
5
+ session_ref?: string;
6
+ credential_ref?: string;
7
+ reason_code?: string;
8
+ request_id?: string;
9
+ };
10
+ export type ManagedInvocationAccess = {
11
+ provider: ManagedProviderCredential;
12
+ route_ref: string;
13
+ session_ref: string;
14
+ delegation_ref: string;
15
+ credential_ref: string;
16
+ };
17
+ export declare class NativeManagedRuntime {
18
+ private readonly options;
19
+ private readonly files;
20
+ private readonly state;
21
+ private readonly client;
22
+ private readonly now;
23
+ constructor(options: {
24
+ tuttiHome: string;
25
+ relayUrl: string;
26
+ projectId: string;
27
+ fetchImpl?: typeof fetch;
28
+ now?: () => Date;
29
+ observe?: (event: ManagedRuntimeObservation) => void;
30
+ });
31
+ private observe;
32
+ private name;
33
+ private readOrCreate;
34
+ private save;
35
+ private pin;
36
+ private providerEndpoint;
37
+ selection(owner: ModelSessionOwner): Promise<{
38
+ session_ref: string;
39
+ route_ref: string;
40
+ provider: {
41
+ model: string;
42
+ base_url: string;
43
+ };
44
+ }>;
45
+ private retire;
46
+ run<T>(owner: ModelSessionOwner, maxDurationSeconds: number, action: (access: ManagedInvocationAccess) => Promise<T>): Promise<T>;
47
+ }
48
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1,301 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Value } from "@sinclair/typebox/value";
3
+ import { authorizeNativeModelRuntime, NativeAuthorityTransportError } from "@tutti/relay-client";
4
+ import { createIdempotencyKey, createPrefixedId, ID_PREFIXES } from "@tutti/shared/ids";
5
+ import { ensureManagedPrivateRoot, ManagedPrivateStore, ManagedLocalStateError, } from "./private-store.js";
6
+ import { NativeModelControlClient, ManagedControlError } from "./model-control-client.js";
7
+ import { ModelSessionOwnerSchema, parseNativeModelSession, readyModelSession, } from "./runtime-records.js";
8
+ import { NativeManagedState, managedRelayOrigin } from "./state.js";
9
+ export class NativeManagedRuntime {
10
+ options;
11
+ files;
12
+ state;
13
+ client;
14
+ now;
15
+ constructor(options) {
16
+ this.options = options;
17
+ this.state = new NativeManagedState(options);
18
+ this.files = new ManagedPrivateStore(ensureManagedPrivateRoot(options.tuttiHome, createHash("sha256").update(this.state.origin).digest("hex")));
19
+ this.now = options.now ?? (() => new Date());
20
+ this.client = new NativeModelControlClient({
21
+ authorize: async () => {
22
+ const journal = this.state.project(options.projectId);
23
+ if (journal.state !== "registered")
24
+ throw new ManagedLocalStateError("managed_project_not_registered");
25
+ const authority = await authorizeNativeModelRuntime({
26
+ relayUrl: this.state.origin,
27
+ body: {
28
+ project_id: journal.project_id,
29
+ relay_project_ref: journal.authority.relay_project_ref,
30
+ delegation_ref: journal.authority.delegation_ref,
31
+ installation_ref: journal.installation_ref,
32
+ installation_secret: this.state.installation().secret,
33
+ host_registration_secret: journal.host_registration_secret,
34
+ },
35
+ ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
36
+ });
37
+ if (authority.tier !== journal.tier ||
38
+ authority.expires_at <= Math.floor(this.now().getTime() / 1000))
39
+ throw new ManagedLocalStateError("managed_runtime_authority_mismatch");
40
+ return authority;
41
+ },
42
+ ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
43
+ });
44
+ }
45
+ observe(event) {
46
+ try {
47
+ this.options.observe?.(event);
48
+ }
49
+ catch {
50
+ /* Diagnostics cannot replay a model call. */
51
+ }
52
+ }
53
+ name(owner) {
54
+ if (!Value.Check(ModelSessionOwnerSchema, owner))
55
+ throw new ManagedLocalStateError("managed_session_owner_invalid");
56
+ return `model-${createHash("sha256")
57
+ .update(JSON.stringify([this.options.projectId, owner.kind, owner.ref]))
58
+ .digest("hex")}`;
59
+ }
60
+ readOrCreate(name, owner, authority, reauthorized) {
61
+ return this.files.transaction(() => {
62
+ const saved = this.files.read(`${name}.json`);
63
+ if (saved !== null) {
64
+ const row = parseNativeModelSession(saved);
65
+ if (row.project_id !== this.options.projectId ||
66
+ row.owner.kind !== owner.kind ||
67
+ row.owner.ref !== owner.ref ||
68
+ row.installation_ref !== authority.installation_ref ||
69
+ row.tier !== authority.tier)
70
+ throw new ManagedLocalStateError("managed_model_session_scope_changed");
71
+ if (row.delegation_ref !== authority.delegation_ref ||
72
+ row.grant_ref !== authority.grant_ref) {
73
+ // An explicitly reauthorized native journal is the only entry to a new
74
+ // scope. Relay admits it only after prior installation keys are confirmed
75
+ // revoked. Keep the exact route/provider and durable Task Thread; the old
76
+ // credential and service session are never reused across delegations.
77
+ if (!reauthorized)
78
+ throw new ManagedLocalStateError("managed_model_session_scope_changed");
79
+ const renewed = {
80
+ ...readyModelSession(row),
81
+ delegation_ref: authority.delegation_ref,
82
+ grant_ref: authority.grant_ref,
83
+ session_ref: createPrefixedId(ID_PREFIXES.managedSession),
84
+ };
85
+ this.files.write(`${name}.json`, renewed);
86
+ return renewed;
87
+ }
88
+ return row;
89
+ }
90
+ const row = {
91
+ version: 1,
92
+ project_id: this.options.projectId,
93
+ delegation_ref: authority.delegation_ref,
94
+ grant_ref: authority.grant_ref,
95
+ installation_ref: authority.installation_ref,
96
+ tier: authority.tier,
97
+ owner,
98
+ session_ref: createPrefixedId(ID_PREFIXES.managedSession),
99
+ state: "ready",
100
+ };
101
+ this.files.write(`${name}.json`, row, true);
102
+ return row;
103
+ });
104
+ }
105
+ save(name, row) {
106
+ this.files.transaction(() => {
107
+ this.files.write(`${name}.json`, parseNativeModelSession(row));
108
+ });
109
+ return row;
110
+ }
111
+ async pin(name, row) {
112
+ const pin = await this.client.pin(row.session_ref, row.route_ref);
113
+ if (pin.session_ref !== row.session_ref ||
114
+ pin.tier !== row.tier ||
115
+ (row.route_ref !== undefined && pin.route_ref !== row.route_ref) ||
116
+ (row.provider_identity !== undefined &&
117
+ (row.provider_identity.model !== pin.provider.model ||
118
+ row.provider_identity.base_url !== pin.provider.base_url)))
119
+ throw new ManagedLocalStateError("managed_route_changed");
120
+ this.providerEndpoint(pin.provider.base_url);
121
+ return this.save(name, { ...row, route_ref: pin.route_ref, provider_identity: pin.provider });
122
+ }
123
+ providerEndpoint(value) {
124
+ try {
125
+ const endpoint = new URL(value);
126
+ managedRelayOrigin(endpoint.origin);
127
+ if (endpoint.username ||
128
+ endpoint.password ||
129
+ endpoint.search ||
130
+ endpoint.hash ||
131
+ !/^\/v1\/?$/.test(endpoint.pathname))
132
+ throw new Error();
133
+ }
134
+ catch {
135
+ throw new ManagedLocalStateError("managed_provider_endpoint_invalid");
136
+ }
137
+ }
138
+ // Resolve the immutable route before opening a durable Codex Thread. No key is
139
+ // issued by selection; each later turn obtains its own bounded credential lease.
140
+ async selection(owner) {
141
+ try {
142
+ const journal = this.state.project(this.options.projectId);
143
+ if (journal.state !== "registered")
144
+ throw new ManagedLocalStateError("managed_project_not_registered");
145
+ const name = this.name(owner), unlock = this.files.acquireExecutionLease(name);
146
+ try {
147
+ const row = await this.pin(name, this.readOrCreate(name, owner, journal.authority, journal.previous_request_ref !== undefined));
148
+ return {
149
+ session_ref: row.session_ref,
150
+ route_ref: row.route_ref,
151
+ provider: row.provider_identity,
152
+ };
153
+ }
154
+ finally {
155
+ unlock();
156
+ }
157
+ }
158
+ catch (error) {
159
+ this.observe({
160
+ stage: "managed_selection_failed",
161
+ reason_code: error instanceof ManagedLocalStateError ||
162
+ error instanceof ManagedControlError ||
163
+ error instanceof NativeAuthorityTransportError
164
+ ? error.code
165
+ : "managed_selection_unavailable",
166
+ ...((error instanceof ManagedControlError ||
167
+ error instanceof NativeAuthorityTransportError) &&
168
+ error.requestId !== undefined
169
+ ? { request_id: error.requestId }
170
+ : {}),
171
+ });
172
+ throw error;
173
+ }
174
+ }
175
+ async retire(name, row) {
176
+ if (row.state !== "active" && row.state !== "revoking")
177
+ return row;
178
+ const pending = {
179
+ ...readyModelSession(row),
180
+ state: "revoking",
181
+ request_ref: row.request_ref,
182
+ credential: row.credential,
183
+ };
184
+ this.save(name, pending); // Drop the local key before any revoke I/O.
185
+ const response = await this.client.revoke(row.credential.credential_ref);
186
+ if (response.credential.credential_ref !== row.credential.credential_ref ||
187
+ response.credential.session_ref !== row.session_ref ||
188
+ response.credential.state !== "revoked")
189
+ throw new ManagedLocalStateError("managed_revocation_unconfirmed");
190
+ this.observe({
191
+ stage: "credential_revoked",
192
+ session_ref: row.session_ref,
193
+ credential_ref: row.credential.credential_ref,
194
+ });
195
+ return this.save(name, readyModelSession(row));
196
+ }
197
+ // One finite credential lease per bounded provider invocation. Fresh credentials
198
+ // renew automatically on the same pinned session; the model callback is never retried.
199
+ async run(owner, maxDurationSeconds, action) {
200
+ if (!Number.isSafeInteger(maxDurationSeconds) ||
201
+ maxDurationSeconds < 1 ||
202
+ maxDurationSeconds > 82_800)
203
+ throw new ManagedLocalStateError("managed_invocation_duration_invalid");
204
+ const journal = this.state.project(this.options.projectId);
205
+ if (journal.state !== "registered")
206
+ throw new ManagedLocalStateError("managed_project_not_registered");
207
+ const name = this.name(owner), unlock = this.files.acquireExecutionLease(name);
208
+ let row;
209
+ try {
210
+ row = this.readOrCreate(name, owner, journal.authority, journal.previous_request_ref !== undefined);
211
+ row = await this.retire(name, row);
212
+ row = await this.pin(name, row);
213
+ for (let attempt = 0; attempt < 2; attempt++) {
214
+ if (row.state === "ready")
215
+ row = this.save(name, { ...row, state: "issuing", request_ref: createIdempotencyKey() });
216
+ if (row.state !== "issuing")
217
+ throw new ManagedLocalStateError("managed_model_session_invalid");
218
+ const requestRef = row.request_ref;
219
+ const result = await this.client.issue(row.session_ref, requestRef);
220
+ if (result.credential.session_ref !== row.session_ref ||
221
+ result.credential.device_ref !==
222
+ row.installation_ref.replace(/^host_installation_/, "managed_device_"))
223
+ throw new ManagedLocalStateError("managed_credential_scope_mismatch");
224
+ // Store enough metadata to revoke even when delivery or policy checks fail.
225
+ row = this.save(name, {
226
+ ...readyModelSession(row),
227
+ state: "revoking",
228
+ request_ref: row.request_ref,
229
+ credential: result.credential,
230
+ });
231
+ if (result.disposition === "already_issued") {
232
+ row = await this.retire(name, row);
233
+ continue;
234
+ }
235
+ if (result.route_ref !== row.route_ref ||
236
+ result.credential.state !== "active" ||
237
+ result.credential.expires_at <=
238
+ Math.floor(this.now().getTime() / 1000) + maxDurationSeconds + 60)
239
+ throw new ManagedLocalStateError("managed_credential_policy_invalid");
240
+ this.providerEndpoint(result.provider.base_url);
241
+ if (row.provider_identity &&
242
+ (row.provider_identity.model !== result.provider.model ||
243
+ row.provider_identity.base_url !== result.provider.base_url))
244
+ throw new ManagedLocalStateError("managed_route_changed");
245
+ row = this.save(name, {
246
+ ...readyModelSession(row),
247
+ state: "active",
248
+ request_ref: requestRef,
249
+ credential: result.credential,
250
+ provider: result.provider,
251
+ provider_identity: { model: result.provider.model, base_url: result.provider.base_url },
252
+ });
253
+ this.observe({
254
+ stage: "credential_ready",
255
+ session_ref: row.session_ref,
256
+ credential_ref: result.credential.credential_ref,
257
+ });
258
+ return await action({
259
+ provider: result.provider,
260
+ route_ref: result.route_ref,
261
+ session_ref: row.session_ref,
262
+ delegation_ref: row.delegation_ref,
263
+ credential_ref: result.credential.credential_ref,
264
+ });
265
+ }
266
+ throw new ManagedLocalStateError("managed_credential_delivery_unconfirmed");
267
+ }
268
+ catch (error) {
269
+ if (row)
270
+ this.observe({
271
+ stage: "managed_invocation_failed",
272
+ session_ref: row.session_ref,
273
+ reason_code: error instanceof ManagedLocalStateError ? error.code : "managed_invocation_unavailable",
274
+ ...(error instanceof ManagedControlError && error.requestId !== undefined
275
+ ? { request_id: error.requestId }
276
+ : {}),
277
+ });
278
+ throw error;
279
+ }
280
+ finally {
281
+ try {
282
+ if (row)
283
+ await this.retire(name, row);
284
+ }
285
+ catch (error) {
286
+ if (row)
287
+ this.observe({
288
+ stage: "credential_revoke_pending",
289
+ session_ref: row.session_ref,
290
+ reason_code: error instanceof ManagedLocalStateError
291
+ ? error.code
292
+ : "managed_revocation_unconfirmed",
293
+ });
294
+ }
295
+ finally {
296
+ unlock();
297
+ }
298
+ }
299
+ }
300
+ }
301
+ //# sourceMappingURL=runtime.js.map