@actor0/harness 0.0.0-stage → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/answer-filter.d.ts +36 -0
- package/dist/answer-filter.d.ts.map +1 -0
- package/dist/answer-filter.js +308 -0
- package/dist/harness.d.ts +154 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +750 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/model-client.d.ts +165 -0
- package/dist/model-client.d.ts.map +1 -0
- package/dist/model-client.js +654 -0
- package/dist/plan-prefix.d.ts +8 -0
- package/dist/plan-prefix.d.ts.map +1 -0
- package/dist/plan-prefix.js +59 -0
- package/dist/protocol.d.ts +21 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +70 -0
- package/dist/reasoning-summary.d.ts +15 -0
- package/dist/reasoning-summary.d.ts.map +1 -0
- package/dist/reasoning-summary.js +48 -0
- package/dist/tool-call-block.d.ts +141 -0
- package/dist/tool-call-block.d.ts.map +1 -0
- package/dist/tool-call-block.js +377 -0
- package/dist/tool-host.d.ts +31 -0
- package/dist/tool-host.d.ts.map +1 -0
- package/dist/tool-host.js +57 -0
- package/dist/types.d.ts +241 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/package.json +30 -6
- package/README.md +0 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const MAX_HEADING_LEN = 100;
|
|
2
|
+
const MAX_PLAN_LEN = 1_200;
|
|
3
|
+
function headingText(line) {
|
|
4
|
+
const match = /^\s{0,3}#{1,6}\s+(\S.*?)\s*#*\s*$/.exec(line);
|
|
5
|
+
if (!match)
|
|
6
|
+
return null;
|
|
7
|
+
const text = (match[1] ?? "").trim();
|
|
8
|
+
if (!text || text.length > MAX_HEADING_LEN)
|
|
9
|
+
return null;
|
|
10
|
+
return text;
|
|
11
|
+
}
|
|
12
|
+
export function parsePlanPrefix(buffer) {
|
|
13
|
+
const newline = buffer.indexOf("\n");
|
|
14
|
+
if (newline === -1)
|
|
15
|
+
return null;
|
|
16
|
+
const title = headingText(buffer.slice(0, newline));
|
|
17
|
+
if (!title)
|
|
18
|
+
return null;
|
|
19
|
+
let start = newline + 1;
|
|
20
|
+
let end = buffer.indexOf("\n\n", start);
|
|
21
|
+
if (buffer.startsWith("\n", start)) {
|
|
22
|
+
const second = buffer.indexOf("\n\n", start + 1);
|
|
23
|
+
const between = second === -1 ? "" : buffer.slice(start + 1, second);
|
|
24
|
+
if (second !== -1 && between.length > 0 && between.length <= MAX_PLAN_LEN) {
|
|
25
|
+
start += 1;
|
|
26
|
+
end = second;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
start = newline + 1;
|
|
30
|
+
end = newline;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (end === -1)
|
|
34
|
+
return null;
|
|
35
|
+
const body = buffer.slice(start, end);
|
|
36
|
+
if (body.length > MAX_PLAN_LEN)
|
|
37
|
+
return null;
|
|
38
|
+
return {
|
|
39
|
+
title,
|
|
40
|
+
plan: buffer.slice(0, end).replace(/\s+$/, ""),
|
|
41
|
+
consumed: end + 2,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function needsMorePlanData(buffer) {
|
|
45
|
+
if (!/^\s{0,3}(?:#{1,6}\s+|#{0,6}\s*$)/.test(buffer))
|
|
46
|
+
return false;
|
|
47
|
+
const newline = buffer.indexOf("\n");
|
|
48
|
+
if (newline === -1)
|
|
49
|
+
return true;
|
|
50
|
+
if (!headingText(buffer.slice(0, newline)))
|
|
51
|
+
return false;
|
|
52
|
+
return !buffer.includes("\n\n");
|
|
53
|
+
}
|
|
54
|
+
export function extractPlanPrefix(buffer) {
|
|
55
|
+
const plan = parsePlanPrefix(buffer);
|
|
56
|
+
return plan
|
|
57
|
+
? { plan, rest: buffer.slice(plan.consumed) }
|
|
58
|
+
: { plan: null, rest: buffer };
|
|
59
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ChatMessage, HarnessEvent, ToolCall, ToolDefinition } from "./types.js";
|
|
2
|
+
export type ModelConnection = {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
model: string;
|
|
6
|
+
};
|
|
7
|
+
export type TurnRequest = {
|
|
8
|
+
connection: ModelConnection;
|
|
9
|
+
messages: ChatMessage[];
|
|
10
|
+
tools?: ToolDefinition[];
|
|
11
|
+
};
|
|
12
|
+
export type TurnResponseEvent = HarnessEvent | {
|
|
13
|
+
type: "turn_end";
|
|
14
|
+
text: string;
|
|
15
|
+
complete: boolean;
|
|
16
|
+
toolCalls: ToolCall[];
|
|
17
|
+
};
|
|
18
|
+
export declare function encodeEvent(event: TurnResponseEvent): Uint8Array;
|
|
19
|
+
export declare function readEvents(response: Response): AsyncGenerator<TurnResponseEvent, void, unknown>;
|
|
20
|
+
export declare function parseTurnRequest(value: unknown): TurnRequest;
|
|
21
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEtF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,eAAe,CAAC;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAC;CACvB,CAAC;AAGN,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,UAAU,CAEhE;AAED,wBAAuB,UAAU,CAC/B,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,iBAAiB,EAAE,IAAI,EAAE,OAAO,CAAC,CA4BlD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CA2B5D"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export function encodeEvent(event) {
|
|
2
|
+
return new TextEncoder().encode(`data: ${JSON.stringify(event)}\n\n`);
|
|
3
|
+
}
|
|
4
|
+
export async function* readEvents(response) {
|
|
5
|
+
if (!response.body)
|
|
6
|
+
throw new Error("response has no body");
|
|
7
|
+
const reader = response.body.getReader();
|
|
8
|
+
const decoder = new TextDecoder();
|
|
9
|
+
let buffer = "";
|
|
10
|
+
while (true) {
|
|
11
|
+
const { done, value } = await reader.read();
|
|
12
|
+
buffer += decoder.decode(value, { stream: !done });
|
|
13
|
+
const frames = buffer.split(/\r?\n\r?\n/);
|
|
14
|
+
buffer = frames.pop() ?? "";
|
|
15
|
+
for (const frame of frames) {
|
|
16
|
+
const data = frame
|
|
17
|
+
.split(/\r?\n/)
|
|
18
|
+
.filter((line) => line.startsWith("data:"))
|
|
19
|
+
.map((line) => line.slice(5).trim())
|
|
20
|
+
.join("\n");
|
|
21
|
+
if (data)
|
|
22
|
+
yield JSON.parse(data);
|
|
23
|
+
}
|
|
24
|
+
if (done)
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
if (buffer.trim()) {
|
|
28
|
+
const data = buffer
|
|
29
|
+
.split(/\r?\n/)
|
|
30
|
+
.filter((line) => line.startsWith("data:"))
|
|
31
|
+
.map((line) => line.slice(5).trim())
|
|
32
|
+
.join("\n");
|
|
33
|
+
if (data)
|
|
34
|
+
yield JSON.parse(data);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function parseTurnRequest(value) {
|
|
38
|
+
if (!isRecord(value))
|
|
39
|
+
throw new Error("request body must be an object");
|
|
40
|
+
const connection = value.connection;
|
|
41
|
+
const messages = value.messages;
|
|
42
|
+
if (!isRecord(connection))
|
|
43
|
+
throw new Error("connection is required");
|
|
44
|
+
if (typeof connection.baseUrl !== "string" || !connection.baseUrl.trim()) {
|
|
45
|
+
throw new Error("connection.baseUrl is required");
|
|
46
|
+
}
|
|
47
|
+
if (typeof connection.model !== "string" || !connection.model.trim()) {
|
|
48
|
+
throw new Error("connection.model is required");
|
|
49
|
+
}
|
|
50
|
+
if (connection.apiKey !== undefined && typeof connection.apiKey !== "string") {
|
|
51
|
+
throw new Error("connection.apiKey must be a string");
|
|
52
|
+
}
|
|
53
|
+
if (!Array.isArray(messages))
|
|
54
|
+
throw new Error("messages must be an array");
|
|
55
|
+
if (value.tools !== undefined && !Array.isArray(value.tools)) {
|
|
56
|
+
throw new Error("tools must be an array");
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
connection: {
|
|
60
|
+
baseUrl: connection.baseUrl,
|
|
61
|
+
apiKey: connection.apiKey,
|
|
62
|
+
model: connection.model,
|
|
63
|
+
},
|
|
64
|
+
messages: messages,
|
|
65
|
+
tools: (value.tools ?? []),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function isRecord(value) {
|
|
69
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
70
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReasoningSummary } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* One line of model reasoning with the markup stripped off.
|
|
4
|
+
*
|
|
5
|
+
* A heading marker, a bold run or a list bullet means something to a markdown
|
|
6
|
+
* renderer and nothing to anyone reading a terminal. Left in, it is the
|
|
7
|
+
* transcript showing the model its own syntax: a conversation containing a
|
|
8
|
+
* literal "##" looks like a log dump rather than a thought.
|
|
9
|
+
*
|
|
10
|
+
* Exported because every surface that shows reasoning has to strip it, and a
|
|
11
|
+
* second copy of this in a renderer is a second set of rules for the same job.
|
|
12
|
+
*/
|
|
13
|
+
export declare function stripReasoningMarkup(line: string): string;
|
|
14
|
+
export declare function formatReasoningSummary(text: string): ReasoningSummary;
|
|
15
|
+
//# sourceMappingURL=reasoning-summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-summary.d.ts","sourceRoot":"","sources":["../src/reasoning-summary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUzD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAsBrE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const MAX_TITLE = 90;
|
|
2
|
+
const MAX_SUMMARY = 900;
|
|
3
|
+
/**
|
|
4
|
+
* One line of model reasoning with the markup stripped off.
|
|
5
|
+
*
|
|
6
|
+
* A heading marker, a bold run or a list bullet means something to a markdown
|
|
7
|
+
* renderer and nothing to anyone reading a terminal. Left in, it is the
|
|
8
|
+
* transcript showing the model its own syntax: a conversation containing a
|
|
9
|
+
* literal "##" looks like a log dump rather than a thought.
|
|
10
|
+
*
|
|
11
|
+
* Exported because every surface that shows reasoning has to strip it, and a
|
|
12
|
+
* second copy of this in a renderer is a second set of rules for the same job.
|
|
13
|
+
*/
|
|
14
|
+
export function stripReasoningMarkup(line) {
|
|
15
|
+
return line
|
|
16
|
+
.replace(/^\s{0,3}#{1,6}\s+/, "")
|
|
17
|
+
.replace(/^\s*[-*+]\s+/, "")
|
|
18
|
+
.replace(/^\s*\d+[.)]\s+/, "")
|
|
19
|
+
.replace(/\*\*/g, "")
|
|
20
|
+
.replace(/[*_`]/g, "")
|
|
21
|
+
.replace(/\[·?\]/g, "")
|
|
22
|
+
.replace(/\s+/g, " ")
|
|
23
|
+
.trim();
|
|
24
|
+
}
|
|
25
|
+
export function formatReasoningSummary(text) {
|
|
26
|
+
const lines = text.split("\n").map(stripReasoningMarkup).filter((line) => line.length > 0);
|
|
27
|
+
if (lines.length === 0)
|
|
28
|
+
return { title: "", summary: "" };
|
|
29
|
+
const bracketed = /^\[(?:·\s*)?([^\]]{2,60})\]$/.exec(lines[0] ?? "");
|
|
30
|
+
let title = "";
|
|
31
|
+
let body = lines;
|
|
32
|
+
if (bracketed) {
|
|
33
|
+
title = (bracketed[1] ?? "").trim().slice(0, MAX_TITLE);
|
|
34
|
+
body = lines.slice(1);
|
|
35
|
+
}
|
|
36
|
+
else if (lines.length > 1) {
|
|
37
|
+
const first = text.split("\n")[0] ?? "";
|
|
38
|
+
if (/^\s{0,3}(?:#{1,6}\s+|\*\*)/.test(first)) {
|
|
39
|
+
title = (lines[0] ?? "").slice(0, MAX_TITLE);
|
|
40
|
+
body = lines.slice(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const summary = body.join(" ").slice(0, MAX_SUMMARY);
|
|
44
|
+
if (!title && summary) {
|
|
45
|
+
title = (summary.split(/(?<=[.!?])\s/)[0] ?? "").slice(0, MAX_TITLE);
|
|
46
|
+
}
|
|
47
|
+
return { title, summary };
|
|
48
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { ToolCall } from "./types.js";
|
|
2
|
+
/** Which envelope a buffered block is waiting for. */
|
|
3
|
+
export type BlockKind = "fence" | "envelope";
|
|
4
|
+
/**
|
|
5
|
+
* Give up on a block that never closes. An unterminated fence must not
|
|
6
|
+
* swallow the rest of the answer, and no legitimate tool call is this long.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_TOOL_BLOCK = 8000;
|
|
9
|
+
/** True when `line` opens a tool-call candidate. */
|
|
10
|
+
export declare function matchOpenFence(text: string): {
|
|
11
|
+
length: number;
|
|
12
|
+
} | null;
|
|
13
|
+
/**
|
|
14
|
+
* True when `text` could still grow into an opening fence but is not one yet.
|
|
15
|
+
*
|
|
16
|
+
* A fence routinely straddles a delta boundary ("``" + "`json\n"), and
|
|
17
|
+
* deciding per-delta would release the first half as text. This is the hold
|
|
18
|
+
* that makes the protocol recognisable no matter how the stream is chunked.
|
|
19
|
+
*/
|
|
20
|
+
export declare function mayBeOpenFence(text: string): boolean;
|
|
21
|
+
/** True when `line` opens a `<tool_calls>` envelope. */
|
|
22
|
+
export declare function matchOpenEnvelope(text: string): {
|
|
23
|
+
length: number;
|
|
24
|
+
} | null;
|
|
25
|
+
/**
|
|
26
|
+
* The `<tool_calls>` twin of `mayBeOpenFence`.
|
|
27
|
+
*
|
|
28
|
+
* SSE deltas are small enough that `<tool_` on one and `calls>` on the next is
|
|
29
|
+
* routine, and releasing the first half as text would leave `calls>` orphaned
|
|
30
|
+
* in the answer. Same hold, same reason.
|
|
31
|
+
*/
|
|
32
|
+
export declare function mayBeOpenEnvelope(text: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Locate the closing `</tool_calls>`, resuming at `fromIndex`.
|
|
35
|
+
*
|
|
36
|
+
* `fromIndex` is the length of the part already buffered, and the scan starts
|
|
37
|
+
* `CLOSE_ENVELOPE_PREFIX` characters earlier on purpose: a close can straddle
|
|
38
|
+
* the boundary between the buffer and the new delta, and a delta boundary
|
|
39
|
+
* landing mid-tag is the *normal* case for a small-chunked SSE stream. Resuming
|
|
40
|
+
* exactly at `fromIndex` misses it forever, and the block then grows until it
|
|
41
|
+
* is released as text. Resuming from 0 would be correct too, just quadratic on
|
|
42
|
+
* a long unterminated block.
|
|
43
|
+
*/
|
|
44
|
+
export declare function matchCloseEnvelope(text: string, fromIndex?: number): {
|
|
45
|
+
index: number;
|
|
46
|
+
length: number;
|
|
47
|
+
} | null;
|
|
48
|
+
/**
|
|
49
|
+
* Locate the closing fence.
|
|
50
|
+
*
|
|
51
|
+
* Every fence is scanned, not just the first: the text handed in usually still
|
|
52
|
+
* contains the opening fence, which must be skipped rather than mistaken for
|
|
53
|
+
* the close. Mid-stream a close only counts once its newline has arrived, so a
|
|
54
|
+
* buffer ending in "``" is left pending; at end of stream (`atEnd`) a fence
|
|
55
|
+
* with no trailing newline is final and does count.
|
|
56
|
+
*
|
|
57
|
+
* Callers pass the whole buffered block, not just the newest delta: a fence can
|
|
58
|
+
* straddle the two, and a close hidden in the accumulated half is as real as
|
|
59
|
+
* one in the delta.
|
|
60
|
+
*/
|
|
61
|
+
export declare function matchCloseFence(text: string, atEnd?: boolean): {
|
|
62
|
+
index: number;
|
|
63
|
+
length: number;
|
|
64
|
+
} | null;
|
|
65
|
+
/** The JSON body of a complete block, without its fences. */
|
|
66
|
+
export declare function blockBody(block: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* A protocol block that was recognised but refused: it named a tool this
|
|
69
|
+
* request never offered, or its arguments were not parseable JSON.
|
|
70
|
+
*
|
|
71
|
+
* Rejection is a protocol event, not prose. The old behaviour released the
|
|
72
|
+
* block as answer text, which taught the model nothing — it re-emitted the
|
|
73
|
+
* same block, and the user watched raw JSON scroll past. A rejected block now
|
|
74
|
+
* becomes a synthetic call plus an error tool result, so the next round opens
|
|
75
|
+
* with the model being told, in its own protocol, that the call was refused
|
|
76
|
+
* and why. The UI shows the same pair it shows for a failing tool: a cross
|
|
77
|
+
* against a name, with the reason.
|
|
78
|
+
*/
|
|
79
|
+
export type RejectedBlock = {
|
|
80
|
+
/** The name the model asked for, offered or not. For the error message. */
|
|
81
|
+
name: string;
|
|
82
|
+
/** The synthetic call that will carry the error result. */
|
|
83
|
+
call: ToolCall;
|
|
84
|
+
/** One sentence a model (and a user) can act on. */
|
|
85
|
+
reason: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Build the rejection record for a block whose JSON parsed but named a tool
|
|
89
|
+
* the request did not offer. The reason quotes the offered names so the model
|
|
90
|
+
* can pick a real one instead of guessing again.
|
|
91
|
+
*/
|
|
92
|
+
export declare function rejectUnknownTool(name: string, offered: string[]): RejectedBlock;
|
|
93
|
+
/**
|
|
94
|
+
* Build the rejection record for a block that looked like a tool call but
|
|
95
|
+
* whose arguments were not valid JSON. The common cause is a raw newline or
|
|
96
|
+
* tab inside a string literal — the model formatted the command like shell
|
|
97
|
+
* output instead of escaping it — so the reason names that repair first. A
|
|
98
|
+
* body with no usable "name" gets the name-specific hint instead; the
|
|
99
|
+
* generic newline hint would be pointing at a fault it does not have.
|
|
100
|
+
*/
|
|
101
|
+
export declare function rejectMalformedArguments(detail: string, rawBody?: string): RejectedBlock;
|
|
102
|
+
/**
|
|
103
|
+
* Parse a complete fenced block into a tool call.
|
|
104
|
+
*
|
|
105
|
+
* Returns null — meaning "this is ordinary answer text" — unless the block is
|
|
106
|
+
* a JSON object that says `type: "tool_call"` and carries a string `name`.
|
|
107
|
+
* The `type` check is what stops every other JSON example in every answer
|
|
108
|
+
* from being treated as protocol.
|
|
109
|
+
*
|
|
110
|
+
* The result says what happened: a `call` when the name is one the request
|
|
111
|
+
* actually offered, or a `rejected` record when it is not — including when
|
|
112
|
+
* the body is protocol-shaped but not valid JSON, the field case being a raw
|
|
113
|
+
* newline inside a string value. Rejected is not the same as text — the model
|
|
114
|
+
* addressed the protocol; the harness answers in the same protocol (see
|
|
115
|
+
* RejectedBlock).
|
|
116
|
+
*/
|
|
117
|
+
export declare function parseToolCallBlock(block: string, toolNames: string[]): ToolCall | RejectedBlock | null;
|
|
118
|
+
/**
|
|
119
|
+
* The `<tool_calls>` body: every top-level `{…}` object, in order.
|
|
120
|
+
*
|
|
121
|
+
* Braces inside strings are ignored, so a title containing `}` cannot end the
|
|
122
|
+
* scan early. Anything that does not close is not an object and not a call.
|
|
123
|
+
*/
|
|
124
|
+
export declare function scanJsonObjects(text: string): string[];
|
|
125
|
+
/**
|
|
126
|
+
* Parse a closed `<tool_calls>` envelope into the calls it carries.
|
|
127
|
+
*
|
|
128
|
+
* All-or-nothing, like the fence: an envelope is protocol only if *every*
|
|
129
|
+
* object in it is a call for a tool this request actually offered. One
|
|
130
|
+
* `set_conversation_title` in a `<tool_calls>` envelope is not a partial tool
|
|
131
|
+
* round to half-execute — it is a different assistant's protocol, and
|
|
132
|
+
* executing half of it would hide the mismatch. The first non-call object in
|
|
133
|
+
* the envelope therefore poisons the whole envelope: every call-shaped object
|
|
134
|
+
* before it becomes a rejection (the model addressed the protocol and is told
|
|
135
|
+
* so next round), and none of them executes.
|
|
136
|
+
*/
|
|
137
|
+
export declare function parseToolCallEnvelope(block: string, toolNames: string[]): {
|
|
138
|
+
calls: ToolCall[];
|
|
139
|
+
rejected: RejectedBlock[];
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=tool-call-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-call-block.d.ts","sourceRoot":"","sources":["../src/tool-call-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA6D3C,sDAAsD;AACtD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,UAAU,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,cAAc,OAAQ,CAAC;AAIpC,oDAAoD;AACpD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGtE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGzE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKvD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMxG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYrG;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAKD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,IAAI,EAAE,QAAQ,CAAC;IACf,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,aAAa,CAehF;AAgBD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,CA0BxF;AAcD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,QAAQ,GAAG,aAAa,GAAG,IAAI,CAatG;AA4BD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAgCtD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,CAiD1H"}
|