@robota-sdk/agent-sdk 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.
@@ -1,6 +1,3 @@
1
- // src/types.ts
2
- import { TRUST_TO_MODE } from "@robota-sdk/agent-core";
3
-
4
1
  // src/hooks/prompt-executor.ts
5
2
  function extractJson(raw) {
6
3
  const codeBlockMatch = /```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/.exec(raw);
@@ -239,18 +236,6 @@ function createDefaultTools() {
239
236
  ];
240
237
  }
241
238
 
242
- // src/assembly/create-provider.ts
243
- import { AnthropicProvider } from "@robota-sdk/agent-provider-anthropic";
244
- function createProvider(config) {
245
- const apiKey = config.provider.apiKey ?? process.env["ANTHROPIC_API_KEY"];
246
- if (!apiKey) {
247
- throw new Error(
248
- "ANTHROPIC_API_KEY is not set. Set the environment variable or configure provider.apiKey in ~/.robota/settings.json"
249
- );
250
- }
251
- return new AnthropicProvider({ apiKey });
252
- }
253
-
254
239
  // src/tools/agent-tool.ts
255
240
  import { z } from "zod";
256
241
  import { createZodFunctionTool } from "@robota-sdk/agent-tools";
@@ -380,7 +365,7 @@ function createSubagentSession(options) {
380
365
  agentsMd: parentContext.agentsMd,
381
366
  isForkWorker: options.isForkWorker ?? false
382
367
  });
383
- const provider = createProvider(parentConfig);
368
+ const provider = options.provider;
384
369
  return new Session({
385
370
  tools,
386
371
  provider,
@@ -441,6 +426,7 @@ function createAgentTool(deps) {
441
426
  parentConfig: deps.config,
442
427
  parentContext: deps.context,
443
428
  parentTools: deps.tools,
429
+ provider: deps.provider,
444
430
  terminal: deps.terminal,
445
431
  permissionMode: deps.permissionMode,
446
432
  permissionHandler: deps.permissionHandler,
@@ -587,7 +573,12 @@ var AgentDefinitionLoader = class {
587
573
 
588
574
  // src/assembly/create-session.ts
589
575
  function createSession(options) {
590
- const provider = options.provider ?? createProvider(options.config);
576
+ if (!options.provider) {
577
+ throw new Error(
578
+ "provider is required. SDK is provider-neutral \u2014 consumer must create and pass a provider instance."
579
+ );
580
+ }
581
+ const provider = options.provider;
591
582
  const defaultTools = createDefaultTools();
592
583
  const tools = [...defaultTools, ...options.additionalTools ?? []];
593
584
  const agentLoader = new AgentDefinitionLoader(process.cwd());
@@ -615,6 +606,7 @@ function createSession(options) {
615
606
  context: options.context,
616
607
  tools,
617
608
  terminal: options.terminal,
609
+ provider,
618
610
  permissionMode: options.permissionMode,
619
611
  permissionHandler: options.permissionHandler,
620
612
  hooks: options.config.hooks,
@@ -684,14 +676,32 @@ function resolveSubagentLogDir(parentSessionId, baseLogsDir) {
684
676
  return join2(baseLogsDir, parentSessionId, "subagents");
685
677
  }
686
678
 
687
- // src/index.ts
688
- import { Session as Session3 } from "@robota-sdk/agent-sessions";
689
- import { FileSessionLogger as FileSessionLogger3, SilentSessionLogger } from "@robota-sdk/agent-sessions";
690
- import { SessionStore } from "@robota-sdk/agent-sessions";
679
+ // src/interactive/interactive-session.ts
680
+ import { FileSessionLogger as FileSessionLogger2 } from "@robota-sdk/agent-sessions";
681
+
682
+ // src/paths.ts
683
+ import { join as join3 } from "path";
684
+ import { homedir as homedir2 } from "os";
685
+ function projectPaths(cwd) {
686
+ const base = join3(cwd, ".robota");
687
+ return {
688
+ settings: join3(base, "settings.json"),
689
+ settingsLocal: join3(base, "settings.local.json"),
690
+ logs: join3(base, "logs"),
691
+ sessions: join3(base, "sessions")
692
+ };
693
+ }
694
+ function userPaths() {
695
+ const base = join3(homedir2(), ".robota");
696
+ return {
697
+ settings: join3(base, "settings.json"),
698
+ sessions: join3(base, "sessions")
699
+ };
700
+ }
691
701
 
692
702
  // src/config/config-loader.ts
693
703
  import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
694
- import { join as join3 } from "path";
704
+ import { join as join4 } from "path";
695
705
 
696
706
  // src/config/config-types.ts
697
707
  import { z as z2 } from "zod";
@@ -867,15 +877,15 @@ function toResolvedConfig(merged) {
867
877
  function getSettingsPaths(cwd) {
868
878
  const home = getHomeDir();
869
879
  return [
870
- join3(home, ".robota", "settings.json"),
880
+ join4(home, ".robota", "settings.json"),
871
881
  // 1. user (lowest)
872
- join3(cwd, ".robota", "settings.json"),
882
+ join4(cwd, ".robota", "settings.json"),
873
883
  // 2. project
874
- join3(cwd, ".robota", "settings.local.json"),
884
+ join4(cwd, ".robota", "settings.local.json"),
875
885
  // 3. project-local
876
- join3(cwd, ".claude", "settings.json"),
886
+ join4(cwd, ".claude", "settings.json"),
877
887
  // 4. project, Claude Code compat
878
- join3(cwd, ".claude", "settings.local.json")
888
+ join4(cwd, ".claude", "settings.local.json")
879
889
  // 5. project-local (highest)
880
890
  ];
881
891
  }
@@ -901,7 +911,7 @@ async function loadConfig(cwd) {
901
911
 
902
912
  // src/context/context-loader.ts
903
913
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
904
- import { join as join4, dirname, resolve } from "path";
914
+ import { join as join5, dirname, resolve } from "path";
905
915
  var AGENTS_FILENAME = "AGENTS.md";
906
916
  var CLAUDE_FILENAME = "CLAUDE.md";
907
917
  function collectFilesWalkingUp(startDir, filename) {
@@ -909,7 +919,7 @@ function collectFilesWalkingUp(startDir, filename) {
909
919
  let current = resolve(startDir);
910
920
  let atRoot = false;
911
921
  while (!atRoot) {
912
- const candidate = join4(current, filename);
922
+ const candidate = join5(current, filename);
913
923
  if (existsSync3(candidate)) {
914
924
  found.push(candidate);
915
925
  }
@@ -956,7 +966,7 @@ async function loadContext(cwd) {
956
966
 
957
967
  // src/context/project-detector.ts
958
968
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
959
- import { join as join5 } from "path";
969
+ import { join as join6 } from "path";
960
970
  function tryReadJson(filePath) {
961
971
  if (!existsSync4(filePath)) return void 0;
962
972
  try {
@@ -966,26 +976,26 @@ function tryReadJson(filePath) {
966
976
  }
967
977
  }
968
978
  function detectPackageManager(cwd) {
969
- if (existsSync4(join5(cwd, "pnpm-workspace.yaml")) || existsSync4(join5(cwd, "pnpm-lock.yaml"))) {
979
+ if (existsSync4(join6(cwd, "pnpm-workspace.yaml")) || existsSync4(join6(cwd, "pnpm-lock.yaml"))) {
970
980
  return "pnpm";
971
981
  }
972
- if (existsSync4(join5(cwd, "yarn.lock"))) {
982
+ if (existsSync4(join6(cwd, "yarn.lock"))) {
973
983
  return "yarn";
974
984
  }
975
- if (existsSync4(join5(cwd, "bun.lockb"))) {
985
+ if (existsSync4(join6(cwd, "bun.lockb"))) {
976
986
  return "bun";
977
987
  }
978
- if (existsSync4(join5(cwd, "package-lock.json"))) {
988
+ if (existsSync4(join6(cwd, "package-lock.json"))) {
979
989
  return "npm";
980
990
  }
981
991
  return void 0;
982
992
  }
983
993
  async function detectProject(cwd) {
984
- const pkgJsonPath = join5(cwd, "package.json");
985
- const tsconfigPath = join5(cwd, "tsconfig.json");
986
- const pyprojectPath = join5(cwd, "pyproject.toml");
987
- const cargoPath = join5(cwd, "Cargo.toml");
988
- const goModPath = join5(cwd, "go.mod");
994
+ const pkgJsonPath = join6(cwd, "package.json");
995
+ const tsconfigPath = join6(cwd, "tsconfig.json");
996
+ const pyprojectPath = join6(cwd, "pyproject.toml");
997
+ const cargoPath = join6(cwd, "Cargo.toml");
998
+ const goModPath = join6(cwd, "go.mod");
989
999
  if (existsSync4(pkgJsonPath)) {
990
1000
  const pkgJson = tryReadJson(pkgJsonPath);
991
1001
  const language = existsSync4(tsconfigPath) ? "typescript" : "javascript";
@@ -997,7 +1007,7 @@ async function detectProject(cwd) {
997
1007
  language
998
1008
  };
999
1009
  }
1000
- if (existsSync4(pyprojectPath) || existsSync4(join5(cwd, "setup.py"))) {
1010
+ if (existsSync4(pyprojectPath) || existsSync4(join6(cwd, "setup.py"))) {
1001
1011
  return {
1002
1012
  type: "python",
1003
1013
  language: "python"
@@ -1021,102 +1031,225 @@ async function detectProject(cwd) {
1021
1031
  };
1022
1032
  }
1023
1033
 
1024
- // src/permissions/permission-prompt.ts
1025
- import chalk from "chalk";
1026
- var PERMISSION_OPTIONS = ["Allow", "Deny"];
1027
- var ALLOW_INDEX = 0;
1028
- function formatArgs(toolArgs) {
1029
- const entries = Object.entries(toolArgs);
1030
- if (entries.length === 0) {
1031
- return "(no arguments)";
1032
- }
1033
- return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
1034
- }
1035
- async function promptForApproval(terminal, toolName, toolArgs) {
1036
- terminal.writeLine("");
1037
- terminal.writeLine(chalk.yellow(`[Permission Required] Tool: ${toolName}`));
1038
- terminal.writeLine(chalk.dim(` ${formatArgs(toolArgs)}`));
1039
- terminal.writeLine("");
1040
- const selected = await terminal.select(PERMISSION_OPTIONS, ALLOW_INDEX);
1041
- return selected === ALLOW_INDEX;
1042
- }
1034
+ // src/interactive/interactive-session.ts
1035
+ import {
1036
+ createUserMessage,
1037
+ createAssistantMessage,
1038
+ createSystemMessage,
1039
+ messageToHistoryEntry
1040
+ } from "@robota-sdk/agent-core";
1041
+ import { randomUUID } from "crypto";
1043
1042
 
1044
- // src/query.ts
1045
- async function query(prompt, options) {
1046
- const cwd = options?.cwd ?? process.cwd();
1047
- const [config, context, projectInfo] = await Promise.all([
1048
- loadConfig(cwd),
1049
- loadContext(cwd),
1050
- detectProject(cwd)
1051
- ]);
1052
- const noopTerminal = {
1053
- write: () => {
1043
+ // src/commands/system-command.ts
1044
+ var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
1045
+ function createSystemCommands() {
1046
+ return [
1047
+ {
1048
+ name: "help",
1049
+ description: "Show available commands",
1050
+ execute: (_session, _args) => ({
1051
+ message: [
1052
+ "Available commands:",
1053
+ " help \u2014 Show this help",
1054
+ " clear \u2014 Clear conversation",
1055
+ " compact [instr] \u2014 Compact context (optional focus instructions)",
1056
+ " mode [m] \u2014 Show/change permission mode",
1057
+ " model <id> \u2014 Change AI model",
1058
+ " language <code> \u2014 Set response language (ko, en, ja, zh)",
1059
+ " cost \u2014 Show session info",
1060
+ " context \u2014 Context window info",
1061
+ " permissions \u2014 Permission rules",
1062
+ " reset \u2014 Delete settings and exit"
1063
+ ].join("\n"),
1064
+ success: true
1065
+ })
1054
1066
  },
1055
- writeLine: () => {
1067
+ {
1068
+ name: "clear",
1069
+ description: "Clear conversation history",
1070
+ execute: (session, _args) => {
1071
+ const underlying = session.getSession();
1072
+ underlying.clearHistory();
1073
+ return { message: "Conversation cleared.", success: true };
1074
+ }
1056
1075
  },
1057
- writeMarkdown: () => {
1076
+ {
1077
+ name: "compact",
1078
+ description: "Compress context window",
1079
+ execute: async (session, args) => {
1080
+ const underlying = session.getSession();
1081
+ const instructions = args.trim() || void 0;
1082
+ const before = underlying.getContextState().usedPercentage;
1083
+ await underlying.compact(instructions);
1084
+ const after = underlying.getContextState().usedPercentage;
1085
+ return {
1086
+ message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
1087
+ success: true,
1088
+ data: { before, after }
1089
+ };
1090
+ }
1058
1091
  },
1059
- writeError: () => {
1092
+ {
1093
+ name: "mode",
1094
+ description: "Show/change permission mode",
1095
+ execute: (session, args) => {
1096
+ const underlying = session.getSession();
1097
+ const arg = args.trim().split(/\s+/)[0];
1098
+ if (!arg) {
1099
+ return {
1100
+ message: `Current mode: ${underlying.getPermissionMode()}`,
1101
+ success: true,
1102
+ data: { mode: underlying.getPermissionMode() }
1103
+ };
1104
+ }
1105
+ if (VALID_MODES.includes(arg)) {
1106
+ underlying.setPermissionMode(arg);
1107
+ return {
1108
+ message: `Permission mode set to: ${arg}`,
1109
+ success: true,
1110
+ data: { mode: arg }
1111
+ };
1112
+ }
1113
+ return {
1114
+ message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
1115
+ success: false
1116
+ };
1117
+ }
1060
1118
  },
1061
- prompt: () => Promise.resolve(""),
1062
- select: () => Promise.resolve(0),
1063
- spinner: () => ({ stop: () => {
1064
- }, update: () => {
1065
- } })
1066
- };
1067
- const session = createSession({
1068
- config,
1069
- context,
1070
- terminal: noopTerminal,
1071
- projectInfo,
1072
- permissionMode: options?.permissionMode ?? "bypassPermissions",
1073
- maxTurns: options?.maxTurns,
1074
- provider: options?.provider,
1075
- permissionHandler: options?.permissionHandler,
1076
- onTextDelta: options?.onTextDelta,
1077
- onCompact: options?.onCompact,
1078
- compactInstructions: context.compactInstructions,
1079
- promptForApproval
1080
- });
1081
- return session.run(prompt);
1082
- }
1083
-
1084
- // src/index.ts
1085
- import { evaluatePermission } from "@robota-sdk/agent-core";
1086
- import { runHooks } from "@robota-sdk/agent-core";
1087
-
1088
- // src/paths.ts
1089
- import { join as join6 } from "path";
1090
- import { homedir as homedir2 } from "os";
1091
- function projectPaths(cwd) {
1092
- const base = join6(cwd, ".robota");
1093
- return {
1094
- settings: join6(base, "settings.json"),
1095
- settingsLocal: join6(base, "settings.local.json"),
1096
- logs: join6(base, "logs"),
1097
- sessions: join6(base, "sessions")
1098
- };
1099
- }
1100
- function userPaths() {
1101
- const base = join6(homedir2(), ".robota");
1102
- return {
1103
- settings: join6(base, "settings.json"),
1104
- sessions: join6(base, "sessions")
1105
- };
1119
+ {
1120
+ name: "model",
1121
+ description: "Change AI model",
1122
+ execute: (_session, args) => {
1123
+ const modelId = args.trim().split(/\s+/)[0];
1124
+ if (!modelId) {
1125
+ return { message: "Usage: model <model-id>", success: false };
1126
+ }
1127
+ return {
1128
+ message: `Model change requested: ${modelId}`,
1129
+ success: true,
1130
+ data: { modelId }
1131
+ };
1132
+ }
1133
+ },
1134
+ {
1135
+ name: "language",
1136
+ description: "Set response language",
1137
+ execute: (_session, args) => {
1138
+ const lang = args.trim().split(/\s+/)[0];
1139
+ if (!lang) {
1140
+ return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
1141
+ }
1142
+ return {
1143
+ message: `Language set to "${lang}".`,
1144
+ success: true,
1145
+ data: { language: lang }
1146
+ };
1147
+ }
1148
+ },
1149
+ {
1150
+ name: "cost",
1151
+ description: "Show session info",
1152
+ execute: (session, _args) => {
1153
+ const underlying = session.getSession();
1154
+ const sessionId = underlying.getSessionId();
1155
+ const messageCount = underlying.getMessageCount();
1156
+ return {
1157
+ message: `Session: ${sessionId}
1158
+ Messages: ${messageCount}`,
1159
+ success: true,
1160
+ data: { sessionId, messageCount }
1161
+ };
1162
+ }
1163
+ },
1164
+ {
1165
+ name: "context",
1166
+ description: "Context window info",
1167
+ execute: (session, _args) => {
1168
+ const ctx = session.getContextState();
1169
+ return {
1170
+ message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
1171
+ success: true,
1172
+ data: {
1173
+ usedTokens: ctx.usedTokens,
1174
+ maxTokens: ctx.maxTokens,
1175
+ percentage: ctx.usedPercentage
1176
+ }
1177
+ };
1178
+ }
1179
+ },
1180
+ {
1181
+ name: "permissions",
1182
+ description: "Show permission rules",
1183
+ execute: (session, _args) => {
1184
+ const underlying = session.getSession();
1185
+ const mode = underlying.getPermissionMode();
1186
+ const sessionAllowed = underlying.getSessionAllowedTools();
1187
+ const lines = [`Permission mode: ${mode}`];
1188
+ if (sessionAllowed.length > 0) {
1189
+ lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
1190
+ } else {
1191
+ lines.push("No session-approved tools.");
1192
+ }
1193
+ return {
1194
+ message: lines.join("\n"),
1195
+ success: true,
1196
+ data: { mode, sessionAllowed }
1197
+ };
1198
+ }
1199
+ },
1200
+ {
1201
+ name: "reset",
1202
+ description: "Delete settings",
1203
+ execute: (_session, _args) => {
1204
+ return {
1205
+ message: "Reset requested.",
1206
+ success: true,
1207
+ data: { resetRequested: true }
1208
+ };
1209
+ }
1210
+ }
1211
+ ];
1106
1212
  }
1107
-
1108
- // src/plugins/plugin-settings-store.ts
1109
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
1110
- import { dirname as dirname2 } from "path";
1111
- var PluginSettingsStore = class {
1112
- settingsPath;
1113
- constructor(settingsPath) {
1114
- this.settingsPath = settingsPath;
1115
- }
1116
- /** Read the full settings file from disk. */
1117
- readAll() {
1118
- if (!existsSync5(this.settingsPath)) {
1119
- return {};
1213
+ var SystemCommandExecutor = class {
1214
+ commands;
1215
+ constructor(commands) {
1216
+ this.commands = /* @__PURE__ */ new Map();
1217
+ for (const cmd of commands ?? createSystemCommands()) {
1218
+ this.commands.set(cmd.name, cmd);
1219
+ }
1220
+ }
1221
+ /** Register an additional command. */
1222
+ register(command) {
1223
+ this.commands.set(command.name, command);
1224
+ }
1225
+ /** Execute a command by name. Returns null if command not found. */
1226
+ async execute(name, session, args) {
1227
+ const cmd = this.commands.get(name);
1228
+ if (!cmd) return null;
1229
+ return await cmd.execute(session, args);
1230
+ }
1231
+ /** List all registered commands. */
1232
+ listCommands() {
1233
+ return [...this.commands.values()];
1234
+ }
1235
+ /** Check if a command exists. */
1236
+ hasCommand(name) {
1237
+ return this.commands.has(name);
1238
+ }
1239
+ };
1240
+
1241
+ // src/plugins/plugin-settings-store.ts
1242
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
1243
+ import { dirname as dirname2 } from "path";
1244
+ var PluginSettingsStore = class {
1245
+ settingsPath;
1246
+ constructor(settingsPath) {
1247
+ this.settingsPath = settingsPath;
1248
+ }
1249
+ /** Read the full settings file from disk. */
1250
+ readAll() {
1251
+ if (!existsSync5(this.settingsPath)) {
1252
+ return {};
1120
1253
  }
1121
1254
  try {
1122
1255
  const raw = readFileSync5(this.settingsPath, "utf-8");
@@ -1908,479 +2041,160 @@ var MarketplaceClient = class {
1908
2041
  }
1909
2042
  };
1910
2043
 
1911
- // src/commands/command-registry.ts
1912
- var CommandRegistry = class {
1913
- sources = [];
1914
- addSource(source) {
1915
- this.sources.push(source);
2044
+ // src/plugins/plugin-hooks-merger.ts
2045
+ import { join as join10, dirname as dirname5 } from "path";
2046
+ function buildPluginEnv(plugin) {
2047
+ const dataDir = join10(dirname5(dirname5(plugin.pluginDir)), "data", plugin.manifest.name);
2048
+ return {
2049
+ CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
2050
+ CLAUDE_PLUGIN_PATH: plugin.pluginDir,
2051
+ CLAUDE_PLUGIN_DATA: dataDir
2052
+ };
2053
+ }
2054
+ function resolvePluginRoot(group, pluginDir) {
2055
+ if (Array.isArray(group.hooks)) {
2056
+ return {
2057
+ ...group,
2058
+ hooks: group.hooks.map((h) => {
2059
+ if (typeof h.command === "string") {
2060
+ return {
2061
+ ...h,
2062
+ command: h.command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginDir)
2063
+ };
2064
+ }
2065
+ return h;
2066
+ })
2067
+ };
1916
2068
  }
1917
- /** Get all commands, optionally filtered by prefix */
1918
- getCommands(filter) {
1919
- const all = [];
1920
- for (const source of this.sources) {
1921
- all.push(...source.getCommands());
2069
+ return group;
2070
+ }
2071
+ function mergePluginHooks(plugins) {
2072
+ const merged = {};
2073
+ for (const plugin of plugins) {
2074
+ const hooksObj = plugin.hooks;
2075
+ if (!hooksObj) continue;
2076
+ const pluginEnv = buildPluginEnv(plugin);
2077
+ const innerHooks = hooksObj.hooks ?? hooksObj;
2078
+ for (const [event, groups] of Object.entries(innerHooks)) {
2079
+ if (!Array.isArray(groups)) continue;
2080
+ if (!merged[event]) merged[event] = [];
2081
+ const resolved = groups.map((group) => {
2082
+ const r = resolvePluginRoot(group, plugin.pluginDir);
2083
+ r.env = pluginEnv;
2084
+ return r;
2085
+ });
2086
+ merged[event].push(...resolved);
1922
2087
  }
1923
- if (!filter) return all;
1924
- const lower = filter.toLowerCase();
1925
- return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
1926
2088
  }
1927
- /** Resolve a short name to its fully qualified plugin:name form */
1928
- resolveQualifiedName(shortName) {
1929
- const matches = this.getCommands().filter(
1930
- (c) => c.source === "plugin" && c.name.includes(":") && c.name.endsWith(`:${shortName}`)
1931
- );
1932
- if (matches.length !== 1) return null;
1933
- return matches[0].name;
2089
+ return merged;
2090
+ }
2091
+ function mergeHooksIntoConfig(configHooks, pluginHooks) {
2092
+ const pluginKeys = Object.keys(pluginHooks);
2093
+ if (pluginKeys.length === 0) return configHooks;
2094
+ const merged = {};
2095
+ for (const [event, groups] of Object.entries(pluginHooks)) {
2096
+ merged[event] = [...groups];
1934
2097
  }
1935
- /** Get subcommands for a specific command */
1936
- getSubcommands(commandName) {
1937
- const lower = commandName.toLowerCase();
1938
- for (const source of this.sources) {
1939
- for (const cmd of source.getCommands()) {
1940
- if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
1941
- return cmd.subcommands;
1942
- }
1943
- }
2098
+ if (configHooks) {
2099
+ for (const [event, groups] of Object.entries(configHooks)) {
2100
+ if (!Array.isArray(groups)) continue;
2101
+ if (!merged[event]) merged[event] = [];
2102
+ merged[event].push(...groups);
1944
2103
  }
1945
- return [];
1946
- }
1947
- };
1948
-
1949
- // src/commands/builtin-source.ts
1950
- import { CLAUDE_MODELS, formatTokenCount } from "@robota-sdk/agent-core";
1951
- function buildModelSubcommands() {
1952
- const seen = /* @__PURE__ */ new Set();
1953
- const commands = [];
1954
- for (const model of Object.values(CLAUDE_MODELS)) {
1955
- if (seen.has(model.name)) continue;
1956
- seen.add(model.name);
1957
- commands.push({
1958
- name: model.id,
1959
- description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
1960
- source: "builtin"
1961
- });
1962
2104
  }
1963
- return commands;
1964
- }
1965
- function createBuiltinCommands() {
1966
- return [
1967
- { name: "help", description: "Show available commands", source: "builtin" },
1968
- { name: "clear", description: "Clear conversation history", source: "builtin" },
1969
- {
1970
- name: "mode",
1971
- description: "Permission mode",
1972
- source: "builtin",
1973
- subcommands: [
1974
- { name: "plan", description: "Plan only, no execution", source: "builtin" },
1975
- { name: "default", description: "Ask before risky actions", source: "builtin" },
1976
- { name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
1977
- { name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
1978
- ]
1979
- },
1980
- {
1981
- name: "model",
1982
- description: "Select AI model",
1983
- source: "builtin",
1984
- subcommands: buildModelSubcommands()
1985
- },
1986
- {
1987
- name: "language",
1988
- description: "Set response language",
1989
- source: "builtin",
1990
- subcommands: [
1991
- { name: "ko", description: "Korean", source: "builtin" },
1992
- { name: "en", description: "English", source: "builtin" },
1993
- { name: "ja", description: "Japanese", source: "builtin" },
1994
- { name: "zh", description: "Chinese", source: "builtin" }
1995
- ]
1996
- },
1997
- { name: "compact", description: "Compress context window", source: "builtin" },
1998
- { name: "cost", description: "Show session info", source: "builtin" },
1999
- { name: "context", description: "Context window info", source: "builtin" },
2000
- { name: "permissions", description: "Permission rules", source: "builtin" },
2001
- { name: "plugin", description: "Manage plugins", source: "builtin" },
2002
- { name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
2003
- { name: "reset", description: "Delete settings and exit", source: "builtin" },
2004
- { name: "exit", description: "Exit CLI", source: "builtin" }
2005
- ];
2105
+ return merged;
2006
2106
  }
2007
- var BuiltinCommandSource = class {
2008
- name = "builtin";
2009
- commands;
2010
- constructor() {
2011
- this.commands = createBuiltinCommands();
2012
- }
2013
- getCommands() {
2014
- return this.commands;
2015
- }
2016
- };
2017
2107
 
2018
- // src/commands/skill-source.ts
2019
- import { readdirSync as readdirSync3, readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
2020
- import { join as join10, basename as basename2 } from "path";
2108
+ // src/interactive/interactive-session.ts
2021
2109
  import { homedir as homedir3 } from "os";
2022
- var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
2023
- var LIST_KEYS2 = /* @__PURE__ */ new Set(["allowed-tools"]);
2024
- function kebabToCamel(key) {
2025
- return key.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
2026
- }
2027
- function parseFrontmatter2(content) {
2028
- const lines = content.split("\n");
2029
- if (lines[0]?.trim() !== "---") return null;
2030
- const result = {};
2031
- for (let i = 1; i < lines.length; i++) {
2032
- const line = lines[i];
2033
- if (line.trim() === "---") break;
2034
- const match = line.match(/^([a-z][a-z0-9-]*):\s*(.+)/);
2035
- if (!match) continue;
2036
- const key = match[1];
2037
- const rawValue = match[2].trim();
2038
- const camelKey = kebabToCamel(key);
2039
- if (BOOLEAN_KEYS.has(key)) {
2040
- result[camelKey] = rawValue === "true";
2041
- } else if (LIST_KEYS2.has(key)) {
2042
- result[camelKey] = rawValue.split(",").map((s) => s.trim());
2110
+ import { join as join11 } from "path";
2111
+ var TOOL_ARG_DISPLAY_MAX = 80;
2112
+ var TAIL_KEEP = 30;
2113
+ var MAX_COMPLETED_TOOLS = 50;
2114
+ var STREAMING_FLUSH_INTERVAL_MS = 16;
2115
+ var InteractiveSession = class {
2116
+ session = null;
2117
+ commandExecutor;
2118
+ listeners = /* @__PURE__ */ new Map();
2119
+ initialized = false;
2120
+ initPromise = null;
2121
+ // Streaming state
2122
+ streamingText = "";
2123
+ flushTimer = null;
2124
+ // Tool state
2125
+ activeTools = [];
2126
+ // Execution state
2127
+ executing = false;
2128
+ pendingPrompt = null;
2129
+ pendingDisplayInput;
2130
+ pendingRawInput;
2131
+ // Full history timeline (chat messages + events)
2132
+ history = [];
2133
+ constructor(options) {
2134
+ this.commandExecutor = new SystemCommandExecutor(createSystemCommands());
2135
+ if ("session" in options && options.session) {
2136
+ this.session = options.session;
2137
+ this.initialized = true;
2043
2138
  } else {
2044
- result[camelKey] = rawValue;
2139
+ const stdOpts = options;
2140
+ this.initPromise = this.initializeAsync(stdOpts);
2141
+ }
2142
+ }
2143
+ async initializeAsync(options) {
2144
+ const cwd = options.cwd;
2145
+ const [config, context, projectInfo] = await Promise.all([
2146
+ loadConfig(cwd),
2147
+ loadContext(cwd),
2148
+ detectProject(cwd)
2149
+ ]);
2150
+ const pluginsDir = join11(homedir3(), ".robota", "plugins");
2151
+ const pluginLoader = new BundlePluginLoader(pluginsDir);
2152
+ let mergedConfig = config;
2153
+ try {
2154
+ const plugins = pluginLoader.loadPluginsSync();
2155
+ if (plugins.length > 0) {
2156
+ const pluginHooks = mergePluginHooks(plugins);
2157
+ mergedConfig = {
2158
+ ...config,
2159
+ hooks: mergeHooksIntoConfig(
2160
+ config.hooks,
2161
+ pluginHooks
2162
+ )
2163
+ };
2164
+ }
2165
+ } catch {
2045
2166
  }
2167
+ const paths = projectPaths(cwd);
2168
+ this.session = createSession({
2169
+ config: mergedConfig,
2170
+ context,
2171
+ projectInfo,
2172
+ permissionMode: options.permissionMode,
2173
+ maxTurns: options.maxTurns,
2174
+ terminal: NOOP_TERMINAL,
2175
+ sessionLogger: new FileSessionLogger2(paths.logs),
2176
+ permissionHandler: options.permissionHandler,
2177
+ provider: options.provider,
2178
+ onTextDelta: (delta) => this.handleTextDelta(delta),
2179
+ onToolExecution: (event) => this.handleToolExecution(event)
2180
+ });
2181
+ this.initialized = true;
2046
2182
  }
2047
- return Object.keys(result).length > 0 ? result : null;
2048
- }
2049
- function buildCommand(frontmatter, content, fallbackName) {
2050
- const cmd = {
2051
- name: frontmatter?.name ?? fallbackName,
2052
- description: frontmatter?.description ?? `Skill: ${fallbackName}`,
2053
- source: "skill",
2054
- skillContent: content
2055
- };
2056
- if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
2057
- if (frontmatter?.disableModelInvocation !== void 0)
2058
- cmd.disableModelInvocation = frontmatter.disableModelInvocation;
2059
- if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
2060
- if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
2061
- if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
2062
- if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
2063
- if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
2064
- if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
2065
- return cmd;
2066
- }
2067
- function scanSkillsDir(skillsDir) {
2068
- if (!existsSync9(skillsDir)) return [];
2069
- const commands = [];
2070
- const entries = readdirSync3(skillsDir, { withFileTypes: true });
2071
- for (const entry of entries) {
2072
- if (!entry.isDirectory()) continue;
2073
- const skillFile = join10(skillsDir, entry.name, "SKILL.md");
2074
- if (!existsSync9(skillFile)) continue;
2075
- const content = readFileSync9(skillFile, "utf-8");
2076
- const frontmatter = parseFrontmatter2(content);
2077
- commands.push(buildCommand(frontmatter, content, entry.name));
2183
+ async ensureInitialized() {
2184
+ if (this.initialized) return;
2185
+ if (this.initPromise) await this.initPromise;
2078
2186
  }
2079
- return commands;
2080
- }
2081
- function scanCommandsDir(commandsDir) {
2082
- if (!existsSync9(commandsDir)) return [];
2083
- const commands = [];
2084
- const entries = readdirSync3(commandsDir, { withFileTypes: true });
2085
- for (const entry of entries) {
2086
- if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
2087
- const filePath = join10(commandsDir, entry.name);
2088
- const content = readFileSync9(filePath, "utf-8");
2089
- const frontmatter = parseFrontmatter2(content);
2090
- const fallbackName = basename2(entry.name, ".md");
2091
- commands.push(buildCommand(frontmatter, content, fallbackName));
2187
+ getSessionOrThrow() {
2188
+ if (!this.session)
2189
+ throw new Error("InteractiveSession not initialized. Call submit() or await initialization.");
2190
+ return this.session;
2092
2191
  }
2093
- return commands;
2094
- }
2095
- var SkillCommandSource = class {
2096
- name = "skill";
2097
- cwd;
2098
- home;
2099
- cachedCommands = null;
2100
- constructor(cwd, home) {
2101
- this.cwd = cwd;
2102
- this.home = home ?? homedir3();
2103
- }
2104
- getCommands() {
2105
- if (this.cachedCommands) return this.cachedCommands;
2106
- const sources = [
2107
- scanSkillsDir(join10(this.cwd, ".claude", "skills")),
2108
- scanCommandsDir(join10(this.cwd, ".claude", "commands")),
2109
- scanSkillsDir(join10(this.home, ".robota", "skills")),
2110
- scanSkillsDir(join10(this.cwd, ".agents", "skills"))
2111
- ];
2112
- const seen = /* @__PURE__ */ new Set();
2113
- const merged = [];
2114
- for (const commands of sources) {
2115
- for (const cmd of commands) {
2116
- if (!seen.has(cmd.name)) {
2117
- seen.add(cmd.name);
2118
- merged.push(cmd);
2119
- }
2120
- }
2121
- }
2122
- this.cachedCommands = merged;
2123
- return this.cachedCommands;
2124
- }
2125
- getModelInvocableSkills() {
2126
- return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
2127
- }
2128
- getUserInvocableSkills() {
2129
- return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
2130
- }
2131
- };
2132
-
2133
- // src/commands/system-command.ts
2134
- var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
2135
- function createSystemCommands() {
2136
- return [
2137
- {
2138
- name: "help",
2139
- description: "Show available commands",
2140
- execute: (_session, _args) => ({
2141
- message: [
2142
- "Available commands:",
2143
- " help \u2014 Show this help",
2144
- " clear \u2014 Clear conversation",
2145
- " compact [instr] \u2014 Compact context (optional focus instructions)",
2146
- " mode [m] \u2014 Show/change permission mode",
2147
- " model <id> \u2014 Change AI model",
2148
- " language <code> \u2014 Set response language (ko, en, ja, zh)",
2149
- " cost \u2014 Show session info",
2150
- " context \u2014 Context window info",
2151
- " permissions \u2014 Permission rules",
2152
- " reset \u2014 Delete settings and exit"
2153
- ].join("\n"),
2154
- success: true
2155
- })
2156
- },
2157
- {
2158
- name: "clear",
2159
- description: "Clear conversation history",
2160
- execute: (session, _args) => {
2161
- const underlying = session.getSession();
2162
- underlying.clearHistory();
2163
- return { message: "Conversation cleared.", success: true };
2164
- }
2165
- },
2166
- {
2167
- name: "compact",
2168
- description: "Compress context window",
2169
- execute: async (session, args) => {
2170
- const underlying = session.getSession();
2171
- const instructions = args.trim() || void 0;
2172
- const before = underlying.getContextState().usedPercentage;
2173
- await underlying.compact(instructions);
2174
- const after = underlying.getContextState().usedPercentage;
2175
- return {
2176
- message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
2177
- success: true,
2178
- data: { before, after }
2179
- };
2180
- }
2181
- },
2182
- {
2183
- name: "mode",
2184
- description: "Show/change permission mode",
2185
- execute: (session, args) => {
2186
- const underlying = session.getSession();
2187
- const arg = args.trim().split(/\s+/)[0];
2188
- if (!arg) {
2189
- return {
2190
- message: `Current mode: ${underlying.getPermissionMode()}`,
2191
- success: true,
2192
- data: { mode: underlying.getPermissionMode() }
2193
- };
2194
- }
2195
- if (VALID_MODES.includes(arg)) {
2196
- underlying.setPermissionMode(arg);
2197
- return {
2198
- message: `Permission mode set to: ${arg}`,
2199
- success: true,
2200
- data: { mode: arg }
2201
- };
2202
- }
2203
- return {
2204
- message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
2205
- success: false
2206
- };
2207
- }
2208
- },
2209
- {
2210
- name: "model",
2211
- description: "Change AI model",
2212
- execute: (_session, args) => {
2213
- const modelId = args.trim().split(/\s+/)[0];
2214
- if (!modelId) {
2215
- return { message: "Usage: model <model-id>", success: false };
2216
- }
2217
- return {
2218
- message: `Model change requested: ${modelId}`,
2219
- success: true,
2220
- data: { modelId }
2221
- };
2222
- }
2223
- },
2224
- {
2225
- name: "language",
2226
- description: "Set response language",
2227
- execute: (_session, args) => {
2228
- const lang = args.trim().split(/\s+/)[0];
2229
- if (!lang) {
2230
- return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
2231
- }
2232
- return {
2233
- message: `Language set to "${lang}".`,
2234
- success: true,
2235
- data: { language: lang }
2236
- };
2237
- }
2238
- },
2239
- {
2240
- name: "cost",
2241
- description: "Show session info",
2242
- execute: (session, _args) => {
2243
- const underlying = session.getSession();
2244
- const sessionId = underlying.getSessionId();
2245
- const messageCount = underlying.getMessageCount();
2246
- return {
2247
- message: `Session: ${sessionId}
2248
- Messages: ${messageCount}`,
2249
- success: true,
2250
- data: { sessionId, messageCount }
2251
- };
2252
- }
2253
- },
2254
- {
2255
- name: "context",
2256
- description: "Context window info",
2257
- execute: (session, _args) => {
2258
- const ctx = session.getContextState();
2259
- return {
2260
- message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
2261
- success: true,
2262
- data: {
2263
- usedTokens: ctx.usedTokens,
2264
- maxTokens: ctx.maxTokens,
2265
- percentage: ctx.usedPercentage
2266
- }
2267
- };
2268
- }
2269
- },
2270
- {
2271
- name: "permissions",
2272
- description: "Show permission rules",
2273
- execute: (session, _args) => {
2274
- const underlying = session.getSession();
2275
- const mode = underlying.getPermissionMode();
2276
- const sessionAllowed = underlying.getSessionAllowedTools();
2277
- const lines = [`Permission mode: ${mode}`];
2278
- if (sessionAllowed.length > 0) {
2279
- lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
2280
- } else {
2281
- lines.push("No session-approved tools.");
2282
- }
2283
- return {
2284
- message: lines.join("\n"),
2285
- success: true,
2286
- data: { mode, sessionAllowed }
2287
- };
2288
- }
2289
- },
2290
- {
2291
- name: "reset",
2292
- description: "Delete settings",
2293
- execute: (_session, _args) => {
2294
- return {
2295
- message: "Reset requested.",
2296
- success: true,
2297
- data: { resetRequested: true }
2298
- };
2299
- }
2300
- }
2301
- ];
2302
- }
2303
- var SystemCommandExecutor = class {
2304
- commands;
2305
- constructor(commands) {
2306
- this.commands = /* @__PURE__ */ new Map();
2307
- for (const cmd of commands ?? createSystemCommands()) {
2308
- this.commands.set(cmd.name, cmd);
2309
- }
2310
- }
2311
- /** Register an additional command. */
2312
- register(command) {
2313
- this.commands.set(command.name, command);
2314
- }
2315
- /** Execute a command by name. Returns null if command not found. */
2316
- async execute(name, session, args) {
2317
- const cmd = this.commands.get(name);
2318
- if (!cmd) return null;
2319
- return await cmd.execute(session, args);
2320
- }
2321
- /** List all registered commands. */
2322
- listCommands() {
2323
- return [...this.commands.values()];
2324
- }
2325
- /** Check if a command exists. */
2326
- hasCommand(name) {
2327
- return this.commands.has(name);
2328
- }
2329
- };
2330
-
2331
- // src/interactive/interactive-session.ts
2332
- import { FileSessionLogger as FileSessionLogger2 } from "@robota-sdk/agent-sessions";
2333
- import {
2334
- createUserMessage,
2335
- createAssistantMessage,
2336
- createSystemMessage
2337
- } from "@robota-sdk/agent-core";
2338
- var TOOL_ARG_DISPLAY_MAX = 80;
2339
- var TAIL_KEEP = 30;
2340
- var MAX_COMPLETED_TOOLS = 50;
2341
- var STREAMING_FLUSH_INTERVAL_MS = 16;
2342
- var InteractiveSession = class {
2343
- session;
2344
- listeners = /* @__PURE__ */ new Map();
2345
- // Streaming state
2346
- streamingText = "";
2347
- flushTimer = null;
2348
- // Tool state
2349
- activeTools = [];
2350
- // Execution state
2351
- executing = false;
2352
- pendingPrompt = null;
2353
- pendingDisplayInput;
2354
- pendingRawInput;
2355
- // Display messages (what clients render — not the raw session history)
2356
- messages = [];
2357
- constructor(options) {
2358
- if (options.session) {
2359
- this.session = options.session;
2360
- } else {
2361
- const cwd = options.cwd ?? process.cwd();
2362
- const paths = projectPaths(cwd);
2363
- this.session = createSession({
2364
- config: options.config,
2365
- context: options.context,
2366
- projectInfo: options.projectInfo,
2367
- sessionStore: options.sessionStore,
2368
- permissionMode: options.permissionMode,
2369
- maxTurns: options.maxTurns,
2370
- terminal: NOOP_TERMINAL,
2371
- sessionLogger: new FileSessionLogger2(paths.logs),
2372
- permissionHandler: options.permissionHandler,
2373
- onTextDelta: (delta) => this.handleTextDelta(delta),
2374
- onToolExecution: (event) => this.handleToolExecution(event)
2375
- });
2376
- }
2377
- }
2378
- // ── Event system ──────────────────────────────────────────────
2379
- on(event, handler) {
2380
- if (!this.listeners.has(event)) {
2381
- this.listeners.set(event, /* @__PURE__ */ new Set());
2382
- }
2383
- this.listeners.get(event).add(handler);
2192
+ // ── Event system ──────────────────────────────────────────────
2193
+ on(event, handler) {
2194
+ if (!this.listeners.has(event)) {
2195
+ this.listeners.set(event, /* @__PURE__ */ new Set());
2196
+ }
2197
+ this.listeners.get(event).add(handler);
2384
2198
  }
2385
2199
  off(event, handler) {
2386
2200
  this.listeners.get(event)?.delete(handler);
@@ -2394,10 +2208,9 @@ var InteractiveSession = class {
2394
2208
  }
2395
2209
  }
2396
2210
  // ── Public API ────────────────────────────────────────────────
2397
- /** Submit a prompt. Queues if already executing (max 1 queued).
2398
- * displayInput overrides what appears as the user message (e.g., "/audit" instead of full skill prompt).
2399
- * rawInput is passed to Session.run() for hook matching (e.g., "/rulebased-harness:audit"). */
2211
+ /** Submit a prompt. Queues if already executing (max 1 queued). */
2400
2212
  async submit(input, displayInput, rawInput) {
2213
+ await this.ensureInitialized();
2401
2214
  if (this.executing) {
2402
2215
  this.pendingPrompt = input;
2403
2216
  this.pendingDisplayInput = displayInput;
@@ -2406,14 +2219,23 @@ var InteractiveSession = class {
2406
2219
  }
2407
2220
  await this.executePrompt(input, displayInput, rawInput);
2408
2221
  }
2222
+ /** Execute a system command by name. Returns null if not found. */
2223
+ async executeCommand(name, args) {
2224
+ await this.ensureInitialized();
2225
+ return this.commandExecutor.execute(name, this, args);
2226
+ }
2409
2227
  /** Abort current execution and clear queue. */
2410
2228
  abort() {
2411
2229
  this.pendingPrompt = null;
2412
- this.session.abort();
2230
+ this.pendingDisplayInput = void 0;
2231
+ this.pendingRawInput = void 0;
2232
+ this.session?.abort();
2413
2233
  }
2414
2234
  /** Cancel queued prompt without aborting current execution. */
2415
2235
  cancelQueue() {
2416
2236
  this.pendingPrompt = null;
2237
+ this.pendingDisplayInput = void 0;
2238
+ this.pendingRawInput = void 0;
2417
2239
  }
2418
2240
  isExecuting() {
2419
2241
  return this.executing;
@@ -2421,8 +2243,13 @@ var InteractiveSession = class {
2421
2243
  getPendingPrompt() {
2422
2244
  return this.pendingPrompt;
2423
2245
  }
2246
+ /** Get full history timeline (chat + events) for TUI rendering */
2247
+ getFullHistory() {
2248
+ return this.history;
2249
+ }
2250
+ /** Get chat messages only (backward compatible) */
2424
2251
  getMessages() {
2425
- return this.messages;
2252
+ return this.history.filter((e) => e.category === "chat").map((e) => e.data);
2426
2253
  }
2427
2254
  getStreamingText() {
2428
2255
  return this.streamingText;
@@ -2431,39 +2258,44 @@ var InteractiveSession = class {
2431
2258
  return this.activeTools;
2432
2259
  }
2433
2260
  getContextState() {
2434
- return this.session.getContextState();
2261
+ return this.getSessionOrThrow().getContextState();
2435
2262
  }
2263
+ /** Access underlying Session. For advanced use / testing only. */
2436
2264
  getSession() {
2437
- return this.session;
2265
+ return this.getSessionOrThrow();
2438
2266
  }
2439
2267
  // ── Execution ─────────────────────────────────────────────────
2440
2268
  async executePrompt(input, displayInput, rawInput) {
2441
2269
  this.executing = true;
2442
2270
  this.clearStreaming();
2443
2271
  this.emit("thinking", true);
2444
- this.messages.push(createUserMessage(displayInput ?? input));
2445
- const historyBefore = this.session.getHistory().length;
2272
+ this.history.push(messageToHistoryEntry(createUserMessage(displayInput ?? input)));
2273
+ const historyBefore = this.getSessionOrThrow().getHistory().length;
2446
2274
  try {
2447
- const response = await this.session.run(input, rawInput);
2275
+ const response = await this.getSessionOrThrow().run(input, rawInput);
2448
2276
  this.flushStreaming();
2277
+ this.pushToolSummaryMessage();
2449
2278
  this.clearStreaming();
2450
2279
  const result = this.buildResult(response || "(empty response)", historyBefore);
2451
- this.messages.push(createAssistantMessage(result.response));
2280
+ this.history.push(messageToHistoryEntry(createAssistantMessage(result.response)));
2452
2281
  this.emit("complete", result);
2453
2282
  this.emit("context_update", this.getContextState());
2454
2283
  } catch (err) {
2455
2284
  this.flushStreaming();
2456
- this.clearStreaming();
2457
2285
  if (isAbortError(err)) {
2458
2286
  const result = this.buildInterruptedResult(historyBefore);
2287
+ this.pushToolSummaryMessage();
2288
+ this.clearStreaming();
2459
2289
  if (result.response) {
2460
- this.messages.push(createAssistantMessage(result.response));
2290
+ this.history.push(messageToHistoryEntry(createAssistantMessage(result.response)));
2461
2291
  }
2462
- this.messages.push(createSystemMessage("Interrupted by user."));
2292
+ this.history.push(messageToHistoryEntry(createSystemMessage("Interrupted by user.")));
2463
2293
  this.emit("interrupted", result);
2464
2294
  } else {
2295
+ this.pushToolSummaryMessage();
2296
+ this.clearStreaming();
2465
2297
  const errMsg = err instanceof Error ? err.message : String(err);
2466
- this.messages.push(createSystemMessage(`Error: ${errMsg}`));
2298
+ this.history.push(messageToHistoryEntry(createSystemMessage(`Error: ${errMsg}`)));
2467
2299
  this.emit("error", err instanceof Error ? err : new Error(errMsg));
2468
2300
  }
2469
2301
  } finally {
@@ -2493,22 +2325,14 @@ var InteractiveSession = class {
2493
2325
  handleToolExecution(event) {
2494
2326
  if (event.type === "start") {
2495
2327
  const firstArg = extractFirstArg(event.toolArgs);
2496
- const state = {
2497
- toolName: event.toolName,
2498
- firstArg,
2499
- isRunning: true
2500
- };
2328
+ const state = { toolName: event.toolName, firstArg, isRunning: true };
2501
2329
  this.activeTools.push(state);
2502
2330
  this.emit("tool_start", state);
2503
2331
  } else {
2504
2332
  const result = event.denied ? "denied" : event.success === false ? "error" : "success";
2505
2333
  const idx = this.activeTools.findIndex((t) => t.toolName === event.toolName && t.isRunning);
2506
2334
  if (idx !== -1) {
2507
- const finished = {
2508
- ...this.activeTools[idx],
2509
- isRunning: false,
2510
- result
2511
- };
2335
+ const finished = { ...this.activeTools[idx], isRunning: false, result };
2512
2336
  this.activeTools[idx] = finished;
2513
2337
  this.trimCompletedTools();
2514
2338
  this.emit("tool_end", finished);
@@ -2516,6 +2340,31 @@ var InteractiveSession = class {
2516
2340
  }
2517
2341
  }
2518
2342
  // ── Helpers ───────────────────────────────────────────────────
2343
+ /** Push tool execution summary into messages (before Robota response).
2344
+ * Moves tool info from activeTools (real-time display) to messages (permanent display).
2345
+ * After this, activeTools will be cleared by clearStreaming(). */
2346
+ pushToolSummaryMessage() {
2347
+ if (this.activeTools.length === 0) return;
2348
+ const summary = this.activeTools.map((t) => {
2349
+ const status = t.isRunning ? "\u27F3" : t.result === "success" ? "\u2713" : t.result === "error" ? "\u2717" : "\u2298";
2350
+ return `${status} ${t.toolName}${t.firstArg ? `(${t.firstArg})` : ""}`;
2351
+ }).join("\n");
2352
+ this.history.push({
2353
+ id: randomUUID(),
2354
+ timestamp: /* @__PURE__ */ new Date(),
2355
+ category: "event",
2356
+ type: "tool-summary",
2357
+ data: {
2358
+ tools: this.activeTools.map((t) => ({
2359
+ toolName: t.toolName,
2360
+ firstArg: t.firstArg,
2361
+ isRunning: t.isRunning,
2362
+ result: t.result
2363
+ })),
2364
+ summary
2365
+ }
2366
+ });
2367
+ }
2519
2368
  clearStreaming() {
2520
2369
  this.streamingText = "";
2521
2370
  this.activeTools = [];
@@ -2534,30 +2383,28 @@ var InteractiveSession = class {
2534
2383
  const toolSummaries = this.extractToolSummaries(historyBefore);
2535
2384
  return {
2536
2385
  response,
2537
- messages: this.messages,
2386
+ history: this.history,
2538
2387
  toolSummaries,
2539
2388
  contextState: this.getContextState()
2540
2389
  };
2541
2390
  }
2542
2391
  buildInterruptedResult(historyBefore) {
2543
- const history = this.session.getHistory();
2392
+ const history = this.getSessionOrThrow().getHistory();
2544
2393
  const toolSummaries = this.extractToolSummaries(historyBefore);
2545
2394
  const parts = [];
2546
2395
  for (let i = historyBefore; i < history.length; i++) {
2547
2396
  const msg = history[i];
2548
- if (msg?.role === "assistant" && msg.content) {
2549
- parts.push(msg.content);
2550
- }
2397
+ if (msg?.role === "assistant" && msg.content) parts.push(msg.content);
2551
2398
  }
2552
2399
  return {
2553
2400
  response: parts.join("\n\n"),
2554
- messages: this.messages,
2401
+ history: this.history,
2555
2402
  toolSummaries,
2556
2403
  contextState: this.getContextState()
2557
2404
  };
2558
2405
  }
2559
2406
  extractToolSummaries(historyBefore) {
2560
- const history = this.session.getHistory();
2407
+ const history = this.getSessionOrThrow().getHistory();
2561
2408
  const summaries = [];
2562
2409
  for (let i = historyBefore; i < history.length; i++) {
2563
2410
  const msg = history[i];
@@ -2609,13 +2456,400 @@ var NOOP_TERMINAL = {
2609
2456
  } })
2610
2457
  };
2611
2458
 
2459
+ // src/query.ts
2460
+ function createQuery(options) {
2461
+ const session = new InteractiveSession({
2462
+ cwd: options.cwd ?? process.cwd(),
2463
+ provider: options.provider,
2464
+ permissionMode: options.permissionMode ?? "bypassPermissions",
2465
+ maxTurns: options.maxTurns,
2466
+ permissionHandler: options.permissionHandler
2467
+ });
2468
+ if (options.onTextDelta) {
2469
+ session.on("text_delta", options.onTextDelta);
2470
+ }
2471
+ return async (prompt) => {
2472
+ return new Promise((resolve2, reject) => {
2473
+ const onComplete = (result) => {
2474
+ cleanup();
2475
+ resolve2(result.response);
2476
+ };
2477
+ const onInterrupted = (result) => {
2478
+ cleanup();
2479
+ resolve2(result.response);
2480
+ };
2481
+ const onError = (error) => {
2482
+ cleanup();
2483
+ reject(error);
2484
+ };
2485
+ const cleanup = () => {
2486
+ session.off("complete", onComplete);
2487
+ session.off("interrupted", onInterrupted);
2488
+ session.off("error", onError);
2489
+ };
2490
+ session.on("complete", onComplete);
2491
+ session.on("interrupted", onInterrupted);
2492
+ session.on("error", onError);
2493
+ session.submit(prompt).catch((err) => {
2494
+ cleanup();
2495
+ reject(err instanceof Error ? err : new Error(String(err)));
2496
+ });
2497
+ });
2498
+ };
2499
+ }
2500
+
2501
+ // src/commands/command-registry.ts
2502
+ var CommandRegistry = class {
2503
+ sources = [];
2504
+ addSource(source) {
2505
+ this.sources.push(source);
2506
+ }
2507
+ /** Get all commands, optionally filtered by prefix */
2508
+ getCommands(filter) {
2509
+ const all = [];
2510
+ for (const source of this.sources) {
2511
+ all.push(...source.getCommands());
2512
+ }
2513
+ if (!filter) return all;
2514
+ const lower = filter.toLowerCase();
2515
+ return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
2516
+ }
2517
+ /** Resolve a short name to its fully qualified plugin:name form */
2518
+ resolveQualifiedName(shortName) {
2519
+ const matches = this.getCommands().filter(
2520
+ (c) => c.source === "plugin" && c.name.includes(":") && c.name.endsWith(`:${shortName}`)
2521
+ );
2522
+ if (matches.length !== 1) return null;
2523
+ return matches[0].name;
2524
+ }
2525
+ /** Get subcommands for a specific command */
2526
+ getSubcommands(commandName) {
2527
+ const lower = commandName.toLowerCase();
2528
+ for (const source of this.sources) {
2529
+ for (const cmd of source.getCommands()) {
2530
+ if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
2531
+ return cmd.subcommands;
2532
+ }
2533
+ }
2534
+ }
2535
+ return [];
2536
+ }
2537
+ };
2538
+
2539
+ // src/commands/builtin-source.ts
2540
+ import { CLAUDE_MODELS, formatTokenCount } from "@robota-sdk/agent-core";
2541
+ function buildModelSubcommands() {
2542
+ const seen = /* @__PURE__ */ new Set();
2543
+ const commands = [];
2544
+ for (const model of Object.values(CLAUDE_MODELS)) {
2545
+ if (seen.has(model.name)) continue;
2546
+ seen.add(model.name);
2547
+ commands.push({
2548
+ name: model.id,
2549
+ description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
2550
+ source: "builtin"
2551
+ });
2552
+ }
2553
+ return commands;
2554
+ }
2555
+ function createBuiltinCommands() {
2556
+ return [
2557
+ { name: "help", description: "Show available commands", source: "builtin" },
2558
+ { name: "clear", description: "Clear conversation history", source: "builtin" },
2559
+ {
2560
+ name: "mode",
2561
+ description: "Permission mode",
2562
+ source: "builtin",
2563
+ subcommands: [
2564
+ { name: "plan", description: "Plan only, no execution", source: "builtin" },
2565
+ { name: "default", description: "Ask before risky actions", source: "builtin" },
2566
+ { name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
2567
+ { name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
2568
+ ]
2569
+ },
2570
+ {
2571
+ name: "model",
2572
+ description: "Select AI model",
2573
+ source: "builtin",
2574
+ subcommands: buildModelSubcommands()
2575
+ },
2576
+ {
2577
+ name: "language",
2578
+ description: "Set response language",
2579
+ source: "builtin",
2580
+ subcommands: [
2581
+ { name: "ko", description: "Korean", source: "builtin" },
2582
+ { name: "en", description: "English", source: "builtin" },
2583
+ { name: "ja", description: "Japanese", source: "builtin" },
2584
+ { name: "zh", description: "Chinese", source: "builtin" }
2585
+ ]
2586
+ },
2587
+ { name: "compact", description: "Compress context window", source: "builtin" },
2588
+ { name: "cost", description: "Show session info", source: "builtin" },
2589
+ { name: "context", description: "Context window info", source: "builtin" },
2590
+ { name: "permissions", description: "Permission rules", source: "builtin" },
2591
+ { name: "plugin", description: "Manage plugins", source: "builtin" },
2592
+ { name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
2593
+ { name: "reset", description: "Delete settings and exit", source: "builtin" },
2594
+ { name: "exit", description: "Exit CLI", source: "builtin" }
2595
+ ];
2596
+ }
2597
+ var BuiltinCommandSource = class {
2598
+ name = "builtin";
2599
+ commands;
2600
+ constructor() {
2601
+ this.commands = createBuiltinCommands();
2602
+ }
2603
+ getCommands() {
2604
+ return this.commands;
2605
+ }
2606
+ };
2607
+
2608
+ // src/commands/skill-source.ts
2609
+ import { readdirSync as readdirSync3, readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
2610
+ import { join as join12, basename as basename2 } from "path";
2611
+ import { homedir as homedir4 } from "os";
2612
+ var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
2613
+ var LIST_KEYS2 = /* @__PURE__ */ new Set(["allowed-tools"]);
2614
+ function kebabToCamel(key) {
2615
+ return key.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
2616
+ }
2617
+ function parseFrontmatter2(content) {
2618
+ const lines = content.split("\n");
2619
+ if (lines[0]?.trim() !== "---") return null;
2620
+ const result = {};
2621
+ for (let i = 1; i < lines.length; i++) {
2622
+ const line = lines[i];
2623
+ if (line.trim() === "---") break;
2624
+ const match = line.match(/^([a-z][a-z0-9-]*):\s*(.+)/);
2625
+ if (!match) continue;
2626
+ const key = match[1];
2627
+ const rawValue = match[2].trim();
2628
+ const camelKey = kebabToCamel(key);
2629
+ if (BOOLEAN_KEYS.has(key)) {
2630
+ result[camelKey] = rawValue === "true";
2631
+ } else if (LIST_KEYS2.has(key)) {
2632
+ result[camelKey] = rawValue.split(",").map((s) => s.trim());
2633
+ } else {
2634
+ result[camelKey] = rawValue;
2635
+ }
2636
+ }
2637
+ return Object.keys(result).length > 0 ? result : null;
2638
+ }
2639
+ function buildCommand(frontmatter, content, fallbackName) {
2640
+ const cmd = {
2641
+ name: frontmatter?.name ?? fallbackName,
2642
+ description: frontmatter?.description ?? `Skill: ${fallbackName}`,
2643
+ source: "skill",
2644
+ skillContent: content
2645
+ };
2646
+ if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
2647
+ if (frontmatter?.disableModelInvocation !== void 0)
2648
+ cmd.disableModelInvocation = frontmatter.disableModelInvocation;
2649
+ if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
2650
+ if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
2651
+ if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
2652
+ if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
2653
+ if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
2654
+ if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
2655
+ return cmd;
2656
+ }
2657
+ function scanSkillsDir(skillsDir) {
2658
+ if (!existsSync9(skillsDir)) return [];
2659
+ const commands = [];
2660
+ const entries = readdirSync3(skillsDir, { withFileTypes: true });
2661
+ for (const entry of entries) {
2662
+ if (!entry.isDirectory()) continue;
2663
+ const skillFile = join12(skillsDir, entry.name, "SKILL.md");
2664
+ if (!existsSync9(skillFile)) continue;
2665
+ const content = readFileSync9(skillFile, "utf-8");
2666
+ const frontmatter = parseFrontmatter2(content);
2667
+ commands.push(buildCommand(frontmatter, content, entry.name));
2668
+ }
2669
+ return commands;
2670
+ }
2671
+ function scanCommandsDir(commandsDir) {
2672
+ if (!existsSync9(commandsDir)) return [];
2673
+ const commands = [];
2674
+ const entries = readdirSync3(commandsDir, { withFileTypes: true });
2675
+ for (const entry of entries) {
2676
+ if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
2677
+ const filePath = join12(commandsDir, entry.name);
2678
+ const content = readFileSync9(filePath, "utf-8");
2679
+ const frontmatter = parseFrontmatter2(content);
2680
+ const fallbackName = basename2(entry.name, ".md");
2681
+ commands.push(buildCommand(frontmatter, content, fallbackName));
2682
+ }
2683
+ return commands;
2684
+ }
2685
+ var SkillCommandSource = class {
2686
+ name = "skill";
2687
+ cwd;
2688
+ home;
2689
+ cachedCommands = null;
2690
+ constructor(cwd, home) {
2691
+ this.cwd = cwd;
2692
+ this.home = home ?? homedir4();
2693
+ }
2694
+ getCommands() {
2695
+ if (this.cachedCommands) return this.cachedCommands;
2696
+ const sources = [
2697
+ scanSkillsDir(join12(this.cwd, ".claude", "skills")),
2698
+ scanCommandsDir(join12(this.cwd, ".claude", "commands")),
2699
+ scanSkillsDir(join12(this.home, ".robota", "skills")),
2700
+ scanSkillsDir(join12(this.cwd, ".agents", "skills"))
2701
+ ];
2702
+ const seen = /* @__PURE__ */ new Set();
2703
+ const merged = [];
2704
+ for (const commands of sources) {
2705
+ for (const cmd of commands) {
2706
+ if (!seen.has(cmd.name)) {
2707
+ seen.add(cmd.name);
2708
+ merged.push(cmd);
2709
+ }
2710
+ }
2711
+ }
2712
+ this.cachedCommands = merged;
2713
+ return this.cachedCommands;
2714
+ }
2715
+ getModelInvocableSkills() {
2716
+ return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
2717
+ }
2718
+ getUserInvocableSkills() {
2719
+ return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
2720
+ }
2721
+ };
2722
+
2723
+ // src/commands/plugin-source.ts
2724
+ var PluginCommandSource = class {
2725
+ name = "plugin";
2726
+ plugins;
2727
+ constructor(plugins) {
2728
+ this.plugins = plugins;
2729
+ }
2730
+ getCommands() {
2731
+ const commands = [];
2732
+ for (const plugin of this.plugins) {
2733
+ for (const skill of plugin.skills) {
2734
+ const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
2735
+ commands.push({
2736
+ name: baseName,
2737
+ description: `(${plugin.manifest.name}) ${skill.description}`,
2738
+ source: "plugin",
2739
+ skillContent: skill.skillContent,
2740
+ pluginDir: plugin.pluginDir
2741
+ });
2742
+ }
2743
+ for (const cmd of plugin.commands) {
2744
+ commands.push({
2745
+ name: cmd.name,
2746
+ description: cmd.description,
2747
+ source: "plugin",
2748
+ skillContent: cmd.skillContent,
2749
+ pluginDir: plugin.pluginDir
2750
+ });
2751
+ }
2752
+ }
2753
+ return commands;
2754
+ }
2755
+ };
2756
+
2757
+ // src/utils/skill-prompt.ts
2758
+ import { execSync as execSync3 } from "child_process";
2759
+ function substituteVariables(content, args, context) {
2760
+ const argParts = args ? args.split(/\s+/) : [];
2761
+ let result = content;
2762
+ result = result.replace(/\$ARGUMENTS\[(\d+)]/g, (_match, index) => {
2763
+ return argParts[Number(index)] ?? "";
2764
+ });
2765
+ result = result.replace(/\$ARGUMENTS/g, args);
2766
+ result = result.replace(/\$(\d)(?!\d|\w|\[)/g, (_match, digit) => {
2767
+ return argParts[Number(digit)] ?? "";
2768
+ });
2769
+ result = result.replace(/\$\{CLAUDE_SESSION_ID}/g, context?.sessionId ?? "");
2770
+ result = result.replace(/\$\{CLAUDE_SKILL_DIR}/g, context?.skillDir ?? "");
2771
+ return result;
2772
+ }
2773
+ async function preprocessShellCommands(content) {
2774
+ const shellPattern = /!`([^`]+)`/g;
2775
+ if (!shellPattern.test(content)) {
2776
+ return content;
2777
+ }
2778
+ shellPattern.lastIndex = 0;
2779
+ let result = content;
2780
+ let match;
2781
+ const matches = [];
2782
+ while ((match = shellPattern.exec(content)) !== null) {
2783
+ matches.push({ full: match[0], command: match[1] });
2784
+ }
2785
+ for (const { full, command } of matches) {
2786
+ let output = "";
2787
+ try {
2788
+ output = execSync3(command, {
2789
+ timeout: 5e3,
2790
+ encoding: "utf-8",
2791
+ stdio: ["pipe", "pipe", "pipe"]
2792
+ }).trimEnd();
2793
+ } catch {
2794
+ output = "";
2795
+ }
2796
+ result = result.replace(full, output);
2797
+ }
2798
+ return result;
2799
+ }
2800
+ async function buildSkillPrompt(input, registry, context) {
2801
+ const parts = input.slice(1).split(/\s+/);
2802
+ const cmd = parts[0]?.toLowerCase() ?? "";
2803
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
2804
+ if (!skillCmd) return null;
2805
+ const args = parts.slice(1).join(" ").trim();
2806
+ const userInstruction = args || skillCmd.description;
2807
+ if (skillCmd.skillContent) {
2808
+ let processed = await preprocessShellCommands(skillCmd.skillContent);
2809
+ processed = substituteVariables(processed, args, context);
2810
+ return `<skill name="${cmd}">
2811
+ ${processed}
2812
+ </skill>
2813
+
2814
+ Execute the "${cmd}" skill: ${userInstruction}`;
2815
+ }
2816
+ return `Use the "${cmd}" skill: ${userInstruction}`;
2817
+ }
2818
+
2819
+ // src/types.ts
2820
+ import { TRUST_TO_MODE } from "@robota-sdk/agent-core";
2821
+
2612
2822
  // src/index.ts
2613
- import { bashTool as bashTool2 } from "@robota-sdk/agent-tools";
2614
- import { readTool as readTool2 } from "@robota-sdk/agent-tools";
2615
- import { writeTool as writeTool2 } from "@robota-sdk/agent-tools";
2616
- import { editTool as editTool2 } from "@robota-sdk/agent-tools";
2617
- import { globTool as globTool2 } from "@robota-sdk/agent-tools";
2618
- import { grepTool as grepTool2 } from "@robota-sdk/agent-tools";
2823
+ import {
2824
+ isChatEntry,
2825
+ chatEntryToMessage,
2826
+ messageToHistoryEntry as messageToHistoryEntry2,
2827
+ getMessagesForAPI
2828
+ } from "@robota-sdk/agent-core";
2829
+ import { evaluatePermission } from "@robota-sdk/agent-core";
2830
+
2831
+ // src/permissions/permission-prompt.ts
2832
+ import chalk from "chalk";
2833
+ var PERMISSION_OPTIONS = ["Allow", "Deny"];
2834
+ var ALLOW_INDEX = 0;
2835
+ function formatArgs(toolArgs) {
2836
+ const entries = Object.entries(toolArgs);
2837
+ if (entries.length === 0) {
2838
+ return "(no arguments)";
2839
+ }
2840
+ return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
2841
+ }
2842
+ async function promptForApproval(terminal, toolName, toolArgs) {
2843
+ terminal.writeLine("");
2844
+ terminal.writeLine(chalk.yellow(`[Permission Required] Tool: ${toolName}`));
2845
+ terminal.writeLine(chalk.dim(` ${formatArgs(toolArgs)}`));
2846
+ terminal.writeLine("");
2847
+ const selected = await terminal.select(PERMISSION_OPTIONS, ALLOW_INDEX);
2848
+ return selected === ALLOW_INDEX;
2849
+ }
2850
+
2851
+ // src/index.ts
2852
+ import { runHooks } from "@robota-sdk/agent-core";
2619
2853
  export {
2620
2854
  AgentExecutor,
2621
2855
  BUILT_IN_AGENTS,
@@ -2623,47 +2857,35 @@ export {
2623
2857
  BundlePluginInstaller,
2624
2858
  BundlePluginLoader,
2625
2859
  CommandRegistry,
2626
- DEFAULT_TOOL_DESCRIPTIONS,
2627
- FileSessionLogger3 as FileSessionLogger,
2628
2860
  InteractiveSession,
2629
2861
  MarketplaceClient,
2862
+ PluginCommandSource,
2630
2863
  PluginSettingsStore,
2631
2864
  PromptExecutor,
2632
- Session3 as Session,
2633
- SessionStore,
2634
- SilentSessionLogger,
2635
2865
  SkillCommandSource,
2636
- SystemCommandExecutor,
2637
2866
  TRUST_TO_MODE,
2638
2867
  assembleSubagentPrompt,
2639
- bashTool2 as bashTool,
2640
- buildSystemPrompt,
2868
+ buildSkillPrompt,
2869
+ chatEntryToMessage,
2641
2870
  createAgentTool,
2642
- createDefaultTools,
2643
- createProvider,
2644
- createSession,
2871
+ createQuery,
2645
2872
  createSubagentLogger,
2646
2873
  createSubagentSession,
2647
- createSystemCommands,
2648
- detectProject,
2649
- editTool2 as editTool,
2650
2874
  evaluatePermission,
2651
2875
  getBuiltInAgent,
2652
2876
  getForkWorkerSuffix,
2877
+ getMessagesForAPI,
2653
2878
  getSubagentSuffix,
2654
- globTool2 as globTool,
2655
- grepTool2 as grepTool,
2656
- loadConfig,
2657
- loadContext,
2879
+ isChatEntry,
2880
+ messageToHistoryEntry2 as messageToHistoryEntry,
2658
2881
  parseFrontmatter2 as parseFrontmatter,
2882
+ preprocessShellCommands,
2659
2883
  projectPaths,
2660
2884
  promptForApproval,
2661
- query,
2662
- readTool2 as readTool,
2663
2885
  resolveSubagentLogDir,
2664
2886
  retrieveAgentToolDeps,
2665
2887
  runHooks,
2666
2888
  storeAgentToolDeps,
2667
- userPaths,
2668
- writeTool2 as writeTool
2889
+ substituteVariables,
2890
+ userPaths
2669
2891
  };