@parall/claude-agent 1.29.3 → 1.31.0
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.js +2 -2
- package/dist/dispatch.d.ts +1 -0
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +14 -10
- package/dist/index.js +41 -9
- package/dist/session-manager.d.ts +1 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +9 -0
- package/package.json +4 -4
- package/src/config.ts +2 -2
- package/src/dispatch.ts +15 -10
- package/src/index.ts +39 -9
- package/src/session-manager.ts +9 -0
package/dist/config.js
CHANGED
|
@@ -23,8 +23,8 @@ export function resolveClaudeAgentConfig(env = process.env) {
|
|
|
23
23
|
const apiKey = requireEnv(env, "PRLL_API_KEY");
|
|
24
24
|
const orgId = requireEnv(env, "PRLL_ORG_ID");
|
|
25
25
|
const claudeHome = resolvePath(env.PRLL_CLAUDE_HOME?.trim() || env.HOME || os.homedir());
|
|
26
|
-
const stateDir = resolvePath(env.
|
|
27
|
-
const workspaceDir = resolvePath(env.
|
|
26
|
+
const stateDir = resolvePath(env.PRLL_STATE_DIR?.trim() || path.join(claudeHome, ".parall-agent"));
|
|
27
|
+
const workspaceDir = resolvePath(env.PRLL_WORKSPACE_DIR?.trim() || path.join(stateDir, "workspace"));
|
|
28
28
|
const additionalDirs = parseList(env.PRLL_CLAUDE_ADD_DIRS).map(resolvePath);
|
|
29
29
|
return {
|
|
30
30
|
apiUrl,
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
35
35
|
getSessionHistoryPath(sessionKey: string): string | undefined;
|
|
36
36
|
forkSession({ sessionKey }: ForkOpts): import("@parall/agent-core").ForkSessionHandle | null;
|
|
37
37
|
cleanupFork({ fork }: CleanupForkOpts): void;
|
|
38
|
+
resetProcesses(): void;
|
|
38
39
|
shutdown(): Promise<void>;
|
|
39
40
|
private static readonly STEER_TURN_TIMEOUT_MS;
|
|
40
41
|
/**
|
package/dist/dispatch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EAER,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,oBAAoB,EAErB,MAAM,sBAAsB,CAAC;AAE9B,KAAK,wBAAwB,GAAG,IAAI,CAClC,iBAAiB,EACf,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,OAAO,GACP,gBAAgB,GAChB,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,oBAAoB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3D,iDAAiD;IACjD,wBAAwB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3D,CAAC;AAgBF,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,CAAC,UAAU,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,EAChC,IAAI,EAAE;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,MAAM,CAAC,UAAU,CAuBnB;AAiBD,qBAAa,iBAAkB,YAAW,eAAe;IAO3C,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmC;IAC7D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6B;IAC/D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,YAAY,CAAqB;gBAEZ,IAAI,EAAE,wBAAwB;IAI3D,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAAwB;IAC9D,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAA8B;IAErE,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAY7E,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAchE,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAiCxG,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIvD,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ7D,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ;IAIpC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EAER,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,oBAAoB,EAErB,MAAM,sBAAsB,CAAC;AAE9B,KAAK,wBAAwB,GAAG,IAAI,CAClC,iBAAiB,EACf,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,OAAO,GACP,gBAAgB,GAChB,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,oBAAoB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3D,iDAAiD;IACjD,wBAAwB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3D,CAAC;AAgBF,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,CAAC,UAAU,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,EAChC,IAAI,EAAE;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,MAAM,CAAC,UAAU,CAuBnB;AAiBD,qBAAa,iBAAkB,YAAW,eAAe;IAO3C,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmC;IAC7D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6B;IAC/D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,YAAY,CAAqB;gBAEZ,IAAI,EAAE,wBAAwB;IAI3D,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAAwB;IAC9D,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAA8B;IAErE,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAY7E,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAchE,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAiCxG,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIvD,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ7D,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ;IAIpC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAQrC,cAAc,IAAI,IAAI;IAQhB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAU;IAEvD;;;;;;;;;OASG;YACY,gBAAgB;YAsDhB,OAAO;IA2GtB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,YAAY;IA+CpB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,SAAS;IAwCjB,OAAO,CAAC,uBAAuB;CAkBhC"}
|
package/dist/dispatch.js
CHANGED
|
@@ -92,7 +92,7 @@ export class ClaudeCodeAdapter {
|
|
|
92
92
|
const pending = this.pendingInjections.get(sessionKey) ?? 0;
|
|
93
93
|
if (pending > 0) {
|
|
94
94
|
this.pendingInjections.delete(sessionKey);
|
|
95
|
-
context.log?.info?.(`
|
|
95
|
+
context.log?.info?.(`consuming ${pending} steer turn(s)`);
|
|
96
96
|
for (let i = 0; i < pending; i++) {
|
|
97
97
|
yield* this.consumeSteerTurn(sessionKey, context.log);
|
|
98
98
|
}
|
|
@@ -109,7 +109,7 @@ export class ClaudeCodeAdapter {
|
|
|
109
109
|
releasePreparedAttachments = pinLocalAttachmentPaths(prepared.attachments.images);
|
|
110
110
|
}
|
|
111
111
|
catch (err) {
|
|
112
|
-
context.log?.warn?.(`
|
|
112
|
+
context.log?.warn?.(`failed to prepare local attachments: ${String(err)}`);
|
|
113
113
|
}
|
|
114
114
|
try {
|
|
115
115
|
yield* this.runTurn(sessionKey, promptBody, context.log);
|
|
@@ -139,12 +139,16 @@ export class ClaudeCodeAdapter {
|
|
|
139
139
|
}
|
|
140
140
|
this.opts.sessionManager.cleanupFork(fork.sessionKey);
|
|
141
141
|
}
|
|
142
|
-
|
|
143
|
-
this.shuttingDown = true;
|
|
142
|
+
resetProcesses() {
|
|
144
143
|
for (const [sessionKey, state] of this.processes) {
|
|
145
144
|
this.killProcess(sessionKey, state);
|
|
146
145
|
}
|
|
147
146
|
this.processes.clear();
|
|
147
|
+
this.pendingInjections.clear();
|
|
148
|
+
}
|
|
149
|
+
async shutdown() {
|
|
150
|
+
this.shuttingDown = true;
|
|
151
|
+
this.resetProcesses();
|
|
148
152
|
await this.opts.sessionManager.shutdownAll();
|
|
149
153
|
}
|
|
150
154
|
static STEER_TURN_TIMEOUT_MS = 10_000;
|
|
@@ -161,7 +165,7 @@ export class ClaudeCodeAdapter {
|
|
|
161
165
|
async *consumeSteerTurn(sessionKey, log) {
|
|
162
166
|
const state = this.processes.get(sessionKey);
|
|
163
167
|
if (!state || state.done) {
|
|
164
|
-
throw new Error("
|
|
168
|
+
throw new Error("process dead during steer consumption");
|
|
165
169
|
}
|
|
166
170
|
const groupKey = randomUUID();
|
|
167
171
|
const parserNext = state.parser.next();
|
|
@@ -171,7 +175,7 @@ export class ClaudeCodeAdapter {
|
|
|
171
175
|
]);
|
|
172
176
|
if (firstRead.kind === "timeout") {
|
|
173
177
|
state.steerReadPending = parserNext;
|
|
174
|
-
log?.info?.(`
|
|
178
|
+
log?.info?.(`steer turn timeout — steer was incorporated into previous turn`);
|
|
175
179
|
return;
|
|
176
180
|
}
|
|
177
181
|
let next = firstRead.result;
|
|
@@ -180,7 +184,7 @@ export class ClaudeCodeAdapter {
|
|
|
180
184
|
if (next.done) {
|
|
181
185
|
state.done = true;
|
|
182
186
|
this.processes.delete(sessionKey);
|
|
183
|
-
throw new Error("
|
|
187
|
+
throw new Error("process exited while consuming steer turn");
|
|
184
188
|
}
|
|
185
189
|
const parsed = next.value;
|
|
186
190
|
if (parsed.type === "session_id") {
|
|
@@ -263,7 +267,7 @@ export class ClaudeCodeAdapter {
|
|
|
263
267
|
const detail = state.handle.stderrChunks.join("").trim();
|
|
264
268
|
const exit = await state.handle.exitPromise.catch(() => ({ code: null, signal: null }));
|
|
265
269
|
if (detail) {
|
|
266
|
-
log?.warn?.(`
|
|
270
|
+
log?.warn?.(`subprocess stderr: ${detail}`);
|
|
267
271
|
}
|
|
268
272
|
if (!sawError) {
|
|
269
273
|
yield {
|
|
@@ -308,7 +312,7 @@ export class ClaudeCodeAdapter {
|
|
|
308
312
|
}
|
|
309
313
|
ensureProcess(sessionKey, log) {
|
|
310
314
|
if (this.shuttingDown) {
|
|
311
|
-
throw new Error("
|
|
315
|
+
throw new Error("adapter shutting down, refusing new process");
|
|
312
316
|
}
|
|
313
317
|
const existing = this.processes.get(sessionKey);
|
|
314
318
|
if (existing && !existing.done) {
|
|
@@ -333,7 +337,7 @@ export class ClaudeCodeAdapter {
|
|
|
333
337
|
spawnProcess(sessionKey, log) {
|
|
334
338
|
const args = this.buildArgs(sessionKey);
|
|
335
339
|
const env = buildSpawnEnv(process.env, this.opts.claudeHome, this.buildPlaceholderContext(sessionKey), { allowApiKey: this.opts.allowApiKey, effortLevel: this._effortLevel });
|
|
336
|
-
log?.info(`
|
|
340
|
+
log?.info(`spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || "default"}, effort=${this._effortLevel || "default"}, mode=${this.opts.permissionMode})`);
|
|
337
341
|
const proc = spawn(IS_WIN32 ? quoteWin32Arg(this.opts.claudeBin) : this.opts.claudeBin, IS_WIN32 ? args.map(quoteWin32Arg) : args, {
|
|
338
342
|
cwd: this.opts.workspaceDir,
|
|
339
343
|
env,
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as os from "node:os";
|
|
3
|
-
import { ParallAgentGateway, createPlatformConfigManager, createLogger, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
3
|
+
import { ParallAgentGateway, createPlatformConfigManager, createLogger, childLogger, isPlatformManagedProfile, parseShutdownDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource } from "@parall/agent-core";
|
|
4
4
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
5
5
|
import { buildClaudeRuntimeKey, contextFilePathForSession, resolveClaudeAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
|
|
6
6
|
import { ClaudeCodeAdapter } from "./dispatch.js";
|
|
7
7
|
import { ClaudeSessionManager } from "./session-manager.js";
|
|
8
8
|
import { ensureClaudeWorkspace } from "./workspace.js";
|
|
9
9
|
const log = createLogger("claude-agent");
|
|
10
|
+
let activeLog = log;
|
|
10
11
|
async function getAgentMeWithLegacyFallback(client, orgId) {
|
|
11
12
|
try {
|
|
12
13
|
return await client.getAgentMe(orgId);
|
|
@@ -19,7 +20,28 @@ async function getAgentMeWithLegacyFallback(client, orgId) {
|
|
|
19
20
|
return { ...user, agent_profile: null };
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
function resolveProviderEnv() {
|
|
24
|
+
const pc = parseProviderConfig(process.env);
|
|
25
|
+
if (!pc)
|
|
26
|
+
return;
|
|
27
|
+
clearAllProviderCreds(process.env);
|
|
28
|
+
const source = llmSource(pc);
|
|
29
|
+
if (source === "parall") {
|
|
30
|
+
process.env.ANTHROPIC_AUTH_TOKEN = process.env.PRLL_API_KEY;
|
|
31
|
+
process.env.ANTHROPIC_BASE_URL = `${process.env.PRLL_API_URL}/api/llm`;
|
|
32
|
+
process.env.PRLL_CLAUDE_ALLOW_API_KEY = "1";
|
|
33
|
+
}
|
|
34
|
+
else if (source === "custom") {
|
|
35
|
+
if (pc.anthropic_auth_token) {
|
|
36
|
+
process.env.ANTHROPIC_AUTH_TOKEN = pc.anthropic_auth_token;
|
|
37
|
+
process.env.PRLL_CLAUDE_ALLOW_API_KEY = "1";
|
|
38
|
+
}
|
|
39
|
+
if (pc.anthropic_base_url)
|
|
40
|
+
process.env.ANTHROPIC_BASE_URL = pc.anthropic_base_url;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
22
43
|
async function main() {
|
|
44
|
+
resolveProviderEnv();
|
|
23
45
|
const config = resolveClaudeAgentConfig(process.env);
|
|
24
46
|
if (config.allowApiKey &&
|
|
25
47
|
(process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN)) {
|
|
@@ -35,20 +57,22 @@ async function main() {
|
|
|
35
57
|
});
|
|
36
58
|
const me = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
37
59
|
const agentUserId = me.id;
|
|
38
|
-
|
|
60
|
+
const agentLog = childLogger(log, agentUserId);
|
|
61
|
+
activeLog = agentLog;
|
|
62
|
+
ensureClaudeWorkspace(config.workspaceDir, agentLog, {
|
|
39
63
|
userId: agentUserId,
|
|
40
64
|
displayName: me.display_name,
|
|
41
65
|
description: me.agent_profile?.description ?? undefined,
|
|
42
66
|
});
|
|
43
67
|
const runtimeKey = config.runtimeKey || buildClaudeRuntimeKey(agentUserId);
|
|
44
68
|
const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
|
|
45
|
-
const sessionManager = new ClaudeSessionManager(runtimeKey, sessionStateFilePath,
|
|
69
|
+
const sessionManager = new ClaudeSessionManager(runtimeKey, sessionStateFilePath, agentLog);
|
|
46
70
|
const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
|
|
47
71
|
const ws = new ParallWs({
|
|
48
72
|
getTicket: () => client.getWsTicket(),
|
|
49
73
|
wsUrl: resolvedWsUrl,
|
|
50
74
|
});
|
|
51
|
-
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log });
|
|
75
|
+
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log: agentLog });
|
|
52
76
|
const platformDefaults = await configMgr.fetch();
|
|
53
77
|
let platformManaged = isPlatformManagedProfile(me.agent_profile);
|
|
54
78
|
const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
|
|
@@ -56,9 +80,9 @@ async function main() {
|
|
|
56
80
|
? (platformDefaults.thinkingEffort ?? (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || undefined))
|
|
57
81
|
: (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || undefined);
|
|
58
82
|
if (platformDefaults.model)
|
|
59
|
-
|
|
83
|
+
agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
60
84
|
if (platformDefaults.thinkingEffort)
|
|
61
|
-
|
|
85
|
+
agentLog.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
62
86
|
const adapter = new ClaudeCodeAdapter({
|
|
63
87
|
claudeBin: config.claudeBin,
|
|
64
88
|
claudeHome: config.claudeHome,
|
|
@@ -103,7 +127,7 @@ async function main() {
|
|
|
103
127
|
claude_home: config.claudeHome,
|
|
104
128
|
},
|
|
105
129
|
dispatchAdapter: adapter,
|
|
106
|
-
log,
|
|
130
|
+
log: agentLog,
|
|
107
131
|
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
108
132
|
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
109
133
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
@@ -132,13 +156,21 @@ async function main() {
|
|
|
132
156
|
// onBeforeDisconnect to close stdin and SIGTERM any survivors after
|
|
133
157
|
// in-flight drains finish.
|
|
134
158
|
onBeforeDisconnect: () => adapter.shutdown(),
|
|
159
|
+
onNewSession: async () => {
|
|
160
|
+
sessionManager.clearMainSession();
|
|
161
|
+
adapter.resetProcesses();
|
|
162
|
+
},
|
|
163
|
+
onSessionStale: () => {
|
|
164
|
+
sessionManager.clearMainSession();
|
|
165
|
+
adapter.resetProcesses();
|
|
166
|
+
},
|
|
135
167
|
});
|
|
136
168
|
const abortController = new AbortController();
|
|
137
169
|
const abort = () => abortController.abort();
|
|
138
170
|
process.on("SIGINT", abort);
|
|
139
171
|
process.on("SIGTERM", abort);
|
|
140
172
|
try {
|
|
141
|
-
|
|
173
|
+
agentLog.info(`starting self-hosted Claude runtime for ${me.display_name} (${agentUserId})`);
|
|
142
174
|
await gateway.run(abortController.signal);
|
|
143
175
|
}
|
|
144
176
|
finally {
|
|
@@ -147,6 +179,6 @@ async function main() {
|
|
|
147
179
|
}
|
|
148
180
|
}
|
|
149
181
|
main().catch((err) => {
|
|
150
|
-
|
|
182
|
+
activeLog.error(`fatal: ${String(err)}`);
|
|
151
183
|
process.exitCode = 1;
|
|
152
184
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,KAAK,0BAA0B,GAAG;IAChC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,8BAA8B,CAAC;IACrC,WAAW,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7E,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,qBAAa,oBAAoB;IAM7B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAP1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAChE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;gBAGjD,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,0BAA0B,YAAA;IAKtD,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAU3C,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAQrD,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAYrE,WAAW,CAAC,UAAU,EAAE,MAAM;IAU9B,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAI/D,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB;IAsB/D,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,mBAAmB;IAO7D,qEAAqE;IACrE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE5C,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;YAQpB,WAAW;IAuCzB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAuBvB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,OAAO;CAahB"}
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,KAAK,0BAA0B,GAAG;IAChC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,8BAA8B,CAAC;IACrC,WAAW,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7E,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,qBAAa,oBAAoB;IAM7B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAP1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAChE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;gBAGjD,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,0BAA0B,YAAA;IAKtD,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAU3C,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAQrD,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAYrE,WAAW,CAAC,UAAU,EAAE,MAAM;IAU9B,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAI/D,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB;IAsB/D,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,mBAAmB;IAO7D,qEAAqE;IACrE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE5C,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;YAQpB,WAAW;IAuCzB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAuBvB,OAAO,CAAC,WAAW;IAqBnB,gBAAgB;IAShB,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,OAAO;CAahB"}
|
package/dist/session-manager.js
CHANGED
|
@@ -164,6 +164,15 @@ export class ClaudeSessionManager {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
+
clearMainSession() {
|
|
168
|
+
this.sessionIds.delete(this.mainSessionKey);
|
|
169
|
+
try {
|
|
170
|
+
fs.unlinkSync(this.stateFilePath);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// File may not exist.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
167
176
|
restore() {
|
|
168
177
|
try {
|
|
169
178
|
const raw = fs.readFileSync(this.stateFilePath, "utf8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/claude-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.31.0",
|
|
4
4
|
"description": "Claude Code bridge runtime for self-hosted Parall agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"src"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@parall/
|
|
29
|
-
"@parall/cli": "1.
|
|
30
|
-
"@parall/
|
|
28
|
+
"@parall/sdk": "1.31.0",
|
|
29
|
+
"@parall/cli": "1.31.0",
|
|
30
|
+
"@parall/agent-core": "1.31.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.0.0",
|
package/src/config.ts
CHANGED
|
@@ -46,8 +46,8 @@ export function resolveClaudeAgentConfig(env: NodeJS.ProcessEnv = process.env):
|
|
|
46
46
|
const apiKey = requireEnv(env, "PRLL_API_KEY");
|
|
47
47
|
const orgId = requireEnv(env, "PRLL_ORG_ID");
|
|
48
48
|
const claudeHome = resolvePath(env.PRLL_CLAUDE_HOME?.trim() || env.HOME || os.homedir());
|
|
49
|
-
const stateDir = resolvePath(env.
|
|
50
|
-
const workspaceDir = resolvePath(env.
|
|
49
|
+
const stateDir = resolvePath(env.PRLL_STATE_DIR?.trim() || path.join(claudeHome, ".parall-agent"));
|
|
50
|
+
const workspaceDir = resolvePath(env.PRLL_WORKSPACE_DIR?.trim() || path.join(stateDir, "workspace"));
|
|
51
51
|
const additionalDirs = parseList(env.PRLL_CLAUDE_ADD_DIRS).map(resolvePath);
|
|
52
52
|
|
|
53
53
|
return {
|
package/src/dispatch.ts
CHANGED
|
@@ -150,7 +150,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
150
150
|
const pending = this.pendingInjections.get(sessionKey) ?? 0;
|
|
151
151
|
if (pending > 0) {
|
|
152
152
|
this.pendingInjections.delete(sessionKey);
|
|
153
|
-
context.log?.info?.(`
|
|
153
|
+
context.log?.info?.(`consuming ${pending} steer turn(s)`);
|
|
154
154
|
for (let i = 0; i < pending; i++) {
|
|
155
155
|
yield* this.consumeSteerTurn(sessionKey, context.log);
|
|
156
156
|
}
|
|
@@ -168,7 +168,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
168
168
|
releasePreparedAttachments = pinLocalAttachmentPaths(prepared.attachments.images);
|
|
169
169
|
} catch (err) {
|
|
170
170
|
context.log?.warn?.(
|
|
171
|
-
`
|
|
171
|
+
`failed to prepare local attachments: ${String(err)}`,
|
|
172
172
|
);
|
|
173
173
|
}
|
|
174
174
|
|
|
@@ -203,12 +203,17 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
203
203
|
this.opts.sessionManager.cleanupFork(fork.sessionKey);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
this.shuttingDown = true;
|
|
206
|
+
resetProcesses(): void {
|
|
208
207
|
for (const [sessionKey, state] of this.processes) {
|
|
209
208
|
this.killProcess(sessionKey, state);
|
|
210
209
|
}
|
|
211
210
|
this.processes.clear();
|
|
211
|
+
this.pendingInjections.clear();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async shutdown(): Promise<void> {
|
|
215
|
+
this.shuttingDown = true;
|
|
216
|
+
this.resetProcesses();
|
|
212
217
|
await this.opts.sessionManager.shutdownAll();
|
|
213
218
|
}
|
|
214
219
|
|
|
@@ -230,7 +235,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
230
235
|
): AsyncGenerator<RuntimeEvent> {
|
|
231
236
|
const state = this.processes.get(sessionKey);
|
|
232
237
|
if (!state || state.done) {
|
|
233
|
-
throw new Error("
|
|
238
|
+
throw new Error("process dead during steer consumption");
|
|
234
239
|
}
|
|
235
240
|
|
|
236
241
|
const groupKey = randomUUID();
|
|
@@ -245,7 +250,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
245
250
|
|
|
246
251
|
if (firstRead.kind === "timeout") {
|
|
247
252
|
state.steerReadPending = parserNext;
|
|
248
|
-
log?.info?.(`
|
|
253
|
+
log?.info?.(`steer turn timeout — steer was incorporated into previous turn`);
|
|
249
254
|
return;
|
|
250
255
|
}
|
|
251
256
|
|
|
@@ -255,7 +260,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
255
260
|
if (next.done) {
|
|
256
261
|
state.done = true;
|
|
257
262
|
this.processes.delete(sessionKey);
|
|
258
|
-
throw new Error("
|
|
263
|
+
throw new Error("process exited while consuming steer turn");
|
|
259
264
|
}
|
|
260
265
|
const parsed = next.value;
|
|
261
266
|
|
|
@@ -334,7 +339,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
334
339
|
const detail = state.handle.stderrChunks.join("").trim();
|
|
335
340
|
const exit = await state.handle.exitPromise.catch(() => ({ code: null, signal: null } as const));
|
|
336
341
|
if (detail) {
|
|
337
|
-
log?.warn?.(`
|
|
342
|
+
log?.warn?.(`subprocess stderr: ${detail}`);
|
|
338
343
|
}
|
|
339
344
|
if (!sawError) {
|
|
340
345
|
yield {
|
|
@@ -387,7 +392,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
387
392
|
|
|
388
393
|
private ensureProcess(sessionKey: string, log: GatewayLogger | undefined): ProcessState {
|
|
389
394
|
if (this.shuttingDown) {
|
|
390
|
-
throw new Error("
|
|
395
|
+
throw new Error("adapter shutting down, refusing new process");
|
|
391
396
|
}
|
|
392
397
|
|
|
393
398
|
const existing = this.processes.get(sessionKey);
|
|
@@ -420,7 +425,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
420
425
|
);
|
|
421
426
|
|
|
422
427
|
log?.info(
|
|
423
|
-
`
|
|
428
|
+
`spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || "default"}, effort=${this._effortLevel || "default"}, mode=${this.opts.permissionMode})`,
|
|
424
429
|
);
|
|
425
430
|
|
|
426
431
|
const proc = spawn(
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
|
-
import { ParallAgentGateway, createPlatformConfigManager, createLogger, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
|
+
import { ParallAgentGateway, createPlatformConfigManager, createLogger, childLogger, isPlatformManagedProfile, parseShutdownDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource } from "@parall/agent-core";
|
|
5
5
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
6
6
|
import {
|
|
7
7
|
buildClaudeRuntimeKey,
|
|
@@ -16,6 +16,7 @@ import { ClaudeSessionManager } from "./session-manager.js";
|
|
|
16
16
|
import { ensureClaudeWorkspace } from "./workspace.js";
|
|
17
17
|
|
|
18
18
|
const log = createLogger("claude-agent");
|
|
19
|
+
let activeLog = log;
|
|
19
20
|
|
|
20
21
|
async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string) {
|
|
21
22
|
try {
|
|
@@ -29,7 +30,26 @@ async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string)
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function resolveProviderEnv(): void {
|
|
34
|
+
const pc = parseProviderConfig(process.env);
|
|
35
|
+
if (!pc) return;
|
|
36
|
+
clearAllProviderCreds(process.env);
|
|
37
|
+
const source = llmSource(pc);
|
|
38
|
+
if (source === "parall") {
|
|
39
|
+
process.env.ANTHROPIC_AUTH_TOKEN = process.env.PRLL_API_KEY;
|
|
40
|
+
process.env.ANTHROPIC_BASE_URL = `${process.env.PRLL_API_URL}/api/llm`;
|
|
41
|
+
process.env.PRLL_CLAUDE_ALLOW_API_KEY = "1";
|
|
42
|
+
} else if (source === "custom") {
|
|
43
|
+
if (pc.anthropic_auth_token) {
|
|
44
|
+
process.env.ANTHROPIC_AUTH_TOKEN = pc.anthropic_auth_token;
|
|
45
|
+
process.env.PRLL_CLAUDE_ALLOW_API_KEY = "1";
|
|
46
|
+
}
|
|
47
|
+
if (pc.anthropic_base_url) process.env.ANTHROPIC_BASE_URL = pc.anthropic_base_url;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
32
51
|
async function main() {
|
|
52
|
+
resolveProviderEnv();
|
|
33
53
|
const config = resolveClaudeAgentConfig(process.env);
|
|
34
54
|
|
|
35
55
|
if (
|
|
@@ -52,7 +72,9 @@ async function main() {
|
|
|
52
72
|
|
|
53
73
|
const me = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
54
74
|
const agentUserId = me.id;
|
|
55
|
-
|
|
75
|
+
const agentLog = childLogger(log, agentUserId);
|
|
76
|
+
activeLog = agentLog;
|
|
77
|
+
ensureClaudeWorkspace(config.workspaceDir, agentLog, {
|
|
56
78
|
userId: agentUserId,
|
|
57
79
|
displayName: me.display_name,
|
|
58
80
|
description: me.agent_profile?.description ?? undefined,
|
|
@@ -62,7 +84,7 @@ async function main() {
|
|
|
62
84
|
const sessionManager = new ClaudeSessionManager(
|
|
63
85
|
runtimeKey,
|
|
64
86
|
sessionStateFilePath,
|
|
65
|
-
|
|
87
|
+
agentLog,
|
|
66
88
|
);
|
|
67
89
|
|
|
68
90
|
const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
|
|
@@ -71,15 +93,15 @@ async function main() {
|
|
|
71
93
|
wsUrl: resolvedWsUrl,
|
|
72
94
|
});
|
|
73
95
|
|
|
74
|
-
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log });
|
|
96
|
+
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log: agentLog });
|
|
75
97
|
const platformDefaults = await configMgr.fetch();
|
|
76
98
|
let platformManaged = isPlatformManagedProfile(me.agent_profile);
|
|
77
99
|
const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
|
|
78
100
|
const resolvedEffort = platformManaged
|
|
79
101
|
? (platformDefaults.thinkingEffort ?? (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || undefined))
|
|
80
102
|
: (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || undefined);
|
|
81
|
-
if (platformDefaults.model)
|
|
82
|
-
if (platformDefaults.thinkingEffort)
|
|
103
|
+
if (platformDefaults.model) agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
104
|
+
if (platformDefaults.thinkingEffort) agentLog.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
83
105
|
|
|
84
106
|
const adapter = new ClaudeCodeAdapter({
|
|
85
107
|
claudeBin: config.claudeBin,
|
|
@@ -126,7 +148,7 @@ async function main() {
|
|
|
126
148
|
claude_home: config.claudeHome,
|
|
127
149
|
},
|
|
128
150
|
dispatchAdapter: adapter,
|
|
129
|
-
log,
|
|
151
|
+
log: agentLog,
|
|
130
152
|
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
131
153
|
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
132
154
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
@@ -155,6 +177,14 @@ async function main() {
|
|
|
155
177
|
// onBeforeDisconnect to close stdin and SIGTERM any survivors after
|
|
156
178
|
// in-flight drains finish.
|
|
157
179
|
onBeforeDisconnect: () => adapter.shutdown(),
|
|
180
|
+
onNewSession: async () => {
|
|
181
|
+
sessionManager.clearMainSession();
|
|
182
|
+
adapter.resetProcesses();
|
|
183
|
+
},
|
|
184
|
+
onSessionStale: () => {
|
|
185
|
+
sessionManager.clearMainSession();
|
|
186
|
+
adapter.resetProcesses();
|
|
187
|
+
},
|
|
158
188
|
});
|
|
159
189
|
|
|
160
190
|
const abortController = new AbortController();
|
|
@@ -163,7 +193,7 @@ async function main() {
|
|
|
163
193
|
process.on("SIGTERM", abort);
|
|
164
194
|
|
|
165
195
|
try {
|
|
166
|
-
|
|
196
|
+
agentLog.info(`starting self-hosted Claude runtime for ${me.display_name} (${agentUserId})`);
|
|
167
197
|
await gateway.run(abortController.signal);
|
|
168
198
|
} finally {
|
|
169
199
|
process.off("SIGINT", abort);
|
|
@@ -172,6 +202,6 @@ async function main() {
|
|
|
172
202
|
}
|
|
173
203
|
|
|
174
204
|
main().catch((err) => {
|
|
175
|
-
|
|
205
|
+
activeLog.error(`fatal: ${String(err)}`);
|
|
176
206
|
process.exitCode = 1;
|
|
177
207
|
});
|
package/src/session-manager.ts
CHANGED
|
@@ -207,6 +207,15 @@ export class ClaudeSessionManager {
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
clearMainSession() {
|
|
211
|
+
this.sessionIds.delete(this.mainSessionKey);
|
|
212
|
+
try {
|
|
213
|
+
fs.unlinkSync(this.stateFilePath);
|
|
214
|
+
} catch {
|
|
215
|
+
// File may not exist.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
210
219
|
private restore() {
|
|
211
220
|
try {
|
|
212
221
|
const raw = fs.readFileSync(this.stateFilePath, "utf8");
|