@animalabs/agent-framework 0.27.0 → 0.28.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 +56 -15
- package/dist/src/agent.d.ts +29 -2
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +69 -11
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +4 -2
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/auto-continue.d.ts +3 -2
- package/dist/src/auto-continue.d.ts.map +1 -1
- package/dist/src/auto-continue.js +4 -3
- package/dist/src/auto-continue.js.map +1 -1
- package/dist/src/cut-off-round.d.ts +24 -0
- package/dist/src/cut-off-round.d.ts.map +1 -0
- package/dist/src/cut-off-round.js +87 -0
- package/dist/src/cut-off-round.js.map +1 -0
- package/dist/src/framework.d.ts +304 -89
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +2577 -678
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +99 -3
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +538 -13
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/index.d.ts +2 -1
- package/dist/src/gate/index.d.ts.map +1 -1
- package/dist/src/gate/index.js +1 -0
- package/dist/src/gate/index.js.map +1 -1
- package/dist/src/gate/reply-judge.d.ts +116 -0
- package/dist/src/gate/reply-judge.d.ts.map +1 -0
- package/dist/src/gate/reply-judge.js +467 -0
- package/dist/src/gate/reply-judge.js.map +1 -0
- package/dist/src/gate/types.d.ts +161 -0
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/heartbeat/heartbeat.d.ts +184 -0
- package/dist/src/heartbeat/heartbeat.d.ts.map +1 -0
- package/dist/src/heartbeat/heartbeat.js +752 -0
- package/dist/src/heartbeat/heartbeat.js.map +1 -0
- package/dist/src/heartbeat/reminder-time.d.ts +5 -0
- package/dist/src/heartbeat/reminder-time.d.ts.map +1 -0
- package/dist/src/heartbeat/reminder-time.js +107 -0
- package/dist/src/heartbeat/reminder-time.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/capability-mask.d.ts.map +1 -1
- package/dist/src/mcpl/capability-mask.js +3 -2
- package/dist/src/mcpl/capability-mask.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +2 -1
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +26 -10
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
- package/dist/src/mcpl/inbound-source.js +40 -69
- package/dist/src/mcpl/inbound-source.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +18 -8
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +50 -10
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tags.d.ts +9 -2
- package/dist/src/mcpl/tags.d.ts.map +1 -1
- package/dist/src/mcpl/tags.js +12 -2
- package/dist/src/mcpl/tags.js.map +1 -1
- package/dist/src/mcpl/tool-presence.d.ts +66 -14
- package/dist/src/mcpl/tool-presence.d.ts.map +1 -1
- package/dist/src/mcpl/tool-presence.js +134 -65
- package/dist/src/mcpl/tool-presence.js.map +1 -1
- package/dist/src/mcpl/tool-signature.d.ts +10 -3
- package/dist/src/mcpl/tool-signature.d.ts.map +1 -1
- package/dist/src/mcpl/tool-signature.js +47 -18
- package/dist/src/mcpl/tool-signature.js.map +1 -1
- package/dist/src/mcpl/toolset-host.d.ts +88 -37
- package/dist/src/mcpl/toolset-host.d.ts.map +1 -1
- package/dist/src/mcpl/toolset-host.js +207 -108
- package/dist/src/mcpl/toolset-host.js.map +1 -1
- package/dist/src/mcpl/toolsets-module.d.ts +14 -11
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -1
- package/dist/src/mcpl/toolsets-module.js +141 -49
- package/dist/src/mcpl/toolsets-module.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +3 -1
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +4 -1
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +9 -2
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +22 -11
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/workspace/answers.d.ts +22 -0
- package/dist/src/modules/workspace/answers.d.ts.map +1 -0
- package/dist/src/modules/workspace/answers.js +123 -0
- package/dist/src/modules/workspace/answers.js.map +1 -0
- package/dist/src/modules/workspace/branch-intent.d.ts +5 -3
- package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
- package/dist/src/modules/workspace/branch-intent.js +6 -2
- package/dist/src/modules/workspace/branch-intent.js.map +1 -1
- package/dist/src/modules/workspace/direct-write.d.ts +3 -0
- package/dist/src/modules/workspace/direct-write.d.ts.map +1 -1
- package/dist/src/modules/workspace/direct-write.js +57 -24
- package/dist/src/modules/workspace/direct-write.js.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.d.ts +33 -11
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +48 -17
- package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
- package/dist/src/modules/workspace/edit-text.d.ts +2 -1
- package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
- package/dist/src/modules/workspace/edit-text.js +4 -2
- package/dist/src/modules/workspace/edit-text.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +20 -13
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +62 -51
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +934 -531
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +75 -63
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +366 -180
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/read-page.d.ts.map +1 -1
- package/dist/src/modules/workspace/read-page.js +7 -1
- package/dist/src/modules/workspace/read-page.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +26 -16
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/result-text.d.ts +1 -0
- package/dist/src/modules/workspace/result-text.d.ts.map +1 -1
- package/dist/src/modules/workspace/result-text.js.map +1 -1
- package/dist/src/modules/workspace/sync.d.ts +9 -0
- package/dist/src/modules/workspace/sync.d.ts.map +1 -1
- package/dist/src/modules/workspace/sync.js +14 -0
- package/dist/src/modules/workspace/sync.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +96 -43
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
- package/dist/src/modules/workspace/watcher.js +8 -3
- package/dist/src/modules/workspace/watcher.js.map +1 -1
- package/dist/src/prose-drafts.d.ts +4 -3
- package/dist/src/prose-drafts.d.ts.map +1 -1
- package/dist/src/prose-drafts.js +3 -2
- package/dist/src/prose-drafts.js.map +1 -1
- package/dist/src/reasoning-loss-notice.d.ts +23 -0
- package/dist/src/reasoning-loss-notice.d.ts.map +1 -0
- package/dist/src/reasoning-loss-notice.js +50 -0
- package/dist/src/reasoning-loss-notice.js.map +1 -0
- package/dist/src/reasoning-loss.d.ts +24 -0
- package/dist/src/reasoning-loss.d.ts.map +1 -0
- package/dist/src/reasoning-loss.js +102 -0
- package/dist/src/reasoning-loss.js.map +1 -0
- package/dist/src/send-tools.d.ts +3 -2
- package/dist/src/send-tools.d.ts.map +1 -1
- package/dist/src/send-tools.js +3 -2
- package/dist/src/send-tools.js.map +1 -1
- package/dist/src/sent-rows.js +1 -1
- package/dist/src/thinking-drops.d.ts +4 -11
- package/dist/src/thinking-drops.d.ts.map +1 -1
- package/dist/src/thinking-drops.js +7 -14
- package/dist/src/thinking-drops.js.map +1 -1
- package/dist/src/tool-args.d.ts +12 -0
- package/dist/src/tool-args.d.ts.map +1 -1
- package/dist/src/tool-args.js +35 -1
- package/dist/src/tool-args.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +8 -4
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +23 -11
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts +4 -1
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/types/agent.d.ts +40 -9
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +18 -8
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +15 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +5 -2
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +34 -1
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/agent.ts +72 -11
- package/src/api/server.ts +4 -2
- package/src/auto-continue.ts +4 -3
- package/src/cut-off-round.ts +98 -0
- package/src/framework.ts +2694 -700
- package/src/gate/event-gate.ts +579 -15
- package/src/gate/index.ts +8 -0
- package/src/gate/reply-judge.ts +512 -0
- package/src/gate/types.ts +164 -1
- package/src/heartbeat/heartbeat.ts +804 -0
- package/src/heartbeat/reminder-time.ts +107 -0
- package/src/index.ts +4 -0
- package/src/mcpl/capability-mask.ts +3 -2
- package/src/mcpl/channel-registry.ts +2 -1
- package/src/mcpl/inbound-source.ts +42 -69
- package/src/mcpl/server-connection.ts +56 -16
- package/src/mcpl/tags.ts +13 -2
- package/src/mcpl/tool-presence.ts +133 -67
- package/src/mcpl/tool-signature.ts +51 -20
- package/src/mcpl/toolset-host.ts +197 -107
- package/src/mcpl/toolsets-module.ts +143 -49
- package/src/mcpl/types.ts +3 -1
- package/src/module-registry.ts +8 -2
- package/src/modules/history/index.ts +22 -12
- package/src/modules/workspace/answers.ts +133 -0
- package/src/modules/workspace/branch-intent.ts +10 -5
- package/src/modules/workspace/direct-write.ts +58 -23
- package/src/modules/workspace/disk-agreement.ts +52 -21
- package/src/modules/workspace/edit-text.ts +7 -5
- package/src/modules/workspace/effects.ts +21 -12
- package/src/modules/workspace/index.ts +927 -541
- package/src/modules/workspace/observe.ts +381 -177
- package/src/modules/workspace/read-page.ts +7 -1
- package/src/modules/workspace/reconcile.ts +27 -15
- package/src/modules/workspace/result-text.ts +2 -0
- package/src/modules/workspace/sync.ts +16 -0
- package/src/modules/workspace/types.ts +96 -43
- package/src/modules/workspace/watcher.ts +8 -3
- package/src/prose-drafts.ts +4 -3
- package/src/reasoning-loss-notice.ts +62 -0
- package/src/reasoning-loss.ts +120 -0
- package/src/send-tools.ts +3 -2
- package/src/sent-rows.ts +1 -1
- package/src/thinking-drops.ts +10 -16
- package/src/tool-args.ts +34 -1
- package/src/tool-result-guard.ts +26 -11
- package/src/tune-out/tools.ts +4 -1
- package/src/types/agent.ts +40 -9
- package/src/types/events.ts +18 -8
- package/src/types/framework.ts +15 -0
- package/src/types/module.ts +5 -2
- package/src/types/trace.ts +36 -1
package/src/thinking-drops.ts
CHANGED
|
@@ -29,10 +29,11 @@ export interface CompiledForRequest {
|
|
|
29
29
|
recordedAfter?: number;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/** What a stream's driver reads a call's dropped blocks against (`StartStreamResult`'s
|
|
32
|
+
/** What a stream's driver reads a call's dropped blocks against (`StartStreamResult`'s fields, together). */
|
|
33
33
|
export interface StreamThinkingDrops {
|
|
34
34
|
place: (entries: readonly ThinkingBindingEntry[]) => PlacedDrop[];
|
|
35
35
|
recordedAfter?: number;
|
|
36
|
+
branch?: string;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/** A signed block's identity on the wire: a thinking block's signature, a redacted block's data. */
|
|
@@ -112,26 +113,19 @@ export function splitThinkingDrops(
|
|
|
112
113
|
/** Binding failures the context manager's record doesn't track: the account's, not the conversation's. */
|
|
113
114
|
const UNTRACKED_REASONS = new Set(['organization_binding_mismatch', 'end_user_binding_mismatch']);
|
|
114
115
|
|
|
115
|
-
/**
|
|
116
|
-
* When an agent's unexpected drops call for an operator alert: at the first call that shows any, and again only
|
|
117
|
-
* when a call shows more than the alert last counted. Each call of a stream, and each stream until a strip takes
|
|
118
|
-
* them out, drops the same blocks again, so an unchanged count isn't news. A call that shows no unexpected drop, its
|
|
119
|
-
* report empty, absent or naming only expected ones, ends the incident: the blocks it counted no longer drop. The next
|
|
120
|
-
* one alerts afresh. It's held in memory, so a restart mid-incident alerts once more, and an agent's removal ends its
|
|
121
|
-
* incident (`forget`).
|
|
122
|
-
*/
|
|
123
116
|
export class ThinkingLossIncidents {
|
|
124
|
-
private readonly alerted = new Map<string, number>();
|
|
117
|
+
private readonly alerted = new Map<string, { count: number; scope: string | undefined }>();
|
|
125
118
|
|
|
126
|
-
/** A call's report of `
|
|
127
|
-
observe(agent: string,
|
|
128
|
-
|
|
119
|
+
/** A call's report of `count` drops for `agent` in `scope`: the count to alert with, or undefined for no alert. */
|
|
120
|
+
observe(agent: string, count: number, scope?: string): number | undefined {
|
|
121
|
+
const open = this.alerted.get(agent);
|
|
122
|
+
if (count === 0) {
|
|
129
123
|
this.alerted.delete(agent);
|
|
130
124
|
return undefined;
|
|
131
125
|
}
|
|
132
|
-
if (
|
|
133
|
-
this.alerted.set(agent,
|
|
134
|
-
return
|
|
126
|
+
if (open && open.scope === scope && count <= open.count) return undefined;
|
|
127
|
+
this.alerted.set(agent, { count, scope });
|
|
128
|
+
return count;
|
|
135
129
|
}
|
|
136
130
|
|
|
137
131
|
/** The agent is gone: its incident, if one is open, ends with it. */
|
package/src/tool-args.ts
CHANGED
|
@@ -67,6 +67,39 @@ function shown(value: unknown): string {
|
|
|
67
67
|
return head;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
function notAnObject(input: unknown, field = 'input'): string {
|
|
71
|
+
return `its ${field} must be an object, and this call gave ${withArticle(kindOf(input))}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function markedInputRefusal(call: { name: string; unparseableInput?: string }): string | undefined {
|
|
75
|
+
const text = call.unparseableInput;
|
|
76
|
+
if (text === undefined) return undefined;
|
|
77
|
+
// When a stream ends before a call's block stops, membrane marks the call
|
|
78
|
+
// with what had arrived of its arguments: nothing, a part, or a whole
|
|
79
|
+
// object. A block still open when a stream breaks the API's block order
|
|
80
|
+
// is marked the same way, and one that had stopped with its object's JSON,
|
|
81
|
+
// so the arguments may be another call's (ToolUseContent.unparseableInput).
|
|
82
|
+
if (/^[ \t\n\r]*$/.test(text)) return `Nothing was done: ${call.name} its arguments never arrived; call it again with them whole.`;
|
|
83
|
+
let parsed: unknown;
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(text);
|
|
86
|
+
} catch {
|
|
87
|
+
return `Nothing was done: ${call.name} its arguments must be one JSON object, and this call gave text that isn't JSON, which may have been cut off: ${shown(text)}; call it again with one JSON object.`;
|
|
88
|
+
}
|
|
89
|
+
if (parsed === null) return undefined;
|
|
90
|
+
if (typeof parsed !== 'object' || Array.isArray(parsed)) return `Nothing was done: ${call.name} ${notAnObject(parsed)}.`;
|
|
91
|
+
return `Nothing was done: ${call.name} its arguments came in a response that didn't arrive whole and in order, so they can't be taken as this call's; call it again.`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function cutOffRefusal(call: { name: string }): string {
|
|
95
|
+
return `Nothing was done: ${call.name} didn't run, because the response it was in was cut off at the output limit. ` +
|
|
96
|
+
'To make the call, send less in one response: shorter text before it, shorter arguments, or fewer calls at once.';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function asCalled<T extends { name: string; input?: unknown; unparseableInput?: string }>(call: T): T {
|
|
100
|
+
return call.unparseableInput !== undefined && markedInputRefusal(call) === undefined ? { ...call, input: {} } : call;
|
|
101
|
+
}
|
|
102
|
+
|
|
70
103
|
/** A list in prose: `a`, `a or b`, `a, b or c`. */
|
|
71
104
|
function oneOf(items: string[]): string {
|
|
72
105
|
return items.length <= 1 ? items.join('') : `${items.slice(0, -1).join(', ')} or ${items[items.length - 1]}`;
|
|
@@ -169,7 +202,7 @@ export function toolArgsProblems(
|
|
|
169
202
|
if (!schema) return [];
|
|
170
203
|
const absent = input === undefined || input === null;
|
|
171
204
|
if (!absent && (typeof input !== 'object' || Array.isArray(input))) {
|
|
172
|
-
return [
|
|
205
|
+
return [notAnObject(input, options.field)];
|
|
173
206
|
}
|
|
174
207
|
const obj = (absent ? {} : input) as Record<string, unknown>;
|
|
175
208
|
const properties = (isMap(schema.properties) ? schema.properties : {}) as Record<string, { type?: unknown; nullable?: unknown; enum?: unknown } | undefined>;
|
package/src/tool-result-guard.ts
CHANGED
|
@@ -9,6 +9,14 @@ import { resultClaimsOf, rfc005TextOf, toolResultDataToHistoryString } from './t
|
|
|
9
9
|
import { safeSlice } from './safe-slice.js';
|
|
10
10
|
|
|
11
11
|
export const TOOL_RESULT_GUARD_NOTICE = 'Tool result withheld by the guard. The tool has already executed.';
|
|
12
|
+
export type WithheldNoticeKind = 'ran' | 'refused' | 'error';
|
|
13
|
+
const GUARD_NOTICES: Record<WithheldNoticeKind, string> = {
|
|
14
|
+
ran: TOOL_RESULT_GUARD_NOTICE,
|
|
15
|
+
refused: "Tool result withheld by the guard. The tool didn't run.",
|
|
16
|
+
error: "Tool result withheld by the guard. The call returned an error, and this notice can't say whether the tool ran.",
|
|
17
|
+
};
|
|
18
|
+
const noticeKind = (result: CompletedToolCall['result'] | undefined): WithheldNoticeKind =>
|
|
19
|
+
result?.refused === true ? 'refused' : result?.isError === true ? 'error' : 'ran';
|
|
12
20
|
export const TOOL_RESULT_GUARD_AUDIT_STATE = 'framework/tool-result-guard';
|
|
13
21
|
|
|
14
22
|
/** Where a withheld result's original went: a workspace path; the path a
|
|
@@ -54,9 +62,12 @@ export interface WithheldHost {
|
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
/**
|
|
57
|
-
* The stub a withheld result settles to: #159's neutral notice
|
|
58
|
-
*
|
|
59
|
-
*
|
|
65
|
+
* The stub a withheld result settles to: #159's neutral notice for its
|
|
66
|
+
* `kind` (a result that isn't an error, a refused call's, or another error's),
|
|
67
|
+
* then where the original is, then, for an error whose original is in a file
|
|
68
|
+
* known written, a line telling the agent to read it, then, when its tool
|
|
69
|
+
* marked any, the fields kept from its result (`kept`, from
|
|
70
|
+
* keptWhenWithheld). It names no refusal and no
|
|
60
71
|
* category (#159 keeps those in operational logs), so reading the rest back
|
|
61
72
|
* is the agent's deliberate choice (agent-framework #277). The place holds
|
|
62
73
|
* the result as stored, which for one over the inline cap is its capped
|
|
@@ -67,7 +78,7 @@ export interface WithheldHost {
|
|
|
67
78
|
* save failed is still queued in memory, and a restart before its retry
|
|
68
79
|
* loses it.
|
|
69
80
|
*/
|
|
70
|
-
export function withheldResultNotice(spill: WithheldSpill, auditDurable: boolean, kept?: string): string {
|
|
81
|
+
export function withheldResultNotice(spill: WithheldSpill, auditDurable: boolean, kept?: string, kind: WithheldNoticeKind = 'ran'): string {
|
|
71
82
|
const audit = `the guard's audit record (${TOOL_RESULT_GUARD_AUDIT_STATE}), for an operator`;
|
|
72
83
|
const keptIn = (only: string) => auditDurable
|
|
73
84
|
? `is kept ${only}in ${audit}`
|
|
@@ -80,8 +91,10 @@ export function withheldResultNotice(spill: WithheldSpill, auditDurable: boolean
|
|
|
80
91
|
: spill.error === undefined
|
|
81
92
|
? `Its result is in workspace file ${spill.path}.`
|
|
82
93
|
: `Writing its result to workspace file ${spill.path} failed, so it ${keptIn('only ')}.`;
|
|
94
|
+
const readable = kind === 'error' && !!spill && 'path' in spill && spill.error === undefined && !spill.unfinished;
|
|
95
|
+
const readFirst = readable ? ' Read it before calling again.' : '';
|
|
83
96
|
const fields = kept === undefined ? '' : ` Kept from its result: ${kept}`;
|
|
84
|
-
return `${
|
|
97
|
+
return `${GUARD_NOTICES[kind]} ${place}${readFirst}${fields}`;
|
|
85
98
|
}
|
|
86
99
|
|
|
87
100
|
/**
|
|
@@ -140,6 +153,7 @@ interface PendingBatch {
|
|
|
140
153
|
/** What each result's stub keeps if the batch is withheld, by its index in
|
|
141
154
|
* content: the fields its tool marked (keptWhenWithheld). */
|
|
142
155
|
kept: Map<number, string>;
|
|
156
|
+
notices: Map<string, WithheldNoticeKind>;
|
|
143
157
|
}
|
|
144
158
|
|
|
145
159
|
interface AuditOperation {
|
|
@@ -248,7 +262,7 @@ export class ToolResultGuard {
|
|
|
248
262
|
if (block.type === 'image') images += 1;
|
|
249
263
|
else chars += JSON.stringify(block).length;
|
|
250
264
|
}
|
|
251
|
-
chars -=
|
|
265
|
+
chars -= GUARD_NOTICES[pending.notices.get(result.toolUseId) ?? 'ran'].length;
|
|
252
266
|
}
|
|
253
267
|
return Math.max(0, Math.ceil(chars / 4) + images * 1600);
|
|
254
268
|
}
|
|
@@ -361,12 +375,13 @@ export class ToolResultGuard {
|
|
|
361
375
|
// with a placeholder if the audit cannot become durable below.
|
|
362
376
|
console.error(`[tool-result-guard] agent=${this.agentName} staged audit write queued:`, error);
|
|
363
377
|
}
|
|
378
|
+
const results = new Map(originals.map((call) => [call.id, call.result]));
|
|
379
|
+
const notices = new Map([...results].map(([id, result]) => [id, noticeKind(result)]));
|
|
364
380
|
const withheld: ContentBlock[] = content.map((block) => block.type === 'tool_result'
|
|
365
|
-
? { type: 'tool_result', toolUseId: block.toolUseId, content:
|
|
381
|
+
? { type: 'tool_result', toolUseId: block.toolUseId, content: GUARD_NOTICES[notices.get(block.toolUseId) ?? 'ran'], isError: block.isError }
|
|
366
382
|
: block);
|
|
367
383
|
// What each stub keeps if the batch is withheld, matched to its tool's
|
|
368
384
|
// result by tool use id, keyed by index as the stubs and files are.
|
|
369
|
-
const results = new Map(originals.map((call) => [call.id, call.result]));
|
|
370
385
|
const kept = new Map<number, string>();
|
|
371
386
|
for (const [index, block] of content.entries()) {
|
|
372
387
|
const result = block.type === 'tool_result' ? results.get(block.toolUseId) : undefined;
|
|
@@ -379,7 +394,7 @@ export class ToolResultGuard {
|
|
|
379
394
|
// which is correct since a reopened guard has no pending batch.
|
|
380
395
|
const messageId = this.cm.addMessage('user', withheld, metadata, undefined, { holdCompression: true });
|
|
381
396
|
this.pending = { id, messageId, content, withheld, wireResults,
|
|
382
|
-
branch: this.cm.currentBranch().name, submitted: false, durable: false, kept };
|
|
397
|
+
branch: this.cm.currentBranch().name, submitted: false, durable: false, kept, notices };
|
|
383
398
|
// Durability barrier: the audit must reach Chronicle's chain heads before
|
|
384
399
|
// the originals can go to a provider. On a failed sync the batch fails
|
|
385
400
|
// CLOSED: the placeholders go on the wire instead (the turn continues,
|
|
@@ -406,7 +421,7 @@ export class ToolResultGuard {
|
|
|
406
421
|
if (pending.durable) { pending.submitted = true; return results; }
|
|
407
422
|
const ids = new Set(pending.wireResults.map((result) => result.toolUseId));
|
|
408
423
|
return results.map((result) => ids.has(result.toolUseId)
|
|
409
|
-
? { ...result, content:
|
|
424
|
+
? { ...result, content: GUARD_NOTICES[pending.notices.get(result.toolUseId) ?? 'ran'] } : result);
|
|
410
425
|
}
|
|
411
426
|
|
|
412
427
|
/** The stream carrying this batch ended without a clean round (abort or
|
|
@@ -603,7 +618,7 @@ export class ToolResultGuard {
|
|
|
603
618
|
spills.set(index, spill);
|
|
604
619
|
}
|
|
605
620
|
const annotated: ContentBlock[] = pending.withheld.map((block, index) => block.type === 'tool_result'
|
|
606
|
-
? { ...block, content: withheldResultNotice(spills.get(index) ?? null, pending.durable, pending.kept.get(index)) }
|
|
621
|
+
? { ...block, content: withheldResultNotice(spills.get(index) ?? null, pending.durable, pending.kept.get(index), pending.notices.get(block.toolUseId)) }
|
|
607
622
|
: block);
|
|
608
623
|
try {
|
|
609
624
|
this.append({
|
package/src/tune-out/tools.ts
CHANGED
|
@@ -113,7 +113,10 @@ export interface SubconsciousConfig {
|
|
|
113
113
|
* agent-prefixed identifier.
|
|
114
114
|
*/
|
|
115
115
|
name?: string;
|
|
116
|
-
/** Model id; defaults to the primary agent's (same-model side-process).
|
|
116
|
+
/** Model id; defaults to the primary agent's (same-model side-process).
|
|
117
|
+
* Another id, compared exactly, makes the subconscious leave out the
|
|
118
|
+
* fields that describe the primary's model, its window and its thinking
|
|
119
|
+
* binding (SUBCONSCIOUS_MODEL_FIELDS); nothing here sets its own. */
|
|
117
120
|
model?: string;
|
|
118
121
|
/**
|
|
119
122
|
* The voice/criteria mode block — recipe-side and co-authored with the
|
package/src/types/agent.ts
CHANGED
|
@@ -14,8 +14,9 @@ export type SameRoundThinkTextPolicySource =
|
|
|
14
14
|
export interface AutoContinueRun {
|
|
15
15
|
/** How the agent stops, as the row puts it ("call skip_reply"). */
|
|
16
16
|
stop: string;
|
|
17
|
-
/** The turn before ended in words
|
|
18
|
-
*
|
|
17
|
+
/** The turn before ended in words some or all of which were held, not
|
|
18
|
+
* sent, because a send in it may already have posted where they were
|
|
19
|
+
* going. */
|
|
19
20
|
heldAfterSend?: boolean;
|
|
20
21
|
/** Set by the scheduler: the continuation is the turn's only cause. */
|
|
21
22
|
sole?: boolean;
|
|
@@ -254,7 +255,18 @@ export interface AgentConfig {
|
|
|
254
255
|
* that the record doesn't account for raises the
|
|
255
256
|
* `thinking-binding-loss` ops alert, once per incident and again if a
|
|
256
257
|
* call drops more. The provider reports drops only on a request that
|
|
257
|
-
* carries the drop control; without it, such a block is refused
|
|
258
|
+
* carries the drop control; without it, such a block is refused;
|
|
259
|
+
* - the agent is told, in a `[reasoning] <agent>:` system notice of kind
|
|
260
|
+
* `reasoning-loss` stored for it as the framework's other notices are,
|
|
261
|
+
* when a committing compile leaves out a stored reply's reasoning at a
|
|
262
|
+
* change of the request's head, at a branch's first compile, or in a
|
|
263
|
+
* fold or other change to an earlier message that takes a reply of the
|
|
264
|
+
* current turn, and, with `dropBlock` set too, when the provider drops
|
|
265
|
+
* blocks it can place as the agent's stored replies or the stream's
|
|
266
|
+
* rounds, once per incident and again when a call drops more. The
|
|
267
|
+
* framework hears compiles through `Agent.onThinkingStrip` from the
|
|
268
|
+
* agent's first stream on, unless a host has set its own, so an agent
|
|
269
|
+
* driven only through the direct API is never told.
|
|
258
270
|
* The record and the notes
|
|
259
271
|
* vouch for requests as agent-framework hands them to membrane, so a
|
|
260
272
|
* membrane `beforeRequest` hook that changes a request must change every
|
|
@@ -546,10 +558,18 @@ export interface InferenceRequest {
|
|
|
546
558
|
* agent while a turn was alive: a coalesced render placed in its queue
|
|
547
559
|
* (wakeToRead), or a message, notice or event whose wake was queued with
|
|
548
560
|
* it. Once a request that carried the write was answered and, for a wake
|
|
549
|
-
* that carries a channel
|
|
550
|
-
* channel having read it, or once the write is in
|
|
551
|
-
* queue nor its store's current branch, the wake
|
|
561
|
+
* that carries a channel and isn't marked `ambient`, the agent has since
|
|
562
|
+
* published into that channel having read it, or once the write is in
|
|
563
|
+
* neither the agent's queue nor its store's current branch, the wake
|
|
564
|
+
* starts no turn. */
|
|
552
565
|
readsDeferredWrite?: string;
|
|
566
|
+
/** Host-owned: this wake's sole cause is this message, stored in its
|
|
567
|
+
* agent's store as it arrived, not deferred (its row's id). Once a
|
|
568
|
+
* request that carried the message was answered and, for a wake that
|
|
569
|
+
* carries a channel and isn't marked `ambient`, the agent has since
|
|
570
|
+
* published into that channel having read it, or once the message isn't
|
|
571
|
+
* on its store's current branch, the wake starts no turn. */
|
|
572
|
+
readsStoredMessage?: string;
|
|
553
573
|
agentName: string;
|
|
554
574
|
reason: string;
|
|
555
575
|
source: string;
|
|
@@ -597,11 +617,22 @@ export interface InferenceRequest {
|
|
|
597
617
|
* (`chat:ambient`). A coalesced push's wake carries it only to read a
|
|
598
618
|
* render, and only when every notice the render stands for was. Such a
|
|
599
619
|
* message asked nothing of the agent, so a wake for it that carries
|
|
600
|
-
* `readsDeferredWrite` is dropped once a request
|
|
601
|
-
* was answered, with no publish asked of the
|
|
602
|
-
* tag is in doubt, and is judged as an
|
|
620
|
+
* `readsDeferredWrite` or `readsStoredMessage` is dropped once a request
|
|
621
|
+
* that carried the message was answered, with no publish asked of the
|
|
622
|
+
* agent. A message with neither tag is in doubt, and is judged as an
|
|
623
|
+
* addressed one.
|
|
603
624
|
*/
|
|
604
625
|
ambient?: true;
|
|
626
|
+
/**
|
|
627
|
+
* Host-owned: true when the triggering message's server marked it a
|
|
628
|
+
* `chat:system` notice (RFC-001 rev 3): an event about the place, such as
|
|
629
|
+
* a join or the weather changing, not something anyone said. Such a wake
|
|
630
|
+
* names no channel for its turn's plain speech, though it carries
|
|
631
|
+
* `channelId`: a turn it starts alone resolves its locus as a heartbeat's
|
|
632
|
+
* does. Like an ambient one, a wake to read it is done once read. Never
|
|
633
|
+
* set on a coalesced render's wake, which may fold messages too.
|
|
634
|
+
*/
|
|
635
|
+
systemNotice?: true;
|
|
605
636
|
/**
|
|
606
637
|
* Who spoke the message that triggered this inference, as an adapter-
|
|
607
638
|
* namespaced opaque id (`<serverId>:user:<authorId>`) — never a display
|
package/src/types/events.ts
CHANGED
|
@@ -211,14 +211,17 @@ export interface ToolResult {
|
|
|
211
211
|
/** Whether this was an error (for LLM) */
|
|
212
212
|
isError?: boolean;
|
|
213
213
|
/**
|
|
214
|
-
* The
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
214
|
+
* The call was refused before it opened, RFC-007's refusal, so nothing ran
|
|
215
|
+
* and the call certainly had no effect. The dispatcher's path, the model's
|
|
216
|
+
* calls and a script's, sets it: the caller may not use the tool
|
|
217
|
+
* (allowedTools), a `skip_reply` wake was refused, the call's MCPL server is
|
|
218
|
+
* absent or still connecting, the host's policy for that server denies the
|
|
219
|
+
* tool, or a fork reached outside its channel. RFC-007's lifecycle refuses
|
|
220
|
+
* the call from the same place, so observers see no events for it. The
|
|
221
|
+
* framework also sets it on the result it stores for each call in the last
|
|
222
|
+
* round of a response the output limit cut off: such a call was never
|
|
223
|
+
* dispatched or opened, and its result goes into the context with the reply,
|
|
224
|
+
* not through a tool-result event. A check made after the call opened, such as a module's
|
|
222
225
|
* answer for a name it has no tool for or arguments that don't fit a
|
|
223
226
|
* schema, is a failure whose text says nothing was done, as is an MCPL
|
|
224
227
|
* request that couldn't be written. A programmatic call (`executeToolCall`,
|
|
@@ -270,6 +273,13 @@ export interface ToolCall {
|
|
|
270
273
|
name: string;
|
|
271
274
|
/** Tool input */
|
|
272
275
|
input: unknown;
|
|
276
|
+
/**
|
|
277
|
+
* membrane's marker (ToolUseContent.unparseableInput), present when `input`
|
|
278
|
+
* isn't the model's arguments. The framework refuses such a call without
|
|
279
|
+
* running it, except one whose text is JSON `null`, which runs as a call
|
|
280
|
+
* with no arguments.
|
|
281
|
+
*/
|
|
282
|
+
unparseableInput?: string;
|
|
273
283
|
/** The agent that made this tool call. Set by the framework dispatch layer. */
|
|
274
284
|
callerAgentName?: string;
|
|
275
285
|
/**
|
package/src/types/framework.ts
CHANGED
|
@@ -299,6 +299,21 @@ export interface FrameworkConfig {
|
|
|
299
299
|
|
|
300
300
|
/** EventGate config. If omitted, all events trigger inference (unchanged default). */
|
|
301
301
|
gate?: GateOptions;
|
|
302
|
+
/**
|
|
303
|
+
* The heartbeat: the agent's own periodic check-in and reminders, kept by
|
|
304
|
+
* the framework (src/heartbeat/heartbeat.ts), with the tools
|
|
305
|
+
* `heartbeat--heartbeat_status`, `heartbeat--heartbeat_configure`,
|
|
306
|
+
* `heartbeat--heartbeat_trigger`, `heartbeat--reminder_add`,
|
|
307
|
+
* `heartbeat--reminder_list` and `heartbeat--reminder_cancel`. `true` turns it
|
|
308
|
+
* on with its defaults; `false`, null or leaving it out leaves it off. Its
|
|
309
|
+
* wakes go to the gate as heartbeat-mcpl's pushes did, from the server id
|
|
310
|
+
* `heartbeat`, so the agent's rules decide them and a sleep lets them
|
|
311
|
+
* through. It replaces the heartbeat-mcpl server, which is refused beside it
|
|
312
|
+
* (by its id, a heartbeat tool prefix, or its command), at create and at a
|
|
313
|
+
* runtime connect: both would wake the agent. `importFrom` brings that
|
|
314
|
+
* server's schedule and reminders along.
|
|
315
|
+
*/
|
|
316
|
+
heartbeat?: boolean | null | import('../heartbeat/heartbeat.js').HeartbeatOptions;
|
|
302
317
|
|
|
303
318
|
/**
|
|
304
319
|
* Liveness watchdog: fail hard (and let the supervisor restart) if the main
|
package/src/types/module.ts
CHANGED
|
@@ -232,13 +232,16 @@ export interface AgentSettingsExtension {
|
|
|
232
232
|
properties: Record<string, unknown>;
|
|
233
233
|
/** The setting keys this extension owns (must match `properties` keys). */
|
|
234
234
|
keys: string[];
|
|
235
|
-
/** Current values for the extension's keys.
|
|
235
|
+
/** Current values for the extension's keys. A refused agent_settings call
|
|
236
|
+
* names what changed by comparing every field this returns, before the
|
|
237
|
+
* call and at its refusal. */
|
|
236
238
|
get(agentName: string): Record<string, unknown>;
|
|
237
239
|
/** Apply a partial update (only this extension's keys present) and return
|
|
238
240
|
* the new values. Throw to surface a validation error to the agent. */
|
|
239
241
|
update(agentName: string, patch: Record<string, unknown>): Record<string, unknown>;
|
|
240
242
|
/** Restore the listed keys (or all when omitted) to defaults and return the
|
|
241
|
-
* new values. Optional
|
|
243
|
+
* new values. Optional: without it, a reset of every setting skips the
|
|
244
|
+
* extension, and a reset that names one of its keys is refused. */
|
|
242
245
|
reset?(agentName: string, keys?: string[]): Record<string, unknown>;
|
|
243
246
|
}
|
|
244
247
|
|
package/src/types/trace.ts
CHANGED
|
@@ -114,6 +114,23 @@ export type TraceEvent =
|
|
|
114
114
|
/** Bounded like `error`. */
|
|
115
115
|
stack?: string;
|
|
116
116
|
})
|
|
117
|
+
| (TraceEventBase & {
|
|
118
|
+
/**
|
|
119
|
+
* A turn waiting out its error policy's delay before a retry says so:
|
|
120
|
+
* as the wait begins, with when it is due to end, and again every 60
|
|
121
|
+
* seconds while it lasts, while its agent is the one registered under
|
|
122
|
+
* its name, and never after the wait ends (its time coming, a stop, or
|
|
123
|
+
* an abort). The failure being retried is the `inference:failed` just
|
|
124
|
+
* before it. A host watching for agents gone quiet can tell a waiting
|
|
125
|
+
* one from a stuck one by it.
|
|
126
|
+
*/
|
|
127
|
+
type: 'inference:waiting';
|
|
128
|
+
agentName: string;
|
|
129
|
+
/** When the wait is due to end (epoch ms): it counts down by its
|
|
130
|
+
* timers, so a stalled event loop can end it later. Absent for a wait
|
|
131
|
+
* too long for a date. */
|
|
132
|
+
until?: number;
|
|
133
|
+
})
|
|
117
134
|
| (TraceEventBase & {
|
|
118
135
|
type: 'inference:exhausted';
|
|
119
136
|
agentName: string;
|
|
@@ -472,6 +489,24 @@ export type TraceEvent =
|
|
|
472
489
|
policyName: string;
|
|
473
490
|
eventCount: number;
|
|
474
491
|
})
|
|
492
|
+
| (TraceEventBase & {
|
|
493
|
+
/** The reply judge's verdict on a `judge` rule's batch. `verdict`:
|
|
494
|
+
* `reply` or `wait` as the judge answered, `error` when it failed,
|
|
495
|
+
* timed out or answered nothing usable; `woke` is whether the batch
|
|
496
|
+
* then woke the agent (an error does by the host's onError).
|
|
497
|
+
* The judge's reason is in the agent's gate_status, not here: it's
|
|
498
|
+
* written from the agent's context. */
|
|
499
|
+
type: 'gate:judge-verdict';
|
|
500
|
+
policyName: string;
|
|
501
|
+
verdict: 'reply' | 'wait' | 'error';
|
|
502
|
+
woke: boolean;
|
|
503
|
+
eventCount: number;
|
|
504
|
+
durationMs: number;
|
|
505
|
+
model?: string;
|
|
506
|
+
inputTokens?: number;
|
|
507
|
+
cacheReadTokens?: number;
|
|
508
|
+
outputTokens?: number;
|
|
509
|
+
})
|
|
475
510
|
| (TraceEventBase & {
|
|
476
511
|
/** A batched wake's turn started without its `[Gate: …]` line (the
|
|
477
512
|
* gate hands the line to the wake when its message is the newest at
|
|
@@ -836,7 +871,7 @@ export type TraceEvent =
|
|
|
836
871
|
parkedAdmissions?: number;
|
|
837
872
|
/** True when undrained turns were force-cancelled (abandon). */
|
|
838
873
|
abandoned?: boolean;
|
|
839
|
-
/** Agents whose turn the abandon had not ended when its grace ran out: a token held with no stream and no check ahead (a puppet tool turn, a store surgery's reservation, a turn finishing its last delivery), or a turn that hadn't reached its next check, the tool-result guard's wait for its speech included. A turn whose stream the abandon cancelled while it was still streaming isn't listed, even one still running when the grace ran out. */
|
|
874
|
+
/** Agents whose turn the abandon had not ended when its grace ran out: a token held with no stream and no check ahead (a puppet tool turn, a store surgery's reservation, a turn finishing its last delivery), or a turn that hadn't reached its next check, the tool-result guard's wait for its speech included, or a turn whose stream's cancel threw with no check ahead, so that its stream goes on. A turn whose stream the abandon cancelled while it was still streaming isn't listed, even one still running when the grace ran out. */
|
|
840
875
|
unabandonable?: string[];
|
|
841
876
|
})
|
|
842
877
|
| (TraceEventBase & {
|