@sayknow-cli/coding-agent 0.3.10 → 0.3.12

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 (81) hide show
  1. package/CHANGELOG.md +0 -13
  2. package/dist/types/build-metadata.d.ts +7 -0
  3. package/dist/types/capability/mcp.d.ts +3 -1
  4. package/dist/types/commands/mcp.d.ts +1 -1
  5. package/dist/types/config/keybindings.d.ts +2 -2
  6. package/dist/types/modes/acp/acp-event-mapper.d.ts +2 -0
  7. package/dist/types/modes/components/hook-selector.d.ts +1 -0
  8. package/dist/types/modes/components/status-line/types.d.ts +6 -0
  9. package/dist/types/modes/components/status-line.d.ts +1 -0
  10. package/dist/types/modes/components/welcome.d.ts +1 -0
  11. package/dist/types/modes/rpc/rpc-client.d.ts +7 -2
  12. package/dist/types/modes/utils/ui-helpers.d.ts +12 -0
  13. package/dist/types/notifications/telegram-daemon.d.ts +2 -0
  14. package/dist/types/runtime-mcp/config-writer.d.ts +4 -3
  15. package/dist/types/runtime-mcp/types.d.ts +4 -3
  16. package/dist/types/session/agent-session.d.ts +16 -0
  17. package/dist/types/session/session-manager.d.ts +1 -0
  18. package/dist/types/tools/skill.d.ts +9 -8
  19. package/dist/types/utils/linkedom.d.ts +4 -0
  20. package/dist/types/utils/pasted-image-path.d.ts +5 -3
  21. package/package.json +7 -7
  22. package/scripts/build-binary.ts +2 -43
  23. package/scripts/compile-args.ts +106 -0
  24. package/scripts/generate-docs-index.ts +1 -0
  25. package/src/build-metadata.ts +104 -0
  26. package/src/capability/mcp.ts +3 -1
  27. package/src/cli/mcp-cli.ts +108 -26
  28. package/src/cli.ts +21 -0
  29. package/src/commands/mcp.ts +8 -8
  30. package/src/commands/stats.ts +6 -2
  31. package/src/config/keybindings.ts +2 -2
  32. package/src/config/mcp-schema.json +21 -17
  33. package/src/config/model-profiles.ts +23 -23
  34. package/src/discovery/builtin.ts +14 -0
  35. package/src/discovery/mcp-json.ts +14 -0
  36. package/src/eval/js/executor.ts +7 -1
  37. package/src/internal-urls/docs-index.generated.ts +3 -2
  38. package/src/internal-urls/skc-protocol.ts +14 -1
  39. package/src/modes/acp/acp-agent.ts +72 -16
  40. package/src/modes/acp/acp-client-bridge.ts +27 -5
  41. package/src/modes/acp/acp-event-mapper.ts +39 -1
  42. package/src/modes/components/assistant-message.ts +1 -1
  43. package/src/modes/components/hook-selector.ts +4 -0
  44. package/src/modes/components/model-selector.ts +17 -6
  45. package/src/modes/components/session-selector.ts +2 -2
  46. package/src/modes/components/status-line/presets.ts +5 -19
  47. package/src/modes/components/status-line/segments.ts +14 -0
  48. package/src/modes/components/status-line/types.ts +6 -0
  49. package/src/modes/components/status-line.ts +13 -6
  50. package/src/modes/components/tool-execution.ts +1 -1
  51. package/src/modes/components/welcome.ts +4 -1
  52. package/src/modes/controllers/command-controller.ts +1 -1
  53. package/src/modes/controllers/event-controller.ts +9 -9
  54. package/src/modes/controllers/input-controller.ts +8 -2
  55. package/src/modes/controllers/selector-controller.ts +7 -3
  56. package/src/modes/interactive-mode.ts +12 -4
  57. package/src/modes/rpc/rpc-client.ts +30 -6
  58. package/src/modes/utils/ui-helpers.ts +154 -32
  59. package/src/notifications/telegram-daemon.ts +28 -2
  60. package/src/prompts/tools/skill.md +2 -2
  61. package/src/runtime-mcp/config-writer.ts +4 -3
  62. package/src/runtime-mcp/config.ts +1 -0
  63. package/src/runtime-mcp/types.ts +4 -3
  64. package/src/session/agent-session.ts +117 -3
  65. package/src/session/session-manager.ts +68 -40
  66. package/src/skc-runtime/state-runtime.ts +105 -12
  67. package/src/skc-runtime/workflow-manifest.generated.json +4 -28
  68. package/src/skc-runtime/workflow-manifest.ts +1 -7
  69. package/src/slash-commands/builtin-registry.ts +9 -4
  70. package/src/tools/bash-interactive.ts +9 -4
  71. package/src/tools/browser/readable.ts +2 -2
  72. package/src/tools/fetch.ts +5 -5
  73. package/src/tools/skill.ts +14 -11
  74. package/src/utils/linkedom.ts +14 -0
  75. package/src/utils/pasted-image-path.ts +23 -9
  76. package/src/web/scrapers/arxiv.ts +2 -2
  77. package/src/web/scrapers/go-pkg.ts +2 -2
  78. package/src/web/scrapers/iacr.ts +2 -2
  79. package/src/web/scrapers/readthedocs.ts +2 -2
  80. package/src/web/scrapers/twitter.ts +3 -2
  81. package/src/web/scrapers/wikipedia.ts +2 -2
@@ -37,14 +37,27 @@ interface ScopedPath {
37
37
  path: string;
38
38
  }
39
39
 
40
+ interface RuntimeDisclosure {
41
+ runtimeStatus: "storage-only";
42
+ runtimeLoadedByStandalone: false;
43
+ runtimeNote: string;
44
+ }
45
+
40
46
  interface RedactedServerEntry {
41
47
  name: string;
42
48
  config: MCPServerConfig;
43
49
  }
44
50
 
51
+ interface RuntimeRedactedServerEntry extends RedactedServerEntry, RuntimeDisclosure {}
52
+
45
53
  const REDACTED = "<redacted>";
46
54
  const SENSITIVE_KEY_PATTERN =
47
55
  /(?:token|secret|key|credential|password|passwd|pwd|authorization|auth|bearer|cookie|session)/i;
56
+ const STORAGE_ONLY_RUNTIME_DISCLOSURE: RuntimeDisclosure = {
57
+ runtimeStatus: "storage-only",
58
+ runtimeLoadedByStandalone: false,
59
+ runtimeNote: "Stored MCP registrations are not loaded by normal standalone skc sessions today.",
60
+ };
48
61
 
49
62
  function resolvePath(args: MCPCommandArgs): ScopedPath {
50
63
  const scope = args.flags.project ? "project" : "user";
@@ -128,6 +141,50 @@ function redactRecord(
128
141
  );
129
142
  }
130
143
 
144
+ function redactUrl(value: string | undefined): string | undefined {
145
+ if (!value) return value;
146
+ try {
147
+ const url = new URL(value);
148
+ if (url.username) url.username = REDACTED;
149
+ if (url.password) url.password = REDACTED;
150
+ if (url.search) {
151
+ for (const key of Array.from(url.searchParams.keys())) {
152
+ url.searchParams.set(key, REDACTED);
153
+ }
154
+ }
155
+ url.hash = "";
156
+ return url.toString();
157
+ } catch {
158
+ return SENSITIVE_KEY_PATTERN.test(value) ? REDACTED : value;
159
+ }
160
+ }
161
+
162
+ function redactArgs(args: string[] | undefined): string[] | undefined {
163
+ if (!args) return undefined;
164
+ const redacted: string[] = [];
165
+ let redactNext = false;
166
+ for (const arg of args) {
167
+ if (redactNext) {
168
+ redacted.push(REDACTED);
169
+ redactNext = false;
170
+ continue;
171
+ }
172
+ const equalsIndex = arg.indexOf("=");
173
+ if (equalsIndex > 0) {
174
+ const key = arg.slice(0, equalsIndex);
175
+ redacted.push(SENSITIVE_KEY_PATTERN.test(key) ? `${key}=${REDACTED}` : arg);
176
+ continue;
177
+ }
178
+ if (arg.startsWith("-") && SENSITIVE_KEY_PATTERN.test(arg)) {
179
+ redacted.push(arg);
180
+ redactNext = true;
181
+ continue;
182
+ }
183
+ redacted.push(SENSITIVE_KEY_PATTERN.test(arg) ? REDACTED : arg);
184
+ }
185
+ return redacted;
186
+ }
187
+
131
188
  export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig {
132
189
  const redacted = { ...config } as MCPServerConfig;
133
190
  if ("env" in redacted) {
@@ -138,11 +195,19 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
138
195
  const headers = redactRecord(redacted.headers, true);
139
196
  if (headers) redacted.headers = headers;
140
197
  }
198
+ if ("url" in redacted) {
199
+ const url = redactUrl(redacted.url);
200
+ if (url) redacted.url = url;
201
+ }
202
+ if ("args" in redacted) {
203
+ const args = redactArgs(redacted.args);
204
+ if (args) redacted.args = args;
205
+ }
141
206
  if (redacted.auth) {
142
207
  redacted.auth = {
143
208
  type: redacted.auth.type,
144
209
  credentialId: redacted.auth.credentialId ? REDACTED : undefined,
145
- tokenUrl: redacted.auth.tokenUrl,
210
+ tokenUrl: redactUrl(redacted.auth.tokenUrl),
146
211
  clientId: redacted.auth.clientId ? REDACTED : undefined,
147
212
  clientSecret: redacted.auth.clientSecret ? REDACTED : undefined,
148
213
  };
@@ -151,7 +216,7 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
151
216
  redacted.oauth = {
152
217
  clientId: redacted.oauth.clientId ? REDACTED : undefined,
153
218
  clientSecret: redacted.oauth.clientSecret ? REDACTED : undefined,
154
- redirectUri: redacted.oauth.redirectUri,
219
+ redirectUri: redactUrl(redacted.oauth.redirectUri),
155
220
  callbackPort: redacted.oauth.callbackPort,
156
221
  callbackPath: redacted.oauth.callbackPath,
157
222
  };
@@ -159,10 +224,14 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
159
224
  return redacted;
160
225
  }
161
226
 
162
- function collectEntries(config: MCPConfigFile): RedactedServerEntry[] {
227
+ function withRuntimeDisclosure<T extends object>(value: T): T & RuntimeDisclosure {
228
+ return { ...value, ...STORAGE_ONLY_RUNTIME_DISCLOSURE };
229
+ }
230
+
231
+ function collectEntries(config: MCPConfigFile): RuntimeRedactedServerEntry[] {
163
232
  return Object.entries(config.mcpServers ?? {})
164
233
  .sort(([a], [b]) => a.localeCompare(b))
165
- .map(([name, serverConfig]) => ({ name, config: redactMCPServerConfig(serverConfig) }));
234
+ .map(([name, serverConfig]) => withRuntimeDisclosure({ name, config: redactMCPServerConfig(serverConfig) }));
166
235
  }
167
236
 
168
237
  function writeJson(value: unknown): void {
@@ -188,37 +257,46 @@ async function runAdd(args: MCPCommandArgs, scoped: ScopedPath): Promise<void> {
188
257
  const result = await upsertMCPServer(scoped.path, args.name, config, { force: args.flags.force });
189
258
  const redacted = redactMCPServerConfig(config);
190
259
  if (args.flags.json) {
191
- writeJson({
192
- action: "add",
193
- status: result.status,
194
- name: args.name,
195
- scope: scoped.scope,
196
- path: scoped.path,
197
- config: redacted,
198
- });
260
+ writeJson(
261
+ withRuntimeDisclosure({
262
+ action: "add",
263
+ status: result.status,
264
+ name: args.name,
265
+ scope: scoped.scope,
266
+ path: scoped.path,
267
+ config: redacted,
268
+ }),
269
+ );
199
270
  return;
200
271
  }
201
272
  if (result.status === "skipped") {
202
273
  process.stdout.write(
203
- `MCP server "${args.name}" already exists in ${scoped.scope} config. Pass --force to overwrite.\n`,
274
+ `MCP server "${args.name}" already exists in ${scoped.scope} config. Pass --force to overwrite. ` +
275
+ "Status: storage-only; normal standalone skc sessions do not load stored MCP registrations today.\n",
204
276
  );
205
277
  return;
206
278
  }
207
- process.stdout.write(`MCP server "${args.name}" ${result.status} in ${scoped.scope} config: ${scoped.path}\n`);
279
+ process.stdout.write(
280
+ `MCP server "${args.name}" ${result.status} in ${scoped.scope} config: ${scoped.path}\nStatus: storage-only; normal standalone skc sessions do not load stored MCP registrations today.\n`,
281
+ );
208
282
  }
209
283
 
210
284
  async function runList(args: MCPCommandArgs, scoped: ScopedPath): Promise<void> {
211
285
  const config = await readMCPConfigFile(scoped.path);
212
286
  const entries = collectEntries(config);
213
287
  if (args.flags.json) {
214
- writeJson({ action: "list", scope: scoped.scope, path: scoped.path, servers: entries });
288
+ writeJson(withRuntimeDisclosure({ action: "list", scope: scoped.scope, path: scoped.path, servers: entries }));
215
289
  return;
216
290
  }
217
291
  if (entries.length === 0) {
218
- process.stdout.write(`No MCP servers registered in ${scoped.scope} config: ${scoped.path}\n`);
292
+ process.stdout.write(
293
+ `No MCP servers registered in ${scoped.scope} config: ${scoped.path}\nStatus: storage-only; normal standalone skc sessions do not load stored MCP registrations today.\n`,
294
+ );
219
295
  return;
220
296
  }
221
- process.stdout.write(`MCP servers in ${scoped.scope} config: ${scoped.path}\n`);
297
+ process.stdout.write(
298
+ `MCP servers in ${scoped.scope} config: ${scoped.path}\nStatus: storage-only; normal standalone skc sessions do not load stored MCP registrations today.\n`,
299
+ );
222
300
  for (const entry of entries) {
223
301
  process.stdout.write(`${renderDetails(entry)}\n`);
224
302
  }
@@ -233,17 +311,21 @@ async function runRemove(args: MCPCommandArgs, scoped: ScopedPath): Promise<void
233
311
  await removeMCPServer(scoped.path, args.name);
234
312
  const entry = { name: args.name, config: redactMCPServerConfig(existing) };
235
313
  if (args.flags.json) {
236
- writeJson({
237
- action: "remove",
238
- status: "removed",
239
- name: args.name,
240
- scope: scoped.scope,
241
- path: scoped.path,
242
- removed: entry,
243
- });
314
+ writeJson(
315
+ withRuntimeDisclosure({
316
+ action: "remove",
317
+ status: "removed",
318
+ name: args.name,
319
+ scope: scoped.scope,
320
+ path: scoped.path,
321
+ removed: entry,
322
+ }),
323
+ );
244
324
  return;
245
325
  }
246
- process.stdout.write(`Removed MCP server "${args.name}" from ${scoped.scope} config: ${scoped.path}\n`);
326
+ process.stdout.write(
327
+ `Removed MCP server "${args.name}" from ${scoped.scope} config: ${scoped.path}\nStatus: storage-only; normal standalone skc sessions do not load stored MCP registrations today.\n`,
328
+ );
247
329
  process.stdout.write(`${renderDetails(entry)}\n`);
248
330
  }
249
331
 
package/src/cli.ts CHANGED
@@ -87,6 +87,23 @@ async function installRuntimeGlobals(): Promise<void> {
87
87
  delete process.env.MallocStackLoggingNoCompact;
88
88
  }
89
89
 
90
+ function isStatsHelpFastPath(argv: string[]): boolean {
91
+ return argv[0] === "stats" && (argv.includes("--help") || argv.includes("-h"));
92
+ }
93
+
94
+ function showStatsFastHelp(): void {
95
+ process.stdout.write(`Usage: ${APP_NAME} stats [options]
96
+
97
+ View usage statistics
98
+
99
+ Options:
100
+ -p, --port <number> Port for the dashboard server (default: 3847)
101
+ -j, --json Output stats as JSON
102
+ -s, --summary Print summary to console
103
+ -h, --help Show this help
104
+ `);
105
+ }
106
+
90
107
  function isNotifyDaemonInternalFastPath(argv: string[]): boolean {
91
108
  return argv[0] === "notify" && argv[1] === "daemon-internal";
92
109
  }
@@ -264,6 +281,10 @@ export async function runCli(argv: string[]): Promise<void> {
264
281
  process.exitCode = await runFixtureReport(id);
265
282
  return;
266
283
  }
284
+ if (isStatsHelpFastPath(argv)) {
285
+ showStatsFastHelp();
286
+ return;
287
+ }
267
288
  if (hasRootHelpFlag(argv)) {
268
289
  const { renderRootHelp } = await import("@sayknow-cli/utils/cli");
269
290
  const { getExtraHelpText } = await import("./cli/fast-help");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Direct MCP server registration for standalone SKC.
2
+ * Explicit MCP server config storage for standalone SKC.
3
3
  */
4
4
  import { Args, Command, Flags } from "@sayknow-cli/utils/cli";
5
5
  import { type MCPAction, type MCPCommandArgs, runMCPCommand } from "../cli/mcp-cli";
@@ -7,7 +7,7 @@ import { type MCPAction, type MCPCommandArgs, runMCPCommand } from "../cli/mcp-c
7
7
  const ACTIONS: MCPAction[] = ["add", "list", "remove"];
8
8
 
9
9
  export default class MCP extends Command {
10
- static description = "Register standalone MCP servers explicitly in SKC config";
10
+ static description = "Store standalone MCP server definitions without loading them at runtime";
11
11
  static delegateHelp = true;
12
12
 
13
13
  static examples = [
@@ -81,15 +81,15 @@ export default class MCP extends Command {
81
81
  }
82
82
 
83
83
  private printHelp(): void {
84
- process.stdout.write(`Register standalone MCP servers explicitly in SKC config
84
+ process.stdout.write(`Store standalone MCP server definitions in SKC config without loading them at runtime
85
85
 
86
86
  USAGE
87
87
  $ skc mcp [add|list|remove] [NAME] [COMMAND_OR_URL] [ARGS...] [FLAGS]
88
88
 
89
89
  COMMANDS
90
- add Add an explicit user-provided MCP server definition
91
- list List registered servers with env/header/auth values redacted
92
- remove Remove a registered server and print the removed definition redacted
90
+ add Store an explicit user-provided MCP server definition
91
+ list List stored servers with env/header/auth values redacted
92
+ remove Remove a stored server and print the removed definition redacted
93
93
 
94
94
  FLAGS
95
95
  --project Use project scope (./.skc/mcp.json) instead of user scope
@@ -110,8 +110,8 @@ EXAMPLES
110
110
  $ skc mcp list --json
111
111
  $ skc mcp remove context7
112
112
 
113
- SECURITY
114
- This command writes only the server definition supplied on this invocation. It does not import or inherit Claude Code, Codex, OpenCode, or other live MCP configs. Public output redacts env, header, auth, and OAuth credential values.
113
+ STORAGE/RUNTIME
114
+ This command stores only the server definition supplied on this invocation. It does not import or inherit Claude Code, Codex, OpenCode, or other live MCP configs. Normal standalone skc sessions do not load stored MCP registrations today. Public output redacts env, header, auth, and OAuth credential values.
115
115
  `);
116
116
  }
117
117
  }
@@ -2,8 +2,7 @@
2
2
  * View usage statistics dashboard.
3
3
  */
4
4
  import { Command, Flags } from "@sayknow-cli/utils/cli";
5
- import { runStatsCommand, type StatsCommandArgs } from "../cli/stats-cli";
6
- import { initTheme } from "../modes/theme/theme";
5
+ import type { StatsCommandArgs } from "../cli/stats-cli";
7
6
 
8
7
  export default class Stats extends Command {
9
8
  static description = "View usage statistics";
@@ -23,6 +22,11 @@ export default class Stats extends Command {
23
22
  summary: flags.summary,
24
23
  };
25
24
 
25
+ const [{ initTheme }, { runStatsCommand }] = await Promise.all([
26
+ import("../modes/theme/theme"),
27
+ import("../cli/stats-cli"),
28
+ ]);
29
+
26
30
  await initTheme();
27
31
  await runStatsCommand(cmd);
28
32
  }
@@ -188,11 +188,11 @@ export const KEYBINDINGS = {
188
188
  },
189
189
  "app.session.delete": {
190
190
  defaultKeys: "ctrl+d",
191
- description: "Delete session",
191
+ description: "Delete current session transcript/artifacts",
192
192
  },
193
193
  "app.session.deleteNoninvasive": {
194
194
  defaultKeys: "ctrl+backspace",
195
- description: "Delete session (non-invasive)",
195
+ description: "Delete selected session transcript/artifacts (non-invasive)",
196
196
  },
197
197
  "app.tree.foldOrUp": {
198
198
  defaultKeys: ["ctrl+left", "alt+left"],
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://raw.githubusercontent.com/jaybeyond/Sayknow_CLI/main/packages/coding-agent/src/config/mcp-schema.json",
4
4
  "title": "SKC MCP configuration",
5
- "description": "Schema for mcp.json, .mcp.json, .skc/mcp.json, and ~/.skc/agent/mcp.json used by the SKC coding agent.",
5
+ "description": "Schema for stored MCP server definitions in mcp.json, .mcp.json, .skc/mcp.json, and ~/.skc/agent/mcp.json. Normal standalone skc sessions do not consume skc mcp registrations as runtime tools today.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "properties": {
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "mcpServers": {
14
14
  "type": "object",
15
- "description": "Map of MCP server name to server configuration.",
15
+ "description": "Map of MCP server name to stored server configuration.",
16
16
  "propertyNames": {
17
17
  "pattern": "^[a-zA-Z0-9_.-]{1,100}$"
18
18
  },
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "disabledServers": {
24
24
  "type": "array",
25
- "description": "User-level denylist for disabling discovered servers by name.",
25
+ "description": "Denylist for explicit runtime MCP consumers that load discovered or stored servers by name.",
26
26
  "items": {
27
27
  "type": "string",
28
28
  "minLength": 1
@@ -94,11 +94,11 @@
94
94
  "properties": {
95
95
  "enabled": {
96
96
  "type": "boolean",
97
- "description": "Whether SKC should try to connect this server."
97
+ "description": "Whether explicit runtime MCP consumers should try to connect this server."
98
98
  },
99
99
  "autoload": {
100
100
  "type": "boolean",
101
- "description": "Whether to connect this server automatically at session startup (default: true). When false the server stays configured until connected explicitly (e.g. /mcp connect <name>)."
101
+ "description": "Whether an explicit runtime MCP consumer should connect this server automatically when that consumer starts (default: true). Normal standalone skc, skc --tmux, and print-mode sessions do not consume skc mcp registrations today; false keeps the server configured for consumers that support explicit connection."
102
102
  },
103
103
  "timeout": {
104
104
  "type": "number",
@@ -114,13 +114,13 @@
114
114
  }
115
115
  },
116
116
  "stdioServer": {
117
+ "type": "object",
118
+ "unevaluatedProperties": false,
117
119
  "allOf": [
118
120
  {
119
121
  "$ref": "#/$defs/serverBase"
120
122
  },
121
123
  {
122
- "type": "object",
123
- "additionalProperties": false,
124
124
  "required": ["command"],
125
125
  "properties": {
126
126
  "type": {
@@ -138,11 +138,15 @@
138
138
  "items": {
139
139
  "type": "string"
140
140
  },
141
- "description": "Arguments passed to the stdio server process."
141
+ "description": "Arguments passed to the stdio server process. Public CLI output redacts sensitive-looking argument values."
142
142
  },
143
143
  "env": {
144
144
  "$ref": "#/$defs/stringMap",
145
- "description": "Environment variables passed to the stdio process."
145
+ "description": "Environment variables passed to the stdio process. Public CLI output redacts values."
146
+ },
147
+ "noInheritEnv": {
148
+ "type": "boolean",
149
+ "description": "When true, explicit runtime consumers should avoid inheriting the host environment and pass only a minimal allowlist plus explicit env keys."
146
150
  },
147
151
  "cwd": {
148
152
  "type": "string",
@@ -156,13 +160,13 @@
156
160
  ]
157
161
  },
158
162
  "httpServer": {
163
+ "type": "object",
164
+ "unevaluatedProperties": false,
159
165
  "allOf": [
160
166
  {
161
167
  "$ref": "#/$defs/serverBase"
162
168
  },
163
169
  {
164
- "type": "object",
165
- "additionalProperties": false,
166
170
  "required": ["type", "url"],
167
171
  "properties": {
168
172
  "type": {
@@ -173,11 +177,11 @@
173
177
  "url": {
174
178
  "type": "string",
175
179
  "minLength": 1,
176
- "description": "MCP endpoint URL."
180
+ "description": "MCP endpoint URL. Public CLI output redacts userinfo, query values, and fragments."
177
181
  },
178
182
  "headers": {
179
183
  "$ref": "#/$defs/stringMap",
180
- "description": "HTTP headers sent with MCP requests."
184
+ "description": "HTTP headers sent with MCP requests. Public CLI output redacts values."
181
185
  }
182
186
  },
183
187
  "not": {
@@ -187,13 +191,13 @@
187
191
  ]
188
192
  },
189
193
  "sseServer": {
194
+ "type": "object",
195
+ "unevaluatedProperties": false,
190
196
  "allOf": [
191
197
  {
192
198
  "$ref": "#/$defs/serverBase"
193
199
  },
194
200
  {
195
- "type": "object",
196
- "additionalProperties": false,
197
201
  "required": ["type", "url"],
198
202
  "properties": {
199
203
  "type": {
@@ -204,11 +208,11 @@
204
208
  "url": {
205
209
  "type": "string",
206
210
  "minLength": 1,
207
- "description": "Legacy SSE endpoint URL."
211
+ "description": "Legacy SSE endpoint URL. Public CLI output redacts userinfo, query values, and fragments."
208
212
  },
209
213
  "headers": {
210
214
  "$ref": "#/$defs/stringMap",
211
- "description": "HTTP headers sent with the SSE transport."
215
+ "description": "HTTP headers sent with the SSE transport. Public CLI output redacts values."
212
216
  }
213
217
  },
214
218
  "not": {
@@ -70,25 +70,25 @@ const profile = (
70
70
 
71
71
  export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
72
72
  profile("codex-eco", ["openai-codex"], {
73
- default: "openai-codex/gpt-5.5:low",
74
- executor: "openai-codex/gpt-5.5:minimal",
75
- planner: "openai-codex/gpt-5.5:low",
76
- critic: "openai-codex/gpt-5.5:medium",
77
- architect: "openai-codex/gpt-5.5:high",
73
+ default: "openai-codex/gpt-5.6-sol:medium",
74
+ executor: "openai-codex/gpt-5.6-luna:low",
75
+ planner: "openai-codex/gpt-5.6-luna:medium",
76
+ critic: "openai-codex/gpt-5.6-luna:medium",
77
+ architect: "openai-codex/gpt-5.6-sol:medium",
78
78
  }),
79
79
  profile("codex-medium", ["openai-codex"], {
80
- default: "openai-codex/gpt-5.5:medium",
81
- executor: "openai-codex/gpt-5.5:low",
82
- planner: "openai-codex/gpt-5.5:medium",
83
- critic: "openai-codex/gpt-5.5:high",
84
- architect: "openai-codex/gpt-5.5:xhigh",
80
+ default: "openai-codex/gpt-5.6-sol:high",
81
+ executor: "openai-codex/gpt-5.6-luna:medium",
82
+ planner: "openai-codex/gpt-5.6-terra:medium",
83
+ critic: "openai-codex/gpt-5.6-terra:high",
84
+ architect: "openai-codex/gpt-5.6-sol:high",
85
85
  }),
86
86
  profile("codex-pro", ["openai-codex"], {
87
- default: "openai-codex/gpt-5.5:xhigh",
88
- executor: "openai-codex/gpt-5.5:medium",
89
- planner: "openai-codex/gpt-5.5:high",
90
- critic: "openai-codex/gpt-5.5:xhigh",
91
- architect: "openai-codex/gpt-5.5:xhigh",
87
+ default: "openai-codex/gpt-5.6-sol:xhigh",
88
+ executor: "openai-codex/gpt-5.6-terra:medium",
89
+ planner: "openai-codex/gpt-5.6-sol:high",
90
+ critic: "openai-codex/gpt-5.6-sol:xhigh",
91
+ architect: "openai-codex/gpt-5.6-sol:max",
92
92
  }),
93
93
  profile("opencodego", ["opencode-go"], {
94
94
  default: "opencode-go/kimi-k2.6",
@@ -256,24 +256,24 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
256
256
  }),
257
257
  profile("opus-codex", ["anthropic", "openai-codex"], {
258
258
  default: "anthropic/claude-opus-4-8:xhigh",
259
- executor: "openai-codex/gpt-5.5:low",
260
- planner: "openai-codex/gpt-5.5:medium",
261
- critic: "openai-codex/gpt-5.5:high",
262
- architect: "openai-codex/gpt-5.5:xhigh",
259
+ executor: "openai-codex/gpt-5.6-terra:low",
260
+ planner: "openai-codex/gpt-5.6-terra:medium",
261
+ critic: "openai-codex/gpt-5.6-terra:high",
262
+ architect: "openai-codex/gpt-5.6-sol:xhigh",
263
263
  }),
264
264
  profile("codex-opencodego", ["openai-codex", "opencode-go"], {
265
- default: "openai-codex/gpt-5.5:medium",
265
+ default: "openai-codex/gpt-5.6-sol:medium",
266
266
  executor: "opencode-go/deepseek-v4-pro",
267
267
  planner: "opencode-go/kimi-k2.6",
268
268
  critic: "opencode-go/mimo-v2.5-pro",
269
- architect: "openai-codex/gpt-5.5:xhigh",
269
+ architect: "openai-codex/gpt-5.6-sol:xhigh",
270
270
  }),
271
271
  profile("fable-opus-codex", ["anthropic", "openai-codex"], {
272
272
  default: "anthropic/claude-fable-5:high",
273
- executor: "openai-codex/gpt-5.5:high",
273
+ executor: "openai-codex/gpt-5.6-terra:high",
274
274
  planner: "anthropic/claude-opus-4-8:medium",
275
275
  critic: "anthropic/claude-opus-4-8:high",
276
- architect: "openai-codex/gpt-5.5:xhigh",
276
+ architect: "openai-codex/gpt-5.6-sol:xhigh",
277
277
  }),
278
278
  ];
279
279
 
@@ -176,6 +176,19 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
176
176
  autoload = undefined;
177
177
  }
178
178
 
179
+ // Validate noInheritEnv: boolean only, warn on other types
180
+ let noInheritEnv: boolean | undefined;
181
+ if (serverConfig.noInheritEnv === undefined || serverConfig.noInheritEnv === null) {
182
+ noInheritEnv = undefined;
183
+ } else if (typeof serverConfig.noInheritEnv === "boolean") {
184
+ noInheritEnv = serverConfig.noInheritEnv;
185
+ } else {
186
+ logger.warn(
187
+ `MCP server "${serverName}": invalid noInheritEnv type ${typeof serverConfig.noInheritEnv}, ignoring`,
188
+ );
189
+ noInheritEnv = undefined;
190
+ }
191
+
179
192
  result.push({
180
193
  name: serverName,
181
194
  enabled,
@@ -184,6 +197,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
184
197
  command: serverConfig.command as string | undefined,
185
198
  args: serverConfig.args as string[] | undefined,
186
199
  env: serverConfig.env as Record<string, string> | undefined,
200
+ noInheritEnv,
187
201
  cwd: serverConfig.cwd as string | undefined,
188
202
  url: serverConfig.url as string | undefined,
189
203
  headers: serverConfig.headers as Record<string, string> | undefined,
@@ -30,6 +30,7 @@ interface MCPConfigFile {
30
30
  command?: string;
31
31
  args?: string[];
32
32
  env?: Record<string, string>;
33
+ noInheritEnv?: boolean;
33
34
  cwd?: string;
34
35
  url?: string;
35
36
  headers?: Record<string, string>;
@@ -92,6 +93,18 @@ function transformMCPConfig(config: MCPConfigFile, source: SourceMeta): MCPServe
92
93
  }
93
94
  }
94
95
 
96
+ let noInheritEnv: boolean | undefined;
97
+ if (serverConfig.noInheritEnv !== undefined) {
98
+ if (typeof serverConfig.noInheritEnv === "boolean") {
99
+ noInheritEnv = serverConfig.noInheritEnv;
100
+ } else {
101
+ logger.warn("MCP server has invalid 'noInheritEnv' value, ignoring", {
102
+ name,
103
+ value: serverConfig.noInheritEnv,
104
+ });
105
+ }
106
+ }
107
+
95
108
  const server: MCPServer = {
96
109
  name,
97
110
  enabled,
@@ -100,6 +113,7 @@ function transformMCPConfig(config: MCPConfigFile, source: SourceMeta): MCPServe
100
113
  command: serverConfig.command,
101
114
  args: serverConfig.args,
102
115
  env: serverConfig.env,
116
+ noInheritEnv,
103
117
  cwd: serverConfig.cwd,
104
118
  url: serverConfig.url,
105
119
  headers: serverConfig.headers,
@@ -1,6 +1,7 @@
1
1
  import { DEFAULT_MAX_BYTES, OutputSink } from "../../session/streaming-output";
2
2
  import type { ToolSession } from "../../tools";
3
3
  import { resolveOutputMaxColumns, resolveOutputSinkHeadBytes } from "../../tools/output-meta";
4
+ import { ToolError } from "../../tools/tool-errors";
4
5
  import { executeInVmContext, type JsDisplayOutput } from "./context-manager";
5
6
 
6
7
  export interface JsExecutorOptions {
@@ -117,7 +118,12 @@ export async function executeJs(code: string, options: JsExecutorOptions): Promi
117
118
  displayOutputs,
118
119
  };
119
120
  }
120
- const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
121
+ const message =
122
+ error instanceof ToolError || (error instanceof Error && error.name === "ToolError")
123
+ ? error.message
124
+ : error instanceof Error
125
+ ? (error.stack ?? error.message)
126
+ : String(error);
121
127
  outputSink.push(message);
122
128
  const summary = await outputSink.dump();
123
129
  return {