@robota-sdk/agent-sdk 3.0.0-beta.54 → 3.0.0-beta.56

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