@earendil-works/pi-agent-core 0.84.1 → 0.84.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 (67) hide show
  1. package/dist/harness/events.d.ts +43 -0
  2. package/dist/harness/events.d.ts.map +1 -0
  3. package/dist/harness/events.js +65 -0
  4. package/dist/harness/events.js.map +1 -0
  5. package/dist/harness/session/jsonl/codec.d.ts +4 -2
  6. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  7. package/dist/harness/session/jsonl/codec.js +127 -88
  8. package/dist/harness/session/jsonl/codec.js.map +1 -1
  9. package/dist/harness/session/jsonl/errors.d.ts +5 -1
  10. package/dist/harness/session/jsonl/errors.d.ts.map +1 -1
  11. package/dist/harness/session/jsonl/errors.js +10 -2
  12. package/dist/harness/session/jsonl/errors.js.map +1 -1
  13. package/dist/harness/session/jsonl/repo.d.ts +7 -3
  14. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  15. package/dist/harness/session/jsonl/repo.js +88 -45
  16. package/dist/harness/session/jsonl/repo.js.map +1 -1
  17. package/dist/harness/session/jsonl/storage.d.ts +1 -1
  18. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  19. package/dist/harness/session/jsonl/storage.js +28 -19
  20. package/dist/harness/session/jsonl/storage.js.map +1 -1
  21. package/dist/harness/session/jsonl/types.d.ts +1 -1
  22. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  23. package/dist/harness/session/jsonl/types.js.map +1 -1
  24. package/dist/harness/session/memory.d.ts +1 -1
  25. package/dist/harness/session/memory.d.ts.map +1 -1
  26. package/dist/harness/session/memory.js.map +1 -1
  27. package/dist/harness/session/session.d.ts +1 -1
  28. package/dist/harness/session/session.d.ts.map +1 -1
  29. package/dist/harness/session/session.js +7 -2
  30. package/dist/harness/session/session.js.map +1 -1
  31. package/dist/harness/session/state.d.ts +1 -1
  32. package/dist/harness/session/state.d.ts.map +1 -1
  33. package/dist/harness/session/state.js.map +1 -1
  34. package/dist/harness/session/testing/conformance.d.ts.map +1 -1
  35. package/dist/harness/session/testing/conformance.js +19 -0
  36. package/dist/harness/session/testing/conformance.js.map +1 -1
  37. package/dist/harness/session/types.d.ts +18 -5
  38. package/dist/harness/session/types.d.ts.map +1 -1
  39. package/dist/harness/session/types.js.map +1 -1
  40. package/dist/harness/skills.d.ts +3 -2
  41. package/dist/harness/skills.d.ts.map +1 -1
  42. package/dist/harness/skills.js +13 -3
  43. package/dist/harness/skills.js.map +1 -1
  44. package/dist/harness/tools/edit.d.ts.map +1 -1
  45. package/dist/harness/tools/edit.js +14 -1
  46. package/dist/harness/tools/edit.js.map +1 -1
  47. package/dist/index.d.ts +1 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +1 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/proxy.d.ts +2 -1
  52. package/dist/proxy.d.ts.map +1 -1
  53. package/dist/proxy.js +1 -0
  54. package/dist/proxy.js.map +1 -1
  55. package/dist/search/index.d.ts +21 -0
  56. package/dist/search/index.d.ts.map +1 -0
  57. package/dist/search/index.js +2 -0
  58. package/dist/search/index.js.map +1 -0
  59. package/dist/search/scanning.d.ts +29 -0
  60. package/dist/search/scanning.d.ts.map +1 -0
  61. package/dist/search/scanning.js +103 -0
  62. package/dist/search/scanning.js.map +1 -0
  63. package/package.json +4 -4
  64. package/dist/harness/session/search.d.ts +0 -19
  65. package/dist/harness/session/search.d.ts.map +0 -1
  66. package/dist/harness/session/search.js +0 -42
  67. package/dist/harness/session/search.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,WAAW,EACX,KAAK,KAAK,EAEV,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAEf,MAAM,uBAAuB,CAAC;AAG/B,cAAM,uBAAwB,SAAQ,WAAW,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IACzF,cASC;CACD;AAED;;GAEG;AACH,MAAM,MAAM,0BAA0B,GACnC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC9C;IACA,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC;IAC3D,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAChC,GACD;IACA,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAChC,CAAC;AAEL,KAAK,8BAA8B,GAAG,IAAI,CACzC,mBAAmB,EACjB,aAAa,GACb,gBAAgB,GAChB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,WAAW,GACX,SAAS,GACT,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,iBAAiB,CACnB,CAAC;AAEF,MAAM,WAAW,kBAAmB,SAAQ,8BAA8B;IACzE,+CAA+C;IAC/C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;CACjB;AAqCD,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,GAAG,uBAAuB,CAqHrH","sourcesContent":["/**\n * Proxy stream function for apps that route LLM calls through a server.\n * The server manages auth and proxies requests to LLM providers.\n */\n\n// Internal import for JSON parsing utility\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n\ttype StopReason,\n\ttype ToolCall,\n} from \"@earendil-works/pi-ai\";\n\n// Create stream class matching ProxyMessageEventStream\nclass ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Proxy event types - server sends these with partial field stripped to reduce bandwidth.\n */\nexport type ProxyAssistantMessageEvent =\n\t| { type: \"start\" }\n\t| { type: \"text_start\"; contentIndex: number }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string }\n\t| { type: \"text_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"thinking_start\"; contentIndex: number }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string }\n\t| { type: \"thinking_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"toolcall_start\"; contentIndex: number; id: string; toolName: string }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n\t| { type: \"toolcall_end\"; contentIndex: number }\n\t| {\n\t\t\ttype: \"done\";\n\t\t\treason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\treason: Extract<StopReason, \"aborted\" | \"error\">;\n\t\t\terrorMessage?: string;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t };\n\ntype ProxySerializableStreamOptions = Pick<\n\tSimpleStreamOptions,\n\t| \"temperature\"\n\t| \"samplingParams\"\n\t| \"maxTokens\"\n\t| \"reasoning\"\n\t| \"cacheRetention\"\n\t| \"sessionId\"\n\t| \"headers\"\n\t| \"metadata\"\n\t| \"transport\"\n\t| \"thinkingBudgets\"\n\t| \"maxRetryDelayMs\"\n>;\n\nexport interface ProxyStreamOptions extends ProxySerializableStreamOptions {\n\t/** Local abort signal for the proxy request */\n\tsignal?: AbortSignal;\n\t/** Auth token for the proxy server */\n\tauthToken: string;\n\t/** Proxy server URL (e.g., \"https://genai.example.com\") */\n\tproxyUrl: string;\n}\n\n/**\n * Stream function that proxies through a server instead of calling LLM providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n *\n * Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.\n *\n * @example\n * ```typescript\n * const agent = new Agent({\n * streamFn: (model, context, options) =>\n * streamProxy(model, context, {\n * ...options,\n * authToken: await getAuthToken(),\n * proxyUrl: \"https://genai.example.com\",\n * }),\n * });\n * ```\n */\nfunction buildProxyRequestOptions(options: ProxyStreamOptions): ProxySerializableStreamOptions {\n\treturn {\n\t\ttemperature: options.temperature,\n\t\tsamplingParams: options.samplingParams,\n\t\tmaxTokens: options.maxTokens,\n\t\treasoning: options.reasoning,\n\t\tcacheRetention: options.cacheRetention,\n\t\tsessionId: options.sessionId,\n\t\theaders: options.headers,\n\t\tmetadata: options.metadata,\n\t\ttransport: options.transport,\n\t\tthinkingBudgets: options.thinkingBudgets,\n\t\tmaxRetryDelayMs: options.maxRetryDelayMs,\n\t};\n}\n\nexport function streamProxy(model: Model<any>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream {\n\tconst stream = new ProxyMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"pending\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${options.proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: buildProxyRequestOptions(options),\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\n/**\n * Process a proxy event and update the partial message.\n */\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\ttoolCall: content,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,WAAW,EACX,KAAK,KAAK,EAEV,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,MAAM,uBAAuB,CAAC;AAG/B,cAAM,uBAAwB,SAAQ,WAAW,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IACzF,cASC;CACD;AAED;;GAEG;AACH,MAAM,MAAM,0BAA0B,GACnC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAClE;IACA,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC;IAC3D,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAChC,GACD;IACA,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAChC,CAAC;AAEL,KAAK,8BAA8B,GAAG,IAAI,CACzC,mBAAmB,EACjB,aAAa,GACb,gBAAgB,GAChB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,WAAW,GACX,SAAS,GACT,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,iBAAiB,CACnB,CAAC;AAEF,MAAM,WAAW,kBAAmB,SAAQ,8BAA8B;IACzE,+CAA+C;IAC/C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;CACjB;AAqCD,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,GAAG,uBAAuB,CAqHrH","sourcesContent":["/**\n * Proxy stream function for apps that route LLM calls through a server.\n * The server manages auth and proxies requests to LLM providers.\n */\n\n// Internal import for JSON parsing utility\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n\ttype StopReason,\n\ttype ToolCall,\n} from \"@earendil-works/pi-ai\";\n\n// Create stream class matching ProxyMessageEventStream\nclass ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Proxy event types - server sends these with partial field stripped to reduce bandwidth.\n */\nexport type ProxyAssistantMessageEvent =\n\t| { type: \"start\" }\n\t| { type: \"text_start\"; contentIndex: number }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string }\n\t| { type: \"text_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"thinking_start\"; contentIndex: number }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string }\n\t| { type: \"thinking_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"toolcall_start\"; contentIndex: number; id: string; toolName: string }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n\t| { type: \"toolcall_end\"; contentIndex: number; toolCall: ToolCall }\n\t| {\n\t\t\ttype: \"done\";\n\t\t\treason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\treason: Extract<StopReason, \"aborted\" | \"error\">;\n\t\t\terrorMessage?: string;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t };\n\ntype ProxySerializableStreamOptions = Pick<\n\tSimpleStreamOptions,\n\t| \"temperature\"\n\t| \"samplingParams\"\n\t| \"maxTokens\"\n\t| \"reasoning\"\n\t| \"cacheRetention\"\n\t| \"sessionId\"\n\t| \"headers\"\n\t| \"metadata\"\n\t| \"transport\"\n\t| \"thinkingBudgets\"\n\t| \"maxRetryDelayMs\"\n>;\n\nexport interface ProxyStreamOptions extends ProxySerializableStreamOptions {\n\t/** Local abort signal for the proxy request */\n\tsignal?: AbortSignal;\n\t/** Auth token for the proxy server */\n\tauthToken: string;\n\t/** Proxy server URL (e.g., \"https://genai.example.com\") */\n\tproxyUrl: string;\n}\n\n/**\n * Stream function that proxies through a server instead of calling LLM providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n *\n * Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.\n *\n * @example\n * ```typescript\n * const agent = new Agent({\n * streamFn: (model, context, options) =>\n * streamProxy(model, context, {\n * ...options,\n * authToken: await getAuthToken(),\n * proxyUrl: \"https://genai.example.com\",\n * }),\n * });\n * ```\n */\nfunction buildProxyRequestOptions(options: ProxyStreamOptions): ProxySerializableStreamOptions {\n\treturn {\n\t\ttemperature: options.temperature,\n\t\tsamplingParams: options.samplingParams,\n\t\tmaxTokens: options.maxTokens,\n\t\treasoning: options.reasoning,\n\t\tcacheRetention: options.cacheRetention,\n\t\tsessionId: options.sessionId,\n\t\theaders: options.headers,\n\t\tmetadata: options.metadata,\n\t\ttransport: options.transport,\n\t\tthinkingBudgets: options.thinkingBudgets,\n\t\tmaxRetryDelayMs: options.maxRetryDelayMs,\n\t};\n}\n\nexport function streamProxy(model: Model<any>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream {\n\tconst stream = new ProxyMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"pending\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${options.proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: buildProxyRequestOptions(options),\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\n/**\n * Process a proxy event and update the partial message.\n */\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tObject.assign(content, proxyEvent.toolCall);\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\ttoolCall: content,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
package/dist/proxy.js CHANGED
@@ -250,6 +250,7 @@ function processProxyEvent(proxyEvent, partial) {
250
250
  case "toolcall_end": {
251
251
  const content = partial.content[proxyEvent.contentIndex];
252
252
  if (content?.type === "toolCall") {
253
+ Object.assign(content, proxyEvent.toolCall);
253
254
  delete content.partialJson;
254
255
  return {
255
256
  type: "toolcall_end",
package/dist/proxy.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,2CAA2C;AAC3C,OAAO,EAIN,WAAW,EAEX,kBAAkB,GAIlB,MAAM,uBAAuB,CAAC;AAE/B,uDAAuD;AACvD,MAAM,uBAAwB,SAAQ,WAAoD;IACzF,cAAc;QACb,KAAK,CACJ,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAC1D,CAAC,KAAK,EAAE,EAAE,CAAC;YACV,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;gBAAE,OAAO,KAAK,CAAC,OAAO,CAAC;YAChD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;gBAAE,OAAO,KAAK,CAAC,KAAK,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAAA,CACzC,CACD,CAAC;IAAA,CACF;CACD;AAoDD;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,wBAAwB,CAAC,OAA2B,EAAkC;IAC9F,OAAO;QACN,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,eAAe,EAAE,OAAO,CAAC,eAAe;KACxC,CAAC;AAAA,CACF;AAED,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,OAAgB,EAAE,OAA2B,EAA2B;IACtH,MAAM,MAAM,GAAG,IAAI,uBAAuB,EAAE,CAAC;IAE7C,CAAC,KAAK,IAAI,EAAE,CAAC;QACZ,iEAAiE;QACjE,MAAM,OAAO,GAAqB;YACjC,IAAI,EAAE,WAAW;YACjB,UAAU,EAAE,SAAS;YACrB,OAAO,EAAE,EAAE;YACX,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,KAAK,EAAE,KAAK,CAAC,EAAE;YACf,KAAK,EAAE;gBACN,KAAK,EAAE,CAAC;gBACR,MAAM,EAAE,CAAC;gBACT,SAAS,EAAE,CAAC;gBACZ,UAAU,EAAE,CAAC;gBACb,WAAW,EAAE,CAAC;gBACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;aACpE;YACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACrB,CAAC;QAEF,IAAI,MAA2D,CAAC;QAEhE,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC;YAC1B,IAAI,MAAM,EAAE,CAAC;gBACZ,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC,CAAC;YAC1D,CAAC;QAAA,CACD,CAAC;QAEF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACxD,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,QAAQ,aAAa,EAAE;gBAC9D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACR,aAAa,EAAE,UAAU,OAAO,CAAC,SAAS,EAAE;oBAC5C,cAAc,EAAE,kBAAkB;iBAClC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACpB,KAAK;oBACL,OAAO;oBACP,OAAO,EAAE,wBAAwB,CAAC,OAAO,CAAC;iBAC1C,CAAC;gBACF,MAAM,EAAE,OAAO,CAAC,MAAM;aACtB,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAClB,IAAI,YAAY,GAAG,gBAAgB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;gBAC5E,IAAI,CAAC;oBACJ,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;oBAChE,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;wBACrB,YAAY,GAAG,gBAAgB,SAAS,CAAC,KAAK,EAAE,CAAC;oBAClD,CAAC;gBACF,CAAC;gBAAC,MAAM,CAAC;oBACR,gCAAgC;gBACjC,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;YAC/B,CAAC;YAED,MAAM,GAAG,QAAQ,CAAC,IAAK,CAAC,SAAS,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;YAClC,IAAI,MAAM,GAAG,EAAE,CAAC;YAEhB,OAAO,IAAI,EAAE,CAAC;gBACb,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI;oBAAE,MAAM;gBAEhB,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;oBAC7B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;gBAC5C,CAAC;gBAED,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACjC,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;gBAE3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBAC1B,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;wBAClC,IAAI,IAAI,EAAE,CAAC;4BACV,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA+B,CAAC;4BAClE,MAAM,KAAK,GAAG,iBAAiB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;4BACrD,IAAI,KAAK,EAAE,CAAC;gCACX,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;4BACpB,CAAC;wBACF,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAC5C,CAAC;YAED,MAAM,CAAC,GAAG,EAAE,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;YAC7D,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC;YAC5B,OAAO,CAAC,YAAY,GAAG,YAAY,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,OAAO;gBACb,MAAM;gBACN,KAAK,EAAE,OAAO;aACd,CAAC,CAAC;YACH,MAAM,CAAC,GAAG,EAAE,CAAC;QACd,CAAC;gBAAS,CAAC;YACV,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAC3D,CAAC;QACF,CAAC;IAAA,CACD,CAAC,EAAE,CAAC;IAEL,OAAO,MAAM,CAAC;AAAA,CACd;AAED;;GAEG;AACH,SAAS,iBAAiB,CACzB,UAAsC,EACtC,OAAyB,EACW;IACpC,QAAQ,UAAU,CAAC,IAAI,EAAE,CAAC;QACzB,KAAK,OAAO;YACX,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QAEnC,KAAK,YAAY;YAChB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACtE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC;QAE/E,KAAK,YAAY,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC9B,OAAO,CAAC,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC;gBACjC,OAAO;oBACN,IAAI,EAAE,YAAY;oBAClB,YAAY,EAAE,UAAU,CAAC,YAAY;oBACrC,KAAK,EAAE,UAAU,CAAC,KAAK;oBACvB,OAAO;iBACP,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC7D,CAAC;QAED,KAAK,UAAU,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC9B,OAAO,CAAC,aAAa,GAAG,UAAU,CAAC,gBAAgB,CAAC;gBACpD,OAAO;oBACN,IAAI,EAAE,UAAU;oBAChB,YAAY,EAAE,UAAU,CAAC,YAAY;oBACrC,OAAO,EAAE,OAAO,CAAC,IAAI;oBACrB,OAAO;iBACP,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC3D,CAAC;QAED,KAAK,gBAAgB;YACpB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAC9E,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,YAAY,EAAE,UAAU,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC;QAEnF,KAAK,gBAAgB,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,OAAO,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;gBAClC,OAAO,CAAC,QAAQ,IAAI,UAAU,CAAC,KAAK,CAAC;gBACrC,OAAO;oBACN,IAAI,EAAE,gBAAgB;oBACtB,YAAY,EAAE,UAAU,CAAC,YAAY;oBACrC,KAAK,EAAE,UAAU,CAAC,KAAK;oBACvB,OAAO;iBACP,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACrE,CAAC;QAED,KAAK,cAAc,EAAE,CAAC;YACrB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,OAAO,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;gBAClC,OAAO,CAAC,iBAAiB,GAAG,UAAU,CAAC,gBAAgB,CAAC;gBACxD,OAAO;oBACN,IAAI,EAAE,cAAc;oBACpB,YAAY,EAAE,UAAU,CAAC,YAAY;oBACrC,OAAO,EAAE,OAAO,CAAC,QAAQ;oBACzB,OAAO;iBACP,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACnE,CAAC;QAED,KAAK,gBAAgB;YACpB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG;gBAC1C,IAAI,EAAE,UAAU;gBAChB,EAAE,EAAE,UAAU,CAAC,EAAE;gBACjB,IAAI,EAAE,UAAU,CAAC,QAAQ;gBACzB,SAAS,EAAE,EAAE;gBACb,WAAW,EAAE,EAAE;aAC0C,CAAC;YAC3D,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,YAAY,EAAE,UAAU,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC;QAEnF,KAAK,gBAAgB,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,OAAO,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;gBACjC,OAAe,CAAC,WAAW,IAAI,UAAU,CAAC,KAAK,CAAC;gBACjD,OAAO,CAAC,SAAS,GAAG,kBAAkB,CAAE,OAAe,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;gBAC3E,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,qBAAqB;gBAChF,OAAO;oBACN,IAAI,EAAE,gBAAgB;oBACtB,YAAY,EAAE,UAAU,CAAC,YAAY;oBACrC,KAAK,EAAE,UAAU,CAAC,KAAK;oBACvB,OAAO;iBACP,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACrE,CAAC;QAED,KAAK,cAAc,EAAE,CAAC;YACrB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,OAAO,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;gBAClC,OAAQ,OAAe,CAAC,WAAW,CAAC;gBACpC,OAAO;oBACN,IAAI,EAAE,cAAc;oBACpB,YAAY,EAAE,UAAU,CAAC,YAAY;oBACrC,QAAQ,EAAE,OAAO;oBACjB,OAAO;iBACP,CAAC;YACH,CAAC;YACD,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,KAAK,MAAM;YACV,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;YACvC,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;YACjC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QAEtE,KAAK,OAAO;YACX,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;YACvC,OAAO,CAAC,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC;YAC/C,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;YACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAErE,SAAS,CAAC;YACT,MAAM,gBAAgB,GAAU,UAAU,CAAC;YAC3C,OAAO,CAAC,IAAI,CAAC,+BAAgC,UAAkB,CAAC,IAAI,EAAE,CAAC,CAAC;YACxE,OAAO,SAAS,CAAC;QAClB,CAAC;IACF,CAAC;AAAA,CACD","sourcesContent":["/**\n * Proxy stream function for apps that route LLM calls through a server.\n * The server manages auth and proxies requests to LLM providers.\n */\n\n// Internal import for JSON parsing utility\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n\ttype StopReason,\n\ttype ToolCall,\n} from \"@earendil-works/pi-ai\";\n\n// Create stream class matching ProxyMessageEventStream\nclass ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Proxy event types - server sends these with partial field stripped to reduce bandwidth.\n */\nexport type ProxyAssistantMessageEvent =\n\t| { type: \"start\" }\n\t| { type: \"text_start\"; contentIndex: number }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string }\n\t| { type: \"text_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"thinking_start\"; contentIndex: number }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string }\n\t| { type: \"thinking_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"toolcall_start\"; contentIndex: number; id: string; toolName: string }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n\t| { type: \"toolcall_end\"; contentIndex: number }\n\t| {\n\t\t\ttype: \"done\";\n\t\t\treason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\treason: Extract<StopReason, \"aborted\" | \"error\">;\n\t\t\terrorMessage?: string;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t };\n\ntype ProxySerializableStreamOptions = Pick<\n\tSimpleStreamOptions,\n\t| \"temperature\"\n\t| \"samplingParams\"\n\t| \"maxTokens\"\n\t| \"reasoning\"\n\t| \"cacheRetention\"\n\t| \"sessionId\"\n\t| \"headers\"\n\t| \"metadata\"\n\t| \"transport\"\n\t| \"thinkingBudgets\"\n\t| \"maxRetryDelayMs\"\n>;\n\nexport interface ProxyStreamOptions extends ProxySerializableStreamOptions {\n\t/** Local abort signal for the proxy request */\n\tsignal?: AbortSignal;\n\t/** Auth token for the proxy server */\n\tauthToken: string;\n\t/** Proxy server URL (e.g., \"https://genai.example.com\") */\n\tproxyUrl: string;\n}\n\n/**\n * Stream function that proxies through a server instead of calling LLM providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n *\n * Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.\n *\n * @example\n * ```typescript\n * const agent = new Agent({\n * streamFn: (model, context, options) =>\n * streamProxy(model, context, {\n * ...options,\n * authToken: await getAuthToken(),\n * proxyUrl: \"https://genai.example.com\",\n * }),\n * });\n * ```\n */\nfunction buildProxyRequestOptions(options: ProxyStreamOptions): ProxySerializableStreamOptions {\n\treturn {\n\t\ttemperature: options.temperature,\n\t\tsamplingParams: options.samplingParams,\n\t\tmaxTokens: options.maxTokens,\n\t\treasoning: options.reasoning,\n\t\tcacheRetention: options.cacheRetention,\n\t\tsessionId: options.sessionId,\n\t\theaders: options.headers,\n\t\tmetadata: options.metadata,\n\t\ttransport: options.transport,\n\t\tthinkingBudgets: options.thinkingBudgets,\n\t\tmaxRetryDelayMs: options.maxRetryDelayMs,\n\t};\n}\n\nexport function streamProxy(model: Model<any>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream {\n\tconst stream = new ProxyMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"pending\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${options.proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: buildProxyRequestOptions(options),\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\n/**\n * Process a proxy event and update the partial message.\n */\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\ttoolCall: content,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Proxy stream function for apps that route LLM calls through a server.\n * The server manages auth and proxies requests to LLM providers.\n */\n\n// Internal import for JSON parsing utility\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n\ttype StopReason,\n\ttype ToolCall,\n} from \"@earendil-works/pi-ai\";\n\n// Create stream class matching ProxyMessageEventStream\nclass ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Proxy event types - server sends these with partial field stripped to reduce bandwidth.\n */\nexport type ProxyAssistantMessageEvent =\n\t| { type: \"start\" }\n\t| { type: \"text_start\"; contentIndex: number }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string }\n\t| { type: \"text_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"thinking_start\"; contentIndex: number }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string }\n\t| { type: \"thinking_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"toolcall_start\"; contentIndex: number; id: string; toolName: string }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n\t| { type: \"toolcall_end\"; contentIndex: number; toolCall: ToolCall }\n\t| {\n\t\t\ttype: \"done\";\n\t\t\treason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\treason: Extract<StopReason, \"aborted\" | \"error\">;\n\t\t\terrorMessage?: string;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t };\n\ntype ProxySerializableStreamOptions = Pick<\n\tSimpleStreamOptions,\n\t| \"temperature\"\n\t| \"samplingParams\"\n\t| \"maxTokens\"\n\t| \"reasoning\"\n\t| \"cacheRetention\"\n\t| \"sessionId\"\n\t| \"headers\"\n\t| \"metadata\"\n\t| \"transport\"\n\t| \"thinkingBudgets\"\n\t| \"maxRetryDelayMs\"\n>;\n\nexport interface ProxyStreamOptions extends ProxySerializableStreamOptions {\n\t/** Local abort signal for the proxy request */\n\tsignal?: AbortSignal;\n\t/** Auth token for the proxy server */\n\tauthToken: string;\n\t/** Proxy server URL (e.g., \"https://genai.example.com\") */\n\tproxyUrl: string;\n}\n\n/**\n * Stream function that proxies through a server instead of calling LLM providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n *\n * Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.\n *\n * @example\n * ```typescript\n * const agent = new Agent({\n * streamFn: (model, context, options) =>\n * streamProxy(model, context, {\n * ...options,\n * authToken: await getAuthToken(),\n * proxyUrl: \"https://genai.example.com\",\n * }),\n * });\n * ```\n */\nfunction buildProxyRequestOptions(options: ProxyStreamOptions): ProxySerializableStreamOptions {\n\treturn {\n\t\ttemperature: options.temperature,\n\t\tsamplingParams: options.samplingParams,\n\t\tmaxTokens: options.maxTokens,\n\t\treasoning: options.reasoning,\n\t\tcacheRetention: options.cacheRetention,\n\t\tsessionId: options.sessionId,\n\t\theaders: options.headers,\n\t\tmetadata: options.metadata,\n\t\ttransport: options.transport,\n\t\tthinkingBudgets: options.thinkingBudgets,\n\t\tmaxRetryDelayMs: options.maxRetryDelayMs,\n\t};\n}\n\nexport function streamProxy(model: Model<any>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream {\n\tconst stream = new ProxyMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"pending\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${options.proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: buildProxyRequestOptions(options),\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\n/**\n * Process a proxy event and update the partial message.\n */\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tObject.assign(content, proxyEvent.toolCall);\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\ttoolCall: content,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { Entry } from "../harness/session/types.ts";
2
+ export type { ScanningReadable, ScanningReadableOptions, ScanningReadableSource, ScanningSearchTextProjector, ScanningSessionSearchHit, ScanningSessionSearchOptions, SessionSearchCandidate, } from "./scanning.ts";
3
+ export { createScanningSessionSearch, scanningEntries } from "./scanning.ts";
4
+ export interface SessionSearchOptions {
5
+ /** Restrict results to specific canonical entry types. */
6
+ readonly entryTypes?: readonly Entry["type"][];
7
+ /** Maximum number of hits to return. */
8
+ readonly limit?: number;
9
+ /** Abort signal for cancellation, e.g. search-as-you-type. */
10
+ readonly signal?: AbortSignal;
11
+ }
12
+ export interface SessionSearchHit {
13
+ /** Logical identifier of the session that owns the entry. */
14
+ readonly sessionId: string;
15
+ /** Logical identifier of the entry within that session. */
16
+ readonly entryId: string;
17
+ }
18
+ export interface SessionSearch<T extends SessionSearchHit = SessionSearchHit> {
19
+ search(text: string, options?: SessionSearchOptions): AsyncIterable<T>;
20
+ }
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAEzD,YAAY,EACX,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,2BAA2B,EAC3B,wBAAwB,EACxB,4BAA4B,EAC5B,sBAAsB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,2BAA2B,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE7E,MAAM,WAAW,oBAAoB;IACpC,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;IAC/C,wCAAwC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,gBAAgB,GAAG,gBAAgB;IAC3E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;CACvE","sourcesContent":["import type { Entry } from \"../harness/session/types.ts\";\n\nexport type {\n\tScanningReadable,\n\tScanningReadableOptions,\n\tScanningReadableSource,\n\tScanningSearchTextProjector,\n\tScanningSessionSearchHit,\n\tScanningSessionSearchOptions,\n\tSessionSearchCandidate,\n} from \"./scanning.ts\";\nexport { createScanningSessionSearch, scanningEntries } from \"./scanning.ts\";\n\nexport interface SessionSearchOptions {\n\t/** Restrict results to specific canonical entry types. */\n\treadonly entryTypes?: readonly Entry[\"type\"][];\n\t/** Maximum number of hits to return. */\n\treadonly limit?: number;\n\t/** Abort signal for cancellation, e.g. search-as-you-type. */\n\treadonly signal?: AbortSignal;\n}\n\nexport interface SessionSearchHit {\n\t/** Logical identifier of the session that owns the entry. */\n\treadonly sessionId: string;\n\t/** Logical identifier of the entry within that session. */\n\treadonly entryId: string;\n}\n\nexport interface SessionSearch<T extends SessionSearchHit = SessionSearchHit> {\n\tsearch(text: string, options?: SessionSearchOptions): AsyncIterable<T>;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export { createScanningSessionSearch, scanningEntries } from "./scanning.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,2BAA2B,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC","sourcesContent":["import type { Entry } from \"../harness/session/types.ts\";\n\nexport type {\n\tScanningReadable,\n\tScanningReadableOptions,\n\tScanningReadableSource,\n\tScanningSearchTextProjector,\n\tScanningSessionSearchHit,\n\tScanningSessionSearchOptions,\n\tSessionSearchCandidate,\n} from \"./scanning.ts\";\nexport { createScanningSessionSearch, scanningEntries } from \"./scanning.ts\";\n\nexport interface SessionSearchOptions {\n\t/** Restrict results to specific canonical entry types. */\n\treadonly entryTypes?: readonly Entry[\"type\"][];\n\t/** Maximum number of hits to return. */\n\treadonly limit?: number;\n\t/** Abort signal for cancellation, e.g. search-as-you-type. */\n\treadonly signal?: AbortSignal;\n}\n\nexport interface SessionSearchHit {\n\t/** Logical identifier of the session that owns the entry. */\n\treadonly sessionId: string;\n\t/** Logical identifier of the entry within that session. */\n\treadonly entryId: string;\n}\n\nexport interface SessionSearch<T extends SessionSearchHit = SessionSearchHit> {\n\tsearch(text: string, options?: SessionSearchOptions): AsyncIterable<T>;\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import type { Entry, SessionMetadata, SessionStorage } from "../harness/session/types.ts";
2
+ import type { SessionSearch, SessionSearchHit, SessionSearchOptions } from "./index.ts";
3
+ export interface SessionSearchCandidate {
4
+ readonly entryId: string;
5
+ readonly seq: number;
6
+ readonly type: Entry["type"];
7
+ readonly timestamp: number;
8
+ readonly text: string;
9
+ readonly fields?: Record<string, unknown>;
10
+ }
11
+ export type ScanningReadable<TMetadata extends SessionMetadata = SessionMetadata> = Pick<SessionStorage<TMetadata>, "getMetadata" | "findEntries" | "getLabel">;
12
+ export type ScanningReadableSource<TMetadata extends SessionMetadata = SessionMetadata, TOptions = unknown> = (options?: TOptions) => AsyncIterable<ScanningReadable<TMetadata>>;
13
+ export type ScanningSearchTextProjector<TMetadata extends SessionMetadata = SessionMetadata> = (metadata: TMetadata, entry: Entry, label: string | undefined) => string;
14
+ export interface ScanningReadableOptions<TMetadata extends SessionMetadata = SessionMetadata> {
15
+ projectText?: ScanningSearchTextProjector<TMetadata>;
16
+ pageSize?: number;
17
+ }
18
+ export interface ScanningSessionSearchHit extends SessionSearchHit {
19
+ readonly timestamp: number;
20
+ readonly snippet: string;
21
+ }
22
+ export interface ScanningSessionSearchOptions<TMetadata extends SessionMetadata = SessionMetadata, TSourceOptions = unknown, THit extends SessionSearchHit = ScanningSessionSearchHit> extends ScanningReadableOptions<TMetadata> {
23
+ sourceOptions?: (text: string, options: SessionSearchOptions) => TSourceOptions | undefined;
24
+ match?: (queryText: string, candidate: SessionSearchCandidate, metadata: TMetadata) => boolean;
25
+ createHit?: (metadata: TMetadata, candidate: SessionSearchCandidate) => THit;
26
+ }
27
+ export declare function scanningEntries<TMetadata extends SessionMetadata>(readable: ScanningReadable<TMetadata>, options?: ScanningReadableOptions<TMetadata>): AsyncIterable<SessionSearchCandidate>;
28
+ export declare function createScanningSessionSearch<TMetadata extends SessionMetadata, TSourceOptions = unknown, THit extends SessionSearchHit = ScanningSessionSearchHit>(source: readonly ScanningReadable<TMetadata>[] | ScanningReadableSource<TMetadata, TSourceOptions>, options?: ScanningSessionSearchOptions<TMetadata, TSourceOptions, THit>): SessionSearch<THit>;
29
+ //# sourceMappingURL=scanning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanning.d.ts","sourceRoot":"","sources":["../../src/search/scanning.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAExF,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,MAAM,MAAM,gBAAgB,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe,IAAI,IAAI,CACvF,cAAc,CAAC,SAAS,CAAC,EACzB,aAAa,GAAG,aAAa,GAAG,UAAU,CAC1C,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe,EAAE,QAAQ,GAAG,OAAO,IAAI,CAC7G,OAAO,CAAC,EAAE,QAAQ,KACd,aAAa,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEhD,MAAM,MAAM,2BAA2B,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe,IAAI,CAC9F,QAAQ,EAAE,SAAS,EACnB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,GAAG,SAAS,KACrB,MAAM,CAAC;AAEZ,MAAM,WAAW,uBAAuB,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe;IAC3F,WAAW,CAAC,EAAE,2BAA2B,CAAC,SAAS,CAAC,CAAC;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B,CAC5C,SAAS,SAAS,eAAe,GAAG,eAAe,EACnD,cAAc,GAAG,OAAO,EACxB,IAAI,SAAS,gBAAgB,GAAG,wBAAwB,CACvD,SAAQ,uBAAuB,CAAC,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,KAAK,cAAc,GAAG,SAAS,CAAC;IAC5F,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,sBAAsB,EAAE,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC;IAC/F,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC7E;AA6CD,wBAAuB,eAAe,CAAC,SAAS,SAAS,eAAe,EACvE,QAAQ,EAAE,gBAAgB,CAAC,SAAS,CAAC,EACrC,OAAO,GAAE,uBAAuB,CAAC,SAAS,CAAM,GAC9C,aAAa,CAAC,sBAAsB,CAAC,CAEvC;AAuCD,wBAAgB,2BAA2B,CAC1C,SAAS,SAAS,eAAe,EACjC,cAAc,GAAG,OAAO,EACxB,IAAI,SAAS,gBAAgB,GAAG,wBAAwB,EAExD,MAAM,EAAE,SAAS,gBAAgB,CAAC,SAAS,CAAC,EAAE,GAAG,sBAAsB,CAAC,SAAS,EAAE,cAAc,CAAC,EAClG,OAAO,GAAE,4BAA4B,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,CAAM,GACzE,aAAa,CAAC,IAAI,CAAC,CAkCrB","sourcesContent":["import type { Entry, SessionMetadata, SessionStorage } from \"../harness/session/types.ts\";\nimport type { SessionSearch, SessionSearchHit, SessionSearchOptions } from \"./index.ts\";\n\nexport interface SessionSearchCandidate {\n\treadonly entryId: string;\n\treadonly seq: number;\n\treadonly type: Entry[\"type\"];\n\treadonly timestamp: number;\n\treadonly text: string;\n\treadonly fields?: Record<string, unknown>;\n}\n\nexport type ScanningReadable<TMetadata extends SessionMetadata = SessionMetadata> = Pick<\n\tSessionStorage<TMetadata>,\n\t\"getMetadata\" | \"findEntries\" | \"getLabel\"\n>;\n\nexport type ScanningReadableSource<TMetadata extends SessionMetadata = SessionMetadata, TOptions = unknown> = (\n\toptions?: TOptions,\n) => AsyncIterable<ScanningReadable<TMetadata>>;\n\nexport type ScanningSearchTextProjector<TMetadata extends SessionMetadata = SessionMetadata> = (\n\tmetadata: TMetadata,\n\tentry: Entry,\n\tlabel: string | undefined,\n) => string;\n\nexport interface ScanningReadableOptions<TMetadata extends SessionMetadata = SessionMetadata> {\n\tprojectText?: ScanningSearchTextProjector<TMetadata>;\n\tpageSize?: number;\n}\n\nexport interface ScanningSessionSearchHit extends SessionSearchHit {\n\treadonly timestamp: number;\n\treadonly snippet: string;\n}\n\nexport interface ScanningSessionSearchOptions<\n\tTMetadata extends SessionMetadata = SessionMetadata,\n\tTSourceOptions = unknown,\n\tTHit extends SessionSearchHit = ScanningSessionSearchHit,\n> extends ScanningReadableOptions<TMetadata> {\n\tsourceOptions?: (text: string, options: SessionSearchOptions) => TSourceOptions | undefined;\n\tmatch?: (queryText: string, candidate: SessionSearchCandidate, metadata: TMetadata) => boolean;\n\tcreateHit?: (metadata: TMetadata, candidate: SessionSearchCandidate) => THit;\n}\n\nfunction defaultSearchText<TMetadata extends SessionMetadata>(\n\t_metadata: TMetadata,\n\tentry: Entry,\n\tlabel: string | undefined,\n): string {\n\treturn label === undefined ? JSON.stringify(entry) : `${JSON.stringify(entry)} ${label}`;\n}\n\nasync function* scanReadableEntries<TMetadata extends SessionMetadata>(\n\treadable: ScanningReadable<TMetadata>,\n\tmetadata: TMetadata,\n\toptions: ScanningReadableOptions<TMetadata>,\n\tquery: { afterSeq?: number; limit?: number; entryTypes?: readonly Entry[\"type\"][] } = {},\n): AsyncIterable<SessionSearchCandidate> {\n\tconst projectText = options.projectText ?? defaultSearchText;\n\tconst pageSize = query.limit ?? options.pageSize ?? 100;\n\tlet afterSeq = query.afterSeq ?? 0;\n\tconst entryTypes = query.entryTypes === undefined ? undefined : new Set(query.entryTypes);\n\twhile (true) {\n\t\tconst entries = await readable.findEntries({\n\t\t\torder: \"oldestFirst\",\n\t\t\tlimit: pageSize,\n\t\t\tcursor: { afterSeq },\n\t\t\ttype: query.entryTypes?.length === 1 ? query.entryTypes[0] : undefined,\n\t\t});\n\t\tif (entries.length === 0) break;\n\t\tfor (const entry of entries) {\n\t\t\tif (entryTypes !== undefined && !entryTypes.has(entry.type)) continue;\n\t\t\tconst label = await readable.getLabel(entry.id);\n\t\t\tyield {\n\t\t\t\tentryId: entry.id,\n\t\t\t\tseq: entry.seq,\n\t\t\t\ttype: entry.type,\n\t\t\t\ttimestamp: entry.timestamp,\n\t\t\t\ttext: projectText(metadata, entry, label),\n\t\t\t\tfields: label === undefined ? undefined : { label },\n\t\t\t};\n\t\t}\n\t\tafterSeq = entries[entries.length - 1]?.seq ?? afterSeq;\n\t\tif (entries.length < pageSize) break;\n\t}\n}\n\nexport async function* scanningEntries<TMetadata extends SessionMetadata>(\n\treadable: ScanningReadable<TMetadata>,\n\toptions: ScanningReadableOptions<TMetadata> = {},\n): AsyncIterable<SessionSearchCandidate> {\n\tyield* scanReadableEntries(readable, await readable.getMetadata(), options);\n}\n\nasync function* arraySource<TMetadata extends SessionMetadata>(\n\treadables: readonly ScanningReadable<TMetadata>[],\n): AsyncIterable<ScanningReadable<TMetadata>> {\n\tyield* readables;\n}\n\nfunction readablesFor<TMetadata extends SessionMetadata, TSourceOptions>(\n\tsource: readonly ScanningReadable<TMetadata>[] | ScanningReadableSource<TMetadata, TSourceOptions>,\n\toptions: TSourceOptions | undefined,\n): AsyncIterable<ScanningReadable<TMetadata>> {\n\treturn typeof source === \"function\" ? source(options) : arraySource(source);\n}\n\nfunction defaultMatch(queryText: string, candidate: SessionSearchCandidate): boolean {\n\treturn candidate.text.toLowerCase().includes(queryText);\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n\tif (!signal?.aborted) return;\n\tif (signal.reason instanceof Error) throw signal.reason;\n\tconst error = new Error(\"The operation was aborted\");\n\terror.name = \"AbortError\";\n\tthrow error;\n}\n\nfunction createDefaultScanningHit<TMetadata extends SessionMetadata>(\n\tmetadata: TMetadata,\n\tcandidate: SessionSearchCandidate,\n): ScanningSessionSearchHit {\n\treturn {\n\t\tsessionId: metadata.id,\n\t\tentryId: candidate.entryId,\n\t\ttimestamp: candidate.timestamp,\n\t\tsnippet: candidate.text,\n\t};\n}\n\nexport function createScanningSessionSearch<\n\tTMetadata extends SessionMetadata,\n\tTSourceOptions = unknown,\n\tTHit extends SessionSearchHit = ScanningSessionSearchHit,\n>(\n\tsource: readonly ScanningReadable<TMetadata>[] | ScanningReadableSource<TMetadata, TSourceOptions>,\n\toptions: ScanningSessionSearchOptions<TMetadata, TSourceOptions, THit> = {},\n): SessionSearch<THit> {\n\tconst createHit =\n\t\toptions.createHit ??\n\t\t((metadata: TMetadata, candidate: SessionSearchCandidate) =>\n\t\t\tcreateDefaultScanningHit(metadata, candidate) as unknown as THit);\n\treturn {\n\t\tasync *search(text: string, searchOptions: SessionSearchOptions = {}): AsyncIterable<THit> {\n\t\t\tconst normalizedText = text.trim().toLowerCase();\n\t\t\tif (!normalizedText || (searchOptions.limit !== undefined && searchOptions.limit <= 0)) return;\n\t\t\tif (searchOptions.entryTypes?.length === 0) return;\n\t\t\tlet hitCount = 0;\n\t\t\tconst seenSessionIds = new Set<string>();\n\t\t\tconst entryTypes = searchOptions.entryTypes === undefined ? undefined : new Set(searchOptions.entryTypes);\n\t\t\tconst sourceOptions = options.sourceOptions?.(normalizedText, searchOptions);\n\t\t\tfor await (const readable of readablesFor(source, sourceOptions)) {\n\t\t\t\tthrowIfAborted(searchOptions.signal);\n\t\t\t\tconst metadata = await readable.getMetadata();\n\t\t\t\tif (seenSessionIds.has(metadata.id)) throw new Error(`Duplicate sessionId: ${metadata.id}`);\n\t\t\t\tseenSessionIds.add(metadata.id);\n\t\t\t\tfor await (const candidate of scanReadableEntries(readable, metadata, options, {\n\t\t\t\t\tentryTypes: searchOptions.entryTypes,\n\t\t\t\t})) {\n\t\t\t\t\tthrowIfAborted(searchOptions.signal);\n\t\t\t\t\tif (entryTypes !== undefined && !entryTypes.has(candidate.type)) continue;\n\t\t\t\t\tconst matches =\n\t\t\t\t\t\toptions.match?.(normalizedText, candidate, metadata) ?? defaultMatch(normalizedText, candidate);\n\t\t\t\t\tif (!matches) continue;\n\t\t\t\t\tyield createHit(metadata, candidate);\n\t\t\t\t\thitCount += 1;\n\t\t\t\t\tif (searchOptions.limit !== undefined && hitCount >= searchOptions.limit) return;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,103 @@
1
+ function defaultSearchText(_metadata, entry, label) {
2
+ return label === undefined ? JSON.stringify(entry) : `${JSON.stringify(entry)} ${label}`;
3
+ }
4
+ async function* scanReadableEntries(readable, metadata, options, query = {}) {
5
+ const projectText = options.projectText ?? defaultSearchText;
6
+ const pageSize = query.limit ?? options.pageSize ?? 100;
7
+ let afterSeq = query.afterSeq ?? 0;
8
+ const entryTypes = query.entryTypes === undefined ? undefined : new Set(query.entryTypes);
9
+ while (true) {
10
+ const entries = await readable.findEntries({
11
+ order: "oldestFirst",
12
+ limit: pageSize,
13
+ cursor: { afterSeq },
14
+ type: query.entryTypes?.length === 1 ? query.entryTypes[0] : undefined,
15
+ });
16
+ if (entries.length === 0)
17
+ break;
18
+ for (const entry of entries) {
19
+ if (entryTypes !== undefined && !entryTypes.has(entry.type))
20
+ continue;
21
+ const label = await readable.getLabel(entry.id);
22
+ yield {
23
+ entryId: entry.id,
24
+ seq: entry.seq,
25
+ type: entry.type,
26
+ timestamp: entry.timestamp,
27
+ text: projectText(metadata, entry, label),
28
+ fields: label === undefined ? undefined : { label },
29
+ };
30
+ }
31
+ afterSeq = entries[entries.length - 1]?.seq ?? afterSeq;
32
+ if (entries.length < pageSize)
33
+ break;
34
+ }
35
+ }
36
+ export async function* scanningEntries(readable, options = {}) {
37
+ yield* scanReadableEntries(readable, await readable.getMetadata(), options);
38
+ }
39
+ async function* arraySource(readables) {
40
+ yield* readables;
41
+ }
42
+ function readablesFor(source, options) {
43
+ return typeof source === "function" ? source(options) : arraySource(source);
44
+ }
45
+ function defaultMatch(queryText, candidate) {
46
+ return candidate.text.toLowerCase().includes(queryText);
47
+ }
48
+ function throwIfAborted(signal) {
49
+ if (!signal?.aborted)
50
+ return;
51
+ if (signal.reason instanceof Error)
52
+ throw signal.reason;
53
+ const error = new Error("The operation was aborted");
54
+ error.name = "AbortError";
55
+ throw error;
56
+ }
57
+ function createDefaultScanningHit(metadata, candidate) {
58
+ return {
59
+ sessionId: metadata.id,
60
+ entryId: candidate.entryId,
61
+ timestamp: candidate.timestamp,
62
+ snippet: candidate.text,
63
+ };
64
+ }
65
+ export function createScanningSessionSearch(source, options = {}) {
66
+ const createHit = options.createHit ??
67
+ ((metadata, candidate) => createDefaultScanningHit(metadata, candidate));
68
+ return {
69
+ async *search(text, searchOptions = {}) {
70
+ const normalizedText = text.trim().toLowerCase();
71
+ if (!normalizedText || (searchOptions.limit !== undefined && searchOptions.limit <= 0))
72
+ return;
73
+ if (searchOptions.entryTypes?.length === 0)
74
+ return;
75
+ let hitCount = 0;
76
+ const seenSessionIds = new Set();
77
+ const entryTypes = searchOptions.entryTypes === undefined ? undefined : new Set(searchOptions.entryTypes);
78
+ const sourceOptions = options.sourceOptions?.(normalizedText, searchOptions);
79
+ for await (const readable of readablesFor(source, sourceOptions)) {
80
+ throwIfAborted(searchOptions.signal);
81
+ const metadata = await readable.getMetadata();
82
+ if (seenSessionIds.has(metadata.id))
83
+ throw new Error(`Duplicate sessionId: ${metadata.id}`);
84
+ seenSessionIds.add(metadata.id);
85
+ for await (const candidate of scanReadableEntries(readable, metadata, options, {
86
+ entryTypes: searchOptions.entryTypes,
87
+ })) {
88
+ throwIfAborted(searchOptions.signal);
89
+ if (entryTypes !== undefined && !entryTypes.has(candidate.type))
90
+ continue;
91
+ const matches = options.match?.(normalizedText, candidate, metadata) ?? defaultMatch(normalizedText, candidate);
92
+ if (!matches)
93
+ continue;
94
+ yield createHit(metadata, candidate);
95
+ hitCount += 1;
96
+ if (searchOptions.limit !== undefined && hitCount >= searchOptions.limit)
97
+ return;
98
+ }
99
+ }
100
+ },
101
+ };
102
+ }
103
+ //# sourceMappingURL=scanning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanning.js","sourceRoot":"","sources":["../../src/search/scanning.ts"],"names":[],"mappings":"AA+CA,SAAS,iBAAiB,CACzB,SAAoB,EACpB,KAAY,EACZ,KAAyB,EAChB;IACT,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;AAAA,CACzF;AAED,KAAK,SAAS,CAAC,CAAC,mBAAmB,CAClC,QAAqC,EACrC,QAAmB,EACnB,OAA2C,EAC3C,KAAK,GAAiF,EAAE,EAChD;IACxC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,iBAAiB,CAAC;IAC7D,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC;IACxD,IAAI,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1F,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC;YAC1C,KAAK,EAAE,aAAa;YACpB,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,EAAE,QAAQ,EAAE;YACpB,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;SACtE,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAChC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,SAAS;YACtE,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAChD,MAAM;gBACL,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,IAAI,EAAE,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC;gBACzC,MAAM,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE;aACnD,CAAC;QACH,CAAC;QACD,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC;QACxD,IAAI,OAAO,CAAC,MAAM,GAAG,QAAQ;YAAE,MAAM;IACtC,CAAC;AAAA,CACD;AAED,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,eAAe,CACrC,QAAqC,EACrC,OAAO,GAAuC,EAAE,EACR;IACxC,KAAK,CAAC,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,QAAQ,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;AAAA,CAC5E;AAED,KAAK,SAAS,CAAC,CAAC,WAAW,CAC1B,SAAiD,EACJ;IAC7C,KAAK,CAAC,CAAC,SAAS,CAAC;AAAA,CACjB;AAED,SAAS,YAAY,CACpB,MAAkG,EAClG,OAAmC,EACU;IAC7C,OAAO,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AAAA,CAC5E;AAED,SAAS,YAAY,CAAC,SAAiB,EAAE,SAAiC,EAAW;IACpF,OAAO,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAAA,CACxD;AAED,SAAS,cAAc,CAAC,MAA+B,EAAQ;IAC9D,IAAI,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO;IAC7B,IAAI,MAAM,CAAC,MAAM,YAAY,KAAK;QAAE,MAAM,MAAM,CAAC,MAAM,CAAC;IACxD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACrD,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;IAC1B,MAAM,KAAK,CAAC;AAAA,CACZ;AAED,SAAS,wBAAwB,CAChC,QAAmB,EACnB,SAAiC,EACN;IAC3B,OAAO;QACN,SAAS,EAAE,QAAQ,CAAC,EAAE;QACtB,OAAO,EAAE,SAAS,CAAC,OAAO;QAC1B,SAAS,EAAE,SAAS,CAAC,SAAS;QAC9B,OAAO,EAAE,SAAS,CAAC,IAAI;KACvB,CAAC;AAAA,CACF;AAED,MAAM,UAAU,2BAA2B,CAK1C,MAAkG,EAClG,OAAO,GAAkE,EAAE,EACrD;IACtB,MAAM,SAAS,GACd,OAAO,CAAC,SAAS;QACjB,CAAC,CAAC,QAAmB,EAAE,SAAiC,EAAE,EAAE,CAC3D,wBAAwB,CAAC,QAAQ,EAAE,SAAS,CAAoB,CAAC,CAAC;IACpE,OAAO;QACN,KAAK,CAAC,CAAC,MAAM,CAAC,IAAY,EAAE,aAAa,GAAyB,EAAE,EAAuB;YAC1F,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,CAAC,cAAc,IAAI,CAAC,aAAa,CAAC,KAAK,KAAK,SAAS,IAAI,aAAa,CAAC,KAAK,IAAI,CAAC,CAAC;gBAAE,OAAO;YAC/F,IAAI,aAAa,CAAC,UAAU,EAAE,MAAM,KAAK,CAAC;gBAAE,OAAO;YACnD,IAAI,QAAQ,GAAG,CAAC,CAAC;YACjB,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;YACzC,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC1G,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;YAC7E,IAAI,KAAK,EAAE,MAAM,QAAQ,IAAI,YAAY,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC;gBAClE,cAAc,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBACrC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAC9C,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC5F,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gBAChC,IAAI,KAAK,EAAE,MAAM,SAAS,IAAI,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE;oBAC9E,UAAU,EAAE,aAAa,CAAC,UAAU;iBACpC,CAAC,EAAE,CAAC;oBACJ,cAAc,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;oBACrC,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC;wBAAE,SAAS;oBAC1E,MAAM,OAAO,GACZ,OAAO,CAAC,KAAK,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,CAAC,IAAI,YAAY,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;oBACjG,IAAI,CAAC,OAAO;wBAAE,SAAS;oBACvB,MAAM,SAAS,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;oBACrC,QAAQ,IAAI,CAAC,CAAC;oBACd,IAAI,aAAa,CAAC,KAAK,KAAK,SAAS,IAAI,QAAQ,IAAI,aAAa,CAAC,KAAK;wBAAE,OAAO;gBAClF,CAAC;YACF,CAAC;QAAA,CACD;KACD,CAAC;AAAA,CACF","sourcesContent":["import type { Entry, SessionMetadata, SessionStorage } from \"../harness/session/types.ts\";\nimport type { SessionSearch, SessionSearchHit, SessionSearchOptions } from \"./index.ts\";\n\nexport interface SessionSearchCandidate {\n\treadonly entryId: string;\n\treadonly seq: number;\n\treadonly type: Entry[\"type\"];\n\treadonly timestamp: number;\n\treadonly text: string;\n\treadonly fields?: Record<string, unknown>;\n}\n\nexport type ScanningReadable<TMetadata extends SessionMetadata = SessionMetadata> = Pick<\n\tSessionStorage<TMetadata>,\n\t\"getMetadata\" | \"findEntries\" | \"getLabel\"\n>;\n\nexport type ScanningReadableSource<TMetadata extends SessionMetadata = SessionMetadata, TOptions = unknown> = (\n\toptions?: TOptions,\n) => AsyncIterable<ScanningReadable<TMetadata>>;\n\nexport type ScanningSearchTextProjector<TMetadata extends SessionMetadata = SessionMetadata> = (\n\tmetadata: TMetadata,\n\tentry: Entry,\n\tlabel: string | undefined,\n) => string;\n\nexport interface ScanningReadableOptions<TMetadata extends SessionMetadata = SessionMetadata> {\n\tprojectText?: ScanningSearchTextProjector<TMetadata>;\n\tpageSize?: number;\n}\n\nexport interface ScanningSessionSearchHit extends SessionSearchHit {\n\treadonly timestamp: number;\n\treadonly snippet: string;\n}\n\nexport interface ScanningSessionSearchOptions<\n\tTMetadata extends SessionMetadata = SessionMetadata,\n\tTSourceOptions = unknown,\n\tTHit extends SessionSearchHit = ScanningSessionSearchHit,\n> extends ScanningReadableOptions<TMetadata> {\n\tsourceOptions?: (text: string, options: SessionSearchOptions) => TSourceOptions | undefined;\n\tmatch?: (queryText: string, candidate: SessionSearchCandidate, metadata: TMetadata) => boolean;\n\tcreateHit?: (metadata: TMetadata, candidate: SessionSearchCandidate) => THit;\n}\n\nfunction defaultSearchText<TMetadata extends SessionMetadata>(\n\t_metadata: TMetadata,\n\tentry: Entry,\n\tlabel: string | undefined,\n): string {\n\treturn label === undefined ? JSON.stringify(entry) : `${JSON.stringify(entry)} ${label}`;\n}\n\nasync function* scanReadableEntries<TMetadata extends SessionMetadata>(\n\treadable: ScanningReadable<TMetadata>,\n\tmetadata: TMetadata,\n\toptions: ScanningReadableOptions<TMetadata>,\n\tquery: { afterSeq?: number; limit?: number; entryTypes?: readonly Entry[\"type\"][] } = {},\n): AsyncIterable<SessionSearchCandidate> {\n\tconst projectText = options.projectText ?? defaultSearchText;\n\tconst pageSize = query.limit ?? options.pageSize ?? 100;\n\tlet afterSeq = query.afterSeq ?? 0;\n\tconst entryTypes = query.entryTypes === undefined ? undefined : new Set(query.entryTypes);\n\twhile (true) {\n\t\tconst entries = await readable.findEntries({\n\t\t\torder: \"oldestFirst\",\n\t\t\tlimit: pageSize,\n\t\t\tcursor: { afterSeq },\n\t\t\ttype: query.entryTypes?.length === 1 ? query.entryTypes[0] : undefined,\n\t\t});\n\t\tif (entries.length === 0) break;\n\t\tfor (const entry of entries) {\n\t\t\tif (entryTypes !== undefined && !entryTypes.has(entry.type)) continue;\n\t\t\tconst label = await readable.getLabel(entry.id);\n\t\t\tyield {\n\t\t\t\tentryId: entry.id,\n\t\t\t\tseq: entry.seq,\n\t\t\t\ttype: entry.type,\n\t\t\t\ttimestamp: entry.timestamp,\n\t\t\t\ttext: projectText(metadata, entry, label),\n\t\t\t\tfields: label === undefined ? undefined : { label },\n\t\t\t};\n\t\t}\n\t\tafterSeq = entries[entries.length - 1]?.seq ?? afterSeq;\n\t\tif (entries.length < pageSize) break;\n\t}\n}\n\nexport async function* scanningEntries<TMetadata extends SessionMetadata>(\n\treadable: ScanningReadable<TMetadata>,\n\toptions: ScanningReadableOptions<TMetadata> = {},\n): AsyncIterable<SessionSearchCandidate> {\n\tyield* scanReadableEntries(readable, await readable.getMetadata(), options);\n}\n\nasync function* arraySource<TMetadata extends SessionMetadata>(\n\treadables: readonly ScanningReadable<TMetadata>[],\n): AsyncIterable<ScanningReadable<TMetadata>> {\n\tyield* readables;\n}\n\nfunction readablesFor<TMetadata extends SessionMetadata, TSourceOptions>(\n\tsource: readonly ScanningReadable<TMetadata>[] | ScanningReadableSource<TMetadata, TSourceOptions>,\n\toptions: TSourceOptions | undefined,\n): AsyncIterable<ScanningReadable<TMetadata>> {\n\treturn typeof source === \"function\" ? source(options) : arraySource(source);\n}\n\nfunction defaultMatch(queryText: string, candidate: SessionSearchCandidate): boolean {\n\treturn candidate.text.toLowerCase().includes(queryText);\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n\tif (!signal?.aborted) return;\n\tif (signal.reason instanceof Error) throw signal.reason;\n\tconst error = new Error(\"The operation was aborted\");\n\terror.name = \"AbortError\";\n\tthrow error;\n}\n\nfunction createDefaultScanningHit<TMetadata extends SessionMetadata>(\n\tmetadata: TMetadata,\n\tcandidate: SessionSearchCandidate,\n): ScanningSessionSearchHit {\n\treturn {\n\t\tsessionId: metadata.id,\n\t\tentryId: candidate.entryId,\n\t\ttimestamp: candidate.timestamp,\n\t\tsnippet: candidate.text,\n\t};\n}\n\nexport function createScanningSessionSearch<\n\tTMetadata extends SessionMetadata,\n\tTSourceOptions = unknown,\n\tTHit extends SessionSearchHit = ScanningSessionSearchHit,\n>(\n\tsource: readonly ScanningReadable<TMetadata>[] | ScanningReadableSource<TMetadata, TSourceOptions>,\n\toptions: ScanningSessionSearchOptions<TMetadata, TSourceOptions, THit> = {},\n): SessionSearch<THit> {\n\tconst createHit =\n\t\toptions.createHit ??\n\t\t((metadata: TMetadata, candidate: SessionSearchCandidate) =>\n\t\t\tcreateDefaultScanningHit(metadata, candidate) as unknown as THit);\n\treturn {\n\t\tasync *search(text: string, searchOptions: SessionSearchOptions = {}): AsyncIterable<THit> {\n\t\t\tconst normalizedText = text.trim().toLowerCase();\n\t\t\tif (!normalizedText || (searchOptions.limit !== undefined && searchOptions.limit <= 0)) return;\n\t\t\tif (searchOptions.entryTypes?.length === 0) return;\n\t\t\tlet hitCount = 0;\n\t\t\tconst seenSessionIds = new Set<string>();\n\t\t\tconst entryTypes = searchOptions.entryTypes === undefined ? undefined : new Set(searchOptions.entryTypes);\n\t\t\tconst sourceOptions = options.sourceOptions?.(normalizedText, searchOptions);\n\t\t\tfor await (const readable of readablesFor(source, sourceOptions)) {\n\t\t\t\tthrowIfAborted(searchOptions.signal);\n\t\t\t\tconst metadata = await readable.getMetadata();\n\t\t\t\tif (seenSessionIds.has(metadata.id)) throw new Error(`Duplicate sessionId: ${metadata.id}`);\n\t\t\t\tseenSessionIds.add(metadata.id);\n\t\t\t\tfor await (const candidate of scanReadableEntries(readable, metadata, options, {\n\t\t\t\t\tentryTypes: searchOptions.entryTypes,\n\t\t\t\t})) {\n\t\t\t\t\tthrowIfAborted(searchOptions.signal);\n\t\t\t\t\tif (entryTypes !== undefined && !entryTypes.has(candidate.type)) continue;\n\t\t\t\t\tconst matches =\n\t\t\t\t\t\toptions.match?.(normalizedText, candidate, metadata) ?? defaultMatch(normalizedText, candidate);\n\t\t\t\t\tif (!matches) continue;\n\t\t\t\t\tyield createHit(metadata, candidate);\n\t\t\t\t\thitCount += 1;\n\t\t\t\t\tif (searchOptions.limit !== undefined && hitCount >= searchOptions.limit) return;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-agent-core",
3
- "version": "0.84.1",
3
+ "version": "0.84.3",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,8 +35,8 @@
35
35
  "prepublishOnly": "npm run build"
36
36
  },
37
37
  "dependencies": {
38
- "@earendil-works/pi-ai": "^0.84.1",
39
- "@earendil-works/pi-telemetry": "^0.84.1",
38
+ "@earendil-works/pi-ai": "^0.84.3",
39
+ "@earendil-works/pi-telemetry": "^0.84.3",
40
40
  "diff": "8.0.4",
41
41
  "ignore": "7.0.5",
42
42
  "typebox": "1.3.7",
@@ -60,7 +60,7 @@
60
60
  "node": ">=22.19.0"
61
61
  },
62
62
  "devDependencies": {
63
- "@types/node": "24.12.4",
63
+ "@types/node": "22.19.19",
64
64
  "@vitest/coverage-v8": "4.1.9",
65
65
  "typescript": "5.9.3",
66
66
  "vitest": "4.1.9"
@@ -1,19 +0,0 @@
1
- import type { FileError, Result } from "../types.ts";
2
- import { type SessionCreateOptions, type SessionMetadata, type SessionRepo } from "./types.ts";
3
- export interface SessionSearchOptions {
4
- text: string;
5
- cwd?: string;
6
- }
7
- export interface SessionSearchHit<TMetadata extends SessionMetadata = SessionMetadata> {
8
- metadata: TMetadata;
9
- entryId: string;
10
- timestamp: string;
11
- snippet?: string;
12
- score?: number;
13
- }
14
- export interface SessionSearch<TMetadata extends SessionMetadata = SessionMetadata> {
15
- search(options: SessionSearchOptions): Promise<SessionSearchHit<TMetadata>[]>;
16
- }
17
- export declare function getFileSystemResultOrThrow<TValue>(result: Result<TValue, FileError>, message: string): TValue;
18
- export declare function createScanningSessionSearch<TMetadata extends SessionMetadata, TCreateOptions extends SessionCreateOptions, TListOptions>(source: Pick<SessionRepo<TMetadata, TCreateOptions, TListOptions>, "list" | "open">): SessionSearch<TMetadata>;
19
- //# sourceMappingURL=search.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/harness/session/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,KAAK,oBAAoB,EAAgB,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE7G,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe;IACpF,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe;IACjF,MAAM,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;CAC9E;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAM7G;AAqCD,wBAAgB,2BAA2B,CAC1C,SAAS,SAAS,eAAe,EACjC,cAAc,SAAS,oBAAoB,EAC3C,YAAY,EACX,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,aAAa,CAAC,SAAS,CAAC,CAE/G","sourcesContent":["import type { FileError, Result } from \"../types.ts\";\nimport type { Session } from \"./session.ts\";\nimport { type SessionCreateOptions, SessionError, type SessionMetadata, type SessionRepo } from \"./types.ts\";\n\nexport interface SessionSearchOptions {\n\ttext: string;\n\tcwd?: string;\n}\n\nexport interface SessionSearchHit<TMetadata extends SessionMetadata = SessionMetadata> {\n\tmetadata: TMetadata;\n\tentryId: string;\n\ttimestamp: string;\n\tsnippet?: string;\n\tscore?: number;\n}\n\nexport interface SessionSearch<TMetadata extends SessionMetadata = SessionMetadata> {\n\tsearch(options: SessionSearchOptions): Promise<SessionSearchHit<TMetadata>[]>;\n}\n\nexport function getFileSystemResultOrThrow<TValue>(result: Result<TValue, FileError>, message: string): TValue {\n\tif (!result.ok) {\n\t\tconst code = result.error.code === \"not_found\" ? \"not_found\" : \"storage\";\n\t\tthrow new SessionError(code, `${message}: ${result.error.message}`, result.error);\n\t}\n\treturn result.value;\n}\n\ntype ScanningSessionSearchSource<TMetadata extends SessionMetadata> = {\n\tlist(): Promise<TMetadata[]>;\n\topen(metadata: TMetadata): Promise<Session<TMetadata>>;\n};\n\nclass ScanningSessionSearch<TMetadata extends SessionMetadata = SessionMetadata> implements SessionSearch<TMetadata> {\n\tprivate readonly source: ScanningSessionSearchSource<TMetadata>;\n\n\tconstructor(source: ScanningSessionSearchSource<TMetadata>) {\n\t\tthis.source = source;\n\t}\n\n\tasync search(options: SessionSearchOptions): Promise<SessionSearchHit<TMetadata>[]> {\n\t\tconst normalizedText = options.text.trim().toLowerCase();\n\t\tif (!normalizedText) return [];\n\t\tconst hits: SessionSearchHit<TMetadata>[] = [];\n\t\tfor (const metadata of await this.source.list()) {\n\t\t\tconst cwd = (metadata as { cwd?: unknown }).cwd;\n\t\t\tif (options.cwd !== undefined && cwd !== options.cwd) continue;\n\t\t\tconst session = await this.source.open(metadata);\n\t\t\tfor (const entry of await session.findEntries({ order: \"oldestFirst\" })) {\n\t\t\t\tconst payload = JSON.stringify(entry);\n\t\t\t\tif (!payload.toLowerCase().includes(normalizedText)) continue;\n\t\t\t\thits.push({\n\t\t\t\t\tmetadata,\n\t\t\t\t\tentryId: entry.id,\n\t\t\t\t\ttimestamp: new Date(entry.timestamp).toISOString(),\n\t\t\t\t\tsnippet: payload,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn hits;\n\t}\n}\n\nexport function createScanningSessionSearch<\n\tTMetadata extends SessionMetadata,\n\tTCreateOptions extends SessionCreateOptions,\n\tTListOptions,\n>(source: Pick<SessionRepo<TMetadata, TCreateOptions, TListOptions>, \"list\" | \"open\">): SessionSearch<TMetadata> {\n\treturn new ScanningSessionSearch(source);\n}\n"]}
@@ -1,42 +0,0 @@
1
- import { SessionError } from "./types.js";
2
- export function getFileSystemResultOrThrow(result, message) {
3
- if (!result.ok) {
4
- const code = result.error.code === "not_found" ? "not_found" : "storage";
5
- throw new SessionError(code, `${message}: ${result.error.message}`, result.error);
6
- }
7
- return result.value;
8
- }
9
- class ScanningSessionSearch {
10
- source;
11
- constructor(source) {
12
- this.source = source;
13
- }
14
- async search(options) {
15
- const normalizedText = options.text.trim().toLowerCase();
16
- if (!normalizedText)
17
- return [];
18
- const hits = [];
19
- for (const metadata of await this.source.list()) {
20
- const cwd = metadata.cwd;
21
- if (options.cwd !== undefined && cwd !== options.cwd)
22
- continue;
23
- const session = await this.source.open(metadata);
24
- for (const entry of await session.findEntries({ order: "oldestFirst" })) {
25
- const payload = JSON.stringify(entry);
26
- if (!payload.toLowerCase().includes(normalizedText))
27
- continue;
28
- hits.push({
29
- metadata,
30
- entryId: entry.id,
31
- timestamp: new Date(entry.timestamp).toISOString(),
32
- snippet: payload,
33
- });
34
- }
35
- }
36
- return hits;
37
- }
38
- }
39
- export function createScanningSessionSearch(source) {
40
- return new ScanningSessionSearch(source);
41
- }
42
- //# sourceMappingURL=search.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/harness/session/search.ts"],"names":[],"mappings":"AAEA,OAAO,EAA6B,YAAY,EAA0C,MAAM,YAAY,CAAC;AAmB7G,MAAM,UAAU,0BAA0B,CAAS,MAAiC,EAAE,OAAe,EAAU;IAC9G,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,MAAM,IAAI,YAAY,CAAC,IAAI,EAAE,GAAG,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC;AAAA,CACpB;AAOD,MAAM,qBAAqB;IACT,MAAM,CAAyC;IAEhE,YAAY,MAA8C,EAAE;QAC3D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IAAA,CACrB;IAED,KAAK,CAAC,MAAM,CAAC,OAA6B,EAA0C;QACnF,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACzD,IAAI,CAAC,cAAc;YAAE,OAAO,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAkC,EAAE,CAAC;QAC/C,KAAK,MAAM,QAAQ,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YACjD,MAAM,GAAG,GAAI,QAA8B,CAAC,GAAG,CAAC;YAChD,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,OAAO,CAAC,GAAG;gBAAE,SAAS;YAC/D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjD,KAAK,MAAM,KAAK,IAAI,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;gBACzE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBACtC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;oBAAE,SAAS;gBAC9D,IAAI,CAAC,IAAI,CAAC;oBACT,QAAQ;oBACR,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;oBAClD,OAAO,EAAE,OAAO;iBAChB,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QACD,OAAO,IAAI,CAAC;IAAA,CACZ;CACD;AAED,MAAM,UAAU,2BAA2B,CAIzC,MAAmF,EAA4B;IAChH,OAAO,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAAA,CACzC","sourcesContent":["import type { FileError, Result } from \"../types.ts\";\nimport type { Session } from \"./session.ts\";\nimport { type SessionCreateOptions, SessionError, type SessionMetadata, type SessionRepo } from \"./types.ts\";\n\nexport interface SessionSearchOptions {\n\ttext: string;\n\tcwd?: string;\n}\n\nexport interface SessionSearchHit<TMetadata extends SessionMetadata = SessionMetadata> {\n\tmetadata: TMetadata;\n\tentryId: string;\n\ttimestamp: string;\n\tsnippet?: string;\n\tscore?: number;\n}\n\nexport interface SessionSearch<TMetadata extends SessionMetadata = SessionMetadata> {\n\tsearch(options: SessionSearchOptions): Promise<SessionSearchHit<TMetadata>[]>;\n}\n\nexport function getFileSystemResultOrThrow<TValue>(result: Result<TValue, FileError>, message: string): TValue {\n\tif (!result.ok) {\n\t\tconst code = result.error.code === \"not_found\" ? \"not_found\" : \"storage\";\n\t\tthrow new SessionError(code, `${message}: ${result.error.message}`, result.error);\n\t}\n\treturn result.value;\n}\n\ntype ScanningSessionSearchSource<TMetadata extends SessionMetadata> = {\n\tlist(): Promise<TMetadata[]>;\n\topen(metadata: TMetadata): Promise<Session<TMetadata>>;\n};\n\nclass ScanningSessionSearch<TMetadata extends SessionMetadata = SessionMetadata> implements SessionSearch<TMetadata> {\n\tprivate readonly source: ScanningSessionSearchSource<TMetadata>;\n\n\tconstructor(source: ScanningSessionSearchSource<TMetadata>) {\n\t\tthis.source = source;\n\t}\n\n\tasync search(options: SessionSearchOptions): Promise<SessionSearchHit<TMetadata>[]> {\n\t\tconst normalizedText = options.text.trim().toLowerCase();\n\t\tif (!normalizedText) return [];\n\t\tconst hits: SessionSearchHit<TMetadata>[] = [];\n\t\tfor (const metadata of await this.source.list()) {\n\t\t\tconst cwd = (metadata as { cwd?: unknown }).cwd;\n\t\t\tif (options.cwd !== undefined && cwd !== options.cwd) continue;\n\t\t\tconst session = await this.source.open(metadata);\n\t\t\tfor (const entry of await session.findEntries({ order: \"oldestFirst\" })) {\n\t\t\t\tconst payload = JSON.stringify(entry);\n\t\t\t\tif (!payload.toLowerCase().includes(normalizedText)) continue;\n\t\t\t\thits.push({\n\t\t\t\t\tmetadata,\n\t\t\t\t\tentryId: entry.id,\n\t\t\t\t\ttimestamp: new Date(entry.timestamp).toISOString(),\n\t\t\t\t\tsnippet: payload,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn hits;\n\t}\n}\n\nexport function createScanningSessionSearch<\n\tTMetadata extends SessionMetadata,\n\tTCreateOptions extends SessionCreateOptions,\n\tTListOptions,\n>(source: Pick<SessionRepo<TMetadata, TCreateOptions, TListOptions>, \"list\" | \"open\">): SessionSearch<TMetadata> {\n\treturn new ScanningSessionSearch(source);\n}\n"]}