@scopebond/hook 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +124 -11
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts +24 -2
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +71 -8
- package/dist/cloud.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/managed.d.ts +71 -0
- package/dist/managed.d.ts.map +1 -0
- package/dist/managed.js +230 -0
- package/dist/managed.js.map +1 -0
- package/dist/obs-emitter.d.ts.map +1 -1
- package/dist/obs-emitter.js +2 -2
- package/dist/obs-emitter.js.map +1 -1
- package/dist/policy-sync.d.ts +45 -0
- package/dist/policy-sync.d.ts.map +1 -0
- package/dist/policy-sync.js +173 -0
- package/dist/policy-sync.js.map +1 -0
- package/dist/proof.d.ts +1 -1
- package/dist/proof.d.ts.map +1 -1
- package/dist/proof.js +2 -1
- package/dist/proof.js.map +1 -1
- package/dist/recover.d.ts +33 -0
- package/dist/recover.d.ts.map +1 -0
- package/dist/recover.js +201 -0
- package/dist/recover.js.map +1 -0
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +5 -0
- package/dist/runtime.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"managed.js","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"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","sourcesContent":["// Rules managed from a Scopebond Cloud workspace.\n//\n// A connected computer keeps its own rules (`rules.json` compiled into `policy.json`) until the workspace sets rules for it.\n// From then on the workspace decides, rule by rule, whether a matching action is blocked or only recorded, and may add entries\n// to the computer's own lists (protected branches, programs, allowed sites). It never sends a pattern: this module compiles the\n// workspace's choices with the same compiler `rules apply` uses, so what decides a block lives in one place.\n//\n// A workspace may also name exact targets one rule skips: paths for the secret-read and CI-configuration rules, branches for the\n// push rule. An exclusion matches exactly (same case; a trailing `/**` covers a folder), so it is never broader than what was typed,\n// and an exclusion that would touch the always-on protection of the hook's own settings is dropped when the policy is compiled.\n//\n// What a workspace cannot change: the protection of the hook's own settings (and the settings of the agents it guards), the\n// machine key policy, fail-closed handling of anything unparseable, and the computer's own opt-in extras (`allowed_roots`,\n// `protect_remote_database`). A rule set to Monitor is still covered by an allow clause, so the action is recorded and passes;\n// leaving it out would block the whole action type, because the verifier denies an action no clause covers.\n//\n// Loading is checked and atomic: the document must be well formed, name this computer, carry a matching digest and be newer\n// than the one in force; the merged policy must build; policy.json is written through a temp file and a rename, the previous\n// one kept. Disconnecting, or a workspace that stops managing this computer, restores the computer's own rules.\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { canonical } from \"@scopebond/policy-schema/canonical\";\nimport { GUARDRAIL_LOOKAHEADS, GUARDRAIL_WRITE_PATTERN, ci } from \"./runtime.js\";\nimport { compile, defaultRules, loadRules, type RuleSet } from \"./rules.js\";\n\nexport const MANAGED_DOC_FILE = \"managed-rules.json\";\nexport const MANAGED_META_FILE = \"managed-meta.json\";\nexport const PREVIOUS_POLICY_FILE = \"policy.previous.json\";\n\nexport const MANAGED_RULE_IDS = [\"force-push-protected\", \"push-protected\", \"destructive-shell\", \"secret-read\", \"ci-config-write\", \"network-egress\"] as const;\nexport type ManagedRuleId = (typeof MANAGED_RULE_IDS)[number];\ntype ListKey = \"protected_branches\" | \"destructive_programs\" | \"allowed_hosts\";\ntype ExclusionKey = \"excluded_paths\" | \"excluded_branches\";\nconst LIST_OF: Partial<Record<ManagedRuleId, ListKey>> = {\n \"force-push-protected\": \"protected_branches\", \"push-protected\": \"protected_branches\",\n \"destructive-shell\": \"destructive_programs\", \"network-egress\": \"allowed_hosts\",\n};\nconst LIST_PATTERN: Record<ListKey, RegExp> = {\n protected_branches: /^[A-Za-z0-9._/*-]{1,100}$/,\n destructive_programs: /^[a-z0-9][a-z0-9._-]{0,63}$/,\n allowed_hosts: /^(?=.{1,253}$)(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/,\n};\n/** The exact targets a rule may skip, and the one list each rule accepts. */\nconst EXCLUSIONS_OF: Partial<Record<ManagedRuleId, ExclusionKey>> = {\n \"secret-read\": \"excluded_paths\", \"ci-config-write\": \"excluded_paths\", \"push-protected\": \"excluded_branches\",\n};\nconst EXCLUSION_PATTERN: Record<ExclusionKey, RegExp> = {\n // A relative path with forward slashes: no empty, \".\" or \"..\" segment, optionally ending in /** for a folder.\n excluded_paths: /^(?!\\/)(?!.*\\/\\/)(?!(?:.*\\/)?\\.{1,2}(?:\\/|$))[A-Za-z0-9._@+-][A-Za-z0-9._@+/-]{0,199}?(?:\\/\\*\\*)?$/,\n // One exact branch name; it starts with a letter or digit, so it can never be a push flag such as --all.\n excluded_branches: /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/,\n};\nconst HEX64 = /^[0-9a-f]{64}$/;\nconst OPAQUE = /^[A-Za-z0-9._:-]{1,128}$/;\n\nexport type ManagedRule = { mode: \"monitor\" | \"block\" } & Partial<Record<ListKey | ExclusionKey, string[]>>;\nexport interface ManagedDocument {\n type: \"scopebond:managed-rules\";\n version: 1;\n revision: number;\n export_id: string;\n environment_id: string;\n agent_id: string;\n installation_id: string;\n rules: Record<ManagedRuleId, ManagedRule>;\n rules_digest: string;\n}\n\nexport type RefusalReason = \"invalid_document\" | \"stale_revision\" | \"wrong_computer\" | \"unsupported\" | \"write_failed\";\nexport type Inspection = { ok: true; doc: ManagedDocument } | { ok: false; reason: RefusalReason; message: string };\n\nexport const digestRules = (rules: unknown): string => createHash(\"sha256\").update(canonical(rules)).digest(\"hex\");\n\nconst isObject = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Check a document from the workspace without touching disk. Pure. */\nexport function inspectManaged(raw: unknown, context: { installationId: string; currentRevision: number | null; currentDigest?: string | null }): Inspection {\n const bad = (message: string): Inspection => ({ ok: false, reason: \"invalid_document\", message });\n if (!isObject(raw) || raw.type !== \"scopebond:managed-rules\") return bad(\"this is not a Scopebond workspace rules document\");\n if (raw.version !== 1) return { ok: false, reason: \"unsupported\", message: `unsupported rules document version ${String(raw.version)}` };\n const { revision, export_id: exportId, environment_id: environmentId, agent_id: agentId, installation_id: installationId, rules, rules_digest: rulesDigest } = raw;\n if (!Number.isInteger(revision) || (revision as number) < 1 || (revision as number) > 2_147_483_647\n || typeof exportId !== \"string\" || typeof environmentId !== \"string\" || !OPAQUE.test(environmentId)\n || typeof agentId !== \"string\" || !OPAQUE.test(agentId) || typeof installationId !== \"string\" || !OPAQUE.test(installationId)\n || typeof rulesDigest !== \"string\" || !HEX64.test(rulesDigest) || !isObject(rules)) {\n return bad(\"the rules document is incomplete or malformed\");\n }\n if (installationId !== context.installationId || exportId !== `rev-${revision}-${installationId}`) {\n return { ok: false, reason: \"wrong_computer\", message: \"these rules were issued for a different computer\" };\n }\n const keys = Object.keys(rules);\n if (keys.length !== MANAGED_RULE_IDS.length || !MANAGED_RULE_IDS.every((id) => keys.includes(id))) {\n return { ok: false, reason: \"unsupported\", message: \"the rules document names rules this version does not know; update Scopebond\" };\n }\n for (const id of MANAGED_RULE_IDS) {\n const rule = rules[id];\n if (!isObject(rule) || (rule.mode !== \"monitor\" && rule.mode !== \"block\")) return bad(`rule ${id} has no valid mode`);\n for (const key of Object.keys(rule)) {\n if (key === \"mode\") continue;\n const listKey = LIST_OF[id];\n const exclusionKey = EXCLUSIONS_OF[id];\n const pattern = key === listKey ? LIST_PATTERN[listKey] : key === exclusionKey ? EXCLUSION_PATTERN[exclusionKey] : null;\n if (!pattern) return bad(`rule ${id} has an unknown setting`);\n const list = rule[key];\n if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === \"string\" && pattern.test(v) && !v.includes(\"..\"))) {\n return bad(`rule ${id} has an invalid list`);\n }\n }\n }\n const network = rules[\"network-egress\"] as ManagedRule;\n if (network.mode === \"block\" && !(network.allowed_hosts?.length)) return bad(\"network blocking needs at least one allowed site\");\n if (digestRules(rules) !== rulesDigest) return bad(\"the rules do not match their digest; the document was changed or damaged\");\n // The same version with different rules is a newer document too: the workspace can change what reaches this computer without a new\n // version (an agent moved to another team, or this hook started reading a setting it did not read before). The export id binds it here.\n const sameVersionChanged = context.currentRevision !== null && revision === context.currentRevision && !!context.currentDigest && rulesDigest !== context.currentDigest;\n if (context.currentRevision !== null && (revision as number) <= context.currentRevision && !sameVersionChanged) {\n return { ok: false, reason: \"stale_revision\", message: `version ${String(revision)} is not newer than the version in force (${context.currentRevision})` };\n }\n return { ok: true, doc: raw as unknown as ManagedDocument };\n}\n\nconst union = (a: readonly string[], b: readonly string[] | undefined): string[] => [...new Set([...a, ...(b ?? [])])];\nconst branchGlob = (b: string): string => (b.endsWith(\"/*\") ? `${b.slice(0, -1)}**` : b);\nconst hostPattern = (hosts: readonly string[]): string =>\n `^(?:${hosts.map((h) => (h.startsWith(\"*.\") ? `(?:[^.]+\\\\.)+${ci(h.slice(2).replace(/\\./g, \"\\\\.\"))}` : ci(h.replace(/\\./g, \"\\\\.\")))).join(\"|\")})$`;\n\ntype Clause = Record<string, unknown> & { id: string };\n\nconst escapeRegex = (text: string): string => text.replace(/[\\\\^$.*+?()[\\]{}|/]/g, \"\\\\$&\");\n/** Exclusions that passed validation (the document check refuses the rest; this is a second line of defence). */\nconst safeExclusions = (paths: readonly string[] | undefined): string[] => (paths ?? []).filter((path) => EXCLUSION_PATTERN.excluded_paths.test(path) && !path.includes(\"..\"));\n/** An allow pattern that also allows exactly these paths (a trailing /** allows everything under the folder). The added alternative\n * carries the always-on floor and refuses any path with a \".\" or \"..\" segment, so no exclusion — however it is written — can reach\n * Scopebond's own settings or the agents' hook settings, or climb out of the folder it names. */\nconst withPathExclusions = (pattern: string, paths: readonly string[]): string => paths.length === 0 ? pattern\n : `^(?!(?:.*/)?\\\\.{1,2}(?:/|$))${GUARDRAIL_LOOKAHEADS}(?:${paths.map((p) => p.endsWith(\"/**\") ? `${escapeRegex(p.slice(0, -3))}/.+` : escapeRegex(p)).join(\"|\")})$|${pattern}`;\n/** An allow pattern for pushes that also allows exactly these branches. */\nconst withBranchExclusions = (pattern: string, branches: readonly string[]): string => branches.length === 0 ? pattern\n : `^(?:refs/heads/)?(?:${branches.map(escapeRegex).join(\"|\")})$|${pattern}`;\nconst boundPattern = (clause: Clause, key: string): string => String(((clause.param_bounds as Record<string, { pattern: string }> | undefined)?.[key])?.pattern ?? \"\");\nconst withBound = (clause: Clause, key: string, pattern: string): Clause => ({ ...clause, param_bounds: { ...(clause.param_bounds as Record<string, unknown>), [key]: { pattern } } });\n\n/** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */\nexport function compileManaged(local: RuleSet, doc: ManagedDocument, agentKid: string): Record<string, unknown> {\n const r = doc.rules;\n const rules: RuleSet = {\n ...local,\n protected_branches: union(local.protected_branches, [...(r[\"push-protected\"].protected_branches ?? []), ...(r[\"force-push-protected\"].protected_branches ?? [])]),\n destructive_programs: union(local.destructive_programs, r[\"destructive-shell\"].destructive_programs),\n };\n const policy = compile(rules, agentKid) as { clauses: Clause[] } & Record<string, unknown>;\n const allowAll = (clause: Clause): Clause => { const { param_bounds: _dropped, ...rest } = clause; return { ...rest, mode: \"enforce\", description: `${String(clause.description ?? \"\").split(\".\")[0]}. Recorded, not blocked: set by your workspace.` }; };\n const clauses: Clause[] = [];\n for (const clause of policy.clauses) {\n if (clause.id === \"protect-branches\" && r[\"push-protected\"].mode === \"monitor\") {\n clauses.push(allowAll(clause));\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch; ordinary pushes are allowed. Set by your workspace.\" });\n }\n } else if (clause.id === \"safe-shell\" && r[\"destructive-shell\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-read\" && r[\"secret-read\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].mode === \"monitor\") {\n clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },\n description: \"Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace.\" });\n } else if (clause.id === \"protect-read\" && r[\"secret-read\"].excluded_paths?.length) {\n clauses.push(withBound(clause, \"path\", withPathExclusions(boundPattern(clause, \"path\"), safeExclusions(r[\"secret-read\"].excluded_paths))));\n } else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].excluded_paths?.length) {\n clauses.push(withBound(clause, \"path\", withPathExclusions(boundPattern(clause, \"path\"), safeExclusions(r[\"ci-config-write\"].excluded_paths))));\n } else if (clause.id === \"protect-branches\" && r[\"push-protected\"].excluded_branches?.length) {\n clauses.push(withBound(clause, \"ref\", withBranchExclusions(boundPattern(clause, \"ref\"), r[\"push-protected\"].excluded_branches)));\n // Skipping ordinary pushes to a branch never skips history protection: force-pushes, deletions and mirror pushes stay stopped.\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch. Set by your workspace.\" });\n }\n } else clauses.push(clause);\n }\n if (r[\"network-egress\"].mode === \"block\") {\n const keysAt = clauses.findIndex((c) => c.id === \"keys\");\n clauses.splice(keysAt < 0 ? clauses.length : keysAt, 0, {\n id: \"allowed-sites\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"net.fetch\"],\n param_bounds: { host: { pattern: hostPattern(r[\"network-egress\"].allowed_hosts ?? []) } },\n description: `Deny web requests to sites other than ${r[\"network-egress\"].allowed_hosts!.join(\", \")}. Set by your workspace.`,\n });\n }\n return { ...policy, policy_id: \"coding-agent\", version: doc.revision, clauses };\n}\n\n// --- on disk ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ManagedMeta {\n revision: number | null;\n rules_digest: string | null;\n export_id: string | null;\n etag: string | null;\n checked_at: string | null;\n last_ack: { revision: number; result: \"loaded\" | \"rejected\"; at: string } | null;\n last_error: string | null;\n}\nconst EMPTY_META: ManagedMeta = { revision: null, rules_digest: null, export_id: null, etag: null, checked_at: null, last_ack: null, last_error: null };\n\nexport function readMeta(dir: string): ManagedMeta {\n try { return { ...EMPTY_META, ...(JSON.parse(readFileSync(join(dir, MANAGED_META_FILE), \"utf8\")) as Partial<ManagedMeta>) }; } catch { return { ...EMPTY_META }; }\n}\nexport function writeMeta(dir: string, meta: ManagedMeta): void { writeAtomic(join(dir, MANAGED_META_FILE), `${JSON.stringify(meta, null, 2)}\\n`); }\n\n/** Whether a workspace currently manages the rules on this computer. */\nexport const isManaged = (dir: string): boolean => existsSync(join(dir, MANAGED_DOC_FILE));\n\nexport function writeAtomic(file: string, text: string): void {\n const temp = `${file}.${randomBytes(6).toString(\"hex\")}.tmp`;\n try { writeFileSync(temp, text, { mode: 0o600 }); renameSync(temp, file); }\n catch (error) { try { rmSync(temp, { force: true }); } catch { /* nothing to remove */ } throw error; }\n}\n\n/** Install a checked document: write the merged policy (previous kept) and the document. `policyBuilds` is the caller's check\n * that the gateway can load the result. */\nexport function installManaged(dir: string, doc: ManagedDocument, agentKid: string, policyBuilds: (policy: Record<string, unknown>) => boolean): { ok: true } | { ok: false; reason: RefusalReason; message: string } {\n const local = loadRules(dir) ?? defaultRules();\n const policy = compileManaged(local, doc, agentKid);\n if (!policyBuilds(policy)) return { ok: false, reason: \"unsupported\", message: \"this version of Scopebond cannot load the workspace rules; update it\" };\n try {\n mkdirSync(dir, { recursive: true });\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(policy, null, 2)}\\n`);\n writeAtomic(join(dir, MANAGED_DOC_FILE), `${JSON.stringify(doc, null, 2)}\\n`);\n return { ok: true };\n } catch (error) {\n return { ok: false, reason: \"write_failed\", message: `could not write the rules (${(error as Error).name})` };\n }\n}\n\n/** Go back to this computer's own rules: recompile policy.json from rules.json and forget the workspace document. */\nexport function restoreLocal(dir: string, agentKid: string): void {\n const local = loadRules(dir) ?? defaultRules();\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(compile(local, agentKid), null, 2)}\\n`);\n rmSync(join(dir, MANAGED_DOC_FILE), { force: true });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obs-emitter.d.ts","sourceRoot":"","sources":["../src/obs-emitter.ts"],"names":[],"mappings":"AA4BA,OAAO,
|
|
1
|
+
{"version":3,"file":"obs-emitter.d.ts","sourceRoot":"","sources":["../src/obs-emitter.ts"],"names":[],"mappings":"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"}
|
package/dist/obs-emitter.js
CHANGED
|
@@ -25,7 +25,7 @@ import { createRequire } from "node:module";
|
|
|
25
25
|
import { spawn, execFileSync } from "node:child_process";
|
|
26
26
|
import { join } from "node:path";
|
|
27
27
|
import { fileURLToPath } from "node:url";
|
|
28
|
-
import { loadConnection } from "./cloud.js";
|
|
28
|
+
import { ingestUrl, loadConnection } from "./cloud.js";
|
|
29
29
|
import { operationsForCall, capabilityProofData, digestPolicy, heartbeatData, intentData, loadOrCreateBindingKey, observationSigner, outcomeData, policyAckData, queueData, sessionStartData, sessionStopData, sourceReceiptHash, } from "./observation.js";
|
|
30
30
|
import { openApprovalBinder } from "@scopebond/gateway/node";
|
|
31
31
|
import { OBSERVATION_DB, ObservationStore } from "./obs-store.js";
|
|
@@ -132,7 +132,7 @@ export class ObservationEmitter {
|
|
|
132
132
|
async flush(timeoutMs = this.options.flushTimeoutMs ?? 800) {
|
|
133
133
|
try {
|
|
134
134
|
return await uploadPending(this.store, {
|
|
135
|
-
url: this.connection
|
|
135
|
+
url: ingestUrl(this.connection), credential: this.connection.credential,
|
|
136
136
|
fetch: this.options.fetch, now: this.options.now, timeoutMs,
|
|
137
137
|
});
|
|
138
138
|
}
|
package/dist/obs-emitter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obs-emitter.js","sourceRoot":"","sources":["../src/obs-emitter.ts"],"names":[],"mappings":"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","sourcesContent":["// Observation emitters: when the hook may emit, and what it emits.\n//\n// Emission is opt-in through the workspace enrollment. It is on only when the connection\n// (`cloud.json`) carries the `observations:write` scope AND the enrollment handoff gave an\n// installation id and generation AND the agent key's id matches the one the workspace\n// enrolled. Otherwise it is silently off (with a status line) or reported unsupported;\n// nothing is guessed. None of it can change a policy decision: every failure here is\n// swallowed by the caller and the local receipt path is untouched.\n//\n// Kinds emitted (an agent-adapter key may emit these and no others):\n// session start / stop, from Claude Code's SessionStart and SessionEnd hooks\n// health heartbeat (60 s while a session is explicitly active), queue telemetry\n// capability proof, from the `capabilities --prove` runner\n// policy_ack loaded / rejected, from `policy load` once an exported policy is verified or refused\n// tool_intent the request binding of each dispatched action, linked to its receipt\n// tool_outcome the after-action result, echoing the stored binding (Claude Code)\n//\n// There is no always-on process. Heartbeats come from one short helper per active session,\n// claimed under a lease so hook calls cannot multiply it; it ends when the session ends,\n// after ten minutes without hook activity (it then releases the lease with a final\n// `lease_active: false` heartbeat), or when the host clock jumps (the host slept: it records\n// a stop with reason `sleep` and emits nothing for the gap).\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { spawn, execFileSync } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { loadConnection, type HookConnection } from \"./cloud.js\";\nimport {\n operationsForCall, capabilityProofData, digestPolicy, heartbeatData, intentData, loadOrCreateBindingKey, observationSigner,\n outcomeData, policyAckData, queueData, sessionStartData, sessionStopData, sourceReceiptHash,\n type BindingKey, type CallRequest, type ExitCategory, type ObservationSigner, type PolicyAckInput, type SessionStopReason, type CapabilityProofInput,\n} from \"./observation.js\";\nimport { openApprovalBinder } from \"@scopebond/gateway/node\";\nimport { OBSERVATION_DB, ObservationStore, type EnqueueResult } from \"./obs-store.js\";\nimport type { GitProbe } from \"./typed-ops.js\";\nimport { isProtectedBranch, loadRules } from \"./rules.js\";\nimport { uploadPending, type UploadOutcome } from \"./obs-upload.js\";\n\nexport const OBSERVATIONS_SCOPE = \"observations:write\";\nexport const HEARTBEAT_INTERVAL_MS = 60_000;\n/** A loop that finds the clock moved by more than this since its last tick treats the host as slept. */\nexport const SLEEP_GAP_MS = 3 * HEARTBEAT_INTERVAL_MS;\n/** Heartbeats continue this long after the last hook activity, then the lease is released. */\nexport const IDLE_LIMIT_MS = 10 * 60_000;\nexport const HEARTBEAT_LEASE_MS = 2.5 * HEARTBEAT_INTERVAL_MS;\nexport const QUEUE_TELEMETRY_INTERVAL_MS = 5 * 60_000;\n/** At most this many typed operations are recorded per tool call; the rest are counted. */\nexport const MAX_INTENTS_PER_CALL = 10;\n\nexport type ObservationStatus =\n | { state: \"off\"; reason: string }\n | { state: \"unsupported\"; reason: string }\n | { state: \"on\" };\n\n/** The installation id is the enrollment's `installation_id`, or its `gateway_id` (the same\n * identifier, which is what an enrollment answer carries today). The generation has no such\n * fallback: only the workspace can say what it is. */\nexport function withInstallationId(connection: HookConnection): HookConnection {\n return connection.installation_id === undefined && typeof connection.gateway_id === \"string\"\n ? { ...connection, installation_id: connection.gateway_id } : connection;\n}\n\n/** Decide from the connection alone whether emission is enabled. Pure; reads nothing. */\nexport function observationStatus(input: HookConnection | null, keyKid?: string): ObservationStatus {\n if (!input) return { state: \"off\", reason: \"not connected to a workspace\" };\n const connection = withInstallationId(input);\n if (!Array.isArray(connection.scopes) || !connection.scopes.includes(OBSERVATIONS_SCOPE)) {\n return { state: \"off\", reason: `this enrollment does not grant ${OBSERVATIONS_SCOPE}` };\n }\n if (typeof connection.installation_id !== \"string\" || !/^[\\x21-\\x7e]{1,200}$/.test(connection.installation_id)) {\n return { state: \"unsupported\", reason: \"the enrollment did not include an installation id; reconnect after the workspace supports observations\" };\n }\n if (!Number.isInteger(connection.installation_generation) || (connection.installation_generation as number) < 1 || (connection.installation_generation as number) > 2_147_483_647) {\n return { state: \"unsupported\", reason: \"the enrollment did not include an installation generation; reconnect after the workspace supports observations\" };\n }\n if (typeof connection.agent_kid !== \"string\" || !/^[A-Za-z0-9:._-]{1,200}$/.test(connection.agent_kid)) {\n return { state: \"unsupported\", reason: \"the enrollment did not register the agent signing key\" };\n }\n if (keyKid !== undefined && keyKid !== connection.agent_kid) {\n return { state: \"unsupported\", reason: \"the local agent key is not the key the workspace enrolled\" };\n }\n return { state: \"on\" };\n}\n\nexport interface EmitterOptions {\n fetch?: typeof fetch;\n now?: () => number;\n adapterVersion?: string;\n /** Tests: do not start the heartbeat helper process. */\n spawnHeartbeat?: boolean;\n /** Bound on one upload attempt from a hook call. */\n flushTimeoutMs?: number;\n}\n\nexport interface OpenResult {\n status: ObservationStatus;\n emitter?: ObservationEmitter;\n}\n\n/** Open the emitter for a hook config dir, or explain why not. Never throws: an\n * unreadable key or store simply leaves observations off. */\nexport function openObservations(dir: string, options: EmitterOptions = {}): OpenResult {\n const loaded = loadConnection(dir);\n const early = observationStatus(loaded);\n if (early.state !== \"on\" || !loaded) return { status: early };\n const connection = withInstallationId(loaded);\n let store: ObservationStore | undefined;\n try {\n const keyPem = readFileSync(join(dir, \"agent.key\"), \"utf8\");\n const signer = observationSigner(keyPem, connection.agent_kid as string);\n store = new ObservationStore(join(dir, OBSERVATION_DB), options.now);\n const bound = store.bindGeneration(connection.installation_generation as number);\n if (!bound.ok) { store.close(); return { status: { state: \"unsupported\", reason: bound.reason } }; }\n const emitter = new ObservationEmitter(dir, connection, store, signer, loadOrCreateBindingKey(dir), options);\n return { status: { state: \"on\" }, emitter };\n } catch (error) {\n try { store?.close(); } catch { /* nothing to release */ }\n return { status: { state: \"unsupported\", reason: `observation setup failed: ${(error as Error).message.slice(0, 120)}` } };\n }\n}\n\nexport class ObservationEmitter {\n constructor(\n readonly dir: string,\n readonly connection: HookConnection,\n readonly store: ObservationStore,\n private readonly signer: ObservationSigner,\n readonly binding: BindingKey,\n private readonly options: EmitterOptions = {},\n ) {}\n\n private now(): number { return (this.options.now ?? Date.now)(); }\n private get context() {\n return { installationId: this.connection.installation_id as string, generation: this.connection.installation_generation as number };\n }\n private get adapterVersion(): string { return this.options.adapterVersion ?? \"unknown\"; }\n\n /** Queue one observation. Any failure is contained: emission never throws to the caller. */\n emit(draft: Parameters<ObservationStore[\"enqueue\"]>[0]): EnqueueResult | null {\n try { return this.store.enqueue(draft, this.context, this.signer); } catch { return null; }\n }\n\n /** Try to deliver the outbox, bounded. Never throws. */\n async flush(timeoutMs = this.options.flushTimeoutMs ?? 800): Promise<UploadOutcome | null> {\n try {\n return await uploadPending(this.store, {\n url: this.connection.url, credential: this.connection.credential,\n fetch: this.options.fetch, now: this.options.now, timeoutMs,\n });\n } catch { return null; }\n }\n\n close(): void { this.store.close(); }\n\n // ---- session lifecycle ---------------------------------------------------------------------\n\n sessionIdOf(harnessSessionId: string): string { return this.binding.sessionId(harnessSessionId); }\n\n /** A local key for a harness tool call, so the after-action hook finds what the\n * before-action hook recorded. Keyed, so the raw ids are not written to the local store. */\n private callKey(harnessSessionId: string | undefined, callId: string): string {\n return this.binding.resourceId(\"call\", `${harnessSessionId ?? \"\"}\\u0000${callId}`);\n }\n\n /** SessionStart: record one start per session and make sure the heartbeat helper runs. */\n sessionStart(harnessSessionId: string, cwd: string): void {\n const sessionId = this.sessionIdOf(harnessSessionId);\n const repositoryId = this.binding.resourceId(\"repo\", cwd);\n if (this.store.activateSession(sessionId, repositoryId)) {\n this.emit({ kind: \"session\", occurredAt: this.now(), sessionId, data: sessionStartData(repositoryId) });\n }\n this.ensureHeartbeat(sessionId);\n }\n\n /** SessionEnd: record one stop, with its reason, only for a session that was active. */\n sessionStop(harnessSessionId: string, reason: SessionStopReason): void {\n const sessionId = this.sessionIdOf(harnessSessionId);\n const row = this.store.session(sessionId);\n if (this.store.deactivateSession(sessionId, reason)) {\n this.emit({ kind: \"session\", occurredAt: this.now(), sessionId, data: sessionStopData(reason, row?.repository_id ?? undefined) });\n }\n }\n\n /** Hook activity in a session: keeps the lease alive, and resumes a session that was\n * stopped only because the host slept. A session that was ended, or never started\n * through the lifecycle hooks, is left alone. */\n activity(harnessSessionId: string, cwd: string): void {\n const sessionId = this.sessionIdOf(harnessSessionId);\n const row = this.store.session(sessionId);\n if (!row) return;\n if (row.state === \"stopped\" && row.stopped_reason === \"sleep\") { this.sessionStart(harnessSessionId, cwd); return; }\n if (row.state !== \"active\") return;\n this.store.touchSession(sessionId);\n this.ensureHeartbeat(sessionId);\n }\n\n /** Start the single heartbeat helper for a session unless one already holds the lease. */\n ensureHeartbeat(sessionId: string): void {\n // SCOPEBOND_OBSERVATIONS_HEARTBEAT=off keeps session start/stop and tool observations but\n // runs no helper process (tests, or a host where a background process is unwelcome).\n if (this.options.spawnHeartbeat === false || process.env.SCOPEBOND_OBSERVATIONS_HEARTBEAT === \"off\") return;\n try {\n if (!this.store.claimHeartbeat(sessionId, HEARTBEAT_LEASE_MS)) return;\n const cli = fileURLToPath(new URL(\"./cli.js\", import.meta.url));\n const child = spawn(process.execPath, [cli, \"observations\", \"heartbeat\", sessionId], {\n detached: true, stdio: \"ignore\", windowsHide: true, env: { ...process.env, SCOPEBOND_HOOK_DIR: this.dir },\n });\n child.on(\"error\", () => { /* the lease lapses and the next hook call tries again */ });\n child.unref();\n } catch { /* best effort */ }\n }\n\n // ---- health --------------------------------------------------------------------------------\n\n private policyDigest(): string | undefined {\n try { return digestPolicy(JSON.parse(readFileSync(join(this.dir, \"policy.json\"), \"utf8\"))); } catch { return undefined; }\n }\n\n /** Queue oldest-pending-receipt time and count, at most every five minutes, and once more\n * when a backlog drains. */\n queueTelemetry(): void {\n const at = this.now();\n const last = this.store.getMark(\"queue_at\");\n const backlog = receiptBacklog(this.dir);\n if (!backlog) return;\n const previous = this.store.getMark(\"queue_pending\") ?? 0;\n if (backlog.count === 0 && previous === 0) return;\n if (backlog.count > 0 && last !== null && at - last < QUEUE_TELEMETRY_INTERVAL_MS) return;\n this.emit({ kind: \"health\", occurredAt: at, data: queueData(backlog.count > 0 ? backlog.oldestAt : at, backlog.count, this.policyDigest()) });\n this.store.mark(\"queue_at\", at);\n this.store.mark(\"queue_pending\", backlog.count);\n }\n\n /**\n * One heartbeat-loop tick. Returns whether the loop should keep running.\n * - session no longer active: stop quietly\n * - the clock moved more than SLEEP_GAP_MS since the last tick: the host slept; record a\n * stop (reason sleep) at the last known time and emit no heartbeat for the gap\n * - no hook activity for IDLE_LIMIT_MS: send a final heartbeat that releases the lease\n * - otherwise: heartbeat with the lease active\n */\n heartbeatTick(sessionId: string, lastTickAt: number): \"continue\" | \"stop\" {\n const at = this.now();\n const row = this.store.session(sessionId);\n if (!row || row.state !== \"active\") { this.store.releaseHeartbeat(sessionId); return \"stop\"; }\n if (at - lastTickAt > SLEEP_GAP_MS) {\n if (this.store.deactivateSession(sessionId, \"sleep\")) {\n this.emit({ kind: \"session\", occurredAt: lastTickAt, sessionId, data: sessionStopData(\"sleep\", row.repository_id ?? undefined) });\n }\n return \"stop\";\n }\n if (at - row.last_activity_at > IDLE_LIMIT_MS) {\n this.emit({ kind: \"health\", occurredAt: at, sessionId, data: heartbeatData(false, { policyDigest: this.policyDigest() }) });\n this.store.releaseHeartbeat(sessionId);\n return \"stop\";\n }\n this.emit({ kind: \"health\", occurredAt: at, sessionId, data: heartbeatData(true, { policyDigest: this.policyDigest() }) });\n this.queueTelemetry();\n return this.store.renewHeartbeat(sessionId, HEARTBEAT_LEASE_MS, true) ? \"continue\" : \"stop\";\n }\n\n // ---- actions -------------------------------------------------------------------------------\n\n /**\n * Record the request binding of each dispatched action, linked to its receipt, and keep\n * the operation so the after-action hook can echo it. `dispatched` is what was actually\n * evaluated: the final intent (after root scoping and grouping) and its receipt.\n */\n toolIntents(input: {\n harnessSessionId?: string; callId?: string; cwd: string;\n dispatched: Array<{ action: { action_type: string; params: Record<string, unknown> }; receipt?: unknown }>;\n /** The tool call as sent, so typed operations read the actual request: a shell tool's raw\n * command, or an MCP tool's server, name and input. */\n request?: CallRequest;\n probe?: GitProbe;\n }): void {\n const sessionId = input.harnessSessionId ? this.sessionIdOf(input.harnessSessionId) : undefined;\n const repositoryId = this.binding.resourceId(\"repo\", input.cwd);\n const rules = loadRules(this.dir);\n const operations = operationsForCall({ dispatched: input.dispatched, request: input.request }, {\n key: this.binding, cwd: input.cwd, repositoryId,\n ...(input.probe ? { probe: input.probe } : {}), ...(rules ? { isProtectedRef: (ref: string) => isProtectedBranch(rules, ref) } : {}),\n ...(process.env.SCOPEBOND_REQUIRED_CHECK_POLICY_VERSION ? { requiredCheckPolicyVersion: process.env.SCOPEBOND_REQUIRED_CHECK_POLICY_VERSION.slice(0, 200) } : {}),\n });\n // With approvals held in the workspace, an operation carries the hash the dispatch guard asks it to consume, so a consumed approval can be correlated.\n const binder = openApprovalBinder(this.dir);\n let recorded = 0;\n input.dispatched.forEach((item, index) => {\n const receipt = item.receipt as { payload?: { action_ref?: { action_id?: unknown } } } | undefined;\n const actionId = receipt?.payload?.action_ref?.action_id;\n if (typeof actionId !== \"string\" || actionId === \"\" || actionId.length > 200) return;\n if (recorded >= MAX_INTENTS_PER_CALL) { this.store.mark(\"omitted_intents\", (this.store.getMark(\"omitted_intents\") ?? 0) + 1); return; }\n const plain = operations[index];\n if (!plain) return;\n const operation = binder ? binder.bind(plain, item.action) : plain;\n const linked = sourceReceiptHash(item.receipt);\n const result = this.emit({ kind: \"tool_intent\", occurredAt: this.now(), sessionId, parentActionId: actionId, sourceReceiptHash: linked, data: intentData(operation) });\n recorded += 1;\n if (result?.queued && input.callId) {\n try {\n this.store.recordCall({ call_key: `${this.callKey(input.harnessSessionId, input.callId)}:${String(index).padStart(3, \"0\")}`, parent_action_id: actionId, source_receipt_hash: linked, session_id: sessionId ?? null, operation });\n } catch { /* the outcome is then simply not recorded */ }\n }\n });\n }\n\n /** After-action result for a call whose intents were recorded. `exit` comes from the\n * harness's own success/failure event, never from model text. */\n toolOutcomes(harnessSessionId: string | undefined, callId: string, exit: ExitCategory): void {\n let calls;\n try { calls = this.store.takeCalls(`${this.callKey(harnessSessionId, callId)}:`); } catch { return; }\n const at = this.now();\n for (const call of calls) {\n this.emit({\n kind: \"tool_outcome\", occurredAt: at, sessionId: call.session_id ?? undefined, parentActionId: call.parent_action_id,\n sourceReceiptHash: call.source_receipt_hash,\n data: outcomeData(call.operation, exit, this.adapterVersion, at - call.at),\n });\n }\n }\n\n // ---- policy acknowledgement and capability proof ------------------------------------------------\n\n policyAck(input: PolicyAckInput): EnqueueResult | null {\n return this.emit({ kind: \"policy_ack\", occurredAt: this.now(), data: policyAckData(input) });\n }\n\n capabilityProofs(proofs: CapabilityProofInput[]): number {\n let queued = 0;\n for (const proof of proofs) if (this.emit({ kind: \"capability\", occurredAt: this.now(), data: capabilityProofData(proof) })?.queued) queued += 1;\n return queued;\n }\n}\n\n// ---- helpers ---------------------------------------------------------------------------------\n\n/** HEAD of the workspace repository, or null when it cannot be read quickly. */\nexport function gitHead(cwd: string): string | null {\n try {\n const sha = execFileSync(\"git\", [\"-C\", cwd, \"rev-parse\", \"HEAD\"], { encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], timeout: 2000 }).trim();\n return /^([0-9a-f]{40}|[0-9a-f]{64})$/.test(sha) ? sha : null;\n } catch { return null; }\n}\n\n/** Count and oldest enqueue time of receipts not yet delivered to the workspace, from the\n * receipt outbox beside the local receipts. Null when there is no outbox. */\nexport function receiptBacklog(dir: string): { count: number; oldestAt: number } | null {\n const file = join(dir, \"receipts.db.cloud-outbox.db\");\n if (!existsSync(file)) return null;\n try {\n const require = createRequire(import.meta.url);\n const { DatabaseSync } = require(\"node:sqlite\") as { DatabaseSync: new (p: string) => { prepare(s: string): { get(): unknown }; close(): void } };\n const db = new DatabaseSync(file);\n try {\n const row = db.prepare(\"SELECT COUNT(*) AS n, MIN(enqueued_at) AS o FROM cloud_outbox\").get() as { n: number; o: number | null };\n return { count: row.n, oldestAt: row.o ?? Date.now() };\n } finally { db.close(); }\n } catch { return null; }\n}\n\n/** Claude Code's SessionEnd `reason` mapped to the stop-reason vocabulary. An unlisted or\n * missing reason is `unknown`, never a guess. */\nexport function stopReasonFromClaude(reason: unknown): SessionStopReason {\n switch (reason) {\n case \"clear\": case \"prompt_input_exit\": return \"completed\";\n case \"logout\": return \"cancelled\";\n default: return \"unknown\";\n }\n}\n\n/** Claude Code's failed-tool event to an exit category. */\nexport const exitFromClaudeFailure = (interrupted: unknown): ExitCategory => (interrupted === true ? \"cancelled\" : \"error\");\n\n/** A one-line, human description for `status`. */\nexport function describeObservations(dir: string): string[] {\n const connection = loadConnection(dir);\n const status = observationStatus(connection);\n if (status.state === \"off\") return [`off (${status.reason})`];\n if (status.state === \"unsupported\") return [`unsupported (${status.reason})`];\n const file = join(dir, OBSERVATION_DB);\n if (!existsSync(file)) return [\"on (nothing recorded yet)\"];\n let store: ObservationStore | undefined;\n try {\n store = new ObservationStore(file);\n const state = store.state();\n if (!state) return [\"on (nothing recorded yet)\"];\n const pending = store.pendingSummary();\n const terminal = store.terminalCounts();\n const terminalTotal = Object.values(terminal).reduce((a, b) => a + b, 0);\n const lines: string[] = [];\n const head = state.capability === \"active\" ? \"on\" : state.capability === \"unsupported\" ? \"unsupported by this workspace\" : \"paused\";\n lines.push(`${head}${state.reason ? ` (${state.reason})` : \"\"}; generation ${state.generation}; ${pending.count} pending${pending.oldest_at ? ` since ${new Date(pending.oldest_at).toISOString()}` : \"\"}`);\n if (terminalTotal > 0) lines.push(`${terminalTotal} refused or unsendable, kept locally: ${Object.entries(terminal).map(([code, n]) => `${code} ${n}`).join(\", \")}`);\n if (state.dropped > 0) lines.push(`${state.dropped} not queued (local queue full)`);\n if (state.last_error) lines.push(`last upload problem: ${state.last_error}`);\n return lines;\n } catch (error) {\n return [`on (status unavailable: ${(error as Error).message.slice(0, 80)})`];\n } finally { store?.close(); }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"obs-emitter.js","sourceRoot":"","sources":["../src/obs-emitter.ts"],"names":[],"mappings":"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","sourcesContent":["// Observation emitters: when the hook may emit, and what it emits.\n//\n// Emission is opt-in through the workspace enrollment. It is on only when the connection\n// (`cloud.json`) carries the `observations:write` scope AND the enrollment handoff gave an\n// installation id and generation AND the agent key's id matches the one the workspace\n// enrolled. Otherwise it is silently off (with a status line) or reported unsupported;\n// nothing is guessed. None of it can change a policy decision: every failure here is\n// swallowed by the caller and the local receipt path is untouched.\n//\n// Kinds emitted (an agent-adapter key may emit these and no others):\n// session start / stop, from Claude Code's SessionStart and SessionEnd hooks\n// health heartbeat (60 s while a session is explicitly active), queue telemetry\n// capability proof, from the `capabilities --prove` runner\n// policy_ack loaded / rejected, from `policy load` once an exported policy is verified or refused\n// tool_intent the request binding of each dispatched action, linked to its receipt\n// tool_outcome the after-action result, echoing the stored binding (Claude Code)\n//\n// There is no always-on process. Heartbeats come from one short helper per active session,\n// claimed under a lease so hook calls cannot multiply it; it ends when the session ends,\n// after ten minutes without hook activity (it then releases the lease with a final\n// `lease_active: false` heartbeat), or when the host clock jumps (the host slept: it records\n// a stop with reason `sleep` and emits nothing for the gap).\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { spawn, execFileSync } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { ingestUrl, loadConnection, type HookConnection } from \"./cloud.js\";\nimport {\n operationsForCall, capabilityProofData, digestPolicy, heartbeatData, intentData, loadOrCreateBindingKey, observationSigner,\n outcomeData, policyAckData, queueData, sessionStartData, sessionStopData, sourceReceiptHash,\n type BindingKey, type CallRequest, type ExitCategory, type ObservationSigner, type PolicyAckInput, type SessionStopReason, type CapabilityProofInput,\n} from \"./observation.js\";\nimport { openApprovalBinder } from \"@scopebond/gateway/node\";\nimport { OBSERVATION_DB, ObservationStore, type EnqueueResult } from \"./obs-store.js\";\nimport type { GitProbe } from \"./typed-ops.js\";\nimport { isProtectedBranch, loadRules } from \"./rules.js\";\nimport { uploadPending, type UploadOutcome } from \"./obs-upload.js\";\n\nexport const OBSERVATIONS_SCOPE = \"observations:write\";\nexport const HEARTBEAT_INTERVAL_MS = 60_000;\n/** A loop that finds the clock moved by more than this since its last tick treats the host as slept. */\nexport const SLEEP_GAP_MS = 3 * HEARTBEAT_INTERVAL_MS;\n/** Heartbeats continue this long after the last hook activity, then the lease is released. */\nexport const IDLE_LIMIT_MS = 10 * 60_000;\nexport const HEARTBEAT_LEASE_MS = 2.5 * HEARTBEAT_INTERVAL_MS;\nexport const QUEUE_TELEMETRY_INTERVAL_MS = 5 * 60_000;\n/** At most this many typed operations are recorded per tool call; the rest are counted. */\nexport const MAX_INTENTS_PER_CALL = 10;\n\nexport type ObservationStatus =\n | { state: \"off\"; reason: string }\n | { state: \"unsupported\"; reason: string }\n | { state: \"on\" };\n\n/** The installation id is the enrollment's `installation_id`, or its `gateway_id` (the same\n * identifier, which is what an enrollment answer carries today). The generation has no such\n * fallback: only the workspace can say what it is. */\nexport function withInstallationId(connection: HookConnection): HookConnection {\n return connection.installation_id === undefined && typeof connection.gateway_id === \"string\"\n ? { ...connection, installation_id: connection.gateway_id } : connection;\n}\n\n/** Decide from the connection alone whether emission is enabled. Pure; reads nothing. */\nexport function observationStatus(input: HookConnection | null, keyKid?: string): ObservationStatus {\n if (!input) return { state: \"off\", reason: \"not connected to a workspace\" };\n const connection = withInstallationId(input);\n if (!Array.isArray(connection.scopes) || !connection.scopes.includes(OBSERVATIONS_SCOPE)) {\n return { state: \"off\", reason: `this enrollment does not grant ${OBSERVATIONS_SCOPE}` };\n }\n if (typeof connection.installation_id !== \"string\" || !/^[\\x21-\\x7e]{1,200}$/.test(connection.installation_id)) {\n return { state: \"unsupported\", reason: \"the enrollment did not include an installation id; reconnect after the workspace supports observations\" };\n }\n if (!Number.isInteger(connection.installation_generation) || (connection.installation_generation as number) < 1 || (connection.installation_generation as number) > 2_147_483_647) {\n return { state: \"unsupported\", reason: \"the enrollment did not include an installation generation; reconnect after the workspace supports observations\" };\n }\n if (typeof connection.agent_kid !== \"string\" || !/^[A-Za-z0-9:._-]{1,200}$/.test(connection.agent_kid)) {\n return { state: \"unsupported\", reason: \"the enrollment did not register the agent signing key\" };\n }\n if (keyKid !== undefined && keyKid !== connection.agent_kid) {\n return { state: \"unsupported\", reason: \"the local agent key is not the key the workspace enrolled\" };\n }\n return { state: \"on\" };\n}\n\nexport interface EmitterOptions {\n fetch?: typeof fetch;\n now?: () => number;\n adapterVersion?: string;\n /** Tests: do not start the heartbeat helper process. */\n spawnHeartbeat?: boolean;\n /** Bound on one upload attempt from a hook call. */\n flushTimeoutMs?: number;\n}\n\nexport interface OpenResult {\n status: ObservationStatus;\n emitter?: ObservationEmitter;\n}\n\n/** Open the emitter for a hook config dir, or explain why not. Never throws: an\n * unreadable key or store simply leaves observations off. */\nexport function openObservations(dir: string, options: EmitterOptions = {}): OpenResult {\n const loaded = loadConnection(dir);\n const early = observationStatus(loaded);\n if (early.state !== \"on\" || !loaded) return { status: early };\n const connection = withInstallationId(loaded);\n let store: ObservationStore | undefined;\n try {\n const keyPem = readFileSync(join(dir, \"agent.key\"), \"utf8\");\n const signer = observationSigner(keyPem, connection.agent_kid as string);\n store = new ObservationStore(join(dir, OBSERVATION_DB), options.now);\n const bound = store.bindGeneration(connection.installation_generation as number);\n if (!bound.ok) { store.close(); return { status: { state: \"unsupported\", reason: bound.reason } }; }\n const emitter = new ObservationEmitter(dir, connection, store, signer, loadOrCreateBindingKey(dir), options);\n return { status: { state: \"on\" }, emitter };\n } catch (error) {\n try { store?.close(); } catch { /* nothing to release */ }\n return { status: { state: \"unsupported\", reason: `observation setup failed: ${(error as Error).message.slice(0, 120)}` } };\n }\n}\n\nexport class ObservationEmitter {\n constructor(\n readonly dir: string,\n readonly connection: HookConnection,\n readonly store: ObservationStore,\n private readonly signer: ObservationSigner,\n readonly binding: BindingKey,\n private readonly options: EmitterOptions = {},\n ) {}\n\n private now(): number { return (this.options.now ?? Date.now)(); }\n private get context() {\n return { installationId: this.connection.installation_id as string, generation: this.connection.installation_generation as number };\n }\n private get adapterVersion(): string { return this.options.adapterVersion ?? \"unknown\"; }\n\n /** Queue one observation. Any failure is contained: emission never throws to the caller. */\n emit(draft: Parameters<ObservationStore[\"enqueue\"]>[0]): EnqueueResult | null {\n try { return this.store.enqueue(draft, this.context, this.signer); } catch { return null; }\n }\n\n /** Try to deliver the outbox, bounded. Never throws. */\n async flush(timeoutMs = this.options.flushTimeoutMs ?? 800): Promise<UploadOutcome | null> {\n try {\n return await uploadPending(this.store, {\n url: ingestUrl(this.connection), credential: this.connection.credential,\n fetch: this.options.fetch, now: this.options.now, timeoutMs,\n });\n } catch { return null; }\n }\n\n close(): void { this.store.close(); }\n\n // ---- session lifecycle ---------------------------------------------------------------------\n\n sessionIdOf(harnessSessionId: string): string { return this.binding.sessionId(harnessSessionId); }\n\n /** A local key for a harness tool call, so the after-action hook finds what the\n * before-action hook recorded. Keyed, so the raw ids are not written to the local store. */\n private callKey(harnessSessionId: string | undefined, callId: string): string {\n return this.binding.resourceId(\"call\", `${harnessSessionId ?? \"\"}\\u0000${callId}`);\n }\n\n /** SessionStart: record one start per session and make sure the heartbeat helper runs. */\n sessionStart(harnessSessionId: string, cwd: string): void {\n const sessionId = this.sessionIdOf(harnessSessionId);\n const repositoryId = this.binding.resourceId(\"repo\", cwd);\n if (this.store.activateSession(sessionId, repositoryId)) {\n this.emit({ kind: \"session\", occurredAt: this.now(), sessionId, data: sessionStartData(repositoryId) });\n }\n this.ensureHeartbeat(sessionId);\n }\n\n /** SessionEnd: record one stop, with its reason, only for a session that was active. */\n sessionStop(harnessSessionId: string, reason: SessionStopReason): void {\n const sessionId = this.sessionIdOf(harnessSessionId);\n const row = this.store.session(sessionId);\n if (this.store.deactivateSession(sessionId, reason)) {\n this.emit({ kind: \"session\", occurredAt: this.now(), sessionId, data: sessionStopData(reason, row?.repository_id ?? undefined) });\n }\n }\n\n /** Hook activity in a session: keeps the lease alive, and resumes a session that was\n * stopped only because the host slept. A session that was ended, or never started\n * through the lifecycle hooks, is left alone. */\n activity(harnessSessionId: string, cwd: string): void {\n const sessionId = this.sessionIdOf(harnessSessionId);\n const row = this.store.session(sessionId);\n if (!row) return;\n if (row.state === \"stopped\" && row.stopped_reason === \"sleep\") { this.sessionStart(harnessSessionId, cwd); return; }\n if (row.state !== \"active\") return;\n this.store.touchSession(sessionId);\n this.ensureHeartbeat(sessionId);\n }\n\n /** Start the single heartbeat helper for a session unless one already holds the lease. */\n ensureHeartbeat(sessionId: string): void {\n // SCOPEBOND_OBSERVATIONS_HEARTBEAT=off keeps session start/stop and tool observations but\n // runs no helper process (tests, or a host where a background process is unwelcome).\n if (this.options.spawnHeartbeat === false || process.env.SCOPEBOND_OBSERVATIONS_HEARTBEAT === \"off\") return;\n try {\n if (!this.store.claimHeartbeat(sessionId, HEARTBEAT_LEASE_MS)) return;\n const cli = fileURLToPath(new URL(\"./cli.js\", import.meta.url));\n const child = spawn(process.execPath, [cli, \"observations\", \"heartbeat\", sessionId], {\n detached: true, stdio: \"ignore\", windowsHide: true, env: { ...process.env, SCOPEBOND_HOOK_DIR: this.dir },\n });\n child.on(\"error\", () => { /* the lease lapses and the next hook call tries again */ });\n child.unref();\n } catch { /* best effort */ }\n }\n\n // ---- health --------------------------------------------------------------------------------\n\n private policyDigest(): string | undefined {\n try { return digestPolicy(JSON.parse(readFileSync(join(this.dir, \"policy.json\"), \"utf8\"))); } catch { return undefined; }\n }\n\n /** Queue oldest-pending-receipt time and count, at most every five minutes, and once more\n * when a backlog drains. */\n queueTelemetry(): void {\n const at = this.now();\n const last = this.store.getMark(\"queue_at\");\n const backlog = receiptBacklog(this.dir);\n if (!backlog) return;\n const previous = this.store.getMark(\"queue_pending\") ?? 0;\n if (backlog.count === 0 && previous === 0) return;\n if (backlog.count > 0 && last !== null && at - last < QUEUE_TELEMETRY_INTERVAL_MS) return;\n this.emit({ kind: \"health\", occurredAt: at, data: queueData(backlog.count > 0 ? backlog.oldestAt : at, backlog.count, this.policyDigest()) });\n this.store.mark(\"queue_at\", at);\n this.store.mark(\"queue_pending\", backlog.count);\n }\n\n /**\n * One heartbeat-loop tick. Returns whether the loop should keep running.\n * - session no longer active: stop quietly\n * - the clock moved more than SLEEP_GAP_MS since the last tick: the host slept; record a\n * stop (reason sleep) at the last known time and emit no heartbeat for the gap\n * - no hook activity for IDLE_LIMIT_MS: send a final heartbeat that releases the lease\n * - otherwise: heartbeat with the lease active\n */\n heartbeatTick(sessionId: string, lastTickAt: number): \"continue\" | \"stop\" {\n const at = this.now();\n const row = this.store.session(sessionId);\n if (!row || row.state !== \"active\") { this.store.releaseHeartbeat(sessionId); return \"stop\"; }\n if (at - lastTickAt > SLEEP_GAP_MS) {\n if (this.store.deactivateSession(sessionId, \"sleep\")) {\n this.emit({ kind: \"session\", occurredAt: lastTickAt, sessionId, data: sessionStopData(\"sleep\", row.repository_id ?? undefined) });\n }\n return \"stop\";\n }\n if (at - row.last_activity_at > IDLE_LIMIT_MS) {\n this.emit({ kind: \"health\", occurredAt: at, sessionId, data: heartbeatData(false, { policyDigest: this.policyDigest() }) });\n this.store.releaseHeartbeat(sessionId);\n return \"stop\";\n }\n this.emit({ kind: \"health\", occurredAt: at, sessionId, data: heartbeatData(true, { policyDigest: this.policyDigest() }) });\n this.queueTelemetry();\n return this.store.renewHeartbeat(sessionId, HEARTBEAT_LEASE_MS, true) ? \"continue\" : \"stop\";\n }\n\n // ---- actions -------------------------------------------------------------------------------\n\n /**\n * Record the request binding of each dispatched action, linked to its receipt, and keep\n * the operation so the after-action hook can echo it. `dispatched` is what was actually\n * evaluated: the final intent (after root scoping and grouping) and its receipt.\n */\n toolIntents(input: {\n harnessSessionId?: string; callId?: string; cwd: string;\n dispatched: Array<{ action: { action_type: string; params: Record<string, unknown> }; receipt?: unknown }>;\n /** The tool call as sent, so typed operations read the actual request: a shell tool's raw\n * command, or an MCP tool's server, name and input. */\n request?: CallRequest;\n probe?: GitProbe;\n }): void {\n const sessionId = input.harnessSessionId ? this.sessionIdOf(input.harnessSessionId) : undefined;\n const repositoryId = this.binding.resourceId(\"repo\", input.cwd);\n const rules = loadRules(this.dir);\n const operations = operationsForCall({ dispatched: input.dispatched, request: input.request }, {\n key: this.binding, cwd: input.cwd, repositoryId,\n ...(input.probe ? { probe: input.probe } : {}), ...(rules ? { isProtectedRef: (ref: string) => isProtectedBranch(rules, ref) } : {}),\n ...(process.env.SCOPEBOND_REQUIRED_CHECK_POLICY_VERSION ? { requiredCheckPolicyVersion: process.env.SCOPEBOND_REQUIRED_CHECK_POLICY_VERSION.slice(0, 200) } : {}),\n });\n // With approvals held in the workspace, an operation carries the hash the dispatch guard asks it to consume, so a consumed approval can be correlated.\n const binder = openApprovalBinder(this.dir);\n let recorded = 0;\n input.dispatched.forEach((item, index) => {\n const receipt = item.receipt as { payload?: { action_ref?: { action_id?: unknown } } } | undefined;\n const actionId = receipt?.payload?.action_ref?.action_id;\n if (typeof actionId !== \"string\" || actionId === \"\" || actionId.length > 200) return;\n if (recorded >= MAX_INTENTS_PER_CALL) { this.store.mark(\"omitted_intents\", (this.store.getMark(\"omitted_intents\") ?? 0) + 1); return; }\n const plain = operations[index];\n if (!plain) return;\n const operation = binder ? binder.bind(plain, item.action) : plain;\n const linked = sourceReceiptHash(item.receipt);\n const result = this.emit({ kind: \"tool_intent\", occurredAt: this.now(), sessionId, parentActionId: actionId, sourceReceiptHash: linked, data: intentData(operation) });\n recorded += 1;\n if (result?.queued && input.callId) {\n try {\n this.store.recordCall({ call_key: `${this.callKey(input.harnessSessionId, input.callId)}:${String(index).padStart(3, \"0\")}`, parent_action_id: actionId, source_receipt_hash: linked, session_id: sessionId ?? null, operation });\n } catch { /* the outcome is then simply not recorded */ }\n }\n });\n }\n\n /** After-action result for a call whose intents were recorded. `exit` comes from the\n * harness's own success/failure event, never from model text. */\n toolOutcomes(harnessSessionId: string | undefined, callId: string, exit: ExitCategory): void {\n let calls;\n try { calls = this.store.takeCalls(`${this.callKey(harnessSessionId, callId)}:`); } catch { return; }\n const at = this.now();\n for (const call of calls) {\n this.emit({\n kind: \"tool_outcome\", occurredAt: at, sessionId: call.session_id ?? undefined, parentActionId: call.parent_action_id,\n sourceReceiptHash: call.source_receipt_hash,\n data: outcomeData(call.operation, exit, this.adapterVersion, at - call.at),\n });\n }\n }\n\n // ---- policy acknowledgement and capability proof ------------------------------------------------\n\n policyAck(input: PolicyAckInput): EnqueueResult | null {\n return this.emit({ kind: \"policy_ack\", occurredAt: this.now(), data: policyAckData(input) });\n }\n\n capabilityProofs(proofs: CapabilityProofInput[]): number {\n let queued = 0;\n for (const proof of proofs) if (this.emit({ kind: \"capability\", occurredAt: this.now(), data: capabilityProofData(proof) })?.queued) queued += 1;\n return queued;\n }\n}\n\n// ---- helpers ---------------------------------------------------------------------------------\n\n/** HEAD of the workspace repository, or null when it cannot be read quickly. */\nexport function gitHead(cwd: string): string | null {\n try {\n const sha = execFileSync(\"git\", [\"-C\", cwd, \"rev-parse\", \"HEAD\"], { encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], timeout: 2000 }).trim();\n return /^([0-9a-f]{40}|[0-9a-f]{64})$/.test(sha) ? sha : null;\n } catch { return null; }\n}\n\n/** Count and oldest enqueue time of receipts not yet delivered to the workspace, from the\n * receipt outbox beside the local receipts. Null when there is no outbox. */\nexport function receiptBacklog(dir: string): { count: number; oldestAt: number } | null {\n const file = join(dir, \"receipts.db.cloud-outbox.db\");\n if (!existsSync(file)) return null;\n try {\n const require = createRequire(import.meta.url);\n const { DatabaseSync } = require(\"node:sqlite\") as { DatabaseSync: new (p: string) => { prepare(s: string): { get(): unknown }; close(): void } };\n const db = new DatabaseSync(file);\n try {\n const row = db.prepare(\"SELECT COUNT(*) AS n, MIN(enqueued_at) AS o FROM cloud_outbox\").get() as { n: number; o: number | null };\n return { count: row.n, oldestAt: row.o ?? Date.now() };\n } finally { db.close(); }\n } catch { return null; }\n}\n\n/** Claude Code's SessionEnd `reason` mapped to the stop-reason vocabulary. An unlisted or\n * missing reason is `unknown`, never a guess. */\nexport function stopReasonFromClaude(reason: unknown): SessionStopReason {\n switch (reason) {\n case \"clear\": case \"prompt_input_exit\": return \"completed\";\n case \"logout\": return \"cancelled\";\n default: return \"unknown\";\n }\n}\n\n/** Claude Code's failed-tool event to an exit category. */\nexport const exitFromClaudeFailure = (interrupted: unknown): ExitCategory => (interrupted === true ? \"cancelled\" : \"error\");\n\n/** A one-line, human description for `status`. */\nexport function describeObservations(dir: string): string[] {\n const connection = loadConnection(dir);\n const status = observationStatus(connection);\n if (status.state === \"off\") return [`off (${status.reason})`];\n if (status.state === \"unsupported\") return [`unsupported (${status.reason})`];\n const file = join(dir, OBSERVATION_DB);\n if (!existsSync(file)) return [\"on (nothing recorded yet)\"];\n let store: ObservationStore | undefined;\n try {\n store = new ObservationStore(file);\n const state = store.state();\n if (!state) return [\"on (nothing recorded yet)\"];\n const pending = store.pendingSummary();\n const terminal = store.terminalCounts();\n const terminalTotal = Object.values(terminal).reduce((a, b) => a + b, 0);\n const lines: string[] = [];\n const head = state.capability === \"active\" ? \"on\" : state.capability === \"unsupported\" ? \"unsupported by this workspace\" : \"paused\";\n lines.push(`${head}${state.reason ? ` (${state.reason})` : \"\"}; generation ${state.generation}; ${pending.count} pending${pending.oldest_at ? ` since ${new Date(pending.oldest_at).toISOString()}` : \"\"}`);\n if (terminalTotal > 0) lines.push(`${terminalTotal} refused or unsendable, kept locally: ${Object.entries(terminal).map(([code, n]) => `${code} ${n}`).join(\", \")}`);\n if (state.dropped > 0) lines.push(`${state.dropped} not queued (local queue full)`);\n if (state.last_error) lines.push(`last upload problem: ${state.last_error}`);\n return lines;\n } catch (error) {\n return [`on (status unavailable: ${(error as Error).message.slice(0, 80)})`];\n } finally { store?.close(); }\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type RefusalReason } from "./managed.js";
|
|
2
|
+
export declare const SYNC_INTERVAL_MS: number;
|
|
3
|
+
/** How long a tool call may spend on the five-minute check, at most (`SCOPEBOND_POLICY_SYNC_MS` overrides). */
|
|
4
|
+
export declare const INLINE_BUDGET_MS = 1500;
|
|
5
|
+
export type SyncOutcome = {
|
|
6
|
+
state: "not_connected";
|
|
7
|
+
} | {
|
|
8
|
+
state: "own_rules";
|
|
9
|
+
changed: boolean;
|
|
10
|
+
} | {
|
|
11
|
+
state: "unchanged";
|
|
12
|
+
revision: number;
|
|
13
|
+
} | {
|
|
14
|
+
state: "applied";
|
|
15
|
+
revision: number;
|
|
16
|
+
} | {
|
|
17
|
+
state: "refused";
|
|
18
|
+
revision: number | null;
|
|
19
|
+
reason: RefusalReason;
|
|
20
|
+
message: string;
|
|
21
|
+
} | {
|
|
22
|
+
state: "disconnected";
|
|
23
|
+
} | {
|
|
24
|
+
state: "unavailable";
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
export interface SyncOptions {
|
|
28
|
+
agentKid: string;
|
|
29
|
+
hookVersion: string;
|
|
30
|
+
policyBuilds: (policy: Record<string, unknown>) => boolean;
|
|
31
|
+
fetchImpl?: typeof fetch;
|
|
32
|
+
now?: () => Date;
|
|
33
|
+
/** Recompute a project's trust pin after its policy.json changed (the same step `rules apply` takes). */
|
|
34
|
+
afterPolicyWrite?: (dir: string) => void;
|
|
35
|
+
/** Per-request timeout; defaults to five seconds. */
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}
|
|
38
|
+
export declare function syncPolicy(dir: string, options: SyncOptions): Promise<SyncOutcome>;
|
|
39
|
+
/** Called on every tool call: when this computer is connected and has not checked in five minutes, run the check, capped at
|
|
40
|
+
* `budgetMs`. Resolves `null` when no check was due, another one is running, or the cap was reached first (the check then
|
|
41
|
+
* finishes if the process lives long enough, else runs again on a later call). Never throws. `makeOptions` is called only when a
|
|
42
|
+
* check is due, so an ordinary call pays nothing but two file reads. */
|
|
43
|
+
export declare function syncIfDue(dir: string, makeOptions: () => SyncOptions, budgetMs?: number, now?: number): Promise<SyncOutcome | null>;
|
|
44
|
+
export declare function releaseSyncLock(dir: string): void;
|
|
45
|
+
//# sourceMappingURL=policy-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-sync.d.ts","sourceRoot":"","sources":["../src/policy-sync.ts"],"names":[],"mappings":"AAmBA,OAAO,EAC2F,KAAK,aAAa,EACnH,MAAM,cAAc,CAAC;AAEtB,eAAO,MAAM,gBAAgB,QAAgB,CAAC;AAI9C,+GAA+G;AAC/G,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AAGtC,MAAM,MAAM,WAAW,GACnB;IAAE,KAAK,EAAE,eAAe,CAAA;CAAE,GAC1B;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACxC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrF;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,GACzB;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IAC3D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,yGAAyG;IACzG,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA8FxF;AAED;;;yEAGyE;AACzE,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,WAAW,EAAE,QAAQ,SAAmE,EAAE,GAAG,SAAa,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAyBvM;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEjD"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Keep a connected computer's rules in step with its Scopebond Cloud workspace.
|
|
2
|
+
//
|
|
3
|
+
// `syncPolicy` asks the workspace for this computer's rules (`GET /v1/policy`), installs a newer version when there is one, and
|
|
4
|
+
// tells the workspace exactly what it loaded or why it could not (`POST /v1/policy/ack`). A tool call checks two small files;
|
|
5
|
+
// at most every five minutes it also runs the check (`syncIfDue`), in parallel with the delivery of its activity record and
|
|
6
|
+
// capped at about one and a half seconds, so a change made in the workspace applies within a few minutes of the agent's next
|
|
7
|
+
// action. It runs in the hook's own process on purpose: a detached helper process inherits the hook's output pipe on Windows,
|
|
8
|
+
// which makes the coding agent wait for the helper. Any failure or timeout keeps the rules already in force; an interrupted
|
|
9
|
+
// write cannot leave a half-written policy, because every write is a temp file and a rename.
|
|
10
|
+
//
|
|
11
|
+
// Answers from the workspace:
|
|
12
|
+
// 204 the workspace does not set rules for this computer: use (or go back to) its own rules
|
|
13
|
+
// 304 nothing changed since the last check
|
|
14
|
+
// 200 a rules document: checked, installed if newer, confirmed
|
|
15
|
+
// 401 the connection is no longer valid (revoked, expired, removed): go back to this computer's own rules
|
|
16
|
+
import { closeSync, existsSync, openSync, rmSync, statSync, writeSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { connectionPath, loadConnection } from "./cloud.js";
|
|
19
|
+
import { inspectManaged, installManaged, isManaged, readMeta, restoreLocal, writeMeta, } from "./managed.js";
|
|
20
|
+
export const SYNC_INTERVAL_MS = 5 * 60 * 1000;
|
|
21
|
+
const LOCK_FILE = "managed-sync.lock";
|
|
22
|
+
const LOCK_STALE_MS = 2 * 60 * 1000;
|
|
23
|
+
const REQUEST_TIMEOUT_MS = 5_000;
|
|
24
|
+
/** How long a tool call may spend on the five-minute check, at most (`SCOPEBOND_POLICY_SYNC_MS` overrides). */
|
|
25
|
+
export const INLINE_BUDGET_MS = 1_500;
|
|
26
|
+
const UNMANAGED_DIGEST = "0".repeat(64);
|
|
27
|
+
export async function syncPolicy(dir, options) {
|
|
28
|
+
const connection = loadConnection(dir);
|
|
29
|
+
if (!connection)
|
|
30
|
+
return { state: "not_connected" };
|
|
31
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
32
|
+
const now = (options.now ?? (() => new Date()))();
|
|
33
|
+
const installationId = connection.installation_id ?? connection.gateway_id;
|
|
34
|
+
const base = connection.url.replace(/\/+$/, "");
|
|
35
|
+
const auth = { authorization: `Bearer ${connection.credential}` };
|
|
36
|
+
const meta = readMeta(dir);
|
|
37
|
+
const save = (patch) => writeMeta(dir, { ...meta, ...patch, checked_at: now.toISOString() });
|
|
38
|
+
const ack = async (body) => {
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetchImpl(`${base}/v1/policy/ack`, {
|
|
41
|
+
method: "POST", headers: { ...auth, "content-type": "application/json" }, redirect: "error", signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS),
|
|
42
|
+
body: JSON.stringify({ ...body, reason: body.reason ?? null, hook_version: options.hookVersion }),
|
|
43
|
+
});
|
|
44
|
+
return res.ok ? { revision: body.revision, result: body.result, at: now.toISOString() } : meta.last_ack;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return meta.last_ack;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const backToOwnRules = () => {
|
|
51
|
+
if (!isManaged(dir))
|
|
52
|
+
return false;
|
|
53
|
+
restoreLocal(dir, options.agentKid);
|
|
54
|
+
options.afterPolicyWrite?.(dir);
|
|
55
|
+
return true;
|
|
56
|
+
};
|
|
57
|
+
let res;
|
|
58
|
+
try {
|
|
59
|
+
res = await fetchImpl(`${base}/v1/policy`, {
|
|
60
|
+
// The hook's version tells the workspace which settings this computer understands (for example exact-target
|
|
61
|
+
// exclusions), so it is never sent a document an older hook would refuse.
|
|
62
|
+
headers: { ...auth, ...(options.hookVersion ? { "x-scopebond-hook-version": options.hookVersion } : {}), ...(isManaged(dir) && meta.etag ? { "if-none-match": meta.etag } : {}) },
|
|
63
|
+
redirect: "error", signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
save({ last_error: `could not reach the workspace (${error.name}); the rules in force stay` });
|
|
68
|
+
return { state: "unavailable", message: "could not reach the workspace" };
|
|
69
|
+
}
|
|
70
|
+
if (res.status === 401) {
|
|
71
|
+
backToOwnRules();
|
|
72
|
+
save({ revision: null, rules_digest: null, export_id: null, etag: null, last_error: "the workspace connection is no longer valid; this computer uses its own rules" });
|
|
73
|
+
return { state: "disconnected" };
|
|
74
|
+
}
|
|
75
|
+
if (res.status === 204) {
|
|
76
|
+
const changed = backToOwnRules();
|
|
77
|
+
const last = meta.last_ack;
|
|
78
|
+
const confirmed = changed || !last || last.revision !== 0
|
|
79
|
+
? await ack({ export_id: `rev-0-${installationId}`, revision: 0, rules_digest: UNMANAGED_DIGEST, result: "loaded" }) : last;
|
|
80
|
+
save({ revision: null, rules_digest: null, export_id: null, etag: null, last_ack: confirmed, last_error: null });
|
|
81
|
+
return { state: "own_rules", changed };
|
|
82
|
+
}
|
|
83
|
+
if (res.status === 304 && isManaged(dir) && meta.revision !== null) {
|
|
84
|
+
const confirmed = meta.last_ack?.revision === meta.revision && meta.last_ack.result === "loaded" ? meta.last_ack
|
|
85
|
+
: await ack({ export_id: meta.export_id, revision: meta.revision, rules_digest: meta.rules_digest, result: "loaded" });
|
|
86
|
+
save({ last_ack: confirmed, last_error: null });
|
|
87
|
+
return { state: "unchanged", revision: meta.revision };
|
|
88
|
+
}
|
|
89
|
+
if (res.status !== 200) {
|
|
90
|
+
save({ last_error: `the workspace answered ${res.status}; the rules in force stay` });
|
|
91
|
+
return { state: "unavailable", message: `the workspace answered ${res.status}` };
|
|
92
|
+
}
|
|
93
|
+
let raw;
|
|
94
|
+
try {
|
|
95
|
+
raw = await res.json();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
raw = null;
|
|
99
|
+
}
|
|
100
|
+
const r = (raw && typeof raw === "object" ? raw : {});
|
|
101
|
+
const etag = res.headers.get("etag");
|
|
102
|
+
// The same version already in force: confirm it again if the last confirmation did not land.
|
|
103
|
+
if (isManaged(dir) && r.revision === meta.revision && r.rules_digest === meta.rules_digest && meta.revision !== null) {
|
|
104
|
+
const confirmed = meta.last_ack?.revision === meta.revision ? meta.last_ack
|
|
105
|
+
: await ack({ export_id: meta.export_id, revision: meta.revision, rules_digest: meta.rules_digest, result: "loaded" });
|
|
106
|
+
save({ etag, last_ack: confirmed, last_error: null });
|
|
107
|
+
return { state: "unchanged", revision: meta.revision };
|
|
108
|
+
}
|
|
109
|
+
const inspected = inspectManaged(raw, { installationId, currentRevision: isManaged(dir) ? meta.revision : null, currentDigest: isManaged(dir) ? meta.rules_digest : null });
|
|
110
|
+
const echo = typeof r.export_id === "string" && Number.isInteger(r.revision) && typeof r.rules_digest === "string" && /^[0-9a-f]{64}$/.test(r.rules_digest)
|
|
111
|
+
? { export_id: r.export_id, revision: r.revision, rules_digest: r.rules_digest } : null;
|
|
112
|
+
if (!inspected.ok) {
|
|
113
|
+
const confirmed = echo ? await ack({ ...echo, result: "rejected", reason: inspected.reason }) : meta.last_ack;
|
|
114
|
+
save({ last_ack: confirmed, last_error: inspected.message });
|
|
115
|
+
return { state: "refused", revision: echo?.revision ?? null, reason: inspected.reason, message: inspected.message };
|
|
116
|
+
}
|
|
117
|
+
const doc = inspected.doc;
|
|
118
|
+
const installed = installManaged(dir, doc, options.agentKid, options.policyBuilds);
|
|
119
|
+
if (!installed.ok) {
|
|
120
|
+
const confirmed = await ack({ export_id: doc.export_id, revision: doc.revision, rules_digest: doc.rules_digest, result: "rejected", reason: installed.reason });
|
|
121
|
+
save({ last_ack: confirmed, last_error: installed.message });
|
|
122
|
+
return { state: "refused", revision: doc.revision, reason: installed.reason, message: installed.message };
|
|
123
|
+
}
|
|
124
|
+
options.afterPolicyWrite?.(dir);
|
|
125
|
+
const confirmed = await ack({ export_id: doc.export_id, revision: doc.revision, rules_digest: doc.rules_digest, result: "loaded" });
|
|
126
|
+
save({ revision: doc.revision, rules_digest: doc.rules_digest, export_id: doc.export_id, etag, last_ack: confirmed, last_error: null });
|
|
127
|
+
return { state: "applied", revision: doc.revision };
|
|
128
|
+
}
|
|
129
|
+
/** Called on every tool call: when this computer is connected and has not checked in five minutes, run the check, capped at
|
|
130
|
+
* `budgetMs`. Resolves `null` when no check was due, another one is running, or the cap was reached first (the check then
|
|
131
|
+
* finishes if the process lives long enough, else runs again on a later call). Never throws. `makeOptions` is called only when a
|
|
132
|
+
* check is due, so an ordinary call pays nothing but two file reads. */
|
|
133
|
+
export async function syncIfDue(dir, makeOptions, budgetMs = Number(process.env.SCOPEBOND_POLICY_SYNC_MS ?? INLINE_BUDGET_MS), now = Date.now()) {
|
|
134
|
+
try {
|
|
135
|
+
if (process.env.SCOPEBOND_POLICY_SYNC === "off")
|
|
136
|
+
return null;
|
|
137
|
+
if (!existsSync(connectionPath(dir)))
|
|
138
|
+
return null;
|
|
139
|
+
const checked = readMeta(dir).checked_at;
|
|
140
|
+
if (checked && now - Date.parse(checked) < SYNC_INTERVAL_MS)
|
|
141
|
+
return null;
|
|
142
|
+
const lock = join(dir, LOCK_FILE);
|
|
143
|
+
if (existsSync(lock)) {
|
|
144
|
+
if (now - statSync(lock).mtimeMs < LOCK_STALE_MS)
|
|
145
|
+
return null;
|
|
146
|
+
rmSync(lock, { force: true });
|
|
147
|
+
}
|
|
148
|
+
const fd = openSync(lock, "wx");
|
|
149
|
+
writeSync(fd, String(process.pid));
|
|
150
|
+
closeSync(fd);
|
|
151
|
+
const budget = Number.isFinite(budgetMs) && budgetMs > 0 ? budgetMs : INLINE_BUDGET_MS;
|
|
152
|
+
const work = Promise.resolve()
|
|
153
|
+
.then(() => syncPolicy(dir, { ...makeOptions(), timeoutMs: budget }))
|
|
154
|
+
.catch(() => ({ state: "unavailable", message: "the check failed" }))
|
|
155
|
+
.finally(() => releaseSyncLock(dir));
|
|
156
|
+
let timer;
|
|
157
|
+
const capped = new Promise((resolve) => { timer = setTimeout(() => resolve(null), budget); });
|
|
158
|
+
const outcome = await Promise.race([work, capped]);
|
|
159
|
+
if (timer)
|
|
160
|
+
clearTimeout(timer);
|
|
161
|
+
return outcome;
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export function releaseSyncLock(dir) {
|
|
168
|
+
try {
|
|
169
|
+
rmSync(join(dir, LOCK_FILE), { force: true });
|
|
170
|
+
}
|
|
171
|
+
catch { /* expires on its own */ }
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=policy-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-sync.js","sourceRoot":"","sources":["../src/policy-sync.ts"],"names":[],"mappings":"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","sourcesContent":["// Keep a connected computer's rules in step with its Scopebond Cloud workspace.\n//\n// `syncPolicy` asks the workspace for this computer's rules (`GET /v1/policy`), installs a newer version when there is one, and\n// tells the workspace exactly what it loaded or why it could not (`POST /v1/policy/ack`). A tool call checks two small files;\n// at most every five minutes it also runs the check (`syncIfDue`), in parallel with the delivery of its activity record and\n// capped at about one and a half seconds, so a change made in the workspace applies within a few minutes of the agent's next\n// action. It runs in the hook's own process on purpose: a detached helper process inherits the hook's output pipe on Windows,\n// which makes the coding agent wait for the helper. Any failure or timeout keeps the rules already in force; an interrupted\n// write cannot leave a half-written policy, because every write is a temp file and a rename.\n//\n// Answers from the workspace:\n// 204 the workspace does not set rules for this computer: use (or go back to) its own rules\n// 304 nothing changed since the last check\n// 200 a rules document: checked, installed if newer, confirmed\n// 401 the connection is no longer valid (revoked, expired, removed): go back to this computer's own rules\n\nimport { closeSync, existsSync, openSync, rmSync, statSync, writeSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { connectionPath, loadConnection } from \"./cloud.js\";\nimport {\n inspectManaged, installManaged, isManaged, readMeta, restoreLocal, writeMeta, type ManagedMeta, type RefusalReason,\n} from \"./managed.js\";\n\nexport const SYNC_INTERVAL_MS = 5 * 60 * 1000;\nconst LOCK_FILE = \"managed-sync.lock\";\nconst LOCK_STALE_MS = 2 * 60 * 1000;\nconst REQUEST_TIMEOUT_MS = 5_000;\n/** How long a tool call may spend on the five-minute check, at most (`SCOPEBOND_POLICY_SYNC_MS` overrides). */\nexport const INLINE_BUDGET_MS = 1_500;\nconst UNMANAGED_DIGEST = \"0\".repeat(64);\n\nexport type SyncOutcome =\n | { state: \"not_connected\" }\n | { state: \"own_rules\"; changed: boolean }\n | { state: \"unchanged\"; revision: number }\n | { state: \"applied\"; revision: number }\n | { state: \"refused\"; revision: number | null; reason: RefusalReason; message: string }\n | { state: \"disconnected\" }\n | { state: \"unavailable\"; message: string };\n\nexport interface SyncOptions {\n agentKid: string;\n hookVersion: string;\n policyBuilds: (policy: Record<string, unknown>) => boolean;\n fetchImpl?: typeof fetch;\n now?: () => Date;\n /** Recompute a project's trust pin after its policy.json changed (the same step `rules apply` takes). */\n afterPolicyWrite?: (dir: string) => void;\n /** Per-request timeout; defaults to five seconds. */\n timeoutMs?: number;\n}\n\nexport async function syncPolicy(dir: string, options: SyncOptions): Promise<SyncOutcome> {\n const connection = loadConnection(dir);\n if (!connection) return { state: \"not_connected\" };\n const fetchImpl = options.fetchImpl ?? fetch;\n const now = (options.now ?? (() => new Date()))();\n const installationId = (connection as { installation_id?: string }).installation_id ?? connection.gateway_id;\n const base = connection.url.replace(/\\/+$/, \"\");\n const auth = { authorization: `Bearer ${connection.credential}` };\n const meta = readMeta(dir);\n const save = (patch: Partial<ManagedMeta>) => writeMeta(dir, { ...meta, ...patch, checked_at: now.toISOString() });\n\n const ack = async (body: { export_id: string; revision: number; rules_digest: string; result: \"loaded\" | \"rejected\"; reason?: RefusalReason }): Promise<ManagedMeta[\"last_ack\"]> => {\n try {\n const res = await fetchImpl(`${base}/v1/policy/ack`, {\n method: \"POST\", headers: { ...auth, \"content-type\": \"application/json\" }, redirect: \"error\", signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS),\n body: JSON.stringify({ ...body, reason: body.reason ?? null, hook_version: options.hookVersion }),\n });\n return res.ok ? { revision: body.revision, result: body.result, at: now.toISOString() } : meta.last_ack;\n } catch { return meta.last_ack; }\n };\n const backToOwnRules = (): boolean => {\n if (!isManaged(dir)) return false;\n restoreLocal(dir, options.agentKid);\n options.afterPolicyWrite?.(dir);\n return true;\n };\n\n let res: Response;\n try {\n res = await fetchImpl(`${base}/v1/policy`, {\n // The hook's version tells the workspace which settings this computer understands (for example exact-target\n // exclusions), so it is never sent a document an older hook would refuse.\n headers: { ...auth, ...(options.hookVersion ? { \"x-scopebond-hook-version\": options.hookVersion } : {}), ...(isManaged(dir) && meta.etag ? { \"if-none-match\": meta.etag } : {}) },\n redirect: \"error\", signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS),\n });\n } catch (error) {\n save({ last_error: `could not reach the workspace (${(error as Error).name}); the rules in force stay` });\n return { state: \"unavailable\", message: \"could not reach the workspace\" };\n }\n\n if (res.status === 401) {\n backToOwnRules();\n save({ revision: null, rules_digest: null, export_id: null, etag: null, last_error: \"the workspace connection is no longer valid; this computer uses its own rules\" });\n return { state: \"disconnected\" };\n }\n if (res.status === 204) {\n const changed = backToOwnRules();\n const last = meta.last_ack;\n const confirmed = changed || !last || last.revision !== 0\n ? await ack({ export_id: `rev-0-${installationId}`, revision: 0, rules_digest: UNMANAGED_DIGEST, result: \"loaded\" }) : last;\n save({ revision: null, rules_digest: null, export_id: null, etag: null, last_ack: confirmed, last_error: null });\n return { state: \"own_rules\", changed };\n }\n if (res.status === 304 && isManaged(dir) && meta.revision !== null) {\n const confirmed = meta.last_ack?.revision === meta.revision && meta.last_ack.result === \"loaded\" ? meta.last_ack\n : await ack({ export_id: meta.export_id!, revision: meta.revision, rules_digest: meta.rules_digest!, result: \"loaded\" });\n save({ last_ack: confirmed, last_error: null });\n return { state: \"unchanged\", revision: meta.revision };\n }\n if (res.status !== 200) {\n save({ last_error: `the workspace answered ${res.status}; the rules in force stay` });\n return { state: \"unavailable\", message: `the workspace answered ${res.status}` };\n }\n\n let raw: unknown;\n try { raw = await res.json(); } catch { raw = null; }\n const r = (raw && typeof raw === \"object\" ? raw : {}) as Record<string, unknown>;\n const etag = res.headers.get(\"etag\");\n // The same version already in force: confirm it again if the last confirmation did not land.\n if (isManaged(dir) && r.revision === meta.revision && r.rules_digest === meta.rules_digest && meta.revision !== null) {\n const confirmed = meta.last_ack?.revision === meta.revision ? meta.last_ack\n : await ack({ export_id: meta.export_id!, revision: meta.revision, rules_digest: meta.rules_digest!, result: \"loaded\" });\n save({ etag, last_ack: confirmed, last_error: null });\n return { state: \"unchanged\", revision: meta.revision };\n }\n const inspected = inspectManaged(raw, { installationId, currentRevision: isManaged(dir) ? meta.revision : null, currentDigest: isManaged(dir) ? meta.rules_digest : null });\n const echo = typeof r.export_id === \"string\" && Number.isInteger(r.revision) && typeof r.rules_digest === \"string\" && /^[0-9a-f]{64}$/.test(r.rules_digest)\n ? { export_id: r.export_id, revision: r.revision as number, rules_digest: r.rules_digest } : null;\n if (!inspected.ok) {\n const confirmed = echo ? await ack({ ...echo, result: \"rejected\", reason: inspected.reason }) : meta.last_ack;\n save({ last_ack: confirmed, last_error: inspected.message });\n return { state: \"refused\", revision: echo?.revision ?? null, reason: inspected.reason, message: inspected.message };\n }\n const doc = inspected.doc;\n const installed = installManaged(dir, doc, options.agentKid, options.policyBuilds);\n if (!installed.ok) {\n const confirmed = await ack({ export_id: doc.export_id, revision: doc.revision, rules_digest: doc.rules_digest, result: \"rejected\", reason: installed.reason });\n save({ last_ack: confirmed, last_error: installed.message });\n return { state: \"refused\", revision: doc.revision, reason: installed.reason, message: installed.message };\n }\n options.afterPolicyWrite?.(dir);\n const confirmed = await ack({ export_id: doc.export_id, revision: doc.revision, rules_digest: doc.rules_digest, result: \"loaded\" });\n save({ revision: doc.revision, rules_digest: doc.rules_digest, export_id: doc.export_id, etag, last_ack: confirmed, last_error: null });\n return { state: \"applied\", revision: doc.revision };\n}\n\n/** Called on every tool call: when this computer is connected and has not checked in five minutes, run the check, capped at\n * `budgetMs`. Resolves `null` when no check was due, another one is running, or the cap was reached first (the check then\n * finishes if the process lives long enough, else runs again on a later call). Never throws. `makeOptions` is called only when a\n * check is due, so an ordinary call pays nothing but two file reads. */\nexport async function syncIfDue(dir: string, makeOptions: () => SyncOptions, budgetMs = Number(process.env.SCOPEBOND_POLICY_SYNC_MS ?? INLINE_BUDGET_MS), now = Date.now()): Promise<SyncOutcome | null> {\n try {\n if (process.env.SCOPEBOND_POLICY_SYNC === \"off\") return null;\n if (!existsSync(connectionPath(dir))) return null;\n const checked = readMeta(dir).checked_at;\n if (checked && now - Date.parse(checked) < SYNC_INTERVAL_MS) return null;\n const lock = join(dir, LOCK_FILE);\n if (existsSync(lock)) {\n if (now - statSync(lock).mtimeMs < LOCK_STALE_MS) return null;\n rmSync(lock, { force: true });\n }\n const fd = openSync(lock, \"wx\");\n writeSync(fd, String(process.pid));\n closeSync(fd);\n const budget = Number.isFinite(budgetMs) && budgetMs > 0 ? budgetMs : INLINE_BUDGET_MS;\n const work = Promise.resolve()\n .then(() => syncPolicy(dir, { ...makeOptions(), timeoutMs: budget }))\n .catch((): SyncOutcome => ({ state: \"unavailable\", message: \"the check failed\" }))\n .finally(() => releaseSyncLock(dir));\n let timer: ReturnType<typeof setTimeout> | undefined;\n const capped = new Promise<null>((resolve) => { timer = setTimeout(() => resolve(null), budget); });\n const outcome = await Promise.race([work, capped]);\n if (timer) clearTimeout(timer);\n return outcome;\n } catch { return null; }\n}\n\nexport function releaseSyncLock(dir: string): void {\n try { rmSync(join(dir, LOCK_FILE), { force: true }); } catch { /* expires on its own */ }\n}\n"]}
|
package/dist/proof.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CapabilityCell, type ProofRecord } from "./capabilities.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type HookConnection } from "./cloud.js";
|
|
3
3
|
export declare const PROOF_FILE = "capability-proof.json";
|
|
4
4
|
/** Options for a fixture run. */
|
|
5
5
|
export interface ProofRunOptions {
|
package/dist/proof.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"AA4BA,OAAO,EAA4E,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKpJ,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"AA4BA,OAAO,EAA4E,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKpJ,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5D,eAAO,MAAM,UAAU,0BAA0B,CAAC;AAKlD,iCAAiC;AACjC,MAAM,WAAW,eAAe;IAC9B;;;wFAGoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;CACrB;AAyED;;;0CAG0C;AAC1C,wBAAsB,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAkBlI;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAOzE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAIzF;AAED,iEAAiE;AACjE,eAAO,MAAM,WAAW,GAAI,OAAO,WAAW,EAAE,MAAM,cAAc,KAAG,OAC6E,CAAC;AAErJ;;2FAE2F;AAC3F,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAkBxK"}
|