@agimon-ai/doompi 0.0.1-alpha.71 → 0.0.1-alpha.73
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/builders/cli/contextCatalog.cjs +15 -3
- package/dist/builders/cli/contextCatalog.cjs.map +1 -1
- package/dist/builders/cli/contextCatalog.mjs +15 -3
- package/dist/builders/cli/contextCatalog.mjs.map +1 -1
- package/dist/builders/server/runtime.cjs +64 -5
- package/dist/builders/server/runtime.cjs.map +1 -1
- package/dist/builders/server/runtime.mjs +65 -6
- package/dist/builders/server/runtime.mjs.map +1 -1
- package/dist/builders/web/bundleAssetPolicy.cjs +3 -3
- package/dist/builders/web/bundleAssetPolicy.cjs.map +1 -1
- package/dist/builders/web/bundleAssetPolicy.mjs +1 -1
- package/dist/builders/web/bundleAssetPolicy.mjs.map +1 -1
- package/dist/cli/commands/sync/index.cjs +14 -18
- package/dist/cli/commands/sync/index.cjs.map +1 -1
- package/dist/cli/commands/sync/index.d.cts.map +1 -1
- package/dist/cli/commands/sync/index.d.mts.map +1 -1
- package/dist/cli/commands/sync/index.mjs +14 -18
- package/dist/cli/commands/sync/index.mjs.map +1 -1
- package/dist/compiler/index.cjs +5 -5
- package/dist/compiler/index.cjs.map +1 -1
- package/dist/compiler/index.mjs +5 -5
- package/dist/compiler/index.mjs.map +1 -1
- package/dist/extensions/context-catalog.cjs +35 -4
- package/dist/extensions/context-catalog.cjs.map +1 -1
- package/dist/extensions/context-catalog.mjs +35 -4
- package/dist/extensions/context-catalog.mjs.map +1 -1
- package/dist/extensions/terminal-child-session.cjs +18 -0
- package/dist/extensions/terminal-child-session.cjs.map +1 -1
- package/dist/extensions/terminal-child-session.d.cts.map +1 -1
- package/dist/extensions/terminal-child-session.d.mts.map +1 -1
- package/dist/extensions/terminal-child-session.mjs +18 -0
- package/dist/extensions/terminal-child-session.mjs.map +1 -1
- package/docs/server/api-export.md +10 -0
- package/docs/server/api.md +5 -5
- package/docs/server/getting-started.md +3 -3
- package/docs/server/index.md +6 -6
- package/docs/server/ipc.md +28 -14
- package/docs/server/lifecycle.md +5 -5
- package/docs/server/security.md +4 -4
- package/package.json +24 -24
|
@@ -57,6 +57,11 @@ function createContextPublisher(pi, cordis, options = {}) {
|
|
|
57
57
|
})).groups);
|
|
58
58
|
} catch {}
|
|
59
59
|
const countTokens = await (0, _agimon_ai_doompi_skill_catalog.counter)();
|
|
60
|
+
const prompt = options.readSystemPrompt?.();
|
|
61
|
+
const promptCost = prompt === void 0 ? void 0 : {
|
|
62
|
+
tokens: countTokens(prompt.text),
|
|
63
|
+
stage: prompt.stage
|
|
64
|
+
};
|
|
60
65
|
const projection = (0, _agimon_ai_doompi_core_context_projection.projectContext)({
|
|
61
66
|
revision: revision + 1,
|
|
62
67
|
majorMode: harness.majorMode,
|
|
@@ -68,11 +73,13 @@ function createContextPublisher(pi, cordis, options = {}) {
|
|
|
68
73
|
sources,
|
|
69
74
|
skills,
|
|
70
75
|
attribution: attributionFor(repoRoot, harness.domains, harness.pluginDirectories),
|
|
71
|
-
countTokens
|
|
76
|
+
countTokens,
|
|
77
|
+
...promptCost === void 0 ? {} : { systemPrompt: promptCost }
|
|
72
78
|
});
|
|
73
79
|
const serialized = JSON.stringify({
|
|
74
80
|
...projection,
|
|
75
|
-
revision: 0
|
|
81
|
+
revision: 0,
|
|
82
|
+
promptText: prompt?.text
|
|
76
83
|
});
|
|
77
84
|
if (serialized === published || disposed) return;
|
|
78
85
|
published = serialized;
|
|
@@ -83,7 +90,12 @@ function createContextPublisher(pi, cordis, options = {}) {
|
|
|
83
90
|
options.writeDetail(sessionId, revision, (0, _agimon_ai_doompi_core_context_detail.buildContextDetail)({
|
|
84
91
|
sources,
|
|
85
92
|
skills,
|
|
86
|
-
countTokens
|
|
93
|
+
countTokens,
|
|
94
|
+
...prompt === void 0 || promptCost === void 0 ? {} : { systemPrompt: {
|
|
95
|
+
text: prompt.text,
|
|
96
|
+
tokens: promptCost.tokens,
|
|
97
|
+
stage: prompt.stage
|
|
98
|
+
} }
|
|
87
99
|
}));
|
|
88
100
|
}
|
|
89
101
|
pi.appendEntry(_agimon_ai_doompi_core_context_projection.DOOM_CONTEXT_ENTRY_TYPE, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextCatalog.cjs","names":["getHarnessState","resolvePluginEntries","readDoomMcpStatus","buildToolSources","extensionName","extensionPackageName","extensionToolSource","harnessRoot","buildSkillCatalog","readDoomSkillSourcesService","counter","projectContext","buildContextDetail","DOOM_CONTEXT_ENTRY_TYPE"],"sources":["../../../src/builders/cli/contextCatalog.ts"],"sourcesContent":["import { resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport { getHarnessState, harnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport { buildContextDetail } from '@agimon-ai/doompi-core/context-detail';\nimport { DOOM_CONTEXT_ENTRY_TYPE, projectContext } from '@agimon-ai/doompi-core/context-projection';\nimport { readDoomMcpStatus } from '@agimon-ai/doompi-core/mcp-status';\nimport { readDoomSkillSourcesService } from '@agimon-ai/doompi-core/skills';\nimport type { ContextItemDetail } from '@agimon-ai/doompi-core/types-context-api';\nimport { buildSkillCatalog, counter, type SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { extensionName, extensionPackageName, extensionToolSource } from '@agimon-ai/doompi-ui/extensionName';\nimport { buildToolSources } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\n\n/**\n * Publishes what the session is composed of, and what it costs.\n *\n * Read at publish time rather than accumulated, because the answer changes for\n * reasons this module does not see: a reconnected MCP server, a domain switch,\n * a plan-mode tool swap. Reading the live inventory is cheap next to being\n * wrong about it.\n */\nexport interface ContextPublisher {\n /** Never rejects: callers fire this and forget it. */\n publish: () => Promise<void>;\n dispose: () => void;\n}\n\n/** The active minor modes at publish time, read rather than remembered. */\nexport type ReadMinorModes = () => readonly { readonly id: string; readonly label: string }[];\n\nexport interface ContextPublisherOptions {\n readMinorModes?: ReadMinorModes;\n /**\n * The session the detail file is keyed by, read at publish time.\n *\n * Undefined until a session is bound, and undefined in a host that has no\n * session at all; the projection is still journaled either way, and only the\n * click-through detail goes unwritten.\n */\n readSessionId?: () => string | undefined;\n /**\n * Where the click-through detail is left for the session API to find.\n *\n * Injected because writing it is filesystem work and this is not the layer\n * that does filesystem work. A host that supplies neither still gets the\n * projection; only the detail behind a row goes unpublished.\n */\n writeDetail?: (sessionId: string, revision: number, items: readonly ContextItemDetail[]) => void;\n removeDetail?: (sessionId: string) => void;\n}\n\n/** Skills sit under owners, which is one level deeper than a flat list. */\nfunction flattenSkills(groups: Awaited<ReturnType<typeof buildSkillCatalog>>['groups']): SkillEntry[] {\n return groups.flatMap((group) => group.owners.flatMap((owner) => owner.skills));\n}\n\n/**\n * Package and plugin names to the mode that admitted them.\n *\n * Two halves meet here. Layer packages come from the harness, recorded when the\n * composition resolved. Domain plugins are re-resolved, because only the plugin\n * entry knows the domain that carried it.\n */\nfunction attributionFor(repoRoot: string, domains: readonly string[], pluginDirectories: readonly string[]) {\n const harness = getHarnessState();\n const attribution: Record<string, PackageAttribution> = { ...harness.packageAttribution };\n try {\n for (const entry of resolvePluginEntries(repoRoot, [...domains], [...pluginDirectories])) {\n if (entry.name && entry.domain) attribution[entry.name] = { kind: 'domain', mode: entry.domain };\n }\n } catch {\n // A domain that no longer resolves must not cost the reader the tool list.\n // Anything unmatched simply lands under core.\n }\n return attribution;\n}\n\nexport function createContextPublisher(\n pi: ExtensionAPI,\n cordis: Context,\n options: ContextPublisherOptions = {},\n): ContextPublisher {\n let published: string | undefined;\n let revision = 0;\n let disposed = false;\n /** Removed on disposal, so a session leaves nothing behind in the store. */\n let detailSessionId: string | undefined;\n\n const build = async (): Promise<void> => {\n if (disposed) return;\n const harness = getHarnessState();\n const mcpServers = readDoomMcpStatus(cordis)?.getSnapshot().servers;\n const sources = buildToolSources({\n tools: pi.getAllTools(),\n activeTools: pi.getActiveTools(),\n ...(mcpServers ? { mcpServers } : {}),\n resolveExtensionName: extensionName,\n resolveExtensionPackageName: extensionPackageName,\n resolveExtensionToolSource: (toolName) => extensionToolSource(pi, toolName),\n });\n\n const repoRoot = harnessRoot(harness);\n let skills: SkillEntry[] = [];\n try {\n const catalog = await buildSkillCatalog({\n repoRoot,\n activeSkillDirectories: harness.skillDirectories,\n extensionSources: readDoomSkillSourcesService(cordis)?.list() ?? [],\n });\n skills = flattenSkills(catalog.groups);\n } catch {\n // Tools are the larger cost and are already in hand; a skill walk that\n // fails should not take the whole figure down with it.\n }\n\n const countTokens = await counter();\n const projection = projectContext({\n revision: revision + 1,\n majorMode: harness.majorMode,\n groups: (options.readMinorModes?.() ?? []).map((mode) => ({ ...mode, kind: 'minor' as const })),\n domains: harness.domains,\n sources,\n skills,\n attribution: attributionFor(repoRoot, harness.domains, harness.pluginDirectories),\n countTokens,\n });\n\n // Revision is compared out, so a republish that changed nothing is silent\n // rather than a new journal entry saying the same thing.\n const serialized = JSON.stringify({ ...projection, revision: 0 });\n if (serialized === published || disposed) return;\n published = serialized;\n revision += 1;\n // The detail lands before the entry that invites a reader to ask for it, so\n // a click that follows the panel's update cannot outrun the file behind it.\n const sessionId = options.readSessionId?.();\n if (sessionId !== undefined && sessionId !== '' && options.writeDetail !== undefined) {\n detailSessionId = sessionId;\n options.writeDetail(sessionId, revision, buildContextDetail({ sources, skills, countTokens }));\n }\n pi.appendEntry(DOOM_CONTEXT_ENTRY_TYPE, { ...projection, revision });\n };\n\n /**\n * Reporting the composition is a convenience, so it fails quietly.\n *\n * Callers publish without awaiting, and an escaping rejection would surface\n * as an unhandled error in a live session. A panel that cannot say what the\n * toolbox costs is a far smaller problem than that.\n */\n const publish = async (): Promise<void> => {\n try {\n await build();\n } catch {\n // Left unreported on purpose: the next composition change tries again.\n }\n };\n\n return {\n publish,\n dispose: () => {\n disposed = true;\n if (detailSessionId !== undefined) options.removeDetail?.(detailSessionId);\n detailSessionId = undefined;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;AAqDA,SAAS,cAAc,QAA+E;CACpG,OAAO,OAAO,SAAS,UAAU,MAAM,OAAO,SAAS,UAAU,MAAM,MAAM,CAAC;AAChF;;;;;;;;AASA,SAAS,eAAe,UAAkB,SAA4B,mBAAsC;CAE1G,MAAM,cAAkD,EAAE,IAAA,GAD1CA,sCAAAA,gBAAAA,CACmD,CAAC,CAAC,mBAAmB;CACxF,IAAI;EACF,KAAK,MAAM,UAAA,GAASC,iCAAAA,qBAAAA,CAAqB,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,GAAG,iBAAiB,CAAC,GACrF,IAAI,MAAM,QAAQ,MAAM,QAAQ,YAAY,MAAM,QAAQ;GAAE,MAAM;GAAU,MAAM,MAAM;EAAO;CAEnG,QAAQ,CAGR;CACA,OAAO;AACT;AAEA,SAAgB,uBACd,IACA,QACA,UAAmC,CAAC,GAClB;CAClB,IAAI;CACJ,IAAI,WAAW;CACf,IAAI,WAAW;;CAEf,IAAI;CAEJ,MAAM,QAAQ,YAA2B;EACvC,IAAI,UAAU;EACd,MAAM,WAAA,GAAUD,sCAAAA,gBAAAA,CAAgB;EAChC,MAAM,cAAA,GAAaE,kCAAAA,kBAAAA,CAAkB,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;EAC5D,MAAM,WAAA,GAAUC,mCAAAA,iBAAAA,CAAiB;GAC/B,OAAO,GAAG,YAAY;GACtB,aAAa,GAAG,eAAe;GAC/B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;GACnC,sBAAsBC,mCAAAA;GACtB,6BAA6BC,mCAAAA;GAC7B,6BAA6B,cAAA,GAAaC,mCAAAA,oBAAAA,CAAoB,IAAI,QAAQ;EAC5E,CAAC;EAED,MAAM,YAAA,GAAWC,sCAAAA,YAAAA,CAAY,OAAO;EACpC,IAAI,SAAuB,CAAC;EAC5B,IAAI;GAMF,SAAS,eAAc,OAAA,GALDC,gCAAAA,kBAAAA,CAAkB;IACtC;IACA,wBAAwB,QAAQ;IAChC,mBAAA,GAAkBC,8BAAAA,4BAAAA,CAA4B,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC;GACpE,CAAC,EAAA,CAC8B,MAAM;EACvC,QAAQ,CAGR;EAEA,MAAM,cAAc,OAAA,GAAMC,gCAAAA,QAAAA,CAAQ;EAClC,MAAM,cAAA,GAAaC,0CAAAA,eAAAA,CAAe;GAChC,UAAU,WAAW;GACrB,WAAW,QAAQ;GACnB,SAAS,QAAQ,iBAAiB,KAAK,CAAC,EAAA,CAAG,KAAK,UAAU;IAAE,GAAG;IAAM,MAAM;GAAiB,EAAE;GAC9F,SAAS,QAAQ;GACjB;GACA;GACA,aAAa,eAAe,UAAU,QAAQ,SAAS,QAAQ,iBAAiB;GAChF;EACF,CAAC;EAID,MAAM,aAAa,KAAK,UAAU;GAAE,GAAG;GAAY,UAAU;EAAE,CAAC;EAChE,IAAI,eAAe,aAAa,UAAU;EAC1C,YAAY;EACZ,YAAY;EAGZ,MAAM,YAAY,QAAQ,gBAAgB;EAC1C,IAAI,cAAc,KAAA,KAAa,cAAc,MAAM,QAAQ,gBAAgB,KAAA,GAAW;GACpF,kBAAkB;GAClB,QAAQ,YAAY,WAAW,WAAA,GAAUC,sCAAAA,mBAAAA,CAAmB;IAAE;IAAS;IAAQ;GAAY,CAAC,CAAC;EAC/F;EACA,GAAG,YAAYC,0CAAAA,yBAAyB;GAAE,GAAG;GAAY;EAAS,CAAC;CACrE;;;;;;;;CASA,MAAM,UAAU,YAA2B;EACzC,IAAI;GACF,MAAM,MAAM;EACd,QAAQ,CAER;CACF;CAEA,OAAO;EACL;EACA,eAAe;GACb,WAAW;GACX,IAAI,oBAAoB,KAAA,GAAW,QAAQ,eAAe,eAAe;GACzE,kBAAkB,KAAA;EACpB;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"contextCatalog.cjs","names":["getHarnessState","resolvePluginEntries","readDoomMcpStatus","buildToolSources","extensionName","extensionPackageName","extensionToolSource","harnessRoot","buildSkillCatalog","readDoomSkillSourcesService","counter","projectContext","buildContextDetail","DOOM_CONTEXT_ENTRY_TYPE"],"sources":["../../../src/builders/cli/contextCatalog.ts"],"sourcesContent":["import { resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport { getHarnessState, harnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport { buildContextDetail } from '@agimon-ai/doompi-core/context-detail';\nimport { DOOM_CONTEXT_ENTRY_TYPE, projectContext } from '@agimon-ai/doompi-core/context-projection';\nimport { readDoomMcpStatus } from '@agimon-ai/doompi-core/mcp-status';\nimport { readDoomSkillSourcesService } from '@agimon-ai/doompi-core/skills';\nimport type { ContextItemDetail, ContextPromptStage } from '@agimon-ai/doompi-core/types-context-api';\nimport { buildSkillCatalog, counter, type SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { extensionName, extensionPackageName, extensionToolSource } from '@agimon-ai/doompi-ui/extensionName';\nimport { buildToolSources } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\n\n/**\n * Publishes what the session is composed of, and what it costs.\n *\n * Read at publish time rather than accumulated, because the answer changes for\n * reasons this module does not see: a reconnected MCP server, a domain switch,\n * a plan-mode tool swap. Reading the live inventory is cheap next to being\n * wrong about it.\n */\nexport interface ContextPublisher {\n /** Never rejects: callers fire this and forget it. */\n publish: () => Promise<void>;\n dispose: () => void;\n}\n\n/** The active minor modes at publish time, read rather than remembered. */\nexport type ReadMinorModes = () => readonly { readonly id: string; readonly label: string }[];\n\nexport interface ContextPublisherOptions {\n readMinorModes?: ReadMinorModes;\n /**\n * The session the detail file is keyed by, read at publish time.\n *\n * Undefined until a session is bound, and undefined in a host that has no\n * session at all; the projection is still journaled either way, and only the\n * click-through detail goes unwritten.\n */\n readSessionId?: () => string | undefined;\n /**\n * Where the click-through detail is left for the session API to find.\n *\n * Injected because writing it is filesystem work and this is not the layer\n * that does filesystem work. A host that supplies neither still gets the\n * projection; only the detail behind a row goes unpublished.\n */\n writeDetail?: (sessionId: string, revision: number, items: readonly ContextItemDetail[]) => void;\n removeDetail?: (sessionId: string) => void;\n /**\n * The system prompt as the session currently stands.\n *\n * Read rather than pushed, for the same reason the tool inventory is: the\n * prompt changes for reasons this module does not see. A host with no way to\n * answer returns undefined and the panel reports no prompt rather than a\n * stale one.\n */\n readSystemPrompt?: () => { readonly text: string; readonly stage: ContextPromptStage } | undefined;\n}\n\n/** Skills sit under owners, which is one level deeper than a flat list. */\nfunction flattenSkills(groups: Awaited<ReturnType<typeof buildSkillCatalog>>['groups']): SkillEntry[] {\n return groups.flatMap((group) => group.owners.flatMap((owner) => owner.skills));\n}\n\n/**\n * Package and plugin names to the mode that admitted them.\n *\n * Two halves meet here. Layer packages come from the harness, recorded when the\n * composition resolved. Domain plugins are re-resolved, because only the plugin\n * entry knows the domain that carried it.\n */\nfunction attributionFor(repoRoot: string, domains: readonly string[], pluginDirectories: readonly string[]) {\n const harness = getHarnessState();\n const attribution: Record<string, PackageAttribution> = { ...harness.packageAttribution };\n try {\n for (const entry of resolvePluginEntries(repoRoot, [...domains], [...pluginDirectories])) {\n if (entry.name && entry.domain) attribution[entry.name] = { kind: 'domain', mode: entry.domain };\n }\n } catch {\n // A domain that no longer resolves must not cost the reader the tool list.\n // Anything unmatched simply lands under core.\n }\n return attribution;\n}\n\nexport function createContextPublisher(\n pi: ExtensionAPI,\n cordis: Context,\n options: ContextPublisherOptions = {},\n): ContextPublisher {\n let published: string | undefined;\n let revision = 0;\n let disposed = false;\n /** Removed on disposal, so a session leaves nothing behind in the store. */\n let detailSessionId: string | undefined;\n\n const build = async (): Promise<void> => {\n if (disposed) return;\n const harness = getHarnessState();\n const mcpServers = readDoomMcpStatus(cordis)?.getSnapshot().servers;\n const sources = buildToolSources({\n tools: pi.getAllTools(),\n activeTools: pi.getActiveTools(),\n ...(mcpServers ? { mcpServers } : {}),\n resolveExtensionName: extensionName,\n resolveExtensionPackageName: extensionPackageName,\n resolveExtensionToolSource: (toolName) => extensionToolSource(pi, toolName),\n });\n\n const repoRoot = harnessRoot(harness);\n let skills: SkillEntry[] = [];\n try {\n const catalog = await buildSkillCatalog({\n repoRoot,\n activeSkillDirectories: harness.skillDirectories,\n extensionSources: readDoomSkillSourcesService(cordis)?.list() ?? [],\n });\n skills = flattenSkills(catalog.groups);\n } catch {\n // Tools are the larger cost and are already in hand; a skill walk that\n // fails should not take the whole figure down with it.\n }\n\n const countTokens = await counter();\n const prompt = options.readSystemPrompt?.();\n const promptCost = prompt === undefined ? undefined : { tokens: countTokens(prompt.text), stage: prompt.stage };\n const projection = projectContext({\n revision: revision + 1,\n majorMode: harness.majorMode,\n groups: (options.readMinorModes?.() ?? []).map((mode) => ({ ...mode, kind: 'minor' as const })),\n domains: harness.domains,\n sources,\n skills,\n attribution: attributionFor(repoRoot, harness.domains, harness.pluginDirectories),\n countTokens,\n ...(promptCost === undefined ? {} : { systemPrompt: promptCost }),\n });\n\n // Revision is compared out, so a republish that changed nothing is silent\n // rather than a new journal entry saying the same thing. The prompt joins by\n // its text: a reworded prompt of the same length is still a different answer.\n const serialized = JSON.stringify({ ...projection, revision: 0, promptText: prompt?.text });\n if (serialized === published || disposed) return;\n published = serialized;\n revision += 1;\n // The detail lands before the entry that invites a reader to ask for it, so\n // a click that follows the panel's update cannot outrun the file behind it.\n const sessionId = options.readSessionId?.();\n if (sessionId !== undefined && sessionId !== '' && options.writeDetail !== undefined) {\n detailSessionId = sessionId;\n options.writeDetail(\n sessionId,\n revision,\n buildContextDetail({\n sources,\n skills,\n countTokens,\n ...(prompt === undefined || promptCost === undefined\n ? {}\n : { systemPrompt: { text: prompt.text, tokens: promptCost.tokens, stage: prompt.stage } }),\n }),\n );\n }\n pi.appendEntry(DOOM_CONTEXT_ENTRY_TYPE, { ...projection, revision });\n };\n\n /**\n * Reporting the composition is a convenience, so it fails quietly.\n *\n * Callers publish without awaiting, and an escaping rejection would surface\n * as an unhandled error in a live session. A panel that cannot say what the\n * toolbox costs is a far smaller problem than that.\n */\n const publish = async (): Promise<void> => {\n try {\n await build();\n } catch {\n // Left unreported on purpose: the next composition change tries again.\n }\n };\n\n return {\n publish,\n dispose: () => {\n disposed = true;\n if (detailSessionId !== undefined) options.removeDetail?.(detailSessionId);\n detailSessionId = undefined;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;AA8DA,SAAS,cAAc,QAA+E;CACpG,OAAO,OAAO,SAAS,UAAU,MAAM,OAAO,SAAS,UAAU,MAAM,MAAM,CAAC;AAChF;;;;;;;;AASA,SAAS,eAAe,UAAkB,SAA4B,mBAAsC;CAE1G,MAAM,cAAkD,EAAE,IAAA,GAD1CA,sCAAAA,gBAAAA,CACmD,CAAC,CAAC,mBAAmB;CACxF,IAAI;EACF,KAAK,MAAM,UAAA,GAASC,iCAAAA,qBAAAA,CAAqB,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,GAAG,iBAAiB,CAAC,GACrF,IAAI,MAAM,QAAQ,MAAM,QAAQ,YAAY,MAAM,QAAQ;GAAE,MAAM;GAAU,MAAM,MAAM;EAAO;CAEnG,QAAQ,CAGR;CACA,OAAO;AACT;AAEA,SAAgB,uBACd,IACA,QACA,UAAmC,CAAC,GAClB;CAClB,IAAI;CACJ,IAAI,WAAW;CACf,IAAI,WAAW;;CAEf,IAAI;CAEJ,MAAM,QAAQ,YAA2B;EACvC,IAAI,UAAU;EACd,MAAM,WAAA,GAAUD,sCAAAA,gBAAAA,CAAgB;EAChC,MAAM,cAAA,GAAaE,kCAAAA,kBAAAA,CAAkB,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;EAC5D,MAAM,WAAA,GAAUC,mCAAAA,iBAAAA,CAAiB;GAC/B,OAAO,GAAG,YAAY;GACtB,aAAa,GAAG,eAAe;GAC/B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;GACnC,sBAAsBC,mCAAAA;GACtB,6BAA6BC,mCAAAA;GAC7B,6BAA6B,cAAA,GAAaC,mCAAAA,oBAAAA,CAAoB,IAAI,QAAQ;EAC5E,CAAC;EAED,MAAM,YAAA,GAAWC,sCAAAA,YAAAA,CAAY,OAAO;EACpC,IAAI,SAAuB,CAAC;EAC5B,IAAI;GAMF,SAAS,eAAc,OAAA,GALDC,gCAAAA,kBAAAA,CAAkB;IACtC;IACA,wBAAwB,QAAQ;IAChC,mBAAA,GAAkBC,8BAAAA,4BAAAA,CAA4B,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC;GACpE,CAAC,EAAA,CAC8B,MAAM;EACvC,QAAQ,CAGR;EAEA,MAAM,cAAc,OAAA,GAAMC,gCAAAA,QAAAA,CAAQ;EAClC,MAAM,SAAS,QAAQ,mBAAmB;EAC1C,MAAM,aAAa,WAAW,KAAA,IAAY,KAAA,IAAY;GAAE,QAAQ,YAAY,OAAO,IAAI;GAAG,OAAO,OAAO;EAAM;EAC9G,MAAM,cAAA,GAAaC,0CAAAA,eAAAA,CAAe;GAChC,UAAU,WAAW;GACrB,WAAW,QAAQ;GACnB,SAAS,QAAQ,iBAAiB,KAAK,CAAC,EAAA,CAAG,KAAK,UAAU;IAAE,GAAG;IAAM,MAAM;GAAiB,EAAE;GAC9F,SAAS,QAAQ;GACjB;GACA;GACA,aAAa,eAAe,UAAU,QAAQ,SAAS,QAAQ,iBAAiB;GAChF;GACA,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,WAAW;EACjE,CAAC;EAKD,MAAM,aAAa,KAAK,UAAU;GAAE,GAAG;GAAY,UAAU;GAAG,YAAY,QAAQ;EAAK,CAAC;EAC1F,IAAI,eAAe,aAAa,UAAU;EAC1C,YAAY;EACZ,YAAY;EAGZ,MAAM,YAAY,QAAQ,gBAAgB;EAC1C,IAAI,cAAc,KAAA,KAAa,cAAc,MAAM,QAAQ,gBAAgB,KAAA,GAAW;GACpF,kBAAkB;GAClB,QAAQ,YACN,WACA,WAAA,GACAC,sCAAAA,mBAAAA,CAAmB;IACjB;IACA;IACA;IACA,GAAI,WAAW,KAAA,KAAa,eAAe,KAAA,IACvC,CAAC,IACD,EAAE,cAAc;KAAE,MAAM,OAAO;KAAM,QAAQ,WAAW;KAAQ,OAAO,OAAO;IAAM,EAAE;GAC5F,CAAC,CACH;EACF;EACA,GAAG,YAAYC,0CAAAA,yBAAyB;GAAE,GAAG;GAAY;EAAS,CAAC;CACrE;;;;;;;;CASA,MAAM,UAAU,YAA2B;EACzC,IAAI;GACF,MAAM,MAAM;EACd,QAAQ,CAER;CACF;CAEA,OAAO;EACL;EACA,eAAe;GACb,WAAW;GACX,IAAI,oBAAoB,KAAA,GAAW,QAAQ,eAAe,eAAe;GACzE,kBAAkB,KAAA;EACpB;CACF;AACF"}
|
|
@@ -57,6 +57,11 @@ function createContextPublisher(pi, cordis, options = {}) {
|
|
|
57
57
|
})).groups);
|
|
58
58
|
} catch {}
|
|
59
59
|
const countTokens = await counter();
|
|
60
|
+
const prompt = options.readSystemPrompt?.();
|
|
61
|
+
const promptCost = prompt === void 0 ? void 0 : {
|
|
62
|
+
tokens: countTokens(prompt.text),
|
|
63
|
+
stage: prompt.stage
|
|
64
|
+
};
|
|
60
65
|
const projection = projectContext({
|
|
61
66
|
revision: revision + 1,
|
|
62
67
|
majorMode: harness.majorMode,
|
|
@@ -68,11 +73,13 @@ function createContextPublisher(pi, cordis, options = {}) {
|
|
|
68
73
|
sources,
|
|
69
74
|
skills,
|
|
70
75
|
attribution: attributionFor(repoRoot, harness.domains, harness.pluginDirectories),
|
|
71
|
-
countTokens
|
|
76
|
+
countTokens,
|
|
77
|
+
...promptCost === void 0 ? {} : { systemPrompt: promptCost }
|
|
72
78
|
});
|
|
73
79
|
const serialized = JSON.stringify({
|
|
74
80
|
...projection,
|
|
75
|
-
revision: 0
|
|
81
|
+
revision: 0,
|
|
82
|
+
promptText: prompt?.text
|
|
76
83
|
});
|
|
77
84
|
if (serialized === published || disposed) return;
|
|
78
85
|
published = serialized;
|
|
@@ -83,7 +90,12 @@ function createContextPublisher(pi, cordis, options = {}) {
|
|
|
83
90
|
options.writeDetail(sessionId, revision, buildContextDetail({
|
|
84
91
|
sources,
|
|
85
92
|
skills,
|
|
86
|
-
countTokens
|
|
93
|
+
countTokens,
|
|
94
|
+
...prompt === void 0 || promptCost === void 0 ? {} : { systemPrompt: {
|
|
95
|
+
text: prompt.text,
|
|
96
|
+
tokens: promptCost.tokens,
|
|
97
|
+
stage: prompt.stage
|
|
98
|
+
} }
|
|
87
99
|
}));
|
|
88
100
|
}
|
|
89
101
|
pi.appendEntry(DOOM_CONTEXT_ENTRY_TYPE, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextCatalog.mjs","names":[],"sources":["../../../src/builders/cli/contextCatalog.ts"],"sourcesContent":["import { resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport { getHarnessState, harnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport { buildContextDetail } from '@agimon-ai/doompi-core/context-detail';\nimport { DOOM_CONTEXT_ENTRY_TYPE, projectContext } from '@agimon-ai/doompi-core/context-projection';\nimport { readDoomMcpStatus } from '@agimon-ai/doompi-core/mcp-status';\nimport { readDoomSkillSourcesService } from '@agimon-ai/doompi-core/skills';\nimport type { ContextItemDetail } from '@agimon-ai/doompi-core/types-context-api';\nimport { buildSkillCatalog, counter, type SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { extensionName, extensionPackageName, extensionToolSource } from '@agimon-ai/doompi-ui/extensionName';\nimport { buildToolSources } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\n\n/**\n * Publishes what the session is composed of, and what it costs.\n *\n * Read at publish time rather than accumulated, because the answer changes for\n * reasons this module does not see: a reconnected MCP server, a domain switch,\n * a plan-mode tool swap. Reading the live inventory is cheap next to being\n * wrong about it.\n */\nexport interface ContextPublisher {\n /** Never rejects: callers fire this and forget it. */\n publish: () => Promise<void>;\n dispose: () => void;\n}\n\n/** The active minor modes at publish time, read rather than remembered. */\nexport type ReadMinorModes = () => readonly { readonly id: string; readonly label: string }[];\n\nexport interface ContextPublisherOptions {\n readMinorModes?: ReadMinorModes;\n /**\n * The session the detail file is keyed by, read at publish time.\n *\n * Undefined until a session is bound, and undefined in a host that has no\n * session at all; the projection is still journaled either way, and only the\n * click-through detail goes unwritten.\n */\n readSessionId?: () => string | undefined;\n /**\n * Where the click-through detail is left for the session API to find.\n *\n * Injected because writing it is filesystem work and this is not the layer\n * that does filesystem work. A host that supplies neither still gets the\n * projection; only the detail behind a row goes unpublished.\n */\n writeDetail?: (sessionId: string, revision: number, items: readonly ContextItemDetail[]) => void;\n removeDetail?: (sessionId: string) => void;\n}\n\n/** Skills sit under owners, which is one level deeper than a flat list. */\nfunction flattenSkills(groups: Awaited<ReturnType<typeof buildSkillCatalog>>['groups']): SkillEntry[] {\n return groups.flatMap((group) => group.owners.flatMap((owner) => owner.skills));\n}\n\n/**\n * Package and plugin names to the mode that admitted them.\n *\n * Two halves meet here. Layer packages come from the harness, recorded when the\n * composition resolved. Domain plugins are re-resolved, because only the plugin\n * entry knows the domain that carried it.\n */\nfunction attributionFor(repoRoot: string, domains: readonly string[], pluginDirectories: readonly string[]) {\n const harness = getHarnessState();\n const attribution: Record<string, PackageAttribution> = { ...harness.packageAttribution };\n try {\n for (const entry of resolvePluginEntries(repoRoot, [...domains], [...pluginDirectories])) {\n if (entry.name && entry.domain) attribution[entry.name] = { kind: 'domain', mode: entry.domain };\n }\n } catch {\n // A domain that no longer resolves must not cost the reader the tool list.\n // Anything unmatched simply lands under core.\n }\n return attribution;\n}\n\nexport function createContextPublisher(\n pi: ExtensionAPI,\n cordis: Context,\n options: ContextPublisherOptions = {},\n): ContextPublisher {\n let published: string | undefined;\n let revision = 0;\n let disposed = false;\n /** Removed on disposal, so a session leaves nothing behind in the store. */\n let detailSessionId: string | undefined;\n\n const build = async (): Promise<void> => {\n if (disposed) return;\n const harness = getHarnessState();\n const mcpServers = readDoomMcpStatus(cordis)?.getSnapshot().servers;\n const sources = buildToolSources({\n tools: pi.getAllTools(),\n activeTools: pi.getActiveTools(),\n ...(mcpServers ? { mcpServers } : {}),\n resolveExtensionName: extensionName,\n resolveExtensionPackageName: extensionPackageName,\n resolveExtensionToolSource: (toolName) => extensionToolSource(pi, toolName),\n });\n\n const repoRoot = harnessRoot(harness);\n let skills: SkillEntry[] = [];\n try {\n const catalog = await buildSkillCatalog({\n repoRoot,\n activeSkillDirectories: harness.skillDirectories,\n extensionSources: readDoomSkillSourcesService(cordis)?.list() ?? [],\n });\n skills = flattenSkills(catalog.groups);\n } catch {\n // Tools are the larger cost and are already in hand; a skill walk that\n // fails should not take the whole figure down with it.\n }\n\n const countTokens = await counter();\n const projection = projectContext({\n revision: revision + 1,\n majorMode: harness.majorMode,\n groups: (options.readMinorModes?.() ?? []).map((mode) => ({ ...mode, kind: 'minor' as const })),\n domains: harness.domains,\n sources,\n skills,\n attribution: attributionFor(repoRoot, harness.domains, harness.pluginDirectories),\n countTokens,\n });\n\n // Revision is compared out, so a republish that changed nothing is silent\n // rather than a new journal entry saying the same thing.\n const serialized = JSON.stringify({ ...projection, revision: 0 });\n if (serialized === published || disposed) return;\n published = serialized;\n revision += 1;\n // The detail lands before the entry that invites a reader to ask for it, so\n // a click that follows the panel's update cannot outrun the file behind it.\n const sessionId = options.readSessionId?.();\n if (sessionId !== undefined && sessionId !== '' && options.writeDetail !== undefined) {\n detailSessionId = sessionId;\n options.writeDetail(sessionId, revision, buildContextDetail({ sources, skills, countTokens }));\n }\n pi.appendEntry(DOOM_CONTEXT_ENTRY_TYPE, { ...projection, revision });\n };\n\n /**\n * Reporting the composition is a convenience, so it fails quietly.\n *\n * Callers publish without awaiting, and an escaping rejection would surface\n * as an unhandled error in a live session. A panel that cannot say what the\n * toolbox costs is a far smaller problem than that.\n */\n const publish = async (): Promise<void> => {\n try {\n await build();\n } catch {\n // Left unreported on purpose: the next composition change tries again.\n }\n };\n\n return {\n publish,\n dispose: () => {\n disposed = true;\n if (detailSessionId !== undefined) options.removeDetail?.(detailSessionId);\n detailSessionId = undefined;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;AAqDA,SAAS,cAAc,QAA+E;CACpG,OAAO,OAAO,SAAS,UAAU,MAAM,OAAO,SAAS,UAAU,MAAM,MAAM,CAAC;AAChF;;;;;;;;AASA,SAAS,eAAe,UAAkB,SAA4B,mBAAsC;CAE1G,MAAM,cAAkD,EAAE,GAD1C,gBACmD,CAAC,CAAC,mBAAmB;CACxF,IAAI;EACF,KAAK,MAAM,SAAS,qBAAqB,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,GAAG,iBAAiB,CAAC,GACrF,IAAI,MAAM,QAAQ,MAAM,QAAQ,YAAY,MAAM,QAAQ;GAAE,MAAM;GAAU,MAAM,MAAM;EAAO;CAEnG,QAAQ,CAGR;CACA,OAAO;AACT;AAEA,SAAgB,uBACd,IACA,QACA,UAAmC,CAAC,GAClB;CAClB,IAAI;CACJ,IAAI,WAAW;CACf,IAAI,WAAW;;CAEf,IAAI;CAEJ,MAAM,QAAQ,YAA2B;EACvC,IAAI,UAAU;EACd,MAAM,UAAU,gBAAgB;EAChC,MAAM,aAAa,kBAAkB,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;EAC5D,MAAM,UAAU,iBAAiB;GAC/B,OAAO,GAAG,YAAY;GACtB,aAAa,GAAG,eAAe;GAC/B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;GACnC,sBAAsB;GACtB,6BAA6B;GAC7B,6BAA6B,aAAa,oBAAoB,IAAI,QAAQ;EAC5E,CAAC;EAED,MAAM,WAAW,YAAY,OAAO;EACpC,IAAI,SAAuB,CAAC;EAC5B,IAAI;GAMF,SAAS,eAAc,MALD,kBAAkB;IACtC;IACA,wBAAwB,QAAQ;IAChC,kBAAkB,4BAA4B,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC;GACpE,CAAC,EAAA,CAC8B,MAAM;EACvC,QAAQ,CAGR;EAEA,MAAM,cAAc,MAAM,QAAQ;EAClC,MAAM,aAAa,eAAe;GAChC,UAAU,WAAW;GACrB,WAAW,QAAQ;GACnB,SAAS,QAAQ,iBAAiB,KAAK,CAAC,EAAA,CAAG,KAAK,UAAU;IAAE,GAAG;IAAM,MAAM;GAAiB,EAAE;GAC9F,SAAS,QAAQ;GACjB;GACA;GACA,aAAa,eAAe,UAAU,QAAQ,SAAS,QAAQ,iBAAiB;GAChF;EACF,CAAC;EAID,MAAM,aAAa,KAAK,UAAU;GAAE,GAAG;GAAY,UAAU;EAAE,CAAC;EAChE,IAAI,eAAe,aAAa,UAAU;EAC1C,YAAY;EACZ,YAAY;EAGZ,MAAM,YAAY,QAAQ,gBAAgB;EAC1C,IAAI,cAAc,KAAA,KAAa,cAAc,MAAM,QAAQ,gBAAgB,KAAA,GAAW;GACpF,kBAAkB;GAClB,QAAQ,YAAY,WAAW,UAAU,mBAAmB;IAAE;IAAS;IAAQ;GAAY,CAAC,CAAC;EAC/F;EACA,GAAG,YAAY,yBAAyB;GAAE,GAAG;GAAY;EAAS,CAAC;CACrE;;;;;;;;CASA,MAAM,UAAU,YAA2B;EACzC,IAAI;GACF,MAAM,MAAM;EACd,QAAQ,CAER;CACF;CAEA,OAAO;EACL;EACA,eAAe;GACb,WAAW;GACX,IAAI,oBAAoB,KAAA,GAAW,QAAQ,eAAe,eAAe;GACzE,kBAAkB,KAAA;EACpB;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"contextCatalog.mjs","names":[],"sources":["../../../src/builders/cli/contextCatalog.ts"],"sourcesContent":["import { resolvePluginEntries } from '@agimon-ai/doompi-config/domains';\nimport { getHarnessState, harnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport type { PackageAttribution } from '@agimon-ai/doompi-config/types';\nimport { buildContextDetail } from '@agimon-ai/doompi-core/context-detail';\nimport { DOOM_CONTEXT_ENTRY_TYPE, projectContext } from '@agimon-ai/doompi-core/context-projection';\nimport { readDoomMcpStatus } from '@agimon-ai/doompi-core/mcp-status';\nimport { readDoomSkillSourcesService } from '@agimon-ai/doompi-core/skills';\nimport type { ContextItemDetail, ContextPromptStage } from '@agimon-ai/doompi-core/types-context-api';\nimport { buildSkillCatalog, counter, type SkillEntry } from '@agimon-ai/doompi-skill/catalog';\nimport { extensionName, extensionPackageName, extensionToolSource } from '@agimon-ai/doompi-ui/extensionName';\nimport { buildToolSources } from '@agimon-ai/doompi-ui/toolInventory';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\n\n/**\n * Publishes what the session is composed of, and what it costs.\n *\n * Read at publish time rather than accumulated, because the answer changes for\n * reasons this module does not see: a reconnected MCP server, a domain switch,\n * a plan-mode tool swap. Reading the live inventory is cheap next to being\n * wrong about it.\n */\nexport interface ContextPublisher {\n /** Never rejects: callers fire this and forget it. */\n publish: () => Promise<void>;\n dispose: () => void;\n}\n\n/** The active minor modes at publish time, read rather than remembered. */\nexport type ReadMinorModes = () => readonly { readonly id: string; readonly label: string }[];\n\nexport interface ContextPublisherOptions {\n readMinorModes?: ReadMinorModes;\n /**\n * The session the detail file is keyed by, read at publish time.\n *\n * Undefined until a session is bound, and undefined in a host that has no\n * session at all; the projection is still journaled either way, and only the\n * click-through detail goes unwritten.\n */\n readSessionId?: () => string | undefined;\n /**\n * Where the click-through detail is left for the session API to find.\n *\n * Injected because writing it is filesystem work and this is not the layer\n * that does filesystem work. A host that supplies neither still gets the\n * projection; only the detail behind a row goes unpublished.\n */\n writeDetail?: (sessionId: string, revision: number, items: readonly ContextItemDetail[]) => void;\n removeDetail?: (sessionId: string) => void;\n /**\n * The system prompt as the session currently stands.\n *\n * Read rather than pushed, for the same reason the tool inventory is: the\n * prompt changes for reasons this module does not see. A host with no way to\n * answer returns undefined and the panel reports no prompt rather than a\n * stale one.\n */\n readSystemPrompt?: () => { readonly text: string; readonly stage: ContextPromptStage } | undefined;\n}\n\n/** Skills sit under owners, which is one level deeper than a flat list. */\nfunction flattenSkills(groups: Awaited<ReturnType<typeof buildSkillCatalog>>['groups']): SkillEntry[] {\n return groups.flatMap((group) => group.owners.flatMap((owner) => owner.skills));\n}\n\n/**\n * Package and plugin names to the mode that admitted them.\n *\n * Two halves meet here. Layer packages come from the harness, recorded when the\n * composition resolved. Domain plugins are re-resolved, because only the plugin\n * entry knows the domain that carried it.\n */\nfunction attributionFor(repoRoot: string, domains: readonly string[], pluginDirectories: readonly string[]) {\n const harness = getHarnessState();\n const attribution: Record<string, PackageAttribution> = { ...harness.packageAttribution };\n try {\n for (const entry of resolvePluginEntries(repoRoot, [...domains], [...pluginDirectories])) {\n if (entry.name && entry.domain) attribution[entry.name] = { kind: 'domain', mode: entry.domain };\n }\n } catch {\n // A domain that no longer resolves must not cost the reader the tool list.\n // Anything unmatched simply lands under core.\n }\n return attribution;\n}\n\nexport function createContextPublisher(\n pi: ExtensionAPI,\n cordis: Context,\n options: ContextPublisherOptions = {},\n): ContextPublisher {\n let published: string | undefined;\n let revision = 0;\n let disposed = false;\n /** Removed on disposal, so a session leaves nothing behind in the store. */\n let detailSessionId: string | undefined;\n\n const build = async (): Promise<void> => {\n if (disposed) return;\n const harness = getHarnessState();\n const mcpServers = readDoomMcpStatus(cordis)?.getSnapshot().servers;\n const sources = buildToolSources({\n tools: pi.getAllTools(),\n activeTools: pi.getActiveTools(),\n ...(mcpServers ? { mcpServers } : {}),\n resolveExtensionName: extensionName,\n resolveExtensionPackageName: extensionPackageName,\n resolveExtensionToolSource: (toolName) => extensionToolSource(pi, toolName),\n });\n\n const repoRoot = harnessRoot(harness);\n let skills: SkillEntry[] = [];\n try {\n const catalog = await buildSkillCatalog({\n repoRoot,\n activeSkillDirectories: harness.skillDirectories,\n extensionSources: readDoomSkillSourcesService(cordis)?.list() ?? [],\n });\n skills = flattenSkills(catalog.groups);\n } catch {\n // Tools are the larger cost and are already in hand; a skill walk that\n // fails should not take the whole figure down with it.\n }\n\n const countTokens = await counter();\n const prompt = options.readSystemPrompt?.();\n const promptCost = prompt === undefined ? undefined : { tokens: countTokens(prompt.text), stage: prompt.stage };\n const projection = projectContext({\n revision: revision + 1,\n majorMode: harness.majorMode,\n groups: (options.readMinorModes?.() ?? []).map((mode) => ({ ...mode, kind: 'minor' as const })),\n domains: harness.domains,\n sources,\n skills,\n attribution: attributionFor(repoRoot, harness.domains, harness.pluginDirectories),\n countTokens,\n ...(promptCost === undefined ? {} : { systemPrompt: promptCost }),\n });\n\n // Revision is compared out, so a republish that changed nothing is silent\n // rather than a new journal entry saying the same thing. The prompt joins by\n // its text: a reworded prompt of the same length is still a different answer.\n const serialized = JSON.stringify({ ...projection, revision: 0, promptText: prompt?.text });\n if (serialized === published || disposed) return;\n published = serialized;\n revision += 1;\n // The detail lands before the entry that invites a reader to ask for it, so\n // a click that follows the panel's update cannot outrun the file behind it.\n const sessionId = options.readSessionId?.();\n if (sessionId !== undefined && sessionId !== '' && options.writeDetail !== undefined) {\n detailSessionId = sessionId;\n options.writeDetail(\n sessionId,\n revision,\n buildContextDetail({\n sources,\n skills,\n countTokens,\n ...(prompt === undefined || promptCost === undefined\n ? {}\n : { systemPrompt: { text: prompt.text, tokens: promptCost.tokens, stage: prompt.stage } }),\n }),\n );\n }\n pi.appendEntry(DOOM_CONTEXT_ENTRY_TYPE, { ...projection, revision });\n };\n\n /**\n * Reporting the composition is a convenience, so it fails quietly.\n *\n * Callers publish without awaiting, and an escaping rejection would surface\n * as an unhandled error in a live session. A panel that cannot say what the\n * toolbox costs is a far smaller problem than that.\n */\n const publish = async (): Promise<void> => {\n try {\n await build();\n } catch {\n // Left unreported on purpose: the next composition change tries again.\n }\n };\n\n return {\n publish,\n dispose: () => {\n disposed = true;\n if (detailSessionId !== undefined) options.removeDetail?.(detailSessionId);\n detailSessionId = undefined;\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
2
|
require("../../composition/harnessState/index.cjs");
|
|
3
3
|
const require_index$1 = require("../../composition/repository/index.cjs");
|
|
4
|
+
const require_index$2 = require("../../composition/syncState/index.cjs");
|
|
4
5
|
const require_harnessContext = require("../cli/harnessContext.cjs");
|
|
6
|
+
const require_index$3 = require("../../composition/syncDrift/index.cjs");
|
|
5
7
|
const require_selectionStatus = require("./selectionStatus.cjs");
|
|
6
8
|
const require_sessionArguments = require("./sessionArguments.cjs");
|
|
7
9
|
let node_fs = require("node:fs");
|
|
@@ -53,6 +55,10 @@ async function runServerRuntime(options, runtime) {
|
|
|
53
55
|
env: baseEnvironment,
|
|
54
56
|
warn: notice
|
|
55
57
|
});
|
|
58
|
+
const openSessions = (0, _agimon_ai_doompi_core_history.createOpenSessionRegistry)({
|
|
59
|
+
directory: node_path.default.join((0, _agimon_ai_doompi_core_pi_settings.piAgentDirectory)(baseEnvironment), "server"),
|
|
60
|
+
onNotice: notice
|
|
61
|
+
});
|
|
56
62
|
let nextEventLoopTick = performance.now() + 1e3;
|
|
57
63
|
const eventLoopMonitor = setInterval(() => {
|
|
58
64
|
const now = performance.now();
|
|
@@ -78,7 +84,14 @@ async function runServerRuntime(options, runtime) {
|
|
|
78
84
|
const pendingSessions = /* @__PURE__ */ new Map();
|
|
79
85
|
const sessionArtifacts = /* @__PURE__ */ new Map();
|
|
80
86
|
let mountSessionApis;
|
|
87
|
+
/**
|
|
88
|
+
* Shutdown runs every session through the same close path a user-initiated
|
|
89
|
+
* close uses, so without this the registry would be emptied by the very event
|
|
90
|
+
* it exists to survive.
|
|
91
|
+
*/
|
|
92
|
+
let shuttingDown = false;
|
|
81
93
|
const closeManagedSession = async (sessionId) => {
|
|
94
|
+
if (!shuttingDown) openSessions.remove(sessionId);
|
|
82
95
|
const artifacts = sessionArtifacts.get(sessionId);
|
|
83
96
|
sessionArtifacts.delete(sessionId);
|
|
84
97
|
webCompositions?.remove({
|
|
@@ -155,6 +168,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
155
168
|
}),
|
|
156
169
|
createSession: (request) => openSession(request),
|
|
157
170
|
onNotice: notice,
|
|
171
|
+
hubToken: () => attachToken,
|
|
158
172
|
requestSessionApi: (scope, request) => requestSessionApi(scope, request)
|
|
159
173
|
});
|
|
160
174
|
let harnessContext;
|
|
@@ -207,6 +221,20 @@ async function runServerRuntime(options, runtime) {
|
|
|
207
221
|
active: hub.snapshot().some((session) => session.workspaceId === workspace.id)
|
|
208
222
|
})),
|
|
209
223
|
resolveRepository: (id) => hub.workspaces().find((workspace) => workspace.id === id)?.root,
|
|
224
|
+
readRepositorySync: (id) => {
|
|
225
|
+
const root = hub.workspaces().find((workspace) => workspace.id === id)?.root;
|
|
226
|
+
if (!root) return void 0;
|
|
227
|
+
const drift = require_index$3.readSyncDrift({
|
|
228
|
+
repoRoot: root,
|
|
229
|
+
homeDirectory,
|
|
230
|
+
requireWebBundle: true
|
|
231
|
+
});
|
|
232
|
+
const state = require_index$2.readSyncState(root, homeDirectory);
|
|
233
|
+
return {
|
|
234
|
+
...drift,
|
|
235
|
+
mcpProjection: state?.fileState.mcpProjection
|
|
236
|
+
};
|
|
237
|
+
},
|
|
210
238
|
onNotice: notice
|
|
211
239
|
};
|
|
212
240
|
const globalBundle = await loadComposition(globalRoot, "global");
|
|
@@ -223,9 +251,9 @@ async function runServerRuntime(options, runtime) {
|
|
|
223
251
|
headers.set("x-doompi-token", attachToken);
|
|
224
252
|
return fetch(new URL(`${from.pathname}${from.search}`, cockpit.url), new Request(request, { headers }));
|
|
225
253
|
},
|
|
226
|
-
connectProtocol: () => {
|
|
254
|
+
connectProtocol: (pathname) => {
|
|
227
255
|
if (!cockpit || !attachToken) throw new Error("The headless protocol listener is not ready.");
|
|
228
|
-
const url = new URL(
|
|
256
|
+
const url = new URL(pathname, cockpit.url);
|
|
229
257
|
url.protocol = "ws:";
|
|
230
258
|
return new ws.default(url, { headers: { "x-doompi-token": attachToken } });
|
|
231
259
|
}
|
|
@@ -245,6 +273,12 @@ async function runServerRuntime(options, runtime) {
|
|
|
245
273
|
const pending = admissions.get(id);
|
|
246
274
|
if (pending) return pending;
|
|
247
275
|
const admission = (async () => {
|
|
276
|
+
const syncEnvironment = {
|
|
277
|
+
...baseEnvironment,
|
|
278
|
+
DOOMPI_ROOT: root
|
|
279
|
+
};
|
|
280
|
+
for (const key of [_agimon_ai_doompi_config_harnessStore.HARNESS_STATE_POINTER, ...Object.values(_agimon_ai_doompi_config_harnessState.HARNESS_STATE_KEYS)]) delete syncEnvironment[key];
|
|
281
|
+
await syncWorkspace(root, syncEnvironment);
|
|
248
282
|
const bundle = await loadComposition(root, "workspace");
|
|
249
283
|
await hub.mountFacets(bundle.facets, {
|
|
250
284
|
...sharedApiContext,
|
|
@@ -335,6 +369,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
335
369
|
directEvents: hub.directEvents,
|
|
336
370
|
hubToken: token,
|
|
337
371
|
sessionService: hub.sessionService,
|
|
372
|
+
pluginRegistry: hub.pluginRegistry,
|
|
338
373
|
apis: [],
|
|
339
374
|
facets: pendingSessions.get(sessionOptions.sessionId)?.bundle.facets ?? [],
|
|
340
375
|
workspaceId: sessionOptions.workspaceId,
|
|
@@ -362,7 +397,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
362
397
|
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(request.basePath)) return Response.json({ error: "Invalid session API base path." }, { status: 400 });
|
|
363
398
|
if (!request.path.startsWith("/") || request.path.startsWith("//") || request.path.includes("#")) return Response.json({ error: "Invalid session API path." }, { status: 400 });
|
|
364
399
|
const body = request.body === null || request.body === void 0 ? void 0 : request.body;
|
|
365
|
-
return artifacts.apis.request(new Request(`http://doompi.local/api/
|
|
400
|
+
return artifacts.apis.request(new Request(`http://doompi.local/api/plugins/${request.basePath}${request.path}`, {
|
|
366
401
|
method: request.method,
|
|
367
402
|
headers: request.headers,
|
|
368
403
|
...body === void 0 ? {} : { body },
|
|
@@ -425,7 +460,16 @@ async function runServerRuntime(options, runtime) {
|
|
|
425
460
|
cleanup: () => childContext.cleanup(),
|
|
426
461
|
bundle
|
|
427
462
|
});
|
|
428
|
-
await hub.create(sessionHostOptions(childContext, bundle, identity));
|
|
463
|
+
const created = await hub.create(sessionHostOptions(childContext, bundle, identity));
|
|
464
|
+
if (created.workspaceId !== void 0) openSessions.add({
|
|
465
|
+
sessionId: created.id,
|
|
466
|
+
workspaceId: created.workspaceId,
|
|
467
|
+
cwd: created.cwd,
|
|
468
|
+
name: created.name,
|
|
469
|
+
createdAt: created.createdAt,
|
|
470
|
+
...created.parentSessionId === void 0 ? {} : { parentSessionId: created.parentSessionId },
|
|
471
|
+
...created.sessionProvenance === void 0 ? {} : { sessionProvenance: created.sessionProvenance }
|
|
472
|
+
});
|
|
429
473
|
return {
|
|
430
474
|
sessionId: identity.sessionId,
|
|
431
475
|
cwd: childContext.options.cwd
|
|
@@ -478,6 +522,20 @@ async function runServerRuntime(options, runtime) {
|
|
|
478
522
|
}, target.id);
|
|
479
523
|
return target.id;
|
|
480
524
|
},
|
|
525
|
+
dormantSessions: () => openSessions.list(),
|
|
526
|
+
reviveSession: async (record) => {
|
|
527
|
+
try {
|
|
528
|
+
await openSession({
|
|
529
|
+
cwd: record.cwd,
|
|
530
|
+
name: record.name,
|
|
531
|
+
...record.parentSessionId === void 0 ? {} : { parentSessionId: record.parentSessionId },
|
|
532
|
+
...record.sessionProvenance === void 0 ? {} : { sessionProvenance: record.sessionProvenance }
|
|
533
|
+
}, record.sessionId);
|
|
534
|
+
} catch (error) {
|
|
535
|
+
openSessions.remove(record.sessionId);
|
|
536
|
+
throw error;
|
|
537
|
+
}
|
|
538
|
+
},
|
|
481
539
|
requestAsset: (request) => webCompositions?.request(request) ?? Promise.resolve(void 0),
|
|
482
540
|
compositions: () => ({
|
|
483
541
|
global: webCompositions?.get({ scope: "global" }),
|
|
@@ -494,7 +552,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
494
552
|
telemetry,
|
|
495
553
|
onNotice: notice
|
|
496
554
|
});
|
|
497
|
-
notice(`protocol on ${cockpit.url}/api/
|
|
555
|
+
notice(`protocol on ${cockpit.url}/api/ws`);
|
|
498
556
|
let resolveShutdown;
|
|
499
557
|
const shutdown = new Promise((resolve) => {
|
|
500
558
|
resolveShutdown = resolve;
|
|
@@ -508,6 +566,7 @@ async function runServerRuntime(options, runtime) {
|
|
|
508
566
|
signal.removeEventListener("abort", stop);
|
|
509
567
|
}
|
|
510
568
|
} finally {
|
|
569
|
+
shuttingDown = true;
|
|
511
570
|
clearInterval(eventLoopMonitor);
|
|
512
571
|
await bounded(telemetry.recordEvent("doompi_server.shutdown"), "shutdown telemetry", notice);
|
|
513
572
|
await Promise.allSettled([cockpit?.close()]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.cjs","names":["createServerTelemetry","createHarnessTelemetry","createHeadlessSessionManager","publishHeadlessSelectionStatus","readMinorModeCatalog","createHeadlessHub","fs","resolveSessionIdentity","crypto","os","globalDoomConfigDirectory","createWebCompositions","path","readSyncRegistration","resolveServerBundleSource","loadMajorModesConfig","resolveLayers","loadServerBundle","createRemoteRuntime","WebSocket","findRepositoryRoot","resolveSyncLocation","HARNESS_STATE_POINTER","HARNESS_STATE_KEYS","filterHookDisabledLayers","serveSessionApis","buildHarnessContext","serveHeadlessServer","listSavedSessions","piAgentDirectory"],"sources":["../../../src/builders/server/runtime.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { filterHookDisabledLayers, loadMajorModesConfig, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { createHeadlessHub, type HeadlessHub } from '@agimon-ai/doompi-core/headless-hub';\nimport { serveHeadlessServer } from '@agimon-ai/doompi-core/headless-server';\nimport type { HeadlessSessionHost, HeadlessSessionHostOptions } from '@agimon-ai/doompi-core/headless-session-host';\nimport { createHeadlessSessionManager } from '@agimon-ai/doompi-core/headless-session-manager';\nimport { listSavedSessions } from '@agimon-ai/doompi-core/history';\nimport type {\n DoomHubSessionApiRequest,\n DoomHubSessionCreateRequest,\n DoomHubSessionScope,\n} from '@agimon-ai/doompi-core/hub-channel';\nimport { serveSessionApis, type PackageApiServer } from '@agimon-ai/doompi-core/package-api-server';\nimport { piAgentDirectory } from '@agimon-ai/doompi-core/pi-settings';\nimport { createRemoteRuntime, type RemoteRuntime } from '@agimon-ai/doompi-core/remote-runtime';\nimport type { HeadlessSessionManager } from '@agimon-ai/doompi-core/runtime-headless-session-manager';\nimport { createHarnessTelemetry } from '@agimon-ai/doompi-core/runtime-log-sink-telemetry';\nimport { loadServerBundle, resolveServerBundleSource } from '@agimon-ai/doompi-core/server-facet';\nimport { createServerTelemetry } from '@agimon-ai/doompi-core/server-telemetry';\nimport { resolveSyncLocation } from '@agimon-ai/doompi-core/sync-location';\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\nimport { createWebCompositions } from '@agimon-ai/doompi-core/web-compositions';\nimport { readMinorModeCatalog } from '@agimon-ai/doompi-minor-mode';\nimport WebSocket from 'ws';\n\nimport { HARNESS_STATE_KEYS, HARNESS_STATE_POINTER } from '../../composition/harnessState';\nimport { findRepositoryRoot } from '../../composition/repository';\nimport { buildHarnessContext } from '../cli/harnessContext';\nimport { publishHeadlessSelectionStatus } from './selectionStatus';\nimport { resolveSessionIdentity } from './sessionArguments';\nimport type { ServeOptions, ServerRuntimeEnvironment } from './types';\nconst TELEMETRY_SHUTDOWN_TIMEOUT_MS = 2_000;\n\nasync function bounded(operation: Promise<unknown>, label: string, notice: (message: string) => void): Promise<void> {\n let timeout: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n operation,\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, TELEMETRY_SHUTDOWN_TIMEOUT_MS);\n }),\n ]);\n } catch (error) {\n notice(`${label} failed: ${error instanceof Error ? error.message : String(error)}`);\n } finally {\n if (timeout) clearTimeout(timeout);\n }\n}\n\nexport async function runServerRuntime(options: ServeOptions, runtime: ServerRuntimeEnvironment): Promise<number> {\n const { cwd: baseCwd, environment: baseEnvironment, notice, resolveHarnessOptions, signal, syncWorkspace } = runtime;\n const telemetry = createServerTelemetry({ cwd: baseCwd, env: baseEnvironment, warn: notice });\n let nextEventLoopTick = performance.now() + 1_000;\n const eventLoopMonitor = setInterval(() => {\n const now = performance.now();\n const delay = Math.max(0, now - nextEventLoopTick);\n nextEventLoopTick = now + 1_000;\n if (delay >= 100)\n void telemetry\n .recordEvent('doompi_server.event_loop_delay', { duration_ms: Math.round(delay) })\n .catch((error: unknown) => notice(`event loop telemetry failed: ${String(error)}`));\n }, 1_000);\n eventLoopMonitor.unref();\n const harnessTelemetry = createHarnessTelemetry({\n cwd: baseCwd,\n env: baseEnvironment,\n warn: notice,\n deferSpans: true,\n });\n const baseSessionManager = createHeadlessSessionManager({\n publishSelectionStatus: publishHeadlessSelectionStatus,\n contextGroups: (context) =>\n (readMinorModeCatalog(context)?.list() ?? [])\n .filter(({ state }) => state.activation === 'active')\n .map(({ descriptor }) => ({ id: descriptor.id, label: descriptor.label, kind: 'minor' })),\n });\n type SessionSetup = { cleanup: () => Promise<void>; bundle: Awaited<ReturnType<typeof loadServerBundle>> };\n type SessionArtifacts = SessionSetup & { apis: PackageApiServer };\n const pendingSessions = new Map<string, SessionSetup>();\n const sessionArtifacts = new Map<string, SessionArtifacts>();\n let mountSessionApis:\n | ((options: HeadlessSessionHostOptions, host: HeadlessSessionHost) => Promise<PackageApiServer>)\n | undefined;\n\n const closeManagedSession = async (sessionId: string): Promise<void> => {\n const artifacts = sessionArtifacts.get(sessionId);\n sessionArtifacts.delete(sessionId);\n webCompositions?.remove({ scope: 'session', sessionId });\n const failures: unknown[] = [];\n try {\n await artifacts?.apis.close();\n } catch (error) {\n failures.push(error);\n }\n try {\n await artifacts?.cleanup();\n } catch (error) {\n failures.push(error);\n }\n try {\n await baseSessionManager.closeSession(sessionId);\n } catch (error) {\n failures.push(error);\n }\n if (failures.length > 0) throw new AggregateError(failures, `Session '${sessionId}' shutdown failed`);\n };\n\n const sessionManager: HeadlessSessionManager = {\n async create(sessionOptions) {\n const setup = pendingSessions.get(sessionOptions.sessionId);\n let host: HeadlessSessionHost;\n try {\n host = await baseSessionManager.create(sessionOptions);\n } catch (error) {\n if (setup !== undefined && pendingSessions.delete(sessionOptions.sessionId))\n await setup.cleanup().catch((cleanupError: unknown) => notice(String(cleanupError)));\n throw error;\n }\n if (setup === undefined || mountSessionApis === undefined) return host;\n try {\n const apis = await mountSessionApis(sessionOptions, host);\n pendingSessions.delete(sessionOptions.sessionId);\n sessionArtifacts.set(sessionOptions.sessionId, { ...setup, apis });\n return host;\n } catch (error) {\n pendingSessions.delete(sessionOptions.sessionId);\n await baseSessionManager\n .closeSession(sessionOptions.sessionId)\n .catch((closeError: unknown) => notice(String(closeError)));\n await setup.cleanup().catch((cleanupError: unknown) => notice(String(cleanupError)));\n throw error;\n }\n },\n get: (sessionId) => baseSessionManager.get(sessionId),\n sessions: () => baseSessionManager.sessions(),\n closeSession: closeManagedSession,\n async close() {\n const ids = baseSessionManager.sessions().map((session) => session.runtime.sessionId);\n const outcomes = await Promise.allSettled(ids.map((id) => closeManagedSession(id)));\n const failures = outcomes\n .filter((outcome): outcome is PromiseRejectedResult => outcome.status === 'rejected')\n .map((outcome) => outcome.reason);\n if (failures.length > 0) throw new AggregateError(failures, 'Headless session shutdown failed');\n },\n };\n\n let requestSessionApi: (\n scope: DoomHubSessionScope,\n request: DoomHubSessionApiRequest,\n ) => Promise<Response> = async () => Response.json({ error: 'Session API unavailable.' }, { status: 404 });\n let openSession: (\n request: DoomHubSessionCreateRequest,\n sessionId?: string,\n ) => Promise<DoomHubSessionScope> = async () => {\n throw new Error('The cockpit session service is not ready.');\n };\n let admitWorkspace: (root: string) => Promise<{ id: string; root: string }> = async () => {\n throw new Error('Workspace admission is not ready.');\n };\n let hub!: HeadlessHub;\n hub = createHeadlessHub({\n manager: sessionManager,\n admitWorkspace: (root) => admitWorkspace(root),\n onWorkspaceRemoved: (workspaceId) => webCompositions?.remove({ scope: 'workspace', workspaceId }),\n createSession: (request) => openSession(request),\n onNotice: notice,\n requestSessionApi: (scope, request) => requestSessionApi(scope, request),\n });\n let harnessContext: Awaited<ReturnType<typeof buildHarnessContext>> | undefined;\n let cockpit: Awaited<ReturnType<typeof serveHeadlessServer>> | undefined;\n let remoteRuntime: RemoteRuntime | undefined;\n let attachToken: string | undefined;\n let webCompositions: ReturnType<typeof createWebCompositions> | undefined;\n\n try {\n await telemetry.runInSpan('doompi_server.startup', {}, async () => {\n const token = fs.readFileSync(options.tokenFile, 'utf8').trim();\n if (!token) throw new Error('The attach token file is empty.');\n attachToken = token;\n\n const resolved = resolveSessionIdentity(options.agentArgs, {\n sessionId: options.sessionId ?? crypto.randomUUID(),\n sessionName: options.sessionName,\n });\n\n const homeDirectory = baseEnvironment.HOME ?? os.homedir();\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n webCompositions = createWebCompositions(path.join(globalRoot, 'server'), notice);\n const loadComposition = async (\n root: string,\n scope: 'global' | 'workspace' | 'session',\n selection?: { root: string; majorMode: string; activeLayers: string[] },\n ) => {\n const registration = readSyncRegistration(root, homeDirectory);\n const source = resolveServerBundleSource({ registration });\n if (source.kind !== 'descriptor')\n throw new Error(`Run the scoped DoomPi sync for '${root}' before opening it.`);\n const selected =\n selection ??\n (() => {\n const config = loadMajorModesConfig(root, homeDirectory);\n const majorMode = config.defaultMajorMode;\n return { root, majorMode, activeLayers: resolveLayers(config, majorMode) };\n })();\n return loadServerBundle(scope, {\n ...source,\n ...selected,\n retainCandidates: scope === 'session',\n onNotice: notice,\n });\n };\n const sharedApiContext = {\n homeDirectory,\n environment: baseEnvironment,\n hubToken: token,\n sessionService: hub.sessionService,\n directEvents: hub.directEvents,\n repositories: () =>\n hub.workspaces().map((workspace) => ({\n id: workspace.id,\n path: workspace.root,\n name: workspace.root.split('/').at(-1) ?? workspace.id,\n active: hub.snapshot().some((session) => session.workspaceId === workspace.id),\n })),\n resolveRepository: (id: string) => hub.workspaces().find((workspace) => workspace.id === id)?.root,\n onNotice: notice,\n };\n const globalBundle = await loadComposition(globalRoot, 'global');\n webCompositions.publishShell(readSyncRegistration(globalRoot, homeDirectory)!);\n remoteRuntime = createRemoteRuntime({\n homeDirectory,\n registrationToken: token,\n bundleTrust: () => webCompositions?.shellTrust(),\n onNotice: notice,\n forward: async (request) => {\n if (!cockpit || !attachToken)\n return Response.json({ error: 'The headless server is not ready.' }, { status: 503 });\n const from = new URL(request.url);\n const headers = new Headers(request.headers);\n headers.set('x-doompi-token', attachToken);\n return fetch(new URL(`${from.pathname}${from.search}`, cockpit.url), new Request(request, { headers }));\n },\n connectProtocol: () => {\n if (!cockpit || !attachToken) throw new Error('The headless protocol listener is not ready.');\n const url = new URL('/api/pi', cockpit.url);\n url.protocol = 'ws:';\n return new WebSocket(url, { headers: { 'x-doompi-token': attachToken } });\n },\n });\n await hub.mountFacets(globalBundle.facets, {\n ...sharedApiContext,\n scope: 'global',\n remoteControl: { fetch: (request: Request) => remoteRuntime!.fetchLocal(request) },\n });\n webCompositions.publish(\n { scope: 'global' },\n readSyncRegistration(globalRoot, homeDirectory)!,\n hub.channelTypes(),\n );\n const admissions = new Map<string, Promise<{ id: string; root: string }>>();\n admitWorkspace = async (from) => {\n const root = fs.realpathSync(findRepositoryRoot(from));\n const id = resolveSyncLocation(root, homeDirectory).identity.worktreeId;\n const existing = hub.workspaces().find((workspace) => workspace.id === id);\n if (existing) return existing;\n const pending = admissions.get(id);\n if (pending) return pending;\n const admission = (async () => {\n const bundle = await loadComposition(root, 'workspace');\n await hub.mountFacets(bundle.facets, {\n ...sharedApiContext,\n scope: 'workspace',\n workspaceId: id,\n workspaceRoot: root,\n cwd: root,\n resolveRepository: (requestedId) => (requestedId === id ? root : undefined),\n });\n webCompositions?.publish(\n { scope: 'workspace', workspaceId: id },\n readSyncRegistration(root, homeDirectory)!,\n hub.channelTypes(),\n );\n return { id, root };\n })();\n admissions.set(id, admission);\n try {\n return await admission;\n } finally {\n admissions.delete(id);\n }\n };\n\n const sessionHostOptions = (\n context: Awaited<ReturnType<typeof buildHarnessContext>>,\n bundle: Awaited<ReturnType<typeof loadServerBundle>>,\n identity: {\n sessionId: string;\n sessionName: string;\n parentSessionId?: string;\n sessionProvenance?: string;\n },\n ): HeadlessSessionHostOptions => {\n const policyOptions = context.options;\n const sessionSelection = {\n root: policyOptions.repoRoot,\n majorMode: policyOptions.majorMode,\n activeLayers: context.selectedLayers,\n domains: policyOptions.domains,\n profile: context.profile,\n state: { 'minor-mode': [] },\n };\n return {\n cwd: policyOptions.cwd,\n repoRoot: policyOptions.repoRoot,\n sessionId: identity.sessionId,\n workspaceId: resolveSyncLocation(policyOptions.repoRoot, homeDirectory).identity.worktreeId,\n sessionName: identity.sessionName,\n webComposition: webCompositions?.publish(\n { scope: 'session', sessionId: identity.sessionId },\n readSyncRegistration(policyOptions.repoRoot, homeDirectory)!,\n hub.channelTypes(),\n ),\n ...(identity.parentSessionId === undefined ? {} : { parentSessionId: identity.parentSessionId }),\n ...(identity.sessionProvenance === undefined ? {} : { sessionProvenance: identity.sessionProvenance }),\n agentArgs: policyOptions.piArgs,\n environment: Object.freeze({ ...context.environment }),\n selection: sessionSelection,\n selectionOverrides: (['majorMode', 'domains', 'profile'] as const).filter((axis) => {\n const flag = axis === 'majorMode' ? '--major-mode' : `--${axis}`;\n return (\n identity.sessionId === resolved.identity.sessionId &&\n resolved.agentArgs.some((arg) => arg === flag || arg.startsWith(`${flag}=`))\n );\n }),\n inheritedSelection: () => {\n const environment = { ...baseEnvironment };\n for (const key of [HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete environment[key];\n const defaults = resolveHarnessOptions({\n args: ['--cwd', policyOptions.cwd],\n cwd: policyOptions.cwd,\n environment,\n });\n return { majorMode: defaults.majorMode, domains: defaults.domains, profile: defaults.profile };\n },\n candidates: bundle.descriptor.entries,\n resolveSelection: (requested) => {\n const config = loadMajorModesConfig(policyOptions.repoRoot, policyOptions.homeDirectory);\n return {\n ...requested,\n activeLayers: filterHookDisabledLayers(\n config,\n resolveLayers(config, requested.majorMode),\n policyOptions.hooks,\n ),\n };\n },\n onNotice: notice,\n };\n };\n\n mountSessionApis = (sessionOptions, host) =>\n serveSessionApis({\n sessionId: sessionOptions.sessionId,\n cwd: sessionOptions.cwd,\n environment: sessionOptions.environment,\n directEvents: hub.directEvents,\n hubToken: token,\n sessionService: hub.sessionService,\n apis: [],\n facets: pendingSessions.get(sessionOptions.sessionId)?.bundle.facets ?? [],\n workspaceId: sessionOptions.workspaceId,\n workspaceRoot: sessionOptions.repoRoot,\n homeDirectory,\n mountChannel: (channel) => {\n const dispose = hub.registerChannel(channel, { scope: 'session', sessionId: sessionOptions.sessionId });\n return { mounted: true, dispose };\n },\n prepareFacets: host.prepareFacets,\n activateFacets: host.activateFacets,\n canDispatch: host.canDispatch,\n telemetry,\n onNotice: notice,\n });\n\n requestSessionApi = async (scope, request) => {\n const session = hub.session(scope.sessionId);\n const artifacts = sessionArtifacts.get(scope.sessionId);\n if (session === undefined || session.cwd !== scope.cwd || artifacts === undefined)\n return Response.json({ error: 'Session not found.' }, { status: 404 });\n if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(request.basePath))\n return Response.json({ error: 'Invalid session API base path.' }, { status: 400 });\n if (!request.path.startsWith('/') || request.path.startsWith('//') || request.path.includes('#'))\n return Response.json({ error: 'Invalid session API path.' }, { status: 400 });\n const body = request.body === null || request.body === undefined ? undefined : request.body;\n return artifacts.apis.request(\n new Request(`http://doompi.local/api/plugin/${request.basePath}${request.path}`, {\n method: request.method,\n headers: request.headers,\n ...(body === undefined ? {} : { body }),\n ...(request.signal === undefined ? {} : { signal: request.signal }),\n }),\n );\n };\n\n if (!options.noSession) {\n harnessContext = await buildHarnessContext(\n resolveHarnessOptions({ args: resolved.agentArgs, cwd: baseCwd, environment: baseEnvironment }),\n harnessTelemetry,\n );\n const activeHarnessContext = harnessContext;\n try {\n await admitWorkspace(harnessContext.options.repoRoot);\n const initialBundle = await loadComposition(harnessContext.options.repoRoot, 'session', {\n root: harnessContext.options.repoRoot,\n majorMode: harnessContext.options.majorMode,\n activeLayers: harnessContext.selectedLayers,\n });\n pendingSessions.set(resolved.identity.sessionId, {\n cleanup: () => activeHarnessContext.cleanup(),\n bundle: initialBundle,\n });\n await hub.create(sessionHostOptions(harnessContext, initialBundle, resolved.identity));\n } catch (error) {\n pendingSessions.delete(resolved.identity.sessionId);\n await activeHarnessContext.cleanup().catch((cleanupError: unknown) => notice(String(cleanupError)));\n throw error;\n }\n await bounded(harnessTelemetry.flush(), 'initial composition telemetry flush', notice);\n }\n\n openSession = async (request, sessionId) => {\n const identity = {\n sessionId: sessionId ?? crypto.randomUUID(),\n sessionName: request.name,\n parentSessionId: request.parentSessionId,\n sessionProvenance: request.sessionProvenance,\n };\n const childIdentity = resolveSessionIdentity([], identity);\n const childEnvironment = { ...baseEnvironment };\n for (const key of [HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete childEnvironment[key];\n const childContext = await buildHarnessContext(\n resolveHarnessOptions({\n args: ['--cwd', request.cwd, ...childIdentity.agentArgs],\n cwd: request.cwd,\n environment: childEnvironment,\n }),\n harnessTelemetry,\n );\n try {\n await admitWorkspace(childContext.options.repoRoot);\n const bundle = await loadComposition(childContext.options.repoRoot, 'session', {\n root: childContext.options.repoRoot,\n majorMode: childContext.options.majorMode,\n activeLayers: childContext.selectedLayers,\n });\n pendingSessions.set(identity.sessionId, { cleanup: () => childContext.cleanup(), bundle });\n await hub.create(sessionHostOptions(childContext, bundle, identity));\n return { sessionId: identity.sessionId, cwd: childContext.options.cwd };\n } catch (error) {\n pendingSessions.delete(identity.sessionId);\n await childContext.cleanup().catch((cleanupError: unknown) => notice(String(cleanupError)));\n throw error;\n }\n };\n });\n\n cockpit = await serveHeadlessServer({\n port: options.webPort,\n headlessHub: hub,\n token: attachToken,\n sessionHistory: (session) => {\n const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === session.workspaceId)?.root;\n if (!workspaceRoot) throw new Error('Session workspace not found.');\n return listSavedSessions(\n path.join(piAgentDirectory(baseEnvironment), 'server', 'sessions'),\n workspaceRoot,\n new Set(hub.snapshot().map((active) => active.id)),\n );\n },\n restartSession: async (session) => {\n const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === session.workspaceId)?.root;\n if (!workspaceRoot) throw new Error('Session workspace not found.');\n const syncEnvironment: NodeJS.ProcessEnv = { ...baseEnvironment, DOOMPI_ROOT: workspaceRoot };\n for (const key of [HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete syncEnvironment[key];\n try {\n await syncWorkspace(workspaceRoot, syncEnvironment);\n } catch (error) {\n notice(`Workspace sync failed before restart: ${error instanceof Error ? error.message : String(error)}`);\n throw new Error('Workspace sync failed; the session is still running.', { cause: error });\n }\n await hub.closeSession(session.id);\n await openSession({ cwd: session.cwd, name: session.name }, session.id);\n },\n resumeSession: async (session, targetSessionId) => {\n const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === session.workspaceId)?.root;\n if (!workspaceRoot) throw new Error('Session workspace not found.');\n const saved = await listSavedSessions(\n path.join(piAgentDirectory(baseEnvironment), 'server', 'sessions'),\n workspaceRoot,\n new Set(hub.snapshot().map((active) => active.id)),\n );\n const target = saved.find((item) => item.id === targetSessionId);\n if (!target) throw new Error('Saved Pi thread not found in this workspace.');\n await hub.closeSession(session.id);\n await openSession({ cwd: session.cwd, name: target.name ?? 'untitled' }, target.id);\n return target.id;\n },\n requestAsset: (request) => webCompositions?.request(request) ?? Promise.resolve(undefined),\n compositions: () => ({\n global: webCompositions?.get({ scope: 'global' }),\n publicKey: webCompositions?.publicKey(),\n shell: webCompositions?.shellTrust(),\n workspaces: hub.workspaces().map((workspace) => ({\n ...workspace,\n webComposition: webCompositions?.get({ scope: 'workspace', workspaceId: workspace.id }),\n })),\n }),\n telemetry,\n onNotice: notice,\n });\n notice(`protocol on ${cockpit.url}/api/pi`);\n let resolveShutdown!: (exitCode: number) => void;\n const shutdown = new Promise<number>((resolve) => {\n resolveShutdown = resolve;\n });\n const stop = (): void => resolveShutdown(0);\n signal.addEventListener('abort', stop, { once: true });\n if (signal.aborted) stop();\n try {\n return await shutdown;\n } finally {\n signal.removeEventListener('abort', stop);\n }\n } finally {\n clearInterval(eventLoopMonitor);\n await bounded(telemetry.recordEvent('doompi_server.shutdown'), 'shutdown telemetry', notice);\n await Promise.allSettled([cockpit?.close()]);\n await bounded(remoteRuntime?.close() ?? Promise.resolve(), 'remote control shutdown', notice);\n try {\n await hub.close();\n } catch (error) {\n notice(error instanceof Error ? error.message : String(error));\n }\n try {\n await sessionManager.close();\n } catch (error) {\n notice(error instanceof Error ? error.message : String(error));\n }\n webCompositions?.close();\n const pendingCleanups = [...pendingSessions.values()].map((setup) => setup.cleanup());\n pendingSessions.clear();\n await Promise.allSettled(pendingCleanups);\n await bounded(harnessTelemetry.flush(), 'harness telemetry flush', notice);\n await bounded(harnessTelemetry.shutdown(), 'harness telemetry shutdown', notice);\n await bounded(telemetry.flush(), 'server telemetry flush', notice);\n await bounded(telemetry.shutdown(), 'server telemetry shutdown', notice);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.cjs","names":["createServerTelemetry","createOpenSessionRegistry","path","piAgentDirectory","createHarnessTelemetry","createHeadlessSessionManager","publishHeadlessSelectionStatus","readMinorModeCatalog","createHeadlessHub","fs","resolveSessionIdentity","crypto","os","globalDoomConfigDirectory","createWebCompositions","readSyncRegistration","resolveServerBundleSource","loadMajorModesConfig","resolveLayers","loadServerBundle","readSyncDrift","readSyncState","createRemoteRuntime","WebSocket","findRepositoryRoot","resolveSyncLocation","HARNESS_STATE_POINTER","HARNESS_STATE_KEYS","filterHookDisabledLayers","serveSessionApis","buildHarnessContext","serveHeadlessServer","listSavedSessions"],"sources":["../../../src/builders/server/runtime.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { filterHookDisabledLayers, loadMajorModesConfig, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport { createHeadlessHub, type HeadlessHub } from '@agimon-ai/doompi-core/headless-hub';\nimport { serveHeadlessServer } from '@agimon-ai/doompi-core/headless-server';\nimport type { HeadlessSessionHost, HeadlessSessionHostOptions } from '@agimon-ai/doompi-core/headless-session-host';\nimport { createHeadlessSessionManager } from '@agimon-ai/doompi-core/headless-session-manager';\nimport { createOpenSessionRegistry, listSavedSessions } from '@agimon-ai/doompi-core/history';\nimport type { OpenSessionRecord } from '@agimon-ai/doompi-core/history';\nimport type {\n DoomHubSessionApiRequest,\n DoomHubSessionCreateRequest,\n DoomHubSessionScope,\n} from '@agimon-ai/doompi-core/hub-channel';\nimport { serveSessionApis, type PackageApiServer } from '@agimon-ai/doompi-core/package-api-server';\nimport { piAgentDirectory } from '@agimon-ai/doompi-core/pi-settings';\nimport { createRemoteRuntime, type RemoteRuntime } from '@agimon-ai/doompi-core/remote-runtime';\nimport type { HeadlessSessionManager } from '@agimon-ai/doompi-core/runtime-headless-session-manager';\nimport { createHarnessTelemetry } from '@agimon-ai/doompi-core/runtime-log-sink-telemetry';\nimport { loadServerBundle, resolveServerBundleSource } from '@agimon-ai/doompi-core/server-facet';\nimport { createServerTelemetry } from '@agimon-ai/doompi-core/server-telemetry';\nimport { resolveSyncLocation } from '@agimon-ai/doompi-core/sync-location';\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\nimport { createWebCompositions } from '@agimon-ai/doompi-core/web-compositions';\nimport { readMinorModeCatalog } from '@agimon-ai/doompi-minor-mode';\nimport WebSocket from 'ws';\n\nimport { HARNESS_STATE_KEYS, HARNESS_STATE_POINTER } from '../../composition/harnessState';\nimport { findRepositoryRoot } from '../../composition/repository';\nimport { readSyncDrift } from '../../composition/syncDrift';\nimport { readSyncState } from '../../composition/syncState';\nimport { buildHarnessContext } from '../cli/harnessContext';\nimport { publishHeadlessSelectionStatus } from './selectionStatus';\nimport { resolveSessionIdentity } from './sessionArguments';\nimport type { ServeOptions, ServerRuntimeEnvironment } from './types';\nconst TELEMETRY_SHUTDOWN_TIMEOUT_MS = 2_000;\n\nasync function bounded(operation: Promise<unknown>, label: string, notice: (message: string) => void): Promise<void> {\n let timeout: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n operation,\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, TELEMETRY_SHUTDOWN_TIMEOUT_MS);\n }),\n ]);\n } catch (error) {\n notice(`${label} failed: ${error instanceof Error ? error.message : String(error)}`);\n } finally {\n if (timeout) clearTimeout(timeout);\n }\n}\n\nexport async function runServerRuntime(options: ServeOptions, runtime: ServerRuntimeEnvironment): Promise<number> {\n const { cwd: baseCwd, environment: baseEnvironment, notice, resolveHarnessOptions, signal, syncWorkspace } = runtime;\n const telemetry = createServerTelemetry({ cwd: baseCwd, env: baseEnvironment, warn: notice });\n // Beside the journals it names, because a record pointing at a sessions\n // directory it is not stored next to is a record that can outlive its target.\n const openSessions = createOpenSessionRegistry({\n directory: path.join(piAgentDirectory(baseEnvironment), 'server'),\n onNotice: notice,\n });\n let nextEventLoopTick = performance.now() + 1_000;\n const eventLoopMonitor = setInterval(() => {\n const now = performance.now();\n const delay = Math.max(0, now - nextEventLoopTick);\n nextEventLoopTick = now + 1_000;\n if (delay >= 100)\n void telemetry\n .recordEvent('doompi_server.event_loop_delay', { duration_ms: Math.round(delay) })\n .catch((error: unknown) => notice(`event loop telemetry failed: ${String(error)}`));\n }, 1_000);\n eventLoopMonitor.unref();\n const harnessTelemetry = createHarnessTelemetry({\n cwd: baseCwd,\n env: baseEnvironment,\n warn: notice,\n deferSpans: true,\n });\n const baseSessionManager = createHeadlessSessionManager({\n publishSelectionStatus: publishHeadlessSelectionStatus,\n contextGroups: (context) =>\n (readMinorModeCatalog(context)?.list() ?? [])\n .filter(({ state }) => state.activation === 'active')\n .map(({ descriptor }) => ({ id: descriptor.id, label: descriptor.label, kind: 'minor' })),\n });\n type SessionSetup = { cleanup: () => Promise<void>; bundle: Awaited<ReturnType<typeof loadServerBundle>> };\n type SessionArtifacts = SessionSetup & { apis: PackageApiServer };\n const pendingSessions = new Map<string, SessionSetup>();\n const sessionArtifacts = new Map<string, SessionArtifacts>();\n let mountSessionApis:\n | ((options: HeadlessSessionHostOptions, host: HeadlessSessionHost) => Promise<PackageApiServer>)\n | undefined;\n /**\n * Shutdown runs every session through the same close path a user-initiated\n * close uses, so without this the registry would be emptied by the very event\n * it exists to survive.\n */\n let shuttingDown = false;\n\n const closeManagedSession = async (sessionId: string): Promise<void> => {\n if (!shuttingDown) openSessions.remove(sessionId);\n const artifacts = sessionArtifacts.get(sessionId);\n sessionArtifacts.delete(sessionId);\n webCompositions?.remove({ scope: 'session', sessionId });\n const failures: unknown[] = [];\n try {\n await artifacts?.apis.close();\n } catch (error) {\n failures.push(error);\n }\n try {\n await artifacts?.cleanup();\n } catch (error) {\n failures.push(error);\n }\n try {\n await baseSessionManager.closeSession(sessionId);\n } catch (error) {\n failures.push(error);\n }\n if (failures.length > 0) throw new AggregateError(failures, `Session '${sessionId}' shutdown failed`);\n };\n\n const sessionManager: HeadlessSessionManager = {\n async create(sessionOptions) {\n const setup = pendingSessions.get(sessionOptions.sessionId);\n let host: HeadlessSessionHost;\n try {\n host = await baseSessionManager.create(sessionOptions);\n } catch (error) {\n if (setup !== undefined && pendingSessions.delete(sessionOptions.sessionId))\n await setup.cleanup().catch((cleanupError: unknown) => notice(String(cleanupError)));\n throw error;\n }\n if (setup === undefined || mountSessionApis === undefined) return host;\n try {\n const apis = await mountSessionApis(sessionOptions, host);\n pendingSessions.delete(sessionOptions.sessionId);\n sessionArtifacts.set(sessionOptions.sessionId, { ...setup, apis });\n return host;\n } catch (error) {\n pendingSessions.delete(sessionOptions.sessionId);\n await baseSessionManager\n .closeSession(sessionOptions.sessionId)\n .catch((closeError: unknown) => notice(String(closeError)));\n await setup.cleanup().catch((cleanupError: unknown) => notice(String(cleanupError)));\n throw error;\n }\n },\n get: (sessionId) => baseSessionManager.get(sessionId),\n sessions: () => baseSessionManager.sessions(),\n closeSession: closeManagedSession,\n async close() {\n const ids = baseSessionManager.sessions().map((session) => session.runtime.sessionId);\n const outcomes = await Promise.allSettled(ids.map((id) => closeManagedSession(id)));\n const failures = outcomes\n .filter((outcome): outcome is PromiseRejectedResult => outcome.status === 'rejected')\n .map((outcome) => outcome.reason);\n if (failures.length > 0) throw new AggregateError(failures, 'Headless session shutdown failed');\n },\n };\n\n let requestSessionApi: (\n scope: DoomHubSessionScope,\n request: DoomHubSessionApiRequest,\n ) => Promise<Response> = async () => Response.json({ error: 'Session API unavailable.' }, { status: 404 });\n let openSession: (\n request: DoomHubSessionCreateRequest,\n sessionId?: string,\n ) => Promise<DoomHubSessionScope> = async () => {\n throw new Error('The cockpit session service is not ready.');\n };\n let admitWorkspace: (root: string) => Promise<{ id: string; root: string }> = async () => {\n throw new Error('Workspace admission is not ready.');\n };\n let hub!: HeadlessHub;\n hub = createHeadlessHub({\n manager: sessionManager,\n admitWorkspace: (root) => admitWorkspace(root),\n onWorkspaceRemoved: (workspaceId) => webCompositions?.remove({ scope: 'workspace', workspaceId }),\n createSession: (request) => openSession(request),\n onNotice: notice,\n hubToken: () => attachToken,\n requestSessionApi: (scope, request) => requestSessionApi(scope, request),\n });\n let harnessContext: Awaited<ReturnType<typeof buildHarnessContext>> | undefined;\n let cockpit: Awaited<ReturnType<typeof serveHeadlessServer>> | undefined;\n let remoteRuntime: RemoteRuntime | undefined;\n let attachToken: string | undefined;\n let webCompositions: ReturnType<typeof createWebCompositions> | undefined;\n\n try {\n await telemetry.runInSpan('doompi_server.startup', {}, async () => {\n const token = fs.readFileSync(options.tokenFile, 'utf8').trim();\n if (!token) throw new Error('The attach token file is empty.');\n attachToken = token;\n\n const resolved = resolveSessionIdentity(options.agentArgs, {\n sessionId: options.sessionId ?? crypto.randomUUID(),\n sessionName: options.sessionName,\n });\n\n const homeDirectory = baseEnvironment.HOME ?? os.homedir();\n const globalRoot = globalDoomConfigDirectory(homeDirectory);\n webCompositions = createWebCompositions(path.join(globalRoot, 'server'), notice);\n const loadComposition = async (\n root: string,\n scope: 'global' | 'workspace' | 'session',\n selection?: { root: string; majorMode: string; activeLayers: string[] },\n ) => {\n const registration = readSyncRegistration(root, homeDirectory);\n const source = resolveServerBundleSource({ registration });\n if (source.kind !== 'descriptor')\n throw new Error(`Run the scoped DoomPi sync for '${root}' before opening it.`);\n const selected =\n selection ??\n (() => {\n const config = loadMajorModesConfig(root, homeDirectory);\n const majorMode = config.defaultMajorMode;\n return { root, majorMode, activeLayers: resolveLayers(config, majorMode) };\n })();\n return loadServerBundle(scope, {\n ...source,\n ...selected,\n retainCandidates: scope === 'session',\n onNotice: notice,\n });\n };\n const sharedApiContext = {\n homeDirectory,\n environment: baseEnvironment,\n hubToken: token,\n sessionService: hub.sessionService,\n directEvents: hub.directEvents,\n repositories: () =>\n hub.workspaces().map((workspace) => ({\n id: workspace.id,\n path: workspace.root,\n name: workspace.root.split('/').at(-1) ?? workspace.id,\n active: hub.snapshot().some((session) => session.workspaceId === workspace.id),\n })),\n resolveRepository: (id: string) => hub.workspaces().find((workspace) => workspace.id === id)?.root,\n readRepositorySync: (id: string) => {\n const root = hub.workspaces().find((workspace) => workspace.id === id)?.root;\n if (!root) return undefined;\n const drift = readSyncDrift({ repoRoot: root, homeDirectory, requireWebBundle: true });\n const state = readSyncState(root, homeDirectory);\n return { ...drift, mcpProjection: state?.fileState.mcpProjection };\n },\n onNotice: notice,\n };\n const globalBundle = await loadComposition(globalRoot, 'global');\n webCompositions.publishShell(readSyncRegistration(globalRoot, homeDirectory)!);\n remoteRuntime = createRemoteRuntime({\n homeDirectory,\n registrationToken: token,\n bundleTrust: () => webCompositions?.shellTrust(),\n onNotice: notice,\n forward: async (request) => {\n if (!cockpit || !attachToken)\n return Response.json({ error: 'The headless server is not ready.' }, { status: 503 });\n const from = new URL(request.url);\n const headers = new Headers(request.headers);\n headers.set('x-doompi-token', attachToken);\n return fetch(new URL(`${from.pathname}${from.search}`, cockpit.url), new Request(request, { headers }));\n },\n connectProtocol: (pathname) => {\n if (!cockpit || !attachToken) throw new Error('The headless protocol listener is not ready.');\n const url = new URL(pathname, cockpit.url);\n url.protocol = 'ws:';\n return new WebSocket(url, { headers: { 'x-doompi-token': attachToken } });\n },\n });\n await hub.mountFacets(globalBundle.facets, {\n ...sharedApiContext,\n scope: 'global',\n remoteControl: { fetch: (request: Request) => remoteRuntime!.fetchLocal(request) },\n });\n webCompositions.publish(\n { scope: 'global' },\n readSyncRegistration(globalRoot, homeDirectory)!,\n hub.channelTypes(),\n );\n const admissions = new Map<string, Promise<{ id: string; root: string }>>();\n admitWorkspace = async (from) => {\n const root = fs.realpathSync(findRepositoryRoot(from));\n const id = resolveSyncLocation(root, homeDirectory).identity.worktreeId;\n const existing = hub.workspaces().find((workspace) => workspace.id === id);\n if (existing) return existing;\n const pending = admissions.get(id);\n if (pending) return pending;\n const admission = (async () => {\n const syncEnvironment: NodeJS.ProcessEnv = { ...baseEnvironment, DOOMPI_ROOT: root };\n for (const key of [HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete syncEnvironment[key];\n await syncWorkspace(root, syncEnvironment);\n const bundle = await loadComposition(root, 'workspace');\n await hub.mountFacets(bundle.facets, {\n ...sharedApiContext,\n scope: 'workspace',\n workspaceId: id,\n workspaceRoot: root,\n cwd: root,\n resolveRepository: (requestedId) => (requestedId === id ? root : undefined),\n });\n webCompositions?.publish(\n { scope: 'workspace', workspaceId: id },\n readSyncRegistration(root, homeDirectory)!,\n hub.channelTypes(),\n );\n return { id, root };\n })();\n admissions.set(id, admission);\n try {\n return await admission;\n } finally {\n admissions.delete(id);\n }\n };\n\n const sessionHostOptions = (\n context: Awaited<ReturnType<typeof buildHarnessContext>>,\n bundle: Awaited<ReturnType<typeof loadServerBundle>>,\n identity: {\n sessionId: string;\n sessionName: string;\n parentSessionId?: string;\n sessionProvenance?: string;\n },\n ): HeadlessSessionHostOptions => {\n const policyOptions = context.options;\n const sessionSelection = {\n root: policyOptions.repoRoot,\n majorMode: policyOptions.majorMode,\n activeLayers: context.selectedLayers,\n domains: policyOptions.domains,\n profile: context.profile,\n state: { 'minor-mode': [] },\n };\n return {\n cwd: policyOptions.cwd,\n repoRoot: policyOptions.repoRoot,\n sessionId: identity.sessionId,\n workspaceId: resolveSyncLocation(policyOptions.repoRoot, homeDirectory).identity.worktreeId,\n sessionName: identity.sessionName,\n webComposition: webCompositions?.publish(\n { scope: 'session', sessionId: identity.sessionId },\n readSyncRegistration(policyOptions.repoRoot, homeDirectory)!,\n hub.channelTypes(),\n ),\n ...(identity.parentSessionId === undefined ? {} : { parentSessionId: identity.parentSessionId }),\n ...(identity.sessionProvenance === undefined ? {} : { sessionProvenance: identity.sessionProvenance }),\n agentArgs: policyOptions.piArgs,\n environment: Object.freeze({ ...context.environment }),\n selection: sessionSelection,\n selectionOverrides: (['majorMode', 'domains', 'profile'] as const).filter((axis) => {\n const flag = axis === 'majorMode' ? '--major-mode' : `--${axis}`;\n return (\n identity.sessionId === resolved.identity.sessionId &&\n resolved.agentArgs.some((arg) => arg === flag || arg.startsWith(`${flag}=`))\n );\n }),\n inheritedSelection: () => {\n const environment = { ...baseEnvironment };\n for (const key of [HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete environment[key];\n const defaults = resolveHarnessOptions({\n args: ['--cwd', policyOptions.cwd],\n cwd: policyOptions.cwd,\n environment,\n });\n return { majorMode: defaults.majorMode, domains: defaults.domains, profile: defaults.profile };\n },\n candidates: bundle.descriptor.entries,\n resolveSelection: (requested) => {\n const config = loadMajorModesConfig(policyOptions.repoRoot, policyOptions.homeDirectory);\n return {\n ...requested,\n activeLayers: filterHookDisabledLayers(\n config,\n resolveLayers(config, requested.majorMode),\n policyOptions.hooks,\n ),\n };\n },\n onNotice: notice,\n };\n };\n\n mountSessionApis = (sessionOptions, host) =>\n serveSessionApis({\n sessionId: sessionOptions.sessionId,\n cwd: sessionOptions.cwd,\n environment: sessionOptions.environment,\n directEvents: hub.directEvents,\n hubToken: token,\n sessionService: hub.sessionService,\n pluginRegistry: hub.pluginRegistry,\n apis: [],\n facets: pendingSessions.get(sessionOptions.sessionId)?.bundle.facets ?? [],\n workspaceId: sessionOptions.workspaceId,\n workspaceRoot: sessionOptions.repoRoot,\n homeDirectory,\n mountChannel: (channel) => {\n const dispose = hub.registerChannel(channel, { scope: 'session', sessionId: sessionOptions.sessionId });\n return { mounted: true, dispose };\n },\n prepareFacets: host.prepareFacets,\n activateFacets: host.activateFacets,\n canDispatch: host.canDispatch,\n telemetry,\n onNotice: notice,\n });\n\n requestSessionApi = async (scope, request) => {\n const session = hub.session(scope.sessionId);\n const artifacts = sessionArtifacts.get(scope.sessionId);\n if (session === undefined || session.cwd !== scope.cwd || artifacts === undefined)\n return Response.json({ error: 'Session not found.' }, { status: 404 });\n if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(request.basePath))\n return Response.json({ error: 'Invalid session API base path.' }, { status: 400 });\n if (!request.path.startsWith('/') || request.path.startsWith('//') || request.path.includes('#'))\n return Response.json({ error: 'Invalid session API path.' }, { status: 400 });\n const body = request.body === null || request.body === undefined ? undefined : request.body;\n return artifacts.apis.request(\n new Request(`http://doompi.local/api/plugins/${request.basePath}${request.path}`, {\n method: request.method,\n headers: request.headers,\n ...(body === undefined ? {} : { body }),\n ...(request.signal === undefined ? {} : { signal: request.signal }),\n }),\n );\n };\n\n if (!options.noSession) {\n harnessContext = await buildHarnessContext(\n resolveHarnessOptions({ args: resolved.agentArgs, cwd: baseCwd, environment: baseEnvironment }),\n harnessTelemetry,\n );\n const activeHarnessContext = harnessContext;\n try {\n await admitWorkspace(harnessContext.options.repoRoot);\n const initialBundle = await loadComposition(harnessContext.options.repoRoot, 'session', {\n root: harnessContext.options.repoRoot,\n majorMode: harnessContext.options.majorMode,\n activeLayers: harnessContext.selectedLayers,\n });\n pendingSessions.set(resolved.identity.sessionId, {\n cleanup: () => activeHarnessContext.cleanup(),\n bundle: initialBundle,\n });\n await hub.create(sessionHostOptions(harnessContext, initialBundle, resolved.identity));\n } catch (error) {\n pendingSessions.delete(resolved.identity.sessionId);\n await activeHarnessContext.cleanup().catch((cleanupError: unknown) => notice(String(cleanupError)));\n throw error;\n }\n await bounded(harnessTelemetry.flush(), 'initial composition telemetry flush', notice);\n }\n\n openSession = async (request, sessionId) => {\n const identity = {\n sessionId: sessionId ?? crypto.randomUUID(),\n sessionName: request.name,\n parentSessionId: request.parentSessionId,\n sessionProvenance: request.sessionProvenance,\n };\n const childIdentity = resolveSessionIdentity([], identity);\n const childEnvironment = { ...baseEnvironment };\n for (const key of [HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete childEnvironment[key];\n const childContext = await buildHarnessContext(\n resolveHarnessOptions({\n args: ['--cwd', request.cwd, ...childIdentity.agentArgs],\n cwd: request.cwd,\n environment: childEnvironment,\n }),\n harnessTelemetry,\n );\n try {\n await admitWorkspace(childContext.options.repoRoot);\n const bundle = await loadComposition(childContext.options.repoRoot, 'session', {\n root: childContext.options.repoRoot,\n majorMode: childContext.options.majorMode,\n activeLayers: childContext.selectedLayers,\n });\n pendingSessions.set(identity.sessionId, { cleanup: () => childContext.cleanup(), bundle });\n const created = await hub.create(sessionHostOptions(childContext, bundle, identity));\n // Recorded only once the session is live, and only with a workspace,\n // because a record the revive route cannot address is a rail card that\n // never wakes.\n if (created.workspaceId !== undefined) {\n openSessions.add({\n sessionId: created.id,\n workspaceId: created.workspaceId,\n cwd: created.cwd,\n name: created.name,\n createdAt: created.createdAt,\n ...(created.parentSessionId === undefined ? {} : { parentSessionId: created.parentSessionId }),\n ...(created.sessionProvenance === undefined ? {} : { sessionProvenance: created.sessionProvenance }),\n });\n }\n return { sessionId: identity.sessionId, cwd: childContext.options.cwd };\n } catch (error) {\n pendingSessions.delete(identity.sessionId);\n await childContext.cleanup().catch((cleanupError: unknown) => notice(String(cleanupError)));\n throw error;\n }\n };\n });\n\n cockpit = await serveHeadlessServer({\n port: options.webPort,\n headlessHub: hub,\n token: attachToken,\n sessionHistory: (session) => {\n const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === session.workspaceId)?.root;\n if (!workspaceRoot) throw new Error('Session workspace not found.');\n return listSavedSessions(\n path.join(piAgentDirectory(baseEnvironment), 'server', 'sessions'),\n workspaceRoot,\n new Set(hub.snapshot().map((active) => active.id)),\n );\n },\n restartSession: async (session) => {\n const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === session.workspaceId)?.root;\n if (!workspaceRoot) throw new Error('Session workspace not found.');\n const syncEnvironment: NodeJS.ProcessEnv = { ...baseEnvironment, DOOMPI_ROOT: workspaceRoot };\n for (const key of [HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete syncEnvironment[key];\n try {\n await syncWorkspace(workspaceRoot, syncEnvironment);\n } catch (error) {\n notice(`Workspace sync failed before restart: ${error instanceof Error ? error.message : String(error)}`);\n throw new Error('Workspace sync failed; the session is still running.', { cause: error });\n }\n await hub.closeSession(session.id);\n await openSession({ cwd: session.cwd, name: session.name }, session.id);\n },\n resumeSession: async (session, targetSessionId) => {\n const workspaceRoot = hub.workspaces().find((workspace) => workspace.id === session.workspaceId)?.root;\n if (!workspaceRoot) throw new Error('Session workspace not found.');\n const saved = await listSavedSessions(\n path.join(piAgentDirectory(baseEnvironment), 'server', 'sessions'),\n workspaceRoot,\n new Set(hub.snapshot().map((active) => active.id)),\n );\n const target = saved.find((item) => item.id === targetSessionId);\n if (!target) throw new Error('Saved Pi thread not found in this workspace.');\n await hub.closeSession(session.id);\n await openSession({ cwd: session.cwd, name: target.name ?? 'untitled' }, target.id);\n return target.id;\n },\n dormantSessions: () => openSessions.list(),\n reviveSession: async (record: OpenSessionRecord) => {\n try {\n await openSession(\n {\n cwd: record.cwd,\n name: record.name,\n ...(record.parentSessionId === undefined ? {} : { parentSessionId: record.parentSessionId }),\n ...(record.sessionProvenance === undefined ? {} : { sessionProvenance: record.sessionProvenance }),\n },\n record.sessionId,\n );\n } catch (error) {\n // A record whose journal or worktree is gone will fail the same way on\n // every attempt, so it is dropped rather than left offering a card\n // that cannot wake.\n openSessions.remove(record.sessionId);\n throw error;\n }\n },\n requestAsset: (request) => webCompositions?.request(request) ?? Promise.resolve(undefined),\n compositions: () => ({\n global: webCompositions?.get({ scope: 'global' }),\n publicKey: webCompositions?.publicKey(),\n shell: webCompositions?.shellTrust(),\n workspaces: hub.workspaces().map((workspace) => ({\n ...workspace,\n webComposition: webCompositions?.get({ scope: 'workspace', workspaceId: workspace.id }),\n })),\n }),\n telemetry,\n onNotice: notice,\n });\n notice(`protocol on ${cockpit.url}/api/ws`);\n let resolveShutdown!: (exitCode: number) => void;\n const shutdown = new Promise<number>((resolve) => {\n resolveShutdown = resolve;\n });\n const stop = (): void => resolveShutdown(0);\n signal.addEventListener('abort', stop, { once: true });\n if (signal.aborted) stop();\n try {\n return await shutdown;\n } finally {\n signal.removeEventListener('abort', stop);\n }\n } finally {\n shuttingDown = true;\n clearInterval(eventLoopMonitor);\n await bounded(telemetry.recordEvent('doompi_server.shutdown'), 'shutdown telemetry', notice);\n await Promise.allSettled([cockpit?.close()]);\n await bounded(remoteRuntime?.close() ?? Promise.resolve(), 'remote control shutdown', notice);\n try {\n await hub.close();\n } catch (error) {\n notice(error instanceof Error ? error.message : String(error));\n }\n try {\n await sessionManager.close();\n } catch (error) {\n notice(error instanceof Error ? error.message : String(error));\n }\n webCompositions?.close();\n const pendingCleanups = [...pendingSessions.values()].map((setup) => setup.cleanup());\n pendingSessions.clear();\n await Promise.allSettled(pendingCleanups);\n await bounded(harnessTelemetry.flush(), 'harness telemetry flush', notice);\n await bounded(harnessTelemetry.shutdown(), 'harness telemetry shutdown', notice);\n await bounded(telemetry.flush(), 'server telemetry flush', notice);\n await bounded(telemetry.shutdown(), 'server telemetry shutdown', notice);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAM,gCAAgC;AAEtC,eAAe,QAAQ,WAA6B,OAAe,QAAkD;CACnH,IAAI;CACJ,IAAI;EACF,MAAM,QAAQ,KAAK,CACjB,WACA,IAAI,SAAe,YAAY;GAC7B,UAAU,WAAW,SAAS,6BAA6B;EAC7D,CAAC,CACH,CAAC;CACH,SAAS,OAAO;EACd,OAAO,GAAG,MAAM,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;CACrF,UAAU;EACR,IAAI,SAAS,aAAa,OAAO;CACnC;AACF;AAEA,eAAsB,iBAAiB,SAAuB,SAAoD;CAChH,MAAM,EAAE,KAAK,SAAS,aAAa,iBAAiB,QAAQ,uBAAuB,QAAQ,kBAAkB;CAC7G,MAAM,aAAA,GAAYA,wCAAAA,sBAAAA,CAAsB;EAAE,KAAK;EAAS,KAAK;EAAiB,MAAM;CAAO,CAAC;CAG5F,MAAM,gBAAA,GAAeC,+BAAAA,0BAAAA,CAA0B;EAC7C,WAAWC,UAAAA,QAAK,MAAA,GAAKC,mCAAAA,iBAAAA,CAAiB,eAAe,GAAG,QAAQ;EAChE,UAAU;CACZ,CAAC;CACD,IAAI,oBAAoB,YAAY,IAAI,IAAI;CAC5C,MAAM,mBAAmB,kBAAkB;EACzC,MAAM,MAAM,YAAY,IAAI;EAC5B,MAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,iBAAiB;EACjD,oBAAoB,MAAM;EAC1B,IAAI,SAAS,KACX,UACG,YAAY,kCAAkC,EAAE,aAAa,KAAK,MAAM,KAAK,EAAE,CAAC,CAAC,CACjF,OAAO,UAAmB,OAAO,gCAAgC,OAAO,KAAK,GAAG,CAAC;CACxF,GAAG,GAAK;CACR,iBAAiB,MAAM;CACvB,MAAM,oBAAA,GAAmBC,kDAAAA,uBAAAA,CAAuB;EAC9C,KAAK;EACL,KAAK;EACL,MAAM;EACN,YAAY;CACd,CAAC;CACD,MAAM,sBAAA,GAAqBC,gDAAAA,6BAAAA,CAA6B;EACtD,wBAAwBC,wBAAAA;EACxB,gBAAgB,cAAA,GACbC,6BAAAA,qBAAAA,CAAqB,OAAO,CAAC,EAAE,KAAK,KAAK,CAAC,EAAA,CACxC,QAAQ,EAAE,YAAY,MAAM,eAAe,QAAQ,CAAC,CACpD,KAAK,EAAE,kBAAkB;GAAE,IAAI,WAAW;GAAI,OAAO,WAAW;GAAO,MAAM;EAAQ,EAAE;CAC9F,CAAC;CAGD,MAAM,kCAAkB,IAAI,IAA0B;CACtD,MAAM,mCAAmB,IAAI,IAA8B;CAC3D,IAAI;;;;;;CAQJ,IAAI,eAAe;CAEnB,MAAM,sBAAsB,OAAO,cAAqC;EACtE,IAAI,CAAC,cAAc,aAAa,OAAO,SAAS;EAChD,MAAM,YAAY,iBAAiB,IAAI,SAAS;EAChD,iBAAiB,OAAO,SAAS;EACjC,iBAAiB,OAAO;GAAE,OAAO;GAAW;EAAU,CAAC;EACvD,MAAM,WAAsB,CAAC;EAC7B,IAAI;GACF,MAAM,WAAW,KAAK,MAAM;EAC9B,SAAS,OAAO;GACd,SAAS,KAAK,KAAK;EACrB;EACA,IAAI;GACF,MAAM,WAAW,QAAQ;EAC3B,SAAS,OAAO;GACd,SAAS,KAAK,KAAK;EACrB;EACA,IAAI;GACF,MAAM,mBAAmB,aAAa,SAAS;EACjD,SAAS,OAAO;GACd,SAAS,KAAK,KAAK;EACrB;EACA,IAAI,SAAS,SAAS,GAAG,MAAM,IAAI,eAAe,UAAU,YAAY,UAAU,kBAAkB;CACtG;CAEA,MAAM,iBAAyC;EAC7C,MAAM,OAAO,gBAAgB;GAC3B,MAAM,QAAQ,gBAAgB,IAAI,eAAe,SAAS;GAC1D,IAAI;GACJ,IAAI;IACF,OAAO,MAAM,mBAAmB,OAAO,cAAc;GACvD,SAAS,OAAO;IACd,IAAI,UAAU,KAAA,KAAa,gBAAgB,OAAO,eAAe,SAAS,GACxE,MAAM,MAAM,QAAQ,CAAC,CAAC,OAAO,iBAA0B,OAAO,OAAO,YAAY,CAAC,CAAC;IACrF,MAAM;GACR;GACA,IAAI,UAAU,KAAA,KAAa,qBAAqB,KAAA,GAAW,OAAO;GAClE,IAAI;IACF,MAAM,OAAO,MAAM,iBAAiB,gBAAgB,IAAI;IACxD,gBAAgB,OAAO,eAAe,SAAS;IAC/C,iBAAiB,IAAI,eAAe,WAAW;KAAE,GAAG;KAAO;IAAK,CAAC;IACjE,OAAO;GACT,SAAS,OAAO;IACd,gBAAgB,OAAO,eAAe,SAAS;IAC/C,MAAM,mBACH,aAAa,eAAe,SAAS,CAAC,CACtC,OAAO,eAAwB,OAAO,OAAO,UAAU,CAAC,CAAC;IAC5D,MAAM,MAAM,QAAQ,CAAC,CAAC,OAAO,iBAA0B,OAAO,OAAO,YAAY,CAAC,CAAC;IACnF,MAAM;GACR;EACF;EACA,MAAM,cAAc,mBAAmB,IAAI,SAAS;EACpD,gBAAgB,mBAAmB,SAAS;EAC5C,cAAc;EACd,MAAM,QAAQ;GACZ,MAAM,MAAM,mBAAmB,SAAS,CAAC,CAAC,KAAK,YAAY,QAAQ,QAAQ,SAAS;GAEpF,MAAM,YAAW,MADM,QAAQ,WAAW,IAAI,KAAK,OAAO,oBAAoB,EAAE,CAAC,CAAC,EAAA,CAE/E,QAAQ,YAA8C,QAAQ,WAAW,UAAU,CAAC,CACpF,KAAK,YAAY,QAAQ,MAAM;GAClC,IAAI,SAAS,SAAS,GAAG,MAAM,IAAI,eAAe,UAAU,kCAAkC;EAChG;CACF;CAEA,IAAI,oBAGqB,YAAY,SAAS,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;CACzG,IAAI,cAGgC,YAAY;EAC9C,MAAM,IAAI,MAAM,2CAA2C;CAC7D;CACA,IAAI,iBAA0E,YAAY;EACxF,MAAM,IAAI,MAAM,mCAAmC;CACrD;CACA,IAAI;CACJ,OAAA,GAAMC,oCAAAA,kBAAAA,CAAkB;EACtB,SAAS;EACT,iBAAiB,SAAS,eAAe,IAAI;EAC7C,qBAAqB,gBAAgB,iBAAiB,OAAO;GAAE,OAAO;GAAa;EAAY,CAAC;EAChG,gBAAgB,YAAY,YAAY,OAAO;EAC/C,UAAU;EACV,gBAAgB;EAChB,oBAAoB,OAAO,YAAY,kBAAkB,OAAO,OAAO;CACzE,CAAC;CACD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,IAAI;EACF,MAAM,UAAU,UAAU,yBAAyB,CAAC,GAAG,YAAY;GACjE,MAAM,QAAQC,QAAAA,QAAG,aAAa,QAAQ,WAAW,MAAM,CAAC,CAAC,KAAK;GAC9D,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,iCAAiC;GAC7D,cAAc;GAEd,MAAM,WAAWC,yBAAAA,uBAAuB,QAAQ,WAAW;IACzD,WAAW,QAAQ,aAAaC,YAAAA,QAAO,WAAW;IAClD,aAAa,QAAQ;GACvB,CAAC;GAED,MAAM,gBAAgB,gBAAgB,QAAQC,QAAAA,QAAG,QAAQ;GACzD,MAAM,cAAA,GAAaC,gCAAAA,0BAAAA,CAA0B,aAAa;GAC1D,mBAAA,GAAkBC,wCAAAA,sBAAAA,CAAsBZ,UAAAA,QAAK,KAAK,YAAY,QAAQ,GAAG,MAAM;GAC/E,MAAM,kBAAkB,OACtB,MACA,OACA,cACG;IACH,MAAM,gBAAA,GAAea,yCAAAA,qBAAAA,CAAqB,MAAM,aAAa;IAC7D,MAAM,UAAA,GAASC,oCAAAA,0BAAAA,CAA0B,EAAE,aAAa,CAAC;IACzD,IAAI,OAAO,SAAS,cAClB,MAAM,IAAI,MAAM,mCAAmC,KAAK,qBAAqB;IAC/E,MAAM,WACJ,oBACO;KACL,MAAM,UAAA,GAASC,oCAAAA,qBAAAA,CAAqB,MAAM,aAAa;KACvD,MAAM,YAAY,OAAO;KACzB,OAAO;MAAE;MAAM;MAAW,eAAA,GAAcC,oCAAAA,cAAAA,CAAc,QAAQ,SAAS;KAAE;IAC3E,EAAA,CAAG;IACL,QAAA,GAAOC,oCAAAA,iBAAAA,CAAiB,OAAO;KAC7B,GAAG;KACH,GAAG;KACH,kBAAkB,UAAU;KAC5B,UAAU;IACZ,CAAC;GACH;GACA,MAAM,mBAAmB;IACvB;IACA,aAAa;IACb,UAAU;IACV,gBAAgB,IAAI;IACpB,cAAc,IAAI;IAClB,oBACE,IAAI,WAAW,CAAC,CAAC,KAAK,eAAe;KACnC,IAAI,UAAU;KACd,MAAM,UAAU;KAChB,MAAM,UAAU,KAAK,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,UAAU;KACpD,QAAQ,IAAI,SAAS,CAAC,CAAC,MAAM,YAAY,QAAQ,gBAAgB,UAAU,EAAE;IAC/E,EAAE;IACJ,oBAAoB,OAAe,IAAI,WAAW,CAAC,CAAC,MAAM,cAAc,UAAU,OAAO,EAAE,CAAC,EAAE;IAC9F,qBAAqB,OAAe;KAClC,MAAM,OAAO,IAAI,WAAW,CAAC,CAAC,MAAM,cAAc,UAAU,OAAO,EAAE,CAAC,EAAE;KACxE,IAAI,CAAC,MAAM,OAAO,KAAA;KAClB,MAAM,QAAQC,gBAAAA,cAAc;MAAE,UAAU;MAAM;MAAe,kBAAkB;KAAK,CAAC;KACrF,MAAM,QAAQC,gBAAAA,cAAc,MAAM,aAAa;KAC/C,OAAO;MAAE,GAAG;MAAO,eAAe,OAAO,UAAU;KAAc;IACnE;IACA,UAAU;GACZ;GACA,MAAM,eAAe,MAAM,gBAAgB,YAAY,QAAQ;GAC/D,gBAAgB,cAAA,GAAaN,yCAAAA,qBAAAA,CAAqB,YAAY,aAAa,CAAE;GAC7E,iBAAA,GAAgBO,sCAAAA,oBAAAA,CAAoB;IAClC;IACA,mBAAmB;IACnB,mBAAmB,iBAAiB,WAAW;IAC/C,UAAU;IACV,SAAS,OAAO,YAAY;KAC1B,IAAI,CAAC,WAAW,CAAC,aACf,OAAO,SAAS,KAAK,EAAE,OAAO,oCAAoC,GAAG,EAAE,QAAQ,IAAI,CAAC;KACtF,MAAM,OAAO,IAAI,IAAI,QAAQ,GAAG;KAChC,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;KAC3C,QAAQ,IAAI,kBAAkB,WAAW;KACzC,OAAO,MAAM,IAAI,IAAI,GAAG,KAAK,WAAW,KAAK,UAAU,QAAQ,GAAG,GAAG,IAAI,QAAQ,SAAS,EAAE,QAAQ,CAAC,CAAC;IACxG;IACA,kBAAkB,aAAa;KAC7B,IAAI,CAAC,WAAW,CAAC,aAAa,MAAM,IAAI,MAAM,8CAA8C;KAC5F,MAAM,MAAM,IAAI,IAAI,UAAU,QAAQ,GAAG;KACzC,IAAI,WAAW;KACf,OAAO,IAAIC,GAAAA,QAAU,KAAK,EAAE,SAAS,EAAE,kBAAkB,YAAY,EAAE,CAAC;IAC1E;GACF,CAAC;GACD,MAAM,IAAI,YAAY,aAAa,QAAQ;IACzC,GAAG;IACH,OAAO;IACP,eAAe,EAAE,QAAQ,YAAqB,cAAe,WAAW,OAAO,EAAE;GACnF,CAAC;GACD,gBAAgB,QACd,EAAE,OAAO,SAAS,IAAA,GAClBR,yCAAAA,qBAAAA,CAAqB,YAAY,aAAa,GAC9C,IAAI,aAAa,CACnB;GACA,MAAM,6BAAa,IAAI,IAAmD;GAC1E,iBAAiB,OAAO,SAAS;IAC/B,MAAM,OAAON,QAAAA,QAAG,aAAae,gBAAAA,mBAAmB,IAAI,CAAC;IACrD,MAAM,MAAA,GAAKC,qCAAAA,oBAAAA,CAAoB,MAAM,aAAa,CAAC,CAAC,SAAS;IAC7D,MAAM,WAAW,IAAI,WAAW,CAAC,CAAC,MAAM,cAAc,UAAU,OAAO,EAAE;IACzE,IAAI,UAAU,OAAO;IACrB,MAAM,UAAU,WAAW,IAAI,EAAE;IACjC,IAAI,SAAS,OAAO;IACpB,MAAM,aAAa,YAAY;KAC7B,MAAM,kBAAqC;MAAE,GAAG;MAAiB,aAAa;KAAK;KACnF,KAAK,MAAM,OAAO,CAACC,sCAAAA,uBAAuB,GAAG,OAAO,OAAOC,sCAAAA,kBAAkB,CAAC,GAAG,OAAO,gBAAgB;KACxG,MAAM,cAAc,MAAM,eAAe;KACzC,MAAM,SAAS,MAAM,gBAAgB,MAAM,WAAW;KACtD,MAAM,IAAI,YAAY,OAAO,QAAQ;MACnC,GAAG;MACH,OAAO;MACP,aAAa;MACb,eAAe;MACf,KAAK;MACL,oBAAoB,gBAAiB,gBAAgB,KAAK,OAAO,KAAA;KACnE,CAAC;KACD,iBAAiB,QACf;MAAE,OAAO;MAAa,aAAa;KAAG,IAAA,GACtCZ,yCAAAA,qBAAAA,CAAqB,MAAM,aAAa,GACxC,IAAI,aAAa,CACnB;KACA,OAAO;MAAE;MAAI;KAAK;IACpB,EAAA,CAAG;IACH,WAAW,IAAI,IAAI,SAAS;IAC5B,IAAI;KACF,OAAO,MAAM;IACf,UAAU;KACR,WAAW,OAAO,EAAE;IACtB;GACF;GAEA,MAAM,sBACJ,SACA,QACA,aAM+B;IAC/B,MAAM,gBAAgB,QAAQ;IAC9B,MAAM,mBAAmB;KACvB,MAAM,cAAc;KACpB,WAAW,cAAc;KACzB,cAAc,QAAQ;KACtB,SAAS,cAAc;KACvB,SAAS,QAAQ;KACjB,OAAO,EAAE,cAAc,CAAC,EAAE;IAC5B;IACA,OAAO;KACL,KAAK,cAAc;KACnB,UAAU,cAAc;KACxB,WAAW,SAAS;KACpB,cAAA,GAAaU,qCAAAA,oBAAAA,CAAoB,cAAc,UAAU,aAAa,CAAC,CAAC,SAAS;KACjF,aAAa,SAAS;KACtB,gBAAgB,iBAAiB,QAC/B;MAAE,OAAO;MAAW,WAAW,SAAS;KAAU,IAAA,GAClDV,yCAAAA,qBAAAA,CAAqB,cAAc,UAAU,aAAa,GAC1D,IAAI,aAAa,CACnB;KACA,GAAI,SAAS,oBAAoB,KAAA,IAAY,CAAC,IAAI,EAAE,iBAAiB,SAAS,gBAAgB;KAC9F,GAAI,SAAS,sBAAsB,KAAA,IAAY,CAAC,IAAI,EAAE,mBAAmB,SAAS,kBAAkB;KACpG,WAAW,cAAc;KACzB,aAAa,OAAO,OAAO,EAAE,GAAG,QAAQ,YAAY,CAAC;KACrD,WAAW;KACX,oBAAqB;MAAC;MAAa;MAAW;KAAS,CAAC,CAAW,QAAQ,SAAS;MAClF,MAAM,OAAO,SAAS,cAAc,iBAAiB,KAAK;MAC1D,OACE,SAAS,cAAc,SAAS,SAAS,aACzC,SAAS,UAAU,MAAM,QAAQ,QAAQ,QAAQ,IAAI,WAAW,GAAG,KAAK,EAAE,CAAC;KAE/E,CAAC;KACD,0BAA0B;MACxB,MAAM,cAAc,EAAE,GAAG,gBAAgB;MACzC,KAAK,MAAM,OAAO,CAACW,sCAAAA,uBAAuB,GAAG,OAAO,OAAOC,sCAAAA,kBAAkB,CAAC,GAAG,OAAO,YAAY;MACpG,MAAM,WAAW,sBAAsB;OACrC,MAAM,CAAC,SAAS,cAAc,GAAG;OACjC,KAAK,cAAc;OACnB;MACF,CAAC;MACD,OAAO;OAAE,WAAW,SAAS;OAAW,SAAS,SAAS;OAAS,SAAS,SAAS;MAAQ;KAC/F;KACA,YAAY,OAAO,WAAW;KAC9B,mBAAmB,cAAc;MAC/B,MAAM,UAAA,GAASV,oCAAAA,qBAAAA,CAAqB,cAAc,UAAU,cAAc,aAAa;MACvF,OAAO;OACL,GAAG;OACH,eAAA,GAAcW,oCAAAA,yBAAAA,CACZ,SAAA,GACAV,oCAAAA,cAAAA,CAAc,QAAQ,UAAU,SAAS,GACzC,cAAc,KAChB;MACF;KACF;KACA,UAAU;IACZ;GACF;GAEA,oBAAoB,gBAAgB,UAAA,GAClCW,0CAAAA,iBAAAA,CAAiB;IACf,WAAW,eAAe;IAC1B,KAAK,eAAe;IACpB,aAAa,eAAe;IAC5B,cAAc,IAAI;IAClB,UAAU;IACV,gBAAgB,IAAI;IACpB,gBAAgB,IAAI;IACpB,MAAM,CAAC;IACP,QAAQ,gBAAgB,IAAI,eAAe,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC;IACzE,aAAa,eAAe;IAC5B,eAAe,eAAe;IAC9B;IACA,eAAe,YAAY;KAEzB,OAAO;MAAE,SAAS;MAAM,SADR,IAAI,gBAAgB,SAAS;OAAE,OAAO;OAAW,WAAW,eAAe;MAAU,CACvE;KAAE;IAClC;IACA,eAAe,KAAK;IACpB,gBAAgB,KAAK;IACrB,aAAa,KAAK;IAClB;IACA,UAAU;GACZ,CAAC;GAEH,oBAAoB,OAAO,OAAO,YAAY;IAC5C,MAAM,UAAU,IAAI,QAAQ,MAAM,SAAS;IAC3C,MAAM,YAAY,iBAAiB,IAAI,MAAM,SAAS;IACtD,IAAI,YAAY,KAAA,KAAa,QAAQ,QAAQ,MAAM,OAAO,cAAc,KAAA,GACtE,OAAO,SAAS,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;IACvE,IAAI,CAAC,mCAAmC,KAAK,QAAQ,QAAQ,GAC3D,OAAO,SAAS,KAAK,EAAE,OAAO,iCAAiC,GAAG,EAAE,QAAQ,IAAI,CAAC;IACnF,IAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,KAAK,QAAQ,KAAK,WAAW,IAAI,KAAK,QAAQ,KAAK,SAAS,GAAG,GAC7F,OAAO,SAAS,KAAK,EAAE,OAAO,4BAA4B,GAAG,EAAE,QAAQ,IAAI,CAAC;IAC9E,MAAM,OAAO,QAAQ,SAAS,QAAQ,QAAQ,SAAS,KAAA,IAAY,KAAA,IAAY,QAAQ;IACvF,OAAO,UAAU,KAAK,QACpB,IAAI,QAAQ,mCAAmC,QAAQ,WAAW,QAAQ,QAAQ;KAChF,QAAQ,QAAQ;KAChB,SAAS,QAAQ;KACjB,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;KACrC,GAAI,QAAQ,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,QAAQ,OAAO;IACnE,CAAC,CACH;GACF;GAEA,IAAI,CAAC,QAAQ,WAAW;IACtB,iBAAiB,MAAMC,uBAAAA,oBACrB,sBAAsB;KAAE,MAAM,SAAS;KAAW,KAAK;KAAS,aAAa;IAAgB,CAAC,GAC9F,gBACF;IACA,MAAM,uBAAuB;IAC7B,IAAI;KACF,MAAM,eAAe,eAAe,QAAQ,QAAQ;KACpD,MAAM,gBAAgB,MAAM,gBAAgB,eAAe,QAAQ,UAAU,WAAW;MACtF,MAAM,eAAe,QAAQ;MAC7B,WAAW,eAAe,QAAQ;MAClC,cAAc,eAAe;KAC/B,CAAC;KACD,gBAAgB,IAAI,SAAS,SAAS,WAAW;MAC/C,eAAe,qBAAqB,QAAQ;MAC5C,QAAQ;KACV,CAAC;KACD,MAAM,IAAI,OAAO,mBAAmB,gBAAgB,eAAe,SAAS,QAAQ,CAAC;IACvF,SAAS,OAAO;KACd,gBAAgB,OAAO,SAAS,SAAS,SAAS;KAClD,MAAM,qBAAqB,QAAQ,CAAC,CAAC,OAAO,iBAA0B,OAAO,OAAO,YAAY,CAAC,CAAC;KAClG,MAAM;IACR;IACA,MAAM,QAAQ,iBAAiB,MAAM,GAAG,uCAAuC,MAAM;GACvF;GAEA,cAAc,OAAO,SAAS,cAAc;IAC1C,MAAM,WAAW;KACf,WAAW,aAAanB,YAAAA,QAAO,WAAW;KAC1C,aAAa,QAAQ;KACrB,iBAAiB,QAAQ;KACzB,mBAAmB,QAAQ;IAC7B;IACA,MAAM,gBAAgBD,yBAAAA,uBAAuB,CAAC,GAAG,QAAQ;IACzD,MAAM,mBAAmB,EAAE,GAAG,gBAAgB;IAC9C,KAAK,MAAM,OAAO,CAACgB,sCAAAA,uBAAuB,GAAG,OAAO,OAAOC,sCAAAA,kBAAkB,CAAC,GAAG,OAAO,iBAAiB;IACzG,MAAM,eAAe,MAAMG,uBAAAA,oBACzB,sBAAsB;KACpB,MAAM;MAAC;MAAS,QAAQ;MAAK,GAAG,cAAc;KAAS;KACvD,KAAK,QAAQ;KACb,aAAa;IACf,CAAC,GACD,gBACF;IACA,IAAI;KACF,MAAM,eAAe,aAAa,QAAQ,QAAQ;KAClD,MAAM,SAAS,MAAM,gBAAgB,aAAa,QAAQ,UAAU,WAAW;MAC7E,MAAM,aAAa,QAAQ;MAC3B,WAAW,aAAa,QAAQ;MAChC,cAAc,aAAa;KAC7B,CAAC;KACD,gBAAgB,IAAI,SAAS,WAAW;MAAE,eAAe,aAAa,QAAQ;MAAG;KAAO,CAAC;KACzF,MAAM,UAAU,MAAM,IAAI,OAAO,mBAAmB,cAAc,QAAQ,QAAQ,CAAC;KAInF,IAAI,QAAQ,gBAAgB,KAAA,GAC1B,aAAa,IAAI;MACf,WAAW,QAAQ;MACnB,aAAa,QAAQ;MACrB,KAAK,QAAQ;MACb,MAAM,QAAQ;MACd,WAAW,QAAQ;MACnB,GAAI,QAAQ,oBAAoB,KAAA,IAAY,CAAC,IAAI,EAAE,iBAAiB,QAAQ,gBAAgB;MAC5F,GAAI,QAAQ,sBAAsB,KAAA,IAAY,CAAC,IAAI,EAAE,mBAAmB,QAAQ,kBAAkB;KACpG,CAAC;KAEH,OAAO;MAAE,WAAW,SAAS;MAAW,KAAK,aAAa,QAAQ;KAAI;IACxE,SAAS,OAAO;KACd,gBAAgB,OAAO,SAAS,SAAS;KACzC,MAAM,aAAa,QAAQ,CAAC,CAAC,OAAO,iBAA0B,OAAO,OAAO,YAAY,CAAC,CAAC;KAC1F,MAAM;IACR;GACF;EACF,CAAC;EAED,UAAU,OAAA,GAAMC,uCAAAA,oBAAAA,CAAoB;GAClC,MAAM,QAAQ;GACd,aAAa;GACb,OAAO;GACP,iBAAiB,YAAY;IAC3B,MAAM,gBAAgB,IAAI,WAAW,CAAC,CAAC,MAAM,cAAc,UAAU,OAAO,QAAQ,WAAW,CAAC,EAAE;IAClG,IAAI,CAAC,eAAe,MAAM,IAAI,MAAM,8BAA8B;IAClE,QAAA,GAAOC,+BAAAA,kBAAAA,CACL9B,UAAAA,QAAK,MAAA,GAAKC,mCAAAA,iBAAAA,CAAiB,eAAe,GAAG,UAAU,UAAU,GACjE,eACA,IAAI,IAAI,IAAI,SAAS,CAAC,CAAC,KAAK,WAAW,OAAO,EAAE,CAAC,CACnD;GACF;GACA,gBAAgB,OAAO,YAAY;IACjC,MAAM,gBAAgB,IAAI,WAAW,CAAC,CAAC,MAAM,cAAc,UAAU,OAAO,QAAQ,WAAW,CAAC,EAAE;IAClG,IAAI,CAAC,eAAe,MAAM,IAAI,MAAM,8BAA8B;IAClE,MAAM,kBAAqC;KAAE,GAAG;KAAiB,aAAa;IAAc;IAC5F,KAAK,MAAM,OAAO,CAACuB,sCAAAA,uBAAuB,GAAG,OAAO,OAAOC,sCAAAA,kBAAkB,CAAC,GAAG,OAAO,gBAAgB;IACxG,IAAI;KACF,MAAM,cAAc,eAAe,eAAe;IACpD,SAAS,OAAO;KACd,OAAO,yCAAyC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;KACxG,MAAM,IAAI,MAAM,wDAAwD,EAAE,OAAO,MAAM,CAAC;IAC1F;IACA,MAAM,IAAI,aAAa,QAAQ,EAAE;IACjC,MAAM,YAAY;KAAE,KAAK,QAAQ;KAAK,MAAM,QAAQ;IAAK,GAAG,QAAQ,EAAE;GACxE;GACA,eAAe,OAAO,SAAS,oBAAoB;IACjD,MAAM,gBAAgB,IAAI,WAAW,CAAC,CAAC,MAAM,cAAc,UAAU,OAAO,QAAQ,WAAW,CAAC,EAAE;IAClG,IAAI,CAAC,eAAe,MAAM,IAAI,MAAM,8BAA8B;IAMlE,MAAM,UAAS,OAAA,GALKK,+BAAAA,kBAAAA,CAClB9B,UAAAA,QAAK,MAAA,GAAKC,mCAAAA,iBAAAA,CAAiB,eAAe,GAAG,UAAU,UAAU,GACjE,eACA,IAAI,IAAI,IAAI,SAAS,CAAC,CAAC,KAAK,WAAW,OAAO,EAAE,CAAC,CACnD,EAAA,CACqB,MAAM,SAAS,KAAK,OAAO,eAAe;IAC/D,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,8CAA8C;IAC3E,MAAM,IAAI,aAAa,QAAQ,EAAE;IACjC,MAAM,YAAY;KAAE,KAAK,QAAQ;KAAK,MAAM,OAAO,QAAQ;IAAW,GAAG,OAAO,EAAE;IAClF,OAAO,OAAO;GAChB;GACA,uBAAuB,aAAa,KAAK;GACzC,eAAe,OAAO,WAA8B;IAClD,IAAI;KACF,MAAM,YACJ;MACE,KAAK,OAAO;MACZ,MAAM,OAAO;MACb,GAAI,OAAO,oBAAoB,KAAA,IAAY,CAAC,IAAI,EAAE,iBAAiB,OAAO,gBAAgB;MAC1F,GAAI,OAAO,sBAAsB,KAAA,IAAY,CAAC,IAAI,EAAE,mBAAmB,OAAO,kBAAkB;KAClG,GACA,OAAO,SACT;IACF,SAAS,OAAO;KAId,aAAa,OAAO,OAAO,SAAS;KACpC,MAAM;IACR;GACF;GACA,eAAe,YAAY,iBAAiB,QAAQ,OAAO,KAAK,QAAQ,QAAQ,KAAA,CAAS;GACzF,qBAAqB;IACnB,QAAQ,iBAAiB,IAAI,EAAE,OAAO,SAAS,CAAC;IAChD,WAAW,iBAAiB,UAAU;IACtC,OAAO,iBAAiB,WAAW;IACnC,YAAY,IAAI,WAAW,CAAC,CAAC,KAAK,eAAe;KAC/C,GAAG;KACH,gBAAgB,iBAAiB,IAAI;MAAE,OAAO;MAAa,aAAa,UAAU;KAAG,CAAC;IACxF,EAAE;GACJ;GACA;GACA,UAAU;EACZ,CAAC;EACD,OAAO,eAAe,QAAQ,IAAI,QAAQ;EAC1C,IAAI;EACJ,MAAM,WAAW,IAAI,SAAiB,YAAY;GAChD,kBAAkB;EACpB,CAAC;EACD,MAAM,aAAmB,gBAAgB,CAAC;EAC1C,OAAO,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;EACrD,IAAI,OAAO,SAAS,KAAK;EACzB,IAAI;GACF,OAAO,MAAM;EACf,UAAU;GACR,OAAO,oBAAoB,SAAS,IAAI;EAC1C;CACF,UAAU;EACR,eAAe;EACf,cAAc,gBAAgB;EAC9B,MAAM,QAAQ,UAAU,YAAY,wBAAwB,GAAG,sBAAsB,MAAM;EAC3F,MAAM,QAAQ,WAAW,CAAC,SAAS,MAAM,CAAC,CAAC;EAC3C,MAAM,QAAQ,eAAe,MAAM,KAAK,QAAQ,QAAQ,GAAG,2BAA2B,MAAM;EAC5F,IAAI;GACF,MAAM,IAAI,MAAM;EAClB,SAAS,OAAO;GACd,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAC/D;EACA,IAAI;GACF,MAAM,eAAe,MAAM;EAC7B,SAAS,OAAO;GACd,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAC/D;EACA,iBAAiB,MAAM;EACvB,MAAM,kBAAkB,CAAC,GAAG,gBAAgB,OAAO,CAAC,CAAC,CAAC,KAAK,UAAU,MAAM,QAAQ,CAAC;EACpF,gBAAgB,MAAM;EACtB,MAAM,QAAQ,WAAW,eAAe;EACxC,MAAM,QAAQ,iBAAiB,MAAM,GAAG,2BAA2B,MAAM;EACzE,MAAM,QAAQ,iBAAiB,SAAS,GAAG,8BAA8B,MAAM;EAC/E,MAAM,QAAQ,UAAU,MAAM,GAAG,0BAA0B,MAAM;EACjE,MAAM,QAAQ,UAAU,SAAS,GAAG,6BAA6B,MAAM;CACzE;AACF"}
|