@animalabs/agent-framework 0.26.1 → 0.27.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 +57 -7
- package/dist/src/agent.d.ts +263 -22
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +531 -54
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +1 -2
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/auto-continue.d.ts +33 -0
- package/dist/src/auto-continue.d.ts.map +1 -0
- package/dist/src/auto-continue.js +35 -0
- package/dist/src/auto-continue.js.map +1 -0
- package/dist/src/code-execution/isolate/scripts.d.ts +11 -0
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +5 -1
- package/dist/src/code-execution/isolate/scripts.js.map +1 -1
- package/dist/src/framework.d.ts +829 -156
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +3906 -1008
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +309 -46
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +2117 -395
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/index.d.ts +2 -2
- package/dist/src/gate/index.d.ts.map +1 -1
- package/dist/src/gate/index.js +1 -1
- package/dist/src/gate/index.js.map +1 -1
- package/dist/src/gate/recipe-reconcile.d.ts +103 -0
- package/dist/src/gate/recipe-reconcile.d.ts.map +1 -0
- package/dist/src/gate/recipe-reconcile.js +325 -0
- package/dist/src/gate/recipe-reconcile.js.map +1 -0
- package/dist/src/gate/types.d.ts +68 -8
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +433 -113
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +1152 -434
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +16 -8
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +29 -15
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/convert-block.d.ts.map +1 -1
- package/dist/src/mcpl/convert-block.js +14 -2
- package/dist/src/mcpl/convert-block.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts +7 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.js +32 -7
- package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
- package/dist/src/mcpl/index.d.ts +5 -1
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +4 -0
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +14 -2
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js +73 -28
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/prose-grammar.d.ts +4 -0
- package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
- package/dist/src/mcpl/prose-grammar.js +6 -0
- package/dist/src/mcpl/prose-grammar.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +3 -0
- package/dist/src/mcpl/protocol-family.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +20 -2
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +33 -11
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +63 -42
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +177 -145
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +380 -46
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +737 -78
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +82 -3
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +132 -7
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.d.ts +11 -0
- package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.js +21 -0
- package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
- package/dist/src/mcpl/tool-presence.d.ts +259 -0
- package/dist/src/mcpl/tool-presence.d.ts.map +1 -0
- package/dist/src/mcpl/tool-presence.js +628 -0
- package/dist/src/mcpl/tool-presence.js.map +1 -0
- package/dist/src/mcpl/tool-result-normalize.d.ts +9 -3
- package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
- package/dist/src/mcpl/tool-result-normalize.js +14 -6
- package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
- package/dist/src/mcpl/tool-signature.d.ts +39 -0
- package/dist/src/mcpl/tool-signature.d.ts.map +1 -0
- package/dist/src/mcpl/tool-signature.js +292 -0
- package/dist/src/mcpl/tool-signature.js.map +1 -0
- package/dist/src/mcpl/toolset-host.d.ts +200 -0
- package/dist/src/mcpl/toolset-host.d.ts.map +1 -0
- package/dist/src/mcpl/toolset-host.js +621 -0
- package/dist/src/mcpl/toolset-host.js.map +1 -0
- package/dist/src/mcpl/toolsets-module.d.ts +80 -0
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -0
- package/dist/src/mcpl/toolsets-module.js +431 -0
- package/dist/src/mcpl/toolsets-module.js.map +1 -0
- package/dist/src/mcpl/transport.d.ts +2 -2
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +3 -2
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +21 -1
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/mcpl/visible-content.d.ts +7 -9
- package/dist/src/mcpl/visible-content.d.ts.map +1 -1
- package/dist/src/mcpl/visible-content.js +21 -9
- package/dist/src/mcpl/visible-content.js.map +1 -1
- package/dist/src/module-registry.d.ts +23 -8
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +68 -15
- package/dist/src/module-registry.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/history/index.d.ts +2 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +51 -17
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +108 -6
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +273 -57
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/direct-write.d.ts +181 -0
- package/dist/src/modules/workspace/direct-write.d.ts.map +1 -0
- package/dist/src/modules/workspace/direct-write.js +384 -0
- package/dist/src/modules/workspace/direct-write.js.map +1 -0
- package/dist/src/modules/workspace/edit-text.d.ts +26 -12
- package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
- package/dist/src/modules/workspace/edit-text.js +58 -28
- package/dist/src/modules/workspace/edit-text.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts +10 -0
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +1 -1
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +246 -27
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +1216 -236
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/read-page.d.ts +41 -0
- package/dist/src/modules/workspace/read-page.d.ts.map +1 -0
- package/dist/src/modules/workspace/read-page.js +213 -0
- package/dist/src/modules/workspace/read-page.js.map +1 -0
- package/dist/src/modules/workspace/result-text.d.ts +67 -0
- package/dist/src/modules/workspace/result-text.d.ts.map +1 -0
- package/dist/src/modules/workspace/result-text.js +94 -0
- package/dist/src/modules/workspace/result-text.js.map +1 -0
- package/dist/src/modules/workspace/text.d.ts +29 -12
- package/dist/src/modules/workspace/text.d.ts.map +1 -1
- package/dist/src/modules/workspace/text.js +35 -33
- package/dist/src/modules/workspace/text.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +41 -17
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/sent-rows.d.ts +59 -0
- package/dist/src/sent-rows.d.ts.map +1 -0
- package/dist/src/sent-rows.js +145 -0
- package/dist/src/sent-rows.js.map +1 -0
- package/dist/src/silent-heartbeat.d.ts +17 -10
- package/dist/src/silent-heartbeat.d.ts.map +1 -1
- package/dist/src/silent-heartbeat.js.map +1 -1
- package/dist/src/stable-hash.d.ts +3 -0
- package/dist/src/stable-hash.d.ts.map +1 -0
- package/dist/src/stable-hash.js +15 -0
- package/dist/src/stable-hash.js.map +1 -0
- package/dist/src/stored-injections.d.ts +99 -0
- package/dist/src/stored-injections.d.ts.map +1 -0
- package/dist/src/stored-injections.js +106 -0
- package/dist/src/stored-injections.js.map +1 -0
- package/dist/src/text-as-stored.d.ts +4 -3
- package/dist/src/text-as-stored.d.ts.map +1 -1
- package/dist/src/text-as-stored.js +4 -3
- package/dist/src/text-as-stored.js.map +1 -1
- package/dist/src/thinking-drops.d.ts +81 -0
- package/dist/src/thinking-drops.d.ts.map +1 -0
- package/dist/src/thinking-drops.js +103 -0
- package/dist/src/thinking-drops.js.map +1 -0
- package/dist/src/tool-image-ledger.d.ts +9 -8
- package/dist/src/tool-image-ledger.d.ts.map +1 -1
- package/dist/src/tool-image-ledger.js +9 -8
- package/dist/src/tool-image-ledger.js.map +1 -1
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js +3 -1
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +4 -2
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +21 -6
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +186 -21
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +428 -89
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +17 -9
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +84 -37
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/types/agent.d.ts +186 -7
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +19 -2
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +24 -3
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +90 -7
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +57 -2
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/agent.ts +593 -60
- package/src/api/server.ts +1 -2
- package/src/auto-continue.ts +42 -0
- package/src/code-execution/isolate/scripts.ts +11 -3
- package/src/framework.ts +4004 -1026
- package/src/gate/event-gate.ts +2315 -389
- package/src/gate/index.ts +2 -1
- package/src/gate/recipe-reconcile.ts +355 -0
- package/src/gate/types.ts +70 -8
- package/src/index.ts +7 -4
- package/src/mcpl/channel-registry.ts +1220 -467
- package/src/mcpl/conversation-router.ts +34 -16
- package/src/mcpl/convert-block.ts +14 -1
- package/src/mcpl/hook-orchestrator.ts +33 -6
- package/src/mcpl/index.ts +6 -1
- package/src/mcpl/inference-router.ts +79 -34
- package/src/mcpl/prose-grammar.ts +7 -0
- package/src/mcpl/protocol-family.ts +2 -0
- package/src/mcpl/push-coalescer.ts +51 -13
- package/src/mcpl/reference-fetcher.ts +201 -162
- package/src/mcpl/references.ts +852 -78
- package/src/mcpl/server-connection.ts +145 -7
- package/src/mcpl/tool-lifecycle.ts +21 -0
- package/src/mcpl/tool-presence.ts +650 -0
- package/src/mcpl/tool-result-normalize.ts +18 -6
- package/src/mcpl/tool-signature.ts +285 -0
- package/src/mcpl/toolset-host.ts +637 -0
- package/src/mcpl/toolsets-module.ts +451 -0
- package/src/mcpl/transport.ts +3 -2
- package/src/mcpl/types.ts +22 -1
- package/src/mcpl/visible-content.ts +22 -9
- package/src/module-registry.ts +77 -24
- package/src/modules/discord/index.ts +1 -1
- package/src/modules/history/index.ts +48 -18
- package/src/modules/history/semantic.ts +308 -54
- package/src/modules/workspace/direct-write.ts +425 -0
- package/src/modules/workspace/edit-text.ts +82 -35
- package/src/modules/workspace/effects.ts +1 -1
- package/src/modules/workspace/index.ts +1274 -241
- package/src/modules/workspace/read-page.ts +222 -0
- package/src/modules/workspace/result-text.ts +119 -0
- package/src/modules/workspace/text.ts +58 -32
- package/src/modules/workspace/types.ts +41 -17
- package/src/sent-rows.ts +162 -0
- package/src/silent-heartbeat.ts +17 -10
- package/src/stable-hash.ts +14 -0
- package/src/stored-injections.ts +131 -0
- package/src/text-as-stored.ts +4 -3
- package/src/thinking-drops.ts +141 -0
- package/src/tool-image-ledger.ts +9 -8
- package/src/tool-presentation.ts +3 -1
- package/src/tool-result-guard.ts +19 -6
- package/src/tool-result-history.ts +488 -88
- package/src/tune-out/coordinator.ts +80 -37
- package/src/types/agent.ts +189 -7
- package/src/types/events.ts +19 -2
- package/src/types/framework.ts +21 -3
- package/src/types/module.ts +86 -7
- package/src/types/trace.ts +58 -2
|
@@ -9,18 +9,148 @@
|
|
|
9
9
|
* `maxChars` slice and re-introduces, one turn deferred, exactly the silent
|
|
10
10
|
* hallucination this feature exists to kill.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* apart from a lone UTF-16 surrogate,
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
12
|
+
* A content array (toolResultBlocks) is shown block by block, each block as
|
|
13
|
+
* both copies show it: text verbatim, apart from a lone UTF-16 surrogate,
|
|
14
|
+
* which becomes U+FFFD (#695), an image as a short
|
|
15
|
+
* `[image: mimeType, ~NKB]` placeholder, a reference as its stub, and any
|
|
16
|
+
* other block as a marker saying what it was and why it isn't shown. Anything
|
|
17
|
+
* else falls back to JSON, written by RFC-005's rules at every depth
|
|
18
|
+
* (historyJson): a reference is shown as its stub wherever it sits in the
|
|
19
|
+
* data, so its uri reaches history only as a link's line shows it where the
|
|
20
|
+
* host can't fetch the link and its uri can't carry a credential
|
|
21
|
+
* (shownLinkUri), and inline data that claims a uri or a disposition is
|
|
22
|
+
* withheld. A string is already text and is kept as it is.
|
|
20
23
|
*/
|
|
21
|
-
import { INLINE_WITHHELD_TEXT,
|
|
22
|
-
import { NON_STRING_TEXT_PLACEHOLDER } from './mcpl/visible-content.js';
|
|
24
|
+
import { AUDIO_NO_DATA_TEXT, AUDIO_NO_MEDIA_TYPE_TEXT, IMAGE_NO_DATA_TEXT, IMAGE_NO_MEDIA_TYPE_TEXT, INLINE_WITHHELD_TEXT, buildReferenceStub, classifyBlock, contentClaims, inlineNeverUri, referenceBlockForScript, referenceStubOrNull, referenceTypeLabel, resultClaims, } from './mcpl/references.js';
|
|
23
25
|
import { formatToolImagePlaceholder, toolResultCutAt } from './tool-image-ledger.js';
|
|
26
|
+
/**
|
|
27
|
+
* Whether a tool result's live copy goes as native blocks, an image among
|
|
28
|
+
* them, rather than as its stored text. It does when the result holds an
|
|
29
|
+
* image and its links' blocks and markers come to no more than `maxChars`
|
|
30
|
+
* together. Past that, the live copy is the stored text, cut at the cap, so
|
|
31
|
+
* no number of links and markers carries a result past it. A reference's
|
|
32
|
+
* stub and a withheld line go whole beside an image whatever their length,
|
|
33
|
+
* and past its cut the stored copy keeps the newest 64 image slots the live
|
|
34
|
+
* copy showed (#403). Text blocks don't count either: the native copy cuts
|
|
35
|
+
* each at the cap on its own. Both copies decide by this, from each block's
|
|
36
|
+
* kind and the length of its text.
|
|
37
|
+
*/
|
|
38
|
+
export function goesNative(blocks, maxChars) {
|
|
39
|
+
if (!blocks.some((block) => block.kind === 'image'))
|
|
40
|
+
return false;
|
|
41
|
+
if (!maxChars)
|
|
42
|
+
return true;
|
|
43
|
+
let bounded = 0;
|
|
44
|
+
for (const block of blocks)
|
|
45
|
+
if (block.kind === 'link' || block.kind === 'marker')
|
|
46
|
+
bounded += block.length;
|
|
47
|
+
return bounded <= maxChars;
|
|
48
|
+
}
|
|
49
|
+
/** The block types MCP defines for a tool result's content. */
|
|
50
|
+
const MCP_BLOCK_TYPES = new Set(['text', 'image', 'audio', 'resource', 'resource_link']);
|
|
51
|
+
/**
|
|
52
|
+
* Each element of `data` as both copies of a tool result show it, or null
|
|
53
|
+
* when `data` isn't a content array, so the result is data, written as JSON.
|
|
54
|
+
* An array is content when the result says so (`dataIsContent`, which the
|
|
55
|
+
* framework sets on an MCPL tool's result: every element of a protocol
|
|
56
|
+
* result's content is a block, of a type the host shows or not). Otherwise
|
|
57
|
+
* it's content only when every element is a block of one of MCP's own types,
|
|
58
|
+
* so a list of records of other types stays data. A module's array of
|
|
59
|
+
* records that all carry MCP's type names, such as `[{ type: 'image', url }]`,
|
|
60
|
+
* is read as content, and shows a marker for each record that isn't a block
|
|
61
|
+
* the lane can show.
|
|
62
|
+
*/
|
|
63
|
+
export function toolResultBlocks(data, dataIsContent, claims) {
|
|
64
|
+
if (!isContentArray(data, dataIsContent))
|
|
65
|
+
return null;
|
|
66
|
+
// The result's claims, read as one (ResultClaims): what any of its
|
|
67
|
+
// references says of a uri (a `disposition: never`, an expiry) holds for
|
|
68
|
+
// each block that names it, whatever their order.
|
|
69
|
+
const read = claims ?? contentClaims(data);
|
|
70
|
+
// Each text well formed, as the context store makes it when it writes, so
|
|
71
|
+
// the copy sent and the copy stored carry the same text (#695): a stub's
|
|
72
|
+
// labels and a link's uri come from the block too.
|
|
73
|
+
return data.map((raw) => {
|
|
74
|
+
const block = toolResultBlock(raw, read);
|
|
75
|
+
return 'text' in block ? { ...block, text: block.text.toWellFormed() } : block;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** Whether a tool result's data is a content array, which toolResultBlocks
|
|
79
|
+
* shows block by block. */
|
|
80
|
+
export function isContentArray(data, dataIsContent) {
|
|
81
|
+
return Array.isArray(data)
|
|
82
|
+
&& (dataIsContent === true || data.every((element) => isPlainRecord(element) && MCP_BLOCK_TYPES.has(element.type)));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The claims of a tool result's data (ResultClaims), as showing it reads
|
|
86
|
+
* them: a content array's from its elements (contentClaims), and any other
|
|
87
|
+
* data's by the walk serialization takes (claimsInHistory). For a decision
|
|
88
|
+
* made about the result outside showing it, such as which of its references
|
|
89
|
+
* to fetch, or for showing part of it as the rest of it claims. Null where a
|
|
90
|
+
* value stops the walk, so what the data claims is unknown (claimsInHistory).
|
|
91
|
+
*/
|
|
92
|
+
export function resultClaimsOf(data, dataIsContent) {
|
|
93
|
+
return isContentArray(data, dataIsContent) ? contentClaims(data) : claimsInHistory(data);
|
|
94
|
+
}
|
|
95
|
+
/** resultClaimsOf, throwing what stops the walk, for a caller that answers
|
|
96
|
+
* a stop itself: a script's text route, which fails then, as its view does
|
|
97
|
+
* (Ada-1017, room-203 #200374). */
|
|
98
|
+
export function resultClaimsOrThrow(data, dataIsContent) {
|
|
99
|
+
return isContentArray(data, dataIsContent) ? contentClaims(data) : claimsInHistoryOrThrow(data);
|
|
100
|
+
}
|
|
101
|
+
/** One element of a content array, as toolResultBlocks gives it. Only an
|
|
102
|
+
* image with data and a media type is shown as itself, as the push lane's
|
|
103
|
+
* converter shows one, and only text and images can be: this host's tool
|
|
104
|
+
* results carry nothing else, so inline audio is a marker here. */
|
|
105
|
+
function toolResultBlock(raw, claims) {
|
|
106
|
+
const c = classifyBlock(raw);
|
|
107
|
+
switch (c.kind) {
|
|
108
|
+
case 'text':
|
|
109
|
+
return { kind: 'text', text: raw.text };
|
|
110
|
+
case 'reference':
|
|
111
|
+
// A bounded stub, never the block's JSON, which would show its uri. A
|
|
112
|
+
// stub that shows an image type occupies a slot in save_recent_image's
|
|
113
|
+
// inventory (a failing one: its bytes are behind fetch_reference), so
|
|
114
|
+
// the index never slides past an image the resident saw. A link the
|
|
115
|
+
// host can't fetch, shown by its uri, is no slot, as no link was before
|
|
116
|
+
// links were references, when the result's JSON showed it. A link's
|
|
117
|
+
// block, stub or line, is `link`, counted toward the cap (goesNative).
|
|
118
|
+
return {
|
|
119
|
+
kind: c.testimony.fromLink ? 'link' : 'reference',
|
|
120
|
+
text: buildReferenceStub(c.testimony, 'from tool result', undefined, { unfetchedLinkUri: true, claims }),
|
|
121
|
+
};
|
|
122
|
+
case 'invalid':
|
|
123
|
+
return { kind: 'marker', text: c.shown };
|
|
124
|
+
case 'embedded':
|
|
125
|
+
// MCP's embedded resource shows as the block its contents make would
|
|
126
|
+
// show here. Its uri never shows.
|
|
127
|
+
if (c.contents.kind === 'block')
|
|
128
|
+
return toolResultBlock(c.contents.block, claims);
|
|
129
|
+
return c.contents.kind === 'withheld'
|
|
130
|
+
? { kind: 'withheld', text: INLINE_WITHHELD_TEXT }
|
|
131
|
+
: { kind: 'marker', text: c.contents.text };
|
|
132
|
+
case 'inline': {
|
|
133
|
+
if (c.contradiction)
|
|
134
|
+
return { kind: 'withheld', text: INLINE_WITHHELD_TEXT };
|
|
135
|
+
const b = raw;
|
|
136
|
+
const image = b.type === 'image';
|
|
137
|
+
if (b.data === '')
|
|
138
|
+
return { kind: 'marker', text: image ? IMAGE_NO_DATA_TEXT : AUDIO_NO_DATA_TEXT };
|
|
139
|
+
if (typeof b.mimeType !== 'string' || b.mimeType === '') {
|
|
140
|
+
return { kind: 'marker', text: image ? IMAGE_NO_MEDIA_TYPE_TEXT : AUDIO_NO_MEDIA_TYPE_TEXT };
|
|
141
|
+
}
|
|
142
|
+
if (image)
|
|
143
|
+
return { kind: 'image', data: b.data, mimeType: b.mimeType };
|
|
144
|
+
return { kind: 'marker', text: toolResultAudioText(b.mimeType, b.data) };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** What a tool result's inline audio shows as: its type and size, and that
|
|
149
|
+
* this host's tool results carry only text and images. */
|
|
150
|
+
function toolResultAudioText(mimeType, data) {
|
|
151
|
+
const what = [referenceTypeLabel(mimeType), formatSize(Math.floor(data.length * 3 / 4))].filter(Boolean).join(', ');
|
|
152
|
+
return `[Audio not shown: this host's tool results carry only text and images (${what}).]`;
|
|
153
|
+
}
|
|
24
154
|
/**
|
|
25
155
|
* House-safe default inline cap (chars) for tool results, error results, and
|
|
26
156
|
* script wake payloads (issue #89: a resident should never eat a
|
|
@@ -52,7 +182,7 @@ export function toolResultDataToHistoryString(data, maxChars, options) {
|
|
|
52
182
|
// RFC-005 shows only objects otherwise (shownInHistory), so a string has
|
|
53
183
|
// nothing for historyJson to stub or withhold.
|
|
54
184
|
const str = fromArray
|
|
55
|
-
?? (typeof data === 'string' ? data.toWellFormed() : historyJson(data))
|
|
185
|
+
?? (typeof data === 'string' ? data.toWellFormed() : historyJson(data, options?.claims))
|
|
56
186
|
?? '';
|
|
57
187
|
return maxChars ? truncateForHistory(str, maxChars) : str;
|
|
58
188
|
}
|
|
@@ -64,6 +194,11 @@ export function toolResultDataToHistoryString(data, maxChars, options) {
|
|
|
64
194
|
*
|
|
65
195
|
* Each field is read once: the direct Agent API passes its caller's own
|
|
66
196
|
* result here, unchecked, and a getter needn't answer the same way twice.
|
|
197
|
+
* Whatever stops reading or showing the data is UNCHECKED_RESULT_TEXT, with
|
|
198
|
+
* a log line naming the tool (`options.toolName`) and the error: the model's
|
|
199
|
+
* lane has no failure answer that doesn't wedge the round.
|
|
200
|
+
* Whether an array is content is the result's own `dataIsContent`, read for
|
|
201
|
+
* its truth as a result's flags are, whatever `options` says.
|
|
67
202
|
*
|
|
68
203
|
* Each text a tool result's copies carry is made well formed where it's
|
|
69
204
|
* taken from the result: a lone UTF-16 surrogate becomes U+FFFD, as the
|
|
@@ -74,6 +209,40 @@ export function toolResultDataToHistoryString(data, maxChars, options) {
|
|
|
74
209
|
* as before; a cut at a lone high half no longer steps back over it.
|
|
75
210
|
*/
|
|
76
211
|
export function nonErrorResultText(result, maxChars, options) {
|
|
212
|
+
// The blocks go to the caller only once the text is made from them, so a
|
|
213
|
+
// result that shows the notice has no live copy of its own: the notice is
|
|
214
|
+
// both copies' text.
|
|
215
|
+
let blocks;
|
|
216
|
+
const onBlocks = options?.onBlocks;
|
|
217
|
+
try {
|
|
218
|
+
// The result's own fields are read inside the try too: the direct Agent
|
|
219
|
+
// API's result is its caller's own object, whose `data` can be a getter.
|
|
220
|
+
const text = nonErrorResultTextOrThrow(result, maxChars, {
|
|
221
|
+
...options,
|
|
222
|
+
...(onBlocks ? { onBlocks: (spans, made) => { blocks = [spans, made]; } } : {}),
|
|
223
|
+
});
|
|
224
|
+
if (onBlocks && blocks)
|
|
225
|
+
onBlocks(blocks[0], blocks[1]);
|
|
226
|
+
return text;
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
// The model's lane has no failure answer that doesn't wedge the round, so
|
|
230
|
+
// whatever stops reading or showing a result, such as a getter that
|
|
231
|
+
// throws in a content array's block or a render deeper than the claims
|
|
232
|
+
// walk got (a script's routes call the serializer directly, and fail),
|
|
233
|
+
// is the notice, with a line naming the tool and the error, since it can
|
|
234
|
+
// also be a fault of this host's (Ada-1017, room-203 #201013).
|
|
235
|
+
console.error(`[tool-results] ${options?.toolName ? `tool '${options.toolName}'` : 'a tool'}: its result couldn't be read or shown, so the model reads that it was withheld: ${thrownLine(error)}`);
|
|
236
|
+
return maxChars ? truncateForHistory(UNCHECKED_RESULT_TEXT, maxChars) : UNCHECKED_RESULT_TEXT;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* nonErrorResultText's text, throwing whatever stops reading or showing the
|
|
241
|
+
* result, such as a stopped claims walk (historyJson), for a caller that
|
|
242
|
+
* answers a stop itself: a script's routes, which fail naming the tool
|
|
243
|
+
* (Ada-1017, room-203 #200374), never the model lane's notice.
|
|
244
|
+
*/
|
|
245
|
+
export function nonErrorResultTextOrThrow(result, maxChars, options) {
|
|
77
246
|
const data = result.data;
|
|
78
247
|
if (data === undefined || data === null) {
|
|
79
248
|
const error = result.error;
|
|
@@ -82,7 +251,16 @@ export function nonErrorResultText(result, maxChars, options) {
|
|
|
82
251
|
return maxChars ? truncateForHistory(text, maxChars) : text;
|
|
83
252
|
}
|
|
84
253
|
}
|
|
85
|
-
return toolResultDataToHistoryString(data, maxChars, options);
|
|
254
|
+
return toolResultDataToHistoryString(data, maxChars, { ...options, dataIsContent: Boolean(result.dataIsContent) });
|
|
255
|
+
}
|
|
256
|
+
/** The first line of what was thrown, for a log line. */
|
|
257
|
+
function thrownLine(error) {
|
|
258
|
+
try {
|
|
259
|
+
return (error instanceof Error ? error.message : String(error)).split('\n')[0] || 'no reason given';
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return 'a value that can\'t be read';
|
|
263
|
+
}
|
|
86
264
|
}
|
|
87
265
|
/** Bounded copy of an arbitrary string with the standard truncation notice,
|
|
88
266
|
* cut at the cap where a tool result's stored copy is cut at that cap
|
|
@@ -98,53 +276,20 @@ export function truncateForHistory(str, maxChars) {
|
|
|
98
276
|
return str.slice(0, toolResultCutAt(str, maxChars)) + '\n\n[truncated — original was ' + str.length + ' chars]';
|
|
99
277
|
}
|
|
100
278
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* reference anywhere in the data as its stub.
|
|
279
|
+
* A content array's history text (toolResultBlocks): each block's text, an
|
|
280
|
+
* image as its placeholder, joined by newlines. Null when `data` isn't a
|
|
281
|
+
* content array, for the fallback, historyJson.
|
|
105
282
|
*/
|
|
106
283
|
function tryHistoryStringFromContentArray(data, options) {
|
|
107
|
-
|
|
284
|
+
const blocks = toolResultBlocks(data, options?.dataIsContent, options?.claims);
|
|
285
|
+
if (blocks === null)
|
|
108
286
|
return null;
|
|
109
|
-
const parts =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// A text that isn't a string shows as the converters' placeholder,
|
|
116
|
-
// rather than sending the whole array to the JSON fallback.
|
|
117
|
-
// Read once: a direct Agent API result is unchecked.
|
|
118
|
-
const text = b.text;
|
|
119
|
-
parts.push({ kind: 'text', text: typeof text === 'string' ? text.toWellFormed() : NON_STRING_TEXT_PLACEHOLDER });
|
|
120
|
-
}
|
|
121
|
-
else if (isInlineContradiction(b)) {
|
|
122
|
-
// RFC-005 vector 2: checked before the data branch so inline data
|
|
123
|
-
// claiming bulk disposition is withheld, not rendered.
|
|
124
|
-
parts.push({ kind: 'withheld', text: INLINE_WITHHELD_TEXT });
|
|
125
|
-
}
|
|
126
|
-
else if (b.type === 'image' && typeof b.data === 'string' && typeof b.mimeType === 'string') {
|
|
127
|
-
// Decoded byte estimate from base64 length (3/4 ratio, rounded).
|
|
128
|
-
const approxBytes = Math.floor(b.data.length * 3 / 4);
|
|
129
|
-
const ref = options?.imageRef?.(blockIndex, { data: b.data, mimeType: b.mimeType }) ?? null;
|
|
130
|
-
parts.push({ kind: 'image', text: formatToolImagePlaceholder(b.mimeType, formatSize(approxBytes), ref) });
|
|
131
|
-
}
|
|
132
|
-
else if (b.type === 'resource' || ((b.type === 'image' || b.type === 'audio') && typeof b.uri === 'string')) {
|
|
133
|
-
// RFC-005 reference block: a bounded stub, never the raw block JSON
|
|
134
|
-
// (which used to inline the URI — and the whole array — into history).
|
|
135
|
-
// A stub that shows an image type occupies a slot in
|
|
136
|
-
// save_recent_image's inventory (a failing one — bytes live behind
|
|
137
|
-
// fetch_reference), so the index never slides past an image the
|
|
138
|
-
// resident saw.
|
|
139
|
-
const stub = referenceStubOrNull(b, 'from tool result');
|
|
140
|
-
if (stub === null)
|
|
141
|
-
return null;
|
|
142
|
-
parts.push({ kind: 'reference', text: stub });
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
287
|
+
const parts = blocks.map((block, blockIndex) => {
|
|
288
|
+
if (block.kind !== 'image')
|
|
289
|
+
return block;
|
|
290
|
+
const ref = options?.imageRef?.(blockIndex, { data: block.data, mimeType: block.mimeType }) ?? null;
|
|
291
|
+
return { kind: 'image', text: storedText(block, ref) };
|
|
292
|
+
});
|
|
148
293
|
if (options?.onBlocks) {
|
|
149
294
|
const spans = [];
|
|
150
295
|
let start = 0;
|
|
@@ -152,23 +297,47 @@ function tryHistoryStringFromContentArray(data, options) {
|
|
|
152
297
|
spans.push({ kind: part.kind, start, end: start + part.text.length });
|
|
153
298
|
start += part.text.length + 1; // the newline that joins them
|
|
154
299
|
}
|
|
155
|
-
options.onBlocks(spans);
|
|
300
|
+
options.onBlocks(spans, blocks);
|
|
156
301
|
}
|
|
157
302
|
return parts.map((part) => part.text).join('\n');
|
|
158
303
|
}
|
|
159
304
|
/**
|
|
160
305
|
* Whether RFC-005 governs a value of a tool result's data, wherever it sits.
|
|
161
|
-
* A reference is a `resource` with a uri
|
|
162
|
-
* `
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
306
|
+
* A reference is a `resource` with a uri at its top level, or an `image` or
|
|
307
|
+
* `audio` given by uri; inline data is an `image` or `audio` whose data is a
|
|
308
|
+
* string, or MCP's embedded resource, a `resource` with a uri under
|
|
309
|
+
* `resource` beside its contents. There a uri that is null, absent or empty
|
|
310
|
+
* counts as none, except under `resource`, where a string, an empty one
|
|
311
|
+
* included, tells an embedded resource, as the classifier tells it. Data
|
|
312
|
+
* that isn't a string isn't inline data, so a record that only shares one of
|
|
313
|
+
* those type names is data. A uri of any other value, string or not, counts,
|
|
314
|
+
* so a credential fails closed. MCP's `resource_link` (RFC-005 §8.1) is
|
|
315
|
+
* governed whatever value its uri has, so one whose uri isn't usable is its
|
|
316
|
+
* marker; one that names no uri is data here, where as a block of a content
|
|
317
|
+
* array, which the classifier reads, it's the marker.
|
|
168
318
|
*/
|
|
169
319
|
function governedByRfc005(b) {
|
|
170
|
-
if (b.type === 'resource')
|
|
171
|
-
|
|
320
|
+
if (b.type === 'resource') {
|
|
321
|
+
// MCP's embedded resource is told by a string uri under `resource`, ''
|
|
322
|
+
// included, as classifyBlock tells it; a uri of any other value there
|
|
323
|
+
// counts too, failing closed.
|
|
324
|
+
return hasUri(b.uri) || (isPlainRecord(b.resource) && (typeof b.resource.uri === 'string' || hasUri(b.resource.uri)));
|
|
325
|
+
}
|
|
326
|
+
// A link that names a uri is governed whatever its value, so one without
|
|
327
|
+
// a usable uri (empty, null, not a string) is its marker here, as on the
|
|
328
|
+
// content lanes, never its fields. One that names none is data at depth,
|
|
329
|
+
// on the model's lanes and a script's, as main has every link at depth:
|
|
330
|
+
// its fields hold no uri RFC-005 governs, though a value in them, such as
|
|
331
|
+
// an untyped object's `uri`, is data as anywhere else. As a block of a
|
|
332
|
+
// content array on the model's line, the classifier reads it, and there
|
|
333
|
+
// it's the marker; a script's view of a module's array, which reads every
|
|
334
|
+
// block here, shows its fields, as module data reached a script on main
|
|
335
|
+
// (Ada-1017, room-203 #202138).
|
|
336
|
+
// That's the object the script lane makes of a link whose uri it leaves
|
|
337
|
+
// out (referenceBlockForScript), which a script's view, given it again,
|
|
338
|
+
// leaves as it is, as it leaves a reference's stub.
|
|
339
|
+
if (b.type === 'resource_link')
|
|
340
|
+
return b.uri !== undefined;
|
|
172
341
|
if (b.type === 'image' || b.type === 'audio')
|
|
173
342
|
return typeof b.data === 'string' || hasUri(b.uri);
|
|
174
343
|
return false;
|
|
@@ -183,39 +352,134 @@ function isPlainRecord(value) {
|
|
|
183
352
|
* A value of a tool result's data as history shows it, by RFC-005's rules,
|
|
184
353
|
* wherever it sits in the data. A value RFC-005 governs (governedByRfc005) is
|
|
185
354
|
* shown as the reference lanes show it: a reference as its stub, an invalid
|
|
186
|
-
* one (such as a uri that isn't a string
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
355
|
+
* one (such as a uri that isn't a string) as the marker saying why it isn't
|
|
356
|
+
* shown, MCP's embedded resource as the text its stored copy shows (its
|
|
357
|
+
* text, an image's placeholder or a marker), never its fields, which carry
|
|
358
|
+
* its uri, and inline data that also claims a uri or a disposition as
|
|
359
|
+
* withheld. So of the uris RFC-005 governs, the only one shown is a link's,
|
|
360
|
+
* in its line, where the host can't fetch the link and its uri can't carry a
|
|
361
|
+
* credential (shownLinkUri). Inline data that claims neither is itself, as is
|
|
362
|
+
* any other value: a block of another type keeps its fields, a url among
|
|
191
363
|
* them, since RFC-005 governs references.
|
|
192
364
|
*/
|
|
193
|
-
function shownInHistory(value) {
|
|
365
|
+
function shownInHistory(value, claims) {
|
|
366
|
+
return walkedInHistory(value, (b) => {
|
|
367
|
+
// MCP's embedded resource is the text its stored copy shows: its text,
|
|
368
|
+
// an image's placeholder or its marker. As JSON it would carry its uri.
|
|
369
|
+
// Made well formed, as a content array's texts are (toolResultBlocks),
|
|
370
|
+
// since at the root this text is the result's whole text, as it's sent.
|
|
371
|
+
if (classifyBlock(b).kind === 'embedded')
|
|
372
|
+
return storedText(toolResultBlock(b, claims ?? resultClaims([]))).toWellFormed();
|
|
373
|
+
return referenceStubOrNull(b, 'from tool result', undefined, { unfetchedLinkUri: true, claims });
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* The one walk of a tool result's data that showing it and reading its
|
|
378
|
+
* claims both take: a value RFC-005 governs is text, from `asText`, except
|
|
379
|
+
* inline data that claims no uri or disposition, which is itself, as is any
|
|
380
|
+
* other value, so serialization descends into the same values each time, so
|
|
381
|
+
* long as each value's toJSON and getters answer the same each time they're
|
|
382
|
+
* read. `asText` gives a governed value's text, or null where it's itself.
|
|
383
|
+
*/
|
|
384
|
+
function walkedInHistory(value, asText) {
|
|
194
385
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
195
386
|
return value;
|
|
196
387
|
const b = value;
|
|
197
388
|
if (!governedByRfc005(b))
|
|
198
389
|
return value;
|
|
199
390
|
if ((b.type === 'image' || b.type === 'audio') && typeof b.data === 'string') {
|
|
200
|
-
// JSON writes every field, so here inline data beside a uri of any
|
|
391
|
+
// JSON writes every field, so here inline data beside a uri of any type
|
|
201
392
|
// is withheld, not only beside a string one as in the lanes, which show
|
|
202
|
-
// no uri: an object can carry a credential.
|
|
393
|
+
// no uri: an object can carry a credential. A null, absent or empty uri
|
|
394
|
+
// carries none (hasUri).
|
|
203
395
|
return hasUri(b.uri) || b.disposition !== undefined ? INLINE_WITHHELD_TEXT : value;
|
|
204
396
|
}
|
|
205
|
-
return
|
|
397
|
+
return asText(b) ?? value;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* What a tool result's data claims of each uri its references name
|
|
401
|
+
* (ResultClaims), read by the walk serialization takes (walkedInHistory),
|
|
402
|
+
* through toJSON, the fields of inline data and every depth it shows: so
|
|
403
|
+
* every reference whose stub the result shows counts, and nothing it doesn't
|
|
404
|
+
* show does. A value the walk can't read or write, such as a cycle or a
|
|
405
|
+
* getter that throws, stops it, and then it's null, since what the rest of
|
|
406
|
+
* the data claims is unknown. It's a walk of its own before the one that
|
|
407
|
+
* shows the data, so a value's toJSON and getters run in both: one that
|
|
408
|
+
* answers differently the second time can claim what the shown text doesn't
|
|
409
|
+
* show, or show a reference the claims never read. Only data no check has
|
|
410
|
+
* read through JSON can do either: a module's result is parsed JSON from its
|
|
411
|
+
* dispatch, and an MCPL peer's blocks are parsed wire JSON, so this is the
|
|
412
|
+
* direct Agent API's, whose results a host gives unchecked.
|
|
413
|
+
*/
|
|
414
|
+
function claimsInHistory(data) {
|
|
415
|
+
try {
|
|
416
|
+
return claimsInHistoryOrThrow(data);
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
// What the rest of the data claims is unknown, so a withheld result's
|
|
420
|
+
// kept fields keep nothing (keptWhenWithheld).
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/** claimsInHistory's walk, which throws what stops it. */
|
|
425
|
+
function claimsInHistoryOrThrow(data) {
|
|
426
|
+
const testimonies = [];
|
|
427
|
+
// Inline data's never, which walkedInHistory withholds before it asks
|
|
428
|
+
// `read`, and an embedded resource's, which `read` notes (inlineNeverUri).
|
|
429
|
+
const nevers = [];
|
|
430
|
+
const noteInline = (value) => {
|
|
431
|
+
if (!isPlainRecord(value) || (value.type !== 'image' && value.type !== 'audio'))
|
|
432
|
+
return;
|
|
433
|
+
const never = inlineNeverUri(value);
|
|
434
|
+
if (never !== null)
|
|
435
|
+
nevers.push(never);
|
|
436
|
+
};
|
|
437
|
+
const read = (b) => {
|
|
438
|
+
const c = classifyBlock(b);
|
|
439
|
+
if (c.kind === 'reference')
|
|
440
|
+
testimonies.push(c.testimony);
|
|
441
|
+
const never = inlineNeverUri(b, c);
|
|
442
|
+
if (never !== null)
|
|
443
|
+
nevers.push(never);
|
|
444
|
+
// Text ('') wherever showing gives text (shownInHistory), an embedded
|
|
445
|
+
// resource included, so the walk stops where showing stops, never
|
|
446
|
+
// reading such a value's fields, and descends only where showing does.
|
|
447
|
+
// Inline data never reaches here: walkedInHistory decides it first.
|
|
448
|
+
return c.kind === 'text' ? null : '';
|
|
449
|
+
};
|
|
450
|
+
noteInline(data);
|
|
451
|
+
const root = walkedInHistory(data, read);
|
|
452
|
+
// Its text is thrown away, so a string is written as 0, which keeps a
|
|
453
|
+
// long one from being copied for nothing; the walk descends as before.
|
|
454
|
+
if (root === data || typeof root !== 'string') {
|
|
455
|
+
JSON.stringify(data, (_key, value) => {
|
|
456
|
+
if (value !== data)
|
|
457
|
+
noteInline(value);
|
|
458
|
+
const walked = walkedInHistory(value, read);
|
|
459
|
+
return typeof walked === 'string' ? 0 : walked;
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return resultClaims(testimonies, nevers);
|
|
206
463
|
}
|
|
207
464
|
/**
|
|
208
465
|
* A value of a tool result's data as a script reads it, by history's rules
|
|
209
466
|
* (shownInHistory), as a JSON replacer applies them at every depth: what
|
|
210
467
|
* RFC-005 governs is shown as the script lane shows it, a reference as its
|
|
211
|
-
* stub's facts
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
468
|
+
* stub's facts, a link's uri among them only where the model's line for it
|
|
469
|
+
* would show it, an invalid one as the notice, and MCP's embedded resource as
|
|
470
|
+
* the block its contents make (referenceBlockForScript), and inline data
|
|
471
|
+
* that also claims a uri of any type or a disposition as the withheld
|
|
472
|
+
* notice, each notice as a text block. A null, absent or empty uri claims
|
|
473
|
+
* none (hasUri), so inline data beside one is itself. Any other value is
|
|
474
|
+
* itself. `claims` are those of everything the script's view shows
|
|
475
|
+
* (scriptViewClaims), and `line` the model's line for the result, its claims
|
|
476
|
+
* and its records as it reads them (ScriptLine), which decide where a link's
|
|
477
|
+
* uri is shown. What it gives is either a stub, a notice or a text block,
|
|
478
|
+
* which RFC-005 doesn't govern, or inline data that claims nothing, or the
|
|
479
|
+
* value itself, each of which it gives again, so applying it twice gives
|
|
480
|
+
* what applying it once gave.
|
|
217
481
|
*/
|
|
218
|
-
export function shownToScript(value) {
|
|
482
|
+
export function shownToScript(value, claims, line) {
|
|
219
483
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
220
484
|
return value;
|
|
221
485
|
const b = value;
|
|
@@ -224,27 +488,102 @@ export function shownToScript(value) {
|
|
|
224
488
|
if ((b.type === 'image' || b.type === 'audio') && typeof b.data === 'string') {
|
|
225
489
|
return hasUri(b.uri) || b.disposition !== undefined ? { type: 'text', text: INLINE_WITHHELD_TEXT } : value;
|
|
226
490
|
}
|
|
227
|
-
return referenceBlockForScript(b);
|
|
491
|
+
return referenceBlockForScript(b, undefined, claims, line);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* The claims (ResultClaims) of everything a script's view of a content array
|
|
495
|
+
* shows, nested fields included (Ada-1017, room-203 #200094), by the walk
|
|
496
|
+
* history's JSON takes (claimsInHistory): a script is shown the references
|
|
497
|
+
* nested in a block's fields, each as its stub, so a `never` among them
|
|
498
|
+
* withholds that uri from every block in the view. A link's uri is still
|
|
499
|
+
* shown only where the model's line shows it, by the claims the line reads
|
|
500
|
+
* (contentClaims, which reads a block and not its fields): reading more
|
|
501
|
+
* claims can loosen as well as tighten, since an expiry makes a link one the
|
|
502
|
+
* host can't fetch (referenceBlockForScript's `line`). An MCPL peer's
|
|
503
|
+
* top-level block the classifier rejects is
|
|
504
|
+
* its notice, its fields unshown, so they don't count (`fromServer`). It
|
|
505
|
+
* throws what stops the walk, such as a value too deep to convert or a
|
|
506
|
+
* getter that throws, and the script's view then isn't written: the
|
|
507
|
+
* treatment never runs without the claims it reads (Ada-1017, room-203
|
|
508
|
+
* #200250), whether or not its own walk, with other replacers and so other
|
|
509
|
+
* stack depths, would stop at the same value.
|
|
510
|
+
*/
|
|
511
|
+
export function scriptViewClaims(content, fromServer) {
|
|
512
|
+
return claimsInHistoryOrThrow(fromServer ? content.filter((block) => classifyBlock(block).kind !== 'invalid') : content);
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* A value nested in an MCPL peer's content block as a script reads it, as a
|
|
516
|
+
* JSON replacer applies it inside the blocks: what RFC-005 governs
|
|
517
|
+
* (governedByRfc005, history's guard, since the classifier would make a
|
|
518
|
+
* notice of an object that isn't a content block, such as a peer's own
|
|
519
|
+
* nested data) gets the treatment the script lane gives a
|
|
520
|
+
* peer's block (referenceBlockForScript), its reference bound to the server
|
|
521
|
+
* that sent it, and any other value is itself. A stub or a notice isn't
|
|
522
|
+
* governed, so a block the top-level pass already treated is left as it is.
|
|
523
|
+
* `claims` are those of the script's whole view (scriptViewClaims).
|
|
524
|
+
*/
|
|
525
|
+
export function shownToScriptFromServer(value, serverId, claims, line) {
|
|
526
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
527
|
+
return value;
|
|
528
|
+
const b = value;
|
|
529
|
+
return governedByRfc005(b) ? referenceBlockForScript(b, serverId, claims, line) : value;
|
|
228
530
|
}
|
|
229
531
|
/**
|
|
230
532
|
* The text RFC-005 shows a value of a tool result's data as, when it shows it
|
|
231
533
|
* as text rather than as its fields: a reference's stub, an invalid one's
|
|
232
|
-
*
|
|
534
|
+
* marker, inline data withheld, or what an embedded resource's stored copy
|
|
535
|
+
* shows. Null for any other value.
|
|
233
536
|
*/
|
|
234
537
|
export function rfc005TextOf(value) {
|
|
538
|
+
// A single value claims only what it says itself, which its stub reads.
|
|
235
539
|
const shown = shownInHistory(value);
|
|
236
540
|
return shown !== value && typeof shown === 'string' ? shown : null;
|
|
237
541
|
}
|
|
542
|
+
/**
|
|
543
|
+
* What the model's lane shows for a tool result it couldn't read through,
|
|
544
|
+
* check for references and show (nonErrorResultText, the lane's one catch,
|
|
545
|
+
* which logs the tool and the error): the tool ran, and its result isn't
|
|
546
|
+
* shown, rather than shown with fewer claims, where a link could show a uri a
|
|
547
|
+
* reference in the result withholds, and rather than a throw, which would
|
|
548
|
+
* leave the round unstored and unsent. Where the walk of its claims stops
|
|
549
|
+
* (historyJson throws then), the claims walk meets data no JSON can hold,
|
|
550
|
+
* such as a cycle or a BigInt, or data too deep to write, before the render
|
|
551
|
+
* does, so such data shows this, where the render threw (Desmond-2579's
|
|
552
|
+
* run); so does a render that stops after the walk, or a getter that throws
|
|
553
|
+
* (Ada-1017, room-203 #200344, #201013). The round completes, as a withheld
|
|
554
|
+
* result's kept fields keep nothing then (keptWhenWithheld). A script never
|
|
555
|
+
* gets it: its routes call the serializer, or nonErrorResultTextOrThrow,
|
|
556
|
+
* directly, whose walk throws where it stops, and fail naming the tool
|
|
557
|
+
* (#200374). An error result's own error field is read as before, and so
|
|
558
|
+
* are the fields the tool-result guard keeps of a result it withholds.
|
|
559
|
+
*/
|
|
560
|
+
export const UNCHECKED_RESULT_TEXT = "[Result not shown: the tool ran, but its result couldn't be read through, checked for references and shown, so this host withheld it.]";
|
|
238
561
|
/**
|
|
239
562
|
* JSON of a tool result's data with each value in it shown as history shows
|
|
240
|
-
* it (shownInHistory), at any depth
|
|
241
|
-
*
|
|
563
|
+
* it (shownInHistory), at any depth, by the claims `given` or else its own
|
|
564
|
+
* (claimsInHistoryOrThrow), throwing where that walk stops. Data that is
|
|
565
|
+
* itself shown as text, such as a bare reference, is that text.
|
|
242
566
|
*/
|
|
243
|
-
function historyJson(data) {
|
|
244
|
-
|
|
567
|
+
function historyJson(data, given) {
|
|
568
|
+
// A walk that stopped can't say what the data claims, and showing it with
|
|
569
|
+
// no claims would let a link show a uri a reference withholds, should the
|
|
570
|
+
// render's own walk, with other replacers, get past the same value.
|
|
571
|
+
// Where the walk stops this throws: the model's lane answers with
|
|
572
|
+
// UNCHECKED_RESULT_TEXT and a log line (nonErrorResultText), a script's
|
|
573
|
+
// routes with a failure naming the tool.
|
|
574
|
+
const claims = given ?? claimsInHistoryOrThrow(data);
|
|
575
|
+
const root = shownInHistory(data, claims);
|
|
245
576
|
if (root !== data && typeof root === 'string')
|
|
246
577
|
return root;
|
|
247
|
-
return JSON.stringify(data, (_key, value) => shownInHistory(value));
|
|
578
|
+
return JSON.stringify(data, (_key, value) => shownInHistory(value, claims));
|
|
579
|
+
}
|
|
580
|
+
/** A block's text in the stored copy: an image as its placeholder, with the
|
|
581
|
+
* retention ref given, and any other block as its text. */
|
|
582
|
+
function storedText(block, ref = null) {
|
|
583
|
+
if (block.kind !== 'image')
|
|
584
|
+
return block.text;
|
|
585
|
+
// Decoded byte estimate from base64 length (3/4 ratio, rounded).
|
|
586
|
+
return formatToolImagePlaceholder(block.mimeType, formatSize(Math.floor(block.data.length * 3 / 4)), ref);
|
|
248
587
|
}
|
|
249
588
|
function formatSize(bytes) {
|
|
250
589
|
if (bytes < 1024)
|