agent-relay-server 0.94.1 → 0.94.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Relay API",
5
- "version": "0.94.1",
5
+ "version": "0.94.3",
6
6
  "description": "Real-time message bus for inter-agent communication. Agent-first: this spec is designed for machine consumption — agents can self-discover the full API surface via GET /api/spec.",
7
7
  "license": {
8
8
  "name": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.94.1",
3
+ "version": "0.94.3",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -34,7 +34,7 @@
34
34
  "CONTRIBUTING.md"
35
35
  ],
36
36
  "dependencies": {
37
- "agent-relay-sdk": "0.2.74",
37
+ "agent-relay-sdk": "0.2.75",
38
38
  "ajv": "^8.20.0"
39
39
  },
40
40
  "scripts": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
3
  "description": "Thin Agent Relay runner bridge for Claude Code",
4
- "version": "0.94.1",
4
+ "version": "0.94.3",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
- import { credentialAccountKey } from "agent-relay-sdk/provider-quota";
4
+ import { resolveStableCodexQuotaIdentity } from "agent-relay-sdk/provider-quota";
5
5
  import { quotaStateFromProbeMetrics } from "agent-relay-sdk/context-probe";
6
6
  import { errMessage, isRecord, stringValue } from "agent-relay-sdk";
7
7
  import { codexWhamQuotaEnabled } from "./config";
@@ -119,11 +119,12 @@ async function readCodexWhamAuth(path: string): Promise<CodexWhamAuth | undefine
119
119
  const tokens = isRecord(parsed) && isRecord(parsed.tokens) ? parsed.tokens : undefined;
120
120
  const accessToken = stringValue(tokens?.access_token);
121
121
  const accountId = stringValue(tokens?.account_id);
122
- if (!accessToken || !accountId) return undefined;
122
+ const identity = resolveStableCodexQuotaIdentity({ authPayload: parsed });
123
+ if (!accessToken || !accountId || !identity) return undefined;
123
124
  return {
124
125
  accessToken,
125
126
  accountId,
126
- accountKey: credentialAccountKey(accountId),
127
+ accountKey: identity.accountKey,
127
128
  };
128
129
  } catch {
129
130
  return undefined;
@@ -23,6 +23,7 @@ import { ensureContinuationArchiveSearchSchema } from "./continuation-archives.t
23
23
  import { ensureCrashReportsSchema } from "./crash-reports.ts";
24
24
  import { ensureTerminalEventsSchema } from "./terminal-events.ts";
25
25
  import { seedBundledProvisioningAssets } from "./provisioning-registry.ts";
26
+ import { provisioningAssetsColumnNames, provisioningAssetsNeedsRebuild, rebuildProvisioningAssetsTable } from "./provisioning-assets-migration.ts";
26
27
  import { normalizeReactionEmoji } from "./mappers.ts";
27
28
  import { normalizeProviderQuotaAccountKeys } from "./provider-quotas.ts";
28
29
  import { projectRootsMissingIssueRepo, setProjectIssueRepo } from "./projects.ts";
@@ -584,9 +585,7 @@ export function applyMigrations(): void {
584
585
  UNIQUE(kind, name, provider)
585
586
  )
586
587
  `);
587
- getDb().run("CREATE INDEX IF NOT EXISTS idx_provisioning_assets_kind ON provisioning_assets(kind, enabled)");
588
- const provisioningAssetCols = getDb().query("PRAGMA table_info(provisioning_assets)").all() as any[];
589
- const provisioningAssetColNames = provisioningAssetCols.map((c: any) => c.name);
588
+ let provisioningAssetColNames = provisioningAssetsColumnNames();
590
589
  const provisioningAssetAddColumns = [
591
590
  ["capability_id", "capability_id TEXT"],
592
591
  ["source_type", "source_type TEXT"],
@@ -629,11 +628,12 @@ export function applyMigrations(): void {
629
628
  getDb().run("UPDATE provisioning_assets SET imported_at = coalesce(imported_at, created_at)");
630
629
  });
631
630
  backfillCapabilities(existingCapabilities);
631
+ if (provisioningAssetsNeedsRebuild()) rebuildProvisioningAssetsTable();
632
+ getDb().run("CREATE INDEX IF NOT EXISTS idx_provisioning_assets_kind ON provisioning_assets(kind, enabled)");
632
633
  getDb().run("CREATE INDEX IF NOT EXISTS idx_provisioning_assets_capability ON provisioning_assets(capability_id, provider, enabled)");
633
634
  getDb().run(`
634
635
  CREATE UNIQUE INDEX IF NOT EXISTS idx_provisioning_assets_capability_provider
635
636
  ON provisioning_assets(capability_id, provider)
636
- WHERE capability_id IS NOT NULL
637
637
  `);
638
638
  seedBundledProvisioningAssets();
639
639
  if (!agentColNames.includes("spawned_by")) {
@@ -1,4 +1,5 @@
1
1
  import { DAY_MS, QUOTA_HISTORY_RETENTION_MS } from "../config";
2
+ import { credentialAccountKey } from "agent-relay-sdk/provider-quota";
2
3
  import { parseJson } from "../utils";
3
4
  import { getDb } from "./connection.ts";
4
5
  import { deriveProviderQuotaBurn } from "./provider-quota-burn.ts";
@@ -98,6 +99,7 @@ function quotaRowsForProvider(provider: string): ProviderQuotaRow[] {
98
99
  function supersededAccountKeys(provider: string, accountKey: string, rawAccountKey: string, sourceAgentId: string | undefined): string[] {
99
100
  const candidates = new Set<string>();
100
101
  if (rawAccountKey && rawAccountKey !== accountKey) candidates.add(rawAccountKey);
102
+ if (provider === "codex" && accountKey.startsWith("account:")) candidates.add(credentialAccountKey(accountKey.slice("account:".length)));
101
103
  const host = hostFromAccountKey(accountKey) ?? sourceAgentHost(sourceAgentId);
102
104
  if (host) {
103
105
  candidates.add(`host:${host}:${provider}`);
@@ -607,9 +609,7 @@ export function normalizeProviderQuotaAccountKeys(now = Date.now()): number {
607
609
  let changes = 0;
608
610
  for (const row of getDb().query("SELECT * FROM provider_quotas").all() as ProviderQuotaRow[]) {
609
611
  const accountKey = canonicalProviderQuotaAccountKey(row.provider, row.account_key);
610
- if (accountKey !== row.account_key) {
611
- changes += mergeProviderQuotaRows(row.provider, accountKey, [row.account_key], now);
612
- }
612
+ changes += mergeProviderQuotaRows(row.provider, accountKey, accountKey === row.account_key ? supersededAccountKeys(row.provider, accountKey, row.account_key, undefined) : [row.account_key], now);
613
613
  }
614
614
  changes += pruneUnstableProviderQuotaRecords();
615
615
  changes += pruneProviderQuotaRecords(undefined, undefined, now - DAY_MS);
@@ -0,0 +1,85 @@
1
+ import { getDb } from "./connection.ts";
2
+
3
+ export function provisioningAssetsColumnNames(): string[] {
4
+ return (getDb().query("PRAGMA table_info(provisioning_assets)").all() as Array<{ name: string }>).map((col) => col.name);
5
+ }
6
+
7
+ export function provisioningAssetsNeedsRebuild(): boolean {
8
+ const columns = getDb().query("PRAGMA table_info(provisioning_assets)").all() as Array<{ name: string; notnull: number }>;
9
+ const capabilityId = columns.find((col) => col.name === "capability_id");
10
+ if (!capabilityId || capabilityId.notnull !== 1) return true;
11
+
12
+ const indexes = getDb().query("PRAGMA index_list(provisioning_assets)").all() as Array<{ name: string; unique: number; partial: number }>;
13
+ for (const index of indexes) {
14
+ if (index.unique !== 1 || index.partial !== 0) continue;
15
+ const indexColumns = (getDb().query(`PRAGMA index_info(${JSON.stringify(index.name)})`).all() as Array<{ name: string }>).map((col) => col.name);
16
+ if (indexColumns.join(",") === "capability_id,provider") return false;
17
+ }
18
+
19
+ return true;
20
+ }
21
+
22
+ export function rebuildProvisioningAssetsTable(): void {
23
+ getDb()
24
+ .transaction(() => {
25
+ getDb().run("DROP TABLE IF EXISTS provisioning_assets_rebuild");
26
+ getDb().run(`
27
+ CREATE TABLE provisioning_assets_rebuild (
28
+ id TEXT PRIMARY KEY,
29
+ capability_id TEXT NOT NULL REFERENCES provisioning_capabilities(id) ON DELETE CASCADE,
30
+ kind TEXT NOT NULL,
31
+ name TEXT NOT NULL,
32
+ provider TEXT NOT NULL DEFAULT 'any',
33
+ definition TEXT NOT NULL,
34
+ enabled INTEGER NOT NULL DEFAULT 1,
35
+ source_type TEXT,
36
+ source_url TEXT,
37
+ source_ref TEXT,
38
+ resolved_ref TEXT,
39
+ source_subpath TEXT,
40
+ version_constraint TEXT,
41
+ approval_status TEXT NOT NULL DEFAULT 'approved',
42
+ validation_status TEXT NOT NULL DEFAULT 'valid',
43
+ validation_errors TEXT NOT NULL DEFAULT '[]',
44
+ provenance TEXT NOT NULL DEFAULT '{}',
45
+ imported_at INTEGER,
46
+ created_at INTEGER NOT NULL,
47
+ updated_at INTEGER NOT NULL,
48
+ UNIQUE(kind, name, provider),
49
+ UNIQUE(capability_id, provider)
50
+ )
51
+ `);
52
+ getDb().run(`
53
+ INSERT INTO provisioning_assets_rebuild (
54
+ id, capability_id, kind, name, provider, definition, enabled,
55
+ source_type, source_url, source_ref, resolved_ref, source_subpath, version_constraint,
56
+ approval_status, validation_status, validation_errors, provenance, imported_at,
57
+ created_at, updated_at
58
+ )
59
+ SELECT
60
+ id,
61
+ capability_id,
62
+ kind,
63
+ name,
64
+ provider,
65
+ definition,
66
+ enabled,
67
+ source_type,
68
+ source_url,
69
+ source_ref,
70
+ resolved_ref,
71
+ source_subpath,
72
+ version_constraint,
73
+ coalesce(approval_status, 'approved'),
74
+ coalesce(validation_status, 'valid'),
75
+ coalesce(validation_errors, '[]'),
76
+ coalesce(provenance, '{}'),
77
+ coalesce(imported_at, created_at),
78
+ created_at,
79
+ updated_at
80
+ FROM provisioning_assets
81
+ `);
82
+ getDb().run("DROP TABLE provisioning_assets");
83
+ getDb().run("ALTER TABLE provisioning_assets_rebuild RENAME TO provisioning_assets");
84
+ })();
85
+ }
package/src/db/schema.ts CHANGED
@@ -990,29 +990,15 @@ export function initDb(path: string = "agent-relay.db"): Database {
990
990
 
991
991
  CREATE TABLE IF NOT EXISTS provisioning_assets (
992
992
  id TEXT PRIMARY KEY,
993
- capability_id TEXT NOT NULL REFERENCES provisioning_capabilities(id) ON DELETE CASCADE,
994
993
  kind TEXT NOT NULL,
995
994
  name TEXT NOT NULL,
996
995
  provider TEXT NOT NULL DEFAULT 'any',
997
996
  definition TEXT NOT NULL,
998
997
  enabled INTEGER NOT NULL DEFAULT 1,
999
- source_type TEXT,
1000
- source_url TEXT,
1001
- source_ref TEXT,
1002
- resolved_ref TEXT,
1003
- source_subpath TEXT,
1004
- version_constraint TEXT,
1005
- approval_status TEXT NOT NULL DEFAULT 'approved',
1006
- validation_status TEXT NOT NULL DEFAULT 'valid',
1007
- validation_errors TEXT NOT NULL DEFAULT '[]',
1008
- provenance TEXT NOT NULL DEFAULT '{}',
1009
- imported_at INTEGER,
1010
998
  created_at INTEGER NOT NULL,
1011
999
  updated_at INTEGER NOT NULL,
1012
- UNIQUE(kind, name, provider),
1013
- UNIQUE(capability_id, provider)
1000
+ UNIQUE(kind, name, provider)
1014
1001
  );
1015
- CREATE INDEX IF NOT EXISTS idx_provisioning_assets_kind ON provisioning_assets(kind, enabled);
1016
1002
  `);
1017
1003
 
1018
1004
  ensureContinuationArchiveSearchSchema();