@animalabs/agent-framework 0.8.0 → 0.9.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.
Files changed (172) hide show
  1. package/dist/src/agent.d.ts +10 -0
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +33 -3
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +171 -15
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +1059 -224
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/gate/event-gate.d.ts +11 -0
  10. package/dist/src/gate/event-gate.d.ts.map +1 -1
  11. package/dist/src/gate/event-gate.js +66 -0
  12. package/dist/src/gate/event-gate.js.map +1 -1
  13. package/dist/src/index.d.ts +1 -0
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +1 -0
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/mcpl/capability-grant.d.ts +115 -0
  18. package/dist/src/mcpl/capability-grant.d.ts.map +1 -0
  19. package/dist/src/mcpl/capability-grant.js +291 -0
  20. package/dist/src/mcpl/capability-grant.js.map +1 -0
  21. package/dist/src/mcpl/capability-mask.d.ts +51 -0
  22. package/dist/src/mcpl/capability-mask.d.ts.map +1 -0
  23. package/dist/src/mcpl/capability-mask.js +190 -0
  24. package/dist/src/mcpl/capability-mask.js.map +1 -0
  25. package/dist/src/mcpl/channel-registry.d.ts +3 -1
  26. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  27. package/dist/src/mcpl/channel-registry.js +163 -36
  28. package/dist/src/mcpl/channel-registry.js.map +1 -1
  29. package/dist/src/mcpl/checkpoint-manager.d.ts +8 -3
  30. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  31. package/dist/src/mcpl/checkpoint-manager.js +28 -8
  32. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  33. package/dist/src/mcpl/errors.d.ts +6 -0
  34. package/dist/src/mcpl/errors.d.ts.map +1 -1
  35. package/dist/src/mcpl/errors.js +6 -0
  36. package/dist/src/mcpl/errors.js.map +1 -1
  37. package/dist/src/mcpl/feature-set-manager.d.ts +10 -1
  38. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
  39. package/dist/src/mcpl/feature-set-manager.js +45 -4
  40. package/dist/src/mcpl/feature-set-manager.js.map +1 -1
  41. package/dist/src/mcpl/hook-orchestrator.d.ts +10 -14
  42. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  43. package/dist/src/mcpl/hook-orchestrator.js +58 -67
  44. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  45. package/dist/src/mcpl/index.d.ts +1 -1
  46. package/dist/src/mcpl/index.d.ts.map +1 -1
  47. package/dist/src/mcpl/index.js.map +1 -1
  48. package/dist/src/mcpl/prose-stream-router.d.ts +9 -0
  49. package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
  50. package/dist/src/mcpl/prose-stream-router.js +16 -0
  51. package/dist/src/mcpl/prose-stream-router.js.map +1 -1
  52. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  53. package/dist/src/mcpl/push-handler.js +20 -2
  54. package/dist/src/mcpl/push-handler.js.map +1 -1
  55. package/dist/src/mcpl/server-connection.d.ts +93 -9
  56. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  57. package/dist/src/mcpl/server-connection.js +231 -32
  58. package/dist/src/mcpl/server-connection.js.map +1 -1
  59. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  60. package/dist/src/mcpl/server-registry.js +4 -3
  61. package/dist/src/mcpl/server-registry.js.map +1 -1
  62. package/dist/src/mcpl/tags.d.ts +24 -0
  63. package/dist/src/mcpl/tags.d.ts.map +1 -0
  64. package/dist/src/mcpl/tags.js +52 -0
  65. package/dist/src/mcpl/tags.js.map +1 -0
  66. package/dist/src/mcpl/transport.d.ts.map +1 -1
  67. package/dist/src/mcpl/transport.js +10 -0
  68. package/dist/src/mcpl/transport.js.map +1 -1
  69. package/dist/src/mcpl/types.d.ts +159 -64
  70. package/dist/src/mcpl/types.d.ts.map +1 -1
  71. package/dist/src/mcpl/types.js +16 -2
  72. package/dist/src/mcpl/types.js.map +1 -1
  73. package/dist/src/refusal-reactions.d.ts +27 -0
  74. package/dist/src/refusal-reactions.d.ts.map +1 -0
  75. package/dist/src/refusal-reactions.js +35 -0
  76. package/dist/src/refusal-reactions.js.map +1 -0
  77. package/dist/src/tool-result-history.d.ts +10 -0
  78. package/dist/src/tool-result-history.d.ts.map +1 -1
  79. package/dist/src/tool-result-history.js +16 -5
  80. package/dist/src/tool-result-history.js.map +1 -1
  81. package/dist/src/types/agent.d.ts +35 -0
  82. package/dist/src/types/agent.d.ts.map +1 -1
  83. package/dist/src/types/framework.d.ts +16 -0
  84. package/dist/src/types/framework.d.ts.map +1 -1
  85. package/dist/src/types/trace.d.ts +24 -0
  86. package/dist/src/types/trace.d.ts.map +1 -1
  87. package/dist/test/channel-registry-routing.test.js +38 -10
  88. package/dist/test/channel-registry-routing.test.js.map +1 -1
  89. package/dist/test/channel-registry-typing.test.js +10 -1
  90. package/dist/test/channel-registry-typing.test.js.map +1 -1
  91. package/dist/test/checkpoint-attribution.test.js +168 -8
  92. package/dist/test/checkpoint-attribution.test.js.map +1 -1
  93. package/dist/test/conversation-fork-injection-scoping.test.js +8 -3
  94. package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -1
  95. package/dist/test/event-gate-selfwake.test.d.ts +15 -0
  96. package/dist/test/event-gate-selfwake.test.d.ts.map +1 -0
  97. package/dist/test/event-gate-selfwake.test.js +125 -0
  98. package/dist/test/event-gate-selfwake.test.js.map +1 -0
  99. package/dist/test/machine-close-provenance.test.js +6 -0
  100. package/dist/test/machine-close-provenance.test.js.map +1 -1
  101. package/dist/test/mcpl-awareness-barrier.test.js +10 -0
  102. package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
  103. package/dist/test/mcpl-capability-grant.test.d.ts +10 -0
  104. package/dist/test/mcpl-capability-grant.test.d.ts.map +1 -0
  105. package/dist/test/mcpl-capability-grant.test.js +109 -0
  106. package/dist/test/mcpl-capability-grant.test.js.map +1 -0
  107. package/dist/test/mcpl-capability-scoping.test.d.ts +2 -0
  108. package/dist/test/mcpl-capability-scoping.test.d.ts.map +1 -0
  109. package/dist/test/mcpl-capability-scoping.test.js +266 -0
  110. package/dist/test/mcpl-capability-scoping.test.js.map +1 -0
  111. package/dist/test/mcpl-gate-roundtrip.test.js +13 -0
  112. package/dist/test/mcpl-gate-roundtrip.test.js.map +1 -1
  113. package/dist/test/mcpl-list-status.test.d.ts +2 -0
  114. package/dist/test/mcpl-list-status.test.d.ts.map +1 -0
  115. package/dist/test/mcpl-list-status.test.js +78 -0
  116. package/dist/test/mcpl-list-status.test.js.map +1 -0
  117. package/dist/test/mcpl-manifest-changed.test.d.ts +2 -0
  118. package/dist/test/mcpl-manifest-changed.test.d.ts.map +1 -0
  119. package/dist/test/mcpl-manifest-changed.test.js +177 -0
  120. package/dist/test/mcpl-manifest-changed.test.js.map +1 -0
  121. package/dist/test/mcpl-reconnect-events.test.js +27 -2
  122. package/dist/test/mcpl-reconnect-events.test.js.map +1 -1
  123. package/dist/test/mcpl-reconnect-reregistration.test.js +53 -17
  124. package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -1
  125. package/dist/test/mcpl-request-timeout.test.js +1 -1
  126. package/dist/test/mcpl-request-timeout.test.js.map +1 -1
  127. package/dist/test/physical-window-restart.test.d.ts +14 -0
  128. package/dist/test/physical-window-restart.test.d.ts.map +1 -0
  129. package/dist/test/physical-window-restart.test.js +222 -0
  130. package/dist/test/physical-window-restart.test.js.map +1 -0
  131. package/dist/test/present-while-acting.test.js +199 -1
  132. package/dist/test/present-while-acting.test.js.map +1 -1
  133. package/dist/test/refusal-reaction-baseline.test.d.ts +2 -0
  134. package/dist/test/refusal-reaction-baseline.test.d.ts.map +1 -0
  135. package/dist/test/refusal-reaction-baseline.test.js +74 -0
  136. package/dist/test/refusal-reaction-baseline.test.js.map +1 -0
  137. package/dist/test/state-scaling-e2e.test.js +7 -1
  138. package/dist/test/state-scaling-e2e.test.js.map +1 -1
  139. package/dist/test/state-scaling.test.js +9 -1
  140. package/dist/test/state-scaling.test.js.map +1 -1
  141. package/dist/test/tool-result-spill.test.d.ts +13 -0
  142. package/dist/test/tool-result-spill.test.d.ts.map +1 -0
  143. package/dist/test/tool-result-spill.test.js +463 -0
  144. package/dist/test/tool-result-spill.test.js.map +1 -0
  145. package/dist/test/trunk-channel-routing.test.js +45 -0
  146. package/dist/test/trunk-channel-routing.test.js.map +1 -1
  147. package/dist/tsconfig.tsbuildinfo +1 -1
  148. package/package.json +4 -4
  149. package/src/agent.ts +37 -3
  150. package/src/framework.ts +1219 -248
  151. package/src/gate/event-gate.ts +72 -0
  152. package/src/index.ts +1 -0
  153. package/src/mcpl/capability-grant.ts +319 -0
  154. package/src/mcpl/capability-mask.ts +228 -0
  155. package/src/mcpl/channel-registry.ts +167 -39
  156. package/src/mcpl/checkpoint-manager.ts +28 -8
  157. package/src/mcpl/errors.ts +7 -0
  158. package/src/mcpl/feature-set-manager.ts +50 -5
  159. package/src/mcpl/hook-orchestrator.ts +64 -86
  160. package/src/mcpl/index.ts +0 -2
  161. package/src/mcpl/prose-stream-router.ts +17 -0
  162. package/src/mcpl/push-handler.ts +18 -2
  163. package/src/mcpl/server-connection.ts +262 -34
  164. package/src/mcpl/server-registry.ts +4 -3
  165. package/src/mcpl/tags.ts +50 -0
  166. package/src/mcpl/transport.ts +11 -0
  167. package/src/mcpl/types.ts +176 -78
  168. package/src/refusal-reactions.ts +37 -0
  169. package/src/tool-result-history.ts +17 -5
  170. package/src/types/agent.ts +36 -0
  171. package/src/types/framework.ts +17 -0
  172. package/src/types/trace.ts +22 -0
package/src/mcpl/types.ts CHANGED
@@ -99,22 +99,32 @@ export interface McplResourceContent {
99
99
  * Parsed from the server's `initialize` response.
100
100
  */
101
101
  export interface McplCapabilities {
102
- /** MCPL protocol version (e.g., "0.4") */
102
+ /** MCPL protocol version (e.g., "0.5") */
103
103
  version: string;
104
104
 
105
+ /** §17.2 canonical content digest; absent = manifest fixed at initialize. */
106
+ revision?: string;
107
+
105
108
  /** Server supports push/event */
106
109
  pushEvents?: boolean;
107
110
 
108
- /** Context hook capabilities */
111
+ /**
112
+ * Context hooks — §5.1 recursive shape. Boolean `true` at any level is
113
+ * shorthand for every leaf beneath it. `afterInference` is REMOVED in
114
+ * 0.5.0 (replaced by metadata-only inference/lifecycle, §10.5).
115
+ */
109
116
  contextHooks?: {
110
- beforeInference?: boolean;
111
- afterInference?: boolean | { blocking: boolean };
117
+ beforeInference?: boolean | {
118
+ observe?: boolean;
119
+ inject?: { system?: boolean; beforeUser?: boolean; afterUser?: boolean };
120
+ };
112
121
  };
113
122
 
114
- /** Server-initiated inference capabilities */
115
- inferenceRequest?: {
116
- streaming?: boolean;
117
- };
123
+ /** Server-initiated inference. `true` = the capability with no refinements. */
124
+ inferenceRequest?: boolean | { streaming?: boolean };
125
+
126
+ /** Server consumes inference/lifecycle notifications (§10.5). */
127
+ inferenceLifecycle?: boolean;
118
128
 
119
129
  /** Server supports model/info requests */
120
130
  modelInfo?: boolean;
@@ -122,8 +132,8 @@ export interface McplCapabilities {
122
132
  /** Declared feature sets (keyed by feature set name) */
123
133
  featureSets?: Record<string, FeatureSetDeclaration>;
124
134
 
125
- /** Channel capabilities */
126
- channels?: McplChannelCapabilities;
135
+ /** Channel capabilities — §14.1 object of leaves, or `true` for all. */
136
+ channels?: boolean | McplChannelCapabilities;
127
137
  }
128
138
 
129
139
  /**
@@ -134,23 +144,28 @@ export interface McplHostCapabilities {
134
144
  version: string;
135
145
  pushEvents?: boolean;
136
146
  contextHooks?: {
137
- beforeInference?: boolean;
138
- afterInference?: boolean | { blocking: boolean };
139
- };
140
- inferenceRequest?: {
141
- streaming?: boolean;
147
+ beforeInference?: boolean | {
148
+ observe?: boolean;
149
+ inject?: { system?: boolean; beforeUser?: boolean; afterUser?: boolean };
150
+ };
142
151
  };
152
+ inferenceRequest?: boolean | { streaming?: boolean };
153
+ inferenceLifecycle?: boolean;
154
+ modelInfo?: boolean;
143
155
  featureSets?: boolean;
144
- channels?: McplChannelCapabilities;
156
+ channels?: boolean | McplChannelCapabilities;
145
157
  }
146
158
 
147
- /** Channel-specific capability flags. */
159
+ /** Channel capability leaves (§14.1). `observe` was the pre-0.5 name for
160
+ * what split into `incoming` — removed, not aliased. */
148
161
  export interface McplChannelCapabilities {
149
162
  register?: boolean;
150
- publish?: boolean;
151
- observe?: boolean;
152
163
  lifecycle?: boolean;
164
+ publish?: boolean;
165
+ incoming?: boolean;
153
166
  streaming?: boolean;
167
+ acknowledge?: boolean;
168
+ typing?: boolean;
154
169
  }
155
170
 
156
171
  // ============================================================================
@@ -209,6 +224,15 @@ export interface McplServerConfig {
209
224
  */
210
225
  accessProvider?: () => Promise<string | null>;
211
226
 
227
+ /**
228
+ * Host-owned authority for the `host/command` admin surface (undo / hide /
229
+ * unstick). DEFAULT FALSE: no §6.2 capability path exists for it, and the
230
+ * grant cannot confer it — only this config, decided by the operator, can
231
+ * (PR #79 review blocker 9). The one legitimate fleet user is
232
+ * discord-mcpl's slash-command relay.
233
+ */
234
+ allowHostCommands?: boolean;
235
+
212
236
  /** Feature sets to enable on connect */
213
237
  enabledFeatureSets?: string[];
214
238
 
@@ -233,6 +257,37 @@ export interface McplServerConfig {
233
257
  */
234
258
  disabledTools?: string[];
235
259
 
260
+ /**
261
+ * Capability allow-list: dotted paths as the server advertises them in its
262
+ * initialize response (`pushEvents`, `contextHooks.beforeInference`,
263
+ * `contextHooks.afterInference`, `inferenceRequest`, `modelInfo`,
264
+ * `channels`, `channels.streaming`, …). `*` matches one dot-segment
265
+ * (feature-set pattern rules), and a pattern matching a parent
266
+ * (`contextHooks`) covers every flag beneath it.
267
+ *
268
+ * If set, only advertised capabilities matching at least one pattern
269
+ * survive the handshake; everything else behaves as if the server had
270
+ * never advertised it. This is the host-side gate on hook fan-out: a
271
+ * server whose `contextHooks.afterInference` is masked never receives
272
+ * turn text, no matter what it self-advertises. Masked server-initiated
273
+ * capabilities (`pushEvents`, `inferenceRequest`, whole-`channels`) are
274
+ * also enforced inbound — the connection rejects those methods with
275
+ * CAPABILITY_DISABLED (-32002).
276
+ *
277
+ * `disabledCapabilities` takes precedence on conflict. `version` and
278
+ * `featureSets` are not maskable here — feature sets have their own
279
+ * enable/disable knobs above.
280
+ */
281
+ enabledCapabilities?: string[];
282
+
283
+ /**
284
+ * Capability deny-list (same paths and wildcard syntax as
285
+ * enabledCapabilities). Wins over enabledCapabilities on conflict.
286
+ * `disabledCapabilities: ['contextHooks.*']` is the one-line "this server
287
+ * gets no hook visibility" switch.
288
+ */
289
+ disabledCapabilities?: string[];
290
+
236
291
  /** Scope configurations per feature set */
237
292
  scopes?: Record<string, ScopeConfig>;
238
293
 
@@ -352,16 +407,51 @@ export interface FeatureSetDeclaration {
352
407
  * Spec Section 6.7.
353
408
  */
354
409
  export interface FeatureSetsUpdateParams {
355
- /** Feature sets to enable */
410
+ /**
411
+ * The effective capability grant (§5.4) — the sole normative allowlist.
412
+ * Full §6.2 paths. Absence of a path is denial. In the Request form an
413
+ * absent FIELD is a grant of nothing (§5.3 as pinned 2026-08-02), never
414
+ * "no change".
415
+ */
416
+ effectiveCapabilities?: string[];
417
+
418
+ /** Advertised-but-denied paths. Diagnostic only (§5.4) — MUST NOT
419
+ * participate in any authorization decision on either side. */
420
+ deniedCapabilities?: string[];
421
+
422
+ /** Feature sets to enable. Absent = no constraint (derivation governs);
423
+ * present = allowlist (§5.3 as pinned). */
356
424
  enabled?: string[];
357
425
 
358
426
  /** Feature sets to disable */
359
427
  disabled?: string[];
360
-
361
- /** Scope configurations per feature set */
362
- scopes?: Record<string, ScopeConfig>;
363
428
  }
364
429
 
430
+ /**
431
+ * featureSets/update result — the degradation receipt (§6.7). Consequence
432
+ * testimony, never policy authority: the host MUST NOT widen any grant in
433
+ * response to anything in here.
434
+ */
435
+ export type FeatureSetsUpdateResult =
436
+ | {
437
+ accepted: true;
438
+ /** Omitted when nothing degraded (§6.7). */
439
+ mode?: 'degraded';
440
+ unavailableFeatures?: Array<{
441
+ featureSet: string;
442
+ missingCapabilities: string[];
443
+ effect: string;
444
+ }>;
445
+ notes?: string[];
446
+ }
447
+ | {
448
+ accepted: false;
449
+ /** REQUIRED on refusal (§6.7): the server names which applies. */
450
+ fallback: 'mcp-only' | 'close';
451
+ missingCapabilities?: string[];
452
+ reason?: string;
453
+ };
454
+
365
455
  /**
366
456
  * featureSets/changed params (Server → Host, Notification).
367
457
  * Spec Section 6.7.
@@ -556,18 +646,48 @@ export interface PushEventResult {
556
646
  * Distinct from membrane's ModelInfo which tracks requested vs actual model.
557
647
  * Spec Section 10.1.
558
648
  */
649
+ /**
650
+ * inference/lifecycle params (Host → Server, Notification). SPEC §10.5.
651
+ *
652
+ * BEST-EFFORT: the host attempts exactly one terminal phase per `started`
653
+ * on every exit path it controls, but an unacknowledged Notification cannot
654
+ * guarantee delivery — consumers MUST dedupe by inferenceId and MUST retain
655
+ * a safety timeout. Carries no content: `modifiedResponse` and the blocking
656
+ * hook form are gone with context/afterInference.
657
+ */
658
+ export interface InferenceLifecycleParams {
659
+ inferenceId: string;
660
+ conversationId: string;
661
+ turnIndex: number;
662
+ phase: 'started' | 'completed' | 'aborted' | 'failed';
663
+ }
664
+
665
+ /** mcpl/manifestChanged params (Server → Host, Notification). §17.3. */
666
+ export interface ManifestChangedParams {
667
+ /** §17.2 canonical content digest. Untrusted; equality-only. */
668
+ revision: string;
669
+ /** Subset of the three §17.1 domains. A hint the host MAY ignore. */
670
+ domains: Array<'capabilities' | 'featureSets' | 'tagOntology'>;
671
+ }
672
+
559
673
  export interface McplModelInfo {
560
674
  /** Model identifier (e.g., "claude-opus-4-5-20251101") */
561
675
  id: string;
562
676
 
563
- /** Model vendor (e.g., "anthropic") */
564
- vendor: string;
677
+ /** Model vendor (e.g., "anthropic"). Omitted when the host has no
678
+ * truthful source — never fabricated. */
679
+ vendor?: string;
565
680
 
566
- /** Context window size in tokens */
567
- contextWindow: number;
681
+ /** Context window size in tokens. Omitted when unknown (a numeric field
682
+ * cannot express "unknown", and a fabricated 200000 was false for
683
+ * residents configured at 300k/600k — PR #79 review). The §12.2
684
+ * model/info RESULT requires it, which is why this host does not
685
+ * advertise modelInfo; hook params carry honesty-over-completeness. */
686
+ contextWindow?: number;
568
687
 
569
- /** Model capabilities (e.g., ["vision", "tools"]) */
570
- capabilities: string[];
688
+ /** Model capabilities (e.g., ["vision", "tools"]). Omitted when the
689
+ * host has no truthful source. */
690
+ capabilities?: string[];
571
691
  }
572
692
 
573
693
  /**
@@ -629,53 +749,6 @@ export interface BeforeInferenceResult {
629
749
  contextInjections: McplContextInjection[];
630
750
  }
631
751
 
632
- /**
633
- * context/afterInference params (Host → Server, Request or Notification).
634
- * Spec Section 10.5.
635
- */
636
- export interface AfterInferenceParams {
637
- /** Inference identifier (matches beforeInference) */
638
- inferenceId: string;
639
-
640
- /** Persistent across turns */
641
- conversationId: string;
642
-
643
- /** 0-indexed turn number */
644
- turnIndex: number;
645
-
646
- /** Original user input */
647
- userMessage: string | null;
648
-
649
- /** Generated assistant response */
650
- assistantMessage: string;
651
-
652
- /** Model metadata */
653
- model: McplModelInfo;
654
-
655
- /** Token usage */
656
- usage: {
657
- inputTokens: number;
658
- outputTokens: number;
659
- cacheCreationTokens?: number;
660
- cacheReadTokens?: number;
661
- };
662
- }
663
-
664
- /**
665
- * context/afterInference result (Server → Host, only for blocking hooks).
666
- * Spec Section 10.5.
667
- */
668
- export interface AfterInferenceResult {
669
- /** Feature set that provided this response */
670
- featureSet: string;
671
-
672
- /** Modified response text (replaces assistantMessage if present) */
673
- modifiedResponse?: string;
674
-
675
- /** Arbitrary metadata */
676
- metadata?: Record<string, unknown>;
677
- }
678
-
679
752
  // ============================================================================
680
753
  // Section 11 — Server-Initiated Inference
681
754
  // ============================================================================
@@ -861,7 +934,14 @@ export interface ChannelsRegisterParams {
861
934
  * channels/register result (Host → Server).
862
935
  */
863
936
  export interface ChannelsRegisterResult {
937
+ /** Legacy pre-0.5 field: ids accepted. Kept for un-migrated servers. */
864
938
  registered: string[];
939
+ /**
940
+ * §14.5 itemized results — one entry per SUBMITTED descriptor. 0.5
941
+ * servers key off this: a missing verdict is not a verdict, and a result
942
+ * without `results` is read by strict servers as accepting nothing.
943
+ */
944
+ results: Array<{ id: string; accepted: boolean; reason?: string }>;
865
945
  }
866
946
 
867
947
  /**
@@ -1033,6 +1113,8 @@ export interface ChannelIncomingMessageResult {
1033
1113
  messageId: string;
1034
1114
  accepted: boolean;
1035
1115
  conversationId?: string;
1116
+ /** §14.5: why a message was rejected (e.g. unknown/unregistered channel). */
1117
+ reason?: string;
1036
1118
  }
1037
1119
 
1038
1120
  // ============================================================================
@@ -1066,9 +1148,13 @@ export const McplMethod = {
1066
1148
  // Push events (Server → Host)
1067
1149
  PushEvent: 'push/event',
1068
1150
 
1069
- // Context hooks (Host → Server)
1151
+ // Context hooks (Host → Server). context/afterInference is gone with the
1152
+ // rest of its surface: removed from the spec in 0.5.0 (§10.5, replaced by
1153
+ // inference/lifecycle), never sent by the runtime, and not part of the
1154
+ // package's public API. Pre-0.5 SERVERS' wire handlers for it are their
1155
+ // own compatibility surface, retired on the fleet-on-0.5 evidence
1156
+ // schedule, not here.
1070
1157
  BeforeInference: 'context/beforeInference',
1071
- AfterInference: 'context/afterInference',
1072
1158
 
1073
1159
  // Server-initiated inference (Server → Host / Host → Server)
1074
1160
  InferenceRequest: 'inference/request',
@@ -1077,6 +1163,18 @@ export const McplMethod = {
1077
1163
  // Model info (Server → Host)
1078
1164
  ModelInfo: 'model/info',
1079
1165
 
1166
+ // Inference lifecycle (Host → Server, Notification) — §10.5, replaces
1167
+ // context/afterInference. Metadata only; BEST-EFFORT delivery.
1168
+ InferenceLifecycle: 'inference/lifecycle',
1169
+
1170
+ // Server manifest changes (§17). manifestChanged is S→H Notification —
1171
+ // an opaque revision plus changed domains, NO payload, deliberately
1172
+ // ungated (§17.3: gating it would silence exactly the servers whose
1173
+ // grants just narrowed). manifest is H→S Request returning the complete
1174
+ // current experimental.mcpl object, never a delta.
1175
+ ManifestChanged: 'mcpl/manifestChanged',
1176
+ Manifest: 'mcpl/manifest',
1177
+
1080
1178
  // Feature sets
1081
1179
  FeatureSetsUpdate: 'featureSets/update',
1082
1180
  FeatureSetsChanged: 'featureSets/changed',
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Refusal-reaction markers — the single source of truth for the emoji the
3
+ * framework places on a message when inference is refused (see
4
+ * AgentFramework.reactToRefusal), exported so host composition can derive
5
+ * a protective suppression baseline from the exact set the framework emits.
6
+ *
7
+ * These annotations are placed by the framework on the resident's account
8
+ * and must never re-enter a resident's context as reaction events — that is
9
+ * the self-amplifying loop behind the 8/3 Mythos incident. The Discord
10
+ * adapter suppresses them when its operator config or the host-injected
11
+ * `DISCORD_SUPPRESSED_REACTIONS_BASELINE` names them; keeping the emitted
12
+ * set and the exported baseline as one constant is what makes drift between
13
+ * "what we stamp" and "what we suppress" structurally impossible, rather
14
+ * than a promise kept in two files.
15
+ */
16
+
17
+ /** Refusal category → Discord reaction emoji. Unknown categories get the
18
+ * fallback marker. */
19
+ export const REFUSAL_REACTIONS: Readonly<Record<string, string>> = {
20
+ bio: '☣️',
21
+ chem: '🧪',
22
+ nuclear: '☢️',
23
+ cyber: '💻',
24
+ reasoning_extraction: '🧠',
25
+ };
26
+
27
+ /** Marker used when the refusal category has no dedicated emoji. */
28
+ export const REFUSAL_REACTION_FALLBACK = '🛑';
29
+
30
+ /** Every marker the framework can emit — the category map plus the
31
+ * fallback, deduplicated, in stable declaration order. This IS the
32
+ * protective baseline: host composition serializes it (comma-joined) into
33
+ * `DISCORD_SUPPRESSED_REACTIONS_BASELINE` for adapters that render
34
+ * reactions. */
35
+ export const REFUSAL_REACTION_BASELINE: readonly string[] = [
36
+ ...new Set([...Object.values(REFUSAL_REACTIONS), REFUSAL_REACTION_FALLBACK]),
37
+ ];
@@ -16,14 +16,26 @@
16
16
 
17
17
  import { safeSlice } from './safe-slice.js';
18
18
 
19
+ /**
20
+ * House-safe default inline cap (chars) for tool results, error results, and
21
+ * background-script wake payloads (issue #89: a resident should never eat a
22
+ * 42k blob they didn't ask for). Durable per-residence value comes from
23
+ * `FrameworkConfig.toolResultInlineMaxChars`; a temporary per-agent lift from
24
+ * agent_settings `tool_result_inline_max_chars`.
25
+ */
26
+ export const DEFAULT_TOOL_RESULT_INLINE_MAX_CHARS = 5000;
27
+
19
28
  export function toolResultDataToHistoryString(data: unknown, maxChars?: number): string {
20
29
  const fromArray = tryHistoryStringFromContentArray(data);
21
30
  const str = fromArray ?? JSON.stringify(data);
22
- if (maxChars && str.length > maxChars) {
23
- return safeSlice(str, 0, maxChars)
24
- + '\n\n[truncated — original was ' + str.length + ' chars]';
25
- }
26
- return str;
31
+ return maxChars ? truncateForHistory(str, maxChars) : str;
32
+ }
33
+
34
+ /** Bounded copy of an arbitrary string with the standard truncation notice. */
35
+ export function truncateForHistory(str: string, maxChars: number): string {
36
+ if (str.length <= maxChars) return str;
37
+ return safeSlice(str, 0, maxChars)
38
+ + '\n\n[truncated — original was ' + str.length + ' chars]';
27
39
  }
28
40
 
29
41
  /**
@@ -48,6 +48,17 @@ export interface AgentConfig {
48
48
  * restarts with recompiled (compressed) context. Default: 150000. */
49
49
  maxStreamTokens?: number;
50
50
 
51
+ /**
52
+ * The model's PHYSICAL context window (provider hard cap, e.g. 200000).
53
+ * When set, the framework projects each continuation round's real size
54
+ * (cache-inclusive input of the prior round + the blocks about to be
55
+ * appended + reserve for response) and restarts the stream through a fresh
56
+ * compile instead of dispatching a request the provider will 400
57
+ * (issue #92: a legal compile can walk past the physical window mid-turn).
58
+ * Unset → no projection; only the maxStreamTokens restart applies.
59
+ */
60
+ physicalWindowTokens?: number;
61
+
51
62
  /** Per-agent context compile budget (input tokens). When unset, the
52
63
  * ContextManager's built-in default (100k) applies. */
53
64
  contextBudgetTokens?: number;
@@ -122,6 +133,31 @@ export interface AgentConfig {
122
133
  * a poison turn. Default: off (a refusal just surfaces a marker + reaction).
123
134
  */
124
135
  refusalHandling?: {
136
+ /**
137
+ * Same-model retries on a content-policy refusal, performed by MEMBRANE
138
+ * at the provider seam (passed through as `refusalRetries`). Default 0.
139
+ *
140
+ * Near the classifier's threshold a refusal is probabilistic rather than a
141
+ * property of the payload: identical bytes pass and refuse minutes apart
142
+ * (mythos, 2026-07-27 replay reps). So the first response is to ask again
143
+ * — no context surgery, no fallback model. Membrane replays the SAME
144
+ * request immediately and cache-warm, so only the discarded output tokens
145
+ * are real spend.
146
+ *
147
+ * Retrying in the framework instead was tried and removed: a requeue
148
+ * recompiles, so the attempts are correlated rather than fresh draws, and
149
+ * it cannot tell a streaming consumer to drop the abandoned attempt.
150
+ * Membrane announces each retry with a `retrying` stream event, which
151
+ * driveStream handles by discarding what was streamed.
152
+ *
153
+ * Retries are spent before the framework sees the refusal at all, so the
154
+ * escalation order is: membrane retries -> autoRewind (if on) -> the
155
+ * refusal reaction, which therefore means "the border is close" rather
156
+ * than firing on every near-threshold flip.
157
+ *
158
+ * Native tool mode only — see the XML-mode warning in Membrane.
159
+ */
160
+ retries?: number;
125
161
  /** Auto-rewind the triggering turn on refusal and retry. Default false. */
126
162
  autoRewind?: boolean;
127
163
  /** Max consecutive rewinds before giving up a turn. Default 3. */
@@ -138,6 +138,23 @@ export interface FrameworkConfig {
138
138
  */
139
139
  codeExecution?: CodeExecutionConfig;
140
140
 
141
+ /**
142
+ * Durable inline cap (chars) for tool results — successes AND errors — and
143
+ * background-script wake payloads. Content over the cap is written to a
144
+ * workspace file under `tool-results/` (deterministic `<date>-<callId>.txt`
145
+ * name, overwritten on collision, retained until the workspace owner
146
+ * deletes it — never auto-GC'd) and replaced inline by a bounded preview
147
+ * plus the file reference; with no writable workspace the fallback is
148
+ * explicit plain truncation. Default 5000 (house-safe; issue #89). Must be
149
+ * >= 1000. A resident's own agent_settings value
150
+ * `tool_result_inline_max_chars` (durable, persisted in framework state)
151
+ * takes precedence over this for that agent; the EFFECTIVE cap for every
152
+ * source is min(desired, strategy bound) — desired values above the
153
+ * strategy-derived bound (maxMessageTokens * 4) are hard-clamped to it,
154
+ * with the clamp reported via agent_settings get.
155
+ */
156
+ toolResultInlineMaxChars?: number;
157
+
141
158
  /** Inference routing policy for server-initiated inference (optional). */
142
159
  inferenceRouting?: InferenceRoutingPolicy;
143
160
 
@@ -169,6 +169,19 @@ export type TraceEvent =
169
169
  agentStatus: string;
170
170
  result: unknown;
171
171
  })
172
+ | (TraceEventBase & {
173
+ /**
174
+ * A writable workspace existed but the oversized-result spill write
175
+ * failed (size cap, storeBlob failure, …) — the over-cap tail was NOT
176
+ * retained. Distinct from the no-workspace fallback, which is silent
177
+ * by design (nothing unexpected happened).
178
+ */
179
+ type: 'tool:spill_failed';
180
+ label: string;
181
+ path: string;
182
+ contentLength: number;
183
+ error: string;
184
+ })
172
185
 
173
186
  // Module lifecycle
174
187
  | (TraceEventBase & { type: 'module:added'; moduleName: string })
@@ -339,6 +352,15 @@ export type TraceEvent =
339
352
  /** True when a background reconnect loop will keep retrying. */
340
353
  willRetry: boolean;
341
354
  })
355
+ | (TraceEventBase & {
356
+ /** §17.6: the ONE normalized receipt per manifest change — closed
357
+ * host-derived impact vocabulary, never a server-authored flag. */
358
+ type: 'mcpl:manifest-change-receipt';
359
+ serverId: string;
360
+ revision: string | null;
361
+ announcedDomains: string[];
362
+ impacts: Array<{ impact: string; subject: string; disposition: string }>;
363
+ })
342
364
  | (TraceEventBase & {
343
365
  type: 'mcpl:server-reconnected';
344
366
  serverId: string;