@agent-native/core 0.122.2 → 0.122.4
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/run-loop-with-resume.ts +12 -0
- package/corpus/core/src/cli/index.ts +2 -1
- package/corpus/core/src/cli/mcp.ts +13 -3
- package/corpus/core/src/cli/skills-content/rewind-skill.ts +36 -4
- package/corpus/core/src/cli/skills.ts +250 -37
- package/corpus/core/src/cli/telemetry-routing.ts +57 -0
- package/corpus/core/src/cli/telemetry.ts +3 -3
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +182 -0
- package/corpus/core/src/integrations/a2a-continuations-store.ts +122 -12
- package/corpus/core/src/integrations/integration-campaign-recovery.ts +127 -0
- package/corpus/core/src/integrations/integration-campaigns-store.ts +1039 -0
- package/corpus/core/src/integrations/integration-durable-dispatch.ts +28 -3
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +8 -0
- package/corpus/core/src/integrations/plugin.ts +453 -35
- package/corpus/core/src/integrations/webhook-handler.ts +469 -11
- package/corpus/core/src/mcp/screen-memory-stdio.ts +21 -2
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +7 -3
- package/corpus/templates/content/app/components/editor/body-hydration.ts +10 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +1 -0
- package/corpus/templates/content/app/root.tsx +12 -1
- package/corpus/templates/content/changelog/2026-07-23-new-pages-open-immediately-while-slow-network-work-continues.md +6 -0
- package/corpus/templates/content/changelog/2026-07-25-database-preview-actions-open-reliably.md +6 -0
- package/corpus/templates/content/e2e/playwright.config.ts +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +7 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +7 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +12 -3
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/skills-content/rewind-skill.d.ts +1 -1
- package/dist/cli/skills-content/rewind-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/rewind-skill.js +36 -4
- package/dist/cli/skills-content/rewind-skill.js.map +1 -1
- package/dist/cli/skills.d.ts +2 -0
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +191 -40
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/telemetry-routing.d.ts +2 -0
- package/dist/cli/telemetry-routing.d.ts.map +1 -0
- package/dist/cli/telemetry-routing.js +54 -0
- package/dist/cli/telemetry-routing.js.map +1 -0
- package/dist/cli/telemetry.js +3 -3
- package/dist/cli/telemetry.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +3 -3
- package/dist/integrations/a2a-continuation-processor.d.ts +14 -0
- package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
- package/dist/integrations/a2a-continuation-processor.js +134 -1
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/a2a-continuations-store.d.ts +23 -0
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +99 -13
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/integration-campaign-recovery.d.ts +16 -0
- package/dist/integrations/integration-campaign-recovery.d.ts.map +1 -0
- package/dist/integrations/integration-campaign-recovery.js +94 -0
- package/dist/integrations/integration-campaign-recovery.js.map +1 -0
- package/dist/integrations/integration-campaigns-store.d.ts +121 -0
- package/dist/integrations/integration-campaigns-store.d.ts.map +1 -0
- package/dist/integrations/integration-campaigns-store.js +784 -0
- package/dist/integrations/integration-campaigns-store.js.map +1 -0
- package/dist/integrations/integration-durable-dispatch.d.ts +3 -0
- package/dist/integrations/integration-durable-dispatch.d.ts.map +1 -1
- package/dist/integrations/integration-durable-dispatch.js +22 -3
- package/dist/integrations/integration-durable-dispatch.js.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +6 -0
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +325 -18
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts +16 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +368 -13
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +2 -0
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/mcp/screen-memory-stdio.js +12 -2
- package/dist/mcp/screen-memory-stdio.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
- package/src/agent/run-loop-with-resume.ts +12 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/mcp.ts +13 -3
- package/src/cli/skills-content/rewind-skill.ts +36 -4
- package/src/cli/skills.ts +250 -37
- package/src/cli/telemetry-routing.ts +57 -0
- package/src/cli/telemetry.ts +3 -3
- package/src/integrations/a2a-continuation-processor.ts +182 -0
- package/src/integrations/a2a-continuations-store.ts +122 -12
- package/src/integrations/integration-campaign-recovery.ts +127 -0
- package/src/integrations/integration-campaigns-store.ts +1039 -0
- package/src/integrations/integration-durable-dispatch.ts +28 -3
- package/src/integrations/pending-tasks-retry-job.ts +8 -0
- package/src/integrations/plugin.ts +453 -35
- package/src/integrations/webhook-handler.ts +469 -11
- package/src/mcp/screen-memory-stdio.ts +21 -2
|
@@ -1146,6 +1146,24 @@ export function screenMemoryMcpToolDefinitions() {
|
|
|
1146
1146
|
];
|
|
1147
1147
|
}
|
|
1148
1148
|
|
|
1149
|
+
export async function queryScreenMemoryContextForStore(
|
|
1150
|
+
args: Parameters<typeof queryScreenMemoryContext>[0],
|
|
1151
|
+
storeDir: string,
|
|
1152
|
+
env: NodeJS.ProcessEnv,
|
|
1153
|
+
) {
|
|
1154
|
+
return queryScreenMemoryContext(args, {
|
|
1155
|
+
homeDir: env.HOME,
|
|
1156
|
+
env: {
|
|
1157
|
+
...env,
|
|
1158
|
+
AGENT_NATIVE_SCREEN_MEMORY_DIR: storeDir,
|
|
1159
|
+
AGENT_NATIVE_SCREEN_MEMORY_CONFIG: path.join(
|
|
1160
|
+
path.dirname(storeDir),
|
|
1161
|
+
"feature-config.json",
|
|
1162
|
+
),
|
|
1163
|
+
},
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1149
1167
|
export async function runScreenMemoryMCPStdio(
|
|
1150
1168
|
opts: RunScreenMemoryMCPStdioOptions = {},
|
|
1151
1169
|
): Promise<void> {
|
|
@@ -1187,14 +1205,15 @@ export async function runScreenMemoryMCPStdio(
|
|
|
1187
1205
|
});
|
|
1188
1206
|
}
|
|
1189
1207
|
if (name === "screen_memory_recent_context") {
|
|
1190
|
-
const result = await
|
|
1208
|
+
const result = await queryScreenMemoryContextForStore(
|
|
1191
1209
|
{
|
|
1192
1210
|
query: typeof args.query === "string" ? args.query : undefined,
|
|
1193
1211
|
sinceMinutes:
|
|
1194
1212
|
typeof args.minutes === "number" ? args.minutes : undefined,
|
|
1195
1213
|
limit: typeof args.limit === "number" ? args.limit : undefined,
|
|
1196
1214
|
},
|
|
1197
|
-
|
|
1215
|
+
storeDir,
|
|
1216
|
+
env,
|
|
1198
1217
|
);
|
|
1199
1218
|
const sanitizedEvidence = result.evidence.map((item) => ({
|
|
1200
1219
|
...item,
|
|
@@ -86,6 +86,7 @@ import { cn } from "@/lib/utils";
|
|
|
86
86
|
import {
|
|
87
87
|
documentBodyHydrationIsPending,
|
|
88
88
|
isEffectivelyEmptyDocumentContent,
|
|
89
|
+
newDocumentPageChoiceIsDisabled,
|
|
89
90
|
} from "./body-hydration";
|
|
90
91
|
import { BuilderBodySyncingNotice } from "./BuilderBodySyncingNotice";
|
|
91
92
|
import type { CommentTextAnchor } from "./comment-anchors";
|
|
@@ -1807,9 +1808,12 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
|
|
|
1807
1808
|
type="button"
|
|
1808
1809
|
variant="outline"
|
|
1809
1810
|
className="justify-start gap-2"
|
|
1810
|
-
disabled={
|
|
1811
|
-
|
|
1812
|
-
|
|
1811
|
+
disabled={newDocumentPageChoiceIsDisabled({
|
|
1812
|
+
canEdit,
|
|
1813
|
+
bodyHydrationPending,
|
|
1814
|
+
databaseCreationPending:
|
|
1815
|
+
createDatabase.isPending,
|
|
1816
|
+
})}
|
|
1813
1817
|
onClick={handleChoosePage}
|
|
1814
1818
|
>
|
|
1815
1819
|
<IconFileText />
|
|
@@ -69,6 +69,16 @@ export function documentBodyHydrationIsPending(
|
|
|
69
69
|
return builderBodyHydrationIsPending(hydration);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
export function newDocumentPageChoiceIsDisabled(args: {
|
|
73
|
+
canEdit: boolean;
|
|
74
|
+
bodyHydrationPending: boolean;
|
|
75
|
+
databaseCreationPending: boolean;
|
|
76
|
+
}) {
|
|
77
|
+
return (
|
|
78
|
+
!args.canEdit || args.bodyHydrationPending || args.databaseCreationPending
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
export function previewBodyHydrationIsPending(args: {
|
|
73
83
|
item: Pick<ContentDatabaseItem, "bodyHydration" | "document">;
|
|
74
84
|
document: Pick<Document, "content" | "databaseMembership"> | null | undefined;
|
|
@@ -49,7 +49,11 @@ import {
|
|
|
49
49
|
useRouteLoaderData,
|
|
50
50
|
useRouteError,
|
|
51
51
|
} from "react-router";
|
|
52
|
-
import type {
|
|
52
|
+
import type {
|
|
53
|
+
LinksFunction,
|
|
54
|
+
LoaderFunctionArgs,
|
|
55
|
+
ShouldRevalidateFunctionArgs,
|
|
56
|
+
} from "react-router";
|
|
53
57
|
|
|
54
58
|
// Styled sonner wrapper — passed via AppProviders `toaster` prop to avoid duplicate.
|
|
55
59
|
import { Toaster as Sonner } from "@/components/ui/sonner";
|
|
@@ -105,6 +109,13 @@ export async function loader({
|
|
|
105
109
|
};
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
export function shouldRevalidate({
|
|
113
|
+
defaultShouldRevalidate,
|
|
114
|
+
formMethod,
|
|
115
|
+
}: ShouldRevalidateFunctionArgs) {
|
|
116
|
+
return formMethod ? defaultShouldRevalidate : false;
|
|
117
|
+
}
|
|
118
|
+
|
|
108
119
|
// Pass args to match content's 3-way theme-cycle UX (no disableTransitionOnChange).
|
|
109
120
|
const THEME_INIT_SCRIPT = getThemeInitScript("system", true);
|
|
110
121
|
|
|
@@ -14,7 +14,7 @@ import { defineConfig, devices } from "@playwright/test";
|
|
|
14
14
|
*/
|
|
15
15
|
export default defineConfig({
|
|
16
16
|
testDir: ".",
|
|
17
|
-
testMatch: /(registry-blocks|local-files)\.spec\.ts/,
|
|
17
|
+
testMatch: /(registry-blocks|local-files|database-preview-menu)\.spec\.ts/,
|
|
18
18
|
fullyParallel: true,
|
|
19
19
|
workers: process.env.CI ? 2 : 3,
|
|
20
20
|
retries: 2,
|
|
@@ -20,8 +20,14 @@
|
|
|
20
20
|
* Both paths route through `appendAgentLoopContinuation` so the agent sees a
|
|
21
21
|
* uniform "continue" instruction regardless of which recovery fired.
|
|
22
22
|
*/
|
|
23
|
-
import {
|
|
23
|
+
import type { EngineMessage } from "./engine/types.js";
|
|
24
|
+
import { runAgentLoop, type AgentLoopContinuationReason } from "./production-agent.js";
|
|
24
25
|
import type { ResolveRunSoftTimeoutOptions } from "./run-manager.js";
|
|
26
|
+
/**
|
|
27
|
+
* Rebuild the same safe continuation context for a logical turn that resumes
|
|
28
|
+
* in a fresh hosted invocation.
|
|
29
|
+
*/
|
|
30
|
+
export declare function appendDurableContinuationContext(messages: EngineMessage[], reason: AgentLoopContinuationReason, threadId: string): Promise<void>;
|
|
25
31
|
/**
|
|
26
32
|
* Cap on continuation iterations inside a single
|
|
27
33
|
* `runAgentLoopDirectWithSoftTimeout` invocation. The host's hard function
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-loop-with-resume.d.ts","sourceRoot":"","sources":["../../src/agent/run-loop-with-resume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"run-loop-with-resume.d.ts","sourceRoot":"","sources":["../../src/agent/run-loop-with-resume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EACL,YAAY,EAMZ,KAAK,2BAA2B,EACjC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAC;AA4FrE;;;GAGG;AACH,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,aAAa,EAAE,EACzB,MAAM,EAAE,2BAA2B,EACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAEf;AAsCD;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;wDAIwD;AACxD,eAAO,MAAM,+BAA+B,yBAAyB,CAAC;AAEtE;;;;;iCAKiC;AACjC,eAAO,MAAM,4BAA4B,QAGkD,CAAC;AAE5F;;;;;;;;GAQG;AACH,wBAAsB,iCAAiC,CACrD,IAAI,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,EACxC,aAAa,CAAC,EAAE,MAAM,EACtB,cAAc,CAAC,EAAE,4BAA4B,GAC5C,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CA4LnD"}
|
|
@@ -91,6 +91,13 @@ async function appendContinuationAndJournal(messages, reason, threadId, localEve
|
|
|
91
91
|
appendAgentLoopContinuation(messages, reason, actionPreparationContinuationOptions(events));
|
|
92
92
|
appendToolCallJournalNote(messages, events);
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Rebuild the same safe continuation context for a logical turn that resumes
|
|
96
|
+
* in a fresh hosted invocation.
|
|
97
|
+
*/
|
|
98
|
+
export async function appendDurableContinuationContext(messages, reason, threadId) {
|
|
99
|
+
await appendContinuationAndJournal(messages, reason, threadId);
|
|
100
|
+
}
|
|
94
101
|
async function hasCompletedSideEffectToolCallInCurrentTurn(threadId, localEvents = []) {
|
|
95
102
|
try {
|
|
96
103
|
const events = await readCurrentTurnEventsForResume(threadId, localEvents);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-loop-with-resume.js","sourceRoot":"","sources":["../../src/agent/run-loop-with-resume.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Wraps `runAgentLoop` with two layered recovery mechanisms so a single hosted\n * invocation can survive interruptions without showing the user a dead chat:\n *\n * 1. **Soft timeout** — an inner timer that aborts the LLM call before the\n * hosting function's hard limit (Lambda 75s, Vercel 60s, etc.) so we have a\n * chance to gracefully wind down and append a continuation nudge. Without\n * this the function gets killed mid-stream and the user sees a frozen\n * spinner.\n *\n * 2. **Resumable-error continuation** — when the LLM call errors with a\n * transport- or gateway-level interruption (Builder gateway 45s timeout,\n * socket hang up, ECONNRESET, upstream 5xx that survived engine retries),\n * we save the conversation prefix, append a \"continue from where you left\n * off\" message, and run another LLM call. Anthropic's prompt cache makes\n * the resume call dramatically faster than the cold first attempt, and the\n * agent gets explicit context that it was cut off so it doesn't re-do\n * completed work.\n *\n * Both paths route through `appendAgentLoopContinuation` so the agent sees a\n * uniform \"continue\" instruction regardless of which recovery fired.\n */\n\nimport type { EngineMessage } from \"./engine/types.js\";\nimport {\n runAgentLoop,\n appendAgentLoopContinuation,\n isResumableEngineError,\n continuationReasonForResumableError,\n lastUnfinishedPreparingActionToolFromEvents,\n resolveFinalResponseGuardRequestText,\n type AgentLoopContinuationReason,\n} from \"./production-agent.js\";\nimport { resolveRunSoftTimeoutMs } from \"./run-manager.js\";\nimport type { ResolveRunSoftTimeoutOptions } from \"./run-manager.js\";\nimport { getCurrentTurnEventsForThread } from \"./run-store.js\";\nimport {\n classifyToolCallJournal,\n buildResumeJournalNote,\n} from \"./tool-call-journal.js\";\nimport type { AgentChatEvent } from \"./types.js\";\n\nasync function readCurrentTurnEventsForResume(\n threadId: string | undefined,\n localEvents: readonly AgentChatEvent[] = [],\n): Promise<AgentChatEvent[]> {\n let persistedEvents: AgentChatEvent[] = [];\n try {\n persistedEvents = threadId\n ? await getCurrentTurnEventsForThread(threadId)\n : [];\n } catch {\n persistedEvents = [];\n }\n if (localEvents.length === 0) return persistedEvents;\n const seen = new Set(persistedEvents.map((event) => JSON.stringify(event)));\n return [\n ...persistedEvents,\n ...localEvents.filter((event) => !seen.has(JSON.stringify(event))),\n ];\n}\n\nfunction actionPreparationContinuationOptions(\n events: readonly AgentChatEvent[],\n): { actionPreparationTool?: string } {\n const actionPreparationTool =\n lastUnfinishedPreparingActionToolFromEvents(events);\n return actionPreparationTool ? { actionPreparationTool } : {};\n}\n\n/**\n * Derive the per-turn tool-call journal from the durable run-event ledger and,\n * when there is anything to report, append a STRUCTURED note to the message\n * prefix so the resumed model:\n * - does NOT re-execute tool calls that already completed (avoiding duplicate\n * side effects like re-sending an email or re-creating a ticket), and\n * - is explicitly told about any tool call that started but whose outcome was\n * never recorded (\"interrupted, unknown outcome\") so it can decide.\n *\n * This is additive to the existing \"continue from where you left off\" nudge —\n * it is appended right after it. When the journal is empty (no completed or\n * interrupted tool calls — e.g. a turn with no tool activity, or a clean\n * continuation), nothing extra is appended and resume behavior is byte-for-byte\n * what it was before. Best-effort: any ledger read/parse failure is swallowed so\n * a journal hiccup can never block a recovery that would otherwise succeed.\n *\n * This prompt-level journal is paired with tool-layer enforcement in\n * production-agent.ts/runToolCall, which refuses to re-execute a journaled-\n * complete write tool (returning the journaled result instead). See\n * `tool-call-journal.ts` (`findCompletedJournalEntry`) for the keying used.\n */\nfunction appendToolCallJournalNote(\n messages: EngineMessage[],\n events: readonly AgentChatEvent[],\n): void {\n try {\n if (events.length === 0) return;\n const journal = classifyToolCallJournal(events);\n const note = buildResumeJournalNote(journal);\n if (!note) return;\n messages.push({\n role: \"user\",\n content: [{ type: \"text\", text: note }],\n });\n } catch {\n // The journal is a hardening layer, never a gate. A failed ledger read or\n // parse must not break the resume that the continuation nudge already set\n // up — the model still continues, just without the structured journal.\n }\n}\n\nasync function appendContinuationAndJournal(\n messages: EngineMessage[],\n reason: AgentLoopContinuationReason,\n threadId: string | undefined,\n localEvents: readonly AgentChatEvent[] = [],\n): Promise<void> {\n const events = await readCurrentTurnEventsForResume(threadId, localEvents);\n appendAgentLoopContinuation(\n messages,\n reason,\n actionPreparationContinuationOptions(events),\n );\n appendToolCallJournalNote(messages, events);\n}\n\nasync function hasCompletedSideEffectToolCallInCurrentTurn(\n threadId: string | undefined,\n localEvents: readonly AgentChatEvent[] = [],\n): Promise<boolean> {\n try {\n const events = await readCurrentTurnEventsForResume(threadId, localEvents);\n if (events.length === 0) return false;\n return events.some(\n (event) =>\n event.type === \"tool_done\" &&\n event.completedSideEffect === true &&\n event.isError !== true,\n );\n } catch {\n return false;\n }\n}\n\nfunction internalContinuationReasonForAttempt(\n events: readonly AgentChatEvent[],\n): AgentLoopContinuationReason | undefined {\n const last = events.at(-1);\n if (last?.type !== \"auto_continue\") return undefined;\n if (\n last.reason === \"run_timeout\" ||\n last.reason === \"loop_limit\" ||\n last.reason === \"no_progress\" ||\n last.reason === \"stream_ended\" ||\n last.reason === \"gateway_timeout\" ||\n last.reason === \"network_interrupted\"\n ) {\n return last.reason;\n }\n return undefined;\n}\n\n/**\n * Cap on continuation iterations inside a single\n * `runAgentLoopDirectWithSoftTimeout` invocation. The host's hard function\n * timeout usually bounds this naturally — but a defensive cap prevents an\n * instant-error spiral from looping forever inside hosting environments with a\n * generous budget.\n *\n * 6 leaves room for: 1 normal completion + a few resume rounds for design\n * generation (prompt + 3 variants ≈ 4 LLM calls), with a small safety margin.\n */\nexport const MAX_RUN_LOOP_CONTINUATIONS = 6;\n\n/** Machine-readable code carried on the give-up terminal `error` event so the\n * client renders a loud \"stopped before finishing\" terminal instead of an\n * ambiguous silent stall. Deliberately NOT in the client's auto-recoverable\n * allow-list (`isAutoRecoverableError`) so it terminates the chain rather than\n * looping another POST that would hit the same wall. */\nexport const RUN_BUDGET_EXHAUSTED_ERROR_CODE = \"run_budget_exhausted\";\n\n/** User-facing terminal message when a hosted run is cut off mid-step and\n * exhausts its in-invocation continuation budget without finishing. Generic and\n * framework-level (not app-specific). Mirrors the `reliable-mutations` skill's\n * \"fail loud, retry as a single bulk action\" guidance so the user understands\n * the turn stopped before finishing without implying that earlier completed\n * tool calls did not persist. */\nexport const RUN_BUDGET_EXHAUSTED_MESSAGE =\n \"I ran out of time before finishing this step. \" +\n \"I stopped rather than keep retrying silently. \" +\n \"Check any completed tool cards above before retrying, ideally as one smaller follow-up.\";\n\n/**\n * Internal entry point used by the agent-chat plugin's run handler. Wraps\n * `runAgentLoop` with soft-timeout + resumable-error continuation recovery.\n *\n * The `softTimeoutMs` argument falls back to `resolveRunSoftTimeoutMs(...)` so\n * different hosting environments (Lambda, Vercel, Cloudflare, local dev) get\n * an appropriate inner budget. Setting it to <= 0 disables both layers — the\n * call goes straight to `runAgentLoop` with no wrapping.\n */\nexport async function runAgentLoopDirectWithSoftTimeout(\n opts: Parameters<typeof runAgentLoop>[0],\n softTimeoutMs?: number,\n timeoutOptions?: ResolveRunSoftTimeoutOptions,\n): Promise<Awaited<ReturnType<typeof runAgentLoop>>> {\n const finalResponseGuardRequestText =\n opts.finalResponseGuardRequestText ??\n resolveFinalResponseGuardRequestText(opts.messages);\n const stableOpts = { ...opts, finalResponseGuardRequestText };\n const timeoutMs = resolveRunSoftTimeoutMs(softTimeoutMs, timeoutOptions);\n if (timeoutMs <= 0) return runAgentLoop(stableOpts);\n\n const upstreamSignal = opts.signal;\n const usage: Awaited<ReturnType<typeof runAgentLoop>> = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n model: opts.model,\n };\n\n const addUsage = (next: Awaited<ReturnType<typeof runAgentLoop>>) => {\n usage.inputTokens += next.inputTokens;\n usage.outputTokens += next.outputTokens;\n usage.cacheReadTokens += next.cacheReadTokens;\n usage.cacheWriteTokens += next.cacheWriteTokens;\n usage.model = next.model;\n };\n\n const localTurnEvents: AgentChatEvent[] = [];\n let attempts = 0;\n // Tracks whether the most recent attempt ended by scheduling another\n // continuation (soft-timeout or resumable error → `continue`) rather than\n // returning a finished turn. When the loop then exits because the budget is\n // exhausted (NOT because the user aborted and NOT because the turn finished),\n // this is the silent give-up case: emit a loud terminal so the user sees an\n // unambiguous \"stopped before finishing\" instead of a bare done/\"…\".\n let lastAttemptWasUnfinishedContinuation = false;\n while (!upstreamSignal.aborted && attempts < MAX_RUN_LOOP_CONTINUATIONS) {\n attempts++;\n lastAttemptWasUnfinishedContinuation = false;\n const controller = new AbortController();\n const abortFromUpstream = () => controller.abort();\n if (upstreamSignal.aborted) {\n controller.abort();\n } else {\n upstreamSignal.addEventListener(\"abort\", abortFromUpstream, {\n once: true,\n });\n }\n\n let softTimedOut = false;\n const timer = setTimeout(() => {\n if (controller.signal.aborted) return;\n softTimedOut = true;\n controller.abort();\n }, timeoutMs);\n\n const attemptStartIndex = localTurnEvents.length;\n const send = (event: AgentChatEvent) => {\n localTurnEvents.push(event);\n opts.send(event);\n };\n\n try {\n const nextUsage = await runAgentLoop({\n ...stableOpts,\n send,\n signal: controller.signal,\n });\n addUsage(nextUsage);\n const attemptEvents = localTurnEvents.slice(attemptStartIndex);\n const internalContinuationReason =\n internalContinuationReasonForAttempt(attemptEvents);\n if (internalContinuationReason && !upstreamSignal.aborted) {\n lastAttemptWasUnfinishedContinuation = true;\n const continuationEvents = [...localTurnEvents];\n if (\n !(await hasCompletedSideEffectToolCallInCurrentTurn(\n opts.threadId,\n continuationEvents,\n ))\n ) {\n opts.send({ type: \"clear\" });\n }\n await appendContinuationAndJournal(\n opts.messages,\n internalContinuationReason,\n opts.threadId,\n continuationEvents,\n );\n continue;\n }\n if (softTimedOut && !upstreamSignal.aborted) {\n lastAttemptWasUnfinishedContinuation = true;\n await appendContinuationAndJournal(\n opts.messages,\n \"run_timeout\",\n opts.threadId,\n localTurnEvents,\n );\n continue;\n }\n return usage;\n } catch (err) {\n if (softTimedOut && !upstreamSignal.aborted) {\n // Clear partial text the client received before the abort so the\n // resumed model doesn't re-emit it and produce duplicated output.\n lastAttemptWasUnfinishedContinuation = true;\n if (\n !(await hasCompletedSideEffectToolCallInCurrentTurn(\n opts.threadId,\n localTurnEvents,\n ))\n ) {\n opts.send({ type: \"clear\" });\n }\n await appendContinuationAndJournal(\n opts.messages,\n \"run_timeout\",\n opts.threadId,\n localTurnEvents,\n );\n continue;\n }\n // Resumable transport / gateway interruptions: the LLM call was cut off\n // mid-stream (gateway 45s timeout, socket hang up, function-level\n // timeout that didn't trip our soft timer first). Treat it the same way\n // as a soft timeout — append a \"continue from where you left off\" nudge\n // and let the loop run another LLM call. The conversation prefix up to\n // the cut-off is preserved in opts.messages, and Anthropic's prompt\n // cache makes the resume call much faster.\n //\n // Emit 'clear' so any partial streamed text is discarded on the client\n // before the model resumes. Without this the model restarts its sentence\n // from scratch and the fold produces duplicated text in one message\n // (the partial text was already sent to the client but never entered\n // the in-memory messages array, so the next attempt re-emits it).\n if (!upstreamSignal.aborted && isResumableEngineError(err)) {\n lastAttemptWasUnfinishedContinuation = true;\n if (\n !(await hasCompletedSideEffectToolCallInCurrentTurn(\n opts.threadId,\n localTurnEvents,\n ))\n ) {\n opts.send({ type: \"clear\" });\n }\n await appendContinuationAndJournal(\n opts.messages,\n continuationReasonForResumableError(err),\n opts.threadId,\n localTurnEvents,\n );\n continue;\n }\n throw err;\n } finally {\n clearTimeout(timer);\n upstreamSignal.removeEventListener(\"abort\", abortFromUpstream);\n }\n }\n\n // The loop exited without a clean return. If the user aborted, that's a Stop —\n // stay silent. Otherwise we only get here by exhausting\n // MAX_RUN_LOOP_CONTINUATIONS while the last attempt was still trying to\n // continue (soft-timeout / resumable error). That is the genuinely-silent\n // give-up the run-manager would otherwise report as a clean `done`: emit a\n // loud, non-auto-continuing terminal so the user knows the turn stopped\n // before finishing and nothing was partially saved by the run itself.\n if (!upstreamSignal.aborted && lastAttemptWasUnfinishedContinuation) {\n // Discard any partial text already streamed for the unfinished attempt so\n // the terminal message stands alone instead of trailing a half sentence.\n // Preserve completed tool cards: they are the user's only durable proof\n // that a side effect landed before the final assistant note timed out.\n if (\n !(await hasCompletedSideEffectToolCallInCurrentTurn(\n opts.threadId,\n localTurnEvents,\n ))\n ) {\n opts.send({ type: \"clear\" });\n }\n opts.send({\n type: \"error\",\n error: RUN_BUDGET_EXHAUSTED_MESSAGE,\n errorCode: RUN_BUDGET_EXHAUSTED_ERROR_CODE,\n recoverable: true,\n });\n }\n\n return usage;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"run-loop-with-resume.js","sourceRoot":"","sources":["../../src/agent/run-loop-with-resume.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Wraps `runAgentLoop` with two layered recovery mechanisms so a single hosted\n * invocation can survive interruptions without showing the user a dead chat:\n *\n * 1. **Soft timeout** — an inner timer that aborts the LLM call before the\n * hosting function's hard limit (Lambda 75s, Vercel 60s, etc.) so we have a\n * chance to gracefully wind down and append a continuation nudge. Without\n * this the function gets killed mid-stream and the user sees a frozen\n * spinner.\n *\n * 2. **Resumable-error continuation** — when the LLM call errors with a\n * transport- or gateway-level interruption (Builder gateway 45s timeout,\n * socket hang up, ECONNRESET, upstream 5xx that survived engine retries),\n * we save the conversation prefix, append a \"continue from where you left\n * off\" message, and run another LLM call. Anthropic's prompt cache makes\n * the resume call dramatically faster than the cold first attempt, and the\n * agent gets explicit context that it was cut off so it doesn't re-do\n * completed work.\n *\n * Both paths route through `appendAgentLoopContinuation` so the agent sees a\n * uniform \"continue\" instruction regardless of which recovery fired.\n */\n\nimport type { EngineMessage } from \"./engine/types.js\";\nimport {\n runAgentLoop,\n appendAgentLoopContinuation,\n isResumableEngineError,\n continuationReasonForResumableError,\n lastUnfinishedPreparingActionToolFromEvents,\n resolveFinalResponseGuardRequestText,\n type AgentLoopContinuationReason,\n} from \"./production-agent.js\";\nimport { resolveRunSoftTimeoutMs } from \"./run-manager.js\";\nimport type { ResolveRunSoftTimeoutOptions } from \"./run-manager.js\";\nimport { getCurrentTurnEventsForThread } from \"./run-store.js\";\nimport {\n classifyToolCallJournal,\n buildResumeJournalNote,\n} from \"./tool-call-journal.js\";\nimport type { AgentChatEvent } from \"./types.js\";\n\nasync function readCurrentTurnEventsForResume(\n threadId: string | undefined,\n localEvents: readonly AgentChatEvent[] = [],\n): Promise<AgentChatEvent[]> {\n let persistedEvents: AgentChatEvent[] = [];\n try {\n persistedEvents = threadId\n ? await getCurrentTurnEventsForThread(threadId)\n : [];\n } catch {\n persistedEvents = [];\n }\n if (localEvents.length === 0) return persistedEvents;\n const seen = new Set(persistedEvents.map((event) => JSON.stringify(event)));\n return [\n ...persistedEvents,\n ...localEvents.filter((event) => !seen.has(JSON.stringify(event))),\n ];\n}\n\nfunction actionPreparationContinuationOptions(\n events: readonly AgentChatEvent[],\n): { actionPreparationTool?: string } {\n const actionPreparationTool =\n lastUnfinishedPreparingActionToolFromEvents(events);\n return actionPreparationTool ? { actionPreparationTool } : {};\n}\n\n/**\n * Derive the per-turn tool-call journal from the durable run-event ledger and,\n * when there is anything to report, append a STRUCTURED note to the message\n * prefix so the resumed model:\n * - does NOT re-execute tool calls that already completed (avoiding duplicate\n * side effects like re-sending an email or re-creating a ticket), and\n * - is explicitly told about any tool call that started but whose outcome was\n * never recorded (\"interrupted, unknown outcome\") so it can decide.\n *\n * This is additive to the existing \"continue from where you left off\" nudge —\n * it is appended right after it. When the journal is empty (no completed or\n * interrupted tool calls — e.g. a turn with no tool activity, or a clean\n * continuation), nothing extra is appended and resume behavior is byte-for-byte\n * what it was before. Best-effort: any ledger read/parse failure is swallowed so\n * a journal hiccup can never block a recovery that would otherwise succeed.\n *\n * This prompt-level journal is paired with tool-layer enforcement in\n * production-agent.ts/runToolCall, which refuses to re-execute a journaled-\n * complete write tool (returning the journaled result instead). See\n * `tool-call-journal.ts` (`findCompletedJournalEntry`) for the keying used.\n */\nfunction appendToolCallJournalNote(\n messages: EngineMessage[],\n events: readonly AgentChatEvent[],\n): void {\n try {\n if (events.length === 0) return;\n const journal = classifyToolCallJournal(events);\n const note = buildResumeJournalNote(journal);\n if (!note) return;\n messages.push({\n role: \"user\",\n content: [{ type: \"text\", text: note }],\n });\n } catch {\n // The journal is a hardening layer, never a gate. A failed ledger read or\n // parse must not break the resume that the continuation nudge already set\n // up — the model still continues, just without the structured journal.\n }\n}\n\nasync function appendContinuationAndJournal(\n messages: EngineMessage[],\n reason: AgentLoopContinuationReason,\n threadId: string | undefined,\n localEvents: readonly AgentChatEvent[] = [],\n): Promise<void> {\n const events = await readCurrentTurnEventsForResume(threadId, localEvents);\n appendAgentLoopContinuation(\n messages,\n reason,\n actionPreparationContinuationOptions(events),\n );\n appendToolCallJournalNote(messages, events);\n}\n\n/**\n * Rebuild the same safe continuation context for a logical turn that resumes\n * in a fresh hosted invocation.\n */\nexport async function appendDurableContinuationContext(\n messages: EngineMessage[],\n reason: AgentLoopContinuationReason,\n threadId: string,\n): Promise<void> {\n await appendContinuationAndJournal(messages, reason, threadId);\n}\n\nasync function hasCompletedSideEffectToolCallInCurrentTurn(\n threadId: string | undefined,\n localEvents: readonly AgentChatEvent[] = [],\n): Promise<boolean> {\n try {\n const events = await readCurrentTurnEventsForResume(threadId, localEvents);\n if (events.length === 0) return false;\n return events.some(\n (event) =>\n event.type === \"tool_done\" &&\n event.completedSideEffect === true &&\n event.isError !== true,\n );\n } catch {\n return false;\n }\n}\n\nfunction internalContinuationReasonForAttempt(\n events: readonly AgentChatEvent[],\n): AgentLoopContinuationReason | undefined {\n const last = events.at(-1);\n if (last?.type !== \"auto_continue\") return undefined;\n if (\n last.reason === \"run_timeout\" ||\n last.reason === \"loop_limit\" ||\n last.reason === \"no_progress\" ||\n last.reason === \"stream_ended\" ||\n last.reason === \"gateway_timeout\" ||\n last.reason === \"network_interrupted\"\n ) {\n return last.reason;\n }\n return undefined;\n}\n\n/**\n * Cap on continuation iterations inside a single\n * `runAgentLoopDirectWithSoftTimeout` invocation. The host's hard function\n * timeout usually bounds this naturally — but a defensive cap prevents an\n * instant-error spiral from looping forever inside hosting environments with a\n * generous budget.\n *\n * 6 leaves room for: 1 normal completion + a few resume rounds for design\n * generation (prompt + 3 variants ≈ 4 LLM calls), with a small safety margin.\n */\nexport const MAX_RUN_LOOP_CONTINUATIONS = 6;\n\n/** Machine-readable code carried on the give-up terminal `error` event so the\n * client renders a loud \"stopped before finishing\" terminal instead of an\n * ambiguous silent stall. Deliberately NOT in the client's auto-recoverable\n * allow-list (`isAutoRecoverableError`) so it terminates the chain rather than\n * looping another POST that would hit the same wall. */\nexport const RUN_BUDGET_EXHAUSTED_ERROR_CODE = \"run_budget_exhausted\";\n\n/** User-facing terminal message when a hosted run is cut off mid-step and\n * exhausts its in-invocation continuation budget without finishing. Generic and\n * framework-level (not app-specific). Mirrors the `reliable-mutations` skill's\n * \"fail loud, retry as a single bulk action\" guidance so the user understands\n * the turn stopped before finishing without implying that earlier completed\n * tool calls did not persist. */\nexport const RUN_BUDGET_EXHAUSTED_MESSAGE =\n \"I ran out of time before finishing this step. \" +\n \"I stopped rather than keep retrying silently. \" +\n \"Check any completed tool cards above before retrying, ideally as one smaller follow-up.\";\n\n/**\n * Internal entry point used by the agent-chat plugin's run handler. Wraps\n * `runAgentLoop` with soft-timeout + resumable-error continuation recovery.\n *\n * The `softTimeoutMs` argument falls back to `resolveRunSoftTimeoutMs(...)` so\n * different hosting environments (Lambda, Vercel, Cloudflare, local dev) get\n * an appropriate inner budget. Setting it to <= 0 disables both layers — the\n * call goes straight to `runAgentLoop` with no wrapping.\n */\nexport async function runAgentLoopDirectWithSoftTimeout(\n opts: Parameters<typeof runAgentLoop>[0],\n softTimeoutMs?: number,\n timeoutOptions?: ResolveRunSoftTimeoutOptions,\n): Promise<Awaited<ReturnType<typeof runAgentLoop>>> {\n const finalResponseGuardRequestText =\n opts.finalResponseGuardRequestText ??\n resolveFinalResponseGuardRequestText(opts.messages);\n const stableOpts = { ...opts, finalResponseGuardRequestText };\n const timeoutMs = resolveRunSoftTimeoutMs(softTimeoutMs, timeoutOptions);\n if (timeoutMs <= 0) return runAgentLoop(stableOpts);\n\n const upstreamSignal = opts.signal;\n const usage: Awaited<ReturnType<typeof runAgentLoop>> = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n model: opts.model,\n };\n\n const addUsage = (next: Awaited<ReturnType<typeof runAgentLoop>>) => {\n usage.inputTokens += next.inputTokens;\n usage.outputTokens += next.outputTokens;\n usage.cacheReadTokens += next.cacheReadTokens;\n usage.cacheWriteTokens += next.cacheWriteTokens;\n usage.model = next.model;\n };\n\n const localTurnEvents: AgentChatEvent[] = [];\n let attempts = 0;\n // Tracks whether the most recent attempt ended by scheduling another\n // continuation (soft-timeout or resumable error → `continue`) rather than\n // returning a finished turn. When the loop then exits because the budget is\n // exhausted (NOT because the user aborted and NOT because the turn finished),\n // this is the silent give-up case: emit a loud terminal so the user sees an\n // unambiguous \"stopped before finishing\" instead of a bare done/\"…\".\n let lastAttemptWasUnfinishedContinuation = false;\n while (!upstreamSignal.aborted && attempts < MAX_RUN_LOOP_CONTINUATIONS) {\n attempts++;\n lastAttemptWasUnfinishedContinuation = false;\n const controller = new AbortController();\n const abortFromUpstream = () => controller.abort();\n if (upstreamSignal.aborted) {\n controller.abort();\n } else {\n upstreamSignal.addEventListener(\"abort\", abortFromUpstream, {\n once: true,\n });\n }\n\n let softTimedOut = false;\n const timer = setTimeout(() => {\n if (controller.signal.aborted) return;\n softTimedOut = true;\n controller.abort();\n }, timeoutMs);\n\n const attemptStartIndex = localTurnEvents.length;\n const send = (event: AgentChatEvent) => {\n localTurnEvents.push(event);\n opts.send(event);\n };\n\n try {\n const nextUsage = await runAgentLoop({\n ...stableOpts,\n send,\n signal: controller.signal,\n });\n addUsage(nextUsage);\n const attemptEvents = localTurnEvents.slice(attemptStartIndex);\n const internalContinuationReason =\n internalContinuationReasonForAttempt(attemptEvents);\n if (internalContinuationReason && !upstreamSignal.aborted) {\n lastAttemptWasUnfinishedContinuation = true;\n const continuationEvents = [...localTurnEvents];\n if (\n !(await hasCompletedSideEffectToolCallInCurrentTurn(\n opts.threadId,\n continuationEvents,\n ))\n ) {\n opts.send({ type: \"clear\" });\n }\n await appendContinuationAndJournal(\n opts.messages,\n internalContinuationReason,\n opts.threadId,\n continuationEvents,\n );\n continue;\n }\n if (softTimedOut && !upstreamSignal.aborted) {\n lastAttemptWasUnfinishedContinuation = true;\n await appendContinuationAndJournal(\n opts.messages,\n \"run_timeout\",\n opts.threadId,\n localTurnEvents,\n );\n continue;\n }\n return usage;\n } catch (err) {\n if (softTimedOut && !upstreamSignal.aborted) {\n // Clear partial text the client received before the abort so the\n // resumed model doesn't re-emit it and produce duplicated output.\n lastAttemptWasUnfinishedContinuation = true;\n if (\n !(await hasCompletedSideEffectToolCallInCurrentTurn(\n opts.threadId,\n localTurnEvents,\n ))\n ) {\n opts.send({ type: \"clear\" });\n }\n await appendContinuationAndJournal(\n opts.messages,\n \"run_timeout\",\n opts.threadId,\n localTurnEvents,\n );\n continue;\n }\n // Resumable transport / gateway interruptions: the LLM call was cut off\n // mid-stream (gateway 45s timeout, socket hang up, function-level\n // timeout that didn't trip our soft timer first). Treat it the same way\n // as a soft timeout — append a \"continue from where you left off\" nudge\n // and let the loop run another LLM call. The conversation prefix up to\n // the cut-off is preserved in opts.messages, and Anthropic's prompt\n // cache makes the resume call much faster.\n //\n // Emit 'clear' so any partial streamed text is discarded on the client\n // before the model resumes. Without this the model restarts its sentence\n // from scratch and the fold produces duplicated text in one message\n // (the partial text was already sent to the client but never entered\n // the in-memory messages array, so the next attempt re-emits it).\n if (!upstreamSignal.aborted && isResumableEngineError(err)) {\n lastAttemptWasUnfinishedContinuation = true;\n if (\n !(await hasCompletedSideEffectToolCallInCurrentTurn(\n opts.threadId,\n localTurnEvents,\n ))\n ) {\n opts.send({ type: \"clear\" });\n }\n await appendContinuationAndJournal(\n opts.messages,\n continuationReasonForResumableError(err),\n opts.threadId,\n localTurnEvents,\n );\n continue;\n }\n throw err;\n } finally {\n clearTimeout(timer);\n upstreamSignal.removeEventListener(\"abort\", abortFromUpstream);\n }\n }\n\n // The loop exited without a clean return. If the user aborted, that's a Stop —\n // stay silent. Otherwise we only get here by exhausting\n // MAX_RUN_LOOP_CONTINUATIONS while the last attempt was still trying to\n // continue (soft-timeout / resumable error). That is the genuinely-silent\n // give-up the run-manager would otherwise report as a clean `done`: emit a\n // loud, non-auto-continuing terminal so the user knows the turn stopped\n // before finishing and nothing was partially saved by the run itself.\n if (!upstreamSignal.aborted && lastAttemptWasUnfinishedContinuation) {\n // Discard any partial text already streamed for the unfinished attempt so\n // the terminal message stands alone instead of trailing a half sentence.\n // Preserve completed tool cards: they are the user's only durable proof\n // that a side effect landed before the final assistant note timed out.\n if (\n !(await hasCompletedSideEffectToolCallInCurrentTurn(\n opts.threadId,\n localTurnEvents,\n ))\n ) {\n opts.send({ type: \"clear\" });\n }\n opts.send({\n type: \"error\",\n error: RUN_BUDGET_EXHAUSTED_MESSAGE,\n errorCode: RUN_BUDGET_EXHAUSTED_ERROR_CODE,\n recoverable: true,\n });\n }\n\n return usage;\n}\n"]}
|
package/dist/cli/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import path from "path";
|
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import * as Sentry from "@sentry/node";
|
|
8
8
|
import { resolveDeployPostBuildInvocation } from "./deploy-build.js";
|
|
9
|
+
import { shouldTrackCliRun } from "./telemetry-routing.js";
|
|
9
10
|
import { createCliTelemetry } from "./telemetry.js";
|
|
10
11
|
// Resolve version once at module scope — used by both --version and --help
|
|
11
12
|
let _version = "unknown";
|
|
@@ -524,7 +525,8 @@ function runBuildStep(cmd, cmdArgs, opts) {
|
|
|
524
525
|
});
|
|
525
526
|
});
|
|
526
527
|
}
|
|
527
|
-
|
|
528
|
+
if (shouldTrackCliRun(command, args))
|
|
529
|
+
trackCli("cli.run");
|
|
528
530
|
switch (command) {
|
|
529
531
|
case "dev": {
|
|
530
532
|
if (isWorkspaceRoot()) {
|