@ai-devkit/agent-manager 0.33.0 → 0.34.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.
- package/dist/AgentManager.d.ts +11 -2
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +37 -6
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +212 -5
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/capacity/codex.test.js +2 -1
- package/dist/__tests__/capacity/codex.test.js.map +1 -1
- package/dist/__tests__/capacity/index.test.js +6 -3
- package/dist/__tests__/capacity/index.test.js.map +1 -1
- package/dist/__tests__/capacity/zai.test.js +213 -0
- package/dist/__tests__/capacity/zai.test.js.map +1 -0
- package/dist/__tests__/{adapters → providers/copilot}/CopilotAdapter.test.js +19 -14
- package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -0
- package/dist/__tests__/{adapters → providers/gemini}/GeminiCliAdapter.test.js +37 -29
- package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js +189 -0
- package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeAgentMapper.test.js +89 -0
- package/dist/__tests__/providers/opencode/OpenCodeAgentMapper.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeSessionLocator.test.js +94 -0
- package/dist/__tests__/providers/opencode/OpenCodeSessionLocator.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeSessionParser.test.js +147 -0
- package/dist/__tests__/providers/opencode/OpenCodeSessionParser.test.js.map +1 -0
- package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js +164 -0
- package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js.map +1 -0
- package/dist/__tests__/runtime/HerdrAgentRuntime.test.js +37 -1
- package/dist/__tests__/runtime/HerdrAgentRuntime.test.js.map +1 -1
- package/dist/__tests__/utils/AgentRegistry.test.js +20 -13
- package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
- package/dist/adapters/index.d.ts +3 -3
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +3 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/capacity/codex.d.ts +1 -0
- package/dist/capacity/codex.d.ts.map +1 -1
- package/dist/capacity/codex.js +9 -4
- package/dist/capacity/codex.js.map +1 -1
- package/dist/capacity/index.d.ts +8 -0
- package/dist/capacity/index.d.ts.map +1 -1
- package/dist/capacity/index.js +10 -9
- package/dist/capacity/index.js.map +1 -1
- package/dist/capacity/types.d.ts +8 -0
- package/dist/capacity/types.d.ts.map +1 -1
- package/dist/capacity/types.js.map +1 -1
- package/dist/capacity/zai.d.ts +14 -0
- package/dist/capacity/zai.d.ts.map +1 -0
- package/dist/capacity/zai.js +203 -0
- package/dist/capacity/zai.js.map +1 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/{adapters → providers/copilot}/CopilotAdapter.d.ts +11 -25
- package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -0
- package/dist/providers/copilot/CopilotAdapter.js +108 -0
- package/dist/providers/copilot/CopilotAdapter.js.map +1 -0
- package/dist/providers/copilot/CopilotAgentMapper.d.ts +9 -0
- package/dist/providers/copilot/CopilotAgentMapper.d.ts.map +1 -0
- package/dist/providers/copilot/CopilotAgentMapper.js +37 -0
- package/dist/providers/copilot/CopilotAgentMapper.js.map +1 -0
- package/dist/providers/copilot/CopilotSessionLocator.d.ts +19 -0
- package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -0
- package/dist/providers/copilot/CopilotSessionLocator.js +41 -0
- package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -0
- package/dist/providers/copilot/CopilotSessionParser.d.ts +30 -0
- package/dist/providers/copilot/CopilotSessionParser.d.ts.map +1 -0
- package/dist/providers/copilot/CopilotSessionParser.js +181 -0
- package/dist/providers/copilot/CopilotSessionParser.js.map +1 -0
- package/dist/providers/gemini/GeminiAgentMapper.d.ts +9 -0
- package/dist/providers/gemini/GeminiAgentMapper.d.ts.map +1 -0
- package/dist/providers/gemini/GeminiAgentMapper.js +37 -0
- package/dist/providers/gemini/GeminiAgentMapper.js.map +1 -0
- package/dist/providers/gemini/GeminiCliAdapter.d.ts +52 -0
- package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -0
- package/dist/providers/gemini/GeminiCliAdapter.js +188 -0
- package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -0
- package/dist/providers/gemini/GeminiSessionLocator.d.ts +30 -0
- package/dist/providers/gemini/GeminiSessionLocator.d.ts.map +1 -0
- package/dist/providers/gemini/GeminiSessionLocator.js +142 -0
- package/dist/providers/gemini/GeminiSessionLocator.js.map +1 -0
- package/dist/providers/gemini/GeminiSessionParser.d.ts +94 -0
- package/dist/providers/gemini/GeminiSessionParser.d.ts.map +1 -0
- package/dist/providers/gemini/GeminiSessionParser.js +166 -0
- package/dist/providers/gemini/GeminiSessionParser.js.map +1 -0
- package/dist/{adapters → providers/opencode}/OpenCodeAdapter.d.ts +6 -12
- package/dist/providers/opencode/OpenCodeAdapter.d.ts.map +1 -0
- package/dist/providers/opencode/OpenCodeAdapter.js +86 -0
- package/dist/providers/opencode/OpenCodeAdapter.js.map +1 -0
- package/dist/providers/opencode/OpenCodeAgentMapper.d.ts +11 -0
- package/dist/providers/opencode/OpenCodeAgentMapper.d.ts.map +1 -0
- package/dist/providers/opencode/OpenCodeAgentMapper.js +50 -0
- package/dist/providers/opencode/OpenCodeAgentMapper.js.map +1 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.d.ts +21 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.d.ts.map +1 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.js +99 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.js.map +1 -0
- package/dist/providers/opencode/OpenCodeSessionParser.d.ts +18 -0
- package/dist/providers/opencode/OpenCodeSessionParser.d.ts.map +1 -0
- package/dist/providers/opencode/OpenCodeSessionParser.js +104 -0
- package/dist/providers/opencode/OpenCodeSessionParser.js.map +1 -0
- package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts +18 -0
- package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -0
- package/dist/runtime/herdr/HerdrAgentDiscovery.js +183 -0
- package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -0
- package/dist/runtime/herdr/HerdrAgentRuntime.d.ts +5 -0
- package/dist/runtime/herdr/HerdrAgentRuntime.d.ts.map +1 -1
- package/dist/runtime/herdr/HerdrAgentRuntime.js +26 -8
- package/dist/runtime/herdr/HerdrAgentRuntime.js.map +1 -1
- package/dist/runtime/herdr/HerdrResponseParsers.d.ts +2 -0
- package/dist/runtime/herdr/HerdrResponseParsers.d.ts.map +1 -1
- package/dist/runtime/herdr/HerdrResponseParsers.js +12 -1
- package/dist/runtime/herdr/HerdrResponseParsers.js.map +1 -1
- package/dist/utils/AgentRegistry.d.ts.map +1 -1
- package/dist/utils/AgentRegistry.js +5 -5
- package/dist/utils/AgentRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +67 -5
- package/src/__tests__/AgentManager.test.ts +231 -5
- package/src/__tests__/capacity/codex.test.ts +94 -26
- package/src/__tests__/capacity/fixtures/zai-auth.json +6 -0
- package/src/__tests__/capacity/fixtures/zai-quota-invalid.json +7 -0
- package/src/__tests__/capacity/fixtures/zai-quota.json +38 -0
- package/src/__tests__/capacity/index.test.ts +10 -3
- package/src/__tests__/capacity/zai.test.ts +186 -0
- package/src/__tests__/{adapters → providers/copilot}/CopilotAdapter.test.ts +13 -16
- package/src/__tests__/{adapters → providers/gemini}/GeminiCliAdapter.test.ts +39 -31
- package/src/__tests__/providers/opencode/OpenCodeAdapter.test.ts +197 -0
- package/src/__tests__/providers/opencode/OpenCodeAgentMapper.test.ts +83 -0
- package/src/__tests__/providers/opencode/OpenCodeSessionLocator.test.ts +84 -0
- package/src/__tests__/providers/opencode/OpenCodeSessionParser.test.ts +148 -0
- package/src/__tests__/runtime/HerdrAgentDiscovery.test.ts +131 -0
- package/src/__tests__/runtime/HerdrAgentRuntime.test.ts +20 -1
- package/src/__tests__/utils/AgentRegistry.test.ts +20 -10
- package/src/adapters/index.ts +3 -3
- package/src/capacity/codex.ts +96 -28
- package/src/capacity/index.ts +29 -11
- package/src/capacity/types.ts +8 -0
- package/src/capacity/zai.ts +237 -0
- package/src/index.ts +18 -6
- package/src/providers/copilot/CopilotAdapter.ts +145 -0
- package/src/providers/copilot/CopilotAgentMapper.ts +36 -0
- package/src/providers/copilot/CopilotSessionLocator.ts +57 -0
- package/src/providers/copilot/CopilotSessionParser.ts +289 -0
- package/src/providers/gemini/GeminiAgentMapper.ts +36 -0
- package/src/providers/gemini/GeminiCliAdapter.ts +266 -0
- package/src/providers/gemini/GeminiSessionLocator.ts +183 -0
- package/src/providers/gemini/GeminiSessionParser.ts +271 -0
- package/src/providers/opencode/OpenCodeAdapter.ts +112 -0
- package/src/providers/opencode/OpenCodeAgentMapper.ts +61 -0
- package/src/providers/opencode/OpenCodeSessionLocator.ts +123 -0
- package/src/providers/opencode/OpenCodeSessionParser.ts +145 -0
- package/src/runtime/herdr/HerdrAgentDiscovery.ts +178 -0
- package/src/runtime/herdr/HerdrAgentRuntime.ts +33 -7
- package/src/runtime/herdr/HerdrResponseParsers.ts +11 -1
- package/src/utils/AgentRegistry.ts +8 -6
- package/dist/__tests__/adapters/CopilotAdapter.test.js.map +0 -1
- package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +0 -1
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js +0 -635
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +0 -1
- package/dist/adapters/CopilotAdapter.d.ts.map +0 -1
- package/dist/adapters/CopilotAdapter.js +0 -329
- package/dist/adapters/CopilotAdapter.js.map +0 -1
- package/dist/adapters/GeminiCliAdapter.d.ts +0 -98
- package/dist/adapters/GeminiCliAdapter.d.ts.map +0 -1
- package/dist/adapters/GeminiCliAdapter.js +0 -513
- package/dist/adapters/GeminiCliAdapter.js.map +0 -1
- package/dist/adapters/OpenCodeAdapter.d.ts.map +0 -1
- package/dist/adapters/OpenCodeAdapter.js +0 -286
- package/dist/adapters/OpenCodeAdapter.js.map +0 -1
- package/src/__tests__/adapters/OpenCodeAdapter.test.ts +0 -543
- package/src/adapters/CopilotAdapter.ts +0 -483
- package/src/adapters/GeminiCliAdapter.ts +0 -699
- package/src/adapters/OpenCodeAdapter.ts +0 -369
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { OpenCodeSessionParser } from "../../../providers/opencode/OpenCodeSessionParser.js";
|
|
4
|
+
describe("OpenCodeSessionParser", ()=>{
|
|
5
|
+
let db;
|
|
6
|
+
let parser;
|
|
7
|
+
beforeEach(()=>{
|
|
8
|
+
db = new Database(":memory:");
|
|
9
|
+
db.exec(`
|
|
10
|
+
CREATE TABLE message (id TEXT, session_id TEXT, data TEXT, time_created INTEGER, time_updated INTEGER);
|
|
11
|
+
CREATE TABLE part (message_id TEXT, session_id TEXT, data TEXT, time_created INTEGER);
|
|
12
|
+
`);
|
|
13
|
+
parser = new OpenCodeSessionParser();
|
|
14
|
+
});
|
|
15
|
+
afterEach(()=>{
|
|
16
|
+
db.close();
|
|
17
|
+
});
|
|
18
|
+
it("extracts session stats from OpenCode messages and parts", ()=>{
|
|
19
|
+
insertMessage(db, {
|
|
20
|
+
id: "msg-user",
|
|
21
|
+
sessionId: "sess-1",
|
|
22
|
+
role: "user",
|
|
23
|
+
timeCreated: 1000,
|
|
24
|
+
timeUpdated: 1000
|
|
25
|
+
});
|
|
26
|
+
insertMessage(db, {
|
|
27
|
+
id: "msg-assistant",
|
|
28
|
+
sessionId: "sess-1",
|
|
29
|
+
role: "assistant",
|
|
30
|
+
timeCreated: 2000,
|
|
31
|
+
timeUpdated: 2500,
|
|
32
|
+
completed: 2400
|
|
33
|
+
});
|
|
34
|
+
insertPart(db, {
|
|
35
|
+
messageId: "msg-user",
|
|
36
|
+
sessionId: "sess-1",
|
|
37
|
+
data: {
|
|
38
|
+
type: "text",
|
|
39
|
+
text: "Plan the migration"
|
|
40
|
+
},
|
|
41
|
+
timeCreated: 1000
|
|
42
|
+
});
|
|
43
|
+
expect(parser.getSessionStats(db, "sess-1")).toEqual({
|
|
44
|
+
lastRole: "assistant",
|
|
45
|
+
lastTimeUpdated: 2500,
|
|
46
|
+
lastAssistantCompleted: true,
|
|
47
|
+
lastAssistantErrored: false,
|
|
48
|
+
summary: "Plan the migration"
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
it("orders stats by message creation time, not metadata update time", ()=>{
|
|
52
|
+
insertMessage(db, {
|
|
53
|
+
id: "msg-user",
|
|
54
|
+
sessionId: "sess-1",
|
|
55
|
+
role: "user",
|
|
56
|
+
timeCreated: 1000,
|
|
57
|
+
timeUpdated: 3000
|
|
58
|
+
});
|
|
59
|
+
insertMessage(db, {
|
|
60
|
+
id: "msg-assistant",
|
|
61
|
+
sessionId: "sess-1",
|
|
62
|
+
role: "assistant",
|
|
63
|
+
timeCreated: 2000,
|
|
64
|
+
timeUpdated: 2100,
|
|
65
|
+
completed: 2200
|
|
66
|
+
});
|
|
67
|
+
expect(parser.getSessionStats(db, "sess-1").lastRole).toBe("assistant");
|
|
68
|
+
});
|
|
69
|
+
it("reads visible conversation text and hides verbose-only parts by default", ()=>{
|
|
70
|
+
insertMessage(db, {
|
|
71
|
+
id: "msg-user",
|
|
72
|
+
sessionId: "sess-1",
|
|
73
|
+
role: "user",
|
|
74
|
+
timeCreated: 1000,
|
|
75
|
+
timeUpdated: 1000
|
|
76
|
+
});
|
|
77
|
+
insertMessage(db, {
|
|
78
|
+
id: "msg-assistant",
|
|
79
|
+
sessionId: "sess-1",
|
|
80
|
+
role: "assistant",
|
|
81
|
+
timeCreated: 2000,
|
|
82
|
+
timeUpdated: 2000
|
|
83
|
+
});
|
|
84
|
+
insertPart(db, {
|
|
85
|
+
messageId: "msg-user",
|
|
86
|
+
sessionId: "sess-1",
|
|
87
|
+
data: {
|
|
88
|
+
type: "text",
|
|
89
|
+
text: "Hello"
|
|
90
|
+
},
|
|
91
|
+
timeCreated: 1000
|
|
92
|
+
});
|
|
93
|
+
insertPart(db, {
|
|
94
|
+
messageId: "msg-assistant",
|
|
95
|
+
sessionId: "sess-1",
|
|
96
|
+
data: {
|
|
97
|
+
type: "reasoning",
|
|
98
|
+
reasoning: "private thought"
|
|
99
|
+
},
|
|
100
|
+
timeCreated: 2000
|
|
101
|
+
});
|
|
102
|
+
insertPart(db, {
|
|
103
|
+
messageId: "msg-assistant",
|
|
104
|
+
sessionId: "sess-1",
|
|
105
|
+
data: {
|
|
106
|
+
type: "tool",
|
|
107
|
+
tool: "read_file"
|
|
108
|
+
},
|
|
109
|
+
timeCreated: 3000
|
|
110
|
+
});
|
|
111
|
+
expect(parser.getConversation(db, "sess-1")).toEqual([
|
|
112
|
+
{
|
|
113
|
+
role: "user",
|
|
114
|
+
content: "Hello"
|
|
115
|
+
}
|
|
116
|
+
]);
|
|
117
|
+
expect(parser.getConversation(db, "sess-1", {
|
|
118
|
+
verbose: true
|
|
119
|
+
})).toEqual([
|
|
120
|
+
{
|
|
121
|
+
role: "user",
|
|
122
|
+
content: "Hello"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
role: "assistant",
|
|
126
|
+
content: "[thinking] private thought"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
role: "assistant",
|
|
130
|
+
content: "[tool: read_file]"
|
|
131
|
+
}
|
|
132
|
+
]);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
function insertMessage(db, input) {
|
|
136
|
+
db.prepare("INSERT INTO message (id, session_id, data, time_created, time_updated) VALUES (?, ?, ?, ?, ?)").run(input.id, input.sessionId, JSON.stringify({
|
|
137
|
+
role: input.role,
|
|
138
|
+
time: input.completed === undefined ? {} : {
|
|
139
|
+
completed: input.completed
|
|
140
|
+
}
|
|
141
|
+
}), input.timeCreated, input.timeUpdated);
|
|
142
|
+
}
|
|
143
|
+
function insertPart(db, input) {
|
|
144
|
+
db.prepare("INSERT INTO part (message_id, session_id, data, time_created) VALUES (?, ?, ?, ?)").run(input.messageId, input.sessionId, JSON.stringify(input.data), input.timeCreated);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//# sourceMappingURL=OpenCodeSessionParser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/providers/opencode/OpenCodeSessionParser.test.ts"],"sourcesContent":["import Database from \"better-sqlite3\";\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\nimport { OpenCodeSessionParser } from \"../../../providers/opencode/OpenCodeSessionParser.js\";\n\ndescribe(\"OpenCodeSessionParser\", () => {\n let db: Database.Database;\n let parser: OpenCodeSessionParser;\n\n beforeEach(() => {\n db = new Database(\":memory:\");\n db.exec(`\n CREATE TABLE message (id TEXT, session_id TEXT, data TEXT, time_created INTEGER, time_updated INTEGER);\n CREATE TABLE part (message_id TEXT, session_id TEXT, data TEXT, time_created INTEGER);\n `);\n parser = new OpenCodeSessionParser();\n });\n\n afterEach(() => {\n db.close();\n });\n\n it(\"extracts session stats from OpenCode messages and parts\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 1000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2500,\n completed: 2400,\n });\n insertPart(db, {\n messageId: \"msg-user\",\n sessionId: \"sess-1\",\n data: { type: \"text\", text: \"Plan the migration\" },\n timeCreated: 1000,\n });\n\n expect(parser.getSessionStats(db, \"sess-1\")).toEqual({\n lastRole: \"assistant\",\n lastTimeUpdated: 2500,\n lastAssistantCompleted: true,\n lastAssistantErrored: false,\n summary: \"Plan the migration\",\n });\n });\n\n it(\"orders stats by message creation time, not metadata update time\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 3000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2100,\n completed: 2200,\n });\n\n expect(parser.getSessionStats(db, \"sess-1\").lastRole).toBe(\"assistant\");\n });\n\n it(\"reads visible conversation text and hides verbose-only parts by default\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 1000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2000,\n });\n insertPart(db, {\n messageId: \"msg-user\",\n sessionId: \"sess-1\",\n data: { type: \"text\", text: \"Hello\" },\n timeCreated: 1000,\n });\n insertPart(db, {\n messageId: \"msg-assistant\",\n sessionId: \"sess-1\",\n data: { type: \"reasoning\", reasoning: \"private thought\" },\n timeCreated: 2000,\n });\n insertPart(db, {\n messageId: \"msg-assistant\",\n sessionId: \"sess-1\",\n data: { type: \"tool\", tool: \"read_file\" },\n timeCreated: 3000,\n });\n\n expect(parser.getConversation(db, \"sess-1\")).toEqual([{ role: \"user\", content: \"Hello\" }]);\n expect(parser.getConversation(db, \"sess-1\", { verbose: true })).toEqual([\n { role: \"user\", content: \"Hello\" },\n { role: \"assistant\", content: \"[thinking] private thought\" },\n { role: \"assistant\", content: \"[tool: read_file]\" },\n ]);\n });\n});\n\nfunction insertMessage(\n db: Database.Database,\n input: {\n id: string;\n sessionId: string;\n role: string;\n timeCreated: number;\n timeUpdated: number;\n completed?: number;\n },\n): void {\n db.prepare(\n \"INSERT INTO message (id, session_id, data, time_created, time_updated) VALUES (?, ?, ?, ?, ?)\",\n ).run(\n input.id,\n input.sessionId,\n JSON.stringify({\n role: input.role,\n time: input.completed === undefined ? {} : { completed: input.completed },\n }),\n input.timeCreated,\n input.timeUpdated,\n );\n}\n\nfunction insertPart(\n db: Database.Database,\n input: { messageId: string; sessionId: string; data: unknown; timeCreated: number },\n): void {\n db.prepare(\"INSERT INTO part (message_id, session_id, data, time_created) VALUES (?, ?, ?, ?)\")\n .run(input.messageId, input.sessionId, JSON.stringify(input.data), input.timeCreated);\n}\n"],"names":["Database","afterEach","beforeEach","describe","expect","it","OpenCodeSessionParser","db","parser","exec","close","insertMessage","id","sessionId","role","timeCreated","timeUpdated","completed","insertPart","messageId","data","type","text","getSessionStats","toEqual","lastRole","lastTimeUpdated","lastAssistantCompleted","lastAssistantErrored","summary","toBe","reasoning","tool","getConversation","content","verbose","input","prepare","run","JSON","stringify","time","undefined"],"mappings":"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"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { extractHerdrAgentPanes, fetchHerdrAgentPanes, findMatchingHerdrPane } from "../../runtime/herdr/HerdrAgentDiscovery.js";
|
|
2
|
+
function jsonResponse(value) {
|
|
3
|
+
return {
|
|
4
|
+
stdout: JSON.stringify(value),
|
|
5
|
+
stderr: ""
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
describe("HerdrAgentDiscovery", ()=>{
|
|
9
|
+
it("extracts agent panes from pane list responses", ()=>{
|
|
10
|
+
const panes = extractHerdrAgentPanes({
|
|
11
|
+
result: {
|
|
12
|
+
panes: [
|
|
13
|
+
{
|
|
14
|
+
agent: "codex",
|
|
15
|
+
agent_session: {
|
|
16
|
+
value: "session-1"
|
|
17
|
+
},
|
|
18
|
+
cwd: "/repo",
|
|
19
|
+
pane_id: "w32:p1",
|
|
20
|
+
workspace_id: "w32",
|
|
21
|
+
tab_id: "w32:t1"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
expect(panes).toEqual([
|
|
27
|
+
{
|
|
28
|
+
agent: "codex",
|
|
29
|
+
agentSessionId: "session-1",
|
|
30
|
+
cwd: "/repo",
|
|
31
|
+
paneId: "w32:p1",
|
|
32
|
+
workspaceId: "w32",
|
|
33
|
+
tabId: "w32:t1"
|
|
34
|
+
}
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
it("matches by foreground PID before falling back to ambiguous cwd and type", ()=>{
|
|
38
|
+
const pane = findMatchingHerdrPane({
|
|
39
|
+
type: "codex",
|
|
40
|
+
projectPath: "/repo",
|
|
41
|
+
sessionId: "pid-64904",
|
|
42
|
+
pid: 64904
|
|
43
|
+
}, [
|
|
44
|
+
{
|
|
45
|
+
agent: "codex",
|
|
46
|
+
cwd: "/repo",
|
|
47
|
+
paneId: "w31:p1",
|
|
48
|
+
foregroundPids: [
|
|
49
|
+
44290
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
agent: "codex",
|
|
54
|
+
cwd: "/repo",
|
|
55
|
+
paneId: "w32:p1",
|
|
56
|
+
foregroundPids: [
|
|
57
|
+
64904
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
]);
|
|
61
|
+
expect(pane?.paneId).toBe("w32:p1");
|
|
62
|
+
});
|
|
63
|
+
it("prefers a foreground PID match over a shell PID match", ()=>{
|
|
64
|
+
const pane = findMatchingHerdrPane({
|
|
65
|
+
type: "codex",
|
|
66
|
+
projectPath: "/repo",
|
|
67
|
+
sessionId: "pid-64904",
|
|
68
|
+
pid: 64904
|
|
69
|
+
}, [
|
|
70
|
+
{
|
|
71
|
+
agent: "codex",
|
|
72
|
+
cwd: "/repo",
|
|
73
|
+
paneId: "w31:p1",
|
|
74
|
+
shellPid: 64904
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
agent: "codex",
|
|
78
|
+
cwd: "/repo",
|
|
79
|
+
paneId: "w32:p1",
|
|
80
|
+
foregroundPids: [
|
|
81
|
+
64904
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
]);
|
|
85
|
+
expect(pane?.paneId).toBe("w32:p1");
|
|
86
|
+
});
|
|
87
|
+
it("enriches pane list output with foreground process IDs", async ()=>{
|
|
88
|
+
const runner = vi.fn().mockResolvedValueOnce(jsonResponse({
|
|
89
|
+
result: {
|
|
90
|
+
panes: [
|
|
91
|
+
{
|
|
92
|
+
agent: "codex",
|
|
93
|
+
cwd: "/repo",
|
|
94
|
+
pane_id: "w31:p1"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
agent: "codex",
|
|
98
|
+
cwd: "/repo",
|
|
99
|
+
pane_id: "w32:p1"
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
})).mockResolvedValueOnce(jsonResponse({
|
|
104
|
+
result: {
|
|
105
|
+
process_info: {
|
|
106
|
+
pane_id: "w31:p1",
|
|
107
|
+
shell_pid: 111,
|
|
108
|
+
foreground_processes: [
|
|
109
|
+
{
|
|
110
|
+
pid: 44290
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
})).mockResolvedValueOnce(jsonResponse({
|
|
116
|
+
result: {
|
|
117
|
+
process_info: {
|
|
118
|
+
pane_id: "w32:p1",
|
|
119
|
+
shell_pid: 222,
|
|
120
|
+
foreground_processes: [
|
|
121
|
+
{
|
|
122
|
+
pid: 64904
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}));
|
|
128
|
+
const panes = await fetchHerdrAgentPanes(runner);
|
|
129
|
+
expect(runner).toHaveBeenNthCalledWith(1, "herdr", [
|
|
130
|
+
"pane",
|
|
131
|
+
"list"
|
|
132
|
+
]);
|
|
133
|
+
expect(runner).toHaveBeenNthCalledWith(2, "herdr", [
|
|
134
|
+
"pane",
|
|
135
|
+
"process-info",
|
|
136
|
+
"--pane",
|
|
137
|
+
"w31:p1"
|
|
138
|
+
]);
|
|
139
|
+
expect(runner).toHaveBeenNthCalledWith(3, "herdr", [
|
|
140
|
+
"pane",
|
|
141
|
+
"process-info",
|
|
142
|
+
"--pane",
|
|
143
|
+
"w32:p1"
|
|
144
|
+
]);
|
|
145
|
+
expect(panes).toMatchObject([
|
|
146
|
+
{
|
|
147
|
+
paneId: "w31:p1",
|
|
148
|
+
foregroundPids: [
|
|
149
|
+
44290
|
|
150
|
+
],
|
|
151
|
+
shellPid: 111
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
paneId: "w32:p1",
|
|
155
|
+
foregroundPids: [
|
|
156
|
+
64904
|
|
157
|
+
],
|
|
158
|
+
shellPid: 222
|
|
159
|
+
}
|
|
160
|
+
]);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
//# sourceMappingURL=HerdrAgentDiscovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/__tests__/runtime/HerdrAgentDiscovery.test.ts"],"sourcesContent":["import {\n extractHerdrAgentPanes,\n fetchHerdrAgentPanes,\n findMatchingHerdrPane,\n} from \"../../runtime/herdr/HerdrAgentDiscovery.js\";\nimport type { HerdrCommandRunner } from \"../../runtime/herdr/HerdrCliClient.js\";\n\nfunction jsonResponse(value: unknown): { stdout: string; stderr: string } {\n return { stdout: JSON.stringify(value), stderr: \"\" };\n}\n\ndescribe(\"HerdrAgentDiscovery\", () => {\n it(\"extracts agent panes from pane list responses\", () => {\n const panes = extractHerdrAgentPanes({\n result: {\n panes: [\n {\n agent: \"codex\",\n agent_session: { value: \"session-1\" },\n cwd: \"/repo\",\n pane_id: \"w32:p1\",\n workspace_id: \"w32\",\n tab_id: \"w32:t1\",\n },\n ],\n },\n });\n\n expect(panes).toEqual([\n {\n agent: \"codex\",\n agentSessionId: \"session-1\",\n cwd: \"/repo\",\n paneId: \"w32:p1\",\n workspaceId: \"w32\",\n tabId: \"w32:t1\",\n },\n ]);\n });\n\n it(\"matches by foreground PID before falling back to ambiguous cwd and type\", () => {\n const pane = findMatchingHerdrPane(\n {\n type: \"codex\",\n projectPath: \"/repo\",\n sessionId: \"pid-64904\",\n pid: 64904,\n },\n [\n { agent: \"codex\", cwd: \"/repo\", paneId: \"w31:p1\", foregroundPids: [44290] },\n { agent: \"codex\", cwd: \"/repo\", paneId: \"w32:p1\", foregroundPids: [64904] },\n ],\n );\n\n expect(pane?.paneId).toBe(\"w32:p1\");\n });\n\n it(\"prefers a foreground PID match over a shell PID match\", () => {\n const pane = findMatchingHerdrPane(\n {\n type: \"codex\",\n projectPath: \"/repo\",\n sessionId: \"pid-64904\",\n pid: 64904,\n },\n [\n { agent: \"codex\", cwd: \"/repo\", paneId: \"w31:p1\", shellPid: 64904 },\n { agent: \"codex\", cwd: \"/repo\", paneId: \"w32:p1\", foregroundPids: [64904] },\n ],\n );\n\n expect(pane?.paneId).toBe(\"w32:p1\");\n });\n\n it(\"enriches pane list output with foreground process IDs\", async () => {\n const runner = vi\n .fn<HerdrCommandRunner>()\n .mockResolvedValueOnce(\n jsonResponse({\n result: {\n panes: [\n { agent: \"codex\", cwd: \"/repo\", pane_id: \"w31:p1\" },\n { agent: \"codex\", cwd: \"/repo\", pane_id: \"w32:p1\" },\n ],\n },\n }),\n )\n .mockResolvedValueOnce(\n jsonResponse({\n result: {\n process_info: {\n pane_id: \"w31:p1\",\n shell_pid: 111,\n foreground_processes: [{ pid: 44290 }],\n },\n },\n }),\n )\n .mockResolvedValueOnce(\n jsonResponse({\n result: {\n process_info: {\n pane_id: \"w32:p1\",\n shell_pid: 222,\n foreground_processes: [{ pid: 64904 }],\n },\n },\n }),\n );\n\n const panes = await fetchHerdrAgentPanes(runner);\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"pane\", \"list\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"pane\",\n \"process-info\",\n \"--pane\",\n \"w31:p1\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"pane\",\n \"process-info\",\n \"--pane\",\n \"w32:p1\",\n ]);\n expect(panes).toMatchObject([\n { paneId: \"w31:p1\", foregroundPids: [44290], shellPid: 111 },\n { paneId: \"w32:p1\", foregroundPids: [64904], shellPid: 222 },\n ]);\n });\n});\n"],"names":["extractHerdrAgentPanes","fetchHerdrAgentPanes","findMatchingHerdrPane","jsonResponse","value","stdout","JSON","stringify","stderr","describe","it","panes","result","agent","agent_session","cwd","pane_id","workspace_id","tab_id","expect","toEqual","agentSessionId","paneId","workspaceId","tabId","pane","type","projectPath","sessionId","pid","foregroundPids","toBe","shellPid","runner","vi","fn","mockResolvedValueOnce","process_info","shell_pid","foreground_processes","toHaveBeenNthCalledWith","toMatchObject"],"mappings":"AAAA,SACEA,sBAAsB,EACtBC,oBAAoB,EACpBC,qBAAqB,QAChB,6CAA6C;AAGpD,SAASC,aAAaC,KAAc;IAClC,OAAO;QAAEC,QAAQC,KAAKC,SAAS,CAACH;QAAQI,QAAQ;IAAG;AACrD;AAEAC,SAAS,uBAAuB;IAC9BC,GAAG,iDAAiD;QAClD,MAAMC,QAAQX,uBAAuB;YACnCY,QAAQ;gBACND,OAAO;oBACL;wBACEE,OAAO;wBACPC,eAAe;4BAAEV,OAAO;wBAAY;wBACpCW,KAAK;wBACLC,SAAS;wBACTC,cAAc;wBACdC,QAAQ;oBACV;iBACD;YACH;QACF;QAEAC,OAAOR,OAAOS,OAAO,CAAC;YACpB;gBACEP,OAAO;gBACPQ,gBAAgB;gBAChBN,KAAK;gBACLO,QAAQ;gBACRC,aAAa;gBACbC,OAAO;YACT;SACD;IACH;IAEAd,GAAG,2EAA2E;QAC5E,MAAMe,OAAOvB,sBACX;YACEwB,MAAM;YACNC,aAAa;YACbC,WAAW;YACXC,KAAK;QACP,GACA;YACE;gBAAEhB,OAAO;gBAASE,KAAK;gBAASO,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;YAAC;YAC1E;gBAAEjB,OAAO;gBAASE,KAAK;gBAASO,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;YAAC;SAC3E;QAGHX,OAAOM,MAAMH,QAAQS,IAAI,CAAC;IAC5B;IAEArB,GAAG,yDAAyD;QAC1D,MAAMe,OAAOvB,sBACX;YACEwB,MAAM;YACNC,aAAa;YACbC,WAAW;YACXC,KAAK;QACP,GACA;YACE;gBAAEhB,OAAO;gBAASE,KAAK;gBAASO,QAAQ;gBAAUU,UAAU;YAAM;YAClE;gBAAEnB,OAAO;gBAASE,KAAK;gBAASO,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;YAAC;SAC3E;QAGHX,OAAOM,MAAMH,QAAQS,IAAI,CAAC;IAC5B;IAEArB,GAAG,yDAAyD;QAC1D,MAAMuB,SAASC,GACZC,EAAE,GACFC,qBAAqB,CACpBjC,aAAa;YACXS,QAAQ;gBACND,OAAO;oBACL;wBAAEE,OAAO;wBAASE,KAAK;wBAASC,SAAS;oBAAS;oBAClD;wBAAEH,OAAO;wBAASE,KAAK;wBAASC,SAAS;oBAAS;iBACnD;YACH;QACF,IAEDoB,qBAAqB,CACpBjC,aAAa;YACXS,QAAQ;gBACNyB,cAAc;oBACZrB,SAAS;oBACTsB,WAAW;oBACXC,sBAAsB;wBAAC;4BAAEV,KAAK;wBAAM;qBAAE;gBACxC;YACF;QACF,IAEDO,qBAAqB,CACpBjC,aAAa;YACXS,QAAQ;gBACNyB,cAAc;oBACZrB,SAAS;oBACTsB,WAAW;oBACXC,sBAAsB;wBAAC;4BAAEV,KAAK;wBAAM;qBAAE;gBACxC;YACF;QACF;QAGJ,MAAMlB,QAAQ,MAAMV,qBAAqBgC;QAEzCd,OAAOc,QAAQO,uBAAuB,CAAC,GAAG,SAAS;YAAC;YAAQ;SAAO;QACnErB,OAAOc,QAAQO,uBAAuB,CAAC,GAAG,SAAS;YACjD;YACA;YACA;YACA;SACD;QACDrB,OAAOc,QAAQO,uBAAuB,CAAC,GAAG,SAAS;YACjD;YACA;YACA;YACA;SACD;QACDrB,OAAOR,OAAO8B,aAAa,CAAC;YAC1B;gBAAEnB,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;gBAAEE,UAAU;YAAI;YAC3D;gBAAEV,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;gBAAEE,UAAU;YAAI;SAC5D;IACH;AACF"}
|
|
@@ -222,12 +222,48 @@ describe("HerdrAgentRuntime", ()=>{
|
|
|
222
222
|
]);
|
|
223
223
|
const runtime = new HerdrAgentRuntime({
|
|
224
224
|
runner,
|
|
225
|
-
env: {}
|
|
225
|
+
env: {},
|
|
226
|
+
processInfoPollMs: 0
|
|
226
227
|
});
|
|
227
228
|
await expect(runtime.startAgent(startInput)).resolves.toMatchObject({
|
|
228
229
|
pid: 12000
|
|
229
230
|
});
|
|
230
231
|
});
|
|
232
|
+
it("waits for a foreground process pid before falling back to the pane shell pid", async ()=>{
|
|
233
|
+
const runner = createRunner([
|
|
234
|
+
workspaceResponse(),
|
|
235
|
+
agentResponse(),
|
|
236
|
+
processInfoResponse({
|
|
237
|
+
shellPid: 12000
|
|
238
|
+
}),
|
|
239
|
+
processInfoResponse({
|
|
240
|
+
shellPid: 12000,
|
|
241
|
+
pid: 12345,
|
|
242
|
+
processName: "node"
|
|
243
|
+
})
|
|
244
|
+
]);
|
|
245
|
+
const runtime = new HerdrAgentRuntime({
|
|
246
|
+
runner,
|
|
247
|
+
env: {},
|
|
248
|
+
processInfoPollMs: 100,
|
|
249
|
+
processInfoPollIntervalMs: 0
|
|
250
|
+
});
|
|
251
|
+
await expect(runtime.startAgent(startInput)).resolves.toMatchObject({
|
|
252
|
+
pid: 12345
|
|
253
|
+
});
|
|
254
|
+
expect(runner).toHaveBeenNthCalledWith(3, "herdr", [
|
|
255
|
+
"pane",
|
|
256
|
+
"process-info",
|
|
257
|
+
"--pane",
|
|
258
|
+
"w1:p1"
|
|
259
|
+
]);
|
|
260
|
+
expect(runner).toHaveBeenNthCalledWith(4, "herdr", [
|
|
261
|
+
"pane",
|
|
262
|
+
"process-info",
|
|
263
|
+
"--pane",
|
|
264
|
+
"w1:p1"
|
|
265
|
+
]);
|
|
266
|
+
});
|
|
231
267
|
it("passes only explicit extra agent args after the Herdr start separator", async ()=>{
|
|
232
268
|
const runner = createRunner([
|
|
233
269
|
workspaceResponse(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/__tests__/runtime/HerdrAgentRuntime.test.ts"],"sourcesContent":["import { HerdrAgentRuntime } from \"../../runtime/herdr/HerdrAgentRuntime.js\";\nimport { HerdrRuntimeError } from \"../../runtime/herdr/HerdrErrors.js\";\nimport type { HerdrCommandRunner } from \"../../runtime/herdr/HerdrCliClient.js\";\n\nfunction createRunner(\n responses: Array<{ stdout?: string; stderr?: string; reject?: Error }> = [],\n): HerdrCommandRunner {\n const runner = vi.fn(async () => {\n const response = responses.shift();\n if (!response) return { stdout: \"{}\", stderr: \"\" };\n if (response.reject) throw response.reject;\n return { stdout: response.stdout ?? \"\", stderr: response.stderr ?? \"\" };\n });\n return runner;\n}\n\nfunction herdrCliError(response: unknown): Error {\n return Object.assign(new Error(\"herdr command failed\"), {\n stdout: JSON.stringify(response),\n stderr: \"\",\n code: 1,\n });\n}\n\nfunction jsonResponse(value: unknown): { stdout: string } {\n return { stdout: JSON.stringify(value) };\n}\n\nfunction workspaceResponse(workspaceId = \"w1\", paneId = \"w1:p1\"): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"workspace_info\",\n workspace: { workspace_id: workspaceId },\n root_pane: { pane_id: paneId },\n },\n });\n}\n\nfunction agentResponse({\n name = \"reviewer\",\n paneId = \"w1:p1\",\n workspaceId,\n tabId,\n}: {\n name?: string;\n paneId?: string;\n workspaceId?: string;\n tabId?: string;\n} = {}): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"agent_info\",\n agent: {\n name,\n agent: \"codex\",\n pane_id: paneId,\n ...(workspaceId ? { workspace_id: workspaceId } : {}),\n ...(tabId ? { tab_id: tabId } : {}),\n },\n },\n });\n}\n\nfunction processInfoResponse({\n paneId = \"w1:p1\",\n pid,\n shellPid,\n processName,\n}: {\n paneId?: string;\n pid?: number;\n shellPid?: number;\n processName?: string;\n} = {}): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"pane_process_info\",\n process_info: {\n pane_id: paneId,\n ...(shellPid ? { shell_pid: shellPid } : {}),\n foreground_processes: pid ? [{ pid, ...(processName ? { name: processName } : {}) }] : [],\n },\n },\n });\n}\n\nconst startInput = {\n name: \"reviewer\",\n cwd: \"/repo\",\n kind: \"codex\",\n args: [],\n timeoutMs: 15000,\n};\n\ndescribe(\"HerdrAgentRuntime\", () => {\n it(\"reports unavailable when the herdr binary is missing\", async () => {\n const runner = createRunner([\n { reject: Object.assign(new Error(\"not found\"), { code: \"ENOENT\" }) },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: false,\n reason: \"binary-missing\",\n detail: \"herdr command was not found in PATH.\",\n });\n });\n\n it(\"reports available outside Herdr when binary and snapshot work\", async () => {\n const runner = createRunner([\n { stdout: \"herdr 0.8.2\\n\" },\n jsonResponse({ id: \"cli:api:snapshot\", result: { type: \"session_snapshot\" } }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: true,\n insideRuntime: false,\n });\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"--version\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\"api\", \"snapshot\"]);\n });\n\n it(\"includes Herdr environment context when present\", async () => {\n const runner = createRunner([\n { stdout: \"herdr 0.8.2\\n\" },\n jsonResponse({ id: \"cli:api:snapshot\", result: { type: \"session_snapshot\" } }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: { HERDR_ENV: \"1\", HERDR_PANE_ID: \"w1:p1\" },\n });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: true,\n insideRuntime: true,\n currentPaneId: \"w1:p1\",\n });\n });\n\n it(\"creates a workspace, starts the named agent, and returns an opaque Herdr ref\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse({ workspaceId: \"w1\", tabId: \"w1:t1\" }),\n processInfoResponse({ shellPid: 12000, pid: 12345, processName: \"codex\" }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n const result = await runtime.startAgent(startInput);\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\n \"workspace\",\n \"create\",\n \"--cwd\",\n \"/repo\",\n \"--label\",\n \"reviewer\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w1:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\"pane\", \"process-info\", \"--pane\", \"w1:p1\"]);\n expect(result).toEqual({\n pid: 12345,\n runtimeRef: {\n session: \"default\",\n workspaceId: \"w1\",\n tabId: \"w1:t1\",\n paneId: \"w1:p1\",\n agentName: \"reviewer\",\n },\n });\n });\n\n it(\"falls back to the pane shell pid when no foreground process pid is reported\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ shellPid: 12000 }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({ pid: 12000 });\n });\n\n it(\"passes only explicit extra agent args after the Herdr start separator\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ pid: 12345 }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await runtime.startAgent({\n ...startInput,\n args: [\"--model\", \"gpt-5-codex\"],\n });\n\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w1:p1\",\n \"--timeout\",\n \"15000\",\n \"--\",\n \"--model\",\n \"gpt-5-codex\",\n ]);\n });\n\n it(\"retries agent start when a freshly-created Herdr pane is temporarily busy\", async () => {\n const runner = createRunner([\n workspaceResponse(\"w15\", \"w15:p1\"),\n {\n reject: herdrCliError({\n error: {\n code: \"agent_pane_busy\",\n message: \"agent target pane w15:p1 is not an available shell\",\n },\n id: \"cli:agent:start\",\n }),\n },\n agentResponse({ paneId: \"w15:p1\", workspaceId: \"w15\", tabId: \"w15:t1\" }),\n processInfoResponse({ paneId: \"w15:p1\", pid: 12345 }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: {},\n agentStartBusyRetryMs: 100,\n agentStartBusyRetryIntervalMs: 0,\n });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({\n runtimeRef: {\n workspaceId: \"w15\",\n paneId: \"w15:p1\",\n },\n });\n\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w15:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w15:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n });\n\n it(\"surfaces non-retryable Herdr agent start errors without retrying\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n {\n reject: herdrCliError({\n error: {\n code: \"duplicate_agent_name\",\n message: \"agent name reviewer already exists\",\n },\n id: \"cli:agent:start\",\n }),\n },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toMatchObject({\n code: \"duplicate_agent_name\",\n message: \"agent name reviewer already exists\",\n });\n\n expect(runner).toHaveBeenCalledTimes(2);\n });\n\n it(\"rejects start responses when Herdr process info does not include a pid\", async () => {\n const runner = createRunner([workspaceResponse(), agentResponse(), processInfoResponse()]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toThrow(\n new HerdrRuntimeError(\"Herdr pane process-info response did not include process pid.\"),\n );\n });\n\n it(\"rejects start responses that do not include a pane id\", async () => {\n const runner = createRunner([{ stdout: '{\"result\":{\"workspace\":{\"workspace_id\":\"w1\"}}}' }]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toThrow(\n new HerdrRuntimeError(\"Herdr workspace create response did not include root pane id.\"),\n );\n });\n\n it(\"sends, waits, reads, focuses, and stops by Herdr pane ref\", async () => {\n const runner = createRunner([\n { stdout: \"{}\" },\n { stdout: '{\"result\":{\"agent\":{\"agent_status\":\"done\"}}}' },\n { stdout: \"done\\n\" },\n { stdout: \"{}\" },\n { stdout: \"{}\" },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n const runtimeRef = { paneId: \"w1:p2\", agentName: \"reviewer\" };\n\n await runtime.send({ runtimeRef, prompt: \"continue\" });\n await runtime.wait({ runtimeRef, timeoutMs: 2000 });\n await expect(runtime.readOutput({ runtimeRef, lines: 80 })).resolves.toBe(\"done\\n\");\n await expect(runtime.focus({ runtimeRef })).resolves.toBe(true);\n await runtime.stop({ runtimeRef });\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"agent\", \"prompt\", \"w1:p2\", \"continue\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"wait\",\n \"w1:p2\",\n \"--until\",\n \"done\",\n \"--until\",\n \"blocked\",\n \"--timeout\",\n \"2000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"agent\",\n \"read\",\n \"w1:p2\",\n \"--source\",\n \"recent-unwrapped\",\n \"--lines\",\n \"80\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(4, \"herdr\", [\"agent\", \"focus\", \"w1:p2\"]);\n expect(runner).toHaveBeenNthCalledWith(5, \"herdr\", [\"pane\", \"close\", \"w1:p2\"]);\n });\n});\n"],"names":["HerdrAgentRuntime","HerdrRuntimeError","createRunner","responses","runner","vi","fn","response","shift","stdout","stderr","reject","herdrCliError","Object","assign","Error","JSON","stringify","code","jsonResponse","value","workspaceResponse","workspaceId","paneId","result","type","workspace","workspace_id","root_pane","pane_id","agentResponse","name","tabId","agent","tab_id","processInfoResponse","pid","shellPid","processName","process_info","shell_pid","foreground_processes","startInput","cwd","kind","args","timeoutMs","describe","it","runtime","env","expect","isAvailable","resolves","toEqual","ok","reason","detail","id","insideRuntime","toHaveBeenNthCalledWith","HERDR_ENV","HERDR_PANE_ID","currentPaneId","startAgent","runtimeRef","session","agentName","toMatchObject","error","message","agentStartBusyRetryMs","agentStartBusyRetryIntervalMs","rejects","toHaveBeenCalledTimes","toThrow","send","prompt","wait","readOutput","lines","toBe","focus","stop"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/__tests__/runtime/HerdrAgentRuntime.test.ts"],"sourcesContent":["import { HerdrAgentRuntime } from \"../../runtime/herdr/HerdrAgentRuntime.js\";\nimport { HerdrRuntimeError } from \"../../runtime/herdr/HerdrErrors.js\";\nimport type { HerdrCommandRunner } from \"../../runtime/herdr/HerdrCliClient.js\";\n\nfunction createRunner(\n responses: Array<{ stdout?: string; stderr?: string; reject?: Error }> = [],\n): HerdrCommandRunner {\n const runner = vi.fn(async () => {\n const response = responses.shift();\n if (!response) return { stdout: \"{}\", stderr: \"\" };\n if (response.reject) throw response.reject;\n return { stdout: response.stdout ?? \"\", stderr: response.stderr ?? \"\" };\n });\n return runner;\n}\n\nfunction herdrCliError(response: unknown): Error {\n return Object.assign(new Error(\"herdr command failed\"), {\n stdout: JSON.stringify(response),\n stderr: \"\",\n code: 1,\n });\n}\n\nfunction jsonResponse(value: unknown): { stdout: string } {\n return { stdout: JSON.stringify(value) };\n}\n\nfunction workspaceResponse(workspaceId = \"w1\", paneId = \"w1:p1\"): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"workspace_info\",\n workspace: { workspace_id: workspaceId },\n root_pane: { pane_id: paneId },\n },\n });\n}\n\nfunction agentResponse({\n name = \"reviewer\",\n paneId = \"w1:p1\",\n workspaceId,\n tabId,\n}: {\n name?: string;\n paneId?: string;\n workspaceId?: string;\n tabId?: string;\n} = {}): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"agent_info\",\n agent: {\n name,\n agent: \"codex\",\n pane_id: paneId,\n ...(workspaceId ? { workspace_id: workspaceId } : {}),\n ...(tabId ? { tab_id: tabId } : {}),\n },\n },\n });\n}\n\nfunction processInfoResponse({\n paneId = \"w1:p1\",\n pid,\n shellPid,\n processName,\n}: {\n paneId?: string;\n pid?: number;\n shellPid?: number;\n processName?: string;\n} = {}): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"pane_process_info\",\n process_info: {\n pane_id: paneId,\n ...(shellPid ? { shell_pid: shellPid } : {}),\n foreground_processes: pid ? [{ pid, ...(processName ? { name: processName } : {}) }] : [],\n },\n },\n });\n}\n\nconst startInput = {\n name: \"reviewer\",\n cwd: \"/repo\",\n kind: \"codex\",\n args: [],\n timeoutMs: 15000,\n};\n\ndescribe(\"HerdrAgentRuntime\", () => {\n it(\"reports unavailable when the herdr binary is missing\", async () => {\n const runner = createRunner([\n { reject: Object.assign(new Error(\"not found\"), { code: \"ENOENT\" }) },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: false,\n reason: \"binary-missing\",\n detail: \"herdr command was not found in PATH.\",\n });\n });\n\n it(\"reports available outside Herdr when binary and snapshot work\", async () => {\n const runner = createRunner([\n { stdout: \"herdr 0.8.2\\n\" },\n jsonResponse({ id: \"cli:api:snapshot\", result: { type: \"session_snapshot\" } }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: true,\n insideRuntime: false,\n });\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"--version\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\"api\", \"snapshot\"]);\n });\n\n it(\"includes Herdr environment context when present\", async () => {\n const runner = createRunner([\n { stdout: \"herdr 0.8.2\\n\" },\n jsonResponse({ id: \"cli:api:snapshot\", result: { type: \"session_snapshot\" } }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: { HERDR_ENV: \"1\", HERDR_PANE_ID: \"w1:p1\" },\n });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: true,\n insideRuntime: true,\n currentPaneId: \"w1:p1\",\n });\n });\n\n it(\"creates a workspace, starts the named agent, and returns an opaque Herdr ref\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse({ workspaceId: \"w1\", tabId: \"w1:t1\" }),\n processInfoResponse({ shellPid: 12000, pid: 12345, processName: \"codex\" }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n const result = await runtime.startAgent(startInput);\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\n \"workspace\",\n \"create\",\n \"--cwd\",\n \"/repo\",\n \"--label\",\n \"reviewer\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w1:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\"pane\", \"process-info\", \"--pane\", \"w1:p1\"]);\n expect(result).toEqual({\n pid: 12345,\n runtimeRef: {\n session: \"default\",\n workspaceId: \"w1\",\n tabId: \"w1:t1\",\n paneId: \"w1:p1\",\n agentName: \"reviewer\",\n },\n });\n });\n\n it(\"falls back to the pane shell pid when no foreground process pid is reported\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ shellPid: 12000 }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {}, processInfoPollMs: 0 });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({ pid: 12000 });\n });\n\n it(\"waits for a foreground process pid before falling back to the pane shell pid\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ shellPid: 12000 }),\n processInfoResponse({ shellPid: 12000, pid: 12345, processName: \"node\" }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: {},\n processInfoPollMs: 100,\n processInfoPollIntervalMs: 0,\n });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({ pid: 12345 });\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\"pane\", \"process-info\", \"--pane\", \"w1:p1\"]);\n expect(runner).toHaveBeenNthCalledWith(4, \"herdr\", [\"pane\", \"process-info\", \"--pane\", \"w1:p1\"]);\n });\n\n it(\"passes only explicit extra agent args after the Herdr start separator\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ pid: 12345 }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await runtime.startAgent({\n ...startInput,\n args: [\"--model\", \"gpt-5-codex\"],\n });\n\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w1:p1\",\n \"--timeout\",\n \"15000\",\n \"--\",\n \"--model\",\n \"gpt-5-codex\",\n ]);\n });\n\n it(\"retries agent start when a freshly-created Herdr pane is temporarily busy\", async () => {\n const runner = createRunner([\n workspaceResponse(\"w15\", \"w15:p1\"),\n {\n reject: herdrCliError({\n error: {\n code: \"agent_pane_busy\",\n message: \"agent target pane w15:p1 is not an available shell\",\n },\n id: \"cli:agent:start\",\n }),\n },\n agentResponse({ paneId: \"w15:p1\", workspaceId: \"w15\", tabId: \"w15:t1\" }),\n processInfoResponse({ paneId: \"w15:p1\", pid: 12345 }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: {},\n agentStartBusyRetryMs: 100,\n agentStartBusyRetryIntervalMs: 0,\n });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({\n runtimeRef: {\n workspaceId: \"w15\",\n paneId: \"w15:p1\",\n },\n });\n\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w15:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w15:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n });\n\n it(\"surfaces non-retryable Herdr agent start errors without retrying\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n {\n reject: herdrCliError({\n error: {\n code: \"duplicate_agent_name\",\n message: \"agent name reviewer already exists\",\n },\n id: \"cli:agent:start\",\n }),\n },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toMatchObject({\n code: \"duplicate_agent_name\",\n message: \"agent name reviewer already exists\",\n });\n\n expect(runner).toHaveBeenCalledTimes(2);\n });\n\n it(\"rejects start responses when Herdr process info does not include a pid\", async () => {\n const runner = createRunner([workspaceResponse(), agentResponse(), processInfoResponse()]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toThrow(\n new HerdrRuntimeError(\"Herdr pane process-info response did not include process pid.\"),\n );\n });\n\n it(\"rejects start responses that do not include a pane id\", async () => {\n const runner = createRunner([{ stdout: '{\"result\":{\"workspace\":{\"workspace_id\":\"w1\"}}}' }]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toThrow(\n new HerdrRuntimeError(\"Herdr workspace create response did not include root pane id.\"),\n );\n });\n\n it(\"sends, waits, reads, focuses, and stops by Herdr pane ref\", async () => {\n const runner = createRunner([\n { stdout: \"{}\" },\n { stdout: '{\"result\":{\"agent\":{\"agent_status\":\"done\"}}}' },\n { stdout: \"done\\n\" },\n { stdout: \"{}\" },\n { stdout: \"{}\" },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n const runtimeRef = { paneId: \"w1:p2\", agentName: \"reviewer\" };\n\n await runtime.send({ runtimeRef, prompt: \"continue\" });\n await runtime.wait({ runtimeRef, timeoutMs: 2000 });\n await expect(runtime.readOutput({ runtimeRef, lines: 80 })).resolves.toBe(\"done\\n\");\n await expect(runtime.focus({ runtimeRef })).resolves.toBe(true);\n await runtime.stop({ runtimeRef });\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"agent\", \"prompt\", \"w1:p2\", \"continue\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"wait\",\n \"w1:p2\",\n \"--until\",\n \"done\",\n \"--until\",\n \"blocked\",\n \"--timeout\",\n \"2000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"agent\",\n \"read\",\n \"w1:p2\",\n \"--source\",\n \"recent-unwrapped\",\n \"--lines\",\n \"80\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(4, \"herdr\", [\"agent\", \"focus\", \"w1:p2\"]);\n expect(runner).toHaveBeenNthCalledWith(5, \"herdr\", [\"pane\", \"close\", \"w1:p2\"]);\n });\n});\n"],"names":["HerdrAgentRuntime","HerdrRuntimeError","createRunner","responses","runner","vi","fn","response","shift","stdout","stderr","reject","herdrCliError","Object","assign","Error","JSON","stringify","code","jsonResponse","value","workspaceResponse","workspaceId","paneId","result","type","workspace","workspace_id","root_pane","pane_id","agentResponse","name","tabId","agent","tab_id","processInfoResponse","pid","shellPid","processName","process_info","shell_pid","foreground_processes","startInput","cwd","kind","args","timeoutMs","describe","it","runtime","env","expect","isAvailable","resolves","toEqual","ok","reason","detail","id","insideRuntime","toHaveBeenNthCalledWith","HERDR_ENV","HERDR_PANE_ID","currentPaneId","startAgent","runtimeRef","session","agentName","processInfoPollMs","toMatchObject","processInfoPollIntervalMs","error","message","agentStartBusyRetryMs","agentStartBusyRetryIntervalMs","rejects","toHaveBeenCalledTimes","toThrow","send","prompt","wait","readOutput","lines","toBe","focus","stop"],"mappings":"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"}
|
|
@@ -96,7 +96,7 @@ describe("AgentRegistry", ()=>{
|
|
|
96
96
|
runtimeRef
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
|
-
it("
|
|
99
|
+
it("preserves existing identity and routing fields for same-pid refreshes", ()=>{
|
|
100
100
|
const runtimeRef = {
|
|
101
101
|
session: "default",
|
|
102
102
|
paneId: "w1:p2",
|
|
@@ -104,19 +104,28 @@ describe("AgentRegistry", ()=>{
|
|
|
104
104
|
};
|
|
105
105
|
registry.register(makeEntry({
|
|
106
106
|
name: "old-name",
|
|
107
|
-
pid: process.pid
|
|
107
|
+
pid: process.pid,
|
|
108
|
+
runtime: "tmux",
|
|
109
|
+
runtimeRef: {
|
|
110
|
+
session: "old-name"
|
|
111
|
+
},
|
|
112
|
+
cwd: "/original"
|
|
108
113
|
}));
|
|
109
114
|
registry.register(makeEntry({
|
|
110
115
|
name: "requested-name",
|
|
111
116
|
pid: process.pid,
|
|
112
117
|
runtime: "herdr",
|
|
113
|
-
runtimeRef
|
|
118
|
+
runtimeRef,
|
|
119
|
+
cwd: "/incoming"
|
|
114
120
|
}));
|
|
115
|
-
expect(registry.lookup("
|
|
116
|
-
expect(registry.lookup("
|
|
121
|
+
expect(registry.lookup("requested-name")).toBeNull();
|
|
122
|
+
expect(registry.lookup("old-name")).toMatchObject({
|
|
117
123
|
pid: process.pid,
|
|
118
|
-
runtime: "
|
|
119
|
-
runtimeRef
|
|
124
|
+
runtime: "tmux",
|
|
125
|
+
runtimeRef: {
|
|
126
|
+
session: "old-name"
|
|
127
|
+
},
|
|
128
|
+
cwd: "/original"
|
|
120
129
|
});
|
|
121
130
|
});
|
|
122
131
|
it("preserves existing runtime ref when incoming runtime ref is empty", ()=>{
|
|
@@ -144,7 +153,7 @@ describe("AgentRegistry", ()=>{
|
|
|
144
153
|
}));
|
|
145
154
|
expect(registry.lookup("a")?.runtimeRef).toBeNull();
|
|
146
155
|
});
|
|
147
|
-
it("
|
|
156
|
+
it("preserves an existing generated fallback for the same pid", ()=>{
|
|
148
157
|
registry.register(makeEntry({
|
|
149
158
|
name: `ai-devkit-${process.pid}`,
|
|
150
159
|
runtimeRef: null
|
|
@@ -155,10 +164,8 @@ describe("AgentRegistry", ()=>{
|
|
|
155
164
|
session: "custom-name"
|
|
156
165
|
}
|
|
157
166
|
}));
|
|
158
|
-
expect(registry.lookup("custom-name")
|
|
159
|
-
|
|
160
|
-
});
|
|
161
|
-
expect(registry.lookup(`ai-devkit-${process.pid}`)).toBeNull();
|
|
167
|
+
expect(registry.lookup("custom-name")).toBeNull();
|
|
168
|
+
expect(registry.lookup(`ai-devkit-${process.pid}`)?.runtimeRef).toBeNull();
|
|
162
169
|
expect(registry.list()).toHaveLength(1);
|
|
163
170
|
});
|
|
164
171
|
it("preserves an existing name conflict when its probe fails with EPERM", ()=>{
|
|
@@ -241,7 +248,7 @@ describe("AgentRegistry", ()=>{
|
|
|
241
248
|
runtimeRef: null
|
|
242
249
|
}));
|
|
243
250
|
expect(registry.list()).toHaveLength(1);
|
|
244
|
-
expect(registry.lookup(
|
|
251
|
+
expect(registry.lookup(`ai-devkit-${process.pid}`)?.pid).toBe(process.pid);
|
|
245
252
|
});
|
|
246
253
|
it("cleans up a cross-type row when its pid has been reused", ()=>{
|
|
247
254
|
registry.register(makeEntry({
|