cclawd 1.0.3 → 1.0.4

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 (122) hide show
  1. package/dist/{active-listener-Dkhmfuwx.js → active-listener-BLd27Pxd.js} +1 -1
  2. package/dist/{api-key-rotation-BOfI3cG3.js → api-key-rotation-Dg3JlNDQ.js} +1 -1
  3. package/dist/{audio-preflight-CtkZ5SAs.js → audio-preflight-eV5m9mMp.js} +6 -6
  4. package/dist/{audio-transcription-runner-CbPqoiHX.js → audio-transcription-runner-CQU4Eg1M.js} +6 -6
  5. package/dist/build-info.json +3 -3
  6. package/dist/bundled/boot-md/handler.js +23 -23
  7. package/dist/bundled/session-memory/handler.js +23 -23
  8. package/dist/{channel-activity-DWAER4wd.js → channel-activity-dT3cYb0e.js} +1 -1
  9. package/dist/{commands-registry-BUyiA7nE.js → commands-registry-CyLMCPuP.js} +1 -1
  10. package/dist/compact.runtime-DGRl4st4.js +39 -0
  11. package/dist/{deliver-aHOaRbkt.js → deliver-B6eTtXSk.js} +4 -4
  12. package/dist/{deliver-runtime-D4bCsr6d.js → deliver-runtime-CLDpY6AW.js} +7 -7
  13. package/dist/{deps-send-discord.runtime-BziKU-pE.js → deps-send-discord.runtime-CxADlame.js} +7 -7
  14. package/dist/{deps-send-imessage.runtime-CFRnDTqp.js → deps-send-imessage.runtime-Wi79xm6H.js} +7 -7
  15. package/dist/{deps-send-signal.runtime-BuOtABJm.js → deps-send-signal.runtime-BDtzvsnR.js} +6 -6
  16. package/dist/{deps-send-slack.runtime-BOLqvMxW.js → deps-send-slack.runtime-CgX24hgT.js} +5 -5
  17. package/dist/{deps-send-telegram.runtime-DeEoFLv5.js → deps-send-telegram.runtime-CEWc7ePn.js} +6 -6
  18. package/dist/deps-send-whatsapp.runtime-B1KJ7YOp.js +43 -0
  19. package/dist/{diagnostic-BdcXX9iJ.js → diagnostic-BZmAxdu9.js} +1 -1
  20. package/dist/{fetch-D9NUULbj.js → fetch-CMLoICyN.js} +2 -2
  21. package/dist/{fetch-guard-B5ZMnGaN.js → fetch-guard-DCj3k042.js} +1 -1
  22. package/dist/{image-4x07m4Jl.js → image-Bt49ybRv.js} +2 -2
  23. package/dist/{image-runtime-smkMrIol.js → image-runtime-Cilhq73U.js} +2 -2
  24. package/dist/{ir-CsgNUpOU.js → ir-CVtBjUiL.js} +2 -2
  25. package/dist/llm-slug-generator.js +23 -23
  26. package/dist/{login-p_O59TVQ.js → login-D0fUoX-p.js} +2 -2
  27. package/dist/{login-qr-BCJpDsAy.js → login-qr-ClBxstxZ.js} +2 -2
  28. package/dist/{manager-CwYv8O3T.js → manager-DSfEj66R.js} +3 -3
  29. package/dist/{manager-runtime-D_jEoBr9.js → manager-runtime-BrZlGJsj.js} +4 -4
  30. package/dist/{model-selection-Cv2Puf5z.js → model-selection-CMEj8bpy.js} +11 -11
  31. package/dist/{outbound-Chpiwybe.js → outbound-BxIJyMzV.js} +4 -4
  32. package/dist/{outbound-attachment-BnAVJDLe.js → outbound-attachment-CVJwpypG.js} +2 -2
  33. package/dist/{pi-embedded-CJVNBk0y.js → pi-embedded-CHNPEUAv.js} +57 -57
  34. package/dist/{pi-model-discovery-7IzK0Uc3.js → pi-model-discovery-D-r5y7kV.js} +1 -1
  35. package/dist/{pi-model-discovery-runtime-DABef3qy.js → pi-model-discovery-runtime-DZQXYmdu.js} +2 -2
  36. package/dist/{pi-tools.before-tool-call.runtime-BP2UvGJb.js → pi-tools.before-tool-call.runtime-DagGpfw0.js} +2 -2
  37. package/dist/plugin-sdk/active-listener-CN-tMEvN.js +35 -0
  38. package/dist/plugin-sdk/api-key-rotation-CimGYMBc.js +176 -0
  39. package/dist/plugin-sdk/audio-preflight-C-xXBoE2.js +51 -0
  40. package/dist/plugin-sdk/audio-transcription-runner-CTIHpebA.js +2173 -0
  41. package/dist/plugin-sdk/audit-membership-runtime-BFatB2LJ.js +58 -0
  42. package/dist/plugin-sdk/channel-activity-DO0FEzyj.js +95 -0
  43. package/dist/plugin-sdk/channel-web-Da-__nUF.js +2238 -0
  44. package/dist/plugin-sdk/commands-registry-6no2NNrY.js +1118 -0
  45. package/dist/plugin-sdk/compact.runtime-CCoclu5e.js +35 -0
  46. package/dist/plugin-sdk/config-B9ODwgpz.js +37426 -0
  47. package/dist/plugin-sdk/deliver-B1fFpKjV.js +1757 -0
  48. package/dist/plugin-sdk/deliver-runtime-DB-VRMe1.js +15 -0
  49. package/dist/plugin-sdk/deps-send-discord.runtime-DklqycYG.js +15 -0
  50. package/dist/plugin-sdk/deps-send-imessage.runtime-Chs8zeon.js +14 -0
  51. package/dist/plugin-sdk/deps-send-signal.runtime-clW9aSJP.js +13 -0
  52. package/dist/plugin-sdk/deps-send-slack.runtime-BUx0LYY1.js +13 -0
  53. package/dist/plugin-sdk/deps-send-telegram.runtime-LECSHgMG.js +16 -0
  54. package/dist/plugin-sdk/deps-send-whatsapp.runtime-D2d65fw0.js +40 -0
  55. package/dist/plugin-sdk/diagnostic-CxIvS-C2.js +315 -0
  56. package/dist/plugin-sdk/dispatch-BqlR4dPx.js +105863 -0
  57. package/dist/plugin-sdk/env-b9k1PHMI.js +34 -0
  58. package/dist/plugin-sdk/fetch-PoxzAANT.js +326 -0
  59. package/dist/plugin-sdk/fetch-guard-4UVSZ0uS.js +164 -0
  60. package/dist/plugin-sdk/image-Ch6M4tnJ.js +2420 -0
  61. package/dist/plugin-sdk/image-runtime-CSh2o5wY.js +8 -0
  62. package/dist/plugin-sdk/index.js +35 -35
  63. package/dist/plugin-sdk/ir-CugsqGH8.js +1312 -0
  64. package/dist/plugin-sdk/local-roots-adnEg9zb.js +217 -0
  65. package/dist/plugin-sdk/logger-D6zRubj0.js +1164 -0
  66. package/dist/plugin-sdk/login-CYvkQ0At.js +54 -0
  67. package/dist/plugin-sdk/login-qr-ll4NtaT5.js +316 -0
  68. package/dist/plugin-sdk/manager-CHy8IclH.js +3959 -0
  69. package/dist/plugin-sdk/manager-runtime-C70EkEr7.js +11 -0
  70. package/dist/plugin-sdk/outbound-Wzs2iN7X.js +216 -0
  71. package/dist/plugin-sdk/outbound-attachment-khXJwucX.js +17 -0
  72. package/dist/plugin-sdk/paths-BtVqCdw4.js +3063 -0
  73. package/dist/plugin-sdk/pi-model-discovery-Dh4ziodY.js +131 -0
  74. package/dist/plugin-sdk/pi-model-discovery-runtime-b83Xe-HT.js +8 -0
  75. package/dist/plugin-sdk/pi-tools.before-tool-call.runtime-C1z5CDBF.js +349 -0
  76. package/dist/plugin-sdk/proxy-fetch-CJEmoBxi.js +54 -0
  77. package/dist/plugin-sdk/pw-ai-Dj3Cvlzl.js +1990 -0
  78. package/dist/plugin-sdk/qmd-manager-egHUAseQ.js +1581 -0
  79. package/dist/plugin-sdk/resolve-outbound-target-BiICvIKs.js +38 -0
  80. package/dist/plugin-sdk/runtime-whatsapp-login.runtime-DNApufzW.js +9 -0
  81. package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-CBmtfIQ8.js +13 -0
  82. package/dist/plugin-sdk/send-CScblaI4.js +532 -0
  83. package/dist/plugin-sdk/send-CeHhnld6.js +407 -0
  84. package/dist/plugin-sdk/send-DP_c8JfR.js +3277 -0
  85. package/dist/plugin-sdk/send-Dc5fI6e8.js +495 -0
  86. package/dist/plugin-sdk/send-l-77_s1_.js +2507 -0
  87. package/dist/plugin-sdk/session-CkOKZaqa.js +166 -0
  88. package/dist/plugin-sdk/signal.js +2 -2
  89. package/dist/plugin-sdk/skill-commands-BohYCgkq.js +336 -0
  90. package/dist/plugin-sdk/slash-commands.runtime-DpLfVTM6.js +8 -0
  91. package/dist/plugin-sdk/slash-dispatch.runtime-CASMHwpm.js +35 -0
  92. package/dist/plugin-sdk/slash-skill-commands.runtime-D7rrJEci.js +9 -0
  93. package/dist/plugin-sdk/sqlite-CJE3X7Mv.js +1005 -0
  94. package/dist/plugin-sdk/subagent-registry-runtime-B1oo5bih.js +35 -0
  95. package/dist/plugin-sdk/tables-D5VgpTmm.js +53 -0
  96. package/dist/plugin-sdk/target-errors-C6zZ_OpA.js +191 -0
  97. package/dist/plugin-sdk/tokens-DUnJnpMS.js +50 -0
  98. package/dist/plugin-sdk/web-TfUM1nSi.js +39 -0
  99. package/dist/plugin-sdk/whatsapp-actions-DuWJ0j1r.js +71 -0
  100. package/dist/{pw-ai-DwH5GpEO.js → pw-ai-CoIUdns_.js} +1 -1
  101. package/dist/{runtime-whatsapp-login.runtime-BI3U306v.js → runtime-whatsapp-login.runtime-ChqE9BkX.js} +3 -3
  102. package/dist/{runtime-whatsapp-outbound.runtime-Bsc2uD09.js → runtime-whatsapp-outbound.runtime-yiy6jzKk.js} +6 -6
  103. package/dist/{send-DtBvCnPQ.js → send-4rRrSKp9.js} +4 -4
  104. package/dist/{send-ORtn50qg.js → send-BKO1-P1t.js} +3 -3
  105. package/dist/{send-C-Q_WPMf.js → send-EDBPXjTT.js} +3 -3
  106. package/dist/{send-DUibfNQD.js → send-K2mAG7KC.js} +4 -4
  107. package/dist/{send-BDnOgWIp.js → send-V1MRV7QF.js} +3 -3
  108. package/dist/{session-B7imi6T5.js → session-CuVCho2m.js} +1 -1
  109. package/dist/{skill-commands-B9brPuiL.js → skill-commands-B55LOaMB.js} +2 -2
  110. package/dist/{slash-commands.runtime-Cf6ygfBp.js → slash-commands.runtime-BchS0VkW.js} +2 -2
  111. package/dist/{slash-dispatch.runtime-CsmvhO5K.js → slash-dispatch.runtime-BIKRY3fr.js} +23 -23
  112. package/dist/{slash-skill-commands.runtime-CX7stIEP.js → slash-skill-commands.runtime-BP4jBHU9.js} +3 -3
  113. package/dist/{subagent-registry-runtime-B_S1nf7y.js → subagent-registry-runtime-DjEYzSyM.js} +23 -23
  114. package/dist/{tables-CjQqTOdD.js → tables-BAGqh2XD.js} +1 -1
  115. package/dist/{target-errors-BZE1mc-W.js → target-errors-CeBF8Pws.js} +1 -1
  116. package/dist/{web-Cd8yK1Zq.js → web-BRSmQdtm.js} +27 -27
  117. package/dist/{whatsapp-actions-CYEzUMBI.js → whatsapp-actions-Dxb2K2Xh.js} +7 -7
  118. package/extensions/mfa-auth/index.ts +22 -13
  119. package/extensions/mfa-auth/src/auth-manager.ts +4 -0
  120. package/package.json +1 -1
  121. package/dist/compact.runtime-DpcZpcTl.js +0 -39
  122. package/dist/deps-send-whatsapp.runtime-CG1uXYLY.js +0 -43
@@ -0,0 +1,3063 @@
1
+ import { c as resolveStateDir, d as resolveRequiredHomeDir, l as expandHomePrefix } from "./paths-eFexkPEh.js";
2
+ import { B as shouldLogVerbose, D as resolveUserPath, I as danger, Y as resolveNodeRequireFromMeta, a as createSubsystemLogger, i as logWarn, n as logError, t as logDebug, w as pathExists$1 } from "./logger-D6zRubj0.js";
3
+ import fs, { constants, readFileSync, statSync } from "node:fs";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+ import fs$1 from "node:fs/promises";
7
+ import { randomUUID } from "node:crypto";
8
+ import { promisify } from "node:util";
9
+ import { execFile, spawn } from "node:child_process";
10
+ import process$1 from "node:process";
11
+ import { fileURLToPath } from "node:url";
12
+ import { pipeline } from "node:stream/promises";
13
+ //#region src/sessions/session-key-utils.ts
14
+ /**
15
+ * Parse agent-scoped session keys in a canonical, case-insensitive way.
16
+ * Returned values are normalized to lowercase for stable comparisons/routing.
17
+ */
18
+ function parseAgentSessionKey(sessionKey) {
19
+ const raw = (sessionKey ?? "").trim().toLowerCase();
20
+ if (!raw) return null;
21
+ const parts = raw.split(":").filter(Boolean);
22
+ if (parts.length < 3) return null;
23
+ if (parts[0] !== "agent") return null;
24
+ const agentId = parts[1]?.trim();
25
+ const rest = parts.slice(2).join(":");
26
+ if (!agentId || !rest) return null;
27
+ return {
28
+ agentId,
29
+ rest
30
+ };
31
+ }
32
+ /**
33
+ * Best-effort chat-type extraction from session keys across canonical and legacy formats.
34
+ */
35
+ function deriveSessionChatType(sessionKey) {
36
+ const raw = (sessionKey ?? "").trim().toLowerCase();
37
+ if (!raw) return "unknown";
38
+ const scoped = parseAgentSessionKey(raw)?.rest ?? raw;
39
+ const tokens = new Set(scoped.split(":").filter(Boolean));
40
+ if (tokens.has("group")) return "group";
41
+ if (tokens.has("channel")) return "channel";
42
+ if (tokens.has("direct") || tokens.has("dm")) return "direct";
43
+ if (/^discord:(?:[^:]+:)?guild-[^:]+:channel-[^:]+$/.test(scoped)) return "channel";
44
+ return "unknown";
45
+ }
46
+ function isCronSessionKey(sessionKey) {
47
+ const parsed = parseAgentSessionKey(sessionKey);
48
+ if (!parsed) return false;
49
+ return parsed.rest.toLowerCase().startsWith("cron:");
50
+ }
51
+ function isSubagentSessionKey(sessionKey) {
52
+ const raw = (sessionKey ?? "").trim();
53
+ if (!raw) return false;
54
+ if (raw.toLowerCase().startsWith("subagent:")) return true;
55
+ const parsed = parseAgentSessionKey(raw);
56
+ return Boolean((parsed?.rest ?? "").toLowerCase().startsWith("subagent:"));
57
+ }
58
+ function getSubagentDepth(sessionKey) {
59
+ const raw = (sessionKey ?? "").trim().toLowerCase();
60
+ if (!raw) return 0;
61
+ return raw.split(":subagent:").length - 1;
62
+ }
63
+ function isAcpSessionKey(sessionKey) {
64
+ const raw = (sessionKey ?? "").trim();
65
+ if (!raw) return false;
66
+ if (raw.toLowerCase().startsWith("acp:")) return true;
67
+ const parsed = parseAgentSessionKey(raw);
68
+ return Boolean((parsed?.rest ?? "").toLowerCase().startsWith("acp:"));
69
+ }
70
+ const THREAD_SESSION_MARKERS = [":thread:", ":topic:"];
71
+ function resolveThreadParentSessionKey(sessionKey) {
72
+ const raw = (sessionKey ?? "").trim();
73
+ if (!raw) return null;
74
+ const normalized = raw.toLowerCase();
75
+ let idx = -1;
76
+ for (const marker of THREAD_SESSION_MARKERS) {
77
+ const candidate = normalized.lastIndexOf(marker);
78
+ if (candidate > idx) idx = candidate;
79
+ }
80
+ if (idx <= 0) return null;
81
+ const parent = raw.slice(0, idx).trim();
82
+ return parent ? parent : null;
83
+ }
84
+ //#endregion
85
+ //#region src/infra/prototype-keys.ts
86
+ const BLOCKED_OBJECT_KEYS = new Set([
87
+ "__proto__",
88
+ "prototype",
89
+ "constructor"
90
+ ]);
91
+ function isBlockedObjectKey(key) {
92
+ return BLOCKED_OBJECT_KEYS.has(key);
93
+ }
94
+ //#endregion
95
+ //#region src/routing/account-id.ts
96
+ const DEFAULT_ACCOUNT_ID = "default";
97
+ const VALID_ID_RE$1 = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
98
+ const INVALID_CHARS_RE$1 = /[^a-z0-9_-]+/g;
99
+ const LEADING_DASH_RE$1 = /^-+/;
100
+ const TRAILING_DASH_RE$1 = /-+$/;
101
+ const ACCOUNT_ID_CACHE_MAX = 512;
102
+ const normalizeAccountIdCache = /* @__PURE__ */ new Map();
103
+ const normalizeOptionalAccountIdCache = /* @__PURE__ */ new Map();
104
+ function canonicalizeAccountId(value) {
105
+ if (VALID_ID_RE$1.test(value)) return value.toLowerCase();
106
+ return value.toLowerCase().replace(INVALID_CHARS_RE$1, "-").replace(LEADING_DASH_RE$1, "").replace(TRAILING_DASH_RE$1, "").slice(0, 64);
107
+ }
108
+ function normalizeCanonicalAccountId(value) {
109
+ const canonical = canonicalizeAccountId(value);
110
+ if (!canonical || isBlockedObjectKey(canonical)) return;
111
+ return canonical;
112
+ }
113
+ function normalizeAccountId(value) {
114
+ const trimmed = (value ?? "").trim();
115
+ if (!trimmed) return DEFAULT_ACCOUNT_ID;
116
+ const cached = normalizeAccountIdCache.get(trimmed);
117
+ if (cached) return cached;
118
+ const normalized = normalizeCanonicalAccountId(trimmed) || "default";
119
+ setNormalizeCache(normalizeAccountIdCache, trimmed, normalized);
120
+ return normalized;
121
+ }
122
+ function normalizeOptionalAccountId(value) {
123
+ const trimmed = (value ?? "").trim();
124
+ if (!trimmed) return;
125
+ if (normalizeOptionalAccountIdCache.has(trimmed)) return normalizeOptionalAccountIdCache.get(trimmed);
126
+ const normalized = normalizeCanonicalAccountId(trimmed) || void 0;
127
+ setNormalizeCache(normalizeOptionalAccountIdCache, trimmed, normalized);
128
+ return normalized;
129
+ }
130
+ function setNormalizeCache(cache, key, value) {
131
+ cache.set(key, value);
132
+ if (cache.size <= ACCOUNT_ID_CACHE_MAX) return;
133
+ const oldest = cache.keys().next();
134
+ if (!oldest.done) cache.delete(oldest.value);
135
+ }
136
+ //#endregion
137
+ //#region src/routing/session-key.ts
138
+ const DEFAULT_AGENT_ID = "main";
139
+ const DEFAULT_MAIN_KEY = "main";
140
+ const VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
141
+ const INVALID_CHARS_RE = /[^a-z0-9_-]+/g;
142
+ const LEADING_DASH_RE = /^-+/;
143
+ const TRAILING_DASH_RE = /-+$/;
144
+ function normalizeToken(value) {
145
+ return (value ?? "").trim().toLowerCase();
146
+ }
147
+ function scopedHeartbeatWakeOptions(sessionKey, wakeOptions) {
148
+ return parseAgentSessionKey(sessionKey) ? {
149
+ ...wakeOptions,
150
+ sessionKey
151
+ } : wakeOptions;
152
+ }
153
+ function normalizeMainKey(value) {
154
+ const trimmed = (value ?? "").trim();
155
+ return trimmed ? trimmed.toLowerCase() : DEFAULT_MAIN_KEY;
156
+ }
157
+ function resolveAgentIdFromSessionKey(sessionKey) {
158
+ return normalizeAgentId(parseAgentSessionKey(sessionKey)?.agentId ?? "main");
159
+ }
160
+ function classifySessionKeyShape(sessionKey) {
161
+ const raw = (sessionKey ?? "").trim();
162
+ if (!raw) return "missing";
163
+ if (parseAgentSessionKey(raw)) return "agent";
164
+ return raw.toLowerCase().startsWith("agent:") ? "malformed_agent" : "legacy_or_alias";
165
+ }
166
+ function normalizeAgentId(value) {
167
+ const trimmed = (value ?? "").trim();
168
+ if (!trimmed) return DEFAULT_AGENT_ID;
169
+ if (VALID_ID_RE.test(trimmed)) return trimmed.toLowerCase();
170
+ return trimmed.toLowerCase().replace(INVALID_CHARS_RE, "-").replace(LEADING_DASH_RE, "").replace(TRAILING_DASH_RE, "").slice(0, 64) || "main";
171
+ }
172
+ function isValidAgentId(value) {
173
+ const trimmed = (value ?? "").trim();
174
+ return Boolean(trimmed) && VALID_ID_RE.test(trimmed);
175
+ }
176
+ function sanitizeAgentId(value) {
177
+ return normalizeAgentId(value);
178
+ }
179
+ function buildAgentMainSessionKey(params) {
180
+ return `agent:${normalizeAgentId(params.agentId)}:${normalizeMainKey(params.mainKey)}`;
181
+ }
182
+ function buildAgentPeerSessionKey(params) {
183
+ const peerKind = params.peerKind ?? "direct";
184
+ if (peerKind === "direct") {
185
+ const dmScope = params.dmScope ?? "main";
186
+ let peerId = (params.peerId ?? "").trim();
187
+ const linkedPeerId = dmScope === "main" ? null : resolveLinkedPeerId({
188
+ identityLinks: params.identityLinks,
189
+ channel: params.channel,
190
+ peerId
191
+ });
192
+ if (linkedPeerId) peerId = linkedPeerId;
193
+ peerId = peerId.toLowerCase();
194
+ if (dmScope === "per-account-channel-peer" && peerId) {
195
+ const channel = (params.channel ?? "").trim().toLowerCase() || "unknown";
196
+ const accountId = normalizeAccountId(params.accountId);
197
+ return `agent:${normalizeAgentId(params.agentId)}:${channel}:${accountId}:direct:${peerId}`;
198
+ }
199
+ if (dmScope === "per-channel-peer" && peerId) {
200
+ const channel = (params.channel ?? "").trim().toLowerCase() || "unknown";
201
+ return `agent:${normalizeAgentId(params.agentId)}:${channel}:direct:${peerId}`;
202
+ }
203
+ if (dmScope === "per-peer" && peerId) return `agent:${normalizeAgentId(params.agentId)}:direct:${peerId}`;
204
+ return buildAgentMainSessionKey({
205
+ agentId: params.agentId,
206
+ mainKey: params.mainKey
207
+ });
208
+ }
209
+ const channel = (params.channel ?? "").trim().toLowerCase() || "unknown";
210
+ const peerId = ((params.peerId ?? "").trim() || "unknown").toLowerCase();
211
+ return `agent:${normalizeAgentId(params.agentId)}:${channel}:${peerKind}:${peerId}`;
212
+ }
213
+ function resolveLinkedPeerId(params) {
214
+ const identityLinks = params.identityLinks;
215
+ if (!identityLinks) return null;
216
+ const peerId = params.peerId.trim();
217
+ if (!peerId) return null;
218
+ const candidates = /* @__PURE__ */ new Set();
219
+ const rawCandidate = normalizeToken(peerId);
220
+ if (rawCandidate) candidates.add(rawCandidate);
221
+ const channel = normalizeToken(params.channel);
222
+ if (channel) {
223
+ const scopedCandidate = normalizeToken(`${channel}:${peerId}`);
224
+ if (scopedCandidate) candidates.add(scopedCandidate);
225
+ }
226
+ if (candidates.size === 0) return null;
227
+ for (const [canonical, ids] of Object.entries(identityLinks)) {
228
+ const canonicalName = canonical.trim();
229
+ if (!canonicalName) continue;
230
+ if (!Array.isArray(ids)) continue;
231
+ for (const id of ids) {
232
+ const normalized = normalizeToken(id);
233
+ if (normalized && candidates.has(normalized)) return canonicalName;
234
+ }
235
+ }
236
+ return null;
237
+ }
238
+ function buildGroupHistoryKey(params) {
239
+ const channel = normalizeToken(params.channel) || "unknown";
240
+ const accountId = normalizeAccountId(params.accountId);
241
+ const peerId = params.peerId.trim().toLowerCase() || "unknown";
242
+ return `${channel}:${accountId}:${params.peerKind}:${peerId}`;
243
+ }
244
+ function resolveThreadSessionKeys(params) {
245
+ const threadId = (params.threadId ?? "").trim();
246
+ if (!threadId) return {
247
+ sessionKey: params.baseSessionKey,
248
+ parentSessionKey: void 0
249
+ };
250
+ const normalizedThreadId = (params.normalizeThreadId ?? ((value) => value.toLowerCase()))(threadId);
251
+ return {
252
+ sessionKey: params.useSuffix ?? true ? `${params.baseSessionKey}:thread:${normalizedThreadId}` : params.baseSessionKey,
253
+ parentSessionKey: params.parentSessionKey
254
+ };
255
+ }
256
+ //#endregion
257
+ //#region src/infra/openclaw-exec-env.ts
258
+ const OPENCLAW_CLI_ENV_VAR = "OPENCLAW_CLI";
259
+ function markOpenClawExecEnv(env) {
260
+ return {
261
+ ...env,
262
+ [OPENCLAW_CLI_ENV_VAR]: "1"
263
+ };
264
+ }
265
+ //#endregion
266
+ //#region src/config/model-input.ts
267
+ function resolveAgentModelPrimaryValue(model) {
268
+ if (typeof model === "string") return model.trim() || void 0;
269
+ if (!model || typeof model !== "object") return;
270
+ return model.primary?.trim() || void 0;
271
+ }
272
+ function resolveAgentModelFallbackValues(model) {
273
+ if (!model || typeof model !== "object") return [];
274
+ return Array.isArray(model.fallbacks) ? model.fallbacks : [];
275
+ }
276
+ function toAgentModelListLike(model) {
277
+ if (typeof model === "string") {
278
+ const primary = model.trim();
279
+ return primary ? { primary } : void 0;
280
+ }
281
+ if (!model || typeof model !== "object") return;
282
+ return model;
283
+ }
284
+ //#endregion
285
+ //#region src/shared/string-normalization.ts
286
+ function normalizeStringEntries(list) {
287
+ return (list ?? []).map((entry) => String(entry).trim()).filter(Boolean);
288
+ }
289
+ function normalizeStringEntriesLower(list) {
290
+ return normalizeStringEntries(list).map((entry) => entry.toLowerCase());
291
+ }
292
+ function normalizeHyphenSlug(raw) {
293
+ const trimmed = raw?.trim().toLowerCase() ?? "";
294
+ if (!trimmed) return "";
295
+ return trimmed.replace(/\s+/g, "-").replace(/[^a-z0-9#@._+-]+/g, "-").replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
296
+ }
297
+ function normalizeAtHashSlug(raw) {
298
+ const trimmed = raw?.trim().toLowerCase() ?? "";
299
+ if (!trimmed) return "";
300
+ return trimmed.replace(/^[@#]+/, "").replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]+/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
301
+ }
302
+ //#endregion
303
+ //#region src/agents/skills/filter.ts
304
+ function normalizeSkillFilter(skillFilter) {
305
+ if (skillFilter === void 0) return;
306
+ return normalizeStringEntries(skillFilter);
307
+ }
308
+ //#endregion
309
+ //#region src/infra/path-guards.ts
310
+ const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
311
+ const SYMLINK_OPEN_CODES = new Set([
312
+ "ELOOP",
313
+ "EINVAL",
314
+ "ENOTSUP"
315
+ ]);
316
+ function normalizeWindowsPathForComparison(input) {
317
+ let normalized = path.win32.normalize(input);
318
+ if (normalized.startsWith("\\\\?\\")) {
319
+ normalized = normalized.slice(4);
320
+ if (normalized.toUpperCase().startsWith("UNC\\")) normalized = `\\\\${normalized.slice(4)}`;
321
+ }
322
+ return normalized.replaceAll("/", "\\").toLowerCase();
323
+ }
324
+ function isNodeError(value) {
325
+ return Boolean(value && typeof value === "object" && "code" in value);
326
+ }
327
+ function hasNodeErrorCode(value, code) {
328
+ return isNodeError(value) && value.code === code;
329
+ }
330
+ function isNotFoundPathError(value) {
331
+ return isNodeError(value) && typeof value.code === "string" && NOT_FOUND_CODES.has(value.code);
332
+ }
333
+ function isSymlinkOpenError(value) {
334
+ return isNodeError(value) && typeof value.code === "string" && SYMLINK_OPEN_CODES.has(value.code);
335
+ }
336
+ function isPathInside(root, target) {
337
+ const resolvedRoot = path.resolve(root);
338
+ const resolvedTarget = path.resolve(target);
339
+ if (process.platform === "win32") {
340
+ const rootForCompare = normalizeWindowsPathForComparison(resolvedRoot);
341
+ const targetForCompare = normalizeWindowsPathForComparison(resolvedTarget);
342
+ const relative = path.win32.relative(rootForCompare, targetForCompare);
343
+ return relative === "" || !relative.startsWith("..") && !path.win32.isAbsolute(relative);
344
+ }
345
+ const relative = path.relative(resolvedRoot, resolvedTarget);
346
+ return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
347
+ }
348
+ //#endregion
349
+ //#region src/infra/boundary-path.ts
350
+ const BOUNDARY_PATH_ALIAS_POLICIES = {
351
+ strict: Object.freeze({
352
+ allowFinalSymlinkForUnlink: false,
353
+ allowFinalHardlinkForUnlink: false
354
+ }),
355
+ unlinkTarget: Object.freeze({
356
+ allowFinalSymlinkForUnlink: true,
357
+ allowFinalHardlinkForUnlink: true
358
+ })
359
+ };
360
+ async function resolveBoundaryPath(params) {
361
+ const rootPath = path.resolve(params.rootPath);
362
+ const absolutePath = path.resolve(params.absolutePath);
363
+ const context = createBoundaryResolutionContext({
364
+ resolveParams: params,
365
+ rootPath,
366
+ absolutePath,
367
+ rootCanonicalPath: params.rootCanonicalPath ? path.resolve(params.rootCanonicalPath) : await resolvePathViaExistingAncestor(rootPath),
368
+ outsideLexicalCanonicalPath: await resolveOutsideLexicalCanonicalPathAsync({
369
+ rootPath,
370
+ absolutePath
371
+ })
372
+ });
373
+ const outsideResult = await resolveOutsideBoundaryPathAsync({
374
+ boundaryLabel: params.boundaryLabel,
375
+ context
376
+ });
377
+ if (outsideResult) return outsideResult;
378
+ return resolveBoundaryPathLexicalAsync({
379
+ params,
380
+ absolutePath: context.absolutePath,
381
+ rootPath: context.rootPath,
382
+ rootCanonicalPath: context.rootCanonicalPath
383
+ });
384
+ }
385
+ function resolveBoundaryPathSync(params) {
386
+ const rootPath = path.resolve(params.rootPath);
387
+ const absolutePath = path.resolve(params.absolutePath);
388
+ const context = createBoundaryResolutionContext({
389
+ resolveParams: params,
390
+ rootPath,
391
+ absolutePath,
392
+ rootCanonicalPath: params.rootCanonicalPath ? path.resolve(params.rootCanonicalPath) : resolvePathViaExistingAncestorSync(rootPath),
393
+ outsideLexicalCanonicalPath: resolveOutsideLexicalCanonicalPathSync({
394
+ rootPath,
395
+ absolutePath
396
+ })
397
+ });
398
+ const outsideResult = resolveOutsideBoundaryPathSync({
399
+ boundaryLabel: params.boundaryLabel,
400
+ context
401
+ });
402
+ if (outsideResult) return outsideResult;
403
+ return resolveBoundaryPathLexicalSync({
404
+ params,
405
+ absolutePath: context.absolutePath,
406
+ rootPath: context.rootPath,
407
+ rootCanonicalPath: context.rootCanonicalPath
408
+ });
409
+ }
410
+ function isPromiseLike(value) {
411
+ return Boolean(value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function");
412
+ }
413
+ function createLexicalTraversalState(params) {
414
+ return {
415
+ segments: path.relative(params.rootPath, params.absolutePath).split(path.sep).filter(Boolean),
416
+ allowFinalSymlink: params.params.policy?.allowFinalSymlinkForUnlink === true,
417
+ canonicalCursor: params.rootCanonicalPath,
418
+ lexicalCursor: params.rootPath,
419
+ preserveFinalSymlink: false
420
+ };
421
+ }
422
+ function assertLexicalCursorInsideBoundary(params) {
423
+ assertInsideBoundary({
424
+ boundaryLabel: params.params.boundaryLabel,
425
+ rootCanonicalPath: params.rootCanonicalPath,
426
+ candidatePath: params.candidatePath,
427
+ absolutePath: params.absolutePath
428
+ });
429
+ }
430
+ function applyMissingSuffixToCanonicalCursor(params) {
431
+ const missingSuffix = params.state.segments.slice(params.missingFromIndex);
432
+ params.state.canonicalCursor = path.resolve(params.state.canonicalCursor, ...missingSuffix);
433
+ assertLexicalCursorInsideBoundary({
434
+ params: params.params,
435
+ rootCanonicalPath: params.rootCanonicalPath,
436
+ candidatePath: params.state.canonicalCursor,
437
+ absolutePath: params.absolutePath
438
+ });
439
+ }
440
+ function advanceCanonicalCursorForSegment(params) {
441
+ params.state.canonicalCursor = path.resolve(params.state.canonicalCursor, params.segment);
442
+ assertLexicalCursorInsideBoundary({
443
+ params: params.params,
444
+ rootCanonicalPath: params.rootCanonicalPath,
445
+ candidatePath: params.state.canonicalCursor,
446
+ absolutePath: params.absolutePath
447
+ });
448
+ }
449
+ function finalizeLexicalResolution(params) {
450
+ assertLexicalCursorInsideBoundary({
451
+ params: params.params,
452
+ rootCanonicalPath: params.rootCanonicalPath,
453
+ candidatePath: params.state.canonicalCursor,
454
+ absolutePath: params.absolutePath
455
+ });
456
+ return buildResolvedBoundaryPath({
457
+ absolutePath: params.absolutePath,
458
+ canonicalPath: params.state.canonicalCursor,
459
+ rootPath: params.rootPath,
460
+ rootCanonicalPath: params.rootCanonicalPath,
461
+ kind: params.kind
462
+ });
463
+ }
464
+ function handleLexicalLstatFailure(params) {
465
+ if (!isNotFoundPathError(params.error)) return false;
466
+ applyMissingSuffixToCanonicalCursor({
467
+ state: params.state,
468
+ missingFromIndex: params.missingFromIndex,
469
+ rootCanonicalPath: params.rootCanonicalPath,
470
+ params: params.resolveParams,
471
+ absolutePath: params.absolutePath
472
+ });
473
+ return true;
474
+ }
475
+ function handleLexicalStatReadFailure(params) {
476
+ if (handleLexicalLstatFailure({
477
+ error: params.error,
478
+ state: params.state,
479
+ missingFromIndex: params.missingFromIndex,
480
+ rootCanonicalPath: params.rootCanonicalPath,
481
+ resolveParams: params.resolveParams,
482
+ absolutePath: params.absolutePath
483
+ })) return null;
484
+ throw params.error;
485
+ }
486
+ function handleLexicalStatDisposition(params) {
487
+ if (!params.isSymbolicLink) {
488
+ advanceCanonicalCursorForSegment({
489
+ state: params.state,
490
+ segment: params.segment,
491
+ rootCanonicalPath: params.rootCanonicalPath,
492
+ params: params.resolveParams,
493
+ absolutePath: params.absolutePath
494
+ });
495
+ return "continue";
496
+ }
497
+ if (params.state.allowFinalSymlink && params.isLast) {
498
+ params.state.preserveFinalSymlink = true;
499
+ advanceCanonicalCursorForSegment({
500
+ state: params.state,
501
+ segment: params.segment,
502
+ rootCanonicalPath: params.rootCanonicalPath,
503
+ params: params.resolveParams,
504
+ absolutePath: params.absolutePath
505
+ });
506
+ return "break";
507
+ }
508
+ return "resolve-link";
509
+ }
510
+ function applyResolvedSymlinkHop(params) {
511
+ if (!isPathInside(params.rootCanonicalPath, params.linkCanonical)) throw symlinkEscapeError({
512
+ boundaryLabel: params.boundaryLabel,
513
+ rootCanonicalPath: params.rootCanonicalPath,
514
+ symlinkPath: params.state.lexicalCursor
515
+ });
516
+ params.state.canonicalCursor = params.linkCanonical;
517
+ params.state.lexicalCursor = params.linkCanonical;
518
+ }
519
+ function readLexicalStat(params) {
520
+ try {
521
+ const stat = params.read(params.state.lexicalCursor);
522
+ if (isPromiseLike(stat)) return Promise.resolve(stat).catch((error) => handleLexicalStatReadFailure({
523
+ ...params,
524
+ error
525
+ }));
526
+ return stat;
527
+ } catch (error) {
528
+ return handleLexicalStatReadFailure({
529
+ ...params,
530
+ error
531
+ });
532
+ }
533
+ }
534
+ function resolveAndApplySymlinkHop(params) {
535
+ const linkCanonical = params.resolveLinkCanonical(params.state.lexicalCursor);
536
+ if (isPromiseLike(linkCanonical)) return Promise.resolve(linkCanonical).then((value) => applyResolvedSymlinkHop({
537
+ state: params.state,
538
+ linkCanonical: value,
539
+ rootCanonicalPath: params.rootCanonicalPath,
540
+ boundaryLabel: params.boundaryLabel
541
+ }));
542
+ applyResolvedSymlinkHop({
543
+ state: params.state,
544
+ linkCanonical,
545
+ rootCanonicalPath: params.rootCanonicalPath,
546
+ boundaryLabel: params.boundaryLabel
547
+ });
548
+ }
549
+ function* iterateLexicalTraversal(state) {
550
+ for (let idx = 0; idx < state.segments.length; idx += 1) {
551
+ const segment = state.segments[idx] ?? "";
552
+ const isLast = idx === state.segments.length - 1;
553
+ state.lexicalCursor = path.join(state.lexicalCursor, segment);
554
+ yield {
555
+ idx,
556
+ segment,
557
+ isLast
558
+ };
559
+ }
560
+ }
561
+ async function resolveBoundaryPathLexicalAsync(params) {
562
+ const state = createLexicalTraversalState(params);
563
+ const sharedStepParams = {
564
+ state,
565
+ rootCanonicalPath: params.rootCanonicalPath,
566
+ resolveParams: params.params,
567
+ absolutePath: params.absolutePath
568
+ };
569
+ for (const { idx, segment, isLast } of iterateLexicalTraversal(state)) {
570
+ const stat = await readLexicalStat({
571
+ ...sharedStepParams,
572
+ missingFromIndex: idx,
573
+ read: (cursor) => fs$1.lstat(cursor)
574
+ });
575
+ if (!stat) break;
576
+ const disposition = handleLexicalStatDisposition({
577
+ ...sharedStepParams,
578
+ isSymbolicLink: stat.isSymbolicLink(),
579
+ segment,
580
+ isLast
581
+ });
582
+ if (disposition === "continue") continue;
583
+ if (disposition === "break") break;
584
+ await resolveAndApplySymlinkHop({
585
+ state,
586
+ rootCanonicalPath: params.rootCanonicalPath,
587
+ boundaryLabel: params.params.boundaryLabel,
588
+ resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor)
589
+ });
590
+ }
591
+ const kind = await getPathKind(params.absolutePath, state.preserveFinalSymlink);
592
+ return finalizeLexicalResolution({
593
+ ...params,
594
+ state,
595
+ kind
596
+ });
597
+ }
598
+ function resolveBoundaryPathLexicalSync(params) {
599
+ const state = createLexicalTraversalState(params);
600
+ for (let idx = 0; idx < state.segments.length; idx += 1) {
601
+ const segment = state.segments[idx] ?? "";
602
+ const isLast = idx === state.segments.length - 1;
603
+ state.lexicalCursor = path.join(state.lexicalCursor, segment);
604
+ const maybeStat = readLexicalStat({
605
+ state,
606
+ missingFromIndex: idx,
607
+ rootCanonicalPath: params.rootCanonicalPath,
608
+ resolveParams: params.params,
609
+ absolutePath: params.absolutePath,
610
+ read: (cursor) => fs.lstatSync(cursor)
611
+ });
612
+ if (isPromiseLike(maybeStat)) throw new Error("Unexpected async lexical stat");
613
+ const stat = maybeStat;
614
+ if (!stat) break;
615
+ const disposition = handleLexicalStatDisposition({
616
+ state,
617
+ isSymbolicLink: stat.isSymbolicLink(),
618
+ segment,
619
+ isLast,
620
+ rootCanonicalPath: params.rootCanonicalPath,
621
+ resolveParams: params.params,
622
+ absolutePath: params.absolutePath
623
+ });
624
+ if (disposition === "continue") continue;
625
+ if (disposition === "break") break;
626
+ if (isPromiseLike(resolveAndApplySymlinkHop({
627
+ state,
628
+ rootCanonicalPath: params.rootCanonicalPath,
629
+ boundaryLabel: params.params.boundaryLabel,
630
+ resolveLinkCanonical: (cursor) => resolveSymlinkHopPathSync(cursor)
631
+ }))) throw new Error("Unexpected async symlink resolution");
632
+ }
633
+ const kind = getPathKindSync(params.absolutePath, state.preserveFinalSymlink);
634
+ return finalizeLexicalResolution({
635
+ ...params,
636
+ state,
637
+ kind
638
+ });
639
+ }
640
+ function resolveCanonicalOutsideLexicalPath(params) {
641
+ return params.outsideLexicalCanonicalPath ?? params.absolutePath;
642
+ }
643
+ function createBoundaryResolutionContext(params) {
644
+ const lexicalInside = isPathInside(params.rootPath, params.absolutePath);
645
+ const canonicalOutsideLexicalPath = resolveCanonicalOutsideLexicalPath({
646
+ absolutePath: params.absolutePath,
647
+ outsideLexicalCanonicalPath: params.outsideLexicalCanonicalPath
648
+ });
649
+ assertLexicalBoundaryOrCanonicalAlias({
650
+ skipLexicalRootCheck: params.resolveParams.skipLexicalRootCheck,
651
+ lexicalInside,
652
+ canonicalOutsideLexicalPath,
653
+ rootCanonicalPath: params.rootCanonicalPath,
654
+ boundaryLabel: params.resolveParams.boundaryLabel,
655
+ rootPath: params.rootPath,
656
+ absolutePath: params.absolutePath
657
+ });
658
+ return {
659
+ rootPath: params.rootPath,
660
+ absolutePath: params.absolutePath,
661
+ rootCanonicalPath: params.rootCanonicalPath,
662
+ lexicalInside,
663
+ canonicalOutsideLexicalPath
664
+ };
665
+ }
666
+ async function resolveOutsideBoundaryPathAsync(params) {
667
+ if (params.context.lexicalInside) return null;
668
+ const kind = await getPathKind(params.context.absolutePath, false);
669
+ return buildOutsideBoundaryPathFromContext({
670
+ boundaryLabel: params.boundaryLabel,
671
+ context: params.context,
672
+ kind
673
+ });
674
+ }
675
+ function resolveOutsideBoundaryPathSync(params) {
676
+ if (params.context.lexicalInside) return null;
677
+ const kind = getPathKindSync(params.context.absolutePath, false);
678
+ return buildOutsideBoundaryPathFromContext({
679
+ boundaryLabel: params.boundaryLabel,
680
+ context: params.context,
681
+ kind
682
+ });
683
+ }
684
+ function buildOutsideBoundaryPathFromContext(params) {
685
+ return buildOutsideLexicalBoundaryPath({
686
+ boundaryLabel: params.boundaryLabel,
687
+ rootCanonicalPath: params.context.rootCanonicalPath,
688
+ absolutePath: params.context.absolutePath,
689
+ canonicalOutsideLexicalPath: params.context.canonicalOutsideLexicalPath,
690
+ rootPath: params.context.rootPath,
691
+ kind: params.kind
692
+ });
693
+ }
694
+ async function resolveOutsideLexicalCanonicalPathAsync(params) {
695
+ if (isPathInside(params.rootPath, params.absolutePath)) return;
696
+ return await resolvePathViaExistingAncestor(params.absolutePath);
697
+ }
698
+ function resolveOutsideLexicalCanonicalPathSync(params) {
699
+ if (isPathInside(params.rootPath, params.absolutePath)) return;
700
+ return resolvePathViaExistingAncestorSync(params.absolutePath);
701
+ }
702
+ function buildOutsideLexicalBoundaryPath(params) {
703
+ assertInsideBoundary({
704
+ boundaryLabel: params.boundaryLabel,
705
+ rootCanonicalPath: params.rootCanonicalPath,
706
+ candidatePath: params.canonicalOutsideLexicalPath,
707
+ absolutePath: params.absolutePath
708
+ });
709
+ return buildResolvedBoundaryPath({
710
+ absolutePath: params.absolutePath,
711
+ canonicalPath: params.canonicalOutsideLexicalPath,
712
+ rootPath: params.rootPath,
713
+ rootCanonicalPath: params.rootCanonicalPath,
714
+ kind: params.kind
715
+ });
716
+ }
717
+ function assertLexicalBoundaryOrCanonicalAlias(params) {
718
+ if (params.skipLexicalRootCheck || params.lexicalInside) return;
719
+ if (isPathInside(params.rootCanonicalPath, params.canonicalOutsideLexicalPath)) return;
720
+ throw pathEscapeError({
721
+ boundaryLabel: params.boundaryLabel,
722
+ rootPath: params.rootPath,
723
+ absolutePath: params.absolutePath
724
+ });
725
+ }
726
+ function buildResolvedBoundaryPath(params) {
727
+ return {
728
+ absolutePath: params.absolutePath,
729
+ canonicalPath: params.canonicalPath,
730
+ rootPath: params.rootPath,
731
+ rootCanonicalPath: params.rootCanonicalPath,
732
+ relativePath: relativeInsideRoot(params.rootCanonicalPath, params.canonicalPath),
733
+ exists: params.kind.exists,
734
+ kind: params.kind.kind
735
+ };
736
+ }
737
+ async function resolvePathViaExistingAncestor(targetPath) {
738
+ const normalized = path.resolve(targetPath);
739
+ let cursor = normalized;
740
+ const missingSuffix = [];
741
+ while (!isFilesystemRoot(cursor) && !await pathExists(cursor)) {
742
+ missingSuffix.unshift(path.basename(cursor));
743
+ const parent = path.dirname(cursor);
744
+ if (parent === cursor) break;
745
+ cursor = parent;
746
+ }
747
+ if (!await pathExists(cursor)) return normalized;
748
+ try {
749
+ const resolvedAncestor = path.resolve(await fs$1.realpath(cursor));
750
+ if (missingSuffix.length === 0) return resolvedAncestor;
751
+ return path.resolve(resolvedAncestor, ...missingSuffix);
752
+ } catch {
753
+ return normalized;
754
+ }
755
+ }
756
+ function resolvePathViaExistingAncestorSync(targetPath) {
757
+ const normalized = path.resolve(targetPath);
758
+ let cursor = normalized;
759
+ const missingSuffix = [];
760
+ while (!isFilesystemRoot(cursor) && !fs.existsSync(cursor)) {
761
+ missingSuffix.unshift(path.basename(cursor));
762
+ const parent = path.dirname(cursor);
763
+ if (parent === cursor) break;
764
+ cursor = parent;
765
+ }
766
+ if (!fs.existsSync(cursor)) return normalized;
767
+ try {
768
+ const resolvedAncestor = path.resolve(fs.realpathSync(cursor));
769
+ if (missingSuffix.length === 0) return resolvedAncestor;
770
+ return path.resolve(resolvedAncestor, ...missingSuffix);
771
+ } catch {
772
+ return normalized;
773
+ }
774
+ }
775
+ async function getPathKind(absolutePath, preserveFinalSymlink) {
776
+ try {
777
+ return {
778
+ exists: true,
779
+ kind: toResolvedKind(preserveFinalSymlink ? await fs$1.lstat(absolutePath) : await fs$1.stat(absolutePath))
780
+ };
781
+ } catch (error) {
782
+ if (isNotFoundPathError(error)) return {
783
+ exists: false,
784
+ kind: "missing"
785
+ };
786
+ throw error;
787
+ }
788
+ }
789
+ function getPathKindSync(absolutePath, preserveFinalSymlink) {
790
+ try {
791
+ return {
792
+ exists: true,
793
+ kind: toResolvedKind(preserveFinalSymlink ? fs.lstatSync(absolutePath) : fs.statSync(absolutePath))
794
+ };
795
+ } catch (error) {
796
+ if (isNotFoundPathError(error)) return {
797
+ exists: false,
798
+ kind: "missing"
799
+ };
800
+ throw error;
801
+ }
802
+ }
803
+ function toResolvedKind(stat) {
804
+ if (stat.isFile()) return "file";
805
+ if (stat.isDirectory()) return "directory";
806
+ if (stat.isSymbolicLink()) return "symlink";
807
+ return "other";
808
+ }
809
+ function relativeInsideRoot(rootPath, targetPath) {
810
+ const relative = path.relative(path.resolve(rootPath), path.resolve(targetPath));
811
+ if (!relative || relative === ".") return "";
812
+ if (relative.startsWith("..") || path.isAbsolute(relative)) return "";
813
+ return relative;
814
+ }
815
+ function assertInsideBoundary(params) {
816
+ if (isPathInside(params.rootCanonicalPath, params.candidatePath)) return;
817
+ throw new Error(`Path resolves outside ${params.boundaryLabel} (${shortPath$1(params.rootCanonicalPath)}): ${shortPath$1(params.absolutePath)}`);
818
+ }
819
+ function pathEscapeError(params) {
820
+ return /* @__PURE__ */ new Error(`Path escapes ${params.boundaryLabel} (${shortPath$1(params.rootPath)}): ${shortPath$1(params.absolutePath)}`);
821
+ }
822
+ function symlinkEscapeError(params) {
823
+ return /* @__PURE__ */ new Error(`Symlink escapes ${params.boundaryLabel} (${shortPath$1(params.rootCanonicalPath)}): ${shortPath$1(params.symlinkPath)}`);
824
+ }
825
+ function shortPath$1(value) {
826
+ const home = os.homedir();
827
+ if (value.startsWith(home)) return `~${value.slice(home.length)}`;
828
+ return value;
829
+ }
830
+ function isFilesystemRoot(candidate) {
831
+ return path.parse(candidate).root === candidate;
832
+ }
833
+ async function pathExists(targetPath) {
834
+ try {
835
+ await fs$1.lstat(targetPath);
836
+ return true;
837
+ } catch (error) {
838
+ if (isNotFoundPathError(error)) return false;
839
+ throw error;
840
+ }
841
+ }
842
+ async function resolveSymlinkHopPath(symlinkPath) {
843
+ try {
844
+ return path.resolve(await fs$1.realpath(symlinkPath));
845
+ } catch (error) {
846
+ if (!isNotFoundPathError(error)) throw error;
847
+ const linkTarget = await fs$1.readlink(symlinkPath);
848
+ return resolvePathViaExistingAncestor(path.resolve(path.dirname(symlinkPath), linkTarget));
849
+ }
850
+ }
851
+ function resolveSymlinkHopPathSync(symlinkPath) {
852
+ try {
853
+ return path.resolve(fs.realpathSync(symlinkPath));
854
+ } catch (error) {
855
+ if (!isNotFoundPathError(error)) throw error;
856
+ const linkTarget = fs.readlinkSync(symlinkPath);
857
+ return resolvePathViaExistingAncestorSync(path.resolve(path.dirname(symlinkPath), linkTarget));
858
+ }
859
+ }
860
+ //#endregion
861
+ //#region src/infra/file-identity.ts
862
+ function isZero(value) {
863
+ return value === 0 || value === 0n;
864
+ }
865
+ function sameFileIdentity$1(left, right, platform = process.platform) {
866
+ if (left.ino !== right.ino) return false;
867
+ if (left.dev === right.dev) return true;
868
+ return platform === "win32" && (isZero(left.dev) || isZero(right.dev));
869
+ }
870
+ //#endregion
871
+ //#region src/infra/safe-open-sync.ts
872
+ function isExpectedPathError(error) {
873
+ const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
874
+ return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
875
+ }
876
+ function sameFileIdentity(left, right) {
877
+ return sameFileIdentity$1(left, right);
878
+ }
879
+ function openVerifiedFileSync(params) {
880
+ const ioFs = params.ioFs ?? fs;
881
+ const allowedType = params.allowedType ?? "file";
882
+ const openReadFlags = ioFs.constants.O_RDONLY | (typeof ioFs.constants.O_NOFOLLOW === "number" ? ioFs.constants.O_NOFOLLOW : 0);
883
+ let fd = null;
884
+ try {
885
+ if (params.rejectPathSymlink) {
886
+ if (ioFs.lstatSync(params.filePath).isSymbolicLink()) return {
887
+ ok: false,
888
+ reason: "validation"
889
+ };
890
+ }
891
+ const realPath = params.resolvedPath ?? ioFs.realpathSync(params.filePath);
892
+ const preOpenStat = ioFs.lstatSync(realPath);
893
+ if (!isAllowedType(preOpenStat, allowedType)) return {
894
+ ok: false,
895
+ reason: "validation"
896
+ };
897
+ if (params.rejectHardlinks && preOpenStat.isFile() && preOpenStat.nlink > 1) return {
898
+ ok: false,
899
+ reason: "validation"
900
+ };
901
+ if (params.maxBytes !== void 0 && preOpenStat.isFile() && preOpenStat.size > params.maxBytes) return {
902
+ ok: false,
903
+ reason: "validation"
904
+ };
905
+ fd = ioFs.openSync(realPath, openReadFlags);
906
+ const openedStat = ioFs.fstatSync(fd);
907
+ if (!isAllowedType(openedStat, allowedType)) return {
908
+ ok: false,
909
+ reason: "validation"
910
+ };
911
+ if (params.rejectHardlinks && openedStat.isFile() && openedStat.nlink > 1) return {
912
+ ok: false,
913
+ reason: "validation"
914
+ };
915
+ if (params.maxBytes !== void 0 && openedStat.isFile() && openedStat.size > params.maxBytes) return {
916
+ ok: false,
917
+ reason: "validation"
918
+ };
919
+ if (!sameFileIdentity(preOpenStat, openedStat)) return {
920
+ ok: false,
921
+ reason: "validation"
922
+ };
923
+ const opened = {
924
+ ok: true,
925
+ path: realPath,
926
+ fd,
927
+ stat: openedStat
928
+ };
929
+ fd = null;
930
+ return opened;
931
+ } catch (error) {
932
+ if (isExpectedPathError(error)) return {
933
+ ok: false,
934
+ reason: "path",
935
+ error
936
+ };
937
+ return {
938
+ ok: false,
939
+ reason: "io",
940
+ error
941
+ };
942
+ } finally {
943
+ if (fd !== null) ioFs.closeSync(fd);
944
+ }
945
+ }
946
+ function isAllowedType(stat, allowedType) {
947
+ if (allowedType === "directory") return stat.isDirectory();
948
+ return stat.isFile();
949
+ }
950
+ //#endregion
951
+ //#region src/infra/boundary-file-read.ts
952
+ function canUseBoundaryFileOpen(ioFs) {
953
+ return typeof ioFs.openSync === "function" && typeof ioFs.closeSync === "function" && typeof ioFs.fstatSync === "function" && typeof ioFs.lstatSync === "function" && typeof ioFs.realpathSync === "function" && typeof ioFs.readFileSync === "function" && typeof ioFs.constants === "object" && ioFs.constants !== null;
954
+ }
955
+ function openBoundaryFileSync(params) {
956
+ const ioFs = params.ioFs ?? fs;
957
+ const resolved = resolveBoundaryFilePathGeneric({
958
+ absolutePath: params.absolutePath,
959
+ resolve: (absolutePath) => resolveBoundaryPathSync({
960
+ absolutePath,
961
+ rootPath: params.rootPath,
962
+ rootCanonicalPath: params.rootRealPath,
963
+ boundaryLabel: params.boundaryLabel,
964
+ skipLexicalRootCheck: params.skipLexicalRootCheck
965
+ })
966
+ });
967
+ if (resolved instanceof Promise) return toBoundaryValidationError(/* @__PURE__ */ new Error("Unexpected async boundary resolution"));
968
+ return finalizeBoundaryFileOpen({
969
+ resolved,
970
+ maxBytes: params.maxBytes,
971
+ rejectHardlinks: params.rejectHardlinks,
972
+ allowedType: params.allowedType,
973
+ ioFs
974
+ });
975
+ }
976
+ function openBoundaryFileResolved(params) {
977
+ const opened = openVerifiedFileSync({
978
+ filePath: params.absolutePath,
979
+ resolvedPath: params.resolvedPath,
980
+ rejectHardlinks: params.rejectHardlinks ?? true,
981
+ maxBytes: params.maxBytes,
982
+ allowedType: params.allowedType,
983
+ ioFs: params.ioFs
984
+ });
985
+ if (!opened.ok) return opened;
986
+ return {
987
+ ok: true,
988
+ path: opened.path,
989
+ fd: opened.fd,
990
+ stat: opened.stat,
991
+ rootRealPath: params.rootRealPath
992
+ };
993
+ }
994
+ function finalizeBoundaryFileOpen(params) {
995
+ if ("ok" in params.resolved) return params.resolved;
996
+ return openBoundaryFileResolved({
997
+ absolutePath: params.resolved.absolutePath,
998
+ resolvedPath: params.resolved.resolvedPath,
999
+ rootRealPath: params.resolved.rootRealPath,
1000
+ maxBytes: params.maxBytes,
1001
+ rejectHardlinks: params.rejectHardlinks,
1002
+ allowedType: params.allowedType,
1003
+ ioFs: params.ioFs
1004
+ });
1005
+ }
1006
+ async function openBoundaryFile(params) {
1007
+ const ioFs = params.ioFs ?? fs;
1008
+ const maybeResolved = resolveBoundaryFilePathGeneric({
1009
+ absolutePath: params.absolutePath,
1010
+ resolve: (absolutePath) => resolveBoundaryPath({
1011
+ absolutePath,
1012
+ rootPath: params.rootPath,
1013
+ rootCanonicalPath: params.rootRealPath,
1014
+ boundaryLabel: params.boundaryLabel,
1015
+ policy: params.aliasPolicy,
1016
+ skipLexicalRootCheck: params.skipLexicalRootCheck
1017
+ })
1018
+ });
1019
+ return finalizeBoundaryFileOpen({
1020
+ resolved: maybeResolved instanceof Promise ? await maybeResolved : maybeResolved,
1021
+ maxBytes: params.maxBytes,
1022
+ rejectHardlinks: params.rejectHardlinks,
1023
+ allowedType: params.allowedType,
1024
+ ioFs
1025
+ });
1026
+ }
1027
+ function toBoundaryValidationError(error) {
1028
+ return {
1029
+ ok: false,
1030
+ reason: "validation",
1031
+ error
1032
+ };
1033
+ }
1034
+ function mapResolvedBoundaryPath(absolutePath, resolved) {
1035
+ return {
1036
+ absolutePath,
1037
+ resolvedPath: resolved.canonicalPath,
1038
+ rootRealPath: resolved.rootCanonicalPath
1039
+ };
1040
+ }
1041
+ function resolveBoundaryFilePathGeneric(params) {
1042
+ const absolutePath = path.resolve(params.absolutePath);
1043
+ try {
1044
+ const resolved = params.resolve(absolutePath);
1045
+ if (resolved instanceof Promise) return resolved.then((value) => mapResolvedBoundaryPath(absolutePath, value)).catch((error) => toBoundaryValidationError(error));
1046
+ return mapResolvedBoundaryPath(absolutePath, resolved);
1047
+ } catch (error) {
1048
+ return toBoundaryValidationError(error);
1049
+ }
1050
+ }
1051
+ //#endregion
1052
+ //#region src/process/spawn-utils.ts
1053
+ const DEFAULT_RETRY_CODES = ["EBADF"];
1054
+ function resolveCommandStdio(params) {
1055
+ return [
1056
+ params.hasInput ? "pipe" : params.preferInherit ? "inherit" : "pipe",
1057
+ "pipe",
1058
+ "pipe"
1059
+ ];
1060
+ }
1061
+ function shouldRetry(err, codes) {
1062
+ const code = err && typeof err === "object" && "code" in err ? String(err.code) : "";
1063
+ return code.length > 0 && codes.includes(code);
1064
+ }
1065
+ async function spawnAndWaitForSpawn(spawnImpl, argv, options) {
1066
+ const child = spawnImpl(argv[0], argv.slice(1), options);
1067
+ return await new Promise((resolve, reject) => {
1068
+ let settled = false;
1069
+ const cleanup = () => {
1070
+ child.removeListener("error", onError);
1071
+ child.removeListener("spawn", onSpawn);
1072
+ };
1073
+ const finishResolve = () => {
1074
+ if (settled) return;
1075
+ settled = true;
1076
+ cleanup();
1077
+ resolve(child);
1078
+ };
1079
+ const onError = (err) => {
1080
+ if (settled) return;
1081
+ settled = true;
1082
+ cleanup();
1083
+ reject(err);
1084
+ };
1085
+ const onSpawn = () => {
1086
+ finishResolve();
1087
+ };
1088
+ child.once("error", onError);
1089
+ child.once("spawn", onSpawn);
1090
+ process.nextTick(() => {
1091
+ if (typeof child.pid === "number") finishResolve();
1092
+ });
1093
+ });
1094
+ }
1095
+ async function spawnWithFallback(params) {
1096
+ const spawnImpl = params.spawnImpl ?? spawn;
1097
+ const retryCodes = params.retryCodes ?? DEFAULT_RETRY_CODES;
1098
+ const baseOptions = { ...params.options };
1099
+ const fallbacks = params.fallbacks ?? [];
1100
+ const attempts = [{ options: baseOptions }, ...fallbacks.map((fallback) => ({
1101
+ label: fallback.label,
1102
+ options: {
1103
+ ...baseOptions,
1104
+ ...fallback.options
1105
+ }
1106
+ }))];
1107
+ let lastError;
1108
+ for (let index = 0; index < attempts.length; index += 1) {
1109
+ const attempt = attempts[index];
1110
+ try {
1111
+ return {
1112
+ child: await spawnAndWaitForSpawn(spawnImpl, params.argv, attempt.options),
1113
+ usedFallback: index > 0,
1114
+ fallbackLabel: attempt.label
1115
+ };
1116
+ } catch (err) {
1117
+ lastError = err;
1118
+ const nextFallback = fallbacks[index];
1119
+ if (!nextFallback || !shouldRetry(err, retryCodes)) throw err;
1120
+ params.onFallback?.(err, nextFallback);
1121
+ }
1122
+ }
1123
+ throw lastError;
1124
+ }
1125
+ //#endregion
1126
+ //#region src/process/exec.ts
1127
+ const execFileAsync = promisify(execFile);
1128
+ const WINDOWS_UNSAFE_CMD_CHARS_RE = /[&|<>^%\r\n]/;
1129
+ function isWindowsBatchCommand(resolvedCommand) {
1130
+ if (process$1.platform !== "win32") return false;
1131
+ const ext = path.extname(resolvedCommand).toLowerCase();
1132
+ return ext === ".cmd" || ext === ".bat";
1133
+ }
1134
+ function escapeForCmdExe(arg) {
1135
+ if (WINDOWS_UNSAFE_CMD_CHARS_RE.test(arg)) throw new Error(`Unsafe Windows cmd.exe argument detected: ${JSON.stringify(arg)}. Pass an explicit shell-wrapper argv at the call site instead.`);
1136
+ if (!arg.includes(" ") && !arg.includes("\"")) return arg;
1137
+ return `"${arg.replace(/"/g, "\"\"")}"`;
1138
+ }
1139
+ function buildCmdExeCommandLine(resolvedCommand, args) {
1140
+ return [escapeForCmdExe(resolvedCommand), ...args.map(escapeForCmdExe)].join(" ");
1141
+ }
1142
+ /**
1143
+ * On Windows, Node 18.20.2+ (CVE-2024-27980) rejects spawning .cmd/.bat directly
1144
+ * without shell, causing EINVAL. Resolve npm/npx to node + cli script so we
1145
+ * spawn node.exe instead of npm.cmd.
1146
+ */
1147
+ function resolveNpmArgvForWindows(argv) {
1148
+ if (process$1.platform !== "win32" || argv.length === 0) return null;
1149
+ const basename = path.basename(argv[0]).toLowerCase().replace(/\.(cmd|exe|bat)$/, "");
1150
+ const cliName = basename === "npx" ? "npx-cli.js" : basename === "npm" ? "npm-cli.js" : null;
1151
+ if (!cliName) return null;
1152
+ const nodeDir = path.dirname(process$1.execPath);
1153
+ const cliPath = path.join(nodeDir, "node_modules", "npm", "bin", cliName);
1154
+ if (!fs.existsSync(cliPath)) {
1155
+ const command = argv[0] ?? "";
1156
+ return [path.extname(command).toLowerCase() ? command : `${command}.cmd`, ...argv.slice(1)];
1157
+ }
1158
+ return [
1159
+ process$1.execPath,
1160
+ cliPath,
1161
+ ...argv.slice(1)
1162
+ ];
1163
+ }
1164
+ /**
1165
+ * Resolves a command for Windows compatibility.
1166
+ * On Windows, non-.exe commands (like pnpm, yarn) are resolved to .cmd; npm/npx
1167
+ * are handled by resolveNpmArgvForWindows to avoid spawn EINVAL (no direct .cmd).
1168
+ */
1169
+ function resolveCommand(command) {
1170
+ if (process$1.platform !== "win32") return command;
1171
+ const basename = path.basename(command).toLowerCase();
1172
+ if (path.extname(basename)) return command;
1173
+ if (["pnpm", "yarn"].includes(basename)) return `${command}.cmd`;
1174
+ return command;
1175
+ }
1176
+ function shouldSpawnWithShell(params) {
1177
+ return false;
1178
+ }
1179
+ async function runExec(command, args, opts = 1e4) {
1180
+ const options = typeof opts === "number" ? {
1181
+ timeout: opts,
1182
+ encoding: "utf8"
1183
+ } : {
1184
+ timeout: opts.timeoutMs,
1185
+ maxBuffer: opts.maxBuffer,
1186
+ cwd: opts.cwd,
1187
+ encoding: "utf8"
1188
+ };
1189
+ try {
1190
+ const argv = [command, ...args];
1191
+ let execCommand;
1192
+ let execArgs;
1193
+ if (process$1.platform === "win32") {
1194
+ const resolved = resolveNpmArgvForWindows(argv);
1195
+ if (resolved) {
1196
+ execCommand = resolved[0] ?? "";
1197
+ execArgs = resolved.slice(1);
1198
+ } else {
1199
+ execCommand = resolveCommand(command);
1200
+ execArgs = args;
1201
+ }
1202
+ } else {
1203
+ execCommand = resolveCommand(command);
1204
+ execArgs = args;
1205
+ }
1206
+ const { stdout, stderr } = isWindowsBatchCommand(execCommand) ? await execFileAsync(process$1.env.ComSpec ?? "cmd.exe", [
1207
+ "/d",
1208
+ "/s",
1209
+ "/c",
1210
+ buildCmdExeCommandLine(execCommand, execArgs)
1211
+ ], {
1212
+ ...options,
1213
+ windowsVerbatimArguments: true
1214
+ }) : await execFileAsync(execCommand, execArgs, options);
1215
+ if (shouldLogVerbose()) {
1216
+ if (stdout.trim()) logDebug(stdout.trim());
1217
+ if (stderr.trim()) logError(stderr.trim());
1218
+ }
1219
+ return {
1220
+ stdout,
1221
+ stderr
1222
+ };
1223
+ } catch (err) {
1224
+ if (shouldLogVerbose()) logError(danger(`Command failed: ${command} ${args.join(" ")}`));
1225
+ throw err;
1226
+ }
1227
+ }
1228
+ function resolveCommandEnv(params) {
1229
+ const baseEnv = params.baseEnv ?? process$1.env;
1230
+ const argv = params.argv;
1231
+ const shouldSuppressNpmFund = (() => {
1232
+ const cmd = path.basename(argv[0] ?? "");
1233
+ if (cmd === "npm" || cmd === "npm.cmd" || cmd === "npm.exe") return true;
1234
+ if (cmd === "node" || cmd === "node.exe") return (argv[1] ?? "").includes("npm-cli.js");
1235
+ return false;
1236
+ })();
1237
+ const mergedEnv = params.env ? {
1238
+ ...baseEnv,
1239
+ ...params.env
1240
+ } : { ...baseEnv };
1241
+ const resolvedEnv = Object.fromEntries(Object.entries(mergedEnv).filter(([, value]) => value !== void 0).map(([key, value]) => [key, String(value)]));
1242
+ if (shouldSuppressNpmFund) {
1243
+ if (resolvedEnv.NPM_CONFIG_FUND == null) resolvedEnv.NPM_CONFIG_FUND = "false";
1244
+ if (resolvedEnv.npm_config_fund == null) resolvedEnv.npm_config_fund = "false";
1245
+ }
1246
+ return markOpenClawExecEnv(resolvedEnv);
1247
+ }
1248
+ async function runCommandWithTimeout(argv, optionsOrTimeout) {
1249
+ const options = typeof optionsOrTimeout === "number" ? { timeoutMs: optionsOrTimeout } : optionsOrTimeout;
1250
+ const { timeoutMs, cwd, input, env, noOutputTimeoutMs } = options;
1251
+ const { windowsVerbatimArguments } = options;
1252
+ const hasInput = input !== void 0;
1253
+ const resolvedEnv = resolveCommandEnv({
1254
+ argv,
1255
+ env
1256
+ });
1257
+ const stdio = resolveCommandStdio({
1258
+ hasInput,
1259
+ preferInherit: true
1260
+ });
1261
+ const finalArgv = process$1.platform === "win32" ? resolveNpmArgvForWindows(argv) ?? argv : argv;
1262
+ const resolvedCommand = finalArgv !== argv ? finalArgv[0] ?? "" : resolveCommand(argv[0] ?? "");
1263
+ const useCmdWrapper = isWindowsBatchCommand(resolvedCommand);
1264
+ const child = spawn(useCmdWrapper ? process$1.env.ComSpec ?? "cmd.exe" : resolvedCommand, useCmdWrapper ? [
1265
+ "/d",
1266
+ "/s",
1267
+ "/c",
1268
+ buildCmdExeCommandLine(resolvedCommand, finalArgv.slice(1))
1269
+ ] : finalArgv.slice(1), {
1270
+ stdio,
1271
+ cwd,
1272
+ env: resolvedEnv,
1273
+ windowsVerbatimArguments: useCmdWrapper ? true : windowsVerbatimArguments,
1274
+ ...shouldSpawnWithShell({
1275
+ resolvedCommand,
1276
+ platform: process$1.platform
1277
+ }) ? { shell: true } : {}
1278
+ });
1279
+ return await new Promise((resolve, reject) => {
1280
+ let stdout = "";
1281
+ let stderr = "";
1282
+ let settled = false;
1283
+ let timedOut = false;
1284
+ let noOutputTimedOut = false;
1285
+ let noOutputTimer = null;
1286
+ const shouldTrackOutputTimeout = typeof noOutputTimeoutMs === "number" && Number.isFinite(noOutputTimeoutMs) && noOutputTimeoutMs > 0;
1287
+ const clearNoOutputTimer = () => {
1288
+ if (!noOutputTimer) return;
1289
+ clearTimeout(noOutputTimer);
1290
+ noOutputTimer = null;
1291
+ };
1292
+ const armNoOutputTimer = () => {
1293
+ if (!shouldTrackOutputTimeout || settled) return;
1294
+ clearNoOutputTimer();
1295
+ noOutputTimer = setTimeout(() => {
1296
+ if (settled) return;
1297
+ noOutputTimedOut = true;
1298
+ if (typeof child.kill === "function") child.kill("SIGKILL");
1299
+ }, Math.floor(noOutputTimeoutMs));
1300
+ };
1301
+ const timer = setTimeout(() => {
1302
+ timedOut = true;
1303
+ if (typeof child.kill === "function") child.kill("SIGKILL");
1304
+ }, timeoutMs);
1305
+ armNoOutputTimer();
1306
+ if (hasInput && child.stdin) {
1307
+ child.stdin.write(input ?? "");
1308
+ child.stdin.end();
1309
+ }
1310
+ child.stdout?.on("data", (d) => {
1311
+ stdout += d.toString();
1312
+ armNoOutputTimer();
1313
+ });
1314
+ child.stderr?.on("data", (d) => {
1315
+ stderr += d.toString();
1316
+ armNoOutputTimer();
1317
+ });
1318
+ child.on("error", (err) => {
1319
+ if (settled) return;
1320
+ settled = true;
1321
+ clearTimeout(timer);
1322
+ clearNoOutputTimer();
1323
+ reject(err);
1324
+ });
1325
+ child.on("close", (code, signal) => {
1326
+ if (settled) return;
1327
+ settled = true;
1328
+ clearTimeout(timer);
1329
+ clearNoOutputTimer();
1330
+ const termination = noOutputTimedOut ? "no-output-timeout" : timedOut ? "timeout" : signal != null ? "signal" : "exit";
1331
+ resolve({
1332
+ pid: child.pid ?? void 0,
1333
+ stdout,
1334
+ stderr,
1335
+ code,
1336
+ signal,
1337
+ killed: child.killed,
1338
+ termination,
1339
+ noOutputTimedOut
1340
+ });
1341
+ });
1342
+ });
1343
+ }
1344
+ //#endregion
1345
+ //#region src/infra/openclaw-root.ts
1346
+ const CORE_PACKAGE_NAMES = new Set(["openclaw"]);
1347
+ async function readPackageName(dir) {
1348
+ try {
1349
+ const raw = await fs$1.readFile(path.join(dir, "package.json"), "utf-8");
1350
+ const parsed = JSON.parse(raw);
1351
+ return typeof parsed.name === "string" ? parsed.name : null;
1352
+ } catch {
1353
+ return null;
1354
+ }
1355
+ }
1356
+ function readPackageNameSync(dir) {
1357
+ try {
1358
+ const raw = fs.readFileSync(path.join(dir, "package.json"), "utf-8");
1359
+ const parsed = JSON.parse(raw);
1360
+ return typeof parsed.name === "string" ? parsed.name : null;
1361
+ } catch {
1362
+ return null;
1363
+ }
1364
+ }
1365
+ async function findPackageRoot(startDir, maxDepth = 12) {
1366
+ for (const current of iterAncestorDirs(startDir, maxDepth)) {
1367
+ const name = await readPackageName(current);
1368
+ if (name && CORE_PACKAGE_NAMES.has(name)) return current;
1369
+ }
1370
+ return null;
1371
+ }
1372
+ function findPackageRootSync(startDir, maxDepth = 12) {
1373
+ for (const current of iterAncestorDirs(startDir, maxDepth)) {
1374
+ const name = readPackageNameSync(current);
1375
+ if (name && CORE_PACKAGE_NAMES.has(name)) return current;
1376
+ }
1377
+ return null;
1378
+ }
1379
+ function* iterAncestorDirs(startDir, maxDepth) {
1380
+ let current = path.resolve(startDir);
1381
+ for (let i = 0; i < maxDepth; i += 1) {
1382
+ yield current;
1383
+ const parent = path.dirname(current);
1384
+ if (parent === current) break;
1385
+ current = parent;
1386
+ }
1387
+ }
1388
+ function candidateDirsFromArgv1(argv1) {
1389
+ const normalized = path.resolve(argv1);
1390
+ const candidates = [path.dirname(normalized)];
1391
+ try {
1392
+ const resolved = fs.realpathSync(normalized);
1393
+ if (resolved !== normalized) candidates.push(path.dirname(resolved));
1394
+ } catch {}
1395
+ const parts = normalized.split(path.sep);
1396
+ const binIndex = parts.lastIndexOf(".bin");
1397
+ if (binIndex > 0 && parts[binIndex - 1] === "node_modules") {
1398
+ const binName = path.basename(normalized);
1399
+ const nodeModulesDir = parts.slice(0, binIndex).join(path.sep);
1400
+ candidates.push(path.join(nodeModulesDir, binName));
1401
+ }
1402
+ return candidates;
1403
+ }
1404
+ async function resolveOpenClawPackageRoot(opts) {
1405
+ for (const candidate of buildCandidates(opts)) {
1406
+ const found = await findPackageRoot(candidate);
1407
+ if (found) return found;
1408
+ }
1409
+ return null;
1410
+ }
1411
+ function resolveOpenClawPackageRootSync(opts) {
1412
+ for (const candidate of buildCandidates(opts)) {
1413
+ const found = findPackageRootSync(candidate);
1414
+ if (found) return found;
1415
+ }
1416
+ return null;
1417
+ }
1418
+ function buildCandidates(opts) {
1419
+ const candidates = [];
1420
+ if (opts.moduleUrl) try {
1421
+ candidates.push(path.dirname(fileURLToPath(opts.moduleUrl)));
1422
+ } catch {}
1423
+ if (opts.argv1) candidates.push(...candidateDirsFromArgv1(opts.argv1));
1424
+ if (opts.cwd) candidates.push(opts.cwd);
1425
+ return candidates;
1426
+ }
1427
+ //#endregion
1428
+ //#region src/agents/workspace-templates.ts
1429
+ const FALLBACK_TEMPLATE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../docs/reference/templates");
1430
+ let cachedTemplateDir;
1431
+ let resolvingTemplateDir;
1432
+ async function resolveWorkspaceTemplateDir(opts) {
1433
+ if (cachedTemplateDir) return cachedTemplateDir;
1434
+ if (resolvingTemplateDir) return resolvingTemplateDir;
1435
+ resolvingTemplateDir = (async () => {
1436
+ const moduleUrl = opts?.moduleUrl ?? import.meta.url;
1437
+ const argv1 = opts?.argv1 ?? process.argv[1];
1438
+ const cwd = opts?.cwd ?? process.cwd();
1439
+ const packageRoot = await resolveOpenClawPackageRoot({
1440
+ moduleUrl,
1441
+ argv1,
1442
+ cwd
1443
+ });
1444
+ const candidates = [
1445
+ packageRoot ? path.join(packageRoot, "docs", "reference", "templates") : null,
1446
+ cwd ? path.resolve(cwd, "docs", "reference", "templates") : null,
1447
+ FALLBACK_TEMPLATE_DIR
1448
+ ].filter(Boolean);
1449
+ for (const candidate of candidates) if (await pathExists$1(candidate)) {
1450
+ cachedTemplateDir = candidate;
1451
+ return candidate;
1452
+ }
1453
+ cachedTemplateDir = candidates[0] ?? FALLBACK_TEMPLATE_DIR;
1454
+ return cachedTemplateDir;
1455
+ })();
1456
+ try {
1457
+ return await resolvingTemplateDir;
1458
+ } finally {
1459
+ resolvingTemplateDir = void 0;
1460
+ }
1461
+ }
1462
+ //#endregion
1463
+ //#region src/agents/workspace.ts
1464
+ function resolveDefaultAgentWorkspaceDir(env = process.env, homedir = os.homedir) {
1465
+ const home = resolveRequiredHomeDir(env, homedir);
1466
+ const profile = env.OPENCLAW_PROFILE?.trim();
1467
+ if (profile && profile.toLowerCase() !== "default") return path.join(home, ".openclaw", `workspace-${profile}`);
1468
+ return path.join(home, ".openclaw", "workspace");
1469
+ }
1470
+ const DEFAULT_AGENT_WORKSPACE_DIR = resolveDefaultAgentWorkspaceDir();
1471
+ const DEFAULT_AGENTS_FILENAME = "AGENTS.md";
1472
+ const DEFAULT_SOUL_FILENAME = "SOUL.md";
1473
+ const DEFAULT_TOOLS_FILENAME = "TOOLS.md";
1474
+ const DEFAULT_IDENTITY_FILENAME = "IDENTITY.md";
1475
+ const DEFAULT_USER_FILENAME = "USER.md";
1476
+ const DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md";
1477
+ const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md";
1478
+ const DEFAULT_MEMORY_FILENAME = "MEMORY.md";
1479
+ const DEFAULT_MEMORY_ALT_FILENAME = "memory.md";
1480
+ const WORKSPACE_STATE_DIRNAME = ".openclaw";
1481
+ const WORKSPACE_STATE_FILENAME = "workspace-state.json";
1482
+ const WORKSPACE_STATE_VERSION = 1;
1483
+ const workspaceTemplateCache = /* @__PURE__ */ new Map();
1484
+ let gitAvailabilityPromise = null;
1485
+ const MAX_WORKSPACE_BOOTSTRAP_FILE_BYTES = 2 * 1024 * 1024;
1486
+ const workspaceFileCache = /* @__PURE__ */ new Map();
1487
+ function workspaceFileIdentity(stat, canonicalPath) {
1488
+ return `${canonicalPath}|${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeMs}`;
1489
+ }
1490
+ async function readWorkspaceFileWithGuards(params) {
1491
+ const opened = await openBoundaryFile({
1492
+ absolutePath: params.filePath,
1493
+ rootPath: params.workspaceDir,
1494
+ boundaryLabel: "workspace root",
1495
+ maxBytes: MAX_WORKSPACE_BOOTSTRAP_FILE_BYTES
1496
+ });
1497
+ if (!opened.ok) {
1498
+ workspaceFileCache.delete(params.filePath);
1499
+ return opened;
1500
+ }
1501
+ const identity = workspaceFileIdentity(opened.stat, opened.path);
1502
+ const cached = workspaceFileCache.get(params.filePath);
1503
+ if (cached && cached.identity === identity) {
1504
+ fs.closeSync(opened.fd);
1505
+ return {
1506
+ ok: true,
1507
+ content: cached.content
1508
+ };
1509
+ }
1510
+ try {
1511
+ const content = fs.readFileSync(opened.fd, "utf-8");
1512
+ workspaceFileCache.set(params.filePath, {
1513
+ content,
1514
+ identity
1515
+ });
1516
+ return {
1517
+ ok: true,
1518
+ content
1519
+ };
1520
+ } catch (error) {
1521
+ workspaceFileCache.delete(params.filePath);
1522
+ return {
1523
+ ok: false,
1524
+ reason: "io",
1525
+ error
1526
+ };
1527
+ } finally {
1528
+ fs.closeSync(opened.fd);
1529
+ }
1530
+ }
1531
+ function stripFrontMatter(content) {
1532
+ if (!content.startsWith("---")) return content;
1533
+ const endIndex = content.indexOf("\n---", 3);
1534
+ if (endIndex === -1) return content;
1535
+ const start = endIndex + 4;
1536
+ let trimmed = content.slice(start);
1537
+ trimmed = trimmed.replace(/^\s+/, "");
1538
+ return trimmed;
1539
+ }
1540
+ async function loadTemplate(name) {
1541
+ const cached = workspaceTemplateCache.get(name);
1542
+ if (cached) return cached;
1543
+ const pending = (async () => {
1544
+ const templateDir = await resolveWorkspaceTemplateDir();
1545
+ const templatePath = path.join(templateDir, name);
1546
+ try {
1547
+ return stripFrontMatter(await fs$1.readFile(templatePath, "utf-8"));
1548
+ } catch {
1549
+ throw new Error(`Missing workspace template: ${name} (${templatePath}). Ensure docs/reference/templates are packaged.`);
1550
+ }
1551
+ })();
1552
+ workspaceTemplateCache.set(name, pending);
1553
+ try {
1554
+ return await pending;
1555
+ } catch (error) {
1556
+ workspaceTemplateCache.delete(name);
1557
+ throw error;
1558
+ }
1559
+ }
1560
+ async function writeFileIfMissing(filePath, content) {
1561
+ try {
1562
+ await fs$1.writeFile(filePath, content, {
1563
+ encoding: "utf-8",
1564
+ flag: "wx"
1565
+ });
1566
+ return true;
1567
+ } catch (err) {
1568
+ if (err.code !== "EEXIST") throw err;
1569
+ return false;
1570
+ }
1571
+ }
1572
+ async function fileExists(filePath) {
1573
+ try {
1574
+ await fs$1.access(filePath);
1575
+ return true;
1576
+ } catch {
1577
+ return false;
1578
+ }
1579
+ }
1580
+ function resolveWorkspaceStatePath(dir) {
1581
+ return path.join(dir, WORKSPACE_STATE_DIRNAME, WORKSPACE_STATE_FILENAME);
1582
+ }
1583
+ function parseWorkspaceOnboardingState(raw) {
1584
+ try {
1585
+ const parsed = JSON.parse(raw);
1586
+ if (!parsed || typeof parsed !== "object") return null;
1587
+ return {
1588
+ version: WORKSPACE_STATE_VERSION,
1589
+ bootstrapSeededAt: typeof parsed.bootstrapSeededAt === "string" ? parsed.bootstrapSeededAt : void 0,
1590
+ onboardingCompletedAt: typeof parsed.onboardingCompletedAt === "string" ? parsed.onboardingCompletedAt : void 0
1591
+ };
1592
+ } catch {
1593
+ return null;
1594
+ }
1595
+ }
1596
+ async function readWorkspaceOnboardingState(statePath) {
1597
+ try {
1598
+ return parseWorkspaceOnboardingState(await fs$1.readFile(statePath, "utf-8")) ?? { version: WORKSPACE_STATE_VERSION };
1599
+ } catch (err) {
1600
+ if (err.code !== "ENOENT") throw err;
1601
+ return { version: WORKSPACE_STATE_VERSION };
1602
+ }
1603
+ }
1604
+ async function writeWorkspaceOnboardingState(statePath, state) {
1605
+ await fs$1.mkdir(path.dirname(statePath), { recursive: true });
1606
+ const payload = `${JSON.stringify(state, null, 2)}\n`;
1607
+ const tmpPath = `${statePath}.tmp-${process.pid}-${Date.now().toString(36)}`;
1608
+ try {
1609
+ await fs$1.writeFile(tmpPath, payload, { encoding: "utf-8" });
1610
+ await fs$1.rename(tmpPath, statePath);
1611
+ } catch (err) {
1612
+ await fs$1.unlink(tmpPath).catch(() => {});
1613
+ throw err;
1614
+ }
1615
+ }
1616
+ async function hasGitRepo(dir) {
1617
+ try {
1618
+ await fs$1.stat(path.join(dir, ".git"));
1619
+ return true;
1620
+ } catch {
1621
+ return false;
1622
+ }
1623
+ }
1624
+ async function isGitAvailable() {
1625
+ if (gitAvailabilityPromise) return gitAvailabilityPromise;
1626
+ gitAvailabilityPromise = (async () => {
1627
+ try {
1628
+ return (await runCommandWithTimeout(["git", "--version"], { timeoutMs: 2e3 })).code === 0;
1629
+ } catch {
1630
+ return false;
1631
+ }
1632
+ })();
1633
+ return gitAvailabilityPromise;
1634
+ }
1635
+ async function ensureGitRepo(dir, isBrandNewWorkspace) {
1636
+ if (!isBrandNewWorkspace) return;
1637
+ if (await hasGitRepo(dir)) return;
1638
+ if (!await isGitAvailable()) return;
1639
+ try {
1640
+ await runCommandWithTimeout(["git", "init"], {
1641
+ cwd: dir,
1642
+ timeoutMs: 1e4
1643
+ });
1644
+ } catch {}
1645
+ }
1646
+ async function ensureAgentWorkspace(params) {
1647
+ const dir = resolveUserPath(params?.dir?.trim() ? params.dir.trim() : DEFAULT_AGENT_WORKSPACE_DIR);
1648
+ await fs$1.mkdir(dir, { recursive: true });
1649
+ if (!params?.ensureBootstrapFiles) return { dir };
1650
+ const agentsPath = path.join(dir, DEFAULT_AGENTS_FILENAME);
1651
+ const soulPath = path.join(dir, DEFAULT_SOUL_FILENAME);
1652
+ const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME);
1653
+ const identityPath = path.join(dir, DEFAULT_IDENTITY_FILENAME);
1654
+ const userPath = path.join(dir, DEFAULT_USER_FILENAME);
1655
+ const heartbeatPath = path.join(dir, DEFAULT_HEARTBEAT_FILENAME);
1656
+ const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME);
1657
+ const statePath = resolveWorkspaceStatePath(dir);
1658
+ const isBrandNewWorkspace = await (async () => {
1659
+ const templatePaths = [
1660
+ agentsPath,
1661
+ soulPath,
1662
+ toolsPath,
1663
+ identityPath,
1664
+ userPath,
1665
+ heartbeatPath
1666
+ ];
1667
+ const userContentPaths = [
1668
+ path.join(dir, "memory"),
1669
+ path.join(dir, DEFAULT_MEMORY_FILENAME),
1670
+ path.join(dir, ".git")
1671
+ ];
1672
+ const paths = [...templatePaths, ...userContentPaths];
1673
+ return (await Promise.all(paths.map(async (p) => {
1674
+ try {
1675
+ await fs$1.access(p);
1676
+ return true;
1677
+ } catch {
1678
+ return false;
1679
+ }
1680
+ }))).every((v) => !v);
1681
+ })();
1682
+ const agentsTemplate = await loadTemplate(DEFAULT_AGENTS_FILENAME);
1683
+ const soulTemplate = await loadTemplate(DEFAULT_SOUL_FILENAME);
1684
+ const toolsTemplate = await loadTemplate(DEFAULT_TOOLS_FILENAME);
1685
+ const identityTemplate = await loadTemplate(DEFAULT_IDENTITY_FILENAME);
1686
+ const userTemplate = await loadTemplate(DEFAULT_USER_FILENAME);
1687
+ const heartbeatTemplate = await loadTemplate(DEFAULT_HEARTBEAT_FILENAME);
1688
+ await writeFileIfMissing(agentsPath, agentsTemplate);
1689
+ await writeFileIfMissing(soulPath, soulTemplate);
1690
+ await writeFileIfMissing(toolsPath, toolsTemplate);
1691
+ await writeFileIfMissing(identityPath, identityTemplate);
1692
+ await writeFileIfMissing(userPath, userTemplate);
1693
+ await writeFileIfMissing(heartbeatPath, heartbeatTemplate);
1694
+ let state = await readWorkspaceOnboardingState(statePath);
1695
+ let stateDirty = false;
1696
+ const markState = (next) => {
1697
+ state = {
1698
+ ...state,
1699
+ ...next
1700
+ };
1701
+ stateDirty = true;
1702
+ };
1703
+ const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
1704
+ let bootstrapExists = await fileExists(bootstrapPath);
1705
+ if (!state.bootstrapSeededAt && bootstrapExists) markState({ bootstrapSeededAt: nowIso() });
1706
+ if (!state.onboardingCompletedAt && state.bootstrapSeededAt && !bootstrapExists) markState({ onboardingCompletedAt: nowIso() });
1707
+ if (!state.bootstrapSeededAt && !state.onboardingCompletedAt && !bootstrapExists) {
1708
+ const [identityContent, userContent] = await Promise.all([fs$1.readFile(identityPath, "utf-8"), fs$1.readFile(userPath, "utf-8")]);
1709
+ const hasUserContent = await (async () => {
1710
+ const indicators = [
1711
+ path.join(dir, "memory"),
1712
+ path.join(dir, DEFAULT_MEMORY_FILENAME),
1713
+ path.join(dir, ".git")
1714
+ ];
1715
+ for (const indicator of indicators) try {
1716
+ await fs$1.access(indicator);
1717
+ return true;
1718
+ } catch {}
1719
+ return false;
1720
+ })();
1721
+ if (identityContent !== identityTemplate || userContent !== userTemplate || hasUserContent) markState({ onboardingCompletedAt: nowIso() });
1722
+ else {
1723
+ if (!await writeFileIfMissing(bootstrapPath, await loadTemplate("BOOTSTRAP.md"))) bootstrapExists = await fileExists(bootstrapPath);
1724
+ else bootstrapExists = true;
1725
+ if (bootstrapExists && !state.bootstrapSeededAt) markState({ bootstrapSeededAt: nowIso() });
1726
+ }
1727
+ }
1728
+ if (stateDirty) await writeWorkspaceOnboardingState(statePath, state);
1729
+ await ensureGitRepo(dir, isBrandNewWorkspace);
1730
+ return {
1731
+ dir,
1732
+ agentsPath,
1733
+ soulPath,
1734
+ toolsPath,
1735
+ identityPath,
1736
+ userPath,
1737
+ heartbeatPath,
1738
+ bootstrapPath
1739
+ };
1740
+ }
1741
+ async function resolveMemoryBootstrapEntries(resolvedDir) {
1742
+ const candidates = [DEFAULT_MEMORY_FILENAME, DEFAULT_MEMORY_ALT_FILENAME];
1743
+ const entries = [];
1744
+ for (const name of candidates) {
1745
+ const filePath = path.join(resolvedDir, name);
1746
+ try {
1747
+ await fs$1.access(filePath);
1748
+ entries.push({
1749
+ name,
1750
+ filePath
1751
+ });
1752
+ } catch {}
1753
+ }
1754
+ if (entries.length <= 1) return entries;
1755
+ const seen = /* @__PURE__ */ new Set();
1756
+ const deduped = [];
1757
+ for (const entry of entries) {
1758
+ let key = entry.filePath;
1759
+ try {
1760
+ key = await fs$1.realpath(entry.filePath);
1761
+ } catch {}
1762
+ if (seen.has(key)) continue;
1763
+ seen.add(key);
1764
+ deduped.push(entry);
1765
+ }
1766
+ return deduped;
1767
+ }
1768
+ async function loadWorkspaceBootstrapFiles(dir) {
1769
+ const resolvedDir = resolveUserPath(dir);
1770
+ const entries = [
1771
+ {
1772
+ name: DEFAULT_AGENTS_FILENAME,
1773
+ filePath: path.join(resolvedDir, DEFAULT_AGENTS_FILENAME)
1774
+ },
1775
+ {
1776
+ name: DEFAULT_SOUL_FILENAME,
1777
+ filePath: path.join(resolvedDir, DEFAULT_SOUL_FILENAME)
1778
+ },
1779
+ {
1780
+ name: DEFAULT_TOOLS_FILENAME,
1781
+ filePath: path.join(resolvedDir, DEFAULT_TOOLS_FILENAME)
1782
+ },
1783
+ {
1784
+ name: DEFAULT_IDENTITY_FILENAME,
1785
+ filePath: path.join(resolvedDir, DEFAULT_IDENTITY_FILENAME)
1786
+ },
1787
+ {
1788
+ name: DEFAULT_USER_FILENAME,
1789
+ filePath: path.join(resolvedDir, DEFAULT_USER_FILENAME)
1790
+ },
1791
+ {
1792
+ name: DEFAULT_HEARTBEAT_FILENAME,
1793
+ filePath: path.join(resolvedDir, DEFAULT_HEARTBEAT_FILENAME)
1794
+ },
1795
+ {
1796
+ name: DEFAULT_BOOTSTRAP_FILENAME,
1797
+ filePath: path.join(resolvedDir, DEFAULT_BOOTSTRAP_FILENAME)
1798
+ }
1799
+ ];
1800
+ entries.push(...await resolveMemoryBootstrapEntries(resolvedDir));
1801
+ const result = [];
1802
+ for (const entry of entries) {
1803
+ const loaded = await readWorkspaceFileWithGuards({
1804
+ filePath: entry.filePath,
1805
+ workspaceDir: resolvedDir
1806
+ });
1807
+ if (loaded.ok) result.push({
1808
+ name: entry.name,
1809
+ path: entry.filePath,
1810
+ content: loaded.content,
1811
+ missing: false
1812
+ });
1813
+ else result.push({
1814
+ name: entry.name,
1815
+ path: entry.filePath,
1816
+ missing: true
1817
+ });
1818
+ }
1819
+ return result;
1820
+ }
1821
+ const MINIMAL_BOOTSTRAP_ALLOWLIST = new Set([
1822
+ DEFAULT_AGENTS_FILENAME,
1823
+ DEFAULT_TOOLS_FILENAME,
1824
+ DEFAULT_SOUL_FILENAME,
1825
+ DEFAULT_IDENTITY_FILENAME,
1826
+ DEFAULT_USER_FILENAME
1827
+ ]);
1828
+ function filterBootstrapFilesForSession(files, sessionKey) {
1829
+ if (!sessionKey || !isSubagentSessionKey(sessionKey) && !isCronSessionKey(sessionKey)) return files;
1830
+ return files.filter((file) => MINIMAL_BOOTSTRAP_ALLOWLIST.has(file.name));
1831
+ }
1832
+ //#endregion
1833
+ //#region src/agents/agent-scope.ts
1834
+ const log = createSubsystemLogger("agent-scope");
1835
+ /** Strip null bytes from paths to prevent ENOTDIR errors. */
1836
+ function stripNullBytes(s) {
1837
+ return s.replace(/\0/g, "");
1838
+ }
1839
+ let defaultAgentWarned = false;
1840
+ function listAgentEntries(cfg) {
1841
+ const list = cfg.agents?.list;
1842
+ if (!Array.isArray(list)) return [];
1843
+ return list.filter((entry) => Boolean(entry && typeof entry === "object"));
1844
+ }
1845
+ function listAgentIds(cfg) {
1846
+ const agents = listAgentEntries(cfg);
1847
+ if (agents.length === 0) return [DEFAULT_AGENT_ID];
1848
+ const seen = /* @__PURE__ */ new Set();
1849
+ const ids = [];
1850
+ for (const entry of agents) {
1851
+ const id = normalizeAgentId(entry?.id);
1852
+ if (seen.has(id)) continue;
1853
+ seen.add(id);
1854
+ ids.push(id);
1855
+ }
1856
+ return ids.length > 0 ? ids : [DEFAULT_AGENT_ID];
1857
+ }
1858
+ function resolveDefaultAgentId(cfg) {
1859
+ const agents = listAgentEntries(cfg);
1860
+ if (agents.length === 0) return DEFAULT_AGENT_ID;
1861
+ const defaults = agents.filter((agent) => agent?.default);
1862
+ if (defaults.length > 1 && !defaultAgentWarned) {
1863
+ defaultAgentWarned = true;
1864
+ log.warn("Multiple agents marked default=true; using the first entry as default.");
1865
+ }
1866
+ const chosen = (defaults[0] ?? agents[0])?.id?.trim();
1867
+ return normalizeAgentId(chosen || "main");
1868
+ }
1869
+ function resolveSessionAgentIds(params) {
1870
+ const defaultAgentId = resolveDefaultAgentId(params.config ?? {});
1871
+ const explicitAgentIdRaw = typeof params.agentId === "string" ? params.agentId.trim().toLowerCase() : "";
1872
+ const explicitAgentId = explicitAgentIdRaw ? normalizeAgentId(explicitAgentIdRaw) : null;
1873
+ const sessionKey = params.sessionKey?.trim();
1874
+ const normalizedSessionKey = sessionKey ? sessionKey.toLowerCase() : void 0;
1875
+ const parsed = normalizedSessionKey ? parseAgentSessionKey(normalizedSessionKey) : null;
1876
+ return {
1877
+ defaultAgentId,
1878
+ sessionAgentId: explicitAgentId ?? (parsed?.agentId ? normalizeAgentId(parsed.agentId) : defaultAgentId)
1879
+ };
1880
+ }
1881
+ function resolveSessionAgentId(params) {
1882
+ return resolveSessionAgentIds(params).sessionAgentId;
1883
+ }
1884
+ function resolveAgentEntry(cfg, agentId) {
1885
+ const id = normalizeAgentId(agentId);
1886
+ return listAgentEntries(cfg).find((entry) => normalizeAgentId(entry.id) === id);
1887
+ }
1888
+ function resolveAgentConfig(cfg, agentId) {
1889
+ const entry = resolveAgentEntry(cfg, normalizeAgentId(agentId));
1890
+ if (!entry) return;
1891
+ return {
1892
+ name: typeof entry.name === "string" ? entry.name : void 0,
1893
+ workspace: typeof entry.workspace === "string" ? entry.workspace : void 0,
1894
+ agentDir: typeof entry.agentDir === "string" ? entry.agentDir : void 0,
1895
+ model: typeof entry.model === "string" || entry.model && typeof entry.model === "object" ? entry.model : void 0,
1896
+ skills: Array.isArray(entry.skills) ? entry.skills : void 0,
1897
+ memorySearch: entry.memorySearch,
1898
+ humanDelay: entry.humanDelay,
1899
+ heartbeat: entry.heartbeat,
1900
+ identity: entry.identity,
1901
+ groupChat: entry.groupChat,
1902
+ subagents: typeof entry.subagents === "object" && entry.subagents ? entry.subagents : void 0,
1903
+ sandbox: entry.sandbox,
1904
+ tools: entry.tools
1905
+ };
1906
+ }
1907
+ function resolveAgentSkillsFilter(cfg, agentId) {
1908
+ return normalizeSkillFilter(resolveAgentConfig(cfg, agentId)?.skills);
1909
+ }
1910
+ function resolveModelPrimary(raw) {
1911
+ if (typeof raw === "string") return raw.trim() || void 0;
1912
+ if (!raw || typeof raw !== "object") return;
1913
+ const primary = raw.primary;
1914
+ if (typeof primary !== "string") return;
1915
+ return primary.trim() || void 0;
1916
+ }
1917
+ function resolveAgentExplicitModelPrimary(cfg, agentId) {
1918
+ const raw = resolveAgentConfig(cfg, agentId)?.model;
1919
+ return resolveModelPrimary(raw);
1920
+ }
1921
+ function resolveAgentEffectiveModelPrimary(cfg, agentId) {
1922
+ return resolveAgentExplicitModelPrimary(cfg, agentId) ?? resolveModelPrimary(cfg.agents?.defaults?.model);
1923
+ }
1924
+ function resolveAgentModelFallbacksOverride(cfg, agentId) {
1925
+ const raw = resolveAgentConfig(cfg, agentId)?.model;
1926
+ if (!raw || typeof raw === "string") return;
1927
+ if (!Object.hasOwn(raw, "fallbacks")) return;
1928
+ return Array.isArray(raw.fallbacks) ? raw.fallbacks : void 0;
1929
+ }
1930
+ function resolveFallbackAgentId(params) {
1931
+ const explicitAgentId = typeof params.agentId === "string" ? params.agentId.trim() : "";
1932
+ if (explicitAgentId) return normalizeAgentId(explicitAgentId);
1933
+ return resolveAgentIdFromSessionKey(params.sessionKey);
1934
+ }
1935
+ function resolveRunModelFallbacksOverride(params) {
1936
+ if (!params.cfg) return;
1937
+ return resolveAgentModelFallbacksOverride(params.cfg, resolveFallbackAgentId({
1938
+ agentId: params.agentId,
1939
+ sessionKey: params.sessionKey
1940
+ }));
1941
+ }
1942
+ function hasConfiguredModelFallbacks(params) {
1943
+ const fallbacksOverride = resolveRunModelFallbacksOverride(params);
1944
+ const defaultFallbacks = resolveAgentModelFallbackValues(params.cfg?.agents?.defaults?.model);
1945
+ return (fallbacksOverride ?? defaultFallbacks).length > 0;
1946
+ }
1947
+ function resolveEffectiveModelFallbacks(params) {
1948
+ const agentFallbacksOverride = resolveAgentModelFallbacksOverride(params.cfg, params.agentId);
1949
+ if (!params.hasSessionModelOverride) return agentFallbacksOverride;
1950
+ const defaultFallbacks = resolveAgentModelFallbackValues(params.cfg.agents?.defaults?.model);
1951
+ return agentFallbacksOverride ?? defaultFallbacks;
1952
+ }
1953
+ function resolveAgentWorkspaceDir(cfg, agentId) {
1954
+ const id = normalizeAgentId(agentId);
1955
+ const configured = resolveAgentConfig(cfg, id)?.workspace?.trim();
1956
+ if (configured) return stripNullBytes(resolveUserPath(configured));
1957
+ if (id === resolveDefaultAgentId(cfg)) {
1958
+ const fallback = cfg.agents?.defaults?.workspace?.trim();
1959
+ if (fallback) return stripNullBytes(resolveUserPath(fallback));
1960
+ return stripNullBytes(resolveDefaultAgentWorkspaceDir(process.env));
1961
+ }
1962
+ const stateDir = resolveStateDir(process.env);
1963
+ return stripNullBytes(path.join(stateDir, `workspace-${id}`));
1964
+ }
1965
+ function resolveAgentDir(cfg, agentId) {
1966
+ const id = normalizeAgentId(agentId);
1967
+ const configured = resolveAgentConfig(cfg, id)?.agentDir?.trim();
1968
+ if (configured) return resolveUserPath(configured);
1969
+ const root = resolveStateDir(process.env);
1970
+ return path.join(root, "agents", id, "agent");
1971
+ }
1972
+ //#endregion
1973
+ //#region src/utils/run-with-concurrency.ts
1974
+ async function runTasksWithConcurrency(params) {
1975
+ const { tasks, limit, onTaskError } = params;
1976
+ const errorMode = params.errorMode ?? "continue";
1977
+ if (tasks.length === 0) return {
1978
+ results: [],
1979
+ firstError: void 0,
1980
+ hasError: false
1981
+ };
1982
+ const resolvedLimit = Math.max(1, Math.min(limit, tasks.length));
1983
+ const results = Array.from({ length: tasks.length });
1984
+ let next = 0;
1985
+ let firstError = void 0;
1986
+ let hasError = false;
1987
+ const workers = Array.from({ length: resolvedLimit }, async () => {
1988
+ while (true) {
1989
+ if (errorMode === "stop" && hasError) return;
1990
+ const index = next;
1991
+ next += 1;
1992
+ if (index >= tasks.length) return;
1993
+ try {
1994
+ results[index] = await tasks[index]();
1995
+ } catch (error) {
1996
+ if (!hasError) {
1997
+ firstError = error;
1998
+ hasError = true;
1999
+ }
2000
+ onTaskError?.(error, index);
2001
+ if (errorMode === "stop") return;
2002
+ }
2003
+ }
2004
+ });
2005
+ await Promise.allSettled(workers);
2006
+ return {
2007
+ results,
2008
+ firstError,
2009
+ hasError
2010
+ };
2011
+ }
2012
+ //#endregion
2013
+ //#region src/security/safe-regex.ts
2014
+ const SAFE_REGEX_CACHE_MAX = 256;
2015
+ const SAFE_REGEX_TEST_WINDOW = 2048;
2016
+ const safeRegexCache = /* @__PURE__ */ new Map();
2017
+ function createParseFrame() {
2018
+ return {
2019
+ lastToken: null,
2020
+ containsRepetition: false,
2021
+ hasAlternation: false,
2022
+ branchMinLength: 0,
2023
+ branchMaxLength: 0,
2024
+ altMinLength: null,
2025
+ altMaxLength: null
2026
+ };
2027
+ }
2028
+ function addLength(left, right) {
2029
+ if (!Number.isFinite(left) || !Number.isFinite(right)) return Number.POSITIVE_INFINITY;
2030
+ return left + right;
2031
+ }
2032
+ function multiplyLength(length, factor) {
2033
+ if (!Number.isFinite(length)) return factor === 0 ? 0 : Number.POSITIVE_INFINITY;
2034
+ return length * factor;
2035
+ }
2036
+ function recordAlternative(frame) {
2037
+ if (frame.altMinLength === null || frame.altMaxLength === null) {
2038
+ frame.altMinLength = frame.branchMinLength;
2039
+ frame.altMaxLength = frame.branchMaxLength;
2040
+ return;
2041
+ }
2042
+ frame.altMinLength = Math.min(frame.altMinLength, frame.branchMinLength);
2043
+ frame.altMaxLength = Math.max(frame.altMaxLength, frame.branchMaxLength);
2044
+ }
2045
+ function readQuantifier(source, index) {
2046
+ const ch = source[index];
2047
+ const consumed = source[index + 1] === "?" ? 2 : 1;
2048
+ if (ch === "*") return {
2049
+ consumed,
2050
+ minRepeat: 0,
2051
+ maxRepeat: null
2052
+ };
2053
+ if (ch === "+") return {
2054
+ consumed,
2055
+ minRepeat: 1,
2056
+ maxRepeat: null
2057
+ };
2058
+ if (ch === "?") return {
2059
+ consumed,
2060
+ minRepeat: 0,
2061
+ maxRepeat: 1
2062
+ };
2063
+ if (ch !== "{") return null;
2064
+ let i = index + 1;
2065
+ while (i < source.length && /\d/.test(source[i])) i += 1;
2066
+ if (i === index + 1) return null;
2067
+ const minRepeat = Number.parseInt(source.slice(index + 1, i), 10);
2068
+ let maxRepeat = minRepeat;
2069
+ if (source[i] === ",") {
2070
+ i += 1;
2071
+ const maxStart = i;
2072
+ while (i < source.length && /\d/.test(source[i])) i += 1;
2073
+ maxRepeat = i === maxStart ? null : Number.parseInt(source.slice(maxStart, i), 10);
2074
+ }
2075
+ if (source[i] !== "}") return null;
2076
+ i += 1;
2077
+ if (source[i] === "?") i += 1;
2078
+ if (maxRepeat !== null && maxRepeat < minRepeat) return null;
2079
+ return {
2080
+ consumed: i - index,
2081
+ minRepeat,
2082
+ maxRepeat
2083
+ };
2084
+ }
2085
+ function tokenizePattern(source) {
2086
+ const tokens = [];
2087
+ let inCharClass = false;
2088
+ for (let i = 0; i < source.length; i += 1) {
2089
+ const ch = source[i];
2090
+ if (ch === "\\") {
2091
+ i += 1;
2092
+ tokens.push({ kind: "simple-token" });
2093
+ continue;
2094
+ }
2095
+ if (inCharClass) {
2096
+ if (ch === "]") inCharClass = false;
2097
+ continue;
2098
+ }
2099
+ if (ch === "[") {
2100
+ inCharClass = true;
2101
+ tokens.push({ kind: "simple-token" });
2102
+ continue;
2103
+ }
2104
+ if (ch === "(") {
2105
+ tokens.push({ kind: "group-open" });
2106
+ continue;
2107
+ }
2108
+ if (ch === ")") {
2109
+ tokens.push({ kind: "group-close" });
2110
+ continue;
2111
+ }
2112
+ if (ch === "|") {
2113
+ tokens.push({ kind: "alternation" });
2114
+ continue;
2115
+ }
2116
+ const quantifier = readQuantifier(source, i);
2117
+ if (quantifier) {
2118
+ tokens.push({
2119
+ kind: "quantifier",
2120
+ quantifier
2121
+ });
2122
+ i += quantifier.consumed - 1;
2123
+ continue;
2124
+ }
2125
+ tokens.push({ kind: "simple-token" });
2126
+ }
2127
+ return tokens;
2128
+ }
2129
+ function analyzeTokensForNestedRepetition(tokens) {
2130
+ const frames = [createParseFrame()];
2131
+ const emitToken = (token) => {
2132
+ const frame = frames[frames.length - 1];
2133
+ frame.lastToken = token;
2134
+ if (token.containsRepetition) frame.containsRepetition = true;
2135
+ frame.branchMinLength = addLength(frame.branchMinLength, token.minLength);
2136
+ frame.branchMaxLength = addLength(frame.branchMaxLength, token.maxLength);
2137
+ };
2138
+ const emitSimpleToken = () => {
2139
+ emitToken({
2140
+ containsRepetition: false,
2141
+ hasAmbiguousAlternation: false,
2142
+ minLength: 1,
2143
+ maxLength: 1
2144
+ });
2145
+ };
2146
+ for (const token of tokens) {
2147
+ if (token.kind === "simple-token") {
2148
+ emitSimpleToken();
2149
+ continue;
2150
+ }
2151
+ if (token.kind === "group-open") {
2152
+ frames.push(createParseFrame());
2153
+ continue;
2154
+ }
2155
+ if (token.kind === "group-close") {
2156
+ if (frames.length > 1) {
2157
+ const frame = frames.pop();
2158
+ if (frame.hasAlternation) recordAlternative(frame);
2159
+ const groupMinLength = frame.hasAlternation ? frame.altMinLength ?? 0 : frame.branchMinLength;
2160
+ const groupMaxLength = frame.hasAlternation ? frame.altMaxLength ?? 0 : frame.branchMaxLength;
2161
+ emitToken({
2162
+ containsRepetition: frame.containsRepetition,
2163
+ hasAmbiguousAlternation: frame.hasAlternation && frame.altMinLength !== null && frame.altMaxLength !== null && frame.altMinLength !== frame.altMaxLength,
2164
+ minLength: groupMinLength,
2165
+ maxLength: groupMaxLength
2166
+ });
2167
+ }
2168
+ continue;
2169
+ }
2170
+ if (token.kind === "alternation") {
2171
+ const frame = frames[frames.length - 1];
2172
+ frame.hasAlternation = true;
2173
+ recordAlternative(frame);
2174
+ frame.branchMinLength = 0;
2175
+ frame.branchMaxLength = 0;
2176
+ frame.lastToken = null;
2177
+ continue;
2178
+ }
2179
+ const frame = frames[frames.length - 1];
2180
+ const previousToken = frame.lastToken;
2181
+ if (!previousToken) continue;
2182
+ if (previousToken.containsRepetition) return true;
2183
+ if (previousToken.hasAmbiguousAlternation && token.quantifier.maxRepeat === null) return true;
2184
+ const previousMinLength = previousToken.minLength;
2185
+ const previousMaxLength = previousToken.maxLength;
2186
+ previousToken.minLength = multiplyLength(previousToken.minLength, token.quantifier.minRepeat);
2187
+ previousToken.maxLength = token.quantifier.maxRepeat === null ? Number.POSITIVE_INFINITY : multiplyLength(previousToken.maxLength, token.quantifier.maxRepeat);
2188
+ previousToken.containsRepetition = true;
2189
+ frame.containsRepetition = true;
2190
+ frame.branchMinLength = frame.branchMinLength - previousMinLength + previousToken.minLength;
2191
+ frame.branchMaxLength = addLength(Number.isFinite(frame.branchMaxLength) && Number.isFinite(previousMaxLength) ? frame.branchMaxLength - previousMaxLength : Number.POSITIVE_INFINITY, previousToken.maxLength);
2192
+ }
2193
+ return false;
2194
+ }
2195
+ function testRegexFromStart(regex, value) {
2196
+ regex.lastIndex = 0;
2197
+ return regex.test(value);
2198
+ }
2199
+ function testRegexWithBoundedInput(regex, input, maxWindow = SAFE_REGEX_TEST_WINDOW) {
2200
+ if (maxWindow <= 0) return false;
2201
+ if (input.length <= maxWindow) return testRegexFromStart(regex, input);
2202
+ if (testRegexFromStart(regex, input.slice(0, maxWindow))) return true;
2203
+ return testRegexFromStart(regex, input.slice(-maxWindow));
2204
+ }
2205
+ function hasNestedRepetition(source) {
2206
+ return analyzeTokensForNestedRepetition(tokenizePattern(source));
2207
+ }
2208
+ function compileSafeRegex(source, flags = "") {
2209
+ const trimmed = source.trim();
2210
+ if (!trimmed) return null;
2211
+ const cacheKey = `${flags}::${trimmed}`;
2212
+ if (safeRegexCache.has(cacheKey)) return safeRegexCache.get(cacheKey) ?? null;
2213
+ let compiled = null;
2214
+ if (!hasNestedRepetition(trimmed)) try {
2215
+ compiled = new RegExp(trimmed, flags);
2216
+ } catch {
2217
+ compiled = null;
2218
+ }
2219
+ safeRegexCache.set(cacheKey, compiled);
2220
+ if (safeRegexCache.size > SAFE_REGEX_CACHE_MAX) {
2221
+ const oldestKey = safeRegexCache.keys().next().value;
2222
+ if (oldestKey) safeRegexCache.delete(oldestKey);
2223
+ }
2224
+ return compiled;
2225
+ }
2226
+ function replacePatternBounded(text, pattern, replacer, options) {
2227
+ const chunkThreshold = options?.chunkThreshold ?? 32768;
2228
+ const chunkSize = options?.chunkSize ?? 16384;
2229
+ if (chunkThreshold <= 0 || chunkSize <= 0 || text.length <= chunkThreshold) return text.replace(pattern, replacer);
2230
+ let output = "";
2231
+ for (let index = 0; index < text.length; index += chunkSize) output += text.slice(index, index + chunkSize).replace(pattern, replacer);
2232
+ return output;
2233
+ }
2234
+ //#endregion
2235
+ //#region src/logging/redact.ts
2236
+ const requireConfig = resolveNodeRequireFromMeta(import.meta.url);
2237
+ const DEFAULT_REDACT_MODE = "tools";
2238
+ const DEFAULT_REDACT_MIN_LENGTH = 18;
2239
+ const DEFAULT_REDACT_KEEP_START = 6;
2240
+ const DEFAULT_REDACT_KEEP_END = 4;
2241
+ const DEFAULT_REDACT_PATTERNS = [
2242
+ String.raw`\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD)\b\s*[=:]\s*(["']?)([^\s"'\\]+)\1`,
2243
+ String.raw`"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*"([^"]+)"`,
2244
+ String.raw`--(?:api[-_]?key|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1`,
2245
+ String.raw`Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)`,
2246
+ String.raw`\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b`,
2247
+ String.raw`-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----`,
2248
+ String.raw`\b(sk-[A-Za-z0-9_-]{8,})\b`,
2249
+ String.raw`\b(ghp_[A-Za-z0-9]{20,})\b`,
2250
+ String.raw`\b(github_pat_[A-Za-z0-9_]{20,})\b`,
2251
+ String.raw`\b(xox[baprs]-[A-Za-z0-9-]{10,})\b`,
2252
+ String.raw`\b(xapp-[A-Za-z0-9-]{10,})\b`,
2253
+ String.raw`\b(gsk_[A-Za-z0-9_-]{10,})\b`,
2254
+ String.raw`\b(AIza[0-9A-Za-z\-_]{20,})\b`,
2255
+ String.raw`\b(pplx-[A-Za-z0-9_-]{10,})\b`,
2256
+ String.raw`\b(npm_[A-Za-z0-9]{10,})\b`,
2257
+ String.raw`\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b`,
2258
+ String.raw`\b(\d{6,}:[A-Za-z0-9_-]{20,})\b`
2259
+ ];
2260
+ function normalizeMode(value) {
2261
+ return value === "off" ? "off" : DEFAULT_REDACT_MODE;
2262
+ }
2263
+ function parsePattern(raw) {
2264
+ if (!raw.trim()) return null;
2265
+ const match = raw.match(/^\/(.+)\/([gimsuy]*)$/);
2266
+ if (match) {
2267
+ const flags = match[2].includes("g") ? match[2] : `${match[2]}g`;
2268
+ return compileSafeRegex(match[1], flags);
2269
+ }
2270
+ return compileSafeRegex(raw, "gi");
2271
+ }
2272
+ function resolvePatterns(value) {
2273
+ return (value?.length ? value : DEFAULT_REDACT_PATTERNS).map(parsePattern).filter((re) => Boolean(re));
2274
+ }
2275
+ function maskToken(token) {
2276
+ if (token.length < DEFAULT_REDACT_MIN_LENGTH) return "***";
2277
+ return `${token.slice(0, DEFAULT_REDACT_KEEP_START)}…${token.slice(-DEFAULT_REDACT_KEEP_END)}`;
2278
+ }
2279
+ function redactPemBlock(block) {
2280
+ const lines = block.split(/\r?\n/).filter(Boolean);
2281
+ if (lines.length < 2) return "***";
2282
+ return `${lines[0]}\n…redacted…\n${lines[lines.length - 1]}`;
2283
+ }
2284
+ function redactMatch(match, groups) {
2285
+ if (match.includes("PRIVATE KEY-----")) return redactPemBlock(match);
2286
+ const token = groups.filter((value) => typeof value === "string" && value.length > 0).at(-1) ?? match;
2287
+ const masked = maskToken(token);
2288
+ if (token === match) return masked;
2289
+ return match.replace(token, masked);
2290
+ }
2291
+ function redactText(text, patterns) {
2292
+ let next = text;
2293
+ for (const pattern of patterns) next = replacePatternBounded(next, pattern, (...args) => redactMatch(args[0], args.slice(1, args.length - 2)));
2294
+ return next;
2295
+ }
2296
+ function resolveConfigRedaction() {
2297
+ let cfg;
2298
+ try {
2299
+ cfg = (requireConfig?.("../config/config.js"))?.loadConfig?.().logging;
2300
+ } catch {
2301
+ cfg = void 0;
2302
+ }
2303
+ return {
2304
+ mode: normalizeMode(cfg?.redactSensitive),
2305
+ patterns: cfg?.redactPatterns
2306
+ };
2307
+ }
2308
+ function redactSensitiveText(text, options) {
2309
+ if (!text) return text;
2310
+ const resolved = options ?? resolveConfigRedaction();
2311
+ if (normalizeMode(resolved.mode) === "off") return text;
2312
+ const patterns = resolvePatterns(resolved.patterns);
2313
+ if (!patterns.length) return text;
2314
+ return redactText(text, patterns);
2315
+ }
2316
+ function redactToolDetail(detail) {
2317
+ const resolved = resolveConfigRedaction();
2318
+ if (normalizeMode(resolved.mode) !== "tools") return detail;
2319
+ return redactSensitiveText(detail, resolved);
2320
+ }
2321
+ function getDefaultRedactPatterns() {
2322
+ return [...DEFAULT_REDACT_PATTERNS];
2323
+ }
2324
+ //#endregion
2325
+ //#region src/infra/hardlink-guards.ts
2326
+ async function assertNoHardlinkedFinalPath(params) {
2327
+ if (params.allowFinalHardlinkForUnlink) return;
2328
+ let stat;
2329
+ try {
2330
+ stat = await fs$1.stat(params.filePath);
2331
+ } catch (err) {
2332
+ if (isNotFoundPathError(err)) return;
2333
+ throw err;
2334
+ }
2335
+ if (!stat.isFile()) return;
2336
+ if (stat.nlink > 1) throw new Error(`Hardlinked path is not allowed under ${params.boundaryLabel} (${shortPath(params.root)}): ${shortPath(params.filePath)}`);
2337
+ }
2338
+ function shortPath(value) {
2339
+ if (value.startsWith(os.homedir())) return `~${value.slice(os.homedir().length)}`;
2340
+ return value;
2341
+ }
2342
+ //#endregion
2343
+ //#region src/infra/path-alias-guards.ts
2344
+ const PATH_ALIAS_POLICIES = BOUNDARY_PATH_ALIAS_POLICIES;
2345
+ async function assertNoPathAliasEscape(params) {
2346
+ const resolved = await resolveBoundaryPath({
2347
+ absolutePath: params.absolutePath,
2348
+ rootPath: params.rootPath,
2349
+ boundaryLabel: params.boundaryLabel,
2350
+ policy: params.policy
2351
+ });
2352
+ if (params.policy?.allowFinalSymlinkForUnlink === true && resolved.kind === "symlink") return;
2353
+ await assertNoHardlinkedFinalPath({
2354
+ filePath: resolved.absolutePath,
2355
+ root: resolved.rootPath,
2356
+ boundaryLabel: params.boundaryLabel,
2357
+ allowFinalHardlinkForUnlink: params.policy?.allowFinalHardlinkForUnlink
2358
+ });
2359
+ }
2360
+ //#endregion
2361
+ //#region src/infra/fs-safe.ts
2362
+ var SafeOpenError = class extends Error {
2363
+ constructor(code, message, options) {
2364
+ super(message, options);
2365
+ this.code = code;
2366
+ this.name = "SafeOpenError";
2367
+ }
2368
+ };
2369
+ const SUPPORTS_NOFOLLOW = process.platform !== "win32" && "O_NOFOLLOW" in constants;
2370
+ const OPEN_READ_FLAGS = constants.O_RDONLY | (SUPPORTS_NOFOLLOW ? constants.O_NOFOLLOW : 0);
2371
+ const OPEN_WRITE_EXISTING_FLAGS = constants.O_WRONLY | (SUPPORTS_NOFOLLOW ? constants.O_NOFOLLOW : 0);
2372
+ const OPEN_WRITE_CREATE_FLAGS = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (SUPPORTS_NOFOLLOW ? constants.O_NOFOLLOW : 0);
2373
+ const OPEN_APPEND_EXISTING_FLAGS = constants.O_RDWR | constants.O_APPEND | (SUPPORTS_NOFOLLOW ? constants.O_NOFOLLOW : 0);
2374
+ const OPEN_APPEND_CREATE_FLAGS = constants.O_RDWR | constants.O_APPEND | constants.O_CREAT | constants.O_EXCL | (SUPPORTS_NOFOLLOW ? constants.O_NOFOLLOW : 0);
2375
+ const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
2376
+ async function expandRelativePathWithHome(relativePath) {
2377
+ let home = process.env.HOME || process.env.USERPROFILE || os.homedir();
2378
+ try {
2379
+ home = await fs$1.realpath(home);
2380
+ } catch {}
2381
+ return expandHomePrefix(relativePath, { home });
2382
+ }
2383
+ async function openVerifiedLocalFile(filePath, options) {
2384
+ try {
2385
+ if ((await fs$1.lstat(filePath)).isDirectory()) throw new SafeOpenError("not-file", "not a file");
2386
+ } catch (err) {
2387
+ if (err instanceof SafeOpenError) throw err;
2388
+ }
2389
+ let handle;
2390
+ try {
2391
+ handle = await fs$1.open(filePath, OPEN_READ_FLAGS);
2392
+ } catch (err) {
2393
+ if (isNotFoundPathError(err)) throw new SafeOpenError("not-found", "file not found");
2394
+ if (isSymlinkOpenError(err)) throw new SafeOpenError("symlink", "symlink open blocked", { cause: err });
2395
+ if (hasNodeErrorCode(err, "EISDIR")) throw new SafeOpenError("not-file", "not a file");
2396
+ throw err;
2397
+ }
2398
+ try {
2399
+ const [stat, lstat] = await Promise.all([handle.stat(), fs$1.lstat(filePath)]);
2400
+ if (lstat.isSymbolicLink()) throw new SafeOpenError("symlink", "symlink not allowed");
2401
+ if (!stat.isFile()) throw new SafeOpenError("not-file", "not a file");
2402
+ if (options?.rejectHardlinks && stat.nlink > 1) throw new SafeOpenError("invalid-path", "hardlinked path not allowed");
2403
+ if (!sameFileIdentity$1(stat, lstat)) throw new SafeOpenError("path-mismatch", "path changed during read");
2404
+ const realPath = await fs$1.realpath(filePath);
2405
+ const realStat = await fs$1.stat(realPath);
2406
+ if (options?.rejectHardlinks && realStat.nlink > 1) throw new SafeOpenError("invalid-path", "hardlinked path not allowed");
2407
+ if (!sameFileIdentity$1(stat, realStat)) throw new SafeOpenError("path-mismatch", "path mismatch");
2408
+ return {
2409
+ handle,
2410
+ realPath,
2411
+ stat
2412
+ };
2413
+ } catch (err) {
2414
+ await handle.close().catch(() => {});
2415
+ if (err instanceof SafeOpenError) throw err;
2416
+ if (isNotFoundPathError(err)) throw new SafeOpenError("not-found", "file not found");
2417
+ throw err;
2418
+ }
2419
+ }
2420
+ async function resolvePathWithinRoot(params) {
2421
+ let rootReal;
2422
+ try {
2423
+ rootReal = await fs$1.realpath(params.rootDir);
2424
+ } catch (err) {
2425
+ if (isNotFoundPathError(err)) throw new SafeOpenError("not-found", "root dir not found");
2426
+ throw err;
2427
+ }
2428
+ const rootWithSep = ensureTrailingSep(rootReal);
2429
+ const expanded = await expandRelativePathWithHome(params.relativePath);
2430
+ const resolved = path.resolve(rootWithSep, expanded);
2431
+ if (!isPathInside(rootWithSep, resolved)) throw new SafeOpenError("outside-workspace", "file is outside workspace root");
2432
+ return {
2433
+ rootReal,
2434
+ rootWithSep,
2435
+ resolved
2436
+ };
2437
+ }
2438
+ async function openFileWithinRoot(params) {
2439
+ const { rootWithSep, resolved } = await resolvePathWithinRoot(params);
2440
+ let opened;
2441
+ try {
2442
+ opened = await openVerifiedLocalFile(resolved);
2443
+ } catch (err) {
2444
+ if (err instanceof SafeOpenError) {
2445
+ if (err.code === "not-found") throw err;
2446
+ throw new SafeOpenError("invalid-path", "path is not a regular file under root", { cause: err });
2447
+ }
2448
+ throw err;
2449
+ }
2450
+ if (params.rejectHardlinks !== false && opened.stat.nlink > 1) {
2451
+ await opened.handle.close().catch(() => {});
2452
+ throw new SafeOpenError("invalid-path", "hardlinked path not allowed");
2453
+ }
2454
+ if (!isPathInside(rootWithSep, opened.realPath)) {
2455
+ await opened.handle.close().catch(() => {});
2456
+ throw new SafeOpenError("outside-workspace", "file is outside workspace root");
2457
+ }
2458
+ return opened;
2459
+ }
2460
+ async function readFileWithinRoot(params) {
2461
+ const opened = await openFileWithinRoot({
2462
+ rootDir: params.rootDir,
2463
+ relativePath: params.relativePath,
2464
+ rejectHardlinks: params.rejectHardlinks
2465
+ });
2466
+ try {
2467
+ return await readOpenedFileSafely({
2468
+ opened,
2469
+ maxBytes: params.maxBytes
2470
+ });
2471
+ } finally {
2472
+ await opened.handle.close().catch(() => {});
2473
+ }
2474
+ }
2475
+ async function readPathWithinRoot(params) {
2476
+ const rootDir = path.resolve(params.rootDir);
2477
+ const candidatePath = path.isAbsolute(params.filePath) ? path.resolve(params.filePath) : path.resolve(rootDir, params.filePath);
2478
+ return await readFileWithinRoot({
2479
+ rootDir,
2480
+ relativePath: path.relative(rootDir, candidatePath),
2481
+ rejectHardlinks: params.rejectHardlinks,
2482
+ maxBytes: params.maxBytes
2483
+ });
2484
+ }
2485
+ function createRootScopedReadFile(params) {
2486
+ const rootDir = path.resolve(params.rootDir);
2487
+ return async (filePath) => {
2488
+ return (await readPathWithinRoot({
2489
+ rootDir,
2490
+ filePath,
2491
+ rejectHardlinks: params.rejectHardlinks,
2492
+ maxBytes: params.maxBytes
2493
+ })).buffer;
2494
+ };
2495
+ }
2496
+ async function readLocalFileSafely(params) {
2497
+ const opened = await openVerifiedLocalFile(params.filePath);
2498
+ try {
2499
+ return await readOpenedFileSafely({
2500
+ opened,
2501
+ maxBytes: params.maxBytes
2502
+ });
2503
+ } finally {
2504
+ await opened.handle.close().catch(() => {});
2505
+ }
2506
+ }
2507
+ async function readOpenedFileSafely(params) {
2508
+ if (params.maxBytes !== void 0 && params.opened.stat.size > params.maxBytes) throw new SafeOpenError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${params.opened.stat.size})`);
2509
+ return {
2510
+ buffer: await params.opened.handle.readFile(),
2511
+ realPath: params.opened.realPath,
2512
+ stat: params.opened.stat
2513
+ };
2514
+ }
2515
+ function emitWriteBoundaryWarning(reason) {
2516
+ logWarn(`security: fs-safe write boundary warning (${reason})`);
2517
+ }
2518
+ function buildAtomicWriteTempPath(targetPath) {
2519
+ const dir = path.dirname(targetPath);
2520
+ const base = path.basename(targetPath);
2521
+ return path.join(dir, `.${base}.${process.pid}.${randomUUID()}.tmp`);
2522
+ }
2523
+ async function writeTempFileForAtomicReplace(params) {
2524
+ const tempHandle = await fs$1.open(params.tempPath, OPEN_WRITE_CREATE_FLAGS, params.mode);
2525
+ try {
2526
+ if (typeof params.data === "string") await tempHandle.writeFile(params.data, params.encoding ?? "utf8");
2527
+ else await tempHandle.writeFile(params.data);
2528
+ return await tempHandle.stat();
2529
+ } finally {
2530
+ await tempHandle.close().catch(() => {});
2531
+ }
2532
+ }
2533
+ async function verifyAtomicWriteResult(params) {
2534
+ const rootWithSep = ensureTrailingSep(await fs$1.realpath(params.rootDir));
2535
+ const opened = await openVerifiedLocalFile(params.targetPath, { rejectHardlinks: true });
2536
+ try {
2537
+ if (!sameFileIdentity$1(opened.stat, params.expectedStat)) throw new SafeOpenError("path-mismatch", "path changed during write");
2538
+ if (!isPathInside(rootWithSep, opened.realPath)) throw new SafeOpenError("outside-workspace", "file is outside workspace root");
2539
+ } finally {
2540
+ await opened.handle.close().catch(() => {});
2541
+ }
2542
+ }
2543
+ async function resolveOpenedFileRealPathForHandle(handle, ioPath) {
2544
+ try {
2545
+ return await fs$1.realpath(ioPath);
2546
+ } catch (err) {
2547
+ if (!isNotFoundPathError(err)) throw err;
2548
+ }
2549
+ const fdCandidates = process.platform === "linux" ? [`/proc/self/fd/${handle.fd}`, `/dev/fd/${handle.fd}`] : process.platform === "win32" ? [] : [`/dev/fd/${handle.fd}`];
2550
+ for (const fdPath of fdCandidates) try {
2551
+ return await fs$1.realpath(fdPath);
2552
+ } catch {}
2553
+ throw new SafeOpenError("path-mismatch", "unable to resolve opened file path");
2554
+ }
2555
+ async function openWritableFileWithinRoot(params) {
2556
+ const { rootReal, rootWithSep, resolved } = await resolvePathWithinRoot(params);
2557
+ try {
2558
+ await assertNoPathAliasEscape({
2559
+ absolutePath: resolved,
2560
+ rootPath: rootReal,
2561
+ boundaryLabel: "root"
2562
+ });
2563
+ } catch (err) {
2564
+ throw new SafeOpenError("invalid-path", "path alias escape blocked", { cause: err });
2565
+ }
2566
+ if (params.mkdir !== false) await fs$1.mkdir(path.dirname(resolved), { recursive: true });
2567
+ let ioPath = resolved;
2568
+ try {
2569
+ const resolvedRealPath = await fs$1.realpath(resolved);
2570
+ if (!isPathInside(rootWithSep, resolvedRealPath)) throw new SafeOpenError("outside-workspace", "file is outside workspace root");
2571
+ ioPath = resolvedRealPath;
2572
+ } catch (err) {
2573
+ if (err instanceof SafeOpenError) throw err;
2574
+ if (!isNotFoundPathError(err)) throw err;
2575
+ }
2576
+ const fileMode = params.mode ?? 384;
2577
+ let handle;
2578
+ let createdForWrite = false;
2579
+ const existingFlags = params.append ? OPEN_APPEND_EXISTING_FLAGS : OPEN_WRITE_EXISTING_FLAGS;
2580
+ const createFlags = params.append ? OPEN_APPEND_CREATE_FLAGS : OPEN_WRITE_CREATE_FLAGS;
2581
+ try {
2582
+ try {
2583
+ handle = await fs$1.open(ioPath, existingFlags, fileMode);
2584
+ } catch (err) {
2585
+ if (!isNotFoundPathError(err)) throw err;
2586
+ handle = await fs$1.open(ioPath, createFlags, fileMode);
2587
+ createdForWrite = true;
2588
+ }
2589
+ } catch (err) {
2590
+ if (isNotFoundPathError(err)) throw new SafeOpenError("not-found", "file not found");
2591
+ if (isSymlinkOpenError(err)) throw new SafeOpenError("invalid-path", "symlink open blocked", { cause: err });
2592
+ throw err;
2593
+ }
2594
+ let openedRealPath = null;
2595
+ try {
2596
+ const stat = await handle.stat();
2597
+ if (!stat.isFile()) throw new SafeOpenError("invalid-path", "path is not a regular file under root");
2598
+ if (stat.nlink > 1) throw new SafeOpenError("invalid-path", "hardlinked path not allowed");
2599
+ try {
2600
+ const lstat = await fs$1.lstat(ioPath);
2601
+ if (lstat.isSymbolicLink() || !lstat.isFile()) throw new SafeOpenError("invalid-path", "path is not a regular file under root");
2602
+ if (!sameFileIdentity$1(stat, lstat)) throw new SafeOpenError("path-mismatch", "path changed during write");
2603
+ } catch (err) {
2604
+ if (!isNotFoundPathError(err)) throw err;
2605
+ }
2606
+ const realPath = await resolveOpenedFileRealPathForHandle(handle, ioPath);
2607
+ openedRealPath = realPath;
2608
+ const realStat = await fs$1.stat(realPath);
2609
+ if (!sameFileIdentity$1(stat, realStat)) throw new SafeOpenError("path-mismatch", "path mismatch");
2610
+ if (realStat.nlink > 1) throw new SafeOpenError("invalid-path", "hardlinked path not allowed");
2611
+ if (!isPathInside(rootWithSep, realPath)) throw new SafeOpenError("outside-workspace", "file is outside workspace root");
2612
+ if (params.append !== true && params.truncateExisting !== false && !createdForWrite) await handle.truncate(0);
2613
+ return {
2614
+ handle,
2615
+ createdForWrite,
2616
+ openedRealPath: realPath,
2617
+ openedStat: stat
2618
+ };
2619
+ } catch (err) {
2620
+ const cleanupCreatedPath = createdForWrite && err instanceof SafeOpenError;
2621
+ const cleanupPath = openedRealPath ?? ioPath;
2622
+ await handle.close().catch(() => {});
2623
+ if (cleanupCreatedPath) await fs$1.rm(cleanupPath, { force: true }).catch(() => {});
2624
+ throw err;
2625
+ }
2626
+ }
2627
+ async function appendFileWithinRoot(params) {
2628
+ const target = await openWritableFileWithinRoot({
2629
+ rootDir: params.rootDir,
2630
+ relativePath: params.relativePath,
2631
+ mkdir: params.mkdir,
2632
+ truncateExisting: false,
2633
+ append: true
2634
+ });
2635
+ try {
2636
+ let prefix = "";
2637
+ if (params.prependNewlineIfNeeded === true && !target.createdForWrite && target.openedStat.size > 0 && (typeof params.data === "string" && !params.data.startsWith("\n") || Buffer.isBuffer(params.data) && params.data.length > 0 && params.data[0] !== 10)) {
2638
+ const lastByte = Buffer.alloc(1);
2639
+ const { bytesRead } = await target.handle.read(lastByte, 0, 1, target.openedStat.size - 1);
2640
+ if (bytesRead === 1 && lastByte[0] !== 10) prefix = "\n";
2641
+ }
2642
+ if (typeof params.data === "string") {
2643
+ await target.handle.appendFile(`${prefix}${params.data}`, params.encoding ?? "utf8");
2644
+ return;
2645
+ }
2646
+ const payload = prefix.length > 0 ? Buffer.concat([Buffer.from(prefix, "utf8"), params.data]) : params.data;
2647
+ await target.handle.appendFile(payload);
2648
+ } finally {
2649
+ await target.handle.close().catch(() => {});
2650
+ }
2651
+ }
2652
+ async function writeFileWithinRoot(params) {
2653
+ const target = await openWritableFileWithinRoot({
2654
+ rootDir: params.rootDir,
2655
+ relativePath: params.relativePath,
2656
+ mkdir: params.mkdir,
2657
+ truncateExisting: false
2658
+ });
2659
+ const destinationPath = target.openedRealPath;
2660
+ const targetMode = target.openedStat.mode & 511;
2661
+ await target.handle.close().catch(() => {});
2662
+ let tempPath = null;
2663
+ try {
2664
+ tempPath = buildAtomicWriteTempPath(destinationPath);
2665
+ const writtenStat = await writeTempFileForAtomicReplace({
2666
+ tempPath,
2667
+ data: params.data,
2668
+ encoding: params.encoding,
2669
+ mode: targetMode || 384
2670
+ });
2671
+ await fs$1.rename(tempPath, destinationPath);
2672
+ tempPath = null;
2673
+ try {
2674
+ await verifyAtomicWriteResult({
2675
+ rootDir: params.rootDir,
2676
+ targetPath: destinationPath,
2677
+ expectedStat: writtenStat
2678
+ });
2679
+ } catch (err) {
2680
+ emitWriteBoundaryWarning(`post-write verification failed: ${String(err)}`);
2681
+ throw err;
2682
+ }
2683
+ } finally {
2684
+ if (tempPath) await fs$1.rm(tempPath, { force: true }).catch(() => {});
2685
+ }
2686
+ }
2687
+ async function copyFileWithinRoot(params) {
2688
+ const source = await openVerifiedLocalFile(params.sourcePath, { rejectHardlinks: params.rejectSourceHardlinks });
2689
+ if (params.maxBytes !== void 0 && source.stat.size > params.maxBytes) {
2690
+ await source.handle.close().catch(() => {});
2691
+ throw new SafeOpenError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${source.stat.size})`);
2692
+ }
2693
+ let target = null;
2694
+ let sourceClosedByStream = false;
2695
+ let targetClosedByUs = false;
2696
+ let tempHandle = null;
2697
+ let tempPath = null;
2698
+ let tempClosedByStream = false;
2699
+ try {
2700
+ target = await openWritableFileWithinRoot({
2701
+ rootDir: params.rootDir,
2702
+ relativePath: params.relativePath,
2703
+ mkdir: params.mkdir,
2704
+ truncateExisting: false
2705
+ });
2706
+ const destinationPath = target.openedRealPath;
2707
+ const targetMode = target.openedStat.mode & 511;
2708
+ await target.handle.close().catch(() => {});
2709
+ targetClosedByUs = true;
2710
+ tempPath = buildAtomicWriteTempPath(destinationPath);
2711
+ tempHandle = await fs$1.open(tempPath, OPEN_WRITE_CREATE_FLAGS, targetMode || 384);
2712
+ const sourceStream = source.handle.createReadStream();
2713
+ const targetStream = tempHandle.createWriteStream();
2714
+ sourceStream.once("close", () => {
2715
+ sourceClosedByStream = true;
2716
+ });
2717
+ targetStream.once("close", () => {
2718
+ tempClosedByStream = true;
2719
+ });
2720
+ await pipeline(sourceStream, targetStream);
2721
+ const writtenStat = await fs$1.stat(tempPath);
2722
+ if (!tempClosedByStream) {
2723
+ await tempHandle.close().catch(() => {});
2724
+ tempClosedByStream = true;
2725
+ }
2726
+ tempHandle = null;
2727
+ await fs$1.rename(tempPath, destinationPath);
2728
+ tempPath = null;
2729
+ try {
2730
+ await verifyAtomicWriteResult({
2731
+ rootDir: params.rootDir,
2732
+ targetPath: destinationPath,
2733
+ expectedStat: writtenStat
2734
+ });
2735
+ } catch (err) {
2736
+ emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
2737
+ throw err;
2738
+ }
2739
+ } catch (err) {
2740
+ if (target?.createdForWrite) await fs$1.rm(target.openedRealPath, { force: true }).catch(() => {});
2741
+ throw err;
2742
+ } finally {
2743
+ if (tempPath) await fs$1.rm(tempPath, { force: true }).catch(() => {});
2744
+ if (!sourceClosedByStream) await source.handle.close().catch(() => {});
2745
+ if (tempHandle && !tempClosedByStream) await tempHandle.close().catch(() => {});
2746
+ if (target && !targetClosedByUs) await target.handle.close().catch(() => {});
2747
+ }
2748
+ }
2749
+ async function writeFileFromPathWithinRoot(params) {
2750
+ await copyFileWithinRoot({
2751
+ sourcePath: params.sourcePath,
2752
+ rootDir: params.rootDir,
2753
+ relativePath: params.relativePath,
2754
+ mkdir: params.mkdir,
2755
+ rejectSourceHardlinks: true
2756
+ });
2757
+ }
2758
+ //#endregion
2759
+ //#region src/plugin-sdk/windows-spawn.ts
2760
+ function isFilePath(candidate) {
2761
+ try {
2762
+ return statSync(candidate).isFile();
2763
+ } catch {
2764
+ return false;
2765
+ }
2766
+ }
2767
+ function resolveWindowsExecutablePath(command, env) {
2768
+ if (command.includes("/") || command.includes("\\") || path.isAbsolute(command)) return command;
2769
+ const pathEntries = (env.PATH ?? env.Path ?? process.env.PATH ?? process.env.Path ?? "").split(";").map((entry) => entry.trim()).filter(Boolean);
2770
+ const hasExtension = path.extname(command).length > 0;
2771
+ const pathExtRaw = env.PATHEXT ?? env.Pathext ?? process.env.PATHEXT ?? process.env.Pathext ?? ".EXE;.CMD;.BAT;.COM";
2772
+ const pathExt = hasExtension ? [""] : pathExtRaw.split(";").map((ext) => ext.trim()).filter(Boolean).map((ext) => ext.startsWith(".") ? ext : `.${ext}`);
2773
+ for (const dir of pathEntries) for (const ext of pathExt) for (const candidateExt of [
2774
+ ext,
2775
+ ext.toLowerCase(),
2776
+ ext.toUpperCase()
2777
+ ]) {
2778
+ const candidate = path.join(dir, `${command}${candidateExt}`);
2779
+ if (isFilePath(candidate)) return candidate;
2780
+ }
2781
+ return command;
2782
+ }
2783
+ function resolveEntrypointFromCmdShim(wrapperPath) {
2784
+ if (!isFilePath(wrapperPath)) return null;
2785
+ try {
2786
+ const content = readFileSync(wrapperPath, "utf8");
2787
+ const candidates = [];
2788
+ for (const match of content.matchAll(/"([^"\r\n]*)"/g)) {
2789
+ const relative = (match[1] ?? "").match(/%~?dp0%?\s*[\\/]*(.*)$/i)?.[1]?.trim();
2790
+ if (!relative) continue;
2791
+ const normalizedRelative = relative.replace(/[\\/]+/g, path.sep).replace(/^[\\/]+/, "");
2792
+ const candidate = path.resolve(path.dirname(wrapperPath), normalizedRelative);
2793
+ if (isFilePath(candidate)) candidates.push(candidate);
2794
+ }
2795
+ return candidates.find((candidate) => {
2796
+ const base = path.basename(candidate).toLowerCase();
2797
+ return base !== "node.exe" && base !== "node";
2798
+ }) ?? null;
2799
+ } catch {
2800
+ return null;
2801
+ }
2802
+ }
2803
+ function resolveBinEntry(packageName, binField) {
2804
+ if (typeof binField === "string") return binField.trim() || null;
2805
+ if (!binField || typeof binField !== "object") return null;
2806
+ if (packageName) {
2807
+ const preferred = binField[packageName];
2808
+ if (typeof preferred === "string" && preferred.trim()) return preferred.trim();
2809
+ }
2810
+ for (const value of Object.values(binField)) if (typeof value === "string" && value.trim()) return value.trim();
2811
+ return null;
2812
+ }
2813
+ function resolveEntrypointFromPackageJson(wrapperPath, packageName) {
2814
+ if (!packageName) return null;
2815
+ const wrapperDir = path.dirname(wrapperPath);
2816
+ const packageDirs = [path.resolve(wrapperDir, "..", packageName), path.resolve(wrapperDir, "node_modules", packageName)];
2817
+ for (const packageDir of packageDirs) {
2818
+ const packageJsonPath = path.join(packageDir, "package.json");
2819
+ if (!isFilePath(packageJsonPath)) continue;
2820
+ try {
2821
+ const entryRel = resolveBinEntry(packageName, JSON.parse(readFileSync(packageJsonPath, "utf8")).bin);
2822
+ if (!entryRel) continue;
2823
+ const entryPath = path.resolve(packageDir, entryRel);
2824
+ if (isFilePath(entryPath)) return entryPath;
2825
+ } catch {}
2826
+ }
2827
+ return null;
2828
+ }
2829
+ function resolveWindowsSpawnProgramCandidate(params) {
2830
+ const platform = params.platform ?? process.platform;
2831
+ const env = params.env ?? process.env;
2832
+ const execPath = params.execPath ?? process.execPath;
2833
+ if (platform !== "win32") return {
2834
+ command: params.command,
2835
+ leadingArgv: [],
2836
+ resolution: "direct"
2837
+ };
2838
+ const resolvedCommand = resolveWindowsExecutablePath(params.command, env);
2839
+ const ext = path.extname(resolvedCommand).toLowerCase();
2840
+ if (ext === ".js" || ext === ".cjs" || ext === ".mjs") return {
2841
+ command: execPath,
2842
+ leadingArgv: [resolvedCommand],
2843
+ resolution: "node-entrypoint",
2844
+ windowsHide: true
2845
+ };
2846
+ if (ext === ".cmd" || ext === ".bat") {
2847
+ const entrypoint = resolveEntrypointFromCmdShim(resolvedCommand) ?? resolveEntrypointFromPackageJson(resolvedCommand, params.packageName);
2848
+ if (entrypoint) {
2849
+ if (path.extname(entrypoint).toLowerCase() === ".exe") return {
2850
+ command: entrypoint,
2851
+ leadingArgv: [],
2852
+ resolution: "exe-entrypoint",
2853
+ windowsHide: true
2854
+ };
2855
+ return {
2856
+ command: execPath,
2857
+ leadingArgv: [entrypoint],
2858
+ resolution: "node-entrypoint",
2859
+ windowsHide: true
2860
+ };
2861
+ }
2862
+ return {
2863
+ command: resolvedCommand,
2864
+ leadingArgv: [],
2865
+ resolution: "unresolved-wrapper"
2866
+ };
2867
+ }
2868
+ return {
2869
+ command: resolvedCommand,
2870
+ leadingArgv: [],
2871
+ resolution: "direct"
2872
+ };
2873
+ }
2874
+ function applyWindowsSpawnProgramPolicy(params) {
2875
+ if (params.candidate.resolution !== "unresolved-wrapper") return {
2876
+ command: params.candidate.command,
2877
+ leadingArgv: params.candidate.leadingArgv,
2878
+ resolution: params.candidate.resolution,
2879
+ windowsHide: params.candidate.windowsHide
2880
+ };
2881
+ if (params.allowShellFallback !== false) return {
2882
+ command: params.candidate.command,
2883
+ leadingArgv: [],
2884
+ resolution: "shell-fallback",
2885
+ shell: true
2886
+ };
2887
+ throw new Error(`${path.basename(params.candidate.command)} wrapper resolved, but no executable/Node entrypoint could be resolved without shell execution.`);
2888
+ }
2889
+ function resolveWindowsSpawnProgram(params) {
2890
+ return applyWindowsSpawnProgramPolicy({
2891
+ candidate: resolveWindowsSpawnProgramCandidate(params),
2892
+ allowShellFallback: params.allowShellFallback
2893
+ });
2894
+ }
2895
+ function materializeWindowsSpawnProgram(program, argv) {
2896
+ return {
2897
+ command: program.command,
2898
+ argv: [...program.leadingArgv, ...argv],
2899
+ resolution: program.resolution,
2900
+ shell: program.shell,
2901
+ windowsHide: program.windowsHide
2902
+ };
2903
+ }
2904
+ //#endregion
2905
+ //#region src/config/sessions/paths.ts
2906
+ function resolveAgentSessionsDir(agentId, env = process.env, homedir = () => resolveRequiredHomeDir(env, os.homedir)) {
2907
+ const root = resolveStateDir(env, homedir);
2908
+ const id = normalizeAgentId(agentId ?? "main");
2909
+ return path.join(root, "agents", id, "sessions");
2910
+ }
2911
+ function resolveSessionTranscriptsDirForAgent(agentId, env = process.env, homedir = () => resolveRequiredHomeDir(env, os.homedir)) {
2912
+ return resolveAgentSessionsDir(agentId, env, homedir);
2913
+ }
2914
+ function resolveDefaultSessionStorePath(agentId) {
2915
+ return path.join(resolveAgentSessionsDir(agentId), "sessions.json");
2916
+ }
2917
+ const MULTI_STORE_PATH_SENTINEL = "(multiple)";
2918
+ function resolveSessionFilePathOptions(params) {
2919
+ const agentId = params.agentId?.trim();
2920
+ const storePath = params.storePath?.trim();
2921
+ if (storePath && storePath !== MULTI_STORE_PATH_SENTINEL) {
2922
+ const sessionsDir = path.dirname(path.resolve(storePath));
2923
+ return agentId ? {
2924
+ sessionsDir,
2925
+ agentId
2926
+ } : { sessionsDir };
2927
+ }
2928
+ if (agentId) return { agentId };
2929
+ }
2930
+ const SAFE_SESSION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
2931
+ function validateSessionId(sessionId) {
2932
+ const trimmed = sessionId.trim();
2933
+ if (!SAFE_SESSION_ID_RE.test(trimmed)) throw new Error(`Invalid session ID: ${sessionId}`);
2934
+ return trimmed;
2935
+ }
2936
+ function resolveSessionsDir(opts) {
2937
+ const sessionsDir = opts?.sessionsDir?.trim();
2938
+ if (sessionsDir) return path.resolve(sessionsDir);
2939
+ return resolveAgentSessionsDir(opts?.agentId);
2940
+ }
2941
+ function resolvePathFromAgentSessionsDir(agentSessionsDir, candidateAbsPath) {
2942
+ const agentBase = safeRealpathSync(path.resolve(agentSessionsDir)) ?? path.resolve(agentSessionsDir);
2943
+ const realCandidate = safeRealpathSync(candidateAbsPath) ?? candidateAbsPath;
2944
+ const relative = path.relative(agentBase, realCandidate);
2945
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) return;
2946
+ return path.resolve(agentBase, relative);
2947
+ }
2948
+ function resolveSiblingAgentSessionsDir(baseSessionsDir, agentId) {
2949
+ const resolvedBase = path.resolve(baseSessionsDir);
2950
+ if (path.basename(resolvedBase) !== "sessions") return;
2951
+ const baseAgentDir = path.dirname(resolvedBase);
2952
+ const baseAgentsDir = path.dirname(baseAgentDir);
2953
+ if (path.basename(baseAgentsDir) !== "agents") return;
2954
+ const rootDir = path.dirname(baseAgentsDir);
2955
+ return path.join(rootDir, "agents", normalizeAgentId(agentId), "sessions");
2956
+ }
2957
+ function resolveAgentSessionsPathParts(candidateAbsPath) {
2958
+ const parts = path.normalize(path.resolve(candidateAbsPath)).split(path.sep).filter(Boolean);
2959
+ const sessionsIndex = parts.lastIndexOf("sessions");
2960
+ if (sessionsIndex < 2 || parts[sessionsIndex - 2] !== "agents") return null;
2961
+ return {
2962
+ parts,
2963
+ sessionsIndex
2964
+ };
2965
+ }
2966
+ function extractAgentIdFromAbsoluteSessionPath(candidateAbsPath) {
2967
+ const parsed = resolveAgentSessionsPathParts(candidateAbsPath);
2968
+ if (!parsed) return;
2969
+ const { parts, sessionsIndex } = parsed;
2970
+ return parts[sessionsIndex - 1] || void 0;
2971
+ }
2972
+ function resolveStructuralSessionFallbackPath(candidateAbsPath, expectedAgentId) {
2973
+ const parsed = resolveAgentSessionsPathParts(candidateAbsPath);
2974
+ if (!parsed) return;
2975
+ const { parts, sessionsIndex } = parsed;
2976
+ const agentIdPart = parts[sessionsIndex - 1];
2977
+ if (!agentIdPart) return;
2978
+ const normalizedAgentId = normalizeAgentId(agentIdPart);
2979
+ if (normalizedAgentId !== agentIdPart.toLowerCase()) return;
2980
+ if (normalizedAgentId !== normalizeAgentId(expectedAgentId)) return;
2981
+ const relativeSegments = parts.slice(sessionsIndex + 1);
2982
+ if (relativeSegments.length !== 1) return;
2983
+ const fileName = relativeSegments[0];
2984
+ if (!fileName || fileName === "." || fileName === "..") return;
2985
+ return path.normalize(path.resolve(candidateAbsPath));
2986
+ }
2987
+ function safeRealpathSync(filePath) {
2988
+ try {
2989
+ return fs.realpathSync(filePath);
2990
+ } catch {
2991
+ return;
2992
+ }
2993
+ }
2994
+ function resolvePathWithinSessionsDir(sessionsDir, candidate, opts) {
2995
+ const trimmed = candidate.trim();
2996
+ if (!trimmed) throw new Error("Session file path must not be empty");
2997
+ const resolvedBase = path.resolve(sessionsDir);
2998
+ const realBase = safeRealpathSync(resolvedBase) ?? resolvedBase;
2999
+ const realTrimmed = path.isAbsolute(trimmed) ? safeRealpathSync(trimmed) ?? trimmed : trimmed;
3000
+ const normalized = path.isAbsolute(realTrimmed) ? path.relative(realBase, realTrimmed) : realTrimmed;
3001
+ if (normalized.startsWith("..") && path.isAbsolute(realTrimmed)) {
3002
+ const tryAgentFallback = (agentId) => {
3003
+ const normalizedAgentId = normalizeAgentId(agentId);
3004
+ const siblingSessionsDir = resolveSiblingAgentSessionsDir(realBase, normalizedAgentId);
3005
+ if (siblingSessionsDir) {
3006
+ const siblingResolved = resolvePathFromAgentSessionsDir(siblingSessionsDir, realTrimmed);
3007
+ if (siblingResolved) return siblingResolved;
3008
+ }
3009
+ return resolvePathFromAgentSessionsDir(resolveAgentSessionsDir(normalizedAgentId), realTrimmed);
3010
+ };
3011
+ const explicitAgentId = opts?.agentId?.trim();
3012
+ if (explicitAgentId) {
3013
+ const resolvedFromAgent = tryAgentFallback(explicitAgentId);
3014
+ if (resolvedFromAgent) return resolvedFromAgent;
3015
+ }
3016
+ const extractedAgentId = extractAgentIdFromAbsoluteSessionPath(realTrimmed);
3017
+ if (extractedAgentId) {
3018
+ const resolvedFromPath = tryAgentFallback(extractedAgentId);
3019
+ if (resolvedFromPath) return resolvedFromPath;
3020
+ const structuralFallback = resolveStructuralSessionFallbackPath(realTrimmed, extractedAgentId);
3021
+ if (structuralFallback) return structuralFallback;
3022
+ }
3023
+ }
3024
+ if (!normalized || normalized.startsWith("..") || path.isAbsolute(normalized)) throw new Error("Session file path must be within sessions directory");
3025
+ return path.resolve(realBase, normalized);
3026
+ }
3027
+ function resolveSessionTranscriptPathInDir(sessionId, sessionsDir, topicId) {
3028
+ const safeSessionId = validateSessionId(sessionId);
3029
+ const safeTopicId = typeof topicId === "string" ? encodeURIComponent(topicId) : typeof topicId === "number" ? String(topicId) : void 0;
3030
+ return resolvePathWithinSessionsDir(sessionsDir, safeTopicId !== void 0 ? `${safeSessionId}-topic-${safeTopicId}.jsonl` : `${safeSessionId}.jsonl`);
3031
+ }
3032
+ function resolveSessionTranscriptPath(sessionId, agentId, topicId) {
3033
+ return resolveSessionTranscriptPathInDir(sessionId, resolveAgentSessionsDir(agentId), topicId);
3034
+ }
3035
+ function resolveSessionFilePath(sessionId, entry, opts) {
3036
+ const sessionsDir = resolveSessionsDir(opts);
3037
+ const candidate = entry?.sessionFile?.trim();
3038
+ if (candidate) try {
3039
+ return resolvePathWithinSessionsDir(sessionsDir, candidate, { agentId: opts?.agentId });
3040
+ } catch {}
3041
+ return resolveSessionTranscriptPathInDir(sessionId, sessionsDir);
3042
+ }
3043
+ function resolveStorePath(store, opts) {
3044
+ const agentId = normalizeAgentId(opts?.agentId ?? "main");
3045
+ if (!store) return resolveDefaultSessionStorePath(agentId);
3046
+ if (store.includes("{agentId}")) {
3047
+ const expanded = store.replaceAll("{agentId}", agentId);
3048
+ if (expanded.startsWith("~")) return path.resolve(expandHomePrefix(expanded, {
3049
+ home: resolveRequiredHomeDir(process.env, os.homedir),
3050
+ env: process.env,
3051
+ homedir: os.homedir
3052
+ }));
3053
+ return path.resolve(expanded);
3054
+ }
3055
+ if (store.startsWith("~")) return path.resolve(expandHomePrefix(store, {
3056
+ home: resolveRequiredHomeDir(process.env, os.homedir),
3057
+ env: process.env,
3058
+ homedir: os.homedir
3059
+ }));
3060
+ return path.resolve(store);
3061
+ }
3062
+ //#endregion
3063
+ export { loadWorkspaceBootstrapFiles as $, runTasksWithConcurrency as A, normalizeMainKey as At, resolveRunModelFallbacksOverride as B, getSubagentDepth as Bt, PATH_ALIAS_POLICIES as C, DEFAULT_MAIN_KEY as Ct, redactToolDetail as D, classifySessionKeyShape as Dt, redactSensitiveText as E, buildGroupHistoryKey as Et, resolveAgentEffectiveModelPrimary as F, DEFAULT_ACCOUNT_ID as Ft, DEFAULT_BOOTSTRAP_FILENAME as G, resolveThreadParentSessionKey as Gt, resolveSessionAgentIds as H, isCronSessionKey as Ht, resolveAgentSkillsFilter as I, normalizeAccountId as It, DEFAULT_SOUL_FILENAME as J, DEFAULT_HEARTBEAT_FILENAME as K, resolveAgentWorkspaceDir as L, normalizeOptionalAccountId as Lt, listAgentIds as M, resolveThreadSessionKeys as Mt, resolveAgentConfig as N, sanitizeAgentId as Nt, compileSafeRegex as O, isValidAgentId as Ot, resolveAgentDir as P, scopedHeartbeatWakeOptions as Pt, filterBootstrapFilesForSession as Q, resolveDefaultAgentId as R, isBlockedObjectKey as Rt, writeFileWithinRoot as S, DEFAULT_AGENT_ID as St, getDefaultRedactPatterns as T, buildAgentPeerSessionKey as Tt, DEFAULT_AGENTS_FILENAME as U, isSubagentSessionKey as Ut, resolveSessionAgentId as V, isAcpSessionKey as Vt, DEFAULT_AGENT_WORKSPACE_DIR as W, parseAgentSessionKey as Wt, DEFAULT_USER_FILENAME as X, DEFAULT_TOOLS_FILENAME as Y, ensureAgentWorkspace as Z, openFileWithinRoot as _, normalizeStringEntriesLower as _t, resolveSessionTranscriptPathInDir as a, canUseBoundaryFileOpen as at, readLocalFileSafely as b, toAgentModelListLike as bt, applyWindowsSpawnProgramPolicy as c, sameFileIdentity$1 as ct, resolveWindowsSpawnProgram as d, isPathInside as dt, resolveOpenClawPackageRoot as et, resolveWindowsSpawnProgramCandidate as f, normalizeWindowsPathForComparison as ft, createRootScopedReadFile as g, normalizeStringEntries as gt, copyFileWithinRoot as h, normalizeHyphenSlug as ht, resolveSessionTranscriptPath as i, spawnWithFallback as it, hasConfiguredModelFallbacks as j, resolveAgentIdFromSessionKey as jt, testRegexWithBoundedInput as k, normalizeAgentId as kt, materializeWindowsSpawnProgram as l, resolvePathViaExistingAncestorSync as lt, appendFileWithinRoot as m, normalizeAtHashSlug as mt, resolveSessionFilePath as n, runCommandWithTimeout as nt, resolveSessionTranscriptsDirForAgent as o, openBoundaryFile as ot, SafeOpenError as p, normalizeSkillFilter as pt, DEFAULT_IDENTITY_FILENAME as q, resolveSessionFilePathOptions as r, runExec as rt, resolveStorePath as s, openBoundaryFileSync as st, resolveDefaultSessionStorePath as t, resolveOpenClawPackageRootSync as tt, resolveWindowsExecutablePath as u, isNotFoundPathError as ut, openWritableFileWithinRoot as v, resolveAgentModelFallbackValues as vt, assertNoPathAliasEscape as w, buildAgentMainSessionKey as wt, writeFileFromPathWithinRoot as x, markOpenClawExecEnv as xt, readFileWithinRoot as y, resolveAgentModelPrimaryValue as yt, resolveEffectiveModelFallbacks as z, deriveSessionChatType as zt };