@orangecheck/agent-core 2.4.0 → 2.5.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/dist/scope.js CHANGED
@@ -11,7 +11,12 @@ var REGISTERED_SCOPES = {
11
11
  "ln:send": { keys: ["max_sats", "node", "max_fee_sats"] },
12
12
  "mcp:invoke": { keys: ["server", "tool", "max_invocations"] },
13
13
  // oc-pledge-protocol SPEC §7.3.
14
- "pledge:create": { keys: ["max_bond_sats", "mechanism", "counterparty"] }
14
+ "pledge:create": { keys: ["max_bond_sats", "mechanism", "counterparty"] },
15
+ // The agent-stack adapters (agent-anthropic, -openai, -vercel, -langgraph).
16
+ "anthropic:tool": { keys: ["name"] },
17
+ "openai:function": { keys: ["name"] },
18
+ "vercel:tool": { keys: ["verb"] },
19
+ "langgraph:tool": { keys: ["verb"] }
15
20
  };
16
21
  var NUMERIC_KEYS = /* @__PURE__ */ new Set([
17
22
  "max_bytes",
package/dist/scope.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/scope.ts"],"names":[],"mappings":"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","file":"scope.js","sourcesContent":["// Scope grammar, canonicalization, and sub-scope relation. See SPEC.md §7.\n//\n// A scope is <product>:<verb>(<constraint-list>).\n// Constraints are <key><op><value>, op ∈ { =, !=, <, <=, >, >=, * }.\n// Canonical form: constraints sorted by key; no whitespace.\n\nexport type ScopeOp = '=' | '!=' | '<' | '<=' | '>' | '>=' | '*';\n\nexport interface ScopeConstraint {\n key: string;\n op: ScopeOp;\n /** `undefined` for the wildcard `*` op; otherwise the raw textual value (unquoted). */\n value: string | undefined;\n /** True if the value was supplied as a quoted string; preserved for round-trip fidelity. */\n quoted: boolean;\n}\n\nexport interface Scope {\n product: string;\n verb: string;\n constraints: ScopeConstraint[];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Registered products/verbs (SPEC §7.3) and constraint keys (SPEC §7.6).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const REGISTERED_SCOPES: Record<string, { keys: string[] }> = {\n 'lock:seal': { keys: ['recipient', 'mime', 'max_bytes'] },\n 'lock:chat': { keys: ['recipient', 'max_bytes_per_msg', 'max_msgs'] },\n 'stamp:sign': { keys: ['mime', 'max_bytes', 'content_hash_prefix'] },\n 'vote:cast': { keys: ['poll_id', 'choice'] },\n 'nostr:publish': { keys: ['kind', 'relay', 'max_bytes'] },\n 'http:request': { keys: ['origin', 'method', 'max_rps', 'max_bytes_out'] },\n 'ln:send': { keys: ['max_sats', 'node', 'max_fee_sats'] },\n 'mcp:invoke': { keys: ['server', 'tool', 'max_invocations'] },\n // oc-pledge-protocol SPEC §7.3.\n 'pledge:create': { keys: ['max_bond_sats', 'mechanism', 'counterparty'] },\n};\n\n/** Keys whose values are compared numerically for sub-scope ordering. */\nconst NUMERIC_KEYS = new Set<string>([\n 'max_bytes',\n 'max_bytes_per_msg',\n 'max_msgs',\n 'max_bytes_out',\n 'max_rps',\n 'max_sats',\n 'max_fee_sats',\n 'max_invocations',\n 'max_bond_sats',\n 'kind',\n]);\n\nconst IDENT_RE = /^[a-z][a-z0-9_]*$/;\nconst BARE_TOKEN_RE = /^[A-Za-z0-9_.:/@+\\-]+$/;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Parse\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ScopeParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ScopeParseError';\n }\n}\n\nexport function parseScope(input: string): Scope {\n if (typeof input !== 'string' || input.length === 0) {\n throw new ScopeParseError('scope must be a non-empty string');\n }\n if (/\\s/.test(input)) {\n throw new ScopeParseError(`scope may not contain whitespace: ${JSON.stringify(input)}`);\n }\n\n const colonIdx = input.indexOf(':');\n if (colonIdx < 0) throw new ScopeParseError('scope missing \"product:verb\" separator');\n\n const product = input.slice(0, colonIdx);\n if (!IDENT_RE.test(product)) throw new ScopeParseError(`invalid product: ${product}`);\n\n const rest = input.slice(colonIdx + 1);\n const parenIdx = rest.indexOf('(');\n\n let verb: string;\n let constraintText = '';\n if (parenIdx < 0) {\n verb = rest;\n } else {\n verb = rest.slice(0, parenIdx);\n if (!rest.endsWith(')')) throw new ScopeParseError('scope constraint list must end with \")\"');\n constraintText = rest.slice(parenIdx + 1, -1);\n }\n if (!IDENT_RE.test(verb)) throw new ScopeParseError(`invalid verb: ${verb}`);\n\n const constraints: ScopeConstraint[] = [];\n if (constraintText.length > 0) {\n for (const piece of splitTopLevelCommas(constraintText)) {\n constraints.push(parseConstraint(piece));\n }\n }\n\n // No duplicate keys.\n const seen = new Set<string>();\n for (const c of constraints) {\n if (seen.has(c.key)) throw new ScopeParseError(`duplicate constraint key: ${c.key}`);\n seen.add(c.key);\n }\n\n return { product, verb, constraints };\n}\n\nfunction splitTopLevelCommas(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let inQuotes = false;\n let start = 0;\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (inQuotes) {\n if (ch === '\\\\' && i + 1 < text.length) {\n i++;\n continue;\n }\n if (ch === '\"') inQuotes = false;\n continue;\n }\n if (ch === '\"') {\n inQuotes = true;\n continue;\n }\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n else if (ch === ',' && depth === 0) {\n out.push(text.slice(start, i));\n start = i + 1;\n }\n }\n out.push(text.slice(start));\n return out;\n}\n\nfunction parseConstraint(piece: string): ScopeConstraint {\n if (piece.length === 0) throw new ScopeParseError('empty constraint');\n\n // The `*` op (wildcard) is an op with no value. Recognized by \"key=*\" form.\n // SPEC uses \"key=*\"; we also accept \"key*\" as legacy alias.\n // Ops in descending length so \">=\" beats \">\" and \"!=\" beats \"!\".\n const OPS: ScopeOp[] = ['>=', '<=', '!=', '=', '>', '<'];\n\n // Special-case wildcard: \"key=*\" or \"key*\".\n const wildcardMatch = /^([a-z][a-z0-9_]*)(?:=\\*|\\*)$/.exec(piece);\n if (wildcardMatch) {\n return { key: wildcardMatch[1]!, op: '*', value: undefined, quoted: false };\n }\n\n for (const op of OPS) {\n const idx = piece.indexOf(op);\n if (idx <= 0) continue; // key must come first and be non-empty\n const key = piece.slice(0, idx);\n if (!IDENT_RE.test(key)) continue;\n const raw = piece.slice(idx + op.length);\n const { value, quoted } = parseValue(raw);\n return { key, op, value, quoted };\n }\n throw new ScopeParseError(`constraint missing operator: ${piece}`);\n}\n\nfunction parseValue(raw: string): { value: string; quoted: boolean } {\n if (raw.length === 0) throw new ScopeParseError('constraint value is empty');\n if (raw.startsWith('\"')) {\n if (!raw.endsWith('\"') || raw.length < 2) {\n throw new ScopeParseError(`unterminated quoted value: ${raw}`);\n }\n let v = '';\n for (let i = 1; i < raw.length - 1; i++) {\n const ch = raw[i]!;\n if (ch === '\\\\' && i + 1 < raw.length - 1) {\n const next = raw[++i]!;\n v += next;\n } else if (ch === '\"') {\n throw new ScopeParseError(`unescaped quote in value: ${raw}`);\n } else {\n v += ch;\n }\n }\n return { value: v, quoted: true };\n }\n if (!BARE_TOKEN_RE.test(raw)) {\n throw new ScopeParseError(`invalid bare-token value: ${JSON.stringify(raw)}`);\n }\n return { value: raw, quoted: false };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonicalize\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function canonicalizeScope(scope: Scope): string {\n const sorted = [...scope.constraints].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n const parts = sorted.map(serializeConstraint);\n const inner = parts.join(',');\n return `${scope.product}:${scope.verb}${parts.length === 0 ? '' : `(${inner})`}`;\n}\n\nexport function canonicalizeScopeString(input: string): string {\n return canonicalizeScope(parseScope(input));\n}\n\nfunction serializeConstraint(c: ScopeConstraint): string {\n if (c.op === '*') return `${c.key}=*`;\n const v = c.quoted ? quoteValue(c.value ?? '') : c.value ?? '';\n return `${c.key}${c.op}${v}`;\n}\n\nfunction quoteValue(v: string): string {\n let out = '\"';\n for (const ch of v) {\n if (ch === '\"' || ch === '\\\\') out += '\\\\' + ch;\n else out += ch;\n }\n out += '\"';\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Registry-based validation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ValidationOptions {\n /**\n * Strict: reject unknown products/verbs and unknown constraint keys.\n * Permissive: accept unknown products/verbs; ignore unknown keys without treating them as wider.\n * Default: 'strict'.\n */\n mode?: 'strict' | 'permissive';\n}\n\nexport function validateScope(scope: Scope, options: ValidationOptions = {}): void {\n const mode = options.mode ?? 'strict';\n const reg = REGISTERED_SCOPES[`${scope.product}:${scope.verb}`];\n if (!reg) {\n if (mode === 'strict') {\n throw new ScopeParseError(`unregistered scope: ${scope.product}:${scope.verb}`);\n }\n return; // permissive: no further checks\n }\n const registered = new Set(reg.keys);\n for (const c of scope.constraints) {\n if (!registered.has(c.key)) {\n if (mode === 'strict') {\n throw new ScopeParseError(\n `unregistered constraint key for ${scope.product}:${scope.verb}: ${c.key}`\n );\n }\n // permissive: ignore\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-scope relation (SPEC §7.4)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Is `exercised` a sub-scope of `granted`?\n * Returns true iff every constraint of `granted` admits the corresponding constraint\n * (or absence) in `exercised`, per SPEC §7.4.\n */\nexport function isSubScope(exercised: Scope, granted: Scope): boolean {\n if (exercised.product !== granted.product) return false;\n if (exercised.verb !== granted.verb) return false;\n\n const exIndex = new Map<string, ScopeConstraint>();\n for (const c of exercised.constraints) exIndex.set(c.key, c);\n\n for (const g of granted.constraints) {\n const ex = exIndex.get(g.key);\n if (g.op === '*') continue; // wildcard: no requirement\n\n if (g.op === '=') {\n if (!ex) return false;\n if (ex.op !== '=' || ex.value !== g.value) return false;\n continue;\n }\n\n if (g.op === '!=') {\n if (!ex) return false;\n if (ex.op === '=' && ex.value !== g.value) continue;\n if (ex.op === '!=' && ex.value === g.value) continue;\n return false;\n }\n\n // Ordered ops: >=, <=, >, <. Exercised's implied range must be ⊆ granted's.\n if (g.op === '<' || g.op === '<=' || g.op === '>' || g.op === '>=') {\n if (!ex) return false;\n if (!NUMERIC_KEYS.has(g.key)) return false;\n if (ex.op === '*') return false;\n if (ex.value === undefined || g.value === undefined) return false;\n if (!rangeSubset(ex, g)) return false;\n continue;\n }\n }\n return true;\n}\n\nfunction rangeSubset(ex: ScopeConstraint, g: ScopeConstraint): boolean {\n const exRange = opToRange(ex);\n const gRange = opToRange(g);\n if (!exRange || !gRange) return false;\n return gRange.lo <= exRange.lo && exRange.hi <= gRange.hi;\n}\n\n/**\n * SPEC §7.4: \"values MUST be decimal integers for ordered ops\".\n *\n * `Number()` is far more permissive than that and accepts `1e3`, `0x3e8`,\n * `+1000`, `\" 1000 \"`, `1.5` — and `\"\"`, which becomes 0, turning an empty\n * constraint into a silent zero bound. A verifier using a strict decimal parser\n * rejects every one of those, so the same bytes produced different accept/reject\n * answers on different implementations. SECURITY §5 requires they not.\n */\nfunction parseDecimalInteger(value: string): number | null {\n if (!/^-?\\d+$/.test(value)) return null;\n const n = Number(value);\n return Number.isSafeInteger(n) ? n : null;\n}\n\nfunction opToRange(c: ScopeConstraint): { lo: number; hi: number } | null {\n if (c.value === undefined) return null;\n const n = parseDecimalInteger(c.value);\n if (n === null) return null;\n switch (c.op) {\n case '=':\n return { lo: n, hi: n };\n case '<':\n return { lo: -Infinity, hi: n - 1 }; // integers only\n case '<=':\n return { lo: -Infinity, hi: n };\n case '>':\n return { lo: n + 1, hi: Infinity };\n case '>=':\n return { lo: n, hi: Infinity };\n default:\n return null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/scope.ts"],"names":[],"mappings":"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","file":"scope.js","sourcesContent":["// Scope grammar, canonicalization, and sub-scope relation. See SPEC.md §7.\n//\n// A scope is <product>:<verb>(<constraint-list>).\n// Constraints are <key><op><value>, op ∈ { =, !=, <, <=, >, >=, * }.\n// Canonical form: constraints sorted by key; no whitespace.\n\nexport type ScopeOp = '=' | '!=' | '<' | '<=' | '>' | '>=' | '*';\n\nexport interface ScopeConstraint {\n key: string;\n op: ScopeOp;\n /** `undefined` for the wildcard `*` op; otherwise the raw textual value (unquoted). */\n value: string | undefined;\n /** True if the value was supplied as a quoted string; preserved for round-trip fidelity. */\n quoted: boolean;\n}\n\nexport interface Scope {\n product: string;\n verb: string;\n constraints: ScopeConstraint[];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Registered products/verbs (SPEC §7.3) and constraint keys (SPEC §7.6).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const REGISTERED_SCOPES: Record<string, { keys: string[] }> = {\n 'lock:seal': { keys: ['recipient', 'mime', 'max_bytes'] },\n 'lock:chat': { keys: ['recipient', 'max_bytes_per_msg', 'max_msgs'] },\n 'stamp:sign': { keys: ['mime', 'max_bytes', 'content_hash_prefix'] },\n 'vote:cast': { keys: ['poll_id', 'choice'] },\n 'nostr:publish': { keys: ['kind', 'relay', 'max_bytes'] },\n 'http:request': { keys: ['origin', 'method', 'max_rps', 'max_bytes_out'] },\n 'ln:send': { keys: ['max_sats', 'node', 'max_fee_sats'] },\n 'mcp:invoke': { keys: ['server', 'tool', 'max_invocations'] },\n // oc-pledge-protocol SPEC §7.3.\n 'pledge:create': { keys: ['max_bond_sats', 'mechanism', 'counterparty'] },\n // The agent-stack adapters (agent-anthropic, -openai, -vercel, -langgraph).\n 'anthropic:tool': { keys: ['name'] },\n 'openai:function': { keys: ['name'] },\n 'vercel:tool': { keys: ['verb'] },\n 'langgraph:tool': { keys: ['verb'] },\n};\n\n/** Keys whose values are compared numerically for sub-scope ordering. */\nconst NUMERIC_KEYS = new Set<string>([\n 'max_bytes',\n 'max_bytes_per_msg',\n 'max_msgs',\n 'max_bytes_out',\n 'max_rps',\n 'max_sats',\n 'max_fee_sats',\n 'max_invocations',\n 'max_bond_sats',\n 'kind',\n]);\n\nconst IDENT_RE = /^[a-z][a-z0-9_]*$/;\nconst BARE_TOKEN_RE = /^[A-Za-z0-9_.:/@+\\-]+$/;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Parse\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ScopeParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ScopeParseError';\n }\n}\n\nexport function parseScope(input: string): Scope {\n if (typeof input !== 'string' || input.length === 0) {\n throw new ScopeParseError('scope must be a non-empty string');\n }\n if (/\\s/.test(input)) {\n throw new ScopeParseError(`scope may not contain whitespace: ${JSON.stringify(input)}`);\n }\n\n const colonIdx = input.indexOf(':');\n if (colonIdx < 0) throw new ScopeParseError('scope missing \"product:verb\" separator');\n\n const product = input.slice(0, colonIdx);\n if (!IDENT_RE.test(product)) throw new ScopeParseError(`invalid product: ${product}`);\n\n const rest = input.slice(colonIdx + 1);\n const parenIdx = rest.indexOf('(');\n\n let verb: string;\n let constraintText = '';\n if (parenIdx < 0) {\n verb = rest;\n } else {\n verb = rest.slice(0, parenIdx);\n if (!rest.endsWith(')')) throw new ScopeParseError('scope constraint list must end with \")\"');\n constraintText = rest.slice(parenIdx + 1, -1);\n }\n if (!IDENT_RE.test(verb)) throw new ScopeParseError(`invalid verb: ${verb}`);\n\n const constraints: ScopeConstraint[] = [];\n if (constraintText.length > 0) {\n for (const piece of splitTopLevelCommas(constraintText)) {\n constraints.push(parseConstraint(piece));\n }\n }\n\n // No duplicate keys.\n const seen = new Set<string>();\n for (const c of constraints) {\n if (seen.has(c.key)) throw new ScopeParseError(`duplicate constraint key: ${c.key}`);\n seen.add(c.key);\n }\n\n return { product, verb, constraints };\n}\n\nfunction splitTopLevelCommas(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let inQuotes = false;\n let start = 0;\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (inQuotes) {\n if (ch === '\\\\' && i + 1 < text.length) {\n i++;\n continue;\n }\n if (ch === '\"') inQuotes = false;\n continue;\n }\n if (ch === '\"') {\n inQuotes = true;\n continue;\n }\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n else if (ch === ',' && depth === 0) {\n out.push(text.slice(start, i));\n start = i + 1;\n }\n }\n out.push(text.slice(start));\n return out;\n}\n\nfunction parseConstraint(piece: string): ScopeConstraint {\n if (piece.length === 0) throw new ScopeParseError('empty constraint');\n\n // The `*` op (wildcard) is an op with no value. Recognized by \"key=*\" form.\n // SPEC uses \"key=*\"; we also accept \"key*\" as legacy alias.\n // Ops in descending length so \">=\" beats \">\" and \"!=\" beats \"!\".\n const OPS: ScopeOp[] = ['>=', '<=', '!=', '=', '>', '<'];\n\n // Special-case wildcard: \"key=*\" or \"key*\".\n const wildcardMatch = /^([a-z][a-z0-9_]*)(?:=\\*|\\*)$/.exec(piece);\n if (wildcardMatch) {\n return { key: wildcardMatch[1]!, op: '*', value: undefined, quoted: false };\n }\n\n for (const op of OPS) {\n const idx = piece.indexOf(op);\n if (idx <= 0) continue; // key must come first and be non-empty\n const key = piece.slice(0, idx);\n if (!IDENT_RE.test(key)) continue;\n const raw = piece.slice(idx + op.length);\n const { value, quoted } = parseValue(raw);\n return { key, op, value, quoted };\n }\n throw new ScopeParseError(`constraint missing operator: ${piece}`);\n}\n\nfunction parseValue(raw: string): { value: string; quoted: boolean } {\n if (raw.length === 0) throw new ScopeParseError('constraint value is empty');\n if (raw.startsWith('\"')) {\n if (!raw.endsWith('\"') || raw.length < 2) {\n throw new ScopeParseError(`unterminated quoted value: ${raw}`);\n }\n let v = '';\n for (let i = 1; i < raw.length - 1; i++) {\n const ch = raw[i]!;\n if (ch === '\\\\' && i + 1 < raw.length - 1) {\n const next = raw[++i]!;\n v += next;\n } else if (ch === '\"') {\n throw new ScopeParseError(`unescaped quote in value: ${raw}`);\n } else {\n v += ch;\n }\n }\n return { value: v, quoted: true };\n }\n if (!BARE_TOKEN_RE.test(raw)) {\n throw new ScopeParseError(`invalid bare-token value: ${JSON.stringify(raw)}`);\n }\n return { value: raw, quoted: false };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonicalize\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function canonicalizeScope(scope: Scope): string {\n const sorted = [...scope.constraints].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n const parts = sorted.map(serializeConstraint);\n const inner = parts.join(',');\n return `${scope.product}:${scope.verb}${parts.length === 0 ? '' : `(${inner})`}`;\n}\n\nexport function canonicalizeScopeString(input: string): string {\n return canonicalizeScope(parseScope(input));\n}\n\nfunction serializeConstraint(c: ScopeConstraint): string {\n if (c.op === '*') return `${c.key}=*`;\n const v = c.quoted ? quoteValue(c.value ?? '') : c.value ?? '';\n return `${c.key}${c.op}${v}`;\n}\n\nfunction quoteValue(v: string): string {\n let out = '\"';\n for (const ch of v) {\n if (ch === '\"' || ch === '\\\\') out += '\\\\' + ch;\n else out += ch;\n }\n out += '\"';\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Registry-based validation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ValidationOptions {\n /**\n * Strict: reject unknown products/verbs and unknown constraint keys.\n * Permissive: accept unknown products/verbs; ignore unknown keys without treating them as wider.\n * Default: 'strict'.\n */\n mode?: 'strict' | 'permissive';\n}\n\nexport function validateScope(scope: Scope, options: ValidationOptions = {}): void {\n const mode = options.mode ?? 'strict';\n const reg = REGISTERED_SCOPES[`${scope.product}:${scope.verb}`];\n if (!reg) {\n if (mode === 'strict') {\n throw new ScopeParseError(`unregistered scope: ${scope.product}:${scope.verb}`);\n }\n return; // permissive: no further checks\n }\n const registered = new Set(reg.keys);\n for (const c of scope.constraints) {\n if (!registered.has(c.key)) {\n if (mode === 'strict') {\n throw new ScopeParseError(\n `unregistered constraint key for ${scope.product}:${scope.verb}: ${c.key}`\n );\n }\n // permissive: ignore\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-scope relation (SPEC §7.4)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Is `exercised` a sub-scope of `granted`?\n * Returns true iff every constraint of `granted` admits the corresponding constraint\n * (or absence) in `exercised`, per SPEC §7.4.\n */\nexport function isSubScope(exercised: Scope, granted: Scope): boolean {\n if (exercised.product !== granted.product) return false;\n if (exercised.verb !== granted.verb) return false;\n\n const exIndex = new Map<string, ScopeConstraint>();\n for (const c of exercised.constraints) exIndex.set(c.key, c);\n\n for (const g of granted.constraints) {\n const ex = exIndex.get(g.key);\n if (g.op === '*') continue; // wildcard: no requirement\n\n if (g.op === '=') {\n if (!ex) return false;\n if (ex.op !== '=' || ex.value !== g.value) return false;\n continue;\n }\n\n if (g.op === '!=') {\n if (!ex) return false;\n if (ex.op === '=' && ex.value !== g.value) continue;\n if (ex.op === '!=' && ex.value === g.value) continue;\n return false;\n }\n\n // Ordered ops: >=, <=, >, <. Exercised's implied range must be ⊆ granted's.\n if (g.op === '<' || g.op === '<=' || g.op === '>' || g.op === '>=') {\n if (!ex) return false;\n if (!NUMERIC_KEYS.has(g.key)) return false;\n if (ex.op === '*') return false;\n if (ex.value === undefined || g.value === undefined) return false;\n if (!rangeSubset(ex, g)) return false;\n continue;\n }\n }\n return true;\n}\n\nfunction rangeSubset(ex: ScopeConstraint, g: ScopeConstraint): boolean {\n const exRange = opToRange(ex);\n const gRange = opToRange(g);\n if (!exRange || !gRange) return false;\n return gRange.lo <= exRange.lo && exRange.hi <= gRange.hi;\n}\n\n/**\n * SPEC §7.4: \"values MUST be decimal integers for ordered ops\".\n *\n * `Number()` is far more permissive than that and accepts `1e3`, `0x3e8`,\n * `+1000`, `\" 1000 \"`, `1.5` — and `\"\"`, which becomes 0, turning an empty\n * constraint into a silent zero bound. A verifier using a strict decimal parser\n * rejects every one of those, so the same bytes produced different accept/reject\n * answers on different implementations. SECURITY §5 requires they not.\n */\nfunction parseDecimalInteger(value: string): number | null {\n if (!/^-?\\d+$/.test(value)) return null;\n const n = Number(value);\n return Number.isSafeInteger(n) ? n : null;\n}\n\nfunction opToRange(c: ScopeConstraint): { lo: number; hi: number } | null {\n if (c.value === undefined) return null;\n const n = parseDecimalInteger(c.value);\n if (n === null) return null;\n switch (c.op) {\n case '=':\n return { lo: n, hi: n };\n case '<':\n return { lo: -Infinity, hi: n - 1 }; // integers only\n case '<=':\n return { lo: -Infinity, hi: n };\n case '>':\n return { lo: n + 1, hi: Infinity };\n case '>=':\n return { lo: n, hi: Infinity };\n default:\n return null;\n }\n}\n"]}
package/dist/scope.mjs CHANGED
@@ -9,7 +9,12 @@ var REGISTERED_SCOPES = {
9
9
  "ln:send": { keys: ["max_sats", "node", "max_fee_sats"] },
10
10
  "mcp:invoke": { keys: ["server", "tool", "max_invocations"] },
11
11
  // oc-pledge-protocol SPEC §7.3.
12
- "pledge:create": { keys: ["max_bond_sats", "mechanism", "counterparty"] }
12
+ "pledge:create": { keys: ["max_bond_sats", "mechanism", "counterparty"] },
13
+ // The agent-stack adapters (agent-anthropic, -openai, -vercel, -langgraph).
14
+ "anthropic:tool": { keys: ["name"] },
15
+ "openai:function": { keys: ["name"] },
16
+ "vercel:tool": { keys: ["verb"] },
17
+ "langgraph:tool": { keys: ["verb"] }
13
18
  };
14
19
  var NUMERIC_KEYS = /* @__PURE__ */ new Set([
15
20
  "max_bytes",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/scope.ts"],"names":[],"mappings":"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","file":"scope.mjs","sourcesContent":["// Scope grammar, canonicalization, and sub-scope relation. See SPEC.md §7.\n//\n// A scope is <product>:<verb>(<constraint-list>).\n// Constraints are <key><op><value>, op ∈ { =, !=, <, <=, >, >=, * }.\n// Canonical form: constraints sorted by key; no whitespace.\n\nexport type ScopeOp = '=' | '!=' | '<' | '<=' | '>' | '>=' | '*';\n\nexport interface ScopeConstraint {\n key: string;\n op: ScopeOp;\n /** `undefined` for the wildcard `*` op; otherwise the raw textual value (unquoted). */\n value: string | undefined;\n /** True if the value was supplied as a quoted string; preserved for round-trip fidelity. */\n quoted: boolean;\n}\n\nexport interface Scope {\n product: string;\n verb: string;\n constraints: ScopeConstraint[];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Registered products/verbs (SPEC §7.3) and constraint keys (SPEC §7.6).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const REGISTERED_SCOPES: Record<string, { keys: string[] }> = {\n 'lock:seal': { keys: ['recipient', 'mime', 'max_bytes'] },\n 'lock:chat': { keys: ['recipient', 'max_bytes_per_msg', 'max_msgs'] },\n 'stamp:sign': { keys: ['mime', 'max_bytes', 'content_hash_prefix'] },\n 'vote:cast': { keys: ['poll_id', 'choice'] },\n 'nostr:publish': { keys: ['kind', 'relay', 'max_bytes'] },\n 'http:request': { keys: ['origin', 'method', 'max_rps', 'max_bytes_out'] },\n 'ln:send': { keys: ['max_sats', 'node', 'max_fee_sats'] },\n 'mcp:invoke': { keys: ['server', 'tool', 'max_invocations'] },\n // oc-pledge-protocol SPEC §7.3.\n 'pledge:create': { keys: ['max_bond_sats', 'mechanism', 'counterparty'] },\n};\n\n/** Keys whose values are compared numerically for sub-scope ordering. */\nconst NUMERIC_KEYS = new Set<string>([\n 'max_bytes',\n 'max_bytes_per_msg',\n 'max_msgs',\n 'max_bytes_out',\n 'max_rps',\n 'max_sats',\n 'max_fee_sats',\n 'max_invocations',\n 'max_bond_sats',\n 'kind',\n]);\n\nconst IDENT_RE = /^[a-z][a-z0-9_]*$/;\nconst BARE_TOKEN_RE = /^[A-Za-z0-9_.:/@+\\-]+$/;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Parse\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ScopeParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ScopeParseError';\n }\n}\n\nexport function parseScope(input: string): Scope {\n if (typeof input !== 'string' || input.length === 0) {\n throw new ScopeParseError('scope must be a non-empty string');\n }\n if (/\\s/.test(input)) {\n throw new ScopeParseError(`scope may not contain whitespace: ${JSON.stringify(input)}`);\n }\n\n const colonIdx = input.indexOf(':');\n if (colonIdx < 0) throw new ScopeParseError('scope missing \"product:verb\" separator');\n\n const product = input.slice(0, colonIdx);\n if (!IDENT_RE.test(product)) throw new ScopeParseError(`invalid product: ${product}`);\n\n const rest = input.slice(colonIdx + 1);\n const parenIdx = rest.indexOf('(');\n\n let verb: string;\n let constraintText = '';\n if (parenIdx < 0) {\n verb = rest;\n } else {\n verb = rest.slice(0, parenIdx);\n if (!rest.endsWith(')')) throw new ScopeParseError('scope constraint list must end with \")\"');\n constraintText = rest.slice(parenIdx + 1, -1);\n }\n if (!IDENT_RE.test(verb)) throw new ScopeParseError(`invalid verb: ${verb}`);\n\n const constraints: ScopeConstraint[] = [];\n if (constraintText.length > 0) {\n for (const piece of splitTopLevelCommas(constraintText)) {\n constraints.push(parseConstraint(piece));\n }\n }\n\n // No duplicate keys.\n const seen = new Set<string>();\n for (const c of constraints) {\n if (seen.has(c.key)) throw new ScopeParseError(`duplicate constraint key: ${c.key}`);\n seen.add(c.key);\n }\n\n return { product, verb, constraints };\n}\n\nfunction splitTopLevelCommas(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let inQuotes = false;\n let start = 0;\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (inQuotes) {\n if (ch === '\\\\' && i + 1 < text.length) {\n i++;\n continue;\n }\n if (ch === '\"') inQuotes = false;\n continue;\n }\n if (ch === '\"') {\n inQuotes = true;\n continue;\n }\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n else if (ch === ',' && depth === 0) {\n out.push(text.slice(start, i));\n start = i + 1;\n }\n }\n out.push(text.slice(start));\n return out;\n}\n\nfunction parseConstraint(piece: string): ScopeConstraint {\n if (piece.length === 0) throw new ScopeParseError('empty constraint');\n\n // The `*` op (wildcard) is an op with no value. Recognized by \"key=*\" form.\n // SPEC uses \"key=*\"; we also accept \"key*\" as legacy alias.\n // Ops in descending length so \">=\" beats \">\" and \"!=\" beats \"!\".\n const OPS: ScopeOp[] = ['>=', '<=', '!=', '=', '>', '<'];\n\n // Special-case wildcard: \"key=*\" or \"key*\".\n const wildcardMatch = /^([a-z][a-z0-9_]*)(?:=\\*|\\*)$/.exec(piece);\n if (wildcardMatch) {\n return { key: wildcardMatch[1]!, op: '*', value: undefined, quoted: false };\n }\n\n for (const op of OPS) {\n const idx = piece.indexOf(op);\n if (idx <= 0) continue; // key must come first and be non-empty\n const key = piece.slice(0, idx);\n if (!IDENT_RE.test(key)) continue;\n const raw = piece.slice(idx + op.length);\n const { value, quoted } = parseValue(raw);\n return { key, op, value, quoted };\n }\n throw new ScopeParseError(`constraint missing operator: ${piece}`);\n}\n\nfunction parseValue(raw: string): { value: string; quoted: boolean } {\n if (raw.length === 0) throw new ScopeParseError('constraint value is empty');\n if (raw.startsWith('\"')) {\n if (!raw.endsWith('\"') || raw.length < 2) {\n throw new ScopeParseError(`unterminated quoted value: ${raw}`);\n }\n let v = '';\n for (let i = 1; i < raw.length - 1; i++) {\n const ch = raw[i]!;\n if (ch === '\\\\' && i + 1 < raw.length - 1) {\n const next = raw[++i]!;\n v += next;\n } else if (ch === '\"') {\n throw new ScopeParseError(`unescaped quote in value: ${raw}`);\n } else {\n v += ch;\n }\n }\n return { value: v, quoted: true };\n }\n if (!BARE_TOKEN_RE.test(raw)) {\n throw new ScopeParseError(`invalid bare-token value: ${JSON.stringify(raw)}`);\n }\n return { value: raw, quoted: false };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonicalize\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function canonicalizeScope(scope: Scope): string {\n const sorted = [...scope.constraints].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n const parts = sorted.map(serializeConstraint);\n const inner = parts.join(',');\n return `${scope.product}:${scope.verb}${parts.length === 0 ? '' : `(${inner})`}`;\n}\n\nexport function canonicalizeScopeString(input: string): string {\n return canonicalizeScope(parseScope(input));\n}\n\nfunction serializeConstraint(c: ScopeConstraint): string {\n if (c.op === '*') return `${c.key}=*`;\n const v = c.quoted ? quoteValue(c.value ?? '') : c.value ?? '';\n return `${c.key}${c.op}${v}`;\n}\n\nfunction quoteValue(v: string): string {\n let out = '\"';\n for (const ch of v) {\n if (ch === '\"' || ch === '\\\\') out += '\\\\' + ch;\n else out += ch;\n }\n out += '\"';\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Registry-based validation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ValidationOptions {\n /**\n * Strict: reject unknown products/verbs and unknown constraint keys.\n * Permissive: accept unknown products/verbs; ignore unknown keys without treating them as wider.\n * Default: 'strict'.\n */\n mode?: 'strict' | 'permissive';\n}\n\nexport function validateScope(scope: Scope, options: ValidationOptions = {}): void {\n const mode = options.mode ?? 'strict';\n const reg = REGISTERED_SCOPES[`${scope.product}:${scope.verb}`];\n if (!reg) {\n if (mode === 'strict') {\n throw new ScopeParseError(`unregistered scope: ${scope.product}:${scope.verb}`);\n }\n return; // permissive: no further checks\n }\n const registered = new Set(reg.keys);\n for (const c of scope.constraints) {\n if (!registered.has(c.key)) {\n if (mode === 'strict') {\n throw new ScopeParseError(\n `unregistered constraint key for ${scope.product}:${scope.verb}: ${c.key}`\n );\n }\n // permissive: ignore\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-scope relation (SPEC §7.4)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Is `exercised` a sub-scope of `granted`?\n * Returns true iff every constraint of `granted` admits the corresponding constraint\n * (or absence) in `exercised`, per SPEC §7.4.\n */\nexport function isSubScope(exercised: Scope, granted: Scope): boolean {\n if (exercised.product !== granted.product) return false;\n if (exercised.verb !== granted.verb) return false;\n\n const exIndex = new Map<string, ScopeConstraint>();\n for (const c of exercised.constraints) exIndex.set(c.key, c);\n\n for (const g of granted.constraints) {\n const ex = exIndex.get(g.key);\n if (g.op === '*') continue; // wildcard: no requirement\n\n if (g.op === '=') {\n if (!ex) return false;\n if (ex.op !== '=' || ex.value !== g.value) return false;\n continue;\n }\n\n if (g.op === '!=') {\n if (!ex) return false;\n if (ex.op === '=' && ex.value !== g.value) continue;\n if (ex.op === '!=' && ex.value === g.value) continue;\n return false;\n }\n\n // Ordered ops: >=, <=, >, <. Exercised's implied range must be ⊆ granted's.\n if (g.op === '<' || g.op === '<=' || g.op === '>' || g.op === '>=') {\n if (!ex) return false;\n if (!NUMERIC_KEYS.has(g.key)) return false;\n if (ex.op === '*') return false;\n if (ex.value === undefined || g.value === undefined) return false;\n if (!rangeSubset(ex, g)) return false;\n continue;\n }\n }\n return true;\n}\n\nfunction rangeSubset(ex: ScopeConstraint, g: ScopeConstraint): boolean {\n const exRange = opToRange(ex);\n const gRange = opToRange(g);\n if (!exRange || !gRange) return false;\n return gRange.lo <= exRange.lo && exRange.hi <= gRange.hi;\n}\n\n/**\n * SPEC §7.4: \"values MUST be decimal integers for ordered ops\".\n *\n * `Number()` is far more permissive than that and accepts `1e3`, `0x3e8`,\n * `+1000`, `\" 1000 \"`, `1.5` — and `\"\"`, which becomes 0, turning an empty\n * constraint into a silent zero bound. A verifier using a strict decimal parser\n * rejects every one of those, so the same bytes produced different accept/reject\n * answers on different implementations. SECURITY §5 requires they not.\n */\nfunction parseDecimalInteger(value: string): number | null {\n if (!/^-?\\d+$/.test(value)) return null;\n const n = Number(value);\n return Number.isSafeInteger(n) ? n : null;\n}\n\nfunction opToRange(c: ScopeConstraint): { lo: number; hi: number } | null {\n if (c.value === undefined) return null;\n const n = parseDecimalInteger(c.value);\n if (n === null) return null;\n switch (c.op) {\n case '=':\n return { lo: n, hi: n };\n case '<':\n return { lo: -Infinity, hi: n - 1 }; // integers only\n case '<=':\n return { lo: -Infinity, hi: n };\n case '>':\n return { lo: n + 1, hi: Infinity };\n case '>=':\n return { lo: n, hi: Infinity };\n default:\n return null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/scope.ts"],"names":[],"mappings":"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","file":"scope.mjs","sourcesContent":["// Scope grammar, canonicalization, and sub-scope relation. See SPEC.md §7.\n//\n// A scope is <product>:<verb>(<constraint-list>).\n// Constraints are <key><op><value>, op ∈ { =, !=, <, <=, >, >=, * }.\n// Canonical form: constraints sorted by key; no whitespace.\n\nexport type ScopeOp = '=' | '!=' | '<' | '<=' | '>' | '>=' | '*';\n\nexport interface ScopeConstraint {\n key: string;\n op: ScopeOp;\n /** `undefined` for the wildcard `*` op; otherwise the raw textual value (unquoted). */\n value: string | undefined;\n /** True if the value was supplied as a quoted string; preserved for round-trip fidelity. */\n quoted: boolean;\n}\n\nexport interface Scope {\n product: string;\n verb: string;\n constraints: ScopeConstraint[];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Registered products/verbs (SPEC §7.3) and constraint keys (SPEC §7.6).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const REGISTERED_SCOPES: Record<string, { keys: string[] }> = {\n 'lock:seal': { keys: ['recipient', 'mime', 'max_bytes'] },\n 'lock:chat': { keys: ['recipient', 'max_bytes_per_msg', 'max_msgs'] },\n 'stamp:sign': { keys: ['mime', 'max_bytes', 'content_hash_prefix'] },\n 'vote:cast': { keys: ['poll_id', 'choice'] },\n 'nostr:publish': { keys: ['kind', 'relay', 'max_bytes'] },\n 'http:request': { keys: ['origin', 'method', 'max_rps', 'max_bytes_out'] },\n 'ln:send': { keys: ['max_sats', 'node', 'max_fee_sats'] },\n 'mcp:invoke': { keys: ['server', 'tool', 'max_invocations'] },\n // oc-pledge-protocol SPEC §7.3.\n 'pledge:create': { keys: ['max_bond_sats', 'mechanism', 'counterparty'] },\n // The agent-stack adapters (agent-anthropic, -openai, -vercel, -langgraph).\n 'anthropic:tool': { keys: ['name'] },\n 'openai:function': { keys: ['name'] },\n 'vercel:tool': { keys: ['verb'] },\n 'langgraph:tool': { keys: ['verb'] },\n};\n\n/** Keys whose values are compared numerically for sub-scope ordering. */\nconst NUMERIC_KEYS = new Set<string>([\n 'max_bytes',\n 'max_bytes_per_msg',\n 'max_msgs',\n 'max_bytes_out',\n 'max_rps',\n 'max_sats',\n 'max_fee_sats',\n 'max_invocations',\n 'max_bond_sats',\n 'kind',\n]);\n\nconst IDENT_RE = /^[a-z][a-z0-9_]*$/;\nconst BARE_TOKEN_RE = /^[A-Za-z0-9_.:/@+\\-]+$/;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Parse\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ScopeParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ScopeParseError';\n }\n}\n\nexport function parseScope(input: string): Scope {\n if (typeof input !== 'string' || input.length === 0) {\n throw new ScopeParseError('scope must be a non-empty string');\n }\n if (/\\s/.test(input)) {\n throw new ScopeParseError(`scope may not contain whitespace: ${JSON.stringify(input)}`);\n }\n\n const colonIdx = input.indexOf(':');\n if (colonIdx < 0) throw new ScopeParseError('scope missing \"product:verb\" separator');\n\n const product = input.slice(0, colonIdx);\n if (!IDENT_RE.test(product)) throw new ScopeParseError(`invalid product: ${product}`);\n\n const rest = input.slice(colonIdx + 1);\n const parenIdx = rest.indexOf('(');\n\n let verb: string;\n let constraintText = '';\n if (parenIdx < 0) {\n verb = rest;\n } else {\n verb = rest.slice(0, parenIdx);\n if (!rest.endsWith(')')) throw new ScopeParseError('scope constraint list must end with \")\"');\n constraintText = rest.slice(parenIdx + 1, -1);\n }\n if (!IDENT_RE.test(verb)) throw new ScopeParseError(`invalid verb: ${verb}`);\n\n const constraints: ScopeConstraint[] = [];\n if (constraintText.length > 0) {\n for (const piece of splitTopLevelCommas(constraintText)) {\n constraints.push(parseConstraint(piece));\n }\n }\n\n // No duplicate keys.\n const seen = new Set<string>();\n for (const c of constraints) {\n if (seen.has(c.key)) throw new ScopeParseError(`duplicate constraint key: ${c.key}`);\n seen.add(c.key);\n }\n\n return { product, verb, constraints };\n}\n\nfunction splitTopLevelCommas(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let inQuotes = false;\n let start = 0;\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (inQuotes) {\n if (ch === '\\\\' && i + 1 < text.length) {\n i++;\n continue;\n }\n if (ch === '\"') inQuotes = false;\n continue;\n }\n if (ch === '\"') {\n inQuotes = true;\n continue;\n }\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n else if (ch === ',' && depth === 0) {\n out.push(text.slice(start, i));\n start = i + 1;\n }\n }\n out.push(text.slice(start));\n return out;\n}\n\nfunction parseConstraint(piece: string): ScopeConstraint {\n if (piece.length === 0) throw new ScopeParseError('empty constraint');\n\n // The `*` op (wildcard) is an op with no value. Recognized by \"key=*\" form.\n // SPEC uses \"key=*\"; we also accept \"key*\" as legacy alias.\n // Ops in descending length so \">=\" beats \">\" and \"!=\" beats \"!\".\n const OPS: ScopeOp[] = ['>=', '<=', '!=', '=', '>', '<'];\n\n // Special-case wildcard: \"key=*\" or \"key*\".\n const wildcardMatch = /^([a-z][a-z0-9_]*)(?:=\\*|\\*)$/.exec(piece);\n if (wildcardMatch) {\n return { key: wildcardMatch[1]!, op: '*', value: undefined, quoted: false };\n }\n\n for (const op of OPS) {\n const idx = piece.indexOf(op);\n if (idx <= 0) continue; // key must come first and be non-empty\n const key = piece.slice(0, idx);\n if (!IDENT_RE.test(key)) continue;\n const raw = piece.slice(idx + op.length);\n const { value, quoted } = parseValue(raw);\n return { key, op, value, quoted };\n }\n throw new ScopeParseError(`constraint missing operator: ${piece}`);\n}\n\nfunction parseValue(raw: string): { value: string; quoted: boolean } {\n if (raw.length === 0) throw new ScopeParseError('constraint value is empty');\n if (raw.startsWith('\"')) {\n if (!raw.endsWith('\"') || raw.length < 2) {\n throw new ScopeParseError(`unterminated quoted value: ${raw}`);\n }\n let v = '';\n for (let i = 1; i < raw.length - 1; i++) {\n const ch = raw[i]!;\n if (ch === '\\\\' && i + 1 < raw.length - 1) {\n const next = raw[++i]!;\n v += next;\n } else if (ch === '\"') {\n throw new ScopeParseError(`unescaped quote in value: ${raw}`);\n } else {\n v += ch;\n }\n }\n return { value: v, quoted: true };\n }\n if (!BARE_TOKEN_RE.test(raw)) {\n throw new ScopeParseError(`invalid bare-token value: ${JSON.stringify(raw)}`);\n }\n return { value: raw, quoted: false };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonicalize\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function canonicalizeScope(scope: Scope): string {\n const sorted = [...scope.constraints].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n const parts = sorted.map(serializeConstraint);\n const inner = parts.join(',');\n return `${scope.product}:${scope.verb}${parts.length === 0 ? '' : `(${inner})`}`;\n}\n\nexport function canonicalizeScopeString(input: string): string {\n return canonicalizeScope(parseScope(input));\n}\n\nfunction serializeConstraint(c: ScopeConstraint): string {\n if (c.op === '*') return `${c.key}=*`;\n const v = c.quoted ? quoteValue(c.value ?? '') : c.value ?? '';\n return `${c.key}${c.op}${v}`;\n}\n\nfunction quoteValue(v: string): string {\n let out = '\"';\n for (const ch of v) {\n if (ch === '\"' || ch === '\\\\') out += '\\\\' + ch;\n else out += ch;\n }\n out += '\"';\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Registry-based validation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ValidationOptions {\n /**\n * Strict: reject unknown products/verbs and unknown constraint keys.\n * Permissive: accept unknown products/verbs; ignore unknown keys without treating them as wider.\n * Default: 'strict'.\n */\n mode?: 'strict' | 'permissive';\n}\n\nexport function validateScope(scope: Scope, options: ValidationOptions = {}): void {\n const mode = options.mode ?? 'strict';\n const reg = REGISTERED_SCOPES[`${scope.product}:${scope.verb}`];\n if (!reg) {\n if (mode === 'strict') {\n throw new ScopeParseError(`unregistered scope: ${scope.product}:${scope.verb}`);\n }\n return; // permissive: no further checks\n }\n const registered = new Set(reg.keys);\n for (const c of scope.constraints) {\n if (!registered.has(c.key)) {\n if (mode === 'strict') {\n throw new ScopeParseError(\n `unregistered constraint key for ${scope.product}:${scope.verb}: ${c.key}`\n );\n }\n // permissive: ignore\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-scope relation (SPEC §7.4)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Is `exercised` a sub-scope of `granted`?\n * Returns true iff every constraint of `granted` admits the corresponding constraint\n * (or absence) in `exercised`, per SPEC §7.4.\n */\nexport function isSubScope(exercised: Scope, granted: Scope): boolean {\n if (exercised.product !== granted.product) return false;\n if (exercised.verb !== granted.verb) return false;\n\n const exIndex = new Map<string, ScopeConstraint>();\n for (const c of exercised.constraints) exIndex.set(c.key, c);\n\n for (const g of granted.constraints) {\n const ex = exIndex.get(g.key);\n if (g.op === '*') continue; // wildcard: no requirement\n\n if (g.op === '=') {\n if (!ex) return false;\n if (ex.op !== '=' || ex.value !== g.value) return false;\n continue;\n }\n\n if (g.op === '!=') {\n if (!ex) return false;\n if (ex.op === '=' && ex.value !== g.value) continue;\n if (ex.op === '!=' && ex.value === g.value) continue;\n return false;\n }\n\n // Ordered ops: >=, <=, >, <. Exercised's implied range must be ⊆ granted's.\n if (g.op === '<' || g.op === '<=' || g.op === '>' || g.op === '>=') {\n if (!ex) return false;\n if (!NUMERIC_KEYS.has(g.key)) return false;\n if (ex.op === '*') return false;\n if (ex.value === undefined || g.value === undefined) return false;\n if (!rangeSubset(ex, g)) return false;\n continue;\n }\n }\n return true;\n}\n\nfunction rangeSubset(ex: ScopeConstraint, g: ScopeConstraint): boolean {\n const exRange = opToRange(ex);\n const gRange = opToRange(g);\n if (!exRange || !gRange) return false;\n return gRange.lo <= exRange.lo && exRange.hi <= gRange.hi;\n}\n\n/**\n * SPEC §7.4: \"values MUST be decimal integers for ordered ops\".\n *\n * `Number()` is far more permissive than that and accepts `1e3`, `0x3e8`,\n * `+1000`, `\" 1000 \"`, `1.5` — and `\"\"`, which becomes 0, turning an empty\n * constraint into a silent zero bound. A verifier using a strict decimal parser\n * rejects every one of those, so the same bytes produced different accept/reject\n * answers on different implementations. SECURITY §5 requires they not.\n */\nfunction parseDecimalInteger(value: string): number | null {\n if (!/^-?\\d+$/.test(value)) return null;\n const n = Number(value);\n return Number.isSafeInteger(n) ? n : null;\n}\n\nfunction opToRange(c: ScopeConstraint): { lo: number; hi: number } | null {\n if (c.value === undefined) return null;\n const n = parseDecimalInteger(c.value);\n if (n === null) return null;\n switch (c.op) {\n case '=':\n return { lo: n, hi: n };\n case '<':\n return { lo: -Infinity, hi: n - 1 }; // integers only\n case '<=':\n return { lo: -Infinity, hi: n };\n case '>':\n return { lo: n + 1, hi: Infinity };\n case '>=':\n return { lo: n, hi: Infinity };\n default:\n return null;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangecheck/agent-core",
3
- "version": "2.4.0",
3
+ "version": "2.5.1",
4
4
  "description": "OC Agent canonical messages, envelope formats (delegation/action/revocation), scope grammar, and verification. See https://github.com/orangecheck/oc-agent-protocol.",
5
5
  "keywords": [
6
6
  "bitcoin",
@@ -170,7 +170,7 @@ d('federation reject reasons (each invalid case fails for its named reason)', ()
170
170
  });
171
171
  });
172
172
 
173
- // FEDERATION.md §4 / SECURITY §7 item 7: a revoked federation delegation must
173
+ // FEDERATION.md §4 / SECURITY §5 item 7: a revoked federation delegation must
174
174
  // not verify, and skipping the revocation check has to be stated.
175
175
  d('federation delegation revocation check', () => {
176
176
  const base = { skipSignatureVerification: true, skipTemporalCheck: true } as const;
@@ -180,6 +180,8 @@ d('federation delegation revocation check', () => {
180
180
  const r = await verifyFederationDelegation({ envelope: dv.expected.envelope, ...base });
181
181
  expect(r.ok).toBe(false);
182
182
  expect(r.ok === false && r.message).toMatch(/requires `revocations`/);
183
+ expect(r.ok === false && r.message).toMatch(/"#t": \[delegation id\]/);
184
+ expect(r.ok === false && r.message).not.toMatch(/#delegation/);
183
185
  });
184
186
 
185
187
  it('verifies with an empty revocation list', async () => {
package/src/federation.ts CHANGED
@@ -228,7 +228,7 @@ async function checkFederationQuorum(
228
228
  export interface VerifyFederationDelegationInput extends VerifyFederationBase {
229
229
  envelope: FederationDelegationEnvelope;
230
230
  /**
231
- * Revocations to check (kind-30085 by `#delegation`): federation revocations,
231
+ * Revocations to check (kind-30085 by `#t` with the delegation id, SPEC §9.4): federation revocations,
232
232
  * and agent self-revocations when `revocation.holders` includes "agent".
233
233
  * Required unless `skipRevocationCheck` states that revocation is not checked.
234
234
  */
@@ -290,7 +290,7 @@ export async function verifyFederationDelegation(
290
290
  if (!input.skipRevocationCheck && !input.revocations) {
291
291
  return fail(
292
292
  'E_MALFORMED',
293
- 'verifyFederationDelegation requires `revocations` (fetch kind-30085 by #delegation) ' +
293
+ 'verifyFederationDelegation requires `revocations` (query kind 30085 with "#t": [delegation id], SPEC §9.4) ' +
294
294
  'or an explicit skipRevocationCheck:true to state that revocation is not being checked'
295
295
  );
296
296
  }
@@ -1,6 +1,6 @@
1
- // SECURITY §7 item 7: "Query revocation feeds (Nostr kind-30085 by
2
- // `#delegation`) for the cited delegation id before reporting OK, **unless the
3
- // caller explicitly opts out** of revocation checking."
1
+ // SECURITY §5 item 7: "Query revocation feeds (Nostr kind-30085 by `#t` with
2
+ // the cited delegation id) before reporting OK, **unless the caller explicitly
3
+ // opts out** of revocation checking."
4
4
  //
5
5
  // The default was inverted. verifyDelegation had NO revocations parameter at
6
6
  // all — a revoked delegation returned ok:true and a caller had no way to say
@@ -12,8 +12,9 @@
12
12
  // changed is that declining to is now a decision they have to state.
13
13
  import { describe, expect, it } from 'vitest';
14
14
 
15
- import { verifyDelegation } from './verify.js';
16
- import type { DelegationEnvelope } from './types.js';
15
+ import { computeActionId } from './canonical.js';
16
+ import { verifyAction, verifyDelegation } from './verify.js';
17
+ import type { ActionEnvelope, DelegationEnvelope } from './types.js';
17
18
 
18
19
  // Inlined rather than read from oc-agent-protocol/test-vectors: this test is
19
20
  // about the revocation guard, not vector conformance, and a relative path into
@@ -68,11 +69,46 @@ describe('verifyDelegation fails closed on revocation', () => {
68
69
  });
69
70
  expect(r.ok).toBe(true);
70
71
  });
72
+ });
73
+
74
+ // The refusal tells the caller how to fetch revocations. SPEC §9.4: relays index
75
+ // single-letter tags only and reject a `#delegation` filter (SPEC §10.4), so the
76
+ // message must name the `#t` query.
77
+ describe('the refusal names a query relays answer', () => {
78
+ const action: ActionEnvelope = (() => {
79
+ const canon = {
80
+ address: delegation.agent.address,
81
+ content_hash: 'sha256:' + '00'.repeat(32),
82
+ content_length: 0,
83
+ content_mime: 'text/plain',
84
+ signed_at: '2026-04-23T12:00:00Z',
85
+ delegation_id: delegation.id,
86
+ scope_exercised: delegation.scopes![0]!,
87
+ };
88
+ return {
89
+ v: 1,
90
+ kind: 'agent-action',
91
+ id: computeActionId(canon),
92
+ content: { hash: canon.content_hash, length: 0, mime: 'text/plain', ref: null },
93
+ signer: { address: canon.address, alg: 'bip322' },
94
+ signed_at: canon.signed_at,
95
+ delegation_id: delegation.id,
96
+ scope_exercised: canon.scope_exercised,
97
+ ots: null,
98
+ sig: { alg: 'bip322', pubkey: canon.address, value: 'AAAA' },
99
+ } as ActionEnvelope;
100
+ })();
71
101
 
72
- // An empty list and a skip both verify — but they mean different things, and
73
- // only one of them is a claim about the feed. That distinction is the point
74
- // of the change.
75
- it('the two are not the same statement', () => {
76
- expect('revocations: []').not.toBe('skipRevocationCheck: true');
102
+ it('verifyDelegation', async () => {
103
+ const r = await verifyDelegation({ envelope: delegation, skipSignatureVerification: true, now: AT });
104
+ expect(r.ok === false && r.message).toMatch(/"#t": \[delegation id\]/);
105
+ expect(r.ok === false && r.message).not.toMatch(/#delegation/);
106
+ });
107
+
108
+ it('verifyAction', async () => {
109
+ const r = await verifyAction({ action, delegation, skipSignatureVerification: true });
110
+ expect(r.ok === false && r.code).toBe('E_MALFORMED');
111
+ expect(r.ok === false && r.message).toMatch(/"#t": \[id of every link in the chain\]/);
112
+ expect(r.ok === false && r.message).not.toMatch(/#delegation/);
77
113
  });
78
114
  });
package/src/scope.test.ts CHANGED
@@ -229,3 +229,31 @@ describe('pledge:create is a registered scope', () => {
229
229
  expect(isSubScope(parseScope('pledge:create(max_bond_sats=3000000)'), granted)).toBe(false);
230
230
  });
231
231
  });
232
+
233
+ // SPEC §7.3 / §7.6 (1.4.0): the scopes the agent-stack adapters exercise.
234
+ describe('the adapter scopes are registered', () => {
235
+ for (const s of [
236
+ 'anthropic:tool(name=invoice.create)',
237
+ 'openai:function(name=getWeather)',
238
+ 'vercel:tool(verb=invoice.create)',
239
+ 'langgraph:tool(verb=search)',
240
+ 'anthropic:tool(name=*)',
241
+ ]) {
242
+ it(`strict mode accepts ${s}`, () => {
243
+ expect(() => validateScope(parseScope(s), { mode: 'strict' })).not.toThrow();
244
+ });
245
+ }
246
+
247
+ it('strict mode rejects a key the adapter does not use', () => {
248
+ expect(() => validateScope(parseScope('vercel:tool(name=search)'), { mode: 'strict' })).toThrow(ScopeParseError);
249
+ expect(() => validateScope(parseScope('anthropic:tool(verb=search)'), { mode: 'strict' })).toThrow(ScopeParseError);
250
+ });
251
+
252
+ it('a tool grant admits only that tool, case included', () => {
253
+ const granted = parseScope('openai:function(name=getWeather)');
254
+ expect(isSubScope(parseScope('openai:function(name=getWeather)'), granted)).toBe(true);
255
+ expect(isSubScope(parseScope('openai:function(name=getweather)'), granted)).toBe(false);
256
+ expect(isSubScope(parseScope('openai:function(name=deleteAll)'), granted)).toBe(false);
257
+ expect(isSubScope(parseScope('anthropic:tool(name=getWeather)'), granted)).toBe(false);
258
+ });
259
+ });
package/src/scope.ts CHANGED
@@ -36,6 +36,11 @@ export const REGISTERED_SCOPES: Record<string, { keys: string[] }> = {
36
36
  'mcp:invoke': { keys: ['server', 'tool', 'max_invocations'] },
37
37
  // oc-pledge-protocol SPEC §7.3.
38
38
  'pledge:create': { keys: ['max_bond_sats', 'mechanism', 'counterparty'] },
39
+ // The agent-stack adapters (agent-anthropic, -openai, -vercel, -langgraph).
40
+ 'anthropic:tool': { keys: ['name'] },
41
+ 'openai:function': { keys: ['name'] },
42
+ 'vercel:tool': { keys: ['verb'] },
43
+ 'langgraph:tool': { keys: ['verb'] },
39
44
  };
40
45
 
41
46
  /** Keys whose values are compared numerically for sub-scope ordering. */
package/src/verify.ts CHANGED
@@ -51,9 +51,9 @@ export interface VerifyBase {
51
51
  /**
52
52
  * Verify WITHOUT consulting revocation feeds. Required to be explicit.
53
53
  *
54
- * SECURITY §7 item 7: "Query revocation feeds (Nostr kind-30085 by
55
- * `#delegation`) for the cited delegation id before reporting OK, **unless
56
- * the caller explicitly opts out**." The default was inverted — revocations
54
+ * SECURITY §5 item 7: "Query revocation feeds (Nostr kind-30085 by `#t`
55
+ * with the cited delegation id) before reporting OK, **unless the caller
56
+ * explicitly opts out**." The default was inverted — revocations
57
57
  * were checked only when supplied, and omitting them skipped the check
58
58
  * silently, so "checked, clean" was indistinguishable from "never checked".
59
59
  *
@@ -63,7 +63,9 @@ export interface VerifyBase {
63
63
  skipRevocationCheck?: boolean;
64
64
  /**
65
65
  * Revocations to evaluate against the delegation (or, for an action, every
66
- * link in its chain). Fetch these from kind-30085 by `#delegation`.
66
+ * link in its chain). Fetch kind-30085 events with `"#t": [<delegation id>]`
67
+ * (SPEC §9.4); relays do not index the multi-letter `delegation` tag. Pass
68
+ * `[]` only when a relay answered with EOSE and none matched (SPEC §10.4).
67
69
  */
68
70
  revocations?: RevocationEnvelope[];
69
71
  }
@@ -222,15 +224,15 @@ export async function verifyDelegation(input: VerifyDelegationInput): Promise<Ve
222
224
  if (!ok) return err('E_BAD_SIG', 'BIP-322 signature did not verify');
223
225
  }
224
226
 
225
- // Revocation. SECURITY §7 item 7 — check unless the caller states otherwise.
227
+ // Revocation. SECURITY §5 item 7 — check unless the caller states otherwise.
226
228
  // Before this, verifyDelegation had NO revocations parameter at all: a
227
229
  // revoked delegation returned ok:true and a caller had no way to say
228
230
  // otherwise.
229
231
  if (!input.skipRevocationCheck && !input.revocations) {
230
232
  return err(
231
233
  'E_MALFORMED',
232
- 'verifyDelegation requires `revocations` (fetch kind-30085 by #delegation) or an ' +
233
- 'explicit skipRevocationCheck:true to state that revocation is not being checked',
234
+ 'verifyDelegation requires `revocations` (query kind 30085 with "#t": [delegation id], ' +
235
+ 'SPEC §9.4) or an explicit skipRevocationCheck:true to state that revocation is not being checked',
234
236
  );
235
237
  }
236
238
  if (input.revocations) {
@@ -452,8 +454,8 @@ export async function verifyAction(input: VerifyActionInput): Promise<VerifyActi
452
454
  if (!input.skipRevocationCheck && !input.revocations) {
453
455
  return err(
454
456
  'E_MALFORMED',
455
- 'verifyAction requires `revocations` (fetch kind-30085 by #delegation for every link ' +
456
- 'in the chain) or an explicit skipRevocationCheck:true',
457
+ 'verifyAction requires `revocations` (query kind 30085 with "#t": [id of every link ' +
458
+ 'in the chain], SPEC §9.4) or an explicit skipRevocationCheck:true',
457
459
  );
458
460
  }
459
461
  // Anchor heights only count once the proof is verified against the