@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/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
- export declare const REQUEST_TIMEOUT_MS = 10000;
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
- * onOpen generation check needs the authoritative id to compare against the
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): Promise<string | null>;
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
- /** The current emit attempt for a source: its envelope id + emit time. */
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
- * One immediate retry on failure; a still-failed call leaves the rows live
318
- * and the catch-up TTL re-emit self-heals.
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,
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
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
- export const REQUEST_TIMEOUT_MS = 10_000;
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
- * onOpen generation check needs the authoritative id to compare against the
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: { message_id: args.messageId },
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), { id: envelopeId, at: ctx.now() })
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 && (await completeSources(ctx, fenced, outcome))) {
711
- for (const src of fenced) {
712
- if (outcome === 'error') {
713
- await burnActiveEmit(ctx, src.source_type, src.source_id);
714
- }
715
- else {
716
- await clearActiveEmit(ctx, src.source_type, src.source_id);
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
- * One immediate retry on failure; a still-failed call leaves the rows live
728
- * and the catch-up TTL re-emit self-heals.
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({ sources, turn_outcome: outcome }),
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)
@@ -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 resolve to `pending` — the dispatch stays
17
- * pending and the reconnect catch-up sweep retries it.
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
- /** Transient failure — leave pending for the catch-up sweep. */
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. Never throws: any unexpected error
78
- * degrades to `pending` (same posture as the message path — the sweep
79
- * retries, and a poisoned row eventually surfaces as a stuck pending count).
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;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,gEAAgE;GAC9D;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC;AAY3B;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,iBAAiB,EACvB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,mBAAmB,CAAC,CAwC9B"}
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"}
@@ -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. Never throws: any unexpected error
26
- * degrades to `pending` (same posture as the message path — the sweep
27
- * retries, and a poisoned row eventually surfaces as a stuck pending count).
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
- try {
33
- switch (eventType) {
34
- case 'task_assign':
35
- case 'task_update': {
36
- const taskId = data.task_id ? String(data.task_id) : sourceId;
37
- if (!taskId)
38
- return STALE;
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
- catch (err) {
73
- console.error(`[parel-channel] typed dispatch ${eventType} failed`, err);
74
- return PENDING;
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
- return goneStatus(res.status) ? STALE : PENDING;
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
- return goneStatus(res.status) ? STALE : PENDING;
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
- return goneStatus(res.status) ? STALE : PENDING;
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
- return goneStatus(res.status) ? STALE : PENDING;
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
- return goneStatus(res.status) ? STALE : PENDING;
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.51.0",
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": {