@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.
@@ -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
+ };