@rync/moorline-discord-default 0.0.2
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/index.mjs +9 -0
- package/manifest.json +21 -0
- package/moorline.dist.json +26 -0
- package/package.json +36 -0
- package/packages/plugins/rync/discord-runtime/index.mjs +1195 -0
- package/packages/plugins/rync/discord-runtime/manifest.json +37 -0
- package/packages/plugins/rync/discord-runtime/modules/routing/coordination.md +9 -0
- package/packages/plugins/rync/discord-runtime/modules/routing/session.md +7 -0
- package/packages/plugins/rync/discord-runtime/moorline.dist.json +8 -0
- package/packages/transports/rync/discord/index.mjs +76839 -0
- package/packages/transports/rync/discord/manifest.json +48 -0
- package/packages/transports/rync/discord/moorline.dist.json +26 -0
|
@@ -0,0 +1,1195 @@
|
|
|
1
|
+
import { createRequire as __moorlineCreateRequire } from 'node:module'; const require = __moorlineCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// packages/discord-runtime/manifest.json
|
|
4
|
+
var manifest_default = {
|
|
5
|
+
id: "rync/discord-runtime",
|
|
6
|
+
name: "rync/discord-runtime",
|
|
7
|
+
version: "0.0.2",
|
|
8
|
+
type: "plugin",
|
|
9
|
+
description: "Discord runtime commands and message routing for Moorline.",
|
|
10
|
+
entrypoint: "index.mjs",
|
|
11
|
+
priority: 100,
|
|
12
|
+
capabilities: [
|
|
13
|
+
"runtime.control",
|
|
14
|
+
"memory.read",
|
|
15
|
+
"transport.message.send",
|
|
16
|
+
"transport.resource.create",
|
|
17
|
+
"transport.resource.update",
|
|
18
|
+
"transport.resource.delete",
|
|
19
|
+
"net.connect",
|
|
20
|
+
"fs.read",
|
|
21
|
+
"session.inspect",
|
|
22
|
+
"session.create",
|
|
23
|
+
"session.archive",
|
|
24
|
+
"session.delete"
|
|
25
|
+
],
|
|
26
|
+
hooks: [
|
|
27
|
+
"onAction",
|
|
28
|
+
"onDomainEvent",
|
|
29
|
+
"onTransportEvent"
|
|
30
|
+
],
|
|
31
|
+
defaultEnabled: true,
|
|
32
|
+
dependencies: [
|
|
33
|
+
{
|
|
34
|
+
surface: "transport",
|
|
35
|
+
packageId: "rync/discord",
|
|
36
|
+
requiredState: "active",
|
|
37
|
+
reason: "Discord runtime routing and commands depend on the rync Discord transport."
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// packages/discord-runtime/modules/shared.mjs
|
|
43
|
+
function discordAction(id, title, commandName, commandDescription, subcommandName, subcommandDescription, options, policy) {
|
|
44
|
+
if (Array.isArray(subcommandName) || subcommandName === void 0 && subcommandDescription && typeof subcommandDescription === "object" && !Array.isArray(subcommandDescription)) {
|
|
45
|
+
policy = subcommandDescription;
|
|
46
|
+
options = subcommandName;
|
|
47
|
+
subcommandDescription = void 0;
|
|
48
|
+
subcommandName = void 0;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
id,
|
|
52
|
+
title,
|
|
53
|
+
description: subcommandDescription ?? commandDescription,
|
|
54
|
+
...policy ? { policy } : {},
|
|
55
|
+
metadata: {
|
|
56
|
+
discordCommand: {
|
|
57
|
+
commandName,
|
|
58
|
+
commandDescription,
|
|
59
|
+
...subcommandName ? { subcommandName } : {},
|
|
60
|
+
...subcommandDescription ? { subcommandDescription } : {},
|
|
61
|
+
...options ? { options } : {}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function stringOption(input, key) {
|
|
67
|
+
const value = input[key];
|
|
68
|
+
return typeof value === "string" ? value.trim() : "";
|
|
69
|
+
}
|
|
70
|
+
function toRuntimeReply(payload) {
|
|
71
|
+
const blocks = (payload.embeds ?? []).map((embed) => ({
|
|
72
|
+
kind: "fields",
|
|
73
|
+
...embed.title ? { title: embed.title } : {},
|
|
74
|
+
...embed.description ? { text: embed.description } : {},
|
|
75
|
+
...embed.fields ? {
|
|
76
|
+
fields: embed.fields.map((field) => ({
|
|
77
|
+
label: field.name,
|
|
78
|
+
value: field.value,
|
|
79
|
+
...field.inline !== void 0 ? { inline: field.inline } : {}
|
|
80
|
+
}))
|
|
81
|
+
} : {}
|
|
82
|
+
}));
|
|
83
|
+
return {
|
|
84
|
+
...payload.content ? { text: payload.content } : {},
|
|
85
|
+
...blocks.length > 0 ? { blocks } : {}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function reply(event, payload) {
|
|
89
|
+
const native = event.native?.payload;
|
|
90
|
+
if (native && typeof native === "object" && typeof native.reply === "function") {
|
|
91
|
+
await native.reply(payload);
|
|
92
|
+
return { handled: true };
|
|
93
|
+
}
|
|
94
|
+
return { handled: true, reply: toRuntimeReply(payload) };
|
|
95
|
+
}
|
|
96
|
+
async function defer(event, payload) {
|
|
97
|
+
const native = event.native?.payload;
|
|
98
|
+
if (native && typeof native === "object" && typeof native.defer === "function") {
|
|
99
|
+
await native.defer(payload);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function isMissingPermissions(error) {
|
|
103
|
+
return !!error && typeof error === "object" && error.code === 50013;
|
|
104
|
+
}
|
|
105
|
+
function workspaceDisplay(sessionId) {
|
|
106
|
+
return `runtime/workspaces/${sessionId}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// packages/discord-runtime/modules/admin-control/index.mjs
|
|
110
|
+
function currentThreadIdOrNull(context, transportResourceId) {
|
|
111
|
+
return context.getSessionByTransportResourceId(transportResourceId)?.threadId ?? null;
|
|
112
|
+
}
|
|
113
|
+
var admin_control_default = {
|
|
114
|
+
id: manifest_default.id,
|
|
115
|
+
manifest: manifest_default,
|
|
116
|
+
actions() {
|
|
117
|
+
return [
|
|
118
|
+
discordAction(
|
|
119
|
+
"runtime.admin.status",
|
|
120
|
+
"Admin status",
|
|
121
|
+
"admin",
|
|
122
|
+
"Administrative runtime controls",
|
|
123
|
+
"status",
|
|
124
|
+
"Show runtime control state",
|
|
125
|
+
void 0,
|
|
126
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
127
|
+
),
|
|
128
|
+
discordAction(
|
|
129
|
+
"runtime.admin.reload",
|
|
130
|
+
"Reload runtime",
|
|
131
|
+
"admin",
|
|
132
|
+
"Administrative runtime controls",
|
|
133
|
+
"reload",
|
|
134
|
+
"Reload the runtime worker",
|
|
135
|
+
[
|
|
136
|
+
{
|
|
137
|
+
type: "string",
|
|
138
|
+
name: "mode",
|
|
139
|
+
description: "Reload mode",
|
|
140
|
+
required: true,
|
|
141
|
+
choices: [
|
|
142
|
+
{ name: "graceful", value: "graceful" },
|
|
143
|
+
{ name: "force", value: "force" }
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
148
|
+
),
|
|
149
|
+
discordAction(
|
|
150
|
+
"runtime.admin.provider-stop",
|
|
151
|
+
"Stop provider sessions",
|
|
152
|
+
"admin",
|
|
153
|
+
"Administrative runtime controls",
|
|
154
|
+
"provider-stop",
|
|
155
|
+
"Stop provider sessions",
|
|
156
|
+
[
|
|
157
|
+
{
|
|
158
|
+
type: "string",
|
|
159
|
+
name: "scope",
|
|
160
|
+
description: "Whether to stop all sessions or only the current session",
|
|
161
|
+
required: true,
|
|
162
|
+
choices: [
|
|
163
|
+
{ name: "all", value: "all" },
|
|
164
|
+
{ name: "current", value: "current" }
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
169
|
+
),
|
|
170
|
+
discordAction(
|
|
171
|
+
"runtime.admin.provider-start",
|
|
172
|
+
"Recover provider sessions",
|
|
173
|
+
"admin",
|
|
174
|
+
"Administrative runtime controls",
|
|
175
|
+
"provider-start",
|
|
176
|
+
"Recover provider sessions",
|
|
177
|
+
[
|
|
178
|
+
{
|
|
179
|
+
type: "string",
|
|
180
|
+
name: "scope",
|
|
181
|
+
description: "Whether to recover all sessions or only the current session",
|
|
182
|
+
required: true,
|
|
183
|
+
choices: [
|
|
184
|
+
{ name: "all", value: "all" },
|
|
185
|
+
{ name: "current", value: "current" }
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
190
|
+
),
|
|
191
|
+
discordAction(
|
|
192
|
+
"runtime.admin.accepting",
|
|
193
|
+
"Toggle accepting work",
|
|
194
|
+
"admin",
|
|
195
|
+
"Administrative runtime controls",
|
|
196
|
+
"accepting",
|
|
197
|
+
"Enable or disable acceptance of new work",
|
|
198
|
+
[
|
|
199
|
+
{
|
|
200
|
+
type: "string",
|
|
201
|
+
name: "value",
|
|
202
|
+
description: "Whether the runtime should accept new work",
|
|
203
|
+
required: true,
|
|
204
|
+
choices: [
|
|
205
|
+
{ name: "true", value: "true" },
|
|
206
|
+
{ name: "false", value: "false" }
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
211
|
+
)
|
|
212
|
+
];
|
|
213
|
+
},
|
|
214
|
+
async onAction(event, context) {
|
|
215
|
+
if (!event.actionId.startsWith("runtime.admin.")) {
|
|
216
|
+
return { handled: false };
|
|
217
|
+
}
|
|
218
|
+
const requestedBy = event.actor;
|
|
219
|
+
if (!context.isAdminActor(requestedBy)) {
|
|
220
|
+
context.appendAuditEvent("runtime.admin.denied", {
|
|
221
|
+
actionId: event.actionId,
|
|
222
|
+
requestedBy: requestedBy.actorId,
|
|
223
|
+
accessGroupIds: requestedBy.accessGroupIds ?? [],
|
|
224
|
+
isSurfaceAdmin: requestedBy.isSurfaceAdmin === true
|
|
225
|
+
});
|
|
226
|
+
return await reply(event, {
|
|
227
|
+
content: "This command is restricted to Moorline admins.",
|
|
228
|
+
ephemeral: true
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (event.actionId === "runtime.admin.status") {
|
|
232
|
+
const control = context.getRuntimeControlStatus();
|
|
233
|
+
const runtime = context.getRuntimeStatus();
|
|
234
|
+
return await reply(event, {
|
|
235
|
+
content: "Moorline admin status",
|
|
236
|
+
embeds: [
|
|
237
|
+
{
|
|
238
|
+
title: "Runtime Control",
|
|
239
|
+
color: 3447003,
|
|
240
|
+
fields: [
|
|
241
|
+
{ name: "Supervised", value: control.supervised ? "yes" : "no", inline: true },
|
|
242
|
+
{ name: "Accepting Work", value: control.acceptingNewWork ? "yes" : "no", inline: true },
|
|
243
|
+
{ name: "Running Sessions", value: String(runtime.runningSessions), inline: true },
|
|
244
|
+
{ name: "Waiting Sessions", value: String(runtime.waitingSessions), inline: true }
|
|
245
|
+
],
|
|
246
|
+
timestamp: context.nowIso()
|
|
247
|
+
}
|
|
248
|
+
],
|
|
249
|
+
ephemeral: true
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (event.actionId === "runtime.admin.reload") {
|
|
253
|
+
const mode = stringOption(event.input, "mode") === "force" ? "force" : "graceful";
|
|
254
|
+
const result = await context.requestRuntimeReload({
|
|
255
|
+
mode,
|
|
256
|
+
reason: `Discord admin request from ${requestedBy.actorId}`,
|
|
257
|
+
requestedBy
|
|
258
|
+
});
|
|
259
|
+
context.appendAuditEvent("runtime.reload.requested.by-admin", {
|
|
260
|
+
requestedBy: requestedBy.actorId,
|
|
261
|
+
mode
|
|
262
|
+
});
|
|
263
|
+
return await reply(event, {
|
|
264
|
+
content: result.detail,
|
|
265
|
+
ephemeral: true
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (event.actionId === "runtime.admin.provider-stop") {
|
|
269
|
+
const threadId = stringOption(event.input, "scope") === "current" && event.transportResourceId ? currentThreadIdOrNull(context, event.transportResourceId) : null;
|
|
270
|
+
if (stringOption(event.input, "scope") === "current" && !threadId) {
|
|
271
|
+
return await reply(event, {
|
|
272
|
+
content: "This resource does not map to an active Moorline session.",
|
|
273
|
+
ephemeral: true
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
await context.stopProvider({
|
|
277
|
+
...threadId ? { threadId } : {},
|
|
278
|
+
reason: `Discord admin request from ${requestedBy.actorId}`,
|
|
279
|
+
requestedBy
|
|
280
|
+
});
|
|
281
|
+
return await reply(event, {
|
|
282
|
+
content: threadId ? `Stopped provider session ${threadId}.` : "Stopped all provider sessions.",
|
|
283
|
+
ephemeral: true
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
if (event.actionId === "runtime.admin.provider-start") {
|
|
287
|
+
const threadId = stringOption(event.input, "scope") === "current" && event.transportResourceId ? currentThreadIdOrNull(context, event.transportResourceId) : null;
|
|
288
|
+
if (stringOption(event.input, "scope") === "current" && !threadId) {
|
|
289
|
+
return await reply(event, {
|
|
290
|
+
content: "This resource does not map to an active Moorline session.",
|
|
291
|
+
ephemeral: true
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
await context.startProvider({
|
|
295
|
+
...threadId ? { threadId } : {},
|
|
296
|
+
reason: `Discord admin request from ${requestedBy.actorId}`,
|
|
297
|
+
requestedBy
|
|
298
|
+
});
|
|
299
|
+
return await reply(event, {
|
|
300
|
+
content: threadId ? `Recovered provider session ${threadId}.` : "Recovered all provider sessions.",
|
|
301
|
+
ephemeral: true
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (event.actionId === "runtime.admin.accepting") {
|
|
305
|
+
const accepting = stringOption(event.input, "value") === "true";
|
|
306
|
+
await context.setRuntimeAcceptingNewWork({
|
|
307
|
+
accepting,
|
|
308
|
+
reason: `Discord admin request from ${requestedBy.actorId}`,
|
|
309
|
+
requestedBy
|
|
310
|
+
});
|
|
311
|
+
return await reply(event, {
|
|
312
|
+
content: accepting ? "Runtime is now accepting new work." : "Runtime is no longer accepting new work.",
|
|
313
|
+
ephemeral: true
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
return await reply(event, {
|
|
317
|
+
content: `Unsupported admin action: ${event.actionId}`,
|
|
318
|
+
ephemeral: true
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// packages/discord-runtime/modules/channel-lifecycle/index.mjs
|
|
324
|
+
function summarizeArchivedTarget(target) {
|
|
325
|
+
return {
|
|
326
|
+
id: target.session.sessionId,
|
|
327
|
+
transportResourceId: target.session.transportResourceId,
|
|
328
|
+
workspacePath: target.session.workspacePath
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
var channel_lifecycle_default = {
|
|
332
|
+
id: manifest_default.id,
|
|
333
|
+
manifest: manifest_default,
|
|
334
|
+
actions() {
|
|
335
|
+
return [
|
|
336
|
+
discordAction(
|
|
337
|
+
"resource.archive",
|
|
338
|
+
"Archive the current Moorline session resource",
|
|
339
|
+
"archive",
|
|
340
|
+
"Archive the current Moorline session resource",
|
|
341
|
+
void 0,
|
|
342
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
343
|
+
),
|
|
344
|
+
discordAction(
|
|
345
|
+
"resource.delete",
|
|
346
|
+
"Delete the current archived Moorline session resource",
|
|
347
|
+
"delete",
|
|
348
|
+
"Delete the current archived Moorline session resource",
|
|
349
|
+
[
|
|
350
|
+
{
|
|
351
|
+
type: "string",
|
|
352
|
+
name: "confirm",
|
|
353
|
+
description: "Type delete to confirm destructive removal",
|
|
354
|
+
required: true,
|
|
355
|
+
choices: [{ name: "delete", value: "delete" }]
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
359
|
+
)
|
|
360
|
+
];
|
|
361
|
+
},
|
|
362
|
+
async onAction(event, context) {
|
|
363
|
+
if (event.actionId === "resource.archive") {
|
|
364
|
+
if (!event.transportResourceId) {
|
|
365
|
+
return await reply(event, { content: "This action requires a target resource.", ephemeral: true });
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
const archived = await context.archiveTransportResourceTarget({ transportResourceId: event.transportResourceId });
|
|
369
|
+
if (!archived) {
|
|
370
|
+
return await reply(event, {
|
|
371
|
+
content: "This resource is not an archivable Moorline session.",
|
|
372
|
+
ephemeral: true
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
const target = summarizeArchivedTarget(archived);
|
|
376
|
+
await context.sendMessage(target.transportResourceId, {
|
|
377
|
+
text: "Session archived. Use `/delete confirm:delete` to remove the local workspace.",
|
|
378
|
+
blocks: [
|
|
379
|
+
{
|
|
380
|
+
kind: "fields",
|
|
381
|
+
title: "Session Archived",
|
|
382
|
+
tone: "warning",
|
|
383
|
+
fields: [
|
|
384
|
+
{ label: "Session", value: target.id },
|
|
385
|
+
{ label: "Workspace", value: workspaceDisplay(target.id) }
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
});
|
|
390
|
+
await context.sendStatusUpdate({
|
|
391
|
+
text: `Archived session ${target.id} from ${target.transportResourceId}.`,
|
|
392
|
+
blocks: [
|
|
393
|
+
{
|
|
394
|
+
kind: "fields",
|
|
395
|
+
title: "Session Archived",
|
|
396
|
+
tone: "warning",
|
|
397
|
+
fields: [
|
|
398
|
+
{ label: "Session", value: target.id },
|
|
399
|
+
{ label: "Resource", value: target.transportResourceId }
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
]
|
|
403
|
+
});
|
|
404
|
+
context.appendAuditEvent("session.archived", {
|
|
405
|
+
sessionId: target.id,
|
|
406
|
+
transportResourceId: target.transportResourceId,
|
|
407
|
+
pluginId: manifest_default.id
|
|
408
|
+
});
|
|
409
|
+
return await reply(event, {
|
|
410
|
+
content: `Archived session ${target.id}.`,
|
|
411
|
+
ephemeral: true
|
|
412
|
+
});
|
|
413
|
+
} catch (error) {
|
|
414
|
+
if (!isMissingPermissions(error)) throw error;
|
|
415
|
+
return await reply(event, {
|
|
416
|
+
content: "Archive failed: Moorline needs permission to update the current resource and archive area.",
|
|
417
|
+
ephemeral: true
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (event.actionId === "resource.delete") {
|
|
422
|
+
if (!event.transportResourceId) {
|
|
423
|
+
return await reply(event, { content: "This action requires a target resource.", ephemeral: true });
|
|
424
|
+
}
|
|
425
|
+
if (stringOption(event.input, "confirm") !== "delete") {
|
|
426
|
+
return await reply(event, {
|
|
427
|
+
content: "Deletion cancelled: pass confirm:delete to remove the archived resource.",
|
|
428
|
+
ephemeral: true
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
const session = context.getSessionByTransportResourceId(event.transportResourceId);
|
|
432
|
+
if (session && session.lifecycleStatus !== "archived") {
|
|
433
|
+
return await reply(event, {
|
|
434
|
+
content: `Session ${session.sessionId} must be archived before deletion.`,
|
|
435
|
+
ephemeral: true
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
if (!session) {
|
|
439
|
+
return await reply(event, {
|
|
440
|
+
content: "This resource is not a deletable Moorline session.",
|
|
441
|
+
ephemeral: true
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
await defer(event, { ephemeral: true });
|
|
445
|
+
try {
|
|
446
|
+
const deleted = await context.deleteArchivedTransportResourceTarget({ transportResourceId: event.transportResourceId });
|
|
447
|
+
if (!deleted) {
|
|
448
|
+
return await reply(event, {
|
|
449
|
+
content: "This archived resource could not be deleted.",
|
|
450
|
+
ephemeral: true
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
const target = summarizeArchivedTarget(deleted);
|
|
454
|
+
await context.sendStatusUpdate({
|
|
455
|
+
text: `Deleted archived session ${target.id} and removed its local workspace.`,
|
|
456
|
+
blocks: [
|
|
457
|
+
{
|
|
458
|
+
kind: "fields",
|
|
459
|
+
title: "Session Deleted",
|
|
460
|
+
tone: "danger",
|
|
461
|
+
fields: [
|
|
462
|
+
{ label: "Session", value: target.id },
|
|
463
|
+
{ label: "Workspace", value: workspaceDisplay(target.id) }
|
|
464
|
+
]
|
|
465
|
+
}
|
|
466
|
+
]
|
|
467
|
+
});
|
|
468
|
+
context.appendAuditEvent("session.deleted", {
|
|
469
|
+
sessionId: target.id,
|
|
470
|
+
transportResourceId: target.transportResourceId,
|
|
471
|
+
workspacePath: target.workspacePath,
|
|
472
|
+
pluginId: manifest_default.id
|
|
473
|
+
});
|
|
474
|
+
return await reply(event, {
|
|
475
|
+
content: `Deleted archived session ${target.id} and removed its local workspace (${workspaceDisplay(target.id)}).`,
|
|
476
|
+
ephemeral: true
|
|
477
|
+
});
|
|
478
|
+
} catch (error) {
|
|
479
|
+
if (!isMissingPermissions(error)) throw error;
|
|
480
|
+
return await reply(event, {
|
|
481
|
+
content: "Delete failed: Moorline needs permission to delete the archived resource.",
|
|
482
|
+
ephemeral: true
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return { handled: false };
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// packages/discord-runtime/modules/routing/index.mjs
|
|
491
|
+
import { readFile } from "node:fs/promises";
|
|
492
|
+
var prompts = {
|
|
493
|
+
coordination: readFile(new globalThis.URL("./coordination.md", import.meta.url), "utf8").then((value) => value.trim()),
|
|
494
|
+
session: readFile(new globalThis.URL("./session.md", import.meta.url), "utf8").then((value) => value.trim())
|
|
495
|
+
};
|
|
496
|
+
function providerCommandLine(context) {
|
|
497
|
+
const providerCommand = typeof context.config?.surfaces?.provider?.config?.command === "string" ? context.config.surfaces.provider.config.command.trim() : "";
|
|
498
|
+
return providerCommand ? `Provider command: ${providerCommand}.` : "Provider command: unknown.";
|
|
499
|
+
}
|
|
500
|
+
async function loadPromptSections(context, surface, dynamicSections = []) {
|
|
501
|
+
const sections = [
|
|
502
|
+
await prompts[surface],
|
|
503
|
+
`Transport surface: discord ${surface} resource.`,
|
|
504
|
+
`Provider package: ${context.config?.surfaces?.provider?.activePackageId ?? "unknown"}.`,
|
|
505
|
+
`Default model preference: ${context.config?.defaults?.model ?? "latest"}.`,
|
|
506
|
+
providerCommandLine(context)
|
|
507
|
+
];
|
|
508
|
+
for (const section of dynamicSections) {
|
|
509
|
+
const trimmed = section.trim();
|
|
510
|
+
if (trimmed) {
|
|
511
|
+
sections.push(trimmed);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return sections;
|
|
515
|
+
}
|
|
516
|
+
function mainCoordinationRuntimeMode(context) {
|
|
517
|
+
return context.config?.defaults?.runtimeMode === "approval-required" ? "approval-required" : "full-access";
|
|
518
|
+
}
|
|
519
|
+
async function routeCoordinationMessage(event, context) {
|
|
520
|
+
const runtimeMode = mainCoordinationRuntimeMode(context);
|
|
521
|
+
const reply2 = await context.runAgent({
|
|
522
|
+
surface: "coordination",
|
|
523
|
+
transportResourceId: event.transportResourceId,
|
|
524
|
+
actorId: event.actor.actorId,
|
|
525
|
+
actorLabel: event.actor.displayName ?? event.actor.actorId,
|
|
526
|
+
message: event.message.text,
|
|
527
|
+
attachments: event.message.attachments,
|
|
528
|
+
session: null,
|
|
529
|
+
cwd: context.getRuntimeRootPath(),
|
|
530
|
+
runtimeMode,
|
|
531
|
+
context: {
|
|
532
|
+
systemPromptSections: await loadPromptSections(context, "coordination", [
|
|
533
|
+
"This coordination surface may use the runtime root for machine-level work, but it is not a durable worker workspace."
|
|
534
|
+
])
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
await context.sendMessage(event.transportResourceId, reply2);
|
|
538
|
+
context.appendAuditEvent("coordination.replied", {
|
|
539
|
+
transportResourceId: event.transportResourceId,
|
|
540
|
+
mode: runtimeMode,
|
|
541
|
+
pluginId: manifest_default.id
|
|
542
|
+
});
|
|
543
|
+
return { handled: true };
|
|
544
|
+
}
|
|
545
|
+
async function routeSessionMessage(event, context, session) {
|
|
546
|
+
const reply2 = await context.runAgent({
|
|
547
|
+
surface: "session",
|
|
548
|
+
transportResourceId: event.transportResourceId,
|
|
549
|
+
actorId: event.actor.actorId,
|
|
550
|
+
actorLabel: event.actor.displayName ?? event.actor.actorId,
|
|
551
|
+
message: event.message.text,
|
|
552
|
+
attachments: event.message.attachments,
|
|
553
|
+
session,
|
|
554
|
+
cwd: session.workspacePath,
|
|
555
|
+
runtimeMode: session.runtimeMode,
|
|
556
|
+
context: {
|
|
557
|
+
systemPromptSections: await loadPromptSections(context, "session", [
|
|
558
|
+
`Session ID: ${session.sessionId}`,
|
|
559
|
+
`Workspace: ${session.workspacePath}`,
|
|
560
|
+
`Runtime mode: ${session.runtimeMode}`
|
|
561
|
+
])
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
await context.sendMessage(event.transportResourceId, reply2);
|
|
565
|
+
context.appendAuditEvent("session.replied", {
|
|
566
|
+
sessionId: session.sessionId,
|
|
567
|
+
transportResourceId: event.transportResourceId,
|
|
568
|
+
pluginId: manifest_default.id
|
|
569
|
+
});
|
|
570
|
+
return { handled: true };
|
|
571
|
+
}
|
|
572
|
+
var routing_default = {
|
|
573
|
+
id: manifest_default.id,
|
|
574
|
+
manifest: manifest_default,
|
|
575
|
+
async onTransportEvent(event, context) {
|
|
576
|
+
if (event.type !== "message.received") {
|
|
577
|
+
return { handled: false };
|
|
578
|
+
}
|
|
579
|
+
const surface = context.getSurfaceState();
|
|
580
|
+
if (event.transportResourceId === surface.coordinationResourceId) {
|
|
581
|
+
return await routeCoordinationMessage(event, context);
|
|
582
|
+
}
|
|
583
|
+
const session = context.getSessionByTransportResourceId(event.transportResourceId);
|
|
584
|
+
if (!session || session.lifecycleStatus === "archived") {
|
|
585
|
+
return { handled: false };
|
|
586
|
+
}
|
|
587
|
+
return await routeSessionMessage(event, context, session);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
// packages/discord-runtime/modules/session-commands/index.mjs
|
|
592
|
+
function summarizeSessions(sessions) {
|
|
593
|
+
if (sessions.length === 0) return "No sessions yet.";
|
|
594
|
+
return sessions.map(
|
|
595
|
+
(session) => `${`- ${session.sessionId} (${session.lifecycleStatus}, ${session.runtimeMode})`}${session.summary ? ` | ${session.summary}` : ""}`
|
|
596
|
+
).join("\n");
|
|
597
|
+
}
|
|
598
|
+
function buildSessionListEmbed(sessions) {
|
|
599
|
+
const openSessions = sessions.filter((session) => session.lifecycleStatus !== "archived");
|
|
600
|
+
const coolSessions = sessions.filter((session) => session.lifecycleStatus === "cool");
|
|
601
|
+
const archivedSessions = sessions.filter((session) => session.lifecycleStatus === "archived");
|
|
602
|
+
return {
|
|
603
|
+
title: "Moorline Sessions",
|
|
604
|
+
color: 3447003,
|
|
605
|
+
fields: [
|
|
606
|
+
{ name: "Open", value: String(openSessions.length), inline: true },
|
|
607
|
+
{ name: "Cool", value: String(coolSessions.length), inline: true },
|
|
608
|
+
{ name: "Archived", value: String(archivedSessions.length), inline: true },
|
|
609
|
+
{ name: "Session Summary", value: summarizeSessions(sessions).slice(0, 1024) || "No sessions yet." }
|
|
610
|
+
],
|
|
611
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
function sessionOwnerFromEvent(event) {
|
|
615
|
+
const actorId = typeof event?.actor?.actorId === "string" ? event.actor.actorId.trim() : "";
|
|
616
|
+
if (!actorId) {
|
|
617
|
+
return void 0;
|
|
618
|
+
}
|
|
619
|
+
const actorLabel = typeof event?.actor?.displayName === "string" ? event.actor.displayName.trim() : "";
|
|
620
|
+
return {
|
|
621
|
+
kind: "run",
|
|
622
|
+
id: actorId,
|
|
623
|
+
...actorLabel ? { label: actorLabel } : {}
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
var session_commands_default = {
|
|
627
|
+
id: manifest_default.id,
|
|
628
|
+
manifest: manifest_default,
|
|
629
|
+
actions() {
|
|
630
|
+
return [
|
|
631
|
+
discordAction(
|
|
632
|
+
"session.create",
|
|
633
|
+
"Create a new worker session",
|
|
634
|
+
"session",
|
|
635
|
+
"Manage Moorline worker sessions",
|
|
636
|
+
"create",
|
|
637
|
+
"Create a new worker session",
|
|
638
|
+
[
|
|
639
|
+
{ type: "string", name: "name", description: "Short session name", required: true },
|
|
640
|
+
{
|
|
641
|
+
type: "string",
|
|
642
|
+
name: "mode",
|
|
643
|
+
description: "Runtime mode",
|
|
644
|
+
choices: [
|
|
645
|
+
{ name: "full-access", value: "full-access" },
|
|
646
|
+
{ name: "approval-required", value: "approval-required" }
|
|
647
|
+
]
|
|
648
|
+
}
|
|
649
|
+
]
|
|
650
|
+
),
|
|
651
|
+
discordAction(
|
|
652
|
+
"session.archive",
|
|
653
|
+
"Archive a session",
|
|
654
|
+
"session",
|
|
655
|
+
"Manage Moorline worker sessions",
|
|
656
|
+
"archive",
|
|
657
|
+
"Archive a session resource",
|
|
658
|
+
[{ type: "string", name: "session_id", description: "Archive a specific session id" }],
|
|
659
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
660
|
+
),
|
|
661
|
+
discordAction(
|
|
662
|
+
"session.delete",
|
|
663
|
+
"Delete an archived session",
|
|
664
|
+
"session",
|
|
665
|
+
"Manage Moorline worker sessions",
|
|
666
|
+
"delete",
|
|
667
|
+
"Delete an archived session and its local workspace",
|
|
668
|
+
[
|
|
669
|
+
{
|
|
670
|
+
type: "string",
|
|
671
|
+
name: "confirm",
|
|
672
|
+
description: "Type delete to confirm destructive removal",
|
|
673
|
+
required: true,
|
|
674
|
+
choices: [{ name: "delete", value: "delete" }]
|
|
675
|
+
},
|
|
676
|
+
{ type: "string", name: "session_id", description: "Delete a specific archived session id" }
|
|
677
|
+
],
|
|
678
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
679
|
+
),
|
|
680
|
+
discordAction(
|
|
681
|
+
"session.list",
|
|
682
|
+
"List sessions",
|
|
683
|
+
"session",
|
|
684
|
+
"Manage Moorline worker sessions",
|
|
685
|
+
"list",
|
|
686
|
+
"List active and archived sessions"
|
|
687
|
+
)
|
|
688
|
+
];
|
|
689
|
+
},
|
|
690
|
+
async onAction(event, context) {
|
|
691
|
+
if (!event.actionId.startsWith("session.")) {
|
|
692
|
+
return { handled: false };
|
|
693
|
+
}
|
|
694
|
+
if (event.actionId === "session.create") {
|
|
695
|
+
const requestedName = stringOption(event.input, "name");
|
|
696
|
+
if (!requestedName) {
|
|
697
|
+
return await reply(event, { content: "name is required", ephemeral: true });
|
|
698
|
+
}
|
|
699
|
+
const requestedMode = stringOption(event.input, "mode");
|
|
700
|
+
const runtimeMode = requestedMode === "full-access" || requestedMode === "approval-required" ? requestedMode : context.config.defaults.runtimeMode;
|
|
701
|
+
const created = await context.createSession({
|
|
702
|
+
requestedName,
|
|
703
|
+
runtimeMode,
|
|
704
|
+
owner: sessionOwnerFromEvent(event)
|
|
705
|
+
});
|
|
706
|
+
const notificationErrors = [];
|
|
707
|
+
try {
|
|
708
|
+
await context.sendMessage(created.transportResourceId, {
|
|
709
|
+
text: `Session ready: ${created.session.sessionId}. Start by sharing your first task.`
|
|
710
|
+
});
|
|
711
|
+
} catch (error) {
|
|
712
|
+
notificationErrors.push(error instanceof Error ? error.message : String(error));
|
|
713
|
+
}
|
|
714
|
+
context.appendAuditEvent("session.created", {
|
|
715
|
+
sessionId: created.session.sessionId,
|
|
716
|
+
transportResourceId: created.transportResourceId,
|
|
717
|
+
runtimeMode,
|
|
718
|
+
pluginId: manifest_default.id
|
|
719
|
+
});
|
|
720
|
+
if (notificationErrors.length > 0) {
|
|
721
|
+
context.appendAuditEvent("session.created.notification_failed", {
|
|
722
|
+
sessionId: created.session.sessionId,
|
|
723
|
+
transportResourceId: created.transportResourceId,
|
|
724
|
+
runtimeMode,
|
|
725
|
+
errors: notificationErrors,
|
|
726
|
+
pluginId: manifest_default.id
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
return await reply(event, {
|
|
730
|
+
content: notificationErrors.length === 0 ? `Created session ${created.session.sessionId} in <#${created.transportResourceId}>.` : `Created session ${created.session.sessionId} in <#${created.transportResourceId}>. Warning: follow-up notifications failed; check runtime status.`,
|
|
731
|
+
ephemeral: true
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
if (event.actionId === "session.archive") {
|
|
735
|
+
if (!event.transportResourceId && !stringOption(event.input, "session_id")) {
|
|
736
|
+
return await reply(event, {
|
|
737
|
+
content: "Run this in a session resource or pass session_id.",
|
|
738
|
+
ephemeral: true
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
try {
|
|
742
|
+
const session = await context.archiveSession({
|
|
743
|
+
transportResourceId: event.transportResourceId ?? "",
|
|
744
|
+
...stringOption(event.input, "session_id") ? { sessionId: stringOption(event.input, "session_id") } : {}
|
|
745
|
+
});
|
|
746
|
+
if (!session) {
|
|
747
|
+
return await reply(event, { content: "No matching session found.", ephemeral: true });
|
|
748
|
+
}
|
|
749
|
+
context.appendAuditEvent("session.archived", {
|
|
750
|
+
sessionId: session.sessionId,
|
|
751
|
+
transportResourceId: session.transportResourceId,
|
|
752
|
+
pluginId: manifest_default.id
|
|
753
|
+
});
|
|
754
|
+
return await reply(event, {
|
|
755
|
+
content: `Archived session ${session.sessionId}.`,
|
|
756
|
+
ephemeral: true
|
|
757
|
+
});
|
|
758
|
+
} catch (error) {
|
|
759
|
+
if (!isMissingPermissions(error)) throw error;
|
|
760
|
+
return await reply(event, {
|
|
761
|
+
content: "Archive failed: Moorline needs permission to update the session resource and archive area.",
|
|
762
|
+
ephemeral: true
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (event.actionId === "session.delete") {
|
|
767
|
+
if (!event.transportResourceId && !stringOption(event.input, "session_id")) {
|
|
768
|
+
return await reply(event, {
|
|
769
|
+
content: "Run this in a session resource or pass session_id.",
|
|
770
|
+
ephemeral: true
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
const requestedSessionId = stringOption(event.input, "session_id");
|
|
774
|
+
const target = (requestedSessionId ? context.getSessionById(requestedSessionId) : null) ?? (event.transportResourceId ? context.getSessionByTransportResourceId(event.transportResourceId) : null);
|
|
775
|
+
if (!target) {
|
|
776
|
+
return await reply(event, { content: "No matching session found.", ephemeral: true });
|
|
777
|
+
}
|
|
778
|
+
if (target.lifecycleStatus !== "archived") {
|
|
779
|
+
return await reply(event, {
|
|
780
|
+
content: `Session ${target.sessionId} must be archived before deletion.`,
|
|
781
|
+
ephemeral: true
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
if (stringOption(event.input, "confirm") !== "delete") {
|
|
785
|
+
return await reply(event, {
|
|
786
|
+
content: "Deletion cancelled: pass confirm:delete to remove the archived session.",
|
|
787
|
+
ephemeral: true
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
if (event.transportResourceId && target.transportResourceId === event.transportResourceId) {
|
|
791
|
+
await defer(event, { ephemeral: true });
|
|
792
|
+
}
|
|
793
|
+
try {
|
|
794
|
+
const deleted = await context.deleteArchivedSession({
|
|
795
|
+
transportResourceId: event.transportResourceId ?? target.transportResourceId,
|
|
796
|
+
...requestedSessionId ? { sessionId: requestedSessionId } : {}
|
|
797
|
+
});
|
|
798
|
+
if (!deleted) {
|
|
799
|
+
return await reply(event, { content: "No matching session found.", ephemeral: true });
|
|
800
|
+
}
|
|
801
|
+
context.appendAuditEvent("session.deleted", {
|
|
802
|
+
sessionId: deleted.sessionId,
|
|
803
|
+
transportResourceId: deleted.transportResourceId,
|
|
804
|
+
workspacePath: deleted.workspacePath,
|
|
805
|
+
pluginId: manifest_default.id
|
|
806
|
+
});
|
|
807
|
+
return await reply(event, {
|
|
808
|
+
content: `Deleted archived session ${deleted.sessionId} and removed its local workspace (${workspaceDisplay(deleted.sessionId)}).`,
|
|
809
|
+
ephemeral: true
|
|
810
|
+
});
|
|
811
|
+
} catch (error) {
|
|
812
|
+
if (!isMissingPermissions(error)) throw error;
|
|
813
|
+
return await reply(event, {
|
|
814
|
+
content: "Delete failed: Moorline needs permission to delete the archived session resource.",
|
|
815
|
+
ephemeral: true
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (event.actionId === "session.list") {
|
|
820
|
+
return await reply(event, {
|
|
821
|
+
content: "Current Moorline sessions",
|
|
822
|
+
embeds: [buildSessionListEmbed(context.listSessions())],
|
|
823
|
+
ephemeral: true
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
return await reply(event, {
|
|
827
|
+
content: `Unsupported session action: ${event.actionId}`,
|
|
828
|
+
ephemeral: true
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
// packages/discord-runtime/modules/status/index.mjs
|
|
834
|
+
var MAX_EMBED_FIELD_VALUE = 1024;
|
|
835
|
+
function buildHealthEmbed(input) {
|
|
836
|
+
return {
|
|
837
|
+
title: "Moorline Health",
|
|
838
|
+
color: input.dbOk && input.environmentOk ? 3066993 : 15158332,
|
|
839
|
+
fields: [
|
|
840
|
+
{ name: "Uptime", value: `${input.uptimeSeconds}s`, inline: true },
|
|
841
|
+
{ name: "Database", value: input.dbDetail ?? (input.dbOk ? "OK" : "Error"), inline: true },
|
|
842
|
+
{ name: "Environment", value: input.environmentDetail ?? (input.environmentOk ? "OK" : "Error"), inline: true },
|
|
843
|
+
{ name: "Open Sessions", value: String(input.activeSessions), inline: true },
|
|
844
|
+
{ name: "Cool Sessions", value: String(input.coolSessions), inline: true },
|
|
845
|
+
{ name: "Archived Sessions", value: String(input.archivedSessions), inline: true }
|
|
846
|
+
],
|
|
847
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
function parseAnswers(raw) {
|
|
851
|
+
try {
|
|
852
|
+
const parsed = JSON.parse(raw);
|
|
853
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { answer: raw };
|
|
854
|
+
const result = {};
|
|
855
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
856
|
+
if (typeof value === "string") result[key] = value;
|
|
857
|
+
else if (Array.isArray(value)) {
|
|
858
|
+
const normalized = value.filter((entry) => typeof entry === "string");
|
|
859
|
+
if (normalized.length > 0) result[key] = normalized;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return Object.keys(result).length > 0 ? result : { answer: raw };
|
|
863
|
+
} catch {
|
|
864
|
+
return { answer: raw };
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
function toBlock(title, tone, fields) {
|
|
868
|
+
return {
|
|
869
|
+
kind: "fields",
|
|
870
|
+
title,
|
|
871
|
+
tone,
|
|
872
|
+
fields: fields.map((field) => ({
|
|
873
|
+
label: field.name,
|
|
874
|
+
value: field.value,
|
|
875
|
+
...field.inline !== void 0 ? { inline: field.inline } : {}
|
|
876
|
+
}))
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function truncateEmbedValue(value, maxLength = MAX_EMBED_FIELD_VALUE) {
|
|
880
|
+
const normalized = String(value ?? "").trim();
|
|
881
|
+
if (!normalized) return "None";
|
|
882
|
+
if (normalized.length <= maxLength) return normalized;
|
|
883
|
+
return `${normalized.slice(0, Math.max(0, maxLength - 14))}...(truncated)`;
|
|
884
|
+
}
|
|
885
|
+
function summarizeRuntimeIssue(event) {
|
|
886
|
+
if (event.type === "turn.failed") {
|
|
887
|
+
return "A turn failed. Check local runtime audit logs for detailed diagnostics.";
|
|
888
|
+
}
|
|
889
|
+
if (event.type === "provider.closed") {
|
|
890
|
+
return "A provider session closed unexpectedly. Check local runtime audit logs for details.";
|
|
891
|
+
}
|
|
892
|
+
return "A runtime error occurred. Check local runtime audit logs for details.";
|
|
893
|
+
}
|
|
894
|
+
var status_default = {
|
|
895
|
+
id: manifest_default.id,
|
|
896
|
+
manifest: manifest_default,
|
|
897
|
+
actions() {
|
|
898
|
+
return [
|
|
899
|
+
discordAction(
|
|
900
|
+
"runtime.status",
|
|
901
|
+
"Show runtime status",
|
|
902
|
+
"status",
|
|
903
|
+
"Show Moorline surface and runtime status",
|
|
904
|
+
void 0,
|
|
905
|
+
void 0,
|
|
906
|
+
void 0,
|
|
907
|
+
{ allowedWhileDraining: true }
|
|
908
|
+
),
|
|
909
|
+
discordAction(
|
|
910
|
+
"runtime.turn.stop",
|
|
911
|
+
"Stop the active turn",
|
|
912
|
+
"turn",
|
|
913
|
+
"Control the active provider turn in this session",
|
|
914
|
+
"stop",
|
|
915
|
+
"Interrupt the active turn for this session",
|
|
916
|
+
void 0,
|
|
917
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
918
|
+
),
|
|
919
|
+
discordAction(
|
|
920
|
+
"runtime.request.cancel",
|
|
921
|
+
"Cancel a pending runtime request",
|
|
922
|
+
"request",
|
|
923
|
+
"Respond to a pending runtime user-input request",
|
|
924
|
+
"cancel",
|
|
925
|
+
"Cancel a pending runtime request",
|
|
926
|
+
[{ type: "string", name: "request_id", description: "Pending request id", required: true }],
|
|
927
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
928
|
+
),
|
|
929
|
+
discordAction(
|
|
930
|
+
"runtime.request.answer",
|
|
931
|
+
"Answer a pending runtime request",
|
|
932
|
+
"request",
|
|
933
|
+
"Respond to a pending runtime user-input request",
|
|
934
|
+
"answer",
|
|
935
|
+
"Answer a pending runtime user-input request",
|
|
936
|
+
[
|
|
937
|
+
{ type: "string", name: "request_id", description: "Pending request id", required: true },
|
|
938
|
+
{
|
|
939
|
+
type: "string",
|
|
940
|
+
name: "answers",
|
|
941
|
+
description: "Plain text answer or JSON object keyed by question id",
|
|
942
|
+
required: true
|
|
943
|
+
}
|
|
944
|
+
],
|
|
945
|
+
{ allowedWhileDraining: true, bypassQueue: true }
|
|
946
|
+
)
|
|
947
|
+
];
|
|
948
|
+
},
|
|
949
|
+
async onDomainEvent(event, context) {
|
|
950
|
+
if (event.transportResourceId === null) return;
|
|
951
|
+
if (event.type === "turn.waiting_for_approval" || event.type === "turn.waiting_for_input") {
|
|
952
|
+
await context.sendStatusUpdate({
|
|
953
|
+
text: `Session ${event.sessionId ?? event.threadId} is waiting.`,
|
|
954
|
+
blocks: [
|
|
955
|
+
toBlock("Turn Waiting", "warning", [
|
|
956
|
+
{ name: "Session", value: event.sessionId ?? event.threadId },
|
|
957
|
+
{ name: "State", value: event.type.replace("turn.", "").replace(/_/g, " ") }
|
|
958
|
+
])
|
|
959
|
+
]
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
if (event.type === "turn.failed" || event.type === "runtime.error" || event.type === "provider.closed") {
|
|
963
|
+
const detail = summarizeRuntimeIssue(event);
|
|
964
|
+
await context.sendStatusUpdate({
|
|
965
|
+
text: `Session ${event.sessionId ?? event.threadId} hit a runtime problem.`,
|
|
966
|
+
blocks: [
|
|
967
|
+
toBlock("Runtime Issue", "danger", [
|
|
968
|
+
{ name: "Session", value: event.sessionId ?? event.threadId },
|
|
969
|
+
{ name: "Detail", value: detail }
|
|
970
|
+
])
|
|
971
|
+
]
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
async onAction(event, context) {
|
|
976
|
+
if (event.actionId === "runtime.turn.stop") {
|
|
977
|
+
if (!event.transportResourceId) {
|
|
978
|
+
return await reply(event, { content: "This action requires a target session resource.", ephemeral: true });
|
|
979
|
+
}
|
|
980
|
+
const session = context.getSessionByTransportResourceId(event.transportResourceId);
|
|
981
|
+
if (!session) {
|
|
982
|
+
return await reply(event, {
|
|
983
|
+
content: "This resource does not have an active Moorline session.",
|
|
984
|
+
ephemeral: true
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
await context.interruptTurn({ threadId: session.threadId });
|
|
988
|
+
return await reply(event, {
|
|
989
|
+
content: `Interrupt sent for ${session.sessionId}.`,
|
|
990
|
+
ephemeral: true
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
if (event.actionId === "runtime.request.cancel" || event.actionId === "runtime.request.answer") {
|
|
994
|
+
if (!event.transportResourceId) {
|
|
995
|
+
return await reply(event, { content: "This action requires a target resource.", ephemeral: true });
|
|
996
|
+
}
|
|
997
|
+
const requestId = stringOption(event.input, "request_id");
|
|
998
|
+
if (!requestId) {
|
|
999
|
+
return await reply(event, { content: "request_id is required.", ephemeral: true });
|
|
1000
|
+
}
|
|
1001
|
+
const request = context.listPendingRequests(event.transportResourceId).find((entry) => entry.requestId === requestId);
|
|
1002
|
+
if (!request) {
|
|
1003
|
+
return await reply(event, {
|
|
1004
|
+
content: `No pending request ${requestId} was found in this resource.`,
|
|
1005
|
+
ephemeral: true
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
if (event.actionId === "runtime.request.cancel") {
|
|
1009
|
+
try {
|
|
1010
|
+
await context.cancelRuntimeRequest({
|
|
1011
|
+
threadId: request.threadId,
|
|
1012
|
+
requestId,
|
|
1013
|
+
requestType: request.requestType,
|
|
1014
|
+
requesterActor: event.actor
|
|
1015
|
+
});
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
return await reply(event, {
|
|
1018
|
+
content: error instanceof Error ? error.message : String(error),
|
|
1019
|
+
ephemeral: true
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
return await reply(event, {
|
|
1023
|
+
content: `Cancelled request ${requestId}.`,
|
|
1024
|
+
ephemeral: true
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
if (request.requestType !== "tool_user_input") {
|
|
1028
|
+
return await reply(event, {
|
|
1029
|
+
content: `Request ${requestId} is approval-driven. Use the request actions instead.`,
|
|
1030
|
+
ephemeral: true
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
try {
|
|
1034
|
+
await context.respondToRuntimeUserInput({
|
|
1035
|
+
threadId: request.threadId,
|
|
1036
|
+
requestId,
|
|
1037
|
+
answers: parseAnswers(stringOption(event.input, "answers")),
|
|
1038
|
+
requesterActor: event.actor
|
|
1039
|
+
});
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
return await reply(event, {
|
|
1042
|
+
content: error instanceof Error ? error.message : String(error),
|
|
1043
|
+
ephemeral: true
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
return await reply(event, {
|
|
1047
|
+
content: `Answered request ${requestId}.`,
|
|
1048
|
+
ephemeral: true
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
if (event.actionId !== "runtime.status") {
|
|
1052
|
+
return { handled: false };
|
|
1053
|
+
}
|
|
1054
|
+
const surface = context.getSurfaceState();
|
|
1055
|
+
const runtimeStatus = context.getRuntimeStatus();
|
|
1056
|
+
const providerDiagnostics = context.getProviderDiagnostics();
|
|
1057
|
+
const overview = context.getRuntimeOverview();
|
|
1058
|
+
const receipts = overview.receipts;
|
|
1059
|
+
const activities = overview.sessions.flatMap((session) => session.recentActivities).slice(-5);
|
|
1060
|
+
const projectionStates = overview.projectionStates;
|
|
1061
|
+
const projectionFailures = projectionStates.filter((entry) => entry.failure !== null);
|
|
1062
|
+
const providerErrorCount = Number(providerDiagnostics.statusCounts.error ?? 0);
|
|
1063
|
+
const dbOk = projectionFailures.length === 0;
|
|
1064
|
+
const environmentOk = providerErrorCount === 0;
|
|
1065
|
+
const dbDetail = projectionFailures.length === 0 ? "OK" : `Error (${projectionFailures.length} projection failure${projectionFailures.length === 1 ? "" : "s"})`;
|
|
1066
|
+
const environmentDetail = providerErrorCount === 0 ? "OK" : `Error (${providerErrorCount} provider session${providerErrorCount === 1 ? "" : "s"})`;
|
|
1067
|
+
return await reply(event, {
|
|
1068
|
+
content: "Moorline runtime status",
|
|
1069
|
+
embeds: [
|
|
1070
|
+
buildHealthEmbed({
|
|
1071
|
+
uptimeSeconds: runtimeStatus.uptimeSeconds,
|
|
1072
|
+
dbOk,
|
|
1073
|
+
environmentOk,
|
|
1074
|
+
dbDetail,
|
|
1075
|
+
environmentDetail,
|
|
1076
|
+
activeSessions: runtimeStatus.openSessions,
|
|
1077
|
+
coolSessions: runtimeStatus.coolSessions,
|
|
1078
|
+
archivedSessions: runtimeStatus.archivedSessions
|
|
1079
|
+
}),
|
|
1080
|
+
{
|
|
1081
|
+
title: "Runtime Activity",
|
|
1082
|
+
color: 1752220,
|
|
1083
|
+
fields: [
|
|
1084
|
+
{ name: "Running Sessions", value: String(runtimeStatus.runningSessions), inline: true },
|
|
1085
|
+
{ name: "Waiting Sessions", value: String(runtimeStatus.waitingSessions), inline: true },
|
|
1086
|
+
{
|
|
1087
|
+
name: "Pending Receipts",
|
|
1088
|
+
value: truncateEmbedValue(
|
|
1089
|
+
receipts.length === 0 ? "None" : receipts.slice(-5).map((receipt) => `${receipt.threadId}: ${receipt.state}`).join("\n")
|
|
1090
|
+
)
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
name: "Recent Activity",
|
|
1094
|
+
value: truncateEmbedValue(
|
|
1095
|
+
activities.length === 0 ? "None" : activities.map((activity) => `${activity.title}${activity.detail ? ` - ${activity.detail}` : ""}`).join("\n")
|
|
1096
|
+
)
|
|
1097
|
+
}
|
|
1098
|
+
],
|
|
1099
|
+
timestamp: context.nowIso()
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
title: "Provider Diagnostics",
|
|
1103
|
+
color: 10181046,
|
|
1104
|
+
fields: [
|
|
1105
|
+
{ name: "Account", value: providerDiagnostics.accountLabel ?? "unknown", inline: true },
|
|
1106
|
+
{ name: "Default Model", value: context.getDefaultModel(), inline: true },
|
|
1107
|
+
{ name: "Connected Sessions", value: String(providerDiagnostics.connectedSessions), inline: true },
|
|
1108
|
+
{
|
|
1109
|
+
name: "Models",
|
|
1110
|
+
value: truncateEmbedValue(providerDiagnostics.availableModels.join(", ") || "Unknown"),
|
|
1111
|
+
inline: true
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
name: "Provider Statuses",
|
|
1115
|
+
value: truncateEmbedValue(
|
|
1116
|
+
Object.entries(providerDiagnostics.statusCounts).map(([status, count]) => `${status}: ${count}`).join("\n") || "None"
|
|
1117
|
+
)
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
name: "Capabilities",
|
|
1121
|
+
value: truncateEmbedValue(Object.keys(providerDiagnostics.capabilityMetadata).join(", ") || "None")
|
|
1122
|
+
}
|
|
1123
|
+
],
|
|
1124
|
+
timestamp: context.nowIso()
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
title: "Projection Health",
|
|
1128
|
+
color: projectionFailures.length === 0 ? 3066993 : 15158332,
|
|
1129
|
+
fields: [
|
|
1130
|
+
{
|
|
1131
|
+
name: "Pipelines",
|
|
1132
|
+
value: truncateEmbedValue(
|
|
1133
|
+
projectionStates.map((entry) => `${entry.projector}: ${entry.failure ? `failed (${entry.failure})` : "ok"}`).join("\n") || "None"
|
|
1134
|
+
)
|
|
1135
|
+
}
|
|
1136
|
+
],
|
|
1137
|
+
timestamp: context.nowIso()
|
|
1138
|
+
},
|
|
1139
|
+
{
|
|
1140
|
+
title: "Managed Surface",
|
|
1141
|
+
color: 3447003,
|
|
1142
|
+
fields: [
|
|
1143
|
+
{ name: "Coordination", value: `<#${surface.coordinationResourceId}>`, inline: true },
|
|
1144
|
+
{ name: "Status", value: `<#${surface.statusResourceId}>`, inline: true },
|
|
1145
|
+
{ name: "Sessions", value: surface.sessionsCategoryId, inline: true },
|
|
1146
|
+
{ name: "Archive", value: surface.archiveCategoryId, inline: true }
|
|
1147
|
+
],
|
|
1148
|
+
timestamp: context.nowIso()
|
|
1149
|
+
}
|
|
1150
|
+
],
|
|
1151
|
+
ephemeral: true
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
// packages/discord-runtime/index.mjs
|
|
1157
|
+
var modules = [
|
|
1158
|
+
admin_control_default,
|
|
1159
|
+
channel_lifecycle_default,
|
|
1160
|
+
routing_default,
|
|
1161
|
+
session_commands_default,
|
|
1162
|
+
status_default
|
|
1163
|
+
];
|
|
1164
|
+
async function firstHandled(hook, args) {
|
|
1165
|
+
for (const module of modules) {
|
|
1166
|
+
const handler = module[hook];
|
|
1167
|
+
if (typeof handler !== "function") {
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
const result = await handler(...args);
|
|
1171
|
+
if (result?.handled) {
|
|
1172
|
+
return result;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
return { handled: false };
|
|
1176
|
+
}
|
|
1177
|
+
var index_default = {
|
|
1178
|
+
id: manifest_default.id,
|
|
1179
|
+
manifest: manifest_default,
|
|
1180
|
+
actions() {
|
|
1181
|
+
return modules.flatMap((module) => typeof module.actions === "function" ? module.actions() : []);
|
|
1182
|
+
},
|
|
1183
|
+
async onAction(event, context) {
|
|
1184
|
+
return await firstHandled("onAction", [event, context]);
|
|
1185
|
+
},
|
|
1186
|
+
async onDomainEvent(event, context) {
|
|
1187
|
+
return await firstHandled("onDomainEvent", [event, context]);
|
|
1188
|
+
},
|
|
1189
|
+
async onTransportEvent(event, context) {
|
|
1190
|
+
return await firstHandled("onTransportEvent", [event, context]);
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
export {
|
|
1194
|
+
index_default as default
|
|
1195
|
+
};
|