@atlasharness/atlascode 0.1.78 → 0.1.79

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 (2) hide show
  1. package/dist/cli.js +521 -96
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -26844,24 +26844,47 @@ var init_params = __esm(() => {
26844
26844
  });
26845
26845
 
26846
26846
  // src/modelprovider/types.ts
26847
- var APIError2, APIConnectionError2, APIConnectionTimeoutError2;
26847
+ var APIError2, AuthenticationError2, NotFoundError2, APIConnectionError2, APIConnectionTimeoutError2, APIUserAbortError2;
26848
26848
  var init_types2 = __esm(() => {
26849
26849
  APIError2 = class APIError2 extends Error {
26850
26850
  status;
26851
+ code;
26852
+ headers;
26851
26853
  error;
26852
- constructor(message, status, error) {
26853
- super(message);
26854
+ constructor(message, statusOrOpts, error) {
26855
+ super(message ?? "API error");
26854
26856
  this.name = "APIError";
26855
- if (status !== undefined)
26856
- this.status = status;
26857
- if (error !== undefined)
26858
- this.error = error;
26857
+ if (typeof statusOrOpts === "number") {
26858
+ if (statusOrOpts !== undefined)
26859
+ this.status = statusOrOpts;
26860
+ if (error !== undefined)
26861
+ this.error = error;
26862
+ } else if (statusOrOpts) {
26863
+ if (statusOrOpts.status !== undefined)
26864
+ this.status = statusOrOpts.status;
26865
+ if (statusOrOpts.code !== undefined)
26866
+ this.code = statusOrOpts.code;
26867
+ if (statusOrOpts.headers !== undefined)
26868
+ this.headers = statusOrOpts.headers;
26869
+ if (statusOrOpts.error !== undefined)
26870
+ this.error = statusOrOpts.error;
26871
+ }
26872
+ }
26873
+ };
26874
+ AuthenticationError2 = class AuthenticationError2 extends APIError2 {
26875
+ constructor(message, opts) {
26876
+ super(message, opts);
26877
+ this.name = "AuthenticationError";
26859
26878
  }
26860
26879
  };
26880
+ NotFoundError2 = class NotFoundError2 extends APIError2 {
26881
+ };
26861
26882
  APIConnectionError2 = class APIConnectionError2 extends APIError2 {
26862
26883
  };
26863
26884
  APIConnectionTimeoutError2 = class APIConnectionTimeoutError2 extends APIConnectionError2 {
26864
26885
  };
26886
+ APIUserAbortError2 = class APIUserAbortError2 extends APIError2 {
26887
+ };
26865
26888
  });
26866
26889
 
26867
26890
  // src/modelprovider/errorUtils.ts
@@ -28846,7 +28869,9 @@ var init_hookEvents = __esm(() => {
28846
28869
  "CwdChanged",
28847
28870
  "FileChanged",
28848
28871
  "PreModelSwitch",
28849
- "PostModelSwitch"
28872
+ "PostModelSwitch",
28873
+ "DirectoryAdded",
28874
+ "MessageDisplay"
28850
28875
  ];
28851
28876
  });
28852
28877
 
@@ -28994,6 +29019,9 @@ async function getMatchingHooks(hookEvent, hookInput) {
28994
29019
  case "PostModelSwitch":
28995
29020
  matchQuery = hookInput.new_model;
28996
29021
  break;
29022
+ case "DirectoryAdded":
29023
+ matchQuery = hookInput.directory;
29024
+ break;
28997
29025
  default:
28998
29026
  break;
28999
29027
  }
@@ -29107,6 +29135,8 @@ function interpretHookOutput(p) {
29107
29135
  result.output = "";
29108
29136
  if (json.systemMessage)
29109
29137
  result.systemMessage = json.systemMessage;
29138
+ if (json.terminalSequence)
29139
+ result.terminalSequence = json.terminalSequence;
29110
29140
  } catch {}
29111
29141
  }
29112
29142
  if (!p.aborted && p.code === 2 && !jsonParsed && !result.blockingError) {
@@ -29118,8 +29148,14 @@ function interpretHookOutput(p) {
29118
29148
  return result;
29119
29149
  }
29120
29150
  function mergeAggregated(acc, r) {
29121
- if (r.blockingError && !acc.blockingError)
29151
+ if (r.terminalSequence) {
29152
+ acc.terminalSequence = (acc.terminalSequence ?? "") + r.terminalSequence;
29153
+ }
29154
+ if (r.blockingError && !acc.blockingError) {
29122
29155
  acc.blockingError = r.blockingError;
29156
+ if (r.continueOnBlock)
29157
+ acc.continueOnBlock = true;
29158
+ }
29123
29159
  if (r.preventContinuation)
29124
29160
  acc.preventContinuation = true;
29125
29161
  if (r.stopReason && !acc.stopReason)
@@ -29142,13 +29178,19 @@ ${r.additionalContext}` : r.additionalContext;
29142
29178
  function runOneHook(m, port, env, signal, options) {
29143
29179
  const cmd = m.hook;
29144
29180
  const timeoutMs = cmd.timeoutMs ?? options.timeoutMs ?? HOOK_EXECUTION_TIMEOUT_MS;
29145
- return port.runCommand(cmd.command, env, signal, timeoutMs).then((exec) => interpretHookOutput({
29146
- command: cmd.command,
29147
- stdout: exec.stdout,
29148
- stderr: exec.stderr,
29149
- code: exec.code,
29150
- aborted: exec.aborted
29151
- }), (e) => ({
29181
+ return port.runCommand(cmd.command, env, signal, timeoutMs).then((exec) => {
29182
+ const result = interpretHookOutput({
29183
+ command: cmd.command,
29184
+ stdout: exec.stdout,
29185
+ stderr: exec.stderr,
29186
+ code: exec.code,
29187
+ aborted: exec.aborted
29188
+ });
29189
+ if (result.blockingError && cmd.continueOnBlock === true) {
29190
+ result.continueOnBlock = true;
29191
+ }
29192
+ return result;
29193
+ }, (e) => ({
29152
29194
  command: cmd.command,
29153
29195
  status: -1,
29154
29196
  succeeded: false,
@@ -29172,6 +29214,11 @@ async function runHooks(hookEvent, hookInput, options = {}) {
29172
29214
  results.push(result);
29173
29215
  mergeAggregated(aggregated, result);
29174
29216
  }
29217
+ if (aggregated.terminalSequence) {
29218
+ try {
29219
+ process.stdout.write(aggregated.terminalSequence);
29220
+ } catch {}
29221
+ }
29175
29222
  return aggregated;
29176
29223
  }
29177
29224
  async function runPreToolUseHooks(toolName, toolInput, toolUseID, options = {}) {
@@ -29319,6 +29366,7 @@ var init_hooksSchema = __esm(() => {
29319
29366
  once: boolean2().optional(),
29320
29367
  async: boolean2().optional(),
29321
29368
  asyncRewake: boolean2().optional(),
29369
+ continueOnBlock: boolean2().optional(),
29322
29370
  if: string2().optional()
29323
29371
  }).passthrough();
29324
29372
  PromptHookSchema = object({
@@ -29395,18 +29443,53 @@ var SettingsSchema = () => object({
29395
29443
  defaultShell: union([literal("bash"), literal("powershell")]).optional(),
29396
29444
  skipWebFetchPreflight: boolean2().optional(),
29397
29445
  search: object({ tavilyApiKey: string2().optional() }).optional(),
29398
- tui: object({ fullscreen: boolean2().optional() }).optional()
29446
+ tui: object({ fullscreen: boolean2().optional() }).optional(),
29447
+ skillOverrides: union([
29448
+ literal("off"),
29449
+ literal("user-invocable-only"),
29450
+ literal("name-only")
29451
+ ]).optional()
29399
29452
  }).passthrough();
29400
29453
  var init_types3 = __esm(() => {
29401
29454
  init_zod();
29402
29455
  init_hooksSchema();
29403
29456
  });
29404
29457
 
29458
+ // src/engine/config/restrictedMode.ts
29459
+ function isRestrictedMode() {
29460
+ return restrictedOverride ?? isEnvTruthy(process.env.ATLAS_RESTRICTED);
29461
+ }
29462
+ function setRestrictedMode(enabled) {
29463
+ restrictedOverride = enabled;
29464
+ }
29465
+ function resetRestrictedMode() {
29466
+ restrictedOverride = undefined;
29467
+ }
29468
+ function isSettingsSourceIgnoredInRestricted(source) {
29469
+ if (!isRestrictedMode())
29470
+ return false;
29471
+ return source === "userSettings" || source === "projectSettings" || source === "localSettings";
29472
+ }
29473
+ function restrictedBypassError(opts) {
29474
+ if (!isRestrictedMode())
29475
+ return null;
29476
+ if (opts.permissionMode === "bypassPermissions" || opts.allowDangerouslySkipPermissions === true || opts.dangerouslySkipPermissions === true) {
29477
+ return "--restricted mode does not allow bypassPermissions or --dangerously-skip-permissions for security reasons";
29478
+ }
29479
+ return null;
29480
+ }
29481
+ var restrictedOverride;
29482
+ var init_restrictedMode = __esm(() => {
29483
+ init_shared();
29484
+ });
29485
+
29405
29486
  // src/engine/config/constants.ts
29406
29487
  function getEnabledSettingSources() {
29407
29488
  const result = new Set(ALLOWED_SETTING_SOURCES);
29408
29489
  result.add("policySettings");
29409
29490
  result.add("flagSettings");
29491
+ if (isRestrictedMode())
29492
+ result.delete("userSettings");
29410
29493
  return Array.from(result);
29411
29494
  }
29412
29495
  function isSettingSourceEnabled(source) {
@@ -29415,6 +29498,7 @@ function isSettingSourceEnabled(source) {
29415
29498
  }
29416
29499
  var SETTING_SOURCES, ALLOWED_SETTING_SOURCES;
29417
29500
  var init_constants3 = __esm(() => {
29501
+ init_restrictedMode();
29418
29502
  SETTING_SOURCES = [
29419
29503
  "userSettings",
29420
29504
  "projectSettings",
@@ -30058,7 +30142,9 @@ var init_permissionRuleParser = __esm(() => {
30058
30142
 
30059
30143
  // src/permissions/mcpRuleNames.ts
30060
30144
  function normalizeNameForMCP(name) {
30061
- return name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
30145
+ const folded = name.replace(/[^a-zA-Z0-9_-]/g, "_");
30146
+ const normalized = folded.replace(/_+/g, "_").replace(/^_|_$/g, "");
30147
+ return normalized === "" ? folded : normalized;
30062
30148
  }
30063
30149
  function mcpInfoFromString(toolString) {
30064
30150
  const parts = toolString.split("__");
@@ -31908,6 +31994,8 @@ function getSettingsForSource(source) {
31908
31994
  return result;
31909
31995
  }
31910
31996
  function getSettingsForSourceUncached(source) {
31997
+ if (isSettingsSourceIgnoredInRestricted(source))
31998
+ return null;
31911
31999
  if (source === "policySettings") {
31912
32000
  return loadManagedFileSettings().settings;
31913
32001
  }
@@ -31920,6 +32008,11 @@ function getPolicySettingsOrigin() {
31920
32008
  return settings ? "file" : null;
31921
32009
  }
31922
32010
  function updateSettingsForSource(source, settings) {
32011
+ if (isSettingsSourceIgnoredInRestricted(source)) {
32012
+ return {
32013
+ error: new Error(`Settings source ${source} is ignored in restricted mode (--restricted / ATLAS_RESTRICTED)`)
32014
+ };
32015
+ }
31923
32016
  if (source === "policySettings" || source === "flagSettings") {
31924
32017
  return { error: null };
31925
32018
  }
@@ -32082,6 +32175,7 @@ var init_settings = __esm(() => {
32082
32175
  init_zod();
32083
32176
  init_configRoot();
32084
32177
  init_constants3();
32178
+ init_restrictedMode();
32085
32179
  init_managedPath();
32086
32180
  init_settingsCache();
32087
32181
  init_types3();
@@ -32286,6 +32380,7 @@ var init_autoCompactOverrides = __esm(() => {
32286
32380
  var init_config = __esm(() => {
32287
32381
  init_types3();
32288
32382
  init_constants3();
32383
+ init_restrictedMode();
32289
32384
  init_settingsCache();
32290
32385
  init_managedPath();
32291
32386
  init_configRoot();
@@ -36510,7 +36605,9 @@ var init_killShellTasks = __esm(() => {
36510
36605
 
36511
36606
  // src/engine/tools/mcp.ts
36512
36607
  function normalizeNameForMCP2(name) {
36513
- return name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
36608
+ const folded = name.replace(/[^a-zA-Z0-9_-]/g, "_");
36609
+ const normalized = folded.replace(/_+/g, "_").replace(/^_|_$/g, "");
36610
+ return normalized === "" ? folded : normalized;
36514
36611
  }
36515
36612
  function mcpInfoFromString2(toolString) {
36516
36613
  const parts = toolString.split("__");
@@ -37111,14 +37208,24 @@ function isAscendToolsEnabled(env = process.env) {
37111
37208
  }
37112
37209
  function getAllBaseTools(deps = {}) {
37113
37210
  const env = deps.env ?? process.env;
37211
+ const restricted = isRestrictedMode();
37114
37212
  const pool = [AgentTool];
37115
37213
  if (deps.baseTools)
37116
37214
  pool.push(...deps.baseTools);
37117
- if (deps.ascendTools && isAscendToolsEnabled(env))
37215
+ if (deps.ascendTools && isAscendToolsEnabled(env) && !restricted) {
37118
37216
  pool.push(...deps.ascendTools);
37217
+ }
37119
37218
  if (deps.mcpTools)
37120
37219
  pool.push(...deps.mcpTools);
37121
- return uniqByName(pool);
37220
+ const assembled = uniqByName(pool);
37221
+ if (!restricted)
37222
+ return assembled;
37223
+ const stripped = new Set([
37224
+ ...SHELL_TOOL_NAMES,
37225
+ WEB_FETCH_TOOL_NAME,
37226
+ AGENT_TOOL_NAME
37227
+ ]);
37228
+ return assembled.filter((t) => !stripped.has(t.name));
37122
37229
  }
37123
37230
  function uniqByName(tools) {
37124
37231
  const seen = new Set;
@@ -37150,7 +37257,9 @@ function parseToolPreset(preset) {
37150
37257
  var TOOL_PRESETS;
37151
37258
  var init_toolRegistry = __esm(() => {
37152
37259
  init_permissions3();
37260
+ init_config();
37153
37261
  init_AgentTool();
37262
+ init_toolNames();
37154
37263
  TOOL_PRESETS = ["default"];
37155
37264
  });
37156
37265
 
@@ -78377,6 +78486,21 @@ var init_mcpFetch = __esm(() => {
78377
78486
  function getConnectionTimeoutMs() {
78378
78487
  return parseInt(process.env.MCP_TIMEOUT || "", 10) || 30000;
78379
78488
  }
78489
+ function getMcpConnectBackoffMs(env = process.env) {
78490
+ const raw = env.ATLAS_MCP_CONNECT_BACKOFF_MS;
78491
+ if (raw === undefined || raw === "")
78492
+ return DEFAULT_MCP_CONNECT_BACKOFF_MS;
78493
+ const parsed = Number(raw);
78494
+ if (!Number.isFinite(parsed))
78495
+ return DEFAULT_MCP_CONNECT_BACKOFF_MS;
78496
+ return Math.max(0, parsed);
78497
+ }
78498
+ function mcpConnectBackoffDelayMs(failureCount, baseMs = getMcpConnectBackoffMs()) {
78499
+ if (failureCount <= 0 || baseMs <= 0)
78500
+ return 0;
78501
+ const raw = baseMs * 2 ** (failureCount - 1);
78502
+ return Math.min(raw, MAX_MCP_CONNECT_BACKOFF_MS);
78503
+ }
78380
78504
  function isTerminalConnectionError(msg) {
78381
78505
  return msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("EPIPE") || msg.includes("EHOSTUNREACH") || msg.includes("ECONNREFUSED") || msg.includes("Body Timeout Error") || msg.includes("terminated") || msg.includes("SSE stream disconnected") || msg.includes("Failed to reconnect SSE stream");
78382
78506
  }
@@ -78425,6 +78549,7 @@ async function terminateChild(name, child) {
78425
78549
  function createMcpConnectionManager() {
78426
78550
  const connections = new Map;
78427
78551
  const pending = new Set;
78552
+ const connectFailures = new Map;
78428
78553
  function fail(name, config, error, authFailure = false) {
78429
78554
  const conn = {
78430
78555
  name,
@@ -78529,6 +78654,7 @@ function createMcpConnectionManager() {
78529
78654
  }
78530
78655
  };
78531
78656
  connections.set(name, connection);
78657
+ connectFailures.delete(name);
78532
78658
  return connection;
78533
78659
  } catch (e) {
78534
78660
  stderr.off("data", stderrHandler);
@@ -78539,6 +78665,8 @@ function createMcpConnectionManager() {
78539
78665
  stderrOutput = "";
78540
78666
  }
78541
78667
  const message = e instanceof McpJsonRpcError || e instanceof Error ? e.message : String(e);
78668
+ const prev = connectFailures.get(name);
78669
+ connectFailures.set(name, { count: (prev?.count ?? 0) + 1, lastFailedAt: Date.now() });
78542
78670
  return fail(name, config, message);
78543
78671
  }
78544
78672
  }
@@ -78546,6 +78674,14 @@ function createMcpConnectionManager() {
78546
78674
  const existing = connections.get(name);
78547
78675
  if (existing?.type === "connected")
78548
78676
  return existing;
78677
+ const record = connectFailures.get(name);
78678
+ const backoffDelayMs = record ? mcpConnectBackoffDelayMs(record.count) : 0;
78679
+ if (record !== undefined && backoffDelayMs > 0) {
78680
+ const sinceLastFailure = Date.now() - record.lastFailedAt;
78681
+ if (sinceLastFailure < backoffDelayMs) {
78682
+ return fail(name, config, `MCP server "${name}" connect deferred (backoff): ${record.count} consecutive failure(s), retry in ~${Math.ceil((backoffDelayMs - sinceLastFailure) / 1000)}s (set ATLAS_MCP_CONNECT_BACKOFF_MS=0 to disable)`);
78683
+ }
78684
+ }
78549
78685
  pending.add(name);
78550
78686
  try {
78551
78687
  const t = config.type;
@@ -78583,7 +78719,7 @@ function createMcpConnectionManager() {
78583
78719
  function getMcpConnectionManager() {
78584
78720
  return singleton ??= createMcpConnectionManager();
78585
78721
  }
78586
- var MAX_ERRORS_BEFORE_RECONNECT = 3, FORWARD_SEAM_MESSAGE = "MCP transport = 前向接缝登记(§8.68 R2:非 stdio 传输真实现待 MCP 标准远程传输(M6 波)(#200 波);G-3 ⑩ D3 型面 8→4 后 4 型 config,仅 stdio 连接支 live)", singleton;
78722
+ var DEFAULT_MCP_CONNECT_BACKOFF_MS = 1000, MAX_MCP_CONNECT_BACKOFF_MS = 30000, MAX_ERRORS_BEFORE_RECONNECT = 3, FORWARD_SEAM_MESSAGE = "MCP transport = 前向接缝登记(§8.68 R2:非 stdio 传输真实现待 MCP 标准远程传输(M6 波)(#200 波);G-3 ⑩ D3 型面 8→4 后 4 型 config,仅 stdio 连接支 live)", singleton;
78587
78723
  var init_mcpConnectionManager = __esm(() => {
78588
78724
  init_shared();
78589
78725
  init_bootstrap();
@@ -78695,6 +78831,85 @@ function buildSchemaNotSentHint(tool, discoveredToolNames) {
78695
78831
  This tool's schema was not sent to the API — it was not in the discovered-tool set derived from message history. ` + `Without the schema in your prompt, typed parameters (arrays, numbers, booleans) get emitted as strings and the client-side parser rejects them. ` + `Re-issue the call with correctly-typed parameters (arrays/numbers/booleans, not their string forms).`;
78696
78832
  }
78697
78833
 
78834
+ // src/engine/pipeline/mcpAutoBackground.ts
78835
+ function getMcpAutoBackgroundMs(env = process.env) {
78836
+ const raw = env.ATLAS_MCP_AUTO_BACKGROUND_MS;
78837
+ if (!raw)
78838
+ return DEFAULT_MCP_AUTO_BACKGROUND_MS;
78839
+ const parsed = Number(raw);
78840
+ if (!Number.isFinite(parsed))
78841
+ return DEFAULT_MCP_AUTO_BACKGROUND_MS;
78842
+ return Math.max(MIN_MCP_AUTO_BACKGROUND_MS, parsed);
78843
+ }
78844
+ async function callWithMcpAutoBackground(opts, fn) {
78845
+ let settled = false;
78846
+ return new Promise((resolve, reject) => {
78847
+ const pending = fn();
78848
+ const timer = setTimeout(() => {
78849
+ if (settled)
78850
+ return;
78851
+ settled = true;
78852
+ resolve({ backgrounded: true });
78853
+ pending.then((data) => {
78854
+ opts.onBackgrounded({
78855
+ toolUseId: opts.toolUseId,
78856
+ toolName: opts.toolName,
78857
+ outcome: { ok: true, data }
78858
+ });
78859
+ }).catch((error) => {
78860
+ opts.onBackgrounded({
78861
+ toolUseId: opts.toolUseId,
78862
+ toolName: opts.toolName,
78863
+ outcome: { ok: false, error: String(error?.message ?? error) }
78864
+ });
78865
+ });
78866
+ }, opts.backgroundAfterMs);
78867
+ timer.unref?.();
78868
+ pending.then((result) => {
78869
+ if (settled)
78870
+ return;
78871
+ settled = true;
78872
+ clearTimeout(timer);
78873
+ resolve({ backgrounded: false, result });
78874
+ }, (error) => {
78875
+ if (settled)
78876
+ return;
78877
+ settled = true;
78878
+ clearTimeout(timer);
78879
+ reject(error);
78880
+ });
78881
+ });
78882
+ }
78883
+ var DEFAULT_MCP_AUTO_BACKGROUND_MS = 120000, MIN_MCP_AUTO_BACKGROUND_MS = 1000;
78884
+
78885
+ // src/engine/pipeline/toolHeartbeat.ts
78886
+ function getToolHeartbeatIntervalMs(env = process.env) {
78887
+ const raw = env.ATLAS_TOOL_HEARTBEAT_MS;
78888
+ if (!raw)
78889
+ return DEFAULT_TOOL_HEARTBEAT_INTERVAL_MS;
78890
+ const parsed = Number(raw);
78891
+ if (!Number.isFinite(parsed))
78892
+ return DEFAULT_TOOL_HEARTBEAT_INTERVAL_MS;
78893
+ return Math.max(MIN_TOOL_HEARTBEAT_INTERVAL_MS, parsed);
78894
+ }
78895
+ async function runWithToolHeartbeat(opts, fn) {
78896
+ const start = Date.now();
78897
+ const timer = setInterval(() => {
78898
+ opts.onHeartbeat({
78899
+ toolUseId: opts.toolUseId,
78900
+ toolName: opts.toolName,
78901
+ elapsedMs: Date.now() - start
78902
+ });
78903
+ }, opts.intervalMs);
78904
+ timer.unref?.();
78905
+ try {
78906
+ return await fn();
78907
+ } finally {
78908
+ clearInterval(timer);
78909
+ }
78910
+ }
78911
+ var DEFAULT_TOOL_HEARTBEAT_INTERVAL_MS = 30000, MIN_TOOL_HEARTBEAT_INTERVAL_MS = 100;
78912
+
78698
78913
  // src/engine/pipeline/toolExecution.ts
78699
78914
  function mergeHookPermission(hookBehavior, gate, hookReason) {
78700
78915
  if (hookBehavior === "defer") {
@@ -78806,8 +79021,40 @@ async function executeToolUse(tu, assistantMsg, deps) {
78806
79021
  let block;
78807
79022
  let isError = false;
78808
79023
  let toolUseResult;
79024
+ const mcpAutoBackgroundArmed = deps.onMcpToolBackgrounded !== undefined && tool.isMcp === true;
78809
79025
  try {
78810
- const res = await tool.call(callInput, toolCallContext, undefined, assistantMsg);
79026
+ const callTool = () => tool.call(callInput, toolCallContext, undefined, assistantMsg);
79027
+ const withHeartbeat = () => deps.onToolHeartbeat ? runWithToolHeartbeat({
79028
+ toolUseId: tu.id,
79029
+ toolName: tool.name,
79030
+ intervalMs: getToolHeartbeatIntervalMs(),
79031
+ onHeartbeat: deps.onToolHeartbeat
79032
+ }, callTool) : callTool();
79033
+ let res;
79034
+ if (mcpAutoBackgroundArmed) {
79035
+ const raced = await callWithMcpAutoBackground({
79036
+ toolUseId: tu.id,
79037
+ toolName: tool.name,
79038
+ backgroundAfterMs: deps.mcpAutoBackgroundMs ?? getMcpAutoBackgroundMs(),
79039
+ onBackgrounded: deps.onMcpToolBackgrounded
79040
+ }, withHeartbeat);
79041
+ if (raced.backgrounded === true) {
79042
+ const backgroundedContent = `[mcp tool ${tool.name} is still running after exceeding the auto-background threshold; ` + "it has been moved to the background and will keep running. " + "The result will be delivered later as a task notification — do not call this tool again for the same purpose.";
79043
+ return {
79044
+ block: {
79045
+ type: "tool_result",
79046
+ tool_use_id: tu.id,
79047
+ content: backgroundedContent,
79048
+ is_error: false
79049
+ },
79050
+ isError: false,
79051
+ toolUseResult: backgroundedContent
79052
+ };
79053
+ }
79054
+ res = raced.result;
79055
+ } else {
79056
+ res = await withHeartbeat();
79057
+ }
78811
79058
  block = tool.mapToolResultToToolResultBlockParam(res.data, tu.id);
78812
79059
  toolUseResult = res.data;
78813
79060
  } catch (error) {
@@ -78826,6 +79073,17 @@ async function executeToolUse(tu, assistantMsg, deps) {
78826
79073
  block = { ...block, content: postOutcome.updatedToolOutput };
78827
79074
  toolUseResult = postOutcome.updatedToolOutput;
78828
79075
  }
79076
+ if (postOutcome?.blockingError !== undefined) {
79077
+ const hookDenyText = `<tool_use_error>blocked by PostToolUse hook: ${postOutcome.blockingError}</tool_use_error>`;
79078
+ block = {
79079
+ type: "tool_result",
79080
+ tool_use_id: tu.id,
79081
+ content: hookDenyText,
79082
+ is_error: true
79083
+ };
79084
+ isError = true;
79085
+ toolUseResult = hookDenyText;
79086
+ }
78829
79087
  return { block, isError, toolUseResult };
78830
79088
  }
78831
79089
  var init_toolExecution = () => {};
@@ -82340,7 +82598,9 @@ async function queryOneRound(deps, tools, messages) {
82340
82598
  signal: deps.signal,
82341
82599
  checkPermission: deps.checkPermission,
82342
82600
  hooks: deps.hooks?.toolHooks,
82343
- toolContext: deps.toolContext
82601
+ toolContext: deps.toolContext,
82602
+ onToolHeartbeat: deps.emit ? (info) => deps.emit({ type: "tool_heartbeat", ...info }) : undefined,
82603
+ onMcpToolBackgrounded: deps.onMcpToolBackgrounded
82344
82604
  });
82345
82605
  const deferredOutcome = outcomes.find((o) => o.deferred);
82346
82606
  const deferredPayload = deferredOutcome ? {
@@ -82655,7 +82915,8 @@ function createToolHooks(opts = {}) {
82655
82915
  const r = await runPostToolUseHooks(tool.name, input, block, toolUseId, { ...base, toolUseID: toolUseId });
82656
82916
  const outcome = {
82657
82917
  additionalContext: r.additionalContext,
82658
- updatedToolOutput: r.updatedToolOutput
82918
+ updatedToolOutput: r.updatedToolOutput,
82919
+ blockingError: r.blockingError && r.continueOnBlock ? r.blockingError.blockingError : undefined
82659
82920
  };
82660
82921
  return outcome;
82661
82922
  }
@@ -85339,6 +85600,15 @@ To attach: tmux attach -t ${tmuxSessionName}
85339
85600
  if (!bare) {
85340
85601
  getCommands(getProjectRoot()).catch(() => {});
85341
85602
  }
85603
+ const restrictedBypassErr = restrictedBypassError({
85604
+ permissionMode: options.permissionMode,
85605
+ allowDangerouslySkipPermissions: options.allowDangerouslySkipPermissions === true
85606
+ });
85607
+ if (restrictedBypassErr) {
85608
+ process.stderr.write(`Error: ${restrictedBypassErr}
85609
+ `);
85610
+ process.exit(1);
85611
+ }
85342
85612
  if (options.permissionMode === "bypassPermissions" || options.allowDangerouslySkipPermissions) {
85343
85613
  if (process.platform !== "win32" && typeof process.getuid === "function" && process.getuid() === 0 && process.env.IS_SANDBOX !== "1" && !isEnvTruthy(process.env.ATLAS_BUBBLEWRAP)) {
85344
85614
  process.stderr.write(`--dangerously-skip-permissions cannot be used with root/sudo privileges for security reasons
@@ -88343,6 +88613,19 @@ function buildHeadlessOptions(options) {
88343
88613
  };
88344
88614
  }
88345
88615
  async function mainActionSeam(prompt, options) {
88616
+ if (options.restricted === true) {
88617
+ setRestrictedMode(true);
88618
+ const bypassErr = restrictedBypassError({
88619
+ permissionMode: options.permissionMode,
88620
+ allowDangerouslySkipPermissions: options.allowDangerouslySkipPermissions === true,
88621
+ dangerouslySkipPermissions: options.dangerouslySkipPermissions === true
88622
+ });
88623
+ if (bypassErr) {
88624
+ process.stderr.write(`Error: ${bypassErr}
88625
+ `);
88626
+ process.exit(1);
88627
+ }
88628
+ }
88346
88629
  if (options.initOnly) {
88347
88630
  await runCliSetup({
88348
88631
  permissionMode: options.permissionMode,
@@ -88407,7 +88690,7 @@ function buildProgram() {
88407
88690
  throw new Error("--task-budget must be a positive integer");
88408
88691
  }
88409
88692
  return tokens;
88410
- }).hideHelp()).option("--replay-user-messages", "Re-emit user messages from stdin back on stdout for acknowledgment (only works with --input-format=stream-json and --output-format=stream-json)", () => true).addOption(new Option("--enable-auth-status", "Enable auth status messages in SDK mode").default(false).hideHelp()).option("--allowedTools, --allowed-tools <tools...>", 'Comma or space-separated list of tool names to allow (e.g. "Bash(git:*) Edit")').option("--tools <tools...>", 'Specify the list of available tools from the built-in set. Use "" to disable all tools, "default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read").').option("--disallowedTools, --disallowed-tools <tools...>", 'Comma or space-separated list of tool names to deny (e.g. "Bash(git:*) Edit")').option("--mcp-config <configs...>", "Load MCP servers from JSON files or strings (space-separated)").addOption(new Option("--permission-prompt-tool <tool>", "MCP tool to use for permission prompts (only works with --print)").argParser(String).hideHelp()).addOption(new Option("--system-prompt <prompt>", "System prompt to use for the session").argParser(String)).addOption(new Option("--system-prompt-file <file>", "Read system prompt from a file").argParser(String).hideHelp()).addOption(new Option("--append-system-prompt <prompt>", "Append a system prompt to the default system prompt").argParser(String)).addOption(new Option("--append-system-prompt-file <file>", "Read system prompt from a file and append to the default system prompt").argParser(String).hideHelp()).addOption(new Option("--permission-mode <mode>", "Permission mode to use for the session").argParser(String).choices(PERMISSION_MODES)).addOption(new Option("--permission-prompts <mode>", "Permission prompt policy; 'none' auto-denies prompts that would otherwise ask").argParser(String).choices(["none"])).option("-c, --continue", "Continue the most recent conversation in the current directory", () => true).option("-r, --resume [value]", "Resume a conversation by session ID, or open interactive picker with optional search term", (value) => value || true).option("--fork-session", "When resuming, create a new session ID instead of reusing the original (use with --resume or --continue)", () => true).addOption(new Option("--prefill <text>", "Pre-fill the prompt input with text without submitting it").hideHelp()).addOption(new Option("--deep-link-origin", "Signal that this session was launched from a deep link").hideHelp()).addOption(new Option("--deep-link-repo <slug>", "Repo slug the deep link ?repo= parameter resolved to the current cwd").hideHelp()).addOption(new Option("--deep-link-last-fetch <ms>", "FETCH_HEAD mtime in epoch ms, precomputed by the deep link trampoline").argParser((v) => {
88693
+ }).hideHelp()).option("--replay-user-messages", "Re-emit user messages from stdin back on stdout for acknowledgment (only works with --input-format=stream-json and --output-format=stream-json)", () => true).addOption(new Option("--enable-auth-status", "Enable auth status messages in SDK mode").default(false).hideHelp()).option("--allowedTools, --allowed-tools <tools...>", 'Comma or space-separated list of tool names to allow (e.g. "Bash(git:*) Edit")').option("--tools <tools...>", 'Specify the list of available tools from the built-in set. Use "" to disable all tools, "default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read").').option("--disallowedTools, --disallowed-tools <tools...>", 'Comma or space-separated list of tool names to deny (e.g. "Bash(git:*) Edit")').option("--restricted", "Run in restricted mode: strips command-execution tools (Bash/PowerShell), WebFetch, and Agent; rejects bypassPermissions and --dangerously-skip-permissions; ignores user/project/local settings (env: ATLAS_RESTRICTED)").option("--mcp-config <configs...>", "Load MCP servers from JSON files or strings (space-separated)").addOption(new Option("--permission-prompt-tool <tool>", "MCP tool to use for permission prompts (only works with --print)").argParser(String).hideHelp()).addOption(new Option("--system-prompt <prompt>", "System prompt to use for the session").argParser(String)).addOption(new Option("--system-prompt-file <file>", "Read system prompt from a file").argParser(String).hideHelp()).addOption(new Option("--append-system-prompt <prompt>", "Append a system prompt to the default system prompt").argParser(String)).addOption(new Option("--append-system-prompt-file <file>", "Read system prompt from a file and append to the default system prompt").argParser(String).hideHelp()).addOption(new Option("--permission-mode <mode>", "Permission mode to use for the session").argParser(String).choices(PERMISSION_MODES)).addOption(new Option("--permission-prompts <mode>", "Permission prompt policy; 'none' auto-denies prompts that would otherwise ask").argParser(String).choices(["none"])).option("-c, --continue", "Continue the most recent conversation in the current directory", () => true).option("-r, --resume [value]", "Resume a conversation by session ID, or open interactive picker with optional search term", (value) => value || true).option("--fork-session", "When resuming, create a new session ID instead of reusing the original (use with --resume or --continue)", () => true).addOption(new Option("--prefill <text>", "Pre-fill the prompt input with text without submitting it").hideHelp()).addOption(new Option("--deep-link-origin", "Signal that this session was launched from a deep link").hideHelp()).addOption(new Option("--deep-link-repo <slug>", "Repo slug the deep link ?repo= parameter resolved to the current cwd").hideHelp()).addOption(new Option("--deep-link-last-fetch <ms>", "FETCH_HEAD mtime in epoch ms, precomputed by the deep link trampoline").argParser((v) => {
88411
88694
  const n = Number(v);
88412
88695
  return Number.isFinite(n) ? n : undefined;
88413
88696
  }).hideHelp()).option("--from-pr [value]", "Resume a session linked to a PR by PR number/URL, or open interactive picker with optional search term", (value) => value || true).option("--no-session-persistence", "Disable session persistence - sessions will not be saved to disk and cannot be resumed (only works with --print)").addOption(new Option("--resume-session-at <message id>", "When resuming, only messages up to and including the assistant message with <message.id> (use with --resume in print mode)").argParser(String).hideHelp()).addOption(new Option("--rewind-files <user-message-id>", "Restore files to state at the specified user message and exit (requires --resume)").hideHelp()).option("--model <model>", "Model for the current session. Provide a role alias (e.g. 'small' or 'premium') or a provider/model reference (e.g. 'bailian/qwen38-27b').").addOption(new Option("--effort <level>", "Effort level for the current session (low, medium, high, xhigh, max)").argParser((rawValue) => {
@@ -88513,6 +88796,7 @@ async function runCli() {
88513
88796
  var init_parse4 = __esm(() => {
88514
88797
  init_extra_typings();
88515
88798
  init_bootstrap();
88799
+ init_engine();
88516
88800
  init_permissions3();
88517
88801
  init_shared();
88518
88802
  init_remote();
@@ -96691,34 +96975,8 @@ var init_envUtils2 = __esm(() => {
96691
96975
  });
96692
96976
 
96693
96977
  // src/tui/types/atlas.ts
96694
- var APIError3, AuthenticationError2, NotFoundError2, APIConnectionError3, APIUserAbortError3;
96695
96978
  var init_atlas = __esm(() => {
96696
- APIError3 = class APIError3 extends Error {
96697
- status;
96698
- code;
96699
- headers;
96700
- constructor(message, opts) {
96701
- super(message ?? "API error");
96702
- this.name = "APIError";
96703
- if (opts) {
96704
- this.status = opts.status;
96705
- this.code = opts.code;
96706
- this.headers = opts.headers;
96707
- }
96708
- }
96709
- };
96710
- AuthenticationError2 = class AuthenticationError2 extends APIError3 {
96711
- constructor(message, opts) {
96712
- super(message, opts);
96713
- this.name = "AuthenticationError";
96714
- }
96715
- };
96716
- NotFoundError2 = class NotFoundError2 extends APIError3 {
96717
- };
96718
- APIConnectionError3 = class APIConnectionError3 extends APIError3 {
96719
- };
96720
- APIUserAbortError3 = class APIUserAbortError3 extends APIError3 {
96721
- };
96979
+ init_types2();
96722
96980
  });
96723
96981
 
96724
96982
  // src/tui/utils/errors.ts
@@ -124104,7 +124362,12 @@ var isMcpServerCommandEntry = () => ({}), isMcpServerNameEntry = () => ({}), isM
124104
124362
  xaaIdp: any().optional(),
124105
124363
  agent: any().optional(),
124106
124364
  local: any().optional(),
124107
- worktree: any().optional()
124365
+ worktree: any().optional(),
124366
+ skillOverrides: union([
124367
+ literal("off"),
124368
+ literal("user-invocable-only"),
124369
+ literal("name-only")
124370
+ ]).optional()
124108
124371
  }).passthrough(), CUSTOMIZATION_SURFACES;
124109
124372
  var init_types7 = __esm(() => {
124110
124373
  init_zod();
@@ -124232,7 +124495,9 @@ var init_stringUtils2 = __esm(() => {
124232
124495
 
124233
124496
  // src/tui/services/mcp/normalization.ts
124234
124497
  function normalizeNameForMCP3(name) {
124235
- return name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
124498
+ const folded = name.replace(/[^a-zA-Z0-9_-]/g, "_");
124499
+ const normalized = folded.replace(/_+/g, "_").replace(/^_|_$/g, "");
124500
+ return normalized === "" ? folded : normalized;
124236
124501
  }
124237
124502
 
124238
124503
  // src/tui/services/mcp/mcpStringUtils.ts
@@ -151026,28 +151291,28 @@ function getEventPriority(eventType) {
151026
151291
  case "focus":
151027
151292
  case "blur":
151028
151293
  case "paste":
151029
- return import_constants53.DiscreteEventPriority;
151294
+ return import_constants54.DiscreteEventPriority;
151030
151295
  case "resize":
151031
151296
  case "scroll":
151032
151297
  case "mousemove":
151033
- return import_constants53.ContinuousEventPriority;
151298
+ return import_constants54.ContinuousEventPriority;
151034
151299
  default:
151035
- return import_constants53.DefaultEventPriority;
151300
+ return import_constants54.DefaultEventPriority;
151036
151301
  }
151037
151302
  }
151038
151303
 
151039
151304
  class Dispatcher {
151040
151305
  currentEvent = null;
151041
- currentUpdatePriority = import_constants53.DefaultEventPriority;
151306
+ currentUpdatePriority = import_constants54.DefaultEventPriority;
151042
151307
  discreteUpdates = null;
151043
151308
  resolveEventPriority() {
151044
- if (this.currentUpdatePriority !== import_constants53.NoEventPriority) {
151309
+ if (this.currentUpdatePriority !== import_constants54.NoEventPriority) {
151045
151310
  return this.currentUpdatePriority;
151046
151311
  }
151047
151312
  if (this.currentEvent) {
151048
151313
  return getEventPriority(this.currentEvent.type);
151049
151314
  }
151050
- return import_constants53.DefaultEventPriority;
151315
+ return import_constants54.DefaultEventPriority;
151051
151316
  }
151052
151317
  dispatch(target, event) {
151053
151318
  const previousEvent = this.currentEvent;
@@ -151072,18 +151337,18 @@ class Dispatcher {
151072
151337
  dispatchContinuous(target, event) {
151073
151338
  const previousPriority = this.currentUpdatePriority;
151074
151339
  try {
151075
- this.currentUpdatePriority = import_constants53.ContinuousEventPriority;
151340
+ this.currentUpdatePriority = import_constants54.ContinuousEventPriority;
151076
151341
  return this.dispatch(target, event);
151077
151342
  } finally {
151078
151343
  this.currentUpdatePriority = previousPriority;
151079
151344
  }
151080
151345
  }
151081
151346
  }
151082
- var import_constants53;
151347
+ var import_constants54;
151083
151348
  var init_dispatcher = __esm(() => {
151084
151349
  init_log5();
151085
151350
  init_event_handlers();
151086
- import_constants53 = __toESM(require_constants2(), 1);
151351
+ import_constants54 = __toESM(require_constants2(), 1);
151087
151352
  });
151088
151353
 
151089
151354
  // src/tui/ink/events/terminal-event.ts
@@ -159890,7 +160155,7 @@ function applyPositionedHighlight(screen, stylePool, positions, rowOffset, curre
159890
160155
  }
159891
160156
  return true;
159892
160157
  }
159893
- var import_constants55, timing;
160158
+ var import_constants56, timing;
159894
160159
  var init_render_to_screen = __esm(() => {
159895
160160
  init_debug();
159896
160161
  init_dom();
@@ -159899,7 +160164,7 @@ var init_render_to_screen = __esm(() => {
159899
160164
  init_reconciler();
159900
160165
  init_render_node_to_output();
159901
160166
  init_screen();
159902
- import_constants55 = __toESM(require_constants2(), 1);
160167
+ import_constants56 = __toESM(require_constants2(), 1);
159903
160168
  timing = { reconcile: 0, yoga: 0, paint: 0, scan: 0, calls: 0 };
159904
160169
  });
159905
160170
 
@@ -160205,7 +160470,7 @@ class Ink {
160205
160470
  };
160206
160471
  }
160207
160472
  };
160208
- this.container = reconciler_default.createContainer(this.rootNode, import_constants56.ConcurrentRoot, null, false, null, "id", noop_default, noop_default, noop_default, noop_default);
160473
+ this.container = reconciler_default.createContainer(this.rootNode, import_constants57.ConcurrentRoot, null, false, null, "id", noop_default, noop_default, noop_default, noop_default);
160209
160474
  if (false) {}
160210
160475
  }
160211
160476
  handleResume = () => {
@@ -161018,7 +161283,7 @@ function drainStdin(stdin = process.stdin) {
161018
161283
  }
161019
161284
  }
161020
161285
  }
161021
- var import_constants56, jsx_dev_runtime8, ALT_SCREEN_ANCHOR_CURSOR, CURSOR_HOME_PATCH, ERASE_THEN_HOME_PATCH, CONSOLE_STDOUT_METHODS, CONSOLE_STDERR_METHODS;
161286
+ var import_constants57, jsx_dev_runtime8, ALT_SCREEN_ANCHOR_CURSOR, CURSOR_HOME_PATCH, ERASE_THEN_HOME_PATCH, CONSOLE_STDOUT_METHODS, CONSOLE_STDERR_METHODS;
161022
161287
  var init_ink = __esm(() => {
161023
161288
  init_noop();
161024
161289
  init_throttle2();
@@ -161051,7 +161316,7 @@ var init_ink = __esm(() => {
161051
161316
  init_dec();
161052
161317
  init_osc();
161053
161318
  init_useTerminalNotification();
161054
- import_constants56 = __toESM(require_constants2(), 1);
161319
+ import_constants57 = __toESM(require_constants2(), 1);
161055
161320
  jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
161056
161321
  ALT_SCREEN_ANCHOR_CURSOR = Object.freeze({
161057
161322
  x: 0,
@@ -166814,7 +167079,7 @@ var init_addDirPluginSettings = __esm(() => {
166814
167079
  });
166815
167080
 
166816
167081
  // src/tui/entrypoints/sdk/coreSchemas.ts
166817
- var ModelUsageSchema, OutputFormatTypeSchema, BaseOutputFormatSchema, JsonSchemaOutputFormatSchema, OutputFormatSchema, ApiKeySourceSchema, ConfigScopeSchema2, SdkBetaSchema, ThinkingAdaptiveSchema, ThinkingEnabledSchema, ThinkingDisabledSchema, ThinkingConfigSchema, McpStdioServerConfigSchema2, McpSSEServerConfigSchema2, McpHttpServerConfigSchema, McpSdkServerConfigSchema, McpServerConfigForProcessTransportSchema, McpServerStatusConfigSchema, McpServerStatusSchema, McpSetServersResultSchema, PermissionUpdateDestinationSchema, PermissionBehaviorSchema, PreToolUsePermissionDecisionSchema, PermissionRuleValueSchema, PermissionUpdateSchema, PermissionDecisionClassificationSchema, PermissionResultSchema, PermissionModeSchema2, HOOK_EVENTS2, HookEventSchema, BaseHookInputSchema, PreToolUseHookInputSchema, PermissionRequestHookInputSchema, PostToolUseHookInputSchema, PostToolUseFailureHookInputSchema, PermissionDeniedHookInputSchema, NotificationHookInputSchema, UserPromptSubmitHookInputSchema, SessionStartHookInputSchema, PreModelSwitchHookInputSchema, PostModelSwitchHookInputSchema, SetupHookInputSchema, StopHookInputSchema, StopFailureHookInputSchema, SubagentStartHookInputSchema, SubagentStopHookInputSchema, PreCompactHookInputSchema, PostCompactHookInputSchema, TeammateIdleHookInputSchema, TaskCreatedHookInputSchema, TaskCompletedHookInputSchema, ElicitationHookInputSchema, ElicitationResultHookInputSchema, CONFIG_CHANGE_SOURCES, ConfigChangeHookInputSchema, INSTRUCTIONS_LOAD_REASONS, INSTRUCTIONS_MEMORY_TYPES, InstructionsLoadedHookInputSchema, WorktreeCreateHookInputSchema, WorktreeRemoveHookInputSchema, CwdChangedHookInputSchema, FileChangedHookInputSchema, EXIT_REASONS, ExitReasonSchema, SessionEndHookInputSchema, HookInputSchema, AsyncHookJSONOutputSchema, PreToolUseHookSpecificOutputSchema, UserPromptSubmitHookSpecificOutputSchema, SessionStartHookSpecificOutputSchema, SetupHookSpecificOutputSchema, SubagentStartHookSpecificOutputSchema, PostToolUseHookSpecificOutputSchema, PostToolUseFailureHookSpecificOutputSchema, PermissionDeniedHookSpecificOutputSchema, NotificationHookSpecificOutputSchema, PermissionRequestHookSpecificOutputSchema, CwdChangedHookSpecificOutputSchema, FileChangedHookSpecificOutputSchema, SyncHookJSONOutputSchema, ElicitationHookSpecificOutputSchema, ElicitationResultHookSpecificOutputSchema, WorktreeCreateHookSpecificOutputSchema, HookJSONOutputSchema, PromptRequestOptionSchema, PromptRequestSchema, PromptResponseSchema, SlashCommandSchema, AgentInfoSchema, ModelInfoSchema, AccountInfoSchema, AgentMcpServerSpecSchema, AgentDefinitionSchema, SettingSourceSchema, SdkPluginConfigSchema, RewindFilesResultSchema, APIUserMessagePlaceholder, APIAssistantMessagePlaceholder, RawMessageStreamEventPlaceholder, UUIDPlaceholder, NonNullableUsagePlaceholder, SDKAssistantMessageErrorSchema, SDKStatusSchema, SDKUserMessageContentSchema, SDKUserMessageSchema, SDKUserMessageReplaySchema, SDKRateLimitInfoSchema, SDKAssistantMessageSchema, SDKRateLimitEventSchema, SDKStreamlinedTextMessageSchema, SDKStreamlinedToolUseSummaryMessageSchema, SDKPermissionDenialSchema, SDKResultSuccessSchema, SDKResultErrorSchema, SDKResultMessageSchema, SDKSystemMessageSchema, SDKPartialAssistantMessageSchema, SDKCompactBoundaryMessageSchema, SDKStatusMessageSchema, SDKPostTurnSummaryMessageSchema, SDKAPIRetryMessageSchema, SDKLocalCommandOutputMessageSchema, SDKHookStartedMessageSchema, SDKHookProgressMessageSchema, SDKHookResponseMessageSchema, SDKToolProgressMessageSchema, SDKAuthStatusMessageSchema, SDKFilesPersistedEventSchema, SDKTaskNotificationMessageSchema, SDKTaskStartedMessageSchema, SDKSessionStateChangedMessageSchema, SDKTaskProgressMessageSchema, SDKToolUseSummaryMessageSchema, SDKElicitationCompleteMessageSchema, SDKPromptSuggestionMessageSchema, SDKSessionInfoSchema, SDKMessageSchema, FastModeStateSchema;
167082
+ var ModelUsageSchema, OutputFormatTypeSchema, BaseOutputFormatSchema, JsonSchemaOutputFormatSchema, OutputFormatSchema, ApiKeySourceSchema, ConfigScopeSchema2, SdkBetaSchema, ThinkingAdaptiveSchema, ThinkingEnabledSchema, ThinkingDisabledSchema, ThinkingConfigSchema, McpStdioServerConfigSchema2, McpSSEServerConfigSchema2, McpHttpServerConfigSchema, McpSdkServerConfigSchema, McpServerConfigForProcessTransportSchema, McpServerStatusConfigSchema, McpServerStatusSchema, McpSetServersResultSchema, PermissionUpdateDestinationSchema, PermissionBehaviorSchema, PreToolUsePermissionDecisionSchema, PermissionRuleValueSchema, PermissionUpdateSchema, PermissionDecisionClassificationSchema, PermissionResultSchema, PermissionModeSchema2, HOOK_EVENTS2, HookEventSchema, BaseHookInputSchema, PreToolUseHookInputSchema, PermissionRequestHookInputSchema, PostToolUseHookInputSchema, PostToolUseFailureHookInputSchema, PermissionDeniedHookInputSchema, NotificationHookInputSchema, UserPromptSubmitHookInputSchema, SessionStartHookInputSchema, PreModelSwitchHookInputSchema, PostModelSwitchHookInputSchema, DirectoryAddedHookInputSchema, MessageDisplayHookInputSchema, SetupHookInputSchema, StopHookInputSchema, StopFailureHookInputSchema, SubagentStartHookInputSchema, SubagentStopHookInputSchema, PreCompactHookInputSchema, PostCompactHookInputSchema, TeammateIdleHookInputSchema, TaskCreatedHookInputSchema, TaskCompletedHookInputSchema, ElicitationHookInputSchema, ElicitationResultHookInputSchema, CONFIG_CHANGE_SOURCES, ConfigChangeHookInputSchema, INSTRUCTIONS_LOAD_REASONS, INSTRUCTIONS_MEMORY_TYPES, InstructionsLoadedHookInputSchema, WorktreeCreateHookInputSchema, WorktreeRemoveHookInputSchema, CwdChangedHookInputSchema, FileChangedHookInputSchema, EXIT_REASONS, ExitReasonSchema, SessionEndHookInputSchema, HookInputSchema, AsyncHookJSONOutputSchema, PreToolUseHookSpecificOutputSchema, UserPromptSubmitHookSpecificOutputSchema, SessionStartHookSpecificOutputSchema, SetupHookSpecificOutputSchema, SubagentStartHookSpecificOutputSchema, PostToolUseHookSpecificOutputSchema, PostToolUseFailureHookSpecificOutputSchema, PermissionDeniedHookSpecificOutputSchema, NotificationHookSpecificOutputSchema, PermissionRequestHookSpecificOutputSchema, CwdChangedHookSpecificOutputSchema, FileChangedHookSpecificOutputSchema, SyncHookJSONOutputSchema, ElicitationHookSpecificOutputSchema, ElicitationResultHookSpecificOutputSchema, WorktreeCreateHookSpecificOutputSchema, HookJSONOutputSchema, PromptRequestOptionSchema, PromptRequestSchema, PromptResponseSchema, SlashCommandSchema, AgentInfoSchema, ModelInfoSchema, AccountInfoSchema, AgentMcpServerSpecSchema, AgentDefinitionSchema, SettingSourceSchema, SdkPluginConfigSchema, RewindFilesResultSchema, APIUserMessagePlaceholder, APIAssistantMessagePlaceholder, RawMessageStreamEventPlaceholder, UUIDPlaceholder, NonNullableUsagePlaceholder, SDKAssistantMessageErrorSchema, SDKStatusSchema, SDKUserMessageContentSchema, SDKUserMessageSchema, SDKUserMessageReplaySchema, SDKRateLimitInfoSchema, SDKAssistantMessageSchema, SDKRateLimitEventSchema, SDKStreamlinedTextMessageSchema, SDKStreamlinedToolUseSummaryMessageSchema, SDKPermissionDenialSchema, SDKResultSuccessSchema, SDKResultErrorSchema, SDKResultMessageSchema, SDKSystemMessageSchema, SDKPartialAssistantMessageSchema, SDKCompactBoundaryMessageSchema, SDKStatusMessageSchema, SDKPostTurnSummaryMessageSchema, SDKAPIRetryMessageSchema, SDKLocalCommandOutputMessageSchema, SDKHookStartedMessageSchema, SDKHookProgressMessageSchema, SDKHookResponseMessageSchema, SDKToolProgressMessageSchema, SDKAuthStatusMessageSchema, SDKFilesPersistedEventSchema, SDKTaskNotificationMessageSchema, SDKTaskStartedMessageSchema, SDKSessionStateChangedMessageSchema, SDKTaskProgressMessageSchema, SDKToolUseSummaryMessageSchema, SDKElicitationCompleteMessageSchema, SDKPromptSuggestionMessageSchema, SDKSessionInfoSchema, SDKMessageSchema, FastModeStateSchema;
166818
167083
  var init_coreSchemas = __esm(() => {
166819
167084
  init_v4();
166820
167085
  ModelUsageSchema = lazySchema2(() => object({
@@ -167003,7 +167268,9 @@ var init_coreSchemas = __esm(() => {
167003
167268
  "CwdChanged",
167004
167269
  "FileChanged",
167005
167270
  "PreModelSwitch",
167006
- "PostModelSwitch"
167271
+ "PostModelSwitch",
167272
+ "DirectoryAdded",
167273
+ "MessageDisplay"
167007
167274
  ];
167008
167275
  HookEventSchema = lazySchema2(() => _enum(HOOK_EVENTS2));
167009
167276
  BaseHookInputSchema = lazySchema2(() => object({
@@ -167076,6 +167343,15 @@ var init_coreSchemas = __esm(() => {
167076
167343
  new_model: string2(),
167077
167344
  source: string2()
167078
167345
  })));
167346
+ DirectoryAddedHookInputSchema = lazySchema2(() => BaseHookInputSchema().and(object({
167347
+ hook_event_name: literal("DirectoryAdded"),
167348
+ directory: string2(),
167349
+ source: string2()
167350
+ })));
167351
+ MessageDisplayHookInputSchema = lazySchema2(() => BaseHookInputSchema().and(object({
167352
+ hook_event_name: literal("MessageDisplay"),
167353
+ message: string2()
167354
+ })));
167079
167355
  SetupHookInputSchema = lazySchema2(() => BaseHookInputSchema().and(object({
167080
167356
  hook_event_name: literal("Setup"),
167081
167357
  trigger: _enum(["init", "maintenance"])
@@ -180714,6 +180990,7 @@ __export(exports_engineCompat, {
180714
180990
  getLastSummarizedMessageId: () => getLastSummarizedMessageId,
180715
180991
  getManagedSettingsDir: () => getManagedSettingsDir,
180716
180992
  getManagedSettingsDropInDir: () => getManagedSettingsDropInDir,
180993
+ getMcpAutoBackgroundMs: () => getMcpAutoBackgroundMs,
180717
180994
  getMcpClientRegistry: () => getMcpClientRegistry,
180718
180995
  getMcpPrefix: () => getMcpPrefix2,
180719
180996
  getMcpSkillCommands: () => getMcpSkillCommands,
@@ -180833,11 +181110,13 @@ __export(exports_engineCompat, {
180833
181110
  isReactiveCompactRecoverableError: () => isReactiveCompactRecoverableError,
180834
181111
  isReactiveOnlyMode: () => isReactiveOnlyMode,
180835
181112
  isRecurringTaskAged: () => isRecurringTaskAged,
181113
+ isRestrictedMode: () => isRestrictedMode,
180836
181114
  isSafeRefName: () => isSafeRefName,
180837
181115
  isSandboxPermissionRequest: () => isSandboxPermissionRequest,
180838
181116
  isSandboxPermissionResponse: () => isSandboxPermissionResponse,
180839
181117
  isSessionMemoryInitialized: () => isSessionMemoryInitialized,
180840
181118
  isSettingSourceEnabled: () => isSettingSourceEnabled,
181119
+ isSettingsSourceIgnoredInRestricted: () => isSettingsSourceIgnoredInRestricted,
180841
181120
  isShutdownApproved: () => isShutdownApproved,
180842
181121
  isShutdownRejected: () => isShutdownRejected,
180843
181122
  isShutdownRequest: () => isShutdownRequest,
@@ -180983,6 +181262,7 @@ __export(exports_engineCompat, {
180983
181262
  resetPendingNotifications: () => resetPendingNotifications,
180984
181263
  resetProjectFlushStateForTesting: () => resetProjectFlushStateForTesting,
180985
181264
  resetProjectForTesting: () => resetProjectForTesting,
181265
+ resetRestrictedMode: () => resetRestrictedMode,
180986
181266
  resetSchedulerEnv: () => resetSchedulerEnv,
180987
181267
  resetSdkEventQueueForTesting: () => resetSdkEventQueueForTesting,
180988
181268
  resetSessionContextPort: () => resetSessionContextPort,
@@ -181005,6 +181285,7 @@ __export(exports_engineCompat, {
181005
181285
  restoreGoalState: () => restoreGoalState,
181006
181286
  restoreSessionMetadata: () => restoreSessionMetadata,
181007
181287
  restoreWorktreeSession: () => restoreWorktreeSession,
181288
+ restrictedBypassError: () => restrictedBypassError,
181008
181289
  resumeGoal: () => resumeGoal,
181009
181290
  roughTokenCountEstimation: () => roughTokenCountEstimation2,
181010
181291
  roughTokenCountEstimationForContent: () => roughTokenCountEstimationForContent,
@@ -181041,6 +181322,7 @@ __export(exports_engineCompat, {
181041
181322
  setPartialCompactPort: () => setPartialCompactPort,
181042
181323
  setPluginSettingsBase: () => setPluginSettingsBase,
181043
181324
  setReactiveCompactPort: () => setReactiveCompactPort,
181325
+ setRestrictedMode: () => setRestrictedMode,
181044
181326
  setSchedulerEnv: () => setSchedulerEnv,
181045
181327
  setSessionContextPort: () => setSessionContextPort,
181046
181328
  setSessionEnv: () => setSessionEnv,
@@ -256424,6 +256706,8 @@ async function* queryEngineLoopStream(params) {
256424
256706
  pending.push(event.messages[0]);
256425
256707
  } else if (event.type === "stream_delta") {
256426
256708
  pending.push(event);
256709
+ } else if (event.type === "tool_heartbeat") {
256710
+ pending.push(event);
256427
256711
  }
256428
256712
  notify();
256429
256713
  }
@@ -274444,7 +274728,7 @@ var init_apiErrors = __esm(() => {
274444
274728
  });
274445
274729
  },
274446
274730
  resolveAuthErrorMessaging: (error, _model) => {
274447
- if (error instanceof APIError3 && error.status === 400 && error.message.toLowerCase().includes("organization has been disabled")) {
274731
+ if (error instanceof APIError2 && error.status === 400 && error.message.toLowerCase().includes("organization has been disabled")) {
274448
274732
  const { source } = getAtlasApiKeyWithSource();
274449
274733
  if (source === "OPENAI_API_KEY" && process.env.OPENAI_API_KEY) {
274450
274734
  const hasStoredOAuth = getOAuthTokens()?.accessToken != null;
@@ -274469,13 +274753,13 @@ var init_apiErrors = __esm(() => {
274469
274753
  content: isExternalSource ? INVALID_API_KEY_ERROR_MESSAGE_EXTERNAL : INVALID_API_KEY_ERROR_MESSAGE
274470
274754
  });
274471
274755
  }
274472
- if (error instanceof APIError3 && error.status === 403 && error.message.includes("OAuth token has been revoked")) {
274756
+ if (error instanceof APIError2 && error.status === 403 && error.message.includes("OAuth token has been revoked")) {
274473
274757
  return createAssistantAPIErrorMessage2({
274474
274758
  error: "authentication_failed",
274475
274759
  content: getTokenRevokedErrorMessage()
274476
274760
  });
274477
274761
  }
274478
- if (error instanceof APIError3 && (error.status === 401 || error.status === 403) && error.message.includes("OAuth authentication is currently not allowed for this organization")) {
274762
+ if (error instanceof APIError2 && (error.status === 401 || error.status === 403) && error.message.includes("OAuth authentication is currently not allowed for this organization")) {
274479
274763
  return createAssistantAPIErrorMessage2({
274480
274764
  error: "authentication_failed",
274481
274765
  content: getOauthOrgNotAllowedErrorMessage()
@@ -274495,7 +274779,7 @@ var init_apiErrors = __esm(() => {
274495
274779
  content: getIsNonInteractiveSession() ? `${API_ERROR_MESSAGE_PREFIX}: provider quota exhausted (403). Top up your provider quota, or switch provider/model via --model, then retry.` : `${API_ERROR_MESSAGE_PREFIX}: provider quota exhausted (403). Top up your provider quota, or run /model to switch provider/model, then retry.`
274496
274780
  });
274497
274781
  }
274498
- if (error instanceof APIError3 && (error.status === 401 || error.status === 403)) {
274782
+ if (error instanceof APIError2 && (error.status === 401 || error.status === 403)) {
274499
274783
  if (isCCRMode()) {
274500
274784
  return createAssistantAPIErrorMessage2({
274501
274785
  error: "authentication_failed",
@@ -299518,14 +299802,14 @@ function handleValidationError(error, modelName) {
299518
299802
  error: `Model '${modelName}' not found`
299519
299803
  };
299520
299804
  }
299521
- if (error instanceof APIError3) {
299805
+ if (error instanceof APIError2) {
299522
299806
  if (error instanceof AuthenticationError2) {
299523
299807
  return {
299524
299808
  valid: false,
299525
299809
  error: "Authentication failed. Please check your API credentials."
299526
299810
  };
299527
299811
  }
299528
- if (error instanceof APIConnectionError3) {
299812
+ if (error instanceof APIConnectionError2) {
299529
299813
  return {
299530
299814
  valid: false,
299531
299815
  error: "Network error. Please check your internet connection."
@@ -308884,7 +309168,7 @@ async function executeAsyncClassifierCheck2(pendingCheck, signal, isNonInteracti
308884
309168
  try {
308885
309169
  classifierResult = speculativeResult ? await speculativeResult : await classifyBashCommand2(command, cwd, descriptions, "allow", signal, isNonInteractiveSession);
308886
309170
  } catch (error) {
308887
- if (error instanceof APIUserAbortError3 || error instanceof AbortError2) {
309171
+ if (error instanceof APIUserAbortError2 || error instanceof AbortError2) {
308888
309172
  callbacks.onComplete?.();
308889
309173
  return;
308890
309174
  }
@@ -335371,6 +335655,22 @@ ${EXPLANATORY_FEATURE_PROMPT}`
335371
335655
  });
335372
335656
  });
335373
335657
 
335658
+ // src/tui/utils/messageDisplayHooks.ts
335659
+ function fireMessageDisplayHooks(message) {
335660
+ (async () => {
335661
+ try {
335662
+ for await (const _hookResult of executeMessageDisplayHooks(message, undefined, MESSAGE_DISPLAY_HOOK_TIMEOUT_MS)) {}
335663
+ } catch (e) {
335664
+ logForDebugging3(`MessageDisplay hook execution failed: ${e instanceof Error ? e.message : String(e)}`);
335665
+ }
335666
+ })();
335667
+ }
335668
+ var MESSAGE_DISPLAY_HOOK_TIMEOUT_MS = 15000;
335669
+ var init_messageDisplayHooks = __esm(() => {
335670
+ init_debug();
335671
+ init_hooks7();
335672
+ });
335673
+
335374
335674
  // src/tui/utils/messages.ts
335375
335675
  import { randomUUID as randomUUID36 } from "crypto";
335376
335676
  function isFeatureOn(name) {
@@ -336787,8 +337087,8 @@ function getContentText(content) {
336787
337087
  }
336788
337088
  return null;
336789
337089
  }
336790
- function handleMessageFromStream(message, onMessage, onUpdateLength, onSetStreamMode, onStreamingToolUses, onTombstone, onStreamingThinking, onApiMetrics, onStreamingText) {
336791
- if (message.type !== "stream_event" && message.type !== "stream_request_start" && message.type !== "stream_delta") {
337090
+ function handleMessageFromStream(message, onMessage, onUpdateLength, onSetStreamMode, onStreamingToolUses, onTombstone, onStreamingThinking, onApiMetrics, onStreamingText, onToolHeartbeat) {
337091
+ if (message.type !== "stream_event" && message.type !== "stream_request_start" && message.type !== "stream_delta" && message.type !== "tool_heartbeat") {
336792
337092
  if (message.type === "tombstone") {
336793
337093
  onTombstone?.(message.message);
336794
337094
  return;
@@ -336805,6 +337105,9 @@ function handleMessageFromStream(message, onMessage, onUpdateLength, onSetStream
336805
337105
  streamingEndedAt: Date.now()
336806
337106
  }));
336807
337107
  }
337108
+ const displayText = getContentText(message.message.content);
337109
+ if (displayText)
337110
+ fireMessageDisplayHooks(displayText);
336808
337111
  }
336809
337112
  onStreamingText?.(() => null);
336810
337113
  onMessage(message);
@@ -336814,6 +337117,10 @@ function handleMessageFromStream(message, onMessage, onUpdateLength, onSetStream
336814
337117
  onSetStreamMode("requesting");
336815
337118
  return;
336816
337119
  }
337120
+ if (message.type === "tool_heartbeat") {
337121
+ onToolHeartbeat?.({ toolName: message.toolName, elapsedMs: message.elapsedMs });
337122
+ return;
337123
+ }
336817
337124
  if (message.type === "stream_delta") {
336818
337125
  if (message.delta.type === "text_delta") {
336819
337126
  onSetStreamMode("responding");
@@ -338269,6 +338576,7 @@ var init_messages3 = __esm(() => {
338269
338576
  init_shared();
338270
338577
  init_last();
338271
338578
  init_outputStyles();
338579
+ init_messageDisplayHooks();
338272
338580
  init_paths3();
338273
338581
  init_growthbook();
338274
338582
  init_modelprovider2();
@@ -339510,7 +339818,7 @@ async function checkRuleBasedPermissions(tool, input, context) {
339510
339818
  const parsedInput = tool.inputSchema.parse(input);
339511
339819
  toolPermissionResult = await tool.checkPermissions(parsedInput, context);
339512
339820
  } catch (e) {
339513
- if (e instanceof AbortError2 || e instanceof APIUserAbortError3) {
339821
+ if (e instanceof AbortError2 || e instanceof APIUserAbortError2) {
339514
339822
  throw e;
339515
339823
  }
339516
339824
  logError3(e);
@@ -339564,7 +339872,7 @@ async function hasPermissionsToUseToolInner(tool, input, context) {
339564
339872
  const parsedInput = tool.inputSchema.parse(input);
339565
339873
  toolPermissionResult = await tool.checkPermissions(parsedInput, context);
339566
339874
  } catch (e) {
339567
- if (e instanceof AbortError2 || e instanceof APIUserAbortError3) {
339875
+ if (e instanceof AbortError2 || e instanceof APIUserAbortError2) {
339568
339876
  throw e;
339569
339877
  }
339570
339878
  logError3(e);
@@ -339798,7 +340106,7 @@ var classifierDecisionModule, autoModeStateModule3, CLASSIFIER_FAIL_CLOSED_REFRE
339798
340106
  };
339799
340107
  }
339800
340108
  } catch (e) {
339801
- if (e instanceof AbortError2 || e instanceof APIUserAbortError3) {
340109
+ if (e instanceof AbortError2 || e instanceof APIUserAbortError2) {
339802
340110
  throw e;
339803
340111
  }
339804
340112
  }
@@ -345033,6 +345341,22 @@ var init_AddWorkspaceDirectory = __esm(() => {
345033
345341
  }];
345034
345342
  });
345035
345343
 
345344
+ // src/tui/utils/directoryAddedHooks.ts
345345
+ function fireDirectoryAddedHooks(directory, source) {
345346
+ (async () => {
345347
+ try {
345348
+ for await (const _hookResult of executeDirectoryAddedHooks(directory, source, undefined, DIRECTORY_ADDED_HOOK_TIMEOUT_MS)) {}
345349
+ } catch (e) {
345350
+ logForDebugging3(`DirectoryAdded hook execution failed: ${e instanceof Error ? e.message : String(e)}`);
345351
+ }
345352
+ })();
345353
+ }
345354
+ var DIRECTORY_ADDED_HOOK_TIMEOUT_MS = 15000;
345355
+ var init_directoryAddedHooks = __esm(() => {
345356
+ init_debug();
345357
+ init_hooks7();
345358
+ });
345359
+
345036
345360
  // src/tui/commands/add-dir/add-dir.tsx
345037
345361
  var exports_add_dir = {};
345038
345362
  __export(exports_add_dir, {
@@ -345138,6 +345462,7 @@ async function call2(onDone, context, args) {
345138
345462
  message = `Added ${source_default.bold(path)} as a working directory for this session`;
345139
345463
  }
345140
345464
  const messageWithHint = `${message} ${source_default.dim("· /permissions to manage")}`;
345465
+ fireDirectoryAddedHooks(path, destination);
345141
345466
  onDone(messageWithHint);
345142
345467
  };
345143
345468
  if (!directoryPath) {
@@ -345178,6 +345503,7 @@ var init_add_dir = __esm(() => {
345178
345503
  init_PermissionUpdate();
345179
345504
  init_sandboxCompat();
345180
345505
  init_validation4();
345506
+ init_directoryAddedHooks();
345181
345507
  import_compiler_runtime110 = __toESM(require_compiler_runtime(), 1);
345182
345508
  import_react77 = __toESM(require_react(), 1);
345183
345509
  jsx_dev_runtime132 = __toESM(require_jsx_dev_runtime(), 1);
@@ -398925,7 +399251,7 @@ function GenerateStep() {
398925
399251
  });
398926
399252
  goToStep(6);
398927
399253
  } catch (err) {
398928
- if (err instanceof APIUserAbortError3) {} else if (err instanceof Error && !err.message.includes("No assistant message found")) {
399254
+ if (err instanceof APIUserAbortError2) {} else if (err instanceof Error && !err.message.includes("No assistant message found")) {
398929
399255
  setError(err.message || "Failed to generate agent");
398930
399256
  }
398931
399257
  updateWizardData({
@@ -427439,6 +427765,7 @@ var init_hooks6 = __esm(() => {
427439
427765
  decision: _enum(["approve", "block"]).optional(),
427440
427766
  reason: string2().describe("Explanation for the decision").optional(),
427441
427767
  systemMessage: string2().describe("Warning message shown to the user").optional(),
427768
+ terminalSequence: string2().optional(),
427442
427769
  hookSpecificOutput: union([
427443
427770
  object({
427444
427771
  hookEventName: literal("PreToolUse"),
@@ -428447,6 +428774,9 @@ function processHookJSONOutput({
428447
428774
  if (json.systemMessage) {
428448
428775
  result.systemMessage = json.systemMessage;
428449
428776
  }
428777
+ if (json.terminalSequence) {
428778
+ result.terminalSequence = json.terminalSequence;
428779
+ }
428450
428780
  if (json.hookSpecificOutput?.hookEventName === "PreToolUse" && json.hookSpecificOutput.permissionDecision) {
428451
428781
  switch (json.hookSpecificOutput.permissionDecision) {
428452
428782
  case "allow":
@@ -429087,6 +429417,9 @@ async function getMatchingHooks2(appState, sessionId, hookEvent, hookInput, tool
429087
429417
  case "PostModelSwitch":
429088
429418
  matchQuery = hookInput.new_model;
429089
429419
  break;
429420
+ case "DirectoryAdded":
429421
+ matchQuery = hookInput.directory;
429422
+ break;
429090
429423
  default:
429091
429424
  break;
429092
429425
  }
@@ -429764,6 +430097,11 @@ async function* executeHooks({
429764
430097
  let permissionBehavior;
429765
430098
  for await (const result of all2(hookPromises)) {
429766
430099
  outcomes[result.outcome]++;
430100
+ if (result.terminalSequence) {
430101
+ try {
430102
+ process.stdout.write(result.terminalSequence);
430103
+ } catch {}
430104
+ }
429767
430105
  if (result.preventContinuation) {
429768
430106
  logForDebugging3(`Hook ${hookEvent} (${getHookDisplayText(result.hook)}) requested preventContinuation`);
429769
430107
  yield {
@@ -430259,6 +430597,35 @@ async function* executePostModelSwitchHooks(oldModel, newModel, source, signal,
430259
430597
  timeoutMs
430260
430598
  });
430261
430599
  }
430600
+ async function* executeDirectoryAddedHooks(directory, source, signal, timeoutMs = TOOL_HOOK_EXECUTION_TIMEOUT_MS) {
430601
+ const hookInput = {
430602
+ ...createBaseHookInput2(undefined),
430603
+ hook_event_name: "DirectoryAdded",
430604
+ directory,
430605
+ source
430606
+ };
430607
+ yield* executeHooks({
430608
+ hookInput,
430609
+ toolUseID: randomUUID43(),
430610
+ matchQuery: directory,
430611
+ signal,
430612
+ timeoutMs
430613
+ });
430614
+ }
430615
+ async function* executeMessageDisplayHooks(message, signal, timeoutMs = TOOL_HOOK_EXECUTION_TIMEOUT_MS) {
430616
+ const hookInput = {
430617
+ ...createBaseHookInput2(undefined),
430618
+ hook_event_name: "MessageDisplay",
430619
+ message
430620
+ };
430621
+ yield* executeHooks({
430622
+ hookInput,
430623
+ toolUseID: randomUUID43(),
430624
+ matchQuery: undefined,
430625
+ signal,
430626
+ timeoutMs
430627
+ });
430628
+ }
430262
430629
  async function* executeSetupHooks(trigger, signal, timeoutMs = TOOL_HOOK_EXECUTION_TIMEOUT_MS, forceSyncExecution) {
430263
430630
  const hookInput = {
430264
430631
  ...createBaseHookInput2(undefined),
@@ -431922,7 +432289,7 @@ async function streamCompactSummary({
431922
432289
  }
431923
432290
  if (attempt < maxAttempts) {
431924
432291
  await sleep9(getRetryDelay(attempt), context.abortController.signal, {
431925
- abortError: () => new APIUserAbortError3
432292
+ abortError: () => new APIUserAbortError2
431926
432293
  });
431927
432294
  continue;
431928
432295
  }
@@ -433174,13 +433541,17 @@ function truncateEntrypointContent(raw) {
433174
433541
  const byteCount = trimmed.length;
433175
433542
  const wasLineTruncated = lineCount > MAX_ENTRYPOINT_LINES;
433176
433543
  const wasByteTruncated = byteCount > MAX_ENTRYPOINT_BYTES;
433544
+ const nearLimit = (lineCount >= NEAR_LIMIT_LINES || byteCount >= NEAR_LIMIT_BYTES) && !wasLineTruncated && !wasByteTruncated;
433177
433545
  if (!wasLineTruncated && !wasByteTruncated) {
433178
433546
  return {
433179
- content: trimmed,
433547
+ content: nearLimit ? trimmed + `
433548
+
433549
+ > NOTE: ${ENTRYPOINT_NAME} is close to the read limit (${lineCount} lines / ${formatFileSize3(byteCount)}; limits: ${MAX_ENTRYPOINT_LINES} lines / ${formatFileSize3(MAX_ENTRYPOINT_BYTES)}). ${COMPACT_NOW_DIRECTIVE}` : trimmed,
433180
433550
  lineCount,
433181
433551
  byteCount,
433182
433552
  wasLineTruncated,
433183
- wasByteTruncated
433553
+ wasByteTruncated,
433554
+ nearLimit
433184
433555
  };
433185
433556
  }
433186
433557
  let truncated = wasLineTruncated ? contentLines.slice(0, MAX_ENTRYPOINT_LINES).join(`
@@ -433194,11 +433565,12 @@ function truncateEntrypointContent(raw) {
433194
433565
  return {
433195
433566
  content: truncated + `
433196
433567
 
433197
- > WARNING: ${ENTRYPOINT_NAME} is ${reason}. Only part of it was loaded. Keep index entries to one line under ~200 chars; move detail into topic files.`,
433568
+ > WARNING: ${ENTRYPOINT_NAME} is ${reason}. Only part of it was loaded. Keep index entries to one line under ~200 chars; move detail into topic files. ${COMPACT_NOW_DIRECTIVE}`,
433198
433569
  lineCount,
433199
433570
  byteCount,
433200
433571
  wasLineTruncated,
433201
- wasByteTruncated
433572
+ wasByteTruncated,
433573
+ nearLimit: true
433202
433574
  };
433203
433575
  }
433204
433576
  async function ensureMemoryDirExists(memoryDir, store) {
@@ -433342,7 +433714,7 @@ async function loadMemoryPrompt(store) {
433342
433714
  if (getFeatureValue_CACHED_MAY_BE_STALE("atlas_herring_clock", false)) {}
433343
433715
  return null;
433344
433716
  }
433345
- var teamMemPaths5, ENTRYPOINT_NAME = "MEMORY.md", MAX_ENTRYPOINT_LINES = 200, MAX_ENTRYPOINT_BYTES = 25000, teamMemPrompts, DIR_EXISTS_GUIDANCE = "This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence).", DIRS_EXIST_GUIDANCE = "Both directories already exist — write to them directly with the Write tool (do not run mkdir or check for their existence).";
433717
+ var teamMemPaths5, ENTRYPOINT_NAME = "MEMORY.md", MAX_ENTRYPOINT_LINES = 200, MAX_ENTRYPOINT_BYTES = 25000, NEAR_LIMIT_FRACTION = 0.8, NEAR_LIMIT_LINES, NEAR_LIMIT_BYTES, COMPACT_NOW_DIRECTIVE = "Compact it now: merge duplicate entries and prune stale ones before adding new lines.", teamMemPrompts, DIR_EXISTS_GUIDANCE = "This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence).", DIRS_EXIST_GUIDANCE = "Both directories already exist — write to them directly with the Write tool (do not run mkdir or check for their existence).";
433346
433718
  var init_memdir = __esm(() => {
433347
433719
  init_shared();
433348
433720
  init_memory();
@@ -433360,6 +433732,8 @@ var init_memdir = __esm(() => {
433360
433732
  init_sessionStorage();
433361
433733
  init_memoryTypes2();
433362
433734
  teamMemPaths5 = feature("TEAMMEM") ? (init_teamMemPaths(), __toCommonJS(exports_teamMemPaths)) : null;
433735
+ NEAR_LIMIT_LINES = Math.floor(MAX_ENTRYPOINT_LINES * NEAR_LIMIT_FRACTION);
433736
+ NEAR_LIMIT_BYTES = Math.floor(MAX_ENTRYPOINT_BYTES * NEAR_LIMIT_FRACTION);
433363
433737
  teamMemPrompts = feature("TEAMMEM") ? (init_teamMemPrompts(), __toCommonJS(exports_teamMemPrompts)) : null;
433364
433738
  });
433365
433739
 
@@ -468545,7 +468919,7 @@ function useCanUseTool(setToolUseConfirmQueue, setToolPermissionContext) {
468545
468919
  }
468546
468920
  }
468547
468921
  }).catch((error) => {
468548
- if (error instanceof AbortError2 || error instanceof APIUserAbortError3) {
468922
+ if (error instanceof AbortError2 || error instanceof APIUserAbortError2) {
468549
468923
  logForDebugging3(`Permission check threw ${error.constructor.name} for tool=${tool.name}: ${error.message}`);
468550
468924
  ctx.logCancelled();
468551
468925
  resolve(ctx.cancelAndAbort(undefined, true));
@@ -471807,7 +472181,7 @@ async function generateAwaySummary(messages, signal) {
471807
472181
  });
471808
472182
  return getAssistantMessageText2(response);
471809
472183
  } catch (err) {
471810
- if (err instanceof APIUserAbortError3 || signal.aborted) {
472184
+ if (err instanceof APIUserAbortError2 || signal.aborted) {
471811
472185
  return null;
471812
472186
  }
471813
472187
  logForDebugging3(`[awaySummary] generation failed: ${err}`);
@@ -479447,6 +479821,9 @@ Error: sandbox required but unavailable: ${reason}
479447
479821
  onBackgroundQuery: handleBackgroundQuery
479448
479822
  });
479449
479823
  const onQueryEvent = import_react282.useCallback((event) => {
479824
+ if (event.type === "stream_request_start") {
479825
+ setAppState((prev) => ({ ...prev, spinnerTip: undefined }));
479826
+ }
479450
479827
  handleMessageFromStream(event, (newMessage) => {
479451
479828
  if (isCompactBoundaryMessage2(newMessage)) {
479452
479829
  if (isFullscreenEnvEnabled()) {
@@ -479485,8 +479862,13 @@ Error: sandbox required but unavailable: ${reason}
479485
479862
  responseLengthBaseline: baseline,
479486
479863
  endResponseLength: baseline
479487
479864
  });
479488
- }, onStreamingText);
479489
- }, [setMessages, setResponseLength, setStreamMode, setStreamingToolUses, setStreamingThinking, onStreamingText]);
479865
+ }, onStreamingText, (info) => {
479866
+ setAppState((prev) => ({
479867
+ ...prev,
479868
+ spinnerTip: `${info.toolName} 仍在运行 · ${Math.round(info.elapsedMs / 1000)}s`
479869
+ }));
479870
+ });
479871
+ }, [setMessages, setResponseLength, setStreamMode, setStreamingToolUses, setStreamingThinking, onStreamingText, setAppState]);
479490
479872
  const onQueryImpl = import_react282.useCallback(async (messagesIncludingNewMessages, newMessages, abortController, shouldQuery, additionalAllowedTools, mainLoopModelParam, effort) => {
479491
479873
  if (shouldQuery) {
479492
479874
  const freshClients = mergeClients(initialMcpClients, store.getState().mcp.clients);
@@ -479602,8 +479984,30 @@ Error: sandbox required but unavailable: ${reason}
479602
479984
  toolUseContext,
479603
479985
  querySource: getQuerySourceForREPL()
479604
479986
  });
479987
+ const controller = abortController;
479988
+ const handleMcpToolBackgrounded = (info) => {
479989
+ if (controller.signal.aborted)
479990
+ return;
479991
+ let text;
479992
+ if (info.outcome.ok === true) {
479993
+ let serialized;
479994
+ try {
479995
+ serialized = typeof info.outcome.data === "string" ? info.outcome.data : JSON.stringify(info.outcome.data, null, 2);
479996
+ } catch {
479997
+ serialized = String(info.outcome.data);
479998
+ }
479999
+ if (serialized.length > 1e4)
480000
+ serialized = `${serialized.slice(0, 1e4)}
480001
+ …(结果过长已截断)`;
480002
+ text = `【后台 MCP 工具完成】${info.toolName}(后台运行)已完成,结果如下:
480003
+ ${serialized}`;
480004
+ } else {
480005
+ text = `【后台 MCP 工具失败】${info.toolName}(后台运行)失败:${info.outcome.error}`;
480006
+ }
480007
+ enqueuePendingNotification2({ value: text, mode: "task-notification" });
480008
+ };
479605
480009
  const loopGen = queryEngineLoopStream({
479606
- deps: loopDeps,
480010
+ deps: { ...loopDeps, onMcpToolBackgrounded: handleMcpToolBackgrounded },
479607
480011
  args: loopArgs
479608
480012
  });
479609
480013
  let loopResult;
@@ -488968,6 +489372,14 @@ To attach: ${source_default.bold(`tmux attach -t ${tmuxSessionName}`)}`));
488968
489372
  await getRecentActivity();
488969
489373
  }
488970
489374
  }
489375
+ const restrictedBypassErr = restrictedBypassError({
489376
+ permissionMode,
489377
+ allowDangerouslySkipPermissions
489378
+ });
489379
+ if (restrictedBypassErr) {
489380
+ console.error(`Error: ${restrictedBypassErr}`);
489381
+ process.exit(1);
489382
+ }
488971
489383
  if (permissionMode === "bypassPermissions" || allowDangerouslySkipPermissions) {
488972
489384
  if (process.platform !== "win32" && typeof process.getuid === "function" && process.getuid() === 0 && process.env.IS_SANDBOX !== "1" && !isEnvTruthy2(process.env.ATLAS_BUBBLEWRAP)) {
488973
489385
  console.error(`--dangerously-skip-permissions cannot be used with root/sudo privileges for security reasons`);
@@ -488986,6 +489398,7 @@ var init_setup4 = __esm(() => {
488986
489398
  init_Shell2();
488987
489399
  init_sinks();
488988
489400
  init_bootstrap();
489401
+ init_engine();
488989
489402
  init_commands5();
488990
489403
  init_sessionMemory2();
488991
489404
  init_ids();
@@ -492572,7 +492985,7 @@ async function run() {
492572
492985
  throw new Error("--task-budget must be a positive integer");
492573
492986
  }
492574
492987
  return tokens;
492575
- }).hideHelp()).option("--replay-user-messages", "Re-emit user messages from stdin back on stdout for acknowledgment (only works with --input-format=stream-json and --output-format=stream-json)", () => true).addOption(new Option("--enable-auth-status", "Enable auth status messages in SDK mode").default(false).hideHelp()).option("--allowedTools, --allowed-tools <tools...>", 'Comma or space-separated list of tool names to allow (e.g. "Bash(git:*) Edit")').option("--tools <tools...>", 'Specify the list of available tools from the built-in set. Use "" to disable all tools, "default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read").').option("--disallowedTools, --disallowed-tools <tools...>", 'Comma or space-separated list of tool names to deny (e.g. "Bash(git:*) Edit")').option("--mcp-config <configs...>", "Load MCP servers from JSON files or strings (space-separated)").addOption(new Option("--permission-prompt-tool <tool>", "MCP tool to use for permission prompts (only works with --print)").argParser(String).hideHelp()).addOption(new Option("--system-prompt <prompt>", "System prompt to use for the session").argParser(String)).addOption(new Option("--system-prompt-file <file>", "Read system prompt from a file").argParser(String).hideHelp()).addOption(new Option("--append-system-prompt <prompt>", "Append a system prompt to the default system prompt").argParser(String)).addOption(new Option("--append-system-prompt-file <file>", "Read system prompt from a file and append to the default system prompt").argParser(String).hideHelp()).addOption(new Option("--permission-mode <mode>", "Permission mode to use for the session").argParser(String).choices(PERMISSION_MODES2)).addOption(new Option("--permission-prompts <mode>", "Permission prompt policy; 'none' auto-denies prompts that would otherwise ask").argParser(String).choices(["none"])).option("-c, --continue", "Continue the most recent conversation in the current directory", () => true).option("-r, --resume [value]", "Resume a conversation by session ID, or open interactive picker with optional search term", (value) => value || true).option("--fork-session", "When resuming, create a new session ID instead of reusing the original (use with --resume or --continue)", () => true).addOption(new Option("--prefill <text>", "Pre-fill the prompt input with text without submitting it").hideHelp()).addOption(new Option("--deep-link-origin", "Signal that this session was launched from a deep link").hideHelp()).addOption(new Option("--deep-link-repo <slug>", "Repo slug the deep link ?repo= parameter resolved to the current cwd").hideHelp()).addOption(new Option("--deep-link-last-fetch <ms>", "FETCH_HEAD mtime in epoch ms, precomputed by the deep link trampoline").argParser((v) => {
492988
+ }).hideHelp()).option("--replay-user-messages", "Re-emit user messages from stdin back on stdout for acknowledgment (only works with --input-format=stream-json and --output-format=stream-json)", () => true).addOption(new Option("--enable-auth-status", "Enable auth status messages in SDK mode").default(false).hideHelp()).option("--allowedTools, --allowed-tools <tools...>", 'Comma or space-separated list of tool names to allow (e.g. "Bash(git:*) Edit")').option("--tools <tools...>", 'Specify the list of available tools from the built-in set. Use "" to disable all tools, "default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read").').option("--disallowedTools, --disallowed-tools <tools...>", 'Comma or space-separated list of tool names to deny (e.g. "Bash(git:*) Edit")').option("--mcp-config <configs...>", "Load MCP servers from JSON files or strings (space-separated)").addOption(new Option("--permission-prompt-tool <tool>", "MCP tool to use for permission prompts (only works with --print)").argParser(String).hideHelp()).addOption(new Option("--system-prompt <prompt>", "System prompt to use for the session").argParser(String)).addOption(new Option("--system-prompt-file <file>", "Read system prompt from a file").argParser(String).hideHelp()).addOption(new Option("--append-system-prompt <prompt>", "Append a system prompt to the default system prompt").argParser(String)).addOption(new Option("--append-system-prompt-file <file>", "Read system prompt from a file and append to the default system prompt").argParser(String).hideHelp()).addOption(new Option("--permission-mode <mode>", "Permission mode to use for the session").argParser(String).choices(PERMISSION_MODES2)).addOption(new Option("--permission-prompts <mode>", "Permission prompt policy; 'none' auto-denies prompts that would otherwise ask").argParser(String).choices(["none"])).option("--restricted", "Run in restricted mode: strips command-execution tools (Bash/PowerShell), WebFetch, and Agent; rejects bypassPermissions and --dangerously-skip-permissions; ignores user/project/local settings (env: ATLAS_RESTRICTED)", () => true).option("-c, --continue", "Continue the most recent conversation in the current directory", () => true).option("-r, --resume [value]", "Resume a conversation by session ID, or open interactive picker with optional search term", (value) => value || true).option("--fork-session", "When resuming, create a new session ID instead of reusing the original (use with --resume or --continue)", () => true).addOption(new Option("--prefill <text>", "Pre-fill the prompt input with text without submitting it").hideHelp()).addOption(new Option("--deep-link-origin", "Signal that this session was launched from a deep link").hideHelp()).addOption(new Option("--deep-link-repo <slug>", "Repo slug the deep link ?repo= parameter resolved to the current cwd").hideHelp()).addOption(new Option("--deep-link-last-fetch <ms>", "FETCH_HEAD mtime in epoch ms, precomputed by the deep link trampoline").argParser((v) => {
492576
492989
  const n = Number(v);
492577
492990
  return Number.isFinite(n) ? n : undefined;
492578
492991
  }).hideHelp()).option("--from-pr [value]", "Resume a session linked to a PR by PR number/URL, or open interactive picker with optional search term", (value) => value || true).option("--no-session-persistence", "Disable session persistence - sessions will not be saved to disk and cannot be resumed (only works with --print)").addOption(new Option("--resume-session-at <message id>", "When resuming, only messages up to and including the assistant message with <message.id> (use with --resume in print mode)").argParser(String).hideHelp()).addOption(new Option("--rewind-files <user-message-id>", "Restore files to state at the specified user message and exit (requires --resume)").hideHelp()).option("--model <model>", `Model for the current session. Provide a role alias (e.g. 'small' or 'premium') or a provider/model reference (e.g. 'bailian/qwen38-27b').`).addOption(new Option("--effort <level>", `Effort level for the current session (low, medium, high, xhigh, max)`).argParser((rawValue) => {
@@ -492612,6 +493025,18 @@ async function run() {
492612
493025
  includePartialMessages
492613
493026
  } = options;
492614
493027
  const permissionModeCli = permissionModeCliRaw ?? (permissionPrompts === "none" ? "dontAsk" : undefined);
493028
+ if (options.restricted === true) {
493029
+ setRestrictedMode(true);
493030
+ const restrictedBypassErr = restrictedBypassError({
493031
+ permissionMode: permissionModeCliRaw,
493032
+ allowDangerouslySkipPermissions,
493033
+ dangerouslySkipPermissions
493034
+ });
493035
+ if (restrictedBypassErr) {
493036
+ console.error(`Error: ${restrictedBypassErr}`);
493037
+ process.exit(1);
493038
+ }
493039
+ }
492615
493040
  if (options.prefill) {
492616
493041
  seedEarlyInput(options.prefill);
492617
493042
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlasharness/atlascode",
3
- "version": "0.1.78",
3
+ "version": "0.1.79",
4
4
  "description": "AtlasCode \u2014 Coding Agent for the domestic computing ecosystem (Ascend NPU + domestic LLM backends)",
5
5
  "bin": {
6
6
  "atlas": "dist/cli.js",