@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.
Files changed (65) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +41 -21
  3. package/dist/errors/index.d.ts +1 -1
  4. package/dist/errors/index.js +1 -1
  5. package/dist/errors/index.js.map +1 -1
  6. package/dist/evaluator-optimizer/index.d.ts +1 -1
  7. package/dist/evaluator-optimizer/index.js.map +1 -1
  8. package/dist/factory.d.ts.map +1 -1
  9. package/dist/factory.js +517 -108
  10. package/dist/factory.js.map +1 -1
  11. package/dist/fallback/index.d.ts +1 -1
  12. package/dist/fallback/index.js.map +1 -1
  13. package/dist/fanout/index.d.ts +8 -8
  14. package/dist/fanout/index.js +3 -3
  15. package/dist/fanout/index.js.map +1 -1
  16. package/dist/filters/index.d.ts +1 -1
  17. package/dist/filters/index.js +2 -2
  18. package/dist/filters/index.js.map +1 -1
  19. package/dist/index.d.ts +5 -5
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +6 -4
  22. package/dist/index.js.map +1 -1
  23. package/dist/internal/ids.js +1 -1
  24. package/dist/internal/ids.js.map +1 -1
  25. package/dist/internal/usage-accumulator.js +17 -2
  26. package/dist/internal/usage-accumulator.js.map +1 -1
  27. package/dist/lateral-leak/causality-monitor.d.ts +4 -4
  28. package/dist/lateral-leak/causality-monitor.js +3 -3
  29. package/dist/lateral-leak/causality-monitor.js.map +1 -1
  30. package/dist/lateral-leak/merge-guard.d.ts +2 -2
  31. package/dist/lateral-leak/merge-guard.js +1 -1
  32. package/dist/lateral-leak/merge-guard.js.map +1 -1
  33. package/dist/lateral-leak/protocol-guard.d.ts +4 -4
  34. package/dist/lateral-leak/protocol-guard.js +4 -4
  35. package/dist/lateral-leak/protocol-guard.js.map +1 -1
  36. package/dist/package.js +6 -0
  37. package/dist/package.js.map +1 -0
  38. package/dist/preferred-model/index.d.ts +2 -2
  39. package/dist/preferred-model/index.js +2 -2
  40. package/dist/preferred-model/index.js.map +1 -1
  41. package/dist/progress/index.js +2 -2
  42. package/dist/progress/index.js.map +1 -1
  43. package/dist/run-state/index.d.ts +7 -5
  44. package/dist/run-state/index.d.ts.map +1 -1
  45. package/dist/run-state/index.js +33 -4
  46. package/dist/run-state/index.js.map +1 -1
  47. package/dist/testing/replay-provider.d.ts +18 -0
  48. package/dist/testing/replay-provider.d.ts.map +1 -0
  49. package/dist/testing/replay-provider.js +100 -0
  50. package/dist/testing/replay-provider.js.map +1 -0
  51. package/dist/tooling/adapters.js +6 -6
  52. package/dist/tooling/adapters.js.map +1 -1
  53. package/dist/tooling/catalogue.js +1 -1
  54. package/dist/tooling/catalogue.js.map +1 -1
  55. package/dist/tooling/dataflow.js +19 -3
  56. package/dist/tooling/dataflow.js.map +1 -1
  57. package/dist/tooling/plan.js +92 -0
  58. package/dist/tooling/plan.js.map +1 -0
  59. package/dist/tooling/policy.js +45 -0
  60. package/dist/tooling/policy.js.map +1 -0
  61. package/dist/tooling/registry-build.js +1 -1
  62. package/dist/tooling/registry-build.js.map +1 -1
  63. package/dist/types.d.ts +189 -18
  64. package/dist/types.d.ts.map +1 -1
  65. package/package.json +11 -8
@@ -0,0 +1,45 @@
1
+ import { buildRuleOfTwoPolicy, evaluateToolArgumentPolicy } from "@graphorin/security/policy";
2
+
3
+ //#region src/tooling/policy.ts
4
+ /**
5
+ * Agent-side adapter for the D4 tool-argument policy (Progent) and the
6
+ * Rule-of-Two capability preset. Compiles `AgentConfig.toolPolicy` /
7
+ * `AgentConfig.ruleOfTwo` into the structural
8
+ * {@link ToolArgumentPolicyGuard} the tool executor consumes plus a
9
+ * capability floor the run loop folds into its single-writer gate.
10
+ *
11
+ * The pure decision engine lives in `@graphorin/security/policy`; this
12
+ * module is the thin binding, mirroring `tooling/dataflow.ts`.
13
+ *
14
+ * @packageDocumentation
15
+ */
16
+ /**
17
+ * Compile the agent's opt-in tool-argument policy + Rule-of-Two profile
18
+ * into a single guard. `ruleOfTwo` is compiled first (yielding a base
19
+ * policy + optional read-only floor); an explicit `toolPolicy` is
20
+ * appended so its rules compose - a forbid in either always wins
21
+ * (forbid-before-allow). Returns `{ guard: undefined }` when neither is
22
+ * configured (zero overhead on the default path).
23
+ */
24
+ function buildToolArgumentPolicy(toolPolicy, ruleOfTwo) {
25
+ if (toolPolicy === void 0 && ruleOfTwo === void 0) return { guard: void 0 };
26
+ const compiled = ruleOfTwo !== void 0 ? buildRuleOfTwoPolicy(ruleOfTwo) : void 0;
27
+ const merged = {
28
+ rules: [...compiled?.policy.rules ?? [], ...toolPolicy?.rules ?? []],
29
+ defaultDenySensitive: (compiled?.policy.defaultDenySensitive ?? false) || (toolPolicy?.defaultDenySensitive ?? false)
30
+ };
31
+ const guard = { evaluate: (input) => evaluateToolArgumentPolicy(merged, {
32
+ toolName: input.toolName,
33
+ sideEffectClass: input.sideEffectClass,
34
+ sensitive: input.sensitive,
35
+ args: input.args
36
+ }) };
37
+ return compiled?.capability !== void 0 ? {
38
+ guard,
39
+ capabilityFloor: compiled.capability
40
+ } : { guard };
41
+ }
42
+
43
+ //#endregion
44
+ export { buildToolArgumentPolicy };
45
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","names":["merged: ToolArgumentPolicy","guard: ToolArgumentPolicyGuard"],"sources":["../../src/tooling/policy.ts"],"sourcesContent":["/**\n * Agent-side adapter for the D4 tool-argument policy (Progent) and the\n * Rule-of-Two capability preset. Compiles `AgentConfig.toolPolicy` /\n * `AgentConfig.ruleOfTwo` into the structural\n * {@link ToolArgumentPolicyGuard} the tool executor consumes plus a\n * capability floor the run loop folds into its single-writer gate.\n *\n * The pure decision engine lives in `@graphorin/security/policy`; this\n * module is the thin binding, mirroring `tooling/dataflow.ts`.\n *\n * @packageDocumentation\n */\n\nimport {\n buildRuleOfTwoPolicy,\n evaluateToolArgumentPolicy,\n type PolicySideEffectClass,\n type RuleOfTwoProfile,\n type ToolArgumentPolicy,\n} from '@graphorin/security/policy';\nimport type { ToolArgumentPolicyGuard } from '@graphorin/tools/executor';\n\nexport type { RuleOfTwoProfile, ToolArgumentPolicy } from '@graphorin/security/policy';\n\n/**\n * Compile the agent's opt-in tool-argument policy + Rule-of-Two profile\n * into a single guard. `ruleOfTwo` is compiled first (yielding a base\n * policy + optional read-only floor); an explicit `toolPolicy` is\n * appended so its rules compose - a forbid in either always wins\n * (forbid-before-allow). Returns `{ guard: undefined }` when neither is\n * configured (zero overhead on the default path).\n */\nexport function buildToolArgumentPolicy(\n toolPolicy: ToolArgumentPolicy | undefined,\n ruleOfTwo: RuleOfTwoProfile | undefined,\n): { readonly guard: ToolArgumentPolicyGuard | undefined; readonly capabilityFloor?: 'read-only' } {\n if (toolPolicy === undefined && ruleOfTwo === undefined) {\n return { guard: undefined };\n }\n\n const compiled = ruleOfTwo !== undefined ? buildRuleOfTwoPolicy(ruleOfTwo) : undefined;\n const merged: ToolArgumentPolicy = {\n rules: [...(compiled?.policy.rules ?? []), ...(toolPolicy?.rules ?? [])],\n defaultDenySensitive:\n (compiled?.policy.defaultDenySensitive ?? false) ||\n (toolPolicy?.defaultDenySensitive ?? false),\n };\n\n const guard: ToolArgumentPolicyGuard = {\n evaluate: (input) =>\n evaluateToolArgumentPolicy(merged, {\n toolName: input.toolName,\n sideEffectClass: input.sideEffectClass as PolicySideEffectClass,\n sensitive: input.sensitive,\n args: input.args,\n }),\n };\n\n return compiled?.capability !== undefined\n ? { guard, capabilityFloor: compiled.capability }\n : { guard };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,wBACd,YACA,WACiG;AACjG,KAAI,eAAe,UAAa,cAAc,OAC5C,QAAO,EAAE,OAAO,QAAW;CAG7B,MAAM,WAAW,cAAc,SAAY,qBAAqB,UAAU,GAAG;CAC7E,MAAMA,SAA6B;EACjC,OAAO,CAAC,GAAI,UAAU,OAAO,SAAS,EAAE,EAAG,GAAI,YAAY,SAAS,EAAE,CAAE;EACxE,uBACG,UAAU,OAAO,wBAAwB,WACzC,YAAY,wBAAwB;EACxC;CAED,MAAMC,QAAiC,EACrC,WAAW,UACT,2BAA2B,QAAQ;EACjC,UAAU,MAAM;EAChB,iBAAiB,MAAM;EACvB,WAAW,MAAM;EACjB,MAAM,MAAM;EACb,CAAC,EACL;AAED,QAAO,UAAU,eAAe,SAC5B;EAAE;EAAO,iBAAiB,SAAS;EAAY,GAC/C,EAAE,OAAO"}
@@ -20,7 +20,7 @@ const TOOL_SOURCE_KINDS = new Set([
20
20
  * is: every `tools` entry, then every inline tool of every skill.
21
21
  * Throws whatever `register(...)` throws for a malformed tool
22
22
  * (`InvalidExampleError`, `InvalidPreferredModelError`,
23
- * `InvalidSideEffectClassError`) the registry is the validation
23
+ * `InvalidSideEffectClassError`) - the registry is the validation
24
24
  * authority, so a bad tool fails fast at build time.
25
25
  *
26
26
  * @stable
@@ -1 +1 @@
1
- {"version":3,"file":"registry-build.js","names":["FIRST_PARTY_SOURCE: ToolSource","TOOL_SOURCE_KINDS: ReadonlySet<string>"],"sources":["../../src/tooling/registry-build.ts"],"sourcesContent":["/**\n * Assemble a single `@graphorin/tools` {@link ToolRegistry} from every\n * tool source the agent knows about (first-party `config.tools` + skill\n * tools), then resolve cross-source name collisions deterministically.\n *\n * This realises Principle #12 (one registry, one collision policy) and\n * gives `createToolRegistry(...)` its first production call-site\n * (`createAgent(...)` warm-up see `factory.ts`). The run loop consumes\n * the resulting registry in a later work item (WI-03); `tool_search`\n * uses it in WI-05. This module only *builds* it.\n *\n * Two deliberate fidelity notes (verified against source, not the plan):\n * - **MCP tools are not auto-stamped.** `adaptMCPTools(...)` returns\n * plain `Tool`s; `__source`/`__trustClass` are only ever assigned by\n * the registry's `normaliseTool(...)` at registration time, derived\n * from the `source` passed to `register(...)`. So {@link inferToolSource}\n * honours an explicit `__source` stamp if one is present (forward-\n * compatible with a dedicated `mcp` config hook / re-registered\n * `ResolvedTool`s) and otherwise treats the tool as first-party. The\n * MCP tool's baked-in `sandboxPolicy` / `inboundSanitization` are\n * preserved regardless (operator override > trust-class default).\n * - **Skill tool-stamping lives here, not in `@graphorin/skills`.** The\n * skills loader defers it (it does not depend on a tools registry);\n * the agent which depends on both stamps each inline skill tool\n * via `stampSkillTool(...)`.\n *\n * @packageDocumentation\n */\n\nimport type { Tool, ToolSource } from '@graphorin/core';\nimport type { SkillMetadata } from '@graphorin/skills';\nimport { stampSkillTool } from '@graphorin/skills';\nimport {\n type CollisionContext,\n type CollisionResolution,\n type CollisionStrategy,\n createToolRegistry,\n type ToolAuditEvent,\n type ToolRegistry,\n type ToolSearchEmbedder,\n} from '@graphorin/tools/registry';\n\nimport type { SkillsRegistryLike } from '../types.js';\n\n/** The source attributed to an unstamped first-party `config.tools` entry. */\nconst FIRST_PARTY_SOURCE: ToolSource = Object.freeze({ kind: 'first-party' });\n\n/** The five `ToolSource.kind` discriminants (used to validate a stamp). */\nconst TOOL_SOURCE_KINDS: ReadonlySet<string> = new Set([\n 'first-party',\n 'built-in',\n 'skill',\n 'mcp',\n 'web-search',\n]);\n\n/** Options for {@link buildToolRegistry}. */\nexport interface BuildToolRegistryOptions {\n /** First-party (and any pre-stamped) tools, i.e. `config.tools`. */\n readonly tools?: ReadonlyArray<Tool<unknown, unknown, unknown>>;\n /**\n * The agent's skill registry (`config.skills`). Only entries that\n * structurally match the {@link SkillLike} surface contribute tools;\n * non-inline skill sources expose `tools(): []` and are a no-op here.\n */\n readonly skills?: SkillsRegistryLike;\n /** Collision-resolution strategy. Default `'auto-prefix'`. */\n readonly collisionStrategy?: CollisionStrategy;\n /**\n * Context tag carried on the collision audit rows + used for the\n * `'priority'` tie-break boost. Default `{ source: first-party }`.\n */\n readonly collisionContext?: CollisionContext;\n /** Audit sink forwarded to the registry (collision + classification rows). */\n readonly emitAudit?: (event: ToolAuditEvent) => void;\n /** Semantic-search embedder. Passed through; consumed by `tool_search` (WI-05). */\n readonly embedder?: ToolSearchEmbedder;\n /** Cosine threshold for the semantic search stage (WI-05). */\n readonly semanticScoreThreshold?: number;\n}\n\n/** Outcome of {@link buildToolRegistry}. */\nexport interface BuildToolRegistryResult {\n /** The assembled registry, post collision-resolution. */\n readonly registry: ToolRegistry;\n /** The collision resolutions (also emitted on the audit sink). */\n readonly resolutions: ReadonlyArray<CollisionResolution>;\n /** Count of tools registered across every source. */\n readonly registered: number;\n /** Count of `skills.list()` entries skipped for not matching the `Skill` surface. */\n readonly skippedSkillEntries: number;\n}\n\n/**\n * Build and collision-resolve a {@link ToolRegistry} from the supplied\n * tool sources.\n *\n * Registration order (which seeds the `'registration-order'` tie-break)\n * is: every `tools` entry, then every inline tool of every skill.\n * Throws whatever `register(...)` throws for a malformed tool\n * (`InvalidExampleError`, `InvalidPreferredModelError`,\n * `InvalidSideEffectClassError`) the registry is the validation\n * authority, so a bad tool fails fast at build time.\n *\n * @stable\n */\nexport function buildToolRegistry(options: BuildToolRegistryOptions = {}): BuildToolRegistryResult {\n const registry = createToolRegistry({\n ...(options.emitAudit !== undefined ? { emitAudit: options.emitAudit } : {}),\n ...(options.embedder !== undefined ? { embedder: options.embedder } : {}),\n ...(options.semanticScoreThreshold !== undefined\n ? { semanticScoreThreshold: options.semanticScoreThreshold }\n : {}),\n });\n\n let registered = 0;\n\n // 1. First-party / pre-stamped tools (`config.tools`).\n for (const tool of options.tools ?? []) {\n registry.register(tool, inferToolSource(tool));\n registered += 1;\n }\n\n // 2. Skill tools. The agent accepts a loose `SkillsRegistryLike`, so\n // each `list()` entry is narrowed structurally before stamping.\n let skippedSkillEntries = 0;\n for (const entry of options.skills?.list?.() ?? []) {\n if (!isSkillLike(entry)) {\n skippedSkillEntries += 1;\n continue;\n }\n for (const tool of entry.tools()) {\n const stamped = stampSkillTool(tool, entry);\n registry.register(stamped.tool, stamped.source);\n registered += 1;\n }\n }\n\n // 3. Resolve collisions deterministically (first-party wins; losers\n // are namespaced). `assertNoDuplicates(strategy, ctx)` emits a\n // `tool:collision:*` audit row per resolution.\n const resolutions = registry.assertNoDuplicates(\n options.collisionStrategy ?? 'auto-prefix',\n options.collisionContext ?? { source: FIRST_PARTY_SOURCE },\n );\n\n return Object.freeze({ registry, resolutions, registered, skippedSkillEntries });\n}\n\n/**\n * Derive the {@link ToolSource} for a `config.tools` entry. Honours an\n * explicit, well-formed `__source` stamp when present (so re-registered\n * `ResolvedTool`s and a future `mcp` config hook keep their provenance);\n * otherwise the tool is first-party. See the module doc on why MCP tools\n * are *not* auto-detected here.\n */\nfunction inferToolSource(tool: Tool<unknown, unknown, unknown>): ToolSource {\n const stamped = (tool as { readonly __source?: unknown }).__source;\n return isToolSource(stamped) ? stamped : FIRST_PARTY_SOURCE;\n}\n\n/** Structural guard: a well-formed {@link ToolSource} discriminated union. */\nfunction isToolSource(value: unknown): value is ToolSource {\n if (typeof value !== 'object' || value === null) return false;\n const kind = (value as { readonly kind?: unknown }).kind;\n return typeof kind === 'string' && TOOL_SOURCE_KINDS.has(kind);\n}\n\n/**\n * Minimal structural view of a `@graphorin/skills` `Skill` exactly the\n * members {@link buildToolRegistry} reads. The agent's `SkillsRegistryLike`\n * is intentionally loose (`list?(): unknown[]`), so entries are validated\n * at runtime before being treated as skills.\n */\ninterface SkillLike {\n readonly metadata: SkillMetadata;\n tools(): ReadonlyArray<Tool<unknown, unknown, unknown>>;\n}\n\n/** Structural guard for the {@link SkillLike} surface. */\nfunction isSkillLike(value: unknown): value is SkillLike {\n if (typeof value !== 'object' || value === null) return false;\n const metadata = (value as { readonly metadata?: unknown }).metadata;\n const metadataOk =\n typeof metadata === 'object' &&\n metadata !== null &&\n typeof (metadata as { readonly name?: unknown }).name === 'string' &&\n typeof (metadata as { readonly graphorinTrustLevel?: unknown }).graphorinTrustLevel ===\n 'string';\n return metadataOk && typeof (value as { readonly tools?: unknown }).tools === 'function';\n}\n"],"mappings":";;;;;AA6CA,MAAMA,qBAAiC,OAAO,OAAO,EAAE,MAAM,eAAe,CAAC;;AAG7E,MAAMC,oBAAyC,IAAI,IAAI;CACrD;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;;AAoDF,SAAgB,kBAAkB,UAAoC,EAAE,EAA2B;CACjG,MAAM,WAAW,mBAAmB;EAClC,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,WAAW,GAAG,EAAE;EAC3E,GAAI,QAAQ,aAAa,SAAY,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;EACxE,GAAI,QAAQ,2BAA2B,SACnC,EAAE,wBAAwB,QAAQ,wBAAwB,GAC1D,EAAE;EACP,CAAC;CAEF,IAAI,aAAa;AAGjB,MAAK,MAAM,QAAQ,QAAQ,SAAS,EAAE,EAAE;AACtC,WAAS,SAAS,MAAM,gBAAgB,KAAK,CAAC;AAC9C,gBAAc;;CAKhB,IAAI,sBAAsB;AAC1B,MAAK,MAAM,SAAS,QAAQ,QAAQ,QAAQ,IAAI,EAAE,EAAE;AAClD,MAAI,CAAC,YAAY,MAAM,EAAE;AACvB,0BAAuB;AACvB;;AAEF,OAAK,MAAM,QAAQ,MAAM,OAAO,EAAE;GAChC,MAAM,UAAU,eAAe,MAAM,MAAM;AAC3C,YAAS,SAAS,QAAQ,MAAM,QAAQ,OAAO;AAC/C,iBAAc;;;CAOlB,MAAM,cAAc,SAAS,mBAC3B,QAAQ,qBAAqB,eAC7B,QAAQ,oBAAoB,EAAE,QAAQ,oBAAoB,CAC3D;AAED,QAAO,OAAO,OAAO;EAAE;EAAU;EAAa;EAAY;EAAqB,CAAC;;;;;;;;;AAUlF,SAAS,gBAAgB,MAAmD;CAC1E,MAAM,UAAW,KAAyC;AAC1D,QAAO,aAAa,QAAQ,GAAG,UAAU;;;AAI3C,SAAS,aAAa,OAAqC;AACzD,KAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;CACxD,MAAM,OAAQ,MAAsC;AACpD,QAAO,OAAO,SAAS,YAAY,kBAAkB,IAAI,KAAK;;;AAehE,SAAS,YAAY,OAAoC;AACvD,KAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;CACxD,MAAM,WAAY,MAA0C;AAO5D,QALE,OAAO,aAAa,YACpB,aAAa,QACb,OAAQ,SAAyC,SAAS,YAC1D,OAAQ,SAAwD,wBAC9D,YACiB,OAAQ,MAAuC,UAAU"}
1
+ {"version":3,"file":"registry-build.js","names":["FIRST_PARTY_SOURCE: ToolSource","TOOL_SOURCE_KINDS: ReadonlySet<string>"],"sources":["../../src/tooling/registry-build.ts"],"sourcesContent":["/**\n * Assemble a single `@graphorin/tools` {@link ToolRegistry} from every\n * tool source the agent knows about (first-party `config.tools` + skill\n * tools), then resolve cross-source name collisions deterministically.\n *\n * This realises Principle #12 (one registry, one collision policy) and\n * gives `createToolRegistry(...)` its first production call-site\n * (`createAgent(...)` warm-up - see `factory.ts`). The run loop consumes\n * the resulting registry in a later work item (WI-03); `tool_search`\n * uses it in WI-05. This module only *builds* it.\n *\n * Two deliberate fidelity notes (verified against source, not the plan):\n * - **MCP tools are not auto-stamped.** `adaptMCPTools(...)` returns\n * plain `Tool`s; `__source`/`__trustClass` are only ever assigned by\n * the registry's `normaliseTool(...)` at registration time, derived\n * from the `source` passed to `register(...)`. So {@link inferToolSource}\n * honours an explicit `__source` stamp if one is present (forward-\n * compatible with a dedicated `mcp` config hook / re-registered\n * `ResolvedTool`s) and otherwise treats the tool as first-party. The\n * MCP tool's baked-in `sandboxPolicy` / `inboundSanitization` are\n * preserved regardless (operator override > trust-class default).\n * - **Skill tool-stamping lives here, not in `@graphorin/skills`.** The\n * skills loader defers it (it does not depend on a tools registry);\n * the agent - which depends on both - stamps each inline skill tool\n * via `stampSkillTool(...)`.\n *\n * @packageDocumentation\n */\n\nimport type { Tool, ToolSource } from '@graphorin/core';\nimport type { SkillMetadata } from '@graphorin/skills';\nimport { stampSkillTool } from '@graphorin/skills';\nimport {\n type CollisionContext,\n type CollisionResolution,\n type CollisionStrategy,\n createToolRegistry,\n type ToolAuditEvent,\n type ToolRegistry,\n type ToolSearchEmbedder,\n} from '@graphorin/tools/registry';\n\nimport type { SkillsRegistryLike } from '../types.js';\n\n/** The source attributed to an unstamped first-party `config.tools` entry. */\nconst FIRST_PARTY_SOURCE: ToolSource = Object.freeze({ kind: 'first-party' });\n\n/** The five `ToolSource.kind` discriminants (used to validate a stamp). */\nconst TOOL_SOURCE_KINDS: ReadonlySet<string> = new Set([\n 'first-party',\n 'built-in',\n 'skill',\n 'mcp',\n 'web-search',\n]);\n\n/** Options for {@link buildToolRegistry}. */\nexport interface BuildToolRegistryOptions {\n /** First-party (and any pre-stamped) tools, i.e. `config.tools`. */\n readonly tools?: ReadonlyArray<Tool<unknown, unknown, unknown>>;\n /**\n * The agent's skill registry (`config.skills`). Only entries that\n * structurally match the {@link SkillLike} surface contribute tools;\n * non-inline skill sources expose `tools(): []` and are a no-op here.\n */\n readonly skills?: SkillsRegistryLike;\n /** Collision-resolution strategy. Default `'auto-prefix'`. */\n readonly collisionStrategy?: CollisionStrategy;\n /**\n * Context tag carried on the collision audit rows + used for the\n * `'priority'` tie-break boost. Default `{ source: first-party }`.\n */\n readonly collisionContext?: CollisionContext;\n /** Audit sink forwarded to the registry (collision + classification rows). */\n readonly emitAudit?: (event: ToolAuditEvent) => void;\n /** Semantic-search embedder. Passed through; consumed by `tool_search` (WI-05). */\n readonly embedder?: ToolSearchEmbedder;\n /** Cosine threshold for the semantic search stage (WI-05). */\n readonly semanticScoreThreshold?: number;\n}\n\n/** Outcome of {@link buildToolRegistry}. */\nexport interface BuildToolRegistryResult {\n /** The assembled registry, post collision-resolution. */\n readonly registry: ToolRegistry;\n /** The collision resolutions (also emitted on the audit sink). */\n readonly resolutions: ReadonlyArray<CollisionResolution>;\n /** Count of tools registered across every source. */\n readonly registered: number;\n /** Count of `skills.list()` entries skipped for not matching the `Skill` surface. */\n readonly skippedSkillEntries: number;\n}\n\n/**\n * Build and collision-resolve a {@link ToolRegistry} from the supplied\n * tool sources.\n *\n * Registration order (which seeds the `'registration-order'` tie-break)\n * is: every `tools` entry, then every inline tool of every skill.\n * Throws whatever `register(...)` throws for a malformed tool\n * (`InvalidExampleError`, `InvalidPreferredModelError`,\n * `InvalidSideEffectClassError`) - the registry is the validation\n * authority, so a bad tool fails fast at build time.\n *\n * @stable\n */\nexport function buildToolRegistry(options: BuildToolRegistryOptions = {}): BuildToolRegistryResult {\n const registry = createToolRegistry({\n ...(options.emitAudit !== undefined ? { emitAudit: options.emitAudit } : {}),\n ...(options.embedder !== undefined ? { embedder: options.embedder } : {}),\n ...(options.semanticScoreThreshold !== undefined\n ? { semanticScoreThreshold: options.semanticScoreThreshold }\n : {}),\n });\n\n let registered = 0;\n\n // 1. First-party / pre-stamped tools (`config.tools`).\n for (const tool of options.tools ?? []) {\n registry.register(tool, inferToolSource(tool));\n registered += 1;\n }\n\n // 2. Skill tools. The agent accepts a loose `SkillsRegistryLike`, so\n // each `list()` entry is narrowed structurally before stamping.\n let skippedSkillEntries = 0;\n for (const entry of options.skills?.list?.() ?? []) {\n if (!isSkillLike(entry)) {\n skippedSkillEntries += 1;\n continue;\n }\n for (const tool of entry.tools()) {\n const stamped = stampSkillTool(tool, entry);\n registry.register(stamped.tool, stamped.source);\n registered += 1;\n }\n }\n\n // 3. Resolve collisions deterministically (first-party wins; losers\n // are namespaced). `assertNoDuplicates(strategy, ctx)` emits a\n // `tool:collision:*` audit row per resolution.\n const resolutions = registry.assertNoDuplicates(\n options.collisionStrategy ?? 'auto-prefix',\n options.collisionContext ?? { source: FIRST_PARTY_SOURCE },\n );\n\n return Object.freeze({ registry, resolutions, registered, skippedSkillEntries });\n}\n\n/**\n * Derive the {@link ToolSource} for a `config.tools` entry. Honours an\n * explicit, well-formed `__source` stamp when present (so re-registered\n * `ResolvedTool`s and a future `mcp` config hook keep their provenance);\n * otherwise the tool is first-party. See the module doc on why MCP tools\n * are *not* auto-detected here.\n */\nfunction inferToolSource(tool: Tool<unknown, unknown, unknown>): ToolSource {\n const stamped = (tool as { readonly __source?: unknown }).__source;\n return isToolSource(stamped) ? stamped : FIRST_PARTY_SOURCE;\n}\n\n/** Structural guard: a well-formed {@link ToolSource} discriminated union. */\nfunction isToolSource(value: unknown): value is ToolSource {\n if (typeof value !== 'object' || value === null) return false;\n const kind = (value as { readonly kind?: unknown }).kind;\n return typeof kind === 'string' && TOOL_SOURCE_KINDS.has(kind);\n}\n\n/**\n * Minimal structural view of a `@graphorin/skills` `Skill` - exactly the\n * members {@link buildToolRegistry} reads. The agent's `SkillsRegistryLike`\n * is intentionally loose (`list?(): unknown[]`), so entries are validated\n * at runtime before being treated as skills.\n */\ninterface SkillLike {\n readonly metadata: SkillMetadata;\n tools(): ReadonlyArray<Tool<unknown, unknown, unknown>>;\n}\n\n/** Structural guard for the {@link SkillLike} surface. */\nfunction isSkillLike(value: unknown): value is SkillLike {\n if (typeof value !== 'object' || value === null) return false;\n const metadata = (value as { readonly metadata?: unknown }).metadata;\n const metadataOk =\n typeof metadata === 'object' &&\n metadata !== null &&\n typeof (metadata as { readonly name?: unknown }).name === 'string' &&\n typeof (metadata as { readonly graphorinTrustLevel?: unknown }).graphorinTrustLevel ===\n 'string';\n return metadataOk && typeof (value as { readonly tools?: unknown }).tools === 'function';\n}\n"],"mappings":";;;;;AA6CA,MAAMA,qBAAiC,OAAO,OAAO,EAAE,MAAM,eAAe,CAAC;;AAG7E,MAAMC,oBAAyC,IAAI,IAAI;CACrD;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;;AAoDF,SAAgB,kBAAkB,UAAoC,EAAE,EAA2B;CACjG,MAAM,WAAW,mBAAmB;EAClC,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,WAAW,GAAG,EAAE;EAC3E,GAAI,QAAQ,aAAa,SAAY,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;EACxE,GAAI,QAAQ,2BAA2B,SACnC,EAAE,wBAAwB,QAAQ,wBAAwB,GAC1D,EAAE;EACP,CAAC;CAEF,IAAI,aAAa;AAGjB,MAAK,MAAM,QAAQ,QAAQ,SAAS,EAAE,EAAE;AACtC,WAAS,SAAS,MAAM,gBAAgB,KAAK,CAAC;AAC9C,gBAAc;;CAKhB,IAAI,sBAAsB;AAC1B,MAAK,MAAM,SAAS,QAAQ,QAAQ,QAAQ,IAAI,EAAE,EAAE;AAClD,MAAI,CAAC,YAAY,MAAM,EAAE;AACvB,0BAAuB;AACvB;;AAEF,OAAK,MAAM,QAAQ,MAAM,OAAO,EAAE;GAChC,MAAM,UAAU,eAAe,MAAM,MAAM;AAC3C,YAAS,SAAS,QAAQ,MAAM,QAAQ,OAAO;AAC/C,iBAAc;;;CAOlB,MAAM,cAAc,SAAS,mBAC3B,QAAQ,qBAAqB,eAC7B,QAAQ,oBAAoB,EAAE,QAAQ,oBAAoB,CAC3D;AAED,QAAO,OAAO,OAAO;EAAE;EAAU;EAAa;EAAY;EAAqB,CAAC;;;;;;;;;AAUlF,SAAS,gBAAgB,MAAmD;CAC1E,MAAM,UAAW,KAAyC;AAC1D,QAAO,aAAa,QAAQ,GAAG,UAAU;;;AAI3C,SAAS,aAAa,OAAqC;AACzD,KAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;CACxD,MAAM,OAAQ,MAAsC;AACpD,QAAO,OAAO,SAAS,YAAY,kBAAkB,IAAI,KAAK;;;AAehE,SAAS,YAAY,OAAoC;AACvD,KAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;CACxD,MAAM,WAAY,MAA0C;AAO5D,QALE,OAAO,aAAa,YACpB,aAAa,QACb,OAAQ,SAAyC,SAAS,YAC1D,OAAQ,SAAwD,wBAC9D,YACiB,OAAQ,MAAuC,UAAU"}
package/dist/types.d.ts CHANGED
@@ -3,10 +3,12 @@ import { MergeGuardConfig } from "./lateral-leak/merge-guard.js";
3
3
  import { ChildResult, FanOutOptions, FanOutResult, MergeStrategy, PerChildBudget } from "./fanout/index.js";
4
4
  import { CausalityMonitorConfig } from "./lateral-leak/causality-monitor.js";
5
5
  import { ProgressIO, ProgressReadOptions, ProgressWriteOptions } from "./progress/index.js";
6
- import { AgentEvent, AgentResult, CheckpointStore, HandoffFilter, Message, ModelHint, ModelSpec, ProgressArtifactRef, Provider, ReasoningRetention, RunContext, RunState, Sensitivity, StopCondition, Tool, ToolChoice, Tracer } from "@graphorin/core";
6
+ import * as _graphorin_core0 from "@graphorin/core";
7
+ import { AgentEvent, AgentResult, CheckpointStore, HandoffFilter, Message, ModelHint, ModelSpec, ProgressArtifactRef, Provider, ProviderCachePolicy, ReasoningRetention, RunContext, RunState, Sensitivity, StopCondition, Tool, ToolChoice, Tracer } from "@graphorin/core";
7
8
  import { InputGuardrail, OutputGuardrail } from "@graphorin/security/guardrails";
8
9
  import { ResultReader } from "@graphorin/tools/result";
9
10
  import { DataFlowPolicyConfig } from "@graphorin/security/dataflow";
11
+ import { RuleOfTwoProfile, ToolArgumentPolicy } from "@graphorin/security/policy";
10
12
  import { ToolRegistry } from "@graphorin/tools/registry";
11
13
  import { Memory, PostCompactionHook } from "@graphorin/memory";
12
14
 
@@ -31,7 +33,7 @@ interface OutputSpec<TOutput> {
31
33
  /**
32
34
  * Local validator (Zod-compatible `{ parse }`) applied to the final
33
35
  * model output on the completed path (AG-3). A parse failure fails
34
- * the run with `output-validation-failed` never a silent cast.
36
+ * the run with `output-validation-failed` - never a silent cast.
35
37
  */
36
38
  readonly schema?: {
37
39
  parse(value: unknown): TOutput;
@@ -43,7 +45,7 @@ interface OutputSpec<TOutput> {
43
45
  * `ProviderRequest.outputType` for adapters with native structured
44
46
  * output, and embedded in the fallback JSON instruction appended as
45
47
  * a trailing system message (the documented contract until adapters
46
- * consume `outputType` natively PS-24).
48
+ * consume `outputType` natively - PS-24).
47
49
  */
48
50
  readonly jsonSchema?: Readonly<Record<string, unknown>>;
49
51
  }
@@ -116,7 +118,7 @@ interface AgentConfig<TDeps = unknown, TOutput = string> {
116
118
  * runtime calls `memory.contextEngine.assemble(...)` once at run start: the
117
119
  * agent's `instructions` become Layer 2 and the engine prepends the memory
118
120
  * base and appends working blocks, procedural rules, skill cards, the
119
- * metadata counts, and when `factsAutoRecall` is configured auto-recalled
121
+ * metadata counts, and - when `factsAutoRecall` is configured - auto-recalled
120
122
  * facts. Defaults `false`: the prompt is built from `instructions` alone and
121
123
  * the model reaches memory only through the memory tools it calls (the
122
124
  * documented explicit pattern). Has no effect without `memory`.
@@ -126,7 +128,7 @@ interface AgentConfig<TDeps = unknown, TOutput = string> {
126
128
  readonly outputType?: OutputSpec<TOutput>;
127
129
  /**
128
130
  * Deterministic checks run by the loop (AG-2; canonical contract is
129
- * `@graphorin/security`'s `GuardrailDefinition` SDF-4).
131
+ * `@graphorin/security`'s `GuardrailDefinition` - SDF-4).
130
132
  *
131
133
  * - `input` guardrails run over each **fresh-run seed user message**
132
134
  * (string content) before the first provider call. `'block'` fails
@@ -136,7 +138,7 @@ interface AgentConfig<TDeps = unknown, TOutput = string> {
136
138
  * - `output` guardrails run over the **final output** on the
137
139
  * completed path before `agent.end`. `'block'` fails the run;
138
140
  * `'rewrite'` replaces `result.output` (text deltas were already
139
- * streamed the rewrite governs the durable result, not the
141
+ * streamed - the rewrite governs the durable result, not the
140
142
  * live token stream).
141
143
  *
142
144
  * Every trip emits a `guardrail.tripped` event.
@@ -152,9 +154,9 @@ interface AgentConfig<TDeps = unknown, TOutput = string> {
152
154
  /**
153
155
  * How the model invokes tools (P1-2).
154
156
  *
155
- * - `'direct'` (default) the model emits one provider tool-call per
157
+ * - `'direct'` (default) - the model emits one provider tool-call per
156
158
  * tool, each result inlined into the conversation.
157
- * - `'code-mode'` the agent advertises only the `code_execute` /
159
+ * - `'code-mode'` - the agent advertises only the `code_execute` /
158
160
  * `code_search` meta-tools; the model writes a script that calls
159
161
  * tools in a sandbox via `tools.<name>(args)`, and **only the
160
162
  * script's final result re-enters context** (intermediate results
@@ -194,13 +196,13 @@ interface AgentConfig<TDeps = unknown, TOutput = string> {
194
196
  * Graphorin already tracks (trust class + source + sensitivity), to
195
197
  * defuse the lethal trifecta: a sink (`side-effecting` /
196
198
  * `external-stateful` tool) is blocked when untrusted content flows
197
- * into it verbatim, or conservatively when it fires while both
199
+ * into it verbatim, or - conservatively - when it fires while both
198
200
  * untrusted content and secret-tier data are present in the run.
199
201
  *
200
- * - `mode: 'shadow'` audit-only; tainted flows are flagged
202
+ * - `mode: 'shadow'` - audit-only; tainted flows are flagged
201
203
  * (`tool:dataflow:flagged` audit + counter) but never blocked. Ship
202
204
  * this first to surface false positives.
203
- * - `mode: 'enforce'` tainted flows are blocked (the sink does not
205
+ * - `mode: 'enforce'` - tainted flows are blocked (the sink does not
204
206
  * run; the call yields a `dataflow_policy_blocked` error) unless the
205
207
  * sink is listed in `declassifySinks` (an audited operator override).
206
208
  *
@@ -218,13 +220,136 @@ interface AgentConfig<TDeps = unknown, TOutput = string> {
218
220
  * any reader force-registers `read_result` even when no tool spills.
219
221
  */
220
222
  readonly resultReaders?: ReadonlyArray<ResultReader>;
223
+ /**
224
+ * Opt-in prompt-cache breakpoint policy (core-provider-02), forwarded
225
+ * verbatim on every `ProviderRequest` the loop issues. With
226
+ * `{ breakpoints: 'auto' }` the Anthropic path (vercel adapter) anchors
227
+ * `cache_control` markers on the first and last conversation messages,
228
+ * so the stable prefix (tools + system + early turns) is written to the
229
+ * provider cache once and read at the discounted rate on every later
230
+ * step. Providers with automatic caching ignore it. Pair with the
231
+ * append-only transcript the loop already maintains - cache hit rate is
232
+ * the #1 production cost lever for multi-step agents.
233
+ */
234
+ readonly cachePolicy?: ProviderCachePolicy;
235
+ /**
236
+ * When deferred-tool promotions (via `tool_search`) take effect (C1):
237
+ *
238
+ * - `'immediate'` (default) - a promoted tool joins the catalogue on the
239
+ * NEXT step. Costs one provider-cache invalidation per promotion
240
+ * (the tools block changes), which is the standard trade for tool
241
+ * discovery.
242
+ * - `'run-boundary'` - the catalogue advertised to the model is frozen
243
+ * for the whole run; promotions are still recorded (and persisted on
244
+ * `RunState.promotedTools`) but only join the catalogue on the next
245
+ * run / resume. Keeps the provider prompt cache byte-stable across
246
+ * every step of a run.
247
+ */
248
+ readonly toolPromotion?: 'immediate' | 'run-boundary';
249
+ /**
250
+ * C3: rules-based verifiers that run when the model emits a terminal
251
+ * (no-tool-call) response. A failing verifier's feedback is appended
252
+ * to the transcript as a user message and the loop continues, up to
253
+ * `maxVerifierRounds` extra rounds. Verifiers are DETERMINISTIC checks
254
+ * (lint/test runners, format validators, exit codes) - deliberately
255
+ * not an evidence-free "reflect on your answer" step, which the
256
+ * self-correction literature shows degrades performance.
257
+ */
258
+ readonly verifiers?: ReadonlyArray<ResponseVerifier>;
259
+ /**
260
+ * Cap on verifier-triggered continuation rounds per run (C3).
261
+ * @default 1
262
+ */
263
+ readonly maxVerifierRounds?: number;
264
+ /**
265
+ * C3: transparent bounded retry for transient tool failures, forwarded
266
+ * to the executor. Defaults (when set): `maxAttempts: 3`,
267
+ * `backoffMs: 250`, `kinds: ['rate_limited']`; retries only ever run
268
+ * for `pure` / `read-only` tools or tools with an `idempotencyKey`.
269
+ */
270
+ readonly toolRetry?: {
271
+ readonly maxAttempts?: number;
272
+ readonly backoffMs?: number;
273
+ readonly kinds?: ReadonlyArray<_graphorin_core0.ToolErrorKind>;
274
+ };
275
+ /**
276
+ * C3: journal each step's raw model response (text + tool calls +
277
+ * model id) onto `RunState.steps[].providerResponse`, enabling
278
+ * deterministic replay via `createReplayProvider(state)` - reproduce
279
+ * an entire run without live model calls.
280
+ * @default false
281
+ */
282
+ readonly recordProviderResponses?: boolean;
221
283
  readonly tracer?: Tracer;
222
284
  readonly checkpointStore?: CheckpointStore;
223
285
  readonly sensitivity?: Sensitivity;
286
+ /**
287
+ * Agent-default capability restriction (D2). `'read-only'` builds a
288
+ * side-effect-free agent: writer tools and handoffs are never
289
+ * advertised and the executor blocks writer calls deterministically
290
+ * (`capability_blocked`). Per-call override:
291
+ * {@link AgentCallOptions.capability}. See {@link AgentCapability}.
292
+ */
293
+ readonly capability?: AgentCapability;
294
+ /**
295
+ * Declarative tool-argument policy (D4 / Progent). Forbid-before-allow
296
+ * rules over tool name + validated args, evaluated by the executor on
297
+ * every call; default-deny sensitive tools with `defaultDenySensitive`.
298
+ * A forbid verdict blocks the call (`capability_blocked`). Composes on
299
+ * top of {@link ruleOfTwo}. See `@graphorin/security/policy`.
300
+ */
301
+ readonly toolPolicy?: ToolArgumentPolicy;
302
+ /**
303
+ * Rule-of-Two capability profile (D4). Declares which of {untrusted
304
+ * input, sensitive data, external side effects} this agent may hold;
305
+ * denying external side effects forces a read-only capability floor
306
+ * and blocks writer tools, denying sensitive data default-denies
307
+ * sensitive tools. Holding all three is the dangerous configuration
308
+ * the preset is designed to prevent. See `@graphorin/security/policy`.
309
+ */
310
+ readonly ruleOfTwo?: RuleOfTwoProfile;
311
+ /**
312
+ * Register the D6 structured plan tool (`update_plan`, TodoWrite-style)
313
+ * and recite the plan back into each step's prompt (attention
314
+ * recitation). The plan is journaled in `RunState.todos` and survives
315
+ * resume. Default `false` - off keeps the tool surface unchanged.
316
+ */
317
+ readonly plan?: boolean;
224
318
  readonly sessionId?: string;
225
319
  readonly userId?: string;
226
320
  readonly deps?: TDeps;
227
321
  }
322
+ /**
323
+ * Outcome of one {@link ResponseVerifier} check (C3).
324
+ *
325
+ * @stable
326
+ */
327
+ type VerifierResult = {
328
+ readonly ok: true;
329
+ } | {
330
+ readonly ok: false;
331
+ readonly feedback: string;
332
+ };
333
+ /**
334
+ * A deterministic check over the model's terminal response (C3). Runs
335
+ * when the loop is about to complete; `ok: false` feeds `feedback` back
336
+ * to the model (as a user message prefixed `[verifier:<id>]`) and the
337
+ * loop continues for up to `AgentConfig.maxVerifierRounds` extra rounds.
338
+ *
339
+ * A verifier that THROWS is treated as passed (and logged): a buggy
340
+ * verifier must never take down a run.
341
+ *
342
+ * @stable
343
+ */
344
+ interface ResponseVerifier {
345
+ readonly id: string;
346
+ verify(ctx: {
347
+ /** The model's terminal text output (raw, pre-structured-parse). */
348
+ readonly output: string;
349
+ readonly state: RunState;
350
+ readonly stepNumber: number;
351
+ }): VerifierResult | Promise<VerifierResult>;
352
+ }
228
353
  /**
229
354
  * Single approval decision attached to a {@link ResumeDirective}.
230
355
  * Mirrors the directive surface the HITL caller supplies on resume
@@ -268,7 +393,27 @@ interface AgentCallOptions<TDeps> {
268
393
  * `agent.run(...)` call suspended.
269
394
  */
270
395
  readonly directive?: ResumeDirective;
396
+ /**
397
+ * Per-run capability restriction (D2) - overrides
398
+ * {@link AgentConfig.capability} for this invocation. See that field
399
+ * for semantics. Not persisted in `RunState`: re-supply it when
400
+ * resuming a suspended run.
401
+ */
402
+ readonly capability?: AgentCapability;
271
403
  }
404
+ /**
405
+ * Run-level capability restriction (D2 - the single-writer constraint
406
+ * from multi-agent practice). `'read-only'` makes the run
407
+ * side-effect-free by construction: writer tools (`side-effecting` /
408
+ * `external-stateful`) and handoffs are never advertised to the model,
409
+ * and the tool executor deterministically blocks any writer call the
410
+ * model fabricates anyway (`capability_blocked`). Use it to run
411
+ * parallel research / explorer sub-agents while exactly one agent in
412
+ * the topology keeps write capability.
413
+ *
414
+ * @stable
415
+ */
416
+ type AgentCapability = 'read-only';
272
417
  /**
273
418
  * `agent.toTool({...})` options.
274
419
  *
@@ -282,12 +427,38 @@ interface AgentToToolOptions {
282
427
  * Shapes the sub-agent seed from the parent history (AG-17): when
283
428
  * supplied, the sub-agent is seeded with
284
429
  * `[...inputFilter(parentMessages), { role: 'user', content: input }]`.
285
- * Without a filter the sub-agent sees ONLY the input string no
430
+ * Without a filter the sub-agent sees ONLY the input string - no
286
431
  * parent conversation crosses the boundary (least authority by
287
432
  * construction; there is no secret-inheritance mechanism at this
288
433
  * boundary at all).
289
434
  */
290
435
  readonly inputFilter?: HandoffFilter;
436
+ /**
437
+ * Run the sub-agent under a restricted capability (D2): a
438
+ * `'read-only'` worker cannot execute or advertise writer tools. The
439
+ * orchestrator-worker recipe is `parent (full capability) + workers
440
+ * via toTool({ capability: 'read-only', contextFold: true })`.
441
+ */
442
+ readonly capability?: AgentCapability;
443
+ /**
444
+ * Context folding at the sub-agent boundary (D2): instead of the raw
445
+ * final output, the parent receives a compact distilled outcome -
446
+ * status, step/tool-call counts, tools used, and the final text
447
+ * clamped to `maxChars` (default 2000). Keeps tool-heavy child runs
448
+ * from flooding the parent window. Default off (raw output).
449
+ */
450
+ readonly contextFold?: boolean | {
451
+ readonly maxChars?: number;
452
+ };
453
+ /**
454
+ * Propagate the child run's coarse taint flags across the fold (D2,
455
+ * default `true`): when the child saw untrusted / sensitive content,
456
+ * the tool result carries a widen-only `taint` override
457
+ * (`sourceKind: 'sub-agent'`) that re-arms the PARENT's data-flow
458
+ * ledger. A no-op when the parent has no `dataFlowPolicy`. Set
459
+ * `false` only for children whose inputs are fully trusted.
460
+ */
461
+ readonly propagateTaint?: boolean;
291
462
  }
292
463
  /**
293
464
  * Cancellation options accepted by `agent.abort({...})`.
@@ -306,9 +477,9 @@ interface AbortOptions {
306
477
  * What to do with approvals that were already requested but not
307
478
  * resolved at abort time.
308
479
  *
309
- * - `'deny'` (default) auto-deny pending approvals.
310
- * - `'hold'` keep the approvals on `RunState.pendingApprovals`.
311
- * - `'fail'` reject the run with `RunError(code: 'run-aborted')`.
480
+ * - `'deny'` (default) - auto-deny pending approvals.
481
+ * - `'hold'` - keep the approvals on `RunState.pendingApprovals`.
482
+ * - `'fail'` - reject the run with `RunError(code: 'run-aborted')`.
312
483
  */
313
484
  readonly onPendingApprovals?: 'deny' | 'hold' | 'fail';
314
485
  }
@@ -345,7 +516,7 @@ interface CompactionApiResult {
345
516
  /**
346
517
  * Per-call shape accepted by `Agent.fanOut(...)`. Mirrors the
347
518
  * pure-function {@link FanOutOptions} but omits the runtime-supplied
348
- * identifiers the `Agent` instance carries those.
519
+ * identifiers - the `Agent` instance carries those.
349
520
  *
350
521
  * @stable
351
522
  */
@@ -389,7 +560,7 @@ interface Agent<TDeps = unknown, TOutput = string> {
389
560
  * Convenience wrapper around the standalone `runFanOut(...)`. The
390
561
  * returned `FanOutResult` carries per-child status + the merged
391
562
  * output. Per-child failures are captured in `children[].status`
392
- * this method never throws on a child failure (the merge
563
+ * - this method never throws on a child failure (the merge
393
564
  * strategy decides whether to propagate).
394
565
  */
395
566
  fanOut<TFanOutOutput = unknown>(options: AgentFanOutOptions<TFanOutOutput>): Promise<FanOutResult<TFanOutOutput>>;
@@ -409,5 +580,5 @@ interface Agent<TDeps = unknown, TOutput = string> {
409
580
  readonly registry?: ToolRegistry;
410
581
  }
411
582
  //#endregion
412
- export { AbortOptions, Agent, AgentCallOptions, AgentConfig, AgentInput, AgentToToolOptions, CompactOptions, CompactionApiResult, OutputSpec, PostCompactionHook$1 as PostCompactionHook, PrepareStepHook, PrepareStepOverrides, ResumeDirective, SkillsRegistryLike };
583
+ export { AbortOptions, Agent, AgentCallOptions, AgentCapability, AgentConfig, AgentInput, AgentToToolOptions, CompactOptions, CompactionApiResult, OutputSpec, PostCompactionHook$1 as PostCompactionHook, PrepareStepHook, PrepareStepOverrides, ResponseVerifier, ResumeDirective, SkillsRegistryLike, VerifierResult };
413
584
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphorin/agent",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Agent runtime for the Graphorin framework: createAgent({...}) factory, the typed model -> tool calls -> model loop, the AgentEvent<TOutput> discriminated event stream, steering / followUp queues, durable HITL approvals via runStateToJSON / runStateFromJSON, multi-agent handoffs (Agent.toTool, filter library, secrets isolation), agent-level model fallback chain, intra-loop reasoning preservation, post-compaction hook lifecycle, agent-step-level fan-out (Agent.fanOut + evaluatorOptimizer + progress artifacts), per-tool model-tier hint resolution, lateral-leak defense layer (CausalityMonitor + MergeAgentSidewaysInjectionGuard + protocol-guard), inbound-content sanitization preamble injection, and AbortSignal-aware hard-kill / drain cancellation. Created and maintained by Oleksiy Stepurenko.",
5
5
  "license": "MIT",
6
6
  "author": "Oleksiy Stepurenko",
@@ -93,18 +93,21 @@
93
93
  "LICENSE"
94
94
  ],
95
95
  "dependencies": {
96
- "@graphorin/core": "0.5.0",
97
- "@graphorin/memory": "0.5.0",
98
- "@graphorin/observability": "0.5.0",
99
- "@graphorin/provider": "0.5.0",
100
- "@graphorin/security": "0.5.0",
101
- "@graphorin/skills": "0.5.0",
102
- "@graphorin/tools": "0.5.0"
96
+ "@graphorin/core": "0.6.1",
97
+ "@graphorin/memory": "0.6.1",
98
+ "@graphorin/observability": "0.6.1",
99
+ "@graphorin/provider": "0.6.1",
100
+ "@graphorin/security": "0.6.1",
101
+ "@graphorin/skills": "0.6.1",
102
+ "@graphorin/tools": "0.6.1"
103
103
  },
104
104
  "publishConfig": {
105
105
  "access": "public",
106
106
  "provenance": true
107
107
  },
108
+ "devDependencies": {
109
+ "zod": "^3.25.0"
110
+ },
108
111
  "scripts": {
109
112
  "build": "tsdown",
110
113
  "typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",