agentic-relay 4.2.1 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/lib/api.mjs CHANGED
@@ -73,15 +73,15 @@ export function search(query, { apiKey, userContext, maxResults = 25, timeoutMs
73
73
  }
74
74
 
75
75
  export function sessionStart(
76
- { capabilityId, adUnitId, clearingPriceCents, initialData },
76
+ { agentId, initialData, taskContext },
77
77
  { apiKey, timeoutMs },
78
78
  ) {
79
79
  return apiPost(
80
80
  "/session/start",
81
81
  {
82
- capability_id: capabilityId,
83
- ad_unit_id: adUnitId,
84
- clearing_price_cents: clearingPriceCents,
82
+ agent_id: agentId,
83
+ // A2A current-task brief; api-v1 forwards it to capability-session /initiate.
84
+ ...(taskContext ? { task_context: taskContext } : {}),
85
85
  ...(initialData ? { initial_data: initialData } : {}),
86
86
  },
87
87
  { apiKey, timeoutMs },
package/bin/lib/cache.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * On-disk cache (spec §9). Layout under --cache-dir (default ./.agent-relay):
3
3
  *
4
- * index.json { slug: { ts, expires, capability_id, ad_unit_id, last_session_id } }
4
+ * index.json { slug: { ts, expires, agent_id, last_session_id } }
5
5
  * specs/<slug>.json a Deliverable
6
6
  * search/<hash>.json { ts, expires, query, capabilities }
7
7
  *
@@ -143,7 +143,7 @@ export class Cache {
143
143
  name: spec.name ?? null,
144
144
  summary: spec.summary ?? null,
145
145
  features: Array.isArray(spec.features) ? spec.features.length : null,
146
- capability_id: spec.capability_id ?? null,
146
+ agent_id: spec.agent_id ?? null,
147
147
  ts: spec.ts ?? null,
148
148
  };
149
149
  });
@@ -23,36 +23,25 @@ import { slugify } from "./config.mjs";
23
23
  import { Cache } from "./cache.mjs";
24
24
  import { cmdFetch } from "./fetch.mjs";
25
25
 
26
- /** Normalize one upstream capability into the spec §4 menu shape. */
26
+ /** Normalize one upstream capability into the lean menu shape. `agent_id` is the only id —
27
+ * session start needs nothing else (ad_unit + price are resolved server-side). */
27
28
  function normalizeCapability(c) {
28
29
  const name = c.name || "";
29
30
  return {
30
31
  slug: slugify(name),
31
32
  name,
32
- type: c.type || "dynamic_capability",
33
+ type: "dynamic_capability", // single type now; kept for the --dynamic-only filter
34
+ agent_id: c.agent_id || null,
33
35
  relevance: typeof c.relevance_score === "number" ? c.relevance_score : null,
34
- clearing_price_cents:
35
- typeof c.clearing_price_cents === "number" ? c.clearing_price_cents : 0,
36
- capability_id: c.capability_id || null,
37
- ad_unit_id: c.ad_unit_id || null,
36
+ reputation: typeof c.reputation_score === "number" ? c.reputation_score : null,
37
+ total_agent_ratings: typeof c.total_agent_ratings === "number" ? c.total_agent_ratings : 0,
38
+ sessions_count: typeof c.sessions_count === "number" ? c.sessions_count : 0,
38
39
  description: c.description || "",
39
- click_url: c.click_url || null,
40
- // Track-record evidence card (server-side from capability_profile.fit). The
41
- // backend already ranks/trims and strips confidence; pass these through as-is
42
- // so --json carries the exact same shape the API and MCP return. Empty arrays
43
- // when a capability has no roller profile yet.
44
- summary: c.summary || "",
45
- feedback_payout:
46
- typeof c.feedback_payout === "number"
47
- ? c.feedback_payout
48
- : typeof c.clearing_price_cents === "number"
49
- ? c.clearing_price_cents
50
- : 0,
51
40
  offers_paid_deliverable: Boolean(c.offers_paid_deliverable),
52
- deliverable: c.deliverable || null,
53
- proven_for: Array.isArray(c.proven_for) ? c.proven_for : [],
54
- watch_out: Array.isArray(c.watch_out) ? c.watch_out : [],
55
- works_with: Array.isArray(c.works_with) ? c.works_with : [],
41
+ deliverable_price_cents: typeof c.deliverable_price_cents === "number" ? c.deliverable_price_cents : null,
42
+ required_inputs: Array.isArray(c.required_inputs) ? c.required_inputs : [],
43
+ // Track-record cards are disabled server-side for now; the API returns a note string.
44
+ fit: typeof c.fit === "string" ? c.fit : "",
56
45
  };
57
46
  }
58
47
 
@@ -92,13 +81,11 @@ export async function cmdSearch(query, opts) {
92
81
  paymentContext = data.payment_context ?? null;
93
82
  capabilities = dedupeSlugs((data.capabilities || []).map(normalizeCapability));
94
83
  cache.writeSearch(query, capabilities, paymentContext);
95
- // Seed the index so session start can resolve ids without re-searching.
84
+ // Seed the index so session start can resolve agent_id without re-searching.
96
85
  for (const c of capabilities) {
97
- if (c.capability_id && c.ad_unit_id) {
86
+ if (c.agent_id) {
98
87
  cache.updateIndex(c.slug, {
99
- capability_id: c.capability_id,
100
- ad_unit_id: c.ad_unit_id,
101
- clearing_price_cents: c.clearing_price_cents,
88
+ agent_id: c.agent_id,
102
89
  name: c.name,
103
90
  ts: new Date().toISOString(),
104
91
  });
@@ -122,38 +109,26 @@ export async function cmdSearch(query, opts) {
122
109
  }
123
110
 
124
111
  /**
125
- * Resolve a slug (or capability_id) to { capabilityId, adUnitId,
126
- * clearingPriceCents, slug, name }. Order: cache index banked spec file
127
- * fresh search keyed on the de-kebabed slug. The spec-file fallback lets a
128
- * depth session resolve even when the cache was filled only by `cache put`
129
- * during the breadth pass (which doesn't write the index).
112
+ * Resolve a slug (or agent_id) to { agentId, slug, name }. Order: cache index →
113
+ * banked spec file fresh search keyed on the de-kebabed slug. The spec-file
114
+ * fallback lets a depth session resolve even when the cache was filled only by
115
+ * `cache put` during the breadth pass (which doesn't write the index).
116
+ * agent_id is the only id needed the ad_unit + price are resolved server-side.
130
117
  */
131
118
  export async function resolveCapability(slugOrId, opts) {
132
119
  const cache = new Cache(opts.cacheDir, { enabled: !opts.noCache });
133
120
  const slug = slugify(slugOrId);
134
121
 
135
122
  const entry = cache.getIndexEntry(slug) || cache.getIndexEntry(slugOrId);
136
- if (entry && entry.capability_id && entry.ad_unit_id && !opts.refresh) {
137
- return {
138
- capabilityId: entry.capability_id,
139
- adUnitId: entry.ad_unit_id,
140
- clearingPriceCents: entry.clearing_price_cents || 0,
141
- slug,
142
- name: entry.name || slug,
143
- };
123
+ if (entry && entry.agent_id && !opts.refresh) {
124
+ return { agentId: entry.agent_id, slug, name: entry.name || slug };
144
125
  }
145
126
 
146
- // Banked spec fallback — a breadth subagent stored ids here via `cache put`.
127
+ // Banked spec fallback — a breadth subagent stored the agent_id here via `cache put`.
147
128
  if (!opts.refresh) {
148
129
  const spec = cache.readSpec(slug, { ttlMs: Infinity });
149
- if (spec && spec.capability_id && spec.ad_unit_id) {
150
- return {
151
- capabilityId: spec.capability_id,
152
- adUnitId: spec.ad_unit_id,
153
- clearingPriceCents: spec.clearing_price_cents || 0,
154
- slug,
155
- name: spec.name || slug,
156
- };
130
+ if (spec && spec.agent_id) {
131
+ return { agentId: spec.agent_id, slug, name: spec.name || slug };
157
132
  }
158
133
  }
159
134
 
@@ -162,20 +137,14 @@ export async function resolveCapability(slugOrId, opts) {
162
137
  const found = await cmdSearch(queryGuess, { ...opts, refresh: true, max: 25 });
163
138
  const match =
164
139
  found.capabilities.find((c) => c.slug === slug) ||
165
- found.capabilities.find((c) => c.capability_id === slugOrId) ||
140
+ found.capabilities.find((c) => c.agent_id === slugOrId) ||
166
141
  found.capabilities.find((c) => c.slug.startsWith(slug));
167
- if (!match || !match.capability_id || !match.ad_unit_id) {
142
+ if (!match || !match.agent_id) {
168
143
  throw new Error(
169
- `could not resolve "${slugOrId}" — run \`agentrelay search\` first or pass a known slug`,
144
+ `could not resolve "${slugOrId}" — run \`agentrelay search\` first or pass a known agent_id`,
170
145
  );
171
146
  }
172
- return {
173
- capabilityId: match.capability_id,
174
- adUnitId: match.ad_unit_id,
175
- clearingPriceCents: match.clearing_price_cents || 0,
176
- slug: match.slug,
177
- name: match.name,
178
- };
147
+ return { agentId: match.agent_id, slug: match.slug, name: match.name };
179
148
  }
180
149
 
181
150
  // ── session primitives (thin api_v1 wrappers — the LLM-driven path) ─────────
@@ -234,16 +203,18 @@ export async function cmdSession(sub, positionals, opts) {
234
203
  if (sub === "start") {
235
204
  const slugOrId = positionals[0];
236
205
  if (!slugOrId) {
237
- const e = new Error("usage: agentrelay session start <slug|capability_id> [--initial json] [--message m]");
206
+ const e = new Error("usage: agentrelay session start <slug|agent_id> [--initial json] [--message m]");
238
207
  e.usage = true;
239
208
  throw e;
240
209
  }
241
210
  const resolved = await resolveCapability(slugOrId, opts);
242
211
  const start = await sessionStart(
243
212
  {
244
- capabilityId: resolved.capabilityId,
245
- adUnitId: resolved.adUnitId,
246
- clearingPriceCents: resolved.clearingPriceCents,
213
+ agentId: resolved.agentId,
214
+ // A2A current-task brief → forwarded to /session/start → /initiate, where it's
215
+ // stored on capability_sessions.task_context and rendered as the business agent's
216
+ // "## Current task" prompt block. Optional on the CLI path (server stores null when absent).
217
+ taskContext: opts.taskContext || null,
247
218
  initialData: parseJsonFlag(opts.initial, "initial"),
248
219
  },
249
220
  net,
@@ -56,6 +56,7 @@ const FLAGS_WITH_VALUE = new Set([
56
56
  "--max",
57
57
  "--user-context",
58
58
  "--initial",
59
+ "--task-context",
59
60
  "--message",
60
61
  "--data",
61
62
  "--score",
@@ -141,25 +142,18 @@ function fail(message, exitCode, json) {
141
142
  const fmtSearch = (e) => {
142
143
  const lines = [`${e.count} capabilities for "${e.query}"${e.from_cache ? ` (cached ${e.cached_at})` : ""}`];
143
144
  for (const c of e.capabilities) {
144
- const price = c.clearing_price_cents ? `$${(c.clearing_price_cents / 100).toFixed(2)}` : "free";
145
145
  const rel = c.relevance != null ? c.relevance.toFixed(2) : "—";
146
- const kind = c.type === "static_offer" ? "[offer]" : "[agent]";
146
+ const rep = c.reputation != null ? c.reputation.toFixed(2) : "";
147
147
  const desc = c.description.length > 90 ? c.description.slice(0, 90) + "…" : c.description;
148
- lines.push(` ${kind} ${c.slug} (rel ${rel}, ${price}) ${c.name}`);
148
+ lines.push(` [agent] ${c.slug} (rel ${rel}, rep ${rep}, ${c.sessions_count ?? 0} sessions/${c.total_agent_ratings ?? 0} ratings) ${c.name}`);
149
149
  if (desc) lines.push(` ${desc}`);
150
- // Track-record evidence card (when the capability has a roller profile). --json
151
- // carries the full structured fields; this is the scannable human digest.
152
- if (Array.isArray(c.proven_for) && c.proven_for.length) {
153
- lines.push(` proven: ${c.proven_for.map((p) => p.use_case).join("; ")}`);
150
+ if (Array.isArray(c.required_inputs) && c.required_inputs.length) {
151
+ lines.push(` needs: ${c.required_inputs.map((i) => i.field).join(", ")}`);
154
152
  }
155
- if (Array.isArray(c.watch_out) && c.watch_out.length) {
156
- lines.push(` watch: ${c.watch_out.map((w) => w.use_case).join("; ")}`);
153
+ if (c.offers_paid_deliverable) {
154
+ const dp = typeof c.deliverable_price_cents === "number" ? ` ($${(c.deliverable_price_cents / 100).toFixed(2)})` : "";
155
+ lines.push(` offers paid deliverable${dp}`);
157
156
  }
158
- if (Array.isArray(c.works_with) && c.works_with.length) {
159
- const flag = (r) => (r === "conflicts" || r === "requires" ? `${r}!` : r);
160
- lines.push(` works-with: ${c.works_with.map((w) => `${w.companion_tool} (${flag(w.relationship)})`).join(", ")}`);
161
- }
162
- if (c.offers_paid_deliverable) lines.push(` offers paid deliverable`);
163
157
  }
164
158
  // Surface the spend policy in human output too — agents reading --json get
165
159
  // the full payment_context object; humans shouldn't be blind to it.
@@ -289,6 +283,7 @@ async function main() {
289
283
  const payload = await cmdSession(positionals[0], positionals.slice(1), {
290
284
  ...shared,
291
285
  initial: flags.initial || null,
286
+ taskContext: flags["task-context"] || null,
292
287
  message: flags.message || null,
293
288
  data: flags.data || null,
294
289
  credential: flags.credential || null,
@@ -27,7 +27,7 @@ test("listSpecHeadlines: returns slug+name+summary+feature-count, no index.json
27
27
  withCache((dir, cache) => {
28
28
  cache.writeSpec("a-tool", {
29
29
  slug: "a-tool", name: "A", summary: "does A",
30
- features: [{ feature: "x", how: "y" }], capability_id: "cap-a", ts: ISO,
30
+ features: [{ feature: "x", how: "y" }], agent_id: "agent-a", ts: ISO,
31
31
  });
32
32
  cache.writeSpec("b-tool", { slug: "b-tool", name: "B", summary: "does B", features: [], ts: ISO });
33
33
 
@@ -37,7 +37,7 @@ test("listSpecHeadlines: returns slug+name+summary+feature-count, no index.json
37
37
  assert.equal(a.name, "A");
38
38
  assert.equal(a.summary, "does A");
39
39
  assert.equal(a.features, 1);
40
- assert.equal(a.capability_id, "cap-a");
40
+ assert.equal(a.agent_id, "agent-a");
41
41
  // Race-safe: the put path never wrote a shared index.json.
42
42
  assert.equal(existsSync(join(dir, "index.json")), false);
43
43
  });
@@ -54,17 +54,15 @@ test("listSpecHeadlines: tolerates a spec missing the optional fields", () => {
54
54
  });
55
55
  });
56
56
 
57
- test("resolveCapability: falls back to the banked spec's ids (no network)", async () => {
57
+ test("resolveCapability: falls back to the banked spec's agent_id (no network)", async () => {
58
58
  await withCache(async (dir, cache) => {
59
59
  cache.writeSpec("a-tool", {
60
60
  slug: "a-tool", name: "A Tool", summary: "s", features: [],
61
- capability_id: "cap-a", ad_unit_id: "unit-a", clearing_price_cents: 130, ts: ISO,
61
+ agent_id: "agent-a", ts: ISO,
62
62
  });
63
63
  // No index.json, refresh falsy → must resolve from the spec file alone.
64
64
  const r = await resolveCapability("a-tool", { cacheDir: dir, noCache: false });
65
- assert.equal(r.capabilityId, "cap-a");
66
- assert.equal(r.adUnitId, "unit-a");
67
- assert.equal(r.clearingPriceCents, 130);
65
+ assert.equal(r.agentId, "agent-a");
68
66
  assert.equal(r.slug, "a-tool");
69
67
  assert.equal(r.name, "A Tool");
70
68
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-relay",
3
- "version": "4.2.1",
3
+ "version": "4.3.1",
4
4
  "description": "Install Agent Relay AI search across 17+ agents — Claude Code, Codex, Cursor, Gemini CLI, Goose, Windsurf, Cline, BoltAI, Claude Desktop, VS Code, Amazon Q, Roo Code, Witsy, LibreChat, OpenClaw, Tome, Raycast — plus the `agentrelay` CLI for live agent sessions, payments, and deliverable fetch",
5
5
  "bin": {
6
6
  "agentrelay": "bin/cli.mjs"
@@ -39,9 +39,7 @@
39
39
  }
40
40
  }
41
41
  },
42
- "capability_id": { "type": ["string", "null"], "description": "So the main agent can open a depth session." },
43
- "ad_unit_id": { "type": ["string", "null"] },
44
- "clearing_price_cents": { "type": "integer" },
42
+ "agent_id": { "type": ["string", "null"], "description": "The only id needed to open a session (ad_unit + price resolved server-side)." },
45
43
  "ts": { "type": "string", "format": "date-time" }
46
44
  }
47
45
  }