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