agentfootprint 7.1.0 → 7.3.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/CLAUDE.md +2 -2
- package/dist/adapters/llm/AnthropicProvider.js +5 -1
- package/dist/adapters/llm/AnthropicProvider.js.map +1 -1
- package/dist/debug.js +5 -2
- package/dist/debug.js.map +1 -1
- package/dist/embedders/index.js +139 -0
- package/dist/embedders/index.js.map +1 -0
- package/dist/esm/adapters/llm/AnthropicProvider.d.ts +12 -0
- package/dist/esm/adapters/llm/AnthropicProvider.js +5 -1
- package/dist/esm/adapters/llm/AnthropicProvider.js.map +1 -1
- package/dist/esm/debug.d.ts +1 -1
- package/dist/esm/debug.js +1 -1
- package/dist/esm/debug.js.map +1 -1
- package/dist/esm/embedders/index.d.ts +49 -0
- package/dist/esm/embedders/index.js +110 -0
- package/dist/esm/embedders/index.js.map +1 -0
- package/dist/esm/lib/context-ledger/contextLedger.d.ts +35 -0
- package/dist/esm/lib/context-ledger/contextLedger.js +309 -0
- package/dist/esm/lib/context-ledger/contextLedger.js.map +1 -0
- package/dist/esm/lib/context-ledger/gates.d.ts +63 -0
- package/dist/esm/lib/context-ledger/gates.js +105 -0
- package/dist/esm/lib/context-ledger/gates.js.map +1 -0
- package/dist/esm/lib/context-ledger/index.d.ts +10 -0
- package/dist/esm/lib/context-ledger/index.js +10 -0
- package/dist/esm/lib/context-ledger/index.js.map +1 -0
- package/dist/esm/lib/context-ledger/types.d.ts +107 -0
- package/dist/esm/lib/context-ledger/types.js +28 -0
- package/dist/esm/lib/context-ledger/types.js.map +1 -0
- package/dist/esm/lib/influence-core/attribute.d.ts +27 -0
- package/dist/esm/lib/influence-core/attribute.js +91 -0
- package/dist/esm/lib/influence-core/attribute.js.map +1 -0
- package/dist/esm/lib/influence-core/explain.d.ts +67 -0
- package/dist/esm/lib/influence-core/explain.js +80 -0
- package/dist/esm/lib/influence-core/explain.js.map +1 -0
- package/dist/esm/lib/influence-core/index.d.ts +4 -1
- package/dist/esm/lib/influence-core/index.js +3 -0
- package/dist/esm/lib/influence-core/index.js.map +1 -1
- package/dist/esm/lib/influence-core/snippets.d.ts +45 -0
- package/dist/esm/lib/influence-core/snippets.js +114 -0
- package/dist/esm/lib/influence-core/snippets.js.map +1 -0
- package/dist/esm/lib/influence-core/types.d.ts +50 -0
- package/dist/esm/observe.d.ts +2 -0
- package/dist/esm/observe.js +4 -0
- package/dist/esm/observe.js.map +1 -1
- package/dist/esm/recorders/observability/ToolChoiceRecorder.d.ts +8 -2
- package/dist/esm/recorders/observability/ToolChoiceRecorder.js +24 -6
- package/dist/esm/recorders/observability/ToolChoiceRecorder.js.map +1 -1
- package/dist/lib/context-ledger/contextLedger.js +313 -0
- package/dist/lib/context-ledger/contextLedger.js.map +1 -0
- package/dist/lib/context-ledger/gates.js +111 -0
- package/dist/lib/context-ledger/gates.js.map +1 -0
- package/dist/lib/context-ledger/index.js +17 -0
- package/dist/lib/context-ledger/index.js.map +1 -0
- package/dist/lib/context-ledger/types.js +29 -0
- package/dist/lib/context-ledger/types.js.map +1 -0
- package/dist/lib/influence-core/attribute.js +95 -0
- package/dist/lib/influence-core/attribute.js.map +1 -0
- package/dist/lib/influence-core/explain.js +84 -0
- package/dist/lib/influence-core/explain.js.map +1 -0
- package/dist/lib/influence-core/index.js +7 -1
- package/dist/lib/influence-core/index.js.map +1 -1
- package/dist/lib/influence-core/snippets.js +118 -0
- package/dist/lib/influence-core/snippets.js.map +1 -0
- package/dist/observe.js +9 -1
- package/dist/observe.js.map +1 -1
- package/dist/recorders/observability/ToolChoiceRecorder.js +24 -6
- package/dist/recorders/observability/ToolChoiceRecorder.js.map +1 -1
- package/dist/types/adapters/llm/AnthropicProvider.d.ts +12 -0
- package/dist/types/adapters/llm/AnthropicProvider.d.ts.map +1 -1
- package/dist/types/debug.d.ts +1 -1
- package/dist/types/debug.d.ts.map +1 -1
- package/dist/types/embedders/index.d.ts +50 -0
- package/dist/types/embedders/index.d.ts.map +1 -0
- package/dist/types/lib/context-ledger/contextLedger.d.ts +36 -0
- package/dist/types/lib/context-ledger/contextLedger.d.ts.map +1 -0
- package/dist/types/lib/context-ledger/gates.d.ts +64 -0
- package/dist/types/lib/context-ledger/gates.d.ts.map +1 -0
- package/dist/types/lib/context-ledger/index.d.ts +11 -0
- package/dist/types/lib/context-ledger/index.d.ts.map +1 -0
- package/dist/types/lib/context-ledger/types.d.ts +108 -0
- package/dist/types/lib/context-ledger/types.d.ts.map +1 -0
- package/dist/types/lib/influence-core/attribute.d.ts +28 -0
- package/dist/types/lib/influence-core/attribute.d.ts.map +1 -0
- package/dist/types/lib/influence-core/explain.d.ts +68 -0
- package/dist/types/lib/influence-core/explain.d.ts.map +1 -0
- package/dist/types/lib/influence-core/index.d.ts +4 -1
- package/dist/types/lib/influence-core/index.d.ts.map +1 -1
- package/dist/types/lib/influence-core/snippets.d.ts +46 -0
- package/dist/types/lib/influence-core/snippets.d.ts.map +1 -0
- package/dist/types/lib/influence-core/types.d.ts +50 -0
- package/dist/types/lib/influence-core/types.d.ts.map +1 -1
- package/dist/types/observe.d.ts +2 -0
- package/dist/types/observe.d.ts.map +1 -1
- package/dist/types/recorders/observability/ToolChoiceRecorder.d.ts +8 -2
- package/dist/types/recorders/observability/ToolChoiceRecorder.d.ts.map +1 -1
- package/package.json +26 -4
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context-ledger/types.ts — the bookkeeper's vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS LIBRARY EXISTS: context engineering fails in one predictable
|
|
5
|
+
* direction — "include everything to be safe". Every injection, skill and
|
|
6
|
+
* tool schema costs tokens EVERY turn, whether or not it ever mattered. The
|
|
7
|
+
* ledger keeps score across runs: which pieces EARNED their tokens (were
|
|
8
|
+
* called, activated, or sat on the answer's dependency slice) and which
|
|
9
|
+
* never did. Its rows feed the L2 gates (gatedTools predicate, skill-graph
|
|
10
|
+
* EntryScorer, injection demotion) so future turns include less — the
|
|
11
|
+
* mechanism that makes lesser models viable.
|
|
12
|
+
*
|
|
13
|
+
* HONESTY (the discipline everything here inherits):
|
|
14
|
+
* - Every counter is a STRUCTURAL fact from the run's own commit log —
|
|
15
|
+
* offers are recorded commits, uses are recorded calls/activations/slice
|
|
16
|
+
* membership. Nothing is inferred from model internals.
|
|
17
|
+
* - Each kind's `used` definition is explicit ({@link UsedSignal}) and rides
|
|
18
|
+
* every count — a consumer can always see WHY a piece counted as used.
|
|
19
|
+
* - Slice membership is slot-granular (all injections sharing a slot share
|
|
20
|
+
* its write) — the signal name says so: `'answer-slice(slot)'`.
|
|
21
|
+
* - `approxTokens*` is a serialized-length estimate (JSON chars ÷ 4), named
|
|
22
|
+
* so nobody mistakes it for a tokenizer count.
|
|
23
|
+
* - The ledger never claims causation. `earnRate` is bookkeeping;
|
|
24
|
+
* ablation (context-bisect) can upgrade individual claims when you pay
|
|
25
|
+
* for it.
|
|
26
|
+
*/
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/lib/context-ledger/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ChoiceAttribution, AttributionUnit, Embedder } from './types.js';
|
|
2
|
+
export interface AttributeChoiceArgs {
|
|
3
|
+
/**
|
|
4
|
+
* The chosen tool being explained. `text` is what gets embedded — pass
|
|
5
|
+
* name + description (the name matters: rules that NAME a tool, e.g.
|
|
6
|
+
* "call report_gap", are its strongest citation).
|
|
7
|
+
*/
|
|
8
|
+
readonly tool: {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly text: string;
|
|
11
|
+
};
|
|
12
|
+
/** The context units to attribute to — the system-prompt rules, the task, … */
|
|
13
|
+
readonly units: readonly AttributionUnit[];
|
|
14
|
+
/** Injected embedder. Wrap in an `EmbeddingCache` so rule texts embed once. */
|
|
15
|
+
readonly embedder: Embedder;
|
|
16
|
+
/** Abort signal threaded to the embedder (network backends). */
|
|
17
|
+
readonly signal?: AbortSignal;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Embed the tool text and every unit once (deduplicated batch), rank the
|
|
21
|
+
* units by cosine to the tool, and report the top unit plus the per-channel
|
|
22
|
+
* share of positive similarity mass.
|
|
23
|
+
*
|
|
24
|
+
* Fail-loud validation: empty units or duplicate unit ids throw — those are
|
|
25
|
+
* caller wiring bugs, not runtime conditions.
|
|
26
|
+
*/
|
|
27
|
+
export declare function attributeChoice(args: AttributeChoiceArgs): Promise<ChoiceAttribution>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* attributeChoice — which context UNIT best explains one tool choice
|
|
3
|
+
* (the transpose of `scoreMargin`).
|
|
4
|
+
*
|
|
5
|
+
* `scoreMargin` fixes the context and ranks candidate tools against it —
|
|
6
|
+
* so a CONSTANT part of the context (the system prompt, identical on every
|
|
7
|
+
* call) is excluded, because it adds no power to tell one call apart from
|
|
8
|
+
* another. But that exclusion also makes procedural picks unexplainable:
|
|
9
|
+
* the agent calls `whats_here` because a RULE in the system prompt says
|
|
10
|
+
* "orient first", and margin scoring can never see that rule.
|
|
11
|
+
*
|
|
12
|
+
* `attributeChoice` fixes the CHOSEN TOOL and ranks the context units
|
|
13
|
+
* against it. Here a constant-but-load-bearing rule is exactly what we
|
|
14
|
+
* want surfaced: cosine(tool, "call whats_here first") is high and
|
|
15
|
+
* specific, so the answer becomes a citation — "picked because rule-1".
|
|
16
|
+
* The units are tagged by channel (`'system' | 'task' | 'result' | …`) so
|
|
17
|
+
* the result also reports HOW procedural vs topical the pick was.
|
|
18
|
+
*
|
|
19
|
+
* Pattern: pure async function, embedder-injected. No agent/runtime
|
|
20
|
+
* imports — `src/lib/influence-core/` leaf, same as `scoreMargin`.
|
|
21
|
+
*
|
|
22
|
+
* Honest claim (RFC-002 §2): `score` is embedding geometry between the
|
|
23
|
+
* tool text and each instruction unit — a PROXY for which instruction the
|
|
24
|
+
* pick ALIGNS with, never proof the model used that rule. This is Tier 1
|
|
25
|
+
* (similarity); Tier 3 (counterfactual ablation of each unit) is the
|
|
26
|
+
* ground truth that would validate it.
|
|
27
|
+
*/
|
|
28
|
+
import { cosineSimilarity } from '../../memory/embedding/cosine.js';
|
|
29
|
+
/**
|
|
30
|
+
* Embed the tool text and every unit once (deduplicated batch), rank the
|
|
31
|
+
* units by cosine to the tool, and report the top unit plus the per-channel
|
|
32
|
+
* share of positive similarity mass.
|
|
33
|
+
*
|
|
34
|
+
* Fail-loud validation: empty units or duplicate unit ids throw — those are
|
|
35
|
+
* caller wiring bugs, not runtime conditions.
|
|
36
|
+
*/
|
|
37
|
+
export async function attributeChoice(args) {
|
|
38
|
+
const { tool, units, embedder } = args;
|
|
39
|
+
validate(units);
|
|
40
|
+
// One deduplicated embedding pass: the tool text + each distinct unit text.
|
|
41
|
+
const distinct = [...new Set([tool.text, ...units.map((u) => u.text)])];
|
|
42
|
+
const vectors = embedder.embedBatch
|
|
43
|
+
? await embedder.embedBatch({ texts: distinct, ...(args.signal ? { signal: args.signal } : {}) })
|
|
44
|
+
: await sequentialEmbed(embedder, distinct, args.signal);
|
|
45
|
+
const vectorByText = new Map();
|
|
46
|
+
for (let i = 0; i < distinct.length; i++)
|
|
47
|
+
vectorByText.set(distinct[i], vectors[i]);
|
|
48
|
+
const toolVec = vectorByText.get(tool.text);
|
|
49
|
+
const scored = units.map((u) => ({
|
|
50
|
+
id: u.id,
|
|
51
|
+
channel: u.channel,
|
|
52
|
+
score: cosineSimilarity(toolVec, vectorByText.get(u.text)),
|
|
53
|
+
}));
|
|
54
|
+
// Stable sort — ties keep input order.
|
|
55
|
+
scored.sort((a, b) => b.score - a.score);
|
|
56
|
+
// Per-channel share of POSITIVE mass (negatives are anti-alignment, not
|
|
57
|
+
// evidence FOR a channel — clamp to 0 so they neither add nor subtract).
|
|
58
|
+
const positiveByChannel = {};
|
|
59
|
+
let total = 0;
|
|
60
|
+
for (const s of scored) {
|
|
61
|
+
const w = Math.max(0, s.score);
|
|
62
|
+
if (w === 0)
|
|
63
|
+
continue;
|
|
64
|
+
positiveByChannel[s.channel] = (positiveByChannel[s.channel] ?? 0) + w;
|
|
65
|
+
total += w;
|
|
66
|
+
}
|
|
67
|
+
const byChannel = {};
|
|
68
|
+
if (total > 0)
|
|
69
|
+
for (const [ch, w] of Object.entries(positiveByChannel))
|
|
70
|
+
byChannel[ch] = w / total;
|
|
71
|
+
return { tool: tool.name, units: scored, top: scored[0], byChannel };
|
|
72
|
+
}
|
|
73
|
+
async function sequentialEmbed(embedder, texts, signal) {
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const text of texts)
|
|
76
|
+
out.push(await embedder.embed({ text, ...(signal ? { signal } : {}) }));
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
function validate(units) {
|
|
80
|
+
if (units.length === 0) {
|
|
81
|
+
throw new Error('attributeChoice: units must be non-empty — nothing to attribute to');
|
|
82
|
+
}
|
|
83
|
+
const ids = new Set();
|
|
84
|
+
for (const u of units) {
|
|
85
|
+
if (ids.has(u.id)) {
|
|
86
|
+
throw new Error(`attributeChoice: duplicate unit id '${u.id}' — ids must be unique`);
|
|
87
|
+
}
|
|
88
|
+
ids.add(u.id);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=attribute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribute.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/attribute.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { AttributionUnit, Embedder, UnitScore } from './types.js';
|
|
2
|
+
export interface ExplainChoiceArgs {
|
|
3
|
+
/**
|
|
4
|
+
* The chosen tool being explained. `text` is what gets embedded — pass
|
|
5
|
+
* name + description (the name matters: a data snippet that NAMES the
|
|
6
|
+
* thing the tool acts on, e.g. "id: d42", is its strongest citation).
|
|
7
|
+
*/
|
|
8
|
+
readonly tool: {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly text: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The context units, tagged by channel — e.g. `'system'` rules,
|
|
14
|
+
* the `'task'`, `'data'` snippets from `snippetUnits`. Channels are
|
|
15
|
+
* free-form strings; the verdict reports whatever labels it is given.
|
|
16
|
+
*/
|
|
17
|
+
readonly units: readonly AttributionUnit[];
|
|
18
|
+
/** Injected embedder. Wrap in an `EmbeddingCache` so rule texts embed once. */
|
|
19
|
+
readonly embedder: Embedder;
|
|
20
|
+
/** Abort signal threaded to the embedder (network backends). */
|
|
21
|
+
readonly signal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
/** One channel's verdict: how much of the pick's positive similarity
|
|
24
|
+
* mass it owns, and the best unit inside it to quote. */
|
|
25
|
+
export interface ChannelVerdict {
|
|
26
|
+
readonly channel: string;
|
|
27
|
+
/**
|
|
28
|
+
* 0..1 share of POSITIVE similarity mass (negatives are clamped out,
|
|
29
|
+
* same as `attributeChoice.byChannel`). Shares across all channels sum
|
|
30
|
+
* to ~1 — or are all 0 when no unit had positive similarity.
|
|
31
|
+
*/
|
|
32
|
+
readonly share: number;
|
|
33
|
+
/** The best-scoring unit IN this channel, text included for quoting.
|
|
34
|
+
* Omitted only when the channel has no units. */
|
|
35
|
+
readonly top?: UnitScore & {
|
|
36
|
+
readonly text: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** The full explanation for one tool pick — `attributeChoice` reshaped
|
|
40
|
+
* for display: text carried through, channels ranked. */
|
|
41
|
+
export interface ChoiceExplanation {
|
|
42
|
+
/** The chosen tool being explained. */
|
|
43
|
+
readonly tool: string;
|
|
44
|
+
/**
|
|
45
|
+
* One verdict per channel present in the units, sorted by share
|
|
46
|
+
* descending (ties keep first-appearance order). Zero-share channels
|
|
47
|
+
* are listed too — absence of pull is part of the verdict.
|
|
48
|
+
*/
|
|
49
|
+
readonly channels: readonly ChannelVerdict[];
|
|
50
|
+
/** The overall best unit — the citation ("picked because …"). */
|
|
51
|
+
readonly top: UnitScore & {
|
|
52
|
+
readonly text: string;
|
|
53
|
+
};
|
|
54
|
+
/** Every unit ranked descending (ties keep input order), text included. */
|
|
55
|
+
readonly units: readonly (UnitScore & {
|
|
56
|
+
readonly text: string;
|
|
57
|
+
})[];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Run `attributeChoice` and reshape the result for a UI: join each unit's
|
|
61
|
+
* text back by id, pick the best unit per channel, and rank the channels
|
|
62
|
+
* by their share of positive similarity mass.
|
|
63
|
+
*
|
|
64
|
+
* Fail-loud validation is inherited from `attributeChoice`: empty units
|
|
65
|
+
* or duplicate unit ids throw — caller wiring bugs, not runtime conditions.
|
|
66
|
+
*/
|
|
67
|
+
export declare function explainChoice(args: ExplainChoiceArgs): Promise<ChoiceExplanation>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* explainChoice — a UI-ready VERDICT for one tool pick: which context
|
|
3
|
+
* CHANNEL (system rules / user task / data from earlier tool results)
|
|
4
|
+
* best explains it, with the exact unit text to quote.
|
|
5
|
+
*
|
|
6
|
+
* Thin composite over `attributeChoice` — same math, zero duplication:
|
|
7
|
+
* the ranking, the top unit, and the per-channel share of positive
|
|
8
|
+
* similarity mass all come from the one existing engine. What this adds
|
|
9
|
+
* is presentation shape: the unit TEXT carried through (so a UI can
|
|
10
|
+
* quote the citation verbatim), a per-channel top unit, and a channels
|
|
11
|
+
* array sorted by share that lists EVERY channel present in the units —
|
|
12
|
+
* including zero-share ones, so "the data channel contributed nothing"
|
|
13
|
+
* is a visible verdict, not a missing key.
|
|
14
|
+
*
|
|
15
|
+
* Pattern: pure async function, embedder-injected. No agent/runtime
|
|
16
|
+
* imports — `src/lib/influence-core/` leaf, same as
|
|
17
|
+
* `attributeChoice` / `scoreMargin`.
|
|
18
|
+
*
|
|
19
|
+
* Honest claim (RFC-002 §2): every score and share here is embedding
|
|
20
|
+
* geometry between the tool text and each unit — a PROXY for which
|
|
21
|
+
* context the pick ALIGNS with, never proof the model used that unit.
|
|
22
|
+
* This is Tier 1 (similarity); counterfactual ablation (Tier 3) is the
|
|
23
|
+
* ground truth that would validate it.
|
|
24
|
+
*/
|
|
25
|
+
import { attributeChoice } from './attribute.js';
|
|
26
|
+
/**
|
|
27
|
+
* Run `attributeChoice` and reshape the result for a UI: join each unit's
|
|
28
|
+
* text back by id, pick the best unit per channel, and rank the channels
|
|
29
|
+
* by their share of positive similarity mass.
|
|
30
|
+
*
|
|
31
|
+
* Fail-loud validation is inherited from `attributeChoice`: empty units
|
|
32
|
+
* or duplicate unit ids throw — caller wiring bugs, not runtime conditions.
|
|
33
|
+
*/
|
|
34
|
+
export async function explainChoice(args) {
|
|
35
|
+
const attribution = await attributeChoice({
|
|
36
|
+
tool: args.tool,
|
|
37
|
+
units: args.units,
|
|
38
|
+
embedder: args.embedder,
|
|
39
|
+
...(args.signal ? { signal: args.signal } : {}),
|
|
40
|
+
});
|
|
41
|
+
// Join the unit text back by id (ids are unique — attributeChoice validated).
|
|
42
|
+
const textById = new Map();
|
|
43
|
+
for (const u of args.units)
|
|
44
|
+
textById.set(u.id, u.text);
|
|
45
|
+
const withText = (score) => ({
|
|
46
|
+
...score,
|
|
47
|
+
text: textById.get(score.id),
|
|
48
|
+
});
|
|
49
|
+
const ranked = attribution.units.map(withText);
|
|
50
|
+
// One verdict per channel, in first-appearance order (stable tiebreak),
|
|
51
|
+
// then sorted by share descending. `ranked` is already descending, so
|
|
52
|
+
// the first unit seen per channel is that channel's top.
|
|
53
|
+
const channelOrder = [];
|
|
54
|
+
const topByChannel = new Map();
|
|
55
|
+
for (const u of args.units) {
|
|
56
|
+
if (!channelOrder.includes(u.channel))
|
|
57
|
+
channelOrder.push(u.channel);
|
|
58
|
+
}
|
|
59
|
+
for (const unit of ranked) {
|
|
60
|
+
if (!topByChannel.has(unit.channel))
|
|
61
|
+
topByChannel.set(unit.channel, unit);
|
|
62
|
+
}
|
|
63
|
+
const channels = channelOrder.map((channel) => {
|
|
64
|
+
const top = topByChannel.get(channel);
|
|
65
|
+
return {
|
|
66
|
+
channel,
|
|
67
|
+
share: attribution.byChannel[channel] ?? 0,
|
|
68
|
+
...(top ? { top } : {}),
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
// Stable sort — zero-share channels sink to the end, ties keep order.
|
|
72
|
+
channels.sort((a, b) => b.share - a.share);
|
|
73
|
+
return {
|
|
74
|
+
tool: attribution.tool,
|
|
75
|
+
channels,
|
|
76
|
+
top: withText(attribution.top),
|
|
77
|
+
units: ranked,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/explain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAsDjD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAuB;IACzD,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC;QACxC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAC,CAAC;IAEH,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;QAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,CAAC,KAAgB,EAAyC,EAAE,CAAC,CAAC;QAC7E,GAAG,KAAK;QACR,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAW;KACvC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE/C,wEAAwE;IACxE,sEAAsE;IACtE,yDAAyD;IACzD,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAiD,CAAC;IAC9E,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;YAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACtE,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,QAAQ,GAAqB,YAAY,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9D,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO;YACL,OAAO;YACP,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;YAC1C,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxB,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAE3C,OAAO;QACL,IAAI,EAAE,WAAW,CAAC,IAAI;QACtB,QAAQ;QACR,GAAG,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC;QAC9B,KAAK,EAAE,MAAM;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* embedding-geometry PROXY — semantic alignment, never model internals
|
|
25
25
|
* and never causal attribution.
|
|
26
26
|
*/
|
|
27
|
-
export type { CandidateScore, ConfidenceStrategy, EmbedArgs, EmbedBatchArgs, Embedder, EvidenceInput, InfluenceScore, InfluenceWeights, MarginCandidate, MarginResult, PairwiseSimilarityResult, RankingConfidence, SignalScores, SimilarityItem, SimilarityPair, } from './types.js';
|
|
27
|
+
export type { CandidateScore, AttributionUnit, ChoiceAttribution, ConfidenceStrategy, EmbedArgs, EmbedBatchArgs, Embedder, EvidenceInput, InfluenceScore, InfluenceWeights, MarginCandidate, MarginResult, PairwiseSimilarityResult, RankingConfidence, SignalScores, SimilarityItem, SimilarityPair, UnitScore, } from './types.js';
|
|
28
28
|
export { DEFAULT_CLEAR_WINNER_MARGIN, DEFAULT_CLEAR_WINNER_RATIO, DEFAULT_INFLUENCE_WEIGHTS, DEFAULT_MARGIN_THRESHOLD, DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_SHORTLIST_BAND, } from './types.js';
|
|
29
29
|
export { marginStrategy, rankingConfidence, ratioStrategy, type RankingConfidenceOptions, } from './attributability.js';
|
|
30
30
|
export { scoreContrastiveInfluence, type ScoreContrastiveInfluenceArgs } from './contrastive.js';
|
|
@@ -32,3 +32,6 @@ export { contentHash, EmbeddingCache, embeddingCache, type EmbeddingCacheOptions
|
|
|
32
32
|
export { adaptWeights, averageRelevancy, compositeScore, finalAnswerSimilarity, persistence, scoreInfluence, structuralProximity, type InfluenceScorer, type ScoreInfluenceArgs, } from './signals.js';
|
|
33
33
|
export { pairwiseSimilarity, type PairwiseSimilarityArgs } from './similarity.js';
|
|
34
34
|
export { scoreMargin, type ScoreMarginArgs } from './margin.js';
|
|
35
|
+
export { attributeChoice, type AttributeChoiceArgs } from './attribute.js';
|
|
36
|
+
export { explainChoice, type ChannelVerdict, type ChoiceExplanation, type ExplainChoiceArgs, } from './explain.js';
|
|
37
|
+
export { snippetUnits, type SnippetUnitsOptions } from './snippets.js';
|
|
@@ -31,4 +31,7 @@ export { contentHash, EmbeddingCache, embeddingCache, } from './cache.js';
|
|
|
31
31
|
export { adaptWeights, averageRelevancy, compositeScore, finalAnswerSimilarity, persistence, scoreInfluence, structuralProximity, } from './signals.js';
|
|
32
32
|
export { pairwiseSimilarity } from './similarity.js';
|
|
33
33
|
export { scoreMargin } from './margin.js';
|
|
34
|
+
export { attributeChoice } from './attribute.js';
|
|
35
|
+
export { explainChoice, } from './explain.js';
|
|
36
|
+
export { snippetUnits } from './snippets.js';
|
|
34
37
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAsBH,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,EACxB,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,aAAa,GAEd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,yBAAyB,EAAsC,MAAM,kBAAkB,CAAC;AAEjG,OAAO,EACL,WAAW,EACX,cAAc,EACd,cAAc,GAGf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,cAAc,EACd,mBAAmB,GAGpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,kBAAkB,EAA+B,MAAM,iBAAiB,CAAC;AAElF,OAAO,EAAE,WAAW,EAAwB,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,eAAe,EAA4B,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EACL,aAAa,GAId,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,YAAY,EAA4B,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snippetUnits — cut a tool result into citable AttributionUnits for the
|
|
3
|
+
* `'data'` channel.
|
|
4
|
+
*
|
|
5
|
+
* `explainChoice` can only credit the data channel with units to point
|
|
6
|
+
* at — a raw tool result (arbitrary JSON or prose) is not citable as one
|
|
7
|
+
* blob. This helper cuts it at the natural citation grain so a pick like
|
|
8
|
+
* "open dress d42" can be attributed to the exact search-result snippet
|
|
9
|
+
* that returned d42:
|
|
10
|
+
*
|
|
11
|
+
* • a string → one unit per sentence/line (non-empty, trimmed),
|
|
12
|
+
* • an array element that is an object → ONE unit summarizing it — its
|
|
13
|
+
* primitive fields joined as `key: value` pairs (a search hit, a row),
|
|
14
|
+
* • any other object → its primitive fields grouped into one unit,
|
|
15
|
+
* then the walk continues into nested objects/arrays,
|
|
16
|
+
* • null/undefined/booleans/empty strings/functions/symbols → skipped.
|
|
17
|
+
*
|
|
18
|
+
* Bounded and total: at most `max` units, each truncated to `maxLength`
|
|
19
|
+
* characters; circular references are guarded (WeakSet) — weird input
|
|
20
|
+
* yields fewer units, never a throw.
|
|
21
|
+
*
|
|
22
|
+
* Pattern: pure synchronous function, zero imports beyond the shared
|
|
23
|
+
* types — `src/lib/influence-core/` leaf. No embedding happens
|
|
24
|
+
* here; the units feed `attributeChoice` / `explainChoice`.
|
|
25
|
+
*
|
|
26
|
+
* Honest claim: this is a CUTTER, not a ranker — it decides what is
|
|
27
|
+
* quotable, not what mattered. Whether a snippet explains the pick is
|
|
28
|
+
* the (proxy) verdict of the similarity scoring downstream.
|
|
29
|
+
*/
|
|
30
|
+
import type { AttributionUnit } from './types.js';
|
|
31
|
+
export interface SnippetUnitsOptions {
|
|
32
|
+
/** Channel tag stamped on every unit. Default `'data'`. */
|
|
33
|
+
readonly channel?: string;
|
|
34
|
+
/** Unit id prefix — ids become `'<prefix>-1'`, `'<prefix>-2'`, … Default `'data'`. */
|
|
35
|
+
readonly idPrefix?: string;
|
|
36
|
+
/** Cap on units returned. Default 12. */
|
|
37
|
+
readonly max?: number;
|
|
38
|
+
/** Cap on characters per unit text (ellipsis included). Default 200. */
|
|
39
|
+
readonly maxLength?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Cut `value` (a tool result — arbitrary JSON value or prose string)
|
|
43
|
+
* into at most `max` citable units for the data channel.
|
|
44
|
+
*/
|
|
45
|
+
export declare function snippetUnits(value: unknown, options?: SnippetUnitsOptions): AttributionUnit[];
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cut `value` (a tool result — arbitrary JSON value or prose string)
|
|
3
|
+
* into at most `max` citable units for the data channel.
|
|
4
|
+
*/
|
|
5
|
+
export function snippetUnits(value, options = {}) {
|
|
6
|
+
const channel = options.channel ?? 'data';
|
|
7
|
+
const idPrefix = options.idPrefix ?? 'data';
|
|
8
|
+
const max = options.max ?? 12;
|
|
9
|
+
const maxLength = options.maxLength ?? 200;
|
|
10
|
+
const texts = [];
|
|
11
|
+
collect(value, texts, max, new WeakSet());
|
|
12
|
+
return texts.map((text, i) => ({
|
|
13
|
+
id: `${idPrefix}-${i + 1}`,
|
|
14
|
+
channel,
|
|
15
|
+
text: truncate(text, maxLength),
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
/** Walk `value` depth-first, pushing snippet texts until `max` is reached. */
|
|
19
|
+
function collect(value, out, max, seen) {
|
|
20
|
+
if (out.length >= max)
|
|
21
|
+
return;
|
|
22
|
+
if (typeof value === 'string') {
|
|
23
|
+
for (const sentence of splitSentences(value)) {
|
|
24
|
+
if (out.length >= max)
|
|
25
|
+
return;
|
|
26
|
+
out.push(sentence);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (isCitablePrimitive(value)) {
|
|
31
|
+
out.push(String(value));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (typeof value !== 'object' || value === null)
|
|
35
|
+
return; // boolean/null/undefined/function/symbol
|
|
36
|
+
if (seen.has(value))
|
|
37
|
+
return; // circular reference — already visited
|
|
38
|
+
seen.add(value);
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
for (const element of value) {
|
|
41
|
+
if (out.length >= max)
|
|
42
|
+
return;
|
|
43
|
+
if (isPlainRecord(element)) {
|
|
44
|
+
// The natural citation grain: one unit per row/hit, then walk its
|
|
45
|
+
// nested values (a hit may itself contain an array of hits).
|
|
46
|
+
if (seen.has(element))
|
|
47
|
+
continue;
|
|
48
|
+
seen.add(element);
|
|
49
|
+
const summary = summarizeFields(element);
|
|
50
|
+
if (summary !== '')
|
|
51
|
+
out.push(summary);
|
|
52
|
+
for (const nested of Object.values(element)) {
|
|
53
|
+
if (typeof nested === 'object' && nested !== null)
|
|
54
|
+
collect(nested, out, max, seen);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
collect(element, out, max, seen);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Non-array object: group its primitive fields into one unit, recurse
|
|
64
|
+
// into the rest.
|
|
65
|
+
const summary = summarizeFields(value);
|
|
66
|
+
if (summary !== '')
|
|
67
|
+
out.push(summary);
|
|
68
|
+
for (const nested of Object.values(value)) {
|
|
69
|
+
if (out.length >= max)
|
|
70
|
+
return;
|
|
71
|
+
if (typeof nested === 'object' && nested !== null)
|
|
72
|
+
collect(nested, out, max, seen);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Join an object's own citable primitive fields as `key: value` pairs. */
|
|
76
|
+
function summarizeFields(record) {
|
|
77
|
+
const pairs = [];
|
|
78
|
+
for (const [key, v] of Object.entries(record)) {
|
|
79
|
+
if (typeof v === 'string') {
|
|
80
|
+
const trimmed = v.trim();
|
|
81
|
+
if (trimmed !== '')
|
|
82
|
+
pairs.push(`${key}: ${trimmed}`);
|
|
83
|
+
}
|
|
84
|
+
else if (isCitablePrimitive(v)) {
|
|
85
|
+
pairs.push(`${key}: ${String(v)}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return pairs.join(', ');
|
|
89
|
+
}
|
|
90
|
+
/** Non-string primitives worth quoting: numbers and bigints. Booleans,
|
|
91
|
+
* null, and undefined carry no citable content — skipped. */
|
|
92
|
+
function isCitablePrimitive(value) {
|
|
93
|
+
return typeof value === 'number' || typeof value === 'bigint';
|
|
94
|
+
}
|
|
95
|
+
/** A record-shaped object (not an array) — the row/hit case. */
|
|
96
|
+
function isPlainRecord(value) {
|
|
97
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
98
|
+
}
|
|
99
|
+
/** Split prose into sentences/lines; non-empty, trimmed. */
|
|
100
|
+
function splitSentences(text) {
|
|
101
|
+
return text
|
|
102
|
+
.split(/(?<=[.!?])\s+|\n+/)
|
|
103
|
+
.map((s) => s.trim())
|
|
104
|
+
.filter((s) => s !== '');
|
|
105
|
+
}
|
|
106
|
+
/** Cap text at `maxLength` characters, ellipsis included. */
|
|
107
|
+
function truncate(text, maxLength) {
|
|
108
|
+
if (text.length <= maxLength)
|
|
109
|
+
return text;
|
|
110
|
+
if (maxLength <= 1)
|
|
111
|
+
return '…';
|
|
112
|
+
return `${text.slice(0, maxLength - 1)}…`;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=snippets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippets.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/snippets.ts"],"names":[],"mappings":"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"}
|
|
@@ -250,3 +250,53 @@ export interface MarginResult {
|
|
|
250
250
|
readonly proxyDisagreement: boolean;
|
|
251
251
|
};
|
|
252
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* One piece of the context the chooser saw, tagged by which channel it
|
|
255
|
+
* came from. `scoreMargin` asks "which tool best fits ONE context";
|
|
256
|
+
* `attributeChoice` asks the transpose — "which context UNIT best explains
|
|
257
|
+
* ONE chosen tool" — so the unit is the thing being scored here.
|
|
258
|
+
*
|
|
259
|
+
* A channel groups units by origin so the attribution can report where the
|
|
260
|
+
* pull came from: `'system'` (a rule in the system prompt), `'task'` (the
|
|
261
|
+
* user's request), `'result'` (data a prior tool returned), `'history'`
|
|
262
|
+
* (an earlier turn). Channels are free-form strings — the caller decides
|
|
263
|
+
* the taxonomy; the engine only sums by whatever labels it is given.
|
|
264
|
+
*
|
|
265
|
+
* (Named `AttributionUnit`, not `ContextUnit`, to stay distinct from
|
|
266
|
+
* context-bisect's own `ContextUnit`, which is a different subsystem.)
|
|
267
|
+
*/
|
|
268
|
+
export interface AttributionUnit {
|
|
269
|
+
/** Unique id — the citation the attribution points at, e.g. `'rule-1'`. */
|
|
270
|
+
readonly id: string;
|
|
271
|
+
/** Origin group, e.g. `'system' | 'task' | 'result' | 'history'`. */
|
|
272
|
+
readonly channel: string;
|
|
273
|
+
/** The text of the unit (a rule sentence, the user task, …). */
|
|
274
|
+
readonly text: string;
|
|
275
|
+
}
|
|
276
|
+
/** One context unit's proximity to the chosen tool, ranked. */
|
|
277
|
+
export interface UnitScore {
|
|
278
|
+
readonly id: string;
|
|
279
|
+
readonly channel: string;
|
|
280
|
+
/** cosine(embed(tool text), embed(unit.text)). */
|
|
281
|
+
readonly score: number;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Which context best explains one tool choice — the transpose of a
|
|
285
|
+
* `MarginResult`. Same honesty caveat: `score` is embedding geometry
|
|
286
|
+
* between the tool text and each instruction unit, a PROXY for which
|
|
287
|
+
* instruction the pick aligns with, never proof the model used that rule.
|
|
288
|
+
*/
|
|
289
|
+
export interface ChoiceAttribution {
|
|
290
|
+
/** The chosen tool being explained. */
|
|
291
|
+
readonly tool: string;
|
|
292
|
+
/** Every unit scored against the tool, descending (ties keep input order). */
|
|
293
|
+
readonly units: readonly UnitScore[];
|
|
294
|
+
/** The single best-attributing unit — the citation ("picked because …"). */
|
|
295
|
+
readonly top: UnitScore;
|
|
296
|
+
/**
|
|
297
|
+
* Share of total POSITIVE similarity mass per channel, summing to ~1
|
|
298
|
+
* (empty when no unit had positive similarity). A pick dominated by the
|
|
299
|
+
* `system` channel is procedural; one dominated by `task` is topical.
|
|
300
|
+
*/
|
|
301
|
+
readonly byChannel: Readonly<Record<string, number>>;
|
|
302
|
+
}
|
package/dist/esm/observe.d.ts
CHANGED
|
@@ -55,3 +55,5 @@ export { typedEmit } from './recorders/core/typedEmit.js';
|
|
|
55
55
|
export * from "./debug.js";
|
|
56
56
|
export { buildChoiceContext, toolChoiceRecorder, type OfferedTool, type ToolChoiceCall, type ToolChoiceRecorderHandle, type ToolChoiceRecorderOptions, type ToolChoiceSkipReason, type ToolChoiceSummary, } from './recorders/observability/ToolChoiceRecorder.js';
|
|
57
57
|
export { routeRecorder, formatRouteHop, type RouteRecorderHandle, type RouteRecorderOptions, type RouteHop, type RouteOutcome, type RouteTrip, type RouteTripKind, } from './recorders/observability/RouteRecorder.js';
|
|
58
|
+
export { contextLedger, ledgerToolGate, ledgerEntryScorer, ledgerGated } from './lib/context-ledger/index.js';
|
|
59
|
+
export type { ContextLedger, LedgerJSON, LedgerPolicy, LedgerRow, PieceKind, RecordedRun, UsedSignal, } from './lib/context-ledger/index.js';
|
package/dist/esm/observe.js
CHANGED
|
@@ -77,4 +77,8 @@ export * from "./debug.js";
|
|
|
77
77
|
// LAZILY on first read; flags narrow margins + proxy disagreements.
|
|
78
78
|
export { buildChoiceContext, toolChoiceRecorder, } from './recorders/observability/ToolChoiceRecorder.js';
|
|
79
79
|
export { routeRecorder, formatRouteHop, } from './recorders/observability/RouteRecorder.js';
|
|
80
|
+
// context-ledger — which context pieces EARNED their tokens? Post-run
|
|
81
|
+
// bookkeeping (offers/uses/outcomes from the commit log) feeding the gating
|
|
82
|
+
// seams. See src/lib/context-ledger/README.md.
|
|
83
|
+
export { contextLedger, ledgerToolGate, ledgerEntryScorer, ledgerGated } from './lib/context-ledger/index.js';
|
|
80
84
|
//# sourceMappingURL=observe.js.map
|
package/dist/esm/observe.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observe.js","sourceRoot":"","sources":["../../src/observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,4BAA4B;AAC5B,OAAO,EAAE,eAAe,EAA+B,MAAM,qCAAqC,CAAC;AACnG,OAAO,EAAE,cAAc,EAA8B,MAAM,oCAAoC,CAAC;AAEhG,+BAA+B;AAC/B,OAAO,EACL,mBAAmB,GAEpB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,aAAa,EAA6B,MAAM,mCAAmC,CAAC;AAC7F,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GAgBjB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EACL,aAAa,EACb,eAAe,EACf,eAAe,GAQhB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,eAAe,EACf,cAAc,EACd,wBAAwB,GAQzB,MAAM,gDAAgD,CAAC;AAExD,8EAA8E;AAC9E,gFAAgF;AAChF,iEAAiE;AACjE,OAAO,EACL,cAAc,EACd,aAAa,EACb,gBAAgB,GAKjB,MAAM,oCAAoC,CAAC;AAE5C,mFAAmF;AACnF,OAAO,EACL,wBAAwB,GAGzB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,oBAAoB,GAKrB,MAAM,gDAAgD,CAAC;AAExD,6BAA6B;AAC7B,OAAO,EAAE,YAAY,EAA4B,MAAM,kCAAkC,CAAC;AAC1F,OAAO,EAAE,aAAa,EAA6B,MAAM,mCAAmC,CAAC;AAC7F,OAAO,EACL,wBAAwB,GAEzB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,YAAY,EAA4B,MAAM,kCAAkC,CAAC;AAC1F,OAAO,EAAE,cAAc,EAA8B,MAAM,oCAAoC,CAAC;AAChG,OAAO,EACL,kBAAkB,GAEnB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,aAAa,EAA6B,MAAM,mCAAmC,CAAC;AAC7F,OAAO,EACL,aAAa,EACb,cAAc,GAIf,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,YAAY,GAGb,MAAM,6CAA6C,CAAC;AACrD,4EAA4E;AAC5E,gFAAgF;AAChF,OAAO,EACL,mBAAmB,GAMpB,MAAM,kDAAkD,CAAC;AAC1D,gFAAgF;AAChF,gFAAgF;AAChF,OAAO,EACL,kBAAkB,GAQnB,MAAM,yDAAyD,CAAC;AAEjE,uDAAuD;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE1D,kEAAkE;AAClE,uEAAuE;AACvE,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,wEAAwE;AACxE,cAAc,YAAY,CAAC;AAC3B,sEAAsE;AACtE,uEAAuE;AACvE,qEAAqE;AACrE,oEAAoE;AACpE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,GAOnB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EACL,aAAa,EACb,cAAc,GAOf,MAAM,4CAA4C,CAAC"}
|
|
1
|
+
{"version":3,"file":"observe.js","sourceRoot":"","sources":["../../src/observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,4BAA4B;AAC5B,OAAO,EAAE,eAAe,EAA+B,MAAM,qCAAqC,CAAC;AACnG,OAAO,EAAE,cAAc,EAA8B,MAAM,oCAAoC,CAAC;AAEhG,+BAA+B;AAC/B,OAAO,EACL,mBAAmB,GAEpB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,aAAa,EAA6B,MAAM,mCAAmC,CAAC;AAC7F,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GAgBjB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EACL,aAAa,EACb,eAAe,EACf,eAAe,GAQhB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,eAAe,EACf,cAAc,EACd,wBAAwB,GAQzB,MAAM,gDAAgD,CAAC;AAExD,8EAA8E;AAC9E,gFAAgF;AAChF,iEAAiE;AACjE,OAAO,EACL,cAAc,EACd,aAAa,EACb,gBAAgB,GAKjB,MAAM,oCAAoC,CAAC;AAE5C,mFAAmF;AACnF,OAAO,EACL,wBAAwB,GAGzB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,oBAAoB,GAKrB,MAAM,gDAAgD,CAAC;AAExD,6BAA6B;AAC7B,OAAO,EAAE,YAAY,EAA4B,MAAM,kCAAkC,CAAC;AAC1F,OAAO,EAAE,aAAa,EAA6B,MAAM,mCAAmC,CAAC;AAC7F,OAAO,EACL,wBAAwB,GAEzB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,YAAY,EAA4B,MAAM,kCAAkC,CAAC;AAC1F,OAAO,EAAE,cAAc,EAA8B,MAAM,oCAAoC,CAAC;AAChG,OAAO,EACL,kBAAkB,GAEnB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,aAAa,EAA6B,MAAM,mCAAmC,CAAC;AAC7F,OAAO,EACL,aAAa,EACb,cAAc,GAIf,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,YAAY,GAGb,MAAM,6CAA6C,CAAC;AACrD,4EAA4E;AAC5E,gFAAgF;AAChF,OAAO,EACL,mBAAmB,GAMpB,MAAM,kDAAkD,CAAC;AAC1D,gFAAgF;AAChF,gFAAgF;AAChF,OAAO,EACL,kBAAkB,GAQnB,MAAM,yDAAyD,CAAC;AAEjE,uDAAuD;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE1D,kEAAkE;AAClE,uEAAuE;AACvE,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,wEAAwE;AACxE,cAAc,YAAY,CAAC;AAC3B,sEAAsE;AACtE,uEAAuE;AACvE,qEAAqE;AACrE,oEAAoE;AACpE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,GAOnB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EACL,aAAa,EACb,cAAc,GAOf,MAAM,4CAA4C,CAAC;AAEpD,sEAAsE;AACtE,4EAA4E;AAC5E,+CAA+C;AAC/C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -69,8 +69,13 @@
|
|
|
69
69
|
*/
|
|
70
70
|
import type { EmitEvent } from 'footprintjs';
|
|
71
71
|
import { type Embedder, type MarginResult } from '../../lib/influence-core/index.js';
|
|
72
|
-
/** Minimal structural slice of footprintjs's
|
|
73
|
-
*
|
|
72
|
+
/** Minimal structural slice of footprintjs's run-boundary events — the
|
|
73
|
+
* `FlowRunEvent` (`onRunStart`/`onRunEnd`/`onRunFailed`) and the
|
|
74
|
+
* control-flow `FlowResumeEvent` (`onResume`). runId is all we read
|
|
75
|
+
* (Convention 4). The scope-channel `ResumeEvent` this recorder also
|
|
76
|
+
* receives (it lands on the scope channel via `onEmit`) carries no
|
|
77
|
+
* `traversalContext`, so `traversalContext?.runId` is `undefined` there
|
|
78
|
+
* and the handler ignores it. */
|
|
74
79
|
interface RunBoundaryEvent {
|
|
75
80
|
readonly traversalContext?: {
|
|
76
81
|
readonly runId?: string;
|
|
@@ -149,6 +154,7 @@ export interface ToolChoiceRecorderHandle {
|
|
|
149
154
|
clear(): void;
|
|
150
155
|
onEmit(event: EmitEvent): void;
|
|
151
156
|
onRunStart(event: RunBoundaryEvent): void;
|
|
157
|
+
onResume(event: RunBoundaryEvent): void;
|
|
152
158
|
onRunEnd(event: RunBoundaryEvent): void;
|
|
153
159
|
onRunFailed(event: RunBoundaryEvent): void;
|
|
154
160
|
}
|