dsh-completion-guard 0.4.3 → 0.5.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 ALPHA2_HOST_PACKAGES, $t as SEMANTIC_ACTIONS, A as verifiedLinearCommitReadback, At as evidenceAvailabilityReason, B as supersedeItem, Bt as captureClause, C as commitIndexSnapshotDigest, Ct as certifyCheckpoint, D as gitCommandMatchesTarget, Dt as openItems, E as executeRevalidatedGitEffect, Et as closingHint, F as latestAssistantText, Ft as evidenceMatchesItem, G as withDurability, Gt as extractOperation, H as extractTextContent, Ht as classifyClause, I as observeAssistantOutcome, It as isVerifyingCapability, J as parsePwshCommand, Jt as canonicalRegistryBase, K as canonicalArgvFromCommand, Kt as isInformationalMessage, L as CAPTURE_V042_NOTICE, Lt as currentContractDigest, M as decideTurnBoundary, Mt as relevantEvidence, N as decideTurnStopping, Nt as bindingSatisfies, O as parseGitCommandManifest, Ot as recoveryDigest, P as isWholeTaskCompletionClaim, Pt as evidenceCoverage, Q as ALPHA2_DSHMARKET_139_HOST_PACKAGES, Qt as CERTIFICATE_VERSION, R as PROTOCOL_V3_NOTICE, Rt as createProjection, S as GIT_COMMAND_MANIFEST_IDS, St as qualifyBoundary, T as createGitPrestateEnvelope, Tt as MIN_RECOVERY_CHAR_BUDGET, U as extractToolSubject, Ut as extractArtifactPaths, V as evidenceFromPersistedToolResult, Vt as captureItem, W as isDeterministicCheck, Wt as extractMethod, X as goalCompletionDenial, Xt as ACTION_MANIFEST, Y as parseShellCommand, Yt as npmEscapedPackageName, Z as hasCurrentCertificate, Zt as ACTION_MANIFEST_VERSION, _ as readActiveHostGraph, _n as sanitizeUrl, _t as segmentAuthorityBlocks, a as createProofManifest, an as requestedTargetAuthorizesMutation, at as HOST_COHORTS, b as verifyComposedHostLockDump, bt as effectuateBoundary, c as sessionQuery, cn as semanticActionFromText, ct as bindLiveGoalCapability, d as hostLockContextFromComposedDump, dn as COMMAND_SURFACE_MANIFEST, dt as evaluateHostLock, en as STATEFUL_ACTIONS, et as BASE_HOST_PACKAGES, f as hostLockRowsFromComposedDump, fn as validateManifest, ft as evaluateToolSurfaceCapability, g as packageRowsFromPnpmLock, gn as sanitizeClauseText, gt as authorityCaptureCounts, h as packageRowsFromActiveGraph, hn as normalizeClause, ht as ALPHA3_HOST_PACKAGES, i as canonicalProjection, in as isStatefulAction, it as HOST_CAPABILITY_PACKAGE_GROUPS, j as classifyCompletionClaim, jt as itemDiagnosis, k as revalidateGitPrestate, kt as renderRecoveryPacket, l as validateProofManifest, ln as validateActionManifest, lt as evaluateExternalWaitCapability, m as inspectTargetHostGraph, mn as digestStrings, mt as RC1_HOST_PACKAGES, n as PROOF_PROTOCOL_VERSION, nn as SUPPORTED_EVIDENCE_ADAPTERS, nt as EXPECTED_HOST_PACKAGES, o as proofDigest, on as requestedTargetMatchesResolved, ot as LEGACY_HOST_COHORTS, p as injectActiveProfileHostLock, pn as canonicalizePath, pt as selectHostCohort, q as isRunExecutable, qt as segmentClauses, r as bindProofToProjection, rn as actionCompatible, rt as GOAL_HOST_PACKAGES, s as proofEvidenceConstraints, sn as semanticActionFromCommand, st as bindExecutableIdentity, t as PROOF_KINDS, tn as STOP_PROTOCOL_VERSION, tt as DEFAULT_HOST_LOCK, u as HostProfileError, un as validateActionTarget, ut as evaluateHostCapability, v as resolveActiveProfileHostLock, vn as sha256, vt as classifyUserInteraction, w as commitTreeSnapshotDigest, wt as DEFAULT_RECOVERY_CHAR_BUDGET, x as snapshotSessionEvents, xt as isCurrentAcceptedBoundary, y as resolveInstalledHostLock, yt as availableBoundaryQualifications, z as deriveProjection, zt as rebindResponse } from "./domain-DebMrpyX.js";
1
+ import { $ as isRunExecutable, $t as itemDiagnosis, A as revalidateGitPrestate, An as canonicalizePath, At as MIN_RECOVERY_CHAR_BUDGET, B as lifecyclePhase, Bt as createProjection, C as GIT_COMMAND_TEMPLATES, Cn as semanticActionFromText, Ct as segmentAuthorityBlocks, D as executeRevalidatedGitEffect, Dn as validateManifest, Dt as qualifyBoundary, E as createGitPrestateEnvelope, En as COMMAND_SURFACE_MANIFEST, Et as isCurrentAcceptedBoundary, F as isWholeTaskCompletionClaim, Fn as sha256, Ft as bindingSatisfies, G as deriveProjection, Gt as rebindAttemptKey, H as CAPTURE_V042_NOTICE, Ht as proposeRebind, I as latestAssistantText, It as evidenceCoverage, J as extractTextContent, Jt as CONFIRM_LINE_PATTERN, K as supersedeItem, Kt as rebindResponse, L as observeAssistantOutcome, Lt as evidenceMatchesItem, M as classifyCompletionClaim, Mn as normalizeClause, Mt as openItems, N as decideTurnBoundary, Nn as sanitizeClauseText, Nt as recoveryDigest, O as gitCommandMatchesTarget, On as classifyTaskIntent, Ot as certifyCheckpoint, P as decideTurnStopping, Pn as sanitizeUrl, Pt as renderRecoveryPacket, Q as canonicalArgvFromCommand, Qt as evidenceAvailabilityReason, R as FIRST_STEP_GUIDANCE, Rt as isVerifyingCapability, S as GIT_COMMAND_MANIFEST_IDS, Sn as semanticActionFromCommand, St as authorityCaptureCounts, T as commitTreeSnapshotDigest, Tn as validateActionTarget, Tt as effectuateBoundary, U as PROTOCOL_V3_NOTICE, Ut as proposeRebindOutcome, V as previewFirstStepInjection, Vt as confirmRebind, W as PROTOCOL_V4_NOTICE, Wt as proposeRebindV042, X as isDeterministicCheck, Xt as parseConfirmationMessage, Y as extractToolSubject, Yt as isFrozenV042RebindResponse, Z as withDurability, Zt as deriveItemDiagnosis, _ as readActiveHostGraph, _n as SUPPORTED_EVIDENCE_ADAPTERS, _t as evaluateHostLock, a as createProofManifest, an as extractMethod, at as ALPHA2_HOST_PACKAGES, b as verifyComposedHostLockDump, bn as requestedTargetAuthorizesMutation, bt as RC1_HOST_PACKAGES, c as sessionQuery, cn as segmentClauses, ct as EXPECTED_HOST_PACKAGES, d as hostLockContextFromComposedDump, dn as ACTION_MANIFEST, dt as HOST_COHORTS, en as relevantEvidence, et as parsePwshCommand, f as hostLockRowsFromComposedDump, fn as ACTION_MANIFEST_VERSION, ft as LEGACY_HOST_COHORTS, g as packageRowsFromPnpmLock, gn as STOP_PROTOCOL_VERSION, gt as evaluateHostCapability, h as packageRowsFromActiveGraph, hn as STATEFUL_ACTIONS, ht as evaluateExternalWaitCapability, i as canonicalProjection, in as extractArtifactPaths, it as ALPHA2_DSHMARKET_139_HOST_PACKAGES, j as verifiedLinearCommitReadback, jn as digestStrings, jt as closingHint, k as parseGitCommandManifest, kn as classifyUserInteraction, kt as DEFAULT_RECOVERY_CHAR_BUDGET, l as validateProofManifest, ln as canonicalRegistryBase, lt as GOAL_HOST_PACKAGES, m as inspectTargetHostGraph, mn as SEMANTIC_ACTIONS, mt as bindLiveGoalCapability, n as PROOF_PROTOCOL_VERSION, nn as captureItem, nt as goalCompletionDenial, o as proofDigest, on as extractOperation, ot as BASE_HOST_PACKAGES, p as injectActiveProfileHostLock, pn as CERTIFICATE_VERSION, pt as bindExecutableIdentity, q as evidenceFromPersistedToolResult, qt as replayRebindResult, r as bindProofToProjection, rn as classifyClause, rt as hasCurrentCertificate, s as proofEvidenceConstraints, sn as isInformationalMessage, st as DEFAULT_HOST_LOCK, t as PROOF_KINDS, tn as captureClause, tt as parseShellCommand, u as HostProfileError, un as npmEscapedPackageName, ut as HOST_CAPABILITY_PACKAGE_GROUPS, v as resolveActiveProfileHostLock, vn as actionCompatible, vt as evaluateToolSurfaceCapability, w as commitIndexSnapshotDigest, wn as validateActionManifest, wt as availableBoundaryQualifications, x as snapshotSessionEvents, xn as requestedTargetMatchesResolved, xt as ALPHA3_HOST_PACKAGES, y as resolveInstalledHostLock, yn as isStatefulAction, yt as selectHostCohort, z as claimedBatchHasRealRootInput, zt as currentContractDigest } from "./domain-Cx8vpxSj.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";
@@ -812,6 +812,102 @@ function createBoundaryTool(getProjection, prepare, onRejected) {
812
812
  });
813
813
  }
814
814
 
815
+ //#endregion
816
+ //#region src/tools/prepare.ts
817
+ /**
818
+ * Thin READ-ONLY preparation surface (v0.5): before any stateful action it
819
+ * reports the supported command shape, the required resolution/effect/state
820
+ * evidence order, existing reusable references, and the exact missing fields.
821
+ * It never executes, installs, commits, pushes, restarts, or probes authority
822
+ * through side effects, and it never upgrades a default into user authority.
823
+ */
824
+ function createPrepareTool(options) {
825
+ return defineTool({
826
+ name: "context_guard_prepare",
827
+ 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.",
828
+ parameters: {
829
+ item_id: {
830
+ type: "string",
831
+ required: true
832
+ },
833
+ item_revision: { type: "number" },
834
+ semantic_action: { type: "string" },
835
+ requested_target: {
836
+ type: "object",
837
+ additionalProperties: true
838
+ },
839
+ planned_operation: { type: "string" }
840
+ },
841
+ output: {
842
+ schema: {
843
+ type: "object",
844
+ additionalProperties: true
845
+ },
846
+ render: (_args, value) => [{
847
+ type: "text",
848
+ text: JSON.stringify(value)
849
+ }]
850
+ },
851
+ async execute(rawArgs) {
852
+ const args = rawArgs;
853
+ const p = options.getProjection();
854
+ if (!p || !p.enabled || p.integrity !== "valid") return {
855
+ status: "unknown",
856
+ reason_code: "guard_unavailable"
857
+ };
858
+ const item = p.items.get(args.item_id ?? "");
859
+ if (!item) return {
860
+ status: "rejected",
861
+ reason_code: "item_not_found"
862
+ };
863
+ if (args.item_revision !== void 0 && item.revision !== args.item_revision) return {
864
+ status: "rejected",
865
+ reason_code: "item_revision_mismatch",
866
+ item_revision: item.revision
867
+ };
868
+ const diagnosis = deriveItemDiagnosis(p, item);
869
+ const plannedAction = args.semantic_action ?? item.semanticAction;
870
+ const manifestEntry = plannedAction ? ACTION_MANIFEST.actions[plannedAction] : void 0;
871
+ if (plannedAction && !manifestEntry) return {
872
+ status: "rejected",
873
+ reason_code: "unsupported_action"
874
+ };
875
+ const reusable = [...p.evidence.values()].filter((evidence) => relevantEvidence(p, item, evidence) && evidenceAvailabilityReason(evidence) === void 0).sort((a, b) => b.toolResultSeq - a.toolResultSeq).slice(0, 8).map((evidence) => ({
876
+ evidence_id: evidence.id,
877
+ role: evidence.evidenceRole,
878
+ semantic_action: evidence.semanticAction,
879
+ resolved_target: evidence.resolvedTarget,
880
+ tool_result_seq: evidence.toolResultSeq
881
+ }));
882
+ const requiredOrder = manifestEntry?.stateful ? [
883
+ "resolution (prestate facts from a trusted read)",
884
+ "effect (the exact planned change)",
885
+ "state (independent post-state readback)"
886
+ ] : ["state (matching durable evidence for the requested verification)"];
887
+ const missingTargetFields = manifestEntry?.stateful && plannedAction ? manifestEntry.resolvedTargetKeys.filter((key) => !(item.requestedTarget?.[key] !== void 0 || args.requested_target?.[key] !== void 0)) : [];
888
+ const capability = plannedAction && options.hostCapability ? options.hostCapability(plannedAction) : void 0;
889
+ return {
890
+ status: "prepared",
891
+ item: {
892
+ id: item.id,
893
+ revision: item.revision
894
+ },
895
+ diagnosis,
896
+ planned_action: plannedAction,
897
+ supported_command_shape: options.commandTemplate && plannedAction ? options.commandTemplate(plannedAction) ?? void 0 : void 0,
898
+ required_evidence_order: requiredOrder,
899
+ reusable_references: reusable,
900
+ missing_target_fields: missingTargetFields,
901
+ host_capability: capability ? {
902
+ status: capability.status,
903
+ reason_code: capability.reasonCode
904
+ } : void 0,
905
+ note: "Preparation performs no action. A default or guessed target is not user authority; explicit root instruction is required for missing target fields."
906
+ };
907
+ }
908
+ });
909
+ }
910
+
815
911
  //#endregion
816
912
  //#region src/tools/evidence.ts
817
913
  const execFileAsync = promisify(execFile);
@@ -2411,7 +2507,7 @@ function createExternalOperationTool(read, capability) {
2411
2507
  function pendingCount(projection) {
2412
2508
  return [...projection.items.values()].filter((item) => item.status === "pending").length;
2413
2509
  }
2414
- function createContextGuardCommand(projectionFor, setEnabled, clearContract) {
2510
+ function createContextGuardCommand(projectionFor, setEnabled, clearContract, lifecycleFor) {
2415
2511
  return {
2416
2512
  name: "context-guard",
2417
2513
  description: "Enable, disable, clear, inspect, or diagnose Context Guard for this session.",
@@ -2449,12 +2545,26 @@ function createContextGuardCommand(projectionFor, setEnabled, clearContract) {
2449
2545
  text: "Usage: /context-guard on|off|clear|status|diagnose"
2450
2546
  };
2451
2547
  const passed = [...projection.items.values()].filter((item) => item.status === "passed").length;
2548
+ let certifiable_missing_evidence = 0;
2549
+ let unsupported = 0;
2550
+ for (const item of projection.items.values()) {
2551
+ if (item.status !== "pending") continue;
2552
+ const diagnosis = deriveItemDiagnosis(projection, item);
2553
+ if (diagnosis.repairability === "agent_repairable") certifiable_missing_evidence += 1;
2554
+ else if (diagnosis.certification === "unsupported") unsupported += 1;
2555
+ }
2452
2556
  const response = {
2453
2557
  enabled: projection.enabled,
2558
+ lifecycle: lifecycleFor?.(agent) ?? (projection.enabled ? "active" : "disabled"),
2454
2559
  epoch: projection.epoch,
2455
2560
  contract_revision: projection.contractRevision,
2456
2561
  pending: pendingCount(projection),
2457
2562
  passed,
2563
+ diagnosis: {
2564
+ certified: passed,
2565
+ certifiable_missing_evidence,
2566
+ unsupported
2567
+ },
2458
2568
  evidence: projection.evidence.size,
2459
2569
  integrity: projection.integrity,
2460
2570
  last_source_seq: projection.lastObservedSourceSeq
@@ -3465,6 +3575,9 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3465
3575
  let observedEpoch = -1;
3466
3576
  let observedCompactionSeq = -1;
3467
3577
  let observedContractRevision = -1;
3578
+ let protocolV4Present = false;
3579
+ let realRootInputSeen = false;
3580
+ let lifecycle = "armed";
3468
3581
  const continuationAttempts = projection.continuationAttempts;
3469
3582
  const persistenceCorrectionAttempts = projection.persistenceCorrectionAttempts;
3470
3583
  const rebuild = () => {
@@ -3489,6 +3602,12 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3489
3602
  projection.continuationAttempts = continuationAttempts;
3490
3603
  projection.persistenceCorrectionAttempts = persistenceCorrectionAttempts;
3491
3604
  projection.lastRecoveryDigest = priorRecoveryDigest;
3605
+ protocolV4Present = derived.protocolV4Present;
3606
+ realRootInputSeen = derived.realRootInputSeen;
3607
+ lifecycle = lifecyclePhase({
3608
+ enabled: projection.enabled,
3609
+ realInputSeen: realRootInputSeen
3610
+ });
3492
3611
  if (observedEpoch >= 0 && derived.projection.epoch > observedEpoch) {
3493
3612
  pendingRecovery = true;
3494
3613
  projection.lastRecoveryDigest = void 0;
@@ -3523,6 +3642,12 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3523
3642
  return {
3524
3643
  projection,
3525
3644
  session,
3645
+ get lifecycle() {
3646
+ return lifecycle;
3647
+ },
3648
+ get protocolV4Present() {
3649
+ return protocolV4Present;
3650
+ },
3526
3651
  sync,
3527
3652
  setEnabled,
3528
3653
  setDurability,
@@ -3530,6 +3655,11 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
3530
3655
  consumeRecovery
3531
3656
  };
3532
3657
  }
3658
+ /** Delegated/subagent sessions never receive root-conversation injections. */
3659
+ function isDelegatedSession(session) {
3660
+ const header = session.header;
3661
+ return header?.origin === "subagent" || typeof header?.delegationDepth === "number" && header.delegationDepth > 0 || typeof header?.parentSession === "string";
3662
+ }
3533
3663
  /** Never reinterpret a legacy injected snapshot as freshly accepted core/v1. */
3534
3664
  function revalidateCoreLock(config, expected) {
3535
3665
  if (config.hostLockPolicy !== "dsh-core/v1" || !config.hostLockRuntimeRoot || !config.hostLockProfileRoot) return {
@@ -3585,10 +3715,8 @@ function apply(ctx, rawConfig = {}) {
3585
3715
  }
3586
3716
  return runtime;
3587
3717
  };
3588
- ctx.commands.register(createContextGuardCommand((agent) => ensure(agent).projection, (agent, enabled) => ensure(agent).setEnabled(enabled), (agent) => ensure(agent).sync()));
3718
+ ctx.commands.register(createContextGuardCommand((agent) => ensure(agent).projection, (agent, enabled) => ensure(agent).setEnabled(enabled), (agent) => ensure(agent).sync(), (agent) => ensure(agent).lifecycle));
3589
3719
  ctx.on("agent/session-start", ({ agent, source }) => {
3590
- ensureProtocolBoundary(agent);
3591
- ensureProtocolBoundary(agent, CAPTURE_V042_NOTICE);
3592
3720
  const runtime = ensure(agent);
3593
3721
  runtime.sync();
3594
3722
  if (source === "resume" || source === "compact") {
@@ -3655,6 +3783,17 @@ function apply(ctx, rawConfig = {}) {
3655
3783
  };
3656
3784
  agent.ctx.tools.register(createEvidenceTool(evidenceOptions));
3657
3785
  agent.ctx.tools.register(createActionTool(evidenceOptions));
3786
+ agent.ctx.tools.register(createPrepareTool({
3787
+ getProjection: () => runtime.projection,
3788
+ hostCapability: (action) => {
3789
+ const evaluation = createHostCapabilityEvaluator(hostLocks.get(agent) ?? installedHostLock)(action);
3790
+ return {
3791
+ status: evaluation.status,
3792
+ reasonCode: evaluation.reasonCode
3793
+ };
3794
+ },
3795
+ commandTemplate: (action) => GIT_COMMAND_TEMPLATES[action]
3796
+ }));
3658
3797
  agent.ctx.tools.register(createExternalOperationTool((id, toolAgent) => readExternalOperation(ctx, toolAgent, id), () => evaluateExternalWaitCapability(hostLocks.get(agent) ?? installedHostLock)));
3659
3798
  agent.ctx.tools.guard((exec) => goalCompletionDenial(runtime.projection, exec.name, exec.arguments));
3660
3799
  });
@@ -3664,12 +3803,33 @@ function apply(ctx, rawConfig = {}) {
3664
3803
  runtime.setDurability(durability);
3665
3804
  runtime.sync();
3666
3805
  const decision = await next();
3667
- if (decision.kind === "enter" && runtime.projection.enabled && runtime.consumeRecovery()) {
3668
- const recovery = renderRecoveryPacket(runtime.projection, { charBudget: 4e3 });
3806
+ if (decision.kind !== "enter") return decision;
3807
+ const injected = [];
3808
+ const delegated = isDelegatedSession(agent.session);
3809
+ let boundaryPending = !runtime.protocolV4Present;
3810
+ const firstStep = previewFirstStepInjection({
3811
+ activation: config.activation,
3812
+ enabled: runtime.projection.enabled,
3813
+ boundaryPresent: runtime.protocolV4Present,
3814
+ delegated
3815
+ }, claimedBatchHasRealRootInput(decision.messages));
3816
+ if (firstStep) {
3817
+ injected.push(pluginNoticeMessage(firstStep.boundary, "Context Guard recorded a replay version boundary"));
3818
+ if (runtime.lifecycle === "armed") injected.push(pluginNoticeMessage(firstStep.guidance, "Context Guard first-step protection guidance"));
3819
+ boundaryPending = false;
3820
+ }
3821
+ if (runtime.projection.enabled && runtime.consumeRecovery()) {
3822
+ const hasOpenWork = [...runtime.projection.items.values()].some((item) => item.status === "pending");
3823
+ const hasRejections = (runtime.projection.lastCheckpointRejections?.length ?? 0) > 0;
3824
+ const recovery = hasOpenWork || hasRejections ? renderRecoveryPacket(runtime.projection, { charBudget: 4e3 }) : void 0;
3669
3825
  const digest$2 = recovery ? recoveryDigest(recovery, runtime.projection) : void 0;
3670
3826
  if (recovery && digest$2 !== runtime.projection.lastRecoveryDigest) {
3671
3827
  runtime.projection.lastRecoveryDigest = digest$2;
3672
- decision.messages = [...decision.messages, createUserMessage({
3828
+ if (boundaryPending && !delegated) {
3829
+ injected.push(pluginNoticeMessage(PROTOCOL_V4_NOTICE, "Context Guard recorded a replay version boundary"));
3830
+ boundaryPending = false;
3831
+ }
3832
+ injected.push(createUserMessage({
3673
3833
  content: [{
3674
3834
  type: "text",
3675
3835
  text: `Open task requirements (recovered after compaction or resume):\n${recovery}`
@@ -3680,9 +3840,10 @@ function apply(ctx, rawConfig = {}) {
3680
3840
  form: "notice",
3681
3841
  summary: boundContextSummary("recovering open task requirements")
3682
3842
  }
3683
- })];
3843
+ }));
3684
3844
  }
3685
3845
  }
3846
+ if (injected.length > 0) decision.messages = [...injected, ...decision.messages];
3686
3847
  return decision;
3687
3848
  });
3688
3849
  ctx.on("agent/turn-stopping", async ({ agent }) => {
@@ -3724,26 +3885,20 @@ function optionalMarketOrigin(ctx, agent) {
3724
3885
  return `http://${service.host === "::1" ? "[::1]" : "127.0.0.1"}:${service.port}`;
3725
3886
  } catch {}
3726
3887
  }
3727
- function ensureProtocolBoundary(agent, notice = PROTOCOL_V3_NOTICE) {
3728
- if (snapshotSessionEvents(agent.session).some((event) => {
3729
- if (event.type !== "user/message" || !event.data || typeof event.data !== "object") return false;
3730
- const data = event.data;
3731
- const source = data.source && typeof data.source === "object" ? data.source : void 0;
3732
- const content = Array.isArray(data.content) ? data.content : [];
3733
- return (content.length === 1 && content[0] && typeof content[0] === "object" ? content[0].text : void 0) === notice && source?.kind === "plugin" && source.plugin === "context-guard" && source.form === "notice";
3734
- })) return;
3735
- agent.session.append.bind(agent.session)("user/message", createUserMessage({
3888
+ /** Build one host-approved plugin notice user/message for step-batch delivery. */
3889
+ function pluginNoticeMessage(text, summaryLabel) {
3890
+ return createUserMessage({
3736
3891
  content: [{
3737
3892
  type: "text",
3738
- text: notice
3893
+ text
3739
3894
  }],
3740
3895
  source: {
3741
3896
  kind: "plugin",
3742
3897
  plugin: "context-guard",
3743
3898
  form: "notice",
3744
- summary: boundContextSummary("Context Guard recorded a replay version boundary")
3899
+ summary: boundContextSummary(summaryLabel)
3745
3900
  }
3746
- }), { surfaceOp: "append" });
3901
+ });
3747
3902
  }
3748
3903
  function hasPinnedUpdateGoalTool(agent) {
3749
3904
  try {
@@ -3815,4 +3970,4 @@ function normalizeGoalState(value) {
3815
3970
  }
3816
3971
 
3817
3972
  //#endregion
3818
- export { ACTION_MANIFEST, ACTION_MANIFEST_VERSION, ALPHA2_DSHMARKET_139_HOST_PACKAGES, ALPHA2_HOST_PACKAGES, ALPHA3_HOST_PACKAGES, BASE_HOST_PACKAGES, CAPTURE_V042_NOTICE, 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, HOST_COHORTS, HostProfileError, LEGACY_HOST_COHORTS, MIN_RECOVERY_CHAR_BUDGET, PROOF_KINDS, PROOF_PROTOCOL_VERSION, PROTOCOL_V3_NOTICE, RC1_HOST_PACKAGES, SEMANTIC_ACTIONS, STATEFUL_ACTIONS, STOP_PROTOCOL_VERSION, SUPPORTED_EVIDENCE_ADAPTERS, actionCompatible, apply, authorityCaptureCounts, availableBoundaryQualifications, bindExecutableIdentity, bindLiveGoalCapability, bindProofToProjection, bindingSatisfies, canonicalArgvFromCommand, canonicalProjection, canonicalizePath, captureClause, captureItem, certifyCheckpoint, classifyClause, classifyCompletionClaim, classifyUserInteraction, closingHint, commitIndexSnapshotDigest, commitTreeSnapshotDigest, createGitPrestateEnvelope, createProjection, createProofManifest, 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, inspectTargetHostGraph, isCurrentAcceptedBoundary, isDeterministicCheck, isInformationalMessage, isRunExecutable, isStatefulAction, isVerifyingCapability, isWholeTaskCompletionClaim, latestAssistantText, name, normalizeClause, observeAssistantOutcome, openItems, packageRowsFromActiveGraph, packageRowsFromPnpmLock, parseGitCommandManifest, parsePwshCommand, parseShellCommand, proofDigest, proofEvidenceConstraints, qualifyBoundary, readActiveHostGraph, recoveryDigest, renderRecoveryPacket, requestedTargetAuthorizesMutation, requestedTargetMatchesResolved, resolveActiveProfileHostLock, resolveInstalledHostLock, revalidateGitPrestate, sanitizeClauseText, sanitizeUrl, segmentAuthorityBlocks, segmentClauses, selectHostCohort, semanticActionFromCommand, semanticActionFromText, sessionQuery, sha256, snapshotSessionEvents, supersedeItem, validateActionManifest, validateActionTarget, validateManifest, validateProofManifest, verifiedLinearCommitReadback, verifyComposedHostLockDump, withDurability };
3973
+ export { ACTION_MANIFEST, ACTION_MANIFEST_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, 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, LEGACY_HOST_COHORTS, MIN_RECOVERY_CHAR_BUDGET, PROOF_KINDS, PROOF_PROTOCOL_VERSION, PROTOCOL_V3_NOTICE, PROTOCOL_V4_NOTICE, RC1_HOST_PACKAGES, SEMANTIC_ACTIONS, STATEFUL_ACTIONS, STOP_PROTOCOL_VERSION, SUPPORTED_EVIDENCE_ADAPTERS, actionCompatible, apply, authorityCaptureCounts, availableBoundaryQualifications, bindExecutableIdentity, bindLiveGoalCapability, bindProofToProjection, bindingSatisfies, canonicalArgvFromCommand, canonicalProjection, canonicalizePath, captureClause, captureItem, certifyCheckpoint, claimedBatchHasRealRootInput, classifyClause, classifyCompletionClaim, classifyTaskIntent, classifyUserInteraction, closingHint, commitIndexSnapshotDigest, commitTreeSnapshotDigest, confirmRebind, createGitPrestateEnvelope, createProjection, createProofManifest, currentContractDigest, decideTurnBoundary, decideTurnStopping, deriveItemDiagnosis, deriveProjection, digestStrings, effectuateBoundary, evaluateExternalWaitCapability, evaluateHostCapability, evaluateHostLock, evaluateToolSurfaceCapability, evidenceAvailabilityReason, evidenceCoverage, evidenceFromPersistedToolResult, evidenceMatchesItem, executeRevalidatedGitEffect, extractArtifactPaths, extractMethod, extractOperation, extractTextContent, extractToolSubject, gitCommandMatchesTarget, goalCompletionDenial, hasCurrentCertificate, hostLockContextFromComposedDump, hostLockRowsFromComposedDump, inject, injectActiveProfileHostLock, inspectTargetHostGraph, isCurrentAcceptedBoundary, isDeterministicCheck, isFrozenV042RebindResponse, isInformationalMessage, isRunExecutable, isStatefulAction, isVerifyingCapability, isWholeTaskCompletionClaim, itemDiagnosis, latestAssistantText, lifecyclePhase, name, normalizeClause, observeAssistantOutcome, openItems, packageRowsFromActiveGraph, packageRowsFromPnpmLock, parseConfirmationMessage, parseGitCommandManifest, parsePwshCommand, parseShellCommand, previewFirstStepInjection, proofDigest, proofEvidenceConstraints, proposeRebind, proposeRebindOutcome, proposeRebindV042, qualifyBoundary, readActiveHostGraph, rebindAttemptKey, rebindResponse, recoveryDigest, relevantEvidence, renderRecoveryPacket, replayRebindResult, requestedTargetAuthorizesMutation, requestedTargetMatchesResolved, resolveActiveProfileHostLock, resolveInstalledHostLock, revalidateGitPrestate, sanitizeClauseText, sanitizeUrl, segmentAuthorityBlocks, segmentClauses, selectHostCohort, semanticActionFromCommand, semanticActionFromText, sessionQuery, sha256, snapshotSessionEvents, supersedeItem, validateActionManifest, validateActionTarget, validateManifest, validateProofManifest, verifiedLinearCommitReadback, verifyComposedHostLockDump, withDurability };
@@ -24,6 +24,24 @@ The effective plugin configuration and the DSH Session append-only log are the i
24
24
 
25
25
  The in-memory `GuardProjection` is a rebuildable cache: `deriveProjection` applies the effective activation configuration, replays the native log deterministically, recomputes every contract, evidence, and certificate, and flags `corrupt` when a recorded certificate no longer re-derives from the evidence in the log. The projection and its evidence are session-scoped: a new DSH session starts a new projection and cannot import, look up, or certify evidence IDs from another session. A completed workflow that needs a certificate must therefore produce its evidence and call `context_guard_checkpoint` in the same session. Under `always`, replay begins enabled; changing an existing profile from `opt-in` to `always` can therefore bring earlier persisted user messages into the derived contract. A recorded `/context-guard off` disables capture from that point until a later `on`. A recorded `/context-guard clear` supersedes every pending requirement and acceptance under a `CLEAR:<revision>` sentinel (prohibitions are retained) and bumps the contract revision, so a fresh empty-binding checkpoint can certify while the guard stays enabled. Captured contracts always carry a concrete subject/surface, so no unrelated evidence can close a requirement.
26
26
 
27
+ ## Session lifecycle: silent start, first-message activation
28
+
29
+ The runtime separates what it injects from what it certifies. An internal lifecycle phase — `armed` (protection enabled, waiting for the first real root input), `active` (real input entered a step), `disabled` (explicitly off) — expresses the startup strategy only; certification still depends solely on durable events, the current contract, and the evidence chain.
30
+
31
+ Session start (`agent/session-start`) registers tools and the runtime, reads history, and arms recovery for resume/compact. It appends nothing, so a fresh session stays blank (`seq === 0`) and a DSH preset can still be selected while the session is new. The rc.1 Web surface treats a session as blank until a `turn/start` (server preset lock) and falls back to `seq === 0` for cold list entries; Guard writes no message at T0, so both stay satisfiable.
32
+
33
+ At `agent/pre-step` the loop has CLAIMED this step's input but has not persisted it yet. A pure preview reads only the validated claim: under `always`, the first claimed batch that carries a real root user message (non-empty text, or any image/attachment part) injects the `Context Guard protocol boundary: v4.0.0` notice and a compact first-step guidance message ahead of the claimed messages, inside the same step batch. The formal projection never derives from the preview — the boundary and the user message become durable events when the loop appends the batch, and the contract is re-derived from them exactly as from any other log. `opt-in` never auto-injects; delegated/subagent sessions receive no boundary, guidance, or activation (their scope arrives through the parent's delegation prompt). Rejection, cancellation, or a filtered batch injects nothing and retries on the next legal input.
34
+
35
+ Recovery packets keep the existing content-dedup and ride the next entered step. The first 0.5 write into a pre-0.5 session carries the v4 boundary with it — that message is the explicit version cut: obligations and certificates recorded before it keep the frozen 0.4 rules (including exact-shape rebind replay), while later messages use the 0.5 confirmation grammar.
36
+
37
+ ## Unified diagnosis and preparation
38
+
39
+ `deriveItemDiagnosis` is the single pure judge shared by checkpoint, recovery, the rebind item query, `context_guard_prepare`, and `/context-guard status`. Per item it reports a task kind (`inquiry`/`action`/`constraint`/…), certification support (`unsupported`/`needs_target`/`needs_evidence`/`unavailable`/`supported`), repairability (agent-repairable, needs user input, unsupported, historical gap, none), the exact missing target fields and evidence facets, and one concrete next action with a resume condition and a stable attempt fingerprint. Inquiries stay captured obligations whose honest next step is to deliver the answer; they are never prescribed a rebind. An effect recorded without its resolution prestate is a historical gap: read back observed state, never re-execute to mint evidence.
40
+
41
+ `context_guard_prepare` is read-only. Before a stateful action it renders the supported command shape from the same audited parser the executor uses, the required resolution/effect/state order, reusable evidence references, the host capability verdict, and the exact missing target fields. It performs no action and never turns a guessed default into user authority.
42
+
43
+ A log-derived retry ledger keys each rejected rebind attempt by its stable inputs and outcome. An identical second attempt returns `unchanged` with the resume condition instead of a fresh rejection; new related evidence, a new root instruction, or a changed target produces a new key and re-opens evaluation.
44
+
27
45
  ## Synchronization
28
46
 
29
47
  The runtime rebuilds the projection from the log before each step. Before evidence is produced, the runtime awaits `ctx.sessions.flush(session)`; if no durability listener participated, evidence is marked `durability-unknown`, which fails closed.
@@ -53,7 +71,11 @@ A legal plugin `user/message` notice marks the 0.4.2 capture boundary. Earlier e
53
71
 
54
72
  `context_guard_rebind` accepts `operation: propose|query|withdraw`, `item_id`, `proposal_id`, `clauses`, and optional `clarification_item_ids`. It has no model confirmation flag. A proposal's 1–8 clauses must concatenate to the complete original normalized text, and its serialized body must fit 8 KiB. Each clarification ID is either empty (keep the source clause) or names a later pending direct-root item that explicitly includes that clause and preserves captured identities and method constraints. The proposal records both root sources, item/revision identities, session, epoch, contract revision, candidate action/target/acceptance, and a digest. A GUI clause cannot be mapped to an installation-only result.
55
73
 
56
- The tool returns a candidate without mutating the projection. A matching durable tool result registers the proposal on replay. Only a complete canonical root message `确认重绑定 <proposal ID>` commits it. Confirmation rechecks the proposal against the current contract; changed, withdrawn, cross-session, or non-durable inputs keep the old item pending. Every child is constructed before the original is marked superseded. `supersededByItems` preserves the one-to-many relation, and `reboundFrom` preserves the proposal and confirmation source. A later root clarification keeps its own authority; partitioning alone cannot authorize a new mutation. Duplicate confirmation is idempotent.
74
+ The tool returns a candidate without mutating the projection. A matching durable tool result registers the proposal on replay. Replay validation is versioned: 0.5-era results match on structured semantic fields (status, reason code, proposal identity and digest) while display text may evolve; results carrying the frozen 0.4 response shapes validate byte-for-byte against the frozen 0.4 rules. Anything else is tampered or unknown and never replays.
75
+
76
+ A proposal that would keep every clause at `generic_run` when the original is already `generic_run` is refused as `no_certification_gain`: it would spend a user confirmation without improving certification. Partition mismatches return the bounded exact source (text, length, digest) instead of a generic error.
77
+
78
+ Since 0.5.0 a durable root message is one atomic confirmation transaction. The first non-empty top-level line may be the control line `确认重绑定 <proposal ID>`; the confirmation validates against the state BEFORE that message, and the remaining lines keep their own semantics — an explanation request stays conversational, a new task is captured like any other instruction, and an explicit reversal ("先不要确认") makes the whole message ambiguous and unapplied. A control string buried in a sentence, inside quotes, or inside a code block is malformed and never confirms; a matching control line that is not in first position, or two control lines, is ambiguous with no partial effect. Confirmation rechecks the proposal against the current contract; changed, withdrawn, cross-session, or non-durable inputs keep the old item pending, and a matching confirmation observed in a not-yet-durable log is reported as `not_durable` — distinguishable from one that was never sent. Every child is constructed before the original is marked superseded. `supersededByItems` preserves the one-to-many relation, and `reboundFrom` preserves the proposal and confirmation source. A later root clarification keeps its own authority; partitioning alone cannot authorize a new mutation. Duplicate confirmation is idempotent.
57
79
 
58
80
  No digest-v3 wire format or upstream fixture changes are needed. A replacement changes the item/status set and contract revision, invalidating the earlier certificate; evidence is never copied into a passed state. A fresh checkpoint must revalidate the exact action, target, executor, host lock, and transition. Evidence predating the authoritative root clause referenced by a replacement is rejected while retaining its historical ID.
59
81
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  Compatibility is pinned to exact host package sets. A nearby version or a partial package match is not treated as supported.
4
4
 
5
+ ## 0.5.0 support policy
6
+
7
+ The active support target is exactly DSH `0.1.2-rc.1` with Cordis `4.0.2`. The npm peer dependencies advertise only `0.1.2-rc.1`, and the active host allowlist contains that single audited core graph. Alpha package sets and older RC sets remain recorded as historical identities in the shipped manifest and source registry so previously accepted annexes stay verifiable, but an installed runtime built from one of those sets fails closed (`host_lock_version_mismatch`) instead of certifying. No floating version range and no alpha support is claimed; future compatibility work starts from the next upstream RC.
8
+
5
9
  ## 0.4.3 core-lock policy
6
10
 
7
11
  `dsh-core/v1` uses manifest version 2 and four exact 33-package DSH core graphs, retaining the previously audited DSH and Cordis versions. Market is not a core row. Its transitive dependencies remain part of active-graph traversal, so replacing or duplicating a core dependency still blocks certification. No floating DSH version range is introduced.
@@ -32,7 +36,7 @@ DSH rc.1 replaces the public `Session.events` getter with `snapshotEvents()` and
32
36
 
33
37
  ## Upstream adaptation policy
34
38
 
35
- Version 0.4.0 remains frozen on the alpha.3 setup above. Alpha.4 and later alpha releases are not new adaptation targets. Compatibility work resumed with DSH `0.1.2-rc.1`; that cohort is included in the published `0.4.1-rc.1` prerelease and retained by the 0.4.2 release. The upstream [tags page](https://github.com/deepseek-ai/deepseek-harness/tags) tracks later releases; a newer tag does not establish support.
39
+ Version 0.5.0 targets DSH `0.1.2-rc.1`; alpha releases are observed for trend only and are never adaptation or validation targets. A newer upstream tag does not establish support by itself; support starts when that exact RC or release is added as its own audited cohort with source, CI, and native acceptance. The upstream [tags page](https://github.com/deepseek-ai/deepseek-harness/tags) tracks later releases.
36
40
 
37
41
  ## Platform and release evidence
38
42
 
@@ -64,7 +68,7 @@ Market versions do not select a core cohort. Market restart has its own protocol
64
68
 
65
69
  The package exposes a named `apply(ctx)` function and a named `inject` array (`['sessions', 'commands']`) with no default export. Its `dsh.bundle.patch` points at `cordis.patch.yml`, which inserts the `context-guard` bundle row.
66
70
 
67
- The plugin accepts an `activation` configuration value of `opt-in` or `always`. The default is `opt-in`; `always` initializes the projection as enabled before the persisted session log is replayed. Invalid values fail during plugin configuration instead of silently falling back. A DSH profile can select `always` with an ID-targeted `config` override in its `cordis.patch.yml`; see the README quick start for the complete example and the replay implications for existing sessions.
71
+ The plugin accepts an `activation` configuration value of `opt-in` or `always`. The default is `opt-in`; `always` means every session is protected automatically from its first real user message. Since 0.5.0, session start writes nothing into the session log: the versioned protocol boundary and first-step guidance are delivered inside the same step batch as and ahead of — the first real user message, so a new session stays blank (`seq === 0`) and a DSH preset can be selected before anything is sent. An explicit `off` suppresses `always` in that session until the next `on`. Invalid values fail during plugin configuration instead of silently falling back. A DSH profile can select `always` with an ID-targeted `config` override in its `cordis.patch.yml`; see the README quick start for the complete example.
68
72
 
69
73
  ### Host-lock setup
70
74