@agent-native/core 0.84.22 → 0.84.24
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 +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/model-config.ts +1 -1
- package/corpus/core/src/cli/skills.ts +17 -3
- package/corpus/core/src/client/AssistantChat.tsx +45 -12
- package/corpus/core/src/client/active-run-state.ts +22 -0
- package/corpus/core/src/client/chat/message-components.tsx +2 -1
- package/corpus/core/src/client/chat/tool-call-display.tsx +18 -2
- package/corpus/core/src/client/sse-event-processor.ts +157 -40
- package/corpus/core/src/notifications/index.ts +1 -0
- package/corpus/core/src/notifications/registry.ts +13 -1
- package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +15 -13
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +18 -1
- package/corpus/templates/analytics/server/db/schema.ts +1 -1
- package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +4 -1
- package/corpus/templates/analytics/server/lib/analytics-alerts.ts +76 -7
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +30 -1
- package/corpus/templates/clips/actions/finalize-recording.ts +84 -4
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +256 -0
- package/corpus/templates/clips/actions/reprocess-recording.ts +160 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +2 -2
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +31 -3
- package/corpus/templates/clips/changelog/2026-07-01-clip-embeds-now-render-as-a-clean-video-only-player-without-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-shared-clips-now-start-playing-and-scrub-instantly-instead-o.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +2 -2
- package/corpus/templates/clips/chrome-extension/src/github-preview.ts +41 -31
- package/corpus/templates/clips/server/lib/video-remux.ts +222 -0
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +75 -19
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +11 -1
- package/corpus/templates/design/changelog/2026-07-01-design-chat-no-longer-shows-an-empty-state-selection-chip-in-the-composer.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/videos/.agents/skills/frontend-design/SKILL.md +2 -0
- package/dist/agent/model-config.js +1 -1
- package/dist/agent/model-config.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +17 -3
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +37 -15
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/active-run-state.d.ts +2 -0
- package/dist/client/active-run-state.d.ts.map +1 -1
- package/dist/client/active-run-state.js +17 -0
- package/dist/client/active-run-state.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +3 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +3 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +8 -8
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +1 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +136 -35
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/registry.d.ts +5 -0
- package/dist/notifications/registry.d.ts.map +1 -1
- package/dist/notifications/registry.js +4 -1
- package/dist/notifications/registry.js.map +1 -1
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
- package/package.json +1 -1
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
|
@@ -27,6 +27,7 @@ export type ContentPart =
|
|
|
27
27
|
mcpApp?: AgentMcpAppPayload;
|
|
28
28
|
chatUI?: ActionChatUIConfig;
|
|
29
29
|
activity?: boolean;
|
|
30
|
+
repeatCount?: number;
|
|
30
31
|
/**
|
|
31
32
|
* Set when the server emitted an `approval_required` event for this tool
|
|
32
33
|
* call (opt-in `needsApproval` actions). The action did NOT run; the UI
|
|
@@ -184,6 +185,10 @@ function isPreparingActionActivity(ev: SSEEvent): boolean {
|
|
|
184
185
|
return label.startsWith("preparing ") && label.includes(" action");
|
|
185
186
|
}
|
|
186
187
|
|
|
188
|
+
function isProgressEvent(ev: SSEEvent): boolean {
|
|
189
|
+
return ev.type !== "stream_keepalive";
|
|
190
|
+
}
|
|
191
|
+
|
|
187
192
|
function baseActivityLabel(ev: SSEEvent, tool?: string): string {
|
|
188
193
|
return humanizeToolLabelText(ev.label ?? "Working", tool);
|
|
189
194
|
}
|
|
@@ -460,6 +465,81 @@ function pendingToolNames(content: ContentPart[]): {
|
|
|
460
465
|
return { activity: [...activity], running: [...running] };
|
|
461
466
|
}
|
|
462
467
|
|
|
468
|
+
function contentSnapshot(content: ContentPart[]): ContentPart[] {
|
|
469
|
+
return content.map((part) => {
|
|
470
|
+
if (part.type === "text") return { ...part };
|
|
471
|
+
return {
|
|
472
|
+
...part,
|
|
473
|
+
args: { ...part.args },
|
|
474
|
+
...(part.mcpApp ? { mcpApp: { ...part.mcpApp } } : {}),
|
|
475
|
+
...(part.chatUI ? { chatUI: { ...part.chatUI } } : {}),
|
|
476
|
+
...(part.approval ? { approval: { ...part.approval } } : {}),
|
|
477
|
+
...(part.structuredMeta
|
|
478
|
+
? { structuredMeta: { ...part.structuredMeta } }
|
|
479
|
+
: {}),
|
|
480
|
+
};
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function repeatSignatureValue(value: unknown): string {
|
|
485
|
+
try {
|
|
486
|
+
return JSON.stringify(value);
|
|
487
|
+
} catch {
|
|
488
|
+
return String(value ?? "");
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function completedToolRepeatSignature(
|
|
493
|
+
part: Extract<ContentPart, { type: "tool-call" }>,
|
|
494
|
+
): string | null {
|
|
495
|
+
if (
|
|
496
|
+
part.result === undefined ||
|
|
497
|
+
part.activity === true ||
|
|
498
|
+
part.approval ||
|
|
499
|
+
part.mcpApp ||
|
|
500
|
+
part.chatUI ||
|
|
501
|
+
part.structuredMeta
|
|
502
|
+
) {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
return [
|
|
506
|
+
part.toolName,
|
|
507
|
+
part.argsText,
|
|
508
|
+
repeatSignatureValue(part.args),
|
|
509
|
+
part.result,
|
|
510
|
+
part.isError === true ? "error" : "",
|
|
511
|
+
part.completedSideEffect === true ? "side-effect" : "",
|
|
512
|
+
].join("\u0000");
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function coalesceCompletedToolRepeat(
|
|
516
|
+
content: ContentPart[],
|
|
517
|
+
completedIndex: number,
|
|
518
|
+
): void {
|
|
519
|
+
const current = content[completedIndex];
|
|
520
|
+
const previous = content[completedIndex - 1];
|
|
521
|
+
if (
|
|
522
|
+
!current ||
|
|
523
|
+
!previous ||
|
|
524
|
+
current.type !== "tool-call" ||
|
|
525
|
+
previous.type !== "tool-call"
|
|
526
|
+
) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const currentSignature = completedToolRepeatSignature(current);
|
|
531
|
+
if (
|
|
532
|
+
!currentSignature ||
|
|
533
|
+
currentSignature !== completedToolRepeatSignature(previous)
|
|
534
|
+
) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
previous.repeatCount =
|
|
539
|
+
(previous.repeatCount ?? 1) + (current.repeatCount ?? 1);
|
|
540
|
+
content.splice(completedIndex, 1);
|
|
541
|
+
}
|
|
542
|
+
|
|
463
543
|
function formatToolNames(tools: string[]): string {
|
|
464
544
|
const names = tools.map(humanizeToolName);
|
|
465
545
|
if (names.length === 0) return "the promised action";
|
|
@@ -578,7 +658,7 @@ export function processEvent(
|
|
|
578
658
|
}
|
|
579
659
|
return {
|
|
580
660
|
action: "yield",
|
|
581
|
-
result: { content:
|
|
661
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
582
662
|
};
|
|
583
663
|
}
|
|
584
664
|
|
|
@@ -615,7 +695,7 @@ export function processEvent(
|
|
|
615
695
|
}
|
|
616
696
|
return {
|
|
617
697
|
action: "yield",
|
|
618
|
-
result: { content:
|
|
698
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
619
699
|
};
|
|
620
700
|
}
|
|
621
701
|
|
|
@@ -672,7 +752,7 @@ export function processEvent(
|
|
|
672
752
|
}
|
|
673
753
|
return {
|
|
674
754
|
action: "yield",
|
|
675
|
-
result: { content:
|
|
755
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
676
756
|
};
|
|
677
757
|
}
|
|
678
758
|
|
|
@@ -694,7 +774,7 @@ export function processEvent(
|
|
|
694
774
|
}
|
|
695
775
|
return {
|
|
696
776
|
action: "yield",
|
|
697
|
-
result: { content:
|
|
777
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
698
778
|
};
|
|
699
779
|
}
|
|
700
780
|
|
|
@@ -730,11 +810,12 @@ export function processEvent(
|
|
|
730
810
|
if (part.activity !== true && part.isError !== true) {
|
|
731
811
|
markCompletedToolAfterAssistantText(state, part.toolName);
|
|
732
812
|
}
|
|
813
|
+
coalesceCompletedToolRepeat(content, doneIdx);
|
|
733
814
|
}
|
|
734
815
|
}
|
|
735
816
|
return {
|
|
736
817
|
action: "yield",
|
|
737
|
-
result: { content:
|
|
818
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
738
819
|
};
|
|
739
820
|
}
|
|
740
821
|
|
|
@@ -764,7 +845,7 @@ export function processEvent(
|
|
|
764
845
|
}
|
|
765
846
|
return {
|
|
766
847
|
action: "yield",
|
|
767
|
-
result: { content:
|
|
848
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
768
849
|
};
|
|
769
850
|
}
|
|
770
851
|
|
|
@@ -784,7 +865,7 @@ export function processEvent(
|
|
|
784
865
|
}
|
|
785
866
|
return {
|
|
786
867
|
action: "yield",
|
|
787
|
-
result: { content:
|
|
868
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
788
869
|
};
|
|
789
870
|
}
|
|
790
871
|
|
|
@@ -826,7 +907,7 @@ export function processEvent(
|
|
|
826
907
|
return {
|
|
827
908
|
action: "missing_api_key",
|
|
828
909
|
result: {
|
|
829
|
-
content:
|
|
910
|
+
content: contentSnapshot(content),
|
|
830
911
|
status: { type: "incomplete" as const, reason: "error" as const },
|
|
831
912
|
metadata: { custom: { runError } },
|
|
832
913
|
} as ChatModelRunResult,
|
|
@@ -938,7 +1019,7 @@ export function processEvent(
|
|
|
938
1019
|
return {
|
|
939
1020
|
action: "error",
|
|
940
1021
|
result: {
|
|
941
|
-
content:
|
|
1022
|
+
content: contentSnapshot(content),
|
|
942
1023
|
status: { type: "incomplete" as const, reason: "error" as const },
|
|
943
1024
|
metadata: { custom: { runError } },
|
|
944
1025
|
} as ChatModelRunResult,
|
|
@@ -974,7 +1055,7 @@ export function processEvent(
|
|
|
974
1055
|
return {
|
|
975
1056
|
action: "error",
|
|
976
1057
|
result: {
|
|
977
|
-
content:
|
|
1058
|
+
content: contentSnapshot(content),
|
|
978
1059
|
status: { type: "incomplete" as const, reason: "error" as const },
|
|
979
1060
|
metadata: { custom: { runError } },
|
|
980
1061
|
} as ChatModelRunResult,
|
|
@@ -993,7 +1074,7 @@ export function processEvent(
|
|
|
993
1074
|
return {
|
|
994
1075
|
action: "done",
|
|
995
1076
|
result: {
|
|
996
|
-
content:
|
|
1077
|
+
content: contentSnapshot(content),
|
|
997
1078
|
status: { type: "complete" as const, reason: "stop" as const },
|
|
998
1079
|
metadata: {
|
|
999
1080
|
custom: {
|
|
@@ -1009,7 +1090,7 @@ export function processEvent(
|
|
|
1009
1090
|
}
|
|
1010
1091
|
return {
|
|
1011
1092
|
action: "done",
|
|
1012
|
-
result: { content:
|
|
1093
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
1013
1094
|
};
|
|
1014
1095
|
}
|
|
1015
1096
|
|
|
@@ -1075,16 +1156,30 @@ export async function* readSSEStream(
|
|
|
1075
1156
|
|
|
1076
1157
|
try {
|
|
1077
1158
|
while (true) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1159
|
+
let readResult: ReadableStreamReadResult<Uint8Array>;
|
|
1160
|
+
try {
|
|
1161
|
+
readResult = await readChunkWithProgressTimeout(
|
|
1162
|
+
reader,
|
|
1163
|
+
lastMeaningfulEventAt,
|
|
1164
|
+
);
|
|
1165
|
+
} catch (err) {
|
|
1166
|
+
if (err instanceof AgentAutoContinueSignal) {
|
|
1167
|
+
throw new AgentAutoContinueSignal({
|
|
1168
|
+
reason: err.reason,
|
|
1169
|
+
maxIterations: err.maxIterations,
|
|
1170
|
+
activityTrail: [...activityTrail],
|
|
1171
|
+
errorInfo: err.errorInfo,
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
throw err;
|
|
1175
|
+
}
|
|
1176
|
+
const { done, value } = readResult;
|
|
1082
1177
|
if (done) break;
|
|
1083
1178
|
|
|
1084
1179
|
buf += decoder.decode(value, { stream: true });
|
|
1085
1180
|
const lines = buf.split("\n");
|
|
1086
1181
|
buf = lines.pop() ?? "";
|
|
1087
|
-
let
|
|
1182
|
+
let sawProgressEvent = false;
|
|
1088
1183
|
|
|
1089
1184
|
for (const line of lines) {
|
|
1090
1185
|
if (!line.startsWith("data: ")) continue;
|
|
@@ -1097,9 +1192,11 @@ export async function* readSSEStream(
|
|
|
1097
1192
|
} catch {
|
|
1098
1193
|
continue;
|
|
1099
1194
|
}
|
|
1100
|
-
sawDataEvent = true;
|
|
1101
1195
|
const now = Date.now();
|
|
1102
|
-
|
|
1196
|
+
if (isProgressEvent(ev)) {
|
|
1197
|
+
sawProgressEvent = true;
|
|
1198
|
+
lastMeaningfulEventAt = now;
|
|
1199
|
+
}
|
|
1103
1200
|
updatePreparingActionState(preparingActionState, ev, now);
|
|
1104
1201
|
|
|
1105
1202
|
// Track sequence number for reconnection
|
|
@@ -1162,10 +1259,13 @@ export async function* readSSEStream(
|
|
|
1162
1259
|
}
|
|
1163
1260
|
|
|
1164
1261
|
if (
|
|
1165
|
-
!
|
|
1262
|
+
!sawProgressEvent &&
|
|
1166
1263
|
Date.now() - lastMeaningfulEventAt >= SSE_NO_PROGRESS_TIMEOUT_MS
|
|
1167
1264
|
) {
|
|
1168
|
-
throw new AgentAutoContinueSignal({
|
|
1265
|
+
throw new AgentAutoContinueSignal({
|
|
1266
|
+
reason: "no_progress",
|
|
1267
|
+
activityTrail: [...activityTrail],
|
|
1268
|
+
});
|
|
1169
1269
|
}
|
|
1170
1270
|
}
|
|
1171
1271
|
} finally {
|
|
@@ -1218,18 +1318,31 @@ export async function readSSEStreamRaw(
|
|
|
1218
1318
|
|
|
1219
1319
|
try {
|
|
1220
1320
|
while (true) {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1321
|
+
let readResult: ReadableStreamReadResult<Uint8Array>;
|
|
1322
|
+
try {
|
|
1323
|
+
readResult = await readChunkWithProgressTimeout(
|
|
1324
|
+
reader,
|
|
1325
|
+
lastMeaningfulEventAt,
|
|
1326
|
+
);
|
|
1327
|
+
} catch (err) {
|
|
1328
|
+
if (err instanceof AgentAutoContinueSignal) {
|
|
1329
|
+
throw new AgentAutoContinueSignal({
|
|
1330
|
+
reason: err.reason,
|
|
1331
|
+
maxIterations: err.maxIterations,
|
|
1332
|
+
activityTrail: [...activityTrail],
|
|
1333
|
+
errorInfo: err.errorInfo,
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
throw err;
|
|
1337
|
+
}
|
|
1338
|
+
const { done, value } = readResult;
|
|
1225
1339
|
if (done) break;
|
|
1226
1340
|
|
|
1227
1341
|
buf += decoder.decode(value, { stream: true });
|
|
1228
1342
|
const lines = buf.split("\n");
|
|
1229
1343
|
buf = lines.pop() ?? "";
|
|
1230
1344
|
|
|
1231
|
-
let
|
|
1232
|
-
let sawDataEvent = false;
|
|
1345
|
+
let sawProgressEvent = false;
|
|
1233
1346
|
for (const line of lines) {
|
|
1234
1347
|
if (!line.startsWith("data: ")) continue;
|
|
1235
1348
|
const raw = line.slice(6).trim();
|
|
@@ -1241,9 +1354,11 @@ export async function readSSEStreamRaw(
|
|
|
1241
1354
|
} catch {
|
|
1242
1355
|
continue;
|
|
1243
1356
|
}
|
|
1244
|
-
sawDataEvent = true;
|
|
1245
1357
|
const now = Date.now();
|
|
1246
|
-
|
|
1358
|
+
if (isProgressEvent(ev)) {
|
|
1359
|
+
sawProgressEvent = true;
|
|
1360
|
+
lastMeaningfulEventAt = now;
|
|
1361
|
+
}
|
|
1247
1362
|
updatePreparingActionState(preparingActionState, ev, now);
|
|
1248
1363
|
|
|
1249
1364
|
if (ev.seq !== undefined && onSeq) {
|
|
@@ -1287,10 +1402,12 @@ export async function readSSEStreamRaw(
|
|
|
1287
1402
|
action === "error" ||
|
|
1288
1403
|
action === "missing_api_key"
|
|
1289
1404
|
) {
|
|
1290
|
-
|
|
1405
|
+
onUpdate(contentSnapshot(content));
|
|
1406
|
+
emittedLatestContent = true;
|
|
1291
1407
|
}
|
|
1292
1408
|
if (action === "auto_continue") {
|
|
1293
|
-
onUpdate(
|
|
1409
|
+
onUpdate(contentSnapshot(content));
|
|
1410
|
+
emittedLatestContent = true;
|
|
1294
1411
|
throw new AgentAutoContinueSignal(
|
|
1295
1412
|
autoContinue
|
|
1296
1413
|
? { ...autoContinue, activityTrail: [...activityTrail] }
|
|
@@ -1298,7 +1415,7 @@ export async function readSSEStreamRaw(
|
|
|
1298
1415
|
);
|
|
1299
1416
|
}
|
|
1300
1417
|
if (hasStalledPreparingAction(preparingActionState, Date.now())) {
|
|
1301
|
-
onUpdate(
|
|
1418
|
+
onUpdate(contentSnapshot(content));
|
|
1302
1419
|
throw new AgentAutoContinueSignal({
|
|
1303
1420
|
reason: "no_progress",
|
|
1304
1421
|
activityTrail: [...activityTrail],
|
|
@@ -1309,20 +1426,18 @@ export async function readSSEStreamRaw(
|
|
|
1309
1426
|
action === "error" ||
|
|
1310
1427
|
action === "missing_api_key"
|
|
1311
1428
|
) {
|
|
1312
|
-
onUpdate([...content]);
|
|
1313
1429
|
return;
|
|
1314
1430
|
}
|
|
1315
1431
|
}
|
|
1316
1432
|
|
|
1317
|
-
if (updated) {
|
|
1318
|
-
onUpdate([...content]);
|
|
1319
|
-
emittedLatestContent = true;
|
|
1320
|
-
}
|
|
1321
1433
|
if (
|
|
1322
|
-
!
|
|
1434
|
+
!sawProgressEvent &&
|
|
1323
1435
|
Date.now() - lastMeaningfulEventAt >= SSE_NO_PROGRESS_TIMEOUT_MS
|
|
1324
1436
|
) {
|
|
1325
|
-
throw new AgentAutoContinueSignal({
|
|
1437
|
+
throw new AgentAutoContinueSignal({
|
|
1438
|
+
reason: "no_progress",
|
|
1439
|
+
activityTrail: [...activityTrail],
|
|
1440
|
+
});
|
|
1326
1441
|
}
|
|
1327
1442
|
}
|
|
1328
1443
|
} finally {
|
|
@@ -1332,6 +1447,8 @@ export async function readSSEStreamRaw(
|
|
|
1332
1447
|
// See readSSEStream: cancellation may race lock release in browsers.
|
|
1333
1448
|
}
|
|
1334
1449
|
}
|
|
1335
|
-
if (content.length > 0 && !emittedLatestContent)
|
|
1450
|
+
if (content.length > 0 && !emittedLatestContent) {
|
|
1451
|
+
onUpdate(contentSnapshot(content));
|
|
1452
|
+
}
|
|
1336
1453
|
throw new AgentAutoContinueSignal({ reason: "stream_ended" });
|
|
1337
1454
|
}
|
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
type Notification,
|
|
14
14
|
} from "./types.js";
|
|
15
15
|
|
|
16
|
+
export interface NotificationDeliveryResult {
|
|
17
|
+
notification?: Notification;
|
|
18
|
+
deliveredChannels: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
registerEvent({
|
|
17
22
|
name: "notification.sent",
|
|
18
23
|
description:
|
|
@@ -84,6 +89,13 @@ export async function notify(
|
|
|
84
89
|
input: NotificationInput,
|
|
85
90
|
meta: NotificationMeta,
|
|
86
91
|
): Promise<Notification | undefined> {
|
|
92
|
+
return (await notifyWithDelivery(input, meta)).notification;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function notifyWithDelivery(
|
|
96
|
+
input: NotificationInput,
|
|
97
|
+
meta: NotificationMeta,
|
|
98
|
+
): Promise<NotificationDeliveryResult> {
|
|
87
99
|
if (!meta?.owner) {
|
|
88
100
|
throw new Error("notify: meta.owner is required");
|
|
89
101
|
}
|
|
@@ -166,7 +178,7 @@ export async function notify(
|
|
|
166
178
|
}
|
|
167
179
|
}
|
|
168
180
|
|
|
169
|
-
return stored;
|
|
181
|
+
return { notification: stored, deliveredChannels: delivered };
|
|
170
182
|
}
|
|
171
183
|
|
|
172
184
|
function selectChannels(allowlist?: string[]): NotificationChannel[] {
|
|
@@ -53,6 +53,8 @@ Default to Apple/Linear-level restraint: make the primary workflow obvious, then
|
|
|
53
53
|
- **Visual assets**: Websites, games, and object-focused pages need real or generated media when images help users understand the subject.
|
|
54
54
|
- **Responsive fit**: Text must not overflow buttons, cards, tabs, sidebars, or fixed-format tools. Use stable dimensions for boards, grids, toolbars, and counters.
|
|
55
55
|
|
|
56
|
+
**Beat convergence, not just defaults.** You sample toward the "on-distribution" center, so naming what to avoid is not enough: every "don't" needs a "do", or you converge on the next safe option (ban Inter and you reach for Roboto; ban purple gradients and you reach for Space Grotesk + a teal accent on every screen). Commit to one named direction, pair any reference with the reason it fits ("Linear: the quiet confidence of its spacing" — a bare "Linear" collapses back to the average), and match implementation effort to the vision: maximalist wants elaborate motion and effects, minimal wants restraint and precise spacing. When building on an existing app, inspect its tokens/type/components first and treat any drift back to a default as a missing token to pin, not something to re-prompt.
|
|
57
|
+
|
|
56
58
|
## Agent-Native UI Rules
|
|
57
59
|
|
|
58
60
|
- Agent-native apps use React, Vite, Tailwind CSS, shadcn/ui, and `@tabler/icons-react`.
|
|
@@ -53,6 +53,8 @@ Default to Apple/Linear-level restraint: make the primary workflow obvious, then
|
|
|
53
53
|
- **Visual assets**: Websites, games, and object-focused pages need real or generated media when images help users understand the subject.
|
|
54
54
|
- **Responsive fit**: Text must not overflow buttons, cards, tabs, sidebars, or fixed-format tools. Use stable dimensions for boards, grids, toolbars, and counters.
|
|
55
55
|
|
|
56
|
+
**Beat convergence, not just defaults.** You sample toward the "on-distribution" center, so naming what to avoid is not enough: every "don't" needs a "do", or you converge on the next safe option (ban Inter and you reach for Roboto; ban purple gradients and you reach for Space Grotesk + a teal accent on every screen). Commit to one named direction, pair any reference with the reason it fits ("Linear: the quiet confidence of its spacing" — a bare "Linear" collapses back to the average), and match implementation effort to the vision: maximalist wants elaborate motion and effects, minimal wants restraint and precise spacing. When building on an existing app, inspect its tokens/type/components first and treat any drift back to a default as a missing token to pin, not something to re-prompt.
|
|
57
|
+
|
|
56
58
|
## Agent-Native UI Rules
|
|
57
59
|
|
|
58
60
|
- Agent-native apps use React, Vite, Tailwind CSS, shadcn/ui, and `@tabler/icons-react`.
|
|
@@ -53,6 +53,8 @@ Default to Apple/Linear-level restraint: make the primary workflow obvious, then
|
|
|
53
53
|
- **Visual assets**: Websites, games, and object-focused pages need real or generated media when images help users understand the subject.
|
|
54
54
|
- **Responsive fit**: Text must not overflow buttons, cards, tabs, sidebars, or fixed-format tools. Use stable dimensions for boards, grids, toolbars, and counters.
|
|
55
55
|
|
|
56
|
+
**Beat convergence, not just defaults.** You sample toward the "on-distribution" center, so naming what to avoid is not enough: every "don't" needs a "do", or you converge on the next safe option (ban Inter and you reach for Roboto; ban purple gradients and you reach for Space Grotesk + a teal accent on every screen). Commit to one named direction, pair any reference with the reason it fits ("Linear: the quiet confidence of its spacing" — a bare "Linear" collapses back to the average), and match implementation effort to the vision: maximalist wants elaborate motion and effects, minimal wants restraint and precise spacing. When building on an existing app, inspect its tokens/type/components first and treat any drift back to a default as a missing token to pin, not something to re-prompt.
|
|
57
|
+
|
|
56
58
|
## Agent-Native UI Rules
|
|
57
59
|
|
|
58
60
|
- Agent-native apps use React, Vite, Tailwind CSS, shadcn/ui, and `@tabler/icons-react`.
|
|
@@ -466,6 +466,21 @@ export function SqlChartCard({
|
|
|
466
466
|
<TooltipContent>{t("sqlDashboard.refreshing")}</TooltipContent>
|
|
467
467
|
</Tooltip>
|
|
468
468
|
) : null}
|
|
469
|
+
{editable && onSaveSql ? (
|
|
470
|
+
<ViewSqlPopover
|
|
471
|
+
panel={panel}
|
|
472
|
+
resolvedSql={resolvedSql}
|
|
473
|
+
onSaveSql={onSaveSql}
|
|
474
|
+
>
|
|
475
|
+
<button
|
|
476
|
+
className="p-1 rounded text-muted-foreground hover:text-foreground"
|
|
477
|
+
aria-label={t("sqlDashboard.viewSql")}
|
|
478
|
+
title={t("sqlDashboard.viewSql")}
|
|
479
|
+
>
|
|
480
|
+
<IconCode className="h-3.5 w-3.5" />
|
|
481
|
+
</button>
|
|
482
|
+
</ViewSqlPopover>
|
|
483
|
+
) : null}
|
|
469
484
|
{showPanelMenu ? (
|
|
470
485
|
<DropdownMenu>
|
|
471
486
|
<Tooltip>
|
|
@@ -503,19 +518,6 @@ export function SqlChartCard({
|
|
|
503
518
|
{editable && panel.chartType === "table" ? (
|
|
504
519
|
<DropdownMenuSeparator />
|
|
505
520
|
) : null}
|
|
506
|
-
{editable && onSaveSql ? (
|
|
507
|
-
<ViewSqlPopover
|
|
508
|
-
panel={panel}
|
|
509
|
-
resolvedSql={resolvedSql}
|
|
510
|
-
onSaveSql={onSaveSql}
|
|
511
|
-
>
|
|
512
|
-
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
|
|
513
|
-
<IconCode className="h-4 w-4 mr-2" />
|
|
514
|
-
{t("sqlDashboard.viewSql")}
|
|
515
|
-
</DropdownMenuItem>
|
|
516
|
-
</ViewSqlPopover>
|
|
517
|
-
) : null}
|
|
518
|
-
{editable ? <DropdownMenuSeparator /> : null}
|
|
519
521
|
{editable && onEdit && (
|
|
520
522
|
<DropdownMenuItem onSelect={() => onEdit()}>
|
|
521
523
|
<IconPencil className="h-4 w-4 mr-2" />
|
|
@@ -135,7 +135,24 @@ function siteOrigin(request) {
|
|
|
135
135
|
return url.origin;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
async function readScheduledInvocation(request) {
|
|
139
|
+
if (request.method !== "POST") return null;
|
|
140
|
+
let body;
|
|
141
|
+
try {
|
|
142
|
+
body = await request.json();
|
|
143
|
+
} catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const nextRun = typeof body?.next_run === "string" ? body.next_run : "";
|
|
147
|
+
return nextRun && Number.isFinite(Date.parse(nextRun))
|
|
148
|
+
? { nextRun }
|
|
149
|
+
: null;
|
|
150
|
+
}
|
|
151
|
+
|
|
138
152
|
export default async function handler(request) {
|
|
153
|
+
const scheduled = await readScheduledInvocation(request);
|
|
154
|
+
if (!scheduled) return new Response("Not Found", { status: 404 });
|
|
155
|
+
|
|
139
156
|
const url = new URL(WORKER_PATH, siteOrigin(request));
|
|
140
157
|
const response = await fetch(url.toString(), {
|
|
141
158
|
method: "POST",
|
|
@@ -143,7 +160,7 @@ export default async function handler(request) {
|
|
|
143
160
|
"content-type": "application/json",
|
|
144
161
|
"x-agent-native-analytics-alert-cron": CRON_TOKEN,
|
|
145
162
|
},
|
|
146
|
-
body: JSON.stringify({ scheduled: true }),
|
|
163
|
+
body: JSON.stringify({ scheduled: true, next_run: scheduled.nextRun }),
|
|
147
164
|
});
|
|
148
165
|
|
|
149
166
|
if (!response.ok && response.status !== 202) {
|
|
@@ -207,7 +207,7 @@ export const analyticsAlertRules = table("analytics_alert_rules", {
|
|
|
207
207
|
lastEvaluatedAt: text("last_evaluated_at"),
|
|
208
208
|
lastTriggeredAt: text("last_triggered_at"),
|
|
209
209
|
lastStatus: text("last_status", {
|
|
210
|
-
enum: ["ok", "triggered", "cooldown", "error"],
|
|
210
|
+
enum: ["ok", "triggered", "cooldown", "error", "running"],
|
|
211
211
|
}),
|
|
212
212
|
lastError: text("last_error"),
|
|
213
213
|
createdAt: text("created_at").notNull().default(now()),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
claimAnalyticsAlertRuleEvaluation,
|
|
2
3
|
evaluateAndNotifyAnalyticsAlertRule,
|
|
3
4
|
listEnabledAnalyticsAlertRules,
|
|
4
5
|
markAnalyticsAlertRuleError,
|
|
@@ -50,8 +51,10 @@ export async function runAnalyticsAlertsOnce(
|
|
|
50
51
|
remaining = rules.length >= sweepLimit ? 1 : 0;
|
|
51
52
|
|
|
52
53
|
for (const rule of rules) {
|
|
53
|
-
processed++;
|
|
54
54
|
try {
|
|
55
|
+
const claimed = await claimAnalyticsAlertRuleEvaluation(rule);
|
|
56
|
+
if (!claimed) continue;
|
|
57
|
+
processed++;
|
|
55
58
|
const result = await evaluateAndNotifyAnalyticsAlertRule(rule);
|
|
56
59
|
if (result.status === "triggered") triggered++;
|
|
57
60
|
} catch (err) {
|