macro-agent 0.0.6 → 0.0.8
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/.claude/settings.local.json +2 -1
- package/README.md +67 -57
- package/dist/acp/index.d.ts +3 -3
- package/dist/acp/index.js +1 -1
- package/dist/acp/index.js.map +1 -1
- package/dist/cli/acp.d.ts +22 -20
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +134 -44
- package/dist/cli/acp.js.map +1 -1
- package/dist/cli/index.d.ts +5 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +6 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/map/adapter/acp-over-map.d.ts +77 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -0
- package/dist/map/adapter/acp-over-map.js +372 -0
- package/dist/map/adapter/acp-over-map.js.map +1 -0
- package/dist/map/adapter/index.d.ts +1 -0
- package/dist/map/adapter/index.d.ts.map +1 -1
- package/dist/map/adapter/index.js +2 -0
- package/dist/map/adapter/index.js.map +1 -1
- package/dist/map/adapter/interface.d.ts +22 -0
- package/dist/map/adapter/interface.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.d.ts +25 -1
- package/dist/map/adapter/map-adapter.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.js +147 -21
- package/dist/map/adapter/map-adapter.js.map +1 -1
- package/dist/map/adapter/rpc-handler.d.ts.map +1 -1
- package/dist/map/adapter/rpc-handler.js +4 -0
- package/dist/map/adapter/rpc-handler.js.map +1 -1
- package/dist/map/adapter/subscription-manager.js.map +1 -1
- package/dist/server/combined-server.d.ts.map +1 -1
- package/dist/server/combined-server.js +8 -1
- package/dist/server/combined-server.js.map +1 -1
- package/dist/store/event-store.js +7 -1
- package/dist/store/event-store.js.map +1 -1
- package/docs/configuration.md +27 -18
- package/docs/sudocode-integration.md +1 -1
- package/docs/troubleshooting.md +3 -3
- package/package.json +4 -4
- package/src/acp/index.ts +4 -4
- package/src/cli/__tests__/acp.test.ts +48 -40
- package/src/cli/acp.ts +149 -55
- package/src/cli/index.ts +6 -2
- package/src/map/adapter/acp-over-map.ts +516 -0
- package/src/map/adapter/index.ts +8 -0
- package/src/map/adapter/interface.ts +30 -6
- package/src/map/adapter/map-adapter.ts +325 -76
- package/src/map/adapter/rpc-handler.ts +4 -0
- package/src/map/adapter/subscription-manager.ts +10 -10
- package/src/server/combined-server.ts +8 -1
- package/src/store/event-store.ts +8 -2
- package/src/trigger/__tests__/wake-manager.test.ts +26 -9
|
@@ -38,11 +38,18 @@ import type {
|
|
|
38
38
|
SubscriptionId,
|
|
39
39
|
SubscriptionFilter,
|
|
40
40
|
EventNotification,
|
|
41
|
+
MAPEventType,
|
|
41
42
|
} from "./types.js";
|
|
42
|
-
import type
|
|
43
|
+
import { isAgentAddress, type Address, type SendOptions, type ScopeId } from "../types.js";
|
|
43
44
|
import type { AgentId } from "../../store/types/index.js";
|
|
44
|
-
import {
|
|
45
|
-
|
|
45
|
+
import {
|
|
46
|
+
createConnectionManager,
|
|
47
|
+
type ConnectionManager,
|
|
48
|
+
} from "./connection-manager.js";
|
|
49
|
+
import {
|
|
50
|
+
createSubscriptionManager,
|
|
51
|
+
type SubscriptionManager,
|
|
52
|
+
} from "./subscription-manager.js";
|
|
46
53
|
import {
|
|
47
54
|
RPCHandler,
|
|
48
55
|
createRPCHandler,
|
|
@@ -54,7 +61,13 @@ import {
|
|
|
54
61
|
type JsonRpcMessage,
|
|
55
62
|
} from "./rpc-handler.js";
|
|
56
63
|
import { EXTENSION_CAPABILITIES } from "./extensions/index.js";
|
|
57
|
-
import type {
|
|
64
|
+
import type {
|
|
65
|
+
FederationHandler,
|
|
66
|
+
FederationCapabilities,
|
|
67
|
+
ConnectedPeer,
|
|
68
|
+
MAPPeerConfig,
|
|
69
|
+
} from "../federation/types.js";
|
|
70
|
+
import { ACPOverMAPHandler, type ACPEnvelope } from "./acp-over-map.js";
|
|
58
71
|
|
|
59
72
|
// =============================================================================
|
|
60
73
|
// Connection Session
|
|
@@ -97,7 +110,7 @@ export interface MAPAdapterServices {
|
|
|
97
110
|
from: AgentId,
|
|
98
111
|
to: Address,
|
|
99
112
|
content: string,
|
|
100
|
-
options?: SendOptions
|
|
113
|
+
options?: SendOptions,
|
|
101
114
|
) => Promise<{ delivered: AgentId[] }>;
|
|
102
115
|
|
|
103
116
|
/**
|
|
@@ -114,6 +127,26 @@ export interface MAPAdapterServices {
|
|
|
114
127
|
* Optional federation handler for cross-system communication.
|
|
115
128
|
*/
|
|
116
129
|
federationHandler?: FederationHandler;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Full AgentManager reference for ACP-over-MAP support.
|
|
133
|
+
*/
|
|
134
|
+
agentManager?: import("../../agent/agent-manager.js").AgentManager;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Full EventStore reference for ACP-over-MAP support.
|
|
138
|
+
*/
|
|
139
|
+
eventStore?: import("../../store/event-store.js").EventStore;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Full TaskManager reference for ACP-over-MAP support.
|
|
143
|
+
*/
|
|
144
|
+
taskManager?: import("../../task/task-manager.js").TaskManager;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Default working directory for ACP sessions.
|
|
148
|
+
*/
|
|
149
|
+
defaultCwd?: string;
|
|
117
150
|
}
|
|
118
151
|
|
|
119
152
|
/**
|
|
@@ -161,10 +194,16 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
161
194
|
private readonly scopes: Map<ScopeId, ScopeState> = new Map();
|
|
162
195
|
private readonly eventHandlers: Set<AdapterEventHandler> = new Set();
|
|
163
196
|
private readonly services: MAPAdapterServices;
|
|
197
|
+
private readonly acpOverMapHandler: ACPOverMAPHandler | null = null;
|
|
198
|
+
/** Sequence numbers per subscription for proper event ordering */
|
|
199
|
+
private readonly subscriptionSequences: Map<SubscriptionId, number> = new Map();
|
|
164
200
|
|
|
165
201
|
private running = false;
|
|
166
202
|
|
|
167
|
-
constructor(
|
|
203
|
+
constructor(
|
|
204
|
+
config: MAPAdapterConfig = {},
|
|
205
|
+
services: MAPAdapterServices = {},
|
|
206
|
+
) {
|
|
168
207
|
this.config = {
|
|
169
208
|
name: config.name ?? "macro-agent",
|
|
170
209
|
version: config.version ?? "1.0.0",
|
|
@@ -172,6 +211,17 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
172
211
|
};
|
|
173
212
|
this.services = services;
|
|
174
213
|
|
|
214
|
+
// Initialize ACP-over-MAP handler if services are available
|
|
215
|
+
if (services.agentManager && services.eventStore && services.taskManager) {
|
|
216
|
+
this.acpOverMapHandler = new ACPOverMAPHandler({
|
|
217
|
+
agentManager: services.agentManager,
|
|
218
|
+
eventStore: services.eventStore,
|
|
219
|
+
taskManager: services.taskManager,
|
|
220
|
+
defaultCwd: services.defaultCwd,
|
|
221
|
+
});
|
|
222
|
+
console.error("[MAPAdapter] ACP-over-MAP handler initialized");
|
|
223
|
+
}
|
|
224
|
+
|
|
175
225
|
// Initialize connection manager
|
|
176
226
|
this.connections = createConnectionManager({
|
|
177
227
|
limits: config.limits,
|
|
@@ -264,7 +314,10 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
264
314
|
return participant;
|
|
265
315
|
}
|
|
266
316
|
|
|
267
|
-
async disconnectParticipant(
|
|
317
|
+
async disconnectParticipant(
|
|
318
|
+
id: ParticipantId,
|
|
319
|
+
reason?: string,
|
|
320
|
+
): Promise<void> {
|
|
268
321
|
const session = this.sessions.get(id);
|
|
269
322
|
if (!session) {
|
|
270
323
|
return;
|
|
@@ -308,7 +361,7 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
308
361
|
participantId: ParticipantId,
|
|
309
362
|
to: Address,
|
|
310
363
|
payload: MessagePayload,
|
|
311
|
-
options?: SendOptions
|
|
364
|
+
options?: SendOptions,
|
|
312
365
|
): Promise<SendResult> {
|
|
313
366
|
const participant = this.connections.getParticipant(participantId);
|
|
314
367
|
if (!participant) {
|
|
@@ -329,8 +382,10 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
329
382
|
const result = await this.services.sendMessage(
|
|
330
383
|
senderId,
|
|
331
384
|
to,
|
|
332
|
-
typeof payload.content === "string"
|
|
333
|
-
|
|
385
|
+
typeof payload.content === "string"
|
|
386
|
+
? payload.content
|
|
387
|
+
: JSON.stringify(payload.content),
|
|
388
|
+
options,
|
|
334
389
|
);
|
|
335
390
|
|
|
336
391
|
return {
|
|
@@ -345,7 +400,7 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
345
400
|
|
|
346
401
|
async createSubscription(
|
|
347
402
|
participantId: ParticipantId,
|
|
348
|
-
filter?: SubscriptionFilter
|
|
403
|
+
filter?: SubscriptionFilter,
|
|
349
404
|
): Promise<SubscriptionId> {
|
|
350
405
|
const participant = this.connections.getParticipant(participantId);
|
|
351
406
|
if (!participant) {
|
|
@@ -361,6 +416,7 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
361
416
|
|
|
362
417
|
async removeSubscription(subscriptionId: SubscriptionId): Promise<void> {
|
|
363
418
|
this.subscriptions.unsubscribe(subscriptionId);
|
|
419
|
+
this.subscriptionSequences.delete(subscriptionId);
|
|
364
420
|
}
|
|
365
421
|
|
|
366
422
|
getSubscriptions(participantId: ParticipantId): SubscriptionId[] {
|
|
@@ -397,7 +453,10 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
397
453
|
return agents.map((agent) => this.toAgentInfo(agent));
|
|
398
454
|
}
|
|
399
455
|
|
|
400
|
-
getAgent(
|
|
456
|
+
getAgent(
|
|
457
|
+
participantId: ParticipantId,
|
|
458
|
+
agentId: AgentId,
|
|
459
|
+
): AgentInfo | undefined {
|
|
401
460
|
const participant = this.connections.getParticipant(participantId);
|
|
402
461
|
if (!participant) {
|
|
403
462
|
return undefined;
|
|
@@ -425,10 +484,15 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
425
484
|
return [];
|
|
426
485
|
}
|
|
427
486
|
|
|
428
|
-
return Array.from(this.scopes.values()).map((scope) =>
|
|
487
|
+
return Array.from(this.scopes.values()).map((scope) =>
|
|
488
|
+
this.toScopeInfo(scope),
|
|
489
|
+
);
|
|
429
490
|
}
|
|
430
491
|
|
|
431
|
-
getScope(
|
|
492
|
+
getScope(
|
|
493
|
+
participantId: ParticipantId,
|
|
494
|
+
scopeId: ScopeId,
|
|
495
|
+
): ScopeInfo | undefined {
|
|
432
496
|
const participant = this.connections.getParticipant(participantId);
|
|
433
497
|
if (!participant) {
|
|
434
498
|
return undefined;
|
|
@@ -448,13 +512,33 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
448
512
|
|
|
449
513
|
emitEvent(event: EventNotification): void {
|
|
450
514
|
// Match event against subscriptions
|
|
451
|
-
const { subscriptions: matchingSubs
|
|
515
|
+
const { subscriptions: matchingSubs } =
|
|
516
|
+
this.subscriptions.match(event);
|
|
452
517
|
|
|
453
|
-
// Send to each matched
|
|
454
|
-
|
|
455
|
-
|
|
518
|
+
// Send to each matched subscription with proper SDK format
|
|
519
|
+
// SDK EventNotificationParams: { subscriptionId, sequenceNumber, eventId?, timestamp?, event, causedBy? }
|
|
520
|
+
for (const subscription of matchingSubs) {
|
|
521
|
+
const session = this.sessions.get(subscription.participantId);
|
|
456
522
|
if (session) {
|
|
457
|
-
|
|
523
|
+
// Get and increment sequence number for this subscription
|
|
524
|
+
const currentSeq = this.subscriptionSequences.get(subscription.id) ?? 0;
|
|
525
|
+
this.subscriptionSequences.set(subscription.id, currentSeq + 1);
|
|
526
|
+
|
|
527
|
+
// Build params in SDK EventNotificationParams format
|
|
528
|
+
const params = {
|
|
529
|
+
subscriptionId: subscription.id,
|
|
530
|
+
sequenceNumber: currentSeq,
|
|
531
|
+
eventId: event.eventId, // For deduplication
|
|
532
|
+
timestamp: event.timestamp, // Envelope timestamp
|
|
533
|
+
event: { // The actual event object
|
|
534
|
+
id: event.eventId,
|
|
535
|
+
type: event.type,
|
|
536
|
+
timestamp: event.timestamp,
|
|
537
|
+
data: event.data,
|
|
538
|
+
},
|
|
539
|
+
...(event.causedBy && { causedBy: event.causedBy }),
|
|
540
|
+
};
|
|
541
|
+
const notification = createNotification("map/event", params);
|
|
458
542
|
this.sendToSession(session, notification).catch((error) => {
|
|
459
543
|
console.error("[MAPAdapter] Failed to send event:", error);
|
|
460
544
|
});
|
|
@@ -578,32 +662,46 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
578
662
|
private createRPCHandler(participantId: ParticipantId): RPCHandler {
|
|
579
663
|
const handlers: HandlerRegistry = {
|
|
580
664
|
// Connection
|
|
581
|
-
"map/connect": async (params) =>
|
|
665
|
+
"map/connect": async (params) =>
|
|
666
|
+
this.handleConnect(participantId, params),
|
|
582
667
|
"map/disconnect": async () => this.handleDisconnect(participantId),
|
|
583
668
|
|
|
584
669
|
// Subscriptions
|
|
585
|
-
"map/subscribe": async (params) =>
|
|
586
|
-
|
|
670
|
+
"map/subscribe": async (params) =>
|
|
671
|
+
this.handleSubscribe(participantId, params),
|
|
672
|
+
"map/unsubscribe": async (params) =>
|
|
673
|
+
this.handleUnsubscribe(participantId, params),
|
|
587
674
|
|
|
588
675
|
// Messaging
|
|
589
|
-
"map/send": async (params, ctx) =>
|
|
676
|
+
"map/send": async (params, ctx) =>
|
|
677
|
+
this.handleSend(participantId, params, ctx),
|
|
590
678
|
|
|
591
679
|
// Agent queries
|
|
592
|
-
"map/agents/list": async (params) =>
|
|
593
|
-
|
|
680
|
+
"map/agents/list": async (params) =>
|
|
681
|
+
this.handleListAgents(participantId, params),
|
|
682
|
+
"map/agents/get": async (params) =>
|
|
683
|
+
this.handleGetAgent(participantId, params),
|
|
594
684
|
|
|
595
685
|
// Scope queries
|
|
596
686
|
"map/scopes/list": async () => this.handleListScopes(participantId),
|
|
597
|
-
"map/scopes/get": async (params) =>
|
|
598
|
-
|
|
599
|
-
"map/scopes/
|
|
600
|
-
|
|
687
|
+
"map/scopes/get": async (params) =>
|
|
688
|
+
this.handleGetScope(participantId, params),
|
|
689
|
+
"map/scopes/create": async (params, ctx) =>
|
|
690
|
+
this.handleCreateScope(participantId, params, ctx),
|
|
691
|
+
"map/scopes/join": async (params) =>
|
|
692
|
+
this.handleJoinScope(participantId, params),
|
|
693
|
+
"map/scopes/leave": async (params) =>
|
|
694
|
+
this.handleLeaveScope(participantId, params),
|
|
601
695
|
|
|
602
696
|
// Federation methods
|
|
603
|
-
"map/federation/connect": async (params) =>
|
|
604
|
-
|
|
605
|
-
"map/federation/
|
|
606
|
-
|
|
697
|
+
"map/federation/connect": async (params) =>
|
|
698
|
+
this.handleFederationConnect(participantId, params),
|
|
699
|
+
"map/federation/disconnect": async (params) =>
|
|
700
|
+
this.handleFederationDisconnect(participantId, params),
|
|
701
|
+
"map/federation/list": async () =>
|
|
702
|
+
this.handleFederationList(participantId),
|
|
703
|
+
"map/federation/capabilities": async (params) =>
|
|
704
|
+
this.handleFederationCapabilities(participantId, params),
|
|
607
705
|
};
|
|
608
706
|
|
|
609
707
|
// Add extension method handlers
|
|
@@ -612,14 +710,18 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
612
710
|
const extCtx: ExtensionContext = {
|
|
613
711
|
participantId: ctx.participantId,
|
|
614
712
|
capabilities: ctx.capabilities,
|
|
615
|
-
sessionId:
|
|
713
|
+
sessionId:
|
|
714
|
+
this.sessions.get(ctx.participantId)?.participant.sessionId ?? "",
|
|
616
715
|
};
|
|
617
716
|
return handler(extCtx, params);
|
|
618
717
|
};
|
|
619
718
|
}
|
|
620
719
|
|
|
621
720
|
// Capability requirements for methods
|
|
622
|
-
const capabilityRequirements: Record<
|
|
721
|
+
const capabilityRequirements: Record<
|
|
722
|
+
string,
|
|
723
|
+
keyof ConnectedParticipant["capabilities"]
|
|
724
|
+
> = {
|
|
623
725
|
"map/subscribe": "canSubscribe",
|
|
624
726
|
"map/send": "canMessage",
|
|
625
727
|
"map/agents/list": "canQuery",
|
|
@@ -638,7 +740,8 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
638
740
|
for (const method of this.extensions.keys()) {
|
|
639
741
|
const capability = EXTENSION_CAPABILITIES[method];
|
|
640
742
|
if (capability) {
|
|
641
|
-
capabilityRequirements[method] =
|
|
743
|
+
capabilityRequirements[method] =
|
|
744
|
+
capability as keyof ConnectedParticipant["capabilities"];
|
|
642
745
|
}
|
|
643
746
|
}
|
|
644
747
|
|
|
@@ -660,7 +763,9 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
660
763
|
}
|
|
661
764
|
|
|
662
765
|
// Process the message
|
|
663
|
-
const participant = this.connections.getParticipant(
|
|
766
|
+
const participant = this.connections.getParticipant(
|
|
767
|
+
session.participantId,
|
|
768
|
+
);
|
|
664
769
|
if (!participant) {
|
|
665
770
|
break;
|
|
666
771
|
}
|
|
@@ -685,15 +790,13 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
685
790
|
}
|
|
686
791
|
}
|
|
687
792
|
|
|
688
|
-
private async sendToSession(
|
|
793
|
+
private async sendToSession(
|
|
794
|
+
session: ConnectionSession,
|
|
795
|
+
message: JsonRpcMessage,
|
|
796
|
+
): Promise<void> {
|
|
689
797
|
try {
|
|
690
|
-
//
|
|
691
|
-
|
|
692
|
-
try {
|
|
693
|
-
await writer.write(message);
|
|
694
|
-
} finally {
|
|
695
|
-
writer.releaseLock();
|
|
696
|
-
}
|
|
798
|
+
// Use the existing writer from the session (acquired in acceptConnection)
|
|
799
|
+
await session.writer.write(message);
|
|
697
800
|
} catch (error) {
|
|
698
801
|
console.error("[MAPAdapter] Failed to send message:", error);
|
|
699
802
|
throw error;
|
|
@@ -716,8 +819,11 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
716
819
|
|
|
717
820
|
private async handleConnect(
|
|
718
821
|
participantId: ParticipantId,
|
|
719
|
-
params: unknown
|
|
720
|
-
): Promise<{
|
|
822
|
+
params: unknown,
|
|
823
|
+
): Promise<{
|
|
824
|
+
participantId: ParticipantId;
|
|
825
|
+
capabilities: ConnectedParticipant["capabilities"];
|
|
826
|
+
}> {
|
|
721
827
|
const participant = this.connections.getParticipant(participantId);
|
|
722
828
|
if (!participant) {
|
|
723
829
|
throw RPCError.notFound("participant", participantId);
|
|
@@ -732,25 +838,44 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
732
838
|
};
|
|
733
839
|
}
|
|
734
840
|
|
|
735
|
-
private async handleDisconnect(
|
|
841
|
+
private async handleDisconnect(
|
|
842
|
+
participantId: ParticipantId,
|
|
843
|
+
): Promise<{ success: boolean }> {
|
|
736
844
|
await this.disconnectParticipant(participantId, "client_disconnect");
|
|
737
845
|
return { success: true };
|
|
738
846
|
}
|
|
739
847
|
|
|
740
848
|
private async handleSubscribe(
|
|
741
849
|
participantId: ParticipantId,
|
|
742
|
-
params: unknown
|
|
850
|
+
params: unknown,
|
|
743
851
|
): Promise<{ subscriptionId: SubscriptionId }> {
|
|
744
|
-
|
|
852
|
+
// Extract filter and translate SDK field names to internal format
|
|
853
|
+
// SDK uses 'fromAgents', macro-agent uses 'agents'
|
|
854
|
+
const rawFilter = (params as { filter?: Record<string, unknown> })?.filter;
|
|
855
|
+
let filter: SubscriptionFilter | undefined;
|
|
856
|
+
|
|
857
|
+
if (rawFilter) {
|
|
858
|
+
filter = {
|
|
859
|
+
eventTypes: rawFilter.eventTypes as MAPEventType[] | undefined,
|
|
860
|
+
agents: (rawFilter.fromAgents ?? rawFilter.agents) as
|
|
861
|
+
| AgentId[]
|
|
862
|
+
| undefined,
|
|
863
|
+
scopes: rawFilter.scopes as ScopeId[] | undefined,
|
|
864
|
+
subtree: rawFilter.subtree as AgentId | undefined,
|
|
865
|
+
lineage: rawFilter.lineage as AgentId | undefined,
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
|
|
745
869
|
const subscriptionId = await this.createSubscription(participantId, filter);
|
|
746
870
|
return { subscriptionId };
|
|
747
871
|
}
|
|
748
872
|
|
|
749
873
|
private async handleUnsubscribe(
|
|
750
874
|
participantId: ParticipantId,
|
|
751
|
-
params: unknown
|
|
875
|
+
params: unknown,
|
|
752
876
|
): Promise<{ success: boolean }> {
|
|
753
|
-
const subscriptionId = (params as { subscriptionId: SubscriptionId })
|
|
877
|
+
const subscriptionId = (params as { subscriptionId: SubscriptionId })
|
|
878
|
+
?.subscriptionId;
|
|
754
879
|
if (!subscriptionId) {
|
|
755
880
|
throw RPCError.invalidParams("subscriptionId required");
|
|
756
881
|
}
|
|
@@ -761,7 +886,7 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
761
886
|
private async handleSend(
|
|
762
887
|
participantId: ParticipantId,
|
|
763
888
|
params: unknown,
|
|
764
|
-
ctx: HandlerContext
|
|
889
|
+
ctx: HandlerContext,
|
|
765
890
|
): Promise<SendResult> {
|
|
766
891
|
const { to, payload, options } = params as {
|
|
767
892
|
to: Address;
|
|
@@ -776,12 +901,120 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
776
901
|
throw RPCError.invalidParams("payload required");
|
|
777
902
|
}
|
|
778
903
|
|
|
904
|
+
// Check if this is an ACP-over-MAP message
|
|
905
|
+
// SDK sends ACP envelope directly as payload (not wrapped in content)
|
|
906
|
+
const rawPayload = payload as unknown as Record<string, unknown> | undefined;
|
|
907
|
+
if (rawPayload && typeof rawPayload === 'object' && 'acp' in rawPayload && 'acpContext' in rawPayload) {
|
|
908
|
+
// This is an ACP envelope - route through ACP-over-MAP handler
|
|
909
|
+
return this.handleACPOverMAP(participantId, to, rawPayload, ctx);
|
|
910
|
+
}
|
|
911
|
+
|
|
779
912
|
return this.sendMessage(participantId, to, payload, options);
|
|
780
913
|
}
|
|
781
914
|
|
|
915
|
+
/**
|
|
916
|
+
* Handle ACP-over-MAP messages.
|
|
917
|
+
* These are ACP protocol messages sent via MAP to an agent.
|
|
918
|
+
*/
|
|
919
|
+
private async handleACPOverMAP(
|
|
920
|
+
participantId: ParticipantId,
|
|
921
|
+
to: Address,
|
|
922
|
+
envelope: Record<string, unknown>,
|
|
923
|
+
ctx: HandlerContext,
|
|
924
|
+
): Promise<SendResult> {
|
|
925
|
+
if (!this.acpOverMapHandler) {
|
|
926
|
+
throw RPCError.internalError("ACP-over-MAP not available - missing services");
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
const acp = envelope.acp as {
|
|
930
|
+
jsonrpc: string;
|
|
931
|
+
id?: string | number;
|
|
932
|
+
method?: string;
|
|
933
|
+
params?: unknown;
|
|
934
|
+
result?: unknown;
|
|
935
|
+
error?: unknown;
|
|
936
|
+
};
|
|
937
|
+
const acpContext = envelope.acpContext as {
|
|
938
|
+
streamId: string;
|
|
939
|
+
sessionId?: string;
|
|
940
|
+
direction: string;
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
console.error(`[ACP-over-MAP] Received - method=${acp.method} to=${JSON.stringify(to)}`);
|
|
944
|
+
|
|
945
|
+
// For now, we only support messages to specific agents
|
|
946
|
+
if (!isAgentAddress(to)) {
|
|
947
|
+
throw RPCError.invalidParams("ACP-over-MAP requires agent target");
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const targetAgentId = to.agent;
|
|
951
|
+
|
|
952
|
+
// Check if this is a request (has method) or response (has result/error)
|
|
953
|
+
if (!acp.method) {
|
|
954
|
+
// This is a response - route it normally
|
|
955
|
+
console.error(`[ACP-over-MAP] Routing response back`);
|
|
956
|
+
return this.sendMessage(participantId, to, { content: envelope }, undefined);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// Create notification emitter to stream session updates
|
|
960
|
+
const emitNotification = (notification: ACPEnvelope) => {
|
|
961
|
+
this.emitEvent({
|
|
962
|
+
eventId: ulid(),
|
|
963
|
+
type: "message_delivered" as MAPEventType,
|
|
964
|
+
timestamp: Date.now(),
|
|
965
|
+
agentId: targetAgentId,
|
|
966
|
+
data: {
|
|
967
|
+
message: {
|
|
968
|
+
id: `acp-notif-${Date.now()}`,
|
|
969
|
+
from: targetAgentId,
|
|
970
|
+
payload: notification,
|
|
971
|
+
},
|
|
972
|
+
},
|
|
973
|
+
});
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// Process the ACP request through the handler
|
|
977
|
+
const acpEnvelope: ACPEnvelope = {
|
|
978
|
+
acp: acp as ACPEnvelope["acp"],
|
|
979
|
+
acpContext: acpContext as ACPEnvelope["acpContext"],
|
|
980
|
+
};
|
|
981
|
+
const responseEnvelope = await this.acpOverMapHandler.processRequest(
|
|
982
|
+
targetAgentId,
|
|
983
|
+
acpEnvelope,
|
|
984
|
+
emitNotification
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
console.error(`[ACP-over-MAP] Request processed - method=${acp.method}`);
|
|
988
|
+
|
|
989
|
+
// Emit response event to the participant's subscriptions
|
|
990
|
+
// SDK's stream.ts checks for "message_delivered" (underscore) not "message.delivered" (dot)
|
|
991
|
+
// Use underscore format for ACP-over-MAP compatibility
|
|
992
|
+
const participant = this.connections.getParticipant(participantId);
|
|
993
|
+
if (participant) {
|
|
994
|
+
this.emitEvent({
|
|
995
|
+
eventId: ulid(),
|
|
996
|
+
type: "message_delivered" as MAPEventType, // Cast needed - SDK expects underscore format
|
|
997
|
+
timestamp: Date.now(),
|
|
998
|
+
agentId: targetAgentId, // Must be at top level for subscription matching
|
|
999
|
+
data: {
|
|
1000
|
+
message: {
|
|
1001
|
+
id: `acp-resp-${Date.now()}`,
|
|
1002
|
+
from: targetAgentId,
|
|
1003
|
+
payload: responseEnvelope,
|
|
1004
|
+
},
|
|
1005
|
+
},
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
return {
|
|
1010
|
+
messageId: `acp-${acp.id}`,
|
|
1011
|
+
delivered: [targetAgentId],
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
|
|
782
1015
|
private async handleListAgents(
|
|
783
1016
|
participantId: ParticipantId,
|
|
784
|
-
params: unknown
|
|
1017
|
+
params: unknown,
|
|
785
1018
|
): Promise<{ agents: AgentInfo[] }> {
|
|
786
1019
|
const filter = (params as { filter?: AgentFilter })?.filter;
|
|
787
1020
|
const agents = this.listAgents(participantId, filter);
|
|
@@ -790,7 +1023,7 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
790
1023
|
|
|
791
1024
|
private async handleGetAgent(
|
|
792
1025
|
participantId: ParticipantId,
|
|
793
|
-
params: unknown
|
|
1026
|
+
params: unknown,
|
|
794
1027
|
): Promise<{ agent: AgentInfo | null }> {
|
|
795
1028
|
const agentId = (params as { agentId: AgentId })?.agentId;
|
|
796
1029
|
if (!agentId) {
|
|
@@ -800,14 +1033,16 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
800
1033
|
return { agent: agent ?? null };
|
|
801
1034
|
}
|
|
802
1035
|
|
|
803
|
-
private async handleListScopes(
|
|
1036
|
+
private async handleListScopes(
|
|
1037
|
+
participantId: ParticipantId,
|
|
1038
|
+
): Promise<{ scopes: ScopeInfo[] }> {
|
|
804
1039
|
const scopes = this.listScopes(participantId);
|
|
805
1040
|
return { scopes };
|
|
806
1041
|
}
|
|
807
1042
|
|
|
808
1043
|
private async handleGetScope(
|
|
809
1044
|
participantId: ParticipantId,
|
|
810
|
-
params: unknown
|
|
1045
|
+
params: unknown,
|
|
811
1046
|
): Promise<{ scope: ScopeInfo | null }> {
|
|
812
1047
|
const scopeId = (params as { scopeId: ScopeId })?.scopeId;
|
|
813
1048
|
if (!scopeId) {
|
|
@@ -820,18 +1055,22 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
820
1055
|
private async handleCreateScope(
|
|
821
1056
|
participantId: ParticipantId,
|
|
822
1057
|
params: unknown,
|
|
823
|
-
ctx: HandlerContext
|
|
1058
|
+
ctx: HandlerContext,
|
|
824
1059
|
): Promise<{ scopeId: ScopeId }> {
|
|
825
|
-
const { name, metadata } =
|
|
1060
|
+
const { name, metadata } =
|
|
1061
|
+
(params as { name?: string; metadata?: Record<string, unknown> }) ?? {};
|
|
826
1062
|
const scopeId = this.createScope(name, metadata);
|
|
827
1063
|
return { scopeId };
|
|
828
1064
|
}
|
|
829
1065
|
|
|
830
1066
|
private async handleJoinScope(
|
|
831
1067
|
participantId: ParticipantId,
|
|
832
|
-
params: unknown
|
|
1068
|
+
params: unknown,
|
|
833
1069
|
): Promise<{ success: boolean }> {
|
|
834
|
-
const { scopeId, agentId } = params as {
|
|
1070
|
+
const { scopeId, agentId } = params as {
|
|
1071
|
+
scopeId: ScopeId;
|
|
1072
|
+
agentId: AgentId;
|
|
1073
|
+
};
|
|
835
1074
|
if (!scopeId) {
|
|
836
1075
|
throw RPCError.invalidParams("scopeId required");
|
|
837
1076
|
}
|
|
@@ -848,9 +1087,12 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
848
1087
|
|
|
849
1088
|
private async handleLeaveScope(
|
|
850
1089
|
participantId: ParticipantId,
|
|
851
|
-
params: unknown
|
|
1090
|
+
params: unknown,
|
|
852
1091
|
): Promise<{ success: boolean }> {
|
|
853
|
-
const { scopeId, agentId } = params as {
|
|
1092
|
+
const { scopeId, agentId } = params as {
|
|
1093
|
+
scopeId: ScopeId;
|
|
1094
|
+
agentId: AgentId;
|
|
1095
|
+
};
|
|
854
1096
|
if (!scopeId) {
|
|
855
1097
|
throw RPCError.invalidParams("scopeId required");
|
|
856
1098
|
}
|
|
@@ -871,7 +1113,7 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
871
1113
|
|
|
872
1114
|
private async handleFederationConnect(
|
|
873
1115
|
participantId: ParticipantId,
|
|
874
|
-
params: unknown
|
|
1116
|
+
params: unknown,
|
|
875
1117
|
): Promise<{ capabilities: FederationCapabilities }> {
|
|
876
1118
|
if (!this.services.federationHandler) {
|
|
877
1119
|
throw RPCError.internalError("Federation not available");
|
|
@@ -894,14 +1136,14 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
894
1136
|
return { capabilities };
|
|
895
1137
|
} catch (err) {
|
|
896
1138
|
throw RPCError.internalError(
|
|
897
|
-
err instanceof Error ? err.message : "Failed to connect"
|
|
1139
|
+
err instanceof Error ? err.message : "Failed to connect",
|
|
898
1140
|
);
|
|
899
1141
|
}
|
|
900
1142
|
}
|
|
901
1143
|
|
|
902
1144
|
private async handleFederationDisconnect(
|
|
903
1145
|
participantId: ParticipantId,
|
|
904
|
-
params: unknown
|
|
1146
|
+
params: unknown,
|
|
905
1147
|
): Promise<{ success: boolean }> {
|
|
906
1148
|
if (!this.services.federationHandler) {
|
|
907
1149
|
throw RPCError.internalError("Federation not available");
|
|
@@ -916,21 +1158,20 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
916
1158
|
await this.services.federationHandler.disconnect(systemId);
|
|
917
1159
|
return { success: true };
|
|
918
1160
|
} catch (err) {
|
|
919
|
-
if (
|
|
920
|
-
err instanceof Error &&
|
|
921
|
-
err.message.includes("Not connected")
|
|
922
|
-
) {
|
|
1161
|
+
if (err instanceof Error && err.message.includes("Not connected")) {
|
|
923
1162
|
throw RPCError.notFound("peer", systemId);
|
|
924
1163
|
}
|
|
925
1164
|
throw RPCError.internalError(
|
|
926
|
-
err instanceof Error ? err.message : "Failed to disconnect"
|
|
1165
|
+
err instanceof Error ? err.message : "Failed to disconnect",
|
|
927
1166
|
);
|
|
928
1167
|
}
|
|
929
1168
|
}
|
|
930
1169
|
|
|
931
1170
|
private async handleFederationList(
|
|
932
|
-
participantId: ParticipantId
|
|
933
|
-
): Promise<{
|
|
1171
|
+
participantId: ParticipantId,
|
|
1172
|
+
): Promise<{
|
|
1173
|
+
peers: Array<{ systemId: string; status: string; connectedAt: number }>;
|
|
1174
|
+
}> {
|
|
934
1175
|
if (!this.services.federationHandler) {
|
|
935
1176
|
return { peers: [] };
|
|
936
1177
|
}
|
|
@@ -947,7 +1188,7 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
947
1188
|
|
|
948
1189
|
private async handleFederationCapabilities(
|
|
949
1190
|
participantId: ParticipantId,
|
|
950
|
-
params: unknown
|
|
1191
|
+
params: unknown,
|
|
951
1192
|
): Promise<{ capabilities: FederationCapabilities | null }> {
|
|
952
1193
|
if (!this.services.federationHandler) {
|
|
953
1194
|
throw RPCError.internalError("Federation not available");
|
|
@@ -958,7 +1199,8 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
958
1199
|
throw RPCError.invalidParams("systemId is required");
|
|
959
1200
|
}
|
|
960
1201
|
|
|
961
|
-
const capabilities =
|
|
1202
|
+
const capabilities =
|
|
1203
|
+
this.services.federationHandler.getCapabilities(systemId);
|
|
962
1204
|
if (!capabilities) {
|
|
963
1205
|
throw RPCError.notFound("peer", systemId);
|
|
964
1206
|
}
|
|
@@ -980,6 +1222,13 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
980
1222
|
scopes: this.getAgentScopes(agent.id),
|
|
981
1223
|
metadata: agent.metadata,
|
|
982
1224
|
createdAt: agent.createdAt,
|
|
1225
|
+
// All macro-agent agents support ACP (they're Claude Code sessions)
|
|
1226
|
+
capabilities: {
|
|
1227
|
+
protocols: ["acp"],
|
|
1228
|
+
acp: {
|
|
1229
|
+
features: ["streaming"],
|
|
1230
|
+
},
|
|
1231
|
+
},
|
|
983
1232
|
};
|
|
984
1233
|
}
|
|
985
1234
|
|
|
@@ -1013,7 +1262,7 @@ export class MAPAdapterImpl implements MAPAdapter {
|
|
|
1013
1262
|
*/
|
|
1014
1263
|
export function createMAPAdapter(
|
|
1015
1264
|
config?: MAPAdapterConfig,
|
|
1016
|
-
services?: MAPAdapterServices
|
|
1265
|
+
services?: MAPAdapterServices,
|
|
1017
1266
|
): MAPAdapter {
|
|
1018
1267
|
return new MAPAdapterImpl(config, services);
|
|
1019
1268
|
}
|