@dsh-electron/dsh-plugin-multi-root-workspace 0.1.14 → 0.1.15

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.
Files changed (61) hide show
  1. package/CHANGELOG.en.md +20 -1
  2. package/CHANGELOG.md +20 -1
  3. package/README.en.md +24 -7
  4. package/README.md +24 -7
  5. package/cordis.patch.yml +5 -1
  6. package/lib/{llm-message-fYORacvh.js → agent-instructions-BXuKhOzr.js} +1 -84
  7. package/lib/auto-review.js +2 -2
  8. package/lib/client.js +208 -26
  9. package/lib/client.js.map +1 -1
  10. package/lib/{command-Dcz6nULo.js → command-y5tfcAeC.js} +21 -10
  11. package/lib/command.js +1 -1
  12. package/lib/compat-CTJbpCxu.js +267 -0
  13. package/lib/compat.js +1 -1
  14. package/lib/{dialects-Dr4rDR-H.js → dialects-CdlG3RF9.js} +1 -1
  15. package/lib/{compat-veDqCOoa.js → dsh-version-DCwF7REk.js} +2 -264
  16. package/lib/index.js +10 -8
  17. package/lib/{instructions-KsUwUFE0.js → instructions-Ck9wp4J4.js} +695 -26
  18. package/lib/instructions.js +1 -1
  19. package/lib/llm-message-CepyzEBQ.js +84 -0
  20. package/lib/lsp.js +3 -3
  21. package/lib/{metadata-DgqSdVX_.js → metadata-99jgViHQ.js} +1 -1
  22. package/lib/{method-wrapper-CFY3p3j2.js → method-wrapper-D4oUCsEF.js} +22 -2
  23. package/lib/{native-win32-CRNXr11U.js → native-win32-C5oMtt7f.js} +1 -1
  24. package/lib/ops-compatibility.js +58 -0
  25. package/lib/ops-settings-UN2oOKEp.js +40 -0
  26. package/lib/ops-tools-k4JYaOrM.js +152 -0
  27. package/lib/process-fence.js +100 -24
  28. package/lib/{protocol-CnR1KTXy.js → protocol-CwDnFEyK.js} +8 -4
  29. package/lib/{registry-DR_sMuvS.js → registry-DyDNDSJZ.js} +235 -79
  30. package/lib/{registry-lease-win32-DDO-c7ig.js → registry-lease-win32-r4puac9m.js} +1 -1
  31. package/lib/registry.js +2 -2
  32. package/lib/{review-context-NDpKeMCc.js → review-context-B-oHkU6a.js} +24 -7
  33. package/lib/review-context.js +1 -1
  34. package/lib/{roots-Ca_W1S9n.js → roots-CmaWxRVy.js} +112 -22
  35. package/lib/{sandbox-C-vCJCp3.js → sandbox-CZElTNPz.js} +1 -1
  36. package/lib/sandbox.js +1 -1
  37. package/lib/scope.js +72 -9
  38. package/lib/skills-utyuvmwr.js +104 -0
  39. package/lib/skills.js +4 -105
  40. package/lib/{tool-context-CpgIFcCB.js → tool-context-CCeY3mrK.js} +1 -1
  41. package/lib/types/authority/protocol.d.ts +5 -1
  42. package/lib/types/client/locales.d.ts +28 -0
  43. package/lib/types/client/ops-settings.d.ts +6 -0
  44. package/lib/types/client/skills-settings.d.ts +3 -2
  45. package/lib/types/command.d.ts +3 -3
  46. package/lib/types/compat/instruction-activity.d.ts +32 -1
  47. package/lib/types/compat/ops-confirmation.d.ts +17 -0
  48. package/lib/types/compat/ops-settings.d.ts +11 -0
  49. package/lib/types/compat/ops-tools.d.ts +25 -0
  50. package/lib/types/compat/skills-client.d.ts +1 -0
  51. package/lib/types/compat/tool-context.d.ts +4 -0
  52. package/lib/types/compat.d.ts +2 -2
  53. package/lib/types/instructions.d.ts +6 -1
  54. package/lib/types/ops-compatibility.d.ts +34 -0
  55. package/lib/types/registry.d.ts +67 -8
  56. package/lib/types/review-context.d.ts +2 -0
  57. package/lib/types/roots.d.ts +114 -20
  58. package/lib/types/scope.d.ts +28 -2
  59. package/lib/workspace-files.js +3 -3
  60. package/lib/{workspace-routing-YPxaQ4V3.js → workspace-routing-D_qwMUh-.js} +4 -4
  61. package/package.json +5 -1
@@ -1,2 +1,2 @@
1
- import { a as composeInstructionMessage, i as apply, n as DEFAULT_MAX_SOURCE_BYTES, o as inject, r as PLUGIN_SOURCE, s as touchedPathOfToolCall, t as DEFAULT_MAX_BYTES } from "./instructions-KsUwUFE0.js";
1
+ import { a as composeInstructionMessage, i as apply, l as touchedPathOfToolCall, n as DEFAULT_MAX_SOURCE_BYTES, o as inject, r as PLUGIN_SOURCE, t as DEFAULT_MAX_BYTES } from "./instructions-Ck9wp4J4.js";
2
2
  export { DEFAULT_MAX_BYTES, DEFAULT_MAX_SOURCE_BYTES, PLUGIN_SOURCE, apply, composeInstructionMessage, inject, touchedPathOfToolCall };
@@ -0,0 +1,84 @@
1
+ //#region src/compat/llm-message.ts
2
+ /**
3
+ * This plugin's own producer kind.
4
+ *
5
+ * Session format 4 rejects the shared `kind: 'plugin'` (`format v4 message
6
+ * requires a producer-owned source kind`). The kind is ours, not
7
+ * `agent-instructions`: upstream treats that kind's `changes` array as its
8
+ * own reconciliation authority.
9
+ */
10
+ const PRODUCER_SOURCE_KIND = "multi-root-workspace";
11
+ /**
12
+ * The first session format whose encoder refuses `kind: 'plugin'`.
13
+ * Earlier formats declare that kind on `MessageSourceMap` and require it.
14
+ */
15
+ const PRODUCER_OWNED_FORMAT = 4;
16
+ /**
17
+ * Which source kind the installed session format will persist.
18
+ * @param formatVersion - `SESSION_FORMAT_VERSION`, when the session package is installed.
19
+ * @returns `plugin` through format 3, and this plugin's own kind from format 4 on.
20
+ */
21
+ function instructionSourceKind(formatVersion) {
22
+ return formatVersion !== void 0 && formatVersion >= PRODUCER_OWNED_FORMAT ? PRODUCER_SOURCE_KIND : "plugin";
23
+ }
24
+ /**
25
+ * The installed session format, when that package is present.
26
+ *
27
+ * A composition that never persists a session can omit the package; format 3
28
+ * is then the spelling that still declares `kind: 'plugin'`. Any other failure
29
+ * is a broken install and must surface.
30
+ * @returns the format version, or `undefined` when the package is not installed.
31
+ */
32
+ async function installedFormatVersion() {
33
+ try {
34
+ const session = await import("@deepseek-ai/dsh-session");
35
+ return typeof session.SESSION_FORMAT_VERSION === "number" ? session.SESSION_FORMAT_VERSION : void 0;
36
+ } catch (error) {
37
+ const code = error.code;
38
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return void 0;
39
+ throw error;
40
+ }
41
+ }
42
+ /**
43
+ * Build one user-role instruction message.
44
+ *
45
+ * Format 3 and earlier: `{ kind: 'plugin', plugin, form: 'instructions' }`.
46
+ * Format 4 and later: `{ kind: 'multi-root-workspace', plugin, form: 'instructions' }`.
47
+ * @param input - the body and the plugin attribution.
48
+ * @returns the frozen message upstream's helper produced.
49
+ * @throws {Error} when the optional peer is absent: a step that has instruction
50
+ * text to deliver cannot be served without it, and staying silent would drop
51
+ * user-visible context instead of reporting a misconfigured composition.
52
+ */
53
+ async function createInstructionMessage(input) {
54
+ let module;
55
+ try {
56
+ module = await import("@deepseek-ai/dsh-llm");
57
+ } catch (error) {
58
+ throw new Error(`multi-root workspace: @deepseek-ai/dsh-llm is not installed, so additional roots' instruction files cannot be delivered as messages (${error instanceof Error ? error.message : String(error)}).`);
59
+ }
60
+ const create = module.createUserMessage;
61
+ return create({
62
+ content: [{
63
+ type: "text",
64
+ text: input.text
65
+ }],
66
+ source: instructionSource(input.plugin, await installedFormatVersion())
67
+ });
68
+ }
69
+ /** The source object the installed format will persist. */
70
+ function instructionSource(plugin, formatVersion) {
71
+ const kind = instructionSourceKind(formatVersion);
72
+ if (kind === "plugin") return {
73
+ kind,
74
+ plugin,
75
+ form: "instructions"
76
+ };
77
+ return {
78
+ kind,
79
+ plugin,
80
+ form: "instructions"
81
+ };
82
+ }
83
+ //#endregion
84
+ export { createInstructionMessage as t };
package/lib/lsp.js CHANGED
@@ -1,7 +1,7 @@
1
- import { t as wrapMethod } from "./method-wrapper-CFY3p3j2.js";
1
+ import { t as wrapMethod } from "./method-wrapper-D4oUCsEF.js";
2
2
  import { c as withRegistryRead } from "./registry-operation-C2ZV3Jhw.js";
3
- import { t as aroundTool } from "./tool-context-CpgIFcCB.js";
4
- import { t as routeWorkspacePath } from "./workspace-routing-YPxaQ4V3.js";
3
+ import { t as aroundTool } from "./tool-context-CCeY3mrK.js";
4
+ import { t as routeWorkspacePath } from "./workspace-routing-D_qwMUh-.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  //#region src/lsp.ts
7
7
  const inject = [
@@ -1,4 +1,4 @@
1
- import { t as RootValidationError } from "./roots-Ca_W1S9n.js";
1
+ import { t as RootValidationError } from "./roots-CmaWxRVy.js";
2
2
  import { createHash } from "node:crypto";
3
3
  import { basename } from "node:path";
4
4
  //#region src/skills/metadata.ts
@@ -1,4 +1,14 @@
1
1
  //#region src/method-wrapper.ts
2
+ const layers = /* @__PURE__ */ new WeakMap();
3
+ function liveDescriptor(target, key, descriptor) {
4
+ let current = descriptor;
5
+ while (typeof current?.value === "function") {
6
+ const layer = layers.get(current.value);
7
+ if (layer === void 0 || layer.active || layer.target !== target || layer.key !== key) break;
8
+ current = layer.own;
9
+ }
10
+ return current;
11
+ }
2
12
  /** Wrap a public method on a Cordis service without capturing a caller-context proxy. */
3
13
  function wrapMethod(target, key, wrap) {
4
14
  const own = Object.getOwnPropertyDescriptor(target, key);
@@ -10,6 +20,14 @@ function wrapMethod(target, key, wrap) {
10
20
  }
11
21
  if (typeof descriptor?.value !== "function") throw new Error(`multi-root: ${String(key)} is not a public method`);
12
22
  const value = wrap(descriptor.value);
23
+ if (value === descriptor.value) return () => {};
24
+ const layer = {
25
+ target,
26
+ key,
27
+ own,
28
+ active: true
29
+ };
30
+ layers.set(value, layer);
13
31
  Object.defineProperty(target, key, {
14
32
  configurable: true,
15
33
  writable: true,
@@ -17,9 +35,11 @@ function wrapMethod(target, key, wrap) {
17
35
  value
18
36
  });
19
37
  return () => {
38
+ layer.active = false;
20
39
  if (Object.getOwnPropertyDescriptor(target, key)?.value !== value) return;
21
- if (own === void 0) Reflect.deleteProperty(target, key);
22
- else Object.defineProperty(target, key, own);
40
+ const previous = liveDescriptor(target, key, own);
41
+ if (previous === void 0) Reflect.deleteProperty(target, key);
42
+ else Object.defineProperty(target, key, previous);
23
43
  };
24
44
  }
25
45
  //#endregion
@@ -1,4 +1,4 @@
1
- import { c as loadHostKoffi } from "./protocol-CnR1KTXy.js";
1
+ import { c as loadHostKoffi } from "./protocol-CwDnFEyK.js";
2
2
  import { EventEmitter } from "node:events";
3
3
  //#region src/authority/native-win32.ts
4
4
  /** Windows local pipes: tokens, never a caller-supplied PID or a bearer file. */
@@ -0,0 +1,58 @@
1
+ import { n as opsCompatibilityField, t as installOpsCompatibilitySettings } from "./ops-settings-UN2oOKEp.js";
2
+ import { t as configValue } from "./skills-utyuvmwr.js";
3
+ import { randomUUID } from "node:crypto";
4
+ import { Service } from "@deepseek-ai/cordis";
5
+ import z from "@deepseek-ai/schemastery";
6
+ //#region src/ops-compatibility.ts
7
+ const enabledField = opsCompatibilityField(z.boolean().default(false));
8
+ const experimental = enabledField.experimental;
9
+ const described = typeof experimental === "function" ? experimental.call(enabledField) : enabledField;
10
+ const Config = z.object({ enabled: described.description("OPS 兼容模式(实验性,仅限 DSH 0.2.*):可能存在功能、版本和平台限制。启用 OPS 写保护、实时活动与 Review 增强;受限模式可能拒绝附加根操作(包括读取),后台缓存可能残留;关闭时显式只读附加根可能被 OPS 写入。") });
11
+ var MultiRootOpsCompatibility = class extends Service {
12
+ settingsConfig;
13
+ static Config = Config;
14
+ static provide = "multiRootOpsCompatibility";
15
+ static inject = [
16
+ "multiRootCompat",
17
+ "multiRootProviders",
18
+ "multiRootScope"
19
+ ];
20
+ constructor(ctx, settingsConfig = {}) {
21
+ super(ctx, "multiRootOpsCompatibility");
22
+ this.settingsConfig = settingsConfig;
23
+ const settings = installOpsCompatibilitySettings(ctx, () => this.settingsConfig.enabled, () => this.sync());
24
+ this.settingsRead = settings.read;
25
+ ctx.effect(() => () => {
26
+ this.closing = true;
27
+ this.enabledNow = false;
28
+ this.currentToken = void 0;
29
+ });
30
+ this.sync();
31
+ }
32
+ settingsRead;
33
+ currentToken;
34
+ enabledNow = false;
35
+ closing = false;
36
+ get token() {
37
+ this.sync();
38
+ return this.currentToken;
39
+ }
40
+ get enabled() {
41
+ this.sync();
42
+ return this.enabledNow;
43
+ }
44
+ sync() {
45
+ if (this.closing) {
46
+ this.enabledNow = false;
47
+ this.currentToken = void 0;
48
+ return;
49
+ }
50
+ const raw = this.settingsRead?.();
51
+ const enabled = configValue(raw) === true;
52
+ if (enabled === this.enabledNow) return;
53
+ this.enabledNow = enabled;
54
+ this.currentToken = enabled ? Object.freeze({ id: randomUUID() }) : void 0;
55
+ }
56
+ };
57
+ //#endregion
58
+ export { Config, MultiRootOpsCompatibility, MultiRootOpsCompatibility as default };
@@ -0,0 +1,40 @@
1
+ import { d as readInstalledVersion } from "./dsh-version-DCwF7REk.js";
2
+ /** Product availability policy, independent of structural Settings adaptation. */
3
+ function opsCompatibilityRelease(release) {
4
+ return release !== void 0 && /^0\.2\.\d+(?:[-+].*)?$/.test(release);
5
+ }
6
+ /** Unsupported hosts retain the config input but expose no editable live field. */
7
+ function opsCompatibilityField(schema) {
8
+ if (!opsCompatibilityRelease(readInstalledVersion("@deepseek-ai/dsh-sandbox"))) return schema;
9
+ const method = schema.volatile;
10
+ return typeof method === "function" ? method.call(schema) : schema;
11
+ }
12
+ function installOpsCompatibilitySettings(ctx, initial, changed) {
13
+ let active = false;
14
+ return {
15
+ fiber: ctx.inject(["settings"], (scope) => {
16
+ if (!opsCompatibilityRelease(scope.get("multiRootCompat")?.release)) return;
17
+ const settings = scope.get("settings");
18
+ if (typeof settings.configure !== "function") return;
19
+ const stop = settings.configure({ auto: false }, ctx.fiber);
20
+ active = true;
21
+ const stopEvent = scope.on("settings/document-updated", (id) => {
22
+ if (id === "multi-root-ops-compatibility") changed();
23
+ });
24
+ changed();
25
+ return () => {
26
+ active = false;
27
+ stopEvent();
28
+ stop();
29
+ changed();
30
+ };
31
+ }),
32
+ read: () => active && opsCompatibilityRelease(ctx.get("multiRootCompat")?.release) ? initial() : void 0
33
+ };
34
+ }
35
+ function opsCompatibilityToken(ctx) {
36
+ const service = ctx.get("multiRootOpsCompatibility");
37
+ return service === void 0 ? void 0 : service.token;
38
+ }
39
+ //#endregion
40
+ export { opsCompatibilityField as n, opsCompatibilityToken as r, installOpsCompatibilitySettings as t };
@@ -0,0 +1,152 @@
1
+ import { r as opsCompatibilityToken } from "./ops-settings-UN2oOKEp.js";
2
+ import { realpathSync } from "node:fs";
3
+ import { isAbsolute, relative } from "node:path";
4
+ import { AsyncLocalStorage } from "node:async_hooks";
5
+ import { tmpdir } from "node:os";
6
+ //#region src/compat/ops-tools.ts
7
+ const OPS_TOOL_PREFIX = "ops_";
8
+ const OPS_EXEC_RAW_NAMES = [
9
+ "inspect_local_file",
10
+ "grep",
11
+ "glob",
12
+ "replace",
13
+ "write_file",
14
+ "edit_file",
15
+ "diff",
16
+ "stat",
17
+ "copy",
18
+ "move",
19
+ "remove",
20
+ "read_files",
21
+ "read_table",
22
+ "read_docx",
23
+ "read_pdf",
24
+ "batch",
25
+ "result",
26
+ "bash"
27
+ ];
28
+ const OPS_PUBLISH_RAW_NAMES = ["discover_tools", "load_sdk"];
29
+ OPS_EXEC_RAW_NAMES.map((raw) => OPS_TOOL_PREFIX + raw);
30
+ function classifyOpsTool(name) {
31
+ if (typeof name !== "string" || !name.startsWith("ops_")) return "other";
32
+ const raw = name.slice(4);
33
+ if (OPS_EXEC_RAW_NAMES.includes(raw)) return "exec";
34
+ if (OPS_PUBLISH_RAW_NAMES.includes(raw)) return "publish";
35
+ return "unknown";
36
+ }
37
+ const spawnPermit = new AsyncLocalStorage();
38
+ function withOpsSpawnPermit(state, body) {
39
+ const permit = {
40
+ state,
41
+ active: true
42
+ };
43
+ try {
44
+ return spawnPermit.run(permit, body);
45
+ } finally {
46
+ permit.active = false;
47
+ }
48
+ }
49
+ function opsSpawnPermitted(state) {
50
+ const permit = spawnPermit.getStore();
51
+ return permit?.state === state && permit.active;
52
+ }
53
+ function createOpsProtectionState() {
54
+ return {
55
+ owner: void 0,
56
+ entered: 0,
57
+ drainWaiters: /* @__PURE__ */ new Set()
58
+ };
59
+ }
60
+ function claimOpsProtection(state) {
61
+ const owner = {};
62
+ state.owner = owner;
63
+ return owner;
64
+ }
65
+ function revokeOpsProtection(state, token) {
66
+ if (state.owner === token) state.owner = void 0;
67
+ }
68
+ function opsProtectionReady(state) {
69
+ return state.owner !== void 0;
70
+ }
71
+ function assertOpsProtectionOwner(state, token) {
72
+ if (state.owner !== token) throw new Error("multi-root workspace: the OPS execution protection generation was replaced during dispatch");
73
+ }
74
+ function enterOpsProtection(state) {
75
+ if (state.owner === void 0) throw new Error("multi-root workspace: the OPS execution protection is not installed on this scope");
76
+ state.entered += 1;
77
+ return state.owner;
78
+ }
79
+ function leaveOpsProtection(state) {
80
+ state.entered -= 1;
81
+ if (state.entered === 0) {
82
+ for (const resolve of state.drainWaiters) resolve();
83
+ state.drainWaiters.clear();
84
+ }
85
+ }
86
+ async function drainOpsProtection(state) {
87
+ if (state.entered === 0) return;
88
+ await new Promise((resolve) => state.drainWaiters.add(resolve));
89
+ }
90
+ const OPS_RESULT_CACHE_LEAF = /^dsh-ops-results-[0-9a-f]{64}$/u;
91
+ function cacheRefusal(detail) {
92
+ return /* @__PURE__ */ new Error(`multi-root workspace: ${detail}; refusing to prepare the OPS execution`);
93
+ }
94
+ function containsPath(root, path) {
95
+ if (root === path) return true;
96
+ const rel = relative(root, path);
97
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
98
+ }
99
+ function assertOpsPosixCacheAdmission(deniedRoots) {
100
+ let canonicalTmp;
101
+ try {
102
+ canonicalTmp = realpathSync.native(tmpdir());
103
+ } catch (error) {
104
+ throw cacheRefusal(`the canonical temporary directory cannot be resolved (${error.message})`);
105
+ }
106
+ if (!isAbsolute(canonicalTmp)) throw cacheRefusal("the canonical temporary directory is not absolute");
107
+ const rawTmp = tmpdir();
108
+ const spellings = /* @__PURE__ */ new Set();
109
+ for (const root of deniedRoots) {
110
+ if (root === "") continue;
111
+ spellings.add(root);
112
+ try {
113
+ spellings.add(realpathSync.native(root));
114
+ } catch {}
115
+ if (containsPath(rawTmp, root) || root === rawTmp) {
116
+ const rel = relative(rawTmp, root);
117
+ spellings.add(rel === "" ? canonicalTmp : canonicalTmp + "/" + rel);
118
+ }
119
+ }
120
+ for (const deny of spellings) {
121
+ if (containsPath(deny, canonicalTmp)) throw cacheRefusal(`a deny boundary ${JSON.stringify(deny)} covers the temporary directory`);
122
+ const rel = relative(canonicalTmp, deny);
123
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) continue;
124
+ const first = rel.split("/")[0];
125
+ if (first !== void 0 && OPS_RESULT_CACHE_LEAF.test(first)) throw cacheRefusal(`a deny boundary ${JSON.stringify(deny)} overlaps the OPS result cache`);
126
+ }
127
+ }
128
+ const object = (value) => typeof value === "object" && value !== null;
129
+ function opsDispatchBarrier(ctx, scope, exec) {
130
+ if (opsCompatibilityToken(ctx) === void 0) return void 0;
131
+ const kind = classifyOpsTool(exec.name);
132
+ if (kind === "other" || kind === "publish") return void 0;
133
+ if (ctx.get("multiRootCompat") === void 0 || ctx.get("multiRootProviders") === void 0) return void 0;
134
+ const session = exec.agent?.session;
135
+ const policyService = ctx.get("sandboxPolicy");
136
+ let policy;
137
+ if (session !== void 0 && policyService !== void 0) try {
138
+ policy = policyService.resolve({ session });
139
+ } catch {
140
+ policy = void 0;
141
+ }
142
+ if (policy === void 0) return `the OPS tool ${JSON.stringify(exec.name)} cannot be confirmed against a trusted session and policy`;
143
+ const preview = scope.previewForTool(policy, exec.name);
144
+ if (preview.authority === "unavailable") return `the multi-root registry authority is unavailable; the OPS call ${JSON.stringify(exec.name)} is refused before dispatch`;
145
+ if (preview.deniedRoots.length === 0) return void 0;
146
+ if (policy.mode !== "danger-full-access") return `the OPS call ${JSON.stringify(exec.name)} is refused in ${policy.mode}: private preparation and worker write boundaries cannot be proven without OPS integration`;
147
+ if (kind === "unknown") return `the unrecognized OPS capability ${JSON.stringify(exec.name)} is refused while write boundaries apply`;
148
+ if (!opsProtectionReady(scope.opsProtection)) return `the OPS execution protection is not installed; the call ${JSON.stringify(exec.name)} is refused`;
149
+ if (exec.name === `ops_bash` && object(exec.arguments) && exec.arguments.run_in_background === true) return "run_in_background is refused while danger-full-access write boundaries apply";
150
+ }
151
+ //#endregion
152
+ export { createOpsProtectionState as a, leaveOpsProtection as c, revokeOpsProtection as d, withOpsSpawnPermit as f, classifyOpsTool as i, opsDispatchBarrier as l, assertOpsProtectionOwner as n, drainOpsProtection as o, claimOpsProtection as r, enterOpsProtection as s, assertOpsPosixCacheAdmission as t, opsSpawnPermitted as u };
@@ -1,7 +1,9 @@
1
- import { t as wrapMethod } from "./method-wrapper-CFY3p3j2.js";
1
+ import { t as wrapMethod } from "./method-wrapper-D4oUCsEF.js";
2
2
  import { s as withRegistryAdmission } from "./registry-operation-C2ZV3Jhw.js";
3
- import { t as aroundTool } from "./tool-context-CpgIFcCB.js";
4
- import { n as detectDialect, s as confineInstalled } from "./dialects-Dr4rDR-H.js";
3
+ import { t as aroundTool } from "./tool-context-CCeY3mrK.js";
4
+ import { r as opsCompatibilityToken } from "./ops-settings-UN2oOKEp.js";
5
+ import { n as detectDialect, s as confineInstalled } from "./dialects-CdlG3RF9.js";
6
+ import { c as leaveOpsProtection, d as revokeOpsProtection, f as withOpsSpawnPermit, i as classifyOpsTool, n as assertOpsProtectionOwner, o as drainOpsProtection, r as claimOpsProtection, s as enterOpsProtection, t as assertOpsPosixCacheAdmission } from "./ops-tools-k4JYaOrM.js";
5
7
  import { AsyncLocalStorage } from "node:async_hooks";
6
8
  //#region src/compat/process-profile.ts
7
9
  /** Structural runner recognition shared by the synchronous process seams. */
@@ -206,9 +208,59 @@ function isThenable(value) {
206
208
  }
207
209
  const object = (value) => typeof value === "object" && value !== null;
208
210
  function apply(ctx) {
211
+ const opsScope = ctx.get("multiRootScope");
212
+ const opsSandbox = ctx.sandbox;
213
+ const runOpsDispatch = async (exec, policy, next) => {
214
+ const modeToken = opsCompatibilityToken(ctx);
215
+ const scope = opsScope;
216
+ const opsState = scope.opsProtection;
217
+ const token = enterOpsProtection(opsState);
218
+ try {
219
+ const dispatch = async () => {
220
+ const resolved = scope.resolve(policy);
221
+ if (resolved.authority === "unavailable") throw new Error("multi-root workspace: the registry authority is unavailable; refusing the OPS execution");
222
+ if (resolved.deniedRoots.length > 0) {
223
+ if (policy.mode !== "danger-full-access") throw new Error(`multi-root workspace: the OPS execution is refused in ${policy.mode}: private preparation and worker write boundaries cannot be proven without OPS integration`);
224
+ if (classifyOpsTool(exec.name) === "unknown") throw new Error(`multi-root workspace: the unrecognized OPS capability ${JSON.stringify(exec.name)} is refused while write boundaries apply`);
225
+ if (exec.name === "ops_bash" && typeof exec.arguments === "object" && exec.arguments !== null && exec.arguments.run_in_background === true) throw new Error("multi-root workspace: run_in_background is refused while danger-full-access write boundaries apply");
226
+ }
227
+ let facts;
228
+ if (resolved.deniedRoots.length > 0) {
229
+ if (process.platform === "win32") throw new Error("multi-root workspace: OPS worker preparation is not verified on this platform; refusing the call while write boundaries apply");
230
+ assertOpsPosixCacheAdmission(resolved.deniedRoots);
231
+ facts = await opsSandbox.restrictedExecution(policy, resolved.deniedRoots, exec.signal, resolved.protectedHome);
232
+ if (facts === void 0 || facts.argv.length === 0) throw new Error("multi-root workspace: the restricted runner could not be prepared; refusing the OPS execution");
233
+ }
234
+ assertOpsProtectionOwner(opsState, token);
235
+ if (opsCompatibilityToken(ctx) !== modeToken) throw new Error("multi-root workspace: the OPS compatibility mode changed during dispatch; refusing the execution");
236
+ const store = {
237
+ argvPrefix: facts?.argv ?? [],
238
+ ops: true,
239
+ opsState,
240
+ active: true
241
+ };
242
+ try {
243
+ return await processDenyStore.run(store, () => facts === void 0 ? next() : decorateProcessOutcome(next(), facts, policy, { stderr: () => store.stderr?.() }));
244
+ } finally {
245
+ store.active = false;
246
+ }
247
+ };
248
+ if (ctx.get("multiRootRegistry")?.shared) return await withRegistryAdmission(ctx, policy, "process", dispatch);
249
+ return await dispatch();
250
+ } finally {
251
+ leaveOpsProtection(opsState);
252
+ }
253
+ };
209
254
  aroundTool(ctx, async (exec, next) => {
210
- if (!ctx.get("multiRootRegistry")?.shared || exec.agent === void 0) return next();
211
- return withRegistryAdmission(ctx, ctx.sandboxPolicy.resolve({ session: exec.agent.session }), "process", next);
255
+ const kind = classifyOpsTool(exec.name);
256
+ if (kind === "other" || kind === "publish" || opsCompatibilityToken(ctx) === void 0) {
257
+ if (!ctx.get("multiRootRegistry")?.shared || exec.agent === void 0) return next();
258
+ const policy = ctx.sandboxPolicy.resolve({ session: exec.agent.session });
259
+ return opsScope.withToolCap(exec.name, () => withRegistryAdmission(ctx, policy, "process", next));
260
+ }
261
+ if (exec.agent === void 0) throw new Error(`multi-root workspace: the OPS call ${JSON.stringify(exec.name)} cannot be confirmed against a trusted session`);
262
+ const policy = ctx.sandboxPolicy.resolve({ session: exec.agent.session });
263
+ return opsScope.withToolCap(exec.name, () => runOpsDispatch(exec, policy, next));
212
264
  });
213
265
  const policyOf = (spec) => spec?.sandboxPolicy ?? ctx.sandboxPolicy.resolve();
214
266
  /**
@@ -240,27 +292,46 @@ function apply(ctx) {
240
292
  return isThenable(prepared) ? prepared.then(run) : run(prepared);
241
293
  };
242
294
  ctx.inject(["subprocess"], (scope) => {
243
- scope.effect(() => wrapMethod(scope.subprocess, "spawn", (original) => function(spec) {
244
- const store = processDenyStore.getStore();
245
- if (scope.multiRootScope.admissionRequired && scope.multiRootScope.hasAdmission() && !scope.multiRootScope.admissionPathsValid()) throw new Error("multi-root workspace: an admitted root was redirected before process launch");
246
- const argv = store === void 0 ? spec.argv : [...store.argvPrefix, ...spec.argv];
247
- if (scope.multiRootScope.admissionRequired && (store !== void 0 || hasConfinedProfile(argv)) && !scope.multiRootScope.permitsProfile(argv)) throw new Error("multi-root workspace: a confined spawn requires a profile from this live admission");
248
- const handle = original.call(this, store === void 0 ? spec : {
249
- ...spec,
250
- argv
251
- });
252
- if (store === void 0) return handle;
253
- const stderr = object(handle) && object(handle.collected) && object(handle.collected.stderr) && typeof handle.collected.stderr.readFrom === "function" ? handle.collected.stderr : void 0;
254
- if (stderr !== void 0) store.stderr = () => {
255
- try {
256
- return stderr.readFrom(0).text;
257
- } catch {
258
- return;
295
+ const opsState = opsScope.opsProtection;
296
+ scope.effect(() => {
297
+ const restore = wrapMethod(scope.get("subprocess"), "spawn", (original) => function(spec) {
298
+ const store = processDenyStore.getStore();
299
+ if (store?.ops === true && (store.active === false || store.opsState !== opsState)) throw new Error("multi-root workspace: the OPS execution domain has expired");
300
+ const admissionScope = store?.ops === true ? opsScope : scope.multiRootScope;
301
+ if (admissionScope.admissionRequired && admissionScope.hasAdmission() && !admissionScope.admissionPathsValid()) throw new Error("multi-root workspace: an admitted root was redirected before process launch");
302
+ let argv = spec.argv;
303
+ if (store?.ops === true) {
304
+ const prefix = store.argvPrefix;
305
+ if (hasConfinedProfile(spec.argv)) {
306
+ if (!(spec.argv.length > prefix.length && prefix.every((segment, index) => spec.argv[index] === segment))) throw new Error("multi-root workspace: a confined spawn inside an OPS execution domain must carry this domain's own profile");
307
+ } else argv = [...prefix, ...spec.argv];
308
+ if (opsScope.admissionRequired && (hasConfinedProfile(spec.argv) || prefix.length > 0) && !opsScope.permitsProfile(argv)) throw new Error("multi-root workspace: a confined spawn requires a profile from this live admission");
309
+ } else {
310
+ argv = store === void 0 ? spec.argv : [...store.argvPrefix, ...spec.argv];
311
+ if (scope.multiRootScope.admissionRequired && (store !== void 0 || hasConfinedProfile(argv)) && !scope.multiRootScope.permitsProfile(argv)) throw new Error("multi-root workspace: a confined spawn requires a profile from this live admission");
259
312
  }
313
+ const launch = () => original.call(this, argv === spec.argv ? spec : {
314
+ ...spec,
315
+ argv
316
+ });
317
+ const handle = store !== void 0 || admissionScope.admissionRequired && admissionScope.permitsProfile(argv) ? withOpsSpawnPermit(opsState, launch) : launch();
318
+ if (store === void 0) return handle;
319
+ const stderr = object(handle) && object(handle.collected) && object(handle.collected.stderr) && typeof handle.collected.stderr.readFrom === "function" ? handle.collected.stderr : void 0;
320
+ if (stderr !== void 0) store.stderr = () => {
321
+ try {
322
+ return stderr.readFrom(0).text;
323
+ } catch {
324
+ return;
325
+ }
326
+ };
327
+ return handle;
328
+ });
329
+ return async () => {
330
+ await drainOpsProtection(opsState);
331
+ restore();
260
332
  };
261
- return handle;
262
- }));
263
- scope.effect(() => wrapMethod(scope.subprocess, "spawnTerminal", (original) => async function(spec) {
333
+ });
334
+ scope.effect(() => wrapMethod(scope.get("subprocess"), "spawnTerminal", (original) => async function(spec) {
264
335
  const ptySessionId = spec.env?.DSH_PTY_SESSION_ID;
265
336
  if (ptySessionId === void 0 || ptySessionId === "") return await original.call(this, spec);
266
337
  const sessionId = spec.env?.DSH_SESSION_ID;
@@ -293,6 +364,11 @@ function apply(ctx) {
293
364
  });
294
365
  });
295
366
  }));
367
+ const token = claimOpsProtection(opsState);
368
+ scope.effect(() => async () => {
369
+ revokeOpsProtection(opsState, token);
370
+ await drainOpsProtection(opsState);
371
+ });
296
372
  });
297
373
  /**
298
374
  * The wrapper every execute-style consumer entry shares: resolve the policy
@@ -212,7 +212,7 @@ function posix$1() {
212
212
  return posixPromise ??= loadPosix();
213
213
  }
214
214
  async function connectNative(endpoint) {
215
- if (process.platform === "win32") return (await import("./native-win32-CRNXr11U.js")).connectWindows(endpoint);
215
+ if (process.platform === "win32") return (await import("./native-win32-C5oMtt7f.js")).connectWindows(endpoint);
216
216
  const api = await posix$1(), address = api.address(endpoint), fd = api.open();
217
217
  if (api.connect(fd, address, address.length) !== 0) {
218
218
  closeSync(fd);
@@ -222,7 +222,7 @@ async function connectNative(endpoint) {
222
222
  }
223
223
  /** Called only by the kernel lease holder. Never removes another epoch's socket. */
224
224
  async function listenNative(endpoint, accepted) {
225
- if (process.platform === "win32") return (await import("./native-win32-CRNXr11U.js")).listenWindows(endpoint, accepted);
225
+ if (process.platform === "win32") return (await import("./native-win32-C5oMtt7f.js")).listenWindows(endpoint, accepted);
226
226
  const api = await posix$1(), address = api.address(endpoint), fd = api.open();
227
227
  try {
228
228
  unlinkSync(endpoint);
@@ -264,7 +264,7 @@ async function listenNative(endpoint, accepted) {
264
264
  }
265
265
  /** Capability probe for native tests and diagnostics; not an authorization bypass. */
266
266
  async function nativeHostAvailability() {
267
- if (process.platform === "win32") return (await import("./native-win32-CRNXr11U.js")).windowsHostAvailability();
267
+ if (process.platform === "win32") return (await import("./native-win32-C5oMtt7f.js")).windowsHostAvailability();
268
268
  if (process.platform === "linux") {
269
269
  if (!/^NoNewPrivs:\s+0$/m.test(readFileSync("/proc/self/status", "utf8"))) return {
270
270
  available: false,
@@ -345,7 +345,11 @@ const rootSchema = z.object({
345
345
  "invalid"
346
346
  ]),
347
347
  detail: z.string().optional(),
348
- invalidReason: z.literal("membership-conflict").optional()
348
+ invalidReason: z.enum([
349
+ "membership-conflict",
350
+ "protected-path",
351
+ "primary-overlap"
352
+ ]).optional()
349
353
  }).strict();
350
354
  const snapshotSchema = z.object({
351
355
  protocol: z.literal(1),