@multi-agent-protocol/sdk 0.1.9 → 0.1.11

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/dist/server.cjs CHANGED
@@ -4639,6 +4639,11 @@ var InMemoryAgentStore = class {
4639
4639
  );
4640
4640
  if (!match) continue;
4641
4641
  }
4642
+ if (filter?.persistentId !== void 0) {
4643
+ if (agent.persistentIdentity?.persistentId !== filter.persistentId) {
4644
+ continue;
4645
+ }
4646
+ }
4642
4647
  results.push({ ...agent });
4643
4648
  }
4644
4649
  return results;
@@ -4767,7 +4772,8 @@ var AgentRegistryImpl = class {
4767
4772
  registeredAt: now,
4768
4773
  lastStateChange: now,
4769
4774
  capabilities: params.capabilities,
4770
- capabilityDescriptor: params.capabilityDescriptor
4775
+ capabilityDescriptor: params.capabilityDescriptor,
4776
+ persistentIdentity: params.persistentIdentity
4771
4777
  };
4772
4778
  this.store.save(agent);
4773
4779
  this.eventBus.emit({
@@ -4862,6 +4868,38 @@ var AgentRegistryImpl = class {
4862
4868
  });
4863
4869
  return updatedAgent;
4864
4870
  }
4871
+ /**
4872
+ * Resume an orphaned agent under a new session.
4873
+ * Updates sessionId, resets state to idle, optionally merges metadata,
4874
+ * and optionally updates the persistent identity (e.g., with fresh proof).
4875
+ * Emits agent.resumed event.
4876
+ */
4877
+ resume(id, newSessionId, metadata, persistentIdentity) {
4878
+ const agent = this.store.get(id);
4879
+ if (!agent) {
4880
+ throw new AgentNotFoundError(id);
4881
+ }
4882
+ const previousSessionId = agent.sessionId;
4883
+ const now = Date.now();
4884
+ const resumedAgent = {
4885
+ ...agent,
4886
+ sessionId: newSessionId,
4887
+ state: "idle",
4888
+ lastStateChange: now,
4889
+ metadata: metadata ? { ...agent.metadata, ...metadata } : agent.metadata,
4890
+ ...persistentIdentity && { persistentIdentity }
4891
+ };
4892
+ this.store.save(resumedAgent);
4893
+ this.eventBus.emit({
4894
+ type: "agent.resumed",
4895
+ data: {
4896
+ agent: resumedAgent,
4897
+ previousSessionId
4898
+ },
4899
+ source: { agentId: id, sessionId: newSessionId }
4900
+ });
4901
+ return resumedAgent;
4902
+ }
4865
4903
  /**
4866
4904
  * Unregister all agents for a session.
4867
4905
  * Used for cleanup when a session disconnects.
@@ -5168,17 +5206,137 @@ var MAPRequestError = class _MAPRequestError extends Error {
5168
5206
 
5169
5207
  // src/server/agents/handlers.ts
5170
5208
  function createAgentHandlers(options) {
5171
- const { agents, sessions, authManager } = options;
5209
+ const { agents, sessions, authManager, eventBus, identityVerifier, uniqueIdentity } = options;
5172
5210
  return {
5173
5211
  "map/agents/register": async (params, ctx) => {
5174
- const { name, role, metadata, capabilities, capabilityDescriptor } = params;
5212
+ const { name, role, metadata, capabilities, capabilityDescriptor, persistentIdentity, resumePersistentIdentity } = params;
5213
+ if (!name || typeof name !== "string" || name.trim() === "") {
5214
+ throw new Error("Agent name is required");
5215
+ }
5216
+ let resolvedIdentity = persistentIdentity;
5217
+ if (!resolvedIdentity) {
5218
+ const tokenData = ctx.session.providers?.["agent-iam"]?.providerData;
5219
+ if (tokenData?.persistentIdentity) {
5220
+ const pi = tokenData.persistentIdentity;
5221
+ if (pi.persistentId && pi.identityType) {
5222
+ resolvedIdentity = {
5223
+ persistentId: pi.persistentId,
5224
+ identityType: pi.identityType,
5225
+ publicKey: pi.publicKey,
5226
+ publicKeyJwk: pi.publicKeyJwk,
5227
+ // Token stores proof and challenge as separate strings;
5228
+ // map to AgentIdentityProof object
5229
+ proof: pi.proof && pi.challenge ? {
5230
+ challenge: pi.challenge,
5231
+ signature: pi.proof,
5232
+ provenAt: (/* @__PURE__ */ new Date()).toISOString()
5233
+ } : void 0,
5234
+ verificationStatus: "auth-derived"
5235
+ };
5236
+ }
5237
+ }
5238
+ }
5239
+ if (resolvedIdentity) {
5240
+ if (!resolvedIdentity.persistentId || typeof resolvedIdentity.persistentId !== "string" || resolvedIdentity.persistentId.trim() === "") {
5241
+ throw new Error("persistentIdentity.persistentId must be a non-empty string");
5242
+ }
5243
+ if (!resolvedIdentity.identityType || typeof resolvedIdentity.identityType !== "string") {
5244
+ throw new Error("persistentIdentity.identityType is required");
5245
+ }
5246
+ }
5247
+ if (resolvedIdentity && identityVerifier) {
5248
+ const verificationContext = {
5249
+ sessionId: ctx.session.id,
5250
+ agentName: name,
5251
+ isResumption: !!resumePersistentIdentity
5252
+ };
5253
+ try {
5254
+ const status = await identityVerifier.verify(resolvedIdentity, verificationContext);
5255
+ if (status) {
5256
+ resolvedIdentity = { ...resolvedIdentity, verificationStatus: status };
5257
+ }
5258
+ } catch (verifyError) {
5259
+ resolvedIdentity = { ...resolvedIdentity, verificationStatus: "unverified" };
5260
+ if (eventBus) {
5261
+ eventBus.emit({
5262
+ type: "agent.identity.verification.failed",
5263
+ data: {
5264
+ persistentId: resolvedIdentity.persistentId,
5265
+ identityType: resolvedIdentity.identityType,
5266
+ error: verifyError instanceof Error ? verifyError.message : String(verifyError)
5267
+ },
5268
+ source: { sessionId: ctx.session.id }
5269
+ });
5270
+ }
5271
+ }
5272
+ }
5273
+ const persistentId = resolvedIdentity?.persistentId;
5274
+ if (persistentId) {
5275
+ const existingAgents = agents.list({ persistentId });
5276
+ const activeAgents = existingAgents.filter((a) => a.state !== "stopped");
5277
+ if (resumePersistentIdentity && activeAgents.length > 0) {
5278
+ const sorted = activeAgents.sort((a, b) => b.lastStateChange - a.lastStateChange);
5279
+ const resumeTarget = sorted[0];
5280
+ try {
5281
+ if (sessions) {
5282
+ try {
5283
+ if (resumeTarget.sessionId !== ctx.session.id) {
5284
+ sessions.removeAgent(resumeTarget.sessionId, resumeTarget.id);
5285
+ }
5286
+ } catch {
5287
+ }
5288
+ sessions.addAgent(ctx.session.id, resumeTarget.id);
5289
+ } else {
5290
+ ctx.session.agentIds.push(resumeTarget.id);
5291
+ }
5292
+ const previousSessionId = resumeTarget.sessionId;
5293
+ const resumedAgent = agents.resume(
5294
+ resumeTarget.id,
5295
+ ctx.session.id,
5296
+ {
5297
+ ...metadata || {},
5298
+ resumedAt: Date.now(),
5299
+ resumedFromSession: previousSessionId
5300
+ },
5301
+ resolvedIdentity
5302
+ );
5303
+ return {
5304
+ agent: {
5305
+ id: resumedAgent.id,
5306
+ name: resumedAgent.name,
5307
+ role: resumedAgent.role,
5308
+ state: resumedAgent.state,
5309
+ metadata: resumedAgent.metadata,
5310
+ capabilities: resumedAgent.capabilities,
5311
+ capabilityDescriptor: resumedAgent.capabilityDescriptor,
5312
+ persistentIdentity: resumedAgent.persistentIdentity,
5313
+ visibility: "public"
5314
+ },
5315
+ resumed: true,
5316
+ resumedFrom: {
5317
+ previousSessionId
5318
+ }
5319
+ };
5320
+ } catch (resumeError) {
5321
+ if (resumeError instanceof AgentNotFoundError) ; else {
5322
+ throw resumeError;
5323
+ }
5324
+ }
5325
+ }
5326
+ if (uniqueIdentity && activeAgents.length > 0) {
5327
+ throw new Error(
5328
+ `An active agent with persistentId "${persistentId}" already exists. Use resumePersistentIdentity: true to resume it, or unregister the existing agent first.`
5329
+ );
5330
+ }
5331
+ }
5175
5332
  const registeredAgent = agents.register({
5176
5333
  name,
5177
5334
  role,
5178
5335
  metadata,
5179
5336
  sessionId: ctx.session.id,
5180
5337
  capabilities,
5181
- capabilityDescriptor
5338
+ capabilityDescriptor,
5339
+ persistentIdentity: resolvedIdentity
5182
5340
  });
5183
5341
  if (sessions) {
5184
5342
  sessions.addAgent(ctx.session.id, registeredAgent.id);
@@ -5194,6 +5352,7 @@ function createAgentHandlers(options) {
5194
5352
  metadata: registeredAgent.metadata,
5195
5353
  capabilities: registeredAgent.capabilities,
5196
5354
  capabilityDescriptor: registeredAgent.capabilityDescriptor,
5355
+ persistentIdentity: registeredAgent.persistentIdentity,
5197
5356
  visibility: "public"
5198
5357
  }
5199
5358
  };
@@ -5230,6 +5389,7 @@ function createAgentHandlers(options) {
5230
5389
  metadata: a.metadata,
5231
5390
  capabilities: a.capabilities,
5232
5391
  capabilityDescriptor: a.capabilityDescriptor,
5392
+ persistentIdentity: a.persistentIdentity,
5233
5393
  visibility: "public"
5234
5394
  }))
5235
5395
  };
@@ -5249,6 +5409,7 @@ function createAgentHandlers(options) {
5249
5409
  metadata: agent.metadata,
5250
5410
  capabilities: agent.capabilities,
5251
5411
  capabilityDescriptor: agent.capabilityDescriptor,
5412
+ persistentIdentity: agent.persistentIdentity,
5252
5413
  visibility: "public"
5253
5414
  }
5254
5415
  };
@@ -5274,6 +5435,7 @@ function createAgentHandlers(options) {
5274
5435
  metadata: agent.metadata,
5275
5436
  capabilities: agent.capabilities,
5276
5437
  capabilityDescriptor: agent.capabilityDescriptor,
5438
+ persistentIdentity: agent.persistentIdentity,
5277
5439
  visibility: "public"
5278
5440
  }
5279
5441
  };
@@ -5323,7 +5485,8 @@ function createAgentHandlers(options) {
5323
5485
  parentId: parent
5324
5486
  },
5325
5487
  sessionId: ctx.session.id,
5326
- capabilityDescriptor
5488
+ capabilityDescriptor,
5489
+ persistentIdentity: parentAgent.persistentIdentity
5327
5490
  });
5328
5491
  if (sessions) {
5329
5492
  sessions.addAgent(ctx.session.id, childAgent.id);
@@ -5356,6 +5519,7 @@ function createAgentHandlers(options) {
5356
5519
  state: childAgent.state,
5357
5520
  metadata: childAgent.metadata,
5358
5521
  capabilityDescriptor: childAgent.capabilityDescriptor,
5522
+ persistentIdentity: childAgent.persistentIdentity,
5359
5523
  visibility: "public"
5360
5524
  }
5361
5525
  };
@@ -7583,94 +7747,113 @@ function toScope(scopeId) {
7583
7747
  }
7584
7748
 
7585
7749
  // src/server/messages/handlers.ts
7750
+ function resolveRouteTarget(to) {
7751
+ if (typeof to === "string") {
7752
+ return { type: "string", value: to };
7753
+ }
7754
+ if (Array.isArray(to)) {
7755
+ return to.map((item) => resolveRouteTarget(item));
7756
+ }
7757
+ if (to && typeof to === "object") {
7758
+ const obj = to;
7759
+ if (typeof obj.agent === "string" && !obj.system) {
7760
+ return { type: "agent", id: obj.agent };
7761
+ }
7762
+ if (Array.isArray(obj.agents)) {
7763
+ return { type: "agents", ids: obj.agents };
7764
+ }
7765
+ if (typeof obj.scope === "string") {
7766
+ return { type: "scope", id: obj.scope };
7767
+ }
7768
+ if (obj.broadcast === true) {
7769
+ return { type: "broadcast" };
7770
+ }
7771
+ if (typeof obj.role === "string") {
7772
+ if (typeof obj.within === "string") {
7773
+ return { type: "scope", id: obj.within };
7774
+ }
7775
+ return { type: "broadcast" };
7776
+ }
7777
+ if (obj.system === true) {
7778
+ return { type: "broadcast" };
7779
+ }
7780
+ if (typeof obj.system === "string" && typeof obj.agent === "string") {
7781
+ return { type: "agent", id: obj.agent };
7782
+ }
7783
+ if (typeof obj.participant === "string") {
7784
+ return { type: "agent", id: obj.participant };
7785
+ }
7786
+ if (obj.participants === "all" || obj.participants === "agents") {
7787
+ return { type: "broadcast" };
7788
+ }
7789
+ if (obj.parent || obj.children || obj.ancestors || obj.descendants || obj.siblings) {
7790
+ return { type: "broadcast" };
7791
+ }
7792
+ }
7793
+ return { type: "string", value: String(to) };
7794
+ }
7586
7795
  function createMessageHandlers(options) {
7587
7796
  const { messages, scopes, agents, turns } = options;
7797
+ function sendToAgent(from, to, params) {
7798
+ return messages.sendToAgent({
7799
+ from,
7800
+ to,
7801
+ payload: params.payload,
7802
+ replyTo: params.replyTo,
7803
+ priority: params.priority,
7804
+ ttlMs: params.ttlMs,
7805
+ messageType: params.messageType
7806
+ });
7807
+ }
7808
+ function sendToScope(from, scopeId, params) {
7809
+ return messages.sendToScope({
7810
+ from,
7811
+ scopeId,
7812
+ payload: params.payload,
7813
+ excludeSender: true,
7814
+ messageType: params.messageType
7815
+ });
7816
+ }
7817
+ function resolveStringAddress(from, to, params) {
7818
+ if (isAddress(to)) {
7819
+ if (isScopeAddress(to)) {
7820
+ const message3 = sendToScope(from, extractId(to), params);
7821
+ return { messageId: message3.id };
7822
+ } else {
7823
+ const message3 = sendToAgent(from, extractId(to), params);
7824
+ return { messageId: message3.id };
7825
+ }
7826
+ }
7827
+ const scope = scopes.get(to);
7828
+ const agent = agents?.get(to);
7829
+ if (scope && agent) {
7830
+ throw new Error(
7831
+ `Ambiguous address "${to}" matches both an agent and a scope. Use "agent:${to}" or "scope:${to}" to disambiguate.`
7832
+ );
7833
+ }
7834
+ if (scope) {
7835
+ const message3 = sendToScope(from, to, params);
7836
+ return { messageId: message3.id };
7837
+ }
7838
+ const message2 = sendToAgent(from, to, params);
7839
+ return { messageId: message2.id };
7840
+ }
7588
7841
  return {
7589
7842
  "map/send": async (params, ctx) => {
7590
- const { to, payload, replyTo, priority, ttlMs, messageType, meta } = params;
7843
+ const sendParams = params;
7844
+ const { to: rawTo, meta } = sendParams;
7591
7845
  const from = ctx.session.agentIds[0] ?? ctx.session.id;
7846
+ const target = resolveRouteTarget(rawTo);
7592
7847
  let result;
7593
- if (Array.isArray(to)) {
7848
+ if (Array.isArray(target)) {
7594
7849
  const results = [];
7595
- for (const recipient of to) {
7596
- if (isScopeAddress(recipient)) {
7597
- const scopeId = extractId(recipient);
7598
- const message2 = messages.sendToScope({
7599
- from,
7600
- scopeId,
7601
- payload,
7602
- excludeSender: true,
7603
- messageType
7604
- });
7605
- results.push(message2.id);
7606
- } else {
7607
- const agentId = isAgentAddress(recipient) ? extractId(recipient) : recipient;
7608
- const message2 = messages.sendToAgent({
7609
- from,
7610
- to: agentId,
7611
- payload,
7612
- replyTo,
7613
- priority,
7614
- ttlMs,
7615
- messageType
7616
- });
7617
- results.push(message2.id);
7618
- }
7850
+ for (const t of target) {
7851
+ const { messageId } = routeSingle(from, t, sendParams);
7852
+ results.push(messageId);
7619
7853
  }
7620
7854
  result = { messageId: results[0], delivered: results };
7621
- } else if (isAddress(to)) {
7622
- if (isScopeAddress(to)) {
7623
- const scopeId = extractId(to);
7624
- const message2 = messages.sendToScope({
7625
- from,
7626
- scopeId,
7627
- payload,
7628
- excludeSender: true,
7629
- messageType
7630
- });
7631
- result = { messageId: message2.id };
7632
- } else {
7633
- const agentId = extractId(to);
7634
- const message2 = messages.sendToAgent({
7635
- from,
7636
- to: agentId,
7637
- payload,
7638
- replyTo,
7639
- priority,
7640
- ttlMs,
7641
- messageType
7642
- });
7643
- result = { messageId: message2.id };
7644
- }
7645
7855
  } else {
7646
- const scope = scopes.get(to);
7647
- const agent = agents?.get(to);
7648
- if (scope && agent) {
7649
- throw new Error(
7650
- `Ambiguous address "${to}" matches both an agent and a scope. Use "agent:${to}" or "scope:${to}" to disambiguate.`
7651
- );
7652
- }
7653
- if (scope) {
7654
- const message2 = messages.sendToScope({
7655
- from,
7656
- scopeId: to,
7657
- payload,
7658
- excludeSender: true,
7659
- messageType
7660
- });
7661
- result = { messageId: message2.id };
7662
- } else {
7663
- const message2 = messages.sendToAgent({
7664
- from,
7665
- to,
7666
- payload,
7667
- replyTo,
7668
- priority,
7669
- ttlMs,
7670
- messageType
7671
- });
7672
- result = { messageId: message2.id };
7673
- }
7856
+ result = routeSingle(from, target, sendParams);
7674
7857
  }
7675
7858
  if (turns && meta?.mail?.conversationId) {
7676
7859
  try {
@@ -7678,7 +7861,7 @@ function createMessageHandlers(options) {
7678
7861
  conversationId: meta.mail.conversationId,
7679
7862
  participant: from,
7680
7863
  contentType: "data",
7681
- content: payload,
7864
+ content: sendParams.payload,
7682
7865
  messageId: result.messageId,
7683
7866
  threadId: meta.mail.threadId,
7684
7867
  inReplyTo: meta.mail.inReplyTo,
@@ -7703,6 +7886,38 @@ function createMessageHandlers(options) {
7703
7886
  return { messageId: message2.id };
7704
7887
  }
7705
7888
  };
7889
+ function routeSingle(from, target, params) {
7890
+ switch (target.type) {
7891
+ case "agent": {
7892
+ const message2 = sendToAgent(from, target.id, params);
7893
+ return { messageId: message2.id };
7894
+ }
7895
+ case "agents": {
7896
+ const results = [];
7897
+ for (const agentId of target.ids) {
7898
+ const message2 = sendToAgent(from, agentId, params);
7899
+ results.push(message2.id);
7900
+ }
7901
+ return { messageId: results[0] };
7902
+ }
7903
+ case "scope": {
7904
+ const message2 = sendToScope(from, target.id, params);
7905
+ return { messageId: message2.id };
7906
+ }
7907
+ case "broadcast": {
7908
+ const message2 = messages.sendToAgent({
7909
+ from,
7910
+ to: "*",
7911
+ payload: params.payload,
7912
+ messageType: params.messageType
7913
+ });
7914
+ return { messageId: message2.id };
7915
+ }
7916
+ case "string": {
7917
+ return resolveStringAddress(from, target.value, params);
7918
+ }
7919
+ }
7920
+ }
7706
7921
  }
7707
7922
 
7708
7923
  // src/server/mail/stores/in-memory-conversation.ts
@@ -9977,6 +10192,9 @@ function createCredentialHandlers(options) {
9977
10192
  type: "credential.denied",
9978
10193
  data: {
9979
10194
  agentId: token.agentId,
10195
+ ...token.persistentIdentity?.persistentId && {
10196
+ persistentId: token.persistentIdentity.persistentId
10197
+ },
9980
10198
  scope,
9981
10199
  resource,
9982
10200
  reason: check2.error
@@ -9992,6 +10210,9 @@ function createCredentialHandlers(options) {
9992
10210
  type: "credential.issued",
9993
10211
  data: {
9994
10212
  agentId: token.agentId,
10213
+ ...token.persistentIdentity?.persistentId && {
10214
+ persistentId: token.persistentIdentity.persistentId
10215
+ },
9995
10216
  scope,
9996
10217
  resource,
9997
10218
  credentialType: credential.credentialType,
@@ -10919,6 +11140,7 @@ var AgentIAMProvider = class {
10919
11140
  const principal = {
10920
11141
  id: token.agentId,
10921
11142
  issuer: token.identity?.systemId ?? "agent-iam",
11143
+ persistentId: token.persistentIdentity?.persistentId,
10922
11144
  claims: {
10923
11145
  scopes: token.scopes,
10924
11146
  delegatable: token.delegatable,
@@ -10947,7 +11169,8 @@ var AgentIAMProvider = class {
10947
11169
  requestedScopes: spawnRequest.requestedScopes ?? parentToken.scopes,
10948
11170
  delegatable: true,
10949
11171
  ttlMinutes: spawnRequest.ttlMinutes,
10950
- inheritIdentity: spawnRequest.inheritIdentity ?? true
11172
+ inheritIdentity: spawnRequest.inheritIdentity ?? true,
11173
+ inheritPersistentIdentity: spawnRequest.inheritIdentity ?? true
10951
11174
  });
10952
11175
  const serialized = this.#tokenService.serialize(childToken);
10953
11176
  return {
@@ -11044,6 +11267,7 @@ var AgentIAMFederationGateway = class {
11044
11267
  const principal = {
11045
11268
  id: `federated:${sourceSystemId}:${deserializedToken.agentId}`,
11046
11269
  issuer: sourceSystemId,
11270
+ persistentId: deserializedToken.persistentIdentity?.persistentId,
11047
11271
  claims: {
11048
11272
  scopes: translatedScopes,
11049
11273
  federatedFrom: sourceSystemId,
@@ -11112,7 +11336,8 @@ var AgentIAMFederationGateway = class {
11112
11336
  requestedScopes: translatedScopes,
11113
11337
  delegatable: false,
11114
11338
  ttlMinutes: this.#federatedTTL,
11115
- inheritIdentity: true
11339
+ inheritIdentity: true,
11340
+ inheritPersistentIdentity: true
11116
11341
  });
11117
11342
  const withFederation = {
11118
11343
  ...delegated,
@@ -12837,6 +13062,13 @@ var FederatedAgentRegistry = class {
12837
13062
  }
12838
13063
  return unregisteredIds;
12839
13064
  }
13065
+ /**
13066
+ * Resume an orphaned agent under a new session.
13067
+ * Delegates to the local registry.
13068
+ */
13069
+ resume(id, newSessionId, metadata, persistentIdentity) {
13070
+ return this.local.resume(id, newSessionId, metadata, persistentIdentity);
13071
+ }
12840
13072
  /**
12841
13073
  * Check if an agent is remote.
12842
13074
  */