agent-relay-server 0.76.0 → 0.77.0

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 (135) hide show
  1. package/docs/openapi.json +94 -1
  2. package/package.json +2 -2
  3. package/public/assets/MessageBubble-Ct4RjObq.js +3 -0
  4. package/public/assets/{MessageBubble-DzhG1x8z.js.map → MessageBubble-Ct4RjObq.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-brNJanGs.js → PlanGraphPanel-BQcLye6d.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-brNJanGs.js.map → PlanGraphPanel-BQcLye6d.js.map} +1 -1
  7. package/public/assets/{activity-DIXJ-CVu.js → activity-DeQhQ17b.js} +2 -2
  8. package/public/assets/{activity-DIXJ-CVu.js.map → activity-DeQhQ17b.js.map} +1 -1
  9. package/public/assets/{agent-profiles-D1X5BBW_.js → agent-profiles-DwR6zuEJ.js} +2 -2
  10. package/public/assets/{agent-profiles-D1X5BBW_.js.map → agent-profiles-DwR6zuEJ.js.map} +1 -1
  11. package/public/assets/{agents-BG9XZcVD.js → agents-coulRN38.js} +2 -2
  12. package/public/assets/{agents-BG9XZcVD.js.map → agents-coulRN38.js.map} +1 -1
  13. package/public/assets/{analytics-e_HOZBPx.js → analytics-BTq4XQiF.js} +3 -3
  14. package/public/assets/{analytics-e_HOZBPx.js.map → analytics-BTq4XQiF.js.map} +1 -1
  15. package/public/assets/{automation-jVNnsYV7.js → automation-V97mnoZp.js} +2 -2
  16. package/public/assets/{automation-jVNnsYV7.js.map → automation-V97mnoZp.js.map} +1 -1
  17. package/public/assets/{badge-YQhT30dy.js → badge-BxG6_yfp.js} +2 -2
  18. package/public/assets/{badge-YQhT30dy.js.map → badge-BxG6_yfp.js.map} +1 -1
  19. package/public/assets/branch-state-badge-DaCW_qGP.js +2 -0
  20. package/public/assets/{branch-state-badge-B7T81Oct.js.map → branch-state-badge-DaCW_qGP.js.map} +1 -1
  21. package/public/assets/{button-CaFI76Ja.js → button-D5k3GbF6.js} +2 -2
  22. package/public/assets/{button-CaFI76Ja.js.map → button-D5k3GbF6.js.map} +1 -1
  23. package/public/assets/{card-B_fQtKce.js → card-DKyFtrez.js} +2 -2
  24. package/public/assets/{card-B_fQtKce.js.map → card-DKyFtrez.js.map} +1 -1
  25. package/public/assets/{channels-BAD_kIo7.js → channels-EHZtUB_U.js} +2 -2
  26. package/public/assets/{channels-BAD_kIo7.js.map → channels-EHZtUB_U.js.map} +1 -1
  27. package/public/assets/chat-B8fs8r1x.js +2 -0
  28. package/public/assets/{chat-B7EMdwIg.js.map → chat-B8fs8r1x.js.map} +1 -1
  29. package/public/assets/{connectors-Cdjtxly2.js → connectors-B3BbETXb.js} +2 -2
  30. package/public/assets/{connectors-Cdjtxly2.js.map → connectors-B3BbETXb.js.map} +1 -1
  31. package/public/assets/copy-button-D3XAkVC2.js +2 -0
  32. package/public/assets/{copy-button-m00JmflM.js.map → copy-button-D3XAkVC2.js.map} +1 -1
  33. package/public/assets/dist-B9_RgXN9.js +2 -0
  34. package/public/assets/{dist-DfQP9-VC.js.map → dist-B9_RgXN9.js.map} +1 -1
  35. package/public/assets/dist-BuYGwGZ-.js +2 -0
  36. package/public/assets/{dist-CydxJz3m.js.map → dist-BuYGwGZ-.js.map} +1 -1
  37. package/public/assets/{dist-ChP4KvKi.js → dist-CAu7-DrN.js} +2 -2
  38. package/public/assets/{dist-ChP4KvKi.js.map → dist-CAu7-DrN.js.map} +1 -1
  39. package/public/assets/{dist-DeAdFM5_.js → dist-CF2meFCY.js} +2 -2
  40. package/public/assets/{dist-DeAdFM5_.js.map → dist-CF2meFCY.js.map} +1 -1
  41. package/public/assets/{dist-B_yrHbsb.js → dist-CR7zKC1F.js} +2 -2
  42. package/public/assets/{dist-B_yrHbsb.js.map → dist-CR7zKC1F.js.map} +1 -1
  43. package/public/assets/{dist-C-viWls-.js → dist-CUxt3gHd.js} +2 -2
  44. package/public/assets/{dist-C-viWls-.js.map → dist-CUxt3gHd.js.map} +1 -1
  45. package/public/assets/{dist-D8Yr1AIJ.js → dist-D1Bwx4cc.js} +2 -2
  46. package/public/assets/{dist-D8Yr1AIJ.js.map → dist-D1Bwx4cc.js.map} +1 -1
  47. package/public/assets/{dist-Cnlh_NpI.js → dist-D32lF-j6.js} +2 -2
  48. package/public/assets/{dist-Cnlh_NpI.js.map → dist-D32lF-j6.js.map} +1 -1
  49. package/public/assets/{dist-DeM72J6a.js → dist-DDrB3y1I.js} +2 -2
  50. package/public/assets/{dist-DeM72J6a.js.map → dist-DDrB3y1I.js.map} +1 -1
  51. package/public/assets/dist-DPQKhU73.js +2 -0
  52. package/public/assets/{dist-CYS2puT9.js.map → dist-DPQKhU73.js.map} +1 -1
  53. package/public/assets/{dist-D7K3qgp7.js → dist-YjHj0NpU.js} +2 -2
  54. package/public/assets/{dist-D7K3qgp7.js.map → dist-YjHj0NpU.js.map} +1 -1
  55. package/public/assets/{es2015-C3DnXBqu.js → es2015-WF4MRcZQ.js} +2 -2
  56. package/public/assets/{es2015-C3DnXBqu.js.map → es2015-WF4MRcZQ.js.map} +1 -1
  57. package/public/assets/{formatted-body-impl-DBWLuF0p.js → formatted-body-impl-CQ-8HQ7u.js} +2 -2
  58. package/public/assets/{formatted-body-impl-DBWLuF0p.js.map → formatted-body-impl-CQ-8HQ7u.js.map} +1 -1
  59. package/public/assets/index-DJtda8l2.js +22 -0
  60. package/public/assets/index-DJtda8l2.js.map +1 -0
  61. package/public/assets/index-DvNy9IVg.css +2 -0
  62. package/public/assets/{input-Cqqq0OFl.js → input-DSJQbsdS.js} +2 -2
  63. package/public/assets/{input-Cqqq0OFl.js.map → input-DSJQbsdS.js.map} +1 -1
  64. package/public/assets/{insights-CzGE5EyU.js → insights-CtT5cEif.js} +2 -2
  65. package/public/assets/{insights-CzGE5EyU.js.map → insights-CtT5cEif.js.map} +1 -1
  66. package/public/assets/{integrations-DzRla8mj.js → integrations-BWBxWl3k.js} +2 -2
  67. package/public/assets/{integrations-DzRla8mj.js.map → integrations-BWBxWl3k.js.map} +1 -1
  68. package/public/assets/{lib-6L4m0E3M.js → lib-BxQSXiQ7.js} +3 -3
  69. package/public/assets/{lib-6L4m0E3M.js.map → lib-BxQSXiQ7.js.map} +1 -1
  70. package/public/assets/{lucide-react-DI8kTavp.js → lucide-react-BH2ROV8n.js} +2 -2
  71. package/public/assets/{lucide-react-DI8kTavp.js.map → lucide-react-BH2ROV8n.js.map} +1 -1
  72. package/public/assets/{maintenance-CGWGOhDP.js → maintenance-BWLMjX8h.js} +2 -2
  73. package/public/assets/{maintenance-CGWGOhDP.js.map → maintenance-BWLMjX8h.js.map} +1 -1
  74. package/public/assets/{managed-agents-DyYn12ir.js → managed-agents-KxHV0VcQ.js} +2 -2
  75. package/public/assets/{managed-agents-DyYn12ir.js.map → managed-agents-KxHV0VcQ.js.map} +1 -1
  76. package/public/assets/{markdown-preview-impl-BA37v8OC.js → markdown-preview-impl-C5b5cBM0.js} +2 -2
  77. package/public/assets/{markdown-preview-impl-BA37v8OC.js.map → markdown-preview-impl-C5b5cBM0.js.map} +1 -1
  78. package/public/assets/{memory-CoIM-761.js → memory-Bh3UgQCk.js} +2 -2
  79. package/public/assets/{memory-CoIM-761.js.map → memory-Bh3UgQCk.js.map} +1 -1
  80. package/public/assets/{messages-D-TOb6_t.js → messages-Dg7TSxpZ.js} +2 -2
  81. package/public/assets/{messages-D-TOb6_t.js.map → messages-Dg7TSxpZ.js.map} +1 -1
  82. package/public/assets/{orchestrators-XLGUAnKs.js → orchestrators-BBKW7bO0.js} +2 -2
  83. package/public/assets/{orchestrators-XLGUAnKs.js.map → orchestrators-BBKW7bO0.js.map} +1 -1
  84. package/public/assets/{overview-C4Netp8S.js → overview-a8lG62AZ.js} +2 -2
  85. package/public/assets/{overview-C4Netp8S.js.map → overview-a8lG62AZ.js.map} +1 -1
  86. package/public/assets/{pairs-1X219PK6.js → pairs-ChSYXFwu.js} +2 -2
  87. package/public/assets/{pairs-1X219PK6.js.map → pairs-ChSYXFwu.js.map} +1 -1
  88. package/public/assets/{projects-DluLrkwe.js → projects-DIN_O7AJ.js} +2 -2
  89. package/public/assets/{projects-DluLrkwe.js.map → projects-DIN_O7AJ.js.map} +1 -1
  90. package/public/assets/{react-dom-Ct8qXivQ.js → react-dom-B_tGtv3Y.js} +2 -2
  91. package/public/assets/{react-dom-Ct8qXivQ.js.map → react-dom-B_tGtv3Y.js.map} +1 -1
  92. package/public/assets/{security-CCfKm-ow.js → security-D0obEjDT.js} +2 -2
  93. package/public/assets/{security-CCfKm-ow.js.map → security-D0obEjDT.js.map} +1 -1
  94. package/public/assets/{select-BpPDknSO.js → select-DwCP2Jhh.js} +2 -2
  95. package/public/assets/{select-BpPDknSO.js.map → select-DwCP2Jhh.js.map} +1 -1
  96. package/public/assets/settings-f1l2R0e8.js +4 -0
  97. package/public/assets/{settings-BUD05ZM3.js.map → settings-f1l2R0e8.js.map} +1 -1
  98. package/public/assets/{store-D1K4_xId.js → store-m74m5eNF.js} +3 -3
  99. package/public/assets/{store-D1K4_xId.js.map → store-m74m5eNF.js.map} +1 -1
  100. package/public/assets/{tasks-Dvqe1bbe.js → tasks-DDVfmUst.js} +2 -2
  101. package/public/assets/{tasks-Dvqe1bbe.js.map → tasks-DDVfmUst.js.map} +1 -1
  102. package/public/assets/{teams-I0UCuF9H.js → teams-COZKriLe.js} +2 -2
  103. package/public/assets/{teams-I0UCuF9H.js.map → teams-COZKriLe.js.map} +1 -1
  104. package/public/assets/{terminal-viewer-impl-CKM8zVGQ.js → terminal-viewer-impl-CjOZ__aw.js} +3 -3
  105. package/public/assets/{terminal-viewer-impl-CKM8zVGQ.js.map → terminal-viewer-impl-CjOZ__aw.js.map} +1 -1
  106. package/public/assets/{use-keyboard-viewport-DxmCAbQi.js → use-keyboard-viewport-BVAU-Ud8.js} +2 -2
  107. package/public/assets/{use-keyboard-viewport-DxmCAbQi.js.map → use-keyboard-viewport-BVAU-Ud8.js.map} +1 -1
  108. package/public/assets/work-queue-BC7heUfD.js +2 -0
  109. package/public/assets/{work-queue-xWRsNNPk.js.map → work-queue-BC7heUfD.js.map} +1 -1
  110. package/public/assets/workspaces-CIGMb3ST.js +3 -0
  111. package/public/assets/{workspaces-B_nIm6Tw.js.map → workspaces-CIGMb3ST.js.map} +1 -1
  112. package/public/index.html +21 -21
  113. package/src/db/agents.ts +4 -46
  114. package/src/db/index.ts +1 -0
  115. package/src/db/migrations.ts +91 -1
  116. package/src/db/provider-quotas.ts +220 -0
  117. package/src/db/schema.ts +16 -1
  118. package/src/quota.ts +14 -1
  119. package/src/routes/index.ts +3 -0
  120. package/src/routes/provider-quotas.ts +48 -0
  121. package/src/security.ts +1 -0
  122. package/src/token-db.ts +3 -3
  123. package/public/assets/MessageBubble-DzhG1x8z.js +0 -3
  124. package/public/assets/branch-state-badge-B7T81Oct.js +0 -2
  125. package/public/assets/chat-B7EMdwIg.js +0 -2
  126. package/public/assets/copy-button-m00JmflM.js +0 -2
  127. package/public/assets/dist-CYS2puT9.js +0 -2
  128. package/public/assets/dist-CydxJz3m.js +0 -2
  129. package/public/assets/dist-DfQP9-VC.js +0 -2
  130. package/public/assets/index-DB6XGZy9.js +0 -23
  131. package/public/assets/index-DB6XGZy9.js.map +0 -1
  132. package/public/assets/index-DepEqGST.css +0 -2
  133. package/public/assets/settings-BUD05ZM3.js +0 -4
  134. package/public/assets/work-queue-xWRsNNPk.js +0 -2
  135. package/public/assets/workspaces-B_nIm6Tw.js +0 -3
@@ -602,13 +602,103 @@ export function applyMigrations(): void {
602
602
  getDb().run(`
603
603
  CREATE TABLE IF NOT EXISTS quota_snapshots (
604
604
  id INTEGER PRIMARY KEY AUTOINCREMENT,
605
- agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
605
+ provider TEXT NOT NULL,
606
+ account_key TEXT NOT NULL,
607
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
606
608
  windows TEXT NOT NULL,
607
609
  source TEXT NOT NULL,
608
610
  quota_state TEXT NOT NULL,
609
611
  captured_at INTEGER NOT NULL
610
612
  )
611
613
  `);
614
+ const quotaSnapshotCols = (getDb().query("PRAGMA table_info(quota_snapshots)").all() as Array<{ name: string }>).map((col) => col.name);
615
+ if (!quotaSnapshotCols.includes("provider") || !quotaSnapshotCols.includes("account_key")) {
616
+ getDb().run("ALTER TABLE quota_snapshots RENAME TO quota_snapshots_legacy_agent");
617
+ getDb().run(`
618
+ CREATE TABLE quota_snapshots (
619
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
620
+ provider TEXT NOT NULL,
621
+ account_key TEXT NOT NULL,
622
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
623
+ windows TEXT NOT NULL,
624
+ source TEXT NOT NULL,
625
+ quota_state TEXT NOT NULL,
626
+ captured_at INTEGER NOT NULL
627
+ )
628
+ `);
629
+ getDb().run(`
630
+ INSERT INTO quota_snapshots (id, provider, account_key, agent_id, windows, source, quota_state, captured_at)
631
+ SELECT
632
+ qs.id,
633
+ CASE
634
+ WHEN json_extract(a.meta, '$.provider') IS NOT NULL THEN lower(json_extract(a.meta, '$.provider'))
635
+ WHEN a.tags LIKE '%"codex"%' OR lower(a.id) LIKE '%codex%' OR lower(a.name) LIKE '%codex%' THEN 'codex'
636
+ ELSE 'claude'
637
+ END,
638
+ coalesce(
639
+ nullif(json_extract(a.meta, '$.accountKey'), ''),
640
+ nullif(json_extract(a.meta, '$.accountId'), ''),
641
+ nullif(json_extract(a.meta, '$.orgId'), ''),
642
+ 'host:' || coalesce(a.machine, a.rig, 'unknown')
643
+ ),
644
+ qs.agent_id,
645
+ qs.windows,
646
+ qs.source,
647
+ qs.quota_state,
648
+ qs.captured_at
649
+ FROM quota_snapshots_legacy_agent qs
650
+ LEFT JOIN agents a ON a.id = qs.agent_id
651
+ `);
652
+ getDb().run("DROP TABLE quota_snapshots_legacy_agent");
653
+ }
654
+ getDb().run(`
655
+ CREATE TABLE IF NOT EXISTS provider_quotas (
656
+ provider TEXT NOT NULL,
657
+ account_key TEXT NOT NULL,
658
+ quota_state TEXT,
659
+ source_agent_ids TEXT NOT NULL DEFAULT '[]',
660
+ last_updated_at INTEGER,
661
+ last_attempt_at INTEGER NOT NULL,
662
+ last_error TEXT,
663
+ updated_at INTEGER NOT NULL,
664
+ created_at INTEGER NOT NULL,
665
+ PRIMARY KEY (provider, account_key)
666
+ )
667
+ `);
668
+ getDb().run(`
669
+ INSERT INTO provider_quotas (
670
+ provider, account_key, quota_state, source_agent_ids, last_updated_at,
671
+ last_attempt_at, last_error, updated_at, created_at
672
+ )
673
+ SELECT
674
+ CASE
675
+ WHEN json_extract(meta, '$.provider') IS NOT NULL THEN lower(json_extract(meta, '$.provider'))
676
+ WHEN tags LIKE '%"codex"%' OR lower(id) LIKE '%codex%' OR lower(name) LIKE '%codex%' THEN 'codex'
677
+ ELSE 'claude'
678
+ END AS provider,
679
+ coalesce(
680
+ nullif(json_extract(meta, '$.accountKey'), ''),
681
+ nullif(json_extract(meta, '$.accountId'), ''),
682
+ nullif(json_extract(meta, '$.orgId'), ''),
683
+ 'host:' || coalesce(machine, rig, 'unknown')
684
+ ) AS account_key,
685
+ quota_state,
686
+ json_array(id),
687
+ coalesce(json_extract(quota_state, '$.updatedAt'), last_seen),
688
+ coalesce(json_extract(quota_state, '$.updatedAt'), last_seen),
689
+ NULL,
690
+ last_seen,
691
+ created_at
692
+ FROM agents
693
+ WHERE quota_state IS NOT NULL
694
+ ON CONFLICT(provider, account_key) DO UPDATE SET
695
+ quota_state = excluded.quota_state,
696
+ source_agent_ids = excluded.source_agent_ids,
697
+ last_updated_at = max(coalesce(provider_quotas.last_updated_at, 0), coalesce(excluded.last_updated_at, 0)),
698
+ last_attempt_at = max(provider_quotas.last_attempt_at, excluded.last_attempt_at),
699
+ updated_at = max(provider_quotas.updated_at, excluded.updated_at)
700
+ `);
701
+ getDb().run("CREATE INDEX IF NOT EXISTS idx_quota_snapshots_account_time ON quota_snapshots(provider, account_key, captured_at DESC)");
612
702
  getDb().run("CREATE INDEX IF NOT EXISTS idx_quota_snapshots_agent_time ON quota_snapshots(agent_id, captured_at DESC)");
613
703
 
614
704
  getDb().run(`
@@ -0,0 +1,220 @@
1
+ import { DAY_MS } from "../config";
2
+ import { parseJson } from "../utils";
3
+ import { getDb } from "./connection.ts";
4
+ import { parseStringArray } from "./shared.ts";
5
+ import type { AgentCard, ProviderQuotaError, ProviderQuotaMatrix, ProviderQuotaRecord, ProviderQuotaSnapshot, ProviderQuotaUpdateInput, QuotaState } from "../types";
6
+
7
+ const PROVIDER_QUOTA_STALE_AFTER_MS = 10 * 60_000;
8
+
9
+ type ProviderQuotaRow = {
10
+ provider: string;
11
+ account_key: string;
12
+ quota_state?: string | null;
13
+ source_agent_ids?: string | null;
14
+ last_updated_at?: number | null;
15
+ last_attempt_at: number;
16
+ last_error?: string | null;
17
+ };
18
+
19
+ type ProviderQuotaSnapshotRow = {
20
+ provider: string;
21
+ account_key: string;
22
+ agent_id?: string | null;
23
+ quota_state: string;
24
+ captured_at: number;
25
+ };
26
+
27
+ function cleanKey(value: string): string {
28
+ return value.trim();
29
+ }
30
+
31
+ function normalizeProvider(value: string): string {
32
+ return cleanKey(value).toLowerCase();
33
+ }
34
+
35
+ function normalizeSourceAgentIds(value: unknown): string[] {
36
+ return [...new Set(parseStringArray(typeof value === "string" ? value : JSON.stringify(value ?? [])).filter(Boolean))];
37
+ }
38
+
39
+ function quotaFingerprint(quota: QuotaState): string {
40
+ return JSON.stringify(quota.windows.map((window) => ({
41
+ name: window.name,
42
+ unit: window.unit,
43
+ utilization: window.utilization,
44
+ })).sort((a, b) => a.name.localeCompare(b.name) || a.unit.localeCompare(b.unit)));
45
+ }
46
+
47
+ function rowToSnapshot(row: ProviderQuotaSnapshotRow): ProviderQuotaSnapshot {
48
+ return {
49
+ provider: row.provider,
50
+ accountKey: row.account_key,
51
+ capturedAt: row.captured_at,
52
+ ...(row.agent_id ? { sourceAgentId: row.agent_id } : {}),
53
+ quota: parseJson<QuotaState>(row.quota_state, { windows: [], source: "probe", updatedAt: row.captured_at }),
54
+ };
55
+ }
56
+
57
+ function rowToRecord(row: ProviderQuotaRow, history: ProviderQuotaSnapshot[], now: number): ProviderQuotaRecord {
58
+ const lastError = parseJson<ProviderQuotaError | undefined>(row.last_error, undefined);
59
+ const lastUpdatedAt = row.last_updated_at ?? undefined;
60
+ return {
61
+ provider: row.provider,
62
+ accountKey: row.account_key,
63
+ ...(row.quota_state ? { quota: parseJson<QuotaState>(row.quota_state, { windows: [], source: "probe", updatedAt: lastUpdatedAt ?? row.last_attempt_at }) } : {}),
64
+ sourceAgentIds: normalizeSourceAgentIds(row.source_agent_ids),
65
+ ...(lastUpdatedAt !== undefined ? { lastUpdatedAt } : {}),
66
+ lastAttemptAt: row.last_attempt_at,
67
+ ...(lastError ? { lastError } : {}),
68
+ stale: lastUpdatedAt === undefined || now - lastUpdatedAt > PROVIDER_QUOTA_STALE_AFTER_MS,
69
+ history,
70
+ };
71
+ }
72
+
73
+ function latestSnapshotFingerprint(provider: string, accountKey: string): string | undefined {
74
+ const row = getDb().query(`
75
+ SELECT quota_state FROM quota_snapshots
76
+ WHERE provider = ? AND account_key = ?
77
+ ORDER BY captured_at DESC, id DESC
78
+ LIMIT 1
79
+ `).get(provider, accountKey) as { quota_state: string } | undefined;
80
+ return row ? quotaFingerprint(parseJson<QuotaState>(row.quota_state, { windows: [], source: "probe", updatedAt: 0 })) : undefined;
81
+ }
82
+
83
+ function providerQuotaIdentityForAgent(agent: Pick<AgentCard, "id" | "name" | "tags" | "machine" | "rig" | "meta">): { provider: string; accountKey: string } {
84
+ const meta = agent.meta ?? {};
85
+ const provider = typeof meta.provider === "string" && meta.provider.trim()
86
+ ? meta.provider
87
+ : agent.tags.includes("codex") || agent.id.toLowerCase().includes("codex") || agent.name.toLowerCase().includes("codex")
88
+ ? "codex"
89
+ : "claude";
90
+ const accountKey = typeof meta.accountKey === "string" && meta.accountKey.trim()
91
+ ? meta.accountKey
92
+ : typeof meta.accountId === "string" && meta.accountId.trim()
93
+ ? meta.accountId
94
+ : typeof meta.orgId === "string" && meta.orgId.trim()
95
+ ? meta.orgId
96
+ : `host:${agent.machine ?? agent.rig ?? "unknown"}`;
97
+ return { provider: normalizeProvider(provider), accountKey: cleanKey(accountKey) };
98
+ }
99
+
100
+ export function upsertProviderQuota(input: ProviderQuotaUpdateInput, now = Date.now()): ProviderQuotaRecord {
101
+ const provider = normalizeProvider(input.provider);
102
+ const accountKey = cleanKey(input.accountKey);
103
+ if (!provider) throw new Error("provider required");
104
+ if (!accountKey) throw new Error("accountKey required");
105
+ const existing = getDb().query("SELECT * FROM provider_quotas WHERE provider = ? AND account_key = ?").get(provider, accountKey) as ProviderQuotaRow | undefined;
106
+ const sourceAgentIds = normalizeSourceAgentIds(existing?.source_agent_ids);
107
+ if (input.sourceAgentId && !sourceAgentIds.includes(input.sourceAgentId)) sourceAgentIds.push(input.sourceAgentId);
108
+ const lastUpdatedAt = input.quota ? input.quota.updatedAt : existing?.last_updated_at ?? undefined;
109
+ const lastAttemptAt = input.lastAttemptAt ?? input.quota?.updatedAt ?? now;
110
+ const lastError = input.lastError === undefined
111
+ ? input.quota ? null : existing?.last_error ?? null
112
+ : input.lastError;
113
+ const quotaState = input.quota ? JSON.stringify(input.quota) : existing?.quota_state ?? null;
114
+
115
+ getDb().query(`
116
+ INSERT INTO provider_quotas (
117
+ provider, account_key, quota_state, source_agent_ids, last_updated_at,
118
+ last_attempt_at, last_error, updated_at, created_at
119
+ )
120
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
121
+ ON CONFLICT(provider, account_key) DO UPDATE SET
122
+ quota_state = excluded.quota_state,
123
+ source_agent_ids = excluded.source_agent_ids,
124
+ last_updated_at = excluded.last_updated_at,
125
+ last_attempt_at = excluded.last_attempt_at,
126
+ last_error = excluded.last_error,
127
+ updated_at = excluded.updated_at
128
+ `).run(
129
+ provider,
130
+ accountKey,
131
+ quotaState,
132
+ JSON.stringify(sourceAgentIds),
133
+ lastUpdatedAt ?? null,
134
+ lastAttemptAt,
135
+ lastError ? JSON.stringify(lastError) : null,
136
+ now,
137
+ now,
138
+ );
139
+
140
+ if (input.quota && latestSnapshotFingerprint(provider, accountKey) !== quotaFingerprint(input.quota)) {
141
+ recordProviderQuotaSnapshot({ provider, accountKey, quota: input.quota, sourceAgentId: input.sourceAgentId }, now);
142
+ }
143
+
144
+ return listProviderQuotas({ provider, accountKey, historyLimit: 24, now }).quotas[0]!;
145
+ }
146
+
147
+ function recordProviderQuotaSnapshot(input: { provider: string; accountKey: string; quota: QuotaState; sourceAgentId?: string }, now: number): void {
148
+ const sourceAgentId = input.sourceAgentId && getDb().query("SELECT 1 FROM agents WHERE id = ?").get(input.sourceAgentId)
149
+ ? input.sourceAgentId
150
+ : undefined;
151
+ getDb().query(`
152
+ INSERT INTO quota_snapshots (provider, account_key, agent_id, windows, source, quota_state, captured_at)
153
+ VALUES (?, ?, ?, ?, ?, ?, ?)
154
+ `).run(
155
+ input.provider,
156
+ input.accountKey,
157
+ sourceAgentId ?? null,
158
+ JSON.stringify(input.quota.windows),
159
+ input.quota.source,
160
+ JSON.stringify(input.quota),
161
+ now,
162
+ );
163
+ pruneProviderQuotaSnapshots(input.provider, input.accountKey, now - DAY_MS);
164
+ }
165
+
166
+ function pruneProviderQuotaSnapshots(provider?: string, accountKey?: string, olderThan = Date.now() - DAY_MS): number {
167
+ const result = provider && accountKey
168
+ ? getDb().query("DELETE FROM quota_snapshots WHERE provider = ? AND account_key = ? AND captured_at < ?").run(provider, accountKey, olderThan)
169
+ : getDb().query("DELETE FROM quota_snapshots WHERE captured_at < ?").run(olderThan);
170
+ return result.changes;
171
+ }
172
+
173
+ function listProviderQuotaSnapshots(provider: string, accountKey: string, options: { since?: number; limit?: number } = {}): ProviderQuotaSnapshot[] {
174
+ const limit = Math.max(1, Math.min(options.limit ?? 288, 1000));
175
+ const params: any[] = [normalizeProvider(provider), cleanKey(accountKey)];
176
+ let sql = "SELECT * FROM quota_snapshots WHERE provider = ? AND account_key = ?";
177
+ if (options.since !== undefined) {
178
+ sql += " AND captured_at >= ?";
179
+ params.push(options.since);
180
+ }
181
+ sql += " ORDER BY captured_at ASC, id ASC LIMIT ?";
182
+ params.push(limit);
183
+ return (getDb().query(sql).all(...params) as ProviderQuotaSnapshotRow[]).map(rowToSnapshot);
184
+ }
185
+
186
+ export function listAgentQuotaSnapshotsFromProviderStore(agent: AgentCard, options: { since?: number; limit?: number } = {}) {
187
+ const identity = providerQuotaIdentityForAgent(agent);
188
+ return listProviderQuotaSnapshots(identity.provider, identity.accountKey, options).map((snapshot) => ({
189
+ agentId: agent.id,
190
+ capturedAt: snapshot.capturedAt,
191
+ windows: snapshot.quota.windows,
192
+ source: snapshot.quota.source,
193
+ updatedAt: snapshot.quota.updatedAt,
194
+ }));
195
+ }
196
+
197
+ export function listProviderQuotas(options: { provider?: string; accountKey?: string; since?: number; historyLimit?: number; now?: number } = {}): ProviderQuotaMatrix {
198
+ const historyLimit = Math.max(0, Math.min(options.historyLimit ?? 24, 288));
199
+ const now = options.now ?? Date.now();
200
+ const params: any[] = [];
201
+ let sql = "SELECT * FROM provider_quotas WHERE 1=1";
202
+ if (options.provider) {
203
+ sql += " AND provider = ?";
204
+ params.push(normalizeProvider(options.provider));
205
+ }
206
+ if (options.accountKey) {
207
+ sql += " AND account_key = ?";
208
+ params.push(cleanKey(options.accountKey));
209
+ }
210
+ sql += " ORDER BY provider ASC, account_key ASC";
211
+ const rows = getDb().query(sql).all(...params) as ProviderQuotaRow[];
212
+ return {
213
+ staleAfterMs: PROVIDER_QUOTA_STALE_AFTER_MS,
214
+ quotas: rows.map((row) => rowToRecord(
215
+ row,
216
+ historyLimit > 0 ? listProviderQuotaSnapshots(row.provider, row.account_key, { since: options.since, limit: historyLimit }) : [],
217
+ now,
218
+ )),
219
+ };
220
+ }
package/src/db/schema.ts CHANGED
@@ -292,7 +292,9 @@ export function initDb(path: string = "agent-relay.db"): Database {
292
292
 
293
293
  CREATE TABLE IF NOT EXISTS quota_snapshots (
294
294
  id INTEGER PRIMARY KEY AUTOINCREMENT,
295
- agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
295
+ provider TEXT NOT NULL,
296
+ account_key TEXT NOT NULL,
297
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
296
298
  windows TEXT NOT NULL,
297
299
  source TEXT NOT NULL,
298
300
  quota_state TEXT NOT NULL,
@@ -300,6 +302,19 @@ export function initDb(path: string = "agent-relay.db"): Database {
300
302
  );
301
303
  CREATE INDEX IF NOT EXISTS idx_quota_snapshots_agent_time ON quota_snapshots(agent_id, captured_at DESC);
302
304
 
305
+ CREATE TABLE IF NOT EXISTS provider_quotas (
306
+ provider TEXT NOT NULL,
307
+ account_key TEXT NOT NULL,
308
+ quota_state TEXT,
309
+ source_agent_ids TEXT NOT NULL DEFAULT '[]',
310
+ last_updated_at INTEGER,
311
+ last_attempt_at INTEGER NOT NULL,
312
+ last_error TEXT,
313
+ updated_at INTEGER NOT NULL,
314
+ created_at INTEGER NOT NULL,
315
+ PRIMARY KEY (provider, account_key)
316
+ );
317
+
303
318
  CREATE TABLE IF NOT EXISTS agent_continuations (
304
319
  agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,
305
320
  objective TEXT NOT NULL,
package/src/quota.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { isRecord } from "agent-relay-sdk";
2
- import type { QuotaState, QuotaWindowName, QuotaSource, QuotaUnit } from "./types";
2
+ import type { ProviderQuotaError, QuotaState, QuotaWindowName, QuotaSource, QuotaUnit } from "./types";
3
3
 
4
4
  const QUOTA_SOURCES = new Set<QuotaSource>(["header", "probe", "ingest"]);
5
5
  const QUOTA_UNITS = new Set<QuotaUnit>(["tokens", "requests", "%"]);
@@ -38,3 +38,16 @@ export function quotaStateFromUnknown(value: unknown): QuotaState | undefined {
38
38
  if (windows.length === 0) return undefined;
39
39
  return { windows, source, updatedAt };
40
40
  }
41
+
42
+ export function providerQuotaErrorFromUnknown(value: unknown): ProviderQuotaError | undefined {
43
+ if (!isRecord(value)) return undefined;
44
+ const type = typeof value.type === "string" && value.type.trim() ? value.type.trim() : undefined;
45
+ const message = typeof value.message === "string" && value.message.trim() ? value.message.trim() : undefined;
46
+ if (!type || !message) return undefined;
47
+ const retryAfterMs = numberValue(value.retryAfterMs);
48
+ return {
49
+ type,
50
+ message,
51
+ ...(retryAfterMs !== undefined && retryAfterMs >= 0 ? { retryAfterMs } : {}),
52
+ };
53
+ }
@@ -5,6 +5,7 @@ import { deleteAgentTerminalSession, postAgentAction, postAgentPermissionDecisio
5
5
  import { deleteArtifactById, getArtifactById, getArtifactContent, getArtifacts, headArtifactContent, postArtifact } from "./artifacts";
6
6
  import { deleteAutomationById, getAutomationById, getAutomationRuns, getAutomations, patchAutomation, postAutomation, postAutomationRun } from "./automations";
7
7
  import { deleteCommandById, deleteProviderModelRoute, getProviderConfigRoute, getProviderConfigsRoute, getProvidersRoute, patchProviderModelRoute, postProviderConfigTestRoute, putProviderConfigRoute, putProviderModelRoute } from "./provider-config";
8
+ import { getProviderQuotasRoute, postProviderQuotaRoute } from "./provider-quotas";
8
9
  import { deleteConfigKey, getConfigKey, getConfigKeyHistory, getConfigNamespace, putConfigKey } from "./config";
9
10
  import { deleteInboxDraftRoute, getChatHistoryImportsRoute, getInboxStateRoute, patchInboxThreadState, postChatHistoryImportRoute, putInboxDraft } from "./inbox";
10
11
  import { deleteMemoryRoute, getMemories, getMemoryBrokerInfo, getMemoryById, getMemoryStats, patchMemory, postMemory, postMemoryInject, postMemorySearch } from "./memory";
@@ -87,6 +88,8 @@ const routes: Route[] = [
87
88
  route("GET", "/api/agents", getAgents),
88
89
  route("GET", "/api/agents/find", findAgents),
89
90
  route("POST", "/api/route", postRouteAdvice),
91
+ route("GET", "/api/provider-quotas", getProviderQuotasRoute),
92
+ route("POST", "/api/provider-quotas", postProviderQuotaRoute),
90
93
  route("GET", "/api/agents/spawn/directories", getHostDirectories),
91
94
  route("GET", "/api/agents/:id/chat", getAgentChatHistory),
92
95
  route("GET", "/api/agents/:id/context-snapshots", getAgentContextSnapshots),
@@ -0,0 +1,48 @@
1
+ import { isRecord } from "agent-relay-sdk";
2
+ import { listProviderQuotas, upsertProviderQuota } from "../db";
3
+ import { quotaStateFromUnknown, providerQuotaErrorFromUnknown } from "../quota";
4
+ import { cleanString } from "../validation";
5
+ import { error, json, parseBody, parseQueryInt, type Handler } from "./_shared";
6
+ import type { ProviderQuotaUpdateInput } from "../types";
7
+
8
+ function cleanProviderQuotaUpdate(body: unknown): ProviderQuotaUpdateInput | string {
9
+ if (!isRecord(body)) return "JSON object body required";
10
+ const provider = cleanString(body.provider, "provider", { required: true, max: 80 })!;
11
+ const accountKey = cleanString(body.accountKey, "accountKey", { required: true, max: 240 })!;
12
+ const quota = quotaStateFromUnknown(body.quota);
13
+ const lastAttemptAt = typeof body.lastAttemptAt === "number" && Number.isFinite(body.lastAttemptAt) ? body.lastAttemptAt : undefined;
14
+ const lastError = body.lastError === null ? null : providerQuotaErrorFromUnknown(body.lastError);
15
+ if (!quota && lastError === undefined) return "quota or lastError required";
16
+ const sourceAgentId = cleanString(body.sourceAgentId, "sourceAgentId", { max: 200 });
17
+ return {
18
+ provider,
19
+ accountKey,
20
+ ...(quota ? { quota } : {}),
21
+ ...(lastAttemptAt !== undefined ? { lastAttemptAt } : {}),
22
+ ...(lastError !== undefined ? { lastError } : {}),
23
+ ...(sourceAgentId ? { sourceAgentId } : {}),
24
+ };
25
+ }
26
+
27
+ export const getProviderQuotasRoute: Handler = (req) => {
28
+ const url = new URL(req.url);
29
+ const historyLimit = parseQueryInt(url.searchParams.get("historyLimit") ?? url.searchParams.get("limit"), { min: 0, max: 288, clamp: true });
30
+ if (Number.isNaN(historyLimit)) return error("historyLimit must be an integer between 0 and 288");
31
+ const sinceRaw = url.searchParams.get("since");
32
+ const since = sinceRaw ? Number(sinceRaw) : undefined;
33
+ if (sinceRaw && (!Number.isFinite(since) || since! < 0)) return error("since must be a positive timestamp");
34
+ return json(listProviderQuotas({
35
+ provider: url.searchParams.get("provider") ?? undefined,
36
+ accountKey: url.searchParams.get("accountKey") ?? undefined,
37
+ since,
38
+ historyLimit: historyLimit ?? undefined,
39
+ }));
40
+ };
41
+
42
+ export const postProviderQuotaRoute: Handler = async (req) => {
43
+ const parsed = await parseBody<unknown>(req);
44
+ if (!parsed.ok) return error(parsed.error, parsed.status);
45
+ const input = cleanProviderQuotaUpdate(parsed.body);
46
+ if (typeof input === "string") return error(input);
47
+ return json(upsertProviderQuota(input), 201);
48
+ };
package/src/security.ts CHANGED
@@ -274,6 +274,7 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
274
274
  // Spawn requires the spawn capability (see requiredScopeFor above) — matches the in-service gate.
275
275
  if (pathname === "/api/agents/spawn") return "command:spawn";
276
276
  if (pathname.startsWith("/api/agents")) return method === "GET" ? "agent:read" : "agent:write";
277
+ if (pathname === "/api/provider-quotas") return method === "GET" ? "agent:read" : "quota:write";
277
278
  if (pathname.startsWith("/api/messages") || pathname.startsWith("/api/inbox")) return method === "GET" ? "message:read" : "message:send";
278
279
  if (pathname.startsWith("/api/agent-profiles")) return method === "GET" ? "agent:read" : "agent:write";
279
280
  if (pathname.startsWith("/api/tasks")) return method === "GET" ? "task:read" : "task:write";
package/src/token-db.ts CHANGED
@@ -78,7 +78,7 @@ const BUILT_IN_PROFILES: Array<Omit<TokenProfile, "createdAt" | "updatedAt">> =
78
78
  name: "Provider Agent",
79
79
  description: "Coding-agent runtime access for messages, commands, tasks, and scoped memory reads.",
80
80
  role: "provider",
81
- scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "plans:read", "plans:write", "teams:read", "teams:write", "blackboard:read", "blackboard:write", "project:read", "project:write", "issue:read", "issue:write", "notifications:read", "notifications:write", "insights:write"],
81
+ scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "plans:read", "plans:write", "teams:read", "teams:write", "blackboard:read", "blackboard:write", "project:read", "project:write", "issue:read", "issue:write", "notifications:read", "notifications:write", "insights:write", "quota:write"],
82
82
  ttlSeconds: 24 * 60 * 60,
83
83
  builtIn: true,
84
84
  createdBy: "system",
@@ -88,7 +88,7 @@ const BUILT_IN_PROFILES: Array<Omit<TokenProfile, "createdAt" | "updatedAt">> =
88
88
  name: "Provider Child Agent",
89
89
  description: "Delegated child-agent runtime access, constrained to its parent and spawn request.",
90
90
  role: "provider",
91
- scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "teams:read", "blackboard:read", "blackboard:write", "project:read", "project:write", "issue:read", "issue:write", "notifications:read", "notifications:write", "insights:write"],
91
+ scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "teams:read", "blackboard:read", "blackboard:write", "project:read", "project:write", "issue:read", "issue:write", "notifications:read", "notifications:write", "insights:write", "quota:write"],
92
92
  constraints: { canDelegate: false },
93
93
  ttlSeconds: 2 * 60 * 60,
94
94
  builtIn: true,
@@ -99,7 +99,7 @@ const BUILT_IN_PROFILES: Array<Omit<TokenProfile, "createdAt" | "updatedAt">> =
99
99
  name: "Provider Interactive Agent",
100
100
  description: "User-launched provider runtime access constrained to its own agent and cwd for long interactive sessions.",
101
101
  role: "provider",
102
- scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "teams:read", "blackboard:read", "blackboard:write", "project:read", "project:write", "issue:read", "issue:write", "notifications:read", "notifications:write", "insights:write"],
102
+ scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "teams:read", "blackboard:read", "blackboard:write", "project:read", "project:write", "issue:read", "issue:write", "notifications:read", "notifications:write", "insights:write", "quota:write"],
103
103
  constraints: { terminalAttach: false, logsRead: false, canDelegate: false },
104
104
  ttlSeconds: 30 * 24 * 60 * 60,
105
105
  builtIn: true,
@@ -1,3 +0,0 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{An as t,Bn as n,Hn as r,L as i,Nt as a,P as o,Qn as s,R as c,Rn as l,St as u,Ut as d,Wt as f,Zt as p,a as m,c as h,cn as g,dn as _,en as v,fn as y,h as b,i as x,it as S,jn as C,ln as w,p as T,pn as E,r as D,sn as O,st as k,tn as ee,v as A,wn as te,xt as j,y as ne,yn as M}from"./lucide-react-DI8kTavp.js";import{g as N,r as P,t as F}from"./store-D1K4_xId.js";import{D as I,I as re,M as L,W as R,at as z,b as B,d as ie,f as ae,j as oe,w as se,z as ce}from"./display-DHgdn4vN.js";import{C as V,a as le,i as ue,o as de,t as H}from"./index-DB6XGZy9.js";var U=e(s(),1),fe={online:`came online`,offline:`went offline`,shutdown:`shut down`,restarted:`restarted`,"compaction-requested":`compaction requested`,compacting:`compaction started`,compacted:`context compacted`,"compaction-stalled":`compaction stalled — may need manual intervention`,"clear-requested":`clear requested`,"clearing-context":`clear started`,"context-cleared":`context cleared`,"clear-stalled":`context clear stalled — may need manual intervention`,"shutting-down":`shutting down`},pe=new Set(Object.keys(fe)),me=3e3;function W(e){return new Date(e.occurredAt??e.createdAt).getTime()}function he(e){if(e.kind!==`session`)return null;let t=e.payload?.session;return!t||t.type!==`narration`&&t.type!==`reasoning`&&t.type!==`tool`?null:{id:e.id,kind:t.type,label:typeof t.label==`string`?t.label:void 0,text:e.body,ts:W(e),turnId:typeof t.turnId==`string`?t.turnId:void 0}}function ge(e){if(e.kind!==`session`)return null;let t=e.payload?.session;if(t?.type!==`notice`)return null;let n=typeof t.label==`string`&&t.label?t.label:`compacted`;return{id:`notice-${e.id}`,status:n,timestamp:W(e)}}function _e(e){if(e.kind!==`session`)return null;let t=e.payload?.session;if(t?.type!==`relay-injection`)return null;let n=t.relayInjection&&typeof t.relayInjection==`object`&&!Array.isArray(t.relayInjection)?t.relayInjection:{},r=typeof n.category==`string`&&[`memory`,`instructions`,`tools`,`comms`,`knowledge`,`context`].includes(n.category)?n.category:`context`,i=typeof n.timestamp==`number`&&Number.isFinite(n.timestamp)?n.timestamp:W(e),a=n.detail&&typeof n.detail==`object`&&!Array.isArray(n.detail)?n.detail:{};return{id:e.id,category:r,summary:typeof n.summary==`string`&&n.summary.trim()?n.summary:e.body,detail:a,agentId:typeof n.agentId==`string`&&n.agentId?n.agentId:e.from,timestamp:i}}function ve(e,t){let n=e.trim();for(let e of t){let t=e.trim();if(t){if(n===t)break;n.startsWith(`${t}\n\n`)&&(n=n.slice(t.length+2).trimStart())}}return n.trim()||e.trim()}function ye(e){return e.kind===`system`&&e.from===`system`}var be=new Set([`agent.ready`,`agent.exited`,`agent.spawn_failed`]);function G(e){let t=new Date(e);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}`}function xe(e){let t=e.filter(e=>pe.has(e.status)).slice().sort((e,t)=>e.timestamp-t.timestamp),n=[];for(let e of t)n.some(t=>t.id===e.id)||n.some(t=>t.status===e.status&&Math.abs(t.timestamp-e.timestamp)<=me)||n.push(e);return n}function Se(e,t,n,r=[],i){let a=[];for(let e of r){let t=e.entries.reduce((e,t)=>Math.min(e,R(t.originalCreatedAt)),1/0),n=Number.isFinite(t)?t-1:e.importedAt;a.push({ts:n,entry:{type:`import-boundary`,history:e,timestamp:n}});for(let t of e.entries)a.push({ts:R(t.originalCreatedAt),entry:{type:`imported-message`,history:e,entry:t}})}if(n){let e=new Date(n).getTime();Number.isFinite(e)&&a.push({ts:e,entry:{type:`created`,timestamp:e}})}if(i?.spawnedBy){let e=n?new Date(n).getTime():0,t=Number.isFinite(e)?e:0;a.push({ts:t,entry:{type:`agent-action`,direction:`spawned-by`,agentId:i.spawnedBy.agentId,provider:i.spawnedBy.provider,status:`ready`,timestamp:t}})}let o=i?.spawnedBy?.agentId,s=null,c=1/0;for(let t of e){if(t.kind===`session`||ye(t)||se(t)||!(t.from===`user`||o!==void 0&&t.from===o)||!I(t).trim())continue;let e=W(t);e<c&&(c=e,s=t.id)}let l=new Map,u=new Map,d=new Map;for(let t of e){if(t.kind!==`session`)continue;let e=t.payload?.session;if(e?.type===`narration`&&typeof e.turnId==`string`){let n=u.get(e.turnId)??[];n.push({id:t.id,body:t.body.trim()}),u.set(e.turnId,n)}else if(e?.type===`response`&&typeof e.turnId==`string`){let n=l.get(e.turnId)??[];n.push(t.body.trim()),l.set(e.turnId,n)}}let f=new Map;for(let[e,t]of l){let n=(u.get(e)??[]).map(e=>e.body);f.set(e,t.map(e=>ve(e,n)))}for(let t of e){if(t.kind!==`session`)continue;let e=t.payload?.session;if(e?.type!==`response`||typeof e.turnId!=`string`)continue;let n=ve(t.body,(u.get(e.turnId)??[]).map(e=>e.body));n&&n!==t.body&&d.set(t.id,n)}let p=new Set;for(let[e,t]of f){let n=u.get(e)??[];for(let e of t)for(let t=n.length-1;t>=0;t--){let r=n[t];if(!p.has(r.id)&&ce(e,r.body)){p.add(r.id);break}}}let m=new Map,h=[];for(let t of e){if(ye(t)){let e=typeof t.payload?.kind==`string`?t.payload.kind:``;if(be.has(e)){let n=t.payload,r=typeof n.agentId==`string`?n.agentId:``,i=W(t);if(!r){a.push({ts:i,entry:{type:`notice`,msg:t}});continue}let o=e===`agent.ready`?`ready`:e===`agent.exited`?`exited`:`failed`,s=m.get(r);if(s)s.entry.status=o,typeof n.reason==`string`&&(s.entry.reason=n.reason),s.entry.timestamp=i,s.ts=i;else{let e={ts:i,entry:{type:`agent-action`,direction:`spawned-child`,agentId:r,provider:typeof n.provider==`string`?n.provider:void 0,status:o,reason:typeof n.reason==`string`?n.reason:void 0,timestamp:i}};m.set(r,e),a.push(e)}continue}a.push({ts:W(t),entry:{type:`notice`,msg:t}});continue}let e=ge(t);if(e){h.push(e);continue}let n=he(t);if(n){if(n.kind===`narration`&&p.has(n.id))continue;a.push({ts:n.ts,entry:{type:`activity`,steps:[n],timestamp:n.ts}});continue}let r=_e(t);if(r){a.push({ts:r.timestamp,entry:{type:`relay-injection`,event:r}});continue}if(t.kind===`session`){let e=t.payload?.session;if(e?.type===`response`&&typeof e.turnId==`string`){let e=d.get(t.id);if(e&&e!==t.body){a.push({ts:W(t),entry:{type:`message`,msg:{...t,body:e}}});continue}}}let i=t.id===s?`initial-prompt`:`message`;a.push({ts:W(t),entry:{type:i,msg:t}})}for(let e of xe([...t,...h]))a.push({ts:e.timestamp,entry:{type:`status`,event:e}});let g={created:0,date:1,"import-boundary":2,"imported-message":3,"initial-prompt":4,message:4,activity:4,notice:4,"agent-action":4,"relay-injection":4,status:5};a.sort((e,t)=>{let n=e.ts-t.ts;return Math.abs(n)<3e3&&g[e.entry.type]-g[t.entry.type]||n});let _=[],v=``;for(let{ts:e,entry:t}of a){if(t.type===`created`){_.push(t);continue}let n=G(e);if(n!==v&&(_.push({type:`date`,date:n,timestamp:e}),v=n),t.type===`activity`){let e=_[_.length-1];if(e&&e.type===`activity`&&Te(e,t)){e.steps.push(...t.steps);continue}}_.push(t)}return _}function Ce(e){return e.type===`message`?`msg-${e.msg.id}`:e.type===`initial-prompt`?`initprompt-${e.msg.id}`:e.type===`notice`?`notice-${e.msg.id}`:e.type===`relay-injection`?`relay-injection-${e.event.id}`:e.type===`agent-action`?`agent-action-${e.direction}-${e.agentId}`:e.type===`activity`?`act-${e.steps[0]?.id??e.timestamp}`:e.type===`import-boundary`?`import-${e.history.id}`:e.type===`imported-message`?`import-msg-${e.history.id}-${e.entry.originalMessageId}-${e.entry.position}`:e.type===`status`?`status-${e.event.id}`:e.type===`date`?`date-${e.date}`:`created-${e.timestamp}`}function we(e){return e.type===`message`||e.type===`initial-prompt`||e.type===`notice`?(e.msg.subject?`${e.msg.subject} `:``)+I(e.msg):e.type===`imported-message`?I(e.entry.message):e.type===`activity`?e.steps.map(e=>[e.label,e.text].filter(Boolean).join(` `)).join(`
2
- `):e.type===`relay-injection`?`${e.event.category} ${e.event.summary}\n${JSON.stringify(e.event.detail)}`:``}function Te(e,t){let n=e.steps[e.steps.length-1]?.turnId,r=t.steps[0]?.turnId;return n&&r?n===r:!n&&!r}function Ee(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`}function De(e,t){if(t.startsWith(`/`))return t;let n=`${e.replace(/\/+$/,``)}/${t}`.split(`/`),r=[];for(let e of n)!e||e===`.`||(e===`..`?r.pop():r.push(e));return`/`+r.join(`/`)}function Oe(e){let t=e.replace(/\/+$/,``),n=t.lastIndexOf(`/`);return n>0?t.slice(0,n):`/`}function ke(e,t){return e.filter(e=>e.status===`online`&&re(t,e.baseDir)).sort((e,t)=>oe(t.baseDir).length-oe(e.baseDir).length)[0]||e.find(e=>e.status===`online`)}function Ae(){return typeof window<`u`&&window.matchMedia(`(min-width: 1280px)`).matches}function je(){return typeof window<`u`&&!!window.getSelection()?.toString()}function Me(e){let t=e.payload?.attachments;return Array.isArray(t)?t.filter(e=>!!(e&&typeof e==`object`&&typeof e.artifactId==`string`)):[]}var Ne=[`👍`,`❤️`,`✅`,`👀`];function Pe(e){let t=new Map;for(let n of e.reactions||[]){let e=L(n.emoji),r=t.get(e)??{emoji:e,count:0,mine:!1,actors:[]};r.count+=1,r.mine=r.mine||n.actorId===`user`,r.actors.push(n.actorId),t.set(e,r)}return[...t.values()].sort((e,t)=>t.count-e.count||e.emoji.localeCompare(t.emoji))}function K(e){return se(e)}function Fe(e,t){let n=e.readBy||[],r=e.resolvedToAgent||t||e.to,i=r?n.includes(r):!1,a=n.some(e=>e!==z);if(i||a){let e=i?r:n.find(e=>e!==z);return{label:`read`,title:e?`Read by ${e}`:`Read by agent`,tone:`ok`,icon:E}}if(e.deliveryStatus===`delivered`)return{label:`delivered`,title:`Delivered to agent inbox`,tone:`ok`,icon:E};if(e.deliveryStatus===`queued`)return{label:`queued`,title:`Queued until the target agent is available`,tone:`warn`,icon:p};if(e.deliveryStatus===`failed`||e.deliveryStatus===`dead`){let t=e.deliveryPoisonReason||e.deliveryLastError||e.deliveryStatus;return{label:e.deliveryStatus,title:t,tone:`danger`,icon:O}}return{label:`sent`,title:`Accepted by Relay`,tone:`muted`,icon:y}}function Ie(e){let t=R(e.createdAt);return(e.reactions||[]).reduce((e,t)=>Math.max(e,R(t.updatedAt||t.createdAt)),t)}function Le(e){return e?e.messages.reduce((e,t)=>Math.max(e,Ie(t)),0):0}function Re(e,t,n){return(e-t)*n}function ze(e){if(!e)return 0;let t=0;for(let n of e.messages)B(n)&&(t=Math.max(t,R(n.createdAt)));return t}function q(e){return new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`})}function Be(e){for(let t of e)t.previewUrl&&URL.revokeObjectURL(t.previewUrl)}function Ve(e,t,n,r){if(e?.teamId)return{teamId:e.teamId};let i=[...t];n&&!i.some(e=>e.id===n.id)&&i.push(n);let a=i.filter(e=>e.threadId).reverse(),o=a.find(e=>!!r[P(e.threadId,e.channel)])??a[0];return o?.threadId?{threadId:o.threadId,...o.channel?{channel:o.channel}:{}}:null}var J=n(),He={online:`text-emerald-400`,offline:`text-zinc-400`,shutdown:`text-zinc-400`,restarted:`text-blue-400`,"compaction-requested":`text-zinc-400`,compacting:`text-amber-400`,compacted:`text-amber-400`,"compaction-stalled":`text-red-400`,"clear-requested":`text-zinc-400`,"clearing-context":`text-amber-400`,"context-cleared":`text-amber-400`,"clear-stalled":`text-red-400`,"shutting-down":`text-zinc-400`};function Ue(e){let t=new Date,[n,r,i]=e.split(`-`).map(Number),a=new Date(n,r-1,i);if(e===G(t.getTime()))return`Today`;let o=new Date(t);return o.setDate(o.getDate()-1),e===G(o.getTime())?`Yesterday`:a.toLocaleDateString(void 0,{weekday:`short`,month:`short`,day:`numeric`,year:a.getFullYear()===t.getFullYear()?void 0:`numeric`})}function We({event:e}){return(0,J.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,J.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,J.jsx)(v,{className:l(`w-2 h-2 fill-current`,He[e.status]||`text-zinc-400`)}),(0,J.jsx)(`span`,{children:fe[e.status]||e.status}),(0,J.jsx)(`span`,{className:`text-muted-foreground/60`,children:q(e.timestamp)})]}),(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var Ge={"branch.landed":j,"context.warning":T,"agent.context_threshold":T,"agent.resume_budget_exhausted":T,"agent.rate_limit_resume":c},Ke={"branch.landed":`branch landed`,"context.warning":`context warning`,"agent.context_threshold":`context threshold reached`};function qe(e){return e&&(e.includes(`.`)?e.slice(e.indexOf(`.`)+1):e).replace(/[._-]+/g,` `).trim()||`system signal`}function Y({agentId:e,fallbackLabel:t,className:n}){let r=F(t=>t.agentsById[e])||null,i=F(e=>e.openInboxThread),a=F(e=>e.switchView),o=r?ie(r):t||e;return(0,J.jsxs)(`button`,{type:`button`,className:l(`inline-flex max-w-full items-center gap-1 rounded px-1 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground`,n),onClick:t=>{t.stopPropagation(),i(e),a(`chat`)},title:`Open chat for ${o}`,children:[(0,J.jsx)(V,{agent:r,className:`w-4 h-4 text-[10px]`}),(0,J.jsx)(`span`,{className:`max-w-[160px] truncate`,children:o})]})}function Je({msg:e}){let t=e.payload??{},n=typeof t.sha==`string`?t.sha:``,r=n?n.slice(0,7):``,i=typeof t.author==`string`?t.author:``,a=typeof t.branch==`string`?t.branch:``,o=typeof t.base==`string`?t.base:``,s=t.pushed;return(0,J.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,J.jsxs)(`div`,{className:`flex max-w-full flex-wrap items-center justify-center gap-x-1.5 gap-y-0.5 rounded-full border border-emerald-500/25 bg-emerald-500/10 px-2.5 py-1 text-xs text-emerald-300/90`,children:[(0,J.jsx)(j,{className:`h-3 w-3 shrink-0 text-emerald-400`}),(0,J.jsx)(`span`,{className:`text-emerald-300`,children:`branch landed`}),a&&o&&(0,J.jsxs)(`span`,{className:`font-mono text-emerald-200/70`,title:`${a} → ${o}`,children:[a,(0,J.jsx)(`span`,{className:`opacity-50`,children:` → `}),o]}),r&&(0,J.jsx)(`span`,{className:`rounded bg-emerald-500/15 px-1 font-mono text-emerald-200`,title:n,children:r}),i&&(0,J.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-emerald-300/80`,children:[(0,J.jsx)(`span`,{className:`opacity-70`,children:`by`}),(0,J.jsx)(Y,{agentId:i,className:`text-emerald-300/90 hover:text-emerald-100`})]}),s===!1&&(0,J.jsx)(`span`,{className:`text-amber-300/80`,title:`Local-only land — not pushed to origin`,children:`· local only`}),(0,J.jsx)(`span`,{className:`text-emerald-300/50`,children:q(e.occurredAt??e.createdAt)})]}),(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function Ye({msg:e}){let t=typeof e.payload?.kind==`string`?e.payload.kind:``;if(t===`branch.landed`)return(0,J.jsx)(Je,{msg:e});let n=Ge[t]??o,r=Ke[t]??qe(t);return(0,J.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,J.jsxs)(`div`,{className:`flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-2.5 py-1 text-xs text-muted-foreground`,children:[(0,J.jsx)(n,{className:`h-3 w-3`}),(0,J.jsx)(`span`,{children:r}),(0,J.jsx)(`span`,{className:`text-muted-foreground/60`,children:q(e.occurredAt??e.createdAt)})]}),(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var Xe={"spawned-child":{ready:`spawned`,exited:`spawned · exited`,failed:`spawn failed`},"spawned-by":{ready:`spawned by`,exited:`spawned by`,failed:`spawned by`}},Ze={"spawned-child-ready":i,"spawned-child-exited":S,"spawned-child-failed":O,"spawned-by-ready":C,"spawned-by-exited":C,"spawned-by-failed":C},Qe={"spawned-child-ready":`text-emerald-400`,"spawned-child-exited":`text-zinc-400`,"spawned-child-failed":`text-red-400`,"spawned-by-ready":`text-blue-400`,"spawned-by-exited":`text-blue-400`,"spawned-by-failed":`text-blue-400`};function $e({entry:e}){let t=`${e.direction}-${e.status}`,n=Xe[e.direction]?.[e.status]??e.direction,r=Ze[t]??o,i=Qe[t]??`text-muted-foreground`,a=e.status===`failed`;return(0,J.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,J.jsxs)(`div`,{className:l(`flex items-center gap-1.5 rounded-full border bg-muted/30 px-2.5 py-1 text-xs`,a?`border-red-500/30`:`border-border`),children:[(0,J.jsx)(r,{className:l(`h-3 w-3 shrink-0`,i)}),(0,J.jsx)(`span`,{className:`text-muted-foreground`,children:n}),(0,J.jsx)(Y,{agentId:e.agentId}),e.reason&&a&&(0,J.jsxs)(`span`,{className:`text-red-400/80 max-w-[120px] truncate`,title:e.reason,children:[`— `,e.reason]}),(0,J.jsx)(`span`,{className:`text-muted-foreground/60 shrink-0`,children:q(e.timestamp)})]}),(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var et={memory:M,instructions:a,tools:m,comms:o,knowledge:te,context:f},X={memory:`border-emerald-500/25 bg-emerald-500/10 text-emerald-300`,instructions:`border-sky-500/25 bg-sky-500/10 text-sky-300`,tools:`border-amber-500/25 bg-amber-500/10 text-amber-300`,comms:`border-violet-500/25 bg-violet-500/10 text-violet-300`,knowledge:`border-cyan-500/25 bg-cyan-500/10 text-cyan-300`,context:`border-zinc-500/25 bg-zinc-500/10 text-zinc-300`};function tt(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function nt({entry:e}){let[t,n]=(0,U.useState)(!1),r=e.event,i=et[r.category]??A;return(0,J.jsx)(`div`,{className:`flex justify-center py-2 my-1`,children:(0,J.jsxs)(`div`,{className:l(`w-full max-w-[85%] md:max-w-[75%] rounded-lg border px-2.5 py-1.5 text-xs shadow-sm`,X[r.category]??X.context),children:[(0,J.jsxs)(`button`,{type:`button`,className:`flex w-full min-w-0 items-center gap-1.5 text-left`,onClick:()=>n(e=>!e),children:[(0,J.jsx)(i,{className:`h-3.5 w-3.5 shrink-0`}),(0,J.jsx)(`span`,{className:`shrink-0 font-medium`,children:r.category}),(0,J.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-foreground/80`,children:r.summary}),(0,J.jsx)(`span`,{className:`shrink-0 text-muted-foreground/60`,children:q(r.timestamp)}),(0,J.jsx)(w,{className:l(`h-3.5 w-3.5 shrink-0 transition-transform`,t&&`rotate-90`)})]}),t&&(0,J.jsx)(`pre`,{className:`mt-2 max-h-72 overflow-auto rounded-md border border-border/60 bg-background/75 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground whitespace-pre-wrap break-words`,children:tt(r.detail)})]})})}function rt({blockedLabel:e,onInterrupt:t}){return e?(0,J.jsx)(`div`,{className:`flex justify-start mb-3 animate-in fade-in slide-in-from-bottom-1 duration-150`,children:(0,J.jsxs)(`div`,{className:`flex h-9 items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-red-500/30 px-4`,children:[(0,J.jsx)(O,{className:`w-3.5 h-3.5 text-red-400`}),(0,J.jsxs)(`span`,{className:`text-xs text-red-300`,children:[`blocked: `,e]})]})}):(0,J.jsx)(`div`,{className:`flex justify-start mb-3 animate-in fade-in slide-in-from-bottom-1 duration-150`,children:(0,J.jsxs)(`div`,{className:`flex h-9 items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-foreground/10 px-4`,children:[(0,J.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,J.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_infinite]`}),(0,J.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.2s_infinite]`}),(0,J.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.4s_infinite]`})]}),(0,J.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`working`}),t&&(0,J.jsxs)(`button`,{onClick:t,title:`Interrupt`,className:`ml-1 flex items-center gap-1 rounded-full px-1.5 py-0.5 text-xs text-muted-foreground hover:text-red-300 hover:bg-red-500/10 transition-colors`,children:[(0,J.jsx)(ee,{className:`w-3.5 h-3.5`}),`Stop`]})]})})}var Z=`agent-relay:chat-hide-tools`,Q=(()=>{let e=new Set,t=(()=>{try{return window.localStorage.getItem(Z)===`on`}catch{return!1}})(),n=()=>e.forEach(e=>e());return typeof window<`u`&&window.addEventListener(`storage`,e=>{e.key===Z&&(t=e.newValue===`on`,n())}),{subscribe(t){return e.add(t),()=>e.delete(t)},get:()=>t,toggle(){t=!t;try{window.localStorage.setItem(Z,t?`on`:`off`)}catch{}n()}}})();function it(){return[(0,U.useSyncExternalStore)(Q.subscribe,Q.get,()=>!1),Q.toggle]}function at({steps:e,showReasoning:t,issueRepo:n}){let[r,i]=it(),a=t?e:e.filter(e=>e.kind!==`reasoning`);if(!a.length)return null;let o=a.filter(e=>e.kind===`tool`).length;return(0,J.jsx)(`div`,{className:`flex justify-start mb-2`,children:(0,J.jsxs)(`div`,{className:`max-w-[85%] md:max-w-[75%] min-w-0 space-y-1.5`,children:[a.map(e=>e.kind===`narration`?(0,J.jsx)(H,{text:e.text,issueRepo:n,className:`text-sm leading-relaxed break-words text-foreground/90 min-w-0`},e.id):e.kind===`reasoning`?(0,J.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground/80`,children:[(0,J.jsx)(M,{className:`w-3.5 h-3.5 mt-0.5 shrink-0 opacity-70`}),(0,J.jsx)(H,{text:e.text,issueRepo:n,className:`italic break-words min-w-0`})]},e.id):r?null:(0,J.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground`,children:[(0,J.jsx)(b,{className:`w-3 h-3 mt-0.5 shrink-0 opacity-70`}),(0,J.jsxs)(`span`,{className:`min-w-0 break-words`,children:[(0,J.jsx)(`span`,{className:`font-medium`,children:e.label||`tool`}),e.text&&e.text.trim()&&e.text.trim()!==(e.label??``).trim()?(0,J.jsxs)(`span`,{className:`opacity-70`,children:[` — `,e.text]}):null]})]},e.id)),o>0&&(0,J.jsxs)(`button`,{onClick:i,className:`flex items-center gap-1 text-[11px] text-muted-foreground/50 hover:text-muted-foreground transition-colors text-left`,children:[(0,J.jsx)(w,{className:l(`w-3 h-3 shrink-0 transition-transform`,!r&&`rotate-90`)}),(0,J.jsx)(`span`,{children:r?`show ${o} tool step${o===1?``:`s`}`:`hide tool steps`})]})]})})}function ot({date:e}){return(0,J.jsxs)(`div`,{className:`flex items-center justify-center gap-3 py-3 my-1`,children:[(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,J.jsx)(`span`,{className:`text-xs font-medium text-muted-foreground`,children:Ue(e)}),(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function st({timestamp:e}){let t=new Date(e).toLocaleString(void 0,{month:`short`,day:`numeric`,year:`numeric`,hour:`2-digit`,minute:`2-digit`});return(0,J.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,J.jsxs)(`span`,{className:`text-xs text-muted-foreground/60`,children:[`created `,t]}),(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function ct({history:e}){let t=e.sourceAgentLabel||e.sourcePeerId,n=e.entries.length;return(0,J.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,J.jsxs)(`div`,{className:`flex items-center gap-1.5 rounded-full border border-dashed border-border bg-muted/30 px-2 py-1 text-[11px] text-muted-foreground`,children:[(0,J.jsx)(u,{className:`h-3 w-3`}),(0,J.jsxs)(`span`,{children:[`forked history from `,t]}),(0,J.jsxs)(`span`,{className:`text-muted-foreground/60`,children:[n,` `,n===1?`entry`:`entries`]})]}),(0,J.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function lt({entry:e,sourceLabel:t,issueRepo:n}){let r=e.message,i=I(r),a=e.originalFrom===z;return(0,J.jsx)(`div`,{className:l(`flex mb-3 opacity-80`,a?`justify-end`:`justify-start`),children:(0,J.jsx)(`div`,{className:`max-w-[85%] md:max-w-[75%]`,children:(0,J.jsxs)(`div`,{className:l(`rounded-2xl border border-dashed px-3.5 py-2 text-sm select-text`,a?`border-primary/25 bg-primary/10 text-foreground rounded-br-sm`:`border-border bg-card/70 rounded-bl-sm`),children:[(0,J.jsxs)(`div`,{className:l(`mb-1 text-[10px] uppercase tracking-normal`,a?`text-primary/70`:`text-muted-foreground`),children:[`forked history · `,a?`you`:t||e.originalFrom]}),(0,J.jsx)(H,{text:i,rawBody:r.body,issueRepo:n,className:`leading-relaxed`}),(0,J.jsxs)(`p`,{className:l(`mt-1 flex items-center justify-end gap-1.5 text-xs`,a?`text-primary/60`:`text-muted-foreground`),children:[(0,J.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.originalMessageId]}),(0,J.jsx)(`span`,{children:q(e.originalCreatedAt)})]})]})})})}function ut({msg:e,issueRepo:t}){let n=I(e),r=q(e.occurredAt??e.createdAt);return(0,J.jsx)(`div`,{"data-msg-id":e.id,className:`mb-3`,children:(0,J.jsxs)(`div`,{className:`rounded-xl border border-primary/30 bg-primary/[0.06] px-3.5 py-2.5 shadow-sm`,children:[(0,J.jsxs)(`div`,{className:`mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-primary/80`,children:[(0,J.jsx)(A,{className:`h-3.5 w-3.5`}),(0,J.jsx)(`span`,{children:`Initial prompt`}),(0,J.jsx)(`span`,{className:`ml-auto text-[10px] font-normal normal-case text-muted-foreground/60`,children:r})]}),(0,J.jsx)(H,{text:n,rawBody:e.body,issueRepo:t,className:`text-sm leading-relaxed text-foreground`})]})})}function dt({scrollEl:e,timeline:t}){let[n,r]=(0,U.useState)(!0),[i,a]=(0,U.useState)(null),[o,s]=(0,U.useState)(null),[c,u]=(0,U.useState)(!1),d=(0,U.useMemo)(()=>{for(let e=t.length-1;e>=0;e--){let n=t[e];if(!(!n||n.type!==`message`&&n.type!==`initial-prompt`)&&n.msg.from===`user`)return{id:n.msg.id,body:I(n.msg)}}return null},[t]);if((0,U.useEffect)(()=>{if(!d||!e){r(!0);return}let t=null,n=null,i=()=>{let i=e.querySelector(`[data-msg-id="${d.id}"]`);if(!i){t?.disconnect(),t=null,n=null,r(!1);return}let a=i.getBoundingClientRect(),o=e.getBoundingClientRect();u(a.top<o.top),i!==n&&(t?.disconnect(),n=i,t=new IntersectionObserver(([e])=>{e&&r(e.isIntersecting)},{root:e,threshold:0}),t.observe(i))};return i(),e.addEventListener(`scroll`,i,{passive:!0}),()=>{t?.disconnect(),e.removeEventListener(`scroll`,i)}},[d?.id,e]),n||!d||!d.body.trim())return null;let f=i?.id===d.id?i.value:!1,p=o?.id===d.id?o.value:!1;function m(){!e||!d||e.querySelector(`[data-msg-id="${d.id}"]`)?.scrollIntoView({behavior:`smooth`,block:`center`})}let h=c?`↑`:`↓`;return p?(0,J.jsx)(`div`,{className:`flex shrink-0 bg-card/45 border-b border-border`,children:(0,J.jsxs)(`button`,{type:`button`,className:`w-full flex items-center justify-center gap-1.5 py-1 text-[11px] text-primary/60 hover:text-primary transition-colors`,onClick:()=>s({id:d.id,value:!1}),children:[(0,J.jsx)(`span`,{children:`Your prompt`}),(0,J.jsx)(_,{className:`w-3 h-3`})]})}):(0,J.jsxs)(`section`,{className:`flex shrink-0 flex-col border-b border-border bg-card/45 px-3 py-2`,children:[(0,J.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] text-muted-foreground mb-0.5`,children:[(0,J.jsx)(`span`,{className:`font-medium text-primary/70`,children:`Your prompt`}),(0,J.jsxs)(`button`,{type:`button`,className:`ml-auto hover:text-foreground transition-colors`,onClick:m,children:[h,` scroll to original`]}),(0,J.jsx)(`button`,{type:`button`,className:`hover:text-foreground transition-colors`,onClick:()=>s({id:d.id,value:!0}),title:`Minimize`,children:(0,J.jsx)(g,{className:`w-3 h-3`})})]}),(0,J.jsx)(`p`,{className:l(`text-sm text-foreground leading-snug cursor-pointer`,!f&&`line-clamp-2`),onClick:()=>a({id:d.id,value:!f}),children:d.body})]})}function ft(){return(0,U.useSyncExternalStore)(e=>N.subscribe(e),()=>N.getPlayingKey(),()=>null)}var pt={id:z};function mt({msg:e,peer:t}){return(0,J.jsx)(ut,{msg:e,issueRepo:F(e=>e.agentsById[t]?.issueRepo)})}function ht({agent:e,onClick:t}){let n=!!(t&&e);return(0,J.jsx)(`button`,{type:`button`,title:e?e.label||e.name||e.id:void 0,onClick:t,disabled:!n,className:l(`shrink-0 self-end transition-opacity`,n?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,J.jsx)(V,{agent:e})})}function gt({attachment:e,outbound:t}){let n=e.kind===`image`,i=e.title||e.artifactId,[o,s]=(0,U.useState)(null),[c,u]=(0,U.useState)(null),[f,p]=(0,U.useState)(!1),[m,h]=(0,U.useState)(!1);(0,U.useEffect)(()=>{let t=!1,n=null;async function i(){p(!0),u(null),s(null);try{let i=await r(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(i),s(n)}catch(e){t||u(e.message)}finally{t||p(!1)}}return i(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function g(){if(!o)return;let e=document.createElement(`a`);e.href=o,e.download=i,document.body.appendChild(e),e.click(),e.remove()}return n?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`button`,{type:`button`,onClick:()=>o&&h(!0),disabled:!o,className:`group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative`,children:o?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`img`,{src:o,alt:i,loading:`lazy`,className:`block max-h-72 max-w-full object-contain`}),(0,J.jsx)(`span`,{className:`absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors`,children:(0,J.jsx)(D,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,J.jsxs)(`span`,{className:l(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,c?`text-red-400`:`text-muted-foreground`),children:[f?(0,J.jsx)(k,{className:`h-4 w-4 animate-spin`}):(0,J.jsx)(O,{className:`h-4 w-4`}),c||`Loading image`]})}),o&&(0,J.jsx)(ue,{open:m,onOpenChange:h,children:(0,J.jsxs)(le,{className:`max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden`,showCloseButton:!1,children:[(0,J.jsx)(de,{className:`sr-only`,children:i}),(0,J.jsxs)(`div`,{className:`relative flex items-center justify-center min-h-[200px]`,children:[(0,J.jsx)(`img`,{src:o,alt:i,className:`max-w-[93vw] max-h-[90vh] object-contain`}),(0,J.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1`,children:[(0,J.jsx)(`button`,{type:`button`,onClick:g,className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Download`,children:(0,J.jsx)(d,{className:`w-4 h-4`})}),(0,J.jsx)(`button`,{type:`button`,onClick:()=>h(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,J.jsx)(x,{className:`w-4 h-4`})})]})]})]})})]}):(0,J.jsxs)(`button`,{type:`button`,onClick:g,disabled:!o,className:l(`mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70`,t?`border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15`:`border-border bg-background hover:bg-muted`),children:[f?(0,J.jsx)(k,{className:`h-4 w-4 shrink-0 animate-spin`}):c?(0,J.jsx)(O,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,J.jsx)(a,{className:`h-4 w-4 shrink-0`}),(0,J.jsx)(`span`,{className:l(`min-w-0 flex-1 truncate`,c&&`text-red-400`),children:c||i}),(0,J.jsx)(d,{className:`h-3.5 w-3.5 shrink-0`})]})}function $({open:e,onToggle:t,onReact:n}){return(0,J.jsxs)(`div`,{className:`relative shrink-0`,children:[(0,J.jsx)(`button`,{type:`button`,title:`Add reaction`,onClick:t,className:l(`inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground`,e?`opacity-100`:`opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto`),children:(0,J.jsx)(ne,{className:`h-3 w-3`})}),e&&(0,J.jsx)(`div`,{className:`absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md`,children:Ne.map(e=>(0,J.jsx)(`button`,{type:`button`,title:`React ${e}`,onClick:()=>n(e),className:`inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted`,children:e},e))})]})}function _t({msg:e,peer:n}){let[r,i]=(0,U.useState)(!1),a=F(e=>e.agentsById),o=F(e=>e.openAgentDetail),s=e.from===n,c=(s?e.to:e.from)||``,u=a[s?n:c]?.issueRepo??a[n]?.issueRepo,d=!c||c===`broadcast`||c.includes(`:`),f=I(e),p=q(e.occurredAt??e.createdAt),m=s?t:C,h=a[e.from??``]??null;function g(e){e.stopPropagation(),h&&o(h)}return(0,J.jsx)(`div`,{"data-msg-id":e.id,className:`flex justify-center mb-1.5`,children:(0,J.jsxs)(`div`,{className:`w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1`,children:[(0,J.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs cursor-pointer select-none`,onClick:()=>i(e=>!e),children:[(0,J.jsx)(w,{className:l(`h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform`,r&&`rotate-90`)}),(0,J.jsx)(`button`,{type:`button`,title:h?h.label||h.name||h.id:void 0,disabled:!h,onClick:g,className:l(`shrink-0 transition-opacity`,h?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,J.jsx)(V,{agent:h,className:`!w-4 !h-4 !rounded-sm opacity-70`})}),(0,J.jsx)(m,{className:`h-3 w-3 shrink-0 text-muted-foreground/70`}),(0,J.jsxs)(`span`,{className:`shrink-0 text-muted-foreground`,children:[`Message `,s?`to`:`from`]}),d?(0,J.jsx)(`span`,{className:`shrink-0 font-medium text-muted-foreground`,children:ae(c,a)}):(0,J.jsx)(Y,{agentId:c,className:`shrink-0 px-0.5 text-foreground/80`}),!r&&(0,J.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-muted-foreground/50`,children:f}),(0,J.jsx)(`span`,{className:`ml-auto shrink-0 text-muted-foreground/40`,children:p})]}),r&&(0,J.jsx)(`div`,{className:`mt-1.5 pl-5`,children:(0,J.jsx)(H,{text:f,rawBody:e.body,issueRepo:u,className:`text-sm leading-relaxed text-foreground/90`})})]})})}var vt=(0,U.memo)(function({msg:e,peer:t,showAvatar:n=!1,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:a}){if(e.from!==`user`&&e.to!==`user`)return(0,J.jsx)(_t,{msg:e,peer:t});let o=e.kind===`session`?e.payload?.session:void 0;if(o?.type===`prompt`&&o?.origin===`provider`)return(0,J.jsx)(mt,{msg:e,peer:t});let s=e.from===z,c=F(e=>e.reactToMessage),u=F(e=>e.voiceTtsEnabled),d=ft(),f=F(e=>{let n=e.agentsById[t]?.meta?.cwd;return typeof n==`string`?n:``}),p=F(e=>e.agentsById[t]?.issueRepo),m=F(e=>e.agentsById[t]??null),g=F(e=>e.agentsById.user??null)??pt,_=F(e=>e.openAgentDetail),[v,y]=(0,U.useState)(!1),b=(0,U.useRef)(null),x=(0,U.useRef)(null),S=(0,U.useRef)(!1),C=I(e),w=q(e.occurredAt??e.createdAt),T=Me(e),E=Pe(e),D=s?Fe(e,t):null,O=D?.icon,k=d===String(e.id),A=u&&!s&&!K(e)&&C.trim().length>0;function te(t){t.stopPropagation(),k?N.bargeIn():N.speak(e.body,String(e.id))}(0,U.useEffect)(()=>{if(!v)return;function e(e){je()||b.current&&!b.current.contains(e.target)&&y(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[v]);function j(e){return De(f||`/`,e)}function ne(e,t){let n=j(e);r?.(n,t)}function M(e,t,n){let r=j(e);i?.(r,t,n)}function P(t){c(e,t),y(!1)}function re(e){e.button===0&&(x.current={x:e.clientX,y:e.clientY},S.current=!1)}function L(e){let t=x.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(S.current=!0)}function R(t){if(!(K(e)||s)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(S.current||je()){S.current=!1;return}y(e=>!e)}}let B=n&&!K(e);function ie(){!s&&m&&_(m)}return(0,J.jsxs)(`div`,{"data-msg-id":e.id,className:l(`group/msg flex mb-3 items-end`,s?`justify-end`:`justify-start`,B&&`gap-1.5`),children:[!s&&B&&(0,J.jsx)(ht,{agent:m,onClick:ie}),(0,J.jsxs)(`div`,{ref:b,className:l(`relative`,B?`max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]`:`max-w-[85%] md:max-w-[75%]`),children:[A&&(0,J.jsx)(`button`,{type:`button`,title:k?`Stop playback`:`Play aloud`,onClick:te,className:l(`absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition`,k?`border-primary/50 text-primary opacity-100`:`border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100`),children:k?(0,J.jsx)(ee,{className:`h-3.5 w-3.5 animate-pulse`}):(0,J.jsx)(h,{className:`h-3.5 w-3.5`})}),(0,J.jsxs)(`div`,{className:l(`rounded-2xl px-3.5 py-2 text-sm select-text`,!s&&!K(e)&&`cursor-pointer`,s?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:re,onPointerMove:L,onClick:R,children:[e.subject&&(0,J.jsx)(`p`,{className:l(`text-xs font-semibold mb-1`,s?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,J.jsx)(H,{text:C,rawBody:e.body,issueRepo:p,className:`leading-relaxed`,onOpenPath:ne,onPreviewPath:M,onPreviewPathEnd:a,resolvePathTitle:(e,t)=>`${j(e)}${t?`:${t}`:``}`}),T.map(t=>(0,J.jsx)(gt,{attachment:t,outbound:s},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,J.jsxs)(`p`,{className:l(`text-xs mt-1 flex items-center gap-1.5`,s?`justify-end text-primary-foreground/60`:`justify-end text-muted-foreground`),children:[(0,J.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.id]}),(0,J.jsx)(`span`,{children:w}),D&&O&&(0,J.jsxs)(`span`,{className:l(`inline-flex items-center gap-1`,D.tone===`ok`&&`text-emerald-300`,D.tone===`warn`&&`text-amber-200`,D.tone===`danger`&&`text-red-200`),title:D.title,children:[(0,J.jsx)(O,{className:`h-3 w-3`}),D.label]})]})]}),!K(e)&&E.length>0&&(0,J.jsxs)(`div`,{className:l(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,s?`justify-end pr-2`:`justify-start pl-2`),children:[!s&&(0,J.jsx)($,{open:v,onToggle:()=>y(e=>!e),onReact:P}),E.map(e=>(0,J.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>P(e.emoji),className:l(`inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors`,e.mine?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background hover:bg-muted text-foreground`),children:[(0,J.jsx)(`span`,{children:e.emoji}),e.count>1&&(0,J.jsx)(`span`,{children:e.count})]},e.emoji))]}),!K(e)&&!s&&E.length===0&&(0,J.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,J.jsx)($,{open:v,onToggle:()=>y(e=>!e),onReact:P})})]}),s&&B&&(0,J.jsx)(ht,{agent:g})]})});export{Se as C,Le as S,we as T,ke as _,st as a,Be as b,lt as c,nt as d,We as f,ze as g,Ee as h,rt as i,ut as l,Re as m,at as n,ot as o,dt as p,$e as r,ct as s,vt as t,Ye as u,Oe as v,Ce as w,Ae as x,Ve as y};
3
- //# sourceMappingURL=MessageBubble-DzhG1x8z.js.map
@@ -1,2 +0,0 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{Bn as t,Ct as n,Qn as r,Rn as i,Vn as a,a as o,en as s,on as c,p as l}from"./lucide-react-DI8kTavp.js";import{t as u}from"./store-D1K4_xId.js";var d=e(r(),1);function f(e){let t=u(e=>e.showError),[n,r]=(0,d.useState)(!1),[i,o]=(0,d.useState)(null),[s,c]=(0,d.useState)(!1);async function l(){if(!(!e||s)){c(!0);try{o(await a(`POST`,`/agents/`+encodeURIComponent(e)+`/terminal-session`)),r(!0)}catch(e){t(`Terminal Failed`,e.message)}finally{c(!1)}}}function f(t){if(t){r(!0);return}r(!1);let n=i;o(null),e&&n?.mode===`guest`&&a(`DELETE`,`/agents/`+encodeURIComponent(e)+`/terminal-session/`+encodeURIComponent(n.session)).catch(()=>{})}return{terminalOpen:n,terminalTarget:i,terminalOpening:s,openTerminal:l,closeTerminal:f}}var p=t(),m={idle:{label:`idle`,tip:`Branch worktree — nothing to land yet.`,className:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,Icon:s},changes:{label:`changes`,tip:`Unlanded work in this branch — use "Mark workspace ready" to land it.`,className:`bg-amber-500/10 text-amber-400 border-amber-500/20`,Icon:n},ready:{label:`ready`,tip:`Handed off — the relay auto-merge will land it (~2 min sweep).`,className:`bg-blue-500/10 text-blue-400 border-blue-500/20`,Icon:c},steward:{label:`steward`,tip:`Under reconciliation — a steward is landing it. No action needed.`,className:`bg-orange-500/10 text-orange-400 border-orange-500/20`,Icon:o},blocked:{label:`blocked`,tip:`Escalated — the auto-merge/steward path is stuck. Needs human attention.`,className:`bg-red-500/10 text-red-400 border-red-500/20`,Icon:l}};function h({state:e,onClick:t,iconOnly:n=!1,className:r}){let a=m[e],{Icon:o}=a,s=!!t,c=s?`${a.tip} Click to open this workspace.`:a.tip;return(0,p.jsxs)(`span`,{role:s?`button`:void 0,tabIndex:s?0:void 0,title:c,onClick:t,onKeyDown:s?e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),t?.(e))}:void 0,className:i(`inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium leading-none`,a.className,s&&`cursor-pointer hover:brightness-125 focus:outline-none focus:ring-1 focus:ring-current`,r),children:[(0,p.jsx)(o,{className:`h-2.5 w-2.5 shrink-0`}),!n&&(0,p.jsx)(`span`,{children:a.label})]})}export{f as n,h as t};
2
- //# sourceMappingURL=branch-state-badge-B7T81Oct.js.map