@parall/parel-channel 1.51.0 → 1.52.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/catchup.d.ts +79 -0
- package/dist/catchup.d.ts.map +1 -0
- package/dist/catchup.js +296 -0
- package/dist/connect.d.ts.map +1 -1
- package/dist/connect.js +4 -2
- package/dist/fork.d.ts +7 -0
- package/dist/fork.d.ts.map +1 -1
- package/dist/fork.js +14 -2
- package/dist/inbound-channel.d.ts.map +1 -1
- package/dist/inbound-channel.js +55 -38
- package/dist/inbound.d.ts +14 -13
- package/dist/inbound.d.ts.map +1 -1
- package/dist/inbound.js +142 -180
- package/dist/index.js +2 -2
- package/dist/session.d.ts +37 -9
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +63 -19
- package/dist/typed-dispatch.d.ts +12 -6
- package/dist/typed-dispatch.d.ts.map +1 -1
- package/dist/typed-dispatch.js +65 -53
- package/package.json +1 -1
- package/src/catchup.ts +337 -0
- package/src/connect.ts +4 -2
- package/src/fork.ts +17 -2
- package/src/inbound-channel.ts +59 -44
- package/src/inbound.ts +156 -192
- package/src/index.ts +2 -2
- package/src/session.ts +80 -19
- package/src/typed-dispatch.ts +63 -46
package/dist/session.d.ts
CHANGED
|
@@ -16,7 +16,16 @@ import type { ConnectorContext, ConnectorEffect } from '@parel/plugin-sdk';
|
|
|
16
16
|
* carry subject=chatId/chv_ — parel groups a turn's inputs by it (F2) and
|
|
17
17
|
* mirrors it on agent events, so steps and turn signals stay chat-addressed.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Single-fetch cap for every inline API call in hook bodies. 5s — the
|
|
21
|
+
* execution-model v2 hook budgets (onMessage/onTimer/deliver/onAgentEvent
|
|
22
|
+
* 60s, first timeout = epoch rotation) price the LONGEST legal serial chain,
|
|
23
|
+
* so per-fetch caps must keep any hook's worst case well inside 60s (target
|
|
24
|
+
* ≤30s, half the budget). Parall API latency is far below this; 10s was
|
|
25
|
+
* slow-path headroom stacked on worst cases, and it priced the 1.48.0 chains
|
|
26
|
+
* at ≈55-68s — over budget.
|
|
27
|
+
*/
|
|
28
|
+
export declare const REQUEST_TIMEOUT_MS = 5000;
|
|
20
29
|
/** The agent-global runtime session identity under `main` channel routing. */
|
|
21
30
|
export declare const MAIN_SESSION_KEY = "main";
|
|
22
31
|
/** Reporting needs the agent id in the endpoint path; old configs lack it. */
|
|
@@ -81,12 +90,15 @@ export declare function ensureSession(ctx: ConnectorContext, opts?: {
|
|
|
81
90
|
}): Promise<string | null>;
|
|
82
91
|
/**
|
|
83
92
|
* Resolve the CURRENT main ase_ from the server, bypassing the cache — the
|
|
84
|
-
*
|
|
93
|
+
* catch-up generation check needs the authoritative id to compare against the
|
|
85
94
|
* cached one (New Session rotates it). Updates the cache on success; returns
|
|
86
95
|
* null on failure WITHOUT touching the cache (the caller must not lose its
|
|
87
|
-
* only generation token to a transient error).
|
|
96
|
+
* only generation token to a transient error). The generation check runs
|
|
97
|
+
* inside a v1 5s onTimer budget, so it passes a tighter timeoutMs.
|
|
88
98
|
*/
|
|
89
|
-
export declare function fetchMainSessionFresh(ctx: ConnectorContext
|
|
99
|
+
export declare function fetchMainSessionFresh(ctx: ConnectorContext, opts?: {
|
|
100
|
+
timeoutMs?: number;
|
|
101
|
+
}): Promise<string | null>;
|
|
90
102
|
/**
|
|
91
103
|
* Get-or-create the parall AgentSession mirroring one fork child session
|
|
92
104
|
* (ase_ with parent_session_id → the main ase_, runtime_session_id = the
|
|
@@ -115,6 +127,7 @@ export declare function createInputStep(ctx: ConnectorContext, sessionId: string
|
|
|
115
127
|
messageId: string;
|
|
116
128
|
senderId?: string;
|
|
117
129
|
text: string;
|
|
130
|
+
threadRootId?: string;
|
|
118
131
|
}): Promise<ReportResult>;
|
|
119
132
|
/**
|
|
120
133
|
* Input step for a channel_message dispatch (external IM). Shaped exactly
|
|
@@ -254,10 +267,17 @@ export declare function claimDispatchSource(ctx: ConnectorContext, sourceType: s
|
|
|
254
267
|
* retired turnwatch timer). Must comfortably exceed any legitimate turn.
|
|
255
268
|
*/
|
|
256
269
|
export declare const EMIT_TTL_MS: number;
|
|
257
|
-
/**
|
|
270
|
+
/**
|
|
271
|
+
* The current emit attempt for a source: its envelope id + emit time, plus
|
|
272
|
+
* the session that owned the turn at emit time (attribution for the turn-end
|
|
273
|
+
* ledger close — bound per attempt so a close landing after a New Session
|
|
274
|
+
* rotation stamps the session that actually processed it, never the fresh
|
|
275
|
+
* post-reset one).
|
|
276
|
+
*/
|
|
258
277
|
export type ActiveEmit = {
|
|
259
278
|
id: string;
|
|
260
279
|
at: number;
|
|
280
|
+
sessionId?: string;
|
|
261
281
|
};
|
|
262
282
|
/**
|
|
263
283
|
* Per-source attempt fence. Written before emit, cleared by the matching
|
|
@@ -267,7 +287,7 @@ export type ActiveEmit = {
|
|
|
267
287
|
* close the in-flight retry's work (codex P2 attempt fence).
|
|
268
288
|
*/
|
|
269
289
|
export declare function readActiveEmit(ctx: ConnectorContext, sourceType: string, sourceId: string): Promise<ActiveEmit | null>;
|
|
270
|
-
export declare function writeActiveEmit(ctx: ConnectorContext, sourceType: string, sourceId: string, envelopeId: string): Promise<void>;
|
|
290
|
+
export declare function writeActiveEmit(ctx: ConnectorContext, sourceType: string, sourceId: string, envelopeId: string, sessionId?: string): Promise<void>;
|
|
271
291
|
export declare function clearActiveEmit(ctx: ConnectorContext, sourceType: string, sourceId: string): Promise<void>;
|
|
272
292
|
/**
|
|
273
293
|
* Burn the fence after an ERROR close: keep the attempt id (so the re-driven
|
|
@@ -314,13 +334,21 @@ export declare function completeFencedEnvelopes(ctx: ConnectorContext, envelopeI
|
|
|
314
334
|
* broad-cover to the turn's reply Effect when one exists; otherwise a
|
|
315
335
|
* no_action sweep on "ok", or a release back to pending on the redrive budget
|
|
316
336
|
* on "error" (a failed turn must not silently consume work it never handled).
|
|
317
|
-
*
|
|
318
|
-
*
|
|
337
|
+
*
|
|
338
|
+
* One immediate retry on a transient failure. This close never throws — it
|
|
339
|
+
* returns a bool the caller uses to decide whether to clear/burn the fence —
|
|
340
|
+
* so a failure is invisible to the v2 platform (no thrown hook error to
|
|
341
|
+
* replay). Whichever host we run on, the ONLY prompt recovery for a dropped
|
|
342
|
+
* close is this in-call retry; without it a single transient 5xx strands the
|
|
343
|
+
* fence live until the catch-up TTL re-emit (up to EMIT_TTL_MS = 30min),
|
|
344
|
+
* keeping the chat activity indicator lit that whole window. The retry is
|
|
345
|
+
* bounded (2 × REQUEST_TIMEOUT_MS) and the call is idempotent, so it stacks
|
|
346
|
+
* onto neither host's budget in the common (first-attempt-succeeds) case.
|
|
319
347
|
*/
|
|
320
348
|
export declare function completeSources(ctx: ConnectorContext, sources: {
|
|
321
349
|
source_type: string;
|
|
322
350
|
source_id: string;
|
|
323
|
-
}[], outcome?: TurnOutcome): Promise<boolean>;
|
|
351
|
+
}[], sessionId?: string, outcome?: TurnOutcome): Promise<boolean>;
|
|
324
352
|
/**
|
|
325
353
|
* Turn-end plain text as a suppressed audit step — the runtime-standard
|
|
326
354
|
* Layer 0 posture (agent-dm-loop-prevention.md): recorded on the session,
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG3E;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js
CHANGED
|
@@ -16,7 +16,16 @@ import { parallAgentId, parallApiUrl, parallOrgId, requireAgk } from './connect.
|
|
|
16
16
|
* carry subject=chatId/chv_ — parel groups a turn's inputs by it (F2) and
|
|
17
17
|
* mirrors it on agent events, so steps and turn signals stay chat-addressed.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Single-fetch cap for every inline API call in hook bodies. 5s — the
|
|
21
|
+
* execution-model v2 hook budgets (onMessage/onTimer/deliver/onAgentEvent
|
|
22
|
+
* 60s, first timeout = epoch rotation) price the LONGEST legal serial chain,
|
|
23
|
+
* so per-fetch caps must keep any hook's worst case well inside 60s (target
|
|
24
|
+
* ≤30s, half the budget). Parall API latency is far below this; 10s was
|
|
25
|
+
* slow-path headroom stacked on worst cases, and it priced the 1.48.0 chains
|
|
26
|
+
* at ≈55-68s — over budget.
|
|
27
|
+
*/
|
|
28
|
+
export const REQUEST_TIMEOUT_MS = 5_000;
|
|
20
29
|
/** The agent-global runtime session identity under `main` channel routing. */
|
|
21
30
|
export const MAIN_SESSION_KEY = 'main';
|
|
22
31
|
/** Reporting needs the agent id in the endpoint path; old configs lack it. */
|
|
@@ -136,12 +145,13 @@ export async function ensureSession(ctx, opts = {}) {
|
|
|
136
145
|
}
|
|
137
146
|
/**
|
|
138
147
|
* Resolve the CURRENT main ase_ from the server, bypassing the cache — the
|
|
139
|
-
*
|
|
148
|
+
* catch-up generation check needs the authoritative id to compare against the
|
|
140
149
|
* cached one (New Session rotates it). Updates the cache on success; returns
|
|
141
150
|
* null on failure WITHOUT touching the cache (the caller must not lose its
|
|
142
|
-
* only generation token to a transient error).
|
|
151
|
+
* only generation token to a transient error). The generation check runs
|
|
152
|
+
* inside a v1 5s onTimer budget, so it passes a tighter timeoutMs.
|
|
143
153
|
*/
|
|
144
|
-
export async function fetchMainSessionFresh(ctx) {
|
|
154
|
+
export async function fetchMainSessionFresh(ctx, opts = {}) {
|
|
145
155
|
if (!reportingEnabled(ctx))
|
|
146
156
|
return null;
|
|
147
157
|
try {
|
|
@@ -150,7 +160,7 @@ export async function fetchMainSessionFresh(ctx) {
|
|
|
150
160
|
method: 'POST',
|
|
151
161
|
headers: jsonHeaders(ctx),
|
|
152
162
|
body: JSON.stringify({ runtime_type: 'parel', runtime_session_id: MAIN_SESSION_KEY }),
|
|
153
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
163
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? REQUEST_TIMEOUT_MS),
|
|
154
164
|
});
|
|
155
165
|
if (!res.ok)
|
|
156
166
|
return null;
|
|
@@ -257,7 +267,12 @@ export async function createInputStep(ctx, sessionId, args) {
|
|
|
257
267
|
idempotency_key: `input:${args.messageId}`,
|
|
258
268
|
content: {
|
|
259
269
|
trigger_type: 'mention',
|
|
260
|
-
trigger_ref: {
|
|
270
|
+
trigger_ref: {
|
|
271
|
+
message_id: args.messageId,
|
|
272
|
+
// Thread context for the session→chat backlink (same contract as
|
|
273
|
+
// agent-core's input steps).
|
|
274
|
+
...(args.threadRootId ? { thread_root_id: args.threadRootId } : {}),
|
|
275
|
+
},
|
|
261
276
|
...(args.senderId ? { sender_id: args.senderId } : {}),
|
|
262
277
|
summary: args.text.slice(0, 200),
|
|
263
278
|
},
|
|
@@ -630,9 +645,13 @@ export async function readActiveEmit(ctx, sourceType, sourceId) {
|
|
|
630
645
|
}
|
|
631
646
|
return null;
|
|
632
647
|
}
|
|
633
|
-
export async function writeActiveEmit(ctx, sourceType, sourceId, envelopeId) {
|
|
648
|
+
export async function writeActiveEmit(ctx, sourceType, sourceId, envelopeId, sessionId) {
|
|
634
649
|
await ctx.store
|
|
635
|
-
?.set(activeEmitKey(sourceType, sourceId), {
|
|
650
|
+
?.set(activeEmitKey(sourceType, sourceId), {
|
|
651
|
+
id: envelopeId,
|
|
652
|
+
at: ctx.now(),
|
|
653
|
+
...(sessionId ? { sessionId } : {}),
|
|
654
|
+
})
|
|
636
655
|
.catch(() => { });
|
|
637
656
|
}
|
|
638
657
|
export async function clearActiveEmit(ctx, sourceType, sourceId) {
|
|
@@ -693,6 +712,7 @@ export function sourceFromEnvelopeId(envelopeId) {
|
|
|
693
712
|
*/
|
|
694
713
|
export async function completeFencedEnvelopes(ctx, envelopeIds, outcome = 'ok') {
|
|
695
714
|
const fenced = [];
|
|
715
|
+
const fenceSessions = new Set();
|
|
696
716
|
for (const envId of envelopeIds) {
|
|
697
717
|
const src = sourceFromEnvelopeId(envId);
|
|
698
718
|
if (!src)
|
|
@@ -705,15 +725,27 @@ export async function completeFencedEnvelopes(ctx, envelopeIds, outcome = 'ok')
|
|
|
705
725
|
// error close just released (the retry hole S3a closes).
|
|
706
726
|
if (active?.id === envId && active.at > 0) {
|
|
707
727
|
fenced.push({ source_type: src.sourceType, source_id: src.sourceId });
|
|
728
|
+
fenceSessions.add(active.sessionId);
|
|
708
729
|
}
|
|
709
730
|
}
|
|
710
|
-
if (fenced.length > 0
|
|
711
|
-
for
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
731
|
+
if (fenced.length > 0) {
|
|
732
|
+
// Session attribution for the swept no_action rows (message ↔ session
|
|
733
|
+
// linking), read off the attempt fences themselves — the session that
|
|
734
|
+
// owned each envelope AT EMIT TIME. Never the mutable main-session cache:
|
|
735
|
+
// a close landing after a New Session rotation must not stamp the fresh
|
|
736
|
+
// session. Session-less fences (reporting disabled / legacy rows) are
|
|
737
|
+
// neutral — only fences DISAGREEING on a session drop the attribution
|
|
738
|
+
// (honest NULL); best-effort either way — never blocks the ledger close.
|
|
739
|
+
const known = [...fenceSessions].filter((s) => Boolean(s));
|
|
740
|
+
const sessionId = new Set(known).size === 1 ? known[0] : undefined;
|
|
741
|
+
if (await completeSources(ctx, fenced, sessionId, outcome)) {
|
|
742
|
+
for (const src of fenced) {
|
|
743
|
+
if (outcome === 'error') {
|
|
744
|
+
await burnActiveEmit(ctx, src.source_type, src.source_id);
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
await clearActiveEmit(ctx, src.source_type, src.source_id);
|
|
748
|
+
}
|
|
717
749
|
}
|
|
718
750
|
}
|
|
719
751
|
}
|
|
@@ -724,10 +756,18 @@ export async function completeFencedEnvelopes(ctx, envelopeIds, outcome = 'ok')
|
|
|
724
756
|
* broad-cover to the turn's reply Effect when one exists; otherwise a
|
|
725
757
|
* no_action sweep on "ok", or a release back to pending on the redrive budget
|
|
726
758
|
* on "error" (a failed turn must not silently consume work it never handled).
|
|
727
|
-
*
|
|
728
|
-
*
|
|
759
|
+
*
|
|
760
|
+
* One immediate retry on a transient failure. This close never throws — it
|
|
761
|
+
* returns a bool the caller uses to decide whether to clear/burn the fence —
|
|
762
|
+
* so a failure is invisible to the v2 platform (no thrown hook error to
|
|
763
|
+
* replay). Whichever host we run on, the ONLY prompt recovery for a dropped
|
|
764
|
+
* close is this in-call retry; without it a single transient 5xx strands the
|
|
765
|
+
* fence live until the catch-up TTL re-emit (up to EMIT_TTL_MS = 30min),
|
|
766
|
+
* keeping the chat activity indicator lit that whole window. The retry is
|
|
767
|
+
* bounded (2 × REQUEST_TIMEOUT_MS) and the call is idempotent, so it stacks
|
|
768
|
+
* onto neither host's budget in the common (first-attempt-succeeds) case.
|
|
729
769
|
*/
|
|
730
|
-
export async function completeSources(ctx, sources, outcome = 'ok') {
|
|
770
|
+
export async function completeSources(ctx, sources, sessionId, outcome = 'ok') {
|
|
731
771
|
// Ledger surface — not gated on reportingEnabled (see claimDispatchSource).
|
|
732
772
|
if (sources.length === 0)
|
|
733
773
|
return true;
|
|
@@ -736,7 +776,11 @@ export async function completeSources(ctx, sources, outcome = 'ok') {
|
|
|
736
776
|
const res = await fetch(url, {
|
|
737
777
|
method: 'POST',
|
|
738
778
|
headers: jsonHeaders(ctx),
|
|
739
|
-
body: JSON.stringify({
|
|
779
|
+
body: JSON.stringify({
|
|
780
|
+
sources,
|
|
781
|
+
turn_outcome: outcome,
|
|
782
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
783
|
+
}),
|
|
740
784
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
741
785
|
});
|
|
742
786
|
if (!res.ok)
|
package/dist/typed-dispatch.d.ts
CHANGED
|
@@ -13,8 +13,12 @@ import type { ConnectorContext } from '@parel/plugin-sdk';
|
|
|
13
13
|
* Semantics carried over from the standard runtimes:
|
|
14
14
|
* - Permanently-unusable sources (404/403, deleted comments, no_reply hints,
|
|
15
15
|
* stale task assignment) resolve to `stale` — the caller acks and drops.
|
|
16
|
-
* - Transient fetch failures
|
|
17
|
-
*
|
|
16
|
+
* - Transient fetch failures THROW (the pipeline's transient-vs-deliberate
|
|
17
|
+
* contract, see inbound.ts): the dispatch stays pending, the live frame
|
|
18
|
+
* path catches at its boundary, and the catch-up job counts the row as
|
|
19
|
+
* starving and re-arms with backoff instead of ending around it.
|
|
20
|
+
* - Unknown FUTURE event types resolve to `pending` deliberately — real work
|
|
21
|
+
* a newer connector will handle; the catch-up job ends cleanly over them.
|
|
18
22
|
* - schedule.fire tolerates 404 by contract (post-fire PATCH on target_ids /
|
|
19
23
|
* attached_to_uri legitimately revokes access to a delivered run snapshot;
|
|
20
24
|
* docs/primitives/schedule.md § Snapshot 不变性) — warn + ack + drop.
|
|
@@ -69,14 +73,16 @@ export type TypedDispatchResult = TypedEventPlan
|
|
|
69
73
|
| {
|
|
70
74
|
outcome: 'stale';
|
|
71
75
|
}
|
|
72
|
-
/**
|
|
76
|
+
/** Deliberately pending (unknown future type) — left for a newer
|
|
77
|
+
* connector; transient failures THROW instead of returning this. */
|
|
73
78
|
| {
|
|
74
79
|
outcome: 'pending';
|
|
75
80
|
};
|
|
76
81
|
/**
|
|
77
|
-
* Build the plan for one typed dispatch.
|
|
78
|
-
*
|
|
79
|
-
*
|
|
82
|
+
* Build the plan for one typed dispatch. Throws on transient failures
|
|
83
|
+
* (including unexpected handler errors — a poisoned row surfaces as catch-up
|
|
84
|
+
* backoff rescans instead of a silently stuck pending count); returns
|
|
85
|
+
* `pending` only for deliberately-left work (unknown future event types).
|
|
80
86
|
*/
|
|
81
87
|
export declare function planTypedDispatch(data: TypedDispatchData, ctx: ConnectorContext): Promise<TypedDispatchResult>;
|
|
82
88
|
//# sourceMappingURL=typed-dispatch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed-dispatch.d.ts","sourceRoot":"","sources":["../src/typed-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAI1D
|
|
1
|
+
{"version":3,"file":"typed-dispatch.d.ts","sourceRoot":"","sources":["../src/typed-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAI1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,kIAQpB,CAAC;AAEX,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAC3B,cAAc;AAChB,2CAA2C;GACzC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE;AACtB;oEACoE;GAClE;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC;AAiB3B;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,iBAAiB,EACvB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,mBAAmB,CAAC,CAmC9B"}
|
package/dist/typed-dispatch.js
CHANGED
|
@@ -15,6 +15,10 @@ export function isTypedEventType(eventType) {
|
|
|
15
15
|
}
|
|
16
16
|
const STALE = { outcome: 'stale' };
|
|
17
17
|
const PENDING = { outcome: 'pending' };
|
|
18
|
+
/** Non-gone fetch failure on a source entity: transient by contract. */
|
|
19
|
+
function transientFetchError(what, status) {
|
|
20
|
+
return new Error(`transient ${what} fetch failure (${status})`);
|
|
21
|
+
}
|
|
18
22
|
function sanitizeMeta(value) {
|
|
19
23
|
return value
|
|
20
24
|
.replace(/[\r\n]+/g, ' ')
|
|
@@ -22,56 +26,51 @@ function sanitizeMeta(value) {
|
|
|
22
26
|
.trim();
|
|
23
27
|
}
|
|
24
28
|
/**
|
|
25
|
-
* Build the plan for one typed dispatch.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
29
|
+
* Build the plan for one typed dispatch. Throws on transient failures
|
|
30
|
+
* (including unexpected handler errors — a poisoned row surfaces as catch-up
|
|
31
|
+
* backoff rescans instead of a silently stuck pending count); returns
|
|
32
|
+
* `pending` only for deliberately-left work (unknown future event types).
|
|
28
33
|
*/
|
|
29
34
|
export async function planTypedDispatch(data, ctx) {
|
|
30
35
|
const eventType = String(data.event_type ?? '');
|
|
31
36
|
const sourceId = data.source_id ? String(data.source_id) : '';
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return await planTask(ctx, taskId, eventType, actorId(data));
|
|
40
|
-
}
|
|
41
|
-
case 'task_comment': {
|
|
42
|
-
const taskId = data.task_id ? String(data.task_id) : '';
|
|
43
|
-
if (!sourceId || !taskId)
|
|
44
|
-
return STALE;
|
|
45
|
-
return await planTaskComment(ctx, sourceId, taskId, actorId(data), deliveryReason(data));
|
|
46
|
-
}
|
|
47
|
-
case 'wiki_comment': {
|
|
48
|
-
if (!sourceId)
|
|
49
|
-
return STALE;
|
|
50
|
-
return await planWikiComment(ctx, sourceId, actorId(data), deliveryReason(data));
|
|
51
|
-
}
|
|
52
|
-
case 'schedule.fire': {
|
|
53
|
-
if (!sourceId)
|
|
54
|
-
return STALE;
|
|
55
|
-
return await planScheduleFire(ctx, sourceId);
|
|
56
|
-
}
|
|
57
|
-
case 'external_trigger': {
|
|
58
|
-
if (!sourceId)
|
|
59
|
-
return STALE;
|
|
60
|
-
return await planExternalTrigger(ctx, sourceId);
|
|
61
|
-
}
|
|
62
|
-
case 'approval_decided': {
|
|
63
|
-
if (!sourceId)
|
|
64
|
-
return STALE;
|
|
65
|
-
const chatId = data.chat_id ? String(data.chat_id) : undefined;
|
|
66
|
-
return await planApprovalDecided(ctx, sourceId, chatId, actorId(data));
|
|
67
|
-
}
|
|
68
|
-
default:
|
|
69
|
-
return PENDING; // unknown typed event — future work, never ack-drop
|
|
37
|
+
switch (eventType) {
|
|
38
|
+
case 'task_assign':
|
|
39
|
+
case 'task_update': {
|
|
40
|
+
const taskId = data.task_id ? String(data.task_id) : sourceId;
|
|
41
|
+
if (!taskId)
|
|
42
|
+
return STALE;
|
|
43
|
+
return await planTask(ctx, taskId, eventType, actorId(data));
|
|
70
44
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
45
|
+
case 'task_comment': {
|
|
46
|
+
const taskId = data.task_id ? String(data.task_id) : '';
|
|
47
|
+
if (!sourceId || !taskId)
|
|
48
|
+
return STALE;
|
|
49
|
+
return await planTaskComment(ctx, sourceId, taskId, actorId(data), deliveryReason(data));
|
|
50
|
+
}
|
|
51
|
+
case 'wiki_comment': {
|
|
52
|
+
if (!sourceId)
|
|
53
|
+
return STALE;
|
|
54
|
+
return await planWikiComment(ctx, sourceId, actorId(data), deliveryReason(data));
|
|
55
|
+
}
|
|
56
|
+
case 'schedule.fire': {
|
|
57
|
+
if (!sourceId)
|
|
58
|
+
return STALE;
|
|
59
|
+
return await planScheduleFire(ctx, sourceId);
|
|
60
|
+
}
|
|
61
|
+
case 'external_trigger': {
|
|
62
|
+
if (!sourceId)
|
|
63
|
+
return STALE;
|
|
64
|
+
return await planExternalTrigger(ctx, sourceId);
|
|
65
|
+
}
|
|
66
|
+
case 'approval_decided': {
|
|
67
|
+
if (!sourceId)
|
|
68
|
+
return STALE;
|
|
69
|
+
const chatId = data.chat_id ? String(data.chat_id) : undefined;
|
|
70
|
+
return await planApprovalDecided(ctx, sourceId, chatId, actorId(data));
|
|
71
|
+
}
|
|
72
|
+
default:
|
|
73
|
+
return PENDING; // unknown typed event — future work, never ack-drop
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
function actorId(data) {
|
|
@@ -92,8 +91,11 @@ function goneStatus(status) {
|
|
|
92
91
|
}
|
|
93
92
|
async function planTask(ctx, taskId, eventType, actor) {
|
|
94
93
|
const res = await orgGet(ctx, `/tasks/${taskId}`);
|
|
95
|
-
if (!res.ok)
|
|
96
|
-
|
|
94
|
+
if (!res.ok) {
|
|
95
|
+
if (goneStatus(res.status))
|
|
96
|
+
return STALE;
|
|
97
|
+
throw transientFetchError('task', res.status);
|
|
98
|
+
}
|
|
97
99
|
const task = (await res.json().catch(() => null));
|
|
98
100
|
if (!task?.id)
|
|
99
101
|
return STALE;
|
|
@@ -153,8 +155,11 @@ function taskActionHint(taskId) {
|
|
|
153
155
|
}
|
|
154
156
|
async function fetchComment(ctx, commentId) {
|
|
155
157
|
const res = await orgGet(ctx, `/comments/${commentId}`);
|
|
156
|
-
if (!res.ok)
|
|
157
|
-
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
if (goneStatus(res.status))
|
|
160
|
+
return STALE;
|
|
161
|
+
throw transientFetchError('comment', res.status);
|
|
162
|
+
}
|
|
158
163
|
const comment = (await res.json().catch(() => null));
|
|
159
164
|
if (!comment?.id)
|
|
160
165
|
return STALE;
|
|
@@ -243,7 +248,9 @@ async function planScheduleFire(ctx, runId) {
|
|
|
243
248
|
console.warn(`[parel-channel] schedule run ${runId} not accessible (404), dropping`);
|
|
244
249
|
return STALE;
|
|
245
250
|
}
|
|
246
|
-
|
|
251
|
+
if (goneStatus(res.status))
|
|
252
|
+
return STALE;
|
|
253
|
+
throw transientFetchError('schedule run', res.status);
|
|
247
254
|
}
|
|
248
255
|
const run = (await res.json().catch(() => null));
|
|
249
256
|
if (!run?.id || !run.schedule_id)
|
|
@@ -273,7 +280,9 @@ async function planExternalTrigger(ctx, runId) {
|
|
|
273
280
|
console.warn(`[parel-channel] external trigger run ${runId} not accessible (404), dropping`);
|
|
274
281
|
return STALE;
|
|
275
282
|
}
|
|
276
|
-
|
|
283
|
+
if (goneStatus(res.status))
|
|
284
|
+
return STALE;
|
|
285
|
+
throw transientFetchError('external trigger run', res.status);
|
|
277
286
|
}
|
|
278
287
|
const run = (await res.json().catch(() => null));
|
|
279
288
|
if (!run?.id || !run.trigger_id)
|
|
@@ -318,8 +327,11 @@ async function planApprovalDecided(ctx, approvalId, chatId, actor) {
|
|
|
318
327
|
headers: { Authorization: `Bearer ${requireAgk(ctx)}` },
|
|
319
328
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
320
329
|
});
|
|
321
|
-
if (!res.ok)
|
|
322
|
-
|
|
330
|
+
if (!res.ok) {
|
|
331
|
+
if (goneStatus(res.status))
|
|
332
|
+
return STALE;
|
|
333
|
+
throw transientFetchError('approval', res.status);
|
|
334
|
+
}
|
|
323
335
|
const approval = (await res.json().catch(() => null));
|
|
324
336
|
if (!approval?.id)
|
|
325
337
|
return STALE;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/parel-channel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.0",
|
|
4
4
|
"description": "Parall channel plugin for the parel runtime — lets a parel agent treat Parall as a managed_ws channel (receive dispatches, reply via the Parall API)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|