@agent-native/core 0.12.9 → 0.12.11

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 (83) hide show
  1. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  2. package/dist/agent/engine/builder-engine.js +10 -2
  3. package/dist/agent/engine/builder-engine.js.map +1 -1
  4. package/dist/agent/engine/translate-ai-sdk.d.ts +2 -2
  5. package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
  6. package/dist/agent/engine/translate-ai-sdk.js +19 -2
  7. package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
  8. package/dist/agent/engine/types.d.ts +9 -0
  9. package/dist/agent/engine/types.d.ts.map +1 -1
  10. package/dist/agent/engine/types.js.map +1 -1
  11. package/dist/agent/production-agent.d.ts +2 -1
  12. package/dist/agent/production-agent.d.ts.map +1 -1
  13. package/dist/agent/production-agent.js +202 -4
  14. package/dist/agent/production-agent.js.map +1 -1
  15. package/dist/agent/types.d.ts +32 -0
  16. package/dist/agent/types.d.ts.map +1 -1
  17. package/dist/agent/types.js.map +1 -1
  18. package/dist/cli/workspace-dev.js +17 -0
  19. package/dist/cli/workspace-dev.js.map +1 -1
  20. package/dist/client/AssistantChat.d.ts.map +1 -1
  21. package/dist/client/AssistantChat.js +74 -5
  22. package/dist/client/AssistantChat.js.map +1 -1
  23. package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
  24. package/dist/client/NewWorkspaceAppFlow.js +1 -0
  25. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  26. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  27. package/dist/client/agent-chat-adapter.js +147 -7
  28. package/dist/client/agent-chat-adapter.js.map +1 -1
  29. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  30. package/dist/client/composer/TiptapComposer.js +3 -2
  31. package/dist/client/composer/TiptapComposer.js.map +1 -1
  32. package/dist/client/composer/pasted-text.d.ts.map +1 -1
  33. package/dist/client/composer/pasted-text.js +5 -6
  34. package/dist/client/composer/pasted-text.js.map +1 -1
  35. package/dist/client/extensions/ExtensionsSidebarSection.js +4 -4
  36. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  37. package/dist/client/frame.d.ts +8 -0
  38. package/dist/client/frame.d.ts.map +1 -1
  39. package/dist/client/frame.js +34 -0
  40. package/dist/client/frame.js.map +1 -1
  41. package/dist/client/index.d.ts +1 -1
  42. package/dist/client/index.d.ts.map +1 -1
  43. package/dist/client/index.js +1 -1
  44. package/dist/client/index.js.map +1 -1
  45. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  46. package/dist/client/sharing/ShareButton.js +5 -3
  47. package/dist/client/sharing/ShareButton.js.map +1 -1
  48. package/dist/client/sharing/ShareDialog.js +2 -1
  49. package/dist/client/sharing/ShareDialog.js.map +1 -1
  50. package/dist/client/sse-event-processor.d.ts +1 -0
  51. package/dist/client/sse-event-processor.d.ts.map +1 -1
  52. package/dist/client/sse-event-processor.js +15 -37
  53. package/dist/client/sse-event-processor.js.map +1 -1
  54. package/dist/deploy/workspace-deploy.js +4 -0
  55. package/dist/deploy/workspace-deploy.js.map +1 -1
  56. package/dist/extensions/routes.d.ts.map +1 -1
  57. package/dist/extensions/routes.js +10 -1
  58. package/dist/extensions/routes.js.map +1 -1
  59. package/dist/server/auth.d.ts.map +1 -1
  60. package/dist/server/auth.js +51 -0
  61. package/dist/server/auth.js.map +1 -1
  62. package/dist/server/google-oauth.d.ts.map +1 -1
  63. package/dist/server/google-oauth.js +19 -1
  64. package/dist/server/google-oauth.js.map +1 -1
  65. package/dist/shared/index.d.ts +1 -0
  66. package/dist/shared/index.d.ts.map +1 -1
  67. package/dist/shared/index.js +1 -0
  68. package/dist/shared/index.js.map +1 -1
  69. package/dist/shared/oauth-state.d.ts +10 -0
  70. package/dist/shared/oauth-state.d.ts.map +1 -0
  71. package/dist/shared/oauth-state.js +32 -0
  72. package/dist/shared/oauth-state.js.map +1 -0
  73. package/dist/templates/default/app/entry.client.tsx +12 -0
  74. package/dist/templates/default/app/root.tsx +4 -1
  75. package/dist/templates/workspace-core/AGENTS.md +11 -0
  76. package/dist/templates/workspace-root/AGENTS.md +11 -0
  77. package/dist/templates/workspace-root/README.md +7 -1
  78. package/package.json +1 -1
  79. package/src/templates/default/app/entry.client.tsx +12 -0
  80. package/src/templates/default/app/root.tsx +4 -1
  81. package/src/templates/workspace-core/AGENTS.md +11 -0
  82. package/src/templates/workspace-root/AGENTS.md +11 -0
  83. package/src/templates/workspace-root/README.md +7 -1
@@ -297,9 +297,13 @@ export function createPlanModeActionRegistry(actions) {
297
297
  }
298
298
  const MAX_RETRIES = 3;
299
299
  const RETRY_BASE_DELAY_MS = 2000;
300
+ const TOOL_INPUT_ACTIVITY_INTERVAL_MS = 1500;
300
301
  function generateRunId() {
301
302
  return `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
302
303
  }
304
+ function toolInputActivityLabel(toolName) {
305
+ return toolName ? `Preparing ${toolName} action` : "Preparing action input";
306
+ }
303
307
  /** Check if an error is transient and should be retried */
304
308
  function isContextTooLongError(err) {
305
309
  if (!(err instanceof Error))
@@ -429,6 +433,69 @@ export function buildUserContentWithAttachments(opts) {
429
433
  });
430
434
  return userContent;
431
435
  }
436
+ export function structuredHistoryToEngineMessages(history) {
437
+ if (!Array.isArray(history))
438
+ return null;
439
+ const messages = [];
440
+ for (const message of history) {
441
+ if (!message ||
442
+ (message.role !== "user" && message.role !== "assistant") ||
443
+ !Array.isArray(message.content)) {
444
+ continue;
445
+ }
446
+ const content = [];
447
+ for (const part of message.content) {
448
+ if (!part || typeof part !== "object")
449
+ continue;
450
+ if (part.type === "text" && typeof part.text === "string") {
451
+ if (part.text.length > 0) {
452
+ content.push({ type: "text", text: part.text });
453
+ }
454
+ continue;
455
+ }
456
+ if (part.type === "tool-call" && message.role === "assistant") {
457
+ const id = typeof part.id === "string"
458
+ ? part.id
459
+ : typeof part.toolCallId === "string"
460
+ ? part.toolCallId
461
+ : "";
462
+ const name = typeof part.name === "string"
463
+ ? part.name
464
+ : typeof part.toolName === "string"
465
+ ? part.toolName
466
+ : "";
467
+ if (!id || !name)
468
+ continue;
469
+ content.push({
470
+ type: "tool-call",
471
+ id,
472
+ name,
473
+ input: part.input ?? part.args ?? {},
474
+ });
475
+ continue;
476
+ }
477
+ if (part.type === "tool-result" && message.role === "user") {
478
+ if (typeof part.toolCallId !== "string" ||
479
+ typeof part.content !== "string") {
480
+ continue;
481
+ }
482
+ content.push({
483
+ type: "tool-result",
484
+ toolCallId: part.toolCallId,
485
+ ...(typeof part.toolName === "string"
486
+ ? { toolName: part.toolName }
487
+ : {}),
488
+ content: part.content,
489
+ ...(part.isError ? { isError: true } : {}),
490
+ });
491
+ }
492
+ }
493
+ if (content.length > 0) {
494
+ messages.push({ role: message.role, content });
495
+ }
496
+ }
497
+ return messages.length > 0 ? messages : null;
498
+ }
432
499
  /** Build enriched message with file/skill/mention references */
433
500
  function enrichMessage(message, references) {
434
501
  if (references.length === 0)
@@ -487,6 +554,52 @@ export function appendAgentLoopContinuation(messages, reason) {
487
554
  ],
488
555
  });
489
556
  }
557
+ function textFromEngineMessage(message) {
558
+ return message.content
559
+ .filter((part) => part.type === "text")
560
+ .map((part) => part.text)
561
+ .join("\n");
562
+ }
563
+ function isInternalContinuationTurn(messages) {
564
+ for (let i = messages.length - 1; i >= 0; i--) {
565
+ const message = messages[i];
566
+ if (message.role !== "user")
567
+ continue;
568
+ return textFromEngineMessage(message).startsWith(AGENT_INTERNAL_CONTINUE_PROMPT);
569
+ }
570
+ return false;
571
+ }
572
+ function seedReadOnlyToolResultsFromHistory(messages, actions) {
573
+ const cache = new Map();
574
+ if (!isInternalContinuationTurn(messages))
575
+ return cache;
576
+ const pendingToolCalls = new Map();
577
+ for (const message of messages) {
578
+ if (message.role === "assistant") {
579
+ for (const part of message.content) {
580
+ if (part.type !== "tool-call")
581
+ continue;
582
+ const entry = actions[part.name];
583
+ if (entry?.readOnly !== true)
584
+ continue;
585
+ pendingToolCalls.set(part.id, {
586
+ name: part.name,
587
+ input: part.input,
588
+ });
589
+ }
590
+ continue;
591
+ }
592
+ for (const part of message.content) {
593
+ if (part.type !== "tool-result")
594
+ continue;
595
+ const call = pendingToolCalls.get(part.toolCallId);
596
+ if (!call)
597
+ continue;
598
+ cache.set(toolCallCacheKey(call.name, call.input), part.content);
599
+ }
600
+ }
601
+ return cache;
602
+ }
490
603
  /**
491
604
  * Convert ActionEntry registry to EngineTool array.
492
605
  */
@@ -531,6 +644,22 @@ function stringifyToolInput(input) {
531
644
  return String(input);
532
645
  }
533
646
  }
647
+ function stableStringify(value) {
648
+ if (value === null || typeof value !== "object") {
649
+ return JSON.stringify(value);
650
+ }
651
+ if (Array.isArray(value)) {
652
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
653
+ }
654
+ const obj = value;
655
+ return `{${Object.keys(obj)
656
+ .sort()
657
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(obj[key])}`)
658
+ .join(",")}}`;
659
+ }
660
+ function toolCallCacheKey(toolName, input) {
661
+ return `${toolName}:${stableStringify(normalizeToolCallInputForHistory(input))}`;
662
+ }
534
663
  function normalizeToolCallInputForHistory(input) {
535
664
  if (input && typeof input === "object" && !Array.isArray(input)) {
536
665
  return input;
@@ -564,6 +693,8 @@ export async function runAgentLoop(opts) {
564
693
  runCtx.toolCalls = toolCallHistory;
565
694
  runCtx.toolResults = toolResultHistory;
566
695
  }
696
+ const readOnlyToolResultCache = seedReadOnlyToolResultsFromHistory(messages, actions);
697
+ const duplicateReadOnlyToolCalls = new Map();
567
698
  let finalGuardRetries = 0;
568
699
  let iterations = 0;
569
700
  while (true) {
@@ -590,6 +721,21 @@ export async function runAgentLoop(opts) {
590
721
  };
591
722
  const eventStream = engine.stream(streamOpts);
592
723
  let thinkingBuffer = "";
724
+ const toolInputNames = new Map();
725
+ let lastToolInputActivityAt = 0;
726
+ const sendToolInputActivity = (toolName, force = false) => {
727
+ const now = Date.now();
728
+ if (!force &&
729
+ now - lastToolInputActivityAt < TOOL_INPUT_ACTIVITY_INTERVAL_MS) {
730
+ return;
731
+ }
732
+ lastToolInputActivityAt = now;
733
+ send({
734
+ type: "activity",
735
+ label: toolInputActivityLabel(toolName),
736
+ ...(toolName ? { tool: toolName } : {}),
737
+ });
738
+ };
593
739
  for await (const event of eventStream) {
594
740
  if (event.type === "text-delta") {
595
741
  send({ type: "text", text: event.text });
@@ -600,6 +746,17 @@ export async function runAgentLoop(opts) {
600
746
  // we accumulate them. In a future iteration, we can surface
601
747
  // them as a collapsible "reasoning" section in the UI.
602
748
  }
749
+ else if (event.type === "tool-input-start") {
750
+ if (event.id && event.name) {
751
+ toolInputNames.set(event.id, event.name);
752
+ }
753
+ sendToolInputActivity(event.name, true);
754
+ }
755
+ else if (event.type === "tool-input-delta") {
756
+ const toolName = event.name ??
757
+ (event.id ? toolInputNames.get(event.id) : undefined);
758
+ sendToolInputActivity(toolName);
759
+ }
603
760
  else if (event.type === "tool-call") {
604
761
  // The authoritative tool-call blocks arrive in assistant-content.
605
762
  }
@@ -734,6 +891,36 @@ export async function runAgentLoop(opts) {
734
891
  isError: true,
735
892
  };
736
893
  }
894
+ const cacheKey = actionEntry.readOnly === true
895
+ ? toolCallCacheKey(toolCall.name, toolCall.input)
896
+ : null;
897
+ if (cacheKey && readOnlyToolResultCache.has(cacheKey)) {
898
+ const repeats = (duplicateReadOnlyToolCalls.get(cacheKey) ?? 0) + 1;
899
+ duplicateReadOnlyToolCalls.set(cacheKey, repeats);
900
+ const previousResult = readOnlyToolResultCache.get(cacheKey) ?? "";
901
+ const result = `Skipped duplicate read-only call to ${toolCall.name}: identical input already ran in this turn. ` +
902
+ `Use the previous result already in the conversation instead of calling this tool again.\n\n` +
903
+ `Previous result:\n${previousResult}`;
904
+ send({
905
+ type: "tool_start",
906
+ tool: toolCall.name,
907
+ input: toolCall.input,
908
+ });
909
+ send({ type: "tool_done", tool: toolCall.name, result });
910
+ recordToolResult(result, false);
911
+ if (repeats >= 3) {
912
+ requestedActionStop ??= {
913
+ message: "I stopped because the agent kept asking for the same read-only context it already had. Please send the request again if you want me to retry from a fresh turn.",
914
+ errorCode: "duplicate_read_only_tool",
915
+ };
916
+ }
917
+ return {
918
+ type: "tool-result",
919
+ toolCallId: toolCall.id,
920
+ toolName: toolCall.name,
921
+ content: result,
922
+ };
923
+ }
737
924
  send({
738
925
  type: "tool_start",
739
926
  tool: toolCall.name,
@@ -823,6 +1010,15 @@ export async function runAgentLoop(opts) {
823
1010
  }
824
1011
  send({ type: "tool_done", tool: toolCall.name, result });
825
1012
  recordToolResult(result, isError);
1013
+ if (!isError) {
1014
+ if (cacheKey) {
1015
+ readOnlyToolResultCache.set(cacheKey, result);
1016
+ }
1017
+ else {
1018
+ readOnlyToolResultCache.clear();
1019
+ duplicateReadOnlyToolCalls.clear();
1020
+ }
1021
+ }
826
1022
  return {
827
1023
  type: "tool-result",
828
1024
  toolCallId: toolCall.id,
@@ -913,7 +1109,7 @@ export function createProductionAgentHandler(options) {
913
1109
  setResponseStatus(event, 400);
914
1110
  return { error: "Invalid request body" };
915
1111
  }
916
- const { message, history = [], references = [], threadId, attachments, model: requestModel, engine: requestEngine, effort: requestEffort, } = body;
1112
+ const { message, history = [], structuredHistory, references = [], threadId, attachments, model: requestModel, engine: requestEngine, effort: requestEffort, } = body;
917
1113
  const requestMode = body.mode === "plan" ? "plan" : "act";
918
1114
  const hasMessageText = typeof message === "string" && message.trim().length > 0;
919
1115
  const hasAttachments = Array.isArray(attachments) && attachments.length > 0;
@@ -1210,13 +1406,15 @@ export function createProductionAgentHandler(options) {
1210
1406
  text: enrichedMessage + screenContext + filesContext + planModeAgentNote,
1211
1407
  attachments,
1212
1408
  });
1213
- const messages = [
1214
- ...history
1409
+ const historyMessages = structuredHistoryToEngineMessages(structuredHistory) ??
1410
+ history
1215
1411
  .filter((m) => m.content.trim())
1216
1412
  .map((m) => ({
1217
1413
  role: m.role,
1218
1414
  content: [{ type: "text", text: m.content }],
1219
- })),
1415
+ }));
1416
+ const messages = [
1417
+ ...historyMessages,
1220
1418
  { role: "user", content: userContent },
1221
1419
  ];
1222
1420
  // If there's already an active run for this thread, reject with 409 so