@animalabs/agent-framework 0.33.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 +136 -8
- package/dist/src/agent.d.ts +25 -4
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +68 -13
- package/dist/src/agent.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/context-options.d.ts +9 -6
- package/dist/src/context-options.d.ts.map +1 -1
- package/dist/src/context-options.js +13 -8
- package/dist/src/context-options.js.map +1 -1
- package/dist/src/cut-off-round.js +4 -4
- package/dist/src/dendrite/registry.d.ts.map +1 -1
- package/dist/src/dendrite/registry.js +4 -2
- package/dist/src/dendrite/registry.js.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/framework.d.ts +94 -30
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +987 -284
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +40 -6
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +66 -25
- package/dist/src/gate/event-gate.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/suppression-review.js +2 -2
- package/dist/src/gate/suppression-review.js.map +1 -1
- package/dist/src/gate/types.d.ts +7 -0
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/heartbeat/heartbeat.js +1 -1
- package/dist/src/heartbeat/heartbeat.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/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/mcpl/channel-registry.d.ts +57 -13
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +136 -24
- package/dist/src/mcpl/channel-registry.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/server-connection.d.ts +37 -18
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +31 -16
- package/dist/src/mcpl/server-connection.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 +25 -4
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/modules/discord/index.js +1 -1
- 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/semantic.js +3 -3
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +4 -3
- 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 +10 -8
- package/dist/src/notice-tags.d.ts.map +1 -1
- package/dist/src/notice-tags.js +10 -8
- package/dist/src/notice-tags.js.map +1 -1
- package/dist/src/presentation.d.ts +2 -1
- package/dist/src/presentation.d.ts.map +1 -1
- package/dist/src/presentation.js +4 -2
- package/dist/src/presentation.js.map +1 -1
- 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.map +1 -1
- package/dist/src/recovery/offline-branch.js +5 -4
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/recovery/recover-cli.js +2 -1
- package/dist/src/recovery/recover-cli.js.map +1 -1
- package/dist/src/sent-rows.js +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/tune-out/coordinator.d.ts +13 -4
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +33 -16
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/tune-out/tools.js +2 -1
- package/dist/src/tune-out/tools.js.map +1 -1
- package/dist/src/types/agent.d.ts +39 -5
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +43 -8
- 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/trace.d.ts +64 -4
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/agent.ts +71 -14
- package/src/append-all.ts +6 -0
- package/src/context-options.ts +18 -8
- package/src/cut-off-round.ts +4 -4
- package/src/dendrite/registry.ts +4 -2
- package/src/failure-text.ts +88 -0
- package/src/framework.ts +1018 -293
- package/src/gate/event-gate.ts +90 -26
- package/src/gate/reply-judge.ts +12 -3
- package/src/gate/suppression-review.ts +2 -2
- package/src/gate/types.ts +7 -0
- package/src/heartbeat/heartbeat.ts +1 -1
- package/src/index.ts +1 -0
- package/src/inference-bounds.ts +11 -6
- package/src/mcpl/channel-registry.ts +160 -31
- package/src/mcpl/modern-connection.ts +629 -91
- package/src/mcpl/protocol-family.ts +15 -9
- package/src/mcpl/server-connection.ts +28 -15
- 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 +25 -4
- package/src/modules/discord/index.ts +1 -1
- package/src/modules/health/index.ts +2 -2
- package/src/modules/history/semantic.ts +3 -3
- package/src/modules/workspace/index.ts +4 -3
- package/src/modules/workspace/observe.ts +2 -1
- package/src/modules/workspace/reconcile.ts +3 -2
- package/src/notice-tags.ts +11 -8
- package/src/presentation.ts +4 -2
- package/src/reasoning-loss.ts +7 -0
- package/src/recovery/offline-branch.ts +5 -4
- package/src/recovery/recover-cli.ts +2 -1
- package/src/sent-rows.ts +1 -1
- package/src/supplied-text.ts +3 -3
- package/src/thinking-drops.ts +2 -0
- package/src/tool-presentation.ts +34 -9
- package/src/tune-out/coordinator.ts +32 -15
- package/src/tune-out/tools.ts +2 -1
- package/src/types/agent.ts +36 -5
- package/src/types/framework.ts +44 -8
- package/src/types/inference-log.ts +1 -0
- package/src/types/trace.ts +66 -4
|
@@ -6,10 +6,14 @@
|
|
|
6
6
|
* - **Legacy**: MCP 2024-11-05, with MCPL riding on it as an experimental
|
|
7
7
|
* capability. `McplServerConnection` speaks it, over stdio or MCPL's
|
|
8
8
|
* WebSocket binding.
|
|
9
|
-
* - **Modern**:
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
9
|
+
* - **Modern**: the official SDK's client, which `ModernMcpConnection` drives.
|
|
10
|
+
* Over stdio it speaks MCP 2026-07-28, which is stateless. That revision
|
|
11
|
+
* has no `initialize`: every request carries its version and client
|
|
12
|
+
* capabilities in `_meta`, and server-to-client requests are replaced by
|
|
13
|
+
* in-band continuation. Over Streamable HTTP the revision is negotiated at
|
|
14
|
+
* each connect, from {@link MODERN_HTTP_PROTOCOL_VERSIONS}: 2026-07-28
|
|
15
|
+
* when the server offers it, and otherwise a 2025 revision, with its
|
|
16
|
+
* `initialize` handshake and its session.
|
|
13
17
|
*
|
|
14
18
|
* A URL's scheme decides a network server's family: `ws://`/`wss://` is
|
|
15
19
|
* legacy, `http://`/`https://` is modern, because neither family has the
|
|
@@ -17,10 +21,10 @@
|
|
|
17
21
|
* unless its config says `protocol: 'modern'`, so every existing stdio
|
|
18
22
|
* configuration opens exactly as before.
|
|
19
23
|
*
|
|
20
|
-
* Why
|
|
21
|
-
* real MCPL servers exit (heartbeat-mcpl closes on any first
|
|
22
|
-
* isn't `initialize`), the SDK's stdio probe launches the server
|
|
23
|
-
* server's era is a stable fact its configuration can state.
|
|
24
|
+
* Why a stdio server's era isn't detected: the modern spec's discover-first
|
|
25
|
+
* probe makes some real MCPL servers exit (heartbeat-mcpl closes on any first
|
|
26
|
+
* message that isn't `initialize`), the SDK's stdio probe launches the server
|
|
27
|
+
* twice, and a server's era is a stable fact its configuration can state.
|
|
24
28
|
*/
|
|
25
29
|
import { NOTICE_TAG } from '../notice-tags.js';
|
|
26
30
|
import { DENY_BY_DEFAULT, capabilityPatternMatches } from './capability-grant.js';
|
|
@@ -31,8 +35,10 @@ import { kindOf } from '../config-checks.js';
|
|
|
31
35
|
|
|
32
36
|
/** The one revision the legacy engine offers and accepts. */
|
|
33
37
|
export const LEGACY_MCP_PROTOCOL_VERSION = '2024-11-05';
|
|
34
|
-
/** The revision the modern engine pins. */
|
|
38
|
+
/** The revision the modern engine pins over stdio, and prefers over HTTP. */
|
|
35
39
|
export const MODERN_MCP_PROTOCOL_VERSION = '2026-07-28';
|
|
40
|
+
/** 2024-11-05 isn't offered: its HTTP binding was HTTP+SSE, another transport. */
|
|
41
|
+
export const MODERN_HTTP_PROTOCOL_VERSIONS: readonly string[] = [MODERN_MCP_PROTOCOL_VERSION, '2025-11-25', '2025-06-18', '2025-03-26'];
|
|
36
42
|
|
|
37
43
|
export type McpProtocolFamily = 'legacy' | 'modern';
|
|
38
44
|
export type McpTransportKind = 'stdio' | 'websocket' | 'http';
|
|
@@ -157,20 +157,27 @@ const UNSUPPORTED_PROTOCOL_VERSION_CODE = -32022;
|
|
|
157
157
|
const MAX_TOOL_LIST_PAGES = 100;
|
|
158
158
|
|
|
159
159
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* revision
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
160
|
+
* This engine and the server could not negotiate a protocol revision, so the
|
|
161
|
+
* connect that throws it established no connection. Usually they share none. Over HTTP it
|
|
162
|
+
* can also be the SDK's own refusal: after a -32022 whose list names a
|
|
163
|
+
* revision from 2026-07-28 on that this client doesn't speak, the SDK
|
|
164
|
+
* negotiates no further, even when the list also names a 2025 revision both
|
|
165
|
+
* speak. This is a protocol-negotiation fact, not a request outcome.
|
|
166
|
+
* Retrying cannot change it: each engine offers the same revisions every
|
|
167
|
+
* time (`offeredRevisions`), and only configuration, or the server's
|
|
168
|
+
* operator, can fix the mismatch. A connection that gets this error does not
|
|
169
|
+
* schedule reconnects. The legacy engine and the modern one
|
|
170
|
+
* (ModernMcpConnection) both throw it, so its readers treat them alike.
|
|
167
171
|
*
|
|
168
|
-
* - `rejected`: the server refused the offered
|
|
169
|
-
* `initialize` answered with error -32022
|
|
170
|
-
* or error
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
172
|
+
* - `rejected`: the server refused the revision offered. The legacy
|
|
173
|
+
* engine's `initialize` was answered with error -32022. A modern server's
|
|
174
|
+
* discovery or -32022 error named no revision in common (over HTTP, a
|
|
175
|
+
* discovery's list counts once the server has refused the `initialize`
|
|
176
|
+
* tried after it too), or, over HTTP, named one this client doesn't speak,
|
|
177
|
+
* as above. `supported` is the server's own list, when it gave one.
|
|
178
|
+
* - `mismatch`: the server answered `initialize` with a revision that
|
|
179
|
+
* handshake doesn't accept, or with none: the legacy engine's, or a modern
|
|
180
|
+
* connection's 2025 handshake over HTTP.
|
|
174
181
|
*/
|
|
175
182
|
export class McplProtocolVersionError extends Error {
|
|
176
183
|
constructor(
|
|
@@ -185,9 +192,15 @@ export class McplProtocolVersionError extends Error {
|
|
|
185
192
|
* redacted (`rejected`). */
|
|
186
193
|
readonly code?: number,
|
|
187
194
|
readonly data?: unknown,
|
|
188
|
-
/** The revision this client offered
|
|
189
|
-
* modern connection's
|
|
195
|
+
/** The revision this client offered first, in its order of preference:
|
|
196
|
+
* the legacy engine's (2024-11-05), a modern connection's over stdio
|
|
197
|
+
* (2026-07-28), or over HTTP 2026-07-28, the first of
|
|
198
|
+
* `offeredRevisions`. */
|
|
190
199
|
readonly offered: string = MCP_PROTOCOL_VERSION,
|
|
200
|
+
/** Every revision this client offered, in its order of preference: the
|
|
201
|
+
* legacy engine's one, a modern connection's one over stdio, or its
|
|
202
|
+
* list over HTTP. */
|
|
203
|
+
readonly offeredRevisions: readonly string[] = [offered],
|
|
191
204
|
) {
|
|
192
205
|
super(message);
|
|
193
206
|
this.name = 'McplProtocolVersionError';
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { objectSignature, renderToolLines, toolDigest, type HintedTool, type Shown } from './tool-signature.js';
|
|
29
29
|
import { nameToTypeBack, quotedValue, suppliedValue } from '../supplied-text.js';
|
|
30
|
+
import { appendAll } from '../append-all.js';
|
|
30
31
|
|
|
31
32
|
export interface ToolsetDecl {
|
|
32
33
|
title?: string;
|
|
@@ -632,7 +633,7 @@ export class ToolPresence {
|
|
|
632
633
|
continue;
|
|
633
634
|
}
|
|
634
635
|
const title = (c.opened || c.retitled) && !bare ? c.title : undefined;
|
|
635
|
-
if (showsBlock(c)) lines
|
|
636
|
+
if (showsBlock(c)) appendAll(lines, this.toolBlock(c.key, c.now, { headed: !bare, title, seen, describeAll: opts.describeAll }));
|
|
636
637
|
else if (title) lines.push(`${this.label(c.key)} ${suppliedValue(title)}`);
|
|
637
638
|
if (c.gone.length) lines.push(`now unavailable: ${c.gone.map(suppliedValue).join(', ')}`);
|
|
638
639
|
if (c.opened && !bare) {
|
|
@@ -641,7 +642,8 @@ export class ToolPresence {
|
|
|
641
642
|
}
|
|
642
643
|
}
|
|
643
644
|
for (const s of cs.suggest) lines.push(this.renderSuggestion(s));
|
|
644
|
-
lines
|
|
645
|
+
appendAll(lines, this.untoldNotes(cs));
|
|
646
|
+
appendAll(lines, cs.notes);
|
|
645
647
|
return lines.join('\n');
|
|
646
648
|
}
|
|
647
649
|
|
|
@@ -663,7 +665,7 @@ export class ToolPresence {
|
|
|
663
665
|
if (!v.size) return null;
|
|
664
666
|
const lines: string[] = [];
|
|
665
667
|
for (const [key, e] of v) {
|
|
666
|
-
lines
|
|
668
|
+
appendAll(lines, this.toolBlock(key, [...e.tools.values()], { describeAll: true, title: e.title }));
|
|
667
669
|
const close = this.closeHint(key);
|
|
668
670
|
if (close) lines.push(` ${close} closes it.`);
|
|
669
671
|
}
|
|
@@ -679,7 +681,7 @@ export class ToolPresence {
|
|
|
679
681
|
const v = this.view();
|
|
680
682
|
if (v.size) {
|
|
681
683
|
for (const [key, e] of v) {
|
|
682
|
-
lines
|
|
684
|
+
appendAll(lines, this.toolBlock(key, [...e.tools.values()], { describeAll: true, title: e.title, seen: opts.mark === false ? new Set<string>() : undefined }).map((l) => ` ${l}`));
|
|
683
685
|
const close = this.closeHint(key);
|
|
684
686
|
if (close) lines.push(` ${close} closes it.`);
|
|
685
687
|
}
|
|
@@ -700,7 +702,10 @@ export class ToolPresence {
|
|
|
700
702
|
const names = this.allTools.filter((t) => this.toolsetOf(t.name) === g).map((t) => t.name);
|
|
701
703
|
openable.push(` ${nameToTypeBack(g)}: ${names.map(nameToTypeBack).join(', ')}`);
|
|
702
704
|
}
|
|
703
|
-
if (openable.length)
|
|
705
|
+
if (openable.length) {
|
|
706
|
+
lines.push(`can open — ${this.names.open} {server:"${this.server}", toolset, instance?, options?}:`);
|
|
707
|
+
appendAll(lines, openable);
|
|
708
|
+
}
|
|
704
709
|
return lines.join('\n');
|
|
705
710
|
}
|
|
706
711
|
|
package/src/mcpl/toolset-host.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
type ChangeSet, type MetaToolNames, type Presence,
|
|
36
36
|
} from './tool-presence.js';
|
|
37
37
|
import type { HintedTool } from './tool-signature.js';
|
|
38
|
+
import { appendAll } from '../append-all.js';
|
|
38
39
|
|
|
39
40
|
/** The meta-tools' module, whose tools are `toolsets--open` and so on. */
|
|
40
41
|
export const TOOLSETS_MODULE = 'toolsets';
|
|
@@ -246,7 +247,7 @@ export class ToolsetController {
|
|
|
246
247
|
const toolset = typeof res.toolset === 'string' ? res.toolset : String(params.toolset ?? '');
|
|
247
248
|
const instance = typeof res.instance === 'string' && res.instance ? res.instance : typeof params.instance === 'string' ? params.instance : undefined;
|
|
248
249
|
this.presence.noteOpened(toolset, instance, typeof res.title === 'string' && res.title ? res.title : undefined);
|
|
249
|
-
cs.changes
|
|
250
|
+
appendAll(cs.changes, this.presence.diff(before, this.presence.view()).changes);
|
|
250
251
|
break;
|
|
251
252
|
}
|
|
252
253
|
case 'toolsets/close': {
|
|
@@ -255,7 +256,7 @@ export class ToolsetController {
|
|
|
255
256
|
cs = this.applyHints(metaHints(res), 'toolsets/close');
|
|
256
257
|
const before = this.presence.view();
|
|
257
258
|
this.presence.noteClosed(String(params.toolset ?? ''), typeof params.instance === 'string' ? params.instance : undefined);
|
|
258
|
-
cs.changes
|
|
259
|
+
appendAll(cs.changes, this.presence.diff(before, this.presence.view()).changes);
|
|
259
260
|
break;
|
|
260
261
|
}
|
|
261
262
|
default:
|
|
@@ -298,7 +299,7 @@ export class ToolsetController {
|
|
|
298
299
|
this.scheduleRecovery();
|
|
299
300
|
}
|
|
300
301
|
const cs = this.presence.diff(before, this.presence.view());
|
|
301
|
-
cs.suggest
|
|
302
|
+
appendAll(cs.suggest, r.suggest);
|
|
302
303
|
return cs;
|
|
303
304
|
}
|
|
304
305
|
|
|
@@ -332,11 +333,11 @@ export class ToolsetController {
|
|
|
332
333
|
this.scheduleRecovery();
|
|
333
334
|
}
|
|
334
335
|
const cs = this.presence.diff(before, this.presence.view());
|
|
335
|
-
cs.suggest
|
|
336
|
+
appendAll(cs.suggest, suggest);
|
|
336
337
|
// A lost toolset gets its note, not also a "closed" line.
|
|
337
338
|
cs.changes = cs.changes.filter((c) => !lost.some((o) => keyOf(o.toolset, o.instance) === c.key));
|
|
338
339
|
const told = this.presence.adoptionNotes(lost, ended);
|
|
339
|
-
cs.notes
|
|
340
|
+
appendAll(cs.notes, told.notes);
|
|
340
341
|
if (told.ended.length) cs.ended = [...(cs.ended ?? []), ...told.ended];
|
|
341
342
|
return cs;
|
|
342
343
|
}
|
|
@@ -625,7 +626,7 @@ export class ToolsetController {
|
|
|
625
626
|
const moved = this.presence.applyMoves(moves);
|
|
626
627
|
if (!moved.length) return;
|
|
627
628
|
const cs = emptyChange();
|
|
628
|
-
cs.notes
|
|
629
|
+
appendAll(cs.notes, moved.map(({ name, to }) => to === 'dynamic'
|
|
629
630
|
? `${nameToTypeBack(name)} left your tool list: it's announced when available, and called with ${META_TOOL_NAMES.act} {server:"${this.serverId}", tool:${quotedValue(name)}, args}.`
|
|
630
631
|
: `${nameToTypeBack(name)} is in your tool list now, as ${nameToTypeBack(this.deps.listedName(this.serverId, name))}.`));
|
|
631
632
|
this.deliver(cs);
|
|
@@ -32,6 +32,7 @@ import { keyOf, type ChangeSet, type ToolPresence } from './tool-presence.js';
|
|
|
32
32
|
import { checkArgs, jsonText, renderToolDefinition, type HintedTool } from './tool-signature.js';
|
|
33
33
|
import { McplRequestError } from './server-connection.js';
|
|
34
34
|
import { BUILTIN_TOOLSET, type HostPresentation } from '../host-presentation.js';
|
|
35
|
+
import { appendAll } from '../append-all.js';
|
|
35
36
|
|
|
36
37
|
/** The namespace a reminder's stored injection carries. */
|
|
37
38
|
export const TOOLSETS_REMINDER_NAMESPACE = 'mcpl-toolsets';
|
|
@@ -516,7 +517,7 @@ export class ToolsetsModule implements Module {
|
|
|
516
517
|
return said;
|
|
517
518
|
})
|
|
518
519
|
: [];
|
|
519
|
-
lines
|
|
520
|
+
appendAll(lines, unread);
|
|
520
521
|
return lines.length ? textResult(`${result.data}\n${lines.join('\n')}`) : result;
|
|
521
522
|
}
|
|
522
523
|
|
package/src/mcpl/types.ts
CHANGED
|
@@ -267,8 +267,9 @@ export interface McplServerConfig {
|
|
|
267
267
|
* - `ws://` or `wss://` is a legacy MCPL server over MCPL's WebSocket
|
|
268
268
|
* binding. The host dials it and runs the same newline-delimited JSON-RPC
|
|
269
269
|
* as over stdio.
|
|
270
|
-
* - `http://` or `https://` is a modern MCP
|
|
271
|
-
*
|
|
270
|
+
* - `http://` or `https://` is a modern MCP server over Streamable HTTP.
|
|
271
|
+
* Its revision is negotiated at each connect: 2026-07-28 when the server
|
|
272
|
+
* offers it, and otherwise 2025-11-25, 2025-06-18 or 2025-03-26.
|
|
272
273
|
*/
|
|
273
274
|
url?: string;
|
|
274
275
|
|
|
@@ -428,14 +429,34 @@ export interface McplServerConfig {
|
|
|
428
429
|
* Only `true` turns it on, and null counts as not set. `create()`,
|
|
429
430
|
* `connectMcplServer` and `restartMcplServer` refuse any other value, such
|
|
430
431
|
* as a quoted "false".
|
|
432
|
+
*
|
|
433
|
+
* A modern server reached over HTTP at a 2025 revision can hold a session
|
|
434
|
+
* for this client. When the server ends it (HTTP 404), or stops serving its
|
|
435
|
+
* revision or its streams as a server upgraded to 2026-07-28 does, after it
|
|
436
|
+
* has answered a request, a new session starts at once whether or not this
|
|
437
|
+
* is set: the connection emits 'close' while its `willReconnect` reads
|
|
438
|
+
* true, then 'reconnect'. If that new session can't start, this setting
|
|
439
|
+
* decides, as for any lost connection, and a revision verdict stops it for
|
|
440
|
+
* good, as on any reconnect; without this setting, a second 'close', while
|
|
441
|
+
* `willReconnect` reads false, says the server is gone. An answered session
|
|
442
|
+
* that ends less than `reconnectIntervalMs` after it started gets a new
|
|
443
|
+
* one at once only if the last session restarted that way had lived that
|
|
444
|
+
* long, or there was none. A second such young end in a row, and a session
|
|
445
|
+
* that ends before it has answered, count as a failed connect, and follow
|
|
446
|
+
* this setting.
|
|
431
447
|
*/
|
|
432
448
|
reconnect?: boolean;
|
|
433
449
|
|
|
434
450
|
/**
|
|
435
451
|
* Base interval between reconnection attempts in milliseconds.
|
|
436
452
|
* The actual delay doubles with each consecutive failure (with ±25% jitter)
|
|
437
|
-
* up to `reconnectMaxIntervalMs`, and resets after a successful handshake
|
|
438
|
-
*
|
|
453
|
+
* up to `reconnectMaxIntervalMs`, and resets after a successful handshake
|
|
454
|
+
* (for a modern server's 2025 session over HTTP, once that session's end
|
|
455
|
+
* starts a new one at once). Over HTTP the same backoff paces a modern
|
|
456
|
+
* server's streams: a dropped stream's first try waits the server's
|
|
457
|
+
* `retry` hint when it sent one above 0, and the backoff's first step
|
|
458
|
+
* otherwise, and a try after a failed one waits the longer of the hint
|
|
459
|
+
* and the doubling backoff. Default: 5000 (5 seconds).
|
|
439
460
|
*/
|
|
440
461
|
reconnectIntervalMs?: number;
|
|
441
462
|
|
|
@@ -1172,7 +1172,7 @@ export class DiscordModule implements Module {
|
|
|
1172
1172
|
// boundary whose tied millisecond must be preserved.
|
|
1173
1173
|
const fetchedCompleteHistory = discordMessages.length < scrollback;
|
|
1174
1174
|
const oldestFetchedTimestamp = discordMessages.length > 0
|
|
1175
|
-
?
|
|
1175
|
+
? discordMessages.reduce((min, msg) => Math.min(min, msg.timestamp.getTime()), Infinity)
|
|
1176
1176
|
: undefined;
|
|
1177
1177
|
for (const [externalId, rows] of storedMessageMap) {
|
|
1178
1178
|
const stored = rows[0]!;
|
|
@@ -188,7 +188,7 @@ export class HealthModule implements Module {
|
|
|
188
188
|
timestamp: safeZoned(s.timestamp),
|
|
189
189
|
agentName: s.agentName,
|
|
190
190
|
success: s.success,
|
|
191
|
-
error: s.
|
|
191
|
+
error: s.errorWords,
|
|
192
192
|
requestId: s.requestId,
|
|
193
193
|
tokenUsage: s.tokenUsage,
|
|
194
194
|
};
|
|
@@ -201,7 +201,7 @@ export class HealthModule implements Module {
|
|
|
201
201
|
.map((s) => ({
|
|
202
202
|
timestamp: s.timestamp,
|
|
203
203
|
agentName: s.agentName,
|
|
204
|
-
error: typeof s.error === 'string' ? safeSlice(s.error, 0, 400) :
|
|
204
|
+
error: typeof s.error === 'string' ? safeSlice(s.error, 0, 400) : null,
|
|
205
205
|
requestId: s.requestId,
|
|
206
206
|
}));
|
|
207
207
|
|
|
@@ -880,14 +880,14 @@ export class SemanticIndexer {
|
|
|
880
880
|
// The current branch may then have items this pass didn't send, and
|
|
881
881
|
// old items a failed delete left are still to go.
|
|
882
882
|
if (left || oldItemsOwed) report.more = true;
|
|
883
|
-
this.consecutiveFailures = 0; this.lastError = named(notStats?.message ?? null, ...phaseErrors); this.lastSyncAt = Date.now();
|
|
883
|
+
this.consecutiveFailures = 0; this.lastError = named([notStats?.message ?? null, ...phaseErrors]); this.lastSyncAt = Date.now();
|
|
884
884
|
return report;
|
|
885
885
|
} catch (e) {
|
|
886
886
|
this.consecutiveFailures++;
|
|
887
887
|
const delay = Math.min(15 * 60_000, 5_000 * 2 ** Math.min(8, this.consecutiveFailures - 1));
|
|
888
888
|
this.backoffUntil = Date.now() + delay;
|
|
889
889
|
const failure = e instanceof Error ? e.message : String(e);
|
|
890
|
-
this.lastError = named(failure, notStats?.message ?? null, ...phaseErrors);
|
|
890
|
+
this.lastError = named([failure, notStats?.message ?? null, ...phaseErrors]);
|
|
891
891
|
this.warn(`sync failed (${this.consecutiveFailures}×, retry in ${Math.round(delay / 1000)}s): ${failure}`);
|
|
892
892
|
report.more = true;
|
|
893
893
|
return report;
|
|
@@ -896,6 +896,6 @@ export class SemanticIndexer {
|
|
|
896
896
|
}
|
|
897
897
|
|
|
898
898
|
/** Each failure a sync pass met, once, in the order given, for its lastError: null if none. */
|
|
899
|
-
function named(
|
|
899
|
+
function named(failures: (string | null)[]): string | null {
|
|
900
900
|
return [...new Set(failures.filter((f): f is string => f !== null))].join('; ') || null;
|
|
901
901
|
}
|
|
@@ -89,6 +89,7 @@ import {
|
|
|
89
89
|
sameIdentity,
|
|
90
90
|
stageContained,
|
|
91
91
|
} from './direct-write.js';
|
|
92
|
+
import { appendAll } from '../../append-all.js';
|
|
92
93
|
|
|
93
94
|
/** Default for how long the after-batch scan may hold the agent's next inference. */
|
|
94
95
|
const AGENT_ACTION_SCAN_DEADLINE_MS = 20_000;
|
|
@@ -4549,7 +4550,7 @@ export class WorkspaceModule implements Module {
|
|
|
4549
4550
|
});
|
|
4550
4551
|
continue;
|
|
4551
4552
|
}
|
|
4552
|
-
notes
|
|
4553
|
+
appendAll(notes, lookedIn);
|
|
4553
4554
|
const plan = planTargetEdits(tool, target, stored.bytes, hints, maxSize, counts, problems);
|
|
4554
4555
|
if (plan === null) continue;
|
|
4555
4556
|
plans.push({
|
|
@@ -5670,8 +5671,8 @@ export class WorkspaceModule implements Module {
|
|
|
5670
5671
|
const found = mount.direct
|
|
5671
5672
|
? await this.textsFromDisk(mount, relativePath, root, start, kept, (text) => searched(text)())
|
|
5672
5673
|
: await this.textsFromStore(mount, relativePath, start, kept, searched);
|
|
5673
|
-
skipped
|
|
5674
|
-
incomplete
|
|
5674
|
+
appendAll(skipped, found.skipped);
|
|
5675
|
+
appendAll(incomplete, found.incomplete);
|
|
5675
5676
|
}
|
|
5676
5677
|
|
|
5677
5678
|
const answer: GrepAnswer = {
|
|
@@ -48,6 +48,7 @@ import type { FileHandle } from 'node:fs/promises';
|
|
|
48
48
|
import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from 'node:path';
|
|
49
49
|
import { onlyDeviceDiffers, sameRootIdentity, type Fingerprint, type RootIdentity } from './disk-agreement.js';
|
|
50
50
|
import { OUTSIDE_STEP, UNFOLLOWED_STEP, errnoClause, errnoCode, systemWords } from './answers.js';
|
|
51
|
+
import { appendAll } from '../../append-all.js';
|
|
51
52
|
|
|
52
53
|
export interface MountView {
|
|
53
54
|
/**
|
|
@@ -2391,7 +2392,7 @@ export async function walkScope(
|
|
|
2391
2392
|
for (const d of subdirs) walk.dirs.add(d);
|
|
2392
2393
|
for (const [p, why] of others) walk.others.set(p, why);
|
|
2393
2394
|
for (const p of otherFiles) walk.otherFiles.add(p);
|
|
2394
|
-
walk.incomplete
|
|
2395
|
+
appendAll(walk.incomplete, regions);
|
|
2395
2396
|
walk.complete.add(rel);
|
|
2396
2397
|
const listing = { leaves, subdirs, ...dir.identity() };
|
|
2397
2398
|
listings.set(rel, listing);
|
|
@@ -94,6 +94,7 @@ import {
|
|
|
94
94
|
} from './observe.js';
|
|
95
95
|
import { EffectFailed, type Expect, callFailed, chainOf, chmodContained, narrowedTo, syncDirectories, unlinkContained, writeContained } from './effects.js';
|
|
96
96
|
import { OUTSIDE_STEP, RESOLVED_OUTSIDE_STEP, UNFOLLOWED_STEP, errnoClause, errnoWords } from './answers.js';
|
|
97
|
+
import { appendAll } from '../../append-all.js';
|
|
97
98
|
|
|
98
99
|
export type EntryState =
|
|
99
100
|
| 'synced'
|
|
@@ -1150,8 +1151,8 @@ export async function reconcilePass(
|
|
|
1150
1151
|
return result;
|
|
1151
1152
|
}
|
|
1152
1153
|
const { walk, facts } = gathered;
|
|
1153
|
-
result.dirs
|
|
1154
|
-
result.incomplete
|
|
1154
|
+
appendAll(result.dirs, gathered.dirs);
|
|
1155
|
+
appendAll(result.incomplete, gathered.incomplete);
|
|
1155
1156
|
|
|
1156
1157
|
// Decide and apply — synchronously, so nothing interleaves.
|
|
1157
1158
|
const branchId = store.currentBranch().id;
|
package/src/notice-tags.ts
CHANGED
|
@@ -17,7 +17,7 @@ export const NOTICE_TAG = {
|
|
|
17
17
|
turnInterrupted: 'turn-interrupted',
|
|
18
18
|
closedChannel: 'closed channel',
|
|
19
19
|
tunedOut: 'tuned out',
|
|
20
|
-
refusalRewind: '
|
|
20
|
+
refusalRewind: 'turns-set-aside',
|
|
21
21
|
drafts: 'drafts',
|
|
22
22
|
delivered: 'delivered',
|
|
23
23
|
routing: 'routing',
|
|
@@ -75,20 +75,23 @@ export const NOTICE_TAG = {
|
|
|
75
75
|
noText: 'no text',
|
|
76
76
|
} as const;
|
|
77
77
|
|
|
78
|
+
export const FORMER_NOTICE_TAGS = ['refusal-rewind'] as const;
|
|
79
|
+
|
|
78
80
|
/**
|
|
79
|
-
* The bracket openings context-manager 0.
|
|
80
|
-
*
|
|
81
|
-
* framework's own are.
|
|
82
|
-
*
|
|
81
|
+
* The bracket openings context-manager 0.25.1 writes into an agent's context
|
|
82
|
+
* outside a tool result or read back from it, the same as 0.23.1 and 0.25.0
|
|
83
|
+
* write, marked as the framework's own are. Its `[truncated — original was`
|
|
84
|
+
* isn't here: NOTICE_TAG.truncated marks it. Check them against each
|
|
85
|
+
* context-manager release the framework takes.
|
|
83
86
|
*/
|
|
84
87
|
export const CONTEXT_MANAGER_TAGS = [
|
|
85
88
|
'content omitted', 'image not shown', 'Operator note', 'Recall', 'tool call omitted', 'tool result moved', 'Topic Transition',
|
|
86
89
|
] as const;
|
|
87
90
|
|
|
88
91
|
/**
|
|
89
|
-
* The bracket openings membrane 0.8.0
|
|
90
|
-
* a tool result, marked as the framework's own are. Check them against
|
|
91
|
-
* membrane release the framework takes.
|
|
92
|
+
* The bracket openings membrane 0.8.0 and 0.9.0 write into an agent's request
|
|
93
|
+
* outside a tool result, marked as the framework's own are. Check them against
|
|
94
|
+
* each membrane release the framework takes.
|
|
92
95
|
*/
|
|
93
96
|
export const MEMBRANE_TAGS = [
|
|
94
97
|
'Audio', 'continuing', 'Document', 'duplicate tool_result for', 'Image', 'orphan tool_result for', 'Start', 'Tool Result', 'Video',
|
package/src/presentation.ts
CHANGED
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
*/
|
|
64
64
|
import type { ContentBlock } from '@animalabs/membrane';
|
|
65
65
|
import { continuesBody, MessageStore, type ContextManager, type MessageId, type MessageMetadata, type StoredMessage } from '@animalabs/context-manager';
|
|
66
|
+
import { isReasoningLoss } from './reasoning-loss.js';
|
|
66
67
|
import { audienceOf, heldFromResidents, observedRowFor, suppressedRow } from './dendrite/audience.js';
|
|
67
68
|
import { sameSlot, type AgentRegistry } from './dendrite/registry.js';
|
|
68
69
|
import type { AgentName, AgentRecord, MessageSlotRef, ObserveEdge } from './dendrite/types.js';
|
|
@@ -228,7 +229,8 @@ export type RowSlot = 'own' | MessageSlotRef;
|
|
|
228
229
|
* a ref sameSlot as its `ownSlot`), else a slot it may observe. undefined,
|
|
229
230
|
* a slot its caller couldn't name, is elsewhere.
|
|
230
231
|
*
|
|
231
|
-
* A row a suppress rule hid
|
|
232
|
+
* A legacy reasoning-loss notice or a row a suppress rule hid
|
|
233
|
+
* (suppressedRow) is elsewhere to every reader,
|
|
232
234
|
* in any slot: no view the framework sets renders it.
|
|
233
235
|
*
|
|
234
236
|
* In its own slot, with `stamp = audienceOf(metadata)`:
|
|
@@ -263,7 +265,7 @@ export function bearingOf(
|
|
|
263
265
|
): Bearing {
|
|
264
266
|
const metadata = row.metadata as Record<string, unknown> | undefined;
|
|
265
267
|
if (slot === undefined) return 'elsewhere';
|
|
266
|
-
if (suppressedRow(metadata)) return 'elsewhere';
|
|
268
|
+
if (isReasoningLoss(metadata) || suppressedRow(metadata)) return 'elsewhere';
|
|
267
269
|
if (slot !== 'own' && !sameSlot(slot, reader.ownSlot)) {
|
|
268
270
|
const shown = reader.observed.some((seen) => sameSlot(seen.slot, slot)
|
|
269
271
|
&& observedRowFor(seen.edge, row, { observer: reader.name, owners: seen.owners }));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** #973: #938 stopped writing these notices, but old stores retain them.
|
|
2
|
+
* Exclude deterministically before selection, compression and thinking binding;
|
|
3
|
+
* never match text or change stored history. */
|
|
4
|
+
export function isReasoningLoss(metadata: unknown): boolean {
|
|
5
|
+
const mark = metadata as { kind?: unknown; system?: unknown } | null | undefined;
|
|
6
|
+
return mark?.kind === 'reasoning-loss' && mark.system === true;
|
|
7
|
+
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type DiscordAwarenessRef,
|
|
17
17
|
type DiscordMessageMetadataCarrier,
|
|
18
18
|
} from './discord-awareness-outbox.js';
|
|
19
|
+
import { appendAll } from '../append-all.js';
|
|
19
20
|
|
|
20
21
|
export interface OfflineRecoveryBranchOptions {
|
|
21
22
|
storePath: string;
|
|
@@ -877,7 +878,7 @@ function buildSuppressionPlan(
|
|
|
877
878
|
const intervals: SuppressionInterval[] = [...stamped];
|
|
878
879
|
for (const id of messageIds) {
|
|
879
880
|
const location = locations.get(id)!;
|
|
880
|
-
intervals
|
|
881
|
+
appendAll(intervals, location.runs.map((run) => ({ ...run })));
|
|
881
882
|
}
|
|
882
883
|
for (const range of ranges) {
|
|
883
884
|
const from = locations.get(range.fromMessageId)!;
|
|
@@ -896,7 +897,7 @@ function buildSuppressionPlan(
|
|
|
896
897
|
fromId: earlier.firstId,
|
|
897
898
|
toId: later.firstId,
|
|
898
899
|
});
|
|
899
|
-
for (const end of [from, to]) intervals
|
|
900
|
+
for (const end of [from, to]) appendAll(intervals, end.runs.map((run) => ({ ...run })));
|
|
900
901
|
}
|
|
901
902
|
|
|
902
903
|
intervals.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
@@ -916,7 +917,7 @@ function buildSuppressionPlan(
|
|
|
916
917
|
const carriers: DiscordMessageMetadataCarrier[] = [];
|
|
917
918
|
const ids = new Set<string>();
|
|
918
919
|
for (const interval of merged) {
|
|
919
|
-
carriers
|
|
920
|
+
appendAll(carriers, collectDiscordCarriers(contextManager, interval.start, interval.end + 1, ids));
|
|
920
921
|
}
|
|
921
922
|
return {
|
|
922
923
|
intervals: merged,
|
|
@@ -946,7 +947,7 @@ function collectDiscordCarriers(
|
|
|
946
947
|
Math.min(RECOVERY_SCAN_WINDOW, end - offset),
|
|
947
948
|
{ resolveBlobs: false },
|
|
948
949
|
).messages;
|
|
949
|
-
carriers
|
|
950
|
+
appendAll(carriers, messages.map(addressingOnly));
|
|
950
951
|
if (ids) for (const message of messages) ids.add(String(message.id));
|
|
951
952
|
}
|
|
952
953
|
return carriers;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { createOfflineRecoveryBranch } from './offline-branch.js';
|
|
4
4
|
import type { DiscordAwarenessMarks } from './discord-awareness-outbox.js';
|
|
5
|
+
import { appendAll } from '../append-all.js';
|
|
5
6
|
|
|
6
7
|
interface CliOptions {
|
|
7
8
|
storePath?: string;
|
|
@@ -86,7 +87,7 @@ function parseArgs(args: string[]): CliOptions {
|
|
|
86
87
|
case '--messages': options.messages = Number(value()); break;
|
|
87
88
|
case '--suppress':
|
|
88
89
|
case '--suppress-message':
|
|
89
|
-
options.suppressMessageIds
|
|
90
|
+
appendAll(options.suppressMessageIds, value().split(',').map((item) => item.trim()).filter(Boolean));
|
|
90
91
|
break;
|
|
91
92
|
case '--suppress-range': {
|
|
92
93
|
const raw = value();
|
package/src/sent-rows.ts
CHANGED
|
@@ -152,7 +152,7 @@ export function roundRows(
|
|
|
152
152
|
* the two alike (read from the provider on 2026-10-23: Haiku 5.5 with the drop control, on an enforcing API-key
|
|
153
153
|
* organization and on a subscription account, reported no drop for either, and dropped both replies' blocks when an
|
|
154
154
|
* earlier user message changed). The store's own fields beside them, such as
|
|
155
|
-
* `toolName`, don't reach the provider on membrane's native path, the only one noted (membrane 0.8.0's XML
|
|
155
|
+
* `toolName`, don't reach the provider on membrane's native path, the only one noted (membrane 0.8.0's and 0.9.0's XML
|
|
156
156
|
* formatter reads `toolName`, and an XML stream is never noted).
|
|
157
157
|
*/
|
|
158
158
|
function normalizedResult(block: ContentBlock): unknown {
|
package/src/supplied-text.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* a line that reads as the framework's own.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { CONTEXT_MANAGER_TAGS, MEMBRANE_TAGS, NOTICE_TAG } from './notice-tags.js';
|
|
8
|
+
import { CONTEXT_MANAGER_TAGS, FORMER_NOTICE_TAGS, MEMBRANE_TAGS, NOTICE_TAG } from './notice-tags.js';
|
|
9
9
|
|
|
10
10
|
/** Every form of line break a reader might take for one: CR LF, CR, LF,
|
|
11
11
|
* vertical tab, form feed, NEL, and the Unicode line and paragraph separators. */
|
|
@@ -27,14 +27,14 @@ const SKIPPED = `[\\s\\p{Cc}${INVISIBLE}]`;
|
|
|
27
27
|
const tagWord = (word: string): string =>
|
|
28
28
|
[...word].map((c) => (/[\\^$.*+?()[\]{}|/]/.test(c) ? `\\${c}` : c)).join(`${SKIPPED}*`);
|
|
29
29
|
const NOTICE_OPENING = new RegExp(
|
|
30
|
-
`\\[(?=${SKIPPED}*(?:${[...Object.values(NOTICE_TAG), ...CONTEXT_MANAGER_TAGS, ...MEMBRANE_TAGS].map((tag) => tag.split(' ').map(tagWord).join(`${SKIPPED}+`)).join('|')})(?![\\p{L}\\p{N}]))`,
|
|
30
|
+
`\\[(?=${SKIPPED}*(?:${[...Object.values(NOTICE_TAG), ...FORMER_NOTICE_TAGS, ...CONTEXT_MANAGER_TAGS, ...MEMBRANE_TAGS].map((tag) => tag.split(' ').map(tagWord).join(`${SKIPPED}+`)).join('|')})(?![\\p{L}\\p{N}]))`,
|
|
31
31
|
'giu',
|
|
32
32
|
);
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Texts read as one string, joined by line breaks, with a backslash put
|
|
36
36
|
* before the bracket of each opening of a listed tag (NOTICE_TAG,
|
|
37
|
-
* CONTEXT_MANAGER_TAGS, MEMBRANE_TAGS) that hasn't one before it in its own
|
|
37
|
+
* FORMER_NOTICE_TAGS, CONTEXT_MANAGER_TAGS, MEMBRANE_TAGS) that hasn't one before it in its own
|
|
38
38
|
* text. A text `marks` says false of is read and never changed.
|
|
39
39
|
*
|
|
40
40
|
* Every opening, not only one that starts a line: membrane's XML formatter
|
package/src/thinking-drops.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface StreamThinkingDrops {
|
|
|
34
34
|
place: (entries: readonly ThinkingBindingEntry[]) => PlacedDrop[];
|
|
35
35
|
recordedAfter?: number;
|
|
36
36
|
branch?: string;
|
|
37
|
+
/** `StartStreamResult`'s `noteThinkingDropped`. */
|
|
38
|
+
noteDropped?: (messageIds: readonly MessageId[]) => boolean;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/** A signed block's identity on the wire: a thinking block's signature, a redacted block's data. */
|