@wrongstack/runtime 0.297.0 → 0.298.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/clipboard.js CHANGED
@@ -6,6 +6,7 @@ import * as os from "node:os";
6
6
  import * as path from "node:path";
7
7
  import { buildChildEnv } from "@wrongstack/core/utils";
8
8
  var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
9
+ var MAX_TEXT_BYTES = 4 * 1024 * 1024;
9
10
  async function readClipboardImage() {
10
11
  const platform = process.platform;
11
12
  if (platform === "win32") return readWindows();
@@ -70,10 +71,14 @@ async function readWindows() {
70
71
  `$img.Save('${tmp.replace(/\\/g, "\\\\")}', [System.Drawing.Imaging.ImageFormat]::Png)`,
71
72
  'Write-Output "OK"'
72
73
  ].join("; ");
73
- const out = await runCmd("powershell", ["-NoProfile", "-Command", ps]);
74
- if (!out || out.trim() === "NO_IMAGE") return null;
75
- if (!out.includes("OK")) return null;
76
- return readPngFile(tmp);
74
+ try {
75
+ const out = await runCmd("powershell", ["-NoProfile", "-Command", ps]);
76
+ if (!out || out.trim() === "NO_IMAGE") return null;
77
+ if (!out.includes("OK")) return null;
78
+ return await readPngFile(tmp);
79
+ } finally {
80
+ await fs.unlink(tmp).catch(() => void 0);
81
+ }
77
82
  }
78
83
  async function readDarwin() {
79
84
  const tmp = path.join(os.tmpdir(), `wstack-clip-${randomUUID()}.png`);
@@ -90,9 +95,13 @@ async function readDarwin() {
90
95
  "end try",
91
96
  'return "OK"'
92
97
  ].join("\n");
93
- const out = await runCmd("osascript", ["-e", script]);
94
- if (out?.trim() !== "OK") return null;
95
- return readPngFile(tmp);
98
+ try {
99
+ const out = await runCmd("osascript", ["-e", script]);
100
+ if (out?.trim() !== "OK") return null;
101
+ return await readPngFile(tmp);
102
+ } finally {
103
+ await fs.unlink(tmp).catch(() => void 0);
104
+ }
96
105
  }
97
106
  async function readLinux() {
98
107
  const tmp = path.join(os.tmpdir(), `wstack-clip-${randomUUID()}.png`);
@@ -137,6 +146,7 @@ function runCmd(cmd, args) {
137
146
  windowsHide: true
138
147
  });
139
148
  let out = "";
149
+ let outBytes = 0;
140
150
  let settled = false;
141
151
  let killedByTimeout = false;
142
152
  const finish = (value) => {
@@ -144,6 +154,7 @@ function runCmd(cmd, args) {
144
154
  settled = true;
145
155
  clearTimeout(timer);
146
156
  clearTimeout(killCap);
157
+ child.stdout.off("data", onStdoutData);
147
158
  resolve(value);
148
159
  };
149
160
  const timer = setTimeout(() => {
@@ -151,9 +162,18 @@ function runCmd(cmd, args) {
151
162
  child.kill("SIGTERM");
152
163
  }, CLIPBOARD_CMD_TIMEOUT_MS);
153
164
  const killCap = setTimeout(() => finish(null), CLIPBOARD_CMD_TIMEOUT_MS + 2e3);
154
- child.stdout.on("data", (c) => {
155
- out += String(c);
156
- });
165
+ const onStdoutData = (c) => {
166
+ const chunk = Buffer.isBuffer(c) ? c : Buffer.from(c);
167
+ outBytes += chunk.byteLength;
168
+ if (outBytes > MAX_TEXT_BYTES) {
169
+ out = "";
170
+ child.kill("SIGTERM");
171
+ finish(null);
172
+ return;
173
+ }
174
+ out += chunk.toString("utf8");
175
+ };
176
+ child.stdout.on("data", onStdoutData);
157
177
  child.on("error", () => finish(null));
158
178
  child.on("exit", (code) => {
159
179
  if (killedByTimeout) return finish(null);
@@ -200,6 +220,7 @@ function runCmdToFile(cmd, args, outPath) {
200
220
  windowsHide: true
201
221
  });
202
222
  const chunks = [];
223
+ let outputBytes = 0;
203
224
  let settled = false;
204
225
  let killedByTimeout = false;
205
226
  const finish = (value) => {
@@ -207,6 +228,7 @@ function runCmdToFile(cmd, args, outPath) {
207
228
  settled = true;
208
229
  clearTimeout(timer);
209
230
  clearTimeout(killCap);
231
+ child.stdout.off("data", onStdoutData);
210
232
  resolve(value);
211
233
  };
212
234
  const timer = setTimeout(() => {
@@ -214,7 +236,17 @@ function runCmdToFile(cmd, args, outPath) {
214
236
  child.kill("SIGTERM");
215
237
  }, CLIPBOARD_CMD_TIMEOUT_MS);
216
238
  const killCap = setTimeout(() => finish(false), CLIPBOARD_CMD_TIMEOUT_MS + 2e3);
217
- child.stdout.on("data", (c) => chunks.push(c));
239
+ const onStdoutData = (c) => {
240
+ outputBytes += c.byteLength;
241
+ if (outputBytes > MAX_IMAGE_BYTES) {
242
+ chunks.length = 0;
243
+ child.kill("SIGTERM");
244
+ finish(false);
245
+ return;
246
+ }
247
+ chunks.push(c);
248
+ };
249
+ child.stdout.on("data", onStdoutData);
218
250
  child.on("error", () => finish(false));
219
251
  child.on("exit", async (code) => {
220
252
  if (killedByTimeout) return finish(false);
@@ -34,6 +34,11 @@ export interface LightSubagentFactoryDeps {
34
34
  * `host.opts.statusTracker` thread in the CLI factory.
35
35
  */
36
36
  statusTracker?: ProviderModelStatusTracker | undefined;
37
+ /**
38
+ * Optional trusted control-plane hook for the isolated tool pipeline.
39
+ * The host owns this hook; it is not exposed through the agent's tools.
40
+ */
41
+ installToolBoundary?: ((pipelines: import('@wrongstack/core/agent').AgentPipelines) => void) | undefined;
37
42
  }
38
43
  /**
39
44
  * Retrieve and abort a light subagent's AbortController (stored in ctx.meta
@@ -0,0 +1,94 @@
1
+ import type { ExistingGovernanceAdminCredential, GovernanceCompatibilityCleanup, GovernanceCompatibilityFallback, GovernanceCompatibilityFallbackCode, GovernanceCompatibilityRuntime, GovernanceCompatibilityRuntimeSnapshot, GovernanceDaemonOperatorStatus, GovernanceModelCapability, GovernanceModelSession, GovernanceObservationCategory, PrepareGovernanceCompatibilityOptions, PrepareGovernanceCompatibilityResult } from '@wrongstack/governance';
2
+ import { connectGovernanceProjectClient, readGovernanceDaemonAttachmentBroker } from '@wrongstack/governance';
3
+ export type { GovernanceEvidenceCandidate, GovernanceEvidenceCandidateMissingBinding, GovernanceEvidenceTraceSnapshot, GovernanceToolOutcomeMetadata, } from '@wrongstack/governance';
4
+ export { createGovernanceEvidenceCandidate, GOVERNANCE_EVIDENCE_CANDIDATE_SCHEMA_VERSION, } from '@wrongstack/governance';
5
+ export interface BootstrapGovernanceRuntimeOptions {
6
+ readonly projectRoot: string;
7
+ readonly projectId: string;
8
+ readonly adminClientId: string;
9
+ readonly modelClientId: string;
10
+ readonly modelCapabilities: readonly GovernanceModelCapability[];
11
+ readonly adminTtlMs?: number | undefined;
12
+ readonly modelTtlMs?: number | undefined;
13
+ readonly timeoutMs?: number | undefined;
14
+ readonly existingAdmin?: ExistingGovernanceAdminCredential | undefined;
15
+ }
16
+ export interface GovernanceRuntimeBootstrapSnapshot {
17
+ readonly mode: 'governed';
18
+ readonly source: 'attached' | 'launched';
19
+ readonly daemon: GovernanceCompatibilityRuntimeSnapshot['daemon'];
20
+ readonly model: GovernanceCompatibilityRuntimeSnapshot['model'];
21
+ }
22
+ export interface GovernanceRuntimeBootstrapCloseResult {
23
+ readonly ok: boolean;
24
+ readonly action: 'detach' | 'shutdown';
25
+ readonly message: string;
26
+ }
27
+ export type GovernanceDaemonOperatorStatusReadResult = {
28
+ readonly available: true;
29
+ readonly status: GovernanceDaemonOperatorStatus;
30
+ } | {
31
+ readonly available: false;
32
+ readonly code: 'broker_missing' | 'broker_invalid' | 'connection_failed' | 'request_rejected' | 'unexpected_response';
33
+ readonly message: string;
34
+ };
35
+ export interface GovernanceDaemonOperatorStatusReaderDependencies {
36
+ readonly readBroker: typeof readGovernanceDaemonAttachmentBroker;
37
+ readonly connectClient: typeof connectGovernanceProjectClient;
38
+ }
39
+ export declare function readGovernanceDaemonOperatorStatus(projectRoot: string): Promise<GovernanceDaemonOperatorStatusReadResult>;
40
+ /** Internal source-test seam. Package consumers use readGovernanceDaemonOperatorStatus. */
41
+ export declare function readGovernanceDaemonOperatorStatusWithDependencies(projectRoot: string, dependencies: GovernanceDaemonOperatorStatusReaderDependencies): Promise<GovernanceDaemonOperatorStatusReadResult>;
42
+ export interface GovernanceRuntimeObservationInput {
43
+ readonly taskId: string | null;
44
+ readonly category: GovernanceObservationCategory;
45
+ readonly observedAt: string;
46
+ readonly payload: Readonly<Record<string, unknown>>;
47
+ }
48
+ export type GovernanceRuntimeWorkspaceSnapshotResult = {
49
+ readonly recorded: true;
50
+ readonly snapshot: import('@wrongstack/governance').WorkspaceSnapshotFenceDescriptor;
51
+ } | {
52
+ readonly recorded: false;
53
+ readonly code: 'closed' | 'request_failed' | 'request_rejected' | 'unexpected_response' | 'workspace_snapshot_invalid';
54
+ readonly message: string;
55
+ };
56
+ export type GovernanceRuntimeObservationResult = {
57
+ readonly recorded: true;
58
+ readonly observationId: string;
59
+ readonly idempotentReplay: boolean;
60
+ readonly sequence: number;
61
+ } | {
62
+ readonly recorded: false;
63
+ readonly observationId?: string | undefined;
64
+ readonly code: 'backpressure' | 'closed' | 'request_failed' | 'request_rejected' | 'unexpected_response';
65
+ readonly message: string;
66
+ };
67
+ export declare const MAX_PENDING_GOVERNANCE_OBSERVATIONS = 256;
68
+ declare const GOVERNANCE_RUNTIME_BOOTSTRAP_HANDLE_CONSTRUCTION: unique symbol;
69
+ export declare class GovernanceRuntimeBootstrapHandle {
70
+ #private;
71
+ readonly model: GovernanceModelSession;
72
+ constructor(construction: typeof GOVERNANCE_RUNTIME_BOOTSTRAP_HANDLE_CONSTRUCTION, runtime: GovernanceCompatibilityRuntime);
73
+ snapshot(): GovernanceRuntimeBootstrapSnapshot;
74
+ observe(input: GovernanceRuntimeObservationInput): Promise<GovernanceRuntimeObservationResult>;
75
+ recordWorkspaceSnapshot(manifestHash: string): Promise<GovernanceRuntimeWorkspaceSnapshotResult>;
76
+ close(): Promise<GovernanceRuntimeBootstrapCloseResult>;
77
+ private closeOnce;
78
+ private recordObservation;
79
+ }
80
+ export type GovernanceRuntimeBootstrapResult = {
81
+ readonly mode: 'governed';
82
+ readonly handle: GovernanceRuntimeBootstrapHandle;
83
+ } | {
84
+ readonly mode: 'legacy';
85
+ readonly code: 'bootstrap_failed' | GovernanceCompatibilityFallbackCode;
86
+ readonly phase: 'bootstrap' | GovernanceCompatibilityFallback['phase'];
87
+ readonly message: string;
88
+ readonly cleanup: GovernanceCompatibilityCleanup;
89
+ };
90
+ type GovernanceCompatibilityFactory = (options: PrepareGovernanceCompatibilityOptions) => Promise<PrepareGovernanceCompatibilityResult>;
91
+ export declare function bootstrapGovernanceRuntime(options: BootstrapGovernanceRuntimeOptions): Promise<GovernanceRuntimeBootstrapResult>;
92
+ /** Internal source-test seam. Package consumers use bootstrapGovernanceRuntime. */
93
+ export declare function bootstrapGovernanceRuntimeWithFactory(options: BootstrapGovernanceRuntimeOptions, prepare: GovernanceCompatibilityFactory): Promise<GovernanceRuntimeBootstrapResult>;
94
+ //# sourceMappingURL=governance-bootstrap.d.ts.map
@@ -0,0 +1,330 @@
1
+ // src/governance-bootstrap.ts
2
+ import { randomUUID } from "node:crypto";
3
+ import {
4
+ connectGovernanceProjectClient,
5
+ GOVERNANCE_SERVICE_PROTOCOL_VERSION,
6
+ prepareGovernanceCompatibilityRuntime,
7
+ projectGovernanceDaemonOperatorStatus,
8
+ readGovernanceDaemonAttachmentBroker
9
+ } from "@wrongstack/governance";
10
+
11
+ // src/governance-sanitize.ts
12
+ import { sanitizeGovernanceMessage } from "@wrongstack/governance";
13
+
14
+ // src/governance-bootstrap.ts
15
+ import {
16
+ createGovernanceEvidenceCandidate,
17
+ GOVERNANCE_EVIDENCE_CANDIDATE_SCHEMA_VERSION
18
+ } from "@wrongstack/governance";
19
+ var GOVERNANCE_DAEMON_OPERATOR_STATUS_DEPENDENCIES = {
20
+ readBroker: readGovernanceDaemonAttachmentBroker,
21
+ connectClient: connectGovernanceProjectClient
22
+ };
23
+ function readGovernanceDaemonOperatorStatus(projectRoot) {
24
+ return readGovernanceDaemonOperatorStatusWithDependencies(
25
+ projectRoot,
26
+ GOVERNANCE_DAEMON_OPERATOR_STATUS_DEPENDENCIES
27
+ );
28
+ }
29
+ async function readGovernanceDaemonOperatorStatusWithDependencies(projectRoot, dependencies) {
30
+ let broker;
31
+ try {
32
+ broker = await dependencies.readBroker(projectRoot);
33
+ } catch (error) {
34
+ return Object.freeze({
35
+ available: false,
36
+ code: "broker_invalid",
37
+ message: sanitizeGovernanceMessage(error instanceof Error ? error.message : String(error))
38
+ });
39
+ }
40
+ if (broker.kind === "missing") {
41
+ return Object.freeze({
42
+ available: false,
43
+ code: "broker_missing",
44
+ message: "Governance attachment broker is not published for this project."
45
+ });
46
+ }
47
+ if (broker.kind === "invalid") {
48
+ return Object.freeze({
49
+ available: false,
50
+ code: "broker_invalid",
51
+ message: sanitizeGovernanceMessage(broker.reason)
52
+ });
53
+ }
54
+ let connection;
55
+ try {
56
+ connection = await dependencies.connectClient({
57
+ projectRoot,
58
+ projectId: broker.broker.projectId,
59
+ credential: broker.broker.credential
60
+ });
61
+ } catch (error) {
62
+ return Object.freeze({
63
+ available: false,
64
+ code: "connection_failed",
65
+ message: sanitizeGovernanceMessage(error instanceof Error ? error.message : String(error))
66
+ });
67
+ }
68
+ if (!connection.connected) {
69
+ return Object.freeze({
70
+ available: false,
71
+ code: "connection_failed",
72
+ message: sanitizeGovernanceMessage(connection.message)
73
+ });
74
+ }
75
+ let response;
76
+ try {
77
+ response = await connection.client.request({
78
+ protocolVersion: GOVERNANCE_SERVICE_PROTOCOL_VERSION,
79
+ requestId: `operator-daemon-status-${randomUUID()}`,
80
+ type: "read_daemon_status"
81
+ });
82
+ } catch (error) {
83
+ return Object.freeze({
84
+ available: false,
85
+ code: "connection_failed",
86
+ message: sanitizeGovernanceMessage(error instanceof Error ? error.message : String(error))
87
+ });
88
+ }
89
+ if (!response.ok) {
90
+ return Object.freeze({
91
+ available: false,
92
+ code: "request_rejected",
93
+ message: sanitizeGovernanceMessage(response.error.message)
94
+ });
95
+ }
96
+ if (response.result.type !== "daemon_status") {
97
+ return Object.freeze({
98
+ available: false,
99
+ code: "unexpected_response",
100
+ message: "Governance daemon returned an unexpected status response."
101
+ });
102
+ }
103
+ return Object.freeze({
104
+ available: true,
105
+ status: projectGovernanceDaemonOperatorStatus(response.result)
106
+ });
107
+ }
108
+ var MAX_PENDING_GOVERNANCE_OBSERVATIONS = 256;
109
+ var GOVERNANCE_RUNTIME_BOOTSTRAP_HANDLE_CONSTRUCTION = /* @__PURE__ */ Symbol(
110
+ "governance-runtime-bootstrap-handle-construction"
111
+ );
112
+ var GovernanceRuntimeBootstrapHandle = class {
113
+ model;
114
+ #runtime;
115
+ #snapshot;
116
+ #pendingObservations = /* @__PURE__ */ new Set();
117
+ #acceptingRuntimeWrites = true;
118
+ #closePromise;
119
+ constructor(construction, runtime) {
120
+ if (construction !== GOVERNANCE_RUNTIME_BOOTSTRAP_HANDLE_CONSTRUCTION) {
121
+ throw new Error("Governance runtime handles must be created through the bootstrap adapter.");
122
+ }
123
+ this.#runtime = runtime;
124
+ this.model = runtime.model;
125
+ const snapshot = runtime.snapshot();
126
+ this.#snapshot = Object.freeze({
127
+ mode: "governed",
128
+ source: snapshot.source,
129
+ daemon: snapshot.daemon,
130
+ model: snapshot.model
131
+ });
132
+ }
133
+ snapshot() {
134
+ return this.#snapshot;
135
+ }
136
+ observe(input) {
137
+ if (!this.#acceptingRuntimeWrites) {
138
+ return Promise.resolve(
139
+ Object.freeze({
140
+ recorded: false,
141
+ code: "closed",
142
+ message: "Governance runtime is closing and no longer accepts observations."
143
+ })
144
+ );
145
+ }
146
+ if (this.#pendingObservations.size >= MAX_PENDING_GOVERNANCE_OBSERVATIONS) {
147
+ return Promise.resolve(
148
+ Object.freeze({
149
+ recorded: false,
150
+ code: "backpressure",
151
+ message: "Governance observation queue reached its bounded capacity."
152
+ })
153
+ );
154
+ }
155
+ const pending = this.recordObservation(input);
156
+ this.#pendingObservations.add(pending);
157
+ void pending.finally(() => this.#pendingObservations.delete(pending));
158
+ return pending;
159
+ }
160
+ async recordWorkspaceSnapshot(manifestHash) {
161
+ if (!this.#acceptingRuntimeWrites) {
162
+ return Object.freeze({
163
+ recorded: false,
164
+ code: "closed",
165
+ message: "Governance runtime is closing and no longer accepts workspace snapshots."
166
+ });
167
+ }
168
+ try {
169
+ const response = await this.#runtime.recordWorkspaceSnapshot(manifestHash);
170
+ if (!response.ok) {
171
+ return Object.freeze({
172
+ recorded: false,
173
+ code: "request_rejected",
174
+ message: sanitizeGovernanceMessage(response.error.message)
175
+ });
176
+ }
177
+ if (response.result.type !== "workspace_snapshot_recorded") {
178
+ return Object.freeze({
179
+ recorded: false,
180
+ code: "unexpected_response",
181
+ message: "Governance workspace snapshot returned an unexpected response."
182
+ });
183
+ }
184
+ if (!response.result.result.recorded) {
185
+ return Object.freeze({
186
+ recorded: false,
187
+ code: response.result.result.code,
188
+ message: sanitizeGovernanceMessage(response.result.result.message)
189
+ });
190
+ }
191
+ return Object.freeze({
192
+ recorded: true,
193
+ snapshot: response.result.result.snapshot
194
+ });
195
+ } catch (error) {
196
+ return Object.freeze({
197
+ recorded: false,
198
+ code: "request_failed",
199
+ message: sanitizeGovernanceMessage(error instanceof Error ? error.message : String(error))
200
+ });
201
+ }
202
+ }
203
+ close() {
204
+ if (this.#closePromise) return this.#closePromise;
205
+ this.#closePromise = this.closeOnce();
206
+ return this.#closePromise;
207
+ }
208
+ async closeOnce() {
209
+ this.#acceptingRuntimeWrites = false;
210
+ await Promise.allSettled([...this.#pendingObservations]);
211
+ const action = this.#snapshot.source === "launched" ? "shutdown" : "detach";
212
+ try {
213
+ const response = action === "shutdown" ? await this.#runtime.shutdownDaemon("WrongStack runtime session ended") : await this.#runtime.close();
214
+ if (response === null) {
215
+ return Object.freeze({ ok: true, action, message: "Governance runtime already closed." });
216
+ }
217
+ if (!response.ok) {
218
+ return Object.freeze({
219
+ ok: false,
220
+ action,
221
+ message: sanitizeGovernanceMessage(response.error.message)
222
+ });
223
+ }
224
+ const completed = action === "shutdown" ? response.result.type === "daemon_shutdown_accepted" : response.result.type === "capability_grant_revoked" || response.result.type === "runtime_attachment_released";
225
+ return Object.freeze({
226
+ ok: completed,
227
+ action,
228
+ message: completed ? `Governance runtime ${action} completed.` : `Governance runtime ${action} returned an unexpected response.`
229
+ });
230
+ } catch (error) {
231
+ return Object.freeze({
232
+ ok: false,
233
+ action,
234
+ message: sanitizeGovernanceMessage(error instanceof Error ? error.message : String(error))
235
+ });
236
+ }
237
+ }
238
+ async recordObservation(input) {
239
+ const observationId = randomUUID();
240
+ try {
241
+ const response = await this.model.request({
242
+ protocolVersion: GOVERNANCE_SERVICE_PROTOCOL_VERSION,
243
+ requestId: `observe-${observationId}`,
244
+ type: "record_observation",
245
+ observation: {
246
+ observationId,
247
+ projectId: this.#snapshot.model.projectId,
248
+ taskId: input.taskId,
249
+ source: this.#snapshot.model.clientId,
250
+ category: input.category,
251
+ observedAt: input.observedAt,
252
+ payload: input.payload
253
+ }
254
+ });
255
+ if (!response.ok) {
256
+ return Object.freeze({
257
+ recorded: false,
258
+ observationId,
259
+ code: "request_rejected",
260
+ message: sanitizeGovernanceMessage(response.error.message)
261
+ });
262
+ }
263
+ if (response.result.type !== "observation_result") {
264
+ return Object.freeze({
265
+ recorded: false,
266
+ observationId,
267
+ code: "unexpected_response",
268
+ message: "Governance observation returned an unexpected response."
269
+ });
270
+ }
271
+ if (!response.result.result.handled) {
272
+ return Object.freeze({
273
+ recorded: false,
274
+ observationId,
275
+ code: "request_rejected",
276
+ message: sanitizeGovernanceMessage(response.result.result.message)
277
+ });
278
+ }
279
+ return Object.freeze({
280
+ recorded: true,
281
+ observationId,
282
+ idempotentReplay: response.result.result.idempotentReplay,
283
+ sequence: response.result.result.observation.sequence
284
+ });
285
+ } catch (error) {
286
+ return Object.freeze({
287
+ recorded: false,
288
+ observationId,
289
+ code: "request_failed",
290
+ message: sanitizeGovernanceMessage(error instanceof Error ? error.message : String(error))
291
+ });
292
+ }
293
+ }
294
+ };
295
+ function bootstrapGovernanceRuntime(options) {
296
+ return bootstrapGovernanceRuntimeWithFactory(options, prepareGovernanceCompatibilityRuntime);
297
+ }
298
+ async function bootstrapGovernanceRuntimeWithFactory(options, prepare) {
299
+ let prepared;
300
+ try {
301
+ prepared = await prepare(options);
302
+ } catch (error) {
303
+ return Object.freeze({
304
+ mode: "legacy",
305
+ code: "bootstrap_failed",
306
+ phase: "bootstrap",
307
+ message: sanitizeGovernanceMessage(error instanceof Error ? error.message : String(error)),
308
+ cleanup: "unavailable"
309
+ });
310
+ }
311
+ if (prepared.mode === "legacy") return prepared;
312
+ return Object.freeze({
313
+ mode: "governed",
314
+ handle: new GovernanceRuntimeBootstrapHandle(
315
+ GOVERNANCE_RUNTIME_BOOTSTRAP_HANDLE_CONSTRUCTION,
316
+ prepared.runtime
317
+ )
318
+ });
319
+ }
320
+ export {
321
+ GOVERNANCE_EVIDENCE_CANDIDATE_SCHEMA_VERSION,
322
+ GovernanceRuntimeBootstrapHandle,
323
+ MAX_PENDING_GOVERNANCE_OBSERVATIONS,
324
+ bootstrapGovernanceRuntime,
325
+ bootstrapGovernanceRuntimeWithFactory,
326
+ createGovernanceEvidenceCandidate,
327
+ readGovernanceDaemonOperatorStatus,
328
+ readGovernanceDaemonOperatorStatusWithDependencies
329
+ };
330
+ //# sourceMappingURL=governance-bootstrap.js.map
@@ -0,0 +1,21 @@
1
+ import type { AgentPipelines } from '@wrongstack/core/agent';
2
+ import type { EventBus } from '@wrongstack/core/kernel';
3
+ import type { WorkspaceCheckpointRef } from '@wrongstack/core/types';
4
+ import type { GovernanceRuntimeWorkspaceSnapshotResult } from './governance-bootstrap.js';
5
+ export declare const MAX_PENDING_GOVERNANCE_MUTATION_SNAPSHOTS = 64;
6
+ export interface GovernanceWorkspaceSnapshotSink {
7
+ recordWorkspaceSnapshot(manifestHash: string): Promise<GovernanceRuntimeWorkspaceSnapshotResult>;
8
+ }
9
+ export interface GovernanceMutationSnapshotBridge {
10
+ installToolBoundary(pipelines: AgentPipelines): void;
11
+ close(): Promise<void>;
12
+ }
13
+ export declare function createGovernanceMutationSnapshotBridge(input: {
14
+ readonly events: EventBus;
15
+ readonly sink: GovernanceWorkspaceSnapshotSink;
16
+ readonly captureWorkspaceCheckpoint: () => Promise<WorkspaceCheckpointRef | undefined>;
17
+ readonly logger: {
18
+ warn(message: string, context?: unknown): void;
19
+ };
20
+ }): GovernanceMutationSnapshotBridge;
21
+ //# sourceMappingURL=governance-mutation-snapshot-bridge.d.ts.map
@@ -0,0 +1,108 @@
1
+ // src/governance-mutation-snapshot-bridge.ts
2
+ var MAX_PENDING_GOVERNANCE_MUTATION_SNAPSHOTS = 64;
3
+ function createGovernanceMutationSnapshotBridge(input) {
4
+ let closed = false;
5
+ let warned = false;
6
+ let pending = 0;
7
+ let tail = Promise.resolve();
8
+ const installedPipelines = /* @__PURE__ */ new WeakSet();
9
+ const awaitedCompletions = /* @__PURE__ */ new Set();
10
+ const awaitedCompletionOrder = [];
11
+ const warnOnce = (message, context) => {
12
+ if (warned) return;
13
+ warned = true;
14
+ if (context === void 0) input.logger.warn(message);
15
+ else input.logger.warn(message, context);
16
+ };
17
+ const completionKey = (sessionId, toolCallId) => sessionId === void 0 || toolCallId === void 0 ? void 0 : `${sessionId}\0${toolCallId}`;
18
+ const rememberAwaitedCompletion = (key) => {
19
+ if (key === void 0 || awaitedCompletions.has(key)) return;
20
+ awaitedCompletions.add(key);
21
+ awaitedCompletionOrder.push(key);
22
+ if (awaitedCompletionOrder.length > 512) {
23
+ const oldest = awaitedCompletionOrder.shift();
24
+ if (oldest !== void 0) awaitedCompletions.delete(oldest);
25
+ }
26
+ };
27
+ const enqueueSnapshot = async (completion, awaitedBoundary = false) => {
28
+ if (closed || !completion.ok || !completion.mutating) return;
29
+ while (pending >= MAX_PENDING_GOVERNANCE_MUTATION_SNAPSHOTS) {
30
+ if (awaitedBoundary) {
31
+ await tail;
32
+ if (closed) return;
33
+ continue;
34
+ }
35
+ warnOnce("governance: mutation snapshot queue reached bounded capacity", {
36
+ capacity: MAX_PENDING_GOVERNANCE_MUTATION_SNAPSHOTS
37
+ });
38
+ return;
39
+ }
40
+ pending += 1;
41
+ const settled = tail.then(async () => {
42
+ const checkpoint = await input.captureWorkspaceCheckpoint();
43
+ if (!checkpoint || !/^[a-f0-9]{64}$/u.test(checkpoint.manifestHash)) {
44
+ warnOnce("governance: failed to capture a valid post-mutation workspace identity");
45
+ return;
46
+ }
47
+ const result = await input.sink.recordWorkspaceSnapshot(checkpoint.manifestHash);
48
+ if (!result.recorded) {
49
+ warnOnce("governance: post-mutation workspace snapshot was not recorded", {
50
+ code: result.code,
51
+ message: result.message
52
+ });
53
+ }
54
+ }).catch((error) => {
55
+ warnOnce("governance: post-mutation workspace snapshot failed open", {
56
+ message: error instanceof Error ? error.message : String(error)
57
+ });
58
+ }).finally(() => {
59
+ pending -= 1;
60
+ });
61
+ tail = settled;
62
+ await settled;
63
+ };
64
+ const dispose = input.events.on("tool.executed", (event) => {
65
+ const key = completionKey(event.sessionId, event.id);
66
+ if (key !== void 0 && awaitedCompletions.delete(key)) return;
67
+ void enqueueSnapshot({ ok: event.ok, mutating: event.mutating === true });
68
+ });
69
+ return Object.freeze({
70
+ installToolBoundary: (pipelines) => {
71
+ if (installedPipelines.has(pipelines)) return;
72
+ installedPipelines.add(pipelines);
73
+ pipelines.toolCall.prepend({
74
+ name: "GovernanceWorkspaceSnapshotFence",
75
+ owner: "governance-compatibility",
76
+ handler: async (payload, next) => {
77
+ let completed = payload;
78
+ try {
79
+ completed = await next(payload);
80
+ return completed;
81
+ } finally {
82
+ const key = completionKey(completed.ctx.session.id, completed.toolUse.id);
83
+ await enqueueSnapshot(
84
+ {
85
+ ok: !completed.result.is_error,
86
+ mutating: completed.tool?.mutating === true
87
+ },
88
+ true
89
+ );
90
+ rememberAwaitedCompletion(key);
91
+ }
92
+ }
93
+ });
94
+ },
95
+ close: async () => {
96
+ if (!closed) {
97
+ closed = true;
98
+ dispose();
99
+ }
100
+ await tail;
101
+ }
102
+ });
103
+ }
104
+ export {
105
+ MAX_PENDING_GOVERNANCE_MUTATION_SNAPSHOTS,
106
+ createGovernanceMutationSnapshotBridge
107
+ };
108
+ //# sourceMappingURL=governance-mutation-snapshot-bridge.js.map