@robota-sdk/agent-cli 3.0.0-beta.43 → 3.0.0-beta.44

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.
@@ -30,21 +30,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- Session: () => import_agent_sdk5.Session,
34
- SessionStore: () => import_agent_sdk5.SessionStore,
35
- TRUST_TO_MODE: () => import_agent_sdk5.TRUST_TO_MODE,
36
- query: () => import_agent_sdk5.query,
37
33
  startCli: () => startCli
38
34
  });
39
35
  module.exports = __toCommonJS(index_exports);
40
- var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
41
36
 
42
37
  // src/cli.ts
43
- var import_node_fs2 = require("fs");
38
+ var import_node_fs3 = require("fs");
44
39
  var import_node_path5 = require("path");
45
40
  var import_node_url = require("url");
46
41
  var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
47
- var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
48
42
 
49
43
  // src/utils/cli-args.ts
50
44
  var import_node_util = require("util");
@@ -127,56 +121,49 @@ function deleteSettings(path) {
127
121
  return false;
128
122
  }
129
123
 
130
- // src/print-terminal.ts
131
- var readline = __toESM(require("readline"), 1);
132
- var PrintTerminal = class {
133
- write(text) {
134
- process.stdout.write(text);
135
- }
136
- writeLine(text) {
137
- process.stdout.write(text + "\n");
138
- }
139
- writeMarkdown(md) {
140
- process.stdout.write(md);
141
- }
142
- writeError(text) {
143
- process.stderr.write(text + "\n");
144
- }
145
- prompt(question) {
146
- return new Promise((resolve) => {
147
- const rl = readline.createInterface({
148
- input: process.stdin,
149
- output: process.stdout,
150
- terminal: false,
151
- historySize: 0
152
- });
153
- rl.question(question, (answer) => {
154
- rl.close();
155
- resolve(answer);
156
- });
157
- });
158
- }
159
- async select(options, initialIndex = 0) {
160
- for (let i = 0; i < options.length; i++) {
161
- const marker = i === initialIndex ? ">" : " ";
162
- process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
163
- `);
124
+ // src/utils/provider-factory.ts
125
+ var import_node_fs2 = require("fs");
126
+ var import_node_path2 = require("path");
127
+ var import_node_os = require("os");
128
+ var import_agent_provider_anthropic = require("@robota-sdk/agent-provider-anthropic");
129
+ function readProviderSettings(cwd) {
130
+ const paths = [
131
+ (0, import_node_path2.join)(cwd, ".robota", "settings.local.json"),
132
+ (0, import_node_path2.join)(cwd, ".robota", "settings.json"),
133
+ (0, import_node_path2.join)(cwd, ".claude", "settings.local.json"),
134
+ (0, import_node_path2.join)(cwd, ".claude", "settings.json"),
135
+ (0, import_node_path2.join)((0, import_node_os.homedir)(), ".robota", "settings.json"),
136
+ (0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "settings.json")
137
+ ];
138
+ for (const filePath of paths) {
139
+ if (!(0, import_node_fs2.existsSync)(filePath)) continue;
140
+ try {
141
+ const raw = (0, import_node_fs2.readFileSync)(filePath, "utf8");
142
+ const parsed = JSON.parse(raw);
143
+ const provider = parsed.provider;
144
+ if (provider?.apiKey && provider?.name) {
145
+ return {
146
+ name: provider.name,
147
+ model: provider.model ?? "claude-sonnet-4-6",
148
+ apiKey: provider.apiKey
149
+ };
150
+ }
151
+ } catch {
152
+ continue;
164
153
  }
165
- const answer = await this.prompt(
166
- ` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
167
- );
168
- const trimmed = answer.trim().toLowerCase();
169
- if (trimmed === "") return initialIndex;
170
- const num = parseInt(trimmed, 10);
171
- if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
172
- return initialIndex;
173
154
  }
174
- spinner(_message) {
175
- return { stop() {
176
- }, update() {
177
- } };
155
+ throw new Error("No provider configuration found. Run `robota` to set up.");
156
+ }
157
+ function createProviderFromSettings(cwd, modelOverride) {
158
+ const settings = readProviderSettings(cwd);
159
+ const model = modelOverride ?? settings.model;
160
+ switch (settings.name) {
161
+ case "anthropic":
162
+ return new import_agent_provider_anthropic.AnthropicProvider({ apiKey: settings.apiKey, defaultModel: model });
163
+ default:
164
+ throw new Error(`Unknown provider: ${settings.name}. Currently supported: anthropic`);
178
165
  }
179
- };
166
+ }
180
167
 
181
168
  // src/ui/render.tsx
182
169
  var import_ink14 = require("ink");
@@ -188,227 +175,136 @@ var import_agent_core4 = require("@robota-sdk/agent-core");
188
175
 
189
176
  // src/ui/hooks/useInteractiveSession.ts
190
177
  var import_react = require("react");
191
- var import_node_os = require("os");
178
+ var import_node_os2 = require("os");
192
179
  var import_node_path3 = require("path");
193
180
  var import_agent_sdk = require("@robota-sdk/agent-sdk");
194
181
  var import_agent_core = require("@robota-sdk/agent-core");
182
+ var import_node_crypto = require("crypto");
195
183
 
196
- // src/commands/plugin-source.ts
197
- var PluginCommandSource = class {
198
- name = "plugin";
199
- plugins;
200
- constructor(plugins) {
201
- this.plugins = plugins;
202
- }
203
- getCommands() {
204
- const commands = [];
205
- for (const plugin of this.plugins) {
206
- for (const skill of plugin.skills) {
207
- const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
208
- commands.push({
209
- name: baseName,
210
- description: `(${plugin.manifest.name}) ${skill.description}`,
211
- source: "plugin",
212
- skillContent: skill.skillContent,
213
- pluginDir: plugin.pluginDir
214
- });
215
- }
216
- for (const cmd of plugin.commands) {
217
- commands.push({
218
- name: cmd.name,
219
- description: cmd.description,
220
- source: "plugin",
221
- skillContent: cmd.skillContent,
222
- pluginDir: plugin.pluginDir
223
- });
224
- }
225
- }
226
- return commands;
227
- }
228
- };
229
-
230
- // src/utils/skill-prompt.ts
231
- var import_node_child_process = require("child_process");
232
- function substituteVariables(content, args, context) {
233
- const argParts = args ? args.split(/\s+/) : [];
234
- let result = content;
235
- result = result.replace(/\$ARGUMENTS\[(\d+)]/g, (_match, index) => {
236
- return argParts[Number(index)] ?? "";
237
- });
238
- result = result.replace(/\$ARGUMENTS/g, args);
239
- result = result.replace(/\$(\d)(?!\d|\w|\[)/g, (_match, digit) => {
240
- return argParts[Number(digit)] ?? "";
241
- });
242
- result = result.replace(/\$\{CLAUDE_SESSION_ID}/g, context?.sessionId ?? "");
243
- result = result.replace(/\$\{CLAUDE_SKILL_DIR}/g, context?.skillDir ?? "");
244
- return result;
245
- }
246
- async function preprocessShellCommands(content) {
247
- const shellPattern = /!`([^`]+)`/g;
248
- if (!shellPattern.test(content)) {
249
- return content;
250
- }
251
- shellPattern.lastIndex = 0;
252
- let result = content;
253
- let match;
254
- const matches = [];
255
- while ((match = shellPattern.exec(content)) !== null) {
256
- matches.push({ full: match[0], command: match[1] });
257
- }
258
- for (const { full, command } of matches) {
259
- let output = "";
260
- try {
261
- output = (0, import_node_child_process.execSync)(command, {
262
- timeout: 5e3,
263
- encoding: "utf-8",
264
- stdio: ["pipe", "pipe", "pipe"]
265
- }).trimEnd();
266
- } catch {
267
- output = "";
184
+ // src/ui/tui-state-manager.ts
185
+ var MAX_RENDERED_MESSAGES = 100;
186
+ var TuiStateManager = class {
187
+ // ── Rendering state ───────────────────────────────────────────
188
+ history = [];
189
+ streamingText = "";
190
+ activeTools = [];
191
+ isThinking = false;
192
+ isAborting = false;
193
+ pendingPrompt = null;
194
+ contextState = { percentage: 0, usedTokens: 0, maxTokens: 0 };
195
+ /** Called after any state change. React hook sets this to trigger re-render. */
196
+ onChange = null;
197
+ // ── Internal ──────────────────────────────────────────────────
198
+ streamBuf = "";
199
+ notify() {
200
+ this.onChange?.();
201
+ }
202
+ // ── Event handlers (InteractiveSession → state) ───────────────
203
+ onTextDelta = (delta) => {
204
+ this.streamBuf += delta;
205
+ this.streamingText = this.streamBuf;
206
+ this.notify();
207
+ };
208
+ onToolStart = (state) => {
209
+ this.activeTools = [...this.activeTools, state];
210
+ this.notify();
211
+ };
212
+ onToolEnd = (state) => {
213
+ this.activeTools = this.activeTools.map(
214
+ (t) => t.toolName === state.toolName && t.isRunning ? state : t
215
+ );
216
+ this.notify();
217
+ };
218
+ onThinking = (thinking) => {
219
+ this.isThinking = thinking;
220
+ if (thinking) {
221
+ this.streamBuf = "";
222
+ this.streamingText = "";
223
+ this.activeTools = [];
224
+ } else {
225
+ this.isAborting = false;
268
226
  }
269
- result = result.replace(full, output);
270
- }
271
- return result;
272
- }
273
- async function buildSkillPrompt(input, registry, context) {
274
- const parts = input.slice(1).split(/\s+/);
275
- const cmd = parts[0]?.toLowerCase() ?? "";
276
- const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
277
- if (!skillCmd) return null;
278
- const args = parts.slice(1).join(" ").trim();
279
- const userInstruction = args || skillCmd.description;
280
- if (skillCmd.skillContent) {
281
- let processed = await preprocessShellCommands(skillCmd.skillContent);
282
- processed = substituteVariables(processed, args, context);
283
- return `<skill name="${cmd}">
284
- ${processed}
285
- </skill>
286
-
287
- Execute the "${cmd}" skill: ${userInstruction}`;
288
- }
289
- return `Use the "${cmd}" skill: ${userInstruction}`;
290
- }
291
-
292
- // src/ui/hooks/plugin-hooks-merger.ts
293
- var import_node_path2 = require("path");
294
- function buildPluginEnv(plugin) {
295
- const dataDir = (0, import_node_path2.join)((0, import_node_path2.dirname)((0, import_node_path2.dirname)(plugin.pluginDir)), "data", plugin.manifest.name);
296
- return {
297
- CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
298
- CLAUDE_PLUGIN_PATH: plugin.pluginDir,
299
- CLAUDE_PLUGIN_DATA: dataDir
227
+ this.notify();
300
228
  };
301
- }
302
- function resolvePluginRoot(group, pluginDir) {
303
- if (Array.isArray(group.hooks)) {
304
- return {
305
- ...group,
306
- hooks: group.hooks.map((h) => {
307
- if (typeof h.command === "string") {
308
- return {
309
- ...h,
310
- command: h.command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginDir)
311
- };
312
- }
313
- return h;
314
- })
229
+ onComplete = (result) => {
230
+ this.streamBuf = "";
231
+ this.streamingText = "";
232
+ this.activeTools = [];
233
+ this.contextState = {
234
+ percentage: result.contextState.usedPercentage,
235
+ usedTokens: result.contextState.usedTokens,
236
+ maxTokens: result.contextState.maxTokens
315
237
  };
238
+ this.notify();
239
+ };
240
+ onInterrupted = () => {
241
+ this.streamBuf = "";
242
+ this.streamingText = "";
243
+ this.activeTools = [];
244
+ this.notify();
245
+ };
246
+ onError = () => {
247
+ this.streamBuf = "";
248
+ this.streamingText = "";
249
+ this.activeTools = [];
250
+ this.notify();
251
+ };
252
+ // ── State updates from external sources ───────────────────────
253
+ /** Sync history from InteractiveSession */
254
+ syncHistory(entries) {
255
+ if (entries.length === 0) return;
256
+ this.history = entries.length > MAX_RENDERED_MESSAGES ? entries.slice(-MAX_RENDERED_MESSAGES) : [...entries];
257
+ this.notify();
258
+ }
259
+ /** Add a single history entry */
260
+ addEntry(entry) {
261
+ const updated = [...this.history, entry];
262
+ this.history = updated.length > MAX_RENDERED_MESSAGES ? updated.slice(-MAX_RENDERED_MESSAGES) : updated;
263
+ this.notify();
264
+ }
265
+ /** Update pending prompt state */
266
+ setPendingPrompt(prompt) {
267
+ this.pendingPrompt = prompt;
268
+ this.notify();
269
+ }
270
+ /** Set aborting flag */
271
+ setAborting(aborting) {
272
+ this.isAborting = aborting;
273
+ this.notify();
274
+ }
275
+ /** Update context state */
276
+ setContextState(state) {
277
+ this.contextState = state;
278
+ this.notify();
316
279
  }
317
- return group;
318
- }
319
- function mergePluginHooks(plugins) {
320
- const merged = {};
321
- for (const plugin of plugins) {
322
- const hooksObj = plugin.hooks;
323
- if (!hooksObj) continue;
324
- const pluginEnv = buildPluginEnv(plugin);
325
- const innerHooks = hooksObj.hooks ?? hooksObj;
326
- for (const [event, groups] of Object.entries(innerHooks)) {
327
- if (!Array.isArray(groups)) continue;
328
- if (!merged[event]) merged[event] = [];
329
- const resolved = groups.map((group) => {
330
- const r = resolvePluginRoot(group, plugin.pluginDir);
331
- r.env = pluginEnv;
332
- return r;
333
- });
334
- merged[event].push(...resolved);
335
- }
336
- }
337
- return merged;
338
- }
339
- function mergeHooksIntoConfig(configHooks, pluginHooks) {
340
- const pluginKeys = Object.keys(pluginHooks);
341
- if (pluginKeys.length === 0) return configHooks;
342
- const merged = {};
343
- for (const [event, groups] of Object.entries(pluginHooks)) {
344
- merged[event] = [...groups];
345
- }
346
- if (configHooks) {
347
- for (const [event, groups] of Object.entries(configHooks)) {
348
- if (!Array.isArray(groups)) continue;
349
- if (!merged[event]) merged[event] = [];
350
- merged[event].push(...groups);
351
- }
352
- }
353
- return merged;
354
- }
280
+ };
355
281
 
356
282
  // src/ui/hooks/useInteractiveSession.ts
357
- var MAX_RENDERED_MESSAGES = 100;
358
283
  function initializeSession(props, permissionHandler) {
359
- const cwd = props.cwd ?? process.cwd();
284
+ const interactiveSession = new import_agent_sdk.InteractiveSession({
285
+ cwd: props.cwd,
286
+ provider: props.provider,
287
+ permissionMode: props.permissionMode,
288
+ maxTurns: props.maxTurns,
289
+ permissionHandler
290
+ });
360
291
  const registry = new import_agent_sdk.CommandRegistry();
361
292
  registry.addSource(new import_agent_sdk.BuiltinCommandSource());
362
- registry.addSource(new import_agent_sdk.SkillCommandSource(cwd));
363
- let pluginHooks = {};
364
- const pluginsDir = (0, import_node_path3.join)((0, import_node_os.homedir)(), ".robota", "plugins");
293
+ registry.addSource(new import_agent_sdk.SkillCommandSource(props.cwd));
294
+ const pluginsDir = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".robota", "plugins");
365
295
  const loader = new import_agent_sdk.BundlePluginLoader(pluginsDir);
366
296
  try {
367
297
  const plugins = loader.loadPluginsSync();
368
298
  if (plugins.length > 0) {
369
- registry.addSource(new PluginCommandSource(plugins));
370
- pluginHooks = mergePluginHooks(plugins);
299
+ registry.addSource(new import_agent_sdk.PluginCommandSource(plugins));
371
300
  }
372
301
  } catch {
373
302
  }
374
- const mergedConfig = {
375
- ...props.config,
376
- hooks: mergeHooksIntoConfig(
377
- props.config.hooks,
378
- pluginHooks
379
- )
380
- };
381
- const interactiveSession = new import_agent_sdk.InteractiveSession({
382
- config: mergedConfig,
383
- context: props.context,
384
- projectInfo: props.projectInfo,
385
- sessionStore: props.sessionStore,
386
- permissionMode: props.permissionMode,
387
- maxTurns: props.maxTurns,
388
- cwd,
389
- permissionHandler
390
- });
391
- return {
392
- interactiveSession,
393
- registry,
394
- commandExecutor: new import_agent_sdk.SystemCommandExecutor(),
395
- pluginHooks
396
- };
303
+ const manager = new TuiStateManager();
304
+ return { interactiveSession, registry, manager };
397
305
  }
398
306
  function useInteractiveSession(props) {
399
- const [messages, setMessages] = (0, import_react.useState)([]);
400
- const addMessage = (0, import_react.useCallback)((msg) => {
401
- setMessages((prev) => {
402
- const updated = [...prev, msg];
403
- return updated.length > MAX_RENDERED_MESSAGES ? updated.slice(-MAX_RENDERED_MESSAGES) : updated;
404
- });
405
- }, []);
406
- const [streamingText, setStreamingText] = (0, import_react.useState)("");
407
- const [activeTools, setActiveTools] = (0, import_react.useState)([]);
408
- const [isThinking, setIsThinking] = (0, import_react.useState)(false);
409
- const [isAborting, setIsAborting] = (0, import_react.useState)(false);
410
- const [pendingPrompt, setPendingPrompt] = (0, import_react.useState)(null);
411
- const [contextState, setContextState] = (0, import_react.useState)({ percentage: 0, usedTokens: 0, maxTokens: 0 });
307
+ const [, forceRender] = (0, import_react.useState)(0);
412
308
  const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
413
309
  const permissionQueueRef = (0, import_react.useRef)([]);
414
310
  const processingRef = (0, import_react.useRef)(false);
@@ -443,79 +339,54 @@ function useInteractiveSession(props) {
443
339
  if (stateRef.current === null) {
444
340
  stateRef.current = initializeSession(props, permissionHandler);
445
341
  }
446
- const { interactiveSession, registry, commandExecutor } = stateRef.current;
342
+ const { interactiveSession, registry, manager } = stateRef.current;
343
+ manager.onChange = () => forceRender((n) => n + 1);
447
344
  (0, import_react.useEffect)(() => {
448
- let streamBuf = "";
449
- const onTextDelta = (delta) => {
450
- streamBuf += delta;
451
- setStreamingText(streamBuf);
452
- };
453
- const onToolStart = (state) => {
454
- setActiveTools((prev) => [...prev, state]);
455
- };
456
- const onToolEnd = (state) => {
457
- setActiveTools(
458
- (prev) => prev.map((t) => t.toolName === state.toolName && t.isRunning ? state : t)
459
- );
460
- };
461
- const onThinking = (thinking) => {
462
- setIsThinking(thinking);
463
- if (thinking) {
464
- streamBuf = "";
465
- setStreamingText("");
466
- setActiveTools([]);
467
- } else {
468
- setIsAborting(false);
345
+ interactiveSession.on("text_delta", manager.onTextDelta);
346
+ interactiveSession.on("tool_start", manager.onToolStart);
347
+ interactiveSession.on("tool_end", manager.onToolEnd);
348
+ interactiveSession.on("thinking", manager.onThinking);
349
+ interactiveSession.on("complete", manager.onComplete);
350
+ interactiveSession.on("interrupted", manager.onInterrupted);
351
+ interactiveSession.on("error", manager.onError);
352
+ const initCheck = setInterval(() => {
353
+ try {
354
+ const ctx = interactiveSession.getContextState();
355
+ manager.setContextState({
356
+ percentage: ctx.usedPercentage,
357
+ usedTokens: ctx.usedTokens,
358
+ maxTokens: ctx.maxTokens
359
+ });
360
+ clearInterval(initCheck);
361
+ } catch {
469
362
  }
470
- };
471
- const onComplete = (result) => {
472
- setContextState({
473
- percentage: result.contextState.usedPercentage,
474
- usedTokens: result.contextState.usedTokens,
475
- maxTokens: result.contextState.maxTokens
476
- });
477
- };
478
- const onInterrupted = () => {
479
- };
480
- const onError = () => {
481
- };
482
- interactiveSession.on("text_delta", onTextDelta);
483
- interactiveSession.on("tool_start", onToolStart);
484
- interactiveSession.on("tool_end", onToolEnd);
485
- interactiveSession.on("thinking", onThinking);
486
- interactiveSession.on("complete", onComplete);
487
- interactiveSession.on("interrupted", onInterrupted);
488
- interactiveSession.on("error", onError);
363
+ }, 200);
489
364
  return () => {
490
- interactiveSession.off("text_delta", onTextDelta);
491
- interactiveSession.off("tool_start", onToolStart);
492
- interactiveSession.off("tool_end", onToolEnd);
493
- interactiveSession.off("thinking", onThinking);
494
- interactiveSession.off("complete", onComplete);
495
- interactiveSession.off("interrupted", onInterrupted);
496
- interactiveSession.off("error", onError);
365
+ clearInterval(initCheck);
366
+ interactiveSession.off("text_delta", manager.onTextDelta);
367
+ interactiveSession.off("tool_start", manager.onToolStart);
368
+ interactiveSession.off("tool_end", manager.onToolEnd);
369
+ interactiveSession.off("thinking", manager.onThinking);
370
+ interactiveSession.off("complete", manager.onComplete);
371
+ interactiveSession.off("interrupted", manager.onInterrupted);
372
+ interactiveSession.off("error", manager.onError);
497
373
  };
498
- }, [interactiveSession]);
374
+ }, [interactiveSession, manager]);
499
375
  (0, import_react.useEffect)(() => {
500
- if (!isThinking) {
501
- const sessionMessages = interactiveSession.getMessages();
502
- if (sessionMessages.length > 0) {
503
- setMessages(
504
- sessionMessages.length > MAX_RENDERED_MESSAGES ? sessionMessages.slice(-MAX_RENDERED_MESSAGES) : [...sessionMessages]
505
- );
506
- }
507
- setPendingPrompt(interactiveSession.getPendingPrompt());
376
+ manager.syncHistory(interactiveSession.getFullHistory());
377
+ if (!manager.isThinking) {
378
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
508
379
  }
509
- }, [isThinking, interactiveSession]);
380
+ }, [manager.isThinking, interactiveSession, manager]);
510
381
  const handleSubmit = (0, import_react.useCallback)(
511
382
  async (input) => {
512
383
  if (input.startsWith("/")) {
513
384
  const parts = input.slice(1).split(/\s+/);
514
385
  const cmd = parts[0]?.toLowerCase() ?? "";
515
386
  const args = parts.slice(1).join(" ");
516
- const result = await commandExecutor.execute(cmd, interactiveSession, args);
387
+ const result = await interactiveSession.executeCommand(cmd, args);
517
388
  if (result) {
518
- addMessage((0, import_agent_core.createSystemMessage)(result.message));
389
+ manager.addEntry((0, import_agent_core.messageToHistoryEntry)((0, import_agent_core.createSystemMessage)(result.message)));
519
390
  const effects = interactiveSession;
520
391
  if (result.data?.modelId) {
521
392
  effects._pendingModelId = result.data.modelId;
@@ -530,7 +401,7 @@ function useInteractiveSession(props) {
530
401
  return;
531
402
  }
532
403
  const ctx = interactiveSession.getContextState();
533
- setContextState({
404
+ manager.setContextState({
534
405
  percentage: ctx.usedPercentage,
535
406
  usedTokens: ctx.usedTokens,
536
407
  maxTokens: ctx.maxTokens
@@ -539,13 +410,23 @@ function useInteractiveSession(props) {
539
410
  }
540
411
  const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
541
412
  if (skillCmd) {
542
- addMessage((0, import_agent_core.createSystemMessage)(`Invoking ${skillCmd.source}: ${cmd}`));
543
- const prompt = await buildSkillPrompt(input, registry);
413
+ manager.addEntry({
414
+ id: (0, import_node_crypto.randomUUID)(),
415
+ timestamp: /* @__PURE__ */ new Date(),
416
+ category: "event",
417
+ type: "skill-invocation",
418
+ data: {
419
+ skillName: cmd,
420
+ source: skillCmd.source,
421
+ message: `Invoking ${skillCmd.source}: ${cmd}`
422
+ }
423
+ });
424
+ const prompt = await (0, import_agent_sdk.buildSkillPrompt)(input, registry);
544
425
  if (prompt) {
545
426
  const qualifiedName = registry.resolveQualifiedName(cmd);
546
427
  const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmd.length)}` : input;
547
428
  await interactiveSession.submit(prompt, input, hookInput);
548
- setPendingPrompt(interactiveSession.getPendingPrompt());
429
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
549
430
  return;
550
431
  }
551
432
  }
@@ -557,45 +438,38 @@ function useInteractiveSession(props) {
557
438
  interactiveSession._triggerPluginTUI = true;
558
439
  return;
559
440
  }
560
- addMessage((0, import_agent_core.createSystemMessage)(`Unknown command "/${cmd}". Type /help for help.`));
441
+ manager.addEntry(
442
+ (0, import_agent_core.messageToHistoryEntry)(
443
+ (0, import_agent_core.createSystemMessage)(`Unknown command "/${cmd}". Type /help for help.`)
444
+ )
445
+ );
561
446
  return;
562
447
  }
563
448
  await interactiveSession.submit(input);
564
- setPendingPrompt(interactiveSession.getPendingPrompt());
449
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
565
450
  },
566
- [interactiveSession, commandExecutor, registry, addMessage]
451
+ [interactiveSession, registry, manager]
567
452
  );
568
453
  const handleAbort = (0, import_react.useCallback)(() => {
569
- setIsAborting(true);
454
+ manager.setAborting(true);
570
455
  interactiveSession.abort();
571
- }, [interactiveSession]);
456
+ }, [interactiveSession, manager]);
572
457
  const handleCancelQueue = (0, import_react.useCallback)(() => {
573
458
  interactiveSession.cancelQueue();
574
- setPendingPrompt(null);
575
- }, [interactiveSession]);
576
- if (contextState.maxTokens === 0) {
577
- const ctx = interactiveSession.getContextState();
578
- setContextState({
579
- percentage: ctx.usedPercentage,
580
- usedTokens: ctx.usedTokens,
581
- maxTokens: ctx.maxTokens
582
- });
583
- }
459
+ manager.setPendingPrompt(null);
460
+ }, [interactiveSession, manager]);
584
461
  return {
585
462
  interactiveSession,
586
463
  registry,
587
- commandExecutor,
588
- pluginHooks: stateRef.current.pluginHooks,
589
- messages,
590
- addMessage,
591
- setMessages,
592
- streamingText,
593
- activeTools,
594
- isThinking,
595
- isAborting,
596
- pendingPrompt,
464
+ history: manager.history,
465
+ addEntry: (entry) => manager.addEntry(entry),
466
+ streamingText: manager.streamingText,
467
+ activeTools: manager.activeTools,
468
+ isThinking: manager.isThinking,
469
+ isAborting: manager.isAborting,
470
+ pendingPrompt: manager.pendingPrompt,
597
471
  permissionRequest,
598
- contextState,
472
+ contextState: manager.contextState,
599
473
  handleSubmit,
600
474
  handleAbort,
601
475
  handleCancelQueue
@@ -604,12 +478,12 @@ function useInteractiveSession(props) {
604
478
 
605
479
  // src/ui/hooks/usePluginCallbacks.ts
606
480
  var import_react2 = require("react");
607
- var import_node_os2 = require("os");
481
+ var import_node_os3 = require("os");
608
482
  var import_node_path4 = require("path");
609
483
  var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
610
484
  function usePluginCallbacks(cwd) {
611
485
  return (0, import_react2.useMemo)(() => {
612
- const home = (0, import_node_os2.homedir)();
486
+ const home = (0, import_node_os3.homedir)();
613
487
  const pluginsDir = (0, import_node_path4.join)(home, ".robota", "plugins");
614
488
  const userSettingsPath = (0, import_node_path4.join)(home, ".robota", "settings.json");
615
489
  const settingsStore = new import_agent_sdk2.PluginSettingsStore(userSettingsPath);
@@ -869,8 +743,45 @@ var MessageItem = import_react3.default.memo(function MessageItem2({
869
743
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: (0, import_agent_core2.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
870
744
  ] });
871
745
  });
872
- function MessageList({ messages }) {
873
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { flexDirection: "column", children: messages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MessageItem, { message: msg }, msg.id)) });
746
+ function ToolSummaryEntry({ entry }) {
747
+ const data = entry.data;
748
+ const lines = data?.summary?.split("\n") ?? [];
749
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
751
+ "Tool:",
752
+ " "
753
+ ] }) }),
754
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
755
+ lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "green", children: [
756
+ " ",
757
+ line
758
+ ] }, i))
759
+ ] });
760
+ }
761
+ function EventEntry({ entry }) {
762
+ const eventData = entry.data;
763
+ const eventMessage = typeof eventData?.message === "string" ? eventData.message : typeof eventData?.content === "string" ? eventData.content : entry.type;
764
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
765
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "yellow", bold: true, children: [
766
+ "System:",
767
+ " "
768
+ ] }) }),
769
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
770
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: eventMessage }) })
771
+ ] });
772
+ }
773
+ function EntryItem({ entry }) {
774
+ if (entry.category === "chat") {
775
+ const message = entry.data;
776
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MessageItem, { message });
777
+ }
778
+ if (entry.type === "tool-summary") {
779
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolSummaryEntry, { entry });
780
+ }
781
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EventEntry, { entry });
782
+ }
783
+ function MessageList({ history }) {
784
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { flexDirection: "column", children: history.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EntryItem, { entry }, entry.id)) });
874
785
  }
875
786
 
876
787
  // src/ui/StatusBar.tsx
@@ -1979,12 +1890,12 @@ var import_jsx_runtime14 = require("react/jsx-runtime");
1979
1890
  var EXIT_DELAY_MS = 500;
1980
1891
  function App(props) {
1981
1892
  const { exit } = (0, import_ink13.useApp)();
1982
- const cwd = props.cwd ?? process.cwd();
1893
+ const cwd = props.cwd;
1983
1894
  const {
1984
1895
  interactiveSession,
1985
1896
  registry,
1986
- messages,
1987
- addMessage,
1897
+ history,
1898
+ addEntry,
1988
1899
  streamingText,
1989
1900
  activeTools,
1990
1901
  isThinking,
@@ -1996,13 +1907,10 @@ function App(props) {
1996
1907
  handleAbort,
1997
1908
  handleCancelQueue
1998
1909
  } = useInteractiveSession({
1999
- config: props.config,
2000
- context: props.context,
2001
- projectInfo: props.projectInfo,
2002
- sessionStore: props.sessionStore,
1910
+ cwd,
1911
+ provider: props.provider,
2003
1912
  permissionMode: props.permissionMode,
2004
- maxTurns: props.maxTurns,
2005
- cwd
1913
+ maxTurns: props.maxTurns
2006
1914
  });
2007
1915
  const pluginCallbacks = usePluginCallbacks(cwd);
2008
1916
  const [pendingModelId, setPendingModelId] = (0, import_react12.useState)(null);
@@ -2025,7 +1933,9 @@ function App(props) {
2025
1933
  const settings = readSettings(settingsPath);
2026
1934
  settings.language = lang;
2027
1935
  writeSettings(settingsPath, settings);
2028
- addMessage((0, import_agent_core4.createSystemMessage)(`Language set to "${lang}". Restarting...`));
1936
+ addEntry(
1937
+ (0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Language set to "${lang}". Restarting...`))
1938
+ );
2029
1939
  setTimeout(() => exit(), EXIT_DELAY_MS);
2030
1940
  return;
2031
1941
  }
@@ -2033,9 +1943,9 @@ function App(props) {
2033
1943
  delete sideEffects._resetRequested;
2034
1944
  const settingsPath = getUserSettingsPath();
2035
1945
  if (deleteSettings(settingsPath)) {
2036
- addMessage((0, import_agent_core4.createSystemMessage)(`Deleted ${settingsPath}. Exiting...`));
1946
+ addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Deleted ${settingsPath}. Exiting...`)));
2037
1947
  } else {
2038
- addMessage((0, import_agent_core4.createSystemMessage)("No user settings found."));
1948
+ addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("No user settings found.")));
2039
1949
  }
2040
1950
  setTimeout(() => exit(), EXIT_DELAY_MS);
2041
1951
  return;
@@ -2059,7 +1969,14 @@ function App(props) {
2059
1969
  },
2060
1970
  { isActive: !permissionRequest && !showPluginTUI }
2061
1971
  );
2062
- const session = interactiveSession.getSession();
1972
+ let permissionMode = props.permissionMode ?? "default";
1973
+ let sessionId = "";
1974
+ try {
1975
+ const session = interactiveSession.getSession();
1976
+ permissionMode = session.getPermissionMode();
1977
+ sessionId = session.getSessionId();
1978
+ } catch {
1979
+ }
2063
1980
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
2064
1981
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2065
1982
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
@@ -2075,7 +1992,7 @@ function App(props) {
2075
1992
  ] })
2076
1993
  ] }),
2077
1994
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2078
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { messages }),
1995
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { history }),
2079
1996
  (isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
2080
1997
  ] }),
2081
1998
  permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PermissionPrompt, { request: permissionRequest }),
@@ -2090,21 +2007,25 @@ function App(props) {
2090
2007
  try {
2091
2008
  const settingsPath = getUserSettingsPath();
2092
2009
  updateModelInSettings(settingsPath, pendingModelId);
2093
- addMessage(
2094
- (0, import_agent_core4.createSystemMessage)(
2095
- `Model changed to ${(0, import_agent_core4.getModelName)(pendingModelId)}. Restarting...`
2010
+ addEntry(
2011
+ (0, import_agent_core4.messageToHistoryEntry)(
2012
+ (0, import_agent_core4.createSystemMessage)(
2013
+ `Model changed to ${(0, import_agent_core4.getModelName)(pendingModelId)}. Restarting...`
2014
+ )
2096
2015
  )
2097
2016
  );
2098
2017
  setTimeout(() => exit(), EXIT_DELAY_MS);
2099
2018
  } catch (err) {
2100
- addMessage(
2101
- (0, import_agent_core4.createSystemMessage)(
2102
- `Failed: ${err instanceof Error ? err.message : String(err)}`
2019
+ addEntry(
2020
+ (0, import_agent_core4.messageToHistoryEntry)(
2021
+ (0, import_agent_core4.createSystemMessage)(
2022
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
2023
+ )
2103
2024
  )
2104
2025
  );
2105
2026
  }
2106
2027
  } else {
2107
- addMessage((0, import_agent_core4.createSystemMessage)("Model change cancelled."));
2028
+ addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Model change cancelled.")));
2108
2029
  }
2109
2030
  }
2110
2031
  }
@@ -2114,16 +2035,16 @@ function App(props) {
2114
2035
  {
2115
2036
  callbacks: pluginCallbacks,
2116
2037
  onClose: () => setShowPluginTUI(false),
2117
- addMessage: (msg) => addMessage((0, import_agent_core4.createSystemMessage)(msg.content))
2038
+ addMessage: (msg) => addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(msg.content)))
2118
2039
  }
2119
2040
  ),
2120
2041
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2121
2042
  StatusBar,
2122
2043
  {
2123
- permissionMode: session.getPermissionMode(),
2124
- modelName: (0, import_agent_core4.getModelName)(props.config.provider.model),
2125
- sessionId: session.getSessionId(),
2126
- messageCount: messages.length,
2044
+ permissionMode,
2045
+ modelName: props.modelId ? (0, import_agent_core4.getModelName)(props.modelId) : "",
2046
+ sessionId,
2047
+ messageCount: history.length,
2127
2048
  isThinking,
2128
2049
  contextPercentage: contextState.percentage,
2129
2050
  contextUsedTokens: contextState.usedTokens,
@@ -2181,9 +2102,9 @@ function renderApp(options) {
2181
2102
  // src/cli.ts
2182
2103
  var import_meta = {};
2183
2104
  function checkSettingsFile(filePath) {
2184
- if (!(0, import_node_fs2.existsSync)(filePath)) return "missing";
2105
+ if (!(0, import_node_fs3.existsSync)(filePath)) return "missing";
2185
2106
  try {
2186
- const raw = (0, import_node_fs2.readFileSync)(filePath, "utf8").trim();
2107
+ const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
2187
2108
  if (raw.length === 0) return "incomplete";
2188
2109
  const parsed = JSON.parse(raw);
2189
2110
  const provider = parsed.provider;
@@ -2200,7 +2121,7 @@ function readVersion() {
2200
2121
  const candidates = [(0, import_node_path5.join)(dir, "..", "..", "package.json"), (0, import_node_path5.join)(dir, "..", "package.json")];
2201
2122
  for (const pkgPath of candidates) {
2202
2123
  try {
2203
- const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf-8");
2124
+ const raw = (0, import_node_fs3.readFileSync)(pkgPath, "utf-8");
2204
2125
  const pkg = JSON.parse(raw);
2205
2126
  if (pkg.version !== void 0 && pkg.name !== void 0) {
2206
2127
  return pkg.version;
@@ -2288,7 +2209,7 @@ async function ensureConfig(cwd) {
2288
2209
  }
2289
2210
  const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
2290
2211
  const settingsDir = (0, import_node_path5.dirname)(userPath);
2291
- (0, import_node_fs2.mkdirSync)(settingsDir, { recursive: true });
2212
+ (0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
2292
2213
  const settings = {
2293
2214
  provider: {
2294
2215
  name: "anthropic",
@@ -2299,7 +2220,7 @@ async function ensureConfig(cwd) {
2299
2220
  if (language) {
2300
2221
  settings.language = language;
2301
2222
  }
2302
- (0, import_node_fs2.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
2223
+ (0, import_node_fs3.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
2303
2224
  process.stdout.write(`
2304
2225
  Config saved to ${userPath}
2305
2226
 
@@ -2327,44 +2248,40 @@ async function startCli() {
2327
2248
  }
2328
2249
  const cwd = process.cwd();
2329
2250
  await ensureConfig(cwd);
2330
- const [config, context, projectInfo] = await Promise.all([
2331
- (0, import_agent_sdk3.loadConfig)(cwd),
2332
- (0, import_agent_sdk3.loadContext)(cwd),
2333
- (0, import_agent_sdk3.detectProject)(cwd)
2334
- ]);
2335
- if (args.model !== void 0) {
2336
- config.provider.model = args.model;
2337
- }
2338
- if (args.language !== void 0) {
2339
- config.language = args.language;
2340
- }
2341
- const sessionStore = new import_agent_sdk3.SessionStore();
2251
+ const providerSettings = readProviderSettings(cwd);
2252
+ const modelId = args.model ?? providerSettings.model;
2253
+ const provider = createProviderFromSettings(cwd, args.model);
2342
2254
  if (args.printMode) {
2343
2255
  const prompt = args.positional.join(" ").trim();
2344
2256
  if (prompt.length === 0) {
2345
2257
  process.stderr.write("Print mode (-p) requires a prompt argument.\n");
2346
2258
  process.exit(1);
2347
2259
  }
2348
- const terminal = new PrintTerminal();
2349
- const paths = (0, import_agent_sdk3.projectPaths)(cwd);
2350
- const session = (0, import_agent_sdk3.createSession)({
2351
- config,
2352
- context,
2353
- terminal,
2354
- sessionLogger: new import_agent_sdk3.FileSessionLogger(paths.logs),
2355
- projectInfo,
2356
- permissionMode: args.permissionMode,
2357
- promptForApproval: import_agent_sdk4.promptForApproval
2260
+ const session = new import_agent_sdk3.InteractiveSession({
2261
+ cwd,
2262
+ provider,
2263
+ permissionMode: args.permissionMode ?? "bypassPermissions",
2264
+ maxTurns: args.maxTurns
2265
+ });
2266
+ await new Promise((resolve, reject) => {
2267
+ session.on("complete", (result) => {
2268
+ process.stdout.write(result.response + "\n");
2269
+ resolve();
2270
+ });
2271
+ session.on("interrupted", (result) => {
2272
+ if (result.response) process.stdout.write(result.response + "\n");
2273
+ resolve();
2274
+ });
2275
+ session.on("error", (err) => reject(err));
2276
+ session.submit(prompt).catch(reject);
2358
2277
  });
2359
- const response = await session.run(prompt);
2360
- process.stdout.write(response + "\n");
2361
2278
  return;
2362
2279
  }
2363
2280
  renderApp({
2364
- config,
2365
- context,
2366
- projectInfo,
2367
- sessionStore,
2281
+ cwd,
2282
+ provider,
2283
+ modelId,
2284
+ language: args.language,
2368
2285
  permissionMode: args.permissionMode,
2369
2286
  maxTurns: args.maxTurns,
2370
2287
  version: readVersion()
@@ -2372,9 +2289,5 @@ async function startCli() {
2372
2289
  }
2373
2290
  // Annotate the CommonJS export names for ESM import in node:
2374
2291
  0 && (module.exports = {
2375
- Session,
2376
- SessionStore,
2377
- TRUST_TO_MODE,
2378
- query,
2379
2292
  startCli
2380
2293
  });