dsh-completion-guard 0.5.3 → 0.6.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,4 +1,4 @@
1
- import { $ as parseShellCommand, $n as isStatefulAction, $t as isWholeTaskCompletionClaim, A as createGitPrestateEnvelope, An as extractMethod, At as RC1_HOST_PACKAGES, B as CAPTURE_V042_NOTICE, Bn as namedActions, Bt as renderRecoveryPacket, C as SESSION_EVENT_ENVELOPE_INVALID, Cn as evidenceAvailabilityReason, Ct as SUPPORTED_HOST_VERSIONS, D as GIT_COMMAND_TEMPLATES, Dn as captureItem, Dt as satisfiesSupportedHostRange, E as GIT_COMMAND_MANIFEST_IDS, En as captureClause, Et as parseHostVersion, F as verifiedLinearCommitReadback, Fn as interpretMessage, Ft as DEFAULT_RECOVERY_CHAR_BUDGET, G as evidenceFromPersistedToolResult, Gn as ACTION_MANIFEST, Gt as CONTROL_RECORD_PREFIX, H as PROTOCOL_V4_NOTICE, Hn as statefulActionsOfScope, Ht as evidenceCoverage, I as FIRST_STEP_GUIDANCE, In as isExecutableItem, It as MIN_RECOVERY_CHAR_BUDGET, J as isDeterministicCheck, Jn as SEMANTIC_ACTIONS, Jt as classifyCompletionClaim, K as extractTextContent, Kn as ACTION_MANIFEST_VERSION, Kt as NO_PROGRESS_RECORD_PREFIX, L as claimedBatchHasRealRootInput, Ln as isOpenObligation, Lt as closingHint, M as gitCommandMatchesTarget, Mn as isInformationalMessage, Mt as authorityCaptureCounts, N as parseGitCommandManifest, Nn as segmentClauses, Nt as segmentAuthorityBlocks, O as commitIndexSnapshotDigest, On as classifyClause, Ot as RC015_RC2_HOST_PACKAGES, P as revalidateGitPrestate, Pn as interpretClause, Pt as certifyCheckpoint, Q as parsePwshCommand, Qn as actionCompatible, Qt as isRootPauseRequest, R as lifecyclePhase, Rn as kindOfScope, Rt as openItems, S as SESSION_API_UNSUPPORTED, Sn as deriveItemDiagnosis, St as SUPPORTED_HOST_RANGE, T as snapshotSessionEvents, Tn as relevantEvidence, Tt as evaluateMinimumHostVersion, U as deriveProjection, Un as canonicalRegistryBase, Ut as evidenceMatchesItem, V as PROTOCOL_V3_NOTICE, Vn as semanticActionOfScope, Vt as bindingSatisfies, W as supersedeItem, Wn as npmEscapedPackageName, Wt as isVerifyingCapability, X as canonicalArgvFromCommand, Xn as STOP_PROTOCOL_VERSION, Xt as decideTurnStopping, Y as withDurability, Yn as STATEFUL_ACTIONS, Yt as decideTurnBoundary, Z as isRunExecutable, Zn as SUPPORTED_EVIDENCE_ADAPTERS, Zt as decisionBoundaryKey, _ as packageRowsFromPnpmLock, _n as rebindResponse, _t as evaluateToolSurfaceCapability, a as createProofManifest, an as hasCurrentCertificate, ar as validateActionTarget, at as BASE_HOST_PACKAGES, b as resolveInstalledHostLock, bn as isFrozenV042RebindResponse, bt as LATEST_SUPPORTED_HOST_VERSION, c as sessionQuery, cn as isCurrentAcceptedBoundary, cr as classifyTaskIntent, ct as GOAL_HOST_PACKAGES, d as combineHostPolicy, dn as createProjection, dr as digestStrings, dt as LEGACY_HOST_COHORTS, en as latestAssistantText, er as requestedTargetAuthorizesMutation, et as ACTIVE_HOST_COHORT_ID, f as hostLockContextFromComposedDump, fn as confirmRebind, fr as normalizeClause, ft as bindExecutableIdentity, g as packageRowsFromActiveGraph, gn as rebindAttemptKey, gt as evaluateHostLock, h as inspectTargetHostGraph, hn as proposeRebindV042, hr as sha256, ht as evaluateHostCapability, i as canonicalProjection, in as goalCompletionDenial, ir as validateActionManifest, it as ALPHA2_HOST_PACKAGES, j as executeRevalidatedGitEffect, jn as extractOperation, jt as ALPHA3_HOST_PACKAGES, k as commitTreeSnapshotDigest, kn as extractArtifactPaths, kt as RC015_HOST_PACKAGES, l as validateProofManifest, ln as qualifyBoundary, lr as classifyUserInteraction, lt as HOST_CAPABILITY_PACKAGE_GROUPS, m as injectActiveProfileHostLock, mn as proposeRebindOutcome, mr as sanitizeUrl, mt as evaluateExternalWaitCapability, n as PROOF_PROTOCOL_VERSION, nn as observeAssistantOutcome, nr as semanticActionFromCommand, nt as ACTIVE_HOST_LAUNCHER_VERSION, o as proofDigest, on as availableBoundaryQualifications, or as COMMAND_SURFACE_MANIFEST, ot as DEFAULT_HOST_LOCK, p as hostLockRowsFromComposedDump, pn as proposeRebind, pr as sanitizeClauseText, pt as bindLiveGoalCapability, q as extractToolSubject, qn as CERTIFICATE_VERSION, qt as NO_PROGRESS_TURNS_BEFORE_STOP, r as bindProofToProjection, rn as progressFingerprint, rr as semanticActionFromText, rt as ALPHA2_DSHMARKET_139_HOST_PACKAGES, s as proofEvidenceConstraints, sn as effectuateBoundary, sr as validateManifest, st as EXPECTED_HOST_PACKAGES, t as PROOF_KINDS, tn as latestRootInstruction, tr as requestedTargetMatchesResolved, tt as ACTIVE_HOST_COHORT_IDS, u as HostProfileError, un as currentContractDigest, ur as canonicalizePath, ut as HOST_COHORTS, v as readActiveHostGraph, vn as replayRebindResult, vt as hostVersionFromPackages, w as SessionApiError, wn as itemDiagnosis, wt as compareHostVersions, x as verifyComposedHostLockDump, xn as parseConfirmationMessage, xt as MIN_SUPPORTED_HOST_VERSION, y as resolveActiveProfileHostLock, yn as CONFIRM_LINE_PATTERN, yt as selectHostCohort, z as previewFirstStepInjection, zn as maskCodeSpans, zt as recoveryDigest } from "./domain-RuX9l07o.js";
1
+ import { $ as isDeterministicCheck, $n as deriveItemDiagnosis, $t as proofDigestV2, A as previewFirstStepInjection, An as isWholeTaskCompletionClaim, Ar as STOP_PROTOCOL_VERSION_V2, At as evaluateMinimumHostVersion, B as RELEASE_SETTLEMENT_PREFIX, Bn as qualifyBoundary, Br as validateActionManifest, Bt as PROOF_CAPABILITY_MATRIX, C as gitCommandMatchesTarget, Cn as NO_PROGRESS_RECORD_PREFIX, Cr as ACTION_MANIFEST_VERSION, Ct as hostVersionFromPackages, D as FIRST_STEP_GUIDANCE, Dn as decideTurnStopping, Dr as SEMANTIC_ACTIONS, Dt as SUPPORTED_HOST_RANGE, E as verifiedLinearCommitReadback, En as decideTurnBoundary, Er as CERTIFICATE_VERSION_V2, Et as MIN_SUPPORTED_HOST_VERSION, F as PROTOCOL_V5_NOTICE, Fn as goalCompletionDenial, Fr as requestedIdentityKey, Ft as RC1_HOST_PACKAGES, G as releaseContractFor, Gn as proposeRebindOutcome, Gr as classifyUserInteraction, Gt as PROOF_PROTOCOL_VERSION_V2, H as inFlightReservation, Hn as createProjection, Hr as COMMAND_SURFACE_MANIFEST, Ht as PROOF_KINDS_V2, I as deriveProjection, In as hasCurrentCertificate, Ir as requestedTargetAuthorizesMutation, It as ALPHA3_HOST_PACKAGES, J as reservationFor, Jn as rebindResponse, Jr as normalizeClause, Jt as canonicalProjection, K as releaseCoverage, Kn as proposeRebindV042, Kr as canonicalizePath, Kt as bindProofToProjection, L as RELEASE_OPERATIONS, Ln as availableBoundaryQualifications, Lr as requestedTargetMatchesResolved, Lt as authorityCaptureCounts, M as DEFAULT_DELEGATION_TOOL_NAMES, Mn as latestRootInstruction, Mr as actionCompatible, Mt as satisfiesSupportedHostRange, N as PROTOCOL_V3_NOTICE, Nn as observeAssistantOutcome, Nr as boundedArtifactChoiceMatches, Nt as RC015_RC2_HOST_PACKAGES, O as claimedBatchHasRealRootInput, On as decisionBoundaryKey, Or as STATEFUL_ACTIONS, Ot as SUPPORTED_HOST_VERSIONS, P as PROTOCOL_V4_NOTICE, Pn as progressFingerprint, Pr as isStatefulAction, Pt as RC015_HOST_PACKAGES, Q as extractToolSubject, Qn as parseConfirmationMessage, Qt as proofDigest, R as RELEASE_OPERATION_SURFACES, Rn as effectuateBoundary, Rr as semanticActionFromCommand, Rt as segmentAuthorityBlocks, S as executeRevalidatedGitEffect, Sn as CONTROL_RECORD_PREFIX, Sr as ACTION_MANIFEST, St as evaluateToolSurfaceCapability, T as revalidateGitPrestate, Tn as classifyCompletionClaim, Tr as CERTIFICATE_VERSION, Tt as LATEST_SUPPORTED_HOST_VERSION, U as normalizeReleaseContract, Un as confirmRebind, Ur as validateManifest, Ut as PROOF_MANIFEST_DOMAIN_V2, V as contractById, Vn as currentContractDigest, Vr as validateActionTarget, Vt as PROOF_KINDS, W as readbackSettlesContract, Wn as proposeRebind, Wr as classifyTaskIntent, Wt as PROOF_PROTOCOL_VERSION, X as evidenceFromPersistedToolResult, Xn as CONFIRM_LINE_PATTERN, Xr as sanitizeUrl, Xt as createProofManifestV2, Y as supersedeItem, Yn as replayRebindResult, Yr as sanitizeClauseText, Yt as createProofManifest, Z as extractTextContent, Zn as isFrozenV042RebindResponse, Zr as sha256, Zt as proofCapabilityReport, _ as GIT_COMMAND_MANIFEST_IDS, _n as renderRecoveryPacket, _r as namedActions, _t as bindExecutableIdentity, a as injectActiveProfileHostLock, an as scopeCoverageDigest, ar as classifyClause, at as ACTIVE_HOST_COHORT_ID, b as commitTreeSnapshotDigest, bn as evidenceMatchesItem, br as canonicalRegistryBase, bt as evaluateHostCapability, c as packageRowsFromPnpmLock, cn as validateProofManifest, cr as extractOperation, ct as ALPHA2_DSHMARKET_139_HOST_PACKAGES, d as resolveInstalledHostLock, dn as certificateClosure, dr as interpretClause, dt as DEFAULT_HOST_LOCK, en as proofEvidenceConstraints, er as evidenceAvailabilityReason, et as withDurability, f as verifyComposedHostLockDump, fn as DEFAULT_RECOVERY_CHAR_BUDGET, fr as interpretMessage, ft as EXPECTED_HOST_PACKAGES, g as snapshotSessionEvents, gn as recoveryDigest, gr as maskCodeSpans, gt as LEGACY_HOST_COHORTS, h as SessionApiError, hn as openItems, hr as kindOfScope, ht as HOST_COHORTS, i as hostLockRowsFromComposedDump, in as requiredSubjectsOf, ir as captureItem, it as parseShellCommand, j as CAPTURE_V042_NOTICE, jn as latestAssistantText, jr as SUPPORTED_EVIDENCE_ADAPTERS, jt as parseHostVersion, k as lifecyclePhase, kn as isRootPauseRequest, kr as STOP_PROTOCOL_VERSION, kt as compareHostVersions, l as readActiveHostGraph, ln as validateProofManifestV2, lr as isInformationalMessage, lt as ALPHA2_HOST_PACKAGES, m as SESSION_EVENT_ENVELOPE_INVALID, mn as closingHint, mr as isOpenObligation, mt as HOST_CAPABILITY_PACKAGE_GROUPS, n as combineHostPolicy, nn as proofOperationMatches, nr as relevantEvidence, nt as isRunExecutable, o as inspectTargetHostGraph, on as sessionQuery, or as extractArtifactPaths, ot as ACTIVE_HOST_COHORT_IDS, p as SESSION_API_UNSUPPORTED, pn as MIN_RECOVERY_CHAR_BUDGET, pr as isExecutableItem, pt as GOAL_HOST_PACKAGES, q as releasePreEffectDecision, qn as rebindAttemptKey, qr as digestStrings, qt as bindProofV2ToProjection, r as hostLockContextFromComposedDump, rn as proofV2Rejection, rr as captureClause, rt as parsePwshCommand, s as packageRowsFromActiveGraph, sn as sessionQueryV2, sr as extractMethod, st as ACTIVE_HOST_LAUNCHER_VERSION, t as HostProfileError, tn as proofHostSurfacesOf, tr as itemDiagnosis, tt as canonicalArgvFromCommand, u as resolveActiveProfileHostLock, un as certifiableOpenItems, ur as segmentClauses, ut as BASE_HOST_PACKAGES, v as GIT_COMMAND_TEMPLATES, vn as bindingSatisfies, vr as semanticActionOfScope, vt as bindLiveGoalCapability, w as parseGitCommandManifest, wn as NO_PROGRESS_TURNS_BEFORE_STOP, wr as BOUNDED_ARTIFACT_TYPES, wt as selectHostCohort, x as createGitPrestateEnvelope, xn as isVerifyingCapability, xr as npmEscapedPackageName, xt as evaluateHostLock, y as commitIndexSnapshotDigest, yn as evidenceCoverage, yr as statefulActionsOfScope, yt as evaluateExternalWaitCapability, z as RELEASE_RESERVATION_PREFIX, zn as isCurrentAcceptedBoundary, zr as semanticActionFromText, zt as certifyCheckpoint } from "./domain-COehX7MB.js";
2
2
  import { defineTool } from "@deepseek-ai/dsh-tools";
3
3
  import { createHash } from "node:crypto";
4
4
  import { basename, delimiter, dirname, isAbsolute, join, resolve } from "node:path";
@@ -166,6 +166,7 @@ function checkpointPage(p, query, full) {
166
166
  available_qualifications: []
167
167
  };
168
168
  if (full.certificate) output.certificate = full.certificate;
169
+ if (full.proof_state) output.proof_state = full.proof_state;
169
170
  const pagination = {
170
171
  snapshot: identity,
171
172
  scope: evidence_scope,
@@ -357,6 +358,10 @@ function createCheckpointTool(getProjection, onRejected, prepare = async () => t
357
358
  detail_id: { type: "string" },
358
359
  detail_offset: { type: "integer" },
359
360
  detail_snapshot: { type: "string" },
361
+ proof: {
362
+ type: "object",
363
+ additionalProperties: true
364
+ },
360
365
  bindings: {
361
366
  type: "array",
362
367
  required: true,
@@ -460,6 +465,25 @@ function createCheckpointTool(getProjection, onRejected, prepare = async () => t
460
465
  type: "object",
461
466
  additionalProperties: true
462
467
  },
468
+ proof_state: {
469
+ type: "object",
470
+ additionalProperties: false,
471
+ properties: {
472
+ status: {
473
+ type: "string",
474
+ enum: [
475
+ "absent",
476
+ "bound",
477
+ "rejected",
478
+ "invalid"
479
+ ]
480
+ },
481
+ reason_codes: {
482
+ type: "array",
483
+ items: { type: "string" }
484
+ }
485
+ }
486
+ },
463
487
  reason_code: { type: "string" },
464
488
  next_step: { type: "string" },
465
489
  detail_id: { type: "string" },
@@ -599,6 +623,8 @@ function createCheckpointTool(getProjection, onRejected, prepare = async () => t
599
623
  evidence_sha256: { type: "string" },
600
624
  binding_digest: { type: "string" },
601
625
  certification_digest: { type: "string" },
626
+ unit_id: { type: "string" },
627
+ unit_closure_digest: { type: "string" },
602
628
  goal_ref: { oneOf: [{
603
629
  type: "object",
604
630
  additionalProperties: false,
@@ -631,7 +657,7 @@ function createCheckpointTool(getProjection, onRejected, prepare = async () => t
631
657
  rejected_bindings: []
632
658
  };
633
659
  }
634
- const result = certifyCheckpoint(projection, args.bindings.map((binding) => ({
660
+ const bindings = args.bindings.map((binding) => ({
635
661
  itemId: binding.item_id,
636
662
  evidenceIds: binding.evidence_ids,
637
663
  ...binding.semantic_action ? { semanticAction: binding.semantic_action } : {},
@@ -654,7 +680,39 @@ function createCheckpointTool(getProjection, onRejected, prepare = async () => t
654
680
  resolvedTarget: closure.resolved_target,
655
681
  order: closure.order
656
682
  })) } : {}
657
- })), `C${projection.checkpoints.length + 1}`, false);
683
+ }));
684
+ let proofState = {
685
+ status: "absent",
686
+ reason_codes: []
687
+ };
688
+ if (args.proof !== void 0) {
689
+ const structural = validateProofManifestV2(args.proof);
690
+ if (structural.length) proofState = {
691
+ status: "invalid",
692
+ reason_codes: structural
693
+ };
694
+ else {
695
+ const binding = bindProofV2ToProjection(projection, args.proof);
696
+ proofState = binding.length ? {
697
+ status: "rejected",
698
+ reason_codes: binding
699
+ } : {
700
+ status: "bound",
701
+ reason_codes: []
702
+ };
703
+ }
704
+ }
705
+ const result = proofState.status === "rejected" || proofState.status === "invalid" ? {
706
+ status: "incomplete",
707
+ contractRevision: projection.contractRevision,
708
+ openItems: [],
709
+ rejectedBindings: []
710
+ } : certifyCheckpoint(projection, bindings, `C${projection.checkpoints.length + 1}`, false);
711
+ if (proofState.status === "rejected" || proofState.status === "invalid") for (const code of proofState.reason_codes) result.rejectedBindings.push({
712
+ itemId: "*",
713
+ reason: "the presented proof does not bind the current contract",
714
+ reasonCode: code
715
+ });
658
716
  if (!result.checkpoint) onRejected();
659
717
  const available_evidence = [...projection.evidence.values()].filter((evidence) => evidence.epoch === projection.epoch && (args.evidence_scope === "history" || [...projection.items.values()].some((item) => item.status === "pending" && relevantEvidence(projection, item, evidence)))).sort((a, b) => b.toolResultSeq - a.toolResultSeq || (a.id < b.id ? -1 : 1)).map((evidence) => ({
660
718
  id: evidence.id,
@@ -680,6 +738,7 @@ function createCheckpointTool(getProjection, onRejected, prepare = async () => t
680
738
  }));
681
739
  return checkpointPage(projection, args, {
682
740
  status: result.status,
741
+ proof_state: proofState,
683
742
  contract_revision: result.contractRevision,
684
743
  blocking_total: result.openItems.length,
685
744
  open_items: (args.item_ids?.length ? args.item_ids : result.openItems).map((id) => projection.items.get(id)).filter((item) => Boolean(item)).sort((a, b) => b.revision - a.revision || a.id.localeCompare(b.id)).map((item) => openItemForTool(projection, item)),
@@ -711,6 +770,10 @@ function createCheckpointTool(getProjection, onRejected, prepare = async () => t
711
770
  evidence_sha256: result.checkpoint.evidenceSha256,
712
771
  binding_digest: result.checkpoint.bindingDigest,
713
772
  certification_digest: result.checkpoint.certificationDigest,
773
+ ...result.checkpoint.unitId !== void 0 ? {
774
+ unit_id: result.checkpoint.unitId,
775
+ unit_closure_digest: result.checkpoint.unitClosureDigest
776
+ } : {},
714
777
  goal_ref: result.checkpoint.goalRef ?? null
715
778
  } } : {}
716
779
  });
@@ -849,52 +912,210 @@ function createBoundaryTool(getProjection, prepare, onRejected) {
849
912
  }
850
913
 
851
914
  //#endregion
852
- //#region src/tools/git-preparation.ts
853
- /** Input recipes are guidance only; live resolution remains authoritative. */
854
- function gitPreparation(action) {
855
- if (![
856
- "commit",
857
- "push",
858
- "fetch",
859
- "pull"
860
- ].includes(action)) return void 0;
861
- return {
862
- selector_fields: action === "commit" ? ["repository", "branch"] : [
863
- "repository",
864
- "remote",
865
- "refspec"
866
- ],
867
- command_manifest_fields: ["planned_tool", "planned_arguments"],
868
- planned_tools: ["bash", "pwsh"],
869
- planned_argument_fields: ["command", "workdir"],
870
- steps: [
871
- "Before execution: context_guard_evidence with evidence_role=resolution, selector and command_manifest. Keep the successful tool call ID and target_digest.",
872
- "Execute once with context_guard_action, semantic_action, resolution_call_id, target_digest, contract_item_id and contract_item_revision.",
873
- "Collect effect and state separately with context_guard_evidence, using the same resolution_call_id and the successful action call ID as effect_call_id.",
874
- "Checkpoint the matching resolution/effect/state evidence IDs. Call IDs identify tool events; evidence IDs identify checkpoint facts.",
875
- "If the action already ran without resolution, report the historical evidence gap and read back state. Do not repeat a mutation to create missing prestate evidence."
876
- ]
877
- };
915
+ //#region src/tools/action-preparation.ts
916
+ const GUARD_TOOL_STEPS = [
917
+ "Before execution: context_guard_evidence with evidence_role=resolution, selector and command_manifest. Keep the successful tool call ID and target_digest.",
918
+ "Execute once with context_guard_action, semantic_action, resolution_call_id, target_digest, contract_item_id and contract_item_revision.",
919
+ "Collect effect and state separately with context_guard_evidence, using the same resolution_call_id and the successful action call ID as effect_call_id.",
920
+ "Checkpoint the matching resolution/effect/state evidence IDs. Call IDs identify tool events; evidence IDs identify checkpoint facts.",
921
+ "If the action already ran without resolution, report the historical evidence gap and read back state. Do not repeat a mutation to create missing prestate evidence."
922
+ ];
923
+ const NATIVE_WRITE_STEPS = [
924
+ "Before execution: context_guard_evidence with evidence_role=resolution, selector and command_manifest. Keep the successful tool call ID and target_digest.",
925
+ "Execute the planned effect once with the native write/edit tool named by command_manifest.planned_tool and exactly the planned_arguments.",
926
+ "Collect effect and state separately with context_guard_evidence, using the same resolution_call_id and the successful write/edit call ID as effect_call_id.",
927
+ "Checkpoint the matching resolution/effect/state evidence IDs. Call IDs identify tool events; evidence IDs identify checkpoint facts.",
928
+ "If the action already ran without resolution, report the historical evidence gap and read back state. Do not repeat a mutation to create missing prestate evidence."
929
+ ];
930
+ /**
931
+ * The unified descriptor for one stateful action. Git actions keep their
932
+ * historical selector/manifest/step shapes; the other six gain the same
933
+ * complete recipe instead of Git-only coverage.
934
+ */
935
+ function actionPreparation(action) {
936
+ switch (action) {
937
+ case "install":
938
+ case "apply": return {
939
+ action,
940
+ selector_fields: ["package_id", "profile"],
941
+ optional_selector_fields: ["version"],
942
+ command_manifest_fields: ["manifest_id", "tgz_path"],
943
+ command_manifest_ids: [action === "install" ? "dsh.plugin_add_tgz.install.v1" : "dsh.plugin_add_tgz.apply.v1"],
944
+ planned_tools: ["context_guard_action"],
945
+ planned_argument_fields: [],
946
+ producer_fields: ["version", "integrity_digest"],
947
+ readback_fields: [
948
+ "package_id",
949
+ "version",
950
+ "integrity_digest",
951
+ "profile"
952
+ ],
953
+ execution_surface: "context_guard_action",
954
+ steps: GUARD_TOOL_STEPS
955
+ };
956
+ case "create": return {
957
+ action,
958
+ selector_fields: ["artifact_id"],
959
+ optional_selector_fields: [],
960
+ command_manifest_fields: ["planned_tool", "planned_arguments"],
961
+ command_manifest_ids: ["artifact.create.v1"],
962
+ planned_tools: ["write", "write_file"],
963
+ planned_argument_fields: ["file_path", "content"],
964
+ producer_fields: [
965
+ "scope",
966
+ "pre_digest",
967
+ "change_set_digest"
968
+ ],
969
+ readback_fields: ["post_digest"],
970
+ execution_surface: "native_write_edit",
971
+ steps: NATIVE_WRITE_STEPS
972
+ };
973
+ case "modify": return {
974
+ action,
975
+ selector_fields: ["artifact_id"],
976
+ optional_selector_fields: [],
977
+ command_manifest_fields: ["planned_tool", "planned_arguments"],
978
+ command_manifest_ids: ["artifact.modify.v1"],
979
+ planned_tools: ["edit", "edit_file"],
980
+ planned_argument_fields: [
981
+ "file_path",
982
+ "old_string",
983
+ "new_string"
984
+ ],
985
+ producer_fields: [
986
+ "scope",
987
+ "pre_digest",
988
+ "change_set_digest"
989
+ ],
990
+ readback_fields: ["post_digest"],
991
+ execution_surface: "native_write_edit",
992
+ steps: NATIVE_WRITE_STEPS
993
+ };
994
+ case "restart": return {
995
+ action,
996
+ selector_fields: ["service_id"],
997
+ optional_selector_fields: [],
998
+ command_manifest_fields: ["manifest_id"],
999
+ command_manifest_ids: ["dshmarket.restart.v1"],
1000
+ planned_tools: ["context_guard_action"],
1001
+ planned_argument_fields: [],
1002
+ producer_fields: ["pre_generation"],
1003
+ readback_fields: ["new_generation", "health"],
1004
+ execution_surface: "context_guard_action",
1005
+ steps: GUARD_TOOL_STEPS
1006
+ };
1007
+ case "publish": return {
1008
+ action,
1009
+ selector_fields: [
1010
+ "artifact_id",
1011
+ "version",
1012
+ "registry"
1013
+ ],
1014
+ optional_selector_fields: [],
1015
+ command_manifest_fields: ["manifest_id", "tgz_path"],
1016
+ command_manifest_ids: ["npm.publish_tgz.v1"],
1017
+ planned_tools: ["context_guard_action"],
1018
+ planned_argument_fields: [],
1019
+ producer_fields: ["integrity_digest"],
1020
+ readback_fields: [
1021
+ "artifact_id",
1022
+ "version",
1023
+ "registry",
1024
+ "integrity_digest"
1025
+ ],
1026
+ execution_surface: "context_guard_action",
1027
+ steps: GUARD_TOOL_STEPS
1028
+ };
1029
+ case "commit": return {
1030
+ action,
1031
+ selector_fields: ["repository", "branch"],
1032
+ optional_selector_fields: [],
1033
+ command_manifest_fields: ["planned_tool", "planned_arguments"],
1034
+ command_manifest_ids: ["git.commit_index_tree.v2"],
1035
+ planned_tools: ["bash", "pwsh"],
1036
+ planned_argument_fields: ["command", "workdir"],
1037
+ producer_fields: ["pre_head_oid", "change_set_digest"],
1038
+ readback_fields: ["post_head_oid", "pre_head_oid"],
1039
+ execution_surface: "context_guard_action",
1040
+ steps: GUARD_TOOL_STEPS
1041
+ };
1042
+ case "push": return {
1043
+ action,
1044
+ selector_fields: [
1045
+ "repository",
1046
+ "remote",
1047
+ "refspec"
1048
+ ],
1049
+ optional_selector_fields: [],
1050
+ command_manifest_fields: ["planned_tool", "planned_arguments"],
1051
+ command_manifest_ids: ["git.push_explicit_refs.v2"],
1052
+ planned_tools: ["bash", "pwsh"],
1053
+ planned_argument_fields: ["command", "workdir"],
1054
+ producer_fields: ["local_oid"],
1055
+ readback_fields: ["remote_oid"],
1056
+ execution_surface: "context_guard_action",
1057
+ steps: GUARD_TOOL_STEPS
1058
+ };
1059
+ case "pull": return {
1060
+ action,
1061
+ selector_fields: [
1062
+ "repository",
1063
+ "remote",
1064
+ "refspec"
1065
+ ],
1066
+ optional_selector_fields: [],
1067
+ command_manifest_fields: ["planned_tool", "planned_arguments"],
1068
+ command_manifest_ids: ["git.pull_ff_only_explicit.v2"],
1069
+ planned_tools: ["bash", "pwsh"],
1070
+ planned_argument_fields: ["command", "workdir"],
1071
+ producer_fields: [
1072
+ "upstream_oid",
1073
+ "pre_head_oid",
1074
+ "pull_mode"
1075
+ ],
1076
+ readback_fields: ["post_head_oid", "tracking_ref_oid"],
1077
+ execution_surface: "context_guard_action",
1078
+ steps: GUARD_TOOL_STEPS
1079
+ };
1080
+ case "fetch": return {
1081
+ action,
1082
+ selector_fields: [
1083
+ "repository",
1084
+ "remote",
1085
+ "refspec"
1086
+ ],
1087
+ optional_selector_fields: [],
1088
+ command_manifest_fields: ["planned_tool", "planned_arguments"],
1089
+ command_manifest_ids: ["git.fetch_tracking_explicit.v2"],
1090
+ planned_tools: ["bash", "pwsh"],
1091
+ planned_argument_fields: ["command", "workdir"],
1092
+ producer_fields: ["upstream_oid", "pre_head_oid"],
1093
+ readback_fields: ["tracking_ref_oid", "post_head_oid"],
1094
+ execution_surface: "context_guard_action",
1095
+ steps: GUARD_TOOL_STEPS
1096
+ };
1097
+ }
878
1098
  }
879
1099
 
880
1100
  //#endregion
881
1101
  //#region src/tools/prepare.ts
1102
+ /** Discovery pages stay bounded like checkpoint pages. */
1103
+ const DISCOVERY_ITEM_LIMIT = 8;
882
1104
  /**
883
- * Thin READ-ONLY preparation surface (v0.5): before any stateful action it
1105
+ * Thin READ-ONLY preparation surface (v0.5/0.6): before any stateful action it
884
1106
  * reports the supported command shape, the required resolution/effect/state
885
1107
  * evidence order, existing reusable references, and the exact missing fields.
886
1108
  * It never executes, installs, commits, pushes, restarts, or probes authority
887
1109
  * through side effects, and it never upgrades a default into user authority.
1110
+ * Without an `item_id` it returns the bounded current-item discovery list, so
1111
+ * the first step of a session can find the right ID instead of guessing one.
888
1112
  */
889
1113
  function createPrepareTool(options) {
890
1114
  return defineTool({
891
1115
  name: "context_guard_prepare",
892
- description: "Read-only pre-action preparation: report the supported command shape, required resolution/effect/state evidence order, existing references, and exact missing target fields for one contract item. Performs no action and grants no authority.",
1116
+ description: "Read-only pre-action preparation: report the supported command shape, required resolution/effect/state evidence order, existing references, and exact missing target fields for one contract item. Omit item_id to list current open items with their IDs. Performs no action and grants no authority.",
893
1117
  parameters: {
894
- item_id: {
895
- type: "string",
896
- required: true
897
- },
1118
+ item_id: { type: "string" },
898
1119
  item_revision: { type: "number" },
899
1120
  semantic_action: { type: "string" },
900
1121
  requested_target: {
@@ -915,11 +1136,46 @@ function createPrepareTool(options) {
915
1136
  },
916
1137
  async execute(rawArgs) {
917
1138
  const args = rawArgs;
1139
+ if (options.refreshProjection) {
1140
+ let durable = false;
1141
+ try {
1142
+ durable = await options.refreshProjection() === true;
1143
+ } catch {
1144
+ durable = false;
1145
+ }
1146
+ if (!durable) return {
1147
+ status: "unknown",
1148
+ reason_code: "projection_durability_unavailable"
1149
+ };
1150
+ }
918
1151
  const p = options.getProjection();
919
1152
  if (!p || !p.enabled || p.integrity !== "valid") return {
920
1153
  status: "unknown",
921
1154
  reason_code: "guard_unavailable"
922
1155
  };
1156
+ if (args.item_id === void 0) {
1157
+ const open = [...p.items.values()].filter((item$1) => item$1.status === "pending").sort((a, b) => a.revision - b.revision || a.id.localeCompare(b.id));
1158
+ return {
1159
+ status: "prepared",
1160
+ mode: "discovery",
1161
+ durability: p.durabilityWatermark,
1162
+ contract_revision: p.contractRevision,
1163
+ total_open: open.length,
1164
+ items: open.slice(0, DISCOVERY_ITEM_LIMIT).map((item$1) => {
1165
+ const diagnosis$1 = deriveItemDiagnosis(p, item$1);
1166
+ return {
1167
+ id: item$1.id,
1168
+ revision: item$1.revision,
1169
+ kind: item$1.kind,
1170
+ ...item$1.taskKind !== void 0 ? { task_kind: item$1.taskKind } : {},
1171
+ semantic_action: item$1.semanticAction ?? "generic_run",
1172
+ reason_code: diagnosis$1.reason_code,
1173
+ text: item$1.normalizedText
1174
+ };
1175
+ }),
1176
+ note: "Re-run with one item_id to prepare that item. Preparation performs no action."
1177
+ };
1178
+ }
923
1179
  const item = p.items.get(args.item_id ?? "");
924
1180
  if (!item) return {
925
1181
  status: "rejected",
@@ -949,8 +1205,8 @@ function createPrepareTool(options) {
949
1205
  "effect (the exact planned change)",
950
1206
  "state (independent post-state readback)"
951
1207
  ] : ["state (matching durable evidence for the requested verification)"];
952
- const recipe = plannedAction ? gitPreparation(plannedAction) : void 0;
953
- const missingTargetFields = manifestEntry?.stateful && plannedAction ? (recipe?.selector_fields ?? manifestEntry.resolvedTargetKeys).filter((key) => !(item.requestedTarget?.[key] !== void 0 || args.requested_target?.[key] !== void 0)) : [];
1208
+ const recipe = plannedAction && isStatefulAction(plannedAction) ? actionPreparation(plannedAction) : void 0;
1209
+ const missingTargetFields = recipe ? recipe.selector_fields.filter((key) => !(item.requestedTarget?.[key] !== void 0 || args.requested_target?.[key] !== void 0)) : [];
954
1210
  const capability = plannedAction && options.hostCapability ? options.hostCapability(plannedAction) : void 0;
955
1211
  const commandShape = options.commandTemplate && plannedAction ? options.commandTemplate(plannedAction) : void 0;
956
1212
  return {
@@ -969,7 +1225,18 @@ function createPrepareTool(options) {
969
1225
  status: capability.status,
970
1226
  ...capability.reasonCode !== void 0 ? { reason_code: capability.reasonCode } : {}
971
1227
  } } : {},
972
- ...recipe ? { evidence_input_contract: recipe } : {},
1228
+ ...recipe ? { evidence_input_contract: {
1229
+ selector_fields: recipe.selector_fields,
1230
+ optional_selector_fields: recipe.optional_selector_fields,
1231
+ command_manifest_fields: recipe.command_manifest_fields,
1232
+ command_manifest_ids: recipe.command_manifest_ids,
1233
+ planned_tools: recipe.planned_tools,
1234
+ planned_argument_fields: recipe.planned_argument_fields,
1235
+ producer_fields: recipe.producer_fields,
1236
+ readback_fields: recipe.readback_fields,
1237
+ execution_surface: recipe.execution_surface,
1238
+ steps: recipe.steps
1239
+ } } : {},
973
1240
  note: "Preparation performs no action. A default or guessed target is not user authority; explicit root instruction is required for missing target fields."
974
1241
  };
975
1242
  }
@@ -1315,12 +1582,63 @@ async function tgzIdentity(path) {
1315
1582
  offset = bodyStart + Math.ceil(size$1 / 512) * 512;
1316
1583
  }
1317
1584
  if (typeof manifest?.name !== "string" || typeof manifest.version !== "string") return void 0;
1585
+ const gitHead = typeof manifest.gitHead === "string" && /^[0-9a-f]{40}$/.test(manifest.gitHead) ? manifest.gitHead : void 0;
1586
+ const repositoryField = manifest.repository;
1587
+ const repository = typeof repositoryField === "string" ? repositoryField : record(repositoryField) && typeof record(repositoryField)?.url === "string" ? String(record(repositoryField).url) : void 0;
1318
1588
  return {
1319
1589
  name: manifest.name,
1320
1590
  version: manifest.version,
1321
- integrity: `sha512-${createHash("sha512").update(bytes).digest("base64")}`
1591
+ integrity: `sha512-${createHash("sha512").update(bytes).digest("base64")}`,
1592
+ sha256: createHash("sha256").update(bytes).digest("hex"),
1593
+ ...gitHead ? { gitHead } : {},
1594
+ ...repository ? { repository } : {}
1322
1595
  };
1323
1596
  }
1597
+ /**
1598
+ * Read the candidate identity of a publish from TRUSTED PRODUCERS only: the
1599
+ * exact tgz bytes (SHA-256 and npm SRI), the manifest the packer embedded
1600
+ * (package name, version, gitHead, repository) and, when the contract names a
1601
+ * ref, the local repository's own answer for that ref. Nothing here comes from
1602
+ * a model argument, so a caller cannot assert an identity the artifact does
1603
+ * not have.
1604
+ */
1605
+ async function observePublishCandidate(resolution, roots, signal, cwd) {
1606
+ const tgzPath = requireString(resolution.commandManifest, "tgz_path");
1607
+ if (!tgzPath) return void 0;
1608
+ const identity = await tgzIdentity(tgzPath);
1609
+ if (!identity) return void 0;
1610
+ const registry = typeof resolution.target.registry === "string" ? resolution.target.registry : void 0;
1611
+ return {
1612
+ packageId: identity.name,
1613
+ version: identity.version,
1614
+ artifactSha256: identity.sha256,
1615
+ artifactSri: identity.integrity,
1616
+ ...identity.gitHead ? { fullSha40: identity.gitHead } : {},
1617
+ ...identity.repository ? { repository: identity.repository } : {},
1618
+ ...registry ? { registry } : {}
1619
+ };
1620
+ }
1621
+ /**
1622
+ * Resolve one ref to its commit with the audited git executable. Exported so
1623
+ * the runtime can resolve the ref an ADOPTED CONTRACT names, which is the only
1624
+ * closed, reachable path for that identity: the action tool's manifest schema
1625
+ * has no field for it, and a model-supplied ref would not be authority.
1626
+ */
1627
+ async function resolveAuditedRef(cwd, ref, roots, signal) {
1628
+ if (!cwd || !/^refs\/[A-Za-z0-9._/-]+$/.test(ref)) return void 0;
1629
+ try {
1630
+ const identity = await (roots.readExecutableIdentity ?? executableIdentity)("git", signal);
1631
+ if (!identity || identity.executable !== "git") return void 0;
1632
+ const resolved = (await git(cwd, [
1633
+ "rev-parse",
1634
+ "--verify",
1635
+ `${ref}^{commit}`
1636
+ ], signal, identity.realpath)).trim().toLowerCase();
1637
+ return /^[0-9a-f]{40}$/.test(resolved) ? resolved : void 0;
1638
+ } catch {
1639
+ return;
1640
+ }
1641
+ }
1324
1642
  const MARKET_SCHEMA = "dsh-market/update-api/v1";
1325
1643
  const MARKET_GENERATION_PREFIX = "dsh-market-instance/v1:";
1326
1644
  async function marketCapabilities(roots, signal) {
@@ -2050,7 +2368,9 @@ function normalizedRoots(options) {
2050
2368
  ...options.authorizeMutation ? { authorizeMutation: options.authorizeMutation } : {},
2051
2369
  ...options.prepareMutation ? { prepareMutation: options.prepareMutation } : {},
2052
2370
  ...options.readExecutableIdentity ? { readExecutableIdentity: options.readExecutableIdentity } : {},
2053
- ...options.allowLoopbackHttpRegistry ? { allowLoopbackHttpRegistry: true } : {}
2371
+ ...options.allowLoopbackHttpRegistry ? { allowLoopbackHttpRegistry: true } : {},
2372
+ ...options.releaseGate ? { releaseGate: options.releaseGate } : {},
2373
+ ...options.releaseSettle ? { releaseSettle: options.releaseSettle } : {}
2054
2374
  };
2055
2375
  }
2056
2376
  function createActionTool(options = {}) {
@@ -2219,14 +2539,70 @@ function createActionTool(options = {}) {
2219
2539
  ...identity
2220
2540
  };
2221
2541
  }
2542
+ const releaseOperation = action === "publish" ? "npm_publish" : void 0;
2543
+ let releaseGranted = false;
2544
+ let releaseContractId;
2545
+ if (releaseOperation && roots.releaseGate) {
2546
+ const observed = await observePublishCandidate(resolution, roots, exec.signal, cwdOf(agent));
2547
+ if (!observed) return {
2548
+ status: "unavailable",
2549
+ reason_code: "release_candidate_unobservable",
2550
+ ...identity
2551
+ };
2552
+ let decision;
2553
+ try {
2554
+ decision = await roots.releaseGate({
2555
+ agent,
2556
+ operation: releaseOperation,
2557
+ callId: args.resolution_call_id,
2558
+ resolvedTarget: resolution.target,
2559
+ observed
2560
+ });
2561
+ } catch {
2562
+ decision = void 0;
2563
+ }
2564
+ if (decision === void 0) return {
2565
+ status: "unavailable",
2566
+ reason_code: "release_gate_unavailable",
2567
+ ...identity
2568
+ };
2569
+ if (decision.status !== "granted") return {
2570
+ status: "unavailable",
2571
+ reason_code: decision.reasonCode,
2572
+ ...identity
2573
+ };
2574
+ releaseGranted = true;
2575
+ releaseContractId = decision.contractId;
2576
+ }
2222
2577
  try {
2223
2578
  const status = await executeGuardAction(action, resolution, roots, exec.signal, currentExecutable, args.resolution_call_id, agent);
2579
+ if (releaseOperation && releaseGranted && roots.releaseSettle) {
2580
+ const readback$1 = status === "completed" ? await publishReadback(resolution.target, roots, exec.signal) : void 0;
2581
+ await roots.releaseSettle({
2582
+ agent,
2583
+ operation: releaseOperation,
2584
+ callId: args.resolution_call_id,
2585
+ ...releaseContractId !== void 0 ? { contractId: releaseContractId } : {},
2586
+ effect: status === "completed" ? "completed" : status === "unavailable" ? "not_effected" : "unknown",
2587
+ readback: readback$1 === void 0 ? "unavailable" : {
2588
+ kind: "npm_integrity",
2589
+ identity: readback$1
2590
+ }
2591
+ });
2592
+ }
2224
2593
  return {
2225
2594
  status,
2226
2595
  reason_code: status === "completed" ? "action_completed" : status === "handoff_pending" ? "restart_handoff_pending" : "action_execution_failed",
2227
2596
  ...identity
2228
2597
  };
2229
2598
  } catch {
2599
+ if (releaseOperation && releaseGranted && roots.releaseSettle) await roots.releaseSettle({
2600
+ agent,
2601
+ operation: releaseOperation,
2602
+ callId: args.resolution_call_id,
2603
+ effect: "unknown",
2604
+ readback: "unavailable"
2605
+ });
2230
2606
  return {
2231
2607
  status: "unavailable",
2232
2608
  reason_code: "action_execution_failed",
@@ -2236,6 +2612,23 @@ function createActionTool(options = {}) {
2236
2612
  }
2237
2613
  });
2238
2614
  }
2615
+ /**
2616
+ * The trusted npm readback identity for a completed publish: the registry's
2617
+ * own `dist.integrity` for the exact package version. `undefined` means no
2618
+ * producer is reachable, which keeps the release attempt `unconfirmed` instead
2619
+ * of claiming a verified release.
2620
+ */
2621
+ async function publishReadback(target, roots, signal) {
2622
+ const registry = typeof target.registry === "string" ? target.registry : void 0;
2623
+ const artifact = typeof target.artifact_id === "string" ? target.artifact_id : void 0;
2624
+ const version = typeof target.version === "string" ? target.version : void 0;
2625
+ if (!registry || !artifact || !version) return void 0;
2626
+ try {
2627
+ return await registryIntegrity(registry, artifact, version, roots, signal);
2628
+ } catch {
2629
+ return;
2630
+ }
2631
+ }
2239
2632
  function createEvidenceTool(options = {}) {
2240
2633
  const roots = normalizedRoots(options);
2241
2634
  return defineTool({
@@ -2452,17 +2845,26 @@ function createEvidenceTool(options = {}) {
2452
2845
  if (!agent) return unavailable(action, role, "producer_agent_unavailable");
2453
2846
  try {
2454
2847
  if (role === "resolution") {
2455
- const recipe = gitPreparation(action);
2456
- if (recipe) {
2848
+ const recipe = actionPreparation(action);
2849
+ {
2457
2850
  const manifest = record(args.command_manifest);
2458
2851
  const plannedArgs = record(manifest?.planned_arguments);
2459
2852
  const selector = record(args.selector);
2853
+ const needsManifestId = [
2854
+ "install",
2855
+ "apply",
2856
+ "publish",
2857
+ "restart"
2858
+ ].includes(action);
2859
+ const needsPlannedEffect = !needsManifestId;
2460
2860
  const missing = [
2461
2861
  ...recipe.selector_fields.filter((key) => !requireString(selector ?? {}, key)).map((key) => `selector.${key}`),
2462
- ...!requireString(manifest ?? {}, "planned_tool") ? ["command_manifest.planned_tool"] : [],
2463
- ...recipe.planned_argument_fields.filter((key) => !requireString(plannedArgs ?? {}, key)).map((key) => `command_manifest.planned_arguments.${key}`)
2862
+ ...needsManifestId && !requireString(manifest ?? {}, "manifest_id") ? ["command_manifest.manifest_id"] : [],
2863
+ ...needsManifestId && recipe.command_manifest_fields.includes("tgz_path") && !requireString(manifest ?? {}, "tgz_path") ? ["command_manifest.tgz_path"] : [],
2864
+ ...needsPlannedEffect && !requireString(manifest ?? {}, "planned_tool") ? ["command_manifest.planned_tool"] : [],
2865
+ ...needsPlannedEffect ? recipe.planned_argument_fields.filter((key) => !requireString(plannedArgs ?? {}, key)).map((key) => `command_manifest.planned_arguments.${key}`) : []
2464
2866
  ];
2465
- if (missing.length) return unavailable(action, role, "resolution_input_missing", missing, "Supply the listed resolution inputs before execution. Use context_guard_prepare for the exact Git input contract. A manifest_id alone does not describe a planned tool call.");
2867
+ if (missing.length) return unavailable(action, role, "resolution_input_missing", missing, "Supply the listed resolution inputs before execution. Use context_guard_prepare for the exact input contract of this action. A manifest_id alone does not describe a planned tool call.");
2466
2868
  }
2467
2869
  const executable$1 = executableFor(action);
2468
2870
  const executableBinding = executable$1 ? await (roots.readExecutableIdentity ?? executableIdentity)(executable$1, exec.signal) : void 0;
@@ -2514,6 +2916,135 @@ function createEvidenceTool(options = {}) {
2514
2916
  });
2515
2917
  }
2516
2918
 
2919
+ //#endregion
2920
+ //#region src/tools/release.ts
2921
+ const RELEASE_TOOL = "context_guard_release";
2922
+ function createReleaseTool(options) {
2923
+ return defineTool({
2924
+ name: RELEASE_TOOL,
2925
+ description: "Read the explicit release state, or reconcile a release attempt whose effect was unknown by reading back the external identity. This tool never re-sends a release.",
2926
+ parameters: {
2927
+ operation: {
2928
+ type: "string",
2929
+ required: true,
2930
+ enum: ["status", "reconcile"]
2931
+ },
2932
+ contract_id: { type: "string" },
2933
+ resolution_call_id: { type: "string" }
2934
+ },
2935
+ output: {
2936
+ schema: {
2937
+ type: "object",
2938
+ additionalProperties: true
2939
+ },
2940
+ render: (_args, value) => [{
2941
+ type: "text",
2942
+ text: JSON.stringify(value)
2943
+ }]
2944
+ },
2945
+ async execute(rawArgs, exec) {
2946
+ const args = rawArgs;
2947
+ const projection = options.getProjection();
2948
+ if (!projection) return {
2949
+ status: "unknown",
2950
+ reason_code: "guard_unavailable"
2951
+ };
2952
+ if (args.operation === "status") return {
2953
+ status: "available",
2954
+ policy: projection.policy,
2955
+ state_damaged: projection.releaseStateDamaged,
2956
+ coverage_surface: Object.keys(RELEASE_OPERATION_SURFACES).map((operation$1) => ({
2957
+ operation: operation$1,
2958
+ ...RELEASE_OPERATION_SURFACES[operation$1]
2959
+ })),
2960
+ contracts: projection.releaseContracts.map((contract$1) => ({
2961
+ contract_id: contract$1.contractId,
2962
+ revoked_at_seq: contract$1.revokedAtSeq ?? null,
2963
+ adopted_at_revision: contract$1.adoptedAtRevision,
2964
+ operations: releaseCoverage(contract$1),
2965
+ candidate: contract$1.candidate
2966
+ })),
2967
+ reservations: projection.releaseReservations.map(({ contractId: contractId$1, operation: operation$1, callId: callId$1, startedAtSeq, observedArtifactSri }) => ({
2968
+ contract_id: contractId$1,
2969
+ operation: operation$1,
2970
+ resolution_call_id: callId$1,
2971
+ started_at_seq: startedAtSeq,
2972
+ observed_artifact_sri: observedArtifactSri ?? null,
2973
+ in_flight: inFlightReservation(projection, contractId$1, operation$1)?.callId === callId$1
2974
+ })),
2975
+ settlements: projection.releaseSettlements.map(({ contractId: contractId$1, operation: operation$1, callId: callId$1, outcome: outcome$1, readback: readback$2, settledAtSeq }) => ({
2976
+ contract_id: contractId$1,
2977
+ operation: operation$1,
2978
+ resolution_call_id: callId$1,
2979
+ outcome: outcome$1,
2980
+ readback: readback$2,
2981
+ settled_at_seq: settledAtSeq
2982
+ })),
2983
+ diagnostics: projection.releaseDiagnostics
2984
+ };
2985
+ if (args.operation !== "reconcile") return {
2986
+ status: "rejected",
2987
+ reason_code: "release_subcommand_unknown"
2988
+ };
2989
+ const contractId = args.contract_id ?? "";
2990
+ const callId = args.resolution_call_id ?? "";
2991
+ const contract = contractById(projection, contractId);
2992
+ if (!contract) return {
2993
+ status: "rejected",
2994
+ reason_code: "release_contract_unknown"
2995
+ };
2996
+ const reservation = reservationFor(projection, contractId, callId);
2997
+ if (!reservation) return {
2998
+ status: "rejected",
2999
+ reason_code: "release_reservation_unknown"
3000
+ };
3001
+ const operation = reservation.operation;
3002
+ const settled = projection.releaseSettlements.find((entry) => entry.contractId === contractId && entry.operation === operation && entry.callId === callId && (entry.outcome === "settled" || entry.outcome === "not_effected"));
3003
+ if (settled) return {
3004
+ status: "already_resolved",
3005
+ outcome: settled.outcome,
3006
+ reason_code: "release_already_resolved"
3007
+ };
3008
+ const registry = contract.candidate.registry;
3009
+ const packageId = contract.candidate.packageId;
3010
+ const version = contract.candidate.version;
3011
+ if (!registry || !packageId || !version) return {
3012
+ status: "unavailable",
3013
+ reason_code: "release_readback_identity_incomplete"
3014
+ };
3015
+ const identity = await registryIntegrity(registry, packageId, version, options, exec.signal);
3016
+ if (!identity) return {
3017
+ status: "unavailable",
3018
+ reason_code: "release_readback_unavailable"
3019
+ };
3020
+ const readback$1 = {
3021
+ kind: "npm_integrity",
3022
+ identity
3023
+ };
3024
+ const verdict = readbackSettlesContract(contract, readback$1, reservation.observedArtifactSri);
3025
+ const outcome = verdict === "settled" ? "settled" : "unknown";
3026
+ if (!await options.persistSettlement({
3027
+ agent: exec.agent,
3028
+ contractId,
3029
+ operation,
3030
+ callId,
3031
+ readback: readback$1,
3032
+ outcome
3033
+ })) return {
3034
+ status: "unknown",
3035
+ reason_code: "release_settlement_not_durable"
3036
+ };
3037
+ return {
3038
+ status: verdict === "settled" ? "settled" : verdict === "mismatch" ? "mismatch" : "unconfirmed",
3039
+ outcome,
3040
+ readback: readback$1,
3041
+ reason_code: verdict === "settled" ? "release_reconciled" : verdict === "mismatch" ? "release_readback_identity_mismatch" : "release_readback_identity_unavailable",
3042
+ note: "Reconciliation never re-sends the release operation."
3043
+ };
3044
+ }
3045
+ });
3046
+ }
3047
+
2517
3048
  //#endregion
2518
3049
  //#region src/tools/external-operation.ts
2519
3050
  function createExternalOperationTool(read, capability) {
@@ -2589,17 +3120,168 @@ function createExternalOperationTool(read, capability) {
2589
3120
  });
2590
3121
  }
2591
3122
 
3123
+ //#endregion
3124
+ //#region src/domain/migration.ts
3125
+ /** The frozen digest domains: a migration never rewrites any of them. */
3126
+ const PRESERVED_DIGEST_DOMAINS = [
3127
+ "ccg.sessionRefDigest.v3",
3128
+ "ccg.hostLockDigest.v3",
3129
+ "ccg.evidenceFact.v3",
3130
+ "ccg.evidenceSha256.v3",
3131
+ "ccg.predParams.v3",
3132
+ "ccg.binding.v3",
3133
+ "ccg.bindingDigest.v3",
3134
+ "ccg.certificationDigest.v3",
3135
+ "ccg.locator.v1",
3136
+ "ccg.proofManifest.v1"
3137
+ ];
3138
+ function migrationReport(projection) {
3139
+ const v5 = projection.boundaryProtocol === 5;
3140
+ const legacyItemIds = certifiableOpenItems(projection).filter((item) => item.unitId === void 0).map((item) => item.id);
3141
+ const closure = v5 ? certificateClosure(projection) : { itemIds: [] };
3142
+ const reasonCodes = [];
3143
+ if (!v5) reasonCodes.push("legacy_session_keeps_v4_contract");
3144
+ if (v5 && legacyItemIds.length > 0) reasonCodes.push("pre_v5_obligations_retained_in_closure");
3145
+ return {
3146
+ ruleMode: v5 ? "v5" : "legacy-v4",
3147
+ certificateVersion: v5 ? CERTIFICATE_VERSION_V2 : CERTIFICATE_VERSION,
3148
+ stopProtocolVersion: v5 ? STOP_PROTOCOL_VERSION_V2 : STOP_PROTOCOL_VERSION,
3149
+ unitClosure: v5,
3150
+ ...v5 && projection.currentUnitId !== void 0 ? { currentUnitId: projection.currentUnitId } : {},
3151
+ legacyItemIds,
3152
+ unitClosureItemIds: [...closure.itemIds],
3153
+ preservedDigestDomains: [...PRESERVED_DIGEST_DOMAINS],
3154
+ historyOnlyCertificateVersions: v5 ? [CERTIFICATE_VERSION] : [],
3155
+ rollbackRequiresStateSnapshot: v5,
3156
+ rollbackInstruction: v5 ? "Restore the 0.5.x state snapshot before starting the older binary. Replaying a v5 log with 0.5.x fails closed with certificate_replay_mismatch; never migrate new-schema data down by hand." : "No rollback action is required for this session: it has not written a v5 boundary.",
3157
+ reasonCodes
3158
+ };
3159
+ }
3160
+
2592
3161
  //#endregion
2593
3162
  //#region src/commands/context-guard.ts
2594
3163
  function pendingCount(projection) {
2595
3164
  return [...projection.items.values()].filter((item) => item.status === "pending").length;
2596
3165
  }
3166
+ /**
3167
+ * The release surface. Adoption is deliberately NOT performed here: the
3168
+ * derivation adopts a contract only from the durable root `command/run` for
3169
+ * `/context-guard release adopt <json>`, so the command handler's job is to
3170
+ * report the resulting state (and to explain what is not protectable), never
3171
+ * to grant authority itself.
3172
+ */
3173
+ function releaseResponse(projection, rawInput) {
3174
+ const rest = rawInput.trim().slice(7).trim();
3175
+ const [verb] = rest.split(/\s+/, 1);
3176
+ if (verb === "adopt") {
3177
+ const payload = rest.slice(5).trim();
3178
+ let parsed;
3179
+ try {
3180
+ parsed = JSON.parse(payload);
3181
+ } catch {
3182
+ return {
3183
+ kind: "error",
3184
+ text: "Context Guard release adopt: the contract must be one JSON object."
3185
+ };
3186
+ }
3187
+ const normalized = normalizeReleaseContract(parsed, {
3188
+ seq: -1,
3189
+ digest: ""
3190
+ });
3191
+ if (!normalized.contract) return {
3192
+ kind: "error",
3193
+ text: `Context Guard release adopt rejected: ${normalized.errors.join(", ")}`
3194
+ };
3195
+ const contract = normalized.contract;
3196
+ const adopted = projection.releaseContracts.some((entry) => entry.contractId === contract.contractId) ? projection.releaseContracts.find((entry) => entry.contractId === contract.contractId) : contract;
3197
+ return {
3198
+ kind: "success",
3199
+ text: JSON.stringify({
3200
+ status: "adopted",
3201
+ contract_id: adopted.contractId,
3202
+ candidate: adopted.candidate,
3203
+ operations: releaseCoverage(adopted),
3204
+ readiness_refs: adopted.readinessRefs,
3205
+ closure_cert_ref: adopted.closureCertRef ?? null,
3206
+ expires_at_epoch_ms: adopted.expiresAtEpochMs ?? null,
3207
+ note: "The contract becomes authoritative from the durable root command that carried it. It grants no authority in this process."
3208
+ })
3209
+ };
3210
+ }
3211
+ if (verb === "revoke") {
3212
+ const contractId = rest.slice(6).trim();
3213
+ if (!contractId) return {
3214
+ kind: "error",
3215
+ text: "Usage: /context-guard release revoke <contract_id>"
3216
+ };
3217
+ const contract = projection.releaseContracts.find((entry) => entry.contractId === contractId);
3218
+ if (!contract) return {
3219
+ kind: "error",
3220
+ text: `Context Guard release revoke: unknown contract ${contractId}`
3221
+ };
3222
+ return {
3223
+ kind: "success",
3224
+ text: JSON.stringify({
3225
+ status: contract.revokedAtSeq === void 0 ? "revoking" : "revoked",
3226
+ contract_id: contractId,
3227
+ revoked_at_seq: contract.revokedAtSeq ?? null,
3228
+ in_flight: projection.releaseReservations.filter((reservation) => reservation.contractId === contractId && !projection.releaseSettlements.some((settlement) => settlement.callId === reservation.callId && (settlement.outcome === "settled" || settlement.outcome === "not_effected"))).map(({ operation, callId }) => ({
3229
+ operation,
3230
+ call_id: callId
3231
+ })),
3232
+ note: "Revocation is recorded durably and keeps the audit trail. It denies the next effect; an operation already in flight still needs its trusted readback to be reconciled."
3233
+ })
3234
+ };
3235
+ }
3236
+ if (verb !== "" && verb !== "status") return {
3237
+ kind: "error",
3238
+ text: "Usage: /context-guard release status | release adopt <json contract> | release revoke <contract_id>. Adoption and revocation are recorded from this command itself; they never grant authority in-process."
3239
+ };
3240
+ const contracts = projection.releaseContracts.map((contract) => ({
3241
+ contract_id: contract.contractId,
3242
+ adopted_at_seq: contract.adoptedBy.seq,
3243
+ revoked_at_seq: contract.revokedAtSeq ?? null,
3244
+ candidate: contract.candidate,
3245
+ readiness_refs: contract.readinessRefs,
3246
+ closure_cert_ref: contract.closureCertRef ?? null,
3247
+ operations: releaseCoverage(contract),
3248
+ expires_at_epoch_ms: contract.expiresAtEpochMs ?? null,
3249
+ consumed_operations: projection.releaseSettlements.filter((settlement) => settlement.contractId === contract.contractId && settlement.outcome === "settled").map((settlement) => settlement.operation),
3250
+ in_flight: projection.releaseReservations.filter((reservation) => reservation.contractId === contract.contractId && !projection.releaseSettlements.some((settlement) => settlement.callId === reservation.callId && (settlement.outcome === "settled" || settlement.outcome === "not_effected"))).map(({ operation, callId, startedAtSeq }) => ({
3251
+ operation,
3252
+ call_id: callId,
3253
+ started_at_seq: startedAtSeq
3254
+ })),
3255
+ settlements: projection.releaseSettlements.filter((settlement) => settlement.contractId === contract.contractId).map(({ operation, callId, outcome, readback: readback$1, settledAtSeq }) => ({
3256
+ operation,
3257
+ call_id: callId,
3258
+ outcome,
3259
+ readback: readback$1,
3260
+ settled_at_seq: settledAtSeq
3261
+ }))
3262
+ }));
3263
+ return {
3264
+ kind: "success",
3265
+ text: JSON.stringify({
3266
+ profile_applicable: projection.policy === "release" || contracts.length > 0,
3267
+ policy: projection.policy,
3268
+ state_damaged: projection.releaseStateDamaged,
3269
+ adopted_contracts: contracts,
3270
+ coverage_surface: RELEASE_OPERATIONS.map((operation) => ({
3271
+ operation,
3272
+ ...RELEASE_OPERATION_SURFACES[operation]
3273
+ })),
3274
+ diagnostics: projection.releaseDiagnostics,
3275
+ note: "A release is never implicit: only an explicit root adoption creates a contract, and only operations with a Guard execution surface can be protected."
3276
+ })
3277
+ };
3278
+ }
2597
3279
  function createContextGuardCommand(projectionFor, setEnabled, clearContract, lifecycleFor) {
2598
3280
  return {
2599
3281
  name: "context-guard",
2600
- description: "Enable, disable, clear, inspect, or diagnose Context Guard for this session.",
3282
+ description: "Enable, disable, clear, inspect, diagnose, or manage the explicit release contract for this session.",
2601
3283
  recordInput: true,
2602
- input: { hint: "on|off|clear|status|diagnose" },
3284
+ input: { hint: "on|off|clear|status|diagnose|migration|release status|release adopt <json>|release revoke <id>" },
2603
3285
  handler: ({ agent, rawInput }) => {
2604
3286
  const projection = projectionFor(agent);
2605
3287
  const [subcommand] = rawInput.trim().split(/\s+/, 1);
@@ -2627,22 +3309,31 @@ function createContextGuardCommand(projectionFor, setEnabled, clearContract, lif
2627
3309
  text: `Context Guard contract cleared: ${before - after} requirement/acceptance item(s) superseded; ${after} pending remain (prohibitions retained).`
2628
3310
  };
2629
3311
  }
3312
+ if (resolved === "release") return releaseResponse(projection, rawInput);
3313
+ if (resolved === "migration") return {
3314
+ kind: "success",
3315
+ text: JSON.stringify(migrationReport(projection))
3316
+ };
2630
3317
  if (resolved !== "status" && resolved !== "diagnose") return {
2631
3318
  kind: "error",
2632
- text: "Usage: /context-guard on|off|clear|status|diagnose"
3319
+ text: "Usage: /context-guard on|off|clear|status|diagnose|migration|release status|release adopt <json>|release revoke <id>"
2633
3320
  };
2634
3321
  const passed = [...projection.items.values()].filter((item) => item.status === "passed").length;
2635
3322
  let certifiable_missing_evidence = 0;
2636
3323
  let unsupported = 0;
3324
+ const reason_classes = {};
2637
3325
  for (const item of projection.items.values()) {
2638
3326
  if (item.status !== "pending") continue;
2639
3327
  const diagnosis = deriveItemDiagnosis(projection, item);
3328
+ reason_classes[diagnosis.reason_class] = (reason_classes[diagnosis.reason_class] ?? 0) + 1;
2640
3329
  if (diagnosis.repairability === "agent_repairable") certifiable_missing_evidence += 1;
2641
3330
  else if (diagnosis.certification === "unsupported") unsupported += 1;
2642
3331
  }
3332
+ const migration = migrationReport(projection);
2643
3333
  const response = {
2644
3334
  enabled: projection.enabled,
2645
3335
  lifecycle: lifecycleFor?.(agent) ?? (projection.enabled ? "active" : "disabled"),
3336
+ policy: projection.policy,
2646
3337
  epoch: projection.epoch,
2647
3338
  contract_revision: projection.contractRevision,
2648
3339
  pending: pendingCount(projection),
@@ -2650,11 +3341,25 @@ function createContextGuardCommand(projectionFor, setEnabled, clearContract, lif
2650
3341
  diagnosis: {
2651
3342
  certified: passed,
2652
3343
  certifiable_missing_evidence,
2653
- unsupported
3344
+ unsupported,
3345
+ reason_classes
2654
3346
  },
2655
3347
  evidence: projection.evidence.size,
2656
3348
  integrity: projection.integrity,
2657
- last_source_seq: projection.lastObservedSourceSeq
3349
+ last_source_seq: projection.lastObservedSourceSeq,
3350
+ migration: {
3351
+ rule_mode: migration.ruleMode,
3352
+ certificate_version: migration.certificateVersion,
3353
+ unit_closure: migration.unitClosure,
3354
+ legacy_open_items: migration.legacyItemIds.length
3355
+ },
3356
+ release: {
3357
+ policy: projection.policy,
3358
+ state_damaged: projection.releaseStateDamaged,
3359
+ adopted_contracts: projection.releaseContracts.length,
3360
+ in_flight: projection.releaseReservations.filter((reservation) => !projection.releaseSettlements.some((settlement) => settlement.callId === reservation.callId && settlement.outcome === "settled")).length,
3361
+ applicable: projection.policy === "release" || projection.releaseContracts.length > 0
3362
+ }
2658
3363
  };
2659
3364
  return {
2660
3365
  kind: "success",
@@ -3464,6 +4169,7 @@ defineMethod("transform", [
3464
4169
  //#region src/config.ts
3465
4170
  const Config = Schema.object({
3466
4171
  activation: Schema.string().default("opt-in"),
4172
+ policy: Schema.string().default("standard"),
3467
4173
  hostLockPlatform: Schema.string(),
3468
4174
  hostLockProfile: Schema.string(),
3469
4175
  hostLockPolicy: Schema.string(),
@@ -3478,6 +4184,8 @@ const Config = Schema.object({
3478
4184
  function resolveConfig(config) {
3479
4185
  const activation = config.activation ?? "opt-in";
3480
4186
  if (activation !== "opt-in" && activation !== "always") throw new TypeError(`activation must be "opt-in" or "always", received ${JSON.stringify(activation)}`);
4187
+ const policy = config.policy ?? "standard";
4188
+ if (policy !== "standard" && policy !== "strict" && policy !== "release") throw new TypeError(`policy must be "standard", "strict", or "release", received ${JSON.stringify(policy)}`);
3481
4189
  let hostLockPackages;
3482
4190
  if (config.hostLockPackages !== void 0) {
3483
4191
  if (!Array.isArray(config.hostLockPackages)) throw new TypeError("hostLockPackages must be an array");
@@ -3508,6 +4216,7 @@ function resolveConfig(config) {
3508
4216
  ...typeof config.hostLockRuntimeRoot === "string" ? { hostLockRuntimeRoot: config.hostLockRuntimeRoot } : {},
3509
4217
  ...typeof config.hostLockProfileRoot === "string" ? { hostLockProfileRoot: config.hostLockProfileRoot } : {},
3510
4218
  activation,
4219
+ policy,
3511
4220
  ...hostLockPackages ? { hostLockPackages } : {},
3512
4221
  ...config.hostLockPlatform ? { hostLockPlatform: config.hostLockPlatform } : {},
3513
4222
  ...config.hostLockProfile ? { hostLockProfile: config.hostLockProfile } : {}
@@ -3584,7 +4293,12 @@ function authorizeMutationFromProjection(projection, request) {
3584
4293
  status: "denied",
3585
4294
  reasonCode: "mutation_target_clarification_required"
3586
4295
  };
3587
- if (!requestedTargetAuthorizesMutation(request.action, item.requestedTarget, request.resolvedTarget)) return {
4296
+ const boundedAuthorized = (scope) => requestedTargetAuthorizesMutation(request.action, {
4297
+ ...item.requestedTarget,
4298
+ scope
4299
+ }, request.resolvedTarget);
4300
+ const selectionAuthorized = projection.trustedSelections.some((selection) => selection.kind === "directory" && typeof item.requestedTarget?.artifact_type === "string" && item.unitId !== void 0 && boundedAuthorized(selection.selected));
4301
+ if (!requestedTargetAuthorizesMutation(request.action, item.requestedTarget, request.resolvedTarget) && !selectionAuthorized) return {
3588
4302
  status: "denied",
3589
4303
  reasonCode: "mutation_requested_target_mismatch"
3590
4304
  };
@@ -3860,10 +4574,12 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3860
4574
  const session = agent.session;
3861
4575
  let pendingRecovery = false;
3862
4576
  let durabilityConfirmed = false;
4577
+ let durabilityWatermark = "unknown";
3863
4578
  let observedEpoch = -1;
3864
4579
  let observedCompactionSeq = -1;
3865
4580
  let observedContractRevision = -1;
3866
4581
  let protocolV4Present = false;
4582
+ let protocolV5Present = false;
3867
4583
  let realRootInputSeen = false;
3868
4584
  let lifecycle = "armed";
3869
4585
  const continuationAttempts = projection.continuationAttempts;
@@ -3882,7 +4598,10 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3882
4598
  if (!projection.integrityViolations.includes(code)) projection.integrityViolations.push(code);
3883
4599
  return;
3884
4600
  }
3885
- const derived = deriveProjection(events, { activation: config.activation }, {
4601
+ const derived = deriveProjection(events, {
4602
+ activation: config.activation,
4603
+ policy: config.policy
4604
+ }, {
3886
4605
  cwd: typeof header?.cwd === "string" ? header.cwd : "",
3887
4606
  sessionHeader
3888
4607
  }, durabilityConfirmed, hostLock);
@@ -3904,7 +4623,9 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3904
4623
  projection.continuationAttempts = continuationAttempts;
3905
4624
  projection.persistenceCorrectionAttempts = persistenceCorrectionAttempts;
3906
4625
  projection.lastRecoveryDigest = priorRecoveryDigest;
4626
+ projection.durabilityWatermark = durabilityWatermark;
3907
4627
  protocolV4Present = derived.protocolV4Present;
4628
+ protocolV5Present = derived.boundaryV5;
3908
4629
  realRootInputSeen = derived.realRootInputSeen;
3909
4630
  lifecycle = lifecyclePhase({
3910
4631
  enabled: projection.enabled,
@@ -3931,6 +4652,8 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3931
4652
  };
3932
4653
  const setDurability = (confirmed) => {
3933
4654
  durabilityConfirmed = confirmed;
4655
+ durabilityWatermark = confirmed ? "confirmed" : "failed";
4656
+ projection.durabilityWatermark = durabilityWatermark;
3934
4657
  };
3935
4658
  const markRecoveryNeeded = () => {
3936
4659
  pendingRecovery = true;
@@ -3950,6 +4673,9 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3950
4673
  get protocolV4Present() {
3951
4674
  return protocolV4Present;
3952
4675
  },
4676
+ get protocolV5Present() {
4677
+ return protocolV5Present;
4678
+ },
3953
4679
  sync,
3954
4680
  setEnabled,
3955
4681
  setDurability,
@@ -3957,6 +4683,15 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3957
4683
  consumeRecovery
3958
4684
  };
3959
4685
  }
4686
+ /** The durable session working directory, when the V3 header carries one. */
4687
+ function sessionCwd(session) {
4688
+ const header = session.header;
4689
+ return typeof header?.cwd === "string" && header.cwd.length > 0 ? header.cwd : void 0;
4690
+ }
4691
+ /** A bounded signal for a short trusted readback performed by the gate. */
4692
+ function requestSignal() {
4693
+ return AbortSignal.timeout(5e3);
4694
+ }
3960
4695
  /** Delegated/subagent sessions never receive root-conversation injections. */
3961
4696
  function isDelegatedSession(session) {
3962
4697
  const header = session.header;
@@ -3992,9 +4727,9 @@ function revalidateCoreLock(config, expected) {
3992
4727
  };
3993
4728
  }
3994
4729
  }
3995
- function apply(ctx, rawConfig = {}) {
4730
+ function apply(ctx, rawConfig = {}, seams = {}) {
3996
4731
  const config = resolveConfig(rawConfig);
3997
- const installedHostLock = evaluateHostLock(config.hostLockPackages ?? [], {
4732
+ const installedHostLock = seams.hostLock ?? evaluateHostLock(config.hostLockPackages ?? [], {
3998
4733
  platform: config.hostLockPlatform,
3999
4734
  profileKind: config.hostLockProfile
4000
4735
  });
@@ -4006,7 +4741,7 @@ function apply(ctx, rawConfig = {}) {
4006
4741
  if (!runtime) {
4007
4742
  const goals = optionalGoalService(ctx, agent);
4008
4743
  const refreshHostLock = () => {
4009
- const current = bindLiveGoalCapability(revalidateCoreLock(config, installedHostLock), Boolean(goals) && hasPinnedUpdateGoalTool(agent));
4744
+ const current = bindLiveGoalCapability(seams.hostLock ?? revalidateCoreLock(config, installedHostLock), Boolean(goals) && hasPinnedUpdateGoalTool(agent));
4010
4745
  hostLocks.set(agent, current);
4011
4746
  return current;
4012
4747
  };
@@ -4045,8 +4780,41 @@ function apply(ctx, rawConfig = {}) {
4045
4780
  runtime.sync();
4046
4781
  return durable;
4047
4782
  }, () => runtime.markRecoveryNeeded()));
4783
+ /**
4784
+ * Persist one C10 release record through the plugin-notice channel and make
4785
+ * it durable before returning. A record that cannot be flushed is reported
4786
+ * as a failure: an unflushed reservation is not an in-flight operation, and
4787
+ * an unflushed settlement would let a consumed ticket look unused.
4788
+ */
4789
+ const persistReleaseRecord = async (toolAgent, prefix, payload) => {
4790
+ const target = toolAgent.session;
4791
+ target.append.bind(target)("user/message", createUserMessage({
4792
+ content: [{
4793
+ type: "text",
4794
+ text: `${prefix}${JSON.stringify(payload)}`
4795
+ }],
4796
+ source: {
4797
+ kind: "plugin",
4798
+ plugin: "context-guard",
4799
+ form: "notice",
4800
+ summary: boundContextSummary("recording an explicit release record")
4801
+ }
4802
+ }), { surfaceOp: "append" });
4803
+ let durable = false;
4804
+ try {
4805
+ durable = await ctx.sessions.flush(target);
4806
+ } catch {
4807
+ durable = false;
4808
+ }
4809
+ runtime.setDurability(durable);
4810
+ runtime.sync();
4811
+ return durable;
4812
+ };
4048
4813
  const evidenceOptions = {
4049
4814
  hostCapability: createHostCapabilityEvaluator(hostLocks.get(agent) ?? installedHostLock),
4815
+ ...seams.commandRunner ? { commandRunner: seams.commandRunner } : {},
4816
+ ...seams.fetcher ? { fetcher: seams.fetcher } : {},
4817
+ ...seams.allowLoopbackHttpRegistry ? { allowLoopbackHttpRegistry: true } : {},
4050
4818
  prepareMutation: async (toolAgent) => {
4051
4819
  if (toolAgent.session !== agent.session) return false;
4052
4820
  const durable = await ctx.sessions.flush(agent.session);
@@ -4059,6 +4827,63 @@ function apply(ctx, rawConfig = {}) {
4059
4827
  return authorizeMutationFromProjection(runtime.projection, request);
4060
4828
  },
4061
4829
  marketOrigin: optionalMarketOrigin(ctx, agent),
4830
+ releaseGate: async (request) => {
4831
+ runtime.sync();
4832
+ const projection = runtime.projection;
4833
+ if (!(projection.policy === "release" || projection.releaseContracts.length > 0)) return {
4834
+ status: "granted",
4835
+ reasonCode: "release_profile_not_adopted"
4836
+ };
4837
+ const observed = { ...request.observed };
4838
+ const declaredRef = releaseContractFor(projection, request.operation)?.candidate.ref;
4839
+ if (declaredRef !== void 0 && observed.ref === void 0) {
4840
+ const refSha = await resolveAuditedRef(sessionCwd(runtime.session), declaredRef, evidenceOptions, requestSignal());
4841
+ if (refSha !== void 0) {
4842
+ observed.ref = declaredRef;
4843
+ observed.refSha = refSha;
4844
+ }
4845
+ }
4846
+ const decision = releasePreEffectDecision(projection, {
4847
+ operation: request.operation,
4848
+ observed,
4849
+ resolvedTarget: request.resolvedTarget,
4850
+ nowEpochMs: Date.now()
4851
+ });
4852
+ if (decision.status !== "granted" || decision.contractId === void 0) return {
4853
+ status: "denied",
4854
+ reasonCode: decision.reasonCode
4855
+ };
4856
+ return await persistReleaseRecord(agent, RELEASE_RESERVATION_PREFIX, {
4857
+ contractId: decision.contractId,
4858
+ operation: request.operation,
4859
+ callId: request.callId,
4860
+ startedAtSeq: 0,
4861
+ status: "in_flight",
4862
+ ...observed.artifactSri !== void 0 ? { observedArtifactSri: observed.artifactSri } : {}
4863
+ }) ? {
4864
+ status: "granted",
4865
+ reasonCode: decision.reasonCode,
4866
+ contractId: decision.contractId
4867
+ } : {
4868
+ status: "denied",
4869
+ reasonCode: "release_reservation_not_durable"
4870
+ };
4871
+ },
4872
+ releaseSettle: async (request) => {
4873
+ const contractId = request.contractId ?? releaseContractFor(runtime.projection, request.operation)?.contractId ?? "unknown";
4874
+ const contract = runtime.projection.releaseContracts.find((entry) => entry.contractId === contractId);
4875
+ const reservation = reservationFor(runtime.projection, contractId, request.callId);
4876
+ let outcome = request.effect === "not_effected" ? "not_effected" : "unknown";
4877
+ if (request.effect === "completed" && contract) outcome = readbackSettlesContract(contract, request.readback, reservation?.observedArtifactSri) === "settled" ? "settled" : "unknown";
4878
+ await persistReleaseRecord(agent, RELEASE_SETTLEMENT_PREFIX, {
4879
+ contractId,
4880
+ operation: request.operation,
4881
+ callId: request.callId,
4882
+ settledAtSeq: 0,
4883
+ readback: request.readback,
4884
+ outcome
4885
+ });
4886
+ },
4062
4887
  persistRestartIntent: async (toolAgent, intent) => {
4063
4888
  const session = toolAgent.session;
4064
4889
  session.append.bind(session)("user/message", createUserMessage({
@@ -4094,7 +4919,26 @@ function apply(ctx, rawConfig = {}) {
4094
4919
  reasonCode: evaluation.reasonCode
4095
4920
  };
4096
4921
  },
4097
- commandTemplate: (action) => GIT_COMMAND_TEMPLATES[action]
4922
+ commandTemplate: (action) => GIT_COMMAND_TEMPLATES[action],
4923
+ refreshProjection: async () => {
4924
+ const durable = await ctx.sessions.flush(agent.session);
4925
+ runtime.setDurability(durable);
4926
+ runtime.sync();
4927
+ return durable;
4928
+ }
4929
+ }));
4930
+ agent.ctx.tools.register(createReleaseTool({
4931
+ getProjection: () => runtime.projection,
4932
+ fetcher: evidenceOptions.fetcher,
4933
+ ...evidenceOptions.allowLoopbackHttpRegistry ? { allowLoopbackHttpRegistry: true } : {},
4934
+ persistSettlement: async (request) => persistReleaseRecord(request.agent, RELEASE_SETTLEMENT_PREFIX, {
4935
+ contractId: request.contractId,
4936
+ operation: request.operation,
4937
+ callId: request.callId,
4938
+ settledAtSeq: 0,
4939
+ readback: request.readback,
4940
+ outcome: request.outcome
4941
+ })
4098
4942
  }));
4099
4943
  agent.ctx.tools.register(createExternalOperationTool((id, toolAgent) => readExternalOperation(ctx, toolAgent, id), () => evaluateExternalWaitCapability(hostLocks.get(agent) ?? installedHostLock)));
4100
4944
  agent.ctx.tools.guard((exec) => goalCompletionDenial(runtime.projection, exec.name, exec.arguments));
@@ -4108,10 +4952,11 @@ function apply(ctx, rawConfig = {}) {
4108
4952
  if (decision.kind !== "enter") return decision;
4109
4953
  const injected = [];
4110
4954
  const delegated = isDelegatedSession(agent.session);
4111
- let boundaryPending = !runtime.protocolV4Present;
4955
+ let boundaryPending = !runtime.protocolV5Present;
4112
4956
  const firstStep = previewFirstStepInjection({
4113
4957
  activation: config.activation,
4114
4958
  enabled: runtime.projection.enabled,
4959
+ boundaryV5Present: runtime.protocolV5Present,
4115
4960
  boundaryPresent: runtime.protocolV4Present,
4116
4961
  delegated
4117
4962
  }, claimedBatchHasRealRootInput(decision.messages));
@@ -4128,7 +4973,7 @@ function apply(ctx, rawConfig = {}) {
4128
4973
  if (recovery && digest$2 !== runtime.projection.lastRecoveryDigest) {
4129
4974
  runtime.projection.lastRecoveryDigest = digest$2;
4130
4975
  if (boundaryPending && !delegated) {
4131
- injected.push(pluginNoticeMessage(PROTOCOL_V4_NOTICE, "Context Guard recorded a replay version boundary"));
4976
+ injected.push(pluginNoticeMessage(PROTOCOL_V5_NOTICE, "Context Guard recorded a replay version boundary"));
4132
4977
  boundaryPending = false;
4133
4978
  }
4134
4979
  injected.push(createUserMessage({
@@ -4272,4 +5117,4 @@ function normalizeGoalState(value) {
4272
5117
  }
4273
5118
 
4274
5119
  //#endregion
4275
- export { ACTION_MANIFEST, ACTION_MANIFEST_VERSION, ACTIVE_HOST_COHORT_ID, ACTIVE_HOST_COHORT_IDS, ACTIVE_HOST_LAUNCHER_VERSION, ALPHA2_DSHMARKET_139_HOST_PACKAGES, ALPHA2_HOST_PACKAGES, ALPHA3_HOST_PACKAGES, BASE_HOST_PACKAGES, CAPTURE_V042_NOTICE, CERTIFICATE_VERSION, COMMAND_SURFACE_MANIFEST, CONFIRM_LINE_PATTERN, CONTROL_RECORD_PREFIX, Config, DEFAULT_HOST_LOCK, DEFAULT_RECOVERY_CHAR_BUDGET, EXPECTED_HOST_PACKAGES, FIRST_STEP_GUIDANCE, GIT_COMMAND_MANIFEST_IDS, GIT_COMMAND_TEMPLATES, GOAL_HOST_PACKAGES, HOST_CAPABILITY_PACKAGE_GROUPS, HOST_COHORTS, HostProfileError, LATEST_SUPPORTED_HOST_VERSION, LEGACY_HOST_COHORTS, MIN_RECOVERY_CHAR_BUDGET, MIN_SUPPORTED_HOST_VERSION, NO_PROGRESS_RECORD_PREFIX, NO_PROGRESS_TURNS_BEFORE_STOP, PROOF_KINDS, PROOF_PROTOCOL_VERSION, PROTOCOL_V3_NOTICE, PROTOCOL_V4_NOTICE, RC015_HOST_PACKAGES, RC015_RC2_HOST_PACKAGES, RC1_HOST_PACKAGES, SEMANTIC_ACTIONS, SESSION_API_UNSUPPORTED, SESSION_EVENT_ENVELOPE_INVALID, STATEFUL_ACTIONS, STOP_PROTOCOL_VERSION, SUPPORTED_EVIDENCE_ADAPTERS, SUPPORTED_HOST_RANGE, SUPPORTED_HOST_VERSIONS, SessionApiError, actionCompatible, apply, authorityCaptureCounts, availableBoundaryQualifications, bindExecutableIdentity, bindLiveGoalCapability, bindProofToProjection, bindingSatisfies, canonicalArgvFromCommand, canonicalProjection, canonicalizePath, captureClause, captureItem, certifyCheckpoint, claimedBatchHasRealRootInput, classifyClause, classifyCompletionClaim, classifyTaskIntent, classifyUserInteraction, closingHint, combineHostPolicy, commitIndexSnapshotDigest, commitTreeSnapshotDigest, compareHostVersions, confirmRebind, createGitPrestateEnvelope, createProjection, createProofManifest, currentContractDigest, decideTurnBoundary, decideTurnStopping, decisionBoundaryKey, deriveItemDiagnosis, deriveProjection, digestStrings, effectuateBoundary, evaluateExternalWaitCapability, evaluateHostCapability, evaluateHostLock, evaluateMinimumHostVersion, evaluateToolSurfaceCapability, evidenceAvailabilityReason, evidenceCoverage, evidenceFromPersistedToolResult, evidenceMatchesItem, executeRevalidatedGitEffect, extractArtifactPaths, extractMethod, extractOperation, extractTextContent, extractToolSubject, gitCommandMatchesTarget, goalCompletionDenial, hasCurrentCertificate, hostLockContextFromComposedDump, hostLockRowsFromComposedDump, hostVersionFromPackages, inject, injectActiveProfileHostLock, inspectTargetHostGraph, interpretClause, interpretMessage, isCurrentAcceptedBoundary, isDeterministicCheck, isExecutableItem, isFrozenV042RebindResponse, isInformationalMessage, isOpenObligation, isRootPauseRequest, isRunExecutable, isStatefulAction, isVerifyingCapability, isWholeTaskCompletionClaim, itemDiagnosis, kindOfScope, latestAssistantText, latestRootInstruction, lifecyclePhase, maskCodeSpans, name, namedActions, normalizeClause, observeAssistantOutcome, openItems, packageRowsFromActiveGraph, packageRowsFromPnpmLock, parseConfirmationMessage, parseGitCommandManifest, parseHostVersion, parsePwshCommand, parseShellCommand, previewFirstStepInjection, progressFingerprint, proofDigest, proofEvidenceConstraints, proposeRebind, proposeRebindOutcome, proposeRebindV042, qualifyBoundary, readActiveHostGraph, rebindAttemptKey, rebindResponse, recoveryDigest, relevantEvidence, renderRecoveryPacket, replayRebindResult, requestedTargetAuthorizesMutation, requestedTargetMatchesResolved, resolveActiveProfileHostLock, resolveInstalledHostLock, revalidateGitPrestate, sanitizeClauseText, sanitizeUrl, satisfiesSupportedHostRange, segmentAuthorityBlocks, segmentClauses, selectHostCohort, semanticActionFromCommand, semanticActionFromText, semanticActionOfScope, sessionQuery, sha256, snapshotSessionEvents, statefulActionsOfScope, supersedeItem, validateActionManifest, validateActionTarget, validateManifest, validateProofManifest, verifiedLinearCommitReadback, verifyComposedHostLockDump, withDurability };
5120
+ export { ACTION_MANIFEST, ACTION_MANIFEST_VERSION, ACTIVE_HOST_COHORT_ID, ACTIVE_HOST_COHORT_IDS, ACTIVE_HOST_LAUNCHER_VERSION, ALPHA2_DSHMARKET_139_HOST_PACKAGES, ALPHA2_HOST_PACKAGES, ALPHA3_HOST_PACKAGES, BASE_HOST_PACKAGES, BOUNDED_ARTIFACT_TYPES, CAPTURE_V042_NOTICE, CERTIFICATE_VERSION, CERTIFICATE_VERSION_V2, COMMAND_SURFACE_MANIFEST, CONFIRM_LINE_PATTERN, CONTROL_RECORD_PREFIX, Config, DEFAULT_DELEGATION_TOOL_NAMES, DEFAULT_HOST_LOCK, DEFAULT_RECOVERY_CHAR_BUDGET, EXPECTED_HOST_PACKAGES, FIRST_STEP_GUIDANCE, GIT_COMMAND_MANIFEST_IDS, GIT_COMMAND_TEMPLATES, GOAL_HOST_PACKAGES, HOST_CAPABILITY_PACKAGE_GROUPS, HOST_COHORTS, HostProfileError, LATEST_SUPPORTED_HOST_VERSION, LEGACY_HOST_COHORTS, MIN_RECOVERY_CHAR_BUDGET, MIN_SUPPORTED_HOST_VERSION, NO_PROGRESS_RECORD_PREFIX, NO_PROGRESS_TURNS_BEFORE_STOP, PROOF_CAPABILITY_MATRIX, PROOF_KINDS, PROOF_KINDS_V2, PROOF_MANIFEST_DOMAIN_V2, PROOF_PROTOCOL_VERSION, PROOF_PROTOCOL_VERSION_V2, PROTOCOL_V3_NOTICE, PROTOCOL_V4_NOTICE, PROTOCOL_V5_NOTICE, RC015_HOST_PACKAGES, RC015_RC2_HOST_PACKAGES, RC1_HOST_PACKAGES, SEMANTIC_ACTIONS, SESSION_API_UNSUPPORTED, SESSION_EVENT_ENVELOPE_INVALID, STATEFUL_ACTIONS, STOP_PROTOCOL_VERSION, STOP_PROTOCOL_VERSION_V2, SUPPORTED_EVIDENCE_ADAPTERS, SUPPORTED_HOST_RANGE, SUPPORTED_HOST_VERSIONS, SessionApiError, actionCompatible, apply, authorityCaptureCounts, availableBoundaryQualifications, bindExecutableIdentity, bindLiveGoalCapability, bindProofToProjection, bindProofV2ToProjection, bindingSatisfies, boundedArtifactChoiceMatches, canonicalArgvFromCommand, canonicalProjection, canonicalizePath, captureClause, captureItem, certifyCheckpoint, claimedBatchHasRealRootInput, classifyClause, classifyCompletionClaim, classifyTaskIntent, classifyUserInteraction, closingHint, combineHostPolicy, commitIndexSnapshotDigest, commitTreeSnapshotDigest, compareHostVersions, confirmRebind, createGitPrestateEnvelope, createProjection, createProofManifest, createProofManifestV2, currentContractDigest, decideTurnBoundary, decideTurnStopping, decisionBoundaryKey, deriveItemDiagnosis, deriveProjection, digestStrings, effectuateBoundary, evaluateExternalWaitCapability, evaluateHostCapability, evaluateHostLock, evaluateMinimumHostVersion, evaluateToolSurfaceCapability, evidenceAvailabilityReason, evidenceCoverage, evidenceFromPersistedToolResult, evidenceMatchesItem, executeRevalidatedGitEffect, extractArtifactPaths, extractMethod, extractOperation, extractTextContent, extractToolSubject, gitCommandMatchesTarget, goalCompletionDenial, hasCurrentCertificate, hostLockContextFromComposedDump, hostLockRowsFromComposedDump, hostVersionFromPackages, inject, injectActiveProfileHostLock, inspectTargetHostGraph, interpretClause, interpretMessage, isCurrentAcceptedBoundary, isDeterministicCheck, isExecutableItem, isFrozenV042RebindResponse, isInformationalMessage, isOpenObligation, isRootPauseRequest, isRunExecutable, isStatefulAction, isVerifyingCapability, isWholeTaskCompletionClaim, itemDiagnosis, kindOfScope, latestAssistantText, latestRootInstruction, lifecyclePhase, maskCodeSpans, name, namedActions, normalizeClause, observeAssistantOutcome, openItems, packageRowsFromActiveGraph, packageRowsFromPnpmLock, parseConfirmationMessage, parseGitCommandManifest, parseHostVersion, parsePwshCommand, parseShellCommand, previewFirstStepInjection, progressFingerprint, proofCapabilityReport, proofDigest, proofDigestV2, proofEvidenceConstraints, proofHostSurfacesOf, proofOperationMatches, proofV2Rejection, proposeRebind, proposeRebindOutcome, proposeRebindV042, qualifyBoundary, readActiveHostGraph, rebindAttemptKey, rebindResponse, recoveryDigest, relevantEvidence, renderRecoveryPacket, replayRebindResult, requestedIdentityKey, requestedTargetAuthorizesMutation, requestedTargetMatchesResolved, requiredSubjectsOf, resolveActiveProfileHostLock, resolveInstalledHostLock, revalidateGitPrestate, sanitizeClauseText, sanitizeUrl, satisfiesSupportedHostRange, scopeCoverageDigest, segmentAuthorityBlocks, segmentClauses, selectHostCohort, semanticActionFromCommand, semanticActionFromText, semanticActionOfScope, sessionQuery, sessionQueryV2, sha256, snapshotSessionEvents, statefulActionsOfScope, supersedeItem, validateActionManifest, validateActionTarget, validateManifest, validateProofManifest, validateProofManifestV2, verifiedLinearCommitReadback, verifyComposedHostLockDump, withDurability };