@kybernesis/dispatch 0.3.2 → 0.5.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.
@@ -33,15 +33,40 @@ async function discover(options) {
33
33
  return hit?.peers ?? [];
34
34
  }
35
35
  }
36
+ /**
37
+ * Who is asking, from the verified session principal.
38
+ *
39
+ * The agent's own door authenticated this; the model did not choose it and
40
+ * cannot set it, which is the only reason it is worth sending at all. Undefined
41
+ * for an unattended turn — a schedule, a webhook — where there is no person.
42
+ *
43
+ * This travels to the peer as an ASSERTION, not as authentication. The peer
44
+ * learns who prompted the call and can address them by name or decline work
45
+ * that is not theirs to do, but the call still carries this agent's authority,
46
+ * not the person's. A peer must never widen what it will do because of this
47
+ * value: the caller could claim anything, and the control plane only records
48
+ * the claim. Reaching a peer AS the person is a different mechanism.
49
+ */
50
+ function askedBy(ctx) {
51
+ const current = ctx?.session?.auth?.current;
52
+ const id = current?.principalId;
53
+ if (!id)
54
+ return undefined;
55
+ const attributes = current?.attributes ?? {};
56
+ const label = ["email", "user_name", "name"]
57
+ .map((key) => attributes[key])
58
+ .find((value) => typeof value === "string" && value.length > 0);
59
+ return label ? { id, label } : { id };
60
+ }
36
61
  /** Mint a short-lived token for one callee, then hold the conversation. */
37
- async function ask(options, peer, message) {
62
+ async function ask(options, peer, message, asker) {
38
63
  const { issuer, credential } = config(options);
39
64
  if (!credential)
40
65
  throw new Error("This agent has no control-plane credential, so it cannot call another agent.");
41
66
  const minted = await fetch(`${issuer}/api/agent/session`, {
42
67
  method: "POST",
43
68
  headers: { "content-type": "application/json", authorization: `Bearer ${credential}` },
44
- body: JSON.stringify({ callee: peer.name }),
69
+ body: JSON.stringify({ callee: peer.name, ...(asker ? { onBehalfOf: asker } : {}) }),
45
70
  signal: AbortSignal.timeout(20_000),
46
71
  });
47
72
  if (!minted.ok) {
@@ -176,7 +201,7 @@ export function governedPeers(options = {}) {
176
201
  .string()
177
202
  .describe("What to ask it. Write it as you would to a colleague — it has no view of this conversation."),
178
203
  }),
179
- execute: (input) => ask(options, peer, input.message),
204
+ execute: (input, ctx) => ask(options, peer, input.message, askedBy(ctx)),
180
205
  });
181
206
  }
182
207
  return tools;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/dispatch",
3
- "version": "0.3.2",
3
+ "version": "0.5.0",
4
4
  "description": "Agent-to-agent dispatch for eve agents: declared remote-peer edges with principal forwarding on by default, and a receiver channel that makes trusting the wrong caller hard to express.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -32,11 +32,11 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "24.x",
35
- "eve": "0.30.8",
35
+ "eve": "0.38.3",
36
36
  "typescript": "7.0.2"
37
37
  },
38
38
  "peerDependencies": {
39
- "eve": ">=0.30.0 <0.31.0",
39
+ "eve": ">=0.38.0 <0.39.0",
40
40
  "@kybernesis/enterprise": ">=0.2.0"
41
41
  },
42
42
  "engines": {