macro-agent 0.0.14 → 0.0.16
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 +59 -0
- package/dist/acp/index.d.ts +1 -1
- package/dist/acp/index.d.ts.map +1 -1
- package/dist/acp/index.js.map +1 -1
- package/dist/acp/macro-agent.d.ts +21 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +182 -0
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +31 -2
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js +10 -4
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/cli/acp.d.ts +6 -0
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +16 -2
- package/dist/cli/acp.js.map +1 -1
- package/dist/map/adapter/acp-over-map.d.ts +5 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
- package/dist/map/adapter/acp-over-map.js +47 -4
- package/dist/map/adapter/acp-over-map.js.map +1 -1
- package/dist/map/utils/address-translation.d.ts +99 -0
- package/dist/map/utils/address-translation.d.ts.map +1 -0
- package/dist/map/utils/address-translation.js +285 -0
- package/dist/map/utils/address-translation.js.map +1 -0
- package/dist/map/utils/index.d.ts +7 -0
- package/dist/map/utils/index.d.ts.map +1 -0
- package/dist/map/utils/index.js +7 -0
- package/dist/map/utils/index.js.map +1 -0
- package/dist/store/event-store.js +9 -2
- package/dist/store/event-store.js.map +1 -1
- package/dist/store/types/agents.d.ts +2 -0
- package/dist/store/types/agents.d.ts.map +1 -1
- package/package.json +4 -4
- package/references/acp-factory-ref/CHANGELOG.md +33 -0
- package/references/acp-factory-ref/LICENSE +21 -0
- package/references/acp-factory-ref/README.md +341 -0
- package/references/acp-factory-ref/package-lock.json +3102 -0
- package/references/acp-factory-ref/package.json +96 -0
- package/references/acp-factory-ref/python/CHANGELOG.md +33 -0
- package/references/acp-factory-ref/python/LICENSE +21 -0
- package/references/acp-factory-ref/python/Makefile +57 -0
- package/references/acp-factory-ref/python/README.md +253 -0
- package/references/acp-factory-ref/python/pyproject.toml +73 -0
- package/references/acp-factory-ref/python/tests/__init__.py +0 -0
- package/references/acp-factory-ref/python/tests/e2e/__init__.py +1 -0
- package/references/acp-factory-ref/python/tests/e2e/test_codex_e2e.py +349 -0
- package/references/acp-factory-ref/python/tests/e2e/test_gemini_e2e.py +165 -0
- package/references/acp-factory-ref/python/tests/e2e/test_opencode_e2e.py +296 -0
- package/references/acp-factory-ref/python/tests/test_client_handler.py +543 -0
- package/references/acp-factory-ref/python/tests/test_pushable.py +199 -0
- package/references/claude-code-acp/.github/workflows/ci.yml +45 -0
- package/references/claude-code-acp/.github/workflows/publish.yml +34 -0
- package/references/claude-code-acp/.prettierrc.json +4 -0
- package/references/claude-code-acp/CHANGELOG.md +249 -0
- package/references/claude-code-acp/LICENSE +222 -0
- package/references/claude-code-acp/README.md +53 -0
- package/references/claude-code-acp/docs/RELEASES.md +24 -0
- package/references/claude-code-acp/eslint.config.js +48 -0
- package/references/claude-code-acp/package-lock.json +4570 -0
- package/references/claude-code-acp/package.json +88 -0
- package/references/claude-code-acp/scripts/release.sh +119 -0
- package/references/claude-code-acp/src/acp-agent.ts +2079 -0
- package/references/claude-code-acp/src/index.ts +26 -0
- package/references/claude-code-acp/src/lib.ts +38 -0
- package/references/claude-code-acp/src/mcp-server.ts +911 -0
- package/references/claude-code-acp/src/settings.ts +522 -0
- package/references/claude-code-acp/src/tests/.claude/commands/quick-math.md +5 -0
- package/references/claude-code-acp/src/tests/.claude/commands/say-hello.md +6 -0
- package/references/claude-code-acp/src/tests/acp-agent-fork.test.ts +479 -0
- package/references/claude-code-acp/src/tests/acp-agent.test.ts +1502 -0
- package/references/claude-code-acp/src/tests/extract-lines.test.ts +103 -0
- package/references/claude-code-acp/src/tests/fork-session.test.ts +335 -0
- package/references/claude-code-acp/src/tests/replace-and-calculate-location.test.ts +334 -0
- package/references/claude-code-acp/src/tests/settings.test.ts +617 -0
- package/references/claude-code-acp/src/tests/skills-options.test.ts +187 -0
- package/references/claude-code-acp/src/tests/tools.test.ts +318 -0
- package/references/claude-code-acp/src/tests/typescript-declarations.test.ts +558 -0
- package/references/claude-code-acp/src/tools.ts +819 -0
- package/references/claude-code-acp/src/utils.ts +171 -0
- package/references/claude-code-acp/tsconfig.json +18 -0
- package/references/claude-code-acp/vitest.config.ts +19 -0
- package/references/multi-agent-protocol/.sudocode/issues.jsonl +111 -0
- package/references/multi-agent-protocol/.sudocode/specs.jsonl +13 -0
- package/references/multi-agent-protocol/LICENSE +21 -0
- package/references/multi-agent-protocol/README.md +113 -0
- package/references/multi-agent-protocol/docs/00-design-specification.md +496 -0
- package/references/multi-agent-protocol/docs/01-open-questions.md +1050 -0
- package/references/multi-agent-protocol/docs/02-wire-protocol.md +296 -0
- package/references/multi-agent-protocol/docs/03-streaming-semantics.md +252 -0
- package/references/multi-agent-protocol/docs/04-error-handling.md +231 -0
- package/references/multi-agent-protocol/docs/05-connection-model.md +244 -0
- package/references/multi-agent-protocol/docs/06-visibility-permissions.md +243 -0
- package/references/multi-agent-protocol/docs/07-federation.md +259 -0
- package/references/multi-agent-protocol/docs/08-macro-agent-migration.md +253 -0
- package/references/multi-agent-protocol/docs/09-authentication.md +680 -0
- package/references/multi-agent-protocol/docs/10-mail-protocol.md +553 -0
- package/references/multi-agent-protocol/docs/agent-iam-integration.md +877 -0
- package/references/multi-agent-protocol/docs/agentic-mesh-integration-draft.md +459 -0
- package/references/multi-agent-protocol/docs/git-transport-draft.md +251 -0
- package/references/multi-agent-protocol/docs-site/Gemfile +22 -0
- package/references/multi-agent-protocol/docs-site/README.md +82 -0
- package/references/multi-agent-protocol/docs-site/_config.yml +91 -0
- package/references/multi-agent-protocol/docs-site/_includes/head_custom.html +20 -0
- package/references/multi-agent-protocol/docs-site/_sass/color_schemes/map.scss +42 -0
- package/references/multi-agent-protocol/docs-site/_sass/custom/custom.scss +34 -0
- package/references/multi-agent-protocol/docs-site/examples/full-integration.md +510 -0
- package/references/multi-agent-protocol/docs-site/examples/index.md +138 -0
- package/references/multi-agent-protocol/docs-site/examples/simple-chat.md +282 -0
- package/references/multi-agent-protocol/docs-site/examples/task-queue.md +399 -0
- package/references/multi-agent-protocol/docs-site/getting-started/index.md +98 -0
- package/references/multi-agent-protocol/docs-site/getting-started/installation.md +219 -0
- package/references/multi-agent-protocol/docs-site/getting-started/overview.md +172 -0
- package/references/multi-agent-protocol/docs-site/getting-started/quickstart.md +237 -0
- package/references/multi-agent-protocol/docs-site/index.md +136 -0
- package/references/multi-agent-protocol/docs-site/protocol/authentication.md +391 -0
- package/references/multi-agent-protocol/docs-site/protocol/connection-model.md +376 -0
- package/references/multi-agent-protocol/docs-site/protocol/design.md +284 -0
- package/references/multi-agent-protocol/docs-site/protocol/error-handling.md +312 -0
- package/references/multi-agent-protocol/docs-site/protocol/federation.md +449 -0
- package/references/multi-agent-protocol/docs-site/protocol/index.md +129 -0
- package/references/multi-agent-protocol/docs-site/protocol/permissions.md +398 -0
- package/references/multi-agent-protocol/docs-site/protocol/streaming.md +353 -0
- package/references/multi-agent-protocol/docs-site/protocol/wire-protocol.md +369 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/agent.md +357 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/client.md +380 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/index.md +62 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/server.md +453 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/types.md +468 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/agent.md +375 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/authentication.md +405 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/client.md +352 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/index.md +89 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/server.md +360 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/testing.md +446 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/transports.md +363 -0
- package/references/multi-agent-protocol/docs-site/sdk/index.md +206 -0
- package/references/multi-agent-protocol/package-lock.json +3886 -0
- package/references/multi-agent-protocol/package.json +56 -0
- package/references/multi-agent-protocol/schema/meta.json +467 -0
- package/references/multi-agent-protocol/schema/schema.json +2558 -0
- package/src/acp/__tests__/history.test.ts +526 -0
- package/src/acp/__tests__/integration.test.ts +2 -1
- package/src/acp/index.ts +4 -0
- package/src/acp/macro-agent.ts +329 -85
- package/src/acp/types.ts +39 -2
- package/src/agent/__tests__/agent-manager.test.ts +67 -1
- package/src/agent/agent-manager.ts +10 -4
- package/src/cli/__tests__/stable-instance-id.test.ts +57 -0
- package/src/cli/acp.ts +17 -2
- package/src/map/adapter/acp-over-map.ts +57 -2
- package/src/store/event-store.ts +10 -3
- package/src/store/types/agents.ts +2 -0
package/src/acp/types.ts
CHANGED
|
@@ -630,10 +630,44 @@ export interface ResumeAgentResponse {
|
|
|
630
630
|
/** The resumed agent's ID */
|
|
631
631
|
agentId: AgentId;
|
|
632
632
|
|
|
633
|
-
/** The agent's session ID */
|
|
633
|
+
/** The agent's session ID (from resume) */
|
|
634
634
|
sessionId: string;
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
+
// ─────────────────────────────────────────────────────────────────
|
|
638
|
+
// History Extension Types
|
|
639
|
+
// ─────────────────────────────────────────────────────────────────
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* A historical turn in a session conversation
|
|
643
|
+
*/
|
|
644
|
+
export interface HistoryTurn {
|
|
645
|
+
/** Turn role */
|
|
646
|
+
role: "user" | "assistant";
|
|
647
|
+
/** Timestamp of the turn */
|
|
648
|
+
timestamp: number;
|
|
649
|
+
/** Turn content — plain text for user, structured parts for assistant */
|
|
650
|
+
content: unknown;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Request for _macro/getHistory extension
|
|
655
|
+
*/
|
|
656
|
+
export interface GetHistoryRequest {
|
|
657
|
+
/** ACP session ID to get history for */
|
|
658
|
+
sessionId: string;
|
|
659
|
+
/** Maximum number of turns to return */
|
|
660
|
+
limit?: number;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Response for _macro/getHistory extension
|
|
665
|
+
*/
|
|
666
|
+
export interface GetHistoryResponse {
|
|
667
|
+
/** Conversation turns in chronological order */
|
|
668
|
+
turns: HistoryTurn[];
|
|
669
|
+
}
|
|
670
|
+
|
|
637
671
|
// ─────────────────────────────────────────────────────────────────
|
|
638
672
|
// Extension Method Types (Union)
|
|
639
673
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -657,7 +691,8 @@ export type ACPExtensionMethod =
|
|
|
657
691
|
| "_macro/checkCapability"
|
|
658
692
|
| "_macro/respondToPermission"
|
|
659
693
|
| "_macro/cancelPermission"
|
|
660
|
-
| "_macro/resume"
|
|
694
|
+
| "_macro/resume"
|
|
695
|
+
| "_macro/getHistory";
|
|
661
696
|
|
|
662
697
|
/**
|
|
663
698
|
* Map of extension methods to their request types
|
|
@@ -679,6 +714,7 @@ export interface ACPExtensionRequests {
|
|
|
679
714
|
"_macro/respondToPermission": RespondToPermissionRequest;
|
|
680
715
|
"_macro/cancelPermission": CancelPermissionRequest;
|
|
681
716
|
"_macro/resume": ResumeAgentRequest;
|
|
717
|
+
"_macro/getHistory": GetHistoryRequest;
|
|
682
718
|
}
|
|
683
719
|
|
|
684
720
|
/**
|
|
@@ -701,6 +737,7 @@ export interface ACPExtensionResponses {
|
|
|
701
737
|
"_macro/respondToPermission": RespondToPermissionResponse;
|
|
702
738
|
"_macro/cancelPermission": CancelPermissionResponse;
|
|
703
739
|
"_macro/resume": ResumeAgentResponse;
|
|
740
|
+
"_macro/getHistory": GetHistoryResponse;
|
|
704
741
|
}
|
|
705
742
|
|
|
706
743
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -455,7 +455,7 @@ describe("AgentManager Integration (with mocked acp-factory)", () => {
|
|
|
455
455
|
});
|
|
456
456
|
|
|
457
457
|
expect(result.id).toMatch(/^agent_/);
|
|
458
|
-
//
|
|
458
|
+
// session_id is macro-agent's own ID (pre-generated before createSession)
|
|
459
459
|
expect(result.session_id).toMatch(/^session_/);
|
|
460
460
|
expect(result.session).toBeDefined();
|
|
461
461
|
|
|
@@ -464,6 +464,8 @@ describe("AgentManager Integration (with mocked acp-factory)", () => {
|
|
|
464
464
|
expect(agent).toBeDefined();
|
|
465
465
|
expect(agent?.state).toBe("running");
|
|
466
466
|
expect(agent?.task).toBe("Test task");
|
|
467
|
+
// provider_session_id should be set from createSession's returned session.id
|
|
468
|
+
expect(agent?.provider_session_id).toBe("mock_session_1");
|
|
467
469
|
});
|
|
468
470
|
|
|
469
471
|
it("should set up default subscriptions", async () => {
|
|
@@ -684,6 +686,70 @@ describe("AgentManager Integration (with mocked acp-factory)", () => {
|
|
|
684
686
|
"already has active session",
|
|
685
687
|
);
|
|
686
688
|
});
|
|
689
|
+
|
|
690
|
+
it("should use agent's original cwd when loading session", async () => {
|
|
691
|
+
const spawned = await agentManager.spawn({
|
|
692
|
+
task: "Test with custom cwd",
|
|
693
|
+
cwd: "/custom/project/path",
|
|
694
|
+
});
|
|
695
|
+
await agentManager.terminate(spawned.id, "completed");
|
|
696
|
+
|
|
697
|
+
await agentManager.resume(spawned.id);
|
|
698
|
+
|
|
699
|
+
// loadSession should be called with the agent's original cwd, not the default
|
|
700
|
+
expect(mockHandle.loadSession).toHaveBeenCalledWith(
|
|
701
|
+
expect.any(String), // session ID
|
|
702
|
+
"/custom/project/path",
|
|
703
|
+
);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
it("should use provider_session_id when loading session", async () => {
|
|
707
|
+
const spawned = await agentManager.spawn({ task: "Test" });
|
|
708
|
+
|
|
709
|
+
// Verify the agent has provider_session_id from createSession
|
|
710
|
+
const agent = agentManager.get(spawned.id);
|
|
711
|
+
expect(agent?.provider_session_id).toBeDefined();
|
|
712
|
+
|
|
713
|
+
await agentManager.terminate(spawned.id, "completed");
|
|
714
|
+
await agentManager.resume(spawned.id);
|
|
715
|
+
|
|
716
|
+
// loadSession should be called with the provider_session_id (Claude Code UUID)
|
|
717
|
+
expect(mockHandle.loadSession).toHaveBeenCalledWith(
|
|
718
|
+
agent!.provider_session_id,
|
|
719
|
+
expect.any(String),
|
|
720
|
+
);
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
it("should fall back to session_id if provider_session_id is not set", async () => {
|
|
724
|
+
// Create agent directly without provider_session_id
|
|
725
|
+
const agentId = "agent_no_provider";
|
|
726
|
+
const sessionId = "session_no_provider";
|
|
727
|
+
eventStore.emit({
|
|
728
|
+
type: "spawn",
|
|
729
|
+
source: { agent_id: "system" },
|
|
730
|
+
payload: {
|
|
731
|
+
agent_id: agentId,
|
|
732
|
+
session_id: sessionId,
|
|
733
|
+
task: "Test",
|
|
734
|
+
parent: null,
|
|
735
|
+
cwd: "/tmp",
|
|
736
|
+
},
|
|
737
|
+
});
|
|
738
|
+
// Emit started status WITHOUT provider_session_id
|
|
739
|
+
eventStore.emit({
|
|
740
|
+
type: "status",
|
|
741
|
+
source: { agent_id: agentId },
|
|
742
|
+
payload: { status_type: "started" },
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
await agentManager.resume(agentId);
|
|
746
|
+
|
|
747
|
+
// Should fall back to the macro-agent session_id
|
|
748
|
+
expect(mockHandle.loadSession).toHaveBeenCalledWith(
|
|
749
|
+
sessionId,
|
|
750
|
+
"/tmp",
|
|
751
|
+
);
|
|
752
|
+
});
|
|
687
753
|
});
|
|
688
754
|
|
|
689
755
|
describe("getOrCreateHeadManager()", () => {
|
|
@@ -564,12 +564,15 @@ export function createAgentManager(
|
|
|
564
564
|
});
|
|
565
565
|
|
|
566
566
|
// Emit started status (session is ready)
|
|
567
|
+
// Include the provider's session ID (e.g., Claude Code UUID) so
|
|
568
|
+
// it can be used for handle.loadSession() during resume
|
|
567
569
|
eventStore.emit({
|
|
568
570
|
type: "status",
|
|
569
571
|
source: { agent_id: agentId },
|
|
570
572
|
payload: {
|
|
571
573
|
status_type: "started",
|
|
572
574
|
summary: "Agent session started",
|
|
575
|
+
provider_session_id: session.id,
|
|
573
576
|
},
|
|
574
577
|
});
|
|
575
578
|
|
|
@@ -703,7 +706,7 @@ export function createAgentManager(
|
|
|
703
706
|
|
|
704
707
|
return {
|
|
705
708
|
id: agentId,
|
|
706
|
-
session_id: sessionId, //
|
|
709
|
+
session_id: sessionId, // Macro-agent's own session ID for ACP protocol mapping
|
|
707
710
|
agent,
|
|
708
711
|
session,
|
|
709
712
|
workspace,
|
|
@@ -903,8 +906,11 @@ export function createAgentManager(
|
|
|
903
906
|
permissionMode: defaultPermissionMode,
|
|
904
907
|
});
|
|
905
908
|
|
|
906
|
-
// Load the existing session
|
|
907
|
-
|
|
909
|
+
// Load the existing session using the provider's session ID (e.g., Claude Code UUID)
|
|
910
|
+
// Falls back to macro-agent session_id for backwards compatibility
|
|
911
|
+
const loadSessionId = agent.provider_session_id ?? agent.session_id;
|
|
912
|
+
const agentCwd = agent.cwd ?? defaultCwd;
|
|
913
|
+
const session = await handle.loadSession(loadSessionId, agentCwd);
|
|
908
914
|
|
|
909
915
|
// Track active session
|
|
910
916
|
const activeSession: ActiveSession = {
|
|
@@ -928,7 +934,7 @@ export function createAgentManager(
|
|
|
928
934
|
|
|
929
935
|
return {
|
|
930
936
|
id: agentId,
|
|
931
|
-
session_id:
|
|
937
|
+
session_id: agent.session_id, // Macro-agent's own session ID
|
|
932
938
|
agent: eventStore.getAgent(agentId)!,
|
|
933
939
|
session,
|
|
934
940
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for getStableInstanceId
|
|
3
|
+
*
|
|
4
|
+
* Verifies that the stable instance ID is deterministic, consistent,
|
|
5
|
+
* and produces valid instance IDs for the EventStore.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from "vitest";
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
|
+
import { getStableInstanceId } from "../acp.js";
|
|
11
|
+
|
|
12
|
+
describe("getStableInstanceId", () => {
|
|
13
|
+
it("should return the same ID for the same path", () => {
|
|
14
|
+
const id1 = getStableInstanceId("/Users/test/project");
|
|
15
|
+
const id2 = getStableInstanceId("/Users/test/project");
|
|
16
|
+
expect(id1).toBe(id2);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should return different IDs for different paths", () => {
|
|
20
|
+
const id1 = getStableInstanceId("/Users/test/project-a");
|
|
21
|
+
const id2 = getStableInstanceId("/Users/test/project-b");
|
|
22
|
+
expect(id1).not.toBe(id2);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should start with inst_ prefix", () => {
|
|
26
|
+
const id = getStableInstanceId("/tmp/test");
|
|
27
|
+
expect(id).toMatch(/^inst_/);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should produce a valid instance ID format (alphanumeric + underscore)", () => {
|
|
31
|
+
const id = getStableInstanceId("/tmp/test");
|
|
32
|
+
expect(id).toMatch(/^[a-zA-Z0-9_]+$/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should normalize relative paths to absolute", () => {
|
|
36
|
+
// Both should resolve to the same absolute path
|
|
37
|
+
const absPath = resolve("./src");
|
|
38
|
+
const id1 = getStableInstanceId("./src");
|
|
39
|
+
const id2 = getStableInstanceId(absPath);
|
|
40
|
+
expect(id1).toBe(id2);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should handle paths with trailing slashes consistently", () => {
|
|
44
|
+
// resolve() strips trailing slashes, so these should be equal
|
|
45
|
+
const id1 = getStableInstanceId("/tmp/project");
|
|
46
|
+
const id2 = getStableInstanceId("/tmp/project/");
|
|
47
|
+
expect(id1).toBe(id2);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should produce a fixed-length ID", () => {
|
|
51
|
+
const short = getStableInstanceId("/a");
|
|
52
|
+
const long = getStableInstanceId("/very/long/path/to/some/deeply/nested/project/directory");
|
|
53
|
+
// Both should be inst_ + 12 hex chars = 17 chars total
|
|
54
|
+
expect(short.length).toBe(17);
|
|
55
|
+
expect(long.length).toBe(17);
|
|
56
|
+
});
|
|
57
|
+
});
|
package/src/cli/acp.ts
CHANGED
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
import { readFileSync } from "node:fs";
|
|
42
|
-
import {
|
|
42
|
+
import { createHash } from "node:crypto";
|
|
43
|
+
import { dirname, join, resolve } from "node:path";
|
|
43
44
|
import { Readable } from "node:stream";
|
|
44
45
|
import { fileURLToPath } from "node:url";
|
|
45
46
|
import {
|
|
@@ -119,6 +120,17 @@ export function parseArgs(argv?: string[]): ACPServerOptions {
|
|
|
119
120
|
return options;
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Generate a stable instance ID from the working directory.
|
|
125
|
+
* This ensures the same project always uses the same EventStore,
|
|
126
|
+
* so agents and sessions persist across server restarts.
|
|
127
|
+
*/
|
|
128
|
+
export function getStableInstanceId(cwd: string): string {
|
|
129
|
+
const normalizedPath = resolve(cwd);
|
|
130
|
+
const hash = createHash("sha256").update(normalizedPath).digest("hex").slice(0, 12);
|
|
131
|
+
return `inst_${hash}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
// ─────────────────────────────────────────────────────────────────
|
|
123
135
|
// Stream Setup
|
|
124
136
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -159,9 +171,12 @@ async function main() {
|
|
|
159
171
|
const defaultCwd = options.cwd ?? process.cwd();
|
|
160
172
|
|
|
161
173
|
// Initialize services
|
|
174
|
+
// Use explicit --instance-id if provided, otherwise derive a stable ID from the CWD
|
|
175
|
+
// so the same project always reuses the same EventStore across server restarts
|
|
176
|
+
const instanceId = options.instanceId ?? getStableInstanceId(defaultCwd);
|
|
162
177
|
const eventStore = await createEventStore({
|
|
163
178
|
inMemory: false,
|
|
164
|
-
instanceId
|
|
179
|
+
instanceId,
|
|
165
180
|
});
|
|
166
181
|
|
|
167
182
|
// We need to create agentManager first (with a placeholder router),
|
|
@@ -117,11 +117,11 @@ export class ACPOverMAPHandler {
|
|
|
117
117
|
break;
|
|
118
118
|
|
|
119
119
|
case "session/new":
|
|
120
|
-
result = await this.handleNewSession(streamState, acp.params);
|
|
120
|
+
result = await this.handleNewSession(streamState, acp.params, emitNotification);
|
|
121
121
|
break;
|
|
122
122
|
|
|
123
123
|
case "session/load":
|
|
124
|
-
result = await this.handleLoadSession(streamState, acp.params);
|
|
124
|
+
result = await this.handleLoadSession(streamState, acp.params, emitNotification);
|
|
125
125
|
break;
|
|
126
126
|
|
|
127
127
|
case "authenticate":
|
|
@@ -201,6 +201,7 @@ export class ACPOverMAPHandler {
|
|
|
201
201
|
private async handleNewSession(
|
|
202
202
|
streamState: StreamState,
|
|
203
203
|
params: unknown,
|
|
204
|
+
emitNotification?: ACPNotificationEmitter,
|
|
204
205
|
): Promise<unknown> {
|
|
205
206
|
if (!streamState.initialized) {
|
|
206
207
|
throw new Error("Must call initialize before newSession");
|
|
@@ -224,12 +225,16 @@ export class ACPOverMAPHandler {
|
|
|
224
225
|
|
|
225
226
|
console.error(`[ACP-over-MAP] Created session ${sessionId} -> agent ${spawned.id}`);
|
|
226
227
|
|
|
228
|
+
// Emit session_info_update so client has title/timestamps
|
|
229
|
+
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
230
|
+
|
|
227
231
|
return { sessionId };
|
|
228
232
|
}
|
|
229
233
|
|
|
230
234
|
private async handleLoadSession(
|
|
231
235
|
streamState: StreamState,
|
|
232
236
|
params: unknown,
|
|
237
|
+
emitNotification?: ACPNotificationEmitter,
|
|
233
238
|
): Promise<unknown> {
|
|
234
239
|
if (!streamState.initialized) {
|
|
235
240
|
throw new Error("Must call initialize before loadSession");
|
|
@@ -271,6 +276,7 @@ export class ACPOverMAPHandler {
|
|
|
271
276
|
streamState.sessionId = sessionId;
|
|
272
277
|
streamState.agentId = existing.id;
|
|
273
278
|
this.sessionMapper.createMapping(sessionId as ACPSessionId, existing.id);
|
|
279
|
+
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
274
280
|
return {};
|
|
275
281
|
}
|
|
276
282
|
|
|
@@ -280,6 +286,7 @@ export class ACPOverMAPHandler {
|
|
|
280
286
|
streamState.sessionId = sessionId;
|
|
281
287
|
streamState.agentId = spawned.id;
|
|
282
288
|
this.sessionMapper.createMapping(sessionId as ACPSessionId, spawned.id);
|
|
289
|
+
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
283
290
|
return {};
|
|
284
291
|
}
|
|
285
292
|
|
|
@@ -293,6 +300,7 @@ export class ACPOverMAPHandler {
|
|
|
293
300
|
streamState.sessionId = sessionId;
|
|
294
301
|
streamState.agentId = spawned.id;
|
|
295
302
|
this.sessionMapper.createMapping(sessionId as ACPSessionId, spawned.id);
|
|
303
|
+
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
296
304
|
|
|
297
305
|
return {};
|
|
298
306
|
}
|
|
@@ -388,6 +396,9 @@ export class ACPOverMAPHandler {
|
|
|
388
396
|
|
|
389
397
|
console.error(`[ACP-over-MAP] Prompt completed for agent ${agentId}, ${updateCount} updates`);
|
|
390
398
|
|
|
399
|
+
// Emit updated session info after prompt completes
|
|
400
|
+
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
401
|
+
|
|
391
402
|
return { stopReason: "end_turn" };
|
|
392
403
|
} catch (error) {
|
|
393
404
|
console.error(`[ACP-over-MAP] Prompt error:`, error);
|
|
@@ -532,6 +543,50 @@ export class ACPOverMAPHandler {
|
|
|
532
543
|
}
|
|
533
544
|
}
|
|
534
545
|
|
|
546
|
+
// ─────────────────────────────────────────────────────────────────
|
|
547
|
+
// Session Info
|
|
548
|
+
// ─────────────────────────────────────────────────────────────────
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Emit a session_info_update notification with title and timestamps.
|
|
552
|
+
* Uses agent task as title and session mapping timestamps.
|
|
553
|
+
*/
|
|
554
|
+
private emitSessionInfo(
|
|
555
|
+
streamState: StreamState,
|
|
556
|
+
sessionId: string,
|
|
557
|
+
emitNotification?: ACPNotificationEmitter,
|
|
558
|
+
): void {
|
|
559
|
+
if (!emitNotification) return;
|
|
560
|
+
|
|
561
|
+
const mapping = this.sessionMapper.getMapping(sessionId as ACPSessionId);
|
|
562
|
+
const agentId = streamState.agentId;
|
|
563
|
+
const agent = agentId ? this.eventStore.getAgent(agentId as AgentId) : null;
|
|
564
|
+
|
|
565
|
+
const title = agent?.task ?? null;
|
|
566
|
+
const updatedAt = new Date(mapping?.updatedAt ?? Date.now()).toISOString();
|
|
567
|
+
|
|
568
|
+
const notification: ACPEnvelope = {
|
|
569
|
+
acp: {
|
|
570
|
+
jsonrpc: "2.0",
|
|
571
|
+
method: "session/update",
|
|
572
|
+
params: {
|
|
573
|
+
sessionId,
|
|
574
|
+
update: {
|
|
575
|
+
sessionUpdate: "session_info_update",
|
|
576
|
+
title,
|
|
577
|
+
updatedAt,
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
acpContext: {
|
|
582
|
+
streamId: streamState.streamId,
|
|
583
|
+
sessionId,
|
|
584
|
+
direction: "agent-to-client",
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
emitNotification(notification);
|
|
588
|
+
}
|
|
589
|
+
|
|
535
590
|
// ─────────────────────────────────────────────────────────────────
|
|
536
591
|
// Cleanup
|
|
537
592
|
// ─────────────────────────────────────────────────────────────────
|
package/src/store/event-store.ts
CHANGED
|
@@ -1354,6 +1354,7 @@ function applySpawnEvent(
|
|
|
1354
1354
|
store.setRow('agents', agentId, {
|
|
1355
1355
|
id: agentId,
|
|
1356
1356
|
session_id: payload.session_id,
|
|
1357
|
+
provider_session_id: '',
|
|
1357
1358
|
parent: parent ?? '',
|
|
1358
1359
|
lineage: JSON.stringify(lineage),
|
|
1359
1360
|
state: 'spawning',
|
|
@@ -1408,15 +1409,20 @@ function applyStatusEvent(
|
|
|
1408
1409
|
const agentId = event.source.agent_id;
|
|
1409
1410
|
if (!agentId) return;
|
|
1410
1411
|
|
|
1411
|
-
const payload = event.payload as { status_type: string };
|
|
1412
|
+
const payload = event.payload as { status_type: string; provider_session_id?: string };
|
|
1412
1413
|
|
|
1413
1414
|
// Handle specific status types
|
|
1414
1415
|
if (payload.status_type === 'started') {
|
|
1415
|
-
|
|
1416
|
+
const updates: Record<string, string | number> = {
|
|
1416
1417
|
state: 'running',
|
|
1417
1418
|
started_at: event.timestamp,
|
|
1418
1419
|
last_activity_at: event.timestamp,
|
|
1419
|
-
}
|
|
1420
|
+
};
|
|
1421
|
+
// Store the provider's session ID (e.g., Claude Code UUID for --resume)
|
|
1422
|
+
if (payload.provider_session_id) {
|
|
1423
|
+
updates.provider_session_id = payload.provider_session_id;
|
|
1424
|
+
}
|
|
1425
|
+
store.setPartialRow('agents', agentId, updates);
|
|
1420
1426
|
} else {
|
|
1421
1427
|
// Always update last_activity_at on any status event
|
|
1422
1428
|
store.setPartialRow('agents', agentId, {
|
|
@@ -1696,6 +1702,7 @@ function rowToAgent(row: Record<string, unknown>): Agent {
|
|
|
1696
1702
|
return {
|
|
1697
1703
|
id: row.id as AgentId,
|
|
1698
1704
|
session_id: row.session_id as string,
|
|
1705
|
+
provider_session_id: (row.provider_session_id as string) || undefined,
|
|
1699
1706
|
parent: (row.parent as string) || null,
|
|
1700
1707
|
lineage: row.lineage ? JSON.parse(row.lineage as string) : [],
|
|
1701
1708
|
state: row.state as AgentState,
|
|
@@ -29,6 +29,8 @@ export interface AgentConfig {
|
|
|
29
29
|
export interface Agent {
|
|
30
30
|
id: AgentId;
|
|
31
31
|
session_id: SessionId;
|
|
32
|
+
/** Session ID from the underlying agent provider (e.g., Claude Code UUID for --resume) */
|
|
33
|
+
provider_session_id?: string;
|
|
32
34
|
parent: AgentId | null;
|
|
33
35
|
lineage: AgentId[];
|
|
34
36
|
state: AgentState;
|