agentfootprint 9.18.0 → 9.19.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 +4 -2
- package/README.md +2 -0
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/core/Agent.js +71 -1
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +66 -1
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/buildAgentChart.js +19 -0
- package/dist/core/agent/buildAgentChart.js.map +1 -1
- package/dist/core/agent/buildDynamicAgentChart.js +42 -0
- package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
- package/dist/core/agent/skillBrains.js +216 -0
- package/dist/core/agent/skillBrains.js.map +1 -0
- package/dist/core/agent/stages/callLLM.js +29 -9
- package/dist/core/agent/stages/callLLM.js.map +1 -1
- package/dist/core/agent/stages/routeTurn.js +123 -3
- package/dist/core/agent/stages/routeTurn.js.map +1 -1
- package/dist/core/agent/stages/seed.js +9 -0
- package/dist/core/agent/stages/seed.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +326 -8
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/agent/toolEffects.js +242 -0
- package/dist/core/agent/toolEffects.js.map +1 -0
- package/dist/esm/core/Agent.d.ts +6 -1
- package/dist/esm/core/Agent.js +71 -1
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +43 -3
- package/dist/esm/core/agent/AgentBuilder.js +66 -1
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/buildAgentChart.d.ts +11 -0
- package/dist/esm/core/agent/buildAgentChart.js +19 -0
- package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
- package/dist/esm/core/agent/buildDynamicAgentChart.js +42 -0
- package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
- package/dist/esm/core/agent/skillBrains.d.ts +144 -0
- package/dist/esm/core/agent/skillBrains.js +209 -0
- package/dist/esm/core/agent/skillBrains.js.map +1 -0
- package/dist/esm/core/agent/stages/callLLM.d.ts +24 -0
- package/dist/esm/core/agent/stages/callLLM.js +29 -9
- package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
- package/dist/esm/core/agent/stages/routeTurn.d.ts +23 -0
- package/dist/esm/core/agent/stages/routeTurn.js +123 -3
- package/dist/esm/core/agent/stages/routeTurn.js.map +1 -1
- package/dist/esm/core/agent/stages/seed.d.ts +8 -0
- package/dist/esm/core/agent/stages/seed.js +9 -0
- package/dist/esm/core/agent/stages/seed.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +33 -0
- package/dist/esm/core/agent/stages/toolCalls.js +326 -8
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/toolEffects.d.ts +202 -0
- package/dist/esm/core/agent/toolEffects.js +234 -0
- package/dist/esm/core/agent/toolEffects.js.map +1 -0
- package/dist/esm/core/agent/types.d.ts +35 -0
- package/dist/esm/events/payloads.d.ts +123 -2
- package/dist/esm/events/registry.d.ts +5 -1
- package/dist/esm/events/registry.js +4 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js +57 -1
- package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
- package/dist/esm/lib/injection-engine/constrainedEnumPick.d.ts +52 -0
- package/dist/esm/lib/injection-engine/constrainedEnumPick.js +101 -0
- package/dist/esm/lib/injection-engine/constrainedEnumPick.js.map +1 -0
- package/dist/esm/lib/injection-engine/evaluator.d.ts +7 -0
- package/dist/esm/lib/injection-engine/evaluator.js +16 -0
- package/dist/esm/lib/injection-engine/evaluator.js.map +1 -1
- package/dist/esm/lib/injection-engine/factories/defineSkill.d.ts +22 -0
- package/dist/esm/lib/injection-engine/factories/defineSkill.js +7 -0
- package/dist/esm/lib/injection-engine/factories/defineSkill.js.map +1 -1
- package/dist/esm/lib/injection-engine/index.d.ts +1 -0
- package/dist/esm/lib/injection-engine/index.js +3 -0
- package/dist/esm/lib/injection-engine/index.js.map +1 -1
- package/dist/esm/lib/injection-engine/llmClassifier.d.ts +11 -8
- package/dist/esm/lib/injection-engine/llmClassifier.js +27 -84
- package/dist/esm/lib/injection-engine/llmClassifier.js.map +1 -1
- package/dist/esm/lib/injection-engine/routingPolicy.d.ts +10 -3
- package/dist/esm/lib/injection-engine/routingPolicy.js.map +1 -1
- package/dist/esm/lib/injection-engine/skillGraph.d.ts +28 -3
- package/dist/esm/lib/injection-engine/skillGraph.js +80 -12
- package/dist/esm/lib/injection-engine/skillGraph.js.map +1 -1
- package/dist/esm/lib/injection-engine/types.d.ts +30 -0
- package/dist/esm/lib/injection-engine/types.js.map +1 -1
- package/dist/esm/recorders/observability/RouteRecorder.d.ts +6 -1
- package/dist/esm/recorders/observability/RouteRecorder.js +10 -0
- package/dist/esm/recorders/observability/RouteRecorder.js.map +1 -1
- package/dist/esm/recorders/observability/commentary/commentaryTemplates.js +58 -0
- package/dist/esm/recorders/observability/commentary/commentaryTemplates.js.map +1 -1
- package/dist/events/registry.js +4 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/injection-engine/buildInjectionEngineSubflow.js +57 -1
- package/dist/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
- package/dist/lib/injection-engine/constrainedEnumPick.js +105 -0
- package/dist/lib/injection-engine/constrainedEnumPick.js.map +1 -0
- package/dist/lib/injection-engine/evaluator.js +16 -0
- package/dist/lib/injection-engine/evaluator.js.map +1 -1
- package/dist/lib/injection-engine/factories/defineSkill.js +7 -0
- package/dist/lib/injection-engine/factories/defineSkill.js.map +1 -1
- package/dist/lib/injection-engine/index.js +5 -1
- package/dist/lib/injection-engine/index.js.map +1 -1
- package/dist/lib/injection-engine/llmClassifier.js +27 -84
- package/dist/lib/injection-engine/llmClassifier.js.map +1 -1
- package/dist/lib/injection-engine/routingPolicy.js.map +1 -1
- package/dist/lib/injection-engine/skillGraph.js +80 -12
- package/dist/lib/injection-engine/skillGraph.js.map +1 -1
- package/dist/lib/injection-engine/types.js.map +1 -1
- package/dist/recorders/observability/RouteRecorder.js +10 -0
- package/dist/recorders/observability/RouteRecorder.js.map +1 -1
- package/dist/recorders/observability/commentary/commentaryTemplates.js +58 -0
- package/dist/recorders/observability/commentary/commentaryTemplates.js.map +1 -1
- package/dist/types/core/Agent.d.ts +6 -1
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +43 -3
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/buildAgentChart.d.ts +11 -0
- 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/skillBrains.d.ts +145 -0
- package/dist/types/core/agent/skillBrains.d.ts.map +1 -0
- package/dist/types/core/agent/stages/callLLM.d.ts +24 -0
- package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
- package/dist/types/core/agent/stages/routeTurn.d.ts +23 -0
- package/dist/types/core/agent/stages/routeTurn.d.ts.map +1 -1
- package/dist/types/core/agent/stages/seed.d.ts +8 -0
- package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +33 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/toolEffects.d.ts +203 -0
- package/dist/types/core/agent/toolEffects.d.ts.map +1 -0
- package/dist/types/core/agent/types.d.ts +35 -0
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +123 -2
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +5 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/constrainedEnumPick.d.ts +53 -0
- package/dist/types/lib/injection-engine/constrainedEnumPick.d.ts.map +1 -0
- package/dist/types/lib/injection-engine/evaluator.d.ts +7 -0
- package/dist/types/lib/injection-engine/evaluator.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/factories/defineSkill.d.ts +22 -0
- package/dist/types/lib/injection-engine/factories/defineSkill.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/index.d.ts +1 -0
- package/dist/types/lib/injection-engine/index.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/llmClassifier.d.ts +11 -8
- package/dist/types/lib/injection-engine/llmClassifier.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/routingPolicy.d.ts +10 -3
- package/dist/types/lib/injection-engine/routingPolicy.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/skillGraph.d.ts +28 -3
- package/dist/types/lib/injection-engine/skillGraph.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/types.d.ts +30 -0
- package/dist/types/lib/injection-engine/types.d.ts.map +1 -1
- package/dist/types/recorders/observability/RouteRecorder.d.ts +6 -1
- package/dist/types/recorders/observability/RouteRecorder.d.ts.map +1 -1
- package/dist/types/recorders/observability/commentary/commentaryTemplates.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
*/
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
47
|
exports.buildToolCallsHandler = void 0;
|
|
48
|
+
const footprintjs_1 = require("footprintjs");
|
|
48
49
|
const types_js_1 = require("../../../adapters/types.js");
|
|
49
50
|
const typedEmit_js_1 = require("../../../recorders/core/typedEmit.js");
|
|
50
51
|
const extractSequence_js_1 = require("../../../security/extractSequence.js");
|
|
@@ -60,6 +61,7 @@ const toolArgsValidation_js_1 = require("../toolArgsValidation.js");
|
|
|
60
61
|
const validators_js_1 = require("../validators.js");
|
|
61
62
|
const toolResultCap_js_1 = require("../toolResultCap.js");
|
|
62
63
|
const skillSteps_js_1 = require("../../../lib/injection-engine/skillSteps.js");
|
|
64
|
+
const toolEffects_js_1 = require("../toolEffects.js");
|
|
63
65
|
/** Declaration order preserved, ids de-duplicated — the shape the gate's re-prompt
|
|
64
66
|
* and the `skill.rejected` payload both report as "what this gate accepts". */
|
|
65
67
|
function dedupeIds(ids) {
|
|
@@ -351,6 +353,195 @@ function buildToolCallsHandler(deps) {
|
|
|
351
353
|
const plan = deps.stepPlanFor?.(pickedId);
|
|
352
354
|
return plan ? (0, skillSteps_js_1.readSkillStepIntro)(plan) : '';
|
|
353
355
|
};
|
|
356
|
+
// ── Escalate-on-evidence (9.19.0) — the refusal budget ─────────────────
|
|
357
|
+
// Called beside BOTH `skill.rejected` emit sites (reachability + posture):
|
|
358
|
+
// real recorded refusals are the only evidence that counts. At the
|
|
359
|
+
// declared threshold the flip is committed (`skillEscalated` — the fact
|
|
360
|
+
// callLLM's brainFor reads) and `skill.escalated` fires ONCE; the counter
|
|
361
|
+
// keeps counting so the record shows the whole loop. Gated on the policy
|
|
362
|
+
// being declared — otherwise not one key is read or written.
|
|
363
|
+
const noteSkillRefusal = (scope, iteration) => {
|
|
364
|
+
const escalation = deps.escalation;
|
|
365
|
+
if (!escalation)
|
|
366
|
+
return;
|
|
367
|
+
const refusals = (scope.skillRefusalsThisTurn ?? 0) + 1;
|
|
368
|
+
scope.skillRefusalsThisTurn = refusals;
|
|
369
|
+
if (refusals >= escalation.afterRefusals && scope.skillEscalated !== true) {
|
|
370
|
+
scope.skillEscalated = true;
|
|
371
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.skill.escalated', {
|
|
372
|
+
iteration,
|
|
373
|
+
afterRefusals: escalation.afterRefusals,
|
|
374
|
+
refusals,
|
|
375
|
+
from: escalation.describeFrom(scope.currentSkillId, scope.resolvedModel),
|
|
376
|
+
to: escalation.to,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
const warnEffect = (toolName, sentence) => {
|
|
381
|
+
if ((0, footprintjs_1.isDevMode)()) {
|
|
382
|
+
// eslint-disable-next-line no-console
|
|
383
|
+
console.warn(`agentfootprint tool-effects: tool '${toolName}' — ${sentence}`);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
/**
|
|
387
|
+
* Judge one finalized result's effects, in declaration order. Returns the
|
|
388
|
+
* model-visible NOTE (teaching refusals only — an accepted effect speaks
|
|
389
|
+
* through what it does: the cursor move, the delivered instruction).
|
|
390
|
+
* Every judgment is a `tools.effect` event; nothing is half-applied.
|
|
391
|
+
*/
|
|
392
|
+
const applyToolEffects = (scope, call, envelope, state) => {
|
|
393
|
+
let note = '';
|
|
394
|
+
const refuse = (kind, refusalReason, fields = {}) => {
|
|
395
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.tools.effect', {
|
|
396
|
+
kind,
|
|
397
|
+
outcome: 'refused',
|
|
398
|
+
toolName: call.toolName,
|
|
399
|
+
toolCallId: call.toolCallId,
|
|
400
|
+
iteration: call.iteration,
|
|
401
|
+
...(fields.targetSkillId !== undefined && { targetSkillId: fields.targetSkillId }),
|
|
402
|
+
...(fields.reason !== undefined && { reason: fields.reason }),
|
|
403
|
+
...(fields.instructionId !== undefined && { instructionId: fields.instructionId }),
|
|
404
|
+
...(fields.deliveryLease !== undefined && { deliveryLease: fields.deliveryLease }),
|
|
405
|
+
refusalReason,
|
|
406
|
+
});
|
|
407
|
+
note += ` [tool effect refused: ${refusalReason}]`;
|
|
408
|
+
warnEffect(call.toolName, refusalReason);
|
|
409
|
+
};
|
|
410
|
+
for (const bad of envelope.malformed)
|
|
411
|
+
refuse(bad.kind, bad.refusalReason);
|
|
412
|
+
for (const effect of envelope.effects) {
|
|
413
|
+
if (effect.kind === 'propose-transition') {
|
|
414
|
+
const target = effect.targetSkillId;
|
|
415
|
+
if (!deps.allowedSkillIds) {
|
|
416
|
+
refuse('propose-transition', `propose-transition → '${target}' needs a mounted skill graph: without one there ` +
|
|
417
|
+
`is no routing law to check the proposal against and no cursor to move. Mount ` +
|
|
418
|
+
`.skillGraph(), or drop the effect.`, { targetSkillId: target, reason: effect.reason });
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const currentSkillId = scope.currentSkillId;
|
|
422
|
+
const hops = deps.allowedSkillIds(currentSkillId);
|
|
423
|
+
if (!hops.includes(target)) {
|
|
424
|
+
refuse('propose-transition', `propose-transition → '${target}' was refused: '${target}' is not reachable from ` +
|
|
425
|
+
`${currentSkillId !== undefined ? `'${currentSkillId}'` : 'the turn start'} per ` +
|
|
426
|
+
`the graph's own law${hops.length > 0 ? ` (reachable: ${hops.join(', ')})` : ''}. The graph decides — a proposal is evidence, never authority.`, { targetSkillId: target, reason: effect.reason });
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (state.winner !== undefined && state.winner.targetSkillId !== target) {
|
|
430
|
+
// A later proposal to a DIFFERENT target — suppressed under the
|
|
431
|
+
// route_conflict law (first in call order wins), on the record.
|
|
432
|
+
state.losers.push({
|
|
433
|
+
toolCallId: call.toolCallId,
|
|
434
|
+
toolName: call.toolName,
|
|
435
|
+
target,
|
|
436
|
+
});
|
|
437
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.tools.effect', {
|
|
438
|
+
kind: 'propose-transition',
|
|
439
|
+
outcome: 'superseded',
|
|
440
|
+
toolName: call.toolName,
|
|
441
|
+
toolCallId: call.toolCallId,
|
|
442
|
+
iteration: call.iteration,
|
|
443
|
+
targetSkillId: target,
|
|
444
|
+
reason: effect.reason,
|
|
445
|
+
supersededBy: 'earlier-proposal',
|
|
446
|
+
});
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
if (state.winner === undefined) {
|
|
450
|
+
state.winner = {
|
|
451
|
+
targetSkillId: target,
|
|
452
|
+
toolName: call.toolName,
|
|
453
|
+
toolCallId: call.toolCallId,
|
|
454
|
+
reason: effect.reason,
|
|
455
|
+
iteration: call.iteration,
|
|
456
|
+
};
|
|
457
|
+
// Committed AT ACCEPTANCE (not batch end) so a pause later in the
|
|
458
|
+
// batch checkpoints the accepted move instead of dropping it.
|
|
459
|
+
scope.pendingToolTransition = state.winner;
|
|
460
|
+
// The activation promise a gate-accepted read_skill pick makes,
|
|
461
|
+
// made here too: a model-edge-only target keeps its llm-activated
|
|
462
|
+
// trigger, so the ledger entry is what makes the body actually
|
|
463
|
+
// load when the cursor lands.
|
|
464
|
+
const activated = scope.activatedInjectionIds;
|
|
465
|
+
if (!activated.includes(target)) {
|
|
466
|
+
scope.activatedInjectionIds = [...activated, target];
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
// First acceptance AND a same-target repeat both land here: every
|
|
470
|
+
// proposal that asked for the move that happens is 'accepted'.
|
|
471
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.tools.effect', {
|
|
472
|
+
kind: 'propose-transition',
|
|
473
|
+
outcome: 'accepted',
|
|
474
|
+
toolName: call.toolName,
|
|
475
|
+
toolCallId: call.toolCallId,
|
|
476
|
+
iteration: call.iteration,
|
|
477
|
+
targetSkillId: target,
|
|
478
|
+
reason: effect.reason,
|
|
479
|
+
});
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
// require-instruction
|
|
483
|
+
const known = deps.leaseTargets?.get(effect.instructionId);
|
|
484
|
+
if (known === undefined) {
|
|
485
|
+
refuse('require-instruction', `require-instruction '${effect.instructionId}' was refused: no registered ` +
|
|
486
|
+
`injection carries that id — the push door serves the declared catalog only ` +
|
|
487
|
+
`(read_skill stays the pull door).`, { instructionId: effect.instructionId, deliveryLease: effect.deliveryLease });
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (known.surfaceMode === 'tool-only') {
|
|
491
|
+
refuse('require-instruction', `require-instruction '${effect.instructionId}' was refused: that skill declares ` +
|
|
492
|
+
`surfaceMode 'tool-only', whose body travels only as a read_skill result — the ` +
|
|
493
|
+
`system slot suppresses it, so the lease would be granted and never delivered. ` +
|
|
494
|
+
`Pull it with read_skill, or change the skill's surfaceMode.`, { instructionId: effect.instructionId, deliveryLease: effect.deliveryLease });
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
const tenant = (0, toolEffects_js_1.tenantOf)(scope.currentSkillId, scope.activatedInjectionIds);
|
|
498
|
+
const lease = {
|
|
499
|
+
instructionId: effect.instructionId,
|
|
500
|
+
deliveryLease: effect.deliveryLease,
|
|
501
|
+
...(tenant !== undefined && { skillId: tenant }),
|
|
502
|
+
toolName: call.toolName,
|
|
503
|
+
toolCallId: call.toolCallId,
|
|
504
|
+
iteration: call.iteration,
|
|
505
|
+
};
|
|
506
|
+
// Prune-as-we-write: spent grants leave with the same write that adds
|
|
507
|
+
// the new one, so a long turn never accumulates dead leases.
|
|
508
|
+
scope.instructionLeases = [
|
|
509
|
+
...(0, toolEffects_js_1.pruneLeases)(scope.instructionLeases, call.iteration, tenant),
|
|
510
|
+
lease,
|
|
511
|
+
];
|
|
512
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.tools.effect', {
|
|
513
|
+
kind: 'require-instruction',
|
|
514
|
+
outcome: 'accepted',
|
|
515
|
+
toolName: call.toolName,
|
|
516
|
+
toolCallId: call.toolCallId,
|
|
517
|
+
iteration: call.iteration,
|
|
518
|
+
instructionId: effect.instructionId,
|
|
519
|
+
deliveryLease: effect.deliveryLease,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
return note;
|
|
523
|
+
};
|
|
524
|
+
/** The batch-end aggregate for conflicting proposals — the
|
|
525
|
+
* `route_conflict` reuse, additive `source` says what conflicted. The
|
|
526
|
+
* per-effect `tools.effect` events above already carried every fact;
|
|
527
|
+
* this is the one-event-per-batch view the edge law already ships. */
|
|
528
|
+
const emitTransitionConflict = (scope, iteration, state) => {
|
|
529
|
+
if (state.winner === undefined || state.losers.length === 0)
|
|
530
|
+
return;
|
|
531
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.skill.route_conflict', {
|
|
532
|
+
iteration,
|
|
533
|
+
...(scope.currentSkillId !== undefined && {
|
|
534
|
+
fromSkillId: scope.currentSkillId,
|
|
535
|
+
}),
|
|
536
|
+
winner: {
|
|
537
|
+
...(state.winner.toolCallId !== undefined && { toolCallId: state.winner.toolCallId }),
|
|
538
|
+
toolName: state.winner.toolName,
|
|
539
|
+
target: state.winner.targetSkillId,
|
|
540
|
+
},
|
|
541
|
+
losers: state.losers.map((l) => ({ ...l })),
|
|
542
|
+
source: 'tool-proposal',
|
|
543
|
+
});
|
|
544
|
+
};
|
|
354
545
|
/**
|
|
355
546
|
* Every teardown scope an Agent run can honour (9.7.0).
|
|
356
547
|
*
|
|
@@ -596,6 +787,18 @@ function buildToolCallsHandler(deps) {
|
|
|
596
787
|
...sessionContext(scope, toolName, toolCallId),
|
|
597
788
|
});
|
|
598
789
|
await endCall(toolCallId);
|
|
790
|
+
// The typed effects channel (9.19.0) — same unwrap as the batch loop,
|
|
791
|
+
// at this path's own execute boundary, so a resumed call's envelope is
|
|
792
|
+
// judged exactly as an inline one's.
|
|
793
|
+
const envelope = (0, toolEffects_js_1.readToolResultEnvelope)(result);
|
|
794
|
+
if (envelope !== undefined) {
|
|
795
|
+
return { result: envelope.content, executed: true, envelope };
|
|
796
|
+
}
|
|
797
|
+
// A status-only shape missing its `effects: []` marker is DATA (bytes
|
|
798
|
+
// unchanged) — but never silently: name the dropped marker in dev mode.
|
|
799
|
+
const nearMiss = (0, toolEffects_js_1.explainStatusOnlyNearMiss)(result);
|
|
800
|
+
if (nearMiss !== undefined)
|
|
801
|
+
warnEffect(toolName, nearMiss);
|
|
599
802
|
return { result, executed: true };
|
|
600
803
|
}
|
|
601
804
|
catch (err) {
|
|
@@ -678,6 +881,10 @@ function buildToolCallsHandler(deps) {
|
|
|
678
881
|
// `on-tool-return` triggers and skill-graph routes read the batch.
|
|
679
882
|
const batchResults = [];
|
|
680
883
|
scope.toolResults = [];
|
|
884
|
+
// ── The batch's transition bookkeeping (9.19.0) ───────────────────
|
|
885
|
+
// First ACCEPTED `propose-transition` wins (committed at acceptance);
|
|
886
|
+
// later ones to other targets are suppressed + aggregated at batch end.
|
|
887
|
+
const transitionState = { losers: [] };
|
|
681
888
|
// Capture run identity from scope for the enriched permission ctx.
|
|
682
889
|
// Same value the Tools slot passes to ToolProvider.list(ctx) so the
|
|
683
890
|
// checker sees consistent identity across both gates.
|
|
@@ -694,6 +901,11 @@ function buildToolCallsHandler(deps) {
|
|
|
694
901
|
const startMs = Date.now();
|
|
695
902
|
let result;
|
|
696
903
|
let error;
|
|
904
|
+
/** The typed effects channel (9.19.0) — set only when the tool
|
|
905
|
+
* returned a recognized result envelope; everything else keeps
|
|
906
|
+
* today's path byte for byte. */
|
|
907
|
+
let toolEnvelope;
|
|
908
|
+
let toolStatus;
|
|
697
909
|
// Permission gate — when a checker is configured, evaluate BEFORE
|
|
698
910
|
// executing the tool. Emits `permission.check` with the decision.
|
|
699
911
|
//
|
|
@@ -1166,6 +1378,26 @@ function buildToolCallsHandler(deps) {
|
|
|
1166
1378
|
...(resolvedCredential && { credential: resolvedCredential }),
|
|
1167
1379
|
...sessionContext(scope, tc.name, tc.id),
|
|
1168
1380
|
});
|
|
1381
|
+
// The typed effects channel (9.19.0): a recognized envelope is
|
|
1382
|
+
// unwrapped HERE, at the one boundary the raw return crosses —
|
|
1383
|
+
// everything downstream (governance rules, the cap, history,
|
|
1384
|
+
// `tool_end`) sees the CONTENT, exactly what a bare return
|
|
1385
|
+
// would have shown. The effects are judged below, after the
|
|
1386
|
+
// gates, in call order.
|
|
1387
|
+
const envelope = (0, toolEffects_js_1.readToolResultEnvelope)(result);
|
|
1388
|
+
if (envelope !== undefined) {
|
|
1389
|
+
toolEnvelope = envelope;
|
|
1390
|
+
result = envelope.content;
|
|
1391
|
+
toolStatus = envelope.status;
|
|
1392
|
+
}
|
|
1393
|
+
else {
|
|
1394
|
+
// A status-only shape missing its `effects: []` marker is
|
|
1395
|
+
// DATA (bytes unchanged) — but never silently: name the
|
|
1396
|
+
// dropped marker in dev mode.
|
|
1397
|
+
const nearMiss = (0, toolEffects_js_1.explainStatusOnlyNearMiss)(result);
|
|
1398
|
+
if (nearMiss !== undefined)
|
|
1399
|
+
warnEffect(tc.name, nearMiss);
|
|
1400
|
+
}
|
|
1169
1401
|
await endCall(tc.id);
|
|
1170
1402
|
}
|
|
1171
1403
|
catch (err) {
|
|
@@ -1235,6 +1467,7 @@ function buildToolCallsHandler(deps) {
|
|
|
1235
1467
|
allowed,
|
|
1236
1468
|
iteration,
|
|
1237
1469
|
});
|
|
1470
|
+
noteSkillRefusal(scope, iteration);
|
|
1238
1471
|
}
|
|
1239
1472
|
else if (deps.skillStrictness !== undefined && skillHop) {
|
|
1240
1473
|
// ── The POSTURE arm (SG-C) — after reachability, hops only. ──
|
|
@@ -1256,6 +1489,7 @@ function buildToolCallsHandler(deps) {
|
|
|
1256
1489
|
iteration,
|
|
1257
1490
|
posture: deps.skillStrictness,
|
|
1258
1491
|
});
|
|
1492
|
+
noteSkillRefusal(scope, iteration);
|
|
1259
1493
|
}
|
|
1260
1494
|
}
|
|
1261
1495
|
}
|
|
@@ -1300,6 +1534,9 @@ function buildToolCallsHandler(deps) {
|
|
|
1300
1534
|
result: capped.result,
|
|
1301
1535
|
durationMs,
|
|
1302
1536
|
...(error === true && { error: true }),
|
|
1537
|
+
// The tool's own declared outcome (9.19.0) — additive, envelope
|
|
1538
|
+
// tools only.
|
|
1539
|
+
...(toolStatus !== undefined && { status: toolStatus }),
|
|
1303
1540
|
});
|
|
1304
1541
|
let resultStr = typeof modelResult === 'string' ? modelResult : (0, validators_js_1.safeStringify)(modelResult);
|
|
1305
1542
|
// ── Step boundary (9.18.0): advance decided, then decorate, THEN
|
|
@@ -1327,6 +1564,15 @@ function buildToolCallsHandler(deps) {
|
|
|
1327
1564
|
});
|
|
1328
1565
|
}
|
|
1329
1566
|
}
|
|
1567
|
+
// ── Typed tool effects (9.19.0) — judged in call order ──────────
|
|
1568
|
+
// Only for a call that RAN clean and returned an envelope. Refusal
|
|
1569
|
+
// notes join the model-visible result AFTER the cap (framework
|
|
1570
|
+
// truth, the step-suffix precedent); accepted effects speak through
|
|
1571
|
+
// what they do — the cursor move, the delivered instruction — and
|
|
1572
|
+
// through `tools.effect` on the record.
|
|
1573
|
+
if (toolEnvelope !== undefined && executed && !error && !denied) {
|
|
1574
|
+
resultStr += applyToolEffects(scope, { toolName: tc.name, toolCallId: tc.id, iteration }, toolEnvelope, transitionState);
|
|
1575
|
+
}
|
|
1330
1576
|
newHistory.push({
|
|
1331
1577
|
role: 'tool',
|
|
1332
1578
|
content: resultStr,
|
|
@@ -1342,8 +1588,17 @@ function buildToolCallsHandler(deps) {
|
|
|
1342
1588
|
// (call order preserved), so an earlier parallel call's
|
|
1343
1589
|
// routing implication is no longer overwritten by a
|
|
1344
1590
|
// later sibling's.
|
|
1345
|
-
scope.lastToolResult = {
|
|
1346
|
-
|
|
1591
|
+
scope.lastToolResult = {
|
|
1592
|
+
toolName: tc.name,
|
|
1593
|
+
result: resultStr,
|
|
1594
|
+
...(toolStatus !== undefined && { status: toolStatus }),
|
|
1595
|
+
};
|
|
1596
|
+
batchResults.push({
|
|
1597
|
+
toolName: tc.name,
|
|
1598
|
+
result: resultStr,
|
|
1599
|
+
toolCallId: tc.id,
|
|
1600
|
+
...(toolStatus !== undefined && { status: toolStatus }),
|
|
1601
|
+
});
|
|
1347
1602
|
scope.toolResults = [...batchResults];
|
|
1348
1603
|
// (2) `read_skill` is the auto-attached activation tool.
|
|
1349
1604
|
// When the LLM calls it with a valid Skill id, append
|
|
@@ -1408,6 +1663,10 @@ function buildToolCallsHandler(deps) {
|
|
|
1408
1663
|
}
|
|
1409
1664
|
}
|
|
1410
1665
|
scope.history = newHistory;
|
|
1666
|
+
// The batch-end aggregate for conflicting transition proposals
|
|
1667
|
+
// (9.19.0) — fires only when ≥2 accepted proposals named different
|
|
1668
|
+
// targets; the per-effect events above already told each story.
|
|
1669
|
+
emitTransitionConflict(scope, iteration, transitionState);
|
|
1411
1670
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.agent.iteration_end', {
|
|
1412
1671
|
turnIndex: 0,
|
|
1413
1672
|
iterIndex: iteration,
|
|
@@ -1464,6 +1723,8 @@ function buildToolCallsHandler(deps) {
|
|
|
1464
1723
|
/** The call REALLY ran, clean — the step-advance eligibility this
|
|
1465
1724
|
* path knows about itself (a decline or a chain-deny never ran). */
|
|
1466
1725
|
let stepToolRan = false;
|
|
1726
|
+
/** The effects envelope the resumed call returned (9.19.0). */
|
|
1727
|
+
let resumeEnvelope;
|
|
1467
1728
|
if (!decision.approved) {
|
|
1468
1729
|
result = decision.note ? `declined by human: ${decision.note}` : 'declined by human';
|
|
1469
1730
|
(0, ledger_js_1.recordDecisions)(scope, [
|
|
@@ -1571,6 +1832,8 @@ function buildToolCallsHandler(deps) {
|
|
|
1571
1832
|
result = dispatched.result;
|
|
1572
1833
|
error = dispatched.error;
|
|
1573
1834
|
stepToolRan = dispatched.executed === true && error !== true;
|
|
1835
|
+
if (stepToolRan)
|
|
1836
|
+
resumeEnvelope = dispatched.envelope;
|
|
1574
1837
|
// skip_step behind a middleware ask, approved (9.18.0): the
|
|
1575
1838
|
// placeholder just landed — replace it with the authoritative
|
|
1576
1839
|
// sentence BEFORE the chain's last word, the execute loop's
|
|
@@ -1610,18 +1873,35 @@ function buildToolCallsHandler(deps) {
|
|
|
1610
1873
|
if (deps.stepPlanFor && stepToolRan) {
|
|
1611
1874
|
askResultStr += applyStepReturn(scope, { toolName, toolCallId, iteration });
|
|
1612
1875
|
}
|
|
1876
|
+
// Typed tool effects (9.19.0) — a resumed call's envelope is judged
|
|
1877
|
+
// exactly as an inline one's (fresh single-call batch state).
|
|
1878
|
+
if (resumeEnvelope !== undefined) {
|
|
1879
|
+
const askTransition = { losers: [] };
|
|
1880
|
+
askResultStr += applyToolEffects(scope, { toolName, toolCallId, iteration }, resumeEnvelope, askTransition);
|
|
1881
|
+
emitTransitionConflict(scope, iteration, askTransition);
|
|
1882
|
+
}
|
|
1613
1883
|
const askHistory = [
|
|
1614
1884
|
...scope.history,
|
|
1615
1885
|
{ role: 'tool', content: askResultStr, toolCallId, toolName },
|
|
1616
1886
|
];
|
|
1617
1887
|
scope.history = askHistory;
|
|
1618
|
-
scope.lastToolResult = {
|
|
1619
|
-
|
|
1888
|
+
scope.lastToolResult = {
|
|
1889
|
+
toolName,
|
|
1890
|
+
result: askResultStr,
|
|
1891
|
+
...(resumeEnvelope?.status !== undefined && { status: resumeEnvelope.status }),
|
|
1892
|
+
};
|
|
1893
|
+
appendBatchResult(scope, {
|
|
1894
|
+
toolName,
|
|
1895
|
+
result: askResultStr,
|
|
1896
|
+
toolCallId,
|
|
1897
|
+
...(resumeEnvelope?.status !== undefined && { status: resumeEnvelope.status }),
|
|
1898
|
+
});
|
|
1620
1899
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.stream.tool_end', {
|
|
1621
1900
|
toolCallId,
|
|
1622
1901
|
result: askCapped.result,
|
|
1623
1902
|
durationMs: Date.now() - startMs,
|
|
1624
1903
|
...(error === true && { error: true }),
|
|
1904
|
+
...(resumeEnvelope?.status !== undefined && { status: resumeEnvelope.status }),
|
|
1625
1905
|
});
|
|
1626
1906
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.agent.iteration_end', {
|
|
1627
1907
|
turnIndex: 0,
|
|
@@ -1669,6 +1949,8 @@ function buildToolCallsHandler(deps) {
|
|
|
1669
1949
|
let error;
|
|
1670
1950
|
/** Step-advance eligibility, judged by this path (a decline ran nothing). */
|
|
1671
1951
|
let stepToolRan = false;
|
|
1952
|
+
/** The effects envelope the approved call returned (9.19.0). */
|
|
1953
|
+
let resumeEnvelope;
|
|
1672
1954
|
if (decision.approved) {
|
|
1673
1955
|
const env = scope.$getEnv();
|
|
1674
1956
|
const tool = lookupTool(toolName);
|
|
@@ -1676,6 +1958,8 @@ function buildToolCallsHandler(deps) {
|
|
|
1676
1958
|
result = dispatched.result;
|
|
1677
1959
|
error = dispatched.error;
|
|
1678
1960
|
stepToolRan = dispatched.executed === true && error !== true;
|
|
1961
|
+
if (stepToolRan)
|
|
1962
|
+
resumeEnvelope = dispatched.envelope;
|
|
1679
1963
|
// Consent moved the execution to this side of the pause; the rules
|
|
1680
1964
|
// about results move with it.
|
|
1681
1965
|
if (dispatched.executed === true) {
|
|
@@ -1706,19 +1990,36 @@ function buildToolCallsHandler(deps) {
|
|
|
1706
1990
|
if (deps.stepPlanFor && stepToolRan) {
|
|
1707
1991
|
decisionResultStr += applyStepReturn(scope, { toolName, toolCallId, iteration });
|
|
1708
1992
|
}
|
|
1993
|
+
// Typed tool effects (9.19.0) — an approved consequential call may
|
|
1994
|
+
// carry them too; judged exactly as inline.
|
|
1995
|
+
if (resumeEnvelope !== undefined) {
|
|
1996
|
+
const decisionTransition = { losers: [] };
|
|
1997
|
+
decisionResultStr += applyToolEffects(scope, { toolName, toolCallId, iteration }, resumeEnvelope, decisionTransition);
|
|
1998
|
+
emitTransitionConflict(scope, iteration, decisionTransition);
|
|
1999
|
+
}
|
|
1709
2000
|
const decisionHistory = [
|
|
1710
2001
|
...scope.history,
|
|
1711
2002
|
{ role: 'tool', content: decisionResultStr, toolCallId, toolName },
|
|
1712
2003
|
];
|
|
1713
2004
|
scope.history = decisionHistory;
|
|
1714
2005
|
// Drives `on-tool-return` triggers, same as the execute path.
|
|
1715
|
-
scope.lastToolResult = {
|
|
1716
|
-
|
|
2006
|
+
scope.lastToolResult = {
|
|
2007
|
+
toolName,
|
|
2008
|
+
result: decisionResultStr,
|
|
2009
|
+
...(resumeEnvelope?.status !== undefined && { status: resumeEnvelope.status }),
|
|
2010
|
+
};
|
|
2011
|
+
appendBatchResult(scope, {
|
|
2012
|
+
toolName,
|
|
2013
|
+
result: decisionResultStr,
|
|
2014
|
+
toolCallId,
|
|
2015
|
+
...(resumeEnvelope?.status !== undefined && { status: resumeEnvelope.status }),
|
|
2016
|
+
});
|
|
1717
2017
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.stream.tool_end', {
|
|
1718
2018
|
toolCallId,
|
|
1719
2019
|
result: decisionCapped.result,
|
|
1720
2020
|
durationMs: Date.now() - startMs,
|
|
1721
2021
|
...(error === true && { error: true }),
|
|
2022
|
+
...(resumeEnvelope?.status !== undefined && { status: resumeEnvelope.status }),
|
|
1722
2023
|
});
|
|
1723
2024
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.agent.iteration_end', {
|
|
1724
2025
|
turnIndex: 0,
|
|
@@ -1781,18 +2082,35 @@ function buildToolCallsHandler(deps) {
|
|
|
1781
2082
|
if (deps.stepPlanFor && dispatched.executed === true && error !== true) {
|
|
1782
2083
|
consentResultStr += applyStepReturn(scope, { toolName, toolCallId, iteration });
|
|
1783
2084
|
}
|
|
2085
|
+
// Typed tool effects (9.19.0) — same judge as every other path.
|
|
2086
|
+
const consentEnvelope = dispatched.executed === true && error !== true ? dispatched.envelope : undefined;
|
|
2087
|
+
if (consentEnvelope !== undefined) {
|
|
2088
|
+
const consentTransition = { losers: [] };
|
|
2089
|
+
consentResultStr += applyToolEffects(scope, { toolName, toolCallId, iteration }, consentEnvelope, consentTransition);
|
|
2090
|
+
emitTransitionConflict(scope, iteration, consentTransition);
|
|
2091
|
+
}
|
|
1784
2092
|
const consentHistory = [
|
|
1785
2093
|
...scope.history,
|
|
1786
2094
|
{ role: 'tool', content: consentResultStr, toolCallId, toolName },
|
|
1787
2095
|
];
|
|
1788
2096
|
scope.history = consentHistory;
|
|
1789
|
-
scope.lastToolResult = {
|
|
1790
|
-
|
|
2097
|
+
scope.lastToolResult = {
|
|
2098
|
+
toolName,
|
|
2099
|
+
result: consentResultStr,
|
|
2100
|
+
...(consentEnvelope?.status !== undefined && { status: consentEnvelope.status }),
|
|
2101
|
+
};
|
|
2102
|
+
appendBatchResult(scope, {
|
|
2103
|
+
toolName,
|
|
2104
|
+
result: consentResultStr,
|
|
2105
|
+
toolCallId,
|
|
2106
|
+
...(consentEnvelope?.status !== undefined && { status: consentEnvelope.status }),
|
|
2107
|
+
});
|
|
1791
2108
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.stream.tool_end', {
|
|
1792
2109
|
toolCallId,
|
|
1793
2110
|
result: consentCapped.result,
|
|
1794
2111
|
durationMs: Date.now() - startMs,
|
|
1795
2112
|
...(error === true && { error: true }),
|
|
2113
|
+
...(consentEnvelope?.status !== undefined && { status: consentEnvelope.status }),
|
|
1796
2114
|
});
|
|
1797
2115
|
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.agent.iteration_end', {
|
|
1798
2116
|
turnIndex: 0,
|