@nordsym/apiclaw 1.3.13 → 1.4.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/PRD-API-CHAINING.md +483 -0
- package/PRD-HARDEN-SHELL.md +18 -12
- package/convex/_generated/api.d.ts +2 -0
- package/convex/chains.ts +1095 -0
- package/convex/schema.ts +101 -0
- package/dist/chain-types.d.ts +187 -0
- package/dist/chain-types.d.ts.map +1 -0
- package/dist/chain-types.js +33 -0
- package/dist/chain-types.js.map +1 -0
- package/dist/chainExecutor.d.ts +122 -0
- package/dist/chainExecutor.d.ts.map +1 -0
- package/dist/chainExecutor.js +454 -0
- package/dist/chainExecutor.js.map +1 -0
- package/dist/chainResolver.d.ts +100 -0
- package/dist/chainResolver.d.ts.map +1 -0
- package/dist/chainResolver.js +519 -0
- package/dist/chainResolver.js.map +1 -0
- package/dist/chainResolver.test.d.ts +5 -0
- package/dist/chainResolver.test.d.ts.map +1 -0
- package/dist/chainResolver.test.js +201 -0
- package/dist/chainResolver.test.js.map +1 -0
- package/dist/execute.d.ts +4 -1
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +3 -0
- package/dist/execute.js.map +1 -1
- package/dist/index.js +382 -2
- package/dist/index.js.map +1 -1
- package/landing/public/logos/chattgpt.svg +1 -0
- package/landing/public/logos/claude.svg +1 -0
- package/landing/public/logos/gemini.svg +1 -0
- package/landing/public/logos/grok.svg +1 -0
- package/landing/src/app/page.tsx +12 -21
- package/landing/src/app/workspace/chains/page.tsx +520 -0
- package/landing/src/components/AITestimonials.tsx +15 -9
- package/landing/src/components/ChainStepDetail.tsx +310 -0
- package/landing/src/components/ChainTrace.tsx +261 -0
- package/landing/src/lib/stats.json +1 -1
- package/package.json +1 -1
- package/src/chain-types.ts +270 -0
- package/src/chainExecutor.ts +730 -0
- package/src/chainResolver.test.ts +246 -0
- package/src/chainResolver.ts +658 -0
- package/src/execute.ts +23 -0
- package/src/index.ts +423 -2
package/src/index.ts
CHANGED
|
@@ -51,6 +51,16 @@ import {
|
|
|
51
51
|
METERED_BILLING
|
|
52
52
|
} from './stripe.js';
|
|
53
53
|
import { estimateCost } from './metered.js';
|
|
54
|
+
import {
|
|
55
|
+
executeChain,
|
|
56
|
+
getChainStatus,
|
|
57
|
+
resumeChain,
|
|
58
|
+
type ChainDefinition,
|
|
59
|
+
type ChainResult,
|
|
60
|
+
type Credentials as ChainCredentials,
|
|
61
|
+
type ChainOptions,
|
|
62
|
+
type ChainStepUnion
|
|
63
|
+
} from './chainExecutor.js';
|
|
54
64
|
|
|
55
65
|
// Default agent ID for MVP (in production, this would come from auth)
|
|
56
66
|
const DEFAULT_AGENT_ID = 'agent_default';
|
|
@@ -320,10 +330,28 @@ const tools: Tool[] = [
|
|
|
320
330
|
},
|
|
321
331
|
{
|
|
322
332
|
name: 'call_api',
|
|
323
|
-
description:
|
|
333
|
+
description: `Execute an API call through APIClaw. Supports single calls AND multi-step chains.
|
|
334
|
+
|
|
335
|
+
SINGLE CALL: Provide provider + action + params
|
|
336
|
+
CHAIN: Provide chain array to execute multiple APIs in sequence/parallel with cross-step references.
|
|
337
|
+
|
|
338
|
+
Chain features:
|
|
339
|
+
- Sequential: Steps execute in order, each can reference previous results via $stepId.property
|
|
340
|
+
- Parallel: Use { parallel: [...steps] } to run concurrently
|
|
341
|
+
- Conditional: Use { if: "$step.success", then: {...}, else: {...} }
|
|
342
|
+
- Loops: Use { forEach: "$step.results", as: "item", do: {...} }
|
|
343
|
+
- Error handling: Per-step retry/fallback via onError
|
|
344
|
+
- Async: Set async: true to get chainId immediately, poll or use webhook
|
|
345
|
+
|
|
346
|
+
Example chain:
|
|
347
|
+
chain: [
|
|
348
|
+
{ id: "search", provider: "brave_search", action: "search", params: { query: "AI agents" } },
|
|
349
|
+
{ id: "summarize", provider: "openrouter", action: "chat", params: { message: "Summarize: $search.results" } }
|
|
350
|
+
]`,
|
|
324
351
|
inputSchema: {
|
|
325
352
|
type: 'object',
|
|
326
353
|
properties: {
|
|
354
|
+
// Single call params
|
|
327
355
|
provider: {
|
|
328
356
|
type: 'string',
|
|
329
357
|
description: 'Provider ID (e.g., "46elks", "brave_search", "resend", "openrouter", "elevenlabs", "twilio", "coaccept", "frankfurter")'
|
|
@@ -347,9 +375,62 @@ const tools: Tool[] = [
|
|
|
347
375
|
dry_run: {
|
|
348
376
|
type: 'boolean',
|
|
349
377
|
description: 'If true, shows what WOULD be sent without making actual API calls. Returns mock response and request details. Great for testing and debugging.'
|
|
378
|
+
},
|
|
379
|
+
// Chain execution params
|
|
380
|
+
chain: {
|
|
381
|
+
type: 'array',
|
|
382
|
+
description: 'Execute multiple API calls as a single chain. Each step can reference previous results via $stepId.property',
|
|
383
|
+
items: {
|
|
384
|
+
type: 'object',
|
|
385
|
+
properties: {
|
|
386
|
+
id: { type: 'string', description: 'Step identifier for cross-step references' },
|
|
387
|
+
provider: { type: 'string', description: 'API provider' },
|
|
388
|
+
action: { type: 'string', description: 'Action to execute' },
|
|
389
|
+
params: { type: 'object', description: 'Action parameters. Use $stepId.path for references.' },
|
|
390
|
+
parallel: { type: 'array', description: 'Steps to run in parallel' },
|
|
391
|
+
if: { type: 'string', description: 'Condition for conditional execution (e.g., "$step1.success")' },
|
|
392
|
+
then: { type: 'object', description: 'Step to execute if condition is true' },
|
|
393
|
+
else: { type: 'object', description: 'Step to execute if condition is false' },
|
|
394
|
+
forEach: { type: 'string', description: 'Array reference to iterate (e.g., "$search.results")' },
|
|
395
|
+
as: { type: 'string', description: 'Variable name for current item in loop' },
|
|
396
|
+
do: { type: 'object', description: 'Step to execute for each item' },
|
|
397
|
+
onError: {
|
|
398
|
+
type: 'object',
|
|
399
|
+
description: 'Error handling configuration',
|
|
400
|
+
properties: {
|
|
401
|
+
retry: {
|
|
402
|
+
type: 'object',
|
|
403
|
+
properties: {
|
|
404
|
+
attempts: { type: 'number', description: 'Max retry attempts' },
|
|
405
|
+
backoff: { type: 'string', description: '"exponential" or "linear" or array of ms delays' }
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
fallback: { type: 'object', description: 'Fallback step if this fails' },
|
|
409
|
+
abort: { type: 'boolean', description: 'Abort entire chain on failure' }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
// Chain options
|
|
416
|
+
continueOnError: {
|
|
417
|
+
type: 'boolean',
|
|
418
|
+
description: 'Continue chain execution even if a step fails (default: false)'
|
|
419
|
+
},
|
|
420
|
+
timeout: {
|
|
421
|
+
type: 'number',
|
|
422
|
+
description: 'Maximum execution time for the entire chain in milliseconds'
|
|
423
|
+
},
|
|
424
|
+
async: {
|
|
425
|
+
type: 'boolean',
|
|
426
|
+
description: 'Return immediately with chainId. Use get_chain_status to poll or provide webhook.'
|
|
427
|
+
},
|
|
428
|
+
webhook: {
|
|
429
|
+
type: 'string',
|
|
430
|
+
description: 'URL to POST results when async chain completes'
|
|
350
431
|
}
|
|
351
432
|
},
|
|
352
|
-
required: [
|
|
433
|
+
required: []
|
|
353
434
|
}
|
|
354
435
|
},
|
|
355
436
|
{
|
|
@@ -485,6 +566,46 @@ const tools: Tool[] = [
|
|
|
485
566
|
},
|
|
486
567
|
required: ['call_count']
|
|
487
568
|
}
|
|
569
|
+
},
|
|
570
|
+
// ============================================
|
|
571
|
+
// CHAIN MANAGEMENT TOOLS
|
|
572
|
+
// ============================================
|
|
573
|
+
{
|
|
574
|
+
name: 'get_chain_status',
|
|
575
|
+
description: 'Check the status of an async chain execution. Use the chainId returned from call_api with async: true.',
|
|
576
|
+
inputSchema: {
|
|
577
|
+
type: 'object',
|
|
578
|
+
properties: {
|
|
579
|
+
chain_id: {
|
|
580
|
+
type: 'string',
|
|
581
|
+
description: 'Chain ID returned from async chain execution'
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
required: ['chain_id']
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
name: 'resume_chain',
|
|
589
|
+
description: 'Resume a failed chain from the point of failure. Use the resumeToken from the error response. Requires the original chain definition.',
|
|
590
|
+
inputSchema: {
|
|
591
|
+
type: 'object',
|
|
592
|
+
properties: {
|
|
593
|
+
resume_token: {
|
|
594
|
+
type: 'string',
|
|
595
|
+
description: 'Resume token from a failed chain (e.g., "chain_xyz_step_2")'
|
|
596
|
+
},
|
|
597
|
+
original_chain: {
|
|
598
|
+
type: 'array',
|
|
599
|
+
description: 'The original chain definition that failed. Required to resume execution.',
|
|
600
|
+
items: { type: 'object' }
|
|
601
|
+
},
|
|
602
|
+
overrides: {
|
|
603
|
+
type: 'object',
|
|
604
|
+
description: 'Optional parameter overrides for specific steps. Format: { "stepId": { ...newParams } }'
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
required: ['resume_token', 'original_chain']
|
|
608
|
+
}
|
|
488
609
|
}
|
|
489
610
|
];
|
|
490
611
|
|
|
@@ -775,6 +896,130 @@ Docs: https://apiclaw.nordsym.com
|
|
|
775
896
|
const params = (args?.params as Record<string, any>) || {};
|
|
776
897
|
const confirmToken = args?.confirm_token as string | undefined;
|
|
777
898
|
const dryRun = args?.dry_run as boolean | undefined;
|
|
899
|
+
const chain = args?.chain as ChainStepUnion[] | undefined;
|
|
900
|
+
|
|
901
|
+
// ============================================
|
|
902
|
+
// CHAIN EXECUTION MODE
|
|
903
|
+
// ============================================
|
|
904
|
+
if (chain && Array.isArray(chain) && chain.length > 0) {
|
|
905
|
+
// Check workspace access for chains
|
|
906
|
+
const access = checkWorkspaceAccess();
|
|
907
|
+
if (!access.allowed) {
|
|
908
|
+
return {
|
|
909
|
+
content: [{
|
|
910
|
+
type: 'text',
|
|
911
|
+
text: JSON.stringify({
|
|
912
|
+
status: 'error',
|
|
913
|
+
error: access.error,
|
|
914
|
+
hint: 'Use register_owner to authenticate your workspace.',
|
|
915
|
+
}, null, 2)
|
|
916
|
+
}],
|
|
917
|
+
isError: true
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
try {
|
|
922
|
+
// Construct ChainDefinition from the input
|
|
923
|
+
const chainDefinition: ChainDefinition = {
|
|
924
|
+
steps: chain as ChainStepUnion[],
|
|
925
|
+
timeout: args?.timeout as number | undefined,
|
|
926
|
+
errorPolicy: args?.continueOnError
|
|
927
|
+
? { mode: 'best-effort' as const }
|
|
928
|
+
: { mode: 'fail-fast' as const },
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
const chainCredentials: ChainCredentials = {
|
|
932
|
+
userId: DEFAULT_AGENT_ID,
|
|
933
|
+
customerKeys: {},
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
// Add customer key if provided
|
|
937
|
+
const customerKey = args?.customer_key as string | undefined;
|
|
938
|
+
if (customerKey) {
|
|
939
|
+
// Apply to all providers (or could be provider-specific)
|
|
940
|
+
chainCredentials.customerKeys = { default: customerKey };
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const chainOptions: ChainOptions = {
|
|
944
|
+
verbose: false,
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
// Execute the chain
|
|
948
|
+
const chainResult = await executeChain(
|
|
949
|
+
chainDefinition,
|
|
950
|
+
chainCredentials,
|
|
951
|
+
{}, // inputs
|
|
952
|
+
chainOptions
|
|
953
|
+
);
|
|
954
|
+
|
|
955
|
+
// Track usage for chain (count completed steps)
|
|
956
|
+
if (chainResult.success && workspaceContext) {
|
|
957
|
+
const completedCount = chainResult.completedSteps.length;
|
|
958
|
+
|
|
959
|
+
for (let i = 0; i < completedCount; i++) {
|
|
960
|
+
try {
|
|
961
|
+
await convex.mutation("workspaces:incrementUsage" as any, {
|
|
962
|
+
workspaceId: workspaceContext.workspaceId as any,
|
|
963
|
+
});
|
|
964
|
+
} catch (e) {
|
|
965
|
+
console.error('[APIClaw] Failed to track chain usage:', e);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// Format response to match expected chain response format
|
|
971
|
+
return {
|
|
972
|
+
content: [{
|
|
973
|
+
type: 'text',
|
|
974
|
+
text: JSON.stringify({
|
|
975
|
+
status: chainResult.success ? 'success' : 'error',
|
|
976
|
+
mode: 'chain',
|
|
977
|
+
chainId: chainResult.chainId,
|
|
978
|
+
steps: chainResult.trace.map(t => ({
|
|
979
|
+
id: t.stepId,
|
|
980
|
+
status: t.success ? 'completed' : 'failed',
|
|
981
|
+
result: t.output,
|
|
982
|
+
error: t.error,
|
|
983
|
+
latencyMs: t.latencyMs,
|
|
984
|
+
cost: t.cost,
|
|
985
|
+
})),
|
|
986
|
+
finalResult: chainResult.finalResult,
|
|
987
|
+
totalLatencyMs: chainResult.totalLatencyMs,
|
|
988
|
+
totalCost: chainResult.totalCost,
|
|
989
|
+
tokensSaved: (chain.length - 1) * 500, // Estimate tokens saved
|
|
990
|
+
...(chainResult.error ? {
|
|
991
|
+
completedSteps: chainResult.completedSteps,
|
|
992
|
+
failedStep: chainResult.failedStep ? {
|
|
993
|
+
id: chainResult.failedStep.stepId,
|
|
994
|
+
error: chainResult.failedStep.error,
|
|
995
|
+
code: chainResult.failedStep.errorCode,
|
|
996
|
+
} : undefined,
|
|
997
|
+
partialResults: chainResult.results,
|
|
998
|
+
canResume: chainResult.canResume,
|
|
999
|
+
resumeToken: chainResult.resumeToken,
|
|
1000
|
+
} : {}),
|
|
1001
|
+
}, null, 2)
|
|
1002
|
+
}],
|
|
1003
|
+
isError: !chainResult.success
|
|
1004
|
+
};
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
return {
|
|
1007
|
+
content: [{
|
|
1008
|
+
type: 'text',
|
|
1009
|
+
text: JSON.stringify({
|
|
1010
|
+
status: 'error',
|
|
1011
|
+
mode: 'chain',
|
|
1012
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1013
|
+
}, null, 2)
|
|
1014
|
+
}],
|
|
1015
|
+
isError: true
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// ============================================
|
|
1021
|
+
// SINGLE CALL MODE (existing logic)
|
|
1022
|
+
// ============================================
|
|
778
1023
|
|
|
779
1024
|
// Handle dry-run mode - no actual API calls, just show what would happen
|
|
780
1025
|
if (dryRun) {
|
|
@@ -1484,6 +1729,182 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1484
1729
|
};
|
|
1485
1730
|
}
|
|
1486
1731
|
|
|
1732
|
+
// ============================================
|
|
1733
|
+
// CHAIN MANAGEMENT TOOLS
|
|
1734
|
+
// ============================================
|
|
1735
|
+
|
|
1736
|
+
case 'get_chain_status': {
|
|
1737
|
+
const chainId = args?.chain_id as string;
|
|
1738
|
+
|
|
1739
|
+
if (!chainId) {
|
|
1740
|
+
return {
|
|
1741
|
+
content: [{
|
|
1742
|
+
type: 'text',
|
|
1743
|
+
text: JSON.stringify({
|
|
1744
|
+
status: 'error',
|
|
1745
|
+
error: 'chain_id is required'
|
|
1746
|
+
}, null, 2)
|
|
1747
|
+
}],
|
|
1748
|
+
isError: true
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
const chainStatus = await getChainStatus(chainId);
|
|
1753
|
+
|
|
1754
|
+
if (chainStatus.status === 'not_found') {
|
|
1755
|
+
return {
|
|
1756
|
+
content: [{
|
|
1757
|
+
type: 'text',
|
|
1758
|
+
text: JSON.stringify({
|
|
1759
|
+
status: 'error',
|
|
1760
|
+
error: `Chain not found: ${chainId}`,
|
|
1761
|
+
hint: 'Chain states expire after 1 hour. The chain may have completed or expired.'
|
|
1762
|
+
}, null, 2)
|
|
1763
|
+
}],
|
|
1764
|
+
isError: true
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
return {
|
|
1769
|
+
content: [{
|
|
1770
|
+
type: 'text',
|
|
1771
|
+
text: JSON.stringify({
|
|
1772
|
+
status: 'success',
|
|
1773
|
+
chain: {
|
|
1774
|
+
chainId: chainStatus.chainId,
|
|
1775
|
+
executionStatus: chainStatus.status,
|
|
1776
|
+
...(chainStatus.result ? {
|
|
1777
|
+
result: {
|
|
1778
|
+
success: chainStatus.result.success,
|
|
1779
|
+
completedSteps: chainStatus.result.completedSteps,
|
|
1780
|
+
totalLatencyMs: chainStatus.result.totalLatencyMs,
|
|
1781
|
+
totalCost: chainStatus.result.totalCost,
|
|
1782
|
+
finalResult: chainStatus.result.finalResult,
|
|
1783
|
+
error: chainStatus.result.error,
|
|
1784
|
+
canResume: chainStatus.result.canResume,
|
|
1785
|
+
resumeToken: chainStatus.result.resumeToken,
|
|
1786
|
+
}
|
|
1787
|
+
} : {})
|
|
1788
|
+
}
|
|
1789
|
+
}, null, 2)
|
|
1790
|
+
}]
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
case 'resume_chain': {
|
|
1795
|
+
const resumeToken = args?.resume_token as string;
|
|
1796
|
+
const overrides = args?.overrides as Record<string, Record<string, any>> | undefined;
|
|
1797
|
+
const originalChain = args?.original_chain as ChainStepUnion[] | undefined;
|
|
1798
|
+
|
|
1799
|
+
if (!resumeToken) {
|
|
1800
|
+
return {
|
|
1801
|
+
content: [{
|
|
1802
|
+
type: 'text',
|
|
1803
|
+
text: JSON.stringify({
|
|
1804
|
+
status: 'error',
|
|
1805
|
+
error: 'resume_token is required'
|
|
1806
|
+
}, null, 2)
|
|
1807
|
+
}],
|
|
1808
|
+
isError: true
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
// Check workspace access
|
|
1813
|
+
const access = checkWorkspaceAccess();
|
|
1814
|
+
if (!access.allowed) {
|
|
1815
|
+
return {
|
|
1816
|
+
content: [{
|
|
1817
|
+
type: 'text',
|
|
1818
|
+
text: JSON.stringify({
|
|
1819
|
+
status: 'error',
|
|
1820
|
+
error: access.error,
|
|
1821
|
+
hint: 'Use register_owner to authenticate your workspace.',
|
|
1822
|
+
}, null, 2)
|
|
1823
|
+
}],
|
|
1824
|
+
isError: true
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
try {
|
|
1829
|
+
// Note: The resume_chain function requires the original chain definition
|
|
1830
|
+
// In practice, you'd store this or require the caller to provide it
|
|
1831
|
+
if (!originalChain) {
|
|
1832
|
+
return {
|
|
1833
|
+
content: [{
|
|
1834
|
+
type: 'text',
|
|
1835
|
+
text: JSON.stringify({
|
|
1836
|
+
status: 'error',
|
|
1837
|
+
error: 'original_chain is required to resume. Please provide the original chain definition.',
|
|
1838
|
+
hint: 'Pass original_chain: [...] with the same chain array used in the failed execution.'
|
|
1839
|
+
}, null, 2)
|
|
1840
|
+
}],
|
|
1841
|
+
isError: true
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
const chainDefinition: ChainDefinition = {
|
|
1846
|
+
steps: originalChain,
|
|
1847
|
+
};
|
|
1848
|
+
|
|
1849
|
+
const chainCredentials: ChainCredentials = {
|
|
1850
|
+
userId: DEFAULT_AGENT_ID,
|
|
1851
|
+
customerKeys: {},
|
|
1852
|
+
};
|
|
1853
|
+
|
|
1854
|
+
const customerKey = args?.customer_key as string | undefined;
|
|
1855
|
+
if (customerKey) {
|
|
1856
|
+
chainCredentials.customerKeys = { default: customerKey };
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
const result = await resumeChain(
|
|
1860
|
+
resumeToken,
|
|
1861
|
+
chainDefinition,
|
|
1862
|
+
chainCredentials,
|
|
1863
|
+
{}, // inputs
|
|
1864
|
+
overrides,
|
|
1865
|
+
{ verbose: false }
|
|
1866
|
+
);
|
|
1867
|
+
|
|
1868
|
+
return {
|
|
1869
|
+
content: [{
|
|
1870
|
+
type: 'text',
|
|
1871
|
+
text: JSON.stringify({
|
|
1872
|
+
status: result.success ? 'success' : 'error',
|
|
1873
|
+
mode: 'chain_resumed',
|
|
1874
|
+
chainId: result.chainId,
|
|
1875
|
+
steps: result.trace.map(t => ({
|
|
1876
|
+
id: t.stepId,
|
|
1877
|
+
status: t.success ? 'completed' : 'failed',
|
|
1878
|
+
result: t.output,
|
|
1879
|
+
error: t.error,
|
|
1880
|
+
latencyMs: t.latencyMs,
|
|
1881
|
+
})),
|
|
1882
|
+
finalResult: result.finalResult,
|
|
1883
|
+
totalLatencyMs: result.totalLatencyMs,
|
|
1884
|
+
totalCost: result.totalCost,
|
|
1885
|
+
...(result.error ? {
|
|
1886
|
+
error: result.error,
|
|
1887
|
+
canResume: result.canResume,
|
|
1888
|
+
resumeToken: result.resumeToken,
|
|
1889
|
+
} : {}),
|
|
1890
|
+
}, null, 2)
|
|
1891
|
+
}],
|
|
1892
|
+
isError: !result.success
|
|
1893
|
+
};
|
|
1894
|
+
} catch (error) {
|
|
1895
|
+
return {
|
|
1896
|
+
content: [{
|
|
1897
|
+
type: 'text',
|
|
1898
|
+
text: JSON.stringify({
|
|
1899
|
+
status: 'error',
|
|
1900
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1901
|
+
}, null, 2)
|
|
1902
|
+
}],
|
|
1903
|
+
isError: true
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1487
1908
|
default:
|
|
1488
1909
|
return {
|
|
1489
1910
|
content: [
|