@wrongstack/runtime 0.297.0 → 0.298.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/dist/clipboard.js +43 -11
- package/dist/fleet/light-subagent-factory.d.ts +5 -0
- package/dist/governance-bootstrap.d.ts +94 -0
- package/dist/governance-bootstrap.js +330 -0
- package/dist/governance-mutation-snapshot-bridge.d.ts +21 -0
- package/dist/governance-mutation-snapshot-bridge.js +108 -0
- package/dist/governance-sanitize.d.ts +10 -0
- package/dist/governance-sanitize.js +6 -0
- package/dist/index.js +46 -13
- package/package.json +18 -4
- package/dist/clipboard.d.ts.map +0 -1
- package/dist/clipboard.js.map +0 -7
- package/dist/container.d.ts.map +0 -1
- package/dist/fleet/light-subagent-factory.d.ts.map +0 -1
- package/dist/host.d.ts.map +0 -1
- package/dist/host.js.map +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -7
- package/dist/local-llm-probe.d.ts.map +0 -1
- package/dist/pack.d.ts.map +0 -1
- package/dist/pack.js.map +0 -7
- package/dist/probe.js.map +0 -7
- package/dist/tool-registration.d.ts.map +0 -1
- package/dist/tool-registration.js.map +0 -7
- package/dist/vision.d.ts.map +0 -1
- package/dist/vision.js.map +0 -7
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|