@hizliemre/horse-code 0.2.0 → 0.3.1
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/{app-2GPGCDX6.js → app-4WN37LZ3.js} +282 -57
- package/dist/{chunk-7JMWPTJ5.js → chunk-27F44PBD.js} +221 -1363
- package/dist/{chunk-23CLQ2KO.js → chunk-2WXG35EM.js} +19 -15
- package/dist/{chunk-LNW557IO.js → chunk-372X5HHU.js} +2 -2
- package/dist/{chunk-5ZV42XGJ.js → chunk-3ACDNDCG.js} +1 -1
- package/dist/{chunk-XEGQT5EN.js → chunk-4M6LXNG2.js} +1 -1
- package/dist/{chunk-6OSEQOYY.js → chunk-6S4WWQMN.js} +2 -2
- package/dist/{chunk-LLL7QWXB.js → chunk-BFIZMM4G.js} +6 -6
- package/dist/chunk-CYLPQWIF.js +214 -0
- package/dist/chunk-G45RWL7S.js +289 -0
- package/dist/{chunk-AE36LLL2.js → chunk-JLWQCA7B.js} +2 -209
- package/dist/{chunk-KKWZBZYK.js → chunk-JR2JLRE3.js} +26 -4
- package/dist/{run-P6ZYL5JL.js → chunk-QJYVZPLG.js} +133 -389
- package/dist/{chunk-UGESK765.js → chunk-UTHLEW5V.js} +1 -1
- package/dist/chunk-YULQ4URQ.js +1220 -0
- package/dist/{chunk-KAGKX2YT.js → chunk-ZPJP2VH5.js} +10 -1
- package/dist/cli.js +586 -104
- package/dist/{fix-JOIXQFVP.js → fix-QCL5AITT.js} +10 -8
- package/dist/{ongoing-WHYXPW24.js → ongoing-6NUSPSCV.js} +3 -2
- package/dist/{project-graph-5HNPRFQG.js → project-graph-OGIM2B33.js} +1 -1
- package/dist/run-V5ZLZ3LS.js +274 -0
- package/dist/{save-skills-X7U3KCPU.js → save-skills-NPKTYNAF.js} +2 -1
- package/dist/{trace-X6TU3AG6.js → trace-UVMZZRA5.js} +1 -1
- package/dist/{trace-adopt-URECQWJV.js → trace-adopt-7HWELJFE.js} +1 -1
- package/dist/{trace-run-7U4WJZ3V.js → trace-run-CZWEZ4R6.js} +8 -4
- package/dist/{triage-FCYHD2AQ.js → triage-IFCVL5MA.js} +7 -6
- package/dist/{verify-6SC4I77M.js → verify-HWZBTK5X.js} +16 -12
- package/package.json +1 -1
- package/dist/chunk-MRZVA5JB.js +0 -163
|
@@ -5,10 +5,8 @@ import {
|
|
|
5
5
|
Board,
|
|
6
6
|
LONG_CALL_MS,
|
|
7
7
|
WHAT_IT_COST,
|
|
8
|
-
applySkills,
|
|
9
8
|
buildAskUserTool,
|
|
10
9
|
buildRememberTool,
|
|
11
|
-
buildSkillTool,
|
|
12
10
|
changedByMerge,
|
|
13
11
|
commitFile,
|
|
14
12
|
commitStep,
|
|
@@ -22,7 +20,6 @@ import {
|
|
|
22
20
|
hasWorkAgainst,
|
|
23
21
|
nextFeatureSlug,
|
|
24
22
|
normalizeQuestion,
|
|
25
|
-
placedSkills,
|
|
26
23
|
readOnlyRegistry,
|
|
27
24
|
refreshAfterChange,
|
|
28
25
|
routeSkills,
|
|
@@ -37,20 +34,25 @@ import {
|
|
|
37
34
|
worktreeState,
|
|
38
35
|
writeFileTool,
|
|
39
36
|
writerRegistry
|
|
40
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-27F44PBD.js";
|
|
41
38
|
import {
|
|
42
39
|
resolveMainBranch
|
|
43
40
|
} from "./chunk-QF4MP6BS.js";
|
|
44
41
|
import {
|
|
45
42
|
askInUserLanguage,
|
|
46
43
|
inUserLanguage
|
|
47
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-UTHLEW5V.js";
|
|
48
45
|
import {
|
|
49
46
|
clearCheckpoint,
|
|
50
47
|
isContinuePrompt,
|
|
51
48
|
readCheckpoint,
|
|
52
49
|
writeCheckpoint
|
|
53
50
|
} from "./chunk-ZSQ24YDJ.js";
|
|
51
|
+
import {
|
|
52
|
+
applySkills,
|
|
53
|
+
buildSkillTool,
|
|
54
|
+
placedSkills
|
|
55
|
+
} from "./chunk-YULQ4URQ.js";
|
|
54
56
|
import {
|
|
55
57
|
defaultGitRunner
|
|
56
58
|
} from "./chunk-LPQU436C.js";
|
|
@@ -72,25 +74,27 @@ import {
|
|
|
72
74
|
relationStrength,
|
|
73
75
|
supersedes,
|
|
74
76
|
verifyAnchors
|
|
75
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-BFIZMM4G.js";
|
|
76
78
|
import {
|
|
77
79
|
ToolRegistry,
|
|
80
|
+
runStructuredRole
|
|
81
|
+
} from "./chunk-CYLPQWIF.js";
|
|
82
|
+
import {
|
|
78
83
|
handedOver,
|
|
79
|
-
runStructuredRole,
|
|
80
84
|
runToCompletion,
|
|
81
85
|
stripThinking,
|
|
82
86
|
telemetry
|
|
83
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-JLWQCA7B.js";
|
|
84
88
|
import {
|
|
85
89
|
TRACE_INDEX,
|
|
86
90
|
mergeTraceIndexes,
|
|
87
91
|
parseTraceIndex,
|
|
88
92
|
serializeTraceIndex,
|
|
89
93
|
traceRootRel
|
|
90
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-ZPJP2VH5.js";
|
|
91
95
|
import {
|
|
92
96
|
loadGraphSync
|
|
93
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-4M6LXNG2.js";
|
|
94
98
|
import {
|
|
95
99
|
inLinkedWorktree,
|
|
96
100
|
sessionBase,
|
|
@@ -3217,7 +3221,7 @@ async function runUpstream(deps, ensureWorktree, prompt, askUser, maxRounds, his
|
|
|
3217
3221
|
emitPhase("verify");
|
|
3218
3222
|
const cwd = await documentWorkdir(process.cwd(), prompt, ensureWorktree, r.title);
|
|
3219
3223
|
laneCheckpoint(cwd, "verify", resume, prompt, r);
|
|
3220
|
-
const { runVerify, describeVerify, currentBranchOf } = await import("./verify-
|
|
3224
|
+
const { runVerify, describeVerify, currentBranchOf } = await import("./verify-HWZBTK5X.js");
|
|
3221
3225
|
const branch = await currentBranchOf(cwd);
|
|
3222
3226
|
const res = await runVerify({
|
|
3223
3227
|
deps,
|
|
@@ -3260,11 +3264,11 @@ async function runUpstream(deps, ensureWorktree, prompt, askUser, maxRounds, his
|
|
|
3260
3264
|
if (!resume && !hasPreservedWork && routeIntent(r.intent) === "pipeline") {
|
|
3261
3265
|
const cwd = workingIn?.() ?? process.cwd();
|
|
3262
3266
|
emitPhase("sizing");
|
|
3263
|
-
const { sizeRequest } = await import("./triage-
|
|
3267
|
+
const { sizeRequest } = await import("./triage-IFCVL5MA.js");
|
|
3264
3268
|
const size = await sizeRequest(deps, cwd, r.refinedPrompt);
|
|
3265
3269
|
let small = size.verdict === "small";
|
|
3266
3270
|
if (size.verdict === "unsure") {
|
|
3267
|
-
const { describeSizeDoubt } = await import("./triage-
|
|
3271
|
+
const { describeSizeDoubt } = await import("./triage-IFCVL5MA.js");
|
|
3268
3272
|
const answer = await askInUserLanguage(
|
|
3269
3273
|
deps,
|
|
3270
3274
|
askUser,
|
|
@@ -3282,8 +3286,8 @@ Which is it?`,
|
|
|
3282
3286
|
if (small) {
|
|
3283
3287
|
emitPhase("small change");
|
|
3284
3288
|
emit({ kind: "note", text: `\u26A1 Small change \u2014 ${size.reason}. No branch, no spec, no plan.` });
|
|
3285
|
-
const { runSmallChange, describeSmallChange } = await import("./fix-
|
|
3286
|
-
const { currentBranchOf } = await import("./verify-
|
|
3289
|
+
const { runSmallChange, describeSmallChange } = await import("./fix-QCL5AITT.js");
|
|
3290
|
+
const { currentBranchOf } = await import("./verify-HWZBTK5X.js");
|
|
3287
3291
|
const res = await runSmallChange(deps, cwd, r.title, r.refinedPrompt, size);
|
|
3288
3292
|
return {
|
|
3289
3293
|
intent: r.intent,
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
memoryHints,
|
|
6
6
|
readFileTool,
|
|
7
7
|
reinforceUsed
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-BFIZMM4G.js";
|
|
9
9
|
import {
|
|
10
10
|
ToolRegistry,
|
|
11
11
|
runStructuredRole
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-CYLPQWIF.js";
|
|
13
13
|
|
|
14
14
|
// src/engine/triage.ts
|
|
15
15
|
import { z } from "zod";
|
|
@@ -300,7 +300,7 @@ function pruneTooling(doc, alsoDrop = /* @__PURE__ */ new Set()) {
|
|
|
300
300
|
async function writtenTracePaths(cwd) {
|
|
301
301
|
const out = /* @__PURE__ */ new Set();
|
|
302
302
|
try {
|
|
303
|
-
const { loadTraceIndex, traceRootRel } = await import("./trace-
|
|
303
|
+
const { loadTraceIndex, traceRootRel } = await import("./trace-UVMZZRA5.js");
|
|
304
304
|
const index = await loadTraceIndex(cwd);
|
|
305
305
|
const root = traceRootRel().replace(/\\/g, "/");
|
|
306
306
|
for (const [file, rec] of Object.entries(index.traces)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hashContent,
|
|
3
3
|
traceDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZPJP2VH5.js";
|
|
5
5
|
|
|
6
6
|
// src/engine/project-brief.ts
|
|
7
7
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -70,7 +70,7 @@ async function gatherBriefInput(cwd, files) {
|
|
|
70
70
|
async function ourOwnOutput(cwd) {
|
|
71
71
|
const out = /* @__PURE__ */ new Set();
|
|
72
72
|
try {
|
|
73
|
-
const { loadTraceIndex, traceRootRel } = await import("./trace-
|
|
73
|
+
const { loadTraceIndex, traceRootRel } = await import("./trace-UVMZZRA5.js");
|
|
74
74
|
const root = traceRootRel().replace(/\\/g, "/");
|
|
75
75
|
out.add(`${root}/PROJECT.md`);
|
|
76
76
|
out.add(`${root}/PROJECT.json`);
|
|
@@ -3,20 +3,20 @@ import {
|
|
|
3
3
|
loadMigratedSync,
|
|
4
4
|
migratedNotice
|
|
5
5
|
} from "./chunk-63E73TGI.js";
|
|
6
|
-
import {
|
|
7
|
-
telemetry
|
|
8
|
-
} from "./chunk-AE36LLL2.js";
|
|
9
6
|
import {
|
|
10
7
|
readBriefSync
|
|
11
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6S4WWQMN.js";
|
|
9
|
+
import {
|
|
10
|
+
telemetry
|
|
11
|
+
} from "./chunk-JLWQCA7B.js";
|
|
12
12
|
import {
|
|
13
13
|
everTraceable,
|
|
14
14
|
readTraceSync
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ZPJP2VH5.js";
|
|
16
16
|
import {
|
|
17
17
|
areaOf,
|
|
18
18
|
loadGraphSync
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-4M6LXNG2.js";
|
|
20
20
|
|
|
21
21
|
// src/tools/read.ts
|
|
22
22
|
import { readFile } from "fs/promises";
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runRoleAgent
|
|
3
|
+
} from "./chunk-JLWQCA7B.js";
|
|
4
|
+
|
|
5
|
+
// src/tools/registry.ts
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
var ToolRegistry = class {
|
|
8
|
+
tools = /* @__PURE__ */ new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Registered and callable, but whose SCHEMA is withheld until something asks for it.
|
|
11
|
+
*
|
|
12
|
+
* A schema is paid for on every turn, whether or not the tool is ever used. Measured across twelve runs:
|
|
13
|
+
* 49 MCP tool schemas came to 86,620 characters (~21,655 tokens), 242 calls carried them, and that is
|
|
14
|
+
* ~5.2M of the 21.7M input tokens billed — 24% of everything — for FIVE tool calls, of two distinct tools.
|
|
15
|
+
* The catalogue that names them costs 900 characters (see MAX_TOOL_NOTE_CHARS); it is the schemas that are
|
|
16
|
+
* expensive, and a schema nobody is about to use buys nothing.
|
|
17
|
+
*/
|
|
18
|
+
deferred = /* @__PURE__ */ new Set();
|
|
19
|
+
/** Bumped by anything that changes what `schemas()` would return, so the derivation can be cached. */
|
|
20
|
+
version = 0;
|
|
21
|
+
cached;
|
|
22
|
+
register(tool) {
|
|
23
|
+
this.tools.set(tool.name, tool);
|
|
24
|
+
this.deferred.delete(tool.name);
|
|
25
|
+
this.version++;
|
|
26
|
+
}
|
|
27
|
+
/** Callable by name from the moment it is registered; sent to the model only once {@link surface}d. */
|
|
28
|
+
registerDeferred(tool) {
|
|
29
|
+
this.tools.set(tool.name, tool);
|
|
30
|
+
this.deferred.add(tool.name);
|
|
31
|
+
this.version++;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Hands over the schemas for these names, from the next turn onward.
|
|
35
|
+
*
|
|
36
|
+
* Returns the ones that were actually withheld, so a caller can say what it just made available and stay
|
|
37
|
+
* quiet about what was already there.
|
|
38
|
+
*/
|
|
39
|
+
surface(names) {
|
|
40
|
+
const opened = names.filter((n) => this.deferred.has(n));
|
|
41
|
+
for (const n of opened) this.deferred.delete(n);
|
|
42
|
+
if (opened.length) this.version++;
|
|
43
|
+
return opened;
|
|
44
|
+
}
|
|
45
|
+
/** Everything still withheld — what a search tool searches. */
|
|
46
|
+
deferredTools() {
|
|
47
|
+
return [...this.deferred].map((n) => this.tools.get(n)).filter((t) => t !== void 0);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A withheld tool is still CALLABLE.
|
|
51
|
+
*
|
|
52
|
+
* A model that reads the catalogue and calls the name straight off is right, and refusing it to enforce a
|
|
53
|
+
* search step would spend a turn teaching it a rule that exists for our benefit, not its.
|
|
54
|
+
*/
|
|
55
|
+
get(name) {
|
|
56
|
+
return this.tools.get(name);
|
|
57
|
+
}
|
|
58
|
+
list() {
|
|
59
|
+
return [...this.tools.values()];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Tool schemas to send to the LLM: zod parameters → JSON Schema (zod 4 native). Withheld ones are omitted.
|
|
63
|
+
*
|
|
64
|
+
* …and so is a tool that has withdrawn itself. `Tool.broken` was documented as being read "where tools are
|
|
65
|
+
* OFFERED, so a broken one stops being handed to fresh agents" — and only `find_tool` ever read it, which
|
|
66
|
+
* covers the deferred tools and not the ones already on the list.
|
|
67
|
+
*
|
|
68
|
+
* Measured on one run: `mcp__angular-cli__list_projects` answered its first caller with a reply that failed
|
|
69
|
+
* its own declared output schema and withdrew itself. It was then offered to seventeen more agents, who
|
|
70
|
+
* called it twenty-eight more times. Every one of those was answered instantly, without touching the
|
|
71
|
+
* server — and still cost a whole model turn to learn what the run already knew.
|
|
72
|
+
*/
|
|
73
|
+
schemas() {
|
|
74
|
+
const withdrawn = this.list().reduce((n, t) => n + (t.broken === void 0 ? 0 : 1), 0);
|
|
75
|
+
if (this.cached?.version === this.version && this.cached.withdrawn === withdrawn) return this.cached.schemas;
|
|
76
|
+
const schemas = this.list().filter((t) => !this.deferred.has(t.name) && t.broken === void 0).map((t) => ({
|
|
77
|
+
name: t.name,
|
|
78
|
+
description: t.description,
|
|
79
|
+
// MCP tools already carry a JSON Schema; everyone else derives it from their zod parameters.
|
|
80
|
+
parameters: t.rawSchema ?? z.toJSONSchema(t.parameters, { target: "draft-7" })
|
|
81
|
+
}));
|
|
82
|
+
this.cached = { version: this.version, withdrawn, schemas };
|
|
83
|
+
return schemas;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// src/core/types.ts
|
|
88
|
+
var DEADLINE_MESSAGE = "the model did not answer within its deadline";
|
|
89
|
+
var CHAIN_BUDGET_MESSAGE = "the chain's total budget ran out before this model was given a fair turn";
|
|
90
|
+
|
|
91
|
+
// src/agent/structured.ts
|
|
92
|
+
function valueAt(args, path) {
|
|
93
|
+
let cur = args;
|
|
94
|
+
for (const key of path) {
|
|
95
|
+
if (typeof cur !== "object" || cur === null) return void 0;
|
|
96
|
+
cur = cur[key];
|
|
97
|
+
}
|
|
98
|
+
return cur;
|
|
99
|
+
}
|
|
100
|
+
function whatWasWrong(issues, args) {
|
|
101
|
+
return issues.map((i) => {
|
|
102
|
+
const where = i.path.length ? i.path.join(".") : void 0;
|
|
103
|
+
const got = valueAt(args, i.path);
|
|
104
|
+
const shown = got === void 0 ? "nothing" : JSON.stringify(got);
|
|
105
|
+
const head = where ? `${where}: ${i.message}` : i.message;
|
|
106
|
+
if (got === void 0 && /received\s+(undefined|null|nothing)/i.test(i.message)) {
|
|
107
|
+
const parent = i.path.length > 1 ? valueAt(args, i.path.slice(0, -1)) : args;
|
|
108
|
+
const sent = parent && typeof parent === "object" ? Object.keys(parent) : [];
|
|
109
|
+
return sent.length ? `${head} \u2014 you sent only ${sent.map((k) => `\`${k}\``).join(", ")}` : head;
|
|
110
|
+
}
|
|
111
|
+
return `${head} \u2014 got ${shown.length > 120 ? `${shown.slice(0, 120)}\u2026` : shown}`;
|
|
112
|
+
}).join("; ");
|
|
113
|
+
}
|
|
114
|
+
function buildSubmitTool(schema) {
|
|
115
|
+
let box;
|
|
116
|
+
const tool = {
|
|
117
|
+
name: "submit",
|
|
118
|
+
description: "When you are done, submit your result in structured form with this tool.",
|
|
119
|
+
permissionLevel: "safe",
|
|
120
|
+
parameters: schema,
|
|
121
|
+
run: async (rawArgs) => {
|
|
122
|
+
const parsed = schema.safeParse(rawArgs);
|
|
123
|
+
if (!parsed.success) {
|
|
124
|
+
return { content: `submit: invalid output: ${whatWasWrong(parsed.error.issues, rawArgs)}`, isError: true };
|
|
125
|
+
}
|
|
126
|
+
box = { value: parsed.data };
|
|
127
|
+
return { content: "received", isError: false };
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
return { tool, result: () => box };
|
|
131
|
+
}
|
|
132
|
+
function extractStructured(text, schema) {
|
|
133
|
+
const trimmed = text.trim();
|
|
134
|
+
if (!trimmed) return void 0;
|
|
135
|
+
const candidates = [trimmed];
|
|
136
|
+
const block = trimmed.match(/\{[\s\S]*\}/);
|
|
137
|
+
if (block) candidates.push(block[0]);
|
|
138
|
+
for (const c of candidates) {
|
|
139
|
+
try {
|
|
140
|
+
const parsed = schema.safeParse(JSON.parse(c));
|
|
141
|
+
if (parsed.success) return parsed.data;
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
var TURN_LIMIT_RE = /maximum turn count exceeded/i;
|
|
148
|
+
async function runStructuredRole(opts, schema, maxAttempts = 2) {
|
|
149
|
+
const handle = buildSubmitTool(schema);
|
|
150
|
+
const registry = new ToolRegistry();
|
|
151
|
+
for (const t of opts.tools.list()) registry.register(t);
|
|
152
|
+
registry.register(handle.tool);
|
|
153
|
+
const chain = [opts.model, ...opts.fallbacks ?? []];
|
|
154
|
+
const total = opts.totalMs ? AbortSignal.timeout(opts.totalMs) : void 0;
|
|
155
|
+
const outOfTime = () => total?.aborted === true;
|
|
156
|
+
const signalFor = () => {
|
|
157
|
+
const parts = [opts.signal];
|
|
158
|
+
if (total) parts.push(total);
|
|
159
|
+
if (opts.perAttemptMs) parts.push(AbortSignal.timeout(opts.perAttemptMs));
|
|
160
|
+
return parts.length === 1 ? opts.signal : AbortSignal.any(parts);
|
|
161
|
+
};
|
|
162
|
+
let lastError;
|
|
163
|
+
for (let ci = 0; ci < chain.length; ci++) {
|
|
164
|
+
const model = chain[ci];
|
|
165
|
+
if (ci > 0) opts.onFallback?.(chain[ci - 1], model, "structured: previous model returned no valid result");
|
|
166
|
+
const messages = [...opts.messages];
|
|
167
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
168
|
+
if (opts.signal.aborted) throw new Error("cancelled");
|
|
169
|
+
if (outOfTime()) break;
|
|
170
|
+
let lastText = "";
|
|
171
|
+
let errored;
|
|
172
|
+
for await (const ev of runRoleAgent({ ...opts, model, fallbacks: [], messages, tools: registry, signal: signalFor() })) {
|
|
173
|
+
if (ev.type === "error") {
|
|
174
|
+
errored = ev.message;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
if (ev.type === "abort") {
|
|
178
|
+
if (opts.signal.aborted) throw new Error("cancelled");
|
|
179
|
+
errored = total?.aborted ? CHAIN_BUDGET_MESSAGE : DEADLINE_MESSAGE;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
if (ev.type === "message.done") lastText = ev.message.content ?? lastText;
|
|
183
|
+
if (handle.result() !== void 0) break;
|
|
184
|
+
}
|
|
185
|
+
const r = handle.result();
|
|
186
|
+
if (r !== void 0) return r.value;
|
|
187
|
+
const salvaged = extractStructured(lastText, schema);
|
|
188
|
+
if (salvaged !== void 0) return salvaged;
|
|
189
|
+
if (errored !== void 0) {
|
|
190
|
+
if (TURN_LIMIT_RE.test(errored) && attempt < maxAttempts - 1) {
|
|
191
|
+
messages.push({ role: "assistant", content: lastText });
|
|
192
|
+
messages.push({ role: "user", content: "You have used your entire tool-call budget. Call `submit` NOW with the findings you already have. Do not read, grep or inspect anything else." });
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
lastError = errored;
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
messages.push({ role: "assistant", content: lastText });
|
|
199
|
+
messages.push({
|
|
200
|
+
role: "user",
|
|
201
|
+
content: "You did not call the `submit` tool. Call `submit` now with your result as structured arguments \u2014 do not answer in prose."
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (opts.signal.aborted) throw new Error("cancelled");
|
|
205
|
+
if (outOfTime()) throw new Error("the model chain did not produce a result within its total budget");
|
|
206
|
+
opts.onStructuralFailure?.(model, "answered in prose instead of calling submit");
|
|
207
|
+
}
|
|
208
|
+
throw new Error(lastError ?? "structured role: submit was not called (whole model chain tried)");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export {
|
|
212
|
+
ToolRegistry,
|
|
213
|
+
runStructuredRole
|
|
214
|
+
};
|