nightshift-mcp 2.0.1 → 2.1.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/agent-spawner.d.ts +9 -0
- package/dist/agent-spawner.d.ts.map +1 -1
- package/dist/agent-spawner.js +88 -5
- package/dist/agent-spawner.js.map +1 -1
- package/dist/daemon.d.ts +19 -5
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +212 -35
- package/dist/daemon.js.map +1 -1
- package/dist/database.d.ts +2 -2
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +4 -4
- package/dist/database.js.map +1 -1
- package/dist/index.js +219 -1146
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.d.ts +10 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +190 -8
- package/dist/orchestrator.js.map +1 -1
- package/dist/pipeline.d.ts +61 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +227 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/project-context.d.ts +28 -0
- package/dist/project-context.d.ts.map +1 -0
- package/dist/project-context.js +45 -0
- package/dist/project-context.js.map +1 -0
- package/dist/run-logger.d.ts +61 -0
- package/dist/run-logger.d.ts.map +1 -0
- package/dist/run-logger.js +219 -0
- package/dist/run-logger.js.map +1 -0
- package/dist/tools/agents.d.ts +3 -0
- package/dist/tools/agents.d.ts.map +1 -1
- package/dist/tools/agents.js +148 -5
- package/dist/tools/agents.js.map +1 -1
- package/dist/tools/bugs.d.ts.map +1 -1
- package/dist/tools/bugs.js +48 -22
- package/dist/tools/bugs.js.map +1 -1
- package/dist/tools/chat.d.ts.map +1 -1
- package/dist/tools/chat.js +47 -20
- package/dist/tools/chat.js.map +1 -1
- package/dist/tools/prd.d.ts.map +1 -1
- package/dist/tools/prd.js +67 -31
- package/dist/tools/prd.js.map +1 -1
- package/dist/tools/progress.d.ts.map +1 -1
- package/dist/tools/progress.js +22 -7
- package/dist/tools/progress.js.map +1 -1
- package/dist/tools/utility.js +2 -2
- package/dist/tools/utility.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
// ============================================
|
|
4
|
+
// Token Parsing
|
|
5
|
+
// ============================================
|
|
6
|
+
/**
|
|
7
|
+
* Parse token usage from agent output. Each agent reports differently.
|
|
8
|
+
*/
|
|
9
|
+
export function parseTokensFromOutput(agent, output) {
|
|
10
|
+
if (!output)
|
|
11
|
+
return undefined;
|
|
12
|
+
switch (agent) {
|
|
13
|
+
case "codex": {
|
|
14
|
+
// Codex reports tokens at the end of output, e.g. "108,712" or "tokens used: 14079"
|
|
15
|
+
// Also: "tokens used**" pattern seen in logs
|
|
16
|
+
const tokensUsedMatch = output.match(/tokens?\s*used[*:]*\s*([\d,]+)/i);
|
|
17
|
+
if (tokensUsedMatch) {
|
|
18
|
+
const total = parseInt(tokensUsedMatch[1].replace(/,/g, ""), 10);
|
|
19
|
+
if (!isNaN(total))
|
|
20
|
+
return { total, model: "gpt-5.4" };
|
|
21
|
+
}
|
|
22
|
+
// Bare large number at end of output (Codex sometimes just prints the count)
|
|
23
|
+
const bareNumberMatch = output.match(/\n([\d,]{4,})\s*$/);
|
|
24
|
+
if (bareNumberMatch) {
|
|
25
|
+
const total = parseInt(bareNumberMatch[1].replace(/,/g, ""), 10);
|
|
26
|
+
if (!isNaN(total) && total > 100)
|
|
27
|
+
return { total, model: "gpt-5.4" };
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
case "ollama": {
|
|
32
|
+
// Ollama API returns: prompt_eval_count, eval_count in JSON responses
|
|
33
|
+
const promptEvalMatch = output.match(/"prompt_eval_count"\s*:\s*(\d+)/);
|
|
34
|
+
const evalMatch = output.match(/"eval_count"\s*:\s*(\d+)/);
|
|
35
|
+
if (promptEvalMatch || evalMatch) {
|
|
36
|
+
const input = promptEvalMatch ? parseInt(promptEvalMatch[1], 10) : undefined;
|
|
37
|
+
const outputTokens = evalMatch ? parseInt(evalMatch[1], 10) : undefined;
|
|
38
|
+
const total = (input || 0) + (outputTokens || 0);
|
|
39
|
+
const model = process.env.NIGHTSHIFT_OLLAMA_MODEL || "qwen3:8b";
|
|
40
|
+
return { input, output: outputTokens, total: total || undefined, model };
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
case "gemini": {
|
|
45
|
+
// Gemini CLI may report: "Token count: N" or usage stats
|
|
46
|
+
const tokenCountMatch = output.match(/token[s]?\s*(?:count|usage|used)[:\s]*([\d,]+)/i);
|
|
47
|
+
if (tokenCountMatch) {
|
|
48
|
+
const total = parseInt(tokenCountMatch[1].replace(/,/g, ""), 10);
|
|
49
|
+
if (!isNaN(total))
|
|
50
|
+
return { total };
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
case "claude": {
|
|
55
|
+
// Claude Code --print mode may report token usage
|
|
56
|
+
const inputMatch = output.match(/input[_ ]tokens?[:\s]*([\d,]+)/i);
|
|
57
|
+
const outputMatch = output.match(/output[_ ]tokens?[:\s]*([\d,]+)/i);
|
|
58
|
+
if (inputMatch || outputMatch) {
|
|
59
|
+
const input = inputMatch ? parseInt(inputMatch[1].replace(/,/g, ""), 10) : undefined;
|
|
60
|
+
const outputTokens = outputMatch ? parseInt(outputMatch[1].replace(/,/g, ""), 10) : undefined;
|
|
61
|
+
const total = (input || 0) + (outputTokens || 0);
|
|
62
|
+
return { input, output: outputTokens, total: total || undefined };
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
default:
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// ============================================
|
|
71
|
+
// Rate Limit Detection
|
|
72
|
+
// ============================================
|
|
73
|
+
/**
|
|
74
|
+
* Detect rate limiting from agent output.
|
|
75
|
+
*/
|
|
76
|
+
export function detectRateLimit(output) {
|
|
77
|
+
if (!output)
|
|
78
|
+
return undefined;
|
|
79
|
+
// HTTP 429
|
|
80
|
+
const has429 = /\b429\b/.test(output) && /rate|limit|quota|capacity|exhausted/i.test(output);
|
|
81
|
+
// Explicit rate limit messages
|
|
82
|
+
const hasRateLimit = /rate.?limit|quota.?exceeded|too many requests|capacity.*exhausted|exhausted.*capacity|throttl/i.test(output);
|
|
83
|
+
// Retry-after header or message
|
|
84
|
+
const retryMatch = output.match(/retry.?after[:\s]*(\d+)/i)
|
|
85
|
+
|| output.match(/reset after (\d+)s/i)
|
|
86
|
+
|| output.match(/wait (\d+)\s*s/i);
|
|
87
|
+
if (!has429 && !hasRateLimit)
|
|
88
|
+
return undefined;
|
|
89
|
+
const retryAfter = retryMatch ? parseInt(retryMatch[1], 10) : undefined;
|
|
90
|
+
// Try to identify provider
|
|
91
|
+
let provider;
|
|
92
|
+
if (/openai|gpt/i.test(output))
|
|
93
|
+
provider = "openai";
|
|
94
|
+
else if (/anthropic|claude/i.test(output))
|
|
95
|
+
provider = "anthropic";
|
|
96
|
+
else if (/google|gemini/i.test(output))
|
|
97
|
+
provider = "google";
|
|
98
|
+
else if (/ollama/i.test(output))
|
|
99
|
+
provider = "ollama";
|
|
100
|
+
return { detected: true, retryAfter, provider };
|
|
101
|
+
}
|
|
102
|
+
// ============================================
|
|
103
|
+
// Tool Usage Parsing
|
|
104
|
+
// ============================================
|
|
105
|
+
/**
|
|
106
|
+
* Parse tool/command usage from agent output.
|
|
107
|
+
* Detects MCP tool calls, shell commands, file operations, etc.
|
|
108
|
+
*/
|
|
109
|
+
export function parseToolsUsed(output) {
|
|
110
|
+
if (!output)
|
|
111
|
+
return undefined;
|
|
112
|
+
const tools = new Set();
|
|
113
|
+
// MCP tool calls (e.g., "nightshift - read_prd", "Tool: read_file")
|
|
114
|
+
const mcpToolMatches = output.matchAll(/(?:Tool|tool_use|nightshift)\s*[-:]?\s*(\w+)/g);
|
|
115
|
+
for (const match of mcpToolMatches) {
|
|
116
|
+
const toolName = match[1];
|
|
117
|
+
// Filter out noise words
|
|
118
|
+
if (toolName && toolName.length > 2 && !/^(the|and|for|was|use|has)$/i.test(toolName)) {
|
|
119
|
+
tools.add(toolName);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Claude Code tool patterns: "Read(file)", "Edit(file)", "Bash(cmd)", "Write(file)"
|
|
123
|
+
const claudeToolMatches = output.matchAll(/\b(Read|Edit|Write|Bash|Glob|Grep|Agent|WebSearch|WebFetch)\s*\(/g);
|
|
124
|
+
for (const match of claudeToolMatches) {
|
|
125
|
+
tools.add(match[1]);
|
|
126
|
+
}
|
|
127
|
+
// Codex tool patterns: "exec", "read_file", "write_file", "shell"
|
|
128
|
+
const codexToolMatches = output.matchAll(/\b(exec|read_file|write_file|shell|list_dir|patch_file)\b/g);
|
|
129
|
+
for (const match of codexToolMatches) {
|
|
130
|
+
tools.add(match[1]);
|
|
131
|
+
}
|
|
132
|
+
// Gemini tool patterns: "ReadFile", "WriteFile", "ExecuteCommand"
|
|
133
|
+
const geminiToolMatches = output.matchAll(/\b(ReadFile|WriteFile|ExecuteCommand|SearchFiles|ListDirectory)\b/g);
|
|
134
|
+
for (const match of geminiToolMatches) {
|
|
135
|
+
tools.add(match[1]);
|
|
136
|
+
}
|
|
137
|
+
// Shell commands (common patterns)
|
|
138
|
+
const shellMatches = output.matchAll(/(?:^|\n)\s*\$?\s*(npm|npx|git|tsc|vitest|eslint|prettier|node)\b/gm);
|
|
139
|
+
for (const match of shellMatches) {
|
|
140
|
+
tools.add(`shell:${match[1]}`);
|
|
141
|
+
}
|
|
142
|
+
return tools.size > 0 ? Array.from(tools) : undefined;
|
|
143
|
+
}
|
|
144
|
+
// ============================================
|
|
145
|
+
// Run Logger
|
|
146
|
+
// ============================================
|
|
147
|
+
/**
|
|
148
|
+
* Append a run log entry to the project's runs.jsonl file.
|
|
149
|
+
*/
|
|
150
|
+
export function appendRunLog(projectPath, entry) {
|
|
151
|
+
const robotChatDir = path.join(projectPath, ".robot-chat");
|
|
152
|
+
if (!fs.existsSync(robotChatDir)) {
|
|
153
|
+
fs.mkdirSync(robotChatDir, { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
const logFile = path.join(robotChatDir, "runs.jsonl");
|
|
156
|
+
const line = JSON.stringify(entry) + "\n";
|
|
157
|
+
try {
|
|
158
|
+
fs.appendFileSync(logFile, line, "utf-8");
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Non-critical — best effort logging
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Read all run log entries from the project's runs.jsonl file.
|
|
166
|
+
*/
|
|
167
|
+
export function readRunLog(projectPath, limit) {
|
|
168
|
+
const logFile = path.join(projectPath, ".robot-chat", "runs.jsonl");
|
|
169
|
+
if (!fs.existsSync(logFile))
|
|
170
|
+
return [];
|
|
171
|
+
try {
|
|
172
|
+
const content = fs.readFileSync(logFile, "utf-8");
|
|
173
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
174
|
+
const entries = lines.map((line) => JSON.parse(line));
|
|
175
|
+
return limit ? entries.slice(-limit) : entries;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get aggregate stats from the run log.
|
|
183
|
+
*/
|
|
184
|
+
export function getRunStats(projectPath) {
|
|
185
|
+
const entries = readRunLog(projectPath);
|
|
186
|
+
const byAgent = {};
|
|
187
|
+
let totalTokens = 0;
|
|
188
|
+
let totalDuration = 0;
|
|
189
|
+
let rateLimitCount = 0;
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
if (!byAgent[entry.agent]) {
|
|
192
|
+
byAgent[entry.agent] = { runs: 0, totalTokens: 0, avgDuration: 0, totalDuration: 0 };
|
|
193
|
+
}
|
|
194
|
+
const agentStats = byAgent[entry.agent];
|
|
195
|
+
agentStats.runs++;
|
|
196
|
+
agentStats.totalDuration += entry.duration_seconds;
|
|
197
|
+
agentStats.avgDuration = Math.round(agentStats.totalDuration / agentStats.runs);
|
|
198
|
+
if (entry.tokens?.total) {
|
|
199
|
+
agentStats.totalTokens += entry.tokens.total;
|
|
200
|
+
totalTokens += entry.tokens.total;
|
|
201
|
+
}
|
|
202
|
+
totalDuration += entry.duration_seconds;
|
|
203
|
+
if (entry.status === "rate_limited")
|
|
204
|
+
rateLimitCount++;
|
|
205
|
+
}
|
|
206
|
+
// Clean up totalDuration from output
|
|
207
|
+
const result = {};
|
|
208
|
+
for (const [agent, stats] of Object.entries(byAgent)) {
|
|
209
|
+
result[agent] = { runs: stats.runs, totalTokens: stats.totalTokens, avgDuration: stats.avgDuration };
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
totalRuns: entries.length,
|
|
213
|
+
byAgent: result,
|
|
214
|
+
totalTokens,
|
|
215
|
+
totalDuration,
|
|
216
|
+
rateLimitCount,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=run-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-logger.js","sourceRoot":"","sources":["../src/run-logger.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/agents.d.ts
CHANGED
|
@@ -8,5 +8,8 @@ export declare const getAgentStatusTool: import("../tool-registry.js").ToolDefin
|
|
|
8
8
|
export declare const listRunningAgentsTool: import("../tool-registry.js").ToolDefinition;
|
|
9
9
|
export declare const getDaemonStatusTool: import("../tool-registry.js").ToolDefinition;
|
|
10
10
|
export declare const stopDaemonTool: import("../tool-registry.js").ToolDefinition;
|
|
11
|
+
export declare const getRunStatsTool: import("../tool-registry.js").ToolDefinition;
|
|
12
|
+
export declare const getPipelineTool: import("../tool-registry.js").ToolDefinition;
|
|
13
|
+
export declare const setPipelineTool: import("../tool-registry.js").ToolDefinition;
|
|
11
14
|
export declare const agentTools: import("../tool-registry.js").ToolDefinition[];
|
|
12
15
|
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/tools/agents.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/tools/agents.ts"],"names":[],"mappings":"AA0EA,eAAO,MAAM,mBAAmB,8CAsC9B,CAAC;AAEH,eAAO,MAAM,cAAc,8CAiGzB,CAAC;AAEH,eAAO,MAAM,wBAAwB,8CAuDnC,CAAC;AAEH,eAAO,MAAM,aAAa,8CA6LxB,CAAC;AAEH,eAAO,MAAM,gBAAgB,8CAoK3B,CAAC;AAEH,eAAO,MAAM,WAAW,8CAsJtB,CAAC;AAEH,eAAO,MAAM,kBAAkB,8CAyC7B,CAAC;AAEH,eAAO,MAAM,qBAAqB,8CAyChC,CAAC;AAEH,eAAO,MAAM,mBAAmB,8CAsB9B,CAAC;AAEH,eAAO,MAAM,cAAc,8CA4CzB,CAAC;AAEH,eAAO,MAAM,eAAe,8CAgC1B,CAAC;AAEH,eAAO,MAAM,eAAe,8CA8B1B,CAAC;AAEH,eAAO,MAAM,eAAe,8CA0D1B,CAAC;AAGH,eAAO,MAAM,UAAU,gDActB,CAAC"}
|
package/dist/tools/agents.js
CHANGED
|
@@ -2,6 +2,8 @@ import { z } from "zod";
|
|
|
2
2
|
import { defineTool } from "../tool-registry.js";
|
|
3
3
|
import { spawnAgent, spawnAgentBackground, getAvailableAgents, getAgentStatus, canAgentRun, getTrackedAgentStatus, listTrackedAgents, updateTrackedAgent, } from "../agent-spawner.js";
|
|
4
4
|
import { getNightshiftCliCommand } from "../platform.js";
|
|
5
|
+
import { getRunStats, readRunLog } from "../run-logger.js";
|
|
6
|
+
import { savePipelineOverride, clearPipelineOverride, resolveExecutablePipeline, listPipelines, getTagRouting, } from "../pipeline.js";
|
|
5
7
|
import { ProjectOrchestrator } from "../orchestrator.js";
|
|
6
8
|
import { isDaemonRunning, ensureDaemonRunning, stopDaemon, } from "../daemon-manager.js";
|
|
7
9
|
const AGENT_TYPES = ["claude", "codex", "gemini", "vibe", "goose", "ollama"];
|
|
@@ -106,8 +108,10 @@ Agent selection guide (only available agents will work — check list_available_
|
|
|
106
108
|
agent: z.enum(AGENT_TYPES).describe("Which agent to spawn. IMPORTANT: only use agents confirmed available by list_available_agents or preflight check. Do NOT guess."),
|
|
107
109
|
prompt: z.string().describe("The prompt/task to send to the agent"),
|
|
108
110
|
timeout: z.number().optional().describe("Timeout in seconds (default: 300). Use 600 for research tasks, 900 for complex implementation. See tool description for guidance."),
|
|
111
|
+
role: z.string().optional().describe("Agent role for audit trail (e.g., 'planner', 'coder', 'fixer', 'verifier', 'reviewer')"),
|
|
112
|
+
storyId: z.string().optional().describe("Story ID being worked on (for run log tracking)"),
|
|
109
113
|
}),
|
|
110
|
-
handler: async ({ agent, prompt, timeout }, ctx) => {
|
|
114
|
+
handler: async ({ agent, prompt, timeout, role, storyId }, ctx) => {
|
|
111
115
|
try {
|
|
112
116
|
// Pre-flight: verify agent is available before attempting spawn
|
|
113
117
|
const preflight = await preflightAgentCheck(agent);
|
|
@@ -128,18 +132,23 @@ Agent selection guide (only available agents will work — check list_available_
|
|
|
128
132
|
prompt,
|
|
129
133
|
projectPath: ctx.projectPath,
|
|
130
134
|
timeout: timeout ? timeout * 1000 : undefined,
|
|
135
|
+
role,
|
|
136
|
+
storyId,
|
|
131
137
|
});
|
|
132
138
|
// End trace span
|
|
133
139
|
if (spanId) {
|
|
134
140
|
ctx.traceManager?.endSpan(spanId, result.success ? "completed" : "failed", {
|
|
135
141
|
exitCode: result.exitCode,
|
|
142
|
+
tokens: result.tokens,
|
|
143
|
+
rateLimited: result.rateLimitInfo?.detected,
|
|
136
144
|
});
|
|
137
145
|
}
|
|
138
146
|
// Post result to chat
|
|
147
|
+
const tokenSummary = result.tokens?.total ? ` (${result.tokens.total.toLocaleString()} tokens)` : "";
|
|
139
148
|
ctx.chatManager.writeMessage({
|
|
140
149
|
agent: "NightShift",
|
|
141
150
|
type: result.success ? "INFO" : "ERROR",
|
|
142
|
-
content: `${agent} agent ${result.success ? "completed" : "failed"}: ${result.output.substring(0, 500)}${result.output.length > 500 ? "..." : ""}`,
|
|
151
|
+
content: `${agent}${role ? ` [${role}]` : ""} agent ${result.success ? "completed" : "failed"}${tokenSummary}: ${result.output.substring(0, 500)}${result.output.length > 500 ? "..." : ""}`,
|
|
143
152
|
});
|
|
144
153
|
return {
|
|
145
154
|
content: [{
|
|
@@ -147,8 +156,13 @@ Agent selection guide (only available agents will work — check list_available_
|
|
|
147
156
|
text: JSON.stringify({
|
|
148
157
|
success: result.success,
|
|
149
158
|
agent,
|
|
159
|
+
role,
|
|
150
160
|
output: result.output,
|
|
151
161
|
exitCode: result.exitCode,
|
|
162
|
+
duration_seconds: result.duration_seconds,
|
|
163
|
+
tokens: result.tokens,
|
|
164
|
+
rateLimitInfo: result.rateLimitInfo,
|
|
165
|
+
toolsUsed: result.toolsUsed,
|
|
152
166
|
error: result.error,
|
|
153
167
|
}, null, 2),
|
|
154
168
|
}],
|
|
@@ -171,8 +185,9 @@ export const spawnAgentBackgroundTool = defineTool({
|
|
|
171
185
|
inputSchema: z.object({
|
|
172
186
|
agent: z.enum(AGENT_TYPES).describe("Which agent to spawn. MUST be an available agent — check list_available_agents first."),
|
|
173
187
|
prompt: z.string().describe("The prompt/task to send to the agent"),
|
|
188
|
+
role: z.string().optional().describe("Agent role for audit trail (e.g., 'planner', 'coder', 'fixer', 'verifier')"),
|
|
174
189
|
}),
|
|
175
|
-
handler: async ({ agent, prompt }, ctx) => {
|
|
190
|
+
handler: async ({ agent, prompt, role }, ctx) => {
|
|
176
191
|
try {
|
|
177
192
|
// Pre-flight: verify agent is available before attempting spawn
|
|
178
193
|
const preflight = await preflightAgentCheck(agent);
|
|
@@ -187,18 +202,20 @@ export const spawnAgentBackgroundTool = defineTool({
|
|
|
187
202
|
agent: agent,
|
|
188
203
|
prompt,
|
|
189
204
|
projectPath: ctx.projectPath,
|
|
205
|
+
role,
|
|
190
206
|
});
|
|
191
207
|
// Post to chat
|
|
192
208
|
ctx.chatManager.writeMessage({
|
|
193
209
|
agent: "NightShift",
|
|
194
210
|
type: "INFO",
|
|
195
|
-
content: `Spawned ${agent} in background (PID: ${result.pid})\nOutput: ${result.outputFile}`,
|
|
211
|
+
content: `Spawned ${agent}${role ? ` [${role}]` : ""} in background (PID: ${result.pid})\nOutput: ${result.outputFile}`,
|
|
196
212
|
});
|
|
197
213
|
return {
|
|
198
214
|
content: [{
|
|
199
215
|
type: "text",
|
|
200
216
|
text: JSON.stringify({
|
|
201
217
|
agent,
|
|
218
|
+
role,
|
|
202
219
|
pid: result.pid,
|
|
203
220
|
outputFile: result.outputFile,
|
|
204
221
|
status: "running in background",
|
|
@@ -225,8 +242,9 @@ IMPORTANT: Only delegate to agents that are actually available. Use list_availab
|
|
|
225
242
|
agent: z.enum(AGENT_TYPES).describe("Which agent to delegate to. MUST be an available agent — check list_available_agents first."),
|
|
226
243
|
storyId: z.string().optional().describe("Specific story ID (defaults to next available)"),
|
|
227
244
|
background: z.boolean().optional().describe("Run in background (default: false)"),
|
|
245
|
+
role: z.string().optional().describe("Agent role for audit trail (default: 'implementer')"),
|
|
228
246
|
}),
|
|
229
|
-
handler: async ({ agent, storyId, background }, ctx) => {
|
|
247
|
+
handler: async ({ agent, storyId, background, role }, ctx) => {
|
|
230
248
|
try {
|
|
231
249
|
// Pre-flight: verify agent is available before attempting delegation
|
|
232
250
|
const preflight = await preflightAgentCheck(agent);
|
|
@@ -312,11 +330,14 @@ Begin implementation now.`;
|
|
|
312
330
|
promptLength: delegationPrompt.length,
|
|
313
331
|
background: !!background,
|
|
314
332
|
});
|
|
333
|
+
const effectiveRole = role || "implementer";
|
|
315
334
|
if (background) {
|
|
316
335
|
const result = spawnAgentBackground({
|
|
317
336
|
agent: agent,
|
|
318
337
|
prompt: delegationPrompt,
|
|
319
338
|
projectPath: ctx.projectPath,
|
|
339
|
+
role: effectiveRole,
|
|
340
|
+
storyId: story.id,
|
|
320
341
|
});
|
|
321
342
|
// Tag the tracked agent with the story ID
|
|
322
343
|
if (result.pid) {
|
|
@@ -328,6 +349,7 @@ Begin implementation now.`;
|
|
|
328
349
|
text: JSON.stringify({
|
|
329
350
|
status: "delegated",
|
|
330
351
|
agent,
|
|
352
|
+
role: effectiveRole,
|
|
331
353
|
story: story.id,
|
|
332
354
|
title: story.title,
|
|
333
355
|
background: true,
|
|
@@ -344,6 +366,8 @@ Begin implementation now.`;
|
|
|
344
366
|
prompt: delegationPrompt,
|
|
345
367
|
projectPath: ctx.projectPath,
|
|
346
368
|
timeout: 10 * 60 * 1000, // 10 minutes for story work
|
|
369
|
+
role: effectiveRole,
|
|
370
|
+
storyId: story.id,
|
|
347
371
|
});
|
|
348
372
|
// End trace span
|
|
349
373
|
if (spanId) {
|
|
@@ -708,6 +732,7 @@ export const getAgentStatusTool = defineTool({
|
|
|
708
732
|
pid,
|
|
709
733
|
status: status.running ? "running" : "exited",
|
|
710
734
|
agent: status.agent,
|
|
735
|
+
role: status.role,
|
|
711
736
|
storyId: status.storyId,
|
|
712
737
|
outputFile: status.outputFile,
|
|
713
738
|
startTime: status.startTime ? new Date(status.startTime).toISOString() : undefined,
|
|
@@ -746,6 +771,7 @@ export const listRunningAgentsTool = defineTool({
|
|
|
746
771
|
agents: agents.map((a) => ({
|
|
747
772
|
pid: a.pid,
|
|
748
773
|
agent: a.agent,
|
|
774
|
+
role: a.role,
|
|
749
775
|
storyId: a.storyId,
|
|
750
776
|
status: a.running ? "running" : "exited",
|
|
751
777
|
elapsedSeconds: a.elapsedSeconds,
|
|
@@ -819,6 +845,120 @@ export const stopDaemonTool = defineTool({
|
|
|
819
845
|
};
|
|
820
846
|
},
|
|
821
847
|
});
|
|
848
|
+
export const getRunStatsTool = defineTool({
|
|
849
|
+
name: "get_run_stats",
|
|
850
|
+
category: "agents",
|
|
851
|
+
description: "Get token usage and performance stats from the run log",
|
|
852
|
+
fullDescription: "Get aggregate statistics from the run log including token usage per agent, average durations, rate limit counts, and tools used. Use this to optimize your agent pipeline and track costs.",
|
|
853
|
+
inputSchema: z.object({
|
|
854
|
+
limit: z.number().optional().describe("Number of recent runs to show in detail (default: 10)"),
|
|
855
|
+
}),
|
|
856
|
+
handler: async ({ limit }, ctx) => {
|
|
857
|
+
const stats = getRunStats(ctx.projectPath);
|
|
858
|
+
const recentRuns = readRunLog(ctx.projectPath, limit ?? 10);
|
|
859
|
+
return {
|
|
860
|
+
content: [{
|
|
861
|
+
type: "text",
|
|
862
|
+
text: JSON.stringify({
|
|
863
|
+
summary: stats,
|
|
864
|
+
recentRuns: recentRuns.map((r) => ({
|
|
865
|
+
timestamp: r.timestamp,
|
|
866
|
+
agent: r.agent,
|
|
867
|
+
role: r.role,
|
|
868
|
+
storyId: r.storyId,
|
|
869
|
+
status: r.status,
|
|
870
|
+
duration_seconds: r.duration_seconds,
|
|
871
|
+
tokens: r.tokens,
|
|
872
|
+
toolsUsed: r.toolsUsed,
|
|
873
|
+
rateLimitInfo: r.rateLimitInfo,
|
|
874
|
+
})),
|
|
875
|
+
}, null, 2),
|
|
876
|
+
}],
|
|
877
|
+
};
|
|
878
|
+
},
|
|
879
|
+
});
|
|
880
|
+
export const getPipelineTool = defineTool({
|
|
881
|
+
name: "get_pipeline",
|
|
882
|
+
category: "agents",
|
|
883
|
+
description: "View configured pipelines and tag routing rules",
|
|
884
|
+
fullDescription: `View all configured agent pipelines and tag routing rules. Shows built-in defaults merged with any project config (nightshift.config.json) and runtime overrides.
|
|
885
|
+
|
|
886
|
+
Pipelines define the sequence of agents for different task types. Stories are automatically routed to pipelines based on their tags.`,
|
|
887
|
+
inputSchema: z.object({
|
|
888
|
+
storyTags: z.array(z.string()).optional().describe("Preview which pipeline would be selected for these tags"),
|
|
889
|
+
}),
|
|
890
|
+
handler: async ({ storyTags }, ctx) => {
|
|
891
|
+
const pipelines = listPipelines(ctx.projectPath);
|
|
892
|
+
const routing = getTagRouting(ctx.projectPath);
|
|
893
|
+
const result = { pipelines, tagRouting: routing };
|
|
894
|
+
if (storyTags && storyTags.length > 0) {
|
|
895
|
+
const resolved = await resolveExecutablePipeline(ctx.projectPath, storyTags);
|
|
896
|
+
result.preview = {
|
|
897
|
+
tags: storyTags,
|
|
898
|
+
selectedPipeline: resolved.name,
|
|
899
|
+
executableSteps: resolved.steps,
|
|
900
|
+
skippedSteps: resolved.skipped,
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
905
|
+
};
|
|
906
|
+
},
|
|
907
|
+
});
|
|
908
|
+
export const setPipelineTool = defineTool({
|
|
909
|
+
name: "set_pipeline",
|
|
910
|
+
category: "agents",
|
|
911
|
+
description: "Configure or update an agent pipeline at runtime",
|
|
912
|
+
fullDescription: `Create or update an agent pipeline at runtime. Changes persist for this project session (stored in .robot-chat/pipeline-override.json).
|
|
913
|
+
|
|
914
|
+
Example: set_pipeline({ name: "default", steps: [{ agent: "gemini", role: "planner" }, { agent: "codex", role: "implementer" }] })
|
|
915
|
+
|
|
916
|
+
Use reset=true to clear all runtime overrides and revert to defaults + config file.`,
|
|
917
|
+
inputSchema: z.object({
|
|
918
|
+
name: z.string().optional().describe("Pipeline name to create/update (e.g., 'default', 'frontend', 'backend', or any custom name)"),
|
|
919
|
+
steps: z.array(z.object({
|
|
920
|
+
agent: z.enum(AGENT_TYPES).describe("Agent for this step"),
|
|
921
|
+
role: z.string().describe("Role name (planner, implementer, verifier, drafter, fixer, reviewer, researcher)"),
|
|
922
|
+
optional: z.boolean().optional().describe("Skip if agent unavailable (default: true)"),
|
|
923
|
+
timeout: z.number().optional().describe("Timeout in seconds for this step"),
|
|
924
|
+
})).optional().describe("Pipeline steps in execution order"),
|
|
925
|
+
tagRouting: z.record(z.string()).optional().describe("Map story tags to pipeline names, e.g. { 'frontend': 'frontend', 'api': 'backend' }"),
|
|
926
|
+
reset: z.boolean().optional().describe("Clear all runtime overrides, revert to defaults"),
|
|
927
|
+
}),
|
|
928
|
+
handler: async ({ name, steps, tagRouting, reset }, ctx) => {
|
|
929
|
+
if (reset) {
|
|
930
|
+
clearPipelineOverride(ctx.projectPath);
|
|
931
|
+
return {
|
|
932
|
+
content: [{ type: "text", text: "Pipeline overrides cleared. Using defaults + config file." }],
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
if (!name && !tagRouting) {
|
|
936
|
+
return {
|
|
937
|
+
content: [{ type: "text", text: "Provide a pipeline name + steps, tagRouting rules, or reset=true." }],
|
|
938
|
+
isError: true,
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
const overrides = {};
|
|
942
|
+
if (name && steps) {
|
|
943
|
+
overrides.pipelines = { [name]: steps };
|
|
944
|
+
}
|
|
945
|
+
if (tagRouting) {
|
|
946
|
+
overrides.tagRouting = tagRouting;
|
|
947
|
+
}
|
|
948
|
+
savePipelineOverride(ctx.projectPath, overrides);
|
|
949
|
+
// Show the resolved state after update
|
|
950
|
+
const allPipelines = listPipelines(ctx.projectPath);
|
|
951
|
+
return {
|
|
952
|
+
content: [{
|
|
953
|
+
type: "text",
|
|
954
|
+
text: JSON.stringify({
|
|
955
|
+
message: `Pipeline${name ? ` "${name}"` : ""} updated.`,
|
|
956
|
+
currentPipelines: allPipelines,
|
|
957
|
+
}, null, 2),
|
|
958
|
+
}],
|
|
959
|
+
};
|
|
960
|
+
},
|
|
961
|
+
});
|
|
822
962
|
// Export all agent tools as an array
|
|
823
963
|
export const agentTools = [
|
|
824
964
|
listAvailableAgents,
|
|
@@ -831,5 +971,8 @@ export const agentTools = [
|
|
|
831
971
|
listRunningAgentsTool,
|
|
832
972
|
getDaemonStatusTool,
|
|
833
973
|
stopDaemonTool,
|
|
974
|
+
getRunStatsTool,
|
|
975
|
+
getPipelineTool,
|
|
976
|
+
setPipelineTool,
|
|
834
977
|
];
|
|
835
978
|
//# sourceMappingURL=agents.js.map
|