@agimon-ai/doompi-loop 0.0.1-alpha.93 → 0.0.1-alpha.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,8 +25,10 @@ pi install npm:@agimon-ai/doompi-loop
25
25
 
26
26
  ## Start and manage loops
27
27
 
28
- Use `/loop` to start a loop and `/loops` to inspect or stop active loops. In DoomPi, `SPC l s` opens
29
- the start flow and `SPC l l` opens the list.
28
+ Activate the Loop minor mode, then ask the agent to set an interval loop or a cron job; it uses the
29
+ `loop_start` tool. The Activity Loops section lists the session's loops whether or not the mode is
30
+ active, and each row has a stop control. `/loops` lists loops and `/loops stop <instanceId>` stops
31
+ one.
30
32
 
31
33
  The default interval is 300 seconds. Accepted intervals range from 30 to 3600 seconds. A loop runs
32
34
  its prompt once immediately, then schedules later passes.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_loopLaunchers = require("../loopLaunchers-BtcLH_xN.cjs");
6
- const require_loopView = require("../loopView-C_OPuy8L.cjs");
6
+ const require_loopView = require("../loopView-FnuHeY7C.cjs");
7
7
  let _agimon_ai_doompi_core_piExtension = require("@agimon-ai/doompi-core/piExtension");
8
8
  let _agimon_ai_doompi_core_extensionFile = require("@agimon-ai/doompi-core/extensionFile");
9
9
  let _agimon_ai_doompi_minor_mode = require("@agimon-ai/doompi-minor-mode");
@@ -1,5 +1,5 @@
1
1
  import { t as DOOM_LOOP_LAUNCHERS_SERVICE } from "../loopLaunchers-cCniFbVA.mjs";
2
- import { _ as PACKAGE_SOURCE, a as CronLoopSchema, c as parseIntervalLoop, d as START_COMMAND_DESCRIPTION, f as START_COMMAND_NAME, g as NOTIFY_WARNING_LEVEL, h as NOTIFY_INFO_LEVEL, i as createDoomLoopLaunchersService, l as LIST_COMMAND_DESCRIPTION, m as MODE_STATUS_ID, n as formatLoopStatusView, o as IntervalLoopSchema, p as ACTIVE_MODE_COLOR, r as createLoopTools, s as parseCronLoop, t as LOOP_VIEW_STATUS_KEY, u as LIST_COMMAND_NAME, v as STATUS_KEY } from "../loopView-BK4X_1UI.mjs";
2
+ import { _ as PACKAGE_SOURCE, a as CronLoopSchema, c as parseIntervalLoop, d as START_COMMAND_DESCRIPTION, f as START_COMMAND_NAME, g as NOTIFY_WARNING_LEVEL, h as NOTIFY_INFO_LEVEL, i as createDoomLoopLaunchersService, l as LIST_COMMAND_DESCRIPTION, m as MODE_STATUS_ID, n as formatLoopStatusView, o as IntervalLoopSchema, p as ACTIVE_MODE_COLOR, r as createLoopTools, s as parseCronLoop, t as LOOP_VIEW_STATUS_KEY, u as LIST_COMMAND_NAME, v as STATUS_KEY } from "../loopView-DUR63zT7.mjs";
3
3
  import { definePiExtension, piToolContributions } from "@agimon-ai/doompi-core/piExtension";
4
4
  import { composeRootHooks, defineCliCommand, defineCliHook, defineResource, defineRoot, defineTool as defineTool$1, defineToolRestriction } from "@agimon-ai/doompi-core/extensionFile";
5
5
  import { defineMinorMode, piMinorModes } from "@agimon-ai/doompi-minor-mode";
@@ -3,51 +3,16 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_loopLaunchers = require("../loopLaunchers-BtcLH_xN.cjs");
6
- const require_loopView = require("../loopView-C_OPuy8L.cjs");
6
+ const require_loopView = require("../loopView-FnuHeY7C.cjs");
7
7
  let _agimon_ai_doompi_core_extensionFile = require("@agimon-ai/doompi-core/extensionFile");
8
8
  let _agimon_ai_doompi_minor_mode = require("@agimon-ai/doompi-minor-mode");
9
9
  let croner = require("croner");
10
10
  let _agimon_ai_doompi_core_serverFacet = require("@agimon-ai/doompi-core/serverFacet");
11
11
  //#region src/services/serverRuntime/index.ts
12
- async function prepareLoop(execution, request, cron) {
13
- if (request.input !== void 0) return cron ? require_loopView.parseCronLoop(request.input) : require_loopView.parseIntervalLoop(request.input);
14
- if (request.interactive === false) throw new Error("Loop configuration is required for agent launches.");
15
- const input = await execution.client.request({
16
- kind: "input",
17
- title: "Loop prompt",
18
- multiline: true
19
- }, request.signal);
20
- const prompt = typeof input === "string" ? input.trim() : "";
21
- if (!prompt || request.signal.aborted) return void 0;
22
- if (cron) {
23
- const expression = await execution.client.request({
24
- kind: "input",
25
- title: "Cron expression (five fields)",
26
- initialValue: "0 * * * *"
27
- }, request.signal);
28
- if (typeof expression !== "string" || request.signal.aborted) return void 0;
29
- const timezone = await execution.client.request({
30
- kind: "input",
31
- title: "Loop timezone",
32
- initialValue: "UTC"
33
- }, request.signal);
34
- if (typeof timezone !== "string" || request.signal.aborted) return void 0;
35
- return require_loopView.parseCronLoop({
36
- prompt,
37
- cron: expression,
38
- timezone: timezone.trim() || "UTC"
39
- });
40
- }
41
- const interval = await execution.client.request({
42
- kind: "input",
43
- title: "Loop interval in seconds",
44
- initialValue: "300"
45
- }, request.signal);
46
- if (typeof interval !== "string" || request.signal.aborted) return void 0;
47
- return require_loopView.parseIntervalLoop({
48
- prompt,
49
- intervalSeconds: Number(interval.trim() || "300")
50
- });
12
+ /** Loops are set by the agent, so a launch always carries its configuration. */
13
+ function prepareLoop(request, cron) {
14
+ if (request.input === void 0) throw new Error("Loop configuration is required. Ask the agent to set the loop.");
15
+ return cron ? require_loopView.parseCronLoop(request.input) : require_loopView.parseIntervalLoop(request.input);
51
16
  }
52
17
  function createSessionState(host) {
53
18
  const launchers = require_loopView.createDoomLoopLaunchersService({
@@ -56,7 +21,7 @@ function createSessionState(host) {
56
21
  timestamp: () => (/* @__PURE__ */ new Date()).toISOString()
57
22
  });
58
23
  let disposed = false;
59
- const modeSelected = () => !disposed && (host.context.selection.state?.["minor-mode"] ?? []).includes("loop.active");
24
+ const modeSelected = () => !disposed && (host.context.selection.state?.["minor-mode"] ?? []).includes("loop");
60
25
  const publishView = () => host.context.client.setStatus(require_loopView.LOOP_VIEW_STATUS_KEY, require_loopView.formatLoopStatusView(launchers.listInstances()));
61
26
  const modeOwner = (0, _agimon_ai_doompi_minor_mode.defineMinorMode)({
62
27
  descriptor: {
@@ -124,7 +89,7 @@ function createSessionState(host) {
124
89
  inputSchema: { ...cron ? require_loopView.CronLoopSchema : require_loopView.IntervalLoopSchema },
125
90
  async launch(request) {
126
91
  const execution = host.context;
127
- const prepared = await prepareLoop(execution, request, cron);
92
+ const prepared = prepareLoop(request, cron);
128
93
  if (!prepared || request.signal.aborted) return void 0;
129
94
  let stopped = false;
130
95
  const tick = async () => {
@@ -220,26 +185,6 @@ function createSessionState(host) {
220
185
  }
221
186
  }],
222
187
  commands: [{
223
- name: require_loopView.START_COMMAND_NAME,
224
- description: "Start a registered loop manually.",
225
- async execute(args, execution) {
226
- const available = launchers.listLaunchers();
227
- const selected = args.trim() || await execution.client.request({
228
- kind: "select",
229
- title: "Choose a loop launcher",
230
- options: available.map((entry) => ({
231
- label: entry.label,
232
- value: entry.id
233
- }))
234
- });
235
- if (typeof selected !== "string" || !selected) return;
236
- const instance = await launchers.launch(selected);
237
- await execution.client.notify({
238
- body: instance ? `Loop '${instance.instanceId}' started.` : "Loop launch was cancelled.",
239
- level: "info"
240
- });
241
- }
242
- }, {
243
188
  name: require_loopView.LIST_COMMAND_NAME,
244
189
  description: "List or stop session loops. Usage: /loops [stop instanceId].",
245
190
  async execute(args, execution) {
@@ -297,11 +242,8 @@ var root_server_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoot)((
297
242
  };
298
243
  });
299
244
  //#endregion
300
- //#region src/extensions/workspaces/sessions/(backend)/command/loop.server.ts
301
- var loop_server_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.commands?.[0], { cardinality: "optional" });
302
- //#endregion
303
245
  //#region src/extensions/workspaces/sessions/(backend)/command/loops.server.ts
304
- var loops_server_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.commands?.[1], { cardinality: "optional" });
246
+ var loops_server_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.commands?.[0], { cardinality: "optional" });
305
247
  //#endregion
306
248
  //#region src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts
307
249
  var session_shutdown_server_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.hooks?.find((hook) => hook.event === "session_shutdown"), { cardinality: "optional" });
@@ -340,7 +282,7 @@ const facet = (0, _agimon_ai_doompi_core_serverFacet.defineServerPlugin)({
340
282
  return {
341
283
  ...(0, _agimon_ai_doompi_core_extensionFile.composeRootHooks)(scopeSession),
342
284
  get commands() {
343
- return defined([via({ name: "loop" }, at(loop_server_default, contextSession)), via({ name: "loops" }, at(loops_server_default, contextSession))]);
285
+ return defined([via({ name: "loops" }, at(loops_server_default, contextSession))]);
344
286
  },
345
287
  get hooks() {
346
288
  return defined([via({ event: "session_shutdown" }, at(session_shutdown_server_default, contextSession)), via({ event: "session_start" }, at(session_start_server_default, contextSession))]);
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":["parseCronLoop","parseIntervalLoop","createDoomLoopLaunchersService","LOOP_VIEW_STATUS_KEY","formatLoopStatusView","defineMinorMode","PACKAGE_SOURCE","MODE_STATUS_ID","CronLoopSchema","IntervalLoopSchema","Cron","createLoopTools","DOOM_LOOP_LAUNCHERS_SERVICE","serverMinorModes","readPackageResource","START_COMMAND_NAME","LIST_COMMAND_NAME","defineRoot","runtime","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineTool","defineTool","defineTool","defineServerPlugin","rootSession","composeRootHooks","commandLoop","commandLoops","hookSessionShutdown","hookSessionStart","resourceDoompiUseLoop","toolLoopList","toolLoopStart","toolLoopStop"],"sources":["../../src/services/serverRuntime/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/loop.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/loops.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-list.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server.ts","../../generated/server.ts"],"sourcesContent":["import type { DoomHeadlessExecutionContext, DoomHeadlessHostService } from '@agimon-ai/doompi-core/headless';\nimport { readPackageResource, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { defineMinorMode, serverMinorModes } from '@agimon-ai/doompi-minor-mode';\nimport { Cron } from 'croner';\n\nimport { LIST_COMMAND_NAME, START_COMMAND_NAME } from '../../constants/loop';\nimport { MODE_STATUS_ID, PACKAGE_SOURCE } from '../../constants/piLoop';\nimport { DOOM_LOOP_LAUNCHERS_SERVICE, type LoopLaunchRequest } from '../../schemas/loopLaunchers';\nimport { CronLoopSchema, IntervalLoopSchema, parseCronLoop, parseIntervalLoop } from '../../schemas/loopTools';\nimport { formatLoopStatusView, LOOP_VIEW_STATUS_KEY } from '../../types/loopView';\nimport { createDoomLoopLaunchersService } from '../loopLaunchers';\nimport { createLoopTools } from '../loopTools';\nimport type { LoopToolsRoot } from '../loopTools/type';\n\nexport type LoopServerState = DoomServerSessionPlugin & LoopToolsRoot;\n\nasync function prepareLoop(execution: DoomHeadlessExecutionContext, request: LoopLaunchRequest, cron: boolean) {\n if (request.input !== undefined) return cron ? parseCronLoop(request.input) : parseIntervalLoop(request.input);\n if (request.interactive === false) throw new Error('Loop configuration is required for agent launches.');\n const input = await execution.client.request(\n { kind: 'input', title: 'Loop prompt', multiline: true },\n request.signal,\n );\n const prompt = typeof input === 'string' ? input.trim() : '';\n if (!prompt || request.signal.aborted) return undefined;\n if (cron) {\n const expression = await execution.client.request(\n { kind: 'input', title: 'Cron expression (five fields)', initialValue: '0 * * * *' },\n request.signal,\n );\n if (typeof expression !== 'string' || request.signal.aborted) return undefined;\n const timezone = await execution.client.request(\n { kind: 'input', title: 'Loop timezone', initialValue: 'UTC' },\n request.signal,\n );\n if (typeof timezone !== 'string' || request.signal.aborted) return undefined;\n return parseCronLoop({ prompt, cron: expression, timezone: timezone.trim() || 'UTC' });\n }\n const interval = await execution.client.request(\n { kind: 'input', title: 'Loop interval in seconds', initialValue: '300' },\n request.signal,\n );\n if (typeof interval !== 'string' || request.signal.aborted) return undefined;\n return parseIntervalLoop({ prompt, intervalSeconds: Number(interval.trim() || '300') });\n}\n\nexport function createSessionState(host: DoomHeadlessHostService): LoopServerState {\n const launchers = createDoomLoopLaunchersService({\n generation: `${host.context.sessionId}:loop-launchers`,\n createInstanceId: () => crypto.randomUUID(),\n timestamp: () => new Date().toISOString(),\n });\n let disposed = false;\n const modeSelected = (): boolean =>\n !disposed && (host.context.selection.state?.['minor-mode'] ?? []).includes(MODE_STATUS_ID);\n const publishView = (): void =>\n host.context.client.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(launchers.listInstances()));\n const modeOwner = defineMinorMode({\n descriptor: {\n source: PACKAGE_SOURCE,\n id: MODE_STATUS_ID,\n label: 'Loop',\n description: 'Give the agent tools to create, inspect, and stop session loops.',\n order: 60,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Expose loop tools to the agent.',\n contexts: ['headless'],\n parameters: [],\n },\n {\n id: 'deactivate',\n label: 'Deactivate',\n description: 'Hide agent loop tools. Existing loops and manual controls remain available.',\n contexts: ['headless'],\n parameters: [],\n },\n ],\n },\n state: () => ({\n activation: modeSelected() ? 'active' : 'inactive',\n condition: 'ready',\n ...(modeSelected() ? { detail: `${launchers.listInstances().length} loops`, color: 'accent' } : {}),\n actions: [\n {\n id: 'activate',\n enabled: !modeSelected(),\n ...(modeSelected() ? { disabledReason: 'Loop mode is already active.' } : {}),\n },\n {\n id: 'deactivate',\n enabled: modeSelected(),\n ...(modeSelected() ? {} : { disabledReason: 'Loop mode is not active.' }),\n },\n ],\n }),\n async handleAction(_runtime, actionId, _arguments, { signal }) {\n signal.throwIfAborted();\n if (actionId !== 'activate' && actionId !== 'deactivate')\n throw new Error(`Unknown loop mode action: ${actionId}`);\n const modes = (host.context.selection.state?.['minor-mode'] ?? []).filter((mode) => mode !== MODE_STATUS_ID);\n await host.changeSelection({\n axis: 'state',\n key: 'minor-mode',\n values: actionId === 'activate' ? [...modes, MODE_STATUS_ID] : modes,\n });\n modeOwner.publish();\n return {\n message:\n actionId === 'activate' ? 'Loop tools activated.' : 'Loop tools deactivated. Existing loops are unchanged.',\n };\n },\n }).createOwner(undefined);\n const publish = (): void => {\n if (disposed) return;\n modeOwner.publish();\n publishView();\n };\n const unsubscribe = launchers.subscribe(publish);\n let submitting = false;\n for (const cron of [false, true]) {\n launchers.register({\n id: cron ? 'doompi.cron' : 'doompi.default',\n source: PACKAGE_SOURCE,\n label: cron ? 'Cron loop' : 'Default loop',\n description: cron\n ? 'Run a prompt on a five-field cron schedule.'\n : 'Run a prompt in this session on an interval.',\n inputSchema: { ...(cron ? CronLoopSchema : IntervalLoopSchema) },\n async launch(request) {\n const execution = host.context;\n const prepared = await prepareLoop(execution, request, cron);\n if (!prepared || request.signal.aborted) return undefined;\n let stopped = false;\n const tick = async (): Promise<void> => {\n if (stopped || request.signal.aborted || submitting) return;\n submitting = true;\n try {\n const activity = await execution.session.activity();\n if (stopped || request.signal.aborted || !activity.isIdle || activity.hasPendingMessages) return;\n await execution.session.prompt(prepared.prompt);\n } catch (error) {\n await execution.client.notify({\n body: `Loop pass failed: ${error instanceof Error ? error.message : String(error)}`,\n level: 'warning',\n });\n } finally {\n submitting = false;\n }\n };\n let stopTimer: () => void;\n let detail: string;\n if ('cron' in prepared) {\n const job = new Cron(prepared.cron, { timezone: prepared.timezone, unref: true }, tick);\n if (job.nextRun() === null) {\n job.stop();\n throw new Error('Cron expression has no future run.');\n }\n stopTimer = () => {\n job.stop();\n };\n detail = `${prepared.cron} (${prepared.timezone})`;\n } else {\n const timer = setInterval(() => {\n void tick();\n }, prepared.intervalSeconds * 1000);\n timer.unref?.();\n stopTimer = () => clearInterval(timer);\n detail = `every ${prepared.intervalSeconds}s`;\n void tick();\n }\n return {\n instanceId: request.instanceId,\n label: cron ? 'Cron loop' : 'Default loop',\n detail,\n stop() {\n stopped = true;\n stopTimer();\n },\n };\n },\n });\n }\n const loopTools = createLoopTools(\n () => launchers,\n () => {\n if (!modeSelected()) throw new Error('Enable Loop minor mode before using agent loop tools.');\n },\n ).map((tool) => ({\n ...tool,\n when: { state: { 'minor-mode': MODE_STATUS_ID }, attribution: { kind: 'minor' as const, mode: MODE_STATUS_ID } },\n }));\n return {\n loopTools,\n services: [\n (context) => {\n context.plugin((provider) => {\n provider.provide(DOOM_LOOP_LAUNCHERS_SERVICE, launchers);\n });\n context.effect(() => host.subscribeSelection(publish));\n },\n serverMinorModes([modeOwner]),\n ],\n resources: [\n {\n when: { state: { 'minor-mode': MODE_STATUS_ID }, attribution: { kind: 'minor', mode: MODE_STATUS_ID } },\n name: 'doompi-use-loop',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-loop/SKILL.md'),\n },\n ],\n activities: [\n {\n name: 'doompi-loop',\n async start() {\n publish();\n return async () => {\n await launchers.stopAll('Session loop activity stopped.');\n publish();\n };\n },\n },\n ],\n commands: [\n {\n name: START_COMMAND_NAME,\n description: 'Start a registered loop manually.',\n async execute(args: string, execution: DoomHeadlessExecutionContext) {\n const available = launchers.listLaunchers();\n const selected =\n args.trim() ||\n (await execution.client.request({\n kind: 'select',\n title: 'Choose a loop launcher',\n options: available.map((entry) => ({ label: entry.label, value: entry.id })),\n }));\n if (typeof selected !== 'string' || !selected) return;\n const instance = await launchers.launch(selected);\n await execution.client.notify({\n body: instance ? `Loop '${instance.instanceId}' started.` : 'Loop launch was cancelled.',\n level: 'info',\n });\n },\n },\n {\n name: LIST_COMMAND_NAME,\n description: 'List or stop session loops. Usage: /loops [stop instanceId].',\n async execute(args: string, execution: DoomHeadlessExecutionContext) {\n const parts = args.trim().split(/\\s+/u);\n if (parts[0] === 'stop' && parts.length === 2) {\n const stopped = await launchers.stop(parts[1]!, 'Stopped manually.');\n await execution.client.notify({\n body: stopped ? 'Loop stopped.' : 'Loop instance was not active.',\n level: 'info',\n });\n return;\n }\n if (args.trim()) throw new Error('Usage: /loops [stop instanceId]');\n const instances = launchers.listInstances();\n await execution.client.notify({\n body: instances.length === 0 ? 'No loops are active.' : JSON.stringify(instances, null, 2),\n level: 'info',\n });\n },\n },\n ],\n hooks: [\n {\n event: 'session_start',\n async handle() {\n publish();\n },\n },\n {\n event: 'session_shutdown',\n async handle() {\n await launchers.stopAll('Headless session shutdown.');\n },\n },\n ],\n onStart: publish,\n async onDispose() {\n if (disposed) return;\n disposed = true;\n unsubscribe();\n await launchers.dispose('Headless loop facet disposed.');\n },\n };\n}\n","import type { DoomServerPluginDefinition } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { createSessionState, type LoopServerState } from '../../../../../services/serverRuntime';\n\nexport default (({ agent }): LoopServerState =>\n agent ? createSessionState(agent) : { loopTools: [] }) satisfies NonNullable<DoomServerPluginDefinition['session']>;\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/doompi-core/serverFacet';\n\nimport runtime from './_lib/runtime.server';\n\nexport default defineRoot((context: DoomServerPluginContext) => {\n const value = runtime(context);\n return {\n value,\n services: value.services,\n activities: value.activities,\n onStart: value.onStart,\n onStop: value.onStop,\n onDispose: value.onDispose,\n };\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.commands?.[0],\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.commands?.[1],\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) =>\n context.root.hooks?.find((hook) => hook.event === 'session_shutdown'),\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) =>\n context.root.hooks?.find((hook) => hook.event === 'session_start'),\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.resources?.[0],\n { cardinality: 'optional' },\n);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[0]!);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[1]!);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[2]!);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandLoop from '../src/extensions/workspaces/sessions/(backend)/command/loop.server';\nimport commandLoops from '../src/extensions/workspaces/sessions/(backend)/command/loops.server';\nimport hookSessionShutdown from '../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/session-start.server';\nimport resourceDoompiUseLoop from '../src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server';\nimport toolLoopList from '../src/extensions/workspaces/sessions/(backend)/tool/loop-list.server';\nimport toolLoopStart from '../src/extensions/workspaces/sessions/(backend)/tool/loop-start.server';\nimport toolLoopStop from '../src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/doompi-loop',\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return defined([via({ name: 'loop' }, at(commandLoop, contextSession)), via({ name: 'loops' }, at(commandLoops, contextSession))]); },\n get hooks(): DoomServerSessionPlugin['hooks'] { return defined([via({ event: 'session_shutdown' }, at(hookSessionShutdown, contextSession)), via({ event: 'session_start' }, at(hookSessionStart, contextSession))]); },\n get resources(): DoomServerSessionPlugin['resources'] { return defined([at(resourceDoompiUseLoop, contextSession)]); },\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'loop_list' }, at(toolLoopList, contextSession)), via({ name: 'loop_start' }, at(toolLoopStart, contextSession)), via({ name: 'loop_stop' }, at(toolLoopStop, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.cjs","names":["parseCronLoop","parseIntervalLoop","createDoomLoopLaunchersService","LOOP_VIEW_STATUS_KEY","formatLoopStatusView","defineMinorMode","PACKAGE_SOURCE","MODE_STATUS_ID","CronLoopSchema","IntervalLoopSchema","Cron","createLoopTools","DOOM_LOOP_LAUNCHERS_SERVICE","serverMinorModes","readPackageResource","LIST_COMMAND_NAME","defineRoot","runtime","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineTool","defineTool","defineTool","defineServerPlugin","rootSession","composeRootHooks","commandLoops","hookSessionShutdown","hookSessionStart","resourceDoompiUseLoop","toolLoopList","toolLoopStart","toolLoopStop"],"sources":["../../src/services/serverRuntime/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/loops.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-list.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server.ts","../../generated/server.ts"],"sourcesContent":["import type { DoomHeadlessExecutionContext, DoomHeadlessHostService } from '@agimon-ai/doompi-core/headless';\nimport { readPackageResource, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { defineMinorMode, serverMinorModes } from '@agimon-ai/doompi-minor-mode';\nimport { Cron } from 'croner';\n\nimport { LIST_COMMAND_NAME } from '../../constants/loop';\nimport { MODE_STATUS_ID, PACKAGE_SOURCE } from '../../constants/piLoop';\nimport { DOOM_LOOP_LAUNCHERS_SERVICE, type LoopLaunchRequest } from '../../schemas/loopLaunchers';\nimport { CronLoopSchema, IntervalLoopSchema, parseCronLoop, parseIntervalLoop } from '../../schemas/loopTools';\nimport { formatLoopStatusView, LOOP_VIEW_STATUS_KEY } from '../../types/loopView';\nimport { createDoomLoopLaunchersService } from '../loopLaunchers';\nimport { createLoopTools } from '../loopTools';\nimport type { LoopToolsRoot } from '../loopTools/type';\n\nexport type LoopServerState = DoomServerSessionPlugin & LoopToolsRoot;\n\n/** Loops are set by the agent, so a launch always carries its configuration. */\nfunction prepareLoop(request: LoopLaunchRequest, cron: boolean) {\n if (request.input === undefined) throw new Error('Loop configuration is required. Ask the agent to set the loop.');\n return cron ? parseCronLoop(request.input) : parseIntervalLoop(request.input);\n}\n\nexport function createSessionState(host: DoomHeadlessHostService): LoopServerState {\n const launchers = createDoomLoopLaunchersService({\n generation: `${host.context.sessionId}:loop-launchers`,\n createInstanceId: () => crypto.randomUUID(),\n timestamp: () => new Date().toISOString(),\n });\n let disposed = false;\n const modeSelected = (): boolean =>\n !disposed && (host.context.selection.state?.['minor-mode'] ?? []).includes(MODE_STATUS_ID);\n const publishView = (): void =>\n host.context.client.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(launchers.listInstances()));\n const modeOwner = defineMinorMode({\n descriptor: {\n source: PACKAGE_SOURCE,\n id: MODE_STATUS_ID,\n label: 'Loop',\n description: 'Give the agent tools to create, inspect, and stop session loops.',\n order: 60,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Expose loop tools to the agent.',\n contexts: ['headless'],\n parameters: [],\n },\n {\n id: 'deactivate',\n label: 'Deactivate',\n description: 'Hide agent loop tools. Existing loops and manual controls remain available.',\n contexts: ['headless'],\n parameters: [],\n },\n ],\n },\n state: () => ({\n activation: modeSelected() ? 'active' : 'inactive',\n condition: 'ready',\n ...(modeSelected() ? { detail: `${launchers.listInstances().length} loops`, color: 'accent' } : {}),\n actions: [\n {\n id: 'activate',\n enabled: !modeSelected(),\n ...(modeSelected() ? { disabledReason: 'Loop mode is already active.' } : {}),\n },\n {\n id: 'deactivate',\n enabled: modeSelected(),\n ...(modeSelected() ? {} : { disabledReason: 'Loop mode is not active.' }),\n },\n ],\n }),\n async handleAction(_runtime, actionId, _arguments, { signal }) {\n signal.throwIfAborted();\n if (actionId !== 'activate' && actionId !== 'deactivate')\n throw new Error(`Unknown loop mode action: ${actionId}`);\n const modes = (host.context.selection.state?.['minor-mode'] ?? []).filter((mode) => mode !== MODE_STATUS_ID);\n await host.changeSelection({\n axis: 'state',\n key: 'minor-mode',\n values: actionId === 'activate' ? [...modes, MODE_STATUS_ID] : modes,\n });\n modeOwner.publish();\n return {\n message:\n actionId === 'activate' ? 'Loop tools activated.' : 'Loop tools deactivated. Existing loops are unchanged.',\n };\n },\n }).createOwner(undefined);\n const publish = (): void => {\n if (disposed) return;\n modeOwner.publish();\n publishView();\n };\n const unsubscribe = launchers.subscribe(publish);\n let submitting = false;\n for (const cron of [false, true]) {\n launchers.register({\n id: cron ? 'doompi.cron' : 'doompi.default',\n source: PACKAGE_SOURCE,\n label: cron ? 'Cron loop' : 'Default loop',\n description: cron\n ? 'Run a prompt on a five-field cron schedule.'\n : 'Run a prompt in this session on an interval.',\n inputSchema: { ...(cron ? CronLoopSchema : IntervalLoopSchema) },\n async launch(request) {\n const execution = host.context;\n const prepared = prepareLoop(request, cron);\n if (!prepared || request.signal.aborted) return undefined;\n let stopped = false;\n const tick = async (): Promise<void> => {\n if (stopped || request.signal.aborted || submitting) return;\n submitting = true;\n try {\n const activity = await execution.session.activity();\n if (stopped || request.signal.aborted || !activity.isIdle || activity.hasPendingMessages) return;\n await execution.session.prompt(prepared.prompt);\n } catch (error) {\n await execution.client.notify({\n body: `Loop pass failed: ${error instanceof Error ? error.message : String(error)}`,\n level: 'warning',\n });\n } finally {\n submitting = false;\n }\n };\n let stopTimer: () => void;\n let detail: string;\n if ('cron' in prepared) {\n const job = new Cron(prepared.cron, { timezone: prepared.timezone, unref: true }, tick);\n if (job.nextRun() === null) {\n job.stop();\n throw new Error('Cron expression has no future run.');\n }\n stopTimer = () => {\n job.stop();\n };\n detail = `${prepared.cron} (${prepared.timezone})`;\n } else {\n const timer = setInterval(() => {\n void tick();\n }, prepared.intervalSeconds * 1000);\n timer.unref?.();\n stopTimer = () => clearInterval(timer);\n detail = `every ${prepared.intervalSeconds}s`;\n void tick();\n }\n return {\n instanceId: request.instanceId,\n label: cron ? 'Cron loop' : 'Default loop',\n detail,\n stop() {\n stopped = true;\n stopTimer();\n },\n };\n },\n });\n }\n const loopTools = createLoopTools(\n () => launchers,\n () => {\n if (!modeSelected()) throw new Error('Enable Loop minor mode before using agent loop tools.');\n },\n ).map((tool) => ({\n ...tool,\n when: { state: { 'minor-mode': MODE_STATUS_ID }, attribution: { kind: 'minor' as const, mode: MODE_STATUS_ID } },\n }));\n return {\n loopTools,\n services: [\n (context) => {\n context.plugin((provider) => {\n provider.provide(DOOM_LOOP_LAUNCHERS_SERVICE, launchers);\n });\n context.effect(() => host.subscribeSelection(publish));\n },\n serverMinorModes([modeOwner]),\n ],\n resources: [\n {\n when: { state: { 'minor-mode': MODE_STATUS_ID }, attribution: { kind: 'minor', mode: MODE_STATUS_ID } },\n name: 'doompi-use-loop',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-loop/SKILL.md'),\n },\n ],\n activities: [\n {\n name: 'doompi-loop',\n async start() {\n publish();\n return async () => {\n await launchers.stopAll('Session loop activity stopped.');\n publish();\n };\n },\n },\n ],\n commands: [\n {\n name: LIST_COMMAND_NAME,\n description: 'List or stop session loops. Usage: /loops [stop instanceId].',\n async execute(args: string, execution: DoomHeadlessExecutionContext) {\n const parts = args.trim().split(/\\s+/u);\n if (parts[0] === 'stop' && parts.length === 2) {\n const stopped = await launchers.stop(parts[1]!, 'Stopped manually.');\n await execution.client.notify({\n body: stopped ? 'Loop stopped.' : 'Loop instance was not active.',\n level: 'info',\n });\n return;\n }\n if (args.trim()) throw new Error('Usage: /loops [stop instanceId]');\n const instances = launchers.listInstances();\n await execution.client.notify({\n body: instances.length === 0 ? 'No loops are active.' : JSON.stringify(instances, null, 2),\n level: 'info',\n });\n },\n },\n ],\n hooks: [\n {\n event: 'session_start',\n async handle() {\n publish();\n },\n },\n {\n event: 'session_shutdown',\n async handle() {\n await launchers.stopAll('Headless session shutdown.');\n },\n },\n ],\n onStart: publish,\n async onDispose() {\n if (disposed) return;\n disposed = true;\n unsubscribe();\n await launchers.dispose('Headless loop facet disposed.');\n },\n };\n}\n","import type { DoomServerPluginDefinition } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { createSessionState, type LoopServerState } from '../../../../../services/serverRuntime';\n\nexport default (({ agent }): LoopServerState =>\n agent ? createSessionState(agent) : { loopTools: [] }) satisfies NonNullable<DoomServerPluginDefinition['session']>;\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/doompi-core/serverFacet';\n\nimport runtime from './_lib/runtime.server';\n\nexport default defineRoot((context: DoomServerPluginContext) => {\n const value = runtime(context);\n return {\n value,\n services: value.services,\n activities: value.activities,\n onStart: value.onStart,\n onStop: value.onStop,\n onDispose: value.onDispose,\n };\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.commands?.[0],\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) =>\n context.root.hooks?.find((hook) => hook.event === 'session_shutdown'),\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) =>\n context.root.hooks?.find((hook) => hook.event === 'session_start'),\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.resources?.[0],\n { cardinality: 'optional' },\n);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[0]!);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[1]!);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[2]!);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandLoops from '../src/extensions/workspaces/sessions/(backend)/command/loops.server';\nimport hookSessionShutdown from '../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/session-start.server';\nimport resourceDoompiUseLoop from '../src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server';\nimport toolLoopList from '../src/extensions/workspaces/sessions/(backend)/tool/loop-list.server';\nimport toolLoopStart from '../src/extensions/workspaces/sessions/(backend)/tool/loop-start.server';\nimport toolLoopStop from '../src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/doompi-loop',\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return defined([via({ name: 'loops' }, at(commandLoops, contextSession))]); },\n get hooks(): DoomServerSessionPlugin['hooks'] { return defined([via({ event: 'session_shutdown' }, at(hookSessionShutdown, contextSession)), via({ event: 'session_start' }, at(hookSessionStart, contextSession))]); },\n get resources(): DoomServerSessionPlugin['resources'] { return defined([at(resourceDoompiUseLoop, contextSession)]); },\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'loop_list' }, at(toolLoopList, contextSession)), via({ name: 'loop_start' }, at(toolLoopStart, contextSession)), via({ name: 'loop_stop' }, at(toolLoopStop, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAS,YAAY,SAA4B,MAAe;CAC9D,IAAI,QAAQ,UAAU,KAAA,GAAW,MAAM,IAAI,MAAM,gEAAgE;CACjH,OAAO,OAAOA,iBAAAA,cAAc,QAAQ,KAAK,IAAIC,iBAAAA,kBAAkB,QAAQ,KAAK;AAC9E;AAEA,SAAgB,mBAAmB,MAAgD;CACjF,MAAM,YAAYC,iBAAAA,+BAA+B;EAC/C,YAAY,GAAG,KAAK,QAAQ,UAAU;EACtC,wBAAwB,OAAO,WAAW;EAC1C,kCAAiB,IAAI,KAAK,EAAA,CAAE,YAAY;CAC1C,CAAC;CACD,IAAI,WAAW;CACf,MAAM,qBACJ,CAAC,aAAa,KAAK,QAAQ,UAAU,QAAQ,iBAAiB,CAAC,EAAA,CAAG,SAAA,MAAuB;CAC3F,MAAM,oBACJ,KAAK,QAAQ,OAAO,UAAUC,iBAAAA,sBAAsBC,iBAAAA,qBAAqB,UAAU,cAAc,CAAC,CAAC;CACrG,MAAM,aAAA,GAAYC,6BAAAA,gBAAAA,CAAgB;EAChC,YAAY;GACV,QAAQC,iBAAAA;GACR,IAAIC,iBAAAA;GACJ,OAAO;GACP,aAAa;GACb,OAAO;GACP,SAAS,CACP;IACE,IAAI;IACJ,OAAO;IACP,aAAa;IACb,UAAU,CAAC,UAAU;IACrB,YAAY,CAAC;GACf,GACA;IACE,IAAI;IACJ,OAAO;IACP,aAAa;IACb,UAAU,CAAC,UAAU;IACrB,YAAY,CAAC;GACf,CACF;EACF;EACA,cAAc;GACZ,YAAY,aAAa,IAAI,WAAW;GACxC,WAAW;GACX,GAAI,aAAa,IAAI;IAAE,QAAQ,GAAG,UAAU,cAAc,CAAC,CAAC,OAAO;IAAS,OAAO;GAAS,IAAI,CAAC;GACjG,SAAS,CACP;IACE,IAAI;IACJ,SAAS,CAAC,aAAa;IACvB,GAAI,aAAa,IAAI,EAAE,gBAAgB,+BAA+B,IAAI,CAAC;GAC7E,GACA;IACE,IAAI;IACJ,SAAS,aAAa;IACtB,GAAI,aAAa,IAAI,CAAC,IAAI,EAAE,gBAAgB,2BAA2B;GACzE,CACF;EACF;EACA,MAAM,aAAa,UAAU,UAAU,YAAY,EAAE,UAAU;GAC7D,OAAO,eAAe;GACtB,IAAI,aAAa,cAAc,aAAa,cAC1C,MAAM,IAAI,MAAM,6BAA6B,UAAU;GACzD,MAAM,SAAS,KAAK,QAAQ,UAAU,QAAQ,iBAAiB,CAAC,EAAA,CAAG,QAAQ,SAAS,SAASA,iBAAAA,cAAc;GAC3G,MAAM,KAAK,gBAAgB;IACzB,MAAM;IACN,KAAK;IACL,QAAQ,aAAa,aAAa,CAAC,GAAG,OAAOA,iBAAAA,cAAc,IAAI;GACjE,CAAC;GACD,UAAU,QAAQ;GAClB,OAAO,EACL,SACE,aAAa,aAAa,0BAA0B,wDACxD;EACF;CACF,CAAC,CAAC,CAAC,YAAY,KAAA,CAAS;CACxB,MAAM,gBAAsB;EAC1B,IAAI,UAAU;EACd,UAAU,QAAQ;EAClB,YAAY;CACd;CACA,MAAM,cAAc,UAAU,UAAU,OAAO;CAC/C,IAAI,aAAa;CACjB,KAAK,MAAM,QAAQ,CAAC,OAAO,IAAI,GAC7B,UAAU,SAAS;EACjB,IAAI,OAAO,gBAAgB;EAC3B,QAAQD,iBAAAA;EACR,OAAO,OAAO,cAAc;EAC5B,aAAa,OACT,gDACA;EACJ,aAAa,EAAE,GAAI,OAAOE,iBAAAA,iBAAiBC,iBAAAA,mBAAoB;EAC/D,MAAM,OAAO,SAAS;GACpB,MAAM,YAAY,KAAK;GACvB,MAAM,WAAW,YAAY,SAAS,IAAI;GAC1C,IAAI,CAAC,YAAY,QAAQ,OAAO,SAAS,OAAO,KAAA;GAChD,IAAI,UAAU;GACd,MAAM,OAAO,YAA2B;IACtC,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY;IACrD,aAAa;IACb,IAAI;KACF,MAAM,WAAW,MAAM,UAAU,QAAQ,SAAS;KAClD,IAAI,WAAW,QAAQ,OAAO,WAAW,CAAC,SAAS,UAAU,SAAS,oBAAoB;KAC1F,MAAM,UAAU,QAAQ,OAAO,SAAS,MAAM;IAChD,SAAS,OAAO;KACd,MAAM,UAAU,OAAO,OAAO;MAC5B,MAAM,qBAAqB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;MAChF,OAAO;KACT,CAAC;IACH,UAAU;KACR,aAAa;IACf;GACF;GACA,IAAI;GACJ,IAAI;GACJ,IAAI,UAAU,UAAU;IACtB,MAAM,MAAM,IAAIC,OAAAA,KAAK,SAAS,MAAM;KAAE,UAAU,SAAS;KAAU,OAAO;IAAK,GAAG,IAAI;IACtF,IAAI,IAAI,QAAQ,MAAM,MAAM;KAC1B,IAAI,KAAK;KACT,MAAM,IAAI,MAAM,oCAAoC;IACtD;IACA,kBAAkB;KAChB,IAAI,KAAK;IACX;IACA,SAAS,GAAG,SAAS,KAAK,IAAI,SAAS,SAAS;GAClD,OAAO;IACL,MAAM,QAAQ,kBAAkB;KAC9B,KAAU;IACZ,GAAG,SAAS,kBAAkB,GAAI;IAClC,MAAM,QAAQ;IACd,kBAAkB,cAAc,KAAK;IACrC,SAAS,SAAS,SAAS,gBAAgB;IAC3C,KAAU;GACZ;GACA,OAAO;IACL,YAAY,QAAQ;IACpB,OAAO,OAAO,cAAc;IAC5B;IACA,OAAO;KACL,UAAU;KACV,UAAU;IACZ;GACF;EACF;CACF,CAAC;CAWH,OAAO;EACL,WAVgBC,iBAAAA,sBACV,iBACA;GACJ,IAAI,CAAC,aAAa,GAAG,MAAM,IAAI,MAAM,uDAAuD;EAC9F,CACF,CAAC,CAAC,KAAK,UAAU;GACf,GAAG;GACH,MAAM;IAAE,OAAO,EAAE,cAAcJ,iBAAAA,eAAe;IAAG,aAAa;KAAE,MAAM;KAAkB,MAAMA,iBAAAA;IAAe;GAAE;EACjH,EAEU;EACR,UAAU,EACP,YAAY;GACX,QAAQ,QAAQ,aAAa;IAC3B,SAAS,QAAQK,sBAAAA,6BAA6B,SAAS;GACzD,CAAC;GACD,QAAQ,aAAa,KAAK,mBAAmB,OAAO,CAAC;EACvD,IAAA,GACAC,6BAAAA,iBAAAA,CAAiB,CAAC,SAAS,CAAC,CAC9B;EACA,WAAW,CACT;GACE,MAAM;IAAE,OAAO,EAAE,cAAcN,iBAAAA,eAAe;IAAG,aAAa;KAAE,MAAM;KAAS,MAAMA,iBAAAA;IAAe;GAAE;GACtG,MAAM;GACN,MAAM;GACN,aAAA,GAAYO,mCAAAA,oBAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAAqC,sCAAsC;EACzF,CACF;EACA,YAAY,CACV;GACE,MAAM;GACN,MAAM,QAAQ;IACZ,QAAQ;IACR,OAAO,YAAY;KACjB,MAAM,UAAU,QAAQ,gCAAgC;KACxD,QAAQ;IACV;GACF;EACF,CACF;EACA,UAAU,CACR;GACE,MAAMC,iBAAAA;GACN,aAAa;GACb,MAAM,QAAQ,MAAc,WAAyC;IACnE,MAAM,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM,MAAM;IACtC,IAAI,MAAM,OAAO,UAAU,MAAM,WAAW,GAAG;KAC7C,MAAM,UAAU,MAAM,UAAU,KAAK,MAAM,IAAK,mBAAmB;KACnE,MAAM,UAAU,OAAO,OAAO;MAC5B,MAAM,UAAU,kBAAkB;MAClC,OAAO;KACT,CAAC;KACD;IACF;IACA,IAAI,KAAK,KAAK,GAAG,MAAM,IAAI,MAAM,iCAAiC;IAClE,MAAM,YAAY,UAAU,cAAc;IAC1C,MAAM,UAAU,OAAO,OAAO;KAC5B,MAAM,UAAU,WAAW,IAAI,yBAAyB,KAAK,UAAU,WAAW,MAAM,CAAC;KACzF,OAAO;IACT,CAAC;GACH;EACF,CACF;EACA,OAAO,CACL;GACE,OAAO;GACP,MAAM,SAAS;IACb,QAAQ;GACV;EACF,GACA;GACE,OAAO;GACP,MAAM,SAAS;IACb,MAAM,UAAU,QAAQ,4BAA4B;GACtD;EACF,CACF;EACA,SAAS;EACT,MAAM,YAAY;GAChB,IAAI,UAAU;GACd,WAAW;GACX,YAAY;GACZ,MAAM,UAAU,QAAQ,+BAA+B;EACzD;CACF;AACF;;;AClPA,IAAA,2BAAiB,EAAE,YACjB,QAAQ,mBAAmB,KAAK,IAAI,EAAE,WAAW,CAAC,EAAE;;;ACAtD,IAAA,uBAAA,GAAeC,qCAAAA,WAAAA,EAAY,YAAqC;CAC9D,MAAM,QAAQC,uBAAQ,OAAO;CAC7B,OAAO;EACL;EACA,UAAU,MAAM;EAChB,YAAY,MAAM;EAClB,SAAS,MAAM;EACf,QAAQ,MAAM;EACd,WAAW,MAAM;CACnB;AACF,CAAC;;;ACZD,IAAA,wBAAA,GAAeC,qCAAAA,yBAAAA,EACZ,YAAiF,QAAQ,KAAK,WAAW,IAC1G,EAAE,aAAa,WAAW,CAC5B;;;ACHA,IAAA,mCAAA,GAAeC,qCAAAA,yBAAAA,EACZ,YACC,QAAQ,KAAK,OAAO,MAAM,SAAS,KAAK,UAAU,kBAAkB,GACtE,EAAE,aAAa,WAAW,CAC5B;;;ACJA,IAAA,gCAAA,GAAeC,qCAAAA,yBAAAA,EACZ,YACC,QAAQ,KAAK,OAAO,MAAM,SAAS,KAAK,UAAU,eAAe,GACnE,EAAE,aAAa,WAAW,CAC5B;;;ACJA,IAAA,kCAAA,GAAeC,qCAAAA,yBAAAA,EACZ,YAAiF,QAAQ,KAAK,YAAY,IAC3G,EAAE,aAAa,WAAW,CAC5B;;;ACFA,IAAA,4BAAA,GAAeC,qCAAAA,WAAAA,EAAY,YAA8C,QAAQ,KAAK,UAAU,EAAG;;;ACAnG,IAAA,6BAAA,GAAeC,qCAAAA,WAAAA,EAAY,YAA8C,QAAQ,KAAK,UAAU,EAAG;;;ACAnG,IAAA,4BAAA,GAAeC,qCAAAA,WAAAA,EAAY,YAA8C,QAAQ,KAAK,UAAU,EAAG;;;ACUnG,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;;AAEpE,MAAM,WAAc,WAClB,OAAO,QAAQ,UAAsB,UAAU,KAAA,CAAS;AAC1D,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,SAAA,GAAQC,mCAAAA,mBAAAA,CAAmB;CACtC,MAAM;CACN,SAAS,OAAO,YAAY;EAC1B,MAAM,eAAe,MAAMC,oBAAY,OAAO;EAC9C,MAAM,iBAAiB;GAAE,GAAG;GAAS,MAAM,aAAa;EAAM;EAC9D,OAAO;GACL,IAAA,GAAGC,qCAAAA,iBAAAA,CAAiB,YAAY;GAChC,IAAI,WAAgD;IAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,QAAQ,GAAG,GAAGC,sBAAc,cAAc,CAAC,CAAC,CAAC;GAAG;GAClI,IAAI,QAA0C;IAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,OAAO,mBAAmB,GAAG,GAAGC,iCAAqB,cAAc,CAAC,GAAG,IAAI,EAAE,OAAO,gBAAgB,GAAG,GAAGC,8BAAkB,cAAc,CAAC,CAAC,CAAC;GAAG;GACtN,IAAI,YAAkD;IAAE,OAAO,QAAQ,CAAC,GAAGC,gCAAuB,cAAc,CAAC,CAAC;GAAG;GACrH,IAAI,QAA0C;IAAE,OAAO;KAAC,IAAI,EAAE,MAAM,YAAY,GAAG,GAAGC,0BAAc,cAAc,CAAC;KAAG,IAAI,EAAE,MAAM,aAAa,GAAG,GAAGC,2BAAe,cAAc,CAAC;KAAG,IAAI,EAAE,MAAM,YAAY,GAAG,GAAGC,0BAAc,cAAc,CAAC;IAAC;GAAG;GACrP,UAAU,CAAC,GAAI,aAAa,YAAY,CAAC,CAAE;GAC3C,IAAI,aAAoD;IAAE,OAAO,CAAC,GAAI,aAAa,cAAc,CAAC,CAAE;GAAG;EACzG;CACF;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAuBa,oDAAK"}
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAsBa,oDAAK"}
@@ -1,49 +1,14 @@
1
1
  import { t as DOOM_LOOP_LAUNCHERS_SERVICE } from "../loopLaunchers-cCniFbVA.mjs";
2
- import { _ as PACKAGE_SOURCE, a as CronLoopSchema, c as parseIntervalLoop, f as START_COMMAND_NAME, i as createDoomLoopLaunchersService, m as MODE_STATUS_ID, n as formatLoopStatusView, o as IntervalLoopSchema, r as createLoopTools, s as parseCronLoop, t as LOOP_VIEW_STATUS_KEY, u as LIST_COMMAND_NAME } from "../loopView-BK4X_1UI.mjs";
2
+ import { _ as PACKAGE_SOURCE, a as CronLoopSchema, c as parseIntervalLoop, i as createDoomLoopLaunchersService, m as MODE_STATUS_ID, n as formatLoopStatusView, o as IntervalLoopSchema, r as createLoopTools, s as parseCronLoop, t as LOOP_VIEW_STATUS_KEY, u as LIST_COMMAND_NAME } from "../loopView-DUR63zT7.mjs";
3
3
  import { composeRootHooks, defineRoot, defineRoutedContribution, defineTool } from "@agimon-ai/doompi-core/extensionFile";
4
4
  import { defineMinorMode, serverMinorModes } from "@agimon-ai/doompi-minor-mode";
5
5
  import { Cron } from "croner";
6
6
  import { defineServerPlugin, readPackageResource } from "@agimon-ai/doompi-core/serverFacet";
7
7
  //#region src/services/serverRuntime/index.ts
8
- async function prepareLoop(execution, request, cron) {
9
- if (request.input !== void 0) return cron ? parseCronLoop(request.input) : parseIntervalLoop(request.input);
10
- if (request.interactive === false) throw new Error("Loop configuration is required for agent launches.");
11
- const input = await execution.client.request({
12
- kind: "input",
13
- title: "Loop prompt",
14
- multiline: true
15
- }, request.signal);
16
- const prompt = typeof input === "string" ? input.trim() : "";
17
- if (!prompt || request.signal.aborted) return void 0;
18
- if (cron) {
19
- const expression = await execution.client.request({
20
- kind: "input",
21
- title: "Cron expression (five fields)",
22
- initialValue: "0 * * * *"
23
- }, request.signal);
24
- if (typeof expression !== "string" || request.signal.aborted) return void 0;
25
- const timezone = await execution.client.request({
26
- kind: "input",
27
- title: "Loop timezone",
28
- initialValue: "UTC"
29
- }, request.signal);
30
- if (typeof timezone !== "string" || request.signal.aborted) return void 0;
31
- return parseCronLoop({
32
- prompt,
33
- cron: expression,
34
- timezone: timezone.trim() || "UTC"
35
- });
36
- }
37
- const interval = await execution.client.request({
38
- kind: "input",
39
- title: "Loop interval in seconds",
40
- initialValue: "300"
41
- }, request.signal);
42
- if (typeof interval !== "string" || request.signal.aborted) return void 0;
43
- return parseIntervalLoop({
44
- prompt,
45
- intervalSeconds: Number(interval.trim() || "300")
46
- });
8
+ /** Loops are set by the agent, so a launch always carries its configuration. */
9
+ function prepareLoop(request, cron) {
10
+ if (request.input === void 0) throw new Error("Loop configuration is required. Ask the agent to set the loop.");
11
+ return cron ? parseCronLoop(request.input) : parseIntervalLoop(request.input);
47
12
  }
48
13
  function createSessionState(host) {
49
14
  const launchers = createDoomLoopLaunchersService({
@@ -52,7 +17,7 @@ function createSessionState(host) {
52
17
  timestamp: () => (/* @__PURE__ */ new Date()).toISOString()
53
18
  });
54
19
  let disposed = false;
55
- const modeSelected = () => !disposed && (host.context.selection.state?.["minor-mode"] ?? []).includes("loop.active");
20
+ const modeSelected = () => !disposed && (host.context.selection.state?.["minor-mode"] ?? []).includes("loop");
56
21
  const publishView = () => host.context.client.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(launchers.listInstances()));
57
22
  const modeOwner = defineMinorMode({
58
23
  descriptor: {
@@ -120,7 +85,7 @@ function createSessionState(host) {
120
85
  inputSchema: { ...cron ? CronLoopSchema : IntervalLoopSchema },
121
86
  async launch(request) {
122
87
  const execution = host.context;
123
- const prepared = await prepareLoop(execution, request, cron);
88
+ const prepared = prepareLoop(request, cron);
124
89
  if (!prepared || request.signal.aborted) return void 0;
125
90
  let stopped = false;
126
91
  const tick = async () => {
@@ -216,26 +181,6 @@ function createSessionState(host) {
216
181
  }
217
182
  }],
218
183
  commands: [{
219
- name: START_COMMAND_NAME,
220
- description: "Start a registered loop manually.",
221
- async execute(args, execution) {
222
- const available = launchers.listLaunchers();
223
- const selected = args.trim() || await execution.client.request({
224
- kind: "select",
225
- title: "Choose a loop launcher",
226
- options: available.map((entry) => ({
227
- label: entry.label,
228
- value: entry.id
229
- }))
230
- });
231
- if (typeof selected !== "string" || !selected) return;
232
- const instance = await launchers.launch(selected);
233
- await execution.client.notify({
234
- body: instance ? `Loop '${instance.instanceId}' started.` : "Loop launch was cancelled.",
235
- level: "info"
236
- });
237
- }
238
- }, {
239
184
  name: LIST_COMMAND_NAME,
240
185
  description: "List or stop session loops. Usage: /loops [stop instanceId].",
241
186
  async execute(args, execution) {
@@ -293,11 +238,8 @@ var root_server_default = defineRoot((context) => {
293
238
  };
294
239
  });
295
240
  //#endregion
296
- //#region src/extensions/workspaces/sessions/(backend)/command/loop.server.ts
297
- var loop_server_default = defineRoutedContribution((context) => context.root.commands?.[0], { cardinality: "optional" });
298
- //#endregion
299
241
  //#region src/extensions/workspaces/sessions/(backend)/command/loops.server.ts
300
- var loops_server_default = defineRoutedContribution((context) => context.root.commands?.[1], { cardinality: "optional" });
242
+ var loops_server_default = defineRoutedContribution((context) => context.root.commands?.[0], { cardinality: "optional" });
301
243
  //#endregion
302
244
  //#region src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts
303
245
  var session_shutdown_server_default = defineRoutedContribution((context) => context.root.hooks?.find((hook) => hook.event === "session_shutdown"), { cardinality: "optional" });
@@ -336,7 +278,7 @@ const facet = defineServerPlugin({
336
278
  return {
337
279
  ...composeRootHooks(scopeSession),
338
280
  get commands() {
339
- return defined([via({ name: "loop" }, at(loop_server_default, contextSession)), via({ name: "loops" }, at(loops_server_default, contextSession))]);
281
+ return defined([via({ name: "loops" }, at(loops_server_default, contextSession))]);
340
282
  },
341
283
  get hooks() {
342
284
  return defined([via({ event: "session_shutdown" }, at(session_shutdown_server_default, contextSession)), via({ event: "session_start" }, at(session_start_server_default, contextSession))]);
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":["runtime","rootSession","commandLoop","commandLoops","hookSessionShutdown","hookSessionStart","resourceDoompiUseLoop","toolLoopList","toolLoopStart","toolLoopStop"],"sources":["../../src/services/serverRuntime/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/loop.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/loops.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-list.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server.ts","../../generated/server.ts"],"sourcesContent":["import type { DoomHeadlessExecutionContext, DoomHeadlessHostService } from '@agimon-ai/doompi-core/headless';\nimport { readPackageResource, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { defineMinorMode, serverMinorModes } from '@agimon-ai/doompi-minor-mode';\nimport { Cron } from 'croner';\n\nimport { LIST_COMMAND_NAME, START_COMMAND_NAME } from '../../constants/loop';\nimport { MODE_STATUS_ID, PACKAGE_SOURCE } from '../../constants/piLoop';\nimport { DOOM_LOOP_LAUNCHERS_SERVICE, type LoopLaunchRequest } from '../../schemas/loopLaunchers';\nimport { CronLoopSchema, IntervalLoopSchema, parseCronLoop, parseIntervalLoop } from '../../schemas/loopTools';\nimport { formatLoopStatusView, LOOP_VIEW_STATUS_KEY } from '../../types/loopView';\nimport { createDoomLoopLaunchersService } from '../loopLaunchers';\nimport { createLoopTools } from '../loopTools';\nimport type { LoopToolsRoot } from '../loopTools/type';\n\nexport type LoopServerState = DoomServerSessionPlugin & LoopToolsRoot;\n\nasync function prepareLoop(execution: DoomHeadlessExecutionContext, request: LoopLaunchRequest, cron: boolean) {\n if (request.input !== undefined) return cron ? parseCronLoop(request.input) : parseIntervalLoop(request.input);\n if (request.interactive === false) throw new Error('Loop configuration is required for agent launches.');\n const input = await execution.client.request(\n { kind: 'input', title: 'Loop prompt', multiline: true },\n request.signal,\n );\n const prompt = typeof input === 'string' ? input.trim() : '';\n if (!prompt || request.signal.aborted) return undefined;\n if (cron) {\n const expression = await execution.client.request(\n { kind: 'input', title: 'Cron expression (five fields)', initialValue: '0 * * * *' },\n request.signal,\n );\n if (typeof expression !== 'string' || request.signal.aborted) return undefined;\n const timezone = await execution.client.request(\n { kind: 'input', title: 'Loop timezone', initialValue: 'UTC' },\n request.signal,\n );\n if (typeof timezone !== 'string' || request.signal.aborted) return undefined;\n return parseCronLoop({ prompt, cron: expression, timezone: timezone.trim() || 'UTC' });\n }\n const interval = await execution.client.request(\n { kind: 'input', title: 'Loop interval in seconds', initialValue: '300' },\n request.signal,\n );\n if (typeof interval !== 'string' || request.signal.aborted) return undefined;\n return parseIntervalLoop({ prompt, intervalSeconds: Number(interval.trim() || '300') });\n}\n\nexport function createSessionState(host: DoomHeadlessHostService): LoopServerState {\n const launchers = createDoomLoopLaunchersService({\n generation: `${host.context.sessionId}:loop-launchers`,\n createInstanceId: () => crypto.randomUUID(),\n timestamp: () => new Date().toISOString(),\n });\n let disposed = false;\n const modeSelected = (): boolean =>\n !disposed && (host.context.selection.state?.['minor-mode'] ?? []).includes(MODE_STATUS_ID);\n const publishView = (): void =>\n host.context.client.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(launchers.listInstances()));\n const modeOwner = defineMinorMode({\n descriptor: {\n source: PACKAGE_SOURCE,\n id: MODE_STATUS_ID,\n label: 'Loop',\n description: 'Give the agent tools to create, inspect, and stop session loops.',\n order: 60,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Expose loop tools to the agent.',\n contexts: ['headless'],\n parameters: [],\n },\n {\n id: 'deactivate',\n label: 'Deactivate',\n description: 'Hide agent loop tools. Existing loops and manual controls remain available.',\n contexts: ['headless'],\n parameters: [],\n },\n ],\n },\n state: () => ({\n activation: modeSelected() ? 'active' : 'inactive',\n condition: 'ready',\n ...(modeSelected() ? { detail: `${launchers.listInstances().length} loops`, color: 'accent' } : {}),\n actions: [\n {\n id: 'activate',\n enabled: !modeSelected(),\n ...(modeSelected() ? { disabledReason: 'Loop mode is already active.' } : {}),\n },\n {\n id: 'deactivate',\n enabled: modeSelected(),\n ...(modeSelected() ? {} : { disabledReason: 'Loop mode is not active.' }),\n },\n ],\n }),\n async handleAction(_runtime, actionId, _arguments, { signal }) {\n signal.throwIfAborted();\n if (actionId !== 'activate' && actionId !== 'deactivate')\n throw new Error(`Unknown loop mode action: ${actionId}`);\n const modes = (host.context.selection.state?.['minor-mode'] ?? []).filter((mode) => mode !== MODE_STATUS_ID);\n await host.changeSelection({\n axis: 'state',\n key: 'minor-mode',\n values: actionId === 'activate' ? [...modes, MODE_STATUS_ID] : modes,\n });\n modeOwner.publish();\n return {\n message:\n actionId === 'activate' ? 'Loop tools activated.' : 'Loop tools deactivated. Existing loops are unchanged.',\n };\n },\n }).createOwner(undefined);\n const publish = (): void => {\n if (disposed) return;\n modeOwner.publish();\n publishView();\n };\n const unsubscribe = launchers.subscribe(publish);\n let submitting = false;\n for (const cron of [false, true]) {\n launchers.register({\n id: cron ? 'doompi.cron' : 'doompi.default',\n source: PACKAGE_SOURCE,\n label: cron ? 'Cron loop' : 'Default loop',\n description: cron\n ? 'Run a prompt on a five-field cron schedule.'\n : 'Run a prompt in this session on an interval.',\n inputSchema: { ...(cron ? CronLoopSchema : IntervalLoopSchema) },\n async launch(request) {\n const execution = host.context;\n const prepared = await prepareLoop(execution, request, cron);\n if (!prepared || request.signal.aborted) return undefined;\n let stopped = false;\n const tick = async (): Promise<void> => {\n if (stopped || request.signal.aborted || submitting) return;\n submitting = true;\n try {\n const activity = await execution.session.activity();\n if (stopped || request.signal.aborted || !activity.isIdle || activity.hasPendingMessages) return;\n await execution.session.prompt(prepared.prompt);\n } catch (error) {\n await execution.client.notify({\n body: `Loop pass failed: ${error instanceof Error ? error.message : String(error)}`,\n level: 'warning',\n });\n } finally {\n submitting = false;\n }\n };\n let stopTimer: () => void;\n let detail: string;\n if ('cron' in prepared) {\n const job = new Cron(prepared.cron, { timezone: prepared.timezone, unref: true }, tick);\n if (job.nextRun() === null) {\n job.stop();\n throw new Error('Cron expression has no future run.');\n }\n stopTimer = () => {\n job.stop();\n };\n detail = `${prepared.cron} (${prepared.timezone})`;\n } else {\n const timer = setInterval(() => {\n void tick();\n }, prepared.intervalSeconds * 1000);\n timer.unref?.();\n stopTimer = () => clearInterval(timer);\n detail = `every ${prepared.intervalSeconds}s`;\n void tick();\n }\n return {\n instanceId: request.instanceId,\n label: cron ? 'Cron loop' : 'Default loop',\n detail,\n stop() {\n stopped = true;\n stopTimer();\n },\n };\n },\n });\n }\n const loopTools = createLoopTools(\n () => launchers,\n () => {\n if (!modeSelected()) throw new Error('Enable Loop minor mode before using agent loop tools.');\n },\n ).map((tool) => ({\n ...tool,\n when: { state: { 'minor-mode': MODE_STATUS_ID }, attribution: { kind: 'minor' as const, mode: MODE_STATUS_ID } },\n }));\n return {\n loopTools,\n services: [\n (context) => {\n context.plugin((provider) => {\n provider.provide(DOOM_LOOP_LAUNCHERS_SERVICE, launchers);\n });\n context.effect(() => host.subscribeSelection(publish));\n },\n serverMinorModes([modeOwner]),\n ],\n resources: [\n {\n when: { state: { 'minor-mode': MODE_STATUS_ID }, attribution: { kind: 'minor', mode: MODE_STATUS_ID } },\n name: 'doompi-use-loop',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-loop/SKILL.md'),\n },\n ],\n activities: [\n {\n name: 'doompi-loop',\n async start() {\n publish();\n return async () => {\n await launchers.stopAll('Session loop activity stopped.');\n publish();\n };\n },\n },\n ],\n commands: [\n {\n name: START_COMMAND_NAME,\n description: 'Start a registered loop manually.',\n async execute(args: string, execution: DoomHeadlessExecutionContext) {\n const available = launchers.listLaunchers();\n const selected =\n args.trim() ||\n (await execution.client.request({\n kind: 'select',\n title: 'Choose a loop launcher',\n options: available.map((entry) => ({ label: entry.label, value: entry.id })),\n }));\n if (typeof selected !== 'string' || !selected) return;\n const instance = await launchers.launch(selected);\n await execution.client.notify({\n body: instance ? `Loop '${instance.instanceId}' started.` : 'Loop launch was cancelled.',\n level: 'info',\n });\n },\n },\n {\n name: LIST_COMMAND_NAME,\n description: 'List or stop session loops. Usage: /loops [stop instanceId].',\n async execute(args: string, execution: DoomHeadlessExecutionContext) {\n const parts = args.trim().split(/\\s+/u);\n if (parts[0] === 'stop' && parts.length === 2) {\n const stopped = await launchers.stop(parts[1]!, 'Stopped manually.');\n await execution.client.notify({\n body: stopped ? 'Loop stopped.' : 'Loop instance was not active.',\n level: 'info',\n });\n return;\n }\n if (args.trim()) throw new Error('Usage: /loops [stop instanceId]');\n const instances = launchers.listInstances();\n await execution.client.notify({\n body: instances.length === 0 ? 'No loops are active.' : JSON.stringify(instances, null, 2),\n level: 'info',\n });\n },\n },\n ],\n hooks: [\n {\n event: 'session_start',\n async handle() {\n publish();\n },\n },\n {\n event: 'session_shutdown',\n async handle() {\n await launchers.stopAll('Headless session shutdown.');\n },\n },\n ],\n onStart: publish,\n async onDispose() {\n if (disposed) return;\n disposed = true;\n unsubscribe();\n await launchers.dispose('Headless loop facet disposed.');\n },\n };\n}\n","import type { DoomServerPluginDefinition } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { createSessionState, type LoopServerState } from '../../../../../services/serverRuntime';\n\nexport default (({ agent }): LoopServerState =>\n agent ? createSessionState(agent) : { loopTools: [] }) satisfies NonNullable<DoomServerPluginDefinition['session']>;\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/doompi-core/serverFacet';\n\nimport runtime from './_lib/runtime.server';\n\nexport default defineRoot((context: DoomServerPluginContext) => {\n const value = runtime(context);\n return {\n value,\n services: value.services,\n activities: value.activities,\n onStart: value.onStart,\n onStop: value.onStop,\n onDispose: value.onDispose,\n };\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.commands?.[0],\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.commands?.[1],\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) =>\n context.root.hooks?.find((hook) => hook.event === 'session_shutdown'),\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) =>\n context.root.hooks?.find((hook) => hook.event === 'session_start'),\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.resources?.[0],\n { cardinality: 'optional' },\n);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[0]!);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[1]!);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[2]!);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandLoop from '../src/extensions/workspaces/sessions/(backend)/command/loop.server';\nimport commandLoops from '../src/extensions/workspaces/sessions/(backend)/command/loops.server';\nimport hookSessionShutdown from '../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/session-start.server';\nimport resourceDoompiUseLoop from '../src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server';\nimport toolLoopList from '../src/extensions/workspaces/sessions/(backend)/tool/loop-list.server';\nimport toolLoopStart from '../src/extensions/workspaces/sessions/(backend)/tool/loop-start.server';\nimport toolLoopStop from '../src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/doompi-loop',\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return defined([via({ name: 'loop' }, at(commandLoop, contextSession)), via({ name: 'loops' }, at(commandLoops, contextSession))]); },\n get hooks(): DoomServerSessionPlugin['hooks'] { return defined([via({ event: 'session_shutdown' }, at(hookSessionShutdown, contextSession)), via({ event: 'session_start' }, at(hookSessionStart, contextSession))]); },\n get resources(): DoomServerSessionPlugin['resources'] { return defined([at(resourceDoompiUseLoop, contextSession)]); },\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'loop_list' }, at(toolLoopList, contextSession)), via({ name: 'loop_start' }, at(toolLoopStart, contextSession)), via({ name: 'loop_stop' }, at(toolLoopStop, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","names":["runtime","rootSession","commandLoops","hookSessionShutdown","hookSessionStart","resourceDoompiUseLoop","toolLoopList","toolLoopStart","toolLoopStop"],"sources":["../../src/services/serverRuntime/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/loops.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-list.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server.ts","../../generated/server.ts"],"sourcesContent":["import type { DoomHeadlessExecutionContext, DoomHeadlessHostService } from '@agimon-ai/doompi-core/headless';\nimport { readPackageResource, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { defineMinorMode, serverMinorModes } from '@agimon-ai/doompi-minor-mode';\nimport { Cron } from 'croner';\n\nimport { LIST_COMMAND_NAME } from '../../constants/loop';\nimport { MODE_STATUS_ID, PACKAGE_SOURCE } from '../../constants/piLoop';\nimport { DOOM_LOOP_LAUNCHERS_SERVICE, type LoopLaunchRequest } from '../../schemas/loopLaunchers';\nimport { CronLoopSchema, IntervalLoopSchema, parseCronLoop, parseIntervalLoop } from '../../schemas/loopTools';\nimport { formatLoopStatusView, LOOP_VIEW_STATUS_KEY } from '../../types/loopView';\nimport { createDoomLoopLaunchersService } from '../loopLaunchers';\nimport { createLoopTools } from '../loopTools';\nimport type { LoopToolsRoot } from '../loopTools/type';\n\nexport type LoopServerState = DoomServerSessionPlugin & LoopToolsRoot;\n\n/** Loops are set by the agent, so a launch always carries its configuration. */\nfunction prepareLoop(request: LoopLaunchRequest, cron: boolean) {\n if (request.input === undefined) throw new Error('Loop configuration is required. Ask the agent to set the loop.');\n return cron ? parseCronLoop(request.input) : parseIntervalLoop(request.input);\n}\n\nexport function createSessionState(host: DoomHeadlessHostService): LoopServerState {\n const launchers = createDoomLoopLaunchersService({\n generation: `${host.context.sessionId}:loop-launchers`,\n createInstanceId: () => crypto.randomUUID(),\n timestamp: () => new Date().toISOString(),\n });\n let disposed = false;\n const modeSelected = (): boolean =>\n !disposed && (host.context.selection.state?.['minor-mode'] ?? []).includes(MODE_STATUS_ID);\n const publishView = (): void =>\n host.context.client.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(launchers.listInstances()));\n const modeOwner = defineMinorMode({\n descriptor: {\n source: PACKAGE_SOURCE,\n id: MODE_STATUS_ID,\n label: 'Loop',\n description: 'Give the agent tools to create, inspect, and stop session loops.',\n order: 60,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Expose loop tools to the agent.',\n contexts: ['headless'],\n parameters: [],\n },\n {\n id: 'deactivate',\n label: 'Deactivate',\n description: 'Hide agent loop tools. Existing loops and manual controls remain available.',\n contexts: ['headless'],\n parameters: [],\n },\n ],\n },\n state: () => ({\n activation: modeSelected() ? 'active' : 'inactive',\n condition: 'ready',\n ...(modeSelected() ? { detail: `${launchers.listInstances().length} loops`, color: 'accent' } : {}),\n actions: [\n {\n id: 'activate',\n enabled: !modeSelected(),\n ...(modeSelected() ? { disabledReason: 'Loop mode is already active.' } : {}),\n },\n {\n id: 'deactivate',\n enabled: modeSelected(),\n ...(modeSelected() ? {} : { disabledReason: 'Loop mode is not active.' }),\n },\n ],\n }),\n async handleAction(_runtime, actionId, _arguments, { signal }) {\n signal.throwIfAborted();\n if (actionId !== 'activate' && actionId !== 'deactivate')\n throw new Error(`Unknown loop mode action: ${actionId}`);\n const modes = (host.context.selection.state?.['minor-mode'] ?? []).filter((mode) => mode !== MODE_STATUS_ID);\n await host.changeSelection({\n axis: 'state',\n key: 'minor-mode',\n values: actionId === 'activate' ? [...modes, MODE_STATUS_ID] : modes,\n });\n modeOwner.publish();\n return {\n message:\n actionId === 'activate' ? 'Loop tools activated.' : 'Loop tools deactivated. Existing loops are unchanged.',\n };\n },\n }).createOwner(undefined);\n const publish = (): void => {\n if (disposed) return;\n modeOwner.publish();\n publishView();\n };\n const unsubscribe = launchers.subscribe(publish);\n let submitting = false;\n for (const cron of [false, true]) {\n launchers.register({\n id: cron ? 'doompi.cron' : 'doompi.default',\n source: PACKAGE_SOURCE,\n label: cron ? 'Cron loop' : 'Default loop',\n description: cron\n ? 'Run a prompt on a five-field cron schedule.'\n : 'Run a prompt in this session on an interval.',\n inputSchema: { ...(cron ? CronLoopSchema : IntervalLoopSchema) },\n async launch(request) {\n const execution = host.context;\n const prepared = prepareLoop(request, cron);\n if (!prepared || request.signal.aborted) return undefined;\n let stopped = false;\n const tick = async (): Promise<void> => {\n if (stopped || request.signal.aborted || submitting) return;\n submitting = true;\n try {\n const activity = await execution.session.activity();\n if (stopped || request.signal.aborted || !activity.isIdle || activity.hasPendingMessages) return;\n await execution.session.prompt(prepared.prompt);\n } catch (error) {\n await execution.client.notify({\n body: `Loop pass failed: ${error instanceof Error ? error.message : String(error)}`,\n level: 'warning',\n });\n } finally {\n submitting = false;\n }\n };\n let stopTimer: () => void;\n let detail: string;\n if ('cron' in prepared) {\n const job = new Cron(prepared.cron, { timezone: prepared.timezone, unref: true }, tick);\n if (job.nextRun() === null) {\n job.stop();\n throw new Error('Cron expression has no future run.');\n }\n stopTimer = () => {\n job.stop();\n };\n detail = `${prepared.cron} (${prepared.timezone})`;\n } else {\n const timer = setInterval(() => {\n void tick();\n }, prepared.intervalSeconds * 1000);\n timer.unref?.();\n stopTimer = () => clearInterval(timer);\n detail = `every ${prepared.intervalSeconds}s`;\n void tick();\n }\n return {\n instanceId: request.instanceId,\n label: cron ? 'Cron loop' : 'Default loop',\n detail,\n stop() {\n stopped = true;\n stopTimer();\n },\n };\n },\n });\n }\n const loopTools = createLoopTools(\n () => launchers,\n () => {\n if (!modeSelected()) throw new Error('Enable Loop minor mode before using agent loop tools.');\n },\n ).map((tool) => ({\n ...tool,\n when: { state: { 'minor-mode': MODE_STATUS_ID }, attribution: { kind: 'minor' as const, mode: MODE_STATUS_ID } },\n }));\n return {\n loopTools,\n services: [\n (context) => {\n context.plugin((provider) => {\n provider.provide(DOOM_LOOP_LAUNCHERS_SERVICE, launchers);\n });\n context.effect(() => host.subscribeSelection(publish));\n },\n serverMinorModes([modeOwner]),\n ],\n resources: [\n {\n when: { state: { 'minor-mode': MODE_STATUS_ID }, attribution: { kind: 'minor', mode: MODE_STATUS_ID } },\n name: 'doompi-use-loop',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-loop/SKILL.md'),\n },\n ],\n activities: [\n {\n name: 'doompi-loop',\n async start() {\n publish();\n return async () => {\n await launchers.stopAll('Session loop activity stopped.');\n publish();\n };\n },\n },\n ],\n commands: [\n {\n name: LIST_COMMAND_NAME,\n description: 'List or stop session loops. Usage: /loops [stop instanceId].',\n async execute(args: string, execution: DoomHeadlessExecutionContext) {\n const parts = args.trim().split(/\\s+/u);\n if (parts[0] === 'stop' && parts.length === 2) {\n const stopped = await launchers.stop(parts[1]!, 'Stopped manually.');\n await execution.client.notify({\n body: stopped ? 'Loop stopped.' : 'Loop instance was not active.',\n level: 'info',\n });\n return;\n }\n if (args.trim()) throw new Error('Usage: /loops [stop instanceId]');\n const instances = launchers.listInstances();\n await execution.client.notify({\n body: instances.length === 0 ? 'No loops are active.' : JSON.stringify(instances, null, 2),\n level: 'info',\n });\n },\n },\n ],\n hooks: [\n {\n event: 'session_start',\n async handle() {\n publish();\n },\n },\n {\n event: 'session_shutdown',\n async handle() {\n await launchers.stopAll('Headless session shutdown.');\n },\n },\n ],\n onStart: publish,\n async onDispose() {\n if (disposed) return;\n disposed = true;\n unsubscribe();\n await launchers.dispose('Headless loop facet disposed.');\n },\n };\n}\n","import type { DoomServerPluginDefinition } from '@agimon-ai/doompi-core/serverFacet';\n\nimport { createSessionState, type LoopServerState } from '../../../../../services/serverRuntime';\n\nexport default (({ agent }): LoopServerState =>\n agent ? createSessionState(agent) : { loopTools: [] }) satisfies NonNullable<DoomServerPluginDefinition['session']>;\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/doompi-core/serverFacet';\n\nimport runtime from './_lib/runtime.server';\n\nexport default defineRoot((context: DoomServerPluginContext) => {\n const value = runtime(context);\n return {\n value,\n services: value.services,\n activities: value.activities,\n onStart: value.onStart,\n onStop: value.onStop,\n onDispose: value.onDispose,\n };\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.commands?.[0],\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) =>\n context.root.hooks?.find((hook) => hook.event === 'session_shutdown'),\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) =>\n context.root.hooks?.find((hook) => hook.event === 'session_start'),\n { cardinality: 'optional' },\n);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\n\nexport default defineRoutedContribution(\n (context: WithRoot<DoomServerPluginContext, Partial<DoomServerSessionPlugin>>) => context.root.resources?.[0],\n { cardinality: 'optional' },\n);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[0]!);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[1]!);\n","import { defineTool, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\n\nimport type { LoopToolsRoot } from '../../../../../services/loopTools/type';\n\nexport default defineTool((context: WithRoot<unknown, LoopToolsRoot>) => context.root.loopTools[2]!);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport commandLoops from '../src/extensions/workspaces/sessions/(backend)/command/loops.server';\nimport hookSessionShutdown from '../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/session-start.server';\nimport resourceDoompiUseLoop from '../src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server';\nimport toolLoopList from '../src/extensions/workspaces/sessions/(backend)/tool/loop-list.server';\nimport toolLoopStart from '../src/extensions/workspaces/sessions/(backend)/tool/loop-start.server';\nimport toolLoopStop from '../src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/doompi-loop',\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get commands(): DoomServerSessionPlugin['commands'] { return defined([via({ name: 'loops' }, at(commandLoops, contextSession))]); },\n get hooks(): DoomServerSessionPlugin['hooks'] { return defined([via({ event: 'session_shutdown' }, at(hookSessionShutdown, contextSession)), via({ event: 'session_start' }, at(hookSessionStart, contextSession))]); },\n get resources(): DoomServerSessionPlugin['resources'] { return defined([at(resourceDoompiUseLoop, contextSession)]); },\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'loop_list' }, at(toolLoopList, contextSession)), via({ name: 'loop_start' }, at(toolLoopStart, contextSession)), via({ name: 'loop_stop' }, at(toolLoopStop, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
@@ -43,27 +43,8 @@ var loops_web_default = defineActivityGroup({
43
43
  });
44
44
  //#endregion
45
45
  //#region src/extensions/workspaces/sessions/(frontend)/fill/_components/LoopsActivitySection.tsx
46
- const MANAGE_COMMAND = "/loops";
47
- const DEFAULT_LOOP_COMMAND = "/loop doompi.default";
48
- /** Starts the built-in prompt loop without opening a generic launcher chooser. */
49
- function DefaultLoopLauncher({ sessionId, sendSessionFrame }) {
50
- return /* @__PURE__ */ jsx(Button, {
51
- variant: "subtle",
52
- size: "xs",
53
- "data-testid": "activity-loop-default-launch",
54
- "aria-label": "launch default loop",
55
- disabled: sessionId === null,
56
- onClick: () => {
57
- if (sessionId === null) return;
58
- sendSessionFrame(sessionId, {
59
- type: "prompt",
60
- message: DEFAULT_LOOP_COMMAND
61
- });
62
- },
63
- className: "text-2xs font-bold",
64
- children: "default loop"
65
- });
66
- }
46
+ /** The minor mode name the cockpit reports while the agent's loop tools are active. */
47
+ const LOOP_MODE = "loop";
67
48
  function toneOf(state) {
68
49
  if (state === "running") return "green";
69
50
  if (state === "stopping") return "orange";
@@ -122,67 +103,26 @@ function LoopActivityItems({ statuses, sessionId, sendSessionFrame }) {
122
103
  children: "loop status unavailable"
123
104
  });
124
105
  }
125
- /** Idle-safe Loop activity shell with extension registration and instance slots. */
126
- function LoopsActivitySection({ sessionId, renderSlot, sendSessionFrame }) {
106
+ /**
107
+ * The Loop activity section. The agent sets loops through its tools while the Loop minor mode is
108
+ * active; this section lists the session's loops whatever the mode, so any of them can be stopped.
109
+ */
110
+ function LoopsActivitySection({ statuses, activeMinorModes, renderSlot }) {
111
+ const modeActive = activeMinorModes?.includes(LOOP_MODE) === true;
112
+ const hasLoops = parseLoopStatusView(statuses[LOOP_VIEW_STATUS_KEY]) !== void 0;
113
+ let hint;
114
+ if (!modeActive) hint = "Activate loop minor mode to set loop or cron job.";
115
+ else if (!hasLoops) hint = "No loops yet. Ask the agent to set a loop or cron job.";
127
116
  return /* @__PURE__ */ jsxs("div", {
128
117
  "data-testid": "activity-loop-instances",
129
118
  className: "flex flex-col gap-2",
130
119
  children: [
131
- /* @__PURE__ */ jsxs("div", {
132
- className: "flex flex-wrap items-center gap-1",
133
- children: [
134
- /* @__PURE__ */ jsx(DefaultLoopLauncher, {
135
- sessionId,
136
- sendSessionFrame
137
- }),
138
- /* @__PURE__ */ jsx(Button, {
139
- variant: "subtle",
140
- size: "xs",
141
- "data-testid": "activity-loop-cron-launch",
142
- "aria-label": "launch cron loop",
143
- disabled: sessionId === null,
144
- onClick: () => {
145
- if (sessionId !== null) sendSessionFrame(sessionId, {
146
- type: "prompt",
147
- message: "/loop doompi.cron"
148
- });
149
- },
150
- className: "text-2xs font-bold",
151
- children: "cron loop"
152
- }),
153
- /* @__PURE__ */ jsx(Button, {
154
- variant: "subtle",
155
- size: "xs",
156
- "aria-label": "choose loop type",
157
- disabled: sessionId === null,
158
- onClick: () => {
159
- if (sessionId !== null) sendSessionFrame(sessionId, {
160
- type: "prompt",
161
- message: "/loop"
162
- });
163
- },
164
- className: "text-2xs font-bold",
165
- children: "choose type"
166
- }),
167
- renderSlot("loop.registration")
168
- ]
169
- }),
120
+ renderSlot("loop.registration"),
170
121
  renderSlot("loop.items"),
171
- /* @__PURE__ */ jsx(Button, {
172
- variant: "subtle",
173
- size: "xs",
174
- "data-testid": "activity-loops-manage",
175
- "aria-label": "manage loops",
176
- disabled: sessionId === null,
177
- onClick: () => {
178
- if (sessionId === null) return;
179
- sendSessionFrame(sessionId, {
180
- type: "prompt",
181
- message: MANAGE_COMMAND
182
- });
183
- },
184
- className: "self-end text-2xs font-bold",
185
- children: "manage"
122
+ hint === void 0 ? null : /* @__PURE__ */ jsx("p", {
123
+ "data-testid": "activity-loops-hint",
124
+ className: "px-1 text-xs text-doom-faint",
125
+ children: hint
186
126
  })
187
127
  ]
188
128
  });
@@ -208,20 +148,6 @@ var list_web_default = defineLeaderBinding({
208
148
  command: "loops"
209
149
  });
210
150
  //#endregion
211
- //#region src/extensions/workspaces/sessions/(frontend)/leader/start.web.ts
212
- var start_web_default = defineLeaderBinding({
213
- path: [{
214
- key: "l",
215
- label: "loops",
216
- detail: "recurring prompt loops"
217
- }, {
218
- key: "s",
219
- label: "start",
220
- detail: "begin a recurring loop"
221
- }],
222
- command: "loop"
223
- });
224
- //#endregion
225
151
  //#region src/extensions/workspaces/sessions/(frontend)/mode/loop/mode.web.ts
226
152
  var mode_web_default = defineMinorModeFile({
227
153
  keys: "l l",
@@ -308,7 +234,7 @@ const webPlugin = defineWebPlugin({
308
234
  }, Loops_activity_loops_web_default)];
309
235
  },
310
236
  get leaderBindings() {
311
- return [via({ id: "loop.list" }, list_web_default), via({ id: "loop.start" }, start_web_default)];
237
+ return [via({ id: "loop.list" }, list_web_default)];
312
238
  },
313
239
  get minorModes() {
314
240
  return [via({ name: "loop" }, mode_web_default)];
@@ -1 +1 @@
1
- {"version":3,"file":"web.mjs","names":["activityGroupLoops","fillInstances","fillLoops","leaderList","leaderStart","modeLoop","slotItems","slotRegistration","toolLoopList","toolLoopStart","toolLoopStop"],"sources":["../../src/types/loopView.ts","../../src/extensions/workspaces/sessions/(frontend)/activity-group/loops.web.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/_components/LoopsActivitySection.tsx","../../src/extensions/workspaces/sessions/(frontend)/fill/Instances.loop.items.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/fill/Loops.activity.loops.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/leader/list.web.ts","../../src/extensions/workspaces/sessions/(frontend)/leader/start.web.ts","../../src/extensions/workspaces/sessions/(frontend)/mode/loop/mode.web.ts","../../src/extensions/workspaces/sessions/(frontend)/slot/items.web.ts","../../src/extensions/workspaces/sessions/(frontend)/slot/registration.web.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/LoopToolCard.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/loop-list.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/loop-start.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/loop-stop.web.tsx","../../generated/web.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 {\n if (instances.length === 0) return '';\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","import { defineActivityGroup } from '@agimon-ai/doompi-core/web';\n\nimport { LOOP_VIEW_STATUS_KEY } from '../../../../../types/loopView';\n\nexport default defineActivityGroup({\n keys: 'l l',\n statusKey: LOOP_VIEW_STATUS_KEY,\n order: 40,\n});\n","import type { WebPluginSlotProps } from '@agimon-ai/doompi-core/web';\nimport { Button, Dot, type DotTone } from '@agimon-ai/doompi-web-components';\n\nimport { LOOP_VIEW_STATUS_KEY, parseLoopStatusView, type LoopStatusState } from '../../../../../../types/loopView';\n\nconst MANAGE_COMMAND = '/loops';\nconst DEFAULT_LOOP_COMMAND = '/loop doompi.default';\n\n/** Starts the built-in prompt loop without opening a generic launcher chooser. */\nexport function DefaultLoopLauncher({\n sessionId,\n sendSessionFrame,\n}: Pick<WebPluginSlotProps, 'sessionId' | 'sendSessionFrame'>) {\n return (\n <Button\n variant=\"subtle\"\n size=\"xs\"\n data-testid=\"activity-loop-default-launch\"\n aria-label=\"launch default loop\"\n disabled={sessionId === null}\n onClick={() => {\n if (sessionId === null) return;\n sendSessionFrame(sessionId, { type: 'prompt', message: DEFAULT_LOOP_COMMAND });\n }}\n className=\"text-2xs font-bold\"\n >\n default loop\n </Button>\n );\n}\n\nfunction toneOf(state: LoopStatusState): DotTone {\n if (state === 'running') return 'green';\n if (state === 'stopping') return 'orange';\n return 'yellow';\n}\n\n/** Renders active recurring prompts contributed to the Loop activity section. */\nexport function LoopActivityItems({\n statuses,\n sessionId,\n sendSessionFrame,\n}: Pick<WebPluginSlotProps, 'statuses' | 'sessionId' | 'sendSessionFrame'>) {\n const raw = statuses[LOOP_VIEW_STATUS_KEY];\n const loops = parseLoopStatusView(raw);\n const unavailable = raw !== undefined && raw.trim() !== '' && loops === undefined;\n if (!loops && !unavailable) return null;\n\n return loops ? (\n <ul aria-label=\"active loops\" className=\"flex flex-col gap-1\">\n {loops.map((loop) => (\n <li\n key={loop.instanceId}\n data-testid={`activity-loop-${loop.instanceId}`}\n data-loop-state={loop.state}\n title={`${loop.label}: ${loop.detail}`}\n className=\"flex min-w-0 flex-col gap-0.5 px-1 py-0.5\"\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <Dot tone={toneOf(loop.state)} pulse={loop.state !== 'running'} />\n <span className=\"min-w-0 flex-1 truncate text-xs font-bold text-doom-hi\">{loop.label}</span>\n <span className=\"shrink-0 text-2xs text-doom-faint\">{loop.state}</span>\n <Button\n variant=\"subtle\"\n size=\"xs\"\n aria-label={`stop ${loop.label}`}\n disabled={sessionId === null || loop.state === 'stopping'}\n onClick={() => {\n if (sessionId !== null)\n sendSessionFrame(sessionId, { type: 'prompt', message: `/loops stop ${loop.instanceId}` });\n }}\n >\n stop\n </Button>\n </span>\n <span className=\"truncate pl-3 text-2xs text-doom-faint\">{loop.detail}</span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"px-1 text-xs text-doom-faint\">loop status unavailable</p>\n );\n}\n\n/** Idle-safe Loop activity shell with extension registration and instance slots. */\nexport function LoopsActivitySection({ sessionId, renderSlot, sendSessionFrame }: WebPluginSlotProps) {\n return (\n <div data-testid=\"activity-loop-instances\" className=\"flex flex-col gap-2\">\n <div className=\"flex flex-wrap items-center gap-1\">\n <DefaultLoopLauncher sessionId={sessionId} sendSessionFrame={sendSessionFrame} />\n <Button\n variant=\"subtle\"\n size=\"xs\"\n data-testid=\"activity-loop-cron-launch\"\n aria-label=\"launch cron loop\"\n disabled={sessionId === null}\n onClick={() => {\n if (sessionId !== null) sendSessionFrame(sessionId, { type: 'prompt', message: '/loop doompi.cron' });\n }}\n className=\"text-2xs font-bold\"\n >\n cron loop\n </Button>\n <Button\n variant=\"subtle\"\n size=\"xs\"\n aria-label=\"choose loop type\"\n disabled={sessionId === null}\n onClick={() => {\n if (sessionId !== null) sendSessionFrame(sessionId, { type: 'prompt', message: '/loop' });\n }}\n className=\"text-2xs font-bold\"\n >\n choose type\n </Button>\n {renderSlot('loop.registration')}\n </div>\n {renderSlot('loop.items')}\n <Button\n variant=\"subtle\"\n size=\"xs\"\n data-testid=\"activity-loops-manage\"\n aria-label=\"manage loops\"\n disabled={sessionId === null}\n onClick={() => {\n if (sessionId === null) return;\n sendSessionFrame(sessionId, { type: 'prompt', message: MANAGE_COMMAND });\n }}\n className=\"self-end text-2xs font-bold\"\n >\n manage\n </Button>\n </div>\n );\n}\n","import { defineFill } from '@agimon-ai/doompi-core/web';\n\nimport { LoopActivityItems } from './_components/LoopsActivitySection';\n\nexport default defineFill({ component: LoopActivityItems });\n","import { defineFill } from '@agimon-ai/doompi-core/web';\n\nimport { LoopsActivitySection } from './_components/LoopsActivitySection';\n\nexport default defineFill({ component: LoopsActivitySection });\n","import { defineLeaderBinding } from '@agimon-ai/doompi-core/web';\n\nexport default defineLeaderBinding({\n path: [\n { key: 'l', label: 'loops', detail: 'recurring prompt loops' },\n { key: 'l', label: 'list', detail: 'loops in this session' },\n ],\n command: 'loops',\n});\n","import { defineLeaderBinding } from '@agimon-ai/doompi-core/web';\n\nexport default defineLeaderBinding({\n path: [\n { key: 'l', label: 'loops', detail: 'recurring prompt loops' },\n { key: 's', label: 'start', detail: 'begin a recurring loop' },\n ],\n command: 'loop',\n});\n","import { defineMinorModeFile } from '@agimon-ai/doompi-core/web';\n\nexport default defineMinorModeFile({ keys: 'l l', statusKey: 'doom-loop', order: 30 });\n","import { defineSlotFile } from '@agimon-ai/doompi-core/web';\n\nexport default defineSlotFile({});\n","import { defineSlotFile } from '@agimon-ai/doompi-core/web';\n\nexport default defineSlotFile({});\n","import type { ToolMessageRenderProps } from '@agimon-ai/doompi-core/web';\nimport {\n collapseLines,\n MessageItem,\n MessageItemBody,\n MessageItemHeader,\n MessageItemStatus,\n MessageLines,\n toolTone,\n} from '@agimon-ai/doompi-web-components';\n\n/** Loop tools share the same compact, expandable result presentation. */\nexport function LoopToolCard({ toolName, result, running, isError }: ToolMessageRenderProps) {\n const lines =\n result === undefined\n ? []\n : JSON.stringify(result, null, 2)\n .split('\\n')\n .map((text) => ({ text, tone: 'text' as const }));\n return (\n <MessageItem tone={toolTone({ running, isError })} expandable={lines.length > 8}>\n {({ expanded }) => {\n const view = collapseLines(lines, 8, expanded);\n return (\n <>\n <MessageItemHeader title={toolName} />\n <MessageItemBody>\n <MessageLines lines={view.shown} />\n {view.hidden > 0 ? <MessageItemStatus expands>{view.hidden} more lines</MessageItemStatus> : null}\n {running ? <MessageItemStatus tone=\"running\">running</MessageItemStatus> : null}\n {isError ? <MessageItemStatus tone=\"error\">failed</MessageItemStatus> : null}\n </MessageItemBody>\n </>\n );\n }}\n </MessageItem>\n );\n}\n","import { defineToolRenderer } from '@agimon-ai/doompi-core/web';\n\nimport { LoopToolCard } from './_components/LoopToolCard';\n\nexport default defineToolRenderer({ tools: ['loop_list'], message: LoopToolCard });\n","import { defineToolRenderer } from '@agimon-ai/doompi-core/web';\n\nimport { LoopToolCard } from './_components/LoopToolCard';\n\nexport default defineToolRenderer({ tools: ['loop_start'], message: LoopToolCard });\n","import { defineToolRenderer } from '@agimon-ai/doompi-core/web';\n\nimport { LoopToolCard } from './_components/LoopToolCard';\n\nexport default defineToolRenderer({ tools: ['loop_stop'], message: LoopToolCard });\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/doompi-core/web';\n\nimport activityGroupLoops from '../src/extensions/workspaces/sessions/(frontend)/activity-group/loops.web';\nimport fillInstances from '../src/extensions/workspaces/sessions/(frontend)/fill/Instances.loop.items.web';\nimport fillLoops from '../src/extensions/workspaces/sessions/(frontend)/fill/Loops.activity.loops.web';\nimport leaderList from '../src/extensions/workspaces/sessions/(frontend)/leader/list.web';\nimport leaderStart from '../src/extensions/workspaces/sessions/(frontend)/leader/start.web';\nimport modeLoop from '../src/extensions/workspaces/sessions/(frontend)/mode/loop/mode.web';\nimport slotItems from '../src/extensions/workspaces/sessions/(frontend)/slot/items.web';\nimport slotRegistration from '../src/extensions/workspaces/sessions/(frontend)/slot/registration.web';\nimport toolLoopList from '../src/extensions/workspaces/sessions/(frontend)/tool/loop-list.web';\nimport toolLoopStart from '../src/extensions/workspaces/sessions/(frontend)/tool/loop-start.web';\nimport toolLoopStop from '../src/extensions/workspaces/sessions/(frontend)/tool/loop-stop.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'loop',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'loops' }, activityGroupLoops)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'loop.items', id: 'instances' }, fillInstances), via({ slot: 'activity.loops', id: 'loops' }, fillLoops)]; },\n get leaderBindings(): WebPluginContributions['leaderBindings'] { return [via({ id: 'loop.list' }, leaderList), via({ id: 'loop.start' }, leaderStart)]; },\n get minorModes(): WebPluginContributions['minorModes'] { return [via({ name: 'loop' }, modeLoop)]; },\n get slots(): WebPluginContributions['slots'] { return [via({ slot: 'loop.items' }, slotItems), via({ slot: 'loop.registration' }, slotRegistration)]; },\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['loop_list'] }, toolLoopList), via({ tools: ['loop_start'] }, toolLoopStart), via({ tools: ['loop_stop'] }, toolLoopStop)]; },\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"web.mjs","names":["activityGroupLoops","fillInstances","fillLoops","leaderList","modeLoop","slotItems","slotRegistration","toolLoopList","toolLoopStart","toolLoopStop"],"sources":["../../src/types/loopView.ts","../../src/extensions/workspaces/sessions/(frontend)/activity-group/loops.web.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/_components/LoopsActivitySection.tsx","../../src/extensions/workspaces/sessions/(frontend)/fill/Instances.loop.items.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/fill/Loops.activity.loops.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/leader/list.web.ts","../../src/extensions/workspaces/sessions/(frontend)/mode/loop/mode.web.ts","../../src/extensions/workspaces/sessions/(frontend)/slot/items.web.ts","../../src/extensions/workspaces/sessions/(frontend)/slot/registration.web.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/LoopToolCard.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/loop-list.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/loop-start.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/loop-stop.web.tsx","../../generated/web.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 {\n if (instances.length === 0) return '';\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","import { defineActivityGroup } from '@agimon-ai/doompi-core/web';\n\nimport { LOOP_VIEW_STATUS_KEY } from '../../../../../types/loopView';\n\nexport default defineActivityGroup({\n keys: 'l l',\n statusKey: LOOP_VIEW_STATUS_KEY,\n order: 40,\n});\n","import type { WebPluginSlotProps } from '@agimon-ai/doompi-core/web';\nimport { Button, Dot, type DotTone } from '@agimon-ai/doompi-web-components';\n\nimport { LOOP_VIEW_STATUS_KEY, parseLoopStatusView, type LoopStatusState } from '../../../../../../types/loopView';\n\n/** The minor mode name the cockpit reports while the agent's loop tools are active. */\nconst LOOP_MODE = 'loop';\n\nfunction toneOf(state: LoopStatusState): DotTone {\n if (state === 'running') return 'green';\n if (state === 'stopping') return 'orange';\n return 'yellow';\n}\n\n/** Renders active recurring prompts contributed to the Loop activity section. */\nexport function LoopActivityItems({\n statuses,\n sessionId,\n sendSessionFrame,\n}: Pick<WebPluginSlotProps, 'statuses' | 'sessionId' | 'sendSessionFrame'>) {\n const raw = statuses[LOOP_VIEW_STATUS_KEY];\n const loops = parseLoopStatusView(raw);\n const unavailable = raw !== undefined && raw.trim() !== '' && loops === undefined;\n if (!loops && !unavailable) return null;\n\n return loops ? (\n <ul aria-label=\"active loops\" className=\"flex flex-col gap-1\">\n {loops.map((loop) => (\n <li\n key={loop.instanceId}\n data-testid={`activity-loop-${loop.instanceId}`}\n data-loop-state={loop.state}\n title={`${loop.label}: ${loop.detail}`}\n className=\"flex min-w-0 flex-col gap-0.5 px-1 py-0.5\"\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <Dot tone={toneOf(loop.state)} pulse={loop.state !== 'running'} />\n <span className=\"min-w-0 flex-1 truncate text-xs font-bold text-doom-hi\">{loop.label}</span>\n <span className=\"shrink-0 text-2xs text-doom-faint\">{loop.state}</span>\n <Button\n variant=\"subtle\"\n size=\"xs\"\n aria-label={`stop ${loop.label}`}\n disabled={sessionId === null || loop.state === 'stopping'}\n onClick={() => {\n if (sessionId !== null)\n sendSessionFrame(sessionId, { type: 'prompt', message: `/loops stop ${loop.instanceId}` });\n }}\n >\n stop\n </Button>\n </span>\n <span className=\"truncate pl-3 text-2xs text-doom-faint\">{loop.detail}</span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"px-1 text-xs text-doom-faint\">loop status unavailable</p>\n );\n}\n\n/**\n * The Loop activity section. The agent sets loops through its tools while the Loop minor mode is\n * active; this section lists the session's loops whatever the mode, so any of them can be stopped.\n */\nexport function LoopsActivitySection({ statuses, activeMinorModes, renderSlot }: WebPluginSlotProps) {\n const modeActive = activeMinorModes?.includes(LOOP_MODE) === true;\n const hasLoops = parseLoopStatusView(statuses[LOOP_VIEW_STATUS_KEY]) !== undefined;\n let hint: string | undefined;\n if (!modeActive) hint = 'Activate loop minor mode to set loop or cron job.';\n else if (!hasLoops) hint = 'No loops yet. Ask the agent to set a loop or cron job.';\n return (\n <div data-testid=\"activity-loop-instances\" className=\"flex flex-col gap-2\">\n {renderSlot('loop.registration')}\n {renderSlot('loop.items')}\n {hint === undefined ? null : (\n <p data-testid=\"activity-loops-hint\" className=\"px-1 text-xs text-doom-faint\">\n {hint}\n </p>\n )}\n </div>\n );\n}\n","import { defineFill } from '@agimon-ai/doompi-core/web';\n\nimport { LoopActivityItems } from './_components/LoopsActivitySection';\n\nexport default defineFill({ component: LoopActivityItems });\n","import { defineFill } from '@agimon-ai/doompi-core/web';\n\nimport { LoopsActivitySection } from './_components/LoopsActivitySection';\n\nexport default defineFill({ component: LoopsActivitySection });\n","import { defineLeaderBinding } from '@agimon-ai/doompi-core/web';\n\nexport default defineLeaderBinding({\n path: [\n { key: 'l', label: 'loops', detail: 'recurring prompt loops' },\n { key: 'l', label: 'list', detail: 'loops in this session' },\n ],\n command: 'loops',\n});\n","import { defineMinorModeFile } from '@agimon-ai/doompi-core/web';\n\nexport default defineMinorModeFile({ keys: 'l l', statusKey: 'doom-loop', order: 30 });\n","import { defineSlotFile } from '@agimon-ai/doompi-core/web';\n\nexport default defineSlotFile({});\n","import { defineSlotFile } from '@agimon-ai/doompi-core/web';\n\nexport default defineSlotFile({});\n","import type { ToolMessageRenderProps } from '@agimon-ai/doompi-core/web';\nimport {\n collapseLines,\n MessageItem,\n MessageItemBody,\n MessageItemHeader,\n MessageItemStatus,\n MessageLines,\n toolTone,\n} from '@agimon-ai/doompi-web-components';\n\n/** Loop tools share the same compact, expandable result presentation. */\nexport function LoopToolCard({ toolName, result, running, isError }: ToolMessageRenderProps) {\n const lines =\n result === undefined\n ? []\n : JSON.stringify(result, null, 2)\n .split('\\n')\n .map((text) => ({ text, tone: 'text' as const }));\n return (\n <MessageItem tone={toolTone({ running, isError })} expandable={lines.length > 8}>\n {({ expanded }) => {\n const view = collapseLines(lines, 8, expanded);\n return (\n <>\n <MessageItemHeader title={toolName} />\n <MessageItemBody>\n <MessageLines lines={view.shown} />\n {view.hidden > 0 ? <MessageItemStatus expands>{view.hidden} more lines</MessageItemStatus> : null}\n {running ? <MessageItemStatus tone=\"running\">running</MessageItemStatus> : null}\n {isError ? <MessageItemStatus tone=\"error\">failed</MessageItemStatus> : null}\n </MessageItemBody>\n </>\n );\n }}\n </MessageItem>\n );\n}\n","import { defineToolRenderer } from '@agimon-ai/doompi-core/web';\n\nimport { LoopToolCard } from './_components/LoopToolCard';\n\nexport default defineToolRenderer({ tools: ['loop_list'], message: LoopToolCard });\n","import { defineToolRenderer } from '@agimon-ai/doompi-core/web';\n\nimport { LoopToolCard } from './_components/LoopToolCard';\n\nexport default defineToolRenderer({ tools: ['loop_start'], message: LoopToolCard });\n","import { defineToolRenderer } from '@agimon-ai/doompi-core/web';\n\nimport { LoopToolCard } from './_components/LoopToolCard';\n\nexport default defineToolRenderer({ tools: ['loop_stop'], message: LoopToolCard });\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/doompi-core/web';\n\nimport activityGroupLoops from '../src/extensions/workspaces/sessions/(frontend)/activity-group/loops.web';\nimport fillInstances from '../src/extensions/workspaces/sessions/(frontend)/fill/Instances.loop.items.web';\nimport fillLoops from '../src/extensions/workspaces/sessions/(frontend)/fill/Loops.activity.loops.web';\nimport leaderList from '../src/extensions/workspaces/sessions/(frontend)/leader/list.web';\nimport modeLoop from '../src/extensions/workspaces/sessions/(frontend)/mode/loop/mode.web';\nimport slotItems from '../src/extensions/workspaces/sessions/(frontend)/slot/items.web';\nimport slotRegistration from '../src/extensions/workspaces/sessions/(frontend)/slot/registration.web';\nimport toolLoopList from '../src/extensions/workspaces/sessions/(frontend)/tool/loop-list.web';\nimport toolLoopStart from '../src/extensions/workspaces/sessions/(frontend)/tool/loop-start.web';\nimport toolLoopStop from '../src/extensions/workspaces/sessions/(frontend)/tool/loop-stop.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'loop',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'loops' }, activityGroupLoops)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'loop.items', id: 'instances' }, fillInstances), via({ slot: 'activity.loops', id: 'loops' }, fillLoops)]; },\n get leaderBindings(): WebPluginContributions['leaderBindings'] { return [via({ id: 'loop.list' }, leaderList)]; },\n get minorModes(): WebPluginContributions['minorModes'] { return [via({ name: 'loop' }, modeLoop)]; },\n get slots(): WebPluginContributions['slots'] { return [via({ slot: 'loop.items' }, slotItems), via({ slot: 'loop.registration' }, slotRegistration)]; },\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['loop_list'] }, toolLoopList), via({ tools: ['loop_start'] }, toolLoopStart), via({ tools: ['loop_stop'] }, toolLoopStop)]; },\n },\n});\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { Check } from "typebox/value";
4
4
  //#region src/constants/piLoop.ts
5
5
  const PACKAGE_SOURCE = "@agimon-ai/doompi-loop";
6
6
  const STATUS_KEY = "doom-loop";
7
- const MODE_STATUS_ID = "loop.active";
7
+ const MODE_STATUS_ID = "loop";
8
8
  const ACTIVE_MODE_COLOR = "warning";
9
9
  const NOTIFY_INFO_LEVEL = "info";
10
10
  const NOTIFY_WARNING_LEVEL = "warning";
@@ -346,4 +346,4 @@ function formatLoopStatusView(instances) {
346
346
  //#endregion
347
347
  export { PACKAGE_SOURCE as _, CronLoopSchema as a, parseIntervalLoop as c, START_COMMAND_DESCRIPTION as d, START_COMMAND_NAME as f, NOTIFY_WARNING_LEVEL as g, NOTIFY_INFO_LEVEL as h, createDoomLoopLaunchersService as i, LIST_COMMAND_DESCRIPTION as l, MODE_STATUS_ID as m, formatLoopStatusView as n, IntervalLoopSchema as o, ACTIVE_MODE_COLOR as p, createLoopTools as r, parseCronLoop as s, LOOP_VIEW_STATUS_KEY as t, LIST_COMMAND_NAME as u, STATUS_KEY as v };
348
348
 
349
- //# sourceMappingURL=loopView-BK4X_1UI.mjs.map
349
+ //# sourceMappingURL=loopView-DUR63zT7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loopView-DUR63zT7.mjs","names":[],"sources":["../src/constants/piLoop.ts","../src/constants/loop.ts","../src/schemas/loopTools.ts","../src/services/loopLaunchers/index.ts","../src/services/loopTools/index.ts","../src/types/loopView.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-loop';\nexport const LOOPS_GROUP_ORDER = 60;\nexport const STATUS_KEY = 'doom-loop';\nexport const MODE_STATUS_ID = 'loop';\nexport const ACTIVE_MODE_COLOR = 'warning';\nexport const NOTIFY_INFO_LEVEL = 'info';\nexport const NOTIFY_WARNING_LEVEL = 'warning';\n","export const START_COMMAND_DESCRIPTION = 'Start a registered loop';\nexport const LIST_COMMAND_DESCRIPTION = 'List and stop active loops';\nexport const START_COMMAND_NAME = 'loop';\nexport const LIST_COMMAND_NAME = 'loops';\n","import { type Static, Type } from 'typebox';\nimport { Check } from 'typebox/value';\n\nexport const LoopListSchema = Type.Object({}, { additionalProperties: false });\nexport const LoopStartSchema = Type.Object(\n {\n launcherId: Type.String({ minLength: 1, maxLength: 256 }),\n instanceId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),\n input: Type.Record(Type.String(), Type.Unknown()),\n },\n { additionalProperties: false },\n);\nexport const LoopStopSchema = Type.Object(\n {\n instanceId: Type.String({ minLength: 1, maxLength: 256 }),\n reason: Type.Optional(Type.String({ minLength: 1, maxLength: 512 })),\n },\n { additionalProperties: false },\n);\n\nexport const IntervalLoopSchema = Type.Object(\n {\n prompt: Type.String({ minLength: 1, maxLength: 32768 }),\n intervalSeconds: Type.Integer({ minimum: 30, maximum: 3600 }),\n },\n { additionalProperties: false },\n);\nexport const CronLoopSchema = Type.Object(\n {\n prompt: Type.String({ minLength: 1, maxLength: 32768 }),\n cron: Type.String({ minLength: 1, maxLength: 256 }),\n timezone: Type.Optional(Type.String({ minLength: 1, maxLength: 128 })),\n },\n { additionalProperties: false },\n);\nexport type IntervalLoopInput = Static<typeof IntervalLoopSchema>;\nexport type CronLoopInput = Static<typeof CronLoopSchema>;\n\nexport function parseIntervalLoop(input: unknown): IntervalLoopInput {\n if (!Check(IntervalLoopSchema, input) || !input.prompt.trim()) {\n throw new Error('Provide a non-empty prompt and an integer intervalSeconds between 30 and 3600.');\n }\n return { ...input, prompt: input.prompt.trim() };\n}\n\nexport function parseCronLoop(input: unknown): CronLoopInput {\n if (!Check(CronLoopSchema, input) || !input.prompt.trim() || input.cron.trim().split(/\\s+/u).length !== 5) {\n throw new Error('Provide a non-empty prompt and a five-field cron expression, with an optional IANA timezone.');\n }\n const timezone = input.timezone ?? 'UTC';\n new Intl.DateTimeFormat('en', { timeZone: timezone }).format();\n return { ...input, prompt: input.prompt.trim(), cron: input.cron.trim(), timezone };\n}\n","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 if (typeof value !== 'string') throw new TypeError(`Invalid ${label}.`);\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 ...(definition.inputSchema ? { inputSchema: definition.inputSchema } : {}),\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 options?.signal?.throwIfAborted();\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 const abortSetup = (): void => entry.abortController.abort(options?.signal?.reason);\n options?.signal?.addEventListener('abort', abortSetup, { once: true });\n instances.set(instanceId, entry);\n notify();\n try {\n if (options?.interactive === false && !launcher.definition.inputSchema) {\n throw new Error(`Loop launcher '${normalizedLauncherId}' requires manual setup in Activity.`);\n }\n const handle = await launcher.definition.launch({\n instanceId,\n signal: entry.abortController.signal,\n ...(options?.input === undefined ? {} : { input: options.input }),\n ...(options?.interactive === undefined ? {} : { interactive: options.interactive }),\n });\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 } finally {\n options?.signal?.removeEventListener('abort', abortSetup);\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","import { defineTool, type DoomPluginTool, type DoomPluginToolResult } from '@agimon-ai/doompi-core/piExtension';\nimport { Check } from 'typebox/value';\n\nimport type { DoomLoopLaunchersService } from '../../schemas/loopLaunchers';\nimport { LoopListSchema, LoopStartSchema, LoopStopSchema } from '../../schemas/loopTools';\n\nfunction result(details: unknown): DoomPluginToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(details, null, 2) }], details };\n}\n\nexport function createLoopTools(\n launchers: () => DoomLoopLaunchersService,\n assertEnabled: () => void,\n): readonly DoomPluginTool[] {\n return [\n defineTool({\n name: 'loop_list',\n label: 'List loops',\n description: 'List active session loops and available launcher types with their configuration schemas.',\n parameters: LoopListSchema,\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopListSchema, input)) throw new Error('loop_list takes no arguments.');\n const service = launchers();\n return result({ launchers: service.listLaunchers(), instances: service.listInstances() });\n },\n }),\n defineTool({\n name: 'loop_start',\n label: 'Start loop',\n description:\n 'Start a session-scoped loop without operator dialogs. Read loop_list for launcher IDs and input schemas. Loops stop when the session shuts down.',\n parameters: LoopStartSchema,\n executionMode: 'serial',\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopStartSchema, input)) throw new Error('Provide launcherId, input, and optionally instanceId.');\n return result(\n (await launchers().launch(input.launcherId, { ...input, interactive: false, signal })) ?? { cancelled: true },\n );\n },\n }),\n defineTool({\n name: 'loop_stop',\n label: 'Stop loop',\n description:\n 'Stop one loop instance owned by the current session. Does not disable Loop mode or stop other loops.',\n parameters: LoopStopSchema,\n executionMode: 'serial',\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopStopSchema, input)) throw new Error('Provide instanceId and an optional reason.');\n return result({ stopped: await launchers().stop(input.instanceId, input.reason ?? 'Stopped by the agent.') });\n },\n }),\n ];\n}\n","/** 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 {\n if (instances.length === 0) return '';\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":"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"}
@@ -4,7 +4,7 @@ let typebox_value = require("typebox/value");
4
4
  //#region src/constants/piLoop.ts
5
5
  const PACKAGE_SOURCE = "@agimon-ai/doompi-loop";
6
6
  const STATUS_KEY = "doom-loop";
7
- const MODE_STATUS_ID = "loop.active";
7
+ const MODE_STATUS_ID = "loop";
8
8
  const ACTIVE_MODE_COLOR = "warning";
9
9
  const NOTIFY_INFO_LEVEL = "info";
10
10
  const NOTIFY_WARNING_LEVEL = "warning";
@@ -453,4 +453,4 @@ Object.defineProperty(exports, "parseIntervalLoop", {
453
453
  }
454
454
  });
455
455
 
456
- //# sourceMappingURL=loopView-C_OPuy8L.cjs.map
456
+ //# sourceMappingURL=loopView-FnuHeY7C.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loopView-FnuHeY7C.cjs","names":["Type","Check","defineTool","Check"],"sources":["../src/constants/piLoop.ts","../src/constants/loop.ts","../src/schemas/loopTools.ts","../src/services/loopLaunchers/index.ts","../src/services/loopTools/index.ts","../src/types/loopView.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-loop';\nexport const LOOPS_GROUP_ORDER = 60;\nexport const STATUS_KEY = 'doom-loop';\nexport const MODE_STATUS_ID = 'loop';\nexport const ACTIVE_MODE_COLOR = 'warning';\nexport const NOTIFY_INFO_LEVEL = 'info';\nexport const NOTIFY_WARNING_LEVEL = 'warning';\n","export const START_COMMAND_DESCRIPTION = 'Start a registered loop';\nexport const LIST_COMMAND_DESCRIPTION = 'List and stop active loops';\nexport const START_COMMAND_NAME = 'loop';\nexport const LIST_COMMAND_NAME = 'loops';\n","import { type Static, Type } from 'typebox';\nimport { Check } from 'typebox/value';\n\nexport const LoopListSchema = Type.Object({}, { additionalProperties: false });\nexport const LoopStartSchema = Type.Object(\n {\n launcherId: Type.String({ minLength: 1, maxLength: 256 }),\n instanceId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),\n input: Type.Record(Type.String(), Type.Unknown()),\n },\n { additionalProperties: false },\n);\nexport const LoopStopSchema = Type.Object(\n {\n instanceId: Type.String({ minLength: 1, maxLength: 256 }),\n reason: Type.Optional(Type.String({ minLength: 1, maxLength: 512 })),\n },\n { additionalProperties: false },\n);\n\nexport const IntervalLoopSchema = Type.Object(\n {\n prompt: Type.String({ minLength: 1, maxLength: 32768 }),\n intervalSeconds: Type.Integer({ minimum: 30, maximum: 3600 }),\n },\n { additionalProperties: false },\n);\nexport const CronLoopSchema = Type.Object(\n {\n prompt: Type.String({ minLength: 1, maxLength: 32768 }),\n cron: Type.String({ minLength: 1, maxLength: 256 }),\n timezone: Type.Optional(Type.String({ minLength: 1, maxLength: 128 })),\n },\n { additionalProperties: false },\n);\nexport type IntervalLoopInput = Static<typeof IntervalLoopSchema>;\nexport type CronLoopInput = Static<typeof CronLoopSchema>;\n\nexport function parseIntervalLoop(input: unknown): IntervalLoopInput {\n if (!Check(IntervalLoopSchema, input) || !input.prompt.trim()) {\n throw new Error('Provide a non-empty prompt and an integer intervalSeconds between 30 and 3600.');\n }\n return { ...input, prompt: input.prompt.trim() };\n}\n\nexport function parseCronLoop(input: unknown): CronLoopInput {\n if (!Check(CronLoopSchema, input) || !input.prompt.trim() || input.cron.trim().split(/\\s+/u).length !== 5) {\n throw new Error('Provide a non-empty prompt and a five-field cron expression, with an optional IANA timezone.');\n }\n const timezone = input.timezone ?? 'UTC';\n new Intl.DateTimeFormat('en', { timeZone: timezone }).format();\n return { ...input, prompt: input.prompt.trim(), cron: input.cron.trim(), timezone };\n}\n","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 if (typeof value !== 'string') throw new TypeError(`Invalid ${label}.`);\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 ...(definition.inputSchema ? { inputSchema: definition.inputSchema } : {}),\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 options?.signal?.throwIfAborted();\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 const abortSetup = (): void => entry.abortController.abort(options?.signal?.reason);\n options?.signal?.addEventListener('abort', abortSetup, { once: true });\n instances.set(instanceId, entry);\n notify();\n try {\n if (options?.interactive === false && !launcher.definition.inputSchema) {\n throw new Error(`Loop launcher '${normalizedLauncherId}' requires manual setup in Activity.`);\n }\n const handle = await launcher.definition.launch({\n instanceId,\n signal: entry.abortController.signal,\n ...(options?.input === undefined ? {} : { input: options.input }),\n ...(options?.interactive === undefined ? {} : { interactive: options.interactive }),\n });\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 } finally {\n options?.signal?.removeEventListener('abort', abortSetup);\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","import { defineTool, type DoomPluginTool, type DoomPluginToolResult } from '@agimon-ai/doompi-core/piExtension';\nimport { Check } from 'typebox/value';\n\nimport type { DoomLoopLaunchersService } from '../../schemas/loopLaunchers';\nimport { LoopListSchema, LoopStartSchema, LoopStopSchema } from '../../schemas/loopTools';\n\nfunction result(details: unknown): DoomPluginToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(details, null, 2) }], details };\n}\n\nexport function createLoopTools(\n launchers: () => DoomLoopLaunchersService,\n assertEnabled: () => void,\n): readonly DoomPluginTool[] {\n return [\n defineTool({\n name: 'loop_list',\n label: 'List loops',\n description: 'List active session loops and available launcher types with their configuration schemas.',\n parameters: LoopListSchema,\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopListSchema, input)) throw new Error('loop_list takes no arguments.');\n const service = launchers();\n return result({ launchers: service.listLaunchers(), instances: service.listInstances() });\n },\n }),\n defineTool({\n name: 'loop_start',\n label: 'Start loop',\n description:\n 'Start a session-scoped loop without operator dialogs. Read loop_list for launcher IDs and input schemas. Loops stop when the session shuts down.',\n parameters: LoopStartSchema,\n executionMode: 'serial',\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopStartSchema, input)) throw new Error('Provide launcherId, input, and optionally instanceId.');\n return result(\n (await launchers().launch(input.launcherId, { ...input, interactive: false, signal })) ?? { cancelled: true },\n );\n },\n }),\n defineTool({\n name: 'loop_stop',\n label: 'Stop loop',\n description:\n 'Stop one loop instance owned by the current session. Does not disable Loop mode or stop other loops.',\n parameters: LoopStopSchema,\n executionMode: 'serial',\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopStopSchema, input)) throw new Error('Provide instanceId and an optional reason.');\n return result({ stopped: await launchers().stop(input.instanceId, input.reason ?? 'Stopped by the agent.') });\n },\n }),\n ];\n}\n","/** 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 {\n if (instances.length === 0) return '';\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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-loop",
3
- "version": "0.0.1-alpha.93",
3
+ "version": "0.0.1-alpha.95",
4
4
  "description": "Session-scoped recurring prompt scheduler and loop controls for Pi coding agents.",
5
5
  "keywords": [
6
6
  "ai",
@@ -69,16 +69,16 @@
69
69
  "access": "public"
70
70
  },
71
71
  "dependencies": {
72
- "@agimon-ai/doompi-core": "0.0.1-alpha.92",
73
- "@agimon-ai/doompi-minor-mode": "0.0.1-alpha.92",
74
- "@agimon-ai/doompi-ui": "0.0.1-alpha.92",
75
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.50",
72
+ "@agimon-ai/doompi-core": "0.0.1-alpha.94",
73
+ "@agimon-ai/doompi-minor-mode": "0.0.1-alpha.94",
74
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.94",
75
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.52",
76
76
  "@deepseek-ai/cordis": "4.0.2",
77
77
  "croner": "10.0.1",
78
78
  "typebox": "1.3.34"
79
79
  },
80
80
  "devDependencies": {
81
- "@agimon-ai/doompi-build": "0.0.1-alpha.15",
81
+ "@agimon-ai/doompi-build": "0.0.1-alpha.17",
82
82
  "@earendil-works/pi-coding-agent": "0.87.1",
83
83
  "@earendil-works/pi-tui": "0.87.1",
84
84
  "@types/node": "26.6.1",
@@ -33,7 +33,7 @@ Pass either configuration to `loop_start`. Stop one instance with `loop_stop` an
33
33
 
34
34
  ## Manual controls
35
35
 
36
- Activity's Loops group is available whenever the Loop package is loaded, even with the minor mode off. It offers interval and cron setup, a chooser for extension types, and per-instance stop controls. `/loop [launcherId]` starts manual setup. `/loops` lists instances, and `/loops stop <instanceId>` stops one. The TUI also provides `SPC l s` and `SPC l l`.
36
+ Activity's Loops group lists this session's loops whenever the Loop package is loaded, even with the minor mode off, and each row has a stop control. Loops are set by asking the agent while the minor mode is active. `/loops` lists instances, and `/loops stop <instanceId>` stops one.
37
37
 
38
38
  ## Cost and lifecycle
39
39
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopView-BK4X_1UI.mjs","names":[],"sources":["../src/constants/piLoop.ts","../src/constants/loop.ts","../src/schemas/loopTools.ts","../src/services/loopLaunchers/index.ts","../src/services/loopTools/index.ts","../src/types/loopView.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-loop';\nexport const LOOPS_GROUP_ORDER = 60;\nexport const STATUS_KEY = 'doom-loop';\nexport const MODE_STATUS_ID = 'loop.active';\nexport const ACTIVE_MODE_COLOR = 'warning';\nexport const NOTIFY_INFO_LEVEL = 'info';\nexport const NOTIFY_WARNING_LEVEL = 'warning';\n","export const START_COMMAND_DESCRIPTION = 'Start a registered loop';\nexport const LIST_COMMAND_DESCRIPTION = 'List and stop active loops';\nexport const START_COMMAND_NAME = 'loop';\nexport const LIST_COMMAND_NAME = 'loops';\n","import { type Static, Type } from 'typebox';\nimport { Check } from 'typebox/value';\n\nexport const LoopListSchema = Type.Object({}, { additionalProperties: false });\nexport const LoopStartSchema = Type.Object(\n {\n launcherId: Type.String({ minLength: 1, maxLength: 256 }),\n instanceId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),\n input: Type.Record(Type.String(), Type.Unknown()),\n },\n { additionalProperties: false },\n);\nexport const LoopStopSchema = Type.Object(\n {\n instanceId: Type.String({ minLength: 1, maxLength: 256 }),\n reason: Type.Optional(Type.String({ minLength: 1, maxLength: 512 })),\n },\n { additionalProperties: false },\n);\n\nexport const IntervalLoopSchema = Type.Object(\n {\n prompt: Type.String({ minLength: 1, maxLength: 32768 }),\n intervalSeconds: Type.Integer({ minimum: 30, maximum: 3600 }),\n },\n { additionalProperties: false },\n);\nexport const CronLoopSchema = Type.Object(\n {\n prompt: Type.String({ minLength: 1, maxLength: 32768 }),\n cron: Type.String({ minLength: 1, maxLength: 256 }),\n timezone: Type.Optional(Type.String({ minLength: 1, maxLength: 128 })),\n },\n { additionalProperties: false },\n);\nexport type IntervalLoopInput = Static<typeof IntervalLoopSchema>;\nexport type CronLoopInput = Static<typeof CronLoopSchema>;\n\nexport function parseIntervalLoop(input: unknown): IntervalLoopInput {\n if (!Check(IntervalLoopSchema, input) || !input.prompt.trim()) {\n throw new Error('Provide a non-empty prompt and an integer intervalSeconds between 30 and 3600.');\n }\n return { ...input, prompt: input.prompt.trim() };\n}\n\nexport function parseCronLoop(input: unknown): CronLoopInput {\n if (!Check(CronLoopSchema, input) || !input.prompt.trim() || input.cron.trim().split(/\\s+/u).length !== 5) {\n throw new Error('Provide a non-empty prompt and a five-field cron expression, with an optional IANA timezone.');\n }\n const timezone = input.timezone ?? 'UTC';\n new Intl.DateTimeFormat('en', { timeZone: timezone }).format();\n return { ...input, prompt: input.prompt.trim(), cron: input.cron.trim(), timezone };\n}\n","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 if (typeof value !== 'string') throw new TypeError(`Invalid ${label}.`);\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 ...(definition.inputSchema ? { inputSchema: definition.inputSchema } : {}),\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 options?.signal?.throwIfAborted();\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 const abortSetup = (): void => entry.abortController.abort(options?.signal?.reason);\n options?.signal?.addEventListener('abort', abortSetup, { once: true });\n instances.set(instanceId, entry);\n notify();\n try {\n if (options?.interactive === false && !launcher.definition.inputSchema) {\n throw new Error(`Loop launcher '${normalizedLauncherId}' requires manual setup in Activity.`);\n }\n const handle = await launcher.definition.launch({\n instanceId,\n signal: entry.abortController.signal,\n ...(options?.input === undefined ? {} : { input: options.input }),\n ...(options?.interactive === undefined ? {} : { interactive: options.interactive }),\n });\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 } finally {\n options?.signal?.removeEventListener('abort', abortSetup);\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","import { defineTool, type DoomPluginTool, type DoomPluginToolResult } from '@agimon-ai/doompi-core/piExtension';\nimport { Check } from 'typebox/value';\n\nimport type { DoomLoopLaunchersService } from '../../schemas/loopLaunchers';\nimport { LoopListSchema, LoopStartSchema, LoopStopSchema } from '../../schemas/loopTools';\n\nfunction result(details: unknown): DoomPluginToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(details, null, 2) }], details };\n}\n\nexport function createLoopTools(\n launchers: () => DoomLoopLaunchersService,\n assertEnabled: () => void,\n): readonly DoomPluginTool[] {\n return [\n defineTool({\n name: 'loop_list',\n label: 'List loops',\n description: 'List active session loops and available launcher types with their configuration schemas.',\n parameters: LoopListSchema,\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopListSchema, input)) throw new Error('loop_list takes no arguments.');\n const service = launchers();\n return result({ launchers: service.listLaunchers(), instances: service.listInstances() });\n },\n }),\n defineTool({\n name: 'loop_start',\n label: 'Start loop',\n description:\n 'Start a session-scoped loop without operator dialogs. Read loop_list for launcher IDs and input schemas. Loops stop when the session shuts down.',\n parameters: LoopStartSchema,\n executionMode: 'serial',\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopStartSchema, input)) throw new Error('Provide launcherId, input, and optionally instanceId.');\n return result(\n (await launchers().launch(input.launcherId, { ...input, interactive: false, signal })) ?? { cancelled: true },\n );\n },\n }),\n defineTool({\n name: 'loop_stop',\n label: 'Stop loop',\n description:\n 'Stop one loop instance owned by the current session. Does not disable Loop mode or stop other loops.',\n parameters: LoopStopSchema,\n executionMode: 'serial',\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopStopSchema, input)) throw new Error('Provide instanceId and an optional reason.');\n return result({ stopped: await launchers().stop(input.instanceId, input.reason ?? 'Stopped by the agent.') });\n },\n }),\n ];\n}\n","/** 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 {\n if (instances.length === 0) return '';\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":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"loopView-C_OPuy8L.cjs","names":["Type","Check","defineTool","Check"],"sources":["../src/constants/piLoop.ts","../src/constants/loop.ts","../src/schemas/loopTools.ts","../src/services/loopLaunchers/index.ts","../src/services/loopTools/index.ts","../src/types/loopView.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-loop';\nexport const LOOPS_GROUP_ORDER = 60;\nexport const STATUS_KEY = 'doom-loop';\nexport const MODE_STATUS_ID = 'loop.active';\nexport const ACTIVE_MODE_COLOR = 'warning';\nexport const NOTIFY_INFO_LEVEL = 'info';\nexport const NOTIFY_WARNING_LEVEL = 'warning';\n","export const START_COMMAND_DESCRIPTION = 'Start a registered loop';\nexport const LIST_COMMAND_DESCRIPTION = 'List and stop active loops';\nexport const START_COMMAND_NAME = 'loop';\nexport const LIST_COMMAND_NAME = 'loops';\n","import { type Static, Type } from 'typebox';\nimport { Check } from 'typebox/value';\n\nexport const LoopListSchema = Type.Object({}, { additionalProperties: false });\nexport const LoopStartSchema = Type.Object(\n {\n launcherId: Type.String({ minLength: 1, maxLength: 256 }),\n instanceId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),\n input: Type.Record(Type.String(), Type.Unknown()),\n },\n { additionalProperties: false },\n);\nexport const LoopStopSchema = Type.Object(\n {\n instanceId: Type.String({ minLength: 1, maxLength: 256 }),\n reason: Type.Optional(Type.String({ minLength: 1, maxLength: 512 })),\n },\n { additionalProperties: false },\n);\n\nexport const IntervalLoopSchema = Type.Object(\n {\n prompt: Type.String({ minLength: 1, maxLength: 32768 }),\n intervalSeconds: Type.Integer({ minimum: 30, maximum: 3600 }),\n },\n { additionalProperties: false },\n);\nexport const CronLoopSchema = Type.Object(\n {\n prompt: Type.String({ minLength: 1, maxLength: 32768 }),\n cron: Type.String({ minLength: 1, maxLength: 256 }),\n timezone: Type.Optional(Type.String({ minLength: 1, maxLength: 128 })),\n },\n { additionalProperties: false },\n);\nexport type IntervalLoopInput = Static<typeof IntervalLoopSchema>;\nexport type CronLoopInput = Static<typeof CronLoopSchema>;\n\nexport function parseIntervalLoop(input: unknown): IntervalLoopInput {\n if (!Check(IntervalLoopSchema, input) || !input.prompt.trim()) {\n throw new Error('Provide a non-empty prompt and an integer intervalSeconds between 30 and 3600.');\n }\n return { ...input, prompt: input.prompt.trim() };\n}\n\nexport function parseCronLoop(input: unknown): CronLoopInput {\n if (!Check(CronLoopSchema, input) || !input.prompt.trim() || input.cron.trim().split(/\\s+/u).length !== 5) {\n throw new Error('Provide a non-empty prompt and a five-field cron expression, with an optional IANA timezone.');\n }\n const timezone = input.timezone ?? 'UTC';\n new Intl.DateTimeFormat('en', { timeZone: timezone }).format();\n return { ...input, prompt: input.prompt.trim(), cron: input.cron.trim(), timezone };\n}\n","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 if (typeof value !== 'string') throw new TypeError(`Invalid ${label}.`);\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 ...(definition.inputSchema ? { inputSchema: definition.inputSchema } : {}),\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 options?.signal?.throwIfAborted();\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 const abortSetup = (): void => entry.abortController.abort(options?.signal?.reason);\n options?.signal?.addEventListener('abort', abortSetup, { once: true });\n instances.set(instanceId, entry);\n notify();\n try {\n if (options?.interactive === false && !launcher.definition.inputSchema) {\n throw new Error(`Loop launcher '${normalizedLauncherId}' requires manual setup in Activity.`);\n }\n const handle = await launcher.definition.launch({\n instanceId,\n signal: entry.abortController.signal,\n ...(options?.input === undefined ? {} : { input: options.input }),\n ...(options?.interactive === undefined ? {} : { interactive: options.interactive }),\n });\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 } finally {\n options?.signal?.removeEventListener('abort', abortSetup);\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","import { defineTool, type DoomPluginTool, type DoomPluginToolResult } from '@agimon-ai/doompi-core/piExtension';\nimport { Check } from 'typebox/value';\n\nimport type { DoomLoopLaunchersService } from '../../schemas/loopLaunchers';\nimport { LoopListSchema, LoopStartSchema, LoopStopSchema } from '../../schemas/loopTools';\n\nfunction result(details: unknown): DoomPluginToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(details, null, 2) }], details };\n}\n\nexport function createLoopTools(\n launchers: () => DoomLoopLaunchersService,\n assertEnabled: () => void,\n): readonly DoomPluginTool[] {\n return [\n defineTool({\n name: 'loop_list',\n label: 'List loops',\n description: 'List active session loops and available launcher types with their configuration schemas.',\n parameters: LoopListSchema,\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopListSchema, input)) throw new Error('loop_list takes no arguments.');\n const service = launchers();\n return result({ launchers: service.listLaunchers(), instances: service.listInstances() });\n },\n }),\n defineTool({\n name: 'loop_start',\n label: 'Start loop',\n description:\n 'Start a session-scoped loop without operator dialogs. Read loop_list for launcher IDs and input schemas. Loops stop when the session shuts down.',\n parameters: LoopStartSchema,\n executionMode: 'serial',\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopStartSchema, input)) throw new Error('Provide launcherId, input, and optionally instanceId.');\n return result(\n (await launchers().launch(input.launcherId, { ...input, interactive: false, signal })) ?? { cancelled: true },\n );\n },\n }),\n defineTool({\n name: 'loop_stop',\n label: 'Stop loop',\n description:\n 'Stop one loop instance owned by the current session. Does not disable Loop mode or stop other loops.',\n parameters: LoopStopSchema,\n executionMode: 'serial',\n async execute(input, { signal }) {\n assertEnabled();\n signal?.throwIfAborted();\n if (!Check(LoopStopSchema, input)) throw new Error('Provide instanceId and an optional reason.');\n return result({ stopped: await launchers().stop(input.instanceId, input.reason ?? 'Stopped by the agent.') });\n },\n }),\n ];\n}\n","/** 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 {\n if (instances.length === 0) return '';\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":"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"}