@parall/agent-core 1.26.3 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dispatch-adapter.d.ts +7 -0
- package/dist/dispatch-adapter.d.ts.map +1 -1
- package/dist/event-format.d.ts.map +1 -1
- package/dist/event-format.js +5 -1
- package/dist/gateway-base.d.ts +9 -0
- package/dist/gateway-base.d.ts.map +1 -1
- package/dist/gateway-base.js +134 -71
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/dispatch-adapter.ts +9 -0
- package/src/event-format.ts +3 -1
- package/src/gateway-base.ts +160 -68
- package/src/types.ts +4 -0
|
@@ -24,6 +24,11 @@ export type DispatchContext = {
|
|
|
24
24
|
log?: GatewayLogger;
|
|
25
25
|
};
|
|
26
26
|
export type RuntimeEvent = {
|
|
27
|
+
type: "runtime_session";
|
|
28
|
+
runtimeSessionId: string;
|
|
29
|
+
runtimeLaneKey?: string;
|
|
30
|
+
runtimeRef?: Record<string, unknown>;
|
|
31
|
+
} | {
|
|
27
32
|
type: "thinking";
|
|
28
33
|
text: string;
|
|
29
34
|
groupKey?: string;
|
|
@@ -80,5 +85,7 @@ export interface DispatchAdapter {
|
|
|
80
85
|
cleanupFork?(opts: CleanupForkOpts): Promise<void> | void;
|
|
81
86
|
/** Return the current on-disk branch point (leaf ID) for the given session, if available. */
|
|
82
87
|
getBranchPoint?(sessionKey: string): string | undefined;
|
|
88
|
+
/** Return the path to the session's history file on disk, if the runtime persists it. */
|
|
89
|
+
getSessionHistoryPath?(sessionKey: string): string | undefined;
|
|
83
90
|
}
|
|
84
91
|
//# sourceMappingURL=dispatch-adapter.d.ts.map
|
|
@@ -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 +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;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAK/D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,
|
|
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,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAK/D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAWnE"}
|
package/dist/event-format.js
CHANGED
|
@@ -69,7 +69,7 @@ export function buildForkScopePrefix(event) {
|
|
|
69
69
|
const targetLabel = event.targetName
|
|
70
70
|
? `"${event.targetName}" (prll://${event.targetId})`
|
|
71
71
|
: `prll://${event.targetId}`;
|
|
72
|
-
return `[Fork scope: You are a parallel worker
|
|
72
|
+
return `[Fork scope: You are a parallel worker forked from the main session to handle ${targetLabel}. The conversation history above belongs to the main session's in-progress dispatch — do NOT continue it or complete it, or produce external output (comments, messages) for work visible there. The main session owns its own output.\n\nOnly act on the event below within your target scope. If your event reveals findings about the main session's work, report them in your summary — do not post them externally.\n\nEnd with a brief "Fork summary" of actions taken and decisions made.]\n\n`;
|
|
73
73
|
}
|
|
74
74
|
export function buildForkResultPrefix(results) {
|
|
75
75
|
if (!results.length)
|
|
@@ -79,6 +79,10 @@ export function buildForkResultPrefix(results) {
|
|
|
79
79
|
lines.push(`[Handled: ${result.sourceEvent.type} — ${result.sourceEvent.summary}]`);
|
|
80
80
|
if (result.actions.length)
|
|
81
81
|
lines.push(`[Actions: ${result.actions.join("; ")}]`);
|
|
82
|
+
if (result.agentSummary)
|
|
83
|
+
lines.push(`[Summary: ${sanitizeMeta(result.agentSummary)}]`);
|
|
84
|
+
if (result.historyPath)
|
|
85
|
+
lines.push(`[History: ${result.historyPath}]`);
|
|
82
86
|
return lines.join("\n");
|
|
83
87
|
});
|
|
84
88
|
return blocks.join("\n\n") + "\n\n---\n\n";
|
package/dist/gateway-base.d.ts
CHANGED
|
@@ -28,6 +28,12 @@ export type ParallGatewayOptions = {
|
|
|
28
28
|
ws: ParallWs;
|
|
29
29
|
runtimeKey: string;
|
|
30
30
|
}) => Promise<void> | void;
|
|
31
|
+
onSessionBinding?: (state: {
|
|
32
|
+
sessionKey: string;
|
|
33
|
+
agentSessionId: string;
|
|
34
|
+
runtimeSessionId: string;
|
|
35
|
+
runtimeLaneKey: string;
|
|
36
|
+
}) => Promise<void> | void;
|
|
31
37
|
onBeforeDisconnect?: () => Promise<void> | void;
|
|
32
38
|
};
|
|
33
39
|
export declare function parseShutdownDeadlineMs(raw: string | undefined): number | undefined;
|
|
@@ -41,6 +47,7 @@ export declare class ParallAgentGateway {
|
|
|
41
47
|
private readonly dispatchState;
|
|
42
48
|
private sessionId;
|
|
43
49
|
private activeSessionId;
|
|
50
|
+
private readonly sessionBindings;
|
|
44
51
|
private heartbeatTimer;
|
|
45
52
|
private hadSuccessfulHello;
|
|
46
53
|
private lastHeartbeatAt;
|
|
@@ -61,11 +68,13 @@ export declare class ParallAgentGateway {
|
|
|
61
68
|
private createRuntimeStep;
|
|
62
69
|
private writeContextFile;
|
|
63
70
|
private updateContextFileStepId;
|
|
71
|
+
private updateContextFileSessionId;
|
|
64
72
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
65
73
|
private writeStepIdFile;
|
|
66
74
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
67
75
|
private clearStepIdFile;
|
|
68
76
|
private createInputStepsForEarlierEvents;
|
|
77
|
+
private bindRuntimeSession;
|
|
69
78
|
private runDispatch;
|
|
70
79
|
private runForkDrainLoop;
|
|
71
80
|
private drainMainBuffer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EACV,qBAAqB,
|
|
1
|
+
{"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway-base.js
CHANGED
|
@@ -66,6 +66,7 @@ export class ParallAgentGateway {
|
|
|
66
66
|
};
|
|
67
67
|
sessionId = "";
|
|
68
68
|
activeSessionId;
|
|
69
|
+
sessionBindings = new Map();
|
|
69
70
|
heartbeatTimer = null;
|
|
70
71
|
hadSuccessfulHello = false;
|
|
71
72
|
lastHeartbeatAt = Date.now();
|
|
@@ -266,6 +267,7 @@ export class ParallAgentGateway {
|
|
|
266
267
|
this.opts.ws.sendTyping(chatId, "stop");
|
|
267
268
|
}
|
|
268
269
|
buildDispatchContext(event, sessionKey) {
|
|
270
|
+
const binding = this.sessionBindings.get(sessionKey);
|
|
269
271
|
return {
|
|
270
272
|
accountId: this.opts.accountId,
|
|
271
273
|
apiUrl: this.opts.config.parall_url,
|
|
@@ -274,7 +276,7 @@ export class ParallAgentGateway {
|
|
|
274
276
|
agentUserId: this.opts.agentUserId,
|
|
275
277
|
runtimeType: this.opts.runtimeType,
|
|
276
278
|
runtimeKey: this.opts.runtimeKey,
|
|
277
|
-
sessionId:
|
|
279
|
+
sessionId: binding?.agentSessionId,
|
|
278
280
|
chatId: (event.type === "message" || event.type === "approval") ? event.targetId : undefined,
|
|
279
281
|
triggerMessageId: event.messageId,
|
|
280
282
|
noReply: event.noReply ?? false,
|
|
@@ -284,12 +286,10 @@ export class ParallAgentGateway {
|
|
|
284
286
|
log: this.opts.log,
|
|
285
287
|
};
|
|
286
288
|
}
|
|
287
|
-
async createInputStep(event) {
|
|
288
|
-
if (!this.activeSessionId)
|
|
289
|
-
return;
|
|
289
|
+
async createInputStep(sessionId, event) {
|
|
290
290
|
const target = resolveStepTarget(event);
|
|
291
291
|
try {
|
|
292
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
292
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
293
293
|
step_type: "input",
|
|
294
294
|
target_type: target.target_type,
|
|
295
295
|
target_id: target.target_id,
|
|
@@ -314,14 +314,12 @@ export class ParallAgentGateway {
|
|
|
314
314
|
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to create input step: ${String(err)}`);
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
|
-
async createRuntimeStep(event, runtimeEvent, stepIdFilePath, contextFilePath) {
|
|
318
|
-
if (!this.activeSessionId)
|
|
319
|
-
return;
|
|
317
|
+
async createRuntimeStep(sessionId, event, runtimeEvent, stepIdFilePath, contextFilePath) {
|
|
320
318
|
const target = resolveStepTarget(event);
|
|
321
319
|
try {
|
|
322
320
|
switch (runtimeEvent.type) {
|
|
323
321
|
case "thinking":
|
|
324
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
322
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
325
323
|
step_type: "thinking",
|
|
326
324
|
target_type: target.target_type,
|
|
327
325
|
target_id: target.target_id,
|
|
@@ -330,7 +328,7 @@ export class ParallAgentGateway {
|
|
|
330
328
|
});
|
|
331
329
|
break;
|
|
332
330
|
case "text":
|
|
333
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
331
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
334
332
|
step_type: "text",
|
|
335
333
|
target_type: target.target_type,
|
|
336
334
|
target_id: target.target_id,
|
|
@@ -343,7 +341,7 @@ export class ParallAgentGateway {
|
|
|
343
341
|
});
|
|
344
342
|
break;
|
|
345
343
|
case "tool_call": {
|
|
346
|
-
const step = await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
344
|
+
const step = await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
347
345
|
step_type: "tool_call",
|
|
348
346
|
target_type: target.target_type,
|
|
349
347
|
target_id: target.target_id,
|
|
@@ -366,7 +364,7 @@ export class ParallAgentGateway {
|
|
|
366
364
|
break;
|
|
367
365
|
}
|
|
368
366
|
case "tool_result":
|
|
369
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
367
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
370
368
|
step_type: "tool_result",
|
|
371
369
|
target_type: target.target_type,
|
|
372
370
|
target_id: target.target_id,
|
|
@@ -388,7 +386,7 @@ export class ParallAgentGateway {
|
|
|
388
386
|
}
|
|
389
387
|
break;
|
|
390
388
|
case "error":
|
|
391
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
389
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
392
390
|
step_type: "text",
|
|
393
391
|
target_type: target.target_type,
|
|
394
392
|
target_id: target.target_id,
|
|
@@ -422,6 +420,17 @@ export class ParallAgentGateway {
|
|
|
422
420
|
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file step_id ${filePath}: ${String(err)}`);
|
|
423
421
|
}
|
|
424
422
|
}
|
|
423
|
+
updateContextFileSessionId(filePath, sessionId) {
|
|
424
|
+
try {
|
|
425
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
426
|
+
const ctx = JSON.parse(raw);
|
|
427
|
+
ctx.session_id = sessionId;
|
|
428
|
+
fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
429
|
+
}
|
|
430
|
+
catch (err) {
|
|
431
|
+
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file session_id ${filePath}: ${String(err)}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
425
434
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
426
435
|
writeStepIdFile(filePath, stepId) {
|
|
427
436
|
try {
|
|
@@ -441,16 +450,56 @@ export class ParallAgentGateway {
|
|
|
441
450
|
// Best-effort cleanup.
|
|
442
451
|
}
|
|
443
452
|
}
|
|
444
|
-
async createInputStepsForEarlierEvents(events) {
|
|
453
|
+
async createInputStepsForEarlierEvents(sessionId, events) {
|
|
445
454
|
for (const event of events) {
|
|
446
|
-
await this.createInputStep(event);
|
|
455
|
+
await this.createInputStep(sessionId, event);
|
|
447
456
|
}
|
|
448
457
|
}
|
|
458
|
+
async bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath) {
|
|
459
|
+
const runtimeLaneKey = runtimeEvent.runtimeLaneKey || sessionKey;
|
|
460
|
+
const existing = this.sessionBindings.get(sessionKey);
|
|
461
|
+
if (existing &&
|
|
462
|
+
existing.runtimeLaneKey === runtimeLaneKey &&
|
|
463
|
+
existing.runtimeSessionId === runtimeEvent.runtimeSessionId) {
|
|
464
|
+
return existing;
|
|
465
|
+
}
|
|
466
|
+
const parentSessionId = sessionKey === this.opts.runtimeKey
|
|
467
|
+
? undefined
|
|
468
|
+
: this.sessionBindings.get(this.opts.runtimeKey)?.agentSessionId;
|
|
469
|
+
const runtimeRef = {
|
|
470
|
+
...(this.opts.runtimeRef ?? {}),
|
|
471
|
+
...(runtimeEvent.runtimeRef ?? {}),
|
|
472
|
+
};
|
|
473
|
+
const session = await this.opts.client.createAgentSession(this.opts.config.org_id, this.opts.agentUserId, {
|
|
474
|
+
runtime_type: this.opts.runtimeType,
|
|
475
|
+
runtime_key: runtimeLaneKey,
|
|
476
|
+
runtime_lane_key: runtimeLaneKey,
|
|
477
|
+
runtime_session_id: runtimeEvent.runtimeSessionId,
|
|
478
|
+
parent_session_id: parentSessionId,
|
|
479
|
+
runtime_ref: Object.keys(runtimeRef).length > 0 ? runtimeRef : undefined,
|
|
480
|
+
});
|
|
481
|
+
const binding = {
|
|
482
|
+
sessionKey,
|
|
483
|
+
agentSessionId: session.id,
|
|
484
|
+
runtimeLaneKey,
|
|
485
|
+
runtimeSessionId: runtimeEvent.runtimeSessionId,
|
|
486
|
+
parentSessionId,
|
|
487
|
+
};
|
|
488
|
+
this.sessionBindings.set(sessionKey, binding);
|
|
489
|
+
if (sessionKey === this.opts.runtimeKey) {
|
|
490
|
+
this.activeSessionId = session.id;
|
|
491
|
+
}
|
|
492
|
+
if (contextFilePath) {
|
|
493
|
+
this.updateContextFileSessionId(contextFilePath, session.id);
|
|
494
|
+
}
|
|
495
|
+
await this.opts.onSessionBinding?.(binding);
|
|
496
|
+
return binding;
|
|
497
|
+
}
|
|
449
498
|
// Returns true if the dispatch actually ran; false if skipped because we
|
|
450
499
|
// are shutting down. Callers MUST treat `false` as "not dispatched" and
|
|
451
500
|
// skip the server-side ack so the event stays in the dispatch queue for
|
|
452
501
|
// catch-up on the replacement pod — otherwise we silently drop work.
|
|
453
|
-
async runDispatch(event, sessionKey, bodyForAgent, earlierEvents = []) {
|
|
502
|
+
async runDispatch(event, sessionKey, bodyForAgent, earlierEvents = [], captureText) {
|
|
454
503
|
// Graceful shutdown: once SIGTERM has fired we stop accepting new work.
|
|
455
504
|
// In-flight dispatches that started before the flag flipped keep running
|
|
456
505
|
// and are awaited by shutdown() up to SHUTDOWN_DEADLINE_MS.
|
|
@@ -478,23 +527,10 @@ export class ParallAgentGateway {
|
|
|
478
527
|
// — JS event loop is single-threaded, so shutdown() cannot interleave
|
|
479
528
|
// here and miss our in-flight count.
|
|
480
529
|
this.inFlightDispatches++;
|
|
530
|
+
let binding = this.sessionBindings.get(sessionKey);
|
|
531
|
+
let inputStepsCreated = false;
|
|
532
|
+
let triggerMessageSet = false;
|
|
481
533
|
try {
|
|
482
|
-
if (this.activeSessionId) {
|
|
483
|
-
try {
|
|
484
|
-
await this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, this.activeSessionId, { status: "active", trigger_message_id: event.messageId });
|
|
485
|
-
}
|
|
486
|
-
catch (err) {
|
|
487
|
-
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to set session active: ${String(err)}`);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
// Persist input steps for "earlier events" (batched events that arrived
|
|
491
|
-
// while a dispatch was in flight) inside the in-flight window so a
|
|
492
|
-
// shutdown short-circuit BEFORE this point cannot leave orphan input
|
|
493
|
-
// steps that the replacement pod would duplicate on replay.
|
|
494
|
-
if (earlierEvents.length > 0) {
|
|
495
|
-
await this.createInputStepsForEarlierEvents(earlierEvents);
|
|
496
|
-
}
|
|
497
|
-
await this.createInputStep(event);
|
|
498
534
|
for await (const runtimeEvent of this.opts.dispatchAdapter.dispatch({
|
|
499
535
|
event,
|
|
500
536
|
earlierEvents,
|
|
@@ -502,17 +538,57 @@ export class ParallAgentGateway {
|
|
|
502
538
|
sessionKey,
|
|
503
539
|
context: dispatchContext,
|
|
504
540
|
})) {
|
|
505
|
-
|
|
541
|
+
if (runtimeEvent.type === "runtime_session") {
|
|
542
|
+
binding = await this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath);
|
|
543
|
+
if (!inputStepsCreated) {
|
|
544
|
+
// Persist input steps for "earlier events" (batched events that arrived
|
|
545
|
+
// while a dispatch was in flight) inside the in-flight window so a
|
|
546
|
+
// shutdown short-circuit BEFORE this point cannot leave orphan input
|
|
547
|
+
// steps that the replacement pod would duplicate on replay.
|
|
548
|
+
if (earlierEvents.length > 0) {
|
|
549
|
+
await this.createInputStepsForEarlierEvents(binding.agentSessionId, earlierEvents);
|
|
550
|
+
}
|
|
551
|
+
await this.createInputStep(binding.agentSessionId, event);
|
|
552
|
+
inputStepsCreated = true;
|
|
553
|
+
}
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (!binding) {
|
|
557
|
+
throw new Error(`runtime emitted ${runtimeEvent.type} before runtime_session`);
|
|
558
|
+
}
|
|
559
|
+
if (!triggerMessageSet) {
|
|
560
|
+
triggerMessageSet = true;
|
|
561
|
+
this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, binding.agentSessionId, { status: "active", trigger_message_id: event.messageId }).catch((err) => this.opts.log?.warn?.(`failed to set session active: ${err}`));
|
|
562
|
+
}
|
|
563
|
+
if (!inputStepsCreated) {
|
|
564
|
+
if (earlierEvents.length > 0) {
|
|
565
|
+
await this.createInputStepsForEarlierEvents(binding.agentSessionId, earlierEvents);
|
|
566
|
+
}
|
|
567
|
+
await this.createInputStep(binding.agentSessionId, event);
|
|
568
|
+
inputStepsCreated = true;
|
|
569
|
+
}
|
|
570
|
+
if (captureText && runtimeEvent.type === "text" && runtimeEvent.text) {
|
|
571
|
+
captureText.push(runtimeEvent.text);
|
|
572
|
+
}
|
|
573
|
+
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath);
|
|
574
|
+
}
|
|
575
|
+
if (!binding) {
|
|
576
|
+
throw new Error("runtime completed without runtime_session");
|
|
506
577
|
}
|
|
507
578
|
}
|
|
508
579
|
catch (err) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
580
|
+
if (binding) {
|
|
581
|
+
await this.createRuntimeStep(binding.agentSessionId, event, {
|
|
582
|
+
type: "error",
|
|
583
|
+
message: `Dispatch failed: ${String(err)}`,
|
|
584
|
+
}, stepIdFilePath, contextFilePath);
|
|
585
|
+
}
|
|
513
586
|
throw err;
|
|
514
587
|
}
|
|
515
588
|
finally {
|
|
589
|
+
if (triggerMessageSet && binding) {
|
|
590
|
+
this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, binding.agentSessionId, { status: "idle" }).catch((err) => this.opts.log?.warn?.(`failed to set session idle: ${err}`));
|
|
591
|
+
}
|
|
516
592
|
clearSessionMessageId(sessionKey);
|
|
517
593
|
clearDispatchMessageId(sessionKey);
|
|
518
594
|
clearDispatchNoReply(sessionKey);
|
|
@@ -522,14 +598,6 @@ export class ParallAgentGateway {
|
|
|
522
598
|
else if (stepIdFilePath) {
|
|
523
599
|
this.clearStepIdFile(stepIdFilePath);
|
|
524
600
|
}
|
|
525
|
-
if (this.activeSessionId) {
|
|
526
|
-
try {
|
|
527
|
-
await this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, this.activeSessionId, { status: "idle" });
|
|
528
|
-
}
|
|
529
|
-
catch (err) {
|
|
530
|
-
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to set session idle: ${String(err)}`);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
601
|
this.inFlightDispatches--;
|
|
534
602
|
if (this.inFlightDispatches === 0 && this.drainResolvers.length > 0) {
|
|
535
603
|
const resolvers = this.drainResolvers.splice(0);
|
|
@@ -540,6 +608,7 @@ export class ParallAgentGateway {
|
|
|
540
608
|
return true;
|
|
541
609
|
}
|
|
542
610
|
async runForkDrainLoop(fork) {
|
|
611
|
+
let lastCapturedText = [];
|
|
543
612
|
try {
|
|
544
613
|
while (fork.queue.length > 0) {
|
|
545
614
|
// Early shutdown guard — pure optimization to avoid wasted draining
|
|
@@ -558,7 +627,8 @@ export class ParallAgentGateway {
|
|
|
558
627
|
const last = events[events.length - 1];
|
|
559
628
|
const earlier = events.slice(0, -1);
|
|
560
629
|
try {
|
|
561
|
-
const
|
|
630
|
+
const batchText = [];
|
|
631
|
+
const dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
|
|
562
632
|
if (!dispatched) {
|
|
563
633
|
// Shutdown short-circuit — resolve un-acked so the server requeues
|
|
564
634
|
// for the replacement pod and stop draining further items.
|
|
@@ -567,6 +637,8 @@ export class ParallAgentGateway {
|
|
|
567
637
|
}
|
|
568
638
|
break;
|
|
569
639
|
}
|
|
640
|
+
if (batchText.length > 0)
|
|
641
|
+
lastCapturedText = batchText;
|
|
570
642
|
fork.processedEvents.push(...events);
|
|
571
643
|
for (const item of items) {
|
|
572
644
|
item.resolve(true);
|
|
@@ -587,6 +659,14 @@ export class ParallAgentGateway {
|
|
|
587
659
|
finally {
|
|
588
660
|
if (fork.processedEvents.length > 0) {
|
|
589
661
|
const first = fork.processedEvents[0];
|
|
662
|
+
const agentSummary = lastCapturedText.join("").trim() || undefined;
|
|
663
|
+
let historyPath;
|
|
664
|
+
try {
|
|
665
|
+
historyPath = this.opts.dispatchAdapter.getSessionHistoryPath?.(fork.fork.sessionKey);
|
|
666
|
+
}
|
|
667
|
+
catch (err) {
|
|
668
|
+
this.opts.log?.warn?.(`parall[${this.opts.accountId}]: failed to resolve fork history path: ${String(err)}`);
|
|
669
|
+
}
|
|
590
670
|
this.dispatchState.pendingForkResults.push({
|
|
591
671
|
forkSessionKey: fork.fork.sessionKey,
|
|
592
672
|
sourceEvent: {
|
|
@@ -597,10 +677,13 @@ export class ParallAgentGateway {
|
|
|
597
677
|
: `${fork.processedEvents.length} events in ${first.targetName ?? fork.targetId}`,
|
|
598
678
|
},
|
|
599
679
|
actions: [],
|
|
680
|
+
agentSummary,
|
|
681
|
+
historyPath,
|
|
600
682
|
});
|
|
601
683
|
}
|
|
602
684
|
this.forkStates.delete(fork.targetId);
|
|
603
685
|
this.dispatchState.activeForks.delete(fork.targetId);
|
|
686
|
+
const forkBinding = this.sessionBindings.get(fork.fork.sessionKey);
|
|
604
687
|
if (this.opts.dispatchAdapter.cleanupFork) {
|
|
605
688
|
const cleanupOpts = {
|
|
606
689
|
fork: fork.fork,
|
|
@@ -612,7 +695,7 @@ export class ParallAgentGateway {
|
|
|
612
695
|
agentUserId: this.opts.agentUserId,
|
|
613
696
|
runtimeType: this.opts.runtimeType,
|
|
614
697
|
runtimeKey: this.opts.runtimeKey,
|
|
615
|
-
sessionId:
|
|
698
|
+
sessionId: forkBinding?.agentSessionId,
|
|
616
699
|
noReply: false,
|
|
617
700
|
client: this.opts.client,
|
|
618
701
|
log: this.opts.log,
|
|
@@ -620,6 +703,10 @@ export class ParallAgentGateway {
|
|
|
620
703
|
};
|
|
621
704
|
await this.opts.dispatchAdapter.cleanupFork(cleanupOpts);
|
|
622
705
|
}
|
|
706
|
+
if (forkBinding) {
|
|
707
|
+
this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, forkBinding.agentSessionId, { status: "closed" }).catch(() => { });
|
|
708
|
+
this.sessionBindings.delete(fork.fork.sessionKey);
|
|
709
|
+
}
|
|
623
710
|
}
|
|
624
711
|
}
|
|
625
712
|
async drainMainBuffer() {
|
|
@@ -1233,20 +1320,6 @@ export class ParallAgentGateway {
|
|
|
1233
1320
|
try {
|
|
1234
1321
|
const count = await fetchAllChats(client, config.org_id, this.chatInfoMap);
|
|
1235
1322
|
log?.info(`parall[${this.opts.accountId}]: WebSocket connected, ${count} chats cached`);
|
|
1236
|
-
if (!this.activeSessionId) {
|
|
1237
|
-
try {
|
|
1238
|
-
const session = await client.createAgentSession(config.org_id, this.opts.agentUserId, {
|
|
1239
|
-
runtime_type: this.opts.runtimeType,
|
|
1240
|
-
runtime_key: this.opts.runtimeKey,
|
|
1241
|
-
runtime_ref: this.opts.runtimeRef,
|
|
1242
|
-
});
|
|
1243
|
-
this.activeSessionId = session.id;
|
|
1244
|
-
log?.info(`parall[${this.opts.accountId}]: created agent session ${session.id}`);
|
|
1245
|
-
}
|
|
1246
|
-
catch (err) {
|
|
1247
|
-
log?.warn(`parall[${this.opts.accountId}]: failed to create agent session: ${String(err)}`);
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
1323
|
await this.opts.onSessionReady?.({
|
|
1251
1324
|
activeSessionId: this.activeSessionId,
|
|
1252
1325
|
ws: this.opts.ws,
|
|
@@ -1329,16 +1402,6 @@ export class ParallAgentGateway {
|
|
|
1329
1402
|
}
|
|
1330
1403
|
this.activeDispatches.clear();
|
|
1331
1404
|
await this.opts.onBeforeDisconnect?.();
|
|
1332
|
-
if (this.activeSessionId) {
|
|
1333
|
-
try {
|
|
1334
|
-
await this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, this.activeSessionId, {
|
|
1335
|
-
status: "completed",
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
catch (err) {
|
|
1339
|
-
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to complete session: ${String(err)}`);
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
1405
|
this.opts.ws.disconnect();
|
|
1343
1406
|
this.opts.log?.info(`parall[${this.opts.accountId}]: disconnected`);
|
|
1344
1407
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export type ForkResult = {
|
|
|
7
7
|
summary: string;
|
|
8
8
|
};
|
|
9
9
|
actions: string[];
|
|
10
|
+
/** Agent's own summary of what it did (captured from the fork's last text output). */
|
|
11
|
+
agentSummary?: string;
|
|
12
|
+
/** Path to the fork session's history file on disk, when the runtime persists it. */
|
|
13
|
+
historyPath?: string;
|
|
10
14
|
};
|
|
11
15
|
/** Mutable dispatch state for the orchestrator gateway. */
|
|
12
16
|
export type DispatchState = {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/agent-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.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
|
+
"@parall/sdk": "1.28.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.0.0",
|
package/src/dispatch-adapter.ts
CHANGED
|
@@ -27,6 +27,12 @@ export type DispatchContext = {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
export type RuntimeEvent =
|
|
30
|
+
| {
|
|
31
|
+
type: "runtime_session";
|
|
32
|
+
runtimeSessionId: string;
|
|
33
|
+
runtimeLaneKey?: string;
|
|
34
|
+
runtimeRef?: Record<string, unknown>;
|
|
35
|
+
}
|
|
30
36
|
| { type: "thinking"; text: string; groupKey?: string }
|
|
31
37
|
| { type: "tool_call"; callId: string; toolName: string; input: unknown; startedAt?: string; groupKey?: string }
|
|
32
38
|
| { type: "tool_result"; callId: string; toolName: string; output: string; error?: string; durationMs?: number; groupKey?: string }
|
|
@@ -69,4 +75,7 @@ export interface DispatchAdapter {
|
|
|
69
75
|
|
|
70
76
|
/** Return the current on-disk branch point (leaf ID) for the given session, if available. */
|
|
71
77
|
getBranchPoint?(sessionKey: string): string | undefined;
|
|
78
|
+
|
|
79
|
+
/** Return the path to the session's history file on disk, if the runtime persists it. */
|
|
80
|
+
getSessionHistoryPath?(sessionKey: string): string | undefined;
|
|
72
81
|
}
|
package/src/event-format.ts
CHANGED
|
@@ -64,7 +64,7 @@ export function buildForkScopePrefix(event: ParallEvent): string {
|
|
|
64
64
|
const targetLabel = event.targetName
|
|
65
65
|
? `"${event.targetName}" (prll://${event.targetId})`
|
|
66
66
|
: `prll://${event.targetId}`;
|
|
67
|
-
return `[Fork scope: You are a parallel worker
|
|
67
|
+
return `[Fork scope: You are a parallel worker forked from the main session to handle ${targetLabel}. The conversation history above belongs to the main session's in-progress dispatch — do NOT continue it or complete it, or produce external output (comments, messages) for work visible there. The main session owns its own output.\n\nOnly act on the event below within your target scope. If your event reveals findings about the main session's work, report them in your summary — do not post them externally.\n\nEnd with a brief "Fork summary" of actions taken and decisions made.]\n\n`;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export function buildForkResultPrefix(results: ForkResult[]): string {
|
|
@@ -73,6 +73,8 @@ export function buildForkResultPrefix(results: ForkResult[]): string {
|
|
|
73
73
|
const lines = [`[Fork result]`];
|
|
74
74
|
lines.push(`[Handled: ${result.sourceEvent.type} — ${result.sourceEvent.summary}]`);
|
|
75
75
|
if (result.actions.length) lines.push(`[Actions: ${result.actions.join("; ")}]`);
|
|
76
|
+
if (result.agentSummary) lines.push(`[Summary: ${sanitizeMeta(result.agentSummary)}]`);
|
|
77
|
+
if (result.historyPath) lines.push(`[History: ${result.historyPath}]`);
|
|
76
78
|
return lines.join("\n");
|
|
77
79
|
});
|
|
78
80
|
return blocks.join("\n\n") + "\n\n---\n\n";
|
package/src/gateway-base.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { randomUUID } from "node:crypto";
|
|
|
5
5
|
import { MENTION_ALL_USER_ID, ParallClient, ParallWs } from "@parall/sdk";
|
|
6
6
|
import type {
|
|
7
7
|
AgentConfigUpdateData,
|
|
8
|
+
AgentSessionDB,
|
|
8
9
|
Approval,
|
|
9
10
|
Chat,
|
|
10
11
|
ChatUpdateData,
|
|
@@ -104,9 +105,18 @@ export type ParallGatewayOptions = {
|
|
|
104
105
|
stepIdFilePathForSession?: (sessionKey: string) => string | undefined;
|
|
105
106
|
onConfigUpdate?: (data: AgentConfigUpdateData) => Promise<void> | void;
|
|
106
107
|
onSessionReady?: (state: { activeSessionId?: string; ws: ParallWs; runtimeKey: string }) => Promise<void> | void;
|
|
108
|
+
onSessionBinding?: (state: { sessionKey: string; agentSessionId: string; runtimeSessionId: string; runtimeLaneKey: string }) => Promise<void> | void;
|
|
107
109
|
onBeforeDisconnect?: () => Promise<void> | void;
|
|
108
110
|
};
|
|
109
111
|
|
|
112
|
+
type AgentSessionBinding = {
|
|
113
|
+
sessionKey: string;
|
|
114
|
+
agentSessionId: string;
|
|
115
|
+
runtimeLaneKey: string;
|
|
116
|
+
runtimeSessionId: string;
|
|
117
|
+
parentSessionId?: string;
|
|
118
|
+
};
|
|
119
|
+
|
|
110
120
|
// Parse PRLL_SHUTDOWN_DEADLINE_MS (or any string env value) into a positive
|
|
111
121
|
// integer milliseconds value, or undefined if unset/invalid. Runtimes pass
|
|
112
122
|
// the result into ParallGatewayOptions.shutdownDeadlineMs; leaving it
|
|
@@ -172,6 +182,7 @@ export class ParallAgentGateway {
|
|
|
172
182
|
|
|
173
183
|
private sessionId = "";
|
|
174
184
|
private activeSessionId: string | undefined;
|
|
185
|
+
private readonly sessionBindings = new Map<string, AgentSessionBinding>();
|
|
175
186
|
private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
176
187
|
private hadSuccessfulHello = false;
|
|
177
188
|
private lastHeartbeatAt = Date.now();
|
|
@@ -367,6 +378,7 @@ export class ParallAgentGateway {
|
|
|
367
378
|
}
|
|
368
379
|
|
|
369
380
|
private buildDispatchContext(event: ParallEvent, sessionKey: string): DispatchContext {
|
|
381
|
+
const binding = this.sessionBindings.get(sessionKey);
|
|
370
382
|
return {
|
|
371
383
|
accountId: this.opts.accountId,
|
|
372
384
|
apiUrl: this.opts.config.parall_url,
|
|
@@ -375,7 +387,7 @@ export class ParallAgentGateway {
|
|
|
375
387
|
agentUserId: this.opts.agentUserId,
|
|
376
388
|
runtimeType: this.opts.runtimeType,
|
|
377
389
|
runtimeKey: this.opts.runtimeKey,
|
|
378
|
-
sessionId:
|
|
390
|
+
sessionId: binding?.agentSessionId,
|
|
379
391
|
chatId: (event.type === "message" || event.type === "approval") ? event.targetId : undefined,
|
|
380
392
|
triggerMessageId: event.messageId,
|
|
381
393
|
noReply: event.noReply ?? false,
|
|
@@ -386,11 +398,10 @@ export class ParallAgentGateway {
|
|
|
386
398
|
};
|
|
387
399
|
}
|
|
388
400
|
|
|
389
|
-
private async createInputStep(event: ParallEvent) {
|
|
390
|
-
if (!this.activeSessionId) return;
|
|
401
|
+
private async createInputStep(sessionId: string, event: ParallEvent) {
|
|
391
402
|
const target = resolveStepTarget(event);
|
|
392
403
|
try {
|
|
393
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
404
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
394
405
|
step_type: "input",
|
|
395
406
|
target_type: target.target_type,
|
|
396
407
|
target_id: target.target_id,
|
|
@@ -417,14 +428,13 @@ export class ParallAgentGateway {
|
|
|
417
428
|
}
|
|
418
429
|
}
|
|
419
430
|
|
|
420
|
-
private async createRuntimeStep(event: ParallEvent, runtimeEvent: RuntimeEvent, stepIdFilePath?: string, contextFilePath?: string) {
|
|
421
|
-
if (!this.activeSessionId) return;
|
|
431
|
+
private async createRuntimeStep(sessionId: string, event: ParallEvent, runtimeEvent: RuntimeEvent, stepIdFilePath?: string, contextFilePath?: string) {
|
|
422
432
|
|
|
423
433
|
const target = resolveStepTarget(event);
|
|
424
434
|
try {
|
|
425
435
|
switch (runtimeEvent.type) {
|
|
426
436
|
case "thinking":
|
|
427
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
437
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
428
438
|
step_type: "thinking",
|
|
429
439
|
target_type: target.target_type,
|
|
430
440
|
target_id: target.target_id,
|
|
@@ -434,7 +444,7 @@ export class ParallAgentGateway {
|
|
|
434
444
|
break;
|
|
435
445
|
|
|
436
446
|
case "text":
|
|
437
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
447
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
438
448
|
step_type: "text",
|
|
439
449
|
target_type: target.target_type,
|
|
440
450
|
target_id: target.target_id,
|
|
@@ -448,7 +458,7 @@ export class ParallAgentGateway {
|
|
|
448
458
|
break;
|
|
449
459
|
|
|
450
460
|
case "tool_call": {
|
|
451
|
-
const step = await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
461
|
+
const step = await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
452
462
|
step_type: "tool_call",
|
|
453
463
|
target_type: target.target_type,
|
|
454
464
|
target_id: target.target_id,
|
|
@@ -471,7 +481,7 @@ export class ParallAgentGateway {
|
|
|
471
481
|
}
|
|
472
482
|
|
|
473
483
|
case "tool_result":
|
|
474
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
484
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
475
485
|
step_type: "tool_result",
|
|
476
486
|
target_type: target.target_type,
|
|
477
487
|
target_id: target.target_id,
|
|
@@ -493,7 +503,7 @@ export class ParallAgentGateway {
|
|
|
493
503
|
break;
|
|
494
504
|
|
|
495
505
|
case "error":
|
|
496
|
-
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId,
|
|
506
|
+
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
497
507
|
step_type: "text",
|
|
498
508
|
target_type: target.target_type,
|
|
499
509
|
target_id: target.target_id,
|
|
@@ -527,6 +537,17 @@ export class ParallAgentGateway {
|
|
|
527
537
|
}
|
|
528
538
|
}
|
|
529
539
|
|
|
540
|
+
private updateContextFileSessionId(filePath: string, sessionId: string) {
|
|
541
|
+
try {
|
|
542
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
543
|
+
const ctx = JSON.parse(raw);
|
|
544
|
+
ctx.session_id = sessionId;
|
|
545
|
+
fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
546
|
+
} catch (err) {
|
|
547
|
+
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to update context file session_id ${filePath}: ${String(err)}`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
530
551
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
531
552
|
private writeStepIdFile(filePath: string, stepId: string) {
|
|
532
553
|
try {
|
|
@@ -546,12 +567,60 @@ export class ParallAgentGateway {
|
|
|
546
567
|
}
|
|
547
568
|
}
|
|
548
569
|
|
|
549
|
-
private async createInputStepsForEarlierEvents(events: ParallEvent[]) {
|
|
570
|
+
private async createInputStepsForEarlierEvents(sessionId: string, events: ParallEvent[]) {
|
|
550
571
|
for (const event of events) {
|
|
551
|
-
await this.createInputStep(event);
|
|
572
|
+
await this.createInputStep(sessionId, event);
|
|
552
573
|
}
|
|
553
574
|
}
|
|
554
575
|
|
|
576
|
+
private async bindRuntimeSession(
|
|
577
|
+
sessionKey: string,
|
|
578
|
+
runtimeEvent: Extract<RuntimeEvent, { type: "runtime_session" }>,
|
|
579
|
+
contextFilePath?: string,
|
|
580
|
+
): Promise<AgentSessionBinding> {
|
|
581
|
+
const runtimeLaneKey = runtimeEvent.runtimeLaneKey || sessionKey;
|
|
582
|
+
const existing = this.sessionBindings.get(sessionKey);
|
|
583
|
+
if (
|
|
584
|
+
existing &&
|
|
585
|
+
existing.runtimeLaneKey === runtimeLaneKey &&
|
|
586
|
+
existing.runtimeSessionId === runtimeEvent.runtimeSessionId
|
|
587
|
+
) {
|
|
588
|
+
return existing;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const parentSessionId = sessionKey === this.opts.runtimeKey
|
|
592
|
+
? undefined
|
|
593
|
+
: this.sessionBindings.get(this.opts.runtimeKey)?.agentSessionId;
|
|
594
|
+
const runtimeRef = {
|
|
595
|
+
...(this.opts.runtimeRef ?? {}),
|
|
596
|
+
...(runtimeEvent.runtimeRef ?? {}),
|
|
597
|
+
};
|
|
598
|
+
const session: AgentSessionDB = await this.opts.client.createAgentSession(this.opts.config.org_id, this.opts.agentUserId, {
|
|
599
|
+
runtime_type: this.opts.runtimeType,
|
|
600
|
+
runtime_key: runtimeLaneKey,
|
|
601
|
+
runtime_lane_key: runtimeLaneKey,
|
|
602
|
+
runtime_session_id: runtimeEvent.runtimeSessionId,
|
|
603
|
+
parent_session_id: parentSessionId,
|
|
604
|
+
runtime_ref: Object.keys(runtimeRef).length > 0 ? runtimeRef : undefined,
|
|
605
|
+
});
|
|
606
|
+
const binding: AgentSessionBinding = {
|
|
607
|
+
sessionKey,
|
|
608
|
+
agentSessionId: session.id,
|
|
609
|
+
runtimeLaneKey,
|
|
610
|
+
runtimeSessionId: runtimeEvent.runtimeSessionId,
|
|
611
|
+
parentSessionId,
|
|
612
|
+
};
|
|
613
|
+
this.sessionBindings.set(sessionKey, binding);
|
|
614
|
+
if (sessionKey === this.opts.runtimeKey) {
|
|
615
|
+
this.activeSessionId = session.id;
|
|
616
|
+
}
|
|
617
|
+
if (contextFilePath) {
|
|
618
|
+
this.updateContextFileSessionId(contextFilePath, session.id);
|
|
619
|
+
}
|
|
620
|
+
await this.opts.onSessionBinding?.(binding);
|
|
621
|
+
return binding;
|
|
622
|
+
}
|
|
623
|
+
|
|
555
624
|
// Returns true if the dispatch actually ran; false if skipped because we
|
|
556
625
|
// are shutting down. Callers MUST treat `false` as "not dispatched" and
|
|
557
626
|
// skip the server-side ack so the event stays in the dispatch queue for
|
|
@@ -561,6 +630,7 @@ export class ParallAgentGateway {
|
|
|
561
630
|
sessionKey: string,
|
|
562
631
|
bodyForAgent: string,
|
|
563
632
|
earlierEvents: ParallEvent[] = [],
|
|
633
|
+
captureText?: string[],
|
|
564
634
|
): Promise<boolean> {
|
|
565
635
|
// Graceful shutdown: once SIGTERM has fired we stop accepting new work.
|
|
566
636
|
// In-flight dispatches that started before the flag flipped keep running
|
|
@@ -595,24 +665,10 @@ export class ParallAgentGateway {
|
|
|
595
665
|
// — JS event loop is single-threaded, so shutdown() cannot interleave
|
|
596
666
|
// here and miss our in-flight count.
|
|
597
667
|
this.inFlightDispatches++;
|
|
668
|
+
let binding = this.sessionBindings.get(sessionKey);
|
|
669
|
+
let inputStepsCreated = false;
|
|
670
|
+
let triggerMessageSet = false;
|
|
598
671
|
try {
|
|
599
|
-
if (this.activeSessionId) {
|
|
600
|
-
try {
|
|
601
|
-
await this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, this.activeSessionId, { status: "active", trigger_message_id: event.messageId });
|
|
602
|
-
} catch (err) {
|
|
603
|
-
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to set session active: ${String(err)}`);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// Persist input steps for "earlier events" (batched events that arrived
|
|
608
|
-
// while a dispatch was in flight) inside the in-flight window so a
|
|
609
|
-
// shutdown short-circuit BEFORE this point cannot leave orphan input
|
|
610
|
-
// steps that the replacement pod would duplicate on replay.
|
|
611
|
-
if (earlierEvents.length > 0) {
|
|
612
|
-
await this.createInputStepsForEarlierEvents(earlierEvents);
|
|
613
|
-
}
|
|
614
|
-
await this.createInputStep(event);
|
|
615
|
-
|
|
616
672
|
for await (const runtimeEvent of this.opts.dispatchAdapter.dispatch({
|
|
617
673
|
event,
|
|
618
674
|
earlierEvents,
|
|
@@ -620,15 +676,62 @@ export class ParallAgentGateway {
|
|
|
620
676
|
sessionKey,
|
|
621
677
|
context: dispatchContext,
|
|
622
678
|
})) {
|
|
623
|
-
|
|
679
|
+
if (runtimeEvent.type === "runtime_session") {
|
|
680
|
+
binding = await this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath);
|
|
681
|
+
if (!inputStepsCreated) {
|
|
682
|
+
// Persist input steps for "earlier events" (batched events that arrived
|
|
683
|
+
// while a dispatch was in flight) inside the in-flight window so a
|
|
684
|
+
// shutdown short-circuit BEFORE this point cannot leave orphan input
|
|
685
|
+
// steps that the replacement pod would duplicate on replay.
|
|
686
|
+
if (earlierEvents.length > 0) {
|
|
687
|
+
await this.createInputStepsForEarlierEvents(binding.agentSessionId, earlierEvents);
|
|
688
|
+
}
|
|
689
|
+
await this.createInputStep(binding.agentSessionId, event);
|
|
690
|
+
inputStepsCreated = true;
|
|
691
|
+
}
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (!binding) {
|
|
696
|
+
throw new Error(`runtime emitted ${runtimeEvent.type} before runtime_session`);
|
|
697
|
+
}
|
|
698
|
+
if (!triggerMessageSet) {
|
|
699
|
+
triggerMessageSet = true;
|
|
700
|
+
this.opts.client.updateAgentSession(
|
|
701
|
+
this.opts.config.org_id, this.opts.agentUserId, binding.agentSessionId,
|
|
702
|
+
{ status: "active", trigger_message_id: event.messageId },
|
|
703
|
+
).catch((err) => this.opts.log?.warn?.(`failed to set session active: ${err}`));
|
|
704
|
+
}
|
|
705
|
+
if (!inputStepsCreated) {
|
|
706
|
+
if (earlierEvents.length > 0) {
|
|
707
|
+
await this.createInputStepsForEarlierEvents(binding.agentSessionId, earlierEvents);
|
|
708
|
+
}
|
|
709
|
+
await this.createInputStep(binding.agentSessionId, event);
|
|
710
|
+
inputStepsCreated = true;
|
|
711
|
+
}
|
|
712
|
+
if (captureText && runtimeEvent.type === "text" && runtimeEvent.text) {
|
|
713
|
+
captureText.push(runtimeEvent.text);
|
|
714
|
+
}
|
|
715
|
+
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath);
|
|
716
|
+
}
|
|
717
|
+
if (!binding) {
|
|
718
|
+
throw new Error("runtime completed without runtime_session");
|
|
624
719
|
}
|
|
625
720
|
} catch (err) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
721
|
+
if (binding) {
|
|
722
|
+
await this.createRuntimeStep(binding.agentSessionId, event, {
|
|
723
|
+
type: "error",
|
|
724
|
+
message: `Dispatch failed: ${String(err)}`,
|
|
725
|
+
}, stepIdFilePath, contextFilePath);
|
|
726
|
+
}
|
|
630
727
|
throw err;
|
|
631
728
|
} finally {
|
|
729
|
+
if (triggerMessageSet && binding) {
|
|
730
|
+
this.opts.client.updateAgentSession(
|
|
731
|
+
this.opts.config.org_id, this.opts.agentUserId, binding.agentSessionId,
|
|
732
|
+
{ status: "idle" },
|
|
733
|
+
).catch((err) => this.opts.log?.warn?.(`failed to set session idle: ${err}`));
|
|
734
|
+
}
|
|
632
735
|
clearSessionMessageId(sessionKey);
|
|
633
736
|
clearDispatchMessageId(sessionKey);
|
|
634
737
|
clearDispatchNoReply(sessionKey);
|
|
@@ -637,13 +740,6 @@ export class ParallAgentGateway {
|
|
|
637
740
|
} else if (stepIdFilePath) {
|
|
638
741
|
this.clearStepIdFile(stepIdFilePath);
|
|
639
742
|
}
|
|
640
|
-
if (this.activeSessionId) {
|
|
641
|
-
try {
|
|
642
|
-
await this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, this.activeSessionId, { status: "idle" });
|
|
643
|
-
} catch (err) {
|
|
644
|
-
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to set session idle: ${String(err)}`);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
743
|
this.inFlightDispatches--;
|
|
648
744
|
if (this.inFlightDispatches === 0 && this.drainResolvers.length > 0) {
|
|
649
745
|
const resolvers = this.drainResolvers.splice(0);
|
|
@@ -654,6 +750,7 @@ export class ParallAgentGateway {
|
|
|
654
750
|
}
|
|
655
751
|
|
|
656
752
|
private async runForkDrainLoop(fork: ActiveForkState) {
|
|
753
|
+
let lastCapturedText: string[] = [];
|
|
657
754
|
try {
|
|
658
755
|
while (fork.queue.length > 0) {
|
|
659
756
|
// Early shutdown guard — pure optimization to avoid wasted draining
|
|
@@ -671,7 +768,8 @@ export class ParallAgentGateway {
|
|
|
671
768
|
const last = events[events.length - 1];
|
|
672
769
|
const earlier = events.slice(0, -1);
|
|
673
770
|
try {
|
|
674
|
-
const
|
|
771
|
+
const batchText: string[] = [];
|
|
772
|
+
const dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
|
|
675
773
|
if (!dispatched) {
|
|
676
774
|
// Shutdown short-circuit — resolve un-acked so the server requeues
|
|
677
775
|
// for the replacement pod and stop draining further items.
|
|
@@ -680,6 +778,7 @@ export class ParallAgentGateway {
|
|
|
680
778
|
}
|
|
681
779
|
break;
|
|
682
780
|
}
|
|
781
|
+
if (batchText.length > 0) lastCapturedText = batchText;
|
|
683
782
|
fork.processedEvents.push(...events);
|
|
684
783
|
for (const item of items) {
|
|
685
784
|
item.resolve(true);
|
|
@@ -699,6 +798,13 @@ export class ParallAgentGateway {
|
|
|
699
798
|
} finally {
|
|
700
799
|
if (fork.processedEvents.length > 0) {
|
|
701
800
|
const first = fork.processedEvents[0];
|
|
801
|
+
const agentSummary = lastCapturedText.join("").trim() || undefined;
|
|
802
|
+
let historyPath: string | undefined;
|
|
803
|
+
try {
|
|
804
|
+
historyPath = this.opts.dispatchAdapter.getSessionHistoryPath?.(fork.fork.sessionKey);
|
|
805
|
+
} catch (err) {
|
|
806
|
+
this.opts.log?.warn?.(`parall[${this.opts.accountId}]: failed to resolve fork history path: ${String(err)}`);
|
|
807
|
+
}
|
|
702
808
|
this.dispatchState.pendingForkResults.push({
|
|
703
809
|
forkSessionKey: fork.fork.sessionKey,
|
|
704
810
|
sourceEvent: {
|
|
@@ -709,10 +815,13 @@ export class ParallAgentGateway {
|
|
|
709
815
|
: `${fork.processedEvents.length} events in ${first.targetName ?? fork.targetId}`,
|
|
710
816
|
},
|
|
711
817
|
actions: [],
|
|
818
|
+
agentSummary,
|
|
819
|
+
historyPath,
|
|
712
820
|
});
|
|
713
821
|
}
|
|
714
822
|
this.forkStates.delete(fork.targetId);
|
|
715
823
|
this.dispatchState.activeForks.delete(fork.targetId);
|
|
824
|
+
const forkBinding = this.sessionBindings.get(fork.fork.sessionKey);
|
|
716
825
|
if (this.opts.dispatchAdapter.cleanupFork) {
|
|
717
826
|
const cleanupOpts: CleanupForkOpts = {
|
|
718
827
|
fork: fork.fork,
|
|
@@ -724,7 +833,7 @@ export class ParallAgentGateway {
|
|
|
724
833
|
agentUserId: this.opts.agentUserId,
|
|
725
834
|
runtimeType: this.opts.runtimeType,
|
|
726
835
|
runtimeKey: this.opts.runtimeKey,
|
|
727
|
-
sessionId:
|
|
836
|
+
sessionId: forkBinding?.agentSessionId,
|
|
728
837
|
noReply: false,
|
|
729
838
|
client: this.opts.client,
|
|
730
839
|
log: this.opts.log,
|
|
@@ -732,6 +841,13 @@ export class ParallAgentGateway {
|
|
|
732
841
|
};
|
|
733
842
|
await this.opts.dispatchAdapter.cleanupFork(cleanupOpts);
|
|
734
843
|
}
|
|
844
|
+
if (forkBinding) {
|
|
845
|
+
this.opts.client.updateAgentSession(
|
|
846
|
+
this.opts.config.org_id, this.opts.agentUserId, forkBinding.agentSessionId,
|
|
847
|
+
{ status: "closed" },
|
|
848
|
+
).catch(() => {});
|
|
849
|
+
this.sessionBindings.delete(fork.fork.sessionKey);
|
|
850
|
+
}
|
|
735
851
|
}
|
|
736
852
|
}
|
|
737
853
|
|
|
@@ -1365,20 +1481,6 @@ export class ParallAgentGateway {
|
|
|
1365
1481
|
const count = await fetchAllChats(client, config.org_id, this.chatInfoMap);
|
|
1366
1482
|
log?.info(`parall[${this.opts.accountId}]: WebSocket connected, ${count} chats cached`);
|
|
1367
1483
|
|
|
1368
|
-
if (!this.activeSessionId) {
|
|
1369
|
-
try {
|
|
1370
|
-
const session = await client.createAgentSession(config.org_id, this.opts.agentUserId, {
|
|
1371
|
-
runtime_type: this.opts.runtimeType,
|
|
1372
|
-
runtime_key: this.opts.runtimeKey,
|
|
1373
|
-
runtime_ref: this.opts.runtimeRef,
|
|
1374
|
-
});
|
|
1375
|
-
this.activeSessionId = session.id;
|
|
1376
|
-
log?.info(`parall[${this.opts.accountId}]: created agent session ${session.id}`);
|
|
1377
|
-
} catch (err) {
|
|
1378
|
-
log?.warn(`parall[${this.opts.accountId}]: failed to create agent session: ${String(err)}`);
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
1484
|
await this.opts.onSessionReady?.({
|
|
1383
1485
|
activeSessionId: this.activeSessionId,
|
|
1384
1486
|
ws: this.opts.ws,
|
|
@@ -1466,16 +1568,6 @@ export class ParallAgentGateway {
|
|
|
1466
1568
|
|
|
1467
1569
|
await this.opts.onBeforeDisconnect?.();
|
|
1468
1570
|
|
|
1469
|
-
if (this.activeSessionId) {
|
|
1470
|
-
try {
|
|
1471
|
-
await this.opts.client.updateAgentSession(this.opts.config.org_id, this.opts.agentUserId, this.activeSessionId, {
|
|
1472
|
-
status: "completed",
|
|
1473
|
-
});
|
|
1474
|
-
} catch (err) {
|
|
1475
|
-
this.opts.log?.warn(`parall[${this.opts.accountId}]: failed to complete session: ${String(err)}`);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
1571
|
this.opts.ws.disconnect();
|
|
1480
1572
|
this.opts.log?.info(`parall[${this.opts.accountId}]: disconnected`);
|
|
1481
1573
|
}
|
package/src/types.ts
CHANGED
|
@@ -3,6 +3,10 @@ export type ForkResult = {
|
|
|
3
3
|
forkSessionKey: string;
|
|
4
4
|
sourceEvent: { type: string; targetId: string; summary: string };
|
|
5
5
|
actions: string[];
|
|
6
|
+
/** Agent's own summary of what it did (captured from the fork's last text output). */
|
|
7
|
+
agentSummary?: string;
|
|
8
|
+
/** Path to the fork session's history file on disk, when the runtime persists it. */
|
|
9
|
+
historyPath?: string;
|
|
6
10
|
};
|
|
7
11
|
|
|
8
12
|
/** Mutable dispatch state for the orchestrator gateway. */
|