@animalabs/agent-framework 0.12.0 → 0.14.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 (106) hide show
  1. package/dist/src/agent.d.ts +2 -0
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +3 -0
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +76 -4
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +933 -151
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/gate/event-gate.d.ts +62 -2
  10. package/dist/src/gate/event-gate.d.ts.map +1 -1
  11. package/dist/src/gate/event-gate.js +56 -2
  12. package/dist/src/gate/event-gate.js.map +1 -1
  13. package/dist/src/gate/index.d.ts +1 -1
  14. package/dist/src/gate/index.d.ts.map +1 -1
  15. package/dist/src/gate/index.js +1 -1
  16. package/dist/src/gate/index.js.map +1 -1
  17. package/dist/src/index.d.ts +1 -1
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/index.js.map +1 -1
  20. package/dist/src/mcpl/channel-registry.d.ts +84 -1
  21. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  22. package/dist/src/mcpl/channel-registry.js +160 -4
  23. package/dist/src/mcpl/channel-registry.js.map +1 -1
  24. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  25. package/dist/src/modules/workspace/index.js +5 -1
  26. package/dist/src/modules/workspace/index.js.map +1 -1
  27. package/dist/src/tool-image-ledger.d.ts +175 -0
  28. package/dist/src/tool-image-ledger.d.ts.map +1 -0
  29. package/dist/src/tool-image-ledger.js +257 -0
  30. package/dist/src/tool-image-ledger.js.map +1 -0
  31. package/dist/src/tool-result-history.d.ts +15 -2
  32. package/dist/src/tool-result-history.d.ts.map +1 -1
  33. package/dist/src/tool-result-history.js +16 -9
  34. package/dist/src/tool-result-history.js.map +1 -1
  35. package/dist/src/tool-wrapper-prose-guard.d.ts +8 -0
  36. package/dist/src/tool-wrapper-prose-guard.d.ts.map +1 -0
  37. package/dist/src/tool-wrapper-prose-guard.js +37 -0
  38. package/dist/src/tool-wrapper-prose-guard.js.map +1 -0
  39. package/dist/src/tune-out/coordinator.d.ts +144 -0
  40. package/dist/src/tune-out/coordinator.d.ts.map +1 -0
  41. package/dist/src/tune-out/coordinator.js +444 -0
  42. package/dist/src/tune-out/coordinator.js.map +1 -0
  43. package/dist/src/tune-out/tools.d.ts +43 -0
  44. package/dist/src/tune-out/tools.d.ts.map +1 -0
  45. package/dist/src/tune-out/tools.js +93 -0
  46. package/dist/src/tune-out/tools.js.map +1 -0
  47. package/dist/src/types/agent.d.ts +17 -0
  48. package/dist/src/types/agent.d.ts.map +1 -1
  49. package/dist/src/types/framework.d.ts +6 -0
  50. package/dist/src/types/framework.d.ts.map +1 -1
  51. package/dist/test/channel-incoming-targeting.test.d.ts +2 -0
  52. package/dist/test/channel-incoming-targeting.test.d.ts.map +1 -0
  53. package/dist/test/channel-incoming-targeting.test.js +81 -0
  54. package/dist/test/channel-incoming-targeting.test.js.map +1 -0
  55. package/dist/test/event-gate.test.js +84 -1
  56. package/dist/test/event-gate.test.js.map +1 -1
  57. package/dist/test/per-agent-delivery.test.d.ts +2 -0
  58. package/dist/test/per-agent-delivery.test.d.ts.map +1 -0
  59. package/dist/test/per-agent-delivery.test.js +108 -0
  60. package/dist/test/per-agent-delivery.test.js.map +1 -0
  61. package/dist/test/puppet-tool-call.test.js +1 -0
  62. package/dist/test/puppet-tool-call.test.js.map +1 -1
  63. package/dist/test/save-recent-image.test.d.ts +26 -0
  64. package/dist/test/save-recent-image.test.d.ts.map +1 -0
  65. package/dist/test/save-recent-image.test.js +707 -0
  66. package/dist/test/save-recent-image.test.js.map +1 -0
  67. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +2 -0
  68. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +1 -0
  69. package/dist/test/tool-wrapper-prose-guard-integration.test.js +485 -0
  70. package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +1 -0
  71. package/dist/test/tool-wrapper-prose-guard.test.d.ts +2 -0
  72. package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +1 -0
  73. package/dist/test/tool-wrapper-prose-guard.test.js +34 -0
  74. package/dist/test/tool-wrapper-prose-guard.test.js.map +1 -0
  75. package/dist/test/tune-out-e2e.test.d.ts +2 -0
  76. package/dist/test/tune-out-e2e.test.d.ts.map +1 -0
  77. package/dist/test/tune-out-e2e.test.js +142 -0
  78. package/dist/test/tune-out-e2e.test.js.map +1 -0
  79. package/dist/test/tune-out-gate-composition.test.d.ts +2 -0
  80. package/dist/test/tune-out-gate-composition.test.d.ts.map +1 -0
  81. package/dist/test/tune-out-gate-composition.test.js +221 -0
  82. package/dist/test/tune-out-gate-composition.test.js.map +1 -0
  83. package/dist/test/tune-out-lifecycle.test.d.ts +2 -0
  84. package/dist/test/tune-out-lifecycle.test.d.ts.map +1 -0
  85. package/dist/test/tune-out-lifecycle.test.js +121 -0
  86. package/dist/test/tune-out-lifecycle.test.js.map +1 -0
  87. package/dist/test/turn-trigger-provenance.test.js +17 -0
  88. package/dist/test/turn-trigger-provenance.test.js.map +1 -1
  89. package/dist/test/workspace-read-image.test.js +8 -0
  90. package/dist/test/workspace-read-image.test.js.map +1 -1
  91. package/dist/tsconfig.tsbuildinfo +1 -1
  92. package/package.json +2 -2
  93. package/src/agent.ts +3 -0
  94. package/src/framework.ts +1051 -154
  95. package/src/gate/event-gate.ts +99 -6
  96. package/src/gate/index.ts +1 -1
  97. package/src/index.ts +1 -1
  98. package/src/mcpl/channel-registry.ts +235 -7
  99. package/src/modules/workspace/index.ts +5 -1
  100. package/src/tool-image-ledger.ts +334 -0
  101. package/src/tool-result-history.ts +30 -9
  102. package/src/tool-wrapper-prose-guard.ts +38 -0
  103. package/src/tune-out/coordinator.ts +548 -0
  104. package/src/tune-out/tools.ts +129 -0
  105. package/src/types/agent.ts +18 -0
  106. package/src/types/framework.ts +7 -0
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Per-agent retention of tool-result images, with provenance (issue #104).
3
+ *
4
+ * History keeps a compact text placeholder for every image a tool returns —
5
+ * the bytes only ever ride the live wire copy of that one turn. That policy
6
+ * is right (megabytes of base64 in the persisted window would be sliced,
7
+ * compressed, and replayed forever), but it left `save_recent_image` with no
8
+ * way to reach an image the resident had just seen: the scanner walked past
9
+ * the byteless placeholder and quietly saved whichever OLDER attachment came
10
+ * next, under the filename the resident chose for the new one.
11
+ *
12
+ * This ledger is the missing half. At ingestion every tool-result image is
13
+ * retained here under a stable per-agent ref (`img_k7x3q2_7`) bound to the
14
+ * exact tool call and block it came from, with its digest. The ref carries a
15
+ * per-ledger nonce: ledgers live and die with the process, but the persisted
16
+ * store keeps yesterday's placeholders visible in context, so a bare sequence
17
+ * number would let a stale `img_7` resolve to whatever this process minted
18
+ * seventh. A stale ref must resolve `unknown`, never to other bytes.
19
+ *
20
+ * The history placeholder carries the ref, so the ordered inventory
21
+ * `save_recent_image` walks can
22
+ * include tool images at their true position — and when the bytes behind a
23
+ * ref are gone (bounded eviction, or a restart: the ledger is in-memory by
24
+ * design, so it never adds to blob growth), the save fails AT THAT INDEX with
25
+ * a specific error instead of sliding to an older image. Silent substitution
26
+ * of bytes from another surface is the failure this exists to end.
27
+ */
28
+ import { createHash, randomBytes } from 'node:crypto';
29
+ import { safeSlice } from './safe-slice.js';
30
+ /** 96 MB of base64 ≈ 72 MB of image bytes — a few dozen world snapshots. */
31
+ export const DEFAULT_TOOL_IMAGE_LEDGER_MAX_CHARS = 96 * 1024 * 1024;
32
+ export const DEFAULT_TOOL_IMAGE_LEDGER_MAX_ENTRIES = 64;
33
+ export class ToolImageLedger {
34
+ /** Namespace for every ref this ledger mints; a fresh ledger (new process,
35
+ * agent re-registration) never shares one with its predecessors. */
36
+ nonce = mintLedgerNonce();
37
+ nextSeq = 1;
38
+ /** Insertion-ordered (oldest first) — eviction pops from the front. */
39
+ retained = new Map();
40
+ /** Provenance for every ref ever issued, bytes or not (bounded separately). */
41
+ issued = new Map();
42
+ refByKey = new Map();
43
+ heldChars = 0;
44
+ maxChars;
45
+ maxEntries;
46
+ constructor(options = {}) {
47
+ this.maxChars = options.maxChars ?? DEFAULT_TOOL_IMAGE_LEDGER_MAX_CHARS;
48
+ this.maxEntries = options.maxEntries ?? DEFAULT_TOOL_IMAGE_LEDGER_MAX_ENTRIES;
49
+ }
50
+ /**
51
+ * Retain one image block from a tool result. Idempotent per
52
+ * (toolCallId, blockIndex) for as long as that block's provenance record
53
+ * is held (bounded — see trimProvenance): within that window the same
54
+ * block always yields the same ref, so a result serialized twice (or
55
+ * retained early in a tool batch, then serialized at commit) agrees with
56
+ * itself. A block re-serialized after its record aged out mints a fresh
57
+ * ref; the old one resolves `evicted`/`unknown`, never to other bytes.
58
+ */
59
+ retain(source) {
60
+ const key = `${source.toolCallId}:${source.blockIndex}`;
61
+ const existingRef = this.refByKey.get(key);
62
+ if (existingRef !== undefined) {
63
+ const known = this.issued.get(existingRef);
64
+ if (known)
65
+ return known;
66
+ }
67
+ const ref = `img_${this.nonce}_${this.nextSeq++}`;
68
+ // Digest without materializing the whole decoded image: a 96MB base64
69
+ // field would otherwise transiently allocate ~72MB more and the ledger
70
+ // would be the one asking for it. Chunked decode keeps the peak small.
71
+ const { byteSize, sha256 } = digestBase64(source.data);
72
+ const image = {
73
+ ref,
74
+ toolCallId: source.toolCallId,
75
+ toolName: source.toolName,
76
+ blockIndex: source.blockIndex,
77
+ // Tools send mime types unvalidated; the placeholder grammar is
78
+ // lowercase, so normalize here and at format time (same rule) or an
79
+ // `Image/PNG` would write a placeholder that never re-parses — an
80
+ // invisible slot, i.e. a silent index slide.
81
+ mediaType: normalizeMediaType(source.mediaType),
82
+ byteSize,
83
+ sha256,
84
+ retainedAt: Date.now(),
85
+ data: source.data,
86
+ };
87
+ const { data: _data, ...provenance } = image;
88
+ this.refByKey.set(key, ref);
89
+ this.issued.set(ref, provenance);
90
+ // A single image over the whole budget is issued (so the placeholder is
91
+ // honest about its provenance) but never held.
92
+ if (source.data.length <= this.maxChars) {
93
+ this.retained.set(ref, image);
94
+ this.heldChars += source.data.length;
95
+ this.evictOverBudget();
96
+ }
97
+ this.trimProvenance();
98
+ return provenance;
99
+ }
100
+ lookup(ref) {
101
+ const held = this.retained.get(ref);
102
+ if (held)
103
+ return { status: 'retained', image: held };
104
+ const known = this.issued.get(ref);
105
+ if (known)
106
+ return { status: 'evicted', image: known };
107
+ return { status: 'unknown' };
108
+ }
109
+ /** Ref already minted for this block, if any (no retention side effect). */
110
+ refFor(toolCallId, blockIndex) {
111
+ return this.refByKey.get(`${toolCallId}:${blockIndex}`);
112
+ }
113
+ /** Count of images whose bytes are currently held. */
114
+ get size() {
115
+ return this.retained.size;
116
+ }
117
+ evictOverBudget() {
118
+ for (const [ref, image] of this.retained) {
119
+ if (this.retained.size <= this.maxEntries && this.heldChars <= this.maxChars)
120
+ break;
121
+ this.retained.delete(ref);
122
+ this.heldChars -= image.data.length;
123
+ }
124
+ }
125
+ /** Provenance for evicted refs is kept for a while so the error can name
126
+ * the tool call; bounded so a long-lived agent can't grow it forever. */
127
+ trimProvenance() {
128
+ const keep = Math.max(this.maxEntries * 4, 256);
129
+ while (this.issued.size > keep) {
130
+ const oldest = this.issued.keys().next().value;
131
+ if (oldest === undefined)
132
+ break;
133
+ const image = this.issued.get(oldest);
134
+ this.issued.delete(oldest);
135
+ if (image)
136
+ this.refByKey.delete(`${image.toolCallId}:${image.blockIndex}`);
137
+ }
138
+ }
139
+ }
140
+ /** Six random base36 chars — one per ledger lifetime, embedded in every ref. */
141
+ function mintLedgerNonce() {
142
+ return randomBytes(4).readUInt32BE(0).toString(36).padStart(6, '0').slice(-6);
143
+ }
144
+ /** Grammar of a ref this module mints: `img_<ledger nonce>_<sequence>`. */
145
+ export const TOOL_IMAGE_REF_RE = /^img_[0-9a-z]+_\d+$/;
146
+ /** RFC 6838 type/subtype token characters (lowercased); no parameters. */
147
+ const MEDIA_TYPE_ESSENCE_RE = /^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/;
148
+ export const FALLBACK_MEDIA_TYPE = 'application/octet-stream';
149
+ /**
150
+ * The one spelling the placeholder grammar accepts: trimmed, lowercased, the
151
+ * `type/subtype` essence only (parameters such as `; charset=binary` are
152
+ * dropped), and — because `mimeType` arrives from external MCPL servers
153
+ * unvalidated — anything that still is not a well-formed type/subtype falls
154
+ * back to `application/octet-stream`. The invariant this buys: the
155
+ * serializer can NEVER write a placeholder that does not re-parse, so a
156
+ * strange mime can never produce an invisible slot (a silent index slide).
157
+ */
158
+ export function normalizeMediaType(mediaType) {
159
+ const essence = mediaType.split(';', 1)[0].trim().toLowerCase();
160
+ return MEDIA_TYPE_ESSENCE_RE.test(essence) ? essence : FALLBACK_MEDIA_TYPE;
161
+ }
162
+ /** History placeholder for a retained tool image: `[image: image/png, ~691KB, ref img_7]`. */
163
+ export function formatToolImagePlaceholder(mediaType, sizeLabel, ref) {
164
+ const mime = normalizeMediaType(mediaType);
165
+ return ref === null
166
+ ? `[image: ${mime}, ${sizeLabel}]`
167
+ : `[image: ${mime}, ${sizeLabel}, ref ${ref}]`;
168
+ }
169
+ const PLACEHOLDER_RE = /\[image: ([a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+), (~?[\d.]+(?:B|KB|MB))(?:, ref (img_[0-9a-z]+_\d+))?\]/g;
170
+ /** `[ref_1_ab3d] cam.png — image/png, 1.2MB claimed — from tool result — …` (one line). */
171
+ const REFERENCE_STUB_RE = /^\[(ref_[0-9a-z]+_[0-9a-z]+)\] ([^\n]*)$/gm;
172
+ const IMAGE_MIME_RE = /\bimage\/[a-z0-9!#$&^_.+-]+/i;
173
+ /**
174
+ * Every image slot in a stored tool-result string, in text order: inline
175
+ * placeholders and image-typed reference stubs. Text-derived and therefore
176
+ * untrusted — a tool result can quote someone else's placeholder verbatim —
177
+ * so the caller MUST cross-check a resolved ref's provenance against the
178
+ * tool_result block it was found in before handing out bytes.
179
+ */
180
+ export function parseImagePlaceholders(text) {
181
+ const out = [];
182
+ for (const m of text.matchAll(PLACEHOLDER_RE)) {
183
+ out.push({ kind: 'inline', mediaType: m[1], sizeLabel: m[2], ref: m[3] ?? null, offset: m.index ?? 0, text: m[0] });
184
+ }
185
+ for (const m of text.matchAll(REFERENCE_STUB_RE)) {
186
+ const mime = m[2].match(IMAGE_MIME_RE);
187
+ out.push({ kind: 'reference', refId: m[1], mediaType: mime ? mime[0].toLowerCase() : null, offset: m.index ?? 0, text: m[0] });
188
+ }
189
+ out.sort((a, b) => a.offset - b.offset);
190
+ return out;
191
+ }
192
+ /**
193
+ * Cut `text` to at most `cap` chars WITHOUT losing or splitting an image
194
+ * slot. The stored tool_result text is the only place the inventory
195
+ * discovers tool images, while the live wire still delivers every image
196
+ * regardless of text length — so a slot dropped by truncation is an image
197
+ * the resident saw that the index silently slides past (the #104 failure,
198
+ * one representation over). Returns the head to keep and the slots that
199
+ * fell at or past the cut, to be re-appended verbatim after the truncation
200
+ * notice. A slot straddling the cut moves the cut back to its start, so the
201
+ * head never contains a partial slot (a partial reference-stub line would
202
+ * otherwise still re-parse and be counted twice).
203
+ */
204
+ export function splitPreservingImageSlots(text, cap) {
205
+ const slots = parseImagePlaceholders(text);
206
+ let cutAt = cap;
207
+ for (const slot of slots) {
208
+ if (slot.offset < cutAt && slot.offset + slot.text.length > cutAt)
209
+ cutAt = slot.offset;
210
+ }
211
+ return {
212
+ head: safeSlice(text, 0, cutAt),
213
+ tail: slots.filter((slot) => slot.offset >= cutAt).map((slot) => slot.text),
214
+ };
215
+ }
216
+ /** Text to append after a truncation notice so the preserved slots stay
217
+ * parseable (each reference stub on its own line). Empty when none. */
218
+ export function formatPreservedImageSlots(tail) {
219
+ if (tail.length === 0)
220
+ return '';
221
+ return `\n[${tail.length} image slot(s) fell past the cut; kept here in order so save_recent_image still counts them:]\n`
222
+ + tail.join('\n');
223
+ }
224
+ export function sha256Hex(bytes) {
225
+ return createHash('sha256').update(bytes).digest('hex');
226
+ }
227
+ /** Only the base64 alphabet, with nothing but trailing `=` padding after it. */
228
+ function isCleanBase64(data) {
229
+ const m = /[^A-Za-z0-9+/]/.exec(data);
230
+ if (!m)
231
+ return true;
232
+ const rest = data.length - m.index;
233
+ return rest <= 2 && /^=+$/.test(data.slice(m.index));
234
+ }
235
+ /**
236
+ * Byte length and SHA-256 of a base64 payload, decoded in 4-char-aligned
237
+ * chunks so the peak allocation stays at one chunk rather than the whole
238
+ * image. Falls back to a whole decode when the string carries whitespace
239
+ * or embedded padding (chunk alignment would no longer be trustworthy) —
240
+ * the digest must describe exactly the bytes a later whole decode writes.
241
+ */
242
+ function digestBase64(data) {
243
+ if (!isCleanBase64(data)) {
244
+ const bytes = Buffer.from(data, 'base64');
245
+ return { byteSize: bytes.byteLength, sha256: sha256Hex(bytes) };
246
+ }
247
+ const CHUNK = 1 << 20; // multiple of 4
248
+ const hash = createHash('sha256');
249
+ let byteSize = 0;
250
+ for (let i = 0; i < data.length; i += CHUNK) {
251
+ const bytes = Buffer.from(data.slice(i, i + CHUNK), 'base64');
252
+ hash.update(bytes);
253
+ byteSize += bytes.byteLength;
254
+ }
255
+ return { byteSize, sha256: hash.digest('hex') };
256
+ }
257
+ //# sourceMappingURL=tool-image-ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-image-ledger.js","sourceRoot":"","sources":["../../src/tool-image-ledger.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,18 @@
13
13
  * replaces image blocks with a short `[image: mimeType, ~NKB]` placeholder.
14
14
  * For anything else it falls back to JSON.
15
15
  */
16
+ export interface HistorySerializeOptions {
17
+ /**
18
+ * Called for every image block the serializer replaces with a placeholder
19
+ * (issue #104). Returns the retention ref to embed in the placeholder —
20
+ * `[image: image/png, ~691KB, ref img_7]` — so the image keeps a saveable
21
+ * handle in history; null leaves the legacy ref-less form.
22
+ */
23
+ imageRef?: (blockIndex: number, image: {
24
+ data: string;
25
+ mimeType: string;
26
+ }) => string | null;
27
+ }
16
28
  /**
17
29
  * House-safe default inline cap (chars) for tool results, error results, and
18
30
  * background-script wake payloads (issue #89: a resident should never eat a
@@ -31,7 +43,8 @@
31
43
  * broad greps) still spill, which is the point.
32
44
  */
33
45
  export declare const DEFAULT_TOOL_RESULT_INLINE_MAX_CHARS = 24000;
34
- export declare function toolResultDataToHistoryString(data: unknown, maxChars?: number): string;
35
- /** Bounded copy of an arbitrary string with the standard truncation notice. */
46
+ export declare function toolResultDataToHistoryString(data: unknown, maxChars?: number, options?: HistorySerializeOptions): string;
47
+ /** Bounded copy of an arbitrary string with the standard truncation notice.
48
+ * Image slots past the cut are re-appended, never dropped (issue #104). */
36
49
  export declare function truncateForHistory(str: string, maxChars: number): string;
37
50
  //# sourceMappingURL=tool-result-history.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-result-history.d.ts","sourceRoot":"","sources":["../../src/tool-result-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oCAAoC,QAAQ,CAAC;AAE1D,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAItF;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIxE"}
1
+ {"version":3,"file":"tool-result-history.d.ts","sourceRoot":"","sources":["../../src/tool-result-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,GAAG,IAAI,CAAC;CAC7F;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oCAAoC,QAAQ,CAAC;AAE1D,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,OAAO,EACb,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAIR;AAED;4EAC4E;AAC5E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMxE"}
@@ -13,8 +13,8 @@
13
13
  * replaces image blocks with a short `[image: mimeType, ~NKB]` placeholder.
14
14
  * For anything else it falls back to JSON.
15
15
  */
16
- import { safeSlice } from './safe-slice.js';
17
16
  import { INLINE_WITHHELD_TEXT, isInlineContradiction, referenceStubOrNull } from './mcpl/references.js';
17
+ import { formatPreservedImageSlots, formatToolImagePlaceholder, splitPreservingImageSlots } from './tool-image-ledger.js';
18
18
  /**
19
19
  * House-safe default inline cap (chars) for tool results, error results, and
20
20
  * background-script wake payloads (issue #89: a resident should never eat a
@@ -33,28 +33,31 @@ import { INLINE_WITHHELD_TEXT, isInlineContradiction, referenceStubOrNull } from
33
33
  * broad greps) still spill, which is the point.
34
34
  */
35
35
  export const DEFAULT_TOOL_RESULT_INLINE_MAX_CHARS = 24000;
36
- export function toolResultDataToHistoryString(data, maxChars) {
37
- const fromArray = tryHistoryStringFromContentArray(data);
36
+ export function toolResultDataToHistoryString(data, maxChars, options) {
37
+ const fromArray = tryHistoryStringFromContentArray(data, options);
38
38
  const str = fromArray ?? JSON.stringify(data);
39
39
  return maxChars ? truncateForHistory(str, maxChars) : str;
40
40
  }
41
- /** Bounded copy of an arbitrary string with the standard truncation notice. */
41
+ /** Bounded copy of an arbitrary string with the standard truncation notice.
42
+ * Image slots past the cut are re-appended, never dropped (issue #104). */
42
43
  export function truncateForHistory(str, maxChars) {
43
44
  if (str.length <= maxChars)
44
45
  return str;
45
- return safeSlice(str, 0, maxChars)
46
- + '\n\n[truncated — original was ' + str.length + ' chars]';
46
+ const { head, tail } = splitPreservingImageSlots(str, maxChars);
47
+ return head
48
+ + '\n\n[truncated — original was ' + str.length + ' chars]'
49
+ + formatPreservedImageSlots(tail);
47
50
  }
48
51
  /**
49
52
  * If `data` is an MCP content array whose every block has a shape we
50
53
  * recognize, return a history-safe string (images → placeholders). Otherwise
51
54
  * return `null` to defer to the caller's fallback (JSON, usually).
52
55
  */
53
- function tryHistoryStringFromContentArray(data) {
56
+ function tryHistoryStringFromContentArray(data, options) {
54
57
  if (!Array.isArray(data))
55
58
  return null;
56
59
  const parts = [];
57
- for (const raw of data) {
60
+ for (const [blockIndex, raw] of data.entries()) {
58
61
  if (!raw || typeof raw !== 'object')
59
62
  return null;
60
63
  const b = raw;
@@ -69,11 +72,15 @@ function tryHistoryStringFromContentArray(data) {
69
72
  else if (b.type === 'image' && typeof b.data === 'string' && typeof b.mimeType === 'string') {
70
73
  // Decoded byte estimate from base64 length (3/4 ratio, rounded).
71
74
  const approxBytes = Math.floor(b.data.length * 3 / 4);
72
- parts.push(`[image: ${b.mimeType}, ${formatSize(approxBytes)}]`);
75
+ const ref = options?.imageRef?.(blockIndex, { data: b.data, mimeType: b.mimeType }) ?? null;
76
+ parts.push(formatToolImagePlaceholder(b.mimeType, formatSize(approxBytes), ref));
73
77
  }
74
78
  else if (b.type === 'resource' || ((b.type === 'image' || b.type === 'audio') && typeof b.uri === 'string')) {
75
79
  // RFC-005 reference block: a bounded stub, never the raw block JSON
76
80
  // (which used to inline the URI — and the whole array — into history).
81
+ // Image-typed stubs still occupy a slot in save_recent_image's
82
+ // inventory (a failing one — bytes live behind fetch_reference), so
83
+ // the index never slides past an image the resident saw.
77
84
  const stub = referenceStubOrNull(b, 'from tool result');
78
85
  if (stub === null)
79
86
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-result-history.js","sourceRoot":"","sources":["../../src/tool-result-history.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-result-history.js","sourceRoot":"","sources":["../../src/tool-result-history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ContentBlock } from '@animalabs/membrane';
2
+ /**
3
+ * Recognize ONLY a whole-response textual invocation wrapper for a tool that
4
+ * was registered on this exact inference. This is containment, not parsing:
5
+ * callers must never execute the returned tool name or wrapper body.
6
+ */
7
+ export declare function detectKnownToolWrapperProse(content: ContentBlock[], knownToolNames: ReadonlySet<string>): string | null;
8
+ //# sourceMappingURL=tool-wrapper-prose-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-wrapper-prose-guard.d.ts","sourceRoot":"","sources":["../../src/tool-wrapper-prose-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,YAAY,EAAE,EACvB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,MAAM,GAAG,IAAI,CA2Bf"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Recognize ONLY a whole-response textual invocation wrapper for a tool that
3
+ * was registered on this exact inference. This is containment, not parsing:
4
+ * callers must never execute the returned tool name or wrapper body.
5
+ */
6
+ export function detectKnownToolWrapperProse(content, knownToolNames) {
7
+ if (knownToolNames.size === 0)
8
+ return null;
9
+ // Signed thinking may precede visible prose. Any other non-text block makes
10
+ // this a mixed response, not an exact textual wrapper.
11
+ const text = [];
12
+ for (const block of content) {
13
+ if (block.type === 'text')
14
+ text.push(block.text);
15
+ else if (block.type !== 'thinking' && block.type !== 'redacted_thinking')
16
+ return null;
17
+ }
18
+ const body = text.join('\n').trim();
19
+ if (!body)
20
+ return null;
21
+ const tagName = '[A-Za-z_][A-Za-z0-9_.:-]*';
22
+ let match = body.match(new RegExp(`^<(${tagName})\\s*/>$`));
23
+ if (match && knownToolNames.has(match[1]))
24
+ return match[1];
25
+ match = body.match(new RegExp(`^<(${tagName})\\s*>([\\s\\S]*?)<\\/\\1\\s*>$`));
26
+ if (match && knownToolNames.has(match[1]))
27
+ return match[1];
28
+ // Legacy XML formatter shape used by older Claude harnesses.
29
+ match = body.match(/^<invoke\s*>\s*<tool\s+name=(['"])([^'"]+)\1(?:\s+[^>]*)?\s*\/>\s*<\/invoke\s*>$/);
30
+ if (match && knownToolNames.has(match[2]))
31
+ return match[2];
32
+ match = body.match(/^<invoke\s*>\s*<tool\s+name=(['"])([^'"]+)\1(?:\s+[^>]*)?\s*>([\s\S]*?)<\/tool\s*>\s*<\/invoke\s*>$/);
33
+ if (match && knownToolNames.has(match[2]))
34
+ return match[2];
35
+ return null;
36
+ }
37
+ //# sourceMappingURL=tool-wrapper-prose-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-wrapper-prose-guard.js","sourceRoot":"","sources":["../../src/tool-wrapper-prose-guard.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CACzC,OAAuB,EACvB,cAAmC;IAEnC,IAAI,cAAc,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3C,4EAA4E;IAC5E,uDAAuD;IACvD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;aAC5C,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB;YAAE,OAAO,IAAI,CAAC;IACxF,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,OAAO,GAAG,2BAA2B,CAAC;IAC5C,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAE,CAAC;IAE7D,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,OAAO,iCAAiC,CAAC,CAAC,CAAC;IAC/E,IAAI,KAAK,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAE,CAAC;IAE7D,6DAA6D;IAC7D,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,kFAAkF,CAAC,CAAC;IACvG,IAAI,KAAK,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAE,CAAC;IAC7D,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,qGAAqG,CAAC,CAAC;IAC1H,IAAI,KAAK,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAE,CAAC;IAE7D,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * TuneOutCoordinator (issue #77) — the host-side machinery between a
3
+ * tuned-out channel and the subconscious resident.
4
+ *
5
+ * Responsibilities: classify diverted traffic (stamp everything, wake on
6
+ * addressed), acknowledge suppressed mentions deterministically, run the
7
+ * per-channel summary cadence, count wakes durably with max-wakes
8
+ * auto-cancel, and deliver the cancel dump.
9
+ *
10
+ * Voice doctrine ("no words in models' mouths"): everything the resident
11
+ * reads from the subconscious is the subconscious's verbatim text under its
12
+ * own participant name. Host-authored framing is system-styled and never
13
+ * voiced: `<tuned-out-backlog …>` wraps raw diverted messages (the
14
+ * <backscroll> precedent), and `[Tune-out: …]` bracket notices carry
15
+ * lifecycle facts (the [Gate: …] precedent).
16
+ */
17
+ import type { ContentBlock } from '@animalabs/membrane';
18
+ import type { StoredMessage } from '@animalabs/context-manager';
19
+ import type { ChannelRegistry, TuneOutParams } from '../mcpl/channel-registry.js';
20
+ import type { McplServerRegistry } from '../mcpl/server-registry.js';
21
+ /** Defaults for tune_out tool params. */
22
+ export declare const TUNE_OUT_DEFAULTS: {
23
+ readonly cadenceSeconds: 1800;
24
+ readonly backlogCap: 200;
25
+ readonly maxWakes: 5;
26
+ };
27
+ export interface TuneOutFrameworkHooks {
28
+ /** Deliver a message into an agent's window (per-agent seam). */
29
+ addMessage: (participant: string, content: ContentBlock[], metadata?: Record<string, unknown>, forAgent?: string) => string;
30
+ /** Queue an inference request for an agent. */
31
+ requestInference: (agentName: string, reason: string, source: string) => void;
32
+ /** The subconscious's registry/participant name. */
33
+ subconsciousName: () => string | null;
34
+ /** The primary resident's registry name. */
35
+ primaryName: () => string | null;
36
+ /** Read the primary's stored messages (shared slot, unfiltered). */
37
+ getStoredMessages: () => StoredMessage[];
38
+ /** Current chronicle head sequence (window anchor for new epochs). */
39
+ currentSequence: () => number;
40
+ /** Move the subconscious window anchor (never forward past active epochs). */
41
+ setSubconsciousAnchor: (sequence: number) => void;
42
+ /** True when a channel is bound to a conversation-fork agent. */
43
+ isForkBound: (channelId: string) => boolean;
44
+ /** Gate privileged-users list (#77: privileged authors wake, like mentions). */
45
+ isPrivilegedAuthor: (authorId: string | null | undefined) => boolean;
46
+ /** speak_in_channel master switch (recipe-side, canary-gated). */
47
+ allowChannelSpeech: () => boolean;
48
+ emitTrace: (event: {
49
+ type: string;
50
+ [key: string]: unknown;
51
+ }) => void;
52
+ }
53
+ export declare class TuneOutCoordinator {
54
+ private readonly channelRegistry;
55
+ private readonly serverRegistry;
56
+ private readonly hooks;
57
+ private cadenceTimers;
58
+ /** Duration-expiry timers (params.expiresAtMs); cleared on any cancel. */
59
+ private expiryTimers;
60
+ /** Channels with a wake invocation pending inside the coalescing window. */
61
+ private pendingWakes;
62
+ /** Diverted-message tallies since the last subconscious invocation, per channel key. */
63
+ private divertedSinceInvocation;
64
+ constructor(channelRegistry: ChannelRegistry, serverRegistry: McplServerRegistry, hooks: TuneOutFrameworkHooks);
65
+ private key;
66
+ /** Restart cadence + expiry timers and re-anchor after a process restart. */
67
+ resumeActiveEpochs(): void;
68
+ private armExpiry;
69
+ private listActive;
70
+ enter(serverId: string, channelId: string, opts: {
71
+ cadenceSeconds?: number;
72
+ backlogCap?: number;
73
+ maxWakes?: number;
74
+ durationSeconds?: number;
75
+ }, source: string): {
76
+ ok: true;
77
+ params: TuneOutParams;
78
+ } | {
79
+ ok: false;
80
+ error: string;
81
+ };
82
+ /**
83
+ * End a tune-out: durable flip, system-framed backlog dump into the
84
+ * resident's window (capped), lifecycle notice, one wake for the
85
+ * resident. `subconsciousNote` is the subconscious's own text (from its
86
+ * cancel_tuneout call) — delivered verbatim under its name, never
87
+ * synthesized here.
88
+ */
89
+ cancel(serverId: string, channelId: string, source: string, reason: string, subconsciousNote?: string): {
90
+ ok: true;
91
+ } | {
92
+ ok: false;
93
+ error: string;
94
+ };
95
+ /**
96
+ * Consulted by the framework for every incoming channel message BEFORE
97
+ * storage. Null = channel not tuned out (proceed normally). Otherwise the
98
+ * caller stamps `metadata.tuneOut = { epochId }`, stores the message
99
+ * without waking the residents, and this coordinator has already handled
100
+ * wake bookkeeping (coalesced subconscious invocation, durable count,
101
+ * suppression ack, possible auto-cancel).
102
+ */
103
+ onIncoming(serverId: string, channelId: string, messageId: string, tags: string[] | undefined, authorId?: string, gatePass?: boolean): {
104
+ epochId: string;
105
+ } | null;
106
+ private acknowledgeSuppressed;
107
+ private startCadence;
108
+ private stopCadence;
109
+ private scheduleWakeInvocation;
110
+ /**
111
+ * One subconscious turn: a system-framed trigger notice (bracket style —
112
+ * what happened, never what to say) plus standing dispositions, then an
113
+ * inference request. The merged view supplies the actual traffic.
114
+ */
115
+ private invokeSubconscious;
116
+ private dispositionsCache;
117
+ private get dispositionsStateId();
118
+ /**
119
+ * The dispositions as a fixed system-position ContextInjection for the
120
+ * subconscious's compiles (antra: deliver changes, never let them wash
121
+ * away — it has no long-term memory, so they must not live in window
122
+ * content). Bytes change only when a disposition changes: KV-friendly.
123
+ */
124
+ getDispositionsInjection(): {
125
+ namespace: string;
126
+ position: 'system';
127
+ content: Array<{
128
+ type: 'text';
129
+ text: string;
130
+ }>;
131
+ } | null;
132
+ private readDispositions;
133
+ noteDisposition(key: string, text: string): void;
134
+ handleSubconsciousTool(name: string, input: Record<string, unknown>): Promise<{
135
+ success: boolean;
136
+ data?: unknown;
137
+ error?: string;
138
+ isError?: boolean;
139
+ }>;
140
+ private findByChannel;
141
+ /** Stop all timers (framework shutdown). */
142
+ stop(): void;
143
+ }
144
+ //# sourceMappingURL=coordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator.d.ts","sourceRoot":"","sources":["../../../src/tune-out/coordinator.ts"],"names":[],"mappings":"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"}