@finalai/final-agent-provider 0.6.1 → 0.6.3

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.
@@ -117,7 +117,22 @@ const createProjector = (config = {}) => {
117
117
  const fold = async (update, ctx) => {
118
118
  const state = projectionStateOf(ctx.state.get());
119
119
  const segment = accumulateUpdate(state, update);
120
- if (segment == null) return;
120
+ if (state.messageId == null && (segment == null || segment.messageId == null)) {
121
+ for (const previous of state.segments) {
122
+ if (previous === segment || !previous.dirty || previous.messageId == null) continue;
123
+ await ctx.platform.conversations.updateMessage({
124
+ messageId: previous.messageId,
125
+ content: visibleContent({ segments: [previous] }),
126
+ final: false
127
+ });
128
+ previous.lastFlushAt = now();
129
+ previous.dirty = false;
130
+ }
131
+ }
132
+ if (segment == null) {
133
+ ctx.state.set(state);
134
+ return;
135
+ }
121
136
  const target = state.messageId == null ? segment : state;
122
137
  target.lastSeq = ctx.seq;
123
138
  const content = visibleContent({ segments: target === state ? state.segments : [segment] });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/channels/projection.ts"],
4
- "sourcesContent": ["import type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';\nimport { textOfBlock } from '@finalai/acp-session';\nimport {\n jsonObjectSchema,\n MessageKind,\n turnEndOf,\n TurnPolicy,\n type JsonValue,\n type ProjectionContextIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlockOf, jsonOfContentBlock } from \"./content.mjs\";\n\n// Each ACP message ID owns a visible conversation message. The message is\n// created on its first visible chunk (streaming),\n// updated at most every `updateIntervalMs`, and finalized at the idle\n// state_update that ends the turn. The fold lives in the turn state the\n// platform keeps (ctx.state). An empty turn projects nothing.\n\nexport const DEFAULT_PROJECTION_UPDATE_INTERVAL_MS = 300;\n\nconst projectedMessageSchema = z.object({\n messageId: z.string().nullable().default(null),\n // The reply's conversation seq, so the finished reply is read back by\n // position however many messages arrived meanwhile.\n messageSeq: z.number().nullable().default(null),\n fromSeq: z.number().nullable().default(null),\n lastSeq: z.number().nullable().default(null),\n lastFlushAt: z.number().default(0),\n dirty: z.boolean().default(false),\n});\n\nconst segmentSchema = projectedMessageSchema.extend({\n acpMessageId: z.string(),\n blocks: z.array(jsonObjectSchema),\n});\n\n// Root message fields are retained for turns created by the old projector.\n// Those turns finish in their existing combined row; new turns use segment rows.\nexport const projectionStateSchema = projectedMessageSchema.extend({\n segments: z.array(segmentSchema),\n});\n\nexport type ProjectionStateIface = z.infer<typeof projectionStateSchema>;\n\nexport interface ProjectorConfigIface {\n updateIntervalMs?: number;\n now?: () => number;\n // The channel's turnPolicy, for routing finished replies (default steer).\n turnPolicy?: TurnPolicy;\n}\n\nconst agentChunkSchema = z.object({\n sessionUpdate: z.literal('agent_message_chunk'),\n messageId: z.string(),\n content: jsonObjectSchema,\n});\n\nconst agentMessageSchema = z.object({\n sessionUpdate: z.literal('agent_message'),\n messageId: z.string(),\n content: z.array(jsonObjectSchema).nullish(),\n});\n\nconst updateJson = (update: SessionUpdate): JsonValue => {\n const parsed = jsonObjectSchema.safeParse(JSON.parse(JSON.stringify(update)));\n return parsed.success ? parsed.data : null;\n};\n\nexport const emptyProjectionState = (): ProjectionStateIface => ({\n messageId: null,\n messageSeq: null,\n fromSeq: null,\n lastSeq: null,\n lastFlushAt: 0,\n dirty: false,\n segments: [],\n});\n\nexport const projectionStateOf = (state: JsonValue | null): ProjectionStateIface => {\n const parsed = projectionStateSchema.safeParse(state);\n return parsed.success ? parsed.data : emptyProjectionState();\n};\n\n// Plain text (no `_meta`) of a block, the only kind consecutive chunks merge into.\nconst plainText = (block: ContentBlock | undefined): string | null =>\n block == null || block._meta != null ? null : textOfBlock(block);\n\nconst appendBlock = (blocks: ContentBlock[], block: ContentBlock, separator: string): void => {\n const lastText = plainText(blocks.at(-1));\n const nextText = plainText(block);\n if (lastText != null && nextText != null) {\n blocks[blocks.length - 1] = { type: 'text', text: `${lastText}${separator}${nextText}` };\n } else {\n blocks.push(block);\n }\n};\n\n// The visible content of the turn: segments in order of first appearance,\n// separated by a blank line between text runs.\nexport const visibleContent = (state: Pick<ProjectionStateIface, 'segments'>): ContentBlock[] => {\n const content: ContentBlock[] = [];\n for (const segment of state.segments) {\n const blocks: ContentBlock[] = [];\n for (const block of segment.blocks) {\n const parsed = contentBlockOf(block);\n if (parsed != null) appendBlock(blocks, parsed, '');\n }\n for (const block of blocks) appendBlock(content, block, '\\n\\n');\n }\n return content.flatMap((block): ContentBlock[] => {\n const text = textOfBlock(block);\n if (text == null) return [block];\n const trimmed = text.trim();\n return trimmed.length === 0 ? [] : [{ ...block, type: 'text', text: trimmed }];\n });\n};\n\nconst segmentFor = (\n state: ProjectionStateIface,\n acpMessageId: string,\n): ProjectionStateIface['segments'][number] => {\n const existing = state.segments.find((segment) => segment.acpMessageId === acpMessageId);\n if (existing != null) {\n return existing;\n }\n const segment = segmentSchema.parse({ acpMessageId, blocks: [] });\n state.segments.push(segment);\n return segment;\n};\n\n// Folds one ACP update into the state and returns the affected segment, or null\n// for updates that are not visible output.\nexport const accumulateUpdate = (\n state: ProjectionStateIface,\n update: SessionUpdate,\n): ProjectionStateIface['segments'][number] | null => {\n const json = updateJson(update);\n const chunk = agentChunkSchema.safeParse(json);\n if (chunk.success) {\n const block = contentBlockOf(chunk.data.content);\n if (block == null) return null;\n const segment = segmentFor(state, chunk.data.messageId);\n segment.blocks.push(jsonOfContentBlock(block));\n return segment;\n }\n const message = agentMessageSchema.safeParse(json);\n if (message.success) {\n // ACP v2 upsert: a concrete array replaces the message's content; omitted leaves it.\n if (message.data.content === undefined) return null;\n const blocks = (message.data.content ?? []).flatMap((block) => {\n const parsed = contentBlockOf(block);\n return parsed == null ? [] : [jsonOfContentBlock(parsed)];\n });\n const segment = segmentFor(state, message.data.messageId);\n segment.blocks = blocks;\n return segment;\n }\n return null;\n};\n\ninterface ConversationProjectionContextIface extends ProjectionContextIface {\n conversation: NonNullable<ProjectionContextIface['conversation']>;\n agentParticipant: NonNullable<ProjectionContextIface['agentParticipant']>;\n}\n\nexport interface ProjectorIface {\n // ChannelDefinitionIface['project'] for the messaging channels: folds visible\n // output into separate messages and, at the turn-ending idle state_update,\n // finalizes and routes them in order of first appearance.\n project(update: SessionUpdate, ctx: ProjectionContextIface): Promise<void>;\n}\n\nexport const createProjector = (config: ProjectorConfigIface = {}): ProjectorIface => {\n const now = config.now ?? Date.now;\n const interval = config.updateIntervalMs ?? DEFAULT_PROJECTION_UPDATE_INTERVAL_MS;\n\n const fold = async (\n update: SessionUpdate,\n ctx: ConversationProjectionContextIface,\n ): Promise<void> => {\n const state = projectionStateOf(ctx.state.get());\n const segment = accumulateUpdate(state, update);\n if (segment == null) return;\n const target = state.messageId == null ? segment : state;\n target.lastSeq = ctx.seq;\n const content = visibleContent({ segments: target === state ? state.segments : [segment] });\n if (content.length === 0 && target.messageId == null) {\n ctx.state.set(state);\n return;\n }\n if (target.messageId == null) {\n const message = await ctx.platform.conversations.postMessage({\n conversationId: ctx.conversation.conversationId,\n authorParticipantId: ctx.agentParticipant.id,\n kind: MessageKind.Message,\n content,\n source: { turnId: ctx.turn.id, fromSeq: ctx.seq, toSeq: ctx.seq },\n });\n target.messageId = message.messageId;\n target.messageSeq = message.seq;\n target.fromSeq = ctx.seq;\n target.lastFlushAt = now();\n target.dirty = false;\n } else if (now() - target.lastFlushAt >= interval) {\n await ctx.platform.conversations.updateMessage({\n messageId: target.messageId,\n content,\n final: false,\n });\n target.lastFlushAt = now();\n target.dirty = false;\n } else {\n target.dirty = true;\n }\n ctx.state.set(state);\n };\n\n const finish = async (ctx: ConversationProjectionContextIface): Promise<void> => {\n const state = projectionStateOf(ctx.state.get());\n if (state.messageId != null) {\n await finishMessage(ctx, state, visibleContent(state));\n return;\n }\n for (const segment of state.segments) {\n await finishMessage(ctx, segment, visibleContent({ segments: [segment] }));\n }\n };\n\n const finishMessage = async (\n ctx: ConversationProjectionContextIface,\n state: z.infer<typeof projectedMessageSchema>,\n content: ContentBlock[],\n ): Promise<void> => {\n if (state.messageId == null) {\n return;\n }\n await ctx.platform.conversations.updateMessage({\n messageId: state.messageId,\n content,\n final: true,\n });\n // The finished reply is routed like any message: other agents may observe\n // it or, when mentioned within the hop limit, be prompted.\n const reply =\n state.messageSeq == null\n ? null\n : (\n await ctx.platform.conversations.recentMessages({\n conversationId: ctx.conversation.conversationId,\n beforeSeq: state.messageSeq + 1,\n limit: 1,\n })\n ).find((message) => message.messageId === state.messageId);\n if (reply == null) {\n ctx.platform.log('finished reply not found for routing', {\n messageId: state.messageId,\n seq: state.messageSeq,\n });\n }\n if (reply != null) {\n if (ctx.platform.conversations.dispatchMessage == null)\n throw new Error('channel dispatch is unavailable');\n await ctx.platform.conversations.dispatchMessage({\n conversationId: reply.conversationId,\n messageId: reply.messageId,\n });\n }\n };\n\n const project: ProjectorIface['project'] = async (update, ctx) => {\n const { conversation, agentParticipant } = ctx;\n if (conversation == null || agentParticipant == null)\n throw new Error('Final rendering requires a Final conversation');\n const context = { ...ctx, conversation, agentParticipant };\n return turnEndOf(update) == null ? fold(update, context) : finish(context);\n };\n\n return { project };\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';\nimport { textOfBlock } from '@finalai/acp-session';\nimport {\n jsonObjectSchema,\n MessageKind,\n turnEndOf,\n TurnPolicy,\n type JsonValue,\n type ProjectionContextIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlockOf, jsonOfContentBlock } from \"./content.mjs\";\n\n// Each ACP message ID owns a visible conversation message. The message is\n// created on its first visible chunk (streaming),\n// updated at most every `updateIntervalMs`, and finalized at the idle\n// state_update that ends the turn. The fold lives in the turn state the\n// platform keeps (ctx.state). An empty turn projects nothing.\n\nexport const DEFAULT_PROJECTION_UPDATE_INTERVAL_MS = 300;\n\nconst projectedMessageSchema = z.object({\n messageId: z.string().nullable().default(null),\n // The reply's conversation seq, so the finished reply is read back by\n // position however many messages arrived meanwhile.\n messageSeq: z.number().nullable().default(null),\n fromSeq: z.number().nullable().default(null),\n lastSeq: z.number().nullable().default(null),\n lastFlushAt: z.number().default(0),\n dirty: z.boolean().default(false),\n});\n\nconst segmentSchema = projectedMessageSchema.extend({\n acpMessageId: z.string(),\n blocks: z.array(jsonObjectSchema),\n});\n\n// Root message fields are retained for turns created by the old projector.\n// Those turns finish in their existing combined row; new turns use segment rows.\nexport const projectionStateSchema = projectedMessageSchema.extend({\n segments: z.array(segmentSchema),\n});\n\nexport type ProjectionStateIface = z.infer<typeof projectionStateSchema>;\n\nexport interface ProjectorConfigIface {\n updateIntervalMs?: number;\n now?: () => number;\n // The channel's turnPolicy, for routing finished replies (default steer).\n turnPolicy?: TurnPolicy;\n}\n\nconst agentChunkSchema = z.object({\n sessionUpdate: z.literal('agent_message_chunk'),\n messageId: z.string(),\n content: jsonObjectSchema,\n});\n\nconst agentMessageSchema = z.object({\n sessionUpdate: z.literal('agent_message'),\n messageId: z.string(),\n content: z.array(jsonObjectSchema).nullish(),\n});\n\nconst updateJson = (update: SessionUpdate): JsonValue => {\n const parsed = jsonObjectSchema.safeParse(JSON.parse(JSON.stringify(update)));\n return parsed.success ? parsed.data : null;\n};\n\nexport const emptyProjectionState = (): ProjectionStateIface => ({\n messageId: null,\n messageSeq: null,\n fromSeq: null,\n lastSeq: null,\n lastFlushAt: 0,\n dirty: false,\n segments: [],\n});\n\nexport const projectionStateOf = (state: JsonValue | null): ProjectionStateIface => {\n const parsed = projectionStateSchema.safeParse(state);\n return parsed.success ? parsed.data : emptyProjectionState();\n};\n\n// Plain text (no `_meta`) of a block, the only kind consecutive chunks merge into.\nconst plainText = (block: ContentBlock | undefined): string | null =>\n block == null || block._meta != null ? null : textOfBlock(block);\n\nconst appendBlock = (blocks: ContentBlock[], block: ContentBlock, separator: string): void => {\n const lastText = plainText(blocks.at(-1));\n const nextText = plainText(block);\n if (lastText != null && nextText != null) {\n blocks[blocks.length - 1] = { type: 'text', text: `${lastText}${separator}${nextText}` };\n } else {\n blocks.push(block);\n }\n};\n\n// The visible content of the turn: segments in order of first appearance,\n// separated by a blank line between text runs.\nexport const visibleContent = (state: Pick<ProjectionStateIface, 'segments'>): ContentBlock[] => {\n const content: ContentBlock[] = [];\n for (const segment of state.segments) {\n const blocks: ContentBlock[] = [];\n for (const block of segment.blocks) {\n const parsed = contentBlockOf(block);\n if (parsed != null) appendBlock(blocks, parsed, '');\n }\n for (const block of blocks) appendBlock(content, block, '\\n\\n');\n }\n return content.flatMap((block): ContentBlock[] => {\n const text = textOfBlock(block);\n if (text == null) return [block];\n const trimmed = text.trim();\n return trimmed.length === 0 ? [] : [{ ...block, type: 'text', text: trimmed }];\n });\n};\n\nconst segmentFor = (\n state: ProjectionStateIface,\n acpMessageId: string,\n): ProjectionStateIface['segments'][number] => {\n const existing = state.segments.find((segment) => segment.acpMessageId === acpMessageId);\n if (existing != null) {\n return existing;\n }\n const segment = segmentSchema.parse({ acpMessageId, blocks: [] });\n state.segments.push(segment);\n return segment;\n};\n\n// Folds one ACP update into the state and returns the affected segment, or null\n// for updates that are not visible output.\nexport const accumulateUpdate = (\n state: ProjectionStateIface,\n update: SessionUpdate,\n): ProjectionStateIface['segments'][number] | null => {\n const json = updateJson(update);\n const chunk = agentChunkSchema.safeParse(json);\n if (chunk.success) {\n const block = contentBlockOf(chunk.data.content);\n if (block == null) return null;\n const segment = segmentFor(state, chunk.data.messageId);\n segment.blocks.push(jsonOfContentBlock(block));\n return segment;\n }\n const message = agentMessageSchema.safeParse(json);\n if (message.success) {\n // ACP v2 upsert: a concrete array replaces the message's content; omitted leaves it.\n if (message.data.content === undefined) return null;\n const blocks = (message.data.content ?? []).flatMap((block) => {\n const parsed = contentBlockOf(block);\n return parsed == null ? [] : [jsonOfContentBlock(parsed)];\n });\n const segment = segmentFor(state, message.data.messageId);\n segment.blocks = blocks;\n return segment;\n }\n return null;\n};\n\ninterface ConversationProjectionContextIface extends ProjectionContextIface {\n conversation: NonNullable<ProjectionContextIface['conversation']>;\n agentParticipant: NonNullable<ProjectionContextIface['agentParticipant']>;\n}\n\nexport interface ProjectorIface {\n // ChannelDefinitionIface['project'] for the messaging channels: folds visible\n // output into separate messages and, at the turn-ending idle state_update,\n // finalizes and routes them in order of first appearance.\n project(update: SessionUpdate, ctx: ProjectionContextIface): Promise<void>;\n}\n\nexport const createProjector = (config: ProjectorConfigIface = {}): ProjectorIface => {\n const now = config.now ?? Date.now;\n const interval = config.updateIntervalMs ?? DEFAULT_PROJECTION_UPDATE_INTERVAL_MS;\n\n const fold = async (\n update: SessionUpdate,\n ctx: ConversationProjectionContextIface,\n ): Promise<void> => {\n const state = projectionStateOf(ctx.state.get());\n const segment = accumulateUpdate(state, update);\n if (state.messageId == null && (segment == null || segment.messageId == null)) {\n for (const previous of state.segments) {\n if (previous === segment || !previous.dirty || previous.messageId == null) continue;\n await ctx.platform.conversations.updateMessage({\n messageId: previous.messageId,\n content: visibleContent({ segments: [previous] }),\n final: false,\n });\n previous.lastFlushAt = now();\n previous.dirty = false;\n }\n }\n if (segment == null) {\n ctx.state.set(state);\n return;\n }\n const target = state.messageId == null ? segment : state;\n target.lastSeq = ctx.seq;\n const content = visibleContent({ segments: target === state ? state.segments : [segment] });\n if (content.length === 0 && target.messageId == null) {\n ctx.state.set(state);\n return;\n }\n if (target.messageId == null) {\n const message = await ctx.platform.conversations.postMessage({\n conversationId: ctx.conversation.conversationId,\n authorParticipantId: ctx.agentParticipant.id,\n kind: MessageKind.Message,\n content,\n source: { turnId: ctx.turn.id, fromSeq: ctx.seq, toSeq: ctx.seq },\n });\n target.messageId = message.messageId;\n target.messageSeq = message.seq;\n target.fromSeq = ctx.seq;\n target.lastFlushAt = now();\n target.dirty = false;\n } else if (now() - target.lastFlushAt >= interval) {\n await ctx.platform.conversations.updateMessage({\n messageId: target.messageId,\n content,\n final: false,\n });\n target.lastFlushAt = now();\n target.dirty = false;\n } else {\n target.dirty = true;\n }\n ctx.state.set(state);\n };\n\n const finish = async (ctx: ConversationProjectionContextIface): Promise<void> => {\n const state = projectionStateOf(ctx.state.get());\n if (state.messageId != null) {\n await finishMessage(ctx, state, visibleContent(state));\n return;\n }\n for (const segment of state.segments) {\n await finishMessage(ctx, segment, visibleContent({ segments: [segment] }));\n }\n };\n\n const finishMessage = async (\n ctx: ConversationProjectionContextIface,\n state: z.infer<typeof projectedMessageSchema>,\n content: ContentBlock[],\n ): Promise<void> => {\n if (state.messageId == null) {\n return;\n }\n await ctx.platform.conversations.updateMessage({\n messageId: state.messageId,\n content,\n final: true,\n });\n // The finished reply is routed like any message: other agents may observe\n // it or, when mentioned within the hop limit, be prompted.\n const reply =\n state.messageSeq == null\n ? null\n : (\n await ctx.platform.conversations.recentMessages({\n conversationId: ctx.conversation.conversationId,\n beforeSeq: state.messageSeq + 1,\n limit: 1,\n })\n ).find((message) => message.messageId === state.messageId);\n if (reply == null) {\n ctx.platform.log('finished reply not found for routing', {\n messageId: state.messageId,\n seq: state.messageSeq,\n });\n }\n if (reply != null) {\n if (ctx.platform.conversations.dispatchMessage == null)\n throw new Error('channel dispatch is unavailable');\n await ctx.platform.conversations.dispatchMessage({\n conversationId: reply.conversationId,\n messageId: reply.messageId,\n });\n }\n };\n\n const project: ProjectorIface['project'] = async (update, ctx) => {\n const { conversation, agentParticipant } = ctx;\n if (conversation == null || agentParticipant == null)\n throw new Error('Final rendering requires a Final conversation');\n const context = { ...ctx, conversation, agentParticipant };\n return turnEndOf(update) == null ? fold(update, context) : finish(context);\n };\n\n return { project };\n};\n"],
5
+ "mappings": "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",
6
6
  "names": ["segment"]
7
7
  }
@@ -18,7 +18,8 @@ export declare enum ClaudePermissionMode {
18
18
  Default = "default",
19
19
  AcceptEdits = "accept-edits",
20
20
  Plan = "plan",
21
- Bypass = "bypass"
21
+ Bypass = "bypass",
22
+ Auto = "auto"
22
23
  }
23
24
  export declare enum CodexApprovalPolicy {
24
25
  Untrusted = "untrusted",
@@ -26,6 +27,10 @@ export declare enum CodexApprovalPolicy {
26
27
  OnFailure = "on-failure",
27
28
  Never = "never"
28
29
  }
30
+ export declare enum CodexApprovalsReviewer {
31
+ User = "user",
32
+ AutoReview = "auto-review"
33
+ }
29
34
  export declare enum CodexSandboxMode {
30
35
  ReadOnly = "read-only",
31
36
  WorkspaceWrite = "workspace-write",
@@ -30,6 +30,7 @@ var ClaudePermissionMode = /* @__PURE__ */ ((ClaudePermissionMode2) => {
30
30
  ClaudePermissionMode2["AcceptEdits"] = "accept-edits";
31
31
  ClaudePermissionMode2["Plan"] = "plan";
32
32
  ClaudePermissionMode2["Bypass"] = "bypass";
33
+ ClaudePermissionMode2["Auto"] = "auto";
33
34
  return ClaudePermissionMode2;
34
35
  })(ClaudePermissionMode || {});
35
36
  var CodexApprovalPolicy = /* @__PURE__ */ ((CodexApprovalPolicy2) => {
@@ -39,6 +40,11 @@ var CodexApprovalPolicy = /* @__PURE__ */ ((CodexApprovalPolicy2) => {
39
40
  CodexApprovalPolicy2["Never"] = "never";
40
41
  return CodexApprovalPolicy2;
41
42
  })(CodexApprovalPolicy || {});
43
+ var CodexApprovalsReviewer = /* @__PURE__ */ ((CodexApprovalsReviewer2) => {
44
+ CodexApprovalsReviewer2["User"] = "user";
45
+ CodexApprovalsReviewer2["AutoReview"] = "auto-review";
46
+ return CodexApprovalsReviewer2;
47
+ })(CodexApprovalsReviewer || {});
42
48
  var CodexSandboxMode = /* @__PURE__ */ ((CodexSandboxMode2) => {
43
49
  CodexSandboxMode2["ReadOnly"] = "read-only";
44
50
  CodexSandboxMode2["WorkspaceWrite"] = "workspace-write";
@@ -118,6 +124,7 @@ const harnessBinding = (specifier, config) => ({
118
124
  export {
119
125
  ClaudePermissionMode,
120
126
  CodexApprovalPolicy,
127
+ CodexApprovalsReviewer,
121
128
  CodexSandboxMode,
122
129
  MODEL_REFERENCE_PATTERN2 as MODEL_REFERENCE_PATTERN,
123
130
  PiPermissionMode,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/harnesses/config-fields.ts"],
4
- "sourcesContent": ["import { MODEL_REFERENCE_PATTERN } from \"./model-reference.mjs\";\nimport {\n credentialPolicySchema,\n instructionFragmentSchema,\n jsonObjectSchema,\n jsonValueSchema,\n packageRef,\n SCHEMA_IDS,\n skillBundleSchema,\n type CredentialPolicyIface,\n type DynamicSlotIface,\n type InstructionFragmentIface,\n type JsonObject,\n type JsonSchema,\n type JsonValue,\n type PackageProviderRefIface,\n type SkillBundleIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\n\nexport { MODEL_REFERENCE_PATTERN } from \"./model-reference.mjs\";\n\n// Config-schema building blocks shared by the harness modules. Each harness\n// writes its JSON Schema (what Build renders, $ref-ing the shared\n// final://schemas/* fragments) and a zod mirror (what open() validates the\n// resolved config with) from these, and a test keeps the two aligned.\n\n// Mirrors the final://schemas/reasoning fragment's enum.\nexport enum ReasoningLevel {\n None = 'none',\n Minimal = 'minimal',\n Low = 'low',\n Medium = 'medium',\n High = 'high',\n Max = 'max',\n}\n\n// Pi and the Final harness.\nexport enum PiPermissionMode {\n Auto = 'auto',\n Ask = 'ask',\n Readonly = 'readonly',\n}\n\n// Claude Code and the nanocode Claude engine.\nexport enum ClaudePermissionMode {\n Default = 'default',\n AcceptEdits = 'accept-edits',\n Plan = 'plan',\n Bypass = 'bypass',\n}\n\n// Codex and the nanocode Codex engine.\nexport enum CodexApprovalPolicy {\n Untrusted = 'untrusted',\n OnRequest = 'on-request',\n OnFailure = 'on-failure',\n Never = 'never',\n}\n\n// The Codex CLI's OS sandbox on a host you run.\nexport enum CodexSandboxMode {\n ReadOnly = 'read-only',\n WorkspaceWrite = 'workspace-write',\n DangerFullAccess = 'danger-full-access',\n}\n\nexport interface SecretRefIface {\n secretKey: string;\n}\n\nexport type InstructionInput = InstructionFragmentIface | DynamicSlotIface;\nexport type SkillInput = SkillBundleIface | DynamicSlotIface;\n\nexport const configObjectJson = (args: {\n title: string;\n properties: Record<string, JsonSchema>;\n required?: string[];\n}): JsonSchema => ({\n type: 'object',\n title: args.title,\n additionalProperties: false,\n properties: args.properties,\n required: args.required ?? [],\n});\n\n// Hosted harnesses leave choices and defaults to the platform; explicit lists stay restricted.\nexport const modelJson = (ids?: readonly string[], defaultId?: string): JsonSchema => ({\n $ref: SCHEMA_IDS.modelPicker,\n type: 'string',\n ...(ids == null\n ? { minLength: 1, maxLength: 200, pattern: MODEL_REFERENCE_PATTERN }\n : { enum: [...ids] }),\n ...(defaultId == null ? {} : { default: defaultId }),\n});\n\nexport const reasoningJson = (levels: readonly ReasoningLevel[]): JsonSchema => ({\n $ref: SCHEMA_IDS.reasoning,\n enum: [...levels],\n});\n\nexport const credentialsJson = (): JsonSchema => ({ $ref: SCHEMA_IDS.credentialPolicy });\n\nexport const permissionModeJson = (modes: readonly string[], defaultMode?: string): JsonSchema => {\n const schema: JsonSchema = { $ref: SCHEMA_IDS.permissionMode, enum: [...modes] };\n if (defaultMode != null) schema.default = defaultMode;\n return schema;\n};\n\nexport const instructionsJson = (): JsonSchema => ({ $ref: SCHEMA_IDS.instructions });\nexport const skillsJson = (): JsonSchema => ({ $ref: SCHEMA_IDS.skills });\nexport const secretRefJson = (): JsonSchema => ({ $ref: SCHEMA_IDS.secretRef });\n\nexport const urlJson = (title: string, schemes: readonly string[]): JsonSchema => ({\n type: 'string',\n title,\n format: 'uri',\n pattern: `^(${schemes.join('|')})://`,\n});\n\nexport const absolutePathJson = (title: string): JsonSchema => ({\n type: 'string',\n title,\n pattern: '^/',\n});\n\nexport const modelZod = (ids?: readonly string[]) => {\n if (ids != null)\n return z.string().refine((id) => ids.includes(id), {\n message: `model must be one of: ${ids.join(', ')}`,\n });\n const pattern = new RegExp(MODEL_REFERENCE_PATTERN);\n return z.string().min(1).max(200).regex(pattern, {\n message: 'model must be a printable ASCII reference without empty path segments',\n });\n};\n\nexport const reasoningZod = (levels: readonly ReasoningLevel[]) =>\n z.nativeEnum(ReasoningLevel).refine((level) => levels.includes(level), {\n message: `reasoning must be one of: ${levels.join(', ')}`,\n });\n\nexport const credentialsZod = () => credentialPolicySchema;\nexport const instructionsZod = () => z.array(instructionFragmentSchema).default([]);\nexport const skillsZod = () => z.array(skillBundleSchema).default([]);\nexport const secretRefZod = () => z.object({ secretKey: z.string().min(1) });\n\nexport const urlZod = (schemes: readonly string[]) =>\n z\n .string()\n .url()\n .refine((value) => schemes.some((scheme) => value.startsWith(`${scheme}://`)), {\n message: `url must use one of: ${schemes.join(', ')}`,\n });\n\nexport const absolutePathZod = () => z.string().startsWith('/');\n\n// Throws a readable error naming the harness; open() surfaces it as the\n// session's open failure.\nexport const parseHarnessConfig = <T extends z.ZodType>(args: {\n specifier: string;\n schema: T;\n config: JsonValue;\n}): z.output<T> => {\n const parsed = args.schema.safeParse(args.config);\n if (parsed.success) return parsed.data;\n const issues = parsed.error.issues\n .map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('; ');\n throw new Error(`invalid config for ${args.specifier}: ${issues}`);\n};\n\n// Authoring input and provider-private payloads are typed objects; a JSON\n// round trip drops undefined fields and the schema proves the result is JSON.\nexport const toJsonValue = <Value>(value: Value): JsonValue =>\n jsonValueSchema.parse(JSON.parse(JSON.stringify(value)));\n\nexport const toJsonObject = <Value>(value: Value): JsonObject =>\n jsonObjectSchema.parse(JSON.parse(JSON.stringify(value)));\n\nexport type CredentialsInput = CredentialPolicyIface;\n\n// What an authoring helper (`serverlessPi({...})`) returns: the manifest's\n// harness binding.\nexport interface HarnessBindingOutputIface {\n harness: PackageProviderRefIface;\n config: object;\n}\n\nexport const harnessBinding = <Config extends object>(\n specifier: string,\n config: Config,\n): HarnessBindingOutputIface => ({\n harness: packageRef(specifier),\n config: { ...config },\n});\n"],
5
- "mappings": "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",
6
- "names": ["MODEL_REFERENCE_PATTERN", "ReasoningLevel", "PiPermissionMode", "ClaudePermissionMode", "CodexApprovalPolicy", "CodexSandboxMode"]
4
+ "sourcesContent": ["import { MODEL_REFERENCE_PATTERN } from \"./model-reference.mjs\";\nimport {\n credentialPolicySchema,\n instructionFragmentSchema,\n jsonObjectSchema,\n jsonValueSchema,\n packageRef,\n SCHEMA_IDS,\n skillBundleSchema,\n type CredentialPolicyIface,\n type DynamicSlotIface,\n type InstructionFragmentIface,\n type JsonObject,\n type JsonSchema,\n type JsonValue,\n type PackageProviderRefIface,\n type SkillBundleIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\n\nexport { MODEL_REFERENCE_PATTERN } from \"./model-reference.mjs\";\n\n// Config-schema building blocks shared by the harness modules. Each harness\n// writes its JSON Schema (what Build renders, $ref-ing the shared\n// final://schemas/* fragments) and a zod mirror (what open() validates the\n// resolved config with) from these, and a test keeps the two aligned.\n\n// Mirrors the final://schemas/reasoning fragment's enum.\nexport enum ReasoningLevel {\n None = 'none',\n Minimal = 'minimal',\n Low = 'low',\n Medium = 'medium',\n High = 'high',\n Max = 'max',\n}\n\n// Pi and the Final harness.\nexport enum PiPermissionMode {\n Auto = 'auto',\n Ask = 'ask',\n Readonly = 'readonly',\n}\n\n// Claude Code and the nanocode Claude engine.\nexport enum ClaudePermissionMode {\n Default = 'default',\n AcceptEdits = 'accept-edits',\n Plan = 'plan',\n Bypass = 'bypass',\n // The nanocode Claude engine only: a reviewer model answers what `default` would ask,\n // as Claude Code's auto mode does.\n Auto = 'auto',\n}\n\n// Codex and the nanocode Codex engine.\nexport enum CodexApprovalPolicy {\n Untrusted = 'untrusted',\n OnRequest = 'on-request',\n OnFailure = 'on-failure',\n Never = 'never',\n}\n\n// Who answers what the nanocode Codex engine's approval policy would ask, as the Codex\n// CLI's approvals_reviewer: the person, or a reviewer model.\nexport enum CodexApprovalsReviewer {\n User = 'user',\n AutoReview = 'auto-review',\n}\n\n// The Codex CLI's OS sandbox on a host you run.\nexport enum CodexSandboxMode {\n ReadOnly = 'read-only',\n WorkspaceWrite = 'workspace-write',\n DangerFullAccess = 'danger-full-access',\n}\n\nexport interface SecretRefIface {\n secretKey: string;\n}\n\nexport type InstructionInput = InstructionFragmentIface | DynamicSlotIface;\nexport type SkillInput = SkillBundleIface | DynamicSlotIface;\n\nexport const configObjectJson = (args: {\n title: string;\n properties: Record<string, JsonSchema>;\n required?: string[];\n}): JsonSchema => ({\n type: 'object',\n title: args.title,\n additionalProperties: false,\n properties: args.properties,\n required: args.required ?? [],\n});\n\n// Hosted harnesses leave choices and defaults to the platform; explicit lists stay restricted.\nexport const modelJson = (ids?: readonly string[], defaultId?: string): JsonSchema => ({\n $ref: SCHEMA_IDS.modelPicker,\n type: 'string',\n ...(ids == null\n ? { minLength: 1, maxLength: 200, pattern: MODEL_REFERENCE_PATTERN }\n : { enum: [...ids] }),\n ...(defaultId == null ? {} : { default: defaultId }),\n});\n\nexport const reasoningJson = (levels: readonly ReasoningLevel[]): JsonSchema => ({\n $ref: SCHEMA_IDS.reasoning,\n enum: [...levels],\n});\n\nexport const credentialsJson = (): JsonSchema => ({ $ref: SCHEMA_IDS.credentialPolicy });\n\nexport const permissionModeJson = (modes: readonly string[], defaultMode?: string): JsonSchema => {\n const schema: JsonSchema = { $ref: SCHEMA_IDS.permissionMode, enum: [...modes] };\n if (defaultMode != null) schema.default = defaultMode;\n return schema;\n};\n\nexport const instructionsJson = (): JsonSchema => ({ $ref: SCHEMA_IDS.instructions });\nexport const skillsJson = (): JsonSchema => ({ $ref: SCHEMA_IDS.skills });\nexport const secretRefJson = (): JsonSchema => ({ $ref: SCHEMA_IDS.secretRef });\n\nexport const urlJson = (title: string, schemes: readonly string[]): JsonSchema => ({\n type: 'string',\n title,\n format: 'uri',\n pattern: `^(${schemes.join('|')})://`,\n});\n\nexport const absolutePathJson = (title: string): JsonSchema => ({\n type: 'string',\n title,\n pattern: '^/',\n});\n\nexport const modelZod = (ids?: readonly string[]) => {\n if (ids != null)\n return z.string().refine((id) => ids.includes(id), {\n message: `model must be one of: ${ids.join(', ')}`,\n });\n const pattern = new RegExp(MODEL_REFERENCE_PATTERN);\n return z.string().min(1).max(200).regex(pattern, {\n message: 'model must be a printable ASCII reference without empty path segments',\n });\n};\n\nexport const reasoningZod = (levels: readonly ReasoningLevel[]) =>\n z.nativeEnum(ReasoningLevel).refine((level) => levels.includes(level), {\n message: `reasoning must be one of: ${levels.join(', ')}`,\n });\n\nexport const credentialsZod = () => credentialPolicySchema;\nexport const instructionsZod = () => z.array(instructionFragmentSchema).default([]);\nexport const skillsZod = () => z.array(skillBundleSchema).default([]);\nexport const secretRefZod = () => z.object({ secretKey: z.string().min(1) });\n\nexport const urlZod = (schemes: readonly string[]) =>\n z\n .string()\n .url()\n .refine((value) => schemes.some((scheme) => value.startsWith(`${scheme}://`)), {\n message: `url must use one of: ${schemes.join(', ')}`,\n });\n\nexport const absolutePathZod = () => z.string().startsWith('/');\n\n// Throws a readable error naming the harness; open() surfaces it as the\n// session's open failure.\nexport const parseHarnessConfig = <T extends z.ZodType>(args: {\n specifier: string;\n schema: T;\n config: JsonValue;\n}): z.output<T> => {\n const parsed = args.schema.safeParse(args.config);\n if (parsed.success) return parsed.data;\n const issues = parsed.error.issues\n .map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('; ');\n throw new Error(`invalid config for ${args.specifier}: ${issues}`);\n};\n\n// Authoring input and provider-private payloads are typed objects; a JSON\n// round trip drops undefined fields and the schema proves the result is JSON.\nexport const toJsonValue = <Value>(value: Value): JsonValue =>\n jsonValueSchema.parse(JSON.parse(JSON.stringify(value)));\n\nexport const toJsonObject = <Value>(value: Value): JsonObject =>\n jsonObjectSchema.parse(JSON.parse(JSON.stringify(value)));\n\nexport type CredentialsInput = CredentialPolicyIface;\n\n// What an authoring helper (`serverlessPi({...})`) returns: the manifest's\n// harness binding.\nexport interface HarnessBindingOutputIface {\n harness: PackageProviderRefIface;\n config: object;\n}\n\nexport const harnessBinding = <Config extends object>(\n specifier: string,\n config: Config,\n): HarnessBindingOutputIface => ({\n harness: packageRef(specifier),\n config: { ...config },\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["MODEL_REFERENCE_PATTERN", "ReasoningLevel", "PiPermissionMode", "ClaudePermissionMode", "CodexApprovalPolicy", "CodexApprovalsReviewer", "CodexSandboxMode"]
7
7
  }
@@ -82,7 +82,7 @@ const NANOCODE_FIDELITY = {
82
82
  },
83
83
  {
84
84
  feature: "Approval policies",
85
- nanocode: "When the definition's tool policy is silent, `untrusted` asks before `exec_command` and `apply_patch`; `on-request`, `on-failure` and `never` run without asking.",
85
+ nanocode: "When the definition's tool policy is silent, `untrusted` asks before `exec_command` and `apply_patch`; `on-request`, `on-failure` and `never` run without asking. With `approvalsReviewer: 'auto-review'`, a reviewer model (`gpt-6-luna`, falling back to the session's model) answers what `untrusted` would ask: low and medium risk run, a rejected action goes to the person with the reviewer's reason, and a critical one is denied.",
86
86
  remoteOnly: "The app-server's own policies together with `sandboxMode`.",
87
87
  gone: {
88
88
  what: "Escalation on failure and sandbox modes.",
@@ -195,7 +195,7 @@ const NANOCODE_FIDELITY = {
195
195
  },
196
196
  {
197
197
  feature: "Permission modes",
198
- nanocode: "When the definition's tool policy is silent, `default` and `plan` ask before `Write` and `Bash`; `accept-edits` asks before `Bash` only; `bypass` never asks.",
198
+ nanocode: "When the definition's tool policy is silent, `default` and `plan` ask before `Write` and `Bash`; `accept-edits` asks before `Bash` only; `bypass` never asks; `auto` has a reviewer model (`claude-sonnet-5`, falling back to the session's model) judge `Write` and `Bash`: low and medium risk run, a rejected action goes to the person with the reviewer's reason, and a critical one is denied.",
199
199
  remoteOnly: "Claude Code's permission modes and the allow and deny rules in your settings.",
200
200
  gone: {
201
201
  what: "Allow and deny rules from Claude Code settings.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/harnesses/fidelity.ts"],
4
- "sourcesContent": ["// Harness fidelity for the serverless nanocode harnesses per engine, what nanocode\n// keeps, what only the matching remote-* harness keeps (the user's own CLI through\n// @finalai/harness-runtime), and what is gone, with a one-line reason a user can read.\n// Measured from the code and tests named in `evidence`; FIDELITY.md renders these tables, and\n// the legacy import writes the matching one into each moved agent's review file.\n\nexport enum NanocodeEngine {\n Codex = 'codex',\n Claude = 'claude',\n}\n\nexport interface FidelityRowIface {\n feature: string;\n nanocode: string;\n remoteOnly: string;\n // Null when nothing is gone.\n gone: { what: string; reason: string } | null;\n // Where the behavior is proved (test names), or why it is read from source only.\n evidence: string;\n}\n\nconst MCP_HTTP_REASON =\n 'Serverless harnesses reach MCP servers over HTTP; stdio servers need a host.';\nconst BACKGROUND_REASON =\n 'Background commands stop when the session goes idle and parks, or when it is cancelled.';\nconst SUBAGENT_REASON = 'Serverless sessions run one agent; there is no subagent runtime.';\nconst IMAGE_REASON =\n 'Images attached to a message are not sent to the serverless engines yet; image tool results are.';\n\nexport const NANOCODE_FIDELITY = {\n [NanocodeEngine.Codex]: [\n {\n feature: 'Plan mode',\n nanocode: '`update_plan` is always offered and shown as the session plan.',\n remoteOnly: \"Codex's own plan and collaboration modes, as your CLI configures them.\",\n gone: {\n what: 'Plan mode (read-only planning, plan-mode questions).',\n reason: 'Serverless Codex has no plan mode; it shares its plan as a checklist instead.',\n },\n evidence: 'nanocode codex.test.ts \"applies a patch \u2026 and projects update_plan\", \"fidelity\"',\n },\n {\n feature: 'Hooks',\n nanocode: 'None (the engine compaction hook is refused by the host).',\n remoteOnly: 'Hooks and `notify` from your `CODEX_HOME` config.',\n gone: {\n what: 'Hooks.',\n reason: 'Hooks run commands on your machine; a serverless session has none of yours.',\n },\n evidence: 'source: nanocode host-bridge.ts beforeCompaction; runtime codex config.ts',\n },\n {\n feature: 'Slash commands',\n nanocode: 'None: a leading `/` reaches the model as text.',\n remoteOnly: 'None either: the app-server has no slash-command surface.',\n gone: {\n what: 'Slash commands.',\n reason: 'Slash commands belong to the Codex terminal; ask for the action in words.',\n },\n evidence: 'source: nanocode session.ts prompt path',\n },\n {\n feature: 'Custom subagents',\n nanocode: 'None (the host refuses engine subagent routes).',\n remoteOnly: \"Codex's multi-agent tools from your CLI.\",\n gone: { what: 'Subagents.', reason: SUBAGENT_REASON },\n evidence: 'nanocode codex.test.ts \"fidelity\" (no spawn_agent offered); host-bridge.ts',\n },\n {\n feature: 'Background tasks',\n nanocode:\n '`exec_command` returns a session id after its yield time and `write_stdin` polls it, with streamed output, while the session is live.',\n remoteOnly: 'Processes on your host, kept by the Codex app-server.',\n gone: {\n what: 'Processes outliving an idle (parked) or cancelled session.',\n reason: BACKGROUND_REASON,\n },\n evidence:\n 'nanocode codex.test.ts \"runs exec_command \u2026 under never\" (streaming); source only: tools/exec.ts (yield and polling)',\n },\n {\n feature: 'Codex rollouts and `thread/resume`',\n nanocode:\n 'The conversation persists in the Final session and continues after a reconnect; accepted turns are resubmitted.',\n remoteOnly: 'Rollout files in your `CODEX_HOME` and `thread/resume` of them.',\n gone: {\n what: 'Rollout files, and continuing an existing CLI thread.',\n reason: 'History lives in the Final session, not in Codex rollout files.',\n },\n evidence:\n 'nanocode codex.test.ts \"continues the conversation \u2026 after a takeover\", \"submits a turn \u2026 again after a takeover\"',\n },\n {\n feature: 'Session fork',\n nanocode: 'None (`supports.fork` is false).',\n remoteOnly: 'None (remote-codex does not fork either).',\n gone: { what: 'Forking a session.', reason: 'Codex sessions cannot be forked on Final.' },\n evidence: 'descriptions.test.ts; runtime codex session.ts refuses fork',\n },\n {\n feature: 'MCP servers',\n nanocode:\n \"The definition's HTTP and ACP connections as `mcp__<server>__<tool>` tools, with elicitation; tools only.\",\n remoteOnly: 'Your own `config.toml` servers, stdio included, beside the definition servers.',\n gone: { what: 'stdio servers, MCP resources and prompts.', reason: MCP_HTTP_REASON },\n evidence: 'nanocode conformance.test.ts (mcp over acp/http); codex.test.ts list_changed',\n },\n {\n feature: 'Approval policies',\n nanocode:\n \"When the definition's tool policy is silent, `untrusted` asks before `exec_command` and `apply_patch`; `on-request`, `on-failure` and `never` run without asking.\",\n remoteOnly: \"The app-server's own policies together with `sandboxMode`.\",\n gone: {\n what: 'Escalation on failure and sandbox modes.',\n reason:\n \"Commands already run in the session's remote sandbox, so there is nothing to escalate to.\",\n },\n evidence:\n 'nanocode codex.test.ts \"asks before exec_command under untrusted\u2026\", \"fidelity\" (on-request, on-failure), \"\u2026under never\"; source only: tools/approvals.ts (untrusted apply_patch)',\n },\n {\n feature: 'Images in prompts',\n nanocode:\n 'Not supported (`prompt.image` is absent); `view_image` and MCP image results reach the model.',\n remoteOnly: 'Prompt images, passed to Codex as image input.',\n gone: { what: 'Images attached to a message.', reason: IMAGE_REASON },\n evidence: 'nanocode codex.test.ts \"fidelity\" (initialize capabilities)',\n },\n {\n feature: 'Compaction',\n nanocode:\n 'Automatic, managed by the engine near the context window and shown as a compaction update.',\n remoteOnly: \"Codex's own automatic compaction.\",\n gone: {\n what: 'Compacting on request.',\n reason: 'Context is compacted automatically; there is no manual compact.',\n },\n evidence:\n 'source only: the engine compaction policy; the scripted-provider tests do not reach it',\n },\n {\n feature: 'Native tool set',\n nanocode:\n '`exec_command`, `write_stdin` (shell), `apply_patch`, `view_image` (files) and `update_plan`, on the bound sandbox.',\n remoteOnly:\n \"Codex's full tool set on your host (code mode, web search, its MCP and plugins).\",\n gone: {\n what: 'Every other Codex tool.',\n reason: \"Serverless Codex has a fixed tool set that runs on the session's sandbox.\",\n },\n evidence: 'nanocode codex.test.ts \"fidelity\" (tool list); descriptions.test.ts',\n },\n ],\n [NanocodeEngine.Claude]: [\n {\n feature: 'Plan mode',\n nanocode:\n '`plan` adds a plan-mode section to the system prompt and asks before `Write` and `Bash`, as `default` does.',\n remoteOnly: \"Claude Code's enforced plan mode with `ExitPlanMode`.\",\n gone: {\n what: 'Enforced read-only planning.',\n reason: 'Plan mode on serverless Claude is guidance; edits still ask for approval.',\n },\n evidence: 'nanocode claude.test.ts \"fidelity\" (plan asks for Write and Bash; the write runs)',\n },\n {\n feature: 'Hooks',\n nanocode: 'None.',\n remoteOnly: 'Your hooks, through `settingSources: user, project, local`.',\n gone: {\n what: 'Hooks.',\n reason:\n 'Hooks come from your own Claude Code settings; run Claude Code on your host to keep them.',\n },\n evidence: 'runtime claude-code.test.ts (settings sources); nanocode has no hook host',\n },\n {\n feature: 'Slash commands',\n nanocode: 'None: a leading `/` reaches the model as text.',\n remoteOnly: 'Your user and project commands, through the same settings sources.',\n gone: {\n what: 'Slash commands.',\n reason:\n 'Slash commands are a Claude Code feature; run Claude Code on your host to keep them.',\n },\n evidence: 'source: nanocode session.ts prompt path',\n },\n {\n feature: 'Custom subagents',\n nanocode: 'None (no Task tool; the host refuses engine subagent routes).',\n remoteOnly: 'Your subagents, through the settings sources.',\n gone: { what: 'Subagents.', reason: SUBAGENT_REASON },\n evidence: 'nanocode claude.test.ts \"runs Bash \u2026 asks under default\" (exact tool list)',\n },\n {\n feature: 'Background tasks',\n nanocode:\n '`Bash` returns a session id for a running command and `BashOutput` polls it, while the session is live.',\n remoteOnly: 'Background shells on your host, kept by Claude Code.',\n gone: {\n what: 'Processes outliving an idle (parked) or cancelled session.',\n reason: BACKGROUND_REASON,\n },\n evidence: 'source: nanocode tools/claude.ts; polling is not exercised by a test',\n },\n {\n feature: 'Session resume',\n nanocode:\n 'The conversation persists in the Final session; accepted queued prompts resume when processing continues.',\n remoteOnly: 'Claude Code sessions in your `~/.claude`, resumed by id.',\n gone: {\n what: 'Claude Code session files and `--resume` of them.',\n reason: 'History lives in the Final session, not in Claude Code session files.',\n },\n evidence:\n 'nanocode claude.test.ts \"resumes an accepted queued prompt after the invocation stops\"',\n },\n {\n feature: 'Claude Code session fork',\n nanocode: 'None: `session/fork` is refused.',\n remoteOnly: 'Forking into a new Claude Code session (`forkSession`).',\n gone: {\n what: 'Forking a session.',\n reason: 'Forking needs Claude Code itself; use remote-claude-code to keep it.',\n },\n evidence:\n 'nanocode claude.test.ts \"fidelity\" (no session/fork); runtime claude-code.test.ts \"forks into a new Claude Code session id\"',\n },\n {\n feature: 'MCP servers',\n nanocode:\n \"The definition's HTTP and ACP connections as `mcp__<server>__<tool>` tools, with elicitation; tools only.\",\n remoteOnly:\n 'Your own servers, stdio included, with the definition servers added as `final.<name>`.',\n gone: { what: 'stdio servers, MCP resources and prompts.', reason: MCP_HTTP_REASON },\n evidence: 'nanocode conformance.test.ts (mcp over acp/http)',\n },\n {\n feature: 'Permission modes',\n nanocode:\n \"When the definition's tool policy is silent, `default` and `plan` ask before `Write` and `Bash`; `accept-edits` asks before `Bash` only; `bypass` never asks.\",\n remoteOnly: \"Claude Code's permission modes and the allow and deny rules in your settings.\",\n gone: {\n what: 'Allow and deny rules from Claude Code settings.',\n reason:\n \"Claude Code settings are not imported; define allow, ask and deny rules in the agent definition's tool policy.\",\n },\n evidence: 'nanocode claude.test.ts \"fidelity\" (all four modes)',\n },\n {\n feature: 'Images in prompts',\n nanocode: 'Not supported (`prompt.image` is absent); MCP image results reach the model.',\n remoteOnly: 'Prompt images, passed to Claude Code.',\n gone: { what: 'Images attached to a message.', reason: IMAGE_REASON },\n evidence: 'nanocode claude.test.ts \"fidelity\" (initialize capabilities)',\n },\n {\n feature: 'Compaction',\n nanocode:\n 'Automatic in the engine; client updates are emitted when the engine reports compaction.',\n remoteOnly: \"Claude Code's automatic and manual (`/compact`) compaction.\",\n gone: {\n what: 'Compacting on request.',\n reason: 'The host exposes no manual compact action.',\n },\n evidence: 'source only: the engine compaction threshold; not reached by tests',\n },\n {\n feature: 'Native tool set',\n nanocode: '`Bash`, `BashOutput` (shell), `Read`, `Write` (files), on the bound sandbox.',\n remoteOnly:\n \"Claude Code's full tool set on your host (`Edit`, `Glob`, `Grep`, `Task`, `WebFetch`, `WebSearch`, \u2026).\",\n gone: {\n what: '`Edit`, `Glob`, `Grep`, `Task`, `TodoWrite`, `WebFetch`, `WebSearch`, `NotebookEdit`.',\n reason:\n 'Serverless Claude has a minimal tool set; it searches and edits through the shell.',\n },\n evidence:\n 'nanocode claude.test.ts \"runs Bash on the shell capability and asks under default\" (exact list)',\n },\n ],\n} satisfies Record<NanocodeEngine, FidelityRowIface[]>;\n\nexport const NANOCODE_HARNESS_OF = {\n [NanocodeEngine.Codex]: 'serverless-nanocode-codex',\n [NanocodeEngine.Claude]: 'serverless-nanocode-claude',\n} satisfies Record<NanocodeEngine, string>;\n\nexport const REMOTE_HARNESS_OF = {\n [NanocodeEngine.Codex]: 'remote-codex',\n [NanocodeEngine.Claude]: 'remote-claude-code',\n} satisfies Record<NanocodeEngine, string>;\n\nconst cell = (text: string): string => text.replaceAll('|', '\\\\|').replaceAll('\\n', ' ');\n\n// One engine's table as Markdown (FIDELITY.md and the import review use it as is).\nexport const renderFidelityTable = (engine: NanocodeEngine): string =>\n [\n `| | \\`${NANOCODE_HARNESS_OF[engine]}\\` keeps | only \\`${REMOTE_HARNESS_OF[engine]}\\` keeps | gone, and why |`,\n '|---|---|---|---|',\n ...NANOCODE_FIDELITY[engine].map(\n (row) =>\n `| ${cell(row.feature)} | ${cell(row.nanocode)} | ${cell(row.remoteOnly)} | ${\n row.gone == null ? 'nothing' : cell(`${row.gone.what} ${row.gone.reason}`)\n } |`,\n ),\n ].join('\\n');\n\n// Where each row is proved.\nexport const renderFidelityEvidence = (engine: NanocodeEngine): string =>\n NANOCODE_FIDELITY[engine].map((row) => `- ${row.feature}: ${row.evidence}`).join('\\n');\n\n// The generated part of FIDELITY.md for one engine.\nexport const fidelitySection = (engine: NanocodeEngine): string =>\n `${renderFidelityTable(engine)}\\n\\nEvidence:\\n\\n${renderFidelityEvidence(engine)}`;\n"],
4
+ "sourcesContent": ["// Harness fidelity for the serverless nanocode harnesses per engine, what nanocode\n// keeps, what only the matching remote-* harness keeps (the user's own CLI through\n// @finalai/harness-runtime), and what is gone, with a one-line reason a user can read.\n// Measured from the code and tests named in `evidence`; FIDELITY.md renders these tables, and\n// the legacy import writes the matching one into each moved agent's review file.\n\nexport enum NanocodeEngine {\n Codex = 'codex',\n Claude = 'claude',\n}\n\nexport interface FidelityRowIface {\n feature: string;\n nanocode: string;\n remoteOnly: string;\n // Null when nothing is gone.\n gone: { what: string; reason: string } | null;\n // Where the behavior is proved (test names), or why it is read from source only.\n evidence: string;\n}\n\nconst MCP_HTTP_REASON =\n 'Serverless harnesses reach MCP servers over HTTP; stdio servers need a host.';\nconst BACKGROUND_REASON =\n 'Background commands stop when the session goes idle and parks, or when it is cancelled.';\nconst SUBAGENT_REASON = 'Serverless sessions run one agent; there is no subagent runtime.';\nconst IMAGE_REASON =\n 'Images attached to a message are not sent to the serverless engines yet; image tool results are.';\n\nexport const NANOCODE_FIDELITY = {\n [NanocodeEngine.Codex]: [\n {\n feature: 'Plan mode',\n nanocode: '`update_plan` is always offered and shown as the session plan.',\n remoteOnly: \"Codex's own plan and collaboration modes, as your CLI configures them.\",\n gone: {\n what: 'Plan mode (read-only planning, plan-mode questions).',\n reason: 'Serverless Codex has no plan mode; it shares its plan as a checklist instead.',\n },\n evidence: 'nanocode codex.test.ts \"applies a patch \u2026 and projects update_plan\", \"fidelity\"',\n },\n {\n feature: 'Hooks',\n nanocode: 'None (the engine compaction hook is refused by the host).',\n remoteOnly: 'Hooks and `notify` from your `CODEX_HOME` config.',\n gone: {\n what: 'Hooks.',\n reason: 'Hooks run commands on your machine; a serverless session has none of yours.',\n },\n evidence: 'source: nanocode host-bridge.ts beforeCompaction; runtime codex config.ts',\n },\n {\n feature: 'Slash commands',\n nanocode: 'None: a leading `/` reaches the model as text.',\n remoteOnly: 'None either: the app-server has no slash-command surface.',\n gone: {\n what: 'Slash commands.',\n reason: 'Slash commands belong to the Codex terminal; ask for the action in words.',\n },\n evidence: 'source: nanocode session.ts prompt path',\n },\n {\n feature: 'Custom subagents',\n nanocode: 'None (the host refuses engine subagent routes).',\n remoteOnly: \"Codex's multi-agent tools from your CLI.\",\n gone: { what: 'Subagents.', reason: SUBAGENT_REASON },\n evidence: 'nanocode codex.test.ts \"fidelity\" (no spawn_agent offered); host-bridge.ts',\n },\n {\n feature: 'Background tasks',\n nanocode:\n '`exec_command` returns a session id after its yield time and `write_stdin` polls it, with streamed output, while the session is live.',\n remoteOnly: 'Processes on your host, kept by the Codex app-server.',\n gone: {\n what: 'Processes outliving an idle (parked) or cancelled session.',\n reason: BACKGROUND_REASON,\n },\n evidence:\n 'nanocode codex.test.ts \"runs exec_command \u2026 under never\" (streaming); source only: tools/exec.ts (yield and polling)',\n },\n {\n feature: 'Codex rollouts and `thread/resume`',\n nanocode:\n 'The conversation persists in the Final session and continues after a reconnect; accepted turns are resubmitted.',\n remoteOnly: 'Rollout files in your `CODEX_HOME` and `thread/resume` of them.',\n gone: {\n what: 'Rollout files, and continuing an existing CLI thread.',\n reason: 'History lives in the Final session, not in Codex rollout files.',\n },\n evidence:\n 'nanocode codex.test.ts \"continues the conversation \u2026 after a takeover\", \"submits a turn \u2026 again after a takeover\"',\n },\n {\n feature: 'Session fork',\n nanocode: 'None (`supports.fork` is false).',\n remoteOnly: 'None (remote-codex does not fork either).',\n gone: { what: 'Forking a session.', reason: 'Codex sessions cannot be forked on Final.' },\n evidence: 'descriptions.test.ts; runtime codex session.ts refuses fork',\n },\n {\n feature: 'MCP servers',\n nanocode:\n \"The definition's HTTP and ACP connections as `mcp__<server>__<tool>` tools, with elicitation; tools only.\",\n remoteOnly: 'Your own `config.toml` servers, stdio included, beside the definition servers.',\n gone: { what: 'stdio servers, MCP resources and prompts.', reason: MCP_HTTP_REASON },\n evidence: 'nanocode conformance.test.ts (mcp over acp/http); codex.test.ts list_changed',\n },\n {\n feature: 'Approval policies',\n nanocode:\n \"When the definition's tool policy is silent, `untrusted` asks before `exec_command` and `apply_patch`; `on-request`, `on-failure` and `never` run without asking. With `approvalsReviewer: 'auto-review'`, a reviewer model (`gpt-6-luna`, falling back to the session's model) answers what `untrusted` would ask: low and medium risk run, a rejected action goes to the person with the reviewer's reason, and a critical one is denied.\",\n remoteOnly: \"The app-server's own policies together with `sandboxMode`.\",\n gone: {\n what: 'Escalation on failure and sandbox modes.',\n reason:\n \"Commands already run in the session's remote sandbox, so there is nothing to escalate to.\",\n },\n evidence:\n 'nanocode codex.test.ts \"asks before exec_command under untrusted\u2026\", \"fidelity\" (on-request, on-failure), \"\u2026under never\"; source only: tools/approvals.ts (untrusted apply_patch)',\n },\n {\n feature: 'Images in prompts',\n nanocode:\n 'Not supported (`prompt.image` is absent); `view_image` and MCP image results reach the model.',\n remoteOnly: 'Prompt images, passed to Codex as image input.',\n gone: { what: 'Images attached to a message.', reason: IMAGE_REASON },\n evidence: 'nanocode codex.test.ts \"fidelity\" (initialize capabilities)',\n },\n {\n feature: 'Compaction',\n nanocode:\n 'Automatic, managed by the engine near the context window and shown as a compaction update.',\n remoteOnly: \"Codex's own automatic compaction.\",\n gone: {\n what: 'Compacting on request.',\n reason: 'Context is compacted automatically; there is no manual compact.',\n },\n evidence:\n 'source only: the engine compaction policy; the scripted-provider tests do not reach it',\n },\n {\n feature: 'Native tool set',\n nanocode:\n '`exec_command`, `write_stdin` (shell), `apply_patch`, `view_image` (files) and `update_plan`, on the bound sandbox.',\n remoteOnly:\n \"Codex's full tool set on your host (code mode, web search, its MCP and plugins).\",\n gone: {\n what: 'Every other Codex tool.',\n reason: \"Serverless Codex has a fixed tool set that runs on the session's sandbox.\",\n },\n evidence: 'nanocode codex.test.ts \"fidelity\" (tool list); descriptions.test.ts',\n },\n ],\n [NanocodeEngine.Claude]: [\n {\n feature: 'Plan mode',\n nanocode:\n '`plan` adds a plan-mode section to the system prompt and asks before `Write` and `Bash`, as `default` does.',\n remoteOnly: \"Claude Code's enforced plan mode with `ExitPlanMode`.\",\n gone: {\n what: 'Enforced read-only planning.',\n reason: 'Plan mode on serverless Claude is guidance; edits still ask for approval.',\n },\n evidence: 'nanocode claude.test.ts \"fidelity\" (plan asks for Write and Bash; the write runs)',\n },\n {\n feature: 'Hooks',\n nanocode: 'None.',\n remoteOnly: 'Your hooks, through `settingSources: user, project, local`.',\n gone: {\n what: 'Hooks.',\n reason:\n 'Hooks come from your own Claude Code settings; run Claude Code on your host to keep them.',\n },\n evidence: 'runtime claude-code.test.ts (settings sources); nanocode has no hook host',\n },\n {\n feature: 'Slash commands',\n nanocode: 'None: a leading `/` reaches the model as text.',\n remoteOnly: 'Your user and project commands, through the same settings sources.',\n gone: {\n what: 'Slash commands.',\n reason:\n 'Slash commands are a Claude Code feature; run Claude Code on your host to keep them.',\n },\n evidence: 'source: nanocode session.ts prompt path',\n },\n {\n feature: 'Custom subagents',\n nanocode: 'None (no Task tool; the host refuses engine subagent routes).',\n remoteOnly: 'Your subagents, through the settings sources.',\n gone: { what: 'Subagents.', reason: SUBAGENT_REASON },\n evidence: 'nanocode claude.test.ts \"runs Bash \u2026 asks under default\" (exact tool list)',\n },\n {\n feature: 'Background tasks',\n nanocode:\n '`Bash` returns a session id for a running command and `BashOutput` polls it, while the session is live.',\n remoteOnly: 'Background shells on your host, kept by Claude Code.',\n gone: {\n what: 'Processes outliving an idle (parked) or cancelled session.',\n reason: BACKGROUND_REASON,\n },\n evidence: 'source: nanocode tools/claude.ts; polling is not exercised by a test',\n },\n {\n feature: 'Session resume',\n nanocode:\n 'The conversation persists in the Final session; accepted queued prompts resume when processing continues.',\n remoteOnly: 'Claude Code sessions in your `~/.claude`, resumed by id.',\n gone: {\n what: 'Claude Code session files and `--resume` of them.',\n reason: 'History lives in the Final session, not in Claude Code session files.',\n },\n evidence:\n 'nanocode claude.test.ts \"resumes an accepted queued prompt after the invocation stops\"',\n },\n {\n feature: 'Claude Code session fork',\n nanocode: 'None: `session/fork` is refused.',\n remoteOnly: 'Forking into a new Claude Code session (`forkSession`).',\n gone: {\n what: 'Forking a session.',\n reason: 'Forking needs Claude Code itself; use remote-claude-code to keep it.',\n },\n evidence:\n 'nanocode claude.test.ts \"fidelity\" (no session/fork); runtime claude-code.test.ts \"forks into a new Claude Code session id\"',\n },\n {\n feature: 'MCP servers',\n nanocode:\n \"The definition's HTTP and ACP connections as `mcp__<server>__<tool>` tools, with elicitation; tools only.\",\n remoteOnly:\n 'Your own servers, stdio included, with the definition servers added as `final.<name>`.',\n gone: { what: 'stdio servers, MCP resources and prompts.', reason: MCP_HTTP_REASON },\n evidence: 'nanocode conformance.test.ts (mcp over acp/http)',\n },\n {\n feature: 'Permission modes',\n nanocode:\n \"When the definition's tool policy is silent, `default` and `plan` ask before `Write` and `Bash`; `accept-edits` asks before `Bash` only; `bypass` never asks; `auto` has a reviewer model (`claude-sonnet-5`, falling back to the session's model) judge `Write` and `Bash`: low and medium risk run, a rejected action goes to the person with the reviewer's reason, and a critical one is denied.\",\n remoteOnly: \"Claude Code's permission modes and the allow and deny rules in your settings.\",\n gone: {\n what: 'Allow and deny rules from Claude Code settings.',\n reason:\n \"Claude Code settings are not imported; define allow, ask and deny rules in the agent definition's tool policy.\",\n },\n evidence: 'nanocode claude.test.ts \"fidelity\" (all four modes)',\n },\n {\n feature: 'Images in prompts',\n nanocode: 'Not supported (`prompt.image` is absent); MCP image results reach the model.',\n remoteOnly: 'Prompt images, passed to Claude Code.',\n gone: { what: 'Images attached to a message.', reason: IMAGE_REASON },\n evidence: 'nanocode claude.test.ts \"fidelity\" (initialize capabilities)',\n },\n {\n feature: 'Compaction',\n nanocode:\n 'Automatic in the engine; client updates are emitted when the engine reports compaction.',\n remoteOnly: \"Claude Code's automatic and manual (`/compact`) compaction.\",\n gone: {\n what: 'Compacting on request.',\n reason: 'The host exposes no manual compact action.',\n },\n evidence: 'source only: the engine compaction threshold; not reached by tests',\n },\n {\n feature: 'Native tool set',\n nanocode: '`Bash`, `BashOutput` (shell), `Read`, `Write` (files), on the bound sandbox.',\n remoteOnly:\n \"Claude Code's full tool set on your host (`Edit`, `Glob`, `Grep`, `Task`, `WebFetch`, `WebSearch`, \u2026).\",\n gone: {\n what: '`Edit`, `Glob`, `Grep`, `Task`, `TodoWrite`, `WebFetch`, `WebSearch`, `NotebookEdit`.',\n reason:\n 'Serverless Claude has a minimal tool set; it searches and edits through the shell.',\n },\n evidence:\n 'nanocode claude.test.ts \"runs Bash on the shell capability and asks under default\" (exact list)',\n },\n ],\n} satisfies Record<NanocodeEngine, FidelityRowIface[]>;\n\nexport const NANOCODE_HARNESS_OF = {\n [NanocodeEngine.Codex]: 'serverless-nanocode-codex',\n [NanocodeEngine.Claude]: 'serverless-nanocode-claude',\n} satisfies Record<NanocodeEngine, string>;\n\nexport const REMOTE_HARNESS_OF = {\n [NanocodeEngine.Codex]: 'remote-codex',\n [NanocodeEngine.Claude]: 'remote-claude-code',\n} satisfies Record<NanocodeEngine, string>;\n\nconst cell = (text: string): string => text.replaceAll('|', '\\\\|').replaceAll('\\n', ' ');\n\n// One engine's table as Markdown (FIDELITY.md and the import review use it as is).\nexport const renderFidelityTable = (engine: NanocodeEngine): string =>\n [\n `| | \\`${NANOCODE_HARNESS_OF[engine]}\\` keeps | only \\`${REMOTE_HARNESS_OF[engine]}\\` keeps | gone, and why |`,\n '|---|---|---|---|',\n ...NANOCODE_FIDELITY[engine].map(\n (row) =>\n `| ${cell(row.feature)} | ${cell(row.nanocode)} | ${cell(row.remoteOnly)} | ${\n row.gone == null ? 'nothing' : cell(`${row.gone.what} ${row.gone.reason}`)\n } |`,\n ),\n ].join('\\n');\n\n// Where each row is proved.\nexport const renderFidelityEvidence = (engine: NanocodeEngine): string =>\n NANOCODE_FIDELITY[engine].map((row) => `- ${row.feature}: ${row.evidence}`).join('\\n');\n\n// The generated part of FIDELITY.md for one engine.\nexport const fidelitySection = (engine: NanocodeEngine): string =>\n `${renderFidelityTable(engine)}\\n\\nEvidence:\\n\\n${renderFidelityEvidence(engine)}`;\n"],
5
5
  "mappings": "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",
6
6
  "names": ["NanocodeEngine"]
7
7
  }
@@ -3,6 +3,13 @@ import * as z from 'zod';
3
3
  import { ClaudePermissionMode, type CredentialsInput, type HarnessBindingOutputIface, type InstructionInput, type SecretRefIface } from "./config-fields.mjs";
4
4
  import { type RemoteClientTlsInputIface } from "./remote-client-tls.mjs";
5
5
  export declare const SPECIFIER = "@finalai/final-agent-provider/harnesses/remote-claude-code";
6
+ export declare const remotePermissionModeSchema: z.ZodEnum<{
7
+ readonly Default: ClaudePermissionMode.Default;
8
+ readonly Plan: ClaudePermissionMode.Plan;
9
+ readonly AcceptEdits: ClaudePermissionMode.AcceptEdits;
10
+ readonly Bypass: ClaudePermissionMode.Bypass;
11
+ }>;
12
+ export type RemoteClaudePermissionMode = z.output<typeof remotePermissionModeSchema>;
6
13
  export declare const remoteClaudeCodeConfigSchema: z.ZodObject<{
7
14
  url: z.ZodString;
8
15
  tls: z.ZodOptional<z.ZodObject<{
@@ -50,7 +57,12 @@ export declare const remoteClaudeCodeConfigSchema: z.ZodObject<{
50
57
  events: z.ZodDefault<z.ZodArray<z.ZodEnum<typeof import("@finalai/agent-provider").DynamicEvent>>>;
51
58
  }, z.core.$strip>]>>;
52
59
  }, z.core.$strip>;
53
- permissionMode: z.ZodOptional<z.ZodEnum<typeof ClaudePermissionMode>>;
60
+ permissionMode: z.ZodOptional<z.ZodEnum<{
61
+ readonly Default: ClaudePermissionMode.Default;
62
+ readonly Plan: ClaudePermissionMode.Plan;
63
+ readonly AcceptEdits: ClaudePermissionMode.AcceptEdits;
64
+ readonly Bypass: ClaudePermissionMode.Bypass;
65
+ }>>;
54
66
  instructions: z.ZodDefault<z.ZodArray<z.ZodObject<{
55
67
  role: z.ZodEnum<typeof import("@finalai/agent-provider").InstructionRole>;
56
68
  content: z.ZodString;
@@ -69,7 +81,7 @@ export interface RemoteClaudeCodeConfigInputIface {
69
81
  executable?: string;
70
82
  model?: string;
71
83
  credentials: CredentialsInput;
72
- permissionMode?: ClaudePermissionMode | `${ClaudePermissionMode}`;
84
+ permissionMode?: RemoteClaudePermissionMode | `${RemoteClaudePermissionMode}`;
73
85
  instructions?: InstructionInput[];
74
86
  allowedTools?: string[];
75
87
  }
@@ -30,6 +30,7 @@ import {
30
30
  } from "./remote-client-tls.mjs";
31
31
  import { remoteUrlJson, remoteUrlZod } from "./remote-url.mjs";
32
32
  const SPECIFIER = "@finalai/final-agent-provider/harnesses/remote-claude-code";
33
+ const remotePermissionModeSchema = z.nativeEnum(ClaudePermissionMode).exclude(["Auto"]);
33
34
  const remoteClaudeCodeConfigSchema = z.strictObject({
34
35
  url: remoteUrlZod(),
35
36
  tls: remoteClientTlsSchema.optional(),
@@ -38,7 +39,7 @@ const remoteClaudeCodeConfigSchema = z.strictObject({
38
39
  executable: z.string().min(1).optional(),
39
40
  model: z.string().min(1).optional(),
40
41
  credentials: credentialsZod(),
41
- permissionMode: z.nativeEnum(ClaudePermissionMode).optional(),
42
+ permissionMode: remotePermissionModeSchema.optional(),
42
43
  instructions: instructionsZod(),
43
44
  allowedTools: z.array(z.string().min(1)).optional()
44
45
  });
@@ -56,7 +57,7 @@ const REMOTE_CLAUDE_CODE_CONFIG_JSON_SCHEMA = configObjectJson({
56
57
  ...credentialsJson(),
57
58
  default: { payer: "agent-owner", tryInOrder: [{ kind: "harness-owned" }] }
58
59
  },
59
- permissionMode: permissionModeJson(Object.values(ClaudePermissionMode)),
60
+ permissionMode: permissionModeJson(Object.values(remotePermissionModeSchema.enum)),
60
61
  instructions: instructionsJson(),
61
62
  allowedTools: { type: "array", title: "Allowed tools", items: { type: "string" } }
62
63
  }
@@ -106,6 +107,7 @@ export {
106
107
  REMOTE_CLAUDE_CODE_DESCRIPTION,
107
108
  SPECIFIER,
108
109
  remoteClaudeCode,
109
- remoteClaudeCodeConfigSchema
110
+ remoteClaudeCodeConfigSchema,
111
+ remotePermissionModeSchema
110
112
  };
111
113
  //# sourceMappingURL=remote-claude-code.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/harnesses/remote-claude-code.ts"],
4
- "sourcesContent": ["import {\n AcpTransport,\n BaseCapability,\n BaseToolProvision,\n CredentialDelivery,\n InferenceCredentialKind,\n McpTransportKind,\n PromptWhileRunning,\n SandboxWire,\n type HarnessDescriptionIface,\n type JsonSchema,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n absolutePathJson,\n absolutePathZod,\n ClaudePermissionMode,\n configObjectJson,\n credentialsJson,\n credentialsZod,\n harnessBinding,\n instructionsJson,\n instructionsZod,\n permissionModeJson,\n secretRefJson,\n secretRefZod,\n type CredentialsInput,\n type HarnessBindingOutputIface,\n type InstructionInput,\n type SecretRefIface,\n} from \"./config-fields.mjs\";\nimport { ModelVendor } from \"./model-reference.mjs\";\nimport {\n remoteClientTlsJson,\n remoteClientTlsSchema,\n type RemoteClientTlsInputIface,\n} from \"./remote-client-tls.mjs\";\nimport { remoteUrlJson, remoteUrlZod } from \"./remote-url.mjs\";\n\n// Claude Code (Agent SDK) behind the Final ACP adapter, served by\n// `final-harness serve --program claude-code` on a host you run.\n\nexport const SPECIFIER = '@finalai/final-agent-provider/harnesses/remote-claude-code';\n\nexport const remoteClaudeCodeConfigSchema = z.strictObject({\n url: remoteUrlZod(),\n tls: remoteClientTlsSchema.optional(),\n hop: secretRefZod(),\n cwd: absolutePathZod().optional(),\n executable: z.string().min(1).optional(),\n model: z.string().min(1).optional(),\n credentials: credentialsZod(),\n permissionMode: z.nativeEnum(ClaudePermissionMode).optional(),\n instructions: instructionsZod(),\n allowedTools: z.array(z.string().min(1)).optional(),\n});\n\nexport type RemoteClaudeCodeConfigIface = z.output<typeof remoteClaudeCodeConfigSchema>;\n\nexport const REMOTE_CLAUDE_CODE_CONFIG_JSON_SCHEMA: JsonSchema = configObjectJson({\n title: 'Claude Code (your host)',\n required: ['credentials', 'url', 'hop'],\n properties: {\n url: remoteUrlJson('Runtime URL'),\n tls: remoteClientTlsJson(),\n hop: secretRefJson(),\n cwd: absolutePathJson('Working directory on your host'),\n executable: { type: 'string', title: 'Installed executable on your host' },\n model: { $ref: 'final://schemas/model-picker' },\n credentials: {\n ...credentialsJson(),\n default: { payer: 'agent-owner', tryInOrder: [{ kind: 'harness-owned' }] },\n },\n permissionMode: permissionModeJson(Object.values(ClaudePermissionMode)),\n instructions: instructionsJson(),\n allowedTools: { type: 'array', title: 'Allowed tools', items: { type: 'string' } },\n },\n});\n\nexport const REMOTE_CLAUDE_CODE_DESCRIPTION: HarnessDescriptionIface = {\n configSchema: REMOTE_CLAUDE_CODE_CONFIG_JSON_SCHEMA,\n acp: {\n transports: [AcpTransport.Ws, AcpTransport.Http],\n mcp: [McpTransportKind.Acp, McpTransportKind.Http, McpTransportKind.Stdio],\n },\n // Claude Code's own tools on your host, switched through its allow-lists.\n baseTools: {\n [BaseCapability.Shell]: {\n provision: BaseToolProvision.HostDisableable,\n nativeTools: ['Bash', 'BashOutput', 'KillShell'],\n },\n [BaseCapability.Files]: {\n provision: BaseToolProvision.HostDisableable,\n nativeTools: ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'NotebookEdit'],\n },\n },\n // Other bound targets reach it as the sandbox MCP projection.\n consumes: [SandboxWire.Mcp],\n inference: {\n vendors: [ModelVendor.Anthropic],\n kinds: [\n InferenceCredentialKind.HarnessOwned,\n InferenceCredentialKind.Subscription,\n InferenceCredentialKind.ApiKey,\n InferenceCredentialKind.Platform,\n ],\n delivery: CredentialDelivery.AtOpen,\n },\n supports: {\n observe: true,\n resumeReplay: true,\n fork: true,\n authorMeta: true,\n nativeToolPolicy: false,\n },\n promptWhileRunning: PromptWhileRunning.Inject,\n sessionSurvivesDisconnect: true,\n};\n\nexport interface RemoteClaudeCodeConfigInputIface {\n url: string;\n tls?: RemoteClientTlsInputIface;\n hop: SecretRefIface;\n cwd?: string;\n executable?: string;\n model?: string;\n credentials: CredentialsInput;\n permissionMode?: ClaudePermissionMode | `${ClaudePermissionMode}`;\n instructions?: InstructionInput[];\n allowedTools?: string[];\n}\n\nexport const remoteClaudeCode = (\n config: RemoteClaudeCodeConfigInputIface,\n): HarnessBindingOutputIface => harnessBinding(SPECIFIER, config);\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n AcpTransport,\n BaseCapability,\n BaseToolProvision,\n CredentialDelivery,\n InferenceCredentialKind,\n McpTransportKind,\n PromptWhileRunning,\n SandboxWire,\n type HarnessDescriptionIface,\n type JsonSchema,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n absolutePathJson,\n absolutePathZod,\n ClaudePermissionMode,\n configObjectJson,\n credentialsJson,\n credentialsZod,\n harnessBinding,\n instructionsJson,\n instructionsZod,\n permissionModeJson,\n secretRefJson,\n secretRefZod,\n type CredentialsInput,\n type HarnessBindingOutputIface,\n type InstructionInput,\n type SecretRefIface,\n} from \"./config-fields.mjs\";\nimport { ModelVendor } from \"./model-reference.mjs\";\nimport {\n remoteClientTlsJson,\n remoteClientTlsSchema,\n type RemoteClientTlsInputIface,\n} from \"./remote-client-tls.mjs\";\nimport { remoteUrlJson, remoteUrlZod } from \"./remote-url.mjs\";\n\n// Claude Code (Agent SDK) behind the Final ACP adapter, served by\n// `final-harness serve --program claude-code` on a host you run.\n\nexport const SPECIFIER = '@finalai/final-agent-provider/harnesses/remote-claude-code';\n\n// Auto-review is the nanocode Claude engine's; the Claude Code CLI behind the adapter takes\n// the other modes.\nexport const remotePermissionModeSchema = z.nativeEnum(ClaudePermissionMode).exclude(['Auto']);\n\nexport type RemoteClaudePermissionMode = z.output<typeof remotePermissionModeSchema>;\n\nexport const remoteClaudeCodeConfigSchema = z.strictObject({\n url: remoteUrlZod(),\n tls: remoteClientTlsSchema.optional(),\n hop: secretRefZod(),\n cwd: absolutePathZod().optional(),\n executable: z.string().min(1).optional(),\n model: z.string().min(1).optional(),\n credentials: credentialsZod(),\n permissionMode: remotePermissionModeSchema.optional(),\n instructions: instructionsZod(),\n allowedTools: z.array(z.string().min(1)).optional(),\n});\n\nexport type RemoteClaudeCodeConfigIface = z.output<typeof remoteClaudeCodeConfigSchema>;\n\nexport const REMOTE_CLAUDE_CODE_CONFIG_JSON_SCHEMA: JsonSchema = configObjectJson({\n title: 'Claude Code (your host)',\n required: ['credentials', 'url', 'hop'],\n properties: {\n url: remoteUrlJson('Runtime URL'),\n tls: remoteClientTlsJson(),\n hop: secretRefJson(),\n cwd: absolutePathJson('Working directory on your host'),\n executable: { type: 'string', title: 'Installed executable on your host' },\n model: { $ref: 'final://schemas/model-picker' },\n credentials: {\n ...credentialsJson(),\n default: { payer: 'agent-owner', tryInOrder: [{ kind: 'harness-owned' }] },\n },\n permissionMode: permissionModeJson(Object.values(remotePermissionModeSchema.enum)),\n instructions: instructionsJson(),\n allowedTools: { type: 'array', title: 'Allowed tools', items: { type: 'string' } },\n },\n});\n\nexport const REMOTE_CLAUDE_CODE_DESCRIPTION: HarnessDescriptionIface = {\n configSchema: REMOTE_CLAUDE_CODE_CONFIG_JSON_SCHEMA,\n acp: {\n transports: [AcpTransport.Ws, AcpTransport.Http],\n mcp: [McpTransportKind.Acp, McpTransportKind.Http, McpTransportKind.Stdio],\n },\n // Claude Code's own tools on your host, switched through its allow-lists.\n baseTools: {\n [BaseCapability.Shell]: {\n provision: BaseToolProvision.HostDisableable,\n nativeTools: ['Bash', 'BashOutput', 'KillShell'],\n },\n [BaseCapability.Files]: {\n provision: BaseToolProvision.HostDisableable,\n nativeTools: ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'NotebookEdit'],\n },\n },\n // Other bound targets reach it as the sandbox MCP projection.\n consumes: [SandboxWire.Mcp],\n inference: {\n vendors: [ModelVendor.Anthropic],\n kinds: [\n InferenceCredentialKind.HarnessOwned,\n InferenceCredentialKind.Subscription,\n InferenceCredentialKind.ApiKey,\n InferenceCredentialKind.Platform,\n ],\n delivery: CredentialDelivery.AtOpen,\n },\n supports: {\n observe: true,\n resumeReplay: true,\n fork: true,\n authorMeta: true,\n nativeToolPolicy: false,\n },\n promptWhileRunning: PromptWhileRunning.Inject,\n sessionSurvivesDisconnect: true,\n};\n\nexport interface RemoteClaudeCodeConfigInputIface {\n url: string;\n tls?: RemoteClientTlsInputIface;\n hop: SecretRefIface;\n cwd?: string;\n executable?: string;\n model?: string;\n credentials: CredentialsInput;\n permissionMode?: RemoteClaudePermissionMode | `${RemoteClaudePermissionMode}`;\n instructions?: InstructionInput[];\n allowedTools?: string[];\n}\n\nexport const remoteClaudeCode = (\n config: RemoteClaudeCodeConfigInputIface,\n): HarnessBindingOutputIface => harnessBinding(SPECIFIER, config);\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,YAAY,OAAO;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AACP,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,eAAe,oBAAoB;AAKrC,MAAM,YAAY;AAIlB,MAAM,6BAA6B,EAAE,WAAW,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC;AAItF,MAAM,+BAA+B,EAAE,aAAa;AAAA,EACzD,KAAK,aAAa;AAAA,EAClB,KAAK,sBAAsB,SAAS;AAAA,EACpC,KAAK,aAAa;AAAA,EAClB,KAAK,gBAAgB,EAAE,SAAS;AAAA,EAChC,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACvC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAClC,aAAa,eAAe;AAAA,EAC5B,gBAAgB,2BAA2B,SAAS;AAAA,EACpD,cAAc,gBAAgB;AAAA,EAC9B,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS;AACpD,CAAC;AAIM,MAAM,wCAAoD,iBAAiB;AAAA,EAChF,OAAO;AAAA,EACP,UAAU,CAAC,eAAe,OAAO,KAAK;AAAA,EACtC,YAAY;AAAA,IACV,KAAK,cAAc,aAAa;AAAA,IAChC,KAAK,oBAAoB;AAAA,IACzB,KAAK,cAAc;AAAA,IACnB,KAAK,iBAAiB,gCAAgC;AAAA,IACtD,YAAY,EAAE,MAAM,UAAU,OAAO,oCAAoC;AAAA,IACzE,OAAO,EAAE,MAAM,+BAA+B;AAAA,IAC9C,aAAa;AAAA,MACX,GAAG,gBAAgB;AAAA,MACnB,SAAS,EAAE,OAAO,eAAe,YAAY,CAAC,EAAE,MAAM,gBAAgB,CAAC,EAAE;AAAA,IAC3E;AAAA,IACA,gBAAgB,mBAAmB,OAAO,OAAO,2BAA2B,IAAI,CAAC;AAAA,IACjF,cAAc,iBAAiB;AAAA,IAC/B,cAAc,EAAE,MAAM,SAAS,OAAO,iBAAiB,OAAO,EAAE,MAAM,SAAS,EAAE;AAAA,EACnF;AACF,CAAC;AAEM,MAAM,iCAA0D;AAAA,EACrE,cAAc;AAAA,EACd,KAAK;AAAA,IACH,YAAY,CAAC,aAAa,IAAI,aAAa,IAAI;AAAA,IAC/C,KAAK,CAAC,iBAAiB,KAAK,iBAAiB,MAAM,iBAAiB,KAAK;AAAA,EAC3E;AAAA;AAAA,EAEA,WAAW;AAAA,IACT,CAAC,eAAe,KAAK,GAAG;AAAA,MACtB,WAAW,kBAAkB;AAAA,MAC7B,aAAa,CAAC,QAAQ,cAAc,WAAW;AAAA,IACjD;AAAA,IACA,CAAC,eAAe,KAAK,GAAG;AAAA,MACtB,WAAW,kBAAkB;AAAA,MAC7B,aAAa,CAAC,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,cAAc;AAAA,IACvE;AAAA,EACF;AAAA;AAAA,EAEA,UAAU,CAAC,YAAY,GAAG;AAAA,EAC1B,WAAW;AAAA,IACT,SAAS,CAAC,YAAY,SAAS;AAAA,IAC/B,OAAO;AAAA,MACL,wBAAwB;AAAA,MACxB,wBAAwB;AAAA,MACxB,wBAAwB;AAAA,MACxB,wBAAwB;AAAA,IAC1B;AAAA,IACA,UAAU,mBAAmB;AAAA,EAC/B;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,cAAc;AAAA,IACd,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,kBAAkB;AAAA,EACpB;AAAA,EACA,oBAAoB,mBAAmB;AAAA,EACvC,2BAA2B;AAC7B;AAeO,MAAM,mBAAmB,CAC9B,WAC8B,eAAe,WAAW,MAAM;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { type HarnessDescriptionIface, type JsonSchema } from '@finalai/agent-provider';
2
2
  import * as z from 'zod';
3
- import { CodexApprovalPolicy, ReasoningLevel, type CredentialsInput, type HarnessBindingOutputIface, type InstructionInput, type SkillInput } from "./config-fields.mjs";
3
+ import { CodexApprovalPolicy, CodexApprovalsReviewer, ReasoningLevel, type CredentialsInput, type HarnessBindingOutputIface, type InstructionInput, type SkillInput } from "./config-fields.mjs";
4
4
  export declare const SPECIFIER = "@finalai/final-agent-provider/harnesses/serverless-nanocode-codex";
5
5
  export declare const CODEX_REASONING_LEVELS: readonly [ReasoningLevel.Low, ReasoningLevel.Medium, ReasoningLevel.High, ReasoningLevel.Max];
6
6
  export declare const CODEX_BASE_TOOLS: HarnessDescriptionIface['baseTools'];
@@ -36,6 +36,7 @@ export declare const serverlessNanocodeCodexConfigSchema: z.ZodObject<{
36
36
  }, z.core.$strip>]>>;
37
37
  }, z.core.$strip>;
38
38
  approvalPolicy: z.ZodDefault<z.ZodEnum<typeof CodexApprovalPolicy>>;
39
+ approvalsReviewer: z.ZodDefault<z.ZodEnum<typeof CodexApprovalsReviewer>>;
39
40
  instructions: z.ZodDefault<z.ZodArray<z.ZodObject<{
40
41
  role: z.ZodEnum<typeof import("@finalai/agent-provider").InstructionRole>;
41
42
  content: z.ZodString;
@@ -58,6 +59,7 @@ export interface ServerlessNanocodeCodexConfigInputIface {
58
59
  reasoning?: (typeof CODEX_REASONING_LEVELS)[number] | `${(typeof CODEX_REASONING_LEVELS)[number]}`;
59
60
  credentials: CredentialsInput;
60
61
  approvalPolicy?: CodexApprovalPolicy | `${CodexApprovalPolicy}`;
62
+ approvalsReviewer?: CodexApprovalsReviewer | `${CodexApprovalsReviewer}`;
61
63
  instructions?: InstructionInput[];
62
64
  skills?: SkillInput[];
63
65
  }
@@ -12,6 +12,7 @@ import {
12
12
  import * as z from "zod";
13
13
  import {
14
14
  CodexApprovalPolicy,
15
+ CodexApprovalsReviewer,
15
16
  configObjectJson,
16
17
  credentialsJson,
17
18
  credentialsZod,
@@ -50,6 +51,7 @@ const serverlessNanocodeCodexConfigSchema = z.strictObject({
50
51
  reasoning: reasoningZod(CODEX_REASONING_LEVELS).optional(),
51
52
  credentials: credentialsZod(),
52
53
  approvalPolicy: z.nativeEnum(CodexApprovalPolicy).default(CodexApprovalPolicy.OnRequest),
54
+ approvalsReviewer: z.nativeEnum(CodexApprovalsReviewer).default(CodexApprovalsReviewer.User),
53
55
  instructions: instructionsZod(),
54
56
  skills: skillsZod()
55
57
  });
@@ -64,6 +66,10 @@ const SERVERLESS_NANOCODE_CODEX_CONFIG_JSON_SCHEMA = configObjectJson({
64
66
  Object.values(CodexApprovalPolicy),
65
67
  CodexApprovalPolicy.OnRequest
66
68
  ),
69
+ approvalsReviewer: permissionModeJson(
70
+ Object.values(CodexApprovalsReviewer),
71
+ CodexApprovalsReviewer.User
72
+ ),
67
73
  instructions: instructionsJson(),
68
74
  skills: skillsJson()
69
75
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/harnesses/serverless-nanocode-codex.ts"],
4
- "sourcesContent": ["import {\n AcpTransport,\n BaseCapability,\n BaseToolProvision,\n CredentialDelivery,\n HarnessTrustClass,\n InferenceCredentialKind,\n McpTransportKind,\n PromptWhileRunning,\n SandboxWire,\n type HarnessDescriptionIface,\n type JsonSchema,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n CodexApprovalPolicy,\n configObjectJson,\n credentialsJson,\n credentialsZod,\n harnessBinding,\n instructionsJson,\n instructionsZod,\n modelJson,\n modelZod,\n permissionModeJson,\n reasoningJson,\n ReasoningLevel,\n reasoningZod,\n skillsJson,\n skillsZod,\n type CredentialsInput,\n type HarnessBindingOutputIface,\n type InstructionInput,\n type SkillInput,\n} from \"./config-fields.mjs\";\nimport { ModelVendor } from \"./model-reference.mjs\";\n\n// nanocode's Codex engine (Rust \u2192 wasm) in the nanocode Durable Object script.\n// Responses Lite models only; ChatGPT subscriptions go through the session\n// service relay because chatgpt.com blocks Workers egress.\n\nexport const SPECIFIER = '@finalai/final-agent-provider/harnesses/serverless-nanocode-codex';\n\n// The final://schemas/reasoning fragment has no xhigh/ultra yet; nanocode's\n// policies accept the rest.\nexport const CODEX_REASONING_LEVELS = [\n ReasoningLevel.Low,\n ReasoningLevel.Medium,\n ReasoningLevel.High,\n ReasoningLevel.Max,\n] as const;\n\nexport const CODEX_BASE_TOOLS: HarnessDescriptionIface['baseTools'] = {\n [BaseCapability.Shell]: {\n provision: BaseToolProvision.Bound,\n nativeTools: ['exec_command', 'write_stdin'],\n },\n [BaseCapability.Files]: {\n provision: BaseToolProvision.Bound,\n nativeTools: ['apply_patch', 'view_image'],\n },\n};\n\nexport const serverlessNanocodeCodexConfigSchema = z.strictObject({\n model: modelZod().optional(),\n reasoning: reasoningZod(CODEX_REASONING_LEVELS).optional(),\n credentials: credentialsZod(),\n approvalPolicy: z.nativeEnum(CodexApprovalPolicy).default(CodexApprovalPolicy.OnRequest),\n instructions: instructionsZod(),\n skills: skillsZod(),\n});\n\nexport type ServerlessNanocodeCodexConfigIface = z.output<\n typeof serverlessNanocodeCodexConfigSchema\n>;\n\nexport const SERVERLESS_NANOCODE_CODEX_CONFIG_JSON_SCHEMA: JsonSchema = configObjectJson({\n required: ['credentials'],\n title: 'Codex on nanocode (serverless)',\n properties: {\n model: modelJson(),\n reasoning: reasoningJson(CODEX_REASONING_LEVELS),\n credentials: credentialsJson(),\n approvalPolicy: permissionModeJson(\n Object.values(CodexApprovalPolicy),\n CodexApprovalPolicy.OnRequest,\n ),\n instructions: instructionsJson(),\n skills: skillsJson(),\n },\n});\n\nexport const SERVERLESS_NANOCODE_CODEX_DESCRIPTION: HarnessDescriptionIface = {\n configSchema: SERVERLESS_NANOCODE_CODEX_CONFIG_JSON_SCHEMA,\n acp: {\n transports: [AcpTransport.Relay, AcpTransport.Ws],\n mcp: [McpTransportKind.Acp, McpTransportKind.Http],\n },\n baseTools: CODEX_BASE_TOOLS,\n consumes: [SandboxWire.HandleRpc],\n inference: {\n vendors: [ModelVendor.OpenAI],\n kinds: [\n InferenceCredentialKind.Subscription,\n InferenceCredentialKind.ApiKey,\n InferenceCredentialKind.Platform,\n ],\n delivery: CredentialDelivery.PerRequest,\n },\n // Final runs these on its own Workers.\n trust: HarnessTrustClass.FirstParty,\n supports: { observe: true, resumeReplay: true, fork: false, authorMeta: true },\n // Measured (agent-harness-workers nanocode/codex.test.ts \"steers a prompt that arrives\n // while the turn runs into it\"); open approvals are asked again with new ids after a\n // lost invocation, so no disconnect survival yet.\n promptWhileRunning: PromptWhileRunning.Inject,\n sessionSurvivesDisconnect: false,\n};\n\nexport interface ServerlessNanocodeCodexConfigInputIface {\n model?: string;\n reasoning?:\n | (typeof CODEX_REASONING_LEVELS)[number]\n | `${(typeof CODEX_REASONING_LEVELS)[number]}`;\n credentials: CredentialsInput;\n approvalPolicy?: CodexApprovalPolicy | `${CodexApprovalPolicy}`;\n instructions?: InstructionInput[];\n skills?: SkillInput[];\n}\n\nexport const serverlessNanocodeCodex = (\n config: ServerlessNanocodeCodexConfigInputIface,\n): HarnessBindingOutputIface => harnessBinding(SPECIFIER, config);\n"],
5
- "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,YAAY,OAAO;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AACP,SAAS,mBAAmB;AAMrB,MAAM,YAAY;AAIlB,MAAM,yBAAyB;AAAA,EACpC,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AACjB;AAEO,MAAM,mBAAyD;AAAA,EACpE,CAAC,eAAe,KAAK,GAAG;AAAA,IACtB,WAAW,kBAAkB;AAAA,IAC7B,aAAa,CAAC,gBAAgB,aAAa;AAAA,EAC7C;AAAA,EACA,CAAC,eAAe,KAAK,GAAG;AAAA,IACtB,WAAW,kBAAkB;AAAA,IAC7B,aAAa,CAAC,eAAe,YAAY;AAAA,EAC3C;AACF;AAEO,MAAM,sCAAsC,EAAE,aAAa;AAAA,EAChE,OAAO,SAAS,EAAE,SAAS;AAAA,EAC3B,WAAW,aAAa,sBAAsB,EAAE,SAAS;AAAA,EACzD,aAAa,eAAe;AAAA,EAC5B,gBAAgB,EAAE,WAAW,mBAAmB,EAAE,QAAQ,oBAAoB,SAAS;AAAA,EACvF,cAAc,gBAAgB;AAAA,EAC9B,QAAQ,UAAU;AACpB,CAAC;AAMM,MAAM,+CAA2D,iBAAiB;AAAA,EACvF,UAAU,CAAC,aAAa;AAAA,EACxB,OAAO;AAAA,EACP,YAAY;AAAA,IACV,OAAO,UAAU;AAAA,IACjB,WAAW,cAAc,sBAAsB;AAAA,IAC/C,aAAa,gBAAgB;AAAA,IAC7B,gBAAgB;AAAA,MACd,OAAO,OAAO,mBAAmB;AAAA,MACjC,oBAAoB;AAAA,IACtB;AAAA,IACA,cAAc,iBAAiB;AAAA,IAC/B,QAAQ,WAAW;AAAA,EACrB;AACF,CAAC;AAEM,MAAM,wCAAiE;AAAA,EAC5E,cAAc;AAAA,EACd,KAAK;AAAA,IACH,YAAY,CAAC,aAAa,OAAO,aAAa,EAAE;AAAA,IAChD,KAAK,CAAC,iBAAiB,KAAK,iBAAiB,IAAI;AAAA,EACnD;AAAA,EACA,WAAW;AAAA,EACX,UAAU,CAAC,YAAY,SAAS;AAAA,EAChC,WAAW;AAAA,IACT,SAAS,CAAC,YAAY,MAAM;AAAA,IAC5B,OAAO;AAAA,MACL,wBAAwB;AAAA,MACxB,wBAAwB;AAAA,MACxB,wBAAwB;AAAA,IAC1B;AAAA,IACA,UAAU,mBAAmB;AAAA,EAC/B;AAAA;AAAA,EAEA,OAAO,kBAAkB;AAAA,EACzB,UAAU,EAAE,SAAS,MAAM,cAAc,MAAM,MAAM,OAAO,YAAY,KAAK;AAAA;AAAA;AAAA;AAAA,EAI7E,oBAAoB,mBAAmB;AAAA,EACvC,2BAA2B;AAC7B;AAaO,MAAM,0BAA0B,CACrC,WAC8B,eAAe,WAAW,MAAM;",
4
+ "sourcesContent": ["import {\n AcpTransport,\n BaseCapability,\n BaseToolProvision,\n CredentialDelivery,\n HarnessTrustClass,\n InferenceCredentialKind,\n McpTransportKind,\n PromptWhileRunning,\n SandboxWire,\n type HarnessDescriptionIface,\n type JsonSchema,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport {\n CodexApprovalPolicy,\n CodexApprovalsReviewer,\n configObjectJson,\n credentialsJson,\n credentialsZod,\n harnessBinding,\n instructionsJson,\n instructionsZod,\n modelJson,\n modelZod,\n permissionModeJson,\n reasoningJson,\n ReasoningLevel,\n reasoningZod,\n skillsJson,\n skillsZod,\n type CredentialsInput,\n type HarnessBindingOutputIface,\n type InstructionInput,\n type SkillInput,\n} from \"./config-fields.mjs\";\nimport { ModelVendor } from \"./model-reference.mjs\";\n\n// nanocode's Codex engine (Rust \u2192 wasm) in the nanocode Durable Object script.\n// Responses Lite models only; ChatGPT subscriptions go through the session\n// service relay because chatgpt.com blocks Workers egress.\n\nexport const SPECIFIER = '@finalai/final-agent-provider/harnesses/serverless-nanocode-codex';\n\n// The final://schemas/reasoning fragment has no xhigh/ultra yet; nanocode's\n// policies accept the rest.\nexport const CODEX_REASONING_LEVELS = [\n ReasoningLevel.Low,\n ReasoningLevel.Medium,\n ReasoningLevel.High,\n ReasoningLevel.Max,\n] as const;\n\nexport const CODEX_BASE_TOOLS: HarnessDescriptionIface['baseTools'] = {\n [BaseCapability.Shell]: {\n provision: BaseToolProvision.Bound,\n nativeTools: ['exec_command', 'write_stdin'],\n },\n [BaseCapability.Files]: {\n provision: BaseToolProvision.Bound,\n nativeTools: ['apply_patch', 'view_image'],\n },\n};\n\nexport const serverlessNanocodeCodexConfigSchema = z.strictObject({\n model: modelZod().optional(),\n reasoning: reasoningZod(CODEX_REASONING_LEVELS).optional(),\n credentials: credentialsZod(),\n approvalPolicy: z.nativeEnum(CodexApprovalPolicy).default(CodexApprovalPolicy.OnRequest),\n approvalsReviewer: z.nativeEnum(CodexApprovalsReviewer).default(CodexApprovalsReviewer.User),\n instructions: instructionsZod(),\n skills: skillsZod(),\n});\n\nexport type ServerlessNanocodeCodexConfigIface = z.output<\n typeof serverlessNanocodeCodexConfigSchema\n>;\n\nexport const SERVERLESS_NANOCODE_CODEX_CONFIG_JSON_SCHEMA: JsonSchema = configObjectJson({\n required: ['credentials'],\n title: 'Codex on nanocode (serverless)',\n properties: {\n model: modelJson(),\n reasoning: reasoningJson(CODEX_REASONING_LEVELS),\n credentials: credentialsJson(),\n approvalPolicy: permissionModeJson(\n Object.values(CodexApprovalPolicy),\n CodexApprovalPolicy.OnRequest,\n ),\n approvalsReviewer: permissionModeJson(\n Object.values(CodexApprovalsReviewer),\n CodexApprovalsReviewer.User,\n ),\n instructions: instructionsJson(),\n skills: skillsJson(),\n },\n});\n\nexport const SERVERLESS_NANOCODE_CODEX_DESCRIPTION: HarnessDescriptionIface = {\n configSchema: SERVERLESS_NANOCODE_CODEX_CONFIG_JSON_SCHEMA,\n acp: {\n transports: [AcpTransport.Relay, AcpTransport.Ws],\n mcp: [McpTransportKind.Acp, McpTransportKind.Http],\n },\n baseTools: CODEX_BASE_TOOLS,\n consumes: [SandboxWire.HandleRpc],\n inference: {\n vendors: [ModelVendor.OpenAI],\n kinds: [\n InferenceCredentialKind.Subscription,\n InferenceCredentialKind.ApiKey,\n InferenceCredentialKind.Platform,\n ],\n delivery: CredentialDelivery.PerRequest,\n },\n // Final runs these on its own Workers.\n trust: HarnessTrustClass.FirstParty,\n supports: { observe: true, resumeReplay: true, fork: false, authorMeta: true },\n // Measured (agent-harness-workers nanocode/codex.test.ts \"steers a prompt that arrives\n // while the turn runs into it\"); open approvals are asked again with new ids after a\n // lost invocation, so no disconnect survival yet.\n promptWhileRunning: PromptWhileRunning.Inject,\n sessionSurvivesDisconnect: false,\n};\n\nexport interface ServerlessNanocodeCodexConfigInputIface {\n model?: string;\n reasoning?:\n | (typeof CODEX_REASONING_LEVELS)[number]\n | `${(typeof CODEX_REASONING_LEVELS)[number]}`;\n credentials: CredentialsInput;\n approvalPolicy?: CodexApprovalPolicy | `${CodexApprovalPolicy}`;\n approvalsReviewer?: CodexApprovalsReviewer | `${CodexApprovalsReviewer}`;\n instructions?: InstructionInput[];\n skills?: SkillInput[];\n}\n\nexport const serverlessNanocodeCodex = (\n config: ServerlessNanocodeCodexConfigInputIface,\n): HarnessBindingOutputIface => harnessBinding(SPECIFIER, config);\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,YAAY,OAAO;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AACP,SAAS,mBAAmB;AAMrB,MAAM,YAAY;AAIlB,MAAM,yBAAyB;AAAA,EACpC,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AACjB;AAEO,MAAM,mBAAyD;AAAA,EACpE,CAAC,eAAe,KAAK,GAAG;AAAA,IACtB,WAAW,kBAAkB;AAAA,IAC7B,aAAa,CAAC,gBAAgB,aAAa;AAAA,EAC7C;AAAA,EACA,CAAC,eAAe,KAAK,GAAG;AAAA,IACtB,WAAW,kBAAkB;AAAA,IAC7B,aAAa,CAAC,eAAe,YAAY;AAAA,EAC3C;AACF;AAEO,MAAM,sCAAsC,EAAE,aAAa;AAAA,EAChE,OAAO,SAAS,EAAE,SAAS;AAAA,EAC3B,WAAW,aAAa,sBAAsB,EAAE,SAAS;AAAA,EACzD,aAAa,eAAe;AAAA,EAC5B,gBAAgB,EAAE,WAAW,mBAAmB,EAAE,QAAQ,oBAAoB,SAAS;AAAA,EACvF,mBAAmB,EAAE,WAAW,sBAAsB,EAAE,QAAQ,uBAAuB,IAAI;AAAA,EAC3F,cAAc,gBAAgB;AAAA,EAC9B,QAAQ,UAAU;AACpB,CAAC;AAMM,MAAM,+CAA2D,iBAAiB;AAAA,EACvF,UAAU,CAAC,aAAa;AAAA,EACxB,OAAO;AAAA,EACP,YAAY;AAAA,IACV,OAAO,UAAU;AAAA,IACjB,WAAW,cAAc,sBAAsB;AAAA,IAC/C,aAAa,gBAAgB;AAAA,IAC7B,gBAAgB;AAAA,MACd,OAAO,OAAO,mBAAmB;AAAA,MACjC,oBAAoB;AAAA,IACtB;AAAA,IACA,mBAAmB;AAAA,MACjB,OAAO,OAAO,sBAAsB;AAAA,MACpC,uBAAuB;AAAA,IACzB;AAAA,IACA,cAAc,iBAAiB;AAAA,IAC/B,QAAQ,WAAW;AAAA,EACrB;AACF,CAAC;AAEM,MAAM,wCAAiE;AAAA,EAC5E,cAAc;AAAA,EACd,KAAK;AAAA,IACH,YAAY,CAAC,aAAa,OAAO,aAAa,EAAE;AAAA,IAChD,KAAK,CAAC,iBAAiB,KAAK,iBAAiB,IAAI;AAAA,EACnD;AAAA,EACA,WAAW;AAAA,EACX,UAAU,CAAC,YAAY,SAAS;AAAA,EAChC,WAAW;AAAA,IACT,SAAS,CAAC,YAAY,MAAM;AAAA,IAC5B,OAAO;AAAA,MACL,wBAAwB;AAAA,MACxB,wBAAwB;AAAA,MACxB,wBAAwB;AAAA,IAC1B;AAAA,IACA,UAAU,mBAAmB;AAAA,EAC/B;AAAA;AAAA,EAEA,OAAO,kBAAkB;AAAA,EACzB,UAAU,EAAE,SAAS,MAAM,cAAc,MAAM,MAAM,OAAO,YAAY,KAAK;AAAA;AAAA;AAAA;AAAA,EAI7E,oBAAoB,mBAAmB;AAAA,EACvC,2BAA2B;AAC7B;AAcO,MAAM,0BAA0B,CACrC,WAC8B,eAAe,WAAW,MAAM;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finalai/final-agent-provider",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "private": false,
5
5
  "description": "Final's provider package: serverless, remote and local harnesses; the computer and device sandboxes; the messaging, schedule and event channels.",
6
6
  "license": "Apache-2.0",
@@ -220,7 +220,7 @@
220
220
  "dependencies": {
221
221
  "@agentclientprotocol/sdk": "1.8.0",
222
222
  "@finalai/acp-session": "0.1.10",
223
- "@finalai/agent-protocol": "0.6.0",
223
+ "@finalai/agent-protocol": "0.6.1",
224
224
  "@finalai/agent-provider": "0.5.1",
225
225
  "@finalai/computers": "0.3.0",
226
226
  "@finalai/websockets": "0.1.6",