@agent-native/core 0.84.13 → 0.84.16
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 +1 -1
- package/corpus/core/CHANGELOG.md +25 -0
- package/corpus/core/package.json +2 -2
- package/corpus/core/src/agent/durable-background.ts +18 -5
- package/corpus/core/src/agent/engine/builder-engine.ts +7 -3
- package/corpus/core/src/agent/production-agent.ts +48 -7
- package/corpus/core/src/agent/run-manager.ts +39 -2
- package/corpus/core/src/agent/run-store.ts +55 -11
- package/corpus/core/src/client/AssistantChat.tsx +30 -4
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +11 -3
- package/corpus/core/src/client/agent-chat-adapter.ts +21 -3
- package/corpus/core/src/client/agent-chat.ts +18 -3
- package/corpus/core/src/client/blocks/library/diagram.tsx +3 -3
- package/corpus/core/src/client/chat/message-components.tsx +17 -0
- package/corpus/core/src/client/use-agent-engine-configured.ts +7 -4
- package/corpus/core/src/db/client.ts +10 -1
- package/corpus/core/src/observability/traces.ts +17 -3
- package/corpus/core/src/server/agent-chat-plugin.ts +80 -44
- package/corpus/core/src/styles/blocks.css +44 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +18 -24
- package/corpus/templates/analytics/changelog/2026-07-01-dashboard-charts-no-longer-show-future-dates-when-clients-se.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-01-dashboard-filters-take-less-space-and-keep-view-actions-tucked.md +6 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +34 -9
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +6 -2
- package/corpus/templates/analytics/server/lib/session-replay.ts +109 -50
- package/corpus/templates/analytics/server/plugins/db.ts +21 -0
- package/corpus/templates/clips/changelog/2026-07-01-chrome-extension-recordings-stream-uploads-while-recording.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/background.ts +8 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +61 -16
- package/corpus/templates/design/app/pages/DesignEditor.tsx +176 -13
- package/corpus/templates/design/changelog/2026-07-01-exporting-a-design-as-png-or-svg-now-downloads-just-the-sele.md +6 -0
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +44 -1
- package/corpus/templates/plan/changelog/2026-07-01-visual-plan-and-recap-diagram-labels-now-wrap-inside-their-b.md +6 -0
- package/dist/agent/durable-background.d.ts +1 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +17 -3
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +7 -1
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +32 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +28 -3
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +9 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +47 -11
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +8 -2
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +18 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +7 -3
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +19 -3
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-chat.d.ts +7 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +5 -1
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/blocks/library/diagram.js +3 -3
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +3 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +16 -2
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +7 -1
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/client.d.ts +6 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +8 -1
- package/dist/db/client.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/observability/traces.d.ts +5 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +9 -3
- package/dist/observability/traces.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts +2 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +64 -42
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/blocks.css +44 -1
- package/package.json +2 -2
|
@@ -1461,8 +1461,29 @@ export function createAgentChatAdapter(
|
|
|
1461
1461
|
const nextContinuationToolCallId = () =>
|
|
1462
1462
|
`continuation_tc_${++continuationToolCallCounter}`;
|
|
1463
1463
|
|
|
1464
|
+
const runDebugContextDetails = (): string => {
|
|
1465
|
+
const pageOrigin =
|
|
1466
|
+
typeof window !== "undefined" && window.location?.origin
|
|
1467
|
+
? window.location.origin
|
|
1468
|
+
: "";
|
|
1469
|
+
return [
|
|
1470
|
+
`api_url: ${apiUrl}`,
|
|
1471
|
+
pageOrigin ? `page_origin: ${pageOrigin}` : "",
|
|
1472
|
+
tabId ? `tab_id: ${tabId}` : "",
|
|
1473
|
+
threadId ? `thread_id: ${threadId}` : "",
|
|
1474
|
+
`turn_id: ${turnId}`,
|
|
1475
|
+
runId ? `current_run: ${runId}` : "",
|
|
1476
|
+
attemptedRunIds.length > 0
|
|
1477
|
+
? `attempted_runs: ${attemptedRunIds.join(", ")}`
|
|
1478
|
+
: "",
|
|
1479
|
+
]
|
|
1480
|
+
.filter(Boolean)
|
|
1481
|
+
.join("\n");
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1464
1484
|
const connectionRecoveryDetails = (): string => {
|
|
1465
1485
|
return [
|
|
1486
|
+
runDebugContextDetails(),
|
|
1466
1487
|
lastAutoContinueReason
|
|
1467
1488
|
? `last_auto_continue_reason: ${lastAutoContinueReason}`
|
|
1468
1489
|
: "",
|
|
@@ -1488,9 +1509,6 @@ export function createAgentChatAdapter(
|
|
|
1488
1509
|
? `activity_trail: ${formatActivityTrail(lastActivityTrail)}`
|
|
1489
1510
|
: "",
|
|
1490
1511
|
`total_transient_continuations: ${totalTransientContinuationAttempts}`,
|
|
1491
|
-
attemptedRunIds.length > 0
|
|
1492
|
-
? `attempted_runs: ${attemptedRunIds.join(", ")}`
|
|
1493
|
-
: "",
|
|
1494
1512
|
]
|
|
1495
1513
|
.filter(Boolean)
|
|
1496
1514
|
.join("\n");
|
|
@@ -113,6 +113,13 @@ export interface AgentChatContextSetOptions extends AgentChatContextItem {
|
|
|
113
113
|
* Defaults to true so the user can see the staged context.
|
|
114
114
|
*/
|
|
115
115
|
openSidebar?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Whether to move keyboard focus into the composer when staging the item.
|
|
118
|
+
* Defaults to true. Pass `false` for context that mirrors ambient UI state
|
|
119
|
+
* (e.g. a canvas element selection) so staging never steals focus from an
|
|
120
|
+
* unrelated editor — such as an inline text editor in a design canvas.
|
|
121
|
+
*/
|
|
122
|
+
focus?: boolean;
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
/** @deprecated Use `AgentChatContextSetOptions` instead. */
|
|
@@ -865,9 +872,17 @@ export function setAgentChatContextItem(
|
|
|
865
872
|
publishAgentChatContextItems(
|
|
866
873
|
withReplacedAgentChatContextItem(agentChatContextState.items, item),
|
|
867
874
|
);
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
875
|
+
// Forward an explicit `focus: false` so the receiving composer can stage the
|
|
876
|
+
// chip without stealing focus. Focus stays enabled by default (field omitted)
|
|
877
|
+
// for every existing caller.
|
|
878
|
+
const messageData = opts.focus === false ? { ...item, focus: false } : item;
|
|
879
|
+
postAgentChatContextMessage(
|
|
880
|
+
AGENT_CHAT_SET_CONTEXT_MESSAGE_TYPE,
|
|
881
|
+
messageData,
|
|
882
|
+
{
|
|
883
|
+
openSidebar: opts.openSidebar !== false,
|
|
884
|
+
},
|
|
885
|
+
);
|
|
871
886
|
}
|
|
872
887
|
|
|
873
888
|
/** @deprecated Use `setAgentChatContextItem` instead. */
|
|
@@ -291,7 +291,7 @@ function PositionedDiagram({
|
|
|
291
291
|
return (
|
|
292
292
|
<span
|
|
293
293
|
key={`${edge.from}-${edge.to}-${index}-label`}
|
|
294
|
-
className="absolute z-10 max-w-[130px] -translate-x-1/2 -translate-y-1/2 rounded-full border border-border bg-background px-2 py-0.5 text-center text-[11px] font-semibold text-muted-foreground shadow-sm"
|
|
294
|
+
className="absolute z-10 max-w-[130px] -translate-x-1/2 -translate-y-1/2 break-words rounded-full border border-border bg-background px-2 py-0.5 text-center text-[11px] font-semibold text-muted-foreground shadow-sm [overflow-wrap:anywhere]"
|
|
295
295
|
style={{
|
|
296
296
|
left: `${(from.displayX + to.displayX) / 2}%`,
|
|
297
297
|
top: `${(from.displayY + to.displayY) / 2}%`,
|
|
@@ -305,7 +305,7 @@ function PositionedDiagram({
|
|
|
305
305
|
{displayNodesForDirection.map((node, index) => (
|
|
306
306
|
<article
|
|
307
307
|
key={node.id}
|
|
308
|
-
className="absolute z-20 -translate-x-1/2 -translate-y-1/2 rounded-xl border-2 border-border bg-background p-3 text-foreground shadow-sm"
|
|
308
|
+
className="absolute z-20 -translate-x-1/2 -translate-y-1/2 break-words rounded-xl border-2 border-border bg-background p-3 text-foreground shadow-sm [overflow-wrap:anywhere]"
|
|
309
309
|
style={{
|
|
310
310
|
left: `${node.displayX}%`,
|
|
311
311
|
top: `${node.displayY}%`,
|
|
@@ -485,7 +485,7 @@ function SequenceDiagram({
|
|
|
485
485
|
{next && (
|
|
486
486
|
<div className="grid min-w-[72px] justify-items-center gap-1 text-muted-foreground">
|
|
487
487
|
{edge?.label && (
|
|
488
|
-
<span className="max-w-[96px]
|
|
488
|
+
<span className="max-w-[96px] whitespace-normal break-words rounded-full border border-border px-2 py-0.5 text-center text-[11px] font-semibold [overflow-wrap:anywhere]">
|
|
489
489
|
{edge.label}
|
|
490
490
|
</span>
|
|
491
491
|
)}
|
|
@@ -784,19 +784,31 @@ function assistantMessageStatusIsTerminal(message: {
|
|
|
784
784
|
return statusType === "complete" || statusType === "incomplete";
|
|
785
785
|
}
|
|
786
786
|
|
|
787
|
+
export function assistantMessageHasUnresolvedTool(content: unknown): boolean {
|
|
788
|
+
if (!Array.isArray(content)) return false;
|
|
789
|
+
return content.some((part): boolean => {
|
|
790
|
+
if (!part || typeof part !== "object") return false;
|
|
791
|
+
const record = part as { type?: unknown; result?: unknown };
|
|
792
|
+
return record.type === "tool-call" && record.result === undefined;
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
787
796
|
export function shouldShowAssistantMessageFooter({
|
|
788
797
|
isLast,
|
|
789
798
|
chatRunning,
|
|
790
799
|
hasRenderableContent,
|
|
791
800
|
statusIsTerminal,
|
|
801
|
+
hasUnresolvedTool,
|
|
792
802
|
}: {
|
|
793
803
|
isLast: boolean;
|
|
794
804
|
chatRunning: boolean;
|
|
795
805
|
hasRenderableContent: boolean;
|
|
796
806
|
statusIsTerminal: boolean;
|
|
807
|
+
hasUnresolvedTool?: boolean;
|
|
797
808
|
}): boolean {
|
|
798
809
|
if (!hasRenderableContent) return false;
|
|
799
810
|
if (!isLast) return true;
|
|
811
|
+
if (hasUnresolvedTool) return false;
|
|
800
812
|
return !chatRunning && statusIsTerminal;
|
|
801
813
|
}
|
|
802
814
|
|
|
@@ -813,11 +825,13 @@ export function AssistantMessage() {
|
|
|
813
825
|
thread.messages.length > 0 &&
|
|
814
826
|
thread.messages[thread.messages.length - 1].id === msg.id;
|
|
815
827
|
const hasRenderableContent = assistantMessageHasRenderableContent(msg);
|
|
828
|
+
const hasUnresolvedTool = assistantMessageHasUnresolvedTool(msg.content);
|
|
816
829
|
const isComplete = shouldShowAssistantMessageFooter({
|
|
817
830
|
isLast,
|
|
818
831
|
chatRunning,
|
|
819
832
|
hasRenderableContent,
|
|
820
833
|
statusIsTerminal: assistantMessageStatusIsTerminal(msg),
|
|
834
|
+
hasUnresolvedTool,
|
|
821
835
|
});
|
|
822
836
|
const cpCtx = React.useContext(CheckpointContext);
|
|
823
837
|
|
|
@@ -907,6 +921,9 @@ export function AssistantMessage() {
|
|
|
907
921
|
{isComplete && hasCodeAgentTools && msgContent && (
|
|
908
922
|
<FilesChangedSummary parts={msgContent} />
|
|
909
923
|
)}
|
|
924
|
+
{isLast && hasUnresolvedTool && !chatRunning && (
|
|
925
|
+
<RunningActivityStatus label="Thinking" />
|
|
926
|
+
)}
|
|
910
927
|
</div>
|
|
911
928
|
{isComplete && (
|
|
912
929
|
<div className="mt-1 flex items-center justify-between">
|
|
@@ -35,10 +35,13 @@ async function fetchStatusJson(
|
|
|
35
35
|
}, timeoutMs);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
)
|
|
38
|
+
// Never serve a stale status from the HTTP cache: this is re-fetched right
|
|
39
|
+
// after a provider connects, and a cached "missing" would keep the composer
|
|
40
|
+
// gate and error banner pinned even though a provider is now configured.
|
|
41
|
+
const request = fetch(agentNativePath(path), {
|
|
42
|
+
cache: "no-store",
|
|
43
|
+
...(controller ? { signal: controller.signal } : {}),
|
|
44
|
+
})
|
|
42
45
|
.then((r) => (r.ok ? r.json() : null))
|
|
43
46
|
.catch(() => null)
|
|
44
47
|
.finally(() => {
|
|
@@ -837,13 +837,22 @@ export function neonPoolMax(): number {
|
|
|
837
837
|
* (agent/durable-background.ts), replicated here to avoid a `db` → `agent`
|
|
838
838
|
* import cycle. Keep the signals in sync with that function.
|
|
839
839
|
*/
|
|
840
|
-
function isBackgroundFunctionPoolContext(): boolean {
|
|
840
|
+
export function isBackgroundFunctionPoolContext(): boolean {
|
|
841
841
|
if (
|
|
842
842
|
(globalThis as Record<string, unknown>)
|
|
843
843
|
.__AGENT_NATIVE_BACKGROUND_RUNTIME__ === true
|
|
844
844
|
) {
|
|
845
845
|
return true;
|
|
846
846
|
}
|
|
847
|
+
// Set by the HMAC-authenticated agent-chat `_process-run` route before it
|
|
848
|
+
// re-enters the normal chat handler. This mirrors the marker-only runtime
|
|
849
|
+
// proof used by durable-background.ts without importing agent code into db/.
|
|
850
|
+
if (
|
|
851
|
+
(globalThis as Record<string, unknown>)
|
|
852
|
+
.__AGENT_NATIVE_BACKGROUND_RUNTIME_EXPECTED__ === true
|
|
853
|
+
) {
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
847
856
|
const lambdaName = process.env.AWS_LAMBDA_FUNCTION_NAME;
|
|
848
857
|
if (
|
|
849
858
|
typeof lambdaName === "string" &&
|
|
@@ -89,6 +89,14 @@ export async function instrumentAgentLoop(opts: {
|
|
|
89
89
|
* reads. */
|
|
90
90
|
userId: string | null;
|
|
91
91
|
config: ObservabilityConfig;
|
|
92
|
+
classifyError?: (error: unknown) =>
|
|
93
|
+
| {
|
|
94
|
+
status?: "success" | "error";
|
|
95
|
+
errorMessage?: string | null;
|
|
96
|
+
metadata?: Record<string, unknown> | null;
|
|
97
|
+
}
|
|
98
|
+
| null
|
|
99
|
+
| undefined;
|
|
92
100
|
}): Promise<AgentLoopUsage> {
|
|
93
101
|
const { runAgentLoop, loopOpts, runId, threadId, userId, config } = opts;
|
|
94
102
|
const runStart = Date.now();
|
|
@@ -260,11 +268,17 @@ export async function instrumentAgentLoop(opts: {
|
|
|
260
268
|
let usage: AgentLoopUsage | undefined;
|
|
261
269
|
let runStatus: "success" | "error" = "success";
|
|
262
270
|
let errorMessage: string | null = null;
|
|
271
|
+
let runMetadata: Record<string, unknown> | null = null;
|
|
263
272
|
try {
|
|
264
273
|
usage = await runAgentLoop({ ...loopOpts, send: instrumentedSend });
|
|
265
274
|
} catch (err: any) {
|
|
266
|
-
|
|
267
|
-
|
|
275
|
+
const classification = opts.classifyError?.(err) ?? null;
|
|
276
|
+
runStatus = classification?.status ?? "error";
|
|
277
|
+
errorMessage =
|
|
278
|
+
classification?.errorMessage === undefined
|
|
279
|
+
? (err?.message ?? String(err))
|
|
280
|
+
: classification.errorMessage;
|
|
281
|
+
runMetadata = classification?.metadata ?? null;
|
|
268
282
|
throw err;
|
|
269
283
|
} finally {
|
|
270
284
|
const runEnd = Date.now();
|
|
@@ -325,7 +339,7 @@ export async function instrumentAgentLoop(opts: {
|
|
|
325
339
|
durationMs: totalDurationMs,
|
|
326
340
|
status: runStatus,
|
|
327
341
|
errorMessage,
|
|
328
|
-
metadata:
|
|
342
|
+
metadata: runMetadata,
|
|
329
343
|
createdAt: runStart,
|
|
330
344
|
};
|
|
331
345
|
spans.push(parentSpan);
|
|
@@ -34,8 +34,9 @@ import {
|
|
|
34
34
|
} from "../agent/app-model-defaults.js";
|
|
35
35
|
import { DEFAULT_ANTHROPIC_MODEL } from "../agent/default-model.js";
|
|
36
36
|
import {
|
|
37
|
+
AGENT_CHAT_BACKGROUND_RUN_FIELD,
|
|
37
38
|
AGENT_CHAT_PROCESS_RUN_PATH,
|
|
38
|
-
|
|
39
|
+
backgroundRunMarkerExpectsBackgroundRuntime,
|
|
39
40
|
isInBackgroundFunctionRuntime,
|
|
40
41
|
prepareProcessRunRequest,
|
|
41
42
|
} from "../agent/durable-background.js";
|
|
@@ -69,6 +70,7 @@ import {
|
|
|
69
70
|
recordRunDiagnostic,
|
|
70
71
|
RUN_DIAG_STAGE,
|
|
71
72
|
setRunError,
|
|
73
|
+
setRunTerminalReason,
|
|
72
74
|
updateRunStatusIfRunning,
|
|
73
75
|
} from "../agent/run-store.js";
|
|
74
76
|
import { buildRuntimeContextPrompt } from "../agent/runtime-context.js";
|
|
@@ -3876,6 +3878,7 @@ type AgentChatProcessRunFailureDeps = {
|
|
|
3876
3878
|
readBackgroundRunClaim?: typeof readBackgroundRunClaim;
|
|
3877
3879
|
recordRunDiagnostic?: typeof recordRunDiagnostic;
|
|
3878
3880
|
setRunError?: typeof setRunError;
|
|
3881
|
+
setRunTerminalReason?: typeof setRunTerminalReason;
|
|
3879
3882
|
updateRunStatusIfRunning?: typeof updateRunStatusIfRunning;
|
|
3880
3883
|
ensureTerminalRunEvent?: typeof ensureTerminalRunEvent;
|
|
3881
3884
|
};
|
|
@@ -3888,6 +3891,7 @@ export async function finalizeClaimedAgentChatProcessRunFailure(
|
|
|
3888
3891
|
const readClaim = deps.readBackgroundRunClaim ?? readBackgroundRunClaim;
|
|
3889
3892
|
const record = deps.recordRunDiagnostic ?? recordRunDiagnostic;
|
|
3890
3893
|
const setError = deps.setRunError ?? setRunError;
|
|
3894
|
+
const setTerminalReason = deps.setRunTerminalReason ?? setRunTerminalReason;
|
|
3891
3895
|
const updateStatus =
|
|
3892
3896
|
deps.updateRunStatusIfRunning ?? updateRunStatusIfRunning;
|
|
3893
3897
|
const ensureTerminal = deps.ensureTerminalRunEvent ?? ensureTerminalRunEvent;
|
|
@@ -3908,7 +3912,13 @@ export async function finalizeClaimedAgentChatProcessRunFailure(
|
|
|
3908
3912
|
CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT.errorCode,
|
|
3909
3913
|
`${CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT.details} setupError=${message}`,
|
|
3910
3914
|
).catch(() => {});
|
|
3911
|
-
await updateStatus(runId, "errored").catch(() =>
|
|
3915
|
+
const statusUpdated = await updateStatus(runId, "errored").catch(() => false);
|
|
3916
|
+
if (statusUpdated) {
|
|
3917
|
+
await setTerminalReason(
|
|
3918
|
+
runId,
|
|
3919
|
+
CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT.errorCode,
|
|
3920
|
+
).catch(() => {});
|
|
3921
|
+
}
|
|
3912
3922
|
await ensureTerminal(
|
|
3913
3923
|
runId,
|
|
3914
3924
|
CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT,
|
|
@@ -7400,6 +7410,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7400
7410
|
// unreadable Netlify background-function logs — read
|
|
7401
7411
|
// `/runs/active?threadId=...` and inspect `diagStage`.
|
|
7402
7412
|
dispatchMode: run.dispatchMode ?? null,
|
|
7413
|
+
terminalReason: run.terminalReason ?? null,
|
|
7403
7414
|
diagStage: run.diagStage ?? null,
|
|
7404
7415
|
workerStage: workerClaim?.workerStage ?? null,
|
|
7405
7416
|
// Server clock so the client computes "stuck" elapsed time
|
|
@@ -7983,18 +7994,6 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7983
7994
|
setResponseStatus(event, 405);
|
|
7984
7995
|
return { error: "Method not allowed" };
|
|
7985
7996
|
}
|
|
7986
|
-
// DIAGNOSTIC: load the run-store diagnostic recorder. Each stage we
|
|
7987
|
-
// reach is written onto the run row (diag_stage) so a silent failure
|
|
7988
|
-
// INSIDE the Netlify background function — whose logs we cannot read —
|
|
7989
|
-
// is still diagnosable from the client via /runs/active. Best-effort:
|
|
7990
|
-
// the import + every record call is wrapped so diagnostics can never
|
|
7991
|
-
// break the worker path.
|
|
7992
|
-
const diag = await import("../agent/run-store.js")
|
|
7993
|
-
.then((m) => ({
|
|
7994
|
-
record: m.recordRunDiagnostic,
|
|
7995
|
-
stages: m.RUN_DIAG_STAGE,
|
|
7996
|
-
}))
|
|
7997
|
-
.catch(() => null);
|
|
7998
7997
|
|
|
7999
7998
|
// Consume the body ONCE (h3 v2's web Request stream is single-use).
|
|
8000
7999
|
let processBody: any;
|
|
@@ -8005,18 +8004,6 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
8005
8004
|
return { error: "Invalid request body" };
|
|
8006
8005
|
}
|
|
8007
8006
|
|
|
8008
|
-
// Record "the route handler was entered" against the run BEFORE auth
|
|
8009
|
-
// runs. This is the proof the bg-fn invocation actually reached Nitro
|
|
8010
|
-
// (vs. dying at the function entry / never being invoked). The runId
|
|
8011
|
-
// is parsed without authenticating so we can attach it even on a
|
|
8012
|
-
// subsequent auth failure.
|
|
8013
|
-
const diagRunId = extractProcessRunId(processBody);
|
|
8014
|
-
if (diag && diagRunId) {
|
|
8015
|
-
await diag
|
|
8016
|
-
.record(diagRunId, diag.stages.routeEntered)
|
|
8017
|
-
.catch(() => {});
|
|
8018
|
-
}
|
|
8019
|
-
|
|
8020
8007
|
// Validate + HMAC-authenticate the self-dispatch and prepare the
|
|
8021
8008
|
// background-worker body. Pure decision (unit-tested in
|
|
8022
8009
|
// durable-background.spec.ts); the route only wires it to h3.
|
|
@@ -8031,6 +8018,12 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
8031
8018
|
// bypassing session auth) inside the unreadable bg function would
|
|
8032
8019
|
// leave the run to time out with NO clue. The detail carries the
|
|
8033
8020
|
// status + whether A2A_SECRET is even present in this isolate.
|
|
8021
|
+
const diag = await import("../agent/run-store.js")
|
|
8022
|
+
.then((m) => ({
|
|
8023
|
+
record: m.recordRunDiagnostic,
|
|
8024
|
+
stages: m.RUN_DIAG_STAGE,
|
|
8025
|
+
}))
|
|
8026
|
+
.catch(() => null);
|
|
8034
8027
|
if (diag && prepared.runId) {
|
|
8035
8028
|
const a2aPresent = Boolean(
|
|
8036
8029
|
process.env.A2A_SECRET && process.env.A2A_SECRET.length > 0,
|
|
@@ -8047,27 +8040,58 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
8047
8040
|
return { error: prepared.error };
|
|
8048
8041
|
}
|
|
8049
8042
|
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8043
|
+
const preparedMarker = (prepared.body as Record<string, unknown>)[
|
|
8044
|
+
AGENT_CHAT_BACKGROUND_RUN_FIELD
|
|
8045
|
+
];
|
|
8046
|
+
const expectsBackgroundRuntime =
|
|
8047
|
+
backgroundRunMarkerExpectsBackgroundRuntime(preparedMarker);
|
|
8048
|
+
const runtimeGlobals = globalThis as Record<string, unknown>;
|
|
8049
|
+
const hadExpectedRuntimeMarker = Object.prototype.hasOwnProperty.call(
|
|
8050
|
+
runtimeGlobals,
|
|
8051
|
+
"__AGENT_NATIVE_BACKGROUND_RUNTIME_EXPECTED__",
|
|
8052
|
+
);
|
|
8053
|
+
const previousExpectedRuntimeMarker =
|
|
8054
|
+
runtimeGlobals.__AGENT_NATIVE_BACKGROUND_RUNTIME_EXPECTED__;
|
|
8055
|
+
if (expectsBackgroundRuntime) {
|
|
8056
|
+
runtimeGlobals.__AGENT_NATIVE_BACKGROUND_RUNTIME_EXPECTED__ = true;
|
|
8056
8057
|
}
|
|
8057
8058
|
|
|
8058
|
-
// Stash the verified+augmented body for the handler — the body stream
|
|
8059
|
-
// is already consumed, so the handler reads this instead.
|
|
8060
|
-
(event as any).context = (event as any).context ?? {};
|
|
8061
|
-
(event as any).context.__agentChatBackgroundBody = prepared.body;
|
|
8062
|
-
|
|
8063
|
-
// Durable owner context: this self-dispatch is cookieless (HMAC-only).
|
|
8064
|
-
// Resolve the owner from the persisted run row, never the request body,
|
|
8065
|
-
// then invoke the normal handler. The shared agent-run context helper
|
|
8066
|
-
// expands that owner into the same user/org AsyncLocalStorage context the
|
|
8067
|
-
// foreground request uses, so credential and data scoping stay aligned.
|
|
8068
|
-
await seedBackgroundAgentRunOwnerContext(event, prepared.runId);
|
|
8069
|
-
|
|
8070
8059
|
try {
|
|
8060
|
+
// DIAGNOSTIC: load the run-store diagnostic recorder only after the
|
|
8061
|
+
// authenticated marker has been mirrored into globalThis. run-store
|
|
8062
|
+
// can initialize the DB pool; the pool must see the same background
|
|
8063
|
+
// proof as the agent timeout logic before that happens.
|
|
8064
|
+
const diag = await import("../agent/run-store.js")
|
|
8065
|
+
.then((m) => ({
|
|
8066
|
+
record: m.recordRunDiagnostic,
|
|
8067
|
+
stages: m.RUN_DIAG_STAGE,
|
|
8068
|
+
}))
|
|
8069
|
+
.catch(() => null);
|
|
8070
|
+
|
|
8071
|
+
// Record "the route handler was entered" against the run after auth
|
|
8072
|
+
// succeeds. This is the proof the bg-fn invocation actually reached
|
|
8073
|
+
// Nitro (vs. dying at the function entry / never being invoked).
|
|
8074
|
+
if (diag) {
|
|
8075
|
+
await diag
|
|
8076
|
+
.record(prepared.runId, diag.stages.routeEntered)
|
|
8077
|
+
.catch(() => {});
|
|
8078
|
+
await diag
|
|
8079
|
+
.record(prepared.runId, diag.stages.authPassed)
|
|
8080
|
+
.catch(() => {});
|
|
8081
|
+
}
|
|
8082
|
+
|
|
8083
|
+
// Stash the verified+augmented body for the handler — the body stream
|
|
8084
|
+
// is already consumed, so the handler reads this instead.
|
|
8085
|
+
(event as any).context = (event as any).context ?? {};
|
|
8086
|
+
(event as any).context.__agentChatBackgroundBody = prepared.body;
|
|
8087
|
+
|
|
8088
|
+
// Durable owner context: this self-dispatch is cookieless (HMAC-only).
|
|
8089
|
+
// Resolve the owner from the persisted run row, never the request
|
|
8090
|
+
// body, then invoke the normal handler. The shared agent-run context
|
|
8091
|
+
// helper expands that owner into the same user/org AsyncLocalStorage
|
|
8092
|
+
// context the foreground request uses, so credential and data scoping
|
|
8093
|
+
// stay aligned.
|
|
8094
|
+
await seedBackgroundAgentRunOwnerContext(event, prepared.runId);
|
|
8071
8095
|
return await invokeAgentChatHandler(event);
|
|
8072
8096
|
} catch (err: any) {
|
|
8073
8097
|
console.error("[agent-chat] _process-run failed:", err);
|
|
@@ -8089,6 +8113,18 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
8089
8113
|
);
|
|
8090
8114
|
setResponseStatus(event, 500);
|
|
8091
8115
|
return { error: "process-run failed" };
|
|
8116
|
+
} finally {
|
|
8117
|
+
if (expectsBackgroundRuntime) {
|
|
8118
|
+
if (hadExpectedRuntimeMarker) {
|
|
8119
|
+
runtimeGlobals.__AGENT_NATIVE_BACKGROUND_RUNTIME_EXPECTED__ =
|
|
8120
|
+
previousExpectedRuntimeMarker;
|
|
8121
|
+
} else {
|
|
8122
|
+
Reflect.deleteProperty(
|
|
8123
|
+
runtimeGlobals,
|
|
8124
|
+
"__AGENT_NATIVE_BACKGROUND_RUNTIME_EXPECTED__",
|
|
8125
|
+
);
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8092
8128
|
}
|
|
8093
8129
|
}),
|
|
8094
8130
|
);
|
|
@@ -1355,8 +1355,37 @@
|
|
|
1355
1355
|
color: var(--wf-ink) !important;
|
|
1356
1356
|
}
|
|
1357
1357
|
|
|
1358
|
-
.plan-diagram-frame
|
|
1358
|
+
.plan-diagram-frame
|
|
1359
|
+
:is(
|
|
1360
|
+
.diagram-panel,
|
|
1361
|
+
.diagram-card,
|
|
1362
|
+
.diagram-node,
|
|
1363
|
+
.diagram-box,
|
|
1364
|
+
[class*="card"],
|
|
1365
|
+
[class*="panel"],
|
|
1366
|
+
[class*="box"]
|
|
1367
|
+
) {
|
|
1359
1368
|
max-width: 100%;
|
|
1369
|
+
overflow-wrap: anywhere !important;
|
|
1370
|
+
word-break: break-word;
|
|
1371
|
+
white-space: normal !important;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
.plan-diagram-frame
|
|
1375
|
+
:is(
|
|
1376
|
+
.diagram-panel,
|
|
1377
|
+
.diagram-card,
|
|
1378
|
+
.diagram-node,
|
|
1379
|
+
.diagram-box,
|
|
1380
|
+
[class*="card"],
|
|
1381
|
+
[class*="panel"],
|
|
1382
|
+
[class*="box"]
|
|
1383
|
+
)
|
|
1384
|
+
:is(h1, h2, h3, p, small, strong, span, li) {
|
|
1385
|
+
max-width: 100%;
|
|
1386
|
+
overflow-wrap: anywhere !important;
|
|
1387
|
+
word-break: break-word;
|
|
1388
|
+
white-space: normal !important;
|
|
1360
1389
|
}
|
|
1361
1390
|
|
|
1362
1391
|
/* Base padding so primitive text never touches the box edge, even when an author
|
|
@@ -1422,6 +1451,7 @@
|
|
|
1422
1451
|
display: inline-flex;
|
|
1423
1452
|
align-items: center;
|
|
1424
1453
|
justify-content: center;
|
|
1454
|
+
flex-wrap: wrap;
|
|
1425
1455
|
/* A pill hugs its label. `fit-content` is a definite cross size, so a flex
|
|
1426
1456
|
* column with the default `align-items: stretch` no longer blows the pill out
|
|
1427
1457
|
* to full width (the number chips and short status labels stay pill-shaped).
|
|
@@ -1434,6 +1464,19 @@
|
|
|
1434
1464
|
padding: 3px 11px;
|
|
1435
1465
|
color: var(--wf-ink);
|
|
1436
1466
|
background: var(--wf-paper);
|
|
1467
|
+
overflow-wrap: anywhere !important;
|
|
1468
|
+
text-align: center;
|
|
1469
|
+
white-space: normal !important;
|
|
1470
|
+
word-break: break-word;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
.plan-diagram-frame
|
|
1474
|
+
:is(.diagram-pill, [class*="pill"], [class*="badge"], [class*="chip"])
|
|
1475
|
+
> * {
|
|
1476
|
+
min-width: 0;
|
|
1477
|
+
max-width: 100%;
|
|
1478
|
+
overflow-wrap: anywhere !important;
|
|
1479
|
+
white-space: normal !important;
|
|
1437
1480
|
}
|
|
1438
1481
|
|
|
1439
1482
|
.plan-diagram-frame :is(.diagram-accent, .diagram-pill.accent) {
|
|
@@ -228,18 +228,25 @@ export function DashboardFilterBar({
|
|
|
228
228
|
<Collapsible
|
|
229
229
|
open={filtersOpen}
|
|
230
230
|
onOpenChange={setFiltersOpen}
|
|
231
|
-
className="group rounded-lg border border-border bg-card
|
|
231
|
+
className="group rounded-lg border border-border bg-card px-3 py-2"
|
|
232
232
|
>
|
|
233
|
-
<div className="flex items-
|
|
234
|
-
<
|
|
235
|
-
<
|
|
236
|
-
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
233
|
+
<div className="flex flex-wrap items-end justify-between gap-3">
|
|
234
|
+
<CollapsibleContent className="min-w-0 flex-1">
|
|
235
|
+
<div className="flex flex-wrap gap-3 items-end">
|
|
236
|
+
{uniqueFilters.map((f) => (
|
|
237
|
+
<FilterControl
|
|
238
|
+
key={f.id}
|
|
239
|
+
filter={f}
|
|
240
|
+
vars={vars}
|
|
241
|
+
hasParam={(key) =>
|
|
242
|
+
searchParams.has(FILTER_PARAM_PREFIX + key)
|
|
243
|
+
}
|
|
244
|
+
setValue={(updates) => setParam(updates)}
|
|
245
|
+
/>
|
|
246
|
+
))}
|
|
247
|
+
</div>
|
|
248
|
+
</CollapsibleContent>
|
|
249
|
+
<div className="flex shrink-0 items-center gap-1 self-center opacity-0 transition-opacity focus-within:opacity-100 group-hover:opacity-100 group-data-[state=closed]:opacity-100">
|
|
243
250
|
{onSaveView && filtersActive && (
|
|
244
251
|
<Button
|
|
245
252
|
variant="ghost"
|
|
@@ -284,19 +291,6 @@ export function DashboardFilterBar({
|
|
|
284
291
|
</CollapsibleTrigger>
|
|
285
292
|
</div>
|
|
286
293
|
</div>
|
|
287
|
-
<CollapsibleContent className="pt-3">
|
|
288
|
-
<div className="flex flex-wrap gap-3 items-end">
|
|
289
|
-
{uniqueFilters.map((f) => (
|
|
290
|
-
<FilterControl
|
|
291
|
-
key={f.id}
|
|
292
|
-
filter={f}
|
|
293
|
-
vars={vars}
|
|
294
|
-
hasParam={(key) => searchParams.has(FILTER_PARAM_PREFIX + key)}
|
|
295
|
-
setValue={(updates) => setParam(updates)}
|
|
296
|
-
/>
|
|
297
|
-
))}
|
|
298
|
-
</div>
|
|
299
|
-
</CollapsibleContent>
|
|
300
294
|
</Collapsible>
|
|
301
295
|
|
|
302
296
|
<Dialog open={saveDialogOpen} onOpenChange={setSaveDialogOpen}>
|