@agimon-ai/doompi-domain 0.0.1-alpha.2 → 0.0.1-alpha.20

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.
Files changed (107) hide show
  1. package/README.md +27 -15
  2. package/dist/adapters/applyDomains.cjs +1 -1
  3. package/dist/adapters/applyDomains.cjs.map +1 -1
  4. package/dist/adapters/applyDomains.d.cts +2 -2
  5. package/dist/adapters/applyDomains.d.cts.map +1 -1
  6. package/dist/adapters/applyDomains.d.mts +2 -2
  7. package/dist/adapters/applyDomains.d.mts.map +1 -1
  8. package/dist/adapters/applyDomains.mjs +1 -1
  9. package/dist/adapters/applyDomains.mjs.map +1 -1
  10. package/dist/adapters/domainCatalog.cjs +1 -1
  11. package/dist/adapters/domainCatalog.cjs.map +1 -1
  12. package/dist/adapters/domainCatalog.d.cts +4 -3
  13. package/dist/adapters/domainCatalog.d.cts.map +1 -1
  14. package/dist/adapters/domainCatalog.d.mts +4 -3
  15. package/dist/adapters/domainCatalog.d.mts.map +1 -1
  16. package/dist/adapters/domainCatalog.mjs +1 -1
  17. package/dist/adapters/domainCatalog.mjs.map +1 -1
  18. package/dist/adapters/domainSwitchHandoff.cjs +1 -1
  19. package/dist/adapters/domainSwitchHandoff.cjs.map +1 -1
  20. package/dist/adapters/domainSwitchHandoff.mjs +1 -1
  21. package/dist/adapters/domainSwitchHandoff.mjs.map +1 -1
  22. package/dist/adapters/mcpFilter.cjs +2 -2
  23. package/dist/adapters/mcpFilter.mjs +2 -2
  24. package/dist/adapters/mcpSessionEnvironment.cjs +1 -1
  25. package/dist/adapters/mcpSessionEnvironment.cjs.map +1 -1
  26. package/dist/adapters/mcpSessionEnvironment.d.cts +2 -2
  27. package/dist/adapters/mcpSessionEnvironment.d.cts.map +1 -1
  28. package/dist/adapters/mcpSessionEnvironment.d.mts +2 -2
  29. package/dist/adapters/mcpSessionEnvironment.d.mts.map +1 -1
  30. package/dist/adapters/mcpSessionEnvironment.mjs +1 -1
  31. package/dist/adapters/mcpSessionEnvironment.mjs.map +1 -1
  32. package/dist/adapters/pi/extension.cjs +1 -1
  33. package/dist/adapters/pi/extension.cjs.map +1 -1
  34. package/dist/adapters/pi/extension.d.cts +2 -6
  35. package/dist/adapters/pi/extension.d.cts.map +1 -1
  36. package/dist/adapters/pi/extension.d.mts +2 -6
  37. package/dist/adapters/pi/extension.d.mts.map +1 -1
  38. package/dist/adapters/pi/extension.mjs +1 -1
  39. package/dist/adapters/pi/extension.mjs.map +1 -1
  40. package/dist/adapters/pi/voiceTool.cjs +1 -1
  41. package/dist/adapters/pi/voiceTool.cjs.map +1 -1
  42. package/dist/adapters/pi/voiceTool.d.cts +5 -2
  43. package/dist/adapters/pi/voiceTool.d.cts.map +1 -1
  44. package/dist/adapters/pi/voiceTool.d.mts +5 -2
  45. package/dist/adapters/pi/voiceTool.d.mts.map +1 -1
  46. package/dist/adapters/pi/voiceTool.mjs +1 -1
  47. package/dist/adapters/pi/voiceTool.mjs.map +1 -1
  48. package/dist/adapters/pluginMaterializer.cjs +1 -1
  49. package/dist/adapters/pluginMaterializer.cjs.map +1 -1
  50. package/dist/adapters/pluginMaterializer.d.cts.map +1 -1
  51. package/dist/adapters/pluginMaterializer.d.mts.map +1 -1
  52. package/dist/adapters/pluginMaterializer.mjs +1 -1
  53. package/dist/adapters/pluginMaterializer.mjs.map +1 -1
  54. package/dist/adapters/resourceCollector.cjs +2 -2
  55. package/dist/adapters/resourceCollector.cjs.map +1 -1
  56. package/dist/adapters/resourceCollector.d.cts +4 -2
  57. package/dist/adapters/resourceCollector.d.cts.map +1 -1
  58. package/dist/adapters/resourceCollector.d.mts +4 -2
  59. package/dist/adapters/resourceCollector.d.mts.map +1 -1
  60. package/dist/adapters/resourceCollector.mjs +2 -2
  61. package/dist/adapters/resourceCollector.mjs.map +1 -1
  62. package/dist/adapters/skillCacheLocation.cjs +1 -1
  63. package/dist/adapters/skillCacheLocation.mjs +1 -1
  64. package/dist/commands/domainsCommand.cjs +1 -1
  65. package/dist/commands/domainsCommand.cjs.map +1 -1
  66. package/dist/commands/domainsCommand.d.cts +4 -0
  67. package/dist/commands/domainsCommand.d.cts.map +1 -1
  68. package/dist/commands/domainsCommand.d.mts +4 -0
  69. package/dist/commands/domainsCommand.d.mts.map +1 -1
  70. package/dist/commands/domainsCommand.mjs +1 -1
  71. package/dist/commands/domainsCommand.mjs.map +1 -1
  72. package/dist/index.cjs +1 -1
  73. package/dist/index.d.cts +2 -2
  74. package/dist/index.d.mts +2 -2
  75. package/dist/index.mjs +1 -1
  76. package/dist/mcp.cjs +1 -1
  77. package/dist/mcp.d.cts +2 -1
  78. package/dist/mcp.d.mts +2 -1
  79. package/dist/mcp.mjs +1 -1
  80. package/dist/resources.cjs +1 -1
  81. package/dist/resources.d.cts +3 -3
  82. package/dist/resources.d.mts +3 -3
  83. package/dist/resources.mjs +1 -1
  84. package/dist/services/domainText.cjs +1 -1
  85. package/dist/services/domainText.cjs.map +1 -1
  86. package/dist/services/domainText.d.cts +5 -1
  87. package/dist/services/domainText.d.cts.map +1 -1
  88. package/dist/services/domainText.d.mts +5 -1
  89. package/dist/services/domainText.d.mts.map +1 -1
  90. package/dist/services/domainText.mjs +1 -1
  91. package/dist/services/domainText.mjs.map +1 -1
  92. package/dist/types/handoff.d.cts +1 -1
  93. package/dist/types/handoff.d.mts +1 -1
  94. package/dist/types/resources.d.cts +26 -7
  95. package/dist/types/resources.d.cts.map +1 -1
  96. package/dist/types/resources.d.mts +26 -7
  97. package/dist/types/resources.d.mts.map +1 -1
  98. package/dist/types/telemetry.d.cts +2 -2
  99. package/dist/types/telemetry.d.cts.map +1 -1
  100. package/dist/types/telemetry.d.mts +2 -2
  101. package/dist/types/telemetry.d.mts.map +1 -1
  102. package/llms.txt +7 -2
  103. package/package.json +32 -17
  104. package/src/prompts/doompi-author-domain/SKILL.md +18 -0
  105. package/src/prompts/doompi-author-domain/references/domains-contract.md +165 -0
  106. package/web/index.ts +14 -0
  107. package/web/tsconfig.json +22 -0
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  Domain selection for [DoomPi](https://www.npmjs.com/package/@agimon-ai/doompi) sessions.
4
4
 
5
- **Domains** are one of the four axes of a DoomPi selection, alongside the major mode, the profile
6
- and the minor modes. Unlike a major mode, domains compose: a session selects a set of them, and the
7
- union of what they contribute is what the model sees.
5
+ **Domains** are one of the four axes of a DoomPi selection, alongside the major mode, the profile,
6
+ and the minor modes. Unlike a major mode, domains compose: a session selects a set of them, and
7
+ the union of what they contribute is what the model sees.
8
8
 
9
9
  ```yaml
10
10
  # .doom/domains.yaml
@@ -14,12 +14,12 @@ plugins:
14
14
  infra: plugins/infra
15
15
  domains:
16
16
  development:
17
- description: Application code, tests and review tooling.
17
+ description: Application code, tests, and review tooling.
18
18
  plugins: [frontend]
19
19
  mcp:
20
20
  servers: [code-intel]
21
21
  platform:
22
- description: Deployment, infrastructure and incident tooling.
22
+ description: Deployment, infrastructure, and incident tooling.
23
23
  plugins: [infra]
24
24
  aliases:
25
25
  everything: [development, platform]
@@ -27,7 +27,7 @@ aliases:
27
27
 
28
28
  ## What it registers
29
29
 
30
- | Surface | Behaviour |
30
+ | Surface | Behavior |
31
31
  | --------------------------- | ------------------------------------------------------------- |
32
32
  | `/domains` | prints the selection, or switches to `name[,name...]` |
33
33
  | `/domains` with no argument | opens a multi-select picker in the TUI |
@@ -36,21 +36,32 @@ aliases:
36
36
  | autocomplete | comma-triggered completion of domain names inside `/domains` |
37
37
  | `resources_discover` | re-answers with the current selection's skills after a reload |
38
38
 
39
+ When the `@agimon-ai/doompi-help` minor mode is active, this package contributes the
40
+ `doompi-author-domain` skill. It provides package-owned guidance for plugin catalogs, domain
41
+ filters, aliases, defaults, shared skills, and MCP scopes in `domains.yaml`. The contribution is
42
+ registered through the session's `doom/help` Cordis service and is withdrawn with this package's
43
+ plugin fiber.
44
+
39
45
  ## What a switch stages
40
46
 
41
47
  A domain switch is not just a config write, which is why it does not live in
42
48
  `@agimon-ai/doompi-config/selectionSwitch` with the major-mode and profile switches. Applying a
43
49
  selection:
44
50
 
45
- - materializes any remote (git or npm) plugins the selected domains name, into a content-addressed
51
+ - materializes any remote (Git or npm) plugins the selected domains name into a content-addressed
46
52
  cache guarded by an install marker;
47
53
  - discovers the exact `SKILL.md` files behind the selection, through a manifest cache keyed by
48
54
  worktree so a second session does not rescan the tree;
49
55
  - adapts each plugin's Claude-authored agent definitions into the form Pi loads, and stages them
50
56
  privately (`0600`) beside a dispatcher agent;
51
- - merges the repository and plugin `.mcp.json` layers, applies the union of the selected domains'
52
- MCP allowlists, and rewrites the proxy's upstream list into a per-run config;
53
- - projects the result into the session environment and the harness state, then reloads Pi.
57
+ - merges the repository and legacy plugin `.mcp.json` layers, and validates root `mcp.json` for
58
+ schema-gated Agent Plugins v1 (including plugin-relative commands, data directories, and transport
59
+ normalization);
60
+ - applies the selected domains' MCP allowlists and rewrites the proxy's upstream list into a
61
+ private per-run config; and
62
+ - atomically stores an immutable MCP projection in harness state, then reloads Pi. The replacement
63
+ Config factory publishes that snapshot through the session Cordis registry for DoomPi MCP to
64
+ consume; Domain never reaches into the live MCP runtime.
54
65
 
55
66
  An allowlist only takes effect once **every** selected domain declares one. Mixing a scoped domain
56
67
  with a domain that has not opted in leaves the session unfiltered, so an unmigrated domain cannot
@@ -58,15 +69,16 @@ silently lose its tools.
58
69
 
59
70
  ## Switching from voice
60
71
 
61
- The voice tool never applies a switch itself. It plans the transition, parks the validated selection
62
- in a TTL-bounded store and sends `/domains --voice-switch-token=…` back as a follow-up, so the reload
63
- happens inside a command handler where it can be the terminal action. The selection travels in the
64
- store rather than in the follow-up text, and the token is bound to the session that minted it.
72
+ The voice tool never applies a switch itself. It plans the transition, parks the validated
73
+ selection in a TTL-bounded store, and sends `/domains --voice-switch-token=…` back as a follow-up,
74
+ so the reload happens inside a command handler where it can be the terminal action. The selection
75
+ travels in the store rather than in the follow-up text, and the token is bound to the session that
76
+ minted it.
65
77
 
66
78
  ## Installation
67
79
 
68
80
  DoomPi depends on this package and activates it as fixed host core, so a DoomPi install already has
69
- it. It is not selectable from `.doom/domains.yaml` — it is what reads that file.
81
+ it. It is not selectable from `.doom/modes.yaml`; it reads `.doom/domains.yaml`.
70
82
 
71
83
  ## License
72
84
 
@@ -1,2 +1,2 @@
1
- const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./mcpFilter.cjs"),n=require("./mcpSessionEnvironment.cjs"),r=require("./pluginMaterializer.cjs"),i=require("./resourceCollector.cjs");let a=require("node:path");a=e.__toESM(a,1);let o=require("@agimon-ai/doompi-config/domains"),s=require("@agimon-ai/doompi-config/harnessStore"),c=require("@agimon-ai/doompi-mcp");async function l(e,l){let{root:u,temporaryDirectory:d}=(0,s.requireHarnessPaths)(l),f=await r.materializePluginEntries((0,o.resolvePluginEntries)(u,e,[...l.pluginDirectories])),p=t.resolveMcpAllowlist(u,e),m=await i.collectResources(u,f,{agents:l.agents,mcp:l.mcp,temporaryDirectory:d,sharedSkills:(0,o.resolveSharedSkills)(u,e),mcpAllowlist:p});l.mcp?Object.assign(process.env,n.mcpSessionEnvironment({repoRoot:u,stagingDirectory:m.temporaryDirectory,generatedConfigPath:m.mcpConfigPath,pluginConfigPaths:m.pluginMcpConfigPaths,allowlist:p})):delete process.env[c.SESSION_ENV_VAR];let h=(0,s.updateHarnessState)({domains:e,skillDirectories:m.skillDirectories,pluginHooks:m.pluginHooks,mcpConfigPath:m.mcpConfigPath});return process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS=m.agentDirectories.join(a.default.delimiter),process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS=m.skillDirectories.join(a.default.delimiter),h}exports.applyDomains=l;
1
+ const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./mcpFilter.cjs"),n=require("./pluginMaterializer.cjs"),r=require("./resourceCollector.cjs");let i=require("node:fs");i=e.__toESM(i,1);let a=require("node:path");a=e.__toESM(a,1);let o=require("@agimon-ai/doompi-config/domains"),s=require("@agimon-ai/doompi-config/harnessStore");async function c(e,c){let{root:l,temporaryDirectory:u}=(0,s.requireHarnessPaths)(c),d=await n.materializePluginEntries((0,o.resolvePluginEntries)(l,e,[...c.pluginDirectories])),f=t.resolveMcpAllowlist(l,e),p=await i.default.promises.mkdtemp(a.default.join(u,`domains-`)),m;try{m=await r.collectResources(l,d,{agents:c.agents,mcp:c.mcp,temporaryDirectory:p,sharedSkills:(0,o.resolveSharedSkills)(l,e),mcpAllowlist:f})}catch(e){throw await i.default.promises.rm(p,{recursive:!0,force:!0}),e}let h;try{h=(0,s.updateHarnessState)({domains:e,skillDirectories:m.skillDirectories,agentDirectories:m.agentDirectories,pluginHooks:m.pluginHooks,mcpConfigPath:m.mcpConfigPath,mcpProjection:m.mcpProjection})}catch(e){throw await i.default.promises.rm(p,{recursive:!0,force:!0}),e}return process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS=m.agentDirectories.join(a.default.delimiter),process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS=m.skillDirectories.join(a.default.delimiter),h}exports.applyDomains=c;
2
2
  //# sourceMappingURL=applyDomains.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"applyDomains.cjs","names":["requireHarnessPaths","materializePluginEntries","resolvePluginEntries","resolveMcpAllowlist","collectResources","resolveSharedSkills","mcpSessionEnvironment","SESSION_ENV_VAR","updateHarnessState","path"],"sources":["../../src/adapters/applyDomains.ts"],"sourcesContent":["import path from 'node:path';\nimport { resolvePluginEntries, resolveSharedSkills } from '@agimon-ai/doompi-config/domains';\nimport { requireHarnessPaths, updateHarnessState } from '@agimon-ai/doompi-config/harnessStore';\nimport type { DoomHarnessContext, HarnessState } from '@agimon-ai/doompi-config/types';\nimport { SESSION_ENV_VAR } from '@agimon-ai/doompi-mcp';\nimport { resolveMcpAllowlist } from './mcpFilter.ts';\nimport { mcpSessionEnvironment } from './mcpSessionEnvironment.ts';\nimport { materializePluginEntries } from './pluginMaterializer.ts';\nimport { collectResources } from './resourceCollector.ts';\n\n/**\n * Live domain switching: skills, plugin hooks, MCP config and subagent directories.\n *\n * Writes through the harness state module and relies on Pi's reload\n * re-executing extension modules, which drops their module-level caches and\n * makes them re-read the environment.\n *\n * What reload cannot do is change the SET of loaded extensions: Pi freezes the\n * CLI --extension list at construction and re-resolves that same array on every\n * reload. Callers report those parts as needing a relaunch instead of pretending\n * they applied.\n *\n * The major-mode and profile switches live in\n * @agimon-ai/doompi-config/selectionSwitch: they are config writes with no\n * resource staging, and the synced startup path applies them before any command\n * exists to run. Domains stays here because it stages plugins, skills and MCP\n * config.\n */\nexport async function applyDomains(domainNames: string[], state: DoomHarnessContext): Promise<HarnessState> {\n const { root, temporaryDirectory } = requireHarnessPaths(state);\n\n const pluginEntries = await materializePluginEntries(\n resolvePluginEntries(root, domainNames, [...state.pluginDirectories]),\n );\n const mcpAllowlist = resolveMcpAllowlist(root, domainNames);\n const resources = await collectResources(root, pluginEntries, {\n agents: state.agents,\n mcp: state.mcp,\n temporaryDirectory,\n sharedSkills: resolveSharedSkills(root, domainNames),\n mcpAllowlist,\n });\n\n if (state.mcp) {\n Object.assign(\n process.env,\n mcpSessionEnvironment({\n repoRoot: root,\n stagingDirectory: resources.temporaryDirectory,\n generatedConfigPath: resources.mcpConfigPath,\n pluginConfigPaths: resources.pluginMcpConfigPaths,\n allowlist: mcpAllowlist,\n }),\n );\n } else {\n delete process.env[SESSION_ENV_VAR];\n }\n const updated = updateHarnessState({\n domains: domainNames,\n skillDirectories: resources.skillDirectories,\n pluginHooks: resources.pluginHooks,\n mcpConfigPath: resources.mcpConfigPath,\n });\n process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS = resources.agentDirectories.join(path.delimiter);\n process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS = resources.skillDirectories.join(path.delimiter);\n return updated;\n}\n"],"mappings":"0XA4BA,eAAsB,EAAa,EAAuB,EAAkD,CAC1G,GAAM,CAAE,OAAM,uBAAA,EAAuBA,EAAAA,oBAAAA,CAAoB,CAAK,EAExD,EAAgB,MAAMC,EAAAA,0BAAAA,EAC1BC,EAAAA,qBAAAA,CAAqB,EAAM,EAAa,CAAC,GAAG,EAAM,iBAAiB,CAAC,CACtE,EACM,EAAeC,EAAAA,oBAAoB,EAAM,CAAW,EACpD,EAAY,MAAMC,EAAAA,iBAAiB,EAAM,EAAe,CAC5D,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,qBACA,cAAA,EAAcC,EAAAA,oBAAAA,CAAoB,EAAM,CAAW,EACnD,cACF,CAAC,EAEG,EAAM,IACR,OAAO,OACL,QAAQ,IACRC,EAAAA,sBAAsB,CACpB,SAAU,EACV,iBAAkB,EAAU,mBAC5B,oBAAqB,EAAU,cAC/B,kBAAmB,EAAU,qBAC7B,UAAW,CACb,CAAC,CACH,EAEA,OAAO,QAAQ,IAAIC,EAAAA,iBAErB,IAAM,GAAA,EAAUC,EAAAA,mBAAAA,CAAmB,CACjC,QAAS,EACT,iBAAkB,EAAU,iBAC5B,YAAa,EAAU,YACvB,cAAe,EAAU,aAC3B,CAAC,EAGD,MAFA,SAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAKC,EAAAA,QAAK,SAAS,EACzF,QAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAKA,EAAAA,QAAK,SAAS,EAClF,CACT"}
1
+ {"version":3,"file":"applyDomains.cjs","names":["requireHarnessPaths","materializePluginEntries","resolvePluginEntries","resolveMcpAllowlist","fs","path","collectResources","resolveSharedSkills","updateHarnessState"],"sources":["../../src/adapters/applyDomains.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { resolvePluginEntries, resolveSharedSkills } from '@agimon-ai/doompi-config/domains';\nimport { requireHarnessPaths, updateHarnessState } from '@agimon-ai/doompi-config/harnessStore';\nimport type { DoomHarnessContext, HarnessState } from '@agimon-ai/doompi-config/types';\nimport { resolveMcpAllowlist } from './mcpFilter.ts';\nimport { materializePluginEntries } from './pluginMaterializer.ts';\nimport { collectResources } from './resourceCollector.ts';\n\n/**\n * Live domain switching: skills, plugin hooks, MCP config and subagent directories.\n *\n * Writes through the harness state module and relies on Pi's reload\n * re-executing extension modules. The replacement Config factory publishes the\n * persisted MCP projection on the new session's Cordis root.\n *\n * What reload cannot do is change the SET of loaded extensions: Pi freezes the\n * CLI --extension list at construction and re-resolves that same array on every\n * reload. Callers report those parts as needing a relaunch instead of pretending\n * they applied.\n *\n * The major-mode and profile switches live in\n * @agimon-ai/doompi-config/selectionSwitch: they are config writes with no\n * resource staging, and the synced startup path applies them before any command\n * exists to run. Domains stays here because it stages plugins, skills and MCP\n * config.\n */\nexport async function applyDomains(domainNames: string[], state: DoomHarnessContext): Promise<HarnessState> {\n const { root, temporaryDirectory } = requireHarnessPaths(state);\n\n const pluginEntries = await materializePluginEntries(\n resolvePluginEntries(root, domainNames, [...state.pluginDirectories]),\n );\n const mcpAllowlist = resolveMcpAllowlist(root, domainNames);\n // Keep the active generation untouched while the candidate is parsed and\n // normalized. Pi's reload later removes the whole process-owned run root, so\n // retired generations need no eager destructive cleanup here.\n const candidateDirectory = await fs.promises.mkdtemp(path.join(temporaryDirectory, 'domains-'));\n let resources: Awaited<ReturnType<typeof collectResources>>;\n try {\n resources = await collectResources(root, pluginEntries, {\n agents: state.agents,\n mcp: state.mcp,\n temporaryDirectory: candidateDirectory,\n sharedSkills: resolveSharedSkills(root, domainNames),\n mcpAllowlist,\n });\n } catch (error) {\n await fs.promises.rm(candidateDirectory, { recursive: true, force: true });\n throw error;\n }\n\n let updated: HarnessState;\n try {\n updated = updateHarnessState({\n domains: domainNames,\n skillDirectories: resources.skillDirectories,\n agentDirectories: resources.agentDirectories,\n pluginHooks: resources.pluginHooks,\n mcpConfigPath: resources.mcpConfigPath,\n mcpProjection: resources.mcpProjection,\n });\n } catch (error) {\n await fs.promises.rm(candidateDirectory, { recursive: true, force: true });\n throw error;\n }\n process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS = resources.agentDirectories.join(path.delimiter);\n process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS = resources.skillDirectories.join(path.delimiter);\n return updated;\n}\n"],"mappings":"wVA2BA,eAAsB,EAAa,EAAuB,EAAkD,CAC1G,GAAM,CAAE,OAAM,uBAAA,EAAuBA,EAAAA,oBAAAA,CAAoB,CAAK,EAExD,EAAgB,MAAMC,EAAAA,0BAAAA,EAC1BC,EAAAA,qBAAAA,CAAqB,EAAM,EAAa,CAAC,GAAG,EAAM,iBAAiB,CAAC,CACtE,EACM,EAAeC,EAAAA,oBAAoB,EAAM,CAAW,EAIpD,EAAqB,MAAMC,EAAAA,QAAG,SAAS,QAAQC,EAAAA,QAAK,KAAK,EAAoB,UAAU,CAAC,EAC1F,EACJ,GAAI,CACF,EAAY,MAAMC,EAAAA,iBAAiB,EAAM,EAAe,CACtD,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,mBAAoB,EACpB,cAAA,EAAcC,EAAAA,oBAAAA,CAAoB,EAAM,CAAW,EACnD,cACF,CAAC,CACH,OAAS,EAAO,CAEd,MADA,MAAMH,EAAAA,QAAG,SAAS,GAAG,EAAoB,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnE,CACR,CAEA,IAAI,EACJ,GAAI,CACF,GAAA,EAAUI,EAAAA,mBAAAA,CAAmB,CAC3B,QAAS,EACT,iBAAkB,EAAU,iBAC5B,iBAAkB,EAAU,iBAC5B,YAAa,EAAU,YACvB,cAAe,EAAU,cACzB,cAAe,EAAU,aAC3B,CAAC,CACH,OAAS,EAAO,CAEd,MADA,MAAMJ,EAAAA,QAAG,SAAS,GAAG,EAAoB,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnE,CACR,CAGA,MAFA,SAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAKC,EAAAA,QAAK,SAAS,EACzF,QAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAKA,EAAAA,QAAK,SAAS,EAClF,CACT"}
@@ -4,8 +4,8 @@ import { DoomHarnessContext, HarnessState } from "@agimon-ai/doompi-config/types
4
4
  * Live domain switching: skills, plugin hooks, MCP config and subagent directories.
5
5
  *
6
6
  * Writes through the harness state module and relies on Pi's reload
7
- * re-executing extension modules, which drops their module-level caches and
8
- * makes them re-read the environment.
7
+ * re-executing extension modules. The replacement Config factory publishes the
8
+ * persisted MCP projection on the new session's Cordis root.
9
9
  *
10
10
  * What reload cannot do is change the SET of loaded extensions: Pi freezes the
11
11
  * CLI --extension list at construction and re-resolves that same array on every
@@ -1 +1 @@
1
- {"version":3,"file":"applyDomains.d.cts","names":[],"sources":["../../src/adapters/applyDomains.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA4BsB,aAAa,uBAAuB,OAAO,qBAAqB,QAAQ"}
1
+ {"version":3,"file":"applyDomains.d.cts","names":[],"sources":["../../src/adapters/applyDomains.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA2BsB,aAAa,uBAAuB,OAAO,qBAAqB,QAAQ"}
@@ -4,8 +4,8 @@ import { DoomHarnessContext, HarnessState } from "@agimon-ai/doompi-config/types
4
4
  * Live domain switching: skills, plugin hooks, MCP config and subagent directories.
5
5
  *
6
6
  * Writes through the harness state module and relies on Pi's reload
7
- * re-executing extension modules, which drops their module-level caches and
8
- * makes them re-read the environment.
7
+ * re-executing extension modules. The replacement Config factory publishes the
8
+ * persisted MCP projection on the new session's Cordis root.
9
9
  *
10
10
  * What reload cannot do is change the SET of loaded extensions: Pi freezes the
11
11
  * CLI --extension list at construction and re-resolves that same array on every
@@ -1 +1 @@
1
- {"version":3,"file":"applyDomains.d.mts","names":[],"sources":["../../src/adapters/applyDomains.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA4BsB,aAAa,uBAAuB,OAAO,qBAAqB,QAAQ"}
1
+ {"version":3,"file":"applyDomains.d.mts","names":[],"sources":["../../src/adapters/applyDomains.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA2BsB,aAAa,uBAAuB,OAAO,qBAAqB,QAAQ"}
@@ -1,2 +1,2 @@
1
- import{resolveMcpAllowlist as e}from"./mcpFilter.mjs";import{mcpSessionEnvironment as t}from"./mcpSessionEnvironment.mjs";import{materializePluginEntries as n}from"./pluginMaterializer.mjs";import{collectResources as r}from"./resourceCollector.mjs";import i from"node:path";import{resolvePluginEntries as a,resolveSharedSkills as o}from"@agimon-ai/doompi-config/domains";import{requireHarnessPaths as s,updateHarnessState as c}from"@agimon-ai/doompi-config/harnessStore";import{SESSION_ENV_VAR as l}from"@agimon-ai/doompi-mcp";async function u(u,d){let{root:f,temporaryDirectory:p}=s(d),m=await n(a(f,u,[...d.pluginDirectories])),h=e(f,u),g=await r(f,m,{agents:d.agents,mcp:d.mcp,temporaryDirectory:p,sharedSkills:o(f,u),mcpAllowlist:h});d.mcp?Object.assign(process.env,t({repoRoot:f,stagingDirectory:g.temporaryDirectory,generatedConfigPath:g.mcpConfigPath,pluginConfigPaths:g.pluginMcpConfigPaths,allowlist:h})):delete process.env[l];let _=c({domains:u,skillDirectories:g.skillDirectories,pluginHooks:g.pluginHooks,mcpConfigPath:g.mcpConfigPath});return process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS=g.agentDirectories.join(i.delimiter),process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS=g.skillDirectories.join(i.delimiter),_}export{u as applyDomains};
1
+ import{resolveMcpAllowlist as e}from"./mcpFilter.mjs";import{materializePluginEntries as t}from"./pluginMaterializer.mjs";import{collectResources as n}from"./resourceCollector.mjs";import r from"node:fs";import i from"node:path";import{resolvePluginEntries as a,resolveSharedSkills as o}from"@agimon-ai/doompi-config/domains";import{requireHarnessPaths as s,updateHarnessState as c}from"@agimon-ai/doompi-config/harnessStore";async function l(l,u){let{root:d,temporaryDirectory:f}=s(u),p=await t(a(d,l,[...u.pluginDirectories])),m=e(d,l),h=await r.promises.mkdtemp(i.join(f,`domains-`)),g;try{g=await n(d,p,{agents:u.agents,mcp:u.mcp,temporaryDirectory:h,sharedSkills:o(d,l),mcpAllowlist:m})}catch(e){throw await r.promises.rm(h,{recursive:!0,force:!0}),e}let _;try{_=c({domains:l,skillDirectories:g.skillDirectories,agentDirectories:g.agentDirectories,pluginHooks:g.pluginHooks,mcpConfigPath:g.mcpConfigPath,mcpProjection:g.mcpProjection})}catch(e){throw await r.promises.rm(h,{recursive:!0,force:!0}),e}return process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS=g.agentDirectories.join(i.delimiter),process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS=g.skillDirectories.join(i.delimiter),_}export{l as applyDomains};
2
2
  //# sourceMappingURL=applyDomains.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"applyDomains.mjs","names":[],"sources":["../../src/adapters/applyDomains.ts"],"sourcesContent":["import path from 'node:path';\nimport { resolvePluginEntries, resolveSharedSkills } from '@agimon-ai/doompi-config/domains';\nimport { requireHarnessPaths, updateHarnessState } from '@agimon-ai/doompi-config/harnessStore';\nimport type { DoomHarnessContext, HarnessState } from '@agimon-ai/doompi-config/types';\nimport { SESSION_ENV_VAR } from '@agimon-ai/doompi-mcp';\nimport { resolveMcpAllowlist } from './mcpFilter.ts';\nimport { mcpSessionEnvironment } from './mcpSessionEnvironment.ts';\nimport { materializePluginEntries } from './pluginMaterializer.ts';\nimport { collectResources } from './resourceCollector.ts';\n\n/**\n * Live domain switching: skills, plugin hooks, MCP config and subagent directories.\n *\n * Writes through the harness state module and relies on Pi's reload\n * re-executing extension modules, which drops their module-level caches and\n * makes them re-read the environment.\n *\n * What reload cannot do is change the SET of loaded extensions: Pi freezes the\n * CLI --extension list at construction and re-resolves that same array on every\n * reload. Callers report those parts as needing a relaunch instead of pretending\n * they applied.\n *\n * The major-mode and profile switches live in\n * @agimon-ai/doompi-config/selectionSwitch: they are config writes with no\n * resource staging, and the synced startup path applies them before any command\n * exists to run. Domains stays here because it stages plugins, skills and MCP\n * config.\n */\nexport async function applyDomains(domainNames: string[], state: DoomHarnessContext): Promise<HarnessState> {\n const { root, temporaryDirectory } = requireHarnessPaths(state);\n\n const pluginEntries = await materializePluginEntries(\n resolvePluginEntries(root, domainNames, [...state.pluginDirectories]),\n );\n const mcpAllowlist = resolveMcpAllowlist(root, domainNames);\n const resources = await collectResources(root, pluginEntries, {\n agents: state.agents,\n mcp: state.mcp,\n temporaryDirectory,\n sharedSkills: resolveSharedSkills(root, domainNames),\n mcpAllowlist,\n });\n\n if (state.mcp) {\n Object.assign(\n process.env,\n mcpSessionEnvironment({\n repoRoot: root,\n stagingDirectory: resources.temporaryDirectory,\n generatedConfigPath: resources.mcpConfigPath,\n pluginConfigPaths: resources.pluginMcpConfigPaths,\n allowlist: mcpAllowlist,\n }),\n );\n } else {\n delete process.env[SESSION_ENV_VAR];\n }\n const updated = updateHarnessState({\n domains: domainNames,\n skillDirectories: resources.skillDirectories,\n pluginHooks: resources.pluginHooks,\n mcpConfigPath: resources.mcpConfigPath,\n });\n process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS = resources.agentDirectories.join(path.delimiter);\n process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS = resources.skillDirectories.join(path.delimiter);\n return updated;\n}\n"],"mappings":"+gBA4BA,eAAsB,EAAa,EAAuB,EAAkD,CAC1G,GAAM,CAAE,OAAM,sBAAuB,EAAoB,CAAK,EAExD,EAAgB,MAAM,EAC1B,EAAqB,EAAM,EAAa,CAAC,GAAG,EAAM,iBAAiB,CAAC,CACtE,EACM,EAAe,EAAoB,EAAM,CAAW,EACpD,EAAY,MAAM,EAAiB,EAAM,EAAe,CAC5D,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,qBACA,aAAc,EAAoB,EAAM,CAAW,EACnD,cACF,CAAC,EAEG,EAAM,IACR,OAAO,OACL,QAAQ,IACR,EAAsB,CACpB,SAAU,EACV,iBAAkB,EAAU,mBAC5B,oBAAqB,EAAU,cAC/B,kBAAmB,EAAU,qBAC7B,UAAW,CACb,CAAC,CACH,EAEA,OAAO,QAAQ,IAAI,GAErB,IAAM,EAAU,EAAmB,CACjC,QAAS,EACT,iBAAkB,EAAU,iBAC5B,YAAa,EAAU,YACvB,cAAe,EAAU,aAC3B,CAAC,EAGD,MAFA,SAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAK,EAAK,SAAS,EACzF,QAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAK,EAAK,SAAS,EAClF,CACT"}
1
+ {"version":3,"file":"applyDomains.mjs","names":[],"sources":["../../src/adapters/applyDomains.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { resolvePluginEntries, resolveSharedSkills } from '@agimon-ai/doompi-config/domains';\nimport { requireHarnessPaths, updateHarnessState } from '@agimon-ai/doompi-config/harnessStore';\nimport type { DoomHarnessContext, HarnessState } from '@agimon-ai/doompi-config/types';\nimport { resolveMcpAllowlist } from './mcpFilter.ts';\nimport { materializePluginEntries } from './pluginMaterializer.ts';\nimport { collectResources } from './resourceCollector.ts';\n\n/**\n * Live domain switching: skills, plugin hooks, MCP config and subagent directories.\n *\n * Writes through the harness state module and relies on Pi's reload\n * re-executing extension modules. The replacement Config factory publishes the\n * persisted MCP projection on the new session's Cordis root.\n *\n * What reload cannot do is change the SET of loaded extensions: Pi freezes the\n * CLI --extension list at construction and re-resolves that same array on every\n * reload. Callers report those parts as needing a relaunch instead of pretending\n * they applied.\n *\n * The major-mode and profile switches live in\n * @agimon-ai/doompi-config/selectionSwitch: they are config writes with no\n * resource staging, and the synced startup path applies them before any command\n * exists to run. Domains stays here because it stages plugins, skills and MCP\n * config.\n */\nexport async function applyDomains(domainNames: string[], state: DoomHarnessContext): Promise<HarnessState> {\n const { root, temporaryDirectory } = requireHarnessPaths(state);\n\n const pluginEntries = await materializePluginEntries(\n resolvePluginEntries(root, domainNames, [...state.pluginDirectories]),\n );\n const mcpAllowlist = resolveMcpAllowlist(root, domainNames);\n // Keep the active generation untouched while the candidate is parsed and\n // normalized. Pi's reload later removes the whole process-owned run root, so\n // retired generations need no eager destructive cleanup here.\n const candidateDirectory = await fs.promises.mkdtemp(path.join(temporaryDirectory, 'domains-'));\n let resources: Awaited<ReturnType<typeof collectResources>>;\n try {\n resources = await collectResources(root, pluginEntries, {\n agents: state.agents,\n mcp: state.mcp,\n temporaryDirectory: candidateDirectory,\n sharedSkills: resolveSharedSkills(root, domainNames),\n mcpAllowlist,\n });\n } catch (error) {\n await fs.promises.rm(candidateDirectory, { recursive: true, force: true });\n throw error;\n }\n\n let updated: HarnessState;\n try {\n updated = updateHarnessState({\n domains: domainNames,\n skillDirectories: resources.skillDirectories,\n agentDirectories: resources.agentDirectories,\n pluginHooks: resources.pluginHooks,\n mcpConfigPath: resources.mcpConfigPath,\n mcpProjection: resources.mcpProjection,\n });\n } catch (error) {\n await fs.promises.rm(candidateDirectory, { recursive: true, force: true });\n throw error;\n }\n process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS = resources.agentDirectories.join(path.delimiter);\n process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS = resources.skillDirectories.join(path.delimiter);\n return updated;\n}\n"],"mappings":"0aA2BA,eAAsB,EAAa,EAAuB,EAAkD,CAC1G,GAAM,CAAE,OAAM,sBAAuB,EAAoB,CAAK,EAExD,EAAgB,MAAM,EAC1B,EAAqB,EAAM,EAAa,CAAC,GAAG,EAAM,iBAAiB,CAAC,CACtE,EACM,EAAe,EAAoB,EAAM,CAAW,EAIpD,EAAqB,MAAM,EAAG,SAAS,QAAQ,EAAK,KAAK,EAAoB,UAAU,CAAC,EAC1F,EACJ,GAAI,CACF,EAAY,MAAM,EAAiB,EAAM,EAAe,CACtD,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,mBAAoB,EACpB,aAAc,EAAoB,EAAM,CAAW,EACnD,cACF,CAAC,CACH,OAAS,EAAO,CAEd,MADA,MAAM,EAAG,SAAS,GAAG,EAAoB,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnE,CACR,CAEA,IAAI,EACJ,GAAI,CACF,EAAU,EAAmB,CAC3B,QAAS,EACT,iBAAkB,EAAU,iBAC5B,iBAAkB,EAAU,iBAC5B,YAAa,EAAU,YACvB,cAAe,EAAU,cACzB,cAAe,EAAU,aAC3B,CAAC,CACH,OAAS,EAAO,CAEd,MADA,MAAM,EAAG,SAAS,GAAG,EAAoB,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnE,CACR,CAGA,MAFA,SAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAK,EAAK,SAAS,EACzF,QAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAK,EAAK,SAAS,EAClF,CACT"}
@@ -1,2 +1,2 @@
1
- const e=require("../services/domainText.cjs");let t=require("@agimon-ai/doompi-config/harnessStore"),n=require("@agimon-ai/doompi-config/piContext");function r(){let r,i=()=>r??=import(`@agimon-ai/doompi-config/domains`);return{list:async e=>{let r=(0,n.requireDoomConfigContext)(e).harness,a=(0,t.requireHarnessRoot)(r),{defaultDomainsForMajorMode:o,listDomainNames:s,loadDomains:c}=await i(),l=[...new Set(s(a))].sort(),u=c(a),d=(0,n.requireDoomConfigContext)(e).pendingSelection?void 0:(0,n.readDoomConfigSelection)(e),f=d?[...d.domains]:[...r.domains],p=o(r.majorMode,process.env,u.defaultDomains);return{active:f,effective:d?[...d.domains]:r.domains.length>0?[...r.domains]:[...p],available:l}},validate:async(r,a)=>{let o=e.normalizeDomainNames(a),s=(0,n.requireDoomConfigContext)(r).harness,c=(0,t.requireHarnessRoot)(s),{listDomainNames:l,resolvePluginEntries:u}=await i(),d=new Set(l(c)),f=o.filter(e=>!d.has(e));if(f.length>0)throw Error(`Unknown domain: ${f.join(`, `)}`);return u(c,o,[...s.pluginDirectories]),o},describe:async e=>{let t=(0,n.requireDoomConfigContext)(e).harness.root;if(!t)return{};let{loadDomains:r}=await i();return Object.fromEntries(Object.entries(r(t).domains).map(([e,t])=>[e,t.description]))},completions:async(e,t)=>{let{domainCompletionItems:n,domainCompletionPrefix:r,listDomainNames:a}=await i(),o=r(t);if(o===void 0)return;let s=n(a(e),o);return s.length===0?void 0:{prefix:o,items:s}}}}exports.createDomainCatalog=r;
1
+ const e=require("../services/domainText.cjs");let t=require("@agimon-ai/doompi-config/harnessStore"),n=require("@agimon-ai/doompi-config/piContext");function r(r){let i,a=()=>i??=import(`@agimon-ai/doompi-config/domains`);return{list:async e=>{let i=(0,n.requireDoomConfigContext)(r()),o=i.harness,s=(0,t.requireHarnessRoot)(o),{defaultDomainsForMajorMode:c,listDomainNames:l,loadDomains:u}=await a(),d=[...new Set(l(s))].sort(),f=u(s),p=i.pendingSelection?void 0:(0,n.readDoomConfigSelection)(e),m=p?[...p.domains]:[...o.domains],h=c(o.majorMode,process.env,f.defaultDomains);return{active:m,effective:p?[...p.domains]:o.domains.length>0?[...o.domains]:[...h],available:d}},validate:async(i,o)=>{let s=e.normalizeDomainNames(o),c=(0,n.requireDoomConfigContext)(r()).harness,l=(0,t.requireHarnessRoot)(c),{listDomainNames:u,resolvePluginEntries:d}=await a(),f=new Set(u(l)),p=s.filter(e=>!f.has(e));if(p.length>0)throw Error(`Unknown domain: ${p.join(`, `)}`);return d(l,s,[...c.pluginDirectories]),s},describe:async e=>{let t=(0,n.requireDoomConfigContext)(r()).harness.root;if(!t)return{};let{loadDomains:i}=await a();return Object.fromEntries(Object.entries(i(t).domains).map(([e,t])=>[e,t.description]))},completions:async(e,t)=>{let{domainCompletionItems:n,domainCompletionPrefix:r,listDomainNames:i}=await a(),o=r(t);if(o===void 0)return;let s=n(i(e),o);return s.length===0?void 0:{prefix:o,items:s}}}}exports.createDomainCatalog=r;
2
2
  //# sourceMappingURL=domainCatalog.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"domainCatalog.cjs","names":["requireDoomConfigContext","requireHarnessRoot","readDoomConfigSelection","normalizeDomainNames"],"sources":["../../src/adapters/domainCatalog.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { readDoomConfigSelection, requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { normalizeDomainNames } from '../services/domainText.ts';\nimport type { DomainCompletion, DomainListing } from '../types/domains.ts';\n\n/**\n * The manifest reader every surface of this package shares.\n *\n * Every session registers `/domains`, but most never run it, so the plugin\n * catalogue and the domain manifest stay behind a dynamic import: only a session\n * that actually lists or switches domains pays to read them.\n */\nexport function createDomainCatalog() {\n let config: Promise<typeof import('@agimon-ai/doompi-config/domains')> | undefined;\n const load = () => (config ??= import('@agimon-ai/doompi-config/domains'));\n\n const list = async (ctx: ExtensionContext): Promise<DomainListing> => {\n const state = requireDoomConfigContext(ctx).harness;\n const repoRoot = requireHarnessRoot(state);\n const { defaultDomainsForMajorMode, listDomainNames, loadDomains } = await load();\n const available = [...new Set(listDomainNames(repoRoot))].sort();\n const manifest = loadDomains(repoRoot);\n // A journalled selection outranks the environment, but only once the switch\n // that produced it has settled: a pending one has not been applied yet.\n const selection = requireDoomConfigContext(ctx).pendingSelection ? undefined : readDoomConfigSelection(ctx);\n const active = selection ? [...selection.domains] : [...state.domains];\n const fallback = defaultDomainsForMajorMode(state.majorMode, process.env, manifest.defaultDomains);\n const effective = selection\n ? [...selection.domains]\n : state.domains.length > 0\n ? [...state.domains]\n : [...fallback];\n return { active, effective, available };\n };\n\n const validate = async (ctx: ExtensionContext, values: readonly string[]): Promise<string[]> => {\n const selected = normalizeDomainNames(values);\n const state = requireDoomConfigContext(ctx).harness;\n const repoRoot = requireHarnessRoot(state);\n const { listDomainNames, resolvePluginEntries } = await load();\n const available = new Set(listDomainNames(repoRoot));\n const unknown = selected.filter((name) => !available.has(name));\n if (unknown.length > 0) throw new Error(`Unknown domain: ${unknown.join(', ')}`);\n resolvePluginEntries(repoRoot, selected, [...state.pluginDirectories]);\n return selected;\n };\n\n const describe = async (ctx: ExtensionContext): Promise<Record<string, string | undefined>> => {\n const root = requireDoomConfigContext(ctx).harness.root;\n if (!root) return {};\n const { loadDomains } = await load();\n return Object.fromEntries(\n Object.entries(loadDomains(root).domains).map(([name, domain]) => [name, domain.description]),\n );\n };\n\n const completions = async (root: string, textBeforeCursor: string): Promise<DomainCompletion | undefined> => {\n const { domainCompletionItems, domainCompletionPrefix, listDomainNames } = await load();\n const prefix = domainCompletionPrefix(textBeforeCursor);\n if (prefix === undefined) return undefined;\n // Re-read on every keystroke: a domain added while the session is running\n // has to be completable without a reload.\n const items = domainCompletionItems(listDomainNames(root), prefix);\n return items.length === 0 ? undefined : { prefix, items };\n };\n\n return { list, validate, describe, completions };\n}\n\nexport type DomainCatalog = ReturnType<typeof createDomainCatalog>;\n"],"mappings":"qJAaA,SAAgB,GAAsB,CACpC,IAAI,EACE,MAAc,IAAW,OAAO,oCAoDtC,MAAO,CAAE,UAlDW,IAAkD,CACpE,IAAM,GAAA,EAAQA,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,QACtC,GAAA,EAAWC,EAAAA,mBAAAA,CAAmB,CAAK,EACnC,CAAE,6BAA4B,kBAAiB,eAAgB,MAAM,EAAK,EAC1E,EAAY,CAAC,GAAG,IAAI,IAAI,EAAgB,CAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACzD,EAAW,EAAY,CAAQ,EAG/B,GAAA,EAAYD,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,iBAAmB,IAAA,IAAA,EAAYE,EAAAA,wBAAAA,CAAwB,CAAG,EACpG,EAAS,EAAY,CAAC,GAAG,EAAU,OAAO,EAAI,CAAC,GAAG,EAAM,OAAO,EAC/D,EAAW,EAA2B,EAAM,UAAW,QAAQ,IAAK,EAAS,cAAc,EAMjG,MAAO,CAAE,SAAQ,UALC,EACd,CAAC,GAAG,EAAU,OAAO,EACrB,EAAM,QAAQ,OAAS,EACrB,CAAC,GAAG,EAAM,OAAO,EACjB,CAAC,GAAG,CAAQ,EACU,WAAU,CACxC,EAiCe,eA/BS,EAAuB,IAAiD,CAC9F,IAAM,EAAWC,EAAAA,qBAAqB,CAAM,EACtC,GAAA,EAAQH,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,QACtC,GAAA,EAAWC,EAAAA,mBAAAA,CAAmB,CAAK,EACnC,CAAE,kBAAiB,wBAAyB,MAAM,EAAK,EACvD,EAAY,IAAI,IAAI,EAAgB,CAAQ,CAAC,EAC7C,EAAU,EAAS,OAAQ,GAAS,CAAC,EAAU,IAAI,CAAI,CAAC,EAC9D,GAAI,EAAQ,OAAS,EAAG,MAAU,MAAM,mBAAmB,EAAQ,KAAK,IAAI,GAAG,EAE/E,OADA,EAAqB,EAAU,EAAU,CAAC,GAAG,EAAM,iBAAiB,CAAC,EAC9D,CACT,EAqByB,cAnBD,IAAuE,CAC7F,IAAM,GAAA,EAAOD,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,QAAQ,KACnD,GAAI,CAAC,EAAM,MAAO,CAAC,EACnB,GAAM,CAAE,eAAgB,MAAM,EAAK,EACnC,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAY,CAAI,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAM,KAAY,CAAC,EAAM,EAAO,WAAW,CAAC,CAC9F,CACF,EAYmC,kBAVR,EAAc,IAAoE,CAC3G,GAAM,CAAE,wBAAuB,yBAAwB,mBAAoB,MAAM,EAAK,EAChF,EAAS,EAAuB,CAAgB,EACtD,GAAI,IAAW,IAAA,GAAW,OAG1B,IAAM,EAAQ,EAAsB,EAAgB,CAAI,EAAG,CAAM,EACjE,OAAO,EAAM,SAAW,EAAI,IAAA,GAAY,CAAE,SAAQ,OAAM,CAC1D,CAE+C,CACjD"}
1
+ {"version":3,"file":"domainCatalog.cjs","names":["requireDoomConfigContext","requireHarnessRoot","readDoomConfigSelection","normalizeDomainNames"],"sources":["../../src/adapters/domainCatalog.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { readDoomConfigSelection, requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { normalizeDomainNames } from '../services/domainText.ts';\nimport type { DomainCompletion, DomainListing } from '../types/domains.ts';\n\n/**\n * The manifest reader every surface of this package shares.\n *\n * Every session registers `/domains`, but most never run it, so the plugin\n * catalogue and the domain manifest stay behind a dynamic import: only a session\n * that actually lists or switches domains pays to read them.\n */\nexport function createDomainCatalog(cordisContext: () => Context) {\n let config: Promise<typeof import('@agimon-ai/doompi-config/domains')> | undefined;\n const load = () => (config ??= import('@agimon-ai/doompi-config/domains'));\n\n const list = async (ctx: ExtensionContext): Promise<DomainListing> => {\n const configContext = requireDoomConfigContext(cordisContext());\n const state = configContext.harness;\n const repoRoot = requireHarnessRoot(state);\n const { defaultDomainsForMajorMode, listDomainNames, loadDomains } = await load();\n const available = [...new Set(listDomainNames(repoRoot))].sort();\n const manifest = loadDomains(repoRoot);\n // A journalled selection outranks the environment, but only once the switch\n // that produced it has settled: a pending one has not been applied yet.\n const selection = configContext.pendingSelection ? undefined : readDoomConfigSelection(ctx);\n const active = selection ? [...selection.domains] : [...state.domains];\n const fallback = defaultDomainsForMajorMode(state.majorMode, process.env, manifest.defaultDomains);\n const effective = selection\n ? [...selection.domains]\n : state.domains.length > 0\n ? [...state.domains]\n : [...fallback];\n return { active, effective, available };\n };\n\n const validate = async (_ctx: ExtensionContext, values: readonly string[]): Promise<string[]> => {\n const selected = normalizeDomainNames(values);\n const state = requireDoomConfigContext(cordisContext()).harness;\n const repoRoot = requireHarnessRoot(state);\n const { listDomainNames, resolvePluginEntries } = await load();\n const available = new Set(listDomainNames(repoRoot));\n const unknown = selected.filter((name) => !available.has(name));\n if (unknown.length > 0) throw new Error(`Unknown domain: ${unknown.join(', ')}`);\n resolvePluginEntries(repoRoot, selected, [...state.pluginDirectories]);\n return selected;\n };\n\n const describe = async (_ctx: ExtensionContext): Promise<Record<string, string | undefined>> => {\n const root = requireDoomConfigContext(cordisContext()).harness.root;\n if (!root) return {};\n const { loadDomains } = await load();\n return Object.fromEntries(\n Object.entries(loadDomains(root).domains).map(([name, domain]) => [name, domain.description]),\n );\n };\n\n const completions = async (root: string, textBeforeCursor: string): Promise<DomainCompletion | undefined> => {\n const { domainCompletionItems, domainCompletionPrefix, listDomainNames } = await load();\n const prefix = domainCompletionPrefix(textBeforeCursor);\n if (prefix === undefined) return undefined;\n // Re-read on every keystroke: a domain added while the session is running\n // has to be completable without a reload.\n const items = domainCompletionItems(listDomainNames(root), prefix);\n return items.length === 0 ? undefined : { prefix, items };\n };\n\n return { list, validate, describe, completions };\n}\n\nexport type DomainCatalog = ReturnType<typeof createDomainCatalog>;\n"],"mappings":"qJAcA,SAAgB,EAAoB,EAA8B,CAChE,IAAI,EACE,MAAc,IAAW,OAAO,oCAqDtC,MAAO,CAAE,UAnDW,IAAkD,CACpE,IAAM,GAAA,EAAgBA,EAAAA,yBAAAA,CAAyB,EAAc,CAAC,EACxD,EAAQ,EAAc,QACtB,GAAA,EAAWC,EAAAA,mBAAAA,CAAmB,CAAK,EACnC,CAAE,6BAA4B,kBAAiB,eAAgB,MAAM,EAAK,EAC1E,EAAY,CAAC,GAAG,IAAI,IAAI,EAAgB,CAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACzD,EAAW,EAAY,CAAQ,EAG/B,EAAY,EAAc,iBAAmB,IAAA,IAAA,EAAYC,EAAAA,wBAAAA,CAAwB,CAAG,EACpF,EAAS,EAAY,CAAC,GAAG,EAAU,OAAO,EAAI,CAAC,GAAG,EAAM,OAAO,EAC/D,EAAW,EAA2B,EAAM,UAAW,QAAQ,IAAK,EAAS,cAAc,EAMjG,MAAO,CAAE,SAAQ,UALC,EACd,CAAC,GAAG,EAAU,OAAO,EACrB,EAAM,QAAQ,OAAS,EACrB,CAAC,GAAG,EAAM,OAAO,EACjB,CAAC,GAAG,CAAQ,EACU,WAAU,CACxC,EAiCe,eA/BS,EAAwB,IAAiD,CAC/F,IAAM,EAAWC,EAAAA,qBAAqB,CAAM,EACtC,GAAA,EAAQH,EAAAA,yBAAAA,CAAyB,EAAc,CAAC,CAAC,CAAC,QAClD,GAAA,EAAWC,EAAAA,mBAAAA,CAAmB,CAAK,EACnC,CAAE,kBAAiB,wBAAyB,MAAM,EAAK,EACvD,EAAY,IAAI,IAAI,EAAgB,CAAQ,CAAC,EAC7C,EAAU,EAAS,OAAQ,GAAS,CAAC,EAAU,IAAI,CAAI,CAAC,EAC9D,GAAI,EAAQ,OAAS,EAAG,MAAU,MAAM,mBAAmB,EAAQ,KAAK,IAAI,GAAG,EAE/E,OADA,EAAqB,EAAU,EAAU,CAAC,GAAG,EAAM,iBAAiB,CAAC,EAC9D,CACT,EAqByB,cAnBD,IAAwE,CAC9F,IAAM,GAAA,EAAOD,EAAAA,yBAAAA,CAAyB,EAAc,CAAC,CAAC,CAAC,QAAQ,KAC/D,GAAI,CAAC,EAAM,MAAO,CAAC,EACnB,GAAM,CAAE,eAAgB,MAAM,EAAK,EACnC,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAY,CAAI,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAM,KAAY,CAAC,EAAM,EAAO,WAAW,CAAC,CAC9F,CACF,EAYmC,kBAVR,EAAc,IAAoE,CAC3G,GAAM,CAAE,wBAAuB,yBAAwB,mBAAoB,MAAM,EAAK,EAChF,EAAS,EAAuB,CAAgB,EACtD,GAAI,IAAW,IAAA,GAAW,OAG1B,IAAM,EAAQ,EAAsB,EAAgB,CAAI,EAAG,CAAM,EACjE,OAAO,EAAM,SAAW,EAAI,IAAA,GAAY,CAAE,SAAQ,OAAM,CAC1D,CAE+C,CACjD"}
@@ -1,5 +1,6 @@
1
1
  import { DomainCompletion, DomainListing } from "../types/domains.cjs";
2
2
  import { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
+ import { Context } from "@deepseek-ai/cordis";
3
4
  //#region src/adapters/domainCatalog.d.ts
4
5
  /**
5
6
  * The manifest reader every surface of this package shares.
@@ -8,10 +9,10 @@ import { ExtensionContext } from "@earendil-works/pi-coding-agent";
8
9
  * catalogue and the domain manifest stay behind a dynamic import: only a session
9
10
  * that actually lists or switches domains pays to read them.
10
11
  */
11
- declare function createDomainCatalog(): {
12
+ declare function createDomainCatalog(cordisContext: () => Context): {
12
13
  list: (ctx: ExtensionContext) => Promise<DomainListing>;
13
- validate: (ctx: ExtensionContext, values: readonly string[]) => Promise<string[]>;
14
- describe: (ctx: ExtensionContext) => Promise<Record<string, string | undefined>>;
14
+ validate: (_ctx: ExtensionContext, values: readonly string[]) => Promise<string[]>;
15
+ describe: (_ctx: ExtensionContext) => Promise<Record<string, string | undefined>>;
15
16
  completions: (root: string, textBeforeCursor: string) => Promise<DomainCompletion | undefined>;
16
17
  };
17
18
  type DomainCatalog = ReturnType<typeof createDomainCatalog>;
@@ -1 +1 @@
1
- {"version":3,"file":"domainCatalog.d.cts","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"mappings":";;;;;;;;;;iBAagB;EAIW,OAAA,KAAA,qBAAmB,QAAQ;EAmBvB,WAAA,KAAA,kBAAgB,8BAA8B;EAY9C,WAAA,KAAA,qBAAmB,QAAQ;EASvB,cAAA,cAAM,6BAA6B,QAAQ;;KAalE,gBAAgB,kBAAkB"}
1
+ {"version":3,"file":"domainCatalog.d.cts","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"mappings":";;;;;;;;;;;iBAcgB,oBAAoB,qBAAqB;EAI9B,OAAA,KAAA,qBAAmB,QAAQ;EAoBtB,WAAA,MAAA,kBAAgB,8BAA8B;EAY9C,WAAA,MAAA,qBAAmB,QAAQ;EASxB,cAAA,cAAM,6BAA6B,QAAQ;;KAalE,gBAAgB,kBAAkB"}
@@ -1,5 +1,6 @@
1
1
  import { DomainCompletion, DomainListing } from "../types/domains.mjs";
2
2
  import { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
+ import { Context } from "@deepseek-ai/cordis";
3
4
  //#region src/adapters/domainCatalog.d.ts
4
5
  /**
5
6
  * The manifest reader every surface of this package shares.
@@ -8,10 +9,10 @@ import { ExtensionContext } from "@earendil-works/pi-coding-agent";
8
9
  * catalogue and the domain manifest stay behind a dynamic import: only a session
9
10
  * that actually lists or switches domains pays to read them.
10
11
  */
11
- declare function createDomainCatalog(): {
12
+ declare function createDomainCatalog(cordisContext: () => Context): {
12
13
  list: (ctx: ExtensionContext) => Promise<DomainListing>;
13
- validate: (ctx: ExtensionContext, values: readonly string[]) => Promise<string[]>;
14
- describe: (ctx: ExtensionContext) => Promise<Record<string, string | undefined>>;
14
+ validate: (_ctx: ExtensionContext, values: readonly string[]) => Promise<string[]>;
15
+ describe: (_ctx: ExtensionContext) => Promise<Record<string, string | undefined>>;
15
16
  completions: (root: string, textBeforeCursor: string) => Promise<DomainCompletion | undefined>;
16
17
  };
17
18
  type DomainCatalog = ReturnType<typeof createDomainCatalog>;
@@ -1 +1 @@
1
- {"version":3,"file":"domainCatalog.d.mts","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"mappings":";;;;;;;;;;iBAagB;EAIW,OAAA,KAAA,qBAAmB,QAAQ;EAmBvB,WAAA,KAAA,kBAAgB,8BAA8B;EAY9C,WAAA,KAAA,qBAAmB,QAAQ;EASvB,cAAA,cAAM,6BAA6B,QAAQ;;KAalE,gBAAgB,kBAAkB"}
1
+ {"version":3,"file":"domainCatalog.d.mts","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"mappings":";;;;;;;;;;;iBAcgB,oBAAoB,qBAAqB;EAI9B,OAAA,KAAA,qBAAmB,QAAQ;EAoBtB,WAAA,MAAA,kBAAgB,8BAA8B;EAY9C,WAAA,MAAA,qBAAmB,QAAQ;EASxB,cAAA,cAAM,6BAA6B,QAAQ;;KAalE,gBAAgB,kBAAkB"}
@@ -1,2 +1,2 @@
1
- import{normalizeDomainNames as e}from"../services/domainText.mjs";import{requireHarnessRoot as t}from"@agimon-ai/doompi-config/harnessStore";import{readDoomConfigSelection as n,requireDoomConfigContext as r}from"@agimon-ai/doompi-config/piContext";function i(){let i,a=()=>i??=import(`@agimon-ai/doompi-config/domains`);return{list:async e=>{let i=r(e).harness,o=t(i),{defaultDomainsForMajorMode:s,listDomainNames:c,loadDomains:l}=await a(),u=[...new Set(c(o))].sort(),d=l(o),f=r(e).pendingSelection?void 0:n(e),p=f?[...f.domains]:[...i.domains],m=s(i.majorMode,process.env,d.defaultDomains);return{active:p,effective:f?[...f.domains]:i.domains.length>0?[...i.domains]:[...m],available:u}},validate:async(n,i)=>{let o=e(i),s=r(n).harness,c=t(s),{listDomainNames:l,resolvePluginEntries:u}=await a(),d=new Set(l(c)),f=o.filter(e=>!d.has(e));if(f.length>0)throw Error(`Unknown domain: ${f.join(`, `)}`);return u(c,o,[...s.pluginDirectories]),o},describe:async e=>{let t=r(e).harness.root;if(!t)return{};let{loadDomains:n}=await a();return Object.fromEntries(Object.entries(n(t).domains).map(([e,t])=>[e,t.description]))},completions:async(e,t)=>{let{domainCompletionItems:n,domainCompletionPrefix:r,listDomainNames:i}=await a(),o=r(t);if(o===void 0)return;let s=n(i(e),o);return s.length===0?void 0:{prefix:o,items:s}}}}export{i as createDomainCatalog};
1
+ import{normalizeDomainNames as e}from"../services/domainText.mjs";import{requireHarnessRoot as t}from"@agimon-ai/doompi-config/harnessStore";import{readDoomConfigSelection as n,requireDoomConfigContext as r}from"@agimon-ai/doompi-config/piContext";function i(i){let a,o=()=>a??=import(`@agimon-ai/doompi-config/domains`);return{list:async e=>{let a=r(i()),s=a.harness,c=t(s),{defaultDomainsForMajorMode:l,listDomainNames:u,loadDomains:d}=await o(),f=[...new Set(u(c))].sort(),p=d(c),m=a.pendingSelection?void 0:n(e),h=m?[...m.domains]:[...s.domains],g=l(s.majorMode,process.env,p.defaultDomains);return{active:h,effective:m?[...m.domains]:s.domains.length>0?[...s.domains]:[...g],available:f}},validate:async(n,a)=>{let s=e(a),c=r(i()).harness,l=t(c),{listDomainNames:u,resolvePluginEntries:d}=await o(),f=new Set(u(l)),p=s.filter(e=>!f.has(e));if(p.length>0)throw Error(`Unknown domain: ${p.join(`, `)}`);return d(l,s,[...c.pluginDirectories]),s},describe:async e=>{let t=r(i()).harness.root;if(!t)return{};let{loadDomains:n}=await o();return Object.fromEntries(Object.entries(n(t).domains).map(([e,t])=>[e,t.description]))},completions:async(e,t)=>{let{domainCompletionItems:n,domainCompletionPrefix:r,listDomainNames:i}=await o(),a=r(t);if(a===void 0)return;let s=n(i(e),a);return s.length===0?void 0:{prefix:a,items:s}}}}export{i as createDomainCatalog};
2
2
  //# sourceMappingURL=domainCatalog.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"domainCatalog.mjs","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { readDoomConfigSelection, requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { normalizeDomainNames } from '../services/domainText.ts';\nimport type { DomainCompletion, DomainListing } from '../types/domains.ts';\n\n/**\n * The manifest reader every surface of this package shares.\n *\n * Every session registers `/domains`, but most never run it, so the plugin\n * catalogue and the domain manifest stay behind a dynamic import: only a session\n * that actually lists or switches domains pays to read them.\n */\nexport function createDomainCatalog() {\n let config: Promise<typeof import('@agimon-ai/doompi-config/domains')> | undefined;\n const load = () => (config ??= import('@agimon-ai/doompi-config/domains'));\n\n const list = async (ctx: ExtensionContext): Promise<DomainListing> => {\n const state = requireDoomConfigContext(ctx).harness;\n const repoRoot = requireHarnessRoot(state);\n const { defaultDomainsForMajorMode, listDomainNames, loadDomains } = await load();\n const available = [...new Set(listDomainNames(repoRoot))].sort();\n const manifest = loadDomains(repoRoot);\n // A journalled selection outranks the environment, but only once the switch\n // that produced it has settled: a pending one has not been applied yet.\n const selection = requireDoomConfigContext(ctx).pendingSelection ? undefined : readDoomConfigSelection(ctx);\n const active = selection ? [...selection.domains] : [...state.domains];\n const fallback = defaultDomainsForMajorMode(state.majorMode, process.env, manifest.defaultDomains);\n const effective = selection\n ? [...selection.domains]\n : state.domains.length > 0\n ? [...state.domains]\n : [...fallback];\n return { active, effective, available };\n };\n\n const validate = async (ctx: ExtensionContext, values: readonly string[]): Promise<string[]> => {\n const selected = normalizeDomainNames(values);\n const state = requireDoomConfigContext(ctx).harness;\n const repoRoot = requireHarnessRoot(state);\n const { listDomainNames, resolvePluginEntries } = await load();\n const available = new Set(listDomainNames(repoRoot));\n const unknown = selected.filter((name) => !available.has(name));\n if (unknown.length > 0) throw new Error(`Unknown domain: ${unknown.join(', ')}`);\n resolvePluginEntries(repoRoot, selected, [...state.pluginDirectories]);\n return selected;\n };\n\n const describe = async (ctx: ExtensionContext): Promise<Record<string, string | undefined>> => {\n const root = requireDoomConfigContext(ctx).harness.root;\n if (!root) return {};\n const { loadDomains } = await load();\n return Object.fromEntries(\n Object.entries(loadDomains(root).domains).map(([name, domain]) => [name, domain.description]),\n );\n };\n\n const completions = async (root: string, textBeforeCursor: string): Promise<DomainCompletion | undefined> => {\n const { domainCompletionItems, domainCompletionPrefix, listDomainNames } = await load();\n const prefix = domainCompletionPrefix(textBeforeCursor);\n if (prefix === undefined) return undefined;\n // Re-read on every keystroke: a domain added while the session is running\n // has to be completable without a reload.\n const items = domainCompletionItems(listDomainNames(root), prefix);\n return items.length === 0 ? undefined : { prefix, items };\n };\n\n return { list, validate, describe, completions };\n}\n\nexport type DomainCatalog = ReturnType<typeof createDomainCatalog>;\n"],"mappings":"wPAaA,SAAgB,GAAsB,CACpC,IAAI,EACE,MAAc,IAAW,OAAO,oCAoDtC,MAAO,CAAE,UAlDW,IAAkD,CACpE,IAAM,EAAQ,EAAyB,CAAG,CAAC,CAAC,QACtC,EAAW,EAAmB,CAAK,EACnC,CAAE,6BAA4B,kBAAiB,eAAgB,MAAM,EAAK,EAC1E,EAAY,CAAC,GAAG,IAAI,IAAI,EAAgB,CAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACzD,EAAW,EAAY,CAAQ,EAG/B,EAAY,EAAyB,CAAG,CAAC,CAAC,iBAAmB,IAAA,GAAY,EAAwB,CAAG,EACpG,EAAS,EAAY,CAAC,GAAG,EAAU,OAAO,EAAI,CAAC,GAAG,EAAM,OAAO,EAC/D,EAAW,EAA2B,EAAM,UAAW,QAAQ,IAAK,EAAS,cAAc,EAMjG,MAAO,CAAE,SAAQ,UALC,EACd,CAAC,GAAG,EAAU,OAAO,EACrB,EAAM,QAAQ,OAAS,EACrB,CAAC,GAAG,EAAM,OAAO,EACjB,CAAC,GAAG,CAAQ,EACU,WAAU,CACxC,EAiCe,eA/BS,EAAuB,IAAiD,CAC9F,IAAM,EAAW,EAAqB,CAAM,EACtC,EAAQ,EAAyB,CAAG,CAAC,CAAC,QACtC,EAAW,EAAmB,CAAK,EACnC,CAAE,kBAAiB,wBAAyB,MAAM,EAAK,EACvD,EAAY,IAAI,IAAI,EAAgB,CAAQ,CAAC,EAC7C,EAAU,EAAS,OAAQ,GAAS,CAAC,EAAU,IAAI,CAAI,CAAC,EAC9D,GAAI,EAAQ,OAAS,EAAG,MAAU,MAAM,mBAAmB,EAAQ,KAAK,IAAI,GAAG,EAE/E,OADA,EAAqB,EAAU,EAAU,CAAC,GAAG,EAAM,iBAAiB,CAAC,EAC9D,CACT,EAqByB,cAnBD,IAAuE,CAC7F,IAAM,EAAO,EAAyB,CAAG,CAAC,CAAC,QAAQ,KACnD,GAAI,CAAC,EAAM,MAAO,CAAC,EACnB,GAAM,CAAE,eAAgB,MAAM,EAAK,EACnC,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAY,CAAI,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAM,KAAY,CAAC,EAAM,EAAO,WAAW,CAAC,CAC9F,CACF,EAYmC,kBAVR,EAAc,IAAoE,CAC3G,GAAM,CAAE,wBAAuB,yBAAwB,mBAAoB,MAAM,EAAK,EAChF,EAAS,EAAuB,CAAgB,EACtD,GAAI,IAAW,IAAA,GAAW,OAG1B,IAAM,EAAQ,EAAsB,EAAgB,CAAI,EAAG,CAAM,EACjE,OAAO,EAAM,SAAW,EAAI,IAAA,GAAY,CAAE,SAAQ,OAAM,CAC1D,CAE+C,CACjD"}
1
+ {"version":3,"file":"domainCatalog.mjs","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { readDoomConfigSelection, requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { normalizeDomainNames } from '../services/domainText.ts';\nimport type { DomainCompletion, DomainListing } from '../types/domains.ts';\n\n/**\n * The manifest reader every surface of this package shares.\n *\n * Every session registers `/domains`, but most never run it, so the plugin\n * catalogue and the domain manifest stay behind a dynamic import: only a session\n * that actually lists or switches domains pays to read them.\n */\nexport function createDomainCatalog(cordisContext: () => Context) {\n let config: Promise<typeof import('@agimon-ai/doompi-config/domains')> | undefined;\n const load = () => (config ??= import('@agimon-ai/doompi-config/domains'));\n\n const list = async (ctx: ExtensionContext): Promise<DomainListing> => {\n const configContext = requireDoomConfigContext(cordisContext());\n const state = configContext.harness;\n const repoRoot = requireHarnessRoot(state);\n const { defaultDomainsForMajorMode, listDomainNames, loadDomains } = await load();\n const available = [...new Set(listDomainNames(repoRoot))].sort();\n const manifest = loadDomains(repoRoot);\n // A journalled selection outranks the environment, but only once the switch\n // that produced it has settled: a pending one has not been applied yet.\n const selection = configContext.pendingSelection ? undefined : readDoomConfigSelection(ctx);\n const active = selection ? [...selection.domains] : [...state.domains];\n const fallback = defaultDomainsForMajorMode(state.majorMode, process.env, manifest.defaultDomains);\n const effective = selection\n ? [...selection.domains]\n : state.domains.length > 0\n ? [...state.domains]\n : [...fallback];\n return { active, effective, available };\n };\n\n const validate = async (_ctx: ExtensionContext, values: readonly string[]): Promise<string[]> => {\n const selected = normalizeDomainNames(values);\n const state = requireDoomConfigContext(cordisContext()).harness;\n const repoRoot = requireHarnessRoot(state);\n const { listDomainNames, resolvePluginEntries } = await load();\n const available = new Set(listDomainNames(repoRoot));\n const unknown = selected.filter((name) => !available.has(name));\n if (unknown.length > 0) throw new Error(`Unknown domain: ${unknown.join(', ')}`);\n resolvePluginEntries(repoRoot, selected, [...state.pluginDirectories]);\n return selected;\n };\n\n const describe = async (_ctx: ExtensionContext): Promise<Record<string, string | undefined>> => {\n const root = requireDoomConfigContext(cordisContext()).harness.root;\n if (!root) return {};\n const { loadDomains } = await load();\n return Object.fromEntries(\n Object.entries(loadDomains(root).domains).map(([name, domain]) => [name, domain.description]),\n );\n };\n\n const completions = async (root: string, textBeforeCursor: string): Promise<DomainCompletion | undefined> => {\n const { domainCompletionItems, domainCompletionPrefix, listDomainNames } = await load();\n const prefix = domainCompletionPrefix(textBeforeCursor);\n if (prefix === undefined) return undefined;\n // Re-read on every keystroke: a domain added while the session is running\n // has to be completable without a reload.\n const items = domainCompletionItems(listDomainNames(root), prefix);\n return items.length === 0 ? undefined : { prefix, items };\n };\n\n return { list, validate, describe, completions };\n}\n\nexport type DomainCatalog = ReturnType<typeof createDomainCatalog>;\n"],"mappings":"wPAcA,SAAgB,EAAoB,EAA8B,CAChE,IAAI,EACE,MAAc,IAAW,OAAO,oCAqDtC,MAAO,CAAE,UAnDW,IAAkD,CACpE,IAAM,EAAgB,EAAyB,EAAc,CAAC,EACxD,EAAQ,EAAc,QACtB,EAAW,EAAmB,CAAK,EACnC,CAAE,6BAA4B,kBAAiB,eAAgB,MAAM,EAAK,EAC1E,EAAY,CAAC,GAAG,IAAI,IAAI,EAAgB,CAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACzD,EAAW,EAAY,CAAQ,EAG/B,EAAY,EAAc,iBAAmB,IAAA,GAAY,EAAwB,CAAG,EACpF,EAAS,EAAY,CAAC,GAAG,EAAU,OAAO,EAAI,CAAC,GAAG,EAAM,OAAO,EAC/D,EAAW,EAA2B,EAAM,UAAW,QAAQ,IAAK,EAAS,cAAc,EAMjG,MAAO,CAAE,SAAQ,UALC,EACd,CAAC,GAAG,EAAU,OAAO,EACrB,EAAM,QAAQ,OAAS,EACrB,CAAC,GAAG,EAAM,OAAO,EACjB,CAAC,GAAG,CAAQ,EACU,WAAU,CACxC,EAiCe,eA/BS,EAAwB,IAAiD,CAC/F,IAAM,EAAW,EAAqB,CAAM,EACtC,EAAQ,EAAyB,EAAc,CAAC,CAAC,CAAC,QAClD,EAAW,EAAmB,CAAK,EACnC,CAAE,kBAAiB,wBAAyB,MAAM,EAAK,EACvD,EAAY,IAAI,IAAI,EAAgB,CAAQ,CAAC,EAC7C,EAAU,EAAS,OAAQ,GAAS,CAAC,EAAU,IAAI,CAAI,CAAC,EAC9D,GAAI,EAAQ,OAAS,EAAG,MAAU,MAAM,mBAAmB,EAAQ,KAAK,IAAI,GAAG,EAE/E,OADA,EAAqB,EAAU,EAAU,CAAC,GAAG,EAAM,iBAAiB,CAAC,EAC9D,CACT,EAqByB,cAnBD,IAAwE,CAC9F,IAAM,EAAO,EAAyB,EAAc,CAAC,CAAC,CAAC,QAAQ,KAC/D,GAAI,CAAC,EAAM,MAAO,CAAC,EACnB,GAAM,CAAE,eAAgB,MAAM,EAAK,EACnC,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAY,CAAI,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAM,KAAY,CAAC,EAAM,EAAO,WAAW,CAAC,CAC9F,CACF,EAYmC,kBAVR,EAAc,IAAoE,CAC3G,GAAM,CAAE,wBAAuB,yBAAwB,mBAAoB,MAAM,EAAK,EAChF,EAAS,EAAuB,CAAgB,EACtD,GAAI,IAAW,IAAA,GAAW,OAG1B,IAAM,EAAQ,EAAsB,EAAgB,CAAI,EAAG,CAAM,EACjE,OAAO,EAAM,SAAW,EAAI,IAAA,GAAY,CAAE,SAAQ,OAAM,CAC1D,CAE+C,CACjD"}
@@ -1,2 +1,2 @@
1
- const e=require("../types/domains.cjs"),t=Symbol.for(`@agimon-ai/doompi-domain.switch-handoff.v1`),n=3e4;function r(){let e=globalThis;return e[t]??={records:new Map}}function i(e){return Error(`Invalid domain switch handoff ${e}.`)}function a(e){for(let t of e){let e=t.codePointAt(0);if(e!==void 0&&(e<=31||e===127))return!0}return!1}function o(e,t,n,r){let o=e.trim();if(!o||o.length>n||a(o)||r&&!r.test(o))throw i(t);return o}function s(e){return o(e,`session id`,256)}function c(e){return o(e,`host generation`,256)}function l(t){return o(t,`operation id`,128,e.SAFE_DOMAIN_NAME)}function u(t){return o(t,`reload handoff token`,256,e.SAFE_DOMAIN_NAME)}function d(t){return o(t,`token`,256,e.SAFE_DOMAIN_NAME)}function f(t){if(t.length>32)throw Error(`A maximum of 32 domains may be selected.`);let n=[],r=new Set;for(let i of t){let t=o(i,`domain name`,128,e.SAFE_DOMAIN_NAME);r.has(t)||(r.add(t),n.push(t))}return n}function p(e=Date.now()){for(let[t,n]of r().records)n.expiresAt<=e&&r().records.delete(t)}function m(e,t){return e.sessionId===s(t.sessionId)?t.hostGeneration===void 0||e.hostGeneration===c(t.hostGeneration):!1}function h(e){return{...e,domains:[...e.domains]}}function g(e,t){p();let i=s(t.sessionId),a=c(t.hostGeneration),o=l(t.operationId),d=f(t.domains),m=u(t.reloadHandoffToken),g=Date.now(),_={token:`doom-domain-switch:${crypto.randomUUID()}`,sessionId:i,hostGeneration:a,operationId:o,domains:d,reloadHandoffToken:m,createdAt:g,expiresAt:g+n,ownerGeneration:e};return r().records.set(_.token,_),h(_)}function _(e,t){p();let n=d(t),i=r().records.get(n);if(!(!i||!m(i,e))&&(r().records.delete(n),!(i.expiresAt<=Date.now())))return h(i)}function v(e,t){p();let n=d(t),i=r().records.get(n);return!i||!m(i,e)?!1:r().records.delete(n)}function y(e,t){p();let n=s(t),i=0;for(let[t,a]of r().records)a.ownerGeneration===e&&a.sessionId===n&&(r().records.delete(t),i+=1);return i}function b(e){let t=0;for(let[n,i]of r().records)i.ownerGeneration===e&&(r().records.delete(n),t+=1);return t}function x(){let e=crypto.randomUUID(),t=!1;return{ownerGeneration:e,issue(n){if(t)throw Error(`Domain switch handoff store is disposed.`);return g(e,n)},consume(e,n){if(!t)return _(n,e)},discard(e,n){return!t&&v(n,e)},clearSession(n){return t?0:y(e,n)},dispose(){return t?0:(t=!0,b(e))}}}exports.DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS=32,exports.DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH=256,exports.DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH=128,exports.DOMAIN_SWITCH_HANDOFF_TTL_MS=n,exports.createDomainSwitchHandoffStore=x;
1
+ const e=require("../types/domains.cjs"),t=Symbol.for(`@agimon-ai/doompi-domain.switch-handoff.v1`),n=3e4;function r(){let e=globalThis;return e[t]??={records:new Map}}function i(e){return Error(`Invalid domain switch handoff ${e}.`)}function a(e){for(let t of e){let e=t.codePointAt(0);if(e!==void 0&&(e<=31||e===127))return!0}return!1}function o(e,t,n,r){let o=typeof e==`string`?e.trim():``;if(!o||o.length>n||a(o)||r&&!r.test(o))throw i(t);return o}function s(e){return o(e,`session id`,256)}function c(e){return o(e,`host generation`,256)}function l(t){return o(t,`operation id`,128,e.SAFE_DOMAIN_NAME)}function u(t){return o(t,`reload handoff token`,256,e.SAFE_DOMAIN_NAME)}function d(t){return o(t,`token`,256,e.SAFE_DOMAIN_NAME)}function f(t){if(t.length>32)throw Error(`A maximum of 32 domains may be selected.`);let n=[],r=new Set;for(let i of t){let t=o(i,`domain name`,128,e.SAFE_DOMAIN_NAME);r.has(t)||(r.add(t),n.push(t))}return n}function p(e=Date.now()){for(let[t,n]of r().records)n.expiresAt<=e&&r().records.delete(t)}function m(e,t){return e.sessionId===s(t.sessionId)&&e.hostGeneration===c(t.hostGeneration)}function h(e){return{...e,domains:[...e.domains]}}function g(e,t){p();let i=s(t.sessionId),a=c(t.hostGeneration),o=l(t.operationId),d=f(t.domains),m=u(t.reloadHandoffToken),g=Date.now(),_={token:`doom-domain-switch:${crypto.randomUUID()}`,sessionId:i,hostGeneration:a,operationId:o,domains:d,reloadHandoffToken:m,createdAt:g,expiresAt:g+n,ownerGeneration:e};return r().records.set(_.token,_),h(_)}function _(e,t){p();let n=d(t),i=r().records.get(n);if(!(!i||!m(i,e))&&(r().records.delete(n),!(i.expiresAt<=Date.now())))return h(i)}function v(e,t){p();let n=d(t),i=r().records.get(n);return!i||!m(i,e)?!1:r().records.delete(n)}function y(e,t){p();let n=s(t),i=0;for(let[t,a]of r().records)a.ownerGeneration===e&&a.sessionId===n&&(r().records.delete(t),i+=1);return i}function b(e){let t=0;for(let[n,i]of r().records)i.ownerGeneration===e&&(r().records.delete(n),t+=1);return t}function x(){let e=crypto.randomUUID(),t=!1;return{ownerGeneration:e,issue(n){if(t)throw Error(`Domain switch handoff store is disposed.`);return g(e,n)},consume(e,n){if(!t)return _(n,e)},discard(e,n){return!t&&v(n,e)},clearSession(n){return t?0:y(e,n)},dispose(){return t?0:(t=!0,b(e))}}}exports.DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS=32,exports.DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH=256,exports.DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH=128,exports.DOMAIN_SWITCH_HANDOFF_TTL_MS=n,exports.createDomainSwitchHandoffStore=x;
2
2
  //# sourceMappingURL=domainSwitchHandoff.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"domainSwitchHandoff.cjs","names":["SAFE_DOMAIN_NAME"],"sources":["../../src/adapters/domainSwitchHandoff.ts"],"sourcesContent":["import type {\n DomainSwitchHandoff,\n DomainSwitchHandoffIdentity,\n DomainSwitchHandoffRequest,\n DomainSwitchHandoffStore,\n} from '../types/handoff.ts';\nimport { SAFE_DOMAIN_NAME } from '../types/domains.ts';\n\nconst DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY = '@agimon-ai/doompi-domain.switch-handoff.v1';\nconst DOMAIN_SWITCH_HANDOFF_SYMBOL = Symbol.for(DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY);\nconst DOMAIN_SWITCH_TOKEN_PREFIX = 'doom-domain-switch:';\n\nexport const DOMAIN_SWITCH_HANDOFF_TTL_MS = 30_000;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS = 32;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH = 256;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH = 128;\n\ninterface StoredDomainSwitchHandoff extends DomainSwitchHandoff {\n readonly ownerGeneration: string;\n}\n\ninterface HandoffRegistry {\n readonly records: Map<string, StoredDomainSwitchHandoff>;\n}\n\ntype HandoffGlobal = typeof globalThis & { [DOMAIN_SWITCH_HANDOFF_SYMBOL]?: HandoffRegistry };\n\n/**\n * One registry per process, keyed by a well-known symbol.\n *\n * Pi can reload this module in place, and a reload that lost the parked handoffs\n * would strand the follow-up command the voice tool already sent. Ownership is\n * tracked per store instead, so a disposed store takes only its own records.\n */\nfunction registry(): HandoffRegistry {\n const root = globalThis as HandoffGlobal;\n return (root[DOMAIN_SWITCH_HANDOFF_SYMBOL] ??= { records: new Map() });\n}\n\nfunction invalid(label: string): Error {\n return new Error(`Invalid domain switch handoff ${label}.`);\n}\n\nfunction hasControlCharacter(value: string): boolean {\n for (const character of value) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) return true;\n }\n return false;\n}\n\nfunction normalized(value: string, label: string, maximum: number, pattern?: RegExp): string {\n const text = value.trim();\n if (!text || text.length > maximum || hasControlCharacter(text) || (pattern && !pattern.test(text))) {\n throw invalid(label);\n }\n return text;\n}\n\nfunction sessionId(value: string): string {\n return normalized(value, 'session id', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction hostGeneration(value: string): string {\n return normalized(value, 'host generation', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction operationId(value: string): string {\n return normalized(value, 'operation id', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction reloadToken(value: string): string {\n return normalized(value, 'reload handoff token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction token(value: string): string {\n return normalized(value, 'token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction domains(values: readonly string[]): string[] {\n if (values.length > DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS) {\n throw new Error(`A maximum of ${DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS} domains may be selected.`);\n }\n const result: string[] = [];\n const seen = new Set<string>();\n for (const value of values) {\n const name = normalized(value, 'domain name', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n if (seen.has(name)) continue;\n seen.add(name);\n result.push(name);\n }\n return result;\n}\n\nfunction prune(now = Date.now()): void {\n for (const [key, record] of registry().records) {\n if (record.expiresAt <= now) registry().records.delete(key);\n }\n}\n\nfunction identityMatches(record: DomainSwitchHandoff, identity: DomainSwitchHandoffIdentity): boolean {\n if (record.sessionId !== sessionId(identity.sessionId)) return false;\n return identity.hostGeneration === undefined || record.hostGeneration === hostGeneration(identity.hostGeneration);\n}\n\nfunction copy(record: DomainSwitchHandoff): DomainSwitchHandoff {\n return { ...record, domains: [...record.domains] };\n}\n\nfunction issue(ownerGeneration: string, request: DomainSwitchHandoffRequest): DomainSwitchHandoff {\n prune();\n const normalizedSessionId = sessionId(request.sessionId);\n const normalizedHostGeneration = hostGeneration(request.hostGeneration);\n const normalizedOperationId = operationId(request.operationId);\n const normalizedDomains = domains(request.domains);\n const normalizedReloadToken = reloadToken(request.reloadHandoffToken);\n const createdAt = Date.now();\n const record: StoredDomainSwitchHandoff = {\n token: `${DOMAIN_SWITCH_TOKEN_PREFIX}${crypto.randomUUID()}`,\n sessionId: normalizedSessionId,\n hostGeneration: normalizedHostGeneration,\n operationId: normalizedOperationId,\n domains: normalizedDomains,\n reloadHandoffToken: normalizedReloadToken,\n createdAt,\n expiresAt: createdAt + DOMAIN_SWITCH_HANDOFF_TTL_MS,\n ownerGeneration,\n };\n registry().records.set(record.token, record);\n return copy(record);\n}\n\nfunction consume(identity: DomainSwitchHandoffIdentity, handoffToken: string): DomainSwitchHandoff | undefined {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return undefined;\n registry().records.delete(normalizedToken);\n if (record.expiresAt <= Date.now()) return undefined;\n return copy(record);\n}\n\nfunction discard(identity: DomainSwitchHandoffIdentity, handoffToken: string): boolean {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return false;\n return registry().records.delete(normalizedToken);\n}\n\nfunction clearOwnedSession(ownerGeneration: string, requestedSessionId: string): number {\n prune();\n const normalizedSessionId = sessionId(requestedSessionId);\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration || record.sessionId !== normalizedSessionId) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nfunction disposeOwner(ownerGeneration: string): number {\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nexport function createDomainSwitchHandoffStore(): DomainSwitchHandoffStore {\n const ownerGeneration = crypto.randomUUID();\n let disposed = false;\n return {\n ownerGeneration,\n issue(request) {\n if (disposed) throw new Error('Domain switch handoff store is disposed.');\n return issue(ownerGeneration, request);\n },\n consume(handoffToken, identity) {\n if (disposed) return undefined;\n return consume(identity, handoffToken);\n },\n discard(handoffToken, identity) {\n if (disposed) return false;\n return discard(identity, handoffToken);\n },\n clearSession(requestedSessionId) {\n if (disposed) return 0;\n return clearOwnedSession(ownerGeneration, requestedSessionId);\n },\n dispose() {\n if (disposed) return 0;\n disposed = true;\n return disposeOwner(ownerGeneration);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"domainSwitchHandoff.cjs","names":["SAFE_DOMAIN_NAME"],"sources":["../../src/adapters/domainSwitchHandoff.ts"],"sourcesContent":["import type {\n DomainSwitchHandoff,\n DomainSwitchHandoffIdentity,\n DomainSwitchHandoffRequest,\n DomainSwitchHandoffStore,\n} from '../types/handoff.ts';\nimport { SAFE_DOMAIN_NAME } from '../types/domains.ts';\n\nconst DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY = '@agimon-ai/doompi-domain.switch-handoff.v1';\nconst DOMAIN_SWITCH_HANDOFF_SYMBOL = Symbol.for(DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY);\nconst DOMAIN_SWITCH_TOKEN_PREFIX = 'doom-domain-switch:';\n\nexport const DOMAIN_SWITCH_HANDOFF_TTL_MS = 30_000;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS = 32;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH = 256;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH = 128;\n\ninterface StoredDomainSwitchHandoff extends DomainSwitchHandoff {\n readonly ownerGeneration: string;\n}\n\ninterface HandoffRegistry {\n readonly records: Map<string, StoredDomainSwitchHandoff>;\n}\n\ntype HandoffGlobal = typeof globalThis & { [DOMAIN_SWITCH_HANDOFF_SYMBOL]?: HandoffRegistry };\n\n/**\n * One registry per process, keyed by a well-known symbol.\n *\n * Pi can reload this module in place, and a reload that lost the parked handoffs\n * would strand the follow-up command the voice tool already sent. Ownership is\n * tracked per store instead, so a disposed store takes only its own records.\n */\nfunction registry(): HandoffRegistry {\n const root = globalThis as HandoffGlobal;\n return (root[DOMAIN_SWITCH_HANDOFF_SYMBOL] ??= { records: new Map() });\n}\n\nfunction invalid(label: string): Error {\n return new Error(`Invalid domain switch handoff ${label}.`);\n}\n\nfunction hasControlCharacter(value: string): boolean {\n for (const character of value) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) return true;\n }\n return false;\n}\n\nfunction normalized(value: unknown, label: string, maximum: number, pattern?: RegExp): string {\n const text = typeof value === 'string' ? value.trim() : '';\n if (!text || text.length > maximum || hasControlCharacter(text) || (pattern && !pattern.test(text))) {\n throw invalid(label);\n }\n return text;\n}\n\nfunction sessionId(value: string): string {\n return normalized(value, 'session id', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction hostGeneration(value: string): string {\n return normalized(value, 'host generation', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction operationId(value: string): string {\n return normalized(value, 'operation id', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction reloadToken(value: string): string {\n return normalized(value, 'reload handoff token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction token(value: string): string {\n return normalized(value, 'token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction domains(values: readonly string[]): string[] {\n if (values.length > DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS) {\n throw new Error(`A maximum of ${DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS} domains may be selected.`);\n }\n const result: string[] = [];\n const seen = new Set<string>();\n for (const value of values) {\n const name = normalized(value, 'domain name', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n if (seen.has(name)) continue;\n seen.add(name);\n result.push(name);\n }\n return result;\n}\n\nfunction prune(now = Date.now()): void {\n for (const [key, record] of registry().records) {\n if (record.expiresAt <= now) registry().records.delete(key);\n }\n}\n\nfunction identityMatches(record: DomainSwitchHandoff, identity: DomainSwitchHandoffIdentity): boolean {\n if (record.sessionId !== sessionId(identity.sessionId)) return false;\n return record.hostGeneration === hostGeneration(identity.hostGeneration);\n}\n\nfunction copy(record: DomainSwitchHandoff): DomainSwitchHandoff {\n return { ...record, domains: [...record.domains] };\n}\n\nfunction issue(ownerGeneration: string, request: DomainSwitchHandoffRequest): DomainSwitchHandoff {\n prune();\n const normalizedSessionId = sessionId(request.sessionId);\n const normalizedHostGeneration = hostGeneration(request.hostGeneration);\n const normalizedOperationId = operationId(request.operationId);\n const normalizedDomains = domains(request.domains);\n const normalizedReloadToken = reloadToken(request.reloadHandoffToken);\n const createdAt = Date.now();\n const record: StoredDomainSwitchHandoff = {\n token: `${DOMAIN_SWITCH_TOKEN_PREFIX}${crypto.randomUUID()}`,\n sessionId: normalizedSessionId,\n hostGeneration: normalizedHostGeneration,\n operationId: normalizedOperationId,\n domains: normalizedDomains,\n reloadHandoffToken: normalizedReloadToken,\n createdAt,\n expiresAt: createdAt + DOMAIN_SWITCH_HANDOFF_TTL_MS,\n ownerGeneration,\n };\n registry().records.set(record.token, record);\n return copy(record);\n}\n\nfunction consume(identity: DomainSwitchHandoffIdentity, handoffToken: string): DomainSwitchHandoff | undefined {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return undefined;\n registry().records.delete(normalizedToken);\n if (record.expiresAt <= Date.now()) return undefined;\n return copy(record);\n}\n\nfunction discard(identity: DomainSwitchHandoffIdentity, handoffToken: string): boolean {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return false;\n return registry().records.delete(normalizedToken);\n}\n\nfunction clearOwnedSession(ownerGeneration: string, requestedSessionId: string): number {\n prune();\n const normalizedSessionId = sessionId(requestedSessionId);\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration || record.sessionId !== normalizedSessionId) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nfunction disposeOwner(ownerGeneration: string): number {\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nexport function createDomainSwitchHandoffStore(): DomainSwitchHandoffStore {\n const ownerGeneration = crypto.randomUUID();\n let disposed = false;\n return {\n ownerGeneration,\n issue(request) {\n if (disposed) throw new Error('Domain switch handoff store is disposed.');\n return issue(ownerGeneration, request);\n },\n consume(handoffToken, identity) {\n if (disposed) return undefined;\n return consume(identity, handoffToken);\n },\n discard(handoffToken, identity) {\n if (disposed) return false;\n return discard(identity, handoffToken);\n },\n clearSession(requestedSessionId) {\n if (disposed) return 0;\n return clearOwnedSession(ownerGeneration, requestedSessionId);\n },\n dispose() {\n if (disposed) return 0;\n disposed = true;\n return disposeOwner(ownerGeneration);\n },\n };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{SAFE_DOMAIN_NAME as e}from"../types/domains.mjs";const t=Symbol.for(`@agimon-ai/doompi-domain.switch-handoff.v1`),n=3e4,r=32,i=256,a=128;function o(){let e=globalThis;return e[t]??={records:new Map}}function s(e){return Error(`Invalid domain switch handoff ${e}.`)}function c(e){for(let t of e){let e=t.codePointAt(0);if(e!==void 0&&(e<=31||e===127))return!0}return!1}function l(e,t,n,r){let i=e.trim();if(!i||i.length>n||c(i)||r&&!r.test(i))throw s(t);return i}function u(e){return l(e,`session id`,256)}function d(e){return l(e,`host generation`,256)}function f(t){return l(t,`operation id`,128,e)}function p(t){return l(t,`reload handoff token`,256,e)}function m(t){return l(t,`token`,256,e)}function h(t){if(t.length>32)throw Error(`A maximum of 32 domains may be selected.`);let n=[],r=new Set;for(let i of t){let t=l(i,`domain name`,128,e);r.has(t)||(r.add(t),n.push(t))}return n}function g(e=Date.now()){for(let[t,n]of o().records)n.expiresAt<=e&&o().records.delete(t)}function _(e,t){return e.sessionId===u(t.sessionId)?t.hostGeneration===void 0||e.hostGeneration===d(t.hostGeneration):!1}function v(e){return{...e,domains:[...e.domains]}}function y(e,t){g();let r=u(t.sessionId),i=d(t.hostGeneration),a=f(t.operationId),s=h(t.domains),c=p(t.reloadHandoffToken),l=Date.now(),m={token:`doom-domain-switch:${crypto.randomUUID()}`,sessionId:r,hostGeneration:i,operationId:a,domains:s,reloadHandoffToken:c,createdAt:l,expiresAt:l+n,ownerGeneration:e};return o().records.set(m.token,m),v(m)}function b(e,t){g();let n=m(t),r=o().records.get(n);if(!(!r||!_(r,e))&&(o().records.delete(n),!(r.expiresAt<=Date.now())))return v(r)}function x(e,t){g();let n=m(t),r=o().records.get(n);return!r||!_(r,e)?!1:o().records.delete(n)}function S(e,t){g();let n=u(t),r=0;for(let[t,i]of o().records)i.ownerGeneration===e&&i.sessionId===n&&(o().records.delete(t),r+=1);return r}function C(e){let t=0;for(let[n,r]of o().records)r.ownerGeneration===e&&(o().records.delete(n),t+=1);return t}function w(){let e=crypto.randomUUID(),t=!1;return{ownerGeneration:e,issue(n){if(t)throw Error(`Domain switch handoff store is disposed.`);return y(e,n)},consume(e,n){if(!t)return b(n,e)},discard(e,n){return!t&&x(n,e)},clearSession(n){return t?0:S(e,n)},dispose(){return t?0:(t=!0,C(e))}}}export{r as DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS,i as DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH,a as DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH,n as DOMAIN_SWITCH_HANDOFF_TTL_MS,w as createDomainSwitchHandoffStore};
1
+ import{SAFE_DOMAIN_NAME as e}from"../types/domains.mjs";const t=Symbol.for(`@agimon-ai/doompi-domain.switch-handoff.v1`),n=3e4,r=32,i=256,a=128;function o(){let e=globalThis;return e[t]??={records:new Map}}function s(e){return Error(`Invalid domain switch handoff ${e}.`)}function c(e){for(let t of e){let e=t.codePointAt(0);if(e!==void 0&&(e<=31||e===127))return!0}return!1}function l(e,t,n,r){let i=typeof e==`string`?e.trim():``;if(!i||i.length>n||c(i)||r&&!r.test(i))throw s(t);return i}function u(e){return l(e,`session id`,256)}function d(e){return l(e,`host generation`,256)}function f(t){return l(t,`operation id`,128,e)}function p(t){return l(t,`reload handoff token`,256,e)}function m(t){return l(t,`token`,256,e)}function h(t){if(t.length>32)throw Error(`A maximum of 32 domains may be selected.`);let n=[],r=new Set;for(let i of t){let t=l(i,`domain name`,128,e);r.has(t)||(r.add(t),n.push(t))}return n}function g(e=Date.now()){for(let[t,n]of o().records)n.expiresAt<=e&&o().records.delete(t)}function _(e,t){return e.sessionId===u(t.sessionId)&&e.hostGeneration===d(t.hostGeneration)}function v(e){return{...e,domains:[...e.domains]}}function y(e,t){g();let r=u(t.sessionId),i=d(t.hostGeneration),a=f(t.operationId),s=h(t.domains),c=p(t.reloadHandoffToken),l=Date.now(),m={token:`doom-domain-switch:${crypto.randomUUID()}`,sessionId:r,hostGeneration:i,operationId:a,domains:s,reloadHandoffToken:c,createdAt:l,expiresAt:l+n,ownerGeneration:e};return o().records.set(m.token,m),v(m)}function b(e,t){g();let n=m(t),r=o().records.get(n);if(!(!r||!_(r,e))&&(o().records.delete(n),!(r.expiresAt<=Date.now())))return v(r)}function x(e,t){g();let n=m(t),r=o().records.get(n);return!r||!_(r,e)?!1:o().records.delete(n)}function S(e,t){g();let n=u(t),r=0;for(let[t,i]of o().records)i.ownerGeneration===e&&i.sessionId===n&&(o().records.delete(t),r+=1);return r}function C(e){let t=0;for(let[n,r]of o().records)r.ownerGeneration===e&&(o().records.delete(n),t+=1);return t}function w(){let e=crypto.randomUUID(),t=!1;return{ownerGeneration:e,issue(n){if(t)throw Error(`Domain switch handoff store is disposed.`);return y(e,n)},consume(e,n){if(!t)return b(n,e)},discard(e,n){return!t&&x(n,e)},clearSession(n){return t?0:S(e,n)},dispose(){return t?0:(t=!0,C(e))}}}export{r as DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS,i as DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH,a as DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH,n as DOMAIN_SWITCH_HANDOFF_TTL_MS,w as createDomainSwitchHandoffStore};
2
2
  //# sourceMappingURL=domainSwitchHandoff.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"domainSwitchHandoff.mjs","names":[],"sources":["../../src/adapters/domainSwitchHandoff.ts"],"sourcesContent":["import type {\n DomainSwitchHandoff,\n DomainSwitchHandoffIdentity,\n DomainSwitchHandoffRequest,\n DomainSwitchHandoffStore,\n} from '../types/handoff.ts';\nimport { SAFE_DOMAIN_NAME } from '../types/domains.ts';\n\nconst DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY = '@agimon-ai/doompi-domain.switch-handoff.v1';\nconst DOMAIN_SWITCH_HANDOFF_SYMBOL = Symbol.for(DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY);\nconst DOMAIN_SWITCH_TOKEN_PREFIX = 'doom-domain-switch:';\n\nexport const DOMAIN_SWITCH_HANDOFF_TTL_MS = 30_000;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS = 32;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH = 256;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH = 128;\n\ninterface StoredDomainSwitchHandoff extends DomainSwitchHandoff {\n readonly ownerGeneration: string;\n}\n\ninterface HandoffRegistry {\n readonly records: Map<string, StoredDomainSwitchHandoff>;\n}\n\ntype HandoffGlobal = typeof globalThis & { [DOMAIN_SWITCH_HANDOFF_SYMBOL]?: HandoffRegistry };\n\n/**\n * One registry per process, keyed by a well-known symbol.\n *\n * Pi can reload this module in place, and a reload that lost the parked handoffs\n * would strand the follow-up command the voice tool already sent. Ownership is\n * tracked per store instead, so a disposed store takes only its own records.\n */\nfunction registry(): HandoffRegistry {\n const root = globalThis as HandoffGlobal;\n return (root[DOMAIN_SWITCH_HANDOFF_SYMBOL] ??= { records: new Map() });\n}\n\nfunction invalid(label: string): Error {\n return new Error(`Invalid domain switch handoff ${label}.`);\n}\n\nfunction hasControlCharacter(value: string): boolean {\n for (const character of value) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) return true;\n }\n return false;\n}\n\nfunction normalized(value: string, label: string, maximum: number, pattern?: RegExp): string {\n const text = value.trim();\n if (!text || text.length > maximum || hasControlCharacter(text) || (pattern && !pattern.test(text))) {\n throw invalid(label);\n }\n return text;\n}\n\nfunction sessionId(value: string): string {\n return normalized(value, 'session id', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction hostGeneration(value: string): string {\n return normalized(value, 'host generation', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction operationId(value: string): string {\n return normalized(value, 'operation id', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction reloadToken(value: string): string {\n return normalized(value, 'reload handoff token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction token(value: string): string {\n return normalized(value, 'token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction domains(values: readonly string[]): string[] {\n if (values.length > DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS) {\n throw new Error(`A maximum of ${DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS} domains may be selected.`);\n }\n const result: string[] = [];\n const seen = new Set<string>();\n for (const value of values) {\n const name = normalized(value, 'domain name', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n if (seen.has(name)) continue;\n seen.add(name);\n result.push(name);\n }\n return result;\n}\n\nfunction prune(now = Date.now()): void {\n for (const [key, record] of registry().records) {\n if (record.expiresAt <= now) registry().records.delete(key);\n }\n}\n\nfunction identityMatches(record: DomainSwitchHandoff, identity: DomainSwitchHandoffIdentity): boolean {\n if (record.sessionId !== sessionId(identity.sessionId)) return false;\n return identity.hostGeneration === undefined || record.hostGeneration === hostGeneration(identity.hostGeneration);\n}\n\nfunction copy(record: DomainSwitchHandoff): DomainSwitchHandoff {\n return { ...record, domains: [...record.domains] };\n}\n\nfunction issue(ownerGeneration: string, request: DomainSwitchHandoffRequest): DomainSwitchHandoff {\n prune();\n const normalizedSessionId = sessionId(request.sessionId);\n const normalizedHostGeneration = hostGeneration(request.hostGeneration);\n const normalizedOperationId = operationId(request.operationId);\n const normalizedDomains = domains(request.domains);\n const normalizedReloadToken = reloadToken(request.reloadHandoffToken);\n const createdAt = Date.now();\n const record: StoredDomainSwitchHandoff = {\n token: `${DOMAIN_SWITCH_TOKEN_PREFIX}${crypto.randomUUID()}`,\n sessionId: normalizedSessionId,\n hostGeneration: normalizedHostGeneration,\n operationId: normalizedOperationId,\n domains: normalizedDomains,\n reloadHandoffToken: normalizedReloadToken,\n createdAt,\n expiresAt: createdAt + DOMAIN_SWITCH_HANDOFF_TTL_MS,\n ownerGeneration,\n };\n registry().records.set(record.token, record);\n return copy(record);\n}\n\nfunction consume(identity: DomainSwitchHandoffIdentity, handoffToken: string): DomainSwitchHandoff | undefined {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return undefined;\n registry().records.delete(normalizedToken);\n if (record.expiresAt <= Date.now()) return undefined;\n return copy(record);\n}\n\nfunction discard(identity: DomainSwitchHandoffIdentity, handoffToken: string): boolean {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return false;\n return registry().records.delete(normalizedToken);\n}\n\nfunction clearOwnedSession(ownerGeneration: string, requestedSessionId: string): number {\n prune();\n const normalizedSessionId = sessionId(requestedSessionId);\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration || record.sessionId !== normalizedSessionId) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nfunction disposeOwner(ownerGeneration: string): number {\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nexport function createDomainSwitchHandoffStore(): DomainSwitchHandoffStore {\n const ownerGeneration = crypto.randomUUID();\n let disposed = false;\n return {\n ownerGeneration,\n issue(request) {\n if (disposed) throw new Error('Domain switch handoff store is disposed.');\n return issue(ownerGeneration, request);\n },\n consume(handoffToken, identity) {\n if (disposed) return undefined;\n return consume(identity, handoffToken);\n },\n discard(handoffToken, identity) {\n if (disposed) return false;\n return discard(identity, handoffToken);\n },\n clearSession(requestedSessionId) {\n if (disposed) return 0;\n return clearOwnedSession(ownerGeneration, requestedSessionId);\n },\n dispose() {\n if (disposed) return 0;\n disposed = true;\n return disposeOwner(ownerGeneration);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"domainSwitchHandoff.mjs","names":[],"sources":["../../src/adapters/domainSwitchHandoff.ts"],"sourcesContent":["import type {\n DomainSwitchHandoff,\n DomainSwitchHandoffIdentity,\n DomainSwitchHandoffRequest,\n DomainSwitchHandoffStore,\n} from '../types/handoff.ts';\nimport { SAFE_DOMAIN_NAME } from '../types/domains.ts';\n\nconst DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY = '@agimon-ai/doompi-domain.switch-handoff.v1';\nconst DOMAIN_SWITCH_HANDOFF_SYMBOL = Symbol.for(DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY);\nconst DOMAIN_SWITCH_TOKEN_PREFIX = 'doom-domain-switch:';\n\nexport const DOMAIN_SWITCH_HANDOFF_TTL_MS = 30_000;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS = 32;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH = 256;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH = 128;\n\ninterface StoredDomainSwitchHandoff extends DomainSwitchHandoff {\n readonly ownerGeneration: string;\n}\n\ninterface HandoffRegistry {\n readonly records: Map<string, StoredDomainSwitchHandoff>;\n}\n\ntype HandoffGlobal = typeof globalThis & { [DOMAIN_SWITCH_HANDOFF_SYMBOL]?: HandoffRegistry };\n\n/**\n * One registry per process, keyed by a well-known symbol.\n *\n * Pi can reload this module in place, and a reload that lost the parked handoffs\n * would strand the follow-up command the voice tool already sent. Ownership is\n * tracked per store instead, so a disposed store takes only its own records.\n */\nfunction registry(): HandoffRegistry {\n const root = globalThis as HandoffGlobal;\n return (root[DOMAIN_SWITCH_HANDOFF_SYMBOL] ??= { records: new Map() });\n}\n\nfunction invalid(label: string): Error {\n return new Error(`Invalid domain switch handoff ${label}.`);\n}\n\nfunction hasControlCharacter(value: string): boolean {\n for (const character of value) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) return true;\n }\n return false;\n}\n\nfunction normalized(value: unknown, label: string, maximum: number, pattern?: RegExp): string {\n const text = typeof value === 'string' ? value.trim() : '';\n if (!text || text.length > maximum || hasControlCharacter(text) || (pattern && !pattern.test(text))) {\n throw invalid(label);\n }\n return text;\n}\n\nfunction sessionId(value: string): string {\n return normalized(value, 'session id', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction hostGeneration(value: string): string {\n return normalized(value, 'host generation', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction operationId(value: string): string {\n return normalized(value, 'operation id', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction reloadToken(value: string): string {\n return normalized(value, 'reload handoff token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction token(value: string): string {\n return normalized(value, 'token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction domains(values: readonly string[]): string[] {\n if (values.length > DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS) {\n throw new Error(`A maximum of ${DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS} domains may be selected.`);\n }\n const result: string[] = [];\n const seen = new Set<string>();\n for (const value of values) {\n const name = normalized(value, 'domain name', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n if (seen.has(name)) continue;\n seen.add(name);\n result.push(name);\n }\n return result;\n}\n\nfunction prune(now = Date.now()): void {\n for (const [key, record] of registry().records) {\n if (record.expiresAt <= now) registry().records.delete(key);\n }\n}\n\nfunction identityMatches(record: DomainSwitchHandoff, identity: DomainSwitchHandoffIdentity): boolean {\n if (record.sessionId !== sessionId(identity.sessionId)) return false;\n return record.hostGeneration === hostGeneration(identity.hostGeneration);\n}\n\nfunction copy(record: DomainSwitchHandoff): DomainSwitchHandoff {\n return { ...record, domains: [...record.domains] };\n}\n\nfunction issue(ownerGeneration: string, request: DomainSwitchHandoffRequest): DomainSwitchHandoff {\n prune();\n const normalizedSessionId = sessionId(request.sessionId);\n const normalizedHostGeneration = hostGeneration(request.hostGeneration);\n const normalizedOperationId = operationId(request.operationId);\n const normalizedDomains = domains(request.domains);\n const normalizedReloadToken = reloadToken(request.reloadHandoffToken);\n const createdAt = Date.now();\n const record: StoredDomainSwitchHandoff = {\n token: `${DOMAIN_SWITCH_TOKEN_PREFIX}${crypto.randomUUID()}`,\n sessionId: normalizedSessionId,\n hostGeneration: normalizedHostGeneration,\n operationId: normalizedOperationId,\n domains: normalizedDomains,\n reloadHandoffToken: normalizedReloadToken,\n createdAt,\n expiresAt: createdAt + DOMAIN_SWITCH_HANDOFF_TTL_MS,\n ownerGeneration,\n };\n registry().records.set(record.token, record);\n return copy(record);\n}\n\nfunction consume(identity: DomainSwitchHandoffIdentity, handoffToken: string): DomainSwitchHandoff | undefined {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return undefined;\n registry().records.delete(normalizedToken);\n if (record.expiresAt <= Date.now()) return undefined;\n return copy(record);\n}\n\nfunction discard(identity: DomainSwitchHandoffIdentity, handoffToken: string): boolean {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return false;\n return registry().records.delete(normalizedToken);\n}\n\nfunction clearOwnedSession(ownerGeneration: string, requestedSessionId: string): number {\n prune();\n const normalizedSessionId = sessionId(requestedSessionId);\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration || record.sessionId !== normalizedSessionId) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nfunction disposeOwner(ownerGeneration: string): number {\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nexport function createDomainSwitchHandoffStore(): DomainSwitchHandoffStore {\n const ownerGeneration = crypto.randomUUID();\n let disposed = false;\n return {\n ownerGeneration,\n issue(request) {\n if (disposed) throw new Error('Domain switch handoff store is disposed.');\n return issue(ownerGeneration, request);\n },\n consume(handoffToken, identity) {\n if (disposed) return undefined;\n return consume(identity, handoffToken);\n },\n discard(handoffToken, identity) {\n if (disposed) return false;\n return discard(identity, handoffToken);\n },\n clearSession(requestedSessionId) {\n if (disposed) return 0;\n return clearOwnedSession(ownerGeneration, requestedSessionId);\n },\n dispose() {\n if (disposed) return 0;\n disposed = true;\n return disposeOwner(ownerGeneration);\n },\n };\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("@agimon-ai/doompi-config/domains"),r=require("node:fs");r=e.__toESM(r,1);let i=require("yaml");const a=`agiflow-proxy`,o=`mcp-config.yaml`,s=`--config`;function c(e,t){let r=(0,n.loadDomains)(e),i=(0,n.expandDomainNames)(r,t);if(i.length===0)return;let a=new Set,o=new Set;for(let e of i){let t=r.domains[e]?.mcp;if(!t)return;for(let e of t.servers??[])a.add(e);for(let e of t.proxy??[])o.add(e)}return{servers:[...a],proxy:[...o]}}function l(e,t){if(!t||t.length===0)return e;let n=e.mcpServers??{},r={};for(let[e,i]of Object.entries(n))t.includes(e)&&(r[e]=i);return{...e,mcpServers:r}}function u(e,t){if(!t||t.length===0)return e;let n=(0,i.parse)(e)??{},r=n.mcpServers??{},a={},o=[];for(let[e,n]of Object.entries(r))t.includes(e)?a[e]=n:o.push(e);return`${[`# Generated by doompi for this run. Do not edit.`,`# Upstreams kept: ${Object.keys(a).join(`, `)||`(none)`}`,`# Upstreams removed by the domain allowlist: ${o.join(`, `)||`(none)`}`,``].join(`
2
- `)}${(0,i.stringify)({...n,mcpServers:a})}`}async function d(e,t){try{return await r.default.promises.copyFile(e,t),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function f(e,n,i){await r.default.promises.mkdir(i,{recursive:!0});let c=JSON.parse(await r.default.promises.readFile(e,`utf8`)),l=c.mcpServers??{},u=l[a],f=t.default.join(n,o);if(u){let e=[...u.args??[]],n=e.indexOf(s);if(n!==-1){let r=t.default.join(i,o);await d(f,r)&&(e[n+1]=r,l[a]={...u,args:e})}}let p=t.default.join(i,`mcp.json`);return await r.default.promises.writeFile(p,`${JSON.stringify({...c,mcpServers:l},null,2)}\n`,{mode:384}),p}async function p(e,n,i,c){if(!n)return e;let d=l(e,n.servers),f=d.mcpServers??{},p=f[a];if(!p||!n.proxy||n.proxy.length===0)return d;let m=[...p.args??[]],h=m.indexOf(s);if(h===-1||!m[h+1])return d;let g=t.default.resolve(i,m[h+1]),_;try{_=await r.default.promises.readFile(g,`utf8`)}catch(e){if(e.code===`ENOENT`)return d;throw e}let v=t.default.join(c,o);return await r.default.promises.writeFile(v,u(_,n.proxy),{mode:384}),m[h+1]=v,{...d,mcpServers:{...f,[a]:{...p,args:m}}}}exports.PROXY_SERVER_NAME=a,exports.applyMcpAllowlist=p,exports.filterMcpServers=l,exports.filterProxyConfig=u,exports.persistMcpConfig=f,exports.resolveMcpAllowlist=c;
1
+ const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("@agimon-ai/doompi-config/domains"),i=require("yaml");const a=`agiflow-proxy`,o=`mcp-config.yaml`,s=`--config`;function c(e,t){let n=(0,r.loadDomains)(e),i=(0,r.expandDomainNames)(n,t);if(i.length===0)return;let a=new Set,o=new Set;for(let e of i){let t=n.domains[e]?.mcp;if(!t)return;for(let e of t.servers??[])a.add(e);for(let e of t.proxy??[])o.add(e)}return{servers:[...a],proxy:[...o]}}function l(e,t){if(!t||t.length===0)return e;let n=e.mcpServers??{},r={};for(let[e,i]of Object.entries(n))t.includes(e)&&(r[e]=i);return{...e,mcpServers:r}}function u(e,t){if(!t||t.length===0)return e;let n=(0,i.parse)(e)??{},r=n.mcpServers??{},a={},o=[];for(let[e,n]of Object.entries(r))t.includes(e)?a[e]=n:o.push(e);return`${[`# Generated by doompi for this run. Do not edit.`,`# Upstreams kept: ${Object.keys(a).join(`, `)||`(none)`}`,`# Upstreams removed by the domain allowlist: ${o.join(`, `)||`(none)`}`,``].join(`
2
+ `)}${(0,i.stringify)({...n,mcpServers:a})}`}async function d(e,n){try{return await t.default.promises.copyFile(e,n),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function f(e,r,i){await t.default.promises.mkdir(i,{recursive:!0});let c=JSON.parse(await t.default.promises.readFile(e,`utf8`)),l=c.mcpServers??{},u=l[a],f=n.default.join(r,o);if(u){let e=[...u.args??[]],t=e.indexOf(s);if(t!==-1){let r=n.default.join(i,o);await d(f,r)&&(e[t+1]=r,l[a]={...u,args:e})}}let p=n.default.join(i,`mcp.json`);return await t.default.promises.writeFile(p,`${JSON.stringify({...c,mcpServers:l},null,2)}\n`,{mode:384}),p}async function p(e,r,i,c){if(!r)return e;let d=l(e,r.servers),f=d.mcpServers??{},p=f[a];if(!p||!r.proxy||r.proxy.length===0)return d;let m=[...p.args??[]],h=m.indexOf(s);if(h===-1||!m[h+1])return d;let g=n.default.resolve(i,m[h+1]),_;try{_=await t.default.promises.readFile(g,`utf8`)}catch(e){if(e.code===`ENOENT`)return d;throw e}let v=n.default.join(c,o);return await t.default.promises.writeFile(v,u(_,r.proxy),{mode:384}),m[h+1]=v,{...d,mcpServers:{...f,[a]:{...p,args:m}}}}exports.PROXY_SERVER_NAME=a,exports.applyMcpAllowlist=p,exports.filterMcpServers=l,exports.filterProxyConfig=u,exports.persistMcpConfig=f,exports.resolveMcpAllowlist=c;
3
3
  //# sourceMappingURL=mcpFilter.cjs.map
@@ -1,3 +1,3 @@
1
- import e from"node:path";import{expandDomainNames as t,loadDomains as n}from"@agimon-ai/doompi-config/domains";import r from"node:fs";import{parse as i,stringify as a}from"yaml";const o=`agiflow-proxy`,s=`mcp-config.yaml`,c=`--config`;function l(e,r){let i=n(e),a=t(i,r);if(a.length===0)return;let o=new Set,s=new Set;for(let e of a){let t=i.domains[e]?.mcp;if(!t)return;for(let e of t.servers??[])o.add(e);for(let e of t.proxy??[])s.add(e)}return{servers:[...o],proxy:[...s]}}function u(e,t){if(!t||t.length===0)return e;let n=e.mcpServers??{},r={};for(let[e,i]of Object.entries(n))t.includes(e)&&(r[e]=i);return{...e,mcpServers:r}}function d(e,t){if(!t||t.length===0)return e;let n=i(e)??{},r=n.mcpServers??{},o={},s=[];for(let[e,n]of Object.entries(r))t.includes(e)?o[e]=n:s.push(e);return`${[`# Generated by doompi for this run. Do not edit.`,`# Upstreams kept: ${Object.keys(o).join(`, `)||`(none)`}`,`# Upstreams removed by the domain allowlist: ${s.join(`, `)||`(none)`}`,``].join(`
2
- `)}${a({...n,mcpServers:o})}`}async function f(e,t){try{return await r.promises.copyFile(e,t),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function p(t,n,i){await r.promises.mkdir(i,{recursive:!0});let a=JSON.parse(await r.promises.readFile(t,`utf8`)),l=a.mcpServers??{},u=l[o],d=e.join(n,s);if(u){let t=[...u.args??[]],n=t.indexOf(c);if(n!==-1){let r=e.join(i,s);await f(d,r)&&(t[n+1]=r,l[o]={...u,args:t})}}let p=e.join(i,`mcp.json`);return await r.promises.writeFile(p,`${JSON.stringify({...a,mcpServers:l},null,2)}\n`,{mode:384}),p}async function m(t,n,i,a){if(!n)return t;let l=u(t,n.servers),f=l.mcpServers??{},p=f[o];if(!p||!n.proxy||n.proxy.length===0)return l;let m=[...p.args??[]],h=m.indexOf(c);if(h===-1||!m[h+1])return l;let g=e.resolve(i,m[h+1]),_;try{_=await r.promises.readFile(g,`utf8`)}catch(e){if(e.code===`ENOENT`)return l;throw e}let v=e.join(a,s);return await r.promises.writeFile(v,d(_,n.proxy),{mode:384}),m[h+1]=v,{...l,mcpServers:{...f,[o]:{...p,args:m}}}}export{o as PROXY_SERVER_NAME,m as applyMcpAllowlist,u as filterMcpServers,d as filterProxyConfig,p as persistMcpConfig,l as resolveMcpAllowlist};
1
+ import e from"node:fs";import t from"node:path";import{expandDomainNames as n,loadDomains as r}from"@agimon-ai/doompi-config/domains";import{parse as i,stringify as a}from"yaml";const o=`agiflow-proxy`,s=`mcp-config.yaml`,c=`--config`;function l(e,t){let i=r(e),a=n(i,t);if(a.length===0)return;let o=new Set,s=new Set;for(let e of a){let t=i.domains[e]?.mcp;if(!t)return;for(let e of t.servers??[])o.add(e);for(let e of t.proxy??[])s.add(e)}return{servers:[...o],proxy:[...s]}}function u(e,t){if(!t||t.length===0)return e;let n=e.mcpServers??{},r={};for(let[e,i]of Object.entries(n))t.includes(e)&&(r[e]=i);return{...e,mcpServers:r}}function d(e,t){if(!t||t.length===0)return e;let n=i(e)??{},r=n.mcpServers??{},o={},s=[];for(let[e,n]of Object.entries(r))t.includes(e)?o[e]=n:s.push(e);return`${[`# Generated by doompi for this run. Do not edit.`,`# Upstreams kept: ${Object.keys(o).join(`, `)||`(none)`}`,`# Upstreams removed by the domain allowlist: ${s.join(`, `)||`(none)`}`,``].join(`
2
+ `)}${a({...n,mcpServers:o})}`}async function f(t,n){try{return await e.promises.copyFile(t,n),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function p(n,r,i){await e.promises.mkdir(i,{recursive:!0});let a=JSON.parse(await e.promises.readFile(n,`utf8`)),l=a.mcpServers??{},u=l[o],d=t.join(r,s);if(u){let e=[...u.args??[]],n=e.indexOf(c);if(n!==-1){let r=t.join(i,s);await f(d,r)&&(e[n+1]=r,l[o]={...u,args:e})}}let p=t.join(i,`mcp.json`);return await e.promises.writeFile(p,`${JSON.stringify({...a,mcpServers:l},null,2)}\n`,{mode:384}),p}async function m(n,r,i,a){if(!r)return n;let l=u(n,r.servers),f=l.mcpServers??{},p=f[o];if(!p||!r.proxy||r.proxy.length===0)return l;let m=[...p.args??[]],h=m.indexOf(c);if(h===-1||!m[h+1])return l;let g=t.resolve(i,m[h+1]),_;try{_=await e.promises.readFile(g,`utf8`)}catch(e){if(e.code===`ENOENT`)return l;throw e}let v=t.join(a,s);return await e.promises.writeFile(v,d(_,r.proxy),{mode:384}),m[h+1]=v,{...l,mcpServers:{...f,[o]:{...p,args:m}}}}export{o as PROXY_SERVER_NAME,m as applyMcpAllowlist,u as filterMcpServers,d as filterProxyConfig,p as persistMcpConfig,l as resolveMcpAllowlist};
3
3
  //# sourceMappingURL=mcpFilter.mjs.map
@@ -1,2 +1,2 @@
1
- let e=require("@agimon-ai/doompi-mcp");function t(t){let n={repoRoot:t.repoRoot,stagingDirectory:t.stagingDirectory,...t.generatedConfigPath?{generatedConfigPath:t.generatedConfigPath}:{},...t.pluginConfigPaths?.length?{pluginConfigPaths:[...t.pluginConfigPaths]}:{},...t.allowlist?{allowlist:structuredClone(t.allowlist)}:{}};return(0,e.sessionConfigEnvironment)(n)}exports.mcpSessionEnvironment=t;
1
+ let e=require("@agimon-ai/doompi-extension-contracts/mcp-session");function t(t){let n={repoRoot:t.repoRoot,stagingDirectory:t.stagingDirectory,...t.generatedConfigPath?{generatedConfigPath:t.generatedConfigPath}:{},...t.pluginConfigPaths?.length?{pluginConfigPaths:[...t.pluginConfigPaths]}:{},...t.allowlist?{allowlist:structuredClone(t.allowlist)}:{}};return(0,e.doomMcpSessionEnvironment)(n)}exports.mcpSessionEnvironment=t;
2
2
  //# sourceMappingURL=mcpSessionEnvironment.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcpSessionEnvironment.cjs","names":["sessionConfigEnvironment"],"sources":["../../src/adapters/mcpSessionEnvironment.ts"],"sourcesContent":["import { type McpAllowlist, type McpSessionConfig, sessionConfigEnvironment } from '@agimon-ai/doompi-mcp';\n\nexport interface McpSessionEnvironmentInput {\n readonly repoRoot: string;\n readonly stagingDirectory: string;\n readonly generatedConfigPath?: string;\n readonly pluginConfigPaths?: readonly string[];\n readonly allowlist?: McpAllowlist;\n}\n\n/** Projects the complete domain-selected MCP picture into the Pi session environment. */\nexport function mcpSessionEnvironment(input: McpSessionEnvironmentInput): Record<string, string> {\n const configuration: McpSessionConfig = {\n repoRoot: input.repoRoot,\n stagingDirectory: input.stagingDirectory,\n ...(input.generatedConfigPath ? { generatedConfigPath: input.generatedConfigPath } : {}),\n ...(input.pluginConfigPaths?.length ? { pluginConfigPaths: [...input.pluginConfigPaths] } : {}),\n ...(input.allowlist ? { allowlist: structuredClone(input.allowlist) } : {}),\n };\n return sessionConfigEnvironment(configuration);\n}\n"],"mappings":"uCAWA,SAAgB,EAAsB,EAA2D,CAC/F,IAAM,EAAkC,CACtC,SAAU,EAAM,SAChB,iBAAkB,EAAM,iBACxB,GAAI,EAAM,oBAAsB,CAAE,oBAAqB,EAAM,mBAAoB,EAAI,CAAC,EACtF,GAAI,EAAM,mBAAmB,OAAS,CAAE,kBAAmB,CAAC,GAAG,EAAM,iBAAiB,CAAE,EAAI,CAAC,EAC7F,GAAI,EAAM,UAAY,CAAE,UAAW,gBAAgB,EAAM,SAAS,CAAE,EAAI,CAAC,CAC3E,EACA,OAAA,EAAOA,EAAAA,yBAAAA,CAAyB,CAAa,CAC/C"}
1
+ {"version":3,"file":"mcpSessionEnvironment.cjs","names":["doomMcpSessionEnvironment"],"sources":["../../src/adapters/mcpSessionEnvironment.ts"],"sourcesContent":["import {\n type DoomMcpSessionAllowlist,\n type DoomMcpSessionConfig,\n doomMcpSessionEnvironment,\n} from '@agimon-ai/doompi-extension-contracts/mcp-session';\n\nexport interface McpSessionEnvironmentInput {\n readonly repoRoot: string;\n readonly stagingDirectory: string;\n readonly generatedConfigPath?: string;\n readonly pluginConfigPaths?: readonly string[];\n readonly allowlist?: DoomMcpSessionAllowlist;\n}\n\n/** Projects the complete domain-selected MCP picture into the Pi session environment. */\nexport function mcpSessionEnvironment(input: McpSessionEnvironmentInput): Record<string, string> {\n const configuration: DoomMcpSessionConfig = {\n repoRoot: input.repoRoot,\n stagingDirectory: input.stagingDirectory,\n ...(input.generatedConfigPath ? { generatedConfigPath: input.generatedConfigPath } : {}),\n ...(input.pluginConfigPaths?.length ? { pluginConfigPaths: [...input.pluginConfigPaths] } : {}),\n ...(input.allowlist ? { allowlist: structuredClone(input.allowlist) } : {}),\n };\n return doomMcpSessionEnvironment(configuration);\n}\n"],"mappings":"mEAeA,SAAgB,EAAsB,EAA2D,CAC/F,IAAM,EAAsC,CAC1C,SAAU,EAAM,SAChB,iBAAkB,EAAM,iBACxB,GAAI,EAAM,oBAAsB,CAAE,oBAAqB,EAAM,mBAAoB,EAAI,CAAC,EACtF,GAAI,EAAM,mBAAmB,OAAS,CAAE,kBAAmB,CAAC,GAAG,EAAM,iBAAiB,CAAE,EAAI,CAAC,EAC7F,GAAI,EAAM,UAAY,CAAE,UAAW,gBAAgB,EAAM,SAAS,CAAE,EAAI,CAAC,CAC3E,EACA,OAAA,EAAOA,EAAAA,0BAAAA,CAA0B,CAAa,CAChD"}
@@ -1,11 +1,11 @@
1
- import { McpAllowlist } from "@agimon-ai/doompi-mcp";
1
+ import { DoomMcpSessionAllowlist } from "@agimon-ai/doompi-extension-contracts/mcp-session";
2
2
  //#region src/adapters/mcpSessionEnvironment.d.ts
3
3
  interface McpSessionEnvironmentInput {
4
4
  readonly repoRoot: string;
5
5
  readonly stagingDirectory: string;
6
6
  readonly generatedConfigPath?: string;
7
7
  readonly pluginConfigPaths?: readonly string[];
8
- readonly allowlist?: McpAllowlist;
8
+ readonly allowlist?: DoomMcpSessionAllowlist;
9
9
  }
10
10
  /** Projects the complete domain-selected MCP picture into the Pi session environment. */
11
11
  declare function mcpSessionEnvironment(input: McpSessionEnvironmentInput): Record<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"mcpSessionEnvironment.d.cts","names":[],"sources":["../../src/adapters/mcpSessionEnvironment.ts"],"mappings":";;UAEiB;WACN;WACA;WACA;WACA;WACA,YAAY;;;iBAIP,sBAAsB,OAAO,6BAA6B"}
1
+ {"version":3,"file":"mcpSessionEnvironment.d.cts","names":[],"sources":["../../src/adapters/mcpSessionEnvironment.ts"],"mappings":";;UAMiB;WACN;WACA;WACA;WACA;WACA,YAAY;;;iBAIP,sBAAsB,OAAO,6BAA6B"}
@@ -1,11 +1,11 @@
1
- import { McpAllowlist } from "@agimon-ai/doompi-mcp";
1
+ import { DoomMcpSessionAllowlist } from "@agimon-ai/doompi-extension-contracts/mcp-session";
2
2
  //#region src/adapters/mcpSessionEnvironment.d.ts
3
3
  interface McpSessionEnvironmentInput {
4
4
  readonly repoRoot: string;
5
5
  readonly stagingDirectory: string;
6
6
  readonly generatedConfigPath?: string;
7
7
  readonly pluginConfigPaths?: readonly string[];
8
- readonly allowlist?: McpAllowlist;
8
+ readonly allowlist?: DoomMcpSessionAllowlist;
9
9
  }
10
10
  /** Projects the complete domain-selected MCP picture into the Pi session environment. */
11
11
  declare function mcpSessionEnvironment(input: McpSessionEnvironmentInput): Record<string, string>;