@earendil-works/pi-server 0.84.4 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +66 -37
  3. package/dist/connection.d.ts +9 -5
  4. package/dist/connection.d.ts.map +1 -1
  5. package/dist/connection.js.map +1 -1
  6. package/dist/errors.d.ts +16 -18
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +23 -27
  9. package/dist/errors.js.map +1 -1
  10. package/dist/index.d.ts +0 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +0 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/listener.d.ts +1 -3
  15. package/dist/listener.d.ts.map +1 -1
  16. package/dist/listener.js.map +1 -1
  17. package/dist/server.d.ts +16 -6
  18. package/dist/server.d.ts.map +1 -1
  19. package/dist/server.js +268 -86
  20. package/dist/server.js.map +1 -1
  21. package/dist/session-router.d.ts +40 -0
  22. package/dist/session-router.d.ts.map +1 -0
  23. package/dist/session-router.js +264 -0
  24. package/dist/session-router.js.map +1 -0
  25. package/dist/testing/client.d.ts +6 -2
  26. package/dist/testing/client.d.ts.map +1 -1
  27. package/dist/testing/client.js +23 -3
  28. package/dist/testing/client.js.map +1 -1
  29. package/dist/testing/host.d.ts +57 -0
  30. package/dist/testing/host.d.ts.map +1 -0
  31. package/dist/testing/host.js +189 -0
  32. package/dist/testing/host.js.map +1 -0
  33. package/dist/testing/index.d.ts +1 -1
  34. package/dist/testing/index.d.ts.map +1 -1
  35. package/dist/testing/index.js +1 -1
  36. package/dist/testing/index.js.map +1 -1
  37. package/dist/testing/server.d.ts +8 -7
  38. package/dist/testing/server.d.ts.map +1 -1
  39. package/dist/testing/server.js +7 -7
  40. package/dist/testing/server.js.map +1 -1
  41. package/dist/transports/unix/address.d.ts +3 -0
  42. package/dist/transports/unix/address.d.ts.map +1 -0
  43. package/dist/transports/unix/address.js +9 -0
  44. package/dist/transports/unix/address.js.map +1 -0
  45. package/dist/transports/unix/index.d.ts +1 -0
  46. package/dist/transports/unix/index.d.ts.map +1 -1
  47. package/dist/transports/unix/index.js +1 -0
  48. package/dist/transports/unix/index.js.map +1 -1
  49. package/dist/transports/unix/listener.d.ts +2 -3
  50. package/dist/transports/unix/listener.d.ts.map +1 -1
  51. package/dist/transports/unix/listener.js +13 -24
  52. package/dist/transports/unix/listener.js.map +1 -1
  53. package/dist/transports/unix/preset.d.ts +5 -4
  54. package/dist/transports/unix/preset.d.ts.map +1 -1
  55. package/dist/transports/unix/preset.js +5 -4
  56. package/dist/transports/unix/preset.js.map +1 -1
  57. package/dist/transports/unix/types.d.ts +2 -2
  58. package/dist/transports/unix/types.d.ts.map +1 -1
  59. package/dist/transports/unix/types.js.map +1 -1
  60. package/dist/types.d.ts +40 -47
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/types.js.map +1 -1
  63. package/package.json +4 -3
  64. package/dist/protocol.d.ts +0 -22
  65. package/dist/protocol.d.ts.map +0 -1
  66. package/dist/protocol.js +0 -266
  67. package/dist/protocol.js.map +0 -1
  68. package/dist/sessions.d.ts +0 -2174
  69. package/dist/sessions.d.ts.map +0 -1
  70. package/dist/sessions.js +0 -305
  71. package/dist/sessions.js.map +0 -1
  72. package/dist/snapshots.d.ts +0 -24
  73. package/dist/snapshots.d.ts.map +0 -1
  74. package/dist/snapshots.js +0 -39
  75. package/dist/snapshots.js.map +0 -1
  76. package/dist/testing/service.d.ts +0 -59
  77. package/dist/testing/service.d.ts.map +0 -1
  78. package/dist/testing/service.js +0 -240
  79. package/dist/testing/service.js.map +0 -1
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n\tCommand,\n\tModelMetadata,\n\tModelRef,\n\tSessionMetadata,\n\tSessionPhase,\n\tSessionSnapshot,\n\tThinkingLevel,\n\tTranscriptProgress,\n} from \"@earendil-works/pi-protocol\";\nimport type { PiServerError } from \"./errors.ts\";\nimport type { PiServerListener } from \"./listener.ts\";\n\nexport interface PiServerOptions {\n\tlisteners: readonly PiServerListener[];\n\tmaxFrameLength?: number;\n\thandshakeTimeoutMs?: number;\n\tserverId?: string;\n\tonError?: (error: Error) => void;\n}\n\nexport type MaybePromise<T> = T | Promise<T>;\n\nexport type PromptInput = Omit<Extract<Command, { command: \"prompt\" }>, \"command\" | \"sessionId\">;\nexport type SteerInput = Omit<Extract<Command, { command: \"steer\" }>, \"command\" | \"sessionId\">;\n\nexport interface CreateSessionOptions {\n\t/** A collision-resistant ID assigned by PiServer. The service must persist this exact ID. */\n\tid: string;\n\tcwd?: string;\n\tname?: string;\n\tmodel?: ModelRef;\n\tthinkingLevel?: ThinkingLevel;\n}\n\nexport type PiSessionRuntimeEvent =\n\t| { type: \"snapshot\" }\n\t| { type: \"progress\"; progress: TranscriptProgress }\n\t| { type: \"error\"; error: PiServerError };\n\n/** One acquired durable session. Conflicting operations must reject rather than queue. */\nexport interface PiSessionRuntime {\n\tsnapshot(): MaybePromise<SessionSnapshot>;\n\tgetPhase(): SessionPhase;\n\tprompt(input: PromptInput): Promise<void>;\n\tsteer(input: SteerInput): Promise<void>;\n\tabort(): Promise<void>;\n\tsetModel(model: ModelRef): Promise<void>;\n\tsetThinking(thinkingLevel: ThinkingLevel): Promise<void>;\n\tsubscribe(listener: (event: PiSessionRuntimeEvent) => void): () => void;\n\tdispose(): Promise<void>;\n}\n\n/** Service boundary for durable sessions and exclusively acquired runtimes. */\nexport interface PiServerService {\n\tlistSessions(): Promise<SessionMetadata[]>;\n\tlistModels(): Promise<ModelMetadata[]>;\n\tcreateSession(options: CreateSessionOptions): Promise<PiSessionRuntime>;\n\topenSession(sessionId: string): Promise<PiSessionRuntime>;\n}\n\nexport type SessionRuntime = PiSessionRuntime;\nexport type SessionRuntimeEvent = PiSessionRuntimeEvent;\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { JsonValue, ServiceCall, ServiceProviderUpdate } from \"@earendil-works/chord\";\nimport type { Context, SessionMetadata } from \"@earendil-works/pi-agent-core\";\nimport type { ServerListener } from \"./listener.ts\";\n\nexport interface ServerOptions {\n\tlisteners: readonly ServerListener[];\n\t/** Stable logical server identity supplied by the installation or profile. */\n\tserverId: string;\n\tmaxFrameLength?: number;\n\thandshakeTimeoutMs?: number;\n\tonConnectionCountChanged?: (count: number) => void;\n\tonError?: (error: Error) => void;\n}\n\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** One presentation connection's live capability for a hosted Session. */\nexport interface RoutedSessionAttachment {\n\t/** Route one contract-agnostic service operation to the attached Session endpoint. */\n\tinvokeService(\n\t\tcall: ServiceCall,\n\t\tpublish: (subscriptionId: string, update: ServiceProviderUpdate, context: Context) => MaybePromise<void>,\n\t\tcontext: Context,\n\t): Promise<JsonValue | undefined>;\n\trelease(context: Context): MaybePromise<void>;\n}\n\n/** Presentation-scoped routing capabilities available to server service implementations. */\nexport interface RoutedServerPresentation {\n\tattachSession(sessionId: string, context: Context): Promise<void>;\n\tdetachSession(context: Context): Promise<void>;\n\t/** Release routed attachments and handles before the application deletes durable metadata. */\n\tprepareSessionRemoval(sessionId: string, context: Context): Promise<void>;\n}\n\n/** One connection's server-scoped service endpoint. */\nexport interface RoutedServerServiceAttachment {\n\tinvokeService(\n\t\tcall: ServiceCall,\n\t\tpublish: (subscriptionId: string, update: ServiceProviderUpdate, context: Context) => MaybePromise<void>,\n\t\tcontext: Context,\n\t): Promise<JsonValue | undefined>;\n\trelease(context: Context): MaybePromise<void>;\n}\n\nexport interface RoutedServerServiceHost {\n\tattachClient(presentation: RoutedServerPresentation, context: Context): MaybePromise<RoutedServerServiceAttachment>;\n}\n\n/** A process-safe handle that acquires presentation-scoped Session capabilities. */\nexport interface RoutedSessionHandle {\n\tattachClient(context: Context): MaybePromise<RoutedSessionAttachment>;\n\t/** Resolves with an error for unexpected termination, or undefined after an expected close. */\n\treadonly terminated?: Promise<Error | undefined>;\n\tclose(context: Context): Promise<void>;\n}\n\n/** Application capabilities used by server-wide management and Session routing. */\nexport interface ServerHost<TMetadata extends SessionMetadata = SessionMetadata> {\n\treadonly serverServices: RoutedServerServiceHost;\n\t/** Resolve one durable Session ID or throw a bounded routing error. */\n\tresolveSession(sessionId: string, context: Context): Promise<TMetadata>;\n\topenSession(metadata: TMetadata, context: Context): Promise<RoutedSessionHandle>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-server",
3
- "version": "0.84.4",
3
+ "version": "0.85.0",
4
4
  "description": "experimental server package for pi",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -47,8 +47,9 @@
47
47
  "node": ">=22.19.0"
48
48
  },
49
49
  "dependencies": {
50
- "@earendil-works/pi-ai": "^0.84.4",
51
- "@earendil-works/pi-protocol": "^0.84.4"
50
+ "@earendil-works/chord": "^0.85.0",
51
+ "@earendil-works/pi-agent-core": "^0.85.0",
52
+ "@earendil-works/pi-protocol": "^0.85.0"
52
53
  },
53
54
  "devDependencies": {
54
55
  "shx": "0.4.0",
@@ -1,22 +0,0 @@
1
- import { type Usage as AiUsage, type Api, type AssistantMessage, type Model, type ToolCall, type ToolResultMessage, type UserMessage } from "@earendil-works/pi-ai";
2
- import type { AssistantTranscriptItem, JsonValue, ModelMetadata, ToolTranscriptItem, Usage, UserTranscriptItem } from "@earendil-works/pi-protocol";
3
- export interface AssistantTranscriptOptions {
4
- id: string;
5
- }
6
- export interface UserTranscriptOptions {
7
- id: string;
8
- }
9
- export interface ToolTranscriptOptions {
10
- id: string;
11
- call: ToolCall;
12
- }
13
- /** Validate and copy a value from an execution boundary into the protocol's JSON-compatible subset. */
14
- export declare function toProtocolJsonValue(value: unknown, seen?: Set<object>): JsonValue;
15
- /** Lossily sanitize diagnostic tool details that must not affect execution semantics. */
16
- export declare function sanitizeProtocolDetails(value: unknown, seen?: Set<object>): JsonValue | undefined;
17
- export declare function toProtocolUsage(usage: AiUsage | undefined): Usage | undefined;
18
- export declare function toProtocolModelMetadata(model: Model<Api>, authenticated: boolean): ModelMetadata;
19
- export declare function toProtocolUserMessage(message: UserMessage, options: UserTranscriptOptions): UserTranscriptItem;
20
- export declare function toProtocolAssistantMessage(message: AssistantMessage, options: AssistantTranscriptOptions): AssistantTranscriptItem;
21
- export declare function toProtocolToolResultMessage(message: ToolResultMessage, options: ToolTranscriptOptions): ToolTranscriptItem;
22
- //# sourceMappingURL=protocol.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,KAAK,IAAI,OAAO,EACrB,KAAK,GAAG,EACR,KAAK,gBAAgB,EAErB,KAAK,KAAK,EAEV,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACX,uBAAuB,EACvB,SAAS,EACT,aAAa,EAEb,kBAAkB,EAClB,KAAK,EACL,kBAAkB,EAClB,MAAM,6BAA6B,CAAC;AAsFrC,MAAM,WAAW,0BAA0B;IAC1C,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;CACf;AAsBD,uGAAuG;AACvG,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,cAAoB,GAAG,SAAS,CAqBvF;AAED,yFAAyF;AACzF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,cAAoB,GAAG,SAAS,GAAG,SAAS,CAoBvG;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,CAmB7E;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,OAAO,GAAG,aAAa,CAoBhG;AAkBD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,qBAAqB,GAAG,kBAAkB,CAQ9G;AA4BD,wBAAgB,0BAA0B,CACzC,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,0BAA0B,GACjC,uBAAuB,CAmDzB;AAiBD,wBAAgB,2BAA2B,CAC1C,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,qBAAqB,GAC5B,kBAAkB,CAyBpB","sourcesContent":["import {\n\ttype ImageContent as AiImageContent,\n\ttype TextContent as AiTextContent,\n\ttype Usage as AiUsage,\n\ttype Api,\n\ttype AssistantMessage,\n\tgetSupportedThinkingLevels,\n\ttype Model,\n\ttype ModelThinkingLevel,\n\ttype ToolCall,\n\ttype ToolResultMessage,\n\ttype UserMessage,\n} from \"@earendil-works/pi-ai\";\nimport type {\n\tAssistantTranscriptItem,\n\tJsonValue,\n\tModelMetadata,\n\tThinkingLevel,\n\tToolTranscriptItem,\n\tUsage,\n\tUserTranscriptItem,\n} from \"@earendil-works/pi-protocol\";\n\ntype Assert<T extends true> = T;\ntype ExactKeys<T, Keys extends keyof T> = keyof T extends Keys ? true : false;\ntype _AiThinkingLevelsFitProtocol = Assert<ModelThinkingLevel extends ThinkingLevel ? true : false>;\ntype _ProtocolThinkingLevelsFitAi = Assert<ThinkingLevel extends ModelThinkingLevel ? true : false>;\ntype AiModelInput = Model<Api>[\"input\"][number];\ntype ProtocolModelInput = ModelMetadata[\"input\"][number];\ntype _AiModelInputsFitProtocol = Assert<AiModelInput extends ProtocolModelInput ? true : false>;\ntype _ProtocolModelInputsFitAi = Assert<ProtocolModelInput extends AiModelInput ? true : false>;\n/**\n * Enumerate mapped and intentionally omitted pi-ai fields so additions fail compilation here.\n * Provider replay metadata, diagnostics, cache-write retention splits, model transport settings,\n * model sampling defaults, pricing tiers, and deferred-tool availability remain intentionally\n * server-side.\n */\ntype _AiTextContentFieldsAccountedFor = Assert<ExactKeys<AiTextContent, \"type\" | \"text\" | \"textSignature\">>;\ntype _AiThinkingContentFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tExtract<AssistantMessage[\"content\"][number], { type: \"thinking\" }>,\n\t\t\"type\" | \"thinking\" | \"thinkingSignature\" | \"redacted\"\n\t>\n>;\ntype _AiImageContentFieldsAccountedFor = Assert<ExactKeys<AiImageContent, \"type\" | \"data\" | \"mimeType\">>;\ntype _AiToolCallFieldsAccountedFor = Assert<\n\tExactKeys<ToolCall, \"type\" | \"id\" | \"name\" | \"arguments\" | \"thoughtSignature\" | \"namespace\">\n>;\ntype _AiUsageFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tAiUsage,\n\t\t\"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\" | \"cacheWrite1h\" | \"reasoning\" | \"totalTokens\" | \"cost\"\n\t>\n>;\ntype _AiUsageCostFieldsAccountedFor = Assert<\n\tExactKeys<AiUsage[\"cost\"], \"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\" | \"total\">\n>;\ntype _AiModelFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tModel<Api>,\n\t\t| \"id\"\n\t\t| \"name\"\n\t\t| \"api\"\n\t\t| \"provider\"\n\t\t| \"baseUrl\"\n\t\t| \"reasoning\"\n\t\t| \"thinkingLevelMap\"\n\t\t| \"input\"\n\t\t| \"cost\"\n\t\t| \"contextWindow\"\n\t\t| \"maxTokens\"\n\t\t| \"samplingParams\"\n\t\t| \"headers\"\n\t\t| \"compat\"\n\t>\n>;\ntype _AiModelCostFieldsAccountedFor = Assert<\n\tExactKeys<Model<Api>[\"cost\"], \"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\" | \"tiers\">\n>;\ntype _AiUserMessageFieldsAccountedFor = Assert<ExactKeys<UserMessage, \"role\" | \"content\" | \"timestamp\">>;\ntype _AiAssistantMessageFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tAssistantMessage,\n\t\t| \"role\"\n\t\t| \"content\"\n\t\t| \"api\"\n\t\t| \"provider\"\n\t\t| \"model\"\n\t\t| \"responseModel\"\n\t\t| \"responseId\"\n\t\t| \"diagnostics\"\n\t\t| \"usage\"\n\t\t| \"stopReason\"\n\t\t| \"deferred\"\n\t\t| \"errorMessage\"\n\t\t| \"rawStopReason\"\n\t\t| \"endTurn\"\n\t\t| \"timestamp\"\n\t>\n>;\ntype _AiToolResultMessageFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tToolResultMessage,\n\t\t\"role\" | \"toolCallId\" | \"toolName\" | \"content\" | \"details\" | \"usage\" | \"addedToolNames\" | \"isError\" | \"timestamp\"\n\t>\n>;\n\nexport interface AssistantTranscriptOptions {\n\tid: string;\n}\n\nexport interface UserTranscriptOptions {\n\tid: string;\n}\n\nexport interface ToolTranscriptOptions {\n\tid: string;\n\tcall: ToolCall;\n}\n\nfunction nonNegativeInteger(value: number | undefined): number | undefined {\n\tif (value === undefined || !Number.isFinite(value)) return undefined;\n\treturn Math.max(0, Math.floor(value));\n}\n\nfunction nonNegativeNumber(value: number): number {\n\treturn Number.isFinite(value) ? Math.max(0, value) : 0;\n}\n\nfunction identifier(value: string, label: string): string {\n\tif (typeof value !== \"string\" || value.length === 0) throw new TypeError(`${label} must be a non-empty string`);\n\treturn value;\n}\n\nfunction timestamp(value: number): number {\n\tif (!Number.isSafeInteger(value) || value < 0)\n\t\tthrow new TypeError(\"Protocol timestamps must be non-negative integers\");\n\treturn value;\n}\n\n/** Validate and copy a value from an execution boundary into the protocol's JSON-compatible subset. */\nexport function toProtocolJsonValue(value: unknown, seen = new Set<object>()): JsonValue {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") return value;\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isFinite(value)) throw new TypeError(\"Protocol JSON numbers must be finite\");\n\t\treturn value;\n\t}\n\tif (typeof value !== \"object\") throw new TypeError(`Unsupported protocol JSON value: ${typeof value}`);\n\tif (seen.has(value)) throw new TypeError(\"Protocol JSON values must not contain circular references\");\n\tconst prototype = Object.getPrototypeOf(value);\n\tif (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {\n\t\tthrow new TypeError(\"Protocol JSON objects must be plain objects\");\n\t}\n\tseen.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) return Array.from(value, (entry) => toProtocolJsonValue(entry, seen));\n\t\tconst result: Record<string, JsonValue> = {};\n\t\tfor (const [key, entry] of Object.entries(value)) result[key] = toProtocolJsonValue(entry, seen);\n\t\treturn result;\n\t} finally {\n\t\tseen.delete(value);\n\t}\n}\n\n/** Lossily sanitize diagnostic tool details that must not affect execution semantics. */\nexport function sanitizeProtocolDetails(value: unknown, seen = new Set<object>()): JsonValue | undefined {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") return value;\n\tif (typeof value === \"number\") return Number.isFinite(value) ? value : String(value);\n\tif (typeof value === \"bigint\") return value.toString();\n\tif (value === undefined || typeof value === \"function\" || typeof value === \"symbol\") return undefined;\n\tif (value instanceof Date) return value.toISOString();\n\tif (typeof value !== \"object\") return String(value);\n\tif (seen.has(value)) return \"[Circular]\";\n\tseen.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) return Array.from(value, (entry) => sanitizeProtocolDetails(entry, seen) ?? null);\n\t\tconst result: Record<string, JsonValue> = {};\n\t\tfor (const [key, entry] of Object.entries(value)) {\n\t\t\tconst normalized = sanitizeProtocolDetails(entry, seen);\n\t\t\tif (normalized !== undefined) result[key] = normalized;\n\t\t}\n\t\treturn result;\n\t} finally {\n\t\tseen.delete(value);\n\t}\n}\n\nexport function toProtocolUsage(usage: AiUsage | undefined): Usage | undefined {\n\tif (!usage) return undefined;\n\tconst reasoning = nonNegativeInteger(usage.reasoning);\n\tconst result = {\n\t\tinput: nonNegativeInteger(usage.input) ?? 0,\n\t\toutput: nonNegativeInteger(usage.output) ?? 0,\n\t\tcacheRead: nonNegativeInteger(usage.cacheRead) ?? 0,\n\t\tcacheWrite: nonNegativeInteger(usage.cacheWrite) ?? 0,\n\t\t...(reasoning === undefined ? {} : { reasoning }),\n\t\ttotalTokens: nonNegativeInteger(usage.totalTokens) ?? 0,\n\t\tcost: {\n\t\t\tinput: nonNegativeNumber(usage.cost.input),\n\t\t\toutput: nonNegativeNumber(usage.cost.output),\n\t\t\tcacheRead: nonNegativeNumber(usage.cost.cacheRead),\n\t\t\tcacheWrite: nonNegativeNumber(usage.cost.cacheWrite),\n\t\t\ttotal: nonNegativeNumber(usage.cost.total),\n\t\t},\n\t} satisfies Usage;\n\treturn result;\n}\n\nexport function toProtocolModelMetadata(model: Model<Api>, authenticated: boolean): ModelMetadata {\n\tconst result = {\n\t\tprovider: identifier(model.provider, \"Model provider\"),\n\t\tid: identifier(model.id, \"Model id\"),\n\t\tname: identifier(model.name, \"Model name\"),\n\t\tapi: identifier(model.api, \"Model API\"),\n\t\treasoning: model.reasoning,\n\t\tinput: [...model.input],\n\t\tcontextWindow: Math.max(1, Math.floor(model.contextWindow)),\n\t\tmaxTokens: Math.max(1, Math.floor(model.maxTokens)),\n\t\tcost: {\n\t\t\tinput: nonNegativeNumber(model.cost.input),\n\t\t\toutput: nonNegativeNumber(model.cost.output),\n\t\t\tcacheRead: nonNegativeNumber(model.cost.cacheRead),\n\t\t\tcacheWrite: nonNegativeNumber(model.cost.cacheWrite),\n\t\t},\n\t\tsupportedThinkingLevels: getSupportedThinkingLevels(model),\n\t\tauthenticated,\n\t} satisfies ModelMetadata;\n\treturn result;\n}\n\nfunction toProtocolUserContent(content: UserMessage[\"content\"]): UserTranscriptItem[\"content\"] {\n\tif (typeof content === \"string\") return [{ type: \"text\", text: content }];\n\treturn content.map((part) => {\n\t\tswitch (part.type) {\n\t\t\tcase \"text\":\n\t\t\t\treturn { type: \"text\", text: part.text };\n\t\t\tcase \"image\":\n\t\t\t\treturn { type: \"image\", data: part.data, mimeType: part.mimeType };\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = part;\n\t\t\t\treturn exhaustive;\n\t\t\t}\n\t\t}\n\t});\n}\n\nexport function toProtocolUserMessage(message: UserMessage, options: UserTranscriptOptions): UserTranscriptItem {\n\tconst result = {\n\t\tid: identifier(options.id, \"Transcript item id\"),\n\t\trole: \"user\",\n\t\tcontent: toProtocolUserContent(message.content),\n\t\ttimestamp: timestamp(message.timestamp),\n\t} satisfies UserTranscriptItem;\n\treturn result;\n}\n\nfunction toProtocolAssistantContent(message: AssistantMessage): AssistantTranscriptItem[\"content\"] {\n\treturn message.content.map((part) => {\n\t\tswitch (part.type) {\n\t\t\tcase \"text\":\n\t\t\t\treturn { type: \"text\", text: part.text };\n\t\t\tcase \"thinking\":\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\tthinking: part.thinking,\n\t\t\t\t\t...(part.redacted === undefined ? {} : { redacted: part.redacted }),\n\t\t\t\t};\n\t\t\tcase \"toolCall\":\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\ttoolCallId: identifier(part.id, \"Tool call id\"),\n\t\t\t\t\ttoolName: identifier(part.name, \"Tool call name\"),\n\t\t\t\t\tinput: toProtocolJsonValue(part.arguments),\n\t\t\t\t};\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = part;\n\t\t\t\treturn exhaustive;\n\t\t\t}\n\t\t}\n\t});\n}\n\nexport function toProtocolAssistantMessage(\n\tmessage: AssistantMessage,\n\toptions: AssistantTranscriptOptions,\n): AssistantTranscriptItem {\n\tconst usage = toProtocolUsage(message.usage);\n\tconst common = {\n\t\tid: identifier(options.id, \"Transcript item id\"),\n\t\trole: \"assistant\",\n\t\tcontent: toProtocolAssistantContent(message),\n\t\tmodel: {\n\t\t\tprovider: identifier(message.provider, \"Assistant provider\"),\n\t\t\tid: identifier(message.model, \"Assistant model\"),\n\t\t},\n\t\t...(message.responseModel === undefined\n\t\t\t? {}\n\t\t\t: { responseModel: identifier(message.responseModel, \"Assistant response model\") }),\n\t\t...(usage ? { usage } : {}),\n\t\ttimestamp: timestamp(message.timestamp),\n\t} as const;\n\tswitch (message.stopReason) {\n\t\tcase \"pending\":\n\t\t\treturn { ...common, status: \"streaming\" } satisfies AssistantTranscriptItem;\n\t\tcase \"stop\":\n\t\tcase \"length\":\n\t\tcase \"toolUse\":\n\t\t\treturn {\n\t\t\t\t...common,\n\t\t\t\tstatus: \"complete\",\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t} satisfies AssistantTranscriptItem;\n\t\tcase \"deferred\":\n\t\t\tthrow new TypeError(\"Deferred assistant messages are not supported by protocol v1\");\n\t\tcase \"error\":\n\t\t\tif (message.errorMessage?.length === 0) {\n\t\t\t\tthrow new TypeError(\"Assistant error messages must not be empty\");\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...common,\n\t\t\t\tstatus: \"error\",\n\t\t\t\tstopReason: \"error\",\n\t\t\t\t...(message.errorMessage === undefined ? {} : { errorMessage: message.errorMessage }),\n\t\t\t} satisfies AssistantTranscriptItem;\n\t\tcase \"aborted\":\n\t\t\treturn {\n\t\t\t\t...common,\n\t\t\t\tstatus: \"aborted\",\n\t\t\t\tstopReason: \"aborted\",\n\t\t\t\t...(message.errorMessage === undefined ? {} : { errorMessage: message.errorMessage }),\n\t\t\t} satisfies AssistantTranscriptItem;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = message.stopReason;\n\t\t\treturn exhaustive;\n\t\t}\n\t}\n}\n\nfunction toProtocolToolContent(content: Array<AiTextContent | AiImageContent>): ToolTranscriptItem[\"content\"] {\n\treturn content.map((part) => {\n\t\tswitch (part.type) {\n\t\t\tcase \"text\":\n\t\t\t\treturn { type: \"text\", text: part.text };\n\t\t\tcase \"image\":\n\t\t\t\treturn { type: \"image\", data: part.data, mimeType: part.mimeType };\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = part;\n\t\t\t\treturn exhaustive;\n\t\t\t}\n\t\t}\n\t});\n}\n\nexport function toProtocolToolResultMessage(\n\tmessage: ToolResultMessage,\n\toptions: ToolTranscriptOptions,\n): ToolTranscriptItem {\n\tconst callId = identifier(options.call.id, \"Tool call id\");\n\tconst callName = identifier(options.call.name, \"Tool call name\");\n\tif (identifier(message.toolCallId, \"Tool result call id\") !== callId) {\n\t\tthrow new TypeError(`Tool result ${message.toolCallId} does not match tool call ${callId}`);\n\t}\n\tif (identifier(message.toolName, \"Tool result name\") !== callName) {\n\t\tthrow new TypeError(`Tool result ${message.toolName} does not match tool call ${callName}`);\n\t}\n\tconst details = sanitizeProtocolDetails(message.details);\n\tconst usage = toProtocolUsage(message.usage);\n\tconst common = {\n\t\tid: identifier(options.id, \"Transcript item id\"),\n\t\trole: \"tool\",\n\t\ttoolCallId: callId,\n\t\ttoolName: callName,\n\t\tinput: toProtocolJsonValue(options.call.arguments),\n\t\tcontent: toProtocolToolContent(message.content),\n\t\t...(details === undefined ? {} : { details }),\n\t\t...(usage ? { usage } : {}),\n\t\ttimestamp: timestamp(message.timestamp),\n\t} as const;\n\treturn message.isError\n\t\t? ({ ...common, status: \"error\", isError: true } satisfies ToolTranscriptItem)\n\t\t: ({ ...common, status: \"complete\", isError: false } satisfies ToolTranscriptItem);\n}\n"]}
package/dist/protocol.js DELETED
@@ -1,266 +0,0 @@
1
- import { getSupportedThinkingLevels, } from "@earendil-works/pi-ai";
2
- function nonNegativeInteger(value) {
3
- if (value === undefined || !Number.isFinite(value))
4
- return undefined;
5
- return Math.max(0, Math.floor(value));
6
- }
7
- function nonNegativeNumber(value) {
8
- return Number.isFinite(value) ? Math.max(0, value) : 0;
9
- }
10
- function identifier(value, label) {
11
- if (typeof value !== "string" || value.length === 0)
12
- throw new TypeError(`${label} must be a non-empty string`);
13
- return value;
14
- }
15
- function timestamp(value) {
16
- if (!Number.isSafeInteger(value) || value < 0)
17
- throw new TypeError("Protocol timestamps must be non-negative integers");
18
- return value;
19
- }
20
- /** Validate and copy a value from an execution boundary into the protocol's JSON-compatible subset. */
21
- export function toProtocolJsonValue(value, seen = new Set()) {
22
- if (value === null || typeof value === "string" || typeof value === "boolean")
23
- return value;
24
- if (typeof value === "number") {
25
- if (!Number.isFinite(value))
26
- throw new TypeError("Protocol JSON numbers must be finite");
27
- return value;
28
- }
29
- if (typeof value !== "object")
30
- throw new TypeError(`Unsupported protocol JSON value: ${typeof value}`);
31
- if (seen.has(value))
32
- throw new TypeError("Protocol JSON values must not contain circular references");
33
- const prototype = Object.getPrototypeOf(value);
34
- if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {
35
- throw new TypeError("Protocol JSON objects must be plain objects");
36
- }
37
- seen.add(value);
38
- try {
39
- if (Array.isArray(value))
40
- return Array.from(value, (entry) => toProtocolJsonValue(entry, seen));
41
- const result = {};
42
- for (const [key, entry] of Object.entries(value))
43
- result[key] = toProtocolJsonValue(entry, seen);
44
- return result;
45
- }
46
- finally {
47
- seen.delete(value);
48
- }
49
- }
50
- /** Lossily sanitize diagnostic tool details that must not affect execution semantics. */
51
- export function sanitizeProtocolDetails(value, seen = new Set()) {
52
- if (value === null || typeof value === "string" || typeof value === "boolean")
53
- return value;
54
- if (typeof value === "number")
55
- return Number.isFinite(value) ? value : String(value);
56
- if (typeof value === "bigint")
57
- return value.toString();
58
- if (value === undefined || typeof value === "function" || typeof value === "symbol")
59
- return undefined;
60
- if (value instanceof Date)
61
- return value.toISOString();
62
- if (typeof value !== "object")
63
- return String(value);
64
- if (seen.has(value))
65
- return "[Circular]";
66
- seen.add(value);
67
- try {
68
- if (Array.isArray(value))
69
- return Array.from(value, (entry) => sanitizeProtocolDetails(entry, seen) ?? null);
70
- const result = {};
71
- for (const [key, entry] of Object.entries(value)) {
72
- const normalized = sanitizeProtocolDetails(entry, seen);
73
- if (normalized !== undefined)
74
- result[key] = normalized;
75
- }
76
- return result;
77
- }
78
- finally {
79
- seen.delete(value);
80
- }
81
- }
82
- export function toProtocolUsage(usage) {
83
- if (!usage)
84
- return undefined;
85
- const reasoning = nonNegativeInteger(usage.reasoning);
86
- const result = {
87
- input: nonNegativeInteger(usage.input) ?? 0,
88
- output: nonNegativeInteger(usage.output) ?? 0,
89
- cacheRead: nonNegativeInteger(usage.cacheRead) ?? 0,
90
- cacheWrite: nonNegativeInteger(usage.cacheWrite) ?? 0,
91
- ...(reasoning === undefined ? {} : { reasoning }),
92
- totalTokens: nonNegativeInteger(usage.totalTokens) ?? 0,
93
- cost: {
94
- input: nonNegativeNumber(usage.cost.input),
95
- output: nonNegativeNumber(usage.cost.output),
96
- cacheRead: nonNegativeNumber(usage.cost.cacheRead),
97
- cacheWrite: nonNegativeNumber(usage.cost.cacheWrite),
98
- total: nonNegativeNumber(usage.cost.total),
99
- },
100
- };
101
- return result;
102
- }
103
- export function toProtocolModelMetadata(model, authenticated) {
104
- const result = {
105
- provider: identifier(model.provider, "Model provider"),
106
- id: identifier(model.id, "Model id"),
107
- name: identifier(model.name, "Model name"),
108
- api: identifier(model.api, "Model API"),
109
- reasoning: model.reasoning,
110
- input: [...model.input],
111
- contextWindow: Math.max(1, Math.floor(model.contextWindow)),
112
- maxTokens: Math.max(1, Math.floor(model.maxTokens)),
113
- cost: {
114
- input: nonNegativeNumber(model.cost.input),
115
- output: nonNegativeNumber(model.cost.output),
116
- cacheRead: nonNegativeNumber(model.cost.cacheRead),
117
- cacheWrite: nonNegativeNumber(model.cost.cacheWrite),
118
- },
119
- supportedThinkingLevels: getSupportedThinkingLevels(model),
120
- authenticated,
121
- };
122
- return result;
123
- }
124
- function toProtocolUserContent(content) {
125
- if (typeof content === "string")
126
- return [{ type: "text", text: content }];
127
- return content.map((part) => {
128
- switch (part.type) {
129
- case "text":
130
- return { type: "text", text: part.text };
131
- case "image":
132
- return { type: "image", data: part.data, mimeType: part.mimeType };
133
- default: {
134
- const exhaustive = part;
135
- return exhaustive;
136
- }
137
- }
138
- });
139
- }
140
- export function toProtocolUserMessage(message, options) {
141
- const result = {
142
- id: identifier(options.id, "Transcript item id"),
143
- role: "user",
144
- content: toProtocolUserContent(message.content),
145
- timestamp: timestamp(message.timestamp),
146
- };
147
- return result;
148
- }
149
- function toProtocolAssistantContent(message) {
150
- return message.content.map((part) => {
151
- switch (part.type) {
152
- case "text":
153
- return { type: "text", text: part.text };
154
- case "thinking":
155
- return {
156
- type: "thinking",
157
- thinking: part.thinking,
158
- ...(part.redacted === undefined ? {} : { redacted: part.redacted }),
159
- };
160
- case "toolCall":
161
- return {
162
- type: "toolCall",
163
- toolCallId: identifier(part.id, "Tool call id"),
164
- toolName: identifier(part.name, "Tool call name"),
165
- input: toProtocolJsonValue(part.arguments),
166
- };
167
- default: {
168
- const exhaustive = part;
169
- return exhaustive;
170
- }
171
- }
172
- });
173
- }
174
- export function toProtocolAssistantMessage(message, options) {
175
- const usage = toProtocolUsage(message.usage);
176
- const common = {
177
- id: identifier(options.id, "Transcript item id"),
178
- role: "assistant",
179
- content: toProtocolAssistantContent(message),
180
- model: {
181
- provider: identifier(message.provider, "Assistant provider"),
182
- id: identifier(message.model, "Assistant model"),
183
- },
184
- ...(message.responseModel === undefined
185
- ? {}
186
- : { responseModel: identifier(message.responseModel, "Assistant response model") }),
187
- ...(usage ? { usage } : {}),
188
- timestamp: timestamp(message.timestamp),
189
- };
190
- switch (message.stopReason) {
191
- case "pending":
192
- return { ...common, status: "streaming" };
193
- case "stop":
194
- case "length":
195
- case "toolUse":
196
- return {
197
- ...common,
198
- status: "complete",
199
- stopReason: message.stopReason,
200
- };
201
- case "deferred":
202
- throw new TypeError("Deferred assistant messages are not supported by protocol v1");
203
- case "error":
204
- if (message.errorMessage?.length === 0) {
205
- throw new TypeError("Assistant error messages must not be empty");
206
- }
207
- return {
208
- ...common,
209
- status: "error",
210
- stopReason: "error",
211
- ...(message.errorMessage === undefined ? {} : { errorMessage: message.errorMessage }),
212
- };
213
- case "aborted":
214
- return {
215
- ...common,
216
- status: "aborted",
217
- stopReason: "aborted",
218
- ...(message.errorMessage === undefined ? {} : { errorMessage: message.errorMessage }),
219
- };
220
- default: {
221
- const exhaustive = message.stopReason;
222
- return exhaustive;
223
- }
224
- }
225
- }
226
- function toProtocolToolContent(content) {
227
- return content.map((part) => {
228
- switch (part.type) {
229
- case "text":
230
- return { type: "text", text: part.text };
231
- case "image":
232
- return { type: "image", data: part.data, mimeType: part.mimeType };
233
- default: {
234
- const exhaustive = part;
235
- return exhaustive;
236
- }
237
- }
238
- });
239
- }
240
- export function toProtocolToolResultMessage(message, options) {
241
- const callId = identifier(options.call.id, "Tool call id");
242
- const callName = identifier(options.call.name, "Tool call name");
243
- if (identifier(message.toolCallId, "Tool result call id") !== callId) {
244
- throw new TypeError(`Tool result ${message.toolCallId} does not match tool call ${callId}`);
245
- }
246
- if (identifier(message.toolName, "Tool result name") !== callName) {
247
- throw new TypeError(`Tool result ${message.toolName} does not match tool call ${callName}`);
248
- }
249
- const details = sanitizeProtocolDetails(message.details);
250
- const usage = toProtocolUsage(message.usage);
251
- const common = {
252
- id: identifier(options.id, "Transcript item id"),
253
- role: "tool",
254
- toolCallId: callId,
255
- toolName: callName,
256
- input: toProtocolJsonValue(options.call.arguments),
257
- content: toProtocolToolContent(message.content),
258
- ...(details === undefined ? {} : { details }),
259
- ...(usage ? { usage } : {}),
260
- timestamp: timestamp(message.timestamp),
261
- };
262
- return message.isError
263
- ? { ...common, status: "error", isError: true }
264
- : { ...common, status: "complete", isError: false };
265
- }
266
- //# sourceMappingURL=protocol.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ImageContent as AiImageContent,\n\ttype TextContent as AiTextContent,\n\ttype Usage as AiUsage,\n\ttype Api,\n\ttype AssistantMessage,\n\tgetSupportedThinkingLevels,\n\ttype Model,\n\ttype ModelThinkingLevel,\n\ttype ToolCall,\n\ttype ToolResultMessage,\n\ttype UserMessage,\n} from \"@earendil-works/pi-ai\";\nimport type {\n\tAssistantTranscriptItem,\n\tJsonValue,\n\tModelMetadata,\n\tThinkingLevel,\n\tToolTranscriptItem,\n\tUsage,\n\tUserTranscriptItem,\n} from \"@earendil-works/pi-protocol\";\n\ntype Assert<T extends true> = T;\ntype ExactKeys<T, Keys extends keyof T> = keyof T extends Keys ? true : false;\ntype _AiThinkingLevelsFitProtocol = Assert<ModelThinkingLevel extends ThinkingLevel ? true : false>;\ntype _ProtocolThinkingLevelsFitAi = Assert<ThinkingLevel extends ModelThinkingLevel ? true : false>;\ntype AiModelInput = Model<Api>[\"input\"][number];\ntype ProtocolModelInput = ModelMetadata[\"input\"][number];\ntype _AiModelInputsFitProtocol = Assert<AiModelInput extends ProtocolModelInput ? true : false>;\ntype _ProtocolModelInputsFitAi = Assert<ProtocolModelInput extends AiModelInput ? true : false>;\n/**\n * Enumerate mapped and intentionally omitted pi-ai fields so additions fail compilation here.\n * Provider replay metadata, diagnostics, cache-write retention splits, model transport settings,\n * model sampling defaults, pricing tiers, and deferred-tool availability remain intentionally\n * server-side.\n */\ntype _AiTextContentFieldsAccountedFor = Assert<ExactKeys<AiTextContent, \"type\" | \"text\" | \"textSignature\">>;\ntype _AiThinkingContentFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tExtract<AssistantMessage[\"content\"][number], { type: \"thinking\" }>,\n\t\t\"type\" | \"thinking\" | \"thinkingSignature\" | \"redacted\"\n\t>\n>;\ntype _AiImageContentFieldsAccountedFor = Assert<ExactKeys<AiImageContent, \"type\" | \"data\" | \"mimeType\">>;\ntype _AiToolCallFieldsAccountedFor = Assert<\n\tExactKeys<ToolCall, \"type\" | \"id\" | \"name\" | \"arguments\" | \"thoughtSignature\" | \"namespace\">\n>;\ntype _AiUsageFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tAiUsage,\n\t\t\"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\" | \"cacheWrite1h\" | \"reasoning\" | \"totalTokens\" | \"cost\"\n\t>\n>;\ntype _AiUsageCostFieldsAccountedFor = Assert<\n\tExactKeys<AiUsage[\"cost\"], \"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\" | \"total\">\n>;\ntype _AiModelFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tModel<Api>,\n\t\t| \"id\"\n\t\t| \"name\"\n\t\t| \"api\"\n\t\t| \"provider\"\n\t\t| \"baseUrl\"\n\t\t| \"reasoning\"\n\t\t| \"thinkingLevelMap\"\n\t\t| \"input\"\n\t\t| \"cost\"\n\t\t| \"contextWindow\"\n\t\t| \"maxTokens\"\n\t\t| \"samplingParams\"\n\t\t| \"headers\"\n\t\t| \"compat\"\n\t>\n>;\ntype _AiModelCostFieldsAccountedFor = Assert<\n\tExactKeys<Model<Api>[\"cost\"], \"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\" | \"tiers\">\n>;\ntype _AiUserMessageFieldsAccountedFor = Assert<ExactKeys<UserMessage, \"role\" | \"content\" | \"timestamp\">>;\ntype _AiAssistantMessageFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tAssistantMessage,\n\t\t| \"role\"\n\t\t| \"content\"\n\t\t| \"api\"\n\t\t| \"provider\"\n\t\t| \"model\"\n\t\t| \"responseModel\"\n\t\t| \"responseId\"\n\t\t| \"diagnostics\"\n\t\t| \"usage\"\n\t\t| \"stopReason\"\n\t\t| \"deferred\"\n\t\t| \"errorMessage\"\n\t\t| \"rawStopReason\"\n\t\t| \"endTurn\"\n\t\t| \"timestamp\"\n\t>\n>;\ntype _AiToolResultMessageFieldsAccountedFor = Assert<\n\tExactKeys<\n\t\tToolResultMessage,\n\t\t\"role\" | \"toolCallId\" | \"toolName\" | \"content\" | \"details\" | \"usage\" | \"addedToolNames\" | \"isError\" | \"timestamp\"\n\t>\n>;\n\nexport interface AssistantTranscriptOptions {\n\tid: string;\n}\n\nexport interface UserTranscriptOptions {\n\tid: string;\n}\n\nexport interface ToolTranscriptOptions {\n\tid: string;\n\tcall: ToolCall;\n}\n\nfunction nonNegativeInteger(value: number | undefined): number | undefined {\n\tif (value === undefined || !Number.isFinite(value)) return undefined;\n\treturn Math.max(0, Math.floor(value));\n}\n\nfunction nonNegativeNumber(value: number): number {\n\treturn Number.isFinite(value) ? Math.max(0, value) : 0;\n}\n\nfunction identifier(value: string, label: string): string {\n\tif (typeof value !== \"string\" || value.length === 0) throw new TypeError(`${label} must be a non-empty string`);\n\treturn value;\n}\n\nfunction timestamp(value: number): number {\n\tif (!Number.isSafeInteger(value) || value < 0)\n\t\tthrow new TypeError(\"Protocol timestamps must be non-negative integers\");\n\treturn value;\n}\n\n/** Validate and copy a value from an execution boundary into the protocol's JSON-compatible subset. */\nexport function toProtocolJsonValue(value: unknown, seen = new Set<object>()): JsonValue {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") return value;\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isFinite(value)) throw new TypeError(\"Protocol JSON numbers must be finite\");\n\t\treturn value;\n\t}\n\tif (typeof value !== \"object\") throw new TypeError(`Unsupported protocol JSON value: ${typeof value}`);\n\tif (seen.has(value)) throw new TypeError(\"Protocol JSON values must not contain circular references\");\n\tconst prototype = Object.getPrototypeOf(value);\n\tif (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {\n\t\tthrow new TypeError(\"Protocol JSON objects must be plain objects\");\n\t}\n\tseen.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) return Array.from(value, (entry) => toProtocolJsonValue(entry, seen));\n\t\tconst result: Record<string, JsonValue> = {};\n\t\tfor (const [key, entry] of Object.entries(value)) result[key] = toProtocolJsonValue(entry, seen);\n\t\treturn result;\n\t} finally {\n\t\tseen.delete(value);\n\t}\n}\n\n/** Lossily sanitize diagnostic tool details that must not affect execution semantics. */\nexport function sanitizeProtocolDetails(value: unknown, seen = new Set<object>()): JsonValue | undefined {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") return value;\n\tif (typeof value === \"number\") return Number.isFinite(value) ? value : String(value);\n\tif (typeof value === \"bigint\") return value.toString();\n\tif (value === undefined || typeof value === \"function\" || typeof value === \"symbol\") return undefined;\n\tif (value instanceof Date) return value.toISOString();\n\tif (typeof value !== \"object\") return String(value);\n\tif (seen.has(value)) return \"[Circular]\";\n\tseen.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) return Array.from(value, (entry) => sanitizeProtocolDetails(entry, seen) ?? null);\n\t\tconst result: Record<string, JsonValue> = {};\n\t\tfor (const [key, entry] of Object.entries(value)) {\n\t\t\tconst normalized = sanitizeProtocolDetails(entry, seen);\n\t\t\tif (normalized !== undefined) result[key] = normalized;\n\t\t}\n\t\treturn result;\n\t} finally {\n\t\tseen.delete(value);\n\t}\n}\n\nexport function toProtocolUsage(usage: AiUsage | undefined): Usage | undefined {\n\tif (!usage) return undefined;\n\tconst reasoning = nonNegativeInteger(usage.reasoning);\n\tconst result = {\n\t\tinput: nonNegativeInteger(usage.input) ?? 0,\n\t\toutput: nonNegativeInteger(usage.output) ?? 0,\n\t\tcacheRead: nonNegativeInteger(usage.cacheRead) ?? 0,\n\t\tcacheWrite: nonNegativeInteger(usage.cacheWrite) ?? 0,\n\t\t...(reasoning === undefined ? {} : { reasoning }),\n\t\ttotalTokens: nonNegativeInteger(usage.totalTokens) ?? 0,\n\t\tcost: {\n\t\t\tinput: nonNegativeNumber(usage.cost.input),\n\t\t\toutput: nonNegativeNumber(usage.cost.output),\n\t\t\tcacheRead: nonNegativeNumber(usage.cost.cacheRead),\n\t\t\tcacheWrite: nonNegativeNumber(usage.cost.cacheWrite),\n\t\t\ttotal: nonNegativeNumber(usage.cost.total),\n\t\t},\n\t} satisfies Usage;\n\treturn result;\n}\n\nexport function toProtocolModelMetadata(model: Model<Api>, authenticated: boolean): ModelMetadata {\n\tconst result = {\n\t\tprovider: identifier(model.provider, \"Model provider\"),\n\t\tid: identifier(model.id, \"Model id\"),\n\t\tname: identifier(model.name, \"Model name\"),\n\t\tapi: identifier(model.api, \"Model API\"),\n\t\treasoning: model.reasoning,\n\t\tinput: [...model.input],\n\t\tcontextWindow: Math.max(1, Math.floor(model.contextWindow)),\n\t\tmaxTokens: Math.max(1, Math.floor(model.maxTokens)),\n\t\tcost: {\n\t\t\tinput: nonNegativeNumber(model.cost.input),\n\t\t\toutput: nonNegativeNumber(model.cost.output),\n\t\t\tcacheRead: nonNegativeNumber(model.cost.cacheRead),\n\t\t\tcacheWrite: nonNegativeNumber(model.cost.cacheWrite),\n\t\t},\n\t\tsupportedThinkingLevels: getSupportedThinkingLevels(model),\n\t\tauthenticated,\n\t} satisfies ModelMetadata;\n\treturn result;\n}\n\nfunction toProtocolUserContent(content: UserMessage[\"content\"]): UserTranscriptItem[\"content\"] {\n\tif (typeof content === \"string\") return [{ type: \"text\", text: content }];\n\treturn content.map((part) => {\n\t\tswitch (part.type) {\n\t\t\tcase \"text\":\n\t\t\t\treturn { type: \"text\", text: part.text };\n\t\t\tcase \"image\":\n\t\t\t\treturn { type: \"image\", data: part.data, mimeType: part.mimeType };\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = part;\n\t\t\t\treturn exhaustive;\n\t\t\t}\n\t\t}\n\t});\n}\n\nexport function toProtocolUserMessage(message: UserMessage, options: UserTranscriptOptions): UserTranscriptItem {\n\tconst result = {\n\t\tid: identifier(options.id, \"Transcript item id\"),\n\t\trole: \"user\",\n\t\tcontent: toProtocolUserContent(message.content),\n\t\ttimestamp: timestamp(message.timestamp),\n\t} satisfies UserTranscriptItem;\n\treturn result;\n}\n\nfunction toProtocolAssistantContent(message: AssistantMessage): AssistantTranscriptItem[\"content\"] {\n\treturn message.content.map((part) => {\n\t\tswitch (part.type) {\n\t\t\tcase \"text\":\n\t\t\t\treturn { type: \"text\", text: part.text };\n\t\t\tcase \"thinking\":\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\tthinking: part.thinking,\n\t\t\t\t\t...(part.redacted === undefined ? {} : { redacted: part.redacted }),\n\t\t\t\t};\n\t\t\tcase \"toolCall\":\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\ttoolCallId: identifier(part.id, \"Tool call id\"),\n\t\t\t\t\ttoolName: identifier(part.name, \"Tool call name\"),\n\t\t\t\t\tinput: toProtocolJsonValue(part.arguments),\n\t\t\t\t};\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = part;\n\t\t\t\treturn exhaustive;\n\t\t\t}\n\t\t}\n\t});\n}\n\nexport function toProtocolAssistantMessage(\n\tmessage: AssistantMessage,\n\toptions: AssistantTranscriptOptions,\n): AssistantTranscriptItem {\n\tconst usage = toProtocolUsage(message.usage);\n\tconst common = {\n\t\tid: identifier(options.id, \"Transcript item id\"),\n\t\trole: \"assistant\",\n\t\tcontent: toProtocolAssistantContent(message),\n\t\tmodel: {\n\t\t\tprovider: identifier(message.provider, \"Assistant provider\"),\n\t\t\tid: identifier(message.model, \"Assistant model\"),\n\t\t},\n\t\t...(message.responseModel === undefined\n\t\t\t? {}\n\t\t\t: { responseModel: identifier(message.responseModel, \"Assistant response model\") }),\n\t\t...(usage ? { usage } : {}),\n\t\ttimestamp: timestamp(message.timestamp),\n\t} as const;\n\tswitch (message.stopReason) {\n\t\tcase \"pending\":\n\t\t\treturn { ...common, status: \"streaming\" } satisfies AssistantTranscriptItem;\n\t\tcase \"stop\":\n\t\tcase \"length\":\n\t\tcase \"toolUse\":\n\t\t\treturn {\n\t\t\t\t...common,\n\t\t\t\tstatus: \"complete\",\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t} satisfies AssistantTranscriptItem;\n\t\tcase \"deferred\":\n\t\t\tthrow new TypeError(\"Deferred assistant messages are not supported by protocol v1\");\n\t\tcase \"error\":\n\t\t\tif (message.errorMessage?.length === 0) {\n\t\t\t\tthrow new TypeError(\"Assistant error messages must not be empty\");\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...common,\n\t\t\t\tstatus: \"error\",\n\t\t\t\tstopReason: \"error\",\n\t\t\t\t...(message.errorMessage === undefined ? {} : { errorMessage: message.errorMessage }),\n\t\t\t} satisfies AssistantTranscriptItem;\n\t\tcase \"aborted\":\n\t\t\treturn {\n\t\t\t\t...common,\n\t\t\t\tstatus: \"aborted\",\n\t\t\t\tstopReason: \"aborted\",\n\t\t\t\t...(message.errorMessage === undefined ? {} : { errorMessage: message.errorMessage }),\n\t\t\t} satisfies AssistantTranscriptItem;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = message.stopReason;\n\t\t\treturn exhaustive;\n\t\t}\n\t}\n}\n\nfunction toProtocolToolContent(content: Array<AiTextContent | AiImageContent>): ToolTranscriptItem[\"content\"] {\n\treturn content.map((part) => {\n\t\tswitch (part.type) {\n\t\t\tcase \"text\":\n\t\t\t\treturn { type: \"text\", text: part.text };\n\t\t\tcase \"image\":\n\t\t\t\treturn { type: \"image\", data: part.data, mimeType: part.mimeType };\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = part;\n\t\t\t\treturn exhaustive;\n\t\t\t}\n\t\t}\n\t});\n}\n\nexport function toProtocolToolResultMessage(\n\tmessage: ToolResultMessage,\n\toptions: ToolTranscriptOptions,\n): ToolTranscriptItem {\n\tconst callId = identifier(options.call.id, \"Tool call id\");\n\tconst callName = identifier(options.call.name, \"Tool call name\");\n\tif (identifier(message.toolCallId, \"Tool result call id\") !== callId) {\n\t\tthrow new TypeError(`Tool result ${message.toolCallId} does not match tool call ${callId}`);\n\t}\n\tif (identifier(message.toolName, \"Tool result name\") !== callName) {\n\t\tthrow new TypeError(`Tool result ${message.toolName} does not match tool call ${callName}`);\n\t}\n\tconst details = sanitizeProtocolDetails(message.details);\n\tconst usage = toProtocolUsage(message.usage);\n\tconst common = {\n\t\tid: identifier(options.id, \"Transcript item id\"),\n\t\trole: \"tool\",\n\t\ttoolCallId: callId,\n\t\ttoolName: callName,\n\t\tinput: toProtocolJsonValue(options.call.arguments),\n\t\tcontent: toProtocolToolContent(message.content),\n\t\t...(details === undefined ? {} : { details }),\n\t\t...(usage ? { usage } : {}),\n\t\ttimestamp: timestamp(message.timestamp),\n\t} as const;\n\treturn message.isError\n\t\t? ({ ...common, status: \"error\", isError: true } satisfies ToolTranscriptItem)\n\t\t: ({ ...common, status: \"complete\", isError: false } satisfies ToolTranscriptItem);\n}\n"]}