context-mode 1.0.162 → 1.0.163

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 (148) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  5. package/.openclaw-plugin/package.json +1 -1
  6. package/README.md +142 -28
  7. package/bin/statusline.mjs +24 -4
  8. package/build/adapters/antigravity/index.d.ts +1 -1
  9. package/build/adapters/antigravity-cli/index.d.ts +51 -0
  10. package/build/adapters/antigravity-cli/index.js +341 -0
  11. package/build/adapters/claude-code/hooks.d.ts +1 -0
  12. package/build/adapters/claude-code/hooks.js +3 -0
  13. package/build/adapters/claude-code/index.js +24 -5
  14. package/build/adapters/client-map.js +5 -0
  15. package/build/adapters/codex/hooks.d.ts +5 -1
  16. package/build/adapters/codex/hooks.js +5 -1
  17. package/build/adapters/codex/index.d.ts +9 -1
  18. package/build/adapters/codex/index.js +87 -5
  19. package/build/adapters/copilot-cli/hooks.d.ts +33 -0
  20. package/build/adapters/copilot-cli/hooks.js +64 -0
  21. package/build/adapters/copilot-cli/index.d.ts +48 -0
  22. package/build/adapters/copilot-cli/index.js +341 -0
  23. package/build/adapters/detect.d.ts +1 -1
  24. package/build/adapters/detect.js +71 -3
  25. package/build/adapters/openclaw/mcp-tools.js +1 -1
  26. package/build/adapters/opencode/index.js +31 -17
  27. package/build/adapters/opencode/zod3tov4.js +27 -6
  28. package/build/adapters/pi/extension.d.ts +2 -12
  29. package/build/adapters/pi/extension.js +114 -96
  30. package/build/adapters/types.d.ts +5 -4
  31. package/build/adapters/types.js +4 -3
  32. package/build/cache-heal.d.ts +48 -0
  33. package/build/cache-heal.js +150 -0
  34. package/build/cli.js +37 -97
  35. package/build/executor.d.ts +25 -0
  36. package/build/executor.js +143 -22
  37. package/build/opencode-plugin.js +5 -2
  38. package/build/routing-block.d.ts +8 -0
  39. package/build/routing-block.js +86 -0
  40. package/build/runtime.d.ts +0 -36
  41. package/build/runtime.js +107 -27
  42. package/build/search/flood-guard.d.ts +57 -0
  43. package/build/search/flood-guard.js +80 -0
  44. package/build/security.d.ts +8 -3
  45. package/build/security.js +155 -29
  46. package/build/server.d.ts +14 -0
  47. package/build/server.js +368 -350
  48. package/build/session/analytics.d.ts +1 -1
  49. package/build/session/analytics.js +5 -1
  50. package/build/session/db.js +23 -3
  51. package/build/session/extract.js +8 -0
  52. package/build/store.d.ts +1 -1
  53. package/build/store.js +139 -25
  54. package/build/tool-naming.d.ts +4 -0
  55. package/build/tool-naming.js +24 -0
  56. package/build/util/jsonc.d.ts +14 -0
  57. package/build/util/jsonc.js +104 -0
  58. package/cli.bundle.mjs +254 -252
  59. package/configs/antigravity/GEMINI.md +2 -2
  60. package/configs/antigravity-cli/hooks/hooks.json +37 -0
  61. package/configs/antigravity-cli/hooks.json +37 -0
  62. package/configs/antigravity-cli/mcp_config.json +10 -0
  63. package/configs/antigravity-cli/plugin.json +14 -0
  64. package/configs/antigravity-cli/rules/context-mode.md +77 -0
  65. package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
  66. package/configs/claude-code/CLAUDE.md +2 -2
  67. package/configs/codex/AGENTS.md +2 -2
  68. package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
  69. package/configs/copilot-cli/.mcp.json +12 -0
  70. package/configs/copilot-cli/README.md +47 -0
  71. package/configs/copilot-cli/hooks.json +41 -0
  72. package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
  73. package/configs/gemini-cli/GEMINI.md +2 -2
  74. package/configs/jetbrains-copilot/copilot-instructions.md +2 -2
  75. package/configs/kilo/AGENTS.md +2 -2
  76. package/configs/kiro/KIRO.md +2 -2
  77. package/configs/omp/SYSTEM.md +2 -2
  78. package/configs/openclaw/AGENTS.md +2 -2
  79. package/configs/opencode/AGENTS.md +2 -2
  80. package/configs/qwen-code/QWEN.md +2 -2
  81. package/configs/vscode-copilot/copilot-instructions.md +2 -2
  82. package/configs/zed/AGENTS.md +2 -2
  83. package/hooks/antigravity-cli/payload.mjs +98 -0
  84. package/hooks/antigravity-cli/posttooluse.mjs +138 -0
  85. package/hooks/antigravity-cli/pretooluse.mjs +78 -0
  86. package/hooks/antigravity-cli/stop.mjs +58 -0
  87. package/hooks/codex/pretooluse.mjs +14 -4
  88. package/hooks/codex/stop.mjs +12 -4
  89. package/hooks/copilot-cli/posttooluse.mjs +79 -0
  90. package/hooks/copilot-cli/precompact.mjs +66 -0
  91. package/hooks/copilot-cli/pretooluse.mjs +41 -0
  92. package/hooks/copilot-cli/sessionstart.mjs +121 -0
  93. package/hooks/copilot-cli/stop.mjs +59 -0
  94. package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
  95. package/hooks/core/codex-caps.mjs +112 -0
  96. package/hooks/core/formatters.mjs +158 -7
  97. package/hooks/core/mcp-ready.mjs +37 -8
  98. package/hooks/core/routing.mjs +94 -8
  99. package/hooks/core/tool-naming.mjs +3 -0
  100. package/hooks/hooks.json +12 -1
  101. package/hooks/pretooluse.mjs +6 -2
  102. package/hooks/routing-block.mjs +2 -2
  103. package/hooks/security.bundle.mjs +2 -1
  104. package/hooks/session-db.bundle.mjs +5 -5
  105. package/hooks/session-directive.mjs +88 -20
  106. package/hooks/session-extract.bundle.mjs +1 -1
  107. package/hooks/session-helpers.mjs +21 -0
  108. package/hooks/sessionstart.mjs +37 -5
  109. package/hooks/stop.mjs +49 -0
  110. package/openclaw.plugin.json +1 -1
  111. package/package.json +4 -10
  112. package/scripts/install-antigravity-cli-plugin.mjs +141 -0
  113. package/server.bundle.mjs +208 -203
  114. package/skills/ctx-insight/SKILL.md +12 -17
  115. package/build/util/db-lock.d.ts +0 -65
  116. package/build/util/db-lock.js +0 -166
  117. package/insight/index.html +0 -13
  118. package/insight/package.json +0 -55
  119. package/insight/server.mjs +0 -1265
  120. package/insight/src/components/analytics.tsx +0 -112
  121. package/insight/src/components/ui/badge.tsx +0 -52
  122. package/insight/src/components/ui/button.tsx +0 -58
  123. package/insight/src/components/ui/card.tsx +0 -103
  124. package/insight/src/components/ui/chart.tsx +0 -371
  125. package/insight/src/components/ui/collapsible.tsx +0 -19
  126. package/insight/src/components/ui/input.tsx +0 -20
  127. package/insight/src/components/ui/progress.tsx +0 -83
  128. package/insight/src/components/ui/scroll-area.tsx +0 -55
  129. package/insight/src/components/ui/separator.tsx +0 -23
  130. package/insight/src/components/ui/table.tsx +0 -114
  131. package/insight/src/components/ui/tabs.tsx +0 -82
  132. package/insight/src/components/ui/tooltip.tsx +0 -64
  133. package/insight/src/lib/api.ts +0 -144
  134. package/insight/src/lib/utils.ts +0 -6
  135. package/insight/src/main.tsx +0 -22
  136. package/insight/src/routeTree.gen.ts +0 -189
  137. package/insight/src/router.tsx +0 -19
  138. package/insight/src/routes/__root.tsx +0 -55
  139. package/insight/src/routes/enterprise.tsx +0 -316
  140. package/insight/src/routes/index.tsx +0 -1482
  141. package/insight/src/routes/knowledge.tsx +0 -221
  142. package/insight/src/routes/knowledge_.$dbHash.$sourceId.tsx +0 -137
  143. package/insight/src/routes/search.tsx +0 -97
  144. package/insight/src/routes/sessions.tsx +0 -179
  145. package/insight/src/routes/sessions_.$dbHash.$sessionId.tsx +0 -181
  146. package/insight/src/styles.css +0 -104
  147. package/insight/tsconfig.json +0 -29
  148. package/insight/vite.config.ts +0 -19
package/build/security.js CHANGED
@@ -120,11 +120,18 @@ export function matchesAnyPattern(command, patterns, caseInsensitive = false) {
120
120
  return null;
121
121
  }
122
122
  // ==============================================================================
123
- // Chained Command Splitting
123
+ // Chained Command Splitting & Subshell Extraction
124
124
  // ==============================================================================
125
+ function isEscaped(command, index) {
126
+ let backslashes = 0;
127
+ for (let i = index - 1; i >= 0 && command[i] === "\\"; i--) {
128
+ backslashes++;
129
+ }
130
+ return backslashes % 2 === 1;
131
+ }
125
132
  /**
126
- * Split a shell command on chain operators (&&, ||, ;, |) while
127
- * respecting single/double quotes and backticks.
133
+ * Split a shell command on chain operators (&&, ||, ;, |, \n, \r, &) while
134
+ * respecting single/double quotes, backticks, subshells, and escape backslashes.
128
135
  *
129
136
  * "echo hello && sudo rm -rf /" → ["echo hello", "sudo rm -rf /"]
130
137
  *
@@ -136,37 +143,57 @@ export function splitChainedCommands(command) {
136
143
  let inSingle = false;
137
144
  let inDouble = false;
138
145
  let inBacktick = false;
146
+ let dollarParenDepth = 0;
139
147
  for (let i = 0; i < command.length; i++) {
140
148
  const ch = command[i];
141
- const prev = i > 0 ? command[i - 1] : "";
142
- if (ch === "'" && !inDouble && !inBacktick && prev !== "\\") {
149
+ const escaped = isEscaped(command, i);
150
+ if (ch === "'" && !inDouble && !inBacktick && !escaped) {
143
151
  inSingle = !inSingle;
144
152
  current += ch;
145
153
  }
146
- else if (ch === '"' && !inSingle && !inBacktick && prev !== "\\") {
154
+ else if (ch === '"' && !inSingle && !inBacktick && !escaped) {
147
155
  inDouble = !inDouble;
148
156
  current += ch;
149
157
  }
150
- else if (ch === "`" && !inSingle && !inDouble && prev !== "\\") {
158
+ else if (ch === "`" && !inSingle && !inDouble && !escaped) {
151
159
  inBacktick = !inBacktick;
152
160
  current += ch;
153
161
  }
154
162
  else if (!inSingle && !inDouble && !inBacktick) {
155
- if (ch === ";") {
163
+ if (ch === "$" && command[i + 1] === "(" && !escaped) {
164
+ dollarParenDepth++;
165
+ current += ch + command[i + 1];
166
+ i++;
167
+ }
168
+ else if (dollarParenDepth > 0 && ch === "(" && !escaped) {
169
+ dollarParenDepth++;
170
+ current += ch;
171
+ }
172
+ else if (ch === ")" && dollarParenDepth > 0 && !escaped) {
173
+ dollarParenDepth--;
174
+ current += ch;
175
+ }
176
+ else if (dollarParenDepth === 0 &&
177
+ (ch === ";" || ch === "\n" || ch === "\r") &&
178
+ !escaped) {
156
179
  parts.push(current.trim());
157
180
  current = "";
158
181
  }
159
- else if (ch === "|" && command[i + 1] === "|") {
182
+ else if (dollarParenDepth === 0 && ch === "|" && command[i + 1] === "|") {
160
183
  parts.push(current.trim());
161
184
  current = "";
162
185
  i++; // skip second |
163
186
  }
164
- else if (ch === "&" && command[i + 1] === "&") {
187
+ else if (dollarParenDepth === 0 && ch === "&" && command[i + 1] === "&") {
165
188
  parts.push(current.trim());
166
189
  current = "";
167
190
  i++; // skip second &
168
191
  }
169
- else if (ch === "|") {
192
+ else if (dollarParenDepth === 0 && ch === "&" && !escaped) {
193
+ parts.push(current.trim());
194
+ current = "";
195
+ }
196
+ else if (dollarParenDepth === 0 && ch === "|") {
170
197
  // Single pipe — left side is a command too
171
198
  parts.push(current.trim());
172
199
  current = "";
@@ -183,6 +210,83 @@ export function splitChainedCommands(command) {
183
210
  parts.push(current.trim());
184
211
  return parts.filter((p) => p.length > 0);
185
212
  }
213
+ /**
214
+ * Recursively extract all nested subshell commands from `$()` and `` `...` ``.
215
+ * Handles escaping and quote contexts to ensure correct command boundary detection.
216
+ */
217
+ export function extractSubshellCommands(command) {
218
+ const subshells = [];
219
+ let inSingle = false;
220
+ let inDouble = false;
221
+ let backtickStart = -1;
222
+ const dollarParenStarts = [];
223
+ const dollarParenDepths = [];
224
+ let parenDepth = 0;
225
+ for (let i = 0; i < command.length; i++) {
226
+ const ch = command[i];
227
+ const escaped = isEscaped(command, i);
228
+ if (ch === "'" && !inDouble && backtickStart === -1 && !escaped) {
229
+ inSingle = !inSingle;
230
+ }
231
+ else if (ch === '"' && !inSingle && backtickStart === -1 && !escaped) {
232
+ inDouble = !inDouble;
233
+ }
234
+ else if (ch === "`" && !inSingle && !inDouble && !escaped) {
235
+ if (backtickStart === -1) {
236
+ backtickStart = i + 1;
237
+ }
238
+ else {
239
+ const sub = command.slice(backtickStart, i);
240
+ subshells.push(sub);
241
+ subshells.push(...extractSubshellCommands(sub));
242
+ backtickStart = -1;
243
+ }
244
+ }
245
+ else if (!inSingle && backtickStart === -1) {
246
+ if (ch === "$" && command[i + 1] === "(" && !escaped) {
247
+ if (command[i + 2] === "(") {
248
+ // Arithmetic expansion is not command execution, but nested command
249
+ // substitutions inside it still get discovered by the scanner.
250
+ parenDepth += 2;
251
+ i += 2; // skip '(('
252
+ }
253
+ else {
254
+ dollarParenStarts.push(i + 2);
255
+ dollarParenDepths.push(parenDepth);
256
+ parenDepth++;
257
+ i++; // skip '('
258
+ }
259
+ }
260
+ else if (ch === "(" && !escaped) {
261
+ parenDepth++;
262
+ }
263
+ else if (ch === ")" && !escaped) {
264
+ if (parenDepth > 0) {
265
+ parenDepth--;
266
+ }
267
+ if (dollarParenDepths.length > 0 &&
268
+ parenDepth === dollarParenDepths[dollarParenDepths.length - 1]) {
269
+ dollarParenDepths.pop();
270
+ const start = dollarParenStarts.pop();
271
+ const sub = command.slice(start, i);
272
+ subshells.push(sub);
273
+ }
274
+ }
275
+ }
276
+ }
277
+ return subshells;
278
+ }
279
+ function collectCommandElements(command) {
280
+ const elements = [];
281
+ const segments = splitChainedCommands(command);
282
+ for (const segment of segments) {
283
+ elements.push(segment);
284
+ for (const subshell of extractSubshellCommands(segment)) {
285
+ elements.push(...collectCommandElements(subshell));
286
+ }
287
+ }
288
+ return elements;
289
+ }
186
290
  // ==============================================================================
187
291
  // Settings Reader
188
292
  // ==============================================================================
@@ -326,24 +430,46 @@ export function readToolDenyPatterns(toolName, projectDir, globalSettingsPath) {
326
430
  * First definitive match across policies wins.
327
431
  * Default (no match in any policy): "ask".
328
432
  */
329
- export function evaluateCommand(command, policies, caseInsensitive = process.platform === "win32") {
330
- // Check each segment of chained commands against deny patterns
331
- const segments = splitChainedCommands(command);
332
- for (const segment of segments) {
433
+ export function evaluateCommand(command, policies, caseInsensitive = process.platform === "win32" || process.platform === "darwin") {
434
+ // Extract all main segments and nested subshell commands
435
+ const allCommands = collectCommandElements(command);
436
+ // 1. Deny check: If ANY segment or subshell command is denied, block the entire command
437
+ for (const cmdElement of allCommands) {
333
438
  for (const policy of policies) {
334
- const denyMatch = matchesAnyPattern(segment, policy.deny, caseInsensitive);
439
+ const denyMatch = matchesAnyPattern(cmdElement, policy.deny, caseInsensitive);
335
440
  if (denyMatch)
336
441
  return { decision: "deny", matchedPattern: denyMatch };
337
442
  }
338
443
  }
339
- // Check ask/allow against the full command (original behavior)
444
+ // 2. Allow/Ask check: Evaluate segment-by-segment in precedence order.
445
+ // The command is allowed if and only if EVERY segment and subshell is explicitly allowed.
446
+ // If any element matches an ask pattern or matches no allow pattern, it defaults to ask.
340
447
  for (const policy of policies) {
341
- const askMatch = matchesAnyPattern(command, policy.ask, caseInsensitive);
342
- if (askMatch)
343
- return { decision: "ask", matchedPattern: askMatch };
344
- const allowMatch = matchesAnyPattern(command, policy.allow, caseInsensitive);
345
- if (allowMatch)
346
- return { decision: "allow", matchedPattern: allowMatch };
448
+ let allAllowed = true;
449
+ let anyAsk = false;
450
+ let matchedAskPattern;
451
+ let matchedAllowPattern;
452
+ for (const cmdElement of allCommands) {
453
+ const askMatch = matchesAnyPattern(cmdElement, policy.ask, caseInsensitive);
454
+ if (askMatch) {
455
+ anyAsk = true;
456
+ matchedAskPattern = askMatch;
457
+ break; // Ask wins immediately within this policy
458
+ }
459
+ const allowMatch = matchesAnyPattern(cmdElement, policy.allow, caseInsensitive);
460
+ if (!allowMatch) {
461
+ allAllowed = false;
462
+ }
463
+ else {
464
+ matchedAllowPattern = allowMatch;
465
+ }
466
+ }
467
+ if (anyAsk) {
468
+ return { decision: "ask", matchedPattern: matchedAskPattern };
469
+ }
470
+ if (allAllowed && allCommands.length > 0) {
471
+ return { decision: "allow", matchedPattern: matchedAllowPattern };
472
+ }
347
473
  }
348
474
  return { decision: "ask" };
349
475
  }
@@ -353,13 +479,13 @@ export function evaluateCommand(command, policies, caseInsensitive = process.pla
353
479
  * The server has no UI for "ask" prompts, so allow/ask patterns are
354
480
  * irrelevant. Returns "deny" if any deny pattern matches, otherwise "allow".
355
481
  *
356
- * Also splits chained commands to prevent bypass.
482
+ * Also splits chained commands and nested subshells to prevent bypass.
357
483
  */
358
- export function evaluateCommandDenyOnly(command, policies, caseInsensitive = process.platform === "win32") {
359
- const segments = splitChainedCommands(command);
360
- for (const segment of segments) {
484
+ export function evaluateCommandDenyOnly(command, policies, caseInsensitive = process.platform === "win32" || process.platform === "darwin") {
485
+ const allCommands = collectCommandElements(command);
486
+ for (const cmdElement of allCommands) {
361
487
  for (const policy of policies) {
362
- const denyMatch = matchesAnyPattern(segment, policy.deny, caseInsensitive);
488
+ const denyMatch = matchesAnyPattern(cmdElement, policy.deny, caseInsensitive);
363
489
  if (denyMatch)
364
490
  return { decision: "deny", matchedPattern: denyMatch };
365
491
  }
@@ -391,7 +517,7 @@ export function evaluateCommandDenyOnly(command, policies, caseInsensitive = pro
391
517
  * still checked. This keeps the function usable for paths that will
392
518
  * be created during execution.
393
519
  */
394
- export function evaluateFilePath(filePath, denyGlobs, caseInsensitive = process.platform === "win32", projectRoot) {
520
+ export function evaluateFilePath(filePath, denyGlobs, caseInsensitive = process.platform === "win32" || process.platform === "darwin", projectRoot) {
395
521
  const toForward = (path) => path.replace(/\\/g, "/");
396
522
  // Match against the raw input, the lexically-resolved absolute path,
397
523
  // and the canonical (symlink-resolved) path when the file exists.
package/build/server.d.ts CHANGED
@@ -61,6 +61,8 @@ type ToolContextOverride = {
61
61
  sessionId?: string;
62
62
  };
63
63
  export declare function withProjectDirOverride<T>(projectDir: string | ToolContextOverride, fn: () => Promise<T>): Promise<T>;
64
+ export declare function sanitizeSchemaForStrictClients(node: unknown): unknown;
65
+ export declare function installStrictClientSchemaCompat(target?: McpServer): void;
64
66
  /**
65
67
  * Build the FK-attribution object passed to every ContentStore.index*() call
66
68
  * in this process. CLAUDE_SESSION_ID is the only MCP-side handle we have on
@@ -116,6 +118,7 @@ export interface BatchRunOptions {
116
118
  timeout: number | undefined;
117
119
  concurrency: number;
118
120
  nodeOptsPrefix: string;
121
+ cwd?: string;
119
122
  onFsBytes?: (bytes: number) => void;
120
123
  }
121
124
  interface BatchExecutor {
@@ -123,12 +126,23 @@ interface BatchExecutor {
123
126
  language: "shell";
124
127
  code: string;
125
128
  timeout: number | undefined;
129
+ cwd?: string;
126
130
  }): Promise<{
127
131
  stdout: string;
128
132
  timedOut?: boolean;
129
133
  }>;
130
134
  }
131
135
  export declare function buildBatchNodeOptionsPrefix(shellPath: string, preloadPath: string): string;
136
+ /**
137
+ * Default execution timeout (ms) applied ONLY under Antigravity CLI (`agy`).
138
+ * agy does not enforce an MCP RPC timeout, so a ctx_execute with a runaway or
139
+ * blocking script hangs forever — the host never kills it and the user must
140
+ * interrupt. Every other host enforces its own RPC timeout, so we keep the
141
+ * no-server-timer behavior there (Issue #406 — long builds need an unbounded
142
+ * run). A caller can still pass an explicit `timeout` to override on any host.
143
+ */
144
+ export declare const AGY_DEFAULT_EXEC_TIMEOUT_MS = 120000;
145
+ export declare function resolveExecTimeout(timeout: number | undefined): number | undefined;
132
146
  /**
133
147
  * Execute batch commands. concurrency=1 preserves the legacy serial path
134
148
  * (shared timeout budget + cascading skip-on-timeout). concurrency>1 runs