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/dist/index.js CHANGED
@@ -1,30 +1,2061 @@
1
- import { A as classifyClause, B as normalizeClause, C as renderRecoveryPacket, D as isVerifyingCapability, E as evidenceMatchesItem, F as segmentClauses, H as sanitizeUrl, I as COMMAND_SURFACE_MANIFEST, L as validateManifest, M as extractMethod, N as extractOperation, O as captureClause, P as isInformationalMessage, R as canonicalizePath, S as recoveryDigest, T as evidenceCoverage, U as sha256, V as sanitizeClauseText, W as createProjection, _ as classifyUserInteraction, a as goalCompletionDenial, b as closingHint, c as supersedeItem, d as extractToolSubject, f as isDeterministicCheck, g as parseShellCommand, h as parsePwshCommand, i as latestAssistantText, j as extractArtifactPaths, k as captureItem, l as evidenceFromPersistedToolResult, m as isRunExecutable, n as decideTurnStopping, o as hasCurrentCertificate, p as withDurability, r as isWholeTaskCompletionClaim, s as deriveProjection, t as classifyCompletionClaim, u as extractTextContent, v as certifyCheckpoint, w as bindingSatisfies, x as openItems, y as DEFAULT_RECOVERY_CHAR_BUDGET, z as digestStrings } from "./domain-BN3_AuUr.js";
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 createCheckpointTool(getProjection, onRejected) {
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: "context_guard_checkpoint",
9
- description: "Request a completion certificate from existing durable evidence.",
10
- parameters: { bindings: {
11
- type: "array",
12
- required: true,
13
- items: {
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
- item_id: {
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
- evidence_ids: {
22
- type: "array",
1875
+ semantic_action: {
1876
+ type: "string",
1877
+ required: true
1878
+ },
1879
+ evidence_role: {
1880
+ type: "string",
23
1881
  required: true,
24
- items: { type: "string" }
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
- "certified",
38
- "incomplete",
2069
+ "running",
2070
+ "pending",
2071
+ "completed",
2072
+ "failed",
39
2073
  "unknown"
40
2074
  ]
41
2075
  },
42
- contract_revision: { type: "integer" },
43
- open_items: {
44
- type: "array",
45
- items: { type: "string" }
2076
+ operation_id: {
2077
+ type: "string",
2078
+ required: true
46
2079
  },
47
- available_evidence: {
48
- type: "array",
49
- items: {
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
- rejected_bindings: {
72
- type: "array",
73
- items: {
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
- async execute(args) {
91
- const projection = getProjection();
92
- if (!projection) return {
2102
+ execute(args, exec) {
2103
+ if (capability().status !== "supported") return Promise.resolve({
93
2104
  status: "unknown",
94
- contract_revision: 0,
95
- open_items: [],
96
- available_evidence: [],
97
- rejected_bindings: []
98
- };
99
- const result = certifyCheckpoint(projection, args.bindings.map((binding) => ({
100
- itemId: binding.item_id,
101
- evidenceIds: binding.evidence_ids
102
- })), `C${projection.checkpoints.length + 1}`);
103
- if (!result.checkpoint) onRejected();
104
- 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) => ({
105
- id: evidence.id,
106
- tool: evidence.toolName,
107
- subjects: evidence.subjects,
108
- surfaces: evidence.surfaces,
109
- outcome: evidence.outcome,
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({ activation: Schema.string().default("opt-in") });
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
- return { activation };
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
- function createRuntime(agent, config) {
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 }, { cwd: typeof header?.cwd === "string" ? header.cwd : "" }, durabilityConfirmed);
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
- runtime = createRuntime(agent, config);
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
- agent.ctx.tools.register(createCheckpointTool(() => runtime.projection, () => runtime.markRecoveryNeeded()));
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, turn }) => {
1101
- const durability = await ctx.sessions.flush(agent.session);
3339
+ ctx.on("agent/turn-stopping", async ({ agent }) => {
1102
3340
  const runtime = ensure(agent);
1103
- runtime.setDurability(durability);
1104
- runtime.sync();
1105
- const assistantText = latestAssistantText(runtime.session.events);
1106
- if (decideTurnStopping(runtime.projection, assistantText, turn, MAX_CONTINUATION_ATTEMPTS_PER_TURN).action === "continue") {
1107
- const recovery = renderRecoveryPacket(runtime.projection, { charBudget: 4e3 });
1108
- agent.steer(createUserMessage({
1109
- content: [{
1110
- type: "text",
1111
- text: `Completion is not certified. ${recovery}`
1112
- }],
1113
- source: {
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 };