@robota-sdk/agent-sdk 3.0.0-beta.54 → 3.0.0-beta.55
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 +34 -16
- package/dist/node/index.cjs +2552 -1184
- package/dist/node/index.d.cts +1286 -952
- package/dist/node/index.d.ts +1286 -952
- package/dist/node/index.js +2530 -1172
- package/package.json +5 -4
package/dist/node/index.js
CHANGED
|
@@ -6,229 +6,1302 @@ import {
|
|
|
6
6
|
messageToHistoryEntry
|
|
7
7
|
} from "@robota-sdk/agent-core";
|
|
8
8
|
|
|
9
|
-
// src/commands/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
9
|
+
// src/commands/capability-descriptors.ts
|
|
10
|
+
function inferKind(command) {
|
|
11
|
+
if (command.source === "skill") return "skill";
|
|
12
|
+
if (command.source === "plugin" && command.skillContent) return "skill";
|
|
13
|
+
return "builtin-command";
|
|
14
|
+
}
|
|
15
|
+
function commandToCapabilityDescriptor(command) {
|
|
16
|
+
const skillLike = command.source === "skill" || command.source === "plugin" && Boolean(command.skillContent);
|
|
17
|
+
return {
|
|
18
|
+
name: `/${command.name}`,
|
|
19
|
+
kind: inferKind(command),
|
|
20
|
+
description: command.description,
|
|
21
|
+
userInvocable: command.userInvocable !== false,
|
|
22
|
+
modelInvocable: command.modelInvocable === true || skillLike && command.disableModelInvocation !== true,
|
|
23
|
+
...command.argumentHint ? { argumentHint: command.argumentHint } : {},
|
|
24
|
+
...command.safety ? { safety: command.safety } : {}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/commands/command-registry.ts
|
|
29
|
+
var CommandRegistry = class {
|
|
30
|
+
sources = [];
|
|
31
|
+
addSource(source) {
|
|
32
|
+
this.sources.push(source);
|
|
33
|
+
}
|
|
34
|
+
addModule(module) {
|
|
35
|
+
for (const source of module.commandSources ?? []) {
|
|
36
|
+
this.addSource(source);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Get all commands, optionally filtered by prefix */
|
|
40
|
+
getCommands(filter) {
|
|
41
|
+
const all = [];
|
|
42
|
+
for (const source of this.sources) {
|
|
43
|
+
all.push(...source.getCommands());
|
|
44
|
+
}
|
|
45
|
+
if (!filter) return all;
|
|
46
|
+
const lower = filter.toLowerCase();
|
|
47
|
+
return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
|
|
48
|
+
}
|
|
49
|
+
/** Resolve a short name to its fully qualified plugin:name form */
|
|
50
|
+
resolveQualifiedName(shortName) {
|
|
51
|
+
const matches = this.getCommands().filter(
|
|
52
|
+
(c) => c.source === "plugin" && c.name.includes(":") && c.name.endsWith(`:${shortName}`)
|
|
53
|
+
);
|
|
54
|
+
if (matches.length !== 1) return null;
|
|
55
|
+
return matches[0].name;
|
|
56
|
+
}
|
|
57
|
+
/** Get subcommands for a specific command */
|
|
58
|
+
getSubcommands(commandName) {
|
|
59
|
+
const lower = commandName.toLowerCase();
|
|
60
|
+
for (const source of this.sources) {
|
|
61
|
+
for (const cmd of source.getCommands()) {
|
|
62
|
+
if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
|
|
63
|
+
return cmd.subcommands;
|
|
64
|
+
}
|
|
58
65
|
}
|
|
59
|
-
}
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
getCapabilityDescriptors() {
|
|
70
|
+
return this.getCommands().map((command) => commandToCapabilityDescriptor(command));
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/commands/builtin-source.ts
|
|
75
|
+
import { CLAUDE_MODELS, formatTokenCount } from "@robota-sdk/agent-core";
|
|
76
|
+
function buildModelSubcommands() {
|
|
77
|
+
const seen = /* @__PURE__ */ new Set();
|
|
78
|
+
const commands = [];
|
|
79
|
+
for (const model of Object.values(CLAUDE_MODELS)) {
|
|
80
|
+
if (seen.has(model.name)) continue;
|
|
81
|
+
seen.add(model.name);
|
|
82
|
+
commands.push({
|
|
83
|
+
name: model.id,
|
|
84
|
+
description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
|
|
85
|
+
source: "builtin"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return commands;
|
|
89
|
+
}
|
|
90
|
+
function buildProviderSubcommands() {
|
|
91
|
+
return [
|
|
92
|
+
{ name: "current", description: "Show current provider", source: "builtin" },
|
|
93
|
+
{ name: "list", description: "List provider profiles", source: "builtin" },
|
|
94
|
+
{ name: "use", description: "Switch provider profile", source: "builtin" },
|
|
95
|
+
{ name: "test", description: "Test provider profile", source: "builtin" }
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
function buildBackgroundSubcommands() {
|
|
99
|
+
return [
|
|
100
|
+
{ name: "list", description: "List background tasks", source: "builtin" },
|
|
101
|
+
{ name: "read", description: "Read a background task log page", source: "builtin" },
|
|
102
|
+
{ name: "cancel", description: "Cancel a running background task", source: "builtin" },
|
|
103
|
+
{ name: "close", description: "Dismiss a terminal background task", source: "builtin" }
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
function createBuiltinCommands() {
|
|
107
|
+
return [
|
|
108
|
+
{ name: "help", description: "Show available commands", source: "builtin" },
|
|
109
|
+
{ name: "clear", description: "Clear conversation history", source: "builtin" },
|
|
60
110
|
{
|
|
61
111
|
name: "mode",
|
|
62
|
-
description: "
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
data: { mode: underlying.getPermissionMode() }
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
if (VALID_MODES.includes(arg)) {
|
|
74
|
-
underlying.setPermissionMode(arg);
|
|
75
|
-
return {
|
|
76
|
-
message: `Permission mode set to: ${arg}`,
|
|
77
|
-
success: true,
|
|
78
|
-
data: { mode: arg }
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
|
|
83
|
-
success: false
|
|
84
|
-
};
|
|
85
|
-
}
|
|
112
|
+
description: "Permission mode",
|
|
113
|
+
source: "builtin",
|
|
114
|
+
subcommands: [
|
|
115
|
+
{ name: "plan", description: "Plan only, no execution", source: "builtin" },
|
|
116
|
+
{ name: "default", description: "Ask before risky actions", source: "builtin" },
|
|
117
|
+
{ name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
|
|
118
|
+
{ name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
|
|
119
|
+
]
|
|
86
120
|
},
|
|
87
121
|
{
|
|
88
122
|
name: "model",
|
|
89
|
-
description: "
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!modelId) {
|
|
93
|
-
return { message: "Usage: model <model-id>", success: false };
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
message: `Model change requested: ${modelId}`,
|
|
97
|
-
success: true,
|
|
98
|
-
data: { modelId }
|
|
99
|
-
};
|
|
100
|
-
}
|
|
123
|
+
description: "Select AI model",
|
|
124
|
+
source: "builtin",
|
|
125
|
+
subcommands: buildModelSubcommands()
|
|
101
126
|
},
|
|
102
127
|
{
|
|
103
128
|
name: "language",
|
|
104
129
|
description: "Set response language",
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
success: true,
|
|
113
|
-
data: { language: lang }
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: "cost",
|
|
119
|
-
description: "Show session info",
|
|
120
|
-
execute: (session, _args) => {
|
|
121
|
-
const underlying = session.getSession();
|
|
122
|
-
const sessionId = underlying.getSessionId();
|
|
123
|
-
const messageCount = underlying.getMessageCount();
|
|
124
|
-
return {
|
|
125
|
-
message: `Session: ${sessionId}
|
|
126
|
-
Messages: ${messageCount}`,
|
|
127
|
-
success: true,
|
|
128
|
-
data: { sessionId, messageCount }
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
name: "context",
|
|
134
|
-
description: "Context window info",
|
|
135
|
-
execute: (session, _args) => {
|
|
136
|
-
const ctx = session.getContextState();
|
|
137
|
-
return {
|
|
138
|
-
message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
|
|
139
|
-
success: true,
|
|
140
|
-
data: {
|
|
141
|
-
usedTokens: ctx.usedTokens,
|
|
142
|
-
maxTokens: ctx.maxTokens,
|
|
143
|
-
percentage: ctx.usedPercentage
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
}
|
|
130
|
+
source: "builtin",
|
|
131
|
+
subcommands: [
|
|
132
|
+
{ name: "ko", description: "Korean", source: "builtin" },
|
|
133
|
+
{ name: "en", description: "English", source: "builtin" },
|
|
134
|
+
{ name: "ja", description: "Japanese", source: "builtin" },
|
|
135
|
+
{ name: "zh", description: "Chinese", source: "builtin" }
|
|
136
|
+
]
|
|
147
137
|
},
|
|
138
|
+
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
139
|
+
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
140
|
+
{ name: "context", description: "Context window info", source: "builtin" },
|
|
141
|
+
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
148
142
|
{
|
|
149
|
-
name: "
|
|
150
|
-
description: "
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const mode = underlying.getPermissionMode();
|
|
154
|
-
const sessionAllowed = underlying.getSessionAllowedTools();
|
|
155
|
-
const lines = [`Permission mode: ${mode}`];
|
|
156
|
-
if (sessionAllowed.length > 0) {
|
|
157
|
-
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
158
|
-
} else {
|
|
159
|
-
lines.push("No session-approved tools.");
|
|
160
|
-
}
|
|
161
|
-
return {
|
|
162
|
-
message: lines.join("\n"),
|
|
163
|
-
success: true,
|
|
164
|
-
data: { mode, sessionAllowed }
|
|
165
|
-
};
|
|
166
|
-
}
|
|
143
|
+
name: "provider",
|
|
144
|
+
description: "Manage provider profiles",
|
|
145
|
+
source: "builtin",
|
|
146
|
+
subcommands: buildProviderSubcommands()
|
|
167
147
|
},
|
|
148
|
+
{ name: "resume", description: "Resume a previous session", source: "builtin" },
|
|
168
149
|
{
|
|
169
|
-
name: "
|
|
170
|
-
description: "
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
success: true,
|
|
174
|
-
data: { triggerResumePicker: true }
|
|
175
|
-
})
|
|
150
|
+
name: "background",
|
|
151
|
+
description: "List and control background tasks",
|
|
152
|
+
source: "builtin",
|
|
153
|
+
subcommands: buildBackgroundSubcommands()
|
|
176
154
|
},
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
155
|
+
{ name: "rename", description: "Rename the current session", source: "builtin" },
|
|
156
|
+
{ name: "plugin", description: "Manage plugins", source: "builtin" },
|
|
157
|
+
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
158
|
+
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
159
|
+
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
var BuiltinCommandSource = class {
|
|
163
|
+
name = "builtin";
|
|
164
|
+
commands;
|
|
165
|
+
constructor() {
|
|
166
|
+
this.commands = createBuiltinCommands();
|
|
167
|
+
}
|
|
168
|
+
getCommands() {
|
|
169
|
+
return this.commands;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/commands/skill-source.ts
|
|
174
|
+
import { readdirSync, readFileSync, existsSync } from "fs";
|
|
175
|
+
import { join, basename } from "path";
|
|
176
|
+
import { homedir } from "os";
|
|
177
|
+
var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
|
|
178
|
+
var LIST_KEYS = /* @__PURE__ */ new Set(["allowed-tools"]);
|
|
179
|
+
function kebabToCamel(key) {
|
|
180
|
+
return key.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
181
|
+
}
|
|
182
|
+
function parseListValue(rawValue) {
|
|
183
|
+
const separator = rawValue.includes(",") ? /\s*,\s*/ : /\s+/;
|
|
184
|
+
return rawValue.split(separator).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
185
|
+
}
|
|
186
|
+
function parseFrontmatter(content) {
|
|
187
|
+
const lines = content.split("\n");
|
|
188
|
+
if (lines[0]?.trim() !== "---") return null;
|
|
189
|
+
const result = {};
|
|
190
|
+
for (let i = 1; i < lines.length; i++) {
|
|
191
|
+
const line = lines[i];
|
|
192
|
+
if (line.trim() === "---") break;
|
|
193
|
+
const match = line.match(/^([a-z][a-z0-9-]*):\s*(.+)/);
|
|
194
|
+
if (!match) continue;
|
|
195
|
+
const key = match[1];
|
|
196
|
+
const rawValue = match[2].trim();
|
|
197
|
+
const camelKey = kebabToCamel(key);
|
|
198
|
+
if (BOOLEAN_KEYS.has(key)) {
|
|
199
|
+
result[camelKey] = rawValue === "true";
|
|
200
|
+
} else if (LIST_KEYS.has(key)) {
|
|
201
|
+
result[camelKey] = parseListValue(rawValue);
|
|
202
|
+
} else {
|
|
203
|
+
result[camelKey] = rawValue;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
207
|
+
}
|
|
208
|
+
function buildCommand(frontmatter, content, fallbackName) {
|
|
209
|
+
const cmd = {
|
|
210
|
+
name: frontmatter?.name ?? fallbackName,
|
|
211
|
+
description: frontmatter?.description ?? `Skill: ${fallbackName}`,
|
|
212
|
+
source: "skill",
|
|
213
|
+
skillContent: content
|
|
214
|
+
};
|
|
215
|
+
if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
|
|
216
|
+
if (frontmatter?.disableModelInvocation !== void 0)
|
|
217
|
+
cmd.disableModelInvocation = frontmatter.disableModelInvocation;
|
|
218
|
+
if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
|
|
219
|
+
if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
|
|
220
|
+
if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
|
|
221
|
+
if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
|
|
222
|
+
if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
|
|
223
|
+
if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
|
|
224
|
+
return cmd;
|
|
225
|
+
}
|
|
226
|
+
function scanSkillsDir(skillsDir) {
|
|
227
|
+
if (!existsSync(skillsDir)) return [];
|
|
228
|
+
const commands = [];
|
|
229
|
+
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
230
|
+
for (const entry of entries) {
|
|
231
|
+
if (!entry.isDirectory()) continue;
|
|
232
|
+
const skillFile = join(skillsDir, entry.name, "SKILL.md");
|
|
233
|
+
if (!existsSync(skillFile)) continue;
|
|
234
|
+
const content = readFileSync(skillFile, "utf-8");
|
|
235
|
+
const frontmatter = parseFrontmatter(content);
|
|
236
|
+
commands.push(buildCommand(frontmatter, content, entry.name));
|
|
237
|
+
}
|
|
238
|
+
return commands;
|
|
239
|
+
}
|
|
240
|
+
function scanCommandsDir(commandsDir) {
|
|
241
|
+
if (!existsSync(commandsDir)) return [];
|
|
242
|
+
const commands = [];
|
|
243
|
+
const entries = readdirSync(commandsDir, { withFileTypes: true });
|
|
244
|
+
for (const entry of entries) {
|
|
245
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
246
|
+
const filePath = join(commandsDir, entry.name);
|
|
247
|
+
const content = readFileSync(filePath, "utf-8");
|
|
248
|
+
const frontmatter = parseFrontmatter(content);
|
|
249
|
+
const fallbackName = basename(entry.name, ".md");
|
|
250
|
+
commands.push(buildCommand(frontmatter, content, fallbackName));
|
|
251
|
+
}
|
|
252
|
+
return commands;
|
|
253
|
+
}
|
|
254
|
+
var SkillCommandSource = class {
|
|
255
|
+
name = "skill";
|
|
256
|
+
cwd;
|
|
257
|
+
home;
|
|
258
|
+
cachedCommands = null;
|
|
259
|
+
constructor(cwd, home) {
|
|
260
|
+
this.cwd = cwd;
|
|
261
|
+
this.home = home ?? homedir();
|
|
262
|
+
}
|
|
263
|
+
getCommands() {
|
|
264
|
+
if (this.cachedCommands) return this.cachedCommands;
|
|
265
|
+
const sources = [
|
|
266
|
+
scanSkillsDir(join(this.cwd, ".claude", "skills")),
|
|
267
|
+
scanCommandsDir(join(this.cwd, ".claude", "commands")),
|
|
268
|
+
scanSkillsDir(join(this.home, ".robota", "skills")),
|
|
269
|
+
scanSkillsDir(join(this.cwd, ".agents", "skills"))
|
|
270
|
+
];
|
|
271
|
+
const seen = /* @__PURE__ */ new Set();
|
|
272
|
+
const merged = [];
|
|
273
|
+
for (const commands of sources) {
|
|
274
|
+
for (const cmd of commands) {
|
|
275
|
+
if (!seen.has(cmd.name)) {
|
|
276
|
+
seen.add(cmd.name);
|
|
277
|
+
merged.push(cmd);
|
|
184
278
|
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
this.cachedCommands = merged;
|
|
282
|
+
return this.cachedCommands;
|
|
283
|
+
}
|
|
284
|
+
getModelInvocableSkills() {
|
|
285
|
+
return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
|
|
286
|
+
}
|
|
287
|
+
getUserInvocableSkills() {
|
|
288
|
+
return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// src/commands/plugin-source.ts
|
|
293
|
+
var PluginCommandSource = class {
|
|
294
|
+
name = "plugin";
|
|
295
|
+
plugins;
|
|
296
|
+
constructor(plugins) {
|
|
297
|
+
this.plugins = plugins;
|
|
298
|
+
}
|
|
299
|
+
getCommands() {
|
|
300
|
+
const commands = [];
|
|
301
|
+
for (const plugin of this.plugins) {
|
|
302
|
+
for (const skill of plugin.skills) {
|
|
303
|
+
const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
|
|
304
|
+
commands.push({
|
|
305
|
+
name: baseName,
|
|
306
|
+
description: `(${plugin.manifest.name}) ${skill.description}`,
|
|
307
|
+
source: "plugin",
|
|
308
|
+
skillContent: skill.skillContent,
|
|
309
|
+
pluginDir: plugin.pluginDir
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
for (const cmd of plugin.commands) {
|
|
313
|
+
commands.push({
|
|
314
|
+
name: cmd.name,
|
|
315
|
+
description: cmd.description,
|
|
316
|
+
source: "plugin",
|
|
317
|
+
skillContent: cmd.skillContent,
|
|
318
|
+
pluginDir: plugin.pluginDir
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return commands;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// src/commands/background-command.ts
|
|
327
|
+
var DECIMAL_RADIX = 10;
|
|
328
|
+
function parseCommandParts(args) {
|
|
329
|
+
return args.trim().split(/\s+/).filter(Boolean);
|
|
330
|
+
}
|
|
331
|
+
function formatBackgroundTask(task) {
|
|
332
|
+
const preview = task.promptPreview ?? task.commandPreview ?? "";
|
|
333
|
+
const unread = task.unread ? " unread" : "";
|
|
334
|
+
const action = task.currentAction ? ` (${task.currentAction})` : "";
|
|
335
|
+
const timeout = task.timeoutReason ? ` timeout=${task.timeoutReason}` : "";
|
|
336
|
+
const activity = task.lastActivityAt ? ` lastActivityAt=${task.lastActivityAt}` : "";
|
|
337
|
+
const suffix = preview ? ` \u2014 ${preview}` : "";
|
|
338
|
+
return `${task.id} [${task.status}${unread}${timeout}${activity}] ${task.kind}:${task.label}${action}${suffix}`;
|
|
339
|
+
}
|
|
340
|
+
function formatBackgroundTaskList(tasks) {
|
|
341
|
+
if (tasks.length === 0) return "No background tasks.";
|
|
342
|
+
return ["Background tasks:", ...tasks.map((task) => ` ${formatBackgroundTask(task)}`)].join(
|
|
343
|
+
"\n"
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
function parseCursor(value) {
|
|
347
|
+
if (!value) return void 0;
|
|
348
|
+
const offset = Number.parseInt(value, DECIMAL_RADIX);
|
|
349
|
+
return Number.isNaN(offset) ? void 0 : { offset };
|
|
350
|
+
}
|
|
351
|
+
async function executeBackgroundCommand(session, args) {
|
|
352
|
+
const [action = "list", taskId, ...reasonParts] = parseCommandParts(args);
|
|
353
|
+
if (action === "list") {
|
|
354
|
+
const tasks = session.listBackgroundTasks();
|
|
355
|
+
return {
|
|
356
|
+
message: formatBackgroundTaskList(tasks),
|
|
357
|
+
success: true,
|
|
358
|
+
data: { count: tasks.length }
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
if (!taskId) {
|
|
362
|
+
return {
|
|
363
|
+
message: "Usage: background list | background read <task-id> [offset] | background cancel <task-id> | background close <task-id>",
|
|
364
|
+
success: false
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
if (action === "read" || action === "log" || action === "open") {
|
|
368
|
+
const page = await session.readBackgroundTaskLog(taskId, parseCursor(reasonParts[0]));
|
|
369
|
+
const next = page.nextCursor ? `
|
|
370
|
+
Next offset: ${page.nextCursor.offset}` : "";
|
|
371
|
+
return {
|
|
372
|
+
message: page.lines.length > 0 ? `${page.lines.join("\n")}${next}` : `No log lines: ${taskId}`,
|
|
373
|
+
success: true,
|
|
374
|
+
data: { taskId, nextOffset: page.nextCursor?.offset }
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
if (action === "cancel" || action === "stop") {
|
|
378
|
+
await session.cancelBackgroundTask(taskId, reasonParts.join(" ") || void 0);
|
|
379
|
+
return { message: `Background task cancelled: ${taskId}`, success: true, data: { taskId } };
|
|
380
|
+
}
|
|
381
|
+
if (action === "close" || action === "dismiss") {
|
|
382
|
+
await session.closeBackgroundTask(taskId);
|
|
383
|
+
return { message: `Background task closed: ${taskId}`, success: true, data: { taskId } };
|
|
384
|
+
}
|
|
385
|
+
return { message: `Unknown background action: ${action}`, success: false };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/commands/system-command.ts
|
|
389
|
+
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
390
|
+
function createSystemCommands() {
|
|
391
|
+
return [
|
|
392
|
+
{
|
|
393
|
+
name: "help",
|
|
394
|
+
description: "Show available commands",
|
|
395
|
+
execute: (_session, _args) => ({
|
|
396
|
+
message: [
|
|
397
|
+
"Available commands:",
|
|
398
|
+
" help \u2014 Show this help",
|
|
399
|
+
" clear \u2014 Clear conversation",
|
|
400
|
+
" compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
401
|
+
" mode [m] \u2014 Show/change permission mode",
|
|
402
|
+
" model <id> \u2014 Change AI model",
|
|
403
|
+
" language <code> \u2014 Set response language (ko, en, ja, zh)",
|
|
404
|
+
" cost \u2014 Show session info",
|
|
405
|
+
" context \u2014 Context window info",
|
|
406
|
+
" permissions \u2014 Permission rules",
|
|
407
|
+
" provider \u2014 Provider profile status and switching",
|
|
408
|
+
" resume \u2014 Resume a previous session",
|
|
409
|
+
" background \u2014 List/cancel/close background tasks",
|
|
410
|
+
" rename <name> \u2014 Rename the current session",
|
|
411
|
+
" reset \u2014 Delete settings and exit"
|
|
412
|
+
].join("\n"),
|
|
413
|
+
success: true
|
|
414
|
+
})
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "clear",
|
|
418
|
+
description: "Clear conversation history",
|
|
419
|
+
execute: (session, _args) => {
|
|
420
|
+
const underlying = session.getSession();
|
|
421
|
+
underlying.clearHistory();
|
|
422
|
+
return { message: "Conversation cleared.", success: true };
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: "compact",
|
|
427
|
+
description: "Compress context window",
|
|
428
|
+
execute: async (session, args) => {
|
|
429
|
+
const underlying = session.getSession();
|
|
430
|
+
const instructions = args.trim() || void 0;
|
|
431
|
+
const before = underlying.getContextState().usedPercentage;
|
|
432
|
+
await underlying.compact(instructions);
|
|
433
|
+
const after = underlying.getContextState().usedPercentage;
|
|
185
434
|
return {
|
|
186
|
-
message: `
|
|
435
|
+
message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
|
|
187
436
|
success: true,
|
|
188
|
-
data: {
|
|
437
|
+
data: { before, after }
|
|
189
438
|
};
|
|
190
439
|
}
|
|
191
440
|
},
|
|
192
441
|
{
|
|
193
|
-
name: "
|
|
194
|
-
description: "
|
|
195
|
-
execute: (
|
|
442
|
+
name: "mode",
|
|
443
|
+
description: "Show/change permission mode",
|
|
444
|
+
execute: (session, args) => {
|
|
445
|
+
const underlying = session.getSession();
|
|
446
|
+
const arg = args.trim().split(/\s+/)[0];
|
|
447
|
+
if (!arg) {
|
|
448
|
+
return {
|
|
449
|
+
message: `Current mode: ${underlying.getPermissionMode()}`,
|
|
450
|
+
success: true,
|
|
451
|
+
data: { mode: underlying.getPermissionMode() }
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (VALID_MODES.includes(arg)) {
|
|
455
|
+
underlying.setPermissionMode(arg);
|
|
456
|
+
return {
|
|
457
|
+
message: `Permission mode set to: ${arg}`,
|
|
458
|
+
success: true,
|
|
459
|
+
data: { mode: arg }
|
|
460
|
+
};
|
|
461
|
+
}
|
|
196
462
|
return {
|
|
197
|
-
message:
|
|
198
|
-
success:
|
|
199
|
-
data: { resetRequested: true }
|
|
463
|
+
message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
|
|
464
|
+
success: false
|
|
200
465
|
};
|
|
201
466
|
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: "model",
|
|
470
|
+
description: "Change AI model",
|
|
471
|
+
execute: (_session, args) => {
|
|
472
|
+
const modelId = args.trim().split(/\s+/)[0];
|
|
473
|
+
if (!modelId) {
|
|
474
|
+
return { message: "Usage: model <model-id>", success: false };
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
message: `Model change requested: ${modelId}`,
|
|
478
|
+
success: true,
|
|
479
|
+
data: { modelId }
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: "language",
|
|
485
|
+
description: "Set response language",
|
|
486
|
+
execute: (_session, args) => {
|
|
487
|
+
const lang = args.trim().split(/\s+/)[0];
|
|
488
|
+
if (!lang) {
|
|
489
|
+
return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
message: `Language set to "${lang}".`,
|
|
493
|
+
success: true,
|
|
494
|
+
data: { language: lang }
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
name: "cost",
|
|
500
|
+
description: "Show session info",
|
|
501
|
+
execute: (session, _args) => {
|
|
502
|
+
const underlying = session.getSession();
|
|
503
|
+
const sessionId = underlying.getSessionId();
|
|
504
|
+
const messageCount = underlying.getMessageCount();
|
|
505
|
+
return {
|
|
506
|
+
message: `Session: ${sessionId}
|
|
507
|
+
Messages: ${messageCount}`,
|
|
508
|
+
success: true,
|
|
509
|
+
data: { sessionId, messageCount }
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
name: "context",
|
|
515
|
+
description: "Context window info",
|
|
516
|
+
execute: (session, _args) => {
|
|
517
|
+
const ctx = session.getContextState();
|
|
518
|
+
return {
|
|
519
|
+
message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
|
|
520
|
+
success: true,
|
|
521
|
+
data: {
|
|
522
|
+
usedTokens: ctx.usedTokens,
|
|
523
|
+
maxTokens: ctx.maxTokens,
|
|
524
|
+
percentage: ctx.usedPercentage
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
name: "permissions",
|
|
531
|
+
description: "Show permission rules",
|
|
532
|
+
execute: (session, _args) => {
|
|
533
|
+
const underlying = session.getSession();
|
|
534
|
+
const mode = underlying.getPermissionMode();
|
|
535
|
+
const sessionAllowed = underlying.getSessionAllowedTools();
|
|
536
|
+
const lines = [`Permission mode: ${mode}`];
|
|
537
|
+
if (sessionAllowed.length > 0) {
|
|
538
|
+
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
539
|
+
} else {
|
|
540
|
+
lines.push("No session-approved tools.");
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
message: lines.join("\n"),
|
|
544
|
+
success: true,
|
|
545
|
+
data: { mode, sessionAllowed }
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
name: "resume",
|
|
551
|
+
description: "Resume a previous session",
|
|
552
|
+
execute: (_session, _args) => ({
|
|
553
|
+
message: "Opening session picker...",
|
|
554
|
+
success: true,
|
|
555
|
+
data: { triggerResumePicker: true }
|
|
556
|
+
})
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: "background",
|
|
560
|
+
description: "List and control background tasks",
|
|
561
|
+
execute: executeBackgroundCommand
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
name: "rename",
|
|
565
|
+
description: "Rename the current session",
|
|
566
|
+
execute: (_session, args) => {
|
|
567
|
+
const name = args.trim();
|
|
568
|
+
if (!name) {
|
|
569
|
+
return { message: "Usage: rename <name>", success: false };
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
message: `Session renamed to "${name}".`,
|
|
573
|
+
success: true,
|
|
574
|
+
data: { name }
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
name: "reset",
|
|
580
|
+
description: "Delete settings",
|
|
581
|
+
execute: (_session, _args) => {
|
|
582
|
+
return {
|
|
583
|
+
message: "Reset requested.",
|
|
584
|
+
success: true,
|
|
585
|
+
data: { resetRequested: true }
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
];
|
|
590
|
+
}
|
|
591
|
+
var SystemCommandExecutor = class {
|
|
592
|
+
commands;
|
|
593
|
+
constructor(commands) {
|
|
594
|
+
this.commands = /* @__PURE__ */ new Map();
|
|
595
|
+
for (const cmd of commands ?? createSystemCommands()) {
|
|
596
|
+
this.commands.set(cmd.name, cmd);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/** Register an additional command. */
|
|
600
|
+
register(command) {
|
|
601
|
+
this.commands.set(command.name, command);
|
|
602
|
+
}
|
|
603
|
+
/** Execute a command by name. Returns null if command not found. */
|
|
604
|
+
async execute(name, session, args) {
|
|
605
|
+
const cmd = this.commands.get(name);
|
|
606
|
+
if (!cmd) return null;
|
|
607
|
+
return await cmd.execute(session, args);
|
|
608
|
+
}
|
|
609
|
+
/** List all registered commands. */
|
|
610
|
+
listCommands() {
|
|
611
|
+
return [...this.commands.values()];
|
|
612
|
+
}
|
|
613
|
+
listModelInvocableCommands() {
|
|
614
|
+
return this.listCommands().filter((command) => command.modelInvocable === true).map((command) => ({
|
|
615
|
+
name: `/${command.name}`,
|
|
616
|
+
kind: "builtin-command",
|
|
617
|
+
description: command.description,
|
|
618
|
+
userInvocable: command.userInvocable !== false,
|
|
619
|
+
modelInvocable: true,
|
|
620
|
+
...command.argumentHint ? { argumentHint: command.argumentHint } : {},
|
|
621
|
+
...command.safety ? { safety: command.safety } : {}
|
|
622
|
+
}));
|
|
623
|
+
}
|
|
624
|
+
isModelInvocable(name) {
|
|
625
|
+
return this.commands.get(name)?.modelInvocable === true;
|
|
626
|
+
}
|
|
627
|
+
async executeModelInvocable(name, session, args) {
|
|
628
|
+
if (!this.isModelInvocable(name)) return null;
|
|
629
|
+
return this.execute(name, session, args);
|
|
630
|
+
}
|
|
631
|
+
/** Check if a command exists. */
|
|
632
|
+
hasCommand(name) {
|
|
633
|
+
return this.commands.has(name);
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
// src/utils/skill-prompt.ts
|
|
638
|
+
import { execSync } from "child_process";
|
|
639
|
+
function substituteVariables(content, args, context) {
|
|
640
|
+
const argParts = args ? args.split(/\s+/) : [];
|
|
641
|
+
let result = content;
|
|
642
|
+
result = result.replace(/\$ARGUMENTS\[(\d+)]/g, (_match, index) => {
|
|
643
|
+
return argParts[Number(index)] ?? "";
|
|
644
|
+
});
|
|
645
|
+
result = result.replace(/\$ARGUMENTS/g, args);
|
|
646
|
+
result = result.replace(/\$(\d)(?!\d|\w|\[)/g, (_match, digit) => {
|
|
647
|
+
return argParts[Number(digit)] ?? "";
|
|
648
|
+
});
|
|
649
|
+
result = result.replace(/\$\{CLAUDE_SESSION_ID}/g, context?.sessionId ?? "");
|
|
650
|
+
result = result.replace(/\$\{CLAUDE_SKILL_DIR}/g, context?.skillDir ?? "");
|
|
651
|
+
return result;
|
|
652
|
+
}
|
|
653
|
+
async function preprocessShellCommands(content) {
|
|
654
|
+
const shellPattern = /!`([^`]+)`/g;
|
|
655
|
+
if (!shellPattern.test(content)) {
|
|
656
|
+
return content;
|
|
657
|
+
}
|
|
658
|
+
shellPattern.lastIndex = 0;
|
|
659
|
+
let result = content;
|
|
660
|
+
let match;
|
|
661
|
+
const matches = [];
|
|
662
|
+
while ((match = shellPattern.exec(content)) !== null) {
|
|
663
|
+
matches.push({ full: match[0], command: match[1] });
|
|
664
|
+
}
|
|
665
|
+
for (const { full, command } of matches) {
|
|
666
|
+
let output = "";
|
|
667
|
+
try {
|
|
668
|
+
output = execSync(command, {
|
|
669
|
+
timeout: 5e3,
|
|
670
|
+
encoding: "utf-8",
|
|
671
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
672
|
+
}).trimEnd();
|
|
673
|
+
} catch {
|
|
674
|
+
output = "";
|
|
675
|
+
}
|
|
676
|
+
result = result.replace(full, output);
|
|
677
|
+
}
|
|
678
|
+
return result;
|
|
679
|
+
}
|
|
680
|
+
async function buildSkillPrompt(input, registry, context) {
|
|
681
|
+
const parts = input.slice(1).split(/\s+/);
|
|
682
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
683
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
684
|
+
if (!skillCmd) return null;
|
|
685
|
+
const args = parts.slice(1).join(" ").trim();
|
|
686
|
+
const userInstruction = args || skillCmd.description;
|
|
687
|
+
if (skillCmd.skillContent) {
|
|
688
|
+
let processed = await preprocessShellCommands(skillCmd.skillContent);
|
|
689
|
+
processed = substituteVariables(processed, args, context);
|
|
690
|
+
return `<skill name="${cmd}">
|
|
691
|
+
${processed}
|
|
692
|
+
</skill>
|
|
693
|
+
|
|
694
|
+
Execute the "${cmd}" skill: ${userInstruction}`;
|
|
695
|
+
}
|
|
696
|
+
return `Use the "${cmd}" skill: ${userInstruction}`;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// src/commands/skill-executor.ts
|
|
700
|
+
async function buildProcessedContent(skill, args, context) {
|
|
701
|
+
if (!skill.skillContent) return null;
|
|
702
|
+
const preprocessed = await preprocessShellCommands(skill.skillContent);
|
|
703
|
+
return substituteVariables(preprocessed, args, context);
|
|
704
|
+
}
|
|
705
|
+
async function buildInjectPrompt(skill, args, context) {
|
|
706
|
+
const processed = await buildProcessedContent(skill, args, context);
|
|
707
|
+
if (processed) {
|
|
708
|
+
const userInstruction = args || skill.description;
|
|
709
|
+
return `<skill name="${skill.name}">
|
|
710
|
+
${processed}
|
|
711
|
+
</skill>
|
|
712
|
+
|
|
713
|
+
Execute the "${skill.name}" skill: ${userInstruction}`;
|
|
714
|
+
}
|
|
715
|
+
return `Use the "${skill.name}" skill: ${args || skill.description}`;
|
|
716
|
+
}
|
|
717
|
+
async function executeSkill(skill, args, callbacks, context) {
|
|
718
|
+
if (skill.context === "fork") {
|
|
719
|
+
if (!callbacks.runInFork) {
|
|
720
|
+
throw new Error("Fork execution is not available. Agent tool deps may not be initialized.");
|
|
721
|
+
}
|
|
722
|
+
const content = await buildProcessedContent(skill, args, context);
|
|
723
|
+
const prompt2 = content ?? `Use the "${skill.name}" skill: ${args || skill.description}`;
|
|
724
|
+
const options = {};
|
|
725
|
+
if (skill.agent) options.agent = skill.agent;
|
|
726
|
+
if (skill.allowedTools) options.allowedTools = skill.allowedTools;
|
|
727
|
+
const result = await callbacks.runInFork(prompt2, options);
|
|
728
|
+
return { mode: "fork", result };
|
|
729
|
+
}
|
|
730
|
+
const prompt = await buildInjectPrompt(skill, args, context);
|
|
731
|
+
return { mode: "inject", prompt };
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/assembly/create-subagent-session.ts
|
|
735
|
+
import { Session } from "@robota-sdk/agent-sessions";
|
|
736
|
+
|
|
737
|
+
// src/assembly/subagent-prompts.ts
|
|
738
|
+
function getSubagentSuffix() {
|
|
739
|
+
return `When you complete the task, respond with a concise report covering what was done and any key findings \u2014 the caller will relay this to the user, so it only needs the essentials.
|
|
740
|
+
|
|
741
|
+
In your final response, share file paths (always absolute, never relative) that are relevant to the task. Include code snippets only when the exact text is load-bearing \u2014 do not recap code you merely read.
|
|
742
|
+
|
|
743
|
+
Do not use emojis.`;
|
|
744
|
+
}
|
|
745
|
+
function getForkWorkerSuffix() {
|
|
746
|
+
return `You are a worker subagent executing a specific task. Do NOT spawn sub-agents; execute directly. Keep your report under 500 words. Use this structure:
|
|
747
|
+
- Scope: What was requested
|
|
748
|
+
- Result: What was done
|
|
749
|
+
- Key files: Relevant file paths (absolute)
|
|
750
|
+
- Files changed: List of modifications
|
|
751
|
+
- Issues: Any problems encountered`;
|
|
752
|
+
}
|
|
753
|
+
function assembleSubagentPrompt(options) {
|
|
754
|
+
const parts = [options.agentBody];
|
|
755
|
+
if (options.claudeMd) {
|
|
756
|
+
parts.push(options.claudeMd);
|
|
757
|
+
}
|
|
758
|
+
if (options.agentsMd) {
|
|
759
|
+
parts.push(options.agentsMd);
|
|
760
|
+
}
|
|
761
|
+
const suffix = options.isForkWorker ? getForkWorkerSuffix() : getSubagentSuffix();
|
|
762
|
+
parts.push(suffix);
|
|
763
|
+
return parts.join("\n\n");
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// src/assembly/create-subagent-session.ts
|
|
767
|
+
var MODEL_SHORTCUTS = {
|
|
768
|
+
sonnet: "claude-sonnet-4-6",
|
|
769
|
+
haiku: "claude-haiku-4-5",
|
|
770
|
+
opus: "claude-opus-4-6"
|
|
771
|
+
};
|
|
772
|
+
function resolveModelId(shortName, _parentModel) {
|
|
773
|
+
return MODEL_SHORTCUTS[shortName] ?? shortName;
|
|
774
|
+
}
|
|
775
|
+
function filterTools(parentTools, agentDefinition) {
|
|
776
|
+
let tools = [...parentTools];
|
|
777
|
+
if (agentDefinition.disallowedTools) {
|
|
778
|
+
const denySet = new Set(agentDefinition.disallowedTools);
|
|
779
|
+
tools = tools.filter((t) => !denySet.has(t.getName()));
|
|
780
|
+
}
|
|
781
|
+
if (agentDefinition.tools) {
|
|
782
|
+
const allowSet = new Set(agentDefinition.tools);
|
|
783
|
+
tools = tools.filter((t) => allowSet.has(t.getName()));
|
|
784
|
+
}
|
|
785
|
+
tools = tools.filter((t) => t.getName() !== "Agent");
|
|
786
|
+
return tools;
|
|
787
|
+
}
|
|
788
|
+
function createSubagentSession(options) {
|
|
789
|
+
const { agentDefinition, parentConfig, parentContext, parentTools, terminal } = options;
|
|
790
|
+
const tools = filterTools(parentTools, agentDefinition);
|
|
791
|
+
const model = agentDefinition.model ? resolveModelId(agentDefinition.model, parentConfig.provider.model) : parentConfig.provider.model;
|
|
792
|
+
const systemMessage = assembleSubagentPrompt({
|
|
793
|
+
agentBody: agentDefinition.systemPrompt,
|
|
794
|
+
claudeMd: parentContext.claudeMd,
|
|
795
|
+
agentsMd: parentContext.agentsMd,
|
|
796
|
+
isForkWorker: options.isForkWorker ?? false
|
|
797
|
+
});
|
|
798
|
+
const provider = options.provider;
|
|
799
|
+
return new Session({
|
|
800
|
+
tools,
|
|
801
|
+
provider,
|
|
802
|
+
systemMessage,
|
|
803
|
+
terminal,
|
|
804
|
+
...options.sessionId !== void 0 ? { sessionId: options.sessionId } : {},
|
|
805
|
+
...options.sessionLogger !== void 0 ? { sessionLogger: options.sessionLogger } : {},
|
|
806
|
+
model,
|
|
807
|
+
maxTurns: agentDefinition.maxTurns,
|
|
808
|
+
permissions: parentConfig.permissions,
|
|
809
|
+
permissionMode: options.permissionMode,
|
|
810
|
+
defaultTrustLevel: parentConfig.defaultTrustLevel,
|
|
811
|
+
permissionHandler: options.permissionHandler,
|
|
812
|
+
hooks: options.hooks,
|
|
813
|
+
hookTypeExecutors: options.hookTypeExecutors,
|
|
814
|
+
onTextDelta: options.onTextDelta,
|
|
815
|
+
onToolExecution: options.onToolExecution
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/agents/built-in-agents.ts
|
|
820
|
+
var GENERAL_PURPOSE_SYSTEM_PROMPT = `You are a general-purpose task execution agent. You have access to all tools available in the parent session and can perform any task delegated to you.
|
|
821
|
+
|
|
822
|
+
Your role is to complete the assigned task thoroughly and accurately. Follow these guidelines:
|
|
823
|
+
|
|
824
|
+
- Execute the task as described in the prompt. Do not expand scope beyond what is requested.
|
|
825
|
+
- Use the most appropriate tools for each step. Prefer precise tools (Read, Grep, Glob) over broad ones (Bash) when possible.
|
|
826
|
+
- Report your findings clearly and concisely when the task is complete.
|
|
827
|
+
- If a task cannot be completed, explain why and what information is missing.
|
|
828
|
+
- Maintain the same code quality standards as the parent session (strict types, no fallbacks, proper error handling).`;
|
|
829
|
+
var EXPLORE_SYSTEM_PROMPT = `You are a codebase exploration and analysis agent. Your purpose is to search, read, and understand code without making any modifications.
|
|
830
|
+
|
|
831
|
+
You operate in read-only mode. You must NEVER attempt to write or edit files. Your tools are restricted to read-only operations: reading files, searching with grep and glob, and running non-destructive bash commands.
|
|
832
|
+
|
|
833
|
+
Your role is to answer questions about the codebase by:
|
|
834
|
+
|
|
835
|
+
- Searching for relevant files, symbols, and patterns using Glob and Grep.
|
|
836
|
+
- Reading source files, configuration, and documentation to understand structure and behavior.
|
|
837
|
+
- Tracing code paths across modules to understand how components interact.
|
|
838
|
+
- Summarizing findings in a clear, structured format with file paths and line references.
|
|
839
|
+
- Identifying architectural patterns, dependencies, and potential issues.
|
|
840
|
+
|
|
841
|
+
When exploring, prefer targeted searches over broad scans. Start with the most likely locations and narrow down. Always include absolute file paths in your responses so the caller can navigate directly to relevant code.`;
|
|
842
|
+
var PLAN_SYSTEM_PROMPT = `You are a planning, research, and architecture agent. Your purpose is to analyze requirements, research approaches, and produce structured plans without making any code modifications.
|
|
843
|
+
|
|
844
|
+
You operate in read-only mode. You must NEVER attempt to write or edit files. Your tools are restricted to read-only operations.
|
|
845
|
+
|
|
846
|
+
Your role is to:
|
|
847
|
+
|
|
848
|
+
- Analyze the current codebase state relevant to the task by reading specs, source code, and tests.
|
|
849
|
+
- Research implementation approaches by examining existing patterns and architectural conventions in the repository.
|
|
850
|
+
- Identify affected files, modules, and interfaces that a proposed change would touch.
|
|
851
|
+
- Assess risks, dependencies, and potential breaking changes.
|
|
852
|
+
- Produce a structured implementation plan with clear steps, file lists, and ordering.
|
|
853
|
+
- Consider edge cases, error handling, and test coverage requirements.
|
|
854
|
+
|
|
855
|
+
Output your plan in a structured format with numbered steps. For each step, specify which files are involved and what changes are needed. Flag any decisions that require human judgment or clarification.`;
|
|
856
|
+
var BUILT_IN_AGENTS = [
|
|
857
|
+
{
|
|
858
|
+
name: "general-purpose",
|
|
859
|
+
description: "General-purpose task execution agent with full tool access.",
|
|
860
|
+
systemPrompt: GENERAL_PURPOSE_SYSTEM_PROMPT
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
name: "Explore",
|
|
864
|
+
description: "Read-only codebase exploration and analysis agent.",
|
|
865
|
+
systemPrompt: EXPLORE_SYSTEM_PROMPT,
|
|
866
|
+
disallowedTools: ["Write", "Edit"]
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
name: "Plan",
|
|
870
|
+
description: "Read-only planning, research, and architecture agent.",
|
|
871
|
+
systemPrompt: PLAN_SYSTEM_PROMPT,
|
|
872
|
+
disallowedTools: ["Write", "Edit"]
|
|
873
|
+
}
|
|
874
|
+
];
|
|
875
|
+
function getBuiltInAgent(name) {
|
|
876
|
+
return BUILT_IN_AGENTS.find((agent) => agent.name === name);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// src/tools/agent-tool.ts
|
|
880
|
+
import { z } from "zod";
|
|
881
|
+
import { createZodFunctionTool } from "@robota-sdk/agent-tools";
|
|
882
|
+
import { SubagentManager } from "@robota-sdk/agent-runtime";
|
|
883
|
+
|
|
884
|
+
// src/subagents/in-process-subagent-runner.ts
|
|
885
|
+
function resolveAgentDefinition(agentType, customRegistry) {
|
|
886
|
+
const definition = customRegistry?.(agentType) ?? getBuiltInAgent(agentType);
|
|
887
|
+
if (!definition) {
|
|
888
|
+
throw new Error(`Unknown agent type: ${agentType}`);
|
|
889
|
+
}
|
|
890
|
+
return definition;
|
|
891
|
+
}
|
|
892
|
+
function applyRequestOverrides(definition, job) {
|
|
893
|
+
return {
|
|
894
|
+
...definition,
|
|
895
|
+
...job.request.model ? { model: job.request.model } : {},
|
|
896
|
+
...job.request.allowedTools ? { tools: job.request.allowedTools } : {},
|
|
897
|
+
...job.request.disallowedTools ? { disallowedTools: job.request.disallowedTools } : {}
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function extractFirstArg(toolArgs) {
|
|
901
|
+
if (!toolArgs) return void 0;
|
|
902
|
+
const firstValue = Object.values(toolArgs)[0];
|
|
903
|
+
if (firstValue === void 0) return void 0;
|
|
904
|
+
return typeof firstValue === "object" ? JSON.stringify(firstValue) : String(firstValue);
|
|
905
|
+
}
|
|
906
|
+
function assertSupportedIsolation(job) {
|
|
907
|
+
if (job.request.isolation === "worktree") {
|
|
908
|
+
throw new Error("Worktree isolation requires a runtime shell subagent runner");
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
function emitToolExecutionEvent(job, event) {
|
|
912
|
+
if (event.type === "start") {
|
|
913
|
+
job.emit?.({
|
|
914
|
+
type: "background_task_tool_start",
|
|
915
|
+
toolName: event.toolName,
|
|
916
|
+
firstArg: extractFirstArg(event.toolArgs)
|
|
917
|
+
});
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
job.emit?.({
|
|
921
|
+
type: "background_task_tool_end",
|
|
922
|
+
toolName: event.toolName,
|
|
923
|
+
success: event.success ?? true
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
function createInProcessSubagentRunner(deps) {
|
|
927
|
+
return {
|
|
928
|
+
start(job) {
|
|
929
|
+
assertSupportedIsolation(job);
|
|
930
|
+
const definition = resolveAgentDefinition(job.request.type, deps.customAgentRegistry);
|
|
931
|
+
const session = createSubagentSession({
|
|
932
|
+
agentDefinition: applyRequestOverrides(definition, job),
|
|
933
|
+
parentConfig: deps.config,
|
|
934
|
+
parentContext: deps.context,
|
|
935
|
+
parentTools: deps.tools,
|
|
936
|
+
provider: deps.provider,
|
|
937
|
+
terminal: deps.terminal,
|
|
938
|
+
permissionMode: deps.permissionMode,
|
|
939
|
+
permissionHandler: deps.permissionHandler,
|
|
940
|
+
hooks: deps.hooks,
|
|
941
|
+
hookTypeExecutors: deps.hookTypeExecutors,
|
|
942
|
+
onTextDelta: (delta) => {
|
|
943
|
+
job.emit?.({ type: "background_task_text_delta", delta });
|
|
944
|
+
deps.onTextDelta?.(delta);
|
|
945
|
+
},
|
|
946
|
+
onToolExecution: (event) => {
|
|
947
|
+
emitToolExecutionEvent(job, event);
|
|
948
|
+
deps.onToolExecution?.(event);
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
return {
|
|
952
|
+
jobId: job.jobId,
|
|
953
|
+
result: session.run(job.request.prompt).then((output) => ({
|
|
954
|
+
jobId: job.jobId,
|
|
955
|
+
output
|
|
956
|
+
})),
|
|
957
|
+
cancel: () => {
|
|
958
|
+
session.abort();
|
|
959
|
+
return Promise.resolve();
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// src/tools/agent-tool.ts
|
|
967
|
+
var AGENT_TOOL_DESCRIPTION = [
|
|
968
|
+
"Creates one subagent job for delegated work in an isolated context.",
|
|
969
|
+
"One tool call creates one subagent job.",
|
|
970
|
+
"When the user explicitly asks to create, run, spawn, delegate to, or use agents/subagents, start the requested subagent job immediately.",
|
|
971
|
+
"Do not ask a follow-up question unless execution is impossible or unsafe.",
|
|
972
|
+
"For multiple or parallel agents, create one Agent tool call per requested role in the current turn.",
|
|
973
|
+
"Subagent jobs run as background tasks by default.",
|
|
974
|
+
"The tool waits for a terminal result and returns completed, failed, or timed-out outcome data to the parent conversation.",
|
|
975
|
+
"Execution is represented by a real tool call and runtime background task event."
|
|
976
|
+
].join(" ");
|
|
977
|
+
function createAgentToolPromptDescription(agentDefinitions = []) {
|
|
978
|
+
const availableAgents = agentDefinitions.length > 0 ? ` Available agent types: ${agentDefinitions.map((agent) => `${agent.name} (${agent.description})`).join(", ")}.` : "";
|
|
979
|
+
return [
|
|
980
|
+
"Agent \u2014 creates one isolated subagent job.",
|
|
981
|
+
"One Agent tool call corresponds to one subagent job.",
|
|
982
|
+
"When the user explicitly asks to create, run, spawn, delegate to, or use agents/subagents, start the requested subagent job immediately.",
|
|
983
|
+
"Do not ask a follow-up question unless execution is impossible or unsafe.",
|
|
984
|
+
"For multiple or parallel agents, create one Agent tool call per requested role in the current turn.",
|
|
985
|
+
"The tool returns terminal result data.",
|
|
986
|
+
"Runtime mode is background.",
|
|
987
|
+
availableAgents
|
|
988
|
+
].join(" ").replace(/\s+/g, " ").trim();
|
|
989
|
+
}
|
|
990
|
+
function asZodSchema(schema) {
|
|
991
|
+
return schema;
|
|
992
|
+
}
|
|
993
|
+
var AgentSchema = z.object({
|
|
994
|
+
prompt: z.string().describe("The task for the subagent to perform"),
|
|
995
|
+
subagent_type: z.string().optional().describe('Agent type: "general-purpose", "Explore", "Plan", or a custom agent name'),
|
|
996
|
+
model: z.string().optional().describe("Optional model override"),
|
|
997
|
+
isolation: z.enum(["none", "worktree"]).optional().describe('Optional runtime isolation mode. "worktree" runs in a Git worktree.')
|
|
998
|
+
}).passthrough();
|
|
999
|
+
var sessionDepsStore = /* @__PURE__ */ new WeakMap();
|
|
1000
|
+
function storeAgentToolDeps(key, deps) {
|
|
1001
|
+
sessionDepsStore.set(key, deps);
|
|
1002
|
+
}
|
|
1003
|
+
function retrieveAgentToolDeps(key) {
|
|
1004
|
+
return sessionDepsStore.get(key);
|
|
1005
|
+
}
|
|
1006
|
+
function resolveAgentDefinition2(agentType, customRegistry) {
|
|
1007
|
+
if (customRegistry) {
|
|
1008
|
+
const custom = customRegistry(agentType);
|
|
1009
|
+
if (custom) return custom;
|
|
1010
|
+
}
|
|
1011
|
+
const builtIn = getBuiltInAgent(agentType);
|
|
1012
|
+
if (builtIn) return builtIn;
|
|
1013
|
+
return void 0;
|
|
1014
|
+
}
|
|
1015
|
+
function createSubagentManager(deps) {
|
|
1016
|
+
return deps.subagentManager ?? new SubagentManager({
|
|
1017
|
+
runner: createInProcessSubagentRunner(deps)
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
function createSpawnRequest(args, agentType, agentDef, deps) {
|
|
1021
|
+
return {
|
|
1022
|
+
type: agentType,
|
|
1023
|
+
label: agentDef.name,
|
|
1024
|
+
parentSessionId: deps.parentSessionId ?? "unknown-session",
|
|
1025
|
+
mode: "background",
|
|
1026
|
+
depth: deps.subagentDepth ?? 1,
|
|
1027
|
+
cwd: deps.cwd ?? process.cwd(),
|
|
1028
|
+
prompt: args.prompt,
|
|
1029
|
+
model: args.model,
|
|
1030
|
+
isolation: args.isolation
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
function stringifyUnknownAgentType(agentType) {
|
|
1034
|
+
return JSON.stringify({
|
|
1035
|
+
success: false,
|
|
1036
|
+
output: "",
|
|
1037
|
+
error: `Unknown agent type: ${agentType}`
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
function stringifyAgentSuccess(result) {
|
|
1041
|
+
const worktreePath = result.metadata?.["worktreePath"];
|
|
1042
|
+
const branchName = result.metadata?.["branchName"];
|
|
1043
|
+
return JSON.stringify({
|
|
1044
|
+
success: true,
|
|
1045
|
+
output: result.output,
|
|
1046
|
+
agentId: result.jobId,
|
|
1047
|
+
metadata: result.metadata,
|
|
1048
|
+
...typeof worktreePath === "string" ? { worktreePath } : {},
|
|
1049
|
+
...typeof branchName === "string" ? { branchName } : {}
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
function stringifyAgentError(message, agentId) {
|
|
1053
|
+
return JSON.stringify({
|
|
1054
|
+
success: false,
|
|
1055
|
+
output: "",
|
|
1056
|
+
error: `Sub-agent error: ${message}`,
|
|
1057
|
+
agentId
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
async function runManagedAgent(args, deps, manager) {
|
|
1061
|
+
const agentType = args.subagent_type ?? "general-purpose";
|
|
1062
|
+
const agentDef = resolveAgentDefinition2(agentType, deps.customAgentRegistry);
|
|
1063
|
+
if (!agentDef) {
|
|
1064
|
+
return stringifyUnknownAgentType(agentType);
|
|
1065
|
+
}
|
|
1066
|
+
let agentId;
|
|
1067
|
+
try {
|
|
1068
|
+
const state = await manager.spawn(createSpawnRequest(args, agentType, agentDef, deps));
|
|
1069
|
+
agentId = state.id;
|
|
1070
|
+
const response = await manager.wait(state.id);
|
|
1071
|
+
return stringifyAgentSuccess(response);
|
|
1072
|
+
} catch (err) {
|
|
1073
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1074
|
+
return stringifyAgentError(message, agentId);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
function createAgentTool(deps) {
|
|
1078
|
+
const manager = createSubagentManager(deps);
|
|
1079
|
+
return createZodFunctionTool(
|
|
1080
|
+
"Agent",
|
|
1081
|
+
AGENT_TOOL_DESCRIPTION,
|
|
1082
|
+
asZodSchema(AgentSchema),
|
|
1083
|
+
async (params) => {
|
|
1084
|
+
const args = params;
|
|
1085
|
+
return runManagedAgent(
|
|
1086
|
+
{
|
|
1087
|
+
prompt: args.prompt,
|
|
1088
|
+
...args.subagent_type !== void 0 ? { subagent_type: args.subagent_type } : {},
|
|
1089
|
+
...args.model !== void 0 ? { model: args.model } : {},
|
|
1090
|
+
...args.isolation !== void 0 ? { isolation: args.isolation } : {}
|
|
1091
|
+
},
|
|
1092
|
+
deps,
|
|
1093
|
+
manager
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// src/background-tasks/index.ts
|
|
1100
|
+
import { BackgroundTaskManager } from "@robota-sdk/agent-runtime";
|
|
1101
|
+
|
|
1102
|
+
// src/background-tasks/background-job-orchestrator.ts
|
|
1103
|
+
import {
|
|
1104
|
+
isTerminalBackgroundTaskStatus
|
|
1105
|
+
} from "@robota-sdk/agent-runtime";
|
|
1106
|
+
var DEFAULT_SUMMARY_LENGTH = 1e3;
|
|
1107
|
+
var BackgroundJobOrchestrator = class {
|
|
1108
|
+
manager;
|
|
1109
|
+
now;
|
|
1110
|
+
idFactory;
|
|
1111
|
+
unsubscribeManager;
|
|
1112
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1113
|
+
groups = /* @__PURE__ */ new Map();
|
|
1114
|
+
sequence = 0;
|
|
1115
|
+
constructor(options) {
|
|
1116
|
+
this.manager = options.manager;
|
|
1117
|
+
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
1118
|
+
this.idFactory = options.idFactory ?? (() => this.nextGroupId());
|
|
1119
|
+
this.sequence = options.initialGroups?.length ?? 0;
|
|
1120
|
+
for (const group of options.initialGroups ?? []) this.restoreGroup(group);
|
|
1121
|
+
this.unsubscribeManager = this.manager.subscribe((event) => this.handleTaskEvent(event));
|
|
1122
|
+
}
|
|
1123
|
+
createGroup(request) {
|
|
1124
|
+
const now = this.now();
|
|
1125
|
+
const state = {
|
|
1126
|
+
id: this.idFactory(request),
|
|
1127
|
+
parentSessionId: request.parentSessionId,
|
|
1128
|
+
waitPolicy: request.waitPolicy,
|
|
1129
|
+
taskIds: [...request.taskIds],
|
|
1130
|
+
status: "running",
|
|
1131
|
+
createdAt: now,
|
|
1132
|
+
updatedAt: now,
|
|
1133
|
+
results: [],
|
|
1134
|
+
...request.label ? { label: request.label } : {}
|
|
1135
|
+
};
|
|
1136
|
+
const record = this.createRecord(state);
|
|
1137
|
+
this.groups.set(state.id, record);
|
|
1138
|
+
this.captureExistingTerminalTasks(record);
|
|
1139
|
+
this.emit({ type: "background_job_group_created", group: cloneGroup(record.state) });
|
|
1140
|
+
this.evaluateCompletion(record);
|
|
1141
|
+
return cloneGroup(record.state);
|
|
1142
|
+
}
|
|
1143
|
+
listGroups() {
|
|
1144
|
+
return [...this.groups.values()].map((record) => cloneGroup(record.state));
|
|
1145
|
+
}
|
|
1146
|
+
getGroup(groupId) {
|
|
1147
|
+
const record = this.groups.get(groupId);
|
|
1148
|
+
return record ? cloneGroup(record.state) : void 0;
|
|
1149
|
+
}
|
|
1150
|
+
waitGroup(groupId) {
|
|
1151
|
+
const record = this.groups.get(groupId);
|
|
1152
|
+
if (!record) return Promise.reject(new Error(`Unknown background job group: ${groupId}`));
|
|
1153
|
+
return record.completion;
|
|
1154
|
+
}
|
|
1155
|
+
subscribe(listener) {
|
|
1156
|
+
this.listeners.add(listener);
|
|
1157
|
+
return () => {
|
|
1158
|
+
this.listeners.delete(listener);
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
dispose() {
|
|
1162
|
+
this.unsubscribeManager();
|
|
1163
|
+
this.listeners.clear();
|
|
1164
|
+
}
|
|
1165
|
+
nextGroupId() {
|
|
1166
|
+
this.sequence += 1;
|
|
1167
|
+
return `group_${this.sequence}`;
|
|
1168
|
+
}
|
|
1169
|
+
restoreGroup(group) {
|
|
1170
|
+
const record = this.createRecord(cloneGroup(group));
|
|
1171
|
+
this.groups.set(group.id, record);
|
|
1172
|
+
if (group.status === "completed") record.resolve(cloneGroup(group));
|
|
1173
|
+
}
|
|
1174
|
+
createRecord(state) {
|
|
1175
|
+
let resolveGroup = () => {
|
|
1176
|
+
};
|
|
1177
|
+
const completion = new Promise((resolve2) => {
|
|
1178
|
+
resolveGroup = resolve2;
|
|
1179
|
+
});
|
|
1180
|
+
return { state, completion, resolve: resolveGroup };
|
|
1181
|
+
}
|
|
1182
|
+
captureExistingTerminalTasks(record) {
|
|
1183
|
+
for (const taskId of record.state.taskIds) {
|
|
1184
|
+
const task = this.manager.get(taskId);
|
|
1185
|
+
if (task && isTerminalBackgroundTaskStatus(task.status)) this.captureTask(record, task);
|
|
211
1186
|
}
|
|
212
1187
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
1188
|
+
handleTaskEvent(event) {
|
|
1189
|
+
const task = getTerminalTask(event);
|
|
1190
|
+
if (!task) return;
|
|
1191
|
+
for (const record of this.groups.values()) {
|
|
1192
|
+
if (!record.state.taskIds.includes(task.id)) continue;
|
|
1193
|
+
if (!this.captureTask(record, task)) continue;
|
|
1194
|
+
if (record.state.status === "running") this.evaluateCompletion(record);
|
|
1195
|
+
else this.emit({ type: "background_job_group_updated", group: cloneGroup(record.state) });
|
|
1196
|
+
}
|
|
216
1197
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return
|
|
1198
|
+
captureTask(record, task) {
|
|
1199
|
+
if (record.state.results.some((result) => result.taskId === task.id)) return false;
|
|
1200
|
+
record.state.results = [...record.state.results, createResultEnvelope(task)];
|
|
1201
|
+
record.state.updatedAt = this.now();
|
|
1202
|
+
return true;
|
|
222
1203
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
1204
|
+
evaluateCompletion(record) {
|
|
1205
|
+
if (record.state.status === "completed") return;
|
|
1206
|
+
if (!shouldComplete(record.state)) {
|
|
1207
|
+
this.emit({ type: "background_job_group_updated", group: cloneGroup(record.state) });
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const now = this.now();
|
|
1211
|
+
record.state.status = "completed";
|
|
1212
|
+
record.state.completedAt = now;
|
|
1213
|
+
record.state.updatedAt = now;
|
|
1214
|
+
const completed = cloneGroup(record.state);
|
|
1215
|
+
record.resolve(completed);
|
|
1216
|
+
this.emit({ type: "background_job_group_completed", group: completed });
|
|
226
1217
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return this.commands.has(name);
|
|
1218
|
+
emit(event) {
|
|
1219
|
+
for (const listener of this.listeners) listener(event);
|
|
230
1220
|
}
|
|
231
1221
|
};
|
|
1222
|
+
function getTerminalTask(event) {
|
|
1223
|
+
if (event.type === "background_task_completed" || event.type === "background_task_failed" || event.type === "background_task_cancelled") {
|
|
1224
|
+
return event.task;
|
|
1225
|
+
}
|
|
1226
|
+
return void 0;
|
|
1227
|
+
}
|
|
1228
|
+
function shouldComplete(group) {
|
|
1229
|
+
if (group.waitPolicy === "manual") return false;
|
|
1230
|
+
if (group.waitPolicy === "wait_any") return group.results.length > 0;
|
|
1231
|
+
return group.taskIds.every((taskId) => group.results.some((result) => result.taskId === taskId));
|
|
1232
|
+
}
|
|
1233
|
+
function createResultEnvelope(task) {
|
|
1234
|
+
return {
|
|
1235
|
+
taskId: task.id,
|
|
1236
|
+
label: task.label,
|
|
1237
|
+
status: task.status,
|
|
1238
|
+
...task.result?.output ? { summary: summarizeOutput(task.result.output) } : {},
|
|
1239
|
+
...task.transcriptPath || task.logPath ? { outputRef: task.transcriptPath ?? task.logPath } : {},
|
|
1240
|
+
...task.error ? { error: { ...task.error } } : {},
|
|
1241
|
+
...task.startedAt ? { startedAt: task.startedAt } : {},
|
|
1242
|
+
...task.completedAt ? { completedAt: task.completedAt } : {}
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
function summarizeOutput(output) {
|
|
1246
|
+
const trimmed = output.trim();
|
|
1247
|
+
if (trimmed.length <= DEFAULT_SUMMARY_LENGTH) return trimmed;
|
|
1248
|
+
return `${trimmed.slice(0, DEFAULT_SUMMARY_LENGTH)}...`;
|
|
1249
|
+
}
|
|
1250
|
+
function summarizeBackgroundJobGroup(group) {
|
|
1251
|
+
const completed = countResults(group, "completed");
|
|
1252
|
+
const failed = countResults(group, "failed");
|
|
1253
|
+
const cancelled = countResults(group, "cancelled");
|
|
1254
|
+
return {
|
|
1255
|
+
groupId: group.id,
|
|
1256
|
+
status: group.status,
|
|
1257
|
+
total: group.taskIds.length,
|
|
1258
|
+
completed,
|
|
1259
|
+
failed,
|
|
1260
|
+
cancelled,
|
|
1261
|
+
pending: Math.max(group.taskIds.length - group.results.length, 0),
|
|
1262
|
+
lines: group.results.map((result) => formatResultLine(result))
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
function countResults(group, status) {
|
|
1266
|
+
return group.results.filter((result) => result.status === status).length;
|
|
1267
|
+
}
|
|
1268
|
+
function formatResultLine(result) {
|
|
1269
|
+
const detail = normalizeResultDetail(result);
|
|
1270
|
+
const output = result.outputRef && result.summary ? ` (output: ${result.outputRef})` : "";
|
|
1271
|
+
return `[${result.status}] ${result.label} ${result.taskId}: ${detail}${output}`;
|
|
1272
|
+
}
|
|
1273
|
+
function normalizeResultDetail(result) {
|
|
1274
|
+
const detail = result.error?.message ?? result.summary ?? "";
|
|
1275
|
+
const normalized = detail.replace(/\s+/g, " ").trim();
|
|
1276
|
+
return normalized.length > 0 ? normalized : "(no summary)";
|
|
1277
|
+
}
|
|
1278
|
+
function cloneGroup(group) {
|
|
1279
|
+
return {
|
|
1280
|
+
...group,
|
|
1281
|
+
taskIds: [...group.taskIds],
|
|
1282
|
+
results: group.results.map((result) => ({
|
|
1283
|
+
...result,
|
|
1284
|
+
...result.error ? { error: { ...result.error } } : {}
|
|
1285
|
+
}))
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// src/background-tasks/index.ts
|
|
1290
|
+
import {
|
|
1291
|
+
getBackgroundTaskTransitions,
|
|
1292
|
+
isTerminalBackgroundTaskStatus as isTerminalBackgroundTaskStatus2,
|
|
1293
|
+
transitionBackgroundTaskStatus
|
|
1294
|
+
} from "@robota-sdk/agent-runtime";
|
|
1295
|
+
import { BackgroundTaskError } from "@robota-sdk/agent-runtime";
|
|
1296
|
+
|
|
1297
|
+
// src/background-tasks/session-background-store.ts
|
|
1298
|
+
var sessionBackgroundTaskManagers = /* @__PURE__ */ new WeakMap();
|
|
1299
|
+
function storeSessionBackgroundTaskManager(key, manager) {
|
|
1300
|
+
sessionBackgroundTaskManagers.set(key, manager);
|
|
1301
|
+
}
|
|
1302
|
+
function retrieveSessionBackgroundTaskManager(key) {
|
|
1303
|
+
return sessionBackgroundTaskManagers.get(key);
|
|
1304
|
+
}
|
|
232
1305
|
|
|
233
1306
|
// src/interactive/interactive-session-execution.ts
|
|
234
1307
|
function isAbortError(err) {
|
|
@@ -269,7 +1342,7 @@ function buildInterruptedResult(sessionHistory, interactiveHistory, historyBefor
|
|
|
269
1342
|
contextState
|
|
270
1343
|
};
|
|
271
1344
|
}
|
|
272
|
-
function persistSession(sessionStore, session, sessionName, cwd, history) {
|
|
1345
|
+
function persistSession(sessionStore, session, sessionName, cwd, history, backgroundState) {
|
|
273
1346
|
try {
|
|
274
1347
|
const sessionId = session.getSessionId();
|
|
275
1348
|
const existing = sessionStore.load(sessionId);
|
|
@@ -280,7 +1353,15 @@ function persistSession(sessionStore, session, sessionName, cwd, history) {
|
|
|
280
1353
|
createdAt: existing?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
281
1354
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
282
1355
|
messages: session.getHistory(),
|
|
283
|
-
history
|
|
1356
|
+
history,
|
|
1357
|
+
systemPrompt: session.getSystemMessage(),
|
|
1358
|
+
toolSchemas: session.getToolSchemas(),
|
|
1359
|
+
...backgroundState ? {
|
|
1360
|
+
backgroundTasks: [...backgroundState.tasks],
|
|
1361
|
+
backgroundTaskEvents: [...backgroundState.events],
|
|
1362
|
+
backgroundJobGroups: [...backgroundState.groups ?? []],
|
|
1363
|
+
backgroundJobGroupEvents: [...backgroundState.groupEvents ?? []]
|
|
1364
|
+
} : {}
|
|
284
1365
|
});
|
|
285
1366
|
} catch {
|
|
286
1367
|
}
|
|
@@ -307,7 +1388,7 @@ var TOOL_ARG_DISPLAY_MAX = 80;
|
|
|
307
1388
|
var TAIL_KEEP = 30;
|
|
308
1389
|
var MAX_COMPLETED_TOOLS = 50;
|
|
309
1390
|
var STREAMING_FLUSH_INTERVAL_MS = 16;
|
|
310
|
-
function
|
|
1391
|
+
function extractFirstArg2(toolArgs) {
|
|
311
1392
|
if (!toolArgs) return "";
|
|
312
1393
|
const firstVal = Object.values(toolArgs)[0];
|
|
313
1394
|
const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
|
|
@@ -349,7 +1430,7 @@ function trimCompletedTools(activeTools) {
|
|
|
349
1430
|
});
|
|
350
1431
|
}
|
|
351
1432
|
function applyToolStart(state, event) {
|
|
352
|
-
const firstArg =
|
|
1433
|
+
const firstArg = extractFirstArg2(event.toolArgs);
|
|
353
1434
|
const toolState = { toolName: event.toolName, firstArg, isRunning: true };
|
|
354
1435
|
state.activeTools.push(toolState);
|
|
355
1436
|
state.history.push({
|
|
@@ -493,14 +1574,31 @@ Respond with JSON: { "ok": boolean, "reason"?: string }`;
|
|
|
493
1574
|
// src/assembly/create-session.ts
|
|
494
1575
|
import { Session as Session2 } from "@robota-sdk/agent-sessions";
|
|
495
1576
|
|
|
496
|
-
// src/context/system-prompt-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
1577
|
+
// src/context/system-prompt-composer.ts
|
|
1578
|
+
function renderSection(section) {
|
|
1579
|
+
const content = section.content.trim();
|
|
1580
|
+
if (!section.title) return content;
|
|
1581
|
+
return [`## ${section.title}`, content].join("\n");
|
|
1582
|
+
}
|
|
1583
|
+
function composeSystemPrompt(sections) {
|
|
1584
|
+
return [...sections].filter((section) => section.content.trim().length > 0).sort((a, b) => a.priority - b.priority || a.id.localeCompare(b.id)).map((section) => renderSection(section)).join("\n\n");
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// src/context/system-prompt-section-providers.ts
|
|
1588
|
+
var TRUST_LEVEL_LABELS = {
|
|
1589
|
+
safe: "safe",
|
|
1590
|
+
moderate: "moderate",
|
|
1591
|
+
full: "full"
|
|
501
1592
|
};
|
|
502
|
-
function
|
|
503
|
-
|
|
1593
|
+
function createSection(id, title, priority, content, source) {
|
|
1594
|
+
return { id, title, priority, content, source };
|
|
1595
|
+
}
|
|
1596
|
+
function createWorkingDirectorySection(cwd) {
|
|
1597
|
+
if (!cwd) return void 0;
|
|
1598
|
+
return createSection("runtime-cwd", "Working Directory", 30, `\`${cwd}\``, "runtime");
|
|
1599
|
+
}
|
|
1600
|
+
function createProjectSection(info) {
|
|
1601
|
+
const lines = [];
|
|
504
1602
|
if (info.name !== void 0) {
|
|
505
1603
|
lines.push(`- **Name:** ${info.name}`);
|
|
506
1604
|
}
|
|
@@ -513,79 +1611,114 @@ function buildProjectSection(info) {
|
|
|
513
1611
|
if (info.packageManager !== void 0) {
|
|
514
1612
|
lines.push(`- **Package manager:** ${info.packageManager}`);
|
|
515
1613
|
}
|
|
516
|
-
return lines.join("\n");
|
|
1614
|
+
return createSection("runtime-project", "Current Project", 40, lines.join("\n"), "runtime");
|
|
517
1615
|
}
|
|
518
|
-
function
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
1616
|
+
function createPermissionSection(trustLevel) {
|
|
1617
|
+
return createSection(
|
|
1618
|
+
"permission-mode",
|
|
1619
|
+
"Permission Mode",
|
|
1620
|
+
50,
|
|
1621
|
+
`- **Trust level:** ${TRUST_LEVEL_LABELS[trustLevel]}`,
|
|
1622
|
+
"permissions"
|
|
1623
|
+
);
|
|
524
1624
|
}
|
|
525
|
-
function
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
"
|
|
533
|
-
"",
|
|
534
|
-
|
|
1625
|
+
function createResponseLanguageSection(language) {
|
|
1626
|
+
if (language === void 0 || language.trim().length === 0) return void 0;
|
|
1627
|
+
return createSection("runtime-response-language", "Response Language", 45, language, "runtime");
|
|
1628
|
+
}
|
|
1629
|
+
function createAgentsMdSection(agentsMd) {
|
|
1630
|
+
if (agentsMd.trim().length === 0) return void 0;
|
|
1631
|
+
return createSection(
|
|
1632
|
+
"project-agents-md",
|
|
1633
|
+
"Agent Instructions",
|
|
1634
|
+
10,
|
|
1635
|
+
agentsMd,
|
|
1636
|
+
"project-instructions"
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1639
|
+
function createClaudeMdSection(claudeMd) {
|
|
1640
|
+
if (claudeMd.trim().length === 0) return void 0;
|
|
1641
|
+
return createSection("project-claude-md", "Project Notes", 20, claudeMd, "project-instructions");
|
|
1642
|
+
}
|
|
1643
|
+
function createToolDescriptionSection(descriptions) {
|
|
1644
|
+
if (descriptions.length === 0) return void 0;
|
|
1645
|
+
return createSection(
|
|
1646
|
+
"tool-descriptions",
|
|
1647
|
+
"Available Tools",
|
|
1648
|
+
60,
|
|
1649
|
+
descriptions.map((description) => `- ${description}`).join("\n"),
|
|
1650
|
+
"tool"
|
|
1651
|
+
);
|
|
1652
|
+
}
|
|
1653
|
+
function formatCapability(descriptor) {
|
|
1654
|
+
const arg = descriptor.argumentHint ? ` ${descriptor.argumentHint}` : "";
|
|
1655
|
+
return `- ${descriptor.name}${arg}: ${descriptor.description}`;
|
|
1656
|
+
}
|
|
1657
|
+
function createCapabilityKindSection(kind, title, priority, source, descriptors) {
|
|
1658
|
+
const content = descriptors.filter((descriptor) => descriptor.modelInvocable && descriptor.kind === kind).map(formatCapability).join("\n");
|
|
1659
|
+
if (content.trim().length === 0) return void 0;
|
|
1660
|
+
return createSection(`capability-${kind}`, title, priority, content, source);
|
|
1661
|
+
}
|
|
1662
|
+
function createCapabilitySections(descriptors) {
|
|
1663
|
+
const sections = [];
|
|
1664
|
+
const commandSection = createCapabilityKindSection(
|
|
1665
|
+
"builtin-command",
|
|
1666
|
+
"Built-in Commands",
|
|
1667
|
+
70,
|
|
1668
|
+
"command",
|
|
1669
|
+
descriptors
|
|
1670
|
+
);
|
|
1671
|
+
const skillSection = createCapabilityKindSection("skill", "Skills", 80, "skill", descriptors);
|
|
1672
|
+
const agentSection = createCapabilityKindSection("agent", "Agents", 90, "agent", descriptors);
|
|
1673
|
+
const toolSection = createCapabilityKindSection("tool", "Tools", 100, "tool", descriptors);
|
|
1674
|
+
if (commandSection) sections.push(commandSection);
|
|
1675
|
+
if (skillSection) sections.push(skillSection);
|
|
1676
|
+
if (agentSection) sections.push(agentSection);
|
|
1677
|
+
if (toolSection) sections.push(toolSection);
|
|
1678
|
+
return sections;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// src/context/system-prompt-builder.ts
|
|
1682
|
+
function appendOptionalSection(sections, section) {
|
|
1683
|
+
if (section !== void 0) sections.push(section);
|
|
1684
|
+
}
|
|
1685
|
+
function mapSkillDescriptors(skills) {
|
|
1686
|
+
return skills.map((skill) => ({
|
|
1687
|
+
name: skill.name,
|
|
1688
|
+
kind: "skill",
|
|
1689
|
+
description: skill.description,
|
|
1690
|
+
userInvocable: true,
|
|
1691
|
+
modelInvocable: skill.disableModelInvocation !== true
|
|
1692
|
+
}));
|
|
1693
|
+
}
|
|
1694
|
+
function mapAgentDescriptors(agents) {
|
|
1695
|
+
return agents.map((agent) => ({
|
|
1696
|
+
name: agent.name,
|
|
1697
|
+
kind: "agent",
|
|
1698
|
+
description: agent.description,
|
|
1699
|
+
userInvocable: false,
|
|
1700
|
+
modelInvocable: true,
|
|
1701
|
+
safety: "background-agent"
|
|
1702
|
+
}));
|
|
1703
|
+
}
|
|
1704
|
+
function buildCapabilityDescriptors(params) {
|
|
1705
|
+
return [
|
|
1706
|
+
...params.commandDescriptors ?? [],
|
|
1707
|
+
...params.skills ? mapSkillDescriptors(params.skills) : [],
|
|
1708
|
+
...params.agents ? mapAgentDescriptors(params.agents) : []
|
|
535
1709
|
];
|
|
536
|
-
return lines.join("\n");
|
|
537
1710
|
}
|
|
538
1711
|
function buildSystemPrompt(params) {
|
|
539
|
-
const { agentsMd, claudeMd, toolDescriptions, trustLevel, projectInfo, cwd, language } = params;
|
|
540
1712
|
const sections = [];
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
);
|
|
551
|
-
}
|
|
552
|
-
sections.push(roleLines.join("\n"));
|
|
553
|
-
if (cwd) {
|
|
554
|
-
sections.push(`## Working Directory
|
|
555
|
-
\`${cwd}\``);
|
|
556
|
-
}
|
|
557
|
-
sections.push(buildProjectSection(projectInfo));
|
|
558
|
-
sections.push(
|
|
559
|
-
[
|
|
560
|
-
"## Permission Mode",
|
|
561
|
-
`Your current trust level is **${TRUST_LEVEL_DESCRIPTIONS[trustLevel]}**.`
|
|
562
|
-
].join("\n")
|
|
563
|
-
);
|
|
564
|
-
if (agentsMd.trim().length > 0) {
|
|
565
|
-
sections.push(["## Agent Instructions", agentsMd].join("\n"));
|
|
566
|
-
}
|
|
567
|
-
if (claudeMd.trim().length > 0) {
|
|
568
|
-
sections.push(["## Project Notes", claudeMd].join("\n"));
|
|
569
|
-
}
|
|
570
|
-
sections.push(
|
|
571
|
-
[
|
|
572
|
-
"## Web Search",
|
|
573
|
-
"You have access to web search. When the user asks to search, look up, or find current/latest information,",
|
|
574
|
-
"you MUST use the web_search tool. Do NOT answer from training data when the user explicitly asks to search.",
|
|
575
|
-
"Always prefer web search for: news, latest versions, current events, live documentation."
|
|
576
|
-
].join("\n")
|
|
577
|
-
);
|
|
578
|
-
const toolsSection = buildToolsSection(toolDescriptions);
|
|
579
|
-
if (toolsSection.length > 0) {
|
|
580
|
-
sections.push(toolsSection);
|
|
581
|
-
}
|
|
582
|
-
if (params.skills !== void 0 && params.skills.length > 0) {
|
|
583
|
-
const skillsSection = buildSkillsSection(params.skills);
|
|
584
|
-
if (skillsSection.length > 0) {
|
|
585
|
-
sections.push(skillsSection);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return sections.join("\n\n");
|
|
1713
|
+
appendOptionalSection(sections, createAgentsMdSection(params.agentsMd));
|
|
1714
|
+
appendOptionalSection(sections, createClaudeMdSection(params.claudeMd));
|
|
1715
|
+
appendOptionalSection(sections, createWorkingDirectorySection(params.cwd));
|
|
1716
|
+
sections.push(createProjectSection(params.projectInfo));
|
|
1717
|
+
appendOptionalSection(sections, createResponseLanguageSection(params.language));
|
|
1718
|
+
sections.push(createPermissionSection(params.trustLevel));
|
|
1719
|
+
appendOptionalSection(sections, createToolDescriptionSection(params.toolDescriptions));
|
|
1720
|
+
sections.push(...createCapabilitySections(buildCapabilityDescriptors(params)));
|
|
1721
|
+
return composeSystemPrompt(sections);
|
|
589
1722
|
}
|
|
590
1723
|
|
|
591
1724
|
// src/assembly/create-tools.ts
|
|
@@ -621,240 +1754,128 @@ function createDefaultTools() {
|
|
|
621
1754
|
];
|
|
622
1755
|
}
|
|
623
1756
|
|
|
624
|
-
// src/tools/
|
|
625
|
-
import { z } from "zod";
|
|
626
|
-
import { createZodFunctionTool } from "@robota-sdk/agent-tools";
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
Your role is to complete the assigned task thoroughly and accurately. Follow these guidelines:
|
|
632
|
-
|
|
633
|
-
- Execute the task as described in the prompt. Do not expand scope beyond what is requested.
|
|
634
|
-
- Use the most appropriate tools for each step. Prefer precise tools (Read, Grep, Glob) over broad ones (Bash) when possible.
|
|
635
|
-
- Report your findings clearly and concisely when the task is complete.
|
|
636
|
-
- If a task cannot be completed, explain why and what information is missing.
|
|
637
|
-
- Maintain the same code quality standards as the parent session (strict types, no fallbacks, proper error handling).`;
|
|
638
|
-
var EXPLORE_SYSTEM_PROMPT = `You are a codebase exploration and analysis agent. Your purpose is to search, read, and understand code without making any modifications.
|
|
639
|
-
|
|
640
|
-
You operate in read-only mode. You must NEVER attempt to write or edit files. Your tools are restricted to read-only operations: reading files, searching with grep and glob, and running non-destructive bash commands.
|
|
641
|
-
|
|
642
|
-
Your role is to answer questions about the codebase by:
|
|
643
|
-
|
|
644
|
-
- Searching for relevant files, symbols, and patterns using Glob and Grep.
|
|
645
|
-
- Reading source files, configuration, and documentation to understand structure and behavior.
|
|
646
|
-
- Tracing code paths across modules to understand how components interact.
|
|
647
|
-
- Summarizing findings in a clear, structured format with file paths and line references.
|
|
648
|
-
- Identifying architectural patterns, dependencies, and potential issues.
|
|
649
|
-
|
|
650
|
-
When exploring, prefer targeted searches over broad scans. Start with the most likely locations and narrow down. Always include absolute file paths in your responses so the caller can navigate directly to relevant code.`;
|
|
651
|
-
var PLAN_SYSTEM_PROMPT = `You are a planning, research, and architecture agent. Your purpose is to analyze requirements, research approaches, and produce structured plans without making any code modifications.
|
|
652
|
-
|
|
653
|
-
You operate in read-only mode. You must NEVER attempt to write or edit files. Your tools are restricted to read-only operations.
|
|
654
|
-
|
|
655
|
-
Your role is to:
|
|
656
|
-
|
|
657
|
-
- Analyze the current codebase state relevant to the task by reading specs, source code, and tests.
|
|
658
|
-
- Research implementation approaches by examining existing patterns and architectural conventions in the repository.
|
|
659
|
-
- Identify affected files, modules, and interfaces that a proposed change would touch.
|
|
660
|
-
- Assess risks, dependencies, and potential breaking changes.
|
|
661
|
-
- Produce a structured implementation plan with clear steps, file lists, and ordering.
|
|
662
|
-
- Consider edge cases, error handling, and test coverage requirements.
|
|
663
|
-
|
|
664
|
-
Output your plan in a structured format with numbered steps. For each step, specify which files are involved and what changes are needed. Flag any decisions that require human judgment or clarification.`;
|
|
665
|
-
var BUILT_IN_AGENTS = [
|
|
666
|
-
{
|
|
667
|
-
name: "general-purpose",
|
|
668
|
-
description: "General-purpose task execution agent with full tool access.",
|
|
669
|
-
systemPrompt: GENERAL_PURPOSE_SYSTEM_PROMPT
|
|
670
|
-
},
|
|
671
|
-
{
|
|
672
|
-
name: "Explore",
|
|
673
|
-
description: "Read-only codebase exploration and analysis agent.",
|
|
674
|
-
systemPrompt: EXPLORE_SYSTEM_PROMPT,
|
|
675
|
-
model: "claude-haiku-4-5",
|
|
676
|
-
disallowedTools: ["Write", "Edit"]
|
|
677
|
-
},
|
|
678
|
-
{
|
|
679
|
-
name: "Plan",
|
|
680
|
-
description: "Read-only planning, research, and architecture agent.",
|
|
681
|
-
systemPrompt: PLAN_SYSTEM_PROMPT,
|
|
682
|
-
disallowedTools: ["Write", "Edit"]
|
|
683
|
-
}
|
|
684
|
-
];
|
|
685
|
-
function getBuiltInAgent(name) {
|
|
686
|
-
return BUILT_IN_AGENTS.find((agent) => agent.name === name);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// src/assembly/create-subagent-session.ts
|
|
690
|
-
import { Session } from "@robota-sdk/agent-sessions";
|
|
691
|
-
|
|
692
|
-
// src/assembly/subagent-prompts.ts
|
|
693
|
-
function getSubagentSuffix() {
|
|
694
|
-
return `When you complete the task, respond with a concise report covering what was done and any key findings \u2014 the caller will relay this to the user, so it only needs the essentials.
|
|
695
|
-
|
|
696
|
-
In your final response, share file paths (always absolute, never relative) that are relevant to the task. Include code snippets only when the exact text is load-bearing \u2014 do not recap code you merely read.
|
|
697
|
-
|
|
698
|
-
Do not use emojis.`;
|
|
699
|
-
}
|
|
700
|
-
function getForkWorkerSuffix() {
|
|
701
|
-
return `You are a worker subagent executing a specific task. Do NOT spawn sub-agents; execute directly. Keep your report under 500 words. Use this structure:
|
|
702
|
-
- Scope: What was requested
|
|
703
|
-
- Result: What was done
|
|
704
|
-
- Key files: Relevant file paths (absolute)
|
|
705
|
-
- Files changed: List of modifications
|
|
706
|
-
- Issues: Any problems encountered`;
|
|
1757
|
+
// src/tools/background-process-tool.ts
|
|
1758
|
+
import { z as z2 } from "zod";
|
|
1759
|
+
import { createZodFunctionTool as createZodFunctionTool2 } from "@robota-sdk/agent-tools";
|
|
1760
|
+
var DEFAULT_PROCESS_TIMEOUT_MS = 12e4;
|
|
1761
|
+
function asZodSchema2(schema) {
|
|
1762
|
+
return schema;
|
|
707
1763
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1764
|
+
var BackgroundProcessSchema = z2.object({
|
|
1765
|
+
command: z2.string().describe("The shell command to start in the background"),
|
|
1766
|
+
timeout: z2.number().optional().describe("Optional timeout in milliseconds. Default is 120000."),
|
|
1767
|
+
workingDirectory: z2.string().optional().describe("Working directory for the command. Defaults to the current project directory."),
|
|
1768
|
+
stdin: z2.string().optional().describe("Optional stdin to write after the process starts."),
|
|
1769
|
+
outputLimitBytes: z2.number().optional().describe("Maximum captured output bytes kept in the task result.")
|
|
1770
|
+
});
|
|
1771
|
+
function stringifyStarted(taskId, status, command) {
|
|
1772
|
+
return JSON.stringify({
|
|
1773
|
+
success: true,
|
|
1774
|
+
background: true,
|
|
1775
|
+
output: "",
|
|
1776
|
+
taskId,
|
|
1777
|
+
status,
|
|
1778
|
+
command
|
|
1779
|
+
});
|
|
719
1780
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
};
|
|
727
|
-
function resolveModelId(shortName, _parentModel) {
|
|
728
|
-
return MODEL_SHORTCUTS[shortName] ?? shortName;
|
|
1781
|
+
function stringifyProcessError(message) {
|
|
1782
|
+
return JSON.stringify({
|
|
1783
|
+
success: false,
|
|
1784
|
+
background: true,
|
|
1785
|
+
output: "",
|
|
1786
|
+
error: `Background process error: ${message}`
|
|
1787
|
+
});
|
|
729
1788
|
}
|
|
730
|
-
function
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
1789
|
+
async function startBackgroundProcess(args, deps) {
|
|
1790
|
+
try {
|
|
1791
|
+
const state = await deps.backgroundTaskManager.spawn({
|
|
1792
|
+
kind: "process",
|
|
1793
|
+
label: args.command,
|
|
1794
|
+
mode: "background",
|
|
1795
|
+
parentSessionId: deps.parentSessionId ?? "unknown-session",
|
|
1796
|
+
depth: 0,
|
|
1797
|
+
cwd: args.workingDirectory ?? deps.cwd ?? process.cwd(),
|
|
1798
|
+
command: args.command,
|
|
1799
|
+
stdin: args.stdin,
|
|
1800
|
+
timeoutMs: args.timeout ?? DEFAULT_PROCESS_TIMEOUT_MS,
|
|
1801
|
+
outputLimitBytes: args.outputLimitBytes
|
|
1802
|
+
});
|
|
1803
|
+
return stringifyStarted(state.id, state.status, args.command);
|
|
1804
|
+
} catch (error) {
|
|
1805
|
+
return stringifyProcessError(error instanceof Error ? error.message : String(error));
|
|
739
1806
|
}
|
|
740
|
-
tools = tools.filter((t) => t.getName() !== "Agent");
|
|
741
|
-
return tools;
|
|
742
1807
|
}
|
|
743
|
-
function
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
agentsMd: parentContext.agentsMd,
|
|
751
|
-
isForkWorker: options.isForkWorker ?? false
|
|
752
|
-
});
|
|
753
|
-
const provider = options.provider;
|
|
754
|
-
return new Session({
|
|
755
|
-
tools,
|
|
756
|
-
provider,
|
|
757
|
-
systemMessage,
|
|
758
|
-
terminal,
|
|
759
|
-
model,
|
|
760
|
-
maxTurns: agentDefinition.maxTurns,
|
|
761
|
-
permissions: parentConfig.permissions,
|
|
762
|
-
permissionMode: options.permissionMode,
|
|
763
|
-
defaultTrustLevel: parentConfig.defaultTrustLevel,
|
|
764
|
-
permissionHandler: options.permissionHandler,
|
|
765
|
-
hooks: options.hooks,
|
|
766
|
-
hookTypeExecutors: options.hookTypeExecutors,
|
|
767
|
-
onTextDelta: options.onTextDelta,
|
|
768
|
-
onToolExecution: options.onToolExecution
|
|
769
|
-
});
|
|
1808
|
+
function createBackgroundProcessTool(deps) {
|
|
1809
|
+
return createZodFunctionTool2(
|
|
1810
|
+
"BackgroundProcess",
|
|
1811
|
+
"Start a shell command as a managed background task. Use this for long-running commands that should not block the current conversation. Use /background list, /background read <taskId>, /background cancel <taskId>, or /background close <taskId> to inspect or control it.",
|
|
1812
|
+
asZodSchema2(BackgroundProcessSchema),
|
|
1813
|
+
async (params) => startBackgroundProcess(params, deps)
|
|
1814
|
+
);
|
|
770
1815
|
}
|
|
771
1816
|
|
|
772
|
-
// src/tools/
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
subagent_type: z.string().optional().describe('Agent type: "general-purpose", "Explore", "Plan", or a custom agent name'),
|
|
779
|
-
model: z.string().optional().describe("Optional model override")
|
|
1817
|
+
// src/tools/command-execution-tool.ts
|
|
1818
|
+
import { z as z3 } from "zod";
|
|
1819
|
+
import { createZodFunctionTool as createZodFunctionTool3 } from "@robota-sdk/agent-tools";
|
|
1820
|
+
var CommandExecutionSchema = z3.object({
|
|
1821
|
+
command: z3.string().describe("Command name to execute, with or without a leading slash"),
|
|
1822
|
+
args: z3.string().optional().describe("Arguments to pass to the command")
|
|
780
1823
|
});
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
sessionDepsStore.set(key, deps);
|
|
784
|
-
}
|
|
785
|
-
function retrieveAgentToolDeps(key) {
|
|
786
|
-
return sessionDepsStore.get(key);
|
|
787
|
-
}
|
|
788
|
-
function resolveAgentDefinition(agentType, customRegistry) {
|
|
789
|
-
const builtIn = getBuiltInAgent(agentType);
|
|
790
|
-
if (builtIn) return builtIn;
|
|
791
|
-
if (customRegistry) return customRegistry(agentType);
|
|
792
|
-
return void 0;
|
|
1824
|
+
function asZodSchema3(schema) {
|
|
1825
|
+
return schema;
|
|
793
1826
|
}
|
|
794
|
-
function
|
|
795
|
-
return
|
|
1827
|
+
function normalizeCommand(command) {
|
|
1828
|
+
return command.trim().replace(/^\/+/, "").split(/\s+/)[0] ?? "";
|
|
796
1829
|
}
|
|
797
|
-
function
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
success: false,
|
|
804
|
-
output: "",
|
|
805
|
-
error: `Unknown agent type: ${agentType}`
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
const effectiveDef = args.model ? { ...agentDef, model: args.model } : agentDef;
|
|
809
|
-
const session = createSubagentSession({
|
|
810
|
-
agentDefinition: effectiveDef,
|
|
811
|
-
parentConfig: deps.config,
|
|
812
|
-
parentContext: deps.context,
|
|
813
|
-
parentTools: deps.tools,
|
|
814
|
-
provider: deps.provider,
|
|
815
|
-
terminal: deps.terminal,
|
|
816
|
-
permissionMode: deps.permissionMode,
|
|
817
|
-
permissionHandler: deps.permissionHandler,
|
|
818
|
-
hooks: deps.hooks,
|
|
819
|
-
hookTypeExecutors: deps.hookTypeExecutors,
|
|
820
|
-
onTextDelta: deps.onTextDelta,
|
|
821
|
-
onToolExecution: deps.onToolExecution
|
|
1830
|
+
function stringifyCommandResult(command, result) {
|
|
1831
|
+
if (!result) {
|
|
1832
|
+
return JSON.stringify({
|
|
1833
|
+
success: false,
|
|
1834
|
+
command,
|
|
1835
|
+
error: `Unknown command: ${command}`
|
|
822
1836
|
});
|
|
823
|
-
const agentId = generateAgentId();
|
|
824
|
-
try {
|
|
825
|
-
const response = await session.run(args.prompt);
|
|
826
|
-
return JSON.stringify({
|
|
827
|
-
success: true,
|
|
828
|
-
output: response,
|
|
829
|
-
agentId
|
|
830
|
-
});
|
|
831
|
-
} catch (err) {
|
|
832
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
833
|
-
return JSON.stringify({
|
|
834
|
-
success: false,
|
|
835
|
-
output: "",
|
|
836
|
-
error: `Sub-agent error: ${message}`,
|
|
837
|
-
agentId
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
1837
|
}
|
|
841
|
-
return
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1838
|
+
return JSON.stringify({
|
|
1839
|
+
success: result.success,
|
|
1840
|
+
command,
|
|
1841
|
+
message: result.message,
|
|
1842
|
+
data: result.data
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
function createCommandExecutionTool(deps) {
|
|
1846
|
+
return createZodFunctionTool3(
|
|
1847
|
+
"ExecuteCommand",
|
|
1848
|
+
"Executes a registered model-invocable Robota command through the command registry. Accepted command names and argument grammar come from registered command descriptors.",
|
|
1849
|
+
asZodSchema3(CommandExecutionSchema),
|
|
845
1850
|
async (params) => {
|
|
846
|
-
|
|
1851
|
+
const args = CommandExecutionSchema.parse(params);
|
|
1852
|
+
const command = normalizeCommand(args.command);
|
|
1853
|
+
if (!deps.isModelInvocable(command)) {
|
|
1854
|
+
return JSON.stringify({
|
|
1855
|
+
success: false,
|
|
1856
|
+
command,
|
|
1857
|
+
error: `Command is not model-invocable: ${command}`
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
return stringifyCommandResult(command, await deps.execute(command, args.args ?? ""));
|
|
847
1861
|
}
|
|
848
1862
|
);
|
|
849
1863
|
}
|
|
850
1864
|
|
|
1865
|
+
// src/assembly/create-session.ts
|
|
1866
|
+
import { BackgroundTaskManager as BackgroundTaskManager2, SubagentManager as SubagentManager2 } from "@robota-sdk/agent-runtime";
|
|
1867
|
+
|
|
851
1868
|
// src/agents/agent-definition-loader.ts
|
|
852
|
-
import { readdirSync, readFileSync, existsSync } from "fs";
|
|
853
|
-
import { join, basename } from "path";
|
|
854
|
-
import { homedir } from "os";
|
|
855
|
-
var
|
|
1869
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
1870
|
+
import { join as join2, basename as basename2 } from "path";
|
|
1871
|
+
import { homedir as homedir2 } from "os";
|
|
1872
|
+
var LIST_KEYS2 = /* @__PURE__ */ new Set(["tools", "disallowedTools"]);
|
|
856
1873
|
var NUMBER_KEYS = /* @__PURE__ */ new Set(["maxTurns"]);
|
|
857
|
-
function
|
|
1874
|
+
function parseListValue2(rawValue) {
|
|
1875
|
+
const separator = rawValue.includes(",") ? /\s*,\s*/ : /\s+/;
|
|
1876
|
+
return rawValue.split(separator).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
1877
|
+
}
|
|
1878
|
+
function parseFrontmatter2(content) {
|
|
858
1879
|
const lines = content.split("\n");
|
|
859
1880
|
if (lines[0]?.trim() !== "---") {
|
|
860
1881
|
return { frontmatter: null, body: content };
|
|
@@ -876,8 +1897,8 @@ function parseFrontmatter(content) {
|
|
|
876
1897
|
if (!match) continue;
|
|
877
1898
|
const key = match[1];
|
|
878
1899
|
const rawValue = match[2].trim();
|
|
879
|
-
if (
|
|
880
|
-
result[key] = rawValue
|
|
1900
|
+
if (LIST_KEYS2.has(key)) {
|
|
1901
|
+
result[key] = parseListValue2(rawValue);
|
|
881
1902
|
} else if (NUMBER_KEYS.has(key)) {
|
|
882
1903
|
result[key] = parseInt(rawValue, 10);
|
|
883
1904
|
} else {
|
|
@@ -891,20 +1912,20 @@ function parseFrontmatter(content) {
|
|
|
891
1912
|
};
|
|
892
1913
|
}
|
|
893
1914
|
function scanAgentsDir(dir) {
|
|
894
|
-
if (!
|
|
1915
|
+
if (!existsSync2(dir)) return [];
|
|
895
1916
|
const agents = [];
|
|
896
1917
|
let entries;
|
|
897
1918
|
try {
|
|
898
|
-
entries =
|
|
1919
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
899
1920
|
} catch {
|
|
900
1921
|
return [];
|
|
901
1922
|
}
|
|
902
1923
|
for (const entry of entries) {
|
|
903
1924
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
904
|
-
const filePath =
|
|
905
|
-
const content =
|
|
906
|
-
const { frontmatter, body } =
|
|
907
|
-
const fallbackName =
|
|
1925
|
+
const filePath = join2(dir, entry.name);
|
|
1926
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1927
|
+
const { frontmatter, body } = parseFrontmatter2(content);
|
|
1928
|
+
const fallbackName = basename2(entry.name, ".md");
|
|
908
1929
|
const agent = {
|
|
909
1930
|
name: frontmatter?.name ?? fallbackName,
|
|
910
1931
|
description: frontmatter?.description ?? "",
|
|
@@ -924,13 +1945,16 @@ var AgentDefinitionLoader = class {
|
|
|
924
1945
|
home;
|
|
925
1946
|
constructor(cwd, home) {
|
|
926
1947
|
this.cwd = cwd;
|
|
927
|
-
this.home = home ??
|
|
1948
|
+
this.home = home ?? homedir2();
|
|
928
1949
|
}
|
|
929
1950
|
/** Load all agent definitions, merged with built-in agents. Custom overrides built-in on name collision. */
|
|
930
1951
|
loadAll() {
|
|
931
1952
|
const sources = [
|
|
932
|
-
scanAgentsDir(
|
|
933
|
-
scanAgentsDir(
|
|
1953
|
+
scanAgentsDir(join2(this.cwd, ".robota", "agents")),
|
|
1954
|
+
scanAgentsDir(join2(this.cwd, ".agents", "agents")),
|
|
1955
|
+
scanAgentsDir(join2(this.cwd, ".claude", "agents")),
|
|
1956
|
+
scanAgentsDir(join2(this.home, ".robota", "agents")),
|
|
1957
|
+
scanAgentsDir(join2(this.home, ".claude", "agents"))
|
|
934
1958
|
];
|
|
935
1959
|
const seen = /* @__PURE__ */ new Set();
|
|
936
1960
|
const customAgents = [];
|
|
@@ -956,7 +1980,49 @@ var AgentDefinitionLoader = class {
|
|
|
956
1980
|
}
|
|
957
1981
|
};
|
|
958
1982
|
|
|
1983
|
+
// src/assembly/background-task-hooks.ts
|
|
1984
|
+
import { runHooks } from "@robota-sdk/agent-core";
|
|
1985
|
+
function getSubagentHookEvent(event) {
|
|
1986
|
+
if (event.type === "background_task_started" && event.task.kind === "agent") {
|
|
1987
|
+
return "SubagentStart";
|
|
1988
|
+
}
|
|
1989
|
+
if ((event.type === "background_task_completed" || event.type === "background_task_failed" || event.type === "background_task_cancelled") && event.task.kind === "agent") {
|
|
1990
|
+
return "SubagentStop";
|
|
1991
|
+
}
|
|
1992
|
+
return void 0;
|
|
1993
|
+
}
|
|
1994
|
+
function fireSubagentLifecycleHook(event, cwd, hooks, hookTypeExecutors) {
|
|
1995
|
+
const hookEventName = getSubagentHookEvent(event);
|
|
1996
|
+
if (!hookEventName || !("task" in event)) return;
|
|
1997
|
+
const input = {
|
|
1998
|
+
session_id: event.task.parentSessionId,
|
|
1999
|
+
cwd,
|
|
2000
|
+
hook_event_name: hookEventName,
|
|
2001
|
+
agent_id: event.task.id,
|
|
2002
|
+
agent_type: event.task.agentType ?? event.task.label,
|
|
2003
|
+
...event.task.transcriptPath ? {
|
|
2004
|
+
agent_transcript_path: event.task.transcriptPath,
|
|
2005
|
+
transcript_path: event.task.transcriptPath
|
|
2006
|
+
} : {},
|
|
2007
|
+
...event.task.error?.message || event.task.timeoutReason ? { reason: event.task.error?.message ?? event.task.timeoutReason } : {},
|
|
2008
|
+
...hookEventName === "SubagentStop" ? {
|
|
2009
|
+
stop_hook_active: false,
|
|
2010
|
+
...event.task.result?.output ? { last_assistant_message: event.task.result.output } : {}
|
|
2011
|
+
} : {},
|
|
2012
|
+
env: {
|
|
2013
|
+
CLAUDE_PROJECT_DIR: cwd,
|
|
2014
|
+
CLAUDE_SESSION_ID: event.task.parentSessionId,
|
|
2015
|
+
ROBOTA_AGENT_ID: event.task.id,
|
|
2016
|
+
ROBOTA_AGENT_TYPE: event.task.agentType ?? event.task.label
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
void runHooks(hooks, hookEventName, input, hookTypeExecutors).catch(() => void 0);
|
|
2020
|
+
}
|
|
2021
|
+
|
|
959
2022
|
// src/assembly/create-session.ts
|
|
2023
|
+
var ID_RADIX = 36;
|
|
2024
|
+
var ID_RANDOM_LENGTH = 9;
|
|
2025
|
+
var DEFAULT_PROVIDER_IDLE_TIMEOUT_MS = 12e4;
|
|
960
2026
|
function createSession(options) {
|
|
961
2027
|
if (!options.provider) {
|
|
962
2028
|
throw new Error(
|
|
@@ -964,9 +2030,18 @@ function createSession(options) {
|
|
|
964
2030
|
);
|
|
965
2031
|
}
|
|
966
2032
|
const provider = options.provider;
|
|
2033
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2034
|
+
const sessionId = options.sessionId ?? createSessionId();
|
|
967
2035
|
const defaultTools = createDefaultTools();
|
|
968
2036
|
const tools = [...defaultTools, ...options.additionalTools ?? []];
|
|
969
|
-
|
|
2037
|
+
if (options.modelCommandExecutor && options.isModelCommandInvocable) {
|
|
2038
|
+
tools.push(
|
|
2039
|
+
createCommandExecutionTool({
|
|
2040
|
+
execute: options.modelCommandExecutor,
|
|
2041
|
+
isModelInvocable: options.isModelCommandInvocable
|
|
2042
|
+
})
|
|
2043
|
+
);
|
|
2044
|
+
}
|
|
970
2045
|
const hookTypeExecutors = [];
|
|
971
2046
|
if (options.providerFactory) {
|
|
972
2047
|
hookTypeExecutors.push(
|
|
@@ -986,31 +2061,102 @@ function createSession(options) {
|
|
|
986
2061
|
if (options.additionalHookExecutors) {
|
|
987
2062
|
hookTypeExecutors.push(...options.additionalHookExecutors);
|
|
988
2063
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
2064
|
+
let agentToolDeps;
|
|
2065
|
+
let agentDefinitions = [];
|
|
2066
|
+
let backgroundTaskManager;
|
|
2067
|
+
if (options.enableAgentRuntime) {
|
|
2068
|
+
const agentLoader = new AgentDefinitionLoader(cwd);
|
|
2069
|
+
agentDefinitions = agentLoader.loadAll();
|
|
2070
|
+
agentToolDeps = {
|
|
2071
|
+
config: options.config,
|
|
2072
|
+
context: options.context,
|
|
2073
|
+
tools,
|
|
2074
|
+
terminal: options.terminal,
|
|
2075
|
+
provider,
|
|
2076
|
+
cwd,
|
|
2077
|
+
parentSessionId: sessionId,
|
|
2078
|
+
permissionMode: options.permissionMode,
|
|
2079
|
+
permissionHandler: options.permissionHandler,
|
|
2080
|
+
hooks: options.config.hooks,
|
|
2081
|
+
hookTypeExecutors: hookTypeExecutors.length > 0 ? hookTypeExecutors : void 0,
|
|
2082
|
+
onTextDelta: options.onTextDelta,
|
|
2083
|
+
onToolExecution: options.onToolExecution,
|
|
2084
|
+
customAgentRegistry: (name) => agentLoader.getAgent(name),
|
|
2085
|
+
agentDefinitions
|
|
2086
|
+
};
|
|
2087
|
+
const subagentManager = new SubagentManager2({
|
|
2088
|
+
runner: (options.subagentRunnerFactory ?? createInProcessSubagentRunner)(agentToolDeps),
|
|
2089
|
+
backgroundTaskRunners: options.backgroundTaskRunners
|
|
2090
|
+
});
|
|
2091
|
+
agentToolDeps.subagentManager = subagentManager;
|
|
2092
|
+
backgroundTaskManager = subagentManager.getBackgroundTaskManager();
|
|
2093
|
+
agentToolDeps.backgroundTaskManager = backgroundTaskManager;
|
|
2094
|
+
} else {
|
|
2095
|
+
backgroundTaskManager = new BackgroundTaskManager2({
|
|
2096
|
+
runners: options.backgroundTaskRunners ?? []
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
const sessionLogger = options.sessionLogger;
|
|
2100
|
+
if (backgroundTaskManager && sessionLogger) {
|
|
2101
|
+
backgroundTaskManager.subscribe(
|
|
2102
|
+
(event) => logBackgroundTaskEvent(sessionLogger, sessionId, event)
|
|
2103
|
+
);
|
|
2104
|
+
}
|
|
2105
|
+
if (backgroundTaskManager) {
|
|
2106
|
+
backgroundTaskManager.subscribe(
|
|
2107
|
+
(event) => fireSubagentLifecycleHook(
|
|
2108
|
+
event,
|
|
2109
|
+
cwd,
|
|
2110
|
+
options.config.hooks,
|
|
2111
|
+
hookTypeExecutors.length > 0 ? hookTypeExecutors : void 0
|
|
2112
|
+
)
|
|
2113
|
+
);
|
|
2114
|
+
}
|
|
2115
|
+
let backgroundProcessToolDeps;
|
|
2116
|
+
if (backgroundTaskManager && options.backgroundTaskRunners?.some((runner) => runner.kind === "process")) {
|
|
2117
|
+
backgroundProcessToolDeps = {
|
|
2118
|
+
backgroundTaskManager,
|
|
2119
|
+
cwd,
|
|
2120
|
+
parentSessionId: sessionId
|
|
2121
|
+
};
|
|
2122
|
+
tools.push(createBackgroundProcessTool(backgroundProcessToolDeps));
|
|
2123
|
+
}
|
|
2124
|
+
if (agentToolDeps) {
|
|
2125
|
+
tools.push(createAgentTool(agentToolDeps));
|
|
2126
|
+
}
|
|
1004
2127
|
const buildPrompt = options.systemPromptBuilder ?? buildSystemPrompt;
|
|
2128
|
+
const defaultToolDescriptions = [
|
|
2129
|
+
...DEFAULT_TOOL_DESCRIPTIONS,
|
|
2130
|
+
...agentToolDeps ? [createAgentToolPromptDescription(agentDefinitions)] : [],
|
|
2131
|
+
...options.modelCommandExecutor ? ["ExecuteCommand \u2014 execute model-invocable Robota commands"] : []
|
|
2132
|
+
];
|
|
1005
2133
|
const systemMessage = buildPrompt({
|
|
1006
2134
|
agentsMd: options.context.agentsMd,
|
|
1007
2135
|
claudeMd: options.context.claudeMd,
|
|
1008
|
-
toolDescriptions: options.toolDescriptions ??
|
|
2136
|
+
toolDescriptions: options.toolDescriptions ?? (backgroundProcessToolDeps ? [
|
|
2137
|
+
...defaultToolDescriptions,
|
|
2138
|
+
"BackgroundProcess \u2014 start long-running shell commands as managed background tasks"
|
|
2139
|
+
] : defaultToolDescriptions),
|
|
1009
2140
|
trustLevel: options.config.defaultTrustLevel,
|
|
1010
2141
|
projectInfo: options.projectInfo ?? { type: "unknown", language: "unknown" },
|
|
1011
|
-
cwd
|
|
1012
|
-
language: options.config.language
|
|
2142
|
+
cwd,
|
|
2143
|
+
language: options.config.language,
|
|
2144
|
+
skills: new SkillCommandSource(cwd).getModelInvocableSkills().map((skill) => ({
|
|
2145
|
+
name: skill.name,
|
|
2146
|
+
description: skill.description,
|
|
2147
|
+
disableModelInvocation: skill.disableModelInvocation
|
|
2148
|
+
})),
|
|
2149
|
+
...agentDefinitions.length > 0 ? {
|
|
2150
|
+
agents: agentDefinitions.map((agent) => ({
|
|
2151
|
+
name: agent.name,
|
|
2152
|
+
description: agent.description
|
|
2153
|
+
}))
|
|
2154
|
+
} : {},
|
|
2155
|
+
commandDescriptors: options.commandDescriptors ?? []
|
|
1013
2156
|
});
|
|
2157
|
+
const finalSystemMessage = options.appendSystemPrompt ? `${systemMessage}
|
|
2158
|
+
|
|
2159
|
+
${options.appendSystemPrompt}` : systemMessage;
|
|
1014
2160
|
const defaultAllow = [
|
|
1015
2161
|
"Read(.agents/**)",
|
|
1016
2162
|
"Read(.claude/**)",
|
|
@@ -1019,23 +2165,25 @@ function createSession(options) {
|
|
|
1019
2165
|
"Glob(.claude/**)",
|
|
1020
2166
|
"Glob(.robota/**)"
|
|
1021
2167
|
];
|
|
2168
|
+
const allowedToolPatterns = (options.allowedTools ?? []).map((name) => `${name}(*)`);
|
|
1022
2169
|
const mergedPermissions = {
|
|
1023
|
-
allow: [...defaultAllow, ...options.config.permissions.allow ?? []],
|
|
2170
|
+
allow: [...defaultAllow, ...options.config.permissions.allow ?? [], ...allowedToolPatterns],
|
|
1024
2171
|
deny: options.config.permissions.deny ?? []
|
|
1025
2172
|
};
|
|
1026
2173
|
const session = new Session2({
|
|
1027
2174
|
tools,
|
|
1028
2175
|
provider,
|
|
1029
|
-
systemMessage,
|
|
2176
|
+
systemMessage: finalSystemMessage,
|
|
1030
2177
|
terminal: options.terminal,
|
|
1031
2178
|
permissions: mergedPermissions,
|
|
1032
2179
|
hooks: options.config.hooks,
|
|
1033
2180
|
permissionMode: options.permissionMode,
|
|
1034
2181
|
defaultTrustLevel: options.config.defaultTrustLevel,
|
|
1035
2182
|
model: options.config.provider.model,
|
|
2183
|
+
providerTimeout: options.config.provider.timeout ?? DEFAULT_PROVIDER_IDLE_TIMEOUT_MS,
|
|
1036
2184
|
maxTurns: options.maxTurns,
|
|
1037
2185
|
sessionStore: options.sessionStore,
|
|
1038
|
-
sessionId
|
|
2186
|
+
sessionId,
|
|
1039
2187
|
permissionHandler: options.permissionHandler,
|
|
1040
2188
|
onTextDelta: options.onTextDelta,
|
|
1041
2189
|
onToolExecution: options.onToolExecution,
|
|
@@ -1045,122 +2193,153 @@ function createSession(options) {
|
|
|
1045
2193
|
sessionLogger: options.sessionLogger,
|
|
1046
2194
|
hookTypeExecutors: hookTypeExecutors.length > 0 ? hookTypeExecutors : void 0
|
|
1047
2195
|
});
|
|
1048
|
-
|
|
2196
|
+
if (agentToolDeps) agentToolDeps.parentSessionId = session.getSessionId();
|
|
2197
|
+
if (backgroundProcessToolDeps) backgroundProcessToolDeps.parentSessionId = session.getSessionId();
|
|
2198
|
+
if (backgroundTaskManager) storeSessionBackgroundTaskManager(session, backgroundTaskManager);
|
|
2199
|
+
if (agentToolDeps) storeAgentToolDeps(session, agentToolDeps);
|
|
1049
2200
|
return session;
|
|
1050
2201
|
}
|
|
2202
|
+
function createSessionId() {
|
|
2203
|
+
return `session_${Date.now()}_${Math.random().toString(ID_RADIX).substr(2, ID_RANDOM_LENGTH)}`;
|
|
2204
|
+
}
|
|
2205
|
+
function logBackgroundTaskEvent(logger, sessionId, event) {
|
|
2206
|
+
logger.log(sessionId, "background_task_event", {
|
|
2207
|
+
backgroundEventType: event.type,
|
|
2208
|
+
backgroundEvent: event
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
1051
2211
|
|
|
1052
2212
|
// src/assembly/subagent-logger.ts
|
|
1053
2213
|
import { mkdirSync } from "fs";
|
|
1054
|
-
import { join as
|
|
2214
|
+
import { join as join3 } from "path";
|
|
1055
2215
|
import { FileSessionLogger } from "@robota-sdk/agent-sessions";
|
|
1056
2216
|
function createSubagentLogger(parentSessionId, _agentId, baseLogsDir) {
|
|
1057
|
-
const subagentDir =
|
|
2217
|
+
const subagentDir = join3(baseLogsDir, parentSessionId, "subagents");
|
|
1058
2218
|
mkdirSync(subagentDir, { recursive: true });
|
|
1059
2219
|
return new FileSessionLogger(subagentDir);
|
|
1060
2220
|
}
|
|
1061
2221
|
function resolveSubagentLogDir(parentSessionId, baseLogsDir) {
|
|
1062
|
-
return
|
|
2222
|
+
return join3(baseLogsDir, parentSessionId, "subagents");
|
|
1063
2223
|
}
|
|
1064
2224
|
|
|
1065
2225
|
// src/interactive/interactive-session-init.ts
|
|
1066
2226
|
import { FileSessionLogger as FileSessionLogger2 } from "@robota-sdk/agent-sessions";
|
|
1067
2227
|
|
|
1068
2228
|
// src/paths.ts
|
|
1069
|
-
import { join as
|
|
1070
|
-
import { homedir as
|
|
2229
|
+
import { join as join4 } from "path";
|
|
2230
|
+
import { homedir as homedir3 } from "os";
|
|
1071
2231
|
function projectPaths(cwd) {
|
|
1072
|
-
const base =
|
|
2232
|
+
const base = join4(cwd, ".robota");
|
|
1073
2233
|
return {
|
|
1074
|
-
settings:
|
|
1075
|
-
settingsLocal:
|
|
1076
|
-
logs:
|
|
1077
|
-
sessions:
|
|
2234
|
+
settings: join4(base, "settings.json"),
|
|
2235
|
+
settingsLocal: join4(base, "settings.local.json"),
|
|
2236
|
+
logs: join4(base, "logs"),
|
|
2237
|
+
sessions: join4(base, "sessions")
|
|
1078
2238
|
};
|
|
1079
2239
|
}
|
|
1080
2240
|
function userPaths() {
|
|
1081
|
-
const base =
|
|
2241
|
+
const base = join4(homedir3(), ".robota");
|
|
1082
2242
|
return {
|
|
1083
|
-
settings:
|
|
1084
|
-
sessions:
|
|
2243
|
+
settings: join4(base, "settings.json"),
|
|
2244
|
+
sessions: join4(base, "sessions")
|
|
1085
2245
|
};
|
|
1086
2246
|
}
|
|
1087
2247
|
|
|
1088
2248
|
// src/config/config-loader.ts
|
|
1089
|
-
import { readFileSync as
|
|
1090
|
-
import { join as
|
|
2249
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
|
|
2250
|
+
import { join as join5 } from "path";
|
|
1091
2251
|
|
|
1092
2252
|
// src/config/config-types.ts
|
|
1093
|
-
import { z as
|
|
1094
|
-
var ProviderSchema =
|
|
1095
|
-
name:
|
|
1096
|
-
model:
|
|
1097
|
-
apiKey:
|
|
2253
|
+
import { z as z4 } from "zod";
|
|
2254
|
+
var ProviderSchema = z4.object({
|
|
2255
|
+
name: z4.string().optional(),
|
|
2256
|
+
model: z4.string().optional(),
|
|
2257
|
+
apiKey: z4.string().optional(),
|
|
2258
|
+
baseURL: z4.string().optional(),
|
|
2259
|
+
timeout: z4.number().optional()
|
|
1098
2260
|
});
|
|
1099
|
-
var
|
|
2261
|
+
var ProviderProfileSchema = z4.object({
|
|
2262
|
+
type: z4.string().optional(),
|
|
2263
|
+
model: z4.string().optional(),
|
|
2264
|
+
apiKey: z4.string().optional(),
|
|
2265
|
+
baseURL: z4.string().optional(),
|
|
2266
|
+
timeout: z4.number().optional()
|
|
2267
|
+
});
|
|
2268
|
+
var PermissionsSchema = z4.object({
|
|
1100
2269
|
/** Patterns that are always approved without prompting */
|
|
1101
|
-
allow:
|
|
2270
|
+
allow: z4.array(z4.string()).optional(),
|
|
1102
2271
|
/** Patterns that are always denied */
|
|
1103
|
-
deny:
|
|
2272
|
+
deny: z4.array(z4.string()).optional()
|
|
1104
2273
|
});
|
|
1105
|
-
var EnvSchema =
|
|
1106
|
-
var CommandHookDefinitionSchema =
|
|
1107
|
-
type:
|
|
1108
|
-
command:
|
|
1109
|
-
timeout:
|
|
2274
|
+
var EnvSchema = z4.record(z4.string()).optional();
|
|
2275
|
+
var CommandHookDefinitionSchema = z4.object({
|
|
2276
|
+
type: z4.literal("command"),
|
|
2277
|
+
command: z4.string(),
|
|
2278
|
+
timeout: z4.number().optional()
|
|
1110
2279
|
});
|
|
1111
|
-
var HttpHookDefinitionSchema =
|
|
1112
|
-
type:
|
|
1113
|
-
url:
|
|
1114
|
-
headers:
|
|
1115
|
-
timeout:
|
|
2280
|
+
var HttpHookDefinitionSchema = z4.object({
|
|
2281
|
+
type: z4.literal("http"),
|
|
2282
|
+
url: z4.string(),
|
|
2283
|
+
headers: z4.record(z4.string()).optional(),
|
|
2284
|
+
timeout: z4.number().optional()
|
|
1116
2285
|
});
|
|
1117
|
-
var PromptHookDefinitionSchema =
|
|
1118
|
-
type:
|
|
1119
|
-
prompt:
|
|
1120
|
-
model:
|
|
2286
|
+
var PromptHookDefinitionSchema = z4.object({
|
|
2287
|
+
type: z4.literal("prompt"),
|
|
2288
|
+
prompt: z4.string(),
|
|
2289
|
+
model: z4.string().optional()
|
|
1121
2290
|
});
|
|
1122
|
-
var AgentHookDefinitionSchema =
|
|
1123
|
-
type:
|
|
1124
|
-
agent:
|
|
1125
|
-
maxTurns:
|
|
1126
|
-
timeout:
|
|
2291
|
+
var AgentHookDefinitionSchema = z4.object({
|
|
2292
|
+
type: z4.literal("agent"),
|
|
2293
|
+
agent: z4.string(),
|
|
2294
|
+
maxTurns: z4.number().optional(),
|
|
2295
|
+
timeout: z4.number().optional()
|
|
1127
2296
|
});
|
|
1128
|
-
var HookDefinitionSchema =
|
|
2297
|
+
var HookDefinitionSchema = z4.discriminatedUnion("type", [
|
|
1129
2298
|
CommandHookDefinitionSchema,
|
|
1130
2299
|
HttpHookDefinitionSchema,
|
|
1131
2300
|
PromptHookDefinitionSchema,
|
|
1132
2301
|
AgentHookDefinitionSchema
|
|
1133
2302
|
]);
|
|
1134
|
-
var HookGroupSchema =
|
|
1135
|
-
matcher:
|
|
1136
|
-
hooks:
|
|
2303
|
+
var HookGroupSchema = z4.object({
|
|
2304
|
+
matcher: z4.string(),
|
|
2305
|
+
hooks: z4.array(HookDefinitionSchema)
|
|
1137
2306
|
});
|
|
1138
|
-
var HooksSchema =
|
|
1139
|
-
PreToolUse:
|
|
1140
|
-
PostToolUse:
|
|
1141
|
-
SessionStart:
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
2307
|
+
var HooksSchema = z4.object({
|
|
2308
|
+
PreToolUse: z4.array(HookGroupSchema).optional(),
|
|
2309
|
+
PostToolUse: z4.array(HookGroupSchema).optional(),
|
|
2310
|
+
SessionStart: z4.array(HookGroupSchema).optional(),
|
|
2311
|
+
SessionEnd: z4.array(HookGroupSchema).optional(),
|
|
2312
|
+
Stop: z4.array(HookGroupSchema).optional(),
|
|
2313
|
+
StopFailure: z4.array(HookGroupSchema).optional(),
|
|
2314
|
+
PreCompact: z4.array(HookGroupSchema).optional(),
|
|
2315
|
+
PostCompact: z4.array(HookGroupSchema).optional(),
|
|
2316
|
+
UserPromptSubmit: z4.array(HookGroupSchema).optional(),
|
|
2317
|
+
SubagentStart: z4.array(HookGroupSchema).optional(),
|
|
2318
|
+
SubagentStop: z4.array(HookGroupSchema).optional(),
|
|
2319
|
+
WorktreeCreate: z4.array(HookGroupSchema).optional(),
|
|
2320
|
+
WorktreeRemove: z4.array(HookGroupSchema).optional()
|
|
1147
2321
|
}).optional();
|
|
1148
|
-
var EnabledPluginsSchema =
|
|
1149
|
-
var MarketplaceSourceSchema =
|
|
1150
|
-
source:
|
|
1151
|
-
type:
|
|
1152
|
-
repo:
|
|
1153
|
-
url:
|
|
1154
|
-
path:
|
|
1155
|
-
ref:
|
|
2322
|
+
var EnabledPluginsSchema = z4.record(z4.boolean()).optional();
|
|
2323
|
+
var MarketplaceSourceSchema = z4.object({
|
|
2324
|
+
source: z4.object({
|
|
2325
|
+
type: z4.enum(["github", "git", "local", "url"]),
|
|
2326
|
+
repo: z4.string().optional(),
|
|
2327
|
+
url: z4.string().optional(),
|
|
2328
|
+
path: z4.string().optional(),
|
|
2329
|
+
ref: z4.string().optional()
|
|
1156
2330
|
})
|
|
1157
2331
|
});
|
|
1158
|
-
var ExtraKnownMarketplacesSchema =
|
|
1159
|
-
var SettingsSchema =
|
|
2332
|
+
var ExtraKnownMarketplacesSchema = z4.record(MarketplaceSourceSchema).optional().catch(void 0);
|
|
2333
|
+
var SettingsSchema = z4.object({
|
|
1160
2334
|
/** Trust level used when no --permission-mode flag is given */
|
|
1161
|
-
defaultTrustLevel:
|
|
2335
|
+
defaultTrustLevel: z4.enum(["safe", "moderate", "full"]).optional(),
|
|
1162
2336
|
/** Response language (e.g., "ko", "en", "ja"). Injected into system prompt. */
|
|
1163
|
-
language:
|
|
2337
|
+
language: z4.string().optional(),
|
|
2338
|
+
/** Active provider profile key from providers. */
|
|
2339
|
+
currentProvider: z4.string().optional(),
|
|
2340
|
+
/** Provider profiles keyed by user-facing profile name. */
|
|
2341
|
+
providers: z4.record(ProviderProfileSchema).optional(),
|
|
2342
|
+
/** Legacy single-provider settings. Prefer currentProvider + providers for new config. */
|
|
1164
2343
|
provider: ProviderSchema.optional(),
|
|
1165
2344
|
permissions: PermissionsSchema.optional(),
|
|
1166
2345
|
env: EnvSchema,
|
|
@@ -1189,10 +2368,10 @@ var DEFAULTS = {
|
|
|
1189
2368
|
env: {}
|
|
1190
2369
|
};
|
|
1191
2370
|
function readJsonFile(filePath) {
|
|
1192
|
-
if (!
|
|
2371
|
+
if (!existsSync3(filePath)) {
|
|
1193
2372
|
return void 0;
|
|
1194
2373
|
}
|
|
1195
|
-
const raw =
|
|
2374
|
+
const raw = readFileSync3(filePath, "utf-8").trim();
|
|
1196
2375
|
if (raw.length === 0) {
|
|
1197
2376
|
return void 0;
|
|
1198
2377
|
}
|
|
@@ -1211,16 +2390,30 @@ function resolveEnvRef(value) {
|
|
|
1211
2390
|
return value;
|
|
1212
2391
|
}
|
|
1213
2392
|
function resolveEnvRefs(settings) {
|
|
1214
|
-
|
|
2393
|
+
const provider = settings.provider?.apiKey !== void 0 ? {
|
|
2394
|
+
...settings.provider,
|
|
2395
|
+
apiKey: resolveEnvRef(settings.provider.apiKey)
|
|
2396
|
+
} : settings.provider;
|
|
2397
|
+
if (settings.providers !== void 0) {
|
|
2398
|
+
const providers = Object.fromEntries(
|
|
2399
|
+
Object.entries(settings.providers).map(([name, profile]) => [
|
|
2400
|
+
name,
|
|
2401
|
+
{
|
|
2402
|
+
...profile,
|
|
2403
|
+
...profile.apiKey !== void 0 && { apiKey: resolveEnvRef(profile.apiKey) }
|
|
2404
|
+
}
|
|
2405
|
+
])
|
|
2406
|
+
);
|
|
1215
2407
|
return {
|
|
1216
2408
|
...settings,
|
|
1217
|
-
provider
|
|
1218
|
-
|
|
1219
|
-
apiKey: resolveEnvRef(settings.provider.apiKey)
|
|
1220
|
-
}
|
|
2409
|
+
provider,
|
|
2410
|
+
providers
|
|
1221
2411
|
};
|
|
1222
2412
|
}
|
|
1223
|
-
return
|
|
2413
|
+
return {
|
|
2414
|
+
...settings,
|
|
2415
|
+
provider
|
|
2416
|
+
};
|
|
1224
2417
|
}
|
|
1225
2418
|
function mergeSettings(layers) {
|
|
1226
2419
|
return layers.reduce((merged, layer) => {
|
|
@@ -1236,20 +2429,53 @@ function mergeSettings(layers) {
|
|
|
1236
2429
|
...merged.env ?? {},
|
|
1237
2430
|
...layer.env ?? {}
|
|
1238
2431
|
},
|
|
2432
|
+
providers: merged.providers !== void 0 || layer.providers !== void 0 ? mergeProviders(merged.providers, layer.providers) : void 0,
|
|
1239
2433
|
enabledPlugins: merged.enabledPlugins !== void 0 || layer.enabledPlugins !== void 0 ? { ...merged.enabledPlugins ?? {}, ...layer.enabledPlugins ?? {} } : void 0,
|
|
1240
2434
|
extraKnownMarketplaces: layer.extraKnownMarketplaces ?? merged.extraKnownMarketplaces
|
|
1241
2435
|
};
|
|
1242
2436
|
}, {});
|
|
1243
2437
|
}
|
|
2438
|
+
function mergeProviders(base, override) {
|
|
2439
|
+
const result = { ...base ?? {} };
|
|
2440
|
+
for (const [name, profile] of Object.entries(override ?? {})) {
|
|
2441
|
+
result[name] = {
|
|
2442
|
+
...result[name],
|
|
2443
|
+
...profile
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
return result;
|
|
2447
|
+
}
|
|
2448
|
+
function resolveProvider(merged) {
|
|
2449
|
+
if (merged.currentProvider !== void 0) {
|
|
2450
|
+
const profile = merged.providers?.[merged.currentProvider];
|
|
2451
|
+
if (profile === void 0) {
|
|
2452
|
+
throw new Error(`currentProvider "${merged.currentProvider}" was not found in providers`);
|
|
2453
|
+
}
|
|
2454
|
+
if (profile.type === void 0) {
|
|
2455
|
+
throw new Error(`Provider profile "${merged.currentProvider}" is missing type`);
|
|
2456
|
+
}
|
|
2457
|
+
return {
|
|
2458
|
+
name: profile.type,
|
|
2459
|
+
model: profile.model ?? DEFAULTS.provider.model,
|
|
2460
|
+
apiKey: profile.apiKey ?? DEFAULTS.provider.apiKey,
|
|
2461
|
+
...profile.baseURL !== void 0 && { baseURL: profile.baseURL },
|
|
2462
|
+
...profile.timeout !== void 0 && { timeout: profile.timeout }
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
return {
|
|
2466
|
+
name: merged.provider?.name ?? DEFAULTS.provider.name,
|
|
2467
|
+
model: merged.provider?.model ?? DEFAULTS.provider.model,
|
|
2468
|
+
apiKey: merged.provider?.apiKey ?? DEFAULTS.provider.apiKey,
|
|
2469
|
+
...merged.provider?.baseURL !== void 0 && { baseURL: merged.provider.baseURL },
|
|
2470
|
+
...merged.provider?.timeout !== void 0 && { timeout: merged.provider.timeout }
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
1244
2473
|
function toResolvedConfig(merged) {
|
|
1245
2474
|
return {
|
|
1246
2475
|
defaultTrustLevel: merged.defaultTrustLevel ?? DEFAULTS.defaultTrustLevel,
|
|
1247
2476
|
language: merged.language,
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
model: merged.provider?.model ?? DEFAULTS.provider.model,
|
|
1251
|
-
apiKey: merged.provider?.apiKey ?? DEFAULTS.provider.apiKey
|
|
1252
|
-
},
|
|
2477
|
+
currentProvider: merged.currentProvider,
|
|
2478
|
+
provider: resolveProvider(merged),
|
|
1253
2479
|
permissions: {
|
|
1254
2480
|
allow: merged.permissions?.allow ?? DEFAULTS.permissions.allow,
|
|
1255
2481
|
deny: merged.permissions?.deny ?? DEFAULTS.permissions.deny
|
|
@@ -1263,15 +2489,17 @@ function toResolvedConfig(merged) {
|
|
|
1263
2489
|
function getSettingsPaths(cwd) {
|
|
1264
2490
|
const home = getHomeDir();
|
|
1265
2491
|
return [
|
|
1266
|
-
|
|
2492
|
+
join5(home, ".robota", "settings.json"),
|
|
1267
2493
|
// 1. user (lowest)
|
|
1268
|
-
|
|
2494
|
+
join5(home, ".claude", "settings.json"),
|
|
2495
|
+
// 1b. user (Claude Code compat)
|
|
2496
|
+
join5(cwd, ".robota", "settings.json"),
|
|
1269
2497
|
// 2. project
|
|
1270
|
-
|
|
2498
|
+
join5(cwd, ".robota", "settings.local.json"),
|
|
1271
2499
|
// 3. project-local
|
|
1272
|
-
|
|
2500
|
+
join5(cwd, ".claude", "settings.json"),
|
|
1273
2501
|
// 4. project, Claude Code compat
|
|
1274
|
-
|
|
2502
|
+
join5(cwd, ".claude", "settings.local.json")
|
|
1275
2503
|
// 5. project-local (highest)
|
|
1276
2504
|
];
|
|
1277
2505
|
}
|
|
@@ -1296,8 +2524,8 @@ async function loadConfig(cwd) {
|
|
|
1296
2524
|
}
|
|
1297
2525
|
|
|
1298
2526
|
// src/context/context-loader.ts
|
|
1299
|
-
import { existsSync as
|
|
1300
|
-
import { join as
|
|
2527
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
2528
|
+
import { join as join6, dirname, resolve } from "path";
|
|
1301
2529
|
var AGENTS_FILENAME = "AGENTS.md";
|
|
1302
2530
|
var CLAUDE_FILENAME = "CLAUDE.md";
|
|
1303
2531
|
function collectFilesWalkingUp(startDir, filename) {
|
|
@@ -1305,8 +2533,8 @@ function collectFilesWalkingUp(startDir, filename) {
|
|
|
1305
2533
|
let current = resolve(startDir);
|
|
1306
2534
|
let atRoot = false;
|
|
1307
2535
|
while (!atRoot) {
|
|
1308
|
-
const candidate =
|
|
1309
|
-
if (
|
|
2536
|
+
const candidate = join6(current, filename);
|
|
2537
|
+
if (existsSync4(candidate)) {
|
|
1310
2538
|
found.push(candidate);
|
|
1311
2539
|
}
|
|
1312
2540
|
const parent = dirname(current);
|
|
@@ -1344,47 +2572,47 @@ function extractCompactInstructions(content) {
|
|
|
1344
2572
|
async function loadContext(cwd) {
|
|
1345
2573
|
const agentsPaths = collectFilesWalkingUp(cwd, AGENTS_FILENAME);
|
|
1346
2574
|
const claudePaths = collectFilesWalkingUp(cwd, CLAUDE_FILENAME);
|
|
1347
|
-
const agentsMd = agentsPaths.map((p) =>
|
|
1348
|
-
const claudeMd = claudePaths.map((p) =>
|
|
2575
|
+
const agentsMd = agentsPaths.map((p) => readFileSync4(p, "utf-8")).join("\n\n");
|
|
2576
|
+
const claudeMd = claudePaths.map((p) => readFileSync4(p, "utf-8")).join("\n\n");
|
|
1349
2577
|
const compactInstructions = extractCompactInstructions(claudeMd);
|
|
1350
2578
|
return { agentsMd, claudeMd, compactInstructions };
|
|
1351
2579
|
}
|
|
1352
2580
|
|
|
1353
2581
|
// src/context/project-detector.ts
|
|
1354
|
-
import { existsSync as
|
|
1355
|
-
import { join as
|
|
2582
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
2583
|
+
import { join as join7 } from "path";
|
|
1356
2584
|
function tryReadJson(filePath) {
|
|
1357
|
-
if (!
|
|
2585
|
+
if (!existsSync5(filePath)) return void 0;
|
|
1358
2586
|
try {
|
|
1359
|
-
return JSON.parse(
|
|
2587
|
+
return JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
1360
2588
|
} catch {
|
|
1361
2589
|
return void 0;
|
|
1362
2590
|
}
|
|
1363
2591
|
}
|
|
1364
2592
|
function detectPackageManager(cwd) {
|
|
1365
|
-
if (
|
|
2593
|
+
if (existsSync5(join7(cwd, "pnpm-workspace.yaml")) || existsSync5(join7(cwd, "pnpm-lock.yaml"))) {
|
|
1366
2594
|
return "pnpm";
|
|
1367
2595
|
}
|
|
1368
|
-
if (
|
|
2596
|
+
if (existsSync5(join7(cwd, "yarn.lock"))) {
|
|
1369
2597
|
return "yarn";
|
|
1370
2598
|
}
|
|
1371
|
-
if (
|
|
2599
|
+
if (existsSync5(join7(cwd, "bun.lockb"))) {
|
|
1372
2600
|
return "bun";
|
|
1373
2601
|
}
|
|
1374
|
-
if (
|
|
2602
|
+
if (existsSync5(join7(cwd, "package-lock.json"))) {
|
|
1375
2603
|
return "npm";
|
|
1376
2604
|
}
|
|
1377
2605
|
return void 0;
|
|
1378
2606
|
}
|
|
1379
2607
|
async function detectProject(cwd) {
|
|
1380
|
-
const pkgJsonPath =
|
|
1381
|
-
const tsconfigPath =
|
|
1382
|
-
const pyprojectPath =
|
|
1383
|
-
const cargoPath =
|
|
1384
|
-
const goModPath =
|
|
1385
|
-
if (
|
|
2608
|
+
const pkgJsonPath = join7(cwd, "package.json");
|
|
2609
|
+
const tsconfigPath = join7(cwd, "tsconfig.json");
|
|
2610
|
+
const pyprojectPath = join7(cwd, "pyproject.toml");
|
|
2611
|
+
const cargoPath = join7(cwd, "Cargo.toml");
|
|
2612
|
+
const goModPath = join7(cwd, "go.mod");
|
|
2613
|
+
if (existsSync5(pkgJsonPath)) {
|
|
1386
2614
|
const pkgJson = tryReadJson(pkgJsonPath);
|
|
1387
|
-
const language =
|
|
2615
|
+
const language = existsSync5(tsconfigPath) ? "typescript" : "javascript";
|
|
1388
2616
|
const packageManager = detectPackageManager(cwd);
|
|
1389
2617
|
return {
|
|
1390
2618
|
type: "node",
|
|
@@ -1393,19 +2621,19 @@ async function detectProject(cwd) {
|
|
|
1393
2621
|
language
|
|
1394
2622
|
};
|
|
1395
2623
|
}
|
|
1396
|
-
if (
|
|
2624
|
+
if (existsSync5(pyprojectPath) || existsSync5(join7(cwd, "setup.py"))) {
|
|
1397
2625
|
return {
|
|
1398
2626
|
type: "python",
|
|
1399
2627
|
language: "python"
|
|
1400
2628
|
};
|
|
1401
2629
|
}
|
|
1402
|
-
if (
|
|
2630
|
+
if (existsSync5(cargoPath)) {
|
|
1403
2631
|
return {
|
|
1404
2632
|
type: "rust",
|
|
1405
2633
|
language: "rust"
|
|
1406
2634
|
};
|
|
1407
2635
|
}
|
|
1408
|
-
if (
|
|
2636
|
+
if (existsSync5(goModPath)) {
|
|
1409
2637
|
return {
|
|
1410
2638
|
type: "go",
|
|
1411
2639
|
language: "go"
|
|
@@ -1418,7 +2646,7 @@ async function detectProject(cwd) {
|
|
|
1418
2646
|
}
|
|
1419
2647
|
|
|
1420
2648
|
// src/plugins/plugin-settings-store.ts
|
|
1421
|
-
import { existsSync as
|
|
2649
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
1422
2650
|
import { dirname as dirname2 } from "path";
|
|
1423
2651
|
var PluginSettingsStore = class {
|
|
1424
2652
|
settingsPath;
|
|
@@ -1427,11 +2655,11 @@ var PluginSettingsStore = class {
|
|
|
1427
2655
|
}
|
|
1428
2656
|
/** Read the full settings file from disk. */
|
|
1429
2657
|
readAll() {
|
|
1430
|
-
if (!
|
|
2658
|
+
if (!existsSync6(this.settingsPath)) {
|
|
1431
2659
|
return {};
|
|
1432
2660
|
}
|
|
1433
2661
|
try {
|
|
1434
|
-
const raw =
|
|
2662
|
+
const raw = readFileSync6(this.settingsPath, "utf-8");
|
|
1435
2663
|
const data = JSON.parse(raw);
|
|
1436
2664
|
if (typeof data === "object" && data !== null) {
|
|
1437
2665
|
return data;
|
|
@@ -1444,7 +2672,7 @@ var PluginSettingsStore = class {
|
|
|
1444
2672
|
/** Write the full settings file to disk. */
|
|
1445
2673
|
writeAll(settings) {
|
|
1446
2674
|
const dir = dirname2(this.settingsPath);
|
|
1447
|
-
if (!
|
|
2675
|
+
if (!existsSync6(dir)) {
|
|
1448
2676
|
mkdirSync2(dir, { recursive: true });
|
|
1449
2677
|
}
|
|
1450
2678
|
writeFileSync(this.settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
@@ -1519,11 +2747,11 @@ var PluginSettingsStore = class {
|
|
|
1519
2747
|
};
|
|
1520
2748
|
|
|
1521
2749
|
// src/plugins/bundle-plugin-loader.ts
|
|
1522
|
-
import { existsSync as
|
|
1523
|
-
import { join as
|
|
2750
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "fs";
|
|
2751
|
+
import { join as join8 } from "path";
|
|
1524
2752
|
|
|
1525
2753
|
// src/plugins/bundle-plugin-utils.ts
|
|
1526
|
-
import { existsSync as
|
|
2754
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3 } from "fs";
|
|
1527
2755
|
function parseSkillFrontmatter(raw) {
|
|
1528
2756
|
const trimmed = raw.trimStart();
|
|
1529
2757
|
if (!trimmed.startsWith("---")) {
|
|
@@ -1572,9 +2800,9 @@ function validateManifest(data) {
|
|
|
1572
2800
|
};
|
|
1573
2801
|
}
|
|
1574
2802
|
function getSortedSubdirs(dirPath) {
|
|
1575
|
-
if (!
|
|
2803
|
+
if (!existsSync7(dirPath)) return [];
|
|
1576
2804
|
try {
|
|
1577
|
-
const entries =
|
|
2805
|
+
const entries = readdirSync3(dirPath, { withFileTypes: true });
|
|
1578
2806
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
1579
2807
|
} catch {
|
|
1580
2808
|
return [];
|
|
@@ -1604,23 +2832,23 @@ var BundlePluginLoader = class {
|
|
|
1604
2832
|
* For each marketplace/plugin pair, the latest version (lexicographically last) is loaded.
|
|
1605
2833
|
*/
|
|
1606
2834
|
discoverAndLoad() {
|
|
1607
|
-
const cacheDir =
|
|
1608
|
-
if (!
|
|
2835
|
+
const cacheDir = join8(this.pluginsDir, "cache");
|
|
2836
|
+
if (!existsSync8(cacheDir)) {
|
|
1609
2837
|
return [];
|
|
1610
2838
|
}
|
|
1611
2839
|
const results = [];
|
|
1612
2840
|
const marketplaces = getSortedSubdirs(cacheDir);
|
|
1613
2841
|
for (const marketplace of marketplaces) {
|
|
1614
|
-
const marketplaceDir =
|
|
2842
|
+
const marketplaceDir = join8(cacheDir, marketplace);
|
|
1615
2843
|
const plugins = getSortedSubdirs(marketplaceDir);
|
|
1616
2844
|
for (const pluginName of plugins) {
|
|
1617
|
-
const pluginDir =
|
|
2845
|
+
const pluginDir = join8(marketplaceDir, pluginName);
|
|
1618
2846
|
const versions = getSortedSubdirs(pluginDir);
|
|
1619
2847
|
if (versions.length === 0) continue;
|
|
1620
2848
|
const latestVersion = versions[versions.length - 1];
|
|
1621
|
-
const versionDir =
|
|
1622
|
-
const manifestPath =
|
|
1623
|
-
if (!
|
|
2849
|
+
const versionDir = join8(pluginDir, latestVersion);
|
|
2850
|
+
const manifestPath = join8(versionDir, ".claude-plugin", "plugin.json");
|
|
2851
|
+
if (!existsSync8(manifestPath)) continue;
|
|
1624
2852
|
const manifest = this.readManifest(manifestPath);
|
|
1625
2853
|
if (!manifest) continue;
|
|
1626
2854
|
const pluginId = `${manifest.name}@${marketplace}`;
|
|
@@ -1634,7 +2862,7 @@ var BundlePluginLoader = class {
|
|
|
1634
2862
|
/** Read and validate a plugin.json manifest. Returns null on failure. */
|
|
1635
2863
|
readManifest(path) {
|
|
1636
2864
|
try {
|
|
1637
|
-
const raw =
|
|
2865
|
+
const raw = readFileSync7(path, "utf-8");
|
|
1638
2866
|
const data = JSON.parse(raw);
|
|
1639
2867
|
return validateManifest(data);
|
|
1640
2868
|
} catch {
|
|
@@ -1669,15 +2897,15 @@ var BundlePluginLoader = class {
|
|
|
1669
2897
|
}
|
|
1670
2898
|
/** Load skills from the plugin's skills/ directory. */
|
|
1671
2899
|
loadSkills(pluginDir, pluginName) {
|
|
1672
|
-
const skillsDir =
|
|
1673
|
-
if (!
|
|
1674
|
-
const entries =
|
|
2900
|
+
const skillsDir = join8(pluginDir, "skills");
|
|
2901
|
+
if (!existsSync8(skillsDir)) return [];
|
|
2902
|
+
const entries = readdirSync4(skillsDir, { withFileTypes: true });
|
|
1675
2903
|
const skills = [];
|
|
1676
2904
|
for (const entry of entries) {
|
|
1677
2905
|
if (!entry.isDirectory()) continue;
|
|
1678
|
-
const skillFile =
|
|
1679
|
-
if (!
|
|
1680
|
-
const raw =
|
|
2906
|
+
const skillFile = join8(skillsDir, entry.name, "SKILL.md");
|
|
2907
|
+
if (!existsSync8(skillFile)) continue;
|
|
2908
|
+
const raw = readFileSync7(skillFile, "utf-8");
|
|
1681
2909
|
const { metadata, content } = parseSkillFrontmatter(raw);
|
|
1682
2910
|
const description = typeof metadata.description === "string" ? metadata.description : "";
|
|
1683
2911
|
const skill = {
|
|
@@ -1692,13 +2920,13 @@ var BundlePluginLoader = class {
|
|
|
1692
2920
|
}
|
|
1693
2921
|
/** Load commands from the plugin's commands/ directory (flat .md files). */
|
|
1694
2922
|
loadCommands(pluginDir, pluginName) {
|
|
1695
|
-
const commandsDir =
|
|
1696
|
-
if (!
|
|
1697
|
-
const entries =
|
|
2923
|
+
const commandsDir = join8(pluginDir, "commands");
|
|
2924
|
+
if (!existsSync8(commandsDir)) return [];
|
|
2925
|
+
const entries = readdirSync4(commandsDir, { withFileTypes: true });
|
|
1698
2926
|
const commands = [];
|
|
1699
2927
|
for (const entry of entries) {
|
|
1700
2928
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
1701
|
-
const raw =
|
|
2929
|
+
const raw = readFileSync7(join8(commandsDir, entry.name), "utf-8");
|
|
1702
2930
|
const { metadata, content } = parseSkillFrontmatter(raw);
|
|
1703
2931
|
const name = typeof metadata.name === "string" ? metadata.name : entry.name.replace(/\.md$/, "");
|
|
1704
2932
|
const description = typeof metadata.description === "string" ? metadata.description : "";
|
|
@@ -1713,10 +2941,10 @@ var BundlePluginLoader = class {
|
|
|
1713
2941
|
}
|
|
1714
2942
|
/** Load hooks from hooks/hooks.json if present. */
|
|
1715
2943
|
loadHooks(pluginDir) {
|
|
1716
|
-
const hooksPath =
|
|
1717
|
-
if (!
|
|
2944
|
+
const hooksPath = join8(pluginDir, "hooks", "hooks.json");
|
|
2945
|
+
if (!existsSync8(hooksPath)) return {};
|
|
1718
2946
|
try {
|
|
1719
|
-
const raw =
|
|
2947
|
+
const raw = readFileSync7(hooksPath, "utf-8");
|
|
1720
2948
|
const data = JSON.parse(raw);
|
|
1721
2949
|
if (typeof data === "object" && data !== null) {
|
|
1722
2950
|
return data;
|
|
@@ -1728,12 +2956,12 @@ var BundlePluginLoader = class {
|
|
|
1728
2956
|
}
|
|
1729
2957
|
/** Load MCP server configuration if present. Checks `.mcp.json` at plugin root first. */
|
|
1730
2958
|
loadMcpConfig(pluginDir) {
|
|
1731
|
-
const primaryPath =
|
|
1732
|
-
const fallbackPath =
|
|
1733
|
-
const mcpPath =
|
|
1734
|
-
if (!
|
|
2959
|
+
const primaryPath = join8(pluginDir, ".mcp.json");
|
|
2960
|
+
const fallbackPath = join8(pluginDir, ".claude-plugin", "mcp.json");
|
|
2961
|
+
const mcpPath = existsSync8(primaryPath) ? primaryPath : fallbackPath;
|
|
2962
|
+
if (!existsSync8(mcpPath)) return void 0;
|
|
1735
2963
|
try {
|
|
1736
|
-
const raw =
|
|
2964
|
+
const raw = readFileSync7(mcpPath, "utf-8");
|
|
1737
2965
|
return JSON.parse(raw);
|
|
1738
2966
|
} catch {
|
|
1739
2967
|
return void 0;
|
|
@@ -1741,10 +2969,10 @@ var BundlePluginLoader = class {
|
|
|
1741
2969
|
}
|
|
1742
2970
|
/** Load agent definitions from agents/ directory if present. */
|
|
1743
2971
|
loadAgents(pluginDir) {
|
|
1744
|
-
const agentsDir =
|
|
1745
|
-
if (!
|
|
2972
|
+
const agentsDir = join8(pluginDir, "agents");
|
|
2973
|
+
if (!existsSync8(agentsDir)) return [];
|
|
1746
2974
|
try {
|
|
1747
|
-
const entries =
|
|
2975
|
+
const entries = readdirSync4(agentsDir, { withFileTypes: true });
|
|
1748
2976
|
return entries.filter((e) => e.isDirectory() || e.name.endsWith(".md")).map((e) => e.name.replace(/\.md$/, ""));
|
|
1749
2977
|
} catch {
|
|
1750
2978
|
return [];
|
|
@@ -1753,9 +2981,9 @@ var BundlePluginLoader = class {
|
|
|
1753
2981
|
};
|
|
1754
2982
|
|
|
1755
2983
|
// src/plugins/bundle-plugin-installer.ts
|
|
1756
|
-
import { execSync } from "child_process";
|
|
1757
|
-
import { cpSync, existsSync as
|
|
1758
|
-
import { join as
|
|
2984
|
+
import { execSync as execSync2 } from "child_process";
|
|
2985
|
+
import { cpSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
2986
|
+
import { join as join9, dirname as dirname3 } from "path";
|
|
1759
2987
|
var GIT_CLONE_TIMEOUT_MS = 6e4;
|
|
1760
2988
|
var BundlePluginInstaller = class {
|
|
1761
2989
|
pluginsDir;
|
|
@@ -1766,8 +2994,8 @@ var BundlePluginInstaller = class {
|
|
|
1766
2994
|
exec;
|
|
1767
2995
|
constructor(options) {
|
|
1768
2996
|
this.pluginsDir = options.pluginsDir;
|
|
1769
|
-
this.cacheDir =
|
|
1770
|
-
this.registryPath =
|
|
2997
|
+
this.cacheDir = join9(this.pluginsDir, "cache");
|
|
2998
|
+
this.registryPath = join9(this.pluginsDir, "installed_plugins.json");
|
|
1771
2999
|
this.settingsStore = options.settingsStore;
|
|
1772
3000
|
this.marketplaceClient = options.marketplaceClient;
|
|
1773
3001
|
this.exec = options.exec ?? this.defaultExec;
|
|
@@ -1787,8 +3015,8 @@ var BundlePluginInstaller = class {
|
|
|
1787
3015
|
throw new Error(`Plugin "${pluginName}" not found in marketplace "${marketplaceName}"`);
|
|
1788
3016
|
}
|
|
1789
3017
|
const version = this.resolveVersion(entry, marketplaceName);
|
|
1790
|
-
const targetDir =
|
|
1791
|
-
if (
|
|
3018
|
+
const targetDir = join9(this.cacheDir, marketplaceName, pluginName, version);
|
|
3019
|
+
if (existsSync9(targetDir)) {
|
|
1792
3020
|
throw new Error(
|
|
1793
3021
|
`Plugin "${pluginName}" version "${version}" is already installed from "${marketplaceName}"`
|
|
1794
3022
|
);
|
|
@@ -1815,7 +3043,7 @@ var BundlePluginInstaller = class {
|
|
|
1815
3043
|
if (!record) {
|
|
1816
3044
|
throw new Error(`Plugin "${pluginId}" is not installed`);
|
|
1817
3045
|
}
|
|
1818
|
-
if (
|
|
3046
|
+
if (existsSync9(record.installPath)) {
|
|
1819
3047
|
rmSync(record.installPath, { recursive: true, force: true });
|
|
1820
3048
|
}
|
|
1821
3049
|
delete registry[pluginId];
|
|
@@ -1867,8 +3095,8 @@ var BundlePluginInstaller = class {
|
|
|
1867
3095
|
try {
|
|
1868
3096
|
if (typeof source === "string") {
|
|
1869
3097
|
const marketplaceDir = this.marketplaceClient.getMarketplaceDir(marketplaceName);
|
|
1870
|
-
const sourcePath =
|
|
1871
|
-
if (!
|
|
3098
|
+
const sourcePath = join9(marketplaceDir, source);
|
|
3099
|
+
if (!existsSync9(sourcePath)) {
|
|
1872
3100
|
throw new Error(
|
|
1873
3101
|
`Plugin source path "${source}" not found in marketplace "${marketplaceName}"`
|
|
1874
3102
|
);
|
|
@@ -1885,7 +3113,7 @@ var BundlePluginInstaller = class {
|
|
|
1885
3113
|
throw new Error(`Unknown source type: ${JSON.stringify(source)}`);
|
|
1886
3114
|
}
|
|
1887
3115
|
} catch (err) {
|
|
1888
|
-
if (
|
|
3116
|
+
if (existsSync9(targetDir)) {
|
|
1889
3117
|
rmSync(targetDir, { recursive: true, force: true });
|
|
1890
3118
|
}
|
|
1891
3119
|
throw err;
|
|
@@ -1904,11 +3132,11 @@ var BundlePluginInstaller = class {
|
|
|
1904
3132
|
}
|
|
1905
3133
|
/** Read the installed_plugins.json registry. */
|
|
1906
3134
|
readRegistry() {
|
|
1907
|
-
if (!
|
|
3135
|
+
if (!existsSync9(this.registryPath)) {
|
|
1908
3136
|
return {};
|
|
1909
3137
|
}
|
|
1910
3138
|
try {
|
|
1911
|
-
const raw =
|
|
3139
|
+
const raw = readFileSync8(this.registryPath, "utf-8");
|
|
1912
3140
|
const data = JSON.parse(raw);
|
|
1913
3141
|
if (typeof data === "object" && data !== null) {
|
|
1914
3142
|
return data;
|
|
@@ -1921,31 +3149,31 @@ var BundlePluginInstaller = class {
|
|
|
1921
3149
|
/** Write the installed_plugins.json registry. */
|
|
1922
3150
|
writeRegistry(registry) {
|
|
1923
3151
|
const dir = dirname3(this.registryPath);
|
|
1924
|
-
if (!
|
|
3152
|
+
if (!existsSync9(dir)) {
|
|
1925
3153
|
mkdirSync3(dir, { recursive: true });
|
|
1926
3154
|
}
|
|
1927
3155
|
writeFileSync2(this.registryPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
1928
3156
|
}
|
|
1929
3157
|
/** Default exec implementation using child_process. */
|
|
1930
3158
|
defaultExec(command, options) {
|
|
1931
|
-
return
|
|
3159
|
+
return execSync2(command, { timeout: options.timeout, stdio: "pipe" });
|
|
1932
3160
|
}
|
|
1933
3161
|
};
|
|
1934
3162
|
|
|
1935
3163
|
// src/plugins/marketplace-client.ts
|
|
1936
|
-
import { execSync as
|
|
1937
|
-
import { cpSync as cpSync2, existsSync as
|
|
1938
|
-
import { join as
|
|
3164
|
+
import { execSync as execSync3 } from "child_process";
|
|
3165
|
+
import { cpSync as cpSync2, existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync10, renameSync, rmSync as rmSync3 } from "fs";
|
|
3166
|
+
import { join as join11 } from "path";
|
|
1939
3167
|
|
|
1940
3168
|
// src/plugins/marketplace-registry.ts
|
|
1941
|
-
import { existsSync as
|
|
1942
|
-
import { join as
|
|
3169
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
3170
|
+
import { join as join10, dirname as dirname4 } from "path";
|
|
1943
3171
|
function readRegistry(registryPath) {
|
|
1944
|
-
if (!
|
|
3172
|
+
if (!existsSync10(registryPath)) {
|
|
1945
3173
|
return {};
|
|
1946
3174
|
}
|
|
1947
3175
|
try {
|
|
1948
|
-
const raw =
|
|
3176
|
+
const raw = readFileSync9(registryPath, "utf-8");
|
|
1949
3177
|
const data = JSON.parse(raw);
|
|
1950
3178
|
if (typeof data === "object" && data !== null) {
|
|
1951
3179
|
return data;
|
|
@@ -1957,17 +3185,17 @@ function readRegistry(registryPath) {
|
|
|
1957
3185
|
}
|
|
1958
3186
|
function writeRegistry(registryPath, registry) {
|
|
1959
3187
|
const dir = dirname4(registryPath);
|
|
1960
|
-
if (!
|
|
3188
|
+
if (!existsSync10(dir)) {
|
|
1961
3189
|
mkdirSync4(dir, { recursive: true });
|
|
1962
3190
|
}
|
|
1963
3191
|
writeFileSync3(registryPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
1964
3192
|
}
|
|
1965
3193
|
function removeInstalledPluginsForMarketplace(pluginsDir, marketplaceName) {
|
|
1966
|
-
const installedPath =
|
|
1967
|
-
if (!
|
|
3194
|
+
const installedPath = join10(pluginsDir, "installed_plugins.json");
|
|
3195
|
+
if (!existsSync10(installedPath)) return;
|
|
1968
3196
|
let registry;
|
|
1969
3197
|
try {
|
|
1970
|
-
const raw =
|
|
3198
|
+
const raw = readFileSync9(installedPath, "utf-8");
|
|
1971
3199
|
const data = JSON.parse(raw);
|
|
1972
3200
|
if (typeof data !== "object" || data === null) return;
|
|
1973
3201
|
registry = data;
|
|
@@ -1977,7 +3205,7 @@ function removeInstalledPluginsForMarketplace(pluginsDir, marketplaceName) {
|
|
|
1977
3205
|
let changed = false;
|
|
1978
3206
|
for (const [pluginId, record] of Object.entries(registry)) {
|
|
1979
3207
|
if (record.marketplace === marketplaceName) {
|
|
1980
|
-
if (record.installPath &&
|
|
3208
|
+
if (record.installPath && existsSync10(record.installPath)) {
|
|
1981
3209
|
rmSync2(record.installPath, { recursive: true, force: true });
|
|
1982
3210
|
}
|
|
1983
3211
|
delete registry[pluginId];
|
|
@@ -1986,7 +3214,7 @@ function removeInstalledPluginsForMarketplace(pluginsDir, marketplaceName) {
|
|
|
1986
3214
|
}
|
|
1987
3215
|
if (changed) {
|
|
1988
3216
|
const dir = dirname4(installedPath);
|
|
1989
|
-
if (!
|
|
3217
|
+
if (!existsSync10(dir)) {
|
|
1990
3218
|
mkdirSync4(dir, { recursive: true });
|
|
1991
3219
|
}
|
|
1992
3220
|
writeFileSync3(installedPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
@@ -2003,8 +3231,8 @@ var MarketplaceClient = class {
|
|
|
2003
3231
|
constructor(options) {
|
|
2004
3232
|
this.pluginsDir = options.pluginsDir;
|
|
2005
3233
|
this.exec = options.exec ?? this.defaultExec;
|
|
2006
|
-
this.marketplacesDir =
|
|
2007
|
-
this.registryPath =
|
|
3234
|
+
this.marketplacesDir = join11(this.pluginsDir, "marketplaces");
|
|
3235
|
+
this.registryPath = join11(this.pluginsDir, "known_marketplaces.json");
|
|
2008
3236
|
}
|
|
2009
3237
|
/**
|
|
2010
3238
|
* Add a marketplace by cloning its repository.
|
|
@@ -2017,10 +3245,10 @@ var MarketplaceClient = class {
|
|
|
2017
3245
|
*/
|
|
2018
3246
|
addMarketplace(source) {
|
|
2019
3247
|
const tempName = "temp-" + Date.now().toString(36);
|
|
2020
|
-
const tempDir =
|
|
3248
|
+
const tempDir = join11(this.marketplacesDir, tempName);
|
|
2021
3249
|
mkdirSync5(this.marketplacesDir, { recursive: true });
|
|
2022
3250
|
if (source.type === "local") {
|
|
2023
|
-
if (!
|
|
3251
|
+
if (!existsSync11(source.path)) {
|
|
2024
3252
|
throw new Error(`Local marketplace path does not exist: ${source.path}`);
|
|
2025
3253
|
}
|
|
2026
3254
|
cpSync2(source.path, tempDir, { recursive: true });
|
|
@@ -2034,8 +3262,8 @@ var MarketplaceClient = class {
|
|
|
2034
3262
|
throw new Error(`Failed to clone marketplace: ${message}`);
|
|
2035
3263
|
}
|
|
2036
3264
|
}
|
|
2037
|
-
const manifestPath =
|
|
2038
|
-
if (!
|
|
3265
|
+
const manifestPath = join11(tempDir, ".claude-plugin", "marketplace.json");
|
|
3266
|
+
if (!existsSync11(manifestPath)) {
|
|
2039
3267
|
rmSync3(tempDir, { recursive: true, force: true });
|
|
2040
3268
|
throw new Error(
|
|
2041
3269
|
source.type === "local" ? "Local directory does not contain .claude-plugin/marketplace.json" : "Cloned repository does not contain .claude-plugin/marketplace.json"
|
|
@@ -2052,7 +3280,7 @@ var MarketplaceClient = class {
|
|
|
2052
3280
|
rmSync3(tempDir, { recursive: true, force: true });
|
|
2053
3281
|
throw new Error(`Marketplace "${name}" already exists`);
|
|
2054
3282
|
}
|
|
2055
|
-
const finalDir =
|
|
3283
|
+
const finalDir = join11(this.marketplacesDir, name);
|
|
2056
3284
|
renameSync(tempDir, finalDir);
|
|
2057
3285
|
registry[name] = {
|
|
2058
3286
|
source,
|
|
@@ -2074,7 +3302,7 @@ var MarketplaceClient = class {
|
|
|
2074
3302
|
throw new Error(`Marketplace "${name}" not found`);
|
|
2075
3303
|
}
|
|
2076
3304
|
removeInstalledPluginsForMarketplace(this.pluginsDir, name);
|
|
2077
|
-
if (
|
|
3305
|
+
if (existsSync11(entry.installLocation)) {
|
|
2078
3306
|
rmSync3(entry.installLocation, { recursive: true, force: true });
|
|
2079
3307
|
}
|
|
2080
3308
|
delete registry[name];
|
|
@@ -2091,12 +3319,12 @@ var MarketplaceClient = class {
|
|
|
2091
3319
|
if (!entry) {
|
|
2092
3320
|
throw new Error(`Marketplace "${name}" not found`);
|
|
2093
3321
|
}
|
|
2094
|
-
if (!
|
|
3322
|
+
if (!existsSync11(entry.installLocation)) {
|
|
2095
3323
|
throw new Error(`Marketplace directory for "${name}" does not exist`);
|
|
2096
3324
|
}
|
|
2097
3325
|
if (entry.source.type === "local") {
|
|
2098
3326
|
const localSource = entry.source;
|
|
2099
|
-
if (!
|
|
3327
|
+
if (!existsSync11(localSource.path)) {
|
|
2100
3328
|
throw new Error(`Local marketplace path does not exist: ${localSource.path}`);
|
|
2101
3329
|
}
|
|
2102
3330
|
rmSync3(entry.installLocation, { recursive: true, force: true });
|
|
@@ -2129,8 +3357,8 @@ var MarketplaceClient = class {
|
|
|
2129
3357
|
if (!entry) {
|
|
2130
3358
|
throw new Error(`Marketplace "${marketplaceName}" not found`);
|
|
2131
3359
|
}
|
|
2132
|
-
const manifestPath =
|
|
2133
|
-
if (!
|
|
3360
|
+
const manifestPath = join11(entry.installLocation, ".claude-plugin", "marketplace.json");
|
|
3361
|
+
if (!existsSync11(manifestPath)) {
|
|
2134
3362
|
throw new Error(
|
|
2135
3363
|
`Marketplace "${marketplaceName}" does not contain .claude-plugin/marketplace.json`
|
|
2136
3364
|
);
|
|
@@ -2193,7 +3421,7 @@ var MarketplaceClient = class {
|
|
|
2193
3421
|
}
|
|
2194
3422
|
/** Read and parse a marketplace.json from a file path. */
|
|
2195
3423
|
readManifestFromPath(path) {
|
|
2196
|
-
const raw =
|
|
3424
|
+
const raw = readFileSync10(path, "utf-8");
|
|
2197
3425
|
const data = JSON.parse(raw);
|
|
2198
3426
|
if (typeof data !== "object" || data === null) {
|
|
2199
3427
|
throw new Error("Invalid marketplace manifest: not an object");
|
|
@@ -2206,14 +3434,14 @@ var MarketplaceClient = class {
|
|
|
2206
3434
|
}
|
|
2207
3435
|
/** Default exec implementation using child_process. */
|
|
2208
3436
|
defaultExec(command, options) {
|
|
2209
|
-
return
|
|
3437
|
+
return execSync3(command, { timeout: options.timeout, stdio: "pipe" });
|
|
2210
3438
|
}
|
|
2211
3439
|
};
|
|
2212
3440
|
|
|
2213
3441
|
// src/plugins/plugin-hooks-merger.ts
|
|
2214
|
-
import { join as
|
|
3442
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
2215
3443
|
function buildPluginEnv(plugin) {
|
|
2216
|
-
const dataDir =
|
|
3444
|
+
const dataDir = join12(dirname5(dirname5(plugin.pluginDir)), "data", plugin.manifest.name);
|
|
2217
3445
|
return {
|
|
2218
3446
|
CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
|
|
2219
3447
|
CLAUDE_PLUGIN_PATH: plugin.pluginDir,
|
|
@@ -2275,36 +3503,39 @@ function mergeHooksIntoConfig(configHooks, pluginHooks) {
|
|
|
2275
3503
|
}
|
|
2276
3504
|
|
|
2277
3505
|
// src/interactive/interactive-session-init.ts
|
|
2278
|
-
import { homedir as
|
|
2279
|
-
import { join as
|
|
3506
|
+
import { homedir as homedir4 } from "os";
|
|
3507
|
+
import { join as join13 } from "path";
|
|
2280
3508
|
async function createInteractiveSession(options) {
|
|
2281
3509
|
const cwd = options.cwd;
|
|
2282
3510
|
const [config, context, projectInfo] = await Promise.all([
|
|
2283
3511
|
loadConfig(cwd),
|
|
2284
|
-
loadContext(cwd),
|
|
2285
|
-
detectProject(cwd)
|
|
3512
|
+
options.bare ? Promise.resolve({ agentsMd: "", claudeMd: "" }) : loadContext(cwd),
|
|
3513
|
+
options.bare ? Promise.resolve({ type: "unknown", language: "unknown" }) : detectProject(cwd)
|
|
2286
3514
|
]);
|
|
2287
|
-
const pluginsDir =
|
|
3515
|
+
const pluginsDir = join13(homedir4(), ".robota", "plugins");
|
|
2288
3516
|
const pluginLoader = new BundlePluginLoader(pluginsDir);
|
|
2289
3517
|
let mergedConfig = config;
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
3518
|
+
if (!options.bare) {
|
|
3519
|
+
try {
|
|
3520
|
+
const plugins = pluginLoader.loadPluginsSync();
|
|
3521
|
+
if (plugins.length > 0) {
|
|
3522
|
+
const pluginHooks = mergePluginHooks(plugins);
|
|
3523
|
+
mergedConfig = {
|
|
3524
|
+
...config,
|
|
3525
|
+
hooks: mergeHooksIntoConfig(
|
|
3526
|
+
config.hooks,
|
|
3527
|
+
pluginHooks
|
|
3528
|
+
)
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
} catch {
|
|
2301
3532
|
}
|
|
2302
|
-
} catch {
|
|
2303
3533
|
}
|
|
2304
3534
|
const paths = projectPaths(cwd);
|
|
2305
3535
|
const sessionId = options.resumeSessionId && !options.forkSession ? options.resumeSessionId : void 0;
|
|
2306
3536
|
return createSession({
|
|
2307
3537
|
config: mergedConfig,
|
|
3538
|
+
cwd,
|
|
2308
3539
|
context,
|
|
2309
3540
|
projectInfo,
|
|
2310
3541
|
permissionMode: options.permissionMode,
|
|
@@ -2315,7 +3546,22 @@ async function createInteractiveSession(options) {
|
|
|
2315
3546
|
provider: options.provider,
|
|
2316
3547
|
onTextDelta: options.onTextDelta,
|
|
2317
3548
|
onToolExecution: options.onToolExecution,
|
|
2318
|
-
sessionId
|
|
3549
|
+
sessionId,
|
|
3550
|
+
allowedTools: options.allowedTools,
|
|
3551
|
+
appendSystemPrompt: options.appendSystemPrompt,
|
|
3552
|
+
backgroundTaskRunners: options.backgroundTaskRunners,
|
|
3553
|
+
subagentRunnerFactory: options.subagentRunnerFactory,
|
|
3554
|
+
...options.commandModules?.some(
|
|
3555
|
+
(module) => module.sessionRequirements?.includes("agent-runtime")
|
|
3556
|
+
) ? { enableAgentRuntime: true } : {},
|
|
3557
|
+
...options.commandModules || options.commandDescriptors ? {
|
|
3558
|
+
commandDescriptors: [
|
|
3559
|
+
...options.commandDescriptors ?? [],
|
|
3560
|
+
...options.commandModules?.flatMap((module) => module.commandDescriptors ?? []) ?? []
|
|
3561
|
+
]
|
|
3562
|
+
} : {},
|
|
3563
|
+
modelCommandExecutor: options.modelCommandExecutor,
|
|
3564
|
+
isModelCommandInvocable: options.isModelCommandInvocable
|
|
2319
3565
|
});
|
|
2320
3566
|
}
|
|
2321
3567
|
function injectSavedMessage(session, msg) {
|
|
@@ -2334,9 +3580,25 @@ function injectSavedMessage(session, msg) {
|
|
|
2334
3580
|
function loadSessionRecord(sessionStore, resumeSessionId, forkSession, existingSession) {
|
|
2335
3581
|
const record = sessionStore.load(resumeSessionId);
|
|
2336
3582
|
if (!record) {
|
|
2337
|
-
return {
|
|
3583
|
+
return {
|
|
3584
|
+
history: [],
|
|
3585
|
+
sessionName: void 0,
|
|
3586
|
+
pendingRestoreMessages: null,
|
|
3587
|
+
backgroundTasks: [],
|
|
3588
|
+
backgroundTaskEvents: [],
|
|
3589
|
+
backgroundJobGroups: [],
|
|
3590
|
+
backgroundJobGroupEvents: []
|
|
3591
|
+
};
|
|
2338
3592
|
}
|
|
2339
3593
|
const history = record.history ?? [];
|
|
3594
|
+
const restoredBackgroundTasks = record.backgroundTasks ?? [];
|
|
3595
|
+
const restoredBackgroundTaskEvents = record.backgroundTaskEvents ?? [];
|
|
3596
|
+
const backgroundJobGroups = record.backgroundJobGroups ?? [];
|
|
3597
|
+
const backgroundJobGroupEvents = record.backgroundJobGroupEvents ?? [];
|
|
3598
|
+
const { backgroundTasks, backgroundTaskEvents } = reconcileRestoredBackgroundTasks(
|
|
3599
|
+
restoredBackgroundTasks,
|
|
3600
|
+
restoredBackgroundTaskEvents
|
|
3601
|
+
);
|
|
2340
3602
|
const sessionName = record.name;
|
|
2341
3603
|
let pendingRestoreMessages = null;
|
|
2342
3604
|
if (!forkSession && record.messages) {
|
|
@@ -2348,7 +3610,44 @@ function loadSessionRecord(sessionStore, resumeSessionId, forkSession, existingS
|
|
|
2348
3610
|
pendingRestoreMessages = record.messages;
|
|
2349
3611
|
}
|
|
2350
3612
|
}
|
|
2351
|
-
return {
|
|
3613
|
+
return {
|
|
3614
|
+
history,
|
|
3615
|
+
sessionName,
|
|
3616
|
+
pendingRestoreMessages,
|
|
3617
|
+
backgroundTasks,
|
|
3618
|
+
backgroundTaskEvents,
|
|
3619
|
+
backgroundJobGroups,
|
|
3620
|
+
backgroundJobGroupEvents
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
function reconcileRestoredBackgroundTasks(tasks, events) {
|
|
3624
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3625
|
+
const syntheticEvents = [];
|
|
3626
|
+
const backgroundTasks = tasks.map((task) => {
|
|
3627
|
+
if (isRestoredTerminalStatus(task.status)) return task;
|
|
3628
|
+
const reconciled = {
|
|
3629
|
+
...task,
|
|
3630
|
+
status: "failed",
|
|
3631
|
+
timeoutReason: "stale_worker",
|
|
3632
|
+
error: {
|
|
3633
|
+
category: "timeout",
|
|
3634
|
+
message: "Restored background task is stale; worker cannot be reattached",
|
|
3635
|
+
recoverable: true
|
|
3636
|
+
},
|
|
3637
|
+
unread: true,
|
|
3638
|
+
completedAt: now,
|
|
3639
|
+
updatedAt: now
|
|
3640
|
+
};
|
|
3641
|
+
syntheticEvents.push({ type: "background_task_failed", task: reconciled });
|
|
3642
|
+
return reconciled;
|
|
3643
|
+
});
|
|
3644
|
+
return {
|
|
3645
|
+
backgroundTasks,
|
|
3646
|
+
backgroundTaskEvents: [...events, ...syntheticEvents]
|
|
3647
|
+
};
|
|
3648
|
+
}
|
|
3649
|
+
function isRestoredTerminalStatus(status) {
|
|
3650
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
2352
3651
|
}
|
|
2353
3652
|
|
|
2354
3653
|
// src/interactive/interactive-session.ts
|
|
@@ -2370,10 +3669,24 @@ var InteractiveSession = class {
|
|
|
2370
3669
|
sessionName;
|
|
2371
3670
|
cwd;
|
|
2372
3671
|
pendingRestoreMessages = null;
|
|
3672
|
+
backgroundTasks = [];
|
|
3673
|
+
backgroundTaskEvents = [];
|
|
3674
|
+
backgroundJobGroups = [];
|
|
3675
|
+
backgroundJobGroupEvents = [];
|
|
2373
3676
|
resumeSessionId;
|
|
2374
3677
|
forkSession;
|
|
3678
|
+
backgroundTaskUnsubscribe = null;
|
|
3679
|
+
backgroundJobUnsubscribe = null;
|
|
3680
|
+
backgroundJobOrchestrator = null;
|
|
3681
|
+
commandModules;
|
|
3682
|
+
shuttingDown = false;
|
|
3683
|
+
shutdownPromise = null;
|
|
2375
3684
|
constructor(options) {
|
|
2376
|
-
this.
|
|
3685
|
+
this.commandModules = "commandModules" in options ? options.commandModules ?? [] : [];
|
|
3686
|
+
this.commandExecutor = new SystemCommandExecutor([
|
|
3687
|
+
...createSystemCommands(),
|
|
3688
|
+
...this.commandModules.flatMap((module) => module.systemCommands ?? [])
|
|
3689
|
+
]);
|
|
2377
3690
|
this.sessionStore = options.sessionStore;
|
|
2378
3691
|
this.sessionName = options.sessionName;
|
|
2379
3692
|
this.cwd = ("cwd" in options ? options.cwd : void 0) ?? "";
|
|
@@ -2395,8 +3708,14 @@ var InteractiveSession = class {
|
|
|
2395
3708
|
);
|
|
2396
3709
|
if (restored.history.length > 0) this.history = restored.history;
|
|
2397
3710
|
if (restored.sessionName) this.sessionName = restored.sessionName;
|
|
3711
|
+
this.backgroundTasks = restored.backgroundTasks;
|
|
3712
|
+
this.backgroundTaskEvents = restored.backgroundTaskEvents;
|
|
3713
|
+
this.backgroundJobGroups = restored.backgroundJobGroups;
|
|
3714
|
+
this.backgroundJobGroupEvents = restored.backgroundJobGroupEvents;
|
|
2398
3715
|
this.pendingRestoreMessages = restored.pendingRestoreMessages;
|
|
2399
3716
|
}
|
|
3717
|
+
if (this.initialized) this.subscribeBackgroundTaskEvents();
|
|
3718
|
+
if (this.initialized) this.persistCurrentSession();
|
|
2400
3719
|
}
|
|
2401
3720
|
async initializeAsync(options) {
|
|
2402
3721
|
this.session = await createInteractiveSession({
|
|
@@ -2408,13 +3727,26 @@ var InteractiveSession = class {
|
|
|
2408
3727
|
resumeSessionId: this.resumeSessionId,
|
|
2409
3728
|
forkSession: this.forkSession,
|
|
2410
3729
|
onTextDelta: (delta) => this.handleTextDelta(delta),
|
|
2411
|
-
onToolExecution: (event) => this.handleToolExecution(event)
|
|
3730
|
+
onToolExecution: (event) => this.handleToolExecution(event),
|
|
3731
|
+
bare: options.bare,
|
|
3732
|
+
allowedTools: options.allowedTools,
|
|
3733
|
+
appendSystemPrompt: options.appendSystemPrompt,
|
|
3734
|
+
backgroundTaskRunners: options.backgroundTaskRunners,
|
|
3735
|
+
subagentRunnerFactory: options.subagentRunnerFactory,
|
|
3736
|
+
...options.commandModules ? { commandModules: options.commandModules } : {},
|
|
3737
|
+
commandDescriptors: this.commandExecutor.listModelInvocableCommands(),
|
|
3738
|
+
...this.commandExecutor.listModelInvocableCommands().length > 0 ? {
|
|
3739
|
+
modelCommandExecutor: (command, args) => this.executeModelCommand(command, args),
|
|
3740
|
+
isModelCommandInvocable: (command) => this.commandExecutor.isModelInvocable(command)
|
|
3741
|
+
} : {}
|
|
2412
3742
|
});
|
|
2413
3743
|
if (this.pendingRestoreMessages) {
|
|
2414
3744
|
for (const msg of this.pendingRestoreMessages) injectSavedMessage(this.session, msg);
|
|
2415
3745
|
this.pendingRestoreMessages = null;
|
|
2416
3746
|
}
|
|
2417
3747
|
this.initialized = true;
|
|
3748
|
+
this.subscribeBackgroundTaskEvents();
|
|
3749
|
+
this.persistCurrentSession();
|
|
2418
3750
|
}
|
|
2419
3751
|
async ensureInitialized() {
|
|
2420
3752
|
if (!this.initialized && this.initPromise) await this.initPromise;
|
|
@@ -2437,6 +3769,7 @@ var InteractiveSession = class {
|
|
|
2437
3769
|
}
|
|
2438
3770
|
async submit(input, displayInput, rawInput) {
|
|
2439
3771
|
await this.ensureInitialized();
|
|
3772
|
+
if (this.shuttingDown) throw new Error("Interactive session is shutting down.");
|
|
2440
3773
|
if (this.executing) {
|
|
2441
3774
|
this.pendingPrompt = input;
|
|
2442
3775
|
this.pendingDisplayInput = displayInput;
|
|
@@ -2449,16 +3782,68 @@ var InteractiveSession = class {
|
|
|
2449
3782
|
await this.ensureInitialized();
|
|
2450
3783
|
return this.commandExecutor.execute(name, this, args);
|
|
2451
3784
|
}
|
|
3785
|
+
async executeModelCommand(name, args) {
|
|
3786
|
+
await this.ensureInitialized();
|
|
3787
|
+
return this.commandExecutor.executeModelInvocable(name, this, args);
|
|
3788
|
+
}
|
|
3789
|
+
async executeSkillCommand(skill, args, displayInput, rawInput) {
|
|
3790
|
+
await this.ensureInitialized();
|
|
3791
|
+
if (skill.context === "fork") {
|
|
3792
|
+
return this.executeForkSkillCommand(skill, args, displayInput);
|
|
3793
|
+
}
|
|
3794
|
+
const result = await executeSkill(
|
|
3795
|
+
skill,
|
|
3796
|
+
args,
|
|
3797
|
+
{
|
|
3798
|
+
runInFork: (content, options) => this.runSkillInFork(content, options)
|
|
3799
|
+
},
|
|
3800
|
+
{ sessionId: this.getSessionOrThrow().getSessionId() }
|
|
3801
|
+
);
|
|
3802
|
+
if (result.mode === "inject") {
|
|
3803
|
+
if (result.prompt) {
|
|
3804
|
+
await this.submit(result.prompt, displayInput, rawInput);
|
|
3805
|
+
}
|
|
3806
|
+
return result;
|
|
3807
|
+
}
|
|
3808
|
+
await this.applyForkSkillResult(result.result ?? "(empty response)");
|
|
3809
|
+
return result;
|
|
3810
|
+
}
|
|
2452
3811
|
listCommands() {
|
|
2453
3812
|
return this.commandExecutor.listCommands().map((cmd) => ({
|
|
2454
3813
|
name: cmd.name,
|
|
2455
3814
|
description: cmd.description
|
|
2456
3815
|
}));
|
|
2457
3816
|
}
|
|
3817
|
+
listModelInvocableCommands() {
|
|
3818
|
+
return this.commandExecutor.listModelInvocableCommands().map((cmd) => ({
|
|
3819
|
+
name: cmd.name,
|
|
3820
|
+
description: cmd.description
|
|
3821
|
+
}));
|
|
3822
|
+
}
|
|
2458
3823
|
abort() {
|
|
2459
3824
|
this.clearPendingQueue();
|
|
2460
3825
|
this.session?.abort();
|
|
2461
3826
|
}
|
|
3827
|
+
shutdown(options = {}) {
|
|
3828
|
+
if (this.shutdownPromise) return this.shutdownPromise;
|
|
3829
|
+
this.shuttingDown = true;
|
|
3830
|
+
this.shutdownPromise = (async () => {
|
|
3831
|
+
await this.ensureInitialized();
|
|
3832
|
+
this.clearPendingQueue();
|
|
3833
|
+
const session = this.session;
|
|
3834
|
+
session?.abort();
|
|
3835
|
+
await this.getBackgroundTaskManager()?.shutdown(options.message ?? "Session shutdown");
|
|
3836
|
+
this.backgroundTaskUnsubscribe?.();
|
|
3837
|
+
this.backgroundTaskUnsubscribe = null;
|
|
3838
|
+
this.backgroundJobUnsubscribe?.();
|
|
3839
|
+
this.backgroundJobUnsubscribe = null;
|
|
3840
|
+
this.backgroundJobOrchestrator?.dispose();
|
|
3841
|
+
this.backgroundJobOrchestrator = null;
|
|
3842
|
+
this.persistCurrentSession();
|
|
3843
|
+
await session?.shutdown({ reason: options.reason ?? "other" });
|
|
3844
|
+
})();
|
|
3845
|
+
return this.shutdownPromise;
|
|
3846
|
+
}
|
|
2462
3847
|
cancelQueue() {
|
|
2463
3848
|
this.clearPendingQueue();
|
|
2464
3849
|
}
|
|
@@ -2494,485 +3879,436 @@ var InteractiveSession = class {
|
|
|
2494
3879
|
getSession() {
|
|
2495
3880
|
return this.getSessionOrThrow();
|
|
2496
3881
|
}
|
|
2497
|
-
|
|
2498
|
-
this.
|
|
2499
|
-
if (this.sessionStore && this.session) {
|
|
2500
|
-
try {
|
|
2501
|
-
const id = this.getSessionOrThrow().getSessionId();
|
|
2502
|
-
const existing = this.sessionStore.load(id);
|
|
2503
|
-
if (existing) {
|
|
2504
|
-
existing.name = name;
|
|
2505
|
-
existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2506
|
-
this.sessionStore.save(existing);
|
|
2507
|
-
}
|
|
2508
|
-
} catch {
|
|
2509
|
-
}
|
|
2510
|
-
}
|
|
3882
|
+
listBackgroundTasks(filter) {
|
|
3883
|
+
return this.getBackgroundTaskManagerOrThrow().list(filter);
|
|
2511
3884
|
}
|
|
2512
|
-
|
|
2513
|
-
|
|
3885
|
+
getBackgroundTask(taskId) {
|
|
3886
|
+
return this.getBackgroundTaskManagerOrThrow().get(taskId);
|
|
2514
3887
|
}
|
|
2515
|
-
async
|
|
2516
|
-
this.
|
|
2517
|
-
this.
|
|
2518
|
-
this.emit("thinking", true);
|
|
2519
|
-
this.history.push(messageToHistoryEntry(createUserMessage(displayInput ?? input)));
|
|
2520
|
-
const historyBefore = this.getSessionOrThrow().getHistory().length;
|
|
2521
|
-
try {
|
|
2522
|
-
const response = await this.getSessionOrThrow().run(input, rawInput);
|
|
2523
|
-
this.flushStreaming();
|
|
2524
|
-
pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
|
|
2525
|
-
this.clearStreaming();
|
|
2526
|
-
const result = buildResult(
|
|
2527
|
-
response || "(empty response)",
|
|
2528
|
-
this.getSessionOrThrow().getHistory(),
|
|
2529
|
-
this.history,
|
|
2530
|
-
historyBefore,
|
|
2531
|
-
this.getContextState()
|
|
2532
|
-
);
|
|
2533
|
-
this.history.push(messageToHistoryEntry(createAssistantMessage(result.response)));
|
|
2534
|
-
this.emit("complete", result);
|
|
2535
|
-
this.emit("context_update", this.getContextState());
|
|
2536
|
-
} catch (err) {
|
|
2537
|
-
this.flushStreaming();
|
|
2538
|
-
if (isAbortError(err)) {
|
|
2539
|
-
const result = buildInterruptedResult(
|
|
2540
|
-
this.getSessionOrThrow().getHistory(),
|
|
2541
|
-
this.history,
|
|
2542
|
-
historyBefore,
|
|
2543
|
-
this.getContextState()
|
|
2544
|
-
);
|
|
2545
|
-
pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
|
|
2546
|
-
this.clearStreaming();
|
|
2547
|
-
if (result.response)
|
|
2548
|
-
this.history.push(messageToHistoryEntry(createAssistantMessage(result.response)));
|
|
2549
|
-
this.history.push(messageToHistoryEntry(createSystemMessage("Interrupted by user.")));
|
|
2550
|
-
this.emit("interrupted", result);
|
|
2551
|
-
} else {
|
|
2552
|
-
pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
|
|
2553
|
-
this.clearStreaming();
|
|
2554
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2555
|
-
this.history.push(messageToHistoryEntry(createSystemMessage(`Error: ${errMsg}`)));
|
|
2556
|
-
this.emit("error", err instanceof Error ? err : new Error(errMsg));
|
|
2557
|
-
}
|
|
2558
|
-
} finally {
|
|
2559
|
-
this.executing = false;
|
|
2560
|
-
this.emit("thinking", false);
|
|
2561
|
-
if (this.sessionStore && this.session) {
|
|
2562
|
-
persistSession(
|
|
2563
|
-
this.sessionStore,
|
|
2564
|
-
this.session,
|
|
2565
|
-
this.sessionName,
|
|
2566
|
-
this.cwd ?? "",
|
|
2567
|
-
this.history
|
|
2568
|
-
);
|
|
2569
|
-
}
|
|
2570
|
-
if (this.pendingPrompt) {
|
|
2571
|
-
const queued = this.pendingPrompt;
|
|
2572
|
-
const queuedDisplay = this.pendingDisplayInput;
|
|
2573
|
-
const queuedRaw = this.pendingRawInput;
|
|
2574
|
-
this.clearPendingQueue();
|
|
2575
|
-
setTimeout(() => this.executePrompt(queued, queuedDisplay, queuedRaw), 0);
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
3888
|
+
async cancelBackgroundTask(taskId, reason) {
|
|
3889
|
+
await this.ensureInitialized();
|
|
3890
|
+
await this.getBackgroundTaskManagerOrThrow().cancel(taskId, reason);
|
|
2578
3891
|
}
|
|
2579
|
-
|
|
2580
|
-
this.
|
|
2581
|
-
this.
|
|
2582
|
-
if (!this.flushTimer) {
|
|
2583
|
-
this.flushTimer = setTimeout(() => {
|
|
2584
|
-
this.flushTimer = null;
|
|
2585
|
-
}, STREAMING_FLUSH_INTERVAL_MS);
|
|
2586
|
-
}
|
|
3892
|
+
async closeBackgroundTask(taskId) {
|
|
3893
|
+
await this.ensureInitialized();
|
|
3894
|
+
await this.getBackgroundTaskManagerOrThrow().close(taskId);
|
|
2587
3895
|
}
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
3896
|
+
async sendBackgroundTask(taskId, input) {
|
|
3897
|
+
await this.ensureInitialized();
|
|
3898
|
+
await this.getBackgroundTaskManagerOrThrow().send(taskId, input);
|
|
3899
|
+
}
|
|
3900
|
+
async readBackgroundTaskLog(taskId, cursor) {
|
|
3901
|
+
await this.ensureInitialized();
|
|
3902
|
+
return this.getBackgroundTaskManagerOrThrow().readLog(taskId, cursor);
|
|
3903
|
+
}
|
|
3904
|
+
createBackgroundJobGroup(input) {
|
|
3905
|
+
const orchestrator = this.getBackgroundJobOrchestratorOrThrow();
|
|
3906
|
+
return orchestrator.createGroup({
|
|
3907
|
+
...input,
|
|
3908
|
+
parentSessionId: this.getSessionOrThrow().getSessionId()
|
|
3909
|
+
});
|
|
3910
|
+
}
|
|
3911
|
+
listBackgroundJobGroups() {
|
|
3912
|
+
return this.getBackgroundJobOrchestratorOrThrow().listGroups();
|
|
2599
3913
|
}
|
|
2600
|
-
|
|
2601
|
-
this.
|
|
2602
|
-
this.activeTools = [];
|
|
2603
|
-
if (this.flushTimer) {
|
|
2604
|
-
clearTimeout(this.flushTimer);
|
|
2605
|
-
this.flushTimer = null;
|
|
2606
|
-
}
|
|
3914
|
+
getBackgroundJobGroup(groupId) {
|
|
3915
|
+
return this.getBackgroundJobOrchestratorOrThrow().getGroup(groupId);
|
|
2607
3916
|
}
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
this.flushTimer = null;
|
|
2612
|
-
}
|
|
3917
|
+
async waitBackgroundJobGroup(groupId) {
|
|
3918
|
+
await this.ensureInitialized();
|
|
3919
|
+
return this.getBackgroundJobOrchestratorOrThrow().waitGroup(groupId);
|
|
2613
3920
|
}
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
provider: options.provider,
|
|
2621
|
-
permissionMode: options.permissionMode ?? "bypassPermissions",
|
|
2622
|
-
maxTurns: options.maxTurns,
|
|
2623
|
-
permissionHandler: options.permissionHandler
|
|
2624
|
-
});
|
|
2625
|
-
if (options.onTextDelta) {
|
|
2626
|
-
session.on("text_delta", options.onTextDelta);
|
|
3921
|
+
listAgentDefinitions() {
|
|
3922
|
+
const deps = retrieveAgentToolDeps(this.getSessionOrThrow());
|
|
3923
|
+
return (deps?.agentDefinitions ?? []).map((agent) => ({
|
|
3924
|
+
name: agent.name,
|
|
3925
|
+
description: agent.description
|
|
3926
|
+
}));
|
|
2627
3927
|
}
|
|
2628
|
-
|
|
2629
|
-
return
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
session.on("complete", onComplete);
|
|
2648
|
-
session.on("interrupted", onInterrupted);
|
|
2649
|
-
session.on("error", onError);
|
|
2650
|
-
session.submit(prompt).catch((err) => {
|
|
2651
|
-
cleanup();
|
|
2652
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
2653
|
-
});
|
|
3928
|
+
listAgentJobs() {
|
|
3929
|
+
return this.getSubagentManagerOrThrow().list();
|
|
3930
|
+
}
|
|
3931
|
+
async spawnAgentJob(input) {
|
|
3932
|
+
await this.ensureInitialized();
|
|
3933
|
+
const deps = this.getAgentToolDepsOrThrow();
|
|
3934
|
+
const definition = this.resolveAgentDefinition(input.agentType, deps);
|
|
3935
|
+
return this.getSubagentManagerOrThrow().spawn({
|
|
3936
|
+
type: input.agentType,
|
|
3937
|
+
label: input.label,
|
|
3938
|
+
parentSessionId: this.getSessionOrThrow().getSessionId(),
|
|
3939
|
+
mode: input.mode,
|
|
3940
|
+
depth: (deps.subagentDepth ?? 0) + 1,
|
|
3941
|
+
cwd: deps.cwd ?? this.cwd ?? process.cwd(),
|
|
3942
|
+
prompt: input.prompt,
|
|
3943
|
+
model: input.model ?? definition.model,
|
|
3944
|
+
isolation: input.isolation,
|
|
3945
|
+
allowedTools: definition.tools,
|
|
3946
|
+
disallowedTools: definition.disallowedTools
|
|
2654
3947
|
});
|
|
2655
|
-
};
|
|
2656
|
-
}
|
|
2657
|
-
|
|
2658
|
-
// src/commands/command-registry.ts
|
|
2659
|
-
var CommandRegistry = class {
|
|
2660
|
-
sources = [];
|
|
2661
|
-
addSource(source) {
|
|
2662
|
-
this.sources.push(source);
|
|
2663
3948
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
for (const source of this.sources) {
|
|
2668
|
-
all.push(...source.getCommands());
|
|
2669
|
-
}
|
|
2670
|
-
if (!filter) return all;
|
|
2671
|
-
const lower = filter.toLowerCase();
|
|
2672
|
-
return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
|
|
3949
|
+
async waitAgentJob(jobId) {
|
|
3950
|
+
await this.ensureInitialized();
|
|
3951
|
+
return this.getSubagentManagerOrThrow().wait(jobId);
|
|
2673
3952
|
}
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
(c) => c.source === "plugin" && c.name.includes(":") && c.name.endsWith(`:${shortName}`)
|
|
2678
|
-
);
|
|
2679
|
-
if (matches.length !== 1) return null;
|
|
2680
|
-
return matches[0].name;
|
|
3953
|
+
async sendAgentJob(jobId, prompt) {
|
|
3954
|
+
await this.ensureInitialized();
|
|
3955
|
+
await this.getSubagentManagerOrThrow().send(jobId, prompt);
|
|
2681
3956
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
3957
|
+
async cancelAgentJob(jobId, reason) {
|
|
3958
|
+
await this.ensureInitialized();
|
|
3959
|
+
await this.getSubagentManagerOrThrow().cancel(jobId, reason);
|
|
3960
|
+
}
|
|
3961
|
+
async closeAgentJob(jobId) {
|
|
3962
|
+
await this.ensureInitialized();
|
|
3963
|
+
await this.getSubagentManagerOrThrow().close(jobId);
|
|
3964
|
+
}
|
|
3965
|
+
setName(name) {
|
|
3966
|
+
this.sessionName = name;
|
|
3967
|
+
if (this.sessionStore && this.session) {
|
|
3968
|
+
try {
|
|
3969
|
+
const id = this.getSessionOrThrow().getSessionId();
|
|
3970
|
+
const existing = this.sessionStore.load(id);
|
|
3971
|
+
if (existing) {
|
|
3972
|
+
existing.name = name;
|
|
3973
|
+
existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3974
|
+
this.sessionStore.save(existing);
|
|
2689
3975
|
}
|
|
3976
|
+
} catch {
|
|
2690
3977
|
}
|
|
2691
3978
|
}
|
|
2692
|
-
return [];
|
|
2693
3979
|
}
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
3980
|
+
attachTransport(transport) {
|
|
3981
|
+
transport.attach(this);
|
|
3982
|
+
}
|
|
3983
|
+
async executeForkSkillCommand(skill, args, displayInput) {
|
|
3984
|
+
if (this.executing) {
|
|
3985
|
+
throw new Error("Cannot execute fork skill while another prompt is running.");
|
|
3986
|
+
}
|
|
3987
|
+
this.startForkSkillExecution(displayInput ?? `/${skill.name}`);
|
|
3988
|
+
try {
|
|
3989
|
+
const result = await executeSkill(
|
|
3990
|
+
skill,
|
|
3991
|
+
args,
|
|
3992
|
+
{ runInFork: (content, options) => this.runSkillInFork(content, options) },
|
|
3993
|
+
{ sessionId: this.getSessionOrThrow().getSessionId() }
|
|
3994
|
+
);
|
|
3995
|
+
await this.applyForkSkillResult(result.result ?? "(empty response)");
|
|
3996
|
+
return result;
|
|
3997
|
+
} catch (err) {
|
|
3998
|
+
this.recordForkSkillError(err instanceof Error ? err : new Error(String(err)));
|
|
3999
|
+
return { mode: "fork", result: "" };
|
|
4000
|
+
} finally {
|
|
4001
|
+
this.finishForkSkillExecution();
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
getBackgroundTaskManagerOrThrow() {
|
|
4005
|
+
const manager = this.getBackgroundTaskManager();
|
|
4006
|
+
if (!manager) {
|
|
4007
|
+
throw new Error("Background task manager is not available for this session.");
|
|
4008
|
+
}
|
|
4009
|
+
return manager;
|
|
4010
|
+
}
|
|
4011
|
+
getBackgroundTaskManager() {
|
|
4012
|
+
if (!this.session) return void 0;
|
|
4013
|
+
return retrieveSessionBackgroundTaskManager(this.session) ?? retrieveAgentToolDeps(this.session)?.backgroundTaskManager;
|
|
4014
|
+
}
|
|
4015
|
+
getBackgroundJobOrchestratorOrThrow() {
|
|
4016
|
+
if (this.backgroundJobOrchestrator) return this.backgroundJobOrchestrator;
|
|
4017
|
+
const manager = this.getBackgroundTaskManagerOrThrow();
|
|
4018
|
+
this.backgroundJobOrchestrator = new BackgroundJobOrchestrator({
|
|
4019
|
+
manager,
|
|
4020
|
+
initialGroups: this.backgroundJobGroups
|
|
2708
4021
|
});
|
|
4022
|
+
this.subscribeBackgroundJobGroupEvents();
|
|
4023
|
+
return this.backgroundJobOrchestrator;
|
|
2709
4024
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
source: "builtin",
|
|
2720
|
-
subcommands: [
|
|
2721
|
-
{ name: "plan", description: "Plan only, no execution", source: "builtin" },
|
|
2722
|
-
{ name: "default", description: "Ask before risky actions", source: "builtin" },
|
|
2723
|
-
{ name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
|
|
2724
|
-
{ name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
|
|
2725
|
-
]
|
|
2726
|
-
},
|
|
2727
|
-
{
|
|
2728
|
-
name: "model",
|
|
2729
|
-
description: "Select AI model",
|
|
2730
|
-
source: "builtin",
|
|
2731
|
-
subcommands: buildModelSubcommands()
|
|
2732
|
-
},
|
|
2733
|
-
{
|
|
2734
|
-
name: "language",
|
|
2735
|
-
description: "Set response language",
|
|
2736
|
-
source: "builtin",
|
|
2737
|
-
subcommands: [
|
|
2738
|
-
{ name: "ko", description: "Korean", source: "builtin" },
|
|
2739
|
-
{ name: "en", description: "English", source: "builtin" },
|
|
2740
|
-
{ name: "ja", description: "Japanese", source: "builtin" },
|
|
2741
|
-
{ name: "zh", description: "Chinese", source: "builtin" }
|
|
2742
|
-
]
|
|
2743
|
-
},
|
|
2744
|
-
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
2745
|
-
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
2746
|
-
{ name: "context", description: "Context window info", source: "builtin" },
|
|
2747
|
-
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
2748
|
-
{ name: "resume", description: "Resume a previous session", source: "builtin" },
|
|
2749
|
-
{ name: "rename", description: "Rename the current session", source: "builtin" },
|
|
2750
|
-
{ name: "plugin", description: "Manage plugins", source: "builtin" },
|
|
2751
|
-
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
2752
|
-
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
2753
|
-
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
2754
|
-
];
|
|
2755
|
-
}
|
|
2756
|
-
var BuiltinCommandSource = class {
|
|
2757
|
-
name = "builtin";
|
|
2758
|
-
commands;
|
|
2759
|
-
constructor() {
|
|
2760
|
-
this.commands = createBuiltinCommands();
|
|
4025
|
+
getAgentToolDepsOrThrow() {
|
|
4026
|
+
const deps = retrieveAgentToolDeps(this.getSessionOrThrow());
|
|
4027
|
+
if (!deps) {
|
|
4028
|
+
throw new Error("Agent runtime dependencies are not available for this session.");
|
|
4029
|
+
}
|
|
4030
|
+
if (!deps.backgroundTaskManager) {
|
|
4031
|
+
throw new Error("Background task manager is not available for this session.");
|
|
4032
|
+
}
|
|
4033
|
+
return deps;
|
|
2761
4034
|
}
|
|
2762
|
-
|
|
2763
|
-
|
|
4035
|
+
getSubagentManagerOrThrow() {
|
|
4036
|
+
const deps = this.getAgentToolDepsOrThrow();
|
|
4037
|
+
if (!deps.subagentManager) {
|
|
4038
|
+
throw new Error("Subagent manager is not available for this session.");
|
|
4039
|
+
}
|
|
4040
|
+
return deps.subagentManager;
|
|
2764
4041
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
import { join as join13, basename as basename2 } from "path";
|
|
2770
|
-
import { homedir as homedir4 } from "os";
|
|
2771
|
-
var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
|
|
2772
|
-
var LIST_KEYS2 = /* @__PURE__ */ new Set(["allowed-tools"]);
|
|
2773
|
-
function kebabToCamel(key) {
|
|
2774
|
-
return key.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
2775
|
-
}
|
|
2776
|
-
function parseFrontmatter2(content) {
|
|
2777
|
-
const lines = content.split("\n");
|
|
2778
|
-
if (lines[0]?.trim() !== "---") return null;
|
|
2779
|
-
const result = {};
|
|
2780
|
-
for (let i = 1; i < lines.length; i++) {
|
|
2781
|
-
const line = lines[i];
|
|
2782
|
-
if (line.trim() === "---") break;
|
|
2783
|
-
const match = line.match(/^([a-z][a-z0-9-]*):\s*(.+)/);
|
|
2784
|
-
if (!match) continue;
|
|
2785
|
-
const key = match[1];
|
|
2786
|
-
const rawValue = match[2].trim();
|
|
2787
|
-
const camelKey = kebabToCamel(key);
|
|
2788
|
-
if (BOOLEAN_KEYS.has(key)) {
|
|
2789
|
-
result[camelKey] = rawValue === "true";
|
|
2790
|
-
} else if (LIST_KEYS2.has(key)) {
|
|
2791
|
-
result[camelKey] = rawValue.split(",").map((s) => s.trim());
|
|
2792
|
-
} else {
|
|
2793
|
-
result[camelKey] = rawValue;
|
|
4042
|
+
resolveAgentDefinition(agentType, deps) {
|
|
4043
|
+
const definition = deps.customAgentRegistry?.(agentType);
|
|
4044
|
+
if (!definition) {
|
|
4045
|
+
throw new Error(`Unknown agent type: ${agentType}`);
|
|
2794
4046
|
}
|
|
4047
|
+
return definition;
|
|
4048
|
+
}
|
|
4049
|
+
subscribeBackgroundTaskEvents() {
|
|
4050
|
+
if (this.backgroundTaskUnsubscribe || !this.session) return;
|
|
4051
|
+
const manager = retrieveSessionBackgroundTaskManager(this.session) ?? retrieveAgentToolDeps(this.session)?.backgroundTaskManager;
|
|
4052
|
+
if (!manager) return;
|
|
4053
|
+
this.backgroundTaskUnsubscribe = manager.subscribe((event) => {
|
|
4054
|
+
this.recordBackgroundTaskEvent(event);
|
|
4055
|
+
this.emit("background_task_event", event);
|
|
4056
|
+
});
|
|
2795
4057
|
}
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
source: "skill",
|
|
2803
|
-
skillContent: content
|
|
2804
|
-
};
|
|
2805
|
-
if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
|
|
2806
|
-
if (frontmatter?.disableModelInvocation !== void 0)
|
|
2807
|
-
cmd.disableModelInvocation = frontmatter.disableModelInvocation;
|
|
2808
|
-
if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
|
|
2809
|
-
if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
|
|
2810
|
-
if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
|
|
2811
|
-
if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
|
|
2812
|
-
if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
|
|
2813
|
-
if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
|
|
2814
|
-
return cmd;
|
|
2815
|
-
}
|
|
2816
|
-
function scanSkillsDir(skillsDir) {
|
|
2817
|
-
if (!existsSync11(skillsDir)) return [];
|
|
2818
|
-
const commands = [];
|
|
2819
|
-
const entries = readdirSync4(skillsDir, { withFileTypes: true });
|
|
2820
|
-
for (const entry of entries) {
|
|
2821
|
-
if (!entry.isDirectory()) continue;
|
|
2822
|
-
const skillFile = join13(skillsDir, entry.name, "SKILL.md");
|
|
2823
|
-
if (!existsSync11(skillFile)) continue;
|
|
2824
|
-
const content = readFileSync10(skillFile, "utf-8");
|
|
2825
|
-
const frontmatter = parseFrontmatter2(content);
|
|
2826
|
-
commands.push(buildCommand(frontmatter, content, entry.name));
|
|
4058
|
+
subscribeBackgroundJobGroupEvents() {
|
|
4059
|
+
if (this.backgroundJobUnsubscribe || !this.backgroundJobOrchestrator) return;
|
|
4060
|
+
this.backgroundJobUnsubscribe = this.backgroundJobOrchestrator.subscribe((event) => {
|
|
4061
|
+
this.recordBackgroundJobGroupEvent(event);
|
|
4062
|
+
this.emit("background_job_group_event", event);
|
|
4063
|
+
});
|
|
2827
4064
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
const commands = [];
|
|
2833
|
-
const entries = readdirSync4(commandsDir, { withFileTypes: true });
|
|
2834
|
-
for (const entry of entries) {
|
|
2835
|
-
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
2836
|
-
const filePath = join13(commandsDir, entry.name);
|
|
2837
|
-
const content = readFileSync10(filePath, "utf-8");
|
|
2838
|
-
const frontmatter = parseFrontmatter2(content);
|
|
2839
|
-
const fallbackName = basename2(entry.name, ".md");
|
|
2840
|
-
commands.push(buildCommand(frontmatter, content, fallbackName));
|
|
4065
|
+
recordBackgroundTaskEvent(event) {
|
|
4066
|
+
this.backgroundTasks = this.getBackgroundTaskSnapshots();
|
|
4067
|
+
this.backgroundTaskEvents.push(event);
|
|
4068
|
+
this.persistCurrentSession();
|
|
2841
4069
|
}
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
4070
|
+
recordBackgroundJobGroupEvent(event) {
|
|
4071
|
+
this.backgroundJobGroups = this.getBackgroundJobGroupSnapshots();
|
|
4072
|
+
this.backgroundJobGroupEvents.push(event);
|
|
4073
|
+
this.persistCurrentSession();
|
|
4074
|
+
}
|
|
4075
|
+
getBackgroundTaskSnapshots() {
|
|
4076
|
+
try {
|
|
4077
|
+
return this.getBackgroundTaskManagerOrThrow().list();
|
|
4078
|
+
} catch {
|
|
4079
|
+
return this.backgroundTasks;
|
|
4080
|
+
}
|
|
2852
4081
|
}
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
4082
|
+
getBackgroundJobGroupSnapshots() {
|
|
4083
|
+
try {
|
|
4084
|
+
return this.backgroundJobOrchestrator?.listGroups() ?? this.backgroundJobGroups;
|
|
4085
|
+
} catch {
|
|
4086
|
+
return this.backgroundJobGroups;
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
persistCurrentSession() {
|
|
4090
|
+
if (!this.sessionStore || !this.session) return;
|
|
4091
|
+
this.backgroundTasks = this.getBackgroundTaskSnapshots();
|
|
4092
|
+
this.backgroundJobGroups = this.getBackgroundJobGroupSnapshots();
|
|
4093
|
+
persistSession(
|
|
4094
|
+
this.sessionStore,
|
|
4095
|
+
this.session,
|
|
4096
|
+
this.sessionName,
|
|
4097
|
+
this.cwd ?? "",
|
|
4098
|
+
this.history,
|
|
4099
|
+
{
|
|
4100
|
+
tasks: this.backgroundTasks,
|
|
4101
|
+
events: this.backgroundTaskEvents,
|
|
4102
|
+
groups: this.backgroundJobGroups,
|
|
4103
|
+
groupEvents: this.backgroundJobGroupEvents
|
|
2869
4104
|
}
|
|
4105
|
+
);
|
|
4106
|
+
}
|
|
4107
|
+
startForkSkillExecution(displayInput) {
|
|
4108
|
+
this.executing = true;
|
|
4109
|
+
this.clearStreaming();
|
|
4110
|
+
this.emit("thinking", true);
|
|
4111
|
+
this.history.push(messageToHistoryEntry(createUserMessage(displayInput)));
|
|
4112
|
+
}
|
|
4113
|
+
finishForkSkillExecution() {
|
|
4114
|
+
this.executing = false;
|
|
4115
|
+
this.emit("thinking", false);
|
|
4116
|
+
this.persistCurrentSession();
|
|
4117
|
+
if (!this.shuttingDown && this.pendingPrompt) {
|
|
4118
|
+
const queued = this.pendingPrompt;
|
|
4119
|
+
const queuedDisplay = this.pendingDisplayInput;
|
|
4120
|
+
const queuedRaw = this.pendingRawInput;
|
|
4121
|
+
this.clearPendingQueue();
|
|
4122
|
+
setTimeout(() => this.executePrompt(queued, queuedDisplay, queuedRaw), 0);
|
|
2870
4123
|
}
|
|
2871
|
-
this.cachedCommands = merged;
|
|
2872
|
-
return this.cachedCommands;
|
|
2873
4124
|
}
|
|
2874
|
-
|
|
2875
|
-
|
|
4125
|
+
recordForkSkillError(err) {
|
|
4126
|
+
this.history.push(messageToHistoryEntry(createSystemMessage(`Error: ${err.message}`)));
|
|
4127
|
+
this.emit("error", err);
|
|
2876
4128
|
}
|
|
2877
|
-
|
|
2878
|
-
|
|
4129
|
+
resolveForkAgentDefinition(agentType, options) {
|
|
4130
|
+
const deps = retrieveAgentToolDeps(this.getSessionOrThrow());
|
|
4131
|
+
const definition = deps?.customAgentRegistry?.(agentType) ?? getBuiltInAgent(agentType);
|
|
4132
|
+
if (!definition) {
|
|
4133
|
+
throw new Error(`Unknown agent type: ${agentType}`);
|
|
4134
|
+
}
|
|
4135
|
+
if (options.allowedTools) {
|
|
4136
|
+
return { ...definition, tools: options.allowedTools };
|
|
4137
|
+
}
|
|
4138
|
+
return definition;
|
|
2879
4139
|
}
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
this.
|
|
4140
|
+
async runSkillInFork(content, options) {
|
|
4141
|
+
const parentSession = this.getSessionOrThrow();
|
|
4142
|
+
const deps = retrieveAgentToolDeps(parentSession);
|
|
4143
|
+
if (!deps) {
|
|
4144
|
+
throw new Error("Fork execution is not available. Agent tool deps may not be initialized.");
|
|
4145
|
+
}
|
|
4146
|
+
const agentType = options.agent ?? "general-purpose";
|
|
4147
|
+
const agentDefinition = this.resolveForkAgentDefinition(agentType, options);
|
|
4148
|
+
const forkSession = createSubagentSession({
|
|
4149
|
+
agentDefinition,
|
|
4150
|
+
parentConfig: deps.config,
|
|
4151
|
+
parentContext: deps.context,
|
|
4152
|
+
parentTools: deps.tools,
|
|
4153
|
+
provider: deps.provider,
|
|
4154
|
+
terminal: deps.terminal,
|
|
4155
|
+
isForkWorker: true,
|
|
4156
|
+
permissionMode: deps.permissionMode,
|
|
4157
|
+
permissionHandler: deps.permissionHandler,
|
|
4158
|
+
hooks: deps.hooks,
|
|
4159
|
+
hookTypeExecutors: deps.hookTypeExecutors,
|
|
4160
|
+
onTextDelta: deps.onTextDelta,
|
|
4161
|
+
onToolExecution: deps.onToolExecution
|
|
4162
|
+
});
|
|
4163
|
+
return forkSession.run(content);
|
|
2888
4164
|
}
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
4165
|
+
async applyForkSkillResult(result) {
|
|
4166
|
+
this.flushStreaming();
|
|
4167
|
+
pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
|
|
4168
|
+
this.clearStreaming();
|
|
4169
|
+
const executionResult = {
|
|
4170
|
+
response: result,
|
|
4171
|
+
history: this.history,
|
|
4172
|
+
toolSummaries: [],
|
|
4173
|
+
contextState: this.getContextState()
|
|
4174
|
+
};
|
|
4175
|
+
this.history.push(messageToHistoryEntry(createAssistantMessage(result)));
|
|
4176
|
+
this.emit("complete", executionResult);
|
|
4177
|
+
this.emit("context_update", this.getContextState());
|
|
4178
|
+
}
|
|
4179
|
+
async executePrompt(input, displayInput, rawInput) {
|
|
4180
|
+
this.executing = true;
|
|
4181
|
+
this.clearStreaming();
|
|
4182
|
+
this.emit("thinking", true);
|
|
4183
|
+
this.history.push(messageToHistoryEntry(createUserMessage(displayInput ?? input)));
|
|
4184
|
+
const historyBefore = this.getSessionOrThrow().getHistory().length;
|
|
4185
|
+
try {
|
|
4186
|
+
const response = await this.getSessionOrThrow().run(input, rawInput);
|
|
4187
|
+
this.flushStreaming();
|
|
4188
|
+
pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
|
|
4189
|
+
this.clearStreaming();
|
|
4190
|
+
const result = buildResult(
|
|
4191
|
+
response || "(empty response)",
|
|
4192
|
+
this.getSessionOrThrow().getHistory(),
|
|
4193
|
+
this.history,
|
|
4194
|
+
historyBefore,
|
|
4195
|
+
this.getContextState()
|
|
4196
|
+
);
|
|
4197
|
+
this.history.push(messageToHistoryEntry(createAssistantMessage(result.response)));
|
|
4198
|
+
this.emit("complete", result);
|
|
4199
|
+
this.emit("context_update", this.getContextState());
|
|
4200
|
+
} catch (err) {
|
|
4201
|
+
this.flushStreaming();
|
|
4202
|
+
if (isAbortError(err)) {
|
|
4203
|
+
const result = buildInterruptedResult(
|
|
4204
|
+
this.getSessionOrThrow().getHistory(),
|
|
4205
|
+
this.history,
|
|
4206
|
+
historyBefore,
|
|
4207
|
+
this.getContextState()
|
|
4208
|
+
);
|
|
4209
|
+
pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
|
|
4210
|
+
this.clearStreaming();
|
|
4211
|
+
if (result.response)
|
|
4212
|
+
this.history.push(messageToHistoryEntry(createAssistantMessage(result.response)));
|
|
4213
|
+
this.history.push(messageToHistoryEntry(createSystemMessage("Interrupted by user.")));
|
|
4214
|
+
this.emit("interrupted", result);
|
|
4215
|
+
} else {
|
|
4216
|
+
pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
|
|
4217
|
+
this.clearStreaming();
|
|
4218
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4219
|
+
this.history.push(messageToHistoryEntry(createSystemMessage(`Error: ${errMsg}`)));
|
|
4220
|
+
this.emit("error", err instanceof Error ? err : new Error(errMsg));
|
|
2901
4221
|
}
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
4222
|
+
} finally {
|
|
4223
|
+
this.executing = false;
|
|
4224
|
+
this.emit("thinking", false);
|
|
4225
|
+
this.persistCurrentSession();
|
|
4226
|
+
if (!this.shuttingDown && this.pendingPrompt) {
|
|
4227
|
+
const queued = this.pendingPrompt;
|
|
4228
|
+
const queuedDisplay = this.pendingDisplayInput;
|
|
4229
|
+
const queuedRaw = this.pendingRawInput;
|
|
4230
|
+
this.clearPendingQueue();
|
|
4231
|
+
setTimeout(() => this.executePrompt(queued, queuedDisplay, queuedRaw), 0);
|
|
2910
4232
|
}
|
|
2911
4233
|
}
|
|
2912
|
-
return commands;
|
|
2913
4234
|
}
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
return argParts[Number(index)] ?? "";
|
|
2923
|
-
});
|
|
2924
|
-
result = result.replace(/\$ARGUMENTS/g, args);
|
|
2925
|
-
result = result.replace(/\$(\d)(?!\d|\w|\[)/g, (_match, digit) => {
|
|
2926
|
-
return argParts[Number(digit)] ?? "";
|
|
2927
|
-
});
|
|
2928
|
-
result = result.replace(/\$\{CLAUDE_SESSION_ID}/g, context?.sessionId ?? "");
|
|
2929
|
-
result = result.replace(/\$\{CLAUDE_SKILL_DIR}/g, context?.skillDir ?? "");
|
|
2930
|
-
return result;
|
|
2931
|
-
}
|
|
2932
|
-
async function preprocessShellCommands(content) {
|
|
2933
|
-
const shellPattern = /!`([^`]+)`/g;
|
|
2934
|
-
if (!shellPattern.test(content)) {
|
|
2935
|
-
return content;
|
|
4235
|
+
handleTextDelta(delta) {
|
|
4236
|
+
this.streamingText += delta;
|
|
4237
|
+
this.emit("text_delta", delta);
|
|
4238
|
+
if (!this.flushTimer) {
|
|
4239
|
+
this.flushTimer = setTimeout(() => {
|
|
4240
|
+
this.flushTimer = null;
|
|
4241
|
+
}, STREAMING_FLUSH_INTERVAL_MS);
|
|
4242
|
+
}
|
|
2936
4243
|
}
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
4244
|
+
handleToolExecution(event) {
|
|
4245
|
+
const streamingState = { activeTools: this.activeTools, history: this.history };
|
|
4246
|
+
if (event.type === "start") {
|
|
4247
|
+
const toolState = applyToolStart(streamingState, event);
|
|
4248
|
+
this.activeTools = streamingState.activeTools;
|
|
4249
|
+
this.emit("tool_start", toolState);
|
|
4250
|
+
} else {
|
|
4251
|
+
const finished = applyToolEnd(streamingState, event);
|
|
4252
|
+
this.activeTools = streamingState.activeTools;
|
|
4253
|
+
if (finished) this.emit("tool_end", finished);
|
|
4254
|
+
}
|
|
2943
4255
|
}
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
2951
|
-
}).trimEnd();
|
|
2952
|
-
} catch {
|
|
2953
|
-
output = "";
|
|
4256
|
+
clearStreaming() {
|
|
4257
|
+
this.streamingText = "";
|
|
4258
|
+
this.activeTools = [];
|
|
4259
|
+
if (this.flushTimer) {
|
|
4260
|
+
clearTimeout(this.flushTimer);
|
|
4261
|
+
this.flushTimer = null;
|
|
2954
4262
|
}
|
|
2955
|
-
result = result.replace(full, output);
|
|
2956
4263
|
}
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
const args = parts.slice(1).join(" ").trim();
|
|
2965
|
-
const userInstruction = args || skillCmd.description;
|
|
2966
|
-
if (skillCmd.skillContent) {
|
|
2967
|
-
let processed = await preprocessShellCommands(skillCmd.skillContent);
|
|
2968
|
-
processed = substituteVariables(processed, args, context);
|
|
2969
|
-
return `<skill name="${cmd}">
|
|
2970
|
-
${processed}
|
|
2971
|
-
</skill>
|
|
4264
|
+
flushStreaming() {
|
|
4265
|
+
if (this.flushTimer) {
|
|
4266
|
+
clearTimeout(this.flushTimer);
|
|
4267
|
+
this.flushTimer = null;
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
};
|
|
2972
4271
|
|
|
2973
|
-
|
|
4272
|
+
// src/query.ts
|
|
4273
|
+
function createQuery(options) {
|
|
4274
|
+
const session = new InteractiveSession({
|
|
4275
|
+
cwd: options.cwd ?? process.cwd(),
|
|
4276
|
+
provider: options.provider,
|
|
4277
|
+
permissionMode: options.permissionMode ?? "bypassPermissions",
|
|
4278
|
+
maxTurns: options.maxTurns,
|
|
4279
|
+
permissionHandler: options.permissionHandler
|
|
4280
|
+
});
|
|
4281
|
+
if (options.onTextDelta) {
|
|
4282
|
+
session.on("text_delta", options.onTextDelta);
|
|
2974
4283
|
}
|
|
2975
|
-
return
|
|
4284
|
+
return async (prompt) => {
|
|
4285
|
+
return new Promise((resolve2, reject) => {
|
|
4286
|
+
const onComplete = (result) => {
|
|
4287
|
+
cleanup();
|
|
4288
|
+
resolve2(result.response);
|
|
4289
|
+
};
|
|
4290
|
+
const onInterrupted = (result) => {
|
|
4291
|
+
cleanup();
|
|
4292
|
+
resolve2(result.response);
|
|
4293
|
+
};
|
|
4294
|
+
const onError = (error) => {
|
|
4295
|
+
cleanup();
|
|
4296
|
+
reject(error);
|
|
4297
|
+
};
|
|
4298
|
+
const cleanup = () => {
|
|
4299
|
+
session.off("complete", onComplete);
|
|
4300
|
+
session.off("interrupted", onInterrupted);
|
|
4301
|
+
session.off("error", onError);
|
|
4302
|
+
};
|
|
4303
|
+
session.on("complete", onComplete);
|
|
4304
|
+
session.on("interrupted", onInterrupted);
|
|
4305
|
+
session.on("error", onError);
|
|
4306
|
+
session.submit(prompt).catch((err) => {
|
|
4307
|
+
cleanup();
|
|
4308
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4309
|
+
});
|
|
4310
|
+
});
|
|
4311
|
+
};
|
|
2976
4312
|
}
|
|
2977
4313
|
|
|
2978
4314
|
// src/types.ts
|
|
@@ -2985,6 +4321,12 @@ import {
|
|
|
2985
4321
|
messageToHistoryEntry as messageToHistoryEntry2,
|
|
2986
4322
|
getMessagesForAPI
|
|
2987
4323
|
} from "@robota-sdk/agent-core";
|
|
4324
|
+
|
|
4325
|
+
// src/subagents/index.ts
|
|
4326
|
+
import { SubagentManager as SubagentManager3 } from "@robota-sdk/agent-runtime";
|
|
4327
|
+
import { WorktreeSubagentRunner, createWorktreeSubagentRunner } from "@robota-sdk/agent-runtime";
|
|
4328
|
+
|
|
4329
|
+
// src/index.ts
|
|
2988
4330
|
import { evaluatePermission } from "@robota-sdk/agent-core";
|
|
2989
4331
|
|
|
2990
4332
|
// src/permissions/permission-prompt.ts
|
|
@@ -3008,10 +4350,13 @@ async function promptForApproval(terminal, toolName, toolArgs) {
|
|
|
3008
4350
|
}
|
|
3009
4351
|
|
|
3010
4352
|
// src/index.ts
|
|
3011
|
-
import { runHooks } from "@robota-sdk/agent-core";
|
|
4353
|
+
import { runHooks as runHooks2 } from "@robota-sdk/agent-core";
|
|
3012
4354
|
export {
|
|
3013
4355
|
AgentExecutor,
|
|
3014
4356
|
BUILT_IN_AGENTS,
|
|
4357
|
+
BackgroundJobOrchestrator,
|
|
4358
|
+
BackgroundTaskError,
|
|
4359
|
+
BackgroundTaskManager,
|
|
3015
4360
|
BuiltinCommandSource,
|
|
3016
4361
|
BundlePluginInstaller,
|
|
3017
4362
|
BundlePluginLoader,
|
|
@@ -3022,29 +4367,42 @@ export {
|
|
|
3022
4367
|
PluginSettingsStore,
|
|
3023
4368
|
PromptExecutor,
|
|
3024
4369
|
SkillCommandSource,
|
|
4370
|
+
SubagentManager3 as SubagentManager,
|
|
4371
|
+
SystemCommandExecutor,
|
|
3025
4372
|
TRUST_TO_MODE,
|
|
4373
|
+
WorktreeSubagentRunner,
|
|
3026
4374
|
assembleSubagentPrompt,
|
|
3027
4375
|
buildSkillPrompt,
|
|
3028
4376
|
chatEntryToMessage,
|
|
3029
4377
|
createAgentTool,
|
|
4378
|
+
createBackgroundProcessTool,
|
|
4379
|
+
createCommandExecutionTool,
|
|
4380
|
+
createDefaultTools,
|
|
3030
4381
|
createQuery,
|
|
3031
4382
|
createSubagentLogger,
|
|
3032
4383
|
createSubagentSession,
|
|
4384
|
+
createSystemCommands,
|
|
4385
|
+
createWorktreeSubagentRunner,
|
|
3033
4386
|
evaluatePermission,
|
|
4387
|
+
executeSkill,
|
|
4388
|
+
getBackgroundTaskTransitions,
|
|
3034
4389
|
getBuiltInAgent,
|
|
3035
4390
|
getForkWorkerSuffix,
|
|
3036
4391
|
getMessagesForAPI,
|
|
3037
4392
|
getSubagentSuffix,
|
|
3038
4393
|
isChatEntry,
|
|
4394
|
+
isTerminalBackgroundTaskStatus2 as isTerminalBackgroundTaskStatus,
|
|
3039
4395
|
messageToHistoryEntry2 as messageToHistoryEntry,
|
|
3040
|
-
|
|
4396
|
+
parseFrontmatter,
|
|
3041
4397
|
preprocessShellCommands,
|
|
3042
4398
|
projectPaths,
|
|
3043
4399
|
promptForApproval,
|
|
3044
4400
|
resolveSubagentLogDir,
|
|
3045
4401
|
retrieveAgentToolDeps,
|
|
3046
|
-
runHooks,
|
|
4402
|
+
runHooks2 as runHooks,
|
|
3047
4403
|
storeAgentToolDeps,
|
|
3048
4404
|
substituteVariables,
|
|
4405
|
+
summarizeBackgroundJobGroup,
|
|
4406
|
+
transitionBackgroundTaskStatus,
|
|
3049
4407
|
userPaths
|
|
3050
4408
|
};
|