@zhijiewang/openharness 0.10.1 → 0.11.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/README.md +79 -13
- package/dist/Tool.d.ts.map +1 -1
- package/dist/Tool.js +7 -1
- package/dist/Tool.js.map +1 -1
- package/dist/Tool.test.js +8 -2
- package/dist/Tool.test.js.map +1 -1
- package/dist/agents/roles.d.ts +25 -0
- package/dist/agents/roles.d.ts.map +1 -0
- package/dist/agents/roles.js +116 -0
- package/dist/agents/roles.js.map +1 -0
- package/dist/agents/roles.test.d.ts +2 -0
- package/dist/agents/roles.test.d.ts.map +1 -0
- package/dist/agents/roles.test.js +38 -0
- package/dist/agents/roles.test.js.map +1 -0
- package/dist/commands/commands-new.test.d.ts +5 -0
- package/dist/commands/commands-new.test.d.ts.map +1 -0
- package/dist/commands/commands-new.test.js +132 -0
- package/dist/commands/commands-new.test.js.map +1 -0
- package/dist/commands/commands.test.js +31 -0
- package/dist/commands/commands.test.js.map +1 -1
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +199 -6
- package/dist/commands/index.js.map +1 -1
- package/dist/components/REPL.js +1 -1
- package/dist/components/REPL.js.map +1 -1
- package/dist/git/git.test.js +33 -1
- package/dist/git/git.test.js.map +1 -1
- package/dist/git/index.d.ts +12 -0
- package/dist/git/index.d.ts.map +1 -1
- package/dist/git/index.js +47 -2
- package/dist/git/index.js.map +1 -1
- package/dist/harness/checkpoints.d.ts +36 -0
- package/dist/harness/checkpoints.d.ts.map +1 -0
- package/dist/harness/checkpoints.js +156 -0
- package/dist/harness/checkpoints.js.map +1 -0
- package/dist/harness/config.d.ts +3 -0
- package/dist/harness/config.d.ts.map +1 -1
- package/dist/harness/config.js +35 -2
- package/dist/harness/config.js.map +1 -1
- package/dist/harness/config.test.js +21 -1
- package/dist/harness/config.test.js.map +1 -1
- package/dist/harness/hooks-env.test.d.ts +5 -0
- package/dist/harness/hooks-env.test.d.ts.map +1 -0
- package/dist/harness/hooks-env.test.js +41 -0
- package/dist/harness/hooks-env.test.js.map +1 -0
- package/dist/harness/hooks.d.ts +7 -0
- package/dist/harness/hooks.d.ts.map +1 -1
- package/dist/harness/hooks.js +14 -0
- package/dist/harness/hooks.js.map +1 -1
- package/dist/harness/keybindings.d.ts.map +1 -1
- package/dist/harness/keybindings.js +4 -0
- package/dist/harness/keybindings.js.map +1 -1
- package/dist/harness/memory.d.ts +19 -0
- package/dist/harness/memory.d.ts.map +1 -1
- package/dist/harness/memory.js +85 -0
- package/dist/harness/memory.js.map +1 -1
- package/dist/harness/onboarding.d.ts +1 -1
- package/dist/harness/onboarding.d.ts.map +1 -1
- package/dist/harness/onboarding.js +59 -4
- package/dist/harness/onboarding.js.map +1 -1
- package/dist/harness/onboarding.test.d.ts +5 -0
- package/dist/harness/onboarding.test.d.ts.map +1 -0
- package/dist/harness/onboarding.test.js +93 -0
- package/dist/harness/onboarding.test.js.map +1 -0
- package/dist/harness/rules.d.ts +6 -1
- package/dist/harness/rules.d.ts.map +1 -1
- package/dist/harness/rules.js +52 -5
- package/dist/harness/rules.js.map +1 -1
- package/dist/harness/rules.test.js +30 -1
- package/dist/harness/rules.test.js.map +1 -1
- package/dist/harness/session.d.ts +8 -1
- package/dist/harness/session.d.ts.map +1 -1
- package/dist/harness/session.js +13 -5
- package/dist/harness/session.js.map +1 -1
- package/dist/harness/store.d.ts +46 -0
- package/dist/harness/store.d.ts.map +1 -0
- package/dist/harness/store.js +56 -0
- package/dist/harness/store.js.map +1 -0
- package/dist/harness/store.test.d.ts +2 -0
- package/dist/harness/store.test.d.ts.map +1 -0
- package/dist/harness/store.test.js +71 -0
- package/dist/harness/store.test.js.map +1 -0
- package/dist/harness/submit-handler.d.ts +2 -0
- package/dist/harness/submit-handler.d.ts.map +1 -1
- package/dist/harness/submit-handler.js +3 -0
- package/dist/harness/submit-handler.js.map +1 -1
- package/dist/main.js +153 -26
- package/dist/main.js.map +1 -1
- package/dist/mcp/client.d.ts +2 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +10 -2
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/loader.d.ts +2 -0
- package/dist/mcp/loader.d.ts.map +1 -1
- package/dist/mcp/loader.js +34 -18
- package/dist/mcp/loader.js.map +1 -1
- package/dist/mcp/loader.test.d.ts +7 -0
- package/dist/mcp/loader.test.d.ts.map +1 -0
- package/dist/mcp/loader.test.js +25 -0
- package/dist/mcp/loader.test.js.map +1 -0
- package/dist/providers/anthropic-convert.test.d.ts +5 -0
- package/dist/providers/anthropic-convert.test.d.ts.map +1 -0
- package/dist/providers/anthropic-convert.test.js +98 -0
- package/dist/providers/anthropic-convert.test.js.map +1 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +23 -4
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/stream-parsing.test.d.ts +6 -0
- package/dist/providers/stream-parsing.test.d.ts.map +1 -0
- package/dist/providers/stream-parsing.test.js +174 -0
- package/dist/providers/stream-parsing.test.js.map +1 -0
- package/dist/query/compress.d.ts +17 -0
- package/dist/query/compress.d.ts.map +1 -0
- package/dist/query/compress.js +115 -0
- package/dist/query/compress.js.map +1 -0
- package/dist/query/errors.d.ts +10 -0
- package/dist/query/errors.d.ts.map +1 -0
- package/dist/query/errors.js +22 -0
- package/dist/query/errors.js.map +1 -0
- package/dist/query/index.d.ts +15 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +199 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/tools.d.ts +17 -0
- package/dist/query/tools.d.ts.map +1 -0
- package/dist/query/tools.js +129 -0
- package/dist/query/tools.js.map +1 -0
- package/dist/query/types.d.ts +31 -0
- package/dist/query/types.d.ts.map +1 -0
- package/dist/query/types.js +5 -0
- package/dist/query/types.js.map +1 -0
- package/dist/query.d.ts +8 -38
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +7 -444
- package/dist/query.js.map +1 -1
- package/dist/query.test.js +1 -1
- package/dist/query.test.js.map +1 -1
- package/dist/renderer/cells.d.ts.map +1 -1
- package/dist/renderer/cells.js +15 -2
- package/dist/renderer/cells.js.map +1 -1
- package/dist/renderer/colors.d.ts +8 -0
- package/dist/renderer/colors.d.ts.map +1 -0
- package/dist/renderer/colors.js +18 -0
- package/dist/renderer/colors.js.map +1 -0
- package/dist/renderer/diff.test.d.ts +5 -0
- package/dist/renderer/diff.test.d.ts.map +1 -0
- package/dist/renderer/diff.test.js +140 -0
- package/dist/renderer/diff.test.js.map +1 -0
- package/dist/renderer/differ.d.ts.map +1 -1
- package/dist/renderer/differ.js +1 -10
- package/dist/renderer/differ.js.map +1 -1
- package/dist/renderer/e2e.test.js +1 -0
- package/dist/renderer/e2e.test.js.map +1 -1
- package/dist/renderer/image.test.d.ts +5 -0
- package/dist/renderer/image.test.d.ts.map +1 -0
- package/dist/renderer/image.test.js +66 -0
- package/dist/renderer/image.test.js.map +1 -0
- package/dist/renderer/index.d.ts +21 -4
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +191 -116
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/layout.d.ts +5 -1
- package/dist/renderer/layout.d.ts.map +1 -1
- package/dist/renderer/layout.js +504 -614
- package/dist/renderer/layout.js.map +1 -1
- package/dist/renderer/markdown.d.ts.map +1 -1
- package/dist/renderer/markdown.js +42 -36
- package/dist/renderer/markdown.js.map +1 -1
- package/dist/renderer/perf.test.js +1 -0
- package/dist/renderer/perf.test.js.map +1 -1
- package/dist/renderer/session-browser.test.d.ts +6 -0
- package/dist/renderer/session-browser.test.d.ts.map +1 -0
- package/dist/renderer/session-browser.test.js +95 -0
- package/dist/renderer/session-browser.test.js.map +1 -0
- package/dist/renderer/ui-ux.test.d.ts +15 -0
- package/dist/renderer/ui-ux.test.d.ts.map +1 -0
- package/dist/renderer/ui-ux.test.js +470 -0
- package/dist/renderer/ui-ux.test.js.map +1 -0
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +178 -14
- package/dist/repl.js.map +1 -1
- package/dist/services/StreamingToolExecutor.d.ts.map +1 -1
- package/dist/services/StreamingToolExecutor.js +4 -2
- package/dist/services/StreamingToolExecutor.js.map +1 -1
- package/dist/services/agent-messaging.d.ts +68 -0
- package/dist/services/agent-messaging.d.ts.map +1 -0
- package/dist/services/agent-messaging.js +121 -0
- package/dist/services/agent-messaging.js.map +1 -0
- package/dist/services/agent-messaging.test.d.ts +2 -0
- package/dist/services/agent-messaging.test.d.ts.map +1 -0
- package/dist/services/agent-messaging.test.js +88 -0
- package/dist/services/agent-messaging.test.js.map +1 -0
- package/dist/services/cron.d.ts +40 -0
- package/dist/services/cron.d.ts.map +1 -0
- package/dist/services/cron.js +90 -0
- package/dist/services/cron.js.map +1 -0
- package/dist/services/cron.test.d.ts +2 -0
- package/dist/services/cron.test.d.ts.map +1 -0
- package/dist/services/cron.test.js +49 -0
- package/dist/services/cron.test.js.map +1 -0
- package/dist/tools/AgentTool/index.d.ts +9 -0
- package/dist/tools/AgentTool/index.d.ts.map +1 -1
- package/dist/tools/AgentTool/index.js +89 -6
- package/dist/tools/AgentTool/index.js.map +1 -1
- package/dist/tools/BashTool/index.d.ts +6 -0
- package/dist/tools/BashTool/index.d.ts.map +1 -1
- package/dist/tools/BashTool/index.js +39 -1
- package/dist/tools/BashTool/index.js.map +1 -1
- package/dist/tools/FileEditTool/index.js +4 -4
- package/dist/tools/FileEditTool/index.js.map +1 -1
- package/dist/tools/FileReadTool/index.d.ts +3 -0
- package/dist/tools/FileReadTool/index.d.ts.map +1 -1
- package/dist/tools/FileReadTool/index.js +102 -4
- package/dist/tools/FileReadTool/index.js.map +1 -1
- package/dist/tools/FileWriteTool/index.d.ts.map +1 -1
- package/dist/tools/FileWriteTool/index.js +20 -5
- package/dist/tools/FileWriteTool/index.js.map +1 -1
- package/dist/tools/GlobTool/index.d.ts.map +1 -1
- package/dist/tools/GlobTool/index.js +4 -61
- package/dist/tools/GlobTool/index.js.map +1 -1
- package/dist/tools/GrepTool/index.d.ts +30 -0
- package/dist/tools/GrepTool/index.d.ts.map +1 -1
- package/dist/tools/GrepTool/index.js +153 -72
- package/dist/tools/GrepTool/index.js.map +1 -1
- package/dist/tools/LSTool/index.d.ts +3 -0
- package/dist/tools/LSTool/index.d.ts.map +1 -1
- package/dist/tools/LSTool/index.js +44 -29
- package/dist/tools/LSTool/index.js.map +1 -1
- package/dist/tools/TaskCreateTool/index.d.ts +6 -0
- package/dist/tools/TaskCreateTool/index.d.ts.map +1 -1
- package/dist/tools/TaskCreateTool/index.js +8 -2
- package/dist/tools/TaskCreateTool/index.js.map +1 -1
- package/dist/tools/TaskGetTool/index.d.ts +12 -0
- package/dist/tools/TaskGetTool/index.d.ts.map +1 -0
- package/dist/tools/TaskGetTool/index.js +50 -0
- package/dist/tools/TaskGetTool/index.js.map +1 -0
- package/dist/tools/TaskOutputTool/index.d.ts +15 -0
- package/dist/tools/TaskOutputTool/index.d.ts.map +1 -0
- package/dist/tools/TaskOutputTool/index.js +45 -0
- package/dist/tools/TaskOutputTool/index.js.map +1 -0
- package/dist/tools/TaskStopTool/index.d.ts +15 -0
- package/dist/tools/TaskStopTool/index.d.ts.map +1 -0
- package/dist/tools/TaskStopTool/index.js +51 -0
- package/dist/tools/TaskStopTool/index.js.map +1 -0
- package/dist/tools/TaskUpdateTool/index.d.ts +21 -3
- package/dist/tools/TaskUpdateTool/index.d.ts.map +1 -1
- package/dist/tools/TaskUpdateTool/index.js +48 -3
- package/dist/tools/TaskUpdateTool/index.js.map +1 -1
- package/dist/tools/tools-basic.test.js +191 -2
- package/dist/tools/tools-basic.test.js.map +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +6 -0
- package/dist/tools.js.map +1 -1
- package/dist/types/permissions.d.ts +2 -2
- package/dist/types/permissions.d.ts.map +1 -1
- package/dist/types/permissions.js +59 -13
- package/dist/types/permissions.js.map +1 -1
- package/dist/types/permissions.test.js +57 -0
- package/dist/types/permissions.test.js.map +1 -1
- package/dist/utils/bash-safety.d.ts +18 -0
- package/dist/utils/bash-safety.d.ts.map +1 -0
- package/dist/utils/bash-safety.js +227 -0
- package/dist/utils/bash-safety.js.map +1 -0
- package/dist/utils/bash-safety.test.d.ts +2 -0
- package/dist/utils/bash-safety.test.d.ts.map +1 -0
- package/dist/utils/bash-safety.test.js +112 -0
- package/dist/utils/bash-safety.test.js.map +1 -0
- package/dist/utils/fs.d.ts +15 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +64 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/fs.test.d.ts +5 -0
- package/dist/utils/fs.test.d.ts.map +1 -0
- package/dist/utils/fs.test.js +82 -0
- package/dist/utils/fs.test.js.map +1 -0
- package/dist/utils/safe-env.d.ts +10 -0
- package/dist/utils/safe-env.d.ts.map +1 -0
- package/dist/utils/safe-env.js +40 -0
- package/dist/utils/safe-env.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent messaging — peer-to-peer communication between sub-agents.
|
|
3
|
+
*
|
|
4
|
+
* Provides a message bus for agents to send/receive messages, share state,
|
|
5
|
+
* and coordinate work. Uses file-based locking for concurrent file access.
|
|
6
|
+
*
|
|
7
|
+
* Architecture (inspired by Claude Code Agent Teams):
|
|
8
|
+
* - Each agent has a unique ID
|
|
9
|
+
* - Messages are stored in a shared inbox (in-memory for same-process agents)
|
|
10
|
+
* - File locking prevents concurrent edits to the same file
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Message bus for agent-to-agent communication.
|
|
14
|
+
*/
|
|
15
|
+
export class AgentMessageBus {
|
|
16
|
+
inboxes = new Map();
|
|
17
|
+
agents = new Map();
|
|
18
|
+
fileLocks = new Map(); // filePath → agentId
|
|
19
|
+
/** Register an agent with the bus */
|
|
20
|
+
registerAgent(id, role) {
|
|
21
|
+
this.agents.set(id, { id, role, status: 'idle' });
|
|
22
|
+
this.inboxes.set(id, []);
|
|
23
|
+
}
|
|
24
|
+
/** Unregister an agent */
|
|
25
|
+
unregisterAgent(id) {
|
|
26
|
+
this.agents.delete(id);
|
|
27
|
+
this.inboxes.delete(id);
|
|
28
|
+
// Release all locks held by this agent
|
|
29
|
+
for (const [file, holder] of this.fileLocks) {
|
|
30
|
+
if (holder === id)
|
|
31
|
+
this.fileLocks.delete(file);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Send a message to a specific agent or broadcast */
|
|
35
|
+
send(message) {
|
|
36
|
+
const msg = { ...message, timestamp: Date.now() };
|
|
37
|
+
if (msg.to === '*') {
|
|
38
|
+
// Broadcast to all agents except sender
|
|
39
|
+
for (const [id, inbox] of this.inboxes) {
|
|
40
|
+
if (id !== msg.from)
|
|
41
|
+
inbox.push(msg);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const inbox = this.inboxes.get(msg.to);
|
|
46
|
+
if (inbox)
|
|
47
|
+
inbox.push(msg);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Receive pending messages for an agent (drains inbox) */
|
|
51
|
+
receive(agentId) {
|
|
52
|
+
const inbox = this.inboxes.get(agentId);
|
|
53
|
+
if (!inbox || inbox.length === 0)
|
|
54
|
+
return [];
|
|
55
|
+
const messages = [...inbox];
|
|
56
|
+
inbox.length = 0;
|
|
57
|
+
return messages;
|
|
58
|
+
}
|
|
59
|
+
/** Peek at inbox without draining */
|
|
60
|
+
peek(agentId) {
|
|
61
|
+
return [...(this.inboxes.get(agentId) ?? [])];
|
|
62
|
+
}
|
|
63
|
+
/** Update agent status */
|
|
64
|
+
updateStatus(agentId, status, currentTask) {
|
|
65
|
+
const agent = this.agents.get(agentId);
|
|
66
|
+
if (agent) {
|
|
67
|
+
agent.status = status;
|
|
68
|
+
agent.currentTask = currentTask;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Get all registered agents */
|
|
72
|
+
getAgents() {
|
|
73
|
+
return [...this.agents.values()];
|
|
74
|
+
}
|
|
75
|
+
/** Get a specific agent's info */
|
|
76
|
+
getAgent(id) {
|
|
77
|
+
return this.agents.get(id);
|
|
78
|
+
}
|
|
79
|
+
// ── File Locking ──
|
|
80
|
+
/** Acquire a lock on a file path. Returns true if acquired, false if already locked. */
|
|
81
|
+
acquireLock(agentId, filePath) {
|
|
82
|
+
const holder = this.fileLocks.get(filePath);
|
|
83
|
+
if (holder && holder !== agentId)
|
|
84
|
+
return false;
|
|
85
|
+
this.fileLocks.set(filePath, agentId);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
/** Release a lock on a file path */
|
|
89
|
+
releaseLock(agentId, filePath) {
|
|
90
|
+
const holder = this.fileLocks.get(filePath);
|
|
91
|
+
if (holder === agentId)
|
|
92
|
+
this.fileLocks.delete(filePath);
|
|
93
|
+
}
|
|
94
|
+
/** Release all locks held by an agent */
|
|
95
|
+
releaseAllLocks(agentId) {
|
|
96
|
+
for (const [file, holder] of this.fileLocks) {
|
|
97
|
+
if (holder === agentId)
|
|
98
|
+
this.fileLocks.delete(file);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Check if a file is locked (and by whom) */
|
|
102
|
+
isLocked(filePath) {
|
|
103
|
+
const holder = this.fileLocks.get(filePath);
|
|
104
|
+
return holder ? { locked: true, holder } : { locked: false };
|
|
105
|
+
}
|
|
106
|
+
/** Get all current locks */
|
|
107
|
+
getLocks() {
|
|
108
|
+
return [...this.fileLocks.entries()].map(([filePath, holder]) => ({ filePath, holder }));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Singleton message bus for the current process */
|
|
112
|
+
let globalBus = null;
|
|
113
|
+
export function getMessageBus() {
|
|
114
|
+
if (!globalBus)
|
|
115
|
+
globalBus = new AgentMessageBus();
|
|
116
|
+
return globalBus;
|
|
117
|
+
}
|
|
118
|
+
export function resetMessageBus() {
|
|
119
|
+
globalBus = null;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=agent-messaging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-messaging.js","sourceRoot":"","sources":["../../src/services/agent-messaging.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-messaging.test.d.ts","sourceRoot":"","sources":["../../src/services/agent-messaging.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, beforeEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { AgentMessageBus } from './agent-messaging.js';
|
|
4
|
+
describe('AgentMessageBus', () => {
|
|
5
|
+
let bus;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
bus = new AgentMessageBus();
|
|
8
|
+
bus.registerAgent('agent-1', 'code-reviewer');
|
|
9
|
+
bus.registerAgent('agent-2', 'test-writer');
|
|
10
|
+
});
|
|
11
|
+
describe('messaging', () => {
|
|
12
|
+
it('sends and receives messages', () => {
|
|
13
|
+
bus.send({ from: 'agent-1', to: 'agent-2', type: 'request', content: 'review file.ts' });
|
|
14
|
+
const msgs = bus.receive('agent-2');
|
|
15
|
+
assert.strictEqual(msgs.length, 1);
|
|
16
|
+
assert.strictEqual(msgs[0].content, 'review file.ts');
|
|
17
|
+
assert.strictEqual(msgs[0].from, 'agent-1');
|
|
18
|
+
});
|
|
19
|
+
it('drains inbox on receive', () => {
|
|
20
|
+
bus.send({ from: 'agent-1', to: 'agent-2', type: 'request', content: 'hello' });
|
|
21
|
+
bus.receive('agent-2');
|
|
22
|
+
const msgs = bus.receive('agent-2');
|
|
23
|
+
assert.strictEqual(msgs.length, 0);
|
|
24
|
+
});
|
|
25
|
+
it('peek does not drain', () => {
|
|
26
|
+
bus.send({ from: 'agent-1', to: 'agent-2', type: 'request', content: 'hello' });
|
|
27
|
+
const peeked = bus.peek('agent-2');
|
|
28
|
+
assert.strictEqual(peeked.length, 1);
|
|
29
|
+
const received = bus.receive('agent-2');
|
|
30
|
+
assert.strictEqual(received.length, 1);
|
|
31
|
+
});
|
|
32
|
+
it('broadcasts to all except sender', () => {
|
|
33
|
+
bus.registerAgent('agent-3', 'debugger');
|
|
34
|
+
bus.send({ from: 'agent-1', to: '*', type: 'status', content: 'done with task A' });
|
|
35
|
+
assert.strictEqual(bus.receive('agent-2').length, 1);
|
|
36
|
+
assert.strictEqual(bus.receive('agent-3').length, 1);
|
|
37
|
+
assert.strictEqual(bus.receive('agent-1').length, 0); // sender doesn't get it
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('agent registry', () => {
|
|
41
|
+
it('lists registered agents', () => {
|
|
42
|
+
const agents = bus.getAgents();
|
|
43
|
+
assert.strictEqual(agents.length, 2);
|
|
44
|
+
assert.ok(agents.find(a => a.id === 'agent-1'));
|
|
45
|
+
});
|
|
46
|
+
it('updates agent status', () => {
|
|
47
|
+
bus.updateStatus('agent-1', 'working', 'task-42');
|
|
48
|
+
const agent = bus.getAgent('agent-1');
|
|
49
|
+
assert.strictEqual(agent?.status, 'working');
|
|
50
|
+
assert.strictEqual(agent?.currentTask, 'task-42');
|
|
51
|
+
});
|
|
52
|
+
it('unregisters agent and cleans up', () => {
|
|
53
|
+
bus.send({ from: 'agent-1', to: 'agent-2', type: 'request', content: 'hello' });
|
|
54
|
+
bus.unregisterAgent('agent-2');
|
|
55
|
+
assert.strictEqual(bus.getAgent('agent-2'), undefined);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('file locking', () => {
|
|
59
|
+
it('acquires and releases locks', () => {
|
|
60
|
+
assert.strictEqual(bus.acquireLock('agent-1', 'src/file.ts'), true);
|
|
61
|
+
assert.strictEqual(bus.isLocked('src/file.ts').locked, true);
|
|
62
|
+
assert.strictEqual(bus.isLocked('src/file.ts').holder, 'agent-1');
|
|
63
|
+
bus.releaseLock('agent-1', 'src/file.ts');
|
|
64
|
+
assert.strictEqual(bus.isLocked('src/file.ts').locked, false);
|
|
65
|
+
});
|
|
66
|
+
it('prevents double-locking by different agent', () => {
|
|
67
|
+
bus.acquireLock('agent-1', 'src/file.ts');
|
|
68
|
+
assert.strictEqual(bus.acquireLock('agent-2', 'src/file.ts'), false);
|
|
69
|
+
});
|
|
70
|
+
it('allows same agent to re-lock', () => {
|
|
71
|
+
bus.acquireLock('agent-1', 'src/file.ts');
|
|
72
|
+
assert.strictEqual(bus.acquireLock('agent-1', 'src/file.ts'), true);
|
|
73
|
+
});
|
|
74
|
+
it('releaseAllLocks clears agent locks', () => {
|
|
75
|
+
bus.acquireLock('agent-1', 'a.ts');
|
|
76
|
+
bus.acquireLock('agent-1', 'b.ts');
|
|
77
|
+
bus.releaseAllLocks('agent-1');
|
|
78
|
+
assert.strictEqual(bus.isLocked('a.ts').locked, false);
|
|
79
|
+
assert.strictEqual(bus.isLocked('b.ts').locked, false);
|
|
80
|
+
});
|
|
81
|
+
it('unregister releases locks', () => {
|
|
82
|
+
bus.acquireLock('agent-1', 'src/file.ts');
|
|
83
|
+
bus.unregisterAgent('agent-1');
|
|
84
|
+
assert.strictEqual(bus.isLocked('src/file.ts').locked, false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=agent-messaging.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-messaging.test.js","sourceRoot":"","sources":["../../src/services/agent-messaging.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron scheduler — run prompts on recurring schedules.
|
|
3
|
+
*
|
|
4
|
+
* Cron definitions are stored in ~/.oh/crons/ as JSON files.
|
|
5
|
+
* Each cron has an id, schedule (cron expression), prompt, and metadata.
|
|
6
|
+
*
|
|
7
|
+
* This is a simple implementation using setInterval for minute-level granularity.
|
|
8
|
+
* For production use, consider node-cron or similar.
|
|
9
|
+
*/
|
|
10
|
+
export type CronDefinition = {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
schedule: string;
|
|
14
|
+
prompt: string;
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
createdAt: number;
|
|
17
|
+
lastRun?: number;
|
|
18
|
+
runCount: number;
|
|
19
|
+
};
|
|
20
|
+
export type CronResult = {
|
|
21
|
+
cronId: string;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
output: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
};
|
|
26
|
+
/** List all cron definitions */
|
|
27
|
+
export declare function listCrons(): CronDefinition[];
|
|
28
|
+
/** Create a new cron definition */
|
|
29
|
+
export declare function createCron(name: string, schedule: string, prompt: string): CronDefinition;
|
|
30
|
+
/** Delete a cron definition */
|
|
31
|
+
export declare function deleteCron(id: string): boolean;
|
|
32
|
+
/** Update a cron definition (e.g., after a run) */
|
|
33
|
+
export declare function updateCron(cron: CronDefinition): void;
|
|
34
|
+
/** Parse a simplified schedule string into milliseconds interval */
|
|
35
|
+
export declare function parseScheduleMs(schedule: string): number | null;
|
|
36
|
+
/**
|
|
37
|
+
* Check which crons are due to run based on their schedule and lastRun.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getDueCrons(crons: CronDefinition[]): CronDefinition[];
|
|
40
|
+
//# sourceMappingURL=cron.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../../src/services/cron.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,gCAAgC;AAChC,wBAAgB,SAAS,IAAI,cAAc,EAAE,CAU5C;AAED,mCAAmC;AACnC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CAgBzF;AAED,+BAA+B;AAC/B,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAK9C;AAED,mDAAmD;AACnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAGrD;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc/D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CASrE"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron scheduler — run prompts on recurring schedules.
|
|
3
|
+
*
|
|
4
|
+
* Cron definitions are stored in ~/.oh/crons/ as JSON files.
|
|
5
|
+
* Each cron has an id, schedule (cron expression), prompt, and metadata.
|
|
6
|
+
*
|
|
7
|
+
* This is a simple implementation using setInterval for minute-level granularity.
|
|
8
|
+
* For production use, consider node-cron or similar.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync, existsSync, unlinkSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
const CRON_DIR = join(homedir(), '.oh', 'crons');
|
|
14
|
+
/** List all cron definitions */
|
|
15
|
+
export function listCrons() {
|
|
16
|
+
if (!existsSync(CRON_DIR))
|
|
17
|
+
return [];
|
|
18
|
+
return readdirSync(CRON_DIR)
|
|
19
|
+
.filter(f => f.endsWith('.json'))
|
|
20
|
+
.map(f => {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(readFileSync(join(CRON_DIR, f), 'utf-8'));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.filter((c) => c !== null);
|
|
29
|
+
}
|
|
30
|
+
/** Create a new cron definition */
|
|
31
|
+
export function createCron(name, schedule, prompt) {
|
|
32
|
+
mkdirSync(CRON_DIR, { recursive: true });
|
|
33
|
+
const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
|
34
|
+
const cron = {
|
|
35
|
+
id,
|
|
36
|
+
name,
|
|
37
|
+
schedule,
|
|
38
|
+
prompt,
|
|
39
|
+
enabled: true,
|
|
40
|
+
createdAt: Date.now(),
|
|
41
|
+
runCount: 0,
|
|
42
|
+
};
|
|
43
|
+
writeFileSync(join(CRON_DIR, `${id}.json`), JSON.stringify(cron, null, 2));
|
|
44
|
+
return cron;
|
|
45
|
+
}
|
|
46
|
+
/** Delete a cron definition */
|
|
47
|
+
export function deleteCron(id) {
|
|
48
|
+
const path = join(CRON_DIR, `${id}.json`);
|
|
49
|
+
if (!existsSync(path))
|
|
50
|
+
return false;
|
|
51
|
+
unlinkSync(path);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/** Update a cron definition (e.g., after a run) */
|
|
55
|
+
export function updateCron(cron) {
|
|
56
|
+
mkdirSync(CRON_DIR, { recursive: true });
|
|
57
|
+
writeFileSync(join(CRON_DIR, `${cron.id}.json`), JSON.stringify(cron, null, 2));
|
|
58
|
+
}
|
|
59
|
+
/** Parse a simplified schedule string into milliseconds interval */
|
|
60
|
+
export function parseScheduleMs(schedule) {
|
|
61
|
+
// "every 5m" → 5 minutes
|
|
62
|
+
const minMatch = schedule.match(/^every\s+(\d+)\s*m(?:in(?:ute)?s?)?$/i);
|
|
63
|
+
if (minMatch)
|
|
64
|
+
return parseInt(minMatch[1]) * 60 * 1000;
|
|
65
|
+
// "every 2h" → 2 hours
|
|
66
|
+
const hourMatch = schedule.match(/^every\s+(\d+)\s*h(?:ours?)?$/i);
|
|
67
|
+
if (hourMatch)
|
|
68
|
+
return parseInt(hourMatch[1]) * 60 * 60 * 1000;
|
|
69
|
+
// "every 1d" → 1 day
|
|
70
|
+
const dayMatch = schedule.match(/^every\s+(\d+)\s*d(?:ays?)?$/i);
|
|
71
|
+
if (dayMatch)
|
|
72
|
+
return parseInt(dayMatch[1]) * 24 * 60 * 60 * 1000;
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check which crons are due to run based on their schedule and lastRun.
|
|
77
|
+
*/
|
|
78
|
+
export function getDueCrons(crons) {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
return crons.filter(c => {
|
|
81
|
+
if (!c.enabled)
|
|
82
|
+
return false;
|
|
83
|
+
const intervalMs = parseScheduleMs(c.schedule);
|
|
84
|
+
if (!intervalMs)
|
|
85
|
+
return false;
|
|
86
|
+
const lastRun = c.lastRun ?? c.createdAt;
|
|
87
|
+
return (now - lastRun) >= intervalMs;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=cron.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../../src/services/cron.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.test.d.ts","sourceRoot":"","sources":["../../src/services/cron.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { parseScheduleMs, getDueCrons } from './cron.js';
|
|
4
|
+
describe('parseScheduleMs', () => {
|
|
5
|
+
it('parses minutes', () => {
|
|
6
|
+
assert.strictEqual(parseScheduleMs('every 5m'), 5 * 60 * 1000);
|
|
7
|
+
assert.strictEqual(parseScheduleMs('every 10 minutes'), 10 * 60 * 1000);
|
|
8
|
+
assert.strictEqual(parseScheduleMs('every 1 min'), 1 * 60 * 1000);
|
|
9
|
+
});
|
|
10
|
+
it('parses hours', () => {
|
|
11
|
+
assert.strictEqual(parseScheduleMs('every 2h'), 2 * 60 * 60 * 1000);
|
|
12
|
+
assert.strictEqual(parseScheduleMs('every 1 hour'), 1 * 60 * 60 * 1000);
|
|
13
|
+
});
|
|
14
|
+
it('parses days', () => {
|
|
15
|
+
assert.strictEqual(parseScheduleMs('every 1d'), 24 * 60 * 60 * 1000);
|
|
16
|
+
assert.strictEqual(parseScheduleMs('every 7 days'), 7 * 24 * 60 * 60 * 1000);
|
|
17
|
+
});
|
|
18
|
+
it('returns null for invalid', () => {
|
|
19
|
+
assert.strictEqual(parseScheduleMs('invalid'), null);
|
|
20
|
+
assert.strictEqual(parseScheduleMs('* * * * *'), null);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('getDueCrons', () => {
|
|
24
|
+
it('returns crons past their interval', () => {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const crons = [
|
|
27
|
+
{ id: '1', name: 'test', schedule: 'every 5m', prompt: 'hi', enabled: true, createdAt: now - 10 * 60 * 1000, lastRun: now - 10 * 60 * 1000, runCount: 0 },
|
|
28
|
+
];
|
|
29
|
+
const due = getDueCrons(crons);
|
|
30
|
+
assert.strictEqual(due.length, 1);
|
|
31
|
+
});
|
|
32
|
+
it('skips crons not yet due', () => {
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
const crons = [
|
|
35
|
+
{ id: '1', name: 'test', schedule: 'every 5m', prompt: 'hi', enabled: true, createdAt: now, lastRun: now, runCount: 0 },
|
|
36
|
+
];
|
|
37
|
+
const due = getDueCrons(crons);
|
|
38
|
+
assert.strictEqual(due.length, 0);
|
|
39
|
+
});
|
|
40
|
+
it('skips disabled crons', () => {
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
const crons = [
|
|
43
|
+
{ id: '1', name: 'test', schedule: 'every 5m', prompt: 'hi', enabled: false, createdAt: now - 10 * 60 * 1000, runCount: 0 },
|
|
44
|
+
];
|
|
45
|
+
const due = getDueCrons(crons);
|
|
46
|
+
assert.strictEqual(due.length, 0);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=cron.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.test.js","sourceRoot":"","sources":["../../src/services/cron.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,14 +4,23 @@ declare const inputSchema: z.ZodObject<{
|
|
|
4
4
|
prompt: z.ZodString;
|
|
5
5
|
description: z.ZodOptional<z.ZodString>;
|
|
6
6
|
isolated: z.ZodOptional<z.ZodBoolean>;
|
|
7
|
+
run_in_background: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
+
model: z.ZodOptional<z.ZodString>;
|
|
9
|
+
subagent_type: z.ZodOptional<z.ZodString>;
|
|
7
10
|
}, "strip", z.ZodTypeAny, {
|
|
8
11
|
prompt: string;
|
|
12
|
+
model?: string | undefined;
|
|
9
13
|
description?: string | undefined;
|
|
14
|
+
run_in_background?: boolean | undefined;
|
|
10
15
|
isolated?: boolean | undefined;
|
|
16
|
+
subagent_type?: string | undefined;
|
|
11
17
|
}, {
|
|
12
18
|
prompt: string;
|
|
19
|
+
model?: string | undefined;
|
|
13
20
|
description?: string | undefined;
|
|
21
|
+
run_in_background?: boolean | undefined;
|
|
14
22
|
isolated?: boolean | undefined;
|
|
23
|
+
subagent_type?: string | undefined;
|
|
15
24
|
}>;
|
|
16
25
|
export declare const AgentTool: Tool<typeof inputSchema>;
|
|
17
26
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/AgentTool/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,IAAI,EAA2B,MAAM,eAAe,CAAC;AAGnE,QAAA,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/AgentTool/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,IAAI,EAA2B,MAAM,eAAe,CAAC;AAGnE,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;EAOf,CAAC;AAEH,eAAO,MAAM,SAAS,EAAE,IAAI,CAAC,OAAO,WAAW,CA6K9C,CAAC"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { createWorktree, removeWorktree, isGitRepo } from "../../git/index.js";
|
|
2
|
+
import { createWorktree, removeWorktree, hasWorktreeChanges, isGitRepo } from "../../git/index.js";
|
|
3
3
|
const inputSchema = z.object({
|
|
4
4
|
prompt: z.string(),
|
|
5
5
|
description: z.string().optional(),
|
|
6
6
|
isolated: z.boolean().optional(),
|
|
7
|
+
run_in_background: z.boolean().optional(),
|
|
8
|
+
model: z.string().optional(),
|
|
9
|
+
subagent_type: z.string().optional(),
|
|
7
10
|
});
|
|
8
11
|
export const AgentTool = {
|
|
9
12
|
name: "Agent",
|
|
@@ -34,16 +37,87 @@ export const AgentTool = {
|
|
|
34
37
|
agentWorkingDir = worktreePath;
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
|
-
|
|
40
|
+
// Subagent type modifies the system prompt — check built-in types first, then agent roles
|
|
41
|
+
let systemPrompt = context.systemPrompt ?? "You are a sub-agent. Complete the delegated task concisely.";
|
|
42
|
+
if (input.subagent_type) {
|
|
43
|
+
const builtinHints = {
|
|
44
|
+
explore: "You are an exploration agent. Search the codebase to answer questions. Use only read-only tools (Read, Grep, Glob, LS). Do not modify any files.",
|
|
45
|
+
plan: "You are a planning agent. Analyze the codebase and design implementation plans. Use only read-only tools. Return a detailed step-by-step plan.",
|
|
46
|
+
};
|
|
47
|
+
const hint = builtinHints[input.subagent_type.toLowerCase()];
|
|
48
|
+
if (hint) {
|
|
49
|
+
systemPrompt = hint + "\n\n" + systemPrompt;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Check agent roles (code-reviewer, test-writer, debugger, etc.)
|
|
53
|
+
const { getRole } = await import("../../agents/roles.js");
|
|
54
|
+
const role = getRole(input.subagent_type.toLowerCase());
|
|
55
|
+
if (role) {
|
|
56
|
+
systemPrompt = role.systemPromptSupplement + "\n\n" + systemPrompt;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Model override for sub-agent
|
|
61
|
+
const agentModel = input.model ?? context.model;
|
|
38
62
|
const config = {
|
|
39
63
|
provider: context.provider,
|
|
40
64
|
tools: context.tools,
|
|
41
65
|
systemPrompt,
|
|
42
66
|
permissionMode: context.permissionMode ?? "trust",
|
|
43
|
-
model:
|
|
67
|
+
model: agentModel,
|
|
44
68
|
maxTurns: 20,
|
|
45
69
|
abortSignal: context.abortSignal,
|
|
46
70
|
};
|
|
71
|
+
// Background execution: start agent and return immediately
|
|
72
|
+
if (input.run_in_background) {
|
|
73
|
+
const bgId = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
|
74
|
+
const runAgent = async () => {
|
|
75
|
+
let finalText = "";
|
|
76
|
+
const originalCwd = process.cwd();
|
|
77
|
+
try {
|
|
78
|
+
if (worktreePath) {
|
|
79
|
+
try {
|
|
80
|
+
process.chdir(agentWorkingDir);
|
|
81
|
+
}
|
|
82
|
+
catch { /* ignore */ }
|
|
83
|
+
}
|
|
84
|
+
for await (const event of query(input.prompt, config)) {
|
|
85
|
+
if (event.type === "text_delta")
|
|
86
|
+
finalText += event.content;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
if (worktreePath) {
|
|
91
|
+
try {
|
|
92
|
+
process.chdir(originalCwd);
|
|
93
|
+
}
|
|
94
|
+
catch { /* ignore */ }
|
|
95
|
+
}
|
|
96
|
+
// Clean up worktree only if no changes were made
|
|
97
|
+
if (worktreePath) {
|
|
98
|
+
const hasChanges = hasWorktreeChanges(worktreePath);
|
|
99
|
+
if (!hasChanges) {
|
|
100
|
+
removeWorktree(worktreePath, context.workingDir);
|
|
101
|
+
}
|
|
102
|
+
else if (context.onOutputChunk && context.callId) {
|
|
103
|
+
context.onOutputChunk(context.callId, `\n[background:${bgId} worktree preserved at ${worktreePath} — agent made changes]`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (context.onOutputChunk && context.callId) {
|
|
108
|
+
context.onOutputChunk(context.callId, `\n[background:${bgId} completed]\n${finalText}`);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
runAgent().catch((err) => {
|
|
112
|
+
if (context.onOutputChunk && context.callId) {
|
|
113
|
+
context.onOutputChunk(context.callId, `\n[background:${bgId} failed: ${err instanceof Error ? err.message : String(err)}]`);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
output: `Background agent started (id: ${bgId}). You will be notified when it completes.`,
|
|
118
|
+
isError: false,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
47
121
|
const outputChunks = [];
|
|
48
122
|
let finalText = "";
|
|
49
123
|
try {
|
|
@@ -93,9 +167,15 @@ export const AgentTool = {
|
|
|
93
167
|
};
|
|
94
168
|
}
|
|
95
169
|
finally {
|
|
96
|
-
// Clean up worktree
|
|
170
|
+
// Clean up worktree only if no changes were made
|
|
97
171
|
if (worktreePath) {
|
|
98
|
-
|
|
172
|
+
const hasChanges = hasWorktreeChanges(worktreePath);
|
|
173
|
+
if (!hasChanges) {
|
|
174
|
+
removeWorktree(worktreePath, context.workingDir);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
finalText += `\n\n[Worktree preserved at ${worktreePath} — agent made changes that can be reviewed/merged]`;
|
|
178
|
+
}
|
|
99
179
|
}
|
|
100
180
|
}
|
|
101
181
|
return { output: finalText || "(sub-agent completed with no text output)", isError: false };
|
|
@@ -104,7 +184,10 @@ export const AgentTool = {
|
|
|
104
184
|
return `Spawn a sub-agent with its own tool-use loop to handle a delegated task autonomously. The sub-agent runs in an isolated git worktree to prevent file conflicts. Parameters:
|
|
105
185
|
- prompt (string, required): The full instructions for the sub-agent.
|
|
106
186
|
- description (string, optional): A short label for what the sub-agent is doing.
|
|
107
|
-
- isolated (boolean, optional): Whether to use git worktree isolation (default: true if in a git repo)
|
|
187
|
+
- isolated (boolean, optional): Whether to use git worktree isolation (default: true if in a git repo).
|
|
188
|
+
- run_in_background (boolean, optional): Run the agent in the background. Returns immediately; you will be notified when it completes.
|
|
189
|
+
- model (string, optional): Override the model for this sub-agent (e.g., use a faster model for exploration).
|
|
190
|
+
- subagent_type (string, optional): Specialize the agent behavior. Types: "Explore" (read-only codebase search), "Plan" (design implementation plans), "code-reviewer" (review code for issues).`;
|
|
108
191
|
},
|
|
109
192
|
};
|
|
110
193
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/AgentTool/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/AgentTool/index.ts"],"names":[],"mappings":"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"}
|