@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.
@@ -6,229 +6,1302 @@ import {
6
6
  messageToHistoryEntry
7
7
  } from "@robota-sdk/agent-core";
8
8
 
9
- // src/commands/system-command.ts
10
- var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
11
- function createSystemCommands() {
12
- return [
13
- {
14
- name: "help",
15
- description: "Show available commands",
16
- execute: (_session, _args) => ({
17
- message: [
18
- "Available commands:",
19
- " help \u2014 Show this help",
20
- " clear \u2014 Clear conversation",
21
- " compact [instr] \u2014 Compact context (optional focus instructions)",
22
- " mode [m] \u2014 Show/change permission mode",
23
- " model <id> \u2014 Change AI model",
24
- " language <code> \u2014 Set response language (ko, en, ja, zh)",
25
- " cost \u2014 Show session info",
26
- " context \u2014 Context window info",
27
- " permissions \u2014 Permission rules",
28
- " resume \u2014 Resume a previous session",
29
- " rename <name> \u2014 Rename the current session",
30
- " reset \u2014 Delete settings and exit"
31
- ].join("\n"),
32
- success: true
33
- })
34
- },
35
- {
36
- name: "clear",
37
- description: "Clear conversation history",
38
- execute: (session, _args) => {
39
- const underlying = session.getSession();
40
- underlying.clearHistory();
41
- return { message: "Conversation cleared.", success: true };
42
- }
43
- },
44
- {
45
- name: "compact",
46
- description: "Compress context window",
47
- execute: async (session, args) => {
48
- const underlying = session.getSession();
49
- const instructions = args.trim() || void 0;
50
- const before = underlying.getContextState().usedPercentage;
51
- await underlying.compact(instructions);
52
- const after = underlying.getContextState().usedPercentage;
53
- return {
54
- message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
55
- success: true,
56
- data: { before, after }
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: "Show/change permission mode",
63
- execute: (session, args) => {
64
- const underlying = session.getSession();
65
- const arg = args.trim().split(/\s+/)[0];
66
- if (!arg) {
67
- return {
68
- message: `Current mode: ${underlying.getPermissionMode()}`,
69
- success: true,
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: "Change AI model",
90
- execute: (_session, args) => {
91
- const modelId = args.trim().split(/\s+/)[0];
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
- execute: (_session, args) => {
106
- const lang = args.trim().split(/\s+/)[0];
107
- if (!lang) {
108
- return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
109
- }
110
- return {
111
- message: `Language set to "${lang}".`,
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: "permissions",
150
- description: "Show permission rules",
151
- execute: (session, _args) => {
152
- const underlying = session.getSession();
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: "resume",
170
- description: "Resume a previous session",
171
- execute: (_session, _args) => ({
172
- message: "Opening session picker...",
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
- name: "rename",
179
- description: "Rename the current session",
180
- execute: (_session, args) => {
181
- const name = args.trim();
182
- if (!name) {
183
- return { message: "Usage: rename <name>", success: false };
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: `Session renamed to "${name}".`,
435
+ message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
187
436
  success: true,
188
- data: { name }
437
+ data: { before, after }
189
438
  };
190
439
  }
191
440
  },
192
441
  {
193
- name: "reset",
194
- description: "Delete settings",
195
- execute: (_session, _args) => {
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: "Reset requested.",
198
- success: true,
199
- data: { resetRequested: true }
463
+ message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
464
+ success: false
200
465
  };
201
466
  }
202
- }
203
- ];
204
- }
205
- var SystemCommandExecutor = class {
206
- commands;
207
- constructor(commands) {
208
- this.commands = /* @__PURE__ */ new Map();
209
- for (const cmd of commands ?? createSystemCommands()) {
210
- this.commands.set(cmd.name, cmd);
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
- /** Register an additional command. */
214
- register(command) {
215
- this.commands.set(command.name, command);
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
- /** Execute a command by name. Returns null if command not found. */
218
- async execute(name, session, args) {
219
- const cmd = this.commands.get(name);
220
- if (!cmd) return null;
221
- return await cmd.execute(session, args);
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
- /** List all registered commands. */
224
- listCommands() {
225
- return [...this.commands.values()];
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
- /** Check if a command exists. */
228
- hasCommand(name) {
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 extractFirstArg(toolArgs) {
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 = extractFirstArg(event.toolArgs);
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-builder.ts
497
- var TRUST_LEVEL_DESCRIPTIONS = {
498
- safe: "safe (read-only / plan mode \u2014 only read-access tools are available)",
499
- moderate: "moderate (default mode \u2014 write and bash tools require approval)",
500
- full: "full (acceptEdits mode \u2014 file writes are auto-approved; bash requires approval)"
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 buildProjectSection(info) {
503
- const lines = ["## Current Project"];
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 buildToolsSection(descriptions) {
519
- if (descriptions.length === 0) {
520
- return "";
521
- }
522
- const lines = ["## Available Tools", ...descriptions.map((d) => `- ${d}`)];
523
- return lines.join("\n");
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 buildSkillsSection(skills) {
526
- const invocable = skills.filter((s) => s.disableModelInvocation !== true);
527
- if (invocable.length === 0) {
528
- return "";
529
- }
530
- const lines = [
531
- "## Skills",
532
- "The following skills are available:",
533
- "",
534
- ...invocable.map((s) => `- ${s.name}: ${s.description}`)
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
- const roleLines = [
542
- "## Role",
543
- "You are an AI coding assistant with access to tools that let you read and modify code.",
544
- "You help developers understand, write, and improve their codebase.",
545
- "Always be precise, follow existing code conventions, and prefer minimal changes."
546
- ];
547
- if (language) {
548
- roleLines.push(
549
- `Always respond in ${language}. Use ${language} for all explanations and communications.`
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/agent-tool.ts
625
- import { z } from "zod";
626
- import { createZodFunctionTool } from "@robota-sdk/agent-tools";
627
-
628
- // src/agents/built-in-agents.ts
629
- 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.
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
- function assembleSubagentPrompt(options) {
709
- const parts = [options.agentBody];
710
- if (options.claudeMd) {
711
- parts.push(options.claudeMd);
712
- }
713
- if (options.agentsMd) {
714
- parts.push(options.agentsMd);
715
- }
716
- const suffix = options.isForkWorker ? getForkWorkerSuffix() : getSubagentSuffix();
717
- parts.push(suffix);
718
- return parts.join("\n\n");
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
- // src/assembly/create-subagent-session.ts
722
- var MODEL_SHORTCUTS = {
723
- sonnet: "claude-sonnet-4-6",
724
- haiku: "claude-haiku-4-5",
725
- opus: "claude-opus-4-6"
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 filterTools(parentTools, agentDefinition) {
731
- let tools = [...parentTools];
732
- if (agentDefinition.disallowedTools) {
733
- const denySet = new Set(agentDefinition.disallowedTools);
734
- tools = tools.filter((t) => !denySet.has(t.getName()));
735
- }
736
- if (agentDefinition.tools) {
737
- const allowSet = new Set(agentDefinition.tools);
738
- tools = tools.filter((t) => allowSet.has(t.getName()));
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 createSubagentSession(options) {
744
- const { agentDefinition, parentConfig, parentContext, parentTools, terminal } = options;
745
- const tools = filterTools(parentTools, agentDefinition);
746
- const model = agentDefinition.model ? resolveModelId(agentDefinition.model, parentConfig.provider.model) : parentConfig.provider.model;
747
- const systemMessage = assembleSubagentPrompt({
748
- agentBody: agentDefinition.systemPrompt,
749
- claudeMd: parentContext.claudeMd,
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/agent-tool.ts
773
- function asZodSchema(schema) {
774
- return schema;
775
- }
776
- var AgentSchema = z.object({
777
- prompt: z.string().describe("The task for the subagent to perform"),
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
- var sessionDepsStore = /* @__PURE__ */ new WeakMap();
782
- function storeAgentToolDeps(key, deps) {
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 generateAgentId() {
795
- return `agent_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1827
+ function normalizeCommand(command) {
1828
+ return command.trim().replace(/^\/+/, "").split(/\s+/)[0] ?? "";
796
1829
  }
797
- function createAgentTool(deps) {
798
- async function runAgent(args) {
799
- const agentType = args.subagent_type ?? "general-purpose";
800
- const agentDef = resolveAgentDefinition(agentType, deps.customAgentRegistry);
801
- if (!agentDef) {
802
- return JSON.stringify({
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 createZodFunctionTool(
842
- "Agent",
843
- "Launch a subagent to handle a task in an isolated context. The subagent gets its own context window and returns a result when done. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.",
844
- asZodSchema(AgentSchema),
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
- return runAgent(params);
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 LIST_KEYS = /* @__PURE__ */ new Set(["tools", "disallowedTools"]);
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 parseFrontmatter(content) {
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 (LIST_KEYS.has(key)) {
880
- result[key] = rawValue.split(",").map((s) => s.trim());
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 (!existsSync(dir)) return [];
1915
+ if (!existsSync2(dir)) return [];
895
1916
  const agents = [];
896
1917
  let entries;
897
1918
  try {
898
- entries = readdirSync(dir, { withFileTypes: true });
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 = join(dir, entry.name);
905
- const content = readFileSync(filePath, "utf-8");
906
- const { frontmatter, body } = parseFrontmatter(content);
907
- const fallbackName = basename(entry.name, ".md");
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 ?? homedir();
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(join(this.cwd, ".claude", "agents")),
933
- scanAgentsDir(join(this.home, ".robota", "agents"))
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
- const agentLoader = new AgentDefinitionLoader(process.cwd());
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
- const agentToolDeps = {
990
- config: options.config,
991
- context: options.context,
992
- tools,
993
- terminal: options.terminal,
994
- provider,
995
- permissionMode: options.permissionMode,
996
- permissionHandler: options.permissionHandler,
997
- hooks: options.config.hooks,
998
- hookTypeExecutors: hookTypeExecutors.length > 0 ? hookTypeExecutors : void 0,
999
- onTextDelta: options.onTextDelta,
1000
- onToolExecution: options.onToolExecution,
1001
- customAgentRegistry: (name) => agentLoader.getAgent(name)
1002
- };
1003
- tools.push(createAgentTool(agentToolDeps));
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 ?? DEFAULT_TOOL_DESCRIPTIONS,
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: process.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: options.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
- storeAgentToolDeps(session, agentToolDeps);
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 join2 } from "path";
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 = join2(baseLogsDir, parentSessionId, "subagents");
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 join2(baseLogsDir, parentSessionId, "subagents");
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 join3 } from "path";
1070
- import { homedir as homedir2 } from "os";
2229
+ import { join as join4 } from "path";
2230
+ import { homedir as homedir3 } from "os";
1071
2231
  function projectPaths(cwd) {
1072
- const base = join3(cwd, ".robota");
2232
+ const base = join4(cwd, ".robota");
1073
2233
  return {
1074
- settings: join3(base, "settings.json"),
1075
- settingsLocal: join3(base, "settings.local.json"),
1076
- logs: join3(base, "logs"),
1077
- sessions: join3(base, "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 = join3(homedir2(), ".robota");
2241
+ const base = join4(homedir3(), ".robota");
1082
2242
  return {
1083
- settings: join3(base, "settings.json"),
1084
- sessions: join3(base, "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 readFileSync2, existsSync as existsSync2 } from "fs";
1090
- import { join as join4 } from "path";
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 z2 } from "zod";
1094
- var ProviderSchema = z2.object({
1095
- name: z2.string().optional(),
1096
- model: z2.string().optional(),
1097
- apiKey: z2.string().optional()
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 PermissionsSchema = z2.object({
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: z2.array(z2.string()).optional(),
2270
+ allow: z4.array(z4.string()).optional(),
1102
2271
  /** Patterns that are always denied */
1103
- deny: z2.array(z2.string()).optional()
2272
+ deny: z4.array(z4.string()).optional()
1104
2273
  });
1105
- var EnvSchema = z2.record(z2.string()).optional();
1106
- var CommandHookDefinitionSchema = z2.object({
1107
- type: z2.literal("command"),
1108
- command: z2.string(),
1109
- timeout: z2.number().optional()
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 = z2.object({
1112
- type: z2.literal("http"),
1113
- url: z2.string(),
1114
- headers: z2.record(z2.string()).optional(),
1115
- timeout: z2.number().optional()
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 = z2.object({
1118
- type: z2.literal("prompt"),
1119
- prompt: z2.string(),
1120
- model: z2.string().optional()
2286
+ var PromptHookDefinitionSchema = z4.object({
2287
+ type: z4.literal("prompt"),
2288
+ prompt: z4.string(),
2289
+ model: z4.string().optional()
1121
2290
  });
1122
- var AgentHookDefinitionSchema = z2.object({
1123
- type: z2.literal("agent"),
1124
- agent: z2.string(),
1125
- maxTurns: z2.number().optional(),
1126
- timeout: z2.number().optional()
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 = z2.discriminatedUnion("type", [
2297
+ var HookDefinitionSchema = z4.discriminatedUnion("type", [
1129
2298
  CommandHookDefinitionSchema,
1130
2299
  HttpHookDefinitionSchema,
1131
2300
  PromptHookDefinitionSchema,
1132
2301
  AgentHookDefinitionSchema
1133
2302
  ]);
1134
- var HookGroupSchema = z2.object({
1135
- matcher: z2.string(),
1136
- hooks: z2.array(HookDefinitionSchema)
2303
+ var HookGroupSchema = z4.object({
2304
+ matcher: z4.string(),
2305
+ hooks: z4.array(HookDefinitionSchema)
1137
2306
  });
1138
- var HooksSchema = z2.object({
1139
- PreToolUse: z2.array(HookGroupSchema).optional(),
1140
- PostToolUse: z2.array(HookGroupSchema).optional(),
1141
- SessionStart: z2.array(HookGroupSchema).optional(),
1142
- Stop: z2.array(HookGroupSchema).optional(),
1143
- PreCompact: z2.array(HookGroupSchema).optional(),
1144
- PostCompact: z2.array(HookGroupSchema).optional(),
1145
- UserPromptSubmit: z2.array(HookGroupSchema).optional(),
1146
- Notification: z2.array(HookGroupSchema).optional()
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 = z2.record(z2.boolean()).optional();
1149
- var MarketplaceSourceSchema = z2.object({
1150
- source: z2.object({
1151
- type: z2.enum(["github", "git", "local", "url"]),
1152
- repo: z2.string().optional(),
1153
- url: z2.string().optional(),
1154
- path: z2.string().optional(),
1155
- ref: z2.string().optional()
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 = z2.record(MarketplaceSourceSchema).optional();
1159
- var SettingsSchema = z2.object({
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: z2.enum(["safe", "moderate", "full"]).optional(),
2335
+ defaultTrustLevel: z4.enum(["safe", "moderate", "full"]).optional(),
1162
2336
  /** Response language (e.g., "ko", "en", "ja"). Injected into system prompt. */
1163
- language: z2.string().optional(),
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 (!existsSync2(filePath)) {
2371
+ if (!existsSync3(filePath)) {
1193
2372
  return void 0;
1194
2373
  }
1195
- const raw = readFileSync2(filePath, "utf-8").trim();
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
- if (settings.provider?.apiKey !== void 0) {
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
- ...settings.provider,
1219
- apiKey: resolveEnvRef(settings.provider.apiKey)
1220
- }
2409
+ provider,
2410
+ providers
1221
2411
  };
1222
2412
  }
1223
- return settings;
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
- provider: {
1249
- name: merged.provider?.name ?? DEFAULTS.provider.name,
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
- join4(home, ".robota", "settings.json"),
2492
+ join5(home, ".robota", "settings.json"),
1267
2493
  // 1. user (lowest)
1268
- join4(cwd, ".robota", "settings.json"),
2494
+ join5(home, ".claude", "settings.json"),
2495
+ // 1b. user (Claude Code compat)
2496
+ join5(cwd, ".robota", "settings.json"),
1269
2497
  // 2. project
1270
- join4(cwd, ".robota", "settings.local.json"),
2498
+ join5(cwd, ".robota", "settings.local.json"),
1271
2499
  // 3. project-local
1272
- join4(cwd, ".claude", "settings.json"),
2500
+ join5(cwd, ".claude", "settings.json"),
1273
2501
  // 4. project, Claude Code compat
1274
- join4(cwd, ".claude", "settings.local.json")
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 existsSync3, readFileSync as readFileSync3 } from "fs";
1300
- import { join as join5, dirname, resolve } from "path";
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 = join5(current, filename);
1309
- if (existsSync3(candidate)) {
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) => readFileSync3(p, "utf-8")).join("\n\n");
1348
- const claudeMd = claudePaths.map((p) => readFileSync3(p, "utf-8")).join("\n\n");
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 existsSync4, readFileSync as readFileSync4 } from "fs";
1355
- import { join as join6 } from "path";
2582
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
2583
+ import { join as join7 } from "path";
1356
2584
  function tryReadJson(filePath) {
1357
- if (!existsSync4(filePath)) return void 0;
2585
+ if (!existsSync5(filePath)) return void 0;
1358
2586
  try {
1359
- return JSON.parse(readFileSync4(filePath, "utf-8"));
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 (existsSync4(join6(cwd, "pnpm-workspace.yaml")) || existsSync4(join6(cwd, "pnpm-lock.yaml"))) {
2593
+ if (existsSync5(join7(cwd, "pnpm-workspace.yaml")) || existsSync5(join7(cwd, "pnpm-lock.yaml"))) {
1366
2594
  return "pnpm";
1367
2595
  }
1368
- if (existsSync4(join6(cwd, "yarn.lock"))) {
2596
+ if (existsSync5(join7(cwd, "yarn.lock"))) {
1369
2597
  return "yarn";
1370
2598
  }
1371
- if (existsSync4(join6(cwd, "bun.lockb"))) {
2599
+ if (existsSync5(join7(cwd, "bun.lockb"))) {
1372
2600
  return "bun";
1373
2601
  }
1374
- if (existsSync4(join6(cwd, "package-lock.json"))) {
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 = join6(cwd, "package.json");
1381
- const tsconfigPath = join6(cwd, "tsconfig.json");
1382
- const pyprojectPath = join6(cwd, "pyproject.toml");
1383
- const cargoPath = join6(cwd, "Cargo.toml");
1384
- const goModPath = join6(cwd, "go.mod");
1385
- if (existsSync4(pkgJsonPath)) {
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 = existsSync4(tsconfigPath) ? "typescript" : "javascript";
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 (existsSync4(pyprojectPath) || existsSync4(join6(cwd, "setup.py"))) {
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 (existsSync4(cargoPath)) {
2630
+ if (existsSync5(cargoPath)) {
1403
2631
  return {
1404
2632
  type: "rust",
1405
2633
  language: "rust"
1406
2634
  };
1407
2635
  }
1408
- if (existsSync4(goModPath)) {
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 existsSync5, readFileSync as readFileSync5, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
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 (!existsSync5(this.settingsPath)) {
2658
+ if (!existsSync6(this.settingsPath)) {
1431
2659
  return {};
1432
2660
  }
1433
2661
  try {
1434
- const raw = readFileSync5(this.settingsPath, "utf-8");
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 (!existsSync5(dir)) {
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 existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
1523
- import { join as join7 } from "path";
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 existsSync6, readdirSync as readdirSync2 } from "fs";
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 (!existsSync6(dirPath)) return [];
2803
+ if (!existsSync7(dirPath)) return [];
1576
2804
  try {
1577
- const entries = readdirSync2(dirPath, { withFileTypes: true });
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 = join7(this.pluginsDir, "cache");
1608
- if (!existsSync7(cacheDir)) {
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 = join7(cacheDir, marketplace);
2842
+ const marketplaceDir = join8(cacheDir, marketplace);
1615
2843
  const plugins = getSortedSubdirs(marketplaceDir);
1616
2844
  for (const pluginName of plugins) {
1617
- const pluginDir = join7(marketplaceDir, pluginName);
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 = join7(pluginDir, latestVersion);
1622
- const manifestPath = join7(versionDir, ".claude-plugin", "plugin.json");
1623
- if (!existsSync7(manifestPath)) continue;
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 = readFileSync6(path, "utf-8");
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 = join7(pluginDir, "skills");
1673
- if (!existsSync7(skillsDir)) return [];
1674
- const entries = readdirSync3(skillsDir, { withFileTypes: true });
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 = join7(skillsDir, entry.name, "SKILL.md");
1679
- if (!existsSync7(skillFile)) continue;
1680
- const raw = readFileSync6(skillFile, "utf-8");
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 = join7(pluginDir, "commands");
1696
- if (!existsSync7(commandsDir)) return [];
1697
- const entries = readdirSync3(commandsDir, { withFileTypes: true });
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 = readFileSync6(join7(commandsDir, entry.name), "utf-8");
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 = join7(pluginDir, "hooks", "hooks.json");
1717
- if (!existsSync7(hooksPath)) return {};
2944
+ const hooksPath = join8(pluginDir, "hooks", "hooks.json");
2945
+ if (!existsSync8(hooksPath)) return {};
1718
2946
  try {
1719
- const raw = readFileSync6(hooksPath, "utf-8");
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 = join7(pluginDir, ".mcp.json");
1732
- const fallbackPath = join7(pluginDir, ".claude-plugin", "mcp.json");
1733
- const mcpPath = existsSync7(primaryPath) ? primaryPath : fallbackPath;
1734
- if (!existsSync7(mcpPath)) return void 0;
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 = readFileSync6(mcpPath, "utf-8");
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 = join7(pluginDir, "agents");
1745
- if (!existsSync7(agentsDir)) return [];
2972
+ const agentsDir = join8(pluginDir, "agents");
2973
+ if (!existsSync8(agentsDir)) return [];
1746
2974
  try {
1747
- const entries = readdirSync3(agentsDir, { withFileTypes: true });
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 existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync2 } from "fs";
1758
- import { join as join8, dirname as dirname3 } from "path";
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 = join8(this.pluginsDir, "cache");
1770
- this.registryPath = join8(this.pluginsDir, "installed_plugins.json");
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 = join8(this.cacheDir, marketplaceName, pluginName, version);
1791
- if (existsSync8(targetDir)) {
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 (existsSync8(record.installPath)) {
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 = join8(marketplaceDir, source);
1871
- if (!existsSync8(sourcePath)) {
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 (existsSync8(targetDir)) {
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 (!existsSync8(this.registryPath)) {
3135
+ if (!existsSync9(this.registryPath)) {
1908
3136
  return {};
1909
3137
  }
1910
3138
  try {
1911
- const raw = readFileSync7(this.registryPath, "utf-8");
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 (!existsSync8(dir)) {
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 execSync(command, { timeout: options.timeout, stdio: "pipe" });
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 execSync2 } from "child_process";
1937
- import { cpSync as cpSync2, existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync9, renameSync, rmSync as rmSync3 } from "fs";
1938
- import { join as join10 } from "path";
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 existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
1942
- import { join as join9, dirname as dirname4 } from "path";
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 (!existsSync9(registryPath)) {
3172
+ if (!existsSync10(registryPath)) {
1945
3173
  return {};
1946
3174
  }
1947
3175
  try {
1948
- const raw = readFileSync8(registryPath, "utf-8");
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 (!existsSync9(dir)) {
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 = join9(pluginsDir, "installed_plugins.json");
1967
- if (!existsSync9(installedPath)) return;
3194
+ const installedPath = join10(pluginsDir, "installed_plugins.json");
3195
+ if (!existsSync10(installedPath)) return;
1968
3196
  let registry;
1969
3197
  try {
1970
- const raw = readFileSync8(installedPath, "utf-8");
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 && existsSync9(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 (!existsSync9(dir)) {
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 = join10(this.pluginsDir, "marketplaces");
2007
- this.registryPath = join10(this.pluginsDir, "known_marketplaces.json");
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 = join10(this.marketplacesDir, tempName);
3248
+ const tempDir = join11(this.marketplacesDir, tempName);
2021
3249
  mkdirSync5(this.marketplacesDir, { recursive: true });
2022
3250
  if (source.type === "local") {
2023
- if (!existsSync10(source.path)) {
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 = join10(tempDir, ".claude-plugin", "marketplace.json");
2038
- if (!existsSync10(manifestPath)) {
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 = join10(this.marketplacesDir, name);
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 (existsSync10(entry.installLocation)) {
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 (!existsSync10(entry.installLocation)) {
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 (!existsSync10(localSource.path)) {
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 = join10(entry.installLocation, ".claude-plugin", "marketplace.json");
2133
- if (!existsSync10(manifestPath)) {
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 = readFileSync9(path, "utf-8");
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 execSync2(command, { timeout: options.timeout, stdio: "pipe" });
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 join11, dirname as dirname5 } from "path";
3442
+ import { join as join12, dirname as dirname5 } from "path";
2215
3443
  function buildPluginEnv(plugin) {
2216
- const dataDir = join11(dirname5(dirname5(plugin.pluginDir)), "data", plugin.manifest.name);
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 homedir3 } from "os";
2279
- import { join as join12 } from "path";
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 = join12(homedir3(), ".robota", "plugins");
3515
+ const pluginsDir = join13(homedir4(), ".robota", "plugins");
2288
3516
  const pluginLoader = new BundlePluginLoader(pluginsDir);
2289
3517
  let mergedConfig = config;
2290
- try {
2291
- const plugins = pluginLoader.loadPluginsSync();
2292
- if (plugins.length > 0) {
2293
- const pluginHooks = mergePluginHooks(plugins);
2294
- mergedConfig = {
2295
- ...config,
2296
- hooks: mergeHooksIntoConfig(
2297
- config.hooks,
2298
- pluginHooks
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 { history: [], sessionName: void 0, pendingRestoreMessages: null };
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 { history, sessionName, pendingRestoreMessages };
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.commandExecutor = new SystemCommandExecutor(createSystemCommands());
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
- setName(name) {
2498
- this.sessionName = name;
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
- attachTransport(transport) {
2513
- transport.attach(this);
3885
+ getBackgroundTask(taskId) {
3886
+ return this.getBackgroundTaskManagerOrThrow().get(taskId);
2514
3887
  }
2515
- async executePrompt(input, displayInput, rawInput) {
2516
- this.executing = true;
2517
- this.clearStreaming();
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
- handleTextDelta(delta) {
2580
- this.streamingText += delta;
2581
- this.emit("text_delta", delta);
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
- handleToolExecution(event) {
2589
- const streamingState = { activeTools: this.activeTools, history: this.history };
2590
- if (event.type === "start") {
2591
- const toolState = applyToolStart(streamingState, event);
2592
- this.activeTools = streamingState.activeTools;
2593
- this.emit("tool_start", toolState);
2594
- } else {
2595
- const finished = applyToolEnd(streamingState, event);
2596
- this.activeTools = streamingState.activeTools;
2597
- if (finished) this.emit("tool_end", finished);
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
- clearStreaming() {
2601
- this.streamingText = "";
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
- flushStreaming() {
2609
- if (this.flushTimer) {
2610
- clearTimeout(this.flushTimer);
2611
- this.flushTimer = null;
2612
- }
3917
+ async waitBackgroundJobGroup(groupId) {
3918
+ await this.ensureInitialized();
3919
+ return this.getBackgroundJobOrchestratorOrThrow().waitGroup(groupId);
2613
3920
  }
2614
- };
2615
-
2616
- // src/query.ts
2617
- function createQuery(options) {
2618
- const session = new InteractiveSession({
2619
- cwd: options.cwd ?? process.cwd(),
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
- return async (prompt) => {
2629
- return new Promise((resolve2, reject) => {
2630
- const onComplete = (result) => {
2631
- cleanup();
2632
- resolve2(result.response);
2633
- };
2634
- const onInterrupted = (result) => {
2635
- cleanup();
2636
- resolve2(result.response);
2637
- };
2638
- const onError = (error) => {
2639
- cleanup();
2640
- reject(error);
2641
- };
2642
- const cleanup = () => {
2643
- session.off("complete", onComplete);
2644
- session.off("interrupted", onInterrupted);
2645
- session.off("error", onError);
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
- /** Get all commands, optionally filtered by prefix */
2665
- getCommands(filter) {
2666
- const all = [];
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
- /** Resolve a short name to its fully qualified plugin:name form */
2675
- resolveQualifiedName(shortName) {
2676
- const matches = this.getCommands().filter(
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
- /** Get subcommands for a specific command */
2683
- getSubcommands(commandName) {
2684
- const lower = commandName.toLowerCase();
2685
- for (const source of this.sources) {
2686
- for (const cmd of source.getCommands()) {
2687
- if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
2688
- return cmd.subcommands;
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
- // src/commands/builtin-source.ts
2697
- import { CLAUDE_MODELS, formatTokenCount } from "@robota-sdk/agent-core";
2698
- function buildModelSubcommands() {
2699
- const seen = /* @__PURE__ */ new Set();
2700
- const commands = [];
2701
- for (const model of Object.values(CLAUDE_MODELS)) {
2702
- if (seen.has(model.name)) continue;
2703
- seen.add(model.name);
2704
- commands.push({
2705
- name: model.id,
2706
- description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
2707
- source: "builtin"
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
- return commands;
2711
- }
2712
- function createBuiltinCommands() {
2713
- return [
2714
- { name: "help", description: "Show available commands", source: "builtin" },
2715
- { name: "clear", description: "Clear conversation history", source: "builtin" },
2716
- {
2717
- name: "mode",
2718
- description: "Permission mode",
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
- getCommands() {
2763
- return this.commands;
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
- // src/commands/skill-source.ts
2768
- import { readdirSync as readdirSync4, readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
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
- return Object.keys(result).length > 0 ? result : null;
2797
- }
2798
- function buildCommand(frontmatter, content, fallbackName) {
2799
- const cmd = {
2800
- name: frontmatter?.name ?? fallbackName,
2801
- description: frontmatter?.description ?? `Skill: ${fallbackName}`,
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
- return commands;
2829
- }
2830
- function scanCommandsDir(commandsDir) {
2831
- if (!existsSync11(commandsDir)) return [];
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
- return commands;
2843
- }
2844
- var SkillCommandSource = class {
2845
- name = "skill";
2846
- cwd;
2847
- home;
2848
- cachedCommands = null;
2849
- constructor(cwd, home) {
2850
- this.cwd = cwd;
2851
- this.home = home ?? homedir4();
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
- getCommands() {
2854
- if (this.cachedCommands) return this.cachedCommands;
2855
- const sources = [
2856
- scanSkillsDir(join13(this.cwd, ".claude", "skills")),
2857
- scanCommandsDir(join13(this.cwd, ".claude", "commands")),
2858
- scanSkillsDir(join13(this.home, ".robota", "skills")),
2859
- scanSkillsDir(join13(this.cwd, ".agents", "skills"))
2860
- ];
2861
- const seen = /* @__PURE__ */ new Set();
2862
- const merged = [];
2863
- for (const commands of sources) {
2864
- for (const cmd of commands) {
2865
- if (!seen.has(cmd.name)) {
2866
- seen.add(cmd.name);
2867
- merged.push(cmd);
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
- getModelInvocableSkills() {
2875
- return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
4125
+ recordForkSkillError(err) {
4126
+ this.history.push(messageToHistoryEntry(createSystemMessage(`Error: ${err.message}`)));
4127
+ this.emit("error", err);
2876
4128
  }
2877
- getUserInvocableSkills() {
2878
- return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
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
- // src/commands/plugin-source.ts
2883
- var PluginCommandSource = class {
2884
- name = "plugin";
2885
- plugins;
2886
- constructor(plugins) {
2887
- this.plugins = plugins;
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
- getCommands() {
2890
- const commands = [];
2891
- for (const plugin of this.plugins) {
2892
- for (const skill of plugin.skills) {
2893
- const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
2894
- commands.push({
2895
- name: baseName,
2896
- description: `(${plugin.manifest.name}) ${skill.description}`,
2897
- source: "plugin",
2898
- skillContent: skill.skillContent,
2899
- pluginDir: plugin.pluginDir
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
- for (const cmd of plugin.commands) {
2903
- commands.push({
2904
- name: cmd.name,
2905
- description: cmd.description,
2906
- source: "plugin",
2907
- skillContent: cmd.skillContent,
2908
- pluginDir: plugin.pluginDir
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
- // src/utils/skill-prompt.ts
2917
- import { execSync as execSync3 } from "child_process";
2918
- function substituteVariables(content, args, context) {
2919
- const argParts = args ? args.split(/\s+/) : [];
2920
- let result = content;
2921
- result = result.replace(/\$ARGUMENTS\[(\d+)]/g, (_match, index) => {
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
- shellPattern.lastIndex = 0;
2938
- let result = content;
2939
- let match;
2940
- const matches = [];
2941
- while ((match = shellPattern.exec(content)) !== null) {
2942
- matches.push({ full: match[0], command: match[1] });
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
- for (const { full, command } of matches) {
2945
- let output = "";
2946
- try {
2947
- output = execSync3(command, {
2948
- timeout: 5e3,
2949
- encoding: "utf-8",
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
- return result;
2958
- }
2959
- async function buildSkillPrompt(input, registry, context) {
2960
- const parts = input.slice(1).split(/\s+/);
2961
- const cmd = parts[0]?.toLowerCase() ?? "";
2962
- const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
2963
- if (!skillCmd) return null;
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
- Execute the "${cmd}" skill: ${userInstruction}`;
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 `Use the "${cmd}" skill: ${userInstruction}`;
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
- parseFrontmatter2 as parseFrontmatter,
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
  };