@agent-native/dispatch 0.14.11 → 0.14.13
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/actions/ask_app.d.ts +4 -0
- package/dist/actions/ask_app_status.d.ts +4 -0
- package/dist/actions/provider-api-catalog.d.ts +1 -1
- package/dist/routes/pages/dreams.js +3 -3
- package/dist/routes/pages/dreams.js.map +1 -1
- package/dist/server/lib/mcp-gateway.d.ts +8 -2
- package/dist/server/lib/mcp-gateway.d.ts.map +1 -1
- package/dist/server/lib/mcp-gateway.js +94 -3
- package/dist/server/lib/mcp-gateway.js.map +1 -1
- package/dist/server/lib/provider-api.d.ts +2 -2
- package/dist/server/lib/provider-api.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/routes/pages/dreams.tsx +9 -9
- package/src/server/lib/mcp-gateway.spec.ts +71 -0
- package/src/server/lib/mcp-gateway.ts +131 -13
|
@@ -34,6 +34,7 @@ const DISPATCH_ASK_APP_DEFAULT_INLINE_WAIT_MS = 20_000;
|
|
|
34
34
|
const DISPATCH_ASK_APP_MAX_INLINE_WAIT_MS = 25_000;
|
|
35
35
|
const DISPATCH_ASK_APP_POLL_INTERVAL_MS = 1_500;
|
|
36
36
|
const DISPATCH_A2A_REQUEST_TIMEOUT_MS = 10_000;
|
|
37
|
+
const DISPATCH_ASK_APP_STATUS_RETRY_DELAYS_MS = [250, 750, 1_500] as const;
|
|
37
38
|
const DISPATCH_ASK_APP_TERMINAL_STATES = new Set([
|
|
38
39
|
"completed",
|
|
39
40
|
"failed",
|
|
@@ -89,10 +90,13 @@ function dispatchTaskText(task: Task): string {
|
|
|
89
90
|
);
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
type DispatchAskAppStatusErrorCategory =
|
|
94
|
+
| "transport"
|
|
95
|
+
| "timeout"
|
|
96
|
+
| "upstream_5xx"
|
|
97
|
+
| "rate_limited";
|
|
98
|
+
|
|
99
|
+
type DispatchAskAppTaskResult = {
|
|
96
100
|
app: string;
|
|
97
101
|
routedVia: "a2a";
|
|
98
102
|
taskId: string;
|
|
@@ -100,10 +104,19 @@ function dispatchAskAppTaskResult(
|
|
|
100
104
|
response?: string;
|
|
101
105
|
error?: string;
|
|
102
106
|
inputRequired?: string;
|
|
107
|
+
statusRead?: "unavailable";
|
|
108
|
+
retryable?: true;
|
|
109
|
+
errorCategory?: DispatchAskAppStatusErrorCategory;
|
|
110
|
+
attempts?: number;
|
|
103
111
|
pollAfterMs?: number;
|
|
104
112
|
poll?: { tool: "ask_app_status"; arguments: { app: string; taskId: string } };
|
|
105
113
|
message?: string;
|
|
106
|
-
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
function dispatchAskAppTaskResult(
|
|
117
|
+
app: string,
|
|
118
|
+
task: Task,
|
|
119
|
+
): DispatchAskAppTaskResult {
|
|
107
120
|
const status = String(task.status.state);
|
|
108
121
|
const response = dispatchTaskText(task);
|
|
109
122
|
const base = {
|
|
@@ -197,12 +210,76 @@ async function createDispatchA2AClient(input: {
|
|
|
197
210
|
}
|
|
198
211
|
|
|
199
212
|
function isTransientDispatchAskAppStatusError(err: unknown): boolean {
|
|
213
|
+
return dispatchAskAppStatusErrorCategory(err) != null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function dispatchAskAppStatusErrorCategory(
|
|
217
|
+
err: unknown,
|
|
218
|
+
): DispatchAskAppStatusErrorCategory | null {
|
|
200
219
|
const message = err instanceof Error ? err.message : String(err ?? "");
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
220
|
+
const causeCode = dispatchAskAppStatusErrorCauseCode(err) ?? "";
|
|
221
|
+
const diagnostic = `${message} ${causeCode}`;
|
|
222
|
+
if (/A2A request failed \(429\)/i.test(message)) return "rate_limited";
|
|
223
|
+
if (/A2A request failed \((?:500|502|503|504)\)/i.test(message)) {
|
|
224
|
+
return "upstream_5xx";
|
|
225
|
+
}
|
|
226
|
+
if (/etimedout|timeout|aborted|aborterror/i.test(diagnostic)) {
|
|
227
|
+
return "timeout";
|
|
228
|
+
}
|
|
229
|
+
if (
|
|
230
|
+
/\bfetch failed\b|failed to fetch|networkerror|socket hang up|econnreset/i.test(
|
|
231
|
+
diagnostic,
|
|
232
|
+
)
|
|
233
|
+
) {
|
|
234
|
+
return "transport";
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function dispatchAskAppStatusErrorCauseCode(err: unknown): string | undefined {
|
|
240
|
+
if (!(err instanceof Error)) return undefined;
|
|
241
|
+
const directCode = (err as Error & { code?: unknown }).code;
|
|
242
|
+
if (typeof directCode === "string" && directCode.trim()) {
|
|
243
|
+
return directCode.trim();
|
|
244
|
+
}
|
|
245
|
+
if (!err.cause || typeof err.cause !== "object") return undefined;
|
|
246
|
+
const code = (err.cause as { code?: unknown }).code;
|
|
247
|
+
return typeof code === "string" && code.trim() ? code.trim() : undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function dispatchAskAppStatusOriginHost(origin: string): string {
|
|
251
|
+
try {
|
|
252
|
+
return new URL(origin).host;
|
|
253
|
+
} catch {
|
|
254
|
+
return "unknown";
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function dispatchAskAppStatusReadUnavailableResult(
|
|
259
|
+
app: string,
|
|
260
|
+
taskId: string,
|
|
261
|
+
errorCategory: DispatchAskAppStatusErrorCategory,
|
|
262
|
+
attempts: number,
|
|
263
|
+
): DispatchAskAppTaskResult {
|
|
264
|
+
return {
|
|
265
|
+
app,
|
|
266
|
+
routedVia: "a2a",
|
|
267
|
+
taskId,
|
|
268
|
+
status: "unknown",
|
|
269
|
+
statusRead: "unavailable",
|
|
270
|
+
retryable: true,
|
|
271
|
+
errorCategory,
|
|
272
|
+
attempts,
|
|
273
|
+
pollAfterMs: DISPATCH_ASK_APP_POLL_INTERVAL_MS,
|
|
274
|
+
poll: {
|
|
275
|
+
tool: "ask_app_status",
|
|
276
|
+
arguments: { app, taskId },
|
|
277
|
+
},
|
|
278
|
+
message:
|
|
279
|
+
"The durable ask_app task status could not be read after bounded retries. " +
|
|
280
|
+
"The task may still be running or completed. Retry ask_app_status " +
|
|
281
|
+
"with the same app and taskId; do not resubmit ask_app.",
|
|
282
|
+
};
|
|
206
283
|
}
|
|
207
284
|
|
|
208
285
|
async function runBeforeDispatchAskAppDeadline<T>(
|
|
@@ -539,7 +616,7 @@ export async function askGrantedDispatchMcpApp(
|
|
|
539
616
|
export async function getGrantedDispatchMcpAppTask(
|
|
540
617
|
app: string,
|
|
541
618
|
taskId: string,
|
|
542
|
-
): Promise<
|
|
619
|
+
): Promise<DispatchAskAppTaskResult> {
|
|
543
620
|
const trimmedTaskId = taskId.trim();
|
|
544
621
|
if (!trimmedTaskId) throw new Error("taskId is required");
|
|
545
622
|
const target = await resolveGrantedDispatchMcpApp(app);
|
|
@@ -559,8 +636,49 @@ export async function getGrantedDispatchMcpAppTask(
|
|
|
559
636
|
orgDomain: orgDomain ?? undefined,
|
|
560
637
|
orgSecret: orgSecret ?? undefined,
|
|
561
638
|
});
|
|
562
|
-
const
|
|
563
|
-
|
|
639
|
+
const maxAttempts = DISPATCH_ASK_APP_STATUS_RETRY_DELAYS_MS.length + 1;
|
|
640
|
+
for (
|
|
641
|
+
let attempt = 0;
|
|
642
|
+
attempt <= DISPATCH_ASK_APP_STATUS_RETRY_DELAYS_MS.length;
|
|
643
|
+
attempt++
|
|
644
|
+
) {
|
|
645
|
+
const startedAt = Date.now();
|
|
646
|
+
try {
|
|
647
|
+
const task = await client.getTask(trimmedTaskId);
|
|
648
|
+
return dispatchAskAppTaskResult(target.id, task);
|
|
649
|
+
} catch (err) {
|
|
650
|
+
const delayMs = DISPATCH_ASK_APP_STATUS_RETRY_DELAYS_MS[attempt];
|
|
651
|
+
const errorCategory = dispatchAskAppStatusErrorCategory(err);
|
|
652
|
+
const retryable = errorCategory != null;
|
|
653
|
+
const willRetry = retryable && delayMs != null;
|
|
654
|
+
if (retryable) {
|
|
655
|
+
console.warn("[ask_app_status] tasks/get attempt failed", {
|
|
656
|
+
app: target.id,
|
|
657
|
+
routedVia: "a2a",
|
|
658
|
+
taskId: trimmedTaskId,
|
|
659
|
+
originHost: dispatchAskAppStatusOriginHost(target.url),
|
|
660
|
+
attempt: attempt + 1,
|
|
661
|
+
maxAttempts,
|
|
662
|
+
elapsedMs: Date.now() - startedAt,
|
|
663
|
+
errorCategory,
|
|
664
|
+
errorName: err instanceof Error ? err.name : typeof err,
|
|
665
|
+
causeCode: dispatchAskAppStatusErrorCauseCode(err),
|
|
666
|
+
willRetry,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
if (!retryable) throw err;
|
|
670
|
+
if (delayMs == null) {
|
|
671
|
+
return dispatchAskAppStatusReadUnavailableResult(
|
|
672
|
+
target.id,
|
|
673
|
+
trimmedTaskId,
|
|
674
|
+
errorCategory,
|
|
675
|
+
maxAttempts,
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
throw new Error("ask_app_status retry loop exited unexpectedly.");
|
|
564
682
|
}
|
|
565
683
|
|
|
566
684
|
export async function openGrantedDispatchMcpApp(input: {
|