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/CHANGELOG.md +20 -0
- package/CHANGELOG.zh-CN.md +20 -0
- package/README.md +9 -9
- package/README.zh-CN.md +9 -9
- package/dist/domain/index.d.ts +2 -2
- package/dist/domain/index.js +2 -2
- package/dist/{domain-DebMrpyX.js → domain-Cx8vpxSj.js} +924 -208
- package/dist/{index-BZYtgqlu.d.ts → index-Cd3wXBLi.d.ts} +255 -19
- package/dist/index.d.ts +2 -2
- package/dist/index.js +177 -22
- package/docs/ARCHITECTURE.md +23 -1
- package/docs/COMPATIBILITY.md +6 -2
- package/manifests/supported-host.v1.json +1 -615
- package/package.json +31 -30
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
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
|
|
3668
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
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
|
|
3893
|
+
text
|
|
3739
3894
|
}],
|
|
3740
3895
|
source: {
|
|
3741
3896
|
kind: "plugin",
|
|
3742
3897
|
plugin: "context-guard",
|
|
3743
3898
|
form: "notice",
|
|
3744
|
-
summary: boundContextSummary(
|
|
3899
|
+
summary: boundContextSummary(summaryLabel)
|
|
3745
3900
|
}
|
|
3746
|
-
})
|
|
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 };
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -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.
|
|
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
|
|
package/docs/COMPATIBILITY.md
CHANGED
|
@@ -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.
|
|
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`
|
|
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
|
|