@agimon-ai/doompi-loop 0.0.1-alpha.84 → 0.0.1-alpha.85
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/dist/extensions/pi.cjs +188 -147
- package/dist/extensions/pi.cjs.map +1 -1
- package/dist/extensions/pi.d.mts.map +1 -1
- package/dist/extensions/pi.mjs +179 -138
- package/dist/extensions/pi.mjs.map +1 -1
- package/dist/extensions/server.cjs +233 -227
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +234 -228
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +114 -11
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/{loopLaunchers-CPUUrDzQ.d.mts → loopLaunchers-Bciapxt0.d.mts} +9 -1
- package/dist/loopLaunchers-Bciapxt0.d.mts.map +1 -0
- package/dist/loopLaunchers-BtcLH_xN.cjs.map +1 -1
- package/dist/loopLaunchers-cCniFbVA.mjs.map +1 -1
- package/dist/loopLaunchers.d.mts +1 -1
- package/dist/{loopView-CmuIEbUa.mjs → loopView-BK4X_1UI.mjs} +153 -5
- package/dist/loopView-BK4X_1UI.mjs.map +1 -0
- package/dist/{loopView-CLIBhqYC.cjs → loopView-C_OPuy8L.cjs} +218 -4
- package/dist/loopView-C_OPuy8L.cjs.map +1 -0
- package/package.json +9 -7
- package/src/prompts/doompi-use-loop/SKILL.md +33 -13
- package/src/types/loopView.ts +2 -2
- package/dist/loopLaunchers-CPUUrDzQ.d.mts.map +0 -1
- package/dist/loopView-CLIBhqYC.cjs.map +0 -1
- package/dist/loopView-CmuIEbUa.mjs.map +0 -1
|
@@ -1,214 +1,204 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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";
|
|
3
|
+
import { composeRootHooks, defineRoot, defineRoutedContribution, defineTool } from "@agimon-ai/doompi-core/extensionFile";
|
|
3
4
|
import { defineMinorMode, serverMinorModes } from "@agimon-ai/doompi-minor-mode";
|
|
5
|
+
import { Cron } from "croner";
|
|
4
6
|
import { defineServerPlugin, readPackageResource } from "@agimon-ai/doompi-core/serverFacet";
|
|
5
7
|
//#region src/services/serverRuntime/index.ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
+
});
|
|
23
47
|
}
|
|
24
48
|
function createSessionState(host) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
activation: active ? "active" : "inactive",
|
|
36
|
-
condition: launchers?.listInstances().some(({ state }) => state !== "running") ? "queued" : "ready",
|
|
37
|
-
...active ? {
|
|
38
|
-
detail: `${launchers?.listInstances().length} active`,
|
|
39
|
-
color: "warning"
|
|
40
|
-
} : {},
|
|
41
|
-
actions: [{
|
|
42
|
-
id: "start",
|
|
43
|
-
enabled: canStart,
|
|
44
|
-
...canStart ? {} : { disabledReason: "No loop launchers are registered." }
|
|
45
|
-
}, {
|
|
46
|
-
id: "stop",
|
|
47
|
-
enabled: active,
|
|
48
|
-
...active ? {} : { disabledReason: "No loops are active." }
|
|
49
|
-
}]
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* The instances the activity dock lists.
|
|
54
|
-
*
|
|
55
|
-
* An empty list publishes an empty value rather than nothing, because the key
|
|
56
|
-
* being present is what keeps the group on screen as a launcher. The group
|
|
57
|
-
* declares no `hideWhenEmpty`, so an empty value is a header with no rows.
|
|
58
|
-
*/
|
|
59
|
-
const publishView = () => host.context.client.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(launchers?.listInstances() ?? []));
|
|
60
|
-
/** Both surfaces a loop shows up on: the minor-mode catalog and the activity dock. */
|
|
61
|
-
const publishMode = () => {
|
|
62
|
-
modeOwner?.publish();
|
|
63
|
-
publishView();
|
|
64
|
-
};
|
|
65
|
-
const startActivity = async (execution) => {
|
|
66
|
-
if (launchers !== void 0) return async () => void 0;
|
|
67
|
-
const dependencies = {
|
|
68
|
-
generation: `${execution.sessionId}:loop-launchers`,
|
|
69
|
-
createInstanceId: () => crypto.randomUUID(),
|
|
70
|
-
timestamp: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
71
|
-
};
|
|
72
|
-
const service = createDoomLoopLaunchersService(dependencies);
|
|
73
|
-
const registration = service.register({
|
|
74
|
-
id: DEFAULT_LAUNCHER_ID,
|
|
75
|
-
source: SOURCE,
|
|
76
|
-
label: "Default loop",
|
|
77
|
-
description: "Run a prompt in this session on an interval.",
|
|
78
|
-
async launch({ instanceId, signal }) {
|
|
79
|
-
const promptValue = await execution.client.request({
|
|
80
|
-
kind: "input",
|
|
81
|
-
title: "Loop prompt",
|
|
82
|
-
multiline: true
|
|
83
|
-
}, signal);
|
|
84
|
-
const prompt = typeof promptValue === "string" ? promptValue.trim() : "";
|
|
85
|
-
if (!prompt || signal.aborted) return void 0;
|
|
86
|
-
const interval = numberInput(await execution.client.request({
|
|
87
|
-
kind: "input",
|
|
88
|
-
title: "Loop interval in seconds",
|
|
89
|
-
initialValue: String(DEFAULT_INTERVAL_SECONDS)
|
|
90
|
-
}, signal), DEFAULT_INTERVAL_SECONDS);
|
|
91
|
-
let stopped = false;
|
|
92
|
-
const tick = () => {
|
|
93
|
-
if (stopped || signal.aborted) return;
|
|
94
|
-
execution.session.prompt(prompt).catch((error) => {
|
|
95
|
-
notify(execution, `Loop pass failed: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
96
|
-
});
|
|
97
|
-
};
|
|
98
|
-
const timer = setInterval(tick, interval * 1e3);
|
|
99
|
-
timer.unref?.();
|
|
100
|
-
tick();
|
|
101
|
-
return {
|
|
102
|
-
instanceId,
|
|
103
|
-
label: "Default loop",
|
|
104
|
-
detail: `every ${interval}s`,
|
|
105
|
-
stop() {
|
|
106
|
-
stopped = true;
|
|
107
|
-
clearInterval(timer);
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
launchers = service;
|
|
113
|
-
defaultRegistration = registration;
|
|
114
|
-
unsubscribe = service.subscribe(publishMode);
|
|
115
|
-
publishMode();
|
|
116
|
-
return async () => {
|
|
117
|
-
unsubscribe?.();
|
|
118
|
-
unsubscribe = void 0;
|
|
119
|
-
await registration.dispose("Headless loop activity stopped.");
|
|
120
|
-
await service.dispose("Headless loop activity stopped.");
|
|
121
|
-
if (launchers === service) launchers = void 0;
|
|
122
|
-
if (defaultRegistration === registration) defaultRegistration = void 0;
|
|
123
|
-
publishMode();
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
|
-
modeOwner = defineMinorMode({
|
|
49
|
+
const launchers = createDoomLoopLaunchersService({
|
|
50
|
+
generation: `${host.context.sessionId}:loop-launchers`,
|
|
51
|
+
createInstanceId: () => crypto.randomUUID(),
|
|
52
|
+
timestamp: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
53
|
+
});
|
|
54
|
+
let disposed = false;
|
|
55
|
+
const modeSelected = () => !disposed && (host.context.selection.state?.["minor-mode"] ?? []).includes("loop.active");
|
|
56
|
+
const publishView = () => host.context.client.setStatus(LOOP_VIEW_STATUS_KEY, formatLoopStatusView(launchers.listInstances()));
|
|
57
|
+
const modeOwner = defineMinorMode({
|
|
127
58
|
descriptor: {
|
|
128
|
-
source:
|
|
129
|
-
id:
|
|
59
|
+
source: PACKAGE_SOURCE,
|
|
60
|
+
id: MODE_STATUS_ID,
|
|
130
61
|
label: "Loop",
|
|
131
|
-
description: "
|
|
62
|
+
description: "Give the agent tools to create, inspect, and stop session loops.",
|
|
132
63
|
order: 60,
|
|
133
64
|
actions: [{
|
|
134
|
-
id: "
|
|
135
|
-
label: "
|
|
136
|
-
description: "
|
|
65
|
+
id: "activate",
|
|
66
|
+
label: "Activate",
|
|
67
|
+
description: "Expose loop tools to the agent.",
|
|
137
68
|
contexts: ["headless"],
|
|
138
|
-
parameters: [
|
|
139
|
-
name: "launcherId",
|
|
140
|
-
label: "Launcher",
|
|
141
|
-
kind: "string",
|
|
142
|
-
required: true,
|
|
143
|
-
minLength: 1
|
|
144
|
-
}, {
|
|
145
|
-
name: "instanceId",
|
|
146
|
-
label: "Instance ID",
|
|
147
|
-
kind: "string",
|
|
148
|
-
required: false,
|
|
149
|
-
minLength: 1
|
|
150
|
-
}]
|
|
69
|
+
parameters: []
|
|
151
70
|
}, {
|
|
152
|
-
id: "
|
|
153
|
-
label: "
|
|
154
|
-
description: "
|
|
71
|
+
id: "deactivate",
|
|
72
|
+
label: "Deactivate",
|
|
73
|
+
description: "Hide agent loop tools. Existing loops and manual controls remain available.",
|
|
155
74
|
contexts: ["headless"],
|
|
156
|
-
parameters: [
|
|
157
|
-
name: "instanceId",
|
|
158
|
-
label: "Instance ID",
|
|
159
|
-
kind: "string",
|
|
160
|
-
required: true,
|
|
161
|
-
minLength: 1
|
|
162
|
-
}, {
|
|
163
|
-
name: "reason",
|
|
164
|
-
label: "Reason",
|
|
165
|
-
kind: "string",
|
|
166
|
-
required: false,
|
|
167
|
-
minLength: 1
|
|
168
|
-
}]
|
|
75
|
+
parameters: []
|
|
169
76
|
}]
|
|
170
77
|
},
|
|
171
|
-
state:
|
|
172
|
-
|
|
78
|
+
state: () => ({
|
|
79
|
+
activation: modeSelected() ? "active" : "inactive",
|
|
80
|
+
condition: "ready",
|
|
81
|
+
...modeSelected() ? {
|
|
82
|
+
detail: `${launchers.listInstances().length} loops`,
|
|
83
|
+
color: "accent"
|
|
84
|
+
} : {},
|
|
85
|
+
actions: [{
|
|
86
|
+
id: "activate",
|
|
87
|
+
enabled: !modeSelected(),
|
|
88
|
+
...modeSelected() ? { disabledReason: "Loop mode is already active." } : {}
|
|
89
|
+
}, {
|
|
90
|
+
id: "deactivate",
|
|
91
|
+
enabled: modeSelected(),
|
|
92
|
+
...modeSelected() ? {} : { disabledReason: "Loop mode is not active." }
|
|
93
|
+
}]
|
|
94
|
+
}),
|
|
95
|
+
async handleAction(_runtime, actionId, _arguments, { signal }) {
|
|
173
96
|
signal.throwIfAborted();
|
|
174
|
-
if (actionId
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
97
|
+
if (actionId !== "activate" && actionId !== "deactivate") throw new Error(`Unknown loop mode action: ${actionId}`);
|
|
98
|
+
const modes = (host.context.selection.state?.["minor-mode"] ?? []).filter((mode) => mode !== MODE_STATUS_ID);
|
|
99
|
+
await host.changeSelection({
|
|
100
|
+
axis: "state",
|
|
101
|
+
key: "minor-mode",
|
|
102
|
+
values: actionId === "activate" ? [...modes, MODE_STATUS_ID] : modes
|
|
103
|
+
});
|
|
104
|
+
modeOwner.publish();
|
|
105
|
+
return { message: actionId === "activate" ? "Loop tools activated." : "Loop tools deactivated. Existing loops are unchanged." };
|
|
106
|
+
}
|
|
107
|
+
}).createOwner(void 0);
|
|
108
|
+
const publish = () => {
|
|
109
|
+
if (disposed) return;
|
|
110
|
+
modeOwner.publish();
|
|
111
|
+
publishView();
|
|
112
|
+
};
|
|
113
|
+
const unsubscribe = launchers.subscribe(publish);
|
|
114
|
+
let submitting = false;
|
|
115
|
+
for (const cron of [false, true]) launchers.register({
|
|
116
|
+
id: cron ? "doompi.cron" : "doompi.default",
|
|
117
|
+
source: PACKAGE_SOURCE,
|
|
118
|
+
label: cron ? "Cron loop" : "Default loop",
|
|
119
|
+
description: cron ? "Run a prompt on a five-field cron schedule." : "Run a prompt in this session on an interval.",
|
|
120
|
+
inputSchema: { ...cron ? CronLoopSchema : IntervalLoopSchema },
|
|
121
|
+
async launch(request) {
|
|
122
|
+
const execution = host.context;
|
|
123
|
+
const prepared = await prepareLoop(execution, request, cron);
|
|
124
|
+
if (!prepared || request.signal.aborted) return void 0;
|
|
125
|
+
let stopped = false;
|
|
126
|
+
const tick = async () => {
|
|
127
|
+
if (stopped || request.signal.aborted || submitting) return;
|
|
128
|
+
submitting = true;
|
|
129
|
+
try {
|
|
130
|
+
const activity = await execution.session.activity();
|
|
131
|
+
if (stopped || request.signal.aborted || !activity.isIdle || activity.hasPendingMessages) return;
|
|
132
|
+
await execution.session.prompt(prepared.prompt);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
await execution.client.notify({
|
|
135
|
+
body: `Loop pass failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
136
|
+
level: "warning"
|
|
196
137
|
});
|
|
138
|
+
} finally {
|
|
139
|
+
submitting = false;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
let stopTimer;
|
|
143
|
+
let detail;
|
|
144
|
+
if ("cron" in prepared) {
|
|
145
|
+
const job = new Cron(prepared.cron, {
|
|
146
|
+
timezone: prepared.timezone,
|
|
147
|
+
unref: true
|
|
148
|
+
}, tick);
|
|
149
|
+
if (job.nextRun() === null) {
|
|
150
|
+
job.stop();
|
|
151
|
+
throw new Error("Cron expression has no future run.");
|
|
197
152
|
}
|
|
198
|
-
|
|
199
|
-
|
|
153
|
+
stopTimer = () => {
|
|
154
|
+
job.stop();
|
|
155
|
+
};
|
|
156
|
+
detail = `${prepared.cron} (${prepared.timezone})`;
|
|
157
|
+
} else {
|
|
158
|
+
const timer = setInterval(() => {
|
|
159
|
+
tick();
|
|
160
|
+
}, prepared.intervalSeconds * 1e3);
|
|
161
|
+
timer.unref?.();
|
|
162
|
+
stopTimer = () => clearInterval(timer);
|
|
163
|
+
detail = `every ${prepared.intervalSeconds}s`;
|
|
164
|
+
tick();
|
|
200
165
|
}
|
|
201
|
-
|
|
166
|
+
return {
|
|
167
|
+
instanceId: request.instanceId,
|
|
168
|
+
label: cron ? "Cron loop" : "Default loop",
|
|
169
|
+
detail,
|
|
170
|
+
stop() {
|
|
171
|
+
stopped = true;
|
|
172
|
+
stopTimer();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
202
175
|
}
|
|
203
|
-
})
|
|
176
|
+
});
|
|
204
177
|
return {
|
|
205
|
-
|
|
178
|
+
loopTools: createLoopTools(() => launchers, () => {
|
|
179
|
+
if (!modeSelected()) throw new Error("Enable Loop minor mode before using agent loop tools.");
|
|
180
|
+
}).map((tool) => ({
|
|
181
|
+
...tool,
|
|
182
|
+
when: {
|
|
183
|
+
state: { "minor-mode": MODE_STATUS_ID },
|
|
184
|
+
attribution: {
|
|
185
|
+
kind: "minor",
|
|
186
|
+
mode: MODE_STATUS_ID
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
})),
|
|
190
|
+
services: [(context) => {
|
|
191
|
+
context.plugin((provider) => {
|
|
192
|
+
provider.provide(DOOM_LOOP_LAUNCHERS_SERVICE, launchers);
|
|
193
|
+
});
|
|
194
|
+
context.effect(() => host.subscribeSelection(publish));
|
|
195
|
+
}, serverMinorModes([modeOwner])],
|
|
206
196
|
resources: [{
|
|
207
197
|
when: {
|
|
208
|
-
state: { "minor-mode":
|
|
198
|
+
state: { "minor-mode": MODE_STATUS_ID },
|
|
209
199
|
attribution: {
|
|
210
200
|
kind: "minor",
|
|
211
|
-
mode:
|
|
201
|
+
mode: MODE_STATUS_ID
|
|
212
202
|
}
|
|
213
203
|
},
|
|
214
204
|
name: "doompi-use-loop",
|
|
@@ -216,29 +206,19 @@ function createSessionState(host) {
|
|
|
216
206
|
read: () => readPackageResource(import.meta.url, "src/prompts/doompi-use-loop/SKILL.md")
|
|
217
207
|
}],
|
|
218
208
|
activities: [{
|
|
219
|
-
when: {
|
|
220
|
-
state: { "minor-mode": MODE_ID },
|
|
221
|
-
attribution: {
|
|
222
|
-
kind: "minor",
|
|
223
|
-
mode: MODE_ID
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
209
|
name: "doompi-loop",
|
|
227
|
-
start
|
|
210
|
+
async start() {
|
|
211
|
+
publish();
|
|
212
|
+
return async () => {
|
|
213
|
+
await launchers.stopAll("Session loop activity stopped.");
|
|
214
|
+
publish();
|
|
215
|
+
};
|
|
216
|
+
}
|
|
228
217
|
}],
|
|
229
218
|
commands: [{
|
|
230
219
|
name: START_COMMAND_NAME,
|
|
231
|
-
description: "Start a registered loop.",
|
|
220
|
+
description: "Start a registered loop manually.",
|
|
232
221
|
async execute(args, execution) {
|
|
233
|
-
if (!launchers) {
|
|
234
|
-
const modes = host.context.selection.state?.["minor-mode"] ?? [];
|
|
235
|
-
await host.changeSelection({
|
|
236
|
-
axis: "state",
|
|
237
|
-
key: "minor-mode",
|
|
238
|
-
values: [...modes, MODE_ID]
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
if (!launchers) throw new Error("Loop activity is not active.");
|
|
242
222
|
const available = launchers.listLaunchers();
|
|
243
223
|
const selected = args.trim() || await execution.client.request({
|
|
244
224
|
kind: "select",
|
|
@@ -248,47 +228,57 @@ function createSessionState(host) {
|
|
|
248
228
|
value: entry.id
|
|
249
229
|
}))
|
|
250
230
|
});
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
+
});
|
|
256
237
|
}
|
|
257
238
|
}, {
|
|
258
239
|
name: LIST_COMMAND_NAME,
|
|
259
|
-
description: "List
|
|
260
|
-
async execute(
|
|
261
|
-
const
|
|
262
|
-
|
|
240
|
+
description: "List or stop session loops. Usage: /loops [stop instanceId].",
|
|
241
|
+
async execute(args, execution) {
|
|
242
|
+
const parts = args.trim().split(/\s+/u);
|
|
243
|
+
if (parts[0] === "stop" && parts.length === 2) {
|
|
244
|
+
const stopped = await launchers.stop(parts[1], "Stopped manually.");
|
|
245
|
+
await execution.client.notify({
|
|
246
|
+
body: stopped ? "Loop stopped." : "Loop instance was not active.",
|
|
247
|
+
level: "info"
|
|
248
|
+
});
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (args.trim()) throw new Error("Usage: /loops [stop instanceId]");
|
|
252
|
+
const instances = launchers.listInstances();
|
|
253
|
+
await execution.client.notify({
|
|
254
|
+
body: instances.length === 0 ? "No loops are active." : JSON.stringify(instances, null, 2),
|
|
255
|
+
level: "info"
|
|
256
|
+
});
|
|
263
257
|
}
|
|
264
258
|
}],
|
|
265
259
|
hooks: [{
|
|
266
260
|
event: "session_start",
|
|
267
261
|
async handle() {
|
|
268
|
-
|
|
262
|
+
publish();
|
|
269
263
|
}
|
|
270
264
|
}, {
|
|
271
|
-
when: {
|
|
272
|
-
state: { "minor-mode": MODE_ID },
|
|
273
|
-
attribution: {
|
|
274
|
-
kind: "minor",
|
|
275
|
-
mode: MODE_ID
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
265
|
event: "session_shutdown",
|
|
279
266
|
async handle() {
|
|
280
|
-
await launchers
|
|
267
|
+
await launchers.stopAll("Headless session shutdown.");
|
|
281
268
|
}
|
|
282
269
|
}],
|
|
270
|
+
onStart: publish,
|
|
283
271
|
async onDispose() {
|
|
284
|
-
|
|
285
|
-
|
|
272
|
+
if (disposed) return;
|
|
273
|
+
disposed = true;
|
|
274
|
+
unsubscribe();
|
|
275
|
+
await launchers.dispose("Headless loop facet disposed.");
|
|
286
276
|
}
|
|
287
277
|
};
|
|
288
278
|
}
|
|
289
279
|
//#endregion
|
|
290
280
|
//#region src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.ts
|
|
291
|
-
var runtime_server_default = (({ agent }) => agent ? createSessionState(agent) : {});
|
|
281
|
+
var runtime_server_default = (({ agent }) => agent ? createSessionState(agent) : { loopTools: [] });
|
|
292
282
|
//#endregion
|
|
293
283
|
//#region src/extensions/workspaces/sessions/(backend)/root.server.ts
|
|
294
284
|
var root_server_default = defineRoot((context) => {
|
|
@@ -318,14 +308,23 @@ var session_start_server_default = defineRoutedContribution((context) => context
|
|
|
318
308
|
//#region src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.ts
|
|
319
309
|
var doompi_use_loop_server_default = defineRoutedContribution((context) => context.root.resources?.[0], { cardinality: "optional" });
|
|
320
310
|
//#endregion
|
|
311
|
+
//#region src/extensions/workspaces/sessions/(backend)/tool/loop-list.server.ts
|
|
312
|
+
var loop_list_server_default = defineTool((context) => context.root.loopTools[0]);
|
|
313
|
+
//#endregion
|
|
314
|
+
//#region src/extensions/workspaces/sessions/(backend)/tool/loop-start.server.ts
|
|
315
|
+
var loop_start_server_default = defineTool((context) => context.root.loopTools[1]);
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/extensions/workspaces/sessions/(backend)/tool/loop-stop.server.ts
|
|
318
|
+
var loop_stop_server_default = defineTool((context) => context.root.loopTools[2]);
|
|
319
|
+
//#endregion
|
|
321
320
|
//#region generated/server.ts
|
|
322
321
|
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
323
322
|
/* v8 ignore next -- generated normalization branch */
|
|
324
323
|
const defined = (values) => values.filter((value) => value !== void 0);
|
|
325
|
-
const via = (identity, value) =>
|
|
324
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
326
325
|
...identity,
|
|
327
326
|
...value
|
|
328
|
-
}
|
|
327
|
+
};
|
|
329
328
|
const facet = defineServerPlugin({
|
|
330
329
|
name: "@agimon-ai/doompi-loop",
|
|
331
330
|
session: async (context) => {
|
|
@@ -345,6 +344,13 @@ const facet = defineServerPlugin({
|
|
|
345
344
|
get resources() {
|
|
346
345
|
return defined([at(doompi_use_loop_server_default, contextSession)]);
|
|
347
346
|
},
|
|
347
|
+
get tools() {
|
|
348
|
+
return [
|
|
349
|
+
via({ name: "loop_list" }, at(loop_list_server_default, contextSession)),
|
|
350
|
+
via({ name: "loop_start" }, at(loop_start_server_default, contextSession)),
|
|
351
|
+
via({ name: "loop_stop" }, at(loop_stop_server_default, contextSession))
|
|
352
|
+
];
|
|
353
|
+
},
|
|
348
354
|
services: [...scopeSession.services ?? []],
|
|
349
355
|
get activities() {
|
|
350
356
|
return [...scopeSession.activities ?? []];
|