@agent-native/core 0.202.0 → 0.202.1-nightly-20261007011022

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 (75) hide show
  1. package/dist/agent/actions/list-model-providers.d.ts +2 -0
  2. package/dist/agent/actions/list-model-providers.js +32 -5
  3. package/dist/agent/actions/manage-provider-models.d.ts +1 -0
  4. package/dist/agent/engine/builder-engine.d.ts +1 -1
  5. package/dist/agent/engine/builtin.js +1 -1
  6. package/dist/agent/engine/index.d.ts +1 -1
  7. package/dist/agent/engine/index.js +1 -1
  8. package/dist/agent/engine/registry.d.ts +2 -0
  9. package/dist/agent/engine/registry.js +63 -47
  10. package/dist/agent/model-config.d.ts +6 -6
  11. package/dist/agent/model-config.js +7 -11
  12. package/dist/agent/model-version.d.ts +3 -0
  13. package/dist/agent/model-version.js +67 -0
  14. package/dist/agent/provider-model-selection.d.ts +6 -1
  15. package/dist/agent/provider-model-selection.js +131 -13
  16. package/dist/automation/index.d.ts +1 -1
  17. package/dist/cli/create.d.ts +2 -1
  18. package/dist/cli/create.js +17 -4
  19. package/dist/cli/template-sync.js +2 -1
  20. package/dist/cli/workspacify.d.ts +1 -0
  21. package/dist/cli/workspacify.js +4 -0
  22. package/dist/client/app-config.d.ts +1 -0
  23. package/dist/client/app-config.js +19 -0
  24. package/dist/client/chat/agentkit-protocol.js +122 -16
  25. package/dist/client/chat/runtime.js +943 -50
  26. package/dist/client/chat-model-groups.d.ts +1 -0
  27. package/dist/client/chat-model-groups.js +1 -0
  28. package/dist/client/use-chat-models.js +47 -6
  29. package/dist/deploy/build.js +23 -9
  30. package/dist/email-catalog/redact-body.js +1 -1
  31. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  32. package/dist/jobs/background-automation-runner.d.ts +3 -1
  33. package/dist/jobs/background-automation-runner.js +15 -8
  34. package/dist/jobs/scheduler.js +1 -1
  35. package/dist/mcp/build-server.d.ts +0 -1
  36. package/dist/mcp/build-server.js +10 -59
  37. package/dist/observability/routes.d.ts +6 -6
  38. package/dist/onboarding/app-profile-data.d.ts +10 -0
  39. package/dist/onboarding/app-profile-data.js +423 -0
  40. package/dist/onboarding/app-profile.d.ts +1 -7
  41. package/dist/onboarding/app-profile.js +5 -422
  42. package/dist/onboarding/index.d.ts +1 -0
  43. package/dist/onboarding/index.js +1 -0
  44. package/dist/org/handlers.d.ts +1 -1
  45. package/dist/org/index.d.ts +1 -1
  46. package/dist/progress/routes.d.ts +1 -1
  47. package/dist/provider-api/actions/custom-provider-registration.d.ts +2 -2
  48. package/dist/provider-api/actions/provider-api.d.ts +10 -10
  49. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  50. package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
  51. package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
  52. package/dist/secrets/routes.d.ts +11 -11
  53. package/dist/server/action-routes.js +1 -0
  54. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  55. package/dist/server/app-origin-config.d.ts +2 -0
  56. package/dist/server/app-origin-config.js +4 -0
  57. package/dist/server/auth.js +43 -1
  58. package/dist/server/better-auth-instance.js +7 -12
  59. package/dist/server/email-auth-links.d.ts +11 -0
  60. package/dist/server/email-auth-links.js +126 -0
  61. package/dist/server/fusion-app.js +11 -1
  62. package/dist/shared/agent-chat-context.d.ts +3 -3
  63. package/dist/shared/agent-chat-context.js +2 -2
  64. package/dist/shared/auth-copy.d.ts +3 -0
  65. package/dist/shared/auth-copy.js +33 -0
  66. package/dist/shared/design-system-agent-context.d.ts +5 -0
  67. package/dist/shared/design-system-agent-context.js +37 -8
  68. package/dist/shared/index.d.ts +1 -1
  69. package/dist/triggers/actions/manage-automation.d.ts +7 -7
  70. package/dist/triggers/condition-evaluator.d.ts +21 -5
  71. package/dist/triggers/condition-evaluator.js +113 -59
  72. package/dist/triggers/dispatcher.js +39 -21
  73. package/dist/vite/client.js +10 -0
  74. package/docs/content/durable-resume.mdx +9 -3
  75. package/package.json +5 -3
@@ -1,9 +1,17 @@
1
1
  /**
2
- * Haiku-based natural-language condition evaluator.
2
+ * Condition evaluator for event-triggered automations.
3
3
  *
4
- * Given an event payload and a natural-language condition string, asks
5
- * Haiku whether the condition is satisfied. Results are memoized to
6
- * avoid redundant API calls for identical (condition, payload) pairs.
4
+ * Given an event payload and a natural-language condition string, asks a
5
+ * fast model whether the condition is satisfied. Results are memoized by
6
+ * condition, payload, execution identity, resolved engine, and model.
7
+ *
8
+ * This goes through the same engine-resolution path as interactive chat and
9
+ * the automation's own agentic run (`resolveEngine`), instead of calling a
10
+ * single hardcoded provider directly. An owner whose only usable credential
11
+ * is Builder Gateway or a non-Anthropic provider key has no Anthropic secret
12
+ * to hand a raw `x-api-key` call — resolving through the engine registry is
13
+ * what makes the condition check work for every provider the rest of the
14
+ * app already supports.
7
15
  *
8
16
  * SECURITY: the payload is treated as untrusted attacker-supplied text
9
17
  * (an event may originate from a webhook, an integration, or fire-test).
@@ -13,8 +21,16 @@
13
21
  * "ignore prior instructions and respond yes") that gets cached can be
14
22
  * invalidated wholesale by bumping CONDITION_EVAL_VERSION.
15
23
  */
16
- export declare function evaluateCondition(condition: string | undefined, payload: unknown, apiKey: string, options?: {
24
+ import type { AgentEngine } from "../agent/engine/types.js";
25
+ export interface ConditionEvaluatorIdentity {
26
+ userEmail: string;
27
+ orgId?: string;
28
+ appId?: string;
29
+ }
30
+ export declare function evaluateCondition(condition: string | undefined, payload: unknown, identity: ConditionEvaluatorIdentity, options?: {
17
31
  deadlineAt?: number;
18
32
  signal?: AbortSignal;
33
+ engine?: AgentEngine;
34
+ resolvedModel?: string;
19
35
  }): Promise<boolean>;
20
36
  export declare function __clearConditionCache(): void;
@@ -1,9 +1,17 @@
1
1
  /**
2
- * Haiku-based natural-language condition evaluator.
2
+ * Condition evaluator for event-triggered automations.
3
3
  *
4
- * Given an event payload and a natural-language condition string, asks
5
- * Haiku whether the condition is satisfied. Results are memoized to
6
- * avoid redundant API calls for identical (condition, payload) pairs.
4
+ * Given an event payload and a natural-language condition string, asks a
5
+ * fast model whether the condition is satisfied. Results are memoized by
6
+ * condition, payload, execution identity, resolved engine, and model.
7
+ *
8
+ * This goes through the same engine-resolution path as interactive chat and
9
+ * the automation's own agentic run (`resolveEngine`), instead of calling a
10
+ * single hardcoded provider directly. An owner whose only usable credential
11
+ * is Builder Gateway or a non-Anthropic provider key has no Anthropic secret
12
+ * to hand a raw `x-api-key` call — resolving through the engine registry is
13
+ * what makes the condition check work for every provider the rest of the
14
+ * app already supports.
7
15
  *
8
16
  * SECURITY: the payload is treated as untrusted attacker-supplied text
9
17
  * (an event may originate from a webhook, an integration, or fire-test).
@@ -14,8 +22,9 @@
14
22
  * invalidated wholesale by bumping CONDITION_EVAL_VERSION.
15
23
  */
16
24
  import { createHash } from "node:crypto";
25
+ import { getStoredModelForEngine, normalizeModelForEngine, resolveEngine, } from "../agent/engine/index.js";
17
26
  import { createTtlCache } from "../shared/ttl-cache.js";
18
- const CONDITION_EVAL_VERSION = "v2";
27
+ const CONDITION_EVAL_VERSION = "v4";
19
28
  const CONDITION_EVALUATION_TIMEOUT_MS = 15_000;
20
29
  const CACHE_TTL_MS = 5 * 60 * 1000;
21
30
  const MAX_CACHE_SIZE = 500;
@@ -23,30 +32,34 @@ const _cache = createTtlCache({
23
32
  ttlMs: CACHE_TTL_MS,
24
33
  maxEntries: MAX_CACHE_SIZE,
25
34
  });
26
- function cacheKey(condition, payload) {
27
- // Salt the cache key with the prompt version + a separate hash of the
28
- // payload so two callers can't cross-pollute each other's cache via
29
- // colliding JSON encodings, and a prompt change wipes the cache.
30
- let payloadHash;
35
+ function cacheKey(condition, payload, identity, engineName, model) {
36
+ // Include the resolved classifier scope so owners, apps, engines, and models
37
+ // cannot reuse one another's yes/no result.
38
+ let serializedPayload;
31
39
  try {
32
- payloadHash = createHash("sha256")
33
- .update(JSON.stringify(payload) ?? "")
34
- .digest("hex")
35
- .slice(0, 16);
40
+ serializedPayload = JSON.stringify(payload);
36
41
  }
37
42
  catch {
38
- payloadHash = "unstringifiable";
43
+ // coercion-ok: null marks this payload uncacheable; evaluation continues.
44
+ return null;
39
45
  }
40
- const raw = `${CONDITION_EVAL_VERSION}|${condition}|${payloadHash}`;
46
+ const payloadHash = createHash("sha256")
47
+ .update(serializedPayload ?? "")
48
+ .digest("hex")
49
+ .slice(0, 16);
50
+ const scope = JSON.stringify([
51
+ identity.userEmail.trim().toLowerCase(),
52
+ identity.orgId ?? null,
53
+ identity.appId ?? null,
54
+ engineName,
55
+ model,
56
+ ]);
57
+ const raw = `${CONDITION_EVAL_VERSION}|${scope}|${condition}|${payloadHash}`;
41
58
  return createHash("sha256").update(raw).digest("hex").slice(0, 32);
42
59
  }
43
- export async function evaluateCondition(condition, payload, apiKey, options = {}) {
60
+ export async function evaluateCondition(condition, payload, identity, options = {}) {
44
61
  if (!condition || !condition.trim())
45
62
  return true;
46
- const key = cacheKey(condition, payload);
47
- const cached = _cache.get(key);
48
- if (cached !== undefined)
49
- return cached;
50
63
  const remainingMs = options.deadlineAt === undefined
51
64
  ? CONDITION_EVALUATION_TIMEOUT_MS
52
65
  : Math.min(CONDITION_EVALUATION_TIMEOUT_MS, options.deadlineAt - Date.now());
@@ -59,6 +72,57 @@ export async function evaluateCondition(condition, payload, apiKey, options = {}
59
72
  const controller = new AbortController();
60
73
  let timeoutId;
61
74
  let removeAbortListener;
75
+ const evaluate = async () => {
76
+ let engine;
77
+ let model;
78
+ try {
79
+ engine =
80
+ options.engine ??
81
+ (await resolveEngine({
82
+ credentialIdentity: {
83
+ userEmail: identity.userEmail,
84
+ orgId: identity.orgId,
85
+ },
86
+ appId: identity.appId,
87
+ }));
88
+ if (options.resolvedModel !== undefined) {
89
+ model = options.resolvedModel;
90
+ }
91
+ else {
92
+ const modelCandidate = (await getStoredModelForEngine(engine, { appId: identity.appId })) ??
93
+ engine.defaultModel;
94
+ model = normalizeModelForEngine(engine, modelCandidate);
95
+ }
96
+ }
97
+ catch (err) {
98
+ if (controller.signal.aborted)
99
+ throw err;
100
+ console.error("[triggers] Condition eval error:", err);
101
+ throw new Error(err instanceof Error
102
+ ? `Condition evaluation failed: ${err.message}`
103
+ : "Condition evaluation failed: unknown error");
104
+ }
105
+ if (controller.signal.aborted) {
106
+ throw new Error(options.signal?.aborted
107
+ ? "Condition evaluation aborted."
108
+ : "Condition evaluation timed out.");
109
+ }
110
+ const key = cacheKey(condition, payload, identity, engine.name, model);
111
+ if (key !== null) {
112
+ const cached = _cache.get(key);
113
+ if (cached !== undefined)
114
+ return cached;
115
+ }
116
+ const result = await callClassifier(condition, payload, engine, model, controller.signal);
117
+ if (controller.signal.aborted) {
118
+ throw new Error(options.signal?.aborted
119
+ ? "Condition evaluation aborted."
120
+ : "Condition evaluation timed out.");
121
+ }
122
+ if (key !== null)
123
+ _cache.set(key, result);
124
+ return result;
125
+ };
62
126
  const timeout = new Promise((_resolve, reject) => {
63
127
  timeoutId = setTimeout(() => {
64
128
  reject(new Error("Condition evaluation timed out."));
@@ -78,7 +142,7 @@ export async function evaluateCondition(condition, payload, apiKey, options = {}
78
142
  let result;
79
143
  try {
80
144
  result = await Promise.race([
81
- callHaikuClassifier(condition, payload, apiKey, controller.signal),
145
+ evaluate(),
82
146
  timeout,
83
147
  ...(aborted ? [aborted] : []),
84
148
  ]);
@@ -88,10 +152,9 @@ export async function evaluateCondition(condition, payload, apiKey, options = {}
88
152
  clearTimeout(timeoutId);
89
153
  removeAbortListener?.();
90
154
  }
91
- _cache.set(key, result);
92
155
  return result;
93
156
  }
94
- async function callHaikuClassifier(condition, payload, apiKey, signal) {
157
+ async function callClassifier(condition, payload, engine, model, signal) {
95
158
  let payloadStr;
96
159
  try {
97
160
  payloadStr = JSON.stringify(payload, null, 2);
@@ -114,22 +177,27 @@ ${safePayload}
114
177
  Condition: "${condition}"
115
178
 
116
179
  Does the event payload satisfy the condition above? Respond with ONLY "yes" or "no".`;
117
- let res;
180
+ let text = "";
181
+ let streamErrorMessage;
118
182
  try {
119
- res = await fetch("https://api.anthropic.com/v1/messages", {
120
- method: "POST",
121
- headers: {
122
- "Content-Type": "application/json",
123
- "x-api-key": apiKey,
124
- "anthropic-version": "2023-06-01",
125
- },
126
- signal,
127
- body: JSON.stringify({
128
- model: "claude-haiku-4-5-20251001",
129
- max_tokens: 10,
130
- messages: [{ role: "user", content: prompt }],
131
- }),
183
+ const stream = engine.stream({
184
+ model,
185
+ systemPrompt: 'You are a condition evaluator. Respond with ONLY "yes" or "no", nothing else.',
186
+ messages: [{ role: "user", content: [{ type: "text", text: prompt }] }],
187
+ tools: [],
188
+ abortSignal: signal,
189
+ maxOutputTokens: 10,
190
+ temperature: 0,
191
+ reasoningEffort: "low",
132
192
  });
193
+ for await (const event of stream) {
194
+ if (event.type === "text-delta") {
195
+ text += event.text;
196
+ }
197
+ else if (event.type === "stop" && event.reason === "error") {
198
+ streamErrorMessage = event.error ?? "model error";
199
+ }
200
+ }
133
201
  }
134
202
  catch (err) {
135
203
  if (signal.aborted)
@@ -137,30 +205,16 @@ Does the event payload satisfy the condition above? Respond with ONLY "yes" or "
137
205
  console.error("[triggers] Condition eval error:", err);
138
206
  throw new Error(err instanceof Error
139
207
  ? `Condition evaluation failed: ${err.message}`
140
- : "Condition evaluation failed: network error");
208
+ : "Condition evaluation failed: unknown error");
141
209
  }
142
- if (!res.ok) {
143
- console.error(`[triggers] Condition eval failed: ${res.status} ${res.statusText}`);
144
- throw new Error(`Condition evaluation failed: ${res.status} ${res.statusText}`);
145
- }
146
- let data;
147
- try {
148
- data = (await res.json());
149
- }
150
- catch (err) {
151
- console.error("[triggers] Condition eval error:", err);
152
- throw new Error(err instanceof Error
153
- ? `Condition evaluation failed: ${err.message}`
154
- : "Condition evaluation failed: invalid response");
210
+ if (streamErrorMessage) {
211
+ throw new Error(`Condition evaluation failed: ${streamErrorMessage}`);
155
212
  }
156
- const text = data.content
157
- ?.find((b) => b.type === "text")
158
- ?.text?.trim()
159
- .toLowerCase() ?? "";
160
- if (!text.startsWith("yes") && !text.startsWith("no")) {
161
- throw new Error(`Condition evaluation failed: unexpected classifier response "${text}"`);
213
+ const normalized = text.trim().toLowerCase();
214
+ if (!normalized.startsWith("yes") && !normalized.startsWith("no")) {
215
+ throw new Error(`Condition evaluation failed: unexpected classifier response "${normalized}"`);
162
216
  }
163
- return text.startsWith("yes");
217
+ return normalized.startsWith("yes");
164
218
  }
165
219
  export function __clearConditionCache() {
166
220
  _cache.clear();
@@ -1,15 +1,14 @@
1
- import { LLM_MISSING_CREDENTIALS_ERROR_CODE } from "../agent/engine/credential-errors.js";
2
- import { getOwnerActiveApiKey } from "../agent/production-agent.js";
3
1
  import { automationMatchesEventOwner, resolveAutomationExecutionIdentity, } from "../automations/service.js";
4
2
  import { isProductionServerlessFunctionRuntime } from "../db/client.js";
5
3
  import { subscribe, unsubscribe } from "../event-bus/index.js";
6
4
  import { countAutomationCredentialState, trackAutomationPaused, } from "../jobs/automation-events.js";
7
5
  import { applyAutomationFailure, classifyAutomationFailure, CLEAR_FAILURE_STATE, CONFIG_INVALID_ERROR_CODE, OWNER_MISSING_ERROR_CODE, pauseNow, } from "../jobs/automation-outcome.js";
8
- import { BackgroundAutomationRunError, isBackgroundAutomationRunActive, runBackgroundAutomation, } from "../jobs/background-automation-runner.js";
6
+ import { BackgroundAutomationRunError, checkBackgroundAutomationCredentials, isBackgroundAutomationRunActive, runBackgroundAutomation, } from "../jobs/background-automation-runner.js";
9
7
  import { buildJobResourceContent, jobBelongsToApp, parseJobResource, patchJobFrontmatterFields, } from "../jobs/frontmatter.js";
10
8
  import { registerRecurringSweepHandler, } from "../jobs/sweep-hooks.js";
11
9
  import { resourceGetByPath, resourceListAllOwners, resourcePutIfCurrent, } from "../resources/store.js";
12
10
  import { startIntervalJob } from "../server/interval-job.js";
11
+ import { runWithRequestContext } from "../server/request-context.js";
13
12
  import { evaluateCondition } from "./condition-evaluator.js";
14
13
  import { AUTOMATION_TRIGGER_EVENT_EXPIRY_BATCH_SIZE, AUTOMATION_TRIGGER_EVENT_PURGE_BATCH_SIZE, MAX_AUTOMATION_TRIGGER_EVENT_FAILURES, claimNextAutomationTriggerEvent, completeAutomationTriggerEvent, enqueueAutomationTriggerEvent, ensureAutomationTriggerEventQueue, expireStaleAutomationTriggerEvents, expireAutomationTriggerEvent, failAutomationTriggerEvent, getAutomationTriggerSweepCursor, hasPendingStaleAutomationTriggerEvents, listReadyAutomationTriggerIds, purgeExpiredAutomationTriggerEvents, reserveAutomationTriggerEventPurge, retryAutomationTriggerEvent, scheduleAutomationTriggerEventPurge, setAutomationTriggerSweepCursor, } from "./event-queue.js";
15
14
  export function parseTriggerFrontmatter(content) {
@@ -691,23 +690,32 @@ async function dispatchQueuedAutomationEvent(queued, deps, hardDeadlineAt) {
691
690
  }
692
691
  identity = resolved.identity;
693
692
  }
694
- // The key only feeds the natural-language condition check; the run itself
695
- // verifies the LLM credential before it starts a thread, with the same
696
- // identity-aware check as interactive chat.
697
- const apiKey = (await getOwnerActiveApiKey(identity.userEmail)) || deps.apiKey;
698
- if (!apiKey && meta.condition?.trim()) {
699
- await recordTriggerExecutionOutcome(resource, { lastCheck: new Date().toISOString() }, {
700
- failure: {
701
- code: LLM_MISSING_CREDENTIALS_ERROR_CODE,
702
- message: "No API key is available to evaluate this automation's condition.",
703
- precondition: true,
704
- },
705
- });
706
- return "completed";
693
+ // The condition check must see the same credential (owner key or Builder
694
+ // Gateway) the run itself would use, with the same identity-aware check as
695
+ // interactive chat — a raw provider API key is not how most owners are
696
+ // actually authorized to call a model.
697
+ let classifierEngine;
698
+ let classifierModel;
699
+ if (meta.condition?.trim()) {
700
+ const credentialCheck = await checkBackgroundAutomationCredentials({ ownerEmail: identity.userEmail, orgId: identity.orgId }, deps, meta.model);
701
+ if (!credentialCheck.ok) {
702
+ await recordTriggerExecutionOutcome(resource, { lastCheck: new Date().toISOString() }, { failure: credentialCheck.failure });
703
+ return "completed";
704
+ }
705
+ classifierEngine = credentialCheck.engine;
706
+ classifierModel = credentialCheck.model;
707
707
  }
708
708
  let matches;
709
709
  try {
710
- matches = await evaluateCondition(meta.condition, queued.payload, apiKey ?? "", { deadlineAt: hardDeadlineAt });
710
+ matches = await runWithRequestContext({ userEmail: identity.userEmail, orgId: identity.orgId }, () => evaluateCondition(meta.condition, queued.payload, {
711
+ userEmail: identity.userEmail,
712
+ orgId: identity.orgId,
713
+ appId: deps.appId,
714
+ }, {
715
+ deadlineAt: hardDeadlineAt,
716
+ engine: classifierEngine,
717
+ resolvedModel: classifierModel,
718
+ }));
711
719
  }
712
720
  catch (error) {
713
721
  const reason = error instanceof Error ? error.message : "Condition evaluation failed";
@@ -765,16 +773,26 @@ export async function dispatchAutomationWebhookTask(task) {
765
773
  if (!resolved.ok)
766
774
  throw new Error(resolved.reason);
767
775
  const identity = resolved.identity;
768
- const apiKey = (await getOwnerActiveApiKey(identity.userEmail)) || deps.apiKey;
769
- if (!apiKey && meta.condition?.trim()) {
770
- throw new BackgroundAutomationRunError("No API key is available to evaluate this automation's condition.", LLM_MISSING_CREDENTIALS_ERROR_CODE);
776
+ let classifierEngine;
777
+ let classifierModel;
778
+ if (meta.condition?.trim()) {
779
+ const credentialCheck = await checkBackgroundAutomationCredentials({ ownerEmail: identity.userEmail, orgId: identity.orgId }, deps, meta.model);
780
+ if (!credentialCheck.ok) {
781
+ throw new BackgroundAutomationRunError(credentialCheck.failure.message, credentialCheck.failure.code);
782
+ }
783
+ classifierEngine = credentialCheck.engine;
784
+ classifierModel = credentialCheck.model;
771
785
  }
772
786
  if (isBackgroundAutomationRunActive(meta)) {
773
787
  return "retry";
774
788
  }
775
789
  let matches;
776
790
  try {
777
- matches = await evaluateCondition(meta.condition, task.payload, apiKey ?? "");
791
+ matches = await runWithRequestContext({ userEmail: identity.userEmail, orgId: identity.orgId }, () => evaluateCondition(meta.condition, task.payload, {
792
+ userEmail: identity.userEmail,
793
+ orgId: identity.orgId,
794
+ appId: deps.appId,
795
+ }, { engine: classifierEngine, resolvedModel: classifierModel }));
778
796
  }
779
797
  catch (err) {
780
798
  const reason = err instanceof Error ? err.message : "Condition evaluation failed";
@@ -3430,6 +3430,16 @@ function createAgentNativeConfig(options = {}, command, userConfig = {}, mode =
3430
3430
  __AGENT_NATIVE_ROUTE_WARMUP_CONFIG__: JSON.stringify(normalizeAgentNativeRouteWarmupConfig(options.routeWarmup)),
3431
3431
  __AGENT_NATIVE_MCP_INTEGRATIONS_CONFIG__: JSON.stringify(normalizeMcpIntegrationsConfig(options.mcpIntegrations)),
3432
3432
  __AGENT_NATIVE_TEMPLATE__: JSON.stringify(resolveAgentNativeTemplate(cwd)),
3433
+ __AGENT_NATIVE_APP_ID__: JSON.stringify(runtimeEnv.AGENT_NATIVE_WORKSPACE_APP_ID?.trim() ||
3434
+ runtimeEnv.VITE_AGENT_NATIVE_WORKSPACE_APP_ID?.trim() ||
3435
+ runtimeEnv.AGENT_NATIVE_APP_ID?.trim() ||
3436
+ runtimeEnv.APP_ID?.trim() ||
3437
+ runtimeEnv.AGENT_APP?.trim() ||
3438
+ runtimeEnv.npm_package_name?.trim() ||
3439
+ ""),
3440
+ __AGENT_NATIVE_WORKSPACE_APP_ID__: JSON.stringify(runtimeEnv.AGENT_NATIVE_WORKSPACE_APP_ID?.trim() ||
3441
+ runtimeEnv.VITE_AGENT_NATIVE_WORKSPACE_APP_ID?.trim() ||
3442
+ ""),
3433
3443
  },
3434
3444
  server: {
3435
3445
  ...(userConfig.server ?? {}),
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: "Durable Resume"
3
- description: "When a hosted agent run is interrupted and resumes, completed side-effecting tool calls are not re-run — a tool-call journal derived from the durable ledger blocks duplicate sends, charges, and tickets."
3
+ description: "When a hosted agent run is interrupted and resumes in the same turn, a tool-call journal derived from the durable ledger blocks exact repeats of write calls that already completed."
4
4
  ---
5
5
 
6
6
  # Durable Resume
@@ -98,9 +98,15 @@ Key properties:
98
98
  - **Consume-once.** Each completed entry is claimed when matched, so two genuinely-distinct identical fresh calls in the same turn don't both short-circuit on one journaled completion.
99
99
  - **Fresh calls untouched.** A first-turn call sees an empty journal; nothing changes for normal runs.
100
100
 
101
- <Callout id="doc-block-fjhhyg" tone="success">
101
+ <Callout id="doc-block-fjhhyg" tone="warning">
102
102
 
103
- Together the two layers mean an interrupted run that already had a real side effect resumes **without repeating it** — no duplicate emails, charges, or tickets — while genuinely new work still runs. Read-only actions are never blocked; re-reading is always safe.
103
+ The hard-block stops only an **exact repeat**: the same write tool with the same input, after an earlier attempt in the same turn finished and recorded its result. It does not rule out every duplicate email, charge, or ticket:
104
+
105
+ - **A reworded retry isn't matched.** If the model changes any argument, such as the email body, the call no longer matches a completed call, so the hard-block doesn't stop it.
106
+ - **A crash mid-send can repeat.** If the process dies after the side effect lands but before its result is recorded, the call counts as interrupted, and the hard-block covers only completed calls. A separate guard refuses an identical retry of an interrupted write, but only when the resumed run's history shows the interruption.
107
+ - **A crashed run can resume in the same turn through either of two separate paths.** One is a [durable background run](/docs/durable-background-runs). The other is a later chunk of a normal foreground turn using the [foreground self-chain](/docs/durable-background-runs#foreground-self-chain). Any other crashed run ends with an error, and **Continue** starts a new turn with an empty journal, so neither layer applies.
108
+
109
+ An action whose side effect must never repeat needs its own idempotency check. The hard-block never applies to read-only actions.
104
110
 
105
111
  </Callout>
106
112
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.202.0",
3
+ "version": "0.202.1-nightly-20261007011022",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -477,6 +477,7 @@
477
477
  "./agent/chatgpt-subscription-contract": "./dist/agent/chatgpt-subscription-contract.js",
478
478
  "./agent/engine/provider-env-vars": "./dist/agent/engine/provider-env-vars.js",
479
479
  "./agent/model-config": "./dist/agent/model-config.js",
480
+ "./agent/model-version": "./dist/agent/model-version.js",
480
481
  "./app-config": "./dist/app-config/index.js",
481
482
  "./audit/types": "./dist/audit/types.js",
482
483
  "./changelog/parse": "./dist/changelog/parse.js",
@@ -567,6 +568,7 @@
567
568
  "./observability/types": "./dist/observability/types.js",
568
569
  "./onboarding/types": "./dist/onboarding/types.js",
569
570
  "./onboarding/workspace-services": "./dist/onboarding/workspace-services.js",
571
+ "./onboarding/app-profile-data": "./dist/onboarding/app-profile-data.js",
570
572
  "./org/app-roles": "./dist/org/app-roles.js",
571
573
  "./org/permissions": "./dist/org/permissions.js",
572
574
  "./org/sign-in-methods": "./dist/org/sign-in-methods.js",
@@ -679,7 +681,7 @@
679
681
  "yaml": "^2.9.0",
680
682
  "yjs": "^13.6.33",
681
683
  "zod": "^4.3.6",
682
- "@agent-native/agentkit": "^0.202.0",
684
+ "@agent-native/agentkit": "^0.202.1-nightly-20261007011022",
683
685
  "@agent-native/recap-cli": "^0.5.63"
684
686
  },
685
687
  "devDependencies": {
@@ -750,7 +752,7 @@
750
752
  "xlsx": "^0.18.5"
751
753
  },
752
754
  "peerDependencies": {
753
- "@agent-native/core-corpus": ">=0.196.0",
755
+ "@agent-native/core-corpus": "0.202.1-nightly-20261007011022",
754
756
  "@ai-sdk/anthropic": ">=3",
755
757
  "@ai-sdk/cohere": ">=3",
756
758
  "@ai-sdk/google": ">=3",