@parall/agent-core 1.28.1 → 1.29.1

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.
@@ -80,19 +80,20 @@ export interface DispatchAdapter {
80
80
  /** Dispatch a Parall event to the runtime and emit normalized runtime events. */
81
81
  dispatch(opts: DispatchOpts): AsyncIterable<RuntimeEvent>;
82
82
  /**
83
- * Inject a user message into the running subprocess's stdin mid-turn so the
84
- * runtime reads it between tool calls and steers immediately, rather than
85
- * waiting for the current turn to complete.
83
+ * Inject a user message into the running runtime mid-turn so it steers
84
+ * immediately, rather than waiting for the current turn to complete.
86
85
  *
87
- * Returns true if the message was successfully written to stdin; false if no
88
- * live process exists for the session (caller should fall back to mainBuffer).
86
+ * Returns true if the injection succeeded; false if no live process/session
87
+ * exists (caller falls back to mainBuffer next-turn path). May return a
88
+ * Promise when the injection requires an async round-trip (e.g. Codex
89
+ * turn/steer JSON-RPC) — the gateway awaits it.
89
90
  *
90
91
  * The gateway still pushes the event to mainBuffer after a successful
91
92
  * injection — drainMainBuffer will call dispatch() for it, and dispatch()
92
- * must detect the pending injection (skip the stdin write) and only consume
93
- * the parser output that Claude produced in response.
93
+ * must detect the pending injection (skip the duplicate input) and only
94
+ * consume output produced in response.
94
95
  */
95
- enqueueDuringDispatch?(sessionKey: string, body: string): boolean;
96
+ enqueueDuringDispatch?(sessionKey: string, body: string): boolean | Promise<boolean>;
96
97
  /** True if enqueueDuringDispatch was called and the injections have not yet been consumed by dispatch(). */
97
98
  hasPendingInjections?(sessionKey: string): boolean;
98
99
  /** Fork the current runtime session for concurrent handling. Return null to buffer on main. */
@@ -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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"}
@@ -1,5 +1,6 @@
1
1
  import type { ForkResult, ParallEvent } from "./types.js";
2
2
  export declare function buildEventBody(event: ParallEvent): string;
3
+ export declare function buildEventBodyForForkResult(event: ParallEvent): string;
3
4
  export declare function buildForkScopePrefix(event: ParallEvent): string;
4
5
  export declare function buildForkResultPrefix(results: ForkResult[]): string;
5
6
  //# sourceMappingURL=event-format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"event-format.d.ts","sourceRoot":"","sources":["../src/event-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM1D,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAsDzD;AAoBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAK/D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAWnE"}
1
+ {"version":3,"file":"event-format.d.ts","sourceRoot":"","sources":["../src/event-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM1D,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAsDzD;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAEtE;AAoBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAK/D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAcnE"}
@@ -65,6 +65,9 @@ export function buildEventBody(event) {
65
65
  }
66
66
  return lines.join("\n") + buildSendMessageHint(event);
67
67
  }
68
+ export function buildEventBodyForForkResult(event) {
69
+ return buildEventBody(event).replace(/\n<system-reminder>[\s\S]*<\/system-reminder>$/, "");
70
+ }
68
71
  function buildSendMessageHint(event) {
69
72
  if (event.noReply)
70
73
  return "";
@@ -89,14 +92,16 @@ export function buildForkResultPrefix(results) {
89
92
  if (!results.length)
90
93
  return "";
91
94
  const blocks = results.map((result) => {
92
- const lines = [`[Fork result]`];
93
- lines.push(`[Handled: ${result.sourceEvent.type} ${result.sourceEvent.summary}]`);
95
+ const lines = [];
96
+ for (const body of result.eventBodies) {
97
+ lines.push(body);
98
+ }
99
+ lines.push(`[This event was handled by a parallel fork session. Do NOT re-handle, re-reply, or duplicate work for it.]`);
100
+ lines.push(`[Fork summary: ${result.agentSummary ? sanitizeMeta(result.agentSummary) : "No fork summary available — the fork completed without producing a text summary. Check the target chat/task for any actions the fork already took before acting."}]`);
94
101
  if (result.actions.length)
95
- lines.push(`[Actions: ${result.actions.join("; ")}]`);
96
- if (result.agentSummary)
97
- lines.push(`[Summary: ${sanitizeMeta(result.agentSummary)}]`);
102
+ lines.push(`[Fork actions: ${result.actions.join("; ")}]`);
98
103
  if (result.historyPath)
99
- lines.push(`[History: ${result.historyPath}]`);
104
+ lines.push(`[Fork history: ${result.historyPath}]`);
100
105
  return lines.join("\n");
101
106
  });
102
107
  return blocks.join("\n\n") + "\n\n---\n\n";
@@ -41,6 +41,7 @@ export declare class ParallAgentGateway {
41
41
  private readonly opts;
42
42
  private readonly chatInfoMap;
43
43
  private readonly activeDispatches;
44
+ private readonly injectedTypingCounts;
44
45
  private readonly dispatchedTasks;
45
46
  private readonly dispatchedMessages;
46
47
  private readonly forkStates;
@@ -63,6 +64,10 @@ export declare class ParallAgentGateway {
63
64
  private tryClaimMessage;
64
65
  private startTyping;
65
66
  private stopTyping;
67
+ private shouldShowTyping;
68
+ private startInjectedTyping;
69
+ private takeInjectedTypingCount;
70
+ private runDispatchWithTyping;
66
71
  private buildDispatchContext;
67
72
  private createInputStep;
68
73
  private createRuntimeStep;
@@ -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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"}
@@ -1,9 +1,8 @@
1
1
  import * as os from "node:os";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { randomUUID } from "node:crypto";
5
4
  import { MENTION_ALL_USER_ID } from "@parall/sdk";
6
- import { buildEventBody, buildForkResultPrefix, buildForkScopePrefix } from "./event-format.js";
5
+ import { buildEventBody, buildEventBodyForForkResult, buildForkResultPrefix, buildForkScopePrefix } from "./event-format.js";
7
6
  import { routeTrigger } from "./routing.js";
8
7
  import { clearDispatchMessageId, clearDispatchNoReply, clearSessionMessageId, setDispatchMessageId, setDispatchNoReply, setSessionChatId, setSessionMessageId, } from "./session-state.js";
9
8
  // Parse PRLL_SHUTDOWN_DEADLINE_MS (or any string env value) into a positive
@@ -55,6 +54,7 @@ export class ParallAgentGateway {
55
54
  opts;
56
55
  chatInfoMap = new Map();
57
56
  activeDispatches = new Map();
57
+ injectedTypingCounts = new Map();
58
58
  dispatchedTasks = new Set();
59
59
  dispatchedMessages = new Set();
60
60
  forkStates = new Map();
@@ -266,6 +266,35 @@ export class ParallAgentGateway {
266
266
  if (this.opts.ws.state === "connected")
267
267
  this.opts.ws.sendTyping(chatId, "stop");
268
268
  }
269
+ shouldShowTyping(event) {
270
+ return event.type === "message" && event.targetId.startsWith("cht_") && !event.noReply;
271
+ }
272
+ startInjectedTyping(event) {
273
+ if (!this.shouldShowTyping(event))
274
+ return;
275
+ this.startTyping(event.targetId);
276
+ this.injectedTypingCounts.set(event.targetId, (this.injectedTypingCounts.get(event.targetId) ?? 0) + 1);
277
+ }
278
+ takeInjectedTypingCount(chatId) {
279
+ const count = this.injectedTypingCounts.get(chatId) ?? 0;
280
+ this.injectedTypingCounts.delete(chatId);
281
+ return count;
282
+ }
283
+ async runDispatchWithTyping(event, sessionKey, bodyForAgent, earlierEvents = [], captureText, opts = {}) {
284
+ const showTyping = !opts.suppressStart && (this.shouldShowTyping(event) || earlierEvents.some(e => this.shouldShowTyping(e)));
285
+ if (showTyping)
286
+ this.startTyping(event.targetId);
287
+ try {
288
+ return await this.runDispatch(event, sessionKey, bodyForAgent, earlierEvents, captureText);
289
+ }
290
+ finally {
291
+ if (showTyping)
292
+ this.stopTyping(event.targetId);
293
+ for (let i = 0; i < (opts.injectedTypingCount ?? 0); i++) {
294
+ this.stopTyping(event.targetId);
295
+ }
296
+ }
297
+ }
269
298
  buildDispatchContext(event, sessionKey) {
270
299
  const binding = this.sessionBindings.get(sessionKey);
271
300
  return {
@@ -307,6 +336,7 @@ export class ParallAgentGateway {
307
336
  sender_id: event.senderId,
308
337
  sender_name: event.senderName,
309
338
  summary: event.body.substring(0, 200),
339
+ ...(event.sentAt ? { sent_at: event.sentAt } : {}),
310
340
  },
311
341
  });
312
342
  }
@@ -554,7 +584,8 @@ export class ParallAgentGateway {
554
584
  continue;
555
585
  }
556
586
  if (!binding) {
557
- throw new Error(`runtime emitted ${runtimeEvent.type} before runtime_session`);
587
+ const detail = runtimeEvent.type === "error" ? `: ${runtimeEvent.message}` : "";
588
+ throw new Error(`runtime emitted ${runtimeEvent.type} before runtime_session${detail}`);
558
589
  }
559
590
  if (!triggerMessageSet) {
560
591
  triggerMessageSet = true;
@@ -637,7 +668,7 @@ export class ParallAgentGateway {
637
668
  const earlier = events.slice(0, -1);
638
669
  try {
639
670
  const batchText = [];
640
- const dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
671
+ const dispatched = await this.runDispatchWithTyping(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
641
672
  if (!dispatched) {
642
673
  // Shutdown short-circuit — resolve un-acked so the server requeues
643
674
  // for the replacement pod and stop draining further items.
@@ -685,6 +716,7 @@ export class ParallAgentGateway {
685
716
  ? `${first.type} from ${first.senderName} in ${first.targetName ?? fork.targetId}`
686
717
  : `${fork.processedEvents.length} events in ${first.targetName ?? fork.targetId}`,
687
718
  },
719
+ eventBodies: fork.processedEvents.map((e) => buildEventBodyForForkResult(e)),
688
720
  actions: [],
689
721
  agentSummary,
690
722
  historyPath,
@@ -723,7 +755,7 @@ export class ParallAgentGateway {
723
755
  return;
724
756
  this.draining = true;
725
757
  try {
726
- while (this.dispatchState.mainBuffer.length > 0 || this.dispatchState.pendingForkResults.length > 0) {
758
+ while (this.dispatchState.mainBuffer.length > 0) {
727
759
  // Early shutdown guard — same reasoning as runForkDrainLoop: stop
728
760
  // before any input AgentSteps are persisted, leaving the buffer +
729
761
  // pendingForkResults intact so dispatch catch-up on the replacement
@@ -732,31 +764,6 @@ export class ParallAgentGateway {
732
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`);
733
765
  break;
734
766
  }
735
- if (this.dispatchState.mainBuffer.length === 0 && this.dispatchState.pendingForkResults.length > 0) {
736
- const pending = this.dispatchState.pendingForkResults.splice(0);
737
- const forkPrefix = buildForkResultPrefix(pending);
738
- const syntheticEvent = {
739
- type: "message",
740
- targetId: "_orchestrator",
741
- targetType: "system",
742
- senderId: "system",
743
- senderName: "system",
744
- messageId: `synthetic-${this.opts.accountId}-${randomUUID()}`,
745
- body: "[Orchestrator: fork session(s) completed — review results above]",
746
- };
747
- this.dispatchState.mainCurrentTargetId = undefined;
748
- this.dispatchState.mainPreDispatchBranchPoint =
749
- this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
750
- const dispatched = await this.runDispatch(syntheticEvent, this.opts.runtimeKey, forkPrefix + buildEventBody(syntheticEvent));
751
- if (!dispatched) {
752
- // Shutdown: put the fork results back at the head so the synthetic
753
- // event is regenerated on the replacement pod. Synthetic events are
754
- // not ack'd, so this is the only state we need to preserve.
755
- this.dispatchState.pendingForkResults.unshift(...pending);
756
- break;
757
- }
758
- continue;
759
- }
760
767
  const targetId = this.dispatchState.mainBuffer[0].targetId;
761
768
  const events = [];
762
769
  while (this.dispatchState.mainBuffer[0]?.targetId === targetId) {
@@ -764,13 +771,14 @@ export class ParallAgentGateway {
764
771
  }
765
772
  const event = events[events.length - 1];
766
773
  const earlier = events.slice(0, -1);
767
- const skipForkSplice = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
768
- const pendingFork = skipForkSplice ? [] : this.dispatchState.pendingForkResults.splice(0);
774
+ const hasPendingInjections = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
775
+ const injectedTypingCount = hasPendingInjections ? this.takeInjectedTypingCount(event.targetId) : 0;
776
+ const pendingFork = hasPendingInjections ? [] : this.dispatchState.pendingForkResults.splice(0);
769
777
  const forkPrefix = buildForkResultPrefix(pendingFork);
770
778
  this.dispatchState.mainCurrentTargetId = event.targetId;
771
779
  this.dispatchState.mainPreDispatchBranchPoint =
772
780
  this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
773
- const dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event), earlier);
781
+ const dispatched = await this.runDispatchWithTyping(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event), earlier, undefined, { suppressStart: injectedTypingCount > 0, injectedTypingCount });
774
782
  if (!dispatched) {
775
783
  // Shutdown: skip the ack so the server redelivers these buffered
776
784
  // events to the replacement pod via dispatch catch-up. Put both the
@@ -811,7 +819,7 @@ export class ParallAgentGateway {
811
819
  this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
812
820
  let dispatched = false;
813
821
  try {
814
- dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
822
+ dispatched = await this.runDispatchWithTyping(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
815
823
  if (!dispatched) {
816
824
  // Shutdown short-circuit — restore the fork results so a future
817
825
  // pod can replay them, and return false so handleMessage skips ack.
@@ -827,11 +835,22 @@ export class ParallAgentGateway {
827
835
  if (this.shuttingDown) {
828
836
  return false;
829
837
  }
838
+ // Push synchronously BEFORE the (possibly async) steer attempt so
839
+ // arrival order is preserved and the event cannot be orphaned in a
840
+ // gap between the steer await and the push.
841
+ this.dispatchState.mainBuffer.push(event);
830
842
  if (this.dispatchState.mainCurrentTargetId === event.targetId &&
831
- this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
832
- this.opts.log?.info(`parall[${this.opts.accountId}]: steer injected to stdin for ${event.messageId} (will drain for bookkeeping)`);
843
+ await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
844
+ this.startInjectedTyping(event);
845
+ this.opts.log?.info(`parall[${this.opts.accountId}]: steer injected for ${event.messageId} (will drain for bookkeeping)`);
846
+ }
847
+ // If the main dispatch cycle ended while we awaited the steer RPC,
848
+ // our event is buffered but no drain is in flight. Re-enter the
849
+ // drain to process it. The draining guard prevents re-entry.
850
+ if (!this.dispatchState.mainDispatching && !this.draining && this.dispatchState.mainBuffer.length > 0) {
851
+ this.dispatchState.mainDispatching = true;
852
+ void this.drainMainBuffer();
833
853
  }
834
- this.dispatchState.mainBuffer.push(event);
835
854
  return false;
836
855
  }
837
856
  case "buffer-fork": {
@@ -934,6 +953,7 @@ export class ParallAgentGateway {
934
953
  threadRootId: message.thread_root_id ?? undefined,
935
954
  noReply: message.hints?.no_reply ?? false,
936
955
  attachments: attachments.length > 0 ? attachments : undefined,
956
+ sentAt: message.created_at,
937
957
  ackSourceType: "message",
938
958
  ackSourceId: message.id,
939
959
  },
@@ -955,9 +975,6 @@ export class ParallAgentGateway {
955
975
  return;
956
976
  }
957
977
  const event = decision.event;
958
- const willDispatch = !this.dispatchState.mainDispatching || this.dispatchState.mainCurrentTargetId !== chatId;
959
- if (willDispatch)
960
- this.startTyping(chatId);
961
978
  try {
962
979
  const dispatched = await this.handleInboundEvent(event);
963
980
  if (dispatched) {
@@ -971,10 +988,6 @@ export class ParallAgentGateway {
971
988
  this.opts.log?.error(`parall[${this.opts.accountId}]: event dispatch failed for ${data.id}: ${String(err)}`);
972
989
  this.dispatchedMessages.delete(data.id);
973
990
  }
974
- finally {
975
- if (willDispatch)
976
- this.stopTyping(chatId);
977
- }
978
991
  }
979
992
  async handleTaskAssignment(task, ackSourceId) {
980
993
  if (this.shuttingDown)
@@ -1003,6 +1016,7 @@ export class ParallAgentGateway {
1003
1016
  senderName: "system",
1004
1017
  messageId: task.id,
1005
1018
  body: parts.join("\n"),
1019
+ sentAt: task.updated_at ?? task.created_at,
1006
1020
  ackSourceType: "task_activity",
1007
1021
  ackSourceId,
1008
1022
  };
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from "./event-format.js";
5
5
  export * from "./prompt-fragments.js";
6
6
  export * from "./bridge-workspace.js";
7
7
  export * from "./dispatch-adapter.js";
8
+ export { createLogger } from "./logger.js";
8
9
  export * from "./gateway-base.js";
9
10
  export * from "./platform-config.js";
10
11
  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,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,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"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export * from "./event-format.js";
5
5
  export * from "./prompt-fragments.js";
6
6
  export * from "./bridge-workspace.js";
7
7
  export * from "./dispatch-adapter.js";
8
+ export { createLogger } from "./logger.js";
8
9
  export * from "./gateway-base.js";
9
10
  export * from "./platform-config.js";
10
11
  export { writeSkillFiles, buildSkillReferences, SKILLS } from "./skills/index.js";
@@ -0,0 +1,3 @@
1
+ import type { GatewayLogger } from "./dispatch-adapter.js";
2
+ export declare function createLogger(prefix: string): GatewayLogger;
3
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +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"}
package/dist/logger.js ADDED
@@ -0,0 +1,7 @@
1
+ export function createLogger(prefix) {
2
+ return {
3
+ info: (msg) => console.log(`${new Date().toISOString()} [${prefix}] ${msg}`),
4
+ warn: (msg) => console.warn(`${new Date().toISOString()} [${prefix}] ${msg}`),
5
+ error: (msg) => console.error(`${new Date().toISOString()} [${prefix}] ${msg}`),
6
+ };
7
+ }
package/dist/types.d.ts CHANGED
@@ -6,6 +6,8 @@ export type ForkResult = {
6
6
  targetId: string;
7
7
  summary: string;
8
8
  };
9
+ /** Original event bodies that the fork processed, built via `buildEventBody()`. */
10
+ eventBodies: string[];
9
11
  actions: string[];
10
12
  /** Agent's own summary of what it did (captured from the fork's last text output). */
11
13
  agentSummary?: string;
@@ -45,6 +47,9 @@ export type ParallEvent = {
45
47
  scheduledFireAt?: string;
46
48
  /** Fire-time attached_to_uri snapshot, used for schedule events. */
47
49
  attachedUri?: string;
50
+ /** Original event timestamp (e.g., message.created_at). When present,
51
+ * input steps use this instead of server insertion time for ordering. */
52
+ sentAt?: string;
48
53
  ackSourceType?: "message" | "task_activity" | "comment" | "schedule_run";
49
54
  ackSourceId?: string;
50
55
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,MAAM,MAAM,UAAU,GAAG;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,2DAA2D;AAC3D,MAAM,MAAM,aAAa,GAAG;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kBAAkB,EAAE,UAAU,EAAE,CAAC;IACjC,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,oFAAoF;IACpF,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC,CAAC;AAEF,4CAA4C;AAC5C,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,GAAG,UAAU,CAAC;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG,SAAS,GAAG,cAAc,CAAC;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,MAAM,MAAM,UAAU,GAAG;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,mFAAmF;IACnF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,2DAA2D;AAC3D,MAAM,MAAM,aAAa,GAAG;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kBAAkB,EAAE,UAAU,EAAE,CAAC;IACjC,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,oFAAoF;IACpF,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC,CAAC;AAEF,4CAA4C;AAC5C,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,GAAG,UAAU,CAAC;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;8EAC0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG,SAAS,GAAG,cAAc,CAAC;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/agent-core",
3
- "version": "1.28.1",
3
+ "version": "1.29.1",
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.28.1"
29
+ "@parall/sdk": "1.29.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.0.0",
@@ -68,19 +68,20 @@ export interface DispatchAdapter {
68
68
  dispatch(opts: DispatchOpts): AsyncIterable<RuntimeEvent>;
69
69
 
70
70
  /**
71
- * Inject a user message into the running subprocess's stdin mid-turn so the
72
- * runtime reads it between tool calls and steers immediately, rather than
73
- * waiting for the current turn to complete.
71
+ * Inject a user message into the running runtime mid-turn so it steers
72
+ * immediately, rather than waiting for the current turn to complete.
74
73
  *
75
- * Returns true if the message was successfully written to stdin; false if no
76
- * live process exists for the session (caller should fall back to mainBuffer).
74
+ * Returns true if the injection succeeded; false if no live process/session
75
+ * exists (caller falls back to mainBuffer next-turn path). May return a
76
+ * Promise when the injection requires an async round-trip (e.g. Codex
77
+ * turn/steer JSON-RPC) — the gateway awaits it.
77
78
  *
78
79
  * The gateway still pushes the event to mainBuffer after a successful
79
80
  * injection — drainMainBuffer will call dispatch() for it, and dispatch()
80
- * must detect the pending injection (skip the stdin write) and only consume
81
- * the parser output that Claude produced in response.
81
+ * must detect the pending injection (skip the duplicate input) and only
82
+ * consume output produced in response.
82
83
  */
83
- enqueueDuringDispatch?(sessionKey: string, body: string): boolean;
84
+ enqueueDuringDispatch?(sessionKey: string, body: string): boolean | Promise<boolean>;
84
85
 
85
86
  /** True if enqueueDuringDispatch was called and the injections have not yet been consumed by dispatch(). */
86
87
  hasPendingInjections?(sessionKey: string): boolean;
@@ -60,6 +60,10 @@ export function buildEventBody(event: ParallEvent): string {
60
60
  return lines.join("\n") + buildSendMessageHint(event);
61
61
  }
62
62
 
63
+ export function buildEventBodyForForkResult(event: ParallEvent): string {
64
+ return buildEventBody(event).replace(/\n<system-reminder>[\s\S]*<\/system-reminder>$/, "");
65
+ }
66
+
63
67
  function buildSendMessageHint(event: ParallEvent): string {
64
68
  if (event.noReply) return "";
65
69
 
@@ -88,11 +92,14 @@ export function buildForkScopePrefix(event: ParallEvent): string {
88
92
  export function buildForkResultPrefix(results: ForkResult[]): string {
89
93
  if (!results.length) return "";
90
94
  const blocks = results.map((result) => {
91
- const lines = [`[Fork result]`];
92
- lines.push(`[Handled: ${result.sourceEvent.type} ${result.sourceEvent.summary}]`);
93
- if (result.actions.length) lines.push(`[Actions: ${result.actions.join("; ")}]`);
94
- if (result.agentSummary) lines.push(`[Summary: ${sanitizeMeta(result.agentSummary)}]`);
95
- if (result.historyPath) lines.push(`[History: ${result.historyPath}]`);
95
+ const lines: string[] = [];
96
+ for (const body of result.eventBodies) {
97
+ lines.push(body);
98
+ }
99
+ lines.push(`[This event was handled by a parallel fork session. Do NOT re-handle, re-reply, or duplicate work for it.]`);
100
+ lines.push(`[Fork summary: ${result.agentSummary ? sanitizeMeta(result.agentSummary) : "No fork summary available — the fork completed without producing a text summary. Check the target chat/task for any actions the fork already took before acting."}]`);
101
+ if (result.actions.length) lines.push(`[Fork actions: ${result.actions.join("; ")}]`);
102
+ if (result.historyPath) lines.push(`[Fork history: ${result.historyPath}]`);
96
103
  return lines.join("\n");
97
104
  });
98
105
  return blocks.join("\n\n") + "\n\n---\n\n";
@@ -1,7 +1,6 @@
1
1
  import * as os from "node:os";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { randomUUID } from "node:crypto";
5
4
  import { MENTION_ALL_USER_ID, ParallClient, ParallWs } from "@parall/sdk";
6
5
  import type {
7
6
  AgentConfigUpdateData,
@@ -19,7 +18,7 @@ import type {
19
18
  TaskAssignedData,
20
19
  TextContent,
21
20
  } from "@parall/sdk";
22
- import { buildEventBody, buildForkResultPrefix, buildForkScopePrefix } from "./event-format.js";
21
+ import { buildEventBody, buildEventBodyForForkResult, buildForkResultPrefix, buildForkScopePrefix } from "./event-format.js";
23
22
  import type {
24
23
  CleanupForkOpts,
25
24
  DispatchAdapter,
@@ -72,6 +71,7 @@ type DispatchableMessage = {
72
71
  thread_root_id?: string | null;
73
72
  hints?: { no_reply?: boolean } | null;
74
73
  attachments?: Array<{ id: string; file_name: string; file_size: number; mime_type: string }>;
74
+ created_at?: string;
75
75
  };
76
76
 
77
77
  type MessageDispatchDecision =
@@ -170,6 +170,7 @@ async function fetchAllChats(
170
170
  export class ParallAgentGateway {
171
171
  private readonly chatInfoMap = new Map<string, ChatInfo>();
172
172
  private readonly activeDispatches = new Map<string, ActiveDispatch>();
173
+ private readonly injectedTypingCounts = new Map<string, number>();
173
174
  private readonly dispatchedTasks = new Set<string>();
174
175
  private readonly dispatchedMessages = new Set<string>();
175
176
  private readonly forkStates = new Map<string, ActiveForkState>();
@@ -377,6 +378,42 @@ export class ParallAgentGateway {
377
378
  if (this.opts.ws.state === "connected") this.opts.ws.sendTyping(chatId, "stop");
378
379
  }
379
380
 
381
+ private shouldShowTyping(event: ParallEvent): boolean {
382
+ return event.type === "message" && event.targetId.startsWith("cht_") && !event.noReply;
383
+ }
384
+
385
+ private startInjectedTyping(event: ParallEvent) {
386
+ if (!this.shouldShowTyping(event)) return;
387
+ this.startTyping(event.targetId);
388
+ this.injectedTypingCounts.set(event.targetId, (this.injectedTypingCounts.get(event.targetId) ?? 0) + 1);
389
+ }
390
+
391
+ private takeInjectedTypingCount(chatId: string): number {
392
+ const count = this.injectedTypingCounts.get(chatId) ?? 0;
393
+ this.injectedTypingCounts.delete(chatId);
394
+ return count;
395
+ }
396
+
397
+ private async runDispatchWithTyping(
398
+ event: ParallEvent,
399
+ sessionKey: string,
400
+ bodyForAgent: string,
401
+ earlierEvents: ParallEvent[] = [],
402
+ captureText?: string[],
403
+ opts: { suppressStart?: boolean; injectedTypingCount?: number } = {},
404
+ ): Promise<boolean> {
405
+ const showTyping = !opts.suppressStart && (this.shouldShowTyping(event) || earlierEvents.some(e => this.shouldShowTyping(e)));
406
+ if (showTyping) this.startTyping(event.targetId);
407
+ try {
408
+ return await this.runDispatch(event, sessionKey, bodyForAgent, earlierEvents, captureText);
409
+ } finally {
410
+ if (showTyping) this.stopTyping(event.targetId);
411
+ for (let i = 0; i < (opts.injectedTypingCount ?? 0); i++) {
412
+ this.stopTyping(event.targetId);
413
+ }
414
+ }
415
+ }
416
+
380
417
  private buildDispatchContext(event: ParallEvent, sessionKey: string): DispatchContext {
381
418
  const binding = this.sessionBindings.get(sessionKey);
382
419
  return {
@@ -421,6 +458,7 @@ export class ParallAgentGateway {
421
458
  sender_id: event.senderId,
422
459
  sender_name: event.senderName,
423
460
  summary: event.body.substring(0, 200),
461
+ ...(event.sentAt ? { sent_at: event.sentAt } : {}),
424
462
  },
425
463
  });
426
464
  } catch (err) {
@@ -693,7 +731,8 @@ export class ParallAgentGateway {
693
731
  }
694
732
 
695
733
  if (!binding) {
696
- throw new Error(`runtime emitted ${runtimeEvent.type} before runtime_session`);
734
+ const detail = runtimeEvent.type === "error" ? `: ${runtimeEvent.message}` : "";
735
+ throw new Error(`runtime emitted ${runtimeEvent.type} before runtime_session${detail}`);
697
736
  }
698
737
  if (!triggerMessageSet) {
699
738
  triggerMessageSet = true;
@@ -778,7 +817,7 @@ export class ParallAgentGateway {
778
817
  const earlier = events.slice(0, -1);
779
818
  try {
780
819
  const batchText: string[] = [];
781
- const dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
820
+ const dispatched = await this.runDispatchWithTyping(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
782
821
  if (!dispatched) {
783
822
  // Shutdown short-circuit — resolve un-acked so the server requeues
784
823
  // for the replacement pod and stop draining further items.
@@ -823,6 +862,7 @@ export class ParallAgentGateway {
823
862
  ? `${first.type} from ${first.senderName} in ${first.targetName ?? fork.targetId}`
824
863
  : `${fork.processedEvents.length} events in ${first.targetName ?? fork.targetId}`,
825
864
  },
865
+ eventBodies: fork.processedEvents.map((e) => buildEventBodyForForkResult(e)),
826
866
  actions: [],
827
867
  agentSummary,
828
868
  historyPath,
@@ -864,7 +904,7 @@ export class ParallAgentGateway {
864
904
  if (this.draining) return;
865
905
  this.draining = true;
866
906
  try {
867
- while (this.dispatchState.mainBuffer.length > 0 || this.dispatchState.pendingForkResults.length > 0) {
907
+ while (this.dispatchState.mainBuffer.length > 0) {
868
908
  // Early shutdown guard — same reasoning as runForkDrainLoop: stop
869
909
  // before any input AgentSteps are persisted, leaving the buffer +
870
910
  // pendingForkResults intact so dispatch catch-up on the replacement
@@ -875,31 +915,6 @@ export class ParallAgentGateway {
875
915
  );
876
916
  break;
877
917
  }
878
- if (this.dispatchState.mainBuffer.length === 0 && this.dispatchState.pendingForkResults.length > 0) {
879
- const pending = this.dispatchState.pendingForkResults.splice(0);
880
- const forkPrefix = buildForkResultPrefix(pending);
881
- const syntheticEvent: ParallEvent = {
882
- type: "message",
883
- targetId: "_orchestrator",
884
- targetType: "system",
885
- senderId: "system",
886
- senderName: "system",
887
- messageId: `synthetic-${this.opts.accountId}-${randomUUID()}`,
888
- body: "[Orchestrator: fork session(s) completed — review results above]",
889
- };
890
- this.dispatchState.mainCurrentTargetId = undefined;
891
- this.dispatchState.mainPreDispatchBranchPoint =
892
- this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
893
- const dispatched = await this.runDispatch(syntheticEvent, this.opts.runtimeKey, forkPrefix + buildEventBody(syntheticEvent));
894
- if (!dispatched) {
895
- // Shutdown: put the fork results back at the head so the synthetic
896
- // event is regenerated on the replacement pod. Synthetic events are
897
- // not ack'd, so this is the only state we need to preserve.
898
- this.dispatchState.pendingForkResults.unshift(...pending);
899
- break;
900
- }
901
- continue;
902
- }
903
918
 
904
919
  const targetId = this.dispatchState.mainBuffer[0].targetId;
905
920
  const events: ParallEvent[] = [];
@@ -909,13 +924,21 @@ export class ParallAgentGateway {
909
924
 
910
925
  const event = events[events.length - 1];
911
926
  const earlier = events.slice(0, -1);
912
- const skipForkSplice = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
913
- const pendingFork = skipForkSplice ? [] : this.dispatchState.pendingForkResults.splice(0);
927
+ const hasPendingInjections = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
928
+ const injectedTypingCount = hasPendingInjections ? this.takeInjectedTypingCount(event.targetId) : 0;
929
+ const pendingFork = hasPendingInjections ? [] : this.dispatchState.pendingForkResults.splice(0);
914
930
  const forkPrefix = buildForkResultPrefix(pendingFork);
915
931
  this.dispatchState.mainCurrentTargetId = event.targetId;
916
932
  this.dispatchState.mainPreDispatchBranchPoint =
917
933
  this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
918
- const dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event), earlier);
934
+ const dispatched = await this.runDispatchWithTyping(
935
+ event,
936
+ this.opts.runtimeKey,
937
+ forkPrefix + buildEventBody(event),
938
+ earlier,
939
+ undefined,
940
+ { suppressStart: injectedTypingCount > 0, injectedTypingCount },
941
+ );
919
942
  if (!dispatched) {
920
943
  // Shutdown: skip the ack so the server redelivers these buffered
921
944
  // events to the replacement pod via dispatch catch-up. Put both the
@@ -957,7 +980,7 @@ export class ParallAgentGateway {
957
980
  this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
958
981
  let dispatched = false;
959
982
  try {
960
- dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
983
+ dispatched = await this.runDispatchWithTyping(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
961
984
  if (!dispatched) {
962
985
  // Shutdown short-circuit — restore the fork results so a future
963
986
  // pod can replay them, and return false so handleMessage skips ack.
@@ -973,15 +996,26 @@ export class ParallAgentGateway {
973
996
  if (this.shuttingDown) {
974
997
  return false;
975
998
  }
999
+ // Push synchronously BEFORE the (possibly async) steer attempt so
1000
+ // arrival order is preserved and the event cannot be orphaned in a
1001
+ // gap between the steer await and the push.
1002
+ this.dispatchState.mainBuffer.push(event);
976
1003
  if (
977
1004
  this.dispatchState.mainCurrentTargetId === event.targetId &&
978
- this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))
1005
+ await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))
979
1006
  ) {
1007
+ this.startInjectedTyping(event);
980
1008
  this.opts.log?.info(
981
- `parall[${this.opts.accountId}]: steer injected to stdin for ${event.messageId} (will drain for bookkeeping)`,
1009
+ `parall[${this.opts.accountId}]: steer injected for ${event.messageId} (will drain for bookkeeping)`,
982
1010
  );
983
1011
  }
984
- this.dispatchState.mainBuffer.push(event);
1012
+ // If the main dispatch cycle ended while we awaited the steer RPC,
1013
+ // our event is buffered but no drain is in flight. Re-enter the
1014
+ // drain to process it. The draining guard prevents re-entry.
1015
+ if (!this.dispatchState.mainDispatching && !this.draining && this.dispatchState.mainBuffer.length > 0) {
1016
+ this.dispatchState.mainDispatching = true;
1017
+ void this.drainMainBuffer();
1018
+ }
985
1019
  return false;
986
1020
  }
987
1021
 
@@ -1098,6 +1132,7 @@ export class ParallAgentGateway {
1098
1132
  threadRootId: message.thread_root_id ?? undefined,
1099
1133
  noReply: message.hints?.no_reply ?? false,
1100
1134
  attachments: attachments.length > 0 ? attachments : undefined,
1135
+ sentAt: message.created_at,
1101
1136
  ackSourceType: "message",
1102
1137
  ackSourceId: message.id,
1103
1138
  },
@@ -1118,8 +1153,6 @@ export class ParallAgentGateway {
1118
1153
  }
1119
1154
  const event = decision.event;
1120
1155
 
1121
- const willDispatch = !this.dispatchState.mainDispatching || this.dispatchState.mainCurrentTargetId !== chatId;
1122
- if (willDispatch) this.startTyping(chatId);
1123
1156
  try {
1124
1157
  const dispatched = await this.handleInboundEvent(event);
1125
1158
  if (dispatched) {
@@ -1130,8 +1163,6 @@ export class ParallAgentGateway {
1130
1163
  } catch (err) {
1131
1164
  this.opts.log?.error(`parall[${this.opts.accountId}]: event dispatch failed for ${data.id}: ${String(err)}`);
1132
1165
  this.dispatchedMessages.delete(data.id);
1133
- } finally {
1134
- if (willDispatch) this.stopTyping(chatId);
1135
1166
  }
1136
1167
  }
1137
1168
 
@@ -1160,6 +1191,7 @@ export class ParallAgentGateway {
1160
1191
  senderName: "system",
1161
1192
  messageId: task.id,
1162
1193
  body: parts.join("\n"),
1194
+ sentAt: task.updated_at ?? task.created_at,
1163
1195
  ackSourceType: "task_activity",
1164
1196
  ackSourceId,
1165
1197
  };
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export * from "./event-format.js";
5
5
  export * from "./prompt-fragments.js";
6
6
  export * from "./bridge-workspace.js";
7
7
  export * from "./dispatch-adapter.js";
8
+ export { createLogger } from "./logger.js";
8
9
  export * from "./gateway-base.js";
9
10
  export * from "./platform-config.js";
10
11
  export { writeSkillFiles, buildSkillReferences, SKILLS } from "./skills/index.js";
package/src/logger.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { GatewayLogger } from "./dispatch-adapter.js";
2
+
3
+ export function createLogger(prefix: string): GatewayLogger {
4
+ return {
5
+ info: (msg: string) => console.log(`${new Date().toISOString()} [${prefix}] ${msg}`),
6
+ warn: (msg: string) => console.warn(`${new Date().toISOString()} [${prefix}] ${msg}`),
7
+ error: (msg: string) => console.error(`${new Date().toISOString()} [${prefix}] ${msg}`),
8
+ };
9
+ }
package/src/types.ts CHANGED
@@ -2,6 +2,8 @@
2
2
  export type ForkResult = {
3
3
  forkSessionKey: string;
4
4
  sourceEvent: { type: string; targetId: string; summary: string };
5
+ /** Original event bodies that the fork processed, built via `buildEventBody()`. */
6
+ eventBodies: string[];
5
7
  actions: string[];
6
8
  /** Agent's own summary of what it did (captured from the fork's last text output). */
7
9
  agentSummary?: string;
@@ -43,6 +45,9 @@ export type ParallEvent = {
43
45
  scheduledFireAt?: string;
44
46
  /** Fire-time attached_to_uri snapshot, used for schedule events. */
45
47
  attachedUri?: string;
48
+ /** Original event timestamp (e.g., message.created_at). When present,
49
+ * input steps use this instead of server insertion time for ordering. */
50
+ sentAt?: string;
46
51
  ackSourceType?: "message" | "task_activity" | "comment" | "schedule_run";
47
52
  ackSourceId?: string;
48
53
  };