@agimon-ai/doompi-core 0.0.1-alpha.89 → 0.0.1-alpha.90

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.
@@ -1,9 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
3
+ const require_index = require("./src/services/piExtensionHost/index.cjs");
3
4
  const require_runtimePiSettings = require("./runtimePiSettings.cjs");
4
- const require_index = require("./src/services/providerAuth/index.cjs");
5
+ const require_index$1 = require("./src/services/providerAuth/index.cjs");
5
6
  let node_path = require("node:path");
6
7
  node_path = require_runtime.__toESM(node_path, 1);
8
+ let node_os = require("node:os");
9
+ node_os = require_runtime.__toESM(node_os, 1);
7
10
  let _earendil_works_pi_coding_agent = require("@earendil-works/pi-coding-agent");
8
11
  //#region src/server/machineApi.ts
9
12
  function record(value) {
@@ -14,14 +17,30 @@ const machineApi = {
14
17
  basePath: "doompi",
15
18
  start(context) {
16
19
  if (context.scope !== "global") throw new Error("Provider authentication requires the global mount.");
17
- const auth = require_index.createProviderAuth({
20
+ const auth = require_index$1.createProviderAuth({
18
21
  onNotice: (message) => context.onNotice(message),
19
22
  runtime: async () => {
20
23
  const directory = require_runtimePiSettings.piAgentDirectory(context.environment, context.homeDirectory);
21
- return _earendil_works_pi_coding_agent.ModelRuntime.create({
24
+ const runtime = await _earendil_works_pi_coding_agent.ModelRuntime.create({
22
25
  authPath: node_path.default.join(directory, "auth.json"),
23
26
  modelsPath: node_path.default.join(directory, "models.json")
24
27
  });
28
+ const onNotice = (message) => context.onNotice(message);
29
+ const cwd = context.homeDirectory ?? node_os.default.homedir();
30
+ const settings = _earendil_works_pi_coding_agent.SettingsManager.create(cwd, directory);
31
+ await require_index.preloadPiExtensions({
32
+ cwd,
33
+ agentDir: directory,
34
+ models: runtime,
35
+ extensionPaths: await require_index.resolvePiSettingsPackageEntries({
36
+ cwd,
37
+ agentDir: directory,
38
+ settings,
39
+ onNotice
40
+ }),
41
+ onNotice
42
+ });
43
+ return runtime;
25
44
  }
26
45
  });
27
46
  let closed = false;
@@ -1 +1 @@
1
- {"version":3,"file":"machineApi.cjs","names":["createProviderAuth","piAgentDirectory","ModelRuntime","path"],"sources":["../src/server/machineApi.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { ModelRuntime } from '@earendil-works/pi-coding-agent';\n\nimport type { DoomApi } from '../exports/packageApi';\nimport { piAgentDirectory } from '../services/piSettings';\nimport { createProviderAuth } from '../services/providerAuth';\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n/** Machine-owned provider credentials and model catalog, available without a session. */\nexport const machineApi: DoomApi = {\n basePath: 'doompi',\n start(context) {\n if (context.scope !== 'global') throw new Error('Provider authentication requires the global mount.');\n const auth = createProviderAuth({\n onNotice: (message) => context.onNotice(message),\n runtime: async () => {\n const directory = piAgentDirectory(context.environment, context.homeDirectory);\n return ModelRuntime.create({\n authPath: path.join(directory, 'auth.json'),\n modelsPath: path.join(directory, 'models.json'),\n });\n },\n });\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return Response.json({ error: 'Provider mount is closed.' }, { status: 503 });\n const url = new URL(request.url);\n try {\n if (url.pathname === '/providers' && request.method === 'GET')\n return Response.json({ providers: await auth.listProviders() });\n if (url.pathname === '/models' && request.method === 'GET')\n return Response.json({ models: await auth.listModels() });\n const provider = /^\\/providers\\/([^/]+)$/u.exec(url.pathname);\n if (provider && request.method === 'DELETE') {\n const result = await auth.logout(decodeURIComponent(provider[1]));\n return result.ok\n ? Response.json({ ok: true })\n : Response.json({ error: result.error }, { status: result.code === 'unknown_provider' ? 404 : 502 });\n }\n if (url.pathname === '/logins' && request.method === 'POST') {\n const body: unknown = await request.json();\n if (\n !record(body) ||\n typeof body.providerId !== 'string' ||\n (body.type !== 'oauth' && body.type !== 'api_key')\n )\n return Response.json({ error: 'A provider and supported login method are required.' }, { status: 400 });\n const result = await auth.startLogin(body.providerId, body.type);\n return result.ok\n ? Response.json({ flow: result.flow }, { status: 201 })\n : Response.json(\n { error: result.error },\n { status: result.code === 'unknown_provider' ? 404 : result.code === 'busy' ? 409 : 400 },\n );\n }\n const flow = /^\\/logins\\/([^/]+)(\\/answer)?$/u.exec(url.pathname);\n if (flow) {\n const id = decodeURIComponent(flow[1]);\n if (!auth.getLogin(id)) return Response.json({ error: 'Login flow not found.' }, { status: 404 });\n if (flow[2] && request.method === 'POST') {\n const body: unknown = await request.json();\n if (!record(body) || typeof body.promptId !== 'string' || typeof body.value !== 'string')\n return Response.json({ error: 'A prompt ID and answer are required.' }, { status: 400 });\n const answer = auth.answerLogin(id, body.promptId, body.value);\n return answer === 'answered'\n ? Response.json({ flow: auth.getLogin(id) })\n : Response.json({ error: 'Login is no longer waiting for this answer.' }, { status: 409 });\n }\n if (!flow[2] && request.method === 'GET') return Response.json({ flow: auth.getLogin(id) });\n if (!flow[2] && request.method === 'DELETE') return Response.json({ flow: auth.cancelLogin(id) });\n }\n return Response.json({ error: 'Provider route not found.' }, { status: 404 });\n } catch (error) {\n return Response.json(\n { error: error instanceof Error ? error.message : String(error) },\n { status: error instanceof SyntaxError ? 400 : 502 },\n );\n }\n },\n close() {\n closed = true;\n auth.close();\n },\n };\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"machineApi.cjs","names":["createProviderAuth","piAgentDirectory","ModelRuntime","path","os","SettingsManager","preloadPiExtensions","resolvePiSettingsPackageEntries"],"sources":["../src/server/machineApi.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\n\nimport { ModelRuntime, SettingsManager } from '@earendil-works/pi-coding-agent';\n\nimport type { DoomApi } from '../exports/packageApi';\nimport { preloadPiExtensions, resolvePiSettingsPackageEntries } from '../services/piExtensionHost';\nimport { piAgentDirectory } from '../services/piSettings';\nimport { createProviderAuth } from '../services/providerAuth';\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n/** Machine-owned provider credentials and model catalog, available without a session. */\nexport const machineApi: DoomApi = {\n basePath: 'doompi',\n start(context) {\n if (context.scope !== 'global') throw new Error('Provider authentication requires the global mount.');\n const auth = createProviderAuth({\n onNotice: (message) => context.onNotice(message),\n runtime: async () => {\n const directory = piAgentDirectory(context.environment, context.homeDirectory);\n const runtime = await ModelRuntime.create({\n authPath: path.join(directory, 'auth.json'),\n modelsPath: path.join(directory, 'models.json'),\n });\n // Providers registered by Pi settings packages (a Claude Code bridge, say) are\n // listed and pickable here as they are in sessions, which load the same packages.\n const onNotice = (message: string): void => context.onNotice(message);\n // Machine scope has no project, so the home directory stands in for Pi's cwd.\n const cwd = context.homeDirectory ?? os.homedir();\n const settings = SettingsManager.create(cwd, directory);\n await preloadPiExtensions({\n cwd,\n agentDir: directory,\n models: runtime,\n extensionPaths: await resolvePiSettingsPackageEntries({\n cwd,\n agentDir: directory,\n settings,\n onNotice,\n }),\n onNotice,\n });\n return runtime;\n },\n });\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return Response.json({ error: 'Provider mount is closed.' }, { status: 503 });\n const url = new URL(request.url);\n try {\n if (url.pathname === '/providers' && request.method === 'GET')\n return Response.json({ providers: await auth.listProviders() });\n if (url.pathname === '/models' && request.method === 'GET')\n return Response.json({ models: await auth.listModels() });\n const provider = /^\\/providers\\/([^/]+)$/u.exec(url.pathname);\n if (provider && request.method === 'DELETE') {\n const result = await auth.logout(decodeURIComponent(provider[1]));\n return result.ok\n ? Response.json({ ok: true })\n : Response.json({ error: result.error }, { status: result.code === 'unknown_provider' ? 404 : 502 });\n }\n if (url.pathname === '/logins' && request.method === 'POST') {\n const body: unknown = await request.json();\n if (\n !record(body) ||\n typeof body.providerId !== 'string' ||\n (body.type !== 'oauth' && body.type !== 'api_key')\n )\n return Response.json({ error: 'A provider and supported login method are required.' }, { status: 400 });\n const result = await auth.startLogin(body.providerId, body.type);\n return result.ok\n ? Response.json({ flow: result.flow }, { status: 201 })\n : Response.json(\n { error: result.error },\n { status: result.code === 'unknown_provider' ? 404 : result.code === 'busy' ? 409 : 400 },\n );\n }\n const flow = /^\\/logins\\/([^/]+)(\\/answer)?$/u.exec(url.pathname);\n if (flow) {\n const id = decodeURIComponent(flow[1]);\n if (!auth.getLogin(id)) return Response.json({ error: 'Login flow not found.' }, { status: 404 });\n if (flow[2] && request.method === 'POST') {\n const body: unknown = await request.json();\n if (!record(body) || typeof body.promptId !== 'string' || typeof body.value !== 'string')\n return Response.json({ error: 'A prompt ID and answer are required.' }, { status: 400 });\n const answer = auth.answerLogin(id, body.promptId, body.value);\n return answer === 'answered'\n ? Response.json({ flow: auth.getLogin(id) })\n : Response.json({ error: 'Login is no longer waiting for this answer.' }, { status: 409 });\n }\n if (!flow[2] && request.method === 'GET') return Response.json({ flow: auth.getLogin(id) });\n if (!flow[2] && request.method === 'DELETE') return Response.json({ flow: auth.cancelLogin(id) });\n }\n return Response.json({ error: 'Provider route not found.' }, { status: 404 });\n } catch (error) {\n return Response.json(\n { error: error instanceof Error ? error.message : String(error) },\n { status: error instanceof SyntaxError ? 400 : 502 },\n );\n }\n },\n close() {\n closed = true;\n auth.close();\n },\n };\n },\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"machineApi.d.mts","names":[],"sources":["../src/server/machineApi.ts"],"mappings":";;;;qBAYa,YAAY"}
1
+ {"version":3,"file":"machineApi.d.mts","names":[],"sources":["../src/server/machineApi.ts"],"mappings":";;;;qBAca,YAAY"}
@@ -1,7 +1,9 @@
1
+ import { preloadPiExtensions, resolvePiSettingsPackageEntries } from "./src/services/piExtensionHost/index.mjs";
1
2
  import { piAgentDirectory } from "./runtimePiSettings.mjs";
2
3
  import { createProviderAuth } from "./src/services/providerAuth/index.mjs";
3
4
  import path from "node:path";
4
- import { ModelRuntime } from "@earendil-works/pi-coding-agent";
5
+ import os from "node:os";
6
+ import { ModelRuntime, SettingsManager } from "@earendil-works/pi-coding-agent";
5
7
  //#region src/server/machineApi.ts
6
8
  function record(value) {
7
9
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -15,10 +17,26 @@ const machineApi = {
15
17
  onNotice: (message) => context.onNotice(message),
16
18
  runtime: async () => {
17
19
  const directory = piAgentDirectory(context.environment, context.homeDirectory);
18
- return ModelRuntime.create({
20
+ const runtime = await ModelRuntime.create({
19
21
  authPath: path.join(directory, "auth.json"),
20
22
  modelsPath: path.join(directory, "models.json")
21
23
  });
24
+ const onNotice = (message) => context.onNotice(message);
25
+ const cwd = context.homeDirectory ?? os.homedir();
26
+ const settings = SettingsManager.create(cwd, directory);
27
+ await preloadPiExtensions({
28
+ cwd,
29
+ agentDir: directory,
30
+ models: runtime,
31
+ extensionPaths: await resolvePiSettingsPackageEntries({
32
+ cwd,
33
+ agentDir: directory,
34
+ settings,
35
+ onNotice
36
+ }),
37
+ onNotice
38
+ });
39
+ return runtime;
22
40
  }
23
41
  });
24
42
  let closed = false;
@@ -1 +1 @@
1
- {"version":3,"file":"machineApi.mjs","names":[],"sources":["../src/server/machineApi.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { ModelRuntime } from '@earendil-works/pi-coding-agent';\n\nimport type { DoomApi } from '../exports/packageApi';\nimport { piAgentDirectory } from '../services/piSettings';\nimport { createProviderAuth } from '../services/providerAuth';\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n/** Machine-owned provider credentials and model catalog, available without a session. */\nexport const machineApi: DoomApi = {\n basePath: 'doompi',\n start(context) {\n if (context.scope !== 'global') throw new Error('Provider authentication requires the global mount.');\n const auth = createProviderAuth({\n onNotice: (message) => context.onNotice(message),\n runtime: async () => {\n const directory = piAgentDirectory(context.environment, context.homeDirectory);\n return ModelRuntime.create({\n authPath: path.join(directory, 'auth.json'),\n modelsPath: path.join(directory, 'models.json'),\n });\n },\n });\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return Response.json({ error: 'Provider mount is closed.' }, { status: 503 });\n const url = new URL(request.url);\n try {\n if (url.pathname === '/providers' && request.method === 'GET')\n return Response.json({ providers: await auth.listProviders() });\n if (url.pathname === '/models' && request.method === 'GET')\n return Response.json({ models: await auth.listModels() });\n const provider = /^\\/providers\\/([^/]+)$/u.exec(url.pathname);\n if (provider && request.method === 'DELETE') {\n const result = await auth.logout(decodeURIComponent(provider[1]));\n return result.ok\n ? Response.json({ ok: true })\n : Response.json({ error: result.error }, { status: result.code === 'unknown_provider' ? 404 : 502 });\n }\n if (url.pathname === '/logins' && request.method === 'POST') {\n const body: unknown = await request.json();\n if (\n !record(body) ||\n typeof body.providerId !== 'string' ||\n (body.type !== 'oauth' && body.type !== 'api_key')\n )\n return Response.json({ error: 'A provider and supported login method are required.' }, { status: 400 });\n const result = await auth.startLogin(body.providerId, body.type);\n return result.ok\n ? Response.json({ flow: result.flow }, { status: 201 })\n : Response.json(\n { error: result.error },\n { status: result.code === 'unknown_provider' ? 404 : result.code === 'busy' ? 409 : 400 },\n );\n }\n const flow = /^\\/logins\\/([^/]+)(\\/answer)?$/u.exec(url.pathname);\n if (flow) {\n const id = decodeURIComponent(flow[1]);\n if (!auth.getLogin(id)) return Response.json({ error: 'Login flow not found.' }, { status: 404 });\n if (flow[2] && request.method === 'POST') {\n const body: unknown = await request.json();\n if (!record(body) || typeof body.promptId !== 'string' || typeof body.value !== 'string')\n return Response.json({ error: 'A prompt ID and answer are required.' }, { status: 400 });\n const answer = auth.answerLogin(id, body.promptId, body.value);\n return answer === 'answered'\n ? Response.json({ flow: auth.getLogin(id) })\n : Response.json({ error: 'Login is no longer waiting for this answer.' }, { status: 409 });\n }\n if (!flow[2] && request.method === 'GET') return Response.json({ flow: auth.getLogin(id) });\n if (!flow[2] && request.method === 'DELETE') return Response.json({ flow: auth.cancelLogin(id) });\n }\n return Response.json({ error: 'Provider route not found.' }, { status: 404 });\n } catch (error) {\n return Response.json(\n { error: error instanceof Error ? error.message : String(error) },\n { status: error instanceof SyntaxError ? 400 : 502 },\n );\n }\n },\n close() {\n closed = true;\n auth.close();\n },\n };\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"machineApi.mjs","names":[],"sources":["../src/server/machineApi.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\n\nimport { ModelRuntime, SettingsManager } from '@earendil-works/pi-coding-agent';\n\nimport type { DoomApi } from '../exports/packageApi';\nimport { preloadPiExtensions, resolvePiSettingsPackageEntries } from '../services/piExtensionHost';\nimport { piAgentDirectory } from '../services/piSettings';\nimport { createProviderAuth } from '../services/providerAuth';\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n/** Machine-owned provider credentials and model catalog, available without a session. */\nexport const machineApi: DoomApi = {\n basePath: 'doompi',\n start(context) {\n if (context.scope !== 'global') throw new Error('Provider authentication requires the global mount.');\n const auth = createProviderAuth({\n onNotice: (message) => context.onNotice(message),\n runtime: async () => {\n const directory = piAgentDirectory(context.environment, context.homeDirectory);\n const runtime = await ModelRuntime.create({\n authPath: path.join(directory, 'auth.json'),\n modelsPath: path.join(directory, 'models.json'),\n });\n // Providers registered by Pi settings packages (a Claude Code bridge, say) are\n // listed and pickable here as they are in sessions, which load the same packages.\n const onNotice = (message: string): void => context.onNotice(message);\n // Machine scope has no project, so the home directory stands in for Pi's cwd.\n const cwd = context.homeDirectory ?? os.homedir();\n const settings = SettingsManager.create(cwd, directory);\n await preloadPiExtensions({\n cwd,\n agentDir: directory,\n models: runtime,\n extensionPaths: await resolvePiSettingsPackageEntries({\n cwd,\n agentDir: directory,\n settings,\n onNotice,\n }),\n onNotice,\n });\n return runtime;\n },\n });\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return Response.json({ error: 'Provider mount is closed.' }, { status: 503 });\n const url = new URL(request.url);\n try {\n if (url.pathname === '/providers' && request.method === 'GET')\n return Response.json({ providers: await auth.listProviders() });\n if (url.pathname === '/models' && request.method === 'GET')\n return Response.json({ models: await auth.listModels() });\n const provider = /^\\/providers\\/([^/]+)$/u.exec(url.pathname);\n if (provider && request.method === 'DELETE') {\n const result = await auth.logout(decodeURIComponent(provider[1]));\n return result.ok\n ? Response.json({ ok: true })\n : Response.json({ error: result.error }, { status: result.code === 'unknown_provider' ? 404 : 502 });\n }\n if (url.pathname === '/logins' && request.method === 'POST') {\n const body: unknown = await request.json();\n if (\n !record(body) ||\n typeof body.providerId !== 'string' ||\n (body.type !== 'oauth' && body.type !== 'api_key')\n )\n return Response.json({ error: 'A provider and supported login method are required.' }, { status: 400 });\n const result = await auth.startLogin(body.providerId, body.type);\n return result.ok\n ? Response.json({ flow: result.flow }, { status: 201 })\n : Response.json(\n { error: result.error },\n { status: result.code === 'unknown_provider' ? 404 : result.code === 'busy' ? 409 : 400 },\n );\n }\n const flow = /^\\/logins\\/([^/]+)(\\/answer)?$/u.exec(url.pathname);\n if (flow) {\n const id = decodeURIComponent(flow[1]);\n if (!auth.getLogin(id)) return Response.json({ error: 'Login flow not found.' }, { status: 404 });\n if (flow[2] && request.method === 'POST') {\n const body: unknown = await request.json();\n if (!record(body) || typeof body.promptId !== 'string' || typeof body.value !== 'string')\n return Response.json({ error: 'A prompt ID and answer are required.' }, { status: 400 });\n const answer = auth.answerLogin(id, body.promptId, body.value);\n return answer === 'answered'\n ? Response.json({ flow: auth.getLogin(id) })\n : Response.json({ error: 'Login is no longer waiting for this answer.' }, { status: 409 });\n }\n if (!flow[2] && request.method === 'GET') return Response.json({ flow: auth.getLogin(id) });\n if (!flow[2] && request.method === 'DELETE') return Response.json({ flow: auth.cancelLogin(id) });\n }\n return Response.json({ error: 'Provider route not found.' }, { status: 404 });\n } catch (error) {\n return Response.json(\n { error: error instanceof Error ? error.message : String(error) },\n { status: error instanceof SyntaxError ? 400 : 502 },\n );\n }\n },\n close() {\n closed = true;\n auth.close();\n },\n };\n },\n};\n"],"mappings":"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"}
@@ -22,6 +22,31 @@ function resolvePiExtensionEntries(repoRoot) {
22
22
  }
23
23
  }
24
24
  /**
25
+ * Extension entries of the packages listed under `packages` in Pi's settings.json.
26
+ *
27
+ * Interactive Pi expands that list through its resource loader, which this server does not run,
28
+ * so a provider package such as a local Claude Code bridge would load under `pi` and be missing
29
+ * here. Only `packages` entries are taken: Pi's top-level `extensions` list can name DoomPi
30
+ * itself, and the auto-discovered `extensions/` folders are already loaded by
31
+ * `discoverAndLoadExtensions`. A package that is not installed is skipped, never installed, so
32
+ * starting a session cannot trigger a network install.
33
+ */
34
+ async function resolvePiSettingsPackageEntries(options) {
35
+ try {
36
+ return (await new _earendil_works_pi_coding_agent.DefaultPackageManager({
37
+ cwd: options.cwd,
38
+ agentDir: options.agentDir,
39
+ settingsManager: options.settings
40
+ }).resolve(async (source) => {
41
+ options.onNotice?.(`Pi package ${source} is not installed; skipped. Install it with pi to load it here.`);
42
+ return "skip";
43
+ })).extensions.filter((resource) => resource.enabled && resource.metadata.origin === "package").map((resource) => resource.path);
44
+ } catch (error) {
45
+ options.onNotice?.(`Pi settings packages: ${error instanceof Error ? error.message : String(error)}`);
46
+ return [];
47
+ }
48
+ }
49
+ /**
25
50
  * Load Pi extensions and apply their provider registrations to the model runtime.
26
51
  *
27
52
  * `pi.registerProvider` only queues into the extension runtime while extensions load; Pi drains
@@ -764,5 +789,6 @@ exports.createBridgedSessionManager = createBridgedSessionManager;
764
789
  exports.createPiExtensionHost = createPiExtensionHost;
765
790
  exports.preloadPiExtensions = preloadPiExtensions;
766
791
  exports.resolvePiExtensionEntries = resolvePiExtensionEntries;
792
+ exports.resolvePiSettingsPackageEntries = resolvePiSettingsPackageEntries;
767
793
 
768
794
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["readSyncRegistration","createEventBus","discoverAndLoadExtensions","ModelRegistry","sessionEntryToContextMessages","toPiSessionEntries","loadSkills","readFileSync","SessionManager","toPiFileEntries","fromPiSessionEntry","isDeepStrictEqual","toPiSessionEntry","contextUsageOf","contextTokensOf","latestAssistantUsage","ExtensionRunner","connectDoomCordisHost"],"sources":["../../../../src/services/piExtensionHost/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { isDeepStrictEqual } from 'node:util';\n\nimport type {\n AgentHarnessTool,\n AgentMessage,\n CompactionPreparation as HarnessCompactionPreparation,\n Entry,\n HarnessEvent,\n HookMap,\n JsonValue,\n Skill,\n ThinkingLevel,\n} from '@earendil-works/pi-agent-core';\nimport type { Api, Message, Model } from '@earendil-works/pi-ai';\nimport {\n createEventBus,\n discoverAndLoadExtensions,\n ExtensionRunner,\n ModelRegistry,\n initTheme,\n loadSkills,\n SessionManager,\n sessionEntryToContextMessages,\n type CompactionEntry,\n type BoundaryContextPreview,\n type CompactionResult,\n type SessionBeforeCompactEvent,\n type Extension,\n type ExtensionActions,\n type ExtensionContextActions,\n type ExtensionUIContext,\n type LoadExtensionsResult,\n type ModelRuntime,\n type RegisteredTool,\n type SessionEntry,\n type ToolInfo,\n} from '@earendil-works/pi-coding-agent';\n\nimport type { DoomHeadlessClient, DoomHeadlessClientRequest, DoomHeadlessToolResult } from '../../exports/headless';\nimport { connectDoomCordisHost, type DoomCordisHostConnection } from '../../pi/cordisHost';\nimport { contextTokensOf, contextUsageOf, latestAssistantUsage } from '../../services/contextUsage';\nimport {\n fromPiSessionEntry,\n toPiFileEntries,\n toPiSessionEntries,\n toPiSessionEntry,\n type PiSessionHeaderInput,\n} from '../../services/piSessionEntries';\nimport { readSyncRegistration } from '../../services/syncRegistration';\nimport type { ToolPromptEntry } from '../../services/toolPrompt';\nimport type { DirectHarnessRuntime } from '../../types/server/directHarnessRuntime';\n\n/**\n * Pi entries for this worktree, taken from the validated sync registration.\n *\n * This is the same admission gate Pi's own managed dispatcher applies: the registration is\n * checked for protocol version, worktree identity, state hash, and manifest agreement before\n * any entry is returned. An unsynced or mismatched worktree contributes nothing.\n */\nexport function resolvePiExtensionEntries(repoRoot: string): readonly string[] {\n try {\n const registration = readSyncRegistration(repoRoot);\n return registration ? [registration.package.entry] : [];\n } catch {\n // A stale or mismatched registration is a normal unsynced state, not a session failure.\n return [];\n }\n}\n\n/**\n * Load Pi extensions and apply their provider registrations to the model runtime.\n *\n * `pi.registerProvider` only queues into the extension runtime while extensions load; Pi drains\n * that queue in `ExtensionRunner.bindCore`. The runner cannot exist before the harness runtime,\n * and the harness runtime is built from an already-resolved model, so waiting for `bindCore`\n * would resolve the session's default model before extension providers exist and silently fall\n * back to the first available model. Draining the queue here puts provider registration ahead of\n * model resolution.\n *\n * The result is returned so `createPiExtensionHost` reuses it instead of discovering again, which\n * keeps every extension factory running exactly once.\n */\nexport async function preloadPiExtensions(options: {\n readonly cwd: string;\n readonly agentDir: string;\n readonly models: ModelRuntime;\n readonly extensionPaths: readonly string[];\n readonly onNotice?: (message: string) => void;\n}): Promise<(LoadExtensionsResult & { readonly events?: ReturnType<typeof createEventBus> }) | undefined> {\n if (options.extensionPaths.length === 0) return undefined;\n\n const events = createEventBus();\n const result = await discoverAndLoadExtensions([...options.extensionPaths], options.cwd, options.agentDir, events);\n for (const failure of result.errors) options.onNotice?.(`Pi extension ${failure.path}: ${failure.error}`);\n\n // The same target bindCore uses: without providerActions it registers through ModelRegistry,\n // which delegates to this ModelRuntime.\n const registry = new ModelRegistry(options.models);\n const report = (extensionPath: string, error: unknown): void =>\n options.onNotice?.(\n `Pi extension ${extensionPath}: provider registration failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n\n for (const pending of result.runtime.pendingProviderRegistrations) {\n try {\n registry.registerProvider(pending.name, pending.config);\n } catch (error) {\n report(pending.extensionPath, error);\n }\n }\n for (const pending of result.runtime.pendingNativeProviderRegistrations) {\n try {\n registry.registerProvider(pending.provider);\n } catch (error) {\n report(pending.extensionPath, error);\n }\n }\n // Cleared so bindCore does not repeat registrations already applied to the same runtime.\n result.runtime.pendingProviderRegistrations = [];\n result.runtime.pendingNativeProviderRegistrations = [];\n\n return Object.assign(result, { events });\n}\n/**\n * Pi native extensions are session lifetime. They contribute tools once and are never\n * retracted, so they are merged at the harness apply boundary rather than contributed to the\n * headless kernel, whose active layers come solely from server bundle candidates and would\n * drop any owner that is not a declared facet package.\n *\n * Their event handlers are a second surface. Pi drives them from an interactive\n * AgentSession this server does not run, so the handful the harness can speak to\n * faithfully are bridged here: the two agent-loop notifications an extension needs\n * to observe a turn, and the three hooks that let one shape or replace context.\n * Everything else stays undelivered rather than being approximated.\n */\nexport interface PiExtensionHostOptions {\n readonly cwd: string;\n readonly agentDir: string;\n readonly models: ModelRuntime;\n readonly runtime: DirectHarnessRuntime;\n /**\n * Extensions already loaded by `preloadPiExtensions`, or undefined when this worktree\n * contributes none. Reused rather than re-discovered so each factory runs exactly once.\n */\n readonly preload: (LoadExtensionsResult & { readonly events?: ReturnType<typeof createEventBus> }) | undefined;\n /** Current model, tracked by the caller because harness session metadata does not carry it. */\n readonly getModel: () => Model<Api> | undefined;\n readonly getThinkingLevel: () => ThinkingLevel;\n /**\n * Resolved at call time, because the host is constructed while the session is still assembling.\n * Backs Pi's extension dialogs, notifications and status text.\n */\n readonly client: () => DoomHeadlessClient | undefined;\n readonly onNotice?: (message: string) => void;\n /**\n * Called when a Pi extension tool restriction changes the active set.\n *\n * Doom extensions register their restrictions asynchronously, after the first tool apply has\n * already run, so the caller has to re-apply rather than read `tools` once at load.\n */\n readonly onActiveToolsChanged?: () => void;\n}\n\nexport interface PiExtensionHost {\n /** Session lifetime tools contributed by Pi extensions, in load order. */\n readonly tools: readonly AgentHarnessTool<object | undefined>[];\n /**\n * System-prompt guidance for those same tools, in the same order.\n *\n * `AgentHarnessTool` has no field for it, so it cannot ride on the harness\n * tool the way it does on a Pi extension definition, and the headless host\n * renders it into the prompt itself.\n */\n readonly toolGuidance: readonly ToolPromptEntry[];\n /** Session lifetime skills contributed by Pi extensions, in discovery order. */\n readonly skills: readonly Skill[];\n executeTool(\n name: string,\n toolCallId: string,\n parameters: Record<string, unknown>,\n signal?: AbortSignal,\n onUpdate?: (result: DoomHeadlessToolResult) => void,\n ): Promise<DoomHeadlessToolResult>;\n /**\n * Pi's `context` event, as one step in the host's transform chain.\n *\n * Returns the messages unchanged when no extension is loaded, so the caller can\n * chain it unconditionally.\n */\n transformContext(messages: AgentMessage[]): Promise<AgentMessage[]>;\n /** Pi's `session_before_compact` event, translated into the harness hook's answer. */\n beforeCompaction(event: HookMap['before_compaction']['event']): Promise<HookMap['before_compaction']['result']>;\n /** Reads a session service from the Pi Cordis root without exposing registration APIs. */\n getService<T>(name: string): T | undefined;\n load(): Promise<void>;\n shutdown(): Promise<void>;\n}\n/** Pi does not export the preparation type on its own, only through the event that carries it. */\ntype PiCompactionPreparation = SessionBeforeCompactEvent['preparation'];\n\n/**\n * The compaction boundary, named the way Pi names it.\n *\n * Pi identifies the cut by the first entry it keeps; the harness carries the kept\n * messages inline instead. Counting whole entries back from the tip until the kept\n * messages are covered recovers the id, and {@link retainedTailFrom} is its exact\n * inverse, so an extension that echoes the id it was given gets its own tail back.\n */\nfunction boundaryEntryId(branchEntries: readonly SessionEntry[], retainedCount: number): string | undefined {\n let remaining = retainedCount;\n for (let index = branchEntries.length - 1; index >= 0; index -= 1) {\n const entry = branchEntries[index];\n if (entry === undefined) continue;\n if (remaining <= 0) return entry.id;\n remaining -= sessionEntryToContextMessages(entry).length;\n if (remaining <= 0) return entry.id;\n }\n return branchEntries[0]?.id;\n}\n\n/** The messages an entry id keeps, which is what the harness stores on the compaction entry. */\nfunction retainedTailFrom(branchEntries: readonly SessionEntry[], firstKeptEntryId: string): AgentMessage[] {\n const index = branchEntries.findIndex((entry) => entry.id === firstKeptEntryId);\n if (index === -1) return [];\n return branchEntries.slice(index).flatMap((entry) => sessionEntryToContextMessages(entry));\n}\n\nfunction toPiCompactionPreparation(\n preparation: HarnessCompactionPreparation,\n branchEntries: readonly SessionEntry[],\n): PiCompactionPreparation {\n return {\n firstKeptEntryId: boundaryEntryId(branchEntries, preparation.retainedTail.length) ?? '',\n messagesToSummarize: preparation.messagesToSummarize,\n turnPrefixMessages: preparation.turnPrefixMessages,\n isSplitTurn: preparation.isSplitTurn,\n tokensBefore: preparation.tokensBefore,\n ...(preparation.previousSummary === undefined ? {} : { previousSummary: preparation.previousSummary }),\n fileOps: preparation.fileOps,\n settings: preparation.settings,\n };\n}\n\nfunction toHarnessCompactResult(\n result: CompactionResult,\n branchEntries: readonly SessionEntry[],\n): NonNullable<NonNullable<HookMap['before_compaction']['result']>['compaction']> {\n return {\n summary: result.summary,\n tokensBefore: result.tokensBefore,\n retainedTail: retainedTailFrom(branchEntries, result.firstKeptEntryId),\n ...(result.usage === undefined ? {} : { usage: result.usage }),\n ...(result.details === undefined ? {} : { details: result.details as JsonValue }),\n };\n}\n\nfunction piBranchEntries(entries: readonly Entry[]): SessionEntry[] {\n return toPiSessionEntries(entries);\n}\n\nasync function executePiTool(\n registered: RegisteredTool,\n runner: () => ExtensionRunner,\n isActive: (name: string) => boolean,\n toolCallId: string,\n parameters: unknown,\n signal: AbortSignal | undefined,\n onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n): Promise<DoomHeadlessToolResult> {\n const definition = registered.definition;\n // Admission, not visibility. A cached definition must still be active when called.\n if (!isActive(definition.name)) throw new Error(`Tool '${definition.name}' is no longer active`);\n const prepared = definition.prepareArguments ? definition.prepareArguments(parameters) : parameters;\n const result = await definition.execute(\n toolCallId,\n prepared,\n signal,\n (partial) => onUpdate?.({ content: partial.content, details: partial.details }),\n runner().createContext(),\n );\n return { content: result.content, details: result.details };\n}\n\nfunction toHarnessTool(\n registered: RegisteredTool,\n runner: () => ExtensionRunner,\n isActive: (name: string) => boolean,\n): AgentHarnessTool<object | undefined> {\n const definition = registered.definition;\n return {\n name: definition.name,\n label: definition.label ?? definition.name,\n description: definition.description,\n parameters: definition.parameters,\n ...(definition.executionMode === undefined ? {} : { executionMode: definition.executionMode }),\n async execute(toolCallId, parameters, onUpdate, _toolContext, _invocation, context) {\n const result = await executePiTool(\n registered,\n runner,\n isActive,\n toolCallId,\n parameters,\n context.abortSignal,\n (partial) => onUpdate({ content: partial.content, details: partial.details }),\n );\n return { content: result.content, details: result.details };\n },\n };\n}\n\n/**\n * Resolves the skills Pi extensions contribute.\n *\n * Pi wires this through AgentSession's resource loader, which the headless server does not use,\n * so the pipeline is rebuilt here against the harness. Pi's own loader parses and validates the\n * frontmatter; only the body has to be read, because Pi's Skill record carries a path where the\n * harness wants inline content.\n */\nasync function loadPiSkills(\n runner: ExtensionRunner,\n cwd: string,\n agentDir: string,\n onNotice: ((message: string) => void) | undefined,\n): Promise<readonly Skill[]> {\n const discovered = await runner.emitResourcesDiscover(cwd, 'startup');\n if (discovered.skillPaths.length === 0) return [];\n\n const loaded = loadSkills({\n cwd,\n agentDir,\n skillPaths: discovered.skillPaths.map((entry) => entry.path),\n includeDefaults: false,\n });\n for (const diagnostic of loaded.diagnostics) onNotice?.(`Pi skill ${diagnostic.path}: ${diagnostic.message}`);\n\n const resolved: Skill[] = [];\n for (const skill of loaded.skills) {\n try {\n resolved.push({\n name: skill.name,\n description: skill.description,\n content: readFileSync(skill.filePath, 'utf8'),\n filePath: skill.filePath,\n ...(skill.disableModelInvocation ? { disableModelInvocation: true } : {}),\n });\n } catch (error) {\n // One unreadable skill file must not cost the session every other skill.\n onNotice?.(`Pi skill ${skill.filePath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n return resolved;\n}\n/**\n * A SessionManager backed by the session the server already has open.\n *\n * Pi extensions read history through a SessionManager, which is JSONL backed, while the server\n * persists to SQLite through the harness. Rather than opening a second connection and taking a\n * second history lease, the manager is hydrated in memory from the live session and its writes\n * are mirrored back through the same harness runtime.\n *\n * `_persist` is public in Pi's source and in its shipped types, but it is not a documented\n * extension point, so the guard below turns a rename in a future Pi release into an immediate,\n * named failure instead of history that silently stops being recorded.\n */\n/**\n * Pi's default theme, reached through public API only.\n *\n * The singleton lives in Pi's interactive theme module, which the package `exports` map does not\n * expose, so a deep import fails with ERR_PACKAGE_PATH_NOT_EXPORTED. A freshly constructed runner\n * still carries Pi's own no-op UI context, whose `theme` getter returns that singleton, so reading\n * it back before binding is the supported route. The getter throws until `initTheme` has run.\n */\nfunction resolveDefaultTheme(runner: ExtensionRunner): ExtensionUIContext['theme'] {\n try {\n return runner.getUIContext().theme;\n } catch {\n // Not a failure: the theme singleton is lazily initialised and no TUI has done it here.\n initTheme(undefined, false);\n return runner.getUIContext().theme;\n }\n}\n\n/**\n * Pi's extension UI surface, backed by the headless client the server already speaks.\n *\n * Dialogs, notifications and status text have a real transport, so they are wired through. The\n * members that need a live TUI (custom components, widgets, footer, header, editor component,\n * autocomplete, terminal input, themes) have no headless equivalent and degrade to the same no-ops\n * Pi's own RPC mode uses, rather than throwing into extension code that checked `hasUI` first.\n */\nfunction createHeadlessUiContext(\n client: () => DoomHeadlessClient | undefined,\n theme: ExtensionUIContext['theme'],\n): ExtensionUIContext {\n const ask = async (request: DoomHeadlessClientRequest, signal?: AbortSignal): Promise<unknown> => {\n const target = client();\n if (target === undefined) return undefined;\n return await target.request(request, signal);\n };\n\n return {\n async select(title, options, opts) {\n const answer = await ask(\n { kind: 'select', title, options: options.map((option) => ({ label: option, value: option })) },\n opts?.signal,\n );\n return typeof answer === 'string' ? answer : undefined;\n },\n async confirm(title, message, opts) {\n return (await ask({ kind: 'confirm', title, message }, opts?.signal)) === true;\n },\n async input(title, _placeholder, opts) {\n // The headless request carries no placeholder field. Sending the hint as initialValue would\n // prefill the answer with it, so the hint is dropped rather than misrepresented.\n const answer = await ask({ kind: 'input', title }, opts?.signal);\n return typeof answer === 'string' ? answer : undefined;\n },\n async editor(title, prefill) {\n const answer = await ask({\n kind: 'input',\n title,\n multiline: true,\n ...(prefill === undefined ? {} : { initialValue: prefill }),\n });\n return typeof answer === 'string' ? answer : undefined;\n },\n notify(message, type) {\n // The notification schema requires a non-empty body.\n if (message.length === 0) return;\n void client()?.notify({ body: message, ...(type === undefined ? {} : { level: type }) });\n },\n setStatus(key, text) {\n client()?.setStatus(key, text);\n },\n pasteToEditor(text) {\n client()?.appendComposerText?.(text);\n },\n\n onTerminalInput: () => () => {},\n setWorkingMessage: () => {},\n setWorkingVisible: () => {},\n setWorkingIndicator: () => {},\n setHiddenThinkingLabel: () => {},\n setWidget: () => {},\n setFooter: () => {},\n setHeader: () => {},\n setTitle: () => {},\n // Matches Pi's own RPC mode, which also resolves undefined because custom components need a TUI.\n custom: async <T>(): Promise<T> => undefined as T,\n setEditorText: () => {},\n getEditorText: () => '',\n addAutocompleteProvider: () => {},\n setEditorComponent: () => {},\n getEditorComponent: () => undefined,\n theme,\n getAllThemes: () => [],\n getTheme: () => undefined,\n setTheme: () => ({ success: false, error: 'Themes are not available in the headless server' }),\n getToolsExpanded: () => false,\n setToolsExpanded: () => {},\n };\n}\n\ninterface BridgedSessionSync {\n append(entry: Entry): void;\n toHarnessId(entryId: string): string;\n toPiId(entryId: string | null): string | null;\n}\n\nconst bridgedSessionSync = new WeakMap<SessionManager, BridgedSessionSync>();\n\nexport async function createBridgedSessionManager(\n runtime: DirectHarnessRuntime,\n cwd: string,\n onNotice?: (message: string) => void,\n /** The branch the caller has already read, so opening a session scans it once. */\n branchEntries?: readonly Entry[],\n): Promise<SessionManager> {\n const metadata = runtime.session.metadata;\n const entries = branchEntries ?? (await runtime.readEntries()).entries;\n const header: PiSessionHeaderInput = {\n id: metadata.id,\n cwd: metadata.cwd ?? cwd,\n createdAt: metadata.createdAt,\n ...(metadata.parentSessionId === undefined ? {} : { parentSessionId: metadata.parentSessionId }),\n };\n\n const manager = SessionManager.inMemory(\n header.cwd,\n {\n id: metadata.id,\n ...(metadata.parentSessionId === undefined ? {} : { parentSession: metadata.parentSessionId }),\n },\n toPiFileEntries(header, entries),\n );\n\n if (typeof manager._persist !== 'function') {\n throw new Error(\"Pi's SessionManager no longer exposes '_persist'; the headless session bridge must be updated\");\n }\n const append = (manager as unknown as { _appendEntry?: (entry: SessionEntry) => void })._appendEntry;\n if (typeof append !== 'function') {\n throw new Error(\"Pi's SessionManager no longer exposes '_appendEntry'; the live session bridge must be updated\");\n }\n\n let syncingHarnessEntry = false;\n const pendingMirrors: Array<{\n readonly entry: SessionEntry;\n readonly mirrored: { customType: string; data: unknown };\n }> = [];\n const harnessToPiId = new Map<string, string>();\n const piToHarnessId = new Map<string, string>();\n const persist = manager._persist.bind(manager);\n manager._persist = (entry) => {\n persist(entry);\n if (syncingHarnessEntry) return;\n const mirrored = fromPiSessionEntry(entry);\n if (mirrored === undefined) return;\n const pending = { entry, mirrored };\n pendingMirrors.push(pending);\n void runtime.appendCustomEntry(mirrored.customType, mirrored.data).catch((error: unknown) => {\n const index = pendingMirrors.indexOf(pending);\n if (index !== -1) pendingMirrors.splice(index, 1);\n onNotice?.(\n `Pi session write for '${mirrored.customType}' was not mirrored: ${error instanceof Error ? error.message : String(error)}`,\n );\n });\n };\n const resolvePiId = (entryId: string | null): string | null => {\n if (entryId === null) return null;\n return harnessToPiId.get(entryId) ?? entryId;\n };\n bridgedSessionSync.set(manager, {\n append(entry) {\n if (manager.getEntry(entry.id) !== undefined) return;\n if (entry.type === 'custom') {\n const pendingIndex = pendingMirrors.findIndex(\n (pending) =>\n pending.mirrored.customType === entry.customType && isDeepStrictEqual(pending.mirrored.data, entry.data),\n );\n if (pendingIndex !== -1) {\n const pending = pendingMirrors.splice(pendingIndex, 1)[0];\n if (pending !== undefined) {\n harnessToPiId.set(entry.id, pending.entry.id);\n piToHarnessId.set(pending.entry.id, entry.id);\n }\n return;\n }\n }\n const converted =\n entry.type === 'compaction'\n ? toPiSessionEntry(entry, boundaryEntryId(manager.getBranch(), entry.retainedTail.length))\n : toPiSessionEntry(entry);\n if (converted === undefined) return;\n const parentId = resolvePiId(converted.parentId);\n syncingHarnessEntry = true;\n try {\n append.call(manager, parentId === converted.parentId ? converted : { ...converted, parentId });\n } finally {\n syncingHarnessEntry = false;\n }\n },\n toHarnessId: (entryId) => piToHarnessId.get(entryId) ?? entryId,\n toPiId: resolvePiId,\n });\n\n return manager;\n}\n\nexport function createPiExtensionHost(options: PiExtensionHostOptions): PiExtensionHost {\n const { cwd, agentDir, models, runtime, preload } = options;\n let runner: ExtensionRunner | undefined;\n let sessionManager: SessionManager | undefined;\n let sessionSync: BridgedSessionSync | undefined;\n let tools: readonly AgentHarnessTool<object | undefined>[] = [];\n let skills: readonly Skill[] = [];\n let registered: readonly RegisteredTool[] = [];\n // Every registered name until a restriction narrows it. Seeded at load(), because an empty set\n // before load must not read as \"everything is hidden\".\n let activeNames = new Set<string>();\n let unsubscribeEvents: (() => void) | undefined;\n let cordisConnection: DoomCordisHostConnection | undefined;\n\n // Pi's ExtensionContext getters are synchronous while every harness read is not, so the\n // facts they answer from are tracked off the harness event stream and primed from history.\n // Hot lifecycle events update only bounded, incremental state. No event rereads history.\n let contextTokens: number | null = null;\n let currentOperation: 'run' | 'compaction' | 'navigation' | null = null;\n let queuedMessages = 0;\n let turnIndex = 0;\n let runMessages: AgentMessage[] = [];\n const toolArguments = new Map<string, unknown>();\n /** Whether the compaction now running came from a Pi extension rather than the harness. */\n let compactionFromExtension = false;\n\n const report = (source: string, error: unknown): void =>\n options.onNotice?.(`Pi extension ${source}: ${error instanceof Error ? error.message : String(error)}`);\n\n const activeTools = (): readonly AgentHarnessTool<object | undefined>[] =>\n tools.filter((tool) => activeNames.has(tool.name));\n const requireRunner = (): ExtensionRunner => {\n if (runner === undefined) throw new Error('The Pi extension host is not loaded');\n return runner;\n };\n\n /**\n * Where one custom message goes, following Pi's own order in `sendCustomMessage`.\n *\n * An explicit `nextTurn` always queues; a running turn takes the message unless the\n * caller opted out; an idle agent is woken only when the caller asked for a turn.\n * Pi's remaining branch, deferring an append made mid-turn, needs no counterpart:\n * the lane already holds a write until the operation reaches a safe commit point.\n */\n const deliverMessage = (\n message: AgentMessage,\n sendOptions?: { triggerTurn?: boolean; deliverAs?: 'steer' | 'followUp' | 'nextTurn' },\n ): Promise<unknown> => {\n if (sendOptions?.deliverAs === 'nextTurn') return runtime.nextRun(message);\n if (currentOperation === 'run' && sendOptions?.triggerTurn !== false) {\n return sendOptions?.deliverAs === 'followUp' ? runtime.followUp(message) : runtime.steer(message);\n }\n if (sendOptions?.triggerTurn === true) return runtime.admitMessage(message);\n return runtime.appendMessage(message);\n };\n\n const actions: ExtensionActions = {\n sendMessage: (message, sendOptions) => {\n const custom: Extract<AgentMessage, { role: 'custom' }> = {\n role: 'custom',\n customType: message.customType,\n // Untyped extensions can pass missing content; Pi normalizes it at the same point.\n content: message.content ?? [],\n display: message.display,\n ...(message.details === undefined ? {} : { details: message.details }),\n timestamp: Date.now(),\n };\n void deliverMessage(custom, sendOptions).catch((error: unknown) => report('send_message', error));\n },\n sendUserMessage: (content) => {\n void runtime.prompt(typeof content === 'string' ? content : JSON.stringify(content));\n },\n appendEntry: (customType, data) => {\n void runtime.appendCustomEntry(customType, data);\n },\n setSessionName: (name) => {\n void runtime.setName(name);\n },\n // Harness SessionMetadata carries no display name, and Pi allows this to be absent.\n getSessionName: () => undefined,\n setLabel: (entryId, label) => {\n void runtime\n .setLabel(sessionSync?.toHarnessId(entryId) ?? entryId, label)\n .catch((error: unknown) => report('set_label', error));\n },\n getActiveTools: () => activeTools().map((tool) => tool.name),\n getAllTools: (): ToolInfo[] =>\n registered.map((tool) => ({\n name: tool.definition.name,\n description: tool.definition.description,\n parameters: tool.definition.parameters,\n ...(tool.definition.promptGuidelines === undefined\n ? {}\n : { promptGuidelines: tool.definition.promptGuidelines }),\n sourceInfo: tool.sourceInfo,\n })),\n // Backs DOOM_TOOL_SURFACE_SERVICE, which is the only way a Pi extension can hide its own\n // tools. Throwing here left every restriction inert and leaked mode-gated tools such as\n // narrate into sessions whose mode was off.\n setActiveTools: (names) => {\n const known = new Set(registered.map((tool) => tool.definition.name));\n activeNames = new Set(names.filter((name) => known.has(name)));\n options.onActiveToolsChanged?.();\n },\n refreshTools: () => {\n void runtime.replaceTools([...activeTools()]);\n },\n getCommands: () => [],\n setModel: async (model) => {\n await runtime.setModel({ provider: model.provider, id: model.id });\n return true;\n },\n getThinkingLevel: () => options.getThinkingLevel(),\n setThinkingLevel: (level) => {\n void runtime.setThinkingLevel(level);\n },\n };\n\n const contextActions: ExtensionContextActions = {\n getModel: () => options.getModel(),\n getScopedModels: () => [],\n // Mirrors the server's own readState: a tree navigation is not a turn, so it does not\n // make the agent busy.\n isIdle: () => currentOperation !== 'run' && currentOperation !== 'compaction',\n isProjectTrusted: () => true,\n getSignal: () => undefined,\n abort: () => {\n void runtime.abort();\n },\n // Queued writes are excluded. An extension's own deferred append is not a message the\n // model is waiting on, and counting it would stall every extension that waits for a\n // quiet queue before acting.\n hasPendingMessages: () => queuedMessages > 0,\n shutdown: () => {\n runtime.stop();\n },\n getContextUsage: () => contextUsageOf(contextTokens, options.getModel()?.contextWindow),\n compact: () => {\n void runtime.compact();\n },\n getSystemPrompt: () => '',\n };\n\n const piCompactionEntry = async (entryId: string): Promise<CompactionEntry | undefined> => {\n const { entries } = await runtime.readEntries();\n const converted = toPiSessionEntries(entries).find((entry) => entry.id === entryId);\n return converted?.type === 'compaction' ? converted : undefined;\n };\n\n const turnBoundaryContext = (): BoundaryContextPreview => {\n const projection = sessionManager?.buildSessionProjection();\n const contextEntries = projection?.entries ?? [];\n const contextMessages = projection?.messages ?? [];\n return {\n contextEntries,\n contextMessages,\n llmMessages: contextMessages.filter((message): message is Message => message.role !== 'bashExecution'),\n pendingMessages: [],\n canContinue: contextMessages.at(-1)?.role !== 'assistant',\n };\n };\n\n /** Harness events, translated into Pi events in AgentSession order. */\n const handleHarnessEvent = async (event: HarnessEvent): Promise<void> => {\n switch (event.type) {\n case 'run_start':\n currentOperation = 'run';\n turnIndex = 0;\n runMessages = [];\n toolArguments.clear();\n await runner?.emit({ type: 'agent_start' });\n return;\n case 'run_resume':\n currentOperation = 'run';\n return;\n case 'turn_start':\n await runner?.emit({ type: 'turn_start', turnIndex, timestamp: Date.now() });\n return;\n case 'message_start':\n await runner?.emit({ type: 'message_start', message: event.message });\n return;\n case 'message_update':\n await runner?.emit({\n type: 'message_update',\n message: event.message,\n assistantMessageEvent: event.event,\n });\n return;\n case 'message_end':\n if (event.runId !== undefined) runMessages.push(event.message);\n if (event.message.role === 'assistant') contextTokens = contextTokensOf(event.message.usage);\n // Harness persistence has already committed at this boundary, so replacement results cannot\n // be honored faithfully. The handler still observes the same lifecycle boundary.\n await runner?.emitMessageEnd({ type: 'message_end', message: event.message });\n return;\n case 'tool_start':\n toolArguments.set(event.toolCallId, event.args);\n await runner?.emit({\n type: 'tool_execution_start',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n args: event.args,\n });\n return;\n case 'tool_update':\n await runner?.emit({\n type: 'tool_execution_update',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n args: toolArguments.get(event.toolCallId),\n partialResult: event.partialResult,\n });\n return;\n case 'tool_end':\n toolArguments.delete(event.toolCallId);\n await runner?.emit({\n type: 'tool_execution_end',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n result: event.result,\n isError: event.isError,\n });\n return;\n case 'entry_added':\n sessionSync?.append(event.entry);\n return;\n case 'queue_update':\n queuedMessages = event.queues.filter((item) => item.kind !== 'write').length;\n return;\n case 'turn_end': {\n const branch = sessionManager?.getBranch() ?? [];\n // The harness commits a fresh entry after message_end (and may normalize the message\n // during settlement), so its entry_added payload need not share the event's identity.\n // A turn ends at the latest persisted assistant on this branch.\n const messageEntryId = branch.findLast(\n (entry) => entry.type === 'message' && entry.message.role === 'assistant',\n )?.id;\n if (messageEntryId === undefined) {\n report('turn_end', new Error('The persisted assistant entry was not available for the Pi boundary'));\n turnIndex += 1;\n return;\n }\n const toolResultEntryIds = event.toolResults.flatMap((result) => {\n const entryId = branch.findLast(\n (entry) =>\n entry.type === 'message' &&\n entry.message.role === 'toolResult' &&\n entry.message.toolCallId === result.toolCallId,\n )?.id;\n return entryId === undefined ? [] : [entryId];\n });\n await runner?.emitBoundary(\n {\n type: 'turn_end',\n turnIndex,\n message: event.message,\n toolResults: event.toolResults,\n messageEntryId,\n toolResultEntryIds,\n outcome:\n event.message.stopReason === 'aborted'\n ? 'aborted'\n : event.message.stopReason === 'error'\n ? 'error'\n : 'completed',\n },\n turnBoundaryContext,\n );\n turnIndex += 1;\n return;\n }\n case 'run_end':\n currentOperation = null;\n await runner?.emit({ type: 'agent_end', messages: runMessages });\n await runner?.emit({ type: 'agent_settled' });\n runMessages = [];\n toolArguments.clear();\n return;\n case 'compaction_start':\n currentOperation = 'compaction';\n return;\n case 'compaction_end': {\n currentOperation = null;\n const fromExtension = compactionFromExtension;\n compactionFromExtension = false;\n if (event.status !== 'completed') {\n await runner?.emit({\n type: 'session_compact_failed',\n reason: event.reason,\n ...(event.status === 'failed' ? { errorMessage: event.error.message } : {}),\n aborted: event.status === 'aborted',\n willRetry: event.reason === 'overflow',\n fromExtension,\n });\n return;\n }\n // Usage measured against the pre-compaction context no longer describes this one.\n contextTokens = null;\n const compactionEntry = await piCompactionEntry(event.entryId);\n if (compactionEntry === undefined) return;\n await runner?.emit({\n type: 'session_compact',\n compactionEntry,\n fromExtension,\n reason: event.reason,\n // Overflow is the only reason the harness compacts to retry a turn it had to abort.\n willRetry: event.reason === 'overflow',\n });\n return;\n }\n case 'navigation_start':\n currentOperation = 'navigation';\n return;\n case 'navigation_end': {\n currentOperation = null;\n if (event.status !== 'completed') return;\n const newLeafId = sessionSync?.toPiId(event.tipId) ?? event.tipId;\n const oldLeafId = sessionSync?.toPiId(event.fromTipId) ?? event.fromTipId;\n if (newLeafId === null) sessionManager?.resetLeaf();\n else if (sessionManager?.getEntry(newLeafId) !== undefined) sessionManager.branch(newLeafId);\n const leaf = newLeafId === null ? undefined : sessionManager?.getEntry(newLeafId);\n await runner?.emit({\n type: 'session_tree',\n newLeafId,\n oldLeafId,\n ...(leaf?.type === 'branch_summary' ? { summaryEntry: leaf } : {}),\n });\n return;\n }\n case 'value_update':\n if (event.value === 'session_name') {\n await runner?.emit({ type: 'session_info_changed', name: event.name });\n }\n return;\n case 'config_update':\n if (event.property === 'model') {\n const model = models.getModel(event.value.provider, event.value.modelId);\n const previous = event.previous as { provider?: unknown; modelId?: unknown } | undefined;\n const previousModel =\n typeof previous?.provider === 'string' && typeof previous.modelId === 'string'\n ? models.getModel(previous.provider, previous.modelId)\n : undefined;\n if (model !== undefined) await runner?.emit({ type: 'model_select', model, previousModel, source: 'set' });\n } else if (event.property === 'thinkingLevel') {\n await runner?.emit({\n type: 'thinking_level_select',\n level: event.value,\n previousLevel: event.previous,\n });\n }\n return;\n default:\n return;\n }\n };\n\n // An extension that throws must not fault the lane, so the rejection is reported here and\n // the harness sees a settled listener.\n const onHarnessEvent = (event: HarnessEvent): Promise<void> =>\n handleHarnessEvent(event).catch((error: unknown) => report(`event '${event.type}'`, error));\n\n const transformContext = async (messages: AgentMessage[]): Promise<AgentMessage[]> =>\n runner === undefined ? messages : runner.emitContext(messages);\n\n const beforeCompaction = async (\n event: HookMap['before_compaction']['event'],\n ): Promise<HookMap['before_compaction']['result']> => {\n if (runner === undefined) return undefined;\n const branchEntries = piBranchEntries((await runtime.readEntries()).entries);\n const result = await runner.emit({\n type: 'session_before_compact',\n preparation: toPiCompactionPreparation(event.preparation, branchEntries),\n branchEntries,\n ...(event.customInstructions === undefined ? {} : { customInstructions: event.customInstructions }),\n reason: event.reason,\n willRetry: event.reason === 'overflow',\n // The harness exposes no per-compaction signal. A handler that checks this sees an\n // accurate \"not aborted\" at the moment it runs; only a long handler misses a later abort.\n signal: new AbortController().signal,\n });\n if (result?.cancel === true) return { decline: true };\n if (result?.compaction === undefined) return undefined;\n compactionFromExtension = true;\n return { compaction: toHarnessCompactResult(result.compaction, branchEntries) };\n };\n\n return {\n transformContext,\n beforeCompaction,\n get tools() {\n return activeTools();\n },\n\n get toolGuidance(): readonly ToolPromptEntry[] {\n // Filtered alongside `tools`: guidance for a hidden tool would otherwise keep instructing the\n // model to call something it cannot see.\n return registered\n .filter((tool) => activeNames.has(tool.definition.name))\n .map((tool) => ({\n name: tool.definition.name,\n ...(tool.definition.promptSnippet === undefined ? {} : { promptSnippet: tool.definition.promptSnippet }),\n ...(tool.definition.promptGuidelines === undefined\n ? {}\n : { promptGuidelines: tool.definition.promptGuidelines }),\n }));\n },\n\n get skills() {\n return skills;\n },\n\n getService<T>(name: string): T | undefined {\n return cordisConnection?.root.get(name) as T | undefined;\n },\n executeTool(name, toolCallId, parameters, signal, onUpdate) {\n const tool = registered.find((entry) => entry.definition.name === name);\n if (tool === undefined) throw new Error(`Unknown Pi extension tool '${name}'`);\n return executePiTool(\n tool,\n requireRunner,\n (candidate) => activeNames.has(candidate),\n toolCallId,\n parameters,\n signal,\n onUpdate,\n );\n },\n\n async load(): Promise<void> {\n if (runner !== undefined) throw new Error('The Pi extension host was loaded more than once');\n if (preload === undefined) return;\n\n const { entries } = await runtime.readEntries();\n // A resumed session has already spent context that no future event will re-announce,\n // so the counter starts from what history measured rather than from zero.\n contextTokens = contextTokensOf(latestAssistantUsage(entries));\n\n const loaded: Extension[] = [...preload.extensions];\n\n sessionManager = await createBridgedSessionManager(runtime, cwd, options.onNotice, entries);\n const manager = sessionManager;\n sessionSync = bridgedSessionSync.get(manager);\n if (sessionSync === undefined) throw new Error('The live Pi session bridge was not initialized');\n\n runner = new ExtensionRunner(loaded, preload.runtime, cwd, manager, new ModelRegistry(models));\n // 'rpc' is the accurate mode for a server with no terminal, and it must be set before any\n // extension runs. Provider registrations were already drained against this same ModelRuntime\n // by preloadPiExtensions, so bindCore finds an empty queue here.\n runner.setUIContext(createHeadlessUiContext(options.client, resolveDefaultTheme(runner)), 'rpc');\n runner.bindCore(actions, contextActions);\n\n registered = loaded.flatMap((extension) => [...extension.tools.values()]);\n tools = registered.map((tool) => toHarnessTool(tool, requireRunner, (name) => activeNames.has(name)));\n activeNames = new Set(tools.map((tool) => tool.name));\n // Subscribe before extension lifecycle handlers run: those handlers may write history,\n // and missing their entry_added acknowledgement would permanently stale this mirror.\n unsubscribeEvents = runtime.onEvent(onHarnessEvent);\n // Pi opens session-scoped extension services before resource discovery. Doom tool\n // restrictions depend on the same ordering.\n await runner.emit({ type: 'session_start', reason: 'startup' });\n if (preload.events !== undefined)\n cordisConnection = await connectDoomCordisHost({ events: preload.events }, 'headless-pi-extension-host');\n skills = await loadPiSkills(runner, cwd, agentDir, options.onNotice);\n },\n\n async shutdown(): Promise<void> {\n unsubscribeEvents?.();\n unsubscribeEvents = undefined;\n sessionSync = undefined;\n sessionManager = undefined;\n runMessages = [];\n toolArguments.clear();\n const connection = cordisConnection;\n cordisConnection = undefined;\n try {\n if (runner !== undefined) await runner.emit({ type: 'session_shutdown', reason: 'quit' });\n } finally {\n await connection?.dispose();\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["readSyncRegistration","DefaultPackageManager","createEventBus","discoverAndLoadExtensions","ModelRegistry","sessionEntryToContextMessages","toPiSessionEntries","loadSkills","readFileSync","SessionManager","toPiFileEntries","fromPiSessionEntry","isDeepStrictEqual","toPiSessionEntry","contextUsageOf","contextTokensOf","latestAssistantUsage","ExtensionRunner","connectDoomCordisHost"],"sources":["../../../../src/services/piExtensionHost/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { isDeepStrictEqual } from 'node:util';\n\nimport type {\n AgentHarnessTool,\n AgentMessage,\n CompactionPreparation as HarnessCompactionPreparation,\n Entry,\n HarnessEvent,\n HookMap,\n JsonValue,\n Skill,\n ThinkingLevel,\n} from '@earendil-works/pi-agent-core';\nimport type { Api, Message, Model } from '@earendil-works/pi-ai';\nimport {\n createEventBus,\n DefaultPackageManager,\n discoverAndLoadExtensions,\n ExtensionRunner,\n ModelRegistry,\n initTheme,\n loadSkills,\n SessionManager,\n sessionEntryToContextMessages,\n type CompactionEntry,\n type BoundaryContextPreview,\n type CompactionResult,\n type SessionBeforeCompactEvent,\n type Extension,\n type ExtensionActions,\n type ExtensionContextActions,\n type ExtensionUIContext,\n type LoadExtensionsResult,\n type ModelRuntime,\n type RegisteredTool,\n type SessionEntry,\n type SettingsManager,\n type ToolInfo,\n} from '@earendil-works/pi-coding-agent';\n\nimport type { DoomHeadlessClient, DoomHeadlessClientRequest, DoomHeadlessToolResult } from '../../exports/headless';\nimport { connectDoomCordisHost, type DoomCordisHostConnection } from '../../pi/cordisHost';\nimport { contextTokensOf, contextUsageOf, latestAssistantUsage } from '../../services/contextUsage';\nimport {\n fromPiSessionEntry,\n toPiFileEntries,\n toPiSessionEntries,\n toPiSessionEntry,\n type PiSessionHeaderInput,\n} from '../../services/piSessionEntries';\nimport { readSyncRegistration } from '../../services/syncRegistration';\nimport type { ToolPromptEntry } from '../../services/toolPrompt';\nimport type { DirectHarnessRuntime } from '../../types/server/directHarnessRuntime';\n\n/**\n * Pi entries for this worktree, taken from the validated sync registration.\n *\n * This is the same admission gate Pi's own managed dispatcher applies: the registration is\n * checked for protocol version, worktree identity, state hash, and manifest agreement before\n * any entry is returned. An unsynced or mismatched worktree contributes nothing.\n */\nexport function resolvePiExtensionEntries(repoRoot: string): readonly string[] {\n try {\n const registration = readSyncRegistration(repoRoot);\n return registration ? [registration.package.entry] : [];\n } catch {\n // A stale or mismatched registration is a normal unsynced state, not a session failure.\n return [];\n }\n}\n\n/**\n * Extension entries of the packages listed under `packages` in Pi's settings.json.\n *\n * Interactive Pi expands that list through its resource loader, which this server does not run,\n * so a provider package such as a local Claude Code bridge would load under `pi` and be missing\n * here. Only `packages` entries are taken: Pi's top-level `extensions` list can name DoomPi\n * itself, and the auto-discovered `extensions/` folders are already loaded by\n * `discoverAndLoadExtensions`. A package that is not installed is skipped, never installed, so\n * starting a session cannot trigger a network install.\n */\nexport async function resolvePiSettingsPackageEntries(options: {\n readonly cwd: string;\n readonly agentDir: string;\n readonly settings: SettingsManager;\n readonly onNotice?: (message: string) => void;\n}): Promise<readonly string[]> {\n try {\n const packages = new DefaultPackageManager({\n cwd: options.cwd,\n agentDir: options.agentDir,\n settingsManager: options.settings,\n });\n const resolved = await packages.resolve(async (source) => {\n options.onNotice?.(`Pi package ${source} is not installed; skipped. Install it with pi to load it here.`);\n return 'skip';\n });\n return resolved.extensions\n .filter((resource) => resource.enabled && resource.metadata.origin === 'package')\n .map((resource) => resource.path);\n } catch (error) {\n // Settings packages are optional additions; a broken entry must not stop the session.\n options.onNotice?.(`Pi settings packages: ${error instanceof Error ? error.message : String(error)}`);\n return [];\n }\n}\n\n/**\n * Load Pi extensions and apply their provider registrations to the model runtime.\n *\n * `pi.registerProvider` only queues into the extension runtime while extensions load; Pi drains\n * that queue in `ExtensionRunner.bindCore`. The runner cannot exist before the harness runtime,\n * and the harness runtime is built from an already-resolved model, so waiting for `bindCore`\n * would resolve the session's default model before extension providers exist and silently fall\n * back to the first available model. Draining the queue here puts provider registration ahead of\n * model resolution.\n *\n * The result is returned so `createPiExtensionHost` reuses it instead of discovering again, which\n * keeps every extension factory running exactly once.\n */\nexport async function preloadPiExtensions(options: {\n readonly cwd: string;\n readonly agentDir: string;\n readonly models: ModelRuntime;\n readonly extensionPaths: readonly string[];\n readonly onNotice?: (message: string) => void;\n}): Promise<(LoadExtensionsResult & { readonly events?: ReturnType<typeof createEventBus> }) | undefined> {\n if (options.extensionPaths.length === 0) return undefined;\n\n const events = createEventBus();\n const result = await discoverAndLoadExtensions([...options.extensionPaths], options.cwd, options.agentDir, events);\n for (const failure of result.errors) options.onNotice?.(`Pi extension ${failure.path}: ${failure.error}`);\n\n // The same target bindCore uses: without providerActions it registers through ModelRegistry,\n // which delegates to this ModelRuntime.\n const registry = new ModelRegistry(options.models);\n const report = (extensionPath: string, error: unknown): void =>\n options.onNotice?.(\n `Pi extension ${extensionPath}: provider registration failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n\n for (const pending of result.runtime.pendingProviderRegistrations) {\n try {\n registry.registerProvider(pending.name, pending.config);\n } catch (error) {\n report(pending.extensionPath, error);\n }\n }\n for (const pending of result.runtime.pendingNativeProviderRegistrations) {\n try {\n registry.registerProvider(pending.provider);\n } catch (error) {\n report(pending.extensionPath, error);\n }\n }\n // Cleared so bindCore does not repeat registrations already applied to the same runtime.\n result.runtime.pendingProviderRegistrations = [];\n result.runtime.pendingNativeProviderRegistrations = [];\n\n return Object.assign(result, { events });\n}\n/**\n * Pi native extensions are session lifetime. They contribute tools once and are never\n * retracted, so they are merged at the harness apply boundary rather than contributed to the\n * headless kernel, whose active layers come solely from server bundle candidates and would\n * drop any owner that is not a declared facet package.\n *\n * Their event handlers are a second surface. Pi drives them from an interactive\n * AgentSession this server does not run, so the handful the harness can speak to\n * faithfully are bridged here: the two agent-loop notifications an extension needs\n * to observe a turn, and the three hooks that let one shape or replace context.\n * Everything else stays undelivered rather than being approximated.\n */\nexport interface PiExtensionHostOptions {\n readonly cwd: string;\n readonly agentDir: string;\n readonly models: ModelRuntime;\n readonly runtime: DirectHarnessRuntime;\n /**\n * Extensions already loaded by `preloadPiExtensions`, or undefined when this worktree\n * contributes none. Reused rather than re-discovered so each factory runs exactly once.\n */\n readonly preload: (LoadExtensionsResult & { readonly events?: ReturnType<typeof createEventBus> }) | undefined;\n /** Current model, tracked by the caller because harness session metadata does not carry it. */\n readonly getModel: () => Model<Api> | undefined;\n readonly getThinkingLevel: () => ThinkingLevel;\n /**\n * Resolved at call time, because the host is constructed while the session is still assembling.\n * Backs Pi's extension dialogs, notifications and status text.\n */\n readonly client: () => DoomHeadlessClient | undefined;\n readonly onNotice?: (message: string) => void;\n /**\n * Called when a Pi extension tool restriction changes the active set.\n *\n * Doom extensions register their restrictions asynchronously, after the first tool apply has\n * already run, so the caller has to re-apply rather than read `tools` once at load.\n */\n readonly onActiveToolsChanged?: () => void;\n}\n\nexport interface PiExtensionHost {\n /** Session lifetime tools contributed by Pi extensions, in load order. */\n readonly tools: readonly AgentHarnessTool<object | undefined>[];\n /**\n * System-prompt guidance for those same tools, in the same order.\n *\n * `AgentHarnessTool` has no field for it, so it cannot ride on the harness\n * tool the way it does on a Pi extension definition, and the headless host\n * renders it into the prompt itself.\n */\n readonly toolGuidance: readonly ToolPromptEntry[];\n /** Session lifetime skills contributed by Pi extensions, in discovery order. */\n readonly skills: readonly Skill[];\n executeTool(\n name: string,\n toolCallId: string,\n parameters: Record<string, unknown>,\n signal?: AbortSignal,\n onUpdate?: (result: DoomHeadlessToolResult) => void,\n ): Promise<DoomHeadlessToolResult>;\n /**\n * Pi's `context` event, as one step in the host's transform chain.\n *\n * Returns the messages unchanged when no extension is loaded, so the caller can\n * chain it unconditionally.\n */\n transformContext(messages: AgentMessage[]): Promise<AgentMessage[]>;\n /** Pi's `session_before_compact` event, translated into the harness hook's answer. */\n beforeCompaction(event: HookMap['before_compaction']['event']): Promise<HookMap['before_compaction']['result']>;\n /** Reads a session service from the Pi Cordis root without exposing registration APIs. */\n getService<T>(name: string): T | undefined;\n load(): Promise<void>;\n shutdown(): Promise<void>;\n}\n/** Pi does not export the preparation type on its own, only through the event that carries it. */\ntype PiCompactionPreparation = SessionBeforeCompactEvent['preparation'];\n\n/**\n * The compaction boundary, named the way Pi names it.\n *\n * Pi identifies the cut by the first entry it keeps; the harness carries the kept\n * messages inline instead. Counting whole entries back from the tip until the kept\n * messages are covered recovers the id, and {@link retainedTailFrom} is its exact\n * inverse, so an extension that echoes the id it was given gets its own tail back.\n */\nfunction boundaryEntryId(branchEntries: readonly SessionEntry[], retainedCount: number): string | undefined {\n let remaining = retainedCount;\n for (let index = branchEntries.length - 1; index >= 0; index -= 1) {\n const entry = branchEntries[index];\n if (entry === undefined) continue;\n if (remaining <= 0) return entry.id;\n remaining -= sessionEntryToContextMessages(entry).length;\n if (remaining <= 0) return entry.id;\n }\n return branchEntries[0]?.id;\n}\n\n/** The messages an entry id keeps, which is what the harness stores on the compaction entry. */\nfunction retainedTailFrom(branchEntries: readonly SessionEntry[], firstKeptEntryId: string): AgentMessage[] {\n const index = branchEntries.findIndex((entry) => entry.id === firstKeptEntryId);\n if (index === -1) return [];\n return branchEntries.slice(index).flatMap((entry) => sessionEntryToContextMessages(entry));\n}\n\nfunction toPiCompactionPreparation(\n preparation: HarnessCompactionPreparation,\n branchEntries: readonly SessionEntry[],\n): PiCompactionPreparation {\n return {\n firstKeptEntryId: boundaryEntryId(branchEntries, preparation.retainedTail.length) ?? '',\n messagesToSummarize: preparation.messagesToSummarize,\n turnPrefixMessages: preparation.turnPrefixMessages,\n isSplitTurn: preparation.isSplitTurn,\n tokensBefore: preparation.tokensBefore,\n ...(preparation.previousSummary === undefined ? {} : { previousSummary: preparation.previousSummary }),\n fileOps: preparation.fileOps,\n settings: preparation.settings,\n };\n}\n\nfunction toHarnessCompactResult(\n result: CompactionResult,\n branchEntries: readonly SessionEntry[],\n): NonNullable<NonNullable<HookMap['before_compaction']['result']>['compaction']> {\n return {\n summary: result.summary,\n tokensBefore: result.tokensBefore,\n retainedTail: retainedTailFrom(branchEntries, result.firstKeptEntryId),\n ...(result.usage === undefined ? {} : { usage: result.usage }),\n ...(result.details === undefined ? {} : { details: result.details as JsonValue }),\n };\n}\n\nfunction piBranchEntries(entries: readonly Entry[]): SessionEntry[] {\n return toPiSessionEntries(entries);\n}\n\nasync function executePiTool(\n registered: RegisteredTool,\n runner: () => ExtensionRunner,\n isActive: (name: string) => boolean,\n toolCallId: string,\n parameters: unknown,\n signal: AbortSignal | undefined,\n onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n): Promise<DoomHeadlessToolResult> {\n const definition = registered.definition;\n // Admission, not visibility. A cached definition must still be active when called.\n if (!isActive(definition.name)) throw new Error(`Tool '${definition.name}' is no longer active`);\n const prepared = definition.prepareArguments ? definition.prepareArguments(parameters) : parameters;\n const result = await definition.execute(\n toolCallId,\n prepared,\n signal,\n (partial) => onUpdate?.({ content: partial.content, details: partial.details }),\n runner().createContext(),\n );\n return { content: result.content, details: result.details };\n}\n\nfunction toHarnessTool(\n registered: RegisteredTool,\n runner: () => ExtensionRunner,\n isActive: (name: string) => boolean,\n): AgentHarnessTool<object | undefined> {\n const definition = registered.definition;\n return {\n name: definition.name,\n label: definition.label ?? definition.name,\n description: definition.description,\n parameters: definition.parameters,\n ...(definition.executionMode === undefined ? {} : { executionMode: definition.executionMode }),\n async execute(toolCallId, parameters, onUpdate, _toolContext, _invocation, context) {\n const result = await executePiTool(\n registered,\n runner,\n isActive,\n toolCallId,\n parameters,\n context.abortSignal,\n (partial) => onUpdate({ content: partial.content, details: partial.details }),\n );\n return { content: result.content, details: result.details };\n },\n };\n}\n\n/**\n * Resolves the skills Pi extensions contribute.\n *\n * Pi wires this through AgentSession's resource loader, which the headless server does not use,\n * so the pipeline is rebuilt here against the harness. Pi's own loader parses and validates the\n * frontmatter; only the body has to be read, because Pi's Skill record carries a path where the\n * harness wants inline content.\n */\nasync function loadPiSkills(\n runner: ExtensionRunner,\n cwd: string,\n agentDir: string,\n onNotice: ((message: string) => void) | undefined,\n): Promise<readonly Skill[]> {\n const discovered = await runner.emitResourcesDiscover(cwd, 'startup');\n if (discovered.skillPaths.length === 0) return [];\n\n const loaded = loadSkills({\n cwd,\n agentDir,\n skillPaths: discovered.skillPaths.map((entry) => entry.path),\n includeDefaults: false,\n });\n for (const diagnostic of loaded.diagnostics) onNotice?.(`Pi skill ${diagnostic.path}: ${diagnostic.message}`);\n\n const resolved: Skill[] = [];\n for (const skill of loaded.skills) {\n try {\n resolved.push({\n name: skill.name,\n description: skill.description,\n content: readFileSync(skill.filePath, 'utf8'),\n filePath: skill.filePath,\n ...(skill.disableModelInvocation ? { disableModelInvocation: true } : {}),\n });\n } catch (error) {\n // One unreadable skill file must not cost the session every other skill.\n onNotice?.(`Pi skill ${skill.filePath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n return resolved;\n}\n/**\n * A SessionManager backed by the session the server already has open.\n *\n * Pi extensions read history through a SessionManager, which is JSONL backed, while the server\n * persists to SQLite through the harness. Rather than opening a second connection and taking a\n * second history lease, the manager is hydrated in memory from the live session and its writes\n * are mirrored back through the same harness runtime.\n *\n * `_persist` is public in Pi's source and in its shipped types, but it is not a documented\n * extension point, so the guard below turns a rename in a future Pi release into an immediate,\n * named failure instead of history that silently stops being recorded.\n */\n/**\n * Pi's default theme, reached through public API only.\n *\n * The singleton lives in Pi's interactive theme module, which the package `exports` map does not\n * expose, so a deep import fails with ERR_PACKAGE_PATH_NOT_EXPORTED. A freshly constructed runner\n * still carries Pi's own no-op UI context, whose `theme` getter returns that singleton, so reading\n * it back before binding is the supported route. The getter throws until `initTheme` has run.\n */\nfunction resolveDefaultTheme(runner: ExtensionRunner): ExtensionUIContext['theme'] {\n try {\n return runner.getUIContext().theme;\n } catch {\n // Not a failure: the theme singleton is lazily initialised and no TUI has done it here.\n initTheme(undefined, false);\n return runner.getUIContext().theme;\n }\n}\n\n/**\n * Pi's extension UI surface, backed by the headless client the server already speaks.\n *\n * Dialogs, notifications and status text have a real transport, so they are wired through. The\n * members that need a live TUI (custom components, widgets, footer, header, editor component,\n * autocomplete, terminal input, themes) have no headless equivalent and degrade to the same no-ops\n * Pi's own RPC mode uses, rather than throwing into extension code that checked `hasUI` first.\n */\nfunction createHeadlessUiContext(\n client: () => DoomHeadlessClient | undefined,\n theme: ExtensionUIContext['theme'],\n): ExtensionUIContext {\n const ask = async (request: DoomHeadlessClientRequest, signal?: AbortSignal): Promise<unknown> => {\n const target = client();\n if (target === undefined) return undefined;\n return await target.request(request, signal);\n };\n\n return {\n async select(title, options, opts) {\n const answer = await ask(\n { kind: 'select', title, options: options.map((option) => ({ label: option, value: option })) },\n opts?.signal,\n );\n return typeof answer === 'string' ? answer : undefined;\n },\n async confirm(title, message, opts) {\n return (await ask({ kind: 'confirm', title, message }, opts?.signal)) === true;\n },\n async input(title, _placeholder, opts) {\n // The headless request carries no placeholder field. Sending the hint as initialValue would\n // prefill the answer with it, so the hint is dropped rather than misrepresented.\n const answer = await ask({ kind: 'input', title }, opts?.signal);\n return typeof answer === 'string' ? answer : undefined;\n },\n async editor(title, prefill) {\n const answer = await ask({\n kind: 'input',\n title,\n multiline: true,\n ...(prefill === undefined ? {} : { initialValue: prefill }),\n });\n return typeof answer === 'string' ? answer : undefined;\n },\n notify(message, type) {\n // The notification schema requires a non-empty body.\n if (message.length === 0) return;\n void client()?.notify({ body: message, ...(type === undefined ? {} : { level: type }) });\n },\n setStatus(key, text) {\n client()?.setStatus(key, text);\n },\n pasteToEditor(text) {\n client()?.appendComposerText?.(text);\n },\n\n onTerminalInput: () => () => {},\n setWorkingMessage: () => {},\n setWorkingVisible: () => {},\n setWorkingIndicator: () => {},\n setHiddenThinkingLabel: () => {},\n setWidget: () => {},\n setFooter: () => {},\n setHeader: () => {},\n setTitle: () => {},\n // Matches Pi's own RPC mode, which also resolves undefined because custom components need a TUI.\n custom: async <T>(): Promise<T> => undefined as T,\n setEditorText: () => {},\n getEditorText: () => '',\n addAutocompleteProvider: () => {},\n setEditorComponent: () => {},\n getEditorComponent: () => undefined,\n theme,\n getAllThemes: () => [],\n getTheme: () => undefined,\n setTheme: () => ({ success: false, error: 'Themes are not available in the headless server' }),\n getToolsExpanded: () => false,\n setToolsExpanded: () => {},\n };\n}\n\ninterface BridgedSessionSync {\n append(entry: Entry): void;\n toHarnessId(entryId: string): string;\n toPiId(entryId: string | null): string | null;\n}\n\nconst bridgedSessionSync = new WeakMap<SessionManager, BridgedSessionSync>();\n\nexport async function createBridgedSessionManager(\n runtime: DirectHarnessRuntime,\n cwd: string,\n onNotice?: (message: string) => void,\n /** The branch the caller has already read, so opening a session scans it once. */\n branchEntries?: readonly Entry[],\n): Promise<SessionManager> {\n const metadata = runtime.session.metadata;\n const entries = branchEntries ?? (await runtime.readEntries()).entries;\n const header: PiSessionHeaderInput = {\n id: metadata.id,\n cwd: metadata.cwd ?? cwd,\n createdAt: metadata.createdAt,\n ...(metadata.parentSessionId === undefined ? {} : { parentSessionId: metadata.parentSessionId }),\n };\n\n const manager = SessionManager.inMemory(\n header.cwd,\n {\n id: metadata.id,\n ...(metadata.parentSessionId === undefined ? {} : { parentSession: metadata.parentSessionId }),\n },\n toPiFileEntries(header, entries),\n );\n\n if (typeof manager._persist !== 'function') {\n throw new Error(\"Pi's SessionManager no longer exposes '_persist'; the headless session bridge must be updated\");\n }\n const append = (manager as unknown as { _appendEntry?: (entry: SessionEntry) => void })._appendEntry;\n if (typeof append !== 'function') {\n throw new Error(\"Pi's SessionManager no longer exposes '_appendEntry'; the live session bridge must be updated\");\n }\n\n let syncingHarnessEntry = false;\n const pendingMirrors: Array<{\n readonly entry: SessionEntry;\n readonly mirrored: { customType: string; data: unknown };\n }> = [];\n const harnessToPiId = new Map<string, string>();\n const piToHarnessId = new Map<string, string>();\n const persist = manager._persist.bind(manager);\n manager._persist = (entry) => {\n persist(entry);\n if (syncingHarnessEntry) return;\n const mirrored = fromPiSessionEntry(entry);\n if (mirrored === undefined) return;\n const pending = { entry, mirrored };\n pendingMirrors.push(pending);\n void runtime.appendCustomEntry(mirrored.customType, mirrored.data).catch((error: unknown) => {\n const index = pendingMirrors.indexOf(pending);\n if (index !== -1) pendingMirrors.splice(index, 1);\n onNotice?.(\n `Pi session write for '${mirrored.customType}' was not mirrored: ${error instanceof Error ? error.message : String(error)}`,\n );\n });\n };\n const resolvePiId = (entryId: string | null): string | null => {\n if (entryId === null) return null;\n return harnessToPiId.get(entryId) ?? entryId;\n };\n bridgedSessionSync.set(manager, {\n append(entry) {\n if (manager.getEntry(entry.id) !== undefined) return;\n if (entry.type === 'custom') {\n const pendingIndex = pendingMirrors.findIndex(\n (pending) =>\n pending.mirrored.customType === entry.customType && isDeepStrictEqual(pending.mirrored.data, entry.data),\n );\n if (pendingIndex !== -1) {\n const pending = pendingMirrors.splice(pendingIndex, 1)[0];\n if (pending !== undefined) {\n harnessToPiId.set(entry.id, pending.entry.id);\n piToHarnessId.set(pending.entry.id, entry.id);\n }\n return;\n }\n }\n const converted =\n entry.type === 'compaction'\n ? toPiSessionEntry(entry, boundaryEntryId(manager.getBranch(), entry.retainedTail.length))\n : toPiSessionEntry(entry);\n if (converted === undefined) return;\n const parentId = resolvePiId(converted.parentId);\n syncingHarnessEntry = true;\n try {\n append.call(manager, parentId === converted.parentId ? converted : { ...converted, parentId });\n } finally {\n syncingHarnessEntry = false;\n }\n },\n toHarnessId: (entryId) => piToHarnessId.get(entryId) ?? entryId,\n toPiId: resolvePiId,\n });\n\n return manager;\n}\n\nexport function createPiExtensionHost(options: PiExtensionHostOptions): PiExtensionHost {\n const { cwd, agentDir, models, runtime, preload } = options;\n let runner: ExtensionRunner | undefined;\n let sessionManager: SessionManager | undefined;\n let sessionSync: BridgedSessionSync | undefined;\n let tools: readonly AgentHarnessTool<object | undefined>[] = [];\n let skills: readonly Skill[] = [];\n let registered: readonly RegisteredTool[] = [];\n // Every registered name until a restriction narrows it. Seeded at load(), because an empty set\n // before load must not read as \"everything is hidden\".\n let activeNames = new Set<string>();\n let unsubscribeEvents: (() => void) | undefined;\n let cordisConnection: DoomCordisHostConnection | undefined;\n\n // Pi's ExtensionContext getters are synchronous while every harness read is not, so the\n // facts they answer from are tracked off the harness event stream and primed from history.\n // Hot lifecycle events update only bounded, incremental state. No event rereads history.\n let contextTokens: number | null = null;\n let currentOperation: 'run' | 'compaction' | 'navigation' | null = null;\n let queuedMessages = 0;\n let turnIndex = 0;\n let runMessages: AgentMessage[] = [];\n const toolArguments = new Map<string, unknown>();\n /** Whether the compaction now running came from a Pi extension rather than the harness. */\n let compactionFromExtension = false;\n\n const report = (source: string, error: unknown): void =>\n options.onNotice?.(`Pi extension ${source}: ${error instanceof Error ? error.message : String(error)}`);\n\n const activeTools = (): readonly AgentHarnessTool<object | undefined>[] =>\n tools.filter((tool) => activeNames.has(tool.name));\n const requireRunner = (): ExtensionRunner => {\n if (runner === undefined) throw new Error('The Pi extension host is not loaded');\n return runner;\n };\n\n /**\n * Where one custom message goes, following Pi's own order in `sendCustomMessage`.\n *\n * An explicit `nextTurn` always queues; a running turn takes the message unless the\n * caller opted out; an idle agent is woken only when the caller asked for a turn.\n * Pi's remaining branch, deferring an append made mid-turn, needs no counterpart:\n * the lane already holds a write until the operation reaches a safe commit point.\n */\n const deliverMessage = (\n message: AgentMessage,\n sendOptions?: { triggerTurn?: boolean; deliverAs?: 'steer' | 'followUp' | 'nextTurn' },\n ): Promise<unknown> => {\n if (sendOptions?.deliverAs === 'nextTurn') return runtime.nextRun(message);\n if (currentOperation === 'run' && sendOptions?.triggerTurn !== false) {\n return sendOptions?.deliverAs === 'followUp' ? runtime.followUp(message) : runtime.steer(message);\n }\n if (sendOptions?.triggerTurn === true) return runtime.admitMessage(message);\n return runtime.appendMessage(message);\n };\n\n const actions: ExtensionActions = {\n sendMessage: (message, sendOptions) => {\n const custom: Extract<AgentMessage, { role: 'custom' }> = {\n role: 'custom',\n customType: message.customType,\n // Untyped extensions can pass missing content; Pi normalizes it at the same point.\n content: message.content ?? [],\n display: message.display,\n ...(message.details === undefined ? {} : { details: message.details }),\n timestamp: Date.now(),\n };\n void deliverMessage(custom, sendOptions).catch((error: unknown) => report('send_message', error));\n },\n sendUserMessage: (content) => {\n void runtime.prompt(typeof content === 'string' ? content : JSON.stringify(content));\n },\n appendEntry: (customType, data) => {\n void runtime.appendCustomEntry(customType, data);\n },\n setSessionName: (name) => {\n void runtime.setName(name);\n },\n // Harness SessionMetadata carries no display name, and Pi allows this to be absent.\n getSessionName: () => undefined,\n setLabel: (entryId, label) => {\n void runtime\n .setLabel(sessionSync?.toHarnessId(entryId) ?? entryId, label)\n .catch((error: unknown) => report('set_label', error));\n },\n getActiveTools: () => activeTools().map((tool) => tool.name),\n getAllTools: (): ToolInfo[] =>\n registered.map((tool) => ({\n name: tool.definition.name,\n description: tool.definition.description,\n parameters: tool.definition.parameters,\n ...(tool.definition.promptGuidelines === undefined\n ? {}\n : { promptGuidelines: tool.definition.promptGuidelines }),\n sourceInfo: tool.sourceInfo,\n })),\n // Backs DOOM_TOOL_SURFACE_SERVICE, which is the only way a Pi extension can hide its own\n // tools. Throwing here left every restriction inert and leaked mode-gated tools such as\n // narrate into sessions whose mode was off.\n setActiveTools: (names) => {\n const known = new Set(registered.map((tool) => tool.definition.name));\n activeNames = new Set(names.filter((name) => known.has(name)));\n options.onActiveToolsChanged?.();\n },\n refreshTools: () => {\n void runtime.replaceTools([...activeTools()]);\n },\n getCommands: () => [],\n setModel: async (model) => {\n await runtime.setModel({ provider: model.provider, id: model.id });\n return true;\n },\n getThinkingLevel: () => options.getThinkingLevel(),\n setThinkingLevel: (level) => {\n void runtime.setThinkingLevel(level);\n },\n };\n\n const contextActions: ExtensionContextActions = {\n getModel: () => options.getModel(),\n getScopedModels: () => [],\n // Mirrors the server's own readState: a tree navigation is not a turn, so it does not\n // make the agent busy.\n isIdle: () => currentOperation !== 'run' && currentOperation !== 'compaction',\n isProjectTrusted: () => true,\n getSignal: () => undefined,\n abort: () => {\n void runtime.abort();\n },\n // Queued writes are excluded. An extension's own deferred append is not a message the\n // model is waiting on, and counting it would stall every extension that waits for a\n // quiet queue before acting.\n hasPendingMessages: () => queuedMessages > 0,\n shutdown: () => {\n runtime.stop();\n },\n getContextUsage: () => contextUsageOf(contextTokens, options.getModel()?.contextWindow),\n compact: () => {\n void runtime.compact();\n },\n getSystemPrompt: () => '',\n };\n\n const piCompactionEntry = async (entryId: string): Promise<CompactionEntry | undefined> => {\n const { entries } = await runtime.readEntries();\n const converted = toPiSessionEntries(entries).find((entry) => entry.id === entryId);\n return converted?.type === 'compaction' ? converted : undefined;\n };\n\n const turnBoundaryContext = (): BoundaryContextPreview => {\n const projection = sessionManager?.buildSessionProjection();\n const contextEntries = projection?.entries ?? [];\n const contextMessages = projection?.messages ?? [];\n return {\n contextEntries,\n contextMessages,\n llmMessages: contextMessages.filter((message): message is Message => message.role !== 'bashExecution'),\n pendingMessages: [],\n canContinue: contextMessages.at(-1)?.role !== 'assistant',\n };\n };\n\n /** Harness events, translated into Pi events in AgentSession order. */\n const handleHarnessEvent = async (event: HarnessEvent): Promise<void> => {\n switch (event.type) {\n case 'run_start':\n currentOperation = 'run';\n turnIndex = 0;\n runMessages = [];\n toolArguments.clear();\n await runner?.emit({ type: 'agent_start' });\n return;\n case 'run_resume':\n currentOperation = 'run';\n return;\n case 'turn_start':\n await runner?.emit({ type: 'turn_start', turnIndex, timestamp: Date.now() });\n return;\n case 'message_start':\n await runner?.emit({ type: 'message_start', message: event.message });\n return;\n case 'message_update':\n await runner?.emit({\n type: 'message_update',\n message: event.message,\n assistantMessageEvent: event.event,\n });\n return;\n case 'message_end':\n if (event.runId !== undefined) runMessages.push(event.message);\n if (event.message.role === 'assistant') contextTokens = contextTokensOf(event.message.usage);\n // Harness persistence has already committed at this boundary, so replacement results cannot\n // be honored faithfully. The handler still observes the same lifecycle boundary.\n await runner?.emitMessageEnd({ type: 'message_end', message: event.message });\n return;\n case 'tool_start':\n toolArguments.set(event.toolCallId, event.args);\n await runner?.emit({\n type: 'tool_execution_start',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n args: event.args,\n });\n return;\n case 'tool_update':\n await runner?.emit({\n type: 'tool_execution_update',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n args: toolArguments.get(event.toolCallId),\n partialResult: event.partialResult,\n });\n return;\n case 'tool_end':\n toolArguments.delete(event.toolCallId);\n await runner?.emit({\n type: 'tool_execution_end',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n result: event.result,\n isError: event.isError,\n });\n return;\n case 'entry_added':\n sessionSync?.append(event.entry);\n return;\n case 'queue_update':\n queuedMessages = event.queues.filter((item) => item.kind !== 'write').length;\n return;\n case 'turn_end': {\n const branch = sessionManager?.getBranch() ?? [];\n // The harness commits a fresh entry after message_end (and may normalize the message\n // during settlement), so its entry_added payload need not share the event's identity.\n // A turn ends at the latest persisted assistant on this branch.\n const messageEntryId = branch.findLast(\n (entry) => entry.type === 'message' && entry.message.role === 'assistant',\n )?.id;\n if (messageEntryId === undefined) {\n report('turn_end', new Error('The persisted assistant entry was not available for the Pi boundary'));\n turnIndex += 1;\n return;\n }\n const toolResultEntryIds = event.toolResults.flatMap((result) => {\n const entryId = branch.findLast(\n (entry) =>\n entry.type === 'message' &&\n entry.message.role === 'toolResult' &&\n entry.message.toolCallId === result.toolCallId,\n )?.id;\n return entryId === undefined ? [] : [entryId];\n });\n await runner?.emitBoundary(\n {\n type: 'turn_end',\n turnIndex,\n message: event.message,\n toolResults: event.toolResults,\n messageEntryId,\n toolResultEntryIds,\n outcome:\n event.message.stopReason === 'aborted'\n ? 'aborted'\n : event.message.stopReason === 'error'\n ? 'error'\n : 'completed',\n },\n turnBoundaryContext,\n );\n turnIndex += 1;\n return;\n }\n case 'run_end':\n currentOperation = null;\n await runner?.emit({ type: 'agent_end', messages: runMessages });\n await runner?.emit({ type: 'agent_settled' });\n runMessages = [];\n toolArguments.clear();\n return;\n case 'compaction_start':\n currentOperation = 'compaction';\n return;\n case 'compaction_end': {\n currentOperation = null;\n const fromExtension = compactionFromExtension;\n compactionFromExtension = false;\n if (event.status !== 'completed') {\n await runner?.emit({\n type: 'session_compact_failed',\n reason: event.reason,\n ...(event.status === 'failed' ? { errorMessage: event.error.message } : {}),\n aborted: event.status === 'aborted',\n willRetry: event.reason === 'overflow',\n fromExtension,\n });\n return;\n }\n // Usage measured against the pre-compaction context no longer describes this one.\n contextTokens = null;\n const compactionEntry = await piCompactionEntry(event.entryId);\n if (compactionEntry === undefined) return;\n await runner?.emit({\n type: 'session_compact',\n compactionEntry,\n fromExtension,\n reason: event.reason,\n // Overflow is the only reason the harness compacts to retry a turn it had to abort.\n willRetry: event.reason === 'overflow',\n });\n return;\n }\n case 'navigation_start':\n currentOperation = 'navigation';\n return;\n case 'navigation_end': {\n currentOperation = null;\n if (event.status !== 'completed') return;\n const newLeafId = sessionSync?.toPiId(event.tipId) ?? event.tipId;\n const oldLeafId = sessionSync?.toPiId(event.fromTipId) ?? event.fromTipId;\n if (newLeafId === null) sessionManager?.resetLeaf();\n else if (sessionManager?.getEntry(newLeafId) !== undefined) sessionManager.branch(newLeafId);\n const leaf = newLeafId === null ? undefined : sessionManager?.getEntry(newLeafId);\n await runner?.emit({\n type: 'session_tree',\n newLeafId,\n oldLeafId,\n ...(leaf?.type === 'branch_summary' ? { summaryEntry: leaf } : {}),\n });\n return;\n }\n case 'value_update':\n if (event.value === 'session_name') {\n await runner?.emit({ type: 'session_info_changed', name: event.name });\n }\n return;\n case 'config_update':\n if (event.property === 'model') {\n const model = models.getModel(event.value.provider, event.value.modelId);\n const previous = event.previous as { provider?: unknown; modelId?: unknown } | undefined;\n const previousModel =\n typeof previous?.provider === 'string' && typeof previous.modelId === 'string'\n ? models.getModel(previous.provider, previous.modelId)\n : undefined;\n if (model !== undefined) await runner?.emit({ type: 'model_select', model, previousModel, source: 'set' });\n } else if (event.property === 'thinkingLevel') {\n await runner?.emit({\n type: 'thinking_level_select',\n level: event.value,\n previousLevel: event.previous,\n });\n }\n return;\n default:\n return;\n }\n };\n\n // An extension that throws must not fault the lane, so the rejection is reported here and\n // the harness sees a settled listener.\n const onHarnessEvent = (event: HarnessEvent): Promise<void> =>\n handleHarnessEvent(event).catch((error: unknown) => report(`event '${event.type}'`, error));\n\n const transformContext = async (messages: AgentMessage[]): Promise<AgentMessage[]> =>\n runner === undefined ? messages : runner.emitContext(messages);\n\n const beforeCompaction = async (\n event: HookMap['before_compaction']['event'],\n ): Promise<HookMap['before_compaction']['result']> => {\n if (runner === undefined) return undefined;\n const branchEntries = piBranchEntries((await runtime.readEntries()).entries);\n const result = await runner.emit({\n type: 'session_before_compact',\n preparation: toPiCompactionPreparation(event.preparation, branchEntries),\n branchEntries,\n ...(event.customInstructions === undefined ? {} : { customInstructions: event.customInstructions }),\n reason: event.reason,\n willRetry: event.reason === 'overflow',\n // The harness exposes no per-compaction signal. A handler that checks this sees an\n // accurate \"not aborted\" at the moment it runs; only a long handler misses a later abort.\n signal: new AbortController().signal,\n });\n if (result?.cancel === true) return { decline: true };\n if (result?.compaction === undefined) return undefined;\n compactionFromExtension = true;\n return { compaction: toHarnessCompactResult(result.compaction, branchEntries) };\n };\n\n return {\n transformContext,\n beforeCompaction,\n get tools() {\n return activeTools();\n },\n\n get toolGuidance(): readonly ToolPromptEntry[] {\n // Filtered alongside `tools`: guidance for a hidden tool would otherwise keep instructing the\n // model to call something it cannot see.\n return registered\n .filter((tool) => activeNames.has(tool.definition.name))\n .map((tool) => ({\n name: tool.definition.name,\n ...(tool.definition.promptSnippet === undefined ? {} : { promptSnippet: tool.definition.promptSnippet }),\n ...(tool.definition.promptGuidelines === undefined\n ? {}\n : { promptGuidelines: tool.definition.promptGuidelines }),\n }));\n },\n\n get skills() {\n return skills;\n },\n\n getService<T>(name: string): T | undefined {\n return cordisConnection?.root.get(name) as T | undefined;\n },\n executeTool(name, toolCallId, parameters, signal, onUpdate) {\n const tool = registered.find((entry) => entry.definition.name === name);\n if (tool === undefined) throw new Error(`Unknown Pi extension tool '${name}'`);\n return executePiTool(\n tool,\n requireRunner,\n (candidate) => activeNames.has(candidate),\n toolCallId,\n parameters,\n signal,\n onUpdate,\n );\n },\n\n async load(): Promise<void> {\n if (runner !== undefined) throw new Error('The Pi extension host was loaded more than once');\n if (preload === undefined) return;\n\n const { entries } = await runtime.readEntries();\n // A resumed session has already spent context that no future event will re-announce,\n // so the counter starts from what history measured rather than from zero.\n contextTokens = contextTokensOf(latestAssistantUsage(entries));\n\n const loaded: Extension[] = [...preload.extensions];\n\n sessionManager = await createBridgedSessionManager(runtime, cwd, options.onNotice, entries);\n const manager = sessionManager;\n sessionSync = bridgedSessionSync.get(manager);\n if (sessionSync === undefined) throw new Error('The live Pi session bridge was not initialized');\n\n runner = new ExtensionRunner(loaded, preload.runtime, cwd, manager, new ModelRegistry(models));\n // 'rpc' is the accurate mode for a server with no terminal, and it must be set before any\n // extension runs. Provider registrations were already drained against this same ModelRuntime\n // by preloadPiExtensions, so bindCore finds an empty queue here.\n runner.setUIContext(createHeadlessUiContext(options.client, resolveDefaultTheme(runner)), 'rpc');\n runner.bindCore(actions, contextActions);\n\n registered = loaded.flatMap((extension) => [...extension.tools.values()]);\n tools = registered.map((tool) => toHarnessTool(tool, requireRunner, (name) => activeNames.has(name)));\n activeNames = new Set(tools.map((tool) => tool.name));\n // Subscribe before extension lifecycle handlers run: those handlers may write history,\n // and missing their entry_added acknowledgement would permanently stale this mirror.\n unsubscribeEvents = runtime.onEvent(onHarnessEvent);\n // Pi opens session-scoped extension services before resource discovery. Doom tool\n // restrictions depend on the same ordering.\n await runner.emit({ type: 'session_start', reason: 'startup' });\n if (preload.events !== undefined)\n cordisConnection = await connectDoomCordisHost({ events: preload.events }, 'headless-pi-extension-host');\n skills = await loadPiSkills(runner, cwd, agentDir, options.onNotice);\n },\n\n async shutdown(): Promise<void> {\n unsubscribeEvents?.();\n unsubscribeEvents = undefined;\n sessionSync = undefined;\n sessionManager = undefined;\n runMessages = [];\n toolArguments.clear();\n const connection = cordisConnection;\n cordisConnection = undefined;\n try {\n if (runner !== undefined) await runner.emit({ type: 'session_shutdown', reason: 'quit' });\n } finally {\n await connection?.dispose();\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { readSyncRegistration } from "../../../syncRegistration.mjs";
3
3
  import { contextTokensOf, contextUsageOf, latestAssistantUsage } from "../contextUsage/index.mjs";
4
4
  import { fromPiSessionEntry, toPiFileEntries, toPiSessionEntries, toPiSessionEntry } from "../piSessionEntries/index.mjs";
5
5
  import { readFileSync } from "node:fs";
6
- import { ExtensionRunner, ModelRegistry, SessionManager, createEventBus, discoverAndLoadExtensions, initTheme, loadSkills, sessionEntryToContextMessages } from "@earendil-works/pi-coding-agent";
6
+ import { DefaultPackageManager, ExtensionRunner, ModelRegistry, SessionManager, createEventBus, discoverAndLoadExtensions, initTheme, loadSkills, sessionEntryToContextMessages } from "@earendil-works/pi-coding-agent";
7
7
  import { isDeepStrictEqual } from "node:util";
8
8
  //#region src/services/piExtensionHost/index.ts
9
9
  /**
@@ -22,6 +22,31 @@ function resolvePiExtensionEntries(repoRoot) {
22
22
  }
23
23
  }
24
24
  /**
25
+ * Extension entries of the packages listed under `packages` in Pi's settings.json.
26
+ *
27
+ * Interactive Pi expands that list through its resource loader, which this server does not run,
28
+ * so a provider package such as a local Claude Code bridge would load under `pi` and be missing
29
+ * here. Only `packages` entries are taken: Pi's top-level `extensions` list can name DoomPi
30
+ * itself, and the auto-discovered `extensions/` folders are already loaded by
31
+ * `discoverAndLoadExtensions`. A package that is not installed is skipped, never installed, so
32
+ * starting a session cannot trigger a network install.
33
+ */
34
+ async function resolvePiSettingsPackageEntries(options) {
35
+ try {
36
+ return (await new DefaultPackageManager({
37
+ cwd: options.cwd,
38
+ agentDir: options.agentDir,
39
+ settingsManager: options.settings
40
+ }).resolve(async (source) => {
41
+ options.onNotice?.(`Pi package ${source} is not installed; skipped. Install it with pi to load it here.`);
42
+ return "skip";
43
+ })).extensions.filter((resource) => resource.enabled && resource.metadata.origin === "package").map((resource) => resource.path);
44
+ } catch (error) {
45
+ options.onNotice?.(`Pi settings packages: ${error instanceof Error ? error.message : String(error)}`);
46
+ return [];
47
+ }
48
+ }
49
+ /**
25
50
  * Load Pi extensions and apply their provider registrations to the model runtime.
26
51
  *
27
52
  * `pi.registerProvider` only queues into the extension runtime while extensions load; Pi drains
@@ -760,6 +785,6 @@ function createPiExtensionHost(options) {
760
785
  };
761
786
  }
762
787
  //#endregion
763
- export { createBridgedSessionManager, createPiExtensionHost, preloadPiExtensions, resolvePiExtensionEntries };
788
+ export { createBridgedSessionManager, createPiExtensionHost, preloadPiExtensions, resolvePiExtensionEntries, resolvePiSettingsPackageEntries };
764
789
 
765
790
  //# sourceMappingURL=index.mjs.map