agent-relay-server 0.105.2 → 0.107.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 (82) hide show
  1. package/docs/openapi.json +29 -1
  2. package/package.json +5 -3
  3. package/public/assets/{MessageBubble-CPYQQLDI.js → MessageBubble-PEdvVh_m.js} +2 -2
  4. package/public/assets/{MessageBubble-CPYQQLDI.js.map → MessageBubble-PEdvVh_m.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-B7xZt5DI.js → PlanGraphPanel-DSts7Rtq.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-B7xZt5DI.js.map → PlanGraphPanel-DSts7Rtq.js.map} +1 -1
  7. package/public/assets/{activity-UKQrQ8_0.js → activity-BcYf8YNO.js} +2 -2
  8. package/public/assets/{activity-UKQrQ8_0.js.map → activity-BcYf8YNO.js.map} +1 -1
  9. package/public/assets/{agent-profiles-Zr_5nNEJ.js → agent-profiles-CuTQtmV6.js} +2 -2
  10. package/public/assets/{agent-profiles-Zr_5nNEJ.js.map → agent-profiles-CuTQtmV6.js.map} +1 -1
  11. package/public/assets/{agents-FdrfmJsj.js → agents-CgRFJyks.js} +2 -2
  12. package/public/assets/{agents-FdrfmJsj.js.map → agents-CgRFJyks.js.map} +1 -1
  13. package/public/assets/{analytics-VximqXQ9.js → analytics-C3OTFIXo.js} +18 -18
  14. package/public/assets/{analytics-VximqXQ9.js.map → analytics-C3OTFIXo.js.map} +1 -1
  15. package/public/assets/{automation-DlEcJvgu.js → automation-CH9YGYoE.js} +2 -2
  16. package/public/assets/{automation-DlEcJvgu.js.map → automation-CH9YGYoE.js.map} +1 -1
  17. package/public/assets/{chat-D1bunuI-.js → chat-BLcm2Aht.js} +2 -2
  18. package/public/assets/{chat-D1bunuI-.js.map → chat-BLcm2Aht.js.map} +1 -1
  19. package/public/assets/{formatted-body-impl-4v9SkUD1.js → formatted-body-impl-BJTthVHg.js} +2 -2
  20. package/public/assets/{formatted-body-impl-4v9SkUD1.js.map → formatted-body-impl-BJTthVHg.js.map} +1 -1
  21. package/public/assets/{index-Vmhw442d.js → index-3ydeNO0c.js} +6 -6
  22. package/public/assets/index-3ydeNO0c.js.map +1 -0
  23. package/public/assets/{insights-CntgHcTp.js → insights-DklrIhb6.js} +2 -2
  24. package/public/assets/{insights-CntgHcTp.js.map → insights-DklrIhb6.js.map} +1 -1
  25. package/public/assets/{maintenance-Dw7frQtu.js → maintenance-FAAMGpuZ.js} +2 -2
  26. package/public/assets/{maintenance-Dw7frQtu.js.map → maintenance-FAAMGpuZ.js.map} +1 -1
  27. package/public/assets/{managed-agents-Bj--bFmZ.js → managed-agents-CVca-s3t.js} +2 -2
  28. package/public/assets/{managed-agents-Bj--bFmZ.js.map → managed-agents-CVca-s3t.js.map} +1 -1
  29. package/public/assets/{markdown-preview-impl-DGLCocU8.js → markdown-preview-impl-BPW4OQo9.js} +2 -2
  30. package/public/assets/{markdown-preview-impl-DGLCocU8.js.map → markdown-preview-impl-BPW4OQo9.js.map} +1 -1
  31. package/public/assets/{memory-yj2MIxUA.js → memory-BzuDTHC5.js} +2 -2
  32. package/public/assets/{memory-yj2MIxUA.js.map → memory-BzuDTHC5.js.map} +1 -1
  33. package/public/assets/{messages-BT5mL7iz.js → messages-_W24PNvT.js} +2 -2
  34. package/public/assets/{messages-BT5mL7iz.js.map → messages-_W24PNvT.js.map} +1 -1
  35. package/public/assets/{orchestrators-BVDAhrPk.js → orchestrators-Du6I_n8i.js} +2 -2
  36. package/public/assets/{orchestrators-BVDAhrPk.js.map → orchestrators-Du6I_n8i.js.map} +1 -1
  37. package/public/assets/{overview-DlqJIqYK.js → overview-Dyvm1qpT.js} +2 -2
  38. package/public/assets/{overview-DlqJIqYK.js.map → overview-Dyvm1qpT.js.map} +1 -1
  39. package/public/assets/{pairs-DQ4tNCo3.js → pairs-B8x1RCNd.js} +2 -2
  40. package/public/assets/{pairs-DQ4tNCo3.js.map → pairs-B8x1RCNd.js.map} +1 -1
  41. package/public/assets/{prompt-settings-DlsqOP0C.js → prompt-settings-xVXmvxB5.js} +2 -2
  42. package/public/assets/{prompt-settings-DlsqOP0C.js.map → prompt-settings-xVXmvxB5.js.map} +1 -1
  43. package/public/assets/{registry-Cw2fdWrn.js → registry-ypiICauZ.js} +2 -2
  44. package/public/assets/{registry-Cw2fdWrn.js.map → registry-ypiICauZ.js.map} +1 -1
  45. package/public/assets/{security-ucr7IT1V.js → security-BfOQ6T5r.js} +2 -2
  46. package/public/assets/{security-ucr7IT1V.js.map → security-BfOQ6T5r.js.map} +1 -1
  47. package/public/assets/{select-D8UWCtYZ.js → select-Dx8Ro-A3.js} +2 -2
  48. package/public/assets/{select-D8UWCtYZ.js.map → select-Dx8Ro-A3.js.map} +1 -1
  49. package/public/assets/settings-87clTx4N.js +6 -0
  50. package/public/assets/settings-87clTx4N.js.map +1 -0
  51. package/public/assets/{tasks-jRUvxUd5.js → tasks-1Kvp53wh.js} +2 -2
  52. package/public/assets/{tasks-jRUvxUd5.js.map → tasks-1Kvp53wh.js.map} +1 -1
  53. package/public/assets/{teams-Dn0yggD-.js → teams-DJWp4AKi.js} +2 -2
  54. package/public/assets/{teams-Dn0yggD-.js.map → teams-DJWp4AKi.js.map} +1 -1
  55. package/public/assets/{terminal-viewer-impl-fLly20IP.js → terminal-viewer-impl-DgMuRAe_.js} +2 -2
  56. package/public/assets/{terminal-viewer-impl-fLly20IP.js.map → terminal-viewer-impl-DgMuRAe_.js.map} +1 -1
  57. package/public/assets/types-DorDDvEb.js.map +1 -1
  58. package/public/assets/{user-avatar-BWLUHt2e.js → user-avatar-CfAUS3PZ.js} +2 -2
  59. package/public/assets/{user-avatar-BWLUHt2e.js.map → user-avatar-CfAUS3PZ.js.map} +1 -1
  60. package/public/assets/{work-queue-BAnNY0xE.js → work-queue-D-Otxd9u.js} +2 -2
  61. package/public/assets/{work-queue-BAnNY0xE.js.map → work-queue-D-Otxd9u.js.map} +1 -1
  62. package/public/index.html +1 -1
  63. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  64. package/runner/plugins/claude/monitors/relay-monitor.provisioned.mjs +15 -3
  65. package/runner/src/adapter.ts +14 -3
  66. package/src/branch-landed.ts +76 -1
  67. package/src/connectors.ts +116 -4
  68. package/src/db/provider-quotas.ts +6 -0
  69. package/src/mcp-egress-tools.ts +3 -2
  70. package/src/notification-router.ts +6 -1
  71. package/src/notification-types.ts +2 -0
  72. package/src/notify.ts +4 -4
  73. package/src/quota-band-transition.ts +6 -0
  74. package/src/routes/commands.ts +17 -1
  75. package/src/routes/index.ts +2 -1
  76. package/src/routes/provider-quotas.ts +13 -1
  77. package/src/security.ts +2 -0
  78. package/src/services/transient-agent-reaper.ts +16 -1
  79. package/src/token-db.ts +3 -3
  80. package/public/assets/index-Vmhw442d.js.map +0 -1
  81. package/public/assets/settings-Dvf_ps1B.js +0 -6
  82. package/public/assets/settings-Dvf_ps1B.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { isRecord } from "agent-relay-sdk";
2
- import { acquireProviderQuotaLease, getOrchestrator, getProviderQuotaLease, listProviderQuotas, releaseProviderQuotaLease, upsertProviderQuota } from "../db";
2
+ import { acquireProviderQuotaLease, getOrchestrator, getProviderQuotaLease, getProviderQuotaHistory, listProviderQuotas, releaseProviderQuotaLease, upsertProviderQuota } from "../db";
3
3
  import { emitRelayEvent } from "../events";
4
4
  import { quotaStateFromUnknown, providerQuotaErrorFromUnknown } from "../quota";
5
5
  import { authContextFromRequest } from "../services/auth-context";
@@ -79,6 +79,18 @@ export const getProviderQuotaAdvisoryRoute: Handler = () => {
79
79
  return json({ advisories });
80
80
  };
81
81
 
82
+ // #609 — per-provider 30-day quota history (consumed by #607 analytics chart + #608 sparkline).
83
+ export const getProviderQuotaHistoryRoute: Handler = (req) => {
84
+ const url = new URL(req.url);
85
+ const provider = url.searchParams.get("provider");
86
+ if (!provider) return error("provider query parameter required");
87
+ const sinceMs = url.searchParams.get("sinceMs") ? Number(url.searchParams.get("sinceMs")) : undefined;
88
+ if (sinceMs !== undefined && (!Number.isFinite(sinceMs) || sinceMs < 0)) return error("sinceMs must be a positive timestamp");
89
+ const accountKey = url.searchParams.get("accountKey") ?? undefined;
90
+ const limit = parseQueryInt(url.searchParams.get("limit"), { min: 1, max: 10_000, clamp: true });
91
+ return json(getProviderQuotaHistory(provider, { sinceMs, accountKey, limit: limit ?? undefined }));
92
+ };
93
+
82
94
  export const postProviderQuotaRoute: Handler = async (req) => {
83
95
  const parsed = await parseBody<unknown>(req);
84
96
  if (!parsed.ok) return error(parsed.error, parsed.status);
package/src/security.ts CHANGED
@@ -199,6 +199,7 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
199
199
  if (pathname.startsWith("/api/pairs")) return method === "GET" ? "pairs:read" : "pairs:write";
200
200
  if (pathname === "/api/provider-quota-leases/acquire" || pathname === "/api/provider-quota-leases/release") return "quota:write";
201
201
  if (pathname === "/api/provider-quotas/advisory") return "agent:read";
202
+ if (pathname === "/api/provider-quotas/history") return "agent:read";
202
203
  if (pathname === "/api/provider-quotas") return method === "GET" ? "agent:read" : "quota:write";
203
204
  if (pathname === "/api/routing-policy") return method === "GET" ? "agent:read" : "settings:write:routing";
204
205
  if (pathname === "/api/teams/config") return method === "GET" ? "teams:read" : "settings:write:teams";
@@ -286,6 +287,7 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
286
287
  if (pathname.startsWith("/api/agents")) return method === "GET" ? "agent:read" : "agent:write";
287
288
  if (pathname === "/api/provider-quota-leases/acquire" || pathname === "/api/provider-quota-leases/release") return "quota:write";
288
289
  if (pathname === "/api/provider-quotas/advisory") return "agent:read";
290
+ if (pathname === "/api/provider-quotas/history") return "agent:read";
289
291
  if (pathname === "/api/provider-quotas") return method === "GET" ? "agent:read" : "quota:write";
290
292
  // Per-provider quota config (#605): reads are agent:read (the orchestrator
291
293
  // collector reads its config each tick); writes are settings:write:provider.
@@ -37,6 +37,19 @@ function defaultCommitsAhead(ws: WorkspaceRecord): number | null {
37
37
  }
38
38
  }
39
39
 
40
+ // #800 — A transient agent is protected from reaping ONLY by one of three evidence-based states:
41
+ // it is not idle (still working), it is not ready (mid-startup/teardown), or it carries a REAL
42
+ // provider block. `isProviderBlocked` reads the agent's own `meta.providerState`, which is set
43
+ // exclusively from hard evidence — an HTTP 429 / StopFailure hook, the scraped CLI usage-limit
44
+ // modal, an approval gate, or a model-unavailable hold (see runner rate-limit.ts / control-server).
45
+ // It is NEVER derived from a quota-utilization number. The reaper deliberately does not read quota
46
+ // utilization at all, so a provider merely sitting at high 7d/5h utilization can neither cause nor
47
+ // prevent a reap. This keeps the false "high-util ⇒ blocked ⇒ reaped" chain (#800) impossible by
48
+ // construction: utilization is not an input to this decision.
49
+ function reapProtected(agent: ReturnType<typeof listAgents>[number]): boolean {
50
+ return agent.status !== "idle" || !agent.ready || isProviderBlocked(agent);
51
+ }
52
+
40
53
  let _commitsAheadFn: (ws: WorkspaceRecord) => number | null = defaultCommitsAhead;
41
54
 
42
55
  export function overrideCommitsAheadForTests(fn: (ws: WorkspaceRecord) => number | null): void {
@@ -109,7 +122,9 @@ export function reapTransientAgents(): Record<string, unknown> {
109
122
  // A held agent (rate-limit hold #286, or any provider-blocked state) reads as
110
123
  // idle+ready but is NOT done — reaping it would kill work that's just waiting
111
124
  // for the limit to reset. Treat it as not-idle; the clock restarts on resume.
112
- if (agent.status !== "idle" || !agent.ready || isProviderBlocked(agent)) { transientTracker.delete(agent.id); continue; }
125
+ // #800 `reapProtected` is the single home for that decision: it honors only
126
+ // evidence-based block states, never a high quota-utilization number.
127
+ if (reapProtected(agent)) { transientTracker.delete(agent.id); continue; }
113
128
  const entry = transientTracker.get(agent.id) ?? { firstIdleAt: now };
114
129
  transientTracker.set(agent.id, entry);
115
130
  const idleForMs = now - entry.firstIdleAt;
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", "schedule:write", "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"],
81
+ scope: ["agent:read", "agent:write", "message:read", "message:send", "schedule:write", "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", "channel:egress", "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", "schedule:write", "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"],
91
+ scope: ["agent:read", "agent:write", "message:read", "message:send", "schedule:write", "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", "channel:egress", "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", "schedule:write", "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"],
102
+ scope: ["agent:read", "agent:write", "message:read", "message:send", "schedule:write", "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", "channel:egress", "insights:write", "quota:write"],
103
103
  constraints: { terminalAttach: false, logsRead: false, canDelegate: false },
104
104
  ttlSeconds: 30 * 24 * 60 * 60,
105
105
  builtIn: true,