@finalai/final-agent-provider 0.6.4 → 0.8.0

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 (106) hide show
  1. package/HOSTING.md +4 -4
  2. package/README.md +113 -2
  3. package/dist/modules/authoring-final-channel.mjs +2 -2
  4. package/dist/modules/authoring-final-channel.mjs.map +2 -2
  5. package/dist/modules/channel-factories.d.mts +5 -7
  6. package/dist/modules/channel-factories.mjs +4 -50
  7. package/dist/modules/channel-factories.mjs.map +2 -2
  8. package/dist/modules/channels/channel-utils.d.mts +132 -0
  9. package/dist/modules/channels/channel-utils.mjs +445 -0
  10. package/dist/modules/channels/channel-utils.mjs.map +7 -0
  11. package/dist/modules/channels/delivery.d.mts +5 -5
  12. package/dist/modules/channels/delivery.mjs.map +1 -1
  13. package/dist/modules/channels/direct-messaging.d.mts +2 -2
  14. package/dist/modules/channels/direct-messaging.mjs.map +2 -2
  15. package/dist/modules/channels/discord-dispatch.d.mts +17 -0
  16. package/dist/modules/channels/discord-dispatch.mjs +139 -0
  17. package/dist/modules/channels/discord-dispatch.mjs.map +7 -0
  18. package/dist/modules/channels/discord-gateway.d.mts +19 -0
  19. package/dist/modules/channels/discord-gateway.mjs +233 -0
  20. package/dist/modules/channels/discord-gateway.mjs.map +7 -0
  21. package/dist/modules/channels/discord.d.mts +9 -0
  22. package/dist/modules/channels/discord.mjs +904 -0
  23. package/dist/modules/channels/discord.mjs.map +7 -0
  24. package/dist/modules/channels/fake-platform.d.mts +2 -7
  25. package/dist/modules/channels/fake-platform.mjs +2 -38
  26. package/dist/modules/channels/fake-platform.mjs.map +2 -2
  27. package/dist/modules/channels/github.d.mts +5 -0
  28. package/dist/modules/channels/github.mjs +720 -0
  29. package/dist/modules/channels/github.mjs.map +7 -0
  30. package/dist/modules/channels/group-messaging.d.mts +2 -2
  31. package/dist/modules/channels/group-messaging.mjs.map +2 -2
  32. package/dist/modules/channels/index.d.mts +7 -3
  33. package/dist/modules/channels/index.mjs +7 -3
  34. package/dist/modules/channels/index.mjs.map +2 -2
  35. package/dist/modules/channels/linear.d.mts +4 -0
  36. package/dist/modules/channels/linear.mjs +621 -0
  37. package/dist/modules/channels/linear.mjs.map +7 -0
  38. package/dist/modules/channels/messaging-channel.d.mts +3 -3
  39. package/dist/modules/channels/messaging-channel.mjs +2 -2
  40. package/dist/modules/channels/messaging-channel.mjs.map +2 -2
  41. package/dist/modules/channels/phone.d.mts +8 -0
  42. package/dist/modules/channels/phone.mjs +382 -0
  43. package/dist/modules/channels/phone.mjs.map +7 -0
  44. package/dist/modules/channels/projection.mjs.map +1 -1
  45. package/dist/modules/channels/reply-receipts.d.mts +31 -0
  46. package/dist/modules/channels/reply-receipts.mjs +63 -0
  47. package/dist/modules/channels/reply-receipts.mjs.map +7 -0
  48. package/dist/modules/channels/routing.d.mts +1 -0
  49. package/dist/modules/channels/routing.mjs +5 -1
  50. package/dist/modules/channels/routing.mjs.map +2 -2
  51. package/dist/modules/channels/slack.d.mts +7 -0
  52. package/dist/modules/channels/slack.mjs +1134 -0
  53. package/dist/modules/channels/slack.mjs.map +7 -0
  54. package/dist/modules/channels/voice-recording.d.mts +7 -0
  55. package/dist/modules/channels/voice-recording.mjs +49 -0
  56. package/dist/modules/channels/voice-recording.mjs.map +7 -0
  57. package/dist/modules/channels/voice.d.mts +33 -0
  58. package/dist/modules/channels/voice.mjs +851 -0
  59. package/dist/modules/channels/voice.mjs.map +7 -0
  60. package/dist/modules/channels/webhook.d.mts +5 -0
  61. package/dist/modules/channels/webhook.mjs +76 -0
  62. package/dist/modules/channels/webhook.mjs.map +7 -0
  63. package/dist/modules/harnesses/acp-describe.mjs +1 -1
  64. package/dist/modules/harnesses/acp-describe.mjs.map +2 -2
  65. package/dist/modules/harnesses/fidelity.mjs +17 -17
  66. package/dist/modules/harnesses/fidelity.mjs.map +2 -2
  67. package/dist/modules/harnesses/remote-url.mjs.map +1 -1
  68. package/dist/modules/runtime/remote-host.mjs.map +1 -1
  69. package/dist/modules/sandboxes/build-scripts.mjs.map +1 -1
  70. package/dist/modules/sandboxes/bytes.d.mts +1 -0
  71. package/dist/modules/sandboxes/bytes.mjs +7 -1
  72. package/dist/modules/sandboxes/bytes.mjs.map +2 -2
  73. package/dist/modules/sandboxes/computer.mjs +5 -1
  74. package/dist/modules/sandboxes/computer.mjs.map +2 -2
  75. package/dist/modules/sandboxes/computers-api-transport.d.mts +1 -1
  76. package/dist/modules/sandboxes/computers-api-transport.mjs +8 -0
  77. package/dist/modules/sandboxes/computers-api-transport.mjs.map +2 -2
  78. package/dist/modules/sandboxes/device-api.mjs +8 -0
  79. package/dist/modules/sandboxes/device-api.mjs.map +2 -2
  80. package/dist/modules/sandboxes/device.mjs +6 -0
  81. package/dist/modules/sandboxes/device.mjs.map +2 -2
  82. package/dist/modules/sandboxes/paths.mjs.map +1 -1
  83. package/dist/modules/sandboxes/session.d.mts +1 -0
  84. package/dist/modules/sandboxes/session.mjs +106 -36
  85. package/dist/modules/sandboxes/session.mjs.map +3 -3
  86. package/dist/modules/sandboxes/testing/conformance.mjs +3 -0
  87. package/dist/modules/sandboxes/testing/conformance.mjs.map +2 -2
  88. package/dist/modules/sandboxes/testing/fake-machine.mjs +11 -0
  89. package/dist/modules/sandboxes/testing/fake-machine.mjs.map +2 -2
  90. package/dist/modules/sandboxes/transport.d.mts +1 -0
  91. package/dist/modules/sandboxes/transport.mjs.map +2 -2
  92. package/dist/modules/testing/harness.mjs +2 -2
  93. package/dist/modules/testing/harness.mjs.map +1 -1
  94. package/package.json +45 -19
  95. package/dist/modules/channels/address-context.d.mts +0 -7
  96. package/dist/modules/channels/address-context.mjs +0 -80
  97. package/dist/modules/channels/address-context.mjs.map +0 -7
  98. package/dist/modules/channels/agent-events.d.mts +0 -103
  99. package/dist/modules/channels/agent-events.mjs +0 -341
  100. package/dist/modules/channels/agent-events.mjs.map +0 -7
  101. package/dist/modules/channels/agent-schedules.d.mts +0 -25
  102. package/dist/modules/channels/agent-schedules.mjs +0 -96
  103. package/dist/modules/channels/agent-schedules.mjs.map +0 -7
  104. package/dist/modules/channels/service-input.d.mts +0 -22
  105. package/dist/modules/channels/service-input.mjs +0 -32
  106. package/dist/modules/channels/service-input.mjs.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/runtime/remote-host.ts"],
4
- "sourcesContent": ["import { RequestError } from '@agentclientprotocol/sdk/experimental/v2';\n\nimport {\n AcpTransport,\n BaseCapability,\n BaseToolProvision,\n CredentialDelivery,\n CredentialRoute,\n formatProviderRef,\n harnessDescriptionSchema,\n InferenceCredentialKind,\n InstructionRole,\n packageRef,\n SandboxWire,\n type HarnessClientTlsIface,\n type HarnessDescriptionIface,\n type HarnessEndpointIface,\n type HarnessHealthIface,\n type HarnessIface,\n type HarnessSessionRefIface,\n type InstructionFragmentIface,\n type JsonObject,\n type OpenHarnessSessionArgsIface,\n type ResumeHarnessSessionArgsIface,\n type SecretReaderIface,\n type SkillBundleIface,\n type ToolApproval,\n} from '@finalai/agent-provider';\n\nimport { createNodeClientTlsFetch } from '@finalai/websockets/node-client-tls';\n\nimport { createHmac } from 'node:crypto';\n\nimport * as z from 'zod';\n\nimport { toJsonObject, type SecretRefIface } from \"../harnesses/config-fields.mjs\";\n\nimport {\n resolveRemoteClientTls,\n type RemoteClientTlsInputIface,\n} from \"../harnesses/remote-client-tls.mjs\";\n\nconst unavailableSchema = z.object({\n code: z.literal('harness_unavailable'),\n executable: z.string(),\n foundVersion: z.string().nullable(),\n requiredVersion: z.string(),\n detail: z.string(),\n});\n\nconst unavailableOf = (text: string) => {\n try {\n return unavailableSchema.safeParse(JSON.parse(text)).data ?? null;\n } catch {\n return null;\n }\n};\n\nconst DEFAULT_HEALTH_TIMEOUT_MS = 5_000;\n\nconst DEFAULT_REGISTER_TIMEOUT_MS = 10_000;\n\nconst DEFAULT_CLOSE_TIMEOUT_MS = 15_000;\n\n// Mirrors @finalai/harness-runtime's serve.ts and server/auth.ts.\nexport const PROGRAM_CONFIG_HEADER = 'x-final-program-config';\n\nexport const HEALTH_TOKEN_HEADER = 'x-final-health-token';\n\nexport const CLOSE_TOKEN_HEADER = 'x-final-close-token';\n\n// The runtime's registration-scoped health credential (HMAC of the registration id under the\n// hop token): health(ref) has no secrets, so open/resume put it in the ref instead of the hop.\nexport const healthTokenFor = (args: { hop: string; id: string }): string =>\n createHmac('sha256', args.hop).update(`final-harness-health:${args.id}`).digest('base64url');\n\nexport const closeTokenFor = (args: { hop: string; id: string }): string =>\n createHmac('sha256', args.hop).update(`final-harness-close:${args.id}`).digest('base64url');\n\nconst PROGRAM_CONFIGS_PATH = '/v1/program-configs/';\n\nexport enum RemoteProgram {\n ClaudeCode = 'claude-code',\n Codex = 'codex',\n Pi = 'pi',\n}\n\nexport interface RemoteHarnessFactoryConfigIface {\n fetch?: typeof fetch;\n tlsFetch?: (tls: HarnessClientTlsIface) => typeof fetch;\n // Test seams.\n now?: () => number;\n newId?: () => string;\n healthTimeoutMs?: number;\n registerTimeoutMs?: number;\n closeTimeoutMs?: number;\n}\n\nexport interface RemoteSettingsIface {\n url: string;\n tls: RemoteClientTlsInputIface | null;\n hop: SecretRefIface;\n cwd: string | null;\n executable: string | null;\n model: string | null;\n reasoning: string | null;\n permissionMode: string | null;\n approvalPolicy: string | null;\n sandboxMode: string | null;\n allowedTools: string[] | null;\n instructions: InstructionFragmentIface[];\n skills: SkillBundleIface[];\n}\n\nexport interface RemoteProgramSpecIface {\n specifier: string;\n program: RemoteProgram;\n description: HarnessDescriptionIface;\n settings: (config: OpenHarnessSessionArgsIface['config']) => RemoteSettingsIface;\n}\n\n// The provider-private program config this harness registers with the runtime\n// See @finalai/harness-runtime's program-config.ts. Version 1.\n// `credentials`, `broker` and `refresh` carry route grants (and, for at-open\n// harnesses, the resolved model credential); they travel only between this\n// provider and the runtime, over the hop-authenticated registration.\nexport interface RemoteProgramConfigIface {\n version: 1;\n program: RemoteProgram;\n // Absolute path on the runtime host; null = the runtime's own working directory.\n cwd: string | null;\n executable: string | null;\n model: string | null;\n reasoning: string | null;\n permissionMode: string | null;\n approvalPolicy: string | null;\n sandboxMode: string | null;\n allowedTools: string[] | null;\n // System fragments only; user fragments arrive as _final/session/observe.\n instructions: Pick<InstructionFragmentIface, 'role' | 'content'>[];\n skills: SkillBundleIface[];\n // Declared capabilities this harness provides, with the target a bound one\n // routes to; null runs it natively on the host. A host-disableable capability\n // absent here must be disabled through the program's own allow-lists.\n baseTools: Record<string, { provision: BaseToolProvision; target: string | null }>;\n sandboxes: Record<\n string,\n {\n bindingId: string;\n capabilities: string[];\n rpc: { url: string; token: string; expiresAt: string } | null;\n mcp: { url: string; token: string; expiresAt: string } | null;\n }\n >;\n toolPolicy: Record<string, ToolApproval>;\n attachments: { url: string; token: string } | null;\n // Matches @finalai/harness-runtime's program config: the model credential\n // handed over at open (claude-code, codex), or the broker `model` route grant\n // for per-request programs (pi), which post to `<broker.url>/model`.\n credentials: OpenHarnessSessionArgsIface['credentials']['atOpen'];\n // `sandboxToken` is the broker `sandbox` route grant, for renewing sandbox endpoints.\n broker: {\n url: string;\n token: string;\n sandboxToken: string | null;\n harnessSessionId: string;\n } | null;\n // The grant's standard refresh route: the runtime pulls the next generation\n // of `credentials` and `broker` from it before they expire.\n refresh: OpenHarnessSessionArgsIface['credentials']['refresh'];\n}\n\nexport type RemoteOpenArgsIface = OpenHarnessSessionArgsIface;\n\nexport type RemoteHarnessIface = HarnessIface;\n\n// Scoped capabilities survive a session-service restart without retaining the hop or TLS keys.\nexport const formatRemoteRef = (args: {\n id: string;\n url: string;\n healthToken: string | null;\n closeToken?: string | null;\n}): string =>\n args.closeToken != null && args.healthToken != null\n ? `${args.id}|${args.healthToken}|${args.closeToken}|${args.url}`\n : args.healthToken == null\n ? `${args.id}|${args.url}`\n : `${args.id}|${args.healthToken}|${args.url}`;\n\nconst HEALTH_TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/u;\n\nexport interface RemoteRefIface {\n id: string;\n url: string | null;\n healthToken: string | null;\n closeToken: string | null;\n}\n\nexport const parseRemoteRef = (ref: string): RemoteRefIface => {\n const bar = ref.indexOf('|');\n if (bar < 0) return { id: ref, url: null, healthToken: null, closeToken: null };\n const id = ref.slice(0, bar);\n const rest = ref.slice(bar + 1);\n const next = rest.indexOf('|');\n const token = next < 0 ? null : rest.slice(0, next);\n if (token == null || !HEALTH_TOKEN_PATTERN.test(token))\n return { id, url: rest, healthToken: null, closeToken: null };\n const remaining = rest.slice(next + 1);\n const separator = remaining.indexOf('|');\n const closeToken = separator < 0 ? null : remaining.slice(0, separator);\n return closeToken != null && HEALTH_TOKEN_PATTERN.test(closeToken)\n ? { id, url: remaining.slice(separator + 1), healthToken: token, closeToken }\n : { id, url: remaining, healthToken: token, closeToken: null };\n};\n\nexport const transportForUrl = (url: string): AcpTransport.Ws | AcpTransport.Http =>\n /^wss?:\\/\\//.test(url) ? AcpTransport.Ws : AcpTransport.Http;\n\nconst httpUrlOf = (url: string): string =>\n url.replace(/^ws(s?):\\/\\//, (_match, secure) => `http${secure}://`);\n\n// Lifecycle routes are siblings of /acp, including a reverse proxy's path prefix.\nconst runtimeUrlFor = (args: { url: string; path: string }): string => {\n const endpoint = new URL(httpUrlOf(args.url));\n if (endpoint.pathname !== '/') endpoint.pathname = endpoint.pathname.replace(/\\/+$/, '');\n return new URL(args.path.replace(/^\\/+/, ''), endpoint).toString();\n};\n\nexport const healthUrlFor = (url: string): string => runtimeUrlFor({ url, path: 'healthz' });\n\nexport const programConfigUrlFor = (args: { url: string; id: string }): string =>\n runtimeUrlFor({ url: args.url, path: `${PROGRAM_CONFIGS_PATH}${encodeURIComponent(args.id)}` });\n\nexport const resolveSecretRef = async (args: {\n specifier: string;\n field: string;\n ref: SecretRefIface;\n secrets: SecretReaderIface;\n}): Promise<string> => {\n const value = await args.secrets.get(args.ref.secretKey);\n if (value == null || value.length === 0) {\n throw new Error(\n `${args.specifier}: secret '${args.ref.secretKey}' for ${args.field} is not bound`,\n );\n }\n return value;\n};\n\nexport const buildRemoteProgramConfig = (args: {\n spec: RemoteProgramSpecIface;\n open: OpenHarnessSessionArgsIface;\n}): RemoteProgramConfigIface => {\n const settings = args.spec.settings(args.open.config);\n const description = args.spec.description;\n // A capability bound to a resolved target routes there. One declared on the\n // harness host (`harnessHost`), or bound to a target a harness that cannot\n // route it ignores (not reboundable), runs natively on the host.\n const routesTargets = description.consumes.includes(SandboxWire.HandleRpc);\n const baseTools: RemoteProgramConfigIface['baseTools'] = {};\n for (const capability of Object.values(BaseCapability)) {\n const endpoint = args.open.baseTools[capability];\n const declaration = description.baseTools[capability];\n if (declaration == null) continue;\n if (endpoint != null) {\n baseTools[capability] = { provision: declaration.provision, target: endpoint.target };\n continue;\n }\n if (declaration.provision === BaseToolProvision.Bound) continue;\n const onHost = args.open.manifest.tools.host?.includes(capability) === true;\n const unroutedTarget = args.open.manifest.tools.base[capability] != null && !routesTargets;\n if (onHost || unroutedTarget) {\n baseTools[capability] = { provision: declaration.provision, target: null };\n }\n }\n const delivery = description.inference.delivery;\n const modelGrant = args.open.credentials.grants.find(\n (grant) => grant.route === CredentialRoute.Model,\n );\n // The broker's sandbox route renews sandbox endpoints; it accepts only this grant.\n const sandboxGrant = args.open.credentials.grants.find(\n (grant) => grant.route === CredentialRoute.Sandbox,\n );\n return {\n attachments: (() => {\n const grant = args.open.credentials.grants.find(\n (entry) => entry.route === CredentialRoute.Mcp,\n );\n return grant == null\n ? null\n : {\n url: `${args.open.credentials.brokerUrl.replace(/\\/+$/, '')}/attachment`,\n token: grant.token,\n };\n })(),\n version: 1,\n program: args.spec.program,\n cwd: settings.cwd,\n executable: settings.executable,\n model: settings.model,\n reasoning: settings.reasoning,\n permissionMode: settings.permissionMode,\n approvalPolicy: settings.approvalPolicy,\n sandboxMode: settings.sandboxMode,\n allowedTools: settings.allowedTools,\n instructions: settings.instructions\n .filter((fragment) => fragment.role === InstructionRole.System)\n .map((fragment) => ({ role: fragment.role, content: fragment.content })),\n skills: settings.skills,\n baseTools,\n sandboxes: Object.fromEntries(\n Object.entries(args.open.sandboxes).map(([target, endpoint]) => [\n target,\n {\n bindingId: endpoint.bindingId,\n capabilities: [...endpoint.capabilities],\n rpc: endpoint.rpc,\n mcp: endpoint.mcp,\n },\n ]),\n ),\n toolPolicy: { ...args.open.manifest.toolPolicy },\n // The harness's own login (`credentials.harness()`) is configured by its\n // user, not by Final: nothing is handed over and the CLI's settings apply.\n credentials:\n delivery === CredentialDelivery.AtOpen &&\n args.open.credentials.atOpen?.kind !== InferenceCredentialKind.HarnessOwned\n ? args.open.credentials.atOpen\n : null,\n broker:\n delivery === CredentialDelivery.PerRequest && modelGrant != null\n ? {\n url: args.open.credentials.brokerUrl,\n token: modelGrant.token,\n sandboxToken: sandboxGrant?.token ?? null,\n harnessSessionId: args.open.harnessSessionId,\n }\n : null,\n refresh: args.open.credentials.refresh,\n };\n};\n\nexport const createRemoteHarness = (\n spec: RemoteProgramSpecIface,\n config: RemoteHarnessFactoryConfigIface = {},\n): RemoteHarnessIface => {\n const fetchImpl = config.fetch ?? fetch;\n const fetchFor = (tls: HarnessClientTlsIface | undefined) =>\n tls == null ? fetchImpl : (config.tlsFetch?.(tls) ?? createNodeClientTlsFetch({ tls }));\n const now = config.now ?? Date.now;\n const healthTimeoutMs = config.healthTimeoutMs ?? DEFAULT_HEALTH_TIMEOUT_MS;\n const registerTimeoutMs = config.registerTimeoutMs ?? DEFAULT_REGISTER_TIMEOUT_MS;\n const clientTls = new Map<string, HarnessClientTlsIface>();\n\n // Registers the program config (fresh credentials included) under the\n // harness session's id; the runtime applies it to session/new and\n // session/resume on connections that name it.\n const register = async (args: {\n url: string;\n hop: string;\n id: string;\n open: OpenHarnessSessionArgsIface;\n tls?: HarnessClientTlsIface;\n }): Promise<void> => {\n const source = args.open.forkSource;\n const forkSource = source == null ? null : parseRemoteRef(source.ref.harnessSessionRef);\n if (\n source != null &&\n (!spec.description.supports.fork ||\n formatProviderRef(source.ref.harness) !== formatProviderRef(packageRef(spec.specifier)))\n )\n throw new Error(`${spec.specifier}: the source does not support this harness's native fork`);\n if (forkSource != null && (forkSource.id === args.id || forkSource.url !== args.url))\n throw new Error(\n `${spec.specifier}: a fork requires a distinct registration on the source host`,\n );\n const target = programConfigUrlFor({ url: args.url, id: args.id });\n const registration: JsonObject = {\n program: toJsonObject(buildRemoteProgramConfig({ spec, open: args.open })),\n };\n if (source != null && forkSource != null)\n registration['forkSource'] = {\n programConfigId: forkSource.id,\n sessionId: source.acpSessionId,\n childSessionId: args.open.session.sessionId,\n };\n let response: Response;\n try {\n response = await fetchFor(args.tls)(target, {\n method: 'PUT',\n headers: { authorization: `Bearer ${args.hop}`, 'content-type': 'application/json' },\n body: JSON.stringify(registration),\n signal: AbortSignal.timeout(registerTimeoutMs),\n });\n } catch (error) {\n throw new Error(\n `${spec.specifier}: registering the program config failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n const body = await response.text().catch(() => '');\n if (!response.ok) {\n const unavailable = unavailableOf(body);\n if (unavailable != null) throw RequestError.internalError(unavailable, unavailable.detail);\n throw new Error(\n `${spec.specifier}: the runtime refused the program config (HTTP ${response.status}) ${body.slice(0, 200)}`.trim(),\n );\n }\n };\n\n const endpointFor = async (args: {\n open: OpenHarnessSessionArgsIface;\n ref: HarnessSessionRefIface;\n }): Promise<HarnessEndpointIface> => {\n const settings = spec.settings(args.open.config);\n const hop = await resolveSecretRef({\n specifier: spec.specifier,\n field: 'hop',\n ref: settings.hop,\n secrets: args.open.secrets,\n });\n const { id } = parseRemoteRef(args.ref.harnessSessionRef);\n const tls = await resolveRemoteClientTls({\n config: settings.tls,\n secrets: args.open.secrets,\n url: settings.url,\n });\n await register({ url: settings.url, hop, id, open: args.open, tls });\n if (tls != null) clientTls.set(id, tls);\n else clientTls.delete(id);\n return {\n // Re-minted on every open and resume: a rotated hop or a legacy ref gets a current token.\n ref: {\n ...args.ref,\n harnessSessionRef: formatRemoteRef({\n id,\n url: settings.url,\n healthToken: healthTokenFor({ hop, id }),\n closeToken: closeTokenFor({ hop, id }),\n }),\n },\n transport: transportForUrl(settings.url),\n url: settings.url,\n tls,\n headers: { authorization: `Bearer ${hop}`, [PROGRAM_CONFIG_HEADER]: id },\n };\n };\n\n return {\n describe: async (binding) => {\n if (binding == null) return spec.description;\n const settings = spec.settings(binding.config);\n const hop = await resolveSecretRef({\n specifier: spec.specifier,\n field: 'hop',\n ref: settings.hop,\n secrets: binding.secrets,\n });\n const tls = await resolveRemoteClientTls({\n config: settings.tls,\n secrets: binding.secrets,\n url: settings.url,\n });\n const response = await fetchFor(tls)(\n runtimeUrlFor({ url: settings.url, path: 'v1/describe' }),\n {\n method: 'POST',\n headers: { authorization: `Bearer ${hop}`, 'content-type': 'application/json' },\n body: JSON.stringify({ program: { executable: settings.executable } }),\n signal: AbortSignal.timeout(registerTimeoutMs),\n },\n );\n const text = await response.text();\n if (!response.ok) {\n const unavailable = unavailableOf(text);\n if (unavailable != null) throw RequestError.internalError(unavailable, unavailable.detail);\n throw new Error(\n `${spec.specifier}: describe failed (HTTP ${response.status}) ${text.slice(0, 200)}`,\n );\n }\n const description = harnessDescriptionSchema.parse(JSON.parse(text));\n return {\n ...description,\n configSchema: spec.description.configSchema,\n trust: spec.description.trust,\n inference: spec.description.inference,\n consumes: spec.description.consumes,\n };\n },\n open: async (args) => {\n const ref: HarnessSessionRefIface = {\n harness: packageRef(spec.specifier),\n // endpointFor() completes the ref with the url and health token.\n harnessSessionRef: args.harnessSessionId,\n generation: 1,\n };\n return endpointFor({ open: args, ref });\n },\n // The runtime resumes the program's own session (Claude Code session, Codex\n // rollout); re-registering hands it the fresh grants.\n resume: async (args: ResumeHarnessSessionArgsIface) =>\n endpointFor({ open: args, ref: args.ref }),\n park: async () => undefined,\n close: async (ref, options) => {\n const { id, url: retainedUrl, closeToken } = parseRemoteRef(ref.harnessSessionRef);\n const context = options?.context;\n const settings = context == null ? null : spec.settings(context.config);\n const url = retainedUrl ?? settings?.url ?? null;\n if (url == null) {\n if (options?.force === true)\n throw new Error(`${spec.specifier}: the legacy ref must be resumed before forced close`);\n return;\n }\n const hop =\n context == null || settings == null\n ? null\n : await context.secrets.get(settings.hop.secretKey);\n const scopedToken = hop == null || hop.length === 0 ? closeToken : closeTokenFor({ hop, id });\n if (scopedToken == null) {\n if (options?.force === true)\n throw new Error(\n `${spec.specifier}: the legacy ref needs close context or resume before forced close`,\n );\n return;\n }\n const tls =\n context == null || settings == null\n ? clientTls.get(id)\n : await resolveRemoteClientTls({\n config: settings.tls,\n secrets: context.secrets,\n url,\n });\n const response = await fetchFor(tls)(programConfigUrlFor({ url, id }), {\n method: 'DELETE',\n headers: { [CLOSE_TOKEN_HEADER]: scopedToken, 'content-type': 'application/json' },\n body: JSON.stringify({ force: options?.force === true }),\n signal: AbortSignal.timeout(config.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS),\n });\n const body = await response.text();\n if (!response.ok && response.status !== 404)\n throw new Error(\n `${spec.specifier}: remote close failed (HTTP ${response.status}) ${body.slice(0, 200)}`,\n );\n clientTls.delete(id);\n },\n health: async (ref, context): Promise<HarnessHealthIface> => {\n const { id, url, healthToken } = parseRemoteRef(ref.harnessSessionRef);\n const checkedAt = new Date(now()).toISOString();\n if (url == null)\n return { ok: false, detail: 'harness session ref has no runtime url', checkedAt };\n try {\n const headers = new Headers({ [PROGRAM_CONFIG_HEADER]: id });\n if (healthToken != null) headers.set(HEALTH_TOKEN_HEADER, healthToken);\n const tls =\n context == null\n ? undefined\n : await resolveRemoteClientTls({\n config: spec.settings(context.config).tls,\n secrets: context.secrets,\n url,\n });\n const response = await fetchFor(tls)(healthUrlFor(url), {\n method: 'GET',\n headers,\n signal: AbortSignal.timeout(healthTimeoutMs),\n });\n const body = await response.text();\n const unavailable = response.ok ? null : unavailableOf(body);\n return response.ok\n ? { ok: true, checkedAt }\n : {\n ok: false,\n detail: unavailable?.detail ?? `healthz returned HTTP ${response.status}`,\n checkedAt,\n };\n } catch (error) {\n return {\n ok: false,\n detail: `healthz failed: ${error instanceof Error ? error.message : String(error)}`,\n checkedAt,\n };\n }\n },\n };\n};\nexport { REMOTE_URL_SCHEMES, remoteUrlJson, remoteUrlZod } from \"../harnesses/remote-url.mjs\";\n"],
4
+ "sourcesContent": ["import { RequestError } from '@agentclientprotocol/sdk/experimental/v2';\n\nimport {\n AcpTransport,\n BaseCapability,\n BaseToolProvision,\n CredentialDelivery,\n CredentialRoute,\n formatProviderRef,\n harnessDescriptionSchema,\n InferenceCredentialKind,\n InstructionRole,\n packageRef,\n SandboxWire,\n type HarnessClientTlsIface,\n type HarnessDescriptionIface,\n type HarnessEndpointIface,\n type HarnessHealthIface,\n type HarnessIface,\n type HarnessSessionRefIface,\n type InstructionFragmentIface,\n type JsonObject,\n type OpenHarnessSessionArgsIface,\n type ResumeHarnessSessionArgsIface,\n type SecretReaderIface,\n type SkillBundleIface,\n type ToolApproval,\n} from '@finalai/agent-provider';\n\nimport { createNodeClientTlsFetch } from '@finalai/websockets/node-client-tls';\n\nimport { createHmac } from 'node:crypto';\n\nimport * as z from 'zod';\n\nimport { toJsonObject, type SecretRefIface } from \"../harnesses/config-fields.mjs\";\n\nimport {\n resolveRemoteClientTls,\n type RemoteClientTlsInputIface,\n} from \"../harnesses/remote-client-tls.mjs\";\n\nconst unavailableSchema = z.object({\n code: z.literal('harness_unavailable'),\n executable: z.string(),\n foundVersion: z.string().nullable(),\n requiredVersion: z.string(),\n detail: z.string(),\n});\n\nconst unavailableOf = (text: string) => {\n try {\n return unavailableSchema.safeParse(JSON.parse(text)).data ?? null;\n } catch {\n return null;\n }\n};\n\nconst DEFAULT_HEALTH_TIMEOUT_MS = 5_000;\n\nconst DEFAULT_REGISTER_TIMEOUT_MS = 10_000;\n\nconst DEFAULT_CLOSE_TIMEOUT_MS = 15_000;\n\n// Authentication modes supported by remote harness servers.\nexport const PROGRAM_CONFIG_HEADER = 'x-final-program-config';\n\nexport const HEALTH_TOKEN_HEADER = 'x-final-health-token';\n\nexport const CLOSE_TOKEN_HEADER = 'x-final-close-token';\n\n// The runtime's registration-scoped health credential (HMAC of the registration id under the\n// hop token): health(ref) has no secrets, so open/resume put it in the ref instead of the hop.\nexport const healthTokenFor = (args: { hop: string; id: string }): string =>\n createHmac('sha256', args.hop).update(`final-harness-health:${args.id}`).digest('base64url');\n\nexport const closeTokenFor = (args: { hop: string; id: string }): string =>\n createHmac('sha256', args.hop).update(`final-harness-close:${args.id}`).digest('base64url');\n\nconst PROGRAM_CONFIGS_PATH = '/v1/program-configs/';\n\nexport enum RemoteProgram {\n ClaudeCode = 'claude-code',\n Codex = 'codex',\n Pi = 'pi',\n}\n\nexport interface RemoteHarnessFactoryConfigIface {\n fetch?: typeof fetch;\n tlsFetch?: (tls: HarnessClientTlsIface) => typeof fetch;\n // Test seams.\n now?: () => number;\n newId?: () => string;\n healthTimeoutMs?: number;\n registerTimeoutMs?: number;\n closeTimeoutMs?: number;\n}\n\nexport interface RemoteSettingsIface {\n url: string;\n tls: RemoteClientTlsInputIface | null;\n hop: SecretRefIface;\n cwd: string | null;\n executable: string | null;\n model: string | null;\n reasoning: string | null;\n permissionMode: string | null;\n approvalPolicy: string | null;\n sandboxMode: string | null;\n allowedTools: string[] | null;\n instructions: InstructionFragmentIface[];\n skills: SkillBundleIface[];\n}\n\nexport interface RemoteProgramSpecIface {\n specifier: string;\n program: RemoteProgram;\n description: HarnessDescriptionIface;\n settings: (config: OpenHarnessSessionArgsIface['config']) => RemoteSettingsIface;\n}\n\n// The provider-private program config this harness registers with the runtime\n// Remote harness program configuration, version 1.\n// `credentials`, `broker` and `refresh` carry route grants (and, for at-open\n// harnesses, the resolved model credential); they travel only between this\n// provider and the runtime, over the hop-authenticated registration.\nexport interface RemoteProgramConfigIface {\n version: 1;\n program: RemoteProgram;\n // Absolute path on the runtime host; null = the runtime's own working directory.\n cwd: string | null;\n executable: string | null;\n model: string | null;\n reasoning: string | null;\n permissionMode: string | null;\n approvalPolicy: string | null;\n sandboxMode: string | null;\n allowedTools: string[] | null;\n // System fragments only; user fragments arrive as _final/session/observe.\n instructions: Pick<InstructionFragmentIface, 'role' | 'content'>[];\n skills: SkillBundleIface[];\n // Declared capabilities this harness provides, with the target a bound one\n // routes to; null runs it natively on the host. A host-disableable capability\n // absent here must be disabled through the program's own allow-lists.\n baseTools: Record<string, { provision: BaseToolProvision; target: string | null }>;\n sandboxes: Record<\n string,\n {\n bindingId: string;\n capabilities: string[];\n rpc: { url: string; token: string; expiresAt: string } | null;\n mcp: { url: string; token: string; expiresAt: string } | null;\n }\n >;\n toolPolicy: Record<string, ToolApproval>;\n attachments: { url: string; token: string } | null;\n // In the remote program configuration, the model credential\n // handed over at open (claude-code, codex), or the broker `model` route grant\n // for per-request programs (pi), which post to `<broker.url>/model`.\n credentials: OpenHarnessSessionArgsIface['credentials']['atOpen'];\n // `sandboxToken` is the broker `sandbox` route grant, for renewing sandbox endpoints.\n broker: {\n url: string;\n token: string;\n sandboxToken: string | null;\n harnessSessionId: string;\n } | null;\n // The grant's standard refresh route: the runtime pulls the next generation\n // of `credentials` and `broker` from it before they expire.\n refresh: OpenHarnessSessionArgsIface['credentials']['refresh'];\n}\n\nexport type RemoteOpenArgsIface = OpenHarnessSessionArgsIface;\n\nexport type RemoteHarnessIface = HarnessIface;\n\n// Scoped capabilities survive a session-service restart without retaining the hop or TLS keys.\nexport const formatRemoteRef = (args: {\n id: string;\n url: string;\n healthToken: string | null;\n closeToken?: string | null;\n}): string =>\n args.closeToken != null && args.healthToken != null\n ? `${args.id}|${args.healthToken}|${args.closeToken}|${args.url}`\n : args.healthToken == null\n ? `${args.id}|${args.url}`\n : `${args.id}|${args.healthToken}|${args.url}`;\n\nconst HEALTH_TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/u;\n\nexport interface RemoteRefIface {\n id: string;\n url: string | null;\n healthToken: string | null;\n closeToken: string | null;\n}\n\nexport const parseRemoteRef = (ref: string): RemoteRefIface => {\n const bar = ref.indexOf('|');\n if (bar < 0) return { id: ref, url: null, healthToken: null, closeToken: null };\n const id = ref.slice(0, bar);\n const rest = ref.slice(bar + 1);\n const next = rest.indexOf('|');\n const token = next < 0 ? null : rest.slice(0, next);\n if (token == null || !HEALTH_TOKEN_PATTERN.test(token))\n return { id, url: rest, healthToken: null, closeToken: null };\n const remaining = rest.slice(next + 1);\n const separator = remaining.indexOf('|');\n const closeToken = separator < 0 ? null : remaining.slice(0, separator);\n return closeToken != null && HEALTH_TOKEN_PATTERN.test(closeToken)\n ? { id, url: remaining.slice(separator + 1), healthToken: token, closeToken }\n : { id, url: remaining, healthToken: token, closeToken: null };\n};\n\nexport const transportForUrl = (url: string): AcpTransport.Ws | AcpTransport.Http =>\n /^wss?:\\/\\//.test(url) ? AcpTransport.Ws : AcpTransport.Http;\n\nconst httpUrlOf = (url: string): string =>\n url.replace(/^ws(s?):\\/\\//, (_match, secure) => `http${secure}://`);\n\n// Lifecycle routes are siblings of /acp, including a reverse proxy's path prefix.\nconst runtimeUrlFor = (args: { url: string; path: string }): string => {\n const endpoint = new URL(httpUrlOf(args.url));\n if (endpoint.pathname !== '/') endpoint.pathname = endpoint.pathname.replace(/\\/+$/, '');\n return new URL(args.path.replace(/^\\/+/, ''), endpoint).toString();\n};\n\nexport const healthUrlFor = (url: string): string => runtimeUrlFor({ url, path: 'healthz' });\n\nexport const programConfigUrlFor = (args: { url: string; id: string }): string =>\n runtimeUrlFor({ url: args.url, path: `${PROGRAM_CONFIGS_PATH}${encodeURIComponent(args.id)}` });\n\nexport const resolveSecretRef = async (args: {\n specifier: string;\n field: string;\n ref: SecretRefIface;\n secrets: SecretReaderIface;\n}): Promise<string> => {\n const value = await args.secrets.get(args.ref.secretKey);\n if (value == null || value.length === 0) {\n throw new Error(\n `${args.specifier}: secret '${args.ref.secretKey}' for ${args.field} is not bound`,\n );\n }\n return value;\n};\n\nexport const buildRemoteProgramConfig = (args: {\n spec: RemoteProgramSpecIface;\n open: OpenHarnessSessionArgsIface;\n}): RemoteProgramConfigIface => {\n const settings = args.spec.settings(args.open.config);\n const description = args.spec.description;\n // A capability bound to a resolved target routes there. One declared on the\n // harness host (`harnessHost`), or bound to a target a harness that cannot\n // route it ignores (not reboundable), runs natively on the host.\n const routesTargets = description.consumes.includes(SandboxWire.HandleRpc);\n const baseTools: RemoteProgramConfigIface['baseTools'] = {};\n for (const capability of Object.values(BaseCapability)) {\n const endpoint = args.open.baseTools[capability];\n const declaration = description.baseTools[capability];\n if (declaration == null) continue;\n if (endpoint != null) {\n baseTools[capability] = { provision: declaration.provision, target: endpoint.target };\n continue;\n }\n if (declaration.provision === BaseToolProvision.Bound) continue;\n const onHost = args.open.manifest.tools.host?.includes(capability) === true;\n const unroutedTarget = args.open.manifest.tools.base[capability] != null && !routesTargets;\n if (onHost || unroutedTarget) {\n baseTools[capability] = { provision: declaration.provision, target: null };\n }\n }\n const delivery = description.inference.delivery;\n const modelGrant = args.open.credentials.grants.find(\n (grant) => grant.route === CredentialRoute.Model,\n );\n // The broker's sandbox route renews sandbox endpoints; it accepts only this grant.\n const sandboxGrant = args.open.credentials.grants.find(\n (grant) => grant.route === CredentialRoute.Sandbox,\n );\n return {\n attachments: (() => {\n const grant = args.open.credentials.grants.find(\n (entry) => entry.route === CredentialRoute.Mcp,\n );\n return grant == null\n ? null\n : {\n url: `${args.open.credentials.brokerUrl.replace(/\\/+$/, '')}/attachment`,\n token: grant.token,\n };\n })(),\n version: 1,\n program: args.spec.program,\n cwd: settings.cwd,\n executable: settings.executable,\n model: settings.model,\n reasoning: settings.reasoning,\n permissionMode: settings.permissionMode,\n approvalPolicy: settings.approvalPolicy,\n sandboxMode: settings.sandboxMode,\n allowedTools: settings.allowedTools,\n instructions: settings.instructions\n .filter((fragment) => fragment.role === InstructionRole.System)\n .map((fragment) => ({ role: fragment.role, content: fragment.content })),\n skills: settings.skills,\n baseTools,\n sandboxes: Object.fromEntries(\n Object.entries(args.open.sandboxes).map(([target, endpoint]) => [\n target,\n {\n bindingId: endpoint.bindingId,\n capabilities: [...endpoint.capabilities],\n rpc: endpoint.rpc,\n mcp: endpoint.mcp,\n },\n ]),\n ),\n toolPolicy: { ...args.open.manifest.toolPolicy },\n // The harness's own login (`credentials.harness()`) is configured by its\n // user, not by Final: nothing is handed over and the CLI's settings apply.\n credentials:\n delivery === CredentialDelivery.AtOpen &&\n args.open.credentials.atOpen?.kind !== InferenceCredentialKind.HarnessOwned\n ? args.open.credentials.atOpen\n : null,\n broker:\n delivery === CredentialDelivery.PerRequest && modelGrant != null\n ? {\n url: args.open.credentials.brokerUrl,\n token: modelGrant.token,\n sandboxToken: sandboxGrant?.token ?? null,\n harnessSessionId: args.open.harnessSessionId,\n }\n : null,\n refresh: args.open.credentials.refresh,\n };\n};\n\nexport const createRemoteHarness = (\n spec: RemoteProgramSpecIface,\n config: RemoteHarnessFactoryConfigIface = {},\n): RemoteHarnessIface => {\n const fetchImpl = config.fetch ?? fetch;\n const fetchFor = (tls: HarnessClientTlsIface | undefined) =>\n tls == null ? fetchImpl : (config.tlsFetch?.(tls) ?? createNodeClientTlsFetch({ tls }));\n const now = config.now ?? Date.now;\n const healthTimeoutMs = config.healthTimeoutMs ?? DEFAULT_HEALTH_TIMEOUT_MS;\n const registerTimeoutMs = config.registerTimeoutMs ?? DEFAULT_REGISTER_TIMEOUT_MS;\n const clientTls = new Map<string, HarnessClientTlsIface>();\n\n // Registers the program config (fresh credentials included) under the\n // harness session's id; the runtime applies it to session/new and\n // session/resume on connections that name it.\n const register = async (args: {\n url: string;\n hop: string;\n id: string;\n open: OpenHarnessSessionArgsIface;\n tls?: HarnessClientTlsIface;\n }): Promise<void> => {\n const source = args.open.forkSource;\n const forkSource = source == null ? null : parseRemoteRef(source.ref.harnessSessionRef);\n if (\n source != null &&\n (!spec.description.supports.fork ||\n formatProviderRef(source.ref.harness) !== formatProviderRef(packageRef(spec.specifier)))\n )\n throw new Error(`${spec.specifier}: the source does not support this harness's native fork`);\n if (forkSource != null && (forkSource.id === args.id || forkSource.url !== args.url))\n throw new Error(\n `${spec.specifier}: a fork requires a distinct registration on the source host`,\n );\n const target = programConfigUrlFor({ url: args.url, id: args.id });\n const registration: JsonObject = {\n program: toJsonObject(buildRemoteProgramConfig({ spec, open: args.open })),\n };\n if (source != null && forkSource != null)\n registration['forkSource'] = {\n programConfigId: forkSource.id,\n sessionId: source.acpSessionId,\n childSessionId: args.open.session.sessionId,\n };\n let response: Response;\n try {\n response = await fetchFor(args.tls)(target, {\n method: 'PUT',\n headers: { authorization: `Bearer ${args.hop}`, 'content-type': 'application/json' },\n body: JSON.stringify(registration),\n signal: AbortSignal.timeout(registerTimeoutMs),\n });\n } catch (error) {\n throw new Error(\n `${spec.specifier}: registering the program config failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n const body = await response.text().catch(() => '');\n if (!response.ok) {\n const unavailable = unavailableOf(body);\n if (unavailable != null) throw RequestError.internalError(unavailable, unavailable.detail);\n throw new Error(\n `${spec.specifier}: the runtime refused the program config (HTTP ${response.status}) ${body.slice(0, 200)}`.trim(),\n );\n }\n };\n\n const endpointFor = async (args: {\n open: OpenHarnessSessionArgsIface;\n ref: HarnessSessionRefIface;\n }): Promise<HarnessEndpointIface> => {\n const settings = spec.settings(args.open.config);\n const hop = await resolveSecretRef({\n specifier: spec.specifier,\n field: 'hop',\n ref: settings.hop,\n secrets: args.open.secrets,\n });\n const { id } = parseRemoteRef(args.ref.harnessSessionRef);\n const tls = await resolveRemoteClientTls({\n config: settings.tls,\n secrets: args.open.secrets,\n url: settings.url,\n });\n await register({ url: settings.url, hop, id, open: args.open, tls });\n if (tls != null) clientTls.set(id, tls);\n else clientTls.delete(id);\n return {\n // Re-minted on every open and resume: a rotated hop or a legacy ref gets a current token.\n ref: {\n ...args.ref,\n harnessSessionRef: formatRemoteRef({\n id,\n url: settings.url,\n healthToken: healthTokenFor({ hop, id }),\n closeToken: closeTokenFor({ hop, id }),\n }),\n },\n transport: transportForUrl(settings.url),\n url: settings.url,\n tls,\n headers: { authorization: `Bearer ${hop}`, [PROGRAM_CONFIG_HEADER]: id },\n };\n };\n\n return {\n describe: async (binding) => {\n if (binding == null) return spec.description;\n const settings = spec.settings(binding.config);\n const hop = await resolveSecretRef({\n specifier: spec.specifier,\n field: 'hop',\n ref: settings.hop,\n secrets: binding.secrets,\n });\n const tls = await resolveRemoteClientTls({\n config: settings.tls,\n secrets: binding.secrets,\n url: settings.url,\n });\n const response = await fetchFor(tls)(\n runtimeUrlFor({ url: settings.url, path: 'v1/describe' }),\n {\n method: 'POST',\n headers: { authorization: `Bearer ${hop}`, 'content-type': 'application/json' },\n body: JSON.stringify({ program: { executable: settings.executable } }),\n signal: AbortSignal.timeout(registerTimeoutMs),\n },\n );\n const text = await response.text();\n if (!response.ok) {\n const unavailable = unavailableOf(text);\n if (unavailable != null) throw RequestError.internalError(unavailable, unavailable.detail);\n throw new Error(\n `${spec.specifier}: describe failed (HTTP ${response.status}) ${text.slice(0, 200)}`,\n );\n }\n const description = harnessDescriptionSchema.parse(JSON.parse(text));\n return {\n ...description,\n configSchema: spec.description.configSchema,\n trust: spec.description.trust,\n inference: spec.description.inference,\n consumes: spec.description.consumes,\n };\n },\n open: async (args) => {\n const ref: HarnessSessionRefIface = {\n harness: packageRef(spec.specifier),\n // endpointFor() completes the ref with the url and health token.\n harnessSessionRef: args.harnessSessionId,\n generation: 1,\n };\n return endpointFor({ open: args, ref });\n },\n // The runtime resumes the program's own session (Claude Code session, Codex\n // rollout); re-registering hands it the fresh grants.\n resume: async (args: ResumeHarnessSessionArgsIface) =>\n endpointFor({ open: args, ref: args.ref }),\n park: async () => undefined,\n close: async (ref, options) => {\n const { id, url: retainedUrl, closeToken } = parseRemoteRef(ref.harnessSessionRef);\n const context = options?.context;\n const settings = context == null ? null : spec.settings(context.config);\n const url = retainedUrl ?? settings?.url ?? null;\n if (url == null) {\n if (options?.force === true)\n throw new Error(`${spec.specifier}: the legacy ref must be resumed before forced close`);\n return;\n }\n const hop =\n context == null || settings == null\n ? null\n : await context.secrets.get(settings.hop.secretKey);\n const scopedToken = hop == null || hop.length === 0 ? closeToken : closeTokenFor({ hop, id });\n if (scopedToken == null) {\n if (options?.force === true)\n throw new Error(\n `${spec.specifier}: the legacy ref needs close context or resume before forced close`,\n );\n return;\n }\n const tls =\n context == null || settings == null\n ? clientTls.get(id)\n : await resolveRemoteClientTls({\n config: settings.tls,\n secrets: context.secrets,\n url,\n });\n const response = await fetchFor(tls)(programConfigUrlFor({ url, id }), {\n method: 'DELETE',\n headers: { [CLOSE_TOKEN_HEADER]: scopedToken, 'content-type': 'application/json' },\n body: JSON.stringify({ force: options?.force === true }),\n signal: AbortSignal.timeout(config.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS),\n });\n const body = await response.text();\n if (!response.ok && response.status !== 404)\n throw new Error(\n `${spec.specifier}: remote close failed (HTTP ${response.status}) ${body.slice(0, 200)}`,\n );\n clientTls.delete(id);\n },\n health: async (ref, context): Promise<HarnessHealthIface> => {\n const { id, url, healthToken } = parseRemoteRef(ref.harnessSessionRef);\n const checkedAt = new Date(now()).toISOString();\n if (url == null)\n return { ok: false, detail: 'harness session ref has no runtime url', checkedAt };\n try {\n const headers = new Headers({ [PROGRAM_CONFIG_HEADER]: id });\n if (healthToken != null) headers.set(HEALTH_TOKEN_HEADER, healthToken);\n const tls =\n context == null\n ? undefined\n : await resolveRemoteClientTls({\n config: spec.settings(context.config).tls,\n secrets: context.secrets,\n url,\n });\n const response = await fetchFor(tls)(healthUrlFor(url), {\n method: 'GET',\n headers,\n signal: AbortSignal.timeout(healthTimeoutMs),\n });\n const body = await response.text();\n const unavailable = response.ok ? null : unavailableOf(body);\n return response.ok\n ? { ok: true, checkedAt }\n : {\n ok: false,\n detail: unavailable?.detail ?? `healthz returned HTTP ${response.status}`,\n checkedAt,\n };\n } catch (error) {\n return {\n ok: false,\n detail: `healthz failed: ${error instanceof Error ? error.message : String(error)}`,\n checkedAt,\n };\n }\n },\n };\n};\nexport { REMOTE_URL_SCHEMES, remoteUrlJson, remoteUrlZod } from \"../harnesses/remote-url.mjs\";\n"],
5
5
  "mappings": "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",
6
6
  "names": ["RemoteProgram"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/sandboxes/build-scripts.ts"],
4
- "sourcesContent": ["import { shellQuote } from \"./paths.mjs\";\n\n// The template builder's check that a guide build leaves nothing under /home or\n// /workspaces: a computer's user volume is mounted over both, so anything a build\n// step put there (~/.cargo, ~/.local, a clone in /workspaces) is hidden at run\n// time. Home content belongs in /usr/share/final-vm/home-skel\n// (internal-packages/vm-platform/STORAGE.md). The provider owns this build check.\n\nexport const HOME_SKELETON_ROOT = '/usr/share/final-vm/home-skel';\nexport const USER_VOLUME_ROOTS = ['/home', '/workspaces'];\n// Under /var/tmp, which the pre-clean and the capture scrub both remove.\nexport const HOME_BASELINE_ROOT = '/var/tmp/final-template-build/home-baseline';\nexport const HOME_CONTENT_LIMIT = 50;\nexport const HOME_BASELINE_MISSING_EXIT_CODE = 3;\n\nexport interface HomeContentScriptOptionsIface {\n // Paths the runner stages or the capture scrub removes; skipped with their subtrees.\n excludedPaths: string[];\n roots?: string[];\n homeRoot?: string;\n skeletonRoot?: string;\n baselineRoot?: string;\n}\n\nconst volumePathPrefixes = (roots: string[]): string[] => roots.map((root) => `${root}/`);\n\n// Snapshot of what the platform put under /home and /workspaces before any build\n// step ran, such as the guest agent's home marker.\nexport const homeBaselineScript = (\n args: { roots?: string[]; baselineRoot?: string } = {},\n): string => {\n const roots = args.roots ?? USER_VOLUME_ROOTS;\n const baselineRoot = shellQuote(args.baselineRoot ?? HOME_BASELINE_ROOT);\n return `\nset -eu\nrm -rf ${baselineRoot}\nmkdir -p ${baselineRoot}\nfor root in ${roots.map(shellQuote).join(' ')}; do\n if [ -d \"$root\" ]; then\n mkdir -p ${baselineRoot}\"$(dirname \"$root\")\"\n cp -PRp \"$root\" ${baselineRoot}\"$root\" 2>/dev/null || true\n fi\ndone\n`;\n};\n\n// A VM's user volume is mounted over /home/user and /workspaces, so the layer\n// must leave nothing there. Prints up to 50 entries the build steps created or\n// changed that neither the home skeleton nor the pre-build baseline has. A\n// directory neither has is reported once, when it holds anything but directories.\nexport const homeContentScript = (options: HomeContentScriptOptionsIface): string => {\n const roots = options.roots ?? USER_VOLUME_ROOTS;\n const homeRoot = options.homeRoot ?? '/home/user';\n const skeletonRoot = options.skeletonRoot ?? HOME_SKELETON_ROOT;\n const baselineRoot = options.baselineRoot ?? HOME_BASELINE_ROOT;\n const excluded = options.excludedPaths.filter((path) =>\n volumePathPrefixes(roots).some((prefix) => path.startsWith(prefix)),\n );\n const prune =\n excluded.length === 0\n ? ''\n : `\\\\( ${excluded.map((path) => `-path ${shellQuote(path)}`).join(' -o ')} \\\\) -prune -o`;\n return `\nset -u\nif [ ! -d ${shellQuote(baselineRoot)} ]; then\n echo \"the pre-build home baseline ${baselineRoot} is missing\"\n exit ${HOME_BASELINE_MISSING_EXIT_CODE}\nfi\nsame_entry() {\n if [ -L \"$1\" ]; then\n [ -L \"$2\" ] && [ \"$(readlink \"$1\")\" = \"$(readlink \"$2\")\" ]\n return\n fi\n if [ -L \"$2\" ]; then\n return 1\n fi\n if [ -d \"$1\" ]; then\n [ -d \"$2\" ]\n return\n fi\n [ -f \"$1\" ] && [ -f \"$2\" ] && cmp -s \"$1\" \"$2\"\n}\nhome_root=${shellQuote(homeRoot)}\nskeleton_root=${shellQuote(skeletonRoot)}\nskeleton_entry() {\n case \"$1\" in\n (\"$home_root\") printf '%s\\\\n' \"$skeleton_root\" ;;\n (\"$home_root\"/*) printf '%s/%s\\\\n' \"$skeleton_root\" \"\\${1#\"$home_root\"/}\" ;;\n (*) printf '%s\\\\n' /nonexistent/final-home-skeleton-entry ;;\n esac\n}\nhas_content() {\n if [ ! -r \"$1\" ] || [ ! -x \"$1\" ]; then\n return 0\n fi\n [ -n \"$(find \"$1\" -xdev -mindepth 1 ${prune} ! -type d -print 2>/dev/null | head -n 1)\" ]\n}\nscan() {\n reported=''\n find ${roots.map(shellQuote).join(' ')} -xdev -mindepth 1 ${prune} -print 2>/dev/null | while IFS= read -r path; do\n if [ -n \"$reported\" ]; then\n case \"$path\" in\n (\"$reported\"/*) continue ;;\n esac\n fi\n if same_entry \"$path\" \"$(skeleton_entry \"$path\")\" || same_entry \"$path\" ${shellQuote(baselineRoot)}\"$path\"; then\n continue\n fi\n if [ -d \"$path\" ] && [ ! -L \"$path\" ]; then\n if ! has_content \"$path\"; then\n continue\n fi\n reported=\"$path\"\n fi\n printf '%s\\\\n' \"$path\"\n done\n}\nhidden=\"$(scan | head -n ${HOME_CONTENT_LIMIT})\"\nif [ -n \"$hidden\" ]; then\n printf '%s\\\\n' \"$hidden\"\n exit 1\nfi\n`;\n};\n"],
4
+ "sourcesContent": ["import { shellQuote } from \"./paths.mjs\";\n\n// The template builder's check that a guide build leaves nothing under /home or\n// /workspaces: a computer's user volume is mounted over both, so anything a build\n// step put there (~/.cargo, ~/.local, a clone in /workspaces) is hidden at run\n// time. Home content belongs in /usr/share/final-vm/home-skel\n// so it remains available when the user volume is mounted.\n\nexport const HOME_SKELETON_ROOT = '/usr/share/final-vm/home-skel';\nexport const USER_VOLUME_ROOTS = ['/home', '/workspaces'];\n// Under /var/tmp, which the pre-clean and the capture scrub both remove.\nexport const HOME_BASELINE_ROOT = '/var/tmp/final-template-build/home-baseline';\nexport const HOME_CONTENT_LIMIT = 50;\nexport const HOME_BASELINE_MISSING_EXIT_CODE = 3;\n\nexport interface HomeContentScriptOptionsIface {\n // Paths the runner stages or the capture scrub removes; skipped with their subtrees.\n excludedPaths: string[];\n roots?: string[];\n homeRoot?: string;\n skeletonRoot?: string;\n baselineRoot?: string;\n}\n\nconst volumePathPrefixes = (roots: string[]): string[] => roots.map((root) => `${root}/`);\n\n// Snapshot of what the platform put under /home and /workspaces before any build\n// step ran, such as the guest agent's home marker.\nexport const homeBaselineScript = (\n args: { roots?: string[]; baselineRoot?: string } = {},\n): string => {\n const roots = args.roots ?? USER_VOLUME_ROOTS;\n const baselineRoot = shellQuote(args.baselineRoot ?? HOME_BASELINE_ROOT);\n return `\nset -eu\nrm -rf ${baselineRoot}\nmkdir -p ${baselineRoot}\nfor root in ${roots.map(shellQuote).join(' ')}; do\n if [ -d \"$root\" ]; then\n mkdir -p ${baselineRoot}\"$(dirname \"$root\")\"\n cp -PRp \"$root\" ${baselineRoot}\"$root\" 2>/dev/null || true\n fi\ndone\n`;\n};\n\n// A VM's user volume is mounted over /home/user and /workspaces, so the layer\n// must leave nothing there. Prints up to 50 entries the build steps created or\n// changed that neither the home skeleton nor the pre-build baseline has. A\n// directory neither has is reported once, when it holds anything but directories.\nexport const homeContentScript = (options: HomeContentScriptOptionsIface): string => {\n const roots = options.roots ?? USER_VOLUME_ROOTS;\n const homeRoot = options.homeRoot ?? '/home/user';\n const skeletonRoot = options.skeletonRoot ?? HOME_SKELETON_ROOT;\n const baselineRoot = options.baselineRoot ?? HOME_BASELINE_ROOT;\n const excluded = options.excludedPaths.filter((path) =>\n volumePathPrefixes(roots).some((prefix) => path.startsWith(prefix)),\n );\n const prune =\n excluded.length === 0\n ? ''\n : `\\\\( ${excluded.map((path) => `-path ${shellQuote(path)}`).join(' -o ')} \\\\) -prune -o`;\n return `\nset -u\nif [ ! -d ${shellQuote(baselineRoot)} ]; then\n echo \"the pre-build home baseline ${baselineRoot} is missing\"\n exit ${HOME_BASELINE_MISSING_EXIT_CODE}\nfi\nsame_entry() {\n if [ -L \"$1\" ]; then\n [ -L \"$2\" ] && [ \"$(readlink \"$1\")\" = \"$(readlink \"$2\")\" ]\n return\n fi\n if [ -L \"$2\" ]; then\n return 1\n fi\n if [ -d \"$1\" ]; then\n [ -d \"$2\" ]\n return\n fi\n [ -f \"$1\" ] && [ -f \"$2\" ] && cmp -s \"$1\" \"$2\"\n}\nhome_root=${shellQuote(homeRoot)}\nskeleton_root=${shellQuote(skeletonRoot)}\nskeleton_entry() {\n case \"$1\" in\n (\"$home_root\") printf '%s\\\\n' \"$skeleton_root\" ;;\n (\"$home_root\"/*) printf '%s/%s\\\\n' \"$skeleton_root\" \"\\${1#\"$home_root\"/}\" ;;\n (*) printf '%s\\\\n' /nonexistent/final-home-skeleton-entry ;;\n esac\n}\nhas_content() {\n if [ ! -r \"$1\" ] || [ ! -x \"$1\" ]; then\n return 0\n fi\n [ -n \"$(find \"$1\" -xdev -mindepth 1 ${prune} ! -type d -print 2>/dev/null | head -n 1)\" ]\n}\nscan() {\n reported=''\n find ${roots.map(shellQuote).join(' ')} -xdev -mindepth 1 ${prune} -print 2>/dev/null | while IFS= read -r path; do\n if [ -n \"$reported\" ]; then\n case \"$path\" in\n (\"$reported\"/*) continue ;;\n esac\n fi\n if same_entry \"$path\" \"$(skeleton_entry \"$path\")\" || same_entry \"$path\" ${shellQuote(baselineRoot)}\"$path\"; then\n continue\n fi\n if [ -d \"$path\" ] && [ ! -L \"$path\" ]; then\n if ! has_content \"$path\"; then\n continue\n fi\n reported=\"$path\"\n fi\n printf '%s\\\\n' \"$path\"\n done\n}\nhidden=\"$(scan | head -n ${HOME_CONTENT_LIMIT})\"\nif [ -n \"$hidden\" ]; then\n printf '%s\\\\n' \"$hidden\"\n exit 1\nfi\n`;\n};\n"],
5
5
  "mappings": "AAAA,SAAS,kBAAkB;AAQpB,MAAM,qBAAqB;AAC3B,MAAM,oBAAoB,CAAC,SAAS,aAAa;AAEjD,MAAM,qBAAqB;AAC3B,MAAM,qBAAqB;AAC3B,MAAM,kCAAkC;AAW/C,MAAM,qBAAqB,CAAC,UAA8B,MAAM,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG;AAIjF,MAAM,qBAAqB,CAChC,OAAoD,CAAC,MAC1C;AACX,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,eAAe,WAAW,KAAK,gBAAgB,kBAAkB;AACvE,SAAO;AAAA;AAAA,SAEA,YAAY;AAAA,WACV,YAAY;AAAA,cACT,MAAM,IAAI,UAAU,EAAE,KAAK,GAAG,CAAC;AAAA;AAAA,eAE9B,YAAY;AAAA,sBACL,YAAY;AAAA;AAAA;AAAA;AAIlC;AAMO,MAAM,oBAAoB,CAAC,YAAmD;AACnF,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,WAAW,QAAQ,cAAc;AAAA,IAAO,CAAC,SAC7C,mBAAmB,KAAK,EAAE,KAAK,CAAC,WAAW,KAAK,WAAW,MAAM,CAAC;AAAA,EACpE;AACA,QAAM,QACJ,SAAS,WAAW,IAChB,KACA,OAAO,SAAS,IAAI,CAAC,SAAS,SAAS,WAAW,IAAI,CAAC,EAAE,EAAE,KAAK,MAAM,CAAC;AAC7E,SAAO;AAAA;AAAA,YAEG,WAAW,YAAY,CAAC;AAAA,sCACE,YAAY;AAAA,SACzC,+BAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAgB5B,WAAW,QAAQ,CAAC;AAAA,gBAChB,WAAW,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAYA,KAAK;AAAA;AAAA;AAAA;AAAA,SAIpC,MAAM,IAAI,UAAU,EAAE,KAAK,GAAG,CAAC,sBAAsB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8EAMW,WAAW,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAY3E,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAM7C;",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ export declare const decodeBase64: (value: string) => Uint8Array;
4
4
  export declare const concatBytes: (chunks: readonly Uint8Array[]) => Uint8Array;
5
5
  export declare const collectStream: (stream: ReadableStream<Uint8Array>) => Promise<Uint8Array>;
6
6
  export declare const rechunk: (stream: ReadableStream<Uint8Array>, size: number) => AsyncGenerator<Uint8Array>;
7
+ export declare const splitChunks: (stream: ReadableStream<Uint8Array>, size: number) => AsyncGenerator<Uint8Array>;
7
8
  export declare const errorMessage: (cause: unknown) => string;
8
9
  export declare const isNotFoundMessage: (message: string) => boolean;
9
10
  export declare const sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
@@ -50,6 +50,11 @@ const rechunk = async function* (stream, size) {
50
50
  }
51
51
  if (pendingBytes > 0) yield concatBytes(pending);
52
52
  };
53
+ const splitChunks = async function* (stream, size) {
54
+ for await (const chunk of stream)
55
+ for (let offset = 0; offset < chunk.byteLength; offset += size)
56
+ yield chunk.subarray(offset, offset + size);
57
+ };
53
58
  const errorMessage = (cause) => cause instanceof Error ? cause.message : String(cause);
54
59
  const isNotFoundMessage = (message) => /no such file|not found|does not exist|os error 2\b|ENOENT/i.test(message);
55
60
  const sleep = (ms, signal) => new Promise((resolve, reject) => {
@@ -91,6 +96,7 @@ export {
91
96
  isNotFoundMessage,
92
97
  rechunk,
93
98
  sha256Hex,
94
- sleep
99
+ sleep,
100
+ splitChunks
95
101
  };
96
102
  //# sourceMappingURL=bytes.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/sandboxes/bytes.ts"],
4
- "sourcesContent": ["import type { JsonValue } from '@finalai/agent-provider';\n\n// Byte and stream helpers shared by the transports and the session handle.\n\nexport const encodeBase64 = (bytes: Uint8Array): string => {\n let binary = '';\n for (let index = 0; index < bytes.length; index += 0x8000) {\n binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000));\n }\n return btoa(binary);\n};\n\nexport const decodeBase64 = (value: string): Uint8Array => {\n const binary = atob(value);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);\n return bytes;\n};\n\nexport const concatBytes = (chunks: readonly Uint8Array[]): Uint8Array => {\n const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);\n const joined = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n joined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return joined;\n};\n\nexport const collectStream = async (stream: ReadableStream<Uint8Array>): Promise<Uint8Array> => {\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n for (;;) {\n const next = await reader.read();\n if (next.done) break;\n chunks.push(next.value);\n }\n return concatBytes(chunks);\n};\n\n// Re-chunks a byte stream into pieces of exactly `size` bytes (the last may be shorter).\nexport const rechunk = async function* (\n stream: ReadableStream<Uint8Array>,\n size: number,\n): AsyncGenerator<Uint8Array> {\n const reader = stream.getReader();\n let pending: Uint8Array[] = [];\n let pendingBytes = 0;\n for (;;) {\n const next = await reader.read();\n if (next.done) break;\n pending.push(next.value);\n pendingBytes += next.value.byteLength;\n while (pendingBytes >= size) {\n const joined = concatBytes(pending);\n yield joined.subarray(0, size);\n const rest = joined.subarray(size);\n pending = rest.byteLength > 0 ? [rest] : [];\n pendingBytes = rest.byteLength;\n }\n }\n if (pendingBytes > 0) yield concatBytes(pending);\n};\n\nexport const errorMessage = (cause: unknown): string =>\n cause instanceof Error ? cause.message : String(cause);\n\nexport const isNotFoundMessage = (message: string): boolean =>\n /no such file|not found|does not exist|os error 2\\b|ENOENT/i.test(message);\n\nexport const sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted === true) {\n reject(signal.reason);\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\nexport const sha256Hex = async (text: string): Promise<string> => {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));\n return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n};\n\n// Sorted-key JSON, so equal inputs hash equally regardless of key order.\nexport const canonicalJson = (value: JsonValue): string => {\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;\n if (value instanceof Object) {\n const entries = Object.entries(value).sort(([left], [right]) =>\n left < right ? -1 : left > right ? 1 : 0,\n );\n return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(',')}}`;\n }\n return JSON.stringify(value);\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { JsonValue } from '@finalai/agent-provider';\n\n// Byte and stream helpers shared by the transports and the session handle.\n\nexport const encodeBase64 = (bytes: Uint8Array): string => {\n let binary = '';\n for (let index = 0; index < bytes.length; index += 0x8000) {\n binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000));\n }\n return btoa(binary);\n};\n\nexport const decodeBase64 = (value: string): Uint8Array => {\n const binary = atob(value);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);\n return bytes;\n};\n\nexport const concatBytes = (chunks: readonly Uint8Array[]): Uint8Array => {\n const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);\n const joined = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n joined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return joined;\n};\n\nexport const collectStream = async (stream: ReadableStream<Uint8Array>): Promise<Uint8Array> => {\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n for (;;) {\n const next = await reader.read();\n if (next.done) break;\n chunks.push(next.value);\n }\n return concatBytes(chunks);\n};\n\n// Re-chunks a byte stream into pieces of exactly `size` bytes (the last may be shorter).\nexport const rechunk = async function* (\n stream: ReadableStream<Uint8Array>,\n size: number,\n): AsyncGenerator<Uint8Array> {\n const reader = stream.getReader();\n let pending: Uint8Array[] = [];\n let pendingBytes = 0;\n for (;;) {\n const next = await reader.read();\n if (next.done) break;\n pending.push(next.value);\n pendingBytes += next.value.byteLength;\n while (pendingBytes >= size) {\n const joined = concatBytes(pending);\n yield joined.subarray(0, size);\n const rest = joined.subarray(size);\n pending = rest.byteLength > 0 ? [rest] : [];\n pendingBytes = rest.byteLength;\n }\n }\n if (pendingBytes > 0) yield concatBytes(pending);\n};\n\n// Passes each chunk on as soon as it arrives, split into pieces of at most\n// `size` bytes; never waits to fill a piece (interactive input).\nexport const splitChunks = async function* (\n stream: ReadableStream<Uint8Array>,\n size: number,\n): AsyncGenerator<Uint8Array> {\n for await (const chunk of stream)\n for (let offset = 0; offset < chunk.byteLength; offset += size)\n yield chunk.subarray(offset, offset + size);\n};\n\nexport const errorMessage = (cause: unknown): string =>\n cause instanceof Error ? cause.message : String(cause);\n\nexport const isNotFoundMessage = (message: string): boolean =>\n /no such file|not found|does not exist|os error 2\\b|ENOENT/i.test(message);\n\nexport const sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted === true) {\n reject(signal.reason);\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\nexport const sha256Hex = async (text: string): Promise<string> => {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));\n return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n};\n\n// Sorted-key JSON, so equal inputs hash equally regardless of key order.\nexport const canonicalJson = (value: JsonValue): string => {\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;\n if (value instanceof Object) {\n const entries = Object.entries(value).sort(([left], [right]) =>\n left < right ? -1 : left > right ? 1 : 0,\n );\n return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(',')}}`;\n }\n return JSON.stringify(value);\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -425,7 +425,11 @@ ${commandHelp}`}`,
425
425
  if (screenAvailable) capabilities.push(SandboxCapability.Screen);
426
426
  if (computerUse != null) capabilities.push(SandboxCapability.ComputerUse);
427
427
  if (args.access !== SandboxAccess.Read)
428
- capabilities.push(SandboxCapability.Process, SandboxCapability.NetworkPolicy);
428
+ capabilities.push(
429
+ SandboxCapability.Process,
430
+ SandboxCapability.DurableProcesses,
431
+ SandboxCapability.NetworkPolicy
432
+ );
429
433
  if (args.access === SandboxAccess.Write && args.networking != null)
430
434
  capabilities.push(...config.networking?.capabilities ?? []);
431
435
  const description = {