@graphorin/agent 0.5.0 → 0.6.1
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/CHANGELOG.md +114 -0
- package/README.md +41 -21
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/index.js.map +1 -1
- package/dist/evaluator-optimizer/index.d.ts +1 -1
- package/dist/evaluator-optimizer/index.js.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +517 -108
- package/dist/factory.js.map +1 -1
- package/dist/fallback/index.d.ts +1 -1
- package/dist/fallback/index.js.map +1 -1
- package/dist/fanout/index.d.ts +8 -8
- package/dist/fanout/index.js +3 -3
- package/dist/fanout/index.js.map +1 -1
- package/dist/filters/index.d.ts +1 -1
- package/dist/filters/index.js +2 -2
- package/dist/filters/index.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/ids.js +1 -1
- package/dist/internal/ids.js.map +1 -1
- package/dist/internal/usage-accumulator.js +17 -2
- package/dist/internal/usage-accumulator.js.map +1 -1
- package/dist/lateral-leak/causality-monitor.d.ts +4 -4
- package/dist/lateral-leak/causality-monitor.js +3 -3
- package/dist/lateral-leak/causality-monitor.js.map +1 -1
- package/dist/lateral-leak/merge-guard.d.ts +2 -2
- package/dist/lateral-leak/merge-guard.js +1 -1
- package/dist/lateral-leak/merge-guard.js.map +1 -1
- package/dist/lateral-leak/protocol-guard.d.ts +4 -4
- package/dist/lateral-leak/protocol-guard.js +4 -4
- package/dist/lateral-leak/protocol-guard.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/preferred-model/index.d.ts +2 -2
- package/dist/preferred-model/index.js +2 -2
- package/dist/preferred-model/index.js.map +1 -1
- package/dist/progress/index.js +2 -2
- package/dist/progress/index.js.map +1 -1
- package/dist/run-state/index.d.ts +7 -5
- package/dist/run-state/index.d.ts.map +1 -1
- package/dist/run-state/index.js +33 -4
- package/dist/run-state/index.js.map +1 -1
- package/dist/testing/replay-provider.d.ts +18 -0
- package/dist/testing/replay-provider.d.ts.map +1 -0
- package/dist/testing/replay-provider.js +100 -0
- package/dist/testing/replay-provider.js.map +1 -0
- package/dist/tooling/adapters.js +6 -6
- package/dist/tooling/adapters.js.map +1 -1
- package/dist/tooling/catalogue.js +1 -1
- package/dist/tooling/catalogue.js.map +1 -1
- package/dist/tooling/dataflow.js +19 -3
- package/dist/tooling/dataflow.js.map +1 -1
- package/dist/tooling/plan.js +92 -0
- package/dist/tooling/plan.js.map +1 -0
- package/dist/tooling/policy.js +45 -0
- package/dist/tooling/policy.js.map +1 -0
- package/dist/tooling/registry-build.js +1 -1
- package/dist/tooling/registry-build.js.map +1 -1
- package/dist/types.d.ts +189 -18
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -8
package/dist/fallback/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ interface AgentFallbackPolicy {
|
|
|
16
16
|
readonly capacityEligible?: boolean;
|
|
17
17
|
/** Default `true`. */
|
|
18
18
|
readonly contextLengthEligible?: boolean;
|
|
19
|
-
/** Default `false`
|
|
19
|
+
/** Default `false` - `withRetry` already covers transient errors. */
|
|
20
20
|
readonly transientEligible?: boolean;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["DEFAULT_POLICY: Required<AgentFallbackPolicy>","merged: Required<AgentFallbackPolicy>"],"sources":["../../src/fallback/index.ts"],"sourcesContent":["/**\n * Agent-level model fallback chain primitives.\n *\n * The agent runtime walks `[primaryModel, ...Agent.fallbackModels]`\n * on fallback-eligible errors during the per-step provider call.\n * `isAgentFallbackEligible(...)` is the pure decision function the\n * runtime calls once per `ProviderError`.\n *\n * Layering: this module is the **agent-step-level** fallback\n * (re-tries the whole step against a different model on rate-limit\n * / capacity / context-length / transient errors). The\n * **request-level** `withFallback` provider middleware\n * (`@graphorin/provider`) is a separate concern
|
|
1
|
+
{"version":3,"file":"index.js","names":["DEFAULT_POLICY: Required<AgentFallbackPolicy>","merged: Required<AgentFallbackPolicy>"],"sources":["../../src/fallback/index.ts"],"sourcesContent":["/**\n * Agent-level model fallback chain primitives.\n *\n * The agent runtime walks `[primaryModel, ...Agent.fallbackModels]`\n * on fallback-eligible errors during the per-step provider call.\n * `isAgentFallbackEligible(...)` is the pure decision function the\n * runtime calls once per `ProviderError`.\n *\n * Layering: this module is the **agent-step-level** fallback\n * (re-tries the whole step against a different model on rate-limit\n * / capacity / context-length / transient errors). The\n * **request-level** `withFallback` provider middleware\n * (`@graphorin/provider`) is a separate concern - it retries against\n * an alternate provider serving the **same** model concept on\n * transient errors inside one `provider.stream(...)` call.\n *\n * @packageDocumentation\n */\n\nimport type { ProviderError } from '@graphorin/core';\n\n/**\n * Operator-supplied policy that lets the consumer toggle which\n * `ProviderError` kinds the agent runtime should consider eligible\n * for whole-step retries against the next model in the chain.\n *\n * @stable\n */\nexport interface AgentFallbackPolicy {\n /** Default `true`. */\n readonly rateLimitEligible?: boolean;\n /** Default `true`. */\n readonly capacityEligible?: boolean;\n /** Default `true`. */\n readonly contextLengthEligible?: boolean;\n /** Default `false` - `withRetry` already covers transient errors. */\n readonly transientEligible?: boolean;\n}\n\n/**\n * Stable taxonomy returned by {@link isAgentFallbackEligible} on\n * eligible errors.\n *\n * @stable\n */\nexport type AgentFallbackReason = 'rate-limit' | 'capacity' | 'context-length' | 'transient';\n\n/**\n * Outcome of {@link isAgentFallbackEligible}.\n *\n * @stable\n */\nexport interface AgentFallbackEligibility {\n readonly eligible: boolean;\n readonly reason?: AgentFallbackReason;\n}\n\nconst DEFAULT_POLICY: Required<AgentFallbackPolicy> = {\n rateLimitEligible: true,\n capacityEligible: true,\n contextLengthEligible: true,\n transientEligible: false,\n};\n\n/**\n * Pure dispatcher that maps a {@link ProviderError} to one of four\n * eligible reasons or to `eligible: false` if the error is on the\n * bypass list (`auth | invalid-input | content-filter | cancelled`).\n *\n * The function is intentionally allocation-free on the hot path so\n * the agent runtime can call it once per provider error per step\n * without budget concerns.\n *\n * @stable\n */\nexport function isAgentFallbackEligible(\n error: ProviderError,\n policy: AgentFallbackPolicy = {},\n): AgentFallbackEligibility {\n const merged: Required<AgentFallbackPolicy> = { ...DEFAULT_POLICY, ...policy };\n switch (error.kind) {\n case 'rate-limit':\n return merged.rateLimitEligible\n ? { eligible: true, reason: 'rate-limit' }\n : { eligible: false };\n case 'capacity':\n return merged.capacityEligible ? { eligible: true, reason: 'capacity' } : { eligible: false };\n case 'context-length':\n return merged.contextLengthEligible\n ? { eligible: true, reason: 'context-length' }\n : { eligible: false };\n case 'transient':\n return merged.transientEligible\n ? { eligible: true, reason: 'transient' }\n : { eligible: false };\n case 'invalid-request':\n case 'unauthorized':\n case 'content-filter':\n case 'unknown':\n return { eligible: false };\n default: {\n // Defensive: unknown error kind treated as ineligible.\n const _exhaustive: never = error.kind;\n void _exhaustive;\n return { eligible: false };\n }\n }\n}\n"],"mappings":";AAyDA,MAAMA,iBAAgD;CACpD,mBAAmB;CACnB,kBAAkB;CAClB,uBAAuB;CACvB,mBAAmB;CACpB;;;;;;;;;;;;AAaD,SAAgB,wBACd,OACA,SAA8B,EAAE,EACN;CAC1B,MAAMC,SAAwC;EAAE,GAAG;EAAgB,GAAG;EAAQ;AAC9E,SAAQ,MAAM,MAAd;EACE,KAAK,aACH,QAAO,OAAO,oBACV;GAAE,UAAU;GAAM,QAAQ;GAAc,GACxC,EAAE,UAAU,OAAO;EACzB,KAAK,WACH,QAAO,OAAO,mBAAmB;GAAE,UAAU;GAAM,QAAQ;GAAY,GAAG,EAAE,UAAU,OAAO;EAC/F,KAAK,iBACH,QAAO,OAAO,wBACV;GAAE,UAAU;GAAM,QAAQ;GAAkB,GAC5C,EAAE,UAAU,OAAO;EACzB,KAAK,YACH,QAAO,OAAO,oBACV;GAAE,UAAU;GAAM,QAAQ;GAAa,GACvC,EAAE,UAAU,OAAO;EACzB,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,UACH,QAAO,EAAE,UAAU,OAAO;EAC5B;AAE6B,SAAM;AAEjC,UAAO,EAAE,UAAU,OAAO"}
|
package/dist/fanout/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ interface PerChildBudget {
|
|
|
13
13
|
/**
|
|
14
14
|
* Max `usage.totalTokens` per child. Enforced **post-hoc** and only
|
|
15
15
|
* for usage-reporting children (an `invoke` that resolves to a full
|
|
16
|
-
* `AgentResult`
|
|
16
|
+
* `AgentResult` - e.g. `() => child.run(input)`); a child returning a
|
|
17
17
|
* plain value reports `tokensUsed: 0` and this cap cannot fire.
|
|
18
18
|
*/
|
|
19
19
|
readonly tokens?: number;
|
|
@@ -45,7 +45,7 @@ type MergeStrategy<TOutput = unknown> = {
|
|
|
45
45
|
/**
|
|
46
46
|
* Per-child outcome surfaced on
|
|
47
47
|
* {@link FanOutResult.children}. Failed-child isolation: a child
|
|
48
|
-
* that throws produces a `ChildResult` with `status: 'failed'`
|
|
48
|
+
* that throws produces a `ChildResult` with `status: 'failed'` -
|
|
49
49
|
* never an exception thrown from the fan-out call itself.
|
|
50
50
|
*
|
|
51
51
|
* @stable
|
|
@@ -81,7 +81,7 @@ interface FanOutResult<TOutput = unknown> {
|
|
|
81
81
|
interface FanOutOptions<TOutput = unknown> {
|
|
82
82
|
/**
|
|
83
83
|
* The sub-agents to invoke. Each entry is invoked as a function
|
|
84
|
-
* returning a `Promise<TOutput>`
|
|
84
|
+
* returning a `Promise<TOutput>` - the fan-out helper does not
|
|
85
85
|
* impose an `Agent` shape on the children so the runtime can
|
|
86
86
|
* adapt any callable surface.
|
|
87
87
|
*/
|
|
@@ -89,7 +89,7 @@ interface FanOutOptions<TOutput = unknown> {
|
|
|
89
89
|
readonly agentId: string;
|
|
90
90
|
/**
|
|
91
91
|
* Child callable. Resolve to a plain `TOutput`, or to a full
|
|
92
|
-
* `AgentResult` (e.g. `() => childAgent.run(input)`)
|
|
92
|
+
* `AgentResult` (e.g. `() => childAgent.run(input)`) - the fan-out
|
|
93
93
|
* detects the result envelope structurally (`output` + numeric
|
|
94
94
|
* `usage.totalTokens` + `state`), unwraps `output`, and harvests
|
|
95
95
|
* `tokensUsed` / `toolCallCount` so per-child budgets can enforce.
|
|
@@ -117,8 +117,8 @@ interface FanOutOptions<TOutput = unknown> {
|
|
|
117
117
|
* Sideways-injection merge guard (AG-7): on `'judge-merge'` the
|
|
118
118
|
* fan-out scores each child's source trust and contribution weight
|
|
119
119
|
* against the judge's merged output; a biased merge emits
|
|
120
|
-
* `agent.lateral-leak.detected` (vector `sideways-injection`) and
|
|
121
|
-
* under `strictness: 'detect-and-block'`
|
|
120
|
+
* `agent.lateral-leak.detected` (vector `sideways-injection`) and -
|
|
121
|
+
* under `strictness: 'detect-and-block'` - throws
|
|
122
122
|
* {@link MergeBlockedError}.
|
|
123
123
|
*/
|
|
124
124
|
readonly mergeGuard?: MergeGuardConfig;
|
|
@@ -126,12 +126,12 @@ interface FanOutOptions<TOutput = unknown> {
|
|
|
126
126
|
readonly runId: string;
|
|
127
127
|
readonly sessionId: string;
|
|
128
128
|
readonly agentId: string;
|
|
129
|
-
/** Default
|
|
129
|
+
/** Default - generated from `runId + Date.now()`. */
|
|
130
130
|
readonly fanOutId?: string;
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
133
|
* Run a fan-out and produce the aggregate {@link FanOutResult}.
|
|
134
|
-
* Pure with respect to side effects
|
|
134
|
+
* Pure with respect to side effects - the runtime emits events /
|
|
135
135
|
* audit rows / counter increments via the supplied `emit` callback.
|
|
136
136
|
*
|
|
137
137
|
* @stable
|
package/dist/fanout/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
|
|
5
5
|
//#region src/fanout/index.ts
|
|
6
6
|
/**
|
|
7
|
-
* Agent-step-level fan-out
|
|
7
|
+
* Agent-step-level fan-out - `Agent.fanOut(...)` convenience that
|
|
8
8
|
* spawns N sub-agents in parallel under a bounded-fanout cap with
|
|
9
9
|
* per-child budgets and four built-in merge strategies.
|
|
10
10
|
*
|
|
@@ -45,7 +45,7 @@ function harvestAgentResult(value) {
|
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Whitespace-token overlap of a child's output against the merged
|
|
48
|
-
* output, in `[0,1]`
|
|
48
|
+
* output, in `[0,1]` - the contribution-weight estimate the merge
|
|
49
49
|
* guard's docstring contracts (each child scored independently; a
|
|
50
50
|
* judge parroting one child verbatim yields ~1.0 for that child).
|
|
51
51
|
*/
|
|
@@ -143,7 +143,7 @@ async function runWithSemaphore(children, cap, signal, perBudget, onChildResult)
|
|
|
143
143
|
}
|
|
144
144
|
/**
|
|
145
145
|
* Run a fan-out and produce the aggregate {@link FanOutResult}.
|
|
146
|
-
* Pure with respect to side effects
|
|
146
|
+
* Pure with respect to side effects - the runtime emits events /
|
|
147
147
|
* audit rows / counter increments via the supplied `emit` callback.
|
|
148
148
|
*
|
|
149
149
|
* @stable
|
package/dist/fanout/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["results: ChildResult<TOutput>[]","running: Set<Promise<void>>","timer: ReturnType<typeof setTimeout> | undefined","status: ChildResult<TOutput>['status']","merge: MergeStrategy<TOutput>","merged: TOutput","parts: string[]","childMetadata: FanOutChildMetadata[]"],"sources":["../../src/fanout/index.ts"],"sourcesContent":["/**\n * Agent-step-level fan-out — `Agent.fanOut(...)` convenience that\n * spawns N sub-agents in parallel under a bounded-fanout cap with\n * per-child budgets and four built-in merge strategies.\n *\n * Boundary discipline against the workflow `Dispatch(...)`\n * primitive: fan-out is **agent-step-level inline result** (children\n * share parent `RunContext` lineage; result consumed by parent's\n * continuing loop within one or few `agent.run(...)` calls).\n * `Dispatch(...)` is workflow-step-level checkpointed durable graph.\n * The two compose orthogonally.\n *\n * @packageDocumentation\n */\n\nimport { createHash } from 'node:crypto';\nimport type { AgentEvent, FanOutChildMetadata } from '@graphorin/core';\nimport { MergeBlockedError } from '../errors/index.js';\nimport {\n type ContentOriginKind,\n computeSourceTrust,\n evaluateMerge,\n type MergeGuardConfig,\n type TrustClass,\n} from '../lateral-leak/merge-guard.js';\n\n/**\n * Per-child budget. Defaults derived from the canonical 2026\n * scaling-rule table for agent fan-out workloads.\n *\n * @stable\n */\nexport interface PerChildBudget {\n /**\n * Max `usage.totalTokens` per child. Enforced **post-hoc** and only\n * for usage-reporting children (an `invoke` that resolves to a full\n * `AgentResult` — e.g. `() => child.run(input)`); a child returning a\n * plain value reports `tokensUsed: 0` and this cap cannot fire.\n */\n readonly tokens?: number;\n /**\n * Max tool calls per child. Same usage-reporting contract as\n * {@link PerChildBudget.tokens} (counted from `state.steps`).\n */\n readonly toolCalls?: number;\n /** Wall-clock cap, enforced for every child via a race timer. */\n readonly durationMs?: number;\n}\n\n/**\n * Built-in merge-strategy taxonomy.\n *\n * @stable\n */\nexport type MergeStrategy<TOutput = unknown> =\n | { readonly kind: 'concat'; readonly separator?: string }\n | { readonly kind: 'first-success' }\n | {\n readonly kind: 'judge-merge';\n readonly judge: (children: ReadonlyArray<ChildResult<TOutput>>) => Promise<TOutput>;\n }\n | {\n readonly kind: 'custom';\n readonly merge: (children: ReadonlyArray<ChildResult<TOutput>>) => Promise<TOutput>;\n };\n\n/**\n * Per-child outcome surfaced on\n * {@link FanOutResult.children}. Failed-child isolation: a child\n * that throws produces a `ChildResult` with `status: 'failed'` —\n * never an exception thrown from the fan-out call itself.\n *\n * @stable\n */\nexport interface ChildResult<TOutput = unknown> {\n readonly agentId: string;\n readonly status: 'completed' | 'failed' | 'budget-exceeded' | 'cancelled';\n readonly output?: TOutput;\n readonly error?: { readonly message: string; readonly code: string };\n readonly tokensUsed: number;\n readonly toolCallCount: number;\n readonly durationMs: number;\n}\n\n/**\n * Aggregate result returned by `Agent.fanOut(...)`.\n *\n * @stable\n */\nexport interface FanOutResult<TOutput = unknown> {\n readonly fanOutId: string;\n readonly output: TOutput;\n readonly children: ReadonlyArray<ChildResult<TOutput>>;\n readonly mergeDurationMs: number;\n}\n\n/**\n * Per-call options accepted by `Agent.fanOut(...)`.\n *\n * @stable\n */\nexport interface FanOutOptions<TOutput = unknown> {\n /**\n * The sub-agents to invoke. Each entry is invoked as a function\n * returning a `Promise<TOutput>` — the fan-out helper does not\n * impose an `Agent` shape on the children so the runtime can\n * adapt any callable surface.\n */\n readonly children: ReadonlyArray<{\n readonly agentId: string;\n /**\n * Child callable. Resolve to a plain `TOutput`, or to a full\n * `AgentResult` (e.g. `() => childAgent.run(input)`) — the fan-out\n * detects the result envelope structurally (`output` + numeric\n * `usage.totalTokens` + `state`), unwraps `output`, and harvests\n * `tokensUsed` / `toolCallCount` so per-child budgets can enforce.\n */\n readonly invoke: () => Promise<TOutput>;\n /** Trust-class for the merge guard (default `'loopback'`). */\n readonly trustClass?: TrustClass;\n /** Content-origin for the merge guard (default `'built-in'`). */\n readonly origin?: ContentOriginKind;\n /** Rolling trust adjustment in `[0,1]` (default `1`). */\n readonly historyAdjustment?: number;\n }>;\n /** Default `4` per the canonical 2026 production lesson. */\n readonly maxConcurrentChildren?: number;\n /** Per-child budget; default unset. */\n readonly perBudget?: PerChildBudget;\n /** Default `{ kind: 'concat' }`. */\n readonly mergeStrategy?: MergeStrategy<TOutput>;\n readonly signal?: AbortSignal;\n /** Optional callback for per-child completion observability. */\n readonly onChildResult?: (result: ChildResult<TOutput>) => void;\n /** Optional event emitter for `agent.fanout.spawned / merged`. */\n readonly emit?: (event: AgentEvent) => void;\n /**\n * Sideways-injection merge guard (AG-7): on `'judge-merge'` the\n * fan-out scores each child's source trust and contribution weight\n * against the judge's merged output; a biased merge emits\n * `agent.lateral-leak.detected` (vector `sideways-injection`) and —\n * under `strictness: 'detect-and-block'` — throws\n * {@link MergeBlockedError}.\n */\n readonly mergeGuard?: MergeGuardConfig;\n /** Identifiers required to populate the events. */\n readonly runId: string;\n readonly sessionId: string;\n readonly agentId: string;\n /** Default — generated from `runId + Date.now()`. */\n readonly fanOutId?: string;\n}\n\nconst DEFAULT_MAX_CONCURRENT_CHILDREN = 4;\n\n/**\n * Structurally detect a full `AgentResult` returned by a child\n * `invoke` (AG-16): an object carrying `output`, a numeric\n * `usage.totalTokens`, and a `state` with string `id`/`status`. The\n * three-field match makes accidental collision with a user `TOutput`\n * negligible; children whose genuine output looks like this should\n * resolve a plain value instead.\n */\nfunction harvestAgentResult(\n value: unknown,\n):\n | { readonly output: unknown; readonly tokensUsed: number; readonly toolCallCount: number }\n | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const v = value as {\n readonly output?: unknown;\n readonly usage?: { readonly totalTokens?: unknown };\n readonly state?: { readonly id?: unknown; readonly status?: unknown; readonly steps?: unknown };\n };\n if (!('output' in v)) return undefined;\n if (typeof v.usage?.totalTokens !== 'number') return undefined;\n if (typeof v.state?.id !== 'string' || typeof v.state.status !== 'string') return undefined;\n let toolCallCount = 0;\n if (Array.isArray(v.state.steps)) {\n for (const step of v.state.steps) {\n const calls = (step as { readonly toolCalls?: unknown }).toolCalls;\n if (Array.isArray(calls)) toolCallCount += calls.length;\n }\n }\n return { output: v.output, tokensUsed: v.usage.totalTokens, toolCallCount };\n}\n\n/**\n * Whitespace-token overlap of a child's output against the merged\n * output, in `[0,1]` — the contribution-weight estimate the merge\n * guard's docstring contracts (each child scored independently; a\n * judge parroting one child verbatim yields ~1.0 for that child).\n */\nfunction contributionWeight(childText: string, mergedText: string): number {\n if (childText.length === 0 || mergedText.length === 0) return 0;\n const mergedTokens = mergedText.split(/\\s+/).filter((t) => t.length > 0);\n if (mergedTokens.length === 0) return 0;\n const childTokens = new Set(childText.split(/\\s+/).filter((t) => t.length > 0));\n let hits = 0;\n for (const token of mergedTokens) {\n if (childTokens.has(token)) hits += 1;\n }\n return hits / mergedTokens.length;\n}\n\nasync function runWithSemaphore<TOutput>(\n children: ReadonlyArray<FanOutOptions<TOutput>['children'][number]>,\n cap: number,\n signal: AbortSignal | undefined,\n perBudget: PerChildBudget | undefined,\n onChildResult?: (r: ChildResult<TOutput>) => void,\n): Promise<ReadonlyArray<ChildResult<TOutput>>> {\n const results: ChildResult<TOutput>[] = new Array(children.length);\n let cursor = 0;\n const running: Set<Promise<void>> = new Set();\n const limit = Math.max(1, Math.min(cap, children.length));\n\n const launchOne = (index: number): Promise<void> => {\n const child = children[index];\n if (child === undefined) return Promise.resolve();\n const start = Date.now();\n const exec = async (): Promise<ChildResult<TOutput>> => {\n if (signal?.aborted) {\n return {\n agentId: child.agentId,\n status: 'cancelled',\n tokensUsed: 0,\n toolCallCount: 0,\n durationMs: 0,\n };\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const timedPromise =\n perBudget?.durationMs !== undefined\n ? new Promise<TOutput>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error('budget-exceeded:durationMs')),\n perBudget.durationMs,\n );\n })\n : undefined;\n const racePromise = timedPromise\n ? Promise.race([child.invoke(), timedPromise])\n : child.invoke();\n const raw = await racePromise;\n // AG-16: a child resolving to a full AgentResult reports its\n // real usage — unwrap the output and harvest the counters.\n const report = harvestAgentResult(raw);\n const output = (report === undefined ? raw : report.output) as TOutput;\n const tokensUsed = report?.tokensUsed ?? 0;\n const toolCallCount = report?.toolCallCount ?? 0;\n // Post-hoc budget enforcement (only fires for usage-reporting\n // children): the over-budget output is withheld from the merge.\n const exceeded =\n (perBudget?.tokens !== undefined && tokensUsed > perBudget.tokens\n ? `tokens ${tokensUsed} > ${perBudget.tokens}`\n : undefined) ??\n (perBudget?.toolCalls !== undefined && toolCallCount > perBudget.toolCalls\n ? `toolCalls ${toolCallCount} > ${perBudget.toolCalls}`\n : undefined);\n if (exceeded !== undefined) {\n return {\n agentId: child.agentId,\n status: 'budget-exceeded',\n error: { message: `budget-exceeded: ${exceeded}`, code: 'budget-exceeded' },\n tokensUsed,\n toolCallCount,\n durationMs: Date.now() - start,\n };\n }\n return {\n agentId: child.agentId,\n status: 'completed',\n output,\n tokensUsed,\n toolCallCount,\n durationMs: Date.now() - start,\n };\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n const aborted = signal?.aborted;\n const status: ChildResult<TOutput>['status'] = message.startsWith('budget-exceeded')\n ? 'budget-exceeded'\n : aborted\n ? 'cancelled'\n : 'failed';\n return {\n agentId: child.agentId,\n status,\n error: { message, code: status },\n tokensUsed: 0,\n toolCallCount: 0,\n durationMs: Date.now() - start,\n };\n } finally {\n // AG-16: the duration timer must die on EVERY path — leaving it\n // armed on rejection held the event loop open.\n if (timer !== undefined) clearTimeout(timer);\n }\n };\n const promise = exec().then((r) => {\n results[index] = r;\n onChildResult?.(r);\n });\n running.add(promise);\n promise.finally(() => running.delete(promise));\n return promise;\n };\n\n while (cursor < children.length || running.size > 0) {\n while (running.size < limit && cursor < children.length) {\n void launchOne(cursor);\n cursor += 1;\n }\n if (running.size > 0) {\n await Promise.race(running);\n }\n }\n return results;\n}\n\n/**\n * Run a fan-out and produce the aggregate {@link FanOutResult}.\n * Pure with respect to side effects — the runtime emits events /\n * audit rows / counter increments via the supplied `emit` callback.\n *\n * @stable\n */\nexport async function runFanOut<TOutput>(\n opts: FanOutOptions<TOutput>,\n): Promise<FanOutResult<TOutput>> {\n const fanOutId = opts.fanOutId ?? `fanout-${opts.runId.slice(-8)}-${Date.now().toString(36)}`;\n const merge: MergeStrategy<TOutput> = opts.mergeStrategy ?? {\n kind: 'concat',\n };\n const cap = opts.maxConcurrentChildren ?? DEFAULT_MAX_CONCURRENT_CHILDREN;\n\n opts.emit?.({\n type: 'agent.fanout.spawned',\n runId: opts.runId,\n sessionId: opts.sessionId,\n agentId: opts.agentId,\n fanOutId,\n childCount: opts.children.length,\n mergeStrategyKind: merge.kind,\n spawnedAtIso: new Date().toISOString(),\n });\n\n const results = await runWithSemaphore<TOutput>(\n opts.children,\n cap,\n opts.signal,\n opts.perBudget,\n opts.onChildResult,\n );\n\n const mergeStart = Date.now();\n let merged: TOutput;\n switch (merge.kind) {\n case 'concat': {\n const sep = merge.separator ?? '\\n\\n---\\n\\n';\n const parts: string[] = [];\n for (const r of results) {\n if (r.status === 'completed' && r.output !== undefined) {\n parts.push(typeof r.output === 'string' ? r.output : JSON.stringify(r.output));\n } else if (\n r.status === 'failed' ||\n r.status === 'budget-exceeded' ||\n r.status === 'cancelled'\n ) {\n parts.push(`[${r.status}: ${r.agentId}]`);\n }\n }\n merged = parts.join(sep) as unknown as TOutput;\n break;\n }\n case 'first-success': {\n const first = results.find((r) => r.status === 'completed');\n merged = first?.output ?? ('' as unknown as TOutput);\n break;\n }\n case 'judge-merge': {\n merged = await merge.judge(results);\n // AG-7: the sideways-injection merge guard scores each child's\n // source trust × contribution weight against the judge's merged\n // output. A biased merge emits `agent.lateral-leak.detected`;\n // 'detect-and-block' refuses the merge entirely.\n if (opts.mergeGuard !== undefined && opts.mergeGuard.strictness !== 'off') {\n const mergedText = typeof merged === 'string' ? merged : JSON.stringify(merged);\n const overrides = opts.mergeGuard.sourceTrustOverrides ?? {};\n const perChild = results.map((r, i) => {\n const c = opts.children[i];\n const childText =\n r.output === undefined\n ? ''\n : typeof r.output === 'string'\n ? r.output\n : JSON.stringify(r.output);\n return {\n agentId: r.agentId,\n sourceTrust: computeSourceTrust(\n {\n agentId: r.agentId,\n trustClass: c?.trustClass ?? 'loopback',\n origin: c?.origin ?? 'built-in',\n ...(c?.historyAdjustment !== undefined\n ? { historyAdjustment: c.historyAdjustment }\n : {}),\n },\n overrides,\n ),\n contributionWeight: contributionWeight(childText, mergedText),\n };\n });\n const verdict = evaluateMerge(perChild, opts.mergeGuard);\n if (verdict.biased) {\n opts.emit?.({\n type: 'agent.lateral-leak.detected',\n runId: opts.runId,\n sessionId: opts.sessionId,\n agentId: opts.agentId,\n vector: 'sideways-injection',\n severity: verdict.decision === 'block' ? 'block' : 'warn',\n causalityChain: verdict.offendingChild === undefined ? [] : [verdict.offendingChild],\n messageContentSha256: createHash('sha256').update(mergedText, 'utf8').digest('hex'),\n decision:\n verdict.decision === 'block'\n ? 'block'\n : verdict.decision === 'flag'\n ? 'flag'\n : 'detect',\n detectedAtIso: new Date().toISOString(),\n });\n if (verdict.decision === 'block') {\n throw new MergeBlockedError(\n fanOutId,\n `low-trust child '${verdict.offendingChild}' (sourceTrust ${verdict.sourceTrust?.toFixed(2)}) contributes ${(\n (verdict.contributionWeight ?? 0) * 100\n ).toFixed(0)}% of the merged output`,\n );\n }\n }\n }\n break;\n }\n case 'custom':\n merged = await merge.merge(results);\n break;\n default: {\n const _exhaustive: never = merge;\n void _exhaustive;\n merged = '' as unknown as TOutput;\n }\n }\n const mergeDurationMs = Date.now() - mergeStart;\n\n const childMetadata: FanOutChildMetadata[] = results.map((r) => ({\n agentId: r.agentId,\n status: r.status,\n tokensUsed: r.tokensUsed,\n toolCallCount: r.toolCallCount,\n durationMs: r.durationMs,\n }));\n const successfulChildCount = results.filter((r) => r.status === 'completed').length;\n\n opts.emit?.({\n type: 'agent.fanout.merged',\n runId: opts.runId,\n sessionId: opts.sessionId,\n agentId: opts.agentId,\n fanOutId,\n childCount: opts.children.length,\n successfulChildCount,\n mergeStrategyKind: merge.kind,\n mergeDurationMs,\n childMetadata,\n });\n\n return {\n fanOutId,\n output: merged,\n children: results,\n mergeDurationMs,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["results: ChildResult<TOutput>[]","running: Set<Promise<void>>","timer: ReturnType<typeof setTimeout> | undefined","status: ChildResult<TOutput>['status']","merge: MergeStrategy<TOutput>","merged: TOutput","parts: string[]","childMetadata: FanOutChildMetadata[]"],"sources":["../../src/fanout/index.ts"],"sourcesContent":["/**\n * Agent-step-level fan-out - `Agent.fanOut(...)` convenience that\n * spawns N sub-agents in parallel under a bounded-fanout cap with\n * per-child budgets and four built-in merge strategies.\n *\n * Boundary discipline against the workflow `Dispatch(...)`\n * primitive: fan-out is **agent-step-level inline result** (children\n * share parent `RunContext` lineage; result consumed by parent's\n * continuing loop within one or few `agent.run(...)` calls).\n * `Dispatch(...)` is workflow-step-level checkpointed durable graph.\n * The two compose orthogonally.\n *\n * @packageDocumentation\n */\n\nimport { createHash } from 'node:crypto';\nimport type { AgentEvent, FanOutChildMetadata } from '@graphorin/core';\nimport { MergeBlockedError } from '../errors/index.js';\nimport {\n type ContentOriginKind,\n computeSourceTrust,\n evaluateMerge,\n type MergeGuardConfig,\n type TrustClass,\n} from '../lateral-leak/merge-guard.js';\n\n/**\n * Per-child budget. Defaults derived from the canonical 2026\n * scaling-rule table for agent fan-out workloads.\n *\n * @stable\n */\nexport interface PerChildBudget {\n /**\n * Max `usage.totalTokens` per child. Enforced **post-hoc** and only\n * for usage-reporting children (an `invoke` that resolves to a full\n * `AgentResult` - e.g. `() => child.run(input)`); a child returning a\n * plain value reports `tokensUsed: 0` and this cap cannot fire.\n */\n readonly tokens?: number;\n /**\n * Max tool calls per child. Same usage-reporting contract as\n * {@link PerChildBudget.tokens} (counted from `state.steps`).\n */\n readonly toolCalls?: number;\n /** Wall-clock cap, enforced for every child via a race timer. */\n readonly durationMs?: number;\n}\n\n/**\n * Built-in merge-strategy taxonomy.\n *\n * @stable\n */\nexport type MergeStrategy<TOutput = unknown> =\n | { readonly kind: 'concat'; readonly separator?: string }\n | { readonly kind: 'first-success' }\n | {\n readonly kind: 'judge-merge';\n readonly judge: (children: ReadonlyArray<ChildResult<TOutput>>) => Promise<TOutput>;\n }\n | {\n readonly kind: 'custom';\n readonly merge: (children: ReadonlyArray<ChildResult<TOutput>>) => Promise<TOutput>;\n };\n\n/**\n * Per-child outcome surfaced on\n * {@link FanOutResult.children}. Failed-child isolation: a child\n * that throws produces a `ChildResult` with `status: 'failed'` -\n * never an exception thrown from the fan-out call itself.\n *\n * @stable\n */\nexport interface ChildResult<TOutput = unknown> {\n readonly agentId: string;\n readonly status: 'completed' | 'failed' | 'budget-exceeded' | 'cancelled';\n readonly output?: TOutput;\n readonly error?: { readonly message: string; readonly code: string };\n readonly tokensUsed: number;\n readonly toolCallCount: number;\n readonly durationMs: number;\n}\n\n/**\n * Aggregate result returned by `Agent.fanOut(...)`.\n *\n * @stable\n */\nexport interface FanOutResult<TOutput = unknown> {\n readonly fanOutId: string;\n readonly output: TOutput;\n readonly children: ReadonlyArray<ChildResult<TOutput>>;\n readonly mergeDurationMs: number;\n}\n\n/**\n * Per-call options accepted by `Agent.fanOut(...)`.\n *\n * @stable\n */\nexport interface FanOutOptions<TOutput = unknown> {\n /**\n * The sub-agents to invoke. Each entry is invoked as a function\n * returning a `Promise<TOutput>` - the fan-out helper does not\n * impose an `Agent` shape on the children so the runtime can\n * adapt any callable surface.\n */\n readonly children: ReadonlyArray<{\n readonly agentId: string;\n /**\n * Child callable. Resolve to a plain `TOutput`, or to a full\n * `AgentResult` (e.g. `() => childAgent.run(input)`) - the fan-out\n * detects the result envelope structurally (`output` + numeric\n * `usage.totalTokens` + `state`), unwraps `output`, and harvests\n * `tokensUsed` / `toolCallCount` so per-child budgets can enforce.\n */\n readonly invoke: () => Promise<TOutput>;\n /** Trust-class for the merge guard (default `'loopback'`). */\n readonly trustClass?: TrustClass;\n /** Content-origin for the merge guard (default `'built-in'`). */\n readonly origin?: ContentOriginKind;\n /** Rolling trust adjustment in `[0,1]` (default `1`). */\n readonly historyAdjustment?: number;\n }>;\n /** Default `4` per the canonical 2026 production lesson. */\n readonly maxConcurrentChildren?: number;\n /** Per-child budget; default unset. */\n readonly perBudget?: PerChildBudget;\n /** Default `{ kind: 'concat' }`. */\n readonly mergeStrategy?: MergeStrategy<TOutput>;\n readonly signal?: AbortSignal;\n /** Optional callback for per-child completion observability. */\n readonly onChildResult?: (result: ChildResult<TOutput>) => void;\n /** Optional event emitter for `agent.fanout.spawned / merged`. */\n readonly emit?: (event: AgentEvent) => void;\n /**\n * Sideways-injection merge guard (AG-7): on `'judge-merge'` the\n * fan-out scores each child's source trust and contribution weight\n * against the judge's merged output; a biased merge emits\n * `agent.lateral-leak.detected` (vector `sideways-injection`) and -\n * under `strictness: 'detect-and-block'` - throws\n * {@link MergeBlockedError}.\n */\n readonly mergeGuard?: MergeGuardConfig;\n /** Identifiers required to populate the events. */\n readonly runId: string;\n readonly sessionId: string;\n readonly agentId: string;\n /** Default - generated from `runId + Date.now()`. */\n readonly fanOutId?: string;\n}\n\nconst DEFAULT_MAX_CONCURRENT_CHILDREN = 4;\n\n/**\n * Structurally detect a full `AgentResult` returned by a child\n * `invoke` (AG-16): an object carrying `output`, a numeric\n * `usage.totalTokens`, and a `state` with string `id`/`status`. The\n * three-field match makes accidental collision with a user `TOutput`\n * negligible; children whose genuine output looks like this should\n * resolve a plain value instead.\n */\nfunction harvestAgentResult(\n value: unknown,\n):\n | { readonly output: unknown; readonly tokensUsed: number; readonly toolCallCount: number }\n | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const v = value as {\n readonly output?: unknown;\n readonly usage?: { readonly totalTokens?: unknown };\n readonly state?: { readonly id?: unknown; readonly status?: unknown; readonly steps?: unknown };\n };\n if (!('output' in v)) return undefined;\n if (typeof v.usage?.totalTokens !== 'number') return undefined;\n if (typeof v.state?.id !== 'string' || typeof v.state.status !== 'string') return undefined;\n let toolCallCount = 0;\n if (Array.isArray(v.state.steps)) {\n for (const step of v.state.steps) {\n const calls = (step as { readonly toolCalls?: unknown }).toolCalls;\n if (Array.isArray(calls)) toolCallCount += calls.length;\n }\n }\n return { output: v.output, tokensUsed: v.usage.totalTokens, toolCallCount };\n}\n\n/**\n * Whitespace-token overlap of a child's output against the merged\n * output, in `[0,1]` - the contribution-weight estimate the merge\n * guard's docstring contracts (each child scored independently; a\n * judge parroting one child verbatim yields ~1.0 for that child).\n */\nfunction contributionWeight(childText: string, mergedText: string): number {\n if (childText.length === 0 || mergedText.length === 0) return 0;\n const mergedTokens = mergedText.split(/\\s+/).filter((t) => t.length > 0);\n if (mergedTokens.length === 0) return 0;\n const childTokens = new Set(childText.split(/\\s+/).filter((t) => t.length > 0));\n let hits = 0;\n for (const token of mergedTokens) {\n if (childTokens.has(token)) hits += 1;\n }\n return hits / mergedTokens.length;\n}\n\nasync function runWithSemaphore<TOutput>(\n children: ReadonlyArray<FanOutOptions<TOutput>['children'][number]>,\n cap: number,\n signal: AbortSignal | undefined,\n perBudget: PerChildBudget | undefined,\n onChildResult?: (r: ChildResult<TOutput>) => void,\n): Promise<ReadonlyArray<ChildResult<TOutput>>> {\n const results: ChildResult<TOutput>[] = new Array(children.length);\n let cursor = 0;\n const running: Set<Promise<void>> = new Set();\n const limit = Math.max(1, Math.min(cap, children.length));\n\n const launchOne = (index: number): Promise<void> => {\n const child = children[index];\n if (child === undefined) return Promise.resolve();\n const start = Date.now();\n const exec = async (): Promise<ChildResult<TOutput>> => {\n if (signal?.aborted) {\n return {\n agentId: child.agentId,\n status: 'cancelled',\n tokensUsed: 0,\n toolCallCount: 0,\n durationMs: 0,\n };\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const timedPromise =\n perBudget?.durationMs !== undefined\n ? new Promise<TOutput>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error('budget-exceeded:durationMs')),\n perBudget.durationMs,\n );\n })\n : undefined;\n const racePromise = timedPromise\n ? Promise.race([child.invoke(), timedPromise])\n : child.invoke();\n const raw = await racePromise;\n // AG-16: a child resolving to a full AgentResult reports its\n // real usage - unwrap the output and harvest the counters.\n const report = harvestAgentResult(raw);\n const output = (report === undefined ? raw : report.output) as TOutput;\n const tokensUsed = report?.tokensUsed ?? 0;\n const toolCallCount = report?.toolCallCount ?? 0;\n // Post-hoc budget enforcement (only fires for usage-reporting\n // children): the over-budget output is withheld from the merge.\n const exceeded =\n (perBudget?.tokens !== undefined && tokensUsed > perBudget.tokens\n ? `tokens ${tokensUsed} > ${perBudget.tokens}`\n : undefined) ??\n (perBudget?.toolCalls !== undefined && toolCallCount > perBudget.toolCalls\n ? `toolCalls ${toolCallCount} > ${perBudget.toolCalls}`\n : undefined);\n if (exceeded !== undefined) {\n return {\n agentId: child.agentId,\n status: 'budget-exceeded',\n error: { message: `budget-exceeded: ${exceeded}`, code: 'budget-exceeded' },\n tokensUsed,\n toolCallCount,\n durationMs: Date.now() - start,\n };\n }\n return {\n agentId: child.agentId,\n status: 'completed',\n output,\n tokensUsed,\n toolCallCount,\n durationMs: Date.now() - start,\n };\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n const aborted = signal?.aborted;\n const status: ChildResult<TOutput>['status'] = message.startsWith('budget-exceeded')\n ? 'budget-exceeded'\n : aborted\n ? 'cancelled'\n : 'failed';\n return {\n agentId: child.agentId,\n status,\n error: { message, code: status },\n tokensUsed: 0,\n toolCallCount: 0,\n durationMs: Date.now() - start,\n };\n } finally {\n // AG-16: the duration timer must die on EVERY path - leaving it\n // armed on rejection held the event loop open.\n if (timer !== undefined) clearTimeout(timer);\n }\n };\n const promise = exec().then((r) => {\n results[index] = r;\n onChildResult?.(r);\n });\n running.add(promise);\n promise.finally(() => running.delete(promise));\n return promise;\n };\n\n while (cursor < children.length || running.size > 0) {\n while (running.size < limit && cursor < children.length) {\n void launchOne(cursor);\n cursor += 1;\n }\n if (running.size > 0) {\n await Promise.race(running);\n }\n }\n return results;\n}\n\n/**\n * Run a fan-out and produce the aggregate {@link FanOutResult}.\n * Pure with respect to side effects - the runtime emits events /\n * audit rows / counter increments via the supplied `emit` callback.\n *\n * @stable\n */\nexport async function runFanOut<TOutput>(\n opts: FanOutOptions<TOutput>,\n): Promise<FanOutResult<TOutput>> {\n const fanOutId = opts.fanOutId ?? `fanout-${opts.runId.slice(-8)}-${Date.now().toString(36)}`;\n const merge: MergeStrategy<TOutput> = opts.mergeStrategy ?? {\n kind: 'concat',\n };\n const cap = opts.maxConcurrentChildren ?? DEFAULT_MAX_CONCURRENT_CHILDREN;\n\n opts.emit?.({\n type: 'agent.fanout.spawned',\n runId: opts.runId,\n sessionId: opts.sessionId,\n agentId: opts.agentId,\n fanOutId,\n childCount: opts.children.length,\n mergeStrategyKind: merge.kind,\n spawnedAtIso: new Date().toISOString(),\n });\n\n const results = await runWithSemaphore<TOutput>(\n opts.children,\n cap,\n opts.signal,\n opts.perBudget,\n opts.onChildResult,\n );\n\n const mergeStart = Date.now();\n let merged: TOutput;\n switch (merge.kind) {\n case 'concat': {\n const sep = merge.separator ?? '\\n\\n---\\n\\n';\n const parts: string[] = [];\n for (const r of results) {\n if (r.status === 'completed' && r.output !== undefined) {\n parts.push(typeof r.output === 'string' ? r.output : JSON.stringify(r.output));\n } else if (\n r.status === 'failed' ||\n r.status === 'budget-exceeded' ||\n r.status === 'cancelled'\n ) {\n parts.push(`[${r.status}: ${r.agentId}]`);\n }\n }\n merged = parts.join(sep) as unknown as TOutput;\n break;\n }\n case 'first-success': {\n const first = results.find((r) => r.status === 'completed');\n merged = first?.output ?? ('' as unknown as TOutput);\n break;\n }\n case 'judge-merge': {\n merged = await merge.judge(results);\n // AG-7: the sideways-injection merge guard scores each child's\n // source trust × contribution weight against the judge's merged\n // output. A biased merge emits `agent.lateral-leak.detected`;\n // 'detect-and-block' refuses the merge entirely.\n if (opts.mergeGuard !== undefined && opts.mergeGuard.strictness !== 'off') {\n const mergedText = typeof merged === 'string' ? merged : JSON.stringify(merged);\n const overrides = opts.mergeGuard.sourceTrustOverrides ?? {};\n const perChild = results.map((r, i) => {\n const c = opts.children[i];\n const childText =\n r.output === undefined\n ? ''\n : typeof r.output === 'string'\n ? r.output\n : JSON.stringify(r.output);\n return {\n agentId: r.agentId,\n sourceTrust: computeSourceTrust(\n {\n agentId: r.agentId,\n trustClass: c?.trustClass ?? 'loopback',\n origin: c?.origin ?? 'built-in',\n ...(c?.historyAdjustment !== undefined\n ? { historyAdjustment: c.historyAdjustment }\n : {}),\n },\n overrides,\n ),\n contributionWeight: contributionWeight(childText, mergedText),\n };\n });\n const verdict = evaluateMerge(perChild, opts.mergeGuard);\n if (verdict.biased) {\n opts.emit?.({\n type: 'agent.lateral-leak.detected',\n runId: opts.runId,\n sessionId: opts.sessionId,\n agentId: opts.agentId,\n vector: 'sideways-injection',\n severity: verdict.decision === 'block' ? 'block' : 'warn',\n causalityChain: verdict.offendingChild === undefined ? [] : [verdict.offendingChild],\n messageContentSha256: createHash('sha256').update(mergedText, 'utf8').digest('hex'),\n decision:\n verdict.decision === 'block'\n ? 'block'\n : verdict.decision === 'flag'\n ? 'flag'\n : 'detect',\n detectedAtIso: new Date().toISOString(),\n });\n if (verdict.decision === 'block') {\n throw new MergeBlockedError(\n fanOutId,\n `low-trust child '${verdict.offendingChild}' (sourceTrust ${verdict.sourceTrust?.toFixed(2)}) contributes ${(\n (verdict.contributionWeight ?? 0) * 100\n ).toFixed(0)}% of the merged output`,\n );\n }\n }\n }\n break;\n }\n case 'custom':\n merged = await merge.merge(results);\n break;\n default: {\n const _exhaustive: never = merge;\n void _exhaustive;\n merged = '' as unknown as TOutput;\n }\n }\n const mergeDurationMs = Date.now() - mergeStart;\n\n const childMetadata: FanOutChildMetadata[] = results.map((r) => ({\n agentId: r.agentId,\n status: r.status,\n tokensUsed: r.tokensUsed,\n toolCallCount: r.toolCallCount,\n durationMs: r.durationMs,\n }));\n const successfulChildCount = results.filter((r) => r.status === 'completed').length;\n\n opts.emit?.({\n type: 'agent.fanout.merged',\n runId: opts.runId,\n sessionId: opts.sessionId,\n agentId: opts.agentId,\n fanOutId,\n childCount: opts.children.length,\n successfulChildCount,\n mergeStrategyKind: merge.kind,\n mergeDurationMs,\n childMetadata,\n });\n\n return {\n fanOutId,\n output: merged,\n children: results,\n mergeDurationMs,\n };\n}\n"],"mappings":"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"}
|
package/dist/filters/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ declare function lastN(n?: number): DescribedFilter;
|
|
|
28
28
|
*/
|
|
29
29
|
declare function lastUser(): DescribedFilter;
|
|
30
30
|
/**
|
|
31
|
-
* The full unfiltered history. Discouraged
|
|
31
|
+
* The full unfiltered history. Discouraged - security-conscious
|
|
32
32
|
* callers should pick {@link lastN} or {@link bySensitivity} instead
|
|
33
33
|
* (a sub-agent rarely needs the parent's entire conversation).
|
|
34
34
|
*
|
package/dist/filters/index.js
CHANGED
|
@@ -10,7 +10,7 @@ const SENSITIVITY_RANK = {
|
|
|
10
10
|
* Return `true` iff the message's effective sensitivity is at most
|
|
11
11
|
* `maxTier` (a strictly weaker check than core's
|
|
12
12
|
* `acceptsSensitivity` because the filter library does not have
|
|
13
|
-
* the provider's `acceptsSensitivity[]` array
|
|
13
|
+
* the provider's `acceptsSensitivity[]` array - only a ceiling).
|
|
14
14
|
*/
|
|
15
15
|
function withinTier(record, maxTier) {
|
|
16
16
|
return SENSITIVITY_RANK[record] <= SENSITIVITY_RANK[maxTier];
|
|
@@ -88,7 +88,7 @@ function lastUser() {
|
|
|
88
88
|
}, { kind: "last-user" });
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
|
-
* The full unfiltered history. Discouraged
|
|
91
|
+
* The full unfiltered history. Discouraged - security-conscious
|
|
92
92
|
* callers should pick {@link lastN} or {@link bySensitivity} instead
|
|
93
93
|
* (a sub-agent rarely needs the parent's entire conversation).
|
|
94
94
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["SENSITIVITY_RANK: Record<Sensitivity, number>","out: Message[]","nonSystem: Message[]","maxTier: Sensitivity","filters","ordered: ReadonlyArray<HandoffFilter>","descriptors: HandoffInputFilterDescriptor[]","current: readonly Message[]","FILTER_KIND_CUSTOM: HandoffInputFilterDescriptor"],"sources":["../../src/filters/index.ts"],"sourcesContent":["/**\n * Handoff filter library — a small set of pure, composable functions\n * that take the parent agent's message history and return a filtered\n * subset suitable for forwarding to a child agent.\n *\n * Every filter pairs a `HandoffFilter` runtime function with a\n * serializable {@link HandoffInputFilterDescriptor} so the JSONL\n * session export (`@graphorin/sessions`) can replay the filter stack\n * even after the runtime implementations evolve.\n *\n * Reasoning content is **always** stripped at the handoff boundary —\n * `filters.compose(...)` guarantees `stripReasoning()` runs last so a\n * caller-supplied filter cannot accidentally forward reasoning to a\n * child agent. This is the confidentiality + token-economy default\n * documented in the agent-loop reference.\n *\n * @packageDocumentation\n */\n\nimport type {\n HandoffFilter,\n HandoffInputFilterDescriptor,\n Message,\n Sensitivity,\n} from '@graphorin/core';\nimport { SENSITIVITY_ORDER } from '@graphorin/core';\n\nconst SENSITIVITY_RANK: Record<Sensitivity, number> = {\n public: 0,\n internal: 1,\n secret: 2,\n};\n\n/**\n * Return `true` iff the message's effective sensitivity is at most\n * `maxTier` (a strictly weaker check than core's\n * `acceptsSensitivity` because the filter library does not have\n * the provider's `acceptsSensitivity[]` array — only a ceiling).\n */\nfunction withinTier(record: Sensitivity, maxTier: Sensitivity): boolean {\n return SENSITIVITY_RANK[record] <= SENSITIVITY_RANK[maxTier];\n}\n\nvoid SENSITIVITY_ORDER;\n\n/**\n * A `HandoffFilter` paired with the serializable descriptor that\n * round-trips through the JSONL session export. Authors of custom\n * filters return one of these via `filters.custom({...})`.\n *\n * @stable\n */\nexport interface DescribedFilter extends HandoffFilter {\n readonly descriptor: HandoffInputFilterDescriptor;\n}\n\nfunction makeFilter(fn: HandoffFilter, descriptor: HandoffInputFilterDescriptor): DescribedFilter {\n const wrapped = ((history: readonly Message[]): readonly Message[] =>\n fn(history)) as DescribedFilter;\n Object.defineProperty(wrapped, 'descriptor', { value: descriptor, enumerable: true });\n return wrapped;\n}\n\nfunction isReasoningPart(part: unknown): boolean {\n if (typeof part !== 'object' || part === null) return false;\n const t = (part as { readonly type?: unknown }).type;\n return t === 'reasoning';\n}\n\nfunction stripReasoningFromMessage(msg: Message): Message {\n if (msg.role === 'system' || msg.role === 'tool') return msg;\n const content = msg.content;\n if (typeof content === 'string') return msg;\n const filtered = content.filter((part) => !isReasoningPart(part));\n if (filtered.length === content.length) return msg;\n if (msg.role === 'assistant') {\n return { ...msg, content: filtered };\n }\n return { ...msg, content: filtered };\n}\n\n/**\n * Keep the parent's system prompt and the last `n` non-system\n * messages. Default `n = 10` per DEC-146 / RB-40 security-first\n * compose.\n *\n * @stable\n */\nexport function lastN(n = 10): DescribedFilter {\n if (!Number.isFinite(n) || n <= 0) {\n throw new RangeError(`filters.lastN: n must be a positive integer (got ${String(n)})`);\n }\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n for (const msg of history) {\n if (msg.role === 'system') {\n out.push(msg);\n }\n }\n const nonSystem: Message[] = history.filter((m) => m.role !== 'system');\n const tail = nonSystem.slice(Math.max(0, nonSystem.length - n));\n return [...out, ...tail];\n },\n { kind: 'last-n', meta: { n } },\n );\n}\n\n/**\n * Keep only the parent's system prompt and the most recent user\n * message. Useful for simple sub-agents that only need the question.\n *\n * @stable\n */\nexport function lastUser(): DescribedFilter {\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n let lastUserIdx = -1;\n for (let i = history.length - 1; i >= 0; i--) {\n const msg = history[i];\n if (msg && msg.role === 'user') {\n lastUserIdx = i;\n break;\n }\n }\n for (const msg of history) {\n if (msg.role === 'system') out.push(msg);\n }\n if (lastUserIdx >= 0) {\n const m = history[lastUserIdx];\n if (m) out.push(m);\n }\n return out;\n },\n { kind: 'last-user' },\n );\n}\n\n/**\n * The full unfiltered history. Discouraged — security-conscious\n * callers should pick {@link lastN} or {@link bySensitivity} instead\n * (a sub-agent rarely needs the parent's entire conversation).\n *\n * @stable\n */\nexport function full(): DescribedFilter {\n return makeFilter((history) => history.slice(), { kind: 'full' });\n}\n\n/**\n * Replace the parent's history with a single system message carrying\n * the supplied summary. Used by callers that wire in an LLM-based\n * summarizer outside the framework.\n *\n * @stable\n */\nexport function summary(text: string): DescribedFilter {\n return makeFilter(\n () => [\n {\n role: 'system',\n content: `[Summary of parent conversation]\\n${text}`,\n },\n ],\n { kind: 'summary', meta: { summaryLength: text.length } },\n );\n}\n\n/**\n * Drop messages whose effective sensitivity ceiling exceeds\n * `maxTier`. Messages without sensitivity metadata default to\n * `'public'` and are always kept.\n *\n * The framework currently records sensitivity at the\n * `MessageContent` part level via the `inboundTrust` / `secret`\n * annotations. v0.1 ships a coarse-grained heuristic: a message is\n * kept iff every text part's content does not contain the literal\n * `[REDACTED:secret]` token AND every part's annotated sensitivity\n * is acceptable to `maxTier`. Operators that need a stricter\n * filter compose the function with `stripSensitiveOutputs()` or a\n * custom predicate.\n *\n * @stable\n */\nexport function bySensitivity(args: { readonly maxTier?: Sensitivity } = {}): DescribedFilter {\n const maxTier: Sensitivity = args.maxTier ?? 'public';\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n for (const msg of history) {\n const content = msg.role === 'system' ? msg.content : msg.content;\n const text = typeof content === 'string' ? content : JSON.stringify(content);\n if (text.includes('[REDACTED:secret]') && !withinTier('secret', maxTier)) {\n continue;\n }\n out.push(msg);\n }\n return out;\n },\n { kind: 'sensitivity-filter', meta: { maxTier } },\n );\n}\n\n/**\n * Strip every `ReasoningContent` part from each message. Always\n * applied at the handoff boundary (the `compose(...)` helper appends\n * this filter automatically).\n *\n * @stable\n */\nexport function stripReasoning(): DescribedFilter {\n return makeFilter((history) => history.map(stripReasoningFromMessage), {\n kind: 'strip-reasoning',\n });\n}\n\n/**\n * Strip tool messages whose `content` carries the literal token\n * `[REDACTED:secret]` or whose `secret` annotation marks the body as\n * sensitive. Conservative-by-design: the agent runtime tags\n * sensitive tool outputs at session-write time so this filter has\n * stable bytes to scan against.\n *\n * @stable\n */\nexport function stripSensitiveOutputs(): DescribedFilter {\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n let stripped = 0;\n for (const msg of history) {\n if (msg.role === 'tool') {\n const text = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n if (text.includes('[REDACTED:')) {\n stripped += 1;\n continue;\n }\n }\n out.push(msg);\n }\n void stripped;\n return out;\n },\n { kind: 'strip-sensitive-outputs' },\n );\n}\n\n/**\n * Drop every assistant `toolCalls` array AND every `tool` message.\n * Useful when a sub-agent should only see the textual conversation.\n *\n * @stable\n */\nexport function stripToolCalls(): DescribedFilter {\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n for (const msg of history) {\n if (msg.role === 'tool') continue;\n if (msg.role === 'assistant' && msg.toolCalls && msg.toolCalls.length > 0) {\n const { toolCalls: _toolCalls, ...rest } = msg;\n out.push(rest);\n continue;\n }\n out.push(msg);\n }\n return out;\n },\n { kind: 'strip-tool-calls' },\n );\n}\n\n/**\n * Compose multiple filters left-to-right. The composer **always**\n * appends `stripReasoning()` at the end so reasoning content never\n * crosses a handoff boundary regardless of caller intent.\n *\n * @stable\n */\nexport function compose(...filters: ReadonlyArray<HandoffFilter>): DescribedFilter {\n const stripper = stripReasoning();\n const ordered: ReadonlyArray<HandoffFilter> = [...filters, stripper];\n const descriptors: HandoffInputFilterDescriptor[] = filters.map((f) => {\n const desc = (f as DescribedFilter).descriptor;\n return desc ?? { kind: 'custom' };\n });\n descriptors.push(stripper.descriptor);\n return makeFilter(\n (history) => {\n let current: readonly Message[] = history;\n for (const filter of ordered) {\n current = filter(current);\n }\n return current;\n },\n { kind: 'compose', meta: { steps: descriptors } },\n );\n}\n\n/**\n * Wrap a caller-supplied function as a {@link DescribedFilter} with\n * the canonical `'custom'` descriptor.\n *\n * @stable\n */\nexport function custom(\n fn: HandoffFilter,\n meta?: Readonly<Record<string, unknown>>,\n): DescribedFilter {\n return makeFilter(fn, meta !== undefined ? { kind: 'custom', meta } : { kind: 'custom' });\n}\n\n/**\n * The canonical default applied by the agent runtime to every\n * `Agent.toTool(...)` and `handoff(...)` invocation when the caller\n * does not supply an explicit filter.\n *\n * @stable\n */\nexport function defaultHandoffFilter(): DescribedFilter {\n return compose(lastN(10), stripSensitiveOutputs());\n}\n\n/**\n * Pure `HandoffInputFilterDescriptor` for callers that just need the\n * descriptor without instantiating the runtime function (e.g. the\n * sessions package's lenient-forward-parse path).\n *\n * @stable\n */\nexport const FILTER_KIND_CUSTOM: HandoffInputFilterDescriptor = { kind: 'custom' };\n\n/** Aggregate module export. */\nexport const filters = {\n lastN,\n lastUser,\n full,\n summary,\n bySensitivity,\n stripReasoning,\n stripSensitiveOutputs,\n stripToolCalls,\n compose,\n custom,\n defaultHandoffFilter,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["SENSITIVITY_RANK: Record<Sensitivity, number>","out: Message[]","nonSystem: Message[]","maxTier: Sensitivity","filters","ordered: ReadonlyArray<HandoffFilter>","descriptors: HandoffInputFilterDescriptor[]","current: readonly Message[]","FILTER_KIND_CUSTOM: HandoffInputFilterDescriptor"],"sources":["../../src/filters/index.ts"],"sourcesContent":["/**\n * Handoff filter library - a small set of pure, composable functions\n * that take the parent agent's message history and return a filtered\n * subset suitable for forwarding to a child agent.\n *\n * Every filter pairs a `HandoffFilter` runtime function with a\n * serializable {@link HandoffInputFilterDescriptor} so the JSONL\n * session export (`@graphorin/sessions`) can replay the filter stack\n * even after the runtime implementations evolve.\n *\n * Reasoning content is **always** stripped at the handoff boundary -\n * `filters.compose(...)` guarantees `stripReasoning()` runs last so a\n * caller-supplied filter cannot accidentally forward reasoning to a\n * child agent. This is the confidentiality + token-economy default\n * documented in the agent-loop reference.\n *\n * @packageDocumentation\n */\n\nimport type {\n HandoffFilter,\n HandoffInputFilterDescriptor,\n Message,\n Sensitivity,\n} from '@graphorin/core';\nimport { SENSITIVITY_ORDER } from '@graphorin/core';\n\nconst SENSITIVITY_RANK: Record<Sensitivity, number> = {\n public: 0,\n internal: 1,\n secret: 2,\n};\n\n/**\n * Return `true` iff the message's effective sensitivity is at most\n * `maxTier` (a strictly weaker check than core's\n * `acceptsSensitivity` because the filter library does not have\n * the provider's `acceptsSensitivity[]` array - only a ceiling).\n */\nfunction withinTier(record: Sensitivity, maxTier: Sensitivity): boolean {\n return SENSITIVITY_RANK[record] <= SENSITIVITY_RANK[maxTier];\n}\n\nvoid SENSITIVITY_ORDER;\n\n/**\n * A `HandoffFilter` paired with the serializable descriptor that\n * round-trips through the JSONL session export. Authors of custom\n * filters return one of these via `filters.custom({...})`.\n *\n * @stable\n */\nexport interface DescribedFilter extends HandoffFilter {\n readonly descriptor: HandoffInputFilterDescriptor;\n}\n\nfunction makeFilter(fn: HandoffFilter, descriptor: HandoffInputFilterDescriptor): DescribedFilter {\n const wrapped = ((history: readonly Message[]): readonly Message[] =>\n fn(history)) as DescribedFilter;\n Object.defineProperty(wrapped, 'descriptor', { value: descriptor, enumerable: true });\n return wrapped;\n}\n\nfunction isReasoningPart(part: unknown): boolean {\n if (typeof part !== 'object' || part === null) return false;\n const t = (part as { readonly type?: unknown }).type;\n return t === 'reasoning';\n}\n\nfunction stripReasoningFromMessage(msg: Message): Message {\n if (msg.role === 'system' || msg.role === 'tool') return msg;\n const content = msg.content;\n if (typeof content === 'string') return msg;\n const filtered = content.filter((part) => !isReasoningPart(part));\n if (filtered.length === content.length) return msg;\n if (msg.role === 'assistant') {\n return { ...msg, content: filtered };\n }\n return { ...msg, content: filtered };\n}\n\n/**\n * Keep the parent's system prompt and the last `n` non-system\n * messages. Default `n = 10` per DEC-146 / RB-40 security-first\n * compose.\n *\n * @stable\n */\nexport function lastN(n = 10): DescribedFilter {\n if (!Number.isFinite(n) || n <= 0) {\n throw new RangeError(`filters.lastN: n must be a positive integer (got ${String(n)})`);\n }\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n for (const msg of history) {\n if (msg.role === 'system') {\n out.push(msg);\n }\n }\n const nonSystem: Message[] = history.filter((m) => m.role !== 'system');\n const tail = nonSystem.slice(Math.max(0, nonSystem.length - n));\n return [...out, ...tail];\n },\n { kind: 'last-n', meta: { n } },\n );\n}\n\n/**\n * Keep only the parent's system prompt and the most recent user\n * message. Useful for simple sub-agents that only need the question.\n *\n * @stable\n */\nexport function lastUser(): DescribedFilter {\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n let lastUserIdx = -1;\n for (let i = history.length - 1; i >= 0; i--) {\n const msg = history[i];\n if (msg && msg.role === 'user') {\n lastUserIdx = i;\n break;\n }\n }\n for (const msg of history) {\n if (msg.role === 'system') out.push(msg);\n }\n if (lastUserIdx >= 0) {\n const m = history[lastUserIdx];\n if (m) out.push(m);\n }\n return out;\n },\n { kind: 'last-user' },\n );\n}\n\n/**\n * The full unfiltered history. Discouraged - security-conscious\n * callers should pick {@link lastN} or {@link bySensitivity} instead\n * (a sub-agent rarely needs the parent's entire conversation).\n *\n * @stable\n */\nexport function full(): DescribedFilter {\n return makeFilter((history) => history.slice(), { kind: 'full' });\n}\n\n/**\n * Replace the parent's history with a single system message carrying\n * the supplied summary. Used by callers that wire in an LLM-based\n * summarizer outside the framework.\n *\n * @stable\n */\nexport function summary(text: string): DescribedFilter {\n return makeFilter(\n () => [\n {\n role: 'system',\n content: `[Summary of parent conversation]\\n${text}`,\n },\n ],\n { kind: 'summary', meta: { summaryLength: text.length } },\n );\n}\n\n/**\n * Drop messages whose effective sensitivity ceiling exceeds\n * `maxTier`. Messages without sensitivity metadata default to\n * `'public'` and are always kept.\n *\n * The framework currently records sensitivity at the\n * `MessageContent` part level via the `inboundTrust` / `secret`\n * annotations. v0.1 ships a coarse-grained heuristic: a message is\n * kept iff every text part's content does not contain the literal\n * `[REDACTED:secret]` token AND every part's annotated sensitivity\n * is acceptable to `maxTier`. Operators that need a stricter\n * filter compose the function with `stripSensitiveOutputs()` or a\n * custom predicate.\n *\n * @stable\n */\nexport function bySensitivity(args: { readonly maxTier?: Sensitivity } = {}): DescribedFilter {\n const maxTier: Sensitivity = args.maxTier ?? 'public';\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n for (const msg of history) {\n const content = msg.role === 'system' ? msg.content : msg.content;\n const text = typeof content === 'string' ? content : JSON.stringify(content);\n if (text.includes('[REDACTED:secret]') && !withinTier('secret', maxTier)) {\n continue;\n }\n out.push(msg);\n }\n return out;\n },\n { kind: 'sensitivity-filter', meta: { maxTier } },\n );\n}\n\n/**\n * Strip every `ReasoningContent` part from each message. Always\n * applied at the handoff boundary (the `compose(...)` helper appends\n * this filter automatically).\n *\n * @stable\n */\nexport function stripReasoning(): DescribedFilter {\n return makeFilter((history) => history.map(stripReasoningFromMessage), {\n kind: 'strip-reasoning',\n });\n}\n\n/**\n * Strip tool messages whose `content` carries the literal token\n * `[REDACTED:secret]` or whose `secret` annotation marks the body as\n * sensitive. Conservative-by-design: the agent runtime tags\n * sensitive tool outputs at session-write time so this filter has\n * stable bytes to scan against.\n *\n * @stable\n */\nexport function stripSensitiveOutputs(): DescribedFilter {\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n let stripped = 0;\n for (const msg of history) {\n if (msg.role === 'tool') {\n const text = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n if (text.includes('[REDACTED:')) {\n stripped += 1;\n continue;\n }\n }\n out.push(msg);\n }\n void stripped;\n return out;\n },\n { kind: 'strip-sensitive-outputs' },\n );\n}\n\n/**\n * Drop every assistant `toolCalls` array AND every `tool` message.\n * Useful when a sub-agent should only see the textual conversation.\n *\n * @stable\n */\nexport function stripToolCalls(): DescribedFilter {\n return makeFilter(\n (history) => {\n const out: Message[] = [];\n for (const msg of history) {\n if (msg.role === 'tool') continue;\n if (msg.role === 'assistant' && msg.toolCalls && msg.toolCalls.length > 0) {\n const { toolCalls: _toolCalls, ...rest } = msg;\n out.push(rest);\n continue;\n }\n out.push(msg);\n }\n return out;\n },\n { kind: 'strip-tool-calls' },\n );\n}\n\n/**\n * Compose multiple filters left-to-right. The composer **always**\n * appends `stripReasoning()` at the end so reasoning content never\n * crosses a handoff boundary regardless of caller intent.\n *\n * @stable\n */\nexport function compose(...filters: ReadonlyArray<HandoffFilter>): DescribedFilter {\n const stripper = stripReasoning();\n const ordered: ReadonlyArray<HandoffFilter> = [...filters, stripper];\n const descriptors: HandoffInputFilterDescriptor[] = filters.map((f) => {\n const desc = (f as DescribedFilter).descriptor;\n return desc ?? { kind: 'custom' };\n });\n descriptors.push(stripper.descriptor);\n return makeFilter(\n (history) => {\n let current: readonly Message[] = history;\n for (const filter of ordered) {\n current = filter(current);\n }\n return current;\n },\n { kind: 'compose', meta: { steps: descriptors } },\n );\n}\n\n/**\n * Wrap a caller-supplied function as a {@link DescribedFilter} with\n * the canonical `'custom'` descriptor.\n *\n * @stable\n */\nexport function custom(\n fn: HandoffFilter,\n meta?: Readonly<Record<string, unknown>>,\n): DescribedFilter {\n return makeFilter(fn, meta !== undefined ? { kind: 'custom', meta } : { kind: 'custom' });\n}\n\n/**\n * The canonical default applied by the agent runtime to every\n * `Agent.toTool(...)` and `handoff(...)` invocation when the caller\n * does not supply an explicit filter.\n *\n * @stable\n */\nexport function defaultHandoffFilter(): DescribedFilter {\n return compose(lastN(10), stripSensitiveOutputs());\n}\n\n/**\n * Pure `HandoffInputFilterDescriptor` for callers that just need the\n * descriptor without instantiating the runtime function (e.g. the\n * sessions package's lenient-forward-parse path).\n *\n * @stable\n */\nexport const FILTER_KIND_CUSTOM: HandoffInputFilterDescriptor = { kind: 'custom' };\n\n/** Aggregate module export. */\nexport const filters = {\n lastN,\n lastUser,\n full,\n summary,\n bySensitivity,\n stripReasoning,\n stripSensitiveOutputs,\n stripToolCalls,\n compose,\n custom,\n defaultHandoffFilter,\n};\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,19 +5,20 @@ import { ChildTrustInput, ContentOriginKind, MergeBiasDecision, MergeGuardConfig
|
|
|
5
5
|
import { ChildResult, FanOutOptions, FanOutResult, MergeStrategy, PerChildBudget, runFanOut } from "./fanout/index.js";
|
|
6
6
|
import { CausalityMonitor, CausalityMonitorCheck, CausalityMonitorConfig, CausalityMonitorStrictness, DEFAULT_DENIAL_PATTERNS, DEFAULT_MAX_CHAIN_DEPTH } from "./lateral-leak/causality-monitor.js";
|
|
7
7
|
import { ProgressIO, ProgressIOConfig, ProgressReadOptions, ProgressWriteOptions, createProgressIO } from "./progress/index.js";
|
|
8
|
-
import { AbortOptions, Agent, AgentCallOptions, AgentConfig, AgentInput, AgentToToolOptions, CompactOptions, CompactionApiResult, OutputSpec, PostCompactionHook, PrepareStepHook, PrepareStepOverrides, ResumeDirective, SkillsRegistryLike } from "./types.js";
|
|
8
|
+
import { AbortOptions, Agent, AgentCallOptions, AgentCapability, AgentConfig, AgentInput, AgentToToolOptions, CompactOptions, CompactionApiResult, OutputSpec, PostCompactionHook, PrepareStepHook, PrepareStepOverrides, ResponseVerifier, ResumeDirective, SkillsRegistryLike, VerifierResult } from "./types.js";
|
|
9
9
|
import { createAgent } from "./factory.js";
|
|
10
10
|
import { DescribedFilter, FILTER_KIND_CUSTOM, bySensitivity, compose, custom, defaultHandoffFilter, filters, full, lastN, lastUser, stripReasoning, stripSensitiveOutputs, stripToolCalls, summary } from "./filters/index.js";
|
|
11
11
|
import { GuardOutcome, ProtocolBoundary, ProtocolEscapePolicy, ProtocolGuardConfig, guardOutboundContent, resolvePolicy } from "./lateral-leak/protocol-guard.js";
|
|
12
12
|
import "./lateral-leak/index.js";
|
|
13
13
|
import { PreferredModelResolution, ResolvePreferredModelInput, pickTopTierAcrossTools, resolvePreferredModel } from "./preferred-model/index.js";
|
|
14
14
|
import { RUN_STATE_SCHEMA_MAJOR_SUPPORTED, RUN_STATE_SCHEMA_VERSION, SerializeRunStateOptions, SerializedRunState, addModelUsage, aggregateUsageFromByModel, completedToolCallsFromState, createInitialRunState, deserializeRunState, runStateFromJSON, runStateToJSON, serializeRunState } from "./run-state/index.js";
|
|
15
|
+
import { ReplayProviderOptions, createReplayProvider } from "./testing/replay-provider.js";
|
|
15
16
|
import { GuardrailContext, GuardrailDefinition, GuardrailResult, InputGuardrail, OutputGuardrail } from "@graphorin/security/guardrails";
|
|
16
17
|
|
|
17
18
|
//#region src/index.d.ts
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
|
-
* `@graphorin/agent`
|
|
21
|
+
* `@graphorin/agent` - agent runtime for the Graphorin framework.
|
|
21
22
|
*
|
|
22
23
|
* The package owns:
|
|
23
24
|
*
|
|
@@ -44,8 +45,7 @@ import { GuardrailContext, GuardrailDefinition, GuardrailResult, InputGuardrail,
|
|
|
44
45
|
*
|
|
45
46
|
* @packageDocumentation
|
|
46
47
|
*/
|
|
47
|
-
|
|
48
|
-
declare const VERSION = "0.5.0";
|
|
48
|
+
declare const VERSION: string;
|
|
49
49
|
//#endregion
|
|
50
|
-
export { type AbortOptions, type Agent, type AgentCallOptions, type AgentConfig, type AgentFallbackEligibility, type AgentFallbackPolicy, type AgentFallbackReason, type AgentInput, AgentResolutionError, AgentRuntimeError, type AgentRuntimeErrorCode, type AgentToToolOptions, CausalityMonitor, type CausalityMonitorCheck, type CausalityMonitorConfig, type CausalityMonitorStrictness, type ChildResult, type ChildTrustInput, type CompactOptions, type CompactionApiResult, type ContentOriginKind, DEFAULT_DENIAL_PATTERNS, DEFAULT_MAX_CHAIN_DEPTH, type DescribedFilter, type EvaluatorCallable, EvaluatorOptimizerConfigError, type EvaluatorOptimizerOptions, type EvaluatorOptimizerOutcome, type EvaluatorOutcome, FILTER_KIND_CUSTOM, type FanOutOptions, type FanOutResult, type GeneratorCallable, type GuardOutcome, type GuardrailContext, type GuardrailDefinition, type GuardrailResult, type InputGuardrail, InvalidAgentConfigError, InvalidPreferredModelError, type MergeBiasDecision, MergeBlockedError, type MergeGuardConfig, type MergeStrategy, MultipleHandoffsInStepError, type OutputGuardrail, type OutputSpec, type PerChildBudget, type PostCompactionHook, type PreferredModelResolution, type PrepareStepHook, type PrepareStepOverrides, type ProgressIO, type ProgressIOConfig, type ProgressReadOptions, ProgressWriteError, type ProgressWriteOptions, type ProtocolBoundary, type ProtocolEscapePolicy, type ProtocolGuardConfig, ProtocolInjectionRejectError, ProviderMiddlewareOrderError, RUN_STATE_SCHEMA_MAJOR_SUPPORTED, RUN_STATE_SCHEMA_VERSION, type ResolvePreferredModelInput, type ResumeDirective, type Rubric, RunStateMalformedError, RunStateVersionUnsupportedError, type SerializeRunStateOptions, type SerializedRunState, type SkillsRegistryLike, ToolNotFoundError, type TrustClass, VERSION, addModelUsage, aggregateUsageFromByModel, bySensitivity, completedToolCallsFromState, compose, computeSourceTrust, createAgent, createInitialRunState, createProgressIO, custom, defaultHandoffFilter, deserializeRunState, evaluateMerge, evaluatorOptimizer, filters, full, guardOutboundContent, isAgentFallbackEligible, lastN, lastUser, pickTopTierAcrossTools, resolvePolicy, resolvePreferredModel, runFanOut, runStateFromJSON, runStateToJSON, serializeRunState, stripReasoning, stripSensitiveOutputs, stripToolCalls, summary };
|
|
50
|
+
export { type AbortOptions, type Agent, type AgentCallOptions, type AgentCapability, type AgentConfig, type AgentFallbackEligibility, type AgentFallbackPolicy, type AgentFallbackReason, type AgentInput, AgentResolutionError, AgentRuntimeError, type AgentRuntimeErrorCode, type AgentToToolOptions, CausalityMonitor, type CausalityMonitorCheck, type CausalityMonitorConfig, type CausalityMonitorStrictness, type ChildResult, type ChildTrustInput, type CompactOptions, type CompactionApiResult, type ContentOriginKind, DEFAULT_DENIAL_PATTERNS, DEFAULT_MAX_CHAIN_DEPTH, type DescribedFilter, type EvaluatorCallable, EvaluatorOptimizerConfigError, type EvaluatorOptimizerOptions, type EvaluatorOptimizerOutcome, type EvaluatorOutcome, FILTER_KIND_CUSTOM, type FanOutOptions, type FanOutResult, type GeneratorCallable, type GuardOutcome, type GuardrailContext, type GuardrailDefinition, type GuardrailResult, type InputGuardrail, InvalidAgentConfigError, InvalidPreferredModelError, type MergeBiasDecision, MergeBlockedError, type MergeGuardConfig, type MergeStrategy, MultipleHandoffsInStepError, type OutputGuardrail, type OutputSpec, type PerChildBudget, type PostCompactionHook, type PreferredModelResolution, type PrepareStepHook, type PrepareStepOverrides, type ProgressIO, type ProgressIOConfig, type ProgressReadOptions, ProgressWriteError, type ProgressWriteOptions, type ProtocolBoundary, type ProtocolEscapePolicy, type ProtocolGuardConfig, ProtocolInjectionRejectError, ProviderMiddlewareOrderError, RUN_STATE_SCHEMA_MAJOR_SUPPORTED, RUN_STATE_SCHEMA_VERSION, type ReplayProviderOptions, type ResolvePreferredModelInput, type ResponseVerifier, type ResumeDirective, type Rubric, RunStateMalformedError, RunStateVersionUnsupportedError, type SerializeRunStateOptions, type SerializedRunState, type SkillsRegistryLike, ToolNotFoundError, type TrustClass, VERSION, type VerifierResult, addModelUsage, aggregateUsageFromByModel, bySensitivity, completedToolCallsFromState, compose, computeSourceTrust, createAgent, createInitialRunState, createProgressIO, createReplayProvider, custom, defaultHandoffFilter, deserializeRunState, evaluateMerge, evaluatorOptimizer, filters, full, guardOutboundContent, isAgentFallbackEligible, lastN, lastUser, pickTopTierAcrossTools, resolvePolicy, resolvePreferredModel, runFanOut, runStateFromJSON, runStateToJSON, serializeRunState, stripReasoning, stripSensitiveOutputs, stripToolCalls, summary };
|
|
51
51
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA;;;;;;;;;;cAAa"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { version } from "./package.js";
|
|
1
2
|
import { AgentResolutionError, AgentRuntimeError, EvaluatorOptimizerConfigError, InvalidAgentConfigError, InvalidPreferredModelError, MergeBlockedError, MultipleHandoffsInStepError, ProgressWriteError, ProtocolInjectionRejectError, ProviderMiddlewareOrderError, RunStateMalformedError, RunStateVersionUnsupportedError, ToolNotFoundError } from "./errors/index.js";
|
|
2
3
|
import { evaluatorOptimizer } from "./evaluator-optimizer/index.js";
|
|
3
4
|
import { isAgentFallbackEligible } from "./fallback/index.js";
|
|
@@ -11,10 +12,11 @@ import { RUN_STATE_SCHEMA_MAJOR_SUPPORTED, RUN_STATE_SCHEMA_VERSION, addModelUsa
|
|
|
11
12
|
import { createAgent } from "./factory.js";
|
|
12
13
|
import { guardOutboundContent, resolvePolicy } from "./lateral-leak/protocol-guard.js";
|
|
13
14
|
import "./lateral-leak/index.js";
|
|
15
|
+
import { createReplayProvider } from "./testing/replay-provider.js";
|
|
14
16
|
|
|
15
17
|
//#region src/index.ts
|
|
16
18
|
/**
|
|
17
|
-
* `@graphorin/agent`
|
|
19
|
+
* `@graphorin/agent` - agent runtime for the Graphorin framework.
|
|
18
20
|
*
|
|
19
21
|
* The package owns:
|
|
20
22
|
*
|
|
@@ -41,9 +43,9 @@ import "./lateral-leak/index.js";
|
|
|
41
43
|
*
|
|
42
44
|
* @packageDocumentation
|
|
43
45
|
*/
|
|
44
|
-
/** Canonical version constant
|
|
45
|
-
const VERSION =
|
|
46
|
+
/** Canonical version constant, derived from `package.json` at build time. */
|
|
47
|
+
const VERSION = version;
|
|
46
48
|
|
|
47
49
|
//#endregion
|
|
48
|
-
export { AgentResolutionError, AgentRuntimeError, CausalityMonitor, DEFAULT_DENIAL_PATTERNS, DEFAULT_MAX_CHAIN_DEPTH, EvaluatorOptimizerConfigError, FILTER_KIND_CUSTOM, InvalidAgentConfigError, InvalidPreferredModelError, MergeBlockedError, MultipleHandoffsInStepError, ProgressWriteError, ProtocolInjectionRejectError, ProviderMiddlewareOrderError, RUN_STATE_SCHEMA_MAJOR_SUPPORTED, RUN_STATE_SCHEMA_VERSION, RunStateMalformedError, RunStateVersionUnsupportedError, ToolNotFoundError, VERSION, addModelUsage, aggregateUsageFromByModel, bySensitivity, completedToolCallsFromState, compose, computeSourceTrust, createAgent, createInitialRunState, createProgressIO, custom, defaultHandoffFilter, deserializeRunState, evaluateMerge, evaluatorOptimizer, filters, full, guardOutboundContent, isAgentFallbackEligible, lastN, lastUser, pickTopTierAcrossTools, resolvePolicy, resolvePreferredModel, runFanOut, runStateFromJSON, runStateToJSON, serializeRunState, stripReasoning, stripSensitiveOutputs, stripToolCalls, summary };
|
|
50
|
+
export { AgentResolutionError, AgentRuntimeError, CausalityMonitor, DEFAULT_DENIAL_PATTERNS, DEFAULT_MAX_CHAIN_DEPTH, EvaluatorOptimizerConfigError, FILTER_KIND_CUSTOM, InvalidAgentConfigError, InvalidPreferredModelError, MergeBlockedError, MultipleHandoffsInStepError, ProgressWriteError, ProtocolInjectionRejectError, ProviderMiddlewareOrderError, RUN_STATE_SCHEMA_MAJOR_SUPPORTED, RUN_STATE_SCHEMA_VERSION, RunStateMalformedError, RunStateVersionUnsupportedError, ToolNotFoundError, VERSION, addModelUsage, aggregateUsageFromByModel, bySensitivity, completedToolCallsFromState, compose, computeSourceTrust, createAgent, createInitialRunState, createProgressIO, createReplayProvider, custom, defaultHandoffFilter, deserializeRunState, evaluateMerge, evaluatorOptimizer, filters, full, guardOutboundContent, isAgentFallbackEligible, lastN, lastUser, pickTopTierAcrossTools, resolvePolicy, resolvePreferredModel, runFanOut, runStateFromJSON, runStateToJSON, serializeRunState, stripReasoning, stripSensitiveOutputs, stripToolCalls, summary };
|
|
49
51
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/agent`
|
|
1
|
+
{"version":3,"file":"index.js","names":["VERSION: string","pkg.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/agent` - agent runtime for the Graphorin framework.\n *\n * The package owns:\n *\n * - The `createAgent({...})` factory that wires the typed\n * `model -> tool calls -> model` loop, the streaming event\n * surface, the steering / followUp queues, durable HITL\n * approvals, multi-agent handoffs (`Agent.toTool`, the filter\n * library, secrets isolation), the agent-level model fallback\n * chain, the per-step compaction lifecycle, the per-tool\n * preferred-model resolution, the structured progress-artifact\n * APIs, and the lateral-leak defense layer.\n * - `runStateToJSON / runStateFromJSON` helpers for caller-managed\n * durable HITL.\n * - The handoff filter library.\n * - The agent-step-level fan-out helpers (`runFanOut`,\n * `evaluatorOptimizer`, the progress IO surface).\n * - Pure decision functions consulted by the loop\n * (`isAgentFallbackEligible`, `resolvePreferredModel`).\n * - Lateral-leak primitives (`CausalityMonitor`,\n * `MergeAgentSidewaysInjectionGuard` decision helpers,\n * protocol-injection guard).\n *\n * The full documentation lives in the package `README.md`.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant, derived from `package.json` at build time. */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n\n// AG-2 / SDF-4: the canonical guardrail contract lives in\n// `@graphorin/security`; re-exported here for config ergonomics.\nexport type {\n GuardrailContext,\n GuardrailDefinition,\n GuardrailResult,\n InputGuardrail,\n OutputGuardrail,\n} from '@graphorin/security/guardrails';\nexport {\n AgentResolutionError,\n AgentRuntimeError,\n type AgentRuntimeErrorCode,\n EvaluatorOptimizerConfigError,\n InvalidAgentConfigError,\n InvalidPreferredModelError,\n MergeBlockedError,\n MultipleHandoffsInStepError,\n ProgressWriteError,\n ProtocolInjectionRejectError,\n ProviderMiddlewareOrderError,\n RunStateMalformedError,\n RunStateVersionUnsupportedError,\n ToolNotFoundError,\n} from './errors/index.js';\nexport {\n type EvaluatorCallable,\n type EvaluatorOptimizerOptions,\n type EvaluatorOptimizerOutcome,\n type EvaluatorOutcome,\n evaluatorOptimizer,\n type GeneratorCallable,\n type Rubric,\n} from './evaluator-optimizer/index.js';\nexport { createAgent } from './factory.js';\nexport {\n type AgentFallbackEligibility,\n type AgentFallbackPolicy,\n type AgentFallbackReason,\n isAgentFallbackEligible,\n} from './fallback/index.js';\nexport {\n type ChildResult,\n type FanOutOptions,\n type FanOutResult,\n type MergeStrategy,\n type PerChildBudget,\n runFanOut,\n} from './fanout/index.js';\nexport {\n bySensitivity,\n compose,\n custom,\n type DescribedFilter,\n defaultHandoffFilter,\n FILTER_KIND_CUSTOM,\n filters,\n full,\n lastN,\n lastUser,\n stripReasoning,\n stripSensitiveOutputs,\n stripToolCalls,\n summary,\n} from './filters/index.js';\nexport {\n CausalityMonitor,\n type CausalityMonitorCheck,\n type CausalityMonitorConfig,\n type CausalityMonitorStrictness,\n type ChildTrustInput,\n type ContentOriginKind,\n computeSourceTrust,\n DEFAULT_DENIAL_PATTERNS,\n DEFAULT_MAX_CHAIN_DEPTH,\n evaluateMerge,\n type GuardOutcome,\n guardOutboundContent,\n type MergeBiasDecision,\n type MergeGuardConfig,\n type ProtocolBoundary,\n type ProtocolEscapePolicy,\n type ProtocolGuardConfig,\n resolvePolicy,\n type TrustClass,\n} from './lateral-leak/index.js';\nexport {\n type PreferredModelResolution,\n pickTopTierAcrossTools,\n type ResolvePreferredModelInput,\n resolvePreferredModel,\n} from './preferred-model/index.js';\nexport {\n createProgressIO,\n type ProgressIO,\n type ProgressIOConfig,\n type ProgressReadOptions,\n type ProgressWriteOptions,\n} from './progress/index.js';\nexport {\n addModelUsage,\n aggregateUsageFromByModel,\n completedToolCallsFromState,\n createInitialRunState,\n deserializeRunState,\n RUN_STATE_SCHEMA_MAJOR_SUPPORTED,\n RUN_STATE_SCHEMA_VERSION,\n runStateFromJSON,\n runStateToJSON,\n type SerializedRunState,\n type SerializeRunStateOptions,\n serializeRunState,\n} from './run-state/index.js';\nexport {\n createReplayProvider,\n type ReplayProviderOptions,\n} from './testing/replay-provider.js';\nexport type {\n AbortOptions,\n Agent,\n AgentCallOptions,\n AgentCapability,\n AgentConfig,\n AgentInput,\n AgentToToolOptions,\n CompactionApiResult,\n CompactOptions,\n OutputSpec,\n PostCompactionHook,\n PrepareStepHook,\n PrepareStepOverrides,\n ResponseVerifier,\n ResumeDirective,\n SkillsRegistryLike,\n VerifierResult,\n} from './types.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAaA,UAAkBC"}
|
package/dist/internal/ids.js
CHANGED
|
@@ -16,7 +16,7 @@ function pad(value, width) {
|
|
|
16
16
|
* 5 random bytes (40 bits) and consume 5 bits per emitted character
|
|
17
17
|
* MSB-first; the leftover bits are discarded. Sourcing from
|
|
18
18
|
* `randomBytes` (uniform over `[0, 256)`) guarantees a uniform
|
|
19
|
-
* distribution over `ALPHABET`
|
|
19
|
+
* distribution over `ALPHABET` - no modulo bias.
|
|
20
20
|
*/
|
|
21
21
|
function randomTail() {
|
|
22
22
|
const buf = randomBytes(5);
|
package/dist/internal/ids.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ids.js","names":[],"sources":["../../src/internal/ids.ts"],"sourcesContent":["/**\n * Tiny id helpers used by the agent runtime. URL-safe Base32 alphabet,\n * monotonic timestamp prefix to keep ids loosely sortable.\n *\n * @packageDocumentation\n */\n\nimport { randomBytes } from 'node:crypto';\n\nconst ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\n\nfunction pad(value: string, width: number): string {\n return value.padStart(width, '0');\n}\n\n/**\n * 7-character Base32 tail backed by 35 bits of CSPRNG entropy. We read\n * 5 random bytes (40 bits) and consume 5 bits per emitted character\n * MSB-first; the leftover bits are discarded. Sourcing from\n * `randomBytes` (uniform over `[0, 256)`) guarantees a uniform\n * distribution over `ALPHABET`
|
|
1
|
+
{"version":3,"file":"ids.js","names":[],"sources":["../../src/internal/ids.ts"],"sourcesContent":["/**\n * Tiny id helpers used by the agent runtime. URL-safe Base32 alphabet,\n * monotonic timestamp prefix to keep ids loosely sortable.\n *\n * @packageDocumentation\n */\n\nimport { randomBytes } from 'node:crypto';\n\nconst ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\n\nfunction pad(value: string, width: number): string {\n return value.padStart(width, '0');\n}\n\n/**\n * 7-character Base32 tail backed by 35 bits of CSPRNG entropy. We read\n * 5 random bytes (40 bits) and consume 5 bits per emitted character\n * MSB-first; the leftover bits are discarded. Sourcing from\n * `randomBytes` (uniform over `[0, 256)`) guarantees a uniform\n * distribution over `ALPHABET` - no modulo bias.\n */\nfunction randomTail(): string {\n const buf = randomBytes(5);\n let bitBuffer = 0;\n let bitCount = 0;\n let cursor = 0;\n let out = '';\n for (let i = 0; i < 7; i++) {\n while (bitCount < 5) {\n bitBuffer = (bitBuffer << 8) | (buf[cursor++] ?? 0);\n bitCount += 8;\n }\n bitCount -= 5;\n const idx = (bitBuffer >>> bitCount) & 0x1f;\n out += ALPHABET[idx];\n }\n return out;\n}\n\n/** Generate a fresh, sortable, URL-safe identifier. */\nexport function newId(prefix?: string): string {\n const ts = pad(Date.now().toString(32).toUpperCase(), 9);\n const id = `${ts}${randomTail()}`;\n return prefix ? `${prefix}_${id}` : id;\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,WAAW;AAEjB,SAAS,IAAI,OAAe,OAAuB;AACjD,QAAO,MAAM,SAAS,OAAO,IAAI;;;;;;;;;AAUnC,SAAS,aAAqB;CAC5B,MAAM,MAAM,YAAY,EAAE;CAC1B,IAAI,YAAY;CAChB,IAAI,WAAW;CACf,IAAI,SAAS;CACb,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,SAAO,WAAW,GAAG;AACnB,eAAa,aAAa,KAAM,IAAI,aAAa;AACjD,eAAY;;AAEd,cAAY;EACZ,MAAM,MAAO,cAAc,WAAY;AACvC,SAAO,SAAS;;AAElB,QAAO;;;AAIT,SAAgB,MAAM,QAAyB;CAE7C,MAAM,KAAK,GADA,IAAI,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,aAAa,EAAE,EAAE,GACrC,YAAY;AAC/B,QAAO,SAAS,GAAG,OAAO,GAAG,OAAO"}
|
|
@@ -21,6 +21,8 @@ var InMemoryUsageAccumulator = class {
|
|
|
21
21
|
totalTokens: usage.totalTokens,
|
|
22
22
|
callCount: 1,
|
|
23
23
|
...usage.reasoningTokens !== void 0 ? { reasoningTokens: usage.reasoningTokens } : {},
|
|
24
|
+
...usage.cachedReadTokens !== void 0 ? { cachedReadTokens: usage.cachedReadTokens } : {},
|
|
25
|
+
...usage.cacheWriteTokens !== void 0 ? { cacheWriteTokens: usage.cacheWriteTokens } : {},
|
|
24
26
|
...usage.cost !== void 0 ? { cost: usage.cost } : {}
|
|
25
27
|
});
|
|
26
28
|
else {
|
|
@@ -30,7 +32,9 @@ var InMemoryUsageAccumulator = class {
|
|
|
30
32
|
completionTokens: prev.completionTokens + usage.completionTokens,
|
|
31
33
|
totalTokens: prev.totalTokens + usage.totalTokens,
|
|
32
34
|
callCount: prev.callCount + 1,
|
|
33
|
-
...
|
|
35
|
+
...sumOptional("reasoningTokens", prev.reasoningTokens, usage.reasoningTokens),
|
|
36
|
+
...sumOptional("cachedReadTokens", prev.cachedReadTokens, usage.cachedReadTokens),
|
|
37
|
+
...sumOptional("cacheWriteTokens", prev.cacheWriteTokens, usage.cacheWriteTokens)
|
|
34
38
|
};
|
|
35
39
|
this.#byModel.set(modelId, merged);
|
|
36
40
|
}
|
|
@@ -38,7 +42,9 @@ var InMemoryUsageAccumulator = class {
|
|
|
38
42
|
promptTokens: this.#aggregate.promptTokens + usage.promptTokens,
|
|
39
43
|
completionTokens: this.#aggregate.completionTokens + usage.completionTokens,
|
|
40
44
|
totalTokens: this.#aggregate.totalTokens + usage.totalTokens,
|
|
41
|
-
...
|
|
45
|
+
...sumOptional("reasoningTokens", this.#aggregate.reasoningTokens, usage.reasoningTokens),
|
|
46
|
+
...sumOptional("cachedReadTokens", this.#aggregate.cachedReadTokens, usage.cachedReadTokens),
|
|
47
|
+
...sumOptional("cacheWriteTokens", this.#aggregate.cacheWriteTokens, usage.cacheWriteTokens)
|
|
42
48
|
};
|
|
43
49
|
}
|
|
44
50
|
reset() {
|
|
@@ -56,6 +62,15 @@ var InMemoryUsageAccumulator = class {
|
|
|
56
62
|
};
|
|
57
63
|
}
|
|
58
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* Sum an optional token field: present in the output only when at least
|
|
67
|
+
* one side carries it (so a run that never saw the field keeps the same
|
|
68
|
+
* serialized shape as before the field existed).
|
|
69
|
+
*/
|
|
70
|
+
function sumOptional(key, a, b) {
|
|
71
|
+
if (a === void 0 && b === void 0) return {};
|
|
72
|
+
return { [key]: (a ?? 0) + (b ?? 0) };
|
|
73
|
+
}
|
|
59
74
|
|
|
60
75
|
//#endregion
|
|
61
76
|
export { InMemoryUsageAccumulator };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-accumulator.js","names":["#byModel","#aggregate","merged: ModelUsage"],"sources":["../../src/internal/usage-accumulator.ts"],"sourcesContent":["/**\n * Minimal {@link UsageAccumulator} implementation used by the agent\n * runtime when the consumer does not pass a custom one through\n * `RunContext.usage`. Tracks per-model breakdown and a running\n * aggregate.\n *\n * @packageDocumentation\n */\n\nimport type { ModelUsage, Usage, UsageAccumulator, UsageSnapshot } from '@graphorin/core';\n\nexport class InMemoryUsageAccumulator implements UsageAccumulator {\n #aggregate: Usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n readonly #byModel = new Map<string, ModelUsage>();\n\n get total(): Usage {\n return this.#aggregate;\n }\n\n get byModel(): ReadonlyMap<string, ModelUsage> {\n return this.#byModel;\n }\n\n add(modelId: string, usage: Usage): void {\n const prev = this.#byModel.get(modelId);\n if (prev === undefined) {\n this.#byModel.set(modelId, {\n modelId,\n promptTokens: usage.promptTokens,\n completionTokens: usage.completionTokens,\n totalTokens: usage.totalTokens,\n callCount: 1,\n ...(usage.reasoningTokens !== undefined ? { reasoningTokens: usage.reasoningTokens } : {}),\n ...(usage.cost !== undefined ? { cost: usage.cost } : {}),\n });\n } else {\n const merged: ModelUsage = {\n modelId,\n promptTokens: prev.promptTokens + usage.promptTokens,\n completionTokens: prev.completionTokens + usage.completionTokens,\n totalTokens: prev.totalTokens + usage.totalTokens,\n callCount: prev.callCount + 1,\n ...(
|
|
1
|
+
{"version":3,"file":"usage-accumulator.js","names":["#byModel","#aggregate","merged: ModelUsage"],"sources":["../../src/internal/usage-accumulator.ts"],"sourcesContent":["/**\n * Minimal {@link UsageAccumulator} implementation used by the agent\n * runtime when the consumer does not pass a custom one through\n * `RunContext.usage`. Tracks per-model breakdown and a running\n * aggregate.\n *\n * @packageDocumentation\n */\n\nimport type { ModelUsage, Usage, UsageAccumulator, UsageSnapshot } from '@graphorin/core';\n\nexport class InMemoryUsageAccumulator implements UsageAccumulator {\n #aggregate: Usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n readonly #byModel = new Map<string, ModelUsage>();\n\n get total(): Usage {\n return this.#aggregate;\n }\n\n get byModel(): ReadonlyMap<string, ModelUsage> {\n return this.#byModel;\n }\n\n add(modelId: string, usage: Usage): void {\n const prev = this.#byModel.get(modelId);\n if (prev === undefined) {\n this.#byModel.set(modelId, {\n modelId,\n promptTokens: usage.promptTokens,\n completionTokens: usage.completionTokens,\n totalTokens: usage.totalTokens,\n callCount: 1,\n ...(usage.reasoningTokens !== undefined ? { reasoningTokens: usage.reasoningTokens } : {}),\n ...(usage.cachedReadTokens !== undefined\n ? { cachedReadTokens: usage.cachedReadTokens }\n : {}),\n ...(usage.cacheWriteTokens !== undefined\n ? { cacheWriteTokens: usage.cacheWriteTokens }\n : {}),\n ...(usage.cost !== undefined ? { cost: usage.cost } : {}),\n });\n } else {\n const merged: ModelUsage = {\n modelId,\n promptTokens: prev.promptTokens + usage.promptTokens,\n completionTokens: prev.completionTokens + usage.completionTokens,\n totalTokens: prev.totalTokens + usage.totalTokens,\n callCount: prev.callCount + 1,\n ...sumOptional('reasoningTokens', prev.reasoningTokens, usage.reasoningTokens),\n ...sumOptional('cachedReadTokens', prev.cachedReadTokens, usage.cachedReadTokens),\n ...sumOptional('cacheWriteTokens', prev.cacheWriteTokens, usage.cacheWriteTokens),\n };\n this.#byModel.set(modelId, merged);\n }\n this.#aggregate = {\n promptTokens: this.#aggregate.promptTokens + usage.promptTokens,\n completionTokens: this.#aggregate.completionTokens + usage.completionTokens,\n totalTokens: this.#aggregate.totalTokens + usage.totalTokens,\n ...sumOptional('reasoningTokens', this.#aggregate.reasoningTokens, usage.reasoningTokens),\n ...sumOptional('cachedReadTokens', this.#aggregate.cachedReadTokens, usage.cachedReadTokens),\n ...sumOptional('cacheWriteTokens', this.#aggregate.cacheWriteTokens, usage.cacheWriteTokens),\n };\n }\n\n reset(): void {\n this.#aggregate = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n this.#byModel.clear();\n }\n\n snapshot(): UsageSnapshot {\n return {\n total: { ...this.#aggregate },\n byModel: Array.from(this.#byModel.values()).map((m) => ({ ...m })),\n };\n }\n}\n\n/**\n * Sum an optional token field: present in the output only when at least\n * one side carries it (so a run that never saw the field keeps the same\n * serialized shape as before the field existed).\n */\nfunction sumOptional<K extends string>(\n key: K,\n a: number | undefined,\n b: number | undefined,\n): Partial<Record<K, number>> {\n if (a === undefined && b === undefined) return {};\n return { [key]: (a ?? 0) + (b ?? 0) } as Partial<Record<K, number>>;\n}\n"],"mappings":";AAWA,IAAa,2BAAb,MAAkE;CAChE,aAAoB;EAAE,cAAc;EAAG,kBAAkB;EAAG,aAAa;EAAG;CAC5E,CAASA,0BAAW,IAAI,KAAyB;CAEjD,IAAI,QAAe;AACjB,SAAO,MAAKC;;CAGd,IAAI,UAA2C;AAC7C,SAAO,MAAKD;;CAGd,IAAI,SAAiB,OAAoB;EACvC,MAAM,OAAO,MAAKA,QAAS,IAAI,QAAQ;AACvC,MAAI,SAAS,OACX,OAAKA,QAAS,IAAI,SAAS;GACzB;GACA,cAAc,MAAM;GACpB,kBAAkB,MAAM;GACxB,aAAa,MAAM;GACnB,WAAW;GACX,GAAI,MAAM,oBAAoB,SAAY,EAAE,iBAAiB,MAAM,iBAAiB,GAAG,EAAE;GACzF,GAAI,MAAM,qBAAqB,SAC3B,EAAE,kBAAkB,MAAM,kBAAkB,GAC5C,EAAE;GACN,GAAI,MAAM,qBAAqB,SAC3B,EAAE,kBAAkB,MAAM,kBAAkB,GAC5C,EAAE;GACN,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,MAAM,MAAM,GAAG,EAAE;GACzD,CAAC;OACG;GACL,MAAME,SAAqB;IACzB;IACA,cAAc,KAAK,eAAe,MAAM;IACxC,kBAAkB,KAAK,mBAAmB,MAAM;IAChD,aAAa,KAAK,cAAc,MAAM;IACtC,WAAW,KAAK,YAAY;IAC5B,GAAG,YAAY,mBAAmB,KAAK,iBAAiB,MAAM,gBAAgB;IAC9E,GAAG,YAAY,oBAAoB,KAAK,kBAAkB,MAAM,iBAAiB;IACjF,GAAG,YAAY,oBAAoB,KAAK,kBAAkB,MAAM,iBAAiB;IAClF;AACD,SAAKF,QAAS,IAAI,SAAS,OAAO;;AAEpC,QAAKC,YAAa;GAChB,cAAc,MAAKA,UAAW,eAAe,MAAM;GACnD,kBAAkB,MAAKA,UAAW,mBAAmB,MAAM;GAC3D,aAAa,MAAKA,UAAW,cAAc,MAAM;GACjD,GAAG,YAAY,mBAAmB,MAAKA,UAAW,iBAAiB,MAAM,gBAAgB;GACzF,GAAG,YAAY,oBAAoB,MAAKA,UAAW,kBAAkB,MAAM,iBAAiB;GAC5F,GAAG,YAAY,oBAAoB,MAAKA,UAAW,kBAAkB,MAAM,iBAAiB;GAC7F;;CAGH,QAAc;AACZ,QAAKA,YAAa;GAAE,cAAc;GAAG,kBAAkB;GAAG,aAAa;GAAG;AAC1E,QAAKD,QAAS,OAAO;;CAGvB,WAA0B;AACxB,SAAO;GACL,OAAO,EAAE,GAAG,MAAKC,WAAY;GAC7B,SAAS,MAAM,KAAK,MAAKD,QAAS,QAAQ,CAAC,CAAC,KAAK,OAAO,EAAE,GAAG,GAAG,EAAE;GACnE;;;;;;;;AASL,SAAS,YACP,KACA,GACA,GAC4B;AAC5B,KAAI,MAAM,UAAa,MAAM,OAAW,QAAO,EAAE;AACjD,QAAO,GAAG,OAAO,KAAK,MAAM,KAAK,IAAI"}
|
|
@@ -24,7 +24,7 @@ interface CausalityMonitorConfig {
|
|
|
24
24
|
/**
|
|
25
25
|
* When `true`, emit the chain on every `checkMessage(...)` call
|
|
26
26
|
* (high-cardinality; opt-in for compliance audits). Default
|
|
27
|
-
* `false`
|
|
27
|
+
* `false` - only emit on detected leaks.
|
|
28
28
|
*/
|
|
29
29
|
readonly auditAllChains?: boolean;
|
|
30
30
|
}
|
|
@@ -69,12 +69,12 @@ declare class CausalityMonitor {
|
|
|
69
69
|
/**
|
|
70
70
|
* Append an entry to the causality chain, dropping the oldest
|
|
71
71
|
* when the chain exceeds `maxChainDepth`. Bounded-length, no PII,
|
|
72
|
-
* no secret values
|
|
72
|
+
* no secret values - entries are short opaque strings like
|
|
73
73
|
* `tool:slack-notify`, `tool.error:SecretAccessDenied`,
|
|
74
74
|
* `subagent:research-east`, `compaction:auto-trigger`.
|
|
75
75
|
*/
|
|
76
76
|
recordCall(entry: string): void;
|
|
77
|
-
/** Reset the chain
|
|
77
|
+
/** Reset the chain - e.g. on `agent.run` boundary. */
|
|
78
78
|
reset(): void;
|
|
79
79
|
/**
|
|
80
80
|
* Inspect a candidate assistant-visible string and return whether
|
|
@@ -84,7 +84,7 @@ declare class CausalityMonitor {
|
|
|
84
84
|
checkMessage(content: string): CausalityMonitorCheck;
|
|
85
85
|
/**
|
|
86
86
|
* Drain the chain to the audit log on `agent.run` completion or
|
|
87
|
-
* `agent.abort`. The runtime supplies the audit emitter
|
|
87
|
+
* `agent.abort`. The runtime supplies the audit emitter - the
|
|
88
88
|
* primitive itself is storage-agnostic.
|
|
89
89
|
*/
|
|
90
90
|
flush(reason: 'agent.run.complete' | 'agent.abort'): {
|