@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 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.PRLL_CLAUDE_STATE_DIR?.trim() || path.join(claudeHome, ".parall-agent"));
27
- const workspaceDir = resolvePath(env.PRLL_CLAUDE_WORKSPACE_DIR?.trim() || path.join(stateDir, "workspace"));
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,
@@ -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
  /**
@@ -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;IAQ/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAS/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"}
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?.(`claude-agent[${context.accountId}]: consuming ${pending} steer turn(s)`);
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?.(`claude-agent[${context.accountId}]: failed to prepare local attachments: ${String(err)}`);
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
- async shutdown() {
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("claude-agent: process dead during steer consumption");
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?.(`claude-agent: steer turn timeout — steer was incorporated into previous turn`);
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("claude-agent: process exited while consuming steer turn");
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?.(`claude-agent: subprocess stderr: ${detail}`);
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("claude-agent: adapter shutting down, refusing new process");
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(`claude-agent: spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || "default"}, effort=${this._effortLevel || "default"}, mode=${this.opts.permissionMode})`);
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
- ensureClaudeWorkspace(config.workspaceDir, log, {
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, log);
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
- log.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
83
+ agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
60
84
  if (platformDefaults.thinkingEffort)
61
- log.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
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
- log.info(`starting self-hosted Claude runtime for ${me.display_name} (${agentUserId})`);
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
- log.error(`fatal: ${String(err)}`);
182
+ activeLog.error(`fatal: ${String(err)}`);
151
183
  process.exitCode = 1;
152
184
  });
@@ -37,6 +37,7 @@ export declare class ClaudeSessionManager {
37
37
  */
38
38
  private raceWithTimeout;
39
39
  private closeHandle;
40
+ clearMainSession(): void;
40
41
  private restore;
41
42
  private persist;
42
43
  }
@@ -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"}
@@ -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.29.3",
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/agent-core": "1.29.3",
29
- "@parall/cli": "1.29.3",
30
- "@parall/sdk": "1.29.3"
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.PRLL_CLAUDE_STATE_DIR?.trim() || path.join(claudeHome, ".parall-agent"));
50
- const workspaceDir = resolvePath(env.PRLL_CLAUDE_WORKSPACE_DIR?.trim() || path.join(stateDir, "workspace"));
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?.(`claude-agent[${context.accountId}]: consuming ${pending} steer turn(s)`);
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
- `claude-agent[${context.accountId}]: failed to prepare local attachments: ${String(err)}`,
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
- async shutdown(): Promise<void> {
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("claude-agent: process dead during steer consumption");
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?.(`claude-agent: steer turn timeout — steer was incorporated into previous turn`);
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("claude-agent: process exited while consuming steer turn");
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?.(`claude-agent: subprocess stderr: ${detail}`);
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("claude-agent: adapter shutting down, refusing new process");
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
- `claude-agent: spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || "default"}, effort=${this._effortLevel || "default"}, mode=${this.opts.permissionMode})`,
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
- ensureClaudeWorkspace(config.workspaceDir, log, {
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
- log,
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) log.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
82
- if (platformDefaults.thinkingEffort) log.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
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
- log.info(`starting self-hosted Claude runtime for ${me.display_name} (${agentUserId})`);
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
- log.error(`fatal: ${String(err)}`);
205
+ activeLog.error(`fatal: ${String(err)}`);
176
206
  process.exitCode = 1;
177
207
  });
@@ -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");