@parall/agent-core 1.26.0 → 1.26.2
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 +3 -0
- package/dist/dispatch-adapter.d.ts.map +1 -1
- package/dist/event-format.d.ts +1 -0
- package/dist/event-format.d.ts.map +1 -1
- package/dist/event-format.js +13 -0
- package/dist/gateway-base.d.ts +1 -0
- package/dist/gateway-base.d.ts.map +1 -1
- package/dist/gateway-base.js +81 -7
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/dispatch-adapter.ts +4 -0
- package/src/event-format.ts +13 -0
- package/src/gateway-base.ts +76 -5
- package/src/types.ts +3 -1
|
@@ -65,6 +65,7 @@ export type ForkSessionHandle = {
|
|
|
65
65
|
export type ForkOpts = {
|
|
66
66
|
sessionKey: string;
|
|
67
67
|
context: DispatchContext;
|
|
68
|
+
preDispatchBranchPoint?: string;
|
|
68
69
|
};
|
|
69
70
|
export type CleanupForkOpts = {
|
|
70
71
|
fork: ForkSessionHandle;
|
|
@@ -77,5 +78,7 @@ export interface DispatchAdapter {
|
|
|
77
78
|
forkSession?(opts: ForkOpts): Promise<ForkSessionHandle | null> | ForkSessionHandle | null;
|
|
78
79
|
/** Clean up a fork session created by forkSession(). */
|
|
79
80
|
cleanupFork?(opts: CleanupForkOpts): Promise<void> | void;
|
|
81
|
+
/** Return the current on-disk branch point (leaf ID) for the given session, if available. */
|
|
82
|
+
getBranchPoint?(sessionKey: string): string | undefined;
|
|
80
83
|
}
|
|
81
84
|
//# sourceMappingURL=dispatch-adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch-adapter.d.ts","sourceRoot":"","sources":["../src/dispatch-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9G;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACjI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatch-adapter.d.ts","sourceRoot":"","sources":["../src/dispatch-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/event-format.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ForkResult, ParallEvent } from "./types.js";
|
|
2
2
|
export declare function buildEventBody(event: ParallEvent): string;
|
|
3
|
+
export declare function buildForkScopePrefix(event: ParallEvent): string;
|
|
3
4
|
export declare function buildForkResultPrefix(results: ForkResult[]): string;
|
|
4
5
|
//# 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,
|
|
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,CASnE"}
|
package/dist/event-format.js
CHANGED
|
@@ -37,6 +37,13 @@ export function buildEventBody(event) {
|
|
|
37
37
|
lines.push(`[Comment ID: prll://${event.messageId}]`);
|
|
38
38
|
lines.push("", event.body);
|
|
39
39
|
}
|
|
40
|
+
else if (event.type === "approval") {
|
|
41
|
+
lines.push(`[Event: approval.decided]`);
|
|
42
|
+
lines.push(`[Approval: prll://${event.messageId}]`);
|
|
43
|
+
lines.push(`[Chat: prll://${event.targetId}]`);
|
|
44
|
+
lines.push(`[Decided by: ${event.senderName} (prll://${event.senderId})]`);
|
|
45
|
+
lines.push("", event.body);
|
|
46
|
+
}
|
|
40
47
|
else if (event.type === "schedule") {
|
|
41
48
|
lines.push(`[Event: schedule.fired]`);
|
|
42
49
|
lines.push(`[Schedule: prll://${event.targetId}]`);
|
|
@@ -58,6 +65,12 @@ export function buildEventBody(event) {
|
|
|
58
65
|
}
|
|
59
66
|
return lines.join("\n");
|
|
60
67
|
}
|
|
68
|
+
export function buildForkScopePrefix(event) {
|
|
69
|
+
const targetLabel = event.targetName
|
|
70
|
+
? `"${event.targetName}" (prll://${event.targetId})`
|
|
71
|
+
: `prll://${event.targetId}`;
|
|
72
|
+
return `[Fork scope: You are a parallel worker handling ${targetLabel} only. The conversation history above is inherited context — do NOT complete unfinished work, retry failed tool calls, or send messages to other chats from that history. Only act on the event below.]\n\n`;
|
|
73
|
+
}
|
|
61
74
|
export function buildForkResultPrefix(results) {
|
|
62
75
|
if (!results.length)
|
|
63
76
|
return "";
|
package/dist/gateway-base.d.ts
CHANGED
|
@@ -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
|
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { MENTION_ALL_USER_ID } from "@parall/sdk";
|
|
6
|
-
import { buildEventBody, buildForkResultPrefix } from "./event-format.js";
|
|
6
|
+
import { buildEventBody, buildForkResultPrefix, buildForkScopePrefix } from "./event-format.js";
|
|
7
7
|
import { routeTrigger } from "./routing.js";
|
|
8
8
|
import { clearDispatchMessageId, clearDispatchNoReply, clearSessionMessageId, setDispatchMessageId, setDispatchNoReply, setSessionChatId, setSessionMessageId, } from "./session-state.js";
|
|
9
9
|
// Parse PRLL_SHUTDOWN_DEADLINE_MS (or any string env value) into a positive
|
|
@@ -192,6 +192,19 @@ export class ParallAgentGateway {
|
|
|
192
192
|
this.opts.log?.error(`parall[${this.opts.accountId}]: schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
+
else if (data.event_type === "approval_decided") {
|
|
196
|
+
if (!data.source_id)
|
|
197
|
+
return;
|
|
198
|
+
try {
|
|
199
|
+
const dispatched = await this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null);
|
|
200
|
+
if (dispatched) {
|
|
201
|
+
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => { });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
this.opts.log?.error(`parall[${this.opts.accountId}]: approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
195
208
|
else if (data.event_type !== "message" && data.event_type !== "task_assign") {
|
|
196
209
|
// Truly unknown event_type — log so a newly-added dispatch type
|
|
197
210
|
// not yet wired here surfaces during runtime testing. "message"
|
|
@@ -262,7 +275,7 @@ export class ParallAgentGateway {
|
|
|
262
275
|
runtimeType: this.opts.runtimeType,
|
|
263
276
|
runtimeKey: this.opts.runtimeKey,
|
|
264
277
|
sessionId: this.activeSessionId,
|
|
265
|
-
chatId: event.type === "message" ? event.targetId : undefined,
|
|
278
|
+
chatId: (event.type === "message" || event.type === "approval") ? event.targetId : undefined,
|
|
266
279
|
triggerMessageId: event.messageId,
|
|
267
280
|
noReply: event.noReply ?? false,
|
|
268
281
|
contextFilePath: this.opts.contextFilePathForSession?.(sessionKey),
|
|
@@ -284,11 +297,13 @@ export class ParallAgentGateway {
|
|
|
284
297
|
trigger_type: event.type === "task" ? "task_assign" :
|
|
285
298
|
event.type === "task_comment" ? "task_comment" :
|
|
286
299
|
event.type === "schedule" ? "schedule_fire" :
|
|
287
|
-
"
|
|
300
|
+
event.type === "approval" ? "approval_decided" :
|
|
301
|
+
"mention",
|
|
288
302
|
trigger_ref: event.type === "task" ? { task_id: event.targetId } :
|
|
289
303
|
event.type === "task_comment" ? { comment_id: event.messageId, task_id: event.targetId } :
|
|
290
304
|
event.type === "schedule" ? { schedule_id: event.targetId, run_id: event.messageId } :
|
|
291
|
-
{
|
|
305
|
+
event.type === "approval" ? { approval_id: event.messageId } :
|
|
306
|
+
{ message_id: event.messageId },
|
|
292
307
|
sender_id: event.senderId,
|
|
293
308
|
sender_name: event.senderName,
|
|
294
309
|
summary: event.body.substring(0, 200),
|
|
@@ -543,7 +558,7 @@ export class ParallAgentGateway {
|
|
|
543
558
|
const last = events[events.length - 1];
|
|
544
559
|
const earlier = events.slice(0, -1);
|
|
545
560
|
try {
|
|
546
|
-
const dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildEventBody(last), earlier);
|
|
561
|
+
const dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier);
|
|
547
562
|
if (!dispatched) {
|
|
548
563
|
// Shutdown short-circuit — resolve un-acked so the server requeues
|
|
549
564
|
// for the replacement pod and stop draining further items.
|
|
@@ -634,6 +649,8 @@ export class ParallAgentGateway {
|
|
|
634
649
|
body: "[Orchestrator: fork session(s) completed — review results above]",
|
|
635
650
|
};
|
|
636
651
|
this.dispatchState.mainCurrentTargetId = undefined;
|
|
652
|
+
this.dispatchState.mainPreDispatchBranchPoint =
|
|
653
|
+
this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
637
654
|
const dispatched = await this.runDispatch(syntheticEvent, this.opts.runtimeKey, forkPrefix + buildEventBody(syntheticEvent));
|
|
638
655
|
if (!dispatched) {
|
|
639
656
|
// Shutdown: put the fork results back at the head so the synthetic
|
|
@@ -654,8 +671,8 @@ export class ParallAgentGateway {
|
|
|
654
671
|
const pendingFork = this.dispatchState.pendingForkResults.splice(0);
|
|
655
672
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
656
673
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
657
|
-
|
|
658
|
-
|
|
674
|
+
this.dispatchState.mainPreDispatchBranchPoint =
|
|
675
|
+
this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
659
676
|
const dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event), earlier);
|
|
660
677
|
if (!dispatched) {
|
|
661
678
|
// Shutdown: skip the ack so the server redelivers these buffered
|
|
@@ -679,6 +696,7 @@ export class ParallAgentGateway {
|
|
|
679
696
|
this.draining = false;
|
|
680
697
|
this.dispatchState.mainDispatching = false;
|
|
681
698
|
this.dispatchState.mainCurrentTargetId = undefined;
|
|
699
|
+
this.dispatchState.mainPreDispatchBranchPoint = undefined;
|
|
682
700
|
}
|
|
683
701
|
}
|
|
684
702
|
async handleInboundEvent(event) {
|
|
@@ -689,6 +707,11 @@ export class ParallAgentGateway {
|
|
|
689
707
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
690
708
|
this.dispatchState.mainDispatching = true;
|
|
691
709
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
710
|
+
// Snapshot the on-disk branch point BEFORE runDispatch starts writing
|
|
711
|
+
// to the session file. Fork sessions created while main is in-flight
|
|
712
|
+
// use this to branch from the clean pre-dispatch state.
|
|
713
|
+
this.dispatchState.mainPreDispatchBranchPoint =
|
|
714
|
+
this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
692
715
|
let dispatched = false;
|
|
693
716
|
try {
|
|
694
717
|
dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
|
|
@@ -728,6 +751,7 @@ export class ParallAgentGateway {
|
|
|
728
751
|
const fork = await this.opts.dispatchAdapter.forkSession({
|
|
729
752
|
sessionKey: this.opts.runtimeKey,
|
|
730
753
|
context: this.buildDispatchContext(event, this.opts.runtimeKey),
|
|
754
|
+
preDispatchBranchPoint: this.dispatchState.mainPreDispatchBranchPoint,
|
|
731
755
|
});
|
|
732
756
|
if (!fork) {
|
|
733
757
|
this.opts.log?.warn(`parall[${this.opts.accountId}]: fork failed, buffering event for main session`);
|
|
@@ -1050,6 +1074,53 @@ export class ParallAgentGateway {
|
|
|
1050
1074
|
}
|
|
1051
1075
|
return dispatched;
|
|
1052
1076
|
}
|
|
1077
|
+
async fetchAndHandleApprovalDecided(approvalId, actorId, chatId) {
|
|
1078
|
+
let approval = null;
|
|
1079
|
+
try {
|
|
1080
|
+
approval = await this.opts.client.getApproval(approvalId);
|
|
1081
|
+
}
|
|
1082
|
+
catch (err) {
|
|
1083
|
+
const status = err?.status;
|
|
1084
|
+
if (status === 404 || status === 403) {
|
|
1085
|
+
this.opts.log?.warn(`parall[${this.opts.accountId}]: approval ${approvalId} not accessible (${status}), acking stale dispatch`);
|
|
1086
|
+
return true;
|
|
1087
|
+
}
|
|
1088
|
+
this.opts.log?.warn(`parall[${this.opts.accountId}]: approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
|
|
1089
|
+
return false;
|
|
1090
|
+
}
|
|
1091
|
+
if (!approval)
|
|
1092
|
+
return true;
|
|
1093
|
+
if (this.shuttingDown)
|
|
1094
|
+
return false;
|
|
1095
|
+
const dedupeKey = `approval:${approval.id}`;
|
|
1096
|
+
if (this.dispatchedTasks.has(dedupeKey))
|
|
1097
|
+
return false;
|
|
1098
|
+
this.dispatchedTasks.add(dedupeKey);
|
|
1099
|
+
this.opts.log?.info(`parall[${this.opts.accountId}]: approval decided: ${approval.id} (${approval.status})`);
|
|
1100
|
+
const statusLabel = approval.status === "approved" ? "Approved" : "Rejected";
|
|
1101
|
+
const execInfo = approval.execution_status ? ` | execution: ${approval.execution_status}` : "";
|
|
1102
|
+
const body = `${statusLabel}: ${approval.title}${execInfo}`;
|
|
1103
|
+
const event = {
|
|
1104
|
+
type: "approval",
|
|
1105
|
+
targetId: chatId ?? approval.chat_id,
|
|
1106
|
+
senderId: actorId ?? approval.decided_by ?? "system",
|
|
1107
|
+
senderName: "approver",
|
|
1108
|
+
messageId: approval.id,
|
|
1109
|
+
body,
|
|
1110
|
+
};
|
|
1111
|
+
let dispatched;
|
|
1112
|
+
try {
|
|
1113
|
+
dispatched = await this.handleInboundEvent(event);
|
|
1114
|
+
}
|
|
1115
|
+
catch (err) {
|
|
1116
|
+
this.dispatchedTasks.delete(dedupeKey);
|
|
1117
|
+
throw err;
|
|
1118
|
+
}
|
|
1119
|
+
if (!dispatched) {
|
|
1120
|
+
this.dispatchedTasks.delete(dedupeKey);
|
|
1121
|
+
}
|
|
1122
|
+
return dispatched;
|
|
1123
|
+
}
|
|
1053
1124
|
async catchUpFromDispatch(coldStart = false) {
|
|
1054
1125
|
const minAge = coldStart ? Date.now() - this.COLD_START_WINDOW_MS : 0;
|
|
1055
1126
|
let cursor;
|
|
@@ -1097,6 +1168,9 @@ export class ParallAgentGateway {
|
|
|
1097
1168
|
else if (item.event_type === "schedule.fire" && item.source_id) {
|
|
1098
1169
|
dispatched = await this.fetchAndHandleScheduleFire(item.source_id, item.actor_id);
|
|
1099
1170
|
}
|
|
1171
|
+
else if (item.event_type === "approval_decided" && item.source_id) {
|
|
1172
|
+
dispatched = await this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null);
|
|
1173
|
+
}
|
|
1100
1174
|
else if (item.event_type === "message" && item.source_id && item.chat_id) {
|
|
1101
1175
|
if (!this.tryClaimMessage(item.source_id))
|
|
1102
1176
|
continue;
|
package/dist/types.d.ts
CHANGED
|
@@ -15,10 +15,12 @@ export type DispatchState = {
|
|
|
15
15
|
activeForks: Map<string, string>;
|
|
16
16
|
pendingForkResults: ForkResult[];
|
|
17
17
|
mainBuffer: ParallEvent[];
|
|
18
|
+
/** Snapshot of the session branch point taken BEFORE runDispatch starts on main. */
|
|
19
|
+
mainPreDispatchBranchPoint?: string;
|
|
18
20
|
};
|
|
19
21
|
/** Normalized inbound event from Parall. */
|
|
20
22
|
export type ParallEvent = {
|
|
21
|
-
type: "message" | "task" | "task_comment" | "schedule";
|
|
23
|
+
type: "message" | "task" | "task_comment" | "schedule" | "approval";
|
|
22
24
|
targetId: string;
|
|
23
25
|
targetName?: string;
|
|
24
26
|
targetType?: string;
|
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;CACnB,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;
|
|
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;CACnB,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.26.
|
|
3
|
+
"version": "1.26.2",
|
|
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.26.
|
|
29
|
+
"@parall/sdk": "1.26.2"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.0.0",
|
package/src/dispatch-adapter.ts
CHANGED
|
@@ -49,6 +49,7 @@ export type ForkSessionHandle = {
|
|
|
49
49
|
export type ForkOpts = {
|
|
50
50
|
sessionKey: string;
|
|
51
51
|
context: DispatchContext;
|
|
52
|
+
preDispatchBranchPoint?: string;
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
export type CleanupForkOpts = {
|
|
@@ -65,4 +66,7 @@ export interface DispatchAdapter {
|
|
|
65
66
|
|
|
66
67
|
/** Clean up a fork session created by forkSession(). */
|
|
67
68
|
cleanupFork?(opts: CleanupForkOpts): Promise<void> | void;
|
|
69
|
+
|
|
70
|
+
/** Return the current on-disk branch point (leaf ID) for the given session, if available. */
|
|
71
|
+
getBranchPoint?(sessionKey: string): string | undefined;
|
|
68
72
|
}
|
package/src/event-format.ts
CHANGED
|
@@ -35,6 +35,12 @@ export function buildEventBody(event: ParallEvent): string {
|
|
|
35
35
|
lines.push(`[From: ${event.senderName} (prll://${event.senderId})]`);
|
|
36
36
|
lines.push(`[Comment ID: prll://${event.messageId}]`);
|
|
37
37
|
lines.push("", event.body);
|
|
38
|
+
} else if (event.type === "approval") {
|
|
39
|
+
lines.push(`[Event: approval.decided]`);
|
|
40
|
+
lines.push(`[Approval: prll://${event.messageId}]`);
|
|
41
|
+
lines.push(`[Chat: prll://${event.targetId}]`);
|
|
42
|
+
lines.push(`[Decided by: ${event.senderName} (prll://${event.senderId})]`);
|
|
43
|
+
lines.push("", event.body);
|
|
38
44
|
} else if (event.type === "schedule") {
|
|
39
45
|
lines.push(`[Event: schedule.fired]`);
|
|
40
46
|
lines.push(`[Schedule: prll://${event.targetId}]`);
|
|
@@ -54,6 +60,13 @@ export function buildEventBody(event: ParallEvent): string {
|
|
|
54
60
|
return lines.join("\n");
|
|
55
61
|
}
|
|
56
62
|
|
|
63
|
+
export function buildForkScopePrefix(event: ParallEvent): string {
|
|
64
|
+
const targetLabel = event.targetName
|
|
65
|
+
? `"${event.targetName}" (prll://${event.targetId})`
|
|
66
|
+
: `prll://${event.targetId}`;
|
|
67
|
+
return `[Fork scope: You are a parallel worker handling ${targetLabel} only. The conversation history above is inherited context — do NOT complete unfinished work, retry failed tool calls, or send messages to other chats from that history. Only act on the event below.]\n\n`;
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
export function buildForkResultPrefix(results: ForkResult[]): string {
|
|
58
71
|
if (!results.length) return "";
|
|
59
72
|
const blocks = results.map((result) => {
|
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
|
+
Approval,
|
|
8
9
|
Chat,
|
|
9
10
|
ChatUpdateData,
|
|
10
11
|
Comment,
|
|
@@ -17,7 +18,7 @@ import type {
|
|
|
17
18
|
TaskAssignedData,
|
|
18
19
|
TextContent,
|
|
19
20
|
} from "@parall/sdk";
|
|
20
|
-
import { buildEventBody, buildForkResultPrefix } from "./event-format.js";
|
|
21
|
+
import { buildEventBody, buildForkResultPrefix, buildForkScopePrefix } from "./event-format.js";
|
|
21
22
|
import type {
|
|
22
23
|
CleanupForkOpts,
|
|
23
24
|
DispatchAdapter,
|
|
@@ -294,6 +295,16 @@ export class ParallAgentGateway {
|
|
|
294
295
|
} catch (err) {
|
|
295
296
|
this.opts.log?.error(`parall[${this.opts.accountId}]: schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
296
297
|
}
|
|
298
|
+
} else if (data.event_type === "approval_decided") {
|
|
299
|
+
if (!data.source_id) return;
|
|
300
|
+
try {
|
|
301
|
+
const dispatched = await this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null);
|
|
302
|
+
if (dispatched) {
|
|
303
|
+
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
|
|
304
|
+
}
|
|
305
|
+
} catch (err) {
|
|
306
|
+
this.opts.log?.error(`parall[${this.opts.accountId}]: approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
307
|
+
}
|
|
297
308
|
} else if (data.event_type !== "message" && data.event_type !== "task_assign") {
|
|
298
309
|
// Truly unknown event_type — log so a newly-added dispatch type
|
|
299
310
|
// not yet wired here surfaces during runtime testing. "message"
|
|
@@ -365,7 +376,7 @@ export class ParallAgentGateway {
|
|
|
365
376
|
runtimeType: this.opts.runtimeType,
|
|
366
377
|
runtimeKey: this.opts.runtimeKey,
|
|
367
378
|
sessionId: this.activeSessionId,
|
|
368
|
-
chatId: event.type === "message" ? event.targetId : undefined,
|
|
379
|
+
chatId: (event.type === "message" || event.type === "approval") ? event.targetId : undefined,
|
|
369
380
|
triggerMessageId: event.messageId,
|
|
370
381
|
noReply: event.noReply ?? false,
|
|
371
382
|
contextFilePath: this.opts.contextFilePathForSession?.(sessionKey),
|
|
@@ -388,11 +399,13 @@ export class ParallAgentGateway {
|
|
|
388
399
|
event.type === "task" ? "task_assign" :
|
|
389
400
|
event.type === "task_comment" ? "task_comment" :
|
|
390
401
|
event.type === "schedule" ? "schedule_fire" :
|
|
402
|
+
event.type === "approval" ? "approval_decided" :
|
|
391
403
|
"mention",
|
|
392
404
|
trigger_ref:
|
|
393
405
|
event.type === "task" ? { task_id: event.targetId } :
|
|
394
406
|
event.type === "task_comment" ? { comment_id: event.messageId, task_id: event.targetId } :
|
|
395
407
|
event.type === "schedule" ? { schedule_id: event.targetId, run_id: event.messageId } :
|
|
408
|
+
event.type === "approval" ? { approval_id: event.messageId } :
|
|
396
409
|
{ message_id: event.messageId },
|
|
397
410
|
sender_id: event.senderId,
|
|
398
411
|
sender_name: event.senderName,
|
|
@@ -658,7 +671,7 @@ export class ParallAgentGateway {
|
|
|
658
671
|
const last = events[events.length - 1];
|
|
659
672
|
const earlier = events.slice(0, -1);
|
|
660
673
|
try {
|
|
661
|
-
const dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildEventBody(last), earlier);
|
|
674
|
+
const dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier);
|
|
662
675
|
if (!dispatched) {
|
|
663
676
|
// Shutdown short-circuit — resolve un-acked so the server requeues
|
|
664
677
|
// for the replacement pod and stop draining further items.
|
|
@@ -750,6 +763,8 @@ export class ParallAgentGateway {
|
|
|
750
763
|
body: "[Orchestrator: fork session(s) completed — review results above]",
|
|
751
764
|
};
|
|
752
765
|
this.dispatchState.mainCurrentTargetId = undefined;
|
|
766
|
+
this.dispatchState.mainPreDispatchBranchPoint =
|
|
767
|
+
this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
753
768
|
const dispatched = await this.runDispatch(syntheticEvent, this.opts.runtimeKey, forkPrefix + buildEventBody(syntheticEvent));
|
|
754
769
|
if (!dispatched) {
|
|
755
770
|
// Shutdown: put the fork results back at the head so the synthetic
|
|
@@ -772,8 +787,8 @@ export class ParallAgentGateway {
|
|
|
772
787
|
const pendingFork = this.dispatchState.pendingForkResults.splice(0);
|
|
773
788
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
774
789
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
775
|
-
|
|
776
|
-
|
|
790
|
+
this.dispatchState.mainPreDispatchBranchPoint =
|
|
791
|
+
this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
777
792
|
const dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event), earlier);
|
|
778
793
|
if (!dispatched) {
|
|
779
794
|
// Shutdown: skip the ack so the server redelivers these buffered
|
|
@@ -796,6 +811,7 @@ export class ParallAgentGateway {
|
|
|
796
811
|
this.draining = false;
|
|
797
812
|
this.dispatchState.mainDispatching = false;
|
|
798
813
|
this.dispatchState.mainCurrentTargetId = undefined;
|
|
814
|
+
this.dispatchState.mainPreDispatchBranchPoint = undefined;
|
|
799
815
|
}
|
|
800
816
|
}
|
|
801
817
|
|
|
@@ -808,6 +824,11 @@ export class ParallAgentGateway {
|
|
|
808
824
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
809
825
|
this.dispatchState.mainDispatching = true;
|
|
810
826
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
827
|
+
// Snapshot the on-disk branch point BEFORE runDispatch starts writing
|
|
828
|
+
// to the session file. Fork sessions created while main is in-flight
|
|
829
|
+
// use this to branch from the clean pre-dispatch state.
|
|
830
|
+
this.dispatchState.mainPreDispatchBranchPoint =
|
|
831
|
+
this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
811
832
|
let dispatched = false;
|
|
812
833
|
try {
|
|
813
834
|
dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
|
|
@@ -850,6 +871,7 @@ export class ParallAgentGateway {
|
|
|
850
871
|
const fork = await this.opts.dispatchAdapter.forkSession({
|
|
851
872
|
sessionKey: this.opts.runtimeKey,
|
|
852
873
|
context: this.buildDispatchContext(event, this.opts.runtimeKey),
|
|
874
|
+
preDispatchBranchPoint: this.dispatchState.mainPreDispatchBranchPoint,
|
|
853
875
|
});
|
|
854
876
|
|
|
855
877
|
if (!fork) {
|
|
@@ -1188,6 +1210,53 @@ export class ParallAgentGateway {
|
|
|
1188
1210
|
return dispatched;
|
|
1189
1211
|
}
|
|
1190
1212
|
|
|
1213
|
+
private async fetchAndHandleApprovalDecided(approvalId: string, actorId: string | null, chatId: string | null): Promise<boolean> {
|
|
1214
|
+
let approval: Approval | null = null;
|
|
1215
|
+
try {
|
|
1216
|
+
approval = await this.opts.client.getApproval(approvalId);
|
|
1217
|
+
} catch (err: unknown) {
|
|
1218
|
+
const status = (err as { status?: number })?.status;
|
|
1219
|
+
if (status === 404 || status === 403) {
|
|
1220
|
+
this.opts.log?.warn(`parall[${this.opts.accountId}]: approval ${approvalId} not accessible (${status}), acking stale dispatch`);
|
|
1221
|
+
return true;
|
|
1222
|
+
}
|
|
1223
|
+
this.opts.log?.warn(`parall[${this.opts.accountId}]: approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
|
|
1224
|
+
return false;
|
|
1225
|
+
}
|
|
1226
|
+
if (!approval) return true;
|
|
1227
|
+
|
|
1228
|
+
if (this.shuttingDown) return false;
|
|
1229
|
+
const dedupeKey = `approval:${approval.id}`;
|
|
1230
|
+
if (this.dispatchedTasks.has(dedupeKey)) return false;
|
|
1231
|
+
this.dispatchedTasks.add(dedupeKey);
|
|
1232
|
+
this.opts.log?.info(`parall[${this.opts.accountId}]: approval decided: ${approval.id} (${approval.status})`);
|
|
1233
|
+
|
|
1234
|
+
const statusLabel = approval.status === "approved" ? "Approved" : "Rejected";
|
|
1235
|
+
const execInfo = approval.execution_status ? ` | execution: ${approval.execution_status}` : "";
|
|
1236
|
+
const body = `${statusLabel}: ${approval.title}${execInfo}`;
|
|
1237
|
+
|
|
1238
|
+
const event: ParallEvent = {
|
|
1239
|
+
type: "approval",
|
|
1240
|
+
targetId: chatId ?? approval.chat_id,
|
|
1241
|
+
senderId: actorId ?? approval.decided_by ?? "system",
|
|
1242
|
+
senderName: "approver",
|
|
1243
|
+
messageId: approval.id,
|
|
1244
|
+
body,
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
let dispatched: boolean;
|
|
1248
|
+
try {
|
|
1249
|
+
dispatched = await this.handleInboundEvent(event);
|
|
1250
|
+
} catch (err) {
|
|
1251
|
+
this.dispatchedTasks.delete(dedupeKey);
|
|
1252
|
+
throw err;
|
|
1253
|
+
}
|
|
1254
|
+
if (!dispatched) {
|
|
1255
|
+
this.dispatchedTasks.delete(dedupeKey);
|
|
1256
|
+
}
|
|
1257
|
+
return dispatched;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1191
1260
|
private async catchUpFromDispatch(coldStart = false) {
|
|
1192
1261
|
const minAge = coldStart ? Date.now() - this.COLD_START_WINDOW_MS : 0;
|
|
1193
1262
|
let cursor: string | undefined;
|
|
@@ -1231,6 +1300,8 @@ export class ParallAgentGateway {
|
|
|
1231
1300
|
dispatched = await this.handleTaskComment(item.source_id, item.task_id, item.actor_id, item.delivery_reason);
|
|
1232
1301
|
} else if (item.event_type === "schedule.fire" && item.source_id) {
|
|
1233
1302
|
dispatched = await this.fetchAndHandleScheduleFire(item.source_id, item.actor_id);
|
|
1303
|
+
} else if (item.event_type === "approval_decided" && item.source_id) {
|
|
1304
|
+
dispatched = await this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null);
|
|
1234
1305
|
} else if (item.event_type === "message" && item.source_id && item.chat_id) {
|
|
1235
1306
|
if (!this.tryClaimMessage(item.source_id)) continue;
|
|
1236
1307
|
let msg: Awaited<ReturnType<typeof this.opts.client.getMessage>> | null = null;
|
package/src/types.ts
CHANGED
|
@@ -12,11 +12,13 @@ export type DispatchState = {
|
|
|
12
12
|
activeForks: Map<string, string>;
|
|
13
13
|
pendingForkResults: ForkResult[];
|
|
14
14
|
mainBuffer: ParallEvent[];
|
|
15
|
+
/** Snapshot of the session branch point taken BEFORE runDispatch starts on main. */
|
|
16
|
+
mainPreDispatchBranchPoint?: string;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
/** Normalized inbound event from Parall. */
|
|
18
20
|
export type ParallEvent = {
|
|
19
|
-
type: "message" | "task" | "task_comment" | "schedule";
|
|
21
|
+
type: "message" | "task" | "task_comment" | "schedule" | "approval";
|
|
20
22
|
targetId: string;
|
|
21
23
|
targetName?: string;
|
|
22
24
|
targetType?: string;
|