@agent-native/core 0.84.10 → 0.84.13
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 +48 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +30 -0
- package/corpus/core/src/agent/production-agent.ts +34 -6
- package/corpus/core/src/agent/run-manager.ts +45 -2
- package/corpus/core/src/agent/run-store.ts +29 -6
- package/corpus/core/src/chat-threads/store.ts +2 -0
- package/corpus/core/src/cli/skills.ts +10 -6
- package/corpus/core/src/client/AssistantChat.tsx +40 -9
- package/corpus/core/src/client/agent-chat-adapter.ts +23 -2
- package/corpus/core/src/client/chat/run-recovery.tsx +52 -44
- package/corpus/core/src/client/chat/tool-call-display.tsx +7 -2
- package/corpus/core/src/client/require-session.tsx +20 -1
- package/corpus/core/src/client/sharing/ShareButton.tsx +9 -3
- package/corpus/core/src/collab/storage.ts +40 -5
- package/corpus/core/src/server/agent-chat-plugin.ts +1 -0
- package/corpus/core/src/server/auth.ts +6 -0
- package/corpus/core/src/shared/streaming-text-smoothing.ts +10 -5
- package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +7 -6
- package/corpus/templates/assets/AGENTS.md +15 -0
- package/corpus/templates/assets/actions/_helpers.ts +3 -1
- package/corpus/templates/assets/actions/create-generation-preset.ts +12 -1
- package/corpus/templates/assets/actions/generate-asset.ts +5 -1
- package/corpus/templates/assets/actions/generate-image-batch.ts +6 -1
- package/corpus/templates/assets/actions/generate-image.ts +18 -11
- package/corpus/templates/assets/actions/open-asset-picker.ts +4 -2
- package/corpus/templates/assets/actions/update-generation-preset.ts +16 -3
- package/corpus/templates/assets/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/assets/app/i18n-data.ts +30 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +42 -7
- package/corpus/templates/assets/app/routes/library.tsx +4 -2
- package/corpus/templates/assets/changelog/2026-07-01-generation-presets-can-now-composite-your-brand-s-canonical-.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-01-tagging-a-preset-now-briefs-the-agent-on-that-preset-s-aesth.md +6 -0
- package/corpus/templates/assets/server/lib/preset-chat-context.ts +118 -0
- package/corpus/templates/assets/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/assets/shared/api.ts +1 -0
- package/corpus/templates/clips/app/components/library/recording-card.tsx +14 -4
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +10 -12
- package/corpus/templates/clips/app/hooks/use-library.ts +7 -4
- package/corpus/templates/clips/app/lib/recording-status.ts +32 -0
- package/corpus/templates/clips/changelog/2026-07-01-dictation-keeps-listening-through-natural-pauses-and-gives-c.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-redoing-a-paused-desktop-recording-no-longer-leaves-the-recording-controls-disabled.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-stalled-clip-uploads-now-update-in-the-library-and-surface-as-failed.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +36 -18
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +99 -27
- package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +4 -43
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +5 -48
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +4 -5
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +15 -6
- package/corpus/templates/design/AGENTS.md +8 -4
- package/corpus/templates/design/actions/create-design.ts +1 -0
- package/corpus/templates/design/actions/duplicate-design.ts +3 -1
- package/corpus/templates/design/actions/edit-design.ts +43 -13
- package/corpus/templates/design/actions/export-html.ts +4 -2
- package/corpus/templates/design/actions/export-pdf.ts +3 -1
- package/corpus/templates/design/actions/export-svg.ts +4 -2
- package/corpus/templates/design/actions/export-zip.ts +10 -3
- package/corpus/templates/design/actions/generate-design.ts +5 -1
- package/corpus/templates/design/actions/present-design-variants.ts +17 -12
- package/corpus/templates/design/actions/update-file.ts +68 -17
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +3 -2
- package/corpus/templates/design/app/components/design/EditPanel.tsx +35 -3
- package/corpus/templates/design/app/components/design/MotionDock.tsx +46 -11
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +80 -30
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +60 -10
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +2 -0
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +4 -0
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +2 -2
- package/corpus/templates/design/app/i18n-data.ts +1 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +493 -109
- package/corpus/templates/design/app/pages/VisualEdit.tsx +4 -16
- package/corpus/templates/design/changelog/2026-07-01-org-members-can-see-design-projects-created-in-their-workspace.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +2 -2
- package/corpus/templates/design/server/plugins/db.ts +9 -0
- package/dist/agent/durable-background.d.ts +3 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +19 -0
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +29 -6
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +40 -2
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +28 -6
- package/dist/agent/run-store.js.map +1 -1
- package/dist/chat-threads/store.d.ts +1 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +2 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +10 -6
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +10 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +26 -8
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +21 -2
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +15 -10
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +1 -0
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +3 -2
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/require-session.d.ts +14 -0
- package/dist/client/require-session.d.ts.map +1 -1
- package/dist/client/require-session.js +19 -1
- package/dist/client/require-session.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +4 -2
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +3 -1
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/collab/storage.d.ts.map +1 -1
- package/dist/collab/storage.js +31 -5
- package/dist/collab/storage.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +1 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +7 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/streaming-text-smoothing.d.ts.map +1 -1
- package/dist/shared/streaming-text-smoothing.js +8 -5
- package/dist/shared/streaming-text-smoothing.js.map +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.84.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- dd5b0a4: Honor the long Netlify background-function timeout for durable agent-chat workers and record runtime diagnostics when a background run starts.
|
|
8
|
+
- dd5b0a4: Make agent chat recovery surface completed-tool timeouts more reliably and keep Design variant follow-ups bounded to the selected screen.
|
|
9
|
+
- dd5b0a4: Double the agent chat smooth-streaming reveal speed.
|
|
10
|
+
- dd5b0a4: Avoid noisy Sentry reports from expected chat auth states and best-effort client thread-save conflicts.
|
|
11
|
+
- dd5b0a4: Keep activity-only tool cards and the latest assistant message visibly running during agent chat continuations, steer interrupted Design generation retries toward smaller existing-file edits, and capture run persistence failures with run IDs so missing request IDs are diagnosable.
|
|
12
|
+
- dd5b0a4: Compress the Plan mode composer callout into a quieter inline status pill.
|
|
13
|
+
|
|
14
|
+
## 0.84.12
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 9032acb: Don't reap a run that is actively making progress. The stale reapers
|
|
19
|
+
(`reapIfStale`, `reapAllStaleRuns`, and the heartbeat-stale path of
|
|
20
|
+
`cleanupOldRuns`) keyed liveness solely on `heartbeat_at` (the 1.5s
|
|
21
|
+
process-liveness timer) and ignored `last_progress_at` (bumped whenever the
|
|
22
|
+
agent emits an event, including a long-running tool's periodic activity
|
|
23
|
+
heartbeats — e.g. image generation streaming activity every 8s). When the
|
|
24
|
+
heartbeat write lagged while a multi-minute tool was in flight, the run was
|
|
25
|
+
flipped to `errored`, and the producing isolate's SQL-abort check then
|
|
26
|
+
self-aborted the in-flight tool with "Run aborted" — looping on the
|
|
27
|
+
durable-background self-chaining path and interrupting once inline. The reapers
|
|
28
|
+
now use the most recent of `heartbeat_at` and `last_progress_at` (falling back to
|
|
29
|
+
`started_at`) as their liveness basis, so a demonstrably-progressing run is never
|
|
30
|
+
reaped mid-tool. Portable across SQLite and Postgres; can only make reaping more
|
|
31
|
+
conservative (a dead producer emits neither signal).
|
|
32
|
+
- 9032acb: Add a `trigger="label-icon"` option to `ShareButton` that renders a leading
|
|
33
|
+
share glyph alongside the "Share" label, so the trigger matches adjacent
|
|
34
|
+
icon+label buttons (e.g. an Upload button). The default `"label"` trigger stays
|
|
35
|
+
text-only and `"icon"` stays icon-only.
|
|
36
|
+
- 351ee93: Fix an infinite sign-in redirect loop under base-path deploys. When the
|
|
37
|
+
authenticated app shell (wrapped in `RequireSession`) was served at the sign-in
|
|
38
|
+
path — e.g. `/<app>/_agent-native/sign-in` on a path-prefixed deploy — the gate
|
|
39
|
+
redirected to the sign-in page from the sign-in page, nesting and re-encoding the
|
|
40
|
+
current URL as a fresh `?return=` on every hop (`…sign-in?return=%252F…sign-in%253Freturn…`).
|
|
41
|
+
`RequireSession` now refuses to redirect when already on the sign-in entry point
|
|
42
|
+
(new exported `isOnSignInPage` helper), and `safeReturnPath` collapses any
|
|
43
|
+
`return` that resolves back to `…/_agent-native/sign-in` to `/`.
|
|
44
|
+
|
|
45
|
+
## 0.84.11
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- a2ce30e: Improve local SQLite collab write robustness for Design editor workflows.
|
|
50
|
+
|
|
3
51
|
## 0.84.10
|
|
4
52
|
|
|
5
53
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.13",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -235,6 +235,36 @@ export function isInBackgroundFunctionRuntime(): boolean {
|
|
|
235
235
|
return false;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
export function backgroundRunMarkerExpectsBackgroundRuntime(
|
|
239
|
+
marker: unknown,
|
|
240
|
+
): boolean {
|
|
241
|
+
return (
|
|
242
|
+
typeof marker === "object" &&
|
|
243
|
+
marker !== null &&
|
|
244
|
+
(marker as { backgroundFunctionRuntimeExpected?: unknown })
|
|
245
|
+
.backgroundFunctionRuntimeExpected === true
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function shouldUseBackgroundFunctionTimeoutForWorker(
|
|
250
|
+
marker: unknown,
|
|
251
|
+
): boolean {
|
|
252
|
+
return (
|
|
253
|
+
isInBackgroundFunctionRuntime() ||
|
|
254
|
+
backgroundRunMarkerExpectsBackgroundRuntime(marker)
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function backgroundRuntimeDiagnosticDetail(marker: unknown): string {
|
|
259
|
+
return [
|
|
260
|
+
`markerExpected=${backgroundRunMarkerExpectsBackgroundRuntime(marker)}`,
|
|
261
|
+
`runtimeDetected=${isInBackgroundFunctionRuntime()}`,
|
|
262
|
+
`globalMarker=${(globalThis as Record<string, unknown>).__AGENT_NATIVE_BACKGROUND_RUNTIME__ === true}`,
|
|
263
|
+
`lambdaNameEndsBackground=${typeof process.env.AWS_LAMBDA_FUNCTION_NAME === "string" && process.env.AWS_LAMBDA_FUNCTION_NAME.toLowerCase().endsWith("-background")}`,
|
|
264
|
+
`forceEnv=${typeof process.env.AGENT_CHAT_FORCE_BACKGROUND_RUNTIME === "string" && process.env.AGENT_CHAT_FORCE_BACKGROUND_RUNTIME.trim().length > 0}`,
|
|
265
|
+
].join(" ");
|
|
266
|
+
}
|
|
267
|
+
|
|
238
268
|
function isFlagEnabled(): boolean {
|
|
239
269
|
// Read the literal key (not `process.env[CONST]`) so guard:no-env-credentials
|
|
240
270
|
// can statically verify it against the allowlisted `AGENT_*` prefix. Keep this
|
|
@@ -53,9 +53,10 @@ import {
|
|
|
53
53
|
import { computeProtectedSegmentIds } from "./context-xray/segments.js";
|
|
54
54
|
import {
|
|
55
55
|
AGENT_CHAT_BACKGROUND_RUN_FIELD,
|
|
56
|
+
backgroundRuntimeDiagnosticDetail,
|
|
56
57
|
isAgentChatDurableBackgroundEnabled,
|
|
57
|
-
isInBackgroundFunctionRuntime,
|
|
58
58
|
resolveAgentChatProcessRunDispatchPath,
|
|
59
|
+
shouldUseBackgroundFunctionTimeoutForWorker,
|
|
59
60
|
} from "./durable-background.js";
|
|
60
61
|
import {
|
|
61
62
|
LLM_MISSING_CREDENTIALS_ERROR_CODE,
|
|
@@ -4032,6 +4033,7 @@ export async function claimBackgroundWorkerRunEarly(opts: {
|
|
|
4032
4033
|
requestTurnId?: string | null;
|
|
4033
4034
|
continuationCount: number;
|
|
4034
4035
|
runsInBackgroundFunction: boolean;
|
|
4036
|
+
backgroundRuntimeDetail?: string;
|
|
4035
4037
|
deps?: {
|
|
4036
4038
|
recordRunDiagnostic?: typeof recordRunDiagnostic;
|
|
4037
4039
|
insertRun?: typeof insertRun;
|
|
@@ -4057,7 +4059,13 @@ export async function claimBackgroundWorkerRunEarly(opts: {
|
|
|
4057
4059
|
await record(
|
|
4058
4060
|
opts.runId,
|
|
4059
4061
|
RUN_DIAG_STAGE.workerEntered,
|
|
4060
|
-
|
|
4062
|
+
[
|
|
4063
|
+
`runsInBackgroundFunction=${opts.runsInBackgroundFunction}`,
|
|
4064
|
+
`continuationCount=${opts.continuationCount}`,
|
|
4065
|
+
opts.backgroundRuntimeDetail,
|
|
4066
|
+
]
|
|
4067
|
+
.filter(Boolean)
|
|
4068
|
+
.join(" "),
|
|
4061
4069
|
).catch(() => {});
|
|
4062
4070
|
|
|
4063
4071
|
if (opts.continuationCount > 0) {
|
|
@@ -4218,7 +4226,11 @@ export function createProductionAgentHandler(
|
|
|
4218
4226
|
// function. Only a true value unlocks the ~13-min soft-timeout budget; a
|
|
4219
4227
|
// worker on the 60s function keeps the 40s clamp and checkpoints cleanly.
|
|
4220
4228
|
const runsInBackgroundFunction =
|
|
4221
|
-
isBackgroundWorker &&
|
|
4229
|
+
isBackgroundWorker &&
|
|
4230
|
+
shouldUseBackgroundFunctionTimeoutForWorker(backgroundRunMarker);
|
|
4231
|
+
const backgroundRuntimeDetail = isBackgroundWorker
|
|
4232
|
+
? backgroundRuntimeDiagnosticDetail(backgroundRunMarker)
|
|
4233
|
+
: "";
|
|
4222
4234
|
// How many server-driven background continuations have already chained into
|
|
4223
4235
|
// this logical turn (0 on the first chunk). Used to bound the chain.
|
|
4224
4236
|
const backgroundContinuationCount =
|
|
@@ -4239,6 +4251,7 @@ export function createProductionAgentHandler(
|
|
|
4239
4251
|
requestTurnId,
|
|
4240
4252
|
continuationCount: backgroundContinuationCount,
|
|
4241
4253
|
runsInBackgroundFunction,
|
|
4254
|
+
backgroundRuntimeDetail,
|
|
4242
4255
|
});
|
|
4243
4256
|
if (!earlyClaim.claimed) {
|
|
4244
4257
|
return { ok: true, skipped: earlyClaim.skipped };
|
|
@@ -5001,6 +5014,9 @@ export function createProductionAgentHandler(
|
|
|
5001
5014
|
}
|
|
5002
5015
|
|
|
5003
5016
|
let dispatched = false;
|
|
5017
|
+
const backgroundDispatchPath = resolveAgentChatProcessRunDispatchPath();
|
|
5018
|
+
const expectsNetlifyBackgroundFunction =
|
|
5019
|
+
backgroundDispatchPath.startsWith("/.netlify/functions/");
|
|
5004
5020
|
try {
|
|
5005
5021
|
await fireInternalDispatch({
|
|
5006
5022
|
event,
|
|
@@ -5014,7 +5030,7 @@ export function createProductionAgentHandler(
|
|
|
5014
5030
|
// inline. `fireInternalDispatch` strips the app base path for
|
|
5015
5031
|
// /.netlify/* targets so the request reaches the host-root function url;
|
|
5016
5032
|
// the Authorization Bearer HMAC is preserved either way.
|
|
5017
|
-
path:
|
|
5033
|
+
path: backgroundDispatchPath,
|
|
5018
5034
|
taskId: runId,
|
|
5019
5035
|
body: {
|
|
5020
5036
|
...body,
|
|
@@ -5022,6 +5038,8 @@ export function createProductionAgentHandler(
|
|
|
5022
5038
|
[AGENT_CHAT_BACKGROUND_RUN_FIELD]: {
|
|
5023
5039
|
runId,
|
|
5024
5040
|
turnId: effectiveTurnId,
|
|
5041
|
+
backgroundFunctionRuntimeExpected:
|
|
5042
|
+
expectsNetlifyBackgroundFunction,
|
|
5025
5043
|
},
|
|
5026
5044
|
},
|
|
5027
5045
|
});
|
|
@@ -5302,6 +5320,8 @@ export function createProductionAgentHandler(
|
|
|
5302
5320
|
runId: nextRunId,
|
|
5303
5321
|
turnId: effectiveTurnId,
|
|
5304
5322
|
continuationCount: backgroundContinuationCount + 1,
|
|
5323
|
+
backgroundFunctionRuntimeExpected:
|
|
5324
|
+
runsInBackgroundFunction,
|
|
5305
5325
|
},
|
|
5306
5326
|
},
|
|
5307
5327
|
});
|
|
@@ -5335,7 +5355,13 @@ export function createProductionAgentHandler(
|
|
|
5335
5355
|
await recordRunDiagnostic(
|
|
5336
5356
|
runId,
|
|
5337
5357
|
RUN_DIAG_STAGE.workerEntered,
|
|
5338
|
-
|
|
5358
|
+
[
|
|
5359
|
+
`runsInBackgroundFunction=${runsInBackgroundFunction}`,
|
|
5360
|
+
`continuationCount=${backgroundContinuationCount}`,
|
|
5361
|
+
backgroundRuntimeDetail,
|
|
5362
|
+
]
|
|
5363
|
+
.filter(Boolean)
|
|
5364
|
+
.join(" "),
|
|
5339
5365
|
).catch(() => {});
|
|
5340
5366
|
if (isChainedBackgroundContinuation) {
|
|
5341
5367
|
await insertRun(runId, effectiveThreadId, effectiveTurnId, {
|
|
@@ -5368,7 +5394,9 @@ export function createProductionAgentHandler(
|
|
|
5368
5394
|
const setupDetail =
|
|
5369
5395
|
Object.entries(setupMarks)
|
|
5370
5396
|
.map(([k, v]) => `${k}=${v}`)
|
|
5371
|
-
.join(" ") +
|
|
5397
|
+
.join(" ") +
|
|
5398
|
+
` total=${Date.now() - setupT0}` +
|
|
5399
|
+
(backgroundRuntimeDetail ? ` ${backgroundRuntimeDetail}` : "");
|
|
5372
5400
|
|
|
5373
5401
|
startRun(
|
|
5374
5402
|
runId,
|
|
@@ -349,11 +349,45 @@ export function startRun(
|
|
|
349
349
|
activeRuns.set(runId, run);
|
|
350
350
|
threadToRun.set(threadId, runId);
|
|
351
351
|
|
|
352
|
+
const captureRunPersistenceError = (
|
|
353
|
+
error: unknown,
|
|
354
|
+
phase: "insert-run" | "insert-event",
|
|
355
|
+
extra: Record<string, unknown> = {},
|
|
356
|
+
) => {
|
|
357
|
+
captureError(error, {
|
|
358
|
+
route: "/_agent-native/agent-chat",
|
|
359
|
+
tags: {
|
|
360
|
+
source: "agent-run-manager",
|
|
361
|
+
phase,
|
|
362
|
+
runStatus: run.status,
|
|
363
|
+
},
|
|
364
|
+
extra: {
|
|
365
|
+
runId,
|
|
366
|
+
threadId,
|
|
367
|
+
eventCount: run.events.length,
|
|
368
|
+
startedAt: run.startedAt,
|
|
369
|
+
...extra,
|
|
370
|
+
},
|
|
371
|
+
contexts: {
|
|
372
|
+
agentRun: {
|
|
373
|
+
runId,
|
|
374
|
+
threadId,
|
|
375
|
+
status: run.status,
|
|
376
|
+
phase,
|
|
377
|
+
eventCount: run.events.length,
|
|
378
|
+
startedAt: run.startedAt,
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
};
|
|
383
|
+
|
|
352
384
|
// Persist run to SQL without blocking the response. Keep the promise so
|
|
353
385
|
// final status cannot race ahead of a slow initial INSERT and then get
|
|
354
386
|
// overwritten by a late row stuck at status='running'.
|
|
355
387
|
const insertRunPromise = insertRun(runId, threadId, options?.turnId).catch(
|
|
356
|
-
() => {
|
|
388
|
+
(error) => {
|
|
389
|
+
captureRunPersistenceError(error, "insert-run");
|
|
390
|
+
},
|
|
357
391
|
);
|
|
358
392
|
|
|
359
393
|
// Per-run event persistence chain: events are chained so SQL inserts commit
|
|
@@ -368,6 +402,7 @@ export function startRun(
|
|
|
368
402
|
// chunk. The stuck-detector threshold is on the order of tens of seconds,
|
|
369
403
|
// so 1s resolution is plenty.
|
|
370
404
|
let lastProgressBumpAt = 0;
|
|
405
|
+
let eventPersistenceErrorCaptured = false;
|
|
371
406
|
const bumpProgressIfDue = () => {
|
|
372
407
|
const now = Date.now();
|
|
373
408
|
if (now - lastProgressBumpAt < 1000) return;
|
|
@@ -494,7 +529,15 @@ export function startRun(
|
|
|
494
529
|
const thisInsert = persistenceChain.then(() =>
|
|
495
530
|
insertRunEvent(runId, runEvent.seq, JSON.stringify(runEvent.event)),
|
|
496
531
|
);
|
|
497
|
-
persistenceChain = thisInsert.catch(() => {
|
|
532
|
+
persistenceChain = thisInsert.catch((error) => {
|
|
533
|
+
if (!eventPersistenceErrorCaptured) {
|
|
534
|
+
eventPersistenceErrorCaptured = true;
|
|
535
|
+
captureRunPersistenceError(error, "insert-event", {
|
|
536
|
+
seq: runEvent.seq,
|
|
537
|
+
eventType: runEvent.event.type,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
});
|
|
498
541
|
const persistence = thisInsert;
|
|
499
542
|
if (!options?.surfacePersistenceError) {
|
|
500
543
|
persistence.catch(() => {});
|
|
@@ -428,6 +428,29 @@ function backgroundAwareStaleCutoffSql(): string {
|
|
|
428
428
|
return `(CAST(? AS BIGINT) - CASE WHEN dispatch_mode LIKE 'background%' THEN ${BACKGROUND_RUN_STALE_MS} ELSE ${RUN_STALE_MS} END)`;
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
+
/**
|
|
432
|
+
* Liveness basis for the stale reapers: the MOST RECENT of `heartbeat_at`
|
|
433
|
+
* ("process is up", bumped on a 1.5s timer) and `last_progress_at` ("real work
|
|
434
|
+
* is happening", bumped whenever the agent emits an event — including a
|
|
435
|
+
* long-running tool's periodic activity heartbeats, e.g. image generation every
|
|
436
|
+
* 8s), falling back to `started_at`.
|
|
437
|
+
*
|
|
438
|
+
* The reapers previously keyed liveness on `heartbeat_at` alone, so a run that
|
|
439
|
+
* was demonstrably progressing got reaped ('running' → 'errored') the moment the
|
|
440
|
+
* process-liveness write lagged (DB latency, a brief event-loop stall). The
|
|
441
|
+
* producing isolate's SQL-abort check then self-aborted the in-flight action
|
|
442
|
+
* with "Run aborted"; on the durable-background self-chaining path this re-drove
|
|
443
|
+
* the turn in a loop. Honoring progress means a run doing real work is never
|
|
444
|
+
* reaped mid-tool. It can only make reaping MORE conservative — a genuinely dead
|
|
445
|
+
* producer emits neither signal — so a truly-dead run is still reaped.
|
|
446
|
+
*
|
|
447
|
+
* Portable across SQLite and Postgres (CASE + COALESCE only; no GREATEST or
|
|
448
|
+
* scalar MAX, which differ between engines).
|
|
449
|
+
*/
|
|
450
|
+
function livenessBasisSql(): string {
|
|
451
|
+
return `(CASE WHEN COALESCE(last_progress_at, started_at) > COALESCE(heartbeat_at, started_at) THEN COALESCE(last_progress_at, started_at) ELSE COALESCE(heartbeat_at, started_at) END)`;
|
|
452
|
+
}
|
|
453
|
+
|
|
431
454
|
/**
|
|
432
455
|
* Atomically claim a background-dispatched run for processing. The foreground
|
|
433
456
|
* POST inserts the run row with `dispatch_mode = 'background'`; the FIRST
|
|
@@ -733,8 +756,8 @@ export async function reapIfStale(
|
|
|
733
756
|
// override forces a flat window for callers that want one.
|
|
734
757
|
const staleClause =
|
|
735
758
|
typeof maxStaleMs === "number"
|
|
736
|
-
?
|
|
737
|
-
:
|
|
759
|
+
? `${livenessBasisSql()} < ?`
|
|
760
|
+
: `${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}`;
|
|
738
761
|
const staleArgs =
|
|
739
762
|
typeof maxStaleMs === "number" ? [completedAt - maxStaleMs] : [completedAt];
|
|
740
763
|
const { rowsAffected } = await client.execute({
|
|
@@ -1151,7 +1174,7 @@ export async function reapAllStaleRuns(): Promise<number> {
|
|
|
1151
1174
|
const stale = await client.execute({
|
|
1152
1175
|
sql: `SELECT id FROM agent_runs
|
|
1153
1176
|
WHERE status = 'running'
|
|
1154
|
-
AND
|
|
1177
|
+
AND ${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}`,
|
|
1155
1178
|
args: [now],
|
|
1156
1179
|
});
|
|
1157
1180
|
const completedAt = Date.now();
|
|
@@ -1162,7 +1185,7 @@ export async function reapAllStaleRuns(): Promise<number> {
|
|
|
1162
1185
|
error_code = ?,
|
|
1163
1186
|
error_detail = ?
|
|
1164
1187
|
WHERE status = 'running'
|
|
1165
|
-
AND
|
|
1188
|
+
AND ${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}`,
|
|
1166
1189
|
args: [
|
|
1167
1190
|
completedAt,
|
|
1168
1191
|
STALE_RUN_ERROR_EVENT.errorCode,
|
|
@@ -1207,7 +1230,7 @@ export async function cleanupOldRuns(
|
|
|
1207
1230
|
sql: `SELECT id FROM agent_runs
|
|
1208
1231
|
WHERE status = 'running'
|
|
1209
1232
|
AND (
|
|
1210
|
-
|
|
1233
|
+
${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}
|
|
1211
1234
|
OR started_at < ?
|
|
1212
1235
|
)`,
|
|
1213
1236
|
args: [now, cutoff],
|
|
@@ -1236,7 +1259,7 @@ export async function cleanupOldRuns(
|
|
|
1236
1259
|
error_code = ?,
|
|
1237
1260
|
error_detail = ?
|
|
1238
1261
|
WHERE status = 'running'
|
|
1239
|
-
AND
|
|
1262
|
+
AND ${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}`,
|
|
1240
1263
|
args: [
|
|
1241
1264
|
completedAt,
|
|
1242
1265
|
STALE_RUN_ERROR_EVENT.errorCode,
|
|
@@ -712,6 +712,7 @@ export interface UpdateThreadDataOptions {
|
|
|
712
712
|
preserveExistingQueuedMessages?: boolean;
|
|
713
713
|
preserveExistingTopLevelKeys?: boolean;
|
|
714
714
|
maxAttempts?: number;
|
|
715
|
+
ignoreConflicts?: boolean;
|
|
715
716
|
}
|
|
716
717
|
|
|
717
718
|
function parseThreadData(value: string): any {
|
|
@@ -792,6 +793,7 @@ export async function updateThreadData(
|
|
|
792
793
|
}
|
|
793
794
|
|
|
794
795
|
if (lastConflict) {
|
|
796
|
+
if (options.ignoreConflicts) return;
|
|
795
797
|
throw new Error(
|
|
796
798
|
`Failed to update chat thread ${id} after concurrent write conflicts.`,
|
|
797
799
|
);
|
|
@@ -329,15 +329,18 @@ iteration, or a human-in-the-loop choice among design directions.
|
|
|
329
329
|
overview board and renders an inline chat choice with one button per screen
|
|
330
330
|
name. After the user picks, delete the unchosen variant screens and continue
|
|
331
331
|
from the kept screen by first calling \`get-design-snapshot\` with that
|
|
332
|
-
screen's \`fileId\`, then
|
|
332
|
+
screen's \`fileId\`, then calling \`edit-design\` on that same \`fileId\` in a
|
|
333
|
+
bounded single-file pass. Use \`mode: "replace-file"\` when expanding the
|
|
334
|
+
representative placeholder into the full chosen direction. Do not call
|
|
335
|
+
\`generate-design\` after a variant pick.
|
|
333
336
|
- If the chat choice buttons are not available in the host, ask the user to
|
|
334
337
|
tell you the screen name they prefer. The variants are already real screens
|
|
335
338
|
on the board, so do not ask them to paste HTML or copy a generated handoff
|
|
336
339
|
summary.
|
|
337
340
|
- For direct refinements to an already chosen direction, call
|
|
338
341
|
\`get-design-snapshot\`, edit from the current tuned HTML, and use
|
|
339
|
-
\`edit-design\` for surgical changes
|
|
340
|
-
|
|
342
|
+
\`edit-design\` for surgical changes or \`mode: "replace-file"\` for a bounded
|
|
343
|
+
selected-file replacement. Use \`generate-design\` for new files only.
|
|
341
344
|
- Use \`export-coding-handoff\` when the user wants to implement the chosen
|
|
342
345
|
design in a codebase.
|
|
343
346
|
|
|
@@ -353,9 +356,10 @@ iteration, or a human-in-the-loop choice among design directions.
|
|
|
353
356
|
5. After \`present-design-variants\`, wait for the user's pick before
|
|
354
357
|
generating the next version. Keep the chosen screen, delete the other
|
|
355
358
|
variant screens, call \`get-design-snapshot\` with \`fileId\` for the kept
|
|
356
|
-
screen, then
|
|
357
|
-
\`
|
|
358
|
-
|
|
359
|
+
screen, then call \`edit-design\` on that same \`fileId\` in a bounded pass.
|
|
360
|
+
Use \`mode: "replace-file"\` when expanding the representative placeholder
|
|
361
|
+
into the full chosen direction. Do not call \`generate-design\` after a
|
|
362
|
+
variant pick. Stop after the first successful \`edit-design\` save.
|
|
359
363
|
|
|
360
364
|
## Design Quality Bar
|
|
361
365
|
|
|
@@ -601,6 +601,31 @@ export function resolveAssistantChatSubmitIntent({
|
|
|
601
601
|
return requestedIntent ?? "immediate";
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
+
export function resolveAssistantChatRunningState({
|
|
605
|
+
forceStopped,
|
|
606
|
+
isRuntimeRunning,
|
|
607
|
+
isReconnecting,
|
|
608
|
+
optimisticRunning,
|
|
609
|
+
isAutoResuming,
|
|
610
|
+
}: {
|
|
611
|
+
forceStopped: boolean;
|
|
612
|
+
isRuntimeRunning: boolean;
|
|
613
|
+
isReconnecting: boolean;
|
|
614
|
+
optimisticRunning: boolean;
|
|
615
|
+
isAutoResuming: boolean;
|
|
616
|
+
}): { isRunning: boolean; showRunningInUI: boolean } {
|
|
617
|
+
const isRunning =
|
|
618
|
+
!forceStopped && (isRuntimeRunning || isReconnecting || optimisticRunning);
|
|
619
|
+
return {
|
|
620
|
+
isRunning,
|
|
621
|
+
// During auto-continuation, assistant-ui can briefly mark the message done
|
|
622
|
+
// between chunks even though the adapter is about to POST the next run.
|
|
623
|
+
// Keep the visible chat state running so the latest assistant message shows
|
|
624
|
+
// Thinking/Resuming and does not expose footer actions prematurely.
|
|
625
|
+
showRunningInUI: !forceStopped && (isRunning || isAutoResuming),
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
604
629
|
type QueuedMessage = {
|
|
605
630
|
id: string;
|
|
606
631
|
text: string;
|
|
@@ -1378,14 +1403,17 @@ const AssistantChatInner = forwardRef<
|
|
|
1378
1403
|
// queueing forever" state where isReconnecting or isRuntimeRunning gets
|
|
1379
1404
|
// wedged (e.g. after a tab refresh + stop during reconnect).
|
|
1380
1405
|
const [forceStopped, setForceStopped] = useState(false);
|
|
1381
|
-
// Real running state
|
|
1382
|
-
//
|
|
1383
|
-
//
|
|
1384
|
-
const isRunning =
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1406
|
+
// Real running state drives submission/queue gating; UI running also covers
|
|
1407
|
+
// short auto-continuation gaps so the latest assistant message does not flash
|
|
1408
|
+
// into a done state while the agent is still working.
|
|
1409
|
+
const { isRunning, showRunningInUI } = resolveAssistantChatRunningState({
|
|
1410
|
+
forceStopped,
|
|
1411
|
+
isRuntimeRunning,
|
|
1412
|
+
isReconnecting,
|
|
1413
|
+
optimisticRunning,
|
|
1414
|
+
isAutoResuming,
|
|
1415
|
+
});
|
|
1416
|
+
const textStreaming = showRunningInUI || externalStreaming;
|
|
1389
1417
|
// A revealed activity label wins; otherwise stay a steady "Thinking" by
|
|
1390
1418
|
// default. We only surface "Reconnecting" while we are actively replaying
|
|
1391
1419
|
// recovered content (reconnectContent populated). A bare reconnect with no
|
|
@@ -2265,6 +2293,8 @@ const AssistantChatInner = forwardRef<
|
|
|
2265
2293
|
|
|
2266
2294
|
useEffect(() => {
|
|
2267
2295
|
if (!authError) return;
|
|
2296
|
+
const shouldCaptureStuckAuthCard =
|
|
2297
|
+
authSessionAvailable || authError.sessionExpired;
|
|
2268
2298
|
// Auto-recovery (`checkAuthSession`) runs immediately + at 250ms. If the
|
|
2269
2299
|
// card is still showing 3 seconds later, recovery failed and the user
|
|
2270
2300
|
// is about to hit "Refresh chat" — that's the "Reload UI required"
|
|
@@ -2273,6 +2303,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2273
2303
|
void (async () => {
|
|
2274
2304
|
const hasSession = await checkAuthSession();
|
|
2275
2305
|
if (hasSession) return;
|
|
2306
|
+
if (!shouldCaptureStuckAuthCard) return;
|
|
2276
2307
|
captureError(new Error("agent-chat:auth_error_card_stuck"), {
|
|
2277
2308
|
tags: {
|
|
2278
2309
|
context: "agent-native-chat",
|
|
@@ -3211,7 +3242,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3211
3242
|
<CheckpointContext.Provider value={checkpointCtx}>
|
|
3212
3243
|
<MessageActionsContext.Provider value={messageActionsCtx}>
|
|
3213
3244
|
<ApprovalContext.Provider value={approvalCtx}>
|
|
3214
|
-
<ChatRunningContext.Provider value={
|
|
3245
|
+
<ChatRunningContext.Provider value={showRunningInUI}>
|
|
3215
3246
|
<TextStreamingContext.Provider value={textStreaming}>
|
|
3216
3247
|
<div
|
|
3217
3248
|
data-agent-empty-state={
|
|
@@ -807,6 +807,21 @@ function lastActivityTool(
|
|
|
807
807
|
return undefined;
|
|
808
808
|
}
|
|
809
809
|
|
|
810
|
+
function formatActivityTrail(
|
|
811
|
+
trail: readonly AgentActivityTrailEntry[],
|
|
812
|
+
): string | undefined {
|
|
813
|
+
const items = trail
|
|
814
|
+
.slice(-8)
|
|
815
|
+
.map((entry) => {
|
|
816
|
+
const label = entry.label.replace(/\s+/g, " ").trim();
|
|
817
|
+
const tool = entry.tool?.replace(/\s+/g, " ").trim();
|
|
818
|
+
if (label && tool && label !== tool) return `${label} (${tool})`;
|
|
819
|
+
return label || tool || "";
|
|
820
|
+
})
|
|
821
|
+
.filter(Boolean);
|
|
822
|
+
return items.length > 0 ? items.join(" > ") : undefined;
|
|
823
|
+
}
|
|
824
|
+
|
|
810
825
|
function lastUnresolvedToolActivity(
|
|
811
826
|
content: ContentPart[],
|
|
812
827
|
): string | undefined {
|
|
@@ -928,9 +943,9 @@ function incrementalActionGuidance(tool: string): string | undefined {
|
|
|
928
943
|
return "create a compact working v1 with `create-extension`, then use focused `update-extension` edits for refinements";
|
|
929
944
|
case "generate-design":
|
|
930
945
|
case "update-design":
|
|
931
|
-
return
|
|
946
|
+
return 'if an existing design file or snapshot is already in history, especially after a Design variant pick, stop retrying `generate-design`: call `get-design-snapshot` for the selected file, then call `edit-design` once on that same `fileId` (`mode: "replace-file"` for a compact full-file replacement, or search/replace for smaller edits). Use `generate-design` only for a brand-new compact first file when no target file exists yet';
|
|
932
947
|
case "present-design-variants":
|
|
933
|
-
return
|
|
948
|
+
return 'call `present-design-variants` with concise labels, descriptions, accent colors, and feature bullets; omit large `content` HTML when needed so the action can render compact representative screens. After the user picks a direction, delete unchosen screens, snapshot the selected `fileId`, and refine that same file with `edit-design` (`mode: "replace-file"` for placeholder expansion). Do not call `generate-design` after the variant pick';
|
|
934
949
|
case "create-visual-plan":
|
|
935
950
|
case "create-ui-plan":
|
|
936
951
|
case "create-plan-design":
|
|
@@ -1439,6 +1454,7 @@ export function createAgentChatAdapter(
|
|
|
1439
1454
|
let visibleContinuationPrefix: ContentPart[] = [];
|
|
1440
1455
|
let lastAutoContinueReason: string | null = null;
|
|
1441
1456
|
let lastRecoverableRunError: AgentAutoContinueErrorInfo | null = null;
|
|
1457
|
+
let lastActivityTrail: AgentActivityTrailEntry[] = [];
|
|
1442
1458
|
const attemptedRunIds: string[] = [];
|
|
1443
1459
|
let authRecoveryAttempted = false;
|
|
1444
1460
|
let continuationToolCallCounter = 0;
|
|
@@ -1468,6 +1484,9 @@ export function createAgentChatAdapter(
|
|
|
1468
1484
|
lastPreparingToolName
|
|
1469
1485
|
? `last_preparing_tool: ${lastPreparingToolName}`
|
|
1470
1486
|
: "",
|
|
1487
|
+
formatActivityTrail(lastActivityTrail)
|
|
1488
|
+
? `activity_trail: ${formatActivityTrail(lastActivityTrail)}`
|
|
1489
|
+
: "",
|
|
1471
1490
|
`total_transient_continuations: ${totalTransientContinuationAttempts}`,
|
|
1472
1491
|
attemptedRunIds.length > 0
|
|
1473
1492
|
? `attempted_runs: ${attemptedRunIds.join(", ")}`
|
|
@@ -1559,6 +1578,7 @@ export function createAgentChatAdapter(
|
|
|
1559
1578
|
lastSeq,
|
|
1560
1579
|
contentParts: content.length,
|
|
1561
1580
|
attemptedRunIds: [...attemptedRunIds],
|
|
1581
|
+
activityTrail: [...lastActivityTrail],
|
|
1562
1582
|
startupRecoveryAttempts,
|
|
1563
1583
|
staleRunContinuationAttempts,
|
|
1564
1584
|
stalledTransientContinuationAttempts,
|
|
@@ -1867,6 +1887,7 @@ export function createAgentChatAdapter(
|
|
|
1867
1887
|
completedToolName?: string;
|
|
1868
1888
|
} => {
|
|
1869
1889
|
lastAutoContinueReason = signal.reason;
|
|
1890
|
+
lastActivityTrail = [...signal.activityTrail];
|
|
1870
1891
|
if (signal.errorInfo) {
|
|
1871
1892
|
lastRecoverableRunError = signal.errorInfo;
|
|
1872
1893
|
}
|