@agimon-ai/doompi-core 0.0.1-alpha.92 → 0.0.1-alpha.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apiContracts.d.mts.map +1 -1
- package/dist/contextDetail.cjs +1 -0
- package/dist/contextDetail.cjs.map +1 -1
- package/dist/contextDetail.mjs +1 -0
- package/dist/contextDetail.mjs.map +1 -1
- package/dist/contextProjection.cjs.map +1 -1
- package/dist/contextProjection.d.mts +2 -0
- package/dist/contextProjection.d.mts.map +1 -1
- package/dist/contextProjection.mjs.map +1 -1
- package/dist/headless.cjs +1 -0
- package/dist/headless.d.mts +2 -2
- package/dist/headless.mjs +2 -2
- package/dist/headlessHub.cjs +37 -3
- package/dist/headlessHub.cjs.map +1 -1
- package/dist/headlessHub.d.mts.map +1 -1
- package/dist/headlessHub.mjs +37 -3
- package/dist/headlessHub.mjs.map +1 -1
- package/dist/headlessServer.cjs +25 -1
- package/dist/headlessServer.cjs.map +1 -1
- package/dist/headlessServer.d.mts.map +1 -1
- package/dist/headlessServer.mjs +25 -1
- package/dist/headlessServer.mjs.map +1 -1
- package/dist/headlessSessionHost.d.mts +3 -0
- package/dist/headlessSessionHost.d.mts.map +1 -1
- package/dist/history.cjs +3 -0
- package/dist/history.d.mts +2 -2
- package/dist/history.mjs +2 -2
- package/dist/hubChannel.d.mts +2 -2
- package/dist/main.cjs +1 -0
- package/dist/main.d.mts +2 -2
- package/dist/main.mjs +2 -2
- package/dist/packageApiServer.cjs +1 -0
- package/dist/packageApiServer.cjs.map +1 -1
- package/dist/packageApiServer.d.mts +1 -0
- package/dist/packageApiServer.d.mts.map +1 -1
- package/dist/packageApiServer.mjs +1 -0
- package/dist/packageApiServer.mjs.map +1 -1
- package/dist/remoteRuntime.cjs +54 -15
- package/dist/remoteRuntime.cjs.map +1 -1
- package/dist/remoteRuntime.d.mts +4 -2
- package/dist/remoteRuntime.d.mts.map +1 -1
- package/dist/remoteRuntime.mjs +41 -2
- package/dist/remoteRuntime.mjs.map +1 -1
- package/dist/src/constants/remote.cjs +9 -0
- package/dist/src/constants/remote.cjs.map +1 -1
- package/dist/src/constants/remote.mjs +8 -1
- package/dist/src/constants/remote.mjs.map +1 -1
- package/dist/src/schemas/headless.cjs +7 -0
- package/dist/src/schemas/headless.cjs.map +1 -1
- package/dist/src/schemas/headless.d.mts +12 -2
- package/dist/src/schemas/headless.d.mts.map +1 -1
- package/dist/src/schemas/headless.mjs +7 -1
- package/dist/src/schemas/headless.mjs.map +1 -1
- package/dist/src/schemas/hubChannel.d.mts +22 -0
- package/dist/src/schemas/hubChannel.d.mts.map +1 -1
- package/dist/src/schemas/packageApi.cjs.map +1 -1
- package/dist/src/schemas/packageApi.d.mts +8 -3
- package/dist/src/schemas/packageApi.d.mts.map +1 -1
- package/dist/src/schemas/packageApi.mjs.map +1 -1
- package/dist/src/server/devProxyForward.cjs +141 -0
- package/dist/src/server/devProxyForward.cjs.map +1 -0
- package/dist/src/server/devProxyForward.mjs +139 -0
- package/dist/src/server/devProxyForward.mjs.map +1 -0
- package/dist/src/server/devProxyRoutes.cjs +97 -0
- package/dist/src/server/devProxyRoutes.cjs.map +1 -0
- package/dist/src/server/devProxyRoutes.mjs +97 -0
- package/dist/src/server/devProxyRoutes.mjs.map +1 -0
- package/dist/src/server/devProxySocket.cjs +87 -0
- package/dist/src/server/devProxySocket.cjs.map +1 -0
- package/dist/src/server/devProxySocket.mjs +87 -0
- package/dist/src/server/devProxySocket.mjs.map +1 -0
- package/dist/src/server/directHarnessRuntime.cjs +39 -15
- package/dist/src/server/directHarnessRuntime.cjs.map +1 -1
- package/dist/src/server/directHarnessRuntime.mjs +39 -16
- package/dist/src/server/directHarnessRuntime.mjs.map +1 -1
- package/dist/src/server/sessionMcpHandler.cjs +36 -41
- package/dist/src/server/sessionMcpHandler.cjs.map +1 -1
- package/dist/src/server/sessionMcpHandler.d.mts +1 -1
- package/dist/src/server/sessionMcpHandler.d.mts.map +1 -1
- package/dist/src/server/sessionMcpHandler.mjs +35 -40
- package/dist/src/server/sessionMcpHandler.mjs.map +1 -1
- package/dist/src/server/sessionMcpRoutes.cjs +21 -1
- package/dist/src/server/sessionMcpRoutes.cjs.map +1 -1
- package/dist/src/server/sessionMcpRoutes.mjs +21 -1
- package/dist/src/server/sessionMcpRoutes.mjs.map +1 -1
- package/dist/src/services/devProxyPolicy/index.cjs +168 -0
- package/dist/src/services/devProxyPolicy/index.cjs.map +1 -0
- package/dist/src/services/devProxyPolicy/index.mjs +165 -0
- package/dist/src/services/devProxyPolicy/index.mjs.map +1 -0
- package/dist/src/services/devProxyStore/index.cjs +107 -0
- package/dist/src/services/devProxyStore/index.cjs.map +1 -0
- package/dist/src/services/devProxyStore/index.mjs +104 -0
- package/dist/src/services/devProxyStore/index.mjs.map +1 -0
- package/dist/src/services/headlessClient/index.cjs +9 -1
- package/dist/src/services/headlessClient/index.cjs.map +1 -1
- package/dist/src/services/headlessClient/index.mjs +9 -1
- package/dist/src/services/headlessClient/index.mjs.map +1 -1
- package/dist/src/services/loopbackAddress/index.cjs +58 -0
- package/dist/src/services/loopbackAddress/index.cjs.map +1 -0
- package/dist/src/services/loopbackAddress/index.mjs +56 -0
- package/dist/src/services/loopbackAddress/index.mjs.map +1 -0
- package/dist/src/services/piExtensionHost/index.cjs +1 -2
- package/dist/src/services/piExtensionHost/index.cjs.map +1 -1
- package/dist/src/services/piExtensionHost/index.mjs +1 -2
- package/dist/src/services/piExtensionHost/index.mjs.map +1 -1
- package/dist/src/services/sessionMcpConversations/index.cjs +17 -1
- package/dist/src/services/sessionMcpConversations/index.cjs.map +1 -1
- package/dist/src/services/sessionMcpConversations/index.mjs +17 -1
- package/dist/src/services/sessionMcpConversations/index.mjs.map +1 -1
- package/dist/src/services/workspaceRegistry/index.cjs +60 -1
- package/dist/src/services/workspaceRegistry/index.cjs.map +1 -1
- package/dist/src/services/workspaceRegistry/index.d.mts +27 -0
- package/dist/src/services/workspaceRegistry/index.d.mts.map +1 -1
- package/dist/src/services/workspaceRegistry/index.mjs +57 -2
- package/dist/src/services/workspaceRegistry/index.mjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessHost.cjs +29 -1
- package/dist/src/systems/main/adapters/headlessHost.cjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessHost.d.mts +6 -0
- package/dist/src/systems/main/adapters/headlessHost.d.mts.map +1 -1
- package/dist/src/systems/main/adapters/headlessHost.mjs +29 -2
- package/dist/src/systems/main/adapters/headlessHost.mjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessSessionHost.cjs +128 -88
- package/dist/src/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessSessionHost.d.mts.map +1 -1
- package/dist/src/systems/main/adapters/headlessSessionHost.mjs +129 -89
- package/dist/src/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
- package/dist/src/types/server/directHarnessRuntime.d.mts +5 -0
- package/dist/src/types/server/directHarnessRuntime.d.mts.map +1 -1
- package/dist/src/web/services/sessionRegistry/index.cjs +0 -37
- package/dist/src/web/services/sessionRegistry/index.cjs.map +1 -1
- package/dist/src/web/services/sessionRegistry/index.d.mts +1 -14
- package/dist/src/web/services/sessionRegistry/index.d.mts.map +1 -1
- package/dist/src/web/services/sessionRegistry/index.mjs +1 -35
- package/dist/src/web/services/sessionRegistry/index.mjs.map +1 -1
- package/dist/src/web/types/mcpWidget.d.mts +1 -1
- package/dist/src/web/types/sessionRegistry.d.mts +0 -16
- package/dist/src/web/types/sessionRegistry.d.mts.map +1 -1
- package/dist/src/web/types/webPlugin.d.mts +13 -0
- package/dist/src/web/types/webPlugin.d.mts.map +1 -1
- package/dist/syncLocation.cjs +2 -0
- package/dist/syncLocation.cjs.map +1 -1
- package/dist/syncLocation.d.mts +2 -0
- package/dist/syncLocation.d.mts.map +1 -1
- package/dist/syncLocation.mjs +2 -1
- package/dist/syncLocation.mjs.map +1 -1
- package/dist/typesContextApi.d.mts +2 -0
- package/dist/typesContextApi.d.mts.map +1 -1
- package/dist/web.cjs +0 -3
- package/dist/web.d.mts +4 -4
- package/dist/web.mjs +2 -2
- package/package.json +6 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/packageApi.ts","../src/schemas/serverBundle.ts","../src/schemas/apiContracts.ts","../src/services/apiContracts/index.ts","../src/schemas/builtinApiContracts.ts","../src/schemas/httpApiContracts.ts","../src/schemas/sessionProtocol.ts","../src/schemas/sessionApiContracts.ts","../src/schemas/transportApiContracts.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/packageApi.ts","../src/schemas/serverBundle.ts","../src/schemas/apiContracts.ts","../src/services/apiContracts/index.ts","../src/schemas/builtinApiContracts.ts","../src/schemas/httpApiContracts.ts","../src/schemas/sessionProtocol.ts","../src/schemas/sessionApiContracts.ts","../src/schemas/transportApiContracts.ts"],"mappings":"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"}
|
package/dist/contextDetail.cjs
CHANGED
|
@@ -47,6 +47,7 @@ function buildContextDetail(input) {
|
|
|
47
47
|
tokens: skill.promptTokens ?? 0,
|
|
48
48
|
description: skill.description,
|
|
49
49
|
...skill.filePath === void 0 ? {} : { filePath: skill.filePath },
|
|
50
|
+
...skill.body === void 0 ? {} : { body: skill.body },
|
|
50
51
|
modelInvocable: skill.modelInvocable
|
|
51
52
|
});
|
|
52
53
|
if (input.systemPrompt !== void 0) items.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextDetail.cjs","names":["tokensForTool"],"sources":["../src/services/contextDetail/index.ts"],"sourcesContent":["import type { ContextItemDetail, ContextPromptStage, ContextToolDetail } from '../../types/contextApi';\nimport type { ContextItemSource, ContextSkillInventory } from '../contextProjection';\nimport { type CountTokens, type ToolSource, tokensForTool } from '../toolInventory';\n\n/**\n * The prose and schema behind the projection's figures.\n *\n * Built from the same inventory in the same pass, so a detail can never\n * describe a composition the panel is not showing. Nothing is grouped here:\n * the panel already knows which mode a row sits under, and a lookup by name is\n * all a click needs.\n */\n\nconst MCP_KEY_PREFIX = 'mcp:';\nconst CORE_OWNER = 'pi';\n\n/** The same join the projection makes, so a row and its detail agree on the owner. */\nfunction ownerOf(source: ToolSource): string {\n if (source.kind === 'extension') return source.packageName ?? source.key;\n if (source.kind === 'mcp') return source.key.slice(MCP_KEY_PREFIX.length);\n return CORE_OWNER;\n}\n\nfunction skillSource(group: ContextSkillInventory['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nexport interface ContextDetailInput {\n readonly sources: readonly ToolSource[];\n readonly skills: readonly ContextSkillInventory[];\n readonly countTokens: CountTokens;\n /** The assembled prompt, when the host has one to report. */\n readonly systemPrompt?: { readonly text: string; readonly tokens: number; readonly stage: ContextPromptStage };\n}\n\nexport function buildContextDetail(input: ContextDetailInput): ContextItemDetail[] {\n const items: ContextItemDetail[] = [];\n\n for (const source of input.sources) {\n const owner = ownerOf(source);\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const detail: ContextToolDetail = {\n itemKind: 'tool',\n name: tool.name,\n owner,\n source: source.kind === 'core' ? 'core' : source.kind,\n active: tool.active,\n tokens: { schemaTokens: cost.schemaTokens, promptTokens: cost.promptTokens, totalTokens: cost.totalTokens },\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {}),\n ...(tool.promptGuidelines?.length ? { promptGuidelines: tool.promptGuidelines } : {}),\n ...(tool.parameters === undefined ? {} : { parameters: tool.parameters }),\n };\n items.push(detail);\n }\n }\n\n for (const skill of input.skills) {\n items.push({\n itemKind: 'skill',\n name: skill.name,\n owner: skill.owner,\n source: skillSource(skill.group),\n active: true,\n tokens: skill.promptTokens ?? 0,\n description: skill.description,\n ...(skill.filePath === undefined ? {} : { filePath: skill.filePath }),\n modelInvocable: skill.modelInvocable,\n });\n }\n\n if (input.systemPrompt !== undefined) {\n items.push({\n itemKind: 'prompt',\n name: 'system',\n tokens: input.systemPrompt.tokens,\n stage: input.systemPrompt.stage,\n text: input.systemPrompt.text,\n });\n }\n\n return items;\n}\n"],"mappings":";;;AAcA,MAAM,aAAa;;AAGnB,SAAS,QAAQ,QAA4B;CAC3C,IAAI,OAAO,SAAS,aAAa,OAAO,OAAO,eAAe,OAAO;CACrE,IAAI,OAAO,SAAS,OAAO,OAAO,OAAO,IAAI,MAAM,CAAqB;CACxE,OAAO;AACT;AAEA,SAAS,YAAY,OAA0D;CAC7E,IAAI,UAAU,WAAW,OAAO;CAChC,IAAI,UAAU,cAAc,OAAO;CACnC,OAAO;AACT;AAUA,SAAgB,mBAAmB,OAAgD;CACjF,MAAM,QAA6B,CAAC;CAEpC,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,QAAQ,QAAQ,MAAM;EAC5B,KAAK,MAAM,QAAQ,OAAO,OAAO;GAC/B,MAAM,OAAOA,cAAAA,cAAc,MAAM,MAAM,WAAW;GAClD,MAAM,SAA4B;IAChC,UAAU;IACV,MAAM,KAAK;IACX;IACA,QAAQ,OAAO,SAAS,SAAS,SAAS,OAAO;IACjD,QAAQ,KAAK;IACb,QAAQ;KAAE,cAAc,KAAK;KAAc,cAAc,KAAK;KAAc,aAAa,KAAK;IAAY;IAC1G,GAAI,KAAK,cAAc,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;IAC5D,GAAI,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,IAAI,CAAC;IAClE,GAAI,KAAK,kBAAkB,SAAS,EAAE,kBAAkB,KAAK,iBAAiB,IAAI,CAAC;IACnF,GAAI,KAAK,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,KAAK,WAAW;GACzE;GACA,MAAM,KAAK,MAAM;EACnB;CACF;CAEA,KAAK,MAAM,SAAS,MAAM,QACxB,MAAM,KAAK;EACT,UAAU;EACV,MAAM,MAAM;EACZ,OAAO,MAAM;EACb,QAAQ,YAAY,MAAM,KAAK;EAC/B,QAAQ;EACR,QAAQ,MAAM,gBAAgB;EAC9B,aAAa,MAAM;EACnB,GAAI,MAAM,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;EACnE,gBAAgB,MAAM;CACxB,CAAC;CAGH,IAAI,MAAM,iBAAiB,KAAA,GACzB,MAAM,KAAK;EACT,UAAU;EACV,MAAM;EACN,QAAQ,MAAM,aAAa;EAC3B,OAAO,MAAM,aAAa;EAC1B,MAAM,MAAM,aAAa;CAC3B,CAAC;CAGH,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"contextDetail.cjs","names":["tokensForTool"],"sources":["../src/services/contextDetail/index.ts"],"sourcesContent":["import type { ContextItemDetail, ContextPromptStage, ContextToolDetail } from '../../types/contextApi';\nimport type { ContextItemSource, ContextSkillInventory } from '../contextProjection';\nimport { type CountTokens, type ToolSource, tokensForTool } from '../toolInventory';\n\n/**\n * The prose and schema behind the projection's figures.\n *\n * Built from the same inventory in the same pass, so a detail can never\n * describe a composition the panel is not showing. Nothing is grouped here:\n * the panel already knows which mode a row sits under, and a lookup by name is\n * all a click needs.\n */\n\nconst MCP_KEY_PREFIX = 'mcp:';\nconst CORE_OWNER = 'pi';\n\n/** The same join the projection makes, so a row and its detail agree on the owner. */\nfunction ownerOf(source: ToolSource): string {\n if (source.kind === 'extension') return source.packageName ?? source.key;\n if (source.kind === 'mcp') return source.key.slice(MCP_KEY_PREFIX.length);\n return CORE_OWNER;\n}\n\nfunction skillSource(group: ContextSkillInventory['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nexport interface ContextDetailInput {\n readonly sources: readonly ToolSource[];\n readonly skills: readonly ContextSkillInventory[];\n readonly countTokens: CountTokens;\n /** The assembled prompt, when the host has one to report. */\n readonly systemPrompt?: { readonly text: string; readonly tokens: number; readonly stage: ContextPromptStage };\n}\n\nexport function buildContextDetail(input: ContextDetailInput): ContextItemDetail[] {\n const items: ContextItemDetail[] = [];\n\n for (const source of input.sources) {\n const owner = ownerOf(source);\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const detail: ContextToolDetail = {\n itemKind: 'tool',\n name: tool.name,\n owner,\n source: source.kind === 'core' ? 'core' : source.kind,\n active: tool.active,\n tokens: { schemaTokens: cost.schemaTokens, promptTokens: cost.promptTokens, totalTokens: cost.totalTokens },\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {}),\n ...(tool.promptGuidelines?.length ? { promptGuidelines: tool.promptGuidelines } : {}),\n ...(tool.parameters === undefined ? {} : { parameters: tool.parameters }),\n };\n items.push(detail);\n }\n }\n\n for (const skill of input.skills) {\n items.push({\n itemKind: 'skill',\n name: skill.name,\n owner: skill.owner,\n source: skillSource(skill.group),\n active: true,\n tokens: skill.promptTokens ?? 0,\n description: skill.description,\n ...(skill.filePath === undefined ? {} : { filePath: skill.filePath }),\n ...(skill.body === undefined ? {} : { body: skill.body }),\n modelInvocable: skill.modelInvocable,\n });\n }\n\n if (input.systemPrompt !== undefined) {\n items.push({\n itemKind: 'prompt',\n name: 'system',\n tokens: input.systemPrompt.tokens,\n stage: input.systemPrompt.stage,\n text: input.systemPrompt.text,\n });\n }\n\n return items;\n}\n"],"mappings":";;;AAcA,MAAM,aAAa;;AAGnB,SAAS,QAAQ,QAA4B;CAC3C,IAAI,OAAO,SAAS,aAAa,OAAO,OAAO,eAAe,OAAO;CACrE,IAAI,OAAO,SAAS,OAAO,OAAO,OAAO,IAAI,MAAM,CAAqB;CACxE,OAAO;AACT;AAEA,SAAS,YAAY,OAA0D;CAC7E,IAAI,UAAU,WAAW,OAAO;CAChC,IAAI,UAAU,cAAc,OAAO;CACnC,OAAO;AACT;AAUA,SAAgB,mBAAmB,OAAgD;CACjF,MAAM,QAA6B,CAAC;CAEpC,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,QAAQ,QAAQ,MAAM;EAC5B,KAAK,MAAM,QAAQ,OAAO,OAAO;GAC/B,MAAM,OAAOA,cAAAA,cAAc,MAAM,MAAM,WAAW;GAClD,MAAM,SAA4B;IAChC,UAAU;IACV,MAAM,KAAK;IACX;IACA,QAAQ,OAAO,SAAS,SAAS,SAAS,OAAO;IACjD,QAAQ,KAAK;IACb,QAAQ;KAAE,cAAc,KAAK;KAAc,cAAc,KAAK;KAAc,aAAa,KAAK;IAAY;IAC1G,GAAI,KAAK,cAAc,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;IAC5D,GAAI,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,IAAI,CAAC;IAClE,GAAI,KAAK,kBAAkB,SAAS,EAAE,kBAAkB,KAAK,iBAAiB,IAAI,CAAC;IACnF,GAAI,KAAK,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,KAAK,WAAW;GACzE;GACA,MAAM,KAAK,MAAM;EACnB;CACF;CAEA,KAAK,MAAM,SAAS,MAAM,QACxB,MAAM,KAAK;EACT,UAAU;EACV,MAAM,MAAM;EACZ,OAAO,MAAM;EACb,QAAQ,YAAY,MAAM,KAAK;EAC/B,QAAQ;EACR,QAAQ,MAAM,gBAAgB;EAC9B,aAAa,MAAM;EACnB,GAAI,MAAM,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;EACnE,GAAI,MAAM,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;EACvD,gBAAgB,MAAM;CACxB,CAAC;CAGH,IAAI,MAAM,iBAAiB,KAAA,GACzB,MAAM,KAAK;EACT,UAAU;EACV,MAAM;EACN,QAAQ,MAAM,aAAa;EAC3B,OAAO,MAAM,aAAa;EAC1B,MAAM,MAAM,aAAa;CAC3B,CAAC;CAGH,OAAO;AACT"}
|
package/dist/contextDetail.mjs
CHANGED
|
@@ -46,6 +46,7 @@ function buildContextDetail(input) {
|
|
|
46
46
|
tokens: skill.promptTokens ?? 0,
|
|
47
47
|
description: skill.description,
|
|
48
48
|
...skill.filePath === void 0 ? {} : { filePath: skill.filePath },
|
|
49
|
+
...skill.body === void 0 ? {} : { body: skill.body },
|
|
49
50
|
modelInvocable: skill.modelInvocable
|
|
50
51
|
});
|
|
51
52
|
if (input.systemPrompt !== void 0) items.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextDetail.mjs","names":[],"sources":["../src/services/contextDetail/index.ts"],"sourcesContent":["import type { ContextItemDetail, ContextPromptStage, ContextToolDetail } from '../../types/contextApi';\nimport type { ContextItemSource, ContextSkillInventory } from '../contextProjection';\nimport { type CountTokens, type ToolSource, tokensForTool } from '../toolInventory';\n\n/**\n * The prose and schema behind the projection's figures.\n *\n * Built from the same inventory in the same pass, so a detail can never\n * describe a composition the panel is not showing. Nothing is grouped here:\n * the panel already knows which mode a row sits under, and a lookup by name is\n * all a click needs.\n */\n\nconst MCP_KEY_PREFIX = 'mcp:';\nconst CORE_OWNER = 'pi';\n\n/** The same join the projection makes, so a row and its detail agree on the owner. */\nfunction ownerOf(source: ToolSource): string {\n if (source.kind === 'extension') return source.packageName ?? source.key;\n if (source.kind === 'mcp') return source.key.slice(MCP_KEY_PREFIX.length);\n return CORE_OWNER;\n}\n\nfunction skillSource(group: ContextSkillInventory['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nexport interface ContextDetailInput {\n readonly sources: readonly ToolSource[];\n readonly skills: readonly ContextSkillInventory[];\n readonly countTokens: CountTokens;\n /** The assembled prompt, when the host has one to report. */\n readonly systemPrompt?: { readonly text: string; readonly tokens: number; readonly stage: ContextPromptStage };\n}\n\nexport function buildContextDetail(input: ContextDetailInput): ContextItemDetail[] {\n const items: ContextItemDetail[] = [];\n\n for (const source of input.sources) {\n const owner = ownerOf(source);\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const detail: ContextToolDetail = {\n itemKind: 'tool',\n name: tool.name,\n owner,\n source: source.kind === 'core' ? 'core' : source.kind,\n active: tool.active,\n tokens: { schemaTokens: cost.schemaTokens, promptTokens: cost.promptTokens, totalTokens: cost.totalTokens },\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {}),\n ...(tool.promptGuidelines?.length ? { promptGuidelines: tool.promptGuidelines } : {}),\n ...(tool.parameters === undefined ? {} : { parameters: tool.parameters }),\n };\n items.push(detail);\n }\n }\n\n for (const skill of input.skills) {\n items.push({\n itemKind: 'skill',\n name: skill.name,\n owner: skill.owner,\n source: skillSource(skill.group),\n active: true,\n tokens: skill.promptTokens ?? 0,\n description: skill.description,\n ...(skill.filePath === undefined ? {} : { filePath: skill.filePath }),\n modelInvocable: skill.modelInvocable,\n });\n }\n\n if (input.systemPrompt !== undefined) {\n items.push({\n itemKind: 'prompt',\n name: 'system',\n tokens: input.systemPrompt.tokens,\n stage: input.systemPrompt.stage,\n text: input.systemPrompt.text,\n });\n }\n\n return items;\n}\n"],"mappings":";;AAcA,MAAM,aAAa;;AAGnB,SAAS,QAAQ,QAA4B;CAC3C,IAAI,OAAO,SAAS,aAAa,OAAO,OAAO,eAAe,OAAO;CACrE,IAAI,OAAO,SAAS,OAAO,OAAO,OAAO,IAAI,MAAM,CAAqB;CACxE,OAAO;AACT;AAEA,SAAS,YAAY,OAA0D;CAC7E,IAAI,UAAU,WAAW,OAAO;CAChC,IAAI,UAAU,cAAc,OAAO;CACnC,OAAO;AACT;AAUA,SAAgB,mBAAmB,OAAgD;CACjF,MAAM,QAA6B,CAAC;CAEpC,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,QAAQ,QAAQ,MAAM;EAC5B,KAAK,MAAM,QAAQ,OAAO,OAAO;GAC/B,MAAM,OAAO,cAAc,MAAM,MAAM,WAAW;GAClD,MAAM,SAA4B;IAChC,UAAU;IACV,MAAM,KAAK;IACX;IACA,QAAQ,OAAO,SAAS,SAAS,SAAS,OAAO;IACjD,QAAQ,KAAK;IACb,QAAQ;KAAE,cAAc,KAAK;KAAc,cAAc,KAAK;KAAc,aAAa,KAAK;IAAY;IAC1G,GAAI,KAAK,cAAc,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;IAC5D,GAAI,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,IAAI,CAAC;IAClE,GAAI,KAAK,kBAAkB,SAAS,EAAE,kBAAkB,KAAK,iBAAiB,IAAI,CAAC;IACnF,GAAI,KAAK,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,KAAK,WAAW;GACzE;GACA,MAAM,KAAK,MAAM;EACnB;CACF;CAEA,KAAK,MAAM,SAAS,MAAM,QACxB,MAAM,KAAK;EACT,UAAU;EACV,MAAM,MAAM;EACZ,OAAO,MAAM;EACb,QAAQ,YAAY,MAAM,KAAK;EAC/B,QAAQ;EACR,QAAQ,MAAM,gBAAgB;EAC9B,aAAa,MAAM;EACnB,GAAI,MAAM,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;EACnE,gBAAgB,MAAM;CACxB,CAAC;CAGH,IAAI,MAAM,iBAAiB,KAAA,GACzB,MAAM,KAAK;EACT,UAAU;EACV,MAAM;EACN,QAAQ,MAAM,aAAa;EAC3B,OAAO,MAAM,aAAa;EAC1B,MAAM,MAAM,aAAa;CAC3B,CAAC;CAGH,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"contextDetail.mjs","names":[],"sources":["../src/services/contextDetail/index.ts"],"sourcesContent":["import type { ContextItemDetail, ContextPromptStage, ContextToolDetail } from '../../types/contextApi';\nimport type { ContextItemSource, ContextSkillInventory } from '../contextProjection';\nimport { type CountTokens, type ToolSource, tokensForTool } from '../toolInventory';\n\n/**\n * The prose and schema behind the projection's figures.\n *\n * Built from the same inventory in the same pass, so a detail can never\n * describe a composition the panel is not showing. Nothing is grouped here:\n * the panel already knows which mode a row sits under, and a lookup by name is\n * all a click needs.\n */\n\nconst MCP_KEY_PREFIX = 'mcp:';\nconst CORE_OWNER = 'pi';\n\n/** The same join the projection makes, so a row and its detail agree on the owner. */\nfunction ownerOf(source: ToolSource): string {\n if (source.kind === 'extension') return source.packageName ?? source.key;\n if (source.kind === 'mcp') return source.key.slice(MCP_KEY_PREFIX.length);\n return CORE_OWNER;\n}\n\nfunction skillSource(group: ContextSkillInventory['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nexport interface ContextDetailInput {\n readonly sources: readonly ToolSource[];\n readonly skills: readonly ContextSkillInventory[];\n readonly countTokens: CountTokens;\n /** The assembled prompt, when the host has one to report. */\n readonly systemPrompt?: { readonly text: string; readonly tokens: number; readonly stage: ContextPromptStage };\n}\n\nexport function buildContextDetail(input: ContextDetailInput): ContextItemDetail[] {\n const items: ContextItemDetail[] = [];\n\n for (const source of input.sources) {\n const owner = ownerOf(source);\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const detail: ContextToolDetail = {\n itemKind: 'tool',\n name: tool.name,\n owner,\n source: source.kind === 'core' ? 'core' : source.kind,\n active: tool.active,\n tokens: { schemaTokens: cost.schemaTokens, promptTokens: cost.promptTokens, totalTokens: cost.totalTokens },\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {}),\n ...(tool.promptGuidelines?.length ? { promptGuidelines: tool.promptGuidelines } : {}),\n ...(tool.parameters === undefined ? {} : { parameters: tool.parameters }),\n };\n items.push(detail);\n }\n }\n\n for (const skill of input.skills) {\n items.push({\n itemKind: 'skill',\n name: skill.name,\n owner: skill.owner,\n source: skillSource(skill.group),\n active: true,\n tokens: skill.promptTokens ?? 0,\n description: skill.description,\n ...(skill.filePath === undefined ? {} : { filePath: skill.filePath }),\n ...(skill.body === undefined ? {} : { body: skill.body }),\n modelInvocable: skill.modelInvocable,\n });\n }\n\n if (input.systemPrompt !== undefined) {\n items.push({\n itemKind: 'prompt',\n name: 'system',\n tokens: input.systemPrompt.tokens,\n stage: input.systemPrompt.stage,\n text: input.systemPrompt.text,\n });\n }\n\n return items;\n}\n"],"mappings":";;AAcA,MAAM,aAAa;;AAGnB,SAAS,QAAQ,QAA4B;CAC3C,IAAI,OAAO,SAAS,aAAa,OAAO,OAAO,eAAe,OAAO;CACrE,IAAI,OAAO,SAAS,OAAO,OAAO,OAAO,IAAI,MAAM,CAAqB;CACxE,OAAO;AACT;AAEA,SAAS,YAAY,OAA0D;CAC7E,IAAI,UAAU,WAAW,OAAO;CAChC,IAAI,UAAU,cAAc,OAAO;CACnC,OAAO;AACT;AAUA,SAAgB,mBAAmB,OAAgD;CACjF,MAAM,QAA6B,CAAC;CAEpC,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,QAAQ,QAAQ,MAAM;EAC5B,KAAK,MAAM,QAAQ,OAAO,OAAO;GAC/B,MAAM,OAAO,cAAc,MAAM,MAAM,WAAW;GAClD,MAAM,SAA4B;IAChC,UAAU;IACV,MAAM,KAAK;IACX;IACA,QAAQ,OAAO,SAAS,SAAS,SAAS,OAAO;IACjD,QAAQ,KAAK;IACb,QAAQ;KAAE,cAAc,KAAK;KAAc,cAAc,KAAK;KAAc,aAAa,KAAK;IAAY;IAC1G,GAAI,KAAK,cAAc,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;IAC5D,GAAI,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,IAAI,CAAC;IAClE,GAAI,KAAK,kBAAkB,SAAS,EAAE,kBAAkB,KAAK,iBAAiB,IAAI,CAAC;IACnF,GAAI,KAAK,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,KAAK,WAAW;GACzE;GACA,MAAM,KAAK,MAAM;EACnB;CACF;CAEA,KAAK,MAAM,SAAS,MAAM,QACxB,MAAM,KAAK;EACT,UAAU;EACV,MAAM,MAAM;EACZ,OAAO,MAAM;EACb,QAAQ,YAAY,MAAM,KAAK;EAC/B,QAAQ;EACR,QAAQ,MAAM,gBAAgB;EAC9B,aAAa,MAAM;EACnB,GAAI,MAAM,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;EACnE,GAAI,MAAM,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;EACvD,gBAAgB,MAAM;CACxB,CAAC;CAGH,IAAI,MAAM,iBAAiB,KAAA,GACzB,MAAM,KAAK;EACT,UAAU;EACV,MAAM;EACN,QAAQ,MAAM,aAAa;EAC3B,OAAO,MAAM,aAAa;EAC1B,MAAM,MAAM,aAAa;CAC3B,CAAC;CAGH,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextProjection.cjs","names":["tokensForTool"],"sources":["../src/services/contextProjection/index.ts"],"sourcesContent":["/** What admitted a package into the composition. */\nexport interface PackageAttribution {\n /** `major` for a layer package, `domain` for a plugin a domain carries. */\n kind: 'major' | 'domain';\n /** The major mode name, or the domain name. */\n mode: string;\n /** The layer the package was listed under; absent for a domain plugin. */\n layer?: string;\n}\n\nimport type { ContextPromptStage } from '../../types/contextApi';\nimport { type CountTokens, type ToolEntry, type ToolSource, tokensForTool } from '../toolInventory';\n\n/**\n * What the session is carrying, and what carrying it costs.\n *\n * Grouped by the mode that admitted each package rather than by where the tool\n * came from, because a mode is the thing a reader can switch off. The mechanism\n * (extension, MCP server, plugin) orders rows inside a group instead, so the\n * expensive thing is still easy to find.\n *\n * Schemas never travel. Only names, kinds, owners and integer counts do, which\n * keeps the payload small enough to republish whenever the composition changes.\n */\nexport const CONTEXT_PROJECTION_VERSION = 1;\n\n/** Journal entry the cockpit reads; mirrored by CONTEXT_ENTRY_TYPE in doompi-web. */\nexport const DOOM_CONTEXT_ENTRY_TYPE = 'doom-context';\n\n/** The tokenizer these counts came from, so a reader can judge them. */\nexport const CONTEXT_PROJECTION_ESTIMATOR = 'gpt-tokenizer';\n\nexport type ContextGroupKind = 'major' | 'minor' | 'domain' | 'core';\nexport type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';\n\n/** Everything the runtime could not attribute lands here rather than vanishing. */\nconst CORE_GROUP = 'core';\n/** Pi's own tools have no package; the runtime itself is the owner. */\nconst CORE_OWNER = 'pi';\nconst MCP_KEY_PREFIX = 'mcp:';\n\nconst KIND_ORDER: Record<ContextGroupKind, number> = { major: 0, minor: 1, domain: 2, core: 3 };\nconst SOURCE_ORDER: Record<ContextItemSource, number> = { extension: 0, mcp: 1, plugin: 2, core: 3 };\n\nexport interface ContextItemProjection {\n readonly name: string;\n readonly itemKind: 'tool' | 'skill';\n readonly source: ContextItemSource;\n /** The package, server, or plugin that registered it. */\n readonly owner: string;\n readonly tokens: number;\n readonly active: boolean;\n}\n\nexport interface ContextGroupProjection {\n readonly id: string;\n readonly label: string;\n readonly kind: ContextGroupKind;\n readonly items: readonly ContextItemProjection[];\n /** What the group costs now: active items only. */\n readonly tokens: number;\n /** What its gated items would add if switched on. */\n readonly inactiveTokens: number;\n}\n\n/**\n * What the assembled system prompt costs, and how complete the figure is.\n *\n * Kept out of `groups` on purpose. Every tool's prose and every skill's listing\n * already count inside the group subtotals, and those are slices of this same\n * text, so folding it into `totalTokens` would bill them twice.\n */\nexport interface ContextSystemPromptProjection {\n readonly tokens: number;\n readonly stage: ContextPromptStage;\n}\n\nexport interface ContextProjection {\n readonly version: typeof CONTEXT_PROJECTION_VERSION;\n readonly revision: number;\n /** Selection snapshot used to build this projection, replayable after reconnect. */\n readonly selection: {\n readonly majorMode: string;\n readonly domains: readonly string[];\n readonly profile?: string;\n };\n readonly groups: readonly ContextGroupProjection[];\n readonly totalTokens: number;\n readonly inactiveTokens: number;\n readonly systemPrompt?: ContextSystemPromptProjection;\n readonly estimator: typeof CONTEXT_PROJECTION_ESTIMATOR;\n}\n\nexport interface ContextConditionalAttribution {\n readonly kind: 'minor' | 'domain';\n readonly mode: string;\n readonly label?: string;\n}\n\nexport interface ContextToolInventory extends ToolEntry {\n /** A contribution-level gate is more specific than its package's major-mode owner. */\n readonly contextAttribution?: ContextConditionalAttribution;\n}\n\nexport interface ContextToolSource extends Omit<ToolSource, 'tools'> {\n readonly tools: readonly ContextToolInventory[];\n}\n\n/** Skill fields needed to project cost and serve detail. Virtual resources may not have a filesystem path. */\nexport interface ContextSkillInventory {\n readonly name: string;\n readonly description: string;\n readonly group: 'extensions' | 'help' | 'plugins' | 'default';\n readonly owner: string;\n readonly modelInvocable: boolean;\n readonly promptTokens?: number;\n readonly filePath?: string;\n readonly contextAttribution?: ContextConditionalAttribution;\n}\n\nexport interface ContextProjectionInput {\n readonly revision: number;\n readonly majorMode: string;\n readonly profile?: string;\n /** Additional groups supplied by the composition owner, in display order. */\n readonly groups: readonly { readonly id: string; readonly label: string; readonly kind: ContextGroupKind }[];\n /** Domains the composition resolved under, whether or not they carried a plugin. */\n readonly domains: readonly string[];\n readonly sources: readonly ContextToolSource[];\n readonly skills: readonly ContextSkillInventory[];\n /**\n * Owner identifier to the mode that admitted it.\n *\n * Keys are package names for extensions and layer packages, and plugin or\n * server names for anything a domain carries. The caller owns this join\n * because only it knows how a given deployment resolved its plugins.\n */\n readonly attribution: Readonly<Record<string, PackageAttribution>>;\n readonly countTokens: CountTokens;\n /** Absent until the session has a prompt to report. */\n readonly systemPrompt?: ContextSystemPromptProjection;\n}\n\ninterface Bucket {\n kind: ContextGroupKind;\n label: string;\n items: ContextItemProjection[];\n}\n\nfunction skillSource(group: ContextSkillInventory['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nfunction byRow(left: ContextItemProjection, right: ContextItemProjection): number {\n return SOURCE_ORDER[left.source] - SOURCE_ORDER[right.source] || left.name.localeCompare(right.name);\n}\n\nexport function projectContext(input: ContextProjectionInput): ContextProjection {\n const buckets = new Map<string, Bucket>();\n\n // Declared before anything is placed, so a mode with nothing attributed to it\n // still appears. An empty group is a fact worth showing: it says the mode is\n // on and cheap, not that the runtime forgot about it.\n const declare = (id: string, label: string, kind: ContextGroupKind): void => {\n if (!buckets.has(id)) buckets.set(id, { kind, label, items: [] });\n };\n if (input.majorMode) declare(input.majorMode, input.majorMode, 'major');\n for (const group of input.groups) declare(group.id, group.label, group.kind);\n // A domain that contributed no plugin is still part of what the session is\n // running under, and a reader comparing the panel against the footer would\n // otherwise read its absence as the panel being out of date.\n for (const domain of input.domains) declare(domain, domain, 'domain');\n\n const place = (owner: string, item: ContextItemProjection, conditional?: ContextConditionalAttribution): void => {\n const attributed = conditional ?? input.attribution[owner];\n const id = attributed?.mode ?? CORE_GROUP;\n const kind = attributed?.kind ?? 'core';\n declare(id, attributed && 'label' in attributed ? (attributed.label ?? id) : id, kind);\n buckets.get(id)?.items.push(item);\n };\n\n for (const source of input.sources) {\n // An extension is owned by its package, an MCP tool by the server that\n // advertised it. `label` is display text like `scaffold · mcp`, so the\n // identifier comes from the key instead: an owner is joined on, not read.\n const owner =\n source.kind === 'extension'\n ? (source.packageName ?? source.key)\n : source.kind === 'mcp'\n ? source.key.slice(MCP_KEY_PREFIX.length)\n : CORE_OWNER;\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const item: ContextItemProjection = {\n name: tool.name,\n itemKind: 'tool',\n source: source.kind === 'core' ? 'core' : source.kind,\n owner,\n tokens: cost.totalTokens,\n active: tool.active,\n };\n if (source.kind === 'core') {\n declare(CORE_GROUP, CORE_GROUP, 'core');\n buckets.get(CORE_GROUP)?.items.push(item);\n } else {\n place(owner, item, tool.contextAttribution);\n }\n }\n }\n\n for (const skill of input.skills) {\n place(\n skill.owner,\n {\n name: skill.name,\n itemKind: 'skill',\n source: skillSource(skill.group),\n owner: skill.owner,\n tokens: skill.promptTokens ?? 0,\n // A skill is always offered to the model; there is no inactive state for\n // it the way there is for a tool Pi has filtered out.\n active: true,\n },\n skill.contextAttribution,\n );\n }\n\n const groups: ContextGroupProjection[] = [...buckets.entries()]\n .map(([id, bucket]) => {\n const items = [...bucket.items].sort(byRow);\n // Only what is actually sent counts. Pi builds the prompt from its\n // active tools, so a gated one costs nothing until it is switched on,\n // and folding it into the total would overstate the bill by a third.\n return {\n id,\n label: bucket.label,\n kind: bucket.kind,\n items,\n tokens: items.reduce((total, item) => total + (item.active ? item.tokens : 0), 0),\n inactiveTokens: items.reduce((total, item) => total + (item.active ? 0 : item.tokens), 0),\n };\n })\n .sort((left, right) => KIND_ORDER[left.kind] - KIND_ORDER[right.kind] || left.label.localeCompare(right.label));\n\n return {\n version: CONTEXT_PROJECTION_VERSION,\n revision: input.revision,\n selection: {\n majorMode: input.majorMode,\n domains: [...input.domains],\n ...(input.profile === undefined ? {} : { profile: input.profile }),\n },\n groups,\n totalTokens: groups.reduce((total, group) => total + group.tokens, 0),\n inactiveTokens: groups.reduce((total, group) => total + group.inactiveTokens, 0),\n ...(input.systemPrompt === undefined ? {} : { systemPrompt: input.systemPrompt }),\n estimator: CONTEXT_PROJECTION_ESTIMATOR,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"contextProjection.cjs","names":["tokensForTool"],"sources":["../src/services/contextProjection/index.ts"],"sourcesContent":["/** What admitted a package into the composition. */\nexport interface PackageAttribution {\n /** `major` for a layer package, `domain` for a plugin a domain carries. */\n kind: 'major' | 'domain';\n /** The major mode name, or the domain name. */\n mode: string;\n /** The layer the package was listed under; absent for a domain plugin. */\n layer?: string;\n}\n\nimport type { ContextPromptStage } from '../../types/contextApi';\nimport { type CountTokens, type ToolEntry, type ToolSource, tokensForTool } from '../toolInventory';\n\n/**\n * What the session is carrying, and what carrying it costs.\n *\n * Grouped by the mode that admitted each package rather than by where the tool\n * came from, because a mode is the thing a reader can switch off. The mechanism\n * (extension, MCP server, plugin) orders rows inside a group instead, so the\n * expensive thing is still easy to find.\n *\n * Schemas never travel. Only names, kinds, owners and integer counts do, which\n * keeps the payload small enough to republish whenever the composition changes.\n */\nexport const CONTEXT_PROJECTION_VERSION = 1;\n\n/** Journal entry the cockpit reads; mirrored by CONTEXT_ENTRY_TYPE in doompi-web. */\nexport const DOOM_CONTEXT_ENTRY_TYPE = 'doom-context';\n\n/** The tokenizer these counts came from, so a reader can judge them. */\nexport const CONTEXT_PROJECTION_ESTIMATOR = 'gpt-tokenizer';\n\nexport type ContextGroupKind = 'major' | 'minor' | 'domain' | 'core';\nexport type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';\n\n/** Everything the runtime could not attribute lands here rather than vanishing. */\nconst CORE_GROUP = 'core';\n/** Pi's own tools have no package; the runtime itself is the owner. */\nconst CORE_OWNER = 'pi';\nconst MCP_KEY_PREFIX = 'mcp:';\n\nconst KIND_ORDER: Record<ContextGroupKind, number> = { major: 0, minor: 1, domain: 2, core: 3 };\nconst SOURCE_ORDER: Record<ContextItemSource, number> = { extension: 0, mcp: 1, plugin: 2, core: 3 };\n\nexport interface ContextItemProjection {\n readonly name: string;\n readonly itemKind: 'tool' | 'skill';\n readonly source: ContextItemSource;\n /** The package, server, or plugin that registered it. */\n readonly owner: string;\n readonly tokens: number;\n readonly active: boolean;\n}\n\nexport interface ContextGroupProjection {\n readonly id: string;\n readonly label: string;\n readonly kind: ContextGroupKind;\n readonly items: readonly ContextItemProjection[];\n /** What the group costs now: active items only. */\n readonly tokens: number;\n /** What its gated items would add if switched on. */\n readonly inactiveTokens: number;\n}\n\n/**\n * What the assembled system prompt costs, and how complete the figure is.\n *\n * Kept out of `groups` on purpose. Every tool's prose and every skill's listing\n * already count inside the group subtotals, and those are slices of this same\n * text, so folding it into `totalTokens` would bill them twice.\n */\nexport interface ContextSystemPromptProjection {\n readonly tokens: number;\n readonly stage: ContextPromptStage;\n}\n\nexport interface ContextProjection {\n readonly version: typeof CONTEXT_PROJECTION_VERSION;\n readonly revision: number;\n /** Selection snapshot used to build this projection, replayable after reconnect. */\n readonly selection: {\n readonly majorMode: string;\n readonly domains: readonly string[];\n readonly profile?: string;\n };\n readonly groups: readonly ContextGroupProjection[];\n readonly totalTokens: number;\n readonly inactiveTokens: number;\n readonly systemPrompt?: ContextSystemPromptProjection;\n readonly estimator: typeof CONTEXT_PROJECTION_ESTIMATOR;\n}\n\nexport interface ContextConditionalAttribution {\n readonly kind: 'minor' | 'domain';\n readonly mode: string;\n readonly label?: string;\n}\n\nexport interface ContextToolInventory extends ToolEntry {\n /** A contribution-level gate is more specific than its package's major-mode owner. */\n readonly contextAttribution?: ContextConditionalAttribution;\n}\n\nexport interface ContextToolSource extends Omit<ToolSource, 'tools'> {\n readonly tools: readonly ContextToolInventory[];\n}\n\n/** Skill fields needed to project cost and serve detail. Virtual resources may not have a filesystem path. */\nexport interface ContextSkillInventory {\n readonly name: string;\n readonly description: string;\n readonly group: 'extensions' | 'help' | 'plugins' | 'default';\n readonly owner: string;\n readonly modelInvocable: boolean;\n readonly promptTokens?: number;\n readonly filePath?: string;\n /** The skill's instructions, when the host holds them; read only when a reader opens the row. */\n readonly body?: string;\n readonly contextAttribution?: ContextConditionalAttribution;\n}\n\nexport interface ContextProjectionInput {\n readonly revision: number;\n readonly majorMode: string;\n readonly profile?: string;\n /** Additional groups supplied by the composition owner, in display order. */\n readonly groups: readonly { readonly id: string; readonly label: string; readonly kind: ContextGroupKind }[];\n /** Domains the composition resolved under, whether or not they carried a plugin. */\n readonly domains: readonly string[];\n readonly sources: readonly ContextToolSource[];\n readonly skills: readonly ContextSkillInventory[];\n /**\n * Owner identifier to the mode that admitted it.\n *\n * Keys are package names for extensions and layer packages, and plugin or\n * server names for anything a domain carries. The caller owns this join\n * because only it knows how a given deployment resolved its plugins.\n */\n readonly attribution: Readonly<Record<string, PackageAttribution>>;\n readonly countTokens: CountTokens;\n /** Absent until the session has a prompt to report. */\n readonly systemPrompt?: ContextSystemPromptProjection;\n}\n\ninterface Bucket {\n kind: ContextGroupKind;\n label: string;\n items: ContextItemProjection[];\n}\n\nfunction skillSource(group: ContextSkillInventory['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nfunction byRow(left: ContextItemProjection, right: ContextItemProjection): number {\n return SOURCE_ORDER[left.source] - SOURCE_ORDER[right.source] || left.name.localeCompare(right.name);\n}\n\nexport function projectContext(input: ContextProjectionInput): ContextProjection {\n const buckets = new Map<string, Bucket>();\n\n // Declared before anything is placed, so a mode with nothing attributed to it\n // still appears. An empty group is a fact worth showing: it says the mode is\n // on and cheap, not that the runtime forgot about it.\n const declare = (id: string, label: string, kind: ContextGroupKind): void => {\n if (!buckets.has(id)) buckets.set(id, { kind, label, items: [] });\n };\n if (input.majorMode) declare(input.majorMode, input.majorMode, 'major');\n for (const group of input.groups) declare(group.id, group.label, group.kind);\n // A domain that contributed no plugin is still part of what the session is\n // running under, and a reader comparing the panel against the footer would\n // otherwise read its absence as the panel being out of date.\n for (const domain of input.domains) declare(domain, domain, 'domain');\n\n const place = (owner: string, item: ContextItemProjection, conditional?: ContextConditionalAttribution): void => {\n const attributed = conditional ?? input.attribution[owner];\n const id = attributed?.mode ?? CORE_GROUP;\n const kind = attributed?.kind ?? 'core';\n declare(id, attributed && 'label' in attributed ? (attributed.label ?? id) : id, kind);\n buckets.get(id)?.items.push(item);\n };\n\n for (const source of input.sources) {\n // An extension is owned by its package, an MCP tool by the server that\n // advertised it. `label` is display text like `scaffold · mcp`, so the\n // identifier comes from the key instead: an owner is joined on, not read.\n const owner =\n source.kind === 'extension'\n ? (source.packageName ?? source.key)\n : source.kind === 'mcp'\n ? source.key.slice(MCP_KEY_PREFIX.length)\n : CORE_OWNER;\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const item: ContextItemProjection = {\n name: tool.name,\n itemKind: 'tool',\n source: source.kind === 'core' ? 'core' : source.kind,\n owner,\n tokens: cost.totalTokens,\n active: tool.active,\n };\n if (source.kind === 'core') {\n declare(CORE_GROUP, CORE_GROUP, 'core');\n buckets.get(CORE_GROUP)?.items.push(item);\n } else {\n place(owner, item, tool.contextAttribution);\n }\n }\n }\n\n for (const skill of input.skills) {\n place(\n skill.owner,\n {\n name: skill.name,\n itemKind: 'skill',\n source: skillSource(skill.group),\n owner: skill.owner,\n tokens: skill.promptTokens ?? 0,\n // A skill is always offered to the model; there is no inactive state for\n // it the way there is for a tool Pi has filtered out.\n active: true,\n },\n skill.contextAttribution,\n );\n }\n\n const groups: ContextGroupProjection[] = [...buckets.entries()]\n .map(([id, bucket]) => {\n const items = [...bucket.items].sort(byRow);\n // Only what is actually sent counts. Pi builds the prompt from its\n // active tools, so a gated one costs nothing until it is switched on,\n // and folding it into the total would overstate the bill by a third.\n return {\n id,\n label: bucket.label,\n kind: bucket.kind,\n items,\n tokens: items.reduce((total, item) => total + (item.active ? item.tokens : 0), 0),\n inactiveTokens: items.reduce((total, item) => total + (item.active ? 0 : item.tokens), 0),\n };\n })\n .sort((left, right) => KIND_ORDER[left.kind] - KIND_ORDER[right.kind] || left.label.localeCompare(right.label));\n\n return {\n version: CONTEXT_PROJECTION_VERSION,\n revision: input.revision,\n selection: {\n majorMode: input.majorMode,\n domains: [...input.domains],\n ...(input.profile === undefined ? {} : { profile: input.profile }),\n },\n groups,\n totalTokens: groups.reduce((total, group) => total + group.tokens, 0),\n inactiveTokens: groups.reduce((total, group) => total + group.inactiveTokens, 0),\n ...(input.systemPrompt === undefined ? {} : { systemPrompt: input.systemPrompt }),\n estimator: CONTEXT_PROJECTION_ESTIMATOR,\n };\n}\n"],"mappings":"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"}
|
|
@@ -95,6 +95,8 @@ export interface ContextSkillInventory {
|
|
|
95
95
|
readonly modelInvocable: boolean;
|
|
96
96
|
readonly promptTokens?: number;
|
|
97
97
|
readonly filePath?: string;
|
|
98
|
+
/** The skill's instructions, when the host holds them; read only when a reader opens the row. */
|
|
99
|
+
readonly body?: string;
|
|
98
100
|
readonly contextAttribution?: ContextConditionalAttribution;
|
|
99
101
|
}
|
|
100
102
|
export interface ContextProjectionInput {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextProjection.d.mts","names":[],"sources":["../src/services/contextProjection/index.ts"],"mappings":";;;;;iBACiB;;EAEf;;EAEA;;EAEA;;;;;;;;;;;;;qBAiBW;;qBAGA;;qBAGA;YAED;YACA;iBAWK;WACN;WACA;WACA,QAAQ;;WAER;WACA;WACA;;iBAGM;WACN;WACA;WACA,MAAM;WACN,gBAAgB;;WAEhB;;WAEA;;;;;;;;;iBAUM;WACN;WACA,OAAO;;iBAGD;WACN,gBAAgB;WAChB;;WAEA;aACE;aACA;aACA;;WAEF,iBAAiB;WACjB;WACA;WACA,eAAe;WACf,kBAAkB;;iBAGZ;WACN;WACA;WACA;;iBAGM,6BAA6B;;WAEnC,qBAAqB;;iBAGf,0BAA0B,KAAK;WACrC,gBAAgB;;;iBAIV;WACN;WACA;WACA;WACA;WACA;WACA;WACA;WACA,qBAAqB;;iBAGf;WACN;WACA;WACA;;WAEA;aAA4B;aAAqB;aAAwB,MAAM;;;WAE/E;WACA,kBAAkB;WAClB,iBAAiB;;;;;;;;WAQjB,aAAa,SAAS,eAAe;WACrC,aAAa;;WAEb,eAAe;;wBAmBV,eAAe,OAAO,yBAAyB"}
|
|
1
|
+
{"version":3,"file":"contextProjection.d.mts","names":[],"sources":["../src/services/contextProjection/index.ts"],"mappings":";;;;;iBACiB;;EAEf;;EAEA;;EAEA;;;;;;;;;;;;;qBAiBW;;qBAGA;;qBAGA;YAED;YACA;iBAWK;WACN;WACA;WACA,QAAQ;;WAER;WACA;WACA;;iBAGM;WACN;WACA;WACA,MAAM;WACN,gBAAgB;;WAEhB;;WAEA;;;;;;;;;iBAUM;WACN;WACA,OAAO;;iBAGD;WACN,gBAAgB;WAChB;;WAEA;aACE;aACA;aACA;;WAEF,iBAAiB;WACjB;WACA;WACA,eAAe;WACf,kBAAkB;;iBAGZ;WACN;WACA;WACA;;iBAGM,6BAA6B;;WAEnC,qBAAqB;;iBAGf,0BAA0B,KAAK;WACrC,gBAAgB;;;iBAIV;WACN;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA;WACA,qBAAqB;;iBAGf;WACN;WACA;WACA;;WAEA;aAA4B;aAAqB;aAAwB,MAAM;;;WAE/E;WACA,kBAAkB;WAClB,iBAAiB;;;;;;;;WAQjB,aAAa,SAAS,eAAe;WACrC,aAAa;;WAEb,eAAe;;wBAmBV,eAAe,OAAO,yBAAyB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextProjection.mjs","names":[],"sources":["../src/services/contextProjection/index.ts"],"sourcesContent":["/** What admitted a package into the composition. */\nexport interface PackageAttribution {\n /** `major` for a layer package, `domain` for a plugin a domain carries. */\n kind: 'major' | 'domain';\n /** The major mode name, or the domain name. */\n mode: string;\n /** The layer the package was listed under; absent for a domain plugin. */\n layer?: string;\n}\n\nimport type { ContextPromptStage } from '../../types/contextApi';\nimport { type CountTokens, type ToolEntry, type ToolSource, tokensForTool } from '../toolInventory';\n\n/**\n * What the session is carrying, and what carrying it costs.\n *\n * Grouped by the mode that admitted each package rather than by where the tool\n * came from, because a mode is the thing a reader can switch off. The mechanism\n * (extension, MCP server, plugin) orders rows inside a group instead, so the\n * expensive thing is still easy to find.\n *\n * Schemas never travel. Only names, kinds, owners and integer counts do, which\n * keeps the payload small enough to republish whenever the composition changes.\n */\nexport const CONTEXT_PROJECTION_VERSION = 1;\n\n/** Journal entry the cockpit reads; mirrored by CONTEXT_ENTRY_TYPE in doompi-web. */\nexport const DOOM_CONTEXT_ENTRY_TYPE = 'doom-context';\n\n/** The tokenizer these counts came from, so a reader can judge them. */\nexport const CONTEXT_PROJECTION_ESTIMATOR = 'gpt-tokenizer';\n\nexport type ContextGroupKind = 'major' | 'minor' | 'domain' | 'core';\nexport type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';\n\n/** Everything the runtime could not attribute lands here rather than vanishing. */\nconst CORE_GROUP = 'core';\n/** Pi's own tools have no package; the runtime itself is the owner. */\nconst CORE_OWNER = 'pi';\nconst MCP_KEY_PREFIX = 'mcp:';\n\nconst KIND_ORDER: Record<ContextGroupKind, number> = { major: 0, minor: 1, domain: 2, core: 3 };\nconst SOURCE_ORDER: Record<ContextItemSource, number> = { extension: 0, mcp: 1, plugin: 2, core: 3 };\n\nexport interface ContextItemProjection {\n readonly name: string;\n readonly itemKind: 'tool' | 'skill';\n readonly source: ContextItemSource;\n /** The package, server, or plugin that registered it. */\n readonly owner: string;\n readonly tokens: number;\n readonly active: boolean;\n}\n\nexport interface ContextGroupProjection {\n readonly id: string;\n readonly label: string;\n readonly kind: ContextGroupKind;\n readonly items: readonly ContextItemProjection[];\n /** What the group costs now: active items only. */\n readonly tokens: number;\n /** What its gated items would add if switched on. */\n readonly inactiveTokens: number;\n}\n\n/**\n * What the assembled system prompt costs, and how complete the figure is.\n *\n * Kept out of `groups` on purpose. Every tool's prose and every skill's listing\n * already count inside the group subtotals, and those are slices of this same\n * text, so folding it into `totalTokens` would bill them twice.\n */\nexport interface ContextSystemPromptProjection {\n readonly tokens: number;\n readonly stage: ContextPromptStage;\n}\n\nexport interface ContextProjection {\n readonly version: typeof CONTEXT_PROJECTION_VERSION;\n readonly revision: number;\n /** Selection snapshot used to build this projection, replayable after reconnect. */\n readonly selection: {\n readonly majorMode: string;\n readonly domains: readonly string[];\n readonly profile?: string;\n };\n readonly groups: readonly ContextGroupProjection[];\n readonly totalTokens: number;\n readonly inactiveTokens: number;\n readonly systemPrompt?: ContextSystemPromptProjection;\n readonly estimator: typeof CONTEXT_PROJECTION_ESTIMATOR;\n}\n\nexport interface ContextConditionalAttribution {\n readonly kind: 'minor' | 'domain';\n readonly mode: string;\n readonly label?: string;\n}\n\nexport interface ContextToolInventory extends ToolEntry {\n /** A contribution-level gate is more specific than its package's major-mode owner. */\n readonly contextAttribution?: ContextConditionalAttribution;\n}\n\nexport interface ContextToolSource extends Omit<ToolSource, 'tools'> {\n readonly tools: readonly ContextToolInventory[];\n}\n\n/** Skill fields needed to project cost and serve detail. Virtual resources may not have a filesystem path. */\nexport interface ContextSkillInventory {\n readonly name: string;\n readonly description: string;\n readonly group: 'extensions' | 'help' | 'plugins' | 'default';\n readonly owner: string;\n readonly modelInvocable: boolean;\n readonly promptTokens?: number;\n readonly filePath?: string;\n readonly contextAttribution?: ContextConditionalAttribution;\n}\n\nexport interface ContextProjectionInput {\n readonly revision: number;\n readonly majorMode: string;\n readonly profile?: string;\n /** Additional groups supplied by the composition owner, in display order. */\n readonly groups: readonly { readonly id: string; readonly label: string; readonly kind: ContextGroupKind }[];\n /** Domains the composition resolved under, whether or not they carried a plugin. */\n readonly domains: readonly string[];\n readonly sources: readonly ContextToolSource[];\n readonly skills: readonly ContextSkillInventory[];\n /**\n * Owner identifier to the mode that admitted it.\n *\n * Keys are package names for extensions and layer packages, and plugin or\n * server names for anything a domain carries. The caller owns this join\n * because only it knows how a given deployment resolved its plugins.\n */\n readonly attribution: Readonly<Record<string, PackageAttribution>>;\n readonly countTokens: CountTokens;\n /** Absent until the session has a prompt to report. */\n readonly systemPrompt?: ContextSystemPromptProjection;\n}\n\ninterface Bucket {\n kind: ContextGroupKind;\n label: string;\n items: ContextItemProjection[];\n}\n\nfunction skillSource(group: ContextSkillInventory['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nfunction byRow(left: ContextItemProjection, right: ContextItemProjection): number {\n return SOURCE_ORDER[left.source] - SOURCE_ORDER[right.source] || left.name.localeCompare(right.name);\n}\n\nexport function projectContext(input: ContextProjectionInput): ContextProjection {\n const buckets = new Map<string, Bucket>();\n\n // Declared before anything is placed, so a mode with nothing attributed to it\n // still appears. An empty group is a fact worth showing: it says the mode is\n // on and cheap, not that the runtime forgot about it.\n const declare = (id: string, label: string, kind: ContextGroupKind): void => {\n if (!buckets.has(id)) buckets.set(id, { kind, label, items: [] });\n };\n if (input.majorMode) declare(input.majorMode, input.majorMode, 'major');\n for (const group of input.groups) declare(group.id, group.label, group.kind);\n // A domain that contributed no plugin is still part of what the session is\n // running under, and a reader comparing the panel against the footer would\n // otherwise read its absence as the panel being out of date.\n for (const domain of input.domains) declare(domain, domain, 'domain');\n\n const place = (owner: string, item: ContextItemProjection, conditional?: ContextConditionalAttribution): void => {\n const attributed = conditional ?? input.attribution[owner];\n const id = attributed?.mode ?? CORE_GROUP;\n const kind = attributed?.kind ?? 'core';\n declare(id, attributed && 'label' in attributed ? (attributed.label ?? id) : id, kind);\n buckets.get(id)?.items.push(item);\n };\n\n for (const source of input.sources) {\n // An extension is owned by its package, an MCP tool by the server that\n // advertised it. `label` is display text like `scaffold · mcp`, so the\n // identifier comes from the key instead: an owner is joined on, not read.\n const owner =\n source.kind === 'extension'\n ? (source.packageName ?? source.key)\n : source.kind === 'mcp'\n ? source.key.slice(MCP_KEY_PREFIX.length)\n : CORE_OWNER;\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const item: ContextItemProjection = {\n name: tool.name,\n itemKind: 'tool',\n source: source.kind === 'core' ? 'core' : source.kind,\n owner,\n tokens: cost.totalTokens,\n active: tool.active,\n };\n if (source.kind === 'core') {\n declare(CORE_GROUP, CORE_GROUP, 'core');\n buckets.get(CORE_GROUP)?.items.push(item);\n } else {\n place(owner, item, tool.contextAttribution);\n }\n }\n }\n\n for (const skill of input.skills) {\n place(\n skill.owner,\n {\n name: skill.name,\n itemKind: 'skill',\n source: skillSource(skill.group),\n owner: skill.owner,\n tokens: skill.promptTokens ?? 0,\n // A skill is always offered to the model; there is no inactive state for\n // it the way there is for a tool Pi has filtered out.\n active: true,\n },\n skill.contextAttribution,\n );\n }\n\n const groups: ContextGroupProjection[] = [...buckets.entries()]\n .map(([id, bucket]) => {\n const items = [...bucket.items].sort(byRow);\n // Only what is actually sent counts. Pi builds the prompt from its\n // active tools, so a gated one costs nothing until it is switched on,\n // and folding it into the total would overstate the bill by a third.\n return {\n id,\n label: bucket.label,\n kind: bucket.kind,\n items,\n tokens: items.reduce((total, item) => total + (item.active ? item.tokens : 0), 0),\n inactiveTokens: items.reduce((total, item) => total + (item.active ? 0 : item.tokens), 0),\n };\n })\n .sort((left, right) => KIND_ORDER[left.kind] - KIND_ORDER[right.kind] || left.label.localeCompare(right.label));\n\n return {\n version: CONTEXT_PROJECTION_VERSION,\n revision: input.revision,\n selection: {\n majorMode: input.majorMode,\n domains: [...input.domains],\n ...(input.profile === undefined ? {} : { profile: input.profile }),\n },\n groups,\n totalTokens: groups.reduce((total, group) => total + group.tokens, 0),\n inactiveTokens: groups.reduce((total, group) => total + group.inactiveTokens, 0),\n ...(input.systemPrompt === undefined ? {} : { systemPrompt: input.systemPrompt }),\n estimator: CONTEXT_PROJECTION_ESTIMATOR,\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAwBA,MAAa,6BAA6B;;AAG1C,MAAa,0BAA0B;;AAGvC,MAAa,+BAA+B;;AAM5C,MAAM,aAAa;;AAEnB,MAAM,aAAa;AAGnB,MAAM,aAA+C;CAAE,OAAO;CAAG,OAAO;CAAG,QAAQ;CAAG,MAAM;AAAE;AAC9F,MAAM,eAAkD;CAAE,WAAW;CAAG,KAAK;CAAG,QAAQ;CAAG,MAAM;AAAE;AA2GnG,SAAS,YAAY,OAA0D;CAC7E,IAAI,UAAU,WAAW,OAAO;CAChC,IAAI,UAAU,cAAc,OAAO;CACnC,OAAO;AACT;AAEA,SAAS,MAAM,MAA6B,OAAsC;CAChF,OAAO,aAAa,KAAK,UAAU,aAAa,MAAM,WAAW,KAAK,KAAK,cAAc,MAAM,IAAI;AACrG;AAEA,SAAgB,eAAe,OAAkD;CAC/E,MAAM,0BAAU,IAAI,IAAoB;CAKxC,MAAM,WAAW,IAAY,OAAe,SAAiC;EAC3E,IAAI,CAAC,QAAQ,IAAI,EAAE,GAAG,QAAQ,IAAI,IAAI;GAAE;GAAM;GAAO,OAAO,CAAC;EAAE,CAAC;CAClE;CACA,IAAI,MAAM,WAAW,QAAQ,MAAM,WAAW,MAAM,WAAW,OAAO;CACtE,KAAK,MAAM,SAAS,MAAM,QAAQ,QAAQ,MAAM,IAAI,MAAM,OAAO,MAAM,IAAI;CAI3E,KAAK,MAAM,UAAU,MAAM,SAAS,QAAQ,QAAQ,QAAQ,QAAQ;CAEpE,MAAM,SAAS,OAAe,MAA6B,gBAAsD;EAC/G,MAAM,aAAa,eAAe,MAAM,YAAY;EACpD,MAAM,KAAK,YAAY,QAAQ;EAC/B,MAAM,OAAO,YAAY,QAAQ;EACjC,QAAQ,IAAI,cAAc,WAAW,aAAc,WAAW,SAAS,KAAM,IAAI,IAAI;EACrF,QAAQ,IAAI,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI;CAClC;CAEA,KAAK,MAAM,UAAU,MAAM,SAAS;EAIlC,MAAM,QACJ,OAAO,SAAS,cACX,OAAO,eAAe,OAAO,MAC9B,OAAO,SAAS,QACd,OAAO,IAAI,MAAM,CAAqB,IACtC;EACR,KAAK,MAAM,QAAQ,OAAO,OAAO;GAC/B,MAAM,OAAO,cAAc,MAAM,MAAM,WAAW;GAClD,MAAM,OAA8B;IAClC,MAAM,KAAK;IACX,UAAU;IACV,QAAQ,OAAO,SAAS,SAAS,SAAS,OAAO;IACjD;IACA,QAAQ,KAAK;IACb,QAAQ,KAAK;GACf;GACA,IAAI,OAAO,SAAS,QAAQ;IAC1B,QAAQ,YAAY,YAAY,MAAM;IACtC,QAAQ,IAAI,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI;GAC1C,OACE,MAAM,OAAO,MAAM,KAAK,kBAAkB;EAE9C;CACF;CAEA,KAAK,MAAM,SAAS,MAAM,QACxB,MACE,MAAM,OACN;EACE,MAAM,MAAM;EACZ,UAAU;EACV,QAAQ,YAAY,MAAM,KAAK;EAC/B,OAAO,MAAM;EACb,QAAQ,MAAM,gBAAgB;EAG9B,QAAQ;CACV,GACA,MAAM,kBACR;CAGF,MAAM,SAAmC,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC,CAC5D,KAAK,CAAC,IAAI,YAAY;EACrB,MAAM,QAAQ,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC,KAAK,KAAK;EAI1C,OAAO;GACL;GACA,OAAO,OAAO;GACd,MAAM,OAAO;GACb;GACA,QAAQ,MAAM,QAAQ,OAAO,SAAS,SAAS,KAAK,SAAS,KAAK,SAAS,IAAI,CAAC;GAChF,gBAAgB,MAAM,QAAQ,OAAO,SAAS,SAAS,KAAK,SAAS,IAAI,KAAK,SAAS,CAAC;EAC1F;CACF,CAAC,CAAC,CACD,MAAM,MAAM,UAAU,WAAW,KAAK,QAAQ,WAAW,MAAM,SAAS,KAAK,MAAM,cAAc,MAAM,KAAK,CAAC;CAEhH,OAAO;EACL,SAAA;EACA,UAAU,MAAM;EAChB,WAAW;GACT,WAAW,MAAM;GACjB,SAAS,CAAC,GAAG,MAAM,OAAO;GAC1B,GAAI,MAAM,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;EAClE;EACA;EACA,aAAa,OAAO,QAAQ,OAAO,UAAU,QAAQ,MAAM,QAAQ,CAAC;EACpE,gBAAgB,OAAO,QAAQ,OAAO,UAAU,QAAQ,MAAM,gBAAgB,CAAC;EAC/E,GAAI,MAAM,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,MAAM,aAAa;EAC/E,WAAW;CACb;AACF"}
|
|
1
|
+
{"version":3,"file":"contextProjection.mjs","names":[],"sources":["../src/services/contextProjection/index.ts"],"sourcesContent":["/** What admitted a package into the composition. */\nexport interface PackageAttribution {\n /** `major` for a layer package, `domain` for a plugin a domain carries. */\n kind: 'major' | 'domain';\n /** The major mode name, or the domain name. */\n mode: string;\n /** The layer the package was listed under; absent for a domain plugin. */\n layer?: string;\n}\n\nimport type { ContextPromptStage } from '../../types/contextApi';\nimport { type CountTokens, type ToolEntry, type ToolSource, tokensForTool } from '../toolInventory';\n\n/**\n * What the session is carrying, and what carrying it costs.\n *\n * Grouped by the mode that admitted each package rather than by where the tool\n * came from, because a mode is the thing a reader can switch off. The mechanism\n * (extension, MCP server, plugin) orders rows inside a group instead, so the\n * expensive thing is still easy to find.\n *\n * Schemas never travel. Only names, kinds, owners and integer counts do, which\n * keeps the payload small enough to republish whenever the composition changes.\n */\nexport const CONTEXT_PROJECTION_VERSION = 1;\n\n/** Journal entry the cockpit reads; mirrored by CONTEXT_ENTRY_TYPE in doompi-web. */\nexport const DOOM_CONTEXT_ENTRY_TYPE = 'doom-context';\n\n/** The tokenizer these counts came from, so a reader can judge them. */\nexport const CONTEXT_PROJECTION_ESTIMATOR = 'gpt-tokenizer';\n\nexport type ContextGroupKind = 'major' | 'minor' | 'domain' | 'core';\nexport type ContextItemSource = 'extension' | 'mcp' | 'plugin' | 'core';\n\n/** Everything the runtime could not attribute lands here rather than vanishing. */\nconst CORE_GROUP = 'core';\n/** Pi's own tools have no package; the runtime itself is the owner. */\nconst CORE_OWNER = 'pi';\nconst MCP_KEY_PREFIX = 'mcp:';\n\nconst KIND_ORDER: Record<ContextGroupKind, number> = { major: 0, minor: 1, domain: 2, core: 3 };\nconst SOURCE_ORDER: Record<ContextItemSource, number> = { extension: 0, mcp: 1, plugin: 2, core: 3 };\n\nexport interface ContextItemProjection {\n readonly name: string;\n readonly itemKind: 'tool' | 'skill';\n readonly source: ContextItemSource;\n /** The package, server, or plugin that registered it. */\n readonly owner: string;\n readonly tokens: number;\n readonly active: boolean;\n}\n\nexport interface ContextGroupProjection {\n readonly id: string;\n readonly label: string;\n readonly kind: ContextGroupKind;\n readonly items: readonly ContextItemProjection[];\n /** What the group costs now: active items only. */\n readonly tokens: number;\n /** What its gated items would add if switched on. */\n readonly inactiveTokens: number;\n}\n\n/**\n * What the assembled system prompt costs, and how complete the figure is.\n *\n * Kept out of `groups` on purpose. Every tool's prose and every skill's listing\n * already count inside the group subtotals, and those are slices of this same\n * text, so folding it into `totalTokens` would bill them twice.\n */\nexport interface ContextSystemPromptProjection {\n readonly tokens: number;\n readonly stage: ContextPromptStage;\n}\n\nexport interface ContextProjection {\n readonly version: typeof CONTEXT_PROJECTION_VERSION;\n readonly revision: number;\n /** Selection snapshot used to build this projection, replayable after reconnect. */\n readonly selection: {\n readonly majorMode: string;\n readonly domains: readonly string[];\n readonly profile?: string;\n };\n readonly groups: readonly ContextGroupProjection[];\n readonly totalTokens: number;\n readonly inactiveTokens: number;\n readonly systemPrompt?: ContextSystemPromptProjection;\n readonly estimator: typeof CONTEXT_PROJECTION_ESTIMATOR;\n}\n\nexport interface ContextConditionalAttribution {\n readonly kind: 'minor' | 'domain';\n readonly mode: string;\n readonly label?: string;\n}\n\nexport interface ContextToolInventory extends ToolEntry {\n /** A contribution-level gate is more specific than its package's major-mode owner. */\n readonly contextAttribution?: ContextConditionalAttribution;\n}\n\nexport interface ContextToolSource extends Omit<ToolSource, 'tools'> {\n readonly tools: readonly ContextToolInventory[];\n}\n\n/** Skill fields needed to project cost and serve detail. Virtual resources may not have a filesystem path. */\nexport interface ContextSkillInventory {\n readonly name: string;\n readonly description: string;\n readonly group: 'extensions' | 'help' | 'plugins' | 'default';\n readonly owner: string;\n readonly modelInvocable: boolean;\n readonly promptTokens?: number;\n readonly filePath?: string;\n /** The skill's instructions, when the host holds them; read only when a reader opens the row. */\n readonly body?: string;\n readonly contextAttribution?: ContextConditionalAttribution;\n}\n\nexport interface ContextProjectionInput {\n readonly revision: number;\n readonly majorMode: string;\n readonly profile?: string;\n /** Additional groups supplied by the composition owner, in display order. */\n readonly groups: readonly { readonly id: string; readonly label: string; readonly kind: ContextGroupKind }[];\n /** Domains the composition resolved under, whether or not they carried a plugin. */\n readonly domains: readonly string[];\n readonly sources: readonly ContextToolSource[];\n readonly skills: readonly ContextSkillInventory[];\n /**\n * Owner identifier to the mode that admitted it.\n *\n * Keys are package names for extensions and layer packages, and plugin or\n * server names for anything a domain carries. The caller owns this join\n * because only it knows how a given deployment resolved its plugins.\n */\n readonly attribution: Readonly<Record<string, PackageAttribution>>;\n readonly countTokens: CountTokens;\n /** Absent until the session has a prompt to report. */\n readonly systemPrompt?: ContextSystemPromptProjection;\n}\n\ninterface Bucket {\n kind: ContextGroupKind;\n label: string;\n items: ContextItemProjection[];\n}\n\nfunction skillSource(group: ContextSkillInventory['group']): ContextItemSource {\n if (group === 'plugins') return 'plugin';\n if (group === 'extensions') return 'extension';\n return 'core';\n}\n\nfunction byRow(left: ContextItemProjection, right: ContextItemProjection): number {\n return SOURCE_ORDER[left.source] - SOURCE_ORDER[right.source] || left.name.localeCompare(right.name);\n}\n\nexport function projectContext(input: ContextProjectionInput): ContextProjection {\n const buckets = new Map<string, Bucket>();\n\n // Declared before anything is placed, so a mode with nothing attributed to it\n // still appears. An empty group is a fact worth showing: it says the mode is\n // on and cheap, not that the runtime forgot about it.\n const declare = (id: string, label: string, kind: ContextGroupKind): void => {\n if (!buckets.has(id)) buckets.set(id, { kind, label, items: [] });\n };\n if (input.majorMode) declare(input.majorMode, input.majorMode, 'major');\n for (const group of input.groups) declare(group.id, group.label, group.kind);\n // A domain that contributed no plugin is still part of what the session is\n // running under, and a reader comparing the panel against the footer would\n // otherwise read its absence as the panel being out of date.\n for (const domain of input.domains) declare(domain, domain, 'domain');\n\n const place = (owner: string, item: ContextItemProjection, conditional?: ContextConditionalAttribution): void => {\n const attributed = conditional ?? input.attribution[owner];\n const id = attributed?.mode ?? CORE_GROUP;\n const kind = attributed?.kind ?? 'core';\n declare(id, attributed && 'label' in attributed ? (attributed.label ?? id) : id, kind);\n buckets.get(id)?.items.push(item);\n };\n\n for (const source of input.sources) {\n // An extension is owned by its package, an MCP tool by the server that\n // advertised it. `label` is display text like `scaffold · mcp`, so the\n // identifier comes from the key instead: an owner is joined on, not read.\n const owner =\n source.kind === 'extension'\n ? (source.packageName ?? source.key)\n : source.kind === 'mcp'\n ? source.key.slice(MCP_KEY_PREFIX.length)\n : CORE_OWNER;\n for (const tool of source.tools) {\n const cost = tokensForTool(tool, input.countTokens);\n const item: ContextItemProjection = {\n name: tool.name,\n itemKind: 'tool',\n source: source.kind === 'core' ? 'core' : source.kind,\n owner,\n tokens: cost.totalTokens,\n active: tool.active,\n };\n if (source.kind === 'core') {\n declare(CORE_GROUP, CORE_GROUP, 'core');\n buckets.get(CORE_GROUP)?.items.push(item);\n } else {\n place(owner, item, tool.contextAttribution);\n }\n }\n }\n\n for (const skill of input.skills) {\n place(\n skill.owner,\n {\n name: skill.name,\n itemKind: 'skill',\n source: skillSource(skill.group),\n owner: skill.owner,\n tokens: skill.promptTokens ?? 0,\n // A skill is always offered to the model; there is no inactive state for\n // it the way there is for a tool Pi has filtered out.\n active: true,\n },\n skill.contextAttribution,\n );\n }\n\n const groups: ContextGroupProjection[] = [...buckets.entries()]\n .map(([id, bucket]) => {\n const items = [...bucket.items].sort(byRow);\n // Only what is actually sent counts. Pi builds the prompt from its\n // active tools, so a gated one costs nothing until it is switched on,\n // and folding it into the total would overstate the bill by a third.\n return {\n id,\n label: bucket.label,\n kind: bucket.kind,\n items,\n tokens: items.reduce((total, item) => total + (item.active ? item.tokens : 0), 0),\n inactiveTokens: items.reduce((total, item) => total + (item.active ? 0 : item.tokens), 0),\n };\n })\n .sort((left, right) => KIND_ORDER[left.kind] - KIND_ORDER[right.kind] || left.label.localeCompare(right.label));\n\n return {\n version: CONTEXT_PROJECTION_VERSION,\n revision: input.revision,\n selection: {\n majorMode: input.majorMode,\n domains: [...input.domains],\n ...(input.profile === undefined ? {} : { profile: input.profile }),\n },\n groups,\n totalTokens: groups.reduce((total, group) => total + group.tokens, 0),\n inactiveTokens: groups.reduce((total, group) => total + group.inactiveTokens, 0),\n ...(input.systemPrompt === undefined ? {} : { systemPrompt: input.systemPrompt }),\n estimator: CONTEXT_PROJECTION_ESTIMATOR,\n };\n}\n"],"mappings":"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"}
|
package/dist/headless.cjs
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_headless = require("./src/schemas/headless.cjs");
|
|
3
3
|
exports.DOOM_HEADLESS_HOST_SERVICE = require_headless.DOOM_HEADLESS_HOST_SERVICE;
|
|
4
4
|
exports.DOOM_HEADLESS_OWNER = require_headless.DOOM_HEADLESS_OWNER;
|
|
5
|
+
exports.DOOM_LOAD_SKILL_TOOL = require_headless.DOOM_LOAD_SKILL_TOOL;
|
|
5
6
|
exports.readDoomHeadlessHost = require_headless.readDoomHeadlessHost;
|
|
6
7
|
exports.readDoomHeadlessOwner = require_headless.readDoomHeadlessOwner;
|
|
7
8
|
exports.requireDoomHeadlessHost = require_headless.requireDoomHeadlessHost;
|
package/dist/headless.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DOOM_HEADLESS_HOST_SERVICE, DOOM_HEADLESS_OWNER, DoomHeadlessActivity, DoomHeadlessCapability, DoomHeadlessCapabilitySnapshot, DoomHeadlessClient, DoomHeadlessClientRequest, DoomHeadlessCommand, DoomHeadlessCompactionResult, DoomHeadlessCondition, DoomHeadlessContent, DoomHeadlessEventName, DoomHeadlessExecutionContext, DoomHeadlessHook, DoomHeadlessHookEvent, DoomHeadlessHookResult, DoomHeadlessHostService, DoomHeadlessModelSettings, DoomHeadlessRegistration, DoomHeadlessResource, DoomHeadlessSelection, DoomHeadlessSelectionChange, DoomHeadlessSession, DoomHeadlessTool, DoomHeadlessToolCompletionRequest, DoomHeadlessToolCompletionResult, DoomHeadlessToolRestriction, DoomHeadlessToolResult, DoomMcpSkillAccess, readDoomHeadlessHost, readDoomHeadlessOwner, requireDoomHeadlessHost } from "./src/schemas/headless.mjs";
|
|
2
|
-
export { DOOM_HEADLESS_HOST_SERVICE, DOOM_HEADLESS_OWNER, DoomHeadlessActivity, DoomHeadlessCapability, DoomHeadlessCapabilitySnapshot, DoomHeadlessClient, DoomHeadlessClientRequest, DoomHeadlessCommand, DoomHeadlessCompactionResult, DoomHeadlessCondition, DoomHeadlessContent, DoomHeadlessEventName, DoomHeadlessExecutionContext, DoomHeadlessHook, DoomHeadlessHookEvent, DoomHeadlessHookResult, DoomHeadlessHostService, DoomHeadlessModelSettings, DoomHeadlessRegistration, DoomHeadlessResource, DoomHeadlessSelection, DoomHeadlessSelectionChange, DoomHeadlessSession, DoomHeadlessTool, DoomHeadlessToolCompletionRequest, DoomHeadlessToolCompletionResult, DoomHeadlessToolRestriction, DoomHeadlessToolResult, DoomMcpSkillAccess, readDoomHeadlessHost, readDoomHeadlessOwner, requireDoomHeadlessHost };
|
|
1
|
+
import { DOOM_HEADLESS_HOST_SERVICE, DOOM_HEADLESS_OWNER, DOOM_LOAD_SKILL_TOOL, DoomHeadlessActivity, DoomHeadlessCapability, DoomHeadlessCapabilitySnapshot, DoomHeadlessClient, DoomHeadlessClientRequest, DoomHeadlessCommand, DoomHeadlessCompactionResult, DoomHeadlessCondition, DoomHeadlessContent, DoomHeadlessEventName, DoomHeadlessExecutionContext, DoomHeadlessHook, DoomHeadlessHookEvent, DoomHeadlessHookResult, DoomHeadlessHostService, DoomHeadlessModelSettings, DoomHeadlessRegistration, DoomHeadlessResource, DoomHeadlessSelection, DoomHeadlessSelectionChange, DoomHeadlessSession, DoomHeadlessTool, DoomHeadlessToolCompletionRequest, DoomHeadlessToolCompletionResult, DoomHeadlessToolRestriction, DoomHeadlessToolResult, DoomMcpSkillAccess, readDoomHeadlessHost, readDoomHeadlessOwner, requireDoomHeadlessHost } from "./src/schemas/headless.mjs";
|
|
2
|
+
export { DOOM_HEADLESS_HOST_SERVICE, DOOM_HEADLESS_OWNER, DOOM_LOAD_SKILL_TOOL, DoomHeadlessActivity, DoomHeadlessCapability, DoomHeadlessCapabilitySnapshot, DoomHeadlessClient, DoomHeadlessClientRequest, DoomHeadlessCommand, DoomHeadlessCompactionResult, DoomHeadlessCondition, DoomHeadlessContent, DoomHeadlessEventName, DoomHeadlessExecutionContext, DoomHeadlessHook, DoomHeadlessHookEvent, DoomHeadlessHookResult, DoomHeadlessHostService, DoomHeadlessModelSettings, DoomHeadlessRegistration, DoomHeadlessResource, DoomHeadlessSelection, DoomHeadlessSelectionChange, DoomHeadlessSession, DoomHeadlessTool, DoomHeadlessToolCompletionRequest, DoomHeadlessToolCompletionResult, DoomHeadlessToolRestriction, DoomHeadlessToolResult, DoomMcpSkillAccess, readDoomHeadlessHost, readDoomHeadlessOwner, requireDoomHeadlessHost };
|
package/dist/headless.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DOOM_HEADLESS_HOST_SERVICE, DOOM_HEADLESS_OWNER, readDoomHeadlessHost, readDoomHeadlessOwner, requireDoomHeadlessHost } from "./src/schemas/headless.mjs";
|
|
2
|
-
export { DOOM_HEADLESS_HOST_SERVICE, DOOM_HEADLESS_OWNER, readDoomHeadlessHost, readDoomHeadlessOwner, requireDoomHeadlessHost };
|
|
1
|
+
import { DOOM_HEADLESS_HOST_SERVICE, DOOM_HEADLESS_OWNER, DOOM_LOAD_SKILL_TOOL, readDoomHeadlessHost, readDoomHeadlessOwner, requireDoomHeadlessHost } from "./src/schemas/headless.mjs";
|
|
2
|
+
export { DOOM_HEADLESS_HOST_SERVICE, DOOM_HEADLESS_OWNER, DOOM_LOAD_SKILL_TOOL, readDoomHeadlessHost, readDoomHeadlessOwner, requireDoomHeadlessHost };
|
package/dist/headlessHub.cjs
CHANGED
|
@@ -2,8 +2,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_index = require("./src/services/serverFacet/index.cjs");
|
|
3
3
|
const require_serverFacetLoader = require("./src/server/serverFacetLoader.cjs");
|
|
4
4
|
require("./serverFacet.cjs");
|
|
5
|
+
const require_directHarnessRuntime = require("./src/server/directHarnessRuntime.cjs");
|
|
5
6
|
const require_pluginProtocol = require("./src/schemas/pluginProtocol.cjs");
|
|
6
7
|
require("./pluginProtocol.cjs");
|
|
8
|
+
let _earendil_works_pi_agent_core_harness_context = require("@earendil-works/pi-agent-core/harness/context");
|
|
7
9
|
//#region src/server/headlessHub.ts
|
|
8
10
|
const AUTHORIZATION_HEADER = "authorization";
|
|
9
11
|
const CHANNEL_PRIORITY = {
|
|
@@ -11,12 +13,30 @@ const CHANNEL_PRIORITY = {
|
|
|
11
13
|
workspace: 1,
|
|
12
14
|
session: 2
|
|
13
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* When a reopened session last settled, from the marker its runtime journals at every run end.
|
|
18
|
+
* Without it a session with a whole history would read as fresh until its next turn.
|
|
19
|
+
*/
|
|
20
|
+
async function lastSettledAt(host) {
|
|
21
|
+
try {
|
|
22
|
+
const entry = await host.runtime.lane.findEntry({
|
|
23
|
+
type: "custom",
|
|
24
|
+
customType: require_directHarnessRuntime.AGENT_SETTLED_ENTRY_TYPE,
|
|
25
|
+
order: "newestFirst"
|
|
26
|
+
}, _earendil_works_pi_agent_core_harness_context.BACKGROUND_CONTEXT);
|
|
27
|
+
const data = entry?.type === "custom" ? entry.data : void 0;
|
|
28
|
+
return typeof data?.timestamp === "number" && Number.isFinite(data.timestamp) ? new Date(data.timestamp).toISOString() : void 0;
|
|
29
|
+
} catch {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
14
33
|
function scopeOf(session) {
|
|
15
34
|
return {
|
|
16
35
|
sessionId: session.id,
|
|
17
36
|
...session.workspaceId === void 0 ? {} : { workspaceId: session.workspaceId },
|
|
18
37
|
cwd: session.cwd,
|
|
19
|
-
...session.environment === void 0 ? {} : { environment: session.environment }
|
|
38
|
+
...session.environment === void 0 ? {} : { environment: session.environment },
|
|
39
|
+
...session.host.sessionContext === void 0 ? {} : { sessionContext: session.host.sessionContext }
|
|
20
40
|
};
|
|
21
41
|
}
|
|
22
42
|
/** Owns direct, same-process session and channel state for presentation adapters. */
|
|
@@ -420,7 +440,8 @@ function createHeadlessHub(options) {
|
|
|
420
440
|
const register = (session) => {
|
|
421
441
|
if (closed) throw new Error("The headless hub is closed.");
|
|
422
442
|
if (sessions.has(session.id)) throw new Error(`Session '${session.id}' is already registered.`);
|
|
423
|
-
if (sessionShutdowns.
|
|
443
|
+
if (sessionShutdowns.get(session.id)?.pending) throw new Error(`Session '${session.id}' is still shutting down.`);
|
|
444
|
+
sessionShutdowns.delete(session.id);
|
|
424
445
|
let current = {
|
|
425
446
|
...session,
|
|
426
447
|
updatedAt: session.updatedAt ?? session.createdAt,
|
|
@@ -664,8 +685,12 @@ function createHeadlessHub(options) {
|
|
|
664
685
|
pluginRegistry,
|
|
665
686
|
invokePlugin: (call, direction, caller) => pluginRegistry.invoke(call, direction, caller),
|
|
666
687
|
async create(sessionOptions) {
|
|
688
|
+
const id = sessionOptions.sessionId;
|
|
667
689
|
if (closed) throw new Error("The headless hub is closed.");
|
|
690
|
+
if (sessions.has(id)) throw new Error(`Session '${id}' is already registered.`);
|
|
691
|
+
if (sessionShutdowns.get(id)?.pending) throw new Error(`Session '${id}' is still shutting down.`);
|
|
668
692
|
const host = await options.manager.create(sessionOptions);
|
|
693
|
+
const settledAt = await lastSettledAt(host);
|
|
669
694
|
const session = {
|
|
670
695
|
id: sessionOptions.sessionId,
|
|
671
696
|
workspaceId: sessionOptions.workspaceId,
|
|
@@ -676,9 +701,18 @@ function createHeadlessHub(options) {
|
|
|
676
701
|
...sessionOptions.parentSessionId === void 0 ? {} : { parentSessionId: sessionOptions.parentSessionId },
|
|
677
702
|
...sessionOptions.sessionProvenance === void 0 ? {} : { sessionProvenance: sessionOptions.sessionProvenance },
|
|
678
703
|
environment: sessionOptions.environment,
|
|
704
|
+
...settledAt === void 0 ? {} : {
|
|
705
|
+
everPrompted: true,
|
|
706
|
+
lastSettledAt: settledAt
|
|
707
|
+
},
|
|
679
708
|
host
|
|
680
709
|
};
|
|
681
|
-
|
|
710
|
+
try {
|
|
711
|
+
register(session);
|
|
712
|
+
} catch (error) {
|
|
713
|
+
await Promise.resolve(options.manager.closeSession(id)).catch((closeError) => options.onNotice?.(`Session '${id}' could not be closed after a failed registration: ${String(closeError)}`));
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
682
716
|
return session;
|
|
683
717
|
},
|
|
684
718
|
closeSession,
|