@animalabs/agent-framework 0.7.4 → 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.
- package/dist/src/agent.d.ts +10 -0
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +33 -3
- package/dist/src/agent.js.map +1 -1
- package/dist/src/code-execution/index.d.ts +1 -1
- package/dist/src/code-execution/index.d.ts.map +1 -1
- package/dist/src/code-execution/index.js.map +1 -1
- package/dist/src/code-execution/py-runner.d.ts +23 -1
- package/dist/src/code-execution/py-runner.d.ts.map +1 -1
- package/dist/src/code-execution/py-runner.js +40 -4
- package/dist/src/code-execution/py-runner.js.map +1 -1
- package/dist/src/code-execution/runtime-py.d.ts +15 -2
- package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
- package/dist/src/code-execution/runtime-py.js +156 -7
- package/dist/src/code-execution/runtime-py.js.map +1 -1
- package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
- package/dist/src/code-execution/tool-definition.js +27 -2
- package/dist/src/code-execution/tool-definition.js.map +1 -1
- package/dist/src/framework.d.ts +234 -5
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +1818 -188
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +11 -0
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +66 -0
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/capability-grant.d.ts +115 -0
- package/dist/src/mcpl/capability-grant.d.ts.map +1 -0
- package/dist/src/mcpl/capability-grant.js +291 -0
- package/dist/src/mcpl/capability-grant.js.map +1 -0
- package/dist/src/mcpl/capability-mask.d.ts +51 -0
- package/dist/src/mcpl/capability-mask.d.ts.map +1 -0
- package/dist/src/mcpl/capability-mask.js +190 -0
- package/dist/src/mcpl/capability-mask.js.map +1 -0
- package/dist/src/mcpl/channel-registry.d.ts +30 -2
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +216 -43
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +8 -3
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js +28 -8
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/errors.d.ts +6 -0
- package/dist/src/mcpl/errors.d.ts.map +1 -1
- package/dist/src/mcpl/errors.js +6 -0
- package/dist/src/mcpl/errors.js.map +1 -1
- package/dist/src/mcpl/feature-set-manager.d.ts +10 -1
- package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
- package/dist/src/mcpl/feature-set-manager.js +45 -4
- package/dist/src/mcpl/feature-set-manager.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts +10 -14
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.js +58 -67
- package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
- package/dist/src/mcpl/index.d.ts +1 -1
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/prose-stream-router.d.ts +9 -0
- package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
- package/dist/src/mcpl/prose-stream-router.js +16 -0
- package/dist/src/mcpl/prose-stream-router.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +20 -2
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +93 -9
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +231 -32
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/server-registry.d.ts.map +1 -1
- package/dist/src/mcpl/server-registry.js +4 -3
- package/dist/src/mcpl/server-registry.js.map +1 -1
- package/dist/src/mcpl/tags.d.ts +24 -0
- package/dist/src/mcpl/tags.d.ts.map +1 -0
- package/dist/src/mcpl/tags.js +52 -0
- package/dist/src/mcpl/tags.js.map +1 -0
- package/dist/src/mcpl/transport.d.ts +1 -1
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +35 -7
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +169 -64
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js +16 -2
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +12 -0
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +26 -0
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +20 -0
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +48 -15
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/sync.d.ts +4 -0
- package/dist/src/modules/workspace/sync.d.ts.map +1 -1
- package/dist/src/modules/workspace/sync.js +1 -1
- package/dist/src/modules/workspace/sync.js.map +1 -1
- package/dist/src/refusal-reactions.d.ts +27 -0
- package/dist/src/refusal-reactions.d.ts.map +1 -0
- package/dist/src/refusal-reactions.js +35 -0
- package/dist/src/refusal-reactions.js.map +1 -0
- package/dist/src/tool-result-history.d.ts +10 -0
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +16 -5
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/types/agent.d.ts +35 -0
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +26 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +22 -0
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +24 -0
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/test/busy-agent-scheduler.test.js +1 -0
- package/dist/test/busy-agent-scheduler.test.js.map +1 -1
- package/dist/test/channel-registry-routing.test.js +38 -10
- package/dist/test/channel-registry-routing.test.js.map +1 -1
- package/dist/test/channel-registry-typing.test.js +10 -1
- package/dist/test/channel-registry-typing.test.js.map +1 -1
- package/dist/test/checkpoint-attribution.test.js +168 -8
- package/dist/test/checkpoint-attribution.test.js.map +1 -1
- package/dist/test/code-execution.test.js +327 -1
- package/dist/test/code-execution.test.js.map +1 -1
- package/dist/test/conversation-fork-injection-scoping.test.js +8 -3
- package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -1
- package/dist/test/deferred-wake-integrity.test.d.ts +29 -0
- package/dist/test/deferred-wake-integrity.test.d.ts.map +1 -0
- package/dist/test/deferred-wake-integrity.test.js +320 -0
- package/dist/test/deferred-wake-integrity.test.js.map +1 -0
- package/dist/test/event-gate-selfwake.test.d.ts +15 -0
- package/dist/test/event-gate-selfwake.test.d.ts.map +1 -0
- package/dist/test/event-gate-selfwake.test.js +125 -0
- package/dist/test/event-gate-selfwake.test.js.map +1 -0
- package/dist/test/machine-close-provenance.test.d.ts +2 -0
- package/dist/test/machine-close-provenance.test.d.ts.map +1 -0
- package/dist/test/machine-close-provenance.test.js +116 -0
- package/dist/test/machine-close-provenance.test.js.map +1 -0
- package/dist/test/mcpl-access-provider.test.d.ts +2 -0
- package/dist/test/mcpl-access-provider.test.d.ts.map +1 -0
- package/dist/test/mcpl-access-provider.test.js +20 -0
- package/dist/test/mcpl-access-provider.test.js.map +1 -0
- package/dist/test/mcpl-awareness-barrier.test.js +10 -0
- package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
- package/dist/test/mcpl-capability-grant.test.d.ts +10 -0
- package/dist/test/mcpl-capability-grant.test.d.ts.map +1 -0
- package/dist/test/mcpl-capability-grant.test.js +109 -0
- package/dist/test/mcpl-capability-grant.test.js.map +1 -0
- package/dist/test/mcpl-capability-scoping.test.d.ts +2 -0
- package/dist/test/mcpl-capability-scoping.test.d.ts.map +1 -0
- package/dist/test/mcpl-capability-scoping.test.js +266 -0
- package/dist/test/mcpl-capability-scoping.test.js.map +1 -0
- package/dist/test/mcpl-gate-roundtrip.test.js +13 -0
- package/dist/test/mcpl-gate-roundtrip.test.js.map +1 -1
- package/dist/test/mcpl-list-status.test.d.ts +2 -0
- package/dist/test/mcpl-list-status.test.d.ts.map +1 -0
- package/dist/test/mcpl-list-status.test.js +78 -0
- package/dist/test/mcpl-list-status.test.js.map +1 -0
- package/dist/test/mcpl-manifest-changed.test.d.ts +2 -0
- package/dist/test/mcpl-manifest-changed.test.d.ts.map +1 -0
- package/dist/test/mcpl-manifest-changed.test.js +177 -0
- package/dist/test/mcpl-manifest-changed.test.js.map +1 -0
- package/dist/test/mcpl-reconnect-events.test.js +27 -2
- package/dist/test/mcpl-reconnect-events.test.js.map +1 -1
- package/dist/test/mcpl-reconnect-reregistration.test.js +53 -17
- package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -1
- package/dist/test/mcpl-request-timeout.test.js +1 -1
- package/dist/test/mcpl-request-timeout.test.js.map +1 -1
- package/dist/test/module-context-integration.test.d.ts +18 -0
- package/dist/test/module-context-integration.test.d.ts.map +1 -0
- package/dist/test/module-context-integration.test.js +181 -0
- package/dist/test/module-context-integration.test.js.map +1 -0
- package/dist/test/physical-window-restart.test.d.ts +14 -0
- package/dist/test/physical-window-restart.test.d.ts.map +1 -0
- package/dist/test/physical-window-restart.test.js +222 -0
- package/dist/test/physical-window-restart.test.js.map +1 -0
- package/dist/test/present-while-acting.test.js +270 -0
- package/dist/test/present-while-acting.test.js.map +1 -1
- package/dist/test/refusal-reaction-baseline.test.d.ts +2 -0
- package/dist/test/refusal-reaction-baseline.test.d.ts.map +1 -0
- package/dist/test/refusal-reaction-baseline.test.js +74 -0
- package/dist/test/refusal-reaction-baseline.test.js.map +1 -0
- package/dist/test/state-scaling-e2e.test.js +7 -1
- package/dist/test/state-scaling-e2e.test.js.map +1 -1
- package/dist/test/state-scaling.test.js +9 -1
- package/dist/test/state-scaling.test.js.map +1 -1
- package/dist/test/tool-result-spill.test.d.ts +13 -0
- package/dist/test/tool-result-spill.test.d.ts.map +1 -0
- package/dist/test/tool-result-spill.test.js +463 -0
- package/dist/test/tool-result-spill.test.js.map +1 -0
- package/dist/test/trunk-channel-routing.test.js +45 -0
- package/dist/test/trunk-channel-routing.test.js.map +1 -1
- package/dist/test/utils-surface.test.d.ts +2 -0
- package/dist/test/utils-surface.test.d.ts.map +1 -0
- package/dist/test/utils-surface.test.js +145 -0
- package/dist/test/utils-surface.test.js.map +1 -0
- package/dist/test/workspace-read-image.test.js +71 -0
- package/dist/test/workspace-read-image.test.js.map +1 -1
- package/dist/test/workspace-restore-order.test.d.ts +16 -0
- package/dist/test/workspace-restore-order.test.d.ts.map +1 -0
- package/dist/test/workspace-restore-order.test.js +140 -0
- package/dist/test/workspace-restore-order.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/agent.ts +37 -3
- package/src/code-execution/index.ts +1 -0
- package/src/code-execution/py-runner.ts +56 -4
- package/src/code-execution/runtime-py.ts +156 -7
- package/src/code-execution/tool-definition.ts +27 -2
- package/src/framework.ts +2080 -203
- package/src/gate/event-gate.ts +72 -0
- package/src/index.ts +1 -0
- package/src/mcpl/capability-grant.ts +319 -0
- package/src/mcpl/capability-mask.ts +228 -0
- package/src/mcpl/channel-registry.ts +265 -43
- package/src/mcpl/checkpoint-manager.ts +28 -8
- package/src/mcpl/errors.ts +7 -0
- package/src/mcpl/feature-set-manager.ts +50 -5
- package/src/mcpl/hook-orchestrator.ts +64 -86
- package/src/mcpl/index.ts +0 -2
- package/src/mcpl/prose-stream-router.ts +17 -0
- package/src/mcpl/push-handler.ts +18 -2
- package/src/mcpl/server-connection.ts +262 -34
- package/src/mcpl/server-registry.ts +4 -3
- package/src/mcpl/tags.ts +50 -0
- package/src/mcpl/transport.ts +37 -7
- package/src/mcpl/types.ts +187 -78
- package/src/module-registry.ts +34 -0
- package/src/modules/workspace/index.ts +49 -15
- package/src/modules/workspace/sync.ts +1 -1
- package/src/refusal-reactions.ts +37 -0
- package/src/tool-result-history.ts +17 -5
- package/src/types/agent.ts +36 -0
- package/src/types/framework.ts +27 -0
- package/src/types/module.ts +24 -0
- 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.
|
|
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
|
-
/**
|
|
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
|
-
|
|
117
|
+
beforeInference?: boolean | {
|
|
118
|
+
observe?: boolean;
|
|
119
|
+
inject?: { system?: boolean; beforeUser?: boolean; afterUser?: boolean };
|
|
120
|
+
};
|
|
112
121
|
};
|
|
113
122
|
|
|
114
|
-
/** Server-initiated inference
|
|
115
|
-
inferenceRequest?: {
|
|
116
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
|
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
|
// ============================================================================
|
|
@@ -198,6 +213,26 @@ export interface McplServerConfig {
|
|
|
198
213
|
*/
|
|
199
214
|
token?: string;
|
|
200
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Lazily resolves the connection credential at EVERY dial — first connect
|
|
218
|
+
* and each background reconnect — overriding `token` when it returns a
|
|
219
|
+
* value (null/throw falls back to `token`). Host-attached plumbing, never
|
|
220
|
+
* serialized: the point is that credentials stay out of the agent's
|
|
221
|
+
* context entirely (the agent asks for access by name; the host fetches
|
|
222
|
+
* something fresh each redial, which also makes short-lived credentials
|
|
223
|
+
* viable where a static `token` forced long ones).
|
|
224
|
+
*/
|
|
225
|
+
accessProvider?: () => Promise<string | null>;
|
|
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
|
+
|
|
201
236
|
/** Feature sets to enable on connect */
|
|
202
237
|
enabledFeatureSets?: string[];
|
|
203
238
|
|
|
@@ -222,6 +257,37 @@ export interface McplServerConfig {
|
|
|
222
257
|
*/
|
|
223
258
|
disabledTools?: string[];
|
|
224
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
|
+
|
|
225
291
|
/** Scope configurations per feature set */
|
|
226
292
|
scopes?: Record<string, ScopeConfig>;
|
|
227
293
|
|
|
@@ -341,16 +407,51 @@ export interface FeatureSetDeclaration {
|
|
|
341
407
|
* Spec Section 6.7.
|
|
342
408
|
*/
|
|
343
409
|
export interface FeatureSetsUpdateParams {
|
|
344
|
-
/**
|
|
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). */
|
|
345
424
|
enabled?: string[];
|
|
346
425
|
|
|
347
426
|
/** Feature sets to disable */
|
|
348
427
|
disabled?: string[];
|
|
349
|
-
|
|
350
|
-
/** Scope configurations per feature set */
|
|
351
|
-
scopes?: Record<string, ScopeConfig>;
|
|
352
428
|
}
|
|
353
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
|
+
|
|
354
455
|
/**
|
|
355
456
|
* featureSets/changed params (Server → Host, Notification).
|
|
356
457
|
* Spec Section 6.7.
|
|
@@ -545,18 +646,48 @@ export interface PushEventResult {
|
|
|
545
646
|
* Distinct from membrane's ModelInfo which tracks requested vs actual model.
|
|
546
647
|
* Spec Section 10.1.
|
|
547
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
|
+
|
|
548
673
|
export interface McplModelInfo {
|
|
549
674
|
/** Model identifier (e.g., "claude-opus-4-5-20251101") */
|
|
550
675
|
id: string;
|
|
551
676
|
|
|
552
|
-
/** Model vendor (e.g., "anthropic")
|
|
553
|
-
|
|
677
|
+
/** Model vendor (e.g., "anthropic"). Omitted when the host has no
|
|
678
|
+
* truthful source — never fabricated. */
|
|
679
|
+
vendor?: string;
|
|
554
680
|
|
|
555
|
-
/** Context window size in tokens
|
|
556
|
-
|
|
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;
|
|
557
687
|
|
|
558
|
-
/** Model capabilities (e.g., ["vision", "tools"])
|
|
559
|
-
|
|
688
|
+
/** Model capabilities (e.g., ["vision", "tools"]). Omitted when the
|
|
689
|
+
* host has no truthful source. */
|
|
690
|
+
capabilities?: string[];
|
|
560
691
|
}
|
|
561
692
|
|
|
562
693
|
/**
|
|
@@ -618,53 +749,6 @@ export interface BeforeInferenceResult {
|
|
|
618
749
|
contextInjections: McplContextInjection[];
|
|
619
750
|
}
|
|
620
751
|
|
|
621
|
-
/**
|
|
622
|
-
* context/afterInference params (Host → Server, Request or Notification).
|
|
623
|
-
* Spec Section 10.5.
|
|
624
|
-
*/
|
|
625
|
-
export interface AfterInferenceParams {
|
|
626
|
-
/** Inference identifier (matches beforeInference) */
|
|
627
|
-
inferenceId: string;
|
|
628
|
-
|
|
629
|
-
/** Persistent across turns */
|
|
630
|
-
conversationId: string;
|
|
631
|
-
|
|
632
|
-
/** 0-indexed turn number */
|
|
633
|
-
turnIndex: number;
|
|
634
|
-
|
|
635
|
-
/** Original user input */
|
|
636
|
-
userMessage: string | null;
|
|
637
|
-
|
|
638
|
-
/** Generated assistant response */
|
|
639
|
-
assistantMessage: string;
|
|
640
|
-
|
|
641
|
-
/** Model metadata */
|
|
642
|
-
model: McplModelInfo;
|
|
643
|
-
|
|
644
|
-
/** Token usage */
|
|
645
|
-
usage: {
|
|
646
|
-
inputTokens: number;
|
|
647
|
-
outputTokens: number;
|
|
648
|
-
cacheCreationTokens?: number;
|
|
649
|
-
cacheReadTokens?: number;
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* context/afterInference result (Server → Host, only for blocking hooks).
|
|
655
|
-
* Spec Section 10.5.
|
|
656
|
-
*/
|
|
657
|
-
export interface AfterInferenceResult {
|
|
658
|
-
/** Feature set that provided this response */
|
|
659
|
-
featureSet: string;
|
|
660
|
-
|
|
661
|
-
/** Modified response text (replaces assistantMessage if present) */
|
|
662
|
-
modifiedResponse?: string;
|
|
663
|
-
|
|
664
|
-
/** Arbitrary metadata */
|
|
665
|
-
metadata?: Record<string, unknown>;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
752
|
// ============================================================================
|
|
669
753
|
// Section 11 — Server-Initiated Inference
|
|
670
754
|
// ============================================================================
|
|
@@ -850,7 +934,14 @@ export interface ChannelsRegisterParams {
|
|
|
850
934
|
* channels/register result (Host → Server).
|
|
851
935
|
*/
|
|
852
936
|
export interface ChannelsRegisterResult {
|
|
937
|
+
/** Legacy pre-0.5 field: ids accepted. Kept for un-migrated servers. */
|
|
853
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 }>;
|
|
854
945
|
}
|
|
855
946
|
|
|
856
947
|
/**
|
|
@@ -1022,6 +1113,8 @@ export interface ChannelIncomingMessageResult {
|
|
|
1022
1113
|
messageId: string;
|
|
1023
1114
|
accepted: boolean;
|
|
1024
1115
|
conversationId?: string;
|
|
1116
|
+
/** §14.5: why a message was rejected (e.g. unknown/unregistered channel). */
|
|
1117
|
+
reason?: string;
|
|
1025
1118
|
}
|
|
1026
1119
|
|
|
1027
1120
|
// ============================================================================
|
|
@@ -1055,9 +1148,13 @@ export const McplMethod = {
|
|
|
1055
1148
|
// Push events (Server → Host)
|
|
1056
1149
|
PushEvent: 'push/event',
|
|
1057
1150
|
|
|
1058
|
-
// 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.
|
|
1059
1157
|
BeforeInference: 'context/beforeInference',
|
|
1060
|
-
AfterInference: 'context/afterInference',
|
|
1061
1158
|
|
|
1062
1159
|
// Server-initiated inference (Server → Host / Host → Server)
|
|
1063
1160
|
InferenceRequest: 'inference/request',
|
|
@@ -1066,6 +1163,18 @@ export const McplMethod = {
|
|
|
1066
1163
|
// Model info (Server → Host)
|
|
1067
1164
|
ModelInfo: 'model/info',
|
|
1068
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
|
+
|
|
1069
1178
|
// Feature sets
|
|
1070
1179
|
FeatureSetsUpdate: 'featureSets/update',
|
|
1071
1180
|
FeatureSetsChanged: 'featureSets/changed',
|
package/src/module-registry.ts
CHANGED
|
@@ -75,6 +75,15 @@ export class ModuleRegistry {
|
|
|
75
75
|
private pushEventFn: (event: ProcessEvent) => void;
|
|
76
76
|
private onTraceFn: (listener: TraceEventListener) => () => void;
|
|
77
77
|
private callToolFn: (call: ToolCall) => Promise<ToolResult>;
|
|
78
|
+
/** Framework ops-alert channel, surfaced to modules via
|
|
79
|
+
* ModuleContext.notifyOps. Readonly-public so ModuleContextImpl can
|
|
80
|
+
* delegate without another positional constructor parameter. */
|
|
81
|
+
readonly notifyOpsFn?: (
|
|
82
|
+
kind: string,
|
|
83
|
+
agentName: string,
|
|
84
|
+
message: string,
|
|
85
|
+
data?: Record<string, unknown>,
|
|
86
|
+
) => void;
|
|
78
87
|
|
|
79
88
|
constructor(
|
|
80
89
|
store: JsStore,
|
|
@@ -89,6 +98,7 @@ export class ModuleRegistry {
|
|
|
89
98
|
pushEvent: (event: ProcessEvent) => void;
|
|
90
99
|
onTrace: (listener: TraceEventListener) => () => void;
|
|
91
100
|
callTool: (call: ToolCall) => Promise<ToolResult>;
|
|
101
|
+
notifyOps?: (kind: string, agentName: string, message: string, data?: Record<string, unknown>) => void;
|
|
92
102
|
}
|
|
93
103
|
) {
|
|
94
104
|
this.store = store;
|
|
@@ -102,6 +112,7 @@ export class ModuleRegistry {
|
|
|
102
112
|
this.pushEventFn = options.pushEvent;
|
|
103
113
|
this.onTraceFn = options.onTrace;
|
|
104
114
|
this.callToolFn = options.callTool;
|
|
115
|
+
this.notifyOpsFn = options.notifyOps;
|
|
105
116
|
}
|
|
106
117
|
|
|
107
118
|
/**
|
|
@@ -230,6 +241,25 @@ export class ModuleRegistry {
|
|
|
230
241
|
return tools;
|
|
231
242
|
}
|
|
232
243
|
|
|
244
|
+
/**
|
|
245
|
+
* All utilities from all modules, namespaced exactly like tools
|
|
246
|
+
* (`module--name`). See Module.getUtilities — these are deliberately kept
|
|
247
|
+
* out of the model-facing tool list and surfaced through the framework's
|
|
248
|
+
* `utils` meta-tool; dispatch goes through the same handleToolCall.
|
|
249
|
+
*/
|
|
250
|
+
getAllUtilities(): ToolDefinition[] {
|
|
251
|
+
const utilities: ToolDefinition[] = [];
|
|
252
|
+
for (const module of this.modules.values()) {
|
|
253
|
+
for (const util of module.getUtilities?.() ?? []) {
|
|
254
|
+
utilities.push({
|
|
255
|
+
...util,
|
|
256
|
+
name: `${module.name}--${util.name}`,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return utilities;
|
|
261
|
+
}
|
|
262
|
+
|
|
233
263
|
/**
|
|
234
264
|
* Handle a tool call by routing to the appropriate module.
|
|
235
265
|
*/
|
|
@@ -556,6 +586,10 @@ class ModuleContextImpl implements ModuleContext {
|
|
|
556
586
|
return this.callToolFn(call);
|
|
557
587
|
}
|
|
558
588
|
|
|
589
|
+
notifyOps(kind: string, agentName: string, message: string, data?: Record<string, unknown>): void {
|
|
590
|
+
this.registry.notifyOpsFn?.(kind, agentName, message, data);
|
|
591
|
+
}
|
|
592
|
+
|
|
559
593
|
addMessage(
|
|
560
594
|
participant: string,
|
|
561
595
|
content: ContentBlock[],
|
|
@@ -34,7 +34,7 @@ import type {
|
|
|
34
34
|
} from './types.js';
|
|
35
35
|
import { WORKSPACE_FS_EVENT_TYPES, opToEventType } from './types.js';
|
|
36
36
|
import { MountWatcher, type FsChange } from './watcher.js';
|
|
37
|
-
import { syncFromFs, materializeToFs, hashContent, DEFAULT_MAX_FILE_SIZE, type ConflictInfo } from './sync.js';
|
|
37
|
+
import { syncFromFs, materializeToFs, hashContent, isBinary, DEFAULT_MAX_FILE_SIZE, type ConflictInfo } from './sync.js';
|
|
38
38
|
|
|
39
39
|
export type {
|
|
40
40
|
WorkspaceConfig,
|
|
@@ -525,6 +525,11 @@ export class WorkspaceModule implements Module {
|
|
|
525
525
|
private config: WorkspaceConfig;
|
|
526
526
|
private mounts = new Map<string, MountState>();
|
|
527
527
|
private watchers = new Map<string, MountWatcher>();
|
|
528
|
+
/** Persisted state decoded in start(), held until mounts exist to apply it
|
|
529
|
+
* to — in the Host ordering start() runs before initStore() creates the
|
|
530
|
+
* mounts, so restoration must be second-callback-safe like watcher
|
|
531
|
+
* startup already is (issue #72). Cleared after application. */
|
|
532
|
+
private savedState: WorkspaceModuleState | null = null;
|
|
528
533
|
|
|
529
534
|
constructor(config: WorkspaceConfig) {
|
|
530
535
|
// Detect overlapping mount paths: if mount A contains mount B,
|
|
@@ -593,32 +598,47 @@ export class WorkspaceModule implements Module {
|
|
|
593
598
|
this.mounts.set(mount.name, mountState);
|
|
594
599
|
}
|
|
595
600
|
|
|
601
|
+
this.applySavedState();
|
|
596
602
|
this.ensureRunning();
|
|
597
603
|
}
|
|
598
604
|
|
|
599
605
|
async start(ctx: ModuleContext): Promise<void> {
|
|
600
606
|
this.ctx = ctx;
|
|
601
607
|
|
|
602
|
-
//
|
|
608
|
+
// Decode persisted state if restarting; application waits until mounts
|
|
609
|
+
// exist (see applySavedState) — in the Host ordering they don't yet.
|
|
603
610
|
if (ctx.isRestart) {
|
|
604
|
-
|
|
605
|
-
if (saved) {
|
|
606
|
-
for (const [name, meta] of Object.entries(saved.mounts)) {
|
|
607
|
-
const mount = this.mounts.get(name);
|
|
608
|
-
if (mount) {
|
|
609
|
-
mount.lastMaterializedSeq = meta.lastMaterializedSeq;
|
|
610
|
-
mount.lastMaterializedBranchId = meta.lastMaterializedBranchId ?? null;
|
|
611
|
-
// watcherReadyAt intentionally not restored — each session must
|
|
612
|
-
// observe its own watcher attach, otherwise a stale timestamp
|
|
613
|
-
// would hide a new-session attach failure.
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
611
|
+
this.savedState = ctx.getState<WorkspaceModuleState>() ?? null;
|
|
617
612
|
}
|
|
618
613
|
|
|
614
|
+
this.applySavedState();
|
|
619
615
|
this.ensureRunning();
|
|
620
616
|
}
|
|
621
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Apply persisted per-mount state once both halves of the lifecycle have
|
|
620
|
+
* happened. Like watcher startup, either callback may fire second, so both
|
|
621
|
+
* start() and initStore() call this; it runs to effect exactly once — the
|
|
622
|
+
* saved payload is cleared after application so stale persisted values can
|
|
623
|
+
* never overwrite newer runtime state (e.g. a materialization that already
|
|
624
|
+
* happened this session).
|
|
625
|
+
*/
|
|
626
|
+
private applySavedState(): void {
|
|
627
|
+
if (!this.savedState || this.mounts.size === 0) return;
|
|
628
|
+
|
|
629
|
+
for (const [name, meta] of Object.entries(this.savedState.mounts)) {
|
|
630
|
+
const mount = this.mounts.get(name);
|
|
631
|
+
if (mount) {
|
|
632
|
+
mount.lastMaterializedSeq = meta.lastMaterializedSeq;
|
|
633
|
+
mount.lastMaterializedBranchId = meta.lastMaterializedBranchId ?? null;
|
|
634
|
+
// watcherReadyAt intentionally not restored — each session must
|
|
635
|
+
// observe its own watcher attach, otherwise a stale timestamp
|
|
636
|
+
// would hide a new-session attach failure.
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
this.savedState = null;
|
|
640
|
+
}
|
|
641
|
+
|
|
622
642
|
/**
|
|
623
643
|
* Start chokidar watchers and emit workspace:mounted events.
|
|
624
644
|
* Idempotent: safe to call from both start() and initStore(), fires once
|
|
@@ -725,6 +745,15 @@ export class WorkspaceModule implements Module {
|
|
|
725
745
|
this.ctx = null;
|
|
726
746
|
}
|
|
727
747
|
|
|
748
|
+
/** Mount names + modes, for peer callers (framework spill/journal paths)
|
|
749
|
+
* that need a writable mount without reaching into private state. */
|
|
750
|
+
getMounts(): Array<{ name: string; mode: 'read-write' | 'read-only' }> {
|
|
751
|
+
return [...this.mounts.values()].map((m) => ({
|
|
752
|
+
name: m.config.name,
|
|
753
|
+
mode: m.config.mode === 'read-only' ? 'read-only' : 'read-write',
|
|
754
|
+
}));
|
|
755
|
+
}
|
|
756
|
+
|
|
728
757
|
// ==========================================================================
|
|
729
758
|
// Tool Definitions
|
|
730
759
|
// ==========================================================================
|
|
@@ -1308,6 +1337,11 @@ export class WorkspaceModule implements Module {
|
|
|
1308
1337
|
if (!fileStat.isFile() || fileStat.size > maxSize) return;
|
|
1309
1338
|
|
|
1310
1339
|
const buffer = await readFile(absolutePath);
|
|
1340
|
+
// Binaries are never synced into the tree (mirrors syncFromFs): the old
|
|
1341
|
+
// utf-8 round-trip here silently replaced non-UTF-8 bytes with U+FFFD,
|
|
1342
|
+
// permanently mangling images in the blob store. Binary reads are served
|
|
1343
|
+
// straight from disk (readImageFromFilesystem / read_image fallback).
|
|
1344
|
+
if (isBinary(buffer)) return;
|
|
1311
1345
|
const content = buffer.toString('utf-8');
|
|
1312
1346
|
const blobHash = store.storeBlob(Buffer.from(content, 'utf-8'), 'text/plain');
|
|
1313
1347
|
|
|
@@ -40,7 +40,7 @@ export function hashContent(content: string | Buffer): string {
|
|
|
40
40
|
/**
|
|
41
41
|
* Check if content appears to be binary.
|
|
42
42
|
*/
|
|
43
|
-
function isBinary(buffer: Buffer): boolean {
|
|
43
|
+
export function isBinary(buffer: Buffer): boolean {
|
|
44
44
|
// Check for null bytes in first 8KB
|
|
45
45
|
const check = buffer.subarray(0, 8192);
|
|
46
46
|
for (let i = 0; i < check.length; i++) {
|
|
@@ -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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
/**
|
package/src/types/agent.ts
CHANGED
|
@@ -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. */
|