dsh-completion-guard 0.2.1 → 0.3.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/CHANGELOG.md +24 -1
- package/CHANGELOG.zh-CN.md +24 -1
- package/README.md +45 -5
- package/README.zh-CN.md +45 -5
- package/bin/dsh-completion-guard-host-lock.mjs +52 -0
- package/dist/domain/index.d.ts +2 -2
- package/dist/domain/index.js +2 -2
- package/dist/domain-CBvBQHTL.js +5828 -0
- package/dist/index-GvKLkTqV.d.ts +957 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +2463 -119
- package/docs/ARCHITECTURE.md +11 -7
- package/docs/COMPATIBILITY.md +23 -3
- package/docs/LOCAL_ACCEPTANCE.md +192 -1
- package/docs/SEMANTIC_COMPATIBILITY.md +118 -0
- package/docs/distribution.md +45 -0
- package/docs/upstream-deltas.json +217 -0
- package/manifests/action-manifest.v1.json +33 -0
- package/manifests/git-command-manifest.v2.json +52 -0
- package/manifests/supported-host.v1.json +66 -0
- package/package.json +52 -14
- package/dist/domain-BN3_AuUr.js +0 -1997
- package/dist/index-Dk4SkQ8H.d.ts +0 -448
package/dist/index.js
CHANGED
|
@@ -1,30 +1,2061 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { $ as classifyUserInteraction, A as extractToolSubject, At as STOP_PROTOCOL_VERSION, B as DEFAULT_HOST_LOCK, Bt as COMMAND_SURFACE_MANIFEST, C as latestAssistantText, Ct as canonicalRegistryBase, D as supersedeItem, Dt as CERTIFICATE_VERSION, E as deriveProjection, Et as ACTION_MANIFEST_VERSION, F as parsePwshCommand, Ft as requestedTargetMatchesResolved, G as bindExecutableIdentity, Gt as sanitizeClauseText, H as GOAL_HOST_PACKAGES, Ht as canonicalizePath, I as parseShellCommand, It as semanticActionFromCommand, J as evaluateHostCapability, Jt as createProjection, K as bindLiveGoalCapability, Kt as sanitizeUrl, L as goalCompletionDenial, Lt as semanticActionFromText, M as withDurability, Mt as actionCompatible, N as canonicalArgvFromCommand, Nt as isStatefulAction, O as evidenceFromPersistedToolResult, Ot as SEMANTIC_ACTIONS, P as isRunExecutable, Pt as requestedTargetAuthorizesMutation, Q as segmentAuthorityBlocks, R as hasCurrentCertificate, Rt as validateActionManifest, S as isWholeTaskCompletionClaim, St as segmentClauses, T as PROTOCOL_V3_NOTICE, Tt as ACTION_MANIFEST, U as HOST_CAPABILITY_PACKAGE_GROUPS, Ut as digestStrings, V as EXPECTED_HOST_PACKAGES, Vt as validateManifest, W as SUPPORTED_HOST_MANIFEST, Wt as normalizeClause, X as evaluateToolSurfaceCapability, Y as evaluateHostLock, Z as authorityCaptureCounts, _ as revalidateGitPrestate, _t as classifyClause, a as packageRowsFromActiveGraph, at as DEFAULT_RECOVERY_CHAR_BUDGET, b as decideTurnBoundary, bt as extractOperation, c as resolveInstalledHostLock, ct as recoveryDigest, d as commitIndexSnapshotDigest, dt as evidenceCoverage, et as availableBoundaryQualifications, f as commitTreeSnapshotDigest, ft as evidenceMatchesItem, g as parseGitCommandManifest, gt as captureItem, h as gitCommandMatchesTarget, ht as captureClause, i as injectActiveProfileHostLock, it as certifyCheckpoint, j as isDeterministicCheck, jt as SUPPORTED_EVIDENCE_ADAPTERS, k as extractTextContent, kt as STATEFUL_ACTIONS, l as verifyComposedHostLockDump, lt as renderRecoveryPacket, m as executeRevalidatedGitEffect, mt as currentContractDigest, n as hostLockContextFromComposedDump, nt as isCurrentAcceptedBoundary, o as packageRowsFromPnpmLock, ot as closingHint, p as createGitPrestateEnvelope, pt as isVerifyingCapability, q as evaluateExternalWaitCapability, qt as sha256, r as hostLockRowsFromComposedDump, rt as qualifyBoundary, s as resolveActiveProfileHostLock, st as openItems, t as HostProfileError, tt as effectuateBoundary, u as GIT_COMMAND_MANIFEST_IDS, ut as bindingSatisfies, v as verifiedLinearCommitReadback, vt as extractArtifactPaths, w as observeAssistantOutcome, wt as npmEscapedPackageName, x as decideTurnStopping, xt as isInformationalMessage, y as classifyCompletionClaim, yt as extractMethod, z as BASE_HOST_PACKAGES, zt as validateActionTarget } from "./domain-CBvBQHTL.js";
|
|
2
2
|
import { boundContextSummary, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { basename, delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
5
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
|
+
import { constants, existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { access, readFile, realpath } from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { promisify } from "node:util";
|
|
12
|
+
import { gunzip } from "node:zlib";
|
|
4
13
|
|
|
5
14
|
//#region src/tools/checkpoint.ts
|
|
6
|
-
function
|
|
15
|
+
function targetForTool(target) {
|
|
16
|
+
if (!target) return {};
|
|
17
|
+
return Object.fromEntries(Object.entries(target).map(([key, value]) => {
|
|
18
|
+
if (typeof value !== "object" || value === null) return [key, value];
|
|
19
|
+
const raw = value.v;
|
|
20
|
+
const jsonValue = raw === null || [
|
|
21
|
+
"string",
|
|
22
|
+
"number",
|
|
23
|
+
"boolean"
|
|
24
|
+
].includes(typeof raw) ? raw : String(raw);
|
|
25
|
+
return [key, {
|
|
26
|
+
k: value.k,
|
|
27
|
+
v: jsonValue
|
|
28
|
+
}];
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
function expectedParameters(action, resolved, observed) {
|
|
32
|
+
if (action === "inspect_remote_updates") return {
|
|
33
|
+
...pick$1(resolved, ["remote", "version"]),
|
|
34
|
+
...pick$1(observed, ["upstream_oid"])
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
expected_outcome: {
|
|
38
|
+
k: "e",
|
|
39
|
+
v: "success"
|
|
40
|
+
},
|
|
41
|
+
min_matches: 1
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function expectedTransitionForTool(transition) {
|
|
45
|
+
return {
|
|
46
|
+
predicate_id: transition.predicateId,
|
|
47
|
+
version: transition.version,
|
|
48
|
+
pred_params_kind: transition.predParamsKind,
|
|
49
|
+
...transition.parameters ? { parameters: targetForTool(transition.parameters) } : {},
|
|
50
|
+
...transition.parametersDigest ? { parameters_digest: transition.parametersDigest } : {}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function pick$1(tuple$1, keys) {
|
|
54
|
+
return Object.fromEntries(keys.filter((key) => Object.hasOwn(tuple$1, key)).map((key) => [key, tuple$1[key]]));
|
|
55
|
+
}
|
|
56
|
+
function sameTuple(left, right) {
|
|
57
|
+
const stable$1 = (value) => Array.isArray(value) ? `[${value.map(stable$1).join(",")}]` : value && typeof value === "object" ? `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => `${JSON.stringify(key)}:${stable$1(entry)}`).join(",")}}` : JSON.stringify(value);
|
|
58
|
+
return stable$1(left ?? {}) === stable$1(right ?? {});
|
|
59
|
+
}
|
|
60
|
+
function evidenceForAction(projection, item) {
|
|
61
|
+
return [...projection.evidence.values()].filter((evidence) => evidence.epoch === projection.epoch && evidence.outcome === "success" && evidence.semanticAction === item.semanticAction);
|
|
62
|
+
}
|
|
63
|
+
function bindingTemplate(projection, item) {
|
|
64
|
+
const action = item.semanticAction;
|
|
65
|
+
if (!action || action === "generic_run") return void 0;
|
|
66
|
+
const evidence = evidenceForAction(projection, item);
|
|
67
|
+
const effect = evidence.find((entry) => (entry.evidenceRole ?? "effect") === "effect");
|
|
68
|
+
if (!effect?.resolvedTarget) return void 0;
|
|
69
|
+
const resolved = effect.resolvedTarget;
|
|
70
|
+
const resolution = isStatefulAction(action) ? evidence.find((entry) => entry.evidenceRole === "resolution" && sameTuple(entry.resolvedTarget, resolved)) : void 0;
|
|
71
|
+
const states = isStatefulAction(action) ? evidence.filter((entry) => entry.evidenceRole === "state" && sameTuple(entry.resolvedTarget, resolved)) : [];
|
|
72
|
+
if (isStatefulAction(action) && (!resolution || states.length === 0)) return void 0;
|
|
73
|
+
if (isStatefulAction(action) && (!resolution?.expectedTransition?.parameters || !resolution.expectedTransitionDigest)) return void 0;
|
|
74
|
+
const observed = isStatefulAction(action) ? Object.assign({}, ...states.map((entry) => entry.observedState ?? {})) : effect.observedState ?? {};
|
|
75
|
+
const expectedTransition = isStatefulAction(action) ? resolution.expectedTransition : {
|
|
76
|
+
predicateId: ACTION_MANIFEST.actions[action].predicateId,
|
|
77
|
+
version: 1,
|
|
78
|
+
predParamsKind: "inline",
|
|
79
|
+
parameters: expectedParameters(action, resolved, observed)
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
item_id: item.id,
|
|
83
|
+
evidence_ids: isStatefulAction(action) ? [
|
|
84
|
+
resolution.id,
|
|
85
|
+
effect.id,
|
|
86
|
+
...states.map((entry) => entry.id)
|
|
87
|
+
] : [effect.id],
|
|
88
|
+
semantic_action: action,
|
|
89
|
+
requested_target: targetForTool(item.requestedTarget),
|
|
90
|
+
resolved_target: targetForTool(resolved),
|
|
91
|
+
observed_state: targetForTool(observed),
|
|
92
|
+
expected_transition: expectedTransitionForTool(expectedTransition),
|
|
93
|
+
...isStatefulAction(action) ? {
|
|
94
|
+
resolution_evidence_id: resolution.id,
|
|
95
|
+
effect_evidence_id: effect.id,
|
|
96
|
+
state_evidence_ids: states.map((entry) => entry.id)
|
|
97
|
+
} : { effect_evidence_id: effect.id }
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function openItemForTool(projection, item) {
|
|
101
|
+
const action = item.semanticAction ?? "generic_run";
|
|
102
|
+
const spec = ACTION_MANIFEST.actions[action];
|
|
103
|
+
const template = bindingTemplate(projection, item);
|
|
104
|
+
return {
|
|
105
|
+
id: item.id,
|
|
106
|
+
kind: item.kind,
|
|
107
|
+
semantic_action: action,
|
|
108
|
+
requested_target: targetForTool(item.requestedTarget),
|
|
109
|
+
certifiable: action !== "generic_run" && ACTION_MANIFEST.actions[action].evidenceProducer === "supported" && !item.legacyFlags?.length && item.targetCaptureStatus !== "clarification_required",
|
|
110
|
+
producer_disposition: ACTION_MANIFEST.actions[action].evidenceProducer,
|
|
111
|
+
...item.targetCaptureStatus ? { target_capture_status: item.targetCaptureStatus } : {},
|
|
112
|
+
...item.targetCaptureReasonCode ? { target_capture_reason_code: item.targetCaptureReasonCode } : {},
|
|
113
|
+
predicate: {
|
|
114
|
+
predicate_id: spec.predicateId,
|
|
115
|
+
version: 1,
|
|
116
|
+
parameters_source: isStatefulAction(action) ? "resolution_evidence_expected_transition" : "versioned_action_manifest",
|
|
117
|
+
resolved_target_keys: spec.resolvedTargetKeys,
|
|
118
|
+
observed_state_keys: spec.observedStateKeys,
|
|
119
|
+
pred_params_kind: "inline"
|
|
120
|
+
},
|
|
121
|
+
...template ? { binding_template: template } : {}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function createCheckpointTool(getProjection, onRejected, prepare = async () => true) {
|
|
125
|
+
return defineTool({
|
|
126
|
+
name: "context_guard_checkpoint",
|
|
127
|
+
description: "Request a completion certificate from existing durable evidence.",
|
|
128
|
+
parameters: { bindings: {
|
|
129
|
+
type: "array",
|
|
130
|
+
required: true,
|
|
131
|
+
items: {
|
|
132
|
+
type: "object",
|
|
133
|
+
additionalProperties: false,
|
|
134
|
+
properties: {
|
|
135
|
+
item_id: {
|
|
136
|
+
type: "string",
|
|
137
|
+
required: true
|
|
138
|
+
},
|
|
139
|
+
evidence_ids: {
|
|
140
|
+
type: "array",
|
|
141
|
+
required: true,
|
|
142
|
+
items: { type: "string" }
|
|
143
|
+
},
|
|
144
|
+
semantic_action: { type: "string" },
|
|
145
|
+
requested_target: {
|
|
146
|
+
type: "object",
|
|
147
|
+
additionalProperties: true
|
|
148
|
+
},
|
|
149
|
+
resolved_target: {
|
|
150
|
+
type: "object",
|
|
151
|
+
additionalProperties: true
|
|
152
|
+
},
|
|
153
|
+
observed_state: {
|
|
154
|
+
type: "object",
|
|
155
|
+
additionalProperties: true
|
|
156
|
+
},
|
|
157
|
+
expected_transition: {
|
|
158
|
+
type: "object",
|
|
159
|
+
additionalProperties: false,
|
|
160
|
+
properties: {
|
|
161
|
+
predicate_id: {
|
|
162
|
+
type: "string",
|
|
163
|
+
required: true
|
|
164
|
+
},
|
|
165
|
+
version: {
|
|
166
|
+
type: "integer",
|
|
167
|
+
required: true
|
|
168
|
+
},
|
|
169
|
+
pred_params_kind: {
|
|
170
|
+
type: "string",
|
|
171
|
+
required: true,
|
|
172
|
+
enum: ["inline"]
|
|
173
|
+
},
|
|
174
|
+
parameters: {
|
|
175
|
+
type: "object",
|
|
176
|
+
additionalProperties: true
|
|
177
|
+
},
|
|
178
|
+
parameters_digest: { type: "string" }
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
resolution_evidence_id: { type: "string" },
|
|
182
|
+
effect_evidence_id: { type: "string" },
|
|
183
|
+
state_evidence_ids: {
|
|
184
|
+
type: "array",
|
|
185
|
+
items: { type: "string" }
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} },
|
|
190
|
+
output: {
|
|
191
|
+
schema: {
|
|
192
|
+
type: "object",
|
|
193
|
+
additionalProperties: false,
|
|
194
|
+
properties: {
|
|
195
|
+
status: {
|
|
196
|
+
type: "string",
|
|
197
|
+
enum: [
|
|
198
|
+
"certified",
|
|
199
|
+
"incomplete",
|
|
200
|
+
"unknown"
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
contract_revision: { type: "integer" },
|
|
204
|
+
open_items: {
|
|
205
|
+
type: "array",
|
|
206
|
+
items: {
|
|
207
|
+
type: "object",
|
|
208
|
+
additionalProperties: true
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
available_evidence: {
|
|
212
|
+
type: "array",
|
|
213
|
+
items: {
|
|
214
|
+
type: "object",
|
|
215
|
+
additionalProperties: false,
|
|
216
|
+
properties: {
|
|
217
|
+
id: { type: "string" },
|
|
218
|
+
call_id: { type: "string" },
|
|
219
|
+
tool: { type: "string" },
|
|
220
|
+
subjects: {
|
|
221
|
+
type: "array",
|
|
222
|
+
items: { type: "string" }
|
|
223
|
+
},
|
|
224
|
+
surfaces: {
|
|
225
|
+
type: "array",
|
|
226
|
+
items: { type: "string" }
|
|
227
|
+
},
|
|
228
|
+
outcome: { type: "string" },
|
|
229
|
+
capabilities: {
|
|
230
|
+
type: "array",
|
|
231
|
+
items: { type: "string" }
|
|
232
|
+
},
|
|
233
|
+
operations: {
|
|
234
|
+
type: "array",
|
|
235
|
+
items: { type: "string" }
|
|
236
|
+
},
|
|
237
|
+
executables: {
|
|
238
|
+
type: "array",
|
|
239
|
+
items: { type: "string" }
|
|
240
|
+
},
|
|
241
|
+
semantic_action: { type: "string" },
|
|
242
|
+
evidence_role: {
|
|
243
|
+
type: "string",
|
|
244
|
+
enum: [
|
|
245
|
+
"resolution",
|
|
246
|
+
"effect",
|
|
247
|
+
"state"
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
resolved_target: {
|
|
251
|
+
type: "object",
|
|
252
|
+
additionalProperties: true
|
|
253
|
+
},
|
|
254
|
+
observed_state: {
|
|
255
|
+
type: "object",
|
|
256
|
+
additionalProperties: true
|
|
257
|
+
},
|
|
258
|
+
expected_transition: {
|
|
259
|
+
type: "object",
|
|
260
|
+
additionalProperties: true
|
|
261
|
+
},
|
|
262
|
+
expected_transition_digest: { type: "string" },
|
|
263
|
+
parse_status: { type: "string" },
|
|
264
|
+
reason_code: { type: "string" },
|
|
265
|
+
adapter_id: { type: "string" },
|
|
266
|
+
adapter_version: { type: "string" },
|
|
267
|
+
adapter_disposition: {
|
|
268
|
+
type: "string",
|
|
269
|
+
enum: ["citable", "unavailable"]
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
available_qualifications: {
|
|
275
|
+
type: "array",
|
|
276
|
+
items: {
|
|
277
|
+
type: "object",
|
|
278
|
+
additionalProperties: false,
|
|
279
|
+
properties: {
|
|
280
|
+
id: { type: "string" },
|
|
281
|
+
kind: { type: "string" },
|
|
282
|
+
disposition: { type: "string" },
|
|
283
|
+
source: { type: "string" },
|
|
284
|
+
status: { type: "string" }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
rejected_bindings: {
|
|
289
|
+
type: "array",
|
|
290
|
+
items: {
|
|
291
|
+
type: "object",
|
|
292
|
+
additionalProperties: false,
|
|
293
|
+
properties: {
|
|
294
|
+
item_id: { type: "string" },
|
|
295
|
+
reason: { type: "string" },
|
|
296
|
+
reason_code: { type: "string" },
|
|
297
|
+
offending_evidence_ids: {
|
|
298
|
+
type: "array",
|
|
299
|
+
items: { type: "string" }
|
|
300
|
+
},
|
|
301
|
+
hint: { type: "string" }
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
certificate: {
|
|
306
|
+
type: "object",
|
|
307
|
+
additionalProperties: false,
|
|
308
|
+
properties: {
|
|
309
|
+
stop_protocol_version: { type: "string" },
|
|
310
|
+
certificate_version: { type: "string" },
|
|
311
|
+
epoch: { type: "integer" },
|
|
312
|
+
session_ref_digest: { type: "string" },
|
|
313
|
+
host_lock_digest: { type: "string" },
|
|
314
|
+
contract_revision: { type: "integer" },
|
|
315
|
+
contract_sha256: { type: "string" },
|
|
316
|
+
open_digest: { type: "string" },
|
|
317
|
+
evidence_sha256: { type: "string" },
|
|
318
|
+
binding_digest: { type: "string" },
|
|
319
|
+
certification_digest: { type: "string" },
|
|
320
|
+
goal_ref: { oneOf: [{
|
|
321
|
+
type: "object",
|
|
322
|
+
additionalProperties: false,
|
|
323
|
+
properties: {
|
|
324
|
+
id: { type: "string" },
|
|
325
|
+
revision: { type: "integer" }
|
|
326
|
+
}
|
|
327
|
+
}, { type: "null" }] }
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
render: (_args, value) => [{
|
|
333
|
+
type: "text",
|
|
334
|
+
text: JSON.stringify(value)
|
|
335
|
+
}]
|
|
336
|
+
},
|
|
337
|
+
async execute(rawArgs) {
|
|
338
|
+
const args = rawArgs;
|
|
339
|
+
const durable = await prepare();
|
|
340
|
+
const projection = getProjection();
|
|
341
|
+
if (!durable || !projection) {
|
|
342
|
+
onRejected();
|
|
343
|
+
return {
|
|
344
|
+
status: "unknown",
|
|
345
|
+
contract_revision: 0,
|
|
346
|
+
open_items: [],
|
|
347
|
+
available_evidence: [],
|
|
348
|
+
available_qualifications: [],
|
|
349
|
+
rejected_bindings: []
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const result = certifyCheckpoint(projection, args.bindings.map((binding) => ({
|
|
353
|
+
itemId: binding.item_id,
|
|
354
|
+
evidenceIds: binding.evidence_ids,
|
|
355
|
+
...binding.semantic_action ? { semanticAction: binding.semantic_action } : {},
|
|
356
|
+
...binding.requested_target ? { requestedTarget: binding.requested_target } : {},
|
|
357
|
+
...binding.resolved_target ? { resolvedTarget: binding.resolved_target } : {},
|
|
358
|
+
...binding.observed_state ? { observedState: binding.observed_state } : {},
|
|
359
|
+
...binding.expected_transition ? { expectedTransition: {
|
|
360
|
+
predicateId: binding.expected_transition.predicate_id,
|
|
361
|
+
version: binding.expected_transition.version,
|
|
362
|
+
predParamsKind: binding.expected_transition.pred_params_kind,
|
|
363
|
+
...binding.expected_transition.parameters ? { parameters: binding.expected_transition.parameters } : {},
|
|
364
|
+
...binding.expected_transition.parameters_digest ? { parametersDigest: binding.expected_transition.parameters_digest } : {}
|
|
365
|
+
} } : {},
|
|
366
|
+
...binding.resolution_evidence_id ? { resolutionEvidenceId: binding.resolution_evidence_id } : {},
|
|
367
|
+
...binding.effect_evidence_id ? { effectEvidenceId: binding.effect_evidence_id } : {},
|
|
368
|
+
...binding.state_evidence_ids ? { stateEvidenceIds: binding.state_evidence_ids } : {}
|
|
369
|
+
})), `C${projection.checkpoints.length + 1}`, false);
|
|
370
|
+
if (!result.checkpoint) onRejected();
|
|
371
|
+
const available_evidence = [...projection.evidence.values()].filter((evidence) => evidence.epoch === projection.epoch && evidence.outcome === "success").sort((a, b) => a.id < b.id ? -1 : 1).map((evidence) => ({
|
|
372
|
+
id: evidence.id,
|
|
373
|
+
call_id: evidence.callId,
|
|
374
|
+
tool: evidence.toolName,
|
|
375
|
+
subjects: evidence.subjects,
|
|
376
|
+
surfaces: evidence.surfaces,
|
|
377
|
+
outcome: evidence.outcome,
|
|
378
|
+
capabilities: evidence.capabilities,
|
|
379
|
+
operations: (evidence.operations ?? []).map((entry) => entry.op),
|
|
380
|
+
executables: evidence.executables ?? [],
|
|
381
|
+
semantic_action: evidence.semanticAction ?? "generic_run",
|
|
382
|
+
evidence_role: evidence.evidenceRole ?? "effect",
|
|
383
|
+
resolved_target: targetForTool(evidence.resolvedTarget),
|
|
384
|
+
observed_state: targetForTool(evidence.observedState),
|
|
385
|
+
...evidence.expectedTransition ? { expected_transition: expectedTransitionForTool(evidence.expectedTransition) } : {},
|
|
386
|
+
...evidence.expectedTransitionDigest ? { expected_transition_digest: evidence.expectedTransitionDigest } : {},
|
|
387
|
+
parse_status: evidence.parseStatus ?? "adapter_unavailable",
|
|
388
|
+
...evidence.adapterId ? { adapter_id: evidence.adapterId } : {},
|
|
389
|
+
...evidence.adapterVersion ? { adapter_version: evidence.adapterVersion } : {},
|
|
390
|
+
adapter_disposition: evidence.parseStatus === "supported" ? "citable" : "unavailable",
|
|
391
|
+
...evidence.reasonCode ? { reason_code: evidence.reasonCode } : {}
|
|
392
|
+
}));
|
|
393
|
+
return {
|
|
394
|
+
status: result.status,
|
|
395
|
+
contract_revision: result.contractRevision,
|
|
396
|
+
open_items: result.openItems.map((id) => projection.items.get(id)).filter((item) => Boolean(item)).map((item) => openItemForTool(projection, item)),
|
|
397
|
+
available_evidence,
|
|
398
|
+
available_qualifications: availableBoundaryQualifications(projection).map((row) => ({
|
|
399
|
+
id: row.id,
|
|
400
|
+
kind: row.kind,
|
|
401
|
+
disposition: row.disposition,
|
|
402
|
+
source: row.source,
|
|
403
|
+
status: row.status
|
|
404
|
+
})),
|
|
405
|
+
rejected_bindings: result.rejectedBindings.map((binding) => ({
|
|
406
|
+
item_id: binding.itemId,
|
|
407
|
+
reason: binding.reason,
|
|
408
|
+
reason_code: binding.reasonCode,
|
|
409
|
+
...binding.offendingEvidenceIds ? { offending_evidence_ids: binding.offendingEvidenceIds } : {},
|
|
410
|
+
...binding.hint !== void 0 ? { hint: binding.hint } : {}
|
|
411
|
+
})),
|
|
412
|
+
...result.checkpoint ? { certificate: {
|
|
413
|
+
stop_protocol_version: result.checkpoint.stopProtocolVersion,
|
|
414
|
+
certificate_version: result.checkpoint.certificateVersion,
|
|
415
|
+
epoch: result.checkpoint.epoch,
|
|
416
|
+
session_ref_digest: result.checkpoint.sessionRefDigest,
|
|
417
|
+
host_lock_digest: result.checkpoint.hostLockDigest,
|
|
418
|
+
contract_revision: result.checkpoint.contractRevision,
|
|
419
|
+
contract_sha256: result.checkpoint.contractSha256,
|
|
420
|
+
open_digest: result.checkpoint.openDigest,
|
|
421
|
+
evidence_sha256: result.checkpoint.evidenceSha256,
|
|
422
|
+
binding_digest: result.checkpoint.bindingDigest,
|
|
423
|
+
certification_digest: result.checkpoint.certificationDigest,
|
|
424
|
+
goal_ref: result.checkpoint.goalRef ?? null
|
|
425
|
+
} } : {}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
//#endregion
|
|
432
|
+
//#region src/tools/boundary.ts
|
|
433
|
+
function createBoundaryTool(getProjection, prepare, onRejected) {
|
|
434
|
+
return defineTool({
|
|
435
|
+
name: "context_guard_boundary",
|
|
436
|
+
description: "Persist a qualified user_wait, external_wait, or deferred boundary. Free-form notes never qualify a boundary.",
|
|
437
|
+
parameters: {
|
|
438
|
+
disposition: {
|
|
439
|
+
type: "string",
|
|
440
|
+
required: true,
|
|
441
|
+
enum: [
|
|
442
|
+
"user_wait",
|
|
443
|
+
"external_wait",
|
|
444
|
+
"deferred"
|
|
445
|
+
]
|
|
446
|
+
},
|
|
447
|
+
qualification_kind: {
|
|
448
|
+
type: "string",
|
|
449
|
+
required: true,
|
|
450
|
+
enum: [
|
|
451
|
+
"user_decision_item",
|
|
452
|
+
"root_explicit_wait",
|
|
453
|
+
"external_operation_pending",
|
|
454
|
+
"root_explicit_defer"
|
|
455
|
+
]
|
|
456
|
+
},
|
|
457
|
+
qualification_ids: {
|
|
458
|
+
type: "array",
|
|
459
|
+
required: true,
|
|
460
|
+
items: { type: "string" }
|
|
461
|
+
},
|
|
462
|
+
note: { type: "string" }
|
|
463
|
+
},
|
|
464
|
+
output: {
|
|
465
|
+
schema: {
|
|
466
|
+
type: "object",
|
|
467
|
+
additionalProperties: false,
|
|
468
|
+
properties: {
|
|
469
|
+
status: {
|
|
470
|
+
type: "string",
|
|
471
|
+
enum: [
|
|
472
|
+
"accepted",
|
|
473
|
+
"rejected",
|
|
474
|
+
"unknown"
|
|
475
|
+
]
|
|
476
|
+
},
|
|
477
|
+
reason_code: { type: "string" },
|
|
478
|
+
available_qualifications: {
|
|
479
|
+
type: "array",
|
|
480
|
+
items: {
|
|
481
|
+
type: "object",
|
|
482
|
+
additionalProperties: false,
|
|
483
|
+
properties: {
|
|
484
|
+
id: { type: "string" },
|
|
485
|
+
kind: { type: "string" },
|
|
486
|
+
disposition: { type: "string" },
|
|
487
|
+
source: { type: "string" },
|
|
488
|
+
status: { type: "string" }
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
boundary: {
|
|
493
|
+
type: "object",
|
|
494
|
+
additionalProperties: false,
|
|
495
|
+
properties: {
|
|
496
|
+
id: { type: "string" },
|
|
497
|
+
disposition: { type: "string" },
|
|
498
|
+
qualification_kind: { type: "string" },
|
|
499
|
+
qualification_ids: {
|
|
500
|
+
type: "array",
|
|
501
|
+
items: { type: "string" }
|
|
502
|
+
},
|
|
503
|
+
epoch: { type: "integer" },
|
|
504
|
+
contract_revision: { type: "integer" },
|
|
505
|
+
contract_sha256: { type: "string" },
|
|
506
|
+
candidate_sha256: { type: "string" }
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
render: (_args, value) => [{
|
|
512
|
+
type: "text",
|
|
513
|
+
text: JSON.stringify(value)
|
|
514
|
+
}]
|
|
515
|
+
},
|
|
516
|
+
async execute(args) {
|
|
517
|
+
const durable = await prepare();
|
|
518
|
+
const projection = getProjection();
|
|
519
|
+
if (!durable || !projection) {
|
|
520
|
+
onRejected();
|
|
521
|
+
return {
|
|
522
|
+
status: "unknown",
|
|
523
|
+
reason_code: "boundary_persistence_unknown",
|
|
524
|
+
available_qualifications: [],
|
|
525
|
+
boundary: void 0
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
const candidate = qualifyBoundary(projection, {
|
|
529
|
+
disposition: args.disposition,
|
|
530
|
+
qualificationKind: args.qualification_kind,
|
|
531
|
+
qualificationIds: args.qualification_ids
|
|
532
|
+
});
|
|
533
|
+
if (candidate.persistedResult !== "accepted") onRejected();
|
|
534
|
+
return {
|
|
535
|
+
status: candidate.persistedResult,
|
|
536
|
+
reason_code: candidate.reasonCode,
|
|
537
|
+
available_qualifications: availableBoundaryQualifications(projection).map((row) => ({
|
|
538
|
+
id: row.id,
|
|
539
|
+
kind: row.kind,
|
|
540
|
+
disposition: row.disposition,
|
|
541
|
+
source: row.source,
|
|
542
|
+
status: row.status
|
|
543
|
+
})),
|
|
544
|
+
boundary: {
|
|
545
|
+
id: candidate.id,
|
|
546
|
+
disposition: candidate.disposition,
|
|
547
|
+
qualification_kind: candidate.qualificationKind,
|
|
548
|
+
qualification_ids: candidate.qualificationIds,
|
|
549
|
+
epoch: candidate.epoch,
|
|
550
|
+
contract_revision: candidate.contractRevision,
|
|
551
|
+
contract_sha256: candidate.contractSha256,
|
|
552
|
+
candidate_sha256: candidate.candidateSha256
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/tools/evidence.ts
|
|
561
|
+
const execFileAsync = promisify(execFile);
|
|
562
|
+
const PRODUCER_VERSION = "1.0.0";
|
|
563
|
+
const PRODUCER_TOOL = "context_guard_evidence";
|
|
564
|
+
const ACTION_TOOL = "context_guard_action";
|
|
565
|
+
const SUPPORTED = [
|
|
566
|
+
"install",
|
|
567
|
+
"apply",
|
|
568
|
+
"create",
|
|
569
|
+
"modify",
|
|
570
|
+
"restart",
|
|
571
|
+
"commit",
|
|
572
|
+
"push",
|
|
573
|
+
"publish",
|
|
574
|
+
"pull",
|
|
575
|
+
"fetch"
|
|
576
|
+
];
|
|
577
|
+
const RESTART_INTENT_PREFIX = "Context Guard restart intent v1: ";
|
|
578
|
+
function restartIntent(events, resolutionCallId, target) {
|
|
579
|
+
for (const raw of events) {
|
|
580
|
+
const event = record(raw);
|
|
581
|
+
if (!event || event.type !== "user/message") continue;
|
|
582
|
+
const data = record(event.data);
|
|
583
|
+
const source = record(data?.source);
|
|
584
|
+
if (source?.kind !== "plugin" || source.plugin !== "context-guard" || source.form !== "notice") continue;
|
|
585
|
+
const content = Array.isArray(data?.content) ? data.content : [];
|
|
586
|
+
const block = content.length === 1 ? record(content[0]) : void 0;
|
|
587
|
+
const text = typeof block?.text === "string" ? block.text : "";
|
|
588
|
+
if (!text.startsWith(RESTART_INTENT_PREFIX)) continue;
|
|
589
|
+
try {
|
|
590
|
+
const value = record(JSON.parse(text.slice(33)));
|
|
591
|
+
if (value?.resolution_call_id === resolutionCallId && value.service_id === target.service_id && value.pre_generation === target.pre_generation) return true;
|
|
592
|
+
} catch {}
|
|
593
|
+
}
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
596
|
+
function installedProfile(moduleUrl = import.meta.url) {
|
|
597
|
+
let directory = dirname(fileURLToPath(moduleUrl));
|
|
598
|
+
while (true) {
|
|
599
|
+
const manifestPath = join(directory, "package.json");
|
|
600
|
+
if (existsSync(manifestPath)) try {
|
|
601
|
+
if (JSON.parse(readFileSync(manifestPath, "utf8")).name === "dsh-completion-guard") {
|
|
602
|
+
const modules = dirname(directory);
|
|
603
|
+
if (basename(modules) !== "node_modules") return void 0;
|
|
604
|
+
const profilePath = dirname(modules);
|
|
605
|
+
return {
|
|
606
|
+
name: basename(profilePath),
|
|
607
|
+
path: profilePath
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
} catch {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const parent = dirname(directory);
|
|
614
|
+
if (parent === directory) return void 0;
|
|
615
|
+
directory = parent;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function record(value) {
|
|
619
|
+
return value && typeof value === "object" ? value : void 0;
|
|
620
|
+
}
|
|
621
|
+
function stable(value) {
|
|
622
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
623
|
+
if (value && typeof value === "object") return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => `${JSON.stringify(key)}:${stable(entry)}`).join(",")}}`;
|
|
624
|
+
return JSON.stringify(value);
|
|
625
|
+
}
|
|
626
|
+
function digest(value) {
|
|
627
|
+
return createHash("sha256").update(stable(value)).digest("hex");
|
|
628
|
+
}
|
|
629
|
+
function jsonTuple(tuple$1) {
|
|
630
|
+
if (!tuple$1) return {};
|
|
631
|
+
return Object.fromEntries(Object.entries(tuple$1).map(([key, value]) => [key, value]));
|
|
632
|
+
}
|
|
633
|
+
function jsonExpectedTransition(transition) {
|
|
634
|
+
return {
|
|
635
|
+
predicateId: transition.predicateId,
|
|
636
|
+
version: transition.version,
|
|
637
|
+
predParamsKind: transition.predParamsKind,
|
|
638
|
+
parameters: jsonTuple(transition.parameters)
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function tuple(value) {
|
|
642
|
+
return record(value);
|
|
643
|
+
}
|
|
644
|
+
function adapterId(action) {
|
|
645
|
+
if (["create", "modify"].includes(action)) return "context-guard.artifact.v1";
|
|
646
|
+
if (["install", "apply"].includes(action)) return "context-guard.package.v1";
|
|
647
|
+
if (action === "restart") return "context-guard.service.v1";
|
|
648
|
+
if (action === "publish") return "context-guard.registry.v1";
|
|
649
|
+
return "context-guard.git.v1";
|
|
650
|
+
}
|
|
651
|
+
function unavailable(action, role, reason) {
|
|
652
|
+
return {
|
|
653
|
+
status: "unavailable",
|
|
654
|
+
reason_code: reason,
|
|
655
|
+
semantic_action: action,
|
|
656
|
+
evidence_role: role,
|
|
657
|
+
resolved_target: {},
|
|
658
|
+
observed_state: {},
|
|
659
|
+
adapter_id: adapterId(action),
|
|
660
|
+
adapter_version: PRODUCER_VERSION,
|
|
661
|
+
target_digest: "",
|
|
662
|
+
command_manifest_digest: ""
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function supported(action, role, resolved, observed = {}, commandManifest = {}, gitBinding, executable, expectedTransition) {
|
|
666
|
+
return {
|
|
667
|
+
status: "supported",
|
|
668
|
+
reason_code: "producer_observation_supported",
|
|
669
|
+
semantic_action: action,
|
|
670
|
+
evidence_role: role,
|
|
671
|
+
resolved_target: jsonTuple(resolved),
|
|
672
|
+
observed_state: jsonTuple(observed),
|
|
673
|
+
adapter_id: adapterId(action),
|
|
674
|
+
adapter_version: PRODUCER_VERSION,
|
|
675
|
+
target_digest: digest(resolved),
|
|
676
|
+
command_manifest_digest: digest(commandManifest),
|
|
677
|
+
...gitBinding ? { git_binding: gitBinding } : {},
|
|
678
|
+
...executable ? { executable_identity: executable } : {},
|
|
679
|
+
...expectedTransition ? (() => {
|
|
680
|
+
const frozen = jsonExpectedTransition(expectedTransition);
|
|
681
|
+
return {
|
|
682
|
+
expected_transition: frozen,
|
|
683
|
+
expected_transition_digest: digest(frozen)
|
|
684
|
+
};
|
|
685
|
+
})() : {}
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function messageMeta(event) {
|
|
689
|
+
return record(record(event.data)?.meta);
|
|
690
|
+
}
|
|
691
|
+
function producerMeta(event) {
|
|
692
|
+
return record(messageMeta(event)?.contextGuard);
|
|
693
|
+
}
|
|
694
|
+
function eventCallId(event) {
|
|
695
|
+
const data = record(event.data);
|
|
696
|
+
if (event.type === "tool/call") return typeof data?.callId === "string" ? data.callId : void 0;
|
|
697
|
+
const source = record(record(data?.message)?.source);
|
|
698
|
+
return typeof source?.callId === "string" ? source.callId : void 0;
|
|
699
|
+
}
|
|
700
|
+
function findResolution(events, callId, action) {
|
|
701
|
+
let selector;
|
|
702
|
+
let commandManifest;
|
|
703
|
+
for (const raw of events) {
|
|
704
|
+
const event = record(raw);
|
|
705
|
+
if (!event || eventCallId(event) !== callId) continue;
|
|
706
|
+
if (event.type === "tool/call") {
|
|
707
|
+
const data = record(event.data);
|
|
708
|
+
try {
|
|
709
|
+
const args = record(JSON.parse(String(data?.arguments ?? "{}")));
|
|
710
|
+
selector = record(args?.selector);
|
|
711
|
+
commandManifest = record(args?.command_manifest);
|
|
712
|
+
} catch {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
if (event.type !== "tool/result") continue;
|
|
718
|
+
const meta = producerMeta(event);
|
|
719
|
+
if (meta?.semanticAction !== action || meta.evidenceRole !== "resolution" || meta.adapterVersion !== PRODUCER_VERSION || !selector || !commandManifest) return void 0;
|
|
720
|
+
const target = tuple(meta.resolvedTarget);
|
|
721
|
+
const rawBinding = record(meta.gitBinding);
|
|
722
|
+
const rawManifest = record(rawBinding?.manifest);
|
|
723
|
+
const rawPrestate = record(rawBinding?.prestate);
|
|
724
|
+
const rawEnvelope = record(rawBinding?.envelope);
|
|
725
|
+
const rawExecutable = record(meta.executableIdentity);
|
|
726
|
+
const rawExpected = record(meta.expectedTransition);
|
|
727
|
+
const expectedParameters$1 = record(rawExpected?.parameters);
|
|
728
|
+
const expectedTransition = rawExpected && typeof rawExpected.predicateId === "string" && rawExpected.version === 1 && rawExpected.predParamsKind === "inline" && expectedParameters$1 ? rawExpected : void 0;
|
|
729
|
+
const expectedTransitionDigest = typeof meta.expectedTransitionDigest === "string" ? meta.expectedTransitionDigest : void 0;
|
|
730
|
+
const interpreterIdentityValid = rawExecutable && (rawExecutable.interpreterRealpath === void 0 && rawExecutable.interpreterVersion === void 0 || typeof rawExecutable.interpreterRealpath === "string" && typeof rawExecutable.interpreterVersion === "string");
|
|
731
|
+
const executableIdentity$1 = rawExecutable && typeof rawExecutable.executable === "string" && typeof rawExecutable.realpath === "string" && typeof rawExecutable.version === "string" && interpreterIdentityValid ? rawExecutable : void 0;
|
|
732
|
+
const gitBinding = rawManifest && rawPrestate && rawEnvelope ? {
|
|
733
|
+
manifest: rawManifest,
|
|
734
|
+
prestate: Object.fromEntries(Object.entries(rawPrestate).filter((entry) => typeof entry[1] === "string")),
|
|
735
|
+
envelope: rawEnvelope
|
|
736
|
+
} : void 0;
|
|
737
|
+
return target && expectedTransition && expectedTransitionDigest === digest(expectedTransition) ? {
|
|
738
|
+
target,
|
|
739
|
+
selector,
|
|
740
|
+
commandManifest,
|
|
741
|
+
...gitBinding ? { gitBinding } : {},
|
|
742
|
+
...executableIdentity$1 ? { executableIdentity: executableIdentity$1 } : {},
|
|
743
|
+
expectedTransition,
|
|
744
|
+
expectedTransitionDigest
|
|
745
|
+
} : void 0;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function findEffect(events, callId) {
|
|
749
|
+
let call;
|
|
750
|
+
for (const raw of events) {
|
|
751
|
+
const event = record(raw);
|
|
752
|
+
if (!event) continue;
|
|
753
|
+
const seq = typeof event.seq === "number" ? event.seq : -1;
|
|
754
|
+
if (event.type === "tool/call" && eventCallId(event) === callId) {
|
|
755
|
+
const data = record(event.data);
|
|
756
|
+
let args = {};
|
|
757
|
+
try {
|
|
758
|
+
args = record(JSON.parse(String(data?.arguments ?? "{}"))) ?? {};
|
|
759
|
+
} catch {
|
|
760
|
+
args = {};
|
|
761
|
+
}
|
|
762
|
+
call = {
|
|
763
|
+
name: String(data?.name ?? ""),
|
|
764
|
+
arguments: args,
|
|
765
|
+
callSeq: seq,
|
|
766
|
+
resultSeq: -1,
|
|
767
|
+
textContent: ""
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
if (event.type === "tool/result" && eventCallId(event) === callId && call) {
|
|
771
|
+
const data = record(event.data);
|
|
772
|
+
call.resultSeq = seq;
|
|
773
|
+
call.error = data?.error;
|
|
774
|
+
call.meta = data?.meta;
|
|
775
|
+
call.textContent = extractTextContent(record(data?.message)?.content ?? []);
|
|
776
|
+
return call;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
function actionCallMatches(events, callId, action, resolutionCallId, target) {
|
|
781
|
+
for (const raw of events) {
|
|
782
|
+
const event = record(raw);
|
|
783
|
+
if (!event || event.type !== "tool/call" || eventCallId(event) !== callId) continue;
|
|
784
|
+
const data = record(event.data);
|
|
785
|
+
if (data?.name !== ACTION_TOOL) return false;
|
|
786
|
+
try {
|
|
787
|
+
const args = record(JSON.parse(String(data.arguments ?? "{}")));
|
|
788
|
+
return args?.semantic_action === action && args.resolution_call_id === resolutionCallId && args.target_digest === digest(target) && typeof args.contract_item_id === "string" && Number.isSafeInteger(args.contract_item_revision);
|
|
789
|
+
} catch {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
function actionResultCompleted(events, callId) {
|
|
796
|
+
for (const raw of events) {
|
|
797
|
+
const event = record(raw);
|
|
798
|
+
if (!event || event.type !== "tool/result" || eventCallId(event) !== callId) continue;
|
|
799
|
+
return record(messageMeta(event)?.contextGuardAction)?.status === "completed";
|
|
800
|
+
}
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
function plannedEffectDigest(manifest) {
|
|
804
|
+
const name$1 = typeof manifest.planned_tool === "string" ? manifest.planned_tool : void 0;
|
|
805
|
+
const args = record(manifest.planned_arguments);
|
|
806
|
+
return name$1 && args ? digest({
|
|
807
|
+
name: name$1,
|
|
808
|
+
arguments: args
|
|
809
|
+
}) : void 0;
|
|
810
|
+
}
|
|
811
|
+
function plannedEffect(manifest) {
|
|
812
|
+
const name$1 = typeof manifest.planned_tool === "string" ? manifest.planned_tool : void 0;
|
|
813
|
+
const args = record(manifest.planned_arguments);
|
|
814
|
+
return name$1 && args ? {
|
|
815
|
+
name: name$1,
|
|
816
|
+
arguments: args,
|
|
817
|
+
digest: digest({
|
|
818
|
+
name: name$1,
|
|
819
|
+
arguments: args
|
|
820
|
+
})
|
|
821
|
+
} : void 0;
|
|
822
|
+
}
|
|
823
|
+
function plannedGitManifest(action, selector, planned) {
|
|
824
|
+
if (planned.name !== "bash" && planned.name !== "pwsh" || !hasExactKeys(planned.arguments, ["command", "workdir"])) return void 0;
|
|
825
|
+
const repository = requireString(selector, "repository");
|
|
826
|
+
const command = requireString(planned.arguments, "command");
|
|
827
|
+
if (!repository || planned.arguments.workdir !== repository || !command) return void 0;
|
|
828
|
+
const parsed = parseGitCommandManifest(command, planned.name);
|
|
829
|
+
if (parsed.status !== "accepted" || parsed.manifest.action !== action) return void 0;
|
|
830
|
+
const remote = requireString(selector, "remote");
|
|
831
|
+
const refspec = requireString(selector, "refspec");
|
|
832
|
+
if (!gitCommandMatchesTarget(parsed.manifest, {
|
|
833
|
+
repository,
|
|
834
|
+
...remote ? { remote } : {},
|
|
835
|
+
...refspec ? { refspec } : {}
|
|
836
|
+
})) return void 0;
|
|
837
|
+
return parsed.manifest;
|
|
838
|
+
}
|
|
839
|
+
function effectDigest(effect) {
|
|
840
|
+
return digest({
|
|
841
|
+
name: effect.name,
|
|
842
|
+
arguments: effect.arguments
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
function cwdOf(agent) {
|
|
846
|
+
const header = record(agent.session.header);
|
|
847
|
+
return typeof header?.cwd === "string" ? header.cwd : void 0;
|
|
848
|
+
}
|
|
849
|
+
function pathOf(selector, cwd) {
|
|
850
|
+
const raw = typeof selector.artifact_id === "string" ? selector.artifact_id : void 0;
|
|
851
|
+
if (!raw) return void 0;
|
|
852
|
+
return isAbsolute(raw) ? raw : cwd ? resolve(cwd, raw) : void 0;
|
|
853
|
+
}
|
|
854
|
+
async function fileDigest(path) {
|
|
855
|
+
try {
|
|
856
|
+
return createHash("sha256").update(await readFile(path)).digest("hex");
|
|
857
|
+
} catch (error) {
|
|
858
|
+
if (error.code === "ENOENT") return "absent";
|
|
859
|
+
throw error;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
async function readJson(path) {
|
|
863
|
+
try {
|
|
864
|
+
return record(JSON.parse(await readFile(path, "utf8")));
|
|
865
|
+
} catch {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
async function tgzIdentity(path) {
|
|
870
|
+
if (!isAbsolute(path) || !path.endsWith(".tgz")) return void 0;
|
|
871
|
+
const bytes = await readFile(path);
|
|
872
|
+
if (bytes.length === 0 || bytes.length > 128 * 1024 * 1024) return void 0;
|
|
873
|
+
const tar = await promisify(gunzip)(bytes);
|
|
874
|
+
let manifest;
|
|
875
|
+
for (let offset = 0; offset + 512 <= tar.length;) {
|
|
876
|
+
const header = tar.subarray(offset, offset + 512);
|
|
877
|
+
if (header.every((byte) => byte === 0)) break;
|
|
878
|
+
const name$1 = header.subarray(0, 100).toString("utf8").replace(/\0.*$/, "");
|
|
879
|
+
const sizeText = header.subarray(124, 136).toString("ascii").replace(/\0.*$/, "").trim();
|
|
880
|
+
if (!/^[0-7]+$/.test(sizeText)) return void 0;
|
|
881
|
+
const size = Number.parseInt(sizeText, 8);
|
|
882
|
+
const bodyStart = offset + 512;
|
|
883
|
+
const bodyEnd = bodyStart + size;
|
|
884
|
+
if (!Number.isSafeInteger(size) || size < 0 || bodyEnd > tar.length) return void 0;
|
|
885
|
+
if (name$1 === "package/package.json") {
|
|
886
|
+
if (manifest || size > 1024 * 1024) return void 0;
|
|
887
|
+
try {
|
|
888
|
+
manifest = record(JSON.parse(tar.subarray(bodyStart, bodyEnd).toString("utf8")));
|
|
889
|
+
} catch {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
offset = bodyStart + Math.ceil(size / 512) * 512;
|
|
894
|
+
}
|
|
895
|
+
if (typeof manifest?.name !== "string" || typeof manifest.version !== "string") return void 0;
|
|
896
|
+
return {
|
|
897
|
+
name: manifest.name,
|
|
898
|
+
version: manifest.version,
|
|
899
|
+
integrity: `sha512-${createHash("sha512").update(bytes).digest("base64")}`
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
const DSHMARKET_VERSION = "1.36.0";
|
|
903
|
+
const DSHMARKET_INTEGRITY = "sha512-xX8CCoXdIALaxtLosj+5qGg8r1cykW2zo1AOPJcSQepg2r4Vd2K0NmERldDqfeyFV0pCuZsUoAPe1Q/BW7De/g==";
|
|
904
|
+
const MARKET_SCHEMA = "dsh-market/update-api/v1";
|
|
905
|
+
async function marketCapabilities(roots, signal) {
|
|
906
|
+
if (!roots.profile || !roots.marketOrigin) return void 0;
|
|
907
|
+
const installed = await profilePackage(roots.profile.path, "dshmarket");
|
|
908
|
+
if (installed?.version !== DSHMARKET_VERSION || installed.integrity !== DSHMARKET_INTEGRITY) return void 0;
|
|
909
|
+
const response = await (roots.fetcher ?? fetch)(`${roots.marketOrigin}/dsh-market/api/v1/capabilities`, {
|
|
910
|
+
signal,
|
|
911
|
+
headers: { accept: "application/json" },
|
|
912
|
+
redirect: "error"
|
|
913
|
+
});
|
|
914
|
+
if (!response.ok) return void 0;
|
|
915
|
+
const value = record(await response.json());
|
|
916
|
+
if (value?.schema !== MARKET_SCHEMA || value.apiVersion !== 1 || value.marketVersion !== DSHMARKET_VERSION || value.profile !== roots.profile.name || typeof value.bootId !== "string") return void 0;
|
|
917
|
+
return value;
|
|
918
|
+
}
|
|
919
|
+
function importerLocator(text, packageId) {
|
|
920
|
+
const escaped = packageId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
921
|
+
const lines = text.split(/\r?\n/);
|
|
922
|
+
const start = lines.findIndex((line) => (/* @__PURE__ */ new RegExp(`^ ['"]?${escaped}['"]?:\\s*$`)).test(line));
|
|
923
|
+
if (start < 0) return void 0;
|
|
924
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
925
|
+
if (/^ \S/.test(lines[index])) break;
|
|
926
|
+
const match = /^ version:\s*(.+?)\s*$/.exec(lines[index]);
|
|
927
|
+
if (match) return match[1].replace(/^['"]|['"]$/g, "");
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
function lockIntegrity(text, packageId, locator) {
|
|
931
|
+
const escaped = `${packageId}@${locator}`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
932
|
+
const lines = text.split(/\r?\n/);
|
|
933
|
+
const start = lines.findIndex((line) => (/* @__PURE__ */ new RegExp(`^ ['"]?${escaped}['"]?:\\s*$`)).test(line));
|
|
934
|
+
if (start < 0) return void 0;
|
|
935
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
936
|
+
if (/^ \S/.test(lines[index])) break;
|
|
937
|
+
const match = /^ resolution: \{integrity: ([^}]+)\}\s*$/.exec(lines[index]);
|
|
938
|
+
if (match) return match[1];
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
async function profilePackage(profilePath, packageId) {
|
|
942
|
+
const manifest = await readJson(resolve(profilePath, "node_modules", ...packageId.split("/"), "package.json"));
|
|
943
|
+
if (manifest?.name !== packageId || typeof manifest.version !== "string") return void 0;
|
|
944
|
+
try {
|
|
945
|
+
const lock = await readFile(resolve(profilePath, "pnpm-lock.yaml"), "utf8");
|
|
946
|
+
const locator = importerLocator(lock, packageId);
|
|
947
|
+
const integrity = locator ? lockIntegrity(lock, packageId, locator) : void 0;
|
|
948
|
+
return integrity ? {
|
|
949
|
+
version: manifest.version,
|
|
950
|
+
integrity
|
|
951
|
+
} : void 0;
|
|
952
|
+
} catch {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
async function registryIntegrity(registry, packageId, version, roots, signal) {
|
|
957
|
+
const canonical = canonicalRegistryBase(registry, { allowLoopbackHttp: roots.allowLoopbackHttpRegistry });
|
|
958
|
+
if (!canonical || canonical !== registry) return void 0;
|
|
959
|
+
const response = await (roots.fetcher ?? fetch)(new URL(npmEscapedPackageName(packageId), canonical), {
|
|
960
|
+
signal,
|
|
961
|
+
headers: { accept: "application/json" },
|
|
962
|
+
redirect: "error"
|
|
963
|
+
});
|
|
964
|
+
if (!response.ok) return void 0;
|
|
965
|
+
const value = record(await response.json());
|
|
966
|
+
const release = record(record(value?.versions)?.[version]);
|
|
967
|
+
const dist = record(release?.dist);
|
|
968
|
+
return value?.name === packageId && release?.name === packageId && release.version === version && typeof dist?.integrity === "string" ? dist.integrity : void 0;
|
|
969
|
+
}
|
|
970
|
+
function windowsBatchCommand(file, args) {
|
|
971
|
+
const values = [file, ...args];
|
|
972
|
+
if (values.some((value) => /[\0\r\n"%!^&|<>]/.test(value))) return void 0;
|
|
973
|
+
return values.map((value) => `"${value}"`).join(" ");
|
|
974
|
+
}
|
|
975
|
+
async function windowsCommandInterpreter(signal) {
|
|
976
|
+
const configured = process.env.ComSpec;
|
|
977
|
+
const systemRoot = process.env.SystemRoot;
|
|
978
|
+
if (!configured || !systemRoot || !isAbsolute(configured) || !isAbsolute(systemRoot) || basename(configured).toLowerCase() !== "cmd.exe" || /[\0\r\n"%!^&|<>]/.test(configured) || /[\0\r\n"%!^&|<>]/.test(systemRoot)) return void 0;
|
|
979
|
+
try {
|
|
980
|
+
const interpreterRealpath = await realpath(configured);
|
|
981
|
+
const systemInterpreter = await realpath(resolve(systemRoot, "System32", "cmd.exe"));
|
|
982
|
+
if (basename(interpreterRealpath).toLowerCase() !== "cmd.exe" || interpreterRealpath.toLowerCase() !== systemInterpreter.toLowerCase()) return void 0;
|
|
983
|
+
const { stdout, stderr } = await execFileAsync(interpreterRealpath, [
|
|
984
|
+
"/d",
|
|
985
|
+
"/v:off",
|
|
986
|
+
"/c",
|
|
987
|
+
"ver"
|
|
988
|
+
], {
|
|
989
|
+
encoding: "utf8",
|
|
990
|
+
signal,
|
|
991
|
+
windowsHide: true
|
|
992
|
+
});
|
|
993
|
+
const interpreterVersion = `${stdout}${stderr}`.trim();
|
|
994
|
+
return interpreterVersion && !/[\0\r\n]/.test(interpreterVersion) ? {
|
|
995
|
+
interpreterRealpath,
|
|
996
|
+
interpreterVersion
|
|
997
|
+
} : void 0;
|
|
998
|
+
} catch {
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
async function execAuditedFile(file, args, options, interpreter) {
|
|
1003
|
+
if (process.platform === "win32" && /\.(?:cmd|bat)$/i.test(file)) {
|
|
1004
|
+
const command = windowsBatchCommand(file, args);
|
|
1005
|
+
if (!command || !interpreter) throw new Error("unsafe Windows batch invocation");
|
|
1006
|
+
return execFileAsync(interpreter.interpreterRealpath, [
|
|
1007
|
+
"/d",
|
|
1008
|
+
"/v:off",
|
|
1009
|
+
"/s",
|
|
1010
|
+
"/c",
|
|
1011
|
+
`"${command}"`
|
|
1012
|
+
], {
|
|
1013
|
+
...options,
|
|
1014
|
+
encoding: "utf8",
|
|
1015
|
+
windowsHide: true,
|
|
1016
|
+
windowsVerbatimArguments: true
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
return execFileAsync(file, args, {
|
|
1020
|
+
...options,
|
|
1021
|
+
encoding: "utf8"
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
async function runCommand(roots, identity, args, cwd, signal) {
|
|
1025
|
+
if (roots.commandRunner) return roots.commandRunner(identity.executable, args, cwd, signal);
|
|
1026
|
+
const interpreter = identity.interpreterRealpath && identity.interpreterVersion ? {
|
|
1027
|
+
interpreterRealpath: identity.interpreterRealpath,
|
|
1028
|
+
interpreterVersion: identity.interpreterVersion
|
|
1029
|
+
} : void 0;
|
|
1030
|
+
await execAuditedFile(identity.realpath, args, {
|
|
1031
|
+
...cwd ? { cwd } : {},
|
|
1032
|
+
signal,
|
|
1033
|
+
env: {
|
|
1034
|
+
...process.env,
|
|
1035
|
+
npm_config_cache: join(tmpdir(), "dsh-completion-guard-npm-cache")
|
|
1036
|
+
}
|
|
1037
|
+
}, interpreter);
|
|
1038
|
+
}
|
|
1039
|
+
function executableFor(action) {
|
|
1040
|
+
if ([
|
|
1041
|
+
"commit",
|
|
1042
|
+
"push",
|
|
1043
|
+
"pull",
|
|
1044
|
+
"fetch"
|
|
1045
|
+
].includes(action)) return "git";
|
|
1046
|
+
if (action === "install" || action === "apply") return "dsh";
|
|
1047
|
+
if (action === "publish") return "npm";
|
|
1048
|
+
}
|
|
1049
|
+
async function executableIdentity(executable, signal) {
|
|
1050
|
+
const suffixes = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
1051
|
+
for (const directory of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) for (const suffix of suffixes) {
|
|
1052
|
+
const candidate = resolve(directory, `${executable}${suffix}`);
|
|
1053
|
+
try {
|
|
1054
|
+
await access(candidate, constants.X_OK);
|
|
1055
|
+
const canonical = await realpath(candidate);
|
|
1056
|
+
const interpreter = process.platform === "win32" && /\.(?:cmd|bat)$/i.test(canonical) ? await windowsCommandInterpreter(signal) : void 0;
|
|
1057
|
+
if (process.platform === "win32" && /\.(?:cmd|bat)$/i.test(canonical) && !interpreter) continue;
|
|
1058
|
+
const { stdout, stderr } = await execAuditedFile(canonical, ["--version"], { signal }, interpreter);
|
|
1059
|
+
const version = `${stdout}${stderr}`.trim();
|
|
1060
|
+
if (version && !version.includes("\n") && !version.includes("\r")) return {
|
|
1061
|
+
executable,
|
|
1062
|
+
realpath: canonical,
|
|
1063
|
+
version,
|
|
1064
|
+
...interpreter
|
|
1065
|
+
};
|
|
1066
|
+
} catch {}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
async function executeGuardAction(action, resolution, roots, signal, executableIdentity$1, resolutionCallId, agent) {
|
|
1070
|
+
const target = resolution.target;
|
|
1071
|
+
if (action === "install" || action === "apply") {
|
|
1072
|
+
const tgzPath = requireString(resolution.commandManifest, "tgz_path");
|
|
1073
|
+
const identity = tgzPath ? await tgzIdentity(tgzPath) : void 0;
|
|
1074
|
+
if (!executableIdentity$1 || !tgzPath || !identity || identity.name !== target.package_id || identity.version !== target.version || identity.integrity !== target.integrity_digest || roots.profile?.name !== target.profile) return "unavailable";
|
|
1075
|
+
await runCommand(roots, executableIdentity$1, [
|
|
1076
|
+
"plugin",
|
|
1077
|
+
"--profile",
|
|
1078
|
+
roots.profile.name,
|
|
1079
|
+
"add",
|
|
1080
|
+
`file:${tgzPath}`
|
|
1081
|
+
], void 0, signal);
|
|
1082
|
+
return "completed";
|
|
1083
|
+
}
|
|
1084
|
+
if (action === "publish") {
|
|
1085
|
+
const tgzPath = requireString(resolution.commandManifest, "tgz_path");
|
|
1086
|
+
const identity = tgzPath ? await tgzIdentity(tgzPath) : void 0;
|
|
1087
|
+
const registry = typeof target.registry === "string" ? canonicalRegistryBase(target.registry, { allowLoopbackHttp: roots.allowLoopbackHttpRegistry }) : void 0;
|
|
1088
|
+
if (!executableIdentity$1 || !tgzPath || !identity || identity.name !== target.artifact_id || identity.version !== target.version || identity.integrity !== target.integrity_digest || !registry || registry !== target.registry) return "unavailable";
|
|
1089
|
+
await runCommand(roots, executableIdentity$1, [
|
|
1090
|
+
"publish",
|
|
1091
|
+
tgzPath,
|
|
1092
|
+
"--registry",
|
|
1093
|
+
registry,
|
|
1094
|
+
"--ignore-scripts"
|
|
1095
|
+
], void 0, signal);
|
|
1096
|
+
return "completed";
|
|
1097
|
+
}
|
|
1098
|
+
if (action === "restart") {
|
|
1099
|
+
const capabilities = await marketCapabilities(roots, signal);
|
|
1100
|
+
if (!capabilities || !roots.marketOrigin || !resolutionCallId || !agent) return "unavailable";
|
|
1101
|
+
if (capabilities.bootId !== target.pre_generation) return restartIntent(agent.session.events, resolutionCallId, target) ? "completed" : "unavailable";
|
|
1102
|
+
if (restartIntent(agent.session.events, resolutionCallId, target)) return "handoff_pending";
|
|
1103
|
+
if (!roots.persistRestartIntent || !await roots.persistRestartIntent(agent, {
|
|
1104
|
+
resolutionCallId,
|
|
1105
|
+
serviceId: String(target.service_id),
|
|
1106
|
+
preGeneration: String(target.pre_generation)
|
|
1107
|
+
})) return "unavailable";
|
|
1108
|
+
const response = await (roots.fetcher ?? fetch)(`${roots.marketOrigin}/dsh-market/api/v1/restart`, {
|
|
1109
|
+
method: "POST",
|
|
1110
|
+
signal,
|
|
1111
|
+
redirect: "error",
|
|
1112
|
+
headers: {
|
|
1113
|
+
accept: "application/json",
|
|
1114
|
+
"content-type": "application/json",
|
|
1115
|
+
origin: roots.marketOrigin
|
|
1116
|
+
},
|
|
1117
|
+
body: "{}"
|
|
1118
|
+
});
|
|
1119
|
+
if (!response.ok) return "unavailable";
|
|
1120
|
+
return record(await response.json())?.schema === MARKET_SCHEMA ? "handoff_pending" : "unavailable";
|
|
1121
|
+
}
|
|
1122
|
+
if (action === "commit" || action === "push" || action === "pull" || action === "fetch") {
|
|
1123
|
+
const binding = resolution.gitBinding;
|
|
1124
|
+
const repository = requireString(resolution.target, "repository");
|
|
1125
|
+
if (!executableIdentity$1 || !binding || !repository) return "unavailable";
|
|
1126
|
+
const targetIdentity = {
|
|
1127
|
+
repository,
|
|
1128
|
+
...typeof resolution.target.remote === "string" ? { remote: resolution.target.remote } : {},
|
|
1129
|
+
...typeof resolution.target.refspec === "string" ? { refspec: resolution.target.refspec } : {}
|
|
1130
|
+
};
|
|
1131
|
+
const current = await gitPrestate(binding.manifest, repository, signal, executableIdentity$1.realpath);
|
|
1132
|
+
if (!current) return "unavailable";
|
|
1133
|
+
if ((await executeRevalidatedGitEffect(binding.envelope, binding.manifest, targetIdentity, current, async (_file, argv, workingDirectory) => runCommand(roots, executableIdentity$1, argv, workingDirectory, signal))).status !== "executed") return "unavailable";
|
|
1134
|
+
if (action === "commit") {
|
|
1135
|
+
const commit = verifiedLinearCommitReadback(await gitBytes(repository, [
|
|
1136
|
+
"rev-list",
|
|
1137
|
+
"--parents",
|
|
1138
|
+
"-n",
|
|
1139
|
+
"1",
|
|
1140
|
+
"HEAD"
|
|
1141
|
+
], signal, executableIdentity$1.realpath), String(resolution.target.pre_head_oid ?? ""));
|
|
1142
|
+
const postTree = commit ? commitTreeSnapshotDigest(await gitBytes(repository, [
|
|
1143
|
+
"ls-tree",
|
|
1144
|
+
"-r",
|
|
1145
|
+
"-z",
|
|
1146
|
+
commit.postHeadOid
|
|
1147
|
+
], signal, executableIdentity$1.realpath)) : void 0;
|
|
1148
|
+
if (!commit || postTree !== resolution.target.change_set_digest) return "unavailable";
|
|
1149
|
+
} else if (action === "push") {
|
|
1150
|
+
const remoteOid = binding.manifest.remote && binding.manifest.destinationRef ? await exactRemoteOid(repository, binding.manifest.remote, binding.manifest.destinationRef, signal, executableIdentity$1.realpath) : void 0;
|
|
1151
|
+
if (!remoteOid || remoteOid !== resolution.target.local_oid) return "unavailable";
|
|
1152
|
+
} else {
|
|
1153
|
+
const upstream = String(resolution.target.upstream_oid ?? "");
|
|
1154
|
+
const trackingRef = binding.manifest.action === "fetch" ? binding.manifest.trackingRef : binding.manifest.remote && binding.manifest.sourceRef ? `refs/remotes/${binding.manifest.remote}/${binding.manifest.sourceRef.slice(11)}` : void 0;
|
|
1155
|
+
const tracking = trackingRef ? oid(await git(repository, [
|
|
1156
|
+
"rev-parse",
|
|
1157
|
+
"--verify",
|
|
1158
|
+
trackingRef
|
|
1159
|
+
], signal, executableIdentity$1.realpath)) : void 0;
|
|
1160
|
+
if (!tracking || tracking !== upstream) return "unavailable";
|
|
1161
|
+
if (action === "pull" && oid(await git(repository, ["rev-parse", "HEAD"], signal, executableIdentity$1.realpath)) !== upstream) return "unavailable";
|
|
1162
|
+
if (action === "fetch" && oid(await git(repository, ["rev-parse", "HEAD"], signal, executableIdentity$1.realpath)) !== resolution.target.pre_head_oid) return "unavailable";
|
|
1163
|
+
}
|
|
1164
|
+
return "completed";
|
|
1165
|
+
}
|
|
1166
|
+
return "unavailable";
|
|
1167
|
+
}
|
|
1168
|
+
async function gitBytes(repository, args, signal, file = "git") {
|
|
1169
|
+
const { stdout } = await execFileAsync(file, args, {
|
|
1170
|
+
cwd: repository,
|
|
1171
|
+
encoding: "buffer",
|
|
1172
|
+
signal,
|
|
1173
|
+
env: {
|
|
1174
|
+
...process.env,
|
|
1175
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
return Buffer.from(stdout);
|
|
1179
|
+
}
|
|
1180
|
+
async function git(repository, args, signal, file = "git") {
|
|
1181
|
+
return (await gitBytes(repository, args, signal, file)).toString("utf8").replace(/[\r\n]+$/, "");
|
|
1182
|
+
}
|
|
1183
|
+
function oid(value) {
|
|
1184
|
+
return /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/i.test(value) ? value.toLowerCase() : void 0;
|
|
1185
|
+
}
|
|
1186
|
+
async function exactRemoteOid(repository, remote, ref, signal, file = "git") {
|
|
1187
|
+
try {
|
|
1188
|
+
const line = await git(repository, [
|
|
1189
|
+
"ls-remote",
|
|
1190
|
+
"--exit-code",
|
|
1191
|
+
"--refs",
|
|
1192
|
+
remote,
|
|
1193
|
+
ref
|
|
1194
|
+
], signal, file);
|
|
1195
|
+
const match = /^([0-9a-f]{40}(?:[0-9a-f]{24})?)\t([^\r\n]+)$/i.exec(line);
|
|
1196
|
+
return match && match[2] === ref ? match[1].toLowerCase() : void 0;
|
|
1197
|
+
} catch {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
async function gitPrestate(manifest, repository, signal, file = "git") {
|
|
1202
|
+
if (manifest.action === "commit") {
|
|
1203
|
+
const head = oid(await git(repository, ["rev-parse", "HEAD"], signal, file));
|
|
1204
|
+
const branch = await git(repository, [
|
|
1205
|
+
"symbolic-ref",
|
|
1206
|
+
"--quiet",
|
|
1207
|
+
"HEAD"
|
|
1208
|
+
], signal, file);
|
|
1209
|
+
const index = commitIndexSnapshotDigest(await gitBytes(repository, [
|
|
1210
|
+
"ls-files",
|
|
1211
|
+
"--stage",
|
|
1212
|
+
"-z"
|
|
1213
|
+
], signal, file));
|
|
1214
|
+
return head && branch.startsWith("refs/heads/") && index ? {
|
|
1215
|
+
pre_head_oid: head,
|
|
1216
|
+
branch,
|
|
1217
|
+
index_digest: index
|
|
1218
|
+
} : void 0;
|
|
1219
|
+
}
|
|
1220
|
+
if (!manifest.remote || !manifest.sourceRef) return void 0;
|
|
1221
|
+
const upstream = await exactRemoteOid(repository, manifest.remote, manifest.sourceRef, signal, file);
|
|
1222
|
+
if (!upstream) return void 0;
|
|
1223
|
+
if (manifest.action === "push") {
|
|
1224
|
+
if (!manifest.destinationRef) return void 0;
|
|
1225
|
+
const source = oid(await git(repository, [
|
|
1226
|
+
"rev-parse",
|
|
1227
|
+
"--verify",
|
|
1228
|
+
manifest.sourceRef
|
|
1229
|
+
], signal, file));
|
|
1230
|
+
const destination = await exactRemoteOid(repository, manifest.remote, manifest.destinationRef, signal, file);
|
|
1231
|
+
return source && destination ? {
|
|
1232
|
+
source_oid: source,
|
|
1233
|
+
destination_oid: destination
|
|
1234
|
+
} : void 0;
|
|
1235
|
+
}
|
|
1236
|
+
if (manifest.action === "fetch") {
|
|
1237
|
+
if (!manifest.trackingRef) return void 0;
|
|
1238
|
+
const head = oid(await git(repository, ["rev-parse", "HEAD"], signal, file));
|
|
1239
|
+
let tracking = "absent";
|
|
1240
|
+
try {
|
|
1241
|
+
tracking = oid(await git(repository, [
|
|
1242
|
+
"rev-parse",
|
|
1243
|
+
"--verify",
|
|
1244
|
+
manifest.trackingRef
|
|
1245
|
+
], signal, file)) ?? "absent";
|
|
1246
|
+
} catch {}
|
|
1247
|
+
return head ? {
|
|
1248
|
+
upstream_oid: upstream,
|
|
1249
|
+
pre_head_oid: head,
|
|
1250
|
+
tracking_oid: tracking
|
|
1251
|
+
} : void 0;
|
|
1252
|
+
}
|
|
1253
|
+
if (manifest.action === "pull") {
|
|
1254
|
+
const head = oid(await git(repository, ["rev-parse", "HEAD"], signal, file));
|
|
1255
|
+
const trackingRef = `refs/remotes/${manifest.remote}/${manifest.sourceRef.slice(11)}`;
|
|
1256
|
+
let tracking = "absent";
|
|
1257
|
+
try {
|
|
1258
|
+
tracking = oid(await git(repository, [
|
|
1259
|
+
"rev-parse",
|
|
1260
|
+
"--verify",
|
|
1261
|
+
trackingRef
|
|
1262
|
+
], signal, file)) ?? "absent";
|
|
1263
|
+
} catch {}
|
|
1264
|
+
return head ? {
|
|
1265
|
+
upstream_oid: upstream,
|
|
1266
|
+
pre_head_oid: head,
|
|
1267
|
+
tracking_oid: tracking
|
|
1268
|
+
} : void 0;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
function requireString(row, key) {
|
|
1272
|
+
return typeof row[key] === "string" && row[key] ? row[key] : void 0;
|
|
1273
|
+
}
|
|
1274
|
+
function hasExactKeys(row, keys) {
|
|
1275
|
+
const actual = Object.keys(row).sort();
|
|
1276
|
+
const expected = [...keys].sort();
|
|
1277
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
1278
|
+
}
|
|
1279
|
+
function selectorHasOnlyCanonicalKeys(action, selector) {
|
|
1280
|
+
const allowed = new Set(ACTION_MANIFEST.actions[action].resolvedTargetKeys);
|
|
1281
|
+
return Object.keys(selector).every((key) => allowed.has(key));
|
|
1282
|
+
}
|
|
1283
|
+
function pickTarget(target, keys) {
|
|
1284
|
+
return Object.fromEntries(keys.filter((key) => Object.hasOwn(target, key)).map((key) => [key, target[key]]));
|
|
1285
|
+
}
|
|
1286
|
+
async function expectedTransitionForResolution(action, target, commandManifest) {
|
|
1287
|
+
let parameters;
|
|
1288
|
+
if (action === "create") {
|
|
1289
|
+
const plannedArguments = record(commandManifest.planned_arguments);
|
|
1290
|
+
const content = typeof plannedArguments?.content === "string" ? plannedArguments.content : void 0;
|
|
1291
|
+
if (content === void 0) return void 0;
|
|
1292
|
+
parameters = { post_digest: createHash("sha256").update(Buffer.from(content, "utf8")).digest("hex") };
|
|
1293
|
+
} else if (action === "modify") {
|
|
1294
|
+
const plannedArguments = record(commandManifest.planned_arguments);
|
|
1295
|
+
const oldString = requireString(plannedArguments ?? {}, "old_string");
|
|
1296
|
+
const newString = typeof plannedArguments?.new_string === "string" ? plannedArguments.new_string : void 0;
|
|
1297
|
+
const artifact = typeof target.artifact_id === "string" ? target.artifact_id : void 0;
|
|
1298
|
+
if (!artifact || !oldString || newString === void 0) return void 0;
|
|
1299
|
+
let before;
|
|
1300
|
+
try {
|
|
1301
|
+
const beforeBytes = await readFile(artifact);
|
|
1302
|
+
const frozenPreDigest = typeof target.pre_digest === "string" ? target.pre_digest : void 0;
|
|
1303
|
+
if (!frozenPreDigest || createHash("sha256").update(beforeBytes).digest("hex") !== frozenPreDigest) return void 0;
|
|
1304
|
+
before = new TextDecoder("utf-8", { fatal: true }).decode(beforeBytes);
|
|
1305
|
+
} catch {
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
const first = before.indexOf(oldString);
|
|
1309
|
+
if (first < 0 || before.indexOf(oldString, first + oldString.length) >= 0) return void 0;
|
|
1310
|
+
const after = `${before.slice(0, first)}${newString}${before.slice(first + oldString.length)}`;
|
|
1311
|
+
parameters = { post_digest: createHash("sha256").update(Buffer.from(after, "utf8")).digest("hex") };
|
|
1312
|
+
} else if (action === "install" || action === "apply") parameters = pickTarget(target, [
|
|
1313
|
+
"package_id",
|
|
1314
|
+
"version",
|
|
1315
|
+
"integrity_digest",
|
|
1316
|
+
"profile"
|
|
1317
|
+
]);
|
|
1318
|
+
else if (action === "publish") parameters = pickTarget(target, [
|
|
1319
|
+
"artifact_id",
|
|
1320
|
+
"version",
|
|
1321
|
+
"registry",
|
|
1322
|
+
"integrity_digest"
|
|
1323
|
+
]);
|
|
1324
|
+
else if (action === "restart") parameters = {
|
|
1325
|
+
...pickTarget(target, ["pre_generation"]),
|
|
1326
|
+
health: "healthy"
|
|
1327
|
+
};
|
|
1328
|
+
else if (action === "commit") parameters = pickTarget(target, ["pre_head_oid", "change_set_digest"]);
|
|
1329
|
+
else if (action === "push") parameters = pickTarget(target, ["local_oid"]);
|
|
1330
|
+
else if (action === "pull") parameters = pickTarget(target, [
|
|
1331
|
+
"pull_mode",
|
|
1332
|
+
"upstream_oid",
|
|
1333
|
+
"pre_head_oid"
|
|
1334
|
+
]);
|
|
1335
|
+
else if (action === "fetch") parameters = pickTarget(target, ["upstream_oid", "pre_head_oid"]);
|
|
1336
|
+
else return;
|
|
1337
|
+
return {
|
|
1338
|
+
predicateId: ACTION_MANIFEST.actions[action].predicateId,
|
|
1339
|
+
version: 1,
|
|
1340
|
+
predParamsKind: "inline",
|
|
1341
|
+
parameters
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
async function resolveTarget(action, selector, commandManifest, cwd, roots, signal, executableIdentity$1) {
|
|
1345
|
+
if (!selectorHasOnlyCanonicalKeys(action, selector)) return void 0;
|
|
1346
|
+
const planned = plannedEffectDigest(commandManifest);
|
|
1347
|
+
const plannedCall = plannedEffect(commandManifest);
|
|
1348
|
+
if (action === "install" || action === "apply") {
|
|
1349
|
+
const packageId = requireString(selector, "package_id");
|
|
1350
|
+
const profile = requireString(selector, "profile");
|
|
1351
|
+
const trustedProfile = roots.profile;
|
|
1352
|
+
const tgzPath = requireString(commandManifest, "tgz_path");
|
|
1353
|
+
const manifestId = action === "install" ? "dsh.plugin_add_tgz.install.v1" : "dsh.plugin_add_tgz.apply.v1";
|
|
1354
|
+
if (!hasExactKeys(commandManifest, ["manifest_id", "tgz_path"]) || !packageId || !profile || profile !== trustedProfile?.name || commandManifest.manifest_id !== manifestId || !ACTION_MANIFEST.actions[action].commandManifestIds.includes(manifestId) || !tgzPath) return void 0;
|
|
1355
|
+
const identity = await tgzIdentity(tgzPath);
|
|
1356
|
+
if (!identity || identity.name !== packageId || selector.version !== void 0 && selector.version !== identity.version) return void 0;
|
|
1357
|
+
const installed = await profilePackage(trustedProfile.path, packageId);
|
|
1358
|
+
if (action === "install" && installed) return void 0;
|
|
1359
|
+
if (action === "apply" && (!installed || installed.version === identity.version && installed.integrity === identity.integrity)) return void 0;
|
|
1360
|
+
return { target: {
|
|
1361
|
+
package_id: packageId,
|
|
1362
|
+
version: identity.version,
|
|
1363
|
+
integrity_digest: identity.integrity,
|
|
1364
|
+
profile
|
|
1365
|
+
} };
|
|
1366
|
+
}
|
|
1367
|
+
if (action === "restart") {
|
|
1368
|
+
const service = requireString(selector, "service_id");
|
|
1369
|
+
if (service !== "dsh-web" || commandManifest.manifest_id !== "dshmarket.restart.v1" || !ACTION_MANIFEST.actions.restart.commandManifestIds.includes("dshmarket.restart.v1") || Object.keys(commandManifest).length !== 1) return void 0;
|
|
1370
|
+
const capabilities = await marketCapabilities(roots, signal);
|
|
1371
|
+
const features = record(capabilities?.features);
|
|
1372
|
+
const restart = record(capabilities?.restart);
|
|
1373
|
+
if (features?.restart !== true || restart?.supported !== true || restart.managedBy !== "market") return void 0;
|
|
1374
|
+
return { target: {
|
|
1375
|
+
service_id: service,
|
|
1376
|
+
pre_generation: String(capabilities?.bootId)
|
|
1377
|
+
} };
|
|
1378
|
+
}
|
|
1379
|
+
if (action === "publish") {
|
|
1380
|
+
const registry = canonicalRegistryBase(requireString(selector, "registry") ?? "", { allowLoopbackHttp: roots.allowLoopbackHttpRegistry });
|
|
1381
|
+
const tgzPath = requireString(commandManifest, "tgz_path");
|
|
1382
|
+
if (!hasExactKeys(commandManifest, ["manifest_id", "tgz_path"]) || !registry || commandManifest.manifest_id !== "npm.publish_tgz.v1" || !ACTION_MANIFEST.actions.publish.commandManifestIds.includes("npm.publish_tgz.v1") || !tgzPath) return void 0;
|
|
1383
|
+
const identity = await tgzIdentity(tgzPath);
|
|
1384
|
+
if (!identity || identity.name !== selector.artifact_id || identity.version !== selector.version) return void 0;
|
|
1385
|
+
return { target: {
|
|
1386
|
+
artifact_id: identity.name,
|
|
1387
|
+
version: identity.version,
|
|
1388
|
+
registry,
|
|
1389
|
+
integrity_digest: identity.integrity
|
|
1390
|
+
} };
|
|
1391
|
+
}
|
|
1392
|
+
if (action === "create" || action === "modify") {
|
|
1393
|
+
if (!hasExactKeys(commandManifest, ["planned_tool", "planned_arguments"]) || !plannedCall) return void 0;
|
|
1394
|
+
const allowedArgs = action === "create" ? ["file_path", "content"] : [
|
|
1395
|
+
"file_path",
|
|
1396
|
+
"old_string",
|
|
1397
|
+
"new_string"
|
|
1398
|
+
];
|
|
1399
|
+
if (!hasExactKeys(plannedCall.arguments, allowedArgs)) return void 0;
|
|
1400
|
+
const artifact = pathOf(selector, cwd);
|
|
1401
|
+
if (!artifact || !planned) return void 0;
|
|
1402
|
+
const pre = await fileDigest(artifact);
|
|
1403
|
+
if (action === "create" && pre !== "absent") return void 0;
|
|
1404
|
+
if (action === "modify" && pre === "absent") return void 0;
|
|
1405
|
+
return { target: {
|
|
1406
|
+
artifact_id: artifact,
|
|
1407
|
+
scope: dirname(artifact),
|
|
1408
|
+
pre_digest: pre,
|
|
1409
|
+
change_set_digest: planned
|
|
1410
|
+
} };
|
|
1411
|
+
}
|
|
1412
|
+
const repository = requireString(selector, "repository");
|
|
1413
|
+
if (!repository || repository !== cwd && !isAbsolute(repository) || !plannedCall || !hasExactKeys(commandManifest, ["planned_tool", "planned_arguments"]) || executableIdentity$1?.executable !== "git") return void 0;
|
|
1414
|
+
const manifest = plannedGitManifest(action, selector, plannedCall);
|
|
1415
|
+
if (!manifest || !ACTION_MANIFEST.actions[action].commandManifestIds.includes(manifest.manifestId)) return void 0;
|
|
1416
|
+
const targetIdentity = {
|
|
1417
|
+
repository,
|
|
1418
|
+
...manifest.remote ? { remote: manifest.remote } : {},
|
|
1419
|
+
...requireString(selector, "refspec") ? { refspec: requireString(selector, "refspec") } : {}
|
|
1420
|
+
};
|
|
1421
|
+
const prestate = await gitPrestate(manifest, repository, signal, executableIdentity$1.realpath);
|
|
1422
|
+
if (!prestate) return void 0;
|
|
1423
|
+
const envelope = createGitPrestateEnvelope(manifest, targetIdentity, prestate);
|
|
1424
|
+
if (action === "commit") {
|
|
1425
|
+
if (!hasExactKeys(selector, ["repository", "branch"])) return void 0;
|
|
1426
|
+
const branch = requireString(selector, "branch");
|
|
1427
|
+
if (!branch || prestate.branch !== `refs/heads/${branch}`) return void 0;
|
|
1428
|
+
return {
|
|
1429
|
+
target: {
|
|
1430
|
+
repository,
|
|
1431
|
+
branch,
|
|
1432
|
+
change_set_digest: prestate.index_digest,
|
|
1433
|
+
pre_head_oid: prestate.pre_head_oid
|
|
1434
|
+
},
|
|
1435
|
+
gitBinding: {
|
|
1436
|
+
manifest,
|
|
1437
|
+
prestate,
|
|
1438
|
+
envelope
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
if (!hasExactKeys(selector, [
|
|
1443
|
+
"repository",
|
|
1444
|
+
"remote",
|
|
1445
|
+
"refspec"
|
|
1446
|
+
])) return void 0;
|
|
1447
|
+
const remote = requireString(selector, "remote");
|
|
1448
|
+
const refspec = requireString(selector, "refspec");
|
|
1449
|
+
if (!remote || !refspec) return void 0;
|
|
1450
|
+
let target;
|
|
1451
|
+
if (action === "push") target = {
|
|
1452
|
+
repository,
|
|
1453
|
+
remote,
|
|
1454
|
+
refspec,
|
|
1455
|
+
local_oid: prestate.source_oid
|
|
1456
|
+
};
|
|
1457
|
+
else if (action === "fetch") target = {
|
|
1458
|
+
repository,
|
|
1459
|
+
remote,
|
|
1460
|
+
refspec,
|
|
1461
|
+
upstream_oid: prestate.upstream_oid,
|
|
1462
|
+
pre_head_oid: prestate.pre_head_oid
|
|
1463
|
+
};
|
|
1464
|
+
else target = {
|
|
1465
|
+
repository,
|
|
1466
|
+
remote,
|
|
1467
|
+
refspec,
|
|
1468
|
+
upstream_oid: prestate.upstream_oid,
|
|
1469
|
+
pre_head_oid: prestate.pre_head_oid,
|
|
1470
|
+
pull_mode: "ff-only"
|
|
1471
|
+
};
|
|
1472
|
+
return {
|
|
1473
|
+
target,
|
|
1474
|
+
gitBinding: {
|
|
1475
|
+
manifest,
|
|
1476
|
+
prestate,
|
|
1477
|
+
envelope
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
function effectMatches(action, resolution, effect) {
|
|
1482
|
+
const resolvedTarget = resolution.target;
|
|
1483
|
+
if (action !== "create" && action !== "modify") return false;
|
|
1484
|
+
const expectedNames = action === "create" ? ["write", "write_file"] : ["edit", "edit_file"];
|
|
1485
|
+
if (effect.error !== void 0 || effect.resultSeq < effect.callSeq || !expectedNames.includes(effect.name)) return false;
|
|
1486
|
+
const parsed = evidenceFromPersistedToolResult({
|
|
1487
|
+
callId: "effect-readback",
|
|
1488
|
+
name: effect.name,
|
|
1489
|
+
arguments: JSON.stringify(effect.arguments)
|
|
1490
|
+
}, {
|
|
1491
|
+
seq: effect.resultSeq,
|
|
1492
|
+
error: effect.error,
|
|
1493
|
+
meta: effect.meta,
|
|
1494
|
+
textContent: effect.textContent
|
|
1495
|
+
}, 0, "effect-readback", typeof effect.arguments.workdir === "string" ? effect.arguments.workdir : void 0);
|
|
1496
|
+
if (parsed.outcome !== "success" || parsed.parseStatus !== "supported") return false;
|
|
1497
|
+
if (effectDigest(effect) !== resolvedTarget.change_set_digest && (action === "create" || action === "modify")) return false;
|
|
1498
|
+
if (effectDigest(effect) !== plannedEffectDigest(resolution.commandManifest)) return false;
|
|
1499
|
+
return true;
|
|
1500
|
+
}
|
|
1501
|
+
async function readback(action, target, roots, signal, executableIdentity$1) {
|
|
1502
|
+
if (action === "install" || action === "apply") {
|
|
1503
|
+
const packageId = typeof target.package_id === "string" ? target.package_id : void 0;
|
|
1504
|
+
const profile = typeof target.profile === "string" ? target.profile : void 0;
|
|
1505
|
+
const trustedProfile = roots.profile;
|
|
1506
|
+
if (!packageId || !profile || profile !== trustedProfile?.name) return void 0;
|
|
1507
|
+
const installed = await profilePackage(trustedProfile.path, packageId);
|
|
1508
|
+
return installed ? {
|
|
1509
|
+
package_id: packageId,
|
|
1510
|
+
version: installed.version,
|
|
1511
|
+
integrity_digest: installed.integrity,
|
|
1512
|
+
profile
|
|
1513
|
+
} : void 0;
|
|
1514
|
+
}
|
|
1515
|
+
if (action === "restart") {
|
|
1516
|
+
const capabilities = await marketCapabilities(roots, signal);
|
|
1517
|
+
if (!capabilities || capabilities.bootId === target.pre_generation) return void 0;
|
|
1518
|
+
return {
|
|
1519
|
+
new_generation: String(capabilities.bootId),
|
|
1520
|
+
health: "healthy"
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
if (action === "publish") {
|
|
1524
|
+
const registry = typeof target.registry === "string" ? target.registry : void 0;
|
|
1525
|
+
const artifact = typeof target.artifact_id === "string" ? target.artifact_id : void 0;
|
|
1526
|
+
const version = typeof target.version === "string" ? target.version : void 0;
|
|
1527
|
+
if (!registry || !artifact || !version) return void 0;
|
|
1528
|
+
const integrity = await registryIntegrity(registry, artifact, version, roots, signal);
|
|
1529
|
+
return integrity ? {
|
|
1530
|
+
artifact_id: artifact,
|
|
1531
|
+
version,
|
|
1532
|
+
registry,
|
|
1533
|
+
integrity_digest: integrity
|
|
1534
|
+
} : void 0;
|
|
1535
|
+
}
|
|
1536
|
+
if (action === "create" || action === "modify") {
|
|
1537
|
+
const artifact = typeof target.artifact_id === "string" ? target.artifact_id : void 0;
|
|
1538
|
+
if (!artifact) return void 0;
|
|
1539
|
+
const post = await fileDigest(artifact);
|
|
1540
|
+
return post === "absent" ? void 0 : { post_digest: post };
|
|
1541
|
+
}
|
|
1542
|
+
const repository = typeof target.repository === "string" ? target.repository : void 0;
|
|
1543
|
+
if (!repository || executableIdentity$1?.executable !== "git") return void 0;
|
|
1544
|
+
if (action === "commit") {
|
|
1545
|
+
const commit = verifiedLinearCommitReadback(await gitBytes(repository, [
|
|
1546
|
+
"rev-list",
|
|
1547
|
+
"--parents",
|
|
1548
|
+
"-n",
|
|
1549
|
+
"1",
|
|
1550
|
+
"HEAD"
|
|
1551
|
+
], signal, executableIdentity$1.realpath), String(target.pre_head_oid ?? ""));
|
|
1552
|
+
const tree = commit ? commitTreeSnapshotDigest(await gitBytes(repository, [
|
|
1553
|
+
"ls-tree",
|
|
1554
|
+
"-r",
|
|
1555
|
+
"-z",
|
|
1556
|
+
commit.postHeadOid
|
|
1557
|
+
], signal, executableIdentity$1.realpath)) : void 0;
|
|
1558
|
+
return commit && tree === target.change_set_digest ? {
|
|
1559
|
+
post_head_oid: commit.postHeadOid,
|
|
1560
|
+
pre_head_oid: commit.preHeadOid
|
|
1561
|
+
} : void 0;
|
|
1562
|
+
}
|
|
1563
|
+
const remote = String(target.remote ?? "");
|
|
1564
|
+
const parsed = String(target.refspec ?? "").split(":");
|
|
1565
|
+
if (action === "push") {
|
|
1566
|
+
if (parsed.length !== 2) return void 0;
|
|
1567
|
+
const remoteOid = await exactRemoteOid(repository, remote, parsed[1], signal, executableIdentity$1.realpath);
|
|
1568
|
+
return remoteOid ? { remote_oid: remoteOid } : void 0;
|
|
1569
|
+
}
|
|
1570
|
+
const sourceRef = parsed[0];
|
|
1571
|
+
const trackingRef = action === "fetch" ? parsed[1] : sourceRef.startsWith("refs/heads/") ? `refs/remotes/${remote}/${sourceRef.slice(11)}` : void 0;
|
|
1572
|
+
if (!trackingRef) return void 0;
|
|
1573
|
+
const tracking = oid(await git(repository, [
|
|
1574
|
+
"rev-parse",
|
|
1575
|
+
"--verify",
|
|
1576
|
+
trackingRef
|
|
1577
|
+
], signal, executableIdentity$1.realpath));
|
|
1578
|
+
if (!tracking) return void 0;
|
|
1579
|
+
const postHead = oid(await git(repository, ["rev-parse", "HEAD"], signal, executableIdentity$1.realpath));
|
|
1580
|
+
if (action === "fetch") return postHead ? {
|
|
1581
|
+
tracking_ref_oid: tracking,
|
|
1582
|
+
post_head_oid: postHead
|
|
1583
|
+
} : void 0;
|
|
1584
|
+
return postHead ? {
|
|
1585
|
+
post_head_oid: postHead,
|
|
1586
|
+
tracking_ref_oid: tracking
|
|
1587
|
+
} : void 0;
|
|
1588
|
+
}
|
|
1589
|
+
function normalizedRoots(options) {
|
|
1590
|
+
const detectedProfile = options.profile ? void 0 : installedProfile();
|
|
1591
|
+
return {
|
|
1592
|
+
...options.profile ? { profile: {
|
|
1593
|
+
name: options.profile.name,
|
|
1594
|
+
path: resolve(options.profile.path)
|
|
1595
|
+
} } : detectedProfile ? { profile: detectedProfile } : {},
|
|
1596
|
+
...options.marketOrigin ? { marketOrigin: options.marketOrigin } : {},
|
|
1597
|
+
...options.fetcher ? { fetcher: options.fetcher } : {},
|
|
1598
|
+
...options.commandRunner ? { commandRunner: options.commandRunner } : {},
|
|
1599
|
+
...options.persistRestartIntent ? { persistRestartIntent: options.persistRestartIntent } : {},
|
|
1600
|
+
...options.hostCapability ? { hostCapability: options.hostCapability } : {},
|
|
1601
|
+
...options.authorizeMutation ? { authorizeMutation: options.authorizeMutation } : {},
|
|
1602
|
+
...options.prepareMutation ? { prepareMutation: options.prepareMutation } : {},
|
|
1603
|
+
...options.readExecutableIdentity ? { readExecutableIdentity: options.readExecutableIdentity } : {},
|
|
1604
|
+
...options.allowLoopbackHttpRegistry ? { allowLoopbackHttpRegistry: true } : {}
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
function createActionTool(options = {}) {
|
|
1608
|
+
const roots = normalizedRoots(options);
|
|
1609
|
+
return defineTool({
|
|
1610
|
+
name: ACTION_TOOL,
|
|
1611
|
+
description: "Execute one explicit Guard-owned mutation after re-reading a persisted resolution and exact target digest. This tool changes package, Git, registry, or Web service state; its result echoes the bounded target and command-manifest digest.",
|
|
1612
|
+
parameters: {
|
|
1613
|
+
semantic_action: {
|
|
1614
|
+
type: "string",
|
|
1615
|
+
required: true,
|
|
1616
|
+
enum: [
|
|
1617
|
+
"install",
|
|
1618
|
+
"apply",
|
|
1619
|
+
"restart",
|
|
1620
|
+
"publish",
|
|
1621
|
+
"commit",
|
|
1622
|
+
"push",
|
|
1623
|
+
"pull",
|
|
1624
|
+
"fetch"
|
|
1625
|
+
]
|
|
1626
|
+
},
|
|
1627
|
+
resolution_call_id: {
|
|
1628
|
+
type: "string",
|
|
1629
|
+
required: true
|
|
1630
|
+
},
|
|
1631
|
+
target_digest: {
|
|
1632
|
+
type: "string",
|
|
1633
|
+
required: true
|
|
1634
|
+
},
|
|
1635
|
+
contract_item_id: {
|
|
1636
|
+
type: "string",
|
|
1637
|
+
required: true
|
|
1638
|
+
},
|
|
1639
|
+
contract_item_revision: {
|
|
1640
|
+
type: "number",
|
|
1641
|
+
required: true
|
|
1642
|
+
}
|
|
1643
|
+
},
|
|
1644
|
+
output: {
|
|
1645
|
+
schema: {
|
|
1646
|
+
type: "object",
|
|
1647
|
+
additionalProperties: false,
|
|
1648
|
+
properties: {
|
|
1649
|
+
status: {
|
|
1650
|
+
type: "string",
|
|
1651
|
+
required: true,
|
|
1652
|
+
enum: [
|
|
1653
|
+
"completed",
|
|
1654
|
+
"handoff_pending",
|
|
1655
|
+
"unavailable"
|
|
1656
|
+
]
|
|
1657
|
+
},
|
|
1658
|
+
reason_code: {
|
|
1659
|
+
type: "string",
|
|
1660
|
+
required: true
|
|
1661
|
+
},
|
|
1662
|
+
resolved_target: {
|
|
1663
|
+
type: "object",
|
|
1664
|
+
required: true,
|
|
1665
|
+
additionalProperties: true
|
|
1666
|
+
},
|
|
1667
|
+
target_digest: {
|
|
1668
|
+
type: "string",
|
|
1669
|
+
required: true
|
|
1670
|
+
},
|
|
1671
|
+
command_manifest_digest: {
|
|
1672
|
+
type: "string",
|
|
1673
|
+
required: true
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
},
|
|
1677
|
+
render: (_args, value) => [{
|
|
1678
|
+
type: "text",
|
|
1679
|
+
text: JSON.stringify(value)
|
|
1680
|
+
}],
|
|
1681
|
+
presentationMeta: (args, value) => ({ contextGuardAction: {
|
|
1682
|
+
status: value.status,
|
|
1683
|
+
reasonCode: value.reason_code,
|
|
1684
|
+
resolvedTarget: value.resolved_target,
|
|
1685
|
+
targetDigest: value.target_digest,
|
|
1686
|
+
commandManifestDigest: value.command_manifest_digest,
|
|
1687
|
+
contractItemId: args.contract_item_id,
|
|
1688
|
+
contractItemRevision: args.contract_item_revision
|
|
1689
|
+
} })
|
|
1690
|
+
},
|
|
1691
|
+
async execute(args, exec) {
|
|
1692
|
+
const action = args.semantic_action;
|
|
1693
|
+
const agent = exec.agent;
|
|
1694
|
+
const empty = {
|
|
1695
|
+
resolved_target: {},
|
|
1696
|
+
target_digest: "",
|
|
1697
|
+
command_manifest_digest: ""
|
|
1698
|
+
};
|
|
1699
|
+
if (!agent || ![
|
|
1700
|
+
"install",
|
|
1701
|
+
"apply",
|
|
1702
|
+
"restart",
|
|
1703
|
+
"publish",
|
|
1704
|
+
"commit",
|
|
1705
|
+
"push",
|
|
1706
|
+
"pull",
|
|
1707
|
+
"fetch"
|
|
1708
|
+
].includes(action)) return {
|
|
1709
|
+
status: "unavailable",
|
|
1710
|
+
reason_code: "action_adapter_unavailable",
|
|
1711
|
+
...empty
|
|
1712
|
+
};
|
|
1713
|
+
if (roots.hostCapability?.(action).status !== "supported" && roots.hostCapability) return {
|
|
1714
|
+
status: "unavailable",
|
|
1715
|
+
reason_code: "host_capability_unavailable",
|
|
1716
|
+
...empty
|
|
1717
|
+
};
|
|
1718
|
+
let durable = false;
|
|
1719
|
+
try {
|
|
1720
|
+
durable = await roots.prepareMutation?.(agent) === true;
|
|
1721
|
+
} catch {
|
|
1722
|
+
durable = false;
|
|
1723
|
+
}
|
|
1724
|
+
if (!durable) return {
|
|
1725
|
+
status: "unavailable",
|
|
1726
|
+
reason_code: "mutation_durability_unavailable",
|
|
1727
|
+
...empty
|
|
1728
|
+
};
|
|
1729
|
+
const resolution = findResolution(agent.session.events, args.resolution_call_id, action);
|
|
1730
|
+
if (!resolution) return {
|
|
1731
|
+
status: "unavailable",
|
|
1732
|
+
reason_code: "resolution_evidence_missing",
|
|
1733
|
+
...empty
|
|
1734
|
+
};
|
|
1735
|
+
const targetDigest = digest(resolution.target);
|
|
1736
|
+
const manifestDigest = digest(resolution.commandManifest);
|
|
1737
|
+
const identity = {
|
|
1738
|
+
resolved_target: jsonTuple(resolution.target),
|
|
1739
|
+
target_digest: targetDigest,
|
|
1740
|
+
command_manifest_digest: manifestDigest
|
|
1741
|
+
};
|
|
1742
|
+
if (args.target_digest !== targetDigest) return {
|
|
1743
|
+
status: "unavailable",
|
|
1744
|
+
reason_code: "target_digest_mismatch",
|
|
1745
|
+
...identity
|
|
1746
|
+
};
|
|
1747
|
+
let authorization;
|
|
1748
|
+
try {
|
|
1749
|
+
authorization = roots.authorizeMutation?.({
|
|
1750
|
+
action,
|
|
1751
|
+
contractItemId: args.contract_item_id,
|
|
1752
|
+
contractItemRevision: args.contract_item_revision,
|
|
1753
|
+
resolvedTarget: resolution.target
|
|
1754
|
+
});
|
|
1755
|
+
} catch {
|
|
1756
|
+
authorization = void 0;
|
|
1757
|
+
}
|
|
1758
|
+
if (authorization?.status !== "authorized") return {
|
|
1759
|
+
status: "unavailable",
|
|
1760
|
+
reason_code: authorization?.reasonCode ?? "mutation_authority_unavailable",
|
|
1761
|
+
...identity
|
|
1762
|
+
};
|
|
1763
|
+
const executable = executableFor(action);
|
|
1764
|
+
let currentExecutable;
|
|
1765
|
+
if (executable) {
|
|
1766
|
+
currentExecutable = await (roots.readExecutableIdentity ?? executableIdentity)(executable, exec.signal);
|
|
1767
|
+
if (bindExecutableIdentity(resolution.executableIdentity, currentExecutable).status !== "supported") return {
|
|
1768
|
+
status: "unavailable",
|
|
1769
|
+
reason_code: "executable_identity_drift",
|
|
1770
|
+
...identity
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
try {
|
|
1774
|
+
const status = await executeGuardAction(action, resolution, roots, exec.signal, currentExecutable, args.resolution_call_id, agent);
|
|
1775
|
+
return {
|
|
1776
|
+
status,
|
|
1777
|
+
reason_code: status === "completed" ? "action_completed" : status === "handoff_pending" ? "restart_handoff_pending" : "action_execution_failed",
|
|
1778
|
+
...identity
|
|
1779
|
+
};
|
|
1780
|
+
} catch {
|
|
1781
|
+
return {
|
|
1782
|
+
status: "unavailable",
|
|
1783
|
+
reason_code: "action_execution_failed",
|
|
1784
|
+
...identity
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
function createEvidenceTool(options = {}) {
|
|
1791
|
+
const roots = normalizedRoots(options);
|
|
7
1792
|
return defineTool({
|
|
8
|
-
name:
|
|
9
|
-
description: "
|
|
10
|
-
parameters: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1793
|
+
name: PRODUCER_TOOL,
|
|
1794
|
+
description: "Create one trusted stateful resolution/effect/state fact from the live resource and persisted DSH tool events. Unsupported adapters fail closed.",
|
|
1795
|
+
parameters: {
|
|
1796
|
+
semantic_action: {
|
|
1797
|
+
type: "string",
|
|
1798
|
+
required: true,
|
|
1799
|
+
enum: [
|
|
1800
|
+
"install",
|
|
1801
|
+
"apply",
|
|
1802
|
+
"create",
|
|
1803
|
+
"modify",
|
|
1804
|
+
"restart",
|
|
1805
|
+
"commit",
|
|
1806
|
+
"push",
|
|
1807
|
+
"publish",
|
|
1808
|
+
"pull",
|
|
1809
|
+
"fetch"
|
|
1810
|
+
]
|
|
1811
|
+
},
|
|
1812
|
+
evidence_role: {
|
|
1813
|
+
type: "string",
|
|
1814
|
+
required: true,
|
|
1815
|
+
enum: [
|
|
1816
|
+
"resolution",
|
|
1817
|
+
"effect",
|
|
1818
|
+
"state"
|
|
1819
|
+
]
|
|
1820
|
+
},
|
|
1821
|
+
selector: {
|
|
14
1822
|
type: "object",
|
|
15
1823
|
additionalProperties: false,
|
|
16
1824
|
properties: {
|
|
17
|
-
|
|
1825
|
+
artifact_id: { type: "string" },
|
|
1826
|
+
package_id: { type: "string" },
|
|
1827
|
+
version: { type: "string" },
|
|
1828
|
+
profile: { type: "string" },
|
|
1829
|
+
registry: { type: "string" },
|
|
1830
|
+
service_id: { type: "string" },
|
|
1831
|
+
repository: { type: "string" },
|
|
1832
|
+
branch: { type: "string" },
|
|
1833
|
+
remote: { type: "string" },
|
|
1834
|
+
refspec: { type: "string" }
|
|
1835
|
+
}
|
|
1836
|
+
},
|
|
1837
|
+
command_manifest: {
|
|
1838
|
+
type: "object",
|
|
1839
|
+
additionalProperties: false,
|
|
1840
|
+
properties: {
|
|
1841
|
+
manifest_id: { type: "string" },
|
|
1842
|
+
tgz_path: { type: "string" },
|
|
1843
|
+
planned_tool: { type: "string" },
|
|
1844
|
+
planned_arguments: {
|
|
1845
|
+
type: "object",
|
|
1846
|
+
additionalProperties: false,
|
|
1847
|
+
properties: {
|
|
1848
|
+
file_path: { type: "string" },
|
|
1849
|
+
content: { type: "string" },
|
|
1850
|
+
old_string: { type: "string" },
|
|
1851
|
+
new_string: { type: "string" },
|
|
1852
|
+
command: { type: "string" },
|
|
1853
|
+
workdir: { type: "string" }
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
},
|
|
1858
|
+
resolution_call_id: { type: "string" },
|
|
1859
|
+
effect_call_id: { type: "string" }
|
|
1860
|
+
},
|
|
1861
|
+
output: {
|
|
1862
|
+
schema: {
|
|
1863
|
+
type: "object",
|
|
1864
|
+
additionalProperties: false,
|
|
1865
|
+
properties: {
|
|
1866
|
+
status: {
|
|
1867
|
+
type: "string",
|
|
1868
|
+
required: true,
|
|
1869
|
+
enum: ["supported", "unavailable"]
|
|
1870
|
+
},
|
|
1871
|
+
reason_code: {
|
|
18
1872
|
type: "string",
|
|
19
1873
|
required: true
|
|
20
1874
|
},
|
|
21
|
-
|
|
22
|
-
type: "
|
|
1875
|
+
semantic_action: {
|
|
1876
|
+
type: "string",
|
|
1877
|
+
required: true
|
|
1878
|
+
},
|
|
1879
|
+
evidence_role: {
|
|
1880
|
+
type: "string",
|
|
23
1881
|
required: true,
|
|
24
|
-
|
|
1882
|
+
enum: [
|
|
1883
|
+
"resolution",
|
|
1884
|
+
"effect",
|
|
1885
|
+
"state"
|
|
1886
|
+
]
|
|
1887
|
+
},
|
|
1888
|
+
resolved_target: {
|
|
1889
|
+
type: "object",
|
|
1890
|
+
required: true,
|
|
1891
|
+
additionalProperties: true
|
|
1892
|
+
},
|
|
1893
|
+
observed_state: {
|
|
1894
|
+
type: "object",
|
|
1895
|
+
required: true,
|
|
1896
|
+
additionalProperties: true
|
|
1897
|
+
},
|
|
1898
|
+
adapter_id: {
|
|
1899
|
+
type: "string",
|
|
1900
|
+
required: true
|
|
1901
|
+
},
|
|
1902
|
+
adapter_version: {
|
|
1903
|
+
type: "string",
|
|
1904
|
+
required: true
|
|
1905
|
+
},
|
|
1906
|
+
target_digest: {
|
|
1907
|
+
type: "string",
|
|
1908
|
+
required: true
|
|
1909
|
+
},
|
|
1910
|
+
command_manifest_digest: {
|
|
1911
|
+
type: "string",
|
|
1912
|
+
required: true
|
|
1913
|
+
},
|
|
1914
|
+
expected_transition: {
|
|
1915
|
+
type: "object",
|
|
1916
|
+
additionalProperties: false,
|
|
1917
|
+
properties: {
|
|
1918
|
+
predicateId: {
|
|
1919
|
+
type: "string",
|
|
1920
|
+
required: true
|
|
1921
|
+
},
|
|
1922
|
+
version: {
|
|
1923
|
+
type: "number",
|
|
1924
|
+
required: true
|
|
1925
|
+
},
|
|
1926
|
+
predParamsKind: {
|
|
1927
|
+
type: "string",
|
|
1928
|
+
required: true,
|
|
1929
|
+
enum: ["inline"]
|
|
1930
|
+
},
|
|
1931
|
+
parameters: {
|
|
1932
|
+
type: "object",
|
|
1933
|
+
required: true,
|
|
1934
|
+
additionalProperties: true
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
},
|
|
1938
|
+
expected_transition_digest: { type: "string" },
|
|
1939
|
+
git_binding: {
|
|
1940
|
+
type: "object",
|
|
1941
|
+
additionalProperties: true
|
|
1942
|
+
},
|
|
1943
|
+
executable_identity: {
|
|
1944
|
+
type: "object",
|
|
1945
|
+
additionalProperties: false,
|
|
1946
|
+
properties: {
|
|
1947
|
+
executable: {
|
|
1948
|
+
type: "string",
|
|
1949
|
+
required: true
|
|
1950
|
+
},
|
|
1951
|
+
realpath: {
|
|
1952
|
+
type: "string",
|
|
1953
|
+
required: true
|
|
1954
|
+
},
|
|
1955
|
+
version: {
|
|
1956
|
+
type: "string",
|
|
1957
|
+
required: true
|
|
1958
|
+
},
|
|
1959
|
+
interpreterRealpath: { type: "string" },
|
|
1960
|
+
interpreterVersion: { type: "string" }
|
|
1961
|
+
}
|
|
25
1962
|
}
|
|
26
1963
|
}
|
|
1964
|
+
},
|
|
1965
|
+
render: (_args, value) => [{
|
|
1966
|
+
type: "text",
|
|
1967
|
+
text: JSON.stringify(value)
|
|
1968
|
+
}],
|
|
1969
|
+
presentationMeta: (_args, value) => {
|
|
1970
|
+
if (value.status !== "supported") return { contextGuardDisposition: {
|
|
1971
|
+
status: value.status,
|
|
1972
|
+
reasonCode: value.reason_code
|
|
1973
|
+
} };
|
|
1974
|
+
return { contextGuard: {
|
|
1975
|
+
adapterId: value.adapter_id,
|
|
1976
|
+
adapterVersion: value.adapter_version,
|
|
1977
|
+
semanticAction: value.semantic_action,
|
|
1978
|
+
evidenceRole: value.evidence_role,
|
|
1979
|
+
resolvedTarget: value.resolved_target,
|
|
1980
|
+
targetDigest: value.target_digest,
|
|
1981
|
+
commandManifestDigest: value.command_manifest_digest,
|
|
1982
|
+
...value.expected_transition ? {
|
|
1983
|
+
expectedTransition: value.expected_transition,
|
|
1984
|
+
expectedTransitionDigest: value.expected_transition_digest
|
|
1985
|
+
} : {},
|
|
1986
|
+
...value.git_binding ? { gitBinding: value.git_binding } : {},
|
|
1987
|
+
...value.executable_identity ? { executableIdentity: value.executable_identity } : {},
|
|
1988
|
+
...value.evidence_role === "state" ? { observedState: value.observed_state } : {}
|
|
1989
|
+
} };
|
|
1990
|
+
}
|
|
1991
|
+
},
|
|
1992
|
+
async execute(args, exec) {
|
|
1993
|
+
const action = args.semantic_action;
|
|
1994
|
+
const role = args.evidence_role;
|
|
1995
|
+
if (!SUPPORTED.includes(action)) return unavailable(action, role, "adapter_unavailable_for_pinned_host");
|
|
1996
|
+
if (roots.hostCapability?.(action).status !== "supported" && roots.hostCapability) return unavailable(action, role, "host_capability_unavailable");
|
|
1997
|
+
const agent = exec.agent;
|
|
1998
|
+
if (!agent) return unavailable(action, role, "producer_agent_unavailable");
|
|
1999
|
+
try {
|
|
2000
|
+
if (role === "resolution") {
|
|
2001
|
+
const executable$1 = executableFor(action);
|
|
2002
|
+
const executableBinding = executable$1 ? await (roots.readExecutableIdentity ?? executableIdentity)(executable$1, exec.signal) : void 0;
|
|
2003
|
+
if (executable$1 && !executableBinding) return unavailable(action, role, "executable_identity_unavailable");
|
|
2004
|
+
const resolved = await resolveTarget(action, record(args.selector) ?? {}, record(args.command_manifest) ?? {}, cwdOf(agent), roots, exec.signal, executableBinding);
|
|
2005
|
+
if (!resolved) return unavailable(action, role, "resolution_unavailable");
|
|
2006
|
+
const gitBinding = resolved.gitBinding ? JSON.parse(JSON.stringify(resolved.gitBinding)) : void 0;
|
|
2007
|
+
const commandManifest = record(args.command_manifest) ?? {};
|
|
2008
|
+
const expectedTransition = await expectedTransitionForResolution(action, resolved.target, commandManifest);
|
|
2009
|
+
if (!expectedTransition) return unavailable(action, role, "expected_transition_unavailable");
|
|
2010
|
+
return supported(action, role, resolved.target, {}, commandManifest, gitBinding, executableBinding, expectedTransition);
|
|
2011
|
+
}
|
|
2012
|
+
if (!args.resolution_call_id) return unavailable(action, role, "producer_reference_missing");
|
|
2013
|
+
const resolution = findResolution(agent.session.events, args.resolution_call_id, action);
|
|
2014
|
+
if (!resolution) return unavailable(action, role, "persisted_effect_mismatch");
|
|
2015
|
+
const executable = executableFor(action);
|
|
2016
|
+
let currentExecutable;
|
|
2017
|
+
if (executable) {
|
|
2018
|
+
currentExecutable = await (roots.readExecutableIdentity ?? executableIdentity)(executable, exec.signal);
|
|
2019
|
+
if (bindExecutableIdentity(resolution.executableIdentity, currentExecutable).status !== "supported") return unavailable(action, role, "executable_identity_drift");
|
|
2020
|
+
}
|
|
2021
|
+
const ownedEffect = [
|
|
2022
|
+
"install",
|
|
2023
|
+
"apply",
|
|
2024
|
+
"restart",
|
|
2025
|
+
"publish",
|
|
2026
|
+
"commit",
|
|
2027
|
+
"push",
|
|
2028
|
+
"pull",
|
|
2029
|
+
"fetch"
|
|
2030
|
+
].includes(action);
|
|
2031
|
+
if (!args.effect_call_id) return unavailable(action, role, "producer_reference_missing");
|
|
2032
|
+
if (ownedEffect) {
|
|
2033
|
+
const actionCall = actionCallMatches(agent.session.events, args.effect_call_id, action, args.resolution_call_id, resolution.target);
|
|
2034
|
+
if (!(action === "restart" ? actionCall && restartIntent(agent.session.events, args.resolution_call_id, resolution.target) && (await marketCapabilities(roots, exec.signal))?.bootId !== resolution.target.pre_generation : actionCall && actionResultCompleted(agent.session.events, args.effect_call_id))) return unavailable(action, role, "persisted_effect_mismatch");
|
|
2035
|
+
if (role === "effect") return supported(action, role, resolution.target, {}, resolution.commandManifest, void 0, currentExecutable);
|
|
2036
|
+
} else {
|
|
2037
|
+
const effect = findEffect(agent.session.events, args.effect_call_id);
|
|
2038
|
+
if (!effect || !effectMatches(action, resolution, effect)) return unavailable(action, role, "persisted_effect_mismatch");
|
|
2039
|
+
if (role === "effect") return supported(action, role, resolution.target, {}, resolution.commandManifest, void 0, currentExecutable);
|
|
2040
|
+
}
|
|
2041
|
+
const observed = await readback(action, resolution.target, roots, exec.signal, currentExecutable);
|
|
2042
|
+
return observed ? supported(action, role, resolution.target, observed, resolution.commandManifest, void 0, currentExecutable) : unavailable(action, role, "independent_readback_unavailable");
|
|
2043
|
+
} catch {
|
|
2044
|
+
return unavailable(action, role, "adapter_readback_failed");
|
|
27
2045
|
}
|
|
2046
|
+
}
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
//#endregion
|
|
2051
|
+
//#region src/tools/external-operation.ts
|
|
2052
|
+
function createExternalOperationTool(read, capability) {
|
|
2053
|
+
return defineTool({
|
|
2054
|
+
name: "context_guard_external_operation",
|
|
2055
|
+
description: "Read a live background operation from the pinned jobs capability and mint a bounded external-wait qualification. Text output never controls status.",
|
|
2056
|
+
parameters: { operation_id: {
|
|
2057
|
+
type: "string",
|
|
2058
|
+
required: true
|
|
28
2059
|
} },
|
|
29
2060
|
output: {
|
|
30
2061
|
schema: {
|
|
@@ -33,93 +2064,60 @@ function createCheckpointTool(getProjection, onRejected) {
|
|
|
33
2064
|
properties: {
|
|
34
2065
|
status: {
|
|
35
2066
|
type: "string",
|
|
2067
|
+
required: true,
|
|
36
2068
|
enum: [
|
|
37
|
-
"
|
|
38
|
-
"
|
|
2069
|
+
"running",
|
|
2070
|
+
"pending",
|
|
2071
|
+
"completed",
|
|
2072
|
+
"failed",
|
|
39
2073
|
"unknown"
|
|
40
2074
|
]
|
|
41
2075
|
},
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
items: { type: "string" }
|
|
2076
|
+
operation_id: {
|
|
2077
|
+
type: "string",
|
|
2078
|
+
required: true
|
|
46
2079
|
},
|
|
47
|
-
|
|
48
|
-
type: "
|
|
49
|
-
|
|
50
|
-
type: "object",
|
|
51
|
-
additionalProperties: false,
|
|
52
|
-
properties: {
|
|
53
|
-
id: { type: "string" },
|
|
54
|
-
tool: { type: "string" },
|
|
55
|
-
subjects: {
|
|
56
|
-
type: "array",
|
|
57
|
-
items: { type: "string" }
|
|
58
|
-
},
|
|
59
|
-
surfaces: {
|
|
60
|
-
type: "array",
|
|
61
|
-
items: { type: "string" }
|
|
62
|
-
},
|
|
63
|
-
outcome: { type: "string" },
|
|
64
|
-
capabilities: {
|
|
65
|
-
type: "array",
|
|
66
|
-
items: { type: "string" }
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
2080
|
+
reason_code: {
|
|
2081
|
+
type: "string",
|
|
2082
|
+
required: true
|
|
70
2083
|
},
|
|
71
|
-
|
|
72
|
-
type: "
|
|
73
|
-
|
|
74
|
-
type: "object",
|
|
75
|
-
additionalProperties: false,
|
|
76
|
-
properties: {
|
|
77
|
-
item_id: { type: "string" },
|
|
78
|
-
reason: { type: "string" },
|
|
79
|
-
hint: { type: "string" }
|
|
80
|
-
}
|
|
81
|
-
}
|
|
2084
|
+
adapter_id: {
|
|
2085
|
+
type: "string",
|
|
2086
|
+
required: true
|
|
82
2087
|
}
|
|
83
2088
|
}
|
|
84
2089
|
},
|
|
85
2090
|
render: (_args, value) => [{
|
|
86
2091
|
type: "text",
|
|
87
2092
|
text: JSON.stringify(value)
|
|
88
|
-
}]
|
|
2093
|
+
}],
|
|
2094
|
+
presentationMeta: (_args, value) => {
|
|
2095
|
+
return { contextGuardExternalOperation: {
|
|
2096
|
+
id: value.operation_id,
|
|
2097
|
+
status: value.status,
|
|
2098
|
+
adapterId: value.adapter_id
|
|
2099
|
+
} };
|
|
2100
|
+
}
|
|
89
2101
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!projection) return {
|
|
2102
|
+
execute(args, exec) {
|
|
2103
|
+
if (capability().status !== "supported") return Promise.resolve({
|
|
93
2104
|
status: "unknown",
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
capabilities: evidence.capabilities
|
|
111
|
-
}));
|
|
112
|
-
return {
|
|
113
|
-
status: result.status,
|
|
114
|
-
contract_revision: result.contractRevision,
|
|
115
|
-
open_items: result.openItems,
|
|
116
|
-
available_evidence,
|
|
117
|
-
rejected_bindings: result.rejectedBindings.map((binding) => ({
|
|
118
|
-
item_id: binding.itemId,
|
|
119
|
-
reason: binding.reason,
|
|
120
|
-
...binding.hint !== void 0 ? { hint: binding.hint } : {}
|
|
121
|
-
}))
|
|
122
|
-
};
|
|
2105
|
+
operation_id: args.operation_id,
|
|
2106
|
+
reason_code: "host_jobs_capability_unavailable",
|
|
2107
|
+
adapter_id: "dsh.jobs.v1"
|
|
2108
|
+
});
|
|
2109
|
+
const snapshot = read(args.operation_id, exec.agent);
|
|
2110
|
+
return Promise.resolve(snapshot ? {
|
|
2111
|
+
status: snapshot.status,
|
|
2112
|
+
operation_id: snapshot.id,
|
|
2113
|
+
reason_code: "external_operation_readback",
|
|
2114
|
+
adapter_id: snapshot.adapterId
|
|
2115
|
+
} : {
|
|
2116
|
+
status: "unknown",
|
|
2117
|
+
operation_id: args.operation_id,
|
|
2118
|
+
reason_code: "external_operation_unavailable",
|
|
2119
|
+
adapter_id: "dsh.jobs.v1"
|
|
2120
|
+
});
|
|
123
2121
|
}
|
|
124
2122
|
});
|
|
125
2123
|
}
|
|
@@ -620,10 +2618,10 @@ for (const key of [
|
|
|
620
2618
|
return schema;
|
|
621
2619
|
} });
|
|
622
2620
|
const resolvers = {};
|
|
623
|
-
Schema.extend = function extend(type, resolve) {
|
|
624
|
-
resolvers[type] = resolve;
|
|
2621
|
+
Schema.extend = function extend(type, resolve$1) {
|
|
2622
|
+
resolvers[type] = resolve$1;
|
|
625
2623
|
};
|
|
626
|
-
Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
|
|
2624
|
+
Schema.resolve = function resolve$1(data, schema, options = {}, strict = false) {
|
|
627
2625
|
if (!schema) return [data];
|
|
628
2626
|
if (options.ignore?.(data, schema)) return [data];
|
|
629
2627
|
if (isNullable(data) && schema.type !== "lazy") {
|
|
@@ -983,19 +2981,181 @@ defineMethod("transform", [
|
|
|
983
2981
|
|
|
984
2982
|
//#endregion
|
|
985
2983
|
//#region src/config.ts
|
|
986
|
-
const Config = Schema.object({
|
|
2984
|
+
const Config = Schema.object({
|
|
2985
|
+
activation: Schema.string().default("opt-in"),
|
|
2986
|
+
hostLockPlatform: Schema.string(),
|
|
2987
|
+
hostLockProfile: Schema.string(),
|
|
2988
|
+
hostLockPackages: Schema.array(Schema.object({
|
|
2989
|
+
name: Schema.string().required(),
|
|
2990
|
+
version: Schema.string(),
|
|
2991
|
+
integrity: Schema.string()
|
|
2992
|
+
}))
|
|
2993
|
+
});
|
|
987
2994
|
function resolveConfig(config) {
|
|
988
2995
|
const activation = config.activation ?? "opt-in";
|
|
989
2996
|
if (activation !== "opt-in" && activation !== "always") throw new TypeError(`activation must be "opt-in" or "always", received ${JSON.stringify(activation)}`);
|
|
990
|
-
|
|
2997
|
+
let hostLockPackages;
|
|
2998
|
+
if (config.hostLockPackages !== void 0) {
|
|
2999
|
+
if (!Array.isArray(config.hostLockPackages)) throw new TypeError("hostLockPackages must be an array");
|
|
3000
|
+
hostLockPackages = config.hostLockPackages.map((entry) => {
|
|
3001
|
+
if (!entry || typeof entry !== "object") throw new TypeError("hostLockPackages entries must be objects");
|
|
3002
|
+
const row = entry;
|
|
3003
|
+
if (typeof row.name !== "string" || !row.name) throw new TypeError("hostLockPackages.name must be a non-empty string");
|
|
3004
|
+
if (row.version !== void 0 && typeof row.version !== "string") throw new TypeError("hostLockPackages.version must be a string");
|
|
3005
|
+
if (row.integrity !== void 0 && typeof row.integrity !== "string") throw new TypeError("hostLockPackages.integrity must be a string");
|
|
3006
|
+
return {
|
|
3007
|
+
name: row.name,
|
|
3008
|
+
...row.version ? { version: row.version } : {},
|
|
3009
|
+
...row.integrity ? { integrity: row.integrity } : {}
|
|
3010
|
+
};
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
if (config.hostLockPlatform !== void 0 && config.hostLockPlatform !== "posix" && config.hostLockPlatform !== "windows") throw new TypeError("hostLockPlatform must be \"posix\" or \"windows\"");
|
|
3014
|
+
if (config.hostLockProfile !== void 0 && config.hostLockProfile !== "headless" && config.hostLockProfile !== "web") throw new TypeError("hostLockProfile must be \"headless\" or \"web\"");
|
|
3015
|
+
const hasHostRows = hostLockPackages !== void 0;
|
|
3016
|
+
if (hasHostRows !== (config.hostLockPlatform !== void 0) || hasHostRows !== (config.hostLockProfile !== void 0)) throw new TypeError("hostLockPackages, hostLockPlatform, and hostLockProfile must be injected together");
|
|
3017
|
+
return {
|
|
3018
|
+
activation,
|
|
3019
|
+
...hostLockPackages ? { hostLockPackages } : {},
|
|
3020
|
+
...config.hostLockPlatform ? { hostLockPlatform: config.hostLockPlatform } : {},
|
|
3021
|
+
...config.hostLockProfile ? { hostLockProfile: config.hostLockProfile } : {}
|
|
3022
|
+
};
|
|
991
3023
|
}
|
|
992
3024
|
|
|
993
3025
|
//#endregion
|
|
994
3026
|
//#region src/runtime.ts
|
|
995
|
-
const MAX_CONTINUATION_ATTEMPTS_PER_TURN = 2;
|
|
996
3027
|
const name = "context-guard";
|
|
997
3028
|
const inject = ["sessions", "commands"];
|
|
998
|
-
|
|
3029
|
+
/**
|
|
3030
|
+
* Bind an explicit mutation to one live root-owned contract item. Resolution
|
|
3031
|
+
* evidence is intentionally not authority: every effect rechecks the current
|
|
3032
|
+
* projection immediately before any command, HTTP request, or durable intent.
|
|
3033
|
+
*/
|
|
3034
|
+
function authorizeMutationFromProjection(projection, request) {
|
|
3035
|
+
if (!projection.enabled) return {
|
|
3036
|
+
status: "denied",
|
|
3037
|
+
reasonCode: "mutation_guard_disabled"
|
|
3038
|
+
};
|
|
3039
|
+
if (projection.integrity !== "valid") return {
|
|
3040
|
+
status: "denied",
|
|
3041
|
+
reasonCode: "mutation_integrity_unavailable"
|
|
3042
|
+
};
|
|
3043
|
+
if (projection.hostStatus !== "supported") return {
|
|
3044
|
+
status: "denied",
|
|
3045
|
+
reasonCode: "mutation_host_lock_unavailable"
|
|
3046
|
+
};
|
|
3047
|
+
const item = projection.items.get(request.contractItemId);
|
|
3048
|
+
if (!item) return {
|
|
3049
|
+
status: "denied",
|
|
3050
|
+
reasonCode: "mutation_contract_item_missing"
|
|
3051
|
+
};
|
|
3052
|
+
if (!Number.isSafeInteger(request.contractItemRevision) || item.revision !== request.contractItemRevision) return {
|
|
3053
|
+
status: "denied",
|
|
3054
|
+
reasonCode: "mutation_contract_item_revision_mismatch"
|
|
3055
|
+
};
|
|
3056
|
+
if (item.status !== "pending") return {
|
|
3057
|
+
status: "denied",
|
|
3058
|
+
reasonCode: "mutation_contract_item_not_pending"
|
|
3059
|
+
};
|
|
3060
|
+
if (item.kind !== "requirement" || item.verification.enforced !== true) return {
|
|
3061
|
+
status: "denied",
|
|
3062
|
+
reasonCode: "mutation_contract_item_not_authorizing"
|
|
3063
|
+
};
|
|
3064
|
+
if (item.authority !== "root_instruction" && item.authority !== "root_adoption") return {
|
|
3065
|
+
status: "denied",
|
|
3066
|
+
reasonCode: "mutation_root_authority_unavailable"
|
|
3067
|
+
};
|
|
3068
|
+
if (item.legacyFlags?.length) return {
|
|
3069
|
+
status: "denied",
|
|
3070
|
+
reasonCode: "mutation_legacy_rebind_required"
|
|
3071
|
+
};
|
|
3072
|
+
if (item.semanticAction !== request.action) return {
|
|
3073
|
+
status: "denied",
|
|
3074
|
+
reasonCode: "mutation_semantic_action_mismatch"
|
|
3075
|
+
};
|
|
3076
|
+
if (item.targetCaptureStatus !== "resolved") return {
|
|
3077
|
+
status: "denied",
|
|
3078
|
+
reasonCode: "mutation_target_clarification_required"
|
|
3079
|
+
};
|
|
3080
|
+
if (!requestedTargetAuthorizesMutation(request.action, item.requestedTarget, request.resolvedTarget)) return {
|
|
3081
|
+
status: "denied",
|
|
3082
|
+
reasonCode: "mutation_requested_target_mismatch"
|
|
3083
|
+
};
|
|
3084
|
+
if ([...projection.items.values()].some((candidate) => candidate.status === "pending" && candidate.kind === "prohibition" && (candidate.authority === "root_instruction" || candidate.authority === "root_adoption") && !candidate.legacyFlags?.length && candidate.semanticAction === request.action && requestedTargetMatchesResolved(request.action, candidate.requestedTarget, request.resolvedTarget))) return {
|
|
3085
|
+
status: "denied",
|
|
3086
|
+
reasonCode: "mutation_conflicting_prohibition"
|
|
3087
|
+
};
|
|
3088
|
+
return {
|
|
3089
|
+
status: "authorized",
|
|
3090
|
+
reasonCode: "mutation_root_contract_authorized"
|
|
3091
|
+
};
|
|
3092
|
+
}
|
|
3093
|
+
const PROTOCOL_CORRECTION_NOTICE = "Context Guard protocol correction: root-authorized work remains pending; continue with tools or obtain a typed boundary.";
|
|
3094
|
+
/** Production Stop boundary: durable replay first, then immutable/live checks. */
|
|
3095
|
+
async function handleGuardTurnStopping(agent, runtime, access$1) {
|
|
3096
|
+
const durable = await access$1.flush();
|
|
3097
|
+
runtime.setDurability(durable);
|
|
3098
|
+
runtime.sync();
|
|
3099
|
+
if (!durable) return "boundary_flush_failed";
|
|
3100
|
+
const decision = decideTurnBoundary(runtime.projection);
|
|
3101
|
+
if (decision.action === "continue") {
|
|
3102
|
+
agent.steer(createUserMessage({
|
|
3103
|
+
content: [{
|
|
3104
|
+
type: "text",
|
|
3105
|
+
text: PROTOCOL_CORRECTION_NOTICE
|
|
3106
|
+
}],
|
|
3107
|
+
source: {
|
|
3108
|
+
kind: "plugin",
|
|
3109
|
+
plugin: "context-guard",
|
|
3110
|
+
form: "notice",
|
|
3111
|
+
summary: boundContextSummary("requesting the one allowed protocol correction step")
|
|
3112
|
+
}
|
|
3113
|
+
}));
|
|
3114
|
+
return decision.reason ?? "protocol_correction_steer";
|
|
3115
|
+
}
|
|
3116
|
+
if (decision.reason !== "accepted_boundary_pending_effectuation") return decision.reason ?? "safe_yield_pending_preserved";
|
|
3117
|
+
const boundary = runtime.projection.boundaries.at(-1);
|
|
3118
|
+
if (!boundary || !isCurrentAcceptedBoundary(runtime.projection, boundary)) return "boundary_candidate_stale";
|
|
3119
|
+
if (boundary.goalRef && (!access$1.goalAccess || !access$1.hostSupported)) {
|
|
3120
|
+
runtime.projection.integrity = "unknown";
|
|
3121
|
+
runtime.projection.integrityViolations.push("boundary_host_lock_unsupported");
|
|
3122
|
+
return "boundary_host_lock_unsupported";
|
|
3123
|
+
}
|
|
3124
|
+
const requalify = boundary.disposition === "external_wait" ? async () => access$1.externalWaitCapability?.status === "supported" && boundary.qualificationIds.every((id) => {
|
|
3125
|
+
const row = access$1.readExternalOperation(id);
|
|
3126
|
+
return row?.status === "running" || row?.status === "pending";
|
|
3127
|
+
}) : void 0;
|
|
3128
|
+
const effect = await effectuateBoundary(boundary, {
|
|
3129
|
+
...access$1.goalAccess ?? {
|
|
3130
|
+
get: async () => void 0,
|
|
3131
|
+
disarm: async () => void 0
|
|
3132
|
+
},
|
|
3133
|
+
...requalify ? { requalify } : {}
|
|
3134
|
+
});
|
|
3135
|
+
if (effect.resumeRequired) {
|
|
3136
|
+
runtime.projection.integrity = "unknown";
|
|
3137
|
+
runtime.projection.integrityViolations.push(effect.reasonCode);
|
|
3138
|
+
}
|
|
3139
|
+
return effect.reasonCode;
|
|
3140
|
+
}
|
|
3141
|
+
function createHostCapabilityEvaluator(hostLock) {
|
|
3142
|
+
return (action) => evaluateHostCapability(hostLock, { action });
|
|
3143
|
+
}
|
|
3144
|
+
function sessionHeaderForDigest(session) {
|
|
3145
|
+
const raw = session.header;
|
|
3146
|
+
if (!raw || typeof raw.version !== "number" || typeof raw.id !== "string" || typeof raw.createdAt !== "number") return void 0;
|
|
3147
|
+
return {
|
|
3148
|
+
version: raw.version,
|
|
3149
|
+
id: raw.id,
|
|
3150
|
+
createdAt: raw.createdAt,
|
|
3151
|
+
...typeof raw.parentSession === "string" ? { parentSession: raw.parentSession } : {},
|
|
3152
|
+
...typeof raw.seedLength === "number" ? { seedLength: raw.seedLength } : {},
|
|
3153
|
+
...typeof raw.agentPreset === "string" ? { agentPreset: raw.agentPreset } : {},
|
|
3154
|
+
...typeof raw.origin === "string" ? { origin: raw.origin } : {},
|
|
3155
|
+
...typeof raw.delegationDepth === "number" ? { delegationDepth: raw.delegationDepth } : {}
|
|
3156
|
+
};
|
|
3157
|
+
}
|
|
3158
|
+
function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalState) {
|
|
999
3159
|
const projection = createProjection();
|
|
1000
3160
|
const session = agent.session;
|
|
1001
3161
|
let pendingRecovery = false;
|
|
@@ -1003,12 +3163,27 @@ function createRuntime(agent, config) {
|
|
|
1003
3163
|
let observedEpoch = -1;
|
|
1004
3164
|
let observedCompactionSeq = -1;
|
|
1005
3165
|
const continuationAttempts = projection.continuationAttempts;
|
|
3166
|
+
const persistenceCorrectionAttempts = projection.persistenceCorrectionAttempts;
|
|
1006
3167
|
const rebuild = () => {
|
|
1007
3168
|
const header = session.header;
|
|
1008
3169
|
const priorRecoveryDigest = projection.lastRecoveryDigest;
|
|
1009
|
-
const derived = deriveProjection(session.events, { activation: config.activation }, {
|
|
3170
|
+
const derived = deriveProjection(session.events, { activation: config.activation }, {
|
|
3171
|
+
cwd: typeof header?.cwd === "string" ? header.cwd : "",
|
|
3172
|
+
sessionHeader: sessionHeaderForDigest(session)
|
|
3173
|
+
}, durabilityConfirmed, hostLock);
|
|
1010
3174
|
Object.assign(projection, derived.projection);
|
|
3175
|
+
if (readGoalState) try {
|
|
3176
|
+
const state = normalizeGoalState(readGoalState());
|
|
3177
|
+
if (state && projection.currentGoalRef?.id === state.id && projection.currentGoalRef.revision === state.revision) {
|
|
3178
|
+
projection.currentGoalPhase = state.phase;
|
|
3179
|
+
projection.currentGoalActivation = state.activation;
|
|
3180
|
+
}
|
|
3181
|
+
} catch {
|
|
3182
|
+
projection.integrity = "unknown";
|
|
3183
|
+
projection.integrityViolations.push("goal_readback_unavailable");
|
|
3184
|
+
}
|
|
1011
3185
|
projection.continuationAttempts = continuationAttempts;
|
|
3186
|
+
projection.persistenceCorrectionAttempts = persistenceCorrectionAttempts;
|
|
1012
3187
|
projection.lastRecoveryDigest = priorRecoveryDigest;
|
|
1013
3188
|
if (observedEpoch >= 0 && derived.projection.epoch > observedEpoch) {
|
|
1014
3189
|
pendingRecovery = true;
|
|
@@ -1050,24 +3225,88 @@ function createRuntime(agent, config) {
|
|
|
1050
3225
|
}
|
|
1051
3226
|
function apply(ctx, rawConfig = {}) {
|
|
1052
3227
|
const config = resolveConfig(rawConfig);
|
|
3228
|
+
const installedHostLock = evaluateHostLock(config.hostLockPackages ?? [], {
|
|
3229
|
+
platform: config.hostLockPlatform,
|
|
3230
|
+
profileKind: config.hostLockProfile
|
|
3231
|
+
});
|
|
1053
3232
|
const runtimes = /* @__PURE__ */ new Map();
|
|
3233
|
+
const hostLocks = /* @__PURE__ */ new Map();
|
|
3234
|
+
const registeredAgents = /* @__PURE__ */ new WeakSet();
|
|
1054
3235
|
const ensure = (agent) => {
|
|
1055
3236
|
let runtime = runtimes.get(agent);
|
|
1056
3237
|
if (!runtime) {
|
|
1057
|
-
|
|
3238
|
+
const goals = optionalGoalService(ctx, agent);
|
|
3239
|
+
const agentHostLock = bindLiveGoalCapability(installedHostLock, Boolean(goals) && hasPinnedUpdateGoalTool(agent));
|
|
3240
|
+
runtime = createRuntime(agent, config, agentHostLock, goals ? () => goals.get(agent) : void 0);
|
|
1058
3241
|
runtimes.set(agent, runtime);
|
|
3242
|
+
hostLocks.set(agent, agentHostLock);
|
|
1059
3243
|
}
|
|
1060
3244
|
return runtime;
|
|
1061
3245
|
};
|
|
1062
3246
|
ctx.commands.register(createContextGuardCommand((agent) => ensure(agent).projection, (agent, enabled) => ensure(agent).setEnabled(enabled), (agent) => ensure(agent).sync()));
|
|
1063
3247
|
ctx.on("agent/session-start", ({ agent, source }) => {
|
|
3248
|
+
ensureProtocolBoundary(agent);
|
|
1064
3249
|
const runtime = ensure(agent);
|
|
1065
3250
|
runtime.sync();
|
|
1066
3251
|
if (source === "resume" || source === "compact") {
|
|
1067
3252
|
runtime.projection.lastRecoveryDigest = void 0;
|
|
1068
3253
|
runtime.markRecoveryNeeded();
|
|
1069
3254
|
}
|
|
1070
|
-
|
|
3255
|
+
if (registeredAgents.has(agent)) return;
|
|
3256
|
+
registeredAgents.add(agent);
|
|
3257
|
+
agent.ctx.tools.register(createCheckpointTool(() => runtime.projection, () => runtime.markRecoveryNeeded(), async () => {
|
|
3258
|
+
const durable = await ctx.sessions.flush(agent.session);
|
|
3259
|
+
runtime.setDurability(durable);
|
|
3260
|
+
runtime.sync();
|
|
3261
|
+
return durable;
|
|
3262
|
+
}));
|
|
3263
|
+
agent.ctx.tools.register(createBoundaryTool(() => runtime.projection, async () => {
|
|
3264
|
+
const durable = await ctx.sessions.flush(agent.session);
|
|
3265
|
+
runtime.setDurability(durable);
|
|
3266
|
+
runtime.sync();
|
|
3267
|
+
return durable;
|
|
3268
|
+
}, () => runtime.markRecoveryNeeded()));
|
|
3269
|
+
const evidenceOptions = {
|
|
3270
|
+
hostCapability: createHostCapabilityEvaluator(hostLocks.get(agent) ?? installedHostLock),
|
|
3271
|
+
prepareMutation: async (toolAgent) => {
|
|
3272
|
+
if (toolAgent.session !== agent.session) return false;
|
|
3273
|
+
const durable = await ctx.sessions.flush(agent.session);
|
|
3274
|
+
runtime.setDurability(durable);
|
|
3275
|
+
runtime.sync();
|
|
3276
|
+
return durable;
|
|
3277
|
+
},
|
|
3278
|
+
authorizeMutation: (request) => {
|
|
3279
|
+
runtime.sync();
|
|
3280
|
+
return authorizeMutationFromProjection(runtime.projection, request);
|
|
3281
|
+
},
|
|
3282
|
+
marketOrigin: optionalMarketOrigin(ctx, agent),
|
|
3283
|
+
persistRestartIntent: async (toolAgent, intent) => {
|
|
3284
|
+
const session = toolAgent.session;
|
|
3285
|
+
session.append.bind(session)("user/message", createUserMessage({
|
|
3286
|
+
content: [{
|
|
3287
|
+
type: "text",
|
|
3288
|
+
text: `${RESTART_INTENT_PREFIX}${JSON.stringify({
|
|
3289
|
+
resolution_call_id: intent.resolutionCallId,
|
|
3290
|
+
service_id: intent.serviceId,
|
|
3291
|
+
pre_generation: intent.preGeneration
|
|
3292
|
+
})}`
|
|
3293
|
+
}],
|
|
3294
|
+
source: {
|
|
3295
|
+
kind: "plugin",
|
|
3296
|
+
plugin: "context-guard",
|
|
3297
|
+
form: "notice",
|
|
3298
|
+
summary: boundContextSummary("persisting a restart handoff intent")
|
|
3299
|
+
}
|
|
3300
|
+
}), { surfaceOp: "append" });
|
|
3301
|
+
const durable = await ctx.sessions.flush(session);
|
|
3302
|
+
runtime.setDurability(durable);
|
|
3303
|
+
runtime.sync();
|
|
3304
|
+
return durable;
|
|
3305
|
+
}
|
|
3306
|
+
};
|
|
3307
|
+
agent.ctx.tools.register(createEvidenceTool(evidenceOptions));
|
|
3308
|
+
agent.ctx.tools.register(createActionTool(evidenceOptions));
|
|
3309
|
+
agent.ctx.tools.register(createExternalOperationTool((id, toolAgent) => readExternalOperation(ctx, toolAgent, id), () => evaluateExternalWaitCapability(hostLocks.get(agent) ?? installedHostLock)));
|
|
1071
3310
|
agent.ctx.tools.guard((exec) => goalCompletionDenial(runtime.projection, exec.name, exec.arguments));
|
|
1072
3311
|
});
|
|
1073
3312
|
ctx.on("agent/pre-step", async ({ agent }, next) => {
|
|
@@ -1078,9 +3317,9 @@ function apply(ctx, rawConfig = {}) {
|
|
|
1078
3317
|
const decision = await next();
|
|
1079
3318
|
if (decision.kind === "enter" && runtime.projection.enabled && runtime.consumeRecovery()) {
|
|
1080
3319
|
const recovery = renderRecoveryPacket(runtime.projection, { charBudget: 4e3 });
|
|
1081
|
-
const digest = recovery ? recoveryDigest(recovery, runtime.projection) : void 0;
|
|
1082
|
-
if (recovery && digest !== runtime.projection.lastRecoveryDigest) {
|
|
1083
|
-
runtime.projection.lastRecoveryDigest = digest;
|
|
3320
|
+
const digest$1 = recovery ? recoveryDigest(recovery, runtime.projection) : void 0;
|
|
3321
|
+
if (recovery && digest$1 !== runtime.projection.lastRecoveryDigest) {
|
|
3322
|
+
runtime.projection.lastRecoveryDigest = digest$1;
|
|
1084
3323
|
decision.messages = [...decision.messages, createUserMessage({
|
|
1085
3324
|
content: [{
|
|
1086
3325
|
type: "text",
|
|
@@ -1097,29 +3336,134 @@ function apply(ctx, rawConfig = {}) {
|
|
|
1097
3336
|
}
|
|
1098
3337
|
return decision;
|
|
1099
3338
|
});
|
|
1100
|
-
ctx.on("agent/turn-stopping", async ({ agent
|
|
1101
|
-
const durability = await ctx.sessions.flush(agent.session);
|
|
3339
|
+
ctx.on("agent/turn-stopping", async ({ agent }) => {
|
|
1102
3340
|
const runtime = ensure(agent);
|
|
1103
|
-
|
|
1104
|
-
runtime
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
kind: "plugin",
|
|
1115
|
-
plugin: "context-guard",
|
|
1116
|
-
form: "notice",
|
|
1117
|
-
summary: boundContextSummary("completion requires a Context Guard checkpoint")
|
|
1118
|
-
}
|
|
1119
|
-
}));
|
|
1120
|
-
}
|
|
3341
|
+
const goals = optionalGoalService(ctx, agent);
|
|
3342
|
+
await handleGuardTurnStopping(agent, runtime, {
|
|
3343
|
+
flush: () => ctx.sessions.flush(agent.session),
|
|
3344
|
+
hostSupported: runtime.projection.hostStatus === "supported",
|
|
3345
|
+
externalWaitCapability: evaluateExternalWaitCapability(hostLocks.get(agent) ?? installedHostLock),
|
|
3346
|
+
...goals ? { goalAccess: {
|
|
3347
|
+
get: async () => normalizeGoalState(await goals.get(agent)),
|
|
3348
|
+
disarm: async () => normalizeGoalState(await goals.disarm(agent))
|
|
3349
|
+
} } : {},
|
|
3350
|
+
readExternalOperation: (id) => readExternalOperation(ctx, agent, id)
|
|
3351
|
+
});
|
|
1121
3352
|
});
|
|
1122
3353
|
}
|
|
3354
|
+
function readExternalOperation(ctx, agent, id) {
|
|
3355
|
+
if (!agent || !id) return void 0;
|
|
3356
|
+
for (const owner of [agent.ctx, ctx]) try {
|
|
3357
|
+
const service = owner.get?.("jobs") ?? owner.jobs;
|
|
3358
|
+
if (!service || typeof service !== "object") continue;
|
|
3359
|
+
const row = typeof service.get === "function" ? service.get(id, agent) : void 0;
|
|
3360
|
+
if (!row) return void 0;
|
|
3361
|
+
const raw = String(row.status ?? "unknown");
|
|
3362
|
+
return {
|
|
3363
|
+
id,
|
|
3364
|
+
status: raw === "running" ? "running" : raw === "stopping" ? "pending" : raw === "completed" ? "completed" : raw === "killed" || raw === "failed" ? "failed" : "unknown",
|
|
3365
|
+
adapterId: "dsh.jobs.v1"
|
|
3366
|
+
};
|
|
3367
|
+
} catch {
|
|
3368
|
+
return;
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
function optionalMarketOrigin(ctx, agent) {
|
|
3372
|
+
for (const owner of [agent.ctx, ctx]) try {
|
|
3373
|
+
const service = owner.get?.("webServer");
|
|
3374
|
+
if (!service || service.host !== "127.0.0.1" && service.host !== "::1" || typeof service.port !== "number" || !Number.isInteger(service.port) || service.port < 1 || service.port > 65535) continue;
|
|
3375
|
+
return `http://${service.host === "::1" ? "[::1]" : "127.0.0.1"}:${service.port}`;
|
|
3376
|
+
} catch {}
|
|
3377
|
+
}
|
|
3378
|
+
function ensureProtocolBoundary(agent) {
|
|
3379
|
+
if (agent.session.events.some((event) => {
|
|
3380
|
+
if (event.type !== "user/message" || !event.data || typeof event.data !== "object") return false;
|
|
3381
|
+
const data = event.data;
|
|
3382
|
+
const source = data.source && typeof data.source === "object" ? data.source : void 0;
|
|
3383
|
+
const content = Array.isArray(data.content) ? data.content : [];
|
|
3384
|
+
return (content.length === 1 && content[0] && typeof content[0] === "object" ? content[0].text : void 0) === PROTOCOL_V3_NOTICE && source?.kind === "plugin" && source.plugin === "context-guard" && source.form === "notice";
|
|
3385
|
+
})) return;
|
|
3386
|
+
agent.session.append.bind(agent.session)("user/message", createUserMessage({
|
|
3387
|
+
content: [{
|
|
3388
|
+
type: "text",
|
|
3389
|
+
text: PROTOCOL_V3_NOTICE
|
|
3390
|
+
}],
|
|
3391
|
+
source: {
|
|
3392
|
+
kind: "plugin",
|
|
3393
|
+
plugin: "context-guard",
|
|
3394
|
+
form: "notice",
|
|
3395
|
+
summary: boundContextSummary("Context Guard upgraded its replay contract to v3")
|
|
3396
|
+
}
|
|
3397
|
+
}), { surfaceOp: "append" });
|
|
3398
|
+
}
|
|
3399
|
+
function hasPinnedUpdateGoalTool(agent) {
|
|
3400
|
+
try {
|
|
3401
|
+
const tool = agent.ctx.tools?.get?.("update_goal", agent);
|
|
3402
|
+
if (!tool || typeof tool !== "object") return false;
|
|
3403
|
+
const row = tool;
|
|
3404
|
+
if (row.name !== "update_goal" || typeof row.execute !== "function") return false;
|
|
3405
|
+
const parameters = row.parameters;
|
|
3406
|
+
if (!parameters || parameters.type !== "object" || !parameters.properties || typeof parameters.properties !== "object") return false;
|
|
3407
|
+
const fields = parameters.properties;
|
|
3408
|
+
const requiredNames = parameters.required;
|
|
3409
|
+
if (!Array.isArray(requiredNames) || JSON.stringify([...requiredNames].sort()) !== JSON.stringify([
|
|
3410
|
+
"action",
|
|
3411
|
+
"goal_id",
|
|
3412
|
+
"revision"
|
|
3413
|
+
])) return false;
|
|
3414
|
+
if (JSON.stringify(Object.keys(fields).sort()) !== JSON.stringify([
|
|
3415
|
+
"action",
|
|
3416
|
+
"blocked_reason",
|
|
3417
|
+
"goal_id",
|
|
3418
|
+
"max_goal_rounds",
|
|
3419
|
+
"objective",
|
|
3420
|
+
"revision"
|
|
3421
|
+
])) return false;
|
|
3422
|
+
const required = (name$1, type) => {
|
|
3423
|
+
const field = fields[name$1];
|
|
3424
|
+
return Boolean(field && typeof field === "object" && field.type === type && requiredNames.includes(name$1));
|
|
3425
|
+
};
|
|
3426
|
+
const action = fields.action;
|
|
3427
|
+
return required("goal_id", "string") && required("revision", "number") && required("action", "string") && Array.isArray(action?.enum) && JSON.stringify(action.enum) === JSON.stringify([
|
|
3428
|
+
"edit",
|
|
3429
|
+
"pause",
|
|
3430
|
+
"resume",
|
|
3431
|
+
"complete",
|
|
3432
|
+
"blocked"
|
|
3433
|
+
]);
|
|
3434
|
+
} catch {
|
|
3435
|
+
return false;
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
function optionalGoalService(ctx, agent) {
|
|
3439
|
+
for (const owner of [agent.ctx, ctx]) try {
|
|
3440
|
+
const service = owner.get?.("goals");
|
|
3441
|
+
if (service && typeof service.get === "function" && typeof service.disarm === "function") return service;
|
|
3442
|
+
} catch {}
|
|
3443
|
+
}
|
|
3444
|
+
function normalizeGoalState(value) {
|
|
3445
|
+
if (!value || typeof value !== "object") return void 0;
|
|
3446
|
+
const record$1 = value;
|
|
3447
|
+
const goal = record$1.goal && typeof record$1.goal === "object" ? record$1.goal : record$1;
|
|
3448
|
+
const id = goal.id;
|
|
3449
|
+
const revision = goal.revision;
|
|
3450
|
+
const phase = goal.phase;
|
|
3451
|
+
const activation = record$1.activation ?? goal.activation;
|
|
3452
|
+
if (typeof id !== "string" || typeof revision !== "number") return void 0;
|
|
3453
|
+
if (![
|
|
3454
|
+
"active",
|
|
3455
|
+
"paused",
|
|
3456
|
+
"blocked",
|
|
3457
|
+
"complete"
|
|
3458
|
+
].includes(String(phase))) return void 0;
|
|
3459
|
+
if (activation !== "armed" && activation !== "disarmed") return void 0;
|
|
3460
|
+
return {
|
|
3461
|
+
id,
|
|
3462
|
+
revision,
|
|
3463
|
+
phase,
|
|
3464
|
+
activation
|
|
3465
|
+
};
|
|
3466
|
+
}
|
|
1123
3467
|
|
|
1124
3468
|
//#endregion
|
|
1125
|
-
export { COMMAND_SURFACE_MANIFEST, Config, DEFAULT_RECOVERY_CHAR_BUDGET, apply, bindingSatisfies, canonicalizePath, captureClause, captureItem, certifyCheckpoint, classifyClause, classifyCompletionClaim, classifyUserInteraction, closingHint, createProjection, decideTurnStopping, deriveProjection, digestStrings, evidenceCoverage, evidenceFromPersistedToolResult, evidenceMatchesItem, extractArtifactPaths, extractMethod, extractOperation, extractTextContent, extractToolSubject, goalCompletionDenial, hasCurrentCertificate, inject, isDeterministicCheck, isInformationalMessage, isRunExecutable, isVerifyingCapability, isWholeTaskCompletionClaim, latestAssistantText, name, normalizeClause, openItems, parsePwshCommand, parseShellCommand, recoveryDigest, renderRecoveryPacket, sanitizeClauseText, sanitizeUrl, segmentClauses, sha256, supersedeItem, validateManifest, withDurability };
|
|
3469
|
+
export { ACTION_MANIFEST, ACTION_MANIFEST_VERSION, BASE_HOST_PACKAGES, CERTIFICATE_VERSION, COMMAND_SURFACE_MANIFEST, Config, DEFAULT_HOST_LOCK, DEFAULT_RECOVERY_CHAR_BUDGET, EXPECTED_HOST_PACKAGES, GIT_COMMAND_MANIFEST_IDS, GOAL_HOST_PACKAGES, HOST_CAPABILITY_PACKAGE_GROUPS, HostProfileError, PROTOCOL_V3_NOTICE, SEMANTIC_ACTIONS, STATEFUL_ACTIONS, STOP_PROTOCOL_VERSION, SUPPORTED_EVIDENCE_ADAPTERS, SUPPORTED_HOST_MANIFEST, actionCompatible, apply, authorityCaptureCounts, availableBoundaryQualifications, bindExecutableIdentity, bindLiveGoalCapability, bindingSatisfies, canonicalArgvFromCommand, canonicalizePath, captureClause, captureItem, certifyCheckpoint, classifyClause, classifyCompletionClaim, classifyUserInteraction, closingHint, commitIndexSnapshotDigest, commitTreeSnapshotDigest, createGitPrestateEnvelope, createProjection, currentContractDigest, decideTurnBoundary, decideTurnStopping, deriveProjection, digestStrings, effectuateBoundary, evaluateExternalWaitCapability, evaluateHostCapability, evaluateHostLock, evaluateToolSurfaceCapability, evidenceCoverage, evidenceFromPersistedToolResult, evidenceMatchesItem, executeRevalidatedGitEffect, extractArtifactPaths, extractMethod, extractOperation, extractTextContent, extractToolSubject, gitCommandMatchesTarget, goalCompletionDenial, hasCurrentCertificate, hostLockContextFromComposedDump, hostLockRowsFromComposedDump, inject, injectActiveProfileHostLock, isCurrentAcceptedBoundary, isDeterministicCheck, isInformationalMessage, isRunExecutable, isStatefulAction, isVerifyingCapability, isWholeTaskCompletionClaim, latestAssistantText, name, normalizeClause, observeAssistantOutcome, openItems, packageRowsFromActiveGraph, packageRowsFromPnpmLock, parseGitCommandManifest, parsePwshCommand, parseShellCommand, qualifyBoundary, recoveryDigest, renderRecoveryPacket, requestedTargetAuthorizesMutation, requestedTargetMatchesResolved, resolveActiveProfileHostLock, resolveInstalledHostLock, revalidateGitPrestate, sanitizeClauseText, sanitizeUrl, segmentAuthorityBlocks, segmentClauses, semanticActionFromCommand, semanticActionFromText, sha256, supersedeItem, validateActionManifest, validateActionTarget, validateManifest, verifiedLinearCommitReadback, verifyComposedHostLockDump, withDurability };
|