@parall/daemon 1.42.1 → 1.44.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.
@@ -122,63 +122,6 @@ var init_lane_key = __esm({
122
122
  }
123
123
  });
124
124
 
125
- // ts/agent-core/dist/session-state.js
126
- var init_session_state = __esm({
127
- "ts/agent-core/dist/session-state.js"() {
128
- "use strict";
129
- }
130
- });
131
-
132
- // ts/agent-core/dist/routing.js
133
- var init_routing = __esm({
134
- "ts/agent-core/dist/routing.js"() {
135
- "use strict";
136
- }
137
- });
138
-
139
- // ts/agent-core/dist/event-format.js
140
- var init_event_format = __esm({
141
- "ts/agent-core/dist/event-format.js"() {
142
- "use strict";
143
- }
144
- });
145
-
146
- // ts/agent-core/dist/prompt-fragments.js
147
- var init_prompt_fragments = __esm({
148
- "ts/agent-core/dist/prompt-fragments.js"() {
149
- "use strict";
150
- }
151
- });
152
-
153
- // ts/agent-core/dist/bridge-workspace.js
154
- var init_bridge_workspace = __esm({
155
- "ts/agent-core/dist/bridge-workspace.js"() {
156
- "use strict";
157
- }
158
- });
159
-
160
- // ts/agent-core/dist/dispatch-adapter.js
161
- var init_dispatch_adapter = __esm({
162
- "ts/agent-core/dist/dispatch-adapter.js"() {
163
- "use strict";
164
- }
165
- });
166
-
167
- // ts/agent-core/dist/logger.js
168
- function createLogger(prefix) {
169
- return {
170
- info: (msg) => console.log(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
171
- warn: (msg) => console.warn(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
172
- error: (msg) => console.error(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
173
- child: (sub) => createLogger(`${prefix}:${sub}`)
174
- };
175
- }
176
- var init_logger = __esm({
177
- "ts/agent-core/dist/logger.js"() {
178
- "use strict";
179
- }
180
- });
181
-
182
125
  // ts/sdk/dist/types.js
183
126
  var init_types2 = __esm({
184
127
  "ts/sdk/dist/types.js"() {
@@ -672,6 +615,7 @@ var init_client = __esm({
672
615
  setTokens;
673
616
  refreshPromise = null;
674
617
  swimlaneName;
618
+ getFeatureFlagOverrides;
675
619
  /** Auth endpoints excluded from automatic 401 refresh to prevent recursion. */
676
620
  static AUTH_PATHS = /* @__PURE__ */ new Set([
677
621
  "/auth/login",
@@ -702,7 +646,7 @@ var init_client = __esm({
702
646
  return apiError;
703
647
  }
704
648
  /** Build headers common to all requests (auth, swimlane). */
705
- buildHeaders(extra) {
649
+ buildHeaders(path19, extra) {
706
650
  const headers = {
707
651
  "Content-Type": "application/json",
708
652
  ...extra
@@ -713,6 +657,11 @@ var init_client = __esm({
713
657
  if (this.swimlaneName) {
714
658
  headers["X-Prll-Swimlane"] = this.swimlaneName;
715
659
  }
660
+ if (path19.startsWith(API_BASE)) {
661
+ const overrides = this.getFeatureFlagOverrides?.();
662
+ if (overrides)
663
+ headers["X-Prll-FF-Override"] = overrides;
664
+ }
716
665
  return headers;
717
666
  }
718
667
  constructor(options = {}) {
@@ -723,6 +672,7 @@ var init_client = __esm({
723
672
  this.getRefreshToken = options.getRefreshToken;
724
673
  this.setTokens = options.setTokens;
725
674
  this.swimlaneName = options.swimlaneName;
675
+ this.getFeatureFlagOverrides = options.getFeatureFlagOverrides;
726
676
  }
727
677
  /**
728
678
  * Pick the origin for a request path: wiki-service base for `/wiki/v1`
@@ -730,8 +680,8 @@ var init_client = __esm({
730
680
  * is authoritative, so wiki vs api routing can't drift from how a caller
731
681
  * happens to invoke the client.
732
682
  */
733
- baseUrlFor(path18) {
734
- return path18.startsWith(WIKI_BASE) ? this.wikiBaseUrl : this.baseUrl;
683
+ baseUrlFor(path19) {
684
+ return path19.startsWith(WIKI_BASE) ? this.wikiBaseUrl : this.baseUrl;
735
685
  }
736
686
  setToken(token) {
737
687
  this.token = token;
@@ -758,10 +708,10 @@ var init_client = __esm({
758
708
  * REFRESH_THRESHOLD_S, refresh it **before** sending the request.
759
709
  * No-op when the token is still fresh, missing, or un-parseable.
760
710
  */
761
- async ensureFreshToken(path18) {
711
+ async ensureFreshToken(path19) {
762
712
  if (!this.token || !this.getRefreshToken)
763
713
  return;
764
- const pathSuffix = path18.replace(/^\/api\/v1/, "");
714
+ const pathSuffix = path19.replace(/^\/api\/v1/, "");
765
715
  if (_ParallClient.AUTH_PATHS.has(pathSuffix))
766
716
  return;
767
717
  const exp = _ParallClient.decodeJwtExp(this.token);
@@ -793,11 +743,11 @@ var init_client = __esm({
793
743
  this.refreshPromise = null;
794
744
  }
795
745
  }
796
- async request(method, path18, body, query, retried = false, opts) {
746
+ async request(method, path19, body, query, retried = false, opts) {
797
747
  if (!retried) {
798
- await this.ensureFreshToken(path18);
748
+ await this.ensureFreshToken(path19);
799
749
  }
800
- let url = `${this.baseUrlFor(path18)}${path18}`;
750
+ let url = `${this.baseUrlFor(path19)}${path19}`;
801
751
  if (query) {
802
752
  const params = new URLSearchParams();
803
753
  for (const [key, value] of Object.entries(query)) {
@@ -809,7 +759,7 @@ var init_client = __esm({
809
759
  if (qs)
810
760
  url += `?${qs}`;
811
761
  }
812
- const headers = this.buildHeaders();
762
+ const headers = this.buildHeaders(path19);
813
763
  const timeoutSignal = AbortSignal.timeout(opts?.timeoutMs ?? 15e3);
814
764
  const signal = opts?.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
815
765
  let res;
@@ -827,12 +777,12 @@ var init_client = __esm({
827
777
  throw _ParallClient.normalizeFetchError(err);
828
778
  }
829
779
  if (res.status === 401) {
830
- const pathSuffix = path18.replace(/^\/api\/v1/, "");
780
+ const pathSuffix = path19.replace(/^\/api\/v1/, "");
831
781
  const isAuthPath = _ParallClient.AUTH_PATHS.has(pathSuffix);
832
782
  if (!retried && !isAuthPath && this.getRefreshToken) {
833
783
  const refreshed = await this.tryRefresh();
834
784
  if (refreshed) {
835
- return this.request(method, path18, body, query, true, opts);
785
+ return this.request(method, path19, body, query, true, opts);
836
786
  }
837
787
  }
838
788
  if (this.onTokenExpired && !isAuthPath) {
@@ -862,15 +812,15 @@ var init_client = __esm({
862
812
  * hit the 100 MiB cap, so a longer 5-minute timeout is used so a
863
813
  * 50 MiB blob on a slow connection doesn't get chopped at 15 s.
864
814
  */
865
- async multipartRequest(method, path18, body, retried = false) {
815
+ async multipartRequest(method, path19, body, retried = false) {
866
816
  if (!retried) {
867
- await this.ensureFreshToken(path18);
817
+ await this.ensureFreshToken(path19);
868
818
  }
869
- const { "Content-Type": _drop, ...headers } = this.buildHeaders();
819
+ const { "Content-Type": _drop, ...headers } = this.buildHeaders(path19);
870
820
  void _drop;
871
821
  let res;
872
822
  try {
873
- res = await fetch(`${this.baseUrlFor(path18)}${path18}`, {
823
+ res = await fetch(`${this.baseUrlFor(path19)}${path19}`, {
874
824
  method,
875
825
  headers,
876
826
  body,
@@ -880,12 +830,12 @@ var init_client = __esm({
880
830
  throw _ParallClient.normalizeFetchError(err);
881
831
  }
882
832
  if (res.status === 401) {
883
- const pathSuffix = path18.replace(/^\/api\/v1/, "");
833
+ const pathSuffix = path19.replace(/^\/api\/v1/, "");
884
834
  const isAuthPath = _ParallClient.AUTH_PATHS.has(pathSuffix);
885
835
  if (!retried && !isAuthPath && this.getRefreshToken) {
886
836
  const refreshed = await this.tryRefresh();
887
837
  if (refreshed) {
888
- return this.multipartRequest(method, path18, body, true);
838
+ return this.multipartRequest(method, path19, body, true);
889
839
  }
890
840
  }
891
841
  if (this.onTokenExpired && !isAuthPath) {
@@ -1589,8 +1539,8 @@ var init_client = __esm({
1589
1539
  async requestMachineUpdate(orgId, machineId, mandatory = false) {
1590
1540
  await this.request("POST", ENDPOINTS.MACHINE_REQUEST_UPDATE(orgId, machineId), { mandatory });
1591
1541
  }
1592
- async browseMachineFilesystem(orgId, machineId, path18) {
1593
- return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path: path18 }, void 0, false, { timeoutMs: 15e3 });
1542
+ async browseMachineFilesystem(orgId, machineId, path19) {
1543
+ return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path: path19 }, void 0, false, { timeoutMs: 15e3 });
1594
1544
  }
1595
1545
  /** Create a new machine key. Returns the raw key string (shown once) + metadata. */
1596
1546
  async createMachineKey(orgId, machineId, name) {
@@ -1675,7 +1625,6 @@ var init_client = __esm({
1675
1625
  async steerDispatch(orgId, req) {
1676
1626
  return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
1677
1627
  }
1678
- /** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
1679
1628
  async completeDispatch(orgId, req) {
1680
1629
  return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
1681
1630
  }
@@ -1683,6 +1632,10 @@ var init_client = __esm({
1683
1632
  * End a turn for a lane-less runtime: resolve the turn's folded WorkItems
1684
1633
  * by source — broad-cover to the turn's reply Effect when one exists,
1685
1634
  * no_action sweep otherwise. Idempotent.
1635
+ *
1636
+ * @deprecated Legacy ok-only alias — use {@link completeDispatch} with the
1637
+ * `sources` form, which also carries `turn_outcome`. The endpoint retires
1638
+ * at S3b (dispatch-convergence-design.md §3).
1686
1639
  */
1687
1640
  async completeDispatchSources(orgId, req) {
1688
1641
  return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
@@ -1711,7 +1664,7 @@ var init_client = __esm({
1711
1664
  if (currentVersion !== void 0) {
1712
1665
  extra["If-None-Match"] = currentVersion;
1713
1666
  }
1714
- const headers = this.buildHeaders(extra);
1667
+ const headers = this.buildHeaders(ENDPOINTS.PLATFORM_CONFIG, extra);
1715
1668
  let res;
1716
1669
  try {
1717
1670
  res = await fetch(url, {
@@ -2159,8 +2112,8 @@ var init_client = __esm({
2159
2112
  async deleteWikiRestriction(orgId, wikiId, restrictionId) {
2160
2113
  await this.request("DELETE", ENDPOINTS.WIKI_RESTRICTION(orgId, wikiId, restrictionId));
2161
2114
  }
2162
- async getWikiAccessStatus(orgId, wikiId, path18) {
2163
- return this.request("GET", ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), void 0, path18 ? { path: path18 } : void 0);
2115
+ async getWikiAccessStatus(orgId, wikiId, path19) {
2116
+ return this.request("GET", ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), void 0, path19 ? { path: path19 } : void 0);
2164
2117
  }
2165
2118
  async createWikiAccessRequest(orgId, wikiId, data) {
2166
2119
  await this.request("POST", ENDPOINTS.WIKI_ACCESS_REQUESTS(orgId, wikiId), data);
@@ -2169,14 +2122,14 @@ var init_client = __esm({
2169
2122
  async getWikiCommits(orgId, wikiId, params) {
2170
2123
  return this.request("GET", ENDPOINTS.WIKI_COMMITS(orgId, wikiId), void 0, params);
2171
2124
  }
2172
- async getWikiFileCommits(orgId, wikiId, path18, params) {
2125
+ async getWikiFileCommits(orgId, wikiId, path19, params) {
2173
2126
  return this.request("GET", ENDPOINTS.WIKI_FILE_COMMITS(orgId, wikiId), void 0, {
2174
- path: path18,
2127
+ path: path19,
2175
2128
  ...params
2176
2129
  });
2177
2130
  }
2178
- async getWikiBlame(orgId, wikiId, path18, ref) {
2179
- return this.request("GET", ENDPOINTS.WIKI_BLAME(orgId, wikiId), void 0, { path: path18, ref });
2131
+ async getWikiBlame(orgId, wikiId, path19, ref) {
2132
+ return this.request("GET", ENDPOINTS.WIKI_BLAME(orgId, wikiId), void 0, { path: path19, ref });
2180
2133
  }
2181
2134
  // ---- Wiki Operations (audit log) ----
2182
2135
  async getWikiOperations(orgId, wikiId, params) {
@@ -2824,10 +2777,69 @@ var init_lane_ledger = __esm({
2824
2777
  });
2825
2778
 
2826
2779
  // ts/agent-core/dist/gateway-lane-flow.js
2780
+ var TYPED_BACKOFF_CAP_MS;
2827
2781
  var init_gateway_lane_flow = __esm({
2828
2782
  "ts/agent-core/dist/gateway-lane-flow.js"() {
2829
2783
  "use strict";
2830
2784
  init_lane_ledger();
2785
+ TYPED_BACKOFF_CAP_MS = 5 * 6e4;
2786
+ }
2787
+ });
2788
+
2789
+ // ts/agent-core/dist/session-state.js
2790
+ var init_session_state = __esm({
2791
+ "ts/agent-core/dist/session-state.js"() {
2792
+ "use strict";
2793
+ }
2794
+ });
2795
+
2796
+ // ts/agent-core/dist/routing.js
2797
+ var init_routing = __esm({
2798
+ "ts/agent-core/dist/routing.js"() {
2799
+ "use strict";
2800
+ }
2801
+ });
2802
+
2803
+ // ts/agent-core/dist/event-format.js
2804
+ var init_event_format = __esm({
2805
+ "ts/agent-core/dist/event-format.js"() {
2806
+ "use strict";
2807
+ }
2808
+ });
2809
+
2810
+ // ts/agent-core/dist/prompt-fragments.js
2811
+ var init_prompt_fragments = __esm({
2812
+ "ts/agent-core/dist/prompt-fragments.js"() {
2813
+ "use strict";
2814
+ }
2815
+ });
2816
+
2817
+ // ts/agent-core/dist/bridge-workspace.js
2818
+ var init_bridge_workspace = __esm({
2819
+ "ts/agent-core/dist/bridge-workspace.js"() {
2820
+ "use strict";
2821
+ }
2822
+ });
2823
+
2824
+ // ts/agent-core/dist/dispatch-adapter.js
2825
+ var init_dispatch_adapter = __esm({
2826
+ "ts/agent-core/dist/dispatch-adapter.js"() {
2827
+ "use strict";
2828
+ }
2829
+ });
2830
+
2831
+ // ts/agent-core/dist/logger.js
2832
+ function createLogger(prefix) {
2833
+ return {
2834
+ info: (msg) => console.log(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
2835
+ warn: (msg) => console.warn(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
2836
+ error: (msg) => console.error(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
2837
+ child: (sub) => createLogger(`${prefix}:${sub}`)
2838
+ };
2839
+ }
2840
+ var init_logger = __esm({
2841
+ "ts/agent-core/dist/logger.js"() {
2842
+ "use strict";
2831
2843
  }
2832
2844
  });
2833
2845
 
@@ -20510,9 +20522,9 @@ var require_getMachineId_linux = __commonJS({
20510
20522
  var api_1 = (init_esm(), __toCommonJS(esm_exports));
20511
20523
  async function getMachineId() {
20512
20524
  const paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
20513
- for (const path18 of paths) {
20525
+ for (const path19 of paths) {
20514
20526
  try {
20515
- const result = await fs_1.promises.readFile(path18, { encoding: "utf8" });
20527
+ const result = await fs_1.promises.readFile(path19, { encoding: "utf8" });
20516
20528
  return result.trim();
20517
20529
  } catch (e) {
20518
20530
  api_1.diag.debug(`error reading machine id: ${e}`);
@@ -20722,7 +20734,7 @@ var require_ProcessDetectorSync = __commonJS({
20722
20734
  var api_1 = (init_esm(), __toCommonJS(esm_exports));
20723
20735
  var semantic_conventions_1 = (init_esm2(), __toCommonJS(esm_exports2));
20724
20736
  var Resource_1 = require_Resource();
20725
- var os6 = __require("os");
20737
+ var os8 = __require("os");
20726
20738
  var ProcessDetectorSync = class {
20727
20739
  detect(_config) {
20728
20740
  const attributes = {
@@ -20742,7 +20754,7 @@ var require_ProcessDetectorSync = __commonJS({
20742
20754
  attributes[semantic_conventions_1.SEMRESATTRS_PROCESS_COMMAND] = process.argv[1];
20743
20755
  }
20744
20756
  try {
20745
- const userInfo = os6.userInfo();
20757
+ const userInfo = os8.userInfo();
20746
20758
  attributes[semantic_conventions_1.SEMRESATTRS_PROCESS_OWNER] = userInfo.username;
20747
20759
  } catch (e) {
20748
20760
  api_1.diag.debug(`error obtaining process owner: ${e}`);
@@ -23915,7 +23927,7 @@ function appendRootPathToUrlIfNeeded(url) {
23915
23927
  return void 0;
23916
23928
  }
23917
23929
  }
23918
- function appendResourcePathToUrl(url, path18) {
23930
+ function appendResourcePathToUrl(url, path19) {
23919
23931
  try {
23920
23932
  new URL(url);
23921
23933
  } catch (_a) {
@@ -23925,11 +23937,11 @@ function appendResourcePathToUrl(url, path18) {
23925
23937
  if (!url.endsWith("/")) {
23926
23938
  url = url + "/";
23927
23939
  }
23928
- url += path18;
23940
+ url += path19;
23929
23941
  try {
23930
23942
  new URL(url);
23931
23943
  } catch (_b) {
23932
- diag2.warn("Configuration: Provided URL appended with '" + path18 + "' is not a valid URL, using 'undefined' instead of '" + url + "'");
23944
+ diag2.warn("Configuration: Provided URL appended with '" + path19 + "' is not a valid URL, using 'undefined' instead of '" + url + "'");
23933
23945
  return void 0;
23934
23946
  }
23935
23947
  return url;
@@ -29390,6 +29402,20 @@ var init_platform_config = __esm({
29390
29402
  }
29391
29403
  });
29392
29404
 
29405
+ // ts/agent-core/dist/channel-capability.js
29406
+ var init_channel_capability = __esm({
29407
+ "ts/agent-core/dist/channel-capability.js"() {
29408
+ "use strict";
29409
+ }
29410
+ });
29411
+
29412
+ // ts/agent-core/dist/channel-token.js
29413
+ var init_channel_token = __esm({
29414
+ "ts/agent-core/dist/channel-token.js"() {
29415
+ "use strict";
29416
+ }
29417
+ });
29418
+
29393
29419
  // ts/agent-core/dist/skills/parall-platform.js
29394
29420
  var init_parall_platform = __esm({
29395
29421
  "ts/agent-core/dist/skills/parall-platform.js"() {
@@ -29458,6 +29484,7 @@ var init_dist2 = __esm({
29458
29484
  init_provider_config();
29459
29485
  init_types();
29460
29486
  init_lane_key();
29487
+ init_gateway_lane_flow();
29461
29488
  init_session_state();
29462
29489
  init_routing();
29463
29490
  init_event_format();
@@ -29467,6 +29494,8 @@ var init_dist2 = __esm({
29467
29494
  init_logger();
29468
29495
  init_gateway_base();
29469
29496
  init_platform_config();
29497
+ init_channel_capability();
29498
+ init_channel_token();
29470
29499
  init_skills();
29471
29500
  init_telemetry();
29472
29501
  }
@@ -29477,6 +29506,7 @@ var config_exports = {};
29477
29506
  __export(config_exports, {
29478
29507
  agentClaudeCredentialsFileFor: () => agentClaudeCredentialsFileFor,
29479
29508
  agentClaudeHomeFor: () => agentClaudeHomeFor,
29509
+ agentHomeDirFor: () => agentHomeDirFor,
29480
29510
  agentStateDirFor: () => agentStateDirFor,
29481
29511
  agentWorkspaceDirFor: () => agentWorkspaceDirFor,
29482
29512
  daemonConfigDir: () => daemonConfigDir,
@@ -29563,7 +29593,8 @@ function resolveClaudeDaemonConfig(env = process.env) {
29563
29593
  updateCdnUrl: env.PRLL_DAEMON_UPDATE_CDN_URL?.trim() || ((env.PRLL_DAEMON_UPDATE_CHANNEL?.trim() ?? "production") === "staging" ? "https://releases.staging.prll.sh/daemon/staging" : "https://releases.parall.com/daemon/production"),
29564
29594
  updateIntervalMs: parseMsAllowZero(env.PRLL_DAEMON_UPDATE_INTERVAL_MS, 6 * 60 * 6e4),
29565
29595
  updateDisabled: env.PRLL_DAEMON_UPDATE_DISABLED === "true" || !!env.KUBERNETES_SERVICE_HOST,
29566
- updateConfirmDelayMs: parseMsAllowZero(env.PRLL_DAEMON_UPDATE_CONFIRM_DELAY_MS?.trim() || void 0, 6e4)
29596
+ updateConfirmDelayMs: parseMsAllowZero(env.PRLL_DAEMON_UPDATE_CONFIRM_DELAY_MS?.trim() || void 0, 6e4),
29597
+ homeIsolationDisabled: env.PRLL_DAEMON_HOME_ISOLATION?.trim() === "0"
29567
29598
  };
29568
29599
  }
29569
29600
  function assertSafeAgentId(agentId) {
@@ -29587,6 +29618,9 @@ function agentClaudeCredentialsFileFor(agentClaudeHome) {
29587
29618
  function agentWorkspaceDirFor(rootStateDir, agentId) {
29588
29619
  return path3.join(rootStateDir, "agents", assertSafeAgentId(agentId), "workspace");
29589
29620
  }
29621
+ function agentHomeDirFor(rootStateDir, agentId) {
29622
+ return path3.join(rootStateDir, "agents", assertSafeAgentId(agentId), "home");
29623
+ }
29590
29624
  function resolveUpdateSigningEnabled(env = process.env) {
29591
29625
  return env.PRLL_DAEMON_SIGNING_DISABLED !== "1";
29592
29626
  }
@@ -31101,6 +31135,7 @@ var init_manifest = __esm({
31101
31135
 
31102
31136
  // ts/daemon/dist/runtimes.js
31103
31137
  import * as fs7 from "node:fs";
31138
+ import * as os4 from "node:os";
31104
31139
  import * as path9 from "node:path";
31105
31140
  function buildStandardEnv(baseEnv, agentId, orgId, apiKey, dirs, pc) {
31106
31141
  const env = { ...baseEnv };
@@ -31113,6 +31148,8 @@ function buildStandardEnv(baseEnv, agentId, orgId, apiKey, dirs, pc) {
31113
31148
  env.PRLL_WORKSPACE_DIR = dirs.workspaceDir;
31114
31149
  if (pc)
31115
31150
  env.PRLL_PROVIDER_CONFIG = JSON.stringify(pc);
31151
+ if (dirs.homeDir)
31152
+ env.HOME = dirs.homeDir;
31116
31153
  delete env.PRLL_DAEMON_MODE;
31117
31154
  return env;
31118
31155
  }
@@ -31158,6 +31195,8 @@ var init_runtimes = __esm({
31158
31195
  const env = buildStandardEnv(baseEnv, agentId, orgId, apiKey, dirs, pc);
31159
31196
  if (baseEnv.KUBERNETES_SERVICE_HOST || llmSource(pc) !== "runtime_auth") {
31160
31197
  env.PRLL_CODEX_HOME = path9.join(dirs.stateDir, ".codex");
31198
+ } else if (dirs.homeDir && !env.CODEX_HOME) {
31199
+ env.CODEX_HOME = path9.join(baseEnv.HOME || os4.homedir(), ".codex");
31161
31200
  }
31162
31201
  return env;
31163
31202
  }
@@ -32408,7 +32447,7 @@ var init_process_manager = __esm({
32408
32447
  * (GetBindings), then forward the invoke through the hub (Invoke) to the bound
32409
32448
  * BrowserProfile's host. No local shortcut, no fallback — unbound is rejected.
32410
32449
  * The execution daemon never verifies clip_token; the hub does.
32411
- * See docs/engineering-design/browser-profile-clip-integration.md §11.3.
32450
+ * See docs/archive/engineering-design/browser-profile-clip-integration.md §11.3.
32412
32451
  */
32413
32452
  async invokeDependency(clipName, command, input, context2) {
32414
32453
  if (!isBrowserDependencyName(clipName)) {
@@ -32441,7 +32480,7 @@ var init_process_manager = __esm({
32441
32480
  * synthetic "browser" clip). The hub forwards clip_token as the PLAINTEXT
32442
32481
  * browser_profile_id, which the host daemon uses directly as the bb-browser
32443
32482
  * account. Only daemons that host BrowserProfiles register this capability.
32444
- * See docs/engineering-design/browser-profile-clip-integration.md §11.2.
32483
+ * See docs/archive/engineering-design/browser-profile-clip-integration.md §11.2.
32445
32484
  */
32446
32485
  async invokeBrowserCapability(clipToken, command, input) {
32447
32486
  if (!this.browserProfileManager) {
@@ -34796,7 +34835,7 @@ var init_clip_runtime = __esm({
34796
34835
  // ts/daemon/dist/filesystem.js
34797
34836
  import * as fs9 from "fs";
34798
34837
  import * as path13 from "path";
34799
- import * as os4 from "os";
34838
+ import * as os5 from "os";
34800
34839
  function browseDenyReason(value) {
34801
34840
  const normalized = path13.resolve(value).split(path13.sep).join("/");
34802
34841
  if (normalized === "/")
@@ -34816,8 +34855,8 @@ function browseDenyReason(value) {
34816
34855
  }
34817
34856
  function syntheticRoots() {
34818
34857
  const roots = [];
34819
- const platform2 = os4.platform();
34820
- const candidates = platform2 === "darwin" ? ["/Users", os4.homedir()] : ["/home", os4.homedir()];
34858
+ const platform2 = os5.platform();
34859
+ const candidates = platform2 === "darwin" ? ["/Users", os5.homedir()] : ["/home", os5.homedir()];
34821
34860
  for (const dir of [...new Set(candidates)]) {
34822
34861
  try {
34823
34862
  fs9.accessSync(dir, fs9.constants.R_OK);
@@ -34913,11 +34952,108 @@ var init_filesystem = __esm({
34913
34952
  }
34914
34953
  });
34915
34954
 
34916
- // ts/daemon/dist/runtime-bin-resolver.js
34917
- import { execFileSync as execFileSync3 } from "node:child_process";
34955
+ // ts/daemon/dist/home-isolation.js
34918
34956
  import * as fs10 from "node:fs";
34919
- import * as os5 from "node:os";
34920
34957
  import * as path14 from "node:path";
34958
+ function ensureIsolatedHome(spec, agentId, log2, platform2 = process.platform) {
34959
+ fs10.mkdirSync(spec.homeDir, { recursive: true });
34960
+ const failures = [];
34961
+ const attempt = (label, fn) => {
34962
+ try {
34963
+ fn();
34964
+ } catch (err) {
34965
+ failures.push(`${label}: ${String(err)}`);
34966
+ }
34967
+ };
34968
+ attempt(".claude link", () => {
34969
+ fs10.mkdirSync(path14.join(spec.claudeStateRoot, ".claude"), { recursive: true });
34970
+ ensureLink(path14.join(spec.homeDir, ".claude"), path14.join(spec.claudeStateRoot, ".claude"), agentId, log2);
34971
+ });
34972
+ attempt(".claude.json link", () => ensureLink(path14.join(spec.homeDir, ".claude.json"), path14.join(spec.claudeStateRoot, ".claude.json"), agentId, log2));
34973
+ if (platform2 === "darwin") {
34974
+ attempt("Library/Keychains link", () => {
34975
+ fs10.mkdirSync(path14.join(spec.homeDir, "Library"), { recursive: true });
34976
+ ensureLink(path14.join(spec.homeDir, "Library", "Keychains"), path14.join(spec.systemHome, "Library", "Keychains"), agentId, log2);
34977
+ });
34978
+ }
34979
+ attempt(".gitconfig", () => {
34980
+ const gitconfig = path14.join(spec.homeDir, ".gitconfig");
34981
+ if (!fs10.existsSync(gitconfig)) {
34982
+ fs10.writeFileSync(gitconfig, `[user]
34983
+ name = ${gitConfigValue(spec.gitUserName)}
34984
+ email = ${gitConfigValue(spec.gitUserEmail)}
34985
+ `, { mode: 420 });
34986
+ log2.info(`agent ${agentId}: wrote per-agent .gitconfig (${spec.gitUserName})`);
34987
+ }
34988
+ });
34989
+ return failures;
34990
+ }
34991
+ function gitConfigValue(value) {
34992
+ const flat = value.replace(/[\r\n\t]+/g, " ").trim();
34993
+ return `"${flat.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
34994
+ }
34995
+ function ensureLink(linkPath, target, agentId, log2) {
34996
+ let existing;
34997
+ try {
34998
+ existing = fs10.lstatSync(linkPath);
34999
+ } catch (err) {
35000
+ if (err.code !== "ENOENT")
35001
+ throw err;
35002
+ }
35003
+ if (existing) {
35004
+ if (existing.isSymbolicLink()) {
35005
+ const current = fs10.readlinkSync(linkPath);
35006
+ if (path14.resolve(path14.dirname(linkPath), current) === path14.resolve(target))
35007
+ return;
35008
+ fs10.unlinkSync(linkPath);
35009
+ log2.info(`agent ${agentId}: relinking ${linkPath} \u2192 ${target}`);
35010
+ } else {
35011
+ const preserved = `${linkPath}.pre-isolation.${Date.now()}`;
35012
+ fs10.renameSync(linkPath, preserved);
35013
+ log2.warn(`agent ${agentId}: ${linkPath} was a real ${existing.isDirectory() ? "directory" : "file"}, not the expected passthrough symlink \u2014 preserved at ${preserved} and relinked`);
35014
+ }
35015
+ }
35016
+ fs10.symlinkSync(target, linkPath);
35017
+ }
35018
+ function ensureSharedCredentialLink(rootClaudeHome, agentClaudeHome, agentId, log2) {
35019
+ const sharedCredentials = path14.resolve(sharedClaudeCredentialsFileFor(rootClaudeHome));
35020
+ const agentCredentials = agentClaudeCredentialsFileFor(agentClaudeHome);
35021
+ const agentCredentialsDir = path14.dirname(agentCredentials);
35022
+ fs10.mkdirSync(path14.dirname(sharedCredentials), { recursive: true });
35023
+ fs10.mkdirSync(agentCredentialsDir, { recursive: true });
35024
+ try {
35025
+ const existing = fs10.lstatSync(agentCredentials);
35026
+ if (existing.isSymbolicLink()) {
35027
+ const currentTarget = fs10.readlinkSync(agentCredentials);
35028
+ if (path14.resolve(agentCredentialsDir, currentTarget) === sharedCredentials) {
35029
+ return;
35030
+ }
35031
+ fs10.unlinkSync(agentCredentials);
35032
+ } else if (existing.isDirectory()) {
35033
+ log2.warn(`agent ${agentId}: credential path is a directory, cannot link ${agentCredentials}`);
35034
+ return;
35035
+ } else {
35036
+ fs10.unlinkSync(agentCredentials);
35037
+ }
35038
+ } catch (err) {
35039
+ if (err.code !== "ENOENT") {
35040
+ throw err;
35041
+ }
35042
+ }
35043
+ fs10.symlinkSync(sharedCredentials, agentCredentials);
35044
+ }
35045
+ var init_home_isolation = __esm({
35046
+ "ts/daemon/dist/home-isolation.js"() {
35047
+ "use strict";
35048
+ init_config();
35049
+ }
35050
+ });
35051
+
35052
+ // ts/daemon/dist/runtime-bin-resolver.js
35053
+ import { execFileSync as execFileSync3 } from "node:child_process";
35054
+ import * as fs11 from "node:fs";
35055
+ import * as os6 from "node:os";
35056
+ import * as path15 from "node:path";
34921
35057
  function runtimeBinaryEnvVar(runtimeType) {
34922
35058
  return RUNTIME_BINARIES[runtimeType]?.envVar;
34923
35059
  }
@@ -34994,16 +35130,16 @@ function resolveRuntimeCommand(command, env, inheritedPath, primaryPath, fallbac
34994
35130
  function resolveDirectPath(command) {
34995
35131
  if (!command.includes("/") && !command.includes("\\"))
34996
35132
  return null;
34997
- const abs = path14.isAbsolute(command) ? command : path14.resolve(process.cwd(), command);
35133
+ const abs = path15.isAbsolute(command) ? command : path15.resolve(process.cwd(), command);
34998
35134
  return isExecutable(abs) ? abs : null;
34999
35135
  }
35000
35136
  function resolveFromPath(command, pathValue, env) {
35001
35137
  if (!pathValue || command.includes("/") || command.includes("\\"))
35002
35138
  return null;
35003
- const dirs = pathValue.split(path14.delimiter).filter(Boolean);
35139
+ const dirs = pathValue.split(path15.delimiter).filter(Boolean);
35004
35140
  for (const dir of dirs) {
35005
35141
  for (const file of commandCandidates(command, env)) {
35006
- const candidate = path14.join(dir, file);
35142
+ const candidate = path15.join(dir, file);
35007
35143
  if (isExecutable(candidate))
35008
35144
  return { binaryPath: candidate, pathValue };
35009
35145
  }
@@ -35042,7 +35178,7 @@ __PRLL_PATH__%s
35042
35178
  if (line.startsWith("__PRLL_PATH__"))
35043
35179
  pathValue = line.slice("__PRLL_PATH__".length);
35044
35180
  }
35045
- if (path14.isAbsolute(binaryPath) && isExecutable(binaryPath)) {
35181
+ if (path15.isAbsolute(binaryPath) && isExecutable(binaryPath)) {
35046
35182
  return { binaryPath, pathValue: pathValue || void 0 };
35047
35183
  }
35048
35184
  } catch {
@@ -35060,25 +35196,25 @@ function cachedCandidatePathPlan(env) {
35060
35196
  return value;
35061
35197
  }
35062
35198
  function candidatePathPlan(env) {
35063
- const home = env.HOME || os5.homedir();
35199
+ const home = env.HOME || os6.homedir();
35064
35200
  const primaryDirs = [
35065
35201
  ...splitPath(env.PRLL_DAEMON_RUNTIME_PATH),
35066
35202
  ...splitPath(env.PRLL_DAEMON_EXTRA_PATH),
35067
- path14.dirname(process.execPath),
35068
- path14.join(path14.dirname(process.execPath), "bin"),
35069
- path14.resolve(path14.dirname(process.execPath), "..", "Resources", "bin"),
35070
- path14.join(home, ".local", "bin"),
35071
- path14.join(home, "bin"),
35072
- path14.join(home, ".npm-global", "bin"),
35073
- path14.join(home, "Library", "pnpm"),
35074
- path14.join(home, ".local", "share", "pnpm"),
35075
- path14.join(home, ".volta", "bin"),
35076
- path14.join(home, ".bun", "bin"),
35077
- path14.join(home, ".asdf", "shims"),
35078
- path14.join(home, ".local", "share", "mise", "shims"),
35079
- path14.join(home, ".mise", "shims"),
35080
- path14.join(home, ".fnm", "aliases", "default", "bin"),
35081
- path14.join(home, "Library", "Application Support", "fnm", "aliases", "default", "bin"),
35203
+ path15.dirname(process.execPath),
35204
+ path15.join(path15.dirname(process.execPath), "bin"),
35205
+ path15.resolve(path15.dirname(process.execPath), "..", "Resources", "bin"),
35206
+ path15.join(home, ".local", "bin"),
35207
+ path15.join(home, "bin"),
35208
+ path15.join(home, ".npm-global", "bin"),
35209
+ path15.join(home, "Library", "pnpm"),
35210
+ path15.join(home, ".local", "share", "pnpm"),
35211
+ path15.join(home, ".volta", "bin"),
35212
+ path15.join(home, ".bun", "bin"),
35213
+ path15.join(home, ".asdf", "shims"),
35214
+ path15.join(home, ".local", "share", "mise", "shims"),
35215
+ path15.join(home, ".mise", "shims"),
35216
+ path15.join(home, ".fnm", "aliases", "default", "bin"),
35217
+ path15.join(home, "Library", "Application Support", "fnm", "aliases", "default", "bin"),
35082
35218
  "/opt/homebrew/bin",
35083
35219
  "/usr/local/bin",
35084
35220
  "/usr/bin",
@@ -35097,29 +35233,29 @@ function candidatePathPlan(env) {
35097
35233
  };
35098
35234
  }
35099
35235
  function nvmVersionBinDirs(home) {
35100
- const root = path14.join(home, ".nvm", "versions", "node");
35236
+ const root = path15.join(home, ".nvm", "versions", "node");
35101
35237
  let versions;
35102
35238
  try {
35103
- versions = fs10.readdirSync(root);
35239
+ versions = fs11.readdirSync(root);
35104
35240
  } catch {
35105
35241
  return [];
35106
35242
  }
35107
- return sortVersionNamesDesc(versions).map((version) => path14.join(root, version, "bin"));
35243
+ return sortVersionNamesDesc(versions).map((version) => path15.join(root, version, "bin"));
35108
35244
  }
35109
35245
  function fnmVersionBinDirs(home) {
35110
35246
  const roots = [
35111
- path14.join(home, ".fnm", "node-versions"),
35112
- path14.join(home, "Library", "Application Support", "fnm", "node-versions")
35247
+ path15.join(home, ".fnm", "node-versions"),
35248
+ path15.join(home, "Library", "Application Support", "fnm", "node-versions")
35113
35249
  ];
35114
35250
  const dirs = [];
35115
35251
  for (const root of roots) {
35116
35252
  let versions;
35117
35253
  try {
35118
- versions = fs10.readdirSync(root);
35254
+ versions = fs11.readdirSync(root);
35119
35255
  } catch {
35120
35256
  continue;
35121
35257
  }
35122
- dirs.push(...sortVersionNamesDesc(versions).map((version) => path14.join(root, version, "installation", "bin")));
35258
+ dirs.push(...sortVersionNamesDesc(versions).map((version) => path15.join(root, version, "installation", "bin")));
35123
35259
  }
35124
35260
  return dirs;
35125
35261
  }
@@ -35141,13 +35277,13 @@ function parseVersionName(value) {
35141
35277
  return value.replace(/^v/i, "").split(".").map((part) => Number.parseInt(part, 10)).filter((part) => Number.isFinite(part));
35142
35278
  }
35143
35279
  function splitPath(value) {
35144
- return value?.split(path14.delimiter).filter(Boolean) ?? [];
35280
+ return value?.split(path15.delimiter).filter(Boolean) ?? [];
35145
35281
  }
35146
35282
  function mergePath(prependDirs, existing) {
35147
- return unique([...prependDirs, ...splitPath(existing)]).join(path14.delimiter);
35283
+ return unique([...prependDirs, ...splitPath(existing)]).join(path15.delimiter);
35148
35284
  }
35149
35285
  function anchorResolvedPath(pathValue, resolution) {
35150
- return mergePath([path14.dirname(resolution.binaryPath), ...splitPath(resolution.pathValue)], pathValue);
35286
+ return mergePath([path15.dirname(resolution.binaryPath), ...splitPath(resolution.pathValue)], pathValue);
35151
35287
  }
35152
35288
  function commandCandidates(command, env) {
35153
35289
  if (process.platform !== "win32")
@@ -35173,10 +35309,10 @@ function setCachedResolution(cacheKey, value) {
35173
35309
  }
35174
35310
  function isExecutable(file) {
35175
35311
  try {
35176
- const stat = fs10.statSync(file);
35312
+ const stat = fs11.statSync(file);
35177
35313
  if (!stat.isFile())
35178
35314
  return false;
35179
- fs10.accessSync(file, fs10.constants.X_OK);
35315
+ fs11.accessSync(file, fs11.constants.X_OK);
35180
35316
  return true;
35181
35317
  } catch {
35182
35318
  return false;
@@ -35184,7 +35320,7 @@ function isExecutable(file) {
35184
35320
  }
35185
35321
  function isDirectory(dir) {
35186
35322
  try {
35187
- return fs10.statSync(dir).isDirectory();
35323
+ return fs11.statSync(dir).isDirectory();
35188
35324
  } catch {
35189
35325
  return false;
35190
35326
  }
@@ -35301,8 +35437,8 @@ var init_runtime_detector = __esm({
35301
35437
  // ts/daemon/dist/workspace.js
35302
35438
  import { spawn as spawn5 } from "node:child_process";
35303
35439
  import { createHash as createHash3 } from "node:crypto";
35304
- import * as fs11 from "node:fs";
35305
- import * as path15 from "node:path";
35440
+ import * as fs12 from "node:fs";
35441
+ import * as path16 from "node:path";
35306
35442
  async function prepareWorkspace(opts) {
35307
35443
  const prior = opts.attached.workspace_state;
35308
35444
  const plan = buildWorkspacePlan(opts.attached.daemon_config, opts.defaultWorkspaceDir, prior?.config_hash);
@@ -35417,7 +35553,7 @@ function resolveWorkspaceDir(workspace, defaultWorkspaceDir) {
35417
35553
  async function ensureWorkspace(plan, log2) {
35418
35554
  const ws = plan.workspace;
35419
35555
  if (ws.mode === "default") {
35420
- fs11.mkdirSync(plan.workspaceDir, { recursive: true });
35556
+ fs12.mkdirSync(plan.workspaceDir, { recursive: true });
35421
35557
  assertWritableWorkspaceDir(plan.workspaceDir);
35422
35558
  return;
35423
35559
  }
@@ -35425,7 +35561,7 @@ async function ensureWorkspace(plan, log2) {
35425
35561
  assertSafeCustomWorkspacePath(plan);
35426
35562
  let st;
35427
35563
  try {
35428
- st = fs11.statSync(plan.workspaceDir);
35564
+ st = fs12.statSync(plan.workspaceDir);
35429
35565
  } catch (err) {
35430
35566
  if (isNodeError(err) && err.code === "ENOENT") {
35431
35567
  throw new Error(`workspace path does not exist: ${plan.workspaceDir}`);
@@ -35435,7 +35571,7 @@ async function ensureWorkspace(plan, log2) {
35435
35571
  if (!st.isDirectory()) {
35436
35572
  throw new Error(`workspace path is not a directory: ${plan.workspaceDir}`);
35437
35573
  }
35438
- assertSafeCustomWorkspacePath(plan, fs11.realpathSync(plan.workspaceDir));
35574
+ assertSafeCustomWorkspacePath(plan, fs12.realpathSync(plan.workspaceDir));
35439
35575
  assertWritableWorkspaceDir(plan.workspaceDir);
35440
35576
  return;
35441
35577
  }
@@ -35446,17 +35582,17 @@ async function ensureWorkspace(plan, log2) {
35446
35582
  if (plan.customWorkspaceField) {
35447
35583
  assertSafeCustomWorkspacePath(plan);
35448
35584
  }
35449
- if (!fs11.existsSync(plan.workspaceDir)) {
35450
- fs11.mkdirSync(path15.dirname(plan.workspaceDir), { recursive: true });
35451
- assertWritableWorkspaceDir(path15.dirname(plan.workspaceDir));
35585
+ if (!fs12.existsSync(plan.workspaceDir)) {
35586
+ fs12.mkdirSync(path16.dirname(plan.workspaceDir), { recursive: true });
35587
+ assertWritableWorkspaceDir(path16.dirname(plan.workspaceDir));
35452
35588
  await runCommand("git", ["clone", remote, plan.workspaceDir], process.cwd());
35453
35589
  } else {
35454
- const st = fs11.statSync(plan.workspaceDir);
35590
+ const st = fs12.statSync(plan.workspaceDir);
35455
35591
  if (!st.isDirectory()) {
35456
35592
  throw new Error(`workspace path is not a directory: ${plan.workspaceDir}`);
35457
35593
  }
35458
35594
  if (plan.customWorkspaceField) {
35459
- assertSafeCustomWorkspacePath(plan, fs11.realpathSync(plan.workspaceDir));
35595
+ assertSafeCustomWorkspacePath(plan, fs12.realpathSync(plan.workspaceDir));
35460
35596
  }
35461
35597
  assertWritableWorkspaceDir(plan.workspaceDir);
35462
35598
  await ensureGitWorktree(plan.workspaceDir);
@@ -35480,13 +35616,13 @@ async function verifyExistingWorkspace(plan, log2) {
35480
35616
  if (plan.customWorkspaceField) {
35481
35617
  assertSafeCustomWorkspacePath(plan);
35482
35618
  }
35483
- const st = fs11.statSync(plan.workspaceDir);
35619
+ const st = fs12.statSync(plan.workspaceDir);
35484
35620
  if (!st.isDirectory()) {
35485
35621
  log2.warn(`workspace ready state ignored: path is not a directory: ${plan.workspaceDir}`);
35486
35622
  return false;
35487
35623
  }
35488
35624
  if (plan.customWorkspaceField) {
35489
- assertSafeCustomWorkspacePath(plan, fs11.realpathSync(plan.workspaceDir));
35625
+ assertSafeCustomWorkspacePath(plan, fs12.realpathSync(plan.workspaceDir));
35490
35626
  }
35491
35627
  assertWritableWorkspaceDir(plan.workspaceDir);
35492
35628
  if (plan.workspace.mode === "git") {
@@ -35630,10 +35766,10 @@ ${tail}`)));
35630
35766
  });
35631
35767
  }
35632
35768
  function requireAbsolute(value, field) {
35633
- if (!value || !path15.isAbsolute(value)) {
35769
+ if (!value || !path16.isAbsolute(value)) {
35634
35770
  throw new Error(`${field} must be an absolute path`);
35635
35771
  }
35636
- return path15.resolve(value);
35772
+ return path16.resolve(value);
35637
35773
  }
35638
35774
  function assertSafeCustomWorkspacePath(plan, candidate = plan.workspaceDir) {
35639
35775
  if (!plan.customWorkspaceField)
@@ -35643,17 +35779,17 @@ function assertSafeCustomWorkspacePath(plan, candidate = plan.workspaceDir) {
35643
35779
  if (reason) {
35644
35780
  throw new Error(`${plan.customWorkspaceField} must point to a project folder, not ${reason}: ${normalized}`);
35645
35781
  }
35646
- const defaultWorkspace = path15.resolve(plan.defaultWorkspaceDir);
35782
+ const defaultWorkspace = path16.resolve(plan.defaultWorkspaceDir);
35647
35783
  if (isAncestorPath(normalized, defaultWorkspace) || normalized === defaultWorkspace) {
35648
35784
  throw new Error(`${plan.customWorkspaceField} must not point at daemon state directories: ${normalized}`);
35649
35785
  }
35650
35786
  }
35651
35787
  function assertWritableWorkspaceDir(dir) {
35652
- fs11.accessSync(dir, fs11.constants.R_OK | fs11.constants.W_OK | fs11.constants.X_OK);
35653
- const probe = path15.join(dir, `.parall-workspace-check-${process.pid}-${Date.now()}`);
35654
- const fd = fs11.openSync(probe, "wx", 384);
35655
- fs11.closeSync(fd);
35656
- fs11.unlinkSync(probe);
35788
+ fs12.accessSync(dir, fs12.constants.R_OK | fs12.constants.W_OK | fs12.constants.X_OK);
35789
+ const probe = path16.join(dir, `.parall-workspace-check-${process.pid}-${Date.now()}`);
35790
+ const fd = fs12.openSync(probe, "wx", 384);
35791
+ fs12.closeSync(fd);
35792
+ fs12.unlinkSync(probe);
35657
35793
  }
35658
35794
  function workspacePathDenyReason(value) {
35659
35795
  if (value === "/")
@@ -35714,11 +35850,11 @@ function workspacePathDenyReason(value) {
35714
35850
  return "";
35715
35851
  }
35716
35852
  function isAncestorPath(parent, child) {
35717
- const relative2 = path15.relative(parent, child);
35718
- return relative2 !== "" && !relative2.startsWith("..") && !path15.isAbsolute(relative2);
35853
+ const relative2 = path16.relative(parent, child);
35854
+ return relative2 !== "" && !relative2.startsWith("..") && !path16.isAbsolute(relative2);
35719
35855
  }
35720
35856
  function toPolicyPath(value) {
35721
- return path15.resolve(value).split(path15.sep).join("/");
35857
+ return path16.resolve(value).split(path16.sep).join("/");
35722
35858
  }
35723
35859
  function isNodeError(err) {
35724
35860
  return err instanceof Error && "code" in err;
@@ -35735,8 +35871,9 @@ var init_workspace = __esm({
35735
35871
 
35736
35872
  // ts/daemon/dist/supervisor.js
35737
35873
  import { spawn as spawn6 } from "node:child_process";
35738
- import * as fs12 from "node:fs";
35739
- import * as path16 from "node:path";
35874
+ import * as fs13 from "node:fs";
35875
+ import * as os7 from "node:os";
35876
+ import * as path17 from "node:path";
35740
35877
  function sleepCancellable(ms, signal) {
35741
35878
  if (signal.aborted)
35742
35879
  return Promise.resolve(false);
@@ -35763,6 +35900,7 @@ var init_supervisor = __esm({
35763
35900
  init_clip_runtime();
35764
35901
  init_config();
35765
35902
  init_filesystem();
35903
+ init_home_isolation();
35766
35904
  init_runtimes();
35767
35905
  init_runtime_bin_resolver();
35768
35906
  init_runtime_detector();
@@ -35886,7 +36024,7 @@ var init_supervisor = __esm({
35886
36024
  this.migrateFlatLayout();
35887
36025
  if (process.env.PRLL_CLIP_RUNTIME_ENABLED === "true") {
35888
36026
  this.browserProfilePool = new BrowserProfilePool({
35889
- baseHomeDir: path16.join(this.config.rootStateDir, "bb-browser"),
36027
+ baseHomeDir: path17.join(this.config.rootStateDir, "bb-browser"),
35890
36028
  log: this.log,
35891
36029
  reportStatus: (profileId, status, errorMsg, generation) => {
35892
36030
  this.client.reportBrowserProfileStatus(profileId, status, errorMsg, generation).catch((err) => this.log.warn(`browser profile status report failed: ${String(err)}`));
@@ -35894,8 +36032,8 @@ var init_supervisor = __esm({
35894
36032
  resolveProxy: (profileId) => this.resolveBrowserProfileProxy(profileId)
35895
36033
  });
35896
36034
  this.clipManager = new ClipProcessManager({
35897
- clipsDir: path16.join(this.config.rootStateDir, "clips"),
35898
- dataDir: path16.join(this.config.rootStateDir, "clip-data"),
36035
+ clipsDir: path17.join(this.config.rootStateDir, "clips"),
36036
+ dataDir: path17.join(this.config.rootStateDir, "clip-data"),
35899
36037
  browserProfileManager: this.browserProfilePool,
35900
36038
  // Execution side: nested browser dependency invokes resolve their
35901
36039
  // binding and route through the hub (no local shortcut).
@@ -36124,6 +36262,22 @@ var init_supervisor = __esm({
36124
36262
  }
36125
36263
  await this.spawnAgent(userId, orgId, a);
36126
36264
  } else {
36265
+ const isolationChanged = !existing.shuttingDown && !!existing.homeIsolation !== !!this.buildHomeIsolationSpec(a, userId);
36266
+ if (isolationChanged) {
36267
+ this.log.info(`agent ${userId}: home isolation changed while disconnected \u2014 rebuilding child`);
36268
+ existing.shuttingDown = true;
36269
+ if (existing.restartTimer) {
36270
+ clearTimeout(existing.restartTimer);
36271
+ existing.restartTimer = null;
36272
+ }
36273
+ await this.terminateChild(existing);
36274
+ this.children.delete(userId);
36275
+ const orgId = this.machineOrgId;
36276
+ if (orgId) {
36277
+ await this.spawnAgent(userId, orgId, a);
36278
+ }
36279
+ continue;
36280
+ }
36127
36281
  const configChanged = JSON.stringify(existing.providerConfig ?? null) !== JSON.stringify(a.provider_config ?? null);
36128
36282
  existing.providerConfig = a.provider_config;
36129
36283
  if (!existing.child && !existing.restartTimer && !existing.shuttingDown) {
@@ -36284,15 +36438,15 @@ var init_supervisor = __esm({
36284
36438
  */
36285
36439
  migrateFlatLayout() {
36286
36440
  const root = this.config.rootStateDir;
36287
- const agentsDir = path16.join(root, "agents");
36288
- const flatWorkspace = path16.join(root, "workspace");
36289
- if (!fs12.existsSync(flatWorkspace) || fs12.existsSync(agentsDir))
36441
+ const agentsDir = path17.join(root, "agents");
36442
+ const flatWorkspace = path17.join(root, "workspace");
36443
+ if (!fs13.existsSync(flatWorkspace) || fs13.existsSync(agentsDir))
36290
36444
  return;
36291
36445
  let ownerAgentId;
36292
- const sessionsDir = path16.join(root, "sessions");
36293
- if (fs12.existsSync(sessionsDir)) {
36446
+ const sessionsDir = path17.join(root, "sessions");
36447
+ if (fs13.existsSync(sessionsDir)) {
36294
36448
  try {
36295
- for (const file of fs12.readdirSync(sessionsDir)) {
36449
+ for (const file of fs13.readdirSync(sessionsDir)) {
36296
36450
  if (!file.endsWith(".json"))
36297
36451
  continue;
36298
36452
  const decoded = Buffer.from(file.replace(".json", ""), "base64url").toString();
@@ -36306,13 +36460,13 @@ var init_supervisor = __esm({
36306
36460
  }
36307
36461
  }
36308
36462
  const targetId = ownerAgentId ?? "_orphan";
36309
- const targetDir = path16.join(agentsDir, targetId);
36463
+ const targetDir = path17.join(agentsDir, targetId);
36310
36464
  try {
36311
- fs12.mkdirSync(targetDir, { recursive: true });
36465
+ fs13.mkdirSync(targetDir, { recursive: true });
36312
36466
  for (const sub of ["workspace", "sessions", "dispatch-context"]) {
36313
- const src = path16.join(root, sub);
36314
- if (fs12.existsSync(src)) {
36315
- fs12.renameSync(src, path16.join(targetDir, sub));
36467
+ const src = path17.join(root, sub);
36468
+ if (fs13.existsSync(src)) {
36469
+ fs13.renameSync(src, path17.join(targetDir, sub));
36316
36470
  }
36317
36471
  }
36318
36472
  this.log.info(`migrated legacy flat state \u2192 agents/${targetId}/`);
@@ -36603,7 +36757,7 @@ var init_supervisor = __esm({
36603
36757
  return this.runtimeDetectInFlight;
36604
36758
  }
36605
36759
  machineClipToConfig(clip) {
36606
- const clipPath = path16.join(this.config.rootStateDir, "clips", clip.alias);
36760
+ const clipPath = path17.join(this.config.rootStateDir, "clips", clip.alias);
36607
36761
  return {
36608
36762
  clipId: clip.clip_id,
36609
36763
  name: clip.alias,
@@ -36669,7 +36823,7 @@ var init_supervisor = __esm({
36669
36823
  if (!sourceRef) {
36670
36824
  throw new Error(`registry clip "${config.name}" is missing source_ref`);
36671
36825
  }
36672
- const expectedPath = path16.join(this.config.rootStateDir, "clips", config.name);
36826
+ const expectedPath = path17.join(this.config.rootStateDir, "clips", config.name);
36673
36827
  const localVersion = this.readInstalledClipVersion(expectedPath);
36674
36828
  if (localVersion && (!config.version || localVersion === config.version)) {
36675
36829
  return { ...config, path: expectedPath, source: expectedPath };
@@ -36687,7 +36841,7 @@ var init_supervisor = __esm({
36687
36841
  const result = await installClip({
36688
36842
  source,
36689
36843
  alias: config.name,
36690
- clipsDir: path16.join(this.config.rootStateDir, "clips"),
36844
+ clipsDir: path17.join(this.config.rootStateDir, "clips"),
36691
36845
  registryUrl: process.env.PRLL_PINIX_REGISTRY_URL?.trim() || void 0
36692
36846
  });
36693
36847
  this.log.info(`clip ensured: ${result.alias} v${result.version} at ${result.path}`);
@@ -36702,7 +36856,7 @@ var init_supervisor = __esm({
36702
36856
  readInstalledClipVersion(dir) {
36703
36857
  for (const file of ["clip.json", "package.json"]) {
36704
36858
  try {
36705
- const raw = fs12.readFileSync(path16.join(dir, file), "utf-8");
36859
+ const raw = fs13.readFileSync(path17.join(dir, file), "utf-8");
36706
36860
  const parsed = JSON.parse(raw);
36707
36861
  if (typeof parsed.version === "string" && parsed.version.trim()) {
36708
36862
  return parsed.version.trim();
@@ -37012,6 +37166,28 @@ var init_supervisor = __esm({
37012
37166
  });
37013
37167
  }
37014
37168
  }
37169
+ /**
37170
+ * Local credential isolation (home-isolation.ts): per-agent opt-in via
37171
+ * daemon_config.home_isolation (unset = legacy shared HOME for existing
37172
+ * agents), machine-level kill switch PRLL_DAEMON_HOME_ISOLATION=0. K8s
37173
+ * pods never take this path — they keep the isolated claude-home +
37174
+ * shared-credential-link branch in spawnAgentOnce.
37175
+ */
37176
+ buildHomeIsolationSpec(attached, agentId) {
37177
+ if (process.env.KUBERNETES_SERVICE_HOST)
37178
+ return void 0;
37179
+ if (this.config.homeIsolationDisabled)
37180
+ return void 0;
37181
+ if (attached.daemon_config?.home_isolation !== true)
37182
+ return void 0;
37183
+ return {
37184
+ homeDir: agentHomeDirFor(this.config.rootStateDir, agentId),
37185
+ claudeStateRoot: this.config.rootClaudeHome,
37186
+ systemHome: os7.homedir(),
37187
+ gitUserName: attached.user?.display_name?.trim() || agentId,
37188
+ gitUserEmail: `${agentId}@noreply.parall.com`
37189
+ };
37190
+ }
37015
37191
  async spawnAgentOnce(agentId, orgId, attached) {
37016
37192
  let credential;
37017
37193
  try {
@@ -37023,12 +37199,13 @@ var init_supervisor = __esm({
37023
37199
  const stateDir = agentStateDirFor(this.config.rootStateDir, agentId);
37024
37200
  const defaultWorkspaceDir = agentWorkspaceDirFor(this.config.rootStateDir, agentId);
37025
37201
  const isK8s = !!process.env.KUBERNETES_SERVICE_HOST;
37026
- const claudeHome = isK8s ? agentClaudeHomeFor(this.config.rootClaudeHome, agentId) : this.config.rootClaudeHome;
37202
+ const homeIsolation = this.buildHomeIsolationSpec(attached, agentId);
37203
+ const claudeHome = isK8s ? agentClaudeHomeFor(this.config.rootClaudeHome, agentId) : homeIsolation?.homeDir ?? this.config.rootClaudeHome;
37027
37204
  try {
37028
- fs12.mkdirSync(stateDir, { recursive: true });
37205
+ fs13.mkdirSync(stateDir, { recursive: true });
37029
37206
  if (isK8s) {
37030
- fs12.mkdirSync(claudeHome, { recursive: true });
37031
- this.ensureSharedCredentialLink(claudeHome, agentId);
37207
+ fs13.mkdirSync(claudeHome, { recursive: true });
37208
+ ensureSharedCredentialLink(this.config.rootClaudeHome, claudeHome, agentId, this.log);
37032
37209
  }
37033
37210
  } catch (err) {
37034
37211
  this.log.warn(`mkdir agent dirs (${agentId}) failed: ${String(err)}`);
@@ -37061,6 +37238,7 @@ var init_supervisor = __esm({
37061
37238
  // resolved at startChild time (see resolveProviderConfig) so a respawn
37062
37239
  // after a machine llm_source change picks up the new source.
37063
37240
  providerConfig: attached.provider_config,
37241
+ homeIsolation,
37064
37242
  child: null,
37065
37243
  credential,
37066
37244
  restartAttempts: 0,
@@ -37080,17 +37258,38 @@ var init_supervisor = __esm({
37080
37258
  return;
37081
37259
  }
37082
37260
  assertAgentKey(state.credential.api_key);
37261
+ const effectiveProviderConfig = effectiveLLMSourceExplicit(state.providerConfig) ? state.providerConfig : { llm_source: this.machineLlmSource };
37262
+ let isolation = state.homeIsolation;
37263
+ if (isolation) {
37264
+ try {
37265
+ const failures = ensureIsolatedHome(isolation, state.agentId, this.log);
37266
+ if (failures.length > 0) {
37267
+ const needsPassthrough = state.runtimeType === "claude-code" && llmSource(effectiveProviderConfig) === "runtime_auth";
37268
+ if (needsPassthrough) {
37269
+ this.log.warn(`agent ${state.agentId}: home isolation passthrough failed (${failures.join("; ")}) \u2014 falling back to the shared HOME for this spawn so the runtime keeps its login`);
37270
+ isolation = void 0;
37271
+ } else {
37272
+ this.log.warn(`agent ${state.agentId}: home isolation passthrough incomplete (${failures.join("; ")}) \u2014 keeping the fake HOME (this runtime/source does not need the shared-login links)`);
37273
+ }
37274
+ }
37275
+ } catch (err) {
37276
+ this.log.warn(`agent ${state.agentId}: home isolation setup failed \u2014 falling back to the shared HOME for this spawn: ${String(err)}`);
37277
+ isolation = void 0;
37278
+ }
37279
+ }
37083
37280
  const adapter = getRuntimeAdapter(state.runtimeType);
37084
37281
  const dirs = {
37085
37282
  stateDir: agentStateDirFor(this.config.rootStateDir, state.agentId),
37086
37283
  workspaceDir: state.workspacePath,
37087
- claudeHome: state.claudeHome
37284
+ // Only the isolation-fallback case replaces claudeHome: K8s (isolation
37285
+ // never set) and legacy-shared children keep their state.claudeHome.
37286
+ claudeHome: state.homeIsolation && !isolation ? this.config.rootClaudeHome : state.claudeHome,
37287
+ homeDir: isolation?.homeDir
37088
37288
  };
37089
37289
  let baseEnv = { ...process.env, PRLL_API_URL: this.config.apiUrl };
37090
37290
  if (this.machineId)
37091
37291
  baseEnv.PRLL_MACHINE_ID = this.machineId;
37092
37292
  baseEnv = applyRuntimeBinaryEnv(state.runtimeType, baseEnv, this.log);
37093
- const effectiveProviderConfig = effectiveLLMSourceExplicit(state.providerConfig) ? state.providerConfig : { llm_source: this.machineLlmSource };
37094
37293
  const env = adapter.buildEnv(baseEnv, state.agentId, state.orgId, state.credential.api_key, dirs, effectiveProviderConfig);
37095
37294
  const spawnCmd = adapter.args.length > 0 ? `${adapter.bin} ${adapter.args.join(" ")}` : adapter.bin;
37096
37295
  this.log.info(`spawning agent ${state.agentId} runtime=${state.runtimeType} cmd=${spawnCmd} (attempt ${state.restartAttempts + 1})`);
@@ -37173,33 +37372,6 @@ var init_supervisor = __esm({
37173
37372
  child.once("exit", () => clearTimeout(hardKill));
37174
37373
  });
37175
37374
  }
37176
- ensureSharedCredentialLink(agentClaudeHome, agentId) {
37177
- const sharedCredentials = path16.resolve(sharedClaudeCredentialsFileFor(this.config.rootClaudeHome));
37178
- const agentCredentials = agentClaudeCredentialsFileFor(agentClaudeHome);
37179
- const agentCredentialsDir = path16.dirname(agentCredentials);
37180
- fs12.mkdirSync(path16.dirname(sharedCredentials), { recursive: true });
37181
- fs12.mkdirSync(agentCredentialsDir, { recursive: true });
37182
- try {
37183
- const existing = fs12.lstatSync(agentCredentials);
37184
- if (existing.isSymbolicLink()) {
37185
- const currentTarget = fs12.readlinkSync(agentCredentials);
37186
- if (path16.resolve(agentCredentialsDir, currentTarget) === sharedCredentials) {
37187
- return;
37188
- }
37189
- fs12.unlinkSync(agentCredentials);
37190
- } else if (existing.isDirectory()) {
37191
- this.log.warn(`agent ${agentId}: credential path is a directory, cannot link ${agentCredentials}`);
37192
- return;
37193
- } else {
37194
- fs12.unlinkSync(agentCredentials);
37195
- }
37196
- } catch (err) {
37197
- if (err.code !== "ENOENT") {
37198
- throw err;
37199
- }
37200
- }
37201
- fs12.symlinkSync(sharedCredentials, agentCredentials);
37202
- }
37203
37375
  };
37204
37376
  }
37205
37377
  });
@@ -37405,8 +37577,8 @@ var init_daemon_main = __esm({
37405
37577
  // ts/daemon/dist/index.js
37406
37578
  init_daemon_paths();
37407
37579
  init_daemon_update_mode();
37408
- import * as fs13 from "node:fs";
37409
- import * as path17 from "node:path";
37580
+ import * as fs14 from "node:fs";
37581
+ import * as path18 from "node:path";
37410
37582
  var UPDATE_EXIT_CODE2 = 42;
37411
37583
  function formatError2(reason) {
37412
37584
  if (reason instanceof Error) {
@@ -37422,7 +37594,7 @@ function errnoCode(err) {
37422
37594
  }
37423
37595
  function clearRunningMarker(markerPath) {
37424
37596
  try {
37425
- fs13.unlinkSync(markerPath);
37597
+ fs14.unlinkSync(markerPath);
37426
37598
  } catch (err) {
37427
37599
  if (errnoCode(err) === "ENOENT")
37428
37600
  return;
@@ -37431,15 +37603,15 @@ function clearRunningMarker(markerPath) {
37431
37603
  }
37432
37604
  function prepareDaemonBootstrap(env = process.env, args = process.argv.slice(2)) {
37433
37605
  const bundleDir = resolveBundleDir(env);
37434
- const runningMarker = path17.join(bundleDir, "daemon-running");
37606
+ const runningMarker = path18.join(bundleDir, "daemon-running");
37435
37607
  const lifecycleMarkerEnabled = args.length === 0 && !isSelfUpdateDisabledByEnv(env) && isSelfUpdateManaged(bundleDir, env);
37436
37608
  if (!lifecycleMarkerEnabled) {
37437
37609
  return { lifecycleMarkerEnabled: false, runningMarker, uncleanPrevExit: false };
37438
37610
  }
37439
- const uncleanPrevExit = fs13.existsSync(runningMarker);
37611
+ const uncleanPrevExit = fs14.existsSync(runningMarker);
37440
37612
  try {
37441
- fs13.mkdirSync(bundleDir, { recursive: true });
37442
- fs13.writeFileSync(runningMarker, String(process.pid));
37613
+ fs14.mkdirSync(bundleDir, { recursive: true });
37614
+ fs14.writeFileSync(runningMarker, String(process.pid));
37443
37615
  } catch (err) {
37444
37616
  console.warn(`failed to write daemon running marker: ${formatError2(err)}`);
37445
37617
  }