@agimon-ai/po-loop 0.0.0-alpha.0

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.
@@ -0,0 +1,673 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ const require_loopLaunchers = require("../loopLaunchers-CfXIHzJR.cjs");
6
+ let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
7
+ let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
8
+ let _agimon_ai_po_minor_mode = require("@agimon-ai/po-minor-mode");
9
+ let croner = require("croner");
10
+ let typebox = require("typebox");
11
+ let typebox_value = require("typebox/value");
12
+ //#region src/constants/loop.ts
13
+ const LIST_COMMAND_NAME = "loops";
14
+ //#endregion
15
+ //#region src/constants/piLoop.ts
16
+ const PACKAGE_SOURCE = "@agimon-ai/po-loop";
17
+ const MODE_STATUS_ID = "loop";
18
+ //#endregion
19
+ //#region src/schemas/loopTools.ts
20
+ const LoopListSchema = typebox.Type.Object({}, { additionalProperties: false });
21
+ const LoopStartSchema = typebox.Type.Object({
22
+ launcherId: typebox.Type.String({
23
+ minLength: 1,
24
+ maxLength: 256
25
+ }),
26
+ instanceId: typebox.Type.Optional(typebox.Type.String({
27
+ minLength: 1,
28
+ maxLength: 256
29
+ })),
30
+ input: typebox.Type.Record(typebox.Type.String(), typebox.Type.Unknown())
31
+ }, { additionalProperties: false });
32
+ const LoopStopSchema = typebox.Type.Object({
33
+ instanceId: typebox.Type.String({
34
+ minLength: 1,
35
+ maxLength: 256
36
+ }),
37
+ reason: typebox.Type.Optional(typebox.Type.String({
38
+ minLength: 1,
39
+ maxLength: 512
40
+ }))
41
+ }, { additionalProperties: false });
42
+ const IntervalLoopSchema = typebox.Type.Object({
43
+ prompt: typebox.Type.String({
44
+ minLength: 1,
45
+ maxLength: 32768
46
+ }),
47
+ intervalSeconds: typebox.Type.Integer({
48
+ minimum: 30,
49
+ maximum: 3600
50
+ })
51
+ }, { additionalProperties: false });
52
+ const CronLoopSchema = typebox.Type.Object({
53
+ prompt: typebox.Type.String({
54
+ minLength: 1,
55
+ maxLength: 32768
56
+ }),
57
+ cron: typebox.Type.String({
58
+ minLength: 1,
59
+ maxLength: 256
60
+ }),
61
+ timezone: typebox.Type.Optional(typebox.Type.String({
62
+ minLength: 1,
63
+ maxLength: 128
64
+ }))
65
+ }, { additionalProperties: false });
66
+ function parseIntervalLoop(input) {
67
+ if (!(0, typebox_value.Check)(IntervalLoopSchema, input) || !input.prompt.trim()) throw new Error("Provide a non-empty prompt and an integer intervalSeconds between 30 and 3600.");
68
+ return {
69
+ ...input,
70
+ prompt: input.prompt.trim()
71
+ };
72
+ }
73
+ function parseCronLoop(input) {
74
+ if (!(0, typebox_value.Check)(CronLoopSchema, input) || !input.prompt.trim() || input.cron.trim().split(/\s+/u).length !== 5) throw new Error("Provide a non-empty prompt and a five-field cron expression, with an optional IANA timezone.");
75
+ const timezone = input.timezone ?? "UTC";
76
+ new Intl.DateTimeFormat("en", { timeZone: timezone }).format();
77
+ return {
78
+ ...input,
79
+ prompt: input.prompt.trim(),
80
+ cron: input.cron.trim(),
81
+ timezone
82
+ };
83
+ }
84
+ //#endregion
85
+ //#region src/types/loopView.ts
86
+ /** The browser-only status that carries active Loop instances into the activity dock. */
87
+ const LOOP_VIEW_STATUS_KEY = "doom-loop-instances";
88
+ new RegExp(String.raw`\u001b\[[0-9;]*m`, "gu");
89
+ /** Projects runtime snapshots into the JSON status consumed by the web plugin. */
90
+ function formatLoopStatusView(instances) {
91
+ if (instances.length === 0) return "";
92
+ return JSON.stringify(instances.map((instance) => ({
93
+ instanceId: instance.instanceId,
94
+ label: instance.label ?? instance.launcherLabel,
95
+ detail: instance.detail ?? instance.instanceId,
96
+ state: instance.state
97
+ })));
98
+ }
99
+ //#endregion
100
+ //#region src/services/loopLaunchers/index.ts
101
+ const MAX_ID_LENGTH = 256;
102
+ const MAX_GENERATION_LENGTH = 256;
103
+ const LAUNCHER_ID_LABEL = "loop launcher id";
104
+ const INSTANCE_ID_LABEL = "loop instance id";
105
+ function validId(value, label) {
106
+ if (typeof value !== "string") throw new TypeError(`Invalid ${label}.`);
107
+ const normalized = value.trim();
108
+ let hasControl = false;
109
+ for (const character of normalized) {
110
+ const code = character.codePointAt(0);
111
+ if (code !== void 0 && (code <= 31 || code === 127)) {
112
+ hasControl = true;
113
+ break;
114
+ }
115
+ }
116
+ if (!normalized || normalized.length > MAX_ID_LENGTH || hasControl) throw new Error(`Invalid ${label}.`);
117
+ return normalized;
118
+ }
119
+ function snapshot(entry) {
120
+ return {
121
+ instanceId: entry.instanceId,
122
+ launcherId: entry.launcher.definition.id,
123
+ launcherLabel: entry.launcher.definition.label,
124
+ ...entry.handle?.label ? { label: entry.handle.label } : {},
125
+ ...entry.handle?.detail ? { detail: entry.handle.detail } : {},
126
+ state: entry.state,
127
+ ...entry.startedAt ? { startedAt: entry.startedAt } : {}
128
+ };
129
+ }
130
+ /** Creates the provider-owned launcher registry for one active Doom session. */
131
+ function createDoomLoopLaunchersService({ generation, createInstanceId, timestamp }) {
132
+ if (!generation || generation.length > MAX_GENERATION_LENGTH) throw new TypeError("Doom loop launchers require a valid generation.");
133
+ const launchers = /* @__PURE__ */ new Map();
134
+ const instances = /* @__PURE__ */ new Map();
135
+ const listeners = /* @__PURE__ */ new Set();
136
+ let registrationSequence = 0;
137
+ let disposed = false;
138
+ const ensureActive = () => {
139
+ if (disposed) throw new Error("The Doom loop-launchers service is disposed.");
140
+ };
141
+ const notify = () => {
142
+ for (const listener of listeners) listener();
143
+ };
144
+ const stopEntry = async (entry, reason) => {
145
+ if (instances.get(entry.instanceId) !== entry) return;
146
+ entry.state = "stopping";
147
+ entry.abortController.abort(reason);
148
+ notify();
149
+ try {
150
+ await entry.handle?.stop(reason);
151
+ } finally {
152
+ if (instances.get(entry.instanceId) === entry) instances.delete(entry.instanceId);
153
+ notify();
154
+ }
155
+ };
156
+ return Object.freeze({
157
+ generation,
158
+ register(definition) {
159
+ ensureActive();
160
+ const id = validId(definition.id, LAUNCHER_ID_LABEL);
161
+ if (launchers.has(id)) throw new Error(`Loop launcher '${id}' is already registered.`);
162
+ const token = Symbol(id);
163
+ const entry = {
164
+ token,
165
+ definition: {
166
+ ...definition,
167
+ id
168
+ }
169
+ };
170
+ launchers.set(id, entry);
171
+ registrationSequence += 1;
172
+ notify();
173
+ let registrationDisposed = false;
174
+ return Object.freeze({
175
+ id,
176
+ generation: `${generation}:launcher:${registrationSequence}`,
177
+ async dispose(reason = "Loop launcher disposed.") {
178
+ if (registrationDisposed) return;
179
+ registrationDisposed = true;
180
+ if (launchers.get(id)?.token !== token) return;
181
+ launchers.delete(id);
182
+ const owned = [...instances.values()].filter((instance) => instance.launcher.token === token);
183
+ await Promise.allSettled(owned.map((instance) => stopEntry(instance, reason)));
184
+ notify();
185
+ }
186
+ });
187
+ },
188
+ listLaunchers() {
189
+ ensureActive();
190
+ return [...launchers.values()].map(({ definition }) => ({
191
+ id: definition.id,
192
+ source: definition.source,
193
+ label: definition.label,
194
+ ...definition.description ? { description: definition.description } : {},
195
+ ...definition.inputSchema ? { inputSchema: definition.inputSchema } : {}
196
+ })).sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id));
197
+ },
198
+ listInstances() {
199
+ ensureActive();
200
+ return [...instances.values()].map(snapshot).sort((left, right) => left.instanceId.localeCompare(right.instanceId));
201
+ },
202
+ subscribe(listener) {
203
+ ensureActive();
204
+ listeners.add(listener);
205
+ let subscribed = true;
206
+ return () => {
207
+ if (!subscribed) return;
208
+ subscribed = false;
209
+ listeners.delete(listener);
210
+ };
211
+ },
212
+ async launch(launcherId, options) {
213
+ ensureActive();
214
+ options?.signal?.throwIfAborted();
215
+ const normalizedLauncherId = validId(launcherId, LAUNCHER_ID_LABEL);
216
+ const launcher = launchers.get(normalizedLauncherId);
217
+ if (!launcher) throw new Error(`Loop launcher '${normalizedLauncherId}' is unavailable.`);
218
+ const instanceId = validId(options?.instanceId ?? createInstanceId(), INSTANCE_ID_LABEL);
219
+ if (instances.has(instanceId)) throw new Error(`Loop instance '${instanceId}' already exists.`);
220
+ const entry = {
221
+ instanceId,
222
+ launcher,
223
+ abortController: new AbortController(),
224
+ state: "starting"
225
+ };
226
+ const abortSetup = () => entry.abortController.abort(options?.signal?.reason);
227
+ options?.signal?.addEventListener("abort", abortSetup, { once: true });
228
+ instances.set(instanceId, entry);
229
+ notify();
230
+ try {
231
+ if (options?.interactive === false && !launcher.definition.inputSchema) throw new Error(`Loop launcher '${normalizedLauncherId}' requires manual setup in Activity.`);
232
+ const handle = await launcher.definition.launch({
233
+ instanceId,
234
+ signal: entry.abortController.signal,
235
+ ...options?.input === void 0 ? {} : { input: options.input },
236
+ ...options?.interactive === void 0 ? {} : { interactive: options.interactive }
237
+ });
238
+ const active = !disposed && instances.get(instanceId) === entry && !entry.abortController.signal.aborted;
239
+ if (!handle) {
240
+ if (instances.get(instanceId) === entry) instances.delete(instanceId);
241
+ notify();
242
+ return;
243
+ }
244
+ if (!active || handle.instanceId !== instanceId) {
245
+ await handle.stop(active ? "Loop launcher returned a mismatched instance id." : "Loop launch was cancelled.");
246
+ if (instances.get(instanceId) === entry) instances.delete(instanceId);
247
+ notify();
248
+ if (active) throw new Error(`Loop launcher '${normalizedLauncherId}' returned a mismatched instance id.`);
249
+ return;
250
+ }
251
+ entry.handle = handle;
252
+ entry.state = "running";
253
+ entry.startedAt = timestamp();
254
+ notify();
255
+ return snapshot(entry);
256
+ } catch (error) {
257
+ const cancelled = entry.abortController.signal.aborted || disposed;
258
+ if (instances.get(instanceId) === entry) instances.delete(instanceId);
259
+ notify();
260
+ if (cancelled) return void 0;
261
+ throw error;
262
+ } finally {
263
+ options?.signal?.removeEventListener("abort", abortSetup);
264
+ }
265
+ },
266
+ async stop(instanceId, reason) {
267
+ ensureActive();
268
+ const entry = instances.get(validId(instanceId, INSTANCE_ID_LABEL));
269
+ if (!entry) return false;
270
+ await stopEntry(entry, reason);
271
+ return true;
272
+ },
273
+ async stopAll(reason) {
274
+ if (disposed && instances.size === 0) return;
275
+ await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));
276
+ },
277
+ async dispose(reason = "Doom loop-launchers service disposed.") {
278
+ if (disposed) return;
279
+ disposed = true;
280
+ launchers.clear();
281
+ await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));
282
+ listeners.clear();
283
+ }
284
+ });
285
+ }
286
+ //#endregion
287
+ //#region src/services/loopTools/index.ts
288
+ function result(details) {
289
+ return {
290
+ content: [{
291
+ type: "text",
292
+ text: JSON.stringify(details, null, 2)
293
+ }],
294
+ details
295
+ };
296
+ }
297
+ function createLoopTools(launchers, assertEnabled) {
298
+ return [
299
+ (0, _agimon_ai_po_core_serverFacet.defineTool)({
300
+ name: "loop_list",
301
+ label: "List loops",
302
+ description: "List active session loops and available launcher types with their configuration schemas.",
303
+ parameters: LoopListSchema,
304
+ async execute(input, { signal }) {
305
+ assertEnabled();
306
+ signal?.throwIfAborted();
307
+ if (!(0, typebox_value.Check)(LoopListSchema, input)) throw new Error("loop_list takes no arguments.");
308
+ const service = launchers();
309
+ return result({
310
+ launchers: service.listLaunchers(),
311
+ instances: service.listInstances()
312
+ });
313
+ }
314
+ }),
315
+ (0, _agimon_ai_po_core_serverFacet.defineTool)({
316
+ name: "loop_start",
317
+ label: "Start loop",
318
+ description: "Start a session-scoped loop without operator dialogs. Read loop_list for launcher IDs and input schemas. Loops stop when the session shuts down.",
319
+ parameters: LoopStartSchema,
320
+ executionMode: "serial",
321
+ async execute(input, { signal }) {
322
+ assertEnabled();
323
+ signal?.throwIfAborted();
324
+ if (!(0, typebox_value.Check)(LoopStartSchema, input)) throw new Error("Provide launcherId, input, and optionally instanceId.");
325
+ return result(await launchers().launch(input.launcherId, {
326
+ ...input,
327
+ interactive: false,
328
+ signal
329
+ }) ?? { cancelled: true });
330
+ }
331
+ }),
332
+ (0, _agimon_ai_po_core_serverFacet.defineTool)({
333
+ name: "loop_stop",
334
+ label: "Stop loop",
335
+ description: "Stop one loop instance owned by the current session. Does not disable Loop mode or stop other loops.",
336
+ parameters: LoopStopSchema,
337
+ executionMode: "serial",
338
+ async execute(input, { signal }) {
339
+ assertEnabled();
340
+ signal?.throwIfAborted();
341
+ if (!(0, typebox_value.Check)(LoopStopSchema, input)) throw new Error("Provide instanceId and an optional reason.");
342
+ return result({ stopped: await launchers().stop(input.instanceId, input.reason ?? "Stopped by the agent.") });
343
+ }
344
+ })
345
+ ];
346
+ }
347
+ //#endregion
348
+ //#region src/services/serverRuntime/index.ts
349
+ /** Loops are set by the agent, so a launch always carries its configuration. */
350
+ function prepareLoop(request, cron) {
351
+ if (request.input === void 0) throw new Error("Loop configuration is required. Ask the agent to set the loop.");
352
+ return cron ? parseCronLoop(request.input) : parseIntervalLoop(request.input);
353
+ }
354
+ function createSessionState(host) {
355
+ const launchers = createDoomLoopLaunchersService({
356
+ generation: `${host.context.sessionId}:loop-launchers`,
357
+ createInstanceId: () => crypto.randomUUID(),
358
+ timestamp: () => (/* @__PURE__ */ new Date()).toISOString()
359
+ });
360
+ let disposed = false;
361
+ const modeSelected = () => !disposed && (host.context.selection.state?.["minor-mode"] ?? []).includes("loop");
362
+ const publishView = () => host.context.client.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(launchers.listInstances()));
363
+ const modeOwner = (0, _agimon_ai_po_minor_mode.defineMinorMode)({
364
+ descriptor: {
365
+ source: PACKAGE_SOURCE,
366
+ id: MODE_STATUS_ID,
367
+ label: "Loop",
368
+ description: "Give the agent tools to create, inspect, and stop session loops.",
369
+ order: 60,
370
+ actions: [{
371
+ id: "activate",
372
+ label: "Activate",
373
+ description: "Expose loop tools to the agent.",
374
+ contexts: ["headless"],
375
+ parameters: []
376
+ }, {
377
+ id: "deactivate",
378
+ label: "Deactivate",
379
+ description: "Hide agent loop tools. Existing loops and manual controls remain available.",
380
+ contexts: ["headless"],
381
+ parameters: []
382
+ }]
383
+ },
384
+ state: () => ({
385
+ activation: modeSelected() ? "active" : "inactive",
386
+ condition: "ready",
387
+ ...modeSelected() ? {
388
+ detail: `${launchers.listInstances().length} loops`,
389
+ color: "accent"
390
+ } : {},
391
+ actions: [{
392
+ id: "activate",
393
+ enabled: !modeSelected(),
394
+ ...modeSelected() ? { disabledReason: "Loop mode is already active." } : {}
395
+ }, {
396
+ id: "deactivate",
397
+ enabled: modeSelected(),
398
+ ...modeSelected() ? {} : { disabledReason: "Loop mode is not active." }
399
+ }]
400
+ }),
401
+ async handleAction(_runtime, actionId, _arguments, { signal }) {
402
+ signal.throwIfAborted();
403
+ if (actionId !== "activate" && actionId !== "deactivate") throw new Error(`Unknown loop mode action: ${actionId}`);
404
+ const modes = (host.context.selection.state?.["minor-mode"] ?? []).filter((mode) => mode !== MODE_STATUS_ID);
405
+ await host.changeSelection({
406
+ axis: "state",
407
+ key: "minor-mode",
408
+ values: actionId === "activate" ? [...modes, MODE_STATUS_ID] : modes
409
+ });
410
+ modeOwner.publish();
411
+ return { message: actionId === "activate" ? "Loop tools activated." : "Loop tools deactivated. Existing loops are unchanged." };
412
+ }
413
+ }).createOwner(void 0);
414
+ const publish = () => {
415
+ if (disposed) return;
416
+ modeOwner.publish();
417
+ publishView();
418
+ };
419
+ const unsubscribe = launchers.subscribe(publish);
420
+ let submitting = false;
421
+ const pending = /* @__PURE__ */ new Set();
422
+ for (const cron of [false, true]) launchers.register({
423
+ id: cron ? "po.cron" : "po.default",
424
+ source: PACKAGE_SOURCE,
425
+ label: cron ? "Cron loop" : "Default loop",
426
+ description: cron ? "Run a prompt on a five-field cron schedule." : "Run a prompt in this session on an interval.",
427
+ inputSchema: { ...cron ? CronLoopSchema : IntervalLoopSchema },
428
+ async launch(request) {
429
+ const execution = host.context;
430
+ const prepared = prepareLoop(request, cron);
431
+ if (!prepared || request.signal.aborted) return void 0;
432
+ let stopped = false;
433
+ const tick = async (due = true) => {
434
+ if (disposed || stopped || request.signal.aborted) return;
435
+ if (due) pending.add(replay);
436
+ if (submitting || !pending.has(replay)) return;
437
+ submitting = true;
438
+ try {
439
+ const activity = await execution.session.activity();
440
+ if (disposed || stopped || request.signal.aborted || !activity.isIdle || activity.hasPendingMessages) return;
441
+ pending.delete(replay);
442
+ if (!execution.session.admitPrompt) throw new Error("Loop prompt admission is unavailable.");
443
+ await execution.session.admitPrompt(prepared.prompt);
444
+ } catch (error) {
445
+ await execution.client.notify({
446
+ body: `Loop pass failed: ${error instanceof Error ? error.message : String(error)}`,
447
+ level: "warning"
448
+ });
449
+ } finally {
450
+ submitting = false;
451
+ }
452
+ };
453
+ const replay = () => tick(false);
454
+ let stopTimer;
455
+ let detail;
456
+ if ("cron" in prepared) {
457
+ const job = new croner.Cron(prepared.cron, {
458
+ timezone: prepared.timezone,
459
+ unref: true
460
+ }, () => tick());
461
+ if (job.nextRun() === null) {
462
+ job.stop();
463
+ throw new Error("Cron expression has no future run.");
464
+ }
465
+ stopTimer = () => {
466
+ job.stop();
467
+ };
468
+ detail = `${prepared.cron} (${prepared.timezone})`;
469
+ } else {
470
+ const timer = setInterval(() => {
471
+ tick();
472
+ }, prepared.intervalSeconds * 1e3);
473
+ timer.unref?.();
474
+ stopTimer = () => clearInterval(timer);
475
+ detail = `every ${prepared.intervalSeconds}s`;
476
+ tick();
477
+ }
478
+ return {
479
+ instanceId: request.instanceId,
480
+ label: cron ? "Cron loop" : "Default loop",
481
+ detail,
482
+ stop() {
483
+ stopped = true;
484
+ pending.delete(replay);
485
+ stopTimer();
486
+ }
487
+ };
488
+ }
489
+ });
490
+ return {
491
+ loopTools: createLoopTools(() => launchers, () => {
492
+ if (!modeSelected()) throw new Error("Enable Loop minor mode before using agent loop tools.");
493
+ }).map((tool) => ({
494
+ ...tool,
495
+ when: {
496
+ state: { "minor-mode": MODE_STATUS_ID },
497
+ attribution: {
498
+ kind: "minor",
499
+ mode: MODE_STATUS_ID
500
+ }
501
+ }
502
+ })),
503
+ services: [(context) => {
504
+ context.plugin((provider) => {
505
+ provider.provide(require_loopLaunchers.DOOM_LOOP_LAUNCHERS_SERVICE, launchers);
506
+ });
507
+ context.effect(() => host.subscribeSelection(publish));
508
+ }, (0, _agimon_ai_po_minor_mode.serverMinorModes)([modeOwner])],
509
+ resources: [{
510
+ when: {
511
+ state: { "minor-mode": MODE_STATUS_ID },
512
+ attribution: {
513
+ kind: "minor",
514
+ mode: MODE_STATUS_ID
515
+ }
516
+ },
517
+ name: "po-use-loop",
518
+ kind: "skill",
519
+ read: () => (0, _agimon_ai_po_core_serverFacet.readPackageResource)(require("url").pathToFileURL(__filename).href, "src/prompts/po-use-loop/SKILL.md")
520
+ }],
521
+ activities: [{
522
+ name: "po-loop",
523
+ async start() {
524
+ publish();
525
+ return async () => {
526
+ await launchers.stopAll("Session loop activity stopped.");
527
+ publish();
528
+ };
529
+ }
530
+ }],
531
+ commands: [{
532
+ name: LIST_COMMAND_NAME,
533
+ description: "List or stop session loops. Usage: /loops [stop instanceId].",
534
+ async execute(args, execution) {
535
+ const parts = args.trim().split(/\s+/u);
536
+ if (parts[0] === "stop" && parts.length === 2) {
537
+ const stopped = await launchers.stop(parts[1], "Stopped manually.");
538
+ await execution.client.notify({
539
+ body: stopped ? "Loop stopped." : "Loop instance was not active.",
540
+ level: "info"
541
+ });
542
+ return;
543
+ }
544
+ if (args.trim()) throw new Error("Usage: /loops [stop instanceId]");
545
+ const instances = launchers.listInstances();
546
+ await execution.client.notify({
547
+ body: instances.length === 0 ? "No loops are active." : JSON.stringify(instances, null, 2),
548
+ level: "info"
549
+ });
550
+ }
551
+ }],
552
+ hooks: [
553
+ {
554
+ event: "agent_settled",
555
+ async handle() {
556
+ if (!disposed) await pending.values().next().value?.();
557
+ }
558
+ },
559
+ {
560
+ event: "session_start",
561
+ async handle() {
562
+ publish();
563
+ }
564
+ },
565
+ {
566
+ event: "session_shutdown",
567
+ async handle() {
568
+ await launchers.stopAll("Headless session shutdown.");
569
+ }
570
+ }
571
+ ],
572
+ onStart: publish,
573
+ async onDispose() {
574
+ if (disposed) return;
575
+ disposed = true;
576
+ pending.clear();
577
+ unsubscribe();
578
+ await launchers.dispose("Headless loop facet disposed.");
579
+ }
580
+ };
581
+ }
582
+ //#endregion
583
+ //#region src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.ts
584
+ var runtime_server_default = (({ agent }) => agent ? createSessionState(agent) : { loopTools: [] });
585
+ //#endregion
586
+ //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
587
+ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((context) => {
588
+ const value = runtime_server_default(context);
589
+ return {
590
+ value,
591
+ services: value.services,
592
+ activities: value.activities,
593
+ onStart: value.onStart,
594
+ onStop: value.onStop,
595
+ onDispose: value.onDispose
596
+ };
597
+ });
598
+ //#endregion
599
+ //#region src/extensions/workspaces/sessions/(backend)/command/loops.server.ts
600
+ var loops_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.commands?.[0], { cardinality: "optional" });
601
+ //#endregion
602
+ //#region src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server.ts
603
+ var agent_settled_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.hooks?.find((hook) => hook.event === "agent_settled"), { cardinality: "optional" });
604
+ //#endregion
605
+ //#region src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts
606
+ var session_shutdown_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.hooks?.find((hook) => hook.event === "session_shutdown"), { cardinality: "optional" });
607
+ //#endregion
608
+ //#region src/extensions/workspaces/sessions/(backend)/hook/session-start.server.ts
609
+ var session_start_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.hooks?.find((hook) => hook.event === "session_start"), { cardinality: "optional" });
610
+ //#endregion
611
+ //#region src/extensions/workspaces/sessions/(backend)/resource/po-use-loop.server.ts
612
+ var po_use_loop_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.resources?.[0], { cardinality: "optional" });
613
+ //#endregion
614
+ //#region src/extensions/workspaces/sessions/(backend)/tool/loop-list.server.ts
615
+ var loop_list_server_default = (0, _agimon_ai_po_core_extensionFile.defineTool)((context) => context.root.loopTools[0]);
616
+ //#endregion
617
+ //#region src/extensions/workspaces/sessions/(backend)/tool/loop-start.server.ts
618
+ var loop_start_server_default = (0, _agimon_ai_po_core_extensionFile.defineTool)((context) => context.root.loopTools[1]);
619
+ //#endregion
620
+ //#region src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server.ts
621
+ var loop_stop_server_default = (0, _agimon_ai_po_core_extensionFile.defineTool)((context) => context.root.loopTools[2]);
622
+ //#endregion
623
+ //#region generated/server.ts
624
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
625
+ /* v8 ignore next -- generated normalization branch */
626
+ const defined = (values) => values.filter((value) => value !== void 0);
627
+ const via = (identity, value) => value === void 0 ? void 0 : {
628
+ ...identity,
629
+ ...value
630
+ };
631
+ const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
632
+ name: "@agimon-ai/po-loop",
633
+ providerOverrides: (0, _agimon_ai_po_core_serverFacet.composeServerProviderOverrides)(root_server_default),
634
+ session: async (context) => {
635
+ const scopeSession = await root_server_default(context);
636
+ const contextSession = {
637
+ ...context,
638
+ root: scopeSession.value
639
+ };
640
+ return {
641
+ ...(0, _agimon_ai_po_core_extensionFile.composeRootHooks)(scopeSession),
642
+ get commands() {
643
+ return defined([via({ name: "loops" }, at(loops_server_default, contextSession))]);
644
+ },
645
+ get hooks() {
646
+ return defined([
647
+ via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)),
648
+ via({ event: "session_shutdown" }, at(session_shutdown_server_default, contextSession)),
649
+ via({ event: "session_start" }, at(session_start_server_default, contextSession))
650
+ ]);
651
+ },
652
+ get resources() {
653
+ return defined([at(po_use_loop_server_default, contextSession)]);
654
+ },
655
+ get tools() {
656
+ return [
657
+ via({ name: "loop_list" }, at(loop_list_server_default, contextSession)),
658
+ via({ name: "loop_start" }, at(loop_start_server_default, contextSession)),
659
+ via({ name: "loop_stop" }, at(loop_stop_server_default, contextSession))
660
+ ];
661
+ },
662
+ services: [...scopeSession.services ?? []],
663
+ get activities() {
664
+ return [...scopeSession.activities ?? []];
665
+ }
666
+ };
667
+ }
668
+ });
669
+ //#endregion
670
+ exports.default = facet;
671
+ exports.facet = facet;
672
+
673
+ //# sourceMappingURL=server.cjs.map