@agent-native/core 0.84.0 → 0.84.2
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 +13 -0
- package/corpus/core/docs/content/evals.mdx +34 -12
- package/corpus/core/docs/content/locales/ar-SA/evals.mdx +7 -0
- package/corpus/core/docs/content/locales/de-DE/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/es-ES/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/fr-FR/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/hi-IN/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/ja-JP/evals.mdx +7 -0
- package/corpus/core/docs/content/locales/ko-KR/evals.mdx +7 -0
- package/corpus/core/docs/content/locales/pt-BR/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-CN/evals.mdx +7 -0
- package/corpus/core/docs/content/locales/zh-TW/evals.mdx +7 -0
- package/corpus/core/docs/design/durable-agent-runs.md +34 -35
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/builder-engine.ts +81 -9
- package/corpus/core/src/agent/run-manager.ts +8 -7
- package/corpus/core/src/cli/design-connect.ts +175 -0
- package/corpus/core/src/cli/skills.ts +5 -7
- package/corpus/core/src/client/AgentPanel.tsx +8 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +4 -1
- package/corpus/core/src/client/agent-chat-adapter.ts +45 -15
- package/corpus/core/src/client/sse-event-processor.ts +31 -4
- package/corpus/core/src/eval/define-eval.ts +4 -1
- package/corpus/core/src/eval/report.ts +23 -8
- package/corpus/core/src/eval/runner.ts +41 -6
- package/corpus/core/src/eval/types.ts +11 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
- package/corpus/templates/content/AGENTS.md +9 -2
- package/corpus/templates/content/actions/_database-row-batch.ts +230 -0
- package/corpus/templates/content/actions/_database-utils.ts +17 -9
- package/corpus/templates/content/actions/_notion-action-utils.ts +26 -0
- package/corpus/templates/content/actions/connect-notion-status.ts +13 -4
- package/corpus/templates/content/actions/create-and-link-notion-page.ts +23 -0
- package/corpus/templates/content/actions/delete-database-items.ts +55 -0
- package/corpus/templates/content/actions/delete-document.ts +4 -4
- package/corpus/templates/content/actions/disconnect-notion.ts +16 -0
- package/corpus/templates/content/actions/duplicate-database-item.ts +1 -1
- package/corpus/templates/content/actions/duplicate-database-items.ts +191 -0
- package/corpus/templates/content/actions/link-notion-page.ts +10 -7
- package/corpus/templates/content/actions/list-notion-links.ts +3 -4
- package/corpus/templates/content/actions/pull-notion-page.ts +7 -9
- package/corpus/templates/content/actions/push-notion-page.ts +7 -9
- package/corpus/templates/content/actions/refresh-notion-sync-status.ts +24 -0
- package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +23 -0
- package/corpus/templates/content/actions/search-notion-pages.ts +67 -0
- package/corpus/templates/content/actions/unlink-notion-page.ts +23 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +27 -34
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +16 -13
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +10 -10
- package/corpus/templates/content/app/components/editor/NotionConflictBanner.tsx +1 -1
- package/corpus/templates/content/app/components/editor/NotionSyncBar.tsx +18 -9
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +27 -34
- package/corpus/templates/content/app/components/editor/database/navigation-state.ts +10 -4
- package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +32 -17
- package/corpus/templates/content/app/hooks/use-content-database.ts +35 -0
- package/corpus/templates/content/app/hooks/use-notion.ts +108 -99
- package/corpus/templates/content/package.json +3 -0
- package/corpus/templates/content/parity/README.md +69 -0
- package/corpus/templates/content/parity/eval-scenarios.ts +118 -0
- package/corpus/templates/content/parity/exceptions.allowlist.ts +17 -0
- package/corpus/templates/content/parity/matrix.md +28 -0
- package/corpus/templates/content/parity/matrix.ts +606 -0
- package/corpus/templates/content/parity/matrix.types.ts +42 -0
- package/corpus/templates/content/parity/parity-evals.eval.ts +4 -0
- package/corpus/templates/content/parity/render-matrix.ts +90 -0
- package/corpus/templates/content/parity/scenario-to-eval.ts +57 -0
- package/corpus/templates/content/shared/api.ts +11 -0
- package/corpus/templates/design/actions/apply-motion-edit.ts +50 -18
- package/corpus/templates/design/actions/get-motion-timeline.ts +4 -14
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +156 -12
- package/corpus/templates/design/app/components/design/MotionDock.tsx +2 -3
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +217 -75
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +39 -39
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +558 -37
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +75 -3
- package/corpus/templates/design/app/components/design/types.ts +13 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +17 -0
- package/corpus/templates/design/app/i18n-data.ts +245 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +762 -153
- package/corpus/templates/design/changelog/2026-06-30-copying-or-dragging-screen-elements-onto-the-infinite-canvas.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-questions-now-use-tighter-editor-typography-and-contr.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-element-drags-can-be-cancelled-with-escape-before-they-commi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-pending-visual-style-edits-now-warn-before-you-leave-the-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-visual-style-drags-stay-live-while-pending-edits-can-be-appl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-01-design-chat-no-longer-shows-a-redundant-context-tab-above-the-composer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-01-motion-track-creation-no-longer-fails-in-local-editors.md +6 -0
- package/corpus/templates/design/server/plugins/db.ts +10 -0
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +61 -10
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/run-manager.d.ts +8 -7
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +8 -7
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/cli/design-connect.d.ts +2 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +140 -0
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +5 -7
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +4 -4
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts +3 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +2 -2
- 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 +37 -14
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +14 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +21 -5
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/eval/define-eval.d.ts.map +1 -1
- package/dist/eval/define-eval.js +2 -1
- package/dist/eval/define-eval.js.map +1 -1
- package/dist/eval/report.d.ts.map +1 -1
- package/dist/eval/report.js +19 -4
- package/dist/eval/report.js.map +1 -1
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +38 -6
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval/types.d.ts +11 -0
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/eval/types.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
- package/docs/content/evals.mdx +34 -12
- package/docs/content/locales/ar-SA/evals.mdx +7 -0
- package/docs/content/locales/de-DE/evals.mdx +8 -0
- package/docs/content/locales/es-ES/evals.mdx +8 -0
- package/docs/content/locales/fr-FR/evals.mdx +8 -0
- package/docs/content/locales/hi-IN/evals.mdx +8 -0
- package/docs/content/locales/ja-JP/evals.mdx +7 -0
- package/docs/content/locales/ko-KR/evals.mdx +7 -0
- package/docs/content/locales/pt-BR/evals.mdx +8 -0
- package/docs/content/locales/zh-CN/evals.mdx +7 -0
- package/docs/content/locales/zh-TW/evals.mdx +7 -0
- package/docs/design/durable-agent-runs.md +34 -35
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
- package/corpus/templates/content/server/routes/api/notion/disconnect.post.ts +0 -12
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
1
2
|
import crypto from "node:crypto";
|
|
2
3
|
import fsSync from "node:fs";
|
|
3
4
|
import fs from "node:fs/promises";
|
|
@@ -44,6 +45,7 @@ export interface DesignConnectArgs {
|
|
|
44
45
|
json: boolean;
|
|
45
46
|
once: boolean;
|
|
46
47
|
dryRun: boolean;
|
|
48
|
+
daemon: boolean;
|
|
47
49
|
help: boolean;
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -115,6 +117,7 @@ export function parseDesignConnectArgs(argv: string[]): DesignConnectArgs {
|
|
|
115
117
|
json: false,
|
|
116
118
|
once: false,
|
|
117
119
|
dryRun: false,
|
|
120
|
+
daemon: false,
|
|
118
121
|
help: false,
|
|
119
122
|
};
|
|
120
123
|
|
|
@@ -155,6 +158,8 @@ export function parseDesignConnectArgs(argv: string[]): DesignConnectArgs {
|
|
|
155
158
|
} else if (arg === "--dry-run") {
|
|
156
159
|
parsed.dryRun = true;
|
|
157
160
|
parsed.once = true;
|
|
161
|
+
} else if (arg === "--daemon") {
|
|
162
|
+
parsed.daemon = true;
|
|
158
163
|
} else if (arg.startsWith("-")) {
|
|
159
164
|
throw new Error(`Unknown option: ${arg}`);
|
|
160
165
|
} else {
|
|
@@ -165,6 +170,11 @@ export function parseDesignConnectArgs(argv: string[]): DesignConnectArgs {
|
|
|
165
170
|
if (!Number.isInteger(parsed.port) || parsed.port <= 0) {
|
|
166
171
|
throw new Error("--port must be a positive integer");
|
|
167
172
|
}
|
|
173
|
+
if (parsed.daemon && (parsed.json || parsed.once || parsed.dryRun)) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
"--daemon cannot be combined with --json, --once, or --dry-run",
|
|
176
|
+
);
|
|
177
|
+
}
|
|
168
178
|
parsed.root = path.resolve(parsed.root);
|
|
169
179
|
parsed.url = parsed.url ? normalizeHttpUrl(parsed.url) : undefined;
|
|
170
180
|
return parsed;
|
|
@@ -298,6 +308,86 @@ async function probeDevServer(url: string): Promise<boolean> {
|
|
|
298
308
|
}
|
|
299
309
|
}
|
|
300
310
|
|
|
311
|
+
async function waitForBridgeHealth(
|
|
312
|
+
bridgeUrl: string,
|
|
313
|
+
timeoutMs = 5_000,
|
|
314
|
+
): Promise<boolean> {
|
|
315
|
+
const healthUrl = new URL("/health", bridgeUrl).toString();
|
|
316
|
+
const deadline = Date.now() + timeoutMs;
|
|
317
|
+
while (Date.now() < deadline) {
|
|
318
|
+
const controller = new AbortController();
|
|
319
|
+
const timeout = setTimeout(() => controller.abort(), 400);
|
|
320
|
+
try {
|
|
321
|
+
const response = await fetch(healthUrl, {
|
|
322
|
+
method: "GET",
|
|
323
|
+
signal: controller.signal,
|
|
324
|
+
});
|
|
325
|
+
if (response.ok) return true;
|
|
326
|
+
} catch {
|
|
327
|
+
// Keep polling until the detached process finishes binding the port.
|
|
328
|
+
} finally {
|
|
329
|
+
clearTimeout(timeout);
|
|
330
|
+
}
|
|
331
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
332
|
+
}
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function isDesignConnectManifest(
|
|
337
|
+
value: unknown,
|
|
338
|
+
): value is DesignConnectManifest {
|
|
339
|
+
if (!value || typeof value !== "object") return false;
|
|
340
|
+
const manifest = value as Partial<DesignConnectManifest>;
|
|
341
|
+
return (
|
|
342
|
+
manifest.version === 1 &&
|
|
343
|
+
manifest.source === "agent-native-design-connect" &&
|
|
344
|
+
manifest.sourceType === "localhost" &&
|
|
345
|
+
manifest.localOnly === true &&
|
|
346
|
+
typeof manifest.devServerUrl === "string" &&
|
|
347
|
+
typeof manifest.bridgeUrl === "string" &&
|
|
348
|
+
typeof manifest.rootPath === "string"
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async function fetchRunningBridgeManifest(
|
|
353
|
+
bridgeUrl: string,
|
|
354
|
+
): Promise<DesignConnectManifest | null> {
|
|
355
|
+
const manifestUrl = new URL("/manifest.json", bridgeUrl).toString();
|
|
356
|
+
const controller = new AbortController();
|
|
357
|
+
const timeout = setTimeout(() => controller.abort(), 800);
|
|
358
|
+
try {
|
|
359
|
+
const response = await fetch(manifestUrl, {
|
|
360
|
+
method: "GET",
|
|
361
|
+
signal: controller.signal,
|
|
362
|
+
});
|
|
363
|
+
if (!response.ok) return null;
|
|
364
|
+
const body = (await response.json()) as unknown;
|
|
365
|
+
return isDesignConnectManifest(body) ? body : null;
|
|
366
|
+
} catch {
|
|
367
|
+
return null;
|
|
368
|
+
} finally {
|
|
369
|
+
clearTimeout(timeout);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function designConnectManifestsTargetSameApp(
|
|
374
|
+
running: Pick<DesignConnectManifest, "devServerUrl" | "rootPath">,
|
|
375
|
+
requested: Pick<DesignConnectManifest, "devServerUrl" | "rootPath">,
|
|
376
|
+
): boolean {
|
|
377
|
+
let runningUrl = running.devServerUrl;
|
|
378
|
+
let requestedUrl = requested.devServerUrl;
|
|
379
|
+
try {
|
|
380
|
+
runningUrl = normalizeHttpUrl(runningUrl);
|
|
381
|
+
requestedUrl = normalizeHttpUrl(requestedUrl);
|
|
382
|
+
} catch {
|
|
383
|
+
// Fall through to direct string comparison for malformed legacy manifests.
|
|
384
|
+
}
|
|
385
|
+
return (
|
|
386
|
+
runningUrl === requestedUrl &&
|
|
387
|
+
path.resolve(running.rootPath) === path.resolve(requested.rootPath)
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
301
391
|
async function resolveDevServerUrl(url?: string): Promise<string> {
|
|
302
392
|
if (url) return normalizeHttpUrl(url);
|
|
303
393
|
for (const candidate of DEFAULT_DEV_SERVER_CANDIDATES) {
|
|
@@ -942,6 +1032,7 @@ Options:
|
|
|
942
1032
|
--route-manifest <path> Non-destructive route manifest output path
|
|
943
1033
|
--app-url <url> Deployed design app URL for self-registration
|
|
944
1034
|
(also reads AGENT_NATIVE_URL / DESIGN_APP_URL env)
|
|
1035
|
+
--daemon Start the bridge detached, wait for /health, then exit
|
|
945
1036
|
--json Print the manifest JSON and exit
|
|
946
1037
|
--once Prepare/scaffold the manifest and exit
|
|
947
1038
|
--dry-run Print what would be exposed without writing files
|
|
@@ -966,6 +1057,87 @@ Element provenance (resolveNodeToFile):
|
|
|
966
1057
|
Cross-origin localhost iframes cannot be read regardless of attributes (CSP).`);
|
|
967
1058
|
}
|
|
968
1059
|
|
|
1060
|
+
function removeDaemonFlag(argv: string[]): string[] {
|
|
1061
|
+
return argv.filter((arg) => arg !== "--daemon");
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function resolveCurrentCliInvocation(argv: string[]): {
|
|
1065
|
+
command: string;
|
|
1066
|
+
args: string[];
|
|
1067
|
+
} {
|
|
1068
|
+
const suffixLength = argv.length + 1; // leading "design" command + runDesign argv
|
|
1069
|
+
const prefixEnd = Math.max(1, process.argv.length - suffixLength);
|
|
1070
|
+
const cliPrefix = process.argv.slice(1, prefixEnd);
|
|
1071
|
+
const entry = cliPrefix[0] ?? process.argv[1];
|
|
1072
|
+
if (!entry) {
|
|
1073
|
+
throw new Error("Could not resolve current CLI entrypoint for --daemon");
|
|
1074
|
+
}
|
|
1075
|
+
if (entry.endsWith(".ts") || entry.endsWith(".tsx")) {
|
|
1076
|
+
return {
|
|
1077
|
+
command: "tsx",
|
|
1078
|
+
args: [...cliPrefix, "design", ...removeDaemonFlag(argv)],
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
return {
|
|
1082
|
+
command: process.execPath,
|
|
1083
|
+
args: [...cliPrefix, "design", ...removeDaemonFlag(argv)],
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
async function startDetachedDesignBridge(
|
|
1088
|
+
argv: string[],
|
|
1089
|
+
manifest: DesignConnectManifest,
|
|
1090
|
+
): Promise<number> {
|
|
1091
|
+
if (await waitForBridgeHealth(manifest.bridgeUrl, 800)) {
|
|
1092
|
+
const runningManifest = await fetchRunningBridgeManifest(
|
|
1093
|
+
manifest.bridgeUrl,
|
|
1094
|
+
);
|
|
1095
|
+
if (
|
|
1096
|
+
runningManifest &&
|
|
1097
|
+
designConnectManifestsTargetSameApp(runningManifest, manifest)
|
|
1098
|
+
) {
|
|
1099
|
+
console.error(
|
|
1100
|
+
`Design localhost bridge already running at ${manifest.bridgeUrl}`,
|
|
1101
|
+
);
|
|
1102
|
+
console.log(JSON.stringify(runningManifest, null, 2));
|
|
1103
|
+
return 0;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
console.error(
|
|
1107
|
+
[
|
|
1108
|
+
`Design localhost bridge already running at ${manifest.bridgeUrl} for a different app.`,
|
|
1109
|
+
runningManifest
|
|
1110
|
+
? `Running app: ${runningManifest.devServerUrl} (${runningManifest.rootPath})`
|
|
1111
|
+
: "Running bridge did not expose a compatible manifest.",
|
|
1112
|
+
`Requested app: ${manifest.devServerUrl} (${manifest.rootPath})`,
|
|
1113
|
+
"Stop the existing bridge or choose a different --port.",
|
|
1114
|
+
].join("\n"),
|
|
1115
|
+
);
|
|
1116
|
+
return 1;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const invocation = resolveCurrentCliInvocation(argv);
|
|
1120
|
+
const child = spawn(invocation.command, invocation.args, {
|
|
1121
|
+
cwd: process.cwd(),
|
|
1122
|
+
detached: true,
|
|
1123
|
+
env: process.env,
|
|
1124
|
+
stdio: "ignore",
|
|
1125
|
+
shell: process.platform === "win32",
|
|
1126
|
+
});
|
|
1127
|
+
child.unref();
|
|
1128
|
+
|
|
1129
|
+
if (await waitForBridgeHealth(manifest.bridgeUrl)) {
|
|
1130
|
+
console.error(`Design localhost bridge running at ${manifest.bridgeUrl}`);
|
|
1131
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
1132
|
+
return 0;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
console.error(
|
|
1136
|
+
`Timed out waiting for detached Design bridge at ${manifest.bridgeUrl}`,
|
|
1137
|
+
);
|
|
1138
|
+
return 1;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
969
1141
|
export async function runDesign(argv: string[]) {
|
|
970
1142
|
const subcommand = argv[0];
|
|
971
1143
|
if (subcommand !== "connect") {
|
|
@@ -988,6 +1160,9 @@ export async function runDesign(argv: string[]) {
|
|
|
988
1160
|
}
|
|
989
1161
|
|
|
990
1162
|
const manifest = await prepareDesignConnectManifest(parsed);
|
|
1163
|
+
if (parsed.daemon) {
|
|
1164
|
+
return startDetachedDesignBridge(argv, manifest);
|
|
1165
|
+
}
|
|
991
1166
|
if (parsed.json || parsed.once || parsed.dryRun) {
|
|
992
1167
|
console.log(JSON.stringify(manifest, null, 2));
|
|
993
1168
|
return 0;
|
|
@@ -464,18 +464,16 @@ iframe-backed screens on the infinite canvas.
|
|
|
464
464
|
From the target app repo, make sure its dev server is running, then run:
|
|
465
465
|
|
|
466
466
|
\`\`\`bash
|
|
467
|
-
npx @agent-native/core@latest design connect --url http://localhost:5173 --root .
|
|
467
|
+
npx @agent-native/core@latest design connect --url http://localhost:5173 --root . --daemon
|
|
468
468
|
\`\`\`
|
|
469
469
|
|
|
470
|
-
Use the app's real port. The command starts a local bridge on
|
|
471
|
-
\`http://127.0.0.1:7331\` by default
|
|
472
|
-
|
|
470
|
+
Use the app's real port. The command starts a detached local bridge on
|
|
471
|
+
\`http://127.0.0.1:7331\` by default, waits for \`/health\`, prints the
|
|
472
|
+
manifest JSON, and keeps the bridge alive after the agent command exits.
|
|
473
473
|
|
|
474
|
-
For
|
|
475
|
-
a second terminal if the user needs live updates:
|
|
474
|
+
For a manual health/manifest check:
|
|
476
475
|
|
|
477
476
|
\`\`\`bash
|
|
478
|
-
npx @agent-native/core@latest design connect --url http://localhost:5173 --root .
|
|
479
477
|
curl http://127.0.0.1:7331/manifest.json
|
|
480
478
|
\`\`\`
|
|
481
479
|
|
|
@@ -548,6 +548,8 @@ export interface AgentPanelProps extends Omit<
|
|
|
548
548
|
* see the `Layout` files for each template.
|
|
549
549
|
*/
|
|
550
550
|
scope?: import("./use-chat-threads.js").ChatThreadScope | null;
|
|
551
|
+
/** Show the compact scope chip above the composer. Default: true. */
|
|
552
|
+
showScopeBadge?: MultiTabAssistantChatProps["showScopeBadge"];
|
|
551
553
|
/** Stable browser tab id used for tab-scoped app-state context. */
|
|
552
554
|
browserTabId?: string;
|
|
553
555
|
/** Keep chat thread selection in URL state. */
|
|
@@ -673,6 +675,7 @@ function AgentPanelInner({
|
|
|
673
675
|
storageKey,
|
|
674
676
|
restoreActiveThread = true,
|
|
675
677
|
scope,
|
|
678
|
+
showScopeBadge,
|
|
676
679
|
browserTabId,
|
|
677
680
|
threadUrlSync,
|
|
678
681
|
chatNotice,
|
|
@@ -1710,6 +1713,7 @@ function AgentPanelInner({
|
|
|
1710
1713
|
storageKey={storageKey}
|
|
1711
1714
|
restoreActiveThread={restoreActiveThread}
|
|
1712
1715
|
scope={scope}
|
|
1716
|
+
showScopeBadge={showScopeBadge}
|
|
1713
1717
|
browserTabId={browserTabId}
|
|
1714
1718
|
threadUrlSync={threadUrlSync}
|
|
1715
1719
|
/>
|
|
@@ -2393,6 +2397,8 @@ export interface AgentSidebarProps {
|
|
|
2393
2397
|
* Templates compute this from the active route (see template layouts).
|
|
2394
2398
|
*/
|
|
2395
2399
|
scope?: import("./use-chat-threads.js").ChatThreadScope | null;
|
|
2400
|
+
/** Show the compact scope chip above the composer. Default: true. */
|
|
2401
|
+
showScopeBadge?: MultiTabAssistantChatProps["showScopeBadge"];
|
|
2396
2402
|
/** Stable browser tab id used for tab-scoped app-state context. */
|
|
2397
2403
|
browserTabId?: string;
|
|
2398
2404
|
/** Keep chat thread selection in URL state. */
|
|
@@ -2422,6 +2428,7 @@ export function AgentSidebar({
|
|
|
2422
2428
|
openOnChatRunning = false,
|
|
2423
2429
|
onFullscreenRequest,
|
|
2424
2430
|
scope,
|
|
2431
|
+
showScopeBadge,
|
|
2425
2432
|
browserTabId,
|
|
2426
2433
|
threadUrlSync,
|
|
2427
2434
|
}: AgentSidebarProps) {
|
|
@@ -2955,6 +2962,7 @@ export function AgentSidebar({
|
|
|
2955
2962
|
}
|
|
2956
2963
|
storageKey={storageKey}
|
|
2957
2964
|
scope={scope}
|
|
2965
|
+
showScopeBadge={showScopeBadge}
|
|
2958
2966
|
browserTabId={browserTabId}
|
|
2959
2967
|
threadUrlSync={threadUrlSync}
|
|
2960
2968
|
/>
|
|
@@ -929,6 +929,8 @@ export type MultiTabAssistantChatProps = Omit<
|
|
|
929
929
|
* the composer.
|
|
930
930
|
*/
|
|
931
931
|
scope?: ChatThreadScope | null;
|
|
932
|
+
/** Show the compact scope chip above the composer. Default: true. */
|
|
933
|
+
showScopeBadge?: boolean;
|
|
932
934
|
};
|
|
933
935
|
|
|
934
936
|
export function MultiTabAssistantChat({
|
|
@@ -942,6 +944,7 @@ export function MultiTabAssistantChat({
|
|
|
942
944
|
browserTabId,
|
|
943
945
|
threadUrlSync = false,
|
|
944
946
|
scope = null,
|
|
947
|
+
showScopeBadge = true,
|
|
945
948
|
...props
|
|
946
949
|
}: MultiTabAssistantChatProps) {
|
|
947
950
|
const {
|
|
@@ -2625,7 +2628,7 @@ export function MultiTabAssistantChat({
|
|
|
2625
2628
|
? props.dynamicSuggestions
|
|
2626
2629
|
: false;
|
|
2627
2630
|
const scopeComposerSlot =
|
|
2628
|
-
tabId === activeThreadId && !contentHidden ? (
|
|
2631
|
+
showScopeBadge && tabId === activeThreadId && !contentHidden ? (
|
|
2629
2632
|
tabScope && activeThreadId ? (
|
|
2630
2633
|
<ScopeBadge
|
|
2631
2634
|
scope={tabScope}
|
|
@@ -17,6 +17,7 @@ import { formatChatErrorText, normalizeChatError } from "./error-format.js";
|
|
|
17
17
|
import {
|
|
18
18
|
AgentAutoContinueSignal,
|
|
19
19
|
type AgentActivityTrailEntry,
|
|
20
|
+
type AgentAutoContinueErrorInfo,
|
|
20
21
|
type ContentPart,
|
|
21
22
|
readSSEStream,
|
|
22
23
|
settleInterruptedToolCalls,
|
|
@@ -1353,6 +1354,7 @@ export function createAgentChatAdapter(
|
|
|
1353
1354
|
const structuredContinuationFragments: AgentChatStructuredMessage[] = [];
|
|
1354
1355
|
let visibleContinuationPrefix: ContentPart[] = [];
|
|
1355
1356
|
let lastAutoContinueReason: string | null = null;
|
|
1357
|
+
let lastRecoverableRunError: AgentAutoContinueErrorInfo | null = null;
|
|
1356
1358
|
const attemptedRunIds: string[] = [];
|
|
1357
1359
|
let authRecoveryAttempted = false;
|
|
1358
1360
|
let continuationToolCallCounter = 0;
|
|
@@ -1364,6 +1366,12 @@ export function createAgentChatAdapter(
|
|
|
1364
1366
|
lastAutoContinueReason
|
|
1365
1367
|
? `last_auto_continue_reason: ${lastAutoContinueReason}`
|
|
1366
1368
|
: "",
|
|
1369
|
+
lastRecoverableRunError?.errorCode
|
|
1370
|
+
? `last_recoverable_error_code: ${lastRecoverableRunError.errorCode}`
|
|
1371
|
+
: "",
|
|
1372
|
+
lastRecoverableRunError?.message
|
|
1373
|
+
? `last_recoverable_error: ${lastRecoverableRunError.message}`
|
|
1374
|
+
: "",
|
|
1367
1375
|
`stale_run_continuations: ${staleRunContinuationAttempts}`,
|
|
1368
1376
|
`stalled_transient_continuations: ${stalledTransientContinuationAttempts}`,
|
|
1369
1377
|
`empty_transient_continuations: ${emptyTransientContinuationAttempts}`,
|
|
@@ -1677,6 +1685,9 @@ export function createAgentChatAdapter(
|
|
|
1677
1685
|
signal: AgentAutoContinueSignal,
|
|
1678
1686
|
): { ok: boolean; resetVisibleContent: boolean } => {
|
|
1679
1687
|
lastAutoContinueReason = signal.reason;
|
|
1688
|
+
if (signal.errorInfo) {
|
|
1689
|
+
lastRecoverableRunError = signal.errorInfo;
|
|
1690
|
+
}
|
|
1680
1691
|
const isTransient = signal.reason !== "loop_limit";
|
|
1681
1692
|
const visibleContent = visibleContentForContinuation();
|
|
1682
1693
|
const currentPartialHistory =
|
|
@@ -1686,14 +1697,12 @@ export function createAgentChatAdapter(
|
|
|
1686
1697
|
// whitespace-only output cannot keep the run alive indefinitely.
|
|
1687
1698
|
const madeContentProgress = hasContinuationProgress(visibleContent);
|
|
1688
1699
|
// An action was streamed but has not returned yet (a tool_start with
|
|
1689
|
-
// no tool_done)
|
|
1690
|
-
//
|
|
1691
|
-
//
|
|
1692
|
-
//
|
|
1693
|
-
//
|
|
1694
|
-
const hasInFlightTool =
|
|
1695
|
-
hasInFlightToolCall(visibleContent) ||
|
|
1696
|
-
Boolean(lastActivityTool(signal.activityTrail));
|
|
1700
|
+
// no tool_done). This is durable enough to survive continuation: the
|
|
1701
|
+
// server already emitted a real tool call. A tool-scoped activity
|
|
1702
|
+
// card ("Preparing generate-design") is useful UI, but it happens
|
|
1703
|
+
// before tool_start; treating it as progress caused silent retry
|
|
1704
|
+
// loops when the LLM timed out while assembling a large tool input.
|
|
1705
|
+
const hasInFlightTool = hasInFlightToolCall(visibleContent);
|
|
1697
1706
|
// Either real output or an actively-running tool counts as progress
|
|
1698
1707
|
// for the stalled/empty caps.
|
|
1699
1708
|
const madeProgress = madeContentProgress || hasInFlightTool;
|
|
@@ -2174,15 +2183,28 @@ export function createAgentChatAdapter(
|
|
|
2174
2183
|
}
|
|
2175
2184
|
const continuation = prepareAutoContinuation(err);
|
|
2176
2185
|
if (!continuation.ok) {
|
|
2177
|
-
const
|
|
2186
|
+
const preservedError =
|
|
2187
|
+
err.errorInfo ?? lastRecoverableRunError ?? null;
|
|
2188
|
+
const message =
|
|
2189
|
+
preservedError?.message ??
|
|
2190
|
+
exhaustedRecoveryMessage(err.reason);
|
|
2191
|
+
const details = [
|
|
2192
|
+
preservedError?.details,
|
|
2193
|
+
connectionRecoveryDetails(),
|
|
2194
|
+
]
|
|
2195
|
+
.filter(Boolean)
|
|
2196
|
+
.join("\n\n");
|
|
2197
|
+
const errorCode =
|
|
2198
|
+
preservedError?.errorCode ?? "connection_error";
|
|
2178
2199
|
captureChatClientError(err, "auto-continuation-exhausted", {
|
|
2179
2200
|
autoContinueReason: err.reason,
|
|
2201
|
+
...(errorCode ? { errorCode } : {}),
|
|
2180
2202
|
});
|
|
2181
2203
|
const runError = {
|
|
2182
2204
|
message,
|
|
2183
|
-
details:
|
|
2184
|
-
errorCode
|
|
2185
|
-
recoverable: true,
|
|
2205
|
+
...(details ? { details } : {}),
|
|
2206
|
+
errorCode,
|
|
2207
|
+
recoverable: preservedError?.recoverable ?? true,
|
|
2186
2208
|
...(runId ? { runId } : {}),
|
|
2187
2209
|
};
|
|
2188
2210
|
if (typeof window !== "undefined") {
|
|
@@ -2192,10 +2214,16 @@ export function createAgentChatAdapter(
|
|
|
2192
2214
|
}),
|
|
2193
2215
|
);
|
|
2194
2216
|
}
|
|
2195
|
-
settleInterruptedToolCalls(content
|
|
2217
|
+
settleInterruptedToolCalls(content, undefined, {
|
|
2218
|
+
includeActivity: true,
|
|
2219
|
+
});
|
|
2196
2220
|
content.push({
|
|
2197
2221
|
type: "text",
|
|
2198
|
-
text:
|
|
2222
|
+
text: formatChatErrorText(
|
|
2223
|
+
message,
|
|
2224
|
+
preservedError?.upgradeUrl,
|
|
2225
|
+
errorCode,
|
|
2226
|
+
),
|
|
2199
2227
|
});
|
|
2200
2228
|
yield {
|
|
2201
2229
|
content: [...content],
|
|
@@ -2343,7 +2371,9 @@ export function createAgentChatAdapter(
|
|
|
2343
2371
|
}),
|
|
2344
2372
|
);
|
|
2345
2373
|
}
|
|
2346
|
-
settleInterruptedToolCalls(content
|
|
2374
|
+
settleInterruptedToolCalls(content, undefined, {
|
|
2375
|
+
includeActivity: true,
|
|
2376
|
+
});
|
|
2347
2377
|
content.push({
|
|
2348
2378
|
type: "text",
|
|
2349
2379
|
text: `Something went wrong: ${message}`,
|
|
@@ -80,21 +80,34 @@ export type AgentAutoContinueReason =
|
|
|
80
80
|
|
|
81
81
|
export type AgentActivityTrailEntry = { label: string; tool?: string };
|
|
82
82
|
|
|
83
|
+
export interface AgentAutoContinueErrorInfo {
|
|
84
|
+
message: string;
|
|
85
|
+
details?: string;
|
|
86
|
+
errorCode?: string;
|
|
87
|
+
recoverable?: boolean;
|
|
88
|
+
upgradeUrl?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
const INTERRUPTED_TOOL_RESULT =
|
|
84
92
|
"Interrupted before this tool returned a result.";
|
|
93
|
+
const INTERRUPTED_ACTIVITY_RESULT = "Stopped before this action started.";
|
|
85
94
|
|
|
86
95
|
export function settleInterruptedToolCalls(
|
|
87
96
|
content: ContentPart[],
|
|
88
97
|
result = INTERRUPTED_TOOL_RESULT,
|
|
98
|
+
options?: { includeActivity?: boolean; activityResult?: string },
|
|
89
99
|
): boolean {
|
|
90
100
|
let changed = false;
|
|
91
101
|
for (const part of content) {
|
|
92
102
|
if (
|
|
93
103
|
part.type === "tool-call" &&
|
|
94
104
|
part.result === undefined &&
|
|
95
|
-
part.activity !== true
|
|
105
|
+
(part.activity !== true || options?.includeActivity === true)
|
|
96
106
|
) {
|
|
97
|
-
part.result =
|
|
107
|
+
part.result =
|
|
108
|
+
part.activity === true
|
|
109
|
+
? (options?.activityResult ?? INTERRUPTED_ACTIVITY_RESULT)
|
|
110
|
+
: result;
|
|
98
111
|
changed = true;
|
|
99
112
|
}
|
|
100
113
|
}
|
|
@@ -105,17 +118,20 @@ export class AgentAutoContinueSignal extends Error {
|
|
|
105
118
|
readonly reason: AgentAutoContinueReason;
|
|
106
119
|
readonly maxIterations?: number;
|
|
107
120
|
readonly activityTrail: AgentActivityTrailEntry[];
|
|
121
|
+
readonly errorInfo?: AgentAutoContinueErrorInfo;
|
|
108
122
|
|
|
109
123
|
constructor(options: {
|
|
110
124
|
reason: AgentAutoContinueReason;
|
|
111
125
|
maxIterations?: number;
|
|
112
126
|
activityTrail?: AgentActivityTrailEntry[];
|
|
127
|
+
errorInfo?: AgentAutoContinueErrorInfo;
|
|
113
128
|
}) {
|
|
114
129
|
super(`Agent run needs automatic continuation: ${options.reason}`);
|
|
115
130
|
this.name = "AgentAutoContinueSignal";
|
|
116
131
|
this.reason = options.reason;
|
|
117
132
|
this.maxIterations = options.maxIterations;
|
|
118
133
|
this.activityTrail = options.activityTrail ?? [];
|
|
134
|
+
this.errorInfo = options.errorInfo;
|
|
119
135
|
}
|
|
120
136
|
}
|
|
121
137
|
|
|
@@ -342,6 +358,7 @@ export function processEvent(
|
|
|
342
358
|
autoContinue?: {
|
|
343
359
|
reason: AgentAutoContinueReason;
|
|
344
360
|
maxIterations?: number;
|
|
361
|
+
errorInfo?: AgentAutoContinueErrorInfo;
|
|
345
362
|
};
|
|
346
363
|
} {
|
|
347
364
|
if (ev.type === "clear") {
|
|
@@ -598,7 +615,7 @@ export function processEvent(
|
|
|
598
615
|
}),
|
|
599
616
|
);
|
|
600
617
|
}
|
|
601
|
-
settleInterruptedToolCalls(content);
|
|
618
|
+
settleInterruptedToolCalls(content, undefined, { includeActivity: true });
|
|
602
619
|
content.push({
|
|
603
620
|
type: "text",
|
|
604
621
|
text: formatChatErrorText(errMsg, undefined, errorCode),
|
|
@@ -665,6 +682,7 @@ export function processEvent(
|
|
|
665
682
|
(ev.errorCode === "run_timeout" && ev.recoverable) ||
|
|
666
683
|
isAutoRecoverableError(ev, errMsg)
|
|
667
684
|
) {
|
|
685
|
+
const normalized = normalizeChatError(errMsg, ev.errorCode);
|
|
668
686
|
return {
|
|
669
687
|
action: "auto_continue",
|
|
670
688
|
autoContinue: {
|
|
@@ -676,6 +694,15 @@ export function processEvent(
|
|
|
676
694
|
errMsg.toLowerCase().includes("timeout")
|
|
677
695
|
? "run_timeout"
|
|
678
696
|
: "stream_ended",
|
|
697
|
+
errorInfo: {
|
|
698
|
+
message: normalized.message,
|
|
699
|
+
...(ev.details || normalized.details
|
|
700
|
+
? { details: ev.details ?? normalized.details }
|
|
701
|
+
: {}),
|
|
702
|
+
...(ev.errorCode ? { errorCode: ev.errorCode } : {}),
|
|
703
|
+
recoverable: ev.recoverable ?? true,
|
|
704
|
+
...(ev.upgradeUrl ? { upgradeUrl: ev.upgradeUrl } : {}),
|
|
705
|
+
},
|
|
679
706
|
},
|
|
680
707
|
};
|
|
681
708
|
}
|
|
@@ -700,7 +727,7 @@ export function processEvent(
|
|
|
700
727
|
}),
|
|
701
728
|
);
|
|
702
729
|
}
|
|
703
|
-
settleInterruptedToolCalls(content);
|
|
730
|
+
settleInterruptedToolCalls(content, undefined, { includeActivity: true });
|
|
704
731
|
content.push({
|
|
705
732
|
type: "text",
|
|
706
733
|
text: formatChatErrorText(errMsg, ev.upgradeUrl, ev.errorCode),
|
|
@@ -35,7 +35,10 @@ export function defineEval(spec: Eval): Eval {
|
|
|
35
35
|
if (!spec.input || typeof spec.input.prompt !== "string") {
|
|
36
36
|
throw new Error(`defineEval("${spec.name}"): \`input.prompt\` is required`);
|
|
37
37
|
}
|
|
38
|
-
if (
|
|
38
|
+
if (
|
|
39
|
+
(!Array.isArray(spec.scorers) || spec.scorers.length === 0) &&
|
|
40
|
+
!spec.skipReason
|
|
41
|
+
) {
|
|
39
42
|
throw new Error(
|
|
40
43
|
`defineEval("${spec.name}"): at least one scorer is required`,
|
|
41
44
|
);
|
|
@@ -27,13 +27,19 @@ export function formatReport(report: EvalRunReport): string {
|
|
|
27
27
|
lines.push(" ─────");
|
|
28
28
|
|
|
29
29
|
for (const row of report.results) {
|
|
30
|
-
const mark = row.passed ? "✓" : "✗";
|
|
30
|
+
const mark = row.status === "skipped" ? "-" : row.passed ? "✓" : "✗";
|
|
31
31
|
lines.push("");
|
|
32
|
-
|
|
33
|
-
` ${mark} ${row.eval} (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
if (row.status === "skipped") {
|
|
33
|
+
lines.push(` ${mark} ${row.eval} (skipped)`);
|
|
34
|
+
lines.push(` reason: ${row.skipReason ?? "No reason provided"}`);
|
|
35
|
+
continue;
|
|
36
|
+
} else {
|
|
37
|
+
lines.push(
|
|
38
|
+
` ${mark} ${row.eval} (avg ${pct(row.avgScore)}, threshold ${pct(
|
|
39
|
+
row.threshold,
|
|
40
|
+
)})`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
37
43
|
if (row.error) {
|
|
38
44
|
lines.push(` ⚠ run error: ${row.error}`);
|
|
39
45
|
}
|
|
@@ -50,9 +56,18 @@ export function formatReport(report: EvalRunReport): string {
|
|
|
50
56
|
|
|
51
57
|
lines.push("");
|
|
52
58
|
lines.push(" ─────");
|
|
53
|
-
const
|
|
59
|
+
const skipped = report.skipped ?? 0;
|
|
60
|
+
const executedTotal = report.total - skipped;
|
|
61
|
+
const executedPassed = report.passed - skipped;
|
|
62
|
+
const verdict =
|
|
63
|
+
executedTotal === 0 && skipped > 0
|
|
64
|
+
? "SKIPPED"
|
|
65
|
+
: report.failed === 0
|
|
66
|
+
? "PASS"
|
|
67
|
+
: "FAIL";
|
|
54
68
|
lines.push(
|
|
55
|
-
` ${verdict}: ${
|
|
69
|
+
` ${verdict}: ${executedPassed}/${executedTotal} evals passed` +
|
|
70
|
+
(skipped > 0 ? `, ${skipped} skipped` : "") +
|
|
56
71
|
(report.failed > 0 ? `, ${report.failed} below threshold` : ""),
|
|
57
72
|
);
|
|
58
73
|
lines.push("");
|