@agimon-ai/doompi-loop 0.0.1-alpha.73 → 0.0.1-alpha.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/package.json +15 -14
  2. package/dist/_virtual/_rolldown/runtime.cjs +0 -1
  3. package/dist/api-contracts.cjs +0 -1
  4. package/dist/api-contracts.d.cts +0 -2
  5. package/dist/api-contracts.d.mts +0 -2
  6. package/dist/api-contracts.mjs +0 -1
  7. package/dist/extensions/pi.cjs +0 -2
  8. package/dist/extensions/pi.cjs.map +0 -1
  9. package/dist/extensions/pi.d.cts +0 -5
  10. package/dist/extensions/pi.d.cts.map +0 -1
  11. package/dist/extensions/pi.d.mts +0 -5
  12. package/dist/extensions/pi.d.mts.map +0 -1
  13. package/dist/extensions/pi.mjs +0 -2
  14. package/dist/extensions/pi.mjs.map +0 -1
  15. package/dist/extensions/server.cjs +0 -2
  16. package/dist/extensions/server.cjs.map +0 -1
  17. package/dist/extensions/server.d.cts +0 -5
  18. package/dist/extensions/server.d.cts.map +0 -1
  19. package/dist/extensions/server.d.mts +0 -5
  20. package/dist/extensions/server.d.mts.map +0 -1
  21. package/dist/extensions/server.mjs +0 -2
  22. package/dist/extensions/server.mjs.map +0 -1
  23. package/dist/index.cjs +0 -0
  24. package/dist/index.d.cts +0 -2
  25. package/dist/index.d.mts +0 -2
  26. package/dist/index.mjs +0 -1
  27. package/dist/loopLaunchers.cjs +0 -1
  28. package/dist/loopLaunchers.d.cts +0 -2
  29. package/dist/loopLaunchers.d.mts +0 -2
  30. package/dist/loopLaunchers.mjs +0 -1
  31. package/dist/src/constants/loop.cjs +0 -2
  32. package/dist/src/constants/loop.cjs.map +0 -1
  33. package/dist/src/constants/loop.mjs +0 -2
  34. package/dist/src/constants/loop.mjs.map +0 -1
  35. package/dist/src/constants/piLoop.cjs +0 -2
  36. package/dist/src/constants/piLoop.cjs.map +0 -1
  37. package/dist/src/constants/piLoop.mjs +0 -2
  38. package/dist/src/constants/piLoop.mjs.map +0 -1
  39. package/dist/src/controllers/loopCommand.cjs +0 -2
  40. package/dist/src/controllers/loopCommand.cjs.map +0 -1
  41. package/dist/src/controllers/loopCommand.mjs +0 -2
  42. package/dist/src/controllers/loopCommand.mjs.map +0 -1
  43. package/dist/src/controllers/serverRuntime.cjs +0 -2
  44. package/dist/src/controllers/serverRuntime.cjs.map +0 -1
  45. package/dist/src/controllers/serverRuntime.mjs +0 -2
  46. package/dist/src/controllers/serverRuntime.mjs.map +0 -1
  47. package/dist/src/schemas/apiContracts.cjs +0 -2
  48. package/dist/src/schemas/apiContracts.cjs.map +0 -1
  49. package/dist/src/schemas/apiContracts.d.cts +0 -53
  50. package/dist/src/schemas/apiContracts.d.cts.map +0 -1
  51. package/dist/src/schemas/apiContracts.d.mts +0 -53
  52. package/dist/src/schemas/apiContracts.d.mts.map +0 -1
  53. package/dist/src/schemas/apiContracts.mjs +0 -2
  54. package/dist/src/schemas/apiContracts.mjs.map +0 -1
  55. package/dist/src/schemas/loopLaunchers.cjs +0 -2
  56. package/dist/src/schemas/loopLaunchers.cjs.map +0 -1
  57. package/dist/src/schemas/loopLaunchers.d.cts +0 -60
  58. package/dist/src/schemas/loopLaunchers.d.cts.map +0 -1
  59. package/dist/src/schemas/loopLaunchers.d.mts +0 -60
  60. package/dist/src/schemas/loopLaunchers.d.mts.map +0 -1
  61. package/dist/src/schemas/loopLaunchers.mjs +0 -2
  62. package/dist/src/schemas/loopLaunchers.mjs.map +0 -1
  63. package/dist/src/services/defaultLoopLauncher/index.cjs +0 -2
  64. package/dist/src/services/defaultLoopLauncher/index.cjs.map +0 -1
  65. package/dist/src/services/defaultLoopLauncher/index.mjs +0 -2
  66. package/dist/src/services/defaultLoopLauncher/index.mjs.map +0 -1
  67. package/dist/src/services/loopLaunchers/index.cjs +0 -2
  68. package/dist/src/services/loopLaunchers/index.cjs.map +0 -1
  69. package/dist/src/services/loopLaunchers/index.mjs +0 -2
  70. package/dist/src/services/loopLaunchers/index.mjs.map +0 -1
  71. package/dist/src/services/loopResource/index.cjs +0 -2
  72. package/dist/src/services/loopResource/index.cjs.map +0 -1
  73. package/dist/src/services/loopResource/index.mjs +0 -2
  74. package/dist/src/services/loopResource/index.mjs.map +0 -1
  75. package/dist/src/tui/leader.cjs +0 -2
  76. package/dist/src/tui/leader.cjs.map +0 -1
  77. package/dist/src/tui/leader.mjs +0 -2
  78. package/dist/src/tui/leader.mjs.map +0 -1
  79. package/dist/src/tui/loopListOverlay.cjs +0 -2
  80. package/dist/src/tui/loopListOverlay.cjs.map +0 -1
  81. package/dist/src/tui/loopListOverlay.mjs +0 -2
  82. package/dist/src/tui/loopListOverlay.mjs.map +0 -1
  83. package/dist/src/tui/loopRuntime.cjs +0 -2
  84. package/dist/src/tui/loopRuntime.cjs.map +0 -1
  85. package/dist/src/tui/loopRuntime.mjs +0 -2
  86. package/dist/src/tui/loopRuntime.mjs.map +0 -1
  87. package/dist/src/tui/startLoopOverlay.cjs +0 -2
  88. package/dist/src/tui/startLoopOverlay.cjs.map +0 -1
  89. package/dist/src/tui/startLoopOverlay.mjs +0 -2
  90. package/dist/src/tui/startLoopOverlay.mjs.map +0 -1
  91. package/dist/src/types/loopView.cjs +0 -2
  92. package/dist/src/types/loopView.cjs.map +0 -1
  93. package/dist/src/types/loopView.mjs +0 -2
  94. package/dist/src/types/loopView.mjs.map +0 -1
  95. package/src/extensions/web.ts +0 -41
  96. package/src/web/components/LoopsActivitySection.tsx +0 -94
  97. package/src/web/tsconfig.json +0 -23
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopLaunchers.d.cts","names":[],"sources":["../../../src/schemas/loopLaunchers.ts"],"mappings":";;;qBAGa;iBAEI;WACN;WACA,QAAQ;;iBAGF;WACN;WACA;WACA;EACT,KAAK,yBAAyB;;iBAGf;WACN;WACA;WACA;WACA;;iBAGM;WACN;WACA;WACA;WACA;WACA;WACA;WACA;;iBAGM,+BAA+B;EAC9C,OAAO,SAAS,oBAAoB,QAAQ;;iBAG7B;WACN;WACA;EACT,QAAQ,kBAAkB;;;iBAIX;WACN;EACT,SAAS,YAAY,yBAAyB;EAC9C,0BAA0B;EAC1B,0BAA0B;EAC1B,UAAU;EACV,OAAO,oBAAoB;IAAY;MAAwB,QAAQ;EACvE,KAAK,oBAAoB,kBAAkB;EAC3C,QAAQ,kBAAkB;EAC1B,QAAQ,kBAAkB;;;YAIhB;IACR,uBAAuB;;;wBAIX,sBAAsB,SAAS,UAAU;wBAIzC,yBAAyB,SAAS,UAAU"}
@@ -1,60 +0,0 @@
1
- import { Context } from "@deepseek-ai/cordis";
2
- //#region src/schemas/loopLaunchers.d.ts
3
- /** Provider-owned Cordis service for session-scoped recurring-loop launchers. */
4
- export declare const DOOM_LOOP_LAUNCHERS_SERVICE = "doom/loop-launchers";
5
- export interface LoopLaunchRequest {
6
- readonly instanceId: string;
7
- readonly signal: AbortSignal;
8
- }
9
- export interface StoppableLoop {
10
- readonly instanceId: string;
11
- readonly label?: string;
12
- readonly detail?: string;
13
- stop(reason?: string): void | Promise<void>;
14
- }
15
- export interface LoopLauncherSummary {
16
- readonly id: string;
17
- readonly source: string;
18
- readonly label: string;
19
- readonly description?: string;
20
- }
21
- export interface LoopInstanceSnapshot {
22
- readonly instanceId: string;
23
- readonly launcherId: string;
24
- readonly launcherLabel: string;
25
- readonly label?: string;
26
- readonly detail?: string;
27
- readonly state: 'starting' | 'running' | 'stopping';
28
- readonly startedAt?: string;
29
- }
30
- export interface LoopLauncherDefinition extends LoopLauncherSummary {
31
- launch(request: LoopLaunchRequest): Promise<StoppableLoop | undefined>;
32
- }
33
- export interface LoopLauncherRegistration {
34
- readonly id: string;
35
- readonly generation: string;
36
- dispose(reason?: string): Promise<void>;
37
- }
38
- /** Direct registrar/control seam owned and provided by doompi-loop. */
39
- export interface DoomLoopLaunchersService {
40
- readonly generation: string;
41
- register(definition: LoopLauncherDefinition): LoopLauncherRegistration;
42
- listLaunchers(): readonly LoopLauncherSummary[];
43
- listInstances(): readonly LoopInstanceSnapshot[];
44
- subscribe(listener: () => void): () => void;
45
- launch(launcherId: string, options?: {
46
- instanceId?: string;
47
- }): Promise<LoopInstanceSnapshot | undefined>;
48
- stop(instanceId: string, reason?: string): Promise<boolean>;
49
- stopAll(reason?: string): Promise<void>;
50
- dispose(reason?: string): Promise<void>;
51
- }
52
- declare module '@deepseek-ai/cordis' {
53
- interface Context {
54
- 'doom/loop-launchers': DoomLoopLaunchersService;
55
- }
56
- }
57
- export declare function readDoomLoopLaunchers(context: Context): DoomLoopLaunchersService | undefined;
58
- export declare function requireDoomLoopLaunchers(context: Context): DoomLoopLaunchersService;
59
- //#endregion
60
- //# sourceMappingURL=loopLaunchers.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopLaunchers.d.mts","names":[],"sources":["../../../src/schemas/loopLaunchers.ts"],"mappings":";;;qBAGa;iBAEI;WACN;WACA,QAAQ;;iBAGF;WACN;WACA;WACA;EACT,KAAK,yBAAyB;;iBAGf;WACN;WACA;WACA;WACA;;iBAGM;WACN;WACA;WACA;WACA;WACA;WACA;WACA;;iBAGM,+BAA+B;EAC9C,OAAO,SAAS,oBAAoB,QAAQ;;iBAG7B;WACN;WACA;EACT,QAAQ,kBAAkB;;;iBAIX;WACN;EACT,SAAS,YAAY,yBAAyB;EAC9C,0BAA0B;EAC1B,0BAA0B;EAC1B,UAAU;EACV,OAAO,oBAAoB;IAAY;MAAwB,QAAQ;EACvE,KAAK,oBAAoB,kBAAkB;EAC3C,QAAQ,kBAAkB;EAC1B,QAAQ,kBAAkB;;;YAIhB;IACR,uBAAuB;;;wBAIX,sBAAsB,SAAS,UAAU;wBAIzC,yBAAyB,SAAS,UAAU"}
@@ -1,2 +0,0 @@
1
- const e=`doom/loop-launchers`;function t(t){return t.get(e)}function n(e){let n=t(e);if(!n)throw Error(`Doom loop launchers are unavailable. Load @agimon-ai/doompi-loop.`);return n}export{e as DOOM_LOOP_LAUNCHERS_SERVICE,t as readDoomLoopLaunchers,n as requireDoomLoopLaunchers};
2
- //# sourceMappingURL=loopLaunchers.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopLaunchers.mjs","names":[],"sources":["../../../src/schemas/loopLaunchers.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\n\n/** Provider-owned Cordis service for session-scoped recurring-loop launchers. */\nexport const DOOM_LOOP_LAUNCHERS_SERVICE = 'doom/loop-launchers';\n\nexport interface LoopLaunchRequest {\n readonly instanceId: string;\n readonly signal: AbortSignal;\n}\n\nexport interface StoppableLoop {\n readonly instanceId: string;\n readonly label?: string;\n readonly detail?: string;\n stop(reason?: string): void | Promise<void>;\n}\n\nexport interface LoopLauncherSummary {\n readonly id: string;\n readonly source: string;\n readonly label: string;\n readonly description?: string;\n}\n\nexport interface LoopInstanceSnapshot {\n readonly instanceId: string;\n readonly launcherId: string;\n readonly launcherLabel: string;\n readonly label?: string;\n readonly detail?: string;\n readonly state: 'starting' | 'running' | 'stopping';\n readonly startedAt?: string;\n}\n\nexport interface LoopLauncherDefinition extends LoopLauncherSummary {\n launch(request: LoopLaunchRequest): Promise<StoppableLoop | undefined>;\n}\n\nexport interface LoopLauncherRegistration {\n readonly id: string;\n readonly generation: string;\n dispose(reason?: string): Promise<void>;\n}\n\n/** Direct registrar/control seam owned and provided by doompi-loop. */\nexport interface DoomLoopLaunchersService {\n readonly generation: string;\n register(definition: LoopLauncherDefinition): LoopLauncherRegistration;\n listLaunchers(): readonly LoopLauncherSummary[];\n listInstances(): readonly LoopInstanceSnapshot[];\n subscribe(listener: () => void): () => void;\n launch(launcherId: string, options?: { instanceId?: string }): Promise<LoopInstanceSnapshot | undefined>;\n stop(instanceId: string, reason?: string): Promise<boolean>;\n stopAll(reason?: string): Promise<void>;\n dispose(reason?: string): Promise<void>;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'doom/loop-launchers': DoomLoopLaunchersService;\n }\n}\n\nexport function readDoomLoopLaunchers(context: Context): DoomLoopLaunchersService | undefined {\n return context.get(DOOM_LOOP_LAUNCHERS_SERVICE) as DoomLoopLaunchersService | undefined;\n}\n\nexport function requireDoomLoopLaunchers(context: Context): DoomLoopLaunchersService {\n const service = readDoomLoopLaunchers(context);\n if (!service) throw new Error('Doom loop launchers are unavailable. Load @agimon-ai/doompi-loop.');\n return service;\n}\n"],"mappings":"AAGA,MAAa,EAA8B,sBA4D3C,SAAgB,EAAsB,EAAwD,CAC5F,OAAO,EAAQ,IAAI,CAA2B,CAChD,CAEA,SAAgB,EAAyB,EAA4C,CACnF,IAAM,EAAU,EAAsB,CAAO,EAC7C,GAAI,CAAC,EAAS,MAAU,MAAM,mEAAmE,EACjG,OAAO,CACT"}
@@ -1,2 +0,0 @@
1
- const e=require("../../constants/piLoop.cjs"),t=3600;function n(e){let n=Number(e);if(!Number.isInteger(n))return`Interval must be a whole number of seconds, got ${e}.`;if(n<30||n>t)return`Interval must be between 30 and ${t} seconds.`}async function r(t,r){let i=(await t.editor(`Loop prompt`,``))?.trim();if(!i||r.aborted)return;let a=await t.input(`Loop interval in seconds`,`Default: 300s`);if(a===void 0||r.aborted)return;let o=a.trim()||`300`,s=n(o);if(s){t.notify(s,e.NOTIFY_WARNING_LEVEL);return}return{prompt:i,intervalSeconds:Number(o)}}function i(e){let t=e.replaceAll(/\s+/gu,` `).trim();return t.length>80?`${t.slice(0,79)}…`:t}function a(t){let n=new Set,a=r=>{if(!r.stopped){if(!r.context.isIdle()){n.add(r);return}n.delete(r);try{t.sendUserMessage(r.prompt)}catch(t){r.context.ui.notify(`Default loop pass could not start: ${t instanceof Error?t.message:String(t)}`,e.NOTIFY_WARNING_LEVEL)}}};return{onAgentSettled:()=>{let e=n.values().next().value;e&&a(e)},register(t,o){return o.register({id:`doompi.default`,source:e.PACKAGE_SOURCE,label:`Default loop`,description:`Run your own prompt in this session on an interval`,async launch({instanceId:e,signal:o}){let s=await r(t.ui,o);if(!s||o.aborted)return;let c={...s,context:t,stopped:!1};return c.timer=setInterval(()=>a(c),c.intervalSeconds*1e3),c.timer.unref?.(),a(c),{instanceId:e,label:`Default loop`,detail:`every ${c.intervalSeconds}s · ${i(c.prompt)}`,stop(){c.stopped||(c.stopped=!0,c.timer&&clearInterval(c.timer),c.timer=void 0,n.delete(c))}}}})}}}exports.createDefaultLoopLauncher=a;
2
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["NOTIFY_WARNING_LEVEL","PACKAGE_SOURCE"],"sources":["../../../../src/services/defaultLoopLauncher/index.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext, ExtensionUIContext } from '@earendil-works/pi-coding-agent';\n\nimport { NOTIFY_WARNING_LEVEL, PACKAGE_SOURCE } from '../../constants/piLoop';\nimport type { DoomLoopLaunchersService, LoopLauncherRegistration, StoppableLoop } from '../../schemas/loopLaunchers';\n\nconst DEFAULT_LAUNCHER_ID = 'doompi.default';\nconst DEFAULT_INTERVAL_SECONDS = 300;\nconst MIN_INTERVAL_SECONDS = 30;\nconst MAX_INTERVAL_SECONDS = 3600;\nconst MILLISECONDS_PER_SECOND = 1000;\nconst PROMPT_PREVIEW_LENGTH = 80;\n\ninterface PreparedDefaultLoop {\n readonly prompt: string;\n readonly intervalSeconds: number;\n}\n\ninterface ActiveDefaultLoop extends PreparedDefaultLoop {\n readonly context: ExtensionContext;\n timer?: ReturnType<typeof setInterval>;\n stopped: boolean;\n}\n\nexport interface DefaultLoopLauncher {\n onAgentSettled(this: void): void;\n register(ctx: ExtensionContext, launchers: DoomLoopLaunchersService): LoopLauncherRegistration;\n}\n\nfunction intervalError(raw: string): string | undefined {\n const seconds = Number(raw);\n if (!Number.isInteger(seconds)) return `Interval must be a whole number of seconds, got ${raw}.`;\n if (seconds < MIN_INTERVAL_SECONDS || seconds > MAX_INTERVAL_SECONDS) {\n return `Interval must be between ${MIN_INTERVAL_SECONDS} and ${MAX_INTERVAL_SECONDS} seconds.`;\n }\n return undefined;\n}\n\nasync function prepareDefaultLoop(\n ui: ExtensionUIContext,\n signal: AbortSignal,\n): Promise<PreparedDefaultLoop | undefined> {\n const prompt = (await ui.editor('Loop prompt', ''))?.trim();\n if (!prompt || signal.aborted) return undefined;\n\n const rawInterval = await ui.input('Loop interval in seconds', `Default: ${DEFAULT_INTERVAL_SECONDS}s`);\n if (rawInterval === undefined || signal.aborted) return undefined;\n const normalizedInterval = rawInterval.trim() || String(DEFAULT_INTERVAL_SECONDS);\n const error = intervalError(normalizedInterval);\n if (error) {\n ui.notify(error, NOTIFY_WARNING_LEVEL);\n return undefined;\n }\n\n return { prompt, intervalSeconds: Number(normalizedInterval) };\n}\n\nfunction promptPreview(prompt: string): string {\n const oneLine = prompt.replaceAll(/\\s+/gu, ' ').trim();\n return oneLine.length > PROMPT_PREVIEW_LENGTH ? `${oneLine.slice(0, PROMPT_PREVIEW_LENGTH - 1)}…` : oneLine;\n}\n\nexport function createDefaultLoopLauncher(pi: Pick<ExtensionAPI, 'sendUserMessage'>): DefaultLoopLauncher {\n const pending = new Set<ActiveDefaultLoop>();\n\n const requestPass = (loop: ActiveDefaultLoop): void => {\n if (loop.stopped) return;\n if (!loop.context.isIdle()) {\n pending.add(loop);\n return;\n }\n\n pending.delete(loop);\n try {\n pi.sendUserMessage(loop.prompt);\n } catch (error) {\n loop.context.ui.notify(\n `Default loop pass could not start: ${error instanceof Error ? error.message : String(error)}`,\n NOTIFY_WARNING_LEVEL,\n );\n }\n };\n\n const onAgentSettled = (): void => {\n const next = pending.values().next().value;\n if (next) requestPass(next);\n };\n\n return {\n onAgentSettled,\n register(ctx, launchers) {\n return launchers.register({\n id: DEFAULT_LAUNCHER_ID,\n source: PACKAGE_SOURCE,\n label: 'Default loop',\n description: 'Run your own prompt in this session on an interval',\n async launch({ instanceId, signal }) {\n const prepared = await prepareDefaultLoop(ctx.ui, signal);\n if (!prepared || signal.aborted) return undefined;\n\n const loop: ActiveDefaultLoop = { ...prepared, context: ctx, stopped: false };\n loop.timer = setInterval(() => requestPass(loop), loop.intervalSeconds * MILLISECONDS_PER_SECOND);\n loop.timer.unref?.();\n requestPass(loop);\n\n const handle: StoppableLoop = {\n instanceId,\n label: 'Default loop',\n detail: `every ${loop.intervalSeconds}s · ${promptPreview(loop.prompt)}`,\n stop() {\n if (loop.stopped) return;\n loop.stopped = true;\n if (loop.timer) clearInterval(loop.timer);\n loop.timer = undefined;\n pending.delete(loop);\n },\n };\n return handle;\n },\n });\n },\n };\n}\n"],"mappings":"8CAQM,EAAuB,KAoB7B,SAAS,EAAc,EAAiC,CACtD,IAAM,EAAU,OAAO,CAAG,EAC1B,GAAI,CAAC,OAAO,UAAU,CAAO,EAAG,MAAO,mDAAmD,EAAI,GAC9F,GAAI,EAAU,IAAwB,EAAU,EAC9C,MAAO,mCAAwD,EAAqB,UAGxF,CAEA,eAAe,EACb,EACA,EAC0C,CAC1C,IAAM,GAAU,MAAM,EAAG,OAAO,cAAe,EAAE,EAAA,EAAI,KAAK,EAC1D,GAAI,CAAC,GAAU,EAAO,QAAS,OAE/B,IAAM,EAAc,MAAM,EAAG,MAAM,2BAA4B,eAAuC,EACtG,GAAI,IAAgB,IAAA,IAAa,EAAO,QAAS,OACjD,IAAM,EAAqB,EAAY,KAAK,GAAK,MAC3C,EAAQ,EAAc,CAAkB,EAC9C,GAAI,EAAO,CACT,EAAG,OAAO,EAAOA,EAAAA,oBAAoB,EACrC,MACF,CAEA,MAAO,CAAE,SAAQ,gBAAiB,OAAO,CAAkB,CAAE,CAC/D,CAEA,SAAS,EAAc,EAAwB,CAC7C,IAAM,EAAU,EAAO,WAAW,QAAS,GAAG,CAAC,CAAC,KAAK,EACrD,OAAO,EAAQ,OAAS,GAAwB,GAAG,EAAQ,MAAM,EAAG,EAAyB,EAAE,GAAK,CACtG,CAEA,SAAgB,EAA0B,EAAgE,CACxG,IAAM,EAAU,IAAI,IAEd,EAAe,GAAkC,CACjD,MAAK,QACT,IAAI,CAAC,EAAK,QAAQ,OAAO,EAAG,CAC1B,EAAQ,IAAI,CAAI,EAChB,MACF,CAEA,EAAQ,OAAO,CAAI,EACnB,GAAI,CACF,EAAG,gBAAgB,EAAK,MAAM,CAChC,OAAS,EAAO,CACd,EAAK,QAAQ,GAAG,OACd,sCAAsC,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,IAC3FA,EAAAA,oBACF,CACF,CAVA,CAWF,EAOA,MAAO,CACL,mBANiC,CACjC,IAAM,EAAO,EAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,MACjC,GAAM,EAAY,CAAI,CAC5B,EAIE,SAAS,EAAK,EAAW,CACvB,OAAO,EAAU,SAAS,CACxB,GAAI,iBACJ,OAAQC,EAAAA,eACR,MAAO,eACP,YAAa,qDACb,MAAM,OAAO,CAAE,aAAY,UAAU,CACnC,IAAM,EAAW,MAAM,EAAmB,EAAI,GAAI,CAAM,EACxD,GAAI,CAAC,GAAY,EAAO,QAAS,OAEjC,IAAM,EAA0B,CAAE,GAAG,EAAU,QAAS,EAAK,QAAS,EAAM,EAiB5E,MAhBA,GAAK,MAAQ,gBAAkB,EAAY,CAAI,EAAG,EAAK,gBAAkB,GAAuB,EAChG,EAAK,MAAM,QAAQ,EACnB,EAAY,CAAI,EAcT,CAXL,aACA,MAAO,eACP,OAAQ,SAAS,EAAK,gBAAgB,MAAM,EAAc,EAAK,MAAM,IACrE,MAAO,CACD,EAAK,UACT,EAAK,QAAU,GACX,EAAK,OAAO,cAAc,EAAK,KAAK,EACxC,EAAK,MAAQ,IAAA,GACb,EAAQ,OAAO,CAAI,EACrB,CAEU,CACd,CACF,CAAC,CACH,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- import{NOTIFY_WARNING_LEVEL as e,PACKAGE_SOURCE as t}from"../../constants/piLoop.mjs";const n=3600;function r(e){let t=Number(e);if(!Number.isInteger(t))return`Interval must be a whole number of seconds, got ${e}.`;if(t<30||t>n)return`Interval must be between 30 and ${n} seconds.`}async function i(t,n){let i=(await t.editor(`Loop prompt`,``))?.trim();if(!i||n.aborted)return;let a=await t.input(`Loop interval in seconds`,`Default: 300s`);if(a===void 0||n.aborted)return;let o=a.trim()||`300`,s=r(o);if(s){t.notify(s,e);return}return{prompt:i,intervalSeconds:Number(o)}}function a(e){let t=e.replaceAll(/\s+/gu,` `).trim();return t.length>80?`${t.slice(0,79)}…`:t}function o(n){let r=new Set,o=t=>{if(!t.stopped){if(!t.context.isIdle()){r.add(t);return}r.delete(t);try{n.sendUserMessage(t.prompt)}catch(n){t.context.ui.notify(`Default loop pass could not start: ${n instanceof Error?n.message:String(n)}`,e)}}};return{onAgentSettled:()=>{let e=r.values().next().value;e&&o(e)},register(e,n){return n.register({id:`doompi.default`,source:t,label:`Default loop`,description:`Run your own prompt in this session on an interval`,async launch({instanceId:t,signal:n}){let s=await i(e.ui,n);if(!s||n.aborted)return;let c={...s,context:e,stopped:!1};return c.timer=setInterval(()=>o(c),c.intervalSeconds*1e3),c.timer.unref?.(),o(c),{instanceId:t,label:`Default loop`,detail:`every ${c.intervalSeconds}s · ${a(c.prompt)}`,stop(){c.stopped||(c.stopped=!0,c.timer&&clearInterval(c.timer),c.timer=void 0,r.delete(c))}}}})}}}export{o as createDefaultLoopLauncher};
2
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/defaultLoopLauncher/index.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext, ExtensionUIContext } from '@earendil-works/pi-coding-agent';\n\nimport { NOTIFY_WARNING_LEVEL, PACKAGE_SOURCE } from '../../constants/piLoop';\nimport type { DoomLoopLaunchersService, LoopLauncherRegistration, StoppableLoop } from '../../schemas/loopLaunchers';\n\nconst DEFAULT_LAUNCHER_ID = 'doompi.default';\nconst DEFAULT_INTERVAL_SECONDS = 300;\nconst MIN_INTERVAL_SECONDS = 30;\nconst MAX_INTERVAL_SECONDS = 3600;\nconst MILLISECONDS_PER_SECOND = 1000;\nconst PROMPT_PREVIEW_LENGTH = 80;\n\ninterface PreparedDefaultLoop {\n readonly prompt: string;\n readonly intervalSeconds: number;\n}\n\ninterface ActiveDefaultLoop extends PreparedDefaultLoop {\n readonly context: ExtensionContext;\n timer?: ReturnType<typeof setInterval>;\n stopped: boolean;\n}\n\nexport interface DefaultLoopLauncher {\n onAgentSettled(this: void): void;\n register(ctx: ExtensionContext, launchers: DoomLoopLaunchersService): LoopLauncherRegistration;\n}\n\nfunction intervalError(raw: string): string | undefined {\n const seconds = Number(raw);\n if (!Number.isInteger(seconds)) return `Interval must be a whole number of seconds, got ${raw}.`;\n if (seconds < MIN_INTERVAL_SECONDS || seconds > MAX_INTERVAL_SECONDS) {\n return `Interval must be between ${MIN_INTERVAL_SECONDS} and ${MAX_INTERVAL_SECONDS} seconds.`;\n }\n return undefined;\n}\n\nasync function prepareDefaultLoop(\n ui: ExtensionUIContext,\n signal: AbortSignal,\n): Promise<PreparedDefaultLoop | undefined> {\n const prompt = (await ui.editor('Loop prompt', ''))?.trim();\n if (!prompt || signal.aborted) return undefined;\n\n const rawInterval = await ui.input('Loop interval in seconds', `Default: ${DEFAULT_INTERVAL_SECONDS}s`);\n if (rawInterval === undefined || signal.aborted) return undefined;\n const normalizedInterval = rawInterval.trim() || String(DEFAULT_INTERVAL_SECONDS);\n const error = intervalError(normalizedInterval);\n if (error) {\n ui.notify(error, NOTIFY_WARNING_LEVEL);\n return undefined;\n }\n\n return { prompt, intervalSeconds: Number(normalizedInterval) };\n}\n\nfunction promptPreview(prompt: string): string {\n const oneLine = prompt.replaceAll(/\\s+/gu, ' ').trim();\n return oneLine.length > PROMPT_PREVIEW_LENGTH ? `${oneLine.slice(0, PROMPT_PREVIEW_LENGTH - 1)}…` : oneLine;\n}\n\nexport function createDefaultLoopLauncher(pi: Pick<ExtensionAPI, 'sendUserMessage'>): DefaultLoopLauncher {\n const pending = new Set<ActiveDefaultLoop>();\n\n const requestPass = (loop: ActiveDefaultLoop): void => {\n if (loop.stopped) return;\n if (!loop.context.isIdle()) {\n pending.add(loop);\n return;\n }\n\n pending.delete(loop);\n try {\n pi.sendUserMessage(loop.prompt);\n } catch (error) {\n loop.context.ui.notify(\n `Default loop pass could not start: ${error instanceof Error ? error.message : String(error)}`,\n NOTIFY_WARNING_LEVEL,\n );\n }\n };\n\n const onAgentSettled = (): void => {\n const next = pending.values().next().value;\n if (next) requestPass(next);\n };\n\n return {\n onAgentSettled,\n register(ctx, launchers) {\n return launchers.register({\n id: DEFAULT_LAUNCHER_ID,\n source: PACKAGE_SOURCE,\n label: 'Default loop',\n description: 'Run your own prompt in this session on an interval',\n async launch({ instanceId, signal }) {\n const prepared = await prepareDefaultLoop(ctx.ui, signal);\n if (!prepared || signal.aborted) return undefined;\n\n const loop: ActiveDefaultLoop = { ...prepared, context: ctx, stopped: false };\n loop.timer = setInterval(() => requestPass(loop), loop.intervalSeconds * MILLISECONDS_PER_SECOND);\n loop.timer.unref?.();\n requestPass(loop);\n\n const handle: StoppableLoop = {\n instanceId,\n label: 'Default loop',\n detail: `every ${loop.intervalSeconds}s · ${promptPreview(loop.prompt)}`,\n stop() {\n if (loop.stopped) return;\n loop.stopped = true;\n if (loop.timer) clearInterval(loop.timer);\n loop.timer = undefined;\n pending.delete(loop);\n },\n };\n return handle;\n },\n });\n },\n };\n}\n"],"mappings":"sFAKA,MAGM,EAAuB,KAoB7B,SAAS,EAAc,EAAiC,CACtD,IAAM,EAAU,OAAO,CAAG,EAC1B,GAAI,CAAC,OAAO,UAAU,CAAO,EAAG,MAAO,mDAAmD,EAAI,GAC9F,GAAI,EAAU,IAAwB,EAAU,EAC9C,MAAO,mCAAwD,EAAqB,UAGxF,CAEA,eAAe,EACb,EACA,EAC0C,CAC1C,IAAM,GAAU,MAAM,EAAG,OAAO,cAAe,EAAE,EAAA,EAAI,KAAK,EAC1D,GAAI,CAAC,GAAU,EAAO,QAAS,OAE/B,IAAM,EAAc,MAAM,EAAG,MAAM,2BAA4B,eAAuC,EACtG,GAAI,IAAgB,IAAA,IAAa,EAAO,QAAS,OACjD,IAAM,EAAqB,EAAY,KAAK,GAAK,MAC3C,EAAQ,EAAc,CAAkB,EAC9C,GAAI,EAAO,CACT,EAAG,OAAO,EAAO,CAAoB,EACrC,MACF,CAEA,MAAO,CAAE,SAAQ,gBAAiB,OAAO,CAAkB,CAAE,CAC/D,CAEA,SAAS,EAAc,EAAwB,CAC7C,IAAM,EAAU,EAAO,WAAW,QAAS,GAAG,CAAC,CAAC,KAAK,EACrD,OAAO,EAAQ,OAAS,GAAwB,GAAG,EAAQ,MAAM,EAAG,EAAyB,EAAE,GAAK,CACtG,CAEA,SAAgB,EAA0B,EAAgE,CACxG,IAAM,EAAU,IAAI,IAEd,EAAe,GAAkC,CACjD,MAAK,QACT,IAAI,CAAC,EAAK,QAAQ,OAAO,EAAG,CAC1B,EAAQ,IAAI,CAAI,EAChB,MACF,CAEA,EAAQ,OAAO,CAAI,EACnB,GAAI,CACF,EAAG,gBAAgB,EAAK,MAAM,CAChC,OAAS,EAAO,CACd,EAAK,QAAQ,GAAG,OACd,sCAAsC,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,IAC3F,CACF,CACF,CAVA,CAWF,EAOA,MAAO,CACL,mBANiC,CACjC,IAAM,EAAO,EAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,MACjC,GAAM,EAAY,CAAI,CAC5B,EAIE,SAAS,EAAK,EAAW,CACvB,OAAO,EAAU,SAAS,CACxB,GAAI,iBACJ,OAAQ,EACR,MAAO,eACP,YAAa,qDACb,MAAM,OAAO,CAAE,aAAY,UAAU,CACnC,IAAM,EAAW,MAAM,EAAmB,EAAI,GAAI,CAAM,EACxD,GAAI,CAAC,GAAY,EAAO,QAAS,OAEjC,IAAM,EAA0B,CAAE,GAAG,EAAU,QAAS,EAAK,QAAS,EAAM,EAiB5E,MAhBA,GAAK,MAAQ,gBAAkB,EAAY,CAAI,EAAG,EAAK,gBAAkB,GAAuB,EAChG,EAAK,MAAM,QAAQ,EACnB,EAAY,CAAI,EAcT,CAXL,aACA,MAAO,eACP,OAAQ,SAAS,EAAK,gBAAgB,MAAM,EAAc,EAAK,MAAM,IACrE,MAAO,CACD,EAAK,UACT,EAAK,QAAU,GACX,EAAK,OAAO,cAAc,EAAK,KAAK,EACxC,EAAK,MAAQ,IAAA,GACb,EAAQ,OAAO,CAAI,EACrB,CAEU,CACd,CACF,CAAC,CACH,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- const e=`loop launcher id`,t=`loop instance id`;function n(e,t){let n=e.trim(),r=!1;for(let e of n){let t=e.codePointAt(0);if(t!==void 0&&(t<=31||t===127)){r=!0;break}}if(!n||n.length>256||r)throw Error(`Invalid ${t}.`);return n}function r(e){return{instanceId:e.instanceId,launcherId:e.launcher.definition.id,launcherLabel:e.launcher.definition.label,...e.handle?.label?{label:e.handle.label}:{},...e.handle?.detail?{detail:e.handle.detail}:{},state:e.state,...e.startedAt?{startedAt:e.startedAt}:{}}}function i({generation:i,createInstanceId:a,timestamp:o}){if(!i||i.length>256)throw TypeError(`Doom loop launchers require a valid generation.`);let s=new Map,c=new Map,l=new Set,u=0,d=!1,f=()=>{if(d)throw Error(`The Doom loop-launchers service is disposed.`)},p=()=>{for(let e of l)e()},m=async(e,t)=>{if(c.get(e.instanceId)===e){e.state=`stopping`,e.abortController.abort(t),p();try{await e.handle?.stop(t)}finally{c.get(e.instanceId)===e&&c.delete(e.instanceId),p()}}};return Object.freeze({generation:i,register(t){f();let r=n(t.id,e);if(s.has(r))throw Error(`Loop launcher '${r}' is already registered.`);let a=Symbol(r),o={token:a,definition:{...t,id:r}};s.set(r,o),u+=1,p();let l=!1;return Object.freeze({id:r,generation:`${i}:launcher:${u}`,async dispose(e=`Loop launcher disposed.`){if(l||(l=!0,s.get(r)?.token!==a))return;s.delete(r);let t=[...c.values()].filter(e=>e.launcher.token===a);await Promise.allSettled(t.map(t=>m(t,e))),p()}})},listLaunchers(){return f(),[...s.values()].map(({definition:e})=>({id:e.id,source:e.source,label:e.label,...e.description?{description:e.description}:{}})).sort((e,t)=>e.label.localeCompare(t.label)||e.id.localeCompare(t.id))},listInstances(){return f(),[...c.values()].map(r).sort((e,t)=>e.instanceId.localeCompare(t.instanceId))},subscribe(e){f(),l.add(e);let t=!0;return()=>{t&&(t=!1,l.delete(e))}},async launch(i,l){f();let u=n(i,e),m=s.get(u);if(!m)throw Error(`Loop launcher '${u}' is unavailable.`);let h=n(l?.instanceId??a(),t);if(c.has(h))throw Error(`Loop instance '${h}' already exists.`);let g={instanceId:h,launcher:m,abortController:new AbortController,state:`starting`};c.set(h,g),p();try{let e=await m.definition.launch({instanceId:h,signal:g.abortController.signal}),t=!d&&c.get(h)===g&&!g.abortController.signal.aborted;if(!e){c.get(h)===g&&c.delete(h),p();return}if(!t||e.instanceId!==h){if(await e.stop(t?`Loop launcher returned a mismatched instance id.`:`Loop launch was cancelled.`),c.get(h)===g&&c.delete(h),p(),t)throw Error(`Loop launcher '${u}' returned a mismatched instance id.`);return}return g.handle=e,g.state=`running`,g.startedAt=o(),p(),r(g)}catch(e){let t=g.abortController.signal.aborted||d;if(c.get(h)===g&&c.delete(h),p(),t)return;throw e}},async stop(e,r){f();let i=c.get(n(e,t));return i?(await m(i,r),!0):!1},async stopAll(e){d&&c.size===0||await Promise.allSettled([...c.values()].map(t=>m(t,e)))},async dispose(e=`Doom loop-launchers service disposed.`){d||(d=!0,s.clear(),await Promise.allSettled([...c.values()].map(t=>m(t,e))),l.clear())}})}exports.createDoomLoopLaunchersService=i;
2
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/services/loopLaunchers/index.ts"],"sourcesContent":["import type {\n DoomLoopLaunchersService,\n LoopInstanceSnapshot,\n LoopLauncherDefinition,\n LoopLauncherRegistration,\n StoppableLoop,\n} from '../../schemas/loopLaunchers';\n\nconst MAX_ID_LENGTH = 256;\nconst MAX_GENERATION_LENGTH = 256;\nconst LAUNCHER_ID_LABEL = 'loop launcher id';\nconst INSTANCE_ID_LABEL = 'loop instance id';\n\ninterface LauncherEntry {\n readonly token: symbol;\n readonly definition: LoopLauncherDefinition;\n}\n\ninterface InstanceEntry {\n readonly instanceId: string;\n readonly launcher: LauncherEntry;\n readonly abortController: AbortController;\n state: LoopInstanceSnapshot['state'];\n handle?: StoppableLoop;\n startedAt?: string;\n}\n\nexport interface LoopLaunchersDependencies {\n readonly generation: string;\n readonly createInstanceId: () => string;\n readonly timestamp: () => string;\n}\n\nfunction validId(value: string, label: string): string {\n const normalized = value.trim();\n let hasControl = false;\n for (const character of normalized) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) {\n hasControl = true;\n break;\n }\n }\n if (!normalized || normalized.length > MAX_ID_LENGTH || hasControl) throw new Error(`Invalid ${label}.`);\n return normalized;\n}\n\nfunction snapshot(entry: InstanceEntry): LoopInstanceSnapshot {\n return {\n instanceId: entry.instanceId,\n launcherId: entry.launcher.definition.id,\n launcherLabel: entry.launcher.definition.label,\n ...(entry.handle?.label ? { label: entry.handle.label } : {}),\n ...(entry.handle?.detail ? { detail: entry.handle.detail } : {}),\n state: entry.state,\n ...(entry.startedAt ? { startedAt: entry.startedAt } : {}),\n };\n}\n\n/** Creates the provider-owned launcher registry for one active Doom session. */\nexport function createDoomLoopLaunchersService({\n generation,\n createInstanceId,\n timestamp,\n}: LoopLaunchersDependencies): DoomLoopLaunchersService {\n if (!generation || generation.length > MAX_GENERATION_LENGTH) {\n throw new TypeError('Doom loop launchers require a valid generation.');\n }\n\n const launchers = new Map<string, LauncherEntry>();\n const instances = new Map<string, InstanceEntry>();\n const listeners = new Set<() => void>();\n let registrationSequence = 0;\n let disposed = false;\n\n const ensureActive = (): void => {\n if (disposed) throw new Error('The Doom loop-launchers service is disposed.');\n };\n const notify = (): void => {\n for (const listener of listeners) listener();\n };\n const stopEntry = async (entry: InstanceEntry, reason?: string): Promise<void> => {\n if (instances.get(entry.instanceId) !== entry) return;\n entry.state = 'stopping';\n entry.abortController.abort(reason);\n notify();\n try {\n await entry.handle?.stop(reason);\n } finally {\n if (instances.get(entry.instanceId) === entry) instances.delete(entry.instanceId);\n notify();\n }\n };\n\n const service: DoomLoopLaunchersService = {\n generation,\n register(definition): LoopLauncherRegistration {\n ensureActive();\n const id = validId(definition.id, LAUNCHER_ID_LABEL);\n if (launchers.has(id)) throw new Error(`Loop launcher '${id}' is already registered.`);\n const token = Symbol(id);\n const entry: LauncherEntry = { token, definition: { ...definition, id } };\n launchers.set(id, entry);\n registrationSequence += 1;\n notify();\n let registrationDisposed = false;\n return Object.freeze({\n id,\n generation: `${generation}:launcher:${registrationSequence}`,\n async dispose(reason = 'Loop launcher disposed.'): Promise<void> {\n if (registrationDisposed) return;\n registrationDisposed = true;\n if (launchers.get(id)?.token !== token) return;\n launchers.delete(id);\n const owned = [...instances.values()].filter((instance) => instance.launcher.token === token);\n await Promise.allSettled(owned.map((instance) => stopEntry(instance, reason)));\n notify();\n },\n });\n },\n listLaunchers() {\n ensureActive();\n return [...launchers.values()]\n .map(({ definition }) => ({\n id: definition.id,\n source: definition.source,\n label: definition.label,\n ...(definition.description ? { description: definition.description } : {}),\n }))\n .sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id));\n },\n listInstances() {\n ensureActive();\n return [...instances.values()]\n .map(snapshot)\n .sort((left, right) => left.instanceId.localeCompare(right.instanceId));\n },\n subscribe(listener) {\n ensureActive();\n listeners.add(listener);\n let subscribed = true;\n return () => {\n if (!subscribed) return;\n subscribed = false;\n listeners.delete(listener);\n };\n },\n async launch(launcherId, options) {\n ensureActive();\n const normalizedLauncherId = validId(launcherId, LAUNCHER_ID_LABEL);\n const launcher = launchers.get(normalizedLauncherId);\n if (!launcher) throw new Error(`Loop launcher '${normalizedLauncherId}' is unavailable.`);\n const instanceId = validId(options?.instanceId ?? createInstanceId(), INSTANCE_ID_LABEL);\n if (instances.has(instanceId)) throw new Error(`Loop instance '${instanceId}' already exists.`);\n const entry: InstanceEntry = { instanceId, launcher, abortController: new AbortController(), state: 'starting' };\n instances.set(instanceId, entry);\n notify();\n try {\n const handle = await launcher.definition.launch({ instanceId, signal: entry.abortController.signal });\n const active = !disposed && instances.get(instanceId) === entry && !entry.abortController.signal.aborted;\n if (!handle) {\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n return undefined;\n }\n if (!active || handle.instanceId !== instanceId) {\n await handle.stop(active ? 'Loop launcher returned a mismatched instance id.' : 'Loop launch was cancelled.');\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n if (active) throw new Error(`Loop launcher '${normalizedLauncherId}' returned a mismatched instance id.`);\n return undefined;\n }\n entry.handle = handle;\n entry.state = 'running';\n entry.startedAt = timestamp();\n notify();\n return snapshot(entry);\n } catch (error) {\n const cancelled = entry.abortController.signal.aborted || disposed;\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n if (cancelled) return undefined;\n throw error;\n }\n },\n async stop(instanceId, reason) {\n ensureActive();\n const entry = instances.get(validId(instanceId, INSTANCE_ID_LABEL));\n if (!entry) return false;\n await stopEntry(entry, reason);\n return true;\n },\n async stopAll(reason) {\n if (disposed && instances.size === 0) return;\n await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));\n },\n async dispose(reason = 'Doom loop-launchers service disposed.') {\n if (disposed) return;\n disposed = true;\n launchers.clear();\n await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));\n listeners.clear();\n },\n };\n\n return Object.freeze(service);\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=`loop launcher id`,t=`loop instance id`;function n(e,t){let n=e.trim(),r=!1;for(let e of n){let t=e.codePointAt(0);if(t!==void 0&&(t<=31||t===127)){r=!0;break}}if(!n||n.length>256||r)throw Error(`Invalid ${t}.`);return n}function r(e){return{instanceId:e.instanceId,launcherId:e.launcher.definition.id,launcherLabel:e.launcher.definition.label,...e.handle?.label?{label:e.handle.label}:{},...e.handle?.detail?{detail:e.handle.detail}:{},state:e.state,...e.startedAt?{startedAt:e.startedAt}:{}}}function i({generation:i,createInstanceId:a,timestamp:o}){if(!i||i.length>256)throw TypeError(`Doom loop launchers require a valid generation.`);let s=new Map,c=new Map,l=new Set,u=0,d=!1,f=()=>{if(d)throw Error(`The Doom loop-launchers service is disposed.`)},p=()=>{for(let e of l)e()},m=async(e,t)=>{if(c.get(e.instanceId)===e){e.state=`stopping`,e.abortController.abort(t),p();try{await e.handle?.stop(t)}finally{c.get(e.instanceId)===e&&c.delete(e.instanceId),p()}}};return Object.freeze({generation:i,register(t){f();let r=n(t.id,e);if(s.has(r))throw Error(`Loop launcher '${r}' is already registered.`);let a=Symbol(r),o={token:a,definition:{...t,id:r}};s.set(r,o),u+=1,p();let l=!1;return Object.freeze({id:r,generation:`${i}:launcher:${u}`,async dispose(e=`Loop launcher disposed.`){if(l||(l=!0,s.get(r)?.token!==a))return;s.delete(r);let t=[...c.values()].filter(e=>e.launcher.token===a);await Promise.allSettled(t.map(t=>m(t,e))),p()}})},listLaunchers(){return f(),[...s.values()].map(({definition:e})=>({id:e.id,source:e.source,label:e.label,...e.description?{description:e.description}:{}})).sort((e,t)=>e.label.localeCompare(t.label)||e.id.localeCompare(t.id))},listInstances(){return f(),[...c.values()].map(r).sort((e,t)=>e.instanceId.localeCompare(t.instanceId))},subscribe(e){f(),l.add(e);let t=!0;return()=>{t&&(t=!1,l.delete(e))}},async launch(i,l){f();let u=n(i,e),m=s.get(u);if(!m)throw Error(`Loop launcher '${u}' is unavailable.`);let h=n(l?.instanceId??a(),t);if(c.has(h))throw Error(`Loop instance '${h}' already exists.`);let g={instanceId:h,launcher:m,abortController:new AbortController,state:`starting`};c.set(h,g),p();try{let e=await m.definition.launch({instanceId:h,signal:g.abortController.signal}),t=!d&&c.get(h)===g&&!g.abortController.signal.aborted;if(!e){c.get(h)===g&&c.delete(h),p();return}if(!t||e.instanceId!==h){if(await e.stop(t?`Loop launcher returned a mismatched instance id.`:`Loop launch was cancelled.`),c.get(h)===g&&c.delete(h),p(),t)throw Error(`Loop launcher '${u}' returned a mismatched instance id.`);return}return g.handle=e,g.state=`running`,g.startedAt=o(),p(),r(g)}catch(e){let t=g.abortController.signal.aborted||d;if(c.get(h)===g&&c.delete(h),p(),t)return;throw e}},async stop(e,r){f();let i=c.get(n(e,t));return i?(await m(i,r),!0):!1},async stopAll(e){d&&c.size===0||await Promise.allSettled([...c.values()].map(t=>m(t,e)))},async dispose(e=`Doom loop-launchers service disposed.`){d||(d=!0,s.clear(),await Promise.allSettled([...c.values()].map(t=>m(t,e))),l.clear())}})}export{i as createDoomLoopLaunchersService};
2
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/loopLaunchers/index.ts"],"sourcesContent":["import type {\n DoomLoopLaunchersService,\n LoopInstanceSnapshot,\n LoopLauncherDefinition,\n LoopLauncherRegistration,\n StoppableLoop,\n} from '../../schemas/loopLaunchers';\n\nconst MAX_ID_LENGTH = 256;\nconst MAX_GENERATION_LENGTH = 256;\nconst LAUNCHER_ID_LABEL = 'loop launcher id';\nconst INSTANCE_ID_LABEL = 'loop instance id';\n\ninterface LauncherEntry {\n readonly token: symbol;\n readonly definition: LoopLauncherDefinition;\n}\n\ninterface InstanceEntry {\n readonly instanceId: string;\n readonly launcher: LauncherEntry;\n readonly abortController: AbortController;\n state: LoopInstanceSnapshot['state'];\n handle?: StoppableLoop;\n startedAt?: string;\n}\n\nexport interface LoopLaunchersDependencies {\n readonly generation: string;\n readonly createInstanceId: () => string;\n readonly timestamp: () => string;\n}\n\nfunction validId(value: string, label: string): string {\n const normalized = value.trim();\n let hasControl = false;\n for (const character of normalized) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) {\n hasControl = true;\n break;\n }\n }\n if (!normalized || normalized.length > MAX_ID_LENGTH || hasControl) throw new Error(`Invalid ${label}.`);\n return normalized;\n}\n\nfunction snapshot(entry: InstanceEntry): LoopInstanceSnapshot {\n return {\n instanceId: entry.instanceId,\n launcherId: entry.launcher.definition.id,\n launcherLabel: entry.launcher.definition.label,\n ...(entry.handle?.label ? { label: entry.handle.label } : {}),\n ...(entry.handle?.detail ? { detail: entry.handle.detail } : {}),\n state: entry.state,\n ...(entry.startedAt ? { startedAt: entry.startedAt } : {}),\n };\n}\n\n/** Creates the provider-owned launcher registry for one active Doom session. */\nexport function createDoomLoopLaunchersService({\n generation,\n createInstanceId,\n timestamp,\n}: LoopLaunchersDependencies): DoomLoopLaunchersService {\n if (!generation || generation.length > MAX_GENERATION_LENGTH) {\n throw new TypeError('Doom loop launchers require a valid generation.');\n }\n\n const launchers = new Map<string, LauncherEntry>();\n const instances = new Map<string, InstanceEntry>();\n const listeners = new Set<() => void>();\n let registrationSequence = 0;\n let disposed = false;\n\n const ensureActive = (): void => {\n if (disposed) throw new Error('The Doom loop-launchers service is disposed.');\n };\n const notify = (): void => {\n for (const listener of listeners) listener();\n };\n const stopEntry = async (entry: InstanceEntry, reason?: string): Promise<void> => {\n if (instances.get(entry.instanceId) !== entry) return;\n entry.state = 'stopping';\n entry.abortController.abort(reason);\n notify();\n try {\n await entry.handle?.stop(reason);\n } finally {\n if (instances.get(entry.instanceId) === entry) instances.delete(entry.instanceId);\n notify();\n }\n };\n\n const service: DoomLoopLaunchersService = {\n generation,\n register(definition): LoopLauncherRegistration {\n ensureActive();\n const id = validId(definition.id, LAUNCHER_ID_LABEL);\n if (launchers.has(id)) throw new Error(`Loop launcher '${id}' is already registered.`);\n const token = Symbol(id);\n const entry: LauncherEntry = { token, definition: { ...definition, id } };\n launchers.set(id, entry);\n registrationSequence += 1;\n notify();\n let registrationDisposed = false;\n return Object.freeze({\n id,\n generation: `${generation}:launcher:${registrationSequence}`,\n async dispose(reason = 'Loop launcher disposed.'): Promise<void> {\n if (registrationDisposed) return;\n registrationDisposed = true;\n if (launchers.get(id)?.token !== token) return;\n launchers.delete(id);\n const owned = [...instances.values()].filter((instance) => instance.launcher.token === token);\n await Promise.allSettled(owned.map((instance) => stopEntry(instance, reason)));\n notify();\n },\n });\n },\n listLaunchers() {\n ensureActive();\n return [...launchers.values()]\n .map(({ definition }) => ({\n id: definition.id,\n source: definition.source,\n label: definition.label,\n ...(definition.description ? { description: definition.description } : {}),\n }))\n .sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id));\n },\n listInstances() {\n ensureActive();\n return [...instances.values()]\n .map(snapshot)\n .sort((left, right) => left.instanceId.localeCompare(right.instanceId));\n },\n subscribe(listener) {\n ensureActive();\n listeners.add(listener);\n let subscribed = true;\n return () => {\n if (!subscribed) return;\n subscribed = false;\n listeners.delete(listener);\n };\n },\n async launch(launcherId, options) {\n ensureActive();\n const normalizedLauncherId = validId(launcherId, LAUNCHER_ID_LABEL);\n const launcher = launchers.get(normalizedLauncherId);\n if (!launcher) throw new Error(`Loop launcher '${normalizedLauncherId}' is unavailable.`);\n const instanceId = validId(options?.instanceId ?? createInstanceId(), INSTANCE_ID_LABEL);\n if (instances.has(instanceId)) throw new Error(`Loop instance '${instanceId}' already exists.`);\n const entry: InstanceEntry = { instanceId, launcher, abortController: new AbortController(), state: 'starting' };\n instances.set(instanceId, entry);\n notify();\n try {\n const handle = await launcher.definition.launch({ instanceId, signal: entry.abortController.signal });\n const active = !disposed && instances.get(instanceId) === entry && !entry.abortController.signal.aborted;\n if (!handle) {\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n return undefined;\n }\n if (!active || handle.instanceId !== instanceId) {\n await handle.stop(active ? 'Loop launcher returned a mismatched instance id.' : 'Loop launch was cancelled.');\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n if (active) throw new Error(`Loop launcher '${normalizedLauncherId}' returned a mismatched instance id.`);\n return undefined;\n }\n entry.handle = handle;\n entry.state = 'running';\n entry.startedAt = timestamp();\n notify();\n return snapshot(entry);\n } catch (error) {\n const cancelled = entry.abortController.signal.aborted || disposed;\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n if (cancelled) return undefined;\n throw error;\n }\n },\n async stop(instanceId, reason) {\n ensureActive();\n const entry = instances.get(validId(instanceId, INSTANCE_ID_LABEL));\n if (!entry) return false;\n await stopEntry(entry, reason);\n return true;\n },\n async stopAll(reason) {\n if (disposed && instances.size === 0) return;\n await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));\n },\n async dispose(reason = 'Doom loop-launchers service disposed.') {\n if (disposed) return;\n disposed = true;\n launchers.clear();\n await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));\n listeners.clear();\n },\n };\n\n return Object.freeze(service);\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=require("../../../_virtual/_rolldown/runtime.cjs");let t=require("node:fs"),n=require("node:fs/promises"),r=require("node:path");r=e.__toESM(r,1);let i=require("node:url");async function a(){let e=r.default.dirname((0,i.fileURLToPath)(require("url").pathToFileURL(__filename).href));for(;!(0,t.existsSync)(r.default.join(e,`package.json`));){let t=r.default.dirname(e);if(t===e)throw Error(`Loop package resource not found.`);e=t}return(0,n.readFile)(r.default.join(e,`src/prompts/doompi-use-loop/SKILL.md`),`utf8`)}exports.readLoopResource=a;
2
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["path","fileURLToPath","existsSync","readFile"],"sources":["../../../../src/services/loopResource/index.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nexport async function readLoopResource(): Promise<string> {\n let directory = path.dirname(fileURLToPath(import.meta.url));\n while (!existsSync(path.join(directory, 'package.json'))) {\n const parent = path.dirname(directory);\n if (parent === directory) throw new Error('Loop package resource not found.');\n directory = parent;\n }\n return readFile(path.join(directory, 'src/prompts/doompi-use-loop/SKILL.md'), 'utf8');\n}\n"],"mappings":"oLAIA,eAAsB,GAAoC,CACxD,IAAI,EAAYA,EAAAA,QAAK,SAAA,EAAQC,EAAAA,cAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,IAA6B,CAAC,EAC3D,KAAO,EAAA,EAACC,EAAAA,WAAAA,CAAWF,EAAAA,QAAK,KAAK,EAAW,cAAc,CAAC,GAAG,CACxD,IAAM,EAASA,EAAAA,QAAK,QAAQ,CAAS,EACrC,GAAI,IAAW,EAAW,MAAU,MAAM,kCAAkC,EAC5E,EAAY,CACd,CACA,OAAA,EAAOG,EAAAA,SAAAA,CAASH,EAAAA,QAAK,KAAK,EAAW,sCAAsC,EAAG,MAAM,CACtF"}
@@ -1,2 +0,0 @@
1
- import{existsSync as e}from"node:fs";import{readFile as t}from"node:fs/promises";import n from"node:path";import{fileURLToPath as r}from"node:url";async function i(){let i=n.dirname(r(import.meta.url));for(;!e(n.join(i,`package.json`));){let e=n.dirname(i);if(e===i)throw Error(`Loop package resource not found.`);i=e}return t(n.join(i,`src/prompts/doompi-use-loop/SKILL.md`),`utf8`)}export{i as readLoopResource};
2
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/loopResource/index.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nexport async function readLoopResource(): Promise<string> {\n let directory = path.dirname(fileURLToPath(import.meta.url));\n while (!existsSync(path.join(directory, 'package.json'))) {\n const parent = path.dirname(directory);\n if (parent === directory) throw new Error('Loop package resource not found.');\n directory = parent;\n }\n return readFile(path.join(directory, 'src/prompts/doompi-use-loop/SKILL.md'), 'utf8');\n}\n"],"mappings":"mJAIA,eAAsB,GAAoC,CACxD,IAAI,EAAY,EAAK,QAAQ,EAAc,YAAY,GAAG,CAAC,EAC3D,KAAO,CAAC,EAAW,EAAK,KAAK,EAAW,cAAc,CAAC,GAAG,CACxD,IAAM,EAAS,EAAK,QAAQ,CAAS,EACrC,GAAI,IAAW,EAAW,MAAU,MAAM,kCAAkC,EAC5E,EAAY,CACd,CACA,OAAO,EAAS,EAAK,KAAK,EAAW,sCAAsC,EAAG,MAAM,CACtF"}
@@ -1,2 +0,0 @@
1
- const e=require("../constants/piLoop.cjs"),t=require("../constants/loop.cjs"),n={key:`l`,label:`loops`,detail:`recurring prompt loops`,order:60};function r(r){let i=r.registerLeader({source:e.PACKAGE_SOURCE,bindings:[{id:`loop.start`,path:[n,{key:`s`,label:`start`,detail:`begin a recurring loop`}],command:{name:t.START_COMMAND_NAME}},{id:`loop.list`,path:[n,{key:`l`,label:`list`,detail:`loops in this session`}],command:{name:t.LIST_COMMAND_NAME}}]});return()=>i.dispose()}exports.registerLeaderContribution=r;
2
- //# sourceMappingURL=leader.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"leader.cjs","names":["PACKAGE_SOURCE","START_COMMAND_NAME","LIST_COMMAND_NAME"],"sources":["../../../src/tui/leader.ts"],"sourcesContent":["import type { DoomUiHubService } from '@agimon-ai/doompi-core/ui-hub';\n\nimport { LIST_COMMAND_NAME, START_COMMAND_NAME } from '../constants/loop';\nimport { LOOPS_GROUP_ORDER, PACKAGE_SOURCE } from '../constants/piLoop';\n\nconst LOOPS_GROUP_SEGMENT = {\n key: 'l',\n label: 'loops',\n detail: 'recurring prompt loops',\n order: LOOPS_GROUP_ORDER,\n} as const;\n\nexport function registerLeaderContribution(hub: DoomUiHubService): () => void {\n const contribution = hub.registerLeader({\n source: PACKAGE_SOURCE,\n bindings: [\n {\n id: 'loop.start',\n path: [LOOPS_GROUP_SEGMENT, { key: 's', label: 'start', detail: 'begin a recurring loop' }],\n command: { name: START_COMMAND_NAME },\n },\n {\n id: 'loop.list',\n path: [LOOPS_GROUP_SEGMENT, { key: 'l', label: 'list', detail: 'loops in this session' }],\n command: { name: LIST_COMMAND_NAME },\n },\n ],\n });\n return () => contribution.dispose();\n}\n"],"mappings":"8EAKM,EAAsB,CAC1B,IAAK,IACL,MAAO,QACP,OAAQ,yBACR,MAAA,EACF,EAEA,SAAgB,EAA2B,EAAmC,CAC5E,IAAM,EAAe,EAAI,eAAe,CACtC,OAAQA,EAAAA,eACR,SAAU,CACR,CACE,GAAI,aACJ,KAAM,CAAC,EAAqB,CAAE,IAAK,IAAK,MAAO,QAAS,OAAQ,wBAAyB,CAAC,EAC1F,QAAS,CAAE,KAAMC,EAAAA,kBAAmB,CACtC,EACA,CACE,GAAI,YACJ,KAAM,CAAC,EAAqB,CAAE,IAAK,IAAK,MAAO,OAAQ,OAAQ,uBAAwB,CAAC,EACxF,QAAS,CAAE,KAAMC,EAAAA,iBAAkB,CACrC,CACF,CACF,CAAC,EACD,UAAa,EAAa,QAAQ,CACpC"}
@@ -1,2 +0,0 @@
1
- import{PACKAGE_SOURCE as e}from"../constants/piLoop.mjs";import{LIST_COMMAND_NAME as t,START_COMMAND_NAME as n}from"../constants/loop.mjs";const r={key:`l`,label:`loops`,detail:`recurring prompt loops`,order:60};function i(i){let a=i.registerLeader({source:e,bindings:[{id:`loop.start`,path:[r,{key:`s`,label:`start`,detail:`begin a recurring loop`}],command:{name:n}},{id:`loop.list`,path:[r,{key:`l`,label:`list`,detail:`loops in this session`}],command:{name:t}}]});return()=>a.dispose()}export{i as registerLeaderContribution};
2
- //# sourceMappingURL=leader.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"leader.mjs","names":[],"sources":["../../../src/tui/leader.ts"],"sourcesContent":["import type { DoomUiHubService } from '@agimon-ai/doompi-core/ui-hub';\n\nimport { LIST_COMMAND_NAME, START_COMMAND_NAME } from '../constants/loop';\nimport { LOOPS_GROUP_ORDER, PACKAGE_SOURCE } from '../constants/piLoop';\n\nconst LOOPS_GROUP_SEGMENT = {\n key: 'l',\n label: 'loops',\n detail: 'recurring prompt loops',\n order: LOOPS_GROUP_ORDER,\n} as const;\n\nexport function registerLeaderContribution(hub: DoomUiHubService): () => void {\n const contribution = hub.registerLeader({\n source: PACKAGE_SOURCE,\n bindings: [\n {\n id: 'loop.start',\n path: [LOOPS_GROUP_SEGMENT, { key: 's', label: 'start', detail: 'begin a recurring loop' }],\n command: { name: START_COMMAND_NAME },\n },\n {\n id: 'loop.list',\n path: [LOOPS_GROUP_SEGMENT, { key: 'l', label: 'list', detail: 'loops in this session' }],\n command: { name: LIST_COMMAND_NAME },\n },\n ],\n });\n return () => contribution.dispose();\n}\n"],"mappings":"2IAKA,MAAM,EAAsB,CAC1B,IAAK,IACL,MAAO,QACP,OAAQ,yBACR,MAAA,EACF,EAEA,SAAgB,EAA2B,EAAmC,CAC5E,IAAM,EAAe,EAAI,eAAe,CACtC,OAAQ,EACR,SAAU,CACR,CACE,GAAI,aACJ,KAAM,CAAC,EAAqB,CAAE,IAAK,IAAK,MAAO,QAAS,OAAQ,wBAAyB,CAAC,EAC1F,QAAS,CAAE,KAAM,CAAmB,CACtC,EACA,CACE,GAAI,YACJ,KAAM,CAAC,EAAqB,CAAE,IAAK,IAAK,MAAO,OAAQ,OAAQ,uBAAwB,CAAC,EACxF,QAAS,CAAE,KAAM,CAAkB,CACrC,CACF,CACF,CAAC,EACD,UAAa,EAAa,QAAQ,CACpC"}
@@ -1,2 +0,0 @@
1
- let e=require("@agimon-ai/doompi-ui/doom-overlay"),t=require("@earendil-works/pi-tui");const n=`Stopped from the loop list.`;var r=class extends e.DoomOverlay{client;done;unsubscribe;instances;selected=0;constructor(e,t,n,r){super(e,t),this.client=n,this.done=r,this.instances=n.listInstances(),this.unsubscribe=n.subscribe(()=>this.refresh())}dispose(){this.unsubscribe()}handleInput(e){if((0,t.matchesKey)(e,`escape`)||(0,t.matchesKey)(e,`ctrl+c`)||e===`q`){this.done(void 0);return}if((0,t.matchesKey)(e,`up`)||e===`k`){this.move(-1);return}if((0,t.matchesKey)(e,`down`)||e===`j`){this.move(1);return}if(e===`x`){let e=this.instances[this.selected];e&&this.client.stop(e.instanceId,n)}}getChrome(){return{title:`LOOPS`,breadcrumb:`SPC › l / loops › l / list`,headerRight:`${this.instances.length} active`,footer:`${e.DOOM_NAVIGATION_KEYS.list} move · x stop · esc close · launched detached work continues`,footerHints:[[e.DOOM_NAVIGATION_KEYS.list,`move`],[`x`,`stop scheduling`],[`esc`,`close`]],accent:e.DOOM_OVERLAY_ACCENT}}renderBody(e){let n=this.theme.fg(`dim`,(0,t.truncateToWidth)(`Stopping a loop does not cancel detached work already launched.`,e));return this.instances.length?[n,``,...this.instances.flatMap((n,r)=>{let i=r===this.selected?this.theme.fg(`accent`,`▸`):` `,a=n.label??n.launcherLabel,o=this.theme.fg(n.state===`running`?`success`:`warning`,n.state),s=(0,t.truncateToWidth)(`${i} ${a} · ${o}`,e),c=n.detail??n.instanceId;return[s,` ${this.theme.fg(`dim`,(0,t.truncateToWidth)(c,Math.max(0,e-4)))}`]})]:[this.theme.fg(`dim`,`No loop instances are active in this session.`),``,n]}refresh(){this.instances=this.client.listInstances(),this.selected=Math.min(this.selected,Math.max(0,this.instances.length-1)),this.invalidate()}move(e){this.instances.length&&(this.selected=Math.max(0,Math.min(this.selected+e,this.instances.length-1)),this.invalidate())}};async function i(t,i){if(t.mode!==`tui`){let e=i.listInstances(),r=e.map(e=>`${e.label??e.launcherLabel} (${e.instanceId})`),a=await t.ui.select(`Stop loop scheduling`,r),o=e[r.indexOf(a??``)];o&&await i.stop(o.instanceId,n);return}await t.ui.custom((e,t,n,a)=>new r(e,t,i,a),e.DOOM_FULLSCREEN_UI_OPTIONS)}exports.LoopListOverlay=r,exports.openLoopListOverlay=i;
2
- //# sourceMappingURL=loopListOverlay.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopListOverlay.cjs","names":["DoomOverlay","matchesKey","DOOM_NAVIGATION_KEYS","DOOM_OVERLAY_ACCENT","truncateToWidth","DOOM_FULLSCREEN_UI_OPTIONS"],"sources":["../../../src/tui/loopListOverlay.ts"],"sourcesContent":["import {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_NAVIGATION_KEYS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/doom-overlay';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { DoomLoopLaunchersService, LoopInstanceSnapshot } from '../schemas/loopLaunchers';\n\nconst EMPTY_MESSAGE = 'No loop instances are active in this session.';\nconst STOP_SCOPE_MESSAGE = 'Stopping a loop does not cancel detached work already launched.';\nconst STOP_REASON = 'Stopped from the loop list.';\n\nexport class LoopListOverlay extends DoomOverlay {\n private readonly unsubscribe: () => void;\n private instances: readonly LoopInstanceSnapshot[];\n private selected = 0;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly client: DoomLoopLaunchersService,\n private readonly done: (result: undefined) => void,\n ) {\n super(tui, theme);\n this.instances = client.listInstances();\n this.unsubscribe = client.subscribe(() => this.refresh());\n }\n\n dispose(): void {\n this.unsubscribe();\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c') || data === 'q') {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down') || data === 'j') {\n this.move(1);\n return;\n }\n if (data === 'x') {\n const instance = this.instances[this.selected];\n if (instance) void this.client.stop(instance.instanceId, STOP_REASON);\n }\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: 'LOOPS',\n breadcrumb: 'SPC › l / loops › l / list',\n headerRight: `${this.instances.length} active`,\n footer: `${DOOM_NAVIGATION_KEYS.list} move · x stop · esc close · launched detached work continues`,\n footerHints: [\n [DOOM_NAVIGATION_KEYS.list, 'move'],\n ['x', 'stop scheduling'],\n ['esc', 'close'],\n ],\n accent: DOOM_OVERLAY_ACCENT,\n };\n }\n\n protected renderBody(width: number): string[] {\n const stopScope = this.theme.fg('dim', truncateToWidth(STOP_SCOPE_MESSAGE, width));\n if (!this.instances.length) return [this.theme.fg('dim', EMPTY_MESSAGE), '', stopScope];\n return [\n stopScope,\n '',\n ...this.instances.flatMap((instance, index) => {\n const marker = index === this.selected ? this.theme.fg('accent', '▸') : ' ';\n const name = instance.label ?? instance.launcherLabel;\n const state = this.theme.fg(instance.state === 'running' ? 'success' : 'warning', instance.state);\n const line = truncateToWidth(`${marker} ${name} · ${state}`, width);\n const detail = instance.detail ?? instance.instanceId;\n return [line, ` ${this.theme.fg('dim', truncateToWidth(detail, Math.max(0, width - 4)))}`];\n }),\n ];\n }\n\n private refresh(): void {\n this.instances = this.client.listInstances();\n this.selected = Math.min(this.selected, Math.max(0, this.instances.length - 1));\n this.invalidate();\n }\n\n private move(delta: number): void {\n if (!this.instances.length) return;\n this.selected = Math.max(0, Math.min(this.selected + delta, this.instances.length - 1));\n this.invalidate();\n }\n}\n\nexport async function openLoopListOverlay(ctx: ExtensionContext, client: DoomLoopLaunchersService): Promise<void> {\n if (ctx.mode !== 'tui') {\n const instances = client.listInstances();\n const labels = instances.map((instance) => `${instance.label ?? instance.launcherLabel} (${instance.instanceId})`);\n const selected = await ctx.ui.select('Stop loop scheduling', labels);\n const instance = instances[labels.indexOf(selected ?? '')];\n if (instance) await client.stop(instance.instanceId, STOP_REASON);\n return;\n }\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new LoopListOverlay(tui, theme, client, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{DOOM_FULLSCREEN_UI_OPTIONS as e,DOOM_NAVIGATION_KEYS as t,DOOM_OVERLAY_ACCENT as n,DoomOverlay as r}from"@agimon-ai/doompi-ui/doom-overlay";import{matchesKey as i,truncateToWidth as a}from"@earendil-works/pi-tui";const o=`Stopped from the loop list.`;var s=class extends r{client;done;unsubscribe;instances;selected=0;constructor(e,t,n,r){super(e,t),this.client=n,this.done=r,this.instances=n.listInstances(),this.unsubscribe=n.subscribe(()=>this.refresh())}dispose(){this.unsubscribe()}handleInput(e){if(i(e,`escape`)||i(e,`ctrl+c`)||e===`q`){this.done(void 0);return}if(i(e,`up`)||e===`k`){this.move(-1);return}if(i(e,`down`)||e===`j`){this.move(1);return}if(e===`x`){let e=this.instances[this.selected];e&&this.client.stop(e.instanceId,o)}}getChrome(){return{title:`LOOPS`,breadcrumb:`SPC › l / loops › l / list`,headerRight:`${this.instances.length} active`,footer:`${t.list} move · x stop · esc close · launched detached work continues`,footerHints:[[t.list,`move`],[`x`,`stop scheduling`],[`esc`,`close`]],accent:n}}renderBody(e){let t=this.theme.fg(`dim`,a(`Stopping a loop does not cancel detached work already launched.`,e));return this.instances.length?[t,``,...this.instances.flatMap((t,n)=>{let r=n===this.selected?this.theme.fg(`accent`,`▸`):` `,i=t.label??t.launcherLabel,o=this.theme.fg(t.state===`running`?`success`:`warning`,t.state),s=a(`${r} ${i} · ${o}`,e),c=t.detail??t.instanceId;return[s,` ${this.theme.fg(`dim`,a(c,Math.max(0,e-4)))}`]})]:[this.theme.fg(`dim`,`No loop instances are active in this session.`),``,t]}refresh(){this.instances=this.client.listInstances(),this.selected=Math.min(this.selected,Math.max(0,this.instances.length-1)),this.invalidate()}move(e){this.instances.length&&(this.selected=Math.max(0,Math.min(this.selected+e,this.instances.length-1)),this.invalidate())}};async function c(t,n){if(t.mode!==`tui`){let e=n.listInstances(),r=e.map(e=>`${e.label??e.launcherLabel} (${e.instanceId})`),i=await t.ui.select(`Stop loop scheduling`,r),a=e[r.indexOf(i??``)];a&&await n.stop(a.instanceId,o);return}await t.ui.custom((e,t,r,i)=>new s(e,t,n,i),e)}export{s as LoopListOverlay,c as openLoopListOverlay};
2
- //# sourceMappingURL=loopListOverlay.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopListOverlay.mjs","names":[],"sources":["../../../src/tui/loopListOverlay.ts"],"sourcesContent":["import {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_NAVIGATION_KEYS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/doom-overlay';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { DoomLoopLaunchersService, LoopInstanceSnapshot } from '../schemas/loopLaunchers';\n\nconst EMPTY_MESSAGE = 'No loop instances are active in this session.';\nconst STOP_SCOPE_MESSAGE = 'Stopping a loop does not cancel detached work already launched.';\nconst STOP_REASON = 'Stopped from the loop list.';\n\nexport class LoopListOverlay extends DoomOverlay {\n private readonly unsubscribe: () => void;\n private instances: readonly LoopInstanceSnapshot[];\n private selected = 0;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly client: DoomLoopLaunchersService,\n private readonly done: (result: undefined) => void,\n ) {\n super(tui, theme);\n this.instances = client.listInstances();\n this.unsubscribe = client.subscribe(() => this.refresh());\n }\n\n dispose(): void {\n this.unsubscribe();\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c') || data === 'q') {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down') || data === 'j') {\n this.move(1);\n return;\n }\n if (data === 'x') {\n const instance = this.instances[this.selected];\n if (instance) void this.client.stop(instance.instanceId, STOP_REASON);\n }\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: 'LOOPS',\n breadcrumb: 'SPC › l / loops › l / list',\n headerRight: `${this.instances.length} active`,\n footer: `${DOOM_NAVIGATION_KEYS.list} move · x stop · esc close · launched detached work continues`,\n footerHints: [\n [DOOM_NAVIGATION_KEYS.list, 'move'],\n ['x', 'stop scheduling'],\n ['esc', 'close'],\n ],\n accent: DOOM_OVERLAY_ACCENT,\n };\n }\n\n protected renderBody(width: number): string[] {\n const stopScope = this.theme.fg('dim', truncateToWidth(STOP_SCOPE_MESSAGE, width));\n if (!this.instances.length) return [this.theme.fg('dim', EMPTY_MESSAGE), '', stopScope];\n return [\n stopScope,\n '',\n ...this.instances.flatMap((instance, index) => {\n const marker = index === this.selected ? this.theme.fg('accent', '▸') : ' ';\n const name = instance.label ?? instance.launcherLabel;\n const state = this.theme.fg(instance.state === 'running' ? 'success' : 'warning', instance.state);\n const line = truncateToWidth(`${marker} ${name} · ${state}`, width);\n const detail = instance.detail ?? instance.instanceId;\n return [line, ` ${this.theme.fg('dim', truncateToWidth(detail, Math.max(0, width - 4)))}`];\n }),\n ];\n }\n\n private refresh(): void {\n this.instances = this.client.listInstances();\n this.selected = Math.min(this.selected, Math.max(0, this.instances.length - 1));\n this.invalidate();\n }\n\n private move(delta: number): void {\n if (!this.instances.length) return;\n this.selected = Math.max(0, Math.min(this.selected + delta, this.instances.length - 1));\n this.invalidate();\n }\n}\n\nexport async function openLoopListOverlay(ctx: ExtensionContext, client: DoomLoopLaunchersService): Promise<void> {\n if (ctx.mode !== 'tui') {\n const instances = client.listInstances();\n const labels = instances.map((instance) => `${instance.label ?? instance.launcherLabel} (${instance.instanceId})`);\n const selected = await ctx.ui.select('Stop loop scheduling', labels);\n const instance = instances[labels.indexOf(selected ?? '')];\n if (instance) await client.stop(instance.instanceId, STOP_REASON);\n return;\n }\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new LoopListOverlay(tui, theme, client, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=require("../schemas/loopLaunchers.cjs"),t=require("../constants/piLoop.cjs"),n=require("../services/defaultLoopLauncher/index.cjs"),r=require("../services/loopLaunchers/index.cjs"),i=require("../types/loopView.cjs"),a=require("./leader.cjs"),o=require("./loopListOverlay.cjs"),s=require("./startLoopOverlay.cjs");let c=require("@agimon-ai/doompi-minor-mode"),l=require("@agimon-ai/doompi-core/cordis-host"),u=require("@agimon-ai/doompi-core/ui-hub");const d=`start`,f=`stop`,p=`Pi session replaced.`;function m(e,n){let r=n.length>0;return{activation:r?`active`:`inactive`,condition:n.some(({state:e})=>e===`starting`||e===`stopping`)?`queued`:`ready`,...r?{detail:`${n.length} active`,color:t.ACTIVE_MODE_COLOR}:{},actions:[...e.length>0?[{id:d,enabled:!0}]:[{id:d,enabled:!1,disabledReason:`No loop launchers are registered.`}],...r?[{id:f,enabled:!0}]:[{id:f,enabled:!1,disabledReason:`No loops are active.`}]]}}function h(h){let g=!0,_,v,y,b=new WeakSet,x=e=>{let t=_;if(g&&t&&t.context.sessionManager===e.sessionManager&&t.hostSession.sessionId===e.sessionManager.getSessionId())return t},S=e=>{e.context.ui?.setStatus(t.STATUS_KEY,void 0),e.context.mode!==`tui`&&e.context.ui?.setStatus(i.LOOP_VIEW_STATUS_KEY,void 0)},C=e=>{if(!g||_!==e)return;let n=e.launchers.listInstances();e.context.ui?.setStatus(t.STATUS_KEY,n.length?`loops: ${n.length}`:void 0),e.context.mode!==`tui`&&e.context.ui?.setStatus(i.LOOP_VIEW_STATUS_KEY,i.formatLoopStatusView(n)),v?.publish()},w=n.createDefaultLoopLauncher(h);return v=(0,c.defineMinorMode)({descriptor:{source:t.PACKAGE_SOURCE,id:t.MODE_STATUS_ID,label:`Loop`,description:`Session-scoped recurring prompt loops.`,order:60,actions:[{id:d,label:`Start`,description:`Start a loop with a registered launcher.`,contexts:[`tui`,`headless`],parameters:[{name:`launcherId`,label:`Launcher`,kind:`string`,required:!0,minLength:1},{name:`instanceId`,label:`Instance ID`,kind:`string`,required:!1,minLength:1}]},{id:f,label:`Stop`,description:`Stop one active loop instance.`,contexts:[`tui`,`headless`],parameters:[{name:`instanceId`,label:`Instance ID`,kind:`string`,required:!0,minLength:1},{name:`reason`,label:`Reason`,kind:`string`,required:!1,minLength:1}]}]},state:()=>_?m(_.launchers.listLaunchers(),_.launchers.listInstances()):m([],[]),async handleAction(e,t,n,{signal:r}){r.throwIfAborted();let i=_?x(_.context):void 0;if(!i)throw Error(`Loop launchers are unavailable for the active session.`);if(t===d){let e=await i.launchers.launch(n.launcherId,n.instanceId?{instanceId:n.instanceId}:{});if(_!==i)throw Error(`Loop action became stale.`);return{message:e?`Loop '${e.instanceId}' started.`:`Loop launch was cancelled.`}}if(t===f){let e=await i.launchers.stop(n.instanceId,n.reason??`Stopped through minor_mode.`);if(_!==i)throw Error(`Loop action became stale.`);return{message:e?`Loop stopped.`:`Loop instance was not active.`}}throw Error(`Unknown loop mode action: ${t}`)}}).createOwner(void 0),{onAgentSettled:w.onAgentSettled,services:[t=>{t.inject([u.DOOM_UI_HUB_SERVICE],e=>{let t=a.registerLeaderContribution((0,u.requireDoomUiHub)(e));return y=t,()=>{t(),y===t&&(y=void 0)}}),t.inject([l.DOOM_CORDIS_SESSION_SERVICE],async t=>{let n=t.get(l.DOOM_CORDIS_SESSION_SERVICE),i=r.createDoomLoopLaunchersService({generation:`${n.generation}:loop-launchers`,createInstanceId:()=>crypto.randomUUID(),timestamp:()=>new Date().toISOString()}),a={context:n.context,hostSession:n,launchers:i};_=a,t.provide(e.DOOM_LOOP_LAUNCHERS_SERVICE,i);let o,s;try{o=w.register(n.context,i),s=i.subscribe(()=>C(a)),C(a)}catch(e){throw _===a&&(_=void 0),await i.dispose(p),e}return async()=>{_===a&&(_=void 0),s(),S(a),v?.publish();let e=[];try{await o.dispose(p)}catch(t){e.push(t)}try{await i.dispose(`Pi session shut down.`)}catch(t){e.push(t)}if(e.length>0)throw AggregateError(e,`Loop session cleanup failed.`)}})}],handlers:{async start(e,n){let r=x(e);if(r){if(b.has(r)){e.ui.notify(`A loop setup is already in progress.`,t.NOTIFY_INFO_LEVEL);return}b.add(r);try{let i=r.launchers.listLaunchers();if(!i.length){e.ui.notify(`No loop launchers are registered for this session.`,t.NOTIFY_INFO_LEVEL);return}let a=n.trim(),o;if(a){if(a.split(/\s+/u).length!==1){e.ui.notify(`Usage: /loop [launcherId]`,`error`);return}if(!i.some(e=>e.id===a)){e.ui.notify(`Unknown loop launcher: ${a}`,`error`);return}o=a}else if(o=await s.openStartLoopOverlay(e,i),_!==r||!o)return;let c=await r.launchers.launch(o);if(_!==r)return;c&&e.ui.notify(`${c.label??c.launcherLabel} started.`,t.NOTIFY_INFO_LEVEL)}catch(t){if(_!==r)return;e.ui.notify(`Loop could not start: ${t instanceof Error?t.message:String(t)}`,`error`)}finally{b.delete(r)}}},async list(e){let t=x(e);t&&await o.openLoopListOverlay(e,t.launchers)}},minorModes:[v],onDispose(){g=!1;let e=_;_=void 0,e&&S(e),v?.publish(),y?.(),y=void 0}}}exports.createLoopPiRuntime=h;
2
- //# sourceMappingURL=loopRuntime.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopRuntime.cjs","names":["ACTIVE_MODE_COLOR","STATUS_KEY","LOOP_VIEW_STATUS_KEY","formatLoopStatusView","createDefaultLoopLauncher","defineMinorMode","PACKAGE_SOURCE","MODE_STATUS_ID","DOOM_UI_HUB_SERVICE","registerLeaderContribution","requireDoomUiHub","DOOM_CORDIS_SESSION_SERVICE","createDoomLoopLaunchersService","DOOM_LOOP_LAUNCHERS_SERVICE","NOTIFY_INFO_LEVEL","openStartLoopOverlay","openLoopListOverlay"],"sources":["../../../src/tui/loopRuntime.ts"],"sourcesContent":["import { DOOM_CORDIS_SESSION_SERVICE, type DoomCordisSessionService } from '@agimon-ai/doompi-core/cordis-host';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-core/ui-hub';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/doompi-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport {\n ACTIVE_MODE_COLOR,\n LOOPS_GROUP_ORDER,\n MODE_STATUS_ID,\n NOTIFY_INFO_LEVEL,\n PACKAGE_SOURCE,\n STATUS_KEY,\n} from '../constants/piLoop';\nimport {\n DOOM_LOOP_LAUNCHERS_SERVICE,\n type DoomLoopLaunchersService,\n type LoopInstanceSnapshot,\n type LoopLauncherRegistration,\n type LoopLauncherSummary,\n} from '../schemas/loopLaunchers';\nimport { createDefaultLoopLauncher } from '../services/defaultLoopLauncher';\nimport { createDoomLoopLaunchersService } from '../services/loopLaunchers';\nimport { formatLoopStatusView, LOOP_VIEW_STATUS_KEY } from '../types/loopView';\nimport { registerLeaderContribution } from './leader';\nimport { openLoopListOverlay } from './loopListOverlay';\nimport { openStartLoopOverlay } from './startLoopOverlay';\n\nconst MODE_ACTION_START = 'start';\nconst MODE_ACTION_STOP = 'stop';\nconst SESSION_REPLACED_REASON = 'Pi session replaced.';\nconst SESSION_SHUTDOWN_REASON = 'Pi session shut down.';\n\ninterface ActiveLoopSession {\n readonly context: ExtensionContext;\n readonly hostSession: DoomCordisSessionService;\n readonly launchers: DoomLoopLaunchersService;\n}\n\nfunction loopModeState(\n launchers: readonly LoopLauncherSummary[],\n instances: readonly LoopInstanceSnapshot[],\n): MinorModeState {\n const active = instances.length > 0;\n return {\n activation: active ? 'active' : 'inactive',\n condition: instances.some(({ state }) => state === 'starting' || state === 'stopping') ? 'queued' : 'ready',\n ...(active ? { detail: `${instances.length} active`, color: ACTIVE_MODE_COLOR } : {}),\n actions: [\n ...(launchers.length > 0\n ? [{ id: MODE_ACTION_START, enabled: true } as const]\n : [{ id: MODE_ACTION_START, enabled: false, disabledReason: 'No loop launchers are registered.' } as const]),\n ...(active\n ? [{ id: MODE_ACTION_STOP, enabled: true } as const]\n : [{ id: MODE_ACTION_STOP, enabled: false, disabledReason: 'No loops are active.' } as const]),\n ],\n };\n}\n\nimport type { LoopCommandHandlers } from '../types/loopCommand';\ninterface LoopPiRuntime {\n onAgentSettled(this: void): void;\n services: Array<(context: Context) => void>;\n handlers: LoopCommandHandlers;\n minorModes: MinorModeOwner[];\n onDispose(): void;\n}\n/** Install Loop state and external resources into its host-owned Cordis plugin fiber. */\nexport function createLoopPiRuntime(pi: ExtensionAPI): LoopPiRuntime {\n let active = true;\n let activeSession: ActiveLoopSession | undefined;\n let mode: MinorModeOwner | undefined;\n let disposeLeader: (() => void) | undefined;\n const launchesInProgress = new WeakSet<ActiveLoopSession>();\n\n const currentSession = (context: ExtensionContext): ActiveLoopSession | undefined => {\n const binding = activeSession;\n if (\n !active ||\n !binding ||\n binding.context.sessionManager !== context.sessionManager ||\n binding.hostSession.sessionId !== context.sessionManager.getSessionId()\n ) {\n return undefined;\n }\n return binding;\n };\n\n const clearStatuses = (binding: ActiveLoopSession): void => {\n binding.context.ui?.setStatus(STATUS_KEY, undefined);\n if (binding.context.mode !== 'tui') binding.context.ui?.setStatus(LOOP_VIEW_STATUS_KEY, undefined);\n };\n const publishStatus = (binding: ActiveLoopSession): void => {\n if (!active || activeSession !== binding) return;\n const instances = binding.launchers.listInstances();\n binding.context.ui?.setStatus(STATUS_KEY, instances.length ? `loops: ${instances.length}` : undefined);\n if (binding.context.mode !== 'tui') {\n binding.context.ui?.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(instances));\n }\n mode?.publish();\n };\n\n const defaultLauncher = createDefaultLoopLauncher(pi);\n\n mode = defineMinorMode({\n descriptor: {\n source: PACKAGE_SOURCE,\n id: MODE_STATUS_ID,\n label: 'Loop',\n description: 'Session-scoped recurring prompt loops.',\n order: LOOPS_GROUP_ORDER,\n actions: [\n {\n id: MODE_ACTION_START,\n label: 'Start',\n description: 'Start a loop with a registered launcher.',\n contexts: ['tui', 'headless'],\n parameters: [\n { name: 'launcherId', label: 'Launcher', kind: 'string', required: true, minLength: 1 },\n { name: 'instanceId', label: 'Instance ID', kind: 'string', required: false, minLength: 1 },\n ],\n },\n {\n id: MODE_ACTION_STOP,\n label: 'Stop',\n description: 'Stop one active loop instance.',\n contexts: ['tui', 'headless'],\n parameters: [\n { name: 'instanceId', label: 'Instance ID', kind: 'string', required: true, minLength: 1 },\n { name: 'reason', label: 'Reason', kind: 'string', required: false, minLength: 1 },\n ],\n },\n ],\n },\n state: () =>\n activeSession\n ? loopModeState(activeSession.launchers.listLaunchers(), activeSession.launchers.listInstances())\n : loopModeState([], []),\n async handleAction(_runtime, actionId, argumentsValue, { signal }) {\n signal.throwIfAborted();\n const binding = activeSession ? currentSession(activeSession.context) : undefined;\n if (!binding) throw new Error('Loop launchers are unavailable for the active session.');\n if (actionId === MODE_ACTION_START) {\n const instance = await binding.launchers.launch(\n argumentsValue.launcherId as string,\n argumentsValue.instanceId ? { instanceId: argumentsValue.instanceId as string } : {},\n );\n if (activeSession !== binding) throw new Error('Loop action became stale.');\n return { message: instance ? `Loop '${instance.instanceId}' started.` : 'Loop launch was cancelled.' };\n }\n if (actionId === MODE_ACTION_STOP) {\n const stopped = await binding.launchers.stop(\n argumentsValue.instanceId as string,\n (argumentsValue.reason as string | undefined) ?? 'Stopped through minor_mode.',\n );\n if (activeSession !== binding) throw new Error('Loop action became stale.');\n return { message: stopped ? 'Loop stopped.' : 'Loop instance was not active.' };\n }\n throw new Error(`Unknown loop mode action: ${actionId}`);\n },\n }).createOwner(undefined);\n return {\n onAgentSettled: defaultLauncher.onAgentSettled,\n services: [\n (cordis: Context) => {\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const dispose = registerLeaderContribution(requireDoomUiHub(uiContext));\n disposeLeader = dispose;\n return () => {\n dispose();\n if (disposeLeader === dispose) disposeLeader = undefined;\n };\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], async (sessionContext) => {\n const hostSession = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const launchers = createDoomLoopLaunchersService({\n generation: `${hostSession.generation}:loop-launchers`,\n createInstanceId: () => crypto.randomUUID(),\n timestamp: () => new Date().toISOString(),\n });\n const binding: ActiveLoopSession = { context: hostSession.context, hostSession, launchers };\n activeSession = binding;\n sessionContext.provide(DOOM_LOOP_LAUNCHERS_SERVICE, launchers);\n\n let defaultRegistration: LoopLauncherRegistration;\n let unsubscribe: () => void;\n try {\n defaultRegistration = defaultLauncher.register(hostSession.context, launchers);\n unsubscribe = launchers.subscribe(() => publishStatus(binding));\n publishStatus(binding);\n } catch (error) {\n if (activeSession === binding) activeSession = undefined;\n await launchers.dispose(SESSION_REPLACED_REASON);\n throw error;\n }\n\n return async () => {\n if (activeSession === binding) activeSession = undefined;\n unsubscribe();\n clearStatuses(binding);\n mode?.publish();\n const cleanupErrors: unknown[] = [];\n try {\n await defaultRegistration.dispose(SESSION_REPLACED_REASON);\n } catch (error) {\n cleanupErrors.push(error);\n }\n try {\n await launchers.dispose(SESSION_SHUTDOWN_REASON);\n } catch (error) {\n cleanupErrors.push(error);\n }\n if (cleanupErrors.length > 0) throw new AggregateError(cleanupErrors, 'Loop session cleanup failed.');\n };\n });\n },\n ],\n handlers: {\n async start(ctx, args) {\n const binding = currentSession(ctx);\n if (!binding) return;\n if (launchesInProgress.has(binding)) {\n ctx.ui.notify('A loop setup is already in progress.', NOTIFY_INFO_LEVEL);\n return;\n }\n launchesInProgress.add(binding);\n try {\n const launchers = binding.launchers.listLaunchers();\n if (!launchers.length) {\n ctx.ui.notify('No loop launchers are registered for this session.', NOTIFY_INFO_LEVEL);\n return;\n }\n\n const argument = args.trim();\n let launcherId: string | undefined;\n if (argument) {\n if (argument.split(/\\s+/u).length !== 1) {\n ctx.ui.notify('Usage: /loop [launcherId]', 'error');\n return;\n }\n if (!launchers.some((launcher) => launcher.id === argument)) {\n ctx.ui.notify(`Unknown loop launcher: ${argument}`, 'error');\n return;\n }\n launcherId = argument;\n } else {\n launcherId = await openStartLoopOverlay(ctx, launchers);\n if (activeSession !== binding || !launcherId) return;\n }\n\n const instance = await binding.launchers.launch(launcherId);\n if (activeSession !== binding) return;\n if (instance) ctx.ui.notify(`${instance.label ?? instance.launcherLabel} started.`, NOTIFY_INFO_LEVEL);\n } catch (error) {\n if (activeSession !== binding) return;\n ctx.ui.notify(`Loop could not start: ${error instanceof Error ? error.message : String(error)}`, 'error');\n } finally {\n launchesInProgress.delete(binding);\n }\n },\n async list(ctx) {\n const binding = currentSession(ctx);\n if (!binding) return;\n await openLoopListOverlay(ctx, binding.launchers);\n },\n },\n minorModes: [mode],\n onDispose() {\n active = false;\n const binding = activeSession;\n activeSession = undefined;\n if (binding) clearStatuses(binding);\n mode?.publish();\n disposeLeader?.();\n disposeLeader = undefined;\n },\n };\n}\n"],"mappings":"0cA4BA,MAAM,EAAoB,QACpB,EAAmB,OACnB,EAA0B,uBAShC,SAAS,EACP,EACA,EACgB,CAChB,IAAM,EAAS,EAAU,OAAS,EAClC,MAAO,CACL,WAAY,EAAS,SAAW,WAChC,UAAW,EAAU,MAAM,CAAE,WAAY,IAAU,YAAc,IAAU,UAAU,EAAI,SAAW,QACpG,GAAI,EAAS,CAAE,OAAQ,GAAG,EAAU,OAAO,SAAU,MAAOA,EAAAA,iBAAkB,EAAI,CAAC,EACnF,QAAS,CACP,GAAI,EAAU,OAAS,EACnB,CAAC,CAAE,GAAI,EAAmB,QAAS,EAAK,CAAU,EAClD,CAAC,CAAE,GAAI,EAAmB,QAAS,GAAO,eAAgB,mCAAoC,CAAU,EAC5G,GAAI,EACA,CAAC,CAAE,GAAI,EAAkB,QAAS,EAAK,CAAU,EACjD,CAAC,CAAE,GAAI,EAAkB,QAAS,GAAO,eAAgB,sBAAuB,CAAU,CAChG,CACF,CACF,CAWA,SAAgB,EAAoB,EAAiC,CACnE,IAAI,EAAS,GACT,EACA,EACA,EACE,EAAqB,IAAI,QAEzB,EAAkB,GAA6D,CACnF,IAAM,EAAU,EAEd,GAAC,GACA,GACD,EAAQ,QAAQ,iBAAmB,EAAQ,gBAC3C,EAAQ,YAAY,YAAc,EAAQ,eAAe,aAAa,EAIxE,OAAO,CACT,EAEM,EAAiB,GAAqC,CAC1D,EAAQ,QAAQ,IAAI,UAAUC,EAAAA,WAAY,IAAA,EAAS,EAC/C,EAAQ,QAAQ,OAAS,OAAO,EAAQ,QAAQ,IAAI,UAAUC,EAAAA,qBAAsB,IAAA,EAAS,CACnG,EACM,EAAiB,GAAqC,CAC1D,GAAI,CAAC,GAAU,IAAkB,EAAS,OAC1C,IAAM,EAAY,EAAQ,UAAU,cAAc,EAClD,EAAQ,QAAQ,IAAI,UAAUD,EAAAA,WAAY,EAAU,OAAS,UAAU,EAAU,SAAW,IAAA,EAAS,EACjG,EAAQ,QAAQ,OAAS,OAC3B,EAAQ,QAAQ,IAAI,UAAUC,EAAAA,qBAAsBC,EAAAA,qBAAqB,CAAS,CAAC,EAErF,GAAM,QAAQ,CAChB,EAEM,EAAkBC,EAAAA,0BAA0B,CAAE,EA2DpD,MAzDA,IAAA,EAAOC,EAAAA,gBAAAA,CAAgB,CACrB,WAAY,CACV,OAAQC,EAAAA,eACR,GAAIC,EAAAA,eACJ,MAAO,OACP,YAAa,yCACb,MAAA,GACA,QAAS,CACP,CACE,GAAI,EACJ,MAAO,QACP,YAAa,2CACb,SAAU,CAAC,MAAO,UAAU,EAC5B,WAAY,CACV,CAAE,KAAM,aAAc,MAAO,WAAY,KAAM,SAAU,SAAU,GAAM,UAAW,CAAE,EACtF,CAAE,KAAM,aAAc,MAAO,cAAe,KAAM,SAAU,SAAU,GAAO,UAAW,CAAE,CAC5F,CACF,EACA,CACE,GAAI,EACJ,MAAO,OACP,YAAa,iCACb,SAAU,CAAC,MAAO,UAAU,EAC5B,WAAY,CACV,CAAE,KAAM,aAAc,MAAO,cAAe,KAAM,SAAU,SAAU,GAAM,UAAW,CAAE,EACzF,CAAE,KAAM,SAAU,MAAO,SAAU,KAAM,SAAU,SAAU,GAAO,UAAW,CAAE,CACnF,CACF,CACF,CACF,EACA,UACE,EACI,EAAc,EAAc,UAAU,cAAc,EAAG,EAAc,UAAU,cAAc,CAAC,EAC9F,EAAc,CAAC,EAAG,CAAC,CAAC,EAC1B,MAAM,aAAa,EAAU,EAAU,EAAgB,CAAE,UAAU,CACjE,EAAO,eAAe,EACtB,IAAM,EAAU,EAAgB,EAAe,EAAc,OAAO,EAAI,IAAA,GACxE,GAAI,CAAC,EAAS,MAAU,MAAM,wDAAwD,EACtF,GAAI,IAAa,EAAmB,CAClC,IAAM,EAAW,MAAM,EAAQ,UAAU,OACvC,EAAe,WACf,EAAe,WAAa,CAAE,WAAY,EAAe,UAAqB,EAAI,CAAC,CACrF,EACA,GAAI,IAAkB,EAAS,MAAU,MAAM,2BAA2B,EAC1E,MAAO,CAAE,QAAS,EAAW,SAAS,EAAS,WAAW,YAAc,4BAA6B,CACvG,CACA,GAAI,IAAa,EAAkB,CACjC,IAAM,EAAU,MAAM,EAAQ,UAAU,KACtC,EAAe,WACd,EAAe,QAAiC,6BACnD,EACA,GAAI,IAAkB,EAAS,MAAU,MAAM,2BAA2B,EAC1E,MAAO,CAAE,QAAS,EAAU,gBAAkB,+BAAgC,CAChF,CACA,MAAU,MAAM,6BAA6B,GAAU,CACzD,CACF,CAAC,CAAC,CAAC,YAAY,IAAA,EAAS,EACjB,CACL,eAAgB,EAAgB,eAChC,SAAU,CACP,GAAoB,CACnB,EAAO,OAAO,CAACC,EAAAA,mBAAmB,EAAI,GAAc,CAClD,IAAM,EAAUC,EAAAA,4BAAAA,EAA2BC,EAAAA,iBAAAA,CAAiB,CAAS,CAAC,EAEtE,MADA,GAAgB,MACH,CACX,EAAQ,EACJ,IAAkB,IAAS,EAAgB,IAAA,GACjD,CACF,CAAC,EAED,EAAO,OAAO,CAACC,EAAAA,2BAA2B,EAAG,KAAO,IAAmB,CACrE,IAAM,EAAc,EAAe,IAAIA,EAAAA,2BAA2B,EAC5D,EAAYC,EAAAA,+BAA+B,CAC/C,WAAY,GAAG,EAAY,WAAW,iBACtC,qBAAwB,OAAO,WAAW,EAC1C,cAAiB,IAAI,KAAK,CAAA,CAAE,YAAY,CAC1C,CAAC,EACK,EAA6B,CAAE,QAAS,EAAY,QAAS,cAAa,WAAU,EAC1F,EAAgB,EAChB,EAAe,QAAQC,EAAAA,4BAA6B,CAAS,EAE7D,IAAI,EACA,EACJ,GAAI,CACF,EAAsB,EAAgB,SAAS,EAAY,QAAS,CAAS,EAC7E,EAAc,EAAU,cAAgB,EAAc,CAAO,CAAC,EAC9D,EAAc,CAAO,CACvB,OAAS,EAAO,CAGd,MAFI,IAAkB,IAAS,EAAgB,IAAA,IAC/C,MAAM,EAAU,QAAQ,CAAuB,EACzC,CACR,CAEA,OAAO,SAAY,CACb,IAAkB,IAAS,EAAgB,IAAA,IAC/C,EAAY,EACZ,EAAc,CAAO,EACrB,GAAM,QAAQ,EACd,IAAM,EAA2B,CAAC,EAClC,GAAI,CACF,MAAM,EAAoB,QAAQ,CAAuB,CAC3D,OAAS,EAAO,CACd,EAAc,KAAK,CAAK,CAC1B,CACA,GAAI,CACF,MAAM,EAAU,QAAQ,uBAAuB,CACjD,OAAS,EAAO,CACd,EAAc,KAAK,CAAK,CAC1B,CACA,GAAI,EAAc,OAAS,EAAG,MAAU,eAAe,EAAe,8BAA8B,CACtG,CACF,CAAC,CACH,CACF,EACA,SAAU,CACR,MAAM,MAAM,EAAK,EAAM,CACrB,IAAM,EAAU,EAAe,CAAG,EAC7B,KACL,IAAI,EAAmB,IAAI,CAAO,EAAG,CACnC,EAAI,GAAG,OAAO,uCAAwCC,EAAAA,iBAAiB,EACvE,MACF,CACA,EAAmB,IAAI,CAAO,EAC9B,GAAI,CACF,IAAM,EAAY,EAAQ,UAAU,cAAc,EAClD,GAAI,CAAC,EAAU,OAAQ,CACrB,EAAI,GAAG,OAAO,qDAAsDA,EAAAA,iBAAiB,EACrF,MACF,CAEA,IAAM,EAAW,EAAK,KAAK,EACvB,EACJ,GAAI,EAAU,CACZ,GAAI,EAAS,MAAM,MAAM,CAAC,CAAC,SAAW,EAAG,CACvC,EAAI,GAAG,OAAO,4BAA6B,OAAO,EAClD,MACF,CACA,GAAI,CAAC,EAAU,KAAM,GAAa,EAAS,KAAO,CAAQ,EAAG,CAC3D,EAAI,GAAG,OAAO,0BAA0B,IAAY,OAAO,EAC3D,MACF,CACA,EAAa,CACf,MAEE,GADA,EAAa,MAAMC,EAAAA,qBAAqB,EAAK,CAAS,EAClD,IAAkB,GAAW,CAAC,EAAY,OAGhD,IAAM,EAAW,MAAM,EAAQ,UAAU,OAAO,CAAU,EAC1D,GAAI,IAAkB,EAAS,OAC3B,GAAU,EAAI,GAAG,OAAO,GAAG,EAAS,OAAS,EAAS,cAAc,WAAYD,EAAAA,iBAAiB,CACvG,OAAS,EAAO,CACd,GAAI,IAAkB,EAAS,OAC/B,EAAI,GAAG,OAAO,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,IAAK,OAAO,CAC1G,QAAU,CACR,EAAmB,OAAO,CAAO,CACnC,CAlCA,CAmCF,EACA,MAAM,KAAK,EAAK,CACd,IAAM,EAAU,EAAe,CAAG,EAC7B,GACL,MAAME,EAAAA,oBAAoB,EAAK,EAAQ,SAAS,CAClD,CACF,EACA,WAAY,CAAC,CAAI,EACjB,WAAY,CACV,EAAS,GACT,IAAM,EAAU,EAChB,EAAgB,IAAA,GACZ,GAAS,EAAc,CAAO,EAClC,GAAM,QAAQ,EACd,IAAgB,EAChB,EAAgB,IAAA,EAClB,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- import{DOOM_LOOP_LAUNCHERS_SERVICE as e}from"../schemas/loopLaunchers.mjs";import{ACTIVE_MODE_COLOR as t,MODE_STATUS_ID as n,NOTIFY_INFO_LEVEL as r,PACKAGE_SOURCE as i,STATUS_KEY as a}from"../constants/piLoop.mjs";import{createDefaultLoopLauncher as o}from"../services/defaultLoopLauncher/index.mjs";import{createDoomLoopLaunchersService as s}from"../services/loopLaunchers/index.mjs";import{LOOP_VIEW_STATUS_KEY as c,formatLoopStatusView as l}from"../types/loopView.mjs";import{registerLeaderContribution as u}from"./leader.mjs";import{openLoopListOverlay as d}from"./loopListOverlay.mjs";import{openStartLoopOverlay as f}from"./startLoopOverlay.mjs";import{defineMinorMode as p}from"@agimon-ai/doompi-minor-mode";import{DOOM_CORDIS_SESSION_SERVICE as m}from"@agimon-ai/doompi-core/cordis-host";import{DOOM_UI_HUB_SERVICE as h,requireDoomUiHub as g}from"@agimon-ai/doompi-core/ui-hub";const _=`start`,v=`stop`,y=`Pi session replaced.`;function b(e,n){let r=n.length>0;return{activation:r?`active`:`inactive`,condition:n.some(({state:e})=>e===`starting`||e===`stopping`)?`queued`:`ready`,...r?{detail:`${n.length} active`,color:t}:{},actions:[...e.length>0?[{id:_,enabled:!0}]:[{id:_,enabled:!1,disabledReason:`No loop launchers are registered.`}],...r?[{id:v,enabled:!0}]:[{id:v,enabled:!1,disabledReason:`No loops are active.`}]]}}function x(t){let x=!0,S,C,w,T=new WeakSet,E=e=>{let t=S;if(x&&t&&t.context.sessionManager===e.sessionManager&&t.hostSession.sessionId===e.sessionManager.getSessionId())return t},D=e=>{e.context.ui?.setStatus(a,void 0),e.context.mode!==`tui`&&e.context.ui?.setStatus(c,void 0)},O=e=>{if(!x||S!==e)return;let t=e.launchers.listInstances();e.context.ui?.setStatus(a,t.length?`loops: ${t.length}`:void 0),e.context.mode!==`tui`&&e.context.ui?.setStatus(c,l(t)),C?.publish()},k=o(t);return C=p({descriptor:{source:i,id:n,label:`Loop`,description:`Session-scoped recurring prompt loops.`,order:60,actions:[{id:_,label:`Start`,description:`Start a loop with a registered launcher.`,contexts:[`tui`,`headless`],parameters:[{name:`launcherId`,label:`Launcher`,kind:`string`,required:!0,minLength:1},{name:`instanceId`,label:`Instance ID`,kind:`string`,required:!1,minLength:1}]},{id:v,label:`Stop`,description:`Stop one active loop instance.`,contexts:[`tui`,`headless`],parameters:[{name:`instanceId`,label:`Instance ID`,kind:`string`,required:!0,minLength:1},{name:`reason`,label:`Reason`,kind:`string`,required:!1,minLength:1}]}]},state:()=>S?b(S.launchers.listLaunchers(),S.launchers.listInstances()):b([],[]),async handleAction(e,t,n,{signal:r}){r.throwIfAborted();let i=S?E(S.context):void 0;if(!i)throw Error(`Loop launchers are unavailable for the active session.`);if(t===_){let e=await i.launchers.launch(n.launcherId,n.instanceId?{instanceId:n.instanceId}:{});if(S!==i)throw Error(`Loop action became stale.`);return{message:e?`Loop '${e.instanceId}' started.`:`Loop launch was cancelled.`}}if(t===v){let e=await i.launchers.stop(n.instanceId,n.reason??`Stopped through minor_mode.`);if(S!==i)throw Error(`Loop action became stale.`);return{message:e?`Loop stopped.`:`Loop instance was not active.`}}throw Error(`Unknown loop mode action: ${t}`)}}).createOwner(void 0),{onAgentSettled:k.onAgentSettled,services:[t=>{t.inject([h],e=>{let t=u(g(e));return w=t,()=>{t(),w===t&&(w=void 0)}}),t.inject([m],async t=>{let n=t.get(m),r=s({generation:`${n.generation}:loop-launchers`,createInstanceId:()=>crypto.randomUUID(),timestamp:()=>new Date().toISOString()}),i={context:n.context,hostSession:n,launchers:r};S=i,t.provide(e,r);let a,o;try{a=k.register(n.context,r),o=r.subscribe(()=>O(i)),O(i)}catch(e){throw S===i&&(S=void 0),await r.dispose(y),e}return async()=>{S===i&&(S=void 0),o(),D(i),C?.publish();let e=[];try{await a.dispose(y)}catch(t){e.push(t)}try{await r.dispose(`Pi session shut down.`)}catch(t){e.push(t)}if(e.length>0)throw AggregateError(e,`Loop session cleanup failed.`)}})}],handlers:{async start(e,t){let n=E(e);if(n){if(T.has(n)){e.ui.notify(`A loop setup is already in progress.`,r);return}T.add(n);try{let i=n.launchers.listLaunchers();if(!i.length){e.ui.notify(`No loop launchers are registered for this session.`,r);return}let a=t.trim(),o;if(a){if(a.split(/\s+/u).length!==1){e.ui.notify(`Usage: /loop [launcherId]`,`error`);return}if(!i.some(e=>e.id===a)){e.ui.notify(`Unknown loop launcher: ${a}`,`error`);return}o=a}else if(o=await f(e,i),S!==n||!o)return;let s=await n.launchers.launch(o);if(S!==n)return;s&&e.ui.notify(`${s.label??s.launcherLabel} started.`,r)}catch(t){if(S!==n)return;e.ui.notify(`Loop could not start: ${t instanceof Error?t.message:String(t)}`,`error`)}finally{T.delete(n)}}},async list(e){let t=E(e);t&&await d(e,t.launchers)}},minorModes:[C],onDispose(){x=!1;let e=S;S=void 0,e&&D(e),C?.publish(),w?.(),w=void 0}}}export{x as createLoopPiRuntime};
2
- //# sourceMappingURL=loopRuntime.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopRuntime.mjs","names":[],"sources":["../../../src/tui/loopRuntime.ts"],"sourcesContent":["import { DOOM_CORDIS_SESSION_SERVICE, type DoomCordisSessionService } from '@agimon-ai/doompi-core/cordis-host';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-core/ui-hub';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/doompi-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport {\n ACTIVE_MODE_COLOR,\n LOOPS_GROUP_ORDER,\n MODE_STATUS_ID,\n NOTIFY_INFO_LEVEL,\n PACKAGE_SOURCE,\n STATUS_KEY,\n} from '../constants/piLoop';\nimport {\n DOOM_LOOP_LAUNCHERS_SERVICE,\n type DoomLoopLaunchersService,\n type LoopInstanceSnapshot,\n type LoopLauncherRegistration,\n type LoopLauncherSummary,\n} from '../schemas/loopLaunchers';\nimport { createDefaultLoopLauncher } from '../services/defaultLoopLauncher';\nimport { createDoomLoopLaunchersService } from '../services/loopLaunchers';\nimport { formatLoopStatusView, LOOP_VIEW_STATUS_KEY } from '../types/loopView';\nimport { registerLeaderContribution } from './leader';\nimport { openLoopListOverlay } from './loopListOverlay';\nimport { openStartLoopOverlay } from './startLoopOverlay';\n\nconst MODE_ACTION_START = 'start';\nconst MODE_ACTION_STOP = 'stop';\nconst SESSION_REPLACED_REASON = 'Pi session replaced.';\nconst SESSION_SHUTDOWN_REASON = 'Pi session shut down.';\n\ninterface ActiveLoopSession {\n readonly context: ExtensionContext;\n readonly hostSession: DoomCordisSessionService;\n readonly launchers: DoomLoopLaunchersService;\n}\n\nfunction loopModeState(\n launchers: readonly LoopLauncherSummary[],\n instances: readonly LoopInstanceSnapshot[],\n): MinorModeState {\n const active = instances.length > 0;\n return {\n activation: active ? 'active' : 'inactive',\n condition: instances.some(({ state }) => state === 'starting' || state === 'stopping') ? 'queued' : 'ready',\n ...(active ? { detail: `${instances.length} active`, color: ACTIVE_MODE_COLOR } : {}),\n actions: [\n ...(launchers.length > 0\n ? [{ id: MODE_ACTION_START, enabled: true } as const]\n : [{ id: MODE_ACTION_START, enabled: false, disabledReason: 'No loop launchers are registered.' } as const]),\n ...(active\n ? [{ id: MODE_ACTION_STOP, enabled: true } as const]\n : [{ id: MODE_ACTION_STOP, enabled: false, disabledReason: 'No loops are active.' } as const]),\n ],\n };\n}\n\nimport type { LoopCommandHandlers } from '../types/loopCommand';\ninterface LoopPiRuntime {\n onAgentSettled(this: void): void;\n services: Array<(context: Context) => void>;\n handlers: LoopCommandHandlers;\n minorModes: MinorModeOwner[];\n onDispose(): void;\n}\n/** Install Loop state and external resources into its host-owned Cordis plugin fiber. */\nexport function createLoopPiRuntime(pi: ExtensionAPI): LoopPiRuntime {\n let active = true;\n let activeSession: ActiveLoopSession | undefined;\n let mode: MinorModeOwner | undefined;\n let disposeLeader: (() => void) | undefined;\n const launchesInProgress = new WeakSet<ActiveLoopSession>();\n\n const currentSession = (context: ExtensionContext): ActiveLoopSession | undefined => {\n const binding = activeSession;\n if (\n !active ||\n !binding ||\n binding.context.sessionManager !== context.sessionManager ||\n binding.hostSession.sessionId !== context.sessionManager.getSessionId()\n ) {\n return undefined;\n }\n return binding;\n };\n\n const clearStatuses = (binding: ActiveLoopSession): void => {\n binding.context.ui?.setStatus(STATUS_KEY, undefined);\n if (binding.context.mode !== 'tui') binding.context.ui?.setStatus(LOOP_VIEW_STATUS_KEY, undefined);\n };\n const publishStatus = (binding: ActiveLoopSession): void => {\n if (!active || activeSession !== binding) return;\n const instances = binding.launchers.listInstances();\n binding.context.ui?.setStatus(STATUS_KEY, instances.length ? `loops: ${instances.length}` : undefined);\n if (binding.context.mode !== 'tui') {\n binding.context.ui?.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(instances));\n }\n mode?.publish();\n };\n\n const defaultLauncher = createDefaultLoopLauncher(pi);\n\n mode = defineMinorMode({\n descriptor: {\n source: PACKAGE_SOURCE,\n id: MODE_STATUS_ID,\n label: 'Loop',\n description: 'Session-scoped recurring prompt loops.',\n order: LOOPS_GROUP_ORDER,\n actions: [\n {\n id: MODE_ACTION_START,\n label: 'Start',\n description: 'Start a loop with a registered launcher.',\n contexts: ['tui', 'headless'],\n parameters: [\n { name: 'launcherId', label: 'Launcher', kind: 'string', required: true, minLength: 1 },\n { name: 'instanceId', label: 'Instance ID', kind: 'string', required: false, minLength: 1 },\n ],\n },\n {\n id: MODE_ACTION_STOP,\n label: 'Stop',\n description: 'Stop one active loop instance.',\n contexts: ['tui', 'headless'],\n parameters: [\n { name: 'instanceId', label: 'Instance ID', kind: 'string', required: true, minLength: 1 },\n { name: 'reason', label: 'Reason', kind: 'string', required: false, minLength: 1 },\n ],\n },\n ],\n },\n state: () =>\n activeSession\n ? loopModeState(activeSession.launchers.listLaunchers(), activeSession.launchers.listInstances())\n : loopModeState([], []),\n async handleAction(_runtime, actionId, argumentsValue, { signal }) {\n signal.throwIfAborted();\n const binding = activeSession ? currentSession(activeSession.context) : undefined;\n if (!binding) throw new Error('Loop launchers are unavailable for the active session.');\n if (actionId === MODE_ACTION_START) {\n const instance = await binding.launchers.launch(\n argumentsValue.launcherId as string,\n argumentsValue.instanceId ? { instanceId: argumentsValue.instanceId as string } : {},\n );\n if (activeSession !== binding) throw new Error('Loop action became stale.');\n return { message: instance ? `Loop '${instance.instanceId}' started.` : 'Loop launch was cancelled.' };\n }\n if (actionId === MODE_ACTION_STOP) {\n const stopped = await binding.launchers.stop(\n argumentsValue.instanceId as string,\n (argumentsValue.reason as string | undefined) ?? 'Stopped through minor_mode.',\n );\n if (activeSession !== binding) throw new Error('Loop action became stale.');\n return { message: stopped ? 'Loop stopped.' : 'Loop instance was not active.' };\n }\n throw new Error(`Unknown loop mode action: ${actionId}`);\n },\n }).createOwner(undefined);\n return {\n onAgentSettled: defaultLauncher.onAgentSettled,\n services: [\n (cordis: Context) => {\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const dispose = registerLeaderContribution(requireDoomUiHub(uiContext));\n disposeLeader = dispose;\n return () => {\n dispose();\n if (disposeLeader === dispose) disposeLeader = undefined;\n };\n });\n\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE], async (sessionContext) => {\n const hostSession = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const launchers = createDoomLoopLaunchersService({\n generation: `${hostSession.generation}:loop-launchers`,\n createInstanceId: () => crypto.randomUUID(),\n timestamp: () => new Date().toISOString(),\n });\n const binding: ActiveLoopSession = { context: hostSession.context, hostSession, launchers };\n activeSession = binding;\n sessionContext.provide(DOOM_LOOP_LAUNCHERS_SERVICE, launchers);\n\n let defaultRegistration: LoopLauncherRegistration;\n let unsubscribe: () => void;\n try {\n defaultRegistration = defaultLauncher.register(hostSession.context, launchers);\n unsubscribe = launchers.subscribe(() => publishStatus(binding));\n publishStatus(binding);\n } catch (error) {\n if (activeSession === binding) activeSession = undefined;\n await launchers.dispose(SESSION_REPLACED_REASON);\n throw error;\n }\n\n return async () => {\n if (activeSession === binding) activeSession = undefined;\n unsubscribe();\n clearStatuses(binding);\n mode?.publish();\n const cleanupErrors: unknown[] = [];\n try {\n await defaultRegistration.dispose(SESSION_REPLACED_REASON);\n } catch (error) {\n cleanupErrors.push(error);\n }\n try {\n await launchers.dispose(SESSION_SHUTDOWN_REASON);\n } catch (error) {\n cleanupErrors.push(error);\n }\n if (cleanupErrors.length > 0) throw new AggregateError(cleanupErrors, 'Loop session cleanup failed.');\n };\n });\n },\n ],\n handlers: {\n async start(ctx, args) {\n const binding = currentSession(ctx);\n if (!binding) return;\n if (launchesInProgress.has(binding)) {\n ctx.ui.notify('A loop setup is already in progress.', NOTIFY_INFO_LEVEL);\n return;\n }\n launchesInProgress.add(binding);\n try {\n const launchers = binding.launchers.listLaunchers();\n if (!launchers.length) {\n ctx.ui.notify('No loop launchers are registered for this session.', NOTIFY_INFO_LEVEL);\n return;\n }\n\n const argument = args.trim();\n let launcherId: string | undefined;\n if (argument) {\n if (argument.split(/\\s+/u).length !== 1) {\n ctx.ui.notify('Usage: /loop [launcherId]', 'error');\n return;\n }\n if (!launchers.some((launcher) => launcher.id === argument)) {\n ctx.ui.notify(`Unknown loop launcher: ${argument}`, 'error');\n return;\n }\n launcherId = argument;\n } else {\n launcherId = await openStartLoopOverlay(ctx, launchers);\n if (activeSession !== binding || !launcherId) return;\n }\n\n const instance = await binding.launchers.launch(launcherId);\n if (activeSession !== binding) return;\n if (instance) ctx.ui.notify(`${instance.label ?? instance.launcherLabel} started.`, NOTIFY_INFO_LEVEL);\n } catch (error) {\n if (activeSession !== binding) return;\n ctx.ui.notify(`Loop could not start: ${error instanceof Error ? error.message : String(error)}`, 'error');\n } finally {\n launchesInProgress.delete(binding);\n }\n },\n async list(ctx) {\n const binding = currentSession(ctx);\n if (!binding) return;\n await openLoopListOverlay(ctx, binding.launchers);\n },\n },\n minorModes: [mode],\n onDispose() {\n active = false;\n const binding = activeSession;\n activeSession = undefined;\n if (binding) clearStatuses(binding);\n mode?.publish();\n disposeLeader?.();\n disposeLeader = undefined;\n },\n };\n}\n"],"mappings":"s3BA4BA,MAAM,EAAoB,QACpB,EAAmB,OACnB,EAA0B,uBAShC,SAAS,EACP,EACA,EACgB,CAChB,IAAM,EAAS,EAAU,OAAS,EAClC,MAAO,CACL,WAAY,EAAS,SAAW,WAChC,UAAW,EAAU,MAAM,CAAE,WAAY,IAAU,YAAc,IAAU,UAAU,EAAI,SAAW,QACpG,GAAI,EAAS,CAAE,OAAQ,GAAG,EAAU,OAAO,SAAU,MAAO,CAAkB,EAAI,CAAC,EACnF,QAAS,CACP,GAAI,EAAU,OAAS,EACnB,CAAC,CAAE,GAAI,EAAmB,QAAS,EAAK,CAAU,EAClD,CAAC,CAAE,GAAI,EAAmB,QAAS,GAAO,eAAgB,mCAAoC,CAAU,EAC5G,GAAI,EACA,CAAC,CAAE,GAAI,EAAkB,QAAS,EAAK,CAAU,EACjD,CAAC,CAAE,GAAI,EAAkB,QAAS,GAAO,eAAgB,sBAAuB,CAAU,CAChG,CACF,CACF,CAWA,SAAgB,EAAoB,EAAiC,CACnE,IAAI,EAAS,GACT,EACA,EACA,EACE,EAAqB,IAAI,QAEzB,EAAkB,GAA6D,CACnF,IAAM,EAAU,EAEd,GAAC,GACA,GACD,EAAQ,QAAQ,iBAAmB,EAAQ,gBAC3C,EAAQ,YAAY,YAAc,EAAQ,eAAe,aAAa,EAIxE,OAAO,CACT,EAEM,EAAiB,GAAqC,CAC1D,EAAQ,QAAQ,IAAI,UAAU,EAAY,IAAA,EAAS,EAC/C,EAAQ,QAAQ,OAAS,OAAO,EAAQ,QAAQ,IAAI,UAAU,EAAsB,IAAA,EAAS,CACnG,EACM,EAAiB,GAAqC,CAC1D,GAAI,CAAC,GAAU,IAAkB,EAAS,OAC1C,IAAM,EAAY,EAAQ,UAAU,cAAc,EAClD,EAAQ,QAAQ,IAAI,UAAU,EAAY,EAAU,OAAS,UAAU,EAAU,SAAW,IAAA,EAAS,EACjG,EAAQ,QAAQ,OAAS,OAC3B,EAAQ,QAAQ,IAAI,UAAU,EAAsB,EAAqB,CAAS,CAAC,EAErF,GAAM,QAAQ,CAChB,EAEM,EAAkB,EAA0B,CAAE,EA2DpD,MAzDA,GAAO,EAAgB,CACrB,WAAY,CACV,OAAQ,EACR,GAAI,EACJ,MAAO,OACP,YAAa,yCACb,MAAA,GACA,QAAS,CACP,CACE,GAAI,EACJ,MAAO,QACP,YAAa,2CACb,SAAU,CAAC,MAAO,UAAU,EAC5B,WAAY,CACV,CAAE,KAAM,aAAc,MAAO,WAAY,KAAM,SAAU,SAAU,GAAM,UAAW,CAAE,EACtF,CAAE,KAAM,aAAc,MAAO,cAAe,KAAM,SAAU,SAAU,GAAO,UAAW,CAAE,CAC5F,CACF,EACA,CACE,GAAI,EACJ,MAAO,OACP,YAAa,iCACb,SAAU,CAAC,MAAO,UAAU,EAC5B,WAAY,CACV,CAAE,KAAM,aAAc,MAAO,cAAe,KAAM,SAAU,SAAU,GAAM,UAAW,CAAE,EACzF,CAAE,KAAM,SAAU,MAAO,SAAU,KAAM,SAAU,SAAU,GAAO,UAAW,CAAE,CACnF,CACF,CACF,CACF,EACA,UACE,EACI,EAAc,EAAc,UAAU,cAAc,EAAG,EAAc,UAAU,cAAc,CAAC,EAC9F,EAAc,CAAC,EAAG,CAAC,CAAC,EAC1B,MAAM,aAAa,EAAU,EAAU,EAAgB,CAAE,UAAU,CACjE,EAAO,eAAe,EACtB,IAAM,EAAU,EAAgB,EAAe,EAAc,OAAO,EAAI,IAAA,GACxE,GAAI,CAAC,EAAS,MAAU,MAAM,wDAAwD,EACtF,GAAI,IAAa,EAAmB,CAClC,IAAM,EAAW,MAAM,EAAQ,UAAU,OACvC,EAAe,WACf,EAAe,WAAa,CAAE,WAAY,EAAe,UAAqB,EAAI,CAAC,CACrF,EACA,GAAI,IAAkB,EAAS,MAAU,MAAM,2BAA2B,EAC1E,MAAO,CAAE,QAAS,EAAW,SAAS,EAAS,WAAW,YAAc,4BAA6B,CACvG,CACA,GAAI,IAAa,EAAkB,CACjC,IAAM,EAAU,MAAM,EAAQ,UAAU,KACtC,EAAe,WACd,EAAe,QAAiC,6BACnD,EACA,GAAI,IAAkB,EAAS,MAAU,MAAM,2BAA2B,EAC1E,MAAO,CAAE,QAAS,EAAU,gBAAkB,+BAAgC,CAChF,CACA,MAAU,MAAM,6BAA6B,GAAU,CACzD,CACF,CAAC,CAAC,CAAC,YAAY,IAAA,EAAS,EACjB,CACL,eAAgB,EAAgB,eAChC,SAAU,CACP,GAAoB,CACnB,EAAO,OAAO,CAAC,CAAmB,EAAI,GAAc,CAClD,IAAM,EAAU,EAA2B,EAAiB,CAAS,CAAC,EAEtE,MADA,GAAgB,MACH,CACX,EAAQ,EACJ,IAAkB,IAAS,EAAgB,IAAA,GACjD,CACF,CAAC,EAED,EAAO,OAAO,CAAC,CAA2B,EAAG,KAAO,IAAmB,CACrE,IAAM,EAAc,EAAe,IAAI,CAA2B,EAC5D,EAAY,EAA+B,CAC/C,WAAY,GAAG,EAAY,WAAW,iBACtC,qBAAwB,OAAO,WAAW,EAC1C,cAAiB,IAAI,KAAK,CAAA,CAAE,YAAY,CAC1C,CAAC,EACK,EAA6B,CAAE,QAAS,EAAY,QAAS,cAAa,WAAU,EAC1F,EAAgB,EAChB,EAAe,QAAQ,EAA6B,CAAS,EAE7D,IAAI,EACA,EACJ,GAAI,CACF,EAAsB,EAAgB,SAAS,EAAY,QAAS,CAAS,EAC7E,EAAc,EAAU,cAAgB,EAAc,CAAO,CAAC,EAC9D,EAAc,CAAO,CACvB,OAAS,EAAO,CAGd,MAFI,IAAkB,IAAS,EAAgB,IAAA,IAC/C,MAAM,EAAU,QAAQ,CAAuB,EACzC,CACR,CAEA,OAAO,SAAY,CACb,IAAkB,IAAS,EAAgB,IAAA,IAC/C,EAAY,EACZ,EAAc,CAAO,EACrB,GAAM,QAAQ,EACd,IAAM,EAA2B,CAAC,EAClC,GAAI,CACF,MAAM,EAAoB,QAAQ,CAAuB,CAC3D,OAAS,EAAO,CACd,EAAc,KAAK,CAAK,CAC1B,CACA,GAAI,CACF,MAAM,EAAU,QAAQ,uBAAuB,CACjD,OAAS,EAAO,CACd,EAAc,KAAK,CAAK,CAC1B,CACA,GAAI,EAAc,OAAS,EAAG,MAAU,eAAe,EAAe,8BAA8B,CACtG,CACF,CAAC,CACH,CACF,EACA,SAAU,CACR,MAAM,MAAM,EAAK,EAAM,CACrB,IAAM,EAAU,EAAe,CAAG,EAC7B,KACL,IAAI,EAAmB,IAAI,CAAO,EAAG,CACnC,EAAI,GAAG,OAAO,uCAAwC,CAAiB,EACvE,MACF,CACA,EAAmB,IAAI,CAAO,EAC9B,GAAI,CACF,IAAM,EAAY,EAAQ,UAAU,cAAc,EAClD,GAAI,CAAC,EAAU,OAAQ,CACrB,EAAI,GAAG,OAAO,qDAAsD,CAAiB,EACrF,MACF,CAEA,IAAM,EAAW,EAAK,KAAK,EACvB,EACJ,GAAI,EAAU,CACZ,GAAI,EAAS,MAAM,MAAM,CAAC,CAAC,SAAW,EAAG,CACvC,EAAI,GAAG,OAAO,4BAA6B,OAAO,EAClD,MACF,CACA,GAAI,CAAC,EAAU,KAAM,GAAa,EAAS,KAAO,CAAQ,EAAG,CAC3D,EAAI,GAAG,OAAO,0BAA0B,IAAY,OAAO,EAC3D,MACF,CACA,EAAa,CACf,MAEE,GADA,EAAa,MAAM,EAAqB,EAAK,CAAS,EAClD,IAAkB,GAAW,CAAC,EAAY,OAGhD,IAAM,EAAW,MAAM,EAAQ,UAAU,OAAO,CAAU,EAC1D,GAAI,IAAkB,EAAS,OAC3B,GAAU,EAAI,GAAG,OAAO,GAAG,EAAS,OAAS,EAAS,cAAc,WAAY,CAAiB,CACvG,OAAS,EAAO,CACd,GAAI,IAAkB,EAAS,OAC/B,EAAI,GAAG,OAAO,yBAAyB,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,IAAK,OAAO,CAC1G,QAAU,CACR,EAAmB,OAAO,CAAO,CACnC,CAlCA,CAmCF,EACA,MAAM,KAAK,EAAK,CACd,IAAM,EAAU,EAAe,CAAG,EAC7B,GACL,MAAM,EAAoB,EAAK,EAAQ,SAAS,CAClD,CACF,EACA,WAAY,CAAC,CAAI,EACjB,WAAY,CACV,EAAS,GACT,IAAM,EAAU,EAChB,EAAgB,IAAA,GACZ,GAAS,EAAc,CAAO,EAClC,GAAM,QAAQ,EACd,IAAgB,EAChB,EAAgB,IAAA,EAClB,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- let e=require("@agimon-ai/doompi-ui/doom-overlay"),t=require("@earendil-works/pi-tui");var n=class extends e.DoomOverlay{launchers;done;selected=0;constructor(e,t,n,r){super(e,t),this.launchers=n,this.done=r}handleInput(e){if((0,t.matchesKey)(e,`escape`)||(0,t.matchesKey)(e,`ctrl+c`)||e===`q`){this.done(void 0);return}if((0,t.matchesKey)(e,`up`)||e===`k`){this.move(-1);return}if((0,t.matchesKey)(e,`down`)||e===`j`){this.move(1);return}(0,t.matchesKey)(e,`enter`)&&this.done(this.launchers[this.selected]?.id)}getChrome(){return{title:`START LOOP`,breadcrumb:`SPC › l / loops › s / start`,footer:`${e.DOOM_NAVIGATION_KEYS.list} move · enter launch · esc close`,footerHints:[[e.DOOM_NAVIGATION_KEYS.list,`move`],[`enter`,`launch`],[`esc`,`close`]],accent:e.DOOM_OVERLAY_ACCENT}}renderBody(e){return this.launchers.length?this.launchers.flatMap((n,r)=>{let i=r===this.selected,a=i?this.theme.fg(`accent`,`▸`):` `,o=i?this.theme.bold(n.label):n.label,s=[`${a} ${(0,t.truncateToWidth)(o,Math.max(0,e-2))}`];return n.description&&s.push(` ${this.theme.fg(`dim`,(0,t.truncateToWidth)(n.description,Math.max(0,e-4)))}`),s}):[this.theme.fg(`dim`,`No loop launchers are registered for this session.`)]}move(e){this.launchers.length&&(this.selected=Math.max(0,Math.min(this.selected+e,this.launchers.length-1)),this.invalidate())}};async function r(t,r){if(t.mode!==`tui`){let e=r.map(e=>e.label),n=await t.ui.select(`Start loop`,e);return r[e.indexOf(n??``)]?.id}return t.ui.custom((e,t,i,a)=>new n(e,t,r,a),e.DOOM_FULLSCREEN_UI_OPTIONS)}exports.StartLoopOverlay=n,exports.openStartLoopOverlay=r;
2
- //# sourceMappingURL=startLoopOverlay.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"startLoopOverlay.cjs","names":["DoomOverlay","matchesKey","DOOM_NAVIGATION_KEYS","DOOM_OVERLAY_ACCENT","truncateToWidth","DOOM_FULLSCREEN_UI_OPTIONS"],"sources":["../../../src/tui/startLoopOverlay.ts"],"sourcesContent":["import {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_NAVIGATION_KEYS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/doom-overlay';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { LoopLauncherSummary } from '../schemas/loopLaunchers';\n\nconst EMPTY_MESSAGE = 'No loop launchers are registered for this session.';\n\nexport class StartLoopOverlay extends DoomOverlay {\n private selected = 0;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly launchers: readonly LoopLauncherSummary[],\n private readonly done: (result: string | undefined) => void,\n ) {\n super(tui, theme);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c') || data === 'q') {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down') || data === 'j') {\n this.move(1);\n return;\n }\n if (matchesKey(data, 'enter')) this.done(this.launchers[this.selected]?.id);\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: 'START LOOP',\n breadcrumb: 'SPC › l / loops › s / start',\n footer: `${DOOM_NAVIGATION_KEYS.list} move · enter launch · esc close`,\n footerHints: [\n [DOOM_NAVIGATION_KEYS.list, 'move'],\n ['enter', 'launch'],\n ['esc', 'close'],\n ],\n accent: DOOM_OVERLAY_ACCENT,\n };\n }\n\n protected renderBody(width: number): string[] {\n if (!this.launchers.length) return [this.theme.fg('dim', EMPTY_MESSAGE)];\n return this.launchers.flatMap((launcher, index) => {\n const selected = index === this.selected;\n const marker = selected ? this.theme.fg('accent', '▸') : ' ';\n const label = selected ? this.theme.bold(launcher.label) : launcher.label;\n const lines = [`${marker} ${truncateToWidth(label, Math.max(0, width - 2))}`];\n if (launcher.description)\n lines.push(` ${this.theme.fg('dim', truncateToWidth(launcher.description, Math.max(0, width - 4)))}`);\n return lines;\n });\n }\n\n private move(delta: number): void {\n if (!this.launchers.length) return;\n this.selected = Math.max(0, Math.min(this.selected + delta, this.launchers.length - 1));\n this.invalidate();\n }\n}\n\nexport async function openStartLoopOverlay(\n ctx: ExtensionContext,\n launchers: readonly LoopLauncherSummary[],\n): Promise<string | undefined> {\n if (ctx.mode !== 'tui') {\n const labels = launchers.map((launcher) => launcher.label);\n const selected = await ctx.ui.select('Start loop', labels);\n return launchers[labels.indexOf(selected ?? '')]?.id;\n }\n return ctx.ui.custom<string | undefined>(\n (tui, theme, _keybindings, done) => new StartLoopOverlay(tui, theme, launchers, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"uFAeA,IAAa,EAAb,cAAsCA,EAAAA,WAAY,CAM7B,UACA,KANnB,SAAmB,EAEnB,YACE,EACA,EACA,EACA,EACA,CACA,MAAM,EAAK,CAAK,EAHC,KAAA,UAAA,EACA,KAAA,KAAA,CAGnB,CAEA,YAAY,EAAoB,CAC9B,IAAA,EAAIC,EAAAA,WAAAA,CAAW,EAAM,QAAQ,IAAA,EAAKA,EAAAA,WAAAA,CAAW,EAAM,QAAQ,GAAK,IAAS,IAAK,CAC5E,KAAK,KAAK,IAAA,EAAS,EACnB,MACF,CACA,IAAA,EAAIA,EAAAA,WAAAA,CAAW,EAAM,IAAI,GAAK,IAAS,IAAK,CAC1C,KAAK,KAAK,EAAE,EACZ,MACF,CACA,IAAA,EAAIA,EAAAA,WAAAA,CAAW,EAAM,MAAM,GAAK,IAAS,IAAK,CAC5C,KAAK,KAAK,CAAC,EACX,MACF,EACA,EAAIA,EAAAA,WAAAA,CAAW,EAAM,OAAO,GAAG,KAAK,KAAK,KAAK,UAAU,KAAK,SAAS,EAAE,EAAE,CAC5E,CAEA,WAAyC,CACvC,MAAO,CACL,MAAO,aACP,WAAY,8BACZ,OAAQ,GAAGC,EAAAA,qBAAqB,KAAK,kCACrC,YAAa,CACX,CAACA,EAAAA,qBAAqB,KAAM,MAAM,EAClC,CAAC,QAAS,QAAQ,EAClB,CAAC,MAAO,OAAO,CACjB,EACA,OAAQC,EAAAA,mBACV,CACF,CAEA,WAAqB,EAAyB,CAE5C,OADK,KAAK,UAAU,OACb,KAAK,UAAU,SAAS,EAAU,IAAU,CACjD,IAAM,EAAW,IAAU,KAAK,SAC1B,EAAS,EAAW,KAAK,MAAM,GAAG,SAAU,GAAG,EAAI,IACnD,EAAQ,EAAW,KAAK,MAAM,KAAK,EAAS,KAAK,EAAI,EAAS,MAC9D,EAAQ,CAAC,GAAG,EAAO,IAAA,EAAGC,EAAAA,gBAAAA,CAAgB,EAAO,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,GAAG,EAG5E,OAFI,EAAS,aACX,EAAM,KAAK,OAAO,KAAK,MAAM,GAAG,OAAA,EAAOA,EAAAA,gBAAAA,CAAgB,EAAS,YAAa,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,CAAC,GAAG,EAClG,CACT,CAAC,EATkC,CAAC,KAAK,MAAM,GAAG,MAAO,oDAAa,CAAC,CAUzE,CAEA,KAAa,EAAqB,CAC3B,KAAK,UAAU,SACpB,KAAK,SAAW,KAAK,IAAI,EAAG,KAAK,IAAI,KAAK,SAAW,EAAO,KAAK,UAAU,OAAS,CAAC,CAAC,EACtF,KAAK,WAAW,EAClB,CACF,EAEA,eAAsB,EACpB,EACA,EAC6B,CAC7B,GAAI,EAAI,OAAS,MAAO,CACtB,IAAM,EAAS,EAAU,IAAK,GAAa,EAAS,KAAK,EACnD,EAAW,MAAM,EAAI,GAAG,OAAO,aAAc,CAAM,EACzD,OAAO,EAAU,EAAO,QAAQ,GAAY,EAAE,EAAE,EAAE,EACpD,CACA,OAAO,EAAI,GAAG,QACX,EAAK,EAAO,EAAc,IAAS,IAAI,EAAiB,EAAK,EAAO,EAAW,CAAI,EACpFC,EAAAA,0BACF,CACF"}
@@ -1,2 +0,0 @@
1
- import{DOOM_FULLSCREEN_UI_OPTIONS as e,DOOM_NAVIGATION_KEYS as t,DOOM_OVERLAY_ACCENT as n,DoomOverlay as r}from"@agimon-ai/doompi-ui/doom-overlay";import{matchesKey as i,truncateToWidth as a}from"@earendil-works/pi-tui";var o=class extends r{launchers;done;selected=0;constructor(e,t,n,r){super(e,t),this.launchers=n,this.done=r}handleInput(e){if(i(e,`escape`)||i(e,`ctrl+c`)||e===`q`){this.done(void 0);return}if(i(e,`up`)||e===`k`){this.move(-1);return}if(i(e,`down`)||e===`j`){this.move(1);return}i(e,`enter`)&&this.done(this.launchers[this.selected]?.id)}getChrome(){return{title:`START LOOP`,breadcrumb:`SPC › l / loops › s / start`,footer:`${t.list} move · enter launch · esc close`,footerHints:[[t.list,`move`],[`enter`,`launch`],[`esc`,`close`]],accent:n}}renderBody(e){return this.launchers.length?this.launchers.flatMap((t,n)=>{let r=n===this.selected,i=r?this.theme.fg(`accent`,`▸`):` `,o=r?this.theme.bold(t.label):t.label,s=[`${i} ${a(o,Math.max(0,e-2))}`];return t.description&&s.push(` ${this.theme.fg(`dim`,a(t.description,Math.max(0,e-4)))}`),s}):[this.theme.fg(`dim`,`No loop launchers are registered for this session.`)]}move(e){this.launchers.length&&(this.selected=Math.max(0,Math.min(this.selected+e,this.launchers.length-1)),this.invalidate())}};async function s(t,n){if(t.mode!==`tui`){let e=n.map(e=>e.label),r=await t.ui.select(`Start loop`,e);return n[e.indexOf(r??``)]?.id}return t.ui.custom((e,t,r,i)=>new o(e,t,n,i),e)}export{o as StartLoopOverlay,s as openStartLoopOverlay};
2
- //# sourceMappingURL=startLoopOverlay.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"startLoopOverlay.mjs","names":[],"sources":["../../../src/tui/startLoopOverlay.ts"],"sourcesContent":["import {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_NAVIGATION_KEYS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/doom-overlay';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { LoopLauncherSummary } from '../schemas/loopLaunchers';\n\nconst EMPTY_MESSAGE = 'No loop launchers are registered for this session.';\n\nexport class StartLoopOverlay extends DoomOverlay {\n private selected = 0;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly launchers: readonly LoopLauncherSummary[],\n private readonly done: (result: string | undefined) => void,\n ) {\n super(tui, theme);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c') || data === 'q') {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down') || data === 'j') {\n this.move(1);\n return;\n }\n if (matchesKey(data, 'enter')) this.done(this.launchers[this.selected]?.id);\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: 'START LOOP',\n breadcrumb: 'SPC › l / loops › s / start',\n footer: `${DOOM_NAVIGATION_KEYS.list} move · enter launch · esc close`,\n footerHints: [\n [DOOM_NAVIGATION_KEYS.list, 'move'],\n ['enter', 'launch'],\n ['esc', 'close'],\n ],\n accent: DOOM_OVERLAY_ACCENT,\n };\n }\n\n protected renderBody(width: number): string[] {\n if (!this.launchers.length) return [this.theme.fg('dim', EMPTY_MESSAGE)];\n return this.launchers.flatMap((launcher, index) => {\n const selected = index === this.selected;\n const marker = selected ? this.theme.fg('accent', '▸') : ' ';\n const label = selected ? this.theme.bold(launcher.label) : launcher.label;\n const lines = [`${marker} ${truncateToWidth(label, Math.max(0, width - 2))}`];\n if (launcher.description)\n lines.push(` ${this.theme.fg('dim', truncateToWidth(launcher.description, Math.max(0, width - 4)))}`);\n return lines;\n });\n }\n\n private move(delta: number): void {\n if (!this.launchers.length) return;\n this.selected = Math.max(0, Math.min(this.selected + delta, this.launchers.length - 1));\n this.invalidate();\n }\n}\n\nexport async function openStartLoopOverlay(\n ctx: ExtensionContext,\n launchers: readonly LoopLauncherSummary[],\n): Promise<string | undefined> {\n if (ctx.mode !== 'tui') {\n const labels = launchers.map((launcher) => launcher.label);\n const selected = await ctx.ui.select('Start loop', labels);\n return launchers[labels.indexOf(selected ?? '')]?.id;\n }\n return ctx.ui.custom<string | undefined>(\n (tui, theme, _keybindings, done) => new StartLoopOverlay(tui, theme, launchers, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"4NAeA,IAAa,EAAb,cAAsC,CAAY,CAM7B,UACA,KANnB,SAAmB,EAEnB,YACE,EACA,EACA,EACA,EACA,CACA,MAAM,EAAK,CAAK,EAHC,KAAA,UAAA,EACA,KAAA,KAAA,CAGnB,CAEA,YAAY,EAAoB,CAC9B,GAAI,EAAW,EAAM,QAAQ,GAAK,EAAW,EAAM,QAAQ,GAAK,IAAS,IAAK,CAC5E,KAAK,KAAK,IAAA,EAAS,EACnB,MACF,CACA,GAAI,EAAW,EAAM,IAAI,GAAK,IAAS,IAAK,CAC1C,KAAK,KAAK,EAAE,EACZ,MACF,CACA,GAAI,EAAW,EAAM,MAAM,GAAK,IAAS,IAAK,CAC5C,KAAK,KAAK,CAAC,EACX,MACF,CACI,EAAW,EAAM,OAAO,GAAG,KAAK,KAAK,KAAK,UAAU,KAAK,SAAS,EAAE,EAAE,CAC5E,CAEA,WAAyC,CACvC,MAAO,CACL,MAAO,aACP,WAAY,8BACZ,OAAQ,GAAG,EAAqB,KAAK,kCACrC,YAAa,CACX,CAAC,EAAqB,KAAM,MAAM,EAClC,CAAC,QAAS,QAAQ,EAClB,CAAC,MAAO,OAAO,CACjB,EACA,OAAQ,CACV,CACF,CAEA,WAAqB,EAAyB,CAE5C,OADK,KAAK,UAAU,OACb,KAAK,UAAU,SAAS,EAAU,IAAU,CACjD,IAAM,EAAW,IAAU,KAAK,SAC1B,EAAS,EAAW,KAAK,MAAM,GAAG,SAAU,GAAG,EAAI,IACnD,EAAQ,EAAW,KAAK,MAAM,KAAK,EAAS,KAAK,EAAI,EAAS,MAC9D,EAAQ,CAAC,GAAG,EAAO,GAAG,EAAgB,EAAO,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,GAAG,EAG5E,OAFI,EAAS,aACX,EAAM,KAAK,OAAO,KAAK,MAAM,GAAG,MAAO,EAAgB,EAAS,YAAa,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,CAAC,GAAG,EAClG,CACT,CAAC,EATkC,CAAC,KAAK,MAAM,GAAG,MAAO,oDAAa,CAAC,CAUzE,CAEA,KAAa,EAAqB,CAC3B,KAAK,UAAU,SACpB,KAAK,SAAW,KAAK,IAAI,EAAG,KAAK,IAAI,KAAK,SAAW,EAAO,KAAK,UAAU,OAAS,CAAC,CAAC,EACtF,KAAK,WAAW,EAClB,CACF,EAEA,eAAsB,EACpB,EACA,EAC6B,CAC7B,GAAI,EAAI,OAAS,MAAO,CACtB,IAAM,EAAS,EAAU,IAAK,GAAa,EAAS,KAAK,EACnD,EAAW,MAAM,EAAI,GAAG,OAAO,aAAc,CAAM,EACzD,OAAO,EAAU,EAAO,QAAQ,GAAY,EAAE,EAAE,EAAE,EACpD,CACA,OAAO,EAAI,GAAG,QACX,EAAK,EAAO,EAAc,IAAS,IAAI,EAAiB,EAAK,EAAO,EAAW,CAAI,EACpF,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- new RegExp(String.raw`\u001b\[[0-9;]*m`,`gu`);function e(e){if(e.length!==0)return JSON.stringify(e.map(e=>({instanceId:e.instanceId,label:e.label??e.launcherLabel,detail:e.detail??e.instanceId,state:e.state})))}exports.LOOP_VIEW_STATUS_KEY=`doom-loop-instances`,exports.formatLoopStatusView=e;
2
- //# sourceMappingURL=loopView.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopView.cjs","names":[],"sources":["../../../src/types/loopView.ts"],"sourcesContent":["/** The browser-only status that carries active Loop instances into the activity dock. */\nexport const LOOP_VIEW_STATUS_KEY = 'doom-loop-instances';\n\nexport type LoopStatusState = 'starting' | 'running' | 'stopping';\n\n/** The small, serializable Loop view the cockpit needs for one activity row. */\nexport interface LoopStatusItem {\n readonly instanceId: string;\n readonly label: string;\n readonly detail: string;\n readonly state: LoopStatusState;\n}\n\ninterface LoopStatusSource {\n readonly instanceId: string;\n readonly launcherLabel: string;\n readonly label?: string;\n readonly detail?: string;\n readonly state: LoopStatusState;\n}\n\nconst ANSI = new RegExp(String.raw`\\u001b\\[[0-9;]*m`, 'gu');\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isState(value: unknown): value is LoopStatusState {\n return value === 'starting' || value === 'running' || value === 'stopping';\n}\n\n/** Projects runtime snapshots into the JSON status consumed by the web plugin. */\nexport function formatLoopStatusView(instances: readonly LoopStatusSource[]): string | undefined {\n if (instances.length === 0) return undefined;\n return JSON.stringify(\n instances.map((instance) => ({\n instanceId: instance.instanceId,\n label: instance.label ?? instance.launcherLabel,\n detail: instance.detail ?? instance.instanceId,\n state: instance.state,\n })),\n );\n}\n\n/** Reads a Loop status without letting malformed session data crash the activity dock. */\nexport function parseLoopStatusView(raw: string | undefined): LoopStatusItem[] | undefined {\n if (raw === undefined) return undefined;\n const text = raw.replace(ANSI, '').trim();\n if (text === '') return undefined;\n\n try {\n const value: unknown = JSON.parse(text);\n if (!Array.isArray(value) || value.length === 0) return undefined;\n const items: LoopStatusItem[] = [];\n for (const item of value) {\n if (\n !isRecord(item) ||\n typeof item.instanceId !== 'string' ||\n item.instanceId.trim() === '' ||\n typeof item.label !== 'string' ||\n item.label.trim() === '' ||\n typeof item.detail !== 'string' ||\n !isState(item.state)\n ) {\n return undefined;\n }\n items.push({ instanceId: item.instanceId, label: item.label, detail: item.detail, state: item.state });\n }\n return items;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"AAqBa,IAAI,OAAO,OAAO,GAAG,mBAAoB,IAAI,EAW1D,SAAgB,EAAqB,EAA4D,CAC3F,KAAU,SAAW,EACzB,OAAO,KAAK,UACV,EAAU,IAAK,IAAc,CAC3B,WAAY,EAAS,WACrB,MAAO,EAAS,OAAS,EAAS,cAClC,OAAQ,EAAS,QAAU,EAAS,WACpC,MAAO,EAAS,KAClB,EAAE,CACJ,CACF"}
@@ -1,2 +0,0 @@
1
- const e=`doom-loop-instances`;new RegExp(String.raw`\u001b\[[0-9;]*m`,`gu`);function t(e){if(e.length!==0)return JSON.stringify(e.map(e=>({instanceId:e.instanceId,label:e.label??e.launcherLabel,detail:e.detail??e.instanceId,state:e.state})))}export{e as LOOP_VIEW_STATUS_KEY,t as formatLoopStatusView};
2
- //# sourceMappingURL=loopView.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopView.mjs","names":[],"sources":["../../../src/types/loopView.ts"],"sourcesContent":["/** The browser-only status that carries active Loop instances into the activity dock. */\nexport const LOOP_VIEW_STATUS_KEY = 'doom-loop-instances';\n\nexport type LoopStatusState = 'starting' | 'running' | 'stopping';\n\n/** The small, serializable Loop view the cockpit needs for one activity row. */\nexport interface LoopStatusItem {\n readonly instanceId: string;\n readonly label: string;\n readonly detail: string;\n readonly state: LoopStatusState;\n}\n\ninterface LoopStatusSource {\n readonly instanceId: string;\n readonly launcherLabel: string;\n readonly label?: string;\n readonly detail?: string;\n readonly state: LoopStatusState;\n}\n\nconst ANSI = new RegExp(String.raw`\\u001b\\[[0-9;]*m`, 'gu');\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isState(value: unknown): value is LoopStatusState {\n return value === 'starting' || value === 'running' || value === 'stopping';\n}\n\n/** Projects runtime snapshots into the JSON status consumed by the web plugin. */\nexport function formatLoopStatusView(instances: readonly LoopStatusSource[]): string | undefined {\n if (instances.length === 0) return undefined;\n return JSON.stringify(\n instances.map((instance) => ({\n instanceId: instance.instanceId,\n label: instance.label ?? instance.launcherLabel,\n detail: instance.detail ?? instance.instanceId,\n state: instance.state,\n })),\n );\n}\n\n/** Reads a Loop status without letting malformed session data crash the activity dock. */\nexport function parseLoopStatusView(raw: string | undefined): LoopStatusItem[] | undefined {\n if (raw === undefined) return undefined;\n const text = raw.replace(ANSI, '').trim();\n if (text === '') return undefined;\n\n try {\n const value: unknown = JSON.parse(text);\n if (!Array.isArray(value) || value.length === 0) return undefined;\n const items: LoopStatusItem[] = [];\n for (const item of value) {\n if (\n !isRecord(item) ||\n typeof item.instanceId !== 'string' ||\n item.instanceId.trim() === '' ||\n typeof item.label !== 'string' ||\n item.label.trim() === '' ||\n typeof item.detail !== 'string' ||\n !isState(item.state)\n ) {\n return undefined;\n }\n items.push({ instanceId: item.instanceId, label: item.label, detail: item.detail, state: item.state });\n }\n return items;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"AACA,MAAa,EAAuB,sBAoBvB,IAAI,OAAO,OAAO,GAAG,mBAAoB,IAAI,EAW1D,SAAgB,EAAqB,EAA4D,CAC3F,KAAU,SAAW,EACzB,OAAO,KAAK,UACV,EAAU,IAAK,IAAc,CAC3B,WAAY,EAAS,WACrB,MAAO,EAAS,OAAS,EAAS,cAClC,OAAQ,EAAS,QAAU,EAAS,WACpC,MAAO,EAAS,KAClB,EAAE,CACJ,CACF"}
@@ -1,41 +0,0 @@
1
- import { defineSlot, defineWebPlugin } from '@agimon-ai/doompi-core/web';
2
-
3
- import { LOOP_VIEW_STATUS_KEY } from '../types/loopView';
4
- import { LoopActivityItems, LoopsActivitySection } from '../web/components/LoopsActivitySection';
5
-
6
- /**
7
- * This package's cockpit presence. The selection bar carries the minor mode,
8
- * while the activity dock shows each loop the active session is scheduling.
9
- */
10
- const LOOPS_GROUP = { key: 'l', label: 'loops', detail: 'recurring prompt loops' };
11
- const LOOPS_ACTIVITY_SOURCE = {
12
- subscribe: () => () => undefined,
13
- // Keeping the scheduler's launcher visible does not mean a result is pending.
14
- isActive: (_sessionId: string | null) => false,
15
- };
16
-
17
- export const webPlugin = defineWebPlugin({
18
- id: 'loop',
19
- session: {
20
- minorModes: [{ name: 'loop', keys: 'l l', statusKey: 'doom-loop', activityGroup: 'loops', order: 30 }],
21
- activityGroups: [
22
- { name: 'loops', keys: 'l l', statusKey: LOOP_VIEW_STATUS_KEY, activeSource: LOOPS_ACTIVITY_SOURCE, order: 40 },
23
- ],
24
- activitySections: [{ id: 'loops', component: LoopsActivitySection }],
25
- slots: [defineSlot({ slot: 'loop.registration' }), defineSlot({ slot: 'loop.items' })],
26
- fills: [{ slot: 'loop.items', id: 'instances', component: LoopActivityItems }],
27
- // The TUI's SPC l s and SPC l l: both are slash commands, so both carry over.
28
- leaderBindings: [
29
- {
30
- id: 'loop.start',
31
- path: [LOOPS_GROUP, { key: 's', label: 'start', detail: 'begin a recurring loop' }],
32
- command: 'loop',
33
- },
34
- {
35
- id: 'loop.list',
36
- path: [LOOPS_GROUP, { key: 'l', label: 'list', detail: 'loops in this session' }],
37
- command: 'loops',
38
- },
39
- ],
40
- },
41
- });