agent-relay-server 0.62.3 → 0.63.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 (160) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +2 -2
  3. package/public/assets/{MessageBubble-DRrxfjyT.js → MessageBubble-Cl6ufPHw.js} +2 -2
  4. package/public/assets/MessageBubble-Cl6ufPHw.js.map +1 -0
  5. package/public/assets/{PlanGraphPanel-Dp2eYazz.js → PlanGraphPanel-BnfjmkQ8.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-Dp2eYazz.js.map → PlanGraphPanel-BnfjmkQ8.js.map} +1 -1
  7. package/public/assets/{activity--t0U2yzq.js → activity-aFxvGLtd.js} +2 -2
  8. package/public/assets/{activity--t0U2yzq.js.map → activity-aFxvGLtd.js.map} +1 -1
  9. package/public/assets/{agent-profiles-BMtJYzxn.js → agent-profiles-B4mleUDz.js} +2 -2
  10. package/public/assets/{agent-profiles-BMtJYzxn.js.map → agent-profiles-B4mleUDz.js.map} +1 -1
  11. package/public/assets/{agents-Ck9AePlc.js → agents-DW5djToQ.js} +2 -2
  12. package/public/assets/{agents-Ck9AePlc.js.map → agents-DW5djToQ.js.map} +1 -1
  13. package/public/assets/{analytics-CzfK387L.js → analytics-CRi6thtg.js} +2 -2
  14. package/public/assets/{analytics-CzfK387L.js.map → analytics-CRi6thtg.js.map} +1 -1
  15. package/public/assets/{automation-Cgw8vtE8.js → automation-Do4OpauI.js} +2 -2
  16. package/public/assets/{automation-Cgw8vtE8.js.map → automation-Do4OpauI.js.map} +1 -1
  17. package/public/assets/{branch-state-badge-DnEEPtOP.js → branch-state-badge-D3W3w7zz.js} +2 -2
  18. package/public/assets/{branch-state-badge-DnEEPtOP.js.map → branch-state-badge-D3W3w7zz.js.map} +1 -1
  19. package/public/assets/{channels-Dc3L2MQs.js → channels-BytdR9NS.js} +2 -2
  20. package/public/assets/{channels-Dc3L2MQs.js.map → channels-BytdR9NS.js.map} +1 -1
  21. package/public/assets/{chat-Fc6iJ7sa.js → chat-CvaQ2czL.js} +2 -2
  22. package/public/assets/{chat-Fc6iJ7sa.js.map → chat-CvaQ2czL.js.map} +1 -1
  23. package/public/assets/{connectors-Dc2ttGWs.js → connectors-BLQDp8Bc.js} +2 -2
  24. package/public/assets/{connectors-Dc2ttGWs.js.map → connectors-BLQDp8Bc.js.map} +1 -1
  25. package/public/assets/{display-DzNvOu1j.js → display-B06I5sqa.js} +3 -3
  26. package/public/assets/display-B06I5sqa.js.map +1 -0
  27. package/public/assets/{formatted-body-impl-Bg-gx7rG.js → formatted-body-impl-0Lf8e1Ic.js} +2 -2
  28. package/public/assets/{formatted-body-impl-Bg-gx7rG.js.map → formatted-body-impl-0Lf8e1Ic.js.map} +1 -1
  29. package/public/assets/{index-BeDncjN4.js → index-C3su8ezq.js} +6 -6
  30. package/public/assets/{index-BeDncjN4.js.map → index-C3su8ezq.js.map} +1 -1
  31. package/public/assets/{insights-c08aW-7n.js → insights-64uExH1E.js} +2 -2
  32. package/public/assets/{insights-c08aW-7n.js.map → insights-64uExH1E.js.map} +1 -1
  33. package/public/assets/{integrations-BwpgcqnF.js → integrations-HyC1EaIp.js} +2 -2
  34. package/public/assets/{integrations-BwpgcqnF.js.map → integrations-HyC1EaIp.js.map} +1 -1
  35. package/public/assets/{maintenance-CTKP0kWB.js → maintenance-EVBpKH07.js} +2 -2
  36. package/public/assets/{maintenance-CTKP0kWB.js.map → maintenance-EVBpKH07.js.map} +1 -1
  37. package/public/assets/{managed-agents-BofCxo8d.js → managed-agents-BLZPxGuw.js} +2 -2
  38. package/public/assets/{managed-agents-BofCxo8d.js.map → managed-agents-BLZPxGuw.js.map} +1 -1
  39. package/public/assets/{markdown-preview-impl-CQ-6BIb8.js → markdown-preview-impl-Bg6rCWYN.js} +2 -2
  40. package/public/assets/{markdown-preview-impl-CQ-6BIb8.js.map → markdown-preview-impl-Bg6rCWYN.js.map} +1 -1
  41. package/public/assets/{memory-DvfXjMoi.js → memory-Bjr8bgjj.js} +2 -2
  42. package/public/assets/{memory-DvfXjMoi.js.map → memory-Bjr8bgjj.js.map} +1 -1
  43. package/public/assets/{messages-C9VsPzZ_.js → messages-CRYw1F4z.js} +2 -2
  44. package/public/assets/{messages-C9VsPzZ_.js.map → messages-CRYw1F4z.js.map} +1 -1
  45. package/public/assets/{orchestrators-MffVueeE.js → orchestrators-D-co1vdp.js} +2 -2
  46. package/public/assets/{orchestrators-MffVueeE.js.map → orchestrators-D-co1vdp.js.map} +1 -1
  47. package/public/assets/{overview-BSVajefM.js → overview-Ddy5czri.js} +2 -2
  48. package/public/assets/{overview-BSVajefM.js.map → overview-Ddy5czri.js.map} +1 -1
  49. package/public/assets/{pairs-B5711-Cs.js → pairs-CFVM8uHL.js} +2 -2
  50. package/public/assets/{pairs-B5711-Cs.js.map → pairs-CFVM8uHL.js.map} +1 -1
  51. package/public/assets/{security-DkUwK9Vj.js → security-Df4_76bh.js} +2 -2
  52. package/public/assets/{security-DkUwK9Vj.js.map → security-Df4_76bh.js.map} +1 -1
  53. package/public/assets/{select-DrM2RRiT.js → select-Cq3BBnL9.js} +2 -2
  54. package/public/assets/{select-DrM2RRiT.js.map → select-Cq3BBnL9.js.map} +1 -1
  55. package/public/assets/{settings-DdKCb-vy.js → settings-DOLEYXZL.js} +2 -2
  56. package/public/assets/{settings-DdKCb-vy.js.map → settings-DOLEYXZL.js.map} +1 -1
  57. package/public/assets/{store-iTiheuTD.js → store-C1E8jmNE.js} +4 -4
  58. package/public/assets/store-C1E8jmNE.js.map +1 -0
  59. package/public/assets/{tasks-e1y1PFfO.js → tasks-BcsmPi9_.js} +2 -2
  60. package/public/assets/{tasks-e1y1PFfO.js.map → tasks-BcsmPi9_.js.map} +1 -1
  61. package/public/assets/{teams-BUcxoGC_.js → teams-BQrV3eKI.js} +2 -2
  62. package/public/assets/{teams-BUcxoGC_.js.map → teams-BQrV3eKI.js.map} +1 -1
  63. package/public/assets/{terminal-viewer-impl-CmZjQOB2.js → terminal-viewer-impl-Dle04qpH.js} +2 -2
  64. package/public/assets/{terminal-viewer-impl-CmZjQOB2.js.map → terminal-viewer-impl-Dle04qpH.js.map} +1 -1
  65. package/public/assets/{work-queue-CXBQx43n.js → work-queue-BLhTLg4o.js} +2 -2
  66. package/public/assets/{work-queue-CXBQx43n.js.map → work-queue-BLhTLg4o.js.map} +1 -1
  67. package/public/assets/{workspaces-D2EfZh9a.js → workspaces-4NCNTmRQ.js} +3 -3
  68. package/public/assets/{workspaces-D2EfZh9a.js.map → workspaces-4NCNTmRQ.js.map} +1 -1
  69. package/public/index.html +3 -3
  70. package/src/agent-ref.ts +8 -6
  71. package/src/automations/constants.ts +12 -0
  72. package/src/automations/cron.ts +129 -0
  73. package/src/automations/crud.ts +84 -0
  74. package/src/automations/dispatch.ts +275 -0
  75. package/src/automations/index.ts +5 -0
  76. package/src/automations/reconcile.ts +245 -0
  77. package/src/automations/storage.ts +181 -0
  78. package/src/automations/types.ts +16 -0
  79. package/src/automations/validation.ts +140 -0
  80. package/src/automations.ts +1 -1045
  81. package/src/db/activity.ts +1 -1
  82. package/src/db/agent-predicates.ts +9 -0
  83. package/src/db/agent-search.ts +2 -2
  84. package/src/db/agents.ts +13 -41
  85. package/src/db/artifacts.ts +8 -8
  86. package/src/db/channel-identity.ts +38 -0
  87. package/src/db/channels.ts +37 -61
  88. package/src/db/continuation-archives.ts +4 -4
  89. package/src/db/delivery.ts +7 -12
  90. package/src/db/inbox.ts +1 -1
  91. package/src/db/integrations.ts +4 -4
  92. package/src/db/issue-lifecycle.ts +6 -6
  93. package/src/db/mappers.ts +1 -2
  94. package/src/db/merge-lease.ts +3 -3
  95. package/src/db/message-claims.ts +16 -0
  96. package/src/db/message-queries.ts +8 -0
  97. package/src/db/message-reads.ts +26 -9
  98. package/src/db/messages.ts +17 -41
  99. package/src/db/notification-subscriptions.ts +2 -2
  100. package/src/db/orchestrators.ts +4 -4
  101. package/src/db/pair-maintenance.ts +9 -0
  102. package/src/db/pairs.ts +10 -18
  103. package/src/db/plans.ts +1 -1
  104. package/src/db/policy-runtime.ts +14 -0
  105. package/src/db/project-entries.ts +1 -1
  106. package/src/db/projects.ts +2 -2
  107. package/src/db/shared.ts +7 -0
  108. package/src/db/stats.ts +2 -2
  109. package/src/db/task-core.ts +18 -0
  110. package/src/db/task-maintenance.ts +24 -0
  111. package/src/db/tasks.ts +5 -17
  112. package/src/db/teams.ts +3 -3
  113. package/src/db/workspaces.ts +6 -6
  114. package/src/issue-tracker/github.ts +3 -3
  115. package/src/maintenance/constants.ts +35 -0
  116. package/src/maintenance/events.ts +32 -0
  117. package/src/maintenance/index.ts +7 -0
  118. package/src/maintenance/jobs.ts +340 -0
  119. package/src/maintenance/orphaned-sessions.ts +86 -0
  120. package/src/maintenance/scheduler.ts +231 -0
  121. package/src/maintenance/teams.ts +65 -0
  122. package/src/maintenance/types.ts +31 -0
  123. package/src/maintenance/workspaces.ts +251 -0
  124. package/src/maintenance.ts +1 -1099
  125. package/src/mcp/identity.ts +131 -0
  126. package/src/mcp/index.ts +208 -0
  127. package/src/mcp/tools-messaging.ts +459 -0
  128. package/src/mcp/tools-spawn.ts +145 -0
  129. package/src/mcp/tools-workspace.ts +227 -0
  130. package/src/mcp/types.ts +24 -0
  131. package/src/mcp/validation.ts +104 -0
  132. package/src/mcp.ts +1 -1319
  133. package/src/notification-router.ts +2 -2
  134. package/src/notification-rules.ts +2 -2
  135. package/src/project-ingest.ts +3 -3
  136. package/src/project-instructions.ts +1 -1
  137. package/src/provider-config-store.ts +3 -3
  138. package/src/provider-state.ts +1 -1
  139. package/src/reviewer-pipeline.ts +1 -1
  140. package/src/self-resume.ts +3 -7
  141. package/src/services/agent-action.ts +2 -2
  142. package/src/services/issue-activity.ts +0 -4
  143. package/src/services/issue-associations.ts +2 -2
  144. package/src/services/issue-lifecycle.ts +7 -7
  145. package/src/services/managed-running.ts +1 -1
  146. package/src/services/parity-harness.ts +3 -3
  147. package/src/services/plan-graph.ts +3 -3
  148. package/src/services/send-message.ts +16 -12
  149. package/src/services/shutdown-agent.ts +2 -2
  150. package/src/services/team-templates.ts +2 -2
  151. package/src/services/team.ts +2 -2
  152. package/src/settings/modules.ts +1 -1
  153. package/src/settings/registry.ts +3 -3
  154. package/src/spawn-command.ts +1 -1
  155. package/src/sse.ts +0 -4
  156. package/src/workspace-actions.ts +1 -1
  157. package/src/workspace-probe.ts +3 -7
  158. package/public/assets/MessageBubble-DRrxfjyT.js.map +0 -1
  159. package/public/assets/display-DzNvOu1j.js.map +0 -1
  160. package/public/assets/store-iTiheuTD.js.map +0 -1
@@ -98,7 +98,7 @@ export interface RecordIssueActivityInput {
98
98
  metadata?: Record<string, unknown>;
99
99
  }
100
100
 
101
- export interface IssueActivityAgentSummary {
101
+ interface IssueActivityAgentSummary {
102
102
  agentId: string;
103
103
  agentName?: string;
104
104
  agentKind?: string;
@@ -0,0 +1,9 @@
1
+ import type { AgentCard } from "../types";
2
+
3
+ export function isDeliveryAgent(agent: AgentCard): boolean {
4
+ return agent.status !== "offline" &&
5
+ agent.id !== "user" &&
6
+ agent.id !== "system" &&
7
+ agent.kind !== "channel" &&
8
+ agent.meta?.kind !== "channel";
9
+ }
@@ -105,9 +105,9 @@ export interface AgentSearchSort {
105
105
  limit?: number;
106
106
  }
107
107
 
108
- export const AGENT_SEARCH_MAX_LIMIT = 200;
108
+ const AGENT_SEARCH_MAX_LIMIT = 200;
109
109
 
110
- export function searchValues(value: string | string[] | undefined): string[] {
110
+ function searchValues(value: string | string[] | undefined): string[] {
111
111
  if (value == null) return [];
112
112
  return (Array.isArray(value) ? value : [value]).filter((v): v is string => typeof v === "string" && v.length > 0);
113
113
  }
package/src/db/agents.ts CHANGED
@@ -14,13 +14,15 @@ import {
14
14
  } from "../contracts";
15
15
  import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
16
16
  import { matchAgents } from "../agent-ref";
17
- import { evaluatePoolBindings, expectedChannelAgentId, upsertChannelForAgent } from "./channels.ts";
17
+ import { expectedChannelAgentId } from "./channel-identity.ts";
18
+ import { evaluatePoolBindings, upsertChannelForAgent } from "./channels.ts";
18
19
  import { seedContinuationObjectiveFromSpawnPrompt } from "./continuations.ts";
19
20
  import { messageTaskClosed } from "./claim-sql.ts";
20
21
  import { ValidationError, getDb } from "./connection.ts";
21
22
  import { rowToAgent, rowToContextSnapshot } from "./mappers.ts";
22
- import { closeOpenPairsForAgent } from "./pairs.ts";
23
- import { TASK_SELECT, insertTaskEvent } from "./tasks.ts";
23
+ import { closeOpenPairsForAgent } from "./pair-maintenance.ts";
24
+ import { parseStringArray } from "./shared.ts";
25
+ import { settleSingleTargetOnDemandTasks } from "./task-maintenance.ts";
24
26
  import { electWorkspaceStewards, electWorkspaceStewardsForAgent } from "./workspaces.ts";
25
27
  import { DEFAULT_SELF_RESUME_CONFIG } from "../self-resume-config";
26
28
  import type { TokenRevokeReason } from "../security";
@@ -89,17 +91,13 @@ import type {
89
91
 
90
92
  const CONTEXT_SNAPSHOT_DEBOUNCE_MS = 60_000;
91
93
 
92
- export function parseStringArray(raw: string): string[] {
93
- const parsed = parseJson<unknown>(raw, []);
94
- if (!Array.isArray(parsed)) return [];
95
- return parsed.filter((value): value is string => typeof value === "string");
96
- }
94
+ export { parseStringArray } from "./shared.ts";
97
95
 
98
96
  export function normalizeTags(tags: string[] | undefined): string[] {
99
97
  return [...new Set((tags ?? []).map((tag) => tag.trim()).filter(Boolean))];
100
98
  }
101
99
 
102
- export function inferAgentKind(input: Pick<RegisterAgentInput, "id" | "kind" | "tags" | "capabilities" | "meta">): AgentKind {
100
+ function inferAgentKind(input: Pick<RegisterAgentInput, "id" | "kind" | "tags" | "capabilities" | "meta">): AgentKind {
103
101
  if (input.kind) return input.kind;
104
102
  if (input.id === "user") return "user";
105
103
  if (input.id === "system") return "system";
@@ -110,7 +108,7 @@ export function inferAgentKind(input: Pick<RegisterAgentInput, "id" | "kind" | "
110
108
  return "provider";
111
109
  }
112
110
 
113
- export function inferProviderTag(input: RegisterAgentInput): "claude" | "codex" | undefined {
111
+ function inferProviderTag(input: RegisterAgentInput): "claude" | "codex" | undefined {
114
112
  if (inferAgentKind(input) !== "provider") return undefined;
115
113
  const meta = input.meta ?? {};
116
114
  const values = [
@@ -134,7 +132,7 @@ export function inferProviderTag(input: RegisterAgentInput): "claude" | "codex"
134
132
  return undefined;
135
133
  }
136
134
 
137
- export function tagsWithProvider(input: RegisterAgentInput): string[] {
135
+ function tagsWithProvider(input: RegisterAgentInput): string[] {
138
136
  const tags = normalizeTags(input.tags);
139
137
  const provider = inferProviderTag(input);
140
138
  if (!provider || tags.includes(provider)) return tags;
@@ -392,14 +390,14 @@ export function listContextSnapshots(agentId: string, options: { since?: number;
392
390
  return (getDb().query(sql).all(...params) as any[]).map(rowToContextSnapshot);
393
391
  }
394
392
 
395
- export function pruneContextSnapshots(agentId?: string, olderThan = Date.now() - DAY_MS): number {
393
+ function pruneContextSnapshots(agentId?: string, olderThan = Date.now() - DAY_MS): number {
396
394
  const result = agentId
397
395
  ? getDb().query("DELETE FROM context_snapshots WHERE agent_id = ? AND captured_at < ?").run(agentId, olderThan)
398
396
  : getDb().query("DELETE FROM context_snapshots WHERE captured_at < ?").run(olderThan);
399
397
  return result.changes;
400
398
  }
401
399
 
402
- export function recordContextSnapshot(agentId: string, context: ContextState, now: number): void {
400
+ function recordContextSnapshot(agentId: string, context: ContextState, now: number): void {
403
401
  const latest = getDb()
404
402
  .query("SELECT captured_at FROM context_snapshots WHERE agent_id = ? ORDER BY captured_at DESC LIMIT 1")
405
403
  .get(agentId) as { captured_at: number } | undefined;
@@ -464,33 +462,7 @@ export function reapStaleAgents(ttlMs: number = STALE_TTL_MS): string[] {
464
462
  return rows.map((r: any) => r.id);
465
463
  }
466
464
 
467
- // On-demand automation tasks (targetMode=on_demand_agent) are bound to a single
468
- // ephemeral agent spawned just for that task. When that agent's claim is released —
469
- // clean shutdown, prune, lease expiry, or orphan-grace — the task must NOT return to
470
- // the claimable pool: its target is a unique `label:automation-…` that no other agent
471
- // will ever match, so a re-opened task lingers forever as an orphaned claim. Resolve
472
- // those as done instead; automation reconcile then settles the run as succeeded.
473
- // Run this BEFORE the generic re-open UPDATE at each release site, with the same WHERE
474
- // condition: it flips matching single-target tasks to 'done', which the subsequent
475
- // re-open (gated on status IN claimed/in_progress/blocked/orphaned) then skips.
476
- export function settleSingleTargetOnDemandTasks(condition: string, params: any[], now: number, reason: string): void {
477
- const selectSql = `${TASK_SELECT} WHERE (${condition}) AND json_extract(metadata, '$.targetMode') = 'on_demand_agent' AND status IN ('claimed', 'in_progress', 'blocked', 'orphaned')`;
478
- const rows = getDb().query(selectSql).all(...params) as any[];
479
- if (rows.length === 0) return;
480
- getDb().query(
481
- `UPDATE tasks SET status = 'done', claim_expires_at = NULL, updated_at = ?, last_seen_at = ? WHERE (${condition}) AND json_extract(metadata, '$.targetMode') = 'on_demand_agent' AND status IN ('claimed', 'in_progress', 'blocked', 'orphaned')`
482
- ).run(now, now, ...params);
483
- for (const row of rows) {
484
- insertTaskEvent(row.id, {
485
- source: "agent-relay",
486
- type: "task.auto-completed",
487
- severity: row.severity,
488
- title: "On-demand task auto-resolved",
489
- body: `On-demand agent ${row.claimed_by ?? "(unknown)"} exited (${reason}); task resolved so it does not orphan`,
490
- metadata: { agentId: row.claimed_by, reason, completedBy: "relay" },
491
- }, now);
492
- }
493
- }
465
+ export { settleSingleTargetOnDemandTasks } from "./task-maintenance.ts";
494
466
 
495
467
  export function pruneOfflineAgents(maxOfflineMs: number = DAY_MS): string[] {
496
468
  const cutoff = Date.now() - maxOfflineMs;
@@ -578,7 +550,7 @@ export function revokeRuntimeTokensForAgent(agentId: string, now = Date.now(), r
578
550
  return rows.map((item) => item.jti);
579
551
  }
580
552
 
581
- export function runtimeTokenJtisFromMeta(meta: Record<string, unknown>): string[] {
553
+ function runtimeTokenJtisFromMeta(meta: Record<string, unknown>): string[] {
582
554
  const jtis = new Set<string>();
583
555
  const auth = isRecord(meta.auth) ? meta.auth : undefined;
584
556
  const direct = typeof auth?.jti === "string" ? auth.jti : typeof meta.runtimeTokenJti === "string" ? meta.runtimeTokenJti : undefined;
@@ -82,28 +82,28 @@ import type {
82
82
  // --- Artifacts ---
83
83
 
84
84
  export const VALID_ARTIFACT_KINDS = new Set<ArtifactKind>(["image", "audio", "video", "document", "archive", "other"]);
85
- export const VALID_ARTIFACT_VISIBILITIES = new Set<ArtifactVisibility>(["private", "project", "org"]);
85
+ const VALID_ARTIFACT_VISIBILITIES = new Set<ArtifactVisibility>(["private", "project", "org"]);
86
86
  export const VALID_ARTIFACT_SENSITIVITIES = new Set<ArtifactSensitivity>(["public", "normal", "sensitive", "secret"]);
87
- export const VALID_ARTIFACT_LINK_ENTITY_TYPES = new Set<ArtifactLink["entityType"]>(["message", "task", "recipeRun", "recipeStep", "channelEvent"]);
87
+ const VALID_ARTIFACT_LINK_ENTITY_TYPES = new Set<ArtifactLink["entityType"]>(["message", "task", "recipeRun", "recipeStep", "channelEvent"]);
88
88
  export const VALID_ARTIFACT_ROLES = new Set<NonNullable<ArtifactLink["role"]>>(["media", "patch", "report", "log", "output", "input"]);
89
89
 
90
90
  export function artifactId(): string {
91
91
  return `art_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
92
92
  }
93
93
 
94
- export function artifactLinkId(): string {
94
+ function artifactLinkId(): string {
95
95
  return `alink_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
96
96
  }
97
97
 
98
- export function normalizeArtifactKind(kind: unknown): ArtifactKind {
98
+ function normalizeArtifactKind(kind: unknown): ArtifactKind {
99
99
  return typeof kind === "string" && VALID_ARTIFACT_KINDS.has(kind as ArtifactKind) ? kind as ArtifactKind : "other";
100
100
  }
101
101
 
102
- export function normalizeArtifactVisibility(visibility: unknown): ArtifactVisibility {
102
+ function normalizeArtifactVisibility(visibility: unknown): ArtifactVisibility {
103
103
  return typeof visibility === "string" && VALID_ARTIFACT_VISIBILITIES.has(visibility as ArtifactVisibility) ? visibility as ArtifactVisibility : "project";
104
104
  }
105
105
 
106
- export function normalizeArtifactSensitivity(sensitivity: unknown): ArtifactSensitivity {
106
+ function normalizeArtifactSensitivity(sensitivity: unknown): ArtifactSensitivity {
107
107
  return typeof sensitivity === "string" && VALID_ARTIFACT_SENSITIVITIES.has(sensitivity as ArtifactSensitivity) ? sensitivity as ArtifactSensitivity : "normal";
108
108
  }
109
109
 
@@ -212,7 +212,7 @@ export function deleteArtifact(id: string): boolean {
212
212
  })();
213
213
  }
214
214
 
215
- export function listArtifactLinks(artifactId: string): ArtifactLink[] {
215
+ function listArtifactLinks(artifactId: string): ArtifactLink[] {
216
216
  return (getDb().query("SELECT * FROM artifact_links WHERE artifact_id = ? ORDER BY created_at ASC").all(artifactId) as any[]).map(rowToArtifactLink);
217
217
  }
218
218
 
@@ -271,7 +271,7 @@ export function artifactBlobReferenceCount(digest: string): number {
271
271
  return row.count ?? 0;
272
272
  }
273
273
 
274
- export function unreferencedArtifactBlobs(): ArtifactBlob[] {
274
+ function unreferencedArtifactBlobs(): ArtifactBlob[] {
275
275
  return (getDb().query(`
276
276
  SELECT b.*
277
277
  FROM artifact_blobs b
@@ -0,0 +1,38 @@
1
+ import { stringValue } from "agent-relay-sdk";
2
+ import type { AgentCard, RegisterAgentInput } from "../types";
3
+
4
+ export function channelProviderForAgent(agent: AgentCard): string {
5
+ return stringValue(agent.meta?.provider) ??
6
+ stringValue(agent.meta?.channelType) ??
7
+ stringValue(agent.meta?.transport) ??
8
+ agent.tags.find((tag) => tag.startsWith("channel:"))?.slice("channel:".length) ??
9
+ "custom";
10
+ }
11
+
12
+ function channelProviderForInput(input: Pick<RegisterAgentInput, "tags" | "meta">): string {
13
+ return stringValue(input.meta?.provider) ??
14
+ stringValue(input.meta?.channelType) ??
15
+ stringValue(input.meta?.transport) ??
16
+ (input.tags ?? []).find((tag) => tag.startsWith("channel:"))?.slice("channel:".length) ??
17
+ "custom";
18
+ }
19
+
20
+ export function channelAccountIdForAgent(agent: AgentCard, provider: string): string {
21
+ const accountId = stringValue(agent.meta?.accountId);
22
+ if (accountId) return accountId;
23
+ const prefix = `${provider}:`;
24
+ return agent.id.startsWith(prefix) ? agent.id.slice(prefix.length) : agent.id;
25
+ }
26
+
27
+ function channelAccountIdForInput(input: Pick<RegisterAgentInput, "id" | "meta">, provider: string): string {
28
+ const accountId = stringValue(input.meta?.accountId);
29
+ if (accountId) return accountId;
30
+ const prefix = `${provider}:`;
31
+ return input.id.startsWith(prefix) ? input.id.slice(prefix.length) : input.id;
32
+ }
33
+
34
+ export function expectedChannelAgentId(input: Pick<RegisterAgentInput, "id" | "tags" | "meta">): string {
35
+ const provider = channelProviderForInput(input);
36
+ const accountId = channelAccountIdForInput(input, provider);
37
+ return `${provider}:${accountId}`;
38
+ }
@@ -15,10 +15,12 @@ import {
15
15
  } from "../contracts";
16
16
  import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
17
17
  import { matchAgents } from "../agent-ref";
18
- import { getAgent, listAgents, parseStringArray } from "./agents.ts";
18
+ import { isDeliveryAgent } from "./agent-predicates.ts";
19
+ import { channelAccountIdForAgent, channelProviderForAgent, expectedChannelAgentId } from "./channel-identity.ts";
19
20
  import { ValidationError, getDb } from "./connection.ts";
20
- import { isDeliveryAgent } from "./delivery.ts";
21
- import { runningAgentForPolicy } from "./messages.ts";
21
+ import { rowToAgent } from "./mappers.ts";
22
+ import { runningAgentForPolicy } from "./policy-runtime.ts";
23
+ import { parseStringArray } from "./shared.ts";
22
24
  import type {
23
25
  AgentCard,
24
26
  ActivityEvent,
@@ -82,7 +84,9 @@ import type {
82
84
  WorkspaceStatus,
83
85
  } from "../types";
84
86
 
85
- export function rowToChannelBinding(row: any): ChannelBinding {
87
+ export { channelProviderForAgent, expectedChannelAgentId } from "./channel-identity.ts";
88
+
89
+ function rowToChannelBinding(row: any): ChannelBinding {
86
90
  const target = row.target_type === "broadcast"
87
91
  ? { type: "broadcast" } as ChannelRouteTarget
88
92
  : { type: row.target_type, id: row.target_id } as ChannelRouteTarget;
@@ -105,7 +109,7 @@ export function rowToChannelBinding(row: any): ChannelBinding {
105
109
  return binding;
106
110
  }
107
111
 
108
- export function bindingTargetToLegacyTarget(target: ChannelRouteTarget): string {
112
+ function bindingTargetToLegacyTarget(target: ChannelRouteTarget): string {
109
113
  if (target.type === "agent") return target.id;
110
114
  if (target.type === "label") return `label:${target.id}`;
111
115
  if (target.type === "team") return `team:${target.id}`;
@@ -118,7 +122,7 @@ export function bindingTargetToLegacyTarget(target: ChannelRouteTarget): string
118
122
  return "";
119
123
  }
120
124
 
121
- export function configuredChannelsForAgent(agent: AgentCard): string[] {
125
+ function configuredChannelsForAgent(agent: AgentCard): string[] {
122
126
  const channels = agent.meta?.channels;
123
127
  if (!Array.isArray(channels)) return [];
124
128
  return channels
@@ -127,7 +131,7 @@ export function configuredChannelsForAgent(agent: AgentCard): string[] {
127
131
  .filter((item) => item.length > 0);
128
132
  }
129
133
 
130
- export function channelEntryMatches(channelId: string, entry: string): boolean {
134
+ function channelEntryMatches(channelId: string, entry: string): boolean {
131
135
  const normalized = entry.trim();
132
136
  if (!normalized) return false;
133
137
  if (normalized === channelId) return true;
@@ -135,27 +139,36 @@ export function channelEntryMatches(channelId: string, entry: string): boolean {
135
139
  return channelId.startsWith(`${normalized}:`);
136
140
  }
137
141
 
138
- export function agentCanServeChannel(agent: AgentCard, channelId?: string): boolean {
142
+ function agentCanServeChannel(agent: AgentCard, channelId?: string): boolean {
139
143
  if (!channelId) return true;
140
144
  const channels = configuredChannelsForAgent(agent);
141
145
  if (channels.length === 0) return true;
142
146
  return channels.some((entry) => channelEntryMatches(channelId, entry));
143
147
  }
144
148
 
145
- export function channelTargetMatches(target: ChannelRouteTarget, channelId?: string): AgentCard[] {
146
- const candidates = listAgents().filter((agent) => (
149
+ function getChannelAgent(id: string): AgentCard | null {
150
+ const row = getDb().query("SELECT * FROM agents WHERE id = ?").get(id) as any;
151
+ return row ? rowToAgent(row) : null;
152
+ }
153
+
154
+ function listChannelAgents(): AgentCard[] {
155
+ return (getDb().query("SELECT * FROM agents ORDER BY last_seen DESC").all() as any[]).map(rowToAgent);
156
+ }
157
+
158
+ function channelTargetMatches(target: ChannelRouteTarget, channelId?: string): AgentCard[] {
159
+ const candidates = listChannelAgents().filter((agent) => (
147
160
  agent.id !== "user" &&
148
161
  agent.id !== "system" &&
149
162
  agent.kind !== "channel" &&
150
163
  agent.meta?.kind !== "channel"
151
164
  ));
152
165
  if (target.type === "agent" || target.type === "orchestrator") {
153
- const agent = getAgent(target.id);
166
+ const agent = getChannelAgent(target.id);
154
167
  return agent ? [agent] : [];
155
168
  }
156
169
  if (target.type === "policy") {
157
170
  const agentId = runningAgentForPolicy(target.id);
158
- const agent = agentId ? getAgent(agentId) : null;
171
+ const agent = agentId ? getChannelAgent(agentId) : null;
159
172
  return agent ? [agent] : [];
160
173
  }
161
174
  const channelCandidates = candidates.filter((agent) => agentCanServeChannel(agent, channelId));
@@ -168,15 +181,15 @@ export function channelTargetMatches(target: ChannelRouteTarget, channelId?: str
168
181
  return [];
169
182
  }
170
183
 
171
- export function poolSelectorMatches(selector: string, candidates: AgentCard[]): AgentCard[] {
184
+ function poolSelectorMatches(selector: string, candidates: AgentCard[]): AgentCard[] {
172
185
  if (selector.startsWith("label:")) return candidates.filter((a) => a.label === selector.slice("label:".length));
173
186
  if (selector.startsWith("tag:")) return candidates.filter((a) => a.tags.includes(selector.slice("tag:".length)));
174
187
  if (selector.startsWith("cap:")) return candidates.filter((a) => a.capabilities.includes(selector.slice("cap:".length)));
175
- const agent = getAgent(selector);
188
+ const agent = getChannelAgent(selector);
176
189
  return agent ? [agent] : [];
177
190
  }
178
191
 
179
- export function channelTargetMatchSnapshot(agent: AgentCard): ChannelTargetHealth["matches"][number] {
192
+ function channelTargetMatchSnapshot(agent: AgentCard): ChannelTargetHealth["matches"][number] {
180
193
  return {
181
194
  id: agent.id,
182
195
  name: agent.name,
@@ -189,15 +202,15 @@ export function channelTargetMatchSnapshot(agent: AgentCard): ChannelTargetHealt
189
202
  };
190
203
  }
191
204
 
192
- export function isHealthyChannelTarget(agent: AgentCard, now: number): boolean {
205
+ function isHealthyChannelTarget(agent: AgentCard, now: number): boolean {
193
206
  return isDeliveryAgent(agent) && agent.ready && agent.lastSeen > now - STALE_TTL_MS;
194
207
  }
195
208
 
196
- export function describeTarget(target: ChannelRouteTarget): string {
209
+ function describeTarget(target: ChannelRouteTarget): string {
197
210
  return bindingTargetToLegacyTarget(target);
198
211
  }
199
212
 
200
- export function channelTargetHealth(binding: ChannelBinding, now: number = Date.now()): ChannelTargetHealth {
213
+ function channelTargetHealth(binding: ChannelBinding, now: number = Date.now()): ChannelTargetHealth {
201
214
  const target = binding.target;
202
215
  const targetLabel = describeTarget(target);
203
216
  const matches = channelTargetMatches(target, binding.channelId);
@@ -209,7 +222,7 @@ export function channelTargetHealth(binding: ChannelBinding, now: number = Date.
209
222
  if (matches.length === 0) return { status: "error", detail: `Pool ${targetLabel}: no eligible agents`, target, matches: [] };
210
223
  return { status: "error", detail: `Pool ${targetLabel}: ${healthyEligibles.length} eligible but slot unclaimed`, target, matches: snapshots };
211
224
  }
212
- const holder = getAgent(binding.poolAgentId);
225
+ const holder = getChannelAgent(binding.poolAgentId);
213
226
  if (!holder || holder.status === "offline") {
214
227
  return { status: "error", detail: `Pool ${targetLabel}: holder ${binding.poolAgentId} is offline`, target, matches: snapshots };
215
228
  }
@@ -271,7 +284,7 @@ export function channelTargetHealth(binding: ChannelBinding, now: number = Date.
271
284
  };
272
285
  }
273
286
 
274
- export function rowToChannelSummary(row: any): ChannelSummary {
287
+ function rowToChannelSummary(row: any): ChannelSummary {
275
288
  const binding = row.binding_id ? rowToChannelBinding({
276
289
  id: row.binding_id,
277
290
  channel_id: row.id,
@@ -309,44 +322,7 @@ export function rowToChannelSummary(row: any): ChannelSummary {
309
322
  };
310
323
  }
311
324
 
312
-
313
- export function channelProviderForAgent(agent: AgentCard): string {
314
- return stringValue(agent.meta?.provider) ??
315
- stringValue(agent.meta?.channelType) ??
316
- stringValue(agent.meta?.transport) ??
317
- agent.tags.find((tag) => tag.startsWith("channel:"))?.slice("channel:".length) ??
318
- "custom";
319
- }
320
-
321
- export function channelProviderForInput(input: Pick<RegisterAgentInput, "tags" | "meta">): string {
322
- return stringValue(input.meta?.provider) ??
323
- stringValue(input.meta?.channelType) ??
324
- stringValue(input.meta?.transport) ??
325
- (input.tags ?? []).find((tag) => tag.startsWith("channel:"))?.slice("channel:".length) ??
326
- "custom";
327
- }
328
-
329
- export function channelAccountIdForAgent(agent: AgentCard, provider: string): string {
330
- const accountId = stringValue(agent.meta?.accountId);
331
- if (accountId) return accountId;
332
- const prefix = `${provider}:`;
333
- return agent.id.startsWith(prefix) ? agent.id.slice(prefix.length) : agent.id;
334
- }
335
-
336
- export function channelAccountIdForInput(input: Pick<RegisterAgentInput, "id" | "meta">, provider: string): string {
337
- const accountId = stringValue(input.meta?.accountId);
338
- if (accountId) return accountId;
339
- const prefix = `${provider}:`;
340
- return input.id.startsWith(prefix) ? input.id.slice(prefix.length) : input.id;
341
- }
342
-
343
- export function expectedChannelAgentId(input: Pick<RegisterAgentInput, "id" | "tags" | "meta">): string {
344
- const provider = channelProviderForInput(input);
345
- const accountId = channelAccountIdForInput(input, provider);
346
- return `${provider}:${accountId}`;
347
- }
348
-
349
- export function channelDirectionForAgent(agent: AgentCard): ChannelSummary["direction"] {
325
+ function channelDirectionForAgent(agent: AgentCard): ChannelSummary["direction"] {
350
326
  const direction = stringValue(agent.meta?.direction);
351
327
  return direction === "inbound" || direction === "outbound" || direction === "bidirectional" ? direction : "bidirectional";
352
328
  }
@@ -497,7 +473,7 @@ export function upsertChannelBinding(input: {
497
473
  return rowToChannelBinding(row);
498
474
  }
499
475
 
500
- export interface PoolBindingChange {
476
+ interface PoolBindingChange {
501
477
  bindingId: string;
502
478
  channelId: string;
503
479
  previousAgentId: string | null;
@@ -517,7 +493,7 @@ export function evaluatePoolBindings(now: number = Date.now()): PoolBindingChang
517
493
 
518
494
  let holderValid = false;
519
495
  if (currentAgentId) {
520
- const holder = getAgent(currentAgentId);
496
+ const holder = getChannelAgent(currentAgentId);
521
497
  if (holder && holder.status !== "offline" && holder.ready && holder.lastSeen > now - STALE_TTL_MS && agentCanServeChannel(holder, channelId)) {
522
498
  if (currentEpoch === null || holder.epoch === currentEpoch) {
523
499
  getDb().query("UPDATE channel_bindings SET pool_claim_expires_at = ? WHERE id = ?")
@@ -534,7 +510,7 @@ export function evaluatePoolBindings(now: number = Date.now()): PoolBindingChang
534
510
  }
535
511
 
536
512
  if (!holderValid) {
537
- const candidates = listAgents().filter((a) =>
513
+ const candidates = listChannelAgents().filter((a) =>
538
514
  a.id !== "user" && a.id !== "system" && a.kind !== "channel" && a.meta?.kind !== "channel"
539
515
  );
540
516
  const eligible = poolSelectorMatches(selector, candidates)
@@ -2,7 +2,7 @@ import { MAX_BODY_BYTES } from "../config";
2
2
  import { getContinuationEnvelope } from "./continuations.ts";
3
3
  import { getDb, ValidationError } from "./connection.ts";
4
4
 
5
- export interface ContinuationArchive {
5
+ interface ContinuationArchive {
6
6
  id: number;
7
7
  ref: string;
8
8
  agentId: string;
@@ -11,7 +11,7 @@ export interface ContinuationArchive {
11
11
  createdAt: number;
12
12
  }
13
13
 
14
- export interface ContinuationArchiveSearchResult extends ContinuationArchive {
14
+ interface ContinuationArchiveSearchResult extends ContinuationArchive {
15
15
  score: number | null;
16
16
  searchMode: "fts5" | "like";
17
17
  }
@@ -80,7 +80,7 @@ export function archiveContinuationSegment(input: {
80
80
  return getContinuationArchive(Number(info.lastInsertRowid))!;
81
81
  }
82
82
 
83
- export function getContinuationArchive(id: number): ContinuationArchive | null {
83
+ function getContinuationArchive(id: number): ContinuationArchive | null {
84
84
  const row = getDb().query("SELECT * FROM continuation_archives WHERE id = ?").get(id) as ContinuationArchiveRow | undefined;
85
85
  return row ? rowToArchive(row) : null;
86
86
  }
@@ -115,7 +115,7 @@ export function searchContinuationArchives(input: {
115
115
  return searchContinuationArchivesLike(agentId, query, limit);
116
116
  }
117
117
 
118
- export function continuationArchiveSearchMode(): "fts5" | "like" {
118
+ function continuationArchiveSearchMode(): "fts5" | "like" {
119
119
  if (process.env.AGENT_RELAY_DISABLE_CONTINUATION_ARCHIVE_FTS === "1") return "like";
120
120
  const row = getDb().query(`
121
121
  SELECT 1 FROM sqlite_master
@@ -16,10 +16,11 @@ import {
16
16
  import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
17
17
  import { matchAgents } from "../agent-ref";
18
18
  import { getAgent, listAgents } from "./agents.ts";
19
- import { channelProviderForAgent } from "./channels.ts";
19
+ import { isDeliveryAgent } from "./agent-predicates.ts";
20
+ import { channelProviderForAgent } from "./channel-identity.ts";
20
21
  import { getDb } from "./connection.ts";
21
22
  import { MSG_SELECT, rowToMessage, rowToMessageDeliveryAttempt } from "./mappers.ts";
22
- import { getMessage } from "./messages.ts";
23
+ import { getMessage } from "./message-queries.ts";
23
24
  import type {
24
25
  AgentCard,
25
26
  ActivityEvent,
@@ -83,7 +84,7 @@ import type {
83
84
  WorkspaceStatus,
84
85
  } from "../types";
85
86
 
86
- export function queueDepthLimit(target: string): number {
87
+ function queueDepthLimit(target: string): number {
87
88
  const row = getDb().query("SELECT value FROM config WHERE namespace = 'system' AND key = 'message-queue'").get() as { value?: string } | undefined;
88
89
  const parsed = row?.value ? parseJson<Record<string, unknown>>(row.value, {}) : {};
89
90
  const perTarget = parsed?.maxDepthPerTarget;
@@ -124,13 +125,7 @@ export function enforceQueueLimit(target: string): void {
124
125
  })();
125
126
  }
126
127
 
127
- export function isDeliveryAgent(agent: AgentCard): boolean {
128
- return agent.status !== "offline" &&
129
- agent.id !== "user" &&
130
- agent.id !== "system" &&
131
- agent.kind !== "channel" &&
132
- agent.meta?.kind !== "channel";
133
- }
128
+ export { isDeliveryAgent } from "./agent-predicates.ts";
134
129
 
135
130
  export function isChannelAgentId(agentId: string): boolean {
136
131
  const agent = getAgent(agentId);
@@ -242,7 +237,7 @@ export function getMessageDeliveryAttempts(messageId: number, limit = 50): Messa
242
237
  `).all(messageId, safeLimit) as any[]).map(rowToMessageDeliveryAttempt);
243
238
  }
244
239
 
245
- export function insertMessageDeliveryAttempt(
240
+ function insertMessageDeliveryAttempt(
246
241
  messageId: number,
247
242
  input: {
248
243
  agentId?: string;
@@ -269,7 +264,7 @@ export function insertMessageDeliveryAttempt(
269
264
  );
270
265
  }
271
266
 
272
- export function setMessageDeliveryState(
267
+ function setMessageDeliveryState(
273
268
  messageId: number,
274
269
  input: {
275
270
  status: MessageDeliveryStatus;
package/src/db/inbox.ts CHANGED
@@ -212,7 +212,7 @@ export function createChatHistoryImport(input: {
212
212
  return getChatHistoryImport(id)!;
213
213
  }
214
214
 
215
- export function getChatHistoryImport(id: string): ChatHistoryImport | null {
215
+ function getChatHistoryImport(id: string): ChatHistoryImport | null {
216
216
  const row = getDb().query("SELECT * FROM chat_history_imports WHERE id = ?").get(id) as any | undefined;
217
217
  if (!row) return null;
218
218
  const entries = (getDb().query(
@@ -212,7 +212,7 @@ export function listIntegrationTaskStats(): IntegrationTaskStats[] {
212
212
  }));
213
213
  }
214
214
 
215
- export type IntegrationRegistryInput = {
215
+ type IntegrationRegistryInput = {
216
216
  name: string;
217
217
  displayName?: string;
218
218
  description?: string;
@@ -231,7 +231,7 @@ export type IntegrationRegistryInput = {
231
231
  source?: "api" | "env" | "observed";
232
232
  };
233
233
 
234
- export function rowToIntegrationRegistry(row: any): Omit<IntegrationSummary, "configured" | "observed" | "callbackConfigured" | "rateLimit" | "taskStats"> & {
234
+ function rowToIntegrationRegistry(row: any): Omit<IntegrationSummary, "configured" | "observed" | "callbackConfigured" | "rateLimit" | "taskStats"> & {
235
235
  source: "api" | "env" | "observed";
236
236
  lastEventAt?: number;
237
237
  lastTaskAt?: number;
@@ -312,7 +312,7 @@ export function upsertIntegrationRegistry(input: IntegrationRegistryInput): Retu
312
312
  return getIntegrationRegistry(name)!;
313
313
  }
314
314
 
315
- export function getIntegrationRegistry(name: string): ReturnType<typeof rowToIntegrationRegistry> | null {
315
+ function getIntegrationRegistry(name: string): ReturnType<typeof rowToIntegrationRegistry> | null {
316
316
  const row = getDb().query("SELECT * FROM integration_registry WHERE name = ?").get(name) as any;
317
317
  return row ? rowToIntegrationRegistry(row) : null;
318
318
  }
@@ -321,7 +321,7 @@ export function listIntegrationRegistry(): ReturnType<typeof rowToIntegrationReg
321
321
  return (getDb().query("SELECT * FROM integration_registry ORDER BY display_name COLLATE NOCASE, name COLLATE NOCASE").all() as any[]).map(rowToIntegrationRegistry);
322
322
  }
323
323
 
324
- export function updateIntegrationObserved(name: string, eventAt: number): void {
324
+ function updateIntegrationObserved(name: string, eventAt: number): void {
325
325
  const now = Date.now();
326
326
  getDb().query(`
327
327
  INSERT INTO integration_registry (name, enabled, scopes, targets, channels, tags, manifest, source, created_at, updated_at, last_event_at, last_task_at)
@@ -1,11 +1,11 @@
1
1
  import { getDb, ValidationError } from "./connection.ts";
2
2
 
3
- export const ISSUE_LIFECYCLE_ACTIONS = ["close", "reopen"] as const;
4
- export const ISSUE_LIFECYCLE_STATUSES = ["succeeded", "failed"] as const;
5
- export type IssueLifecycleAction = (typeof ISSUE_LIFECYCLE_ACTIONS)[number];
6
- export type IssueLifecycleStatus = (typeof ISSUE_LIFECYCLE_STATUSES)[number];
3
+ const ISSUE_LIFECYCLE_ACTIONS = ["close", "reopen"] as const;
4
+ const ISSUE_LIFECYCLE_STATUSES = ["succeeded", "failed"] as const;
5
+ type IssueLifecycleAction = (typeof ISSUE_LIFECYCLE_ACTIONS)[number];
6
+ type IssueLifecycleStatus = (typeof ISSUE_LIFECYCLE_STATUSES)[number];
7
7
 
8
- export interface IssueLifecycleActionRecord {
8
+ interface IssueLifecycleActionRecord {
9
9
  id: number;
10
10
  issueRefId: string;
11
11
  action: IssueLifecycleAction;
@@ -24,7 +24,7 @@ export interface IssueLifecycleActionRecord {
24
24
  updatedAt: number;
25
25
  }
26
26
 
27
- export interface UpsertIssueLifecycleActionInput {
27
+ interface UpsertIssueLifecycleActionInput {
28
28
  issueRefId: string;
29
29
  action: IssueLifecycleAction;
30
30
  landKey: string;
package/src/db/mappers.ts CHANGED
@@ -14,8 +14,7 @@ import {
14
14
  } from "../contracts";
15
15
  import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
16
16
  import { matchAgents } from "../agent-ref";
17
- import { parseStringArray } from "./agents.ts";
18
- import { artifactId } from "./artifacts.ts";
17
+ import { parseStringArray } from "./shared.ts";
19
18
  import type {
20
19
  AgentCard,
21
20
  ActivityEvent,