@caupulican/pi-adaptative 0.81.37 → 0.81.38
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 +12 -0
- package/README.md +1 -1
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +1 -1
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
- package/dist/core/context/sqlite-runtime-index.js +13 -7
- package/dist/core/context/sqlite-runtime-index.js.map +1 -1
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +1 -1
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +3 -7
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +4 -3
- package/dist/core/models/local-runtime.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +52 -44
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +5 -4
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
- package/dist/core/tools/shell-contract-router.js +46 -11
- package/dist/core/tools/shell-contract-router.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +18 -3
- package/dist/utils/shell.js.map +1 -1
- package/docs/development.md +2 -0
- package/docs/packages.md +1 -1
- package/docs/settings.md +2 -2
- 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/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-runtime-index.js","sourceRoot":"","sources":["../../../src/core/context/sqlite-runtime-index.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport type { ContextItem } from \"./context-item.ts\";\nimport type { ContextStore, PolicyDecisionRecord, RetrievalRecord } from \"./context-store.ts\";\nimport type { MemoryIndexRecord, MemoryIndexStore } from \"./memory-index-store.ts\";\nimport type { PolicyDecision } from \"./policy-types.ts\";\n\nexport const SQLITE_RUNTIME_INDEX_SCHEMA_VERSION = 1;\n\nexport const SQLITE_CONTEXT_INDEX_TABLES = [\n\t\"context_items\",\n\t\"artifact_metadata\",\n\t\"policy_score_cache\",\n\t\"policy_decisions\",\n\t\"retrieval_records\",\n\t\"memory_index\",\n] as const;\n\nexport interface SqliteRuntimeIndexOptions {\n\tdatabasePath: string;\n\tbusyTimeoutMs?: number;\n}\n\nexport interface SqliteContextStore extends ContextStore {\n\tclose(): void;\n}\n\nexport interface SqliteMemoryIndexStore extends MemoryIndexStore {\n\tclose(): void;\n}\n\nfunction prepareDatabasePath(databasePath: string): void {\n\tif (databasePath === \":memory:\") return;\n\tmkdirSync(dirname(databasePath), { recursive: true });\n}\n\nfunction openDatabase(options: SqliteRuntimeIndexOptions): DatabaseSync {\n\tprepareDatabasePath(options.databasePath);\n\tconst database = new DatabaseSync(options.databasePath, {\n\t\tenableForeignKeyConstraints: true,\n\t\ttimeout: options.busyTimeoutMs ?? 5_000,\n\t});\n\tdatabase.exec(`\n\t\tPRAGMA foreign_keys = ON;\n\t\tPRAGMA busy_timeout = ${options.busyTimeoutMs ?? 5_000};\n\t\tPRAGMA journal_mode = WAL;\n\t`);\n\tmigrateSqliteRuntimeIndex(database);\n\treturn database;\n}\n\nfunction currentSchemaVersion(database: DatabaseSync): number {\n\tconst row = database.prepare(\"PRAGMA user_version\").get();\n\tconst value = row?.user_version;\n\treturn typeof value === \"number\" ? value : 0;\n}\n\nexport function migrateSqliteRuntimeIndex(database: DatabaseSync): void {\n\tconst version = currentSchemaVersion(database);\n\tif (version > SQLITE_RUNTIME_INDEX_SCHEMA_VERSION) {\n\t\tthrow new Error(\n\t\t\t`Unsupported Pi context runtime-index schema version ${version}; current runtime supports ${SQLITE_RUNTIME_INDEX_SCHEMA_VERSION}`,\n\t\t);\n\t}\n\n\ttry {\n\t\tdatabase.exec(`\n\t\t\tBEGIN IMMEDIATE;\n\n\t\t\tCREATE TABLE IF NOT EXISTS context_items (\n\t\t\t\tid TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tkind TEXT NOT NULL,\n\t\t\t\tretention_class TEXT NOT NULL,\n\t\t\t\tsource TEXT NOT NULL,\n\t\t\t\tcreated_at_turn INTEGER NOT NULL,\n\t\t\t\tlast_used_at_turn INTEGER,\n\t\t\t\ttoken_estimate INTEGER NOT NULL,\n\t\t\t\tbyte_estimate INTEGER NOT NULL,\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE TABLE IF NOT EXISTS artifact_metadata (\n\t\t\t\tid TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tkind TEXT NOT NULL,\n\t\t\t\tstorage_path TEXT,\n\t\t\t\tsession_entry_id TEXT,\n\t\t\t\ttool_name TEXT,\n\t\t\t\tcommand TEXT,\n\t\t\t\tpath TEXT,\n\t\t\t\tbyte_length INTEGER NOT NULL,\n\t\t\t\tline_count INTEGER,\n\t\t\t\tcreated_at_turn INTEGER NOT NULL,\n\t\t\t\treproducible INTEGER NOT NULL CHECK (reproducible IN (0, 1)),\n\t\t\t\tpayload_exists INTEGER NOT NULL DEFAULT 1 CHECK (payload_exists IN (0, 1)),\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE TABLE IF NOT EXISTS policy_score_cache (\n\t\t\t\tcache_key TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tcreated_at_turn INTEGER NOT NULL,\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE TABLE IF NOT EXISTS policy_decisions (\n\t\t\t\tid TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tcontext_item_id TEXT,\n\t\t\t\trecorded_at_turn INTEGER NOT NULL,\n\t\t\t\tdecision_kind TEXT NOT NULL,\n\t\t\t\tselected_action TEXT NOT NULL,\n\t\t\t\tmode TEXT NOT NULL,\n\t\t\t\tapplied INTEGER NOT NULL CHECK (applied IN (0, 1)),\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE INDEX IF NOT EXISTS idx_policy_decisions_context_item_id\n\t\t\t\tON policy_decisions(context_item_id, recorded_at_turn);\n\n\t\t\tCREATE TABLE IF NOT EXISTS retrieval_records (\n\t\t\t\tid TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tcontext_item_id TEXT NOT NULL,\n\t\t\t\trequested_at_turn INTEGER NOT NULL,\n\t\t\t\tslice_kind TEXT NOT NULL,\n\t\t\t\tresult_summary TEXT NOT NULL,\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE INDEX IF NOT EXISTS idx_retrieval_records_context_item_id\n\t\t\t\tON retrieval_records(context_item_id, requested_at_turn);\n\n\t\t\tCREATE TABLE IF NOT EXISTS memory_index (\n\t\t\t\tprovider_id TEXT NOT NULL,\n\t\t\t\titem_id TEXT NOT NULL,\n\t\t\t\tscope TEXT NOT NULL,\n\t\t\t\tkind TEXT NOT NULL,\n\t\t\t\ttitle TEXT,\n\t\t\t\tsummary TEXT NOT NULL,\n\t\t\t\tindexed_at_turn INTEGER NOT NULL,\n\t\t\t\tstale INTEGER NOT NULL CHECK (stale IN (0, 1)),\n\t\t\t\tpayload_json TEXT NOT NULL,\n\t\t\t\tPRIMARY KEY (provider_id, item_id)\n\t\t\t) STRICT;\n\n\t\t\tCREATE INDEX IF NOT EXISTS idx_memory_index_scope\n\t\t\t\tON memory_index(scope, provider_id, item_id);\n\n\t\t\tPRAGMA user_version = ${SQLITE_RUNTIME_INDEX_SCHEMA_VERSION};\n\t\t\tCOMMIT;\n\t\t`);\n\t} catch (error) {\n\t\tif (database.isTransaction) database.exec(\"ROLLBACK;\");\n\t\tthrow error;\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseJsonRecord<T>(json: string, isValid: (value: unknown) => value is T): T | undefined {\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(json);\n\t\treturn isValid(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction textColumn(row: Record<string, unknown> | undefined, column: string): string | undefined {\n\tconst value = row?.[column];\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction isContextItem(value: unknown): value is ContextItem {\n\tif (!isRecord(value)) return false;\n\treturn (\n\t\ttypeof value.id === \"string\" &&\n\t\ttypeof value.kind === \"string\" &&\n\t\ttypeof value.retentionClass === \"string\" &&\n\t\ttypeof value.source === \"string\" &&\n\t\ttypeof value.createdAtTurn === \"number\" &&\n\t\ttypeof value.tokenEstimate === \"number\" &&\n\t\ttypeof value.byteEstimate === \"number\"\n\t);\n}\n\nfunction isPolicyDecision(value: unknown): value is PolicyDecision {\n\tif (!isRecord(value)) return false;\n\treturn (\n\t\ttypeof value.kind === \"string\" &&\n\t\ttypeof value.selectedAction === \"string\" &&\n\t\ttypeof value.mode === \"string\" &&\n\t\ttypeof value.applied === \"boolean\" &&\n\t\tArray.isArray(value.hardConstraints) &&\n\t\tArray.isArray(value.candidates) &&\n\t\tArray.isArray(value.selectedReasonCodes) &&\n\t\ttypeof value.estimatedCostTokens === \"number\" &&\n\t\ttypeof value.estimatedSavingsTokens === \"number\" &&\n\t\ttypeof value.estimatedReliabilityRisk === \"number\" &&\n\t\ttypeof value.cacheImpactTokens === \"number\" &&\n\t\ttypeof value.reworkRiskTokens === \"number\" &&\n\t\tArray.isArray(value.artifactRefs) &&\n\t\tArray.isArray(value.evidenceRefs)\n\t);\n}\n\nfunction isPolicyDecisionRecord(value: unknown): value is PolicyDecisionRecord {\n\tif (!isRecord(value)) return false;\n\treturn (\n\t\ttypeof value.id === \"string\" &&\n\t\t(value.contextItemId === undefined || typeof value.contextItemId === \"string\") &&\n\t\ttypeof value.recordedAtTurn === \"number\" &&\n\t\tisPolicyDecision(value.decision)\n\t);\n}\n\nfunction isRetrievalRecord(value: unknown): value is RetrievalRecord {\n\tif (!isRecord(value)) return false;\n\treturn (\n\t\ttypeof value.id === \"string\" &&\n\t\ttypeof value.contextItemId === \"string\" &&\n\t\ttypeof value.requestedAtTurn === \"number\" &&\n\t\ttypeof value.sliceKind === \"string\" &&\n\t\ttypeof value.resultSummary === \"string\"\n\t);\n}\n\nfunction isMemoryIndexRecord(value: unknown): value is MemoryIndexRecord {\n\tif (!isRecord(value) || !isRecord(value.ref)) return false;\n\treturn (\n\t\ttypeof value.ref.providerId === \"string\" &&\n\t\ttypeof value.ref.itemId === \"string\" &&\n\t\ttypeof value.ref.scope === \"string\" &&\n\t\ttypeof value.ref.kind === \"string\" &&\n\t\t(value.title === undefined || typeof value.title === \"string\") &&\n\t\ttypeof value.summary === \"string\" &&\n\t\ttypeof value.indexedAtTurn === \"number\" &&\n\t\ttypeof value.stale === \"boolean\"\n\t);\n}\n\nfunction contextItemFromRow(row: Record<string, unknown> | undefined): ContextItem | undefined {\n\tconst payload = textColumn(row, \"payload_json\");\n\treturn payload === undefined ? undefined : parseJsonRecord(payload, isContextItem);\n}\n\nfunction policyDecisionRecordFromRow(row: Record<string, unknown> | undefined): PolicyDecisionRecord | undefined {\n\tconst payload = textColumn(row, \"payload_json\");\n\treturn payload === undefined ? undefined : parseJsonRecord(payload, isPolicyDecisionRecord);\n}\n\nfunction retrievalRecordFromRow(row: Record<string, unknown> | undefined): RetrievalRecord | undefined {\n\tconst payload = textColumn(row, \"payload_json\");\n\treturn payload === undefined ? undefined : parseJsonRecord(payload, isRetrievalRecord);\n}\n\nfunction memoryIndexRecordFromRow(row: Record<string, unknown> | undefined): MemoryIndexRecord | undefined {\n\tconst payload = textColumn(row, \"payload_json\");\n\treturn payload === undefined ? undefined : parseJsonRecord(payload, isMemoryIndexRecord);\n}\n\nexport function createSqliteContextStore(options: SqliteRuntimeIndexOptions): SqliteContextStore {\n\tconst database = openDatabase(options);\n\tconst upsertItem = database.prepare(`\n\t\tINSERT INTO context_items (\n\t\t\tid, kind, retention_class, source, created_at_turn, last_used_at_turn, token_estimate, byte_estimate, payload_json\n\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(id) DO UPDATE SET\n\t\t\tkind = excluded.kind,\n\t\t\tretention_class = excluded.retention_class,\n\t\t\tsource = excluded.source,\n\t\t\tcreated_at_turn = excluded.created_at_turn,\n\t\t\tlast_used_at_turn = excluded.last_used_at_turn,\n\t\t\ttoken_estimate = excluded.token_estimate,\n\t\t\tbyte_estimate = excluded.byte_estimate,\n\t\t\tpayload_json = excluded.payload_json\n\t`);\n\tconst getItem = database.prepare(\"SELECT payload_json FROM context_items WHERE id = ?\");\n\tconst listItems = database.prepare(\"SELECT payload_json FROM context_items ORDER BY created_at_turn, id\");\n\tconst removeItem = database.prepare(\"DELETE FROM context_items WHERE id = ?\");\n\tconst recordPolicyDecision = database.prepare(`\n\t\tINSERT INTO policy_decisions (\n\t\t\tid, context_item_id, recorded_at_turn, decision_kind, selected_action, mode, applied, payload_json\n\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n\t`);\n\tconst listAllPolicyDecisions = database.prepare(\n\t\t\"SELECT payload_json FROM policy_decisions ORDER BY recorded_at_turn, id\",\n\t);\n\tconst listPolicyDecisionsByItem = database.prepare(\n\t\t\"SELECT payload_json FROM policy_decisions WHERE context_item_id = ? ORDER BY recorded_at_turn, id\",\n\t);\n\tconst recordRetrieval = database.prepare(`\n\t\tINSERT INTO retrieval_records (\n\t\t\tid, context_item_id, requested_at_turn, slice_kind, result_summary, payload_json\n\t\t) VALUES (?, ?, ?, ?, ?, ?)\n\t`);\n\tconst listAllRetrievals = database.prepare(\n\t\t\"SELECT payload_json FROM retrieval_records ORDER BY requested_at_turn, id\",\n\t);\n\tconst listRetrievalsByItem = database.prepare(\n\t\t\"SELECT payload_json FROM retrieval_records WHERE context_item_id = ? ORDER BY requested_at_turn, id\",\n\t);\n\n\treturn {\n\t\tupsertItem(item: ContextItem): void {\n\t\t\tupsertItem.run(\n\t\t\t\titem.id,\n\t\t\t\titem.kind,\n\t\t\t\titem.retentionClass,\n\t\t\t\titem.source,\n\t\t\t\titem.createdAtTurn,\n\t\t\t\titem.lastUsedAtTurn ?? null,\n\t\t\t\titem.tokenEstimate,\n\t\t\t\titem.byteEstimate,\n\t\t\t\tJSON.stringify(item),\n\t\t\t);\n\t\t},\n\n\t\tgetItem(id: string): ContextItem | undefined {\n\t\t\treturn contextItemFromRow(getItem.get(id));\n\t\t},\n\n\t\tlistItems(): ContextItem[] {\n\t\t\treturn listItems.all().flatMap((row) => {\n\t\t\t\tconst item = contextItemFromRow(row);\n\t\t\t\treturn item === undefined ? [] : [item];\n\t\t\t});\n\t\t},\n\n\t\tremoveItem(id: string): void {\n\t\t\tremoveItem.run(id);\n\t\t},\n\n\t\trecordPolicyDecision(record: PolicyDecisionRecord): void {\n\t\t\trecordPolicyDecision.run(\n\t\t\t\trecord.id,\n\t\t\t\trecord.contextItemId ?? null,\n\t\t\t\trecord.recordedAtTurn,\n\t\t\t\trecord.decision.kind,\n\t\t\t\trecord.decision.selectedAction,\n\t\t\t\trecord.decision.mode,\n\t\t\t\trecord.decision.applied ? 1 : 0,\n\t\t\t\tJSON.stringify(record),\n\t\t\t);\n\t\t},\n\n\t\tlistPolicyDecisions(contextItemId?: string): PolicyDecisionRecord[] {\n\t\t\tconst rows =\n\t\t\t\tcontextItemId === undefined ? listAllPolicyDecisions.all() : listPolicyDecisionsByItem.all(contextItemId);\n\t\t\treturn rows.flatMap((row) => {\n\t\t\t\tconst record = policyDecisionRecordFromRow(row);\n\t\t\t\treturn record === undefined ? [] : [record];\n\t\t\t});\n\t\t},\n\n\t\trecordRetrieval(record: RetrievalRecord): void {\n\t\t\trecordRetrieval.run(\n\t\t\t\trecord.id,\n\t\t\t\trecord.contextItemId,\n\t\t\t\trecord.requestedAtTurn,\n\t\t\t\trecord.sliceKind,\n\t\t\t\trecord.resultSummary,\n\t\t\t\tJSON.stringify(record),\n\t\t\t);\n\t\t},\n\n\t\tlistRetrievals(contextItemId?: string): RetrievalRecord[] {\n\t\t\tconst rows = contextItemId === undefined ? listAllRetrievals.all() : listRetrievalsByItem.all(contextItemId);\n\t\t\treturn rows.flatMap((row) => {\n\t\t\t\tconst record = retrievalRecordFromRow(row);\n\t\t\t\treturn record === undefined ? [] : [record];\n\t\t\t});\n\t\t},\n\n\t\tclose(): void {\n\t\t\tdatabase[Symbol.dispose]();\n\t\t},\n\t};\n}\n\nexport function createSqliteMemoryIndexStore(options: SqliteRuntimeIndexOptions): SqliteMemoryIndexStore {\n\tconst database = openDatabase(options);\n\tconst upsert = database.prepare(`\n\t\tINSERT INTO memory_index (\n\t\t\tprovider_id, item_id, scope, kind, title, summary, indexed_at_turn, stale, payload_json\n\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(provider_id, item_id) DO UPDATE SET\n\t\t\tscope = excluded.scope,\n\t\t\tkind = excluded.kind,\n\t\t\ttitle = excluded.title,\n\t\t\tsummary = excluded.summary,\n\t\t\tindexed_at_turn = excluded.indexed_at_turn,\n\t\t\tstale = excluded.stale,\n\t\t\tpayload_json = excluded.payload_json\n\t`);\n\tconst get = database.prepare(\"SELECT payload_json FROM memory_index WHERE provider_id = ? AND item_id = ?\");\n\tconst listAll = database.prepare(\"SELECT payload_json FROM memory_index ORDER BY provider_id, item_id\");\n\tconst listByScope = database.prepare(\n\t\t\"SELECT payload_json FROM memory_index WHERE scope = ? ORDER BY provider_id, item_id\",\n\t);\n\tconst markStale = database.prepare(\n\t\t\"UPDATE memory_index SET stale = 1, payload_json = ? WHERE provider_id = ? AND item_id = ?\",\n\t);\n\tconst remove = database.prepare(\"DELETE FROM memory_index WHERE provider_id = ? AND item_id = ?\");\n\n\treturn {\n\t\tupsert(record: MemoryIndexRecord): void {\n\t\t\tupsert.run(\n\t\t\t\trecord.ref.providerId,\n\t\t\t\trecord.ref.itemId,\n\t\t\t\trecord.ref.scope,\n\t\t\t\trecord.ref.kind,\n\t\t\t\trecord.title ?? null,\n\t\t\t\trecord.summary,\n\t\t\t\trecord.indexedAtTurn,\n\t\t\t\trecord.stale ? 1 : 0,\n\t\t\t\tJSON.stringify(record),\n\t\t\t);\n\t\t},\n\n\t\tget(providerId: string, itemId: string): MemoryIndexRecord | undefined {\n\t\t\treturn memoryIndexRecordFromRow(get.get(providerId, itemId));\n\t\t},\n\n\t\tlist(scope?: MemoryIndexRecord[\"ref\"][\"scope\"]): MemoryIndexRecord[] {\n\t\t\tconst rows = scope === undefined ? listAll.all() : listByScope.all(scope);\n\t\t\treturn rows.flatMap((row) => {\n\t\t\t\tconst record = memoryIndexRecordFromRow(row);\n\t\t\t\treturn record === undefined ? [] : [record];\n\t\t\t});\n\t\t},\n\n\t\tmarkStale(providerId: string, itemId: string): void {\n\t\t\tconst existing = memoryIndexRecordFromRow(get.get(providerId, itemId));\n\t\t\tif (existing === undefined) return;\n\t\t\tmarkStale.run(JSON.stringify({ ...existing, stale: true }), providerId, itemId);\n\t\t},\n\n\t\tremove(providerId: string, itemId: string): void {\n\t\t\tremove.run(providerId, itemId);\n\t\t},\n\n\t\tclose(): void {\n\t\t\tdatabase[Symbol.dispose]();\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sqlite-runtime-index.js","sourceRoot":"","sources":["../../../src/core/context/sqlite-runtime-index.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport type { ContextItem } from \"./context-item.ts\";\nimport type { ContextStore, PolicyDecisionRecord, RetrievalRecord } from \"./context-store.ts\";\nimport type { MemoryIndexRecord, MemoryIndexStore } from \"./memory-index-store.ts\";\nimport type { PolicyDecision } from \"./policy-types.ts\";\n\nexport const SQLITE_RUNTIME_INDEX_SCHEMA_VERSION = 1;\n\nexport const SQLITE_CONTEXT_INDEX_TABLES = [\n\t\"context_items\",\n\t\"artifact_metadata\",\n\t\"policy_score_cache\",\n\t\"policy_decisions\",\n\t\"retrieval_records\",\n\t\"memory_index\",\n] as const;\n\nexport interface SqliteRuntimeIndexOptions {\n\tdatabasePath: string;\n\tbusyTimeoutMs?: number;\n}\n\nexport interface SqliteContextStore extends ContextStore {\n\tclose(): void;\n}\n\nexport interface SqliteMemoryIndexStore extends MemoryIndexStore {\n\tclose(): void;\n}\n\nfunction prepareDatabasePath(databasePath: string): void {\n\tif (databasePath === \":memory:\") return;\n\tmkdirSync(dirname(databasePath), { recursive: true });\n}\n\nfunction openDatabase(options: SqliteRuntimeIndexOptions): DatabaseSync {\n\tprepareDatabasePath(options.databasePath);\n\tconst database = new DatabaseSync(options.databasePath, {\n\t\tenableForeignKeyConstraints: true,\n\t\ttimeout: options.busyTimeoutMs ?? 5_000,\n\t});\n\ttry {\n\t\tdatabase.exec(`\n\t\t\tPRAGMA foreign_keys = ON;\n\t\t\tPRAGMA busy_timeout = ${options.busyTimeoutMs ?? 5_000};\n\t\t\tPRAGMA journal_mode = WAL;\n\t\t`);\n\t\tmigrateSqliteRuntimeIndex(database);\n\t\treturn database;\n\t} catch (error) {\n\t\tdatabase[Symbol.dispose]();\n\t\tthrow error;\n\t}\n}\n\nfunction currentSchemaVersion(database: DatabaseSync): number {\n\tconst row = database.prepare(\"PRAGMA user_version\").get();\n\tconst value = row?.user_version;\n\treturn typeof value === \"number\" ? value : 0;\n}\n\nexport function migrateSqliteRuntimeIndex(database: DatabaseSync): void {\n\tconst version = currentSchemaVersion(database);\n\tif (version > SQLITE_RUNTIME_INDEX_SCHEMA_VERSION) {\n\t\tthrow new Error(\n\t\t\t`Unsupported Pi context runtime-index schema version ${version}; current runtime supports ${SQLITE_RUNTIME_INDEX_SCHEMA_VERSION}`,\n\t\t);\n\t}\n\n\ttry {\n\t\tdatabase.exec(`\n\t\t\tBEGIN IMMEDIATE;\n\n\t\t\tCREATE TABLE IF NOT EXISTS context_items (\n\t\t\t\tid TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tkind TEXT NOT NULL,\n\t\t\t\tretention_class TEXT NOT NULL,\n\t\t\t\tsource TEXT NOT NULL,\n\t\t\t\tcreated_at_turn INTEGER NOT NULL,\n\t\t\t\tlast_used_at_turn INTEGER,\n\t\t\t\ttoken_estimate INTEGER NOT NULL,\n\t\t\t\tbyte_estimate INTEGER NOT NULL,\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE TABLE IF NOT EXISTS artifact_metadata (\n\t\t\t\tid TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tkind TEXT NOT NULL,\n\t\t\t\tstorage_path TEXT,\n\t\t\t\tsession_entry_id TEXT,\n\t\t\t\ttool_name TEXT,\n\t\t\t\tcommand TEXT,\n\t\t\t\tpath TEXT,\n\t\t\t\tbyte_length INTEGER NOT NULL,\n\t\t\t\tline_count INTEGER,\n\t\t\t\tcreated_at_turn INTEGER NOT NULL,\n\t\t\t\treproducible INTEGER NOT NULL CHECK (reproducible IN (0, 1)),\n\t\t\t\tpayload_exists INTEGER NOT NULL DEFAULT 1 CHECK (payload_exists IN (0, 1)),\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE TABLE IF NOT EXISTS policy_score_cache (\n\t\t\t\tcache_key TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tcreated_at_turn INTEGER NOT NULL,\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE TABLE IF NOT EXISTS policy_decisions (\n\t\t\t\tid TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tcontext_item_id TEXT,\n\t\t\t\trecorded_at_turn INTEGER NOT NULL,\n\t\t\t\tdecision_kind TEXT NOT NULL,\n\t\t\t\tselected_action TEXT NOT NULL,\n\t\t\t\tmode TEXT NOT NULL,\n\t\t\t\tapplied INTEGER NOT NULL CHECK (applied IN (0, 1)),\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE INDEX IF NOT EXISTS idx_policy_decisions_context_item_id\n\t\t\t\tON policy_decisions(context_item_id, recorded_at_turn);\n\n\t\t\tCREATE TABLE IF NOT EXISTS retrieval_records (\n\t\t\t\tid TEXT PRIMARY KEY NOT NULL,\n\t\t\t\tcontext_item_id TEXT NOT NULL,\n\t\t\t\trequested_at_turn INTEGER NOT NULL,\n\t\t\t\tslice_kind TEXT NOT NULL,\n\t\t\t\tresult_summary TEXT NOT NULL,\n\t\t\t\tpayload_json TEXT NOT NULL\n\t\t\t) STRICT;\n\n\t\t\tCREATE INDEX IF NOT EXISTS idx_retrieval_records_context_item_id\n\t\t\t\tON retrieval_records(context_item_id, requested_at_turn);\n\n\t\t\tCREATE TABLE IF NOT EXISTS memory_index (\n\t\t\t\tprovider_id TEXT NOT NULL,\n\t\t\t\titem_id TEXT NOT NULL,\n\t\t\t\tscope TEXT NOT NULL,\n\t\t\t\tkind TEXT NOT NULL,\n\t\t\t\ttitle TEXT,\n\t\t\t\tsummary TEXT NOT NULL,\n\t\t\t\tindexed_at_turn INTEGER NOT NULL,\n\t\t\t\tstale INTEGER NOT NULL CHECK (stale IN (0, 1)),\n\t\t\t\tpayload_json TEXT NOT NULL,\n\t\t\t\tPRIMARY KEY (provider_id, item_id)\n\t\t\t) STRICT;\n\n\t\t\tCREATE INDEX IF NOT EXISTS idx_memory_index_scope\n\t\t\t\tON memory_index(scope, provider_id, item_id);\n\n\t\t\tPRAGMA user_version = ${SQLITE_RUNTIME_INDEX_SCHEMA_VERSION};\n\t\t\tCOMMIT;\n\t\t`);\n\t} catch (error) {\n\t\tif (database.isTransaction) database.exec(\"ROLLBACK;\");\n\t\tthrow error;\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseJsonRecord<T>(json: string, isValid: (value: unknown) => value is T): T | undefined {\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(json);\n\t\treturn isValid(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction textColumn(row: Record<string, unknown> | undefined, column: string): string | undefined {\n\tconst value = row?.[column];\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction isContextItem(value: unknown): value is ContextItem {\n\tif (!isRecord(value)) return false;\n\treturn (\n\t\ttypeof value.id === \"string\" &&\n\t\ttypeof value.kind === \"string\" &&\n\t\ttypeof value.retentionClass === \"string\" &&\n\t\ttypeof value.source === \"string\" &&\n\t\ttypeof value.createdAtTurn === \"number\" &&\n\t\ttypeof value.tokenEstimate === \"number\" &&\n\t\ttypeof value.byteEstimate === \"number\"\n\t);\n}\n\nfunction isPolicyDecision(value: unknown): value is PolicyDecision {\n\tif (!isRecord(value)) return false;\n\treturn (\n\t\ttypeof value.kind === \"string\" &&\n\t\ttypeof value.selectedAction === \"string\" &&\n\t\ttypeof value.mode === \"string\" &&\n\t\ttypeof value.applied === \"boolean\" &&\n\t\tArray.isArray(value.hardConstraints) &&\n\t\tArray.isArray(value.candidates) &&\n\t\tArray.isArray(value.selectedReasonCodes) &&\n\t\ttypeof value.estimatedCostTokens === \"number\" &&\n\t\ttypeof value.estimatedSavingsTokens === \"number\" &&\n\t\ttypeof value.estimatedReliabilityRisk === \"number\" &&\n\t\ttypeof value.cacheImpactTokens === \"number\" &&\n\t\ttypeof value.reworkRiskTokens === \"number\" &&\n\t\tArray.isArray(value.artifactRefs) &&\n\t\tArray.isArray(value.evidenceRefs)\n\t);\n}\n\nfunction isPolicyDecisionRecord(value: unknown): value is PolicyDecisionRecord {\n\tif (!isRecord(value)) return false;\n\treturn (\n\t\ttypeof value.id === \"string\" &&\n\t\t(value.contextItemId === undefined || typeof value.contextItemId === \"string\") &&\n\t\ttypeof value.recordedAtTurn === \"number\" &&\n\t\tisPolicyDecision(value.decision)\n\t);\n}\n\nfunction isRetrievalRecord(value: unknown): value is RetrievalRecord {\n\tif (!isRecord(value)) return false;\n\treturn (\n\t\ttypeof value.id === \"string\" &&\n\t\ttypeof value.contextItemId === \"string\" &&\n\t\ttypeof value.requestedAtTurn === \"number\" &&\n\t\ttypeof value.sliceKind === \"string\" &&\n\t\ttypeof value.resultSummary === \"string\"\n\t);\n}\n\nfunction isMemoryIndexRecord(value: unknown): value is MemoryIndexRecord {\n\tif (!isRecord(value) || !isRecord(value.ref)) return false;\n\treturn (\n\t\ttypeof value.ref.providerId === \"string\" &&\n\t\ttypeof value.ref.itemId === \"string\" &&\n\t\ttypeof value.ref.scope === \"string\" &&\n\t\ttypeof value.ref.kind === \"string\" &&\n\t\t(value.title === undefined || typeof value.title === \"string\") &&\n\t\ttypeof value.summary === \"string\" &&\n\t\ttypeof value.indexedAtTurn === \"number\" &&\n\t\ttypeof value.stale === \"boolean\"\n\t);\n}\n\nfunction contextItemFromRow(row: Record<string, unknown> | undefined): ContextItem | undefined {\n\tconst payload = textColumn(row, \"payload_json\");\n\treturn payload === undefined ? undefined : parseJsonRecord(payload, isContextItem);\n}\n\nfunction policyDecisionRecordFromRow(row: Record<string, unknown> | undefined): PolicyDecisionRecord | undefined {\n\tconst payload = textColumn(row, \"payload_json\");\n\treturn payload === undefined ? undefined : parseJsonRecord(payload, isPolicyDecisionRecord);\n}\n\nfunction retrievalRecordFromRow(row: Record<string, unknown> | undefined): RetrievalRecord | undefined {\n\tconst payload = textColumn(row, \"payload_json\");\n\treturn payload === undefined ? undefined : parseJsonRecord(payload, isRetrievalRecord);\n}\n\nfunction memoryIndexRecordFromRow(row: Record<string, unknown> | undefined): MemoryIndexRecord | undefined {\n\tconst payload = textColumn(row, \"payload_json\");\n\treturn payload === undefined ? undefined : parseJsonRecord(payload, isMemoryIndexRecord);\n}\n\nexport function createSqliteContextStore(options: SqliteRuntimeIndexOptions): SqliteContextStore {\n\tconst database = openDatabase(options);\n\tconst upsertItem = database.prepare(`\n\t\tINSERT INTO context_items (\n\t\t\tid, kind, retention_class, source, created_at_turn, last_used_at_turn, token_estimate, byte_estimate, payload_json\n\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(id) DO UPDATE SET\n\t\t\tkind = excluded.kind,\n\t\t\tretention_class = excluded.retention_class,\n\t\t\tsource = excluded.source,\n\t\t\tcreated_at_turn = excluded.created_at_turn,\n\t\t\tlast_used_at_turn = excluded.last_used_at_turn,\n\t\t\ttoken_estimate = excluded.token_estimate,\n\t\t\tbyte_estimate = excluded.byte_estimate,\n\t\t\tpayload_json = excluded.payload_json\n\t`);\n\tconst getItem = database.prepare(\"SELECT payload_json FROM context_items WHERE id = ?\");\n\tconst listItems = database.prepare(\"SELECT payload_json FROM context_items ORDER BY created_at_turn, id\");\n\tconst removeItem = database.prepare(\"DELETE FROM context_items WHERE id = ?\");\n\tconst recordPolicyDecision = database.prepare(`\n\t\tINSERT INTO policy_decisions (\n\t\t\tid, context_item_id, recorded_at_turn, decision_kind, selected_action, mode, applied, payload_json\n\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n\t`);\n\tconst listAllPolicyDecisions = database.prepare(\n\t\t\"SELECT payload_json FROM policy_decisions ORDER BY recorded_at_turn, id\",\n\t);\n\tconst listPolicyDecisionsByItem = database.prepare(\n\t\t\"SELECT payload_json FROM policy_decisions WHERE context_item_id = ? ORDER BY recorded_at_turn, id\",\n\t);\n\tconst recordRetrieval = database.prepare(`\n\t\tINSERT INTO retrieval_records (\n\t\t\tid, context_item_id, requested_at_turn, slice_kind, result_summary, payload_json\n\t\t) VALUES (?, ?, ?, ?, ?, ?)\n\t`);\n\tconst listAllRetrievals = database.prepare(\n\t\t\"SELECT payload_json FROM retrieval_records ORDER BY requested_at_turn, id\",\n\t);\n\tconst listRetrievalsByItem = database.prepare(\n\t\t\"SELECT payload_json FROM retrieval_records WHERE context_item_id = ? ORDER BY requested_at_turn, id\",\n\t);\n\n\treturn {\n\t\tupsertItem(item: ContextItem): void {\n\t\t\tupsertItem.run(\n\t\t\t\titem.id,\n\t\t\t\titem.kind,\n\t\t\t\titem.retentionClass,\n\t\t\t\titem.source,\n\t\t\t\titem.createdAtTurn,\n\t\t\t\titem.lastUsedAtTurn ?? null,\n\t\t\t\titem.tokenEstimate,\n\t\t\t\titem.byteEstimate,\n\t\t\t\tJSON.stringify(item),\n\t\t\t);\n\t\t},\n\n\t\tgetItem(id: string): ContextItem | undefined {\n\t\t\treturn contextItemFromRow(getItem.get(id));\n\t\t},\n\n\t\tlistItems(): ContextItem[] {\n\t\t\treturn listItems.all().flatMap((row) => {\n\t\t\t\tconst item = contextItemFromRow(row);\n\t\t\t\treturn item === undefined ? [] : [item];\n\t\t\t});\n\t\t},\n\n\t\tremoveItem(id: string): void {\n\t\t\tremoveItem.run(id);\n\t\t},\n\n\t\trecordPolicyDecision(record: PolicyDecisionRecord): void {\n\t\t\trecordPolicyDecision.run(\n\t\t\t\trecord.id,\n\t\t\t\trecord.contextItemId ?? null,\n\t\t\t\trecord.recordedAtTurn,\n\t\t\t\trecord.decision.kind,\n\t\t\t\trecord.decision.selectedAction,\n\t\t\t\trecord.decision.mode,\n\t\t\t\trecord.decision.applied ? 1 : 0,\n\t\t\t\tJSON.stringify(record),\n\t\t\t);\n\t\t},\n\n\t\tlistPolicyDecisions(contextItemId?: string): PolicyDecisionRecord[] {\n\t\t\tconst rows =\n\t\t\t\tcontextItemId === undefined ? listAllPolicyDecisions.all() : listPolicyDecisionsByItem.all(contextItemId);\n\t\t\treturn rows.flatMap((row) => {\n\t\t\t\tconst record = policyDecisionRecordFromRow(row);\n\t\t\t\treturn record === undefined ? [] : [record];\n\t\t\t});\n\t\t},\n\n\t\trecordRetrieval(record: RetrievalRecord): void {\n\t\t\trecordRetrieval.run(\n\t\t\t\trecord.id,\n\t\t\t\trecord.contextItemId,\n\t\t\t\trecord.requestedAtTurn,\n\t\t\t\trecord.sliceKind,\n\t\t\t\trecord.resultSummary,\n\t\t\t\tJSON.stringify(record),\n\t\t\t);\n\t\t},\n\n\t\tlistRetrievals(contextItemId?: string): RetrievalRecord[] {\n\t\t\tconst rows = contextItemId === undefined ? listAllRetrievals.all() : listRetrievalsByItem.all(contextItemId);\n\t\t\treturn rows.flatMap((row) => {\n\t\t\t\tconst record = retrievalRecordFromRow(row);\n\t\t\t\treturn record === undefined ? [] : [record];\n\t\t\t});\n\t\t},\n\n\t\tclose(): void {\n\t\t\tdatabase[Symbol.dispose]();\n\t\t},\n\t};\n}\n\nexport function createSqliteMemoryIndexStore(options: SqliteRuntimeIndexOptions): SqliteMemoryIndexStore {\n\tconst database = openDatabase(options);\n\tconst upsert = database.prepare(`\n\t\tINSERT INTO memory_index (\n\t\t\tprovider_id, item_id, scope, kind, title, summary, indexed_at_turn, stale, payload_json\n\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(provider_id, item_id) DO UPDATE SET\n\t\t\tscope = excluded.scope,\n\t\t\tkind = excluded.kind,\n\t\t\ttitle = excluded.title,\n\t\t\tsummary = excluded.summary,\n\t\t\tindexed_at_turn = excluded.indexed_at_turn,\n\t\t\tstale = excluded.stale,\n\t\t\tpayload_json = excluded.payload_json\n\t`);\n\tconst get = database.prepare(\"SELECT payload_json FROM memory_index WHERE provider_id = ? AND item_id = ?\");\n\tconst listAll = database.prepare(\"SELECT payload_json FROM memory_index ORDER BY provider_id, item_id\");\n\tconst listByScope = database.prepare(\n\t\t\"SELECT payload_json FROM memory_index WHERE scope = ? ORDER BY provider_id, item_id\",\n\t);\n\tconst markStale = database.prepare(\n\t\t\"UPDATE memory_index SET stale = 1, payload_json = ? WHERE provider_id = ? AND item_id = ?\",\n\t);\n\tconst remove = database.prepare(\"DELETE FROM memory_index WHERE provider_id = ? AND item_id = ?\");\n\n\treturn {\n\t\tupsert(record: MemoryIndexRecord): void {\n\t\t\tupsert.run(\n\t\t\t\trecord.ref.providerId,\n\t\t\t\trecord.ref.itemId,\n\t\t\t\trecord.ref.scope,\n\t\t\t\trecord.ref.kind,\n\t\t\t\trecord.title ?? null,\n\t\t\t\trecord.summary,\n\t\t\t\trecord.indexedAtTurn,\n\t\t\t\trecord.stale ? 1 : 0,\n\t\t\t\tJSON.stringify(record),\n\t\t\t);\n\t\t},\n\n\t\tget(providerId: string, itemId: string): MemoryIndexRecord | undefined {\n\t\t\treturn memoryIndexRecordFromRow(get.get(providerId, itemId));\n\t\t},\n\n\t\tlist(scope?: MemoryIndexRecord[\"ref\"][\"scope\"]): MemoryIndexRecord[] {\n\t\t\tconst rows = scope === undefined ? listAll.all() : listByScope.all(scope);\n\t\t\treturn rows.flatMap((row) => {\n\t\t\t\tconst record = memoryIndexRecordFromRow(row);\n\t\t\t\treturn record === undefined ? [] : [record];\n\t\t\t});\n\t\t},\n\n\t\tmarkStale(providerId: string, itemId: string): void {\n\t\t\tconst existing = memoryIndexRecordFromRow(get.get(providerId, itemId));\n\t\t\tif (existing === undefined) return;\n\t\t\tmarkStale.run(JSON.stringify({ ...existing, stale: true }), providerId, itemId);\n\t\t},\n\n\t\tremove(providerId: string, itemId: string): void {\n\t\t\tremove.run(providerId, itemId);\n\t\t},\n\n\t\tclose(): void {\n\t\t\tdatabase[Symbol.dispose]();\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-actions.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/worker-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAQ,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAG7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAyDD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,6BAA6B,GAAG,YAAY,EAAE,CAG3G;AAED,MAAM,WAAW,oBAAoB;IACpC,4CAA4C;IAC5C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uFAAqF;IACrF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,8FAA8F;IAC9F,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;CACZ,GAAG,oBAAoB,CAmCvB","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport type { Tool, ToolArgumentValidationOptions } from \"@caupulican/pi-ai\";\nimport { validateToolArguments } from \"@caupulican/pi-ai\";\nimport { Type } from \"typebox\";\nimport type { CapabilityEnvelope } from \"../autonomy/contracts.ts\";\nimport { isPathWithinEnvelope } from \"../autonomy/envelope-enforcement.ts\";\n\n/**\n * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON\n * actions, and this RUNNER-side module applies them deterministically through the capability\n * envelope's path scope. That keeps the structural-contract philosophy (a local model without\n * tool-calling templates can still write code) and makes enforcement execution-time, not\n * validation-only: an out-of-scope action is REFUSED with a reason, never silently dropped, and\n * refusals surface as blockers on the result.\n */\n\nexport interface WorkerAction {\n\top: \"write\" | \"edit\";\n\tpath: string;\n\t/** write: full file content. */\n\tcontent?: string;\n\t/** edit: exact string to replace (must occur in the file). */\n\told?: string;\n\t/** edit: replacement text. */\n\tnew?: string;\n}\n\nconst MAX_ACTIONS = 20;\nconst MAX_CONTENT_CHARS = 512 * 1024;\n\nconst workerActionSchema = Type.Union([\n\tType.Object({\n\t\top: Type.Literal(\"write\"),\n\t\tpath: Type.String({ minLength: 1 }),\n\t\tcontent: Type.String({ maxLength: MAX_CONTENT_CHARS }),\n\t}),\n\tType.Object({\n\t\top: Type.Literal(\"edit\"),\n\t\tpath: Type.String({ minLength: 1 }),\n\t\told: Type.String({ minLength: 1 }),\n\t\tnew: Type.String({ maxLength: MAX_CONTENT_CHARS }),\n\t}),\n]);\n\nconst workerActionsTool: Tool = {\n\tname: \"worker_actions\",\n\tdescription: \"Worker filesystem action list\",\n\tparameters: Type.Object({ actions: Type.Array(workerActionSchema) }),\n};\n\nfunction validateWorkerActions(raw: unknown, validation?: ToolArgumentValidationOptions): unknown[] {\n\ttry {\n\t\tconst validated = validateToolArguments(\n\t\t\tworkerActionsTool,\n\t\t\t{ type: \"toolCall\", id: \"worker-actions\", name: \"worker_actions\", arguments: { actions: raw } },\n\t\t\tvalidation,\n\t\t).actions;\n\t\treturn Array.isArray(validated) ? validated : [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction sanitizeWorkerActions(raw: readonly unknown[]): WorkerAction[] {\n\tconst actions: WorkerAction[] = [];\n\tfor (const entry of raw.slice(0, MAX_ACTIONS)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst record = entry as Record<string, unknown>;\n\t\tif (record.op !== \"write\" && record.op !== \"edit\") continue;\n\t\tif (typeof record.path !== \"string\" || record.path.length === 0) continue;\n\t\tif (record.op === \"write\") {\n\t\t\tif (typeof record.content !== \"string\" || record.content.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"write\", path: record.path, content: record.content });\n\t\t} else {\n\t\t\tif (typeof record.old !== \"string\" || record.old.length === 0) continue;\n\t\t\tif (typeof record.new !== \"string\" || record.new.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"edit\", path: record.path, old: record.old, new: record.new });\n\t\t}\n\t}\n\treturn actions;\n}\n\nexport function parseWorkerActions(raw: unknown, validation?: ToolArgumentValidationOptions): WorkerAction[] {\n\tif (Array.isArray(raw)) return sanitizeWorkerActions(validateWorkerActions(sanitizeWorkerActions(raw), validation));\n\treturn sanitizeWorkerActions(validateWorkerActions(raw, validation));\n}\n\nexport interface AppliedActionsReport {\n\t/** Repo-relative paths actually changed. */\n\tchangedFiles: string[];\n\t/** Envelope-scope refusals (execution-time enforcement) — surfaced, never silent. */\n\trefused: Array<{ path: string; reason: string }>;\n\t/** Actions that were in scope but could not be applied (missing file, old-text not found). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\nexport function applyWorkerActions(args: {\n\tactions: readonly WorkerAction[];\n\tenvelope: CapabilityEnvelope;\n\tcwd: string;\n}): AppliedActionsReport {\n\tconst report: AppliedActionsReport = { changedFiles: [], refused: [], failed: [] };\n\tfor (const action of args.actions) {\n\t\tif (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {\n\t\t\treport.refused.push({ path: action.path, reason: `outside envelope ${args.envelope.id} path scope` });\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = resolve(args.cwd, action.path);\n\t\tconst relativePath = relative(args.cwd, target);\n\t\ttry {\n\t\t\tif (action.op === \"write\") {\n\t\t\t\tmkdirSync(dirname(target), { recursive: true });\n\t\t\t\twriteFileSync(target, action.content ?? \"\", \"utf-8\");\n\t\t\t} else {\n\t\t\t\tif (!existsSync(target)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"file does not exist\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst current = readFileSync(target, \"utf-8\");\n\t\t\t\tif (!action.old || !current.includes(action.old)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"edit old-text not found in file\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twriteFileSync(\n\t\t\t\t\ttarget,\n\t\t\t\t\tcurrent.replace(action.old, () => action.new ?? \"\"),\n\t\t\t\t\t\"utf-8\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!report.changedFiles.includes(relativePath)) report.changedFiles.push(relativePath);\n\t\t} catch (error) {\n\t\t\treport.failed.push({ path: action.path, reason: error instanceof Error ? error.message : String(error) });\n\t\t}\n\t}\n\treturn report;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"worker-actions.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/worker-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAQ,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAG7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAyDD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,6BAA6B,GAAG,YAAY,EAAE,CAG3G;AAED,MAAM,WAAW,oBAAoB;IACpC,4CAA4C;IAC5C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uFAAqF;IACrF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,8FAA8F;IAC9F,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;CACZ,GAAG,oBAAoB,CAmCvB","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport type { Tool, ToolArgumentValidationOptions } from \"@caupulican/pi-ai\";\nimport { validateToolArguments } from \"@caupulican/pi-ai\";\nimport { Type } from \"typebox\";\nimport type { CapabilityEnvelope } from \"../autonomy/contracts.ts\";\nimport { isPathWithinEnvelope } from \"../autonomy/envelope-enforcement.ts\";\n\n/**\n * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON\n * actions, and this RUNNER-side module applies them deterministically through the capability\n * envelope's path scope. That keeps the structural-contract philosophy (a local model without\n * tool-calling templates can still write code) and makes enforcement execution-time, not\n * validation-only: an out-of-scope action is REFUSED with a reason, never silently dropped, and\n * refusals surface as blockers on the result.\n */\n\nexport interface WorkerAction {\n\top: \"write\" | \"edit\";\n\tpath: string;\n\t/** write: full file content. */\n\tcontent?: string;\n\t/** edit: exact string to replace (must occur in the file). */\n\told?: string;\n\t/** edit: replacement text. */\n\tnew?: string;\n}\n\nconst MAX_ACTIONS = 20;\nconst MAX_CONTENT_CHARS = 512 * 1024;\n\nconst workerActionSchema = Type.Union([\n\tType.Object({\n\t\top: Type.Literal(\"write\"),\n\t\tpath: Type.String({ minLength: 1 }),\n\t\tcontent: Type.String({ maxLength: MAX_CONTENT_CHARS }),\n\t}),\n\tType.Object({\n\t\top: Type.Literal(\"edit\"),\n\t\tpath: Type.String({ minLength: 1 }),\n\t\told: Type.String({ minLength: 1 }),\n\t\tnew: Type.String({ maxLength: MAX_CONTENT_CHARS }),\n\t}),\n]);\n\nconst workerActionsTool: Tool = {\n\tname: \"worker_actions\",\n\tdescription: \"Worker filesystem action list\",\n\tparameters: Type.Object({ actions: Type.Array(workerActionSchema) }),\n};\n\nfunction validateWorkerActions(raw: unknown, validation?: ToolArgumentValidationOptions): unknown[] {\n\ttry {\n\t\tconst validated = validateToolArguments(\n\t\t\tworkerActionsTool,\n\t\t\t{ type: \"toolCall\", id: \"worker-actions\", name: \"worker_actions\", arguments: { actions: raw } },\n\t\t\tvalidation,\n\t\t).actions;\n\t\treturn Array.isArray(validated) ? validated : [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction sanitizeWorkerActions(raw: readonly unknown[]): WorkerAction[] {\n\tconst actions: WorkerAction[] = [];\n\tfor (const entry of raw.slice(0, MAX_ACTIONS)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst record = entry as Record<string, unknown>;\n\t\tif (record.op !== \"write\" && record.op !== \"edit\") continue;\n\t\tif (typeof record.path !== \"string\" || record.path.length === 0) continue;\n\t\tif (record.op === \"write\") {\n\t\t\tif (typeof record.content !== \"string\" || record.content.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"write\", path: record.path, content: record.content });\n\t\t} else {\n\t\t\tif (typeof record.old !== \"string\" || record.old.length === 0) continue;\n\t\t\tif (typeof record.new !== \"string\" || record.new.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"edit\", path: record.path, old: record.old, new: record.new });\n\t\t}\n\t}\n\treturn actions;\n}\n\nexport function parseWorkerActions(raw: unknown, validation?: ToolArgumentValidationOptions): WorkerAction[] {\n\tif (Array.isArray(raw)) return sanitizeWorkerActions(validateWorkerActions(sanitizeWorkerActions(raw), validation));\n\treturn sanitizeWorkerActions(validateWorkerActions(raw, validation));\n}\n\nexport interface AppliedActionsReport {\n\t/** Repo-relative paths actually changed. */\n\tchangedFiles: string[];\n\t/** Envelope-scope refusals (execution-time enforcement) — surfaced, never silent. */\n\trefused: Array<{ path: string; reason: string }>;\n\t/** Actions that were in scope but could not be applied (missing file, old-text not found). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\nexport function applyWorkerActions(args: {\n\tactions: readonly WorkerAction[];\n\tenvelope: CapabilityEnvelope;\n\tcwd: string;\n}): AppliedActionsReport {\n\tconst report: AppliedActionsReport = { changedFiles: [], refused: [], failed: [] };\n\tfor (const action of args.actions) {\n\t\tif (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {\n\t\t\treport.refused.push({ path: action.path, reason: `outside envelope ${args.envelope.id} path scope` });\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = resolve(args.cwd, action.path);\n\t\tconst relativePath = relative(args.cwd, target).replaceAll(\"\\\\\", \"/\");\n\t\ttry {\n\t\t\tif (action.op === \"write\") {\n\t\t\t\tmkdirSync(dirname(target), { recursive: true });\n\t\t\t\twriteFileSync(target, action.content ?? \"\", \"utf-8\");\n\t\t\t} else {\n\t\t\t\tif (!existsSync(target)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"file does not exist\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst current = readFileSync(target, \"utf-8\");\n\t\t\t\tif (!action.old || !current.includes(action.old)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"edit old-text not found in file\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twriteFileSync(\n\t\t\t\t\ttarget,\n\t\t\t\t\tcurrent.replace(action.old, () => action.new ?? \"\"),\n\t\t\t\t\t\"utf-8\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!report.changedFiles.includes(relativePath)) report.changedFiles.push(relativePath);\n\t\t} catch (error) {\n\t\t\treport.failed.push({ path: action.path, reason: error instanceof Error ? error.message : String(error) });\n\t\t}\n\t}\n\treturn report;\n}\n"]}
|
|
@@ -70,7 +70,7 @@ export function applyWorkerActions(args) {
|
|
|
70
70
|
continue;
|
|
71
71
|
}
|
|
72
72
|
const target = resolve(args.cwd, action.path);
|
|
73
|
-
const relativePath = relative(args.cwd, target);
|
|
73
|
+
const relativePath = relative(args.cwd, target).replaceAll("\\", "/");
|
|
74
74
|
try {
|
|
75
75
|
if (action.op === "write") {
|
|
76
76
|
mkdirSync(dirname(target), { recursive: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-actions.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport type { Tool, ToolArgumentValidationOptions } from \"@caupulican/pi-ai\";\nimport { validateToolArguments } from \"@caupulican/pi-ai\";\nimport { Type } from \"typebox\";\nimport type { CapabilityEnvelope } from \"../autonomy/contracts.ts\";\nimport { isPathWithinEnvelope } from \"../autonomy/envelope-enforcement.ts\";\n\n/**\n * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON\n * actions, and this RUNNER-side module applies them deterministically through the capability\n * envelope's path scope. That keeps the structural-contract philosophy (a local model without\n * tool-calling templates can still write code) and makes enforcement execution-time, not\n * validation-only: an out-of-scope action is REFUSED with a reason, never silently dropped, and\n * refusals surface as blockers on the result.\n */\n\nexport interface WorkerAction {\n\top: \"write\" | \"edit\";\n\tpath: string;\n\t/** write: full file content. */\n\tcontent?: string;\n\t/** edit: exact string to replace (must occur in the file). */\n\told?: string;\n\t/** edit: replacement text. */\n\tnew?: string;\n}\n\nconst MAX_ACTIONS = 20;\nconst MAX_CONTENT_CHARS = 512 * 1024;\n\nconst workerActionSchema = Type.Union([\n\tType.Object({\n\t\top: Type.Literal(\"write\"),\n\t\tpath: Type.String({ minLength: 1 }),\n\t\tcontent: Type.String({ maxLength: MAX_CONTENT_CHARS }),\n\t}),\n\tType.Object({\n\t\top: Type.Literal(\"edit\"),\n\t\tpath: Type.String({ minLength: 1 }),\n\t\told: Type.String({ minLength: 1 }),\n\t\tnew: Type.String({ maxLength: MAX_CONTENT_CHARS }),\n\t}),\n]);\n\nconst workerActionsTool: Tool = {\n\tname: \"worker_actions\",\n\tdescription: \"Worker filesystem action list\",\n\tparameters: Type.Object({ actions: Type.Array(workerActionSchema) }),\n};\n\nfunction validateWorkerActions(raw: unknown, validation?: ToolArgumentValidationOptions): unknown[] {\n\ttry {\n\t\tconst validated = validateToolArguments(\n\t\t\tworkerActionsTool,\n\t\t\t{ type: \"toolCall\", id: \"worker-actions\", name: \"worker_actions\", arguments: { actions: raw } },\n\t\t\tvalidation,\n\t\t).actions;\n\t\treturn Array.isArray(validated) ? validated : [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction sanitizeWorkerActions(raw: readonly unknown[]): WorkerAction[] {\n\tconst actions: WorkerAction[] = [];\n\tfor (const entry of raw.slice(0, MAX_ACTIONS)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst record = entry as Record<string, unknown>;\n\t\tif (record.op !== \"write\" && record.op !== \"edit\") continue;\n\t\tif (typeof record.path !== \"string\" || record.path.length === 0) continue;\n\t\tif (record.op === \"write\") {\n\t\t\tif (typeof record.content !== \"string\" || record.content.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"write\", path: record.path, content: record.content });\n\t\t} else {\n\t\t\tif (typeof record.old !== \"string\" || record.old.length === 0) continue;\n\t\t\tif (typeof record.new !== \"string\" || record.new.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"edit\", path: record.path, old: record.old, new: record.new });\n\t\t}\n\t}\n\treturn actions;\n}\n\nexport function parseWorkerActions(raw: unknown, validation?: ToolArgumentValidationOptions): WorkerAction[] {\n\tif (Array.isArray(raw)) return sanitizeWorkerActions(validateWorkerActions(sanitizeWorkerActions(raw), validation));\n\treturn sanitizeWorkerActions(validateWorkerActions(raw, validation));\n}\n\nexport interface AppliedActionsReport {\n\t/** Repo-relative paths actually changed. */\n\tchangedFiles: string[];\n\t/** Envelope-scope refusals (execution-time enforcement) — surfaced, never silent. */\n\trefused: Array<{ path: string; reason: string }>;\n\t/** Actions that were in scope but could not be applied (missing file, old-text not found). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\nexport function applyWorkerActions(args: {\n\tactions: readonly WorkerAction[];\n\tenvelope: CapabilityEnvelope;\n\tcwd: string;\n}): AppliedActionsReport {\n\tconst report: AppliedActionsReport = { changedFiles: [], refused: [], failed: [] };\n\tfor (const action of args.actions) {\n\t\tif (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {\n\t\t\treport.refused.push({ path: action.path, reason: `outside envelope ${args.envelope.id} path scope` });\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = resolve(args.cwd, action.path);\n\t\tconst relativePath = relative(args.cwd, target);\n\t\ttry {\n\t\t\tif (action.op === \"write\") {\n\t\t\t\tmkdirSync(dirname(target), { recursive: true });\n\t\t\t\twriteFileSync(target, action.content ?? \"\", \"utf-8\");\n\t\t\t} else {\n\t\t\t\tif (!existsSync(target)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"file does not exist\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst current = readFileSync(target, \"utf-8\");\n\t\t\t\tif (!action.old || !current.includes(action.old)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"edit old-text not found in file\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twriteFileSync(\n\t\t\t\t\ttarget,\n\t\t\t\t\tcurrent.replace(action.old, () => action.new ?? \"\"),\n\t\t\t\t\t\"utf-8\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!report.changedFiles.includes(relativePath)) report.changedFiles.push(relativePath);\n\t\t} catch (error) {\n\t\t\treport.failed.push({ path: action.path, reason: error instanceof Error ? error.message : String(error) });\n\t\t}\n\t}\n\treturn report;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"worker-actions.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport type { Tool, ToolArgumentValidationOptions } from \"@caupulican/pi-ai\";\nimport { validateToolArguments } from \"@caupulican/pi-ai\";\nimport { Type } from \"typebox\";\nimport type { CapabilityEnvelope } from \"../autonomy/contracts.ts\";\nimport { isPathWithinEnvelope } from \"../autonomy/envelope-enforcement.ts\";\n\n/**\n * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON\n * actions, and this RUNNER-side module applies them deterministically through the capability\n * envelope's path scope. That keeps the structural-contract philosophy (a local model without\n * tool-calling templates can still write code) and makes enforcement execution-time, not\n * validation-only: an out-of-scope action is REFUSED with a reason, never silently dropped, and\n * refusals surface as blockers on the result.\n */\n\nexport interface WorkerAction {\n\top: \"write\" | \"edit\";\n\tpath: string;\n\t/** write: full file content. */\n\tcontent?: string;\n\t/** edit: exact string to replace (must occur in the file). */\n\told?: string;\n\t/** edit: replacement text. */\n\tnew?: string;\n}\n\nconst MAX_ACTIONS = 20;\nconst MAX_CONTENT_CHARS = 512 * 1024;\n\nconst workerActionSchema = Type.Union([\n\tType.Object({\n\t\top: Type.Literal(\"write\"),\n\t\tpath: Type.String({ minLength: 1 }),\n\t\tcontent: Type.String({ maxLength: MAX_CONTENT_CHARS }),\n\t}),\n\tType.Object({\n\t\top: Type.Literal(\"edit\"),\n\t\tpath: Type.String({ minLength: 1 }),\n\t\told: Type.String({ minLength: 1 }),\n\t\tnew: Type.String({ maxLength: MAX_CONTENT_CHARS }),\n\t}),\n]);\n\nconst workerActionsTool: Tool = {\n\tname: \"worker_actions\",\n\tdescription: \"Worker filesystem action list\",\n\tparameters: Type.Object({ actions: Type.Array(workerActionSchema) }),\n};\n\nfunction validateWorkerActions(raw: unknown, validation?: ToolArgumentValidationOptions): unknown[] {\n\ttry {\n\t\tconst validated = validateToolArguments(\n\t\t\tworkerActionsTool,\n\t\t\t{ type: \"toolCall\", id: \"worker-actions\", name: \"worker_actions\", arguments: { actions: raw } },\n\t\t\tvalidation,\n\t\t).actions;\n\t\treturn Array.isArray(validated) ? validated : [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction sanitizeWorkerActions(raw: readonly unknown[]): WorkerAction[] {\n\tconst actions: WorkerAction[] = [];\n\tfor (const entry of raw.slice(0, MAX_ACTIONS)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst record = entry as Record<string, unknown>;\n\t\tif (record.op !== \"write\" && record.op !== \"edit\") continue;\n\t\tif (typeof record.path !== \"string\" || record.path.length === 0) continue;\n\t\tif (record.op === \"write\") {\n\t\t\tif (typeof record.content !== \"string\" || record.content.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"write\", path: record.path, content: record.content });\n\t\t} else {\n\t\t\tif (typeof record.old !== \"string\" || record.old.length === 0) continue;\n\t\t\tif (typeof record.new !== \"string\" || record.new.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"edit\", path: record.path, old: record.old, new: record.new });\n\t\t}\n\t}\n\treturn actions;\n}\n\nexport function parseWorkerActions(raw: unknown, validation?: ToolArgumentValidationOptions): WorkerAction[] {\n\tif (Array.isArray(raw)) return sanitizeWorkerActions(validateWorkerActions(sanitizeWorkerActions(raw), validation));\n\treturn sanitizeWorkerActions(validateWorkerActions(raw, validation));\n}\n\nexport interface AppliedActionsReport {\n\t/** Repo-relative paths actually changed. */\n\tchangedFiles: string[];\n\t/** Envelope-scope refusals (execution-time enforcement) — surfaced, never silent. */\n\trefused: Array<{ path: string; reason: string }>;\n\t/** Actions that were in scope but could not be applied (missing file, old-text not found). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\nexport function applyWorkerActions(args: {\n\tactions: readonly WorkerAction[];\n\tenvelope: CapabilityEnvelope;\n\tcwd: string;\n}): AppliedActionsReport {\n\tconst report: AppliedActionsReport = { changedFiles: [], refused: [], failed: [] };\n\tfor (const action of args.actions) {\n\t\tif (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {\n\t\t\treport.refused.push({ path: action.path, reason: `outside envelope ${args.envelope.id} path scope` });\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = resolve(args.cwd, action.path);\n\t\tconst relativePath = relative(args.cwd, target).replaceAll(\"\\\\\", \"/\");\n\t\ttry {\n\t\t\tif (action.op === \"write\") {\n\t\t\t\tmkdirSync(dirname(target), { recursive: true });\n\t\t\t\twriteFileSync(target, action.content ?? \"\", \"utf-8\");\n\t\t\t} else {\n\t\t\t\tif (!existsSync(target)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"file does not exist\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst current = readFileSync(target, \"utf-8\");\n\t\t\t\tif (!action.old || !current.includes(action.old)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"edit old-text not found in file\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twriteFileSync(\n\t\t\t\t\ttarget,\n\t\t\t\t\tcurrent.replace(action.old, () => action.new ?? \"\"),\n\t\t\t\t\t\"utf-8\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!report.changedFiles.includes(relativePath)) report.changedFiles.push(relativePath);\n\t\t} catch (error) {\n\t\t\treport.failed.push({ path: action.path, reason: error instanceof Error ? error.message : String(error) });\n\t\t}\n\t}\n\treturn report;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuBH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhE,OAAO,KAAK,EACX,SAAS,EAET,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EAKpB,MAAM,YAAY,CAAC;AAkJpB;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAiEzD;AAgTD;;;;;GAKG;AACH,wBAAsB,kCAAkC,CACvD,UAAU,EAAE,SAAS,EAAE,EACvB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACxD,OAAO,CAAC,IAAI,CAAC,CAuCf;AA4CD,wBAAsB,aAAa,CAClC,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,OAAO,CAAC;IAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAyBhE;AA2GD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAa,EAC1B,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GACzC,OAAO,CAAC,SAAS,CAAC,CAapB;AAED;;GAEG;AACH,wBAAsB,cAAc,CACnC,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,iBAAiB,CAAC,EACxC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA0C/B;AAED,UAAU,gBAAgB;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAaD,UAAU,iBAAiB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AA2ID;;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,CA4C/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 * as _bundledPiAgentCore from \"@caupulican/pi-agent-core\";\nimport * as _bundledPiAi from \"@caupulican/pi-ai\";\nimport * as _bundledPiAiOauth from \"@caupulican/pi-ai/oauth\";\nimport type { KeyId } from \"@caupulican/pi-tui\";\nimport * as _bundledPiTui from \"@caupulican/pi-tui\";\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 @caupulican/pi-adaptative.\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 { createSyntheticSourceInfo } from \"../source-info.ts\";\nimport type {\n\tExtension,\n\tExtensionAPI,\n\tExtensionFactory,\n\tExtensionRuntime,\n\tLoadExtensionsResult,\n\tMessageRenderer,\n\tProviderConfig,\n\tRegisteredCommand,\n\tToolDefinition,\n} from \"./types.ts\";\n\nconst EXTENSION_FACTORY_TIMEOUT_MS = 30_000;\nconst EXTENSION_DISPOSAL_TIMEOUT_MS = 5_000;\nconst inactiveExtensions = new WeakSet<Extension>();\n\nclass ExtensionFactoryTimeoutError extends Error {}\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\"@caupulican/pi-agent-core\": _bundledPiAgentCore,\n\t\"@caupulican/pi-tui\": _bundledPiTui,\n\t\"@caupulican/pi-ai\": _bundledPiAi,\n\t\"@caupulican/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@caupulican/pi-adaptative\": _bundledPiCodingAgent,\n\t\"@earendil-works/pi-agent-core\": _bundledPiAgentCore,\n\t\"@earendil-works/pi-tui\": _bundledPiTui,\n\t\"@earendil-works/pi-ai\": _bundledPiAi,\n\t\"@earendil-works/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@earendil-works/pi-coding-agent\": _bundledPiCodingAgent,\n\t\"@mariozechner/pi-agent-core\": _bundledPiAgentCore,\n\t\"@mariozechner/pi-tui\": _bundledPiTui,\n\t\"@mariozechner/pi-ai\": _bundledPiAi,\n\t\"@mariozechner/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@mariozechner/pi-coding-agent\": _bundledPiCodingAgent,\n};\n\nfunction uniquePaths(paths: string[]): string[] {\n\treturn [...new Set(paths)];\n}\n\nfunction safeRealpath(filePath: string): string {\n\ttry {\n\t\treturn fs.realpathSync(filePath);\n\t} catch {\n\t\treturn filePath;\n\t}\n}\n\n/**\n * Get aliases for jiti (used in Node.js/development 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 loaderFile = fileURLToPath(import.meta.url);\n\tconst realLoaderFile = safeRealpath(loaderFile);\n\tconst __dirname = path.dirname(loaderFile);\n\tconst realDirname = path.dirname(realLoaderFile);\n\tconst packageIndex = [\n\t\tpath.resolve(__dirname, \"../..\", \"index.ts\"),\n\t\tpath.resolve(__dirname, \"../..\", \"index.js\"),\n\t].find((candidate) => fs.existsSync(candidate));\n\tif (!packageIndex) throw new Error(\"Unable to resolve the coding-agent package entry\");\n\n\tconst moduleRequires = uniquePaths([loaderFile, realLoaderFile]).map((file) => createRequire(file));\n\tconst resolveModule = (specifier: string): string => {\n\t\tfor (const moduleRequire of moduleRequires) {\n\t\t\ttry {\n\t\t\t\treturn moduleRequire.resolve(specifier);\n\t\t\t} catch {\n\t\t\t\t// Try the next resolution base. Linked global installs may resolve the\n\t\t\t\t// loader through the global symlink path while workspace dependencies are\n\t\t\t\t// hoisted beside the real source path.\n\t\t\t}\n\t\t}\n\t\treturn fileURLToPath(import.meta.resolve(specifier));\n\t};\n\n\tconst typeboxEntry = resolveModule(\"typebox\");\n\tconst typeboxCompileEntry = resolveModule(\"typebox/compile\");\n\tconst typeboxValueEntry = resolveModule(\"typebox/value\");\n\n\tconst packagesRoots = uniquePaths([\n\t\tpath.resolve(__dirname, \"../../../../\"),\n\t\tpath.resolve(realDirname, \"../../../../\"),\n\t]);\n\tconst resolveWorkspaceOrImport = (workspaceRelativePath: string, specifier: string): string => {\n\t\tfor (const packagesRoot of packagesRoots) {\n\t\t\tconst workspacePath = path.join(packagesRoot, workspaceRelativePath);\n\t\t\tif (fs.existsSync(workspacePath)) {\n\t\t\t\treturn workspacePath;\n\t\t\t}\n\t\t}\n\t\treturn resolveModule(specifier);\n\t};\n\n\tconst piCodingAgentEntry = packageIndex;\n\tconst piAgentCoreEntry = resolveWorkspaceOrImport(\"agent/src/index.ts\", \"@caupulican/pi-agent-core\");\n\tconst piAgentCoreNodeEntry = resolveWorkspaceOrImport(\"agent/src/node.ts\", \"@caupulican/pi-agent-core/node\");\n\tconst piAgentCorePathsEntry = resolveWorkspaceOrImport(\n\t\t\"agent/src/utils/paths.ts\",\n\t\t\"@caupulican/pi-agent-core/paths\",\n\t);\n\tconst piTuiEntry = resolveWorkspaceOrImport(\"tui/src/index.ts\", \"@caupulican/pi-tui\");\n\tconst piAiEntry = resolveWorkspaceOrImport(\"ai/src/index.ts\", \"@caupulican/pi-ai\");\n\tconst piAiOauthEntry = resolveWorkspaceOrImport(\"ai/src/oauth.ts\", \"@caupulican/pi-ai/oauth\");\n\n\t_aliases = {\n\t\t\"@caupulican/pi-agent-core/node\": piAgentCoreNodeEntry,\n\t\t\"@caupulican/pi-agent-core/paths\": piAgentCorePathsEntry,\n\t\t\"@caupulican/pi-ai/oauth\": piAiOauthEntry,\n\t\t\"@caupulican/pi-adaptative\": piCodingAgentEntry,\n\t\t\"@caupulican/pi-agent-core\": piAgentCoreEntry,\n\t\t\"@caupulican/pi-tui\": piTuiEntry,\n\t\t\"@caupulican/pi-ai\": piAiEntry,\n\t\t\"@earendil-works/pi-agent-core/node\": piAgentCoreNodeEntry,\n\t\t\"@earendil-works/pi-agent-core/paths\": piAgentCorePathsEntry,\n\t\t\"@earendil-works/pi-ai/oauth\": piAiOauthEntry,\n\t\t\"@earendil-works/pi-coding-agent\": piCodingAgentEntry,\n\t\t\"@earendil-works/pi-agent-core\": piAgentCoreEntry,\n\t\t\"@earendil-works/pi-tui\": piTuiEntry,\n\t\t\"@earendil-works/pi-ai\": piAiEntry,\n\t\t\"@mariozechner/pi-agent-core/node\": piAgentCoreNodeEntry,\n\t\t\"@mariozechner/pi-agent-core/paths\": piAgentCorePathsEntry,\n\t\t\"@mariozechner/pi-ai/oauth\": piAiOauthEntry,\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\": piAiEntry,\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\nfunction yieldToEventLoop(): Promise<void> {\n\treturn new Promise((resolve) => setImmediate(resolve));\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 assertActive = () => {\n\t\tif (state.staleMessage) {\n\t\t\tthrow new Error(state.staleMessage);\n\t\t}\n\t};\n\n\tconst providersByExtension = new Map<string, Set<string>>();\n\tconst providerRegistrations = new Map<string, { config: ProviderConfig; extensionPath?: string }>();\n\tconst memoryProvidersByExtension = new Map<string, Set<Parameters<ExtensionRuntime[\"registerMemoryProvider\"]>[0]>>();\n\tconst contextMemoryProvidersByExtension = new Map<\n\t\tstring,\n\t\tSet<Parameters<ExtensionRuntime[\"registerContextMemoryProvider\"]>[0]>\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: notInitialized,\n\t\tgetThinkingLevel: notInitialized,\n\t\tsetThinkingLevel: notInitialized,\n\t\tgetExternalResourceRoots: notInitialized,\n\t\tregisterMemoryProvider: notInitialized,\n\t\tregisterContextMemoryProvider: notInitialized,\n\t\treportSpawnedUsage: notInitialized,\n\t\tflagValues: new Map(),\n\t\tpendingProviderRegistrations: [],\n\t\tassertActive,\n\t\tinvalidate: (message) => {\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},\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\tunregisterProvider: (name) => {\n\t\t\truntime.pendingProviderRegistrations = runtime.pendingProviderRegistrations.filter((r) => r.name !== name);\n\t\t},\n\t\tprovidersByExtension,\n\t\tproviderRegistrations,\n\t\tgetProvidersForExtension: (extensionPath: string) => {\n\t\t\treturn [...(providersByExtension.get(extensionPath) ?? [])];\n\t\t},\n\t\tmemoryProvidersByExtension,\n\t\tcontextMemoryProvidersByExtension,\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\t// A factory can time out while its promise continues running. Route every later API call through\n\t// an extension-generation guard so stale async work cannot register tools, handlers, or providers.\n\tconst sharedRuntime = runtime;\n\truntime = new Proxy(sharedRuntime, {\n\t\tget(target, property, receiver) {\n\t\t\tif (property === \"assertActive\") {\n\t\t\t\treturn () => {\n\t\t\t\t\ttarget.assertActive();\n\t\t\t\t\tif (inactiveExtensions.has(extension)) {\n\t\t\t\t\t\tthrow new Error(`Extension generation is no longer active: ${extension.path}`);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn Reflect.get(target, property, receiver);\n\t\t},\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\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\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\tgetExternalResourceRoots() {\n\t\t\truntime.assertActive();\n\t\t\treturn runtime.getExternalResourceRoots();\n\t\t},\n\n\t\tregisterMemoryProvider(provider) {\n\t\t\truntime.assertActive();\n\t\t\truntime.registerMemoryProvider(provider);\n\t\t\tconst providers = runtime.memoryProvidersByExtension.get(extension.path) ?? new Set();\n\t\t\tproviders.add(provider);\n\t\t\truntime.memoryProvidersByExtension.set(extension.path, providers);\n\t\t},\n\n\t\tregisterContextMemoryProvider(provider) {\n\t\t\truntime.assertActive();\n\t\t\truntime.registerContextMemoryProvider(provider);\n\t\t\tconst providers = runtime.contextMemoryProvidersByExtension.get(extension.path) ?? new Set();\n\t\t\tproviders.add(provider);\n\t\t\truntime.contextMemoryProvidersByExtension.set(extension.path, providers);\n\t\t},\n\n\t\treportSpawnedUsage(usage, opts) {\n\t\t\truntime.assertActive();\n\t\t\truntime.reportSpawnedUsage(usage, opts);\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(name: string, config: ProviderConfig) {\n\t\t\truntime.assertActive();\n\t\t\truntime.registerProvider(name, config, 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\tonDispose(fn: () => void | Promise<void>): void {\n\t\t\truntime.assertActive();\n\t\t\textension.disposers.push(fn);\n\t\t},\n\n\t\t// Track bus subscriptions per extension generation so hot reloads can\n\t\t// unsubscribe replaced generations (see disposeExtensionEventSubscriptions).\n\t\tevents: {\n\t\t\temit: (channel: string, data: unknown) => {\n\t\t\t\truntime.assertActive();\n\t\t\t\teventBus.emit(channel, data);\n\t\t\t},\n\t\t\ton: (channel: string, handler: (data: unknown) => void) => {\n\t\t\t\truntime.assertActive();\n\t\t\t\tconst unsubscribe = eventBus.on(channel, handler);\n\t\t\t\textension.eventUnsubscribes.push(unsubscribe);\n\t\t\t\treturn unsubscribe;\n\t\t\t},\n\t\t},\n\t} as ExtensionAPI;\n\n\treturn api;\n}\n\nasync function runExtensionFactory(\n\tfactory: ExtensionFactory,\n\tapi: ExtensionAPI,\n\ttimeoutMs: number = EXTENSION_FACTORY_TIMEOUT_MS,\n): Promise<void> {\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\tconst completion = Promise.resolve().then(() => factory(api));\n\ttry {\n\t\tawait Promise.race([\n\t\t\tcompletion,\n\t\t\tnew Promise<never>((_resolve, reject) => {\n\t\t\t\ttimeout = setTimeout(\n\t\t\t\t\t() => reject(new ExtensionFactoryTimeoutError(`Extension factory timed out after ${timeoutMs}ms`)),\n\t\t\t\t\tMath.max(0, timeoutMs),\n\t\t\t\t);\n\t\t\t}),\n\t\t]);\n\t} finally {\n\t\tif (timeout) clearTimeout(timeout);\n\t}\n}\n\nasync function loadExtensionModule(extensionPath: string, opts?: { fresh?: boolean; moduleTimeoutMs?: number }) {\n\tconst jiti = createJiti(import.meta.url, {\n\t\tmoduleCache: false,\n\t\t// In Bun binary: use virtualModules for bundled packages (no filesystem resolution)\n\t\t// Also disable tryNative so jiti handles ALL imports (not just the entry point)\n\t\t// In Node.js/dev: use aliases to resolve to node_modules paths\n\t\t...(isBunBinary ? { virtualModules: VIRTUAL_MODULES, tryNative: false } : { alias: getAliases() }),\n\t});\n\n\t// When fresh mode is enabled, append a cache-busting query string to force re-import\n\tlet resolvedPath = extensionPath;\n\tif (opts?.fresh) {\n\t\tconst separator = extensionPath.includes(\"?\") ? \"&\" : \"?\";\n\t\tresolvedPath = `${extensionPath}${separator}fresh=${Date.now()}`;\n\t}\n\n\tconst timeoutMs = opts?.moduleTimeoutMs ?? EXTENSION_FACTORY_TIMEOUT_MS;\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\ttry {\n\t\tconst module = await Promise.race([\n\t\t\tjiti.import(resolvedPath, { default: true }),\n\t\t\tnew Promise<never>((_resolve, reject) => {\n\t\t\t\ttimeout = setTimeout(\n\t\t\t\t\t() => reject(new Error(`Extension module import timed out after ${timeoutMs}ms: ${extensionPath}`)),\n\t\t\t\t\tMath.max(0, timeoutMs),\n\t\t\t\t);\n\t\t\t}),\n\t\t]);\n\t\tconst factory = module as ExtensionFactory;\n\t\treturn typeof factory !== \"function\" ? undefined : factory;\n\t} finally {\n\t\tif (timeout) clearTimeout(timeout);\n\t}\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\tcommands: new Map(),\n\t\tflags: new Map(),\n\t\tshortcuts: new Map(),\n\t\teventUnsubscribes: [],\n\t\tdisposers: [],\n\t};\n}\n\n/**\n * Unsubscribe a replaced extension generation's pi.events handlers from the shared\n * event bus and invoke cleanup callbacks. Without this, every hot reload leaves the\n * previous generation's handlers subscribed, pinning the old module graph in memory\n * and double-processing events.\n */\nexport async function disposeExtensionEventSubscriptions(\n\textensions: Extension[],\n\toptions: { deactivate?: boolean; timeoutMs?: number } = {},\n): Promise<void> {\n\tconst deadline = Date.now() + (options.timeoutMs ?? EXTENSION_DISPOSAL_TIMEOUT_MS);\n\tfor (const extension of extensions) {\n\t\tif (options.deactivate ?? true) inactiveExtensions.add(extension);\n\t\t// Unsubscribe event listeners\n\t\tfor (const unsubscribe of extension.eventUnsubscribes) {\n\t\t\ttry {\n\t\t\t\tunsubscribe();\n\t\t\t} catch {\n\t\t\t\t// Disposal must never break a reload.\n\t\t\t}\n\t\t}\n\t\textension.eventUnsubscribes.length = 0;\n\n\t\t// Invoke every cleanup callback, but never let one unresolved async disposer block reload or\n\t\t// shutdown indefinitely. Late continuations retain rejection handlers and the inactive API guard.\n\t\tfor (const disposer of extension.disposers) {\n\t\t\ttry {\n\t\t\t\tconst result = disposer();\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\tconst completion = Promise.resolve(result).catch(() => undefined);\n\t\t\t\t\tconst remainingMs = Math.max(0, deadline - Date.now());\n\t\t\t\t\tif (remainingMs > 0) {\n\t\t\t\t\t\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\t\t\tawait Promise.race([\n\t\t\t\t\t\t\tcompletion,\n\t\t\t\t\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t\t\t\t\ttimeout = setTimeout(resolve, remainingMs);\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tif (timeout) clearTimeout(timeout);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Disposal must never break a reload.\n\t\t\t}\n\t\t}\n\t\textension.disposers.length = 0;\n\t}\n}\n\ninterface ExtensionLoadRuntimeSnapshot {\n\tpendingProviderRegistrations: ExtensionRuntime[\"pendingProviderRegistrations\"];\n\tflagValues: Map<string, boolean | string>;\n\tproviderRegistrations: ExtensionRuntime[\"providerRegistrations\"];\n}\n\nfunction snapshotExtensionLoadRuntime(runtime: ExtensionRuntime): ExtensionLoadRuntimeSnapshot {\n\treturn {\n\t\tpendingProviderRegistrations: [...runtime.pendingProviderRegistrations],\n\t\tflagValues: new Map(runtime.flagValues),\n\t\tproviderRegistrations: new Map(runtime.providerRegistrations),\n\t};\n}\n\nfunction restoreExtensionLoadRuntime(runtime: ExtensionRuntime, snapshot: ExtensionLoadRuntimeSnapshot): void {\n\tfor (const [name, current] of runtime.providerRegistrations) {\n\t\tconst previous = snapshot.providerRegistrations.get(name);\n\t\tif (!previous || previous.config !== current.config || previous.extensionPath !== current.extensionPath) {\n\t\t\ttry {\n\t\t\t\truntime.unregisterProvider(name, current.extensionPath);\n\t\t\t} catch {\n\t\t\t\t// Preserve the original factory failure; reload rollback remains the outer safety net.\n\t\t\t}\n\t\t}\n\t}\n\tfor (const [name, previous] of snapshot.providerRegistrations) {\n\t\tconst current = runtime.providerRegistrations.get(name);\n\t\tif (current?.config === previous.config && current.extensionPath === previous.extensionPath) continue;\n\t\ttry {\n\t\t\truntime.registerProvider(name, previous.config, previous.extensionPath);\n\t\t} catch {\n\t\t\t// Preserve the original factory failure; reload rollback remains the outer safety net.\n\t\t}\n\t}\n\n\truntime.pendingProviderRegistrations = snapshot.pendingProviderRegistrations;\n\truntime.flagValues.clear();\n\tfor (const [name, value] of snapshot.flagValues) {\n\t\truntime.flagValues.set(name, value);\n\t}\n}\n\nexport async function loadExtension(\n\textensionPath: string,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n\topts?: { fresh?: boolean; factoryTimeoutMs?: number; moduleTimeoutMs?: number },\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, opts);\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\tconst runtimeSnapshot = snapshotExtensionLoadRuntime(runtime);\n\t\ttry {\n\t\t\tawait runExtensionFactory(factory, api, opts?.factoryTimeoutMs);\n\t\t} catch (err) {\n\t\t\tawait disposeExtensionEventSubscriptions([extension]);\n\t\t\trestoreExtensionLoadRuntime(runtime, runtimeSnapshot);\n\t\t\tthrow err;\n\t\t}\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\nfunction createLazyToolDefinition(\n\textension: Extension,\n\tmanifest: LazyToolManifest,\n\tload: () => Promise<void>,\n): ToolDefinition {\n\tconst name = String(manifest.name).trim();\n\tconst definition: ToolDefinition = {\n\t\tname,\n\t\tlabel: typeof manifest.label === \"string\" && manifest.label.trim() ? manifest.label : name,\n\t\tdescription:\n\t\t\ttypeof manifest.description === \"string\" && manifest.description.trim()\n\t\t\t\t? manifest.description\n\t\t\t\t: `Lazy extension tool ${name}`,\n\t\tparameters: (manifest.parameters ?? defaultLazyToolParameters()) as ToolDefinition[\"parameters\"],\n\t\tpromptSnippet: typeof manifest.promptSnippet === \"string\" ? manifest.promptSnippet : undefined,\n\t\tpromptGuidelines: Array.isArray(manifest.promptGuidelines)\n\t\t\t? manifest.promptGuidelines.filter((item): item is string => typeof item === \"string\")\n\t\t\t: undefined,\n\t\ttoolGroup: typeof manifest.toolGroup === \"string\" ? manifest.toolGroup : undefined,\n\t\texecutionMode:\n\t\t\tmanifest.executionMode === \"parallel\" || manifest.executionMode === \"sequential\"\n\t\t\t\t? manifest.executionMode\n\t\t\t\t: undefined,\n\t\texecute: async (toolCallId, params, signal, onUpdate, ctx) => {\n\t\t\tawait load();\n\t\t\tconst loadedDefinition = extension.tools.get(name)?.definition;\n\t\t\tif (!loadedDefinition || loadedDefinition === definition) {\n\t\t\t\tthrow new Error(`Lazy extension ${extension.path} did not register tool ${name}`);\n\t\t\t}\n\t\t\treturn loadedDefinition.execute(toolCallId, params, signal, onUpdate, ctx);\n\t\t},\n\t};\n\treturn definition;\n}\n\nfunction createLazyExtension(\n\textensionPath: string,\n\tresolvedPath: string,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n\tlazyTools: LazyToolManifest[],\n): Extension {\n\tconst extension = createExtension(extensionPath, resolvedPath);\n\tlet restoreLazyToolPlaceholders = (): void => {};\n\tconst load = async (): Promise<void> => {\n\t\tif (inactiveExtensions.has(extension)) {\n\t\t\tthrow new Error(`Lazy extension generation is no longer active: ${extension.path}`);\n\t\t}\n\t\tif (extension.lazy?.loaded) return;\n\t\tif (extension.lazy?.loading) return extension.lazy.loading;\n\n\t\tconst loading = (async () => {\n\t\t\tconst factory = await loadExtensionModule(resolvedPath);\n\t\t\tif (!factory) {\n\t\t\t\tthrow new Error(`Extension does not export a valid factory function: ${extensionPath}`);\n\t\t\t}\n\t\t\tconst api = createExtensionAPI(extension, runtime, cwd, eventBus);\n\t\t\tconst runtimeSnapshot = snapshotExtensionLoadRuntime(runtime);\n\t\t\ttry {\n\t\t\t\tawait runExtensionFactory(factory, api);\n\t\t\t} catch (error) {\n\t\t\t\tawait disposeExtensionEventSubscriptions([extension], { deactivate: false });\n\t\t\t\trestoreExtensionLoadRuntime(runtime, runtimeSnapshot);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (extension.lazy) {\n\t\t\t\textension.lazy.loaded = true;\n\t\t\t\textension.lazy.loading = undefined;\n\t\t\t}\n\t\t})();\n\n\t\tif (extension.lazy) extension.lazy.loading = loading;\n\t\ttry {\n\t\t\tawait loading;\n\t\t} catch (err) {\n\t\t\tawait disposeExtensionEventSubscriptions([extension], {\n\t\t\t\tdeactivate: err instanceof ExtensionFactoryTimeoutError,\n\t\t\t});\n\t\t\textension.handlers.clear();\n\t\t\textension.messageRenderers.clear();\n\t\t\textension.commands.clear();\n\t\t\textension.flags.clear();\n\t\t\textension.shortcuts.clear();\n\t\t\textension.tools.clear();\n\t\t\trestoreLazyToolPlaceholders();\n\t\t\tif (extension.lazy) extension.lazy.loading = undefined;\n\t\t\tthrow err;\n\t\t}\n\t};\n\n\textension.lazy = { loaded: false, load };\n\trestoreLazyToolPlaceholders = () => {\n\t\tfor (const tool of lazyTools) {\n\t\t\tconst name = String(tool.name).trim();\n\t\t\textension.tools.set(name, {\n\t\t\t\tdefinition: createLazyToolDefinition(extension, tool, load),\n\t\t\t\tsourceInfo: extension.sourceInfo,\n\t\t\t});\n\t\t}\n\t};\n\trestoreLazyToolPlaceholders();\n\treturn extension;\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\toptions: { factoryTimeoutMs?: number } = {},\n): Promise<Extension> {\n\tconst extension = createExtension(extensionPath, extensionPath);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst api = createExtensionAPI(extension, runtime, resolvedCwd, eventBus);\n\tconst runtimeSnapshot = snapshotExtensionLoadRuntime(runtime);\n\ttry {\n\t\tawait runExtensionFactory(factory, api, options.factoryTimeoutMs);\n\t} catch (err) {\n\t\tawait disposeExtensionEventSubscriptions([extension]);\n\t\trestoreExtensionLoadRuntime(runtime, runtimeSnapshot);\n\t\tthrow err;\n\t}\n\treturn extension;\n}\n\n/**\n * Load extensions from paths.\n */\nexport async function loadExtensions(\n\tpaths: Array<string | ExtensionLoadSpec>,\n\tcwd: string,\n\teventBus?: EventBus,\n): Promise<LoadExtensionsResult> {\n\tconst extensions: Extension[] = [];\n\tconst errors: Array<{ path: string; error: string }> = [];\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst resolvedEventBus = eventBus ?? createEventBus();\n\tconst runtime = createExtensionRuntime();\n\n\tfor (const spec of paths) {\n\t\tconst normalized = typeof spec === \"string\" ? { path: spec } : spec;\n\t\tconst extPath = normalized.path;\n\t\tconst resolvedPath = resolvePath(extPath, resolvedCwd, { normalizeUnicodeSpaces: true });\n\t\tconst lazyTools = (normalized.lazyTools ?? inferLazyToolsForExtensionPath(resolvedPath))?.filter(\n\t\t\t(tool) => typeof tool.name === \"string\" && tool.name.trim(),\n\t\t);\n\t\t// Extension imports can be CPU-heavy under jiti. Yield around each load so\n\t\t// interactive reloads can repaint/status-update instead of freezing the TUI\n\t\t// for the whole extension set. Lazy tool manifests skip import entirely here.\n\t\tawait yieldToEventLoop();\n\t\tif (lazyTools?.length) {\n\t\t\textensions.push(createLazyExtension(extPath, resolvedPath, resolvedCwd, resolvedEventBus, runtime, lazyTools));\n\t\t\tawait yieldToEventLoop();\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { extension, error } = await loadExtension(extPath, resolvedCwd, resolvedEventBus, runtime);\n\t\tawait yieldToEventLoop();\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,\n\t};\n}\n\ninterface LazyToolManifest {\n\tname?: unknown;\n\tlabel?: unknown;\n\tdescription?: unknown;\n\tparameters?: unknown;\n\tpromptSnippet?: unknown;\n\tpromptGuidelines?: unknown;\n\ttoolGroup?: unknown;\n\texecutionMode?: unknown;\n\t/** Optional extension entry this tool belongs to when a package declares multiple entries. */\n\textension?: unknown;\n}\n\ninterface PiManifest {\n\textensions?: string[];\n\tthemes?: string[];\n\tskills?: string[];\n\tprompts?: string[];\n\t/** Tool metadata for opt-in lazy extension loading. Factories stay unloaded until one of these tools runs. */\n\tlazyTools?: LazyToolManifest[];\n\t/** Alternate nested spelling: { tools: [...] }. */\n\tlazy?: { tools?: LazyToolManifest[] } | boolean;\n}\n\ninterface ExtensionLoadSpec {\n\tpath: string;\n\tlazyTools?: LazyToolManifest[];\n}\n\nfunction getManifestLazyTools(manifest: PiManifest | null): LazyToolManifest[] | undefined {\n\tif (!manifest) return undefined;\n\tconst tools = Array.isArray(manifest.lazyTools)\n\t\t? manifest.lazyTools\n\t\t: typeof manifest.lazy === \"object\" && Array.isArray(manifest.lazy.tools)\n\t\t\t? manifest.lazy.tools\n\t\t\t: undefined;\n\treturn tools?.filter((tool) => tool && typeof tool.name === \"string\" && tool.name.trim());\n}\n\nfunction defaultLazyToolParameters(): ToolDefinition[\"parameters\"] {\n\treturn { type: \"object\", properties: {}, additionalProperties: false } as ToolDefinition[\"parameters\"];\n}\n\nfunction lazyToolsForEntry(tools: LazyToolManifest[] | undefined, dir: string, entryPath: string, entryCount: number) {\n\tif (!tools?.length) return undefined;\n\tconst selected = tools.filter((tool) => {\n\t\tif (typeof tool.extension !== \"string\" || !tool.extension.trim()) {\n\t\t\treturn entryCount === 1;\n\t\t}\n\t\treturn path.resolve(dir, tool.extension) === entryPath;\n\t});\n\treturn selected.length > 0 ? selected : undefined;\n}\n\nfunction inferLazyToolsForExtensionPath(resolvedPath: string): LazyToolManifest[] | undefined {\n\tconst dir = path.dirname(resolvedPath);\n\tconst manifest = readPiManifest(path.join(dir, \"package.json\"));\n\tconst lazyTools = getManifestLazyTools(manifest);\n\tif (!manifest?.extensions?.length || !lazyTools?.length) return undefined;\n\tconst resolvedEntries = manifest.extensions.map((extPath) => path.resolve(dir, extPath));\n\tif (!resolvedEntries.includes(resolvedPath)) return undefined;\n\treturn lazyToolsForEntry(lazyTools, dir, resolvedPath, resolvedEntries.length);\n}\n\nfunction readPiManifest(packageJsonPath: string): PiManifest | null {\n\ttry {\n\t\tconst content = fs.readFileSync(packageJsonPath, \"utf-8\");\n\t\tconst pkg = JSON.parse(content);\n\t\tif (pkg.pi && typeof pkg.pi === \"object\") {\n\t\t\treturn pkg.pi as PiManifest;\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\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): ExtensionLoadSpec[] | 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 resolvedEntries = manifest.extensions\n\t\t\t\t.map((extPath) => path.resolve(dir, extPath))\n\t\t\t\t.filter((resolvedExtPath) => fs.existsSync(resolvedExtPath));\n\t\t\tif (resolvedEntries.length > 0) {\n\t\t\t\tconst lazyTools = getManifestLazyTools(manifest);\n\t\t\t\treturn resolvedEntries.map((entryPath) => ({\n\t\t\t\t\tpath: entryPath,\n\t\t\t\t\tlazyTools: lazyToolsForEntry(lazyTools, dir, entryPath, resolvedEntries.length),\n\t\t\t\t}));\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 [{ path: indexTs }];\n\t}\n\tif (fs.existsSync(indexJs)) {\n\t\treturn [{ path: 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): ExtensionLoadSpec[] {\n\tif (!fs.existsSync(dir)) {\n\t\treturn [];\n\t}\n\n\tconst discovered: ExtensionLoadSpec[] = [];\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({ path: 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: ExtensionLoadSpec[] = [];\n\tconst seen = new Set<string>();\n\n\tconst addPaths = (paths: Array<string | ExtensionLoadSpec>) => {\n\t\tfor (const spec of paths) {\n\t\t\tconst normalized = typeof spec === \"string\" ? { path: spec } : spec;\n\t\t\tconst resolved = path.resolve(normalized.path);\n\t\t\tif (!seen.has(resolved)) {\n\t\t\t\tseen.add(resolved);\n\t\t\t\tallPaths.push(normalized);\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;AAuBH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhE,OAAO,KAAK,EACX,SAAS,EAET,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EAKpB,MAAM,YAAY,CAAC;AAkJpB;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAiEzD;AA2SD;;;;;GAKG;AACH,wBAAsB,kCAAkC,CACvD,UAAU,EAAE,SAAS,EAAE,EACvB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACxD,OAAO,CAAC,IAAI,CAAC,CAuCf;AA4CD,wBAAsB,aAAa,CAClC,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,OAAO,CAAC;IAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAyBhE;AA2GD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAa,EAC1B,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GACzC,OAAO,CAAC,SAAS,CAAC,CAapB;AAED;;GAEG;AACH,wBAAsB,cAAc,CACnC,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,iBAAiB,CAAC,EACxC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA0C/B;AAED,UAAU,gBAAgB;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAaD,UAAU,iBAAiB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AA2ID;;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,CA4C/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 * as _bundledPiAgentCore from \"@caupulican/pi-agent-core\";\nimport * as _bundledPiAi from \"@caupulican/pi-ai\";\nimport * as _bundledPiAiOauth from \"@caupulican/pi-ai/oauth\";\nimport type { KeyId } from \"@caupulican/pi-tui\";\nimport * as _bundledPiTui from \"@caupulican/pi-tui\";\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 @caupulican/pi-adaptative.\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 { createSyntheticSourceInfo } from \"../source-info.ts\";\nimport type {\n\tExtension,\n\tExtensionAPI,\n\tExtensionFactory,\n\tExtensionRuntime,\n\tLoadExtensionsResult,\n\tMessageRenderer,\n\tProviderConfig,\n\tRegisteredCommand,\n\tToolDefinition,\n} from \"./types.ts\";\n\nconst EXTENSION_FACTORY_TIMEOUT_MS = 30_000;\nconst EXTENSION_DISPOSAL_TIMEOUT_MS = 5_000;\nconst inactiveExtensions = new WeakSet<Extension>();\n\nclass ExtensionFactoryTimeoutError extends Error {}\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\"@caupulican/pi-agent-core\": _bundledPiAgentCore,\n\t\"@caupulican/pi-tui\": _bundledPiTui,\n\t\"@caupulican/pi-ai\": _bundledPiAi,\n\t\"@caupulican/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@caupulican/pi-adaptative\": _bundledPiCodingAgent,\n\t\"@earendil-works/pi-agent-core\": _bundledPiAgentCore,\n\t\"@earendil-works/pi-tui\": _bundledPiTui,\n\t\"@earendil-works/pi-ai\": _bundledPiAi,\n\t\"@earendil-works/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@earendil-works/pi-coding-agent\": _bundledPiCodingAgent,\n\t\"@mariozechner/pi-agent-core\": _bundledPiAgentCore,\n\t\"@mariozechner/pi-tui\": _bundledPiTui,\n\t\"@mariozechner/pi-ai\": _bundledPiAi,\n\t\"@mariozechner/pi-ai/oauth\": _bundledPiAiOauth,\n\t\"@mariozechner/pi-coding-agent\": _bundledPiCodingAgent,\n};\n\nfunction uniquePaths(paths: string[]): string[] {\n\treturn [...new Set(paths)];\n}\n\nfunction safeRealpath(filePath: string): string {\n\ttry {\n\t\treturn fs.realpathSync(filePath);\n\t} catch {\n\t\treturn filePath;\n\t}\n}\n\n/**\n * Get aliases for jiti (used in Node.js/development 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 loaderFile = fileURLToPath(import.meta.url);\n\tconst realLoaderFile = safeRealpath(loaderFile);\n\tconst __dirname = path.dirname(loaderFile);\n\tconst realDirname = path.dirname(realLoaderFile);\n\tconst packageIndex = [\n\t\tpath.resolve(__dirname, \"../..\", \"index.ts\"),\n\t\tpath.resolve(__dirname, \"../..\", \"index.js\"),\n\t].find((candidate) => fs.existsSync(candidate));\n\tif (!packageIndex) throw new Error(\"Unable to resolve the coding-agent package entry\");\n\n\tconst moduleRequires = uniquePaths([loaderFile, realLoaderFile]).map((file) => createRequire(file));\n\tconst resolveModule = (specifier: string): string => {\n\t\tfor (const moduleRequire of moduleRequires) {\n\t\t\ttry {\n\t\t\t\treturn moduleRequire.resolve(specifier);\n\t\t\t} catch {\n\t\t\t\t// Try the next resolution base. Linked global installs may resolve the\n\t\t\t\t// loader through the global symlink path while workspace dependencies are\n\t\t\t\t// hoisted beside the real source path.\n\t\t\t}\n\t\t}\n\t\treturn fileURLToPath(import.meta.resolve(specifier));\n\t};\n\n\tconst typeboxEntry = resolveModule(\"typebox\");\n\tconst typeboxCompileEntry = resolveModule(\"typebox/compile\");\n\tconst typeboxValueEntry = resolveModule(\"typebox/value\");\n\n\tconst packagesRoots = uniquePaths([\n\t\tpath.resolve(__dirname, \"../../../../\"),\n\t\tpath.resolve(realDirname, \"../../../../\"),\n\t]);\n\tconst resolveWorkspaceOrImport = (workspaceRelativePath: string, specifier: string): string => {\n\t\tfor (const packagesRoot of packagesRoots) {\n\t\t\tconst workspacePath = path.join(packagesRoot, workspaceRelativePath);\n\t\t\tif (fs.existsSync(workspacePath)) {\n\t\t\t\treturn workspacePath;\n\t\t\t}\n\t\t}\n\t\treturn resolveModule(specifier);\n\t};\n\n\tconst piCodingAgentEntry = packageIndex;\n\tconst piAgentCoreEntry = resolveWorkspaceOrImport(\"agent/src/index.ts\", \"@caupulican/pi-agent-core\");\n\tconst piAgentCoreNodeEntry = resolveWorkspaceOrImport(\"agent/src/node.ts\", \"@caupulican/pi-agent-core/node\");\n\tconst piAgentCorePathsEntry = resolveWorkspaceOrImport(\n\t\t\"agent/src/utils/paths.ts\",\n\t\t\"@caupulican/pi-agent-core/paths\",\n\t);\n\tconst piTuiEntry = resolveWorkspaceOrImport(\"tui/src/index.ts\", \"@caupulican/pi-tui\");\n\tconst piAiEntry = resolveWorkspaceOrImport(\"ai/src/index.ts\", \"@caupulican/pi-ai\");\n\tconst piAiOauthEntry = resolveWorkspaceOrImport(\"ai/src/oauth.ts\", \"@caupulican/pi-ai/oauth\");\n\n\t_aliases = {\n\t\t\"@caupulican/pi-agent-core/node\": piAgentCoreNodeEntry,\n\t\t\"@caupulican/pi-agent-core/paths\": piAgentCorePathsEntry,\n\t\t\"@caupulican/pi-ai/oauth\": piAiOauthEntry,\n\t\t\"@caupulican/pi-adaptative\": piCodingAgentEntry,\n\t\t\"@caupulican/pi-agent-core\": piAgentCoreEntry,\n\t\t\"@caupulican/pi-tui\": piTuiEntry,\n\t\t\"@caupulican/pi-ai\": piAiEntry,\n\t\t\"@earendil-works/pi-agent-core/node\": piAgentCoreNodeEntry,\n\t\t\"@earendil-works/pi-agent-core/paths\": piAgentCorePathsEntry,\n\t\t\"@earendil-works/pi-ai/oauth\": piAiOauthEntry,\n\t\t\"@earendil-works/pi-coding-agent\": piCodingAgentEntry,\n\t\t\"@earendil-works/pi-agent-core\": piAgentCoreEntry,\n\t\t\"@earendil-works/pi-tui\": piTuiEntry,\n\t\t\"@earendil-works/pi-ai\": piAiEntry,\n\t\t\"@mariozechner/pi-agent-core/node\": piAgentCoreNodeEntry,\n\t\t\"@mariozechner/pi-agent-core/paths\": piAgentCorePathsEntry,\n\t\t\"@mariozechner/pi-ai/oauth\": piAiOauthEntry,\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\": piAiEntry,\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\nfunction yieldToEventLoop(): Promise<void> {\n\treturn new Promise((resolve) => setImmediate(resolve));\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 assertActive = () => {\n\t\tif (state.staleMessage) {\n\t\t\tthrow new Error(state.staleMessage);\n\t\t}\n\t};\n\n\tconst providersByExtension = new Map<string, Set<string>>();\n\tconst providerRegistrations = new Map<string, { config: ProviderConfig; extensionPath?: string }>();\n\tconst memoryProvidersByExtension = new Map<string, Set<Parameters<ExtensionRuntime[\"registerMemoryProvider\"]>[0]>>();\n\tconst contextMemoryProvidersByExtension = new Map<\n\t\tstring,\n\t\tSet<Parameters<ExtensionRuntime[\"registerContextMemoryProvider\"]>[0]>\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: notInitialized,\n\t\tgetThinkingLevel: notInitialized,\n\t\tsetThinkingLevel: notInitialized,\n\t\tgetExternalResourceRoots: notInitialized,\n\t\tregisterMemoryProvider: notInitialized,\n\t\tregisterContextMemoryProvider: notInitialized,\n\t\treportSpawnedUsage: notInitialized,\n\t\tflagValues: new Map(),\n\t\tpendingProviderRegistrations: [],\n\t\tassertActive,\n\t\tinvalidate: (message) => {\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},\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\tunregisterProvider: (name) => {\n\t\t\truntime.pendingProviderRegistrations = runtime.pendingProviderRegistrations.filter((r) => r.name !== name);\n\t\t},\n\t\tprovidersByExtension,\n\t\tproviderRegistrations,\n\t\tgetProvidersForExtension: (extensionPath: string) => {\n\t\t\treturn [...(providersByExtension.get(extensionPath) ?? [])];\n\t\t},\n\t\tmemoryProvidersByExtension,\n\t\tcontextMemoryProvidersByExtension,\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\t// A factory can time out while its promise continues running. Route every later API call through\n\t// an extension-generation guard so stale async work cannot register tools, handlers, or providers.\n\tconst sharedRuntime = runtime;\n\truntime = new Proxy(sharedRuntime, {\n\t\tget(target, property, receiver) {\n\t\t\tif (property === \"assertActive\") {\n\t\t\t\treturn () => {\n\t\t\t\t\ttarget.assertActive();\n\t\t\t\t\tif (inactiveExtensions.has(extension)) {\n\t\t\t\t\t\tthrow new Error(`Extension generation is no longer active: ${extension.path}`);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn Reflect.get(target, property, receiver);\n\t\t},\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\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\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\tgetExternalResourceRoots() {\n\t\t\truntime.assertActive();\n\t\t\treturn runtime.getExternalResourceRoots();\n\t\t},\n\n\t\tregisterMemoryProvider(provider) {\n\t\t\truntime.assertActive();\n\t\t\truntime.registerMemoryProvider(provider);\n\t\t\tconst providers = runtime.memoryProvidersByExtension.get(extension.path) ?? new Set();\n\t\t\tproviders.add(provider);\n\t\t\truntime.memoryProvidersByExtension.set(extension.path, providers);\n\t\t},\n\n\t\tregisterContextMemoryProvider(provider) {\n\t\t\truntime.assertActive();\n\t\t\truntime.registerContextMemoryProvider(provider);\n\t\t\tconst providers = runtime.contextMemoryProvidersByExtension.get(extension.path) ?? new Set();\n\t\t\tproviders.add(provider);\n\t\t\truntime.contextMemoryProvidersByExtension.set(extension.path, providers);\n\t\t},\n\n\t\treportSpawnedUsage(usage, opts) {\n\t\t\truntime.assertActive();\n\t\t\truntime.reportSpawnedUsage(usage, opts);\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(name: string, config: ProviderConfig) {\n\t\t\truntime.assertActive();\n\t\t\truntime.registerProvider(name, config, 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\tonDispose(fn: () => void | Promise<void>): void {\n\t\t\truntime.assertActive();\n\t\t\textension.disposers.push(fn);\n\t\t},\n\n\t\t// Track bus subscriptions per extension generation so hot reloads can\n\t\t// unsubscribe replaced generations (see disposeExtensionEventSubscriptions).\n\t\tevents: {\n\t\t\temit: (channel: string, data: unknown) => {\n\t\t\t\truntime.assertActive();\n\t\t\t\teventBus.emit(channel, data);\n\t\t\t},\n\t\t\ton: (channel: string, handler: (data: unknown) => void) => {\n\t\t\t\truntime.assertActive();\n\t\t\t\tconst unsubscribe = eventBus.on(channel, handler);\n\t\t\t\textension.eventUnsubscribes.push(unsubscribe);\n\t\t\t\treturn unsubscribe;\n\t\t\t},\n\t\t},\n\t} as ExtensionAPI;\n\n\treturn api;\n}\n\nasync function runExtensionFactory(\n\tfactory: ExtensionFactory,\n\tapi: ExtensionAPI,\n\ttimeoutMs: number = EXTENSION_FACTORY_TIMEOUT_MS,\n): Promise<void> {\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\tconst completion = Promise.resolve().then(() => factory(api));\n\ttry {\n\t\tawait Promise.race([\n\t\t\tcompletion,\n\t\t\tnew Promise<never>((_resolve, reject) => {\n\t\t\t\ttimeout = setTimeout(\n\t\t\t\t\t() => reject(new ExtensionFactoryTimeoutError(`Extension factory timed out after ${timeoutMs}ms`)),\n\t\t\t\t\tMath.max(0, timeoutMs),\n\t\t\t\t);\n\t\t\t}),\n\t\t]);\n\t} finally {\n\t\tif (timeout) clearTimeout(timeout);\n\t}\n}\n\nasync function loadExtensionModule(extensionPath: string, opts?: { fresh?: boolean; moduleTimeoutMs?: number }) {\n\tconst jiti = createJiti(import.meta.url, {\n\t\tmoduleCache: false,\n\t\t// In Bun binary: use virtualModules for bundled packages (no filesystem resolution)\n\t\t// Also disable tryNative so jiti handles ALL imports (not just the entry point)\n\t\t// In Node.js/dev: use aliases to resolve to node_modules paths\n\t\t...(isBunBinary ? { virtualModules: VIRTUAL_MODULES, tryNative: false } : { alias: getAliases() }),\n\t});\n\n\t// Every call gets a fresh jiti instance with moduleCache disabled. Do not append a query string:\n\t// jiti's Windows path resolver treats it as part of the .ts filename.\n\tconst timeoutMs = opts?.moduleTimeoutMs ?? EXTENSION_FACTORY_TIMEOUT_MS;\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\ttry {\n\t\tconst module = await Promise.race([\n\t\t\tjiti.import(extensionPath, { default: true }),\n\t\t\tnew Promise<never>((_resolve, reject) => {\n\t\t\t\ttimeout = setTimeout(\n\t\t\t\t\t() => reject(new Error(`Extension module import timed out after ${timeoutMs}ms: ${extensionPath}`)),\n\t\t\t\t\tMath.max(0, timeoutMs),\n\t\t\t\t);\n\t\t\t}),\n\t\t]);\n\t\tconst factory = module as ExtensionFactory;\n\t\treturn typeof factory !== \"function\" ? undefined : factory;\n\t} finally {\n\t\tif (timeout) clearTimeout(timeout);\n\t}\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\tcommands: new Map(),\n\t\tflags: new Map(),\n\t\tshortcuts: new Map(),\n\t\teventUnsubscribes: [],\n\t\tdisposers: [],\n\t};\n}\n\n/**\n * Unsubscribe a replaced extension generation's pi.events handlers from the shared\n * event bus and invoke cleanup callbacks. Without this, every hot reload leaves the\n * previous generation's handlers subscribed, pinning the old module graph in memory\n * and double-processing events.\n */\nexport async function disposeExtensionEventSubscriptions(\n\textensions: Extension[],\n\toptions: { deactivate?: boolean; timeoutMs?: number } = {},\n): Promise<void> {\n\tconst deadline = Date.now() + (options.timeoutMs ?? EXTENSION_DISPOSAL_TIMEOUT_MS);\n\tfor (const extension of extensions) {\n\t\tif (options.deactivate ?? true) inactiveExtensions.add(extension);\n\t\t// Unsubscribe event listeners\n\t\tfor (const unsubscribe of extension.eventUnsubscribes) {\n\t\t\ttry {\n\t\t\t\tunsubscribe();\n\t\t\t} catch {\n\t\t\t\t// Disposal must never break a reload.\n\t\t\t}\n\t\t}\n\t\textension.eventUnsubscribes.length = 0;\n\n\t\t// Invoke every cleanup callback, but never let one unresolved async disposer block reload or\n\t\t// shutdown indefinitely. Late continuations retain rejection handlers and the inactive API guard.\n\t\tfor (const disposer of extension.disposers) {\n\t\t\ttry {\n\t\t\t\tconst result = disposer();\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\tconst completion = Promise.resolve(result).catch(() => undefined);\n\t\t\t\t\tconst remainingMs = Math.max(0, deadline - Date.now());\n\t\t\t\t\tif (remainingMs > 0) {\n\t\t\t\t\t\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\t\t\tawait Promise.race([\n\t\t\t\t\t\t\tcompletion,\n\t\t\t\t\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t\t\t\t\ttimeout = setTimeout(resolve, remainingMs);\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tif (timeout) clearTimeout(timeout);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Disposal must never break a reload.\n\t\t\t}\n\t\t}\n\t\textension.disposers.length = 0;\n\t}\n}\n\ninterface ExtensionLoadRuntimeSnapshot {\n\tpendingProviderRegistrations: ExtensionRuntime[\"pendingProviderRegistrations\"];\n\tflagValues: Map<string, boolean | string>;\n\tproviderRegistrations: ExtensionRuntime[\"providerRegistrations\"];\n}\n\nfunction snapshotExtensionLoadRuntime(runtime: ExtensionRuntime): ExtensionLoadRuntimeSnapshot {\n\treturn {\n\t\tpendingProviderRegistrations: [...runtime.pendingProviderRegistrations],\n\t\tflagValues: new Map(runtime.flagValues),\n\t\tproviderRegistrations: new Map(runtime.providerRegistrations),\n\t};\n}\n\nfunction restoreExtensionLoadRuntime(runtime: ExtensionRuntime, snapshot: ExtensionLoadRuntimeSnapshot): void {\n\tfor (const [name, current] of runtime.providerRegistrations) {\n\t\tconst previous = snapshot.providerRegistrations.get(name);\n\t\tif (!previous || previous.config !== current.config || previous.extensionPath !== current.extensionPath) {\n\t\t\ttry {\n\t\t\t\truntime.unregisterProvider(name, current.extensionPath);\n\t\t\t} catch {\n\t\t\t\t// Preserve the original factory failure; reload rollback remains the outer safety net.\n\t\t\t}\n\t\t}\n\t}\n\tfor (const [name, previous] of snapshot.providerRegistrations) {\n\t\tconst current = runtime.providerRegistrations.get(name);\n\t\tif (current?.config === previous.config && current.extensionPath === previous.extensionPath) continue;\n\t\ttry {\n\t\t\truntime.registerProvider(name, previous.config, previous.extensionPath);\n\t\t} catch {\n\t\t\t// Preserve the original factory failure; reload rollback remains the outer safety net.\n\t\t}\n\t}\n\n\truntime.pendingProviderRegistrations = snapshot.pendingProviderRegistrations;\n\truntime.flagValues.clear();\n\tfor (const [name, value] of snapshot.flagValues) {\n\t\truntime.flagValues.set(name, value);\n\t}\n}\n\nexport async function loadExtension(\n\textensionPath: string,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n\topts?: { fresh?: boolean; factoryTimeoutMs?: number; moduleTimeoutMs?: number },\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, opts);\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\tconst runtimeSnapshot = snapshotExtensionLoadRuntime(runtime);\n\t\ttry {\n\t\t\tawait runExtensionFactory(factory, api, opts?.factoryTimeoutMs);\n\t\t} catch (err) {\n\t\t\tawait disposeExtensionEventSubscriptions([extension]);\n\t\t\trestoreExtensionLoadRuntime(runtime, runtimeSnapshot);\n\t\t\tthrow err;\n\t\t}\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\nfunction createLazyToolDefinition(\n\textension: Extension,\n\tmanifest: LazyToolManifest,\n\tload: () => Promise<void>,\n): ToolDefinition {\n\tconst name = String(manifest.name).trim();\n\tconst definition: ToolDefinition = {\n\t\tname,\n\t\tlabel: typeof manifest.label === \"string\" && manifest.label.trim() ? manifest.label : name,\n\t\tdescription:\n\t\t\ttypeof manifest.description === \"string\" && manifest.description.trim()\n\t\t\t\t? manifest.description\n\t\t\t\t: `Lazy extension tool ${name}`,\n\t\tparameters: (manifest.parameters ?? defaultLazyToolParameters()) as ToolDefinition[\"parameters\"],\n\t\tpromptSnippet: typeof manifest.promptSnippet === \"string\" ? manifest.promptSnippet : undefined,\n\t\tpromptGuidelines: Array.isArray(manifest.promptGuidelines)\n\t\t\t? manifest.promptGuidelines.filter((item): item is string => typeof item === \"string\")\n\t\t\t: undefined,\n\t\ttoolGroup: typeof manifest.toolGroup === \"string\" ? manifest.toolGroup : undefined,\n\t\texecutionMode:\n\t\t\tmanifest.executionMode === \"parallel\" || manifest.executionMode === \"sequential\"\n\t\t\t\t? manifest.executionMode\n\t\t\t\t: undefined,\n\t\texecute: async (toolCallId, params, signal, onUpdate, ctx) => {\n\t\t\tawait load();\n\t\t\tconst loadedDefinition = extension.tools.get(name)?.definition;\n\t\t\tif (!loadedDefinition || loadedDefinition === definition) {\n\t\t\t\tthrow new Error(`Lazy extension ${extension.path} did not register tool ${name}`);\n\t\t\t}\n\t\t\treturn loadedDefinition.execute(toolCallId, params, signal, onUpdate, ctx);\n\t\t},\n\t};\n\treturn definition;\n}\n\nfunction createLazyExtension(\n\textensionPath: string,\n\tresolvedPath: string,\n\tcwd: string,\n\teventBus: EventBus,\n\truntime: ExtensionRuntime,\n\tlazyTools: LazyToolManifest[],\n): Extension {\n\tconst extension = createExtension(extensionPath, resolvedPath);\n\tlet restoreLazyToolPlaceholders = (): void => {};\n\tconst load = async (): Promise<void> => {\n\t\tif (inactiveExtensions.has(extension)) {\n\t\t\tthrow new Error(`Lazy extension generation is no longer active: ${extension.path}`);\n\t\t}\n\t\tif (extension.lazy?.loaded) return;\n\t\tif (extension.lazy?.loading) return extension.lazy.loading;\n\n\t\tconst loading = (async () => {\n\t\t\tconst factory = await loadExtensionModule(resolvedPath);\n\t\t\tif (!factory) {\n\t\t\t\tthrow new Error(`Extension does not export a valid factory function: ${extensionPath}`);\n\t\t\t}\n\t\t\tconst api = createExtensionAPI(extension, runtime, cwd, eventBus);\n\t\t\tconst runtimeSnapshot = snapshotExtensionLoadRuntime(runtime);\n\t\t\ttry {\n\t\t\t\tawait runExtensionFactory(factory, api);\n\t\t\t} catch (error) {\n\t\t\t\tawait disposeExtensionEventSubscriptions([extension], { deactivate: false });\n\t\t\t\trestoreExtensionLoadRuntime(runtime, runtimeSnapshot);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (extension.lazy) {\n\t\t\t\textension.lazy.loaded = true;\n\t\t\t\textension.lazy.loading = undefined;\n\t\t\t}\n\t\t})();\n\n\t\tif (extension.lazy) extension.lazy.loading = loading;\n\t\ttry {\n\t\t\tawait loading;\n\t\t} catch (err) {\n\t\t\tawait disposeExtensionEventSubscriptions([extension], {\n\t\t\t\tdeactivate: err instanceof ExtensionFactoryTimeoutError,\n\t\t\t});\n\t\t\textension.handlers.clear();\n\t\t\textension.messageRenderers.clear();\n\t\t\textension.commands.clear();\n\t\t\textension.flags.clear();\n\t\t\textension.shortcuts.clear();\n\t\t\textension.tools.clear();\n\t\t\trestoreLazyToolPlaceholders();\n\t\t\tif (extension.lazy) extension.lazy.loading = undefined;\n\t\t\tthrow err;\n\t\t}\n\t};\n\n\textension.lazy = { loaded: false, load };\n\trestoreLazyToolPlaceholders = () => {\n\t\tfor (const tool of lazyTools) {\n\t\t\tconst name = String(tool.name).trim();\n\t\t\textension.tools.set(name, {\n\t\t\t\tdefinition: createLazyToolDefinition(extension, tool, load),\n\t\t\t\tsourceInfo: extension.sourceInfo,\n\t\t\t});\n\t\t}\n\t};\n\trestoreLazyToolPlaceholders();\n\treturn extension;\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\toptions: { factoryTimeoutMs?: number } = {},\n): Promise<Extension> {\n\tconst extension = createExtension(extensionPath, extensionPath);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst api = createExtensionAPI(extension, runtime, resolvedCwd, eventBus);\n\tconst runtimeSnapshot = snapshotExtensionLoadRuntime(runtime);\n\ttry {\n\t\tawait runExtensionFactory(factory, api, options.factoryTimeoutMs);\n\t} catch (err) {\n\t\tawait disposeExtensionEventSubscriptions([extension]);\n\t\trestoreExtensionLoadRuntime(runtime, runtimeSnapshot);\n\t\tthrow err;\n\t}\n\treturn extension;\n}\n\n/**\n * Load extensions from paths.\n */\nexport async function loadExtensions(\n\tpaths: Array<string | ExtensionLoadSpec>,\n\tcwd: string,\n\teventBus?: EventBus,\n): Promise<LoadExtensionsResult> {\n\tconst extensions: Extension[] = [];\n\tconst errors: Array<{ path: string; error: string }> = [];\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst resolvedEventBus = eventBus ?? createEventBus();\n\tconst runtime = createExtensionRuntime();\n\n\tfor (const spec of paths) {\n\t\tconst normalized = typeof spec === \"string\" ? { path: spec } : spec;\n\t\tconst extPath = normalized.path;\n\t\tconst resolvedPath = resolvePath(extPath, resolvedCwd, { normalizeUnicodeSpaces: true });\n\t\tconst lazyTools = (normalized.lazyTools ?? inferLazyToolsForExtensionPath(resolvedPath))?.filter(\n\t\t\t(tool) => typeof tool.name === \"string\" && tool.name.trim(),\n\t\t);\n\t\t// Extension imports can be CPU-heavy under jiti. Yield around each load so\n\t\t// interactive reloads can repaint/status-update instead of freezing the TUI\n\t\t// for the whole extension set. Lazy tool manifests skip import entirely here.\n\t\tawait yieldToEventLoop();\n\t\tif (lazyTools?.length) {\n\t\t\textensions.push(createLazyExtension(extPath, resolvedPath, resolvedCwd, resolvedEventBus, runtime, lazyTools));\n\t\t\tawait yieldToEventLoop();\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { extension, error } = await loadExtension(extPath, resolvedCwd, resolvedEventBus, runtime);\n\t\tawait yieldToEventLoop();\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,\n\t};\n}\n\ninterface LazyToolManifest {\n\tname?: unknown;\n\tlabel?: unknown;\n\tdescription?: unknown;\n\tparameters?: unknown;\n\tpromptSnippet?: unknown;\n\tpromptGuidelines?: unknown;\n\ttoolGroup?: unknown;\n\texecutionMode?: unknown;\n\t/** Optional extension entry this tool belongs to when a package declares multiple entries. */\n\textension?: unknown;\n}\n\ninterface PiManifest {\n\textensions?: string[];\n\tthemes?: string[];\n\tskills?: string[];\n\tprompts?: string[];\n\t/** Tool metadata for opt-in lazy extension loading. Factories stay unloaded until one of these tools runs. */\n\tlazyTools?: LazyToolManifest[];\n\t/** Alternate nested spelling: { tools: [...] }. */\n\tlazy?: { tools?: LazyToolManifest[] } | boolean;\n}\n\ninterface ExtensionLoadSpec {\n\tpath: string;\n\tlazyTools?: LazyToolManifest[];\n}\n\nfunction getManifestLazyTools(manifest: PiManifest | null): LazyToolManifest[] | undefined {\n\tif (!manifest) return undefined;\n\tconst tools = Array.isArray(manifest.lazyTools)\n\t\t? manifest.lazyTools\n\t\t: typeof manifest.lazy === \"object\" && Array.isArray(manifest.lazy.tools)\n\t\t\t? manifest.lazy.tools\n\t\t\t: undefined;\n\treturn tools?.filter((tool) => tool && typeof tool.name === \"string\" && tool.name.trim());\n}\n\nfunction defaultLazyToolParameters(): ToolDefinition[\"parameters\"] {\n\treturn { type: \"object\", properties: {}, additionalProperties: false } as ToolDefinition[\"parameters\"];\n}\n\nfunction lazyToolsForEntry(tools: LazyToolManifest[] | undefined, dir: string, entryPath: string, entryCount: number) {\n\tif (!tools?.length) return undefined;\n\tconst selected = tools.filter((tool) => {\n\t\tif (typeof tool.extension !== \"string\" || !tool.extension.trim()) {\n\t\t\treturn entryCount === 1;\n\t\t}\n\t\treturn path.resolve(dir, tool.extension) === entryPath;\n\t});\n\treturn selected.length > 0 ? selected : undefined;\n}\n\nfunction inferLazyToolsForExtensionPath(resolvedPath: string): LazyToolManifest[] | undefined {\n\tconst dir = path.dirname(resolvedPath);\n\tconst manifest = readPiManifest(path.join(dir, \"package.json\"));\n\tconst lazyTools = getManifestLazyTools(manifest);\n\tif (!manifest?.extensions?.length || !lazyTools?.length) return undefined;\n\tconst resolvedEntries = manifest.extensions.map((extPath) => path.resolve(dir, extPath));\n\tif (!resolvedEntries.includes(resolvedPath)) return undefined;\n\treturn lazyToolsForEntry(lazyTools, dir, resolvedPath, resolvedEntries.length);\n}\n\nfunction readPiManifest(packageJsonPath: string): PiManifest | null {\n\ttry {\n\t\tconst content = fs.readFileSync(packageJsonPath, \"utf-8\");\n\t\tconst pkg = JSON.parse(content);\n\t\tif (pkg.pi && typeof pkg.pi === \"object\") {\n\t\t\treturn pkg.pi as PiManifest;\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\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): ExtensionLoadSpec[] | 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 resolvedEntries = manifest.extensions\n\t\t\t\t.map((extPath) => path.resolve(dir, extPath))\n\t\t\t\t.filter((resolvedExtPath) => fs.existsSync(resolvedExtPath));\n\t\t\tif (resolvedEntries.length > 0) {\n\t\t\t\tconst lazyTools = getManifestLazyTools(manifest);\n\t\t\t\treturn resolvedEntries.map((entryPath) => ({\n\t\t\t\t\tpath: entryPath,\n\t\t\t\t\tlazyTools: lazyToolsForEntry(lazyTools, dir, entryPath, resolvedEntries.length),\n\t\t\t\t}));\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 [{ path: indexTs }];\n\t}\n\tif (fs.existsSync(indexJs)) {\n\t\treturn [{ path: 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): ExtensionLoadSpec[] {\n\tif (!fs.existsSync(dir)) {\n\t\treturn [];\n\t}\n\n\tconst discovered: ExtensionLoadSpec[] = [];\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({ path: 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: ExtensionLoadSpec[] = [];\n\tconst seen = new Set<string>();\n\n\tconst addPaths = (paths: Array<string | ExtensionLoadSpec>) => {\n\t\tfor (const spec of paths) {\n\t\t\tconst normalized = typeof spec === \"string\" ? { path: spec } : spec;\n\t\t\tconst resolved = path.resolve(normalized.path);\n\t\t\tif (!seen.has(resolved)) {\n\t\t\t\tseen.add(resolved);\n\t\t\t\tallPaths.push(normalized);\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"]}
|
|
@@ -418,17 +418,13 @@ async function loadExtensionModule(extensionPath, opts) {
|
|
|
418
418
|
// In Node.js/dev: use aliases to resolve to node_modules paths
|
|
419
419
|
...(isBunBinary ? { virtualModules: VIRTUAL_MODULES, tryNative: false } : { alias: getAliases() }),
|
|
420
420
|
});
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
if (opts?.fresh) {
|
|
424
|
-
const separator = extensionPath.includes("?") ? "&" : "?";
|
|
425
|
-
resolvedPath = `${extensionPath}${separator}fresh=${Date.now()}`;
|
|
426
|
-
}
|
|
421
|
+
// Every call gets a fresh jiti instance with moduleCache disabled. Do not append a query string:
|
|
422
|
+
// jiti's Windows path resolver treats it as part of the .ts filename.
|
|
427
423
|
const timeoutMs = opts?.moduleTimeoutMs ?? EXTENSION_FACTORY_TIMEOUT_MS;
|
|
428
424
|
let timeout;
|
|
429
425
|
try {
|
|
430
426
|
const module = await Promise.race([
|
|
431
|
-
jiti.import(
|
|
427
|
+
jiti.import(extensionPath, { default: true }),
|
|
432
428
|
new Promise((_resolve, reject) => {
|
|
433
429
|
timeout = setTimeout(() => reject(new Error(`Extension module import timed out after ${timeoutMs}ms: ${extensionPath}`)), Math.max(0, timeoutMs));
|
|
434
430
|
}),
|