@agent-native/core 0.49.25 → 0.49.26

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.
Files changed (60) hide show
  1. package/dist/agent/production-agent.d.ts.map +1 -1
  2. package/dist/agent/production-agent.js +8 -1
  3. package/dist/agent/production-agent.js.map +1 -1
  4. package/dist/cli/recap.d.ts.map +1 -1
  5. package/dist/cli/recap.js +43 -11
  6. package/dist/cli/recap.js.map +1 -1
  7. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  8. package/dist/client/agent-chat-adapter.js +2 -1
  9. package/dist/client/agent-chat-adapter.js.map +1 -1
  10. package/dist/client/blocks/library/AnnotatedCodeBlock.js +7 -7
  11. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  12. package/dist/client/blocks/library/DiffBlock.js +3 -3
  13. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  14. package/dist/client/blocks/library/annotation-rail.d.ts +4 -3
  15. package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
  16. package/dist/client/blocks/library/annotation-rail.js +16 -9
  17. package/dist/client/blocks/library/annotation-rail.js.map +1 -1
  18. package/dist/client/blocks/types.d.ts +2 -2
  19. package/dist/client/blocks/types.js.map +1 -1
  20. package/dist/coding-tools/run-code.d.ts.map +1 -1
  21. package/dist/coding-tools/run-code.js +198 -15
  22. package/dist/coding-tools/run-code.js.map +1 -1
  23. package/dist/extensions/fetch-tool.js +1 -1
  24. package/dist/extensions/fetch-tool.js.map +1 -1
  25. package/dist/mcp/build-server.d.ts.map +1 -1
  26. package/dist/mcp/build-server.js +1 -0
  27. package/dist/mcp/build-server.js.map +1 -1
  28. package/dist/mcp/builtin-tools.d.ts +8 -4
  29. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  30. package/dist/mcp/builtin-tools.js +247 -13
  31. package/dist/mcp/builtin-tools.js.map +1 -1
  32. package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -1
  33. package/dist/provider-api/actions/query-staged-dataset.js +1 -0
  34. package/dist/provider-api/actions/query-staged-dataset.js.map +1 -1
  35. package/dist/provider-api/index.d.ts +9 -4
  36. package/dist/provider-api/index.d.ts.map +1 -1
  37. package/dist/provider-api/index.js +164 -33
  38. package/dist/provider-api/index.js.map +1 -1
  39. package/dist/provider-api/staged-datasets-store.d.ts.map +1 -1
  40. package/dist/provider-api/staged-datasets-store.js +29 -6
  41. package/dist/provider-api/staged-datasets-store.js.map +1 -1
  42. package/dist/provider-api/staging.d.ts +6 -1
  43. package/dist/provider-api/staging.d.ts.map +1 -1
  44. package/dist/provider-api/staging.js +35 -6
  45. package/dist/provider-api/staging.js.map +1 -1
  46. package/dist/server/agent-chat-plugin.d.ts +1 -1
  47. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  48. package/dist/server/agent-chat-plugin.js +157 -80
  49. package/dist/server/agent-chat-plugin.js.map +1 -1
  50. package/dist/server/prompts/shared-rules.d.ts +1 -1
  51. package/dist/server/prompts/shared-rules.d.ts.map +1 -1
  52. package/dist/server/prompts/shared-rules.js +5 -7
  53. package/dist/server/prompts/shared-rules.js.map +1 -1
  54. package/dist/server/schema-prompt.js +1 -1
  55. package/dist/server/schema-prompt.js.map +1 -1
  56. package/dist/templates/default/.agents/skills/actions/SKILL.md +16 -4
  57. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +16 -4
  58. package/package.json +1 -1
  59. package/src/templates/default/.agents/skills/actions/SKILL.md +16 -4
  60. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +16 -4
@@ -14,7 +14,8 @@
14
14
  * | `list_apps` | none | `{ apps: [{ id, url, running }] }` |
15
15
  * | `open_app` | none | `{ url }` (+ deep-link `link`) |
16
16
  * | `create_embed_session`| ticket mint | `{ startUrl }` for MCP App iframes |
17
- * | `ask_app` | agent loop | `{ app, routedVia, response }` |
17
+ * | `ask_app` | agent loop | `{ app, routedVia, response }` or task |
18
+ * | `ask_app_status` | none | poll a durable `ask_app` task |
18
19
  * | `create_workspace_app`| scaffolds | `{ name, url, port, deepLink }` (+ link) |
19
20
  *
20
21
  * `open_app` / `create_workspace_app` return an **absolute** URL on the
@@ -31,12 +32,15 @@
31
32
  */
32
33
  import type { ActionEntry } from "../agent/production-agent.js";
33
34
  import type { MCPConfig } from "./build-server.js";
35
+ type AskAppRequestMeta = {
36
+ origin?: string;
37
+ basePath?: string;
38
+ };
34
39
  /**
35
40
  * Build the generic cross-app builtin tool registry. Called by
36
41
  * `createMCPServerForRequest`; the result is merged UNDER the config's
37
42
  * actions so template actions of the same name win.
38
43
  */
39
- export declare function getBuiltinCrossAppTools(config: MCPConfig, requestMeta?: {
40
- origin?: string;
41
- }): Record<string, ActionEntry>;
44
+ export declare function getBuiltinCrossAppTools(config: MCPConfig, requestMeta?: AskAppRequestMeta): Record<string, ActionEntry>;
45
+ export {};
42
46
  //# sourceMappingURL=builtin-tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builtin-tools.d.ts","sourceRoot":"","sources":["../../src/mcp/builtin-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAIhE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAmxBnD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,WAAW,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAS7B"}
1
+ {"version":3,"file":"builtin-tools.d.ts","sourceRoot":"","sources":["../../src/mcp/builtin-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAIhE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAuDnD,KAAK,iBAAiB,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA8iChE;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,WAAW,CAAC,EAAE,iBAAiB,GAC9B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAU7B"}
@@ -14,7 +14,8 @@
14
14
  * | `list_apps` | none | `{ apps: [{ id, url, running }] }` |
15
15
  * | `open_app` | none | `{ url }` (+ deep-link `link`) |
16
16
  * | `create_embed_session`| ticket mint | `{ startUrl }` for MCP App iframes |
17
- * | `ask_app` | agent loop | `{ app, routedVia, response }` |
17
+ * | `ask_app` | agent loop | `{ app, routedVia, response }` or task |
18
+ * | `ask_app_status` | none | poll a durable `ask_app` task |
18
19
  * | `create_workspace_app`| scaffolds | `{ name, url, port, deepLink }` (+ link) |
19
20
  *
20
21
  * `open_app` / `create_workspace_app` return an **absolute** URL on the
@@ -61,6 +62,15 @@ function currentAppId(config) {
61
62
  return (config.appId || config.name || "app").toLowerCase();
62
63
  }
63
64
  const CONTROL_CHARS = new RegExp("[\\u0000-\\u001f\\u007f]");
65
+ const ASK_APP_DEFAULT_INLINE_WAIT_MS = 20_000;
66
+ const ASK_APP_MAX_INLINE_WAIT_MS = 25_000;
67
+ const ASK_APP_POLL_INTERVAL_MS = 1_500;
68
+ const ASK_APP_A2A_REQUEST_TIMEOUT_MS = 10_000;
69
+ const ASK_APP_TERMINAL_STATES = new Set([
70
+ "completed",
71
+ "failed",
72
+ "canceled",
73
+ ]);
64
74
  function safeAppPath(raw) {
65
75
  if (typeof raw !== "string" || !raw.trim())
66
76
  return null;
@@ -106,6 +116,140 @@ function withMcpChatBridgeParam(path) {
106
116
  return path;
107
117
  }
108
118
  }
119
+ function agentNativeA2AEndpoint(urlOrOrigin) {
120
+ const value = urlOrOrigin.replace(/\/+$/, "");
121
+ try {
122
+ const parsed = new URL(value);
123
+ const pathname = parsed.pathname.replace(/\/+$/, "");
124
+ if (pathname.endsWith("/_agent-native/a2a") || pathname.endsWith("/a2a")) {
125
+ return value;
126
+ }
127
+ }
128
+ catch {
129
+ // Fall through and append the conventional Agent Native endpoint.
130
+ }
131
+ return `${value}/_agent-native/a2a`;
132
+ }
133
+ function selfA2AEndpointUrl(requestMeta) {
134
+ const origin = requestMeta?.origin?.replace(/\/+$/, "");
135
+ if (!origin)
136
+ return null;
137
+ const basePath = requestMeta?.basePath || getConfiguredAppBasePath();
138
+ return agentNativeA2AEndpoint(`${origin}${basePath}`);
139
+ }
140
+ function boundedAskAppWaitMs(raw) {
141
+ if (raw == null || raw === "")
142
+ return ASK_APP_DEFAULT_INLINE_WAIT_MS;
143
+ const parsed = Number(raw);
144
+ if (!Number.isFinite(parsed))
145
+ return ASK_APP_DEFAULT_INLINE_WAIT_MS;
146
+ return Math.max(0, Math.min(ASK_APP_MAX_INLINE_WAIT_MS, Math.trunc(parsed)));
147
+ }
148
+ function isExplicitAsyncAsk(raw) {
149
+ return raw === true || raw === "true" || raw === 1 || raw === "1";
150
+ }
151
+ function taskState(task) {
152
+ return String(task.status?.state ?? "unknown");
153
+ }
154
+ function isTerminalTask(task) {
155
+ return ASK_APP_TERMINAL_STATES.has(taskState(task));
156
+ }
157
+ function taskText(task) {
158
+ return (task.status.message?.parts
159
+ ?.filter((part) => part.type === "text")
160
+ .map((part) => part.text)
161
+ .join("\n")
162
+ .trim() ?? "");
163
+ }
164
+ function askAppTaskResult(route, task) {
165
+ const status = taskState(task);
166
+ const response = taskText(task);
167
+ const base = {
168
+ app: route.app,
169
+ routedVia: route.routedVia,
170
+ taskId: task.id,
171
+ status,
172
+ ...(route.note ? { note: route.note } : {}),
173
+ };
174
+ if (status === "completed") {
175
+ return {
176
+ ...base,
177
+ response: response || "(no response)",
178
+ };
179
+ }
180
+ if (status === "failed" || status === "canceled") {
181
+ return {
182
+ ...base,
183
+ ...(response ? { response } : {}),
184
+ error: response || `ask_app task ${status}.`,
185
+ };
186
+ }
187
+ return {
188
+ ...base,
189
+ poll: {
190
+ tool: "ask_app_status",
191
+ arguments: { app: route.app, taskId: task.id },
192
+ },
193
+ message: `ask_app is still ${status}. Call ask_app_status with ` +
194
+ `taskId "${task.id}" to retrieve the final response.`,
195
+ };
196
+ }
197
+ async function createA2AClientForAskApp(origin) {
198
+ const { A2AClient } = await import("../a2a/client.js");
199
+ const { resolveA2ACallerAuth } = await import("../a2a/caller-auth.js");
200
+ const auth = await resolveA2ACallerAuth();
201
+ const metadata = {};
202
+ if (auth.userEmail)
203
+ metadata.userEmail = auth.userEmail;
204
+ if (auth.orgDomain)
205
+ metadata.orgDomain = auth.orgDomain;
206
+ return {
207
+ client: new A2AClient(origin, auth.apiKey, {
208
+ requestTimeoutMs: ASK_APP_A2A_REQUEST_TIMEOUT_MS,
209
+ }),
210
+ metadata,
211
+ };
212
+ }
213
+ async function waitForA2ATask(client, initialTask, maxWaitMs) {
214
+ if (maxWaitMs <= 0 || isTerminalTask(initialTask))
215
+ return initialTask;
216
+ const deadline = Date.now() + maxWaitMs;
217
+ let current = initialTask;
218
+ while (!isTerminalTask(current)) {
219
+ const remaining = deadline - Date.now();
220
+ if (remaining <= 0)
221
+ return current;
222
+ await new Promise((resolve) => setTimeout(resolve, Math.min(ASK_APP_POLL_INTERVAL_MS, remaining)));
223
+ try {
224
+ current = await client.getTask(initialTask.id);
225
+ }
226
+ catch {
227
+ // Transient status fetch failures should not turn a successfully
228
+ // submitted durable task into a failed MCP call.
229
+ if (Date.now() >= deadline)
230
+ return current;
231
+ continue;
232
+ }
233
+ }
234
+ return current;
235
+ }
236
+ async function submitAskAppA2ATask(route, message, maxWaitMs) {
237
+ const { client, metadata } = await createA2AClientForAskApp(route.origin);
238
+ const task = await client.send({
239
+ role: "user",
240
+ parts: [{ type: "text", text: message }],
241
+ }, {
242
+ async: true,
243
+ metadata,
244
+ });
245
+ const finalOrRunning = await waitForA2ATask(client, task, maxWaitMs);
246
+ return askAppTaskResult(route, finalOrRunning);
247
+ }
248
+ async function fetchAskAppA2ATask(route, taskId) {
249
+ const { client } = await createA2AClientForAskApp(route.origin);
250
+ const task = await client.getTask(taskId);
251
+ return askAppTaskResult(route, task);
252
+ }
109
253
  /**
110
254
  * Resolve the absolute origin of a *target* workspace app (e.g.
111
255
  * `http://127.0.0.1:8101`) so cross-app deep links / A2A calls point at the
@@ -457,7 +601,10 @@ function createEmbedSessionTool(requestMeta) {
457
601
  * Throws on failure so the caller can be honest — it never falls back to this
458
602
  * app's agent and pretends it was the target.
459
603
  */
460
- async function routeAskOverA2A(origin, id, message) {
604
+ async function routeAskOverA2A(origin, id, message, options) {
605
+ if (options?.durable) {
606
+ return submitAskAppA2ATask({ app: id, origin: agentNativeA2AEndpoint(origin), routedVia: "a2a" }, message, options.maxWaitMs ?? ASK_APP_DEFAULT_INLINE_WAIT_MS);
607
+ }
461
608
  const { callAgent } = await import("../a2a/client.js");
462
609
  const { resolveA2ACallerAuth } = await import("../a2a/caller-auth.js");
463
610
  // The MCP handler runs inside `runWithRequestContext`, so this is the
@@ -475,17 +622,48 @@ async function routeAskOverA2A(origin, id, message) {
475
622
  });
476
623
  return { app: id, routedVia: "a2a", response };
477
624
  }
625
+ async function resolveAskAppStatusRoute(config, requestedApp, requestMeta) {
626
+ const selfId = currentAppId(config);
627
+ const normalized = requestedApp.trim().toLowerCase();
628
+ const selfEndpointUrl = selfA2AEndpointUrl(requestMeta);
629
+ if (!normalized || normalized === selfId) {
630
+ if (!selfEndpointUrl) {
631
+ throw new Error("ask_app_status requires a running app origin for local tasks.");
632
+ }
633
+ return { app: selfId, origin: selfEndpointUrl, routedVia: "local" };
634
+ }
635
+ const targetApp = await resolveTargetAppOrigin(config, requestedApp);
636
+ if (targetApp) {
637
+ return {
638
+ app: targetApp.id,
639
+ origin: agentNativeA2AEndpoint(targetApp.origin),
640
+ routedVia: "a2a",
641
+ };
642
+ }
643
+ const orgApps = await fetchOrgApps({ selfId }).catch(() => []);
644
+ const dirMatch = orgApps.find((a) => a.id === normalized);
645
+ if (dirMatch) {
646
+ return {
647
+ app: dirMatch.id,
648
+ origin: agentNativeA2AEndpoint(dirMatch.a2aUrl),
649
+ routedVia: "a2a",
650
+ };
651
+ }
652
+ throw new Error(`No reachable ask_app task route for app "${requestedApp}".`);
653
+ }
478
654
  // ---------------------------------------------------------------------------
479
655
  // ask_app
480
656
  // ---------------------------------------------------------------------------
481
- function askAppTool(config) {
657
+ function askAppTool(config, requestMeta) {
482
658
  return {
483
659
  tool: tool("Send a natural-language message to an app's AI agent and get its " +
484
660
  "response. Use for complex, multi-step tasks needing the agent's " +
485
661
  "reasoning and full app context. In a single-app project the 'app' " +
486
662
  "param is optional (defaults to this app). When 'app' names a " +
487
663
  "different workspace app it is routed there over A2A; the result's " +
488
- "'routedVia' field reports whether it ran cross-app or locally.", {
664
+ "'routedVia' field reports whether it ran cross-app or locally. " +
665
+ "On hosted MCP, long tasks may return a durable taskId instead of a " +
666
+ "final response; call ask_app_status with that taskId until completed.", {
489
667
  app: {
490
668
  type: "string",
491
669
  description: "App id to route to (optional in a single-app project)",
@@ -494,6 +672,14 @@ function askAppTool(config) {
494
672
  type: "string",
495
673
  description: "The message to send to the app's agent",
496
674
  },
675
+ async: {
676
+ type: "boolean",
677
+ description: "When true, start a durable task and return immediately with a taskId.",
678
+ },
679
+ maxWaitMs: {
680
+ type: "number",
681
+ description: "Maximum time to wait inline before returning a taskId. Hosted MCP clamps this to 25000ms.",
682
+ },
497
683
  }, ["message"]),
498
684
  run: async (args) => {
499
685
  const message = String(args.message ?? "").trim();
@@ -501,6 +687,10 @@ function askAppTool(config) {
501
687
  throw new Error("ask_app requires a 'message'.");
502
688
  const requestedApp = String(args.app ?? "").trim();
503
689
  const selfId = currentAppId(config);
690
+ const useDurableA2A = Boolean(requestMeta?.origin);
691
+ const maxWaitMs = isExplicitAsyncAsk(args.async)
692
+ ? 0
693
+ : boundedAskAppWaitMs(args.maxWaitMs);
504
694
  // Cross-app: the caller named a *different* workspace app. Route the
505
695
  // message to THAT app's agent over A2A (its `/_agent-native/a2a`
506
696
  // endpoint runs the real agent loop with JWT identity) rather than
@@ -508,7 +698,10 @@ function askAppTool(config) {
508
698
  const targetApp = await resolveTargetAppOrigin(config, requestedApp);
509
699
  if (targetApp) {
510
700
  try {
511
- return await routeAskOverA2A(targetApp.origin, targetApp.id, message);
701
+ return await routeAskOverA2A(targetApp.origin, targetApp.id, message, {
702
+ durable: useDurableA2A,
703
+ maxWaitMs,
704
+ });
512
705
  }
513
706
  catch (err) {
514
707
  // Be honest: routing was attempted and failed — do NOT fall back to
@@ -527,7 +720,10 @@ function askAppTool(config) {
527
720
  const dirMatch = orgApps.find((a) => a.id === requestedApp.toLowerCase());
528
721
  if (dirMatch) {
529
722
  try {
530
- return await routeAskOverA2A(dirMatch.a2aUrl, dirMatch.id, message);
723
+ return await routeAskOverA2A(dirMatch.a2aUrl, dirMatch.id, message, {
724
+ durable: useDurableA2A,
725
+ maxWaitMs,
726
+ });
531
727
  }
532
728
  catch (err) {
533
729
  throw new Error(`Failed to route ask_app to "${dirMatch.id}" via A2A ` +
@@ -544,21 +740,58 @@ function askAppTool(config) {
544
740
  // If the caller named an app we couldn't route to (unknown id, or no
545
741
  // workspace), say so honestly instead of claiming we reached it.
546
742
  const unresolved = !!requestedApp && requestedApp.toLowerCase() !== selfId;
743
+ const note = unresolved
744
+ ? `Requested app "${requestedApp}" is not a reachable workspace ` +
745
+ `app; answered with this app ("${selfId}") instead.`
746
+ : undefined;
747
+ // Hosted MCP cannot safely keep a JSON request/response open for a full
748
+ // agent loop: serverless gateways can return an inactivity 504 before
749
+ // the result body exists. When we know the running app origin, submit the
750
+ // local ask through the app's durable A2A task path and only wait a
751
+ // short bounded window for fast completions.
752
+ const localA2AEndpointUrl = selfA2AEndpointUrl(requestMeta);
753
+ if (localA2AEndpointUrl) {
754
+ return submitAskAppA2ATask({
755
+ app: selfId,
756
+ origin: localA2AEndpointUrl,
757
+ routedVia: "local",
758
+ ...(note ? { note } : {}),
759
+ }, message, maxWaitMs);
760
+ }
547
761
  const response = await config.askAgent(message);
548
762
  return {
549
763
  app: selfId,
550
764
  routedVia: "local",
551
- ...(unresolved
552
- ? {
553
- note: `Requested app "${requestedApp}" is not a reachable workspace ` +
554
- `app; answered with this app ("${selfId}") instead.`,
555
- }
556
- : {}),
765
+ ...(note ? { note } : {}),
557
766
  response,
558
767
  };
559
768
  },
560
769
  };
561
770
  }
771
+ function askAppStatusTool(config, requestMeta) {
772
+ return {
773
+ tool: tool("Poll a durable ask_app task and return its current status or final response.", {
774
+ app: {
775
+ type: "string",
776
+ description: "App id returned by ask_app. Optional for same-app local tasks.",
777
+ },
778
+ taskId: {
779
+ type: "string",
780
+ description: "The durable task id returned by ask_app.",
781
+ },
782
+ }, ["taskId"]),
783
+ readOnly: true,
784
+ parallelSafe: true,
785
+ run: async (args) => {
786
+ const taskId = String(args.taskId ?? "").trim();
787
+ if (!taskId)
788
+ throw new Error("ask_app_status requires 'taskId'.");
789
+ const requestedApp = String(args.app ?? "").trim();
790
+ const route = await resolveAskAppStatusRoute(config, requestedApp, requestMeta);
791
+ return fetchAskAppA2ATask(route, taskId);
792
+ },
793
+ };
794
+ }
562
795
  // ---------------------------------------------------------------------------
563
796
  // list_templates
564
797
  // ---------------------------------------------------------------------------
@@ -697,7 +930,8 @@ export function getBuiltinCrossAppTools(config, requestMeta) {
697
930
  list_apps: listAppsTool(config, requestMeta),
698
931
  open_app: openAppTool(config, requestMeta),
699
932
  create_embed_session: createEmbedSessionTool(requestMeta),
700
- ask_app: askAppTool(config),
933
+ ask_app: askAppTool(config, requestMeta),
934
+ ask_app_status: askAppStatusTool(config, requestMeta),
701
935
  create_workspace_app: createWorkspaceAppTool(),
702
936
  list_templates: listTemplatesTool(),
703
937
  };