@cubicecho/agent-core 2.0.3 → 2.0.5
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/capabilities.js +14 -1
- package/dist/tool-loading.d.ts +2 -1
- package/dist/tool-loading.js +17 -3
- package/package.json +1 -1
package/dist/capabilities.js
CHANGED
|
@@ -30,6 +30,8 @@ export function capabilitiesFor(baseUrl) {
|
|
|
30
30
|
export function resetCapabilities() {
|
|
31
31
|
capabilities.clear();
|
|
32
32
|
}
|
|
33
|
+
/** Every flag on a `Capabilities`, typed, so a third one is compared without an edit here. */
|
|
34
|
+
const keys = (of) => Object.keys(of);
|
|
33
35
|
/** `stream_options` is named in the refusal by every server that has not heard of it. */
|
|
34
36
|
const REJECTS_USAGE = /stream_options/i;
|
|
35
37
|
/**
|
|
@@ -61,6 +63,10 @@ const REJECTS_USAGE = /stream_options/i;
|
|
|
61
63
|
*/
|
|
62
64
|
export async function negotiate(supports, send, { produced = { any: false }, onNotice } = {}) {
|
|
63
65
|
for (;;) {
|
|
66
|
+
// What this attempt was built with. `capabilitiesFor` hands one object per endpoint to
|
|
67
|
+
// everyone on it, so a run starting alongside this one may latch a flag off while this call
|
|
68
|
+
// is in flight — and the branches below are guarded on the flag still being set.
|
|
69
|
+
const sent = { ...supports };
|
|
64
70
|
try {
|
|
65
71
|
return await send(supports, produced);
|
|
66
72
|
}
|
|
@@ -76,9 +82,16 @@ export async function negotiate(supports, send, { produced = { any: false }, onN
|
|
|
76
82
|
supports.usageInStream = false;
|
|
77
83
|
onNotice?.("server rejected stream_options; token counts unavailable");
|
|
78
84
|
}
|
|
79
|
-
else {
|
|
85
|
+
else if (keys(sent).every((flag) => sent[flag] === supports[flag])) {
|
|
80
86
|
throw error;
|
|
81
87
|
}
|
|
88
|
+
// Otherwise the refusal was answered by whoever got there first, and this attempt was
|
|
89
|
+
// built before the answer existed. Two runs opening on a fresh llama.cpp box both get the
|
|
90
|
+
// grammar error; the first latches it off and re-sends, and the second used to find the
|
|
91
|
+
// flag already clear, fall through to the throw and die on an error the process had just
|
|
92
|
+
// learned to fix — `isTransient` refuses a 400, so `runTurn` would not send it again
|
|
93
|
+
// either. Sending it again is the whole of the fix: flags only ever latch off, so this
|
|
94
|
+
// gives up after one pass per flag.
|
|
82
95
|
}
|
|
83
96
|
}
|
|
84
97
|
}
|
package/dist/tool-loading.d.ts
CHANGED
|
@@ -88,6 +88,7 @@ export declare function expandNames(requested: string[], catalog: CatalogServer[
|
|
|
88
88
|
name: string;
|
|
89
89
|
hits: string[];
|
|
90
90
|
}[];
|
|
91
|
+
deferred: string[];
|
|
91
92
|
};
|
|
92
93
|
/**
|
|
93
94
|
* What `load_tools` reports back: the descriptions, now that they are worth their tokens.
|
|
@@ -95,7 +96,7 @@ export declare function expandNames(requested: string[], catalog: CatalogServer[
|
|
|
95
96
|
* @param expanded What `expandNames` resolved: the matches, the misses, and the over-broad asks.
|
|
96
97
|
* @param catalog The servers, read for the descriptions now worth their tokens.
|
|
97
98
|
*/
|
|
98
|
-
export declare function loadResult({ matched, unknown, overBroad }: ReturnType<typeof expandNames>, catalog: CatalogServer[]): string;
|
|
99
|
+
export declare function loadResult({ matched, unknown, overBroad, deferred }: ReturnType<typeof expandNames>, catalog: CatalogServer[]): string;
|
|
99
100
|
/**
|
|
100
101
|
* Whether the catalogue holds a tool by this name.
|
|
101
102
|
*
|
package/dist/tool-loading.js
CHANGED
|
@@ -137,6 +137,7 @@ export function expandNames(requested, catalog) {
|
|
|
137
137
|
const matched = new Set();
|
|
138
138
|
const unknown = [];
|
|
139
139
|
const overBroad = [];
|
|
140
|
+
const deferred = [];
|
|
140
141
|
const known = new Set(all.map((tool) => tool.name));
|
|
141
142
|
const resolve = (name) => {
|
|
142
143
|
if (name.endsWith("*")) {
|
|
@@ -172,13 +173,21 @@ export function expandNames(requested, catalog) {
|
|
|
172
173
|
// model that is meant to be choosing from twelve. Already-matched names are free, so a
|
|
173
174
|
// name that only repeats an earlier one never spends budget.
|
|
174
175
|
const fresh = hits.filter((hit) => !matched.has(hit));
|
|
175
|
-
|
|
176
|
+
// Two different refusals, and answering both with "narrow it down" made one of them
|
|
177
|
+
// impossible to act on. A name that would not fit an empty call is over-broad, and the
|
|
178
|
+
// names are what the model needs. A name that only does not fit *this* call is precise
|
|
179
|
+
// enough already — telling a model that asked for one exact tool that it matches one tool,
|
|
180
|
+
// "more than the twelve one call may load", and to choose from a list holding just that
|
|
181
|
+
// name, leaves it nothing to do but send the identical call again.
|
|
182
|
+
if (fresh.length > MAX_PER_LOAD)
|
|
176
183
|
overBroad.push({ name, hits });
|
|
184
|
+
else if (matched.size + fresh.length > MAX_PER_LOAD)
|
|
185
|
+
deferred.push(name);
|
|
177
186
|
else
|
|
178
187
|
for (const hit of hits)
|
|
179
188
|
matched.add(hit);
|
|
180
189
|
}
|
|
181
|
-
return { matched: [...matched], unknown, overBroad };
|
|
190
|
+
return { matched: [...matched], unknown, overBroad, deferred };
|
|
182
191
|
}
|
|
183
192
|
/**
|
|
184
193
|
* What `load_tools` reports back: the descriptions, now that they are worth their tokens.
|
|
@@ -186,7 +195,7 @@ export function expandNames(requested, catalog) {
|
|
|
186
195
|
* @param expanded What `expandNames` resolved: the matches, the misses, and the over-broad asks.
|
|
187
196
|
* @param catalog The servers, read for the descriptions now worth their tokens.
|
|
188
197
|
*/
|
|
189
|
-
export function loadResult({ matched, unknown, overBroad }, catalog) {
|
|
198
|
+
export function loadResult({ matched, unknown, overBroad, deferred }, catalog) {
|
|
190
199
|
const byName = new Map(flatten(catalog).map((tool) => [tool.name, tool.description]));
|
|
191
200
|
const lines = [];
|
|
192
201
|
if (matched.length) {
|
|
@@ -199,6 +208,11 @@ export function loadResult({ matched, unknown, overBroad }, catalog) {
|
|
|
199
208
|
lines.push("");
|
|
200
209
|
lines.push(`\`${name}\` matches ${hits.length} tools, more than the ${MAX_PER_LOAD} one call may load.`, "Name the ones you need from:", ...hits.map((hit) => ` ${hit}`));
|
|
201
210
|
}
|
|
211
|
+
if (deferred.length) {
|
|
212
|
+
if (lines.length)
|
|
213
|
+
lines.push("");
|
|
214
|
+
lines.push(`This call is full at ${MAX_PER_LOAD} tools, so these were not loaded: ${deferred.join(", ")}.`, "Ask for them on your next step.");
|
|
215
|
+
}
|
|
202
216
|
if (unknown.length) {
|
|
203
217
|
if (lines.length)
|
|
204
218
|
lines.push("");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cubicecho/agent-core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "The endpoint-agnostic half of an OpenAI-compatible agent loop: tool-schema compatibility, on-demand tool loading, one-shot side tasks, run events, and a pooled client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openai",
|