agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
93 lines • 4.18 kB
JavaScript
;
/**
* toolChoice/compose — the tools slot's armed tail, behind the same `import()`.
*
* Pattern: one function the slot awaits under `.toolChoice()` — ask, decide,
* file the row, hand back what to commit.
* Role: core/ layer. Called ONLY by `slots/buildToolsSlot.ts ·
* composeStage` after `mergeWire`, through `import()` (the
* optional-family law of docs-next's site budget: everything this
* family adds to a model call lives off the default graph, and the
* slot keeps six lines). What it returns is committed by the slot's
* own `commitWire`, so the decoration, the receipt and the served
* view see the narrowed list at the one site they always read.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.composeToolChoice = void 0;
const pick_js_1 = require("./pick.js");
const record_js_1 = require("./record.js");
/**
* Ask the classifier about the merged wire minus the doors, decide what is
* served, file the `pick` / `pick-error` row (the row records the offer the
* classifier read and the list really served), and return the list to
* commit. A call with nothing to choose among makes no classifier call and
* files no row.
*/
async function composeToolChoice(input) {
const { merged, injections, injectionNames, iteration, prior } = input;
const doors = (0, pick_js_1.alwaysServedNames)(input.alwaysServe);
const offered = merged.filter((t) => !doors.has(t.name));
if (offered.length === 0)
return { served: merged, servedInjections: injections, narrowedTo: undefined };
const pick = await (0, pick_js_1.pickTools)(input.classifier, {
userMessage: input.userMessage,
...(input.currentSkillId !== undefined && { currentSkillId: input.currentSkillId }),
candidates: offered.map((t) => ({ name: t.name, description: t.description ?? '' })),
}, input.scope.$getEnv().signal);
const narrowing = (0, pick_js_1.narrowServed)({
wire: merged.map((t) => t.name),
doors,
pick,
top: input.top,
iteration,
prior,
wrapUpAsked: input.wrapUpAsked,
...(input.toolsOff === true && { toolsOff: true }),
});
const servedNames = new Set(narrowing.served);
const row = pick.ok
? ({
kind: 'pick',
iteration,
source: 'classifier',
classifier: pick.classifier,
offered: offered.map((t) => t.name),
ranked: pick.ranked,
...(pick.chosen !== undefined && { chosen: pick.chosen }),
confidence: pick.confidence,
...(pick.usage !== undefined && { usage: pick.usage }),
latencyMs: pick.latencyMs,
served: [...narrowing.served],
narrowed: narrowing.narrowed,
...(narrowing.narrowedSkipped !== undefined && {
narrowedSkipped: narrowing.narrowedSkipped,
}),
})
: {
kind: 'pick-error',
iteration,
source: 'classifier',
classifier: pick.classifier,
...(pick.status !== undefined && { status: pick.status }),
message: pick.message,
latencyMs: pick.latencyMs,
served: [...narrowing.served],
...(narrowing.narrowedSkipped !== undefined && {
narrowedSkipped: narrowing.narrowedSkipped,
}),
};
(0, record_js_1.recordToolChoice)(input.scope, row, prior);
if (!narrowing.narrowed)
return { served: merged, servedInjections: injections, narrowedTo: undefined };
return {
served: merged.filter((t) => servedNames.has(t.name)),
// The slot record follows the served list: one InjectionRecord per tool
// the model is handed, re-numbered so `position` is the served index.
servedInjections: injections
.filter((_, i) => servedNames.has(injectionNames[i] ?? ''))
.map((r, i) => ({ ...r, position: i })),
narrowedTo: servedNames,
};
}
exports.composeToolChoice = composeToolChoice;
//# sourceMappingURL=compose.js.map