dsh-completion-guard 0.4.2 → 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 +30 -1
- package/CHANGELOG.zh-CN.md +30 -1
- package/README.md +18 -24
- package/README.zh-CN.md +18 -24
- package/bin/dsh-completion-guard-host-lock.mjs +15 -2
- package/dist/domain/index.d.ts +2 -2
- package/dist/domain/index.js +2 -2
- package/dist/{domain-BXB_BPQg.js → domain-Cx8vpxSj.js} +1169 -278
- package/dist/{index-DNF0OTi-.d.ts → index-Cd3wXBLi.d.ts} +279 -20
- package/dist/index.d.ts +8 -2
- package/dist/index.js +270 -41
- package/docs/ARCHITECTURE.md +26 -2
- package/docs/COMPATIBILITY.md +22 -16
- package/docs/HOST_LOCK_UPGRADE.md +83 -0
- package/docs/LOCAL_ACCEPTANCE.md +16 -6
- package/docs/SEMANTIC_COMPATIBILITY.md +4 -2
- package/manifests/action-manifest.v1.json +1 -1
- package/manifests/supported-host.v1.json +221 -14
- 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);
|
|
@@ -900,7 +996,7 @@ function tuple(value) {
|
|
|
900
996
|
function adapterId(action) {
|
|
901
997
|
if (["create", "modify"].includes(action)) return "context-guard.artifact.v1";
|
|
902
998
|
if (["install", "apply"].includes(action)) return "context-guard.package.v1";
|
|
903
|
-
if (action === "restart") return "context-guard.service.
|
|
999
|
+
if (action === "restart") return "context-guard.service.v2";
|
|
904
1000
|
if (action === "publish") return "context-guard.registry.v1";
|
|
905
1001
|
return "context-guard.git.v1";
|
|
906
1002
|
}
|
|
@@ -913,7 +1009,7 @@ function unavailable(action, role, reason) {
|
|
|
913
1009
|
resolved_target: {},
|
|
914
1010
|
observed_state: {},
|
|
915
1011
|
adapter_id: adapterId(action),
|
|
916
|
-
adapter_version: PRODUCER_VERSION,
|
|
1012
|
+
adapter_version: action === "restart" ? "2.0.0" : PRODUCER_VERSION,
|
|
917
1013
|
target_digest: "",
|
|
918
1014
|
command_manifest_digest: ""
|
|
919
1015
|
};
|
|
@@ -927,7 +1023,7 @@ function supported(action, role, resolved, observed = {}, commandManifest = {},
|
|
|
927
1023
|
resolved_target: jsonTuple(resolved),
|
|
928
1024
|
observed_state: jsonTuple(observed),
|
|
929
1025
|
adapter_id: adapterId(action),
|
|
930
|
-
adapter_version: PRODUCER_VERSION,
|
|
1026
|
+
adapter_version: action === "restart" ? "2.0.0" : PRODUCER_VERSION,
|
|
931
1027
|
target_digest: digest(resolved),
|
|
932
1028
|
command_manifest_digest: digest(commandManifest),
|
|
933
1029
|
...gitBinding ? { git_binding: gitBinding } : {},
|
|
@@ -972,7 +1068,7 @@ function findResolution(events, callId, action) {
|
|
|
972
1068
|
}
|
|
973
1069
|
if (event.type !== "tool/result") continue;
|
|
974
1070
|
const meta = producerMeta(event);
|
|
975
|
-
if (meta?.semanticAction !== action || meta.evidenceRole !== "resolution" || meta.adapterVersion !== PRODUCER_VERSION || !selector || !commandManifest) return void 0;
|
|
1071
|
+
if (meta?.semanticAction !== action || meta.evidenceRole !== "resolution" || meta.adapterVersion !== (action === "restart" ? "2.0.0" : PRODUCER_VERSION) || !selector || !commandManifest) return void 0;
|
|
976
1072
|
const target = tuple(meta.resolvedTarget);
|
|
977
1073
|
const rawBinding = record(meta.gitBinding);
|
|
978
1074
|
const rawManifest = record(rawBinding?.manifest);
|
|
@@ -1155,13 +1251,13 @@ async function tgzIdentity(path) {
|
|
|
1155
1251
|
integrity: `sha512-${createHash("sha512").update(bytes).digest("base64")}`
|
|
1156
1252
|
};
|
|
1157
1253
|
}
|
|
1158
|
-
const DSHMARKET_VERSION = "1.36.0";
|
|
1159
|
-
const DSHMARKET_INTEGRITY = "sha512-xX8CCoXdIALaxtLosj+5qGg8r1cykW2zo1AOPJcSQepg2r4Vd2K0NmERldDqfeyFV0pCuZsUoAPe1Q/BW7De/g==";
|
|
1160
1254
|
const MARKET_SCHEMA = "dsh-market/update-api/v1";
|
|
1255
|
+
const MARKET_GENERATION_PREFIX = "dsh-market-instance/v1:";
|
|
1161
1256
|
async function marketCapabilities(roots, signal) {
|
|
1162
|
-
if (!roots.profile || !roots.marketOrigin) return void 0;
|
|
1257
|
+
if (!roots.profile || !roots.marketOrigin || !roots.verifyMarketInstance) return void 0;
|
|
1163
1258
|
const installed = await profilePackage(roots.profile.path, "dshmarket");
|
|
1164
|
-
|
|
1259
|
+
const binding = await roots.verifyMarketInstance(signal);
|
|
1260
|
+
if (!installed || !binding || binding.origin !== roots.marketOrigin || binding.profile !== roots.profile.name || binding.version !== installed.version || binding.integrity !== installed.integrity || !/^[0-9a-f]{64}$/.test(binding.loadedTreeSha256) || !binding.processIdentity || !binding.bootId) return void 0;
|
|
1165
1261
|
const response = await (roots.fetcher ?? fetch)(`${roots.marketOrigin}/dsh-market/api/v1/capabilities`, {
|
|
1166
1262
|
signal,
|
|
1167
1263
|
headers: { accept: "application/json" },
|
|
@@ -1169,8 +1265,32 @@ async function marketCapabilities(roots, signal) {
|
|
|
1169
1265
|
});
|
|
1170
1266
|
if (!response.ok) return void 0;
|
|
1171
1267
|
const value = record(await response.json());
|
|
1172
|
-
if (value?.schema !== MARKET_SCHEMA || value.apiVersion !== 1 || value.marketVersion !==
|
|
1173
|
-
|
|
1268
|
+
if (value?.schema !== MARKET_SCHEMA || value.apiVersion !== 1 || value.marketVersion !== binding.version || value.profile !== binding.profile || value.bootId !== binding.bootId) return void 0;
|
|
1269
|
+
const features = record(value.features);
|
|
1270
|
+
const restart = record(value.restart);
|
|
1271
|
+
if (features?.restart !== true || restart?.supported !== true || restart.managedBy !== "market") return void 0;
|
|
1272
|
+
const provider = createHash("sha256").update(JSON.stringify([
|
|
1273
|
+
MARKET_SCHEMA,
|
|
1274
|
+
binding.origin,
|
|
1275
|
+
binding.profile,
|
|
1276
|
+
binding.version,
|
|
1277
|
+
binding.integrity,
|
|
1278
|
+
binding.loadedTreeSha256,
|
|
1279
|
+
"restart",
|
|
1280
|
+
true,
|
|
1281
|
+
"market"
|
|
1282
|
+
])).digest("hex");
|
|
1283
|
+
const instance = createHash("sha256").update(JSON.stringify([binding.processIdentity, binding.bootId])).digest("hex");
|
|
1284
|
+
return {
|
|
1285
|
+
...value,
|
|
1286
|
+
generation: `${MARKET_GENERATION_PREFIX}${provider}:${instance}`
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
function sameMarketProvider(before, after) {
|
|
1290
|
+
const pattern = /^dsh-market-instance\/v1:([0-9a-f]{64}):[0-9a-f]{64}$/;
|
|
1291
|
+
const left = typeof before === "string" ? pattern.exec(before) : null;
|
|
1292
|
+
const right = typeof after === "string" ? pattern.exec(after) : null;
|
|
1293
|
+
return !!left && !!right && left[1] === right[1];
|
|
1174
1294
|
}
|
|
1175
1295
|
function importerLocator(text, packageId) {
|
|
1176
1296
|
const escaped = packageId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1200,6 +1320,7 @@ async function profilePackage(profilePath, packageId) {
|
|
|
1200
1320
|
try {
|
|
1201
1321
|
const lock = await readFile(resolve(profilePath, "pnpm-lock.yaml"), "utf8");
|
|
1202
1322
|
const locator = importerLocator(lock, packageId);
|
|
1323
|
+
if (packageId === "dshmarket" && (!locator || locator.split("(", 1)[0] !== manifest.version)) return void 0;
|
|
1203
1324
|
const integrity = locator ? lockIntegrity(lock, packageId, locator) : void 0;
|
|
1204
1325
|
return integrity ? {
|
|
1205
1326
|
version: manifest.version,
|
|
@@ -1354,7 +1475,8 @@ async function executeGuardAction(action, resolution, roots, signal, executableI
|
|
|
1354
1475
|
if (action === "restart") {
|
|
1355
1476
|
const capabilities = await marketCapabilities(roots, signal);
|
|
1356
1477
|
if (!capabilities || !roots.marketOrigin || !resolutionCallId || !agent) return "unavailable";
|
|
1357
|
-
if (
|
|
1478
|
+
if (!sameMarketProvider(target.pre_generation, capabilities.generation)) return "unavailable";
|
|
1479
|
+
if (capabilities.generation !== target.pre_generation) return restartIntent(snapshotSessionEvents(agent.session), resolutionCallId, target) ? "completed" : "unavailable";
|
|
1358
1480
|
if (restartIntent(snapshotSessionEvents(agent.session), resolutionCallId, target)) return "handoff_pending";
|
|
1359
1481
|
if (!roots.persistRestartIntent || !await roots.persistRestartIntent(agent, {
|
|
1360
1482
|
resolutionCallId,
|
|
@@ -1629,7 +1751,7 @@ async function resolveTarget(action, selector, commandManifest, cwd, roots, sign
|
|
|
1629
1751
|
if (features?.restart !== true || restart?.supported !== true || restart.managedBy !== "market") return void 0;
|
|
1630
1752
|
return { target: {
|
|
1631
1753
|
service_id: service,
|
|
1632
|
-
pre_generation: String(capabilities?.
|
|
1754
|
+
pre_generation: String(capabilities?.generation)
|
|
1633
1755
|
} };
|
|
1634
1756
|
}
|
|
1635
1757
|
if (action === "publish") {
|
|
@@ -1770,9 +1892,9 @@ async function readback(action, target, roots, signal, executableIdentity$1) {
|
|
|
1770
1892
|
}
|
|
1771
1893
|
if (action === "restart") {
|
|
1772
1894
|
const capabilities = await marketCapabilities(roots, signal);
|
|
1773
|
-
if (!capabilities || capabilities.
|
|
1895
|
+
if (!capabilities || !sameMarketProvider(target.pre_generation, capabilities.generation) || capabilities.generation === target.pre_generation) return void 0;
|
|
1774
1896
|
return {
|
|
1775
|
-
new_generation: String(capabilities.
|
|
1897
|
+
new_generation: String(capabilities.generation),
|
|
1776
1898
|
health: "healthy"
|
|
1777
1899
|
};
|
|
1778
1900
|
}
|
|
@@ -1850,6 +1972,7 @@ function normalizedRoots(options) {
|
|
|
1850
1972
|
path: resolve(options.profile.path)
|
|
1851
1973
|
} } : detectedProfile ? { profile: detectedProfile } : {},
|
|
1852
1974
|
...options.marketOrigin ? { marketOrigin: options.marketOrigin } : {},
|
|
1975
|
+
...options.verifyMarketInstance ? { verifyMarketInstance: options.verifyMarketInstance } : {},
|
|
1853
1976
|
...options.fetcher ? { fetcher: options.fetcher } : {},
|
|
1854
1977
|
...options.commandRunner ? { commandRunner: options.commandRunner } : {},
|
|
1855
1978
|
...options.persistRestartIntent ? { persistRestartIntent: options.persistRestartIntent } : {},
|
|
@@ -2258,7 +2381,7 @@ function createEvidenceTool(options = {}) {
|
|
|
2258
2381
|
const executableBinding = executable$1 ? await (roots.readExecutableIdentity ?? executableIdentity)(executable$1, exec.signal) : void 0;
|
|
2259
2382
|
if (executable$1 && !executableBinding) return unavailable(action, role, "executable_identity_unavailable");
|
|
2260
2383
|
const resolved = await resolveTarget(action, record(args.selector) ?? {}, record(args.command_manifest) ?? {}, cwdOf(agent), roots, exec.signal, executableBinding);
|
|
2261
|
-
if (!resolved) return unavailable(action, role, "resolution_unavailable");
|
|
2384
|
+
if (!resolved) return unavailable(action, role, action === "restart" && !roots.verifyMarketInstance ? "market_instance_binding_unavailable" : "resolution_unavailable");
|
|
2262
2385
|
const gitBinding = resolved.gitBinding ? JSON.parse(JSON.stringify(resolved.gitBinding)) : void 0;
|
|
2263
2386
|
const commandManifest = record(args.command_manifest) ?? {};
|
|
2264
2387
|
const expectedTransition = await expectedTransitionForResolution(action, resolved.target, commandManifest);
|
|
@@ -2288,7 +2411,7 @@ function createEvidenceTool(options = {}) {
|
|
|
2288
2411
|
if (ownedEffect) {
|
|
2289
2412
|
const events = snapshotSessionEvents(agent.session);
|
|
2290
2413
|
const actionCall = actionCallMatches(events, args.effect_call_id, action, args.resolution_call_id, resolution.target);
|
|
2291
|
-
if (!(action === "restart" ? actionCall && restartIntent(events, args.resolution_call_id, resolution.target) &&
|
|
2414
|
+
if (!(action === "restart" ? actionCall && restartIntent(events, args.resolution_call_id, resolution.target) && !!await readback("restart", resolution.target, roots, exec.signal) : actionCall && actionResultCompleted(events, args.effect_call_id))) return unavailable(action, role, "persisted_effect_mismatch");
|
|
2292
2415
|
if (role === "effect") return supported(action, role, resolution.target, {}, resolution.commandManifest, void 0, currentExecutable);
|
|
2293
2416
|
} else {
|
|
2294
2417
|
const effect = findEffect(snapshotSessionEvents(agent.session), args.effect_call_id);
|
|
@@ -2384,7 +2507,7 @@ function createExternalOperationTool(read, capability) {
|
|
|
2384
2507
|
function pendingCount(projection) {
|
|
2385
2508
|
return [...projection.items.values()].filter((item) => item.status === "pending").length;
|
|
2386
2509
|
}
|
|
2387
|
-
function createContextGuardCommand(projectionFor, setEnabled, clearContract) {
|
|
2510
|
+
function createContextGuardCommand(projectionFor, setEnabled, clearContract, lifecycleFor) {
|
|
2388
2511
|
return {
|
|
2389
2512
|
name: "context-guard",
|
|
2390
2513
|
description: "Enable, disable, clear, inspect, or diagnose Context Guard for this session.",
|
|
@@ -2422,12 +2545,26 @@ function createContextGuardCommand(projectionFor, setEnabled, clearContract) {
|
|
|
2422
2545
|
text: "Usage: /context-guard on|off|clear|status|diagnose"
|
|
2423
2546
|
};
|
|
2424
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
|
+
}
|
|
2425
2556
|
const response = {
|
|
2426
2557
|
enabled: projection.enabled,
|
|
2558
|
+
lifecycle: lifecycleFor?.(agent) ?? (projection.enabled ? "active" : "disabled"),
|
|
2427
2559
|
epoch: projection.epoch,
|
|
2428
2560
|
contract_revision: projection.contractRevision,
|
|
2429
2561
|
pending: pendingCount(projection),
|
|
2430
2562
|
passed,
|
|
2563
|
+
diagnosis: {
|
|
2564
|
+
certified: passed,
|
|
2565
|
+
certifiable_missing_evidence,
|
|
2566
|
+
unsupported
|
|
2567
|
+
},
|
|
2431
2568
|
evidence: projection.evidence.size,
|
|
2432
2569
|
integrity: projection.integrity,
|
|
2433
2570
|
last_source_seq: projection.lastObservedSourceSeq
|
|
@@ -3242,6 +3379,9 @@ const Config = Schema.object({
|
|
|
3242
3379
|
activation: Schema.string().default("opt-in"),
|
|
3243
3380
|
hostLockPlatform: Schema.string(),
|
|
3244
3381
|
hostLockProfile: Schema.string(),
|
|
3382
|
+
hostLockPolicy: Schema.string(),
|
|
3383
|
+
hostLockRuntimeRoot: Schema.string(),
|
|
3384
|
+
hostLockProfileRoot: Schema.string(),
|
|
3245
3385
|
hostLockPackages: Schema.array(Schema.object({
|
|
3246
3386
|
name: Schema.string().required(),
|
|
3247
3387
|
version: Schema.string(),
|
|
@@ -3271,7 +3411,15 @@ function resolveConfig(config) {
|
|
|
3271
3411
|
if (config.hostLockProfile !== void 0 && config.hostLockProfile !== "headless" && config.hostLockProfile !== "web") throw new TypeError("hostLockProfile must be \"headless\" or \"web\"");
|
|
3272
3412
|
const hasHostRows = hostLockPackages !== void 0;
|
|
3273
3413
|
if (hasHostRows !== (config.hostLockPlatform !== void 0) || hasHostRows !== (config.hostLockProfile !== void 0)) throw new TypeError("hostLockPackages, hostLockPlatform, and hostLockProfile must be injected together");
|
|
3414
|
+
for (const key of [
|
|
3415
|
+
"hostLockPolicy",
|
|
3416
|
+
"hostLockRuntimeRoot",
|
|
3417
|
+
"hostLockProfileRoot"
|
|
3418
|
+
]) if (config[key] !== void 0 && (typeof config[key] !== "string" || !config[key])) throw new TypeError(`${key} must be a non-empty string`);
|
|
3274
3419
|
return {
|
|
3420
|
+
...typeof config.hostLockPolicy === "string" ? { hostLockPolicy: config.hostLockPolicy } : {},
|
|
3421
|
+
...typeof config.hostLockRuntimeRoot === "string" ? { hostLockRuntimeRoot: config.hostLockRuntimeRoot } : {},
|
|
3422
|
+
...typeof config.hostLockProfileRoot === "string" ? { hostLockProfileRoot: config.hostLockProfileRoot } : {},
|
|
3275
3423
|
activation,
|
|
3276
3424
|
...hostLockPackages ? { hostLockPackages } : {},
|
|
3277
3425
|
...config.hostLockPlatform ? { hostLockPlatform: config.hostLockPlatform } : {},
|
|
@@ -3419,7 +3567,7 @@ function sessionHeaderForDigest(session) {
|
|
|
3419
3567
|
delegationDepth: typeof raw.delegationDepth === "number" ? raw.delegationDepth : 0
|
|
3420
3568
|
};
|
|
3421
3569
|
}
|
|
3422
|
-
function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalState) {
|
|
3570
|
+
function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalState, refreshHostLock) {
|
|
3423
3571
|
const projection = createProjection();
|
|
3424
3572
|
const session = agent.session;
|
|
3425
3573
|
let pendingRecovery = false;
|
|
@@ -3427,9 +3575,13 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
|
|
|
3427
3575
|
let observedEpoch = -1;
|
|
3428
3576
|
let observedCompactionSeq = -1;
|
|
3429
3577
|
let observedContractRevision = -1;
|
|
3578
|
+
let protocolV4Present = false;
|
|
3579
|
+
let realRootInputSeen = false;
|
|
3580
|
+
let lifecycle = "armed";
|
|
3430
3581
|
const continuationAttempts = projection.continuationAttempts;
|
|
3431
3582
|
const persistenceCorrectionAttempts = projection.persistenceCorrectionAttempts;
|
|
3432
3583
|
const rebuild = () => {
|
|
3584
|
+
if (refreshHostLock) hostLock = refreshHostLock();
|
|
3433
3585
|
const header = session.header;
|
|
3434
3586
|
const priorRecoveryDigest = projection.lastRecoveryDigest;
|
|
3435
3587
|
const derived = deriveProjection(snapshotSessionEvents(session), { activation: config.activation }, {
|
|
@@ -3450,6 +3602,12 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
|
|
|
3450
3602
|
projection.continuationAttempts = continuationAttempts;
|
|
3451
3603
|
projection.persistenceCorrectionAttempts = persistenceCorrectionAttempts;
|
|
3452
3604
|
projection.lastRecoveryDigest = priorRecoveryDigest;
|
|
3605
|
+
protocolV4Present = derived.protocolV4Present;
|
|
3606
|
+
realRootInputSeen = derived.realRootInputSeen;
|
|
3607
|
+
lifecycle = lifecyclePhase({
|
|
3608
|
+
enabled: projection.enabled,
|
|
3609
|
+
realInputSeen: realRootInputSeen
|
|
3610
|
+
});
|
|
3453
3611
|
if (observedEpoch >= 0 && derived.projection.epoch > observedEpoch) {
|
|
3454
3612
|
pendingRecovery = true;
|
|
3455
3613
|
projection.lastRecoveryDigest = void 0;
|
|
@@ -3484,6 +3642,12 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
|
|
|
3484
3642
|
return {
|
|
3485
3643
|
projection,
|
|
3486
3644
|
session,
|
|
3645
|
+
get lifecycle() {
|
|
3646
|
+
return lifecycle;
|
|
3647
|
+
},
|
|
3648
|
+
get protocolV4Present() {
|
|
3649
|
+
return protocolV4Present;
|
|
3650
|
+
},
|
|
3487
3651
|
sync,
|
|
3488
3652
|
setEnabled,
|
|
3489
3653
|
setDurability,
|
|
@@ -3491,6 +3655,41 @@ function createRuntime(agent, config, hostLock = DEFAULT_HOST_LOCK, readGoalStat
|
|
|
3491
3655
|
consumeRecovery
|
|
3492
3656
|
};
|
|
3493
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
|
+
}
|
|
3663
|
+
/** Never reinterpret a legacy injected snapshot as freshly accepted core/v1. */
|
|
3664
|
+
function revalidateCoreLock(config, expected) {
|
|
3665
|
+
if (config.hostLockPolicy !== "dsh-core/v1" || !config.hostLockRuntimeRoot || !config.hostLockProfileRoot) return {
|
|
3666
|
+
...expected,
|
|
3667
|
+
status: "unavailable",
|
|
3668
|
+
goalAvailable: false,
|
|
3669
|
+
reasonCode: "host_lock_migration_required"
|
|
3670
|
+
};
|
|
3671
|
+
try {
|
|
3672
|
+
const actual = evaluateHostLock(readActiveHostGraph(config.hostLockRuntimeRoot, config.hostLockProfileRoot), {
|
|
3673
|
+
platform: config.hostLockPlatform,
|
|
3674
|
+
profileKind: config.hostLockProfile
|
|
3675
|
+
});
|
|
3676
|
+
if (actual.status !== "supported") return actual;
|
|
3677
|
+
if (actual.digest !== expected.digest) return {
|
|
3678
|
+
...actual,
|
|
3679
|
+
status: "unsupported",
|
|
3680
|
+
goalAvailable: false,
|
|
3681
|
+
reasonCode: "host_lock_installed_graph_drift"
|
|
3682
|
+
};
|
|
3683
|
+
return actual;
|
|
3684
|
+
} catch {
|
|
3685
|
+
return {
|
|
3686
|
+
...expected,
|
|
3687
|
+
status: "unavailable",
|
|
3688
|
+
goalAvailable: false,
|
|
3689
|
+
reasonCode: "host_lock_missing"
|
|
3690
|
+
};
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3494
3693
|
function apply(ctx, rawConfig = {}) {
|
|
3495
3694
|
const config = resolveConfig(rawConfig);
|
|
3496
3695
|
const installedHostLock = evaluateHostLock(config.hostLockPackages ?? [], {
|
|
@@ -3504,17 +3703,20 @@ function apply(ctx, rawConfig = {}) {
|
|
|
3504
3703
|
let runtime = runtimes.get(agent);
|
|
3505
3704
|
if (!runtime) {
|
|
3506
3705
|
const goals = optionalGoalService(ctx, agent);
|
|
3507
|
-
const
|
|
3508
|
-
|
|
3706
|
+
const refreshHostLock = () => {
|
|
3707
|
+
const current = bindLiveGoalCapability(revalidateCoreLock(config, installedHostLock), Boolean(goals) && hasPinnedUpdateGoalTool(agent));
|
|
3708
|
+
hostLocks.set(agent, current);
|
|
3709
|
+
return current;
|
|
3710
|
+
};
|
|
3711
|
+
const agentHostLock = refreshHostLock();
|
|
3712
|
+
runtime = createRuntime(agent, config, agentHostLock, goals ? () => goals.get(agent) : void 0, refreshHostLock);
|
|
3509
3713
|
runtimes.set(agent, runtime);
|
|
3510
3714
|
hostLocks.set(agent, agentHostLock);
|
|
3511
3715
|
}
|
|
3512
3716
|
return runtime;
|
|
3513
3717
|
};
|
|
3514
|
-
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));
|
|
3515
3719
|
ctx.on("agent/session-start", ({ agent, source }) => {
|
|
3516
|
-
ensureProtocolBoundary(agent);
|
|
3517
|
-
ensureProtocolBoundary(agent, CAPTURE_V042_NOTICE);
|
|
3518
3720
|
const runtime = ensure(agent);
|
|
3519
3721
|
runtime.sync();
|
|
3520
3722
|
if (source === "resume" || source === "compact") {
|
|
@@ -3581,6 +3783,17 @@ function apply(ctx, rawConfig = {}) {
|
|
|
3581
3783
|
};
|
|
3582
3784
|
agent.ctx.tools.register(createEvidenceTool(evidenceOptions));
|
|
3583
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
|
+
}));
|
|
3584
3797
|
agent.ctx.tools.register(createExternalOperationTool((id, toolAgent) => readExternalOperation(ctx, toolAgent, id), () => evaluateExternalWaitCapability(hostLocks.get(agent) ?? installedHostLock)));
|
|
3585
3798
|
agent.ctx.tools.guard((exec) => goalCompletionDenial(runtime.projection, exec.name, exec.arguments));
|
|
3586
3799
|
});
|
|
@@ -3590,12 +3803,33 @@ function apply(ctx, rawConfig = {}) {
|
|
|
3590
3803
|
runtime.setDurability(durability);
|
|
3591
3804
|
runtime.sync();
|
|
3592
3805
|
const decision = await next();
|
|
3593
|
-
if (decision.kind
|
|
3594
|
-
|
|
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;
|
|
3595
3825
|
const digest$2 = recovery ? recoveryDigest(recovery, runtime.projection) : void 0;
|
|
3596
3826
|
if (recovery && digest$2 !== runtime.projection.lastRecoveryDigest) {
|
|
3597
3827
|
runtime.projection.lastRecoveryDigest = digest$2;
|
|
3598
|
-
|
|
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({
|
|
3599
3833
|
content: [{
|
|
3600
3834
|
type: "text",
|
|
3601
3835
|
text: `Open task requirements (recovered after compaction or resume):\n${recovery}`
|
|
@@ -3606,9 +3840,10 @@ function apply(ctx, rawConfig = {}) {
|
|
|
3606
3840
|
form: "notice",
|
|
3607
3841
|
summary: boundContextSummary("recovering open task requirements")
|
|
3608
3842
|
}
|
|
3609
|
-
})
|
|
3843
|
+
}));
|
|
3610
3844
|
}
|
|
3611
3845
|
}
|
|
3846
|
+
if (injected.length > 0) decision.messages = [...injected, ...decision.messages];
|
|
3612
3847
|
return decision;
|
|
3613
3848
|
});
|
|
3614
3849
|
ctx.on("agent/turn-stopping", async ({ agent }) => {
|
|
@@ -3650,26 +3885,20 @@ function optionalMarketOrigin(ctx, agent) {
|
|
|
3650
3885
|
return `http://${service.host === "::1" ? "[::1]" : "127.0.0.1"}:${service.port}`;
|
|
3651
3886
|
} catch {}
|
|
3652
3887
|
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
const data = event.data;
|
|
3657
|
-
const source = data.source && typeof data.source === "object" ? data.source : void 0;
|
|
3658
|
-
const content = Array.isArray(data.content) ? data.content : [];
|
|
3659
|
-
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";
|
|
3660
|
-
})) return;
|
|
3661
|
-
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({
|
|
3662
3891
|
content: [{
|
|
3663
3892
|
type: "text",
|
|
3664
|
-
text
|
|
3893
|
+
text
|
|
3665
3894
|
}],
|
|
3666
3895
|
source: {
|
|
3667
3896
|
kind: "plugin",
|
|
3668
3897
|
plugin: "context-guard",
|
|
3669
3898
|
form: "notice",
|
|
3670
|
-
summary: boundContextSummary(
|
|
3899
|
+
summary: boundContextSummary(summaryLabel)
|
|
3671
3900
|
}
|
|
3672
|
-
})
|
|
3901
|
+
});
|
|
3673
3902
|
}
|
|
3674
3903
|
function hasPinnedUpdateGoalTool(agent) {
|
|
3675
3904
|
try {
|
|
@@ -3741,4 +3970,4 @@ function normalizeGoalState(value) {
|
|
|
3741
3970
|
}
|
|
3742
3971
|
|
|
3743
3972
|
//#endregion
|
|
3744
|
-
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, 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, isCurrentAcceptedBoundary, isDeterministicCheck, isInformationalMessage, isRunExecutable, isStatefulAction, isVerifyingCapability, isWholeTaskCompletionClaim, latestAssistantText, name, normalizeClause, observeAssistantOutcome, openItems, packageRowsFromActiveGraph, packageRowsFromPnpmLock, parseGitCommandManifest, parsePwshCommand, parseShellCommand, proofDigest, proofEvidenceConstraints, qualifyBoundary, 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,11 +24,31 @@ 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.
|
|
30
48
|
|
|
31
|
-
The
|
|
49
|
+
The host lock uses `dsh-core/v1`: one exact 33-package DSH core, independent of optional market versions. The resolver walks all reachable runtime/profile dependencies, including those introduced by plugins, and checks installed manifests against lock identities. Missing, duplicated, mixed or changed core rows fail closed. Every replay rechecks the configured graph sources against the injected core identity before deriving certificate authority.
|
|
50
|
+
|
|
51
|
+
Core manifest version 2 and its policy/cohort values change the digest without changing the shared digest-v3 encoder. Legacy injected configuration requires explicit inspection and migration; historical records are never relabelled. Action capabilities are evaluated only after this core contract succeeds. Market restart additionally requires a trusted loaded-instance verifier. Current DSH supplies none, so restart is unavailable while unrelated guarded work remains supported. A requested restart stays pending. The version-2 service adapter distinguishes ordinary provider/instance drift from a persisted, target-bound restart handoff.
|
|
32
52
|
|
|
33
53
|
Evidence is produced only from persisted `tool/call` + `tool/result` pairs. Guard never inserts context between a Code Mode sub-call and its durable result.
|
|
34
54
|
|
|
@@ -51,7 +71,11 @@ A legal plugin `user/message` notice marks the 0.4.2 capture boundary. Earlier e
|
|
|
51
71
|
|
|
52
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.
|
|
53
73
|
|
|
54
|
-
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.
|
|
55
79
|
|
|
56
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.
|
|
57
81
|
|