@fragno-dev/pi-harness 0.0.0-stage → 0.0.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.
Files changed (140) hide show
  1. package/LICENSE.md +16 -0
  2. package/README.md +181 -2
  3. package/dist/browser/agent-harness-event-protocol-CuKubox1.js +666 -0
  4. package/dist/browser/agent-harness-event-protocol-CuKubox1.js.map +1 -0
  5. package/dist/browser/agent-harness-event-protocol-d6mH_9n3.d.ts +261 -0
  6. package/dist/browser/agent-harness-event-protocol-d6mH_9n3.d.ts.map +1 -0
  7. package/dist/browser/client/clients.d.ts +2 -0
  8. package/dist/browser/client/clients.js +2 -0
  9. package/dist/browser/client/pi-workflow-emission-stream.d.ts +13 -0
  10. package/dist/browser/client/pi-workflow-emission-stream.d.ts.map +1 -0
  11. package/dist/browser/client/pi-workflow-emission-stream.js +29 -0
  12. package/dist/browser/client/pi-workflow-emission-stream.js.map +1 -0
  13. package/dist/browser/client/react.d.ts +147 -0
  14. package/dist/browser/client/react.d.ts.map +1 -0
  15. package/dist/browser/client/react.js +187 -0
  16. package/dist/browser/client/react.js.map +1 -0
  17. package/dist/browser/client/solid.d.ts +150 -0
  18. package/dist/browser/client/solid.d.ts.map +1 -0
  19. package/dist/browser/client/solid.js +130 -0
  20. package/dist/browser/client/solid.js.map +1 -0
  21. package/dist/browser/client/svelte.d.ts +150 -0
  22. package/dist/browser/client/svelte.d.ts.map +1 -0
  23. package/dist/browser/client/svelte.js +132 -0
  24. package/dist/browser/client/svelte.js.map +1 -0
  25. package/dist/browser/client/vanilla.d.ts +177 -0
  26. package/dist/browser/client/vanilla.d.ts.map +1 -0
  27. package/dist/browser/client/vanilla.js +159 -0
  28. package/dist/browser/client/vanilla.js.map +1 -0
  29. package/dist/browser/client/vue.d.ts +150 -0
  30. package/dist/browser/client/vue.d.ts.map +1 -0
  31. package/dist/browser/client/vue.js +128 -0
  32. package/dist/browser/client/vue.js.map +1 -0
  33. package/dist/browser/client/workflow-lofi-session-projection.d.ts +17 -0
  34. package/dist/browser/client/workflow-lofi-session-projection.d.ts.map +1 -0
  35. package/dist/browser/client/workflow-lofi-session-projection.js +107 -0
  36. package/dist/browser/client/workflow-lofi-session-projection.js.map +1 -0
  37. package/dist/browser/clients-DYXj19je.d.ts +247 -0
  38. package/dist/browser/clients-DYXj19je.d.ts.map +1 -0
  39. package/dist/browser/clients-pIAk6ClP.js +1853 -0
  40. package/dist/browser/clients-pIAk6ClP.js.map +1 -0
  41. package/dist/browser/pi/context-usage.d.ts +20 -0
  42. package/dist/browser/pi/context-usage.d.ts.map +1 -0
  43. package/dist/browser/pi/context-usage.js +84 -0
  44. package/dist/browser/pi/context-usage.js.map +1 -0
  45. package/dist/browser/pi/harness/agent-harness-event-protocol.d.ts +2 -0
  46. package/dist/browser/pi/harness/agent-harness-event-protocol.js +2 -0
  47. package/dist/browser/pi/route-schemas.d.ts +2 -0
  48. package/dist/browser/pi/route-schemas.js +130 -0
  49. package/dist/browser/pi/route-schemas.js.map +1 -0
  50. package/dist/browser/pi/workflow-session-projection.d.ts +2 -0
  51. package/dist/browser/pi/workflow-session-projection.js +2 -0
  52. package/dist/browser/route-schemas-DQB5ZR1E.d.ts +109 -0
  53. package/dist/browser/route-schemas-DQB5ZR1E.d.ts.map +1 -0
  54. package/dist/browser/types-8q4RWFXT.js +23 -0
  55. package/dist/browser/types-8q4RWFXT.js.map +1 -0
  56. package/dist/browser/types-Dk856kV1.d.ts +527 -0
  57. package/dist/browser/types-Dk856kV1.d.ts.map +1 -0
  58. package/dist/browser/workflow-session-projection-CJ4MUkpJ.js +443 -0
  59. package/dist/browser/workflow-session-projection-CJ4MUkpJ.js.map +1 -0
  60. package/dist/browser/workflow-session-projection-Dj_a79GF.d.ts +93 -0
  61. package/dist/browser/workflow-session-projection-Dj_a79GF.d.ts.map +1 -0
  62. package/dist/node/pi/compact-command-outcome-projection.js +41 -0
  63. package/dist/node/pi/compact-command-outcome-projection.js.map +1 -0
  64. package/dist/node/pi/context-usage.d.ts +20 -0
  65. package/dist/node/pi/context-usage.d.ts.map +1 -0
  66. package/dist/node/pi/context-usage.js +84 -0
  67. package/dist/node/pi/context-usage.js.map +1 -0
  68. package/dist/node/pi/definition.d.ts +535 -0
  69. package/dist/node/pi/definition.d.ts.map +1 -0
  70. package/dist/node/pi/definition.js +71 -0
  71. package/dist/node/pi/definition.js.map +1 -0
  72. package/dist/node/pi/factory.d.ts +142 -0
  73. package/dist/node/pi/factory.d.ts.map +1 -0
  74. package/dist/node/pi/factory.js +22 -0
  75. package/dist/node/pi/factory.js.map +1 -0
  76. package/dist/node/pi/harness/agent-harness-event-protocol.d.ts +261 -0
  77. package/dist/node/pi/harness/agent-harness-event-protocol.d.ts.map +1 -0
  78. package/dist/node/pi/harness/agent-harness-event-protocol.js +588 -0
  79. package/dist/node/pi/harness/agent-harness-event-protocol.js.map +1 -0
  80. package/dist/node/pi/harness/json-value.js +82 -0
  81. package/dist/node/pi/harness/json-value.js.map +1 -0
  82. package/dist/node/pi/harness/pi-operation-completed.d.ts +16 -0
  83. package/dist/node/pi/harness/pi-operation-completed.d.ts.map +1 -0
  84. package/dist/node/pi/harness/pi-operation-completed.js +71 -0
  85. package/dist/node/pi/harness/pi-operation-completed.js.map +1 -0
  86. package/dist/node/pi/harness/session-storage.d.ts +62 -0
  87. package/dist/node/pi/harness/session-storage.d.ts.map +1 -0
  88. package/dist/node/pi/harness/session-storage.js +174 -0
  89. package/dist/node/pi/harness/session-storage.js.map +1 -0
  90. package/dist/node/pi/pi-jsonl-export.d.ts +11 -0
  91. package/dist/node/pi/pi-jsonl-export.d.ts.map +1 -0
  92. package/dist/node/pi/pi-jsonl-export.js +31 -0
  93. package/dist/node/pi/pi-jsonl-export.js.map +1 -0
  94. package/dist/node/pi/route-schemas.d.ts +109 -0
  95. package/dist/node/pi/route-schemas.d.ts.map +1 -0
  96. package/dist/node/pi/route-schemas.js +130 -0
  97. package/dist/node/pi/route-schemas.js.map +1 -0
  98. package/dist/node/pi/session-command-protocol.d.ts +14 -0
  99. package/dist/node/pi/session-command-protocol.d.ts.map +1 -0
  100. package/dist/node/pi/session-command-protocol.js +25 -0
  101. package/dist/node/pi/session-command-protocol.js.map +1 -0
  102. package/dist/node/pi/session-entry-projection.js +79 -0
  103. package/dist/node/pi/session-entry-projection.js.map +1 -0
  104. package/dist/node/pi/skills.d.ts +70 -0
  105. package/dist/node/pi/skills.d.ts.map +1 -0
  106. package/dist/node/pi/skills.js +150 -0
  107. package/dist/node/pi/skills.js.map +1 -0
  108. package/dist/node/pi/tools.d.ts +12 -0
  109. package/dist/node/pi/tools.d.ts.map +1 -0
  110. package/dist/node/pi/tools.js +8 -0
  111. package/dist/node/pi/tools.js.map +1 -0
  112. package/dist/node/pi/types.d.ts +165 -0
  113. package/dist/node/pi/types.d.ts.map +1 -0
  114. package/dist/node/pi/types.js +49 -0
  115. package/dist/node/pi/types.js.map +1 -0
  116. package/dist/node/pi/wait-for-command-step.js +46 -0
  117. package/dist/node/pi/wait-for-command-step.js.map +1 -0
  118. package/dist/node/pi/workflow-session-live-projection.js +220 -0
  119. package/dist/node/pi/workflow-session-live-projection.js.map +1 -0
  120. package/dist/node/pi/workflow-session-projection.d.ts +84 -0
  121. package/dist/node/pi/workflow-session-projection.d.ts.map +1 -0
  122. package/dist/node/pi/workflow-session-projection.js +100 -0
  123. package/dist/node/pi/workflow-session-projection.js.map +1 -0
  124. package/dist/node/pi/workflows/interactive-chat-workflow.d.ts +37 -0
  125. package/dist/node/pi/workflows/interactive-chat-workflow.d.ts.map +1 -0
  126. package/dist/node/pi/workflows/interactive-chat-workflow.js +219 -0
  127. package/dist/node/pi/workflows/interactive-chat-workflow.js.map +1 -0
  128. package/dist/node/pi/workflows/workflow-agent-harness.d.ts +142 -0
  129. package/dist/node/pi/workflows/workflow-agent-harness.d.ts.map +1 -0
  130. package/dist/node/pi/workflows/workflow-agent-harness.js +450 -0
  131. package/dist/node/pi/workflows/workflow-agent-harness.js.map +1 -0
  132. package/dist/node/routes.d.ts +631 -0
  133. package/dist/node/routes.d.ts.map +1 -0
  134. package/dist/node/routes.js +355 -0
  135. package/dist/node/routes.js.map +1 -0
  136. package/dist/node/schema.d.ts +5 -0
  137. package/dist/node/schema.d.ts.map +1 -0
  138. package/dist/node/schema.js +7 -0
  139. package/dist/node/schema.js.map +1 -0
  140. package/package.json +175 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-harness-event-protocol.js","names":["#idsByValue","#valuesById","#idsByMessage","#messagesById","#messages","#values","#encodeEvent","#advanceAssistantMetadata","#activeAssistantMetadata","#toolCallJsonByContentIndex","#decodeEvent","#activeAssistantMessage","#decodersByStream"],"sources":["../../../../src/pi/harness/agent-harness-event-protocol.ts"],"sourcesContent":["import type { AgentHarnessEvent, AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { AssistantMessage, AssistantMessageEvent, ToolCall } from \"@earendil-works/pi-ai\";\n\nexport interface PiHarnessEventStreamEncoder<TEncodedEvent> {\n encode(event: AgentHarnessEvent): TEncodedEvent;\n}\n\nexport type PiHarnessProjectedEvent = {\n readonly type: AgentHarnessEvent[\"type\"];\n};\n\nexport type PiHarnessFrontendToolCall = Omit<ToolCall, \"thoughtSignature\">;\n\nexport type PiHarnessFrontendAssistantContent =\n | Omit<Extract<AssistantMessage[\"content\"][number], { type: \"text\" }>, \"textSignature\">\n | Omit<Extract<AssistantMessage[\"content\"][number], { type: \"thinking\" }>, \"thinkingSignature\">\n | PiHarnessFrontendToolCall;\n\nexport type PiHarnessFrontendAssistantMessage = Omit<\n AssistantMessage,\n \"api\" | \"provider\" | \"model\" | \"responseModel\" | \"responseId\" | \"diagnostics\" | \"content\"\n> & {\n readonly content: PiHarnessFrontendAssistantContent[];\n};\n\nexport type PiHarnessFrontendMessage<TMessage> = TMessage extends AssistantMessage\n ? PiHarnessFrontendAssistantMessage\n : TMessage;\n\nexport type PiHarnessFrontendAgentMessage = PiHarnessFrontendMessage<AgentMessage>;\n\ntype PiHarnessFrontendAssistantMessageEvent<TEvent> = TEvent extends {\n partial: AssistantMessage;\n}\n ? Omit<TEvent, \"partial\" | \"toolCall\"> & {\n readonly partial: PiHarnessFrontendAssistantMessage;\n } & (TEvent extends { toolCall: ToolCall }\n ? { readonly toolCall: PiHarnessFrontendToolCall }\n : object)\n : TEvent;\n\ntype PiHarnessFrontendEventFields<TEvent> = {\n readonly [TKey in keyof TEvent]: TKey extends \"message\"\n ? PiHarnessFrontendMessage<TEvent[TKey]>\n : TKey extends\n | \"messages\"\n | \"toolResults\"\n | \"steer\"\n | \"followUp\"\n | \"nextTurn\"\n | \"clearedSteer\"\n | \"clearedFollowUp\"\n ? TEvent[TKey] extends readonly (infer TMessage)[]\n ? PiHarnessFrontendMessage<TMessage>[]\n : TEvent[TKey]\n : TKey extends \"assistantMessageEvent\"\n ? PiHarnessFrontendAssistantMessageEvent<TEvent[TKey]>\n : TEvent[TKey];\n};\n\n/** The frontend projection preserves every event while omitting provider-owned message metadata. */\nexport type PiHarnessFrontendEvent = AgentHarnessEvent extends infer TEvent\n ? TEvent extends PiHarnessProjectedEvent\n ? PiHarnessFrontendEventFields<TEvent>\n : never\n : never;\n\nexport interface PiHarnessEventStreamDecoder<TEncodedEvent> {\n decode(event: TEncodedEvent): PiHarnessFrontendEvent;\n}\n\n/** Projects each AgentHarness event into one frontend-safe event with the same event type. */\nexport interface PiHarnessEventProtocol {\n createEncoder(): PiHarnessEventStreamEncoder<unknown>;\n createDecoder(): PiHarnessEventStreamDecoder<unknown>;\n eventType(event: unknown): PiHarnessFrontendEvent[\"type\"];\n}\n\nexport const piToolCallArgumentsText = (toolCall: ToolCall): string | undefined => {\n const partialJson = (toolCall as ToolCall & { partialJson?: unknown }).partialJson;\n return typeof partialJson === \"string\" ? partialJson : undefined;\n};\n\nimport {\n assertPiHarnessJsonValue,\n freezePiHarnessJsonValue,\n snapshotPiHarnessJsonValue,\n} from \"./json-value\";\n\nexport type PiHarnessSubscribedEvent = Exclude<\n AgentHarnessEvent,\n | { type: \"before_agent_start\" }\n | { type: \"context\" }\n | { type: \"before_provider_request\" }\n | { type: \"before_provider_payload\" }\n | { type: \"tool_call\" }\n | { type: \"tool_result\" }\n | { type: \"session_before_compact\" }\n | { type: \"session_before_tree\" }\n>;\n\ntype EncodedValue<TValue = unknown> =\n | { readonly kind: \"value\"; readonly id: number; readonly value: TValue }\n | { readonly kind: \"reference\"; readonly id: number };\n\ntype ProjectedAgentMessage =\n | Exclude<AgentMessage, AssistantMessage>\n | PiHarnessFrontendAssistantMessage;\n\ntype EncodedMessage =\n | {\n readonly kind: \"message\";\n readonly id: number;\n readonly message: ProjectedAgentMessage;\n }\n | { readonly kind: \"reference\"; readonly id: number };\n\ntype StreamedAssistantMessageEvent = Exclude<\n AssistantMessageEvent,\n { type: \"start\" | \"done\" | \"error\" }\n>;\ntype ProjectedMessageUpdateEvent = Extract<\n PiHarnessFrontendEvent,\n { type: \"message_update\" }\n>[\"assistantMessageEvent\"];\n\ntype AssistantThinkingContent = Extract<AssistantMessage[\"content\"][number], { type: \"thinking\" }>;\ntype ProjectedAssistantMessage = PiHarnessFrontendAssistantMessage;\ntype AssistantMessageMetadata = Pick<\n AssistantMessage,\n \"usage\" | \"stopReason\" | \"errorMessage\" | \"timestamp\"\n>;\n\ntype CompactAssistantMessageEvent =\n | {\n readonly type: \"text_start\";\n readonly contentIndex: number;\n }\n | {\n readonly type: \"text_delta\";\n readonly contentIndex: number;\n readonly delta: string;\n }\n | {\n readonly type: \"text_end\";\n readonly contentIndex: number;\n readonly content: string;\n }\n | {\n readonly type: \"thinking_start\";\n readonly contentIndex: number;\n readonly redacted?: boolean;\n }\n | {\n readonly type: \"thinking_delta\";\n readonly contentIndex: number;\n readonly delta: string;\n readonly redacted?: boolean;\n }\n | {\n readonly type: \"thinking_end\";\n readonly contentIndex: number;\n readonly content: string;\n readonly redacted?: boolean;\n }\n | {\n readonly type: \"toolcall_start\";\n readonly contentIndex: number;\n readonly toolCall: PiHarnessFrontendToolCall;\n }\n | {\n readonly type: \"toolcall_delta\";\n readonly contentIndex: number;\n readonly delta: string;\n }\n | {\n readonly type: \"toolcall_end\";\n readonly contentIndex: number;\n readonly toolCall: PiHarnessFrontendToolCall;\n };\n\ntype CompactAssistantMessageMetadataUpdate = {\n readonly usage?: AssistantMessage[\"usage\"];\n readonly stopReason?: AssistantMessage[\"stopReason\"];\n readonly errorMessage?: string | null;\n readonly timestamp?: number;\n};\n\ntype CompactMessageUpdate = {\n readonly type: \"message_update\";\n readonly update: CompactAssistantMessageEvent;\n readonly metadata?: CompactAssistantMessageMetadataUpdate;\n};\n\nexport type PiHarnessCompactEvent =\n | { readonly type: \"raw\"; readonly event: PiHarnessSubscribedEvent }\n | { readonly type: \"agent_start\" }\n | { readonly type: \"agent_end\"; readonly messages: readonly EncodedMessage[] }\n | { readonly type: \"turn_start\" }\n | {\n readonly type: \"turn_end\";\n readonly message: EncodedMessage;\n readonly toolResults: readonly EncodedMessage[];\n }\n | { readonly type: \"message_start\"; readonly message: EncodedMessage }\n | CompactMessageUpdate\n | { readonly type: \"message_end\"; readonly message: EncodedMessage }\n | {\n readonly type: \"tool_execution_start\";\n readonly toolCallId: string;\n readonly toolName: string;\n readonly args: EncodedValue;\n }\n | {\n readonly type: \"tool_execution_update\";\n readonly toolCallId: string;\n readonly toolName: string;\n readonly args: EncodedValue;\n readonly partialResult: EncodedValue;\n }\n | {\n readonly type: \"tool_execution_end\";\n readonly toolCallId: string;\n readonly toolName: string;\n readonly result: EncodedValue;\n readonly isError: boolean;\n }\n | {\n readonly type: \"queue_update\";\n readonly steer: readonly EncodedMessage[];\n readonly followUp: readonly EncodedMessage[];\n readonly nextTurn: readonly EncodedMessage[];\n }\n | { readonly type: \"save_point\"; readonly hadPendingMutations: boolean }\n | {\n readonly type: \"abort\";\n readonly clearedSteer: readonly EncodedMessage[];\n readonly clearedFollowUp: readonly EncodedMessage[];\n }\n | { readonly type: \"settled\"; readonly nextTurnCount: number }\n | {\n readonly type: \"after_provider_response\";\n readonly status: number;\n readonly headers: EncodedValue<SubscribedEvent<\"after_provider_response\">[\"headers\"]>;\n }\n | {\n readonly type: \"session_compact\";\n readonly compactionEntry: EncodedValue<SubscribedEvent<\"session_compact\">[\"compactionEntry\"]>;\n readonly fromHook: boolean;\n }\n | {\n readonly type: \"session_tree\";\n readonly newLeafId: string | null;\n readonly oldLeafId: string | null;\n readonly summaryEntry?: EncodedValue<\n NonNullable<SubscribedEvent<\"session_tree\">[\"summaryEntry\"]>\n >;\n readonly fromHook?: boolean;\n }\n | {\n readonly type: \"retry_scheduled\";\n readonly operation: \"compaction\" | \"branch_summary\";\n readonly attempt: number;\n readonly maxAttempts: number;\n readonly delayMs: number;\n readonly errorMessage: string;\n }\n | {\n readonly type: \"retry_attempt_start\" | \"retry_finished\";\n readonly operation: \"compaction\" | \"branch_summary\";\n }\n | {\n readonly type: \"model_update\";\n readonly model: EncodedValue<SubscribedEvent<\"model_update\">[\"model\"]>;\n readonly previousModel?: EncodedValue<\n NonNullable<SubscribedEvent<\"model_update\">[\"previousModel\"]>\n >;\n readonly source: \"set\" | \"restore\";\n }\n | {\n readonly type: \"thinking_level_update\";\n readonly level: SubscribedEvent<\"thinking_level_update\">[\"level\"];\n readonly previousLevel: SubscribedEvent<\"thinking_level_update\">[\"previousLevel\"];\n }\n | {\n readonly type: \"tools_update\";\n readonly toolNames: readonly string[];\n readonly previousToolNames: readonly string[];\n readonly activeToolNames: readonly string[];\n readonly previousActiveToolNames: readonly string[];\n readonly source: \"set\" | \"restore\";\n }\n | {\n readonly type: \"resources_update\";\n readonly resources: EncodedValue<SubscribedEvent<\"resources_update\">[\"resources\"]>;\n readonly previousResources: EncodedValue<\n SubscribedEvent<\"resources_update\">[\"previousResources\"]\n >;\n };\n\ntype SubscribedEvent<TType extends PiHarnessSubscribedEvent[\"type\"]> = Extract<\n PiHarnessSubscribedEvent,\n { type: TType }\n>;\n\nconst assistantMessageMetadata = ({\n usage,\n stopReason,\n errorMessage,\n timestamp,\n}: AssistantMessage): AssistantMessageMetadata => ({\n usage,\n stopReason,\n errorMessage,\n timestamp,\n});\n\nconst compactAssistantMessageMetadataUpdate = (\n previous: AssistantMessageMetadata,\n next: AssistantMessage,\n): CompactAssistantMessageMetadataUpdate | undefined => {\n const update: CompactAssistantMessageMetadataUpdate = {\n ...(previous.usage.input === next.usage.input &&\n previous.usage.output === next.usage.output &&\n previous.usage.cacheRead === next.usage.cacheRead &&\n previous.usage.cacheWrite === next.usage.cacheWrite &&\n previous.usage.cacheWrite1h === next.usage.cacheWrite1h &&\n previous.usage.reasoning === next.usage.reasoning &&\n previous.usage.totalTokens === next.usage.totalTokens &&\n previous.usage.cost.input === next.usage.cost.input &&\n previous.usage.cost.output === next.usage.cost.output &&\n previous.usage.cost.cacheRead === next.usage.cost.cacheRead &&\n previous.usage.cost.cacheWrite === next.usage.cost.cacheWrite &&\n previous.usage.cost.total === next.usage.cost.total\n ? {}\n : { usage: next.usage }),\n ...(previous.stopReason === next.stopReason ? {} : { stopReason: next.stopReason }),\n ...(previous.errorMessage === next.errorMessage\n ? {}\n : { errorMessage: next.errorMessage ?? null }),\n ...(previous.timestamp === next.timestamp ? {} : { timestamp: next.timestamp }),\n };\n\n return Object.keys(update).length === 0 ? undefined : update;\n};\n\nconst assistantMessageWithMetadataUpdate = (\n message: ProjectedAssistantMessage,\n update: CompactAssistantMessageMetadataUpdate,\n): ProjectedAssistantMessage => ({\n ...message,\n ...(update.usage === undefined ? {} : { usage: update.usage }),\n ...(update.stopReason === undefined ? {} : { stopReason: update.stopReason }),\n ...(update.errorMessage === undefined\n ? {}\n : update.errorMessage === null\n ? { errorMessage: undefined }\n : { errorMessage: update.errorMessage }),\n ...(update.timestamp === undefined ? {} : { timestamp: update.timestamp }),\n});\n\nconst streamedMessageUpdateEvent = (\n event: AssistantMessageEvent,\n): StreamedAssistantMessageEvent => {\n switch (event.type) {\n case \"start\":\n case \"done\":\n case \"error\":\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_NON_STREAMING_MESSAGE_UPDATE\");\n case \"text_start\":\n case \"text_delta\":\n case \"text_end\":\n case \"thinking_start\":\n case \"thinking_delta\":\n case \"thinking_end\":\n case \"toolcall_start\":\n case \"toolcall_delta\":\n case \"toolcall_end\":\n return event;\n }\n\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_UNKNOWN_MESSAGE_UPDATE\");\n};\n\nconst thinkingContentFromPartial = (\n event: Extract<\n StreamedAssistantMessageEvent,\n { type: \"thinking_start\" | \"thinking_delta\" | \"thinking_end\" }\n >,\n): AssistantThinkingContent => {\n const content = event.partial.content[event.contentIndex];\n if (content?.type !== \"thinking\") {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_THINKING_UPDATE_WITHOUT_THINKING_BLOCK\");\n }\n return content;\n};\n\nconst toolCallFromPartial = (\n event: Extract<\n StreamedAssistantMessageEvent,\n { type: \"toolcall_start\" | \"toolcall_delta\" | \"toolcall_end\" }\n >,\n): ToolCall => {\n const content = event.partial.content[event.contentIndex];\n if (content?.type !== \"toolCall\") {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_TOOL_CALL_UPDATE_WITHOUT_TOOL_CALL_BLOCK\");\n }\n return content;\n};\n\nconst thinkingMetadataFields = (\n content: AssistantThinkingContent,\n): Pick<AssistantThinkingContent, \"redacted\"> =>\n content.redacted === undefined ? {} : { redacted: content.redacted };\n\nconst projectToolCall = ({\n thoughtSignature: _thoughtSignature,\n ...toolCall\n}: ToolCall): PiHarnessFrontendToolCall => toolCall;\n\nconst projectAssistantMessage = ({\n api: _api,\n provider: _provider,\n model: _model,\n responseModel: _responseModel,\n responseId: _responseId,\n diagnostics: _diagnostics,\n content,\n ...message\n}: AssistantMessage): ProjectedAssistantMessage => ({\n ...message,\n content: content.map((block) => {\n switch (block.type) {\n case \"text\": {\n const { textSignature: _textSignature, ...text } = block;\n return text;\n }\n case \"thinking\": {\n const { thinkingSignature: _thinkingSignature, ...thinking } = block;\n return thinking;\n }\n case \"toolCall\":\n return projectToolCall(block);\n }\n\n throw new Error(\"PI_HARNESS_FRONTEND_PROTOCOL_UNKNOWN_ASSISTANT_CONTENT\");\n }),\n});\n\nconst projectAgentMessage = (message: AgentMessage): ProjectedAgentMessage =>\n message.role === \"assistant\" ? projectAssistantMessage(message) : message;\n\nconst compactAssistantMessageEvent = (\n event: StreamedAssistantMessageEvent,\n): CompactAssistantMessageEvent => {\n switch (event.type) {\n case \"text_start\":\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n };\n case \"text_delta\":\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n delta: event.delta,\n };\n case \"text_end\":\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n content: event.content,\n };\n case \"thinking_start\": {\n const content = thinkingContentFromPartial(event);\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n ...thinkingMetadataFields(content),\n };\n }\n case \"thinking_delta\": {\n const content = thinkingContentFromPartial(event);\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n delta: event.delta,\n ...thinkingMetadataFields(content),\n };\n }\n case \"thinking_end\": {\n const content = thinkingContentFromPartial(event);\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n content: event.content,\n ...thinkingMetadataFields(content),\n };\n }\n case \"toolcall_start\":\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n toolCall: projectToolCall(toolCallFromPartial(event)),\n };\n case \"toolcall_delta\":\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n delta: event.delta,\n };\n case \"toolcall_end\":\n return {\n type: event.type,\n contentIndex: event.contentIndex,\n toolCall: projectToolCall(event.toolCall),\n };\n }\n\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_UNKNOWN_STREAMING_MESSAGE_UPDATE\");\n};\n\nfunction isPrimitiveOnlyMessageUpdate(event: PiHarnessCompactEvent): event is CompactMessageUpdate {\n if (event.type !== \"message_update\") {\n return false;\n }\n\n // Metadata updates can contain provider-owned usage objects that still require a snapshot.\n if (event.metadata !== undefined) {\n return false;\n }\n\n switch (event.update.type) {\n case \"text_start\":\n case \"text_delta\":\n case \"text_end\":\n case \"thinking_start\":\n case \"thinking_delta\":\n case \"thinking_end\":\n case \"toolcall_delta\":\n return true;\n case \"toolcall_start\":\n case \"toolcall_end\":\n return false;\n }\n\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_UNKNOWN_COMPACT_MESSAGE_UPDATE\");\n}\n\nconst streamedAssistantMessageEvent = (\n update: CompactAssistantMessageEvent,\n partial: ProjectedAssistantMessage,\n): ProjectedMessageUpdateEvent => {\n switch (update.type) {\n case \"text_start\":\n case \"thinking_start\":\n case \"toolcall_start\":\n return { type: update.type, contentIndex: update.contentIndex, partial };\n case \"text_delta\":\n case \"thinking_delta\":\n case \"toolcall_delta\":\n return {\n type: update.type,\n contentIndex: update.contentIndex,\n delta: update.delta,\n partial,\n };\n case \"text_end\":\n case \"thinking_end\":\n return {\n type: update.type,\n contentIndex: update.contentIndex,\n content: update.content,\n partial,\n };\n case \"toolcall_end\":\n return {\n type: update.type,\n contentIndex: update.contentIndex,\n toolCall: update.toolCall,\n partial,\n };\n }\n\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_UNKNOWN_COMPACT_MESSAGE_UPDATE\");\n};\n\nconst applyCompactAssistantMessageEvent = (\n message: ProjectedAssistantMessage,\n update: CompactAssistantMessageEvent,\n toolCallJsonByContentIndex: Map<number, string>,\n): void => {\n if (update.contentIndex > message.content.length) {\n throw new Error(`PI_HARNESS_EVENT_PROTOCOL_SPARSE_CONTENT_INDEX:${update.contentIndex}`);\n }\n\n switch (update.type) {\n case \"text_start\":\n message.content[update.contentIndex] = {\n type: \"text\",\n text: \"\",\n };\n return;\n case \"text_delta\": {\n const content = message.content[update.contentIndex];\n if (content?.type !== \"text\") {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_TEXT_DELTA_WITHOUT_TEXT_BLOCK\");\n }\n message.content[update.contentIndex] = {\n type: \"text\",\n text: content.text + update.delta,\n };\n return;\n }\n case \"text_end\":\n message.content[update.contentIndex] = {\n type: \"text\",\n text: update.content,\n };\n return;\n case \"thinking_start\":\n message.content[update.contentIndex] = {\n type: \"thinking\",\n thinking: \"\",\n ...(update.redacted === undefined ? {} : { redacted: update.redacted }),\n };\n return;\n case \"thinking_delta\": {\n const content = message.content[update.contentIndex];\n if (content?.type !== \"thinking\") {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_THINKING_DELTA_WITHOUT_THINKING_BLOCK\");\n }\n message.content[update.contentIndex] = {\n type: \"thinking\",\n thinking: content.thinking + update.delta,\n ...(update.redacted === undefined ? {} : { redacted: update.redacted }),\n };\n return;\n }\n case \"thinking_end\":\n message.content[update.contentIndex] = {\n type: \"thinking\",\n thinking: update.content,\n ...(update.redacted === undefined ? {} : { redacted: update.redacted }),\n };\n return;\n case \"toolcall_start\":\n toolCallJsonByContentIndex.set(\n update.contentIndex,\n piToolCallArgumentsText(update.toolCall) ?? \"\",\n );\n message.content[update.contentIndex] = update.toolCall;\n return;\n case \"toolcall_delta\": {\n const content = message.content[update.contentIndex];\n if (content?.type !== \"toolCall\") {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_TOOL_CALL_DELTA_WITHOUT_TOOL_CALL_BLOCK\");\n }\n const previousJson = toolCallJsonByContentIndex.get(update.contentIndex);\n if (previousJson === undefined) {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_TOOL_CALL_DELTA_WITHOUT_TOOL_CALL_START\");\n }\n const partialJson = previousJson + update.delta;\n toolCallJsonByContentIndex.set(update.contentIndex, partialJson);\n message.content[update.contentIndex] = {\n ...content,\n partialJson,\n } as PiHarnessFrontendToolCall;\n return;\n }\n case \"toolcall_end\":\n toolCallJsonByContentIndex.delete(update.contentIndex);\n message.content[update.contentIndex] = update.toolCall;\n }\n};\n\nclass ValueEncoder {\n readonly #idsByValue = new Map<unknown, number>();\n\n encode<TValue>(value: TValue): EncodedValue<TValue> {\n const existingId = this.#idsByValue.get(value);\n if (existingId !== undefined) {\n return { kind: \"reference\", id: existingId };\n }\n\n const id = this.#idsByValue.size;\n this.#idsByValue.set(value, id);\n return { kind: \"value\", id, value };\n }\n}\n\nclass ValueDecoder {\n readonly #valuesById = new Map<number, unknown>();\n\n decode<TValue>(encoded: EncodedValue<TValue>): TValue {\n if (encoded.kind === \"value\") {\n this.#valuesById.set(encoded.id, encoded.value);\n return encoded.value;\n }\n\n if (!this.#valuesById.has(encoded.id)) {\n throw new Error(`PI_HARNESS_EVENT_PROTOCOL_UNKNOWN_VALUE_REFERENCE:${encoded.id}`);\n }\n return this.#valuesById.get(encoded.id) as TValue;\n }\n}\n\nclass MessageEncoder {\n readonly #idsByMessage = new Map<AgentMessage, number>();\n\n encode(message: AgentMessage): EncodedMessage {\n const existingId = this.#idsByMessage.get(message);\n if (existingId !== undefined) {\n return { kind: \"reference\", id: existingId };\n }\n\n const id = this.#idsByMessage.size;\n this.#idsByMessage.set(message, id);\n return { kind: \"message\", id, message: projectAgentMessage(message) };\n }\n}\n\nclass MessageDecoder {\n readonly #messagesById = new Map<number, ProjectedAgentMessage>();\n\n decode(encoded: EncodedMessage): ProjectedAgentMessage {\n if (encoded.kind === \"message\") {\n this.#messagesById.set(encoded.id, encoded.message);\n return encoded.message;\n }\n\n const message = this.#messagesById.get(encoded.id);\n if (!message) {\n throw new Error(`PI_HARNESS_EVENT_PROTOCOL_UNKNOWN_MESSAGE_REFERENCE:${encoded.id}`);\n }\n return message;\n }\n}\n\nexport type PiHarnessEncodedEvent = {\n readonly protocol: \"pi-harness-event\";\n readonly version: 2;\n readonly event: PiHarnessCompactEvent;\n};\n\nexport type PiHarnessEncodedEventEmission = {\n readonly kind: \"harness-event\";\n readonly event: PiHarnessEncodedEvent;\n};\n\nexport type PiHarnessEventStreamIdentity = {\n readonly stepKey: string;\n readonly executionId: string;\n readonly epoch: string;\n};\n\nconst piHarnessEventStreamKey = ({\n stepKey,\n executionId,\n epoch,\n}: PiHarnessEventStreamIdentity): string => `${stepKey}\\u0000${executionId}\\u0000${epoch}`;\n\nfunction assertPiHarnessEncodedEvent(value: unknown): asserts value is PiHarnessEncodedEvent {\n assertPiHarnessJsonValue(value);\n if (\n typeof value !== \"object\" ||\n value === null ||\n !(\"protocol\" in value) ||\n value.protocol !== \"pi-harness-event\" ||\n !(\"version\" in value) ||\n value.version !== 2 ||\n !(\"event\" in value) ||\n typeof value.event !== \"object\" ||\n value.event === null ||\n !(\"type\" in value.event) ||\n typeof value.event.type !== \"string\"\n ) {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_UNSUPPORTED_PROTOCOL\");\n }\n}\n\nexport class PiHarnessEventEncoder {\n readonly #messages = new MessageEncoder();\n readonly #values = new ValueEncoder();\n #activeAssistantMetadata: AssistantMessageMetadata | undefined;\n\n encode(event: PiHarnessSubscribedEvent): PiHarnessEncodedEvent {\n const compactEvent = this.#encodeEvent(event);\n const envelope: PiHarnessEncodedEvent = {\n protocol: \"pi-harness-event\",\n version: 2,\n event: compactEvent,\n };\n const encoded = isPrimitiveOnlyMessageUpdate(compactEvent)\n ? envelope\n : snapshotPiHarnessJsonValue(envelope, `$event.${event.type}`);\n this.#advanceAssistantMetadata(event, compactEvent);\n return encoded;\n }\n\n #advanceAssistantMetadata(\n event: PiHarnessSubscribedEvent,\n compactEvent: PiHarnessCompactEvent,\n ): void {\n if (event.type === \"message_start\") {\n this.#activeAssistantMetadata =\n event.message.role === \"assistant\"\n ? snapshotPiHarnessJsonValue(assistantMessageMetadata(event.message))\n : undefined;\n } else if (event.type === \"message_update\") {\n if (compactEvent.type !== \"message_update\") {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_INVALID_COMPACT_MESSAGE_UPDATE\");\n }\n if (compactEvent.metadata === undefined) {\n return;\n }\n this.#activeAssistantMetadata = snapshotPiHarnessJsonValue(\n assistantMessageMetadata(\n (event.assistantMessageEvent as StreamedAssistantMessageEvent).partial,\n ),\n );\n } else if (event.type === \"message_end\") {\n this.#activeAssistantMetadata = undefined;\n }\n }\n\n #encodeEvent(event: PiHarnessSubscribedEvent): PiHarnessCompactEvent {\n switch (event.type) {\n case \"agent_start\":\n case \"turn_start\":\n return { type: event.type };\n case \"agent_end\":\n return {\n type: event.type,\n messages: event.messages.map((message) => this.#messages.encode(message)),\n };\n case \"turn_end\":\n return {\n type: event.type,\n message: this.#messages.encode(event.message),\n toolResults: event.toolResults.map((message) => this.#messages.encode(message)),\n };\n case \"message_start\":\n return {\n type: event.type,\n message: this.#messages.encode(event.message),\n };\n case \"message_update\": {\n if (!this.#activeAssistantMetadata) {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_MESSAGE_UPDATE_WITHOUT_ASSISTANT_START\");\n }\n if (event.message.role !== \"assistant\") {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_MESSAGE_UPDATE_NOT_ASSISTANT\");\n }\n\n const assistantMessageEvent = streamedMessageUpdateEvent(event.assistantMessageEvent);\n const update = compactAssistantMessageEvent(assistantMessageEvent);\n const metadata = compactAssistantMessageMetadataUpdate(\n this.#activeAssistantMetadata,\n assistantMessageEvent.partial,\n );\n\n return {\n type: event.type,\n update,\n ...(metadata ? { metadata } : {}),\n };\n }\n case \"message_end\":\n return {\n type: event.type,\n message: this.#messages.encode(event.message),\n };\n case \"tool_execution_start\":\n return { ...event, args: this.#values.encode(event.args) };\n case \"tool_execution_update\":\n return {\n ...event,\n args: this.#values.encode(event.args),\n partialResult: this.#values.encode(\n snapshotPiHarnessJsonValue(\n event.partialResult,\n \"$event.tool_execution_update.partialResult\",\n ),\n ),\n };\n case \"tool_execution_end\":\n return { ...event, result: this.#values.encode(event.result) };\n case \"queue_update\":\n return {\n type: event.type,\n steer: event.steer.map((message) => this.#messages.encode(message)),\n followUp: event.followUp.map((message) => this.#messages.encode(message)),\n nextTurn: event.nextTurn.map((message) => this.#messages.encode(message)),\n };\n case \"save_point\":\n case \"settled\":\n case \"retry_scheduled\":\n case \"retry_attempt_start\":\n case \"retry_finished\":\n case \"thinking_level_update\":\n case \"tools_update\":\n return event;\n case \"abort\":\n return {\n type: event.type,\n clearedSteer: event.clearedSteer.map((message) => this.#messages.encode(message)),\n clearedFollowUp: event.clearedFollowUp.map((message) => this.#messages.encode(message)),\n };\n case \"after_provider_response\":\n return {\n type: event.type,\n status: event.status,\n headers: this.#values.encode(event.headers),\n };\n case \"session_compact\":\n return {\n type: event.type,\n compactionEntry: this.#values.encode(event.compactionEntry),\n fromHook: event.fromHook,\n };\n case \"session_tree\":\n return {\n type: event.type,\n newLeafId: event.newLeafId,\n oldLeafId: event.oldLeafId,\n ...(event.summaryEntry ? { summaryEntry: this.#values.encode(event.summaryEntry) } : {}),\n ...(event.fromHook === undefined ? {} : { fromHook: event.fromHook }),\n };\n case \"model_update\":\n return {\n type: event.type,\n model: this.#values.encode(event.model),\n ...(event.previousModel\n ? { previousModel: this.#values.encode(event.previousModel) }\n : {}),\n source: event.source,\n };\n case \"resources_update\":\n return {\n type: event.type,\n resources: this.#values.encode(event.resources),\n previousResources: this.#values.encode(event.previousResources),\n };\n default:\n return { type: \"raw\", event };\n }\n }\n}\n\nexport const piHarnessEventProtocol = {\n createEncoder: () => new PiHarnessEventEncoder(),\n createDecoder: () => new PiHarnessEventDecoder(),\n eventType: (value) => {\n assertPiHarnessEncodedEvent(value);\n return value.event.type === \"raw\" ? value.event.event.type : value.event.type;\n },\n} satisfies PiHarnessEventProtocol;\n\nexport class PiHarnessEventDecoder {\n readonly #messages = new MessageDecoder();\n readonly #values = new ValueDecoder();\n readonly #toolCallJsonByContentIndex = new Map<number, string>();\n #activeAssistantMessage: ProjectedAssistantMessage | undefined;\n\n decode(value: unknown): PiHarnessFrontendEvent {\n const encoded = freezePiHarnessJsonValue(value);\n assertPiHarnessEncodedEvent(encoded);\n return this.#decodeEvent(encoded.event);\n }\n\n #decodeEvent(encoded: PiHarnessCompactEvent): PiHarnessFrontendEvent {\n switch (encoded.type) {\n case \"raw\":\n return encoded.event as PiHarnessFrontendEvent;\n case \"agent_start\":\n case \"turn_start\":\n return { type: encoded.type };\n case \"agent_end\":\n return {\n type: encoded.type,\n messages: encoded.messages.map((message) => this.#messages.decode(message)),\n };\n case \"turn_end\":\n return {\n type: encoded.type,\n message: this.#messages.decode(encoded.message),\n toolResults: encoded.toolResults.map((message) =>\n this.#messages.decode(message),\n ) as SubscribedEvent<\"turn_end\">[\"toolResults\"],\n };\n case \"message_start\": {\n const message = this.#messages.decode(encoded.message);\n this.#toolCallJsonByContentIndex.clear();\n this.#activeAssistantMessage = message.role === \"assistant\" ? message : undefined;\n return { type: encoded.type, message };\n }\n case \"message_update\": {\n if (!this.#activeAssistantMessage) {\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_MESSAGE_UPDATE_WITHOUT_ASSISTANT_START\");\n }\n\n const reconstructedMessage = snapshotPiHarnessJsonValue(this.#activeAssistantMessage);\n applyCompactAssistantMessageEvent(\n reconstructedMessage,\n encoded.update,\n this.#toolCallJsonByContentIndex,\n );\n const message = encoded.metadata\n ? assistantMessageWithMetadataUpdate(reconstructedMessage, encoded.metadata)\n : reconstructedMessage;\n this.#activeAssistantMessage = message;\n\n return {\n type: encoded.type,\n message,\n assistantMessageEvent: streamedAssistantMessageEvent(encoded.update, message),\n };\n }\n case \"message_end\": {\n const message = this.#messages.decode(encoded.message);\n this.#toolCallJsonByContentIndex.clear();\n this.#activeAssistantMessage = undefined;\n return { type: encoded.type, message };\n }\n case \"tool_execution_start\":\n return { ...encoded, args: this.#values.decode(encoded.args) };\n case \"tool_execution_update\":\n return {\n ...encoded,\n args: this.#values.decode(encoded.args),\n partialResult: this.#values.decode(encoded.partialResult),\n };\n case \"tool_execution_end\":\n return { ...encoded, result: this.#values.decode(encoded.result) };\n case \"queue_update\":\n return {\n type: encoded.type,\n steer: encoded.steer.map((message) => this.#messages.decode(message)),\n followUp: encoded.followUp.map((message) => this.#messages.decode(message)),\n nextTurn: encoded.nextTurn.map((message) => this.#messages.decode(message)),\n };\n case \"save_point\":\n case \"settled\":\n case \"retry_scheduled\":\n case \"retry_attempt_start\":\n case \"retry_finished\":\n case \"thinking_level_update\":\n return encoded;\n case \"tools_update\":\n return {\n ...encoded,\n toolNames: [...encoded.toolNames],\n previousToolNames: [...encoded.previousToolNames],\n activeToolNames: [...encoded.activeToolNames],\n previousActiveToolNames: [...encoded.previousActiveToolNames],\n };\n case \"abort\":\n return {\n type: encoded.type,\n clearedSteer: encoded.clearedSteer.map((message) => this.#messages.decode(message)),\n clearedFollowUp: encoded.clearedFollowUp.map((message) => this.#messages.decode(message)),\n };\n case \"after_provider_response\":\n return {\n type: encoded.type,\n status: encoded.status,\n headers: this.#values.decode(encoded.headers),\n };\n case \"session_compact\":\n return {\n type: encoded.type,\n compactionEntry: this.#values.decode(encoded.compactionEntry),\n fromHook: encoded.fromHook,\n };\n case \"session_tree\":\n return {\n type: encoded.type,\n newLeafId: encoded.newLeafId,\n oldLeafId: encoded.oldLeafId,\n ...(encoded.summaryEntry\n ? {\n summaryEntry: this.#values.decode(encoded.summaryEntry),\n }\n : {}),\n ...(encoded.fromHook === undefined ? {} : { fromHook: encoded.fromHook }),\n };\n case \"model_update\":\n return {\n type: encoded.type,\n model: this.#values.decode(encoded.model),\n previousModel: encoded.previousModel\n ? this.#values.decode(encoded.previousModel)\n : undefined,\n source: encoded.source,\n };\n case \"resources_update\":\n return {\n type: encoded.type,\n resources: this.#values.decode(encoded.resources),\n previousResources: this.#values.decode(encoded.previousResources),\n };\n }\n\n throw new Error(\"PI_HARNESS_EVENT_PROTOCOL_UNKNOWN_EVENT\");\n }\n}\n\nexport class PiHarnessEventStreamDecoders {\n readonly #decodersByStream = new Map<string, PiHarnessEventDecoder>();\n\n start(identity: PiHarnessEventStreamIdentity): void {\n this.#decodersByStream.set(piHarnessEventStreamKey(identity), new PiHarnessEventDecoder());\n }\n\n finish(identity: PiHarnessEventStreamIdentity): void {\n this.#decodersByStream.delete(piHarnessEventStreamKey(identity));\n }\n\n decode(identity: PiHarnessEventStreamIdentity, event: unknown): PiHarnessFrontendEvent {\n const streamKey = piHarnessEventStreamKey(identity);\n const decoder = this.#decodersByStream.get(streamKey);\n if (!decoder) {\n throw new Error(`Pi harness event stream ${JSON.stringify(streamKey)} was not started.`);\n }\n return decoder.decode(event);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ //#region src/pi/harness/json-value.ts
2
+ const jsonValueError = (path, reason) => /* @__PURE__ */ new Error(`PI_HARNESS_EVENT_VALUE_NOT_JSON_COMPATIBLE:${path}:${reason}`);
3
+ const snapshotJsonValueAtPath = (value, path, ancestors) => {
4
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
5
+ if (typeof value === "number") {
6
+ if (!Number.isFinite(value)) throw jsonValueError(path, "non-finite-number");
7
+ return value;
8
+ }
9
+ if (typeof value !== "object") throw jsonValueError(path, typeof value);
10
+ if (ancestors.has(value)) throw jsonValueError(path, "cycle");
11
+ ancestors.add(value);
12
+ try {
13
+ if (Array.isArray(value)) {
14
+ const snapshot = [];
15
+ for (let index = 0; index < value.length; index += 1) {
16
+ if (!Object.hasOwn(value, index)) throw jsonValueError(`${path}[${index}]`, "sparse-array-entry");
17
+ snapshot.push(snapshotJsonValueAtPath(value[index], `${path}[${index}]`, ancestors));
18
+ }
19
+ return snapshot;
20
+ }
21
+ const prototype = Object.getPrototypeOf(value);
22
+ if (prototype !== Object.prototype && prototype !== null) throw jsonValueError(path, "non-plain-object");
23
+ const snapshot = {};
24
+ for (const key of Object.keys(value)) {
25
+ const propertyValue = value[key];
26
+ if (propertyValue === void 0) continue;
27
+ snapshot[key] = snapshotJsonValueAtPath(propertyValue, `${path}.${key}`, ancestors);
28
+ }
29
+ return snapshot;
30
+ } finally {
31
+ ancestors.delete(value);
32
+ }
33
+ };
34
+ /** Validates and detaches a value at the workflow JSON boundary. */
35
+ const snapshotPiHarnessJsonValue = (value, path = "$event") => snapshotJsonValueAtPath(value, path, /* @__PURE__ */ new Set());
36
+ const assertJsonValueAtPath = (value, path, ancestors) => {
37
+ if (value === null || typeof value === "string" || typeof value === "boolean") return;
38
+ if (typeof value === "number") {
39
+ if (!Number.isFinite(value)) throw jsonValueError(path, "non-finite-number");
40
+ return;
41
+ }
42
+ if (typeof value !== "object") throw jsonValueError(path, typeof value);
43
+ if (ancestors.has(value)) throw jsonValueError(path, "cycle");
44
+ ancestors.add(value);
45
+ try {
46
+ if (Array.isArray(value)) {
47
+ for (let index = 0; index < value.length; index += 1) {
48
+ if (!Object.hasOwn(value, index)) throw jsonValueError(`${path}[${index}]`, "sparse-array-entry");
49
+ assertJsonValueAtPath(value[index], `${path}[${index}]`, ancestors);
50
+ }
51
+ return;
52
+ }
53
+ const prototype = Object.getPrototypeOf(value);
54
+ if (prototype !== Object.prototype && prototype !== null) throw jsonValueError(path, "non-plain-object");
55
+ for (const key of Object.keys(value)) {
56
+ const propertyValue = value[key];
57
+ if (propertyValue !== void 0) assertJsonValueAtPath(propertyValue, `${path}.${key}`, ancestors);
58
+ }
59
+ } finally {
60
+ ancestors.delete(value);
61
+ }
62
+ };
63
+ /** Validates persisted protocol input without copying it. */
64
+ const assertPiHarnessJsonValue = (value, path = "$event") => {
65
+ assertJsonValueAtPath(value, path, /* @__PURE__ */ new Set());
66
+ };
67
+ const freezeJsonValue = (value, visited) => {
68
+ if (value === null || typeof value !== "object" || visited.has(value)) return;
69
+ visited.add(value);
70
+ for (const child of Array.isArray(value) ? value : Object.values(value)) freezeJsonValue(child, visited);
71
+ Object.freeze(value);
72
+ };
73
+ /** Validates and freezes persisted protocol input so interned references remain immutable. */
74
+ const freezePiHarnessJsonValue = (value, path = "$event") => {
75
+ assertPiHarnessJsonValue(value, path);
76
+ freezeJsonValue(value, /* @__PURE__ */ new Set());
77
+ return value;
78
+ };
79
+ //#endregion
80
+ export { assertPiHarnessJsonValue, freezePiHarnessJsonValue, snapshotPiHarnessJsonValue };
81
+
82
+ //# sourceMappingURL=json-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-value.js","names":[],"sources":["../../../../src/pi/harness/json-value.ts"],"sourcesContent":["export type PiHarnessJsonValue =\n | null\n | boolean\n | number\n | string\n | PiHarnessJsonValue[]\n | { [key: string]: PiHarnessJsonValue };\n\nconst jsonValueError = (path: string, reason: string): Error =>\n new Error(`PI_HARNESS_EVENT_VALUE_NOT_JSON_COMPATIBLE:${path}:${reason}`);\n\nconst snapshotJsonValueAtPath = (\n value: unknown,\n path: string,\n ancestors: Set<object>,\n): PiHarnessJsonValue => {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") {\n return value;\n }\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) {\n throw jsonValueError(path, \"non-finite-number\");\n }\n return value;\n }\n if (typeof value !== \"object\") {\n throw jsonValueError(path, typeof value);\n }\n if (ancestors.has(value)) {\n throw jsonValueError(path, \"cycle\");\n }\n\n ancestors.add(value);\n try {\n if (Array.isArray(value)) {\n const snapshot: PiHarnessJsonValue[] = [];\n for (let index = 0; index < value.length; index += 1) {\n if (!Object.hasOwn(value, index)) {\n throw jsonValueError(`${path}[${index}]`, \"sparse-array-entry\");\n }\n snapshot.push(snapshotJsonValueAtPath(value[index], `${path}[${index}]`, ancestors));\n }\n return snapshot;\n }\n\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) {\n throw jsonValueError(path, \"non-plain-object\");\n }\n\n const snapshot: Record<string, PiHarnessJsonValue> = {};\n for (const key of Object.keys(value)) {\n const propertyValue = (value as Record<string, unknown>)[key];\n // JSON omits undefined optional object properties. Pi uses these on otherwise valid messages.\n if (propertyValue === undefined) {\n continue;\n }\n snapshot[key] = snapshotJsonValueAtPath(propertyValue, `${path}.${key}`, ancestors);\n }\n return snapshot;\n } finally {\n ancestors.delete(value);\n }\n};\n\n/** Validates and detaches a value at the workflow JSON boundary. */\nexport const snapshotPiHarnessJsonValue = <T>(value: T, path = \"$event\"): T =>\n snapshotJsonValueAtPath(value, path, new Set()) as T;\n\nconst assertJsonValueAtPath = (value: unknown, path: string, ancestors: Set<object>): void => {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") {\n return;\n }\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) {\n throw jsonValueError(path, \"non-finite-number\");\n }\n return;\n }\n if (typeof value !== \"object\") {\n throw jsonValueError(path, typeof value);\n }\n if (ancestors.has(value)) {\n throw jsonValueError(path, \"cycle\");\n }\n\n ancestors.add(value);\n try {\n if (Array.isArray(value)) {\n for (let index = 0; index < value.length; index += 1) {\n if (!Object.hasOwn(value, index)) {\n throw jsonValueError(`${path}[${index}]`, \"sparse-array-entry\");\n }\n assertJsonValueAtPath(value[index], `${path}[${index}]`, ancestors);\n }\n return;\n }\n\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) {\n throw jsonValueError(path, \"non-plain-object\");\n }\n for (const key of Object.keys(value)) {\n const propertyValue = (value as Record<string, unknown>)[key];\n if (propertyValue !== undefined) {\n assertJsonValueAtPath(propertyValue, `${path}.${key}`, ancestors);\n }\n }\n } finally {\n ancestors.delete(value);\n }\n};\n\n/** Validates persisted protocol input without copying it. */\nexport const assertPiHarnessJsonValue = (value: unknown, path = \"$event\"): void => {\n assertJsonValueAtPath(value, path, new Set());\n};\n\nconst freezeJsonValue = (value: unknown, visited: Set<object>): void => {\n if (value === null || typeof value !== \"object\" || visited.has(value)) {\n return;\n }\n visited.add(value);\n for (const child of Array.isArray(value) ? value : Object.values(value)) {\n freezeJsonValue(child, visited);\n }\n Object.freeze(value);\n};\n\n/** Validates and freezes persisted protocol input so interned references remain immutable. */\nexport const freezePiHarnessJsonValue = <T>(value: T, path = \"$event\"): T => {\n assertPiHarnessJsonValue(value, path);\n freezeJsonValue(value, new Set());\n return value;\n};\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { PiOperationCompletedHookPayload } from "../types.js";
2
+ import { PiHarnessHooksMap } from "../definition.js";
3
+ import { SessionTreeEntry } from "@earendil-works/pi-agent-core";
4
+ import { WorkflowStepTx } from "@fragno-dev/workflows/workflow";
5
+ //#region src/pi/harness/pi-operation-completed.d.ts
6
+ type CreatePiOperationCompletedPayloadOptions = Pick<PiOperationCompletedHookPayload, "actor" | "workflowName" | "sessionId" | "metadata" | "stepName" | "operationId" | "operation"> & {
7
+ operationEntries: readonly SessionTreeEntry[];
8
+ };
9
+ declare const createPiOperationCompletedPayload: (options: CreatePiOperationCompletedPayloadOptions) => PiOperationCompletedHookPayload | undefined;
10
+ type SchedulePiOperationCompletedHookOptions = CreatePiOperationCompletedPayloadOptions & {
11
+ tx: Pick<WorkflowStepTx<PiHarnessHooksMap>, "mutate" | "onTerminalError">;
12
+ };
13
+ declare const schedulePiOperationCompletedHook: (options: SchedulePiOperationCompletedHookOptions) => void;
14
+ //#endregion
15
+ export { CreatePiOperationCompletedPayloadOptions, SchedulePiOperationCompletedHookOptions, createPiOperationCompletedPayload, schedulePiOperationCompletedHook };
16
+ //# sourceMappingURL=pi-operation-completed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-operation-completed.d.ts","names":[],"sources":["../../../../src/pi/harness/pi-operation-completed.ts"],"mappings":";;;;;KA6DY,2CAA2C,KACrD;EAGA,2BAA2B;;cAGhB,oCACX,SAAS,6CACR;KAmBS,0CAA0C;EACpD,IAAI,KAAK,eAAe;;cAGb,mCACX,SAAS"}
@@ -0,0 +1,71 @@
1
+ import { piSchema } from "../../schema.js";
2
+ //#region src/pi/harness/pi-operation-completed.ts
3
+ const assistantMessagesFromEntries = (entries) => entries.flatMap((entry) => entry.type === "message" && entry.message.role === "assistant" ? [entry.message] : []);
4
+ const modelCallsFromEntries = (entries) => assistantMessagesFromEntries(entries).map((message) => ({
5
+ api: message.api,
6
+ provider: message.provider,
7
+ model: message.model,
8
+ ...message.responseModel ? { responseModel: message.responseModel } : {},
9
+ ...message.responseId ? { responseId: message.responseId } : {},
10
+ usage: {
11
+ ...message.usage,
12
+ cost: { ...message.usage.cost }
13
+ },
14
+ stopReason: message.stopReason,
15
+ timestamp: message.timestamp
16
+ }));
17
+ const aggregateModelCallUsage = (modelCalls) => modelCalls.reduce((total, modelCall) => ({
18
+ input: total.input + modelCall.usage.input,
19
+ output: total.output + modelCall.usage.output,
20
+ cacheRead: total.cacheRead + modelCall.usage.cacheRead,
21
+ cacheWrite: total.cacheWrite + modelCall.usage.cacheWrite,
22
+ totalTokens: total.totalTokens + modelCall.usage.totalTokens,
23
+ cost: {
24
+ input: total.cost.input + modelCall.usage.cost.input,
25
+ output: total.cost.output + modelCall.usage.cost.output,
26
+ cacheRead: total.cost.cacheRead + modelCall.usage.cost.cacheRead,
27
+ cacheWrite: total.cost.cacheWrite + modelCall.usage.cost.cacheWrite,
28
+ total: total.cost.total + modelCall.usage.cost.total
29
+ }
30
+ }), {
31
+ input: 0,
32
+ output: 0,
33
+ cacheRead: 0,
34
+ cacheWrite: 0,
35
+ totalTokens: 0,
36
+ cost: {
37
+ input: 0,
38
+ output: 0,
39
+ cacheRead: 0,
40
+ cacheWrite: 0,
41
+ total: 0
42
+ }
43
+ });
44
+ const createPiOperationCompletedPayload = (options) => {
45
+ const modelCalls = modelCallsFromEntries(options.operationEntries);
46
+ if (modelCalls.length === 0) return;
47
+ return {
48
+ actor: options.actor,
49
+ workflowName: options.workflowName,
50
+ sessionId: options.sessionId,
51
+ metadata: options.metadata,
52
+ stepName: options.stepName,
53
+ operationId: options.operationId,
54
+ operation: options.operation,
55
+ modelCalls,
56
+ usage: aggregateModelCallUsage(modelCalls)
57
+ };
58
+ };
59
+ const schedulePiOperationCompletedHook = (options) => {
60
+ const payload = createPiOperationCompletedPayload(options);
61
+ if (!payload) return;
62
+ const triggerHook = ({ forSchema }) => {
63
+ forSchema(piSchema).triggerHook("onOperationCompleted", payload);
64
+ };
65
+ options.tx.mutate(triggerHook);
66
+ options.tx.onTerminalError.mutate(triggerHook);
67
+ };
68
+ //#endregion
69
+ export { createPiOperationCompletedPayload, schedulePiOperationCompletedHook };
70
+
71
+ //# sourceMappingURL=pi-operation-completed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-operation-completed.js","names":[],"sources":["../../../../src/pi/harness/pi-operation-completed.ts"],"sourcesContent":["import type { WorkflowStepTx } from \"@fragno-dev/workflows/workflow\";\n\nimport type { HandlerTxContext } from \"@fragno-dev/db\";\n\nimport type { SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { AssistantMessage } from \"@earendil-works/pi-ai\";\n\nimport { piSchema } from \"../../schema\";\nimport type { PiHarnessHooksMap } from \"../definition\";\nimport type { PiOperationCompletedHookPayload } from \"../types\";\n\nconst assistantMessagesFromEntries = (entries: readonly SessionTreeEntry[]): AssistantMessage[] =>\n entries.flatMap((entry) =>\n entry.type === \"message\" && entry.message.role === \"assistant\" ? [entry.message] : [],\n );\n\nconst modelCallsFromEntries = (\n entries: readonly SessionTreeEntry[],\n): PiOperationCompletedHookPayload[\"modelCalls\"] =>\n assistantMessagesFromEntries(entries).map((message) => ({\n api: message.api,\n provider: message.provider,\n model: message.model,\n ...(message.responseModel ? { responseModel: message.responseModel } : {}),\n ...(message.responseId ? { responseId: message.responseId } : {}),\n usage: {\n ...message.usage,\n cost: { ...message.usage.cost },\n },\n stopReason: message.stopReason,\n timestamp: message.timestamp,\n }));\n\nconst aggregateModelCallUsage = (\n modelCalls: PiOperationCompletedHookPayload[\"modelCalls\"],\n): AssistantMessage[\"usage\"] =>\n modelCalls.reduce<AssistantMessage[\"usage\"]>(\n (total, modelCall) => ({\n input: total.input + modelCall.usage.input,\n output: total.output + modelCall.usage.output,\n cacheRead: total.cacheRead + modelCall.usage.cacheRead,\n cacheWrite: total.cacheWrite + modelCall.usage.cacheWrite,\n totalTokens: total.totalTokens + modelCall.usage.totalTokens,\n cost: {\n input: total.cost.input + modelCall.usage.cost.input,\n output: total.cost.output + modelCall.usage.cost.output,\n cacheRead: total.cost.cacheRead + modelCall.usage.cost.cacheRead,\n cacheWrite: total.cost.cacheWrite + modelCall.usage.cost.cacheWrite,\n total: total.cost.total + modelCall.usage.cost.total,\n },\n }),\n {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n );\n\nexport type CreatePiOperationCompletedPayloadOptions = Pick<\n PiOperationCompletedHookPayload,\n \"actor\" | \"workflowName\" | \"sessionId\" | \"metadata\" | \"stepName\" | \"operationId\" | \"operation\"\n> & {\n operationEntries: readonly SessionTreeEntry[];\n};\n\nexport const createPiOperationCompletedPayload = (\n options: CreatePiOperationCompletedPayloadOptions,\n): PiOperationCompletedHookPayload | undefined => {\n const modelCalls = modelCallsFromEntries(options.operationEntries);\n if (modelCalls.length === 0) {\n return undefined;\n }\n\n return {\n actor: options.actor,\n workflowName: options.workflowName,\n sessionId: options.sessionId,\n metadata: options.metadata,\n stepName: options.stepName,\n operationId: options.operationId,\n operation: options.operation,\n modelCalls,\n usage: aggregateModelCallUsage(modelCalls),\n };\n};\n\nexport type SchedulePiOperationCompletedHookOptions = CreatePiOperationCompletedPayloadOptions & {\n tx: Pick<WorkflowStepTx<PiHarnessHooksMap>, \"mutate\" | \"onTerminalError\">;\n};\n\nexport const schedulePiOperationCompletedHook = (\n options: SchedulePiOperationCompletedHookOptions,\n): void => {\n const payload = createPiOperationCompletedPayload(options);\n if (!payload) {\n return;\n }\n\n const triggerHook = ({ forSchema }: HandlerTxContext<PiHarnessHooksMap>) => {\n forSchema(piSchema).triggerHook(\"onOperationCompleted\", payload);\n };\n\n // Workflow callbacks may run more than once before their step commits. Register both outcomes on\n // every call so the hook follows whichever transaction path eventually becomes durable.\n options.tx.mutate(triggerHook);\n options.tx.onTerminalError.mutate(triggerHook);\n};\n"],"mappings":";;AAWA,MAAM,gCAAgC,YACpC,QAAQ,SAAS,UACf,MAAM,SAAS,aAAa,MAAM,QAAQ,SAAS,cAAc,CAAC,MAAM,OAAO,IAAI,CAAC,CACtF;AAEF,MAAM,yBACJ,YAEA,6BAA6B,OAAO,CAAC,CAAC,KAAK,aAAa;CACtD,KAAK,QAAQ;CACb,UAAU,QAAQ;CAClB,OAAO,QAAQ;CACf,GAAI,QAAQ,gBAAgB,EAAE,eAAe,QAAQ,cAAc,IAAI,CAAC;CACxE,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;CAC/D,OAAO;EACL,GAAG,QAAQ;EACX,MAAM,EAAE,GAAG,QAAQ,MAAM,KAAK;CAChC;CACA,YAAY,QAAQ;CACpB,WAAW,QAAQ;AACrB,EAAE;AAEJ,MAAM,2BACJ,eAEA,WAAW,QACR,OAAO,eAAe;CACrB,OAAO,MAAM,QAAQ,UAAU,MAAM;CACrC,QAAQ,MAAM,SAAS,UAAU,MAAM;CACvC,WAAW,MAAM,YAAY,UAAU,MAAM;CAC7C,YAAY,MAAM,aAAa,UAAU,MAAM;CAC/C,aAAa,MAAM,cAAc,UAAU,MAAM;CACjD,MAAM;EACJ,OAAO,MAAM,KAAK,QAAQ,UAAU,MAAM,KAAK;EAC/C,QAAQ,MAAM,KAAK,SAAS,UAAU,MAAM,KAAK;EACjD,WAAW,MAAM,KAAK,YAAY,UAAU,MAAM,KAAK;EACvD,YAAY,MAAM,KAAK,aAAa,UAAU,MAAM,KAAK;EACzD,OAAO,MAAM,KAAK,QAAQ,UAAU,MAAM,KAAK;CACjD;AACF,IACA;CACE,OAAO;CACP,QAAQ;CACR,WAAW;CACX,YAAY;CACZ,aAAa;CACb,MAAM;EAAE,OAAO;EAAG,QAAQ;EAAG,WAAW;EAAG,YAAY;EAAG,OAAO;CAAE;AACrE,CACF;AASF,MAAa,qCACX,YACgD;CAChD,MAAM,aAAa,sBAAsB,QAAQ,gBAAgB;CACjE,IAAI,WAAW,WAAW,GACxB;CAGF,OAAO;EACL,OAAO,QAAQ;EACf,cAAc,QAAQ;EACtB,WAAW,QAAQ;EACnB,UAAU,QAAQ;EAClB,UAAU,QAAQ;EAClB,aAAa,QAAQ;EACrB,WAAW,QAAQ;EACnB;EACA,OAAO,wBAAwB,UAAU;CAC3C;AACF;AAMA,MAAa,oCACX,YACS;CACT,MAAM,UAAU,kCAAkC,OAAO;CACzD,IAAI,CAAC,SACH;CAGF,MAAM,eAAe,EAAE,gBAAqD;EAC1E,UAAU,QAAQ,CAAC,CAAC,YAAY,wBAAwB,OAAO;CACjE;CAIA,QAAQ,GAAG,OAAO,WAAW;CAC7B,QAAQ,GAAG,gBAAgB,OAAO,WAAW;AAC/C"}
@@ -0,0 +1,62 @@
1
+ import { SessionEntryCursorOptions, SessionMetadata, SessionStorage, SessionTreeEntry } from "@earendil-works/pi-agent-core";
2
+ //#region src/pi/harness/session-storage.d.ts
3
+ type PersistSessionEntry = (entry: SessionTreeEntry) => void | Promise<void>;
4
+ type AllocateSessionEntryId = () => string;
5
+ type WorkflowBackedSessionEntryIdAllocator = {
6
+ next: AllocateSessionEntryId;
7
+ };
8
+ declare const nextWorkflowBackedSessionEntryIndex: (options: {
9
+ prefix: string;
10
+ entries: readonly SessionTreeEntry[];
11
+ }) => number;
12
+ declare const createWorkflowBackedSessionEntryIdAllocator: (options: {
13
+ prefix: string;
14
+ startIndex: number;
15
+ }) => WorkflowBackedSessionEntryIdAllocator;
16
+ type WorkflowBackedSessionStorageOptions<TMetadata extends SessionMetadata = SessionMetadata> = {
17
+ metadata: TMetadata;
18
+ entryIds: WorkflowBackedSessionEntryIdAllocator;
19
+ /** Entries are retained by reference and must remain immutable after storage construction. */
20
+ entries?: readonly SessionTreeEntry[];
21
+ /**
22
+ * Optional append hook used by workflow/database adapters to persist or emit
23
+ * the same immutable entry accepted by the in-memory projection.
24
+ */
25
+ onAppendEntry?: PersistSessionEntry;
26
+ };
27
+ declare const sessionEntriesLeafId: (entries: readonly SessionTreeEntry[]) => string | null;
28
+ /** In-memory Pi session storage that retains immutable session entries by reference. */
29
+ declare class WorkflowBackedSessionStorage<TMetadata extends SessionMetadata = SessionMetadata> implements SessionStorage<TMetadata> {
30
+ private readonly metadata;
31
+ private readonly onAppendEntry;
32
+ private readonly entryIds;
33
+ private readonly entries;
34
+ private readonly byId;
35
+ private readonly labelsById;
36
+ private leafId;
37
+ constructor(options: WorkflowBackedSessionStorageOptions<TMetadata>);
38
+ getMetadata(): Promise<TMetadata>;
39
+ getLeafId(): Promise<string | null>;
40
+ setLeafId(leafId: string | null): Promise<void>;
41
+ createEntryId(): Promise<string>;
42
+ appendEntry(entry: SessionTreeEntry): Promise<void>;
43
+ getEntry(id: string): Promise<SessionTreeEntry | undefined>;
44
+ findEntries<TType extends SessionTreeEntry["type"]>(type: TType): Promise<Array<Extract<SessionTreeEntry, {
45
+ type: TType;
46
+ }>>>;
47
+ getLabel(id: string): Promise<string | undefined>;
48
+ getSessionName(): Promise<string | undefined>;
49
+ getSessionStats(): Promise<{
50
+ messageCount: number;
51
+ cachedTokens: number;
52
+ uncachedTokens: number;
53
+ totalTokens: number;
54
+ costTotal: number;
55
+ }>;
56
+ getPathToRootOrCompaction(leafId: string | null): Promise<SessionTreeEntry[]>;
57
+ getPathToRoot(leafId: string | null): Promise<SessionTreeEntry[]>;
58
+ getEntries(options?: SessionEntryCursorOptions): Promise<SessionTreeEntry[]>;
59
+ }
60
+ //#endregion
61
+ export { WorkflowBackedSessionEntryIdAllocator, WorkflowBackedSessionStorage, WorkflowBackedSessionStorageOptions, createWorkflowBackedSessionEntryIdAllocator, nextWorkflowBackedSessionEntryIndex, sessionEntriesLeafId };
62
+ //# sourceMappingURL=session-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-storage.d.ts","names":[],"sources":["../../../../src/pi/harness/session-storage.ts"],"mappings":";;KASK,uBAAuB,OAAO,4BAA4B;KAC1D;KAEO;EACV,MAAM;;cAGK,sCAAuC;EAClD;EACA,kBAAkB;;cAmBP,8CAA+C;EAC1D;EACA;MACE;KAWQ,oCACV,kBAAkB,kBAAkB;EAEpC,UAAU;EACV,UAAU;;EAEV,mBAAmB;;;;;EAKnB,gBAAgB;;cAML,uBAAwB,kBAAkB;;cAuC1C,6BACX,kBAAkB,kBAAkB,4BACzB,eAAe;mBACT;mBACA;mBACA;mBACA;mBACA;mBACA;UACT;cAEI,SAAS,oCAAoC;EAoBnD,eAAe,QAAQ;EAIvB,aAAa;EAQb,UAAU,wBAAwB;EAgBlC,iBAAiB;EAIjB,YAAY,OAAO,mBAAmB;EAiBtC,SAAS,aAAa,QAAQ;EAI9B,YAAY,cAAc,0BAC9B,MAAM,QACL,QAAQ,MAAM,QAAQ;IAAoB,MAAM;;EAM7C,SAAS,aAAa;EAItB,kBAAkB;EAUlB,mBAAe;;;;;;;EAiCf,0BAA0B,wBAAwB,QAAQ;EAsC1D,cAAc,wBAAwB,QAAQ;EA4B9C,WAAW,UAAU,4BAA4B,QAAQ"}
@@ -0,0 +1,174 @@
1
+ import { SessionError } from "@earendil-works/pi-agent-core";
2
+ //#region src/pi/harness/session-storage.ts
3
+ const nextWorkflowBackedSessionEntryIndex = (options) => {
4
+ const idPrefix = `${options.prefix}-`;
5
+ let highestIndex = -1;
6
+ for (const entry of options.entries) {
7
+ if (!entry.id.startsWith(idPrefix)) continue;
8
+ const index = Number(entry.id.slice(idPrefix.length));
9
+ if (Number.isSafeInteger(index) && index >= 0) highestIndex = Math.max(highestIndex, index);
10
+ }
11
+ return highestIndex + 1;
12
+ };
13
+ const createWorkflowBackedSessionEntryIdAllocator = (options) => {
14
+ let nextIndex = options.startIndex;
15
+ return { next: () => {
16
+ const id = `${options.prefix}-${nextIndex}`;
17
+ nextIndex += 1;
18
+ return id;
19
+ } };
20
+ };
21
+ const leafIdAfterEntry = (entry) => entry.type === "leaf" ? entry.targetId : entry.id;
22
+ const sessionEntriesLeafId = (entries) => {
23
+ let leafId = null;
24
+ for (const entry of entries) leafId = leafIdAfterEntry(entry);
25
+ return leafId;
26
+ };
27
+ const updateLabelCache = (labelsById, entry) => {
28
+ if (entry.type !== "label") return;
29
+ const label = entry.label?.trim();
30
+ if (label) labelsById.set(entry.targetId, label);
31
+ else labelsById.delete(entry.targetId);
32
+ };
33
+ const createUniqueEntryId = (byId, allocator) => {
34
+ for (let i = 0; i < 100; i += 1) {
35
+ const id = allocator.next();
36
+ if (!byId.has(id)) return id;
37
+ }
38
+ throw new SessionError("invalid_session", "Session entry id allocator repeatedly returned used ids");
39
+ };
40
+ /** In-memory Pi session storage that retains immutable session entries by reference. */
41
+ var WorkflowBackedSessionStorage = class {
42
+ metadata;
43
+ onAppendEntry;
44
+ entryIds;
45
+ entries;
46
+ byId;
47
+ labelsById;
48
+ leafId;
49
+ constructor(options) {
50
+ this.metadata = options.metadata;
51
+ this.onAppendEntry = options.onAppendEntry;
52
+ this.entryIds = options.entryIds;
53
+ this.entries = [...options.entries ?? []];
54
+ this.byId = /* @__PURE__ */ new Map();
55
+ this.labelsById = /* @__PURE__ */ new Map();
56
+ this.leafId = null;
57
+ for (const entry of this.entries) {
58
+ this.byId.set(entry.id, entry);
59
+ updateLabelCache(this.labelsById, entry);
60
+ this.leafId = leafIdAfterEntry(entry);
61
+ }
62
+ if (this.leafId !== null && !this.byId.has(this.leafId)) throw new SessionError("invalid_session", `Entry ${this.leafId} not found`);
63
+ }
64
+ async getMetadata() {
65
+ return this.metadata;
66
+ }
67
+ async getLeafId() {
68
+ if (this.leafId !== null && !this.byId.has(this.leafId)) throw new SessionError("invalid_session", `Entry ${this.leafId} not found`);
69
+ return this.leafId;
70
+ }
71
+ async setLeafId(leafId) {
72
+ if (leafId !== null && !this.byId.has(leafId)) throw new SessionError("not_found", `Entry ${leafId} not found`);
73
+ const entry = {
74
+ type: "leaf",
75
+ id: await this.createEntryId(),
76
+ parentId: this.leafId,
77
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
78
+ targetId: leafId
79
+ };
80
+ await this.appendEntry(entry);
81
+ }
82
+ async createEntryId() {
83
+ return createUniqueEntryId(this.byId, this.entryIds);
84
+ }
85
+ async appendEntry(entry) {
86
+ if (this.byId.has(entry.id)) throw new SessionError("invalid_entry", `Entry ${entry.id} already exists`);
87
+ if (entry.parentId !== null && !this.byId.has(entry.parentId)) throw new SessionError("invalid_entry", `Parent entry ${entry.parentId} not found`);
88
+ this.entries.push(entry);
89
+ this.byId.set(entry.id, entry);
90
+ updateLabelCache(this.labelsById, entry);
91
+ this.leafId = leafIdAfterEntry(entry);
92
+ await this.onAppendEntry?.(entry);
93
+ }
94
+ async getEntry(id) {
95
+ return this.byId.get(id);
96
+ }
97
+ async findEntries(type) {
98
+ return this.entries.filter((entry) => entry.type === type);
99
+ }
100
+ async getLabel(id) {
101
+ return this.labelsById.get(id);
102
+ }
103
+ async getSessionName() {
104
+ for (let index = this.entries.length - 1; index >= 0; index -= 1) {
105
+ const entry = this.entries[index];
106
+ if (entry.type === "session_info") return entry.name?.trim() || void 0;
107
+ }
108
+ }
109
+ async getSessionStats() {
110
+ let messageCount = 0;
111
+ let cachedTokens = 0;
112
+ let uncachedTokens = 0;
113
+ let totalTokens = 0;
114
+ let costTotal = 0;
115
+ for (const entry of this.entries) {
116
+ if (entry.type === "message") messageCount += 1;
117
+ const usage = entry.type === "message" ? entry.message.role === "assistant" ? entry.message.usage : void 0 : entry.type === "compaction" || entry.type === "branch_summary" ? entry.usage : void 0;
118
+ if (!usage) continue;
119
+ cachedTokens += usage.cacheRead;
120
+ uncachedTokens += usage.input + usage.cacheWrite;
121
+ totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
122
+ costTotal += usage.cost.total;
123
+ }
124
+ return {
125
+ messageCount,
126
+ cachedTokens,
127
+ uncachedTokens,
128
+ totalTokens,
129
+ costTotal
130
+ };
131
+ }
132
+ async getPathToRootOrCompaction(leafId) {
133
+ if (leafId === null) return [];
134
+ const path = [];
135
+ let stopAtEntryId = null;
136
+ let current = this.byId.get(leafId);
137
+ if (!current) throw new SessionError("not_found", `Entry ${leafId} not found`);
138
+ while (current) {
139
+ path.push(current);
140
+ if (stopAtEntryId !== null && current.id === stopAtEntryId) break;
141
+ if (current.type === "compaction") stopAtEntryId = current.firstKeptEntryId ?? null;
142
+ if (!current.parentId) break;
143
+ const parent = this.byId.get(current.parentId);
144
+ if (!parent) throw new SessionError("invalid_session", `Entry ${current.parentId} not found`);
145
+ current = parent;
146
+ }
147
+ path.reverse();
148
+ return path;
149
+ }
150
+ async getPathToRoot(leafId) {
151
+ if (leafId === null) return [];
152
+ const path = [];
153
+ let current = this.byId.get(leafId);
154
+ if (!current) throw new SessionError("not_found", `Entry ${leafId} not found`);
155
+ while (current) {
156
+ path.push(current);
157
+ if (!current.parentId) break;
158
+ const parent = this.byId.get(current.parentId);
159
+ if (!parent) throw new SessionError("invalid_session", `Entry ${current.parentId} not found`);
160
+ current = parent;
161
+ }
162
+ path.reverse();
163
+ return path;
164
+ }
165
+ async getEntries(options) {
166
+ const start = options?.afterEntrySeq ?? 0;
167
+ const end = options?.limit === void 0 ? void 0 : start + options.limit;
168
+ return this.entries.slice(start, end);
169
+ }
170
+ };
171
+ //#endregion
172
+ export { WorkflowBackedSessionStorage, createWorkflowBackedSessionEntryIdAllocator, nextWorkflowBackedSessionEntryIndex, sessionEntriesLeafId };
173
+
174
+ //# sourceMappingURL=session-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-storage.js","names":[],"sources":["../../../../src/pi/harness/session-storage.ts"],"sourcesContent":["import {\n SessionError,\n type LeafEntry,\n type SessionEntryCursorOptions,\n type SessionMetadata,\n type SessionStorage,\n type SessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\n\ntype PersistSessionEntry = (entry: SessionTreeEntry) => void | Promise<void>;\ntype AllocateSessionEntryId = () => string;\n\nexport type WorkflowBackedSessionEntryIdAllocator = {\n next: AllocateSessionEntryId;\n};\n\nexport const nextWorkflowBackedSessionEntryIndex = (options: {\n prefix: string;\n entries: readonly SessionTreeEntry[];\n}): number => {\n const idPrefix = `${options.prefix}-`;\n let highestIndex = -1;\n\n for (const entry of options.entries) {\n if (!entry.id.startsWith(idPrefix)) {\n continue;\n }\n\n const index = Number(entry.id.slice(idPrefix.length));\n if (Number.isSafeInteger(index) && index >= 0) {\n highestIndex = Math.max(highestIndex, index);\n }\n }\n\n return highestIndex + 1;\n};\n\nexport const createWorkflowBackedSessionEntryIdAllocator = (options: {\n prefix: string;\n startIndex: number;\n}): WorkflowBackedSessionEntryIdAllocator => {\n let nextIndex = options.startIndex;\n return {\n next: () => {\n const id = `${options.prefix}-${nextIndex}`;\n nextIndex += 1;\n return id;\n },\n };\n};\n\nexport type WorkflowBackedSessionStorageOptions<\n TMetadata extends SessionMetadata = SessionMetadata,\n> = {\n metadata: TMetadata;\n entryIds: WorkflowBackedSessionEntryIdAllocator;\n /** Entries are retained by reference and must remain immutable after storage construction. */\n entries?: readonly SessionTreeEntry[];\n /**\n * Optional append hook used by workflow/database adapters to persist or emit\n * the same immutable entry accepted by the in-memory projection.\n */\n onAppendEntry?: PersistSessionEntry;\n};\n\nconst leafIdAfterEntry = (entry: SessionTreeEntry): string | null =>\n entry.type === \"leaf\" ? entry.targetId : entry.id;\n\nexport const sessionEntriesLeafId = (entries: readonly SessionTreeEntry[]): string | null => {\n let leafId: string | null = null;\n for (const entry of entries) {\n leafId = leafIdAfterEntry(entry);\n }\n return leafId;\n};\n\nconst updateLabelCache = (labelsById: Map<string, string>, entry: SessionTreeEntry): void => {\n if (entry.type !== \"label\") {\n return;\n }\n\n const label = entry.label?.trim();\n if (label) {\n labelsById.set(entry.targetId, label);\n } else {\n labelsById.delete(entry.targetId);\n }\n};\n\nconst createUniqueEntryId = (\n byId: ReadonlyMap<string, SessionTreeEntry>,\n allocator: WorkflowBackedSessionEntryIdAllocator,\n): string => {\n for (let i = 0; i < 100; i += 1) {\n const id = allocator.next();\n if (!byId.has(id)) {\n return id;\n }\n }\n\n throw new SessionError(\n \"invalid_session\",\n \"Session entry id allocator repeatedly returned used ids\",\n );\n};\n\n/** In-memory Pi session storage that retains immutable session entries by reference. */\nexport class WorkflowBackedSessionStorage<\n TMetadata extends SessionMetadata = SessionMetadata,\n> implements SessionStorage<TMetadata> {\n private readonly metadata: TMetadata;\n private readonly onAppendEntry: PersistSessionEntry | undefined;\n private readonly entryIds: WorkflowBackedSessionEntryIdAllocator;\n private readonly entries: SessionTreeEntry[];\n private readonly byId: Map<string, SessionTreeEntry>;\n private readonly labelsById: Map<string, string>;\n private leafId: string | null;\n\n constructor(options: WorkflowBackedSessionStorageOptions<TMetadata>) {\n this.metadata = options.metadata;\n this.onAppendEntry = options.onAppendEntry;\n this.entryIds = options.entryIds;\n this.entries = [...(options.entries ?? [])];\n this.byId = new Map();\n this.labelsById = new Map();\n this.leafId = null;\n\n for (const entry of this.entries) {\n this.byId.set(entry.id, entry);\n updateLabelCache(this.labelsById, entry);\n this.leafId = leafIdAfterEntry(entry);\n }\n\n if (this.leafId !== null && !this.byId.has(this.leafId)) {\n throw new SessionError(\"invalid_session\", `Entry ${this.leafId} not found`);\n }\n }\n\n async getMetadata(): Promise<TMetadata> {\n return this.metadata;\n }\n\n async getLeafId(): Promise<string | null> {\n if (this.leafId !== null && !this.byId.has(this.leafId)) {\n throw new SessionError(\"invalid_session\", `Entry ${this.leafId} not found`);\n }\n\n return this.leafId;\n }\n\n async setLeafId(leafId: string | null): Promise<void> {\n if (leafId !== null && !this.byId.has(leafId)) {\n throw new SessionError(\"not_found\", `Entry ${leafId} not found`);\n }\n\n const entry: LeafEntry = {\n type: \"leaf\",\n id: await this.createEntryId(),\n parentId: this.leafId,\n timestamp: new Date().toISOString(),\n targetId: leafId,\n };\n\n await this.appendEntry(entry);\n }\n\n async createEntryId(): Promise<string> {\n return createUniqueEntryId(this.byId, this.entryIds);\n }\n\n async appendEntry(entry: SessionTreeEntry): Promise<void> {\n if (this.byId.has(entry.id)) {\n throw new SessionError(\"invalid_entry\", `Entry ${entry.id} already exists`);\n }\n\n if (entry.parentId !== null && !this.byId.has(entry.parentId)) {\n throw new SessionError(\"invalid_entry\", `Parent entry ${entry.parentId} not found`);\n }\n\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n updateLabelCache(this.labelsById, entry);\n this.leafId = leafIdAfterEntry(entry);\n\n await this.onAppendEntry?.(entry);\n }\n\n async getEntry(id: string): Promise<SessionTreeEntry | undefined> {\n return this.byId.get(id);\n }\n\n async findEntries<TType extends SessionTreeEntry[\"type\"]>(\n type: TType,\n ): Promise<Array<Extract<SessionTreeEntry, { type: TType }>>> {\n return this.entries.filter(\n (entry): entry is Extract<SessionTreeEntry, { type: TType }> => entry.type === type,\n );\n }\n\n async getLabel(id: string): Promise<string | undefined> {\n return this.labelsById.get(id);\n }\n\n async getSessionName(): Promise<string | undefined> {\n for (let index = this.entries.length - 1; index >= 0; index -= 1) {\n const entry = this.entries[index];\n if (entry.type === \"session_info\") {\n return entry.name?.trim() || undefined;\n }\n }\n return undefined;\n }\n\n async getSessionStats() {\n let messageCount = 0;\n let cachedTokens = 0;\n let uncachedTokens = 0;\n let totalTokens = 0;\n let costTotal = 0;\n\n for (const entry of this.entries) {\n if (entry.type === \"message\") {\n messageCount += 1;\n }\n\n const usage =\n entry.type === \"message\"\n ? entry.message.role === \"assistant\"\n ? entry.message.usage\n : undefined\n : entry.type === \"compaction\" || entry.type === \"branch_summary\"\n ? entry.usage\n : undefined;\n if (!usage) {\n continue;\n }\n\n cachedTokens += usage.cacheRead;\n uncachedTokens += usage.input + usage.cacheWrite;\n totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n costTotal += usage.cost.total;\n }\n\n return { messageCount, cachedTokens, uncachedTokens, totalTokens, costTotal };\n }\n\n async getPathToRootOrCompaction(leafId: string | null): Promise<SessionTreeEntry[]> {\n if (leafId === null) {\n return [];\n }\n\n const path: SessionTreeEntry[] = [];\n let stopAtEntryId: string | null = null;\n let current = this.byId.get(leafId);\n if (!current) {\n throw new SessionError(\"not_found\", `Entry ${leafId} not found`);\n }\n\n while (current) {\n path.push(current);\n if (stopAtEntryId !== null && current.id === stopAtEntryId) {\n break;\n }\n if (current.type === \"compaction\") {\n // Retained tails are context snapshots, not replacements for their original tree entries.\n // Repeated compaction must keep walking to the first retained entry so Pi can summarize\n // that history instead of counting it while treating it as inaccessible.\n stopAtEntryId = current.firstKeptEntryId ?? null;\n }\n if (!current.parentId) {\n break;\n }\n\n const parent = this.byId.get(current.parentId);\n if (!parent) {\n throw new SessionError(\"invalid_session\", `Entry ${current.parentId} not found`);\n }\n current = parent;\n }\n\n path.reverse();\n return path;\n }\n\n async getPathToRoot(leafId: string | null): Promise<SessionTreeEntry[]> {\n if (leafId === null) {\n return [];\n }\n\n const path: SessionTreeEntry[] = [];\n let current = this.byId.get(leafId);\n if (!current) {\n throw new SessionError(\"not_found\", `Entry ${leafId} not found`);\n }\n\n while (current) {\n path.push(current);\n if (!current.parentId) {\n break;\n }\n\n const parent = this.byId.get(current.parentId);\n if (!parent) {\n throw new SessionError(\"invalid_session\", `Entry ${current.parentId} not found`);\n }\n current = parent;\n }\n\n path.reverse();\n return path;\n }\n\n async getEntries(options?: SessionEntryCursorOptions): Promise<SessionTreeEntry[]> {\n const start = options?.afterEntrySeq ?? 0;\n const end = options?.limit === undefined ? undefined : start + options.limit;\n return this.entries.slice(start, end);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { SessionStorage } from "@earendil-works/pi-agent-core";
2
+ //#region src/pi/pi-jsonl-export.d.ts
3
+ declare const PI_JSONL_EXPORT_CWD = "/workspace";
4
+ type ExportSessionStorageToJsonlOptions = {
5
+ cwd: string;
6
+ parentSessionPath?: string;
7
+ };
8
+ declare function exportSessionStorageToJsonl(storage: SessionStorage, options: ExportSessionStorageToJsonlOptions): Promise<string>;
9
+ //#endregion
10
+ export { ExportSessionStorageToJsonlOptions, PI_JSONL_EXPORT_CWD, exportSessionStorageToJsonl };
11
+ //# sourceMappingURL=pi-jsonl-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-jsonl-export.d.ts","names":[],"sources":["../../../src/pi/pi-jsonl-export.ts"],"mappings":";;cAEa;KAED;EACV;EACA;;iBAMoB,4BACpB,SAAS,gBACT,SAAS,qCACR"}