@animalabs/agent-framework 0.31.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -14
- package/dist/src/admission/admit.d.ts +18 -0
- package/dist/src/admission/admit.d.ts.map +1 -1
- package/dist/src/admission/admit.js +16 -3
- package/dist/src/admission/admit.js.map +1 -1
- package/dist/src/agent.d.ts +185 -78
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +279 -102
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.d.ts.map +1 -1
- package/dist/src/api/mcp-server-core.js +50 -12
- package/dist/src/api/mcp-server-core.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +4 -20
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/code-execution/isolate/scripts.d.ts +12 -8
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +24 -0
- package/dist/src/code-execution/isolate/scripts.js.map +1 -1
- package/dist/src/config-checks.d.ts +7 -0
- package/dist/src/config-checks.d.ts.map +1 -1
- package/dist/src/config-checks.js +14 -0
- package/dist/src/config-checks.js.map +1 -1
- package/dist/src/context-manager-errors.d.ts +25 -0
- package/dist/src/context-manager-errors.d.ts.map +1 -0
- package/dist/src/context-manager-errors.js +31 -0
- package/dist/src/context-manager-errors.js.map +1 -0
- package/dist/src/context-options.d.ts +67 -0
- package/dist/src/context-options.d.ts.map +1 -0
- package/dist/src/context-options.js +85 -0
- package/dist/src/context-options.js.map +1 -0
- package/dist/src/deferred-import.d.ts +93 -0
- package/dist/src/deferred-import.d.ts.map +1 -0
- package/dist/src/deferred-import.js +177 -0
- package/dist/src/deferred-import.js.map +1 -0
- package/dist/src/dendrite/audience.d.ts +223 -0
- package/dist/src/dendrite/audience.d.ts.map +1 -0
- package/dist/src/dendrite/audience.js +202 -0
- package/dist/src/dendrite/audience.js.map +1 -0
- package/dist/src/dendrite/index.d.ts +5 -3
- package/dist/src/dendrite/index.d.ts.map +1 -1
- package/dist/src/dendrite/index.js +2 -1
- package/dist/src/dendrite/index.js.map +1 -1
- package/dist/src/dendrite/presets.d.ts +10 -5
- package/dist/src/dendrite/presets.d.ts.map +1 -1
- package/dist/src/dendrite/presets.js +17 -5
- package/dist/src/dendrite/presets.js.map +1 -1
- package/dist/src/dendrite/registry.d.ts +62 -7
- package/dist/src/dendrite/registry.d.ts.map +1 -1
- package/dist/src/dendrite/registry.js +179 -10
- package/dist/src/dendrite/registry.js.map +1 -1
- package/dist/src/dendrite/types.d.ts +91 -8
- package/dist/src/dendrite/types.d.ts.map +1 -1
- package/dist/src/foreign-rounds.d.ts +69 -0
- package/dist/src/foreign-rounds.d.ts.map +1 -0
- package/dist/src/foreign-rounds.js +152 -0
- package/dist/src/foreign-rounds.js.map +1 -0
- package/dist/src/framework.d.ts +1977 -820
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +7571 -3457
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +85 -15
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +421 -47
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/index.d.ts +1 -1
- package/dist/src/gate/index.d.ts.map +1 -1
- package/dist/src/gate/index.js.map +1 -1
- package/dist/src/gate/rule-scope.d.ts +30 -0
- package/dist/src/gate/rule-scope.d.ts.map +1 -0
- package/dist/src/gate/rule-scope.js +41 -0
- package/dist/src/gate/rule-scope.js.map +1 -0
- package/dist/src/gate/suppress-script-worker.d.ts +2 -0
- package/dist/src/gate/suppress-script-worker.d.ts.map +1 -0
- package/dist/src/gate/suppress-script-worker.js +103 -0
- package/dist/src/gate/suppress-script-worker.js.map +1 -0
- package/dist/src/gate/suppress-scripts.d.ts +105 -0
- package/dist/src/gate/suppress-scripts.d.ts.map +1 -0
- package/dist/src/gate/suppress-scripts.js +403 -0
- package/dist/src/gate/suppress-scripts.js.map +1 -0
- package/dist/src/gate/suppression-review.d.ts +143 -0
- package/dist/src/gate/suppression-review.d.ts.map +1 -0
- package/dist/src/gate/suppression-review.js +463 -0
- package/dist/src/gate/suppression-review.js.map +1 -0
- package/dist/src/gate/suppression-stats.d.ts +125 -0
- package/dist/src/gate/suppression-stats.d.ts.map +1 -0
- package/dist/src/gate/suppression-stats.js +214 -0
- package/dist/src/gate/suppression-stats.js.map +1 -0
- package/dist/src/gate/types.d.ts +135 -7
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/gate/wake-onboarding.d.ts +119 -0
- package/dist/src/gate/wake-onboarding.d.ts.map +1 -0
- package/dist/src/gate/wake-onboarding.js +277 -0
- package/dist/src/gate/wake-onboarding.js.map +1 -0
- package/dist/src/held-writes.d.ts +31 -23
- package/dist/src/held-writes.d.ts.map +1 -1
- package/dist/src/held-writes.js +44 -59
- package/dist/src/held-writes.js.map +1 -1
- package/dist/src/index.d.ts +5 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/jsonrpc-envelope.d.ts +69 -0
- package/dist/src/jsonrpc-envelope.d.ts.map +1 -0
- package/dist/src/jsonrpc-envelope.js +66 -0
- package/dist/src/jsonrpc-envelope.js.map +1 -0
- package/dist/src/mcpl/channel-registry.d.ts +131 -15
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +290 -46
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +2 -4
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/feature-set-manager.d.ts +2 -4
- package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
- package/dist/src/mcpl/feature-set-manager.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +2 -1
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
- package/dist/src/mcpl/inbound-source.js +2 -1
- package/dist/src/mcpl/inbound-source.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +6 -6
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +132 -22
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +105 -15
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +9 -7
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +13 -10
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +134 -56
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tool-classes.d.ts.map +1 -1
- package/dist/src/mcpl/tool-classes.js +3 -0
- package/dist/src/mcpl/tool-classes.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +8 -2
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +24 -1
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +68 -18
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/discord/index.d.ts +45 -8
- package/dist/src/modules/discord/index.d.ts.map +1 -1
- package/dist/src/modules/discord/index.js +237 -86
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +59 -0
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +432 -67
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +6 -5
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +15 -22
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.d.ts +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +1 -1
- package/dist/src/modules/workspace/index.d.ts +28 -7
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +76 -22
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/notice-tags.d.ts +10 -1
- package/dist/src/notice-tags.d.ts.map +1 -1
- package/dist/src/notice-tags.js +10 -1
- package/dist/src/notice-tags.js.map +1 -1
- package/dist/src/operator-log.d.ts +36 -3
- package/dist/src/operator-log.d.ts.map +1 -1
- package/dist/src/operator-log.js +2 -2
- package/dist/src/operator-log.js.map +1 -1
- package/dist/src/presentation.d.ts +746 -0
- package/dist/src/presentation.d.ts.map +1 -0
- package/dist/src/presentation.js +1435 -0
- package/dist/src/presentation.js.map +1 -0
- package/dist/src/prose-drafts.d.ts +15 -11
- package/dist/src/prose-drafts.d.ts.map +1 -1
- package/dist/src/prose-drafts.js +16 -12
- package/dist/src/prose-drafts.js.map +1 -1
- package/dist/src/read-marks.d.ts +192 -0
- package/dist/src/read-marks.d.ts.map +1 -0
- package/dist/src/read-marks.js +242 -0
- package/dist/src/read-marks.js.map +1 -0
- package/dist/src/recovery/offline-branch.d.ts +91 -6
- package/dist/src/recovery/offline-branch.d.ts.map +1 -1
- package/dist/src/recovery/offline-branch.js +273 -29
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/recovery/recover-cli.js +22 -3
- package/dist/src/recovery/recover-cli.js.map +1 -1
- package/dist/src/send-tools.d.ts +5 -9
- package/dist/src/send-tools.d.ts.map +1 -1
- package/dist/src/send-tools.js +40 -7
- package/dist/src/send-tools.js.map +1 -1
- package/dist/src/stored-injections.d.ts +2 -2
- package/dist/src/stored-injections.js +2 -2
- package/dist/src/stored-removal.d.ts +17 -4
- package/dist/src/stored-removal.d.ts.map +1 -1
- package/dist/src/stored-removal.js +29 -5
- package/dist/src/stored-removal.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +12 -1
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +42 -8
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +1159 -79
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +3709 -363
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts +114 -17
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/tune-out/tools.js +87 -27
- package/dist/src/tune-out/tools.js.map +1 -1
- package/dist/src/tune-out/window-anchor.d.ts +143 -0
- package/dist/src/tune-out/window-anchor.d.ts.map +1 -0
- package/dist/src/tune-out/window-anchor.js +202 -0
- package/dist/src/tune-out/window-anchor.js.map +1 -0
- package/dist/src/types/agent.d.ts +86 -31
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +2 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +43 -21
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +180 -69
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +510 -36
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/admission/admit.ts +32 -3
- package/src/agent.ts +323 -120
- package/src/api/mcp-server-core.ts +54 -17
- package/src/api/server.ts +4 -23
- package/src/code-execution/isolate/scripts.ts +28 -3
- package/src/config-checks.ts +14 -0
- package/src/context-manager-errors.ts +37 -0
- package/src/context-options.ts +96 -0
- package/src/deferred-import.ts +239 -0
- package/src/dendrite/audience.ts +315 -0
- package/src/dendrite/index.ts +14 -2
- package/src/dendrite/presets.ts +18 -6
- package/src/dendrite/registry.ts +209 -12
- package/src/dendrite/types.ts +92 -8
- package/src/foreign-rounds.ts +199 -0
- package/src/framework.ts +7814 -3585
- package/src/gate/event-gate.ts +428 -65
- package/src/gate/index.ts +3 -0
- package/src/gate/rule-scope.ts +45 -0
- package/src/gate/suppress-script-worker.ts +89 -0
- package/src/gate/suppress-scripts.ts +414 -0
- package/src/gate/suppression-review.ts +517 -0
- package/src/gate/suppression-stats.ts +314 -0
- package/src/gate/types.ts +141 -7
- package/src/gate/wake-onboarding.ts +309 -0
- package/src/held-writes.ts +49 -60
- package/src/index.ts +9 -2
- package/src/jsonrpc-envelope.ts +81 -0
- package/src/mcpl/channel-registry.ts +353 -85
- package/src/mcpl/checkpoint-manager.ts +3 -2
- package/src/mcpl/feature-set-manager.ts +2 -1
- package/src/mcpl/inbound-source.ts +2 -1
- package/src/mcpl/inference-router.ts +7 -4
- package/src/mcpl/push-coalescer.ts +193 -34
- package/src/mcpl/push-handler.ts +10 -5
- package/src/mcpl/server-connection.ts +141 -60
- package/src/mcpl/tool-classes.ts +3 -0
- package/src/mcpl/types.ts +8 -2
- package/src/module-registry.ts +73 -17
- package/src/modules/discord/index.ts +229 -81
- package/src/modules/history/index.ts +439 -70
- package/src/modules/history/semantic.ts +15 -21
- package/src/modules/workspace/disk-agreement.ts +1 -1
- package/src/modules/workspace/index.ts +85 -23
- package/src/notice-tags.ts +10 -1
- package/src/operator-log.ts +36 -3
- package/src/presentation.ts +1676 -0
- package/src/prose-drafts.ts +16 -12
- package/src/read-marks.ts +328 -0
- package/src/recovery/offline-branch.ts +346 -32
- package/src/recovery/recover-cli.ts +21 -3
- package/src/send-tools.ts +40 -7
- package/src/stored-injections.ts +2 -2
- package/src/stored-removal.ts +31 -7
- package/src/tool-result-guard.ts +41 -7
- package/src/tune-out/coordinator.ts +4089 -457
- package/src/tune-out/tools.ts +160 -43
- package/src/tune-out/window-anchor.ts +277 -0
- package/src/types/agent.ts +90 -31
- package/src/types/events.ts +2 -0
- package/src/types/framework.ts +43 -21
- package/src/types/module.ts +181 -68
- package/src/types/trace.ts +542 -40
- package/dist/src/reasoning-loss-notice.d.ts +0 -23
- package/dist/src/reasoning-loss-notice.d.ts.map +0 -1
- package/dist/src/reasoning-loss-notice.js +0 -52
- package/dist/src/reasoning-loss-notice.js.map +0 -1
- package/dist/src/reasoning-loss.d.ts +0 -24
- package/dist/src/reasoning-loss.d.ts.map +0 -1
- package/dist/src/reasoning-loss.js +0 -102
- package/dist/src/reasoning-loss.js.map +0 -1
- package/src/reasoning-loss-notice.ts +0 -64
- package/src/reasoning-loss.ts +0 -120
|
@@ -350,7 +350,8 @@ interface LiveCreate {
|
|
|
350
350
|
/** converting: the live path fetches its attachments; queued: its event
|
|
351
351
|
* waits in the queue; writing: onProcess answered it, and each Discord
|
|
352
352
|
* event after it looks for the message's row, and ends the entry once it
|
|
353
|
-
* shows
|
|
353
|
+
* shows, or a release that couldn't store its write ends it with no row
|
|
354
|
+
* (`message:lost`, endLostCreate). */
|
|
354
355
|
phase: 'converting' | 'queued' | 'writing';
|
|
355
356
|
/** A delete for the message came before its create was handled, so no row
|
|
356
357
|
* comes, and the entry waits for that delete to be handled. */
|
|
@@ -358,6 +359,11 @@ interface LiveCreate {
|
|
|
358
359
|
/** The latest edit's text and attachments, from while it converted. */
|
|
359
360
|
text?: string;
|
|
360
361
|
attachments?: DiscordAttachment[];
|
|
362
|
+
unapplied?: 'delete' | Array<{ text: string; attachments?: DiscordAttachment[] }>;
|
|
363
|
+
/** A release lost its write without saying whether it landed, and the
|
|
364
|
+
* lookup made then threw (endLostCreate): the next sweep that reads the
|
|
365
|
+
* store and finds no row ends the entry, as that lookup would have. */
|
|
366
|
+
lostUnread?: true;
|
|
361
367
|
}
|
|
362
368
|
|
|
363
369
|
/**
|
|
@@ -402,18 +408,24 @@ export class DiscordModule implements Module {
|
|
|
402
408
|
|
|
403
409
|
// The Discord messages whose live create is on its way to the store: from
|
|
404
410
|
// the moment the live path takes one up, through its conversion and the
|
|
405
|
-
// event queue, until a query shows the row onProcess answered with
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
//
|
|
411
|
+
// event queue, until a query shows the row onProcess answered with. The
|
|
412
|
+
// framework stores a module's write at once, whatever the agent's turn or
|
|
413
|
+
// the host's mode (#291); only a live operator surgery's hold, or a context
|
|
414
|
+
// manager refusing its work, keeps it in memory, which a restart loses,
|
|
415
|
+
// until the release or the branch move that re-initializes the manager
|
|
416
|
+
// stores it. So the next Discord event once it is stored
|
|
417
|
+
// finds its row, marked held while the agent's window doesn't hold it
|
|
418
|
+
// (asModuleRow), and ends its entry. A history sync stores none of these messages, so no message is
|
|
410
419
|
// stored twice. A delete before the create is handled drops it: nothing is
|
|
411
420
|
// stored and nobody is woken. Any message's entry ends when its delete is
|
|
412
421
|
// handled, since a sync running then skips the message (noteLive) and a
|
|
413
|
-
// later one doesn't fetch it
|
|
414
|
-
//
|
|
415
|
-
//
|
|
416
|
-
//
|
|
422
|
+
// later one doesn't fetch it, but for an answered create with no row yet
|
|
423
|
+
// (`writing`), whose entry keeps its delete for the release that stores
|
|
424
|
+
// it (LiveCreate.unapplied). An edit while it converts is folded into it.
|
|
425
|
+
// A create whose answer the framework fails to write shows no row,
|
|
426
|
+
// and keeps its entry, and the sync off its message, until the module
|
|
427
|
+
// stops, but for one a surgery's hold kept, whose release, losing it,
|
|
428
|
+
// ends its entry (`message:lost`, endLostCreate).
|
|
417
429
|
private readonly creating = new Map<string, LiveCreate>();
|
|
418
430
|
|
|
419
431
|
// Typing keepalive: agentName -> { interval, deadline }. Started on
|
|
@@ -449,17 +461,28 @@ export class DiscordModule implements Module {
|
|
|
449
461
|
// inference and its turn ending, so it stays alive for the whole turn
|
|
450
462
|
// (including tool-call cycles) and stops cleanly whether the agent
|
|
451
463
|
// responds, errors, or decides to stay silent.
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
464
|
+
this.unsubscribeTrace = ctx.onTrace((event: TraceEvent) => {
|
|
465
|
+
// The edits and deletes kept for a held create are applied, or its
|
|
466
|
+
// record ended, on the trace of the surgery's release that stores or
|
|
467
|
+
// loses it (applyUnapplied, endLostCreate): the exception the README
|
|
468
|
+
// names to traces being observability-only.
|
|
469
|
+
if (event.type === 'message:added' && event.source === 'surgery-release') {
|
|
470
|
+
this.applyUnapplied();
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (event.type === 'message:lost' && event.external?.source === 'discord') {
|
|
474
|
+
this.endLostCreate(event.external.id, event.mayBeStored === true);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (!this.config.autoTyping) return;
|
|
478
|
+
const agentName = (event as { agentName?: string }).agentName;
|
|
479
|
+
if (!agentName) return;
|
|
480
|
+
if (event.type === 'inference:started' && !event.silent) {
|
|
481
|
+
this.startTypingLoop(agentName);
|
|
482
|
+
} else if (TURN_TERMINAL_TRACES.has(event.type)) {
|
|
483
|
+
this.stopTypingLoop(agentName);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
463
486
|
|
|
464
487
|
// Set up Discord event handlers
|
|
465
488
|
this.client.onMessage((message) => this.handleDiscordMessage(message));
|
|
@@ -772,10 +795,13 @@ export class DiscordModule implements Module {
|
|
|
772
795
|
// A stopped module answers nothing (stop()).
|
|
773
796
|
if (!this.ctx) return {};
|
|
774
797
|
|
|
775
|
-
// Each Discord event ends the entries whose row a query shows now,
|
|
776
|
-
//
|
|
798
|
+
// Each Discord event ends the entries whose row a query shows now,
|
|
799
|
+
// having applied what was kept for them first (applyUnapplied): an
|
|
800
|
+
// entry kept where its lost create's read threw (endLostCreate) still
|
|
801
|
+
// holds the edits or the delete made while its create was held.
|
|
777
802
|
if (event.type.startsWith('discord:')) {
|
|
778
|
-
|
|
803
|
+
this.applyUnapplied();
|
|
804
|
+
this.endShownCreates();
|
|
779
805
|
}
|
|
780
806
|
|
|
781
807
|
// Handle new Discord message
|
|
@@ -834,7 +860,7 @@ export class DiscordModule implements Module {
|
|
|
834
860
|
const contentBlocks = msg.contentBlocks ?? textFirst(storedOwnText(msg.content));
|
|
835
861
|
this.noteLive(msg.discordMessageId);
|
|
836
862
|
|
|
837
|
-
// A message a history sync wrote first
|
|
863
|
+
// A message a history sync wrote first keeps that row:
|
|
838
864
|
// the create adds none, and still sets the reply context and asks for
|
|
839
865
|
// inference, since the message arrived live. Either way the entry waits
|
|
840
866
|
// until a Discord event finds the message's row.
|
|
@@ -864,53 +890,10 @@ export class DiscordModule implements Module {
|
|
|
864
890
|
}
|
|
865
891
|
|
|
866
892
|
// Handle Discord message edit
|
|
867
|
-
if (event.type === 'discord:edit')
|
|
868
|
-
const edit = event as DiscordEditEvent;
|
|
869
|
-
// The message's row, not only its id: an edit changes the message's own
|
|
870
|
-
// text and keeps what the host added beside it, but for the blocks of
|
|
871
|
-
// attachments the edit took away, when the client passed the edit's
|
|
872
|
-
// attachments and the row is text-first.
|
|
873
|
-
// An edit that comes while the live path converts the message's create
|
|
874
|
-
// is folded into that create (handleDiscordMessageEdit). Any other edit
|
|
875
|
-
// to a message with no row, stored or held, is dropped: a history sync
|
|
876
|
-
// running now stores the text it fetched, and the next sync's text rule
|
|
877
|
-
// changes it. A row stored in shards can't be edited one shard at a
|
|
878
|
-
// time, so it keeps its text, and the host's log says so. Only an edit
|
|
879
|
-
// that writes marks the message live, so an update that changes nothing
|
|
880
|
-
// leaves a running sync free to rewrite the row.
|
|
881
|
-
const stored = this.storedRow(edit.discordMessageId);
|
|
882
|
-
if (!stored) return {};
|
|
883
|
-
if (stored.bodyGroupId) {
|
|
884
|
-
console.warn(`Discord: An update to message ${edit.discordMessageId} wasn't applied: it's stored in shards, which can't be edited, so if its text changed, the agent still has the earlier text`);
|
|
885
|
-
return {};
|
|
886
|
-
}
|
|
887
|
-
const texted = contentForText(stored, edit.newContent);
|
|
888
|
-
const content = edit.attachments && stored.metadata?.[TEXT_FIRST] === true
|
|
889
|
-
? withoutGoneAttachments(texted ?? stored.content, edit.attachments)
|
|
890
|
-
: texted;
|
|
891
|
-
if (!content || content === stored.content) return {};
|
|
892
|
-
this.noteLive(edit.discordMessageId);
|
|
893
|
-
return { editMessages: [{ messageId: stored.id, content }] };
|
|
894
|
-
}
|
|
893
|
+
if (event.type === 'discord:edit') return this.answerEdit(event as DiscordEditEvent);
|
|
895
894
|
|
|
896
895
|
// Handle Discord message delete
|
|
897
|
-
if (event.type === 'discord:delete')
|
|
898
|
-
const del = event as DiscordDeleteEvent;
|
|
899
|
-
// Every row of the message goes, so one stored twice doesn't outlive
|
|
900
|
-
// its delete as a copy. A row stored in shards is removed whole: the
|
|
901
|
-
// framework takes out the body a shard belongs to. The message is the
|
|
902
|
-
// live path's even when no row is found, so a running sync doesn't
|
|
903
|
-
// store what its fetch may still hold, and that ends its entry.
|
|
904
|
-
this.noteLive(del.discordMessageId);
|
|
905
|
-
this.creating.delete(del.discordMessageId);
|
|
906
|
-
const rows = this.storedRows(del.discordMessageId);
|
|
907
|
-
if (rows.length === 0) {
|
|
908
|
-
return {};
|
|
909
|
-
}
|
|
910
|
-
return {
|
|
911
|
-
removeMessages: oncePerBody(rows),
|
|
912
|
-
};
|
|
913
|
-
}
|
|
896
|
+
if (event.type === 'discord:delete') return this.answerDelete(event as DiscordDeleteEvent);
|
|
914
897
|
|
|
915
898
|
return {};
|
|
916
899
|
}
|
|
@@ -1077,8 +1060,8 @@ export class DiscordModule implements Module {
|
|
|
1077
1060
|
|
|
1078
1061
|
/**
|
|
1079
1062
|
* Sync history for a specific channel - compare with Context Manager and update.
|
|
1080
|
-
* Uses queryMessages() to enumerate the channel's rows,
|
|
1081
|
-
*
|
|
1063
|
+
* Uses queryMessages() to enumerate the channel's stored rows, for proper
|
|
1064
|
+
* edit/delete detection.
|
|
1082
1065
|
*
|
|
1083
1066
|
* The live path can store, edit or remove a row whenever the sync awaits.
|
|
1084
1067
|
* So the sync fetches history and runs the live conversion it needs first,
|
|
@@ -1232,8 +1215,8 @@ export class DiscordModule implements Module {
|
|
|
1232
1215
|
* edit replaces a row's own text and keeps what the host added beside it.
|
|
1233
1216
|
* Each id's rows are in store order. A message stored twice holds two,
|
|
1234
1217
|
* of which a sync acts on the first, as the live path does (storedRow),
|
|
1235
|
-
* and a delete takes both. A
|
|
1236
|
-
* (storedRows). */
|
|
1218
|
+
* and a delete takes both. A row the agent hasn't been shown yet (`held`)
|
|
1219
|
+
* is a stored row as any other (storedRows). */
|
|
1237
1220
|
private storedByDiscordId(ctx: ModuleContext, channelId: string): Map<string, StoredMessage[]> {
|
|
1238
1221
|
const rows = new Map<string, StoredMessage[]>();
|
|
1239
1222
|
for (const msg of ctx.queryMessages({ source: 'discord', metadata: { channelId } }).messages) {
|
|
@@ -1247,8 +1230,12 @@ export class DiscordModule implements Module {
|
|
|
1247
1230
|
}
|
|
1248
1231
|
|
|
1249
1232
|
/** The stored rows of a Discord message, by its id in the row's metadata,
|
|
1250
|
-
* in store order.
|
|
1251
|
-
*
|
|
1233
|
+
* in store order. Every write is stored (#291), but for one a surgery's
|
|
1234
|
+
* hold or a context manager refusing its work keeps in memory, which a
|
|
1235
|
+
* restart loses: a row the agent's window doesn't hold yet reads `held`
|
|
1236
|
+
* (one a tune-out holds reads `heldFromResidents`, whatever its window),
|
|
1237
|
+
* in its place, and an edit or a removal
|
|
1238
|
+
* reaches it as any other, and every window that holds it. The
|
|
1252
1239
|
* module context's findMessageByExternalId holds only rows the module
|
|
1253
1240
|
* stored through its context, not those a process response added, so it
|
|
1254
1241
|
* misses what the live path stored. */
|
|
@@ -1256,8 +1243,8 @@ export class DiscordModule implements Module {
|
|
|
1256
1243
|
return this.ctx?.queryMessages({ source: 'discord', externalIds: [discordMessageId] }).messages ?? [];
|
|
1257
1244
|
}
|
|
1258
1245
|
|
|
1259
|
-
/** The row an edit or a create acts on: a message's first row
|
|
1260
|
-
*
|
|
1246
|
+
/** The row an edit or a create acts on: a message's first row in store
|
|
1247
|
+
* order (storedRows). */
|
|
1261
1248
|
private storedRow(discordMessageId: string): StoredMessage | undefined {
|
|
1262
1249
|
return this.storedRows(discordMessageId)[0];
|
|
1263
1250
|
}
|
|
@@ -1482,13 +1469,174 @@ export class DiscordModule implements Module {
|
|
|
1482
1469
|
});
|
|
1483
1470
|
}
|
|
1484
1471
|
|
|
1472
|
+
private answerEdit(edit: DiscordEditEvent): EventResponse {
|
|
1473
|
+
// The message's row, not only its id: an edit changes the message's own
|
|
1474
|
+
// text and keeps what the host added beside it, but for the blocks of
|
|
1475
|
+
// attachments the edit took away, when the client passed the edit's
|
|
1476
|
+
// attachments and the row is text-first.
|
|
1477
|
+
// An edit that comes while the live path converts the message's create
|
|
1478
|
+
// is folded into that create (handleDiscordMessageEdit). One that comes
|
|
1479
|
+
// once onProcess answered the create and finds no row is kept on its
|
|
1480
|
+
// entry (LiveCreate.unapplied), unless a delete is kept there already,
|
|
1481
|
+
// and so is one that comes while edits kept there wait for a sweep that
|
|
1482
|
+
// finds the row (sweptRow threw, or the row came after the sweep): it
|
|
1483
|
+
// goes behind them, and applyUnapplied applies them in order, so an
|
|
1484
|
+
// older kept edit never lands over it. Any other edit
|
|
1485
|
+
// to a message with no row is dropped: a history sync
|
|
1486
|
+
// running now stores the text it fetched, and the next sync's text rule
|
|
1487
|
+
// changes it. A row stored in shards can't be edited one shard at a
|
|
1488
|
+
// time, so it keeps its text, and the host's log says so. Only an edit
|
|
1489
|
+
// that writes marks the message live, so an update that changes nothing
|
|
1490
|
+
// leaves a running sync free to rewrite the row.
|
|
1491
|
+
const kept = this.creating.get(edit.discordMessageId);
|
|
1492
|
+
const keep = () => {
|
|
1493
|
+
if (kept?.phase === 'writing' && kept.unapplied !== 'delete') {
|
|
1494
|
+
kept.unapplied = [...(kept.unapplied ?? []), { text: edit.newContent, ...(edit.attachments ? { attachments: edit.attachments } : {}) }];
|
|
1495
|
+
}
|
|
1496
|
+
return {};
|
|
1497
|
+
};
|
|
1498
|
+
if (kept?.phase === 'writing' && Array.isArray(kept.unapplied)) return keep();
|
|
1499
|
+
const stored = this.storedRow(edit.discordMessageId);
|
|
1500
|
+
if (!stored) return keep();
|
|
1501
|
+
if (stored.bodyGroupId) {
|
|
1502
|
+
console.warn(`Discord: An update to message ${edit.discordMessageId} wasn't applied: it's stored in shards, which can't be edited, so if its text changed, the agent still has the earlier text`);
|
|
1503
|
+
return {};
|
|
1504
|
+
}
|
|
1505
|
+
const texted = contentForText(stored, edit.newContent);
|
|
1506
|
+
const content = edit.attachments && stored.metadata?.[TEXT_FIRST] === true
|
|
1507
|
+
? withoutGoneAttachments(texted ?? stored.content, edit.attachments)
|
|
1508
|
+
: texted;
|
|
1509
|
+
if (!content || content === stored.content) return {};
|
|
1510
|
+
this.noteLive(edit.discordMessageId);
|
|
1511
|
+
return { editMessages: [{ messageId: stored.id, content }] };
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
private answerDelete(del: DiscordDeleteEvent): EventResponse {
|
|
1515
|
+
// Every row of the message goes, so one stored twice doesn't outlive
|
|
1516
|
+
// its delete as a copy. A row stored in shards is removed whole: the
|
|
1517
|
+
// framework takes out the body a shard belongs to. The message is the
|
|
1518
|
+
// live path's even when no row is found, so a running sync doesn't
|
|
1519
|
+
// store what its fetch may still hold, and that ends its entry, but for
|
|
1520
|
+
// an answered create with no row, whose entry keeps the delete its
|
|
1521
|
+
// handler recorded (LiveCreate.unapplied) for the release.
|
|
1522
|
+
this.noteLive(del.discordMessageId);
|
|
1523
|
+
const rows = this.storedRows(del.discordMessageId);
|
|
1524
|
+
if (rows.length === 0 && this.creating.get(del.discordMessageId)?.phase === 'writing') return {};
|
|
1525
|
+
this.creating.delete(del.discordMessageId);
|
|
1526
|
+
if (rows.length === 0) {
|
|
1527
|
+
return {};
|
|
1528
|
+
}
|
|
1529
|
+
return {
|
|
1530
|
+
removeMessages: oncePerBody(rows),
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/**
|
|
1535
|
+
* A write the release of a surgery's hold couldn't store (`message:lost`):
|
|
1536
|
+
* an answered create of the message (`writing`) with no row then ends,
|
|
1537
|
+
* with the edits or delete kept for it (LiveCreate.unapplied), since no
|
|
1538
|
+
* row comes for them to reach. Its delete, a history sync and a live
|
|
1539
|
+
* create of the message are no longer passed over. A definite loss
|
|
1540
|
+
* stored none of it, so it ends with no read. One whose outcome is
|
|
1541
|
+
* unknown (`mayBeStored`) is looked for: where its row landed, no
|
|
1542
|
+
* `message:added` came for it, so what was kept for it is applied now,
|
|
1543
|
+
* and the entry waits, as any answered create, for the next Discord event
|
|
1544
|
+
* to find the row. Where that lookup throws, the entry is flagged
|
|
1545
|
+
* (`lostUnread`), and the next event's sweep that reads the store ends it
|
|
1546
|
+
* where it finds no row.
|
|
1547
|
+
*/
|
|
1548
|
+
private endLostCreate(discordMessageId: string, mayBeStored: boolean): void {
|
|
1549
|
+
if (this.creating.get(discordMessageId)?.phase !== 'writing') return;
|
|
1550
|
+
if (mayBeStored) {
|
|
1551
|
+
let landed: boolean;
|
|
1552
|
+
try {
|
|
1553
|
+
landed = this.storedRow(discordMessageId) !== undefined;
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
console.error(`Discord: whether message ${discordMessageId}'s lost create left a row couldn't be read, so its record waits for the next event to find its row:`, error);
|
|
1556
|
+
const live = this.creating.get(discordMessageId);
|
|
1557
|
+
if (live) live.lostUnread = true;
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
if (landed) {
|
|
1561
|
+
this.applyUnapplied();
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
this.creating.delete(discordMessageId);
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* The row of an answered create, looked for at an event's sweep, or
|
|
1570
|
+
* undefined where the lookup throws, which is logged: that entry waits
|
|
1571
|
+
* for the next event, and the others are swept all the same.
|
|
1572
|
+
*/
|
|
1573
|
+
private sweptRow(discordMessageId: string): { row: StoredMessage | undefined } | undefined {
|
|
1574
|
+
try {
|
|
1575
|
+
return { row: this.storedRow(discordMessageId) };
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
console.error(`Discord: whether message ${discordMessageId}'s create left a row couldn't be read, so its record waits for the next event:`, error);
|
|
1578
|
+
return undefined;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
/**
|
|
1583
|
+
* Ends each answered create (`writing`) whose row a query shows now
|
|
1584
|
+
* (sweptRow), but one still holding what was kept for it, which
|
|
1585
|
+
* applyUnapplied couldn't apply (its lookup threw): it waits for the
|
|
1586
|
+
* next event, so what was kept isn't dropped with it. A lost create
|
|
1587
|
+
* whose release's lookup threw (`lostUnread`) ends where the lookup now
|
|
1588
|
+
* reads no row: no row comes for it.
|
|
1589
|
+
*/
|
|
1590
|
+
private endShownCreates(): void {
|
|
1591
|
+
for (const [id, live] of this.creating) {
|
|
1592
|
+
if (live.phase !== 'writing') continue;
|
|
1593
|
+
const swept = this.sweptRow(id);
|
|
1594
|
+
if (swept === undefined) continue;
|
|
1595
|
+
if (swept.row ? live.unapplied === undefined : live.lostUnread) this.creating.delete(id);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
private applyUnapplied(): void {
|
|
1600
|
+
const ctx = this.ctx;
|
|
1601
|
+
if (!ctx) return;
|
|
1602
|
+
for (const [discordMessageId, live] of this.creating) {
|
|
1603
|
+
const unapplied = live.unapplied;
|
|
1604
|
+
if (unapplied === undefined || !this.sweptRow(discordMessageId)?.row) continue;
|
|
1605
|
+
live.unapplied = undefined;
|
|
1606
|
+
const apply = (what: string, step: () => void) => {
|
|
1607
|
+
try {
|
|
1608
|
+
step();
|
|
1609
|
+
} catch (error) {
|
|
1610
|
+
console.error(`Discord: ${what} of message ${discordMessageId}, made while its create was kept in memory, threw as the release applied it, and may not have been applied:`, error);
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
if (unapplied === 'delete') {
|
|
1614
|
+
apply('the delete', () => {
|
|
1615
|
+
for (const id of this.answerDelete({ type: 'discord:delete', discordMessageId }).removeMessages ?? []) ctx.removeMessage(id);
|
|
1616
|
+
});
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
for (const { text, attachments } of unapplied) {
|
|
1620
|
+
apply('an edit', () => {
|
|
1621
|
+
const answer = this.answerEdit({ type: 'discord:edit', discordMessageId, newContent: text, ...(attachments ? { attachments } : {}) });
|
|
1622
|
+
for (const { messageId, content } of answer.editMessages ?? []) ctx.editMessage(messageId, content);
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1485
1628
|
private handleDiscordMessageDelete(messageId: string): void {
|
|
1486
1629
|
if (!this.ctx) return;
|
|
1487
1630
|
|
|
1488
|
-
// A delete before the message's create is handled drops that create
|
|
1489
|
-
//
|
|
1631
|
+
// A delete before the message's create is handled drops that create,
|
|
1632
|
+
// and one after it was answered is kept on its entry for the release
|
|
1633
|
+
// that stores it (LiveCreate.unapplied). Its event still goes, for a row
|
|
1634
|
+
// a history sync stored first.
|
|
1490
1635
|
const live = this.creating.get(messageId);
|
|
1491
|
-
if (live)
|
|
1636
|
+
if (live) {
|
|
1637
|
+
live.deleted = true;
|
|
1638
|
+
if (live.phase === 'writing') live.unapplied = 'delete';
|
|
1639
|
+
}
|
|
1492
1640
|
|
|
1493
1641
|
// Only queue the event - state writes happen in onProcess()
|
|
1494
1642
|
this.ctx.pushEvent({
|