@opentag/dispatcher 0.3.2 → 0.3.4
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/callbacks.d.ts +8 -0
- package/dist/callbacks.d.ts.map +1 -1
- package/dist/index.js +220 -1
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +11 -0
- package/dist/server.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/callbacks.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export declare function createSlackSourceReceiptSink(input: {
|
|
|
17
17
|
reactionsAddUri?: string;
|
|
18
18
|
timeoutMs?: number;
|
|
19
19
|
}): SourceReceiptSink;
|
|
20
|
+
export declare function createLarkSourceReceiptSink(input: {
|
|
21
|
+
appId?: string;
|
|
22
|
+
appSecret?: string;
|
|
23
|
+
domain?: "lark" | "feishu";
|
|
24
|
+
client?: LarkReplyClient;
|
|
25
|
+
receivedEmojiType?: string;
|
|
26
|
+
}): SourceReceiptSink;
|
|
20
27
|
export declare function createLarkCallbackSink(input: {
|
|
21
28
|
appId?: string;
|
|
22
29
|
appSecret?: string;
|
|
@@ -29,4 +36,5 @@ export declare function createTelegramCallbackSink(input: {
|
|
|
29
36
|
fetchImpl?: FetchLike;
|
|
30
37
|
}): CallbackSink;
|
|
31
38
|
export declare function createCompositeCallbackSink(sinks: CallbackSink[]): CallbackSink;
|
|
39
|
+
export declare function createCompositeSourceReceiptSink(sinks: SourceReceiptSink[]): SourceReceiptSink;
|
|
32
40
|
//# sourceMappingURL=callbacks.d.ts.map
|
package/dist/callbacks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callbacks.d.ts","sourceRoot":"","sources":["../src/callbacks.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"callbacks.d.ts","sourceRoot":"","sources":["../src/callbacks.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,eAAe,EAIrB,MAAM,eAAe,CAAC;AASvB,OAAO,KAAK,EAA2C,YAAY,EAAiB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE3H,MAAM,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC;AA0ErC,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,YAAY,CA+CvG;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,GAAG,YAAY,CA0Df;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,iBAAiB,CAgDpB;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,iBAAiB,CAuBpB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B,GAAG,YAAY,CA6Cf;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,GAAG,YAAY,CAyDf;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,YAAY,CAuB/E;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,CAmB9F"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/callbacks.ts
|
|
2
2
|
import {
|
|
3
|
+
addLarkMessageReaction,
|
|
3
4
|
createLarkReplyClient,
|
|
4
5
|
patchLarkMessageCard,
|
|
5
6
|
parseLarkThreadKey,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
} from "@opentag/slack";
|
|
16
17
|
import { createTelegramSendMessageDraftPayload, createTelegramSendMessagePayload, parseTelegramThreadKey } from "@opentag/telegram";
|
|
17
18
|
var DEFAULT_SLACK_SOURCE_RECEIPT_TIMEOUT_MS = 5e3;
|
|
19
|
+
var DEFAULT_LARK_RECEIVED_REACTION = "Typing";
|
|
18
20
|
function slackUpdateUriFrom(postMessageUri) {
|
|
19
21
|
return postMessageUri.replace(/\/chat\.postMessage$/, "/chat.update");
|
|
20
22
|
}
|
|
@@ -41,6 +43,12 @@ function slackSourceMessageTarget(receipt) {
|
|
|
41
43
|
const messageTs = metadataString(receipt.event.metadata, "messageTs");
|
|
42
44
|
return channelId && messageTs ? { channelId, messageTs } : null;
|
|
43
45
|
}
|
|
46
|
+
function larkSourceMessageTarget(receipt) {
|
|
47
|
+
if (receipt.provider !== "lark" || receipt.state !== "received") return null;
|
|
48
|
+
const threadKey = receipt.event.callback.threadKey;
|
|
49
|
+
if (!threadKey) return null;
|
|
50
|
+
return { messageId: parseLarkThreadKey(threadKey).messageId };
|
|
51
|
+
}
|
|
44
52
|
function isAbortError(error) {
|
|
45
53
|
return error instanceof Error && error.name === "AbortError";
|
|
46
54
|
}
|
|
@@ -200,6 +208,24 @@ function createSlackSourceReceiptSink(input) {
|
|
|
200
208
|
}
|
|
201
209
|
};
|
|
202
210
|
}
|
|
211
|
+
function createLarkSourceReceiptSink(input) {
|
|
212
|
+
if (!input.client && Boolean(input.appId) !== Boolean(input.appSecret)) {
|
|
213
|
+
throw new Error("Lark source receipt sink requires both appId and appSecret (or neither).");
|
|
214
|
+
}
|
|
215
|
+
const client = input.client ?? (input.appId && input.appSecret ? createLarkReplyClient({ appId: input.appId, appSecret: input.appSecret, ...input.domain ? { domain: input.domain } : {} }) : void 0);
|
|
216
|
+
const receivedEmojiType = input.receivedEmojiType ?? DEFAULT_LARK_RECEIVED_REACTION;
|
|
217
|
+
return {
|
|
218
|
+
async deliver(receipt) {
|
|
219
|
+
const target = larkSourceMessageTarget(receipt);
|
|
220
|
+
if (!target || !client) return { delivered: false };
|
|
221
|
+
await addLarkMessageReaction(client, {
|
|
222
|
+
messageId: target.messageId,
|
|
223
|
+
emojiType: receivedEmojiType
|
|
224
|
+
});
|
|
225
|
+
return { delivered: true };
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
203
229
|
function createLarkCallbackSink(input) {
|
|
204
230
|
if (!input.client && Boolean(input.appId) !== Boolean(input.appSecret)) {
|
|
205
231
|
throw new Error("Lark callback sink requires both appId and appSecret (or neither).");
|
|
@@ -314,6 +340,26 @@ function createCompositeCallbackSink(sinks) {
|
|
|
314
340
|
}
|
|
315
341
|
};
|
|
316
342
|
}
|
|
343
|
+
function createCompositeSourceReceiptSink(sinks) {
|
|
344
|
+
return {
|
|
345
|
+
async deliver(receipt) {
|
|
346
|
+
let delivered = false;
|
|
347
|
+
const failures = [];
|
|
348
|
+
for (const sink of sinks) {
|
|
349
|
+
try {
|
|
350
|
+
const result = await sink.deliver(receipt);
|
|
351
|
+
delivered ||= result.delivered;
|
|
352
|
+
} catch (error) {
|
|
353
|
+
failures.push(error);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (!delivered && failures.length > 0) {
|
|
357
|
+
throw new AggregateError(failures, "Composite source receipt delivery failed for every sink.");
|
|
358
|
+
}
|
|
359
|
+
return { delivered };
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
317
363
|
|
|
318
364
|
// src/presentation.ts
|
|
319
365
|
import {
|
|
@@ -874,6 +920,16 @@ function shouldDeliverRunStatusUpdate(presentation, input) {
|
|
|
874
920
|
function larkLifecycleStatusMessageKey(input) {
|
|
875
921
|
return input.provider === "lark" ? `${input.runId}:status` : void 0;
|
|
876
922
|
}
|
|
923
|
+
function isTerminalRun(run) {
|
|
924
|
+
return ["succeeded", "failed", "cancelled", "interrupted", "timed_out"].includes(run.status);
|
|
925
|
+
}
|
|
926
|
+
function shouldUseDelayedLarkStatusCard(provider, options) {
|
|
927
|
+
return provider === "lark" && options.enabled !== false;
|
|
928
|
+
}
|
|
929
|
+
function safeExecutorLabel(executor) {
|
|
930
|
+
if (!executor || !/^[a-z0-9._-]{1,40}$/i.test(executor)) return "the selected agent";
|
|
931
|
+
return executor;
|
|
932
|
+
}
|
|
877
933
|
var CreateRunnerSchema = z.object({
|
|
878
934
|
runnerId: z.string().min(1),
|
|
879
935
|
name: z.string().min(1)
|
|
@@ -1946,7 +2002,7 @@ async function deliverAndAudit(input) {
|
|
|
1946
2002
|
...input.message.blocks ? { blocks: input.message.blocks } : {},
|
|
1947
2003
|
...input.message.rich ? { rich: input.message.rich } : {}
|
|
1948
2004
|
});
|
|
1949
|
-
|
|
2005
|
+
return deliverCallbackDelivery({
|
|
1950
2006
|
repo: input.repo,
|
|
1951
2007
|
sink: input.sink,
|
|
1952
2008
|
delivery,
|
|
@@ -2070,6 +2126,13 @@ function createDispatcherApp(input) {
|
|
|
2070
2126
|
const sourceReceiptSink = input.sourceReceiptSink ?? noopSourceReceiptSink;
|
|
2071
2127
|
const presentation = input.presentation ?? createDefaultCallbackPresentation();
|
|
2072
2128
|
const callbackRetry = input.callbackRetry ?? {};
|
|
2129
|
+
const larkStatusCardOptions = input.larkStatusCards ?? {};
|
|
2130
|
+
const larkStatusCardDelayMs = larkStatusCardOptions.delayMs ?? 1e4;
|
|
2131
|
+
const larkStatusCardMinUpdateIntervalMs = larkStatusCardOptions.minUpdateIntervalMs ?? 5e3;
|
|
2132
|
+
const larkStatusCardNow = larkStatusCardOptions.now ?? (() => Date.now());
|
|
2133
|
+
const setLarkStatusCardTimeout = larkStatusCardOptions.setTimeout ?? ((callback, delayMs) => globalThis.setTimeout(callback, delayMs));
|
|
2134
|
+
const clearLarkStatusCardTimeout = larkStatusCardOptions.clearTimeout ?? ((handle) => globalThis.clearTimeout(handle));
|
|
2135
|
+
const delayedLarkStatusCards = /* @__PURE__ */ new Map();
|
|
2073
2136
|
const maxRequestBodyBytes = input.maxRequestBodyBytes ?? DEFAULT_MAX_REQUEST_BODY_BYTES;
|
|
2074
2137
|
const runnerTokens = configuredRunnerTokens(input);
|
|
2075
2138
|
const revokedRunnerTokenFingerprints = normalizeRevokedRunnerTokenFingerprints(input.revokedRunnerTokenFingerprints);
|
|
@@ -2132,6 +2195,145 @@ function createDispatcherApp(input) {
|
|
|
2132
2195
|
message: "Run status callback suppressed by platform liveness strategy; use status or audit for details."
|
|
2133
2196
|
});
|
|
2134
2197
|
};
|
|
2198
|
+
function delayedLarkStatusMessage(input2) {
|
|
2199
|
+
if (input2.phase === "queued") return "Waiting for the local runner.";
|
|
2200
|
+
if (input2.phase === "progress") return "OpenTag is still working.";
|
|
2201
|
+
return `Running with ${safeExecutorLabel(input2.run.executor)}.`;
|
|
2202
|
+
}
|
|
2203
|
+
function delayedLarkStatusState(input2) {
|
|
2204
|
+
if (input2.phase === "queued") return "queued";
|
|
2205
|
+
return "running";
|
|
2206
|
+
}
|
|
2207
|
+
async function appendDelayedLarkStatusFailure(input2) {
|
|
2208
|
+
await repo.appendRunEvent({
|
|
2209
|
+
runId: input2.runId,
|
|
2210
|
+
type: "callback.progress.failed",
|
|
2211
|
+
payload: {
|
|
2212
|
+
provider: "lark",
|
|
2213
|
+
reason: "delayed_status_card",
|
|
2214
|
+
error: input2.error instanceof Error ? input2.error.message : String(input2.error)
|
|
2215
|
+
},
|
|
2216
|
+
visibility: "audit",
|
|
2217
|
+
importance: "low",
|
|
2218
|
+
message: "Delayed Lark status card update failed."
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
async function deliverDelayedLarkStatusCard(input2) {
|
|
2222
|
+
if (!shouldUseDelayedLarkStatusCard(input2.event.callback.provider, larkStatusCardOptions)) return false;
|
|
2223
|
+
if (!input2.event.callback.threadKey) return false;
|
|
2224
|
+
if (isTerminalRun(input2.run)) return false;
|
|
2225
|
+
const statusMessageKey = larkLifecycleStatusMessageKey({ provider: input2.event.callback.provider, runId: input2.run.id });
|
|
2226
|
+
if (!statusMessageKey) return false;
|
|
2227
|
+
const state = delayedLarkStatusCards.get(input2.run.id) ?? { cardCreated: false };
|
|
2228
|
+
delayedLarkStatusCards.set(input2.run.id, state);
|
|
2229
|
+
if (!input2.createIfMissing && !state.cardCreated) return false;
|
|
2230
|
+
const now = larkStatusCardNow();
|
|
2231
|
+
const phaseChanged = state.lastPhase !== input2.phase;
|
|
2232
|
+
const intervalElapsed = !state.lastUpdateAt || now - state.lastUpdateAt >= larkStatusCardMinUpdateIntervalMs;
|
|
2233
|
+
if (!input2.createIfMissing && !phaseChanged && !intervalElapsed) return false;
|
|
2234
|
+
const statusPresentation = presentation.runStatusPresentation({
|
|
2235
|
+
runId: input2.run.id,
|
|
2236
|
+
state: delayedLarkStatusState({ run: input2.run, phase: input2.phase }),
|
|
2237
|
+
message: delayedLarkStatusMessage({ run: input2.run, phase: input2.phase }),
|
|
2238
|
+
nextAction: "Use /status here for active-run and queue state, or wait for the final result.",
|
|
2239
|
+
detailVisibility: "source_thread"
|
|
2240
|
+
});
|
|
2241
|
+
const rendered = presentation.render({
|
|
2242
|
+
provider: input2.event.callback.provider,
|
|
2243
|
+
presentation: statusPresentation
|
|
2244
|
+
});
|
|
2245
|
+
const delivered = await deliverAndAudit({
|
|
2246
|
+
repo,
|
|
2247
|
+
sink: callbackSink,
|
|
2248
|
+
retry: callbackRetry,
|
|
2249
|
+
message: {
|
|
2250
|
+
runId: input2.run.id,
|
|
2251
|
+
kind: "progress",
|
|
2252
|
+
provider: input2.event.callback.provider,
|
|
2253
|
+
uri: input2.event.callback.uri,
|
|
2254
|
+
body: rendered.body,
|
|
2255
|
+
...input2.event.target.agentId ? { agentId: input2.event.target.agentId } : {},
|
|
2256
|
+
threadKey: input2.event.callback.threadKey,
|
|
2257
|
+
...rendered.blocks?.length ? { blocks: rendered.blocks } : {},
|
|
2258
|
+
...rendered.rich ? { rich: rendered.rich } : {},
|
|
2259
|
+
statusMessageKey
|
|
2260
|
+
}
|
|
2261
|
+
});
|
|
2262
|
+
if (!delivered) return false;
|
|
2263
|
+
state.cardCreated = true;
|
|
2264
|
+
state.lastPhase = input2.phase;
|
|
2265
|
+
state.lastUpdateAt = now;
|
|
2266
|
+
return true;
|
|
2267
|
+
}
|
|
2268
|
+
function scheduleDelayedLarkStatusCard(input2) {
|
|
2269
|
+
if (!shouldUseDelayedLarkStatusCard(input2.event.callback.provider, larkStatusCardOptions)) return;
|
|
2270
|
+
if (!input2.event.callback.threadKey) return;
|
|
2271
|
+
if (larkStatusCardDelayMs < 0) return;
|
|
2272
|
+
const existing = delayedLarkStatusCards.get(input2.run.id);
|
|
2273
|
+
if (existing?.timer || existing?.cardCreated) return;
|
|
2274
|
+
const state = existing ?? { cardCreated: false };
|
|
2275
|
+
const timer = setLarkStatusCardTimeout(() => {
|
|
2276
|
+
delete state.timer;
|
|
2277
|
+
void (async () => {
|
|
2278
|
+
try {
|
|
2279
|
+
const latestRun = await repo.getRun({ runId: input2.run.id });
|
|
2280
|
+
if (!latestRun || isTerminalRun(latestRun.run)) {
|
|
2281
|
+
delayedLarkStatusCards.delete(input2.run.id);
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
const phase = latestRun.run.status === "queued" ? "queued" : "running";
|
|
2285
|
+
await deliverDelayedLarkStatusCard({
|
|
2286
|
+
run: latestRun.run,
|
|
2287
|
+
event: input2.event,
|
|
2288
|
+
phase,
|
|
2289
|
+
createIfMissing: true
|
|
2290
|
+
});
|
|
2291
|
+
} catch (error) {
|
|
2292
|
+
await appendDelayedLarkStatusFailure({ runId: input2.run.id, error });
|
|
2293
|
+
}
|
|
2294
|
+
})();
|
|
2295
|
+
}, larkStatusCardDelayMs);
|
|
2296
|
+
if (timer && typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") {
|
|
2297
|
+
timer.unref();
|
|
2298
|
+
}
|
|
2299
|
+
state.timer = timer;
|
|
2300
|
+
delayedLarkStatusCards.set(input2.run.id, state);
|
|
2301
|
+
}
|
|
2302
|
+
function cancelPendingDelayedLarkStatusCard(runId) {
|
|
2303
|
+
const state = delayedLarkStatusCards.get(runId);
|
|
2304
|
+
if (!state?.timer) return;
|
|
2305
|
+
clearLarkStatusCardTimeout(state.timer);
|
|
2306
|
+
delete state.timer;
|
|
2307
|
+
}
|
|
2308
|
+
function clearDelayedLarkStatusCard(runId) {
|
|
2309
|
+
cancelPendingDelayedLarkStatusCard(runId);
|
|
2310
|
+
delayedLarkStatusCards.delete(runId);
|
|
2311
|
+
}
|
|
2312
|
+
async function patchDelayedLarkStatusCard(input2) {
|
|
2313
|
+
let state = delayedLarkStatusCards.get(input2.run.id);
|
|
2314
|
+
if (!state?.cardCreated) {
|
|
2315
|
+
const statusMessageKey = larkLifecycleStatusMessageKey({ provider: input2.event.callback.provider, runId: input2.run.id });
|
|
2316
|
+
const externalMessageId = statusMessageKey && input2.event.callback.threadKey ? await repo.findCallbackExternalMessageId({
|
|
2317
|
+
runId: input2.run.id,
|
|
2318
|
+
provider: input2.event.callback.provider,
|
|
2319
|
+
threadKey: input2.event.callback.threadKey,
|
|
2320
|
+
statusMessageKey
|
|
2321
|
+
}) : void 0;
|
|
2322
|
+
if (!externalMessageId) return;
|
|
2323
|
+
state = state ?? { cardCreated: true };
|
|
2324
|
+
state.cardCreated = true;
|
|
2325
|
+
delayedLarkStatusCards.set(input2.run.id, state);
|
|
2326
|
+
}
|
|
2327
|
+
try {
|
|
2328
|
+
await deliverDelayedLarkStatusCard({
|
|
2329
|
+
run: input2.run,
|
|
2330
|
+
event: input2.event,
|
|
2331
|
+
phase: input2.phase
|
|
2332
|
+
});
|
|
2333
|
+
} catch (error) {
|
|
2334
|
+
await appendDelayedLarkStatusFailure({ runId: input2.run.id, error });
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2135
2337
|
async function deliverPromotedFollowUpAcknowledgement(input2) {
|
|
2136
2338
|
if (!presentation.shouldDeliverAcknowledgement(input2.event.callback.provider)) return;
|
|
2137
2339
|
const acknowledgementPresentation = presentation.acknowledgementPresentation({ runId: input2.run.id });
|
|
@@ -2645,6 +2847,9 @@ function createDispatcherApp(input) {
|
|
|
2645
2847
|
...parsed.event.target.agentId ? { agentId: parsed.event.target.agentId } : {}
|
|
2646
2848
|
}
|
|
2647
2849
|
});
|
|
2850
|
+
if (sourceReceiptDelivery.delivered) {
|
|
2851
|
+
scheduleDelayedLarkStatusCard({ run, event: parsed.event });
|
|
2852
|
+
}
|
|
2648
2853
|
const shouldDeliverAcknowledgement = presentation.shouldDeliverAcknowledgement(parsed.event.callback.provider) || shouldDeliverSourceReceipt(parsed.event.callback.provider) && !sourceReceiptDelivery.delivered;
|
|
2649
2854
|
if (shouldDeliverAcknowledgement) {
|
|
2650
2855
|
const acknowledgementPresentation = presentation.acknowledgementPresentation({ runId: run.id });
|
|
@@ -3123,6 +3328,11 @@ function createDispatcherApp(input) {
|
|
|
3123
3328
|
} else if (presentation.shouldDeliverStatusUpdate(provider)) {
|
|
3124
3329
|
await appendSuppressedRunStatusCallback({ runId, provider, state: "running" });
|
|
3125
3330
|
}
|
|
3331
|
+
await patchDelayedLarkStatusCard({
|
|
3332
|
+
run: stored.run,
|
|
3333
|
+
event: stored.event,
|
|
3334
|
+
phase: "running"
|
|
3335
|
+
});
|
|
3126
3336
|
return c.json({ ok: true });
|
|
3127
3337
|
});
|
|
3128
3338
|
app.post("/v1/runs/:runId/progress", async () => {
|
|
@@ -3191,6 +3401,11 @@ function createDispatcherApp(input) {
|
|
|
3191
3401
|
message: "Progress callback suppressed by platform liveness strategy; use status or audit for details."
|
|
3192
3402
|
});
|
|
3193
3403
|
}
|
|
3404
|
+
await patchDelayedLarkStatusCard({
|
|
3405
|
+
run: stored.run,
|
|
3406
|
+
event: stored.event,
|
|
3407
|
+
phase: "progress"
|
|
3408
|
+
});
|
|
3194
3409
|
return c.json({ ok: true });
|
|
3195
3410
|
});
|
|
3196
3411
|
app.post("/v1/runs/:runId/complete", async () => {
|
|
@@ -3214,6 +3429,7 @@ function createDispatcherApp(input) {
|
|
|
3214
3429
|
if (outcome === "duplicate") return c.json({ ok: true, replayed: true });
|
|
3215
3430
|
const stored = await repo.getRun({ runId });
|
|
3216
3431
|
if (!stored) return c.json({ error: "run_not_found" }, 404);
|
|
3432
|
+
cancelPendingDelayedLarkStatusCard(runId);
|
|
3217
3433
|
const receiptContext = await actionReceiptContextForFinal({
|
|
3218
3434
|
event: stored.event,
|
|
3219
3435
|
result: parsed.result,
|
|
@@ -3276,6 +3492,7 @@ function createDispatcherApp(input) {
|
|
|
3276
3492
|
...finalCallback.rich ? { rich: finalCallback.rich } : {}
|
|
3277
3493
|
}
|
|
3278
3494
|
});
|
|
3495
|
+
clearDelayedLarkStatusCard(runId);
|
|
3279
3496
|
const shouldPromoteFollowUp = parsed.result.conclusion !== "needs_human" && parsed.result.conclusion !== "cancelled";
|
|
3280
3497
|
const promotedFollowUp = shouldPromoteFollowUp ? await promoteNextFollowUpAfterTerminalRun({ activeRunId: runId }) : null;
|
|
3281
3498
|
return c.json({
|
|
@@ -3482,10 +3699,12 @@ function createDispatcherApp(input) {
|
|
|
3482
3699
|
}
|
|
3483
3700
|
export {
|
|
3484
3701
|
createCompositeCallbackSink,
|
|
3702
|
+
createCompositeSourceReceiptSink,
|
|
3485
3703
|
createDefaultCallbackPresentation,
|
|
3486
3704
|
createDispatcherApp,
|
|
3487
3705
|
createGitHubCallbackSink,
|
|
3488
3706
|
createLarkCallbackSink,
|
|
3707
|
+
createLarkSourceReceiptSink,
|
|
3489
3708
|
createSlackCallbackSink,
|
|
3490
3709
|
createSlackSourceReceiptSink,
|
|
3491
3710
|
createTelegramCallbackSink,
|