@agent-native/core 0.70.0 → 0.70.1
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/README.md +15 -15
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +61 -0
- package/corpus/core/docs/design/durable-agent-runs.md +217 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action.ts +89 -0
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/run-loop-with-resume.ts +48 -0
- package/corpus/core/src/cli/skills.ts +2 -2
- package/corpus/core/src/client/AgentPanel.tsx +5 -3
- package/corpus/core/src/client/AssistantChat.tsx +8 -0
- package/corpus/core/src/client/client-surface.ts +41 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +123 -12
- package/corpus/core/src/client/feedback-context.ts +4 -0
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/sse-event-processor.ts +7 -1
- package/corpus/core/src/mcp/build-server.ts +57 -1
- package/corpus/core/src/mcp/embed-app.ts +86 -2
- package/corpus/core/src/mcp/oauth-route.ts +109 -13
- package/corpus/core/src/server/core-routes-plugin.ts +78 -0
- package/corpus/core/src/server/onboarding-html.ts +35 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +22 -0
- package/corpus/templates/analytics/AGENTS.md +8 -1
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +97 -2
- package/corpus/templates/analytics/actions/update-dashboard.ts +36 -7
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -3
- package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +2 -4
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +0 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -2
- package/corpus/templates/analytics/changelog/2026-06-23-long-dashboard-and-analysis-names-in-the-sidebar-now-truncat.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-23-open-any-dashboard-chart-full-screen-in-a-modal-from-the-pan.md +6 -0
- package/corpus/templates/assets/actions/generate-image.ts +24 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/assets/app/global.css +17 -1
- package/corpus/templates/assets/app/routes/_index.tsx +94 -14
- package/corpus/templates/assets/changelog/2026-06-23-chat-dates-in-the-sidebar-no-longer-wrap-onto-two-lines.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-clicking-image-video-or-refine-on-the-start-screen-now-drops.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-pick-your-image-generation-model-right-from-the-chat-model-m.md +6 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -20
- package/corpus/templates/clips/app/components/recorder/countdown-overlay.tsx +72 -48
- package/corpus/templates/clips/app/lib/countdown-audio-cue.ts +35 -17
- package/corpus/templates/clips/changelog/2026-06-23-removed-select-button-from-library-hover-a-clip-to-select-it.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-shared-video-links-now-play-for-anyone-without-signing-in.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-skip-or-cancel-the-recording-countdown-with-buttons-beside-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-camera-bubble-now-looks-sharp-the-instant-it-ope.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-recording-widget-now-grows-downward-only-when-yo.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-recording-start-sound-is-now-a-softer-more-modern-chime.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +17 -8
- package/corpus/templates/clips/chrome-extension/src/background.ts +769 -180
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +177 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +466 -329
- package/corpus/templates/clips/chrome-extension/src/overlay.css +377 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.html +12 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +330 -0
- package/corpus/templates/clips/chrome-extension/src/permission.html +227 -0
- package/corpus/templates/clips/chrome-extension/src/permission.ts +105 -0
- package/corpus/templates/clips/chrome-extension/src/popup.html +3 -0
- package/corpus/templates/clips/chrome-extension/src/popup.ts +50 -2
- package/corpus/templates/clips/chrome-extension/src/styles.css +50 -0
- package/corpus/templates/clips/chrome-extension/vite.config.ts +3 -0
- package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +41 -24
- package/corpus/templates/clips/desktop/src/lib/bubble-webrtc.ts +119 -2
- package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +21 -2
- package/corpus/templates/clips/desktop/src/styles.css +70 -2
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +73 -0
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +34 -5
- package/corpus/templates/content/AGENTS.md +20 -3
- package/corpus/templates/content/actions/_database-utils.ts +15 -0
- package/corpus/templates/content/actions/_property-utils.ts +371 -4
- package/corpus/templates/content/actions/configure-document-property.ts +35 -1
- package/corpus/templates/content/actions/create-content-database.ts +5 -1
- package/corpus/templates/content/actions/delete-document-property.ts +52 -2
- package/corpus/templates/content/actions/duplicate-document-property.ts +34 -17
- package/corpus/templates/content/actions/reorder-document-property.ts +79 -0
- package/corpus/templates/content/actions/set-document-property.ts +39 -0
- package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +806 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +226 -68
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +59 -30
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +25 -1
- package/corpus/templates/content/app/components/editor/blockFieldSaveController.ts +180 -0
- package/corpus/templates/content/app/components/editor/blockFieldSaveRegistry.ts +179 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +244 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +132 -0
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-document-properties.ts +21 -0
- package/corpus/templates/content/server/db/schema.ts +30 -0
- package/corpus/templates/content/server/plugins/db.ts +87 -0
- package/corpus/templates/content/shared/api.ts +7 -0
- package/corpus/templates/content/shared/properties.ts +109 -0
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +10 -0
- package/corpus/templates/forms/actions/export-responses.ts +6 -0
- package/corpus/templates/forms/actions/list-responses.ts +2 -0
- package/corpus/templates/forms/actions/response-insights.ts +2 -0
- package/corpus/templates/forms/app/pages/ResponsesPage.tsx +138 -1
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-the-page-each-submission-came-from-.md +6 -0
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-whether-feedback-came-from-the-web-.md +6 -0
- package/corpus/templates/forms/server/db/schema.ts +7 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +19 -2
- package/corpus/templates/forms/server/lib/integrations.ts +76 -2
- package/corpus/templates/forms/server/plugins/db.ts +20 -0
- package/corpus/templates/forms/shared/types.ts +12 -0
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +3 -1
- package/corpus/templates/plan/changelog/2026-06-23-large-diagrams-in-a-plan-now-scroll-within-their-block-inste.md +6 -0
- package/dist/action.js +87 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +13 -0
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +44 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/cli/skills.js +2 -2
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +5 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/client-surface.d.ts +17 -0
- package/dist/client/client-surface.d.ts.map +1 -0
- package/dist/client/client-surface.js +24 -0
- package/dist/client/client-surface.js.map +1 -0
- package/dist/client/composer/TiptapComposer.d.ts +26 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +27 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/feedback-context.d.ts +3 -0
- package/dist/client/feedback-context.d.ts.map +1 -1
- package/dist/client/feedback-context.js +2 -0
- package/dist/client/feedback-context.js.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +7 -1
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +21 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +43 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +86 -2
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +95 -12
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +63 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +35 -0
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/docs/design/durable-agent-runs.md +217 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/corpus/templates/analytics/app/pages/About.tsx +0 -108
- package/corpus/templates/analytics/app/routes/about.tsx +0 -9
package/README.md
CHANGED
|
@@ -21,18 +21,6 @@ export default defineAction({
|
|
|
21
21
|
- **Agent runtime**: Chat, tools, skills, memory, jobs, observability, and handoffs ship together.
|
|
22
22
|
- **Backend agnostic**: Plug in any Drizzle-supported SQL database and Nitro-compatible host.
|
|
23
23
|
|
|
24
|
-
## Agents and UIs, Fully Connected
|
|
25
|
-
|
|
26
|
-
The agent and the UI are equal citizens of one system. Every action works both ways: click it or ask for it.
|
|
27
|
-
|
|
28
|
-

|
|
29
|
-
|
|
30
|
-
- **Everything syncs**: One database, one state. Changes from either side show up instantly on the other.
|
|
31
|
-
- **Real-time multiplayer**: Humans and agents edit the same document together, with the agent as a first-class peer.
|
|
32
|
-
- **Context-aware**: The agent knows what you're looking at. Select text, hit Cmd+I, and tell it what to do.
|
|
33
|
-
- **Agents call agents**: Tag another agent from any app and they coordinate over A2A.
|
|
34
|
-
- **Self-improving**: The agent can add features, fix bugs, and refine the UI over time.
|
|
35
|
-
|
|
36
24
|
## Templates
|
|
37
25
|
|
|
38
26
|
Start with a full featured template. Each one is a complete, 100% free and open-source SaaS app: cloneable, not scaffolded, except you own the code and can customize everything.
|
|
@@ -43,11 +31,11 @@ Start with a full featured template. Each one is a complete, 100% free and open-
|
|
|
43
31
|
|
|
44
32
|
**Clips**
|
|
45
33
|
|
|
46
|
-
<a href="https://agent-native.com/templates/clips"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%
|
|
34
|
+
<a href="https://agent-native.com/templates/clips"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F189ebd9b2f2b4f0ead3b33138d4e4c10?format=webp&width=800" alt="Clips template" width="100%" /></a>
|
|
47
35
|
|
|
48
|
-
**Agent-Native Loom**
|
|
36
|
+
**Agent-Native Loom + Jam**
|
|
49
37
|
|
|
50
|
-
Record your screen with auto-transcripts,
|
|
38
|
+
Record your screen with auto-transcripts and captured browser debug logs, share a link, and let an agent read the transcript, see timestamped frames, and fix the bug.
|
|
51
39
|
|
|
52
40
|
</td>
|
|
53
41
|
<td width="33%" align="center" valign="top">
|
|
@@ -112,6 +100,18 @@ Connect analytics data sources, prompt for real charts, and build reusable dashb
|
|
|
112
100
|
|
|
113
101
|
View the full template gallery at **[agent-native.com/templates](https://agent-native.com/templates)**.
|
|
114
102
|
|
|
103
|
+
## Agents and UIs, Fully Connected
|
|
104
|
+
|
|
105
|
+
The agent and the UI are equal citizens of one system. Every action works both ways: click it or ask for it.
|
|
106
|
+
|
|
107
|
+

|
|
108
|
+
|
|
109
|
+
- **Everything syncs**: One database, one state. Changes from either side show up instantly on the other.
|
|
110
|
+
- **Real-time multiplayer**: Humans and agents edit the same document together, with the agent as a first-class peer.
|
|
111
|
+
- **Context-aware**: The agent knows what you're looking at. Select text, hit Cmd+I, and tell it what to do.
|
|
112
|
+
- **Agents call agents**: Tag another agent from any app and they coordinate over A2A.
|
|
113
|
+
- **Self-improving**: The agent can add features, fix bugs, and refine the UI over time.
|
|
114
|
+
|
|
115
115
|
## Try it with a skill
|
|
116
116
|
|
|
117
117
|
Don't want to scaffold a whole app yet? Add visual planning and PR recaps to Claude Code, Codex, Cursor, Pi, OpenCode, GitHub Copilot / VS Code, and similar agents with one command:
|
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,66 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.70.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8003c56: Coerce gateway-stringified tool arguments before action validation. Some model gateways (notably Builder's Gemini-backed gateway) hand structured tool-call arguments back as JSON strings — an array param arrives as `"[{...}]"`, a boolean as `"true"`. Standard Schema (zod) validation does not coerce, so these calls failed validation and the agent could thrash retrying different shapes (and hang). The validation wrapper now coerces a string value to the type its schema field declares (array/object via `JSON.parse`, boolean, number/integer) when — and only when — the schema expects a non-string type and the string parses cleanly to it; ambiguous or unparseable values are left untouched so the normal validation error still surfaces.
|
|
8
|
+
- 8003c56: Composer model picker improvements. The picker now supports an optional secondary "image model" menu via a new `imageModelMenu` prop on `AgentChatSurface` / `AssistantChat` (opt-in; chat-only apps are unaffected) — apps that drive a separate generation model (e.g. Assets' image model) can surface it in the same dropdown so it's clear which model reasons about the request and which produces the output. The reasoning-effort list is now a collapsed-by-default accordion (matching the provider groups) instead of always-expanded, keeping the menu compact. Model catalog: the Builder gateway list now lists Opus 4.8 (was 4.7) and drops the retired GPT-5.1 Codex Mini entry.
|
|
9
|
+
- 8003c56: Remove the setup/onboarding checklist that appeared above the agent chat (the
|
|
10
|
+
"Setup N of 5" panel with Connect an AI engine / image & video generation /
|
|
11
|
+
asset storage / email / GitHub steps) and its header "Setup" re-open button.
|
|
12
|
+
Setup is now surfaced in better places — the settings panel and per-feature
|
|
13
|
+
setup affordances — so the panel no longer takes up sidebar space in any app.
|
|
14
|
+
- 8003c56: Feedback submissions now forward a `clientSurface` hint (web / electron / tauri)
|
|
15
|
+
alongside the existing page URL, so form owners can tell whether feedback came
|
|
16
|
+
from the Agent Native desktop app, a Tauri shell (e.g. Clips), or a browser.
|
|
17
|
+
Detection is exposed as a reusable `getClientSurface()` client helper and is
|
|
18
|
+
passed through as hidden metadata — it never appears as a visible form field.
|
|
19
|
+
- 8003c56: Fix the "Sign in with Google" button getting stuck disabled when the OAuth
|
|
20
|
+
window is closed without finishing (e.g. to retry in a different browser
|
|
21
|
+
profile). The button was only ever re-enabled on an explicit OAuth error or
|
|
22
|
+
the 5-minute poll timeout, so a cancelled sign-in left the primary CTA greyed
|
|
23
|
+
out with no way to retry short of refreshing. The sign-in screen now re-enables
|
|
24
|
+
the button (and stops the pending exchange poll) when the window regains focus
|
|
25
|
+
or becomes visible again — mirroring the existing email-verification recovery.
|
|
26
|
+
- 8003c56: Add observability and a safety valve for inline MCP App embeds, on top of the
|
|
27
|
+
Codex/Cursor transplant fix already shipped in 0.70.0.
|
|
28
|
+
|
|
29
|
+
When an inline embed cannot load in a host, the shell now reports a bounded,
|
|
30
|
+
structured diagnostic (stage, message, HTTP status, host, render mode, bridge
|
|
31
|
+
type) to a new CORS-open `POST /_agent-native/mcp/embed-error` route, which
|
|
32
|
+
forwards it to Sentry via `captureError` — so embed failures across Codex,
|
|
33
|
+
Cursor, ChatGPT, and Claude are inspectable instead of an opaque spinner. The
|
|
34
|
+
failure card also surfaces the specific cause (e.g. "Embedded app returned HTTP
|
|
35
|
+
500" / session-expired) and promotes "Open in new tab" to the primary action.
|
|
36
|
+
|
|
37
|
+
Adds a deploy-toggleable kill switch for inline MCP App embeds, **off by
|
|
38
|
+
default**. Set `AGENT_NATIVE_MCP_APPS_INLINE=1` to enable inline embeds for an
|
|
39
|
+
environment; while it is off, accounts listed in
|
|
40
|
+
`AGENT_NATIVE_MCP_APPS_INLINE_ALLOW_EMAILS` (comma/space separated) still get
|
|
41
|
+
them, so a fix can be verified in production before it reaches normal users.
|
|
42
|
+
When disabled, no `ui://` resource is advertised or referenced and tool results
|
|
43
|
+
fall back to their deep-link text — no skills/instructions change required.
|
|
44
|
+
|
|
45
|
+
- 8003c56: Show a friendly "You're all set" confirmation page after authorizing an MCP
|
|
46
|
+
client whose redirect is a native deep link (cursor://, vscode://, …). Instead
|
|
47
|
+
of leaving the browser tab dangling on a blank page after the OS handed the code
|
|
48
|
+
to the app, the tab now shows a checkmark, a "return to your agent to continue"
|
|
49
|
+
message, and re-fires the deep link so the client still receives the code.
|
|
50
|
+
https/loopback callbacks keep the standard redirect.
|
|
51
|
+
- 8003c56: Clarify the "Where should visual plans and recaps live?" install prompt: move
|
|
52
|
+
the "(recommended)" marker into the hosted option's label and tighten its
|
|
53
|
+
description to "100% free and open source. Supports comments, browser editor,
|
|
54
|
+
and sharing. Requires one-time browser sign-in."
|
|
55
|
+
- 8003c56: When a hosted run is cut off mid-step and exhausts its in-invocation
|
|
56
|
+
continuation budget without finishing, the chat now ends with a loud,
|
|
57
|
+
unambiguous "stopped before finishing" terminal instead of a silent stall or a
|
|
58
|
+
misleading clean `done`. The terminal carries a machine-readable
|
|
59
|
+
`run_budget_exhausted` error code that is deliberately excluded from the
|
|
60
|
+
client's auto-recoverable allow-list, so the chain terminates rather than
|
|
61
|
+
looping another continuation into the same wall, and any half-streamed partial
|
|
62
|
+
text is cleared so the message stands alone.
|
|
63
|
+
|
|
3
64
|
## 0.70.0
|
|
4
65
|
|
|
5
66
|
### Minor Changes
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Design: Durable / Checkpointed Agent Runs
|
|
2
|
+
|
|
3
|
+
Status: proposed
|
|
4
|
+
Owner: core / run-manager
|
|
5
|
+
Related code: `packages/core/src/agent/run-manager.ts`,
|
|
6
|
+
`packages/core/src/agent/engine/builder-engine.ts`
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
Hosted agent runs are bounded by a ~40s soft timeout enforced in
|
|
11
|
+
`run-manager.ts` (`DEFAULT_HOSTED_RUN_SOFT_TIMEOUT_MS = 40_000`, also the
|
|
12
|
+
`HOSTED_SOFT_TIMEOUT_CEILING_MS`). That budget is deliberate and correct: it
|
|
13
|
+
sits just under a stack of upstream walls that the framework does not control.
|
|
14
|
+
|
|
15
|
+
When the soft timeout fires, the run-manager aborts the current chunk, persists
|
|
16
|
+
the partial turn, writes a terminal event, and emits an `auto_continue` event
|
|
17
|
+
(`reason: "run_timeout"`) so the client transparently resumes the turn in a
|
|
18
|
+
fresh chunk. This works well for a single long model call that just needs more
|
|
19
|
+
wall-clock time.
|
|
20
|
+
|
|
21
|
+
It does **not** work well for long _multi-step_ operations. A turn that performs
|
|
22
|
+
many sequential side effects — for example an agent appending many dashboard
|
|
23
|
+
panels through many separate write calls, or any "do N independent mutations in
|
|
24
|
+
a loop" workflow — fails in a characteristic way:
|
|
25
|
+
|
|
26
|
+
- **Continuation thrash / re-hydration.** Each `auto_continue` chunk starts the
|
|
27
|
+
model over from the rebuilt context. If the work isn't expressed as resumable
|
|
28
|
+
progress, the model frequently re-reasons about and re-issues steps it already
|
|
29
|
+
attempted in the previous chunk instead of advancing. Successive chunks burn
|
|
30
|
+
their entire 40s budget re-deciding rather than completing new steps.
|
|
31
|
+
- **Partial or zero net progress.** Because each chunk can be cut mid-step and
|
|
32
|
+
the next chunk may redo earlier steps, the run can churn for many chunks while
|
|
33
|
+
the _persisted_ end state barely moves — or, when nothing reaches a committed
|
|
34
|
+
state before each cutoff, moves not at all.
|
|
35
|
+
- **Silent "looked-done" failure.** A tool call that returned a success marker
|
|
36
|
+
(✓) in an aborted chunk does not guarantee its effect was committed and
|
|
37
|
+
survived the cutoff. The model can reasonably believe a step succeeded, report
|
|
38
|
+
the whole task complete, and leave nothing (or only some rows) actually
|
|
39
|
+
persisted. The user is told it worked; the data says otherwise.
|
|
40
|
+
|
|
41
|
+
Net effect: long multi-step runs can spin indefinitely, never finish, and
|
|
42
|
+
terminate with an untruthful "done" state.
|
|
43
|
+
|
|
44
|
+
## Goals
|
|
45
|
+
|
|
46
|
+
1. Long multi-step runs **complete reliably** — they make monotonic forward
|
|
47
|
+
progress across continuation chunks rather than re-doing work.
|
|
48
|
+
2. The user always gets a **truthful terminal state**: either "completed, here
|
|
49
|
+
is concrete proof (N of N persisted, ids …)", or an honest "did not finish,
|
|
50
|
+
here is what was committed (M of N) and what remains" — never a false
|
|
51
|
+
success.
|
|
52
|
+
3. No change to the upstream walls and no raising of the 40s soft timeout (see
|
|
53
|
+
Guardrail).
|
|
54
|
+
|
|
55
|
+
## Non-goals
|
|
56
|
+
|
|
57
|
+
- Raising or removing the soft timeout. It is correct; see Guardrail.
|
|
58
|
+
- Changing the gateway, serverless function limits, or model call timeout.
|
|
59
|
+
- Replacing `auto_continue`. Both approaches below build on it.
|
|
60
|
+
|
|
61
|
+
## Guardrail: the 40s soft timeout is correct and must not be raised
|
|
62
|
+
|
|
63
|
+
`DEFAULT_HOSTED_RUN_SOFT_TIMEOUT_MS` / `HOSTED_SOFT_TIMEOUT_CEILING_MS` =
|
|
64
|
+
`40_000` is intentional headroom under the upstream hard walls. Raising it does
|
|
65
|
+
not buy more time — it just converts a graceful hand-off into a hard kill. The
|
|
66
|
+
walls, in order:
|
|
67
|
+
|
|
68
|
+
1. **Builder model gateway hard cap — ~45s.**
|
|
69
|
+
`MAX_BUILDER_GATEWAY_TIMEOUT_MS = 45_000` in
|
|
70
|
+
`packages/core/src/agent/engine/builder-engine.ts`. A single model call is
|
|
71
|
+
killed at the gateway after 45s. **Not raisable** by the framework.
|
|
72
|
+
2. **Serverless function kill — ~60–65s.** The hosting function is terminated
|
|
73
|
+
shortly after; the heartbeat then reaps the run row as `stale_run`.
|
|
74
|
+
|
|
75
|
+
40s leaves ~5s under the gateway wall to abort, persist the partial turn, write
|
|
76
|
+
the terminal event, and emit a clean `auto_continue` so the client resumes. A
|
|
77
|
+
larger value (production saw per-template overrides like `240_000`) pushes the
|
|
78
|
+
cutoff past both walls, so `auto_continue` never fires and the run dies as
|
|
79
|
+
`builder_gateway_timeout` / `stale_run` instead. The ceiling clamp in
|
|
80
|
+
`resolveRunSoftTimeoutMs` exists precisely to defeat that footgun. **Do not
|
|
81
|
+
raise it. Fix durability above the timeout, not by moving the timeout.**
|
|
82
|
+
|
|
83
|
+
## Approach options
|
|
84
|
+
|
|
85
|
+
Both options keep the 40s budget and build on the existing `auto_continue`
|
|
86
|
+
mechanism. They differ in _where the long work lives_.
|
|
87
|
+
|
|
88
|
+
### Option A — Checkpointed / idempotent continuation
|
|
89
|
+
|
|
90
|
+
Keep the work inside the normal run/`auto_continue` loop, but make each
|
|
91
|
+
continuation chunk **resume from committed progress instead of restarting**.
|
|
92
|
+
|
|
93
|
+
Mechanism:
|
|
94
|
+
|
|
95
|
+
- **Persist a progress record** for the operation (a checkpoint): the planned
|
|
96
|
+
unit of work (the N items / steps), and which units are already committed.
|
|
97
|
+
This lives in SQL so it survives chunk boundaries and function recycling, the
|
|
98
|
+
same way run rows do.
|
|
99
|
+
- **Idempotent steps.** Each step keys off a stable identity so re-issuing a
|
|
100
|
+
completed step is a no-op (upsert by natural key, or "skip if checkpoint says
|
|
101
|
+
done"). Re-hydration after `auto_continue` then can't double-apply or
|
|
102
|
+
thrash — a redone step costs a cheap check, not a duplicate write.
|
|
103
|
+
- **Resume, don't replan.** On `auto_continue`, the next chunk reads the
|
|
104
|
+
checkpoint, skips committed units, and continues with the remainder. Progress
|
|
105
|
+
is monotonic: every chunk that does anything moves the committed count up.
|
|
106
|
+
- **Truthful terminal state from the checkpoint.** "Done" means the checkpoint
|
|
107
|
+
shows N of N committed. If the run is cut for good (e.g. it exhausts a
|
|
108
|
+
continuation budget), the checkpoint still reports M of N committed and the
|
|
109
|
+
exact remainder — so the terminal message is honest by construction.
|
|
110
|
+
|
|
111
|
+
Interaction with the walls and the 40s budget:
|
|
112
|
+
|
|
113
|
+
- Fully respects the 40s soft timeout and the gateway/function walls — it never
|
|
114
|
+
needs a single chunk to outlast them. It just makes the _sequence_ of chunks
|
|
115
|
+
productive.
|
|
116
|
+
- Works hand-in-glove with `auto_continue`: today a continuation can redo work;
|
|
117
|
+
with a checkpoint, a continuation can only advance.
|
|
118
|
+
|
|
119
|
+
Tradeoffs:
|
|
120
|
+
|
|
121
|
+
- Pro: smallest change to the runtime model; no new infrastructure; the user
|
|
122
|
+
keeps watching one live turn; degrades gracefully (even a half-finished run is
|
|
123
|
+
truthful and re-runnable).
|
|
124
|
+
- Pro: directly kills re-hydration thrash, the actual failure mode.
|
|
125
|
+
- Con: still bounded by however many continuation chunks the client/turn budget
|
|
126
|
+
allows. A truly enormous job (thousands of steps) can still run out of chunks
|
|
127
|
+
— but it now ends _truthfully partial and resumable_, not silently empty.
|
|
128
|
+
- Con: requires per-operation work to define the unit of progress and make
|
|
129
|
+
steps idempotent. Best paid down once at the primitive/action layer (see
|
|
130
|
+
Tie-in) so individual agents don't have to.
|
|
131
|
+
|
|
132
|
+
### Option B — Out-of-band durable background execution
|
|
133
|
+
|
|
134
|
+
Hand a long run to a **queued background job** that executes beyond the
|
|
135
|
+
function/gateway lifetime and reports progress back into the run/event stream.
|
|
136
|
+
|
|
137
|
+
Mechanism:
|
|
138
|
+
|
|
139
|
+
- The foreground turn **enqueues** a durable job (the full operation + its
|
|
140
|
+
inputs) and returns immediately with "started, tracking as job X". The
|
|
141
|
+
user-facing run does not try to do the work itself within 40s.
|
|
142
|
+
- A durable worker (outside the per-request serverless function lifetime — e.g.
|
|
143
|
+
the core run-manager / agent-teams background infrastructure the framework
|
|
144
|
+
already mandates for background agents) runs the job to completion, free of
|
|
145
|
+
the 45s gateway cap and the ~60s function kill on the _original_ request.
|
|
146
|
+
- The worker **streams progress** (committed counts, ids, errors) back so the
|
|
147
|
+
UI and the agent can observe and the final state is truthful.
|
|
148
|
+
|
|
149
|
+
Interaction with the walls and the 40s budget:
|
|
150
|
+
|
|
151
|
+
- Sidesteps the gateway/function walls for the _long_ work by moving it off the
|
|
152
|
+
request path. The walls still apply to each individual model call the worker
|
|
153
|
+
makes, so the worker itself should checkpoint internally (i.e. Option B is
|
|
154
|
+
strongest when it contains Option A).
|
|
155
|
+
- `auto_continue` becomes a lightweight "is the job still running / what's its
|
|
156
|
+
progress" poll on the foreground turn rather than the vehicle for the work.
|
|
157
|
+
|
|
158
|
+
Tradeoffs:
|
|
159
|
+
|
|
160
|
+
- Pro: removes the hard ceiling on total operation length — genuinely large
|
|
161
|
+
jobs can finish.
|
|
162
|
+
- Pro: the foreground turn stays responsive and cheap; the user can leave and
|
|
163
|
+
come back.
|
|
164
|
+
- Con: more infrastructure and lifecycle complexity (job queue, durable worker,
|
|
165
|
+
progress fan-in, failure/retry semantics, surfacing job state in the UI and
|
|
166
|
+
to the agent).
|
|
167
|
+
- Con: changes the UX from "one live turn" to "fire-and-track"; needs clear
|
|
168
|
+
status surfacing so it doesn't become its own kind of silent failure.
|
|
169
|
+
|
|
170
|
+
## Recommendation
|
|
171
|
+
|
|
172
|
+
Build **Option A first**, then layer **Option B** for the genuinely unbounded
|
|
173
|
+
cases. Option A delivers the most reliability per unit of effort: it directly
|
|
174
|
+
removes re-hydration thrash and silent looked-done failure for the common case
|
|
175
|
+
(tens of steps), needs no new infrastructure, and makes terminal state truthful
|
|
176
|
+
by construction. Option B is the right ceiling-remover but is a larger build and
|
|
177
|
+
is most valuable _on top of_ a checkpointed core (the durable worker should
|
|
178
|
+
itself checkpoint).
|
|
179
|
+
|
|
180
|
+
### Phased plan
|
|
181
|
+
|
|
182
|
+
1. **Phase 0 — Stop hitting the ceiling so often (near-term, cheapest).** Land
|
|
183
|
+
the mitigations in the Tie-in below (one-call atomic primitives,
|
|
184
|
+
self-documenting actions, loud termination, proof-of-done verification).
|
|
185
|
+
These don't fix the ceiling but sharply cut how often multi-step loops are
|
|
186
|
+
even attempted, and make the failures that remain _loud and truthful_ instead
|
|
187
|
+
of silent. Capture the agent-facing half as the `reliable-mutations` skill.
|
|
188
|
+
2. **Phase 1 — Checkpointed continuation (Option A).** Add a SQL-backed progress
|
|
189
|
+
checkpoint for long operations and make their steps idempotent/resumable so
|
|
190
|
+
each `auto_continue` chunk advances committed progress instead of replanning.
|
|
191
|
+
Drive terminal state ("N of N", or "M of N + remainder") from the checkpoint.
|
|
192
|
+
This is the primary reliability win.
|
|
193
|
+
3. **Phase 2 — Durable background execution (Option B).** For operations that
|
|
194
|
+
can exceed any reasonable number of continuation chunks, enqueue them onto the
|
|
195
|
+
core background infrastructure, have the durable worker run them to completion
|
|
196
|
+
(checkpointing internally per Phase 1), and stream truthful progress back to
|
|
197
|
+
the foreground run and UI.
|
|
198
|
+
|
|
199
|
+
## Tie-in: cheaper near-term mitigations reduce, but do not replace, the fix
|
|
200
|
+
|
|
201
|
+
The following reduce _how often_ the 40s ceiling is hit and make the remaining
|
|
202
|
+
failures honest. They are valuable and should ship first (Phase 0), but the
|
|
203
|
+
**actual fix is durable/checkpointed runs** (Phases 1–2):
|
|
204
|
+
|
|
205
|
+
- **One-call atomic primitives.** Where an action can accept the whole batch
|
|
206
|
+
(e.g. "set all panels" / "append many in one call"), a single call commits
|
|
207
|
+
atomically inside one chunk instead of looping N writes that race the budget.
|
|
208
|
+
- **Self-documenting actions.** Action descriptions that steer agents toward the
|
|
209
|
+
atomic/batch call and away from per-item loops.
|
|
210
|
+
- **Loud termination.** On a time-budget cutoff, fail loud with what was and
|
|
211
|
+
wasn't committed — never report success on an aborted chunk.
|
|
212
|
+
- **Proof-of-done verification.** After a write, re-read the end state and report
|
|
213
|
+
concrete proof (counts/ids) rather than trusting a tool ✓.
|
|
214
|
+
|
|
215
|
+
The agent-facing rules for these live in the `reliable-mutations` skill
|
|
216
|
+
(`.agents/skills/reliable-mutations/SKILL.md`). They lower the blast radius;
|
|
217
|
+
checkpointed and durable runs remove the ceiling itself.
|
package/corpus/core/package.json
CHANGED
|
@@ -908,6 +908,94 @@ function zodDefToJsonSchema(def: any): any {
|
|
|
908
908
|
// Runtime validation wrapper
|
|
909
909
|
// ---------------------------------------------------------------------------
|
|
910
910
|
|
|
911
|
+
const NO_COERCE = Symbol("no-coerce");
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Coerce a single stringified value to one of the JSON-schema types the field
|
|
915
|
+
* expects. Returns NO_COERCE when nothing safe applies, so the caller leaves
|
|
916
|
+
* the original value untouched and the normal validation error still surfaces.
|
|
917
|
+
*/
|
|
918
|
+
function coerceStringToSchemaType(raw: string, types: string[]): unknown {
|
|
919
|
+
const trimmed = raw.trim();
|
|
920
|
+
if (types.includes("boolean")) {
|
|
921
|
+
if (trimmed === "true") return true;
|
|
922
|
+
if (trimmed === "false") return false;
|
|
923
|
+
}
|
|
924
|
+
if (
|
|
925
|
+
(types.includes("array") &&
|
|
926
|
+
trimmed.startsWith("[") &&
|
|
927
|
+
trimmed.endsWith("]")) ||
|
|
928
|
+
(types.includes("object") &&
|
|
929
|
+
trimmed.startsWith("{") &&
|
|
930
|
+
trimmed.endsWith("}"))
|
|
931
|
+
) {
|
|
932
|
+
try {
|
|
933
|
+
const parsed = JSON.parse(trimmed);
|
|
934
|
+
if (types.includes("array") && Array.isArray(parsed)) return parsed;
|
|
935
|
+
if (
|
|
936
|
+
types.includes("object") &&
|
|
937
|
+
parsed &&
|
|
938
|
+
typeof parsed === "object" &&
|
|
939
|
+
!Array.isArray(parsed)
|
|
940
|
+
) {
|
|
941
|
+
return parsed;
|
|
942
|
+
}
|
|
943
|
+
} catch {
|
|
944
|
+
// fall through — leave the original so validation reports the real error
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (
|
|
948
|
+
(types.includes("number") || types.includes("integer")) &&
|
|
949
|
+
trimmed !== "" &&
|
|
950
|
+
Number.isFinite(Number(trimmed))
|
|
951
|
+
) {
|
|
952
|
+
const n = Number(trimmed);
|
|
953
|
+
if (types.includes("number") || Number.isInteger(n)) return n;
|
|
954
|
+
}
|
|
955
|
+
return NO_COERCE;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Defensively coerce gateway-stringified tool arguments to the types the schema
|
|
960
|
+
* expects. Some model gateways (notably Builder's Gemini-backed gateway) hand
|
|
961
|
+
* back structured tool-call arguments as JSON strings — an array param arrives
|
|
962
|
+
* as `"[{...}]"`, a boolean as `"true"`. Standard Schema (zod) `validate` does
|
|
963
|
+
* not coerce, so these fail validation and the agent thrashes retrying shapes
|
|
964
|
+
* (and can hang). We only touch a string value when the schema expects a
|
|
965
|
+
* non-string type and the string parses cleanly to it; anything ambiguous
|
|
966
|
+
* (schema also allows string) or unparseable is left as-is. Operates on
|
|
967
|
+
* top-level properties only — once an array/object param is parsed, its nested
|
|
968
|
+
* members are already native and validate normally.
|
|
969
|
+
*/
|
|
970
|
+
function coerceGatewayStringifiedArgs(
|
|
971
|
+
args: unknown,
|
|
972
|
+
parameters?: ActionTool["parameters"],
|
|
973
|
+
): unknown {
|
|
974
|
+
const properties = parameters?.properties as
|
|
975
|
+
| Record<string, { type?: string | string[] }>
|
|
976
|
+
| undefined;
|
|
977
|
+
if (!properties) return args;
|
|
978
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return args;
|
|
979
|
+
let out: Record<string, unknown> | null = null;
|
|
980
|
+
for (const [key, raw] of Object.entries(args as Record<string, unknown>)) {
|
|
981
|
+
if (typeof raw !== "string") continue;
|
|
982
|
+
const spec = properties[key];
|
|
983
|
+
if (!spec) continue;
|
|
984
|
+
const types = Array.isArray(spec.type)
|
|
985
|
+
? spec.type
|
|
986
|
+
: spec.type
|
|
987
|
+
? [spec.type]
|
|
988
|
+
: [];
|
|
989
|
+
// No declared type, or the field legitimately accepts a string → leave it.
|
|
990
|
+
if (types.length === 0 || types.includes("string")) continue;
|
|
991
|
+
const coerced = coerceStringToSchemaType(raw, types);
|
|
992
|
+
if (coerced === NO_COERCE) continue;
|
|
993
|
+
if (!out) out = { ...(args as Record<string, unknown>) };
|
|
994
|
+
out[key] = coerced;
|
|
995
|
+
}
|
|
996
|
+
return out ?? args;
|
|
997
|
+
}
|
|
998
|
+
|
|
911
999
|
/**
|
|
912
1000
|
* Wrap an action's run function with schema validation.
|
|
913
1001
|
* Invalid inputs get a clear error message (including what was actually passed)
|
|
@@ -919,6 +1007,7 @@ function wrapWithValidation(
|
|
|
919
1007
|
toolParameters?: ActionTool["parameters"],
|
|
920
1008
|
): (args: any, ctx?: ActionRunContext) => any {
|
|
921
1009
|
return async (args: any, ctx?: ActionRunContext) => {
|
|
1010
|
+
args = coerceGatewayStringifiedArgs(args, toolParameters);
|
|
922
1011
|
const result = await schema["~standard"].validate(args);
|
|
923
1012
|
if (result.issues) {
|
|
924
1013
|
// Split issues into "missing required field" vs other validation errors
|
|
@@ -39,7 +39,6 @@ const MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
|
39
39
|
"gpt-5-5": 1_050_000,
|
|
40
40
|
"gpt-5-4": 1_050_000,
|
|
41
41
|
"gpt-5-4-mini": 400_000,
|
|
42
|
-
"gpt-5-1-codex-mini": 128_000,
|
|
43
42
|
|
|
44
43
|
// ── Gemini (Builder gateway IDs) ─────────────────────────────────────────
|
|
45
44
|
"gemini-3-1-pro": 1_048_576,
|
|
@@ -145,7 +144,6 @@ export const AGENT_MODEL_CONFIG = {
|
|
|
145
144
|
FRAMEWORK_DEFAULT_BUILDER_OPENAI_MODEL,
|
|
146
145
|
"gpt-5-4",
|
|
147
146
|
"gpt-5-4-mini",
|
|
148
|
-
"gpt-5-1-codex-mini",
|
|
149
147
|
"gemini-3-1-pro",
|
|
150
148
|
"gemini-3-5-flash",
|
|
151
149
|
"gemini-3-1-flash-lite",
|
|
@@ -90,6 +90,24 @@ async function appendToolCallJournalNote(
|
|
|
90
90
|
*/
|
|
91
91
|
export const MAX_RUN_LOOP_CONTINUATIONS = 6;
|
|
92
92
|
|
|
93
|
+
/** Machine-readable code carried on the give-up terminal `error` event so the
|
|
94
|
+
* client renders a loud "stopped before finishing" terminal instead of an
|
|
95
|
+
* ambiguous silent stall. Deliberately NOT in the client's auto-recoverable
|
|
96
|
+
* allow-list (`isAutoRecoverableError`) so it terminates the chain rather than
|
|
97
|
+
* looping another POST that would hit the same wall. */
|
|
98
|
+
export const RUN_BUDGET_EXHAUSTED_ERROR_CODE = "run_budget_exhausted";
|
|
99
|
+
|
|
100
|
+
/** User-facing terminal message when a hosted run is cut off mid-step and
|
|
101
|
+
* exhausts its in-invocation continuation budget without finishing. Generic and
|
|
102
|
+
* framework-level (not app-specific). Mirrors the `reliable-mutations` skill's
|
|
103
|
+
* "fail loud, retry as a single bulk action" guidance so the user understands
|
|
104
|
+
* the turn stopped before finishing and was not partially saved by the run
|
|
105
|
+
* itself. */
|
|
106
|
+
export const RUN_BUDGET_EXHAUSTED_MESSAGE =
|
|
107
|
+
"I ran out of time before finishing this step (hosted runs have a ~40s budget). " +
|
|
108
|
+
"I stopped rather than leave things half-done — nothing was partially saved by me here. " +
|
|
109
|
+
"Please retry, ideally as a single bulk action.";
|
|
110
|
+
|
|
93
111
|
/**
|
|
94
112
|
* Internal entry point used by the agent-chat plugin's run handler. Wraps
|
|
95
113
|
* `runAgentLoop` with soft-timeout + resumable-error continuation recovery.
|
|
@@ -124,8 +142,16 @@ export async function runAgentLoopDirectWithSoftTimeout(
|
|
|
124
142
|
};
|
|
125
143
|
|
|
126
144
|
let attempts = 0;
|
|
145
|
+
// Tracks whether the most recent attempt ended by scheduling another
|
|
146
|
+
// continuation (soft-timeout or resumable error → `continue`) rather than
|
|
147
|
+
// returning a finished turn. When the loop then exits because the budget is
|
|
148
|
+
// exhausted (NOT because the user aborted and NOT because the turn finished),
|
|
149
|
+
// this is the silent give-up case: emit a loud terminal so the user sees an
|
|
150
|
+
// unambiguous "stopped before finishing" instead of a bare done/"…".
|
|
151
|
+
let lastAttemptWasUnfinishedContinuation = false;
|
|
127
152
|
while (!upstreamSignal.aborted && attempts < MAX_RUN_LOOP_CONTINUATIONS) {
|
|
128
153
|
attempts++;
|
|
154
|
+
lastAttemptWasUnfinishedContinuation = false;
|
|
129
155
|
const controller = new AbortController();
|
|
130
156
|
const abortFromUpstream = () => controller.abort();
|
|
131
157
|
if (upstreamSignal.aborted) {
|
|
@@ -150,6 +176,7 @@ export async function runAgentLoopDirectWithSoftTimeout(
|
|
|
150
176
|
});
|
|
151
177
|
addUsage(nextUsage);
|
|
152
178
|
if (softTimedOut && !upstreamSignal.aborted) {
|
|
179
|
+
lastAttemptWasUnfinishedContinuation = true;
|
|
153
180
|
appendAgentLoopContinuation(opts.messages, "run_timeout");
|
|
154
181
|
await appendToolCallJournalNote(opts.messages, opts.threadId);
|
|
155
182
|
continue;
|
|
@@ -159,6 +186,7 @@ export async function runAgentLoopDirectWithSoftTimeout(
|
|
|
159
186
|
if (softTimedOut && !upstreamSignal.aborted) {
|
|
160
187
|
// Clear partial text the client received before the abort so the
|
|
161
188
|
// resumed model doesn't re-emit it and produce duplicated output.
|
|
189
|
+
lastAttemptWasUnfinishedContinuation = true;
|
|
162
190
|
opts.send({ type: "clear" });
|
|
163
191
|
appendAgentLoopContinuation(opts.messages, "run_timeout");
|
|
164
192
|
await appendToolCallJournalNote(opts.messages, opts.threadId);
|
|
@@ -178,6 +206,7 @@ export async function runAgentLoopDirectWithSoftTimeout(
|
|
|
178
206
|
// (the partial text was already sent to the client but never entered
|
|
179
207
|
// the in-memory messages array, so the next attempt re-emits it).
|
|
180
208
|
if (!upstreamSignal.aborted && isResumableEngineError(err)) {
|
|
209
|
+
lastAttemptWasUnfinishedContinuation = true;
|
|
181
210
|
opts.send({ type: "clear" });
|
|
182
211
|
appendAgentLoopContinuation(
|
|
183
212
|
opts.messages,
|
|
@@ -193,5 +222,24 @@ export async function runAgentLoopDirectWithSoftTimeout(
|
|
|
193
222
|
}
|
|
194
223
|
}
|
|
195
224
|
|
|
225
|
+
// The loop exited without a clean return. If the user aborted, that's a Stop —
|
|
226
|
+
// stay silent. Otherwise we only get here by exhausting
|
|
227
|
+
// MAX_RUN_LOOP_CONTINUATIONS while the last attempt was still trying to
|
|
228
|
+
// continue (soft-timeout / resumable error). That is the genuinely-silent
|
|
229
|
+
// give-up the run-manager would otherwise report as a clean `done`: emit a
|
|
230
|
+
// loud, non-auto-continuing terminal so the user knows the turn stopped
|
|
231
|
+
// before finishing and nothing was partially saved by the run itself.
|
|
232
|
+
if (!upstreamSignal.aborted && lastAttemptWasUnfinishedContinuation) {
|
|
233
|
+
// Discard any partial text already streamed for the unfinished attempt so
|
|
234
|
+
// the terminal message stands alone instead of trailing a half sentence.
|
|
235
|
+
opts.send({ type: "clear" });
|
|
236
|
+
opts.send({
|
|
237
|
+
type: "error",
|
|
238
|
+
error: RUN_BUDGET_EXHAUSTED_MESSAGE,
|
|
239
|
+
errorCode: RUN_BUDGET_EXHAUSTED_ERROR_CODE,
|
|
240
|
+
recoverable: true,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
196
244
|
return usage;
|
|
197
245
|
}
|
|
@@ -4033,8 +4033,8 @@ async function promptForPlanMode(
|
|
|
4033
4033
|
options: [
|
|
4034
4034
|
{
|
|
4035
4035
|
value: "hosted",
|
|
4036
|
-
label: "Hosted plans, shareable links",
|
|
4037
|
-
hint: "
|
|
4036
|
+
label: "Hosted plans, shareable links (recommended)",
|
|
4037
|
+
hint: "100% free and open source. Supports comments, browser editor, and sharing. Requires one-time browser sign-in.",
|
|
4038
4038
|
},
|
|
4039
4039
|
{
|
|
4040
4040
|
value: "local-files",
|
|
@@ -147,9 +147,11 @@ const SetupButton = lazy(() =>
|
|
|
147
147
|
})),
|
|
148
148
|
);
|
|
149
149
|
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
|
|
150
|
+
// The setup/onboarding checklist that used to appear above chat is disabled
|
|
151
|
+
// for every app — setup (AI engine, image/video gen, asset storage, email,
|
|
152
|
+
// GitHub, etc.) is surfaced in better places (the settings panel and the
|
|
153
|
+
// per-feature setup affordances). Keep this off; do not re-enable globally.
|
|
154
|
+
const SHOW_ONBOARDING = false;
|
|
153
155
|
|
|
154
156
|
const CLI_STORAGE_KEY = "agent-native-cli-command";
|
|
155
157
|
const CLI_DEFAULT = "claude";
|
|
@@ -86,6 +86,7 @@ import { agentNativePath } from "./api-path.js";
|
|
|
86
86
|
import {
|
|
87
87
|
TiptapComposer,
|
|
88
88
|
type ComposerSubmitIntent,
|
|
89
|
+
type ComposerImageModelMenu,
|
|
89
90
|
type TiptapComposerHandle,
|
|
90
91
|
} from "./composer/TiptapComposer.js";
|
|
91
92
|
import { AgentComposerFrame } from "./composer/AgentComposerFrame.js";
|
|
@@ -712,6 +713,11 @@ export interface AssistantChatProps {
|
|
|
712
713
|
onModelChange?: (model: string, engine: string) => void;
|
|
713
714
|
/** Callback when user picks a reasoning effort from the picker */
|
|
714
715
|
onEffortChange?: (effort: ReasoningEffort) => void;
|
|
716
|
+
/**
|
|
717
|
+
* Optional secondary model menu (e.g. an image-generation model) shown inside
|
|
718
|
+
* the composer's model picker. Opt-in; chat-only apps omit it.
|
|
719
|
+
*/
|
|
720
|
+
imageModelMenu?: ComposerImageModelMenu;
|
|
715
721
|
/** Callback when user clicks "Fork Chat" in the message actions menu */
|
|
716
722
|
onForkChat?: () => void | boolean | Promise<void | boolean>;
|
|
717
723
|
/** Override Builder/provider connect routing for embedded hosts. */
|
|
@@ -977,6 +983,7 @@ const AssistantChatInner = forwardRef<
|
|
|
977
983
|
availableModels,
|
|
978
984
|
onModelChange,
|
|
979
985
|
onEffortChange,
|
|
986
|
+
imageModelMenu,
|
|
980
987
|
onForkChat,
|
|
981
988
|
onConnectProvider,
|
|
982
989
|
plusMenuMode = "full",
|
|
@@ -3392,6 +3399,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3392
3399
|
availableModels={availableModels}
|
|
3393
3400
|
onModelChange={onModelChange}
|
|
3394
3401
|
onEffortChange={onEffortChange}
|
|
3402
|
+
imageModelMenu={imageModelMenu}
|
|
3395
3403
|
onConnectProvider={onConnectProvider}
|
|
3396
3404
|
toolbarSlot={composerToolbarSlot}
|
|
3397
3405
|
contextItems={composerContextItems}
|