@parall/agent-core 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.
@@ -4,6 +4,7 @@ export type GatewayLogger = {
4
4
  info: (msg: string) => void;
5
5
  warn: (msg: string) => void;
6
6
  error: (msg: string) => void;
7
+ child?: (sub: string) => GatewayLogger;
7
8
  };
8
9
  export type DispatchContext = {
9
10
  accountId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-adapter.d.ts","sourceRoot":"","sources":["../src/dispatch-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dispatch-adapter.d.ts","sourceRoot":"","sources":["../src/dispatch-adapter.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,8 @@ export type ParallGatewayOptions = {
35
35
  runtimeLaneKey: string;
36
36
  }) => Promise<void> | void;
37
37
  onBeforeDisconnect?: () => Promise<void> | void;
38
+ onNewSession?: (previousSessionId: string) => Promise<void> | void;
39
+ onSessionStale?: (sessionKey: string) => Promise<void> | void;
38
40
  };
39
41
  export declare function parseShutdownDeadlineMs(raw: string | undefined): number | undefined;
40
42
  export declare class ParallAgentGateway {
@@ -56,6 +58,7 @@ export declare class ParallAgentGateway {
56
58
  private shuttingDown;
57
59
  private inFlightDispatches;
58
60
  private drainResolvers;
61
+ private pendingRestartNotification;
59
62
  private readonly DISPATCHED_MESSAGES_CAP;
60
63
  private readonly COLD_START_WINDOW_MS;
61
64
  private readonly SHUTDOWN_DEADLINE_MS;
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import { MENTION_ALL_USER_ID } from "@parall/sdk";
5
5
  import { buildEventBody, buildEventBodyForForkResult, buildForkResultPrefix, buildForkScopePrefix } from "./event-format.js";
6
6
  import { routeTrigger } from "./routing.js";
7
7
  import { clearDispatchMessageId, clearDispatchNoReply, clearSessionMessageId, setDispatchMessageId, setDispatchNoReply, setSessionChatId, setSessionMessageId, } from "./session-state.js";
8
+ const LIVE_SESSION_STATUSES = new Set(["open", "active", "idle"]);
8
9
  // Parse PRLL_SHUTDOWN_DEADLINE_MS (or any string env value) into a positive
9
10
  // integer milliseconds value, or undefined if unset/invalid. Runtimes pass
10
11
  // the result into ParallGatewayOptions.shutdownDeadlineMs; leaving it
@@ -78,6 +79,7 @@ export class ParallAgentGateway {
78
79
  shuttingDown = false;
79
80
  inFlightDispatches = 0;
80
81
  drainResolvers = [];
82
+ pendingRestartNotification = null;
81
83
  DISPATCHED_MESSAGES_CAP = 5000;
82
84
  COLD_START_WINDOW_MS;
83
85
  // SHUTDOWN_DEADLINE_MS is read by waitForDrain via the configured value
@@ -92,7 +94,7 @@ export class ParallAgentGateway {
92
94
  async run(abortSignal) {
93
95
  const { ws, log } = this.opts;
94
96
  ws.onStateChange((state) => {
95
- log?.info(`parall[${this.opts.accountId}]: connection state → ${state}`);
97
+ log?.info(`connection state → ${state}`);
96
98
  });
97
99
  ws.on("hello", async (data) => {
98
100
  await this.handleHello(data);
@@ -126,17 +128,32 @@ export class ParallAgentGateway {
126
128
  await this.handleMessage(data);
127
129
  });
128
130
  ws.on("agent_config.update", async (data) => {
129
- this.opts.log?.info(`parall[${this.opts.accountId}]: config update notification (version=${data.version})`);
131
+ this.opts.log?.info(`config update notification (version=${data.version})`);
130
132
  try {
131
133
  await this.opts.onConfigUpdate?.(data);
132
134
  }
133
135
  catch (err) {
134
- this.opts.log?.warn(`parall[${this.opts.accountId}]: config update failed: ${String(err)}`);
136
+ this.opts.log?.warn(`config update failed: ${String(err)}`);
137
+ }
138
+ });
139
+ ws.on("agent.new_session", async (data) => {
140
+ const prevId = data.previous_session_id ?? "";
141
+ this.opts.log?.info(`new session signal received (previous=${prevId})`);
142
+ this.sessionBindings.clear();
143
+ if (prevId) {
144
+ this.pendingRestartNotification =
145
+ `[Harness Notification] This is a fresh session. Your previous session (${prevId}) was ended by the user and you have been restarted.`;
146
+ }
147
+ try {
148
+ await this.opts.onNewSession?.(prevId);
149
+ }
150
+ catch (err) {
151
+ this.opts.log?.warn(`onNewSession callback failed: ${String(err)}`);
135
152
  }
136
153
  });
137
154
  ws.on("recovery.overflow", () => {
138
- this.opts.log?.warn(`parall[${this.opts.accountId}]: recovery.overflow — triggering full catch-up`);
139
- this.catchUpFromDispatch().catch((err) => this.opts.log?.warn(`parall[${this.opts.accountId}]: overflow catch-up failed: ${String(err)}`));
155
+ this.opts.log?.warn(`recovery.overflow — triggering full catch-up`);
156
+ this.catchUpFromDispatch().catch((err) => this.opts.log?.warn(`overflow catch-up failed: ${String(err)}`));
140
157
  });
141
158
  ws.on("task.assigned", async (data) => {
142
159
  if (data.assignee_id !== this.opts.agentUserId)
@@ -150,7 +167,7 @@ export class ParallAgentGateway {
150
167
  }
151
168
  }
152
169
  catch (err) {
153
- this.opts.log?.error(`parall[${this.opts.accountId}]: task dispatch failed for ${data.id}: ${String(err)}`);
170
+ this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
154
171
  }
155
172
  });
156
173
  ws.on("dispatch.new", async (data) => {
@@ -164,7 +181,7 @@ export class ParallAgentGateway {
164
181
  }
165
182
  }
166
183
  catch (err) {
167
- this.opts.log?.error(`parall[${this.opts.accountId}]: task comment dispatch failed for ${data.source_id}: ${String(err)}`);
184
+ this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
168
185
  }
169
186
  }
170
187
  else if (data.event_type === "task_update") {
@@ -177,7 +194,7 @@ export class ParallAgentGateway {
177
194
  }
178
195
  }
179
196
  catch (err) {
180
- this.opts.log?.error(`parall[${this.opts.accountId}]: task update dispatch failed for ${data.task_id}: ${String(err)}`);
197
+ this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
181
198
  }
182
199
  }
183
200
  else if (data.event_type === "schedule.fire") {
@@ -190,7 +207,7 @@ export class ParallAgentGateway {
190
207
  }
191
208
  }
192
209
  catch (err) {
193
- this.opts.log?.error(`parall[${this.opts.accountId}]: schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
210
+ this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
194
211
  }
195
212
  }
196
213
  else if (data.event_type === "approval_decided") {
@@ -203,7 +220,7 @@ export class ParallAgentGateway {
203
220
  }
204
221
  }
205
222
  catch (err) {
206
- this.opts.log?.error(`parall[${this.opts.accountId}]: approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
223
+ this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
207
224
  }
208
225
  }
209
226
  else if (data.event_type !== "message" && data.event_type !== "task_assign") {
@@ -214,10 +231,10 @@ export class ParallAgentGateway {
214
231
  // (message.new, task.assigned) above and would otherwise spam
215
232
  // info-level logs for every inbound chat message / task
216
233
  // assignment on a busy agent.
217
- this.opts.log?.info(`parall[${this.opts.accountId}]: dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) — no-op`);
234
+ this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) — no-op`);
218
235
  }
219
236
  });
220
- this.opts.log?.info(`parall[${this.opts.accountId}]: connecting to ${this.opts.connectionLabel ?? "Parall WS"}...`);
237
+ this.opts.log?.info(`connecting to ${this.opts.connectionLabel ?? "Parall WS"}...`);
221
238
  await ws.connect();
222
239
  return new Promise((resolve) => {
223
240
  abortSignal.addEventListener("abort", async () => {
@@ -341,7 +358,7 @@ export class ParallAgentGateway {
341
358
  });
342
359
  }
343
360
  catch (err) {
344
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to create input step: ${String(err)}`);
361
+ this.opts.log?.warn(`failed to create input step: ${String(err)}`);
345
362
  }
346
363
  }
347
364
  async createRuntimeStep(sessionId, event, runtimeEvent, stepIdFilePath, contextFilePath) {
@@ -421,13 +438,13 @@ export class ParallAgentGateway {
421
438
  target_type: target.target_type,
422
439
  target_id: target.target_id,
423
440
  content: { text: runtimeEvent.message, suppressed: false },
424
- projection: true,
441
+ projection: false,
425
442
  });
426
443
  break;
427
444
  }
428
445
  }
429
446
  catch (err) {
430
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to create ${runtimeEvent.type} step: ${String(err)}`);
447
+ this.opts.log?.warn(`failed to create ${runtimeEvent.type} step: ${String(err)}`);
431
448
  }
432
449
  }
433
450
  writeContextFile(filePath, ctx) {
@@ -436,7 +453,7 @@ export class ParallAgentGateway {
436
453
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
437
454
  }
438
455
  catch (err) {
439
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to write context file ${filePath}: ${String(err)}`);
456
+ this.opts.log?.warn(`failed to write context file ${filePath}: ${String(err)}`);
440
457
  }
441
458
  }
442
459
  updateContextFileStepId(filePath, stepId) {
@@ -447,7 +464,7 @@ export class ParallAgentGateway {
447
464
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
448
465
  }
449
466
  catch (err) {
450
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file step_id ${filePath}: ${String(err)}`);
467
+ this.opts.log?.warn(`failed to update context file step_id ${filePath}: ${String(err)}`);
451
468
  }
452
469
  }
453
470
  updateContextFileSessionId(filePath, sessionId) {
@@ -458,7 +475,7 @@ export class ParallAgentGateway {
458
475
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
459
476
  }
460
477
  catch (err) {
461
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file session_id ${filePath}: ${String(err)}`);
478
+ this.opts.log?.warn(`failed to update context file session_id ${filePath}: ${String(err)}`);
462
479
  }
463
480
  }
464
481
  /** @deprecated Use writeContextFile / updateContextFileStepId. */
@@ -468,7 +485,7 @@ export class ParallAgentGateway {
468
485
  fs.writeFileSync(filePath, stepId, "utf8");
469
486
  }
470
487
  catch (err) {
471
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to write step id file ${filePath}: ${String(err)}`);
488
+ this.opts.log?.warn(`failed to write step id file ${filePath}: ${String(err)}`);
472
489
  }
473
490
  }
474
491
  /** @deprecated Use writeContextFile / updateContextFileStepId. */
@@ -508,6 +525,18 @@ export class ParallAgentGateway {
508
525
  parent_session_id: parentSessionId,
509
526
  runtime_ref: Object.keys(runtimeRef).length > 0 ? runtimeRef : undefined,
510
527
  });
528
+ if (!LIVE_SESSION_STATUSES.has(session.status)) {
529
+ this.opts.log?.warn?.(`createAgentSession returned terminal session ${session.id} (${session.status}), resetting runtime for ${sessionKey}`);
530
+ this.sessionBindings.delete(sessionKey);
531
+ try {
532
+ await this.opts.onSessionStale?.(sessionKey);
533
+ }
534
+ catch (e) {
535
+ this.opts.log?.warn?.(`onSessionStale failed: ${e}`);
536
+ }
537
+ this.opts.log?.info?.(`stale session self-heal complete for ${sessionKey} — next dispatch will create a fresh session`);
538
+ throw new Error(`Agent session ${session.id} is terminal (${session.status})`);
539
+ }
511
540
  const binding = {
512
541
  sessionKey,
513
542
  agentSessionId: session.id,
@@ -534,9 +563,13 @@ export class ParallAgentGateway {
534
563
  // In-flight dispatches that started before the flag flipped keep running
535
564
  // and are awaited by shutdown() up to SHUTDOWN_DEADLINE_MS.
536
565
  if (this.shuttingDown) {
537
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping dispatch for ${event.messageId} (shutting down) — leaving unacked for catch-up on replacement pod`);
566
+ this.opts.log?.info(`skipping dispatch for ${event.messageId} (shutting down) — leaving unacked for catch-up on replacement pod`);
538
567
  return false;
539
568
  }
569
+ if (this.pendingRestartNotification) {
570
+ bodyForAgent = this.pendingRestartNotification + "\n\n---\n\n" + bodyForAgent;
571
+ this.pendingRestartNotification = null;
572
+ }
540
573
  setSessionChatId(sessionKey, event.targetId);
541
574
  setSessionMessageId(sessionKey, event.messageId);
542
575
  setDispatchMessageId(sessionKey, event.messageId);
@@ -685,7 +718,7 @@ export class ParallAgentGateway {
685
718
  }
686
719
  }
687
720
  catch (err) {
688
- this.opts.log?.error(`parall[${this.opts.accountId}]: fork dispatch failed for ${last.messageId}: ${String(err)}`);
721
+ this.opts.log?.error(`fork dispatch failed for ${last.messageId}: ${String(err)}`);
689
722
  for (const item of items) {
690
723
  item.resolve(false);
691
724
  }
@@ -705,7 +738,7 @@ export class ParallAgentGateway {
705
738
  historyPath = this.opts.dispatchAdapter.getSessionHistoryPath?.(fork.fork.sessionKey);
706
739
  }
707
740
  catch (err) {
708
- this.opts.log?.warn?.(`parall[${this.opts.accountId}]: failed to resolve fork history path: ${String(err)}`);
741
+ this.opts.log?.warn?.(`failed to resolve fork history path: ${String(err)}`);
709
742
  }
710
743
  this.dispatchState.pendingForkResults.push({
711
744
  forkSessionKey: fork.fork.sessionKey,
@@ -761,7 +794,7 @@ export class ParallAgentGateway {
761
794
  // pendingForkResults intact so dispatch catch-up on the replacement
762
795
  // pod is the single source of truth for replay.
763
796
  if (this.shuttingDown) {
764
- this.opts.log?.info(`parall[${this.opts.accountId}]: drainMainBuffer halted (shutting down) — ${this.dispatchState.mainBuffer.length} buffered, ${this.dispatchState.pendingForkResults.length} pending fork results left for catch-up`);
797
+ this.opts.log?.info(`drainMainBuffer halted (shutting down) — ${this.dispatchState.mainBuffer.length} buffered, ${this.dispatchState.pendingForkResults.length} pending fork results left for catch-up`);
765
798
  break;
766
799
  }
767
800
  const targetId = this.dispatchState.mainBuffer[0].targetId;
@@ -842,7 +875,7 @@ export class ParallAgentGateway {
842
875
  if (this.dispatchState.mainCurrentTargetId === event.targetId &&
843
876
  await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
844
877
  this.startInjectedTyping(event);
845
- this.opts.log?.info(`parall[${this.opts.accountId}]: steer injected for ${event.messageId} (will drain for bookkeeping)`);
878
+ this.opts.log?.info(`steer injected for ${event.messageId} (will drain for bookkeeping)`);
846
879
  }
847
880
  // If the main dispatch cycle ended while we awaited the steer RPC,
848
881
  // our event is buffered but no drain is in flight. Re-enter the
@@ -874,7 +907,7 @@ export class ParallAgentGateway {
874
907
  preDispatchBranchPoint: this.dispatchState.mainPreDispatchBranchPoint,
875
908
  });
876
909
  if (!fork) {
877
- this.opts.log?.warn(`parall[${this.opts.accountId}]: fork failed, buffering event for main session`);
910
+ this.opts.log?.warn(`fork failed, buffering event for main session`);
878
911
  this.dispatchState.mainBuffer.push(event);
879
912
  return false;
880
913
  }
@@ -890,7 +923,7 @@ export class ParallAgentGateway {
890
923
  activeFork.queue.push({ event, resolve });
891
924
  });
892
925
  this.runForkDrainLoop(activeFork).catch((err) => {
893
- this.opts.log?.error(`parall[${this.opts.accountId}]: fork drain loop error: ${String(err)}`);
926
+ this.opts.log?.error(`fork drain loop error: ${String(err)}`);
894
927
  });
895
928
  return firstEventPromise;
896
929
  }
@@ -911,7 +944,7 @@ export class ParallAgentGateway {
911
944
  return chatInfo;
912
945
  }
913
946
  catch (err) {
914
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to resolve chat ${chatId}: ${String(err)}`);
947
+ this.opts.log?.warn(`failed to resolve chat ${chatId}: ${String(err)}`);
915
948
  return null;
916
949
  }
917
950
  }
@@ -985,7 +1018,7 @@ export class ParallAgentGateway {
985
1018
  }
986
1019
  }
987
1020
  catch (err) {
988
- this.opts.log?.error(`parall[${this.opts.accountId}]: event dispatch failed for ${data.id}: ${String(err)}`);
1021
+ this.opts.log?.error(`event dispatch failed for ${data.id}: ${String(err)}`);
989
1022
  this.dispatchedMessages.delete(data.id);
990
1023
  }
991
1024
  }
@@ -994,11 +1027,11 @@ export class ParallAgentGateway {
994
1027
  return false; // drain window — let server requeue via catch-up
995
1028
  const dedupeKey = `${task.id}:${task.updated_at}`;
996
1029
  if (this.dispatchedTasks.has(dedupeKey)) {
997
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping already-dispatched task ${task.identifier ?? task.id}`);
1030
+ this.opts.log?.info(`skipping already-dispatched task ${task.identifier ?? task.id}`);
998
1031
  return false;
999
1032
  }
1000
1033
  this.dispatchedTasks.add(dedupeKey);
1001
- this.opts.log?.info(`parall[${this.opts.accountId}]: task assigned: ${task.identifier ?? task.id} "${task.title}"`);
1034
+ this.opts.log?.info(`task assigned: ${task.identifier ?? task.id} "${task.title}"`);
1002
1035
  const parts = [`Title: ${task.title}`];
1003
1036
  parts.push(`Status: ${task.status}`, `Priority: ${task.priority}`);
1004
1037
  if (task.project_id)
@@ -1040,7 +1073,7 @@ export class ParallAgentGateway {
1040
1073
  const isAssignee = task.assignee_id === this.opts.agentUserId;
1041
1074
  const isCreatorUpdate = opts.allowCreator === true && task.creator_id === this.opts.agentUserId;
1042
1075
  if (!isAssignee && !isCreatorUpdate) {
1043
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping stale task dispatch ${ackSourceId ?? taskId} — assigned to ${task.assignee_id}, creator ${task.creator_id}`);
1076
+ this.opts.log?.info(`skipping stale task dispatch ${ackSourceId ?? taskId} — assigned to ${task.assignee_id}, creator ${task.creator_id}`);
1044
1077
  return true;
1045
1078
  }
1046
1079
  return this.handleTaskAssignment(task, ackSourceId);
@@ -1060,7 +1093,7 @@ export class ParallAgentGateway {
1060
1093
  const status = err?.status;
1061
1094
  if (status === 404) {
1062
1095
  // Comment deleted — ack stale dispatch so it doesn't pile up.
1063
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping deleted comment ${commentId}, acking stale dispatch`);
1096
+ this.opts.log?.info(`skipping deleted comment ${commentId}, acking stale dispatch`);
1064
1097
  this.dispatchedTasks.delete(dedupeKey);
1065
1098
  return true; // caller will ack
1066
1099
  }
@@ -1072,7 +1105,7 @@ export class ParallAgentGateway {
1072
1105
  return true; // null response = gone, ack stale dispatch
1073
1106
  }
1074
1107
  if (comment.hints?.no_reply) {
1075
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping no_reply task comment ${commentId}, acking stale dispatch`);
1108
+ this.opts.log?.info(`skipping no_reply task comment ${commentId}, acking stale dispatch`);
1076
1109
  this.dispatchedTasks.delete(dedupeKey);
1077
1110
  return true;
1078
1111
  }
@@ -1082,7 +1115,7 @@ export class ParallAgentGateway {
1082
1115
  }
1083
1116
  catch { /* task context is optional */ }
1084
1117
  const taskLabel = task ? `${task.identifier ?? task.id} "${task.title}"` : taskId;
1085
- this.opts.log?.info(`parall[${this.opts.accountId}]: task comment on ${taskLabel} by ${actorId ?? "unknown"}`);
1118
+ this.opts.log?.info(`task comment on ${taskLabel} by ${actorId ?? "unknown"}`);
1086
1119
  const parts = [];
1087
1120
  if (task) {
1088
1121
  parts.push(`Task: ${task.title} (prll://${task.id})`);
@@ -1141,10 +1174,10 @@ export class ParallAgentGateway {
1141
1174
  // already-delivered run — this is expected but operationally
1142
1175
  // visible, so warn (not info) keeps it findable during incident
1143
1176
  // triage. See docs/primitives/schedule.md § Snapshot 不变性.
1144
- this.opts.log?.warn(`parall[${this.opts.accountId}]: schedule run ${runId} not accessible (404), acking stale dispatch`);
1177
+ this.opts.log?.warn(`schedule run ${runId} not accessible (404), acking stale dispatch`);
1145
1178
  return true;
1146
1179
  }
1147
- this.opts.log?.warn(`parall[${this.opts.accountId}]: schedule run fetch failed for ${runId}, leaving pending: ${String(err)}`);
1180
+ this.opts.log?.warn(`schedule run fetch failed for ${runId}, leaving pending: ${String(err)}`);
1148
1181
  return false;
1149
1182
  }
1150
1183
  if (!run)
@@ -1158,7 +1191,7 @@ export class ParallAgentGateway {
1158
1191
  if (this.dispatchedTasks.has(dedupeKey))
1159
1192
  return false;
1160
1193
  this.dispatchedTasks.add(dedupeKey);
1161
- this.opts.log?.info(`parall[${this.opts.accountId}]: schedule fired: ${run.id} (schedule ${run.schedule_id})`);
1194
+ this.opts.log?.info(`schedule fired: ${run.id} (schedule ${run.schedule_id})`);
1162
1195
  const event = {
1163
1196
  type: "schedule",
1164
1197
  // Route by schedule_id (not attached chat_id) so concurrent fires of
@@ -1197,10 +1230,10 @@ export class ParallAgentGateway {
1197
1230
  catch (err) {
1198
1231
  const status = err?.status;
1199
1232
  if (status === 404 || status === 403) {
1200
- this.opts.log?.warn(`parall[${this.opts.accountId}]: approval ${approvalId} not accessible (${status}), acking stale dispatch`);
1233
+ this.opts.log?.warn(`approval ${approvalId} not accessible (${status}), acking stale dispatch`);
1201
1234
  return true;
1202
1235
  }
1203
- this.opts.log?.warn(`parall[${this.opts.accountId}]: approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
1236
+ this.opts.log?.warn(`approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
1204
1237
  return false;
1205
1238
  }
1206
1239
  if (!approval)
@@ -1211,7 +1244,7 @@ export class ParallAgentGateway {
1211
1244
  if (this.dispatchedTasks.has(dedupeKey))
1212
1245
  return false;
1213
1246
  this.dispatchedTasks.add(dedupeKey);
1214
- this.opts.log?.info(`parall[${this.opts.accountId}]: approval decided: ${approval.id} (${approval.status})`);
1247
+ this.opts.log?.info(`approval decided: ${approval.id} (${approval.status})`);
1215
1248
  const statusLabel = approval.status === "approved" ? "Approved" : "Rejected";
1216
1249
  const execInfo = approval.execution_status ? ` | execution: ${approval.execution_status}` : "";
1217
1250
  const body = `${statusLabel}: ${approval.title}${execInfo}`;
@@ -1264,7 +1297,7 @@ export class ParallAgentGateway {
1264
1297
  dispatched = await this.handleTaskDispatch(item.task_id, item.source_id ?? item.task_id);
1265
1298
  }
1266
1299
  catch (err) {
1267
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
1300
+ this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
1268
1301
  continue;
1269
1302
  }
1270
1303
  }
@@ -1273,7 +1306,7 @@ export class ParallAgentGateway {
1273
1306
  dispatched = await this.handleTaskDispatch(item.task_id, item.source_id ?? item.task_id, { allowCreator: true });
1274
1307
  }
1275
1308
  catch (err) {
1276
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
1309
+ this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
1277
1310
  continue;
1278
1311
  }
1279
1312
  }
@@ -1301,7 +1334,7 @@ export class ParallAgentGateway {
1301
1334
  }
1302
1335
  else {
1303
1336
  msgFetchFailed = true;
1304
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
1337
+ this.opts.log?.warn(`catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
1305
1338
  }
1306
1339
  }
1307
1340
  if (msgFetchFailed) {
@@ -1330,7 +1363,7 @@ export class ParallAgentGateway {
1330
1363
  }
1331
1364
  }
1332
1365
  catch (err) {
1333
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up dispatch ${item.id} (${item.event_type}) failed: ${String(err)}`);
1366
+ this.opts.log?.warn(`catch-up dispatch ${item.id} (${item.event_type}) failed: ${String(err)}`);
1334
1367
  }
1335
1368
  }
1336
1369
  // Stop paginating once shutdown begins — the inner loop already broke,
@@ -1338,7 +1371,7 @@ export class ParallAgentGateway {
1338
1371
  cursor = !this.shuttingDown && page.has_more ? page.next_cursor : undefined;
1339
1372
  } while (cursor);
1340
1373
  if (processed > 0 || skippedOld > 0) {
1341
- this.opts.log?.info(`parall[${this.opts.accountId}]: dispatch catch-up: processed ${processed}, skipped ${skippedOld} old item(s)`);
1374
+ this.opts.log?.info(`dispatch catch-up: processed ${processed}, skipped ${skippedOld} old item(s)`);
1342
1375
  }
1343
1376
  }
1344
1377
  async handleHello(data) {
@@ -1347,7 +1380,7 @@ export class ParallAgentGateway {
1347
1380
  const intervalSec = data.heartbeat_interval > 0 ? data.heartbeat_interval : 30;
1348
1381
  try {
1349
1382
  const count = await fetchAllChats(client, config.org_id, this.chatInfoMap);
1350
- log?.info(`parall[${this.opts.accountId}]: WebSocket connected, ${count} chats cached`);
1383
+ log?.info(`WebSocket connected, ${count} chats cached`);
1351
1384
  await this.opts.onSessionReady?.({
1352
1385
  activeSessionId: this.activeSessionId,
1353
1386
  ws: this.opts.ws,
@@ -1361,7 +1394,7 @@ export class ParallAgentGateway {
1361
1394
  const expectedMs = intervalSec * 1000;
1362
1395
  const drift = now - this.lastHeartbeatAt - expectedMs;
1363
1396
  if (drift > 15000) {
1364
- log?.warn(`parall[${this.opts.accountId}]: heartbeat drift ${drift}ms — event loop may be blocked`);
1397
+ log?.warn(`heartbeat drift ${drift}ms — event loop may be blocked`);
1365
1398
  }
1366
1399
  this.lastHeartbeatAt = now;
1367
1400
  if (this.opts.ws.state !== "connected")
@@ -1377,11 +1410,11 @@ export class ParallAgentGateway {
1377
1410
  const isFirstHello = !this.hadSuccessfulHello;
1378
1411
  this.hadSuccessfulHello = true;
1379
1412
  this.catchUpFromDispatch(isFirstHello).catch((err) => {
1380
- log?.warn(`parall[${this.opts.accountId}]: dispatch catch-up failed: ${String(err)}`);
1413
+ log?.warn(`dispatch catch-up failed: ${String(err)}`);
1381
1414
  });
1382
1415
  }
1383
1416
  catch (err) {
1384
- log?.error(`parall[${this.opts.accountId}]: failed to fetch chats: ${String(err)}`);
1417
+ log?.error(`failed to fetch chats: ${String(err)}`);
1385
1418
  }
1386
1419
  }
1387
1420
  // Resolves when in-flight dispatches hit 0 or the deadline elapses.
@@ -1414,13 +1447,13 @@ export class ParallAgentGateway {
1414
1447
  // the process exit — that's acceptable because K8s will retry the event
1415
1448
  // via dispatch catch-up after the new pod starts.
1416
1449
  if (this.inFlightDispatches > 0) {
1417
- this.opts.log?.info(`parall[${this.opts.accountId}]: draining ${this.inFlightDispatches} in-flight dispatch(es), deadline ${this.SHUTDOWN_DEADLINE_MS}ms`);
1450
+ this.opts.log?.info(`draining ${this.inFlightDispatches} in-flight dispatch(es), deadline ${this.SHUTDOWN_DEADLINE_MS}ms`);
1418
1451
  await this.waitForDrain(this.SHUTDOWN_DEADLINE_MS);
1419
1452
  if (this.inFlightDispatches > 0) {
1420
- this.opts.log?.warn(`parall[${this.opts.accountId}]: drain deadline hit; ${this.inFlightDispatches} dispatch(es) still running — they will be killed by process exit`);
1453
+ this.opts.log?.warn(`drain deadline hit; ${this.inFlightDispatches} dispatch(es) still running — they will be killed by process exit`);
1421
1454
  }
1422
1455
  else {
1423
- this.opts.log?.info(`parall[${this.opts.accountId}]: drain complete`);
1456
+ this.opts.log?.info(`drain complete`);
1424
1457
  }
1425
1458
  }
1426
1459
  if (this.heartbeatTimer)
@@ -1431,6 +1464,6 @@ export class ParallAgentGateway {
1431
1464
  this.activeDispatches.clear();
1432
1465
  await this.opts.onBeforeDisconnect?.();
1433
1466
  this.opts.ws.disconnect();
1434
- this.opts.log?.info(`parall[${this.opts.accountId}]: disconnected`);
1467
+ this.opts.log?.info(`disconnected`);
1435
1468
  }
1436
1469
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./provider-config.js";
1
2
  export * from "./types.js";
2
3
  export * from "./session-state.js";
3
4
  export * from "./routing.js";
@@ -5,7 +6,7 @@ export * from "./event-format.js";
5
6
  export * from "./prompt-fragments.js";
6
7
  export * from "./bridge-workspace.js";
7
8
  export * from "./dispatch-adapter.js";
8
- export { createLogger } from "./logger.js";
9
+ export { createLogger, childLogger } from "./logger.js";
9
10
  export * from "./gateway-base.js";
10
11
  export * from "./platform-config.js";
11
12
  export { writeSkillFiles, buildSkillReferences, SKILLS } from "./skills/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAClF,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAClF,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./provider-config.js";
1
2
  export * from "./types.js";
2
3
  export * from "./session-state.js";
3
4
  export * from "./routing.js";
@@ -5,7 +6,7 @@ export * from "./event-format.js";
5
6
  export * from "./prompt-fragments.js";
6
7
  export * from "./bridge-workspace.js";
7
8
  export * from "./dispatch-adapter.js";
8
- export { createLogger } from "./logger.js";
9
+ export { createLogger, childLogger } from "./logger.js";
9
10
  export * from "./gateway-base.js";
10
11
  export * from "./platform-config.js";
11
12
  export { writeSkillFiles, buildSkillReferences, SKILLS } from "./skills/index.js";
package/dist/logger.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { GatewayLogger } from "./dispatch-adapter.js";
2
2
  export declare function createLogger(prefix: string): GatewayLogger;
3
+ export declare function childLogger(logger: GatewayLogger, sub: string): GatewayLogger;
3
4
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAM1D"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAO1D;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAE7E"}
package/dist/logger.js CHANGED
@@ -3,5 +3,9 @@ export function createLogger(prefix) {
3
3
  info: (msg) => console.log(`${new Date().toISOString()} [${prefix}] ${msg}`),
4
4
  warn: (msg) => console.warn(`${new Date().toISOString()} [${prefix}] ${msg}`),
5
5
  error: (msg) => console.error(`${new Date().toISOString()} [${prefix}] ${msg}`),
6
+ child: (sub) => createLogger(`${prefix}:${sub}`),
6
7
  };
7
8
  }
9
+ export function childLogger(logger, sub) {
10
+ return logger.child ? logger.child(sub) : logger;
11
+ }
@@ -0,0 +1,11 @@
1
+ export interface ProviderConfig {
2
+ llm_source?: string;
3
+ openai_api_key?: string;
4
+ openai_base_url?: string;
5
+ anthropic_auth_token?: string;
6
+ anthropic_base_url?: string;
7
+ }
8
+ export declare function llmSource(pc?: ProviderConfig): string;
9
+ export declare function clearAllProviderCreds(env: NodeJS.ProcessEnv): void;
10
+ export declare function parseProviderConfig(env: NodeJS.ProcessEnv): ProviderConfig | undefined;
11
+ //# sourceMappingURL=provider-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../src/provider-config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,SAAS,CAAC,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAWrD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,IAAI,CAOlE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,cAAc,GAAG,SAAS,CAQtF"}
@@ -0,0 +1,30 @@
1
+ export function llmSource(pc) {
2
+ if (pc?.llm_source)
3
+ return pc.llm_source;
4
+ if (pc?.openai_api_key ||
5
+ pc?.openai_base_url ||
6
+ pc?.anthropic_auth_token ||
7
+ pc?.anthropic_base_url) {
8
+ return "custom";
9
+ }
10
+ return "parall";
11
+ }
12
+ export function clearAllProviderCreds(env) {
13
+ delete env.ANTHROPIC_AUTH_TOKEN;
14
+ delete env.ANTHROPIC_BASE_URL;
15
+ delete env.ANTHROPIC_API_KEY;
16
+ delete env.OPENAI_API_KEY;
17
+ delete env.OPENAI_BASE_URL;
18
+ delete env.PRLL_CLAUDE_ALLOW_API_KEY;
19
+ }
20
+ export function parseProviderConfig(env) {
21
+ const raw = env.PRLL_PROVIDER_CONFIG?.trim();
22
+ if (!raw)
23
+ return undefined;
24
+ try {
25
+ return JSON.parse(raw);
26
+ }
27
+ catch (err) {
28
+ throw new Error(`Invalid PRLL_PROVIDER_CONFIG JSON: ${String(err)}`);
29
+ }
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/agent-core",
3
- "version": "1.29.3",
3
+ "version": "1.31.0",
4
4
  "description": "Shared agent runtime orchestration helpers for Parall",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -26,7 +26,7 @@
26
26
  "src"
27
27
  ],
28
28
  "dependencies": {
29
- "@parall/sdk": "1.29.3"
29
+ "@parall/sdk": "1.31.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.0.0",
@@ -5,6 +5,7 @@ export type GatewayLogger = {
5
5
  info: (msg: string) => void;
6
6
  warn: (msg: string) => void;
7
7
  error: (msg: string) => void;
8
+ child?: (sub: string) => GatewayLogger;
8
9
  };
9
10
 
10
11
  export type DispatchContext = {
@@ -4,6 +4,7 @@ import * as path from "node:path";
4
4
  import { MENTION_ALL_USER_ID, ParallClient, ParallWs } from "@parall/sdk";
5
5
  import type {
6
6
  AgentConfigUpdateData,
7
+ AgentNewSessionData,
7
8
  AgentSessionDB,
8
9
  Approval,
9
10
  Chat,
@@ -39,6 +40,8 @@ import {
39
40
  } from "./session-state.js";
40
41
  import type { DispatchState, ParallEvent } from "./types.js";
41
42
 
43
+ const LIVE_SESSION_STATUSES = new Set(["open", "active", "idle"]);
44
+
42
45
  type ChatInfo = {
43
46
  type: Chat["type"];
44
47
  name: string | null;
@@ -107,6 +110,8 @@ export type ParallGatewayOptions = {
107
110
  onSessionReady?: (state: { activeSessionId?: string; ws: ParallWs; runtimeKey: string }) => Promise<void> | void;
108
111
  onSessionBinding?: (state: { sessionKey: string; agentSessionId: string; runtimeSessionId: string; runtimeLaneKey: string }) => Promise<void> | void;
109
112
  onBeforeDisconnect?: () => Promise<void> | void;
113
+ onNewSession?: (previousSessionId: string) => Promise<void> | void;
114
+ onSessionStale?: (sessionKey: string) => Promise<void> | void;
110
115
  };
111
116
 
112
117
  type AgentSessionBinding = {
@@ -196,6 +201,7 @@ export class ParallAgentGateway {
196
201
  private shuttingDown = false;
197
202
  private inFlightDispatches = 0;
198
203
  private drainResolvers: Array<() => void> = [];
204
+ private pendingRestartNotification: string | null = null;
199
205
 
200
206
  private readonly DISPATCHED_MESSAGES_CAP = 5000;
201
207
  private readonly COLD_START_WINDOW_MS: number;
@@ -213,7 +219,7 @@ export class ParallAgentGateway {
213
219
  const { ws, log } = this.opts;
214
220
 
215
221
  ws.onStateChange((state) => {
216
- log?.info(`parall[${this.opts.accountId}]: connection state → ${state}`);
222
+ log?.info(`connection state → ${state}`);
217
223
  });
218
224
 
219
225
  ws.on("hello", async (data: HelloData) => {
@@ -249,18 +255,33 @@ export class ParallAgentGateway {
249
255
  });
250
256
 
251
257
  ws.on("agent_config.update", async (data: AgentConfigUpdateData) => {
252
- this.opts.log?.info(`parall[${this.opts.accountId}]: config update notification (version=${data.version})`);
258
+ this.opts.log?.info(`config update notification (version=${data.version})`);
253
259
  try {
254
260
  await this.opts.onConfigUpdate?.(data);
255
261
  } catch (err) {
256
- this.opts.log?.warn(`parall[${this.opts.accountId}]: config update failed: ${String(err)}`);
262
+ this.opts.log?.warn(`config update failed: ${String(err)}`);
263
+ }
264
+ });
265
+
266
+ ws.on("agent.new_session", async (data: AgentNewSessionData) => {
267
+ const prevId = data.previous_session_id ?? "";
268
+ this.opts.log?.info(`new session signal received (previous=${prevId})`);
269
+ this.sessionBindings.clear();
270
+ if (prevId) {
271
+ this.pendingRestartNotification =
272
+ `[Harness Notification] This is a fresh session. Your previous session (${prevId}) was ended by the user and you have been restarted.`;
273
+ }
274
+ try {
275
+ await this.opts.onNewSession?.(prevId);
276
+ } catch (err) {
277
+ this.opts.log?.warn(`onNewSession callback failed: ${String(err)}`);
257
278
  }
258
279
  });
259
280
 
260
281
  ws.on("recovery.overflow", () => {
261
- this.opts.log?.warn(`parall[${this.opts.accountId}]: recovery.overflow — triggering full catch-up`);
282
+ this.opts.log?.warn(`recovery.overflow — triggering full catch-up`);
262
283
  this.catchUpFromDispatch().catch((err) =>
263
- this.opts.log?.warn(`parall[${this.opts.accountId}]: overflow catch-up failed: ${String(err)}`));
284
+ this.opts.log?.warn(`overflow catch-up failed: ${String(err)}`));
264
285
  });
265
286
 
266
287
  ws.on("task.assigned", async (data: TaskAssignedData) => {
@@ -272,7 +293,7 @@ export class ParallAgentGateway {
272
293
  this.opts.client.ackDispatch(this.opts.config.org_id, { source_type: "task_activity", source_id: data.id }).catch(() => {});
273
294
  }
274
295
  } catch (err) {
275
- this.opts.log?.error(`parall[${this.opts.accountId}]: task dispatch failed for ${data.id}: ${String(err)}`);
296
+ this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
276
297
  }
277
298
  });
278
299
 
@@ -285,7 +306,7 @@ export class ParallAgentGateway {
285
306
  this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
286
307
  }
287
308
  } catch (err) {
288
- this.opts.log?.error(`parall[${this.opts.accountId}]: task comment dispatch failed for ${data.source_id}: ${String(err)}`);
309
+ this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
289
310
  }
290
311
  } else if (data.event_type === "task_update") {
291
312
  if (!data.task_id) return;
@@ -295,7 +316,7 @@ export class ParallAgentGateway {
295
316
  this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
296
317
  }
297
318
  } catch (err) {
298
- this.opts.log?.error(`parall[${this.opts.accountId}]: task update dispatch failed for ${data.task_id}: ${String(err)}`);
319
+ this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
299
320
  }
300
321
  } else if (data.event_type === "schedule.fire") {
301
322
  if (!data.source_id) return;
@@ -305,7 +326,7 @@ export class ParallAgentGateway {
305
326
  this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
306
327
  }
307
328
  } catch (err) {
308
- this.opts.log?.error(`parall[${this.opts.accountId}]: schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
329
+ this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
309
330
  }
310
331
  } else if (data.event_type === "approval_decided") {
311
332
  if (!data.source_id) return;
@@ -315,7 +336,7 @@ export class ParallAgentGateway {
315
336
  this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
316
337
  }
317
338
  } catch (err) {
318
- this.opts.log?.error(`parall[${this.opts.accountId}]: approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
339
+ this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
319
340
  }
320
341
  } else if (data.event_type !== "message" && data.event_type !== "task_assign") {
321
342
  // Truly unknown event_type — log so a newly-added dispatch type
@@ -325,11 +346,11 @@ export class ParallAgentGateway {
325
346
  // (message.new, task.assigned) above and would otherwise spam
326
347
  // info-level logs for every inbound chat message / task
327
348
  // assignment on a busy agent.
328
- this.opts.log?.info(`parall[${this.opts.accountId}]: dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) — no-op`);
349
+ this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) — no-op`);
329
350
  }
330
351
  });
331
352
 
332
- this.opts.log?.info(`parall[${this.opts.accountId}]: connecting to ${this.opts.connectionLabel ?? "Parall WS"}...`);
353
+ this.opts.log?.info(`connecting to ${this.opts.connectionLabel ?? "Parall WS"}...`);
333
354
  await ws.connect();
334
355
 
335
356
  return new Promise<void>((resolve) => {
@@ -462,7 +483,7 @@ export class ParallAgentGateway {
462
483
  },
463
484
  });
464
485
  } catch (err) {
465
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to create input step: ${String(err)}`);
486
+ this.opts.log?.warn(`failed to create input step: ${String(err)}`);
466
487
  }
467
488
  }
468
489
 
@@ -546,12 +567,12 @@ export class ParallAgentGateway {
546
567
  target_type: target.target_type,
547
568
  target_id: target.target_id,
548
569
  content: { text: runtimeEvent.message, suppressed: false },
549
- projection: true,
570
+ projection: false,
550
571
  });
551
572
  break;
552
573
  }
553
574
  } catch (err) {
554
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to create ${runtimeEvent.type} step: ${String(err)}`);
575
+ this.opts.log?.warn(`failed to create ${runtimeEvent.type} step: ${String(err)}`);
555
576
  }
556
577
  }
557
578
 
@@ -560,7 +581,7 @@ export class ParallAgentGateway {
560
581
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
561
582
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
562
583
  } catch (err) {
563
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to write context file ${filePath}: ${String(err)}`);
584
+ this.opts.log?.warn(`failed to write context file ${filePath}: ${String(err)}`);
564
585
  }
565
586
  }
566
587
 
@@ -571,7 +592,7 @@ export class ParallAgentGateway {
571
592
  ctx.step_id = stepId;
572
593
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
573
594
  } catch (err) {
574
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file step_id ${filePath}: ${String(err)}`);
595
+ this.opts.log?.warn(`failed to update context file step_id ${filePath}: ${String(err)}`);
575
596
  }
576
597
  }
577
598
 
@@ -582,7 +603,7 @@ export class ParallAgentGateway {
582
603
  ctx.session_id = sessionId;
583
604
  fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
584
605
  } catch (err) {
585
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file session_id ${filePath}: ${String(err)}`);
606
+ this.opts.log?.warn(`failed to update context file session_id ${filePath}: ${String(err)}`);
586
607
  }
587
608
  }
588
609
 
@@ -592,7 +613,7 @@ export class ParallAgentGateway {
592
613
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
593
614
  fs.writeFileSync(filePath, stepId, "utf8");
594
615
  } catch (err) {
595
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to write step id file ${filePath}: ${String(err)}`);
616
+ this.opts.log?.warn(`failed to write step id file ${filePath}: ${String(err)}`);
596
617
  }
597
618
  }
598
619
 
@@ -641,6 +662,16 @@ export class ParallAgentGateway {
641
662
  parent_session_id: parentSessionId,
642
663
  runtime_ref: Object.keys(runtimeRef).length > 0 ? runtimeRef : undefined,
643
664
  });
665
+ if (!LIVE_SESSION_STATUSES.has(session.status)) {
666
+ this.opts.log?.warn?.(
667
+ `createAgentSession returned terminal session ${session.id} (${session.status}), resetting runtime for ${sessionKey}`,
668
+ );
669
+ this.sessionBindings.delete(sessionKey);
670
+ try { await this.opts.onSessionStale?.(sessionKey); } catch (e) { this.opts.log?.warn?.(`onSessionStale failed: ${e}`); }
671
+ this.opts.log?.info?.(`stale session self-heal complete for ${sessionKey} — next dispatch will create a fresh session`);
672
+ throw new Error(`Agent session ${session.id} is terminal (${session.status})`);
673
+ }
674
+
644
675
  const binding: AgentSessionBinding = {
645
676
  sessionKey,
646
677
  agentSessionId: session.id,
@@ -675,11 +706,16 @@ export class ParallAgentGateway {
675
706
  // and are awaited by shutdown() up to SHUTDOWN_DEADLINE_MS.
676
707
  if (this.shuttingDown) {
677
708
  this.opts.log?.info(
678
- `parall[${this.opts.accountId}]: skipping dispatch for ${event.messageId} (shutting down) — leaving unacked for catch-up on replacement pod`,
709
+ `skipping dispatch for ${event.messageId} (shutting down) — leaving unacked for catch-up on replacement pod`,
679
710
  );
680
711
  return false;
681
712
  }
682
713
 
714
+ if (this.pendingRestartNotification) {
715
+ bodyForAgent = this.pendingRestartNotification + "\n\n---\n\n" + bodyForAgent;
716
+ this.pendingRestartNotification = null;
717
+ }
718
+
683
719
  setSessionChatId(sessionKey, event.targetId);
684
720
  setSessionMessageId(sessionKey, event.messageId);
685
721
  setDispatchMessageId(sessionKey, event.messageId);
@@ -832,7 +868,7 @@ export class ParallAgentGateway {
832
868
  item.resolve(true);
833
869
  }
834
870
  } catch (err) {
835
- this.opts.log?.error(`parall[${this.opts.accountId}]: fork dispatch failed for ${last.messageId}: ${String(err)}`);
871
+ this.opts.log?.error(`fork dispatch failed for ${last.messageId}: ${String(err)}`);
836
872
  for (const item of items) {
837
873
  item.resolve(false);
838
874
  }
@@ -851,7 +887,7 @@ export class ParallAgentGateway {
851
887
  try {
852
888
  historyPath = this.opts.dispatchAdapter.getSessionHistoryPath?.(fork.fork.sessionKey);
853
889
  } catch (err) {
854
- this.opts.log?.warn?.(`parall[${this.opts.accountId}]: failed to resolve fork history path: ${String(err)}`);
890
+ this.opts.log?.warn?.(`failed to resolve fork history path: ${String(err)}`);
855
891
  }
856
892
  this.dispatchState.pendingForkResults.push({
857
893
  forkSessionKey: fork.fork.sessionKey,
@@ -911,7 +947,7 @@ export class ParallAgentGateway {
911
947
  // pod is the single source of truth for replay.
912
948
  if (this.shuttingDown) {
913
949
  this.opts.log?.info(
914
- `parall[${this.opts.accountId}]: drainMainBuffer halted (shutting down) — ${this.dispatchState.mainBuffer.length} buffered, ${this.dispatchState.pendingForkResults.length} pending fork results left for catch-up`,
950
+ `drainMainBuffer halted (shutting down) — ${this.dispatchState.mainBuffer.length} buffered, ${this.dispatchState.pendingForkResults.length} pending fork results left for catch-up`,
915
951
  );
916
952
  break;
917
953
  }
@@ -1006,7 +1042,7 @@ export class ParallAgentGateway {
1006
1042
  ) {
1007
1043
  this.startInjectedTyping(event);
1008
1044
  this.opts.log?.info(
1009
- `parall[${this.opts.accountId}]: steer injected for ${event.messageId} (will drain for bookkeeping)`,
1045
+ `steer injected for ${event.messageId} (will drain for bookkeeping)`,
1010
1046
  );
1011
1047
  }
1012
1048
  // If the main dispatch cycle ended while we awaited the steer RPC,
@@ -1043,7 +1079,7 @@ export class ParallAgentGateway {
1043
1079
  });
1044
1080
 
1045
1081
  if (!fork) {
1046
- this.opts.log?.warn(`parall[${this.opts.accountId}]: fork failed, buffering event for main session`);
1082
+ this.opts.log?.warn(`fork failed, buffering event for main session`);
1047
1083
  this.dispatchState.mainBuffer.push(event);
1048
1084
  return false;
1049
1085
  }
@@ -1061,7 +1097,7 @@ export class ParallAgentGateway {
1061
1097
  activeFork.queue.push({ event, resolve });
1062
1098
  });
1063
1099
  this.runForkDrainLoop(activeFork).catch((err) => {
1064
- this.opts.log?.error(`parall[${this.opts.accountId}]: fork drain loop error: ${String(err)}`);
1100
+ this.opts.log?.error(`fork drain loop error: ${String(err)}`);
1065
1101
  });
1066
1102
  return firstEventPromise;
1067
1103
  }
@@ -1081,7 +1117,7 @@ export class ParallAgentGateway {
1081
1117
  this.chatInfoMap.set(chatId, chatInfo);
1082
1118
  return chatInfo;
1083
1119
  } catch (err) {
1084
- this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to resolve chat ${chatId}: ${String(err)}`);
1120
+ this.opts.log?.warn(`failed to resolve chat ${chatId}: ${String(err)}`);
1085
1121
  return null;
1086
1122
  }
1087
1123
  }
@@ -1161,7 +1197,7 @@ export class ParallAgentGateway {
1161
1197
  this.dispatchedMessages.delete(data.id);
1162
1198
  }
1163
1199
  } catch (err) {
1164
- this.opts.log?.error(`parall[${this.opts.accountId}]: event dispatch failed for ${data.id}: ${String(err)}`);
1200
+ this.opts.log?.error(`event dispatch failed for ${data.id}: ${String(err)}`);
1165
1201
  this.dispatchedMessages.delete(data.id);
1166
1202
  }
1167
1203
  }
@@ -1170,11 +1206,11 @@ export class ParallAgentGateway {
1170
1206
  if (this.shuttingDown) return false; // drain window — let server requeue via catch-up
1171
1207
  const dedupeKey = `${task.id}:${task.updated_at}`;
1172
1208
  if (this.dispatchedTasks.has(dedupeKey)) {
1173
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping already-dispatched task ${task.identifier ?? task.id}`);
1209
+ this.opts.log?.info(`skipping already-dispatched task ${task.identifier ?? task.id}`);
1174
1210
  return false;
1175
1211
  }
1176
1212
  this.dispatchedTasks.add(dedupeKey);
1177
- this.opts.log?.info(`parall[${this.opts.accountId}]: task assigned: ${task.identifier ?? task.id} "${task.title}"`);
1213
+ this.opts.log?.info(`task assigned: ${task.identifier ?? task.id} "${task.title}"`);
1178
1214
 
1179
1215
  const parts = [`Title: ${task.title}`];
1180
1216
  parts.push(`Status: ${task.status}`, `Priority: ${task.priority}`);
@@ -1220,7 +1256,7 @@ export class ParallAgentGateway {
1220
1256
  const isCreatorUpdate = opts.allowCreator === true && task.creator_id === this.opts.agentUserId;
1221
1257
  if (!isAssignee && !isCreatorUpdate) {
1222
1258
  this.opts.log?.info(
1223
- `parall[${this.opts.accountId}]: skipping stale task dispatch ${ackSourceId ?? taskId} — assigned to ${task.assignee_id}, creator ${task.creator_id}`,
1259
+ `skipping stale task dispatch ${ackSourceId ?? taskId} — assigned to ${task.assignee_id}, creator ${task.creator_id}`,
1224
1260
  );
1225
1261
  return true;
1226
1262
  }
@@ -1245,7 +1281,7 @@ export class ParallAgentGateway {
1245
1281
  const status = (err as { status?: number })?.status;
1246
1282
  if (status === 404) {
1247
1283
  // Comment deleted — ack stale dispatch so it doesn't pile up.
1248
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping deleted comment ${commentId}, acking stale dispatch`);
1284
+ this.opts.log?.info(`skipping deleted comment ${commentId}, acking stale dispatch`);
1249
1285
  this.dispatchedTasks.delete(dedupeKey);
1250
1286
  return true; // caller will ack
1251
1287
  }
@@ -1257,7 +1293,7 @@ export class ParallAgentGateway {
1257
1293
  return true; // null response = gone, ack stale dispatch
1258
1294
  }
1259
1295
  if (comment.hints?.no_reply) {
1260
- this.opts.log?.info(`parall[${this.opts.accountId}]: skipping no_reply task comment ${commentId}, acking stale dispatch`);
1296
+ this.opts.log?.info(`skipping no_reply task comment ${commentId}, acking stale dispatch`);
1261
1297
  this.dispatchedTasks.delete(dedupeKey);
1262
1298
  return true;
1263
1299
  }
@@ -1268,7 +1304,7 @@ export class ParallAgentGateway {
1268
1304
  } catch { /* task context is optional */ }
1269
1305
 
1270
1306
  const taskLabel = task ? `${task.identifier ?? task.id} "${task.title}"` : taskId;
1271
- this.opts.log?.info(`parall[${this.opts.accountId}]: task comment on ${taskLabel} by ${actorId ?? "unknown"}`);
1307
+ this.opts.log?.info(`task comment on ${taskLabel} by ${actorId ?? "unknown"}`);
1272
1308
 
1273
1309
  const parts: string[] = [];
1274
1310
  if (task) {
@@ -1328,10 +1364,10 @@ export class ParallAgentGateway {
1328
1364
  // already-delivered run — this is expected but operationally
1329
1365
  // visible, so warn (not info) keeps it findable during incident
1330
1366
  // triage. See docs/primitives/schedule.md § Snapshot 不变性.
1331
- this.opts.log?.warn(`parall[${this.opts.accountId}]: schedule run ${runId} not accessible (404), acking stale dispatch`);
1367
+ this.opts.log?.warn(`schedule run ${runId} not accessible (404), acking stale dispatch`);
1332
1368
  return true;
1333
1369
  }
1334
- this.opts.log?.warn(`parall[${this.opts.accountId}]: schedule run fetch failed for ${runId}, leaving pending: ${String(err)}`);
1370
+ this.opts.log?.warn(`schedule run fetch failed for ${runId}, leaving pending: ${String(err)}`);
1335
1371
  return false;
1336
1372
  }
1337
1373
  if (!run) return true;
@@ -1343,7 +1379,7 @@ export class ParallAgentGateway {
1343
1379
  const dedupeKey = `schedule_run:${run.id}`;
1344
1380
  if (this.dispatchedTasks.has(dedupeKey)) return false;
1345
1381
  this.dispatchedTasks.add(dedupeKey);
1346
- this.opts.log?.info(`parall[${this.opts.accountId}]: schedule fired: ${run.id} (schedule ${run.schedule_id})`);
1382
+ this.opts.log?.info(`schedule fired: ${run.id} (schedule ${run.schedule_id})`);
1347
1383
 
1348
1384
  const event: ParallEvent = {
1349
1385
  type: "schedule",
@@ -1383,10 +1419,10 @@ export class ParallAgentGateway {
1383
1419
  } catch (err: unknown) {
1384
1420
  const status = (err as { status?: number })?.status;
1385
1421
  if (status === 404 || status === 403) {
1386
- this.opts.log?.warn(`parall[${this.opts.accountId}]: approval ${approvalId} not accessible (${status}), acking stale dispatch`);
1422
+ this.opts.log?.warn(`approval ${approvalId} not accessible (${status}), acking stale dispatch`);
1387
1423
  return true;
1388
1424
  }
1389
- this.opts.log?.warn(`parall[${this.opts.accountId}]: approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
1425
+ this.opts.log?.warn(`approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
1390
1426
  return false;
1391
1427
  }
1392
1428
  if (!approval) return true;
@@ -1395,7 +1431,7 @@ export class ParallAgentGateway {
1395
1431
  const dedupeKey = `approval:${approval.id}`;
1396
1432
  if (this.dispatchedTasks.has(dedupeKey)) return false;
1397
1433
  this.dispatchedTasks.add(dedupeKey);
1398
- this.opts.log?.info(`parall[${this.opts.accountId}]: approval decided: ${approval.id} (${approval.status})`);
1434
+ this.opts.log?.info(`approval decided: ${approval.id} (${approval.status})`);
1399
1435
 
1400
1436
  const statusLabel = approval.status === "approved" ? "Approved" : "Rejected";
1401
1437
  const execInfo = approval.execution_status ? ` | execution: ${approval.execution_status}` : "";
@@ -1452,14 +1488,14 @@ export class ParallAgentGateway {
1452
1488
  try {
1453
1489
  dispatched = await this.handleTaskDispatch(item.task_id, item.source_id ?? item.task_id);
1454
1490
  } catch (err: unknown) {
1455
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
1491
+ this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
1456
1492
  continue;
1457
1493
  }
1458
1494
  } else if (item.event_type === "task_update" && item.task_id) {
1459
1495
  try {
1460
1496
  dispatched = await this.handleTaskDispatch(item.task_id, item.source_id ?? item.task_id, { allowCreator: true });
1461
1497
  } catch (err: unknown) {
1462
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
1498
+ this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
1463
1499
  continue;
1464
1500
  }
1465
1501
  } else if (item.event_type === "task_comment" && item.source_id && item.task_id) {
@@ -1480,7 +1516,7 @@ export class ParallAgentGateway {
1480
1516
  msg = null;
1481
1517
  } else {
1482
1518
  msgFetchFailed = true;
1483
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
1519
+ this.opts.log?.warn(`catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
1484
1520
  }
1485
1521
  }
1486
1522
  if (msgFetchFailed) {
@@ -1510,7 +1546,7 @@ export class ParallAgentGateway {
1510
1546
  this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {});
1511
1547
  }
1512
1548
  } catch (err) {
1513
- this.opts.log?.warn(`parall[${this.opts.accountId}]: catch-up dispatch ${item.id} (${item.event_type}) failed: ${String(err)}`);
1549
+ this.opts.log?.warn(`catch-up dispatch ${item.id} (${item.event_type}) failed: ${String(err)}`);
1514
1550
  }
1515
1551
  }
1516
1552
  // Stop paginating once shutdown begins — the inner loop already broke,
@@ -1519,7 +1555,7 @@ export class ParallAgentGateway {
1519
1555
  } while (cursor);
1520
1556
 
1521
1557
  if (processed > 0 || skippedOld > 0) {
1522
- this.opts.log?.info(`parall[${this.opts.accountId}]: dispatch catch-up: processed ${processed}, skipped ${skippedOld} old item(s)`);
1558
+ this.opts.log?.info(`dispatch catch-up: processed ${processed}, skipped ${skippedOld} old item(s)`);
1523
1559
  }
1524
1560
  }
1525
1561
 
@@ -1529,7 +1565,7 @@ export class ParallAgentGateway {
1529
1565
  const intervalSec = data.heartbeat_interval > 0 ? data.heartbeat_interval : 30;
1530
1566
  try {
1531
1567
  const count = await fetchAllChats(client, config.org_id, this.chatInfoMap);
1532
- log?.info(`parall[${this.opts.accountId}]: WebSocket connected, ${count} chats cached`);
1568
+ log?.info(`WebSocket connected, ${count} chats cached`);
1533
1569
 
1534
1570
  await this.opts.onSessionReady?.({
1535
1571
  activeSessionId: this.activeSessionId,
@@ -1544,7 +1580,7 @@ export class ParallAgentGateway {
1544
1580
  const expectedMs = intervalSec * 1000;
1545
1581
  const drift = now - this.lastHeartbeatAt - expectedMs;
1546
1582
  if (drift > 15000) {
1547
- log?.warn(`parall[${this.opts.accountId}]: heartbeat drift ${drift}ms — event loop may be blocked`);
1583
+ log?.warn(`heartbeat drift ${drift}ms — event loop may be blocked`);
1548
1584
  }
1549
1585
  this.lastHeartbeatAt = now;
1550
1586
  if (this.opts.ws.state !== "connected") return;
@@ -1560,10 +1596,10 @@ export class ParallAgentGateway {
1560
1596
  const isFirstHello = !this.hadSuccessfulHello;
1561
1597
  this.hadSuccessfulHello = true;
1562
1598
  this.catchUpFromDispatch(isFirstHello).catch((err) => {
1563
- log?.warn(`parall[${this.opts.accountId}]: dispatch catch-up failed: ${String(err)}`);
1599
+ log?.warn(`dispatch catch-up failed: ${String(err)}`);
1564
1600
  });
1565
1601
  } catch (err) {
1566
- log?.error(`parall[${this.opts.accountId}]: failed to fetch chats: ${String(err)}`);
1602
+ log?.error(`failed to fetch chats: ${String(err)}`);
1567
1603
  }
1568
1604
  }
1569
1605
 
@@ -1598,15 +1634,15 @@ export class ParallAgentGateway {
1598
1634
  // via dispatch catch-up after the new pod starts.
1599
1635
  if (this.inFlightDispatches > 0) {
1600
1636
  this.opts.log?.info(
1601
- `parall[${this.opts.accountId}]: draining ${this.inFlightDispatches} in-flight dispatch(es), deadline ${this.SHUTDOWN_DEADLINE_MS}ms`,
1637
+ `draining ${this.inFlightDispatches} in-flight dispatch(es), deadline ${this.SHUTDOWN_DEADLINE_MS}ms`,
1602
1638
  );
1603
1639
  await this.waitForDrain(this.SHUTDOWN_DEADLINE_MS);
1604
1640
  if (this.inFlightDispatches > 0) {
1605
1641
  this.opts.log?.warn(
1606
- `parall[${this.opts.accountId}]: drain deadline hit; ${this.inFlightDispatches} dispatch(es) still running — they will be killed by process exit`,
1642
+ `drain deadline hit; ${this.inFlightDispatches} dispatch(es) still running — they will be killed by process exit`,
1607
1643
  );
1608
1644
  } else {
1609
- this.opts.log?.info(`parall[${this.opts.accountId}]: drain complete`);
1645
+ this.opts.log?.info(`drain complete`);
1610
1646
  }
1611
1647
  }
1612
1648
 
@@ -1619,6 +1655,6 @@ export class ParallAgentGateway {
1619
1655
  await this.opts.onBeforeDisconnect?.();
1620
1656
 
1621
1657
  this.opts.ws.disconnect();
1622
- this.opts.log?.info(`parall[${this.opts.accountId}]: disconnected`);
1658
+ this.opts.log?.info(`disconnected`);
1623
1659
  }
1624
1660
  }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./provider-config.js";
1
2
  export * from "./types.js";
2
3
  export * from "./session-state.js";
3
4
  export * from "./routing.js";
@@ -5,7 +6,7 @@ export * from "./event-format.js";
5
6
  export * from "./prompt-fragments.js";
6
7
  export * from "./bridge-workspace.js";
7
8
  export * from "./dispatch-adapter.js";
8
- export { createLogger } from "./logger.js";
9
+ export { createLogger, childLogger } from "./logger.js";
9
10
  export * from "./gateway-base.js";
10
11
  export * from "./platform-config.js";
11
12
  export { writeSkillFiles, buildSkillReferences, SKILLS } from "./skills/index.js";
package/src/logger.ts CHANGED
@@ -5,5 +5,10 @@ export function createLogger(prefix: string): GatewayLogger {
5
5
  info: (msg: string) => console.log(`${new Date().toISOString()} [${prefix}] ${msg}`),
6
6
  warn: (msg: string) => console.warn(`${new Date().toISOString()} [${prefix}] ${msg}`),
7
7
  error: (msg: string) => console.error(`${new Date().toISOString()} [${prefix}] ${msg}`),
8
+ child: (sub: string) => createLogger(`${prefix}:${sub}`),
8
9
  };
9
10
  }
11
+
12
+ export function childLogger(logger: GatewayLogger, sub: string): GatewayLogger {
13
+ return logger.child ? logger.child(sub) : logger;
14
+ }
@@ -0,0 +1,39 @@
1
+ export interface ProviderConfig {
2
+ llm_source?: string;
3
+ openai_api_key?: string;
4
+ openai_base_url?: string;
5
+ anthropic_auth_token?: string;
6
+ anthropic_base_url?: string;
7
+ }
8
+
9
+ export function llmSource(pc?: ProviderConfig): string {
10
+ if (pc?.llm_source) return pc.llm_source;
11
+ if (
12
+ pc?.openai_api_key ||
13
+ pc?.openai_base_url ||
14
+ pc?.anthropic_auth_token ||
15
+ pc?.anthropic_base_url
16
+ ) {
17
+ return "custom";
18
+ }
19
+ return "parall";
20
+ }
21
+
22
+ export function clearAllProviderCreds(env: NodeJS.ProcessEnv): void {
23
+ delete env.ANTHROPIC_AUTH_TOKEN;
24
+ delete env.ANTHROPIC_BASE_URL;
25
+ delete env.ANTHROPIC_API_KEY;
26
+ delete env.OPENAI_API_KEY;
27
+ delete env.OPENAI_BASE_URL;
28
+ delete env.PRLL_CLAUDE_ALLOW_API_KEY;
29
+ }
30
+
31
+ export function parseProviderConfig(env: NodeJS.ProcessEnv): ProviderConfig | undefined {
32
+ const raw = env.PRLL_PROVIDER_CONFIG?.trim();
33
+ if (!raw) return undefined;
34
+ try {
35
+ return JSON.parse(raw) as ProviderConfig;
36
+ } catch (err) {
37
+ throw new Error(`Invalid PRLL_PROVIDER_CONFIG JSON: ${String(err)}`);
38
+ }
39
+ }