agentfootprint 9.14.0 → 9.16.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/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/core/agent/AgentBuilder.js +130 -0
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/buildAgentChart.js +3 -0
- package/dist/core/agent/buildAgentChart.js.map +1 -1
- package/dist/core/agent/buildDynamicAgentChart.js +6 -0
- package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +32 -1
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +24 -1
- package/dist/esm/core/agent/AgentBuilder.js +130 -0
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/buildAgentChart.js +3 -0
- package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
- package/dist/esm/core/agent/buildDynamicAgentChart.js +6 -0
- package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +0 -3
- package/dist/esm/core/agent/stages/toolCalls.js +32 -1
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +13 -1
- package/dist/esm/events/payloads.d.ts +35 -0
- package/dist/esm/events/registry.d.ts +3 -1
- package/dist/esm/events/registry.js +2 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.d.ts +1 -0
- package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js +17 -0
- package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
- package/dist/esm/lib/injection-engine/evaluator.d.ts +8 -3
- package/dist/esm/lib/injection-engine/evaluator.js +13 -7
- package/dist/esm/lib/injection-engine/evaluator.js.map +1 -1
- package/dist/esm/lib/injection-engine/factories/defineSkill.d.ts +28 -12
- package/dist/esm/lib/injection-engine/factories/defineSkill.js +32 -0
- package/dist/esm/lib/injection-engine/factories/defineSkill.js.map +1 -1
- package/dist/esm/lib/injection-engine/index.d.ts +2 -1
- package/dist/esm/lib/injection-engine/index.js +11 -1
- package/dist/esm/lib/injection-engine/index.js.map +1 -1
- package/dist/esm/lib/injection-engine/skillGraph.d.ts +140 -8
- package/dist/esm/lib/injection-engine/skillGraph.js +235 -39
- package/dist/esm/lib/injection-engine/skillGraph.js.map +1 -1
- package/dist/esm/lib/injection-engine/skillGraphCheckup.d.ts +11 -3
- package/dist/esm/lib/injection-engine/skillGraphCheckup.js +75 -2
- package/dist/esm/lib/injection-engine/skillGraphCheckup.js.map +1 -1
- package/dist/esm/lib/injection-engine/skillMatch.d.ts +107 -0
- package/dist/esm/lib/injection-engine/skillMatch.js +144 -0
- package/dist/esm/lib/injection-engine/skillMatch.js.map +1 -0
- package/dist/esm/lib/injection-engine/types.d.ts +39 -3
- package/dist/esm/lib/injection-engine/types.js +12 -0
- package/dist/esm/lib/injection-engine/types.js.map +1 -1
- package/dist/events/registry.js +2 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/lib/injection-engine/buildInjectionEngineSubflow.js +17 -0
- package/dist/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
- package/dist/lib/injection-engine/evaluator.js +13 -7
- package/dist/lib/injection-engine/evaluator.js.map +1 -1
- package/dist/lib/injection-engine/factories/defineSkill.js +32 -0
- package/dist/lib/injection-engine/factories/defineSkill.js.map +1 -1
- package/dist/lib/injection-engine/index.js +12 -1
- package/dist/lib/injection-engine/index.js.map +1 -1
- package/dist/lib/injection-engine/skillGraph.js +236 -40
- package/dist/lib/injection-engine/skillGraph.js.map +1 -1
- package/dist/lib/injection-engine/skillGraphCheckup.js +75 -2
- package/dist/lib/injection-engine/skillGraphCheckup.js.map +1 -1
- package/dist/lib/injection-engine/skillMatch.js +150 -0
- package/dist/lib/injection-engine/skillMatch.js.map +1 -0
- package/dist/lib/injection-engine/types.js +14 -1
- package/dist/lib/injection-engine/types.js.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +24 -1
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
- package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +0 -3
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +13 -1
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +35 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +3 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts +1 -0
- package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/evaluator.d.ts +8 -3
- package/dist/types/lib/injection-engine/evaluator.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/factories/defineSkill.d.ts +28 -12
- package/dist/types/lib/injection-engine/factories/defineSkill.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/index.d.ts +2 -1
- package/dist/types/lib/injection-engine/index.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/skillGraph.d.ts +140 -8
- package/dist/types/lib/injection-engine/skillGraph.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/skillGraphCheckup.d.ts +11 -3
- package/dist/types/lib/injection-engine/skillGraphCheckup.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/skillMatch.d.ts +108 -0
- package/dist/types/lib/injection-engine/skillMatch.d.ts.map +1 -0
- package/dist/types/lib/injection-engine/types.d.ts +39 -3
- package/dist/types/lib/injection-engine/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -14,15 +14,21 @@
|
|
|
14
14
|
* • `rule` → predicate runs against `ctx`. Errors are
|
|
15
15
|
* caught + reported in `skipped`; never
|
|
16
16
|
* propagate. Run never crashes.
|
|
17
|
-
* • `on-tool-return` → active when
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* • `on-tool-return` → active when ANY tool result of the previous
|
|
18
|
+
* iteration's batch (`toolResultsOf(ctx)` —
|
|
19
|
+
* `ctx.toolResults`, falling back to the
|
|
20
|
+
* singular `ctx.lastToolResult`) has a
|
|
21
|
+
* `toolName` matching `trigger.toolName`
|
|
22
|
+
* (string equal or regex test). Before 9.16.0
|
|
23
|
+
* only the LAST call of a parallel batch was
|
|
24
|
+
* consulted — earlier calls were dropped.
|
|
20
25
|
* • `llm-activated` → active when the Injection's `id` is in
|
|
21
26
|
* `ctx.activatedInjectionIds` (the LLM
|
|
22
27
|
* previously called `viaToolName(<id>)`).
|
|
23
28
|
*/
|
|
24
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
30
|
exports.evaluateInjections = void 0;
|
|
31
|
+
const types_js_1 = require("./types.js");
|
|
26
32
|
function evaluateInjections(injections, ctx) {
|
|
27
33
|
const active = [];
|
|
28
34
|
const skipped = [];
|
|
@@ -48,10 +54,10 @@ function evaluateInjections(injections, ctx) {
|
|
|
48
54
|
break;
|
|
49
55
|
}
|
|
50
56
|
case 'on-tool-return': {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const matches = typeof t.toolName === 'string' ? t.toolName === toolName : t.toolName.test(toolName);
|
|
57
|
+
// The WHOLE batch, in call order (9.16.0) — a trigger fires when any
|
|
58
|
+
// call of the previous iteration returned from the named tool, not
|
|
59
|
+
// only when that tool happened to be last in a parallel batch.
|
|
60
|
+
const matches = (0, types_js_1.toolResultsOf)(ctx).some((r) => typeof t.toolName === 'string' ? t.toolName === r.toolName : t.toolName.test(r.toolName));
|
|
55
61
|
if (matches)
|
|
56
62
|
active.push(inj);
|
|
57
63
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../../src/lib/injection-engine/evaluator.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../../src/lib/injection-engine/evaluator.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;AAEH,yCAA2C;AAG3C,SAAgB,kBAAkB,CAChC,UAAgC,EAChC,GAAqB;IAErB,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,MAAM,OAAO,GAIR,EAAE,CAAC;IAER,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;QACtB,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjB,MAAM;YACR,CAAC;YACD,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,IAAI,CAAC;oBACH,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;wBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC1C,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,IAAI,CAAC;wBACX,EAAE,EAAE,GAAG,CAAC,EAAE;wBACV,MAAM,EAAE,iBAAiB;wBACzB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;qBACxD,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,qEAAqE;gBACrE,mEAAmE;gBACnE,+DAA+D;gBAC/D,MAAM,OAAO,GAAG,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5C,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CACzF,CAAC;gBACF,IAAI,OAAO;oBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC9B,MAAM;YACR,CAAC;YACD,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,IAAI,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjE,MAAM;YACR,CAAC;YACD,OAAO,CAAC,CAAC,CAAC;gBACR,yDAAyD;gBACzD,+DAA+D;gBAC/D,MAAM,WAAW,GAAU,CAAC,CAAC;gBAC7B,OAAO,CAAC,IAAI,CAAC;oBACX,EAAE,EAAE,GAAG,CAAC,EAAE;oBACV,MAAM,EAAE,sBAAsB;oBAC9B,KAAK,EAAE,sBAAsB,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE;iBAC3D,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC7B,CAAC;AA1DD,gDA0DC"}
|
|
@@ -11,6 +11,16 @@
|
|
|
11
11
|
* and callable from iteration 1 unless the Skill sets
|
|
12
12
|
* `autoActivate: 'currentSkill'` — see that option.
|
|
13
13
|
*
|
|
14
|
+
* **Activation lifetime: the rest of the turn.** Once the model picks a
|
|
15
|
+
* skill with `read_skill`, the activation lasts until the turn ends — every
|
|
16
|
+
* later iteration of that `run()` keeps the body active, and there is no
|
|
17
|
+
* mid-turn deactivation (nothing removes an id from
|
|
18
|
+
* `ctx.activatedInjectionIds`; the next `run()` starts clean). This is
|
|
19
|
+
* deliberate: the cache layer's skill history and the context ledger both
|
|
20
|
+
* lean on the cumulative property. A skill that should stop applying
|
|
21
|
+
* mid-turn is a `rule` trigger or a `skillGraph()` route, not a read_skill
|
|
22
|
+
* pick.
|
|
23
|
+
*
|
|
14
24
|
* Produces an `Injection` with:
|
|
15
25
|
* - flavor: `'skill'`
|
|
16
26
|
* - trigger: `{ kind: 'llm-activated', viaToolName: 'read_skill' }`
|
|
@@ -34,6 +44,7 @@
|
|
|
34
44
|
*/
|
|
35
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
46
|
exports.defineSkill = exports.resolveSurfaceMode = void 0;
|
|
47
|
+
const footprintjs_1 = require("footprintjs");
|
|
37
48
|
const applyCachePolicy_js_1 = require("../../../cache/applyCachePolicy.js");
|
|
38
49
|
/**
|
|
39
50
|
* Resolve `surfaceMode: 'auto'` to a concrete mode based on provider
|
|
@@ -89,6 +100,25 @@ function assertNoViaToolName(where, opts) {
|
|
|
89
100
|
`skills already share ONE activation tool and the model picks WHICH skill by id. If you ` +
|
|
90
101
|
`need the skill gated on something else, use a \`rule\` trigger or a skillGraph() edge.`);
|
|
91
102
|
}
|
|
103
|
+
/** One warn per process for the deprecated `refreshPolicy` — the field is
|
|
104
|
+
* accepted (additive-only law) but a declaration that does nothing should
|
|
105
|
+
* say so ONCE, not on every skill of a 40-skill catalog. */
|
|
106
|
+
let warnedRefreshPolicyDeprecated = false;
|
|
107
|
+
/**
|
|
108
|
+
* The dev-mode deprecation warn for `refreshPolicy` (9.16.0). Named, not
|
|
109
|
+
* inlined, so the once-flag and the message live beside each other.
|
|
110
|
+
*/
|
|
111
|
+
function warnRefreshPolicyDeprecated(skillId) {
|
|
112
|
+
if (warnedRefreshPolicyDeprecated || !(0, footprintjs_1.isDevMode)())
|
|
113
|
+
return;
|
|
114
|
+
warnedRefreshPolicyDeprecated = true;
|
|
115
|
+
// eslint-disable-next-line no-console
|
|
116
|
+
console.warn(`agentfootprint defineSkill('${skillId}'): \`refreshPolicy\` is DEPRECATED-pending-steps — ` +
|
|
117
|
+
'it is stored on skill.metadata and nothing in the engine reads it, so NO re-injection ' +
|
|
118
|
+
'happens. It is superseded by a planned steps-as-data feature and will not be wired up. ' +
|
|
119
|
+
"To re-surface a body in a long run today, use surfaceMode: 'both' (read_skill returns " +
|
|
120
|
+
'the body afresh) or deliver it yourself. This warning fires once per process.');
|
|
121
|
+
}
|
|
92
122
|
function defineSkill(opts) {
|
|
93
123
|
if (!opts.id || opts.id.trim().length === 0) {
|
|
94
124
|
throw new Error('defineSkill: `id` is required and must be non-empty.');
|
|
@@ -100,6 +130,8 @@ function defineSkill(opts) {
|
|
|
100
130
|
throw new Error(`defineSkill(${opts.id}): \`body\` is required.`);
|
|
101
131
|
}
|
|
102
132
|
assertNoViaToolName(`defineSkill(${opts.id})`, opts);
|
|
133
|
+
if (opts.refreshPolicy)
|
|
134
|
+
warnRefreshPolicyDeprecated(opts.id);
|
|
103
135
|
return Object.freeze({
|
|
104
136
|
id: opts.id,
|
|
105
137
|
description: opts.description,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineSkill.js","sourceRoot":"","sources":["../../../../src/lib/injection-engine/factories/defineSkill.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"defineSkill.js","sourceRoot":"","sources":["../../../../src/lib/injection-engine/factories/defineSkill.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,10 +7,15 @@
|
|
|
7
7
|
* factories. See `README.md` in this folder for the full concept.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.skillToolNames = exports.checkSkillContracts = exports.checkSkillContract = exports.rankEntries = exports.embeddingScorer = exports.keywordScorer = exports.SKILL_GRAPH_METADATA_KEY = exports.formatCheckup = exports.decideSkill = exports.skillGraph = exports.defineInjection = exports.defineFact = exports.defineSteering = exports.buildReadSkillTool = exports.buildListSkillsTool = exports.skillsFromDir = exports.SkillRegistry = exports.resolveSurfaceMode = exports.defineSkill = exports.READS_ENTRY_SCORES_METADATA_KEY = exports.defineRelevanceHint = exports.defineInstruction = exports.buildInjectionEngineSubflow = exports.evaluateInjections = exports.projectActiveInjection = void 0;
|
|
10
|
+
exports.skillToolNames = exports.checkSkillContracts = exports.checkSkillContract = exports.rankEntries = exports.embeddingScorer = exports.keywordScorer = exports.SKILL_GRAPH_DEFERRED_CONTRACT_KEY = exports.SKILL_GRAPH_METADATA_KEY = exports.formatCheckup = exports.decideSkill = exports.skillGraph = exports.defineInjection = exports.defineFact = exports.defineSteering = exports.buildReadSkillTool = exports.buildListSkillsTool = exports.skillsFromDir = exports.SkillRegistry = exports.resolveSurfaceMode = exports.defineSkill = exports.READS_ENTRY_SCORES_METADATA_KEY = exports.defineRelevanceHint = exports.defineInstruction = exports.buildInjectionEngineSubflow = exports.evaluateInjections = exports.toolResultsOf = exports.projectActiveInjection = void 0;
|
|
11
11
|
// POJO projection — used by slot subflows + advanced consumers
|
|
12
12
|
var types_js_1 = require("./types.js");
|
|
13
13
|
Object.defineProperty(exports, "projectActiveInjection", { enumerable: true, get: function () { return types_js_1.projectActiveInjection; } });
|
|
14
|
+
// The iteration's tool-result batch with the singular fallback applied
|
|
15
|
+
// (9.16.0) — the one reader `rule` predicates should use to see every call of
|
|
16
|
+
// a parallel batch, not only the last (`ctx.toolResults ?? [ctx.lastToolResult]`).
|
|
17
|
+
var types_js_2 = require("./types.js");
|
|
18
|
+
Object.defineProperty(exports, "toolResultsOf", { enumerable: true, get: function () { return types_js_2.toolResultsOf; } });
|
|
14
19
|
// Engine
|
|
15
20
|
var evaluator_js_1 = require("./evaluator.js");
|
|
16
21
|
Object.defineProperty(exports, "evaluateInjections", { enumerable: true, get: function () { return evaluator_js_1.evaluateInjections; } });
|
|
@@ -59,6 +64,12 @@ Object.defineProperty(exports, "decideSkill", { enumerable: true, get: function
|
|
|
59
64
|
// is already the door to it.
|
|
60
65
|
Object.defineProperty(exports, "formatCheckup", { enumerable: true, get: function () { return skillGraph_js_1.formatCheckup; } });
|
|
61
66
|
Object.defineProperty(exports, "SKILL_GRAPH_METADATA_KEY", { enumerable: true, get: function () { return skillGraph_js_1.SKILL_GRAPH_METADATA_KEY; } });
|
|
67
|
+
// The graph's note that its body-contract checks wait for Agent build (present
|
|
68
|
+
// only when built without `knownTools`) — see SkillGraph.deferredBodyContract.
|
|
69
|
+
// The note rides the graph AND each compiled skill's metadata (under
|
|
70
|
+
// SKILL_GRAPH_DEFERRED_CONTRACT_KEY), so Agent build honors it whichever way
|
|
71
|
+
// the skills arrive — `.skillGraph(graph)` or `.skills({ list: () => graph.skills })`.
|
|
72
|
+
Object.defineProperty(exports, "SKILL_GRAPH_DEFERRED_CONTRACT_KEY", { enumerable: true, get: function () { return skillGraph_js_1.SKILL_GRAPH_DEFERRED_CONTRACT_KEY; } });
|
|
62
73
|
var entryScorer_js_1 = require("./entryScorer.js");
|
|
63
74
|
Object.defineProperty(exports, "keywordScorer", { enumerable: true, get: function () { return entryScorer_js_1.keywordScorer; } });
|
|
64
75
|
Object.defineProperty(exports, "embeddingScorer", { enumerable: true, get: function () { return entryScorer_js_1.embeddingScorer; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/injection-engine/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAYH,+DAA+D;AAC/D,uCAAoD;AAA3C,kHAAA,sBAAsB,OAAA;AAE/B,SAAS;AACT,+CAAoD;AAA3C,kHAAA,kBAAkB,OAAA;AAC3B,mFAG0C;AAFxC,6IAAA,2BAA2B,OAAA;AAI7B,gFAAgF;AAChF,yEAAoG;AAA3F,yHAAA,iBAAiB,OAAA;AAC1B,6EAI4C;AAH1C,6HAAA,mBAAmB,OAAA;AACnB,yIAAA,+BAA+B,OAAA;AAIjC,6DAOoC;AANlC,6GAAA,WAAW,OAAA;AACX,oHAAA,kBAAkB,OAAA;AAOpB,uDAA8E;AAArE,iHAAA,aAAa,OAAA;AAEtB,8EAA8E;AAC9E,+EAA+E;AAC/E,6DAA6D;AAC7D,uDAA8E;AAArE,iHAAA,aAAa,OAAA;AAEtB,wEAAwE;AACxE,sEAAsE;AACtE,iEAAiE;AACjE,+BAA+B;AAC/B,iDAA8F;AAArF,oHAAA,mBAAmB,OAAA;AAAE,mHAAA,kBAAkB,OAAA;AAEhD,mEAA2F;AAAlF,mHAAA,cAAc,OAAA;AAEvB,2DAA+E;AAAtE,2GAAA,UAAU,OAAA;AAEnB,6EAA6E;AAC7E,0EAA0E;AAC1E,qDAAqD;AACrD,qEAIwC;AAHtC,qHAAA,eAAe,OAAA;AAKjB,4EAA4E;AAC5E,8EAA8E;AAC9E,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/injection-engine/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAYH,+DAA+D;AAC/D,uCAAoD;AAA3C,kHAAA,sBAAsB,OAAA;AAE/B,uEAAuE;AACvE,8EAA8E;AAC9E,mFAAmF;AACnF,uCAA2C;AAAlC,yGAAA,aAAa,OAAA;AAEtB,SAAS;AACT,+CAAoD;AAA3C,kHAAA,kBAAkB,OAAA;AAC3B,mFAG0C;AAFxC,6IAAA,2BAA2B,OAAA;AAI7B,gFAAgF;AAChF,yEAAoG;AAA3F,yHAAA,iBAAiB,OAAA;AAC1B,6EAI4C;AAH1C,6HAAA,mBAAmB,OAAA;AACnB,yIAAA,+BAA+B,OAAA;AAIjC,6DAOoC;AANlC,6GAAA,WAAW,OAAA;AACX,oHAAA,kBAAkB,OAAA;AAOpB,uDAA8E;AAArE,iHAAA,aAAa,OAAA;AAEtB,8EAA8E;AAC9E,+EAA+E;AAC/E,6DAA6D;AAC7D,uDAA8E;AAArE,iHAAA,aAAa,OAAA;AAEtB,wEAAwE;AACxE,sEAAsE;AACtE,iEAAiE;AACjE,+BAA+B;AAC/B,iDAA8F;AAArF,oHAAA,mBAAmB,OAAA;AAAE,mHAAA,kBAAkB,OAAA;AAEhD,mEAA2F;AAAlF,mHAAA,cAAc,OAAA;AAEvB,2DAA+E;AAAtE,2GAAA,UAAU,OAAA;AAEnB,6EAA6E;AAC7E,0EAA0E;AAC1E,qDAAqD;AACrD,qEAIwC;AAHtC,qHAAA,eAAe,OAAA;AAKjB,4EAA4E;AAC5E,8EAA8E;AAC9E,iDAsDyB;AArDvB,2GAAA,UAAU,OAAA;AACV,4GAAA,WAAW,OAAA;AACX,mFAAmF;AACnF,qFAAqF;AACrF,uFAAuF;AACvF,6BAA6B;AAC7B,8GAAA,aAAa,OAAA;AACb,yHAAA,wBAAwB,OAAA;AAiCxB,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,6EAA6E;AAC7E,uFAAuF;AACvF,kIAAA,iCAAiC,OAAA;AASnC,mDAO0B;AANxB,+GAAA,aAAa,OAAA;AACb,iHAAA,eAAe,OAAA;AACf,6GAAA,WAAW,OAAA;AAKb,uDAK4B;AAJ1B,sHAAA,kBAAkB,OAAA;AAClB,uHAAA,mBAAmB,OAAA;AACnB,kHAAA,cAAc,OAAA"}
|
|
@@ -59,11 +59,13 @@
|
|
|
59
59
|
* per-hop cause, so a model pick was recorded under a declared edge's label).
|
|
60
60
|
*/
|
|
61
61
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
|
-
exports.skillGraph = exports.SKILL_GRAPH_METADATA_KEY = exports.decideSkill = exports.formatCheckup = void 0;
|
|
62
|
+
exports.skillGraph = exports.SKILL_GRAPH_DEFERRED_CONTRACT_KEY = exports.SKILL_GRAPH_METADATA_KEY = exports.decideSkill = exports.formatCheckup = void 0;
|
|
63
63
|
const footprintjs_1 = require("footprintjs");
|
|
64
|
+
const types_js_1 = require("./types.js");
|
|
64
65
|
const entryScorer_js_1 = require("./entryScorer.js");
|
|
65
66
|
const skillGraphCheckup_js_1 = require("./skillGraphCheckup.js");
|
|
66
67
|
const skillContract_js_1 = require("./skillContract.js");
|
|
68
|
+
const skillMatch_js_1 = require("./skillMatch.js");
|
|
67
69
|
var skillGraphCheckup_js_2 = require("./skillGraphCheckup.js");
|
|
68
70
|
Object.defineProperty(exports, "formatCheckup", { enumerable: true, get: function () { return skillGraphCheckup_js_2.formatCheckup; } });
|
|
69
71
|
/** Build a decision node. Leaves are skills (an `Injection`); internal nodes are
|
|
@@ -78,6 +80,17 @@ function isDecisionNode(n) {
|
|
|
78
80
|
}
|
|
79
81
|
/** The metadata key carrying a skill's routing provenance. */
|
|
80
82
|
exports.SKILL_GRAPH_METADATA_KEY = 'skillGraph';
|
|
83
|
+
/**
|
|
84
|
+
* The metadata key carrying the {@link DeferredBodyContract} note on each COMPILED
|
|
85
|
+
* skill. The graph-level `graph.deferredBodyContract` note names the deferral, but
|
|
86
|
+
* skills reach an agent through more than one door — `.skillGraph(graph)` sees the
|
|
87
|
+
* graph, `.skills({ list: () => graph.skills })` sees only the skills — so the note
|
|
88
|
+
* also rides each skill's own metadata. Agent build collects it from the final
|
|
89
|
+
* injection list, whichever door the skills came through, and runs the deferred
|
|
90
|
+
* checks exactly once (skills found by BOTH the metadata and the graph note are
|
|
91
|
+
* deduped by id).
|
|
92
|
+
*/
|
|
93
|
+
exports.SKILL_GRAPH_DEFERRED_CONTRACT_KEY = 'skillGraphDeferredBodyContract';
|
|
81
94
|
/** Mermaid node ids must be identifier-safe; keep the original id as the label. */
|
|
82
95
|
function nodeId(id) {
|
|
83
96
|
return 'n_' + id.replace(/[^A-Za-z0-9_]/g, '_');
|
|
@@ -133,7 +146,25 @@ function skillGraph(config) {
|
|
|
133
146
|
const builder = {
|
|
134
147
|
entry(skill, opts) {
|
|
135
148
|
const id = remember(skill);
|
|
136
|
-
|
|
149
|
+
// Exactly one condition per entry: `match` (data) or `when` (code). Both set
|
|
150
|
+
// is a contradiction — which one starts the turn? — refused rather than
|
|
151
|
+
// silently ANDed or ORed. (Neither set stays the `always` entry it has
|
|
152
|
+
// always been; the rules form additionally refuses "neither", because a
|
|
153
|
+
// rule exists to be conditional — see the config translation below.)
|
|
154
|
+
if (opts?.when !== undefined && opts?.match !== undefined) {
|
|
155
|
+
throw new Error(`skillGraph: entry "${id}" sets both \`match\` and \`when\` — an entry takes ` +
|
|
156
|
+
`exactly one condition. Use \`match\` (a RegExp or { keywords: [...] } over the ` +
|
|
157
|
+
`user message — comparable and drawable) OR \`when\` (a predicate over the ` +
|
|
158
|
+
`iteration context). To combine a pattern with extra logic, fold the pattern ` +
|
|
159
|
+
`into your \`when\` predicate.`);
|
|
160
|
+
}
|
|
161
|
+
const compiled = opts?.match !== undefined ? (0, skillMatch_js_1.compileMatch)(opts.match, `entry "${id}"`) : undefined;
|
|
162
|
+
entries.push({
|
|
163
|
+
id,
|
|
164
|
+
when: compiled ? compiled.predicate : opts?.when,
|
|
165
|
+
...(compiled && { match: compiled.data }),
|
|
166
|
+
label: opts?.label,
|
|
167
|
+
});
|
|
137
168
|
return builder;
|
|
138
169
|
},
|
|
139
170
|
route(from, to, opts) {
|
|
@@ -182,6 +213,14 @@ function skillGraph(config) {
|
|
|
182
213
|
throw new Error('skillGraph: .entryBy()/.entryByRelevance() is for flat entry/route graphs; a ' +
|
|
183
214
|
'.tree() already routes by predicate (the scorer would be ignored).');
|
|
184
215
|
}
|
|
216
|
+
// One dial, one home: a tree declares tool scoping on `.tree(root, { scopeTools })`
|
|
217
|
+
// (object form: the tree arm's own field). Accepting it here too would let the two
|
|
218
|
+
// homes disagree, and only one of them would win — silently.
|
|
219
|
+
if (treeRoot && opts.scopeTools !== undefined) {
|
|
220
|
+
throw new Error('skillGraph: `scopeTools` on build() is the FLAT arm\'s dial. This graph is a ' +
|
|
221
|
+
'.tree(), which declares tool scoping on .tree(root, { scopeTools }) (object ' +
|
|
222
|
+
'form: the tree arm\'s own `scopeTools` field) — set it there instead.');
|
|
223
|
+
}
|
|
185
224
|
// A tree and the flat entry/route wiring are two ways to declare the same
|
|
186
225
|
// thing, and only ONE of them compiles: the tree branch below never reads
|
|
187
226
|
// `entries` or `routes`. Until 8.4.0 that was silent — the tree won and every
|
|
@@ -212,7 +251,16 @@ function skillGraph(config) {
|
|
|
212
251
|
const checkup = (options = {}) => {
|
|
213
252
|
const wiring = (0, skillGraphCheckup_js_1.checkupGraph)({
|
|
214
253
|
skillIds: new Set(skillsById.keys()),
|
|
215
|
-
|
|
254
|
+
// `conditional` counts a data `match` exactly like a `when` (it compiled
|
|
255
|
+
// to one), so a rule-router built from matchers is never read as a
|
|
256
|
+
// fan-out. The match DATA rides along so the pairwise rule checks
|
|
257
|
+
// (`overlapping-rules` / `rules-shadowed-by-order`) have something they
|
|
258
|
+
// can honestly compare.
|
|
259
|
+
entries: entries.map((e) => ({
|
|
260
|
+
id: e.id,
|
|
261
|
+
conditional: e.when !== undefined,
|
|
262
|
+
...(e.match && { match: e.match }),
|
|
263
|
+
})),
|
|
216
264
|
routes: routes.map((r) => ({
|
|
217
265
|
fromId: r.fromId,
|
|
218
266
|
toId: r.toId,
|
|
@@ -316,17 +364,39 @@ function skillGraph(config) {
|
|
|
316
364
|
}
|
|
317
365
|
if (entryScorer)
|
|
318
366
|
scoreEntries = makeScoreEntries(entries, skillsById, entryScorer);
|
|
367
|
+
// `scopeTools: true` (flat) — the WIRED skills, i.e. everything an entry or a
|
|
368
|
+
// route mentions. Stamped exactly as the tree stamps its leaves: a graph-level
|
|
369
|
+
// DEFAULT (`existingAuto ?? …`), never an override of a skill's own declaration.
|
|
370
|
+
// A listed-but-unwired skill stays additive — the graph does not route it, so
|
|
371
|
+
// the graph does not scope it (it remains an OPEN, read_skill-reachable skill).
|
|
372
|
+
// Default `false` keeps today's bytes; 10.0.0 flips the default.
|
|
373
|
+
const scopedIds = opts.scopeTools === true
|
|
374
|
+
? new Set([...entries.map((e) => e.id), ...routes.flatMap((r) => [r.fromId, r.toId])])
|
|
375
|
+
: undefined;
|
|
319
376
|
for (const [id, skill] of skillsById) {
|
|
320
377
|
const trigger = deriveTrigger(id, skill, entries, routes, nextSkillForTriggers, entryScorer !== undefined || llmReadEntry);
|
|
321
378
|
const routing = routingFor(id, entries, routes);
|
|
379
|
+
const existingAuto = skill.metadata
|
|
380
|
+
?.autoActivate;
|
|
381
|
+
const autoActivate = existingAuto ?? (scopedIds?.has(id) ? 'currentSkill' : undefined);
|
|
322
382
|
skills.push({
|
|
323
383
|
...skill,
|
|
324
384
|
...(trigger && { trigger }),
|
|
325
|
-
metadata: {
|
|
385
|
+
metadata: {
|
|
386
|
+
...skill.metadata,
|
|
387
|
+
[exports.SKILL_GRAPH_METADATA_KEY]: routing,
|
|
388
|
+
...(autoActivate && { autoActivate }),
|
|
389
|
+
},
|
|
326
390
|
});
|
|
327
391
|
nodes.push({ id, kind: 'skill', label: id });
|
|
328
392
|
}
|
|
329
|
-
edges.push(...entries.map((e) => ({
|
|
393
|
+
edges.push(...entries.map((e) => ({
|
|
394
|
+
from: null,
|
|
395
|
+
to: e.id,
|
|
396
|
+
kind: 'entry',
|
|
397
|
+
label: e.label,
|
|
398
|
+
...(e.match && { match: e.match }),
|
|
399
|
+
})), ...routes.map((r) => ({
|
|
330
400
|
from: r.fromId,
|
|
331
401
|
to: r.toId,
|
|
332
402
|
kind: r.onToolReturn ? 'on-tool-return' : r.when ? 'predicate' : 'model',
|
|
@@ -342,20 +412,53 @@ function skillGraph(config) {
|
|
|
342
412
|
// surfaced as a run that entered no skill. An explicit `check: 'warn'` still
|
|
343
413
|
// never throws, so the opt-down survives with its name intact.
|
|
344
414
|
const check = opts.check ?? 'throw';
|
|
415
|
+
// Without `knownTools`, the body-contract checks (`body-foreign-tool` /
|
|
416
|
+
// `body-unknown-tool`) cannot tell a typo from a baseline tool the agent
|
|
417
|
+
// registers later — the graph builds before `.tool()` runs. So this PASS
|
|
418
|
+
// defers them (reports nothing it cannot prove) and leaves a
|
|
419
|
+
// `deferredBodyContract` note in TWO places that say the same thing: on the
|
|
420
|
+
// graph, and on each compiled skill's metadata — because skills reach an
|
|
421
|
+
// agent through more than one door (`.skillGraph(graph)` sees the graph;
|
|
422
|
+
// `.skills({ list: () => graph.skills })` sees only the skills). Agent build
|
|
423
|
+
// collects the note from its final injection list and runs the checks exactly
|
|
424
|
+
// once against the full tool registry, whichever door was used. With
|
|
425
|
+
// `knownTools` given, the author supplied the full picture and the checks run
|
|
426
|
+
// right here, as always — no note anywhere, and the agent never re-runs them
|
|
427
|
+
// (one problem, one report). `graph.checkup()` is untouched either way: the
|
|
428
|
+
// explicit lint call always runs every check over what it can see.
|
|
429
|
+
const deferBodyContract = check !== 'off' && opts.knownTools === undefined;
|
|
430
|
+
const deferredNote = deferBodyContract
|
|
431
|
+
? { mode: check }
|
|
432
|
+
: undefined;
|
|
345
433
|
if (check !== 'off') {
|
|
346
434
|
const result = checkup({ ...(opts.knownTools && { knownTools: opts.knownTools }) });
|
|
347
|
-
|
|
348
|
-
|
|
435
|
+
const reported = deferBodyContract
|
|
436
|
+
? {
|
|
437
|
+
ok: result.ok, // body-contract problems are warnings — they never decide `ok`
|
|
438
|
+
problems: result.problems.filter((p) => p.code !== 'body-foreign-tool' && p.code !== 'body-unknown-tool'),
|
|
439
|
+
}
|
|
440
|
+
: result;
|
|
441
|
+
if (check === 'throw' && !reported.ok) {
|
|
442
|
+
throw new Error(`skillGraph: build-time check-up failed:\n${(0, skillGraphCheckup_js_1.formatCheckup)(reported)}`);
|
|
349
443
|
}
|
|
350
|
-
if (
|
|
444
|
+
if (reported.problems.length > 0 && (0, footprintjs_1.isDevMode)()) {
|
|
351
445
|
// eslint-disable-next-line no-console
|
|
352
|
-
console.warn(`skillGraph: build-time check-up found problems:\n${(0, skillGraphCheckup_js_1.formatCheckup)(
|
|
446
|
+
console.warn(`skillGraph: build-time check-up found problems:\n${(0, skillGraphCheckup_js_1.formatCheckup)(reported)}`);
|
|
353
447
|
}
|
|
354
448
|
}
|
|
355
449
|
return {
|
|
356
|
-
|
|
450
|
+
// Deferring? The note rides each skill too (dedup at agent build is by id,
|
|
451
|
+
// so the double stamp can never double-report). No deferral → the exact
|
|
452
|
+
// same objects as always: zero cost when unused.
|
|
453
|
+
skills: deferredNote
|
|
454
|
+
? skills.map((s) => ({
|
|
455
|
+
...s,
|
|
456
|
+
metadata: { ...s.metadata, [exports.SKILL_GRAPH_DEFERRED_CONTRACT_KEY]: deferredNote },
|
|
457
|
+
}))
|
|
458
|
+
: skills,
|
|
357
459
|
edges,
|
|
358
460
|
nodes,
|
|
461
|
+
...(deferredNote && { deferredBodyContract: deferredNote }),
|
|
359
462
|
toMermaid: () => renderMermaid(nodes, edges),
|
|
360
463
|
// `nextSkill` is the `.to` PROJECTION of the one resolver, not a second
|
|
361
464
|
// implementation — the two cannot answer differently.
|
|
@@ -412,9 +515,51 @@ function skillGraph(config) {
|
|
|
412
515
|
builder.entry(resolve(start));
|
|
413
516
|
else if ('use' in start)
|
|
414
517
|
builder.entry(resolve(start.use));
|
|
415
|
-
else if ('rules' in start)
|
|
416
|
-
|
|
417
|
-
|
|
518
|
+
else if ('rules' in start) {
|
|
519
|
+
// (a) rule-id-exists — validate EVERY rule's target before compiling any of
|
|
520
|
+
// them, so the refusal lists every bad id at once (not just the first) and
|
|
521
|
+
// names what IS allowed. An unknown id cannot compile into an entry at all,
|
|
522
|
+
// so this refuses under every `check` mode — a rule silently dropped under
|
|
523
|
+
// `check: 'warn'` would be the accepted-and-silently-wrong build.
|
|
524
|
+
const missing = start.rules
|
|
525
|
+
.map((r, i) => ({ use: r.use, i }))
|
|
526
|
+
.filter(({ use }) => !skillsById.has(use));
|
|
527
|
+
if (missing.length > 0) {
|
|
528
|
+
const known = [...skillsById.keys()].map((k) => `"${k}"`).join(', ');
|
|
529
|
+
const problems = missing.map(({ use, i }) => ({
|
|
530
|
+
kind: 'error',
|
|
531
|
+
code: 'rule-id-exists',
|
|
532
|
+
message: `start.rules[${i}] routes to skill "${use}", which is not in skills[]. ` +
|
|
533
|
+
`Known skill ids: ${known || '(none)'}. Fix the rule's \`use\`, or add the ` +
|
|
534
|
+
`skill to skills[].`,
|
|
535
|
+
skill: use,
|
|
536
|
+
}));
|
|
537
|
+
throw new Error(`skillGraph: build-time check-up failed:\n${(0, skillGraphCheckup_js_1.formatCheckup)({ ok: false, problems })}`);
|
|
538
|
+
}
|
|
539
|
+
for (const [i, r] of start.rules.entries()) {
|
|
540
|
+
// Exactly one of `match`/`when` per rule. The type already says so
|
|
541
|
+
// (`SkillStartRule` is a union with `never` on the other arm); this is the
|
|
542
|
+
// runtime half, for JavaScript callers and configs assembled through `any`.
|
|
543
|
+
// "Neither" is refused too — a rule exists to be conditional; the
|
|
544
|
+
// unconditional start is `start: 'id'` / `{ use }`, and saying so beats
|
|
545
|
+
// compiling a rule that would swallow every turn.
|
|
546
|
+
const use = r.use; // read before the narrowing checks — TS collapses the
|
|
547
|
+
// union to `never` inside the refusal branch (the type says it can't happen;
|
|
548
|
+
// this is the runtime half for callers the compiler never saw).
|
|
549
|
+
const both = r.when !== undefined && r.match !== undefined;
|
|
550
|
+
const neither = r.when === undefined && r.match === undefined;
|
|
551
|
+
if (both || neither) {
|
|
552
|
+
throw new Error(`skillGraph: start.rules[${i}] (use: "${use}") ${both ? 'sets both `match` and `when`' : 'sets neither `match` nor `when`'} — a rule takes exactly one condition. Say when it applies with \`match\` ` +
|
|
553
|
+
`(a RegExp or { keywords: [...] } tested against the user message) or ` +
|
|
554
|
+
`\`when\` (a predicate over the iteration context). For an unconditional ` +
|
|
555
|
+
`start, use \`start: '${use}'\` instead of a rule.`);
|
|
556
|
+
}
|
|
557
|
+
builder.entry(resolve(r.use), {
|
|
558
|
+
...(r.when && { when: r.when }),
|
|
559
|
+
...(r.match !== undefined && { match: r.match }),
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
}
|
|
418
563
|
else {
|
|
419
564
|
for (const id of start.entries)
|
|
420
565
|
builder.entry(resolve(id));
|
|
@@ -437,6 +582,11 @@ function skillGraph(config) {
|
|
|
437
582
|
return builder.build({
|
|
438
583
|
check: config.check ?? 'throw',
|
|
439
584
|
...(config.knownTools && { knownTools: config.knownTools }),
|
|
585
|
+
// The FLAT arm's tool-scoping dial rides through to the one compiler below.
|
|
586
|
+
// (The tree arm's `scopeTools` was already handed to `.tree()` above — passing
|
|
587
|
+
// it here too would trip build()'s one-dial-one-home refusal.)
|
|
588
|
+
...(config.tree === undefined &&
|
|
589
|
+
config.scopeTools !== undefined && { scopeTools: config.scopeTools }),
|
|
440
590
|
});
|
|
441
591
|
}
|
|
442
592
|
return builder;
|
|
@@ -493,16 +643,15 @@ function makeScoreEntries(entries, skillsById, scorer) {
|
|
|
493
643
|
return scorer.score({ userMessage: ctx.userMessage, candidates }, signal);
|
|
494
644
|
};
|
|
495
645
|
}
|
|
496
|
-
/** Does a single route edge fire for
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
return false;
|
|
646
|
+
/** Does a single route edge fire for ONE tool result of the batch?
|
|
647
|
+
* `onToolReturn` matches the tool NAME, `when` runs the predicate over the
|
|
648
|
+
* result (same `{ toolName, result }` shape it has always received). The
|
|
649
|
+
* caller walks the batch in call order (9.16.0) — before that, only
|
|
650
|
+
* `ctx.lastToolResult` was ever offered here. */
|
|
651
|
+
function routeMatches(r, res) {
|
|
503
652
|
if (r.onToolReturn)
|
|
504
|
-
return toolMatcher(r.onToolReturn)(
|
|
505
|
-
return r.when ? r.when(
|
|
653
|
+
return toolMatcher(r.onToolReturn)(res.toolName);
|
|
654
|
+
return r.when ? r.when(res) : false;
|
|
506
655
|
}
|
|
507
656
|
/**
|
|
508
657
|
* The cursor resolver (the keystone). Pure + deterministic. Given the iteration
|
|
@@ -512,7 +661,10 @@ function routeMatches(r, ctx) {
|
|
|
512
661
|
* (an `always`-entry — no `when` — matches unconditionally), else the entry
|
|
513
662
|
* the MODEL picked with `read_skill` (see "the model's pick" below);
|
|
514
663
|
* • a `from`-gated route (`fromId === currentSkillId`) whose predicate matches
|
|
515
|
-
*
|
|
664
|
+
* a result of the iteration's tool batch — results in call order, edges per
|
|
665
|
+
* result in declaration order; the first result with a match wins, later
|
|
666
|
+
* results matching OTHER targets are reported as `conflict` (9.16.0) →
|
|
667
|
+
* its target (the transition);
|
|
516
668
|
* • else the model's pick, when the read_skill gate accepted one this turn;
|
|
517
669
|
* • otherwise the current cursor unchanged (sticky stay).
|
|
518
670
|
*
|
|
@@ -576,22 +728,59 @@ function makeResolveCursor(entries, routes, llmReadEntry = false) {
|
|
|
576
728
|
}
|
|
577
729
|
return { by: 'none' };
|
|
578
730
|
}
|
|
579
|
-
// D1 — transition:
|
|
580
|
-
//
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
731
|
+
// D1 — transition: the whole tool batch, in CALL order (9.16.0). Per
|
|
732
|
+
// result, the first from-gated deterministic edge in DECLARATION order
|
|
733
|
+
// that fires names that result's target; the first RESULT with a target
|
|
734
|
+
// wins the cursor. Two orders, two jobs: declaration order is the
|
|
735
|
+
// author's tiebreak when one result could match several edges (the D1
|
|
736
|
+
// law, unchanged), call order is the run's tiebreak across results —
|
|
737
|
+
// the tool that returned first routed first. Before 9.16.0 only the
|
|
738
|
+
// LAST call of a batch was consulted, so identical batches routed
|
|
739
|
+
// differently depending on call order (the 9.15.0 L1 probe). A later
|
|
740
|
+
// result matching a DIFFERENT target is suppressed AND reported on the
|
|
741
|
+
// move (`conflict`), never silently dropped; a batch of one is
|
|
742
|
+
// byte-identical to the old singular walk. D1 still wins over a
|
|
743
|
+
// same-turn model pick — INCLUDING when both name the same target,
|
|
744
|
+
// which is why the cause is decided here and not inferred downstream
|
|
745
|
+
// from the destination.
|
|
746
|
+
let winner;
|
|
747
|
+
let losers;
|
|
748
|
+
for (const res of (0, types_js_1.toolResultsOf)(ctx)) {
|
|
749
|
+
let target;
|
|
750
|
+
for (const r of routes) {
|
|
751
|
+
if (r.fromId !== cur)
|
|
752
|
+
continue;
|
|
753
|
+
if (!r.when && !r.onToolReturn)
|
|
754
|
+
continue; // model edges don't auto-fire
|
|
755
|
+
try {
|
|
756
|
+
if (routeMatches(r, res)) {
|
|
757
|
+
target = r.toId;
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
catch (err) {
|
|
762
|
+
warnMatcherThrew(`route ${r.fromId}→${r.toId}`, err);
|
|
763
|
+
}
|
|
594
764
|
}
|
|
765
|
+
if (target === undefined)
|
|
766
|
+
continue;
|
|
767
|
+
const outcome = {
|
|
768
|
+
...(res.toolCallId !== undefined && { toolCallId: res.toolCallId }),
|
|
769
|
+
toolName: res.toolName,
|
|
770
|
+
target,
|
|
771
|
+
};
|
|
772
|
+
if (winner === undefined)
|
|
773
|
+
winner = outcome;
|
|
774
|
+
else if (target !== winner.target)
|
|
775
|
+
(losers ??= []).push(outcome);
|
|
776
|
+
}
|
|
777
|
+
if (winner !== undefined) {
|
|
778
|
+
return {
|
|
779
|
+
...from,
|
|
780
|
+
to: winner.target,
|
|
781
|
+
by: 'route',
|
|
782
|
+
...(losers !== undefined && { conflict: { winner, losers } }),
|
|
783
|
+
};
|
|
595
784
|
}
|
|
596
785
|
// D2 — the validated volunteer: no declared edge fired, so the model's own
|
|
597
786
|
// (already gated) pick moves the cursor. A pick of the CURRENT skill is a
|
|
@@ -911,7 +1100,11 @@ function attachExactlyOneLeafMonitor(skills) {
|
|
|
911
1100
|
function routingFor(id, entries, routes) {
|
|
912
1101
|
const entry = entries.find((e) => e.id === id);
|
|
913
1102
|
if (entry)
|
|
914
|
-
return {
|
|
1103
|
+
return {
|
|
1104
|
+
via: 'entry',
|
|
1105
|
+
...(entry.label && { label: entry.label }),
|
|
1106
|
+
...(entry.match && { match: entry.match }),
|
|
1107
|
+
};
|
|
915
1108
|
const incoming = routes.filter((r) => r.toId === id && (r.when || r.onToolReturn));
|
|
916
1109
|
const first = incoming[0];
|
|
917
1110
|
if (first) {
|
|
@@ -936,7 +1129,10 @@ function renderMermaid(nodes, edges) {
|
|
|
936
1129
|
for (const e of edges) {
|
|
937
1130
|
const from = e.from === null ? '__start__' : ref(e.from);
|
|
938
1131
|
const arrow = e.kind === 'model' ? '-.->' : '-->'; // model edges dashed
|
|
939
|
-
|
|
1132
|
+
// An explicit label wins unchanged; a data matcher captions its entry edge
|
|
1133
|
+
// (that is half the point of declaring the rule as data — it can be drawn).
|
|
1134
|
+
const caption = e.label ?? (e.match ? (0, skillMatch_js_1.mermaidMatchCaption)(e.match) : undefined);
|
|
1135
|
+
const label = caption ? `|${caption}|` : '';
|
|
940
1136
|
lines.push(` ${from} ${arrow}${label} ${ref(e.to)}`);
|
|
941
1137
|
}
|
|
942
1138
|
return lines.join('\n');
|