apex-code 0.0.1-alpha.8 → 0.0.1-alpha.9
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.
- package/CHANGELOG.md +83 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +29 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +21 -13
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +26 -7
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +163 -95
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -7
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +41 -32
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/durable-state/sqlite.d.ts.map +1 -1
- package/dist/core/durable-state/sqlite.js +26 -2
- package/dist/core/durable-state/sqlite.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +115 -48
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +38 -9
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/keybindings.d.ts +20 -19
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +26 -5
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +3 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +14 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +22 -14
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +4 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +32 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +9 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +53 -11
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +8 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +14 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +36 -20
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +2 -5
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +20 -4
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts +4 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +9 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +6 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +4 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +18 -15
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +10 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +51 -9
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +7 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +118 -56
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +74 -17
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +11 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +164 -17
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +5 -4
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +16 -13
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +20 -6
- package/dist/modes/json-event.js.map +1 -1
- package/dist/package-manager-cli.d.ts +1 -0
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +197 -5
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +17 -5
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +60 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/docs/compaction.md +15 -0
- package/docs/custom-provider.md +5 -2
- package/docs/extensions.md +17 -7
- package/docs/json.md +7 -2
- package/docs/keybindings.md +8 -8
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/packages.md +2 -2
- package/docs/rpc.md +9 -3
- package/docs/sdk.md +7 -2
- package/docs/settings.md +11 -3
- package/docs/skills.md +4 -3
- package/docs/terminal-setup.md +11 -7
- package/docs/usage.md +6 -2
- package/docs/windows.md +24 -2
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-checkpoint.ts +2 -2
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/examples/extensions/subagent/index.ts +6 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +276 -390
- package/package.json +9 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/core/defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,sBAAsB,GAAkB,QAAQ,CAAC","sourcesContent":["import type { ThinkingLevel } from \"apex-code-agent-core\";\n\nexport const DEFAULT_THINKING_LEVEL: ThinkingLevel = \"medium\";\n"]}
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/core/defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,sBAAsB,GAAkB,QAAQ,CAAC;AAC9D,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC/D,KAAK;IACL,SAAS;IACT,KAAK;IACL,QAAQ;IACR,MAAM;IACN,OAAO;IACP,KAAK;CACL,CAAC","sourcesContent":["import type { ThinkingLevel } from \"apex-code-agent-core\";\n\nexport const DEFAULT_THINKING_LEVEL: ThinkingLevel = \"medium\";\nexport const THINKING_LEVEL_OPTIONS: readonly ThinkingLevel[] = [\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n];\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../../src/core/durable-state/sqlite.ts"],"names":[],"mappings":"AAGA,iEAAiE;AACjE,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,CAAC;AAEjG,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,WAAW,CAAC;AACtD,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,gBAAgB,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAiCD;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,SAAS,GAAG,SAAS,CAAC;IACrC,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IACjC,aAAa,IAAI,MAAM,CAAC;IACxB,UAAU,IAAI,MAAM,EAAE,CAAC;IACvB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACrC,YAAY,CAAC,KAAK,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,oBAAoB,CAAC;IAC/F,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnH,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,mBAAmB,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,oBAAoB,CAAC;IACrH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAAC;IACzD,YAAY,IAAI,MAAM,CAAC;IACvB,yBAAyB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACnE,wCAAwC,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAAC;IAChF,YAAY,CAAC,KAAK,EAAE;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,gBAAgB,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;KACd,GAAG,kBAAkB,CAAC;IACvB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAC;IAC5D,sBAAsB,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACvD,oBAAoB,IAAI,mBAAmB,EAAE,CAAC;IAC9C,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IACpD,KAAK,IAAI,IAAI,CAAC;CACd;AA4HD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAsPrE","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { DatabaseSync } from \"node:sqlite\";\n\n/** Current schema for the daemon-owned durable-state sidecar. */\nexport const CURRENT_DURABLE_STATE_SCHEMA_VERSION = 4;\n\nexport type CommandJournalState = \"created\" | \"running\" | \"completed\" | \"failed\" | \"interrupted\";\n\nexport type SessionLeaseMode = \"shared\" | \"exclusive\";\nexport interface SessionLeaseRecord {\n\tsessionId: string;\n\townerId: string;\n\tmode: SessionLeaseMode;\n\texpiresAt: string;\n}\n\nexport interface CommandJournalRecord {\n\tid: string;\n\tsessionId: string;\n\tcommand: string;\n\tstate: CommandJournalState;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\trecoveryReason?: string;\n}\n\nconst COMMAND_TRANSITIONS: Record<CommandJournalState, readonly CommandJournalState[]> = {\n\tcreated: [\"running\", \"failed\", \"interrupted\"],\n\trunning: [\"completed\", \"failed\", \"interrupted\"],\n\tcompleted: [],\n\tfailed: [],\n\tinterrupted: [],\n};\n\nconst TABLES = [\n\t\"cache_entries\",\n\t\"command_journal\",\n\t\"daemon_metadata\",\n\t\"model_performance\",\n\t\"schema_migrations\",\n\t\"session_leases\",\n] as const;\n\n/** Columns added to `model_performance` in schema version 4 (roadmap Phase 8). */\nconst MODEL_PERFORMANCE_V4_COLUMNS: ReadonlyArray<readonly [string, string]> = [\n\t[\"session_id\", \"TEXT\"],\n\t[\"role\", \"TEXT\"],\n\t[\"credential_identity\", \"TEXT\"],\n\t[\"outcome\", \"TEXT\"],\n\t[\"failure_kind\", \"TEXT\"],\n\t[\"input_tokens\", \"INTEGER\"],\n\t[\"output_tokens\", \"INTEGER\"],\n\t[\"cache_read_tokens\", \"INTEGER\"],\n\t[\"cache_write_tokens\", \"INTEGER\"],\n\t[\"cost\", \"REAL\"],\n];\n\n/**\n * Plain, domain-independent row shape for `model_performance`. Deliberately not the\n * richer `UsagePerformanceSample` (which carries a branded `CredentialIdentity` and a\n * nested `usage` object) — this module owns SQLite schema and writes only, never\n * `ModelRuntime`'s domain types, to avoid a circular import between the two.\n */\nexport interface UsagePerformanceRow {\n\tprovider: string;\n\tmodelId: string;\n\tttftMs: number | null;\n\tgenerationMs: number | null;\n\tsampledAt: string;\n\tsessionId: string | null;\n\trole: string | null;\n\tcredentialIdentity: string | null;\n\toutcome: string | null;\n\tfailureKind: string | null;\n\tinputTokens: number | null;\n\toutputTokens: number | null;\n\tcacheReadTokens: number | null;\n\tcacheWriteTokens: number | null;\n\tcost: number | null;\n}\n\nexport interface RecoveryDiagnostic {\n\tcommandId: string;\n\tsessionId: string;\n\tpreviousState: \"created\" | \"running\";\n\tstate: \"interrupted\";\n\treason: string;\n\trecoveredAt: string;\n}\n\nexport interface DurableStateStore {\n\tschemaVersion(): number;\n\ttableNames(): string[];\n\tcolumns(tableName: string): string[];\n\tbeginCommand(input: { id?: string; sessionId: string; command: string }): CommandJournalRecord;\n\trunCommand<T>(input: { id?: string; sessionId: string; command: string }, operation: () => Promise<T>): Promise<T>;\n\ttransitionCommand(id: string, state: Exclude<CommandJournalState, \"created\">, reason?: string): CommandJournalRecord;\n\tgetCommand(id: string): CommandJournalRecord | undefined;\n\tcommandCount(): number;\n\trecoverUnfinishedCommands(reason?: string): CommandJournalRecord[];\n\trecoverUnfinishedCommandsWithDiagnostics(reason?: string): RecoveryDiagnostic[];\n\tacquireLease(input: {\n\t\tsessionId: string;\n\t\townerId: string;\n\t\tmode: SessionLeaseMode;\n\t\tttlMs: number;\n\t}): SessionLeaseRecord;\n\treleaseLease(sessionId: string, ownerId: string): void;\n\tgetLease(sessionId: string): SessionLeaseRecord | undefined;\n\trecordUsagePerformance(row: UsagePerformanceRow): void;\n\tlistUsagePerformance(): UsagePerformanceRow[];\n\tpruneUsagePerformance(olderThanIso: string): number;\n\tclose(): void;\n}\n\nfunction readSchemaVersion(database: DatabaseSync): number {\n\tconst row = database.prepare(\"SELECT COALESCE(MAX(version), 0) AS version FROM schema_migrations\").get() as\n\t\t| { version: number }\n\t\t| undefined;\n\treturn row?.version ?? 0;\n}\n\nfunction createSchema(database: DatabaseSync): void {\n\tdatabase.exec(`\n\t\tCREATE TABLE IF NOT EXISTS schema_migrations (\n\t\t\tversion INTEGER PRIMARY KEY,\n\t\t\tapplied_at TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS daemon_metadata (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS command_journal (\n\t\t\tid TEXT PRIMARY KEY,\n\t\t\tsession_id TEXT NOT NULL,\n\t\t\tcommand TEXT NOT NULL,\n\t\t\tstate TEXT NOT NULL,\n\t\t\trecovery_reason TEXT,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tupdated_at TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS session_leases (\n\t\t\tsession_id TEXT NOT NULL,\n\t\t\towner_id TEXT NOT NULL,\n\t\t\tmode TEXT NOT NULL,\n\t\t\texpires_at TEXT NOT NULL,\n\t\t\tPRIMARY KEY (session_id, owner_id)\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS model_performance (\n\t\t\tid INTEGER PRIMARY KEY,\n\t\t\tprovider TEXT NOT NULL,\n\t\t\tmodel_id TEXT NOT NULL,\n\t\t\tttft_ms REAL,\n\t\t\tgeneration_ms REAL,\n\t\t\tsampled_at TEXT NOT NULL,\n\t\t\tsession_id TEXT,\n\t\t\trole TEXT,\n\t\t\tcredential_identity TEXT,\n\t\t\toutcome TEXT,\n\t\t\tfailure_kind TEXT,\n\t\t\tinput_tokens INTEGER,\n\t\t\toutput_tokens INTEGER,\n\t\t\tcache_read_tokens INTEGER,\n\t\t\tcache_write_tokens INTEGER,\n\t\t\tcost REAL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS cache_entries (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue BLOB NOT NULL,\n\t\t\texpires_at TEXT\n\t\t);\n\t`);\n}\n\nfunction readCommand(database: DatabaseSync, id: string): CommandJournalRecord | undefined {\n\tconst row = database\n\t\t.prepare(\n\t\t\t\"SELECT id, session_id, command, state, recovery_reason, created_at, updated_at FROM command_journal WHERE id = ?\",\n\t\t)\n\t\t.get(id) as\n\t\t| {\n\t\t\t\tid: string;\n\t\t\t\tsession_id: string;\n\t\t\t\tcommand: string;\n\t\t\t\tstate: CommandJournalState;\n\t\t\t\trecovery_reason: string | null;\n\t\t\t\tcreated_at: string;\n\t\t\t\tupdated_at: string;\n\t\t }\n\t\t| undefined;\n\tif (!row) return undefined;\n\treturn {\n\t\tid: row.id,\n\t\tsessionId: row.session_id,\n\t\tcommand: row.command,\n\t\tstate: row.state,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t\t...(row.recovery_reason ? { recoveryReason: row.recovery_reason } : {}),\n\t};\n}\n\nfunction readLeases(database: DatabaseSync, sessionId: string): SessionLeaseRecord[] {\n\tconst rows = database\n\t\t.prepare(\n\t\t\t\"SELECT session_id, owner_id, mode, expires_at FROM session_leases WHERE session_id = ? ORDER BY owner_id\",\n\t\t)\n\t\t.all(sessionId) as Array<{ session_id: string; owner_id: string; mode: SessionLeaseMode; expires_at: string }>;\n\treturn rows.map((row) => ({\n\t\tsessionId: row.session_id,\n\t\townerId: row.owner_id,\n\t\tmode: row.mode,\n\t\texpiresAt: row.expires_at,\n\t}));\n}\n\nfunction readLease(database: DatabaseSync, sessionId: string): SessionLeaseRecord | undefined {\n\treturn readLeases(database, sessionId)[0];\n}\n\nfunction transitionCommand(\n\tdatabase: DatabaseSync,\n\tid: string,\n\tstate: Exclude<CommandJournalState, \"created\">,\n\treason?: string,\n): CommandJournalRecord {\n\tconst current = readCommand(database, id);\n\tif (!current) throw new Error(`Unknown command journal entry: ${id}`);\n\tif (!COMMAND_TRANSITIONS[current.state].includes(state))\n\t\tthrow new Error(`Invalid command transition: ${current.state} -> ${state}`);\n\tconst now = new Date().toISOString();\n\tdatabase\n\t\t.prepare(\"UPDATE command_journal SET state = ?, recovery_reason = ?, updated_at = ? WHERE id = ?\")\n\t\t.run(state, reason ?? null, now, id);\n\treturn readCommand(database, id)!;\n}\n\nexport function openDurableStateStore(path: string): DurableStateStore {\n\tconst database = new DatabaseSync(path);\n\ttry {\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\tcreateSchema(database);\n\t\tconst version = readSchemaVersion(database);\n\t\tif (version > CURRENT_DURABLE_STATE_SCHEMA_VERSION) {\n\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\tthrow new Error(\n\t\t\t\t`Durable state database schema version ${version} is newer than this version (${CURRENT_DURABLE_STATE_SCHEMA_VERSION})`,\n\t\t\t);\n\t\t}\n\t\tif (version < 2) {\n\t\t\tconst commandColumns = database.prepare('PRAGMA table_info(\"command_journal\")').all() as Array<{\n\t\t\t\tname: string;\n\t\t\t}>;\n\t\t\tif (!commandColumns.some((column) => column.name === \"recovery_reason\"))\n\t\t\t\tdatabase.exec(\"ALTER TABLE command_journal ADD COLUMN recovery_reason TEXT\");\n\t\t}\n\t\tif (version < 3 && version > 0) {\n\t\t\tdatabase.exec(`\n\t\t\t\tALTER TABLE session_leases RENAME TO session_leases_v2;\n\t\t\t\tCREATE TABLE session_leases (\n\t\t\t\t\tsession_id TEXT NOT NULL,\n\t\t\t\t\towner_id TEXT NOT NULL,\n\t\t\t\t\tmode TEXT NOT NULL,\n\t\t\t\t\texpires_at TEXT NOT NULL,\n\t\t\t\t\tPRIMARY KEY (session_id, owner_id)\n\t\t\t\t);\n\t\t\t\tINSERT INTO session_leases (session_id, owner_id, mode, expires_at)\n\t\t\t\tSELECT session_id, owner_id, mode, expires_at FROM session_leases_v2;\n\t\t\t\tDROP TABLE session_leases_v2;\n\t\t\t`);\n\t\t}\n\t\tif (version < 4 && version > 0) {\n\t\t\tconst modelPerformanceColumns = database.prepare('PRAGMA table_info(\"model_performance\")').all() as Array<{\n\t\t\t\tname: string;\n\t\t\t}>;\n\t\t\tconst existingNames = new Set(modelPerformanceColumns.map((column) => column.name));\n\t\t\tfor (const [name, type] of MODEL_PERFORMANCE_V4_COLUMNS) {\n\t\t\t\tif (!existingNames.has(name)) database.exec(`ALTER TABLE model_performance ADD COLUMN ${name} ${type}`);\n\t\t\t}\n\t\t\tdatabase.exec(\"DROP TABLE IF EXISTS usage_totals\");\n\t\t}\n\t\tif (version < CURRENT_DURABLE_STATE_SCHEMA_VERSION) {\n\t\t\tdatabase\n\t\t\t\t.prepare(\"INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)\")\n\t\t\t\t.run(CURRENT_DURABLE_STATE_SCHEMA_VERSION, new Date().toISOString());\n\t\t}\n\t\tdatabase.exec(\"COMMIT\");\n\t} catch (error) {\n\t\ttry {\n\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t} catch {\n\t\t\t// Preserve the original migration error.\n\t\t}\n\t\tdatabase.close();\n\t\tthrow error;\n\t}\n\n\treturn {\n\t\tschemaVersion: () => readSchemaVersion(database),\n\t\ttableNames: () => {\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name\")\n\t\t\t\t.all() as Array<{ name: string }>;\n\t\t\treturn rows.map((row) => row.name);\n\t\t},\n\t\tcolumns: (tableName) => {\n\t\t\tif (!TABLES.includes(tableName as (typeof TABLES)[number]))\n\t\t\t\tthrow new Error(`Unknown durable-state table: ${tableName}`);\n\t\t\tconst rows = database.prepare(`PRAGMA table_info(\"${tableName}\")`).all() as Array<{ name: string }>;\n\t\t\treturn rows.map((row) => row.name);\n\t\t},\n\t\tbeginCommand: ({ id = randomUUID(), sessionId, command }) => {\n\t\t\tconst now = new Date().toISOString();\n\t\t\tdatabase\n\t\t\t\t.prepare(\n\t\t\t\t\t\"INSERT INTO command_journal (id, session_id, command, state, created_at, updated_at) VALUES (?, ?, ?, 'created', ?, ?)\",\n\t\t\t\t)\n\t\t\t\t.run(id, sessionId, command, now, now);\n\t\t\treturn readCommand(database, id)!;\n\t\t},\n\t\trunCommand: async (input, operation) => {\n\t\t\tconst entry = (() => {\n\t\t\t\tconst id = input.id ?? randomUUID();\n\t\t\t\tconst now = new Date().toISOString();\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT INTO command_journal (id, session_id, command, state, created_at, updated_at) VALUES (?, ?, ?, 'created', ?, ?)\",\n\t\t\t\t\t)\n\t\t\t\t\t.run(id, input.sessionId, input.command, now, now);\n\t\t\t\treturn readCommand(database, id)!;\n\t\t\t})();\n\t\t\ttransitionCommand(database, entry.id, \"running\");\n\t\t\ttry {\n\t\t\t\tconst result = await operation();\n\t\t\t\ttransitionCommand(database, entry.id, \"completed\");\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\ttransitionCommand(database, entry.id, \"failed\");\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\ttransitionCommand: (id, state, reason) => {\n\t\t\tconst current = readCommand(database, id);\n\t\t\tif (!current) throw new Error(`Unknown command journal entry: ${id}`);\n\t\t\tif (!COMMAND_TRANSITIONS[current.state].includes(state))\n\t\t\t\tthrow new Error(`Invalid command transition: ${current.state} -> ${state}`);\n\t\t\tconst now = new Date().toISOString();\n\t\t\tdatabase\n\t\t\t\t.prepare(\"UPDATE command_journal SET state = ?, recovery_reason = ?, updated_at = ? WHERE id = ?\")\n\t\t\t\t.run(state, reason ?? null, now, id);\n\t\t\treturn readCommand(database, id)!;\n\t\t},\n\t\tgetCommand: (id) => readCommand(database, id),\n\t\tcommandCount: () =>\n\t\t\t(database.prepare(\"SELECT COUNT(*) AS count FROM command_journal\").get() as { count: number }).count,\n\t\trecoverUnfinishedCommands: (reason = \"daemon restarted before command completion\") => {\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT id FROM command_journal WHERE state IN ('created', 'running')\")\n\t\t\t\t.all() as Array<{ id: string }>;\n\t\t\treturn rows.map(({ id }) => {\n\t\t\t\tconst current = readCommand(database, id)!;\n\t\t\t\treturn current.state === \"interrupted\" ? current : transitionCommand(database, id, \"interrupted\", reason);\n\t\t\t});\n\t\t},\n\t\trecoverUnfinishedCommandsWithDiagnostics: (reason = \"daemon restarted before command completion\") => {\n\t\t\tconst recoveredAt = new Date().toISOString();\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT id, session_id, state FROM command_journal WHERE state IN ('created', 'running')\")\n\t\t\t\t.all() as Array<{ id: string; session_id: string; state: \"created\" | \"running\" }>;\n\t\t\treturn rows.map((row) => {\n\t\t\t\ttransitionCommand(database, row.id, \"interrupted\", reason);\n\t\t\t\treturn {\n\t\t\t\t\tcommandId: row.id,\n\t\t\t\t\tsessionId: row.session_id,\n\t\t\t\t\tpreviousState: row.state,\n\t\t\t\t\tstate: \"interrupted\" as const,\n\t\t\t\t\treason,\n\t\t\t\t\trecoveredAt,\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\tacquireLease: ({ sessionId, ownerId, mode, ttlMs }) => {\n\t\t\tif (!Number.isFinite(ttlMs) || ttlMs <= 0) throw new Error(\"Lease TTL must be positive\");\n\t\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\t\ttry {\n\t\t\t\tdatabase.prepare(\"DELETE FROM session_leases WHERE expires_at <= ?\").run(new Date().toISOString());\n\t\t\t\tconst active = readLeases(database, sessionId).filter((lease) => lease.ownerId !== ownerId);\n\t\t\t\tif (active.some((lease) => lease.mode === \"exclusive\") || (mode === \"exclusive\" && active.length > 0)) {\n\t\t\t\t\tthrow new Error(`Session lease is held for ${sessionId}`);\n\t\t\t\t}\n\t\t\t\tconst expiresAt = new Date(Date.now() + ttlMs).toISOString();\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT INTO session_leases (session_id, owner_id, mode, expires_at) VALUES (?, ?, ?, ?) ON CONFLICT(session_id, owner_id) DO UPDATE SET mode=excluded.mode, expires_at=excluded.expires_at\",\n\t\t\t\t\t)\n\t\t\t\t\t.run(sessionId, ownerId, mode, expiresAt);\n\t\t\t\tdatabase.exec(\"COMMIT\");\n\t\t\t\treturn readLeases(database, sessionId).find((lease) => lease.ownerId === ownerId)!;\n\t\t\t} catch (error) {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\treleaseLease: (sessionId, ownerId) => {\n\t\t\tdatabase.prepare(\"DELETE FROM session_leases WHERE session_id = ? AND owner_id = ?\").run(sessionId, ownerId);\n\t\t},\n\t\tgetLease: (sessionId) => readLease(database, sessionId),\n\t\trecordUsagePerformance: (row) => {\n\t\t\tdatabase\n\t\t\t\t.prepare(\n\t\t\t\t\t`INSERT INTO model_performance (\n\t\t\t\t\t\tprovider, model_id, ttft_ms, generation_ms, sampled_at,\n\t\t\t\t\t\tsession_id, role, credential_identity, outcome, failure_kind,\n\t\t\t\t\t\tinput_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost\n\t\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t\t\t\t)\n\t\t\t\t.run(\n\t\t\t\t\trow.provider,\n\t\t\t\t\trow.modelId,\n\t\t\t\t\trow.ttftMs,\n\t\t\t\t\trow.generationMs,\n\t\t\t\t\trow.sampledAt,\n\t\t\t\t\trow.sessionId,\n\t\t\t\t\trow.role,\n\t\t\t\t\trow.credentialIdentity,\n\t\t\t\t\trow.outcome,\n\t\t\t\t\trow.failureKind,\n\t\t\t\t\trow.inputTokens,\n\t\t\t\t\trow.outputTokens,\n\t\t\t\t\trow.cacheReadTokens,\n\t\t\t\t\trow.cacheWriteTokens,\n\t\t\t\t\trow.cost,\n\t\t\t\t);\n\t\t},\n\t\tlistUsagePerformance: () =>\n\t\t\t(\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t`SELECT provider, model_id, ttft_ms, generation_ms, sampled_at, session_id, role,\n\t\t\t\t\t\t\tcredential_identity, outcome, failure_kind, input_tokens, output_tokens,\n\t\t\t\t\t\t\tcache_read_tokens, cache_write_tokens, cost\n\t\t\t\t\t\tFROM model_performance ORDER BY id ASC`,\n\t\t\t\t\t)\n\t\t\t\t\t.all() as Array<{\n\t\t\t\t\tprovider: string;\n\t\t\t\t\tmodel_id: string;\n\t\t\t\t\tttft_ms: number | null;\n\t\t\t\t\tgeneration_ms: number | null;\n\t\t\t\t\tsampled_at: string;\n\t\t\t\t\tsession_id: string | null;\n\t\t\t\t\trole: string | null;\n\t\t\t\t\tcredential_identity: string | null;\n\t\t\t\t\toutcome: string | null;\n\t\t\t\t\tfailure_kind: string | null;\n\t\t\t\t\tinput_tokens: number | null;\n\t\t\t\t\toutput_tokens: number | null;\n\t\t\t\t\tcache_read_tokens: number | null;\n\t\t\t\t\tcache_write_tokens: number | null;\n\t\t\t\t\tcost: number | null;\n\t\t\t\t}>\n\t\t\t).map((row) => ({\n\t\t\t\tprovider: row.provider,\n\t\t\t\tmodelId: row.model_id,\n\t\t\t\tttftMs: row.ttft_ms,\n\t\t\t\tgenerationMs: row.generation_ms,\n\t\t\t\tsampledAt: row.sampled_at,\n\t\t\t\tsessionId: row.session_id,\n\t\t\t\trole: row.role,\n\t\t\t\tcredentialIdentity: row.credential_identity,\n\t\t\t\toutcome: row.outcome,\n\t\t\t\tfailureKind: row.failure_kind,\n\t\t\t\tinputTokens: row.input_tokens,\n\t\t\t\toutputTokens: row.output_tokens,\n\t\t\t\tcacheReadTokens: row.cache_read_tokens,\n\t\t\t\tcacheWriteTokens: row.cache_write_tokens,\n\t\t\t\tcost: row.cost,\n\t\t\t})),\n\t\tpruneUsagePerformance: (olderThanIso) => {\n\t\t\tconst result = database.prepare(\"DELETE FROM model_performance WHERE sampled_at < ?\").run(olderThanIso);\n\t\t\treturn Number(result.changes);\n\t\t},\n\t\tclose: () => database.close(),\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../../src/core/durable-state/sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createRequire } from \"node:module\";\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/**\n * `bun build --compile` bundles this module into the standalone binary, and Bun does not\n * implement `node:sqlite` at all — not under `bun run`, not compiled. A static\n * `import { DatabaseSync } from \"node:sqlite\"` therefore breaks the standalone binary. Bun\n * ships its own `bun:sqlite`, whose `Database` exposes the same `.exec` / `.prepare(sql).get\n * /.all/.run` / `.close` surface this module relies on (verified directly against 1.3.14),\n * so the fix is to pick the binding at runtime rather than pull in either module statically.\n *\n * `createRequire` plus a runtime-computed specifier is load-bearing: it's what keeps this\n * `require(\"node:sqlite\")` call from being resolved when Bun bundles the compiled binary\n * (the branch is never taken under Bun, but the bundler would still try to resolve every\n * import it can see statically). A plain top-level `import` of either module — or a\n * dynamic `import()` with a literal specifier — does not get that exemption.\n */\nconst requireSqliteModule = createRequire(import.meta.url);\n\n/** The subset of `DatabaseSync` / `bun:sqlite`'s `Database` this module actually uses. */\ntype SqliteDatabaseConstructor = new (path: string) => DatabaseSync;\n\nfunction loadSqliteDatabaseConstructor(): SqliteDatabaseConstructor {\n\tif (typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\") {\n\t\tconst { Database } = requireSqliteModule(\"bun:sqlite\") as { Database: SqliteDatabaseConstructor };\n\t\treturn Database;\n\t}\n\tconst { DatabaseSync: NodeDatabaseSync } = requireSqliteModule(\"node:sqlite\") as {\n\t\tDatabaseSync: SqliteDatabaseConstructor;\n\t};\n\treturn NodeDatabaseSync;\n}\n\n/** Current schema for the daemon-owned durable-state sidecar. */\nexport const CURRENT_DURABLE_STATE_SCHEMA_VERSION = 4;\n\nexport type CommandJournalState = \"created\" | \"running\" | \"completed\" | \"failed\" | \"interrupted\";\n\nexport type SessionLeaseMode = \"shared\" | \"exclusive\";\nexport interface SessionLeaseRecord {\n\tsessionId: string;\n\townerId: string;\n\tmode: SessionLeaseMode;\n\texpiresAt: string;\n}\n\nexport interface CommandJournalRecord {\n\tid: string;\n\tsessionId: string;\n\tcommand: string;\n\tstate: CommandJournalState;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\trecoveryReason?: string;\n}\n\nconst COMMAND_TRANSITIONS: Record<CommandJournalState, readonly CommandJournalState[]> = {\n\tcreated: [\"running\", \"failed\", \"interrupted\"],\n\trunning: [\"completed\", \"failed\", \"interrupted\"],\n\tcompleted: [],\n\tfailed: [],\n\tinterrupted: [],\n};\n\nconst TABLES = [\n\t\"cache_entries\",\n\t\"command_journal\",\n\t\"daemon_metadata\",\n\t\"model_performance\",\n\t\"schema_migrations\",\n\t\"session_leases\",\n] as const;\n\n/** Columns added to `model_performance` in schema version 4 (roadmap Phase 8). */\nconst MODEL_PERFORMANCE_V4_COLUMNS: ReadonlyArray<readonly [string, string]> = [\n\t[\"session_id\", \"TEXT\"],\n\t[\"role\", \"TEXT\"],\n\t[\"credential_identity\", \"TEXT\"],\n\t[\"outcome\", \"TEXT\"],\n\t[\"failure_kind\", \"TEXT\"],\n\t[\"input_tokens\", \"INTEGER\"],\n\t[\"output_tokens\", \"INTEGER\"],\n\t[\"cache_read_tokens\", \"INTEGER\"],\n\t[\"cache_write_tokens\", \"INTEGER\"],\n\t[\"cost\", \"REAL\"],\n];\n\n/**\n * Plain, domain-independent row shape for `model_performance`. Deliberately not the\n * richer `UsagePerformanceSample` (which carries a branded `CredentialIdentity` and a\n * nested `usage` object) — this module owns SQLite schema and writes only, never\n * `ModelRuntime`'s domain types, to avoid a circular import between the two.\n */\nexport interface UsagePerformanceRow {\n\tprovider: string;\n\tmodelId: string;\n\tttftMs: number | null;\n\tgenerationMs: number | null;\n\tsampledAt: string;\n\tsessionId: string | null;\n\trole: string | null;\n\tcredentialIdentity: string | null;\n\toutcome: string | null;\n\tfailureKind: string | null;\n\tinputTokens: number | null;\n\toutputTokens: number | null;\n\tcacheReadTokens: number | null;\n\tcacheWriteTokens: number | null;\n\tcost: number | null;\n}\n\nexport interface RecoveryDiagnostic {\n\tcommandId: string;\n\tsessionId: string;\n\tpreviousState: \"created\" | \"running\";\n\tstate: \"interrupted\";\n\treason: string;\n\trecoveredAt: string;\n}\n\nexport interface DurableStateStore {\n\tschemaVersion(): number;\n\ttableNames(): string[];\n\tcolumns(tableName: string): string[];\n\tbeginCommand(input: { id?: string; sessionId: string; command: string }): CommandJournalRecord;\n\trunCommand<T>(input: { id?: string; sessionId: string; command: string }, operation: () => Promise<T>): Promise<T>;\n\ttransitionCommand(id: string, state: Exclude<CommandJournalState, \"created\">, reason?: string): CommandJournalRecord;\n\tgetCommand(id: string): CommandJournalRecord | undefined;\n\tcommandCount(): number;\n\trecoverUnfinishedCommands(reason?: string): CommandJournalRecord[];\n\trecoverUnfinishedCommandsWithDiagnostics(reason?: string): RecoveryDiagnostic[];\n\tacquireLease(input: {\n\t\tsessionId: string;\n\t\townerId: string;\n\t\tmode: SessionLeaseMode;\n\t\tttlMs: number;\n\t}): SessionLeaseRecord;\n\treleaseLease(sessionId: string, ownerId: string): void;\n\tgetLease(sessionId: string): SessionLeaseRecord | undefined;\n\trecordUsagePerformance(row: UsagePerformanceRow): void;\n\tlistUsagePerformance(): UsagePerformanceRow[];\n\tpruneUsagePerformance(olderThanIso: string): number;\n\tclose(): void;\n}\n\nfunction readSchemaVersion(database: DatabaseSync): number {\n\tconst row = database.prepare(\"SELECT COALESCE(MAX(version), 0) AS version FROM schema_migrations\").get() as\n\t\t| { version: number }\n\t\t| undefined;\n\treturn row?.version ?? 0;\n}\n\nfunction createSchema(database: DatabaseSync): void {\n\tdatabase.exec(`\n\t\tCREATE TABLE IF NOT EXISTS schema_migrations (\n\t\t\tversion INTEGER PRIMARY KEY,\n\t\t\tapplied_at TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS daemon_metadata (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS command_journal (\n\t\t\tid TEXT PRIMARY KEY,\n\t\t\tsession_id TEXT NOT NULL,\n\t\t\tcommand TEXT NOT NULL,\n\t\t\tstate TEXT NOT NULL,\n\t\t\trecovery_reason TEXT,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tupdated_at TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS session_leases (\n\t\t\tsession_id TEXT NOT NULL,\n\t\t\towner_id TEXT NOT NULL,\n\t\t\tmode TEXT NOT NULL,\n\t\t\texpires_at TEXT NOT NULL,\n\t\t\tPRIMARY KEY (session_id, owner_id)\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS model_performance (\n\t\t\tid INTEGER PRIMARY KEY,\n\t\t\tprovider TEXT NOT NULL,\n\t\t\tmodel_id TEXT NOT NULL,\n\t\t\tttft_ms REAL,\n\t\t\tgeneration_ms REAL,\n\t\t\tsampled_at TEXT NOT NULL,\n\t\t\tsession_id TEXT,\n\t\t\trole TEXT,\n\t\t\tcredential_identity TEXT,\n\t\t\toutcome TEXT,\n\t\t\tfailure_kind TEXT,\n\t\t\tinput_tokens INTEGER,\n\t\t\toutput_tokens INTEGER,\n\t\t\tcache_read_tokens INTEGER,\n\t\t\tcache_write_tokens INTEGER,\n\t\t\tcost REAL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS cache_entries (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue BLOB NOT NULL,\n\t\t\texpires_at TEXT\n\t\t);\n\t`);\n}\n\nfunction readCommand(database: DatabaseSync, id: string): CommandJournalRecord | undefined {\n\tconst row = database\n\t\t.prepare(\n\t\t\t\"SELECT id, session_id, command, state, recovery_reason, created_at, updated_at FROM command_journal WHERE id = ?\",\n\t\t)\n\t\t.get(id) as\n\t\t| {\n\t\t\t\tid: string;\n\t\t\t\tsession_id: string;\n\t\t\t\tcommand: string;\n\t\t\t\tstate: CommandJournalState;\n\t\t\t\trecovery_reason: string | null;\n\t\t\t\tcreated_at: string;\n\t\t\t\tupdated_at: string;\n\t\t }\n\t\t| undefined;\n\tif (!row) return undefined;\n\treturn {\n\t\tid: row.id,\n\t\tsessionId: row.session_id,\n\t\tcommand: row.command,\n\t\tstate: row.state,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t\t...(row.recovery_reason ? { recoveryReason: row.recovery_reason } : {}),\n\t};\n}\n\nfunction readLeases(database: DatabaseSync, sessionId: string): SessionLeaseRecord[] {\n\tconst rows = database\n\t\t.prepare(\n\t\t\t\"SELECT session_id, owner_id, mode, expires_at FROM session_leases WHERE session_id = ? ORDER BY owner_id\",\n\t\t)\n\t\t.all(sessionId) as Array<{ session_id: string; owner_id: string; mode: SessionLeaseMode; expires_at: string }>;\n\treturn rows.map((row) => ({\n\t\tsessionId: row.session_id,\n\t\townerId: row.owner_id,\n\t\tmode: row.mode,\n\t\texpiresAt: row.expires_at,\n\t}));\n}\n\nfunction readLease(database: DatabaseSync, sessionId: string): SessionLeaseRecord | undefined {\n\treturn readLeases(database, sessionId)[0];\n}\n\nfunction transitionCommand(\n\tdatabase: DatabaseSync,\n\tid: string,\n\tstate: Exclude<CommandJournalState, \"created\">,\n\treason?: string,\n): CommandJournalRecord {\n\tconst current = readCommand(database, id);\n\tif (!current) throw new Error(`Unknown command journal entry: ${id}`);\n\tif (!COMMAND_TRANSITIONS[current.state].includes(state))\n\t\tthrow new Error(`Invalid command transition: ${current.state} -> ${state}`);\n\tconst now = new Date().toISOString();\n\tdatabase\n\t\t.prepare(\"UPDATE command_journal SET state = ?, recovery_reason = ?, updated_at = ? WHERE id = ?\")\n\t\t.run(state, reason ?? null, now, id);\n\treturn readCommand(database, id)!;\n}\n\nexport function openDurableStateStore(path: string): DurableStateStore {\n\tconst Sqlite = loadSqliteDatabaseConstructor();\n\tconst database = new Sqlite(path);\n\ttry {\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\tcreateSchema(database);\n\t\tconst version = readSchemaVersion(database);\n\t\tif (version > CURRENT_DURABLE_STATE_SCHEMA_VERSION) {\n\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\tthrow new Error(\n\t\t\t\t`Durable state database schema version ${version} is newer than this version (${CURRENT_DURABLE_STATE_SCHEMA_VERSION})`,\n\t\t\t);\n\t\t}\n\t\tif (version < 2) {\n\t\t\tconst commandColumns = database.prepare('PRAGMA table_info(\"command_journal\")').all() as Array<{\n\t\t\t\tname: string;\n\t\t\t}>;\n\t\t\tif (!commandColumns.some((column) => column.name === \"recovery_reason\"))\n\t\t\t\tdatabase.exec(\"ALTER TABLE command_journal ADD COLUMN recovery_reason TEXT\");\n\t\t}\n\t\tif (version < 3 && version > 0) {\n\t\t\tdatabase.exec(`\n\t\t\t\tALTER TABLE session_leases RENAME TO session_leases_v2;\n\t\t\t\tCREATE TABLE session_leases (\n\t\t\t\t\tsession_id TEXT NOT NULL,\n\t\t\t\t\towner_id TEXT NOT NULL,\n\t\t\t\t\tmode TEXT NOT NULL,\n\t\t\t\t\texpires_at TEXT NOT NULL,\n\t\t\t\t\tPRIMARY KEY (session_id, owner_id)\n\t\t\t\t);\n\t\t\t\tINSERT INTO session_leases (session_id, owner_id, mode, expires_at)\n\t\t\t\tSELECT session_id, owner_id, mode, expires_at FROM session_leases_v2;\n\t\t\t\tDROP TABLE session_leases_v2;\n\t\t\t`);\n\t\t}\n\t\tif (version < 4 && version > 0) {\n\t\t\tconst modelPerformanceColumns = database.prepare('PRAGMA table_info(\"model_performance\")').all() as Array<{\n\t\t\t\tname: string;\n\t\t\t}>;\n\t\t\tconst existingNames = new Set(modelPerformanceColumns.map((column) => column.name));\n\t\t\tfor (const [name, type] of MODEL_PERFORMANCE_V4_COLUMNS) {\n\t\t\t\tif (!existingNames.has(name)) database.exec(`ALTER TABLE model_performance ADD COLUMN ${name} ${type}`);\n\t\t\t}\n\t\t\tdatabase.exec(\"DROP TABLE IF EXISTS usage_totals\");\n\t\t}\n\t\tif (version < CURRENT_DURABLE_STATE_SCHEMA_VERSION) {\n\t\t\tdatabase\n\t\t\t\t.prepare(\"INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)\")\n\t\t\t\t.run(CURRENT_DURABLE_STATE_SCHEMA_VERSION, new Date().toISOString());\n\t\t}\n\t\tdatabase.exec(\"COMMIT\");\n\t} catch (error) {\n\t\ttry {\n\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t} catch {\n\t\t\t// Preserve the original migration error.\n\t\t}\n\t\tdatabase.close();\n\t\tthrow error;\n\t}\n\n\treturn {\n\t\tschemaVersion: () => readSchemaVersion(database),\n\t\ttableNames: () => {\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name\")\n\t\t\t\t.all() as Array<{ name: string }>;\n\t\t\treturn rows.map((row) => row.name);\n\t\t},\n\t\tcolumns: (tableName) => {\n\t\t\tif (!TABLES.includes(tableName as (typeof TABLES)[number]))\n\t\t\t\tthrow new Error(`Unknown durable-state table: ${tableName}`);\n\t\t\tconst rows = database.prepare(`PRAGMA table_info(\"${tableName}\")`).all() as Array<{ name: string }>;\n\t\t\treturn rows.map((row) => row.name);\n\t\t},\n\t\tbeginCommand: ({ id = randomUUID(), sessionId, command }) => {\n\t\t\tconst now = new Date().toISOString();\n\t\t\tdatabase\n\t\t\t\t.prepare(\n\t\t\t\t\t\"INSERT INTO command_journal (id, session_id, command, state, created_at, updated_at) VALUES (?, ?, ?, 'created', ?, ?)\",\n\t\t\t\t)\n\t\t\t\t.run(id, sessionId, command, now, now);\n\t\t\treturn readCommand(database, id)!;\n\t\t},\n\t\trunCommand: async (input, operation) => {\n\t\t\tconst entry = (() => {\n\t\t\t\tconst id = input.id ?? randomUUID();\n\t\t\t\tconst now = new Date().toISOString();\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT INTO command_journal (id, session_id, command, state, created_at, updated_at) VALUES (?, ?, ?, 'created', ?, ?)\",\n\t\t\t\t\t)\n\t\t\t\t\t.run(id, input.sessionId, input.command, now, now);\n\t\t\t\treturn readCommand(database, id)!;\n\t\t\t})();\n\t\t\ttransitionCommand(database, entry.id, \"running\");\n\t\t\ttry {\n\t\t\t\tconst result = await operation();\n\t\t\t\ttransitionCommand(database, entry.id, \"completed\");\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\ttransitionCommand(database, entry.id, \"failed\");\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\ttransitionCommand: (id, state, reason) => {\n\t\t\tconst current = readCommand(database, id);\n\t\t\tif (!current) throw new Error(`Unknown command journal entry: ${id}`);\n\t\t\tif (!COMMAND_TRANSITIONS[current.state].includes(state))\n\t\t\t\tthrow new Error(`Invalid command transition: ${current.state} -> ${state}`);\n\t\t\tconst now = new Date().toISOString();\n\t\t\tdatabase\n\t\t\t\t.prepare(\"UPDATE command_journal SET state = ?, recovery_reason = ?, updated_at = ? WHERE id = ?\")\n\t\t\t\t.run(state, reason ?? null, now, id);\n\t\t\treturn readCommand(database, id)!;\n\t\t},\n\t\tgetCommand: (id) => readCommand(database, id),\n\t\tcommandCount: () =>\n\t\t\t(database.prepare(\"SELECT COUNT(*) AS count FROM command_journal\").get() as { count: number }).count,\n\t\trecoverUnfinishedCommands: (reason = \"daemon restarted before command completion\") => {\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT id FROM command_journal WHERE state IN ('created', 'running')\")\n\t\t\t\t.all() as Array<{ id: string }>;\n\t\t\treturn rows.map(({ id }) => {\n\t\t\t\tconst current = readCommand(database, id)!;\n\t\t\t\treturn current.state === \"interrupted\" ? current : transitionCommand(database, id, \"interrupted\", reason);\n\t\t\t});\n\t\t},\n\t\trecoverUnfinishedCommandsWithDiagnostics: (reason = \"daemon restarted before command completion\") => {\n\t\t\tconst recoveredAt = new Date().toISOString();\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT id, session_id, state FROM command_journal WHERE state IN ('created', 'running')\")\n\t\t\t\t.all() as Array<{ id: string; session_id: string; state: \"created\" | \"running\" }>;\n\t\t\treturn rows.map((row) => {\n\t\t\t\ttransitionCommand(database, row.id, \"interrupted\", reason);\n\t\t\t\treturn {\n\t\t\t\t\tcommandId: row.id,\n\t\t\t\t\tsessionId: row.session_id,\n\t\t\t\t\tpreviousState: row.state,\n\t\t\t\t\tstate: \"interrupted\" as const,\n\t\t\t\t\treason,\n\t\t\t\t\trecoveredAt,\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\tacquireLease: ({ sessionId, ownerId, mode, ttlMs }) => {\n\t\t\tif (!Number.isFinite(ttlMs) || ttlMs <= 0) throw new Error(\"Lease TTL must be positive\");\n\t\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\t\ttry {\n\t\t\t\tdatabase.prepare(\"DELETE FROM session_leases WHERE expires_at <= ?\").run(new Date().toISOString());\n\t\t\t\tconst active = readLeases(database, sessionId).filter((lease) => lease.ownerId !== ownerId);\n\t\t\t\tif (active.some((lease) => lease.mode === \"exclusive\") || (mode === \"exclusive\" && active.length > 0)) {\n\t\t\t\t\tthrow new Error(`Session lease is held for ${sessionId}`);\n\t\t\t\t}\n\t\t\t\tconst expiresAt = new Date(Date.now() + ttlMs).toISOString();\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT INTO session_leases (session_id, owner_id, mode, expires_at) VALUES (?, ?, ?, ?) ON CONFLICT(session_id, owner_id) DO UPDATE SET mode=excluded.mode, expires_at=excluded.expires_at\",\n\t\t\t\t\t)\n\t\t\t\t\t.run(sessionId, ownerId, mode, expiresAt);\n\t\t\t\tdatabase.exec(\"COMMIT\");\n\t\t\t\treturn readLeases(database, sessionId).find((lease) => lease.ownerId === ownerId)!;\n\t\t\t} catch (error) {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\treleaseLease: (sessionId, ownerId) => {\n\t\t\tdatabase.prepare(\"DELETE FROM session_leases WHERE session_id = ? AND owner_id = ?\").run(sessionId, ownerId);\n\t\t},\n\t\tgetLease: (sessionId) => readLease(database, sessionId),\n\t\trecordUsagePerformance: (row) => {\n\t\t\tdatabase\n\t\t\t\t.prepare(\n\t\t\t\t\t`INSERT INTO model_performance (\n\t\t\t\t\t\tprovider, model_id, ttft_ms, generation_ms, sampled_at,\n\t\t\t\t\t\tsession_id, role, credential_identity, outcome, failure_kind,\n\t\t\t\t\t\tinput_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost\n\t\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t\t\t\t)\n\t\t\t\t.run(\n\t\t\t\t\trow.provider,\n\t\t\t\t\trow.modelId,\n\t\t\t\t\trow.ttftMs,\n\t\t\t\t\trow.generationMs,\n\t\t\t\t\trow.sampledAt,\n\t\t\t\t\trow.sessionId,\n\t\t\t\t\trow.role,\n\t\t\t\t\trow.credentialIdentity,\n\t\t\t\t\trow.outcome,\n\t\t\t\t\trow.failureKind,\n\t\t\t\t\trow.inputTokens,\n\t\t\t\t\trow.outputTokens,\n\t\t\t\t\trow.cacheReadTokens,\n\t\t\t\t\trow.cacheWriteTokens,\n\t\t\t\t\trow.cost,\n\t\t\t\t);\n\t\t},\n\t\tlistUsagePerformance: () =>\n\t\t\t(\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t`SELECT provider, model_id, ttft_ms, generation_ms, sampled_at, session_id, role,\n\t\t\t\t\t\t\tcredential_identity, outcome, failure_kind, input_tokens, output_tokens,\n\t\t\t\t\t\t\tcache_read_tokens, cache_write_tokens, cost\n\t\t\t\t\t\tFROM model_performance ORDER BY id ASC`,\n\t\t\t\t\t)\n\t\t\t\t\t.all() as Array<{\n\t\t\t\t\tprovider: string;\n\t\t\t\t\tmodel_id: string;\n\t\t\t\t\tttft_ms: number | null;\n\t\t\t\t\tgeneration_ms: number | null;\n\t\t\t\t\tsampled_at: string;\n\t\t\t\t\tsession_id: string | null;\n\t\t\t\t\trole: string | null;\n\t\t\t\t\tcredential_identity: string | null;\n\t\t\t\t\toutcome: string | null;\n\t\t\t\t\tfailure_kind: string | null;\n\t\t\t\t\tinput_tokens: number | null;\n\t\t\t\t\toutput_tokens: number | null;\n\t\t\t\t\tcache_read_tokens: number | null;\n\t\t\t\t\tcache_write_tokens: number | null;\n\t\t\t\t\tcost: number | null;\n\t\t\t\t}>\n\t\t\t).map((row) => ({\n\t\t\t\tprovider: row.provider,\n\t\t\t\tmodelId: row.model_id,\n\t\t\t\tttftMs: row.ttft_ms,\n\t\t\t\tgenerationMs: row.generation_ms,\n\t\t\t\tsampledAt: row.sampled_at,\n\t\t\t\tsessionId: row.session_id,\n\t\t\t\trole: row.role,\n\t\t\t\tcredentialIdentity: row.credential_identity,\n\t\t\t\toutcome: row.outcome,\n\t\t\t\tfailureKind: row.failure_kind,\n\t\t\t\tinputTokens: row.input_tokens,\n\t\t\t\toutputTokens: row.output_tokens,\n\t\t\t\tcacheReadTokens: row.cache_read_tokens,\n\t\t\t\tcacheWriteTokens: row.cache_write_tokens,\n\t\t\t\tcost: row.cost,\n\t\t\t})),\n\t\tpruneUsagePerformance: (olderThanIso) => {\n\t\t\tconst result = database.prepare(\"DELETE FROM model_performance WHERE sampled_at < ?\").run(olderThanIso);\n\t\t\treturn Number(result.changes);\n\t\t},\n\t\tclose: () => database.close(),\n\t};\n}\n"]}
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
/**
|
|
4
|
+
* `bun build --compile` bundles this module into the standalone binary, and Bun does not
|
|
5
|
+
* implement `node:sqlite` at all — not under `bun run`, not compiled. A static
|
|
6
|
+
* `import { DatabaseSync } from "node:sqlite"` therefore breaks the standalone binary. Bun
|
|
7
|
+
* ships its own `bun:sqlite`, whose `Database` exposes the same `.exec` / `.prepare(sql).get
|
|
8
|
+
* /.all/.run` / `.close` surface this module relies on (verified directly against 1.3.14),
|
|
9
|
+
* so the fix is to pick the binding at runtime rather than pull in either module statically.
|
|
10
|
+
*
|
|
11
|
+
* `createRequire` plus a runtime-computed specifier is load-bearing: it's what keeps this
|
|
12
|
+
* `require("node:sqlite")` call from being resolved when Bun bundles the compiled binary
|
|
13
|
+
* (the branch is never taken under Bun, but the bundler would still try to resolve every
|
|
14
|
+
* import it can see statically). A plain top-level `import` of either module — or a
|
|
15
|
+
* dynamic `import()` with a literal specifier — does not get that exemption.
|
|
16
|
+
*/
|
|
17
|
+
const requireSqliteModule = createRequire(import.meta.url);
|
|
18
|
+
function loadSqliteDatabaseConstructor() {
|
|
19
|
+
if (typeof globalThis.Bun !== "undefined") {
|
|
20
|
+
const { Database } = requireSqliteModule("bun:sqlite");
|
|
21
|
+
return Database;
|
|
22
|
+
}
|
|
23
|
+
const { DatabaseSync: NodeDatabaseSync } = requireSqliteModule("node:sqlite");
|
|
24
|
+
return NodeDatabaseSync;
|
|
25
|
+
}
|
|
3
26
|
/** Current schema for the daemon-owned durable-state sidecar. */
|
|
4
27
|
export const CURRENT_DURABLE_STATE_SCHEMA_VERSION = 4;
|
|
5
28
|
const COMMAND_TRANSITIONS = {
|
|
@@ -128,7 +151,8 @@ function transitionCommand(database, id, state, reason) {
|
|
|
128
151
|
return readCommand(database, id);
|
|
129
152
|
}
|
|
130
153
|
export function openDurableStateStore(path) {
|
|
131
|
-
const
|
|
154
|
+
const Sqlite = loadSqliteDatabaseConstructor();
|
|
155
|
+
const database = new Sqlite(path);
|
|
132
156
|
try {
|
|
133
157
|
database.exec("BEGIN IMMEDIATE");
|
|
134
158
|
createSchema(database);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../../src/core/durable-state/sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { DatabaseSync } from \"node:sqlite\";\n\n/** Current schema for the daemon-owned durable-state sidecar. */\nexport const CURRENT_DURABLE_STATE_SCHEMA_VERSION = 4;\n\nexport type CommandJournalState = \"created\" | \"running\" | \"completed\" | \"failed\" | \"interrupted\";\n\nexport type SessionLeaseMode = \"shared\" | \"exclusive\";\nexport interface SessionLeaseRecord {\n\tsessionId: string;\n\townerId: string;\n\tmode: SessionLeaseMode;\n\texpiresAt: string;\n}\n\nexport interface CommandJournalRecord {\n\tid: string;\n\tsessionId: string;\n\tcommand: string;\n\tstate: CommandJournalState;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\trecoveryReason?: string;\n}\n\nconst COMMAND_TRANSITIONS: Record<CommandJournalState, readonly CommandJournalState[]> = {\n\tcreated: [\"running\", \"failed\", \"interrupted\"],\n\trunning: [\"completed\", \"failed\", \"interrupted\"],\n\tcompleted: [],\n\tfailed: [],\n\tinterrupted: [],\n};\n\nconst TABLES = [\n\t\"cache_entries\",\n\t\"command_journal\",\n\t\"daemon_metadata\",\n\t\"model_performance\",\n\t\"schema_migrations\",\n\t\"session_leases\",\n] as const;\n\n/** Columns added to `model_performance` in schema version 4 (roadmap Phase 8). */\nconst MODEL_PERFORMANCE_V4_COLUMNS: ReadonlyArray<readonly [string, string]> = [\n\t[\"session_id\", \"TEXT\"],\n\t[\"role\", \"TEXT\"],\n\t[\"credential_identity\", \"TEXT\"],\n\t[\"outcome\", \"TEXT\"],\n\t[\"failure_kind\", \"TEXT\"],\n\t[\"input_tokens\", \"INTEGER\"],\n\t[\"output_tokens\", \"INTEGER\"],\n\t[\"cache_read_tokens\", \"INTEGER\"],\n\t[\"cache_write_tokens\", \"INTEGER\"],\n\t[\"cost\", \"REAL\"],\n];\n\n/**\n * Plain, domain-independent row shape for `model_performance`. Deliberately not the\n * richer `UsagePerformanceSample` (which carries a branded `CredentialIdentity` and a\n * nested `usage` object) — this module owns SQLite schema and writes only, never\n * `ModelRuntime`'s domain types, to avoid a circular import between the two.\n */\nexport interface UsagePerformanceRow {\n\tprovider: string;\n\tmodelId: string;\n\tttftMs: number | null;\n\tgenerationMs: number | null;\n\tsampledAt: string;\n\tsessionId: string | null;\n\trole: string | null;\n\tcredentialIdentity: string | null;\n\toutcome: string | null;\n\tfailureKind: string | null;\n\tinputTokens: number | null;\n\toutputTokens: number | null;\n\tcacheReadTokens: number | null;\n\tcacheWriteTokens: number | null;\n\tcost: number | null;\n}\n\nexport interface RecoveryDiagnostic {\n\tcommandId: string;\n\tsessionId: string;\n\tpreviousState: \"created\" | \"running\";\n\tstate: \"interrupted\";\n\treason: string;\n\trecoveredAt: string;\n}\n\nexport interface DurableStateStore {\n\tschemaVersion(): number;\n\ttableNames(): string[];\n\tcolumns(tableName: string): string[];\n\tbeginCommand(input: { id?: string; sessionId: string; command: string }): CommandJournalRecord;\n\trunCommand<T>(input: { id?: string; sessionId: string; command: string }, operation: () => Promise<T>): Promise<T>;\n\ttransitionCommand(id: string, state: Exclude<CommandJournalState, \"created\">, reason?: string): CommandJournalRecord;\n\tgetCommand(id: string): CommandJournalRecord | undefined;\n\tcommandCount(): number;\n\trecoverUnfinishedCommands(reason?: string): CommandJournalRecord[];\n\trecoverUnfinishedCommandsWithDiagnostics(reason?: string): RecoveryDiagnostic[];\n\tacquireLease(input: {\n\t\tsessionId: string;\n\t\townerId: string;\n\t\tmode: SessionLeaseMode;\n\t\tttlMs: number;\n\t}): SessionLeaseRecord;\n\treleaseLease(sessionId: string, ownerId: string): void;\n\tgetLease(sessionId: string): SessionLeaseRecord | undefined;\n\trecordUsagePerformance(row: UsagePerformanceRow): void;\n\tlistUsagePerformance(): UsagePerformanceRow[];\n\tpruneUsagePerformance(olderThanIso: string): number;\n\tclose(): void;\n}\n\nfunction readSchemaVersion(database: DatabaseSync): number {\n\tconst row = database.prepare(\"SELECT COALESCE(MAX(version), 0) AS version FROM schema_migrations\").get() as\n\t\t| { version: number }\n\t\t| undefined;\n\treturn row?.version ?? 0;\n}\n\nfunction createSchema(database: DatabaseSync): void {\n\tdatabase.exec(`\n\t\tCREATE TABLE IF NOT EXISTS schema_migrations (\n\t\t\tversion INTEGER PRIMARY KEY,\n\t\t\tapplied_at TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS daemon_metadata (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS command_journal (\n\t\t\tid TEXT PRIMARY KEY,\n\t\t\tsession_id TEXT NOT NULL,\n\t\t\tcommand TEXT NOT NULL,\n\t\t\tstate TEXT NOT NULL,\n\t\t\trecovery_reason TEXT,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tupdated_at TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS session_leases (\n\t\t\tsession_id TEXT NOT NULL,\n\t\t\towner_id TEXT NOT NULL,\n\t\t\tmode TEXT NOT NULL,\n\t\t\texpires_at TEXT NOT NULL,\n\t\t\tPRIMARY KEY (session_id, owner_id)\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS model_performance (\n\t\t\tid INTEGER PRIMARY KEY,\n\t\t\tprovider TEXT NOT NULL,\n\t\t\tmodel_id TEXT NOT NULL,\n\t\t\tttft_ms REAL,\n\t\t\tgeneration_ms REAL,\n\t\t\tsampled_at TEXT NOT NULL,\n\t\t\tsession_id TEXT,\n\t\t\trole TEXT,\n\t\t\tcredential_identity TEXT,\n\t\t\toutcome TEXT,\n\t\t\tfailure_kind TEXT,\n\t\t\tinput_tokens INTEGER,\n\t\t\toutput_tokens INTEGER,\n\t\t\tcache_read_tokens INTEGER,\n\t\t\tcache_write_tokens INTEGER,\n\t\t\tcost REAL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS cache_entries (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue BLOB NOT NULL,\n\t\t\texpires_at TEXT\n\t\t);\n\t`);\n}\n\nfunction readCommand(database: DatabaseSync, id: string): CommandJournalRecord | undefined {\n\tconst row = database\n\t\t.prepare(\n\t\t\t\"SELECT id, session_id, command, state, recovery_reason, created_at, updated_at FROM command_journal WHERE id = ?\",\n\t\t)\n\t\t.get(id) as\n\t\t| {\n\t\t\t\tid: string;\n\t\t\t\tsession_id: string;\n\t\t\t\tcommand: string;\n\t\t\t\tstate: CommandJournalState;\n\t\t\t\trecovery_reason: string | null;\n\t\t\t\tcreated_at: string;\n\t\t\t\tupdated_at: string;\n\t\t }\n\t\t| undefined;\n\tif (!row) return undefined;\n\treturn {\n\t\tid: row.id,\n\t\tsessionId: row.session_id,\n\t\tcommand: row.command,\n\t\tstate: row.state,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t\t...(row.recovery_reason ? { recoveryReason: row.recovery_reason } : {}),\n\t};\n}\n\nfunction readLeases(database: DatabaseSync, sessionId: string): SessionLeaseRecord[] {\n\tconst rows = database\n\t\t.prepare(\n\t\t\t\"SELECT session_id, owner_id, mode, expires_at FROM session_leases WHERE session_id = ? ORDER BY owner_id\",\n\t\t)\n\t\t.all(sessionId) as Array<{ session_id: string; owner_id: string; mode: SessionLeaseMode; expires_at: string }>;\n\treturn rows.map((row) => ({\n\t\tsessionId: row.session_id,\n\t\townerId: row.owner_id,\n\t\tmode: row.mode,\n\t\texpiresAt: row.expires_at,\n\t}));\n}\n\nfunction readLease(database: DatabaseSync, sessionId: string): SessionLeaseRecord | undefined {\n\treturn readLeases(database, sessionId)[0];\n}\n\nfunction transitionCommand(\n\tdatabase: DatabaseSync,\n\tid: string,\n\tstate: Exclude<CommandJournalState, \"created\">,\n\treason?: string,\n): CommandJournalRecord {\n\tconst current = readCommand(database, id);\n\tif (!current) throw new Error(`Unknown command journal entry: ${id}`);\n\tif (!COMMAND_TRANSITIONS[current.state].includes(state))\n\t\tthrow new Error(`Invalid command transition: ${current.state} -> ${state}`);\n\tconst now = new Date().toISOString();\n\tdatabase\n\t\t.prepare(\"UPDATE command_journal SET state = ?, recovery_reason = ?, updated_at = ? WHERE id = ?\")\n\t\t.run(state, reason ?? null, now, id);\n\treturn readCommand(database, id)!;\n}\n\nexport function openDurableStateStore(path: string): DurableStateStore {\n\tconst database = new DatabaseSync(path);\n\ttry {\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\tcreateSchema(database);\n\t\tconst version = readSchemaVersion(database);\n\t\tif (version > CURRENT_DURABLE_STATE_SCHEMA_VERSION) {\n\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\tthrow new Error(\n\t\t\t\t`Durable state database schema version ${version} is newer than this version (${CURRENT_DURABLE_STATE_SCHEMA_VERSION})`,\n\t\t\t);\n\t\t}\n\t\tif (version < 2) {\n\t\t\tconst commandColumns = database.prepare('PRAGMA table_info(\"command_journal\")').all() as Array<{\n\t\t\t\tname: string;\n\t\t\t}>;\n\t\t\tif (!commandColumns.some((column) => column.name === \"recovery_reason\"))\n\t\t\t\tdatabase.exec(\"ALTER TABLE command_journal ADD COLUMN recovery_reason TEXT\");\n\t\t}\n\t\tif (version < 3 && version > 0) {\n\t\t\tdatabase.exec(`\n\t\t\t\tALTER TABLE session_leases RENAME TO session_leases_v2;\n\t\t\t\tCREATE TABLE session_leases (\n\t\t\t\t\tsession_id TEXT NOT NULL,\n\t\t\t\t\towner_id TEXT NOT NULL,\n\t\t\t\t\tmode TEXT NOT NULL,\n\t\t\t\t\texpires_at TEXT NOT NULL,\n\t\t\t\t\tPRIMARY KEY (session_id, owner_id)\n\t\t\t\t);\n\t\t\t\tINSERT INTO session_leases (session_id, owner_id, mode, expires_at)\n\t\t\t\tSELECT session_id, owner_id, mode, expires_at FROM session_leases_v2;\n\t\t\t\tDROP TABLE session_leases_v2;\n\t\t\t`);\n\t\t}\n\t\tif (version < 4 && version > 0) {\n\t\t\tconst modelPerformanceColumns = database.prepare('PRAGMA table_info(\"model_performance\")').all() as Array<{\n\t\t\t\tname: string;\n\t\t\t}>;\n\t\t\tconst existingNames = new Set(modelPerformanceColumns.map((column) => column.name));\n\t\t\tfor (const [name, type] of MODEL_PERFORMANCE_V4_COLUMNS) {\n\t\t\t\tif (!existingNames.has(name)) database.exec(`ALTER TABLE model_performance ADD COLUMN ${name} ${type}`);\n\t\t\t}\n\t\t\tdatabase.exec(\"DROP TABLE IF EXISTS usage_totals\");\n\t\t}\n\t\tif (version < CURRENT_DURABLE_STATE_SCHEMA_VERSION) {\n\t\t\tdatabase\n\t\t\t\t.prepare(\"INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)\")\n\t\t\t\t.run(CURRENT_DURABLE_STATE_SCHEMA_VERSION, new Date().toISOString());\n\t\t}\n\t\tdatabase.exec(\"COMMIT\");\n\t} catch (error) {\n\t\ttry {\n\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t} catch {\n\t\t\t// Preserve the original migration error.\n\t\t}\n\t\tdatabase.close();\n\t\tthrow error;\n\t}\n\n\treturn {\n\t\tschemaVersion: () => readSchemaVersion(database),\n\t\ttableNames: () => {\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name\")\n\t\t\t\t.all() as Array<{ name: string }>;\n\t\t\treturn rows.map((row) => row.name);\n\t\t},\n\t\tcolumns: (tableName) => {\n\t\t\tif (!TABLES.includes(tableName as (typeof TABLES)[number]))\n\t\t\t\tthrow new Error(`Unknown durable-state table: ${tableName}`);\n\t\t\tconst rows = database.prepare(`PRAGMA table_info(\"${tableName}\")`).all() as Array<{ name: string }>;\n\t\t\treturn rows.map((row) => row.name);\n\t\t},\n\t\tbeginCommand: ({ id = randomUUID(), sessionId, command }) => {\n\t\t\tconst now = new Date().toISOString();\n\t\t\tdatabase\n\t\t\t\t.prepare(\n\t\t\t\t\t\"INSERT INTO command_journal (id, session_id, command, state, created_at, updated_at) VALUES (?, ?, ?, 'created', ?, ?)\",\n\t\t\t\t)\n\t\t\t\t.run(id, sessionId, command, now, now);\n\t\t\treturn readCommand(database, id)!;\n\t\t},\n\t\trunCommand: async (input, operation) => {\n\t\t\tconst entry = (() => {\n\t\t\t\tconst id = input.id ?? randomUUID();\n\t\t\t\tconst now = new Date().toISOString();\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT INTO command_journal (id, session_id, command, state, created_at, updated_at) VALUES (?, ?, ?, 'created', ?, ?)\",\n\t\t\t\t\t)\n\t\t\t\t\t.run(id, input.sessionId, input.command, now, now);\n\t\t\t\treturn readCommand(database, id)!;\n\t\t\t})();\n\t\t\ttransitionCommand(database, entry.id, \"running\");\n\t\t\ttry {\n\t\t\t\tconst result = await operation();\n\t\t\t\ttransitionCommand(database, entry.id, \"completed\");\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\ttransitionCommand(database, entry.id, \"failed\");\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\ttransitionCommand: (id, state, reason) => {\n\t\t\tconst current = readCommand(database, id);\n\t\t\tif (!current) throw new Error(`Unknown command journal entry: ${id}`);\n\t\t\tif (!COMMAND_TRANSITIONS[current.state].includes(state))\n\t\t\t\tthrow new Error(`Invalid command transition: ${current.state} -> ${state}`);\n\t\t\tconst now = new Date().toISOString();\n\t\t\tdatabase\n\t\t\t\t.prepare(\"UPDATE command_journal SET state = ?, recovery_reason = ?, updated_at = ? WHERE id = ?\")\n\t\t\t\t.run(state, reason ?? null, now, id);\n\t\t\treturn readCommand(database, id)!;\n\t\t},\n\t\tgetCommand: (id) => readCommand(database, id),\n\t\tcommandCount: () =>\n\t\t\t(database.prepare(\"SELECT COUNT(*) AS count FROM command_journal\").get() as { count: number }).count,\n\t\trecoverUnfinishedCommands: (reason = \"daemon restarted before command completion\") => {\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT id FROM command_journal WHERE state IN ('created', 'running')\")\n\t\t\t\t.all() as Array<{ id: string }>;\n\t\t\treturn rows.map(({ id }) => {\n\t\t\t\tconst current = readCommand(database, id)!;\n\t\t\t\treturn current.state === \"interrupted\" ? current : transitionCommand(database, id, \"interrupted\", reason);\n\t\t\t});\n\t\t},\n\t\trecoverUnfinishedCommandsWithDiagnostics: (reason = \"daemon restarted before command completion\") => {\n\t\t\tconst recoveredAt = new Date().toISOString();\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT id, session_id, state FROM command_journal WHERE state IN ('created', 'running')\")\n\t\t\t\t.all() as Array<{ id: string; session_id: string; state: \"created\" | \"running\" }>;\n\t\t\treturn rows.map((row) => {\n\t\t\t\ttransitionCommand(database, row.id, \"interrupted\", reason);\n\t\t\t\treturn {\n\t\t\t\t\tcommandId: row.id,\n\t\t\t\t\tsessionId: row.session_id,\n\t\t\t\t\tpreviousState: row.state,\n\t\t\t\t\tstate: \"interrupted\" as const,\n\t\t\t\t\treason,\n\t\t\t\t\trecoveredAt,\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\tacquireLease: ({ sessionId, ownerId, mode, ttlMs }) => {\n\t\t\tif (!Number.isFinite(ttlMs) || ttlMs <= 0) throw new Error(\"Lease TTL must be positive\");\n\t\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\t\ttry {\n\t\t\t\tdatabase.prepare(\"DELETE FROM session_leases WHERE expires_at <= ?\").run(new Date().toISOString());\n\t\t\t\tconst active = readLeases(database, sessionId).filter((lease) => lease.ownerId !== ownerId);\n\t\t\t\tif (active.some((lease) => lease.mode === \"exclusive\") || (mode === \"exclusive\" && active.length > 0)) {\n\t\t\t\t\tthrow new Error(`Session lease is held for ${sessionId}`);\n\t\t\t\t}\n\t\t\t\tconst expiresAt = new Date(Date.now() + ttlMs).toISOString();\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT INTO session_leases (session_id, owner_id, mode, expires_at) VALUES (?, ?, ?, ?) ON CONFLICT(session_id, owner_id) DO UPDATE SET mode=excluded.mode, expires_at=excluded.expires_at\",\n\t\t\t\t\t)\n\t\t\t\t\t.run(sessionId, ownerId, mode, expiresAt);\n\t\t\t\tdatabase.exec(\"COMMIT\");\n\t\t\t\treturn readLeases(database, sessionId).find((lease) => lease.ownerId === ownerId)!;\n\t\t\t} catch (error) {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\treleaseLease: (sessionId, ownerId) => {\n\t\t\tdatabase.prepare(\"DELETE FROM session_leases WHERE session_id = ? AND owner_id = ?\").run(sessionId, ownerId);\n\t\t},\n\t\tgetLease: (sessionId) => readLease(database, sessionId),\n\t\trecordUsagePerformance: (row) => {\n\t\t\tdatabase\n\t\t\t\t.prepare(\n\t\t\t\t\t`INSERT INTO model_performance (\n\t\t\t\t\t\tprovider, model_id, ttft_ms, generation_ms, sampled_at,\n\t\t\t\t\t\tsession_id, role, credential_identity, outcome, failure_kind,\n\t\t\t\t\t\tinput_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost\n\t\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t\t\t\t)\n\t\t\t\t.run(\n\t\t\t\t\trow.provider,\n\t\t\t\t\trow.modelId,\n\t\t\t\t\trow.ttftMs,\n\t\t\t\t\trow.generationMs,\n\t\t\t\t\trow.sampledAt,\n\t\t\t\t\trow.sessionId,\n\t\t\t\t\trow.role,\n\t\t\t\t\trow.credentialIdentity,\n\t\t\t\t\trow.outcome,\n\t\t\t\t\trow.failureKind,\n\t\t\t\t\trow.inputTokens,\n\t\t\t\t\trow.outputTokens,\n\t\t\t\t\trow.cacheReadTokens,\n\t\t\t\t\trow.cacheWriteTokens,\n\t\t\t\t\trow.cost,\n\t\t\t\t);\n\t\t},\n\t\tlistUsagePerformance: () =>\n\t\t\t(\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t`SELECT provider, model_id, ttft_ms, generation_ms, sampled_at, session_id, role,\n\t\t\t\t\t\t\tcredential_identity, outcome, failure_kind, input_tokens, output_tokens,\n\t\t\t\t\t\t\tcache_read_tokens, cache_write_tokens, cost\n\t\t\t\t\t\tFROM model_performance ORDER BY id ASC`,\n\t\t\t\t\t)\n\t\t\t\t\t.all() as Array<{\n\t\t\t\t\tprovider: string;\n\t\t\t\t\tmodel_id: string;\n\t\t\t\t\tttft_ms: number | null;\n\t\t\t\t\tgeneration_ms: number | null;\n\t\t\t\t\tsampled_at: string;\n\t\t\t\t\tsession_id: string | null;\n\t\t\t\t\trole: string | null;\n\t\t\t\t\tcredential_identity: string | null;\n\t\t\t\t\toutcome: string | null;\n\t\t\t\t\tfailure_kind: string | null;\n\t\t\t\t\tinput_tokens: number | null;\n\t\t\t\t\toutput_tokens: number | null;\n\t\t\t\t\tcache_read_tokens: number | null;\n\t\t\t\t\tcache_write_tokens: number | null;\n\t\t\t\t\tcost: number | null;\n\t\t\t\t}>\n\t\t\t).map((row) => ({\n\t\t\t\tprovider: row.provider,\n\t\t\t\tmodelId: row.model_id,\n\t\t\t\tttftMs: row.ttft_ms,\n\t\t\t\tgenerationMs: row.generation_ms,\n\t\t\t\tsampledAt: row.sampled_at,\n\t\t\t\tsessionId: row.session_id,\n\t\t\t\trole: row.role,\n\t\t\t\tcredentialIdentity: row.credential_identity,\n\t\t\t\toutcome: row.outcome,\n\t\t\t\tfailureKind: row.failure_kind,\n\t\t\t\tinputTokens: row.input_tokens,\n\t\t\t\toutputTokens: row.output_tokens,\n\t\t\t\tcacheReadTokens: row.cache_read_tokens,\n\t\t\t\tcacheWriteTokens: row.cache_write_tokens,\n\t\t\t\tcost: row.cost,\n\t\t\t})),\n\t\tpruneUsagePerformance: (olderThanIso) => {\n\t\t\tconst result = database.prepare(\"DELETE FROM model_performance WHERE sampled_at < ?\").run(olderThanIso);\n\t\t\treturn Number(result.changes);\n\t\t},\n\t\tclose: () => database.close(),\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../../src/core/durable-state/sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createRequire } from \"node:module\";\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/**\n * `bun build --compile` bundles this module into the standalone binary, and Bun does not\n * implement `node:sqlite` at all — not under `bun run`, not compiled. A static\n * `import { DatabaseSync } from \"node:sqlite\"` therefore breaks the standalone binary. Bun\n * ships its own `bun:sqlite`, whose `Database` exposes the same `.exec` / `.prepare(sql).get\n * /.all/.run` / `.close` surface this module relies on (verified directly against 1.3.14),\n * so the fix is to pick the binding at runtime rather than pull in either module statically.\n *\n * `createRequire` plus a runtime-computed specifier is load-bearing: it's what keeps this\n * `require(\"node:sqlite\")` call from being resolved when Bun bundles the compiled binary\n * (the branch is never taken under Bun, but the bundler would still try to resolve every\n * import it can see statically). A plain top-level `import` of either module — or a\n * dynamic `import()` with a literal specifier — does not get that exemption.\n */\nconst requireSqliteModule = createRequire(import.meta.url);\n\n/** The subset of `DatabaseSync` / `bun:sqlite`'s `Database` this module actually uses. */\ntype SqliteDatabaseConstructor = new (path: string) => DatabaseSync;\n\nfunction loadSqliteDatabaseConstructor(): SqliteDatabaseConstructor {\n\tif (typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\") {\n\t\tconst { Database } = requireSqliteModule(\"bun:sqlite\") as { Database: SqliteDatabaseConstructor };\n\t\treturn Database;\n\t}\n\tconst { DatabaseSync: NodeDatabaseSync } = requireSqliteModule(\"node:sqlite\") as {\n\t\tDatabaseSync: SqliteDatabaseConstructor;\n\t};\n\treturn NodeDatabaseSync;\n}\n\n/** Current schema for the daemon-owned durable-state sidecar. */\nexport const CURRENT_DURABLE_STATE_SCHEMA_VERSION = 4;\n\nexport type CommandJournalState = \"created\" | \"running\" | \"completed\" | \"failed\" | \"interrupted\";\n\nexport type SessionLeaseMode = \"shared\" | \"exclusive\";\nexport interface SessionLeaseRecord {\n\tsessionId: string;\n\townerId: string;\n\tmode: SessionLeaseMode;\n\texpiresAt: string;\n}\n\nexport interface CommandJournalRecord {\n\tid: string;\n\tsessionId: string;\n\tcommand: string;\n\tstate: CommandJournalState;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\trecoveryReason?: string;\n}\n\nconst COMMAND_TRANSITIONS: Record<CommandJournalState, readonly CommandJournalState[]> = {\n\tcreated: [\"running\", \"failed\", \"interrupted\"],\n\trunning: [\"completed\", \"failed\", \"interrupted\"],\n\tcompleted: [],\n\tfailed: [],\n\tinterrupted: [],\n};\n\nconst TABLES = [\n\t\"cache_entries\",\n\t\"command_journal\",\n\t\"daemon_metadata\",\n\t\"model_performance\",\n\t\"schema_migrations\",\n\t\"session_leases\",\n] as const;\n\n/** Columns added to `model_performance` in schema version 4 (roadmap Phase 8). */\nconst MODEL_PERFORMANCE_V4_COLUMNS: ReadonlyArray<readonly [string, string]> = [\n\t[\"session_id\", \"TEXT\"],\n\t[\"role\", \"TEXT\"],\n\t[\"credential_identity\", \"TEXT\"],\n\t[\"outcome\", \"TEXT\"],\n\t[\"failure_kind\", \"TEXT\"],\n\t[\"input_tokens\", \"INTEGER\"],\n\t[\"output_tokens\", \"INTEGER\"],\n\t[\"cache_read_tokens\", \"INTEGER\"],\n\t[\"cache_write_tokens\", \"INTEGER\"],\n\t[\"cost\", \"REAL\"],\n];\n\n/**\n * Plain, domain-independent row shape for `model_performance`. Deliberately not the\n * richer `UsagePerformanceSample` (which carries a branded `CredentialIdentity` and a\n * nested `usage` object) — this module owns SQLite schema and writes only, never\n * `ModelRuntime`'s domain types, to avoid a circular import between the two.\n */\nexport interface UsagePerformanceRow {\n\tprovider: string;\n\tmodelId: string;\n\tttftMs: number | null;\n\tgenerationMs: number | null;\n\tsampledAt: string;\n\tsessionId: string | null;\n\trole: string | null;\n\tcredentialIdentity: string | null;\n\toutcome: string | null;\n\tfailureKind: string | null;\n\tinputTokens: number | null;\n\toutputTokens: number | null;\n\tcacheReadTokens: number | null;\n\tcacheWriteTokens: number | null;\n\tcost: number | null;\n}\n\nexport interface RecoveryDiagnostic {\n\tcommandId: string;\n\tsessionId: string;\n\tpreviousState: \"created\" | \"running\";\n\tstate: \"interrupted\";\n\treason: string;\n\trecoveredAt: string;\n}\n\nexport interface DurableStateStore {\n\tschemaVersion(): number;\n\ttableNames(): string[];\n\tcolumns(tableName: string): string[];\n\tbeginCommand(input: { id?: string; sessionId: string; command: string }): CommandJournalRecord;\n\trunCommand<T>(input: { id?: string; sessionId: string; command: string }, operation: () => Promise<T>): Promise<T>;\n\ttransitionCommand(id: string, state: Exclude<CommandJournalState, \"created\">, reason?: string): CommandJournalRecord;\n\tgetCommand(id: string): CommandJournalRecord | undefined;\n\tcommandCount(): number;\n\trecoverUnfinishedCommands(reason?: string): CommandJournalRecord[];\n\trecoverUnfinishedCommandsWithDiagnostics(reason?: string): RecoveryDiagnostic[];\n\tacquireLease(input: {\n\t\tsessionId: string;\n\t\townerId: string;\n\t\tmode: SessionLeaseMode;\n\t\tttlMs: number;\n\t}): SessionLeaseRecord;\n\treleaseLease(sessionId: string, ownerId: string): void;\n\tgetLease(sessionId: string): SessionLeaseRecord | undefined;\n\trecordUsagePerformance(row: UsagePerformanceRow): void;\n\tlistUsagePerformance(): UsagePerformanceRow[];\n\tpruneUsagePerformance(olderThanIso: string): number;\n\tclose(): void;\n}\n\nfunction readSchemaVersion(database: DatabaseSync): number {\n\tconst row = database.prepare(\"SELECT COALESCE(MAX(version), 0) AS version FROM schema_migrations\").get() as\n\t\t| { version: number }\n\t\t| undefined;\n\treturn row?.version ?? 0;\n}\n\nfunction createSchema(database: DatabaseSync): void {\n\tdatabase.exec(`\n\t\tCREATE TABLE IF NOT EXISTS schema_migrations (\n\t\t\tversion INTEGER PRIMARY KEY,\n\t\t\tapplied_at TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS daemon_metadata (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS command_journal (\n\t\t\tid TEXT PRIMARY KEY,\n\t\t\tsession_id TEXT NOT NULL,\n\t\t\tcommand TEXT NOT NULL,\n\t\t\tstate TEXT NOT NULL,\n\t\t\trecovery_reason TEXT,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tupdated_at TEXT NOT NULL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS session_leases (\n\t\t\tsession_id TEXT NOT NULL,\n\t\t\towner_id TEXT NOT NULL,\n\t\t\tmode TEXT NOT NULL,\n\t\t\texpires_at TEXT NOT NULL,\n\t\t\tPRIMARY KEY (session_id, owner_id)\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS model_performance (\n\t\t\tid INTEGER PRIMARY KEY,\n\t\t\tprovider TEXT NOT NULL,\n\t\t\tmodel_id TEXT NOT NULL,\n\t\t\tttft_ms REAL,\n\t\t\tgeneration_ms REAL,\n\t\t\tsampled_at TEXT NOT NULL,\n\t\t\tsession_id TEXT,\n\t\t\trole TEXT,\n\t\t\tcredential_identity TEXT,\n\t\t\toutcome TEXT,\n\t\t\tfailure_kind TEXT,\n\t\t\tinput_tokens INTEGER,\n\t\t\toutput_tokens INTEGER,\n\t\t\tcache_read_tokens INTEGER,\n\t\t\tcache_write_tokens INTEGER,\n\t\t\tcost REAL\n\t\t);\n\t\tCREATE TABLE IF NOT EXISTS cache_entries (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue BLOB NOT NULL,\n\t\t\texpires_at TEXT\n\t\t);\n\t`);\n}\n\nfunction readCommand(database: DatabaseSync, id: string): CommandJournalRecord | undefined {\n\tconst row = database\n\t\t.prepare(\n\t\t\t\"SELECT id, session_id, command, state, recovery_reason, created_at, updated_at FROM command_journal WHERE id = ?\",\n\t\t)\n\t\t.get(id) as\n\t\t| {\n\t\t\t\tid: string;\n\t\t\t\tsession_id: string;\n\t\t\t\tcommand: string;\n\t\t\t\tstate: CommandJournalState;\n\t\t\t\trecovery_reason: string | null;\n\t\t\t\tcreated_at: string;\n\t\t\t\tupdated_at: string;\n\t\t }\n\t\t| undefined;\n\tif (!row) return undefined;\n\treturn {\n\t\tid: row.id,\n\t\tsessionId: row.session_id,\n\t\tcommand: row.command,\n\t\tstate: row.state,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t\t...(row.recovery_reason ? { recoveryReason: row.recovery_reason } : {}),\n\t};\n}\n\nfunction readLeases(database: DatabaseSync, sessionId: string): SessionLeaseRecord[] {\n\tconst rows = database\n\t\t.prepare(\n\t\t\t\"SELECT session_id, owner_id, mode, expires_at FROM session_leases WHERE session_id = ? ORDER BY owner_id\",\n\t\t)\n\t\t.all(sessionId) as Array<{ session_id: string; owner_id: string; mode: SessionLeaseMode; expires_at: string }>;\n\treturn rows.map((row) => ({\n\t\tsessionId: row.session_id,\n\t\townerId: row.owner_id,\n\t\tmode: row.mode,\n\t\texpiresAt: row.expires_at,\n\t}));\n}\n\nfunction readLease(database: DatabaseSync, sessionId: string): SessionLeaseRecord | undefined {\n\treturn readLeases(database, sessionId)[0];\n}\n\nfunction transitionCommand(\n\tdatabase: DatabaseSync,\n\tid: string,\n\tstate: Exclude<CommandJournalState, \"created\">,\n\treason?: string,\n): CommandJournalRecord {\n\tconst current = readCommand(database, id);\n\tif (!current) throw new Error(`Unknown command journal entry: ${id}`);\n\tif (!COMMAND_TRANSITIONS[current.state].includes(state))\n\t\tthrow new Error(`Invalid command transition: ${current.state} -> ${state}`);\n\tconst now = new Date().toISOString();\n\tdatabase\n\t\t.prepare(\"UPDATE command_journal SET state = ?, recovery_reason = ?, updated_at = ? WHERE id = ?\")\n\t\t.run(state, reason ?? null, now, id);\n\treturn readCommand(database, id)!;\n}\n\nexport function openDurableStateStore(path: string): DurableStateStore {\n\tconst Sqlite = loadSqliteDatabaseConstructor();\n\tconst database = new Sqlite(path);\n\ttry {\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\tcreateSchema(database);\n\t\tconst version = readSchemaVersion(database);\n\t\tif (version > CURRENT_DURABLE_STATE_SCHEMA_VERSION) {\n\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\tthrow new Error(\n\t\t\t\t`Durable state database schema version ${version} is newer than this version (${CURRENT_DURABLE_STATE_SCHEMA_VERSION})`,\n\t\t\t);\n\t\t}\n\t\tif (version < 2) {\n\t\t\tconst commandColumns = database.prepare('PRAGMA table_info(\"command_journal\")').all() as Array<{\n\t\t\t\tname: string;\n\t\t\t}>;\n\t\t\tif (!commandColumns.some((column) => column.name === \"recovery_reason\"))\n\t\t\t\tdatabase.exec(\"ALTER TABLE command_journal ADD COLUMN recovery_reason TEXT\");\n\t\t}\n\t\tif (version < 3 && version > 0) {\n\t\t\tdatabase.exec(`\n\t\t\t\tALTER TABLE session_leases RENAME TO session_leases_v2;\n\t\t\t\tCREATE TABLE session_leases (\n\t\t\t\t\tsession_id TEXT NOT NULL,\n\t\t\t\t\towner_id TEXT NOT NULL,\n\t\t\t\t\tmode TEXT NOT NULL,\n\t\t\t\t\texpires_at TEXT NOT NULL,\n\t\t\t\t\tPRIMARY KEY (session_id, owner_id)\n\t\t\t\t);\n\t\t\t\tINSERT INTO session_leases (session_id, owner_id, mode, expires_at)\n\t\t\t\tSELECT session_id, owner_id, mode, expires_at FROM session_leases_v2;\n\t\t\t\tDROP TABLE session_leases_v2;\n\t\t\t`);\n\t\t}\n\t\tif (version < 4 && version > 0) {\n\t\t\tconst modelPerformanceColumns = database.prepare('PRAGMA table_info(\"model_performance\")').all() as Array<{\n\t\t\t\tname: string;\n\t\t\t}>;\n\t\t\tconst existingNames = new Set(modelPerformanceColumns.map((column) => column.name));\n\t\t\tfor (const [name, type] of MODEL_PERFORMANCE_V4_COLUMNS) {\n\t\t\t\tif (!existingNames.has(name)) database.exec(`ALTER TABLE model_performance ADD COLUMN ${name} ${type}`);\n\t\t\t}\n\t\t\tdatabase.exec(\"DROP TABLE IF EXISTS usage_totals\");\n\t\t}\n\t\tif (version < CURRENT_DURABLE_STATE_SCHEMA_VERSION) {\n\t\t\tdatabase\n\t\t\t\t.prepare(\"INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)\")\n\t\t\t\t.run(CURRENT_DURABLE_STATE_SCHEMA_VERSION, new Date().toISOString());\n\t\t}\n\t\tdatabase.exec(\"COMMIT\");\n\t} catch (error) {\n\t\ttry {\n\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t} catch {\n\t\t\t// Preserve the original migration error.\n\t\t}\n\t\tdatabase.close();\n\t\tthrow error;\n\t}\n\n\treturn {\n\t\tschemaVersion: () => readSchemaVersion(database),\n\t\ttableNames: () => {\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name\")\n\t\t\t\t.all() as Array<{ name: string }>;\n\t\t\treturn rows.map((row) => row.name);\n\t\t},\n\t\tcolumns: (tableName) => {\n\t\t\tif (!TABLES.includes(tableName as (typeof TABLES)[number]))\n\t\t\t\tthrow new Error(`Unknown durable-state table: ${tableName}`);\n\t\t\tconst rows = database.prepare(`PRAGMA table_info(\"${tableName}\")`).all() as Array<{ name: string }>;\n\t\t\treturn rows.map((row) => row.name);\n\t\t},\n\t\tbeginCommand: ({ id = randomUUID(), sessionId, command }) => {\n\t\t\tconst now = new Date().toISOString();\n\t\t\tdatabase\n\t\t\t\t.prepare(\n\t\t\t\t\t\"INSERT INTO command_journal (id, session_id, command, state, created_at, updated_at) VALUES (?, ?, ?, 'created', ?, ?)\",\n\t\t\t\t)\n\t\t\t\t.run(id, sessionId, command, now, now);\n\t\t\treturn readCommand(database, id)!;\n\t\t},\n\t\trunCommand: async (input, operation) => {\n\t\t\tconst entry = (() => {\n\t\t\t\tconst id = input.id ?? randomUUID();\n\t\t\t\tconst now = new Date().toISOString();\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT INTO command_journal (id, session_id, command, state, created_at, updated_at) VALUES (?, ?, ?, 'created', ?, ?)\",\n\t\t\t\t\t)\n\t\t\t\t\t.run(id, input.sessionId, input.command, now, now);\n\t\t\t\treturn readCommand(database, id)!;\n\t\t\t})();\n\t\t\ttransitionCommand(database, entry.id, \"running\");\n\t\t\ttry {\n\t\t\t\tconst result = await operation();\n\t\t\t\ttransitionCommand(database, entry.id, \"completed\");\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\ttransitionCommand(database, entry.id, \"failed\");\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\ttransitionCommand: (id, state, reason) => {\n\t\t\tconst current = readCommand(database, id);\n\t\t\tif (!current) throw new Error(`Unknown command journal entry: ${id}`);\n\t\t\tif (!COMMAND_TRANSITIONS[current.state].includes(state))\n\t\t\t\tthrow new Error(`Invalid command transition: ${current.state} -> ${state}`);\n\t\t\tconst now = new Date().toISOString();\n\t\t\tdatabase\n\t\t\t\t.prepare(\"UPDATE command_journal SET state = ?, recovery_reason = ?, updated_at = ? WHERE id = ?\")\n\t\t\t\t.run(state, reason ?? null, now, id);\n\t\t\treturn readCommand(database, id)!;\n\t\t},\n\t\tgetCommand: (id) => readCommand(database, id),\n\t\tcommandCount: () =>\n\t\t\t(database.prepare(\"SELECT COUNT(*) AS count FROM command_journal\").get() as { count: number }).count,\n\t\trecoverUnfinishedCommands: (reason = \"daemon restarted before command completion\") => {\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT id FROM command_journal WHERE state IN ('created', 'running')\")\n\t\t\t\t.all() as Array<{ id: string }>;\n\t\t\treturn rows.map(({ id }) => {\n\t\t\t\tconst current = readCommand(database, id)!;\n\t\t\t\treturn current.state === \"interrupted\" ? current : transitionCommand(database, id, \"interrupted\", reason);\n\t\t\t});\n\t\t},\n\t\trecoverUnfinishedCommandsWithDiagnostics: (reason = \"daemon restarted before command completion\") => {\n\t\t\tconst recoveredAt = new Date().toISOString();\n\t\t\tconst rows = database\n\t\t\t\t.prepare(\"SELECT id, session_id, state FROM command_journal WHERE state IN ('created', 'running')\")\n\t\t\t\t.all() as Array<{ id: string; session_id: string; state: \"created\" | \"running\" }>;\n\t\t\treturn rows.map((row) => {\n\t\t\t\ttransitionCommand(database, row.id, \"interrupted\", reason);\n\t\t\t\treturn {\n\t\t\t\t\tcommandId: row.id,\n\t\t\t\t\tsessionId: row.session_id,\n\t\t\t\t\tpreviousState: row.state,\n\t\t\t\t\tstate: \"interrupted\" as const,\n\t\t\t\t\treason,\n\t\t\t\t\trecoveredAt,\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\tacquireLease: ({ sessionId, ownerId, mode, ttlMs }) => {\n\t\t\tif (!Number.isFinite(ttlMs) || ttlMs <= 0) throw new Error(\"Lease TTL must be positive\");\n\t\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\t\ttry {\n\t\t\t\tdatabase.prepare(\"DELETE FROM session_leases WHERE expires_at <= ?\").run(new Date().toISOString());\n\t\t\t\tconst active = readLeases(database, sessionId).filter((lease) => lease.ownerId !== ownerId);\n\t\t\t\tif (active.some((lease) => lease.mode === \"exclusive\") || (mode === \"exclusive\" && active.length > 0)) {\n\t\t\t\t\tthrow new Error(`Session lease is held for ${sessionId}`);\n\t\t\t\t}\n\t\t\t\tconst expiresAt = new Date(Date.now() + ttlMs).toISOString();\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT INTO session_leases (session_id, owner_id, mode, expires_at) VALUES (?, ?, ?, ?) ON CONFLICT(session_id, owner_id) DO UPDATE SET mode=excluded.mode, expires_at=excluded.expires_at\",\n\t\t\t\t\t)\n\t\t\t\t\t.run(sessionId, ownerId, mode, expiresAt);\n\t\t\t\tdatabase.exec(\"COMMIT\");\n\t\t\t\treturn readLeases(database, sessionId).find((lease) => lease.ownerId === ownerId)!;\n\t\t\t} catch (error) {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\treleaseLease: (sessionId, ownerId) => {\n\t\t\tdatabase.prepare(\"DELETE FROM session_leases WHERE session_id = ? AND owner_id = ?\").run(sessionId, ownerId);\n\t\t},\n\t\tgetLease: (sessionId) => readLease(database, sessionId),\n\t\trecordUsagePerformance: (row) => {\n\t\t\tdatabase\n\t\t\t\t.prepare(\n\t\t\t\t\t`INSERT INTO model_performance (\n\t\t\t\t\t\tprovider, model_id, ttft_ms, generation_ms, sampled_at,\n\t\t\t\t\t\tsession_id, role, credential_identity, outcome, failure_kind,\n\t\t\t\t\t\tinput_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost\n\t\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t\t\t\t)\n\t\t\t\t.run(\n\t\t\t\t\trow.provider,\n\t\t\t\t\trow.modelId,\n\t\t\t\t\trow.ttftMs,\n\t\t\t\t\trow.generationMs,\n\t\t\t\t\trow.sampledAt,\n\t\t\t\t\trow.sessionId,\n\t\t\t\t\trow.role,\n\t\t\t\t\trow.credentialIdentity,\n\t\t\t\t\trow.outcome,\n\t\t\t\t\trow.failureKind,\n\t\t\t\t\trow.inputTokens,\n\t\t\t\t\trow.outputTokens,\n\t\t\t\t\trow.cacheReadTokens,\n\t\t\t\t\trow.cacheWriteTokens,\n\t\t\t\t\trow.cost,\n\t\t\t\t);\n\t\t},\n\t\tlistUsagePerformance: () =>\n\t\t\t(\n\t\t\t\tdatabase\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t`SELECT provider, model_id, ttft_ms, generation_ms, sampled_at, session_id, role,\n\t\t\t\t\t\t\tcredential_identity, outcome, failure_kind, input_tokens, output_tokens,\n\t\t\t\t\t\t\tcache_read_tokens, cache_write_tokens, cost\n\t\t\t\t\t\tFROM model_performance ORDER BY id ASC`,\n\t\t\t\t\t)\n\t\t\t\t\t.all() as Array<{\n\t\t\t\t\tprovider: string;\n\t\t\t\t\tmodel_id: string;\n\t\t\t\t\tttft_ms: number | null;\n\t\t\t\t\tgeneration_ms: number | null;\n\t\t\t\t\tsampled_at: string;\n\t\t\t\t\tsession_id: string | null;\n\t\t\t\t\trole: string | null;\n\t\t\t\t\tcredential_identity: string | null;\n\t\t\t\t\toutcome: string | null;\n\t\t\t\t\tfailure_kind: string | null;\n\t\t\t\t\tinput_tokens: number | null;\n\t\t\t\t\toutput_tokens: number | null;\n\t\t\t\t\tcache_read_tokens: number | null;\n\t\t\t\t\tcache_write_tokens: number | null;\n\t\t\t\t\tcost: number | null;\n\t\t\t\t}>\n\t\t\t).map((row) => ({\n\t\t\t\tprovider: row.provider,\n\t\t\t\tmodelId: row.model_id,\n\t\t\t\tttftMs: row.ttft_ms,\n\t\t\t\tgenerationMs: row.generation_ms,\n\t\t\t\tsampledAt: row.sampled_at,\n\t\t\t\tsessionId: row.session_id,\n\t\t\t\trole: row.role,\n\t\t\t\tcredentialIdentity: row.credential_identity,\n\t\t\t\toutcome: row.outcome,\n\t\t\t\tfailureKind: row.failure_kind,\n\t\t\t\tinputTokens: row.input_tokens,\n\t\t\t\toutputTokens: row.output_tokens,\n\t\t\t\tcacheReadTokens: row.cache_read_tokens,\n\t\t\t\tcacheWriteTokens: row.cache_write_tokens,\n\t\t\t\tcost: row.cost,\n\t\t\t})),\n\t\tpruneUsagePerformance: (olderThanIso) => {\n\t\t\tconst result = database.prepare(\"DELETE FROM model_performance WHERE sampled_at < ?\").run(olderThanIso);\n\t\t\treturn Number(result.changes);\n\t\t},\n\t\tclose: () => database.close(),\n\t};\n}\n"]}
|
|
@@ -6,7 +6,7 @@ export type { SourceInfo } from "../source-info.ts";
|
|
|
6
6
|
export { createExtensionRuntime, discoverAndLoadExtensions, loadExtensionFromFactory, loadExtensions, } from "./loader.ts";
|
|
7
7
|
export type { ExtensionErrorListener, ForkHandler, NavigateTreeHandler, NewSessionHandler, ShutdownHandler, SwitchSessionHandler, } from "./runner.ts";
|
|
8
8
|
export { ExtensionRunner } from "./runner.ts";
|
|
9
|
-
export type { AfterProviderResponseEvent, AgentEndEvent, AgentSettledEvent, AgentStartEvent, AgentToolResult, AgentToolUpdateCallback, AppendEntryHandler, AppKeybinding, AutocompleteProviderFactory, BashToolCallEvent, BashToolResultEvent, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderHeadersEvent, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, BuildSystemPromptOptions, CompactOptions, ContextEvent, ContextEventResult, ContextUsage, CustomToolCallEvent, CustomToolResultEvent, EditorFactory, EditToolCallEvent, EditToolResultEvent, EntryRenderer, EntryRenderOptions, ExecOptions, ExecResult, Extension, ExtensionActions, ExtensionAPI, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFactory, ExtensionFlag, ExtensionHandler, ExtensionMode, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetOptions, FindToolCallEvent, FindToolResultEvent, GetActiveToolsHandler, GetAllToolsHandler, GetCommandsHandler, GetThinkingLevelHandler, GrepToolCallEvent, GrepToolResultEvent, InlineExtension, InputEvent, InputEventResult, InputSource, KeybindingsManager, LoadExtensionsResult, LsToolCallEvent, LsToolResultEvent, MarkdownTransformContext, MarkdownTransformer, MessageEndEvent, MessageRenderer, MessageRenderOptions, MessageStartEvent, MessageUpdateEvent, ModelSelectEvent, ModelSelectSource, ProjectTrustContext, ProjectTrustEvent, ProjectTrustEventDecision, ProjectTrustEventResult, ProjectTrustHandler, ProviderConfig, ProviderModelConfig, ReadToolCallEvent, ReadToolResultEvent, RegisteredCommand, RegisteredTool, ReplacedSessionContext, ResolvedCommand, ResourcesDiscoverEvent, ResourcesDiscoverResult, SendMessageHandler, SendUserMessageHandler, SessionBeforeCompactEvent, SessionBeforeCompactResult, SessionBeforeForkEvent, SessionBeforeForkResult, SessionBeforeSwitchEvent, SessionBeforeSwitchResult, SessionBeforeTreeEvent, SessionBeforeTreeResult, SessionCompactEvent, SessionEvent, SessionInfoChangedEvent, SessionShutdownEvent, SessionStartEvent, SessionTreeEvent, SetActiveToolsHandler, SetLabelHandler, SetModelHandler, SetThinkingLevelHandler, TerminalInputHandler, ToolCallEvent, ToolCallEventResult, ToolDefinition, ToolExecutionEndEvent, ToolExecutionMode, ToolExecutionStartEvent, ToolExecutionUpdateEvent, ToolInfo, ToolRenderResultOptions, ToolResultEvent, ToolResultEventResult, TreePreparation, TurnEndEvent, TurnStartEvent, UserBashEvent, UserBashEventResult, WidgetPlacement, WorkingIndicatorOptions, WriteToolCallEvent, WriteToolResultEvent, } from "./types.ts";
|
|
10
|
-
export { defineTool, isBashToolResult, isEditToolResult, isFindToolResult, isGrepToolResult, isLsToolResult, isReadToolResult, isToolCallEventType, isWriteToolResult, } from "./types.ts";
|
|
9
|
+
export type { AfterProviderResponseEvent, AgentEndEvent, AgentSettledEvent, AgentStartEvent, AgentToolResult, AgentToolUpdateCallback, AppendEntryHandler, AppKeybinding, AutocompleteProviderFactory, BashToolCallEvent, BashToolResultEvent, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderHeadersEvent, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, BuildSystemPromptOptions, CompactOptions, ContextEvent, ContextEventResult, ContextUsage, CustomToolCallEvent, CustomToolResultEvent, EditorFactory, EditToolCallEvent, EditToolResultEvent, EntryRenderer, EntryRenderOptions, ExecOptions, ExecResult, Extension, ExtensionActions, ExtensionAPI, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFactory, ExtensionFlag, ExtensionHandler, ExtensionMode, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetOptions, FindToolCallEvent, FindToolResultEvent, GetActiveToolsHandler, GetAllToolsHandler, GetCommandsHandler, GetThinkingLevelHandler, GrepToolCallEvent, GrepToolResultEvent, InlineExtension, InputEvent, InputEventResult, InputSource, KeybindingsManager, LoadExtensionsResult, LsToolCallEvent, LsToolResultEvent, MarkdownTransformContext, MarkdownTransformer, MessageEndEvent, MessageRenderer, MessageRenderOptions, MessageStartEvent, MessageUpdateEvent, ModelSelectEvent, ModelSelectSource, PowerShellToolCallEvent, PowerShellToolResultEvent, ProjectTrustContext, ProjectTrustEvent, ProjectTrustEventDecision, ProjectTrustEventResult, ProjectTrustHandler, ProviderConfig, ProviderModelConfig, ReadToolCallEvent, ReadToolResultEvent, RegisteredCommand, RegisteredTool, ReplacedSessionContext, ResolvedCommand, ResourcesDiscoverEvent, ResourcesDiscoverResult, SendMessageHandler, SendUserMessageHandler, SessionBeforeCompactEvent, SessionBeforeCompactResult, SessionBeforeForkEvent, SessionBeforeForkResult, SessionBeforeSwitchEvent, SessionBeforeSwitchResult, SessionBeforeTreeEvent, SessionBeforeTreeResult, SessionCompactEvent, SessionCompactFailedEvent, SessionEvent, SessionInfoChangedEvent, SessionShutdownEvent, SessionStartEvent, SessionTreeEvent, SetActiveToolsHandler, SetLabelHandler, SetModelHandler, SetThinkingLevelHandler, TerminalInputHandler, ToolCallEvent, ToolCallEventResult, ToolDefinition, ToolExecutionEndEvent, ToolExecutionMode, ToolExecutionStartEvent, ToolExecutionUpdateEvent, ToolInfo, ToolRenderResultOptions, ToolResultEvent, ToolResultEventResult, TreePreparation, TurnEndEvent, TurnStartEvent, UserBashEvent, UserBashEventResult, WidgetPlacement, WorkingIndicatorOptions, WriteToolCallEvent, WriteToolResultEvent, } from "./types.ts";
|
|
10
|
+
export { defineTool, isBashToolResult, isEditToolResult, isFindToolResult, isGrepToolResult, isLsToolResult, isPowerShellToolResult, isReadToolResult, isToolCallEventType, isWriteToolResult, } from "./types.ts";
|
|
11
11
|
export { wrapRegisteredTool, wrapRegisteredTools } from "./wrapper.ts";
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EACN,sBAAsB,EACtB,yBAAyB,EACzB,wBAAwB,EACxB,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EACX,sBAAsB,EACtB,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EACX,0BAA0B,EAC1B,aAAa,EACb,iBAAiB,EACjB,eAAe,EAEf,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAElB,aAAa,EACb,2BAA2B,EAE3B,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,EAC1B,gCAAgC,EAChC,wBAAwB,EAExB,cAAc,EAEd,YAAY,EAEZ,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EAEnB,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,SAAS,EACT,gBAAgB,EAEhB,YAAY,EACZ,uBAAuB,EACvB,8BAA8B,EAC9B,gBAAgB,EAChB,uBAAuB,EAEvB,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,aAAa,EAEb,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EAEf,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EAEnB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,EAEnB,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EAEnB,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,eAAe,EAEf,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EAEpB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EAEpB,aAAa,EACb,mBAAmB,EAEnB,cAAc,EAEd,qBAAqB,EAErB,iBAAiB,EACjB,uBAAuB,EACvB,wBAAwB,EACxB,QAAQ,EACR,uBAAuB,EACvB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,cAAc,EAEd,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACN,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC","sourcesContent":["/**\n * Extension system for lifecycle events and custom tools.\n */\n\nexport type { SlashCommandInfo, SlashCommandSource } from \"../slash-commands.ts\";\nexport type { SourceInfo } from \"../source-info.ts\";\nexport {\n\tcreateExtensionRuntime,\n\tdiscoverAndLoadExtensions,\n\tloadExtensionFromFactory,\n\tloadExtensions,\n} from \"./loader.ts\";\nexport type {\n\tExtensionErrorListener,\n\tForkHandler,\n\tNavigateTreeHandler,\n\tNewSessionHandler,\n\tShutdownHandler,\n\tSwitchSessionHandler,\n} from \"./runner.ts\";\nexport { ExtensionRunner } from \"./runner.ts\";\nexport type {\n\tAfterProviderResponseEvent,\n\tAgentEndEvent,\n\tAgentSettledEvent,\n\tAgentStartEvent,\n\t// Re-exports\n\tAgentToolResult,\n\tAgentToolUpdateCallback,\n\tAppendEntryHandler,\n\t// App keybindings (for custom editors)\n\tAppKeybinding,\n\tAutocompleteProviderFactory,\n\t// Events - Tool (ToolCallEvent types)\n\tBashToolCallEvent,\n\tBashToolResultEvent,\n\tBeforeAgentStartEvent,\n\tBeforeAgentStartEventResult,\n\tBeforeProviderHeadersEvent,\n\tBeforeProviderRequestEvent,\n\tBeforeProviderRequestEventResult,\n\tBuildSystemPromptOptions,\n\t// Context\n\tCompactOptions,\n\t// Events - Agent\n\tContextEvent,\n\t// Event Results\n\tContextEventResult,\n\tContextUsage,\n\tCustomToolCallEvent,\n\tCustomToolResultEvent,\n\tEditorFactory,\n\tEditToolCallEvent,\n\tEditToolResultEvent,\n\t// Message and Entry Rendering\n\tEntryRenderer,\n\tEntryRenderOptions,\n\tExecOptions,\n\tExecResult,\n\tExtension,\n\tExtensionActions,\n\t// API\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionCommandContextActions,\n\tExtensionContext,\n\tExtensionContextActions,\n\t// Errors\n\tExtensionError,\n\tExtensionEvent,\n\tExtensionFactory,\n\tExtensionFlag,\n\tExtensionHandler,\n\tExtensionMode,\n\t// Runtime\n\tExtensionRuntime,\n\tExtensionShortcut,\n\tExtensionUIContext,\n\tExtensionUIDialogOptions,\n\tExtensionWidgetOptions,\n\tFindToolCallEvent,\n\tFindToolResultEvent,\n\tGetActiveToolsHandler,\n\tGetAllToolsHandler,\n\tGetCommandsHandler,\n\tGetThinkingLevelHandler,\n\tGrepToolCallEvent,\n\tGrepToolResultEvent,\n\tInlineExtension,\n\t// Events - Input\n\tInputEvent,\n\tInputEventResult,\n\tInputSource,\n\tKeybindingsManager,\n\tLoadExtensionsResult,\n\tLsToolCallEvent,\n\tLsToolResultEvent,\n\tMarkdownTransformContext,\n\tMarkdownTransformer,\n\t// Events - Message\n\tMessageEndEvent,\n\tMessageRenderer,\n\tMessageRenderOptions,\n\tMessageStartEvent,\n\tMessageUpdateEvent,\n\tModelSelectEvent,\n\tModelSelectSource,\n\tProjectTrustContext,\n\tProjectTrustEvent,\n\tProjectTrustEventDecision,\n\tProjectTrustEventResult,\n\tProjectTrustHandler,\n\t// Provider Registration\n\tProviderConfig,\n\tProviderModelConfig,\n\tReadToolCallEvent,\n\tReadToolResultEvent,\n\t// Commands\n\tRegisteredCommand,\n\tRegisteredTool,\n\tReplacedSessionContext,\n\tResolvedCommand,\n\t// Events - Resources\n\tResourcesDiscoverEvent,\n\tResourcesDiscoverResult,\n\tSendMessageHandler,\n\tSendUserMessageHandler,\n\tSessionBeforeCompactEvent,\n\tSessionBeforeCompactResult,\n\tSessionBeforeForkEvent,\n\tSessionBeforeForkResult,\n\tSessionBeforeSwitchEvent,\n\tSessionBeforeSwitchResult,\n\tSessionBeforeTreeEvent,\n\tSessionBeforeTreeResult,\n\tSessionCompactEvent,\n\tSessionEvent,\n\tSessionInfoChangedEvent,\n\tSessionShutdownEvent,\n\t// Events - Session\n\tSessionStartEvent,\n\tSessionTreeEvent,\n\tSetActiveToolsHandler,\n\tSetLabelHandler,\n\tSetModelHandler,\n\tSetThinkingLevelHandler,\n\tTerminalInputHandler,\n\t// Events - Tool\n\tToolCallEvent,\n\tToolCallEventResult,\n\t// Tools\n\tToolDefinition,\n\t// Events - Tool Execution\n\tToolExecutionEndEvent,\n\t// Tool execution mode\n\tToolExecutionMode,\n\tToolExecutionStartEvent,\n\tToolExecutionUpdateEvent,\n\tToolInfo,\n\tToolRenderResultOptions,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTreePreparation,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\t// Events - User Bash\n\tUserBashEvent,\n\tUserBashEventResult,\n\tWidgetPlacement,\n\tWorkingIndicatorOptions,\n\tWriteToolCallEvent,\n\tWriteToolResultEvent,\n} from \"./types.ts\";\n// Type guards\nexport {\n\tdefineTool,\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisToolCallEventType,\n\tisWriteToolResult,\n} from \"./types.ts\";\nexport { wrapRegisteredTool, wrapRegisteredTools } from \"./wrapper.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extension system for lifecycle events and custom tools.\n */\n\nexport type { SlashCommandInfo, SlashCommandSource } from \"../slash-commands.ts\";\nexport type { SourceInfo } from \"../source-info.ts\";\nexport {\n\tcreateExtensionRuntime,\n\tdiscoverAndLoadExtensions,\n\tloadExtensionFromFactory,\n\tloadExtensions,\n} from \"./loader.ts\";\nexport type {\n\tExtensionErrorListener,\n\tForkHandler,\n\tNavigateTreeHandler,\n\tNewSessionHandler,\n\tShutdownHandler,\n\tSwitchSessionHandler,\n} from \"./runner.ts\";\nexport { ExtensionRunner } from \"./runner.ts\";\nexport type {\n\tAfterProviderResponseEvent,\n\tAgentEndEvent,\n\tAgentSettledEvent,\n\tAgentStartEvent,\n\t// Re-exports\n\tAgentToolResult,\n\tAgentToolUpdateCallback,\n\tAppendEntryHandler,\n\t// App keybindings (for custom editors)\n\tAppKeybinding,\n\tAutocompleteProviderFactory,\n\t// Events - Tool (ToolCallEvent types)\n\tBashToolCallEvent,\n\tBashToolResultEvent,\n\tBeforeAgentStartEvent,\n\tBeforeAgentStartEventResult,\n\tBeforeProviderHeadersEvent,\n\tBeforeProviderRequestEvent,\n\tBeforeProviderRequestEventResult,\n\tBuildSystemPromptOptions,\n\t// Context\n\tCompactOptions,\n\t// Events - Agent\n\tContextEvent,\n\t// Event Results\n\tContextEventResult,\n\tContextUsage,\n\tCustomToolCallEvent,\n\tCustomToolResultEvent,\n\tEditorFactory,\n\tEditToolCallEvent,\n\tEditToolResultEvent,\n\t// Message and Entry Rendering\n\tEntryRenderer,\n\tEntryRenderOptions,\n\tExecOptions,\n\tExecResult,\n\tExtension,\n\tExtensionActions,\n\t// API\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionCommandContextActions,\n\tExtensionContext,\n\tExtensionContextActions,\n\t// Errors\n\tExtensionError,\n\tExtensionEvent,\n\tExtensionFactory,\n\tExtensionFlag,\n\tExtensionHandler,\n\tExtensionMode,\n\t// Runtime\n\tExtensionRuntime,\n\tExtensionShortcut,\n\tExtensionUIContext,\n\tExtensionUIDialogOptions,\n\tExtensionWidgetOptions,\n\tFindToolCallEvent,\n\tFindToolResultEvent,\n\tGetActiveToolsHandler,\n\tGetAllToolsHandler,\n\tGetCommandsHandler,\n\tGetThinkingLevelHandler,\n\tGrepToolCallEvent,\n\tGrepToolResultEvent,\n\tInlineExtension,\n\t// Events - Input\n\tInputEvent,\n\tInputEventResult,\n\tInputSource,\n\tKeybindingsManager,\n\tLoadExtensionsResult,\n\tLsToolCallEvent,\n\tLsToolResultEvent,\n\tMarkdownTransformContext,\n\tMarkdownTransformer,\n\t// Events - Message\n\tMessageEndEvent,\n\tMessageRenderer,\n\tMessageRenderOptions,\n\tMessageStartEvent,\n\tMessageUpdateEvent,\n\tModelSelectEvent,\n\tModelSelectSource,\n\tPowerShellToolCallEvent,\n\tPowerShellToolResultEvent,\n\tProjectTrustContext,\n\tProjectTrustEvent,\n\tProjectTrustEventDecision,\n\tProjectTrustEventResult,\n\tProjectTrustHandler,\n\t// Provider Registration\n\tProviderConfig,\n\tProviderModelConfig,\n\tReadToolCallEvent,\n\tReadToolResultEvent,\n\t// Commands\n\tRegisteredCommand,\n\tRegisteredTool,\n\tReplacedSessionContext,\n\tResolvedCommand,\n\t// Events - Resources\n\tResourcesDiscoverEvent,\n\tResourcesDiscoverResult,\n\tSendMessageHandler,\n\tSendUserMessageHandler,\n\tSessionBeforeCompactEvent,\n\tSessionBeforeCompactResult,\n\tSessionBeforeForkEvent,\n\tSessionBeforeForkResult,\n\tSessionBeforeSwitchEvent,\n\tSessionBeforeSwitchResult,\n\tSessionBeforeTreeEvent,\n\tSessionBeforeTreeResult,\n\tSessionCompactEvent,\n\tSessionCompactFailedEvent,\n\tSessionEvent,\n\tSessionInfoChangedEvent,\n\tSessionShutdownEvent,\n\t// Events - Session\n\tSessionStartEvent,\n\tSessionTreeEvent,\n\tSetActiveToolsHandler,\n\tSetLabelHandler,\n\tSetModelHandler,\n\tSetThinkingLevelHandler,\n\tTerminalInputHandler,\n\t// Events - Tool\n\tToolCallEvent,\n\tToolCallEventResult,\n\t// Tools\n\tToolDefinition,\n\t// Events - Tool Execution\n\tToolExecutionEndEvent,\n\t// Tool execution mode\n\tToolExecutionMode,\n\tToolExecutionStartEvent,\n\tToolExecutionUpdateEvent,\n\tToolInfo,\n\tToolRenderResultOptions,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTreePreparation,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\t// Events - User Bash\n\tUserBashEvent,\n\tUserBashEventResult,\n\tWidgetPlacement,\n\tWorkingIndicatorOptions,\n\tWriteToolCallEvent,\n\tWriteToolResultEvent,\n} from \"./types.ts\";\n// Type guards\nexport {\n\tdefineTool,\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisPowerShellToolResult,\n\tisReadToolResult,\n\tisToolCallEventType,\n\tisWriteToolResult,\n} from \"./types.ts\";\nexport { wrapRegisteredTool, wrapRegisteredTools } from \"./wrapper.ts\";\n"]}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
export { createExtensionRuntime, discoverAndLoadExtensions, loadExtensionFromFactory, loadExtensions, } from "./loader.js";
|
|
5
5
|
export { ExtensionRunner } from "./runner.js";
|
|
6
6
|
// Type guards
|
|
7
|
-
export { defineTool, isBashToolResult, isEditToolResult, isFindToolResult, isGrepToolResult, isLsToolResult, isReadToolResult, isToolCallEventType, isWriteToolResult, } from "./types.js";
|
|
7
|
+
export { defineTool, isBashToolResult, isEditToolResult, isFindToolResult, isGrepToolResult, isLsToolResult, isPowerShellToolResult, isReadToolResult, isToolCallEventType, isWriteToolResult, } from "./types.js";
|
|
8
8
|
export { wrapRegisteredTool, wrapRegisteredTools } from "./wrapper.js";
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/extensions/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EACN,sBAAsB,EACtB,yBAAyB,EACzB,wBAAwB,EACxB,cAAc,GACd,MAAM,aAAa,CAAC;AASrB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/extensions/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EACN,sBAAsB,EACtB,yBAAyB,EACzB,wBAAwB,EACxB,cAAc,GACd,MAAM,aAAa,CAAC;AASrB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AA4J9C,cAAc;AACd,OAAO,EACN,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC","sourcesContent":["/**\n * Extension system for lifecycle events and custom tools.\n */\n\nexport type { SlashCommandInfo, SlashCommandSource } from \"../slash-commands.ts\";\nexport type { SourceInfo } from \"../source-info.ts\";\nexport {\n\tcreateExtensionRuntime,\n\tdiscoverAndLoadExtensions,\n\tloadExtensionFromFactory,\n\tloadExtensions,\n} from \"./loader.ts\";\nexport type {\n\tExtensionErrorListener,\n\tForkHandler,\n\tNavigateTreeHandler,\n\tNewSessionHandler,\n\tShutdownHandler,\n\tSwitchSessionHandler,\n} from \"./runner.ts\";\nexport { ExtensionRunner } from \"./runner.ts\";\nexport type {\n\tAfterProviderResponseEvent,\n\tAgentEndEvent,\n\tAgentSettledEvent,\n\tAgentStartEvent,\n\t// Re-exports\n\tAgentToolResult,\n\tAgentToolUpdateCallback,\n\tAppendEntryHandler,\n\t// App keybindings (for custom editors)\n\tAppKeybinding,\n\tAutocompleteProviderFactory,\n\t// Events - Tool (ToolCallEvent types)\n\tBashToolCallEvent,\n\tBashToolResultEvent,\n\tBeforeAgentStartEvent,\n\tBeforeAgentStartEventResult,\n\tBeforeProviderHeadersEvent,\n\tBeforeProviderRequestEvent,\n\tBeforeProviderRequestEventResult,\n\tBuildSystemPromptOptions,\n\t// Context\n\tCompactOptions,\n\t// Events - Agent\n\tContextEvent,\n\t// Event Results\n\tContextEventResult,\n\tContextUsage,\n\tCustomToolCallEvent,\n\tCustomToolResultEvent,\n\tEditorFactory,\n\tEditToolCallEvent,\n\tEditToolResultEvent,\n\t// Message and Entry Rendering\n\tEntryRenderer,\n\tEntryRenderOptions,\n\tExecOptions,\n\tExecResult,\n\tExtension,\n\tExtensionActions,\n\t// API\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionCommandContextActions,\n\tExtensionContext,\n\tExtensionContextActions,\n\t// Errors\n\tExtensionError,\n\tExtensionEvent,\n\tExtensionFactory,\n\tExtensionFlag,\n\tExtensionHandler,\n\tExtensionMode,\n\t// Runtime\n\tExtensionRuntime,\n\tExtensionShortcut,\n\tExtensionUIContext,\n\tExtensionUIDialogOptions,\n\tExtensionWidgetOptions,\n\tFindToolCallEvent,\n\tFindToolResultEvent,\n\tGetActiveToolsHandler,\n\tGetAllToolsHandler,\n\tGetCommandsHandler,\n\tGetThinkingLevelHandler,\n\tGrepToolCallEvent,\n\tGrepToolResultEvent,\n\tInlineExtension,\n\t// Events - Input\n\tInputEvent,\n\tInputEventResult,\n\tInputSource,\n\tKeybindingsManager,\n\tLoadExtensionsResult,\n\tLsToolCallEvent,\n\tLsToolResultEvent,\n\tMarkdownTransformContext,\n\tMarkdownTransformer,\n\t// Events - Message\n\tMessageEndEvent,\n\tMessageRenderer,\n\tMessageRenderOptions,\n\tMessageStartEvent,\n\tMessageUpdateEvent,\n\tModelSelectEvent,\n\tModelSelectSource,\n\tPowerShellToolCallEvent,\n\tPowerShellToolResultEvent,\n\tProjectTrustContext,\n\tProjectTrustEvent,\n\tProjectTrustEventDecision,\n\tProjectTrustEventResult,\n\tProjectTrustHandler,\n\t// Provider Registration\n\tProviderConfig,\n\tProviderModelConfig,\n\tReadToolCallEvent,\n\tReadToolResultEvent,\n\t// Commands\n\tRegisteredCommand,\n\tRegisteredTool,\n\tReplacedSessionContext,\n\tResolvedCommand,\n\t// Events - Resources\n\tResourcesDiscoverEvent,\n\tResourcesDiscoverResult,\n\tSendMessageHandler,\n\tSendUserMessageHandler,\n\tSessionBeforeCompactEvent,\n\tSessionBeforeCompactResult,\n\tSessionBeforeForkEvent,\n\tSessionBeforeForkResult,\n\tSessionBeforeSwitchEvent,\n\tSessionBeforeSwitchResult,\n\tSessionBeforeTreeEvent,\n\tSessionBeforeTreeResult,\n\tSessionCompactEvent,\n\tSessionCompactFailedEvent,\n\tSessionEvent,\n\tSessionInfoChangedEvent,\n\tSessionShutdownEvent,\n\t// Events - Session\n\tSessionStartEvent,\n\tSessionTreeEvent,\n\tSetActiveToolsHandler,\n\tSetLabelHandler,\n\tSetModelHandler,\n\tSetThinkingLevelHandler,\n\tTerminalInputHandler,\n\t// Events - Tool\n\tToolCallEvent,\n\tToolCallEventResult,\n\t// Tools\n\tToolDefinition,\n\t// Events - Tool Execution\n\tToolExecutionEndEvent,\n\t// Tool execution mode\n\tToolExecutionMode,\n\tToolExecutionStartEvent,\n\tToolExecutionUpdateEvent,\n\tToolInfo,\n\tToolRenderResultOptions,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTreePreparation,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\t// Events - User Bash\n\tUserBashEvent,\n\tUserBashEventResult,\n\tWidgetPlacement,\n\tWorkingIndicatorOptions,\n\tWriteToolCallEvent,\n\tWriteToolResultEvent,\n} from \"./types.ts\";\n// Type guards\nexport {\n\tdefineTool,\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisPowerShellToolResult,\n\tisReadToolResult,\n\tisToolCallEventType,\n\tisWriteToolResult,\n} from \"./types.ts\";\nexport { wrapRegisteredTool, wrapRegisteredTools } from \"./wrapper.ts\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyBH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAMhE,OAAO,KAAK,EAEX,SAAS,EAET,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EAMpB,MAAM,YAAY,CAAC;AA4GpB,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C;AAWD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAoEzD;AAoRD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAa,GACxB,OAAO,CAAC,SAAS,CAAC,CAOpB;AA6CD,wBAAsB,cAAc,CACnC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AA2FD;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,eAAe,EAAE,MAAM,EAAE,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAsB,EAChC,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA2C/B","sourcesContent":["/**\n * Extension loader - loads TypeScript extension modules using jiti.\n *\n */\n\nimport * as fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Provider } from \"@earendil-works/pi-ai\";\nimport * as _bundledPiAiCompat from \"@earendil-works/pi-ai/compat\";\nimport * as _bundledPiAiOauth from \"@earendil-works/pi-ai/oauth\";\nimport * as _bundledPiAiProviders from \"@earendil-works/pi-ai/providers/all\";\nimport type { KeyId } from \"@earendil-works/pi-tui\";\nimport * as _bundledPiTui from \"@earendil-works/pi-tui\";\nimport * as _bundledPiAgentCore from \"apex-code-agent-core\";\nimport { createJiti } from \"jiti/static\";\n// Static imports of packages that extensions may use.\n// These MUST be static so Bun bundles them into the compiled binary.\n// The virtualModules option then makes them available to extensions.\nimport * as _bundledTypebox from \"typebox\";\nimport * as _bundledTypeboxCompile from \"typebox/compile\";\nimport * as _bundledTypeboxValue from \"typebox/value\";\nimport { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from \"../../config.ts\";\n// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,\n// avoiding a circular dependency. Extensions can import from apex-code.\nimport * as _bundledPiCodingAgent from \"../../index.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { createEventBus, type EventBus } from \"../event-bus.ts\";\nimport type { ExecOptions } from \"../exec.ts\";\nimport { execCommand } from \"../exec.ts\";\nimport { readPiManifest } from \"../pi-manifest.ts\";\nimport { createSyntheticSourceInfo } from \"../source-info.ts\";\nimport { time } from \"../timings.ts\";\nimport type {\n\tEntryRenderer,\n\tExtension,\n\tExtensionAPI,\n\tExtensionFactory,\n\tExtensionRuntime,\n\tLoadExtensionsResult,\n\tMarkdownTransformer,\n\tMessageRenderer,\n\tProviderConfig,\n\tRegisteredCommand,\n\tToolDefinition,\n} from \"./types.ts\";\n\n/** Modules available to extensions via virtualModules (for compiled Bun binary) */\nconst VIRTUAL_MODULES: Record<string, unknown> = {\n\ttypebox: _bundledTypebox,\n\t\"typebox/compile\": _bundledTypeboxCompile,\n\t\"typebox/value\": _bundledTypeboxValue,\n\t\"@sinclair/typebox\": _bundledTypebox,\n\t\"@sinclair/typebox/compile\": _bundledTypeboxCompile,\n\t\"@sinclair/typebox/value\": _bundledTypeboxValue,\n\t\"apex-code-agent-core\": _bundledPiAgentCore,\n\t\"@earendil-works/pi-tui\": _bundledPiTui,\n\t// Extensions resolve the pi-ai root to the compat entrypoint (a strict\n\t// superset of the core entrypoint): existing extensions using the old\n\t// global API keep working at runtime until compat is removed.\n\t\"@earendil-works/pi-ai\": _bundledPiAiCompat,\n\t\"@earendil-works/pi-ai/compat\": _bundledPiAiCompat,\n\t\"@earendil-works/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@earendil-works/pi-ai/providers/all\": _bundledPiAiProviders,\n\t\"apex-code\": _bundledPiCodingAgent,\n\t\"@mariozechner/pi-agent-core\": _bundledPiAgentCore,\n\t\"@mariozechner/pi-tui\": _bundledPiTui,\n\t\"@mariozechner/pi-ai\": _bundledPiAiCompat,\n\t\"@mariozechner/pi-ai/compat\": _bundledPiAiCompat,\n\t\"@mariozechner/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@mariozechner/pi-ai/providers/all\": _bundledPiAiProviders,\n\t\"@mariozechner/pi-coding-agent\": _bundledPiCodingAgent,\n};\n\nconst require = createRequire(import.meta.url);\n\nconst isTypeScriptSourceRuntime = !isBunBinary && path.extname(fileURLToPath(import.meta.url)) === \".ts\";\n\n/**\n * Get aliases for jiti (used in built Node.js mode).\n * In Bun binary mode, virtualModules is used instead.\n */\nlet _aliases: Record<string, string> | null = null;\n\nfunction getAliases(): Record<string, string> {\n\tif (_aliases) return _aliases;\n\n\tconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\tconst packageIndex = path.resolve(__dirname, \"../..\", \"index.js\");\n\n\tconst typeboxEntry = require.resolve(\"typebox\");\n\tconst typeboxCompileEntry = require.resolve(\"typebox/compile\");\n\tconst typeboxValueEntry = require.resolve(\"typebox/value\");\n\n\tconst packagesRoot = path.resolve(__dirname, \"../../../../\");\n\tconst resolveWorkspaceOrImport = (workspaceRelativePath: string, specifier: string): string => {\n\t\tconst workspacePath = path.join(packagesRoot, workspaceRelativePath);\n\t\tif (fs.existsSync(workspacePath)) {\n\t\t\treturn workspacePath;\n\t\t}\n\t\treturn fileURLToPath(import.meta.resolve(specifier));\n\t};\n\n\tconst piCodingAgentEntry = packageIndex;\n\tconst piAgentCoreEntry = resolveWorkspaceOrImport(\"agent/dist/index.js\", \"apex-code-agent-core\");\n\tconst piTuiEntry = resolveWorkspaceOrImport(\"tui/dist/index.js\", \"@earendil-works/pi-tui\");\n\t// Extensions resolve the pi-ai root to the compat entrypoint (a strict\n\t// superset of the core entrypoint): existing extensions using the old\n\t// global API keep working at runtime until compat is removed.\n\tconst piAiCompatEntry = resolveWorkspaceOrImport(\"ai/dist/compat.js\", \"@earendil-works/pi-ai/compat\");\n\tconst piAiOauthEntry = resolveWorkspaceOrImport(\"ai/dist/oauth.js\", \"@earendil-works/pi-ai/oauth\");\n\tconst piAiProvidersEntry = resolveWorkspaceOrImport(\n\t\t\"ai/dist/providers/all.js\",\n\t\t\"@earendil-works/pi-ai/providers/all\",\n\t);\n\n\t_aliases = {\n\t\t\"apex-code\": piCodingAgentEntry,\n\t\t\"apex-code-agent-core\": piAgentCoreEntry,\n\t\t\"@earendil-works/pi-tui\": piTuiEntry,\n\t\t\"@earendil-works/pi-ai/providers/all\": piAiProvidersEntry,\n\t\t\"@earendil-works/pi-ai/compat\": piAiCompatEntry,\n\t\t\"@earendil-works/pi-ai/oauth\": piAiOauthEntry,\n\t\t\"@earendil-works/pi-ai\": piAiCompatEntry,\n\t\t\"@mariozechner/pi-coding-agent\": piCodingAgentEntry,\n\t\t\"@mariozechner/pi-agent-core\": piAgentCoreEntry,\n\t\t\"@mariozechner/pi-tui\": piTuiEntry,\n\t\t\"@mariozechner/pi-ai/providers/all\": piAiProvidersEntry,\n\t\t\"@mariozechner/pi-ai/compat\": piAiCompatEntry,\n\t\t\"@mariozechner/pi-ai/oauth\": piAiOauthEntry,\n\t\t\"@mariozechner/pi-ai\": piAiCompatEntry,\n\t\ttypebox: typeboxEntry,\n\t\t\"typebox/compile\": typeboxCompileEntry,\n\t\t\"typebox/value\": typeboxValueEntry,\n\t\t\"@sinclair/typebox\": typeboxEntry,\n\t\t\"@sinclair/typebox/compile\": typeboxCompileEntry,\n\t\t\"@sinclair/typebox/value\": typeboxValueEntry,\n\t};\n\n\treturn _aliases;\n}\n\ntype HandlerFn = (...args: unknown[]) => Promise<unknown>;\n\nlet extensionCacheCwd: string | undefined;\nlet extensionCacheGeneration = 0;\nconst extensionCache = new Map<string, ExtensionFactory>();\n\ninterface ExtensionCacheToken {\n\tcwd: string;\n\tgeneration: number;\n}\n\nexport function clearExtensionCache(): void {\n\textensionCache.clear();\n\textensionCacheCwd = undefined;\n\textensionCacheGeneration++;\n}\n\nfunction useExtensionCacheCwd(cwd: string): ExtensionCacheToken {\n\tconst resolvedCwd = resolvePath(cwd);\n\tif (extensionCacheCwd !== undefined && extensionCacheCwd !== resolvedCwd) {\n\t\tclearExtensionCache();\n\t}\n\textensionCacheCwd = resolvedCwd;\n\treturn { cwd: resolvedCwd, generation: extensionCacheGeneration };\n}\n\n/**\n * Create a runtime with throwing stubs for action methods.\n * Runner.bindCore() replaces these with real implementations.\n */\nexport function createExtensionRuntime(): ExtensionRuntime {\n\tconst notInitialized = () => {\n\t\tthrow new Error(\"Extension runtime not initialized. Action methods cannot be called during extension loading.\");\n\t};\n\tconst state: { staleMessage?: string } = {};\n\tconst eventBusUnsubscribers = new Set<() => void>();\n\tconst assertActive = () => {\n\t\tif (state.staleMessage) {\n\t\t\tthrow new Error(state.staleMessage);\n\t\t}\n\t};\n\n\tconst runtime: ExtensionRuntime = {\n\t\tsendMessage: notInitialized,\n\t\tsendUserMessage: notInitialized,\n\t\tappendEntry: notInitialized,\n\t\tsetSessionName: notInitialized,\n\t\tgetSessionName: notInitialized,\n\t\tsetLabel: notInitialized,\n\t\tgetActiveTools: notInitialized,\n\t\tgetAllTools: notInitialized,\n\t\tsetActiveTools: notInitialized,\n\t\t// registerTool() is valid during extension load; refresh is only needed post-bind.\n\t\trefreshTools: () => {},\n\t\tgetCommands: notInitialized,\n\t\tsetModel: () => Promise.reject(new Error(\"Extension runtime not initialized\")),\n\t\tgetThinkingLevel: notInitialized,\n\t\tsetThinkingLevel: notInitialized,\n\t\tflagValues: new Map(),\n\t\tpendingProviderRegistrations: [],\n\t\tpendingNativeProviderRegistrations: [],\n\t\tassertActive,\n\t\tinvalidate: (message) => {\n\t\t\tif (state.staleMessage) return;\n\t\t\tstate.staleMessage =\n\t\t\t\tmessage ??\n\t\t\t\t\"This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().\";\n\t\t\tfor (const unsubscribe of eventBusUnsubscribers) unsubscribe();\n\t\t\teventBusUnsubscribers.clear();\n\t\t},\n\t\ttrackEventBusSubscription: (unsubscribe) => {\n\t\t\tlet active = true;\n\t\t\tconst trackedUnsubscribe = () => {\n\t\t\t\tif (!active) return;\n\t\t\t\tactive = false;\n\t\t\t\teventBusUnsubscribers.delete(trackedUnsubscribe);\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t\teventBusUnsubscribers.add(trackedUnsubscribe);\n\t\t\treturn trackedUnsubscribe;\n\t\t},\n\t\t// Pre-bind: queue registrations so bindCore() can flush them once the\n\t\t// model registry is available. bindCore() replaces both with direct calls.\n\t\tregisterProvider: (name, config, extensionPath = \"<unknown>\") => {\n\t\t\truntime.pendingProviderRegistrations.push({ name, config, extensionPath });\n\t\t},\n\t\tregisterNativeProvider: (provider, extensionPath = \"<unknown>\") => {\n\t\t\truntime.pendingNativeProviderRegistrations.push({ provider, extensionPath });\n\t\t},\n\t\tunregisterProvider: (name) => {\n\t\t\truntime.pendingProviderRegistrations = runtime.pendingProviderRegistrations.filter((r) => r.name !== name);\n\t\t\truntime.pendingNativeProviderRegistrations = runtime.pendingNativeProviderRegistrations.filter(\n\t\t\t\t(r) => r.provider.id !== name,\n\t\t\t);\n\t\t},\n\t};\n\n\treturn runtime;\n}\n\n/**\n * Create the ExtensionAPI for an extension.\n * Registration methods write to the extension object.\n * Action methods delegate to the shared runtime.\n */\nfunction createExtensionAPI(\n\textension: Extension,\n\truntime: ExtensionRuntime,\n\tcwd: string,\n\teventBus: EventBus,\n): ExtensionAPI {\n\tconst api = {\n\t\t// Registration methods - write to extension\n\t\ton(event: string, handler: HandlerFn): void {\n\t\t\truntime.assertActive();\n\t\t\tconst list = extension.handlers.get(event) ?? [];\n\t\t\tlist.push(handler);\n\t\t\textension.handlers.set(event, list);\n\t\t},\n\n\t\tregisterTool(tool: ToolDefinition): void {\n\t\t\truntime.assertActive();\n\t\t\textension.tools.set(tool.name, {\n\t\t\t\tdefinition: tool,\n\t\t\t\tsourceInfo: extension.sourceInfo,\n\t\t\t});\n\t\t\truntime.refreshTools();\n\t\t},\n\n\t\tregisterCommand(name: string, options: Omit<RegisteredCommand, \"name\" | \"sourceInfo\">): void {\n\t\t\truntime.assertActive();\n\t\t\textension.commands.set(name, {\n\t\t\t\tname,\n\t\t\t\tsourceInfo: extension.sourceInfo,\n\t\t\t\t...options,\n\t\t\t});\n\t\t},\n\n\t\tregisterShortcut(\n\t\t\tshortcut: KeyId,\n\t\t\toptions: {\n\t\t\t\tdescription?: string;\n\t\t\t\thandler: (ctx: import(\"./types.ts\").ExtensionContext) => Promise<void> | void;\n\t\t\t},\n\t\t): void {\n\t\t\truntime.assertActive();\n\t\t\textension.shortcuts.set(shortcut, { shortcut, extensionPath: extension.path, ...options });\n\t\t},\n\n\t\tregisterFlag(\n\t\t\tname: string,\n\t\t\toptions: { description?: string; type: \"boolean\" | \"string\"; default?: boolean | string },\n\t\t): void {\n\t\t\truntime.assertActive();\n\t\t\textension.flags.set(name, { name, extensionPath: extension.path, ...options });\n\t\t\tif (options.default !== undefined && !runtime.flagValues.has(name)) {\n\t\t\t\truntime.flagValues.set(name, options.default);\n\t\t\t}\n\t\t},\n\n\t\tregisterMessageRenderer<T>(customType: string, renderer: MessageRenderer<T>): void {\n\t\t\truntime.assertActive();\n\t\t\textension.messageRenderers.set(customType, renderer as MessageRenderer);\n\t\t},\n\n\t\tregisterMarkdownTransformer(transformer: MarkdownTransformer): void {\n\t\t\truntime.assertActive();\n\t\t\textension.markdownTransformer = transformer;\n\t\t},\n\n\t\tregisterEntryRenderer<T>(customType: string, renderer: EntryRenderer<T>): void {\n\t\t\truntime.assertActive();\n\t\t\textension.entryRenderers ??= new Map();\n\t\t\textension.entryRenderers.set(customType, renderer as EntryRenderer);\n\t\t},\n\n\t\t// Flag access - checks extension registered it, reads from runtime\n\t\tgetFlag(name: string): boolean | string | undefined {\n\t\t\truntime.assertActive();\n\t\t\tif (!extension.flags.has(name)) return undefined;\n\t\t\treturn runtime.flagValues.get(name);\n\t\t},\n\n\t\t// Action methods - delegate to shared runtime\n\t\tsendMessage(message, options): void {\n\t\t\truntime.assertActive();\n\t\t\truntime.sendMessage(message, options);\n\t\t},\n\n\t\tsendUserMessage(content, options): void {\n\t\t\truntime.assertActive();\n\t\t\truntime.sendUserMessage(content, options);\n\t\t},\n\n\t\tappendEntry(customType: string, data?: unknown): void {\n\t\t\truntime.assertActive();\n\t\t\truntime.appendEntry(customType, data);\n\t\t},\n\n\t\tsetSessionName(name: string): void {\n\t\t\truntime.assertActive();\n\t\t\truntime.setSessionName(name);\n\t\t},\n\n\t\tgetSessionName(): string | undefined {\n\t\t\truntime.assertActive();\n\t\t\treturn runtime.getSessionName();\n\t\t},\n\n\t\tsetLabel(entryId: string, label: string | undefined): void {\n\t\t\truntime.assertActive();\n\t\t\truntime.setLabel(entryId, label);\n\t\t},\n\n\t\texec(command: string, args: string[], options?: ExecOptions) {\n\t\t\truntime.assertActive();\n\t\t\treturn execCommand(command, args, options?.cwd ?? cwd, options);\n\t\t},\n\n\t\tgetActiveTools(): string[] {\n\t\t\truntime.assertActive();\n\t\t\treturn runtime.getActiveTools();\n\t\t},\n\n\t\tgetAllTools() {\n\t\t\truntime.assertActive();\n\t\t\treturn runtime.getAllTools();\n\t\t},\n\n\t\tsetActiveTools(toolNames: string[]): void {\n\t\t\truntime.assertActive();\n\t\t\truntime.setActiveTools(toolNames);\n\t\t},\n\n\t\tgetCommands() {\n\t\t\truntime.assertActive();\n\t\t\treturn runtime.getCommands();\n\t\t},\n\n\t\tsetModel(model) {\n\t\t\truntime.assertActive();\n\t\t\treturn runtime.setModel(model);\n\t\t},\n\n\t\tgetThinkingLevel() {\n\t\t\truntime.assertActive();\n\t\t\treturn runtime.getThinkingLevel();\n\t\t},\n\n\t\tsetThinkingLevel(level) {\n\t\t\truntime.assertActive();\n\t\t\truntime.setThinkingLevel(level);\n\t\t},\n\n\t\tregisterProvider(providerOrName: Provider | string, config?: ProviderConfig) {\n\t\t\truntime.assertActive();\n\t\t\tif (typeof providerOrName === \"string\") {\n\t\t\t\tif (!config) throw new Error(\"Provider config is required when registering by name\");\n\t\t\t\truntime.registerProvider(providerOrName, config, extension.path);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\truntime.registerNativeProvider(providerOrName, extension.path);\n\t\t},\n\n\t\tunregisterProvider(name: string) {\n\t\t\truntime.assertActive();\n\t\t\truntime.unregisterProvider(name, extension.path);\n\t\t},\n\n\t\tevents: {\n\t\t\temit(channel, data) {\n\t\t\t\truntime.assertActive();\n\t\t\t\teventBus.emit(channel, data);\n\t\t\t},\n\t\t\ton(channel, handler) {\n\t\t\t\truntime.assertActive();\n\t\t\t\treturn runtime.trackEventBusSubscription(eventBus.on(channel, handler));\n\t\t\t},\n\t\t},\n\t} as ExtensionAPI;\n\n\treturn api;\n}\n\nfunction isCurrentCacheToken(cacheToken: ExtensionCacheToken | undefined): cacheToken is ExtensionCacheToken {\n\treturn (\n\t\tcacheToken !== undefined &&\n\t\textensionCacheCwd === cacheToken.cwd &&\n\t\textensionCacheGeneration === cacheToken.generation\n\t);\n}\n\nasync function loadExtensionModule(extensionPath: string, cacheToken?: ExtensionCacheToken) {\n\tif (isCurrentCacheToken(cacheToken)) {\n\t\tconst cachedFactory = extensionCache.get(extensionPath);\n\t\tif (cachedFactory) {\n\t\t\treturn cachedFactory;\n\t\t}\n\t}\n\n\tconst jiti = createJiti(import.meta.url, {\n\t\tmoduleCache: false,\n\t\t// Bun uses modules embedded in the executable. Source TypeScript reuses the\n\t\t// host-resolved modules and root tsconfig paths. Built Node uses dist aliases.\n\t\t...(isBunBinary\n\t\t\t? { virtualModules: VIRTUAL_MODULES, tryNative: false }\n\t\t\t: isTypeScriptSourceRuntime\n\t\t\t\t? { virtualModules: VIRTUAL_MODULES, tsconfigPaths: true }\n\t\t\t\t: { alias: getAliases() }),\n\t});\n\n\tconst module = await jiti.import(extensionPath, { default: true });\n\tconst factory = module as ExtensionFactory;\n\tif (typeof factory !== \"function\") {\n\t\treturn undefined;\n\t}\n\tif (isCurrentCacheToken(cacheToken)) {\n\t\textensionCache.set(extensionPath, factory);\n\t}\n\treturn factory;\n}\n\n/**\n * Create an Extension object with empty collections.\n */\nfunction createExtension(extensionPath: string, resolvedPath: string): Extension {\n\tconst source =\n\t\textensionPath.startsWith(\"<\") && extensionPath.endsWith(\">\")\n\t\t\t? extensionPath.slice(1, -1).split(\":\")[0] || \"temporary\"\n\t\t\t: \"local\";\n\tconst baseDir = extensionPath.startsWith(\"<\") ? undefined : path.dirname(resolvedPath);\n\n\treturn {\n\t\tpath: extensionPath,\n\t\tresolvedPath,\n\t\tsourceInfo: createSyntheticSourceInfo(extensionPath, { source, baseDir }),\n\t\thandlers: new Map(),\n\t\ttools: new Map(),\n\t\tmessageRenderers: new Map(),\n\t\tentryRenderers: new Map(),\n\t\tcommands: new Map(),\n\t\tflags: new Map(),\n\t\tshortcuts: new Map(),\n\t};\n}\n\nasync function loadExtension(\n\textensionPath: string,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n\tcacheToken?: ExtensionCacheToken,\n): Promise<{ extension: Extension | null; error: string | null }> {\n\tconst resolvedPath = resolvePath(extensionPath, cwd, { normalizeUnicodeSpaces: true });\n\n\ttry {\n\t\tconst factory = await loadExtensionModule(resolvedPath, cacheToken);\n\t\ttime(`${extensionPath} module import`, \"extensions\");\n\t\tif (!factory) {\n\t\t\treturn { extension: null, error: `Extension does not export a valid factory function: ${extensionPath}` };\n\t\t}\n\n\t\tconst extension = createExtension(extensionPath, resolvedPath);\n\t\tconst api = createExtensionAPI(extension, runtime, cwd, eventBus);\n\t\tawait factory(api);\n\t\ttime(`${extensionPath} factory`, \"extensions\");\n\n\t\treturn { extension, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn { extension: null, error: `Failed to load extension: ${message}` };\n\t}\n}\n\n/**\n * Create an Extension from an inline factory function.\n */\nexport async function loadExtensionFromFactory(\n\tfactory: ExtensionFactory,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n\textensionPath = \"<inline>\",\n): Promise<Extension> {\n\tconst extension = createExtension(extensionPath, extensionPath);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst api = createExtensionAPI(extension, runtime, resolvedCwd, eventBus);\n\tawait factory(api);\n\ttime(`${extensionPath} factory`, \"extensions\");\n\treturn extension;\n}\n\n/**\n * Load extensions from paths.\n */\nasync function loadExtensionsInternal(\n\tpaths: string[],\n\tcwd: string,\n\teventBus?: EventBus,\n\truntime?: ExtensionRuntime,\n\tuseCache = false,\n): Promise<LoadExtensionsResult> {\n\tconst extensions: Extension[] = [];\n\tconst errors: Array<{ path: string; error: string }> = [];\n\tconst cacheToken = useCache ? useExtensionCacheCwd(cwd) : undefined;\n\tconst resolvedCwd = cacheToken?.cwd ?? resolvePath(cwd);\n\tconst resolvedEventBus = eventBus ?? createEventBus();\n\tconst resolvedRuntime = runtime ?? createExtensionRuntime();\n\n\tfor (const extPath of paths) {\n\t\tconst { extension, error } = await loadExtension(\n\t\t\textPath,\n\t\t\tresolvedCwd,\n\t\t\tresolvedEventBus,\n\t\t\tresolvedRuntime,\n\t\t\tcacheToken,\n\t\t);\n\n\t\tif (error) {\n\t\t\terrors.push({ path: extPath, error });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (extension) {\n\t\t\textensions.push(extension);\n\t\t}\n\t}\n\n\treturn {\n\t\textensions,\n\t\terrors,\n\t\truntime: resolvedRuntime,\n\t};\n}\n\nexport async function loadExtensions(\n\tpaths: string[],\n\tcwd: string,\n\teventBus?: EventBus,\n\truntime?: ExtensionRuntime,\n): Promise<LoadExtensionsResult> {\n\treturn loadExtensionsInternal(paths, cwd, eventBus, runtime);\n}\n\nexport async function loadExtensionsCached(\n\tpaths: string[],\n\tcwd: string,\n\teventBus?: EventBus,\n\truntime?: ExtensionRuntime,\n): Promise<LoadExtensionsResult> {\n\treturn loadExtensionsInternal(paths, cwd, eventBus, runtime, true);\n}\n\nfunction isExtensionFile(name: string): boolean {\n\treturn name.endsWith(\".ts\") || name.endsWith(\".js\");\n}\n\n/**\n * Resolve extension entry points from a directory.\n *\n * Checks for:\n * 1. package.json with \"pi.extensions\" field -> returns declared paths\n * 2. index.ts or index.js -> returns the index file\n *\n * Returns resolved paths or null if no entry points found.\n */\nfunction resolveExtensionEntries(dir: string): string[] | null {\n\t// Check for package.json with \"pi\" field first\n\tconst packageJsonPath = path.join(dir, \"package.json\");\n\tif (fs.existsSync(packageJsonPath)) {\n\t\tconst manifest = readPiManifest(packageJsonPath);\n\t\tif (manifest?.extensions?.length) {\n\t\t\tconst entries: string[] = [];\n\t\t\tfor (const extPath of manifest.extensions) {\n\t\t\t\tconst resolvedExtPath = path.resolve(dir, extPath);\n\t\t\t\tif (fs.existsSync(resolvedExtPath)) {\n\t\t\t\t\tentries.push(resolvedExtPath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (entries.length > 0) {\n\t\t\t\treturn entries;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check for index.ts or index.js\n\tconst indexTs = path.join(dir, \"index.ts\");\n\tconst indexJs = path.join(dir, \"index.js\");\n\tif (fs.existsSync(indexTs)) {\n\t\treturn [indexTs];\n\t}\n\tif (fs.existsSync(indexJs)) {\n\t\treturn [indexJs];\n\t}\n\n\treturn null;\n}\n\n/**\n * Discover extensions in a directory.\n *\n * Discovery rules:\n * 1. Direct files: `extensions/*.ts` or `*.js` → load\n * 2. Subdirectory with index: `extensions/* /index.ts` or `index.js` → load\n * 3. Subdirectory with package.json: `extensions/* /package.json` with \"pi\" field → load what it declares\n *\n * No recursion beyond one level. Complex packages must use package.json manifest.\n */\nfunction discoverExtensionsInDir(dir: string): string[] {\n\tif (!fs.existsSync(dir)) {\n\t\treturn [];\n\t}\n\n\tconst discovered: string[] = [];\n\n\ttry {\n\t\tconst entries = fs.readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst entryPath = path.join(dir, entry.name);\n\n\t\t\t// 1. Direct files: *.ts or *.js\n\t\t\tif ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {\n\t\t\t\tdiscovered.push(entryPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// 2 & 3. Subdirectories\n\t\t\tif (entry.isDirectory() || entry.isSymbolicLink()) {\n\t\t\t\tconst entries = resolveExtensionEntries(entryPath);\n\t\t\t\tif (entries) {\n\t\t\t\t\tdiscovered.push(...entries);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn [];\n\t}\n\n\treturn discovered;\n}\n\n/**\n * Discover and load extensions from standard locations.\n */\nexport async function discoverAndLoadExtensions(\n\tconfiguredPaths: string[],\n\tcwd: string,\n\tagentDir: string = getAgentDir(),\n\teventBus?: EventBus,\n): Promise<LoadExtensionsResult> {\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst resolvedAgentDir = resolvePath(agentDir);\n\tconst allPaths: string[] = [];\n\tconst seen = new Set<string>();\n\n\tconst addPaths = (paths: string[]) => {\n\t\tfor (const p of paths) {\n\t\t\tconst resolved = path.resolve(p);\n\t\t\tif (!seen.has(resolved)) {\n\t\t\t\tseen.add(resolved);\n\t\t\t\tallPaths.push(p);\n\t\t\t}\n\t\t}\n\t};\n\n\t// 1. Project-local extensions: cwd/${CONFIG_DIR_NAME}/extensions/\n\tconst localExtDir = path.join(resolvedCwd, CONFIG_DIR_NAME, \"extensions\");\n\taddPaths(discoverExtensionsInDir(localExtDir));\n\n\t// 2. Global extensions: agentDir/extensions/\n\tconst globalExtDir = path.join(resolvedAgentDir, \"extensions\");\n\taddPaths(discoverExtensionsInDir(globalExtDir));\n\n\t// 3. Explicitly configured paths\n\tfor (const p of configuredPaths) {\n\t\tconst resolved = resolvePath(p, resolvedCwd, { normalizeUnicodeSpaces: true });\n\t\tif (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {\n\t\t\t// Check for package.json with pi manifest or index.ts\n\t\t\tconst entries = resolveExtensionEntries(resolved);\n\t\t\tif (entries) {\n\t\t\t\taddPaths(entries);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// No explicit entries - discover individual files in directory\n\t\t\taddPaths(discoverExtensionsInDir(resolved));\n\t\t\tcontinue;\n\t\t}\n\n\t\taddPaths([resolved]);\n\t}\n\n\treturn loadExtensions(allPaths, resolvedCwd, eventBus);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyBH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAMhE,OAAO,KAAK,EAEX,SAAS,EAET,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EAMpB,MAAM,YAAY,CAAC;AAiHpB,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C;AAWD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAoEzD;AAwVD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAa,GACxB,OAAO,CAAC,SAAS,CAAC,CAGpB;AA6CD,wBAAsB,cAAc,CACnC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AA2FD;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,eAAe,EAAE,MAAM,EAAE,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAsB,EAChC,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA2C/B","sourcesContent":["/**\n * Extension loader - loads TypeScript extension modules using jiti.\n *\n */\n\nimport * as fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Provider } from \"@earendil-works/pi-ai\";\nimport * as _bundledPiAiCompat from \"@earendil-works/pi-ai/compat\";\nimport * as _bundledPiAiOauth from \"@earendil-works/pi-ai/oauth\";\nimport * as _bundledPiAiProviders from \"@earendil-works/pi-ai/providers/all\";\nimport type { KeyId } from \"@earendil-works/pi-tui\";\nimport * as _bundledPiTui from \"@earendil-works/pi-tui\";\nimport * as _bundledPiAgentCore from \"apex-code-agent-core\";\nimport { createJiti } from \"jiti/static\";\n// Static imports of packages that extensions may use.\n// These MUST be static so Bun bundles them into the compiled binary.\n// The virtualModules option then makes them available to extensions.\nimport * as _bundledTypebox from \"typebox\";\nimport * as _bundledTypeboxCompile from \"typebox/compile\";\nimport * as _bundledTypeboxValue from \"typebox/value\";\nimport { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from \"../../config.ts\";\n// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,\n// avoiding a circular dependency. Extensions can import from apex-code.\nimport * as _bundledPiCodingAgent from \"../../index.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { createEventBus, type EventBus } from \"../event-bus.ts\";\nimport type { ExecOptions } from \"../exec.ts\";\nimport { execCommand } from \"../exec.ts\";\nimport { readPiManifest } from \"../pi-manifest.ts\";\nimport { createSyntheticSourceInfo } from \"../source-info.ts\";\nimport { time } from \"../timings.ts\";\nimport type {\n\tEntryRenderer,\n\tExtension,\n\tExtensionAPI,\n\tExtensionFactory,\n\tExtensionRuntime,\n\tLoadExtensionsResult,\n\tMarkdownTransformer,\n\tMessageRenderer,\n\tProviderConfig,\n\tRegisteredCommand,\n\tToolDefinition,\n} from \"./types.ts\";\n\n/** Modules available to extensions via virtualModules (for compiled binaries) */\nconst VIRTUAL_MODULES: Record<string, unknown> = {\n\ttypebox: _bundledTypebox,\n\t\"typebox/compile\": _bundledTypeboxCompile,\n\t\"typebox/value\": _bundledTypeboxValue,\n\t\"@sinclair/typebox\": _bundledTypebox,\n\t\"@sinclair/typebox/compile\": _bundledTypeboxCompile,\n\t\"@sinclair/typebox/value\": _bundledTypeboxValue,\n\t\"apex-code-agent-core\": _bundledPiAgentCore,\n\t\"@earendil-works/pi-tui\": _bundledPiTui,\n\t// Extensions resolve the pi-ai root to the compat entrypoint (a strict\n\t// superset of the core entrypoint): existing extensions using the old\n\t// global API keep working at runtime until compat is removed.\n\t\"@earendil-works/pi-ai\": _bundledPiAiCompat,\n\t\"@earendil-works/pi-ai/compat\": _bundledPiAiCompat,\n\t\"@earendil-works/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@earendil-works/pi-ai/providers/all\": _bundledPiAiProviders,\n\t\"apex-code\": _bundledPiCodingAgent,\n\t\"@mariozechner/pi-agent-core\": _bundledPiAgentCore,\n\t\"@mariozechner/pi-tui\": _bundledPiTui,\n\t\"@mariozechner/pi-ai\": _bundledPiAiCompat,\n\t\"@mariozechner/pi-ai/compat\": _bundledPiAiCompat,\n\t\"@mariozechner/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@mariozechner/pi-ai/providers/all\": _bundledPiAiProviders,\n\t\"@mariozechner/pi-coding-agent\": _bundledPiCodingAgent,\n};\n\nconst require = createRequire(import.meta.url);\n\nconst isNodeSeaBinary =\n\t(\"sea\" in process.features && process.features.sea === true) ||\n\tprocess.getBuiltinModule(\"node:sea\")?.isSea() === true;\ndeclare const PI_BUNDLED_NODE: boolean;\nconst isBundledNode = typeof PI_BUNDLED_NODE !== \"undefined\" && PI_BUNDLED_NODE;\nconst isTypeScriptSourceRuntime = !isBunBinary && path.extname(fileURLToPath(import.meta.url)) === \".ts\";\n\n/**\n * Get aliases for jiti (used in built Node.js mode).\n * In compiled binary mode, virtualModules is used instead.\n */\nlet _aliases: Record<string, string> | null = null;\n\nfunction getAliases(): Record<string, string> {\n\tif (_aliases) return _aliases;\n\n\tconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\tconst packageIndex = path.resolve(__dirname, \"../..\", \"index.js\");\n\n\tconst typeboxEntry = require.resolve(\"typebox\");\n\tconst typeboxCompileEntry = require.resolve(\"typebox/compile\");\n\tconst typeboxValueEntry = require.resolve(\"typebox/value\");\n\n\tconst packagesRoot = path.resolve(__dirname, \"../../../../\");\n\tconst resolveWorkspaceOrImport = (workspaceRelativePath: string, specifier: string): string => {\n\t\tconst workspacePath = path.join(packagesRoot, workspaceRelativePath);\n\t\tif (fs.existsSync(workspacePath)) {\n\t\t\treturn workspacePath;\n\t\t}\n\t\treturn fileURLToPath(import.meta.resolve(specifier));\n\t};\n\n\tconst piCodingAgentEntry = packageIndex;\n\tconst piAgentCoreEntry = resolveWorkspaceOrImport(\"agent/dist/index.js\", \"apex-code-agent-core\");\n\tconst piTuiEntry = resolveWorkspaceOrImport(\"tui/dist/index.js\", \"@earendil-works/pi-tui\");\n\t// Extensions resolve the pi-ai root to the compat entrypoint (a strict\n\t// superset of the core entrypoint): existing extensions using the old\n\t// global API keep working at runtime until compat is removed.\n\tconst piAiCompatEntry = resolveWorkspaceOrImport(\"ai/dist/compat.js\", \"@earendil-works/pi-ai/compat\");\n\tconst piAiOauthEntry = resolveWorkspaceOrImport(\"ai/dist/oauth.js\", \"@earendil-works/pi-ai/oauth\");\n\tconst piAiProvidersEntry = resolveWorkspaceOrImport(\n\t\t\"ai/dist/providers/all.js\",\n\t\t\"@earendil-works/pi-ai/providers/all\",\n\t);\n\n\t_aliases = {\n\t\t\"apex-code\": piCodingAgentEntry,\n\t\t\"apex-code-agent-core\": piAgentCoreEntry,\n\t\t\"@earendil-works/pi-tui\": piTuiEntry,\n\t\t\"@earendil-works/pi-ai/providers/all\": piAiProvidersEntry,\n\t\t\"@earendil-works/pi-ai/compat\": piAiCompatEntry,\n\t\t\"@earendil-works/pi-ai/oauth\": piAiOauthEntry,\n\t\t\"@earendil-works/pi-ai\": piAiCompatEntry,\n\t\t\"@mariozechner/pi-coding-agent\": piCodingAgentEntry,\n\t\t\"@mariozechner/pi-agent-core\": piAgentCoreEntry,\n\t\t\"@mariozechner/pi-tui\": piTuiEntry,\n\t\t\"@mariozechner/pi-ai/providers/all\": piAiProvidersEntry,\n\t\t\"@mariozechner/pi-ai/compat\": piAiCompatEntry,\n\t\t\"@mariozechner/pi-ai/oauth\": piAiOauthEntry,\n\t\t\"@mariozechner/pi-ai\": piAiCompatEntry,\n\t\ttypebox: typeboxEntry,\n\t\t\"typebox/compile\": typeboxCompileEntry,\n\t\t\"typebox/value\": typeboxValueEntry,\n\t\t\"@sinclair/typebox\": typeboxEntry,\n\t\t\"@sinclair/typebox/compile\": typeboxCompileEntry,\n\t\t\"@sinclair/typebox/value\": typeboxValueEntry,\n\t};\n\n\treturn _aliases;\n}\n\ntype HandlerFn = (...args: unknown[]) => Promise<unknown>;\n\nlet extensionCacheCwd: string | undefined;\nlet extensionCacheGeneration = 0;\nconst extensionCache = new Map<string, ExtensionFactory>();\n\ninterface ExtensionCacheToken {\n\tcwd: string;\n\tgeneration: number;\n}\n\nexport function clearExtensionCache(): void {\n\textensionCache.clear();\n\textensionCacheCwd = undefined;\n\textensionCacheGeneration++;\n}\n\nfunction useExtensionCacheCwd(cwd: string): ExtensionCacheToken {\n\tconst resolvedCwd = resolvePath(cwd);\n\tif (extensionCacheCwd !== undefined && extensionCacheCwd !== resolvedCwd) {\n\t\tclearExtensionCache();\n\t}\n\textensionCacheCwd = resolvedCwd;\n\treturn { cwd: resolvedCwd, generation: extensionCacheGeneration };\n}\n\n/**\n * Create a runtime with throwing stubs for action methods.\n * Runner.bindCore() replaces these with real implementations.\n */\nexport function createExtensionRuntime(): ExtensionRuntime {\n\tconst notInitialized = () => {\n\t\tthrow new Error(\"Extension runtime not initialized. Action methods cannot be called during extension loading.\");\n\t};\n\tconst state: { staleMessage?: string } = {};\n\tconst eventBusUnsubscribers = new Set<() => void>();\n\tconst assertActive = () => {\n\t\tif (state.staleMessage) {\n\t\t\tthrow new Error(state.staleMessage);\n\t\t}\n\t};\n\n\tconst runtime: ExtensionRuntime = {\n\t\tsendMessage: notInitialized,\n\t\tsendUserMessage: notInitialized,\n\t\tappendEntry: notInitialized,\n\t\tsetSessionName: notInitialized,\n\t\tgetSessionName: notInitialized,\n\t\tsetLabel: notInitialized,\n\t\tgetActiveTools: notInitialized,\n\t\tgetAllTools: notInitialized,\n\t\tsetActiveTools: notInitialized,\n\t\t// registerTool() is valid during extension load; refresh is only needed post-bind.\n\t\trefreshTools: () => {},\n\t\tgetCommands: notInitialized,\n\t\tsetModel: () => Promise.reject(new Error(\"Extension runtime not initialized\")),\n\t\tgetThinkingLevel: notInitialized,\n\t\tsetThinkingLevel: notInitialized,\n\t\tflagValues: new Map(),\n\t\tpendingProviderRegistrations: [],\n\t\tpendingNativeProviderRegistrations: [],\n\t\tassertActive,\n\t\tinvalidate: (message) => {\n\t\t\tif (state.staleMessage) return;\n\t\t\tstate.staleMessage =\n\t\t\t\tmessage ??\n\t\t\t\t\"This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().\";\n\t\t\tfor (const unsubscribe of eventBusUnsubscribers) unsubscribe();\n\t\t\teventBusUnsubscribers.clear();\n\t\t},\n\t\ttrackEventBusSubscription: (unsubscribe) => {\n\t\t\tlet active = true;\n\t\t\tconst trackedUnsubscribe = () => {\n\t\t\t\tif (!active) return;\n\t\t\t\tactive = false;\n\t\t\t\teventBusUnsubscribers.delete(trackedUnsubscribe);\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t\teventBusUnsubscribers.add(trackedUnsubscribe);\n\t\t\treturn trackedUnsubscribe;\n\t\t},\n\t\t// Pre-bind: queue registrations so bindCore() can flush them once the\n\t\t// model registry is available. bindCore() replaces both with direct calls.\n\t\tregisterProvider: (name, config, extensionPath = \"<unknown>\") => {\n\t\t\truntime.pendingProviderRegistrations.push({ name, config, extensionPath });\n\t\t},\n\t\tregisterNativeProvider: (provider, extensionPath = \"<unknown>\") => {\n\t\t\truntime.pendingNativeProviderRegistrations.push({ provider, extensionPath });\n\t\t},\n\t\tunregisterProvider: (name) => {\n\t\t\truntime.pendingProviderRegistrations = runtime.pendingProviderRegistrations.filter((r) => r.name !== name);\n\t\t\truntime.pendingNativeProviderRegistrations = runtime.pendingNativeProviderRegistrations.filter(\n\t\t\t\t(r) => r.provider.id !== name,\n\t\t\t);\n\t\t},\n\t};\n\n\treturn runtime;\n}\n\n/**\n * Create the ExtensionAPI for an extension.\n * Registration methods write to the extension object.\n * Action methods delegate to the shared runtime.\n */\nfunction createExtensionAPI(\n\textension: Extension,\n\truntime: ExtensionRuntime,\n\tcwd: string,\n\teventBus: EventBus,\n): { api: ExtensionAPI; commit: () => void; discard: () => void } {\n\tconst pendingFlagValues = new Map<string, boolean | string>();\n\tconst pendingRuntimeChanges: Array<() => void> = [];\n\tconst loadingUnsubscribers: Array<() => void> = [];\n\tlet state: \"loading\" | \"active\" | \"failed\" = \"loading\";\n\tconst assertActive = () => {\n\t\tif (state === \"failed\") {\n\t\t\tthrow new Error(`Extension \"${extension.path}\" failed to load and its API is no longer active.`);\n\t\t}\n\t\truntime.assertActive();\n\t};\n\tconst applyRuntimeChange = (change: () => void) => {\n\t\tif (state === \"loading\") pendingRuntimeChanges.push(change);\n\t\telse change();\n\t};\n\tconst clearPending = () => {\n\t\tpendingFlagValues.clear();\n\t\tpendingRuntimeChanges.length = 0;\n\t\tloadingUnsubscribers.length = 0;\n\t};\n\n\tconst api = {\n\t\t// Registration methods - write to extension\n\t\ton(event: string, handler: HandlerFn): void {\n\t\t\tassertActive();\n\t\t\tconst list = extension.handlers.get(event) ?? [];\n\t\t\tlist.push(handler);\n\t\t\textension.handlers.set(event, list);\n\t\t},\n\n\t\tregisterTool(tool: ToolDefinition): void {\n\t\t\tassertActive();\n\t\t\textension.tools.set(tool.name, {\n\t\t\t\tdefinition: tool,\n\t\t\t\tsourceInfo: extension.sourceInfo,\n\t\t\t});\n\t\t\truntime.refreshTools();\n\t\t},\n\n\t\tregisterCommand(name: string, options: Omit<RegisteredCommand, \"name\" | \"sourceInfo\">): void {\n\t\t\tassertActive();\n\t\t\textension.commands.set(name, {\n\t\t\t\tname,\n\t\t\t\tsourceInfo: extension.sourceInfo,\n\t\t\t\t...options,\n\t\t\t});\n\t\t},\n\n\t\tregisterShortcut(\n\t\t\tshortcut: KeyId,\n\t\t\toptions: {\n\t\t\t\tdescription?: string;\n\t\t\t\thandler: (ctx: import(\"./types.ts\").ExtensionContext) => Promise<void> | void;\n\t\t\t},\n\t\t): void {\n\t\t\tassertActive();\n\t\t\textension.shortcuts.set(shortcut, { shortcut, extensionPath: extension.path, ...options });\n\t\t},\n\n\t\tregisterFlag(\n\t\t\tname: string,\n\t\t\toptions: { description?: string; type: \"boolean\" | \"string\"; default?: boolean | string },\n\t\t): void {\n\t\t\tassertActive();\n\t\t\tif (options.default !== undefined && typeof options.default !== options.type) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid default for flag \"${name}\": expected ${options.type}, got ${typeof options.default}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\textension.flags.set(name, { name, extensionPath: extension.path, ...options });\n\t\t\tif (options.default !== undefined && !runtime.flagValues.has(name)) {\n\t\t\t\tif (state === \"loading\") {\n\t\t\t\t\tif (!pendingFlagValues.has(name)) pendingFlagValues.set(name, options.default);\n\t\t\t\t} else {\n\t\t\t\t\truntime.flagValues.set(name, options.default);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tregisterMessageRenderer<T>(customType: string, renderer: MessageRenderer<T>): void {\n\t\t\tassertActive();\n\t\t\textension.messageRenderers.set(customType, renderer as MessageRenderer);\n\t\t},\n\n\t\tregisterMarkdownTransformer(transformer: MarkdownTransformer): void {\n\t\t\tassertActive();\n\t\t\textension.markdownTransformer = transformer;\n\t\t},\n\n\t\tregisterEntryRenderer<T>(customType: string, renderer: EntryRenderer<T>): void {\n\t\t\tassertActive();\n\t\t\textension.entryRenderers ??= new Map();\n\t\t\textension.entryRenderers.set(customType, renderer as EntryRenderer);\n\t\t},\n\n\t\t// Flag access - checks extension registered it, reads from runtime\n\t\tgetFlag(name: string): boolean | string | undefined {\n\t\t\tassertActive();\n\t\t\tif (!extension.flags.has(name)) return undefined;\n\t\t\treturn runtime.flagValues.has(name) ? runtime.flagValues.get(name) : pendingFlagValues.get(name);\n\t\t},\n\n\t\t// Action methods - delegate to shared runtime\n\t\tsendMessage(message, options): void {\n\t\t\tassertActive();\n\t\t\truntime.sendMessage(message, options);\n\t\t},\n\n\t\tsendUserMessage(content, options): void {\n\t\t\tassertActive();\n\t\t\truntime.sendUserMessage(content, options);\n\t\t},\n\n\t\tappendEntry(customType: string, data?: unknown): void {\n\t\t\tassertActive();\n\t\t\truntime.appendEntry(customType, data);\n\t\t},\n\n\t\tsetSessionName(name: string): void {\n\t\t\tassertActive();\n\t\t\truntime.setSessionName(name);\n\t\t},\n\n\t\tgetSessionName(): string | undefined {\n\t\t\tassertActive();\n\t\t\treturn runtime.getSessionName();\n\t\t},\n\n\t\tsetLabel(entryId: string, label: string | undefined): void {\n\t\t\tassertActive();\n\t\t\truntime.setLabel(entryId, label);\n\t\t},\n\n\t\texec(command: string, args: string[], options?: ExecOptions) {\n\t\t\tassertActive();\n\t\t\treturn execCommand(command, args, options?.cwd ?? cwd, options);\n\t\t},\n\n\t\tgetActiveTools(): string[] {\n\t\t\tassertActive();\n\t\t\treturn runtime.getActiveTools();\n\t\t},\n\n\t\tgetAllTools() {\n\t\t\tassertActive();\n\t\t\treturn runtime.getAllTools();\n\t\t},\n\n\t\tsetActiveTools(toolNames: string[]): void {\n\t\t\tassertActive();\n\t\t\truntime.setActiveTools(toolNames);\n\t\t},\n\n\t\tgetCommands() {\n\t\t\tassertActive();\n\t\t\treturn runtime.getCommands();\n\t\t},\n\n\t\tsetModel(model) {\n\t\t\tassertActive();\n\t\t\treturn runtime.setModel(model);\n\t\t},\n\n\t\tgetThinkingLevel() {\n\t\t\tassertActive();\n\t\t\treturn runtime.getThinkingLevel();\n\t\t},\n\n\t\tsetThinkingLevel(level) {\n\t\t\tassertActive();\n\t\t\truntime.setThinkingLevel(level);\n\t\t},\n\n\t\tregisterProvider(providerOrName: Provider | string, config?: ProviderConfig) {\n\t\t\tassertActive();\n\t\t\tif (typeof providerOrName === \"string\") {\n\t\t\t\tif (!config) throw new Error(\"Provider config is required when registering by name\");\n\t\t\t\tapplyRuntimeChange(() => runtime.registerProvider(providerOrName, config, extension.path));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tapplyRuntimeChange(() => runtime.registerNativeProvider(providerOrName, extension.path));\n\t\t},\n\n\t\tunregisterProvider(name: string) {\n\t\t\tassertActive();\n\t\t\tapplyRuntimeChange(() => runtime.unregisterProvider(name, extension.path));\n\t\t},\n\n\t\tevents: {\n\t\t\temit(channel, data) {\n\t\t\t\tassertActive();\n\t\t\t\teventBus.emit(channel, data);\n\t\t\t},\n\t\t\ton(channel, handler) {\n\t\t\t\tassertActive();\n\t\t\t\tconst unsubscribe = runtime.trackEventBusSubscription(eventBus.on(channel, handler));\n\t\t\t\tif (state === \"loading\") loadingUnsubscribers.push(unsubscribe);\n\t\t\t\treturn unsubscribe;\n\t\t\t},\n\t\t},\n\t} as ExtensionAPI;\n\n\treturn {\n\t\tapi,\n\t\tcommit: () => {\n\t\t\tif (state !== \"loading\") return;\n\t\t\truntime.assertActive();\n\t\t\tfor (const [name, value] of pendingFlagValues) {\n\t\t\t\tif (!runtime.flagValues.has(name)) runtime.flagValues.set(name, value);\n\t\t\t}\n\t\t\tfor (const apply of pendingRuntimeChanges) apply();\n\t\t\tstate = \"active\";\n\t\t\tclearPending();\n\t\t},\n\t\tdiscard: () => {\n\t\t\tif (state !== \"loading\") return;\n\t\t\tstate = \"failed\";\n\t\t\tfor (const unsubscribe of loadingUnsubscribers) unsubscribe();\n\t\t\tclearPending();\n\t\t},\n\t};\n}\n\nfunction isCurrentCacheToken(cacheToken: ExtensionCacheToken | undefined): cacheToken is ExtensionCacheToken {\n\treturn (\n\t\tcacheToken !== undefined &&\n\t\textensionCacheCwd === cacheToken.cwd &&\n\t\textensionCacheGeneration === cacheToken.generation\n\t);\n}\n\nasync function loadExtensionModule(extensionPath: string, cacheToken?: ExtensionCacheToken) {\n\tif (isCurrentCacheToken(cacheToken)) {\n\t\tconst cachedFactory = extensionCache.get(extensionPath);\n\t\tif (cachedFactory) {\n\t\t\treturn cachedFactory;\n\t\t}\n\t}\n\n\tconst jiti = createJiti(import.meta.url, {\n\t\tmoduleCache: false,\n\t\t// Compiled binaries and the bundled Node distribution use embedded modules.\n\t\t// Source TypeScript reuses host modules and root tsconfig paths. Unbundled\n\t\t// Node builds use dist aliases.\n\t\t...(isBunBinary || isNodeSeaBinary || isBundledNode\n\t\t\t? { virtualModules: VIRTUAL_MODULES, tryNative: false }\n\t\t\t: isTypeScriptSourceRuntime\n\t\t\t\t? { virtualModules: VIRTUAL_MODULES, tsconfigPaths: true }\n\t\t\t\t: { alias: getAliases() }),\n\t});\n\n\tconst module = await jiti.import(extensionPath, { default: true });\n\tconst factory = module as ExtensionFactory;\n\tif (typeof factory !== \"function\") {\n\t\treturn undefined;\n\t}\n\tif (isCurrentCacheToken(cacheToken)) {\n\t\textensionCache.set(extensionPath, factory);\n\t}\n\treturn factory;\n}\n\n/**\n * Create an Extension object with empty collections.\n */\nfunction createExtension(extensionPath: string, resolvedPath: string): Extension {\n\tconst source =\n\t\textensionPath.startsWith(\"<\") && extensionPath.endsWith(\">\")\n\t\t\t? extensionPath.slice(1, -1).split(\":\")[0] || \"temporary\"\n\t\t\t: \"local\";\n\tconst baseDir = extensionPath.startsWith(\"<\") ? undefined : path.dirname(resolvedPath);\n\n\treturn {\n\t\tpath: extensionPath,\n\t\tresolvedPath,\n\t\tsourceInfo: createSyntheticSourceInfo(extensionPath, { source, baseDir }),\n\t\thandlers: new Map(),\n\t\ttools: new Map(),\n\t\tmessageRenderers: new Map(),\n\t\tentryRenderers: new Map(),\n\t\tcommands: new Map(),\n\t\tflags: new Map(),\n\t\tshortcuts: new Map(),\n\t};\n}\n\nasync function initializeExtension(\n\tfactory: ExtensionFactory,\n\textensionPath: string,\n\tresolvedPath: string,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n): Promise<Extension> {\n\tconst extension = createExtension(extensionPath, resolvedPath);\n\tconst load = createExtensionAPI(extension, runtime, cwd, eventBus);\n\ttry {\n\t\tawait factory(load.api);\n\t\tload.commit();\n\t} catch (error) {\n\t\tload.discard();\n\t\tthrow error;\n\t}\n\ttime(`${extensionPath} factory`, \"extensions\");\n\treturn extension;\n}\n\nasync function loadExtension(\n\textensionPath: string,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n\tcacheToken?: ExtensionCacheToken,\n): Promise<{ extension: Extension | null; error: string | null }> {\n\tconst resolvedPath = resolvePath(extensionPath, cwd, { normalizeUnicodeSpaces: true });\n\n\ttry {\n\t\tconst factory = await loadExtensionModule(resolvedPath, cacheToken);\n\t\ttime(`${extensionPath} module import`, \"extensions\");\n\t\tif (!factory) {\n\t\t\treturn { extension: null, error: `Extension does not export a valid factory function: ${extensionPath}` };\n\t\t}\n\n\t\tconst extension = await initializeExtension(factory, extensionPath, resolvedPath, cwd, eventBus, runtime);\n\n\t\treturn { extension, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn { extension: null, error: `Failed to load extension: ${message}` };\n\t}\n}\n\n/**\n * Create an Extension from an inline factory function.\n */\nexport async function loadExtensionFromFactory(\n\tfactory: ExtensionFactory,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n\textensionPath = \"<inline>\",\n): Promise<Extension> {\n\tconst resolvedCwd = resolvePath(cwd);\n\treturn initializeExtension(factory, extensionPath, extensionPath, resolvedCwd, eventBus, runtime);\n}\n\n/**\n * Load extensions from paths.\n */\nasync function loadExtensionsInternal(\n\tpaths: string[],\n\tcwd: string,\n\teventBus?: EventBus,\n\truntime?: ExtensionRuntime,\n\tuseCache = false,\n): Promise<LoadExtensionsResult> {\n\tconst extensions: Extension[] = [];\n\tconst errors: Array<{ path: string; error: string }> = [];\n\tconst cacheToken = useCache ? useExtensionCacheCwd(cwd) : undefined;\n\tconst resolvedCwd = cacheToken?.cwd ?? resolvePath(cwd);\n\tconst resolvedEventBus = eventBus ?? createEventBus();\n\tconst resolvedRuntime = runtime ?? createExtensionRuntime();\n\n\tfor (const extPath of paths) {\n\t\tconst { extension, error } = await loadExtension(\n\t\t\textPath,\n\t\t\tresolvedCwd,\n\t\t\tresolvedEventBus,\n\t\t\tresolvedRuntime,\n\t\t\tcacheToken,\n\t\t);\n\n\t\tif (error) {\n\t\t\terrors.push({ path: extPath, error });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (extension) {\n\t\t\textensions.push(extension);\n\t\t}\n\t}\n\n\treturn {\n\t\textensions,\n\t\terrors,\n\t\truntime: resolvedRuntime,\n\t};\n}\n\nexport async function loadExtensions(\n\tpaths: string[],\n\tcwd: string,\n\teventBus?: EventBus,\n\truntime?: ExtensionRuntime,\n): Promise<LoadExtensionsResult> {\n\treturn loadExtensionsInternal(paths, cwd, eventBus, runtime);\n}\n\nexport async function loadExtensionsCached(\n\tpaths: string[],\n\tcwd: string,\n\teventBus?: EventBus,\n\truntime?: ExtensionRuntime,\n): Promise<LoadExtensionsResult> {\n\treturn loadExtensionsInternal(paths, cwd, eventBus, runtime, true);\n}\n\nfunction isExtensionFile(name: string): boolean {\n\treturn name.endsWith(\".ts\") || name.endsWith(\".js\");\n}\n\n/**\n * Resolve extension entry points from a directory.\n *\n * Checks for:\n * 1. package.json with \"pi.extensions\" field -> returns declared paths\n * 2. index.ts or index.js -> returns the index file\n *\n * Returns resolved paths or null if no entry points found.\n */\nfunction resolveExtensionEntries(dir: string): string[] | null {\n\t// Check for package.json with \"pi\" field first\n\tconst packageJsonPath = path.join(dir, \"package.json\");\n\tif (fs.existsSync(packageJsonPath)) {\n\t\tconst manifest = readPiManifest(packageJsonPath);\n\t\tif (manifest?.extensions?.length) {\n\t\t\tconst entries: string[] = [];\n\t\t\tfor (const extPath of manifest.extensions) {\n\t\t\t\tconst resolvedExtPath = path.resolve(dir, extPath);\n\t\t\t\tif (fs.existsSync(resolvedExtPath)) {\n\t\t\t\t\tentries.push(resolvedExtPath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (entries.length > 0) {\n\t\t\t\treturn entries;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check for index.ts or index.js\n\tconst indexTs = path.join(dir, \"index.ts\");\n\tconst indexJs = path.join(dir, \"index.js\");\n\tif (fs.existsSync(indexTs)) {\n\t\treturn [indexTs];\n\t}\n\tif (fs.existsSync(indexJs)) {\n\t\treturn [indexJs];\n\t}\n\n\treturn null;\n}\n\n/**\n * Discover extensions in a directory.\n *\n * Discovery rules:\n * 1. Direct files: `extensions/*.ts` or `*.js` → load\n * 2. Subdirectory with index: `extensions/* /index.ts` or `index.js` → load\n * 3. Subdirectory with package.json: `extensions/* /package.json` with \"pi\" field → load what it declares\n *\n * No recursion beyond one level. Complex packages must use package.json manifest.\n */\nfunction discoverExtensionsInDir(dir: string): string[] {\n\tif (!fs.existsSync(dir)) {\n\t\treturn [];\n\t}\n\n\tconst discovered: string[] = [];\n\n\ttry {\n\t\tconst entries = fs.readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst entryPath = path.join(dir, entry.name);\n\n\t\t\t// 1. Direct files: *.ts or *.js\n\t\t\tif ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {\n\t\t\t\tdiscovered.push(entryPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// 2 & 3. Subdirectories\n\t\t\tif (entry.isDirectory() || entry.isSymbolicLink()) {\n\t\t\t\tconst entries = resolveExtensionEntries(entryPath);\n\t\t\t\tif (entries) {\n\t\t\t\t\tdiscovered.push(...entries);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn [];\n\t}\n\n\treturn discovered;\n}\n\n/**\n * Discover and load extensions from standard locations.\n */\nexport async function discoverAndLoadExtensions(\n\tconfiguredPaths: string[],\n\tcwd: string,\n\tagentDir: string = getAgentDir(),\n\teventBus?: EventBus,\n): Promise<LoadExtensionsResult> {\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst resolvedAgentDir = resolvePath(agentDir);\n\tconst allPaths: string[] = [];\n\tconst seen = new Set<string>();\n\n\tconst addPaths = (paths: string[]) => {\n\t\tfor (const p of paths) {\n\t\t\tconst resolved = path.resolve(p);\n\t\t\tif (!seen.has(resolved)) {\n\t\t\t\tseen.add(resolved);\n\t\t\t\tallPaths.push(p);\n\t\t\t}\n\t\t}\n\t};\n\n\t// 1. Project-local extensions: cwd/${CONFIG_DIR_NAME}/extensions/\n\tconst localExtDir = path.join(resolvedCwd, CONFIG_DIR_NAME, \"extensions\");\n\taddPaths(discoverExtensionsInDir(localExtDir));\n\n\t// 2. Global extensions: agentDir/extensions/\n\tconst globalExtDir = path.join(resolvedAgentDir, \"extensions\");\n\taddPaths(discoverExtensionsInDir(globalExtDir));\n\n\t// 3. Explicitly configured paths\n\tfor (const p of configuredPaths) {\n\t\tconst resolved = resolvePath(p, resolvedCwd, { normalizeUnicodeSpaces: true });\n\t\tif (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {\n\t\t\t// Check for package.json with pi manifest or index.ts\n\t\t\tconst entries = resolveExtensionEntries(resolved);\n\t\t\tif (entries) {\n\t\t\t\taddPaths(entries);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// No explicit entries - discover individual files in directory\n\t\t\taddPaths(discoverExtensionsInDir(resolved));\n\t\t\tcontinue;\n\t\t}\n\n\t\taddPaths([resolved]);\n\t}\n\n\treturn loadExtensions(allPaths, resolvedCwd, eventBus);\n}\n"]}
|