@cjhyy/code-shell 0.1.0-alpha.3 → 0.1.6

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 (157) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/anthropic-IBTI4WL2.cjs +10 -0
  3. package/dist/anthropic-PRZZKHEZ.js +10 -0
  4. package/dist/arena/index.cjs +7 -6
  5. package/dist/arena/index.js +6 -5
  6. package/dist/arena/model-presets.d.ts +10 -1
  7. package/dist/{arena-ENHBHB2W.js → arena-2LTLIXOL.js} +9 -7
  8. package/dist/{arena-HHDYYVHJ.cjs → arena-ADB7DRFI.cjs} +25 -23
  9. package/dist/{chunk-EL2RL5DG.cjs → chunk-2YW4FPFG.cjs} +2 -2
  10. package/dist/chunk-3OA5G4TM.js +47 -0
  11. package/dist/{chunk-FPFTIKEJ.js → chunk-3QO25N6E.js} +131 -173
  12. package/dist/chunk-3SBSUOWZ.js +447 -0
  13. package/dist/chunk-52LGZKT3.cjs +47 -0
  14. package/dist/{chunk-KOGV6PDQ.cjs → chunk-55XGPMAP.cjs} +44 -9
  15. package/dist/{chunk-YD7XTI7T.cjs → chunk-6QVAYW7C.cjs} +116 -28
  16. package/dist/{chunk-SCDKLTN3.js → chunk-6S4BJ65N.js} +5 -4
  17. package/dist/{chunk-ZLPJJZUW.js → chunk-76HYEPDM.js} +2 -2
  18. package/dist/chunk-7BI3V74Y.cjs +447 -0
  19. package/dist/{chunk-GZBD6QRS.cjs → chunk-7EG3V4JY.cjs} +13 -12
  20. package/dist/{chunk-LMEKCQX4.js → chunk-7JVTYEEC.js} +6 -6
  21. package/dist/{chunk-SW3L7HIM.js → chunk-7QIM3BKD.js} +2 -2
  22. package/dist/{chunk-5C5HIOML.js → chunk-7Y6S3OCX.js} +109 -21
  23. package/dist/{chunk-REFCCVDM.js → chunk-ABRRONJ7.js} +2 -2
  24. package/dist/{chunk-F5BS3GUC.js → chunk-BSW22MPA.js} +39 -4
  25. package/dist/{chunk-ZRVPCXI7.js → chunk-BXCY2FSE.js} +2 -1
  26. package/dist/{chunk-Y7TSCDF6.js → chunk-BZ5TNE7L.js} +347 -6
  27. package/dist/{chunk-X2DNIMPC.cjs → chunk-DDB6QOKJ.cjs} +32 -32
  28. package/dist/{chunk-C7C7ZZY4.cjs → chunk-HEKQCI7B.cjs} +10 -10
  29. package/dist/{chunk-JHNCK7JP.cjs → chunk-IK4EWJBP.cjs} +4 -3
  30. package/dist/{chunk-3CAZIWUA.cjs → chunk-ILHE7XLT.cjs} +9 -9
  31. package/dist/{chunk-M2QI5X3X.cjs → chunk-JMR33QVF.cjs} +2 -2
  32. package/dist/{chunk-TDXBHD24.js → chunk-KAQHULYN.js} +2 -2
  33. package/dist/chunk-KE4EQOWA.js +294 -0
  34. package/dist/{chunk-YAFCN7ZW.cjs → chunk-KOVYSDHZ.cjs} +15 -15
  35. package/dist/{chunk-IJSHQGMP.cjs → chunk-KS3RYAYL.cjs} +4 -4
  36. package/dist/{chunk-DI7RDLOS.js → chunk-MD32R22I.js} +7 -4
  37. package/dist/chunk-MV7YI4WO.cjs +293 -0
  38. package/dist/chunk-MY5XHADU.cjs +216 -0
  39. package/dist/{chunk-OP57WUWQ.cjs → chunk-NUNC3VFB.cjs} +6 -6
  40. package/dist/{chunk-ID6EJUHP.js → chunk-PKGRTCCY.js} +1307 -212
  41. package/dist/{chunk-OUC2DFA7.js → chunk-QARL3IDQ.js} +3 -3
  42. package/dist/{chunk-VIPLU4RY.cjs → chunk-S754JHLR.cjs} +4 -4
  43. package/dist/{chunk-FUOKLKR2.js → chunk-SFXHZSRF.js} +1 -1
  44. package/dist/{chunk-2EUAE7AY.js → chunk-SIHHIQLO.js} +2 -2
  45. package/dist/{chunk-SK62UVS6.js → chunk-T6VYG46N.js} +1 -1
  46. package/dist/{chunk-JHXNOB5G.cjs → chunk-TNLGBJYS.cjs} +6 -6
  47. package/dist/{chunk-3QR52LL5.cjs → chunk-TUY2T5KP.cjs} +2 -2
  48. package/dist/{chunk-VWFET3MG.cjs → chunk-UXOCVJJ6.cjs} +1519 -424
  49. package/dist/{chunk-I57ZD5IQ.js → chunk-VIAGTYYW.js} +1 -1
  50. package/dist/chunk-WASS5YWB.js +215 -0
  51. package/dist/{chunk-HOXQLX5R.cjs → chunk-WZJV6DJC.cjs} +444 -103
  52. package/dist/{chunk-BUJ5KYCI.cjs → chunk-YIQBXAYU.cjs} +13 -13
  53. package/dist/{chunk-BF4LXLQV.cjs → chunk-YKIAELDZ.cjs} +132 -174
  54. package/dist/{chunk-M2SVOKC6.js → chunk-ZBPSQCPU.js} +6 -6
  55. package/dist/cli/commands/builtin/export-md.d.ts +19 -0
  56. package/dist/cli/commands/builtin/init/detect.d.ts +25 -0
  57. package/dist/cli/commands/builtin/init/index.d.ts +19 -0
  58. package/dist/cli/main.cjs +2372 -1453
  59. package/dist/cli/main.js +2502 -1583
  60. package/dist/cli/migrate-models.d.ts +45 -0
  61. package/dist/cli/onboarding.d.ts +110 -5
  62. package/dist/cli/updater.d.ts +59 -4
  63. package/dist/client-base-HTF236ZD.cjs +9 -0
  64. package/dist/client-base-QKRKCOWB.js +9 -0
  65. package/dist/{compaction-YNWCEF7B.js → compaction-6VU2T643.js} +4 -2
  66. package/dist/compaction-OKUNTCA7.cjs +33 -0
  67. package/dist/context/compaction.d.ts +35 -3
  68. package/dist/context/manager.d.ts +40 -3
  69. package/dist/context/tool-result-storage.d.ts +96 -0
  70. package/dist/devtools-T2NIZBJK.cjs +6 -0
  71. package/dist/{devtools-IRQQPIRY.js → devtools-YXWHOVKR.js} +1 -1
  72. package/dist/engine/engine.d.ts +47 -0
  73. package/dist/engine/turn-loop.d.ts +6 -0
  74. package/dist/export-md-RXDW6F5W.cjs +135 -0
  75. package/dist/export-md-XXKZANW5.js +135 -0
  76. package/dist/file-history-KYPE3ECT.cjs +7 -0
  77. package/dist/file-history-LF4F7X26.js +7 -0
  78. package/dist/index.cjs +42 -22
  79. package/dist/index.d.ts +5 -2
  80. package/dist/index.js +38 -18
  81. package/dist/llm/client-base.d.ts +1 -1
  82. package/dist/llm/model-cache.d.ts +21 -0
  83. package/dist/llm/model-fetcher.d.ts +29 -0
  84. package/dist/llm/model-pool.d.ts +13 -0
  85. package/dist/llm/provider-catalog.d.ts +30 -0
  86. package/dist/llm/provider-kinds.d.ts +21 -0
  87. package/dist/llm/types.d.ts +8 -0
  88. package/dist/logging/session-recorder.d.ts +77 -0
  89. package/dist/manager-D65VFAPX.js +7 -0
  90. package/dist/manager-TTF5VHJC.cjs +7 -0
  91. package/dist/mcp-manager-QMKWPBZ4.js +8 -0
  92. package/dist/mcp-manager-Z3OKI74O.cjs +8 -0
  93. package/dist/memory-5LWZ6FI2.cjs +9 -0
  94. package/dist/{memory-EPFDBPII.js → memory-VIJLBAMQ.js} +2 -2
  95. package/dist/model-cache-5KIYL5SL.js +13 -0
  96. package/dist/model-cache-OED3IM4R.cjs +13 -0
  97. package/dist/model-fetcher-HKGICACL.js +8 -0
  98. package/dist/model-fetcher-UR6WJOQP.cjs +8 -0
  99. package/dist/openai-JR2WEKOF.js +10 -0
  100. package/dist/openai-YBTOET4O.cjs +10 -0
  101. package/dist/{openrouter-models-2FM3H4SQ.js → openrouter-models-H56GHBHN.js} +2 -2
  102. package/dist/openrouter-models-TWUFWZ5T.cjs +15 -0
  103. package/dist/openrouter-sync-5VMGN6C2.cjs +11 -0
  104. package/dist/{openrouter-sync-SO3AWEAM.js → openrouter-sync-ALEMF5IU.js} +3 -3
  105. package/dist/product/index.cjs +17 -15
  106. package/dist/product/index.js +16 -14
  107. package/dist/protocol/server.d.ts +3 -1
  108. package/dist/protocol/types.d.ts +36 -2
  109. package/dist/run/index.cjs +17 -15
  110. package/dist/run/index.js +16 -14
  111. package/dist/services/index.d.ts +0 -1
  112. package/dist/session-manager-Q22BBMD3.js +8 -0
  113. package/dist/session-manager-XODQVL3C.cjs +8 -0
  114. package/dist/settings/schema.d.ts +267 -42
  115. package/dist/{tool-summary-RYNR4DDB.cjs → tool-summary-63DPIS7G.cjs} +4 -4
  116. package/dist/{tool-summary-2RUFL5QK.js → tool-summary-NP5CDBKG.js} +2 -2
  117. package/dist/tool-system/builtin/ask-user.d.ts +9 -1
  118. package/dist/tool-system/context.d.ts +31 -2
  119. package/dist/tool-system/executor.d.ts +4 -0
  120. package/dist/tool-system/investigation-guard.d.ts +46 -0
  121. package/dist/types.d.ts +13 -2
  122. package/dist/ui/components/AskUserPrompt.d.ts +8 -1
  123. package/dist/ui/components/DiffLine.d.ts +33 -0
  124. package/dist/ui/components/ModelManager.d.ts +34 -1
  125. package/dist/ui/components/OnboardingPrompt.d.ts +10 -1
  126. package/dist/ui/components/ProviderModelFlow.d.ts +58 -0
  127. package/dist/ui/components/UpdateBanner.d.ts +1 -0
  128. package/dist/updater-I7QBW4DP.cjs +17 -0
  129. package/dist/updater-NVME5S3Z.js +17 -0
  130. package/dist/utils/semver.d.ts +1 -0
  131. package/dist/utils/task-sanitizer.d.ts +6 -0
  132. package/package.json +2 -1
  133. package/skills-builtin/codeshell-help.md +135 -0
  134. package/dist/anthropic-P5VETAFI.cjs +0 -10
  135. package/dist/anthropic-W2GU3D76.js +0 -10
  136. package/dist/chunk-75E4BBFC.js +0 -224
  137. package/dist/chunk-FV2ARONZ.cjs +0 -224
  138. package/dist/client-base-TOVE25FA.js +0 -9
  139. package/dist/client-base-VDDIADAC.cjs +0 -9
  140. package/dist/compaction-D3KV2NWY.cjs +0 -31
  141. package/dist/devtools-WTC3NFNE.cjs +0 -6
  142. package/dist/file-history-HZWO2WQ5.js +0 -7
  143. package/dist/file-history-UQQILIY7.cjs +0 -7
  144. package/dist/manager-FWLOVEEK.cjs +0 -7
  145. package/dist/manager-GEXJLOIS.js +0 -7
  146. package/dist/mcp-manager-37GQLFGX.js +0 -8
  147. package/dist/mcp-manager-U77NEXBY.cjs +0 -8
  148. package/dist/memory-ADGOWADP.cjs +0 -9
  149. package/dist/openai-RXU5URYI.js +0 -10
  150. package/dist/openai-SQW4H2K4.cjs +0 -10
  151. package/dist/openrouter-models-PLCWZZYE.cjs +0 -15
  152. package/dist/openrouter-sync-K2WK3A2A.cjs +0 -11
  153. package/dist/services/compact.d.ts +0 -56
  154. package/dist/session-manager-HRSKENKB.cjs +0 -8
  155. package/dist/session-manager-NEKFEHF3.js +0 -8
  156. package/dist/updater-J7L7R6BY.cjs +0 -185
  157. package/dist/updater-LDBNYCAO.js +0 -185
@@ -0,0 +1,45 @@
1
+ /**
2
+ * One-shot migration of legacy flat models[] into providers[] + models[].
3
+ *
4
+ * Pure function — takes a snapshot, returns the new snapshot plus a flag.
5
+ * The caller (settings load path) is responsible for writing settings.json
6
+ * and the .bak backup.
7
+ */
8
+ import type { ProviderConfig } from "../llm/provider-catalog.js";
9
+ interface LegacyModel {
10
+ key: string;
11
+ label?: string;
12
+ provider?: string;
13
+ baseUrl?: string;
14
+ apiKey?: string;
15
+ model: string;
16
+ maxOutputTokens?: number;
17
+ maxContextTokens?: number;
18
+ providerKey?: string;
19
+ }
20
+ export interface MigrationInput {
21
+ providers: ProviderConfig[];
22
+ models: LegacyModel[];
23
+ }
24
+ export interface MigrationOutput {
25
+ providers: ProviderConfig[];
26
+ models: Array<{
27
+ key: string;
28
+ label?: string;
29
+ providerKey: string;
30
+ model: string;
31
+ /**
32
+ * Self-describing legacy fields. Kept (rather than stripped) so model
33
+ * entries stay valid standalone — the engine can read credentials
34
+ * straight off the entry without a ProviderCatalog round-trip.
35
+ */
36
+ provider?: string;
37
+ baseUrl?: string;
38
+ apiKey?: string;
39
+ maxOutputTokens?: number;
40
+ maxContextTokens?: number;
41
+ }>;
42
+ changed: boolean;
43
+ }
44
+ export declare function migrateModels(input: MigrationInput): MigrationOutput;
45
+ export {};
@@ -5,6 +5,13 @@
5
5
  * pure(-ish) helpers it consumes, so there's a single input stack.
6
6
  */
7
7
  export interface OnboardingResult {
8
+ /**
9
+ * Pool alias key (e.g. "deepseek-v4-pro"). The engine uses this to switch
10
+ * the active model — callers should NOT re-derive an alias from `model`
11
+ * (the old modelKey() helper folded multiple model ids to one key, which
12
+ * silently shadowed entries in the pool).
13
+ */
14
+ key: string;
8
15
  provider: string;
9
16
  model: string;
10
17
  apiKey: string;
@@ -42,7 +49,37 @@ export interface DetectedEnvKey {
42
49
  export declare function detectEnvKeys(): DetectedEnvKey[];
43
50
  /** Mask a key for display: "sk-517f...b594" */
44
51
  export declare function maskKey(key: string): string;
52
+ /**
53
+ * Find a provider definition that matches a given API key.
54
+ * Checks key prefix first, then env var match, then baseUrl.
55
+ */
56
+ export declare function detectProviderFromApiKey(apiKey: string, baseUrl?: string): ProviderDef | undefined;
45
57
  export declare function validateApiKey(baseUrl: string, apiKey: string): Promise<boolean>;
58
+ /**
59
+ * Resolve an API key from the canonical fallback chain.
60
+ * Priority: 1) command-line option 2) settings.json 3) all provider env vars
61
+ */
62
+ export declare function resolveApiKey(optionsApiKey?: string, settingsApiKey?: string): string | undefined;
63
+ /**
64
+ * Look up a previously-persisted API key for a given provider in
65
+ * ~/.code-shell/settings.json. Matches by baseUrl (each ProviderDef.baseUrl
66
+ * is unique, while `provider` is a generic kind like "openai" shared across
67
+ * multiple endpoints).
68
+ *
69
+ * Returns the first match found in: settings.models[] (preferred — newer),
70
+ * then settings.model (legacy top-level). undefined when nothing matches.
71
+ */
72
+ export declare function findSavedKeyForProvider(provider: ProviderDef): {
73
+ apiKey: string;
74
+ baseUrl: string;
75
+ } | undefined;
76
+ /**
77
+ * Return the list of model IDs previously saved under this provider
78
+ * (matched by baseUrl). Used by the onboarding wizard to pre-populate the
79
+ * "model pool" step with the user's prior choices instead of resetting to
80
+ * just provider.defaultModel.
81
+ */
82
+ export declare function loadSavedModelsForProvider(provider: ProviderDef): string[];
46
83
  export declare function hasApiKey(): boolean;
47
84
  /**
48
85
  * Resolve a model's max-output-token budget. Lookup order:
@@ -56,12 +93,29 @@ export declare function hasApiKey(): boolean;
56
93
  */
57
94
  export declare function resolveMaxOutput(model: string): number | undefined;
58
95
  /**
59
- * Derive a short key from a model path.
60
- * "anthropic/claude-opus-4.7" "claude-opus"
61
- * "openai/gpt-5" "gpt"
62
- * "deepseek/deepseek-chat" "deepseek"
96
+ * Resolve a model's context window size. Lookup order:
97
+ * 1. KNOWN_CONTEXT_WINDOWS (covers direct providers like DeepSeek)
98
+ * 2. OpenRouter snapshot (covers `vendor/model` style IDs)
99
+ * 3. undefined unknown
63
100
  */
64
- export declare function modelKey(model: string): string;
101
+ export declare function resolveContextWindow(model: string): number | undefined;
102
+ /**
103
+ * Derive a pool key in `provider-model` form. Single source of truth for
104
+ * model alias generation — replaces the old modelKey() function which
105
+ * folded same-family models to one key (e.g. v4-flash and v4-pro both
106
+ * collapsed to "deepseek"), causing the pool to silently shadow entries.
107
+ *
108
+ * ("deepseek", "deepseek-v4-pro") → "deepseek-v4-pro"
109
+ * ("openai", "openai/gpt-5") → "openai-gpt-5"
110
+ * ("anthropic","claude-opus-4-7") → "anthropic-claude-opus-4-7"
111
+ *
112
+ * If the model id already starts with `<provider>-` (e.g. "deepseek-v4-pro"
113
+ * under provider "deepseek"), the prefix isn't duplicated.
114
+ *
115
+ * `used` is a set of already-taken keys; collisions are resolved with a
116
+ * `-2`, `-3`, ... suffix.
117
+ */
118
+ export declare function deriveModelPoolKey(providerKind: string, modelId: string, used?: string[]): string;
65
119
  /**
66
120
  * Build model pool entries from a provider's model list.
67
121
  */
@@ -73,7 +127,58 @@ export declare function buildModelPool(provider: ProviderDef, apiKey: string): A
73
127
  baseUrl: string;
74
128
  apiKey: string;
75
129
  maxOutputTokens?: number;
130
+ maxContextTokens?: number;
76
131
  }>;
77
132
  export declare function modelDisplayName(model: string): string;
78
133
  export declare function saveSettings(result: OnboardingResult, providerDef?: ProviderDef, poolModels?: string[]): void;
134
+ /**
135
+ * Append-only persistence for the new ProviderModelFlow-based onboarding.
136
+ *
137
+ * Writes the new shape:
138
+ * - settings.activeKey — primary source of truth for active model
139
+ * - settings.models[] — self-describing entries (key, provider,
140
+ * model, baseUrl, apiKey, maxOutput, maxContext)
141
+ * - settings.providers[] — credential source for the wizard
142
+ * - settings.model.* — legacy mirror of the active entry, kept in
143
+ * sync so legacy boot paths (cli/main.ts,
144
+ * repl.ts, run.ts) keep working without
145
+ * each having to learn about activeKey
146
+ *
147
+ * Append-only: never removes existing entries. To start over use /logout.
148
+ * Writes atomically via tmp+rename.
149
+ */
150
+ export declare function appendOnboardingResult(opts: {
151
+ /** Alias key the engine should switch to after writing. Matches one of addedModels[].key. */
152
+ activeKey: string;
153
+ /** Active entry's fields, used to mirror into the legacy settings.model.* block. */
154
+ activeMirror: {
155
+ provider: string;
156
+ model: string;
157
+ apiKey: string;
158
+ baseUrl: string;
159
+ };
160
+ addedProvider?: {
161
+ key: string;
162
+ label?: string;
163
+ kind: string;
164
+ baseUrl: string;
165
+ apiKey?: string;
166
+ protocol?: string;
167
+ modelsPath?: string;
168
+ };
169
+ addedModels: Array<{
170
+ key: string;
171
+ label?: string;
172
+ providerKey?: string;
173
+ /** LLM client/protocol ("openai"/"anthropic"). New canonical name. */
174
+ protocol?: string;
175
+ /** Legacy mirror of `protocol`. Both are written so legacy readers keep working. */
176
+ provider?: string;
177
+ model: string;
178
+ baseUrl: string;
179
+ apiKey?: string;
180
+ maxContextTokens?: number;
181
+ maxOutputTokens?: number;
182
+ }>;
183
+ }): void;
79
184
  export declare function saveArenaSettingsByKeys(keys: string[]): void;
@@ -1,10 +1,65 @@
1
1
  /**
2
- * Auto-update checker non-blocking check for newer versions on npm.
2
+ * Auto-update — checks npm registry for newer versions; can optionally install
3
+ * the new version in the background after the current process exits.
4
+ *
5
+ * Flow:
6
+ * 1. `checkForUpdate()` runs at startup (non-blocking). Stores result in
7
+ * module state for the UI to read via `getUpdateAvailable()`.
8
+ * 2. UI re-polls every 30 min via `pollForUpdate()`.
9
+ * 3. If the user has write permission on the npm global prefix, we register
10
+ * `scheduleAutoInstallOnExit()` so that on process exit a detached
11
+ * `npm i -g` spawns and survives the parent. Next startup picks up
12
+ * the new version.
13
+ * 4. If the user has NO permission, UI shows the manual command instead.
14
+ *
15
+ * Concurrency: multiple `code-shell` processes can run at once. A file lock
16
+ * at `~/.code-shell/.update.lock` prevents both from launching `npm i -g`
17
+ * simultaneously (which corrupts the install).
18
+ *
19
+ * Headless safety: `checkForUpdate()` and `scheduleAutoInstallOnExit()` are
20
+ * only invoked from the Ink-based UpdateBanner component, never from the
21
+ * `run` / `arena` headless code paths. Keep it that way — wiring the updater
22
+ * into bare CLI commands would surprise CI users with background `npm i -g`
23
+ * on every short-lived `code-shell run …` invocation. If you need updater
24
+ * behavior in a new headless entry point, gate it on something explicit
25
+ * (e.g. `--auto-update` flag) rather than calling it unconditionally.
3
26
  */
27
+ export interface UpdateInfo {
28
+ latestVersion: string;
29
+ /** True if `<npm prefix>` is writable by current user — can self-install. */
30
+ canAutoInstall: boolean;
31
+ /** Cached npm global prefix path (for diagnostics / manual command hint). */
32
+ npmPrefix: string | null;
33
+ }
4
34
  /**
5
- * Check if a newer version is available (non-blocking).
6
- * Call this on startup and read the result later.
35
+ * Kick off an update check. Non-blocking: returns immediately, fills module
36
+ * state when network/npm respond. Safe to call multiple times (e.g. on a 30min
37
+ * timer).
7
38
  */
8
39
  export declare function checkForUpdate(packageName?: string): void;
9
- export declare function getUpdateAvailable(): string | undefined;
40
+ /** Returns update info if a newer version was found, else undefined. */
41
+ export declare function getUpdateAvailable(): UpdateInfo | undefined;
42
+ /** Returns "why is auto-update off" — for /doctor-style diagnostics. */
43
+ export declare function getAutoUpdateDisabledReason(): string | null;
44
+ /**
45
+ * Install the on-exit hook so that the new version is installed AFTER the
46
+ * current process exits (avoids overwriting files the running process still
47
+ * has open). Idempotent — calling twice is a no-op.
48
+ */
49
+ export declare function scheduleAutoInstallOnExit(): void;
10
50
  export declare function getCurrentVersion(): string;
51
+ declare function getLockFilePath(): string;
52
+ /**
53
+ * Try to acquire the update lock. Returns true on success.
54
+ * Uses `O_EXCL` (writeFile flag 'wx') for atomic create. Stale locks (mtime
55
+ * older than LOCK_TIMEOUT_MS) are taken over, re-verifying staleness right
56
+ * before unlink to close the TOCTOU race.
57
+ */
58
+ declare function acquireLock(): Promise<boolean>;
59
+ declare function releaseLock(): Promise<void>;
60
+ export declare const __internal: {
61
+ acquireLock: typeof acquireLock;
62
+ releaseLock: typeof releaseLock;
63
+ getLockFilePath: typeof getLockFilePath;
64
+ };
65
+ export {};
@@ -0,0 +1,9 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunk7EG3V4JYcjs = require('./chunk-7EG3V4JY.cjs');
4
+ require('./chunk-2YW4FPFG.cjs');
5
+ require('./chunk-IK4EWJBP.cjs');
6
+ require('./chunk-KS3RYAYL.cjs');
7
+
8
+
9
+ exports.LLMClientBase = _chunk7EG3V4JYcjs.LLMClientBase;
@@ -0,0 +1,9 @@
1
+ import {
2
+ LLMClientBase
3
+ } from "./chunk-6S4BJ65N.js";
4
+ import "./chunk-SFXHZSRF.js";
5
+ import "./chunk-BXCY2FSE.js";
6
+ import "./chunk-MD32R22I.js";
7
+ export {
8
+ LLMClientBase
9
+ };
@@ -1,4 +1,5 @@
1
1
  import {
2
+ COMPACTABLE_TOOL_NAMES,
2
3
  adjustIndexToPreserveAPIInvariants,
3
4
  applySummaryCompaction,
4
5
  applyToolResultBudget,
@@ -12,10 +13,11 @@ import {
12
13
  snipCompact,
13
14
  truncateToolResult,
14
15
  windowCompact
15
- } from "./chunk-5C5HIOML.js";
16
- import "./chunk-DI7RDLOS.js";
16
+ } from "./chunk-7Y6S3OCX.js";
17
+ import "./chunk-MD32R22I.js";
17
18
  init_compaction();
18
19
  export {
20
+ COMPACTABLE_TOOL_NAMES,
19
21
  adjustIndexToPreserveAPIInvariants,
20
22
  applySummaryCompaction,
21
23
  applyToolResultBudget,
@@ -0,0 +1,33 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+ var _chunk6QVAYW7Ccjs = require('./chunk-6QVAYW7C.cjs');
17
+ require('./chunk-KS3RYAYL.cjs');
18
+ _chunk6QVAYW7Ccjs.init_compaction.call(void 0, );
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+ exports.COMPACTABLE_TOOL_NAMES = _chunk6QVAYW7Ccjs.COMPACTABLE_TOOL_NAMES; exports.adjustIndexToPreserveAPIInvariants = _chunk6QVAYW7Ccjs.adjustIndexToPreserveAPIInvariants; exports.applySummaryCompaction = _chunk6QVAYW7Ccjs.applySummaryCompaction; exports.applyToolResultBudget = _chunk6QVAYW7Ccjs.applyToolResultBudget; exports.buildSummarizationPrompt = _chunk6QVAYW7Ccjs.buildSummarizationPrompt; exports.dropOldestRounds = _chunk6QVAYW7Ccjs.dropOldestRounds; exports.estimateTokens = _chunk6QVAYW7Ccjs.estimateTokens; exports.extractReferencedFilePaths = _chunk6QVAYW7Ccjs.extractReferencedFilePaths; exports.groupMessagesByApiRound = _chunk6QVAYW7Ccjs.groupMessagesByApiRound; exports.microcompact = _chunk6QVAYW7Ccjs.microcompact; exports.snipCompact = _chunk6QVAYW7Ccjs.snipCompact; exports.truncateToolResult = _chunk6QVAYW7Ccjs.truncateToolResult; exports.windowCompact = _chunk6QVAYW7Ccjs.windowCompact;
@@ -36,12 +36,44 @@ export declare function snipCompact(messages: Message[], keepFirstN?: number, ke
36
36
  * Uses adjustIndexToPreserveAPIInvariants to avoid splitting tool pairs.
37
37
  */
38
38
  export declare function windowCompact(messages: Message[], keepLastN: number): Message[];
39
+ /**
40
+ * Tools whose results are safe to clear during microcompact.
41
+ *
42
+ * Tracks the CC `COMPACTABLE_TOOLS` set: only "data-fetching" tools where the
43
+ * result is reconstructible (re-Read, re-Glob, re-Bash). Orchestration tools
44
+ * (TaskCreate / TaskUpdate / Agent / Skill / etc.) are excluded — their
45
+ * results carry conversation state the model needs to keep referencing.
46
+ */
47
+ export declare const COMPACTABLE_TOOL_NAMES: ReadonlySet<string>;
48
+ export interface MicrocompactOptions {
49
+ /** Keep this many most-recent compactable tool-result rounds untouched. */
50
+ keepRecentN?: number;
51
+ /** Tool names whose results may be cleared. Defaults to COMPACTABLE_TOOL_NAMES. */
52
+ compactableTools?: ReadonlySet<string>;
53
+ /** Optional callback invoked once when any clearing actually happened. */
54
+ onClear?: (info: {
55
+ clearedRounds: number;
56
+ toolNames: string[];
57
+ }) => void;
58
+ }
39
59
  /**
40
60
  * Remove old tool_result content (microcompact).
41
- * Replaces tool_result content with "[Old tool result cleared]" for messages
42
- * older than keepRecentN tool rounds.
61
+ *
62
+ * For each tool_result block older than the most-recent N compactable rounds,
63
+ * replaces its content with a short fingerprint of the originating call
64
+ * (e.g. `[Old tool result cleared — Read file_path=/.../foo.ts]`) so the
65
+ * model can decide whether it's worth re-running the tool instead of
66
+ * guessing what it had seen.
67
+ *
68
+ * Behavior changes vs. earlier versions:
69
+ * - Only clears whitelisted tool names (Read/Glob/Bash/...). Orchestration
70
+ * tool results (TaskCreate/TaskUpdate/Agent/etc.) are left intact.
71
+ * - "Round" count is per *compactable* result, not per message with any
72
+ * tool_result. A round containing only TaskUpdate doesn't count toward N.
73
+ * - Replacement string includes tool name + key args so the model isn't
74
+ * forced into blind re-Reads (which then trip the Investigation guard).
43
75
  */
44
- export declare function microcompact(messages: Message[], keepRecentN?: number): Message[];
76
+ export declare function microcompact(messages: Message[], options?: MicrocompactOptions): Message[];
45
77
  /**
46
78
  * Apply aggregate per-message tool result budget.
47
79
  * When total tool_result content in a single message exceeds maxTotalChars,
@@ -11,13 +11,27 @@ export interface ContextManagerConfig {
11
11
  compactAtRatio: number;
12
12
  summarizeAtRatio: number;
13
13
  maxToolResultChars: number;
14
+ /**
15
+ * Lower bound, as a ratio of maxTokens, below which microcompact will not
16
+ * run. CC's external (non-cache-edit) path is "autocompact handles context
17
+ * pressure"; mirror that by leaving early-turn context alone so a model
18
+ * with a 1M window doesn't see its first-3-rounds Read results wiped at
19
+ * 23k tokens. Raising the floor also keeps prompt-cache prefixes warm in
20
+ * short sessions.
21
+ */
22
+ microcompactFloorRatio: number;
23
+ /**
24
+ * Most-recent compactable rounds microcompact keeps untouched. Auto-derived
25
+ * from maxTokens when not set explicitly: bigger window → keep more.
26
+ */
27
+ microcompactKeepRecent?: number;
14
28
  }
15
29
  /**
16
30
  * Async function type for LLM summarization calls.
17
31
  * Injected by the Engine so the ContextManager doesn't depend on LLM directly.
18
32
  */
19
33
  export type SummarizeFn = (prompt: string) => Promise<string>;
20
- export type CompactStrategy = "summary" | "window" | "snip" | "emergency";
34
+ export type CompactStrategy = "micro" | "summary" | "window" | "snip" | "emergency";
21
35
  export type OnCompactFn = (info: {
22
36
  strategy: CompactStrategy;
23
37
  before: number;
@@ -35,8 +49,15 @@ export declare class ContextManager {
35
49
  private lastActualAtMessageCount;
36
50
  /** Path to session transcript — passed to summary compaction for on-demand access. */
37
51
  private transcriptPath;
38
- /** Notified whenever a non-trivial compaction tier fires (skips microcompact). */
52
+ /** Notified whenever any compaction tier fires, including microcompact. */
39
53
  private onCompact;
54
+ /**
55
+ * Tool-result persistence state. Created lazily once we know where to
56
+ * write files (i.e. once setTranscriptPath has been called).
57
+ * `null` = persistence has been explicitly disabled.
58
+ */
59
+ private replacementState;
60
+ private toolResultsDir;
40
61
  constructor(config?: Partial<ContextManagerConfig>);
41
62
  setOnCompact(fn: OnCompactFn): void;
42
63
  /**
@@ -54,9 +75,19 @@ export declare class ContextManager {
54
75
  */
55
76
  setSummarizeFn(fn: SummarizeFn): void;
56
77
  /**
57
- * Set the transcript path so compaction can reference it.
78
+ * Set the transcript path so compaction can reference it. The
79
+ * tool-results directory lives alongside the transcript file:
80
+ * <transcriptDir>/tool-results/<toolUseId>.txt
58
81
  */
59
82
  setTranscriptPath(path: string): void;
83
+ /**
84
+ * Initialize tool-result-persistence state from already-loaded messages.
85
+ * Call this once per session, after setTranscriptPath, when resuming so
86
+ * the manager re-enters the same "frozen decision" state it had on the
87
+ * previous run. Without this, a resumed session would re-evaluate
88
+ * every result against the current threshold and might flip choices.
89
+ */
90
+ initReplacementStateFromMessages(messages: Message[]): void;
60
91
  /**
61
92
  * Apply progressive context management (sync path).
62
93
  * For Tier 2 (LLM summary), call manageAsync instead.
@@ -67,6 +98,12 @@ export declare class ContextManager {
67
98
  * Call this when you have access to the LLM (between turns).
68
99
  */
69
100
  manageAsync(messages: Message[]): Promise<Message[]>;
101
+ /**
102
+ * Persist large tool_result blocks to disk and replace them with a
103
+ * preview + filepath. No-op when transcript path hasn't been set
104
+ * (e.g. unit tests that exercise compaction directly without a session).
105
+ */
106
+ private persistLargeToolResults;
70
107
  /**
71
108
  * Truncate oversized tool results in messages.
72
109
  */
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Tool-result persistence — write large tool_result content to disk and
3
+ * replace the in-context message with a reference + preview.
4
+ *
5
+ * Mirrors Claude Code's services/compact/microcompact.ts + utils/toolResultStorage.ts
6
+ * approach, simplified for codeshell:
7
+ *
8
+ * • One file per tool_use_id (UUID), so the same id always maps to the
9
+ * same file — guarantees byte-identical preview on every turn.
10
+ * • Decision state (ContentReplacementState) is held by the caller and
11
+ * mutated in place. Once a result is "seen", its fate is frozen for
12
+ * the rest of the session (prevents flapping replacement choices that
13
+ * would constantly invalidate the prompt prefix).
14
+ * • No transcript persistence / no fork-subagent gap-fill. Resume just
15
+ * re-derives the seenIds set from the loaded messages.
16
+ *
17
+ * Strategy:
18
+ * • Per-result cap (DEFAULT_PERSIST_THRESHOLD): when a single tool_result
19
+ * exceeds it, persist + replace.
20
+ * • Per-message aggregate cap (PER_MESSAGE_AGGREGATE_CAP): when a single
21
+ * user message's tool_results together exceed it (e.g. a batch of
22
+ * parallel Read results), persist the largest ones first until under.
23
+ */
24
+ import type { Message } from "../types.js";
25
+ /** Per-result threshold. Larger than this → persist + replace. */
26
+ export declare const DEFAULT_PERSIST_THRESHOLD = 50000;
27
+ /** Per-message aggregate cap (sum of tool_result content sizes in one user msg). */
28
+ export declare const PER_MESSAGE_AGGREGATE_CAP = 200000;
29
+ /** Preview length included in the replacement string. */
30
+ export declare const PREVIEW_SIZE = 2000;
31
+ /**
32
+ * Per-session decision state for tool-result persistence.
33
+ *
34
+ * seenIds — every tool_use_id we've evaluated; once seen, its fate
35
+ * (persisted or not) is fixed for the rest of the session.
36
+ * replacements — subset of seenIds that were persisted, mapped to the
37
+ * EXACT replacement string sent to the model. Re-applying
38
+ * is a Map lookup, never re-reads the file, guarantees
39
+ * byte-identical output.
40
+ */
41
+ export interface ContentReplacementState {
42
+ seenIds: Set<string>;
43
+ replacements: Map<string, string>;
44
+ }
45
+ export declare function createContentReplacementState(): ContentReplacementState;
46
+ /**
47
+ * Rebuild a state object by walking the loaded message history.
48
+ * Used on resume so the budget makes the same decisions it made before.
49
+ * Replacements are taken from the messages themselves (we identify them
50
+ * by the PERSISTED_OPEN sentinel), so we don't need a side-channel log.
51
+ */
52
+ export declare function reconstructContentReplacementState(messages: Message[]): ContentReplacementState;
53
+ /**
54
+ * Resolve the directory tool results are written to, given the engine's
55
+ * transcript path. Layout: <transcriptDir>/tool-results/
56
+ */
57
+ export declare function resolveToolResultsDir(transcriptPath: string): string;
58
+ interface PersistOptions {
59
+ /** Per-result threshold; default DEFAULT_PERSIST_THRESHOLD. */
60
+ perResultThreshold?: number;
61
+ /** Per-message aggregate cap; default PER_MESSAGE_AGGREGATE_CAP. */
62
+ perMessageCap?: number;
63
+ /** Directory to write tool-result files to. Required. */
64
+ toolResultsDir: string;
65
+ /** Decision state, mutated in place. */
66
+ state: ContentReplacementState;
67
+ /** Optional callback fired when a result is freshly persisted. */
68
+ onPersist?: (info: {
69
+ toolUseId: string;
70
+ filepath: string;
71
+ originalSize: number;
72
+ reason: "per-result-cap" | "per-message-budget";
73
+ }) => void;
74
+ }
75
+ /**
76
+ * Walk each user message, decide which tool_result blocks should be
77
+ * persisted (per-result cap OR per-message aggregate), persist them,
78
+ * and return a new message array with those blocks replaced.
79
+ *
80
+ * Frozen decisions:
81
+ * - If a tool_use_id is already in state.replacements, that exact
82
+ * replacement is re-applied (no I/O).
83
+ * - If it's in state.seenIds but NOT replacements, it was decided
84
+ * "don't persist" on an earlier pass — leave it unreplaced even if
85
+ * it now exceeds the threshold (changing the decision mid-session
86
+ * would invalidate any model state built on the prior content).
87
+ *
88
+ * @returns the new messages array, with replacements applied.
89
+ */
90
+ export declare function applyToolResultPersistence(messages: Message[], options: PersistOptions): Message[];
91
+ /**
92
+ * Public test helper: check whether content already wears the persisted
93
+ * sentinel. Used by tests to assert idempotency.
94
+ */
95
+ export declare function isPersistedReplacement(s: string): boolean;
96
+ export {};
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+
3
+ var _chunkKS3RYAYLcjs = require('./chunk-KS3RYAYL.cjs');
4
+
5
+ // src/render/devtools.ts
6
+ _chunkKS3RYAYLcjs.init_cjs_shims.call(void 0, );
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  init_esm_shims
3
- } from "./chunk-DI7RDLOS.js";
3
+ } from "./chunk-MD32R22I.js";
4
4
 
5
5
  // src/render/devtools.ts
6
6
  init_esm_shims();
@@ -36,6 +36,13 @@ export interface EngineConfig {
36
36
  * Engine doesn't persist cost state — it's a UI concern.
37
37
  */
38
38
  costStore?: CostStateStore;
39
+ /**
40
+ * True when the Engine runs in a no-UI / one-shot context (e.g. the `run`
41
+ * command). Currently controls InvestigationGuard soft-mode: in headless
42
+ * we never hard-block a tool call because there is no human to retry.
43
+ * Defaults to false.
44
+ */
45
+ headless?: boolean;
39
46
  }
40
47
  export interface EngineHookConfig {
41
48
  event: HookEventName;
@@ -64,8 +71,29 @@ export declare class Engine {
64
71
  private lastSessionId;
65
72
  private compactedMessagesBySession;
66
73
  private activePermission;
74
+ /** Public accessor so UI/clients can read the resolved per-model window. */
75
+ get maxContextTokens(): number;
67
76
  private resolveMaxContextTokens;
68
77
  constructor(config: EngineConfig);
78
+ /**
79
+ * Load models[] / providers[] from settings into the active ModelPool and
80
+ * resync this.config.llm with the matching entry. Called from the ctor and
81
+ * from reloadModelPool() (e.g. after onboarding writes new entries to disk).
82
+ */
83
+ private populateModelPoolFromSettings;
84
+ /**
85
+ * Re-read settings and refresh the model pool. Used after onboarding /login
86
+ * writes new providers[] / models[] to disk so the running engine picks them
87
+ * up without a process restart. Existing pool entries are kept (re-registering
88
+ * the same key overwrites them), so callers don't need to clear first.
89
+ */
90
+ reloadModelPool(): void;
91
+ /**
92
+ * Auto-populate the model pool when settings.models[] is empty but
93
+ * the user has configured an API key. Detects the provider from the
94
+ * key prefix / baseUrl and registers all its known models.
95
+ */
96
+ private autoPopulatePool;
69
97
  /**
70
98
  * Register a custom tool (from product adapter) into the tool registry.
71
99
  * Must be called before run().
@@ -89,8 +117,23 @@ export declare class Engine {
89
117
  /**
90
118
  * Switch the active model by pool key. Takes effect on the next run() call.
91
119
  * Returns the new model entry.
120
+ *
121
+ * Persists settings.activeKey (and a legacy settings.model.* mirror) so the
122
+ * next process startup defaults to the same model — without this, switches
123
+ * only live in memory and every restart reverts to the previously persisted
124
+ * activeKey.
92
125
  */
93
126
  switchModel(key: string): ModelEntry;
127
+ /**
128
+ * Write the active model selection to ~/.code-shell/settings.json.
129
+ *
130
+ * We mirror into the legacy settings.model.* block (provider/name/apiKey/
131
+ * baseUrl) because boot paths in cli/main.ts, repl.ts, run.ts still read it.
132
+ * The mirror uses resolved llm values (not raw entry.*) so credentials that
133
+ * live on settings.providers[] flow through correctly — entry.apiKey is
134
+ * undefined when the entry resolves via providerCatalog.
135
+ */
136
+ private persistActiveModel;
94
137
  /** Get the model pool. */
95
138
  getModelPool(): ModelPool;
96
139
  /** Get the current model name (full path). */
@@ -102,6 +145,10 @@ export declare class Engine {
102
145
  * Inject context into a session's transcript without triggering a LLM turn.
103
146
  * The injected content appears as an assistant message so the LLM can see it
104
147
  * in subsequent conversations. The transcript auto-flushes to disk.
148
+ *
149
+ * Also updates the in-memory compacted message cache so the next
150
+ * engine.run() call for this session picks up the injected content
151
+ * instead of a stale snapshot from the previous run.
105
152
  */
106
153
  injectContext(sessionId: string, content: string): void;
107
154
  /**
@@ -16,6 +16,12 @@ export interface TurnLoopConfig {
16
16
  tokenBudget?: number;
17
17
  onStream?: StreamCallback;
18
18
  signal?: AbortSignal;
19
+ /**
20
+ * Fired after each turn boundary is recorded. Lets the engine flush an
21
+ * up-to-date snapshot to state.json mid-run, so a long run doesn't leave
22
+ * the on-disk turnCount/status frozen at the last completion.
23
+ */
24
+ onTurnBoundary?: (turnCount: number) => void;
19
25
  }
20
26
  export interface TurnLoopDeps {
21
27
  model: ModelFacade;