@parall/daemon 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.
@@ -3,6 +3,34 @@
3
3
  // ts/claude-agent/dist/index.js
4
4
  import * as os3 from "node:os";
5
5
 
6
+ // ts/agent-core/dist/provider-config.js
7
+ function llmSource(pc) {
8
+ if (pc?.llm_source)
9
+ return pc.llm_source;
10
+ if (pc?.openai_api_key || pc?.openai_base_url || pc?.anthropic_auth_token || pc?.anthropic_base_url) {
11
+ return "custom";
12
+ }
13
+ return "parall";
14
+ }
15
+ function clearAllProviderCreds(env) {
16
+ delete env.ANTHROPIC_AUTH_TOKEN;
17
+ delete env.ANTHROPIC_BASE_URL;
18
+ delete env.ANTHROPIC_API_KEY;
19
+ delete env.OPENAI_API_KEY;
20
+ delete env.OPENAI_BASE_URL;
21
+ delete env.PRLL_CLAUDE_ALLOW_API_KEY;
22
+ }
23
+ function parseProviderConfig(env) {
24
+ const raw = env.PRLL_PROVIDER_CONFIG?.trim();
25
+ if (!raw)
26
+ return void 0;
27
+ try {
28
+ return JSON.parse(raw);
29
+ } catch (err) {
30
+ throw new Error(`Invalid PRLL_PROVIDER_CONFIG JSON: ${String(err)}`);
31
+ }
32
+ }
33
+
6
34
  // ts/agent-core/dist/session-state.js
7
35
  function normalizeSessionKey(sessionKey) {
8
36
  return sessionKey.toLowerCase();
@@ -428,9 +456,13 @@ function createLogger(prefix) {
428
456
  return {
429
457
  info: (msg) => console.log(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
430
458
  warn: (msg) => console.warn(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
431
- error: (msg) => console.error(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`)
459
+ error: (msg) => console.error(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
460
+ child: (sub) => createLogger(`${prefix}:${sub}`)
432
461
  };
433
462
  }
463
+ function childLogger(logger, sub) {
464
+ return logger.child ? logger.child(sub) : logger;
465
+ }
434
466
 
435
467
  // ts/agent-core/dist/gateway-base.js
436
468
  import * as os from "node:os";
@@ -470,6 +502,7 @@ var ENDPOINTS = {
470
502
  ORG_MEMBER: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
471
503
  ORG_MEMBER_CHATS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/chats`,
472
504
  ORG_MEMBER_TASKS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/tasks`,
505
+ REF_SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/refs/search`,
473
506
  // Direct messages (org-scoped, atomic find-or-create + send)
474
507
  DM: (orgId) => `${API_BASE}/orgs/${orgId}/dm`,
475
508
  // Onboarding
@@ -513,6 +546,7 @@ var ENDPOINTS = {
513
546
  AGENT_ACTIVITY: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/activity`,
514
547
  AGENT_MONITOR: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/monitor`,
515
548
  AGENT_ME: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me`,
549
+ AGENT_NEW_SESSION: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/new-session`,
516
550
  AGENT_SESSIONS: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/sessions`,
517
551
  AGENT_SESSION: (orgId, agentId, sessionId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/sessions/${sessionId}`,
518
552
  AGENT_SESSION_STEPS: (orgId, agentId, sessionId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/sessions/${sessionId}/steps`,
@@ -551,6 +585,8 @@ var ENDPOINTS = {
551
585
  MACHINE_KEY: (orgId, machineId, keyId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/keys/${keyId}`,
552
586
  MACHINE_RUNTIME_AUTH_SESSIONS: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/runtime-auth-sessions`,
553
587
  MACHINE_RUNTIME_AUTH_SESSION_COMPLETE: (orgId, machineId, sessionId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/runtime-auth-sessions/${sessionId}/complete`,
588
+ MACHINE_REQUEST_UPDATE: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/request-update`,
589
+ MACHINE_BROWSE: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/browse`,
554
590
  // Machine self-control-plane (mck_-scoped). The bearer token implicitly
555
591
  // identifies the Machine, so there is no `:mid` URL parameter — these are
556
592
  // "self" routes called by the daemon for its own host.
@@ -560,6 +596,7 @@ var ENDPOINTS = {
560
596
  MACHINES_ME_AGENT_LAUNCH_CREDENTIAL: (agentId) => `${API_BASE}/machines/me/agents/${agentId}/launch-credential`,
561
597
  MACHINES_ME_AGENT_WORKSPACE_STATE: (agentId) => `${API_BASE}/machines/me/agents/${agentId}/workspace-state`,
562
598
  MACHINES_ME_WS_TICKET: `${API_BASE}/machines/me/ws/ticket`,
599
+ MACHINES_ME_BROWSE_RESPONSE: (requestId) => `${API_BASE}/machines/me/browse-response/${requestId}`,
563
600
  // Tasks (org-scoped)
564
601
  TASKS: (orgId) => `${API_BASE}/orgs/${orgId}/tasks`,
565
602
  TASK: (orgId, taskId) => `${API_BASE}/orgs/${orgId}/tasks/${taskId}`,
@@ -594,6 +631,12 @@ var ENDPOINTS = {
594
631
  INVITATION_ACCEPT: (id) => `${API_BASE}/invitations/${id}/accept`,
595
632
  INVITATION_DECLINE: (id) => `${API_BASE}/invitations/${id}/decline`,
596
633
  INVITATION_BY_TOKEN: (token) => `${API_BASE}/invitations/by-token/${token}`,
634
+ // Org-level shareable invite link
635
+ ORG_INVITE_LINK: (orgId) => `${API_BASE}/orgs/${orgId}/invite-link`,
636
+ ORG_INVITE_LINK_REGENERATE: (orgId) => `${API_BASE}/orgs/${orgId}/invite-link/regenerate`,
637
+ ORG_INVITE_LINK_JOIN_REQUESTS: (orgId) => `${API_BASE}/orgs/${orgId}/invite-link/join-requests`,
638
+ ORG_INVITE_LINK_JOIN_REQUEST_DECIDE: (orgId, jrId) => `${API_BASE}/orgs/${orgId}/invite-link/join-requests/${jrId}/decide`,
639
+ INVITE_LINK_JOIN: `${API_BASE}/invite-link/join`,
597
640
  // Wikis (org-scoped, served by wiki-service)
598
641
  WIKIS: (orgId) => `${WIKI_BASE}/orgs/${orgId}/wikis`,
599
642
  WIKI: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}`,
@@ -713,6 +756,8 @@ var WS_EVENTS = {
713
756
  INVITATION_ACCEPTED: "invitation.accepted",
714
757
  INVITATION_DECLINED: "invitation.declined",
715
758
  INVITATION_REVOKED: "invitation.revoked",
759
+ ORG_JOIN_REQUEST_NEW: "org.join_request.new",
760
+ ORG_INVITE_LINK_JOINED: "org.invite_link.joined",
716
761
  AGENT_CONFIG_UPDATE: "agent_config.update",
717
762
  PRESENCE_UPDATE: "presence.update",
718
763
  WIKI_CHANGESET_CREATED: "wiki.changeset.created",
@@ -739,7 +784,11 @@ var WS_EVENTS = {
739
784
  MACHINE_AGENT_ATTACHED: "machine.agent.attached",
740
785
  MACHINE_AGENT_DETACHED: "machine.agent.detached",
741
786
  MACHINE_STOP: "machine.stop",
742
- MACHINE_WORKSPACE_SETUP_REQUESTED: "machine.workspace.setup.requested"
787
+ MACHINE_WORKSPACE_SETUP_REQUESTED: "machine.workspace.setup.requested",
788
+ MACHINE_FILESYSTEM_BROWSE: "machine.filesystem.browse",
789
+ MACHINE_UPDATE: "machine.update",
790
+ MACHINE_CONFIG_UPDATED: "machine.config.updated",
791
+ AGENT_NEW_SESSION: "agent.new_session"
743
792
  };
744
793
 
745
794
  // ts/sdk/dist/client.js
@@ -1068,6 +1117,9 @@ var ParallClient = class _ParallClient {
1068
1117
  const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS_ONLINE(orgId));
1069
1118
  return res.user_ids ?? [];
1070
1119
  }
1120
+ async searchRefs(orgId, params) {
1121
+ return this.request("GET", ENDPOINTS.REF_SEARCH(orgId), void 0, params);
1122
+ }
1071
1123
  async removeOrgMember(orgId, userId) {
1072
1124
  return this.request("DELETE", ENDPOINTS.ORG_MEMBER(orgId, userId));
1073
1125
  }
@@ -1121,6 +1173,34 @@ var ParallClient = class _ParallClient {
1121
1173
  async getInvitationByToken(token) {
1122
1174
  return this.request("GET", ENDPOINTS.INVITATION_BY_TOKEN(token));
1123
1175
  }
1176
+ // ---- Org-level shareable invite link ----
1177
+ /** Fetch the org's current invite link. Auto-creates on first call
1178
+ * so the settings UI never sees an empty state. */
1179
+ async getOrgInviteLink(orgId) {
1180
+ return this.request("GET", ENDPOINTS.ORG_INVITE_LINK(orgId));
1181
+ }
1182
+ /** Rotate the token in place. The old token stops resolving immediately. */
1183
+ async regenerateOrgInviteLink(orgId) {
1184
+ return this.request("POST", ENDPOINTS.ORG_INVITE_LINK_REGENERATE(orgId));
1185
+ }
1186
+ /** Toggle the require-approval flag on the org's invite link. */
1187
+ async updateOrgInviteLink(orgId, data) {
1188
+ return this.request("PATCH", ENDPOINTS.ORG_INVITE_LINK(orgId), data);
1189
+ }
1190
+ /** Redeem an invite-link token. Caller must be authenticated; the
1191
+ * token is the lookup key so this endpoint is NOT org-scoped. */
1192
+ async joinByInviteLink(token) {
1193
+ return this.request("POST", ENDPOINTS.INVITE_LINK_JOIN, { token });
1194
+ }
1195
+ /** Admin: list pending invite-link join requests for an org. */
1196
+ async listOrgJoinRequests(orgId) {
1197
+ const res = await this.request("GET", ENDPOINTS.ORG_INVITE_LINK_JOIN_REQUESTS(orgId));
1198
+ return res.data;
1199
+ }
1200
+ /** Admin: approve or reject a pending invite-link join request. */
1201
+ async decideOrgJoinRequest(orgId, jrId, decision) {
1202
+ return this.request("POST", ENDPOINTS.ORG_INVITE_LINK_JOIN_REQUEST_DECIDE(orgId, jrId), { decision });
1203
+ }
1124
1204
  // ---- Direct Messages (org-scoped) ----
1125
1205
  async sendDirectMessage(orgId, req) {
1126
1206
  return this.request("POST", ENDPOINTS.DM(orgId), req);
@@ -1285,6 +1365,9 @@ var ParallClient = class _ParallClient {
1285
1365
  return this.request("GET", ENDPOINTS.AGENT_ME(orgId));
1286
1366
  }
1287
1367
  // ---- Agent Sessions (org-scoped) ----
1368
+ async requestNewAgentSession(orgId, agentId) {
1369
+ return this.request("POST", ENDPOINTS.AGENT_NEW_SESSION(orgId, agentId));
1370
+ }
1288
1371
  async createAgentSession(orgId, agentId, req) {
1289
1372
  return this.request("POST", ENDPOINTS.AGENT_SESSIONS(orgId, agentId), req);
1290
1373
  }
@@ -1387,19 +1470,20 @@ var ParallClient = class _ParallClient {
1387
1470
  }
1388
1471
  // ---- Daemon-mode Machine management (org-scoped, user auth) ----
1389
1472
  /**
1390
- * Create a new daemon-mode Machine. Returns the Machine row + one-shot
1391
- * mck_ token. The UI uses this when the user clicks "Create Workspace".
1473
+ * Create a new self-hosted daemon-mode Machine. Returns the Machine row +
1474
+ * one-shot mck_ token.
1392
1475
  */
1393
1476
  async createMachine(orgId, opts) {
1394
1477
  return this.request("POST", ENDPOINTS.MACHINES(orgId), opts);
1395
1478
  }
1396
1479
  /**
1397
- * Returns only daemon_mode=true machines (non-terminated). Backs the
1398
- * "Workspaces" UI list. Legacy 1:1 machines are excluded.
1480
+ * Returns local daemon_mode=true machines (non-terminated). Backs the
1481
+ * "Workspaces" UI list. Legacy 1:1 and retired hosted daemon machines are
1482
+ * excluded.
1399
1483
  */
1400
1484
  async getDaemonMachines(orgId) {
1401
1485
  const all = await this.getMachines(orgId);
1402
- return all.filter((m) => m.daemon_mode && m.status !== "terminated");
1486
+ return all.filter((m) => m.daemon_mode && m.compute_mode === "local" && m.status !== "terminated");
1403
1487
  }
1404
1488
  /** Attach an agent to a daemon-mode Machine. */
1405
1489
  async attachAgent(orgId, machineId, agentId, opts) {
@@ -1419,8 +1503,8 @@ var ParallClient = class _ParallClient {
1419
1503
  async retryAgentWorkspaceSetup(orgId, machineId, agentId) {
1420
1504
  return this.request("POST", ENDPOINTS.MACHINE_AGENT_WORKSPACE_SETUP(orgId, machineId, agentId));
1421
1505
  }
1422
- async patchMachineLLMSource(orgId, machineId, llmSource) {
1423
- return this.request("PATCH", ENDPOINTS.MACHINE_LLM_SOURCE(orgId, machineId), { llm_source: llmSource });
1506
+ async patchMachineLLMSource(orgId, machineId, llmSource2) {
1507
+ return this.request("PATCH", ENDPOINTS.MACHINE_LLM_SOURCE(orgId, machineId), { llm_source: llmSource2 });
1424
1508
  }
1425
1509
  /** Get machine-level runtime auth state. */
1426
1510
  async getMachineRuntimeAuth(orgId, machineId) {
@@ -1461,8 +1545,9 @@ var ParallClient = class _ParallClient {
1461
1545
  * daemon should call this on a fixed cadence (e.g. every 30s) so an
1462
1546
  * external observer can detect a wedged supervisor.
1463
1547
  */
1464
- async postMachineHeartbeat() {
1465
- return this.request("POST", ENDPOINTS.MACHINES_ME_HEALTH);
1548
+ async postMachineHeartbeat(daemonVersion) {
1549
+ const body = daemonVersion ? { daemon_version: daemonVersion } : void 0;
1550
+ return this.request("POST", ENDPOINTS.MACHINES_ME_HEALTH, body);
1466
1551
  }
1467
1552
  async reportAgentWorkspaceState(agentId, state) {
1468
1553
  const res = await this.request("PUT", ENDPOINTS.MACHINES_ME_AGENT_WORKSPACE_STATE(agentId), state);
@@ -1484,9 +1569,19 @@ var ParallClient = class _ParallClient {
1484
1569
  async getMachineWsTicket() {
1485
1570
  return this.request("POST", ENDPOINTS.MACHINES_ME_WS_TICKET);
1486
1571
  }
1572
+ async postBrowseResponse(requestId, response) {
1573
+ return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSE_RESPONSE(requestId), response);
1574
+ }
1487
1575
  async resizeMachine(orgId, machineId, spec) {
1488
1576
  return this.request("PATCH", ENDPOINTS.MACHINE_SPEC(orgId, machineId), spec);
1489
1577
  }
1578
+ /** Signal a local daemon-mode Machine to check for and apply an update. */
1579
+ async requestMachineUpdate(orgId, machineId, mandatory = false) {
1580
+ await this.request("POST", ENDPOINTS.MACHINE_REQUEST_UPDATE(orgId, machineId), { mandatory });
1581
+ }
1582
+ async browseMachineFilesystem(orgId, machineId, path9) {
1583
+ return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path: path9 }, void 0, false, { timeoutMs: 15e3 });
1584
+ }
1490
1585
  /** Create a new machine key. Returns the raw key string (shown once) + metadata. */
1491
1586
  async createMachineKey(orgId, machineId, name) {
1492
1587
  return this.request("POST", ENDPOINTS.MACHINE_KEYS(orgId, machineId), name ? { name } : void 0);
@@ -2334,6 +2429,7 @@ var ParallWs = class {
2334
2429
  };
2335
2430
 
2336
2431
  // ts/agent-core/dist/gateway-base.js
2432
+ var LIVE_SESSION_STATUSES = /* @__PURE__ */ new Set(["open", "active", "idle"]);
2337
2433
  function parseShutdownDeadlineMs(raw) {
2338
2434
  if (!raw)
2339
2435
  return void 0;
@@ -2399,6 +2495,7 @@ var ParallAgentGateway = class {
2399
2495
  shuttingDown = false;
2400
2496
  inFlightDispatches = 0;
2401
2497
  drainResolvers = [];
2498
+ pendingRestartNotification = null;
2402
2499
  DISPATCHED_MESSAGES_CAP = 5e3;
2403
2500
  COLD_START_WINDOW_MS;
2404
2501
  // SHUTDOWN_DEADLINE_MS is read by waitForDrain via the configured value
@@ -2413,7 +2510,7 @@ var ParallAgentGateway = class {
2413
2510
  async run(abortSignal) {
2414
2511
  const { ws, log: log2 } = this.opts;
2415
2512
  ws.onStateChange((state) => {
2416
- log2?.info(`parall[${this.opts.accountId}]: connection state \u2192 ${state}`);
2513
+ log2?.info(`connection state \u2192 ${state}`);
2417
2514
  });
2418
2515
  ws.on("hello", async (data) => {
2419
2516
  await this.handleHello(data);
@@ -2442,16 +2539,29 @@ var ParallAgentGateway = class {
2442
2539
  await this.handleMessage(data);
2443
2540
  });
2444
2541
  ws.on("agent_config.update", async (data) => {
2445
- this.opts.log?.info(`parall[${this.opts.accountId}]: config update notification (version=${data.version})`);
2542
+ this.opts.log?.info(`config update notification (version=${data.version})`);
2446
2543
  try {
2447
2544
  await this.opts.onConfigUpdate?.(data);
2448
2545
  } catch (err) {
2449
- this.opts.log?.warn(`parall[${this.opts.accountId}]: config update failed: ${String(err)}`);
2546
+ this.opts.log?.warn(`config update failed: ${String(err)}`);
2547
+ }
2548
+ });
2549
+ ws.on("agent.new_session", async (data) => {
2550
+ const prevId = data.previous_session_id ?? "";
2551
+ this.opts.log?.info(`new session signal received (previous=${prevId})`);
2552
+ this.sessionBindings.clear();
2553
+ if (prevId) {
2554
+ this.pendingRestartNotification = `[Harness Notification] This is a fresh session. Your previous session (${prevId}) was ended by the user and you have been restarted.`;
2555
+ }
2556
+ try {
2557
+ await this.opts.onNewSession?.(prevId);
2558
+ } catch (err) {
2559
+ this.opts.log?.warn(`onNewSession callback failed: ${String(err)}`);
2450
2560
  }
2451
2561
  });
2452
2562
  ws.on("recovery.overflow", () => {
2453
- this.opts.log?.warn(`parall[${this.opts.accountId}]: recovery.overflow \u2014 triggering full catch-up`);
2454
- this.catchUpFromDispatch().catch((err) => this.opts.log?.warn(`parall[${this.opts.accountId}]: overflow catch-up failed: ${String(err)}`));
2563
+ this.opts.log?.warn(`recovery.overflow \u2014 triggering full catch-up`);
2564
+ this.catchUpFromDispatch().catch((err) => this.opts.log?.warn(`overflow catch-up failed: ${String(err)}`));
2455
2565
  });
2456
2566
  ws.on("task.assigned", async (data) => {
2457
2567
  if (data.assignee_id !== this.opts.agentUserId)
@@ -2465,7 +2575,7 @@ var ParallAgentGateway = class {
2465
2575
  });
2466
2576
  }
2467
2577
  } catch (err) {
2468
- this.opts.log?.error(`parall[${this.opts.accountId}]: task dispatch failed for ${data.id}: ${String(err)}`);
2578
+ this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
2469
2579
  }
2470
2580
  });
2471
2581
  ws.on("dispatch.new", async (data) => {
@@ -2479,7 +2589,7 @@ var ParallAgentGateway = class {
2479
2589
  });
2480
2590
  }
2481
2591
  } catch (err) {
2482
- this.opts.log?.error(`parall[${this.opts.accountId}]: task comment dispatch failed for ${data.source_id}: ${String(err)}`);
2592
+ this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
2483
2593
  }
2484
2594
  } else if (data.event_type === "task_update") {
2485
2595
  if (!data.task_id)
@@ -2491,7 +2601,7 @@ var ParallAgentGateway = class {
2491
2601
  });
2492
2602
  }
2493
2603
  } catch (err) {
2494
- this.opts.log?.error(`parall[${this.opts.accountId}]: task update dispatch failed for ${data.task_id}: ${String(err)}`);
2604
+ this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
2495
2605
  }
2496
2606
  } else if (data.event_type === "schedule.fire") {
2497
2607
  if (!data.source_id)
@@ -2503,7 +2613,7 @@ var ParallAgentGateway = class {
2503
2613
  });
2504
2614
  }
2505
2615
  } catch (err) {
2506
- this.opts.log?.error(`parall[${this.opts.accountId}]: schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
2616
+ this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
2507
2617
  }
2508
2618
  } else if (data.event_type === "approval_decided") {
2509
2619
  if (!data.source_id)
@@ -2515,13 +2625,13 @@ var ParallAgentGateway = class {
2515
2625
  });
2516
2626
  }
2517
2627
  } catch (err) {
2518
- this.opts.log?.error(`parall[${this.opts.accountId}]: approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
2628
+ this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
2519
2629
  }
2520
2630
  } else if (data.event_type !== "message" && data.event_type !== "task_assign") {
2521
- this.opts.log?.info(`parall[${this.opts.accountId}]: dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) \u2014 no-op`);
2631
+ this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) \u2014 no-op`);
2522
2632
  }
2523
2633
  });
2524
- this.opts.log?.info(`parall[${this.opts.accountId}]: connecting to ${this.opts.connectionLabel ?? "Parall WS"}...`);
2634
+ this.opts.log?.info(`connecting to ${this.opts.connectionLabel ?? "Parall WS"}...`);
2525
2635
  await ws.connect();
2526
2636
  return new Promise((resolve3) => {
2527
2637
  abortSignal.addEventListener("abort", async () => {
@@ -2635,7 +2745,7 @@ var ParallAgentGateway = class {
2635
2745
  }
2636
2746
  });
2637
2747
  } catch (err) {
2638
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to create input step: ${String(err)}`);
2748
+ this.opts.log?.warn(`failed to create input step: ${String(err)}`);
2639
2749
  }
2640
2750
  }
2641
2751
  async createRuntimeStep(sessionId, event, runtimeEvent, stepIdFilePath, contextFilePath) {
@@ -2713,12 +2823,12 @@ var ParallAgentGateway = class {
2713
2823
  target_type: target.target_type,
2714
2824
  target_id: target.target_id,
2715
2825
  content: { text: runtimeEvent.message, suppressed: false },
2716
- projection: true
2826
+ projection: false
2717
2827
  });
2718
2828
  break;
2719
2829
  }
2720
2830
  } catch (err) {
2721
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to create ${runtimeEvent.type} step: ${String(err)}`);
2831
+ this.opts.log?.warn(`failed to create ${runtimeEvent.type} step: ${String(err)}`);
2722
2832
  }
2723
2833
  }
2724
2834
  writeContextFile(filePath, ctx) {
@@ -2726,7 +2836,7 @@ var ParallAgentGateway = class {
2726
2836
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
2727
2837
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
2728
2838
  } catch (err) {
2729
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to write context file ${filePath}: ${String(err)}`);
2839
+ this.opts.log?.warn(`failed to write context file ${filePath}: ${String(err)}`);
2730
2840
  }
2731
2841
  }
2732
2842
  updateContextFileStepId(filePath, stepId) {
@@ -2736,7 +2846,7 @@ var ParallAgentGateway = class {
2736
2846
  ctx.step_id = stepId;
2737
2847
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
2738
2848
  } catch (err) {
2739
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file step_id ${filePath}: ${String(err)}`);
2849
+ this.opts.log?.warn(`failed to update context file step_id ${filePath}: ${String(err)}`);
2740
2850
  }
2741
2851
  }
2742
2852
  updateContextFileSessionId(filePath, sessionId) {
@@ -2746,7 +2856,7 @@ var ParallAgentGateway = class {
2746
2856
  ctx.session_id = sessionId;
2747
2857
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
2748
2858
  } catch (err) {
2749
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file session_id ${filePath}: ${String(err)}`);
2859
+ this.opts.log?.warn(`failed to update context file session_id ${filePath}: ${String(err)}`);
2750
2860
  }
2751
2861
  }
2752
2862
  /** @deprecated Use writeContextFile / updateContextFileStepId. */
@@ -2755,7 +2865,7 @@ var ParallAgentGateway = class {
2755
2865
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
2756
2866
  fs.writeFileSync(filePath, stepId, "utf8");
2757
2867
  } catch (err) {
2758
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to write step id file ${filePath}: ${String(err)}`);
2868
+ this.opts.log?.warn(`failed to write step id file ${filePath}: ${String(err)}`);
2759
2869
  }
2760
2870
  }
2761
2871
  /** @deprecated Use writeContextFile / updateContextFileStepId. */
@@ -2789,6 +2899,17 @@ var ParallAgentGateway = class {
2789
2899
  parent_session_id: parentSessionId,
2790
2900
  runtime_ref: Object.keys(runtimeRef).length > 0 ? runtimeRef : void 0
2791
2901
  });
2902
+ if (!LIVE_SESSION_STATUSES.has(session.status)) {
2903
+ this.opts.log?.warn?.(`createAgentSession returned terminal session ${session.id} (${session.status}), resetting runtime for ${sessionKey}`);
2904
+ this.sessionBindings.delete(sessionKey);
2905
+ try {
2906
+ await this.opts.onSessionStale?.(sessionKey);
2907
+ } catch (e) {
2908
+ this.opts.log?.warn?.(`onSessionStale failed: ${e}`);
2909
+ }
2910
+ this.opts.log?.info?.(`stale session self-heal complete for ${sessionKey} \u2014 next dispatch will create a fresh session`);
2911
+ throw new Error(`Agent session ${session.id} is terminal (${session.status})`);
2912
+ }
2792
2913
  const binding = {
2793
2914
  sessionKey,
2794
2915
  agentSessionId: session.id,
@@ -2812,9 +2933,13 @@ var ParallAgentGateway = class {
2812
2933
  // catch-up on the replacement pod — otherwise we silently drop work.
2813
2934
  async runDispatch(event, sessionKey, bodyForAgent, earlierEvents = [], captureText) {
2814
2935
  if (this.shuttingDown) {
2815
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping dispatch for ${event.messageId} (shutting down) \u2014 leaving unacked for catch-up on replacement pod`);
2936
+ this.opts.log?.info(`skipping dispatch for ${event.messageId} (shutting down) \u2014 leaving unacked for catch-up on replacement pod`);
2816
2937
  return false;
2817
2938
  }
2939
+ if (this.pendingRestartNotification) {
2940
+ bodyForAgent = this.pendingRestartNotification + "\n\n---\n\n" + bodyForAgent;
2941
+ this.pendingRestartNotification = null;
2942
+ }
2818
2943
  setSessionChatId(sessionKey, event.targetId);
2819
2944
  setSessionMessageId(sessionKey, event.messageId);
2820
2945
  setDispatchMessageId(sessionKey, event.messageId);
@@ -2944,7 +3069,7 @@ var ParallAgentGateway = class {
2944
3069
  item.resolve(true);
2945
3070
  }
2946
3071
  } catch (err) {
2947
- this.opts.log?.error(`parall[${this.opts.accountId}]: fork dispatch failed for ${last.messageId}: ${String(err)}`);
3072
+ this.opts.log?.error(`fork dispatch failed for ${last.messageId}: ${String(err)}`);
2948
3073
  for (const item of items) {
2949
3074
  item.resolve(false);
2950
3075
  }
@@ -2962,7 +3087,7 @@ var ParallAgentGateway = class {
2962
3087
  try {
2963
3088
  historyPath = this.opts.dispatchAdapter.getSessionHistoryPath?.(fork.fork.sessionKey);
2964
3089
  } catch (err) {
2965
- this.opts.log?.warn?.(`parall[${this.opts.accountId}]: failed to resolve fork history path: ${String(err)}`);
3090
+ this.opts.log?.warn?.(`failed to resolve fork history path: ${String(err)}`);
2966
3091
  }
2967
3092
  this.dispatchState.pendingForkResults.push({
2968
3093
  forkSessionKey: fork.fork.sessionKey,
@@ -3013,7 +3138,7 @@ var ParallAgentGateway = class {
3013
3138
  try {
3014
3139
  while (this.dispatchState.mainBuffer.length > 0) {
3015
3140
  if (this.shuttingDown) {
3016
- this.opts.log?.info(`parall[${this.opts.accountId}]: drainMainBuffer halted (shutting down) \u2014 ${this.dispatchState.mainBuffer.length} buffered, ${this.dispatchState.pendingForkResults.length} pending fork results left for catch-up`);
3141
+ this.opts.log?.info(`drainMainBuffer halted (shutting down) \u2014 ${this.dispatchState.mainBuffer.length} buffered, ${this.dispatchState.pendingForkResults.length} pending fork results left for catch-up`);
3017
3142
  break;
3018
3143
  }
3019
3144
  const targetId = this.dispatchState.mainBuffer[0].targetId;
@@ -3079,7 +3204,7 @@ var ParallAgentGateway = class {
3079
3204
  this.dispatchState.mainBuffer.push(event);
3080
3205
  if (this.dispatchState.mainCurrentTargetId === event.targetId && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
3081
3206
  this.startInjectedTyping(event);
3082
- this.opts.log?.info(`parall[${this.opts.accountId}]: steer injected for ${event.messageId} (will drain for bookkeeping)`);
3207
+ this.opts.log?.info(`steer injected for ${event.messageId} (will drain for bookkeeping)`);
3083
3208
  }
3084
3209
  if (!this.dispatchState.mainDispatching && !this.draining && this.dispatchState.mainBuffer.length > 0) {
3085
3210
  this.dispatchState.mainDispatching = true;
@@ -3108,7 +3233,7 @@ var ParallAgentGateway = class {
3108
3233
  preDispatchBranchPoint: this.dispatchState.mainPreDispatchBranchPoint
3109
3234
  });
3110
3235
  if (!fork) {
3111
- this.opts.log?.warn(`parall[${this.opts.accountId}]: fork failed, buffering event for main session`);
3236
+ this.opts.log?.warn(`fork failed, buffering event for main session`);
3112
3237
  this.dispatchState.mainBuffer.push(event);
3113
3238
  return false;
3114
3239
  }
@@ -3124,7 +3249,7 @@ var ParallAgentGateway = class {
3124
3249
  activeFork.queue.push({ event, resolve: resolve3 });
3125
3250
  });
3126
3251
  this.runForkDrainLoop(activeFork).catch((err) => {
3127
- this.opts.log?.error(`parall[${this.opts.accountId}]: fork drain loop error: ${String(err)}`);
3252
+ this.opts.log?.error(`fork drain loop error: ${String(err)}`);
3128
3253
  });
3129
3254
  return firstEventPromise;
3130
3255
  }
@@ -3144,7 +3269,7 @@ var ParallAgentGateway = class {
3144
3269
  this.chatInfoMap.set(chatId, chatInfo);
3145
3270
  return chatInfo;
3146
3271
  } catch (err) {
3147
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to resolve chat ${chatId}: ${String(err)}`);
3272
+ this.opts.log?.warn(`failed to resolve chat ${chatId}: ${String(err)}`);
3148
3273
  return null;
3149
3274
  }
3150
3275
  }
@@ -3217,7 +3342,7 @@ var ParallAgentGateway = class {
3217
3342
  this.dispatchedMessages.delete(data.id);
3218
3343
  }
3219
3344
  } catch (err) {
3220
- this.opts.log?.error(`parall[${this.opts.accountId}]: event dispatch failed for ${data.id}: ${String(err)}`);
3345
+ this.opts.log?.error(`event dispatch failed for ${data.id}: ${String(err)}`);
3221
3346
  this.dispatchedMessages.delete(data.id);
3222
3347
  }
3223
3348
  }
@@ -3226,11 +3351,11 @@ var ParallAgentGateway = class {
3226
3351
  return false;
3227
3352
  const dedupeKey = `${task.id}:${task.updated_at}`;
3228
3353
  if (this.dispatchedTasks.has(dedupeKey)) {
3229
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping already-dispatched task ${task.identifier ?? task.id}`);
3354
+ this.opts.log?.info(`skipping already-dispatched task ${task.identifier ?? task.id}`);
3230
3355
  return false;
3231
3356
  }
3232
3357
  this.dispatchedTasks.add(dedupeKey);
3233
- this.opts.log?.info(`parall[${this.opts.accountId}]: task assigned: ${task.identifier ?? task.id} "${task.title}"`);
3358
+ this.opts.log?.info(`task assigned: ${task.identifier ?? task.id} "${task.title}"`);
3234
3359
  const parts = [`Title: ${task.title}`];
3235
3360
  parts.push(`Status: ${task.status}`, `Priority: ${task.priority}`);
3236
3361
  if (task.project_id)
@@ -3271,7 +3396,7 @@ var ParallAgentGateway = class {
3271
3396
  const isAssignee = task.assignee_id === this.opts.agentUserId;
3272
3397
  const isCreatorUpdate = opts.allowCreator === true && task.creator_id === this.opts.agentUserId;
3273
3398
  if (!isAssignee && !isCreatorUpdate) {
3274
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping stale task dispatch ${ackSourceId ?? taskId} \u2014 assigned to ${task.assignee_id}, creator ${task.creator_id}`);
3399
+ this.opts.log?.info(`skipping stale task dispatch ${ackSourceId ?? taskId} \u2014 assigned to ${task.assignee_id}, creator ${task.creator_id}`);
3275
3400
  return true;
3276
3401
  }
3277
3402
  return this.handleTaskAssignment(task, ackSourceId);
@@ -3289,7 +3414,7 @@ var ParallAgentGateway = class {
3289
3414
  } catch (err) {
3290
3415
  const status = err?.status;
3291
3416
  if (status === 404) {
3292
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping deleted comment ${commentId}, acking stale dispatch`);
3417
+ this.opts.log?.info(`skipping deleted comment ${commentId}, acking stale dispatch`);
3293
3418
  this.dispatchedTasks.delete(dedupeKey);
3294
3419
  return true;
3295
3420
  }
@@ -3301,7 +3426,7 @@ var ParallAgentGateway = class {
3301
3426
  return true;
3302
3427
  }
3303
3428
  if (comment.hints?.no_reply) {
3304
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping no_reply task comment ${commentId}, acking stale dispatch`);
3429
+ this.opts.log?.info(`skipping no_reply task comment ${commentId}, acking stale dispatch`);
3305
3430
  this.dispatchedTasks.delete(dedupeKey);
3306
3431
  return true;
3307
3432
  }
@@ -3311,7 +3436,7 @@ var ParallAgentGateway = class {
3311
3436
  } catch {
3312
3437
  }
3313
3438
  const taskLabel = task ? `${task.identifier ?? task.id} "${task.title}"` : taskId;
3314
- this.opts.log?.info(`parall[${this.opts.accountId}]: task comment on ${taskLabel} by ${actorId ?? "unknown"}`);
3439
+ this.opts.log?.info(`task comment on ${taskLabel} by ${actorId ?? "unknown"}`);
3315
3440
  const parts = [];
3316
3441
  if (task) {
3317
3442
  parts.push(`Task: ${task.title} (prll://${task.id})`);
@@ -3361,10 +3486,10 @@ var ParallAgentGateway = class {
3361
3486
  } catch (err) {
3362
3487
  const status = err?.status;
3363
3488
  if (status === 404) {
3364
- this.opts.log?.warn(`parall[${this.opts.accountId}]: schedule run ${runId} not accessible (404), acking stale dispatch`);
3489
+ this.opts.log?.warn(`schedule run ${runId} not accessible (404), acking stale dispatch`);
3365
3490
  return true;
3366
3491
  }
3367
- this.opts.log?.warn(`parall[${this.opts.accountId}]: schedule run fetch failed for ${runId}, leaving pending: ${String(err)}`);
3492
+ this.opts.log?.warn(`schedule run fetch failed for ${runId}, leaving pending: ${String(err)}`);
3368
3493
  return false;
3369
3494
  }
3370
3495
  if (!run)
@@ -3378,7 +3503,7 @@ var ParallAgentGateway = class {
3378
3503
  if (this.dispatchedTasks.has(dedupeKey))
3379
3504
  return false;
3380
3505
  this.dispatchedTasks.add(dedupeKey);
3381
- this.opts.log?.info(`parall[${this.opts.accountId}]: schedule fired: ${run.id} (schedule ${run.schedule_id})`);
3506
+ this.opts.log?.info(`schedule fired: ${run.id} (schedule ${run.schedule_id})`);
3382
3507
  const event = {
3383
3508
  type: "schedule",
3384
3509
  // Route by schedule_id (not attached chat_id) so concurrent fires of
@@ -3415,10 +3540,10 @@ var ParallAgentGateway = class {
3415
3540
  } catch (err) {
3416
3541
  const status = err?.status;
3417
3542
  if (status === 404 || status === 403) {
3418
- this.opts.log?.warn(`parall[${this.opts.accountId}]: approval ${approvalId} not accessible (${status}), acking stale dispatch`);
3543
+ this.opts.log?.warn(`approval ${approvalId} not accessible (${status}), acking stale dispatch`);
3419
3544
  return true;
3420
3545
  }
3421
- this.opts.log?.warn(`parall[${this.opts.accountId}]: approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
3546
+ this.opts.log?.warn(`approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
3422
3547
  return false;
3423
3548
  }
3424
3549
  if (!approval)
@@ -3429,7 +3554,7 @@ var ParallAgentGateway = class {
3429
3554
  if (this.dispatchedTasks.has(dedupeKey))
3430
3555
  return false;
3431
3556
  this.dispatchedTasks.add(dedupeKey);
3432
- this.opts.log?.info(`parall[${this.opts.accountId}]: approval decided: ${approval.id} (${approval.status})`);
3557
+ this.opts.log?.info(`approval decided: ${approval.id} (${approval.status})`);
3433
3558
  const statusLabel = approval.status === "approved" ? "Approved" : "Rejected";
3434
3559
  const execInfo = approval.execution_status ? ` | execution: ${approval.execution_status}` : "";
3435
3560
  const body = `${statusLabel}: ${approval.title}${execInfo}`;
@@ -3479,14 +3604,14 @@ var ParallAgentGateway = class {
3479
3604
  try {
3480
3605
  dispatched = await this.handleTaskDispatch(item.task_id, item.source_id ?? item.task_id);
3481
3606
  } catch (err) {
3482
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
3607
+ this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
3483
3608
  continue;
3484
3609
  }
3485
3610
  } else if (item.event_type === "task_update" && item.task_id) {
3486
3611
  try {
3487
3612
  dispatched = await this.handleTaskDispatch(item.task_id, item.source_id ?? item.task_id, { allowCreator: true });
3488
3613
  } catch (err) {
3489
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
3614
+ this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
3490
3615
  continue;
3491
3616
  }
3492
3617
  } else if (item.event_type === "task_comment" && item.source_id && item.task_id) {
@@ -3508,7 +3633,7 @@ var ParallAgentGateway = class {
3508
3633
  msg = null;
3509
3634
  } else {
3510
3635
  msgFetchFailed = true;
3511
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
3636
+ this.opts.log?.warn(`catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
3512
3637
  }
3513
3638
  }
3514
3639
  if (msgFetchFailed) {
@@ -3539,13 +3664,13 @@ var ParallAgentGateway = class {
3539
3664
  });
3540
3665
  }
3541
3666
  } catch (err) {
3542
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up dispatch ${item.id} (${item.event_type}) failed: ${String(err)}`);
3667
+ this.opts.log?.warn(`catch-up dispatch ${item.id} (${item.event_type}) failed: ${String(err)}`);
3543
3668
  }
3544
3669
  }
3545
3670
  cursor = !this.shuttingDown && page.has_more ? page.next_cursor : void 0;
3546
3671
  } while (cursor);
3547
3672
  if (processed > 0 || skippedOld > 0) {
3548
- this.opts.log?.info(`parall[${this.opts.accountId}]: dispatch catch-up: processed ${processed}, skipped ${skippedOld} old item(s)`);
3673
+ this.opts.log?.info(`dispatch catch-up: processed ${processed}, skipped ${skippedOld} old item(s)`);
3549
3674
  }
3550
3675
  }
3551
3676
  async handleHello(data) {
@@ -3554,7 +3679,7 @@ var ParallAgentGateway = class {
3554
3679
  const intervalSec = data.heartbeat_interval > 0 ? data.heartbeat_interval : 30;
3555
3680
  try {
3556
3681
  const count = await fetchAllChats(client, config.org_id, this.chatInfoMap);
3557
- log2?.info(`parall[${this.opts.accountId}]: WebSocket connected, ${count} chats cached`);
3682
+ log2?.info(`WebSocket connected, ${count} chats cached`);
3558
3683
  await this.opts.onSessionReady?.({
3559
3684
  activeSessionId: this.activeSessionId,
3560
3685
  ws: this.opts.ws,
@@ -3568,7 +3693,7 @@ var ParallAgentGateway = class {
3568
3693
  const expectedMs = intervalSec * 1e3;
3569
3694
  const drift = now - this.lastHeartbeatAt - expectedMs;
3570
3695
  if (drift > 15e3) {
3571
- log2?.warn(`parall[${this.opts.accountId}]: heartbeat drift ${drift}ms \u2014 event loop may be blocked`);
3696
+ log2?.warn(`heartbeat drift ${drift}ms \u2014 event loop may be blocked`);
3572
3697
  }
3573
3698
  this.lastHeartbeatAt = now;
3574
3699
  if (this.opts.ws.state !== "connected")
@@ -3584,10 +3709,10 @@ var ParallAgentGateway = class {
3584
3709
  const isFirstHello = !this.hadSuccessfulHello;
3585
3710
  this.hadSuccessfulHello = true;
3586
3711
  this.catchUpFromDispatch(isFirstHello).catch((err) => {
3587
- log2?.warn(`parall[${this.opts.accountId}]: dispatch catch-up failed: ${String(err)}`);
3712
+ log2?.warn(`dispatch catch-up failed: ${String(err)}`);
3588
3713
  });
3589
3714
  } catch (err) {
3590
- log2?.error(`parall[${this.opts.accountId}]: failed to fetch chats: ${String(err)}`);
3715
+ log2?.error(`failed to fetch chats: ${String(err)}`);
3591
3716
  }
3592
3717
  }
3593
3718
  // Resolves when in-flight dispatches hit 0 or the deadline elapses.
@@ -3613,12 +3738,12 @@ var ParallAgentGateway = class {
3613
3738
  async shutdown() {
3614
3739
  this.shuttingDown = true;
3615
3740
  if (this.inFlightDispatches > 0) {
3616
- this.opts.log?.info(`parall[${this.opts.accountId}]: draining ${this.inFlightDispatches} in-flight dispatch(es), deadline ${this.SHUTDOWN_DEADLINE_MS}ms`);
3741
+ this.opts.log?.info(`draining ${this.inFlightDispatches} in-flight dispatch(es), deadline ${this.SHUTDOWN_DEADLINE_MS}ms`);
3617
3742
  await this.waitForDrain(this.SHUTDOWN_DEADLINE_MS);
3618
3743
  if (this.inFlightDispatches > 0) {
3619
- this.opts.log?.warn(`parall[${this.opts.accountId}]: drain deadline hit; ${this.inFlightDispatches} dispatch(es) still running \u2014 they will be killed by process exit`);
3744
+ this.opts.log?.warn(`drain deadline hit; ${this.inFlightDispatches} dispatch(es) still running \u2014 they will be killed by process exit`);
3620
3745
  } else {
3621
- this.opts.log?.info(`parall[${this.opts.accountId}]: drain complete`);
3746
+ this.opts.log?.info(`drain complete`);
3622
3747
  }
3623
3748
  }
3624
3749
  if (this.heartbeatTimer)
@@ -3629,7 +3754,7 @@ var ParallAgentGateway = class {
3629
3754
  this.activeDispatches.clear();
3630
3755
  await this.opts.onBeforeDisconnect?.();
3631
3756
  this.opts.ws.disconnect();
3632
- this.opts.log?.info(`parall[${this.opts.accountId}]: disconnected`);
3757
+ this.opts.log?.info(`disconnected`);
3633
3758
  }
3634
3759
  };
3635
3760
 
@@ -4248,8 +4373,8 @@ function resolveClaudeAgentConfig(env = process.env) {
4248
4373
  const apiKey = requireEnv(env, "PRLL_API_KEY");
4249
4374
  const orgId = requireEnv(env, "PRLL_ORG_ID");
4250
4375
  const claudeHome = resolvePath(env.PRLL_CLAUDE_HOME?.trim() || env.HOME || os2.homedir());
4251
- const stateDir = resolvePath(env.PRLL_CLAUDE_STATE_DIR?.trim() || path4.join(claudeHome, ".parall-agent"));
4252
- const workspaceDir = resolvePath(env.PRLL_CLAUDE_WORKSPACE_DIR?.trim() || path4.join(stateDir, "workspace"));
4376
+ const stateDir = resolvePath(env.PRLL_STATE_DIR?.trim() || path4.join(claudeHome, ".parall-agent"));
4377
+ const workspaceDir = resolvePath(env.PRLL_WORKSPACE_DIR?.trim() || path4.join(stateDir, "workspace"));
4253
4378
  const additionalDirs = parseList(env.PRLL_CLAUDE_ADD_DIRS).map(resolvePath);
4254
4379
  return {
4255
4380
  apiUrl,
@@ -5149,7 +5274,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5149
5274
  const pending = this.pendingInjections.get(sessionKey) ?? 0;
5150
5275
  if (pending > 0) {
5151
5276
  this.pendingInjections.delete(sessionKey);
5152
- context.log?.info?.(`claude-agent[${context.accountId}]: consuming ${pending} steer turn(s)`);
5277
+ context.log?.info?.(`consuming ${pending} steer turn(s)`);
5153
5278
  for (let i = 0; i < pending; i++) {
5154
5279
  yield* this.consumeSteerTurn(sessionKey, context.log);
5155
5280
  }
@@ -5166,7 +5291,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5166
5291
  promptBody = prepared.body;
5167
5292
  releasePreparedAttachments = pinLocalAttachmentPaths(prepared.attachments.images);
5168
5293
  } catch (err) {
5169
- context.log?.warn?.(`claude-agent[${context.accountId}]: failed to prepare local attachments: ${String(err)}`);
5294
+ context.log?.warn?.(`failed to prepare local attachments: ${String(err)}`);
5170
5295
  }
5171
5296
  try {
5172
5297
  yield* this.runTurn(sessionKey, promptBody, context.log);
@@ -5195,12 +5320,16 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5195
5320
  }
5196
5321
  this.opts.sessionManager.cleanupFork(fork.sessionKey);
5197
5322
  }
5198
- async shutdown() {
5199
- this.shuttingDown = true;
5323
+ resetProcesses() {
5200
5324
  for (const [sessionKey, state] of this.processes) {
5201
5325
  this.killProcess(sessionKey, state);
5202
5326
  }
5203
5327
  this.processes.clear();
5328
+ this.pendingInjections.clear();
5329
+ }
5330
+ async shutdown() {
5331
+ this.shuttingDown = true;
5332
+ this.resetProcesses();
5204
5333
  await this.opts.sessionManager.shutdownAll();
5205
5334
  }
5206
5335
  static STEER_TURN_TIMEOUT_MS = 1e4;
@@ -5217,7 +5346,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5217
5346
  async *consumeSteerTurn(sessionKey, log2) {
5218
5347
  const state = this.processes.get(sessionKey);
5219
5348
  if (!state || state.done) {
5220
- throw new Error("claude-agent: process dead during steer consumption");
5349
+ throw new Error("process dead during steer consumption");
5221
5350
  }
5222
5351
  const groupKey = randomUUID();
5223
5352
  const parserNext = state.parser.next();
@@ -5227,7 +5356,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5227
5356
  ]);
5228
5357
  if (firstRead.kind === "timeout") {
5229
5358
  state.steerReadPending = parserNext;
5230
- log2?.info?.(`claude-agent: steer turn timeout \u2014 steer was incorporated into previous turn`);
5359
+ log2?.info?.(`steer turn timeout \u2014 steer was incorporated into previous turn`);
5231
5360
  return;
5232
5361
  }
5233
5362
  let next = firstRead.result;
@@ -5235,7 +5364,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5235
5364
  if (next.done) {
5236
5365
  state.done = true;
5237
5366
  this.processes.delete(sessionKey);
5238
- throw new Error("claude-agent: process exited while consuming steer turn");
5367
+ throw new Error("process exited while consuming steer turn");
5239
5368
  }
5240
5369
  const parsed = next.value;
5241
5370
  if (parsed.type === "session_id") {
@@ -5306,7 +5435,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5306
5435
  const detail = state.handle.stderrChunks.join("").trim();
5307
5436
  const exit = await state.handle.exitPromise.catch(() => ({ code: null, signal: null }));
5308
5437
  if (detail) {
5309
- log2?.warn?.(`claude-agent: subprocess stderr: ${detail}`);
5438
+ log2?.warn?.(`subprocess stderr: ${detail}`);
5310
5439
  }
5311
5440
  if (!sawError) {
5312
5441
  yield {
@@ -5350,7 +5479,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5350
5479
  }
5351
5480
  ensureProcess(sessionKey, log2) {
5352
5481
  if (this.shuttingDown) {
5353
- throw new Error("claude-agent: adapter shutting down, refusing new process");
5482
+ throw new Error("adapter shutting down, refusing new process");
5354
5483
  }
5355
5484
  const existing = this.processes.get(sessionKey);
5356
5485
  if (existing && !existing.done) {
@@ -5373,7 +5502,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
5373
5502
  spawnProcess(sessionKey, log2) {
5374
5503
  const args = this.buildArgs(sessionKey);
5375
5504
  const env = buildSpawnEnv(process.env, this.opts.claudeHome, this.buildPlaceholderContext(sessionKey), { allowApiKey: this.opts.allowApiKey, effortLevel: this._effortLevel });
5376
- log2?.info(`claude-agent: spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || "default"}, effort=${this._effortLevel || "default"}, mode=${this.opts.permissionMode})`);
5505
+ log2?.info(`spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || "default"}, effort=${this._effortLevel || "default"}, mode=${this.opts.permissionMode})`);
5377
5506
  const proc = spawn(IS_WIN32 ? quoteWin32Arg(this.opts.claudeBin) : this.opts.claudeBin, IS_WIN32 ? args.map(quoteWin32Arg) : args, {
5378
5507
  cwd: this.opts.workspaceDir,
5379
5508
  env,
@@ -5628,6 +5757,13 @@ var ClaudeSessionManager = class _ClaudeSessionManager {
5628
5757
  }
5629
5758
  }
5630
5759
  }
5760
+ clearMainSession() {
5761
+ this.sessionIds.delete(this.mainSessionKey);
5762
+ try {
5763
+ fs6.unlinkSync(this.stateFilePath);
5764
+ } catch {
5765
+ }
5766
+ }
5631
5767
  restore() {
5632
5768
  try {
5633
5769
  const raw = fs6.readFileSync(this.stateFilePath, "utf8");
@@ -5673,6 +5809,7 @@ function ensureClaudeWorkspace(workspaceDir, log2, agentIdentity) {
5673
5809
 
5674
5810
  // ts/claude-agent/dist/index.js
5675
5811
  var log = createLogger("claude-agent");
5812
+ var activeLog = log;
5676
5813
  async function getAgentMeWithLegacyFallback(client, orgId) {
5677
5814
  try {
5678
5815
  return await client.getAgentMe(orgId);
@@ -5684,7 +5821,27 @@ async function getAgentMeWithLegacyFallback(client, orgId) {
5684
5821
  return { ...user, agent_profile: null };
5685
5822
  }
5686
5823
  }
5824
+ function resolveProviderEnv() {
5825
+ const pc = parseProviderConfig(process.env);
5826
+ if (!pc)
5827
+ return;
5828
+ clearAllProviderCreds(process.env);
5829
+ const source = llmSource(pc);
5830
+ if (source === "parall") {
5831
+ process.env.ANTHROPIC_AUTH_TOKEN = process.env.PRLL_API_KEY;
5832
+ process.env.ANTHROPIC_BASE_URL = `${process.env.PRLL_API_URL}/api/llm`;
5833
+ process.env.PRLL_CLAUDE_ALLOW_API_KEY = "1";
5834
+ } else if (source === "custom") {
5835
+ if (pc.anthropic_auth_token) {
5836
+ process.env.ANTHROPIC_AUTH_TOKEN = pc.anthropic_auth_token;
5837
+ process.env.PRLL_CLAUDE_ALLOW_API_KEY = "1";
5838
+ }
5839
+ if (pc.anthropic_base_url)
5840
+ process.env.ANTHROPIC_BASE_URL = pc.anthropic_base_url;
5841
+ }
5842
+ }
5687
5843
  async function main() {
5844
+ resolveProviderEnv();
5688
5845
  const config = resolveClaudeAgentConfig(process.env);
5689
5846
  if (config.allowApiKey && (process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN)) {
5690
5847
  log.warn("PRLL_CLAUDE_ALLOW_API_KEY=1 \u2014 ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN will reach the Claude CLI; billing routes through Anthropic API pay-per-use instead of Claude.ai OAuth.");
@@ -5696,28 +5853,30 @@ async function main() {
5696
5853
  });
5697
5854
  const me = await getAgentMeWithLegacyFallback(client, config.orgId);
5698
5855
  const agentUserId = me.id;
5699
- ensureClaudeWorkspace(config.workspaceDir, log, {
5856
+ const agentLog = childLogger(log, agentUserId);
5857
+ activeLog = agentLog;
5858
+ ensureClaudeWorkspace(config.workspaceDir, agentLog, {
5700
5859
  userId: agentUserId,
5701
5860
  displayName: me.display_name,
5702
5861
  description: me.agent_profile?.description ?? void 0
5703
5862
  });
5704
5863
  const runtimeKey = config.runtimeKey || buildClaudeRuntimeKey(agentUserId);
5705
5864
  const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
5706
- const sessionManager = new ClaudeSessionManager(runtimeKey, sessionStateFilePath, log);
5865
+ const sessionManager = new ClaudeSessionManager(runtimeKey, sessionStateFilePath, agentLog);
5707
5866
  const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
5708
5867
  const ws = new ParallWs({
5709
5868
  getTicket: () => client.getWsTicket(),
5710
5869
  wsUrl: resolvedWsUrl
5711
5870
  });
5712
- const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log });
5871
+ const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log: agentLog });
5713
5872
  const platformDefaults = await configMgr.fetch();
5714
5873
  let platformManaged = isPlatformManagedProfile(me.agent_profile);
5715
5874
  const resolvedModel = platformManaged ? platformDefaults.model ?? config.model : config.model;
5716
5875
  const resolvedEffort = platformManaged ? platformDefaults.thinkingEffort ?? (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || void 0) : process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || void 0;
5717
5876
  if (platformDefaults.model)
5718
- log.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
5877
+ agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
5719
5878
  if (platformDefaults.thinkingEffort)
5720
- log.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
5879
+ agentLog.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
5721
5880
  const adapter = new ClaudeCodeAdapter({
5722
5881
  claudeBin: config.claudeBin,
5723
5882
  claudeHome: config.claudeHome,
@@ -5762,7 +5921,7 @@ async function main() {
5762
5921
  claude_home: config.claudeHome
5763
5922
  },
5764
5923
  dispatchAdapter: adapter,
5765
- log,
5924
+ log: agentLog,
5766
5925
  shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
5767
5926
  contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
5768
5927
  stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
@@ -5790,14 +5949,22 @@ async function main() {
5790
5949
  // gateway's shuttingDown flag alone does not tear them down. Piggyback on
5791
5950
  // onBeforeDisconnect to close stdin and SIGTERM any survivors after
5792
5951
  // in-flight drains finish.
5793
- onBeforeDisconnect: () => adapter.shutdown()
5952
+ onBeforeDisconnect: () => adapter.shutdown(),
5953
+ onNewSession: async () => {
5954
+ sessionManager.clearMainSession();
5955
+ adapter.resetProcesses();
5956
+ },
5957
+ onSessionStale: () => {
5958
+ sessionManager.clearMainSession();
5959
+ adapter.resetProcesses();
5960
+ }
5794
5961
  });
5795
5962
  const abortController = new AbortController();
5796
5963
  const abort = () => abortController.abort();
5797
5964
  process.on("SIGINT", abort);
5798
5965
  process.on("SIGTERM", abort);
5799
5966
  try {
5800
- log.info(`starting self-hosted Claude runtime for ${me.display_name} (${agentUserId})`);
5967
+ agentLog.info(`starting self-hosted Claude runtime for ${me.display_name} (${agentUserId})`);
5801
5968
  await gateway.run(abortController.signal);
5802
5969
  } finally {
5803
5970
  process.off("SIGINT", abort);
@@ -5805,6 +5972,6 @@ async function main() {
5805
5972
  }
5806
5973
  }
5807
5974
  main().catch((err) => {
5808
- log.error(`fatal: ${String(err)}`);
5975
+ activeLog.error(`fatal: ${String(err)}`);
5809
5976
  process.exitCode = 1;
5810
5977
  });