@atlasharness/atlascode 0.1.68 → 0.1.70

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 (3) hide show
  1. package/README.md +18 -3
  2. package/dist/cli.js +337 -124
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -5,8 +5,8 @@
5
5
  (OpenAI-protocol 静态键车道,任意 OpenAI-compatible 端点)与国产平台工具链预留
6
6
  (Ascend NPU 域包为可插拔叠加层,W6 起接入)。
7
7
 
8
- > 版本现状:G-α 前置阶段(v0.1.0 发布待 W2–W4 闭环 + P-1 远端仓库,见
9
- > `docs/roadmap-domestic-and-residuals.md`)。
8
+ > 版本现状:v0.1.x 持续发布(npm `@atlasharness/atlascode` live,P-1 远端仓已解除,
9
+ > 见 `docs/roadmap-domestic-and-residuals.md`)。
10
10
 
11
11
  ## 安装
12
12
 
@@ -20,7 +20,22 @@ npm install -g @atlasharness/atlascode
20
20
  atlascode --help # 别名 `atlas` 同效
21
21
  ```
22
22
 
23
- **② git 源码**(前置:`git` + [bun](https://bun.sh)(构建)+ `node`(运行))
23
+ **Windows(PowerShell / cmd)**
24
+
25
+ ```powershell
26
+ # npm 通道(与 Unix 同款,仅需 Node ≥ 20)
27
+ npm install -g @atlasharness/atlascode
28
+ atlascode --help # 别名 `atlas` 同效
29
+ ```
30
+
31
+ > - 装完 `atlascode`/`atlas` 不识别 → 重开终端(或把 npm 全局 bin 目录加入 `PATH`)。
32
+ > - git 源码通道 `./install.sh` 为 Unix shell 脚本,Windows 不可直接用;Windows 走 npm 通道,
33
+ > 或手动 `git clone <repo-url>` + [bun](https://bun.sh)(`bun install && bun run build`)。
34
+ > - 镜像 registry(如 npmmirror)超时/403 时切官方源:
35
+ > `npm install -g @atlasharness/atlascode --registry https://registry.npmjs.org`
36
+ > (Windows 多 OS 波 0.1.43 已验 npm 通道安装 + 启动链)。
37
+
38
+ **② git 源码**(Unix;前置:`git` + [bun](https://bun.sh)(构建)+ `node`(运行))
24
39
 
25
40
  ```bash
26
41
  ./install.sh [repo-url]
package/dist/cli.js CHANGED
@@ -19926,10 +19926,7 @@ function getRoleConfig(role) {
19926
19926
  const provider = normalizeProvider(envProvider) ?? normalizeProvider(roleSetting.provider) ?? "openai";
19927
19927
  const model = envModel ?? roleSetting.model;
19928
19928
  const baseURL = envBaseUrl ?? roleSetting.baseURL;
19929
- let apiKey = envApiKey ?? roleSetting.apiKey;
19930
- if (apiKey === undefined) {
19931
- apiKey = getEndpointConfigSource().getGlobalApiKey();
19932
- }
19929
+ const apiKey = envApiKey ?? roleSetting.apiKey;
19933
19930
  return { provider, model, baseURL, apiKey };
19934
19931
  }
19935
19932
  function getRoleModel(role) {
@@ -27063,6 +27060,9 @@ class OpenAIProvider {
27063
27060
  return this.timeoutResolver ? this.timeoutResolver() : this.timeoutMs;
27064
27061
  }
27065
27062
  async chat(args) {
27063
+ if (args.onStreamDelta) {
27064
+ return await this.chatWithDelta(args);
27065
+ }
27066
27066
  const refs = getRoleModels(args.role, args.sessionModel, args.fallbackModel);
27067
27067
  if (refs.length === 0) {
27068
27068
  throw new Error(`No models configured for role '${args.role}' (empty pool). ` + `Add modelRoles/providers to ~/.atlas/settings.json, or set ATLAS_${args.role.toUpperCase()}_MODEL, then restart.`);
@@ -27160,6 +27160,66 @@ class OpenAIProvider {
27160
27160
  throw lastErr;
27161
27161
  throw new Error(`No models configured for role '${args.role}'.`);
27162
27162
  }
27163
+ async chatWithDelta(args) {
27164
+ const refs = getRoleModels(args.role, args.sessionModel, args.fallbackModel);
27165
+ if (refs.length === 0) {
27166
+ throw new Error(`No models configured for role '${args.role}' (empty pool). ` + `Add modelRoles/providers to ~/.atlas/settings.json, or set ATLAS_${args.role.toUpperCase()}_MODEL, then restart.`);
27167
+ }
27168
+ const attemptRefs = refs;
27169
+ let lastErr = null;
27170
+ const onDelta = args.onStreamDelta;
27171
+ for (const ref of attemptRefs) {
27172
+ const entry = resolveModel(ref);
27173
+ if (!entry) {
27174
+ lastErr = new Error(`unknown provider/model reference: ${ref}`);
27175
+ continue;
27176
+ }
27177
+ const client = getClientForEntry(entry);
27178
+ let params;
27179
+ if (args.openaiParams) {
27180
+ params = { ...args.openaiParams, model: entry.modelId, stream: true };
27181
+ } else {
27182
+ params = {
27183
+ ...await buildOpenAIParams({
27184
+ messages: args.messages ?? [],
27185
+ systemPrompt: args.systemPrompt ?? asSystemPrompt([]),
27186
+ tools: args.tools,
27187
+ thinkingConfig: args.thinkingConfig,
27188
+ options: args.options
27189
+ }, args.role, entry),
27190
+ ...args.responseFormat ? { response_format: args.responseFormat } : {},
27191
+ stream: true
27192
+ };
27193
+ }
27194
+ for (let attempt = 0;attempt < this.maxRetriesPerModel; attempt++) {
27195
+ if (args.signal?.aborted)
27196
+ throw args.signal.reason ?? new Error("aborted");
27197
+ let assistant = null;
27198
+ try {
27199
+ for await (const ev of this.streamOneAttempt(client, params, entry, args.signal, attempt, this.maxRetriesPerModel)) {
27200
+ const t = ev.type;
27201
+ if (t === "text_delta" || t === "tool_use_start" || t === "tool_use_delta" || t === "tool_use_end") {
27202
+ onDelta(ev);
27203
+ } else if (t === "assistant") {
27204
+ assistant = ev;
27205
+ }
27206
+ }
27207
+ if (assistant)
27208
+ return assistant;
27209
+ } catch (err) {
27210
+ lastErr = err;
27211
+ if (!shouldRetryModelError(err, args.signal))
27212
+ break;
27213
+ if (attempt < this.maxRetriesPerModel - 1) {
27214
+ await this.retryBackoffSleep(modelRetryBackoffMs(attempt, this.retryBackoffBaseMs, this.retryBackoffCapMs), args.signal);
27215
+ }
27216
+ }
27217
+ }
27218
+ }
27219
+ if (lastErr)
27220
+ throw lastErr;
27221
+ throw new Error(`No models configured for role '${args.role}'.`);
27222
+ }
27163
27223
  async* chatStream(args) {
27164
27224
  const refs = getRoleModels(args.role, args.sessionModel);
27165
27225
  if (refs.length === 0) {
@@ -81394,7 +81454,17 @@ REMINDER: Do NOT call any tools. Respond with plain text only — ` + "an <analy
81394
81454
  });
81395
81455
 
81396
81456
  // src/engine/context/autoCompactWindow.ts
81397
- function resolveAutoCompactWindow(setting) {
81457
+ function isAutoCompactWindowSetting(x) {
81458
+ return typeof x === "object" && x !== null && "kind" in x;
81459
+ }
81460
+ function normalizeAutoCompactWindowSetting(raw) {
81461
+ if (!raw)
81462
+ return {};
81463
+ if (isAutoCompactWindowSetting(raw))
81464
+ return { default: raw };
81465
+ return raw;
81466
+ }
81467
+ function resolveAutoCompactWindowSingle(setting) {
81398
81468
  if (!setting || setting.kind === "auto") {
81399
81469
  return {};
81400
81470
  }
@@ -81412,6 +81482,11 @@ function resolveAutoCompactWindow(setting) {
81412
81482
  }
81413
81483
  return {};
81414
81484
  }
81485
+ function resolveAutoCompactWindow(setting, model) {
81486
+ const perModel = normalizeAutoCompactWindowSetting(setting);
81487
+ const chosen = model && perModel[model] || perModel.default;
81488
+ return resolveAutoCompactWindowSingle(chosen);
81489
+ }
81415
81490
  function parseAutoCompactTierInput(raw) {
81416
81491
  const s = raw.trim();
81417
81492
  if (s === "") {
@@ -81464,16 +81539,16 @@ function setAutoCompactSettingsSource(next) {
81464
81539
  function setAutoCompactWindowSettingsSource(next) {
81465
81540
  autoCompactWindowSettingsSource = next;
81466
81541
  }
81467
- function mergeAutoCompactOverrides(env, setting) {
81468
- const tier = resolveAutoCompactWindow(setting);
81542
+ function mergeAutoCompactOverrides(env, setting, model) {
81543
+ const tier = resolveAutoCompactWindow(setting, model);
81469
81544
  return {
81470
81545
  pctOverride: env.pctOverride ?? tier.pctOverride,
81471
81546
  windowOverride: env.windowOverride ?? tier.windowOverride,
81472
81547
  autoCompactDisabled: env.disabled === true || env.autoCompactDisabled === true || tier.autoCompactDisabled === true
81473
81548
  };
81474
81549
  }
81475
- function getMergedAutoCompactOverrides() {
81476
- return mergeAutoCompactOverrides(getAutoCompactEnvOverrides(), autoCompactWindowSettingsSource?.() ?? null);
81550
+ function getMergedAutoCompactOverrides(model) {
81551
+ return mergeAutoCompactOverrides(getAutoCompactEnvOverrides(), autoCompactWindowSettingsSource?.() ?? null, model);
81477
81552
  }
81478
81553
  function getEffectiveContextWindowSizeCore(contextWindow, maxOutputTokens, windowOverride) {
81479
81554
  const window2 = windowOverride !== undefined && windowOverride > 0 ? Math.min(contextWindow, windowOverride) : contextWindow;
@@ -81482,7 +81557,7 @@ function getEffectiveContextWindowSizeCore(contextWindow, maxOutputTokens, windo
81482
81557
  }
81483
81558
  function getEffectiveContextWindowSize(contextWindowOrModel, maxOutputTokens, windowOverride) {
81484
81559
  if (typeof contextWindowOrModel === "string") {
81485
- const { windowOverride } = getMergedAutoCompactOverrides();
81560
+ const { windowOverride } = getMergedAutoCompactOverrides(contextWindowOrModel);
81486
81561
  return getEffectiveContextWindowSizeCore(resolveModelContextWindow(contextWindowOrModel), undefined, windowOverride);
81487
81562
  }
81488
81563
  return getEffectiveContextWindowSizeCore(contextWindowOrModel, maxOutputTokens, windowOverride);
@@ -81497,7 +81572,7 @@ function getAutoCompactThresholdCore(contextWindow, maxOutputTokens, pctOverride
81497
81572
  }
81498
81573
  function getAutoCompactThreshold(contextWindowOrModel, maxOutputTokens, pctOverride, windowOverride) {
81499
81574
  if (typeof contextWindowOrModel === "string") {
81500
- const o = getMergedAutoCompactOverrides();
81575
+ const o = getMergedAutoCompactOverrides(contextWindowOrModel);
81501
81576
  return getAutoCompactThresholdCore(resolveModelContextWindow(contextWindowOrModel), undefined, o.pctOverride, o.windowOverride);
81502
81577
  }
81503
81578
  return getAutoCompactThresholdCore(contextWindowOrModel, maxOutputTokens, pctOverride, windowOverride);
@@ -81508,10 +81583,10 @@ function isAutoCompactThresholdPositive(contextWindow, maxOutputTokens, pctOverr
81508
81583
  function calculateTokenWarningState(tokenUsage, paramsOrModel) {
81509
81584
  if (typeof paramsOrModel === "string") {
81510
81585
  const env = getAutoCompactEnvOverrides();
81511
- const o = getMergedAutoCompactOverrides();
81586
+ const o = getMergedAutoCompactOverrides(paramsOrModel);
81512
81587
  return calculateTokenWarningStateCore(tokenUsage, {
81513
81588
  contextWindow: resolveModelContextWindow(paramsOrModel),
81514
- autoCompactEnabled: isAutoCompactEnabled(),
81589
+ autoCompactEnabled: isAutoCompactEnabledForModel(paramsOrModel),
81515
81590
  pctOverride: o.pctOverride,
81516
81591
  windowOverride: o.windowOverride,
81517
81592
  blockingLimitOverride: env.blockingLimitOverride
@@ -81570,6 +81645,17 @@ function isAutoCompactEnabled(flags) {
81570
81645
  return false;
81571
81646
  return flags.settingsEnabled !== false;
81572
81647
  }
81648
+ function isAutoCompactEnabledForModel(model) {
81649
+ const o = getAutoCompactEnvOverrides();
81650
+ if (o.disabled)
81651
+ return false;
81652
+ if (o.autoCompactDisabled)
81653
+ return false;
81654
+ if (getMergedAutoCompactOverrides(model).autoCompactDisabled) {
81655
+ return false;
81656
+ }
81657
+ return autoCompactSettingsSource ? autoCompactSettingsSource() ?? true : true;
81658
+ }
81573
81659
  async function shouldAutoCompact(messages, deps) {
81574
81660
  if (deps.querySource === "compact" || deps.querySource === "session_memory") {
81575
81661
  return false;
@@ -82163,7 +82249,8 @@ async function queryOneRound(deps, tools, messages) {
82163
82249
  thinkingConfig: deps.thinkingConfig,
82164
82250
  responseFormat: deps.responseFormat,
82165
82251
  fallbackModel: deps.fallbackModel,
82166
- options: deps.effortValue !== undefined ? { effortValue: deps.effortValue } : undefined
82252
+ options: deps.effortValue !== undefined ? { effortValue: deps.effortValue } : undefined,
82253
+ onStreamDelta: deps.emit ? (delta) => deps.emit({ type: "stream_delta", delta }) : undefined
82167
82254
  };
82168
82255
  let resp = await withTurnRecovery(() => deps.modelProvider.chat(chatArgs), {
82169
82256
  signal: deps.signal
@@ -123897,7 +123984,7 @@ var init_internalWrites = __esm(() => {
123897
123984
  timestamps = new Map;
123898
123985
  });
123899
123986
  // src/tui/utils/settings/types.ts
123900
- var isMcpServerCommandEntry = () => ({}), isMcpServerNameEntry = () => ({}), isMcpServerUrlEntry = () => ({}), SettingsSchema2 = () => object({
123987
+ var isMcpServerCommandEntry = () => ({}), isMcpServerNameEntry = () => ({}), isMcpServerUrlEntry = () => ({}), autoCompactWindowSettingSchema, SettingsSchema2 = () => object({
123901
123988
  model: string2().optional(),
123902
123989
  modelRoles: record(string2(), any()).optional(),
123903
123990
  defaultRole: string2().optional(),
@@ -123940,11 +124027,9 @@ var isMcpServerCommandEntry = () => ({}), isMcpServerNameEntry = () => ({}), isM
123940
124027
  language: string2().optional(),
123941
124028
  theme: string2().optional(),
123942
124029
  tui: object({ fullscreen: boolean2().optional() }).optional(),
123943
- autoCompactWindow: discriminatedUnion("kind", [
123944
- object({ kind: literal("auto") }),
123945
- object({ kind: literal("off") }),
123946
- object({ kind: literal("window"), tokens: number2().int().min(1000) }),
123947
- object({ kind: literal("pct"), pct: number2().min(1).max(100) })
124030
+ autoCompactWindow: union([
124031
+ autoCompactWindowSettingSchema,
124032
+ record(string2(), autoCompactWindowSettingSchema)
123948
124033
  ]).optional(),
123949
124034
  llmTimeoutMs: number2().int().positive().optional(),
123950
124035
  prefersReducedMotion: boolean2().optional(),
@@ -123983,6 +124068,12 @@ var isMcpServerCommandEntry = () => ({}), isMcpServerNameEntry = () => ({}), isM
123983
124068
  }).passthrough(), CUSTOMIZATION_SURFACES;
123984
124069
  var init_types7 = __esm(() => {
123985
124070
  init_zod();
124071
+ autoCompactWindowSettingSchema = discriminatedUnion("kind", [
124072
+ object({ kind: literal("auto") }),
124073
+ object({ kind: literal("off") }),
124074
+ object({ kind: literal("window"), tokens: number2().int().min(1000) }),
124075
+ object({ kind: literal("pct"), pct: number2().min(1).max(100) })
124076
+ ]);
123986
124077
  CUSTOMIZATION_SURFACES = ["mcp", "command", "agent"];
123987
124078
  });
123988
124079
 
@@ -180679,7 +180770,9 @@ __export(exports_engineCompat, {
180679
180770
  isAscendToolsEnabled: () => isAscendToolsEnabled,
180680
180771
  isAutoCompactCircuitTripped: () => isAutoCompactCircuitTripped,
180681
180772
  isAutoCompactEnabled: () => isAutoCompactEnabled,
180773
+ isAutoCompactEnabledForModel: () => isAutoCompactEnabledForModel,
180682
180774
  isAutoCompactThresholdPositive: () => isAutoCompactThresholdPositive,
180775
+ isAutoCompactWindowSetting: () => isAutoCompactWindowSetting,
180683
180776
  isAutoModeGateEnabled: () => isAutoModeGateEnabled,
180684
180777
  isBuiltInAgent: () => isBuiltInAgent,
180685
180778
  isBypassPermissionsModeDisabled: () => isBypassPermissionsModeDisabled,
@@ -180780,6 +180873,7 @@ __export(exports_engineCompat, {
180780
180873
  mergeWith: () => mergeWith,
180781
180874
  microcompactMessages: () => microcompactMessages,
180782
180875
  nextCronRunMs: () => nextCronRunMs,
180876
+ normalizeAutoCompactWindowSetting: () => normalizeAutoCompactWindowSetting,
180783
180877
  normalizeControlMessageKeys: () => normalizeControlMessageKeys,
180784
180878
  normalizeNameForMCP: () => normalizeNameForMCP2,
180785
180879
  notifyTasksUpdated: () => notifyTasksUpdated,
@@ -256525,6 +256619,8 @@ async function* queryEngineLoopStream(params) {
256525
256619
  pending.push(event.assistantMessage, ...event.toolResultMessages);
256526
256620
  } else if (event.type === "compacted") {
256527
256621
  pending.push(event.messages[0]);
256622
+ } else if (event.type === "stream_delta") {
256623
+ pending.push(event);
256528
256624
  }
256529
256625
  notify();
256530
256626
  }
@@ -321290,7 +321386,7 @@ function buildAgentLoopParams(m) {
321290
321386
  const resolved = pool.length > 0 ? resolveModel(pool[0]) : undefined;
321291
321387
  const toolList = m.tools;
321292
321388
  const countTokens = (msgs) => modelProvider.countTokens(role, sessionModel, msgs, toolList);
321293
- const mergedAutoCompact = mergeAutoCompactOverrides(getAutoCompactEnvOverrides(), getInitialSettings2().autoCompactWindow);
321389
+ const mergedAutoCompact = mergeAutoCompactOverrides(getAutoCompactEnvOverrides(), getInitialSettings2().autoCompactWindow, mainLoopModel);
321294
321390
  const autoCompact = {
321295
321391
  contextWindow: resolved?.contextWindow ?? HARD_DEFAULT_CONTEXT_WINDOW,
321296
321392
  maxOutputTokens: resolved?.maxTokens,
@@ -325748,7 +325844,7 @@ async function analyzeContextUsage(messages, model, getToolPermissionContext, to
325748
325844
  const skillInfo = skillResult.skillInfo;
325749
325845
  const skillFrontmatterTokens = skillInfo.skillFrontmatter.reduce((sum, skill) => sum + skill.tokens, 0);
325750
325846
  const messageTokens = messageBreakdown.totalTokens;
325751
- const isAutoCompact = isAutoCompactEnabled();
325847
+ const isAutoCompact = isAutoCompactEnabledForModel(model);
325752
325848
  const autoCompactThreshold = isAutoCompact ? getEffectiveContextWindowSize(model) - AUTOCOMPACT_BUFFER_TOKENS : undefined;
325753
325849
  const cats = [];
325754
325850
  if (systemPromptTokens > 0) {
@@ -329411,7 +329507,7 @@ function getCompactionReminderAttachment(messages, model) {
329411
329507
  if (!getFeatureValue_CACHED_MAY_BE_STALE("atlas_marble_fox", false)) {
329412
329508
  return [];
329413
329509
  }
329414
- if (!isAutoCompactEnabled()) {
329510
+ if (!isAutoCompactEnabledForModel(model)) {
329415
329511
  return [];
329416
329512
  }
329417
329513
  const contextWindow = getContextWindowForModel(model, getSdkBetas());
@@ -336878,7 +336974,7 @@ function getContentText(content) {
336878
336974
  return null;
336879
336975
  }
336880
336976
  function handleMessageFromStream(message, onMessage, onUpdateLength, onSetStreamMode, onStreamingToolUses, onTombstone, onStreamingThinking, onApiMetrics, onStreamingText) {
336881
- if (message.type !== "stream_event" && message.type !== "stream_request_start") {
336977
+ if (message.type !== "stream_event" && message.type !== "stream_request_start" && message.type !== "stream_delta") {
336882
336978
  if (message.type === "tombstone") {
336883
336979
  onTombstone?.(message.message);
336884
336980
  return;
@@ -336904,6 +337000,14 @@ function handleMessageFromStream(message, onMessage, onUpdateLength, onSetStream
336904
337000
  onSetStreamMode("requesting");
336905
337001
  return;
336906
337002
  }
337003
+ if (message.type === "stream_delta") {
337004
+ if (message.delta.type === "text_delta") {
337005
+ onSetStreamMode("responding");
337006
+ onUpdateLength(message.delta.text);
337007
+ onStreamingText?.((text) => (text ?? "") + message.delta.text);
337008
+ }
337009
+ return;
337010
+ }
336907
337011
  if (message.event.type === "message_start") {
336908
337012
  if (message.ttftMs != null) {
336909
337013
  onApiMetrics?.({ ttftMs: message.ttftMs });
@@ -337311,6 +337415,13 @@ ${attachment.snippet}`,
337311
337415
  isMeta: true
337312
337416
  })
337313
337417
  ]);
337418
+ case "git_status_refresh":
337419
+ return wrapMessagesInSystemReminder([
337420
+ createUserMessage2({
337421
+ content: attachment.status,
337422
+ isMeta: true
337423
+ })
337424
+ ]);
337314
337425
  case "file": {
337315
337426
  const fileContent = attachment.content;
337316
337427
  switch (fileContent.type) {
@@ -349063,6 +349174,10 @@ var exports_autocompact = {};
349063
349174
  __export(exports_autocompact, {
349064
349175
  call: () => call9
349065
349176
  });
349177
+ function currentTierFor(raw, model) {
349178
+ const map = normalizeAutoCompactWindowSetting(raw);
349179
+ return map[model] ?? map.default;
349180
+ }
349066
349181
  function currentToOptionValue(setting) {
349067
349182
  if (!setting || setting.kind === "auto")
349068
349183
  return "auto";
@@ -349079,21 +349194,25 @@ function customInitialValue(setting) {
349079
349194
  }
349080
349195
  return setting.kind === "window" ? `${setting.tokens / 1000}k` : `${setting.pct}%`;
349081
349196
  }
349082
- function describeTier(setting) {
349197
+ function describeTier(setting, model) {
349198
+ const label = ` [${renderModelName(model)}]`;
349083
349199
  switch (setting.kind) {
349084
349200
  case "auto":
349085
- return "Auto-compact: Auto (default) — compact near the model window limit";
349201
+ return `Auto-compact: Auto (default)${label} — compact near the model window limit`;
349086
349202
  case "off":
349087
- return "Auto-compact: Off (manual /compact still works)";
349203
+ return `Auto-compact: Off${label} (manual /compact still works)`;
349088
349204
  case "window":
349089
- return `Auto-compact window: ${setting.tokens / 1000}k tokens`;
349205
+ return `Auto-compact window: ${setting.tokens / 1000}k tokens${label}`;
349090
349206
  case "pct":
349091
- return `Auto-compact threshold: ${setting.pct}% of the effective window`;
349207
+ return `Auto-compact threshold: ${setting.pct}% of the effective window${label}`;
349092
349208
  }
349093
349209
  }
349094
- function persistTier(setting, setAppState, onDone) {
349210
+ function persistTier(setting, model, setAppState, onDone) {
349211
+ const map = normalizeAutoCompactWindowSetting(getInitialSettings2().autoCompactWindow);
349212
+ map[model] = setting;
349213
+ const mapForSettings = map;
349095
349214
  const { error } = updateSettingsForSource2("userSettings", {
349096
- autoCompactWindow: setting
349215
+ autoCompactWindow: mapForSettings
349097
349216
  });
349098
349217
  if (error) {
349099
349218
  onDone(`Failed to persist auto-compact tier: ${error.message}`, {
@@ -349103,9 +349222,9 @@ function persistTier(setting, setAppState, onDone) {
349103
349222
  }
349104
349223
  setAppState((prev) => ({
349105
349224
  ...prev,
349106
- settings: { ...prev.settings, autoCompactWindow: setting }
349225
+ settings: { ...prev.settings, autoCompactWindow: mapForSettings }
349107
349226
  }));
349108
- onDone(describeTier(setting));
349227
+ onDone(describeTier(setting, model));
349109
349228
  }
349110
349229
  function AutoCompactApply({
349111
349230
  setting,
@@ -349113,34 +349232,23 @@ function AutoCompactApply({
349113
349232
  }) {
349114
349233
  const setAppState = useSetAppState();
349115
349234
  const doneRef = React41.useRef(false);
349235
+ const model = getMainLoopModel();
349116
349236
  React41.useEffect(() => {
349117
349237
  if (doneRef.current) {
349118
349238
  return;
349119
349239
  }
349120
349240
  doneRef.current = true;
349121
- persistTier(setting, setAppState, onDone);
349122
- }, [setting, onDone, setAppState]);
349241
+ persistTier(setting, model, setAppState, onDone);
349242
+ }, [setting, model, onDone, setAppState]);
349123
349243
  return null;
349124
349244
  }
349125
349245
  function AutoCompactCommand({ onDone }) {
349126
349246
  const setAppState = useSetAppState();
349247
+ const model = getMainLoopModel();
349127
349248
  const current = useAppState((s) => s.settings.autoCompactWindow);
349128
349249
  const [inputError, setInputError] = React41.useState(null);
349129
349250
  const apply = (setting) => {
349130
- const { error } = updateSettingsForSource2("userSettings", {
349131
- autoCompactWindow: setting
349132
- });
349133
- if (error) {
349134
- onDone(`Failed to persist auto-compact tier: ${error.message}`, {
349135
- display: "system"
349136
- });
349137
- return;
349138
- }
349139
- setAppState((prev) => ({
349140
- ...prev,
349141
- settings: { ...prev.settings, autoCompactWindow: setting }
349142
- }));
349143
- onDone(describeTier(setting));
349251
+ persistTier(setting, model, setAppState, onDone);
349144
349252
  };
349145
349253
  const options = [
349146
349254
  {
@@ -349158,7 +349266,7 @@ function AutoCompactCommand({ onDone }) {
349158
349266
  value: "custom",
349159
349267
  type: "input",
349160
349268
  placeholder: "e.g. 150k (window) or 75% (bare number = percent)",
349161
- initialValue: customInitialValue(current),
349269
+ initialValue: customInitialValue(currentTierFor(current, model)),
349162
349270
  onChange: (raw) => {
349163
349271
  const parsed = parseAutoCompactTierInput(raw);
349164
349272
  if (!parsed) {
@@ -349180,10 +349288,10 @@ function AutoCompactCommand({ onDone }) {
349180
349288
  children: [
349181
349289
  /* @__PURE__ */ jsx_dev_runtime140.jsxDEV(ThemedText, {
349182
349290
  bold: true,
349183
- children: "Auto-compact window tier"
349291
+ children: `Auto-compact window tier — ${renderModelName(model)}`
349184
349292
  }, undefined, false, undefined, this),
349185
349293
  /* @__PURE__ */ jsx_dev_runtime140.jsxDEV(Select, {
349186
- defaultValue: currentToOptionValue(current),
349294
+ defaultValue: currentToOptionValue(currentTierFor(current, model)),
349187
349295
  options,
349188
349296
  onChange: (value) => {
349189
349297
  if (value === "auto") {
@@ -349227,6 +349335,7 @@ var init_autocompact = __esm(() => {
349227
349335
  init_CustomSelect();
349228
349336
  init_ink2();
349229
349337
  init_AppState();
349338
+ init_model();
349230
349339
  init_settings3();
349231
349340
  init_engine();
349232
349341
  React41 = __toESM(require_react(), 1);
@@ -374960,6 +375069,7 @@ var init_nullRenderingAttachments = __esm(() => {
374960
375069
  "critical_system_reminder",
374961
375070
  "edited_image_file",
374962
375071
  "edited_text_file",
375072
+ "git_status_refresh",
374963
375073
  "opened_file_in_ide",
374964
375074
  "output_style",
374965
375075
  "plan_mode",
@@ -432146,7 +432256,8 @@ async function streamCompactSummary({
432146
432256
  appState,
432147
432257
  context,
432148
432258
  preCompactTokenCount,
432149
- cacheSafeParams
432259
+ cacheSafeParams,
432260
+ modelOverride
432150
432261
  }) {
432151
432262
  const promptCacheSharingEnabled = getFeatureValue_CACHED_MAY_BE_STALE("atlas_compact_cache_prefix", true);
432152
432263
  const activityInterval = isSessionActivityTrackingActive() ? setInterval((statusSetter) => {
@@ -432154,7 +432265,7 @@ async function streamCompactSummary({
432154
432265
  statusSetter?.("compacting");
432155
432266
  }, 30000, context.setSDKStatus) : undefined;
432156
432267
  try {
432157
- if (promptCacheSharingEnabled) {
432268
+ if (promptCacheSharingEnabled && !modelOverride) {
432158
432269
  try {
432159
432270
  const result = await runForkedAgent({
432160
432271
  promptMessages: [summaryRequest],
@@ -432179,11 +432290,12 @@ async function streamCompactSummary({
432179
432290
  }
432180
432291
  const retryEnabled = getFeatureValue_CACHED_MAY_BE_STALE("atlas_compact_streaming_retry", false);
432181
432292
  const maxAttempts = retryEnabled ? MAX_COMPACT_STREAMING_RETRIES : 1;
432293
+ const compactModel = modelOverride ?? context.options.mainLoopModel;
432182
432294
  for (let attempt = 1;attempt <= maxAttempts; attempt++) {
432183
432295
  let hasStartedStreaming = false;
432184
432296
  let response;
432185
432297
  context.setResponseLength?.(() => 0);
432186
- const useToolSearch = await isToolSearchEnabled(context.options.mainLoopModel, context.options.tools, async () => appState.toolPermissionContext, context.options.agentDefinitions.activeAgents, "compact");
432298
+ const useToolSearch = await isToolSearchEnabled(compactModel, context.options.tools, async () => appState.toolPermissionContext, context.options.agentDefinitions.activeAgents, "compact");
432187
432299
  const tools = useToolSearch ? uniqBy_default([
432188
432300
  FileReadTool,
432189
432301
  ToolSearchTool2,
@@ -432193,7 +432305,6 @@ async function streamCompactSummary({
432193
432305
  ...getMessagesAfterCompactBoundary(messages),
432194
432306
  summaryRequest
432195
432307
  ])), context.options.tools);
432196
- const compactModel = context.options.mainLoopModel;
432197
432308
  const compactRole = modelToRole(compactModel);
432198
432309
  const params = await buildOpenAIParams({
432199
432310
  messages: compactMessages,
@@ -432283,6 +432394,23 @@ async function createPostCompactFileAttachments(readFileState, toolUseContext, m
432283
432394
  return false;
432284
432395
  });
432285
432396
  }
432397
+ async function createCurrentGitStatusAttachment() {
432398
+ const baseline = getSessionStartGitStatusBlocks();
432399
+ if (!baseline) {
432400
+ return null;
432401
+ }
432402
+ const fresh = await getGitStatusFresh();
432403
+ if (!fresh) {
432404
+ return null;
432405
+ }
432406
+ if (JSON.stringify(fresh.blocks) === JSON.stringify(baseline)) {
432407
+ return null;
432408
+ }
432409
+ return createAttachmentMessage({
432410
+ type: "git_status_refresh",
432411
+ status: fresh.text
432412
+ });
432413
+ }
432286
432414
  function createPlanAttachmentIfNeeded(agentId) {
432287
432415
  const planContent = getPlan2(agentId);
432288
432416
  if (!planContent) {
@@ -432423,6 +432551,7 @@ var init_compact4 = __esm(() => {
432423
432551
  init_attachments();
432424
432552
  init_config6();
432425
432553
  init_context2();
432554
+ init_context5();
432426
432555
  init_debug();
432427
432556
  init_errors4();
432428
432557
  init_fileStateCache();
@@ -432453,6 +432582,62 @@ var init_compact4 = __esm(() => {
432453
432582
  init_prompt22();
432454
432583
  });
432455
432584
 
432585
+ // src/tui/contextBodies/summarizeFallback.ts
432586
+ function isFallbackEligibleError(err) {
432587
+ let msg = "";
432588
+ if (err instanceof Error) {
432589
+ if (err.name === "APIUserAbortError")
432590
+ return false;
432591
+ msg = err.message;
432592
+ } else if (typeof err === "string") {
432593
+ msg = err;
432594
+ } else {
432595
+ return false;
432596
+ }
432597
+ if (msg.includes(PROMPT_TOO_LONG_ERROR_MESSAGE))
432598
+ return false;
432599
+ if (msg.includes("Request was aborted."))
432600
+ return false;
432601
+ return true;
432602
+ }
432603
+ function isFallbackEligibleResult(msg, text) {
432604
+ if (!text)
432605
+ return false;
432606
+ if (text.includes(PROMPT_TOO_LONG_ERROR_MESSAGE))
432607
+ return false;
432608
+ if (text.includes("Request was aborted."))
432609
+ return false;
432610
+ const flagged = typeof msg === "object" && msg !== null && msg.isApiErrorMessage === true;
432611
+ return flagged || startsWithApiErrorPrefix(text);
432612
+ }
432613
+ function createSummarizeWithFallback(primary, opts) {
432614
+ return async (p) => {
432615
+ let result;
432616
+ try {
432617
+ result = await primary(p);
432618
+ } catch (err) {
432619
+ const fb = opts.resolveFallbackModel(p);
432620
+ if (!fb || !isFallbackEligibleError(err)) {
432621
+ throw err;
432622
+ }
432623
+ return primary({ ...p, modelOverride: fb });
432624
+ }
432625
+ if (opts.isEligibleResult?.(result)) {
432626
+ const fb = opts.resolveFallbackModel(p);
432627
+ if (fb) {
432628
+ const retried = await primary({ ...p, modelOverride: fb });
432629
+ if (!opts.isEligibleResult(retried)) {
432630
+ return retried;
432631
+ }
432632
+ }
432633
+ }
432634
+ return result;
432635
+ };
432636
+ }
432637
+ var init_summarizeFallback = __esm(() => {
432638
+ init_modelprovider2();
432639
+ });
432640
+
432456
432641
  // src/tui/services/SessionMemory/prompts.ts
432457
432642
  import { readFile as readFile61 } from "fs/promises";
432458
432643
  import { join as join165 } from "path";
@@ -433012,6 +433197,9 @@ async function composePostCompactAttachments(preCompactReadFileState, context) {
433012
433197
  createAsyncAgentAttachmentsIfNeeded(ctx)
433013
433198
  ]);
433014
433199
  const out = [...fileAttachments, ...asyncAgentAttachments];
433200
+ const gitStatusAttachment = await createCurrentGitStatusAttachment();
433201
+ if (gitStatusAttachment)
433202
+ out.push(gitStatusAttachment);
433015
433203
  const planAttachment = createPlanAttachmentIfNeeded(ctx.agentId);
433016
433204
  if (planAttachment)
433017
433205
  out.push(planAttachment);
@@ -433033,8 +433221,20 @@ async function composePostCompactAttachments(preCompactReadFileState, context) {
433033
433221
  return out;
433034
433222
  }
433035
433223
  function wireContextHostPorts() {
433224
+ const summarizePrimary = (p) => streamCompactSummary(p);
433225
+ const summarizeWithFallback = createSummarizeWithFallback(summarizePrimary, {
433226
+ resolveFallbackModel: (p) => {
433227
+ const fb = getRoleModel("small");
433228
+ if (!fb)
433229
+ return;
433230
+ if (p.context.options.mainLoopModel === fb)
433231
+ return;
433232
+ return fb;
433233
+ },
433234
+ isEligibleResult: (r) => isFallbackEligibleResult(r, getAssistantMessageText2(r))
433235
+ });
433036
433236
  setCompactPorts({
433037
- summarize: (p) => streamCompactSummary({
433237
+ summarize: (p) => summarizeWithFallback({
433038
433238
  messages: p.messages,
433039
433239
  summaryRequest: p.summaryRequest,
433040
433240
  appState: p.context.getAppState(),
@@ -433110,7 +433310,9 @@ var init_contextHostWiring = __esm(() => {
433110
433310
  init_sessionStorage();
433111
433311
  init_betaSessionTracing();
433112
433312
  init_compact4();
433313
+ init_messages3();
433113
433314
  init_compact4();
433315
+ init_summarizeFallback();
433114
433316
  init_sessionMemoryCompact();
433115
433317
  init_compact4();
433116
433318
  });
@@ -433216,17 +433418,12 @@ function createSettingsAdapter() {
433216
433418
  return settings.providers || {};
433217
433419
  },
433218
433420
  getGlobalApiKey() {
433219
- try {
433220
- return getApiKeyFromConfigOrMacOSKeychain()?.key;
433221
- } catch {
433222
- return;
433223
- }
433421
+ return;
433224
433422
  }
433225
433423
  };
433226
433424
  }
433227
433425
  var init_settings_adapter = __esm(() => {
433228
433426
  init_settings3();
433229
- init_auth4();
433230
433427
  });
433231
433428
 
433232
433429
  // src/tui/factory.ts
@@ -434287,7 +434484,77 @@ function setSystemPromptInjection(value) {
434287
434484
  getUserContext.cache.clear?.();
434288
434485
  getSystemContext.cache.clear?.();
434289
434486
  }
434290
- var MAX_STATUS_CHARS = 2000, systemPromptInjection = null, getGitStatus, getSystemContext, getUserContext;
434487
+ async function fetchGitStatusBlocks() {
434488
+ if (false) {}
434489
+ const startTime = Date.now();
434490
+ logForDiagnosticsNoPII("info", "git_status_started");
434491
+ const isGitStart = Date.now();
434492
+ const isGit = await getIsGit();
434493
+ logForDiagnosticsNoPII("info", "git_is_git_check_completed", {
434494
+ duration_ms: Date.now() - isGitStart,
434495
+ is_git: isGit
434496
+ });
434497
+ if (!isGit) {
434498
+ logForDiagnosticsNoPII("info", "git_status_skipped_not_git", {
434499
+ duration_ms: Date.now() - startTime
434500
+ });
434501
+ return null;
434502
+ }
434503
+ try {
434504
+ const gitCmdsStart = Date.now();
434505
+ const [branch, mainBranch, status, log, userName] = await Promise.all([
434506
+ getBranch2(),
434507
+ getDefaultBranch2(),
434508
+ execFileNoThrow3(gitExe2(), ["--no-optional-locks", "status", "--short"], {
434509
+ preserveOutputOnError: false
434510
+ }).then(({ stdout }) => stdout.trim()),
434511
+ execFileNoThrow3(gitExe2(), ["--no-optional-locks", "log", "--oneline", "-n", "5"], {
434512
+ preserveOutputOnError: false
434513
+ }).then(({ stdout }) => stdout.trim()),
434514
+ execFileNoThrow3(gitExe2(), ["config", "user.name"], {
434515
+ preserveOutputOnError: false
434516
+ }).then(({ stdout }) => stdout.trim())
434517
+ ]);
434518
+ logForDiagnosticsNoPII("info", "git_commands_completed", {
434519
+ duration_ms: Date.now() - gitCmdsStart,
434520
+ status_length: status.length
434521
+ });
434522
+ const truncatedStatus = status.length > MAX_STATUS_CHARS ? status.substring(0, MAX_STATUS_CHARS) + `
434523
+ ... (truncated because it exceeds 2k characters. If you need more information, run "git status" using BashTool)` : status;
434524
+ logForDiagnosticsNoPII("info", "git_status_completed", {
434525
+ duration_ms: Date.now() - startTime,
434526
+ truncated: status.length > MAX_STATUS_CHARS
434527
+ });
434528
+ return { branch, mainBranch, status: truncatedStatus, log, userName };
434529
+ } catch (error) {
434530
+ logForDiagnosticsNoPII("error", "git_status_failed", {
434531
+ duration_ms: Date.now() - startTime
434532
+ });
434533
+ logError3(error);
434534
+ return null;
434535
+ }
434536
+ }
434537
+ function formatGitStatus(blocks, header) {
434538
+ return [
434539
+ header,
434540
+ `Current branch: ${blocks.branch}`,
434541
+ `Main branch (you will usually use this for PRs): ${blocks.mainBranch}`,
434542
+ ...blocks.userName ? [`Git user: ${blocks.userName}`] : [],
434543
+ `Status:
434544
+ ${blocks.status || "(clean)"}`,
434545
+ `Recent commits:
434546
+ ${blocks.log}`
434547
+ ].join(`
434548
+
434549
+ `);
434550
+ }
434551
+ function getGitStatusFresh() {
434552
+ return fetchGitStatusBlocks().then((blocks) => blocks ? { text: formatGitStatus(blocks, GIT_STATUS_FRESH_HEADER), blocks } : null);
434553
+ }
434554
+ function getSessionStartGitStatusBlocks() {
434555
+ return sessionStartGitStatusBlocks;
434556
+ }
434557
+ var MAX_STATUS_CHARS = 2000, systemPromptInjection = null, GIT_STATUS_START_HEADER = "This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.", GIT_STATUS_FRESH_HEADER = "This is the current git status, re-injected after context compaction. It reflects the working tree as of now and supersedes any earlier session-start snapshot.", sessionStartGitStatusBlocks = null, getGitStatus, getSystemContext, getUserContext;
434291
434558
  var init_context5 = __esm(() => {
434292
434559
  init_shared();
434293
434560
  init_memoize();
@@ -434301,65 +434568,11 @@ var init_context5 = __esm(() => {
434301
434568
  init_gitSettings();
434302
434569
  init_log5();
434303
434570
  getGitStatus = memoize_default(async () => {
434304
- if (false) {}
434305
- const startTime = Date.now();
434306
- logForDiagnosticsNoPII("info", "git_status_started");
434307
- const isGitStart = Date.now();
434308
- const isGit = await getIsGit();
434309
- logForDiagnosticsNoPII("info", "git_is_git_check_completed", {
434310
- duration_ms: Date.now() - isGitStart,
434311
- is_git: isGit
434312
- });
434313
- if (!isGit) {
434314
- logForDiagnosticsNoPII("info", "git_status_skipped_not_git", {
434315
- duration_ms: Date.now() - startTime
434316
- });
434317
- return null;
434318
- }
434319
- try {
434320
- const gitCmdsStart = Date.now();
434321
- const [branch, mainBranch, status, log, userName] = await Promise.all([
434322
- getBranch2(),
434323
- getDefaultBranch2(),
434324
- execFileNoThrow3(gitExe2(), ["--no-optional-locks", "status", "--short"], {
434325
- preserveOutputOnError: false
434326
- }).then(({ stdout }) => stdout.trim()),
434327
- execFileNoThrow3(gitExe2(), ["--no-optional-locks", "log", "--oneline", "-n", "5"], {
434328
- preserveOutputOnError: false
434329
- }).then(({ stdout }) => stdout.trim()),
434330
- execFileNoThrow3(gitExe2(), ["config", "user.name"], {
434331
- preserveOutputOnError: false
434332
- }).then(({ stdout }) => stdout.trim())
434333
- ]);
434334
- logForDiagnosticsNoPII("info", "git_commands_completed", {
434335
- duration_ms: Date.now() - gitCmdsStart,
434336
- status_length: status.length
434337
- });
434338
- const truncatedStatus = status.length > MAX_STATUS_CHARS ? status.substring(0, MAX_STATUS_CHARS) + `
434339
- ... (truncated because it exceeds 2k characters. If you need more information, run "git status" using BashTool)` : status;
434340
- logForDiagnosticsNoPII("info", "git_status_completed", {
434341
- duration_ms: Date.now() - startTime,
434342
- truncated: status.length > MAX_STATUS_CHARS
434343
- });
434344
- return [
434345
- `This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.`,
434346
- `Current branch: ${branch}`,
434347
- `Main branch (you will usually use this for PRs): ${mainBranch}`,
434348
- ...userName ? [`Git user: ${userName}`] : [],
434349
- `Status:
434350
- ${truncatedStatus || "(clean)"}`,
434351
- `Recent commits:
434352
- ${log}`
434353
- ].join(`
434354
-
434355
- `);
434356
- } catch (error) {
434357
- logForDiagnosticsNoPII("error", "git_status_failed", {
434358
- duration_ms: Date.now() - startTime
434359
- });
434360
- logError3(error);
434571
+ const blocks = await fetchGitStatusBlocks();
434572
+ if (!blocks)
434361
434573
  return null;
434362
- }
434574
+ sessionStartGitStatusBlocks = blocks;
434575
+ return formatGitStatus(blocks, GIT_STATUS_START_HEADER);
434363
434576
  });
434364
434577
  getSystemContext = memoize_default(async () => {
434365
434578
  const startTime = Date.now();
@@ -451387,7 +451600,7 @@ function TokenWarning(t0) {
451387
451600
  }
451388
451601
  let t2;
451389
451602
  if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
451390
- t2 = isAutoCompactEnabled();
451603
+ t2 = isAutoCompactEnabledForModel(model);
451391
451604
  $[3] = t2;
451392
451605
  } else {
451393
451606
  t2 = $[3];
@@ -459943,7 +460156,7 @@ var jsx_dev_runtime381, ContextBarSegment = ({ input }) => {
459943
460156
  }
459944
460157
  const color = contextColorForPercentage(pct);
459945
460158
  const modelId = input.model?.id;
459946
- const showCompactWarning = modelId != null && isAutoCompactEnabled() && calculateTokenWarningState(used, modelId).isAboveWarningThreshold;
460159
+ const showCompactWarning = modelId != null && isAutoCompactEnabledForModel(modelId) && calculateTokenWarningState(used, modelId).isAboveWarningThreshold;
459947
460160
  return /* @__PURE__ */ jsx_dev_runtime381.jsxDEV(ThemedText, {
459948
460161
  color,
459949
460162
  children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlasharness/atlascode",
3
- "version": "0.1.68",
3
+ "version": "0.1.70",
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",