compact-agent 1.30.0 → 1.30.2
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.
- package/dist/config.d.ts +3 -4
- package/dist/config.js +25 -14
- package/dist/config.js.map +1 -1
- package/dist/index.js +130 -17
- package/dist/index.js.map +1 -1
- package/dist/sessions.d.ts +46 -1
- package/dist/sessions.js +92 -6
- package/dist/sessions.js.map +1 -1
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -11,10 +11,9 @@ export declare const LEGACY_CONFIG_DIR_NAME = ".crowcoder";
|
|
|
11
11
|
export declare function getProjectStateDir(cwd: string): string;
|
|
12
12
|
export declare function getConfigDir(): string;
|
|
13
13
|
/**
|
|
14
|
-
* Same as
|
|
15
|
-
* modules
|
|
16
|
-
*
|
|
17
|
-
* `join(homedir(), '.crowcoder')`.
|
|
14
|
+
* Same as getConfigDir() but exported under a clearer name for
|
|
15
|
+
* other modules that want the home-dir state root. Used by
|
|
16
|
+
* sessions, debug log, gateguard state, etc.
|
|
18
17
|
*/
|
|
19
18
|
export declare function getHomeStateDir(): string;
|
|
20
19
|
export declare function loadConfig(): CrowcoderConfig;
|
package/dist/config.js
CHANGED
|
@@ -14,10 +14,21 @@ import { homedir } from 'node:os';
|
|
|
14
14
|
// skills, memory etc. without any manual step.
|
|
15
15
|
export const CONFIG_DIR_NAME = '.compact-agent';
|
|
16
16
|
export const LEGACY_CONFIG_DIR_NAME = '.crowcoder';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
// Resolve the config dir LAZILY (every call) instead of caching at
|
|
18
|
+
// module-load time. The cached form prevented tests + sandboxed runs
|
|
19
|
+
// from overriding via COMPACT_AGENT_HOME after the first import: the
|
|
20
|
+
// first module to load would freeze the path at the user's real
|
|
21
|
+
// home, and all subsequent overrides were ignored. The resolution
|
|
22
|
+
// is cheap (env lookup + one join) so calling per access has no
|
|
23
|
+
// observable cost on the hot path.
|
|
24
|
+
function resolveConfigDir() {
|
|
25
|
+
return (process.env.COMPACT_AGENT_HOME ||
|
|
26
|
+
process.env.CROWCODER_HOME ||
|
|
27
|
+
join(homedir(), CONFIG_DIR_NAME));
|
|
28
|
+
}
|
|
29
|
+
function resolveConfigFile() {
|
|
30
|
+
return join(resolveConfigDir(), 'config.json');
|
|
31
|
+
}
|
|
21
32
|
// Tracks whether we've already attempted the legacy-dir migration this
|
|
22
33
|
// process. Migration is idempotent (existsSync guard) but we still cache
|
|
23
34
|
// the decision so loadConfig() can be called repeatedly without re-stat'ing.
|
|
@@ -161,25 +172,25 @@ const DEFAULT_CONFIG = {
|
|
|
161
172
|
},
|
|
162
173
|
};
|
|
163
174
|
export function getConfigDir() {
|
|
164
|
-
return
|
|
175
|
+
return resolveConfigDir();
|
|
165
176
|
}
|
|
166
177
|
/**
|
|
167
|
-
* Same as
|
|
168
|
-
* modules
|
|
169
|
-
*
|
|
170
|
-
* `join(homedir(), '.crowcoder')`.
|
|
178
|
+
* Same as getConfigDir() but exported under a clearer name for
|
|
179
|
+
* other modules that want the home-dir state root. Used by
|
|
180
|
+
* sessions, debug log, gateguard state, etc.
|
|
171
181
|
*/
|
|
172
182
|
export function getHomeStateDir() {
|
|
173
|
-
return
|
|
183
|
+
return resolveConfigDir();
|
|
174
184
|
}
|
|
175
185
|
export function loadConfig() {
|
|
176
186
|
// Try to migrate legacy ~/.crowcoder → ~/.compact-agent before reading.
|
|
177
187
|
migrateLegacyHomeDir();
|
|
178
|
-
|
|
188
|
+
const configFile = resolveConfigFile();
|
|
189
|
+
if (!existsSync(configFile)) {
|
|
179
190
|
return { ...DEFAULT_CONFIG };
|
|
180
191
|
}
|
|
181
192
|
try {
|
|
182
|
-
const raw = readFileSync(
|
|
193
|
+
const raw = readFileSync(configFile, 'utf-8');
|
|
183
194
|
const loaded = JSON.parse(raw);
|
|
184
195
|
const config = { ...DEFAULT_CONFIG, ...loaded };
|
|
185
196
|
validateConfig(config);
|
|
@@ -229,8 +240,8 @@ function validateConfig(config) {
|
|
|
229
240
|
}
|
|
230
241
|
}
|
|
231
242
|
export function saveConfig(config) {
|
|
232
|
-
mkdirSync(
|
|
233
|
-
writeFileSync(
|
|
243
|
+
mkdirSync(resolveConfigDir(), { recursive: true });
|
|
244
|
+
writeFileSync(resolveConfigFile(), JSON.stringify(config, null, 2), 'utf-8');
|
|
234
245
|
}
|
|
235
246
|
export function configExists() {
|
|
236
247
|
const cfg = loadConfig();
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACzG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,wEAAwE;AACxE,yEAAyE;AACzE,EAAE;AACF,sEAAsE;AACtE,4DAA4D;AAC5D,4CAA4C;AAC5C,EAAE;AACF,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,+CAA+C;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAChD,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AAEnD,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACzG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,wEAAwE;AACxE,yEAAyE;AACzE,EAAE;AACF,sEAAsE;AACtE,4DAA4D;AAC5D,4CAA4C;AAC5C,EAAE;AACF,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,+CAA+C;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAChD,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AAEnD,mEAAmE;AACnE,qEAAqE;AACrE,qEAAqE;AACrE,gEAAgE;AAChE,kEAAkE;AAClE,gEAAgE;AAChE,mCAAmC;AACnC,SAAS,gBAAgB;IACvB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAC9B,OAAO,CAAC,GAAG,CAAC,cAAc;QAC1B,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CACjC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,IAAI,CAAC,gBAAgB,EAAE,EAAE,aAAa,CAAC,CAAC;AACjD,CAAC;AAED,uEAAuE;AACvE,yEAAyE;AACzE,6EAA6E;AAC7E,IAAI,uBAAuB,GAAG,KAAK,CAAC;AAEpC;;;;;;;;;;;;;;GAcG;AACH,SAAS,oBAAoB;IAC3B,IAAI,uBAAuB;QAAE,OAAO;IACpC,uBAAuB,GAAG,IAAI,CAAC;IAE/B,uDAAuD;IACvD,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO;IAEzE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAE1D,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO;IAEzD,IAAI,CAAC;QACH,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC9B,OAAO,CAAC,IAAI,CACV,oBAAoB,sBAAsB,QAAQ,eAAe,6CAA6C,CAC/G,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;QACpE,+DAA+D;QAC/D,IAAI,CAAC;YACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,OAAO,CAAC,IAAI,CACV,oBAAoB,sBAAsB,QAAQ,eAAe,eAAe,CACjF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CACV,qCAAqC,sBAAsB,QAAQ,eAAe,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,6DAA6D,CAC7L,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACnE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,cAAc,GAAoB;IACtC,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,8BAA8B;IACvC,KAAK,EAAE,2BAA2B;IAClC,gEAAgE;IAChE,mEAAmE;IACnE,gEAAgE;IAChE,oEAAoE;IACpE,4DAA4D;IAC5D,aAAa,EAAE,2BAA2B;IAC1C,QAAQ,EAAE,YAAY;IACtB,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,GAAG;IAChB,cAAc,EAAE,KAAK;IACrB,sEAAsE;IACtE,wEAAwE;IACxE,OAAO,EAAE,cAAc;IACvB,4EAA4E;IAC5E,+DAA+D;IAC/D,YAAY,EAAE,IAAI;IAClB,6DAA6D;IAC7D,mEAAmE;IACnE,wEAAwE;IACxE,sBAAsB;IACtB,OAAO,EAAE;QACP,KAAK,EAAE,KAAK;KACb;IACD,2EAA2E;IAC3E,wEAAwE;IACxE,2EAA2E;IAC3E,mDAAmD;IACnD,MAAM,EAAE;QACN,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,IAAI;KACnB;IACD,4EAA4E;IAC5E,sEAAsE;IACtE,4EAA4E;IAC5E,oDAAoD;IACpD,KAAK,EAAE;QACL,OAAO,EAAE,KAAK;QACd,GAAG,EAAE;YACH,sEAAsE;YACtE,oEAAoE;YACpE,sEAAsE;YACtE,gBAAgB;YAChB,OAAO,EAAE,2BAA2B;YACpC,KAAK,EAAE,WAAW;YAClB,YAAY,EAAE,IAAI;YAClB,UAAU,EAAE,KAAK;SAClB;QACD,GAAG,EAAE;YACH,uEAAuE;YACvE,wCAAwC;YACxC,OAAO,EAAE,8BAA8B;YACvC,KAAK,EAAE,mBAAmB;YAC1B,sEAAsE;YACtE,iEAAiE;YACjE,4BAA4B;YAC5B,gBAAgB,EAAE,sBAAsB;YACxC,WAAW,EAAE,sBAAsB;YACnC,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,GAAG;YACd,eAAe,EAAE,IAAI;SACtB;QACD,aAAa,EAAE;YACb,sEAAsE;YACtE,wEAAwE;YACxE,mBAAmB;YACnB,YAAY,EAAE,KAAK;YACnB,SAAS,EAAE,IAAI;YACf,cAAc,EAAE,IAAI;YACpB,oBAAoB,EAAE,IAAI;YAC1B,oBAAoB,EAAE,IAAI;YAC1B,qBAAqB,EAAE,GAAG;SAC3B;KACF;CACF,CAAC;AAEF,MAAM,UAAU,YAAY;IAC1B,OAAO,gBAAgB,EAAE,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,gBAAgB,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,wEAAwE;IACxE,oBAAoB,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC;IACvC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;QAChD,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,mCAAmC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5F,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,0EAA0E;AAC1E,yEAAyE;AACzE,2EAA2E;AAC3E,0EAA0E;AAC1E,8CAA8C;AAC9C,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAU,CAAC;AAE/C,SAAS,cAAc,CAAC,MAAuB;IAC7C,mBAAmB;IACnB,IAAI,MAAM,CAAC,OAAO,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzD,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,IAAI,CAAC,6BAA6B,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,MAAM,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,iBAAiB;IACjB,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACpF,OAAO,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAC3D,MAAM,CAAC,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC;IACtC,CAAC;IAED,0BAA0B;IAC1B,MAAM,UAAU,GAAwC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChF,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,oCAAoC,MAAM,CAAC,cAAc,eAAe,CAAC,CAAC;QACvF,MAAM,CAAC,cAAc,GAAG,KAAK,CAAC;IAChC,CAAC;IAED,4BAA4B;IAC5B,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;IACnQ,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/D,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,qCAAqC,GAAG,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAuB;IAChD,SAAS,CAAC,gBAAgB,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,aAAa,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,WAAW,CAAC,GAAoB;IACvC,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAClF,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { runQuery } from './query.js';
|
|
|
13
13
|
import { ALL_TOOLS } from './tools/index.js';
|
|
14
14
|
import { PROVIDERS } from './types.js';
|
|
15
15
|
// New systems
|
|
16
|
-
import { createSession, autoSave, listSessions, loadSession, deleteSession, saveSession, generateSessionId } from './sessions.js';
|
|
16
|
+
import { createSession, autoSave, listSessions, loadSession, deleteSession, saveSession, generateSessionId, resolveSessionRef } from './sessions.js';
|
|
17
17
|
import { initHooksDir, runHooks, listHooks, saveHooksConfig, clearQuarantinedHooks } from './hooks.js';
|
|
18
18
|
import { printUsageSummary, setBudget } from './cost-tracker.js';
|
|
19
19
|
import { getCompactionStats } from './compaction.js';
|
|
@@ -777,40 +777,139 @@ export function handleSlashCommand(input, config, messages, session, mode) {
|
|
|
777
777
|
}
|
|
778
778
|
else {
|
|
779
779
|
console.log(chalk.cyan(`\n Saved Sessions (${sessions.length}):`));
|
|
780
|
+
// Show the FULL ID. Previously we truncated to 12 chars for
|
|
781
|
+
// visual density, but users copy-paste the displayed string
|
|
782
|
+
// into /resume and the truncated form doesn't match the
|
|
783
|
+
// actual filename — every paste failed. Width-aware padding
|
|
784
|
+
// keeps the table aligned across rows of differing ID
|
|
785
|
+
// lengths.
|
|
786
|
+
const maxIdLen = Math.max(...sessions.map((s) => s.id.length));
|
|
780
787
|
for (const s of sessions.slice(0, 20)) {
|
|
781
|
-
console.log(chalk.white(` ${s.id.
|
|
788
|
+
console.log(chalk.white(` ${s.id.padEnd(maxIdLen + 2)}`) +
|
|
782
789
|
chalk.dim(`${s.name.padEnd(30)} ${s.turnCount} turns ${s.model} ${s.updatedAt.slice(0, 10)}`));
|
|
783
790
|
}
|
|
784
|
-
console.log();
|
|
791
|
+
console.log(chalk.dim(`\n Use /resume <id>, /resume <prefix>, or /resume last to restore one.`));
|
|
785
792
|
}
|
|
786
793
|
return { handled: true };
|
|
787
794
|
}
|
|
788
795
|
case '/save':
|
|
789
796
|
session.name = args || session.name;
|
|
790
|
-
|
|
797
|
+
// Snapshot live state so the saved session captures the
|
|
798
|
+
// user's current mode + perm choices, not the values that
|
|
799
|
+
// were on the Session object at create time. Otherwise
|
|
800
|
+
// /resume restores stale config and the model behaves
|
|
801
|
+
// unexpectedly (e.g. a yolo session resumed in ask mode).
|
|
802
|
+
autoSave(session, messages, {
|
|
803
|
+
model: config.model,
|
|
804
|
+
provider: config.provider,
|
|
805
|
+
mode: mode.current,
|
|
806
|
+
permissionMode: config.permissionMode,
|
|
807
|
+
cwd: process.cwd(),
|
|
808
|
+
});
|
|
791
809
|
console.log(chalk.green(` Session saved: ${session.id} "${session.name}"`));
|
|
792
810
|
return { handled: true };
|
|
793
811
|
case '/resume': {
|
|
794
|
-
if (!args) {
|
|
795
|
-
console.log(chalk.yellow(' Usage: /resume <session-id>'));
|
|
812
|
+
if (!args.trim()) {
|
|
813
|
+
console.log(chalk.yellow(' Usage: /resume <session-id> | <prefix> | last'));
|
|
814
|
+
console.log(chalk.dim(' /sessions lists what\'s saved.'));
|
|
796
815
|
return { handled: true };
|
|
797
816
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
817
|
+
// resolveSessionRef accepts exact ID, prefix match (like git),
|
|
818
|
+
// "last"/"latest" shortcut, and strips angle/quote wrappers
|
|
819
|
+
// (users pasting /resume <id> from the help text). This
|
|
820
|
+
// replaces the exact-only lookup that broke for everyone
|
|
821
|
+
// who copy-pasted from /sessions's previously-truncated
|
|
822
|
+
// display.
|
|
823
|
+
const ref = resolveSessionRef(args);
|
|
824
|
+
if ('error' in ref) {
|
|
825
|
+
console.log(chalk.red(` ${ref.error}`));
|
|
826
|
+
if (ref.candidates && ref.candidates.length > 0) {
|
|
827
|
+
console.log(chalk.dim(' Matching candidates:'));
|
|
828
|
+
for (const c of ref.candidates.slice(0, 8))
|
|
829
|
+
console.log(chalk.dim(` ${c}`));
|
|
830
|
+
}
|
|
801
831
|
return { handled: true };
|
|
802
832
|
}
|
|
833
|
+
const loaded = loadSession(ref.id);
|
|
834
|
+
if (!loaded) {
|
|
835
|
+
// Resolver said the file exists but loadSession returned
|
|
836
|
+
// null — corrupt JSON. Surface clearly rather than silently
|
|
837
|
+
// looking like a not-found.
|
|
838
|
+
console.log(chalk.red(` Session ${ref.id} resolved but its JSON is corrupt.`));
|
|
839
|
+
return { handled: true };
|
|
840
|
+
}
|
|
841
|
+
// ── Restore live state ─────────────────────────────────
|
|
842
|
+
// Previously /resume only returned newMessages and left the
|
|
843
|
+
// live config / mode / perm at whatever the current REPL
|
|
844
|
+
// happened to be in. That breaks the contract: a yolo
|
|
845
|
+
// session resumed in ask mode means the model needs
|
|
846
|
+
// permission for every tool call the original session ran
|
|
847
|
+
// freely. Now we mutate the live config + session + mode
|
|
848
|
+
// objects in place so the resumed state takes effect
|
|
849
|
+
// immediately. Fields absent from old (pre-v1.30.2) session
|
|
850
|
+
// files leave the current value untouched.
|
|
851
|
+
const changes = [];
|
|
852
|
+
if (loaded.model && loaded.model !== config.model) {
|
|
853
|
+
changes.push(`model ${config.model} → ${loaded.model}`);
|
|
854
|
+
config.model = loaded.model;
|
|
855
|
+
}
|
|
856
|
+
if (loaded.provider && loaded.provider !== config.provider) {
|
|
857
|
+
changes.push(`provider ${config.provider} → ${loaded.provider}`);
|
|
858
|
+
config.provider = loaded.provider;
|
|
859
|
+
}
|
|
860
|
+
if (loaded.permissionMode && loaded.permissionMode !== config.permissionMode) {
|
|
861
|
+
changes.push(`perms ${config.permissionMode} → ${loaded.permissionMode}`);
|
|
862
|
+
config.permissionMode = loaded.permissionMode;
|
|
863
|
+
}
|
|
864
|
+
// mode.current is a { current: Mode } box (shared closure
|
|
865
|
+
// ref) so mutation propagates to the hotkey listener +
|
|
866
|
+
// prompt rendering automatically.
|
|
867
|
+
if (loaded.mode && loaded.mode !== mode.current) {
|
|
868
|
+
changes.push(`mode ${mode.current} → ${loaded.mode}`);
|
|
869
|
+
mode.current = loaded.mode;
|
|
870
|
+
}
|
|
871
|
+
// Adopt the resumed session's identity so subsequent
|
|
872
|
+
// autosaves write to its file, not the current session's.
|
|
873
|
+
session.id = loaded.id;
|
|
874
|
+
session.name = loaded.name;
|
|
875
|
+
session.createdAt = loaded.createdAt;
|
|
876
|
+
session.updatedAt = loaded.updatedAt;
|
|
877
|
+
session.tokenCount = loaded.tokenCount;
|
|
878
|
+
session.turnCount = loaded.turnCount;
|
|
879
|
+
// Persist the config update so the change survives restart.
|
|
880
|
+
saveConfig(config);
|
|
803
881
|
console.log(chalk.green(` Resumed: ${loaded.name} (${loaded.messages.length} messages)`));
|
|
882
|
+
if (changes.length > 0) {
|
|
883
|
+
console.log(chalk.yellow(` Restored config: ${changes.join(', ')}`));
|
|
884
|
+
}
|
|
885
|
+
else {
|
|
886
|
+
console.log(chalk.dim(' Config unchanged (already matches the saved session).'));
|
|
887
|
+
}
|
|
804
888
|
return { handled: true, newMessages: loaded.messages };
|
|
805
889
|
}
|
|
806
|
-
case '/delete':
|
|
807
|
-
if (args
|
|
808
|
-
console.log(chalk.
|
|
890
|
+
case '/delete': {
|
|
891
|
+
if (!args.trim()) {
|
|
892
|
+
console.log(chalk.yellow(' Usage: /delete <session-id> | <prefix> | last'));
|
|
893
|
+
return { handled: true };
|
|
894
|
+
}
|
|
895
|
+
const ref = resolveSessionRef(args);
|
|
896
|
+
if ('error' in ref) {
|
|
897
|
+
console.log(chalk.yellow(` ${ref.error}`));
|
|
898
|
+
if (ref.candidates && ref.candidates.length > 0) {
|
|
899
|
+
console.log(chalk.dim(' Matching candidates:'));
|
|
900
|
+
for (const c of ref.candidates.slice(0, 8))
|
|
901
|
+
console.log(chalk.dim(` ${c}`));
|
|
902
|
+
}
|
|
903
|
+
return { handled: true };
|
|
904
|
+
}
|
|
905
|
+
if (deleteSession(ref.id)) {
|
|
906
|
+
console.log(chalk.green(` Deleted session: ${ref.id}`));
|
|
809
907
|
}
|
|
810
908
|
else {
|
|
811
|
-
console.log(chalk.yellow(`
|
|
909
|
+
console.log(chalk.yellow(` Could not delete session: ${ref.id}`));
|
|
812
910
|
}
|
|
813
911
|
return { handled: true };
|
|
912
|
+
}
|
|
814
913
|
// ── Git ───────────────────────────────────────────
|
|
815
914
|
case '/commit': {
|
|
816
915
|
const prompt = buildCommitPrompt(process.cwd());
|
|
@@ -2955,6 +3054,20 @@ async function main() {
|
|
|
2955
3054
|
// with the per-chain timer printed after each model response (see runQuery),
|
|
2956
3055
|
// gives both "how long am I here" and "how long was that last response."
|
|
2957
3056
|
const sessionStartMs = new Date(session.createdAt).getTime();
|
|
3057
|
+
// autoSave wrapper that snapshots the live config + mode + perm
|
|
3058
|
+
// into the session before writing. Without this, the saved
|
|
3059
|
+
// session captures only the values the Session object was created
|
|
3060
|
+
// with — every /perm, /model, /mode change made during the
|
|
3061
|
+
// session is lost on /resume. Used by all in-process autosave
|
|
3062
|
+
// callsites; /save in the slash-command handler passes the same
|
|
3063
|
+
// snapshot inline (it doesn't have access to this closure).
|
|
3064
|
+
const saveWithSnapshot = () => autoSave(session, messages, {
|
|
3065
|
+
model: config.model,
|
|
3066
|
+
provider: config.provider,
|
|
3067
|
+
mode: mode.current,
|
|
3068
|
+
permissionMode: config.permissionMode,
|
|
3069
|
+
cwd: process.cwd(),
|
|
3070
|
+
});
|
|
2958
3071
|
// Main REPL loop
|
|
2959
3072
|
while (true) {
|
|
2960
3073
|
let input;
|
|
@@ -3101,14 +3214,14 @@ async function main() {
|
|
|
3101
3214
|
catch (e) {
|
|
3102
3215
|
console.log(chalk.red(` Swarm failed: ${e instanceof Error ? e.message : e}`));
|
|
3103
3216
|
}
|
|
3104
|
-
await
|
|
3217
|
+
await saveWithSnapshot();
|
|
3105
3218
|
continue;
|
|
3106
3219
|
}
|
|
3107
3220
|
else {
|
|
3108
3221
|
messages.push({ role: 'user', content: result.injectPrompt });
|
|
3109
3222
|
}
|
|
3110
3223
|
await runQuery({ config, messages, cwd: process.cwd(), rl, sessionId: session.id, mode: mode.current });
|
|
3111
|
-
await
|
|
3224
|
+
await saveWithSnapshot();
|
|
3112
3225
|
continue;
|
|
3113
3226
|
}
|
|
3114
3227
|
if (result.handled)
|
|
@@ -3136,7 +3249,7 @@ async function main() {
|
|
|
3136
3249
|
mode: mode.current,
|
|
3137
3250
|
});
|
|
3138
3251
|
// Auto-save session
|
|
3139
|
-
await
|
|
3252
|
+
await saveWithSnapshot();
|
|
3140
3253
|
// Strategic compaction check
|
|
3141
3254
|
const compactionHint = shouldSuggestCompaction(messages, 0);
|
|
3142
3255
|
if (compactionHint) {
|
|
@@ -3147,7 +3260,7 @@ async function main() {
|
|
|
3147
3260
|
onSessionEnd(session.id, messages, process.cwd());
|
|
3148
3261
|
await runHooks({ event: 'SessionStop', sessionId: session.id, cwd: process.cwd(), permissionMode: config.permissionMode });
|
|
3149
3262
|
// Final save
|
|
3150
|
-
await
|
|
3263
|
+
await saveWithSnapshot();
|
|
3151
3264
|
console.log(chalk.dim(`\nSession saved: ${session.id}`));
|
|
3152
3265
|
console.log(chalk.dim('Goodbye!\n'));
|
|
3153
3266
|
rl.close();
|