@opentag/dispatcher 0.3.3 → 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/index.js +173 -2
- 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/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "@opentag/slack";
|
|
17
17
|
import { createTelegramSendMessageDraftPayload, createTelegramSendMessagePayload, parseTelegramThreadKey } from "@opentag/telegram";
|
|
18
18
|
var DEFAULT_SLACK_SOURCE_RECEIPT_TIMEOUT_MS = 5e3;
|
|
19
|
-
var DEFAULT_LARK_RECEIVED_REACTION = "
|
|
19
|
+
var DEFAULT_LARK_RECEIVED_REACTION = "Typing";
|
|
20
20
|
function slackUpdateUriFrom(postMessageUri) {
|
|
21
21
|
return postMessageUri.replace(/\/chat\.postMessage$/, "/chat.update");
|
|
22
22
|
}
|
|
@@ -920,6 +920,16 @@ function shouldDeliverRunStatusUpdate(presentation, input) {
|
|
|
920
920
|
function larkLifecycleStatusMessageKey(input) {
|
|
921
921
|
return input.provider === "lark" ? `${input.runId}:status` : void 0;
|
|
922
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
|
+
}
|
|
923
933
|
var CreateRunnerSchema = z.object({
|
|
924
934
|
runnerId: z.string().min(1),
|
|
925
935
|
name: z.string().min(1)
|
|
@@ -1992,7 +2002,7 @@ async function deliverAndAudit(input) {
|
|
|
1992
2002
|
...input.message.blocks ? { blocks: input.message.blocks } : {},
|
|
1993
2003
|
...input.message.rich ? { rich: input.message.rich } : {}
|
|
1994
2004
|
});
|
|
1995
|
-
|
|
2005
|
+
return deliverCallbackDelivery({
|
|
1996
2006
|
repo: input.repo,
|
|
1997
2007
|
sink: input.sink,
|
|
1998
2008
|
delivery,
|
|
@@ -2116,6 +2126,13 @@ function createDispatcherApp(input) {
|
|
|
2116
2126
|
const sourceReceiptSink = input.sourceReceiptSink ?? noopSourceReceiptSink;
|
|
2117
2127
|
const presentation = input.presentation ?? createDefaultCallbackPresentation();
|
|
2118
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();
|
|
2119
2136
|
const maxRequestBodyBytes = input.maxRequestBodyBytes ?? DEFAULT_MAX_REQUEST_BODY_BYTES;
|
|
2120
2137
|
const runnerTokens = configuredRunnerTokens(input);
|
|
2121
2138
|
const revokedRunnerTokenFingerprints = normalizeRevokedRunnerTokenFingerprints(input.revokedRunnerTokenFingerprints);
|
|
@@ -2178,6 +2195,145 @@ function createDispatcherApp(input) {
|
|
|
2178
2195
|
message: "Run status callback suppressed by platform liveness strategy; use status or audit for details."
|
|
2179
2196
|
});
|
|
2180
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
|
+
}
|
|
2181
2337
|
async function deliverPromotedFollowUpAcknowledgement(input2) {
|
|
2182
2338
|
if (!presentation.shouldDeliverAcknowledgement(input2.event.callback.provider)) return;
|
|
2183
2339
|
const acknowledgementPresentation = presentation.acknowledgementPresentation({ runId: input2.run.id });
|
|
@@ -2691,6 +2847,9 @@ function createDispatcherApp(input) {
|
|
|
2691
2847
|
...parsed.event.target.agentId ? { agentId: parsed.event.target.agentId } : {}
|
|
2692
2848
|
}
|
|
2693
2849
|
});
|
|
2850
|
+
if (sourceReceiptDelivery.delivered) {
|
|
2851
|
+
scheduleDelayedLarkStatusCard({ run, event: parsed.event });
|
|
2852
|
+
}
|
|
2694
2853
|
const shouldDeliverAcknowledgement = presentation.shouldDeliverAcknowledgement(parsed.event.callback.provider) || shouldDeliverSourceReceipt(parsed.event.callback.provider) && !sourceReceiptDelivery.delivered;
|
|
2695
2854
|
if (shouldDeliverAcknowledgement) {
|
|
2696
2855
|
const acknowledgementPresentation = presentation.acknowledgementPresentation({ runId: run.id });
|
|
@@ -3169,6 +3328,11 @@ function createDispatcherApp(input) {
|
|
|
3169
3328
|
} else if (presentation.shouldDeliverStatusUpdate(provider)) {
|
|
3170
3329
|
await appendSuppressedRunStatusCallback({ runId, provider, state: "running" });
|
|
3171
3330
|
}
|
|
3331
|
+
await patchDelayedLarkStatusCard({
|
|
3332
|
+
run: stored.run,
|
|
3333
|
+
event: stored.event,
|
|
3334
|
+
phase: "running"
|
|
3335
|
+
});
|
|
3172
3336
|
return c.json({ ok: true });
|
|
3173
3337
|
});
|
|
3174
3338
|
app.post("/v1/runs/:runId/progress", async () => {
|
|
@@ -3237,6 +3401,11 @@ function createDispatcherApp(input) {
|
|
|
3237
3401
|
message: "Progress callback suppressed by platform liveness strategy; use status or audit for details."
|
|
3238
3402
|
});
|
|
3239
3403
|
}
|
|
3404
|
+
await patchDelayedLarkStatusCard({
|
|
3405
|
+
run: stored.run,
|
|
3406
|
+
event: stored.event,
|
|
3407
|
+
phase: "progress"
|
|
3408
|
+
});
|
|
3240
3409
|
return c.json({ ok: true });
|
|
3241
3410
|
});
|
|
3242
3411
|
app.post("/v1/runs/:runId/complete", async () => {
|
|
@@ -3260,6 +3429,7 @@ function createDispatcherApp(input) {
|
|
|
3260
3429
|
if (outcome === "duplicate") return c.json({ ok: true, replayed: true });
|
|
3261
3430
|
const stored = await repo.getRun({ runId });
|
|
3262
3431
|
if (!stored) return c.json({ error: "run_not_found" }, 404);
|
|
3432
|
+
cancelPendingDelayedLarkStatusCard(runId);
|
|
3263
3433
|
const receiptContext = await actionReceiptContextForFinal({
|
|
3264
3434
|
event: stored.event,
|
|
3265
3435
|
result: parsed.result,
|
|
@@ -3322,6 +3492,7 @@ function createDispatcherApp(input) {
|
|
|
3322
3492
|
...finalCallback.rich ? { rich: finalCallback.rich } : {}
|
|
3323
3493
|
}
|
|
3324
3494
|
});
|
|
3495
|
+
clearDelayedLarkStatusCard(runId);
|
|
3325
3496
|
const shouldPromoteFollowUp = parsed.result.conclusion !== "needs_human" && parsed.result.conclusion !== "cancelled";
|
|
3326
3497
|
const promotedFollowUp = shouldPromoteFollowUp ? await promoteNextFollowUpAfterTerminalRun({ activeRunId: runId }) : null;
|
|
3327
3498
|
return c.json({
|