@agimon-ai/doompi 0.0.1-alpha.57 → 0.0.1-alpha.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -115,8 +115,8 @@ matter, and profiles choose a point of view.
115
115
  ### Major and minor modes
116
116
 
117
117
  A major mode is the base config. It names the extension layers for development, marketing,
118
- or whatever else you do. Define as many as you like; only one is active at a time, and you
119
- can switch it without leaving the session.
118
+ or whatever else you do. Define as many as you like; only one is active at a time. Synchronized
119
+ sessions can reload compatible changes; launcher-owned composition changes require a relaunch.
120
120
 
121
121
  Minor modes are batteries-included switches inside that base. They start off, stack freely,
122
122
  and keep their model tools and skills out of context until turned on. DoomPi ships six:
@@ -371,11 +371,11 @@ generated mode gets it. Short commands still return inline; commands that pass t
371
371
  threshold of 60 seconds by default move into the background with durable logs. Use
372
372
  `background: true` for an immediate detached run or `interactive: true` when the command needs
373
373
  terminal input. Runner IDs and complete raw logs remain available to the current session through
374
- Runner Space at `SPC r l` or the `doom-runner` CLI. Platform-specific RMUX binaries are selected
375
- automatically; non-interactive commands use a supervised subprocess fallback when RMUX is absent,
376
- while interactive commands require RMUX. Conservatively recognized single commands use the matching
377
- RTK stdin filter after completion. Compound commands, pipelines, incompatible formats, and
378
- unsupported commands keep bounded raw output. Remove Runner from `default.packages` to keep Pi's
374
+ Runner Space at `SPC r l` or the `doom-runner` CLI. Runner tries bundled RMUX, then `rmux` on PATH,
375
+ then `tmux` on PATH. If no pane backend accepts the run, non-interactive commands use a supervised
376
+ subprocess; interactive commands require RMUX or tmux. Conservatively recognized single commands
377
+ use the matching RTK stdin filter after completion. Compound commands, pipelines, incompatible
378
+ formats, and unsupported commands keep bounded raw output. Remove Runner from `default.packages` to keep Pi's
379
379
  `bash`, or add it to a named layer when only selected modes should replace `bash`.
380
380
 
381
381
  #### Auto-compact
@@ -464,7 +464,7 @@ handoffs rather than one long conversation.
464
464
 
465
465
  [`@agimon-ai/doompi-voice`][pkg-doompi-voice] captures PCM audio through the local platform helper
466
466
  and can transcribe it locally. Transcript text, session state, and model requests may still cross
467
- process or provider boundaries according to the configured engines. `SPC v v` is one-shot manual
467
+ process or provider boundaries according to the configured engines. `SPC v m` is one-shot manual
468
468
  dictation and never enables Voice tools. `SPC v e` enters autonomous capture and exits it again; only its exact-active
469
469
  TUI session receives the two Voice façades plus the standalone `narrate` tool.
470
470
 
@@ -706,8 +706,8 @@ its owning package's controls; values supplied by callers are not automatically
706
706
 
707
707
  - Run `doompi sync --check` to detect drift or missing configured packages, and `doompi sync`
708
708
  to install them and rebuild synchronized state.
709
- - If RMUX is unavailable, non-interactive Runner calls report the supervised subprocess fallback.
710
- Interactive calls require RMUX. Linux native artifacts require a compatible loader and libc.
709
+ - If neither RMUX nor tmux is available, non-interactive Runner calls report the supervised subprocess
710
+ fallback. Interactive calls require RMUX or tmux. Linux native artifacts require a compatible loader and libc.
711
711
  - Leader menus and overlays require Pi's interactive TUI. Commands and tools remain the interface
712
712
  for JSON, RPC, and other headless sessions where the owning package supports them.
713
713
  - Install a directly loaded Pi subsystem with `pi install npm:@agimon-ai/<package>`. Library consumers
@@ -1,4 +1,4 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../adapters/repository/repository.cjs"),n=require("../adapters/syncLocation.cjs"),r=require("../adapters/layerPackageInstaller.cjs"),i=require("../adapters/syncDrift.cjs"),a=require("./syncPresenter.cjs"),o=require("./syncCommand.cjs"),s=require("./buildCommand.cjs");let c=require("node:path");c=e.__toESM(c,1);let l=require("node:os");l=e.__toESM(l,1);let u=require("@agimon-ai/doompi-config/majorModes");const d=`--check`,f=`--force`,p=`DOOMPI_ROOT`,m=`packages`;function h(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function g(e){let t=[];return e.updated.length>0&&t.push(`updated ${h(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${h(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${h(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var _=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(e,r=process.env,u=process.cwd(),m=process.stdout){if(e.includes(d))return new o.SyncCommand({settingsMode:this.settingsMode}).execute(e,r,u,m);let h=r.HOME??l.default.homedir(),g=r[p],_=g?c.default.resolve(g):t.resolveDoomConfigurationRoot(u,h);if(!e.includes(f)&&i.readSyncDrift({repoRoot:_,homeDirectory:h}).fresh)return m.write(`doompi sync is already up to date
2
- `),0;let v=await n.acquireSyncLocationLock(n.resolveSyncLocation(_,h));try{if(!e.includes(f)&&i.readSyncDrift({repoRoot:_,homeDirectory:h}).fresh)return m.write(`doompi sync is already up to date
3
- `),0;let t=new a.SyncProgress(m);await this.refreshPackages(r,u,t);let n=[],c=t.start(`build`,`compiling the mode extension`),l=await new s.BuildCommand(this.telemetry).execute([`build`,...e.slice(1).filter(e=>e!==d)],r,u,{write:e=>(n.push(String(e)),!0)});return l===0?(c(`mode extension compiled`),await new o.SyncCommand({settingsMode:this.settingsMode,homeDirectory:h,lockHeld:!0}).execute(e,r,u,m)):(c(`failed`),m.write(n.join(``)),l)}finally{await v()}}async refreshPackages(e,n,i){let a=e.HOME??l.default.homedir(),o=e[p],s=o?c.default.resolve(o):t.resolveDoomConfigurationRoot(n,a),d=(0,u.loadMajorModesConfig)(s,a);i.start(m,`checking configured packages for updates`)(g(await r.ensureLayerPackages({repoRoot:s,config:d,layers:Object.keys(d.layers),environment:e,refresh:!0,onProgress:e=>i.line(m,e)})))}};exports.SyncPipeline=_;
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../adapters/repository/repository.cjs"),n=require("../adapters/syncLocation.cjs"),r=require("../adapters/layerPackageInstaller.cjs"),i=require("../adapters/syncDrift.cjs"),a=require("./syncPresenter.cjs"),o=require("./syncCommand.cjs"),s=require("./buildCommand.cjs");let c=require("node:path");c=e.__toESM(c,1);let l=require("node:os");l=e.__toESM(l,1);let u=require("@agimon-ai/doompi-config/majorModes");const d=`--check`,f=`--force`,p=`DOOMPI_ROOT`,m=`packages`;function h(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function g(e){let t=[];return e.updated.length>0&&t.push(`updated ${h(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${h(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${h(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var _=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(e,r=process.env,u=process.cwd(),m=process.stdout){if(e.includes(d))return new o.SyncCommand({settingsMode:this.settingsMode}).execute(e,r,u,m);let h=r.HOME??l.default.homedir(),g=r[p],_=g?c.default.resolve(g):t.resolveDoomConfigurationRoot(u,h),v={repoRoot:_,homeDirectory:h,requireWebBundle:!!r.DOOMPI_WEB_PACKAGE_ROOT};if(!e.includes(f)&&i.readSyncDrift(v).fresh)return m.write(`doompi sync is already up to date
2
+ `),0;let y=await n.acquireSyncLocationLock(n.resolveSyncLocation(_,h));try{if(!e.includes(f)&&i.readSyncDrift(v).fresh)return m.write(`doompi sync is already up to date
3
+ `),0;let t=new a.SyncProgress(m);await this.refreshPackages(r,u,t);let n=[],c=t.start(`build`,`compiling the mode extension`),l=await new s.BuildCommand(this.telemetry).execute([`build`,...e.slice(1).filter(e=>e!==d)],r,u,{write:e=>(n.push(String(e)),!0)});return l===0?(c(`mode extension compiled`),await new o.SyncCommand({settingsMode:this.settingsMode,homeDirectory:h,lockHeld:!0}).execute(e,r,u,m)):(c(`failed`),m.write(n.join(``)),l)}finally{await y()}}async refreshPackages(e,n,i){let a=e.HOME??l.default.homedir(),o=e[p],s=o?c.default.resolve(o):t.resolveDoomConfigurationRoot(n,a),d=(0,u.loadMajorModesConfig)(s,a);i.start(m,`checking configured packages for updates`)(g(await r.ensureLayerPackages({repoRoot:s,config:d,layers:Object.keys(d.layers),environment:e,refresh:!0,onProgress:e=>i.line(m,e)})))}};exports.SyncPipeline=_;
4
4
  //# sourceMappingURL=syncPipeline.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncPipeline.cjs","names":["SyncCommand","os","path","resolveDoomConfigurationRoot","readSyncDrift","acquireSyncLocationLock","resolveSyncLocation","SyncProgress","BuildCommand","loadMajorModesConfig","ensureLayerPackages"],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.ts';\nimport { BuildCommand } from './buildCommand.ts';\nimport { SyncCommand, type SyncSettingsMode } from './syncCommand.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\n\nconst CHECK_OPTION = '--check';\n/** Rebuilds and republishes even when nothing drifted. */\nconst FORCE_OPTION = '--force';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst BUILD_COMMAND = 'build';\nconst PACKAGES_LABEL = 'packages';\nconst BUILD_LABEL = 'build';\n\nexport interface SyncPipelineOptions {\n settingsMode?: SyncSettingsMode;\n telemetry?: HarnessTelemetry;\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${String(count)} ${noun}${count === 1 ? '' : 's'}`;\n}\n\nfunction packageSummary(result: LayerPackageResult): string {\n const parts: string[] = [];\n if (result.updated.length > 0) parts.push(`updated ${pluralize(result.updated.length, 'package')}`);\n if (result.installed.length > 0) parts.push(`installed ${pluralize(result.installed.length, 'missing package')}`);\n if (parts.length === 0) parts.push('already up to date');\n if (result.unchecked.length > 0) parts.push(`${pluralize(result.unchecked.length, 'package')} left unchecked`);\n return parts.join(', ');\n}\n\n/** Refreshes packages, runs the private cache build, then commits the synchronized state. */\nexport class SyncPipeline {\n private readonly settingsMode: SyncSettingsMode;\n private readonly telemetry: HarnessTelemetry | undefined;\n\n constructor(options: SyncPipelineOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.telemetry = options.telemetry;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncProgressOutput = process.stdout,\n ): Promise<number> {\n if (args.includes(CHECK_OPTION)) {\n return new SyncCommand({ settingsMode: this.settingsMode }).execute(args, environment, currentDirectory, output);\n }\n\n const homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n\n // Nothing drifted means the packages are current, the mode extension is\n // compiled from these exact bytes, and the published generation already\n // describes them. Refreshing and rebuilding anyway costs seconds per call\n // and, worse, ends in a republished generation that reloads every attached\n // cockpit for no change at all. The cockpit calls this before every session\n // launch, so the cheap answer has to be the common one.\n if (!args.includes(FORCE_OPTION) && readSyncDrift({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Do not rebuild and republish the same generation.\n if (!args.includes(FORCE_OPTION) && readSyncDrift({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n const progress = new SyncProgress(output);\n await this.refreshPackages(environment, currentDirectory, progress);\n\n const captured: string[] = [];\n const done = progress.start(BUILD_LABEL, 'compiling the mode extension');\n const buildCode = await new BuildCommand(this.telemetry).execute(\n [BUILD_COMMAND, ...args.slice(1).filter((argument) => argument !== CHECK_OPTION)],\n environment,\n currentDirectory,\n {\n write: (chunk: unknown) => {\n captured.push(String(chunk));\n return true;\n },\n },\n );\n if (buildCode !== 0) {\n done('failed');\n output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return await new SyncCommand({\n settingsMode: this.settingsMode,\n homeDirectory,\n lockHeld: true,\n }).execute(args, environment, currentDirectory, output);\n } finally {\n await releaseLock();\n }\n }\n\n /** Moves every package sync owns to its newest published version. */\n private async refreshPackages(\n environment: NodeJS.ProcessEnv,\n currentDirectory: string,\n progress: SyncProgress,\n ): Promise<void> {\n const homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const config = loadMajorModesConfig(repoRoot, homeDirectory);\n const done = progress.start(PACKAGES_LABEL, 'checking configured packages for updates');\n const result = await ensureLayerPackages({\n repoRoot,\n config,\n // Every declared layer, not only the selected one: sync writes the state a\n // later /mode switch reads without reinstalling.\n layers: Object.keys(config.layers),\n environment,\n refresh: true,\n onProgress: (message) => progress.line(PACKAGES_LABEL, message),\n });\n done(packageSummary(result));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncPipeline.cjs","names":["SyncCommand","os","path","resolveDoomConfigurationRoot","readSyncDrift","acquireSyncLocationLock","resolveSyncLocation","SyncProgress","BuildCommand","loadMajorModesConfig","ensureLayerPackages"],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.ts';\nimport { BuildCommand } from './buildCommand.ts';\nimport { SyncCommand, type SyncSettingsMode } from './syncCommand.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\n\nconst CHECK_OPTION = '--check';\n/** Rebuilds and republishes even when nothing drifted. */\nconst FORCE_OPTION = '--force';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst BUILD_COMMAND = 'build';\nconst PACKAGES_LABEL = 'packages';\nconst BUILD_LABEL = 'build';\n\nexport interface SyncPipelineOptions {\n settingsMode?: SyncSettingsMode;\n telemetry?: HarnessTelemetry;\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${String(count)} ${noun}${count === 1 ? '' : 's'}`;\n}\n\nfunction packageSummary(result: LayerPackageResult): string {\n const parts: string[] = [];\n if (result.updated.length > 0) parts.push(`updated ${pluralize(result.updated.length, 'package')}`);\n if (result.installed.length > 0) parts.push(`installed ${pluralize(result.installed.length, 'missing package')}`);\n if (parts.length === 0) parts.push('already up to date');\n if (result.unchecked.length > 0) parts.push(`${pluralize(result.unchecked.length, 'package')} left unchecked`);\n return parts.join(', ');\n}\n\n/** Refreshes packages, runs the private cache build, then commits the synchronized state. */\nexport class SyncPipeline {\n private readonly settingsMode: SyncSettingsMode;\n private readonly telemetry: HarnessTelemetry | undefined;\n\n constructor(options: SyncPipelineOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.telemetry = options.telemetry;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncProgressOutput = process.stdout,\n ): Promise<number> {\n if (args.includes(CHECK_OPTION)) {\n return new SyncCommand({ settingsMode: this.settingsMode }).execute(args, environment, currentDirectory, output);\n }\n\n const homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n\n // Nothing drifted means the packages are current, the mode extension is\n // compiled from these exact bytes, and the published generation already\n // describes them. Refreshing and rebuilding anyway costs seconds per call\n // and, worse, ends in a republished generation that reloads every attached\n // cockpit for no change at all. The cockpit calls this before every session\n // launch, so the cheap answer has to be the common one.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n if (!args.includes(FORCE_OPTION) && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Do not rebuild and republish the same generation.\n if (!args.includes(FORCE_OPTION) && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n const progress = new SyncProgress(output);\n await this.refreshPackages(environment, currentDirectory, progress);\n\n const captured: string[] = [];\n const done = progress.start(BUILD_LABEL, 'compiling the mode extension');\n const buildCode = await new BuildCommand(this.telemetry).execute(\n [BUILD_COMMAND, ...args.slice(1).filter((argument) => argument !== CHECK_OPTION)],\n environment,\n currentDirectory,\n {\n write: (chunk: unknown) => {\n captured.push(String(chunk));\n return true;\n },\n },\n );\n if (buildCode !== 0) {\n done('failed');\n output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return await new SyncCommand({\n settingsMode: this.settingsMode,\n homeDirectory,\n lockHeld: true,\n }).execute(args, environment, currentDirectory, output);\n } finally {\n await releaseLock();\n }\n }\n\n /** Moves every package sync owns to its newest published version. */\n private async refreshPackages(\n environment: NodeJS.ProcessEnv,\n currentDirectory: string,\n progress: SyncProgress,\n ): Promise<void> {\n const homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const config = loadMajorModesConfig(repoRoot, homeDirectory);\n const done = progress.start(PACKAGES_LABEL, 'checking configured packages for updates');\n const result = await ensureLayerPackages({\n repoRoot,\n config,\n // Every declared layer, not only the selected one: sync writes the state a\n // later /mode switch reads without reinstalling.\n layers: Object.keys(config.layers),\n environment,\n refresh: true,\n onProgress: (message) => progress.line(PACKAGES_LABEL, message),\n });\n done(packageSummary(result));\n }\n}\n"],"mappings":"0dAYA,MAAM,EAAe,UAEf,EAAe,UACf,EAAmB,cAEnB,EAAiB,WAQvB,SAAS,EAAU,EAAe,EAAsB,CACtD,MAAO,GAAG,OAAO,CAAK,EAAE,GAAG,IAAO,IAAU,EAAI,GAAK,KACvD,CAEA,SAAS,EAAe,EAAoC,CAC1D,IAAM,EAAkB,CAAC,EAKzB,OAJI,EAAO,QAAQ,OAAS,GAAG,EAAM,KAAK,WAAW,EAAU,EAAO,QAAQ,OAAQ,SAAS,GAAG,EAC9F,EAAO,UAAU,OAAS,GAAG,EAAM,KAAK,aAAa,EAAU,EAAO,UAAU,OAAQ,iBAAiB,GAAG,EAC5G,EAAM,SAAW,GAAG,EAAM,KAAK,oBAAoB,EACnD,EAAO,UAAU,OAAS,GAAG,EAAM,KAAK,GAAG,EAAU,EAAO,UAAU,OAAQ,SAAS,EAAE,gBAAgB,EACtG,EAAM,KAAK,IAAI,CACxB,CAGA,IAAa,EAAb,KAA0B,CACxB,aACA,UAEA,YAAY,EAA+B,CAAC,EAAG,CAC7C,KAAK,aAAe,EAAQ,cAAgB,YAC5C,KAAK,UAAY,EAAQ,SAC3B,CAEA,MAAM,QACJ,EACA,EAAiC,QAAQ,IACzC,EAAmB,QAAQ,IAAI,EAC/B,EAA6B,QAAQ,OACpB,CACjB,GAAI,EAAK,SAAS,CAAY,EAC5B,OAAO,IAAIA,EAAAA,YAAY,CAAE,aAAc,KAAK,YAAa,CAAC,CAAC,CAAC,QAAQ,EAAM,EAAa,EAAkB,CAAM,EAGjH,IAAM,EAAgB,EAAY,MAAQC,EAAAA,QAAG,QAAQ,EAC/C,EAAgB,EAAY,GAC5B,EAAW,EACbC,EAAAA,QAAK,QAAQ,CAAa,EAC1BC,EAAAA,6BAA6B,EAAkB,CAAa,EAQ1D,EAAe,CACnB,WACA,gBACA,iBAAkB,EAAQ,EAAY,uBACxC,EACA,GAAI,CAAC,EAAK,SAAS,CAAY,GAAKC,EAAAA,cAAc,CAAY,CAAC,CAAC,MAE9D,OADA,EAAO,MAAM;CAAqC,EAC3C,EAGT,IAAM,EAAc,MAAMC,EAAAA,wBAAwBC,EAAAA,oBAAoB,EAAU,CAAa,CAAC,EAC9F,GAAI,CAGF,GAAI,CAAC,EAAK,SAAS,CAAY,GAAKF,EAAAA,cAAc,CAAY,CAAC,CAAC,MAE9D,OADA,EAAO,MAAM;CAAqC,EAC3C,EAET,IAAM,EAAW,IAAIG,EAAAA,aAAa,CAAM,EACxC,MAAM,KAAK,gBAAgB,EAAa,EAAkB,CAAQ,EAElE,IAAM,EAAqB,CAAC,EACtB,EAAO,EAAS,MAAM,QAAa,8BAA8B,EACjE,EAAY,MAAM,IAAIC,EAAAA,aAAa,KAAK,SAAS,CAAC,CAAC,QACvD,CAAC,QAAe,GAAG,EAAK,MAAM,CAAC,CAAC,CAAC,OAAQ,GAAa,IAAa,CAAY,CAAC,EAChF,EACA,EACA,CACE,MAAQ,IACN,EAAS,KAAK,OAAO,CAAK,CAAC,EACpB,GAEX,CACF,EAQA,OAPI,IAAc,GAKlB,EAAK,yBAAyB,EAEvB,MAAM,IAAIR,EAAAA,YAAY,CAC3B,aAAc,KAAK,aACnB,gBACA,SAAU,EACZ,CAAC,CAAC,CAAC,QAAQ,EAAM,EAAa,EAAkB,CAAM,IAVpD,EAAK,QAAQ,EACb,EAAO,MAAM,EAAS,KAAK,EAAE,CAAC,EACvB,EASX,QAAU,CACR,MAAM,EAAY,CACpB,CACF,CAGA,MAAc,gBACZ,EACA,EACA,EACe,CACf,IAAM,EAAgB,EAAY,MAAQC,EAAAA,QAAG,QAAQ,EAC/C,EAAgB,EAAY,GAC5B,EAAW,EACbC,EAAAA,QAAK,QAAQ,CAAa,EAC1BC,EAAAA,6BAA6B,EAAkB,CAAa,EAC1D,GAAA,EAASM,EAAAA,qBAAAA,CAAqB,EAAU,CAAa,EAY3D,EAXsB,MAAM,EAAgB,0CAWzC,CAAC,CAAC,EAAe,MAVCC,EAAAA,oBAAoB,CACvC,WACA,SAGA,OAAQ,OAAO,KAAK,EAAO,MAAM,EACjC,cACA,QAAS,GACT,WAAa,GAAY,EAAS,KAAK,EAAgB,CAAO,CAChE,CAAC,CACyB,CAAC,CAC7B,CACF"}
@@ -1,4 +1,4 @@
1
- import{resolveDoomConfigurationRoot as e}from"../adapters/repository/repository.mjs";import{acquireSyncLocationLock as t,resolveSyncLocation as n}from"../adapters/syncLocation.mjs";import{ensureLayerPackages as r}from"../adapters/layerPackageInstaller.mjs";import{readSyncDrift as i}from"../adapters/syncDrift.mjs";import{SyncProgress as a}from"./syncPresenter.mjs";import{SyncCommand as o}from"./syncCommand.mjs";import{BuildCommand as s}from"./buildCommand.mjs";import c from"node:path";import l from"node:os";import{loadMajorModesConfig as u}from"@agimon-ai/doompi-config/majorModes";const d=`--check`,f=`--force`,p=`DOOMPI_ROOT`,m=`packages`;function h(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function g(e){let t=[];return e.updated.length>0&&t.push(`updated ${h(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${h(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${h(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var _=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(r,u=process.env,m=process.cwd(),h=process.stdout){if(r.includes(d))return new o({settingsMode:this.settingsMode}).execute(r,u,m,h);let g=u.HOME??l.homedir(),_=u[p],v=_?c.resolve(_):e(m,g);if(!r.includes(f)&&i({repoRoot:v,homeDirectory:g}).fresh)return h.write(`doompi sync is already up to date
2
- `),0;let y=await t(n(v,g));try{if(!r.includes(f)&&i({repoRoot:v,homeDirectory:g}).fresh)return h.write(`doompi sync is already up to date
3
- `),0;let e=new a(h);await this.refreshPackages(u,m,e);let t=[],n=e.start(`build`,`compiling the mode extension`),c=await new s(this.telemetry).execute([`build`,...r.slice(1).filter(e=>e!==d)],u,m,{write:e=>(t.push(String(e)),!0)});return c===0?(n(`mode extension compiled`),await new o({settingsMode:this.settingsMode,homeDirectory:g,lockHeld:!0}).execute(r,u,m,h)):(n(`failed`),h.write(t.join(``)),c)}finally{await y()}}async refreshPackages(t,n,i){let a=t.HOME??l.homedir(),o=t[p],s=o?c.resolve(o):e(n,a),d=u(s,a);i.start(m,`checking configured packages for updates`)(g(await r({repoRoot:s,config:d,layers:Object.keys(d.layers),environment:t,refresh:!0,onProgress:e=>i.line(m,e)})))}};export{_ as SyncPipeline};
1
+ import{resolveDoomConfigurationRoot as e}from"../adapters/repository/repository.mjs";import{acquireSyncLocationLock as t,resolveSyncLocation as n}from"../adapters/syncLocation.mjs";import{ensureLayerPackages as r}from"../adapters/layerPackageInstaller.mjs";import{readSyncDrift as i}from"../adapters/syncDrift.mjs";import{SyncProgress as a}from"./syncPresenter.mjs";import{SyncCommand as o}from"./syncCommand.mjs";import{BuildCommand as s}from"./buildCommand.mjs";import c from"node:path";import l from"node:os";import{loadMajorModesConfig as u}from"@agimon-ai/doompi-config/majorModes";const d=`--check`,f=`--force`,p=`DOOMPI_ROOT`,m=`packages`;function h(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function g(e){let t=[];return e.updated.length>0&&t.push(`updated ${h(e.updated.length,`package`)}`),e.installed.length>0&&t.push(`installed ${h(e.installed.length,`missing package`)}`),t.length===0&&t.push(`already up to date`),e.unchecked.length>0&&t.push(`${h(e.unchecked.length,`package`)} left unchecked`),t.join(`, `)}var _=class{settingsMode;telemetry;constructor(e={}){this.settingsMode=e.settingsMode??`persisted`,this.telemetry=e.telemetry}async execute(r,u=process.env,m=process.cwd(),h=process.stdout){if(r.includes(d))return new o({settingsMode:this.settingsMode}).execute(r,u,m,h);let g=u.HOME??l.homedir(),_=u[p],v=_?c.resolve(_):e(m,g),y={repoRoot:v,homeDirectory:g,requireWebBundle:!!u.DOOMPI_WEB_PACKAGE_ROOT};if(!r.includes(f)&&i(y).fresh)return h.write(`doompi sync is already up to date
2
+ `),0;let b=await t(n(v,g));try{if(!r.includes(f)&&i(y).fresh)return h.write(`doompi sync is already up to date
3
+ `),0;let e=new a(h);await this.refreshPackages(u,m,e);let t=[],n=e.start(`build`,`compiling the mode extension`),c=await new s(this.telemetry).execute([`build`,...r.slice(1).filter(e=>e!==d)],u,m,{write:e=>(t.push(String(e)),!0)});return c===0?(n(`mode extension compiled`),await new o({settingsMode:this.settingsMode,homeDirectory:g,lockHeld:!0}).execute(r,u,m,h)):(n(`failed`),h.write(t.join(``)),c)}finally{await b()}}async refreshPackages(t,n,i){let a=t.HOME??l.homedir(),o=t[p],s=o?c.resolve(o):e(n,a),d=u(s,a);i.start(m,`checking configured packages for updates`)(g(await r({repoRoot:s,config:d,layers:Object.keys(d.layers),environment:t,refresh:!0,onProgress:e=>i.line(m,e)})))}};export{_ as SyncPipeline};
4
4
  //# sourceMappingURL=syncPipeline.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncPipeline.mjs","names":[],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.ts';\nimport { BuildCommand } from './buildCommand.ts';\nimport { SyncCommand, type SyncSettingsMode } from './syncCommand.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\n\nconst CHECK_OPTION = '--check';\n/** Rebuilds and republishes even when nothing drifted. */\nconst FORCE_OPTION = '--force';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst BUILD_COMMAND = 'build';\nconst PACKAGES_LABEL = 'packages';\nconst BUILD_LABEL = 'build';\n\nexport interface SyncPipelineOptions {\n settingsMode?: SyncSettingsMode;\n telemetry?: HarnessTelemetry;\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${String(count)} ${noun}${count === 1 ? '' : 's'}`;\n}\n\nfunction packageSummary(result: LayerPackageResult): string {\n const parts: string[] = [];\n if (result.updated.length > 0) parts.push(`updated ${pluralize(result.updated.length, 'package')}`);\n if (result.installed.length > 0) parts.push(`installed ${pluralize(result.installed.length, 'missing package')}`);\n if (parts.length === 0) parts.push('already up to date');\n if (result.unchecked.length > 0) parts.push(`${pluralize(result.unchecked.length, 'package')} left unchecked`);\n return parts.join(', ');\n}\n\n/** Refreshes packages, runs the private cache build, then commits the synchronized state. */\nexport class SyncPipeline {\n private readonly settingsMode: SyncSettingsMode;\n private readonly telemetry: HarnessTelemetry | undefined;\n\n constructor(options: SyncPipelineOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.telemetry = options.telemetry;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncProgressOutput = process.stdout,\n ): Promise<number> {\n if (args.includes(CHECK_OPTION)) {\n return new SyncCommand({ settingsMode: this.settingsMode }).execute(args, environment, currentDirectory, output);\n }\n\n const homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n\n // Nothing drifted means the packages are current, the mode extension is\n // compiled from these exact bytes, and the published generation already\n // describes them. Refreshing and rebuilding anyway costs seconds per call\n // and, worse, ends in a republished generation that reloads every attached\n // cockpit for no change at all. The cockpit calls this before every session\n // launch, so the cheap answer has to be the common one.\n if (!args.includes(FORCE_OPTION) && readSyncDrift({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Do not rebuild and republish the same generation.\n if (!args.includes(FORCE_OPTION) && readSyncDrift({ repoRoot, homeDirectory }).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n const progress = new SyncProgress(output);\n await this.refreshPackages(environment, currentDirectory, progress);\n\n const captured: string[] = [];\n const done = progress.start(BUILD_LABEL, 'compiling the mode extension');\n const buildCode = await new BuildCommand(this.telemetry).execute(\n [BUILD_COMMAND, ...args.slice(1).filter((argument) => argument !== CHECK_OPTION)],\n environment,\n currentDirectory,\n {\n write: (chunk: unknown) => {\n captured.push(String(chunk));\n return true;\n },\n },\n );\n if (buildCode !== 0) {\n done('failed');\n output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return await new SyncCommand({\n settingsMode: this.settingsMode,\n homeDirectory,\n lockHeld: true,\n }).execute(args, environment, currentDirectory, output);\n } finally {\n await releaseLock();\n }\n }\n\n /** Moves every package sync owns to its newest published version. */\n private async refreshPackages(\n environment: NodeJS.ProcessEnv,\n currentDirectory: string,\n progress: SyncProgress,\n ): Promise<void> {\n const homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const config = loadMajorModesConfig(repoRoot, homeDirectory);\n const done = progress.start(PACKAGES_LABEL, 'checking configured packages for updates');\n const result = await ensureLayerPackages({\n repoRoot,\n config,\n // Every declared layer, not only the selected one: sync writes the state a\n // later /mode switch reads without reinstalling.\n layers: Object.keys(config.layers),\n environment,\n refresh: true,\n onProgress: (message) => progress.line(PACKAGES_LABEL, message),\n });\n done(packageSummary(result));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncPipeline.mjs","names":[],"sources":["../../../src/commands/syncPipeline.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { ensureLayerPackages, type LayerPackageResult } from '../adapters/layerPackageInstaller.ts';\nimport { resolveDoomConfigurationRoot } from '../adapters/repository/repository';\nimport type { HarnessTelemetry } from '../adapters/telemetry/logSinkTelemetry.ts';\nimport { acquireSyncLocationLock, resolveSyncLocation } from '../adapters/syncLocation.ts';\nimport { readSyncDrift } from '../adapters/syncDrift.ts';\nimport { BuildCommand } from './buildCommand.ts';\nimport { SyncCommand, type SyncSettingsMode } from './syncCommand.ts';\nimport { SyncProgress, type SyncProgressOutput } from './syncPresenter.ts';\n\nconst CHECK_OPTION = '--check';\n/** Rebuilds and republishes even when nothing drifted. */\nconst FORCE_OPTION = '--force';\nconst HARNESS_ROOT_ENV = 'DOOMPI_ROOT';\nconst BUILD_COMMAND = 'build';\nconst PACKAGES_LABEL = 'packages';\nconst BUILD_LABEL = 'build';\n\nexport interface SyncPipelineOptions {\n settingsMode?: SyncSettingsMode;\n telemetry?: HarnessTelemetry;\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${String(count)} ${noun}${count === 1 ? '' : 's'}`;\n}\n\nfunction packageSummary(result: LayerPackageResult): string {\n const parts: string[] = [];\n if (result.updated.length > 0) parts.push(`updated ${pluralize(result.updated.length, 'package')}`);\n if (result.installed.length > 0) parts.push(`installed ${pluralize(result.installed.length, 'missing package')}`);\n if (parts.length === 0) parts.push('already up to date');\n if (result.unchecked.length > 0) parts.push(`${pluralize(result.unchecked.length, 'package')} left unchecked`);\n return parts.join(', ');\n}\n\n/** Refreshes packages, runs the private cache build, then commits the synchronized state. */\nexport class SyncPipeline {\n private readonly settingsMode: SyncSettingsMode;\n private readonly telemetry: HarnessTelemetry | undefined;\n\n constructor(options: SyncPipelineOptions = {}) {\n this.settingsMode = options.settingsMode ?? 'persisted';\n this.telemetry = options.telemetry;\n }\n\n async execute(\n args: string[],\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n output: SyncProgressOutput = process.stdout,\n ): Promise<number> {\n if (args.includes(CHECK_OPTION)) {\n return new SyncCommand({ settingsMode: this.settingsMode }).execute(args, environment, currentDirectory, output);\n }\n\n const homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n\n // Nothing drifted means the packages are current, the mode extension is\n // compiled from these exact bytes, and the published generation already\n // describes them. Refreshing and rebuilding anyway costs seconds per call\n // and, worse, ends in a republished generation that reloads every attached\n // cockpit for no change at all. The cockpit calls this before every session\n // launch, so the cheap answer has to be the common one.\n const driftOptions = {\n repoRoot,\n homeDirectory,\n requireWebBundle: Boolean(environment.DOOMPI_WEB_PACKAGE_ROOT),\n };\n if (!args.includes(FORCE_OPTION) && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n\n const releaseLock = await acquireSyncLocationLock(resolveSyncLocation(repoRoot, homeDirectory));\n try {\n // A concurrent publisher may have resolved the drift while this command\n // waited for the lock. Do not rebuild and republish the same generation.\n if (!args.includes(FORCE_OPTION) && readSyncDrift(driftOptions).fresh) {\n output.write('doompi sync is already up to date\\n');\n return 0;\n }\n const progress = new SyncProgress(output);\n await this.refreshPackages(environment, currentDirectory, progress);\n\n const captured: string[] = [];\n const done = progress.start(BUILD_LABEL, 'compiling the mode extension');\n const buildCode = await new BuildCommand(this.telemetry).execute(\n [BUILD_COMMAND, ...args.slice(1).filter((argument) => argument !== CHECK_OPTION)],\n environment,\n currentDirectory,\n {\n write: (chunk: unknown) => {\n captured.push(String(chunk));\n return true;\n },\n },\n );\n if (buildCode !== 0) {\n done('failed');\n output.write(captured.join(''));\n return buildCode;\n }\n done('mode extension compiled');\n\n return await new SyncCommand({\n settingsMode: this.settingsMode,\n homeDirectory,\n lockHeld: true,\n }).execute(args, environment, currentDirectory, output);\n } finally {\n await releaseLock();\n }\n }\n\n /** Moves every package sync owns to its newest published version. */\n private async refreshPackages(\n environment: NodeJS.ProcessEnv,\n currentDirectory: string,\n progress: SyncProgress,\n ): Promise<void> {\n const homeDirectory = environment.HOME ?? os.homedir();\n const inheritedRoot = environment[HARNESS_ROOT_ENV];\n const repoRoot = inheritedRoot\n ? path.resolve(inheritedRoot)\n : resolveDoomConfigurationRoot(currentDirectory, homeDirectory);\n const config = loadMajorModesConfig(repoRoot, homeDirectory);\n const done = progress.start(PACKAGES_LABEL, 'checking configured packages for updates');\n const result = await ensureLayerPackages({\n repoRoot,\n config,\n // Every declared layer, not only the selected one: sync writes the state a\n // later /mode switch reads without reinstalling.\n layers: Object.keys(config.layers),\n environment,\n refresh: true,\n onProgress: (message) => progress.line(PACKAGES_LABEL, message),\n });\n done(packageSummary(result));\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../services/modeCatalog.cjs"),t=require("../../adapters/contextDetailStore.cjs"),n=require("../../services/contextCatalog.cjs"),r=require("../../services/minorModeProjection.cjs"),i=require("./minorModeCommand.cjs");let a=require("@agimon-ai/doompi-extension-contracts/cordis-host"),o=require("@agimon-ai/doompi-extension-contracts/mode"),s=require("@agimon-ai/doompi-extension-contracts/help"),c=require("@agimon-ai/doompi-extension-contracts/notification"),l=require("@agimon-ai/doompi-extension-contracts/transition");function u(e,t,n,r){let i=(0,c.readDoomNotificationService)(e);if(i){try{Promise.resolve(i.request({body:n,level:r})).catch(()=>void 0)}catch{}return}t.ui.notify(n,r)}function d(e){return e.includes(`voice`)?`voice`:e.includes(`leader`)?`leader`:e.includes(`ui`)?`ui`:`system`}function f(e){let t=e.current?.getSnapshot();return t?t.modes.filter(e=>e.state.activation===`active`).map(e=>({id:e.descriptor.id,label:e.descriptor.label})):[]}async function p(e){let r={current:void 0};i.registerMinorModeCommand(e,()=>r.current);let o={};e.on(`session_start`,()=>{setTimeout(()=>{r.republish?.(),o.publisher?.publish()},500)}),e.on(`turn_start`,()=>{o.publisher?.publish()});let s=await(0,a.connectDoomCordisHost)(e,`@agimon-ai/doompi/mode-catalog`),c=s.root.plugin(i=>(o.publisher=n.createContextPublisher(e,i,{readMinorModes:()=>f(r),readSessionId:()=>r.sessionId,writeDetail:(e,n,r)=>void t.writeContextDetail(e,n,r),removeDetail:t.removeContextDetail}),m(i,e,r,()=>void o.publisher?.publish()),()=>{o.publisher?.dispose(),o.publisher=void 0}));try{await c}catch(e){throw await c.dispose(),await s.dispose(),e}let l;e.on(`session_shutdown`,()=>l??=(async()=>{try{await c.dispose()}finally{await s.dispose()}})())}function m(t,n,i,c){t.inject([s.DOOM_HELP_SERVICE],e=>{let t=(0,s.requireDoomHelpService)(e).register({source:`@agimon-ai/doompi`,moduleUrl:require("url").pathToFileURL(__filename).href,skills:[{name:`doompi-author-extension`,description:`Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.`}]});return()=>t.dispose()}),t.inject([a.DOOM_CORDIS_SESSION_SERVICE,l.DOOM_TRANSITION_SERVICE],t=>{let s=t.get(a.DOOM_CORDIS_SESSION_SERVICE),f=s.context,p=(0,l.requireDoomTransitionCoordinator)(t),m=s.reason===`reload`,h=s.sessionId,g=m?(0,l.consumeMinorModeReloadHandoff)(h):void 0;m||(0,l.discardMinorModeReloadHandoff)(h);let _=f.hasUI&&f.mode===`tui`?`tui`:`headless`,v=e.createMinorModeCatalogHost({sessionKind:_,context:f,restoreSnapshot:g,onRestorationError(e){u(t,f,`Could not restore a minor mode after reload: ${e instanceof Error?e.message:String(e)}`,`warning`)},async routeInvocation(e,t,n){let r,i=await p.execute({sessionId:f.sessionManager.getSessionId(),hostGeneration:p.hostGeneration,operationId:e.operationId,source:d(t),target:{axis:`minor-mode`,action:e,requesterSource:t}},async()=>(r=await n(),`applied`));if(i.outcome!==`applied`||!r)throw Error(`Minor-mode transition was ${i.outcome}: ${i.diagnostics.join(`, `)}`);return r}});t.provide(o.DOOM_MINOR_MODE_CATALOG_SERVICE,v),i.current=v,i.sessionId=h;let y,b,x=()=>{b=void 0;let e=r.projectMinorModes(v.getSnapshot(),_),t=JSON.stringify(e);t!==y&&(y=t,n.appendEntry(o.DOOM_MINOR_MODE_ENTRY_TYPE,e),c())},S=v.subscribe(()=>{b??=setTimeout(x,50)});return i.republish=()=>{y=void 0,x()},()=>{b&&clearTimeout(b),S(),i.republish=void 0,i.current=void 0,i.sessionId=void 0,v.dispose()}})}exports.default=p,exports.modeCatalogExtension=p;
1
+ const e=require("../../services/modeCatalog.cjs"),t=require("../../adapters/contextDetailStore.cjs"),n=require("../../services/contextCatalog.cjs"),r=require("../../services/minorModeProjection.cjs"),i=require("./minorModeCommand.cjs");let a=require("@agimon-ai/doompi-extension-contracts/cordis-host"),o=require("@agimon-ai/doompi-extension-contracts/mode"),s=require("@agimon-ai/doompi-extension-contracts/mcp-status"),c=require("@agimon-ai/doompi-extension-contracts/help"),l=require("@agimon-ai/doompi-extension-contracts/notification"),u=require("@agimon-ai/doompi-extension-contracts/transition");function d(e,t,n,r){let i=(0,l.readDoomNotificationService)(e);if(i){try{Promise.resolve(i.request({body:n,level:r})).catch(()=>void 0)}catch{}return}t.ui.notify(n,r)}function f(e){return e.includes(`voice`)?`voice`:e.includes(`leader`)?`leader`:e.includes(`ui`)?`ui`:`system`}function p(e){let t=e.current?.getSnapshot();return t?t.modes.filter(e=>e.state.activation===`active`).map(e=>({id:e.descriptor.id,label:e.descriptor.label})):[]}async function m(e){let r={current:void 0};i.registerMinorModeCommand(e,()=>r.current);let o={};e.on(`session_start`,()=>{setTimeout(()=>{r.republish?.(),o.publisher?.publish()},500)}),e.on(`turn_start`,()=>{o.publisher?.publish()});let c=await(0,a.connectDoomCordisHost)(e,`@agimon-ai/doompi/mode-catalog`),l=c.root.plugin(i=>(o.publisher=n.createContextPublisher(e,i,{readMinorModes:()=>p(r),readSessionId:()=>r.sessionId,writeDetail:(e,n,r)=>void t.writeContextDetail(e,n,r),removeDetail:t.removeContextDetail}),i.inject([s.DOOM_MCP_STATUS_SERVICE],e=>{let t=()=>void o.publisher?.publish(),n=(0,s.readDoomMcpStatus)(e)?.onChange?.(t);return t(),n}),h(i,e,r,()=>void o.publisher?.publish()),()=>{o.publisher?.dispose(),o.publisher=void 0}));try{await l}catch(e){throw await l.dispose(),await c.dispose(),e}let u;e.on(`session_shutdown`,()=>u??=(async()=>{try{await l.dispose()}finally{await c.dispose()}})())}function h(t,n,i,s){t.inject([c.DOOM_HELP_SERVICE],e=>{let t=(0,c.requireDoomHelpService)(e).register({source:`@agimon-ai/doompi`,moduleUrl:require("url").pathToFileURL(__filename).href,skills:[{name:`doompi-author-extension`,description:`Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.`}]});return()=>t.dispose()}),t.inject([a.DOOM_CORDIS_SESSION_SERVICE,u.DOOM_TRANSITION_SERVICE],t=>{let c=t.get(a.DOOM_CORDIS_SESSION_SERVICE),l=c.context,p=(0,u.requireDoomTransitionCoordinator)(t),m=c.reason===`reload`,h=c.sessionId,g=m?(0,u.consumeMinorModeReloadHandoff)(h):void 0;m||(0,u.discardMinorModeReloadHandoff)(h);let _=l.hasUI&&l.mode===`tui`?`tui`:`headless`,v=e.createMinorModeCatalogHost({sessionKind:_,context:l,restoreSnapshot:g,onRestorationError(e){d(t,l,`Could not restore a minor mode after reload: ${e instanceof Error?e.message:String(e)}`,`warning`)},async routeInvocation(e,t,n){let r,i=await p.execute({sessionId:l.sessionManager.getSessionId(),hostGeneration:p.hostGeneration,operationId:e.operationId,source:f(t),target:{axis:`minor-mode`,action:e,requesterSource:t}},async()=>(r=await n(),`applied`));if(i.outcome!==`applied`||!r)throw Error(`Minor-mode transition was ${i.outcome}: ${i.diagnostics.join(`, `)}`);return r}});t.provide(o.DOOM_MINOR_MODE_CATALOG_SERVICE,v),i.current=v,i.sessionId=h;let y,b,x=()=>{b=void 0;let e=r.projectMinorModes(v.getSnapshot(),_),t=JSON.stringify(e);t!==y&&(y=t,n.appendEntry(o.DOOM_MINOR_MODE_ENTRY_TYPE,e),s())},S=v.subscribe(()=>{b??=setTimeout(x,50)});return i.republish=()=>{y=void 0,x()},()=>{b&&clearTimeout(b),S(),i.republish=void 0,i.current=void 0,i.sessionId=void 0,v.dispose()}})}exports.default=m,exports.modeCatalogExtension=m;
2
2
  //# sourceMappingURL=modeCatalog.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"modeCatalog.cjs","names":["readDoomNotificationService","connectDoomCordisHost","createContextPublisher","writeContextDetail","removeContextDetail","DOOM_HELP_SERVICE","requireDoomHelpService","DOOM_CORDIS_SESSION_SERVICE","DOOM_TRANSITION_SERVICE","requireDoomTransitionCoordinator","consumeMinorModeReloadHandoff","createMinorModeCatalogHost","DOOM_MINOR_MODE_CATALOG_SERVICE","projectMinorModes","DOOM_MINOR_MODE_ENTRY_TYPE"],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"sourcesContent":["import {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n type DoomNotificationLevel,\n readDoomNotificationService,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n consumeMinorModeReloadHandoff,\n discardMinorModeReloadHandoff,\n type MinorModeCatalogHost,\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-extension-contracts/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { removeContextDetail, writeContextDetail } from '../../adapters/contextDetailStore.ts';\nimport { type ContextPublisher, createContextPublisher } from '../../services/contextCatalog.ts';\nimport { projectMinorModes } from '../../services/minorModeProjection.ts';\nimport { createMinorModeCatalogHost } from '../../services/modeCatalog.ts';\nimport { registerMinorModeCommand } from './minorModeCommand.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/mode-catalog';\nconst HELP_CONTRIBUTION_SOURCE = '@agimon-ai/doompi';\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n /**\n * The bound session, which the detail store is keyed by.\n *\n * The server launches the agent with `--session-id`, so this is the same id\n * the cockpit and the session API know the session by, and a browser asking\n * for a row's detail looks under the file this session wrote.\n */\n sessionId?: string;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\n/**\n * The minor modes that are actually on, for the composition panel.\n *\n * Read from the bound catalog at publish time rather than passed in, because a\n * mode switching on is one of the reasons a publish happens at all, and a\n * remembered list would describe the composition before the switch.\n */\nfunction activeMinorModes(binding: CatalogBinding): { id: string; label: string }[] {\n const snapshot = binding.current?.getSnapshot();\n if (!snapshot) return [];\n return snapshot.modes\n .filter((record) => record.state.activation === 'active')\n .map((record) => ({ id: record.descriptor.id, label: record.descriptor.label }));\n}\n\nexport async function modeCatalogExtension(pi: ExtensionAPI): Promise<void> {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n const contextBinding: { publisher?: ContextPublisher } = {};\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n void contextBinding.publisher?.publish();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n // MCP status is pull-only and servers connect long after startup, so the\n // boot read above cannot see them. A turn is the moment the composition is\n // actually sent, which makes it both the freshest and the most meaningful\n // time to price. Identical compositions are skipped, so a quiet session\n // journals nothing extra.\n pi.on('turn_start', () => {\n void contextBinding.publisher?.publish();\n });\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin((cordis: Context) => {\n contextBinding.publisher = createContextPublisher(pi, cordis, {\n readMinorModes: () => activeMinorModes(activeCatalog),\n readSessionId: () => activeCatalog.sessionId,\n writeDetail: (sessionId, revision, items) => void writeContextDetail(sessionId, revision, items),\n removeDetail: removeContextDetail,\n });\n modeCatalogPlugin(cordis, pi, activeCatalog, () => void contextBinding.publisher?.publish());\n return () => {\n contextBinding.publisher?.dispose();\n contextBinding.publisher = undefined;\n };\n });\n try {\n await fiber;\n } catch (error) {\n await fiber.dispose();\n await connection.dispose();\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n onCatalogChanged: () => void,\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: HELP_CONTRIBUTION_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-extension',\n description:\n 'Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogHost = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'minor-mode', action: request, requesterSource },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n activeCatalog.sessionId = sessionId;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n onCatalogChanged();\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n activeCatalog.sessionId = undefined;\n catalog.dispose();\n };\n });\n}\n\nexport default modeCatalogExtension;\n"],"mappings":"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"}
1
+ {"version":3,"file":"modeCatalog.cjs","names":["readDoomNotificationService","connectDoomCordisHost","createContextPublisher","writeContextDetail","removeContextDetail","DOOM_MCP_STATUS_SERVICE","readDoomMcpStatus","DOOM_HELP_SERVICE","requireDoomHelpService","DOOM_CORDIS_SESSION_SERVICE","DOOM_TRANSITION_SERVICE","requireDoomTransitionCoordinator","consumeMinorModeReloadHandoff","createMinorModeCatalogHost","DOOM_MINOR_MODE_CATALOG_SERVICE","projectMinorModes","DOOM_MINOR_MODE_ENTRY_TYPE"],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"sourcesContent":["import { DOOM_MCP_STATUS_SERVICE, readDoomMcpStatus } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n type DoomNotificationLevel,\n readDoomNotificationService,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n consumeMinorModeReloadHandoff,\n discardMinorModeReloadHandoff,\n type MinorModeCatalogHost,\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-extension-contracts/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { removeContextDetail, writeContextDetail } from '../../adapters/contextDetailStore.ts';\nimport { type ContextPublisher, createContextPublisher } from '../../services/contextCatalog.ts';\nimport { projectMinorModes } from '../../services/minorModeProjection.ts';\nimport { createMinorModeCatalogHost } from '../../services/modeCatalog.ts';\nimport { registerMinorModeCommand } from './minorModeCommand.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/mode-catalog';\nconst HELP_CONTRIBUTION_SOURCE = '@agimon-ai/doompi';\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n /**\n * The bound session, which the detail store is keyed by.\n *\n * The server launches the agent with `--session-id`, so this is the same id\n * the cockpit and the session API know the session by, and a browser asking\n * for a row's detail looks under the file this session wrote.\n */\n sessionId?: string;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\n/**\n * The minor modes that are actually on, for the composition panel.\n *\n * Read from the bound catalog at publish time rather than passed in, because a\n * mode switching on is one of the reasons a publish happens at all, and a\n * remembered list would describe the composition before the switch.\n */\nfunction activeMinorModes(binding: CatalogBinding): { id: string; label: string }[] {\n const snapshot = binding.current?.getSnapshot();\n if (!snapshot) return [];\n return snapshot.modes\n .filter((record) => record.state.activation === 'active')\n .map((record) => ({ id: record.descriptor.id, label: record.descriptor.label }));\n}\n\nexport async function modeCatalogExtension(pi: ExtensionAPI): Promise<void> {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n const contextBinding: { publisher?: ContextPublisher } = {};\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n void contextBinding.publisher?.publish();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n // Turns still refresh active-tool costs, including for older pull-only MCP providers.\n // Identical compositions are skipped rather than journaled again.\n pi.on('turn_start', () => {\n void contextBinding.publisher?.publish();\n });\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin((cordis: Context) => {\n contextBinding.publisher = createContextPublisher(pi, cordis, {\n readMinorModes: () => activeMinorModes(activeCatalog),\n readSessionId: () => activeCatalog.sessionId,\n writeDetail: (sessionId, revision, items) => void writeContextDetail(sessionId, revision, items),\n removeDetail: removeContextDetail,\n });\n cordis.inject([DOOM_MCP_STATUS_SERVICE], (mcpContext) => {\n const publish = () => void contextBinding.publisher?.publish();\n const unsubscribe = readDoomMcpStatus(mcpContext)?.onChange?.(publish);\n // Subscribe first, then read: discovery may already have finished before binding.\n publish();\n return unsubscribe;\n });\n modeCatalogPlugin(cordis, pi, activeCatalog, () => void contextBinding.publisher?.publish());\n return () => {\n contextBinding.publisher?.dispose();\n contextBinding.publisher = undefined;\n };\n });\n try {\n await fiber;\n } catch (error) {\n await fiber.dispose();\n await connection.dispose();\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n onCatalogChanged: () => void,\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: HELP_CONTRIBUTION_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-extension',\n description:\n 'Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogHost = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'minor-mode', action: request, requesterSource },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n activeCatalog.sessionId = sessionId;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n onCatalogChanged();\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n activeCatalog.sessionId = undefined;\n catalog.dispose();\n };\n });\n}\n\nexport default modeCatalogExtension;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"modeCatalog.d.cts","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"mappings":";;iBA4FsB,qBAAqB,IAAI,eAAe"}
1
+ {"version":3,"file":"modeCatalog.d.cts","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"mappings":";;iBA6FsB,qBAAqB,IAAI,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"modeCatalog.d.mts","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"mappings":";;iBA4FsB,qBAAqB,IAAI,eAAe"}
1
+ {"version":3,"file":"modeCatalog.d.mts","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"mappings":";;iBA6FsB,qBAAqB,IAAI,eAAe"}
@@ -1,2 +1,2 @@
1
- import{createMinorModeCatalogHost as e}from"../../services/modeCatalog.mjs";import{removeContextDetail as t,writeContextDetail as n}from"../../adapters/contextDetailStore.mjs";import{createContextPublisher as r}from"../../services/contextCatalog.mjs";import{projectMinorModes as i}from"../../services/minorModeProjection.mjs";import{registerMinorModeCommand as a}from"./minorModeCommand.mjs";import{DOOM_CORDIS_SESSION_SERVICE as o,connectDoomCordisHost as s}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_MINOR_MODE_CATALOG_SERVICE as c,DOOM_MINOR_MODE_ENTRY_TYPE as l}from"@agimon-ai/doompi-extension-contracts/mode";import{DOOM_HELP_SERVICE as u,requireDoomHelpService as d}from"@agimon-ai/doompi-extension-contracts/help";import{readDoomNotificationService as f}from"@agimon-ai/doompi-extension-contracts/notification";import{DOOM_TRANSITION_SERVICE as p,consumeMinorModeReloadHandoff as m,discardMinorModeReloadHandoff as h,requireDoomTransitionCoordinator as g}from"@agimon-ai/doompi-extension-contracts/transition";function _(e,t,n,r){let i=f(e);if(i){try{Promise.resolve(i.request({body:n,level:r})).catch(()=>void 0)}catch{}return}t.ui.notify(n,r)}function v(e){return e.includes(`voice`)?`voice`:e.includes(`leader`)?`leader`:e.includes(`ui`)?`ui`:`system`}function y(e){let t=e.current?.getSnapshot();return t?t.modes.filter(e=>e.state.activation===`active`).map(e=>({id:e.descriptor.id,label:e.descriptor.label})):[]}async function b(e){let i={current:void 0};a(e,()=>i.current);let o={};e.on(`session_start`,()=>{setTimeout(()=>{i.republish?.(),o.publisher?.publish()},500)}),e.on(`turn_start`,()=>{o.publisher?.publish()});let c=await s(e,`@agimon-ai/doompi/mode-catalog`),l=c.root.plugin(a=>(o.publisher=r(e,a,{readMinorModes:()=>y(i),readSessionId:()=>i.sessionId,writeDetail:(e,t,r)=>void n(e,t,r),removeDetail:t}),x(a,e,i,()=>void o.publisher?.publish()),()=>{o.publisher?.dispose(),o.publisher=void 0}));try{await l}catch(e){throw await l.dispose(),await c.dispose(),e}let u;e.on(`session_shutdown`,()=>u??=(async()=>{try{await l.dispose()}finally{await c.dispose()}})())}function x(t,n,r,a){t.inject([u],e=>{let t=d(e).register({source:`@agimon-ai/doompi`,moduleUrl:import.meta.url,skills:[{name:`doompi-author-extension`,description:`Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.`}]});return()=>t.dispose()}),t.inject([o,p],t=>{let s=t.get(o),u=s.context,d=g(t),f=s.reason===`reload`,p=s.sessionId,y=f?m(p):void 0;f||h(p);let b=u.hasUI&&u.mode===`tui`?`tui`:`headless`,x=e({sessionKind:b,context:u,restoreSnapshot:y,onRestorationError(e){_(t,u,`Could not restore a minor mode after reload: ${e instanceof Error?e.message:String(e)}`,`warning`)},async routeInvocation(e,t,n){let r,i=await d.execute({sessionId:u.sessionManager.getSessionId(),hostGeneration:d.hostGeneration,operationId:e.operationId,source:v(t),target:{axis:`minor-mode`,action:e,requesterSource:t}},async()=>(r=await n(),`applied`));if(i.outcome!==`applied`||!r)throw Error(`Minor-mode transition was ${i.outcome}: ${i.diagnostics.join(`, `)}`);return r}});t.provide(c,x),r.current=x,r.sessionId=p;let S,C,w=()=>{C=void 0;let e=i(x.getSnapshot(),b),t=JSON.stringify(e);t!==S&&(S=t,n.appendEntry(l,e),a())},T=x.subscribe(()=>{C??=setTimeout(w,50)});return r.republish=()=>{S=void 0,w()},()=>{C&&clearTimeout(C),T(),r.republish=void 0,r.current=void 0,r.sessionId=void 0,x.dispose()}})}export{b as default,b as modeCatalogExtension};
1
+ import{createMinorModeCatalogHost as e}from"../../services/modeCatalog.mjs";import{removeContextDetail as t,writeContextDetail as n}from"../../adapters/contextDetailStore.mjs";import{createContextPublisher as r}from"../../services/contextCatalog.mjs";import{projectMinorModes as i}from"../../services/minorModeProjection.mjs";import{registerMinorModeCommand as a}from"./minorModeCommand.mjs";import{DOOM_CORDIS_SESSION_SERVICE as o,connectDoomCordisHost as s}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_MINOR_MODE_CATALOG_SERVICE as c,DOOM_MINOR_MODE_ENTRY_TYPE as l}from"@agimon-ai/doompi-extension-contracts/mode";import{DOOM_MCP_STATUS_SERVICE as u,readDoomMcpStatus as d}from"@agimon-ai/doompi-extension-contracts/mcp-status";import{DOOM_HELP_SERVICE as f,requireDoomHelpService as p}from"@agimon-ai/doompi-extension-contracts/help";import{readDoomNotificationService as m}from"@agimon-ai/doompi-extension-contracts/notification";import{DOOM_TRANSITION_SERVICE as h,consumeMinorModeReloadHandoff as g,discardMinorModeReloadHandoff as _,requireDoomTransitionCoordinator as v}from"@agimon-ai/doompi-extension-contracts/transition";function y(e,t,n,r){let i=m(e);if(i){try{Promise.resolve(i.request({body:n,level:r})).catch(()=>void 0)}catch{}return}t.ui.notify(n,r)}function b(e){return e.includes(`voice`)?`voice`:e.includes(`leader`)?`leader`:e.includes(`ui`)?`ui`:`system`}function x(e){let t=e.current?.getSnapshot();return t?t.modes.filter(e=>e.state.activation===`active`).map(e=>({id:e.descriptor.id,label:e.descriptor.label})):[]}async function S(e){let i={current:void 0};a(e,()=>i.current);let o={};e.on(`session_start`,()=>{setTimeout(()=>{i.republish?.(),o.publisher?.publish()},500)}),e.on(`turn_start`,()=>{o.publisher?.publish()});let c=await s(e,`@agimon-ai/doompi/mode-catalog`),l=c.root.plugin(a=>(o.publisher=r(e,a,{readMinorModes:()=>x(i),readSessionId:()=>i.sessionId,writeDetail:(e,t,r)=>void n(e,t,r),removeDetail:t}),a.inject([u],e=>{let t=()=>void o.publisher?.publish(),n=d(e)?.onChange?.(t);return t(),n}),C(a,e,i,()=>void o.publisher?.publish()),()=>{o.publisher?.dispose(),o.publisher=void 0}));try{await l}catch(e){throw await l.dispose(),await c.dispose(),e}let f;e.on(`session_shutdown`,()=>f??=(async()=>{try{await l.dispose()}finally{await c.dispose()}})())}function C(t,n,r,a){t.inject([f],e=>{let t=p(e).register({source:`@agimon-ai/doompi`,moduleUrl:import.meta.url,skills:[{name:`doompi-author-extension`,description:`Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.`}]});return()=>t.dispose()}),t.inject([o,h],t=>{let s=t.get(o),u=s.context,d=v(t),f=s.reason===`reload`,p=s.sessionId,m=f?g(p):void 0;f||_(p);let h=u.hasUI&&u.mode===`tui`?`tui`:`headless`,x=e({sessionKind:h,context:u,restoreSnapshot:m,onRestorationError(e){y(t,u,`Could not restore a minor mode after reload: ${e instanceof Error?e.message:String(e)}`,`warning`)},async routeInvocation(e,t,n){let r,i=await d.execute({sessionId:u.sessionManager.getSessionId(),hostGeneration:d.hostGeneration,operationId:e.operationId,source:b(t),target:{axis:`minor-mode`,action:e,requesterSource:t}},async()=>(r=await n(),`applied`));if(i.outcome!==`applied`||!r)throw Error(`Minor-mode transition was ${i.outcome}: ${i.diagnostics.join(`, `)}`);return r}});t.provide(c,x),r.current=x,r.sessionId=p;let S,C,w=()=>{C=void 0;let e=i(x.getSnapshot(),h),t=JSON.stringify(e);t!==S&&(S=t,n.appendEntry(l,e),a())},T=x.subscribe(()=>{C??=setTimeout(w,50)});return r.republish=()=>{S=void 0,w()},()=>{C&&clearTimeout(C),T(),r.republish=void 0,r.current=void 0,r.sessionId=void 0,x.dispose()}})}export{S as default,S as modeCatalogExtension};
2
2
  //# sourceMappingURL=modeCatalog.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"modeCatalog.mjs","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"sourcesContent":["import {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n type DoomNotificationLevel,\n readDoomNotificationService,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n consumeMinorModeReloadHandoff,\n discardMinorModeReloadHandoff,\n type MinorModeCatalogHost,\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-extension-contracts/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { removeContextDetail, writeContextDetail } from '../../adapters/contextDetailStore.ts';\nimport { type ContextPublisher, createContextPublisher } from '../../services/contextCatalog.ts';\nimport { projectMinorModes } from '../../services/minorModeProjection.ts';\nimport { createMinorModeCatalogHost } from '../../services/modeCatalog.ts';\nimport { registerMinorModeCommand } from './minorModeCommand.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/mode-catalog';\nconst HELP_CONTRIBUTION_SOURCE = '@agimon-ai/doompi';\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n /**\n * The bound session, which the detail store is keyed by.\n *\n * The server launches the agent with `--session-id`, so this is the same id\n * the cockpit and the session API know the session by, and a browser asking\n * for a row's detail looks under the file this session wrote.\n */\n sessionId?: string;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\n/**\n * The minor modes that are actually on, for the composition panel.\n *\n * Read from the bound catalog at publish time rather than passed in, because a\n * mode switching on is one of the reasons a publish happens at all, and a\n * remembered list would describe the composition before the switch.\n */\nfunction activeMinorModes(binding: CatalogBinding): { id: string; label: string }[] {\n const snapshot = binding.current?.getSnapshot();\n if (!snapshot) return [];\n return snapshot.modes\n .filter((record) => record.state.activation === 'active')\n .map((record) => ({ id: record.descriptor.id, label: record.descriptor.label }));\n}\n\nexport async function modeCatalogExtension(pi: ExtensionAPI): Promise<void> {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n const contextBinding: { publisher?: ContextPublisher } = {};\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n void contextBinding.publisher?.publish();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n // MCP status is pull-only and servers connect long after startup, so the\n // boot read above cannot see them. A turn is the moment the composition is\n // actually sent, which makes it both the freshest and the most meaningful\n // time to price. Identical compositions are skipped, so a quiet session\n // journals nothing extra.\n pi.on('turn_start', () => {\n void contextBinding.publisher?.publish();\n });\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin((cordis: Context) => {\n contextBinding.publisher = createContextPublisher(pi, cordis, {\n readMinorModes: () => activeMinorModes(activeCatalog),\n readSessionId: () => activeCatalog.sessionId,\n writeDetail: (sessionId, revision, items) => void writeContextDetail(sessionId, revision, items),\n removeDetail: removeContextDetail,\n });\n modeCatalogPlugin(cordis, pi, activeCatalog, () => void contextBinding.publisher?.publish());\n return () => {\n contextBinding.publisher?.dispose();\n contextBinding.publisher = undefined;\n };\n });\n try {\n await fiber;\n } catch (error) {\n await fiber.dispose();\n await connection.dispose();\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n onCatalogChanged: () => void,\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: HELP_CONTRIBUTION_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-extension',\n description:\n 'Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogHost = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'minor-mode', action: request, requesterSource },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n activeCatalog.sessionId = sessionId;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n onCatalogChanged();\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n activeCatalog.sessionId = undefined;\n catalog.dispose();\n };\n });\n}\n\nexport default modeCatalogExtension;\n"],"mappings":"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"}
1
+ {"version":3,"file":"modeCatalog.mjs","names":[],"sources":["../../../../src/extensions/entries/modeCatalog.ts"],"sourcesContent":["import { DOOM_MCP_STATUS_SERVICE, readDoomMcpStatus } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport {\n connectDoomCordisHost,\n DOOM_CORDIS_SESSION_SERVICE,\n type DoomCordisSessionService,\n} from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport {\n type DoomNotificationLevel,\n readDoomNotificationService,\n} from '@agimon-ai/doompi-extension-contracts/notification';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport {\n consumeMinorModeReloadHandoff,\n discardMinorModeReloadHandoff,\n type MinorModeCatalogHost,\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-extension-contracts/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport { removeContextDetail, writeContextDetail } from '../../adapters/contextDetailStore.ts';\nimport { type ContextPublisher, createContextPublisher } from '../../services/contextCatalog.ts';\nimport { projectMinorModes } from '../../services/minorModeProjection.ts';\nimport { createMinorModeCatalogHost } from '../../services/modeCatalog.ts';\nimport { registerMinorModeCommand } from './minorModeCommand.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/mode-catalog';\nconst HELP_CONTRIBUTION_SOURCE = '@agimon-ai/doompi';\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n /**\n * The bound session, which the detail store is keyed by.\n *\n * The server launches the agent with `--session-id`, so this is the same id\n * the cockpit and the session API know the session by, and a browser asking\n * for a row's detail looks under the file this session wrote.\n */\n sessionId?: string;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\n/**\n * The minor modes that are actually on, for the composition panel.\n *\n * Read from the bound catalog at publish time rather than passed in, because a\n * mode switching on is one of the reasons a publish happens at all, and a\n * remembered list would describe the composition before the switch.\n */\nfunction activeMinorModes(binding: CatalogBinding): { id: string; label: string }[] {\n const snapshot = binding.current?.getSnapshot();\n if (!snapshot) return [];\n return snapshot.modes\n .filter((record) => record.state.activation === 'active')\n .map((record) => ({ id: record.descriptor.id, label: record.descriptor.label }));\n}\n\nexport async function modeCatalogExtension(pi: ExtensionAPI): Promise<void> {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n const contextBinding: { publisher?: ContextPublisher } = {};\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n void contextBinding.publisher?.publish();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n // Turns still refresh active-tool costs, including for older pull-only MCP providers.\n // Identical compositions are skipped rather than journaled again.\n pi.on('turn_start', () => {\n void contextBinding.publisher?.publish();\n });\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin((cordis: Context) => {\n contextBinding.publisher = createContextPublisher(pi, cordis, {\n readMinorModes: () => activeMinorModes(activeCatalog),\n readSessionId: () => activeCatalog.sessionId,\n writeDetail: (sessionId, revision, items) => void writeContextDetail(sessionId, revision, items),\n removeDetail: removeContextDetail,\n });\n cordis.inject([DOOM_MCP_STATUS_SERVICE], (mcpContext) => {\n const publish = () => void contextBinding.publisher?.publish();\n const unsubscribe = readDoomMcpStatus(mcpContext)?.onChange?.(publish);\n // Subscribe first, then read: discovery may already have finished before binding.\n publish();\n return unsubscribe;\n });\n modeCatalogPlugin(cordis, pi, activeCatalog, () => void contextBinding.publisher?.publish());\n return () => {\n contextBinding.publisher?.dispose();\n contextBinding.publisher = undefined;\n };\n });\n try {\n await fiber;\n } catch (error) {\n await fiber.dispose();\n await connection.dispose();\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n onCatalogChanged: () => void,\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: HELP_CONTRIBUTION_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-author-extension',\n description:\n 'Create or update a DoomPi extension package inside the DoomPi monorepo or as an external npm package. Use for package layout, Pi discovery entries, shared Cordis lifecycle, package-owned Help, and extension verification.',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogHost = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'minor-mode', action: request, requesterSource },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n activeCatalog.sessionId = sessionId;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n onCatalogChanged();\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n activeCatalog.sessionId = undefined;\n catalog.dispose();\n };\n });\n}\n\nexport default modeCatalogExtension;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi",
3
- "version": "0.0.1-alpha.57",
3
+ "version": "0.0.1-alpha.59",
4
4
  "description": "Opinionated, composable Pi distribution for scoped agent tools, skills, MCP servers, and developer workflows.",
5
5
  "keywords": [
6
6
  "ai",
@@ -320,17 +320,17 @@
320
320
  "rolldown": "1.2.6",
321
321
  "typebox": "1.3.19",
322
322
  "yaml": "2.9.0",
323
- "@agimon-ai/doompi-autostop": "0.0.1-alpha.33",
324
- "@agimon-ai/doompi-config": "0.0.1-alpha.55",
325
- "@agimon-ai/doompi-cache": "0.0.1-alpha.22",
326
- "@agimon-ai/doompi-domain": "0.0.1-alpha.34",
327
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.55",
328
- "@agimon-ai/doompi-major-mode": "0.0.1-alpha.34",
329
- "@agimon-ai/doompi-profile": "0.0.1-alpha.34",
330
- "@agimon-ai/doompi-notification": "0.0.1-alpha.33",
331
- "@agimon-ai/doompi-skill": "0.0.1-alpha.34",
332
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.55",
333
- "@agimon-ai/doompi-ui": "0.0.1-alpha.56"
323
+ "@agimon-ai/doompi-autostop": "0.0.1-alpha.35",
324
+ "@agimon-ai/doompi-config": "0.0.1-alpha.57",
325
+ "@agimon-ai/doompi-domain": "0.0.1-alpha.36",
326
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.57",
327
+ "@agimon-ai/doompi-cache": "0.0.1-alpha.24",
328
+ "@agimon-ai/doompi-major-mode": "0.0.1-alpha.36",
329
+ "@agimon-ai/doompi-notification": "0.0.1-alpha.35",
330
+ "@agimon-ai/doompi-skill": "0.0.1-alpha.36",
331
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.56",
332
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.58",
333
+ "@agimon-ai/doompi-profile": "0.0.1-alpha.36"
334
334
  },
335
335
  "devDependencies": {
336
336
  "@earendil-works/pi-ai": "0.85.0",
@@ -340,7 +340,7 @@
340
340
  "@vitest/coverage-v8": "4.1.11",
341
341
  "typescript": "7.0.2",
342
342
  "vitest": "4.1.11",
343
- "@agimon-ai/doompi-runner": "0.0.1-alpha.56"
343
+ "@agimon-ai/doompi-runner": "0.0.1-alpha.58"
344
344
  },
345
345
  "peerDependencies": {
346
346
  "@earendil-works/pi-tui": "0.85.0"