@scopebond/hook 0.10.0 → 0.12.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 +27 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +116 -15
- 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 +77 -9
- package/dist/cloud.js.map +1 -1
- package/dist/delivery-report.d.ts +16 -0
- package/dist/delivery-report.d.ts.map +1 -0
- package/dist/delivery-report.js +51 -0
- package/dist/delivery-report.js.map +1 -0
- package/dist/delivery-state.d.ts +30 -0
- package/dist/delivery-state.d.ts.map +1 -0
- package/dist/delivery-state.js +86 -0
- package/dist/delivery-state.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +8 -2
- package/dist/install.js.map +1 -1
- package/dist/managed.d.ts +3 -1
- package/dist/managed.d.ts.map +1 -1
- package/dist/managed.js +50 -4
- package/dist/managed.js.map +1 -1
- 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.map +1 -1
- package/dist/policy-sync.js +6 -2
- package/dist/policy-sync.js.map +1 -1
- 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 +2 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +12 -2
- package/dist/runtime.js.map +1 -1
- package/dist/version.d.ts +4 -4
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +5 -5
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-sync.d.ts","sourceRoot":"","sources":["../src/policy-sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"policy-sync.d.ts","sourceRoot":"","sources":["../src/policy-sync.ts"],"names":[],"mappings":"AAoBA,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,CA+FxF;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"}
|
package/dist/policy-sync.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { closeSync, existsSync, openSync, rmSync, statSync, writeSync } from "node:fs";
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
import { connectionPath, loadConnection } from "./cloud.js";
|
|
19
|
+
import { recordRulesCredential } from "./delivery-state.js";
|
|
19
20
|
import { inspectManaged, installManaged, isManaged, readMeta, restoreLocal, writeMeta, } from "./managed.js";
|
|
20
21
|
export const SYNC_INTERVAL_MS = 5 * 60 * 1000;
|
|
21
22
|
const LOCK_FILE = "managed-sync.lock";
|
|
@@ -57,7 +58,9 @@ export async function syncPolicy(dir, options) {
|
|
|
57
58
|
let res;
|
|
58
59
|
try {
|
|
59
60
|
res = await fetchImpl(`${base}/v1/policy`, {
|
|
60
|
-
|
|
61
|
+
// The hook's version tells the workspace which settings this computer understands (for example exact-target
|
|
62
|
+
// exclusions), so it is never sent a document an older hook would refuse.
|
|
63
|
+
headers: { ...auth, ...(options.hookVersion ? { "x-scopebond-hook-version": options.hookVersion } : {}), ...(isManaged(dir) && meta.etag ? { "if-none-match": meta.etag } : {}) },
|
|
61
64
|
redirect: "error", signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS),
|
|
62
65
|
});
|
|
63
66
|
}
|
|
@@ -65,6 +68,7 @@ export async function syncPolicy(dir, options) {
|
|
|
65
68
|
save({ last_error: `could not reach the workspace (${error.name}); the rules in force stay` });
|
|
66
69
|
return { state: "unavailable", message: "could not reach the workspace" };
|
|
67
70
|
}
|
|
71
|
+
recordRulesCredential(dir, res.status !== 401, now.getTime());
|
|
68
72
|
if (res.status === 401) {
|
|
69
73
|
backToOwnRules();
|
|
70
74
|
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" });
|
|
@@ -104,7 +108,7 @@ export async function syncPolicy(dir, options) {
|
|
|
104
108
|
save({ etag, last_ack: confirmed, last_error: null });
|
|
105
109
|
return { state: "unchanged", revision: meta.revision };
|
|
106
110
|
}
|
|
107
|
-
const inspected = inspectManaged(raw, { installationId, currentRevision: isManaged(dir) ? meta.revision : null });
|
|
111
|
+
const inspected = inspectManaged(raw, { installationId, currentRevision: isManaged(dir) ? meta.revision : null, currentDigest: isManaged(dir) ? meta.rules_digest : null });
|
|
108
112
|
const echo = typeof r.export_id === "string" && Number.isInteger(r.revision) && typeof r.rules_digest === "string" && /^[0-9a-f]{64}$/.test(r.rules_digest)
|
|
109
113
|
? { export_id: r.export_id, revision: r.revision, rules_digest: r.rules_digest } : null;
|
|
110
114
|
if (!inspected.ok) {
|
package/dist/policy-sync.js.map
CHANGED
|
@@ -1 +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 headers: { ...auth, ...(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 });\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"]}
|
|
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 { recordRulesCredential } from \"./delivery-state.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 recordRulesCredential(dir, res.status !== 401, now.getTime());\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"}
|
package/dist/proof.js
CHANGED
|
@@ -30,6 +30,7 @@ import { mapVector } from "./vectors.js";
|
|
|
30
30
|
import { bindingKeyFromHex, operationsForCall, sourceReceiptHash } from "./observation.js";
|
|
31
31
|
import { callRequestOf, fixtureProbe } from "./typed-ops.js";
|
|
32
32
|
import { fixtureFiles } from "./typed-infra.js";
|
|
33
|
+
import { ingestUrl } from "./cloud.js";
|
|
33
34
|
export const PROOF_FILE = "capability-proof.json";
|
|
34
35
|
const receiptActionType = (receipt) => receipt?.payload?.intent?.action_type;
|
|
35
36
|
const receiptParams = (receipt) => {
|
|
@@ -162,7 +163,7 @@ export async function deliverProofReceipts(connection, receipts, options = {}) {
|
|
|
162
163
|
if (receipts.length === 0)
|
|
163
164
|
return true;
|
|
164
165
|
const exporter = createCloudExporter({
|
|
165
|
-
url: connection
|
|
166
|
+
url: ingestUrl(connection), credential: connection.credential, outbox: createMemoryCloudOutbox({ maxPending: Math.max(1000, receipts.length) }),
|
|
166
167
|
fetch: options.fetch, flushMs: 60_000,
|
|
167
168
|
});
|
|
168
169
|
try {
|
package/dist/proof.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"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","sourcesContent":["// Safe proof fixtures.\n//\n// For each supported capability cell this runs its tagged vectors end to end against a\n// throwaway hook home: a safe action must be allowed and countersigned; a violating one\n// must be denied at the hook boundary; every receipt must verify against the\n// countersigning key; and every receipt of one tool call must carry one action group.\n//\n// Nothing here touches a developer's real settings, policy, keys or receipts. Every run\n// creates its own directory under the OS temp dir, scaffolds a fresh machine key, policy\n// and store there, and deletes it afterwards. No network is used, so a fixture can never\n// produce a Cloud acknowledgement: `cloud_ack` is always `not_checked`, and `origin` is\n// always `fixture`. A cell therefore cannot reach `verified_reporting` from this runner\n// (see capabilities.ts); it can only show `configured_unverified` with a passing\n// fixture, or `degraded` with a failing one.\n//\n// Observation-only cells (Cursor's after-edit event, and monitor-mode fetch and MCP\n// calls) are proven with a known successful after-action fixture, labelled\n// observation-only. No deny fixture is run for them and none is claimed.\n\nimport { copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createCloudExporter, createMemoryCloudOutbox, verifyReceipt, type SignedReceipt } from \"@scopebond/gateway\";\nimport { loadOrCreateAttester } from \"@scopebond/gateway/node\";\nimport { fillPushBranch } from \"./map.js\";\nimport { scaffold } from \"./init.js\";\nimport { createHookRuntime } from \"./runtime.js\";\nimport { ACTION_GROUP_PARAM } from \"./group.js\";\nimport { computeManifest, specForCell, vectorDigest, vectorsForCell, type Adapter, type CapabilityCell, type ProofRecord } from \"./capabilities.js\";\nimport { mapVector, type Vector } from \"./vectors.js\";\nimport { bindingKeyFromHex, operationsForCall, sourceReceiptHash } from \"./observation.js\";\nimport { callRequestOf, fixtureProbe } from \"./typed-ops.js\";\nimport { fixtureFiles } from \"./typed-infra.js\";\nimport type { HookConnection } from \"./cloud.js\";\n\nexport const PROOF_FILE = \"capability-proof.json\";\n\nconst receiptActionType = (receipt: unknown): string | undefined =>\n (receipt as { payload?: { intent?: { action_type?: unknown } } } | undefined)?.payload?.intent?.action_type as string | undefined;\n\n/** Options for a fixture run. */\nexport interface ProofRunOptions {\n /** A real hook config dir whose machine and countersigning keys the fixtures should be\n * signed with (copied into the temp home, never modified), so the workspace can accept\n * the fixture receipts and resolve a proof's digests against them. Without it the\n * fixtures use throwaway keys and produce no digests a workspace could resolve. */\n identityDir?: string;\n /** Receives every fixture receipt produced, once each, for delivery. */\n collect?: unknown[];\n}\n\nconst receiptParams = (receipt: unknown): Record<string, unknown> => {\n const payload = (receipt as { payload?: { intent?: { params?: Record<string, unknown> } } } | undefined)?.payload;\n return payload?.intent?.params ?? {};\n};\n\ninterface Outcome { decision: string; receipts: unknown[]; valid: boolean; grouped: boolean; typed?: boolean }\n\nconst FIXTURE_KEY = bindingKeyFromHex(\"00\".repeat(32));\n\n/** Whether the adapter derives the typed operation a vector expects, from the same dispatched\n * actions and raw request the hook uses, against a deterministic fixture repository. */\nfunction typedDerived(vector: Vector, dispatched: Array<{ action: { action_type: string; params: Record<string, unknown> } }> | undefined): boolean {\n if (!vector.typed) return true;\n const request = callRequestOf(vector.input);\n const operations = operationsForCall({ dispatched: dispatched ?? [], request }, { key: FIXTURE_KEY, cwd: \"/fixture\", repositoryId: \"sbr_fixture\", probe: fixtureProbe, packageManagerVersion: () => undefined, files: fixtureFiles, env: () => undefined });\n return operations.some((op) => op?.type === vector.typed!.type && (vector.typed!.verb === undefined || op.verb === vector.typed!.verb));\n}\n\nasync function run(vector: Vector, runtime: ReturnType<typeof createHookRuntime>, attesterPem: string): Promise<Outcome> {\n const decision = await runtime.evaluate(fillPushBranch(mapVector(vector), \"feature/work\"), { groupKey: `proof:${vector.id}` });\n const receipts = decision.receipts ?? (decision.receipt ? [decision.receipt] : []);\n const valid = receipts.length > 0 && receipts.every((r) => (verifyReceipt(r as Parameters<typeof verifyReceipt>[0], attesterPem) as unknown as { valid: boolean }).valid);\n const groups = new Set(receipts.map((r) => receiptParams(r)[ACTION_GROUP_PARAM]));\n const grouped = receipts.length > 0 && groups.size === 1 && typeof [...groups][0] === \"string\" && String([...groups][0]) !== \"\";\n return { decision: decision.decision, receipts, valid, grouped, typed: typedDerived(vector, decision.dispatched) };\n}\n\n/** Run one cell's fixtures in a temp hook home. Returns null when the cell has no vectors. */\nasync function proveCell(adapter: Adapter, actionType: string, phase: \"pre_action\" | \"after_action\", observationOnly: boolean, adapterVersion: string, options: ProofRunOptions): Promise<{ proof: Omit<ProofRecord, \"cell\">; receipts: unknown[] } | null> {\n const vectors = vectorsForCell(adapter, actionType, phase);\n const digest = vectorDigest(vectors);\n if (digest === null) return null;\n const dir = mkdtempSync(join(tmpdir(), \"scopebond-proof-\"));\n let runtime: ReturnType<typeof createHookRuntime> | undefined;\n try {\n if (options.identityDir) {\n for (const name of [\"agent.key\", \"attester.key\"]) {\n const source = join(options.identityDir, name);\n if (existsSync(source)) copyFileSync(source, join(dir, name));\n }\n }\n scaffold(dir);\n const attesterPath = join(dir, \"attester.key\");\n const { attester } = loadOrCreateAttester({ file: attesterPath });\n runtime = createHookRuntime({ policyPath: join(dir, \"policy.json\"), keyPath: join(dir, \"agent.key\"), attesterPath, dbPath: join(dir, \"receipts.db\") });\n const allow = vectors.filter((v) => v.cell?.role === \"allow\" || v.cell?.role === \"after_action\");\n const deny = vectors.filter((v) => v.cell?.role === \"deny\");\n const allowed: Outcome[] = [];\n for (const v of allow) allowed.push(await run(v, runtime, attester.publicKeyPem));\n const denied: Outcome[] = [];\n if (!observationOnly) for (const v of deny) denied.push(await run(v, runtime, attester.publicKeyPem));\n const all = [...allowed, ...denied];\n const receipts = all.flatMap((o) => o.receipts);\n // Only receipts of the cell's own action type stand as its proof (a compound command can\n // also produce receipts of other types), and each is named by the workspace's projection.\n const proofDigests = [...new Set(receipts.filter((r) => receiptActionType(r) === actionType).map((r) => sourceReceiptHash(r)))];\n return { receipts, proof: {\n ran_at: new Date().toISOString(), adapter_version: adapterVersion, test_vector_digest: digest, origin: \"fixture\",\n safe_allow: allowed.length > 0 && allowed.every((o) => o.decision !== \"deny\" && o.receipts.length > 0),\n safe_deny: observationOnly ? \"not_applicable\" : denied.length > 0 && denied.every((o) => o.decision === \"deny\"),\n signature: all.length > 0 && all.every((o) => o.valid),\n grouping: all.length > 0 && all.every((o) => o.grouped),\n cloud_ack: \"not_checked\", observation_only: observationOnly, proof_digests: proofDigests,\n ...(vectors.some((v) => v.typed) ? { typed_operation: all.length > 0 && all.every((o) => o.typed !== false) } : {}),\n } };\n } finally {\n try { runtime?.close(); } catch { /* the temp home is removed next */ }\n try { rmSync(dir, { recursive: true, force: true, maxRetries: 5 }); } catch { /* a handle still open on Windows; the OS temp dir is cleaned later */ }\n }\n}\n\n/** Run every supported cell's fixtures once per adapter/action/phase, and record the\n * result under each host variant's key. A fixture exercises the adapter, so it cannot\n * tell Codex desktop from Codex CLI: both keys get the same fixture proof, and neither\n * becomes verified (see `cellState`). */\nexport async function runProofFixtures(adapterVersion: string, options: ProofRunOptions = {}): Promise<Record<string, ProofRecord>> {\n const cells = computeManifest({ adapterVersion, configured: { claude: true, codex: true, cursor: true } }).cells;\n const records: Record<string, ProofRecord> = {};\n const cache = new Map<string, Omit<ProofRecord, \"cell\"> | null>();\n for (const cell of cells) {\n if (cell.state === \"unsupported\") continue;\n const spec = specForCell(cell);\n if (!spec) continue;\n const id = `${spec.adapter}/${spec.action_type}/${spec.phase}`;\n if (!cache.has(id)) {\n const run = await proveCell(spec.adapter, spec.action_type, spec.phase, cell.observation_only, adapterVersion, options);\n if (run) options.collect?.push(...run.receipts);\n cache.set(id, run?.proof ?? null);\n }\n const proof = cache.get(id);\n if (proof) records[cell.key] = { cell: cell.key, ...proof };\n }\n return records;\n}\n\nexport function loadProofs(configDir: string): Record<string, ProofRecord> {\n const file = join(configDir, PROOF_FILE);\n if (!existsSync(file)) return {};\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\")) as { proofs?: Record<string, ProofRecord> };\n return parsed.proofs && typeof parsed.proofs === \"object\" ? parsed.proofs : {};\n } catch { return {}; }\n}\n\nexport function saveProofs(configDir: string, proofs: Record<string, ProofRecord>): string {\n const file = join(configDir, PROOF_FILE);\n writeFileSync(file, `${JSON.stringify({ version: 1, proofs }, null, 2)}\\n`);\n return file;\n}\n\n/** Whether a cell's fixture proof passed, for a summary line. */\nexport const proofPassed = (proof: ProofRecord, cell: CapabilityCell): boolean =>\n proof.safe_allow && (cell.observation_only ? proof.safe_deny === \"not_applicable\" : proof.safe_deny === true) && proof.signature && proof.grouping;\n\n/** Deliver fixture receipts to the workspace's receipt route and report whether every one was\n * accepted. A proof names these receipts by digest, so it is only worth sending after they\n * landed; anything short of full delivery leaves the proof unsent, never half-claimed. */\nexport async function deliverProofReceipts(connection: HookConnection, receipts: unknown[], options: { fetch?: typeof fetch; timeoutMs?: number } = {}): Promise<boolean> {\n if (receipts.length === 0) return true;\n const exporter = createCloudExporter({\n url: connection.url, credential: connection.credential, outbox: createMemoryCloudOutbox({ maxPending: Math.max(1000, receipts.length) }),\n fetch: options.fetch, flushMs: 60_000,\n });\n try {\n for (const receipt of receipts) exporter.enqueue(receipt as SignedReceipt);\n const deadline = Date.now() + (options.timeoutMs ?? 15_000);\n while (exporter.pending() > 0 && Date.now() < deadline) {\n await exporter.flush();\n if (exporter.pending() > 0) {\n if (exporter.status().consecutiveFailures > 0) return false;\n await new Promise((resolve) => setTimeout(resolve, 25));\n }\n }\n return exporter.pending() === 0;\n } finally { exporter.stop(); }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"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","sourcesContent":["// Safe proof fixtures.\n//\n// For each supported capability cell this runs its tagged vectors end to end against a\n// throwaway hook home: a safe action must be allowed and countersigned; a violating one\n// must be denied at the hook boundary; every receipt must verify against the\n// countersigning key; and every receipt of one tool call must carry one action group.\n//\n// Nothing here touches a developer's real settings, policy, keys or receipts. Every run\n// creates its own directory under the OS temp dir, scaffolds a fresh machine key, policy\n// and store there, and deletes it afterwards. No network is used, so a fixture can never\n// produce a Cloud acknowledgement: `cloud_ack` is always `not_checked`, and `origin` is\n// always `fixture`. A cell therefore cannot reach `verified_reporting` from this runner\n// (see capabilities.ts); it can only show `configured_unverified` with a passing\n// fixture, or `degraded` with a failing one.\n//\n// Observation-only cells (Cursor's after-edit event, and monitor-mode fetch and MCP\n// calls) are proven with a known successful after-action fixture, labelled\n// observation-only. No deny fixture is run for them and none is claimed.\n\nimport { copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createCloudExporter, createMemoryCloudOutbox, verifyReceipt, type SignedReceipt } from \"@scopebond/gateway\";\nimport { loadOrCreateAttester } from \"@scopebond/gateway/node\";\nimport { fillPushBranch } from \"./map.js\";\nimport { scaffold } from \"./init.js\";\nimport { createHookRuntime } from \"./runtime.js\";\nimport { ACTION_GROUP_PARAM } from \"./group.js\";\nimport { computeManifest, specForCell, vectorDigest, vectorsForCell, type Adapter, type CapabilityCell, type ProofRecord } from \"./capabilities.js\";\nimport { mapVector, type Vector } from \"./vectors.js\";\nimport { bindingKeyFromHex, operationsForCall, sourceReceiptHash } from \"./observation.js\";\nimport { callRequestOf, fixtureProbe } from \"./typed-ops.js\";\nimport { fixtureFiles } from \"./typed-infra.js\";\nimport { ingestUrl, type HookConnection } from \"./cloud.js\";\n\nexport const PROOF_FILE = \"capability-proof.json\";\n\nconst receiptActionType = (receipt: unknown): string | undefined =>\n (receipt as { payload?: { intent?: { action_type?: unknown } } } | undefined)?.payload?.intent?.action_type as string | undefined;\n\n/** Options for a fixture run. */\nexport interface ProofRunOptions {\n /** A real hook config dir whose machine and countersigning keys the fixtures should be\n * signed with (copied into the temp home, never modified), so the workspace can accept\n * the fixture receipts and resolve a proof's digests against them. Without it the\n * fixtures use throwaway keys and produce no digests a workspace could resolve. */\n identityDir?: string;\n /** Receives every fixture receipt produced, once each, for delivery. */\n collect?: unknown[];\n}\n\nconst receiptParams = (receipt: unknown): Record<string, unknown> => {\n const payload = (receipt as { payload?: { intent?: { params?: Record<string, unknown> } } } | undefined)?.payload;\n return payload?.intent?.params ?? {};\n};\n\ninterface Outcome { decision: string; receipts: unknown[]; valid: boolean; grouped: boolean; typed?: boolean }\n\nconst FIXTURE_KEY = bindingKeyFromHex(\"00\".repeat(32));\n\n/** Whether the adapter derives the typed operation a vector expects, from the same dispatched\n * actions and raw request the hook uses, against a deterministic fixture repository. */\nfunction typedDerived(vector: Vector, dispatched: Array<{ action: { action_type: string; params: Record<string, unknown> } }> | undefined): boolean {\n if (!vector.typed) return true;\n const request = callRequestOf(vector.input);\n const operations = operationsForCall({ dispatched: dispatched ?? [], request }, { key: FIXTURE_KEY, cwd: \"/fixture\", repositoryId: \"sbr_fixture\", probe: fixtureProbe, packageManagerVersion: () => undefined, files: fixtureFiles, env: () => undefined });\n return operations.some((op) => op?.type === vector.typed!.type && (vector.typed!.verb === undefined || op.verb === vector.typed!.verb));\n}\n\nasync function run(vector: Vector, runtime: ReturnType<typeof createHookRuntime>, attesterPem: string): Promise<Outcome> {\n const decision = await runtime.evaluate(fillPushBranch(mapVector(vector), \"feature/work\"), { groupKey: `proof:${vector.id}` });\n const receipts = decision.receipts ?? (decision.receipt ? [decision.receipt] : []);\n const valid = receipts.length > 0 && receipts.every((r) => (verifyReceipt(r as Parameters<typeof verifyReceipt>[0], attesterPem) as unknown as { valid: boolean }).valid);\n const groups = new Set(receipts.map((r) => receiptParams(r)[ACTION_GROUP_PARAM]));\n const grouped = receipts.length > 0 && groups.size === 1 && typeof [...groups][0] === \"string\" && String([...groups][0]) !== \"\";\n return { decision: decision.decision, receipts, valid, grouped, typed: typedDerived(vector, decision.dispatched) };\n}\n\n/** Run one cell's fixtures in a temp hook home. Returns null when the cell has no vectors. */\nasync function proveCell(adapter: Adapter, actionType: string, phase: \"pre_action\" | \"after_action\", observationOnly: boolean, adapterVersion: string, options: ProofRunOptions): Promise<{ proof: Omit<ProofRecord, \"cell\">; receipts: unknown[] } | null> {\n const vectors = vectorsForCell(adapter, actionType, phase);\n const digest = vectorDigest(vectors);\n if (digest === null) return null;\n const dir = mkdtempSync(join(tmpdir(), \"scopebond-proof-\"));\n let runtime: ReturnType<typeof createHookRuntime> | undefined;\n try {\n if (options.identityDir) {\n for (const name of [\"agent.key\", \"attester.key\"]) {\n const source = join(options.identityDir, name);\n if (existsSync(source)) copyFileSync(source, join(dir, name));\n }\n }\n scaffold(dir);\n const attesterPath = join(dir, \"attester.key\");\n const { attester } = loadOrCreateAttester({ file: attesterPath });\n runtime = createHookRuntime({ policyPath: join(dir, \"policy.json\"), keyPath: join(dir, \"agent.key\"), attesterPath, dbPath: join(dir, \"receipts.db\") });\n const allow = vectors.filter((v) => v.cell?.role === \"allow\" || v.cell?.role === \"after_action\");\n const deny = vectors.filter((v) => v.cell?.role === \"deny\");\n const allowed: Outcome[] = [];\n for (const v of allow) allowed.push(await run(v, runtime, attester.publicKeyPem));\n const denied: Outcome[] = [];\n if (!observationOnly) for (const v of deny) denied.push(await run(v, runtime, attester.publicKeyPem));\n const all = [...allowed, ...denied];\n const receipts = all.flatMap((o) => o.receipts);\n // Only receipts of the cell's own action type stand as its proof (a compound command can\n // also produce receipts of other types), and each is named by the workspace's projection.\n const proofDigests = [...new Set(receipts.filter((r) => receiptActionType(r) === actionType).map((r) => sourceReceiptHash(r)))];\n return { receipts, proof: {\n ran_at: new Date().toISOString(), adapter_version: adapterVersion, test_vector_digest: digest, origin: \"fixture\",\n safe_allow: allowed.length > 0 && allowed.every((o) => o.decision !== \"deny\" && o.receipts.length > 0),\n safe_deny: observationOnly ? \"not_applicable\" : denied.length > 0 && denied.every((o) => o.decision === \"deny\"),\n signature: all.length > 0 && all.every((o) => o.valid),\n grouping: all.length > 0 && all.every((o) => o.grouped),\n cloud_ack: \"not_checked\", observation_only: observationOnly, proof_digests: proofDigests,\n ...(vectors.some((v) => v.typed) ? { typed_operation: all.length > 0 && all.every((o) => o.typed !== false) } : {}),\n } };\n } finally {\n try { runtime?.close(); } catch { /* the temp home is removed next */ }\n try { rmSync(dir, { recursive: true, force: true, maxRetries: 5 }); } catch { /* a handle still open on Windows; the OS temp dir is cleaned later */ }\n }\n}\n\n/** Run every supported cell's fixtures once per adapter/action/phase, and record the\n * result under each host variant's key. A fixture exercises the adapter, so it cannot\n * tell Codex desktop from Codex CLI: both keys get the same fixture proof, and neither\n * becomes verified (see `cellState`). */\nexport async function runProofFixtures(adapterVersion: string, options: ProofRunOptions = {}): Promise<Record<string, ProofRecord>> {\n const cells = computeManifest({ adapterVersion, configured: { claude: true, codex: true, cursor: true } }).cells;\n const records: Record<string, ProofRecord> = {};\n const cache = new Map<string, Omit<ProofRecord, \"cell\"> | null>();\n for (const cell of cells) {\n if (cell.state === \"unsupported\") continue;\n const spec = specForCell(cell);\n if (!spec) continue;\n const id = `${spec.adapter}/${spec.action_type}/${spec.phase}`;\n if (!cache.has(id)) {\n const run = await proveCell(spec.adapter, spec.action_type, spec.phase, cell.observation_only, adapterVersion, options);\n if (run) options.collect?.push(...run.receipts);\n cache.set(id, run?.proof ?? null);\n }\n const proof = cache.get(id);\n if (proof) records[cell.key] = { cell: cell.key, ...proof };\n }\n return records;\n}\n\nexport function loadProofs(configDir: string): Record<string, ProofRecord> {\n const file = join(configDir, PROOF_FILE);\n if (!existsSync(file)) return {};\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\")) as { proofs?: Record<string, ProofRecord> };\n return parsed.proofs && typeof parsed.proofs === \"object\" ? parsed.proofs : {};\n } catch { return {}; }\n}\n\nexport function saveProofs(configDir: string, proofs: Record<string, ProofRecord>): string {\n const file = join(configDir, PROOF_FILE);\n writeFileSync(file, `${JSON.stringify({ version: 1, proofs }, null, 2)}\\n`);\n return file;\n}\n\n/** Whether a cell's fixture proof passed, for a summary line. */\nexport const proofPassed = (proof: ProofRecord, cell: CapabilityCell): boolean =>\n proof.safe_allow && (cell.observation_only ? proof.safe_deny === \"not_applicable\" : proof.safe_deny === true) && proof.signature && proof.grouping;\n\n/** Deliver fixture receipts to the workspace's receipt route and report whether every one was\n * accepted. A proof names these receipts by digest, so it is only worth sending after they\n * landed; anything short of full delivery leaves the proof unsent, never half-claimed. */\nexport async function deliverProofReceipts(connection: HookConnection, receipts: unknown[], options: { fetch?: typeof fetch; timeoutMs?: number } = {}): Promise<boolean> {\n if (receipts.length === 0) return true;\n const exporter = createCloudExporter({\n url: ingestUrl(connection), credential: connection.credential, outbox: createMemoryCloudOutbox({ maxPending: Math.max(1000, receipts.length) }),\n fetch: options.fetch, flushMs: 60_000,\n });\n try {\n for (const receipt of receipts) exporter.enqueue(receipt as SignedReceipt);\n const deadline = Date.now() + (options.timeoutMs ?? 15_000);\n while (exporter.pending() > 0 && Date.now() < deadline) {\n await exporter.flush();\n if (exporter.pending() > 0) {\n if (exporter.status().consecutiveFailures > 0) return false;\n await new Promise((resolve) => setTimeout(resolve, 25));\n }\n }\n return exporter.pending() === 0;\n } finally { exporter.stop(); }\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SqliteReceiptStore } from "@scopebond/gateway/node";
|
|
2
|
+
import { type HookConnection } from "./cloud.js";
|
|
3
|
+
export interface KeyGroup {
|
|
4
|
+
kid: string;
|
|
5
|
+
count: number;
|
|
6
|
+
first: string | null;
|
|
7
|
+
last: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface RecoverIo {
|
|
10
|
+
log(line: string): void;
|
|
11
|
+
fetch: typeof fetch;
|
|
12
|
+
sleep(ms: number): Promise<void>;
|
|
13
|
+
now(): number;
|
|
14
|
+
}
|
|
15
|
+
/** Walk the whole local log once and count receipts per signing key other than `currentKid`. */
|
|
16
|
+
export declare function groupByEarlierKey(store: SqliteReceiptStore, currentKid: string): KeyGroup[];
|
|
17
|
+
export interface RecoverOptions {
|
|
18
|
+
wait: boolean;
|
|
19
|
+
waitMs: number;
|
|
20
|
+
pollMs: number;
|
|
21
|
+
}
|
|
22
|
+
export interface RecoverResult {
|
|
23
|
+
groups: number;
|
|
24
|
+
accepted: number;
|
|
25
|
+
duplicates: number;
|
|
26
|
+
rejected: number;
|
|
27
|
+
pending: number;
|
|
28
|
+
skipped: number;
|
|
29
|
+
failed: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** Recover every earlier key's receipts, one approval per key. */
|
|
32
|
+
export declare function recoverEarlierReceipts(dir: string, connection: HookConnection, io: RecoverIo, options: RecoverOptions): Promise<RecoverResult>;
|
|
33
|
+
//# sourceMappingURL=recover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recover.d.ts","sourceRoot":"","sources":["../src/recover.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,kBAAkB,EAAwB,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAU5D,MAAM,WAAW,QAAQ;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAEnG,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,GAAG,IAAI,MAAM,CAAC;CACf;AAOD,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,EAAE,CAkB3F;AAeD,MAAM,WAAW,cAAc;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAEjF,MAAM,WAAW,aAAa;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE;AAE5J,kEAAkE;AAClE,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,GAC9E,OAAO,CAAC,aAAa,CAAC,CA6CxB"}
|