@animalabs/agent-framework 0.32.0 → 0.34.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 +340 -22
- 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 +208 -80
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +345 -113
- 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/append-all.d.ts +5 -0
- package/dist/src/append-all.d.ts.map +1 -0
- package/dist/src/append-all.js +8 -0
- package/dist/src/append-all.js.map +1 -0
- 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 +70 -0
- package/dist/src/context-options.d.ts.map +1 -0
- package/dist/src/context-options.js +90 -0
- package/dist/src/context-options.js.map +1 -0
- package/dist/src/cut-off-round.js +4 -4
- 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 +183 -12
- 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/failure-text.d.ts +36 -0
- package/dist/src/failure-text.d.ts.map +1 -0
- package/dist/src/failure-text.js +89 -0
- package/dist/src/failure-text.js.map +1 -0
- 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 +2064 -842
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +8586 -3739
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +125 -21
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +487 -72
- 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/reply-judge.d.ts +4 -0
- package/dist/src/gate/reply-judge.d.ts.map +1 -1
- package/dist/src/gate/reply-judge.js +12 -3
- package/dist/src/gate/reply-judge.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 +142 -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/heartbeat/heartbeat.js +1 -1
- package/dist/src/heartbeat/heartbeat.js.map +1 -1
- 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 +6 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/inference-bounds.d.ts +2 -1
- package/dist/src/inference-bounds.d.ts.map +1 -1
- package/dist/src/inference-bounds.js +9 -6
- package/dist/src/inference-bounds.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 +188 -28
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +426 -70
- 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/modern-connection.d.ts +109 -15
- package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
- package/dist/src/mcpl/modern-connection.js +593 -86
- package/dist/src/mcpl/modern-connection.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts +3 -1
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +15 -9
- package/dist/src/mcpl/protocol-family.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 +50 -28
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +165 -72
- 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/tool-presence.d.ts.map +1 -1
- package/dist/src/mcpl/tool-presence.js +10 -6
- package/dist/src/mcpl/tool-presence.js.map +1 -1
- package/dist/src/mcpl/toolset-host.d.ts.map +1 -1
- package/dist/src/mcpl/toolset-host.js +7 -6
- package/dist/src/mcpl/toolset-host.js.map +1 -1
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -1
- package/dist/src/mcpl/toolsets-module.js +2 -1
- package/dist/src/mcpl/toolsets-module.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +33 -6
- 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 +238 -87
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/health/index.js +2 -2
- package/dist/src/modules/health/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 +18 -25
- 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 +80 -25
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +2 -1
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +3 -2
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/notice-tags.d.ts +20 -9
- package/dist/src/notice-tags.d.ts.map +1 -1
- package/dist/src/notice-tags.js +20 -9
- 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 +747 -0
- package/dist/src/presentation.d.ts.map +1 -0
- package/dist/src/presentation.js +1437 -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/reasoning-loss.d.ts +5 -0
- package/dist/src/reasoning-loss.d.ts.map +1 -0
- package/dist/src/reasoning-loss.js +8 -0
- package/dist/src/reasoning-loss.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 +277 -32
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/recovery/recover-cli.js +24 -4
- 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/sent-rows.js +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/supplied-text.d.ts +1 -1
- package/dist/src/supplied-text.js +3 -3
- package/dist/src/supplied-text.js.map +1 -1
- package/dist/src/thinking-drops.d.ts +2 -0
- package/dist/src/thinking-drops.d.ts.map +1 -1
- package/dist/src/thinking-drops.js.map +1 -1
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js +38 -9
- package/dist/src/tool-presentation.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 +1170 -81
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +3732 -369
- 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 +89 -28
- 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 +121 -25
- 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 +72 -17
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/inference-log.d.ts +1 -0
- package/dist/src/types/inference-log.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 +573 -39
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/admission/admit.ts +32 -3
- package/src/agent.ts +392 -132
- package/src/api/mcp-server-core.ts +54 -17
- package/src/api/server.ts +4 -23
- package/src/append-all.ts +6 -0
- 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 +106 -0
- package/src/cut-off-round.ts +4 -4
- 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 +213 -14
- package/src/dendrite/types.ts +92 -8
- package/src/failure-text.ts +88 -0
- package/src/foreign-rounds.ts +199 -0
- package/src/framework.ts +8796 -3813
- package/src/gate/event-gate.ts +518 -91
- package/src/gate/index.ts +3 -0
- package/src/gate/reply-judge.ts +12 -3
- 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 +148 -7
- package/src/gate/wake-onboarding.ts +309 -0
- package/src/heartbeat/heartbeat.ts +1 -1
- package/src/held-writes.ts +49 -60
- package/src/index.ts +10 -2
- package/src/inference-bounds.ts +11 -6
- package/src/jsonrpc-envelope.ts +81 -0
- package/src/mcpl/channel-registry.ts +513 -116
- 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/modern-connection.ts +629 -91
- package/src/mcpl/protocol-family.ts +15 -9
- package/src/mcpl/push-coalescer.ts +193 -34
- package/src/mcpl/push-handler.ts +10 -5
- package/src/mcpl/server-connection.ts +169 -75
- package/src/mcpl/tool-classes.ts +3 -0
- package/src/mcpl/tool-presence.ts +10 -5
- package/src/mcpl/toolset-host.ts +7 -6
- package/src/mcpl/toolsets-module.ts +2 -1
- package/src/mcpl/types.ts +33 -6
- package/src/module-registry.ts +73 -17
- package/src/modules/discord/index.ts +230 -82
- package/src/modules/health/index.ts +2 -2
- package/src/modules/history/index.ts +439 -70
- package/src/modules/history/semantic.ts +18 -24
- package/src/modules/workspace/disk-agreement.ts +1 -1
- package/src/modules/workspace/index.ts +89 -26
- package/src/modules/workspace/observe.ts +2 -1
- package/src/modules/workspace/reconcile.ts +3 -2
- package/src/notice-tags.ts +21 -9
- package/src/operator-log.ts +36 -3
- package/src/presentation.ts +1678 -0
- package/src/prose-drafts.ts +16 -12
- package/src/read-marks.ts +328 -0
- package/src/reasoning-loss.ts +7 -0
- package/src/recovery/offline-branch.ts +350 -35
- package/src/recovery/recover-cli.ts +23 -4
- package/src/send-tools.ts +40 -7
- package/src/sent-rows.ts +1 -1
- package/src/stored-injections.ts +2 -2
- package/src/stored-removal.ts +31 -7
- package/src/supplied-text.ts +3 -3
- package/src/thinking-drops.ts +2 -0
- package/src/tool-presentation.ts +34 -9
- package/src/tool-result-guard.ts +41 -7
- package/src/tune-out/coordinator.ts +4121 -472
- package/src/tune-out/tools.ts +162 -44
- package/src/tune-out/window-anchor.ts +277 -0
- package/src/types/agent.ts +122 -25
- package/src/types/events.ts +2 -0
- package/src/types/framework.ts +73 -17
- package/src/types/inference-log.ts +1 -0
- package/src/types/module.ts +181 -68
- package/src/types/trace.ts +607 -43
package/README.md
CHANGED
|
@@ -148,10 +148,29 @@ Each agent has a `ContextManager` (from `@connectome/context-manager`) that main
|
|
|
148
148
|
- **PassthroughStrategy** - No compression, raw message replay up to budget
|
|
149
149
|
- **AutobiographicalStrategy** - Chunks old messages and summarizes them into diary entries, preserving recent context uncompressed
|
|
150
150
|
|
|
151
|
-
A turn keeps the messages it opened with whole: at a start that opens a turn, the framework pins raw (`pinRange`, named `agent-framework:turn-inputs`) the rows the agent
|
|
151
|
+
A turn keeps the messages it opened with whole: at a start that opens a turn, the framework pins raw (`pinRange`, named `agent-framework:turn-inputs`) the rows of the agent's window (#291) that it sees after both the newest of its own messages there and its consumed watermark, in the window's order, starting at the first such row that holds more than tool results, and removes the pin when the turn ends. Those are the rows presented to it since the mark, a row stored before the mark and presented after it included, and a row presented to it again, also when the mark was taken before its window was written down; a row stored and not yet presented is in neither its window nor the pin. The pinned rows' tokens count against the budget. If a committing compile refuses as over budget with the pin held, the pin is removed and the compile runs once more, so the turn goes on as it would have without it. A silent heartbeat tick pins nothing, and neither does a strategy without pins, or an agent whose configuration sets `turnInputsPin: false`.
|
|
152
152
|
|
|
153
153
|
Before inference, modules and MCPL hooks can inject additional context via `gatherContext()`. An `afterUser` injection is stored in the agent's window, as the message `compile()` would have placed after the last user message, so it stays where the agent saw it: the replies after it keep their thinking, and the cache keeps the turns before it. A source's unchanged content isn't stored again while the copy the agent will read as current has it whole: the one its last request held, or a newer one a start stored whose compile then didn't record (it threw, or the start failed before it); once a fold has summarized it or the strategy's window has passed it, it's stored again, as one more row. Where a changed one lands right after the agent's own latest message, with no one else's message between, `[Continue]` still ends the request, after it. A stored row is history to the strategy, capped, sharded, budgeted and folded like any message. Its metadata carries `system: true`, `kind: 'context-injection'` and `contextInjection: { agent, namespace, contentHash, part, parts }`, which `storedInjectionMarkOf` reads, and every other agent's view leaves it out. A module's injection can set `placedEachCompile: true` to be placed by `compile()` at each start instead, near the end, at the cost of the thinking of every reply after its old place.
|
|
154
154
|
|
|
155
|
+
#### Legacy reasoning-loss notices
|
|
156
|
+
|
|
157
|
+
Rows marked with both `metadata.system === true` and
|
|
158
|
+
`metadata.kind === 'reasoning-loss'` are omitted from the agent's compiled
|
|
159
|
+
context and compression inputs only. Stored history is unchanged: history
|
|
160
|
+
`extract`, `search` and around-message lookup still return these notices
|
|
161
|
+
verbatim, and tool output or previously written summaries can reintroduce their
|
|
162
|
+
text. Selection never matches notice text.
|
|
163
|
+
|
|
164
|
+
Hosts opening the same stores should use the package's
|
|
165
|
+
`residentViewFilter(message, agentName)` or
|
|
166
|
+
`subconsciousViewFilter(message, agentName)` rather than copy the view rules.
|
|
167
|
+
On context-manager releases up to at least 0.25.1, a hidden first source can place that
|
|
168
|
+
summary after newer messages; a hidden first or last source makes it permanently
|
|
169
|
+
unmergeable and absent from history overview until anima-research/context-manager#369 or equivalent lands. The release that raises the context-manager dependency floor to a version
|
|
170
|
+
containing that fix should remove this migration limit and the migration test
|
|
171
|
+
`todo`. See the
|
|
172
|
+
[release note](changelog.d/973-reasoning-loss.fixed.md).
|
|
173
|
+
|
|
155
174
|
### Persistence
|
|
156
175
|
|
|
157
176
|
All state is persisted in a [Chronicle](https://github.com/antra-tess/chronicle) store:
|
|
@@ -180,10 +199,17 @@ agent-framework-recover \
|
|
|
180
199
|
```
|
|
181
200
|
|
|
182
201
|
The specified Discord message is the last message the agent will still see;
|
|
183
|
-
everything after it is left on the old branch
|
|
184
|
-
|
|
185
|
-
branch
|
|
186
|
-
|
|
202
|
+
everything after it is left on the old branch (on a window presented since
|
|
203
|
+
#291, but for the rows stored before the write that presented it, which the
|
|
204
|
+
new branch keeps and the window takes again after that message, and the rows
|
|
205
|
+
that one write presented together with it, which stay after it in the window).
|
|
206
|
+
The command
|
|
207
|
+
scans in bounded windows (on a presented window, a `--messages` count and the
|
|
208
|
+
walk of the rows after the anchor, which the cut returns or keeps, read the
|
|
209
|
+
window whole), never compiles
|
|
210
|
+
message content, and activates the new Chronicle branch. By default the
|
|
211
|
+
recovery is local to the resident: nothing is posted to Discord. To let the
|
|
212
|
+
people involved see that the agent no longer has their
|
|
187
213
|
messages, choose awareness marks explicitly with `--marks addressed` (removed
|
|
188
214
|
messages that mentioned or replied to the bot, and DMs) or `--marks all`
|
|
189
215
|
(every removed Discord message). The output counts what will be marked and
|
|
@@ -229,7 +255,9 @@ agent-framework-recover \
|
|
|
229
255
|
|
|
230
256
|
`--suppress` may be repeated or given a comma-separated list.
|
|
231
257
|
`--suppress-range <first>..<last>` suppresses the inclusive context interval
|
|
232
|
-
between two Discord messages, including intervening agent/tool entries
|
|
258
|
+
between two Discord messages, including intervening agent/tool entries: every
|
|
259
|
+
row stored between the two, any agent's, so on a slot residents share, rows
|
|
260
|
+
an agent's window never held too. These
|
|
233
261
|
removals exist only on the recovery branch; the source branch remains intact.
|
|
234
262
|
Suppressed Discord messages follow the same `--marks` choice.
|
|
235
263
|
Selections that split a sharded body group or a tool-use/tool-result exchange
|
|
@@ -298,18 +326,19 @@ A pattern that matches no tool is reported once, as a console line and an
|
|
|
298
326
|
A pattern that could name a server's tools is not judged until that server
|
|
299
327
|
has listed them, so late connects and reconnects don't cause early reports.
|
|
300
328
|
|
|
301
|
-
#### Modern MCP servers
|
|
329
|
+
#### Modern MCP servers
|
|
302
330
|
|
|
303
|
-
The same `mcplServers` list also takes servers that speak modern MCP
|
|
304
|
-
|
|
305
|
-
configuration chooses the family, and
|
|
331
|
+
The same `mcplServers` list also takes servers that speak modern MCP,
|
|
332
|
+
reached through the official SDK client: revision `2026-07-28`, and over
|
|
333
|
+
HTTP the 2025 revisions too. The configuration chooses the family, and a
|
|
334
|
+
stdio server speaks `2026-07-28` alone:
|
|
306
335
|
|
|
307
336
|
| Configuration | Family | Transport |
|
|
308
337
|
|---|---|---|
|
|
309
338
|
| `url: 'ws://…'` / `'wss://…'` | MCPL (MCP `2024-11-05`) | WebSocket (`token` / `accessProvider` as `?token=`, or as bearer with `tokenTransport: 'header'`) |
|
|
310
|
-
| `url: 'http://…'` / `'https://…'` | modern MCP | Streamable HTTP (`token` / `accessProvider` as bearer) |
|
|
339
|
+
| `url: 'http://…'` / `'https://…'` | modern MCP (`2026-07-28`, `2025-11-25`, `2025-06-18` or `2025-03-26`, negotiated) | Streamable HTTP (`token` / `accessProvider` as bearer) |
|
|
311
340
|
| `command` | MCPL | stdio |
|
|
312
|
-
| `command` + `protocol: 'modern'` | modern MCP | stdio |
|
|
341
|
+
| `command` + `protocol: 'modern'` | modern MCP (`2026-07-28`) | stdio |
|
|
313
342
|
|
|
314
343
|
```typescript
|
|
315
344
|
{
|
|
@@ -410,6 +439,114 @@ revisions the server says it supports ("none listed" for an empty list), or its
|
|
|
410
439
|
and when the list includes `2026-07-28` it says to configure the server with
|
|
411
440
|
`protocol: 'modern'` (stdio) or reach it over http(s).
|
|
412
441
|
|
|
442
|
+
Over HTTP the revision is negotiated at each connect. The client asks
|
|
443
|
+
`server/discover` first and connects at `2026-07-28` when the server offers
|
|
444
|
+
it. A server that doesn't gets the 2025 `initialize` handshake, which asks
|
|
445
|
+
for `2025-11-25` and accepts `2025-11-25`, `2025-06-18` or `2025-03-26` in
|
|
446
|
+
the answer.
|
|
447
|
+
`listMcplServers()` shows the revision it settles on as `protocolVersion`,
|
|
448
|
+
and its `family` stays `modern`. On a 2025 revision:
|
|
449
|
+
- List changes arrive on the session's standalone stream, which is kept
|
|
450
|
+
open while the session lives, whether or not the server issues a session
|
|
451
|
+
id, unless the server answers its first open with a status that won't
|
|
452
|
+
pass: a 405, which says it offers no stream, a 501 or 505, a 4xx other
|
|
453
|
+
than 408, 409 and 429, or a redirect the SDK doesn't follow, such as one
|
|
454
|
+
off the server's origin. With a `token` or `accessProvider` set, a 401
|
|
455
|
+
first gets one more try, after the SDK refreshes the credential.
|
|
456
|
+
Such an open isn't tried again, and the session's list changes don't
|
|
457
|
+
arrive. A failure to open it that may pass is reported as the
|
|
458
|
+
connection's `error` and tried again, and a stream that drops is
|
|
459
|
+
reopened: the first try waits the server's `retry` hint, or the first
|
|
460
|
+
step of the connection's backoff when it gave none or a hint of 0, and a
|
|
461
|
+
try after a failed one waits the longer of the hint and the connection's
|
|
462
|
+
backoff.
|
|
463
|
+
Its first open, once the connection is up, and a reopen that missed what
|
|
464
|
+
came meanwhile each relist the tools of a server that announces list
|
|
465
|
+
changes, since a change announced while no stream was open never
|
|
466
|
+
arrives.
|
|
467
|
+
- A request's stream that the server closes after a priming event is
|
|
468
|
+
resumed for as long as the request lives. Its cancellation at the
|
|
469
|
+
deadline stops the resume too. A stream cut again before it carried a
|
|
470
|
+
new event id can't be resumed: the SDK sends its resume without the id
|
|
471
|
+
(modelcontextprotocol/typescript-sdk#2499), which the server can't match
|
|
472
|
+
to the call's stream, so the call doesn't get its answer, and fails as
|
|
473
|
+
`no-response`, at the latest at its deadline.
|
|
474
|
+
A call whose stream ends unanswered with nothing to resume from fails
|
|
475
|
+
then, as `no-response`, not at its deadline.
|
|
476
|
+
- The server ends the session by answering 404 to a request that carried
|
|
477
|
+
its id (except the stream's first open, whose 404 can also mean the
|
|
478
|
+
server has no GET route). A call whose POST met the 404 fails as
|
|
479
|
+
`error-response` with `data.httpStatus` 404, and isn't replayed. A call
|
|
480
|
+
whose stream the server's restart cut is `no-response`, since the server
|
|
481
|
+
may have run it. A session that had answered a request gets a new one at
|
|
482
|
+
once, whatever `reconnect` says: the connection emits `close` while its
|
|
483
|
+
`willReconnect` reads true, then `reconnect`, and the framework keeps the
|
|
484
|
+
server and its tools and relists. If the new session can't start,
|
|
485
|
+
`reconnect` decides, as for any lost connection, and a revision verdict
|
|
486
|
+
stops it for good, as on any reconnect. Without `reconnect`, that failure
|
|
487
|
+
emits a second `close`, while `willReconnect` reads false: the server is
|
|
488
|
+
gone. An answered session that ended less than the base interval
|
|
489
|
+
(`reconnectIntervalMs`) after it started gets a new one at once only if
|
|
490
|
+
the last session restarted that way had lived the interval, or there was
|
|
491
|
+
none: a server restarted once soon after a connect costs a restart, not
|
|
492
|
+
the server. A second such young end in a row is a failed connect, as is
|
|
493
|
+
a session's end before it answered anything: it emits `reconnect-failed`
|
|
494
|
+
and `close`, and follows `reconnect` with a growing backoff. So a load
|
|
495
|
+
balancer without sticky sessions, or a rolling upgrade that lands a
|
|
496
|
+
connect on an old node and its requests on a new one, shows up as a
|
|
497
|
+
failing server, with the framework's alert at the fifth attempt, when its
|
|
498
|
+
sessions end within the interval. A deployment whose sessions each live
|
|
499
|
+
longer restarts at most once an interval, and each restart costs the
|
|
500
|
+
calls in flight: the client can't tell a balancer that moves a session's
|
|
501
|
+
requests from a server that restarts. Once the framework's `stop()` has
|
|
502
|
+
begun, an ended session isn't started again, and a new one already
|
|
503
|
+
connecting doesn't come up.
|
|
504
|
+
- A server that moves to `2026-07-28` in place is followed, since the
|
|
505
|
+
session's end brings a new connect, which negotiates afresh. Two more
|
|
506
|
+
answers end a 2025 session, with a session id or without: a 400 carrying
|
|
507
|
+
JSON-RPC `-32022` to any request, as the SDK's 2026 handler answers 2025
|
|
508
|
+
traffic it refuses (`legacy: 'reject'`), and a 405 to a GET for one of
|
|
509
|
+
the session's streams after the server had served it (the standalone
|
|
510
|
+
stream's reopen, or a request stream's resume with its event id), as that
|
|
511
|
+
handler answers a 2025 session it serves statelessly (its default). A call
|
|
512
|
+
refused with `-32022` fails as `error-response` with the server's data,
|
|
513
|
+
its code in the host's log, and isn't replayed. Its words say the server
|
|
514
|
+
refused the session's revision, so the request didn't run, and whether
|
|
515
|
+
this host is reconnecting. A server that keeps serving a 2025 revision
|
|
516
|
+
statelessly to a connection that never had a standalone stream isn't
|
|
517
|
+
followed, unless its upgrade cut a call's stream, whose resume with its
|
|
518
|
+
event id then meets the 405: that connection stays on 2025 until its next
|
|
519
|
+
connect.
|
|
520
|
+
- Each session the server holds by its id is ended with a `DELETE` when the
|
|
521
|
+
connection lets it go, waiting at most 3 s: at `close()`, and so `stop()`
|
|
522
|
+
or a disconnect, a connect in flight included, and when a connect fails
|
|
523
|
+
after its `initialize` was answered. A session the server let go, with a
|
|
524
|
+
404, a `-32022` or such a 405, gets none.
|
|
525
|
+
|
|
526
|
+
On `2026-07-28` too, a dropped stream's retries follow that timing. They
|
|
527
|
+
stop after two failed tries, the SDK's default, on every stream: a listen
|
|
528
|
+
stream that can't be resumed is reopened, with a relist, and a call whose
|
|
529
|
+
stream can't be resumed fails then, as `no-response`, rather than at its
|
|
530
|
+
deadline.
|
|
531
|
+
|
|
532
|
+
A server over HTTP that this client can't negotiate a revision with fails
|
|
533
|
+
with `McplProtocolVersionError`, naming what each side speaks, and is not
|
|
534
|
+
retried: the SDK's own refusal, after which it negotiates no further (a
|
|
535
|
+
`-32022` whose list names a revision from `2026-07-28` on that this client
|
|
536
|
+
doesn't speak, even if the list also names a 2025 revision it does, which
|
|
537
|
+
the message then says); a discovery or `-32022` error that lists no
|
|
538
|
+
revision in common, once the server has also refused the `initialize`
|
|
539
|
+
tried after it (`rejected`); or an `initialize` answered with a revision
|
|
540
|
+
the 2025 handshake doesn't accept, or with none (`mismatch`). `2026-07-28` is
|
|
541
|
+
negotiated through `server/discover` alone, so it isn't accepted from
|
|
542
|
+
`initialize`. An `initialize` that fails without an answer, such as a
|
|
543
|
+
timeout, is no verdict, and is retried as any failed connect is. The
|
|
544
|
+
error's `offeredRevisions` lists the revisions this client offered, and
|
|
545
|
+
`offered` is the first of them. `2024-11-05` isn't one of them over HTTP:
|
|
546
|
+
its HTTP binding was HTTP+SSE, another transport. Over stdio, a discovery
|
|
547
|
+
answer that lists revisions without `2026-07-28` is a verdict too, even one
|
|
548
|
+
the SDK can't read as a DiscoverResult.
|
|
549
|
+
|
|
413
550
|
Server and transport text the framework records has the server's configured credentials replaced, in both engines. That covers connection traces, console lines, stderr traces, ops alerts, and a tool call's failure as its caller, the model and the `tool:failed` trace get it. The credentials are those the configuration holds and those its dials sent: from the url as each dial sends it (a WebSocket dial sets the token in the query, which writes the whole query again, form-encoded, and an access provider's token can be in it; with `tokenTransport: 'header'` it sets none), credential-like path segments and query names, credential-shaped or credential-named query values, and a WebSocket url's userinfo, in their encoded and decoded forms, replaced wherever they occur; every non-empty query parameter as its whole `name=value`, where no letter or digit adjoins it; the static `token`, and each token an access provider handed a dial, the newest sixteen, raw and url-encoded; and a stdio server's `env` values whose names have a part ending in a credential word, such as `GITHUB_TOKEN`, `PGPASSWORD` or `SESSION_COOKIE`, raw and url-encoded. A stdio server's command and arguments aren't derived. Standalone strings shorter than eight characters stay unchanged because they may be ordinary words; short query values are still removed with their parameter names. Treat a submitted configuration as immutable while the server uses it: apply changes with `restartMcplServer(id, newConfig)`. Secrets neither the configuration holds nor a dial sent remain outside this filtering. A JSON-RPC tool answer's result passes through as sent. No error a host's own call gets, whether thrown, rejected with or in an error answer's data, carries a credential this filtering replaces, whoever wrote its words: a server's words are redacted, then bounded, where an error is built from them, the strings in an error answer's data are redacted, and an error a library raised, such as a failed modern connect's, is handed on as its name and its redacted words alone. A stdio child that couldn't be started is refused with Node's words redacted, stopping before Node's quote of a value it refused at once, and a cause that keeps Node's error's name, those words and its `code`. With a configuration that can't be read, the server's words and data are withheld. A failed handshake's `stderrTail` is the end of the server's stderr, with every credential of up to 14,336 characters that reaches into it replaced whole. A number in an error answer's data is matched as JavaScript reads it, so one with more significant digits than a double holds has lost its last digits before it's matched. A close's or a cleanup's failure carries the transports' own words, a child's pid, its server's id and the wait it was given. A connection's events are inputs to the framework's records, which redact them.
|
|
414
551
|
|
|
415
552
|
#### Feature sets
|
|
@@ -449,14 +586,15 @@ first.
|
|
|
449
586
|
|
|
450
587
|
Plain content stays in context and is delivered on arrival through the ordinary
|
|
451
588
|
channel/push path; the host only remembers where it landed. "Unread" means:
|
|
452
|
-
above the agent's consumed watermark (
|
|
589
|
+
above the agent's consumed watermark (a position in the agent's window, #291:
|
|
590
|
+
a row not in the window yet is unread wherever it is in the store; set when the agent is created, and
|
|
453
591
|
advanced at every compile of a request it sends, which a preview isn't, and at
|
|
454
592
|
a tool boundary that injects messages into a stream declaring it carries them,
|
|
455
593
|
as membrane's native path does from 0.6.0), on the current branch, not a
|
|
456
594
|
sharded message, and not folded into a summary by compression. A stream that
|
|
457
595
|
doesn't say whether it carries injections is taken as one that doesn't: at a
|
|
458
|
-
tool boundary nothing is injected into it, and what
|
|
459
|
-
turn's end. On any doubt — restart, branch switch, eviction — the host
|
|
596
|
+
tool boundary nothing is injected into it, and what arrived for the agent
|
|
597
|
+
waits out of its window for the turn's end. On any doubt — restart, branch switch, eviction — the host
|
|
460
598
|
appends, as it does today. There is no per-subject cap: a busy channel's unread
|
|
461
599
|
backlog is simply its unread backlog. Receipts and subject history are kept in
|
|
462
600
|
the `mcpl/coalescing` state (retry window 1 h); audit lines are
|
|
@@ -514,9 +652,150 @@ tool list itself (RFC-014 §8.5 host-made toolsets, §8.1 surface presentation;
|
|
|
514
652
|
and works for every server, with or without RFC-014. The `toolsets--` tools
|
|
515
653
|
are registered for every agent, MCPL or none, unless a module holds the name.
|
|
516
654
|
|
|
655
|
+
- **Wake onboarding** (`src/gate/wake-onboarding.ts`). An MCPL server's
|
|
656
|
+
events are often let by, without waking the agent, by a rule written for
|
|
657
|
+
something else (a catch-all with no `source`) or by the gate's default. The
|
|
658
|
+
first such event from a server is followed by a `[wake onboarding]` notice:
|
|
659
|
+
the rule that let it by, and the server's suggested treatment (MCPL RFC-001
|
|
660
|
+
§5), numbered, with each rule's rationale, presented as the claims of a
|
|
661
|
+
server that may not be trustworthy. An event tagged `chat:addressed`,
|
|
662
|
+
`surface:turn` or `surface:alert` wakes the agent to decide.
|
|
663
|
+
`wake_accept_suggestions { server, rules? }` writes the chosen suggestions as
|
|
664
|
+
`suggested:<server>:<n>` rules scoped to that server, ahead of the first
|
|
665
|
+
catch-all; `wake_decline { server }` removes them. Decisions are kept in
|
|
666
|
+
`wake-decisions.json` beside `gate.json`, keyed to the server's declared
|
|
667
|
+
ontology, so the agent is asked again only when it changes (and at most once
|
|
668
|
+
a day per server). `gate_status.catchAll` names the catch-all; `event_tags`
|
|
669
|
+
shows each server's offer. `gate.onboarding: false` turns it off.
|
|
670
|
+
|
|
671
|
+
- **Event suppression** (`EventGate.decideSuppression`, `src/gate/suppress-scripts.ts`,
|
|
672
|
+
`src/gate/suppression-stats.ts`, `src/gate/suppression-review.ts`). A wake
|
|
673
|
+
rule with `suppress: { description, script? }` keeps the events it takes out
|
|
674
|
+
of the agent's context entirely. A rule that only doesn't wake still shows
|
|
675
|
+
its events. A suppressed event is stored in the chronicle as it arrives,
|
|
676
|
+
with `metadata.suppressed = { rule, eventType }`. Every view filter the
|
|
677
|
+
framework sets leaves stamped rows out: the residents', the subconscious's,
|
|
678
|
+
and every view derived from them. The row is stamped for nobody (an empty
|
|
679
|
+
`metadata.audience`), so the presentation index presents it to no window.
|
|
680
|
+
So a suppressed row is never compiled,
|
|
681
|
+
never injected at a tool boundary, never folded into memory, and never
|
|
682
|
+
woken for. It never becomes the speech locus and never brings a clock row
|
|
683
|
+
in ahead of it. The decision is made once, at arrival, before the hold, the
|
|
684
|
+
veto and the gate's evaluate, so no debounce batch forms for the event. The
|
|
685
|
+
suppress rule's `matchCount` counts what it took, and a passthrough rule ahead
|
|
686
|
+
of it counts it too, so a sampler fires on schedule. Because the view reads only the stamp, editing or removing the
|
|
687
|
+
rule changes only what arrives afterwards; the rows already hidden stay
|
|
688
|
+
hidden, which keeps each row's visibility fixed, as context-manager's
|
|
689
|
+
`viewFilter` requires. **Rollback needs a step.** A release without
|
|
690
|
+
suppression doesn't read the stamp. Where compression formed a summary around
|
|
691
|
+
a hidden row, that summary is non-contiguous under such a release's view, and
|
|
692
|
+
context-manager refuses the store at open (`StoreTopologyError`), unless the
|
|
693
|
+
strategy sets `topologyPolicy` or a kv-unified gap flag. Before rolling back,
|
|
694
|
+
with the host stopped, run this release's `agent-framework-recover --store
|
|
695
|
+
<path> --agent <name> --remove-suppressed`: it makes and activates a branch
|
|
696
|
+
without the hidden rows, which such a release opens, and the source branch
|
|
697
|
+
keeps them. That holds unless this release has made a window write of two or
|
|
698
|
+
more rows (the first write-down of a window that holds a row, as at the first
|
|
699
|
+
boot on a store with history, or a presentation of more than one row): the
|
|
700
|
+
store's MANIFEST is then at version 2, which no earlier release opens, and
|
|
701
|
+
only a copy taken before the upgrade goes back (the 291-presentation-mandatory
|
|
702
|
+
changelog entry). Skipped where no summary spans one, the rows render again where
|
|
703
|
+
they were stored (a prompt-cache miss from the first). Suppress rules load as
|
|
704
|
+
`defer` in every release back to 0.22, so nothing wakes for their events.
|
|
705
|
+
Releases 0.22 to 0.26 rewrite gate.json without `suppress` at their first
|
|
706
|
+
rule edit, so rolling back that far and forward again leaves rules that defer
|
|
707
|
+
and no longer suppress.
|
|
708
|
+
- **The rule.** It needs `behavior: "defer"` (or the legacy `"skip"`) written out, so an earlier
|
|
709
|
+
release that doesn't know `suppress` defers those events instead of
|
|
710
|
+
waking on them, a match that narrows it, and a `description` of what it's
|
|
711
|
+
for, which the review checks against. Three kinds of event are never
|
|
712
|
+
suppressed and go on to the next rules: an event addressed to the agent
|
|
713
|
+
(`chat:addressed`), the heartbeat's, and console or API input. A suppress
|
|
714
|
+
rule decides no wake for an event suppression was asked about: `evaluate`
|
|
715
|
+
and `probe` pass over it there, and the rule-order notes and the catch-all
|
|
716
|
+
check pass over it always. Where suppression isn't
|
|
717
|
+
asked, as for an event delivered to a conversation fork's own store, a
|
|
718
|
+
suppress rule defers the events it matches, as its behavior says.
|
|
719
|
+
- **The agent's own logic.** `script` is the source of a function
|
|
720
|
+
`(event, state) => boolean`, called for each event the rule's match takes
|
|
721
|
+
that reaches it (not one an earlier rule decides, an addressed one, or the
|
|
722
|
+
heartbeat's);
|
|
723
|
+
true hides it, and anything else (a truthy string included) lets the event
|
|
724
|
+
on to the rules after it.
|
|
725
|
+
`event` carries the event type, source, channel, author, tags, text,
|
|
726
|
+
arrival time (`at`, ms) and metadata. `state` is the rule's own JSON object
|
|
727
|
+
(at most 64 KB), which the script reads and changes as it likes, so a rule
|
|
728
|
+
can hide repeats, count, or keep a window of its own. The framework keeps
|
|
729
|
+
it between events and across restarts in `suppress-state.json` beside
|
|
730
|
+
gate.json, and starts it afresh when the script changes or the rule is
|
|
731
|
+
removed (not for a new description). Scripts run on one worker thread,
|
|
732
|
+
called synchronously with a 50 ms timeout (`gate.suppressScriptTimeoutMs`), as
|
|
733
|
+
gate.js is: a robustness boundary for the agent's own code, not a security
|
|
734
|
+
sandbox. A script that throws, times out, isn't a function, or leaves a
|
|
735
|
+
state it can't keep lets the event on to the rules after it, as one that
|
|
736
|
+
says show does, keeps its state as it was, and is named in `gate_status`
|
|
737
|
+
(`suppress.scriptStatus`). A timeout restarts the worker, which loads every
|
|
738
|
+
script again one at a time; a script that times out three times in its
|
|
739
|
+
last twenty runs, probes included, isn't run again until its script
|
|
740
|
+
changes or the host restarts, and a worker that fails to start is tried again with a later
|
|
741
|
+
event, later each time. wake_add_rule compiles a
|
|
742
|
+
script on the worker before it saves the rule. `suppressionFor` runs a
|
|
743
|
+
script on a copy of its state, for the probe table; the arrival paths call
|
|
744
|
+
`decideSuppression`, once per event, which keeps the state the script
|
|
745
|
+
leaves. The worker starts with the first rule that has a script.
|
|
746
|
+
- **Covered:** MCPL channel messages and push events (admission; a deferred
|
|
747
|
+
coalesced push is decided once, at its wake, and a suppressed batch is
|
|
748
|
+
stored at once; a batch's row follows its latest occurrence, as its wake
|
|
749
|
+
does, unless the batch holds an addressed occurrence, when it's shown), a module's events (every message its responses add, and
|
|
750
|
+
every wake they ask for, the event decided once whichever module answers
|
|
751
|
+
first), and a server's `[toolsets]` notices, gated as its
|
|
752
|
+
`mcpl:tool-changes` events.
|
|
753
|
+
Not covered: deliveries into a conversation fork's own store, which has no
|
|
754
|
+
view filter.
|
|
755
|
+
- **Tools.** `wake_add_rule { …, suppress: {…} }` adds a suppress rule (saved
|
|
756
|
+
with behavior "defer"), and `wake_remove_rule` removes it. `gate_status`
|
|
757
|
+
shows each suppress rule's settings and how its script has run, and counts
|
|
758
|
+
what it suppressed since start. `suppression_stats { rule?, recent?, since? }`
|
|
759
|
+
reads the stamped rows in the caller's message slot on its current
|
|
760
|
+
branch, so its counts cover every start and follow the branch. Per rule it
|
|
761
|
+
reports the count, the text size, the first and last arrival, a breakdown
|
|
762
|
+
by source and event type, and the newest rows with a one-line preview.
|
|
763
|
+
Rules removed since are listed with `inForce: false`.
|
|
764
|
+
`dump_suppressed { path?, rule?, since?, until?, format? }` writes the
|
|
765
|
+
rows to a workspace file, their text in full and any other block named:
|
|
766
|
+
a transcript, or JSON lines. Times are when each row was stored. At most
|
|
767
|
+
4 MB; past that, the newest that fit, and the result counts the rest.
|
|
768
|
+
Hosts read the same rows with `AgentFramework.suppressedEvents()`.
|
|
769
|
+
- **Review.** Every `gate.suppressionReview.intervalMs` (two hours by
|
|
770
|
+
default), for each rule that hid events since the last review, a small
|
|
771
|
+
model (`model`, Claude Haiku 5.5 by default, on the reply judge's
|
|
772
|
+
membrane) reads the rule's description, its match and script while it's in
|
|
773
|
+
force (the script cut to 3,000 characters), and a sample of the events it hid: at most
|
|
774
|
+
`maxEventsPerRule` (60) spread across the period, sharing 24,000 characters
|
|
775
|
+
of text, so a short event is read whole and a long one takes what the
|
|
776
|
+
others leave. These bounds are chosen, not measured: they keep one rule's
|
|
777
|
+
request to roughly 8,000 input tokens. It flags any that don't fit the
|
|
778
|
+
description or that the agent would plausibly need to see. The agent gets a `[suppression review]`
|
|
779
|
+
notice naming each rule, what it hid and what was flagged, with the first
|
|
780
|
+
five flagged events of each rule shown, each cut short. A rule the
|
|
781
|
+
reviewer couldn't answer about is reviewed again next time, from where its
|
|
782
|
+
events begin (at most a week back; older events aren't reviewed again). A flag wakes the agent (`wakeOnFlag`, default true), unless it's asleep. With
|
|
783
|
+
`notify: "flagged"` the agent is told only of a flag; with the default,
|
|
784
|
+
`"always"`, after every review in which something was hidden. The notice
|
|
785
|
+
is the framework's own and never suppressed. Progress, retries and each
|
|
786
|
+
rule's last description (noted as the rule hides a row) are kept in
|
|
787
|
+
`suppression-review.json` beside gate.json, written as the review starts,
|
|
788
|
+
so a restart resumes and a removed rule's events are still judged. Its
|
|
789
|
+
calls count toward the agent's usage, as the reply judge's do, and a wake
|
|
790
|
+
on a flag is a full turn of the agent's own model, so a false flag costs
|
|
791
|
+
that turn.
|
|
792
|
+
`gate.suppressionReview: false` turns it off; `AgentFramework.reviewSuppression()`
|
|
793
|
+
runs one now. The `gate:suppression-review` trace records each review.
|
|
794
|
+
|
|
517
795
|
- **The built-in toolset, `settings`.** The host tools an agent reaches for
|
|
518
796
|
rarely: `agent_settings`, `wake_add_rule`, `wake_remove_rule`,
|
|
519
|
-
`
|
|
797
|
+
`wake_accept_suggestions`, `wake_decline`,
|
|
798
|
+
`gate_status`, `suppression_stats`, `dump_suppressed`, `event_tags`, `set_tool_visibility`, `set_tool_description`,
|
|
520
799
|
`heartbeat--heartbeat_status`, `heartbeat--heartbeat_trigger`,
|
|
521
800
|
`workspace--status`, every module utility (`Module.getUtilities`), and a
|
|
522
801
|
module's tools it places there (`Module.toolPlacement` answering
|
|
@@ -634,7 +913,7 @@ A model call that stops making progress is cut, so an agent isn't held by a prov
|
|
|
634
913
|
- **A resident's streamed call**, and an MCPL server's streamed inference request, get membrane's progress bounds where the adapter's stream reports content (`AnthropicAdapter`, `BedrockAdapter`). Before content, a call ends 60 s after its send, or, once a thinking block has started, at the later of that and the block's allowance, its `max_tokens` at 87 tokens a second. Once content has begun, it ends after 150 s without a provider event, or after the block's allowance when the last event was a thinking block's start and the allowance is longer. Membrane's README gives the rules whole.
|
|
635
914
|
- **Every streamed call the framework makes** also gets a flat bound from its last provider event: the larger of a setup term plus that allowance and 1,862 s (`FLAT_MINIMUM_MS`). The setup term is the larger of the before-content and between-events bounds, 150 s by default. So on an adapter with progress bounds the flat bound never runs out first. On one without, such as `OpenAICompatibleAdapter`, it's the only progress bound, and an SSE keepalive comment doesn't restart it, though a data frame that parses as an event does, a provider's own ping object included.
|
|
636
915
|
- **A whole non-streamed call**, a compression's, the reply judge's, or an MCPL server's non-streamed inference request, ends the setup term plus its allowance after its send, or, when it names no `max_tokens`, at the larger of the setup term and the flat minimum, unless its caller passed a `progressTimeoutMs` of its own. Its caller gets the `timeout` error. The event gate's own 20 s judge timeout ends the reply judge's call first at the defaults. Context-manager takes a cut compression call as a transient failure: the lane pauses and tries again, so compression that never lands within its bound shows as a lane that keeps pausing, not as a failed turn, and is a reason to raise the bound. On an adapter whose `complete()` streams under the hood, such as membrane's OpenAI Responses adapter in subscription mode, membrane renews the bound at each parsed provider event, so there it runs from the last event, as a stream's flat bound does.
|
|
637
|
-
- **A turn's call a bound cuts** fails as a `timeout`, and `DefaultErrorPolicy` gives it one fresh call, 1 s later, whatever other failures its turn had, except in a turn sent again after its retries ran out, before one of its rounds has run or had its tool calls cut off, whose failures get no retries. A fresh call that's cut too, with no other failure between, ends the turn, and its `[inference-failed]` note says the call timed out, that the call was made again and timed out again, and
|
|
916
|
+
- **A turn's call a bound cuts** fails as a `timeout`, and `DefaultErrorPolicy` gives it one fresh call, 1 s later, whatever other failures its turn had, except in a turn sent again after its retries ran out, before one of its rounds has run or had its tool calls cut off, whose failures get no retries. A fresh call that's cut too, with no other failure between, ends the turn, and its `[inference-failed]` note says the call timed out, that the call was made again and timed out again, and, for a bound's cut, the reason, which names the bound that ran out and the host's setting for it. A deadline expiry membrane throws (the Anthropic adapter's own watchdogs, the SDK's request timeout, a runtime's fetch timeout on a silent connection) counts the same toward the fresh call and the turn's end, and its note gives no reason: the note gives one only for a bound's cut, a stream's abort or context-manager's refusal of a compile. A bound's cut isn't `retryable`, so a host's own `errorPolicy` that retries only retryable errors gives it no fresh call; an expiry membrane throws stays `retryable`, as membrane made it.
|
|
638
917
|
- **A stream that keeps sending content** isn't ended by any of these, since content is progress.
|
|
639
918
|
|
|
640
919
|
These bounds don't replace the deadlines below them: a silent connection still meets the runtime's own fetch timeout (360 s on Bun 1.4.4) and, on `AnthropicAdapter`, the adapter's 600 s first-event and idle watchdogs, so on a silent connection a bound raised past those is met by them first.
|
|
@@ -668,7 +947,7 @@ The non-streaming path uses `AbortSignal` forwarded to `membrane.stream()`, whic
|
|
|
668
947
|
|
|
669
948
|
A turn whose stream is cancelled this way, or by `agent.cancelStream()`, ends as a stop, whether the stream reports the cancel as `aborted`, as an `error`, or by just closing: one `inference:aborted` carrying the caller's reason (`user` when none was given), a `[turn-interrupted]` note in the agent's context, and never `inference:exhausted`, which is for failed inferences. A stream that membrane aborts on its own, at a deadline or on an error (reason `timeout` or `error`), is a failed inference. `stop()` and an operator's quiesce abandon end a turn as a stop too, with their own reasons, `shutdown` and `quiesce_abandoned`, and write no note, except the `[speech-not-posted]` notice when the turn's rounds left speech unposted (XML tool mode, or a membrane without `roundContent`; see below). A stopped stream still writes its inference-log row, with `success: false`.
|
|
670
949
|
|
|
671
|
-
A caller's stop and an operator's quiesce abandon also reach a turn that has no live stream: one running its modules' `gatherContext`, its MCPL hooks or its compile, one waiting to retry a failed inference or compile (`abortInference()` and an operator's abandon end the wait, and a stop through `Agent.cancelStream()` waits for it to run out), and one whose tool-result guard is waiting to retry after a refusal, which holds only the round's finished stream. The turn ends at its next check, before it sends anything more, as a stop on a live stream ends it, and a turn whose compile fails after the stop ends at the failure, before a provider hold, the error policy's retry or `inference:exhausted`. A caller's stop leaves the note saying the turn was stopped before it sent anything or before it finished, and an operator's abandon leaves none, except the `[speech-not-posted]` notice when the turn's rounds left speech unposted. `abortInference()` returns `true` for such a turn. In the guard's wait, `stop()` ends the turn as a stop with reason `shutdown`, and an operator's abandon ends it as the abandon, even when a caller's stop came first; with both, it ends as `stop()`'s. `stop()` drops a turn in its hooks, its compile or a wait to retry before it sends anything, as before, and drops one whose compile fails while it runs, once a caller's stop has reached it, rather than ending it as that stop. While the failure of a turn's stream is reported (`inference:failed`), the agent no longer holds that stream, so `abortInference()` from a listener reports `false`. A failure to build a turn's request is reported while the turn's check is still ahead, so there `abortInference()` reports `true`, and the turn ends at that check. A caller's stop made where no check of a turn is ahead stops nothing, and doesn't end the agent's next turn: while the agent is idle, while a turn finishes its last delivery, and while a non-streaming turn's tool results wait to start the turn's next call. A turn whose continuation (a budget restart's, or a tool-result guard's retry) waits to start, or whose failed call a provider-capacity hold or a provider's stated wait keeps for its retry, is ended by `abortInference()`, by an operator's quiesce abandon and by `stop()`: the continuation or retry is dropped, and the turn ends with the caller's reason, `quiesce_abandoned` or `shutdown`. After `abortInference()` or an abandon, wakes that arrived meanwhile still run (after the abandon, once the host resumes): those a hold keeps beside the retry, and those the scheduler merged into the retry's or the continuation's start before that start began, which the stop or the abandon puts back in the queue. A stop through `Agent.cancelStream()` stops nothing there.
|
|
950
|
+
A caller's stop and an operator's quiesce abandon also reach a turn that has no live stream: one running its modules' `gatherContext`, its MCPL hooks or its compile, one waiting to retry a failed inference or compile (`abortInference()` and an operator's abandon end the wait, and a stop through `Agent.cancelStream()` waits for it to run out), and one whose tool-result guard is waiting to retry after a refusal, which holds only the round's finished stream. The turn ends at its next check, before it sends anything more, as a stop on a live stream ends it, and a turn whose compile fails after the stop ends at the failure, before a provider hold, the error policy's retry or `inference:exhausted`. A caller's stop leaves the note saying the turn was stopped before it sent anything or before it finished, and an operator's abandon leaves none, except the `[speech-not-posted]` notice when the turn's rounds left speech unposted. `abortInference()` returns `true` for such a turn. In the guard's wait, `stop()` ends the turn as a stop with reason `shutdown`, and an operator's abandon ends it as the abandon, even when a caller's stop came first; with both, it ends as `stop()`'s. `stop()` drops a turn in its hooks, its compile or a wait to retry before it sends anything, as before, and drops one whose compile fails while it runs, once a caller's stop has reached it, rather than ending it as that stop. While the failure of a turn's stream is reported (`inference:failed`), the agent no longer holds that stream, so `abortInference()` from a listener reports `false`. A failure to build a turn's request is reported while the turn's check is still ahead, so there `abortInference()` reports `true`, and the turn ends at that check. A caller's stop made where no check of a turn is ahead stops nothing, and doesn't end the agent's next turn: while the agent is idle, except for a turn a budget hold keeps (below), while a turn finishes its last delivery, and while a non-streaming turn's tool results wait to start the turn's next call. A turn whose continuation (a budget restart's, or a tool-result guard's retry) waits to start, or whose failed call a provider-capacity hold or a provider's stated wait keeps for its retry, is ended by `abortInference()`, by an operator's quiesce abandon and by `stop()`: the continuation or retry is dropped, and the turn ends with the caller's reason, `quiesce_abandoned` or `shutdown`. After `abortInference()` or an abandon, wakes that arrived meanwhile still run (after the abandon, once the host resumes): those a hold keeps beside the retry, and those the scheduler merged into the retry's or the continuation's start before that start began, which the stop or the abandon puts back in the queue. A stop through `Agent.cancelStream()` stops nothing there. A turn a budget hold keeps waiting for compression is idle: `abortInference()` and `stop()` end it with one `inference:aborted`, and after `abortInference()` the wakes that joined it go back in the queue; a quiesce has nothing of it to abandon. Once released, a first start is a queued wake until its start begins, which `abortInference()` leaves to run, and a continuation is a queued continuation, which it ends.
|
|
672
951
|
|
|
673
952
|
In XML tool mode, or with a membrane that gives a tool round no `roundContent`, such a round's speech reaches a channel only when its stream completes. When the stream ends any other way (a failure, a stop, a tool that ends the turn, an error policy's retry, a context-budget or tool-result-guard restart, a quiesce abandon or `stop()`), the rounds' words are already in the agent's context, and a `[speech-not-posted]` notice tells the agent that nothing it wrote before its tool calls in those rounds was posted, so it can send what should go out again. It's written once for those rounds: where the turn ends, or before a retry's or a restart's request is sent. A turn its own `skip_reply` ends, a silent turn, a round still waiting on its tools when its stream ends (which never reaches the context) and the rounds of a stream that completes get none. In XML tool mode with thinking on, a round whose text was only thinking gets the notice too, since the framework has only the round's raw text there. A failed turn's `[inference-failed]` note says that its earlier rounds ran when any round reached the context with its results, and that a round's tool calls were started and may have taken effect when any of its streams ended before that round's results came back; a caller's stop says the latter in its `[turn-interrupted]` note too. A turn that fails, or that a caller stops, writes no `[delivered]` receipt, so when its rounds' plain speech was delivered live (native tool mode, in locus or hybrid routing), its `[inference-failed]` note, or the stop's `[turn-interrupted]` note, says that speech was handed on for delivery as written and that no `[delivered]` line was written for it. When some of its speech was still being routed as the turn ended, the note also says it may yet be posted and to check the channel before sending it again. A delivery that finished undelivered gets neither: a channel delivery's `[discord-send-failed]` note, or a refused `>>>` envelope's bounce, already says what happened. A quiesce abandon and `stop()` write neither note.
|
|
674
953
|
|
|
@@ -691,10 +970,14 @@ Pause the inference thread and MCPL data planes while keeping the framework,
|
|
|
691
970
|
context managers, and membrane hot — so compression/refold/quarantine work runs
|
|
692
971
|
through the live machinery instead of offline scripts (issue #122). Quiesce
|
|
693
972
|
persists across restarts; `resume` gates on a fresh per-agent feasibility
|
|
694
|
-
preview of the current runtime settings (`force` overrides).
|
|
973
|
+
preview of the current runtime settings (`force` overrides). While quiesced, a
|
|
974
|
+
context write (a module event, an API `message.send`) is stored at once, unless
|
|
975
|
+
a surgery's hold or a context manager refusing its work keeps it in memory, and
|
|
976
|
+
enters the agents' windows at `resume`, or, for an agent whose turn is still
|
|
977
|
+
draining then, at that turn's next point (#291).
|
|
695
978
|
|
|
696
979
|
```typescript
|
|
697
|
-
await framework.quiesce({ reason: 'refold', timeoutMs: 120_000, abandon: false });
|
|
980
|
+
await framework.quiesce({ reason: 'refold', timeoutMs: 120_000, abandon: false });
|
|
698
981
|
await framework.maintenanceTick(); // drain quarantine / advance merges
|
|
699
982
|
await framework.resume(); // throws ResumeBlockedError if the layout won't compile
|
|
700
983
|
```
|
|
@@ -715,7 +998,8 @@ for deployments that front the port with a proxy.
|
|
|
715
998
|
message and switches to the fork; `suppressMessages(agent, { messageIds })`
|
|
716
999
|
forks at the head and removes the chosen messages on the fork. The source
|
|
717
1000
|
branch stays intact either way. Removing Discord messages from an agent's
|
|
718
|
-
context
|
|
1001
|
+
context marks nothing on Discord unless the operator also chooses to mark
|
|
1002
|
+
them:
|
|
719
1003
|
|
|
720
1004
|
```typescript
|
|
721
1005
|
const preview = framework.previewSurgeryMarks('cairn', { rollbackTo: messageId });
|
|
@@ -727,6 +1011,36 @@ const result = await framework.rollbackToMessage('cairn', {
|
|
|
727
1011
|
// result.markers: { status: 'none' | 'queued' | 'not-scheduled', ... }
|
|
728
1012
|
```
|
|
729
1013
|
|
|
1014
|
+
An agent's context follows its own window (#291), so the cut is the write
|
|
1015
|
+
that presented the target: a rollback removes only the rows stored after
|
|
1016
|
+
that write, and the rows stored before it that the window took after the
|
|
1017
|
+
target leave the window (`returnedToArrivals`, on the result and the
|
|
1018
|
+
preview). The agent's own rows among them enter it again at its next
|
|
1019
|
+
presentation point, the surgery's release; the rest wait outside it until
|
|
1020
|
+
the agent's next turn start, so for them the cut holds until the agent
|
|
1021
|
+
speaks again, on the rollback's branch: a later move of the store's branch,
|
|
1022
|
+
a suppression or rollback through another agent included, ends it. That hold
|
|
1023
|
+
is in memory: a restart before then presents them at its pass over every
|
|
1024
|
+
agent at rest, or, where it boots quiesced, at the resume. Rows that one write presented together with
|
|
1025
|
+
the target stay after it (`keptInWindowAfterTarget`), since no branch cut
|
|
1026
|
+
separates them from it: a rollback that only they follow is refused and
|
|
1027
|
+
names them, and `suppressMessages` (on the slot's owner, for a row of a slot
|
|
1028
|
+
the agent observes, which the refusal names), or, for those with a Discord message id,
|
|
1029
|
+
the host's `hide` by it, run on the agent whose own slot holds the row (the
|
|
1030
|
+
refusal names it), takes them out. A row holding a result of a call at or
|
|
1031
|
+
before the target (a tool boundary presents a round's reply and its results
|
|
1032
|
+
in one write) isn't advised for suppression, which would leave the call
|
|
1033
|
+
without its result: the refusal says to roll back before the call. The preview's `neverPresented` counts the messages it
|
|
1034
|
+
removes that no live agent is shown: no window holds them, or only windows
|
|
1035
|
+
whose view leaves them out (a row a tune-out holds); a message that may be in
|
|
1036
|
+
a window that couldn't be read isn't counted, and is logged. The host command's
|
|
1037
|
+
`/undo n` counts back over the rows of the agent's own slot in its window. A
|
|
1038
|
+
removal in place
|
|
1039
|
+
(a `hide`, a suppression on its fork, a module's or the coalescer's removal)
|
|
1040
|
+
takes the rows out of every other live agent's window on that branch that
|
|
1041
|
+
held them too, each at its own place; a window on a branch whose record
|
|
1042
|
+
still holds them keeps them.
|
|
1043
|
+
|
|
730
1044
|
- **`marks`** is `'none'` (the default), or `{ scope: 'addressed' | 'all', refs? }`.
|
|
731
1045
|
`addressed` covers messages tagged `chat:addressed` (mentions, replies to
|
|
732
1046
|
the bot, DMs). Passing a preview's `refs` binds the choice to exactly that
|
|
@@ -878,7 +1192,11 @@ framework.onTrace((event) => {
|
|
|
878
1192
|
Trace events are observability-only. They are intended for logs, metrics, UI
|
|
879
1193
|
updates, and debugging, not framework-internal control flow. Internal lifecycle
|
|
880
1194
|
code should use state-machine signals or process events instead of consuming
|
|
881
|
-
traces, so trace emit order does not become an API contract.
|
|
1195
|
+
traces, so trace emit order does not become an API contract. One exception: the
|
|
1196
|
+
Discord module applies the edits and deletes it kept for a held create on
|
|
1197
|
+
`message:added` (source `surgery-release`), and ends a lost create's record on
|
|
1198
|
+
`message:lost`, so those two traces, emitted as a surgery's release stores a
|
|
1199
|
+
held write or loses it, are a contract.
|
|
882
1200
|
|
|
883
1201
|
Query raw inference logs:
|
|
884
1202
|
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
* gets the same disposition. A reader of the slot (the subconscious, or a
|
|
23
23
|
* reader fork) sees held traffic through its own view; that is not a
|
|
24
24
|
* delivery decision of this stage.
|
|
25
|
+
*
|
|
26
|
+
* Suppression is the other disposition: a suppress rule (a gate rule with
|
|
27
|
+
* a `suppress` object) took the event, so it's stored and never rendered, for
|
|
28
|
+
* any reader. It's decided first. A suppressed event is never held, never
|
|
29
|
+
* shown to the gate, never woken for, and never the locus.
|
|
25
30
|
*/
|
|
26
31
|
export interface InboundEnvelope {
|
|
27
32
|
/** Which MCPL lane carried it. */
|
|
@@ -70,11 +75,24 @@ export interface AdmissionPolicy {
|
|
|
70
75
|
evaluate(env: InboundEnvelope): boolean;
|
|
71
76
|
probe(env: InboundEnvelope): boolean;
|
|
72
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* The suppress rule that takes this event, or null; null where the event
|
|
80
|
+
* would land in a view that doesn't hide suppressed rows. Asked once per
|
|
81
|
+
* event, since it commits (a rule's script keeps the state it leaves, the
|
|
82
|
+
* gate's decideSuppression). Left out, nothing is suppressed.
|
|
83
|
+
*/
|
|
84
|
+
suppressionFor?(env: InboundEnvelope): string | null;
|
|
85
|
+
}
|
|
86
|
+
/** The record a suppressed event's row carries: the rule that took it. */
|
|
87
|
+
export interface SuppressionRecord {
|
|
88
|
+
rule: string;
|
|
73
89
|
}
|
|
74
90
|
export interface AdmissionDecision {
|
|
75
91
|
recipients: string[];
|
|
76
92
|
/** Null = delivered into the recipients' view; otherwise held, with the record to stamp. */
|
|
77
93
|
held: HoldRecord | null;
|
|
94
|
+
/** Null unless a suppress rule took the event: then it's stored, never rendered, and nothing else is decided for it. */
|
|
95
|
+
suppressed: SuppressionRecord | null;
|
|
78
96
|
/** Whether the recipients may be woken by this event. */
|
|
79
97
|
wake: boolean;
|
|
80
98
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admit.d.ts","sourceRoot":"","sources":["../../../src/admission/admit.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"admit.d.ts","sourceRoot":"","sources":["../../../src/admission/admit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB;qEACiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,0EAA0E;IAC1E,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oFAAoF;IACpF,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,oBAAoB,IAAI,MAAM,EAAE,CAAC;IACjC,mDAAmD;IACnD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IAChE;;;;;OAKG;IACH,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC;QACxC,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC;KACtC,CAAC;IACF;;;;;OAKG;IACH,cAAc,CAAC,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAAC;CACtD;AAED,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,4FAA4F;IAC5F,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,wHAAwH;IACxH,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACrC,yDAAyD;IACzD,IAAI,EAAE,OAAO,CAAC;IACd;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,eAAe,GAAG,iBAAiB,CA6BtF"}
|
|
@@ -22,22 +22,35 @@
|
|
|
22
22
|
* gets the same disposition. A reader of the slot (the subconscious, or a
|
|
23
23
|
* reader fork) sees held traffic through its own view; that is not a
|
|
24
24
|
* delivery decision of this stage.
|
|
25
|
+
*
|
|
26
|
+
* Suppression is the other disposition: a suppress rule (a gate rule with
|
|
27
|
+
* a `suppress` object) took the event, so it's stored and never rendered, for
|
|
28
|
+
* any reader. It's decided first. A suppressed event is never held, never
|
|
29
|
+
* shown to the gate, never woken for, and never the locus.
|
|
25
30
|
*/
|
|
26
31
|
export function admit(env, policy) {
|
|
27
32
|
const recipients = env.targetAgents ?? policy.untargetedRecipients();
|
|
33
|
+
// Suppressed: stored where it arrived and never rendered, so nothing
|
|
34
|
+
// downstream acts for anyone. Not held (a reader would see it), not shown
|
|
35
|
+
// to the gate's evaluate (no batch forms; the passthrough rules ahead of its
|
|
36
|
+
// rule have counted it), not a wake, not the locus.
|
|
37
|
+
const rule = policy.suppressionFor?.(env) ?? null;
|
|
38
|
+
if (rule !== null) {
|
|
39
|
+
return { recipients, held: null, suppressed: { rule }, wake: false, gatePass: false, locus: false };
|
|
40
|
+
}
|
|
28
41
|
const held = env.channelId ? policy.holdFor(env.serverId, env.channelId) : null;
|
|
29
42
|
if (held) {
|
|
30
43
|
// Held: nothing downstream may act for the recipients. The gate is only
|
|
31
44
|
// asked what it would have done — no batch forms, no counter moves.
|
|
32
45
|
const gatePass = env.vetoed ? false : (policy.gate ? policy.gate.probe(env) : true);
|
|
33
|
-
return { recipients, held, wake: false, gatePass, locus: false };
|
|
46
|
+
return { recipients, held, suppressed: null, wake: false, gatePass, locus: false };
|
|
34
47
|
}
|
|
35
48
|
if (env.vetoed) {
|
|
36
|
-
return { recipients, held: null, wake: false, gatePass: false, locus: true };
|
|
49
|
+
return { recipients, held: null, suppressed: null, wake: false, gatePass: false, locus: true };
|
|
37
50
|
}
|
|
38
51
|
// Deliverable: now the gate may take its side effects (a debounce batch
|
|
39
52
|
// is the gate's own deferred wake and is consumed later by the same rule).
|
|
40
53
|
const gatePass = policy.gate ? policy.gate.evaluate(env) : true;
|
|
41
|
-
return { recipients, held: null, wake: gatePass, gatePass, locus: true };
|
|
54
|
+
return { recipients, held: null, suppressed: null, wake: gatePass, gatePass, locus: true };
|
|
42
55
|
}
|
|
43
56
|
//# sourceMappingURL=admit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admit.js","sourceRoot":"","sources":["../../../src/admission/admit.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"admit.js","sourceRoot":"","sources":["../../../src/admission/admit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAmFH,MAAM,UAAU,KAAK,CAAC,GAAoB,EAAE,MAAuB;IACjE,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,IAAI,MAAM,CAAC,oBAAoB,EAAE,CAAC;IAErE,qEAAqE;IACrE,0EAA0E;IAC1E,6EAA6E;IAC7E,oDAAoD;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,cAAc,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IAClD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACtG,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEhF,IAAI,IAAI,EAAE,CAAC;QACT,wEAAwE;QACxE,oEAAoE;QACpE,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACpF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACrF,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QACf,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACjG,CAAC;IAED,wEAAwE;IACxE,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7F,CAAC"}
|