@agent-native/core 0.76.0 → 0.76.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.
Files changed (38) hide show
  1. package/corpus/core/CHANGELOG.md +41 -0
  2. package/corpus/core/package.json +1 -1
  3. package/corpus/core/src/agent/durable-background.ts +38 -2
  4. package/corpus/core/src/agent/production-agent.ts +54 -0
  5. package/corpus/core/src/agent/run-manager.ts +27 -0
  6. package/corpus/core/src/agent/run-store.ts +185 -4
  7. package/corpus/core/src/server/agent-chat-plugin.ts +71 -0
  8. package/corpus/core/src/server/auth.ts +13 -0
  9. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +12 -12
  10. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +23 -23
  11. package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +2 -2
  12. package/corpus/templates/brain/app/routes/knowledge.tsx +6 -6
  13. package/corpus/templates/brain/app/routes/ops.tsx +4 -4
  14. package/corpus/templates/brain/app/routes/search.tsx +4 -4
  15. package/corpus/templates/brain/app/routes/settings.tsx +3 -3
  16. package/corpus/templates/brain/app/routes/sources.tsx +3 -3
  17. package/dist/agent/durable-background.d.ts +15 -0
  18. package/dist/agent/durable-background.d.ts.map +1 -1
  19. package/dist/agent/durable-background.js +28 -2
  20. package/dist/agent/durable-background.js.map +1 -1
  21. package/dist/agent/production-agent.d.ts.map +1 -1
  22. package/dist/agent/production-agent.js +34 -1
  23. package/dist/agent/production-agent.js.map +1 -1
  24. package/dist/agent/run-manager.d.ts +8 -0
  25. package/dist/agent/run-manager.d.ts.map +1 -1
  26. package/dist/agent/run-manager.js +18 -1
  27. package/dist/agent/run-manager.js.map +1 -1
  28. package/dist/agent/run-store.d.ts +99 -0
  29. package/dist/agent/run-store.d.ts.map +1 -1
  30. package/dist/agent/run-store.js +155 -4
  31. package/dist/agent/run-store.js.map +1 -1
  32. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  33. package/dist/server/agent-chat-plugin.js +58 -1
  34. package/dist/server/agent-chat-plugin.js.map +1 -1
  35. package/dist/server/auth.d.ts.map +1 -1
  36. package/dist/server/auth.js +12 -0
  37. package/dist/server/auth.js.map +1 -1
  38. package/package.json +1 -1
@@ -1627,6 +1627,19 @@ function createAuthGuardFn(): (
1627
1627
  return;
1628
1628
  }
1629
1629
 
1630
+ // Durable-background AGENT-CHAT processor. The foreground POST self-dispatches
1631
+ // a long chat turn here (through the Netlify `-background` function, which
1632
+ // rewrites its default url to this path); the route HMAC-verifies the
1633
+ // dispatch (same internal-token scheme as agent-teams above) plus an atomic
1634
+ // SQL claim. The self-dispatch carries ONLY a Bearer HMAC token and NO
1635
+ // session cookie, so without this bypass the blanket 401-for-/_agent-native/*
1636
+ // gate below blocks the worker before `prepareProcessRunRequest` ever runs —
1637
+ // the run is never claimed, its heartbeat never starts, and it times out with
1638
+ // no visible progress. Exact path only (mirrors agent-teams).
1639
+ if (p === "/_agent-native/agent-chat/_process-run") {
1640
+ return;
1641
+ }
1642
+
1630
1643
  // Read-only agent chat share links. The random token is the bearer secret;
1631
1644
  // the route returns a sanitized transcript plus bounded run summaries and
1632
1645
  // exposes no write surface, live event stream, tool payloads, or owner APIs.
@@ -76,7 +76,7 @@ function CommandLoadingGroup({
76
76
  forceMount
77
77
  value={`${heading} loading ${index + 1}`}
78
78
  >
79
- <Skeleton className="mr-2 h-4 w-4 shrink-0 rounded-sm" />
79
+ <Skeleton className="me-2 h-4 w-4 shrink-0 rounded-sm" />
80
80
  <Skeleton
81
81
  className={`h-4 rounded ${
82
82
  loadingRowWidths[index % loadingRowWidths.length]
@@ -303,10 +303,10 @@ export function CommandPalette() {
303
303
  "dashboard",
304
304
  )}
305
305
  >
306
- <IconLayoutDashboard className="mr-2 h-4 w-4 text-muted-foreground" />
306
+ <IconLayoutDashboard className="me-2 h-4 w-4 text-muted-foreground" />
307
307
  <span className="truncate">{d.name}</span>
308
308
  {d.hiddenAt ? (
309
- <span className="ml-2 rounded border border-border px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
309
+ <span className="ms-2 rounded border border-border px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
310
310
  Hidden
311
311
  </span>
312
312
  ) : null}
@@ -331,10 +331,10 @@ export function CommandPalette() {
331
331
  "dashboard",
332
332
  )}
333
333
  >
334
- <IconLayoutDashboard className="mr-2 h-4 w-4 text-muted-foreground" />
334
+ <IconLayoutDashboard className="me-2 h-4 w-4 text-muted-foreground" />
335
335
  <span className="truncate">{d.name}</span>
336
336
  {d.hiddenAt ? (
337
- <span className="ml-2 rounded border border-border px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
337
+ <span className="ms-2 rounded border border-border px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
338
338
  Hidden
339
339
  </span>
340
340
  ) : null}
@@ -362,7 +362,7 @@ export function CommandPalette() {
362
362
  "tool",
363
363
  )}
364
364
  >
365
- <IconTool className="mr-2 h-4 w-4 text-muted-foreground" />
365
+ <IconTool className="me-2 h-4 w-4 text-muted-foreground" />
366
366
  {extension.name}
367
367
  </CommandItem>
368
368
  ))}
@@ -376,7 +376,7 @@ export function CommandPalette() {
376
376
  onSelect={() => go(`/dashboards/${d.id}`)}
377
377
  keywords={commandPaletteKeywords(d.name, "dashboard")}
378
378
  >
379
- <IconFlask className="mr-2 h-4 w-4 text-muted-foreground" />
379
+ <IconFlask className="me-2 h-4 w-4 text-muted-foreground" />
380
380
  {d.name}
381
381
  </CommandItem>
382
382
  ))}
@@ -389,7 +389,7 @@ export function CommandPalette() {
389
389
  onSelect={() => go(t.href)}
390
390
  keywords={commandPaletteKeywords(t.name, "tool")}
391
391
  >
392
- <IconTool className="mr-2 h-4 w-4 text-muted-foreground" />
392
+ <IconTool className="me-2 h-4 w-4 text-muted-foreground" />
393
393
  {t.name}
394
394
  </CommandItem>
395
395
  ))}
@@ -405,9 +405,9 @@ export function CommandPalette() {
405
405
  keywords={["theme", "dark", "light", "mode"]}
406
406
  >
407
407
  {isDark ? (
408
- <IconSun className="mr-2 h-4 w-4 text-muted-foreground" />
408
+ <IconSun className="me-2 h-4 w-4 text-muted-foreground" />
409
409
  ) : (
410
- <IconMoon className="mr-2 h-4 w-4 text-muted-foreground" />
410
+ <IconMoon className="me-2 h-4 w-4 text-muted-foreground" />
411
411
  )}
412
412
  Toggle {isDark ? "light" : "dark"} mode
413
413
  </CommandItem>
@@ -427,7 +427,7 @@ export function CommandPalette() {
427
427
  "changes",
428
428
  )}
429
429
  >
430
- <IconHistory className="mr-2 h-4 w-4 text-muted-foreground" />
430
+ <IconHistory className="me-2 h-4 w-4 text-muted-foreground" />
431
431
  What's new
432
432
  </CommandItem>
433
433
  </CommandGroup>
@@ -448,7 +448,7 @@ export function CommandPalette() {
448
448
  "chart",
449
449
  )}
450
450
  >
451
- <IconChartBar className="mr-2 h-4 w-4 text-muted-foreground" />
451
+ <IconChartBar className="me-2 h-4 w-4 text-muted-foreground" />
452
452
  {c.name}
453
453
  </CommandItem>
454
454
  ))}
@@ -499,7 +499,7 @@ function SortableRow({
499
499
  <div ref={setNodeRef} style={style} className="group/item relative min-w-0">
500
500
  <button
501
501
  type="button"
502
- className="absolute -left-4 top-1/2 z-10 -translate-y-1/2 cursor-grab rounded p-1 text-muted-foreground/30 opacity-0 transition-colors hover:text-muted-foreground/60 group-hover/item:opacity-100 active:cursor-grabbing"
502
+ className="absolute -start-4 top-1/2 z-10 -translate-y-1/2 cursor-grab rounded p-1 text-muted-foreground/30 opacity-0 transition-colors hover:text-muted-foreground/60 group-hover/item:opacity-100 active:cursor-grabbing"
503
503
  aria-label={`Drag ${name}`}
504
504
  {...attributes}
505
505
  {...listeners}
@@ -527,7 +527,7 @@ function SortableRow({
527
527
  setIsRenaming(false);
528
528
  }
529
529
  }}
530
- className="min-w-0 flex-1 bg-transparent px-2 py-1.5 pr-12 text-xs outline-none"
530
+ className="min-w-0 flex-1 bg-transparent px-2 py-1.5 pe-12 text-xs outline-none"
531
531
  />
532
532
  ) : (
533
533
  <Tooltip>
@@ -537,7 +537,7 @@ function SortableRow({
537
537
  onFocus={onPrefetch}
538
538
  onMouseEnter={onPrefetch}
539
539
  onTouchStart={onPrefetch}
540
- className="min-w-0 flex-1 px-2 py-1.5 pr-12 text-xs transition-[padding] md:pr-2 md:group-hover/item:pr-12 md:group-focus-within/item:pr-12"
540
+ className="min-w-0 flex-1 px-2 py-1.5 pe-12 text-xs transition-[padding] md:pe-2 md:group-hover/item:pe-12 md:group-focus-within/item:pe-12"
541
541
  >
542
542
  <span className="block truncate">{name}</span>
543
543
  </Link>
@@ -545,7 +545,7 @@ function SortableRow({
545
545
  <TooltipContent side="right">{name}</TooltipContent>
546
546
  </Tooltip>
547
547
  )}
548
- <div className="pointer-events-none absolute right-1 top-1/2 flex -translate-y-1/2 items-center gap-0.5 opacity-100 transition-opacity md:opacity-0 md:group-hover/item:opacity-100 md:group-focus-within/item:opacity-100">
548
+ <div className="pointer-events-none absolute end-1 top-1/2 flex -translate-y-1/2 items-center gap-0.5 opacity-100 transition-opacity md:opacity-0 md:group-hover/item:opacity-100 md:group-focus-within/item:opacity-100">
549
549
  <Tooltip>
550
550
  <TooltipTrigger asChild>
551
551
  <button
@@ -588,7 +588,7 @@ function SortableRow({
588
588
  setIsRenaming(true);
589
589
  }}
590
590
  >
591
- <IconPencil className="mr-2 h-3.5 w-3.5" />
591
+ <IconPencil className="me-2 h-3.5 w-3.5" />
592
592
  Rename
593
593
  </DropdownMenuItem>
594
594
  {onSetVisibility && visibility !== undefined && (
@@ -601,15 +601,15 @@ function SortableRow({
601
601
  }}
602
602
  >
603
603
  {visibility === "private" ? (
604
- <IconBuilding className="mr-2 h-3.5 w-3.5" />
604
+ <IconBuilding className="me-2 h-3.5 w-3.5" />
605
605
  ) : (
606
- <IconLock className="mr-2 h-3.5 w-3.5" />
606
+ <IconLock className="me-2 h-3.5 w-3.5" />
607
607
  )}
608
608
  {visibility === "private" ? "Share with org" : "Make private"}
609
609
  </DropdownMenuItem>
610
610
  )}
611
611
  <DropdownMenuItem onSelect={copyLink}>
612
- <IconLink className="mr-2 h-3.5 w-3.5" />
612
+ <IconLink className="me-2 h-3.5 w-3.5" />
613
613
  Copy link
614
614
  </DropdownMenuItem>
615
615
  {onUnhide && hidden ? (
@@ -619,7 +619,7 @@ function SortableRow({
619
619
  void runUnhide();
620
620
  }}
621
621
  >
622
- <IconEye className="mr-2 h-3.5 w-3.5" />
622
+ <IconEye className="me-2 h-3.5 w-3.5" />
623
623
  Unhide
624
624
  </DropdownMenuItem>
625
625
  ) : onHide ? (
@@ -629,7 +629,7 @@ function SortableRow({
629
629
  void runHide();
630
630
  }}
631
631
  >
632
- <IconEyeOff className="mr-2 h-3.5 w-3.5" />
632
+ <IconEyeOff className="me-2 h-3.5 w-3.5" />
633
633
  Hide
634
634
  </DropdownMenuItem>
635
635
  ) : null}
@@ -642,7 +642,7 @@ function SortableRow({
642
642
  void runArchive();
643
643
  }}
644
644
  >
645
- <IconArchive className="mr-2 h-3.5 w-3.5" />
645
+ <IconArchive className="me-2 h-3.5 w-3.5" />
646
646
  Archive
647
647
  </DropdownMenuItem>
648
648
  <DropdownMenuSeparator />
@@ -654,7 +654,7 @@ function SortableRow({
654
654
  }}
655
655
  className="text-destructive focus:text-destructive"
656
656
  >
657
- <IconTrash className="mr-2 h-3.5 w-3.5" />
657
+ <IconTrash className="me-2 h-3.5 w-3.5" />
658
658
  Delete permanently
659
659
  </DropdownMenuItem>
660
660
  </>
@@ -667,7 +667,7 @@ function SortableRow({
667
667
  }}
668
668
  className="text-destructive focus:text-destructive"
669
669
  >
670
- <IconTrash className="mr-2 h-3.5 w-3.5" />
670
+ <IconTrash className="me-2 h-3.5 w-3.5" />
671
671
  Delete
672
672
  </DropdownMenuItem>
673
673
  )}
@@ -789,7 +789,7 @@ function SortableDashboardItem({
789
789
  }
790
790
  >
791
791
  {isActive && allSubviews.length > 0 && (
792
- <div className="ml-6 mt-0.5 space-y-0.5">
792
+ <div className="ms-6 mt-0.5 space-y-0.5">
793
793
  {allSubviews.map((sv) => {
794
794
  const currentSearch = new URLSearchParams(location.search);
795
795
  const svUrl = new URL(sv.href, window.location.origin);
@@ -805,7 +805,7 @@ function SortableDashboardItem({
805
805
  <div
806
806
  key={sv.id}
807
807
  className={cn(
808
- "group/sv flex items-center gap-1 rounded-md pr-1 transition-all",
808
+ "group/sv flex items-center gap-1 rounded-md pe-1 transition-all",
809
809
  isSubviewActive
810
810
  ? "bg-primary/10 text-primary"
811
811
  : "text-muted-foreground/70 hover:bg-sidebar-accent/50 hover:text-primary",
@@ -1842,7 +1842,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1842
1842
  {!mobile && (
1843
1843
  <div
1844
1844
  onMouseDown={handleResizeStart}
1845
- className="absolute right-0 top-0 bottom-0 w-1 cursor-col-resize hover:bg-primary/30 active:bg-primary/50 z-10"
1845
+ className="absolute end-0 top-0 bottom-0 w-1 cursor-col-resize hover:bg-primary/30 active:bg-primary/50 z-10"
1846
1846
  />
1847
1847
  )}
1848
1848
  <div className="flex h-12 shrink-0 items-center border-b border-border px-4 lg:px-6">
@@ -1959,7 +1959,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1959
1959
  <button
1960
1960
  type="button"
1961
1961
  onClick={toggleDashOpen}
1962
- className="flex min-w-0 flex-1 items-center gap-3 px-3 py-2 text-left"
1962
+ className="flex min-w-0 flex-1 items-center gap-3 px-3 py-2 text-start"
1963
1963
  aria-expanded={dashOpen}
1964
1964
  >
1965
1965
  <IconChartBar className="h-4 w-4 shrink-0" />
@@ -1977,7 +1977,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1977
1977
  <button
1978
1978
  type="button"
1979
1979
  onClick={toggleDashOpen}
1980
- className="mr-1 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 hover:bg-sidebar-accent hover:text-foreground"
1980
+ className="me-1 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 hover:bg-sidebar-accent hover:text-foreground"
1981
1981
  aria-label={
1982
1982
  dashOpen ? "Collapse dashboards" : "Expand dashboards"
1983
1983
  }
@@ -2001,7 +2001,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2001
2001
  items={displayedDashboards.map((d) => d.id)}
2002
2002
  strategy={verticalListSortingStrategy}
2003
2003
  >
2004
- <div className="ml-4 min-w-0 space-y-0.5">
2004
+ <div className="ms-4 min-w-0 space-y-0.5">
2005
2005
  {displayedDashboards.map((d) => (
2006
2006
  <SortableDashboardItem
2007
2007
  key={d.id}
@@ -2062,7 +2062,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2062
2062
  <button
2063
2063
  type="button"
2064
2064
  onClick={toggleAnalysesOpen}
2065
- className="flex min-w-0 flex-1 items-center gap-3 px-3 py-2 text-left"
2065
+ className="flex min-w-0 flex-1 items-center gap-3 px-3 py-2 text-start"
2066
2066
  aria-expanded={analysesOpen}
2067
2067
  >
2068
2068
  <IconReportAnalytics className="h-4 w-4 shrink-0" />
@@ -2084,7 +2084,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2084
2084
  <button
2085
2085
  type="button"
2086
2086
  onClick={toggleAnalysesOpen}
2087
- className="mr-1 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 hover:bg-sidebar-accent hover:text-foreground"
2087
+ className="me-1 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 hover:bg-sidebar-accent hover:text-foreground"
2088
2088
  aria-label={
2089
2089
  analysesOpen ? "Collapse analyses" : "Expand analyses"
2090
2090
  }
@@ -2108,7 +2108,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2108
2108
  items={displayedAnalyses.map((a) => a.id)}
2109
2109
  strategy={verticalListSortingStrategy}
2110
2110
  >
2111
- <div className="ml-4 min-w-0 space-y-0.5">
2111
+ <div className="ms-4 min-w-0 space-y-0.5">
2112
2112
  {displayedAnalyses.map((a) => (
2113
2113
  <SortableRow
2114
2114
  key={a.id}
@@ -2233,7 +2233,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2233
2233
  <TooltipTrigger asChild>
2234
2234
  <PopoverTrigger asChild>
2235
2235
  <button className="flex items-center justify-center rounded-lg p-2 text-muted-foreground transition-all hover:text-primary cursor-pointer hover:bg-sidebar-accent/50">
2236
- <IconLogout className="h-4 w-4" />
2236
+ <IconLogout className="h-4 w-4 rtl:-scale-x-100" />
2237
2237
  </button>
2238
2238
  </PopoverTrigger>
2239
2239
  </TooltipTrigger>
@@ -78,13 +78,13 @@ export default function AnalysesList() {
78
78
  </p>
79
79
  {analyses && analyses.length > 0 && (
80
80
  <div className="relative">
81
- <IconSearch className="absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/60 pointer-events-none" />
81
+ <IconSearch className="absolute start-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/60 pointer-events-none" />
82
82
  <input
83
83
  type="search"
84
84
  placeholder="Search analyses…"
85
85
  value={searchQuery}
86
86
  onChange={(e) => setSearchQuery(e.target.value)}
87
- className="h-8 rounded-md border border-input bg-background pl-8 pr-3 text-xs placeholder:text-muted-foreground/60 focus:outline-none focus:ring-2 focus:ring-ring"
87
+ className="h-8 rounded-md border border-input bg-background ps-8 pe-3 text-xs placeholder:text-muted-foreground/60 focus:outline-none focus:ring-2 focus:ring-ring"
88
88
  />
89
89
  </div>
90
90
  )}
@@ -163,12 +163,12 @@ export default function KnowledgeRoute() {
163
163
  <Card>
164
164
  <CardContent className="flex flex-col gap-3 p-4 lg:flex-row lg:items-center">
165
165
  <div className="relative min-w-0 flex-1">
166
- <IconSearch className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
166
+ <IconSearch className="pointer-events-none absolute start-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
167
167
  <Input
168
168
  value={query}
169
169
  onChange={(event) => updateParam("q", event.target.value)}
170
170
  placeholder="Search memories, topics, source names..."
171
- className="pl-9"
171
+ className="ps-9"
172
172
  />
173
173
  </div>
174
174
  <div className="flex flex-col gap-2 sm:flex-row">
@@ -217,8 +217,8 @@ export default function KnowledgeRoute() {
217
217
  <TableHead>Source</TableHead>
218
218
  <TableHead>Status</TableHead>
219
219
  <TableHead>Company context</TableHead>
220
- <TableHead className="text-right">Confidence</TableHead>
221
- <TableHead className="text-right">Cites</TableHead>
220
+ <TableHead className="text-end">Confidence</TableHead>
221
+ <TableHead className="text-end">Cites</TableHead>
222
222
  </TableRow>
223
223
  </TableHeader>
224
224
  <TableBody>
@@ -262,12 +262,12 @@ export default function KnowledgeRoute() {
262
262
  onPreview={() => void openCanonicalPreview(row)}
263
263
  />
264
264
  </TableCell>
265
- <TableCell className="text-right">
265
+ <TableCell className="text-end">
266
266
  {typeof row.confidence === "number"
267
267
  ? formatPercent(row.confidence)
268
268
  : "n/a"}
269
269
  </TableCell>
270
- <TableCell className="text-right">
270
+ <TableCell className="text-end">
271
271
  {row.citations ?? 0}
272
272
  </TableCell>
273
273
  </TableRow>
@@ -416,11 +416,11 @@ export default function OpsRoute() {
416
416
  <TableHead>Status</TableHead>
417
417
  <TableHead>Capture</TableHead>
418
418
  <TableHead>Source</TableHead>
419
- <TableHead className="text-right">Attempts</TableHead>
419
+ <TableHead className="text-end">Attempts</TableHead>
420
420
  <TableHead>Run after</TableHead>
421
421
  <TableHead>Updated</TableHead>
422
422
  <TableHead>Reason</TableHead>
423
- <TableHead className="text-right">Action</TableHead>
423
+ <TableHead className="text-end">Action</TableHead>
424
424
  </TableRow>
425
425
  </TableHeader>
426
426
  <TableBody>
@@ -474,7 +474,7 @@ export default function OpsRoute() {
474
474
  </p>
475
475
  </div>
476
476
  </TableCell>
477
- <TableCell className="text-right tabular-nums">
477
+ <TableCell className="text-end tabular-nums">
478
478
  {item.attempts}
479
479
  </TableCell>
480
480
  <TableCell className="whitespace-nowrap text-sm text-muted-foreground">
@@ -491,7 +491,7 @@ export default function OpsRoute() {
491
491
  "No issue recorded"}
492
492
  </p>
493
493
  </TableCell>
494
- <TableCell className="text-right">
494
+ <TableCell className="text-end">
495
495
  <Button
496
496
  size="sm"
497
497
  variant="outline"
@@ -144,12 +144,12 @@ export default function SearchRoute() {
144
144
  <Card>
145
145
  <CardContent className="grid gap-3 p-4">
146
146
  <div className="relative">
147
- <IconSearch className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
147
+ <IconSearch className="pointer-events-none absolute start-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
148
148
  <Input
149
149
  value={query}
150
150
  onChange={(event) => updateParam("q", event.target.value)}
151
151
  placeholder="Search decisions, customer facts, source names, transcripts, or policy snippets..."
152
- className="h-11 pl-9 text-base"
152
+ className="h-11 ps-9 text-base"
153
153
  autoFocus
154
154
  />
155
155
  </div>
@@ -420,10 +420,10 @@ function SearchResultDetails({
420
420
  </Badge>
421
421
  ) : null}
422
422
  </div>
423
- <SheetTitle className="text-left text-xl leading-7">
423
+ <SheetTitle className="text-start text-xl leading-7">
424
424
  {result.title}
425
425
  </SheetTitle>
426
- <SheetDescription className="text-left leading-6">
426
+ <SheetDescription className="text-start leading-6">
427
427
  {body}
428
428
  </SheetDescription>
429
429
  </SheetHeader>
@@ -608,9 +608,9 @@ function NumberField({
608
608
  max={max}
609
609
  value={value}
610
610
  onChange={(event) => onChange(Number(event.target.value))}
611
- className="rounded-r-none"
611
+ className="rounded-e-none"
612
612
  />
613
- <div className="flex min-w-20 items-center justify-center rounded-r-md border border-l-0 border-input bg-muted px-3 text-sm text-muted-foreground">
613
+ <div className="flex min-w-20 items-center justify-center rounded-e-md border border-s-0 border-input bg-muted px-3 text-sm text-muted-foreground">
614
614
  {suffix}
615
615
  </div>
616
616
  </div>
@@ -653,7 +653,7 @@ function PolicyRow({ label, value }: { label: string; value: string }) {
653
653
  return (
654
654
  <div className="flex items-center justify-between gap-3">
655
655
  <span className="min-w-0 text-muted-foreground">{label}</span>
656
- <span className="max-w-40 truncate text-right font-medium capitalize">
656
+ <span className="max-w-40 truncate text-end font-medium capitalize">
657
657
  {value.replace(/_/g, " ")}
658
658
  </span>
659
659
  </div>
@@ -1074,7 +1074,7 @@ function ProviderCatalog({
1074
1074
  variant="outline"
1075
1075
  className={`${grantStateClass(grantState)} w-fit max-w-full`}
1076
1076
  >
1077
- <GrantIcon className="mr-1 size-3" />
1077
+ <GrantIcon className="me-1 size-3" />
1078
1078
  {grantStateLabel(grantState)}
1079
1079
  </Badge>
1080
1080
  <Badge
@@ -2037,7 +2037,7 @@ export default function SourcesRoute() {
2037
2037
  {enqueueCapturesDistillation.isPending ? (
2038
2038
  <IconLoader2 className="size-4 animate-spin" />
2039
2039
  ) : (
2040
- <IconSend className="size-4" />
2040
+ <IconSend className="size-4 rtl:-scale-x-100" />
2041
2041
  )}
2042
2042
  Queue selected
2043
2043
  </Button>
@@ -2205,7 +2205,7 @@ export default function SourcesRoute() {
2205
2205
  {enqueueDistillation.isPending ? (
2206
2206
  <IconLoader2 className="size-4 animate-spin" />
2207
2207
  ) : (
2208
- <IconSend className="size-4" />
2208
+ <IconSend className="size-4 rtl:-scale-x-100" />
2209
2209
  )}
2210
2210
  {queueActionLabel(queue)}
2211
2211
  </Button>
@@ -122,7 +122,22 @@ export type ProcessRunPreparation = {
122
122
  status: number;
123
123
  /** Error payload. */
124
124
  error: string;
125
+ /**
126
+ * The run id parsed from the body, when present. Carried even on failure
127
+ * so the route can RECORD the auth/validation failure ONTO the run
128
+ * (diag_stage) before returning the error status — otherwise a 401/503 in
129
+ * the unreadable Netlify background function would leave the run to time
130
+ * out with no clue why. Null when no run id could be parsed.
131
+ */
132
+ runId: string | null;
125
133
  };
134
+ /**
135
+ * Parse the run id from a `_process-run` request body without authenticating.
136
+ * Mirrors the precedence in `prepareProcessRunRequest` (marker.runId, then
137
+ * top-level taskId). Returns null when neither is a usable string. Used so the
138
+ * route can attach a diagnostic to the run even on an auth/validation failure.
139
+ */
140
+ export declare function extractProcessRunId(body: unknown): string | null;
126
141
  /**
127
142
  * Pure, transport-agnostic core of the `_process-run` route: validate the body,
128
143
  * authenticate the HMAC self-dispatch, and produce the body the re-entered
@@ -1 +1 @@
1
- {"version":3,"file":"durable-background.d.ts","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"AA6CA;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,2CACE,CAAC;AAE3C;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,4BAA4B,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,kCAAkC,gDAA0D,CAAC;AAsB1G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,sCAAsC,IAAI,MAAM,CAY/D;AAED;;;GAGG;AACH,eAAO,MAAM,iCAAiC,kCACb,CAAC;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,oBAAoB,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,mCAAmC,IAAI,OAAO,CAsB7D;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAsBvD;AAyBD;;;;;;GAMG;AACH,wBAAgB,mCAAmC,IAAI,OAAO,CAM7D;AAED,uDAAuD;AACvD,MAAM,MAAM,qBAAqB,GAC7B;IACE,EAAE,EAAE,IAAI,CAAC;IACT,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,qBAAqB,CA0CvB"}
1
+ {"version":3,"file":"durable-background.d.ts","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"AA6CA;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,2CACE,CAAC;AAE3C;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,4BAA4B,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,kCAAkC,gDAA0D,CAAC;AAsB1G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,sCAAsC,IAAI,MAAM,CAY/D;AAED;;;GAGG;AACH,eAAO,MAAM,iCAAiC,kCACb,CAAC;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,oBAAoB,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,mCAAmC,IAAI,OAAO,CAsB7D;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAsBvD;AAyBD;;;;;;GAMG;AACH,wBAAgB,mCAAmC,IAAI,OAAO,CAM7D;AAED,uDAAuD;AACvD,MAAM,MAAM,qBAAqB,GAC7B;IACE,EAAE,EAAE,IAAI,CAAC;IACT,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEN;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAahE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,qBAAqB,CAiDvB"}
@@ -235,6 +235,25 @@ export function isAgentChatDurableBackgroundEnabled() {
235
235
  isHostedRuntimeForDurableBackground() &&
236
236
  hasConfiguredA2ASecret());
237
237
  }
238
+ /**
239
+ * Parse the run id from a `_process-run` request body without authenticating.
240
+ * Mirrors the precedence in `prepareProcessRunRequest` (marker.runId, then
241
+ * top-level taskId). Returns null when neither is a usable string. Used so the
242
+ * route can attach a diagnostic to the run even on an auth/validation failure.
243
+ */
244
+ export function extractProcessRunId(body) {
245
+ if (!body || typeof body !== "object")
246
+ return null;
247
+ const record = body;
248
+ const marker = record[AGENT_CHAT_BACKGROUND_RUN_FIELD];
249
+ if (marker && typeof marker.runId === "string" && marker.runId) {
250
+ return marker.runId;
251
+ }
252
+ if (typeof record.taskId === "string" && record.taskId) {
253
+ return record.taskId;
254
+ }
255
+ return null;
256
+ }
238
257
  /**
239
258
  * Pure, transport-agnostic core of the `_process-run` route: validate the body,
240
259
  * authenticate the HMAC self-dispatch, and produce the body the re-entered
@@ -253,7 +272,12 @@ export function isAgentChatDurableBackgroundEnabled() {
253
272
  */
254
273
  export function prepareProcessRunRequest(body, authHeader) {
255
274
  if (!body || typeof body !== "object") {
256
- return { ok: false, status: 400, error: "Invalid request body" };
275
+ return {
276
+ ok: false,
277
+ status: 400,
278
+ error: "Invalid request body",
279
+ runId: null,
280
+ };
257
281
  }
258
282
  const record = body;
259
283
  const marker = record[AGENT_CHAT_BACKGROUND_RUN_FIELD];
@@ -263,7 +287,7 @@ export function prepareProcessRunRequest(body, authHeader) {
263
287
  ? record.taskId
264
288
  : "";
265
289
  if (!runId) {
266
- return { ok: false, status: 400, error: "runId required" };
290
+ return { ok: false, status: 400, error: "runId required", runId: null };
267
291
  }
268
292
  if (hasConfiguredA2ASecret()) {
269
293
  const token = extractBearerToken(authHeader);
@@ -272,6 +296,7 @@ export function prepareProcessRunRequest(body, authHeader) {
272
296
  ok: false,
273
297
  status: 401,
274
298
  error: "Invalid or expired processor token",
299
+ runId,
275
300
  };
276
301
  }
277
302
  }
@@ -280,6 +305,7 @@ export function prepareProcessRunRequest(body, authHeader) {
280
305
  ok: false,
281
306
  status: 503,
282
307
  error: "Agent chat background processor not configured — set A2A_SECRET on this deployment.",
308
+ runId,
283
309
  };
284
310
  }
285
311
  // Ensure the marker is present so the re-entered handler runs as the