@robota-sdk/agent-sdk 3.0.0-beta.59 → 3.0.0-beta.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -21
- package/dist/node/index.cjs +1690 -898
- package/dist/node/index.d.cts +738 -246
- package/dist/node/index.d.ts +738 -246
- package/dist/node/index.js +1487 -805
- package/package.json +5 -5
package/dist/node/index.js
CHANGED
|
@@ -31,6 +31,12 @@ var CommandRegistry = class {
|
|
|
31
31
|
addSource(source) {
|
|
32
32
|
this.sources.push(source);
|
|
33
33
|
}
|
|
34
|
+
replaceSource(name, source) {
|
|
35
|
+
this.sources = this.sources.filter((candidate) => candidate.name !== name);
|
|
36
|
+
if (source !== void 0) {
|
|
37
|
+
this.sources.push(source);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
34
40
|
addModule(module) {
|
|
35
41
|
for (const source of module.commandSources ?? []) {
|
|
36
42
|
this.addSource(source);
|
|
@@ -71,138 +77,222 @@ var CommandRegistry = class {
|
|
|
71
77
|
}
|
|
72
78
|
};
|
|
73
79
|
|
|
74
|
-
// src/commands/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
seen.add(model.name);
|
|
82
|
-
commands.push({
|
|
83
|
-
name: model.id,
|
|
84
|
-
description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
|
|
85
|
-
source: "builtin"
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
return commands;
|
|
89
|
-
}
|
|
90
|
-
function buildProviderSubcommands() {
|
|
91
|
-
return [
|
|
92
|
-
{ name: "current", description: "Show current provider", source: "builtin" },
|
|
93
|
-
{ name: "list", description: "List provider profiles", source: "builtin" },
|
|
94
|
-
{ name: "use", description: "Switch provider profile", source: "builtin" },
|
|
95
|
-
{ name: "test", description: "Test provider profile", source: "builtin" }
|
|
96
|
-
];
|
|
97
|
-
}
|
|
98
|
-
function buildBackgroundSubcommands() {
|
|
99
|
-
return [
|
|
100
|
-
{ name: "list", description: "List background tasks", source: "builtin" },
|
|
101
|
-
{ name: "read", description: "Read a background task log page", source: "builtin" },
|
|
102
|
-
{ name: "cancel", description: "Cancel a running background task", source: "builtin" },
|
|
103
|
-
{ name: "close", description: "Dismiss a terminal background task", source: "builtin" }
|
|
104
|
-
];
|
|
105
|
-
}
|
|
106
|
-
function buildRewindSubcommands() {
|
|
107
|
-
return [
|
|
108
|
-
{ name: "list", description: "List edit checkpoints", source: "builtin" },
|
|
109
|
-
{ name: "restore", description: "Restore code to a checkpoint", source: "builtin" },
|
|
110
|
-
{ name: "code", description: "Restore code to a checkpoint", source: "builtin" }
|
|
111
|
-
];
|
|
112
|
-
}
|
|
113
|
-
function buildMemorySubcommands() {
|
|
114
|
-
return [
|
|
115
|
-
{ name: "list", description: "List project memory topics", source: "builtin" },
|
|
116
|
-
{ name: "show", description: "Show project memory index or a topic", source: "builtin" },
|
|
117
|
-
{ name: "add", description: "Save durable project memory", source: "builtin" },
|
|
118
|
-
{ name: "pending", description: "List pending memory candidates", source: "builtin" },
|
|
119
|
-
{ name: "approve", description: "Approve a pending memory candidate", source: "builtin" },
|
|
120
|
-
{ name: "reject", description: "Reject a pending memory candidate", source: "builtin" },
|
|
121
|
-
{
|
|
122
|
-
name: "used",
|
|
123
|
-
description: "Show memory references used in the current turn",
|
|
124
|
-
source: "builtin"
|
|
80
|
+
// src/commands/system-command-executor.ts
|
|
81
|
+
var SystemCommandExecutor = class {
|
|
82
|
+
commands;
|
|
83
|
+
constructor(commands) {
|
|
84
|
+
this.commands = /* @__PURE__ */ new Map();
|
|
85
|
+
for (const cmd of commands ?? createSystemCommands()) {
|
|
86
|
+
this.commands.set(cmd.name, cmd);
|
|
125
87
|
}
|
|
126
|
-
|
|
88
|
+
}
|
|
89
|
+
/** Register an additional command. */
|
|
90
|
+
register(command) {
|
|
91
|
+
this.commands.set(command.name, command);
|
|
92
|
+
}
|
|
93
|
+
/** Execute a command by name. Returns null if command not found. */
|
|
94
|
+
async execute(name, session, args) {
|
|
95
|
+
const cmd = this.getCommand(name);
|
|
96
|
+
if (!cmd) return null;
|
|
97
|
+
return await this.executeCommand(cmd, session, args);
|
|
98
|
+
}
|
|
99
|
+
getCommand(name) {
|
|
100
|
+
return this.commands.get(name);
|
|
101
|
+
}
|
|
102
|
+
async executeCommand(command, session, args) {
|
|
103
|
+
return await command.execute(session, args);
|
|
104
|
+
}
|
|
105
|
+
/** List all registered commands. */
|
|
106
|
+
listCommands() {
|
|
107
|
+
return [...this.commands.values()];
|
|
108
|
+
}
|
|
109
|
+
listModelInvocableCommands() {
|
|
110
|
+
return this.listCommands().filter((command) => command.modelInvocable === true).map((command) => ({
|
|
111
|
+
name: `/${command.name}`,
|
|
112
|
+
kind: "builtin-command",
|
|
113
|
+
description: command.description,
|
|
114
|
+
userInvocable: command.userInvocable !== false,
|
|
115
|
+
modelInvocable: true,
|
|
116
|
+
...command.argumentHint ? { argumentHint: command.argumentHint } : {},
|
|
117
|
+
...command.safety ? { safety: command.safety } : {}
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
isModelInvocable(name) {
|
|
121
|
+
return this.commands.get(name)?.modelInvocable === true;
|
|
122
|
+
}
|
|
123
|
+
async executeModelInvocable(name, session, args) {
|
|
124
|
+
if (!this.isModelInvocable(name)) return null;
|
|
125
|
+
return this.execute(name, session, args);
|
|
126
|
+
}
|
|
127
|
+
/** Check if a command exists. */
|
|
128
|
+
hasCommand(name) {
|
|
129
|
+
return this.commands.has(name);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// src/commands/system-command.ts
|
|
134
|
+
function createSystemCommands() {
|
|
135
|
+
return [];
|
|
127
136
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
]
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
name: "model",
|
|
145
|
-
description: "Select AI model",
|
|
146
|
-
source: "builtin",
|
|
147
|
-
subcommands: buildModelSubcommands()
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
name: "language",
|
|
151
|
-
description: "Set response language",
|
|
152
|
-
source: "builtin",
|
|
153
|
-
subcommands: [
|
|
154
|
-
{ name: "ko", description: "Korean", source: "builtin" },
|
|
155
|
-
{ name: "en", description: "English", source: "builtin" },
|
|
156
|
-
{ name: "ja", description: "Japanese", source: "builtin" },
|
|
157
|
-
{ name: "zh", description: "Chinese", source: "builtin" }
|
|
158
|
-
]
|
|
159
|
-
},
|
|
160
|
-
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
161
|
-
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
162
|
-
{ name: "context", description: "Context window info", source: "builtin" },
|
|
163
|
-
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
164
|
-
{
|
|
165
|
-
name: "memory",
|
|
166
|
-
description: "Inspect, save, review, and audit project memory",
|
|
167
|
-
source: "builtin",
|
|
168
|
-
subcommands: buildMemorySubcommands()
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
name: "rewind",
|
|
172
|
-
description: "List and restore edit checkpoints",
|
|
173
|
-
source: "builtin",
|
|
174
|
-
subcommands: buildRewindSubcommands()
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
name: "provider",
|
|
178
|
-
description: "Manage provider profiles",
|
|
179
|
-
source: "builtin",
|
|
180
|
-
subcommands: buildProviderSubcommands()
|
|
181
|
-
},
|
|
182
|
-
{ name: "resume", description: "Resume a previous session", source: "builtin" },
|
|
183
|
-
{
|
|
184
|
-
name: "background",
|
|
185
|
-
description: "List and control background tasks",
|
|
186
|
-
source: "builtin",
|
|
187
|
-
subcommands: buildBackgroundSubcommands()
|
|
188
|
-
},
|
|
189
|
-
{ name: "rename", description: "Rename the current session", source: "builtin" },
|
|
190
|
-
{ name: "plugin", description: "Manage plugins", source: "builtin" },
|
|
191
|
-
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
192
|
-
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
193
|
-
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
194
|
-
];
|
|
137
|
+
|
|
138
|
+
// src/commands/builtin-source.ts
|
|
139
|
+
function commandToPaletteEntry(command) {
|
|
140
|
+
return {
|
|
141
|
+
name: command.name,
|
|
142
|
+
description: command.description,
|
|
143
|
+
source: "builtin",
|
|
144
|
+
...command.subcommands ? { subcommands: [...command.subcommands] } : {},
|
|
145
|
+
...command.argumentHint ? { argumentHint: command.argumentHint } : {},
|
|
146
|
+
...command.modelInvocable !== void 0 ? { modelInvocable: command.modelInvocable } : {},
|
|
147
|
+
...command.userInvocable !== void 0 ? { userInvocable: command.userInvocable } : {},
|
|
148
|
+
...command.safety ? { safety: command.safety } : {}
|
|
149
|
+
};
|
|
195
150
|
}
|
|
196
151
|
var BuiltinCommandSource = class {
|
|
197
152
|
name = "builtin";
|
|
198
153
|
commands;
|
|
199
|
-
constructor() {
|
|
200
|
-
this.commands =
|
|
154
|
+
constructor(systemCommands = createSystemCommands()) {
|
|
155
|
+
this.commands = systemCommands.map(commandToPaletteEntry);
|
|
201
156
|
}
|
|
202
157
|
getCommands() {
|
|
203
158
|
return this.commands;
|
|
204
159
|
}
|
|
205
160
|
};
|
|
161
|
+
function createBuiltinCommandModule() {
|
|
162
|
+
const systemCommands = createSystemCommands();
|
|
163
|
+
return {
|
|
164
|
+
name: "sdk-builtin",
|
|
165
|
+
commandSources: [new BuiltinCommandSource(systemCommands)],
|
|
166
|
+
systemCommands
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/command-api/provider/provider-command-probe.ts
|
|
171
|
+
import { findProviderDefinition as findProviderDefinition2 } from "@robota-sdk/agent-core";
|
|
172
|
+
|
|
173
|
+
// src/command-api/provider/provider-settings.ts
|
|
174
|
+
import { findProviderDefinition } from "@robota-sdk/agent-core";
|
|
175
|
+
|
|
176
|
+
// src/command-api/provider/provider-env-ref.ts
|
|
177
|
+
var ENV_REFERENCE_PREFIX = "$ENV:";
|
|
178
|
+
function isEnvReference(value) {
|
|
179
|
+
return value.startsWith(ENV_REFERENCE_PREFIX);
|
|
180
|
+
}
|
|
181
|
+
function formatEnvReference(name) {
|
|
182
|
+
return `${ENV_REFERENCE_PREFIX}${name}`;
|
|
183
|
+
}
|
|
184
|
+
function resolveEnvReference(value) {
|
|
185
|
+
if (!isEnvReference(value)) {
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
const envName = value.slice(ENV_REFERENCE_PREFIX.length).trim();
|
|
189
|
+
if (envName.length === 0) {
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
return process.env[envName];
|
|
193
|
+
}
|
|
194
|
+
function hasUsableSecretReference(value) {
|
|
195
|
+
if (value === void 0 || value.length === 0) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
return resolveEnvReference(value) !== void 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/command-api/provider/provider-settings.ts
|
|
202
|
+
function upsertProviderProfile(settings, profileName, profile) {
|
|
203
|
+
return {
|
|
204
|
+
...settings,
|
|
205
|
+
providers: {
|
|
206
|
+
...settings.providers ?? {},
|
|
207
|
+
[profileName]: profile
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function setCurrentProvider(settings, profileName) {
|
|
212
|
+
if (!settings.providers?.[profileName]) {
|
|
213
|
+
throw new Error(`Provider profile "${profileName}" was not found`);
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
...settings,
|
|
217
|
+
currentProvider: profileName
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function validateProviderProfile(profileName, profile, options = {}) {
|
|
221
|
+
if (!profile.type) {
|
|
222
|
+
throw new Error(`Provider profile "${profileName}" is missing type`);
|
|
223
|
+
}
|
|
224
|
+
if (!profile.model) {
|
|
225
|
+
throw new Error(`Provider profile "${profileName}" is missing model`);
|
|
226
|
+
}
|
|
227
|
+
const definition = findProviderDefinition(options.providerDefinitions ?? [], profile.type);
|
|
228
|
+
if (definition?.requiresApiKey === true && !hasUsableSecretReference(profile.apiKey ?? definition.defaults?.apiKey)) {
|
|
229
|
+
throw new Error(`Provider profile "${profileName}" is missing apiKey`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function buildProviderSetupPatch(input, options = {}) {
|
|
233
|
+
const profile = buildProviderProfile(input, options);
|
|
234
|
+
validateProviderProfile(input.profile, profile, options);
|
|
235
|
+
return {
|
|
236
|
+
...input.setCurrent && { currentProvider: input.profile },
|
|
237
|
+
providers: {
|
|
238
|
+
[input.profile]: profile
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function buildProviderProfile(input, options = {}) {
|
|
243
|
+
const defaults = getProviderDefaults(input.type, options.providerDefinitions ?? []);
|
|
244
|
+
const apiKey = input.apiKeyEnv !== void 0 ? formatEnvReference(input.apiKeyEnv) : input.apiKey ?? defaults.apiKey;
|
|
245
|
+
const baseURL = input.baseURL ?? defaults.baseURL;
|
|
246
|
+
return {
|
|
247
|
+
type: input.type,
|
|
248
|
+
model: input.model ?? defaults.model,
|
|
249
|
+
...apiKey !== void 0 && { apiKey },
|
|
250
|
+
...baseURL !== void 0 && { baseURL },
|
|
251
|
+
...input.timeout !== void 0 && { timeout: input.timeout }
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function mergeProviderPatch(settings, patch) {
|
|
255
|
+
const [profileName, profile] = Object.entries(patch.providers)[0] ?? [];
|
|
256
|
+
if (!profileName || !profile) {
|
|
257
|
+
return settings;
|
|
258
|
+
}
|
|
259
|
+
const withProfile = upsertProviderProfile(settings, profileName, profile);
|
|
260
|
+
return patch.currentProvider ? setCurrentProvider(withProfile, patch.currentProvider) : withProfile;
|
|
261
|
+
}
|
|
262
|
+
function getProviderDefaults(type, providerDefinitions) {
|
|
263
|
+
return findProviderDefinition(providerDefinitions, type)?.defaults ?? {};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/command-api/provider/provider-command-probe.ts
|
|
267
|
+
async function testProviderProfileCommand(currentProvider, providers, profileArg, options) {
|
|
268
|
+
const profileName = profileArg ?? currentProvider;
|
|
269
|
+
if (!profileName) {
|
|
270
|
+
return { message: "No provider profile selected.", success: false };
|
|
271
|
+
}
|
|
272
|
+
const profile = providers?.[profileName];
|
|
273
|
+
if (!profile) {
|
|
274
|
+
return { message: `Provider profile "${profileName}" was not found.`, success: false };
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
validateProviderProfile(profileName, profile, {
|
|
278
|
+
providerDefinitions: options.providerDefinitions
|
|
279
|
+
});
|
|
280
|
+
} catch (error) {
|
|
281
|
+
return { message: error instanceof Error ? error.message : String(error), success: false };
|
|
282
|
+
}
|
|
283
|
+
const definition = profile.type ? findProviderDefinition2(options.providerDefinitions, profile.type) : void 0;
|
|
284
|
+
const probe = definition?.probeProfile ?? probeProviderProfile;
|
|
285
|
+
const result = await probe(profile);
|
|
286
|
+
return {
|
|
287
|
+
message: result.ok ? `Provider "${profileName}" test passed: ${result.message}` : `Provider "${profileName}" test failed: ${result.message}; manual configuration can continue.`,
|
|
288
|
+
success: true,
|
|
289
|
+
data: { providerTest: { profile: profileName } }
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
async function probeProviderProfile(profile) {
|
|
293
|
+
void profile;
|
|
294
|
+
return { ok: true, message: "Profile fields are valid; no endpoint probe configured." };
|
|
295
|
+
}
|
|
206
296
|
|
|
207
297
|
// src/commands/skill-source.ts
|
|
208
298
|
import { readdirSync, readFileSync, existsSync } from "fs";
|
|
@@ -357,66 +447,523 @@ var PluginCommandSource = class {
|
|
|
357
447
|
}
|
|
358
448
|
};
|
|
359
449
|
|
|
360
|
-
// src/
|
|
450
|
+
// src/command-api/context/context-command-api.ts
|
|
451
|
+
import { AUTO_COMPACT_THRESHOLD } from "@robota-sdk/agent-sessions";
|
|
452
|
+
var DEFAULT_AUTO_COMPACT_THRESHOLD = AUTO_COMPACT_THRESHOLD;
|
|
453
|
+
var AUTO_COMPACT_THRESHOLD_SETTINGS_KEY = "autoCompactThreshold";
|
|
454
|
+
function readCommandContextState(context) {
|
|
455
|
+
return context.getContextState();
|
|
456
|
+
}
|
|
457
|
+
function readAutoCompactThreshold(context) {
|
|
458
|
+
return context.getAutoCompactThreshold();
|
|
459
|
+
}
|
|
460
|
+
function readAutoCompactThresholdSource(context) {
|
|
461
|
+
return context.getAutoCompactThresholdSource?.() ?? "session";
|
|
462
|
+
}
|
|
463
|
+
function setCommandAutoCompactThreshold(context, threshold, source) {
|
|
464
|
+
if (context.setAutoCompactThreshold) {
|
|
465
|
+
context.setAutoCompactThreshold(threshold, source);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const session = context.getSession();
|
|
469
|
+
if (!session.setAutoCompactThreshold) {
|
|
470
|
+
throw new Error("Command host does not support changing auto-compact threshold.");
|
|
471
|
+
}
|
|
472
|
+
session.setAutoCompactThreshold(threshold);
|
|
473
|
+
}
|
|
474
|
+
function writeAutoCompactThresholdSetting(context, threshold) {
|
|
475
|
+
const settings = getSettingsAdapter(context);
|
|
476
|
+
if (!settings) return false;
|
|
477
|
+
settings.write({
|
|
478
|
+
...settings.read(),
|
|
479
|
+
[AUTO_COMPACT_THRESHOLD_SETTINGS_KEY]: threshold
|
|
480
|
+
});
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
function resetAutoCompactThresholdSetting(context) {
|
|
484
|
+
const settings = getSettingsAdapter(context);
|
|
485
|
+
if (!settings) return false;
|
|
486
|
+
const next = { ...settings.read() };
|
|
487
|
+
delete next[AUTO_COMPACT_THRESHOLD_SETTINGS_KEY];
|
|
488
|
+
settings.write(next);
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
async function compactCommandContext(context, instructions) {
|
|
492
|
+
const before = readCommandContextState(context);
|
|
493
|
+
await context.compactContext(instructions);
|
|
494
|
+
const after = readCommandContextState(context);
|
|
495
|
+
return { before, after };
|
|
496
|
+
}
|
|
497
|
+
function getSettingsAdapter(context) {
|
|
498
|
+
return context.getCommandHostAdapters?.().settings;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/command-api/provider/provider-setup-flow.ts
|
|
502
|
+
import { findProviderDefinition as findProviderDefinition3, formatSupportedProviderTypes } from "@robota-sdk/agent-core";
|
|
503
|
+
function createProviderSetupFlow(type, providerDefinitions) {
|
|
504
|
+
return {
|
|
505
|
+
type,
|
|
506
|
+
steps: getProviderSetupSteps(type, providerDefinitions),
|
|
507
|
+
stepIndex: 0,
|
|
508
|
+
values: {}
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
function formatProviderSetupSelectionPrompt(providerDefinitions) {
|
|
512
|
+
if (providerDefinitions.length === 0) {
|
|
513
|
+
return " No providers are available.";
|
|
514
|
+
}
|
|
515
|
+
const lines = [
|
|
516
|
+
" Select provider:",
|
|
517
|
+
...providerDefinitions.map(
|
|
518
|
+
(definition, index) => ` ${index + 1}. ${formatProviderSetupChoiceLabel(definition)}`
|
|
519
|
+
),
|
|
520
|
+
` Provider [1-${providerDefinitions.length}] (default: 1): `
|
|
521
|
+
];
|
|
522
|
+
return lines.join("\n");
|
|
523
|
+
}
|
|
524
|
+
function resolveProviderSetupSelection(rawValue, providerDefinitions) {
|
|
525
|
+
const value = rawValue.trim();
|
|
526
|
+
const selectedValue = value.length > 0 ? value : "1";
|
|
527
|
+
const index = parseProviderSelectionIndex(selectedValue);
|
|
528
|
+
if (index !== void 0) {
|
|
529
|
+
const definition2 = providerDefinitions[index];
|
|
530
|
+
if (definition2 !== void 0) {
|
|
531
|
+
return definition2.type;
|
|
532
|
+
}
|
|
533
|
+
throw new Error(
|
|
534
|
+
`Provider selection ${selectedValue} is out of range. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
const definition = findProviderDefinition3(providerDefinitions, selectedValue);
|
|
538
|
+
if (definition === void 0) {
|
|
539
|
+
throw new Error(
|
|
540
|
+
`Unknown provider: ${selectedValue}. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
return definition.type;
|
|
544
|
+
}
|
|
545
|
+
function getProviderSetupStep(state) {
|
|
546
|
+
const step = state.steps[state.stepIndex];
|
|
547
|
+
if (step === void 0) {
|
|
548
|
+
throw new Error(`Provider setup step ${state.stepIndex} is out of range`);
|
|
549
|
+
}
|
|
550
|
+
return step;
|
|
551
|
+
}
|
|
552
|
+
function submitProviderSetupValue(state, rawValue) {
|
|
553
|
+
const step = getProviderSetupStep(state);
|
|
554
|
+
const value = rawValue.trim() || step.defaultValue || "";
|
|
555
|
+
const validationMessage = validateProviderSetupValue(step, value);
|
|
556
|
+
if (validationMessage !== void 0) {
|
|
557
|
+
return { status: "error", state, message: validationMessage };
|
|
558
|
+
}
|
|
559
|
+
const nextState = {
|
|
560
|
+
...state,
|
|
561
|
+
stepIndex: state.stepIndex + 1,
|
|
562
|
+
values: { ...state.values, [step.key]: value }
|
|
563
|
+
};
|
|
564
|
+
if (nextState.stepIndex < state.steps.length) {
|
|
565
|
+
return { status: "next", state: nextState };
|
|
566
|
+
}
|
|
567
|
+
return { status: "complete", input: buildProviderSetupInput(nextState) };
|
|
568
|
+
}
|
|
569
|
+
async function runProviderSetupPromptFlow(type, promptInput, providerDefinitions) {
|
|
570
|
+
let state = createProviderSetupFlow(type, providerDefinitions);
|
|
571
|
+
const stepCount = state.steps.length;
|
|
572
|
+
while (state.stepIndex < stepCount) {
|
|
573
|
+
const step = getProviderSetupStep(state);
|
|
574
|
+
const value = await promptInput(formatProviderSetupPromptLabel(step), step.masked === true);
|
|
575
|
+
const result = submitProviderSetupValue(state, value);
|
|
576
|
+
if (result.status === "complete") {
|
|
577
|
+
return result.input;
|
|
578
|
+
}
|
|
579
|
+
if (result.status === "error") {
|
|
580
|
+
throw new Error(result.message);
|
|
581
|
+
}
|
|
582
|
+
state = result.state;
|
|
583
|
+
}
|
|
584
|
+
throw new Error("Provider setup flow ended without completion");
|
|
585
|
+
}
|
|
586
|
+
function formatProviderSetupPromptLabel(step) {
|
|
587
|
+
const suffix = step.defaultValue !== void 0 ? ` (default: ${step.defaultValue})` : "";
|
|
588
|
+
return ` ${step.title}${suffix}: `;
|
|
589
|
+
}
|
|
590
|
+
function formatProviderSetupChoiceLabel(definition) {
|
|
591
|
+
const label = definition.displayName !== void 0 ? `${definition.displayName} (${definition.type})` : definition.type;
|
|
592
|
+
return definition.description !== void 0 ? `${label} - ${definition.description}` : label;
|
|
593
|
+
}
|
|
594
|
+
function parseProviderSelectionIndex(value) {
|
|
595
|
+
if (!/^\d+$/.test(value)) {
|
|
596
|
+
return void 0;
|
|
597
|
+
}
|
|
598
|
+
return Number(value) - 1;
|
|
599
|
+
}
|
|
600
|
+
function validateProviderSetupValue(step, value) {
|
|
601
|
+
if (step.required === true && value.length === 0) {
|
|
602
|
+
return "Required";
|
|
603
|
+
}
|
|
604
|
+
return void 0;
|
|
605
|
+
}
|
|
606
|
+
function getProviderSetupSteps(type, providerDefinitions) {
|
|
607
|
+
const definition = findProviderDefinition3(providerDefinitions, type);
|
|
608
|
+
if (definition === void 0) {
|
|
609
|
+
throw new Error(
|
|
610
|
+
`Unknown provider: ${type}. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
if (definition.setupSteps !== void 0) {
|
|
614
|
+
return [...definition.setupSteps];
|
|
615
|
+
}
|
|
616
|
+
const steps = [
|
|
617
|
+
{
|
|
618
|
+
key: "model",
|
|
619
|
+
title: `${definition.type} model`,
|
|
620
|
+
defaultValue: definition.defaults?.model,
|
|
621
|
+
required: definition.defaults?.model === void 0
|
|
622
|
+
}
|
|
623
|
+
];
|
|
624
|
+
if (definition.defaults?.baseURL !== void 0) {
|
|
625
|
+
steps.unshift({
|
|
626
|
+
key: "baseURL",
|
|
627
|
+
title: `${definition.type} base URL`,
|
|
628
|
+
defaultValue: definition.defaults.baseURL
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
if (definition.requiresApiKey === true) {
|
|
632
|
+
steps.push({
|
|
633
|
+
key: "apiKey",
|
|
634
|
+
title: `${definition.type} API key`,
|
|
635
|
+
defaultValue: definition.defaults?.apiKey,
|
|
636
|
+
required: definition.defaults?.apiKey === void 0,
|
|
637
|
+
masked: true
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
return steps;
|
|
641
|
+
}
|
|
642
|
+
function buildProviderSetupInput(state) {
|
|
643
|
+
return {
|
|
644
|
+
profile: state.type,
|
|
645
|
+
type: state.type,
|
|
646
|
+
model: state.values.model,
|
|
647
|
+
apiKey: state.values.apiKey,
|
|
648
|
+
...state.values.baseURL !== void 0 && { baseURL: state.values.baseURL },
|
|
649
|
+
setCurrent: true
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// src/command-api/help/help-command-api.ts
|
|
654
|
+
var HELP_COMMAND_DESCRIPTION = "Show available commands";
|
|
655
|
+
var HELP_COMMAND_NAME_COLUMN_WIDTH = 16;
|
|
656
|
+
function readCommandList(context) {
|
|
657
|
+
return context.listCommands?.() ?? [];
|
|
658
|
+
}
|
|
659
|
+
function formatCommandHelpMessage(context) {
|
|
660
|
+
const commands = readCommandList(context);
|
|
661
|
+
return [
|
|
662
|
+
"Available commands:",
|
|
663
|
+
...commands.map(
|
|
664
|
+
(command) => ` ${command.name.padEnd(HELP_COMMAND_NAME_COLUMN_WIDTH)} \u2014 ${command.description}`
|
|
665
|
+
)
|
|
666
|
+
].join("\n");
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/command-api/background/background-command-api.ts
|
|
361
670
|
var DECIMAL_RADIX = 10;
|
|
362
|
-
|
|
363
|
-
|
|
671
|
+
var INLINE_METADATA_LIMIT = 160;
|
|
672
|
+
var BACKGROUND_COMMAND_DESCRIPTION = "List and control background tasks";
|
|
673
|
+
var BACKGROUND_COMMAND_USAGE = "Usage: background list | background read <task-id> [offset] | background cancel <task-id> | background close <task-id>";
|
|
674
|
+
function buildBackgroundCommandSubcommands() {
|
|
675
|
+
return [
|
|
676
|
+
{ name: "list", description: "List background tasks", source: "background" },
|
|
677
|
+
{ name: "read", description: "Read a background task log page", source: "background" },
|
|
678
|
+
{ name: "cancel", description: "Cancel a running background task", source: "background" },
|
|
679
|
+
{ name: "close", description: "Dismiss a terminal background task", source: "background" }
|
|
680
|
+
];
|
|
364
681
|
}
|
|
365
|
-
function
|
|
682
|
+
function formatCommandBackgroundTask(task) {
|
|
366
683
|
const preview = task.promptPreview ?? task.commandPreview ?? "";
|
|
367
684
|
const unread = task.unread ? " unread" : "";
|
|
368
685
|
const action = task.currentAction ? ` (${task.currentAction})` : "";
|
|
369
686
|
const timeout = task.timeoutReason ? ` timeout=${task.timeoutReason}` : "";
|
|
370
687
|
const activity = task.lastActivityAt ? ` lastActivityAt=${task.lastActivityAt}` : "";
|
|
688
|
+
const worktree = formatWorktreeMetadata(task);
|
|
371
689
|
const suffix = preview ? ` \u2014 ${preview}` : "";
|
|
372
|
-
return `${task.id} [${task.status}${unread}${timeout}${activity}] ${task.kind}:${task.label}${action}${suffix}`;
|
|
690
|
+
return `${task.id} [${task.status}${unread}${timeout}${activity}${worktree}] ${task.kind}:${task.label}${action}${suffix}`;
|
|
373
691
|
}
|
|
374
|
-
function
|
|
692
|
+
function formatCommandBackgroundTaskList(tasks) {
|
|
375
693
|
if (tasks.length === 0) return "No background tasks.";
|
|
376
|
-
return [
|
|
377
|
-
"
|
|
378
|
-
)
|
|
694
|
+
return [
|
|
695
|
+
"Background tasks:",
|
|
696
|
+
...tasks.map((task) => ` ${formatCommandBackgroundTask(task)}`)
|
|
697
|
+
].join("\n");
|
|
379
698
|
}
|
|
380
|
-
function
|
|
699
|
+
function parseCommandBackgroundLogCursor(value) {
|
|
381
700
|
if (!value) return void 0;
|
|
382
701
|
const offset = Number.parseInt(value, DECIMAL_RADIX);
|
|
383
702
|
return Number.isNaN(offset) ? void 0 : { offset };
|
|
384
703
|
}
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
if (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
success: true,
|
|
392
|
-
data: { count: tasks.length }
|
|
393
|
-
};
|
|
704
|
+
function formatWorktreeMetadata(task) {
|
|
705
|
+
const segments = [];
|
|
706
|
+
if (task.worktreePath) segments.push(`worktree=${task.worktreePath}`);
|
|
707
|
+
if (task.branchName) segments.push(`branch=${task.branchName}`);
|
|
708
|
+
if (task.worktreeStatus) {
|
|
709
|
+
segments.push(`worktreeStatus="${formatInlineMetadata(task.worktreeStatus)}"`);
|
|
394
710
|
}
|
|
395
|
-
if (
|
|
396
|
-
|
|
397
|
-
message: "Usage: background list | background read <task-id> [offset] | background cancel <task-id> | background close <task-id>",
|
|
398
|
-
success: false
|
|
399
|
-
};
|
|
711
|
+
if (task.worktreeNextAction) {
|
|
712
|
+
segments.push(`next="${formatInlineMetadata(task.worktreeNextAction)}"`);
|
|
400
713
|
}
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
714
|
+
if (segments.length === 0) return "";
|
|
715
|
+
return ` ${segments.join(" ")}`;
|
|
716
|
+
}
|
|
717
|
+
function formatInlineMetadata(value) {
|
|
718
|
+
const normalized = value.trim().replace(/\s+/g, " ");
|
|
719
|
+
return normalized.length > INLINE_METADATA_LIMIT ? `${normalized.slice(0, INLINE_METADATA_LIMIT)}...` : normalized;
|
|
720
|
+
}
|
|
721
|
+
function listCommandBackgroundTasks(context, filter) {
|
|
722
|
+
return context.listBackgroundTasks(filter);
|
|
723
|
+
}
|
|
724
|
+
function readCommandBackgroundTaskLog(context, taskId, cursor) {
|
|
725
|
+
return context.readBackgroundTaskLog(taskId, cursor);
|
|
726
|
+
}
|
|
727
|
+
function cancelCommandBackgroundTask(context, taskId, reason) {
|
|
728
|
+
return context.cancelBackgroundTask(taskId, reason);
|
|
729
|
+
}
|
|
730
|
+
function closeCommandBackgroundTask(context, taskId) {
|
|
731
|
+
return context.closeBackgroundTask(taskId);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/command-api/model/model-command-api.ts
|
|
735
|
+
import { CLAUDE_MODELS, formatTokenCount } from "@robota-sdk/agent-core";
|
|
736
|
+
var MODEL_COMMAND_DESCRIPTION = "Change AI model";
|
|
737
|
+
var MODEL_COMMAND_ARGUMENT_HINT = "<model-id>";
|
|
738
|
+
function buildModelCommandSubcommands(source = "model") {
|
|
739
|
+
const seen = /* @__PURE__ */ new Set();
|
|
740
|
+
const commands = [];
|
|
741
|
+
for (const model of Object.values(CLAUDE_MODELS)) {
|
|
742
|
+
if (seen.has(model.name)) continue;
|
|
743
|
+
seen.add(model.name);
|
|
744
|
+
commands.push({
|
|
745
|
+
name: model.id,
|
|
746
|
+
description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
|
|
747
|
+
source
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
return commands;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// src/command-api/language/language-command-api.ts
|
|
754
|
+
var LANGUAGE_COMMAND_DESCRIPTION = "Set response language";
|
|
755
|
+
var LANGUAGE_COMMAND_ARGUMENT_HINT = "<code>";
|
|
756
|
+
var RECOMMENDED_RESPONSE_LANGUAGES = [
|
|
757
|
+
{ code: "ko", description: "Korean" },
|
|
758
|
+
{ code: "en", description: "English" },
|
|
759
|
+
{ code: "ja", description: "Japanese" },
|
|
760
|
+
{ code: "zh", description: "Chinese" }
|
|
761
|
+
];
|
|
762
|
+
function buildLanguageCommandSubcommands(source = "language") {
|
|
763
|
+
return RECOMMENDED_RESPONSE_LANGUAGES.map((language) => ({
|
|
764
|
+
name: language.code,
|
|
765
|
+
description: language.description,
|
|
766
|
+
source
|
|
767
|
+
}));
|
|
768
|
+
}
|
|
769
|
+
function parseLanguageArgument(args) {
|
|
770
|
+
const language = args.trim().split(/\s+/)[0];
|
|
771
|
+
return language !== void 0 && language.length > 0 ? language : void 0;
|
|
772
|
+
}
|
|
773
|
+
function formatLanguageUsageMessage(commandName = "language") {
|
|
774
|
+
return `Usage: ${commandName} <code> (e.g., ko, en, ja, zh)`;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// src/command-api/permissions/permission-mode-command-api.ts
|
|
778
|
+
var PERMISSION_MODE_COMMAND_DESCRIPTION = "Show/change permission mode";
|
|
779
|
+
var PERMISSION_MODE_ARGUMENT_HINT = "plan | default | acceptEdits | bypassPermissions";
|
|
780
|
+
var PERMISSIONS_COMMAND_DESCRIPTION = "Show permission rules";
|
|
781
|
+
var VALID_PERMISSION_MODES = [
|
|
782
|
+
"plan",
|
|
783
|
+
"default",
|
|
784
|
+
"acceptEdits",
|
|
785
|
+
"bypassPermissions"
|
|
786
|
+
];
|
|
787
|
+
function buildPermissionModeSubcommands(source = "mode") {
|
|
788
|
+
return [
|
|
789
|
+
{ name: "plan", description: "Plan only, no execution", source },
|
|
790
|
+
{ name: "default", description: "Ask before risky actions", source },
|
|
791
|
+
{ name: "acceptEdits", description: "Auto-approve file edits", source },
|
|
792
|
+
{ name: "bypassPermissions", description: "Skip all permission checks", source }
|
|
793
|
+
];
|
|
794
|
+
}
|
|
795
|
+
function parsePermissionModeArgument(args) {
|
|
796
|
+
const mode = args.trim().split(/\s+/)[0];
|
|
797
|
+
return mode !== void 0 && mode.length > 0 ? mode : void 0;
|
|
798
|
+
}
|
|
799
|
+
function isPermissionMode(value) {
|
|
800
|
+
return VALID_PERMISSION_MODES.includes(value);
|
|
801
|
+
}
|
|
802
|
+
function formatInvalidPermissionModeMessage() {
|
|
803
|
+
return `Invalid mode. Valid: ${VALID_PERMISSION_MODES.join(" | ")}`;
|
|
804
|
+
}
|
|
805
|
+
function resolvePermissionModeAdapter(context) {
|
|
806
|
+
const adapter = context.getCommandHostAdapters?.().permissionMode;
|
|
807
|
+
if (adapter !== void 0) {
|
|
808
|
+
return adapter;
|
|
809
|
+
}
|
|
810
|
+
const runtime = context.getSession();
|
|
811
|
+
return {
|
|
812
|
+
getPermissionMode: () => runtime.getPermissionMode(),
|
|
813
|
+
setPermissionMode: (mode) => runtime.setPermissionMode(mode),
|
|
814
|
+
listSessionAllowedTools: () => runtime.getSessionAllowedTools()
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function readCommandPermissionMode(context) {
|
|
818
|
+
return resolvePermissionModeAdapter(context).getPermissionMode();
|
|
819
|
+
}
|
|
820
|
+
function writeCommandPermissionMode(context, mode) {
|
|
821
|
+
resolvePermissionModeAdapter(context).setPermissionMode(mode);
|
|
822
|
+
}
|
|
823
|
+
function listCommandSessionAllowedTools(context) {
|
|
824
|
+
return resolvePermissionModeAdapter(context).listSessionAllowedTools();
|
|
825
|
+
}
|
|
826
|
+
function readCommandPermissionsState(context) {
|
|
827
|
+
return {
|
|
828
|
+
mode: readCommandPermissionMode(context),
|
|
829
|
+
sessionAllowed: listCommandSessionAllowedTools(context)
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
function formatCommandPermissionsMessage(state) {
|
|
833
|
+
const lines = [`Permission mode: ${state.mode}`];
|
|
834
|
+
if (state.sessionAllowed.length > 0) {
|
|
835
|
+
lines.push(`Session-approved tools: ${state.sessionAllowed.join(", ")}`);
|
|
836
|
+
} else {
|
|
837
|
+
lines.push("No session-approved tools.");
|
|
410
838
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
839
|
+
return lines.join("\n");
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/command-api/statusline/statusline-command-api.ts
|
|
843
|
+
var STATUSLINE_COMMAND_DESCRIPTION = "Configure TUI status-line visibility and fields such as model, context, tokens, session, and git branch.";
|
|
844
|
+
var STATUSLINE_COMMAND_ARGUMENT_HINT = "on | off | reset | git on | git off";
|
|
845
|
+
var DEFAULT_STATUS_LINE_COMMAND_SETTINGS = {
|
|
846
|
+
enabled: true,
|
|
847
|
+
gitBranch: true
|
|
848
|
+
};
|
|
849
|
+
function buildStatusLineCommandSubcommands(source = "statusline") {
|
|
850
|
+
return [
|
|
851
|
+
{ name: "on", description: "Show the status line", source },
|
|
852
|
+
{ name: "off", description: "Hide the status line", source },
|
|
853
|
+
{ name: "reset", description: "Restore default status-line fields", source },
|
|
854
|
+
{ name: "git", description: "Show or hide git branch field", source }
|
|
855
|
+
];
|
|
856
|
+
}
|
|
857
|
+
function isStatusLineCommandSettingsPatch(value) {
|
|
858
|
+
return (value.enabled === void 0 || typeof value.enabled === "boolean") && (value.gitBranch === void 0 || typeof value.gitBranch === "boolean");
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// src/command-api/plugin/plugin-command-api.ts
|
|
862
|
+
var PLUGIN_COMMAND_DESCRIPTION = "Manage plugins";
|
|
863
|
+
var PLUGIN_COMMAND_ARGUMENT_HINT = "manage | install <name@marketplace> | uninstall <name@marketplace> | enable <name@marketplace> | disable <name@marketplace> | marketplace <action>";
|
|
864
|
+
var RELOAD_PLUGINS_COMMAND_DESCRIPTION = "Reload all plugin resources";
|
|
865
|
+
function createPluginTuiRequestedEffect() {
|
|
866
|
+
return { type: "plugin-tui-requested" };
|
|
867
|
+
}
|
|
868
|
+
function createPluginRegistryReloadRequestedEffect() {
|
|
869
|
+
return { type: "plugin-registry-reload-requested" };
|
|
870
|
+
}
|
|
871
|
+
function resolvePluginCommandAdapter(context) {
|
|
872
|
+
return context.getCommandHostAdapters?.().plugin;
|
|
873
|
+
}
|
|
874
|
+
function buildPluginCommandSubcommands() {
|
|
875
|
+
return [
|
|
876
|
+
{ name: "manage", description: "Open plugin manager", source: "plugin-manager" },
|
|
877
|
+
{ name: "install", description: "Install a plugin", source: "plugin-manager" },
|
|
878
|
+
{ name: "uninstall", description: "Uninstall a plugin", source: "plugin-manager" },
|
|
879
|
+
{ name: "enable", description: "Enable a plugin", source: "plugin-manager" },
|
|
880
|
+
{ name: "disable", description: "Disable a plugin", source: "plugin-manager" },
|
|
881
|
+
{
|
|
882
|
+
name: "marketplace",
|
|
883
|
+
description: "Manage plugin marketplaces",
|
|
884
|
+
source: "plugin-manager",
|
|
885
|
+
subcommands: [
|
|
886
|
+
{ name: "add", description: "Add marketplace source", source: "plugin-manager" },
|
|
887
|
+
{ name: "remove", description: "Remove marketplace source", source: "plugin-manager" },
|
|
888
|
+
{ name: "update", description: "Update marketplace source", source: "plugin-manager" },
|
|
889
|
+
{ name: "list", description: "List marketplace sources", source: "plugin-manager" }
|
|
890
|
+
]
|
|
891
|
+
}
|
|
892
|
+
];
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/command-api/session/session-command-api.ts
|
|
896
|
+
var CLEAR_COMMAND_DESCRIPTION = "Clear conversation history";
|
|
897
|
+
var RENAME_COMMAND_DESCRIPTION = "Rename the current session";
|
|
898
|
+
var RENAME_COMMAND_USAGE = "Usage: rename <name>";
|
|
899
|
+
var RESUME_COMMAND_DESCRIPTION = "Resume a previous session";
|
|
900
|
+
var COST_COMMAND_DESCRIPTION = "Show session info";
|
|
901
|
+
var EXIT_COMMAND_DESCRIPTION = "Exit CLI";
|
|
902
|
+
function clearConversationHistory(context) {
|
|
903
|
+
if (context.clearConversationHistory !== void 0) {
|
|
904
|
+
context.clearConversationHistory();
|
|
905
|
+
return;
|
|
414
906
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
907
|
+
context.getSession().clearHistory();
|
|
908
|
+
}
|
|
909
|
+
function parseSessionNameArgument(args) {
|
|
910
|
+
const name = args.trim();
|
|
911
|
+
return name.length > 0 ? name : void 0;
|
|
912
|
+
}
|
|
913
|
+
function createSessionRenamedEffect(name) {
|
|
914
|
+
return { type: "session-renamed", name };
|
|
915
|
+
}
|
|
916
|
+
function createSessionPickerRequestedEffect() {
|
|
917
|
+
return { type: "session-picker-requested" };
|
|
918
|
+
}
|
|
919
|
+
function createSessionExitRequestedEffect() {
|
|
920
|
+
return { type: "session-exit-requested" };
|
|
921
|
+
}
|
|
922
|
+
function readCommandSessionInfo(context) {
|
|
923
|
+
const session = context.getSession();
|
|
924
|
+
return {
|
|
925
|
+
sessionId: session.getSessionId(),
|
|
926
|
+
messageCount: session.getMessageCount()
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// src/command-api/checkpoint/rewind-command-api.ts
|
|
931
|
+
var REWIND_COMMAND_DESCRIPTION = "List, inspect, restore, or rollback edit checkpoints.";
|
|
932
|
+
var REWIND_COMMAND_ARGUMENT_HINT = "list | inspect CHECKPOINT_ID | restore CHECKPOINT_ID | code CHECKPOINT_ID | rollback CHECKPOINT_ID";
|
|
933
|
+
function buildRewindCommandSubcommands(source = "rewind") {
|
|
934
|
+
return [
|
|
935
|
+
{ name: "list", description: "List edit checkpoints", source },
|
|
936
|
+
{ name: "inspect", description: "Inspect captured files and restore plans", source },
|
|
937
|
+
{ name: "restore", description: "Restore code to a checkpoint", source },
|
|
938
|
+
{ name: "code", description: "Restore code to a checkpoint", source },
|
|
939
|
+
{ name: "rollback", description: "Rollback code through a checkpoint", source }
|
|
940
|
+
];
|
|
941
|
+
}
|
|
942
|
+
function listCommandEditCheckpoints(context) {
|
|
943
|
+
return context.listEditCheckpoints();
|
|
944
|
+
}
|
|
945
|
+
function inspectCommandEditCheckpoint(context, checkpointId) {
|
|
946
|
+
if (!context.inspectEditCheckpoint) {
|
|
947
|
+
throw new Error("Checkpoint inspection is not available in this command host.");
|
|
418
948
|
}
|
|
419
|
-
return
|
|
949
|
+
return context.inspectEditCheckpoint(checkpointId);
|
|
950
|
+
}
|
|
951
|
+
function restoreCommandEditCheckpoint(context, checkpointId) {
|
|
952
|
+
return context.restoreEditCheckpoint(checkpointId);
|
|
953
|
+
}
|
|
954
|
+
function rollbackCommandEditCheckpoint(context, checkpointId) {
|
|
955
|
+
return context.rollbackEditCheckpoint(checkpointId);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// src/memory/memory-policy-evaluator.ts
|
|
959
|
+
var SENSITIVE_PATTERNS = [
|
|
960
|
+
/\b(api[_-]?key|secret|token|password|private key)\b/i,
|
|
961
|
+
/\b\d{3}-\d{2}-\d{4}\b/,
|
|
962
|
+
/\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/,
|
|
963
|
+
/주민등록|비밀번호|시크릿|토큰/u
|
|
964
|
+
];
|
|
965
|
+
function containsSensitiveMemoryContent(text) {
|
|
966
|
+
return SENSITIVE_PATTERNS.some((pattern) => pattern.test(text));
|
|
420
967
|
}
|
|
421
968
|
|
|
422
969
|
// src/memory/pending-memory-store.ts
|
|
@@ -616,529 +1163,52 @@ var ProjectMemoryStore = class {
|
|
|
616
1163
|
}
|
|
617
1164
|
};
|
|
618
1165
|
|
|
619
|
-
// src/memory/memory-
|
|
620
|
-
var
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
function usage() {
|
|
636
|
-
return {
|
|
637
|
-
message: "Usage: memory list | memory show [topic] | memory add <user|feedback|project|reference> <topic> <text> | memory pending | memory approve <id> | memory reject <id> | memory used",
|
|
638
|
-
success: false
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
function formatList(store) {
|
|
642
|
-
const summary = store.list();
|
|
643
|
-
const topics = summary.topics.length > 0 ? summary.topics.map((topic) => `- ${topic.name}: ${topic.path}`).join("\n") : "(none)";
|
|
644
|
-
return {
|
|
645
|
-
message: [
|
|
646
|
-
`Memory index: ${summary.indexPath}`,
|
|
647
|
-
`Topics directory: ${summary.topicsPath}`,
|
|
648
|
-
"Topics:",
|
|
649
|
-
topics
|
|
650
|
-
].join("\n"),
|
|
651
|
-
success: true,
|
|
652
|
-
data: {
|
|
653
|
-
indexPath: summary.indexPath,
|
|
654
|
-
topicsPath: summary.topicsPath,
|
|
655
|
-
topicCount: summary.topics.length
|
|
1166
|
+
// src/command-api/memory/memory-command-api.ts
|
|
1167
|
+
var MEMORY_COMMAND_DESCRIPTION = "Project memory command. Use it to inspect project memory when stored context may help, save durable preferences, project conventions, feedback, or references worth reusing across sessions, review pending candidates, and report memory provenance. Do not store secrets, credentials, or transient facts.";
|
|
1168
|
+
var MEMORY_COMMAND_ARGUMENT_HINT = "list | show [topic] | add <user|feedback|project|reference> <topic> <text> | pending | approve <id> | reject <id> | used";
|
|
1169
|
+
var MEMORY_COMMAND_USAGE = "Usage: memory list | memory show [topic] | memory add <user|feedback|project|reference> <topic> <text> | memory pending | memory approve <id> | memory reject <id> | memory used";
|
|
1170
|
+
function buildMemoryCommandSubcommands(source = "memory") {
|
|
1171
|
+
return [
|
|
1172
|
+
{ name: "list", description: "List project memory topics", source },
|
|
1173
|
+
{ name: "show", description: "Show project memory index or a topic", source },
|
|
1174
|
+
{ name: "add", description: "Save durable project memory", source },
|
|
1175
|
+
{ name: "pending", description: "List pending memory candidates", source },
|
|
1176
|
+
{ name: "approve", description: "Approve a pending memory candidate", source },
|
|
1177
|
+
{ name: "reject", description: "Reject a pending memory candidate", source },
|
|
1178
|
+
{
|
|
1179
|
+
name: "used",
|
|
1180
|
+
description: "Show memory references used in the current turn",
|
|
1181
|
+
source
|
|
656
1182
|
}
|
|
657
|
-
|
|
658
|
-
}
|
|
659
|
-
function formatShow(store, topic) {
|
|
660
|
-
if (!topic || topic === "index") {
|
|
661
|
-
const memory = store.loadStartupMemory();
|
|
662
|
-
return {
|
|
663
|
-
message: memory.content || "(empty memory index)",
|
|
664
|
-
success: true,
|
|
665
|
-
data: {
|
|
666
|
-
path: memory.path,
|
|
667
|
-
lineCount: memory.lineCount,
|
|
668
|
-
truncated: memory.truncated
|
|
669
|
-
}
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
const content = store.readTopic(topic);
|
|
673
|
-
return {
|
|
674
|
-
message: content || `(empty memory topic: ${topic})`,
|
|
675
|
-
success: true,
|
|
676
|
-
data: { topic }
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function parseAdd(args) {
|
|
680
|
-
const type = args[TYPE_INDEX];
|
|
681
|
-
const topic = args[TOPIC_INDEX];
|
|
682
|
-
const text = args.slice(TEXT_START_INDEX).join(" ").trim();
|
|
683
|
-
if (!type || !isMemoryType(type) || !topic || text.length === 0) return void 0;
|
|
684
|
-
return { type, topic, text };
|
|
685
|
-
}
|
|
686
|
-
function formatPending(store) {
|
|
687
|
-
const records = store.list("pending");
|
|
688
|
-
const lines = records.length > 0 ? records.map(
|
|
689
|
-
(record) => `- ${record.id} ${record.type}/${record.topic} confidence=${record.confidence}: ${record.text}`
|
|
690
|
-
) : ["(no pending memory candidates)"];
|
|
691
|
-
return {
|
|
692
|
-
message: ["Pending memory candidates:", ...lines].join("\n"),
|
|
693
|
-
success: true,
|
|
694
|
-
data: { count: records.length }
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
function recordEvent(session, event) {
|
|
698
|
-
session.recordMemoryEvent({
|
|
699
|
-
...event,
|
|
700
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
function approvePending(session, pendingStore, memoryStore, id) {
|
|
704
|
-
if (!id) return usage();
|
|
705
|
-
try {
|
|
706
|
-
const approved = pendingStore.mark(id, "approved", "approved-by-user");
|
|
707
|
-
const saved = memoryStore.append(approved);
|
|
708
|
-
const record = pendingStore.mark(id, "saved", "approved-and-saved");
|
|
709
|
-
recordEvent(session, {
|
|
710
|
-
type: "memory_candidate_approved",
|
|
711
|
-
candidateId: record.id,
|
|
712
|
-
topic: record.topic,
|
|
713
|
-
reason: "approved-by-user"
|
|
714
|
-
});
|
|
715
|
-
recordEvent(session, {
|
|
716
|
-
type: "memory_candidate_saved",
|
|
717
|
-
candidateId: record.id,
|
|
718
|
-
topic: record.topic,
|
|
719
|
-
reason: saved.deduplicated ? "deduplicated" : "approved-and-saved"
|
|
720
|
-
});
|
|
721
|
-
return {
|
|
722
|
-
message: saved.deduplicated ? `Saved memory candidate ${id} was already present in ${saved.topicPath}` : `Saved memory candidate ${id} to ${saved.topicPath}`,
|
|
723
|
-
success: true,
|
|
724
|
-
data: {
|
|
725
|
-
id,
|
|
726
|
-
status: record.status,
|
|
727
|
-
topic: saved.topic,
|
|
728
|
-
topicPath: saved.topicPath,
|
|
729
|
-
deduplicated: saved.deduplicated
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
} catch (error) {
|
|
733
|
-
return {
|
|
734
|
-
message: error instanceof Error ? error.message : String(error),
|
|
735
|
-
success: false
|
|
736
|
-
};
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
function rejectPending(session, pendingStore, id) {
|
|
740
|
-
if (!id) return usage();
|
|
741
|
-
try {
|
|
742
|
-
const record = pendingStore.mark(id, "rejected", "rejected-by-user");
|
|
743
|
-
recordEvent(session, {
|
|
744
|
-
type: "memory_candidate_rejected",
|
|
745
|
-
candidateId: record.id,
|
|
746
|
-
topic: record.topic,
|
|
747
|
-
reason: "rejected-by-user"
|
|
748
|
-
});
|
|
749
|
-
return {
|
|
750
|
-
message: `Rejected memory candidate ${id}`,
|
|
751
|
-
success: true,
|
|
752
|
-
data: { id, status: record.status }
|
|
753
|
-
};
|
|
754
|
-
} catch (error) {
|
|
755
|
-
return {
|
|
756
|
-
message: error instanceof Error ? error.message : String(error),
|
|
757
|
-
success: false
|
|
758
|
-
};
|
|
759
|
-
}
|
|
1183
|
+
];
|
|
760
1184
|
}
|
|
761
|
-
function
|
|
762
|
-
|
|
763
|
-
const lines = references.length > 0 ? references.map((reference) => {
|
|
764
|
-
const suffix = reference.truncated ? " truncated=true" : "";
|
|
765
|
-
return `- ${reference.topic} score=${reference.score}${suffix}: ${reference.path}`;
|
|
766
|
-
}) : ["(no memory used in current turn)"];
|
|
767
|
-
return {
|
|
768
|
-
message: ["Used memory references:", ...lines].join("\n"),
|
|
769
|
-
success: true,
|
|
770
|
-
data: { count: references.length, references }
|
|
771
|
-
};
|
|
1185
|
+
function createCommandProjectMemoryStore(cwd, now) {
|
|
1186
|
+
return new ProjectMemoryStore(cwd, now);
|
|
772
1187
|
}
|
|
773
|
-
function
|
|
774
|
-
|
|
775
|
-
const subcommand = args[SUBCOMMAND_INDEX] ?? "list";
|
|
776
|
-
const store = new ProjectMemoryStore(session.getCwd());
|
|
777
|
-
const pendingStore = new PendingMemoryStore(session.getCwd());
|
|
778
|
-
if (subcommand === "list") return formatList(store);
|
|
779
|
-
if (subcommand === "show") return formatShow(store, args[TYPE_INDEX]);
|
|
780
|
-
if (subcommand === "pending") return formatPending(pendingStore);
|
|
781
|
-
if (subcommand === "approve")
|
|
782
|
-
return approvePending(session, pendingStore, store, args[TYPE_INDEX]);
|
|
783
|
-
if (subcommand === "reject") return rejectPending(session, pendingStore, args[TYPE_INDEX]);
|
|
784
|
-
if (subcommand === "used") return formatUsed(session);
|
|
785
|
-
if (subcommand === "add") {
|
|
786
|
-
const input = parseAdd(args);
|
|
787
|
-
if (!input) return usage();
|
|
788
|
-
if (containsSensitiveMemoryContent(input.text)) {
|
|
789
|
-
return {
|
|
790
|
-
message: "Refusing to save sensitive memory content.",
|
|
791
|
-
success: false
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
const result = store.append(input);
|
|
795
|
-
return {
|
|
796
|
-
message: result.deduplicated ? `${input.type} memory already exists in ${result.topicPath}` : `Saved ${input.type} memory to ${result.topicPath}`,
|
|
797
|
-
success: true,
|
|
798
|
-
data: {
|
|
799
|
-
indexPath: result.indexPath,
|
|
800
|
-
topicPath: result.topicPath,
|
|
801
|
-
topic: result.topic,
|
|
802
|
-
deduplicated: result.deduplicated
|
|
803
|
-
}
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
return usage();
|
|
1188
|
+
function createCommandPendingMemoryStore(cwd, now) {
|
|
1189
|
+
return new PendingMemoryStore(cwd, now);
|
|
807
1190
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
var SUBCOMMAND_INDEX2 = 0;
|
|
811
|
-
var CHECKPOINT_ID_INDEX = 1;
|
|
812
|
-
var PROMPT_PREVIEW_LENGTH = 120;
|
|
813
|
-
var ELLIPSIS_LENGTH = 3;
|
|
814
|
-
function usage2() {
|
|
1191
|
+
function createCommandMemoryStores(context, now) {
|
|
1192
|
+
const cwd = context.getCwd();
|
|
815
1193
|
return {
|
|
816
|
-
|
|
817
|
-
|
|
1194
|
+
project: createCommandProjectMemoryStore(cwd, now),
|
|
1195
|
+
pending: createCommandPendingMemoryStore(cwd, now)
|
|
818
1196
|
};
|
|
819
1197
|
}
|
|
820
|
-
function
|
|
821
|
-
|
|
822
|
-
if (compact.length <= PROMPT_PREVIEW_LENGTH) return compact;
|
|
823
|
-
return `${compact.slice(0, PROMPT_PREVIEW_LENGTH - ELLIPSIS_LENGTH)}...`;
|
|
824
|
-
}
|
|
825
|
-
function formatList2(checkpoints) {
|
|
826
|
-
const lines = checkpoints.length > 0 ? checkpoints.map(
|
|
827
|
-
(checkpoint) => `- ${checkpoint.id} files=${checkpoint.fileCount} ${checkpoint.createdAt} ${formatPrompt(
|
|
828
|
-
checkpoint.prompt
|
|
829
|
-
)}`
|
|
830
|
-
) : ["(no edit checkpoints)"];
|
|
831
|
-
return {
|
|
832
|
-
message: ["Edit checkpoints:", ...lines].join("\n"),
|
|
833
|
-
success: true,
|
|
834
|
-
data: { count: checkpoints.length, checkpoints: [...checkpoints] }
|
|
835
|
-
};
|
|
1198
|
+
function isCommandMemoryType(value) {
|
|
1199
|
+
return isMemoryType(value);
|
|
836
1200
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
try {
|
|
840
|
-
const result = await session.restoreEditCheckpoint(checkpointId);
|
|
841
|
-
return {
|
|
842
|
-
message: [
|
|
843
|
-
`Restored code to ${result.target.id}.`,
|
|
844
|
-
`Restored files: ${result.restoredFileCount}`,
|
|
845
|
-
`Rolled back checkpoints: ${result.restoredCheckpointCount}`
|
|
846
|
-
].join("\n"),
|
|
847
|
-
success: true,
|
|
848
|
-
data: {
|
|
849
|
-
target: result.target,
|
|
850
|
-
restoredCheckpointCount: result.restoredCheckpointCount,
|
|
851
|
-
restoredFileCount: result.restoredFileCount,
|
|
852
|
-
removedCheckpointCount: result.removedCheckpointCount
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
} catch (error) {
|
|
856
|
-
return {
|
|
857
|
-
message: error instanceof Error ? error.message : String(error),
|
|
858
|
-
success: false
|
|
859
|
-
};
|
|
860
|
-
}
|
|
1201
|
+
function hasSensitiveCommandMemoryContent(text) {
|
|
1202
|
+
return containsSensitiveMemoryContent(text);
|
|
861
1203
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
const subcommand = args[SUBCOMMAND_INDEX2] ?? "list";
|
|
865
|
-
if (subcommand === "list") {
|
|
866
|
-
return formatList2(session.listEditCheckpoints());
|
|
867
|
-
}
|
|
868
|
-
if (subcommand === "restore" || subcommand === "code") {
|
|
869
|
-
return restore(session, args[CHECKPOINT_ID_INDEX]);
|
|
870
|
-
}
|
|
871
|
-
return usage2();
|
|
1204
|
+
function listCommandUsedMemoryReferences(context) {
|
|
1205
|
+
return context.getUsedMemoryReferences();
|
|
872
1206
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
this.commands = /* @__PURE__ */ new Map();
|
|
879
|
-
for (const cmd of commands ?? createSystemCommands()) {
|
|
880
|
-
this.commands.set(cmd.name, cmd);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
/** Register an additional command. */
|
|
884
|
-
register(command) {
|
|
885
|
-
this.commands.set(command.name, command);
|
|
886
|
-
}
|
|
887
|
-
/** Execute a command by name. Returns null if command not found. */
|
|
888
|
-
async execute(name, session, args) {
|
|
889
|
-
const cmd = this.commands.get(name);
|
|
890
|
-
if (!cmd) return null;
|
|
891
|
-
return await cmd.execute(session, args);
|
|
892
|
-
}
|
|
893
|
-
/** List all registered commands. */
|
|
894
|
-
listCommands() {
|
|
895
|
-
return [...this.commands.values()];
|
|
896
|
-
}
|
|
897
|
-
listModelInvocableCommands() {
|
|
898
|
-
return this.listCommands().filter((command) => command.modelInvocable === true).map((command) => ({
|
|
899
|
-
name: `/${command.name}`,
|
|
900
|
-
kind: "builtin-command",
|
|
901
|
-
description: command.description,
|
|
902
|
-
userInvocable: command.userInvocable !== false,
|
|
903
|
-
modelInvocable: true,
|
|
904
|
-
...command.argumentHint ? { argumentHint: command.argumentHint } : {},
|
|
905
|
-
...command.safety ? { safety: command.safety } : {}
|
|
906
|
-
}));
|
|
907
|
-
}
|
|
908
|
-
isModelInvocable(name) {
|
|
909
|
-
return this.commands.get(name)?.modelInvocable === true;
|
|
910
|
-
}
|
|
911
|
-
async executeModelInvocable(name, session, args) {
|
|
912
|
-
if (!this.isModelInvocable(name)) return null;
|
|
913
|
-
return this.execute(name, session, args);
|
|
914
|
-
}
|
|
915
|
-
/** Check if a command exists. */
|
|
916
|
-
hasCommand(name) {
|
|
917
|
-
return this.commands.has(name);
|
|
918
|
-
}
|
|
919
|
-
};
|
|
920
|
-
|
|
921
|
-
// src/commands/system-command.ts
|
|
922
|
-
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
923
|
-
var MEMORY_COMMAND_DESCRIPTION = "Project memory command. Use it to inspect project memory when stored context may help, save durable preferences, project conventions, feedback, or references worth reusing across sessions, review pending candidates, and report memory provenance. Do not store secrets, credentials, or transient facts.";
|
|
924
|
-
var MEMORY_COMMAND_ARGUMENT_HINT = "list | show [topic] | add <user|feedback|project|reference> <topic> <text> | pending | approve <id> | reject <id> | used";
|
|
925
|
-
function createSystemCommands() {
|
|
926
|
-
return [
|
|
927
|
-
{
|
|
928
|
-
name: "help",
|
|
929
|
-
description: "Show available commands",
|
|
930
|
-
execute: (_session, _args) => ({
|
|
931
|
-
message: [
|
|
932
|
-
"Available commands:",
|
|
933
|
-
" help \u2014 Show this help",
|
|
934
|
-
" clear \u2014 Clear conversation",
|
|
935
|
-
" compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
936
|
-
" mode [m] \u2014 Show/change permission mode",
|
|
937
|
-
" model <id> \u2014 Change AI model",
|
|
938
|
-
" language <code> \u2014 Set response language (ko, en, ja, zh)",
|
|
939
|
-
" cost \u2014 Show session info",
|
|
940
|
-
" context \u2014 Context window info",
|
|
941
|
-
" permissions \u2014 Permission rules",
|
|
942
|
-
" memory \u2014 Manage project memory and pending candidates",
|
|
943
|
-
" rewind \u2014 List or restore edit checkpoints",
|
|
944
|
-
" provider \u2014 Provider profile status and switching",
|
|
945
|
-
" resume \u2014 Resume a previous session",
|
|
946
|
-
" background \u2014 List/cancel/close background tasks",
|
|
947
|
-
" rename <name> \u2014 Rename the current session",
|
|
948
|
-
" reset \u2014 Delete settings and exit"
|
|
949
|
-
].join("\n"),
|
|
950
|
-
success: true
|
|
951
|
-
})
|
|
952
|
-
},
|
|
953
|
-
{
|
|
954
|
-
name: "clear",
|
|
955
|
-
description: "Clear conversation history",
|
|
956
|
-
execute: (session, _args) => {
|
|
957
|
-
const underlying = session.getSession();
|
|
958
|
-
underlying.clearHistory();
|
|
959
|
-
return { message: "Conversation cleared.", success: true };
|
|
960
|
-
}
|
|
961
|
-
},
|
|
962
|
-
{
|
|
963
|
-
name: "compact",
|
|
964
|
-
description: "Compress context window",
|
|
965
|
-
execute: async (session, args) => {
|
|
966
|
-
const underlying = session.getSession();
|
|
967
|
-
const instructions = args.trim() || void 0;
|
|
968
|
-
const before = underlying.getContextState().usedPercentage;
|
|
969
|
-
await underlying.compact(instructions);
|
|
970
|
-
const after = underlying.getContextState().usedPercentage;
|
|
971
|
-
return {
|
|
972
|
-
message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
|
|
973
|
-
success: true,
|
|
974
|
-
data: { before, after }
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
},
|
|
978
|
-
{
|
|
979
|
-
name: "mode",
|
|
980
|
-
description: "Show/change permission mode",
|
|
981
|
-
execute: (session, args) => {
|
|
982
|
-
const underlying = session.getSession();
|
|
983
|
-
const arg = args.trim().split(/\s+/)[0];
|
|
984
|
-
if (!arg) {
|
|
985
|
-
return {
|
|
986
|
-
message: `Current mode: ${underlying.getPermissionMode()}`,
|
|
987
|
-
success: true,
|
|
988
|
-
data: { mode: underlying.getPermissionMode() }
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
if (VALID_MODES.includes(arg)) {
|
|
992
|
-
underlying.setPermissionMode(arg);
|
|
993
|
-
return {
|
|
994
|
-
message: `Permission mode set to: ${arg}`,
|
|
995
|
-
success: true,
|
|
996
|
-
data: { mode: arg }
|
|
997
|
-
};
|
|
998
|
-
}
|
|
999
|
-
return {
|
|
1000
|
-
message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
|
|
1001
|
-
success: false
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
},
|
|
1005
|
-
{
|
|
1006
|
-
name: "model",
|
|
1007
|
-
description: "Change AI model",
|
|
1008
|
-
execute: (_session, args) => {
|
|
1009
|
-
const modelId = args.trim().split(/\s+/)[0];
|
|
1010
|
-
if (!modelId) {
|
|
1011
|
-
return { message: "Usage: model <model-id>", success: false };
|
|
1012
|
-
}
|
|
1013
|
-
return {
|
|
1014
|
-
message: `Model change requested: ${modelId}`,
|
|
1015
|
-
success: true,
|
|
1016
|
-
data: { modelId }
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
},
|
|
1020
|
-
{
|
|
1021
|
-
name: "language",
|
|
1022
|
-
description: "Set response language",
|
|
1023
|
-
execute: (_session, args) => {
|
|
1024
|
-
const lang = args.trim().split(/\s+/)[0];
|
|
1025
|
-
if (!lang) {
|
|
1026
|
-
return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
|
|
1027
|
-
}
|
|
1028
|
-
return {
|
|
1029
|
-
message: `Language set to "${lang}".`,
|
|
1030
|
-
success: true,
|
|
1031
|
-
data: { language: lang }
|
|
1032
|
-
};
|
|
1033
|
-
}
|
|
1034
|
-
},
|
|
1035
|
-
{
|
|
1036
|
-
name: "cost",
|
|
1037
|
-
description: "Show session info",
|
|
1038
|
-
execute: (session, _args) => {
|
|
1039
|
-
const underlying = session.getSession();
|
|
1040
|
-
const sessionId = underlying.getSessionId();
|
|
1041
|
-
const messageCount = underlying.getMessageCount();
|
|
1042
|
-
return {
|
|
1043
|
-
message: `Session: ${sessionId}
|
|
1044
|
-
Messages: ${messageCount}`,
|
|
1045
|
-
success: true,
|
|
1046
|
-
data: { sessionId, messageCount }
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
},
|
|
1050
|
-
{
|
|
1051
|
-
name: "context",
|
|
1052
|
-
description: "Context window info",
|
|
1053
|
-
execute: (session, _args) => {
|
|
1054
|
-
const ctx = session.getContextState();
|
|
1055
|
-
return {
|
|
1056
|
-
message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
|
|
1057
|
-
success: true,
|
|
1058
|
-
data: {
|
|
1059
|
-
usedTokens: ctx.usedTokens,
|
|
1060
|
-
maxTokens: ctx.maxTokens,
|
|
1061
|
-
percentage: ctx.usedPercentage
|
|
1062
|
-
}
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
},
|
|
1066
|
-
{
|
|
1067
|
-
name: "permissions",
|
|
1068
|
-
description: "Show permission rules",
|
|
1069
|
-
execute: (session, _args) => {
|
|
1070
|
-
const underlying = session.getSession();
|
|
1071
|
-
const mode = underlying.getPermissionMode();
|
|
1072
|
-
const sessionAllowed = underlying.getSessionAllowedTools();
|
|
1073
|
-
const lines = [`Permission mode: ${mode}`];
|
|
1074
|
-
if (sessionAllowed.length > 0) {
|
|
1075
|
-
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
1076
|
-
} else {
|
|
1077
|
-
lines.push("No session-approved tools.");
|
|
1078
|
-
}
|
|
1079
|
-
return {
|
|
1080
|
-
message: lines.join("\n"),
|
|
1081
|
-
success: true,
|
|
1082
|
-
data: { mode, sessionAllowed }
|
|
1083
|
-
};
|
|
1084
|
-
}
|
|
1085
|
-
},
|
|
1086
|
-
{
|
|
1087
|
-
name: "memory",
|
|
1088
|
-
description: MEMORY_COMMAND_DESCRIPTION,
|
|
1089
|
-
modelInvocable: true,
|
|
1090
|
-
argumentHint: MEMORY_COMMAND_ARGUMENT_HINT,
|
|
1091
|
-
safety: "write",
|
|
1092
|
-
execute: executeMemoryCommand
|
|
1093
|
-
},
|
|
1094
|
-
{
|
|
1095
|
-
name: "rewind",
|
|
1096
|
-
description: "List edit checkpoints or restore code to a previous checkpoint.",
|
|
1097
|
-
argumentHint: "list | restore CHECKPOINT_ID | code CHECKPOINT_ID",
|
|
1098
|
-
safety: "write",
|
|
1099
|
-
execute: executeRewindCommand
|
|
1100
|
-
},
|
|
1101
|
-
{
|
|
1102
|
-
name: "resume",
|
|
1103
|
-
description: "Resume a previous session",
|
|
1104
|
-
execute: (_session, _args) => ({
|
|
1105
|
-
message: "Opening session picker...",
|
|
1106
|
-
success: true,
|
|
1107
|
-
data: { triggerResumePicker: true }
|
|
1108
|
-
})
|
|
1109
|
-
},
|
|
1110
|
-
{
|
|
1111
|
-
name: "background",
|
|
1112
|
-
description: "List and control background tasks",
|
|
1113
|
-
execute: executeBackgroundCommand
|
|
1114
|
-
},
|
|
1115
|
-
{
|
|
1116
|
-
name: "rename",
|
|
1117
|
-
description: "Rename the current session",
|
|
1118
|
-
execute: (_session, args) => {
|
|
1119
|
-
const name = args.trim();
|
|
1120
|
-
if (!name) {
|
|
1121
|
-
return { message: "Usage: rename <name>", success: false };
|
|
1122
|
-
}
|
|
1123
|
-
return {
|
|
1124
|
-
message: `Session renamed to "${name}".`,
|
|
1125
|
-
success: true,
|
|
1126
|
-
data: { name }
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
},
|
|
1130
|
-
{
|
|
1131
|
-
name: "reset",
|
|
1132
|
-
description: "Delete settings",
|
|
1133
|
-
execute: (_session, _args) => {
|
|
1134
|
-
return {
|
|
1135
|
-
message: "Reset requested.",
|
|
1136
|
-
success: true,
|
|
1137
|
-
data: { resetRequested: true }
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
];
|
|
1207
|
+
function recordCommandMemoryEvent(context, event, now = () => /* @__PURE__ */ new Date()) {
|
|
1208
|
+
context.recordMemoryEvent({
|
|
1209
|
+
...event,
|
|
1210
|
+
at: now().toISOString()
|
|
1211
|
+
});
|
|
1142
1212
|
}
|
|
1143
1213
|
|
|
1144
1214
|
// src/utils/skill-prompt.ts
|
|
@@ -1470,15 +1540,149 @@ function createInProcessSubagentRunner(deps) {
|
|
|
1470
1540
|
};
|
|
1471
1541
|
}
|
|
1472
1542
|
|
|
1543
|
+
// src/tools/agent-tool-batch.ts
|
|
1544
|
+
function stringifyAgentBatchResult(input) {
|
|
1545
|
+
const successfulJobs = input.jobs.filter((job) => job.success);
|
|
1546
|
+
const agentIds = input.jobs.map((job) => job.agentId).filter((agentId) => typeof agentId === "string" && agentId.length > 0);
|
|
1547
|
+
const failedJobCount = input.jobs.filter((job) => !job.success).length;
|
|
1548
|
+
return JSON.stringify({
|
|
1549
|
+
success: input.jobs.every((job) => job.success),
|
|
1550
|
+
mode: "batch",
|
|
1551
|
+
output: successfulJobs.map((job) => job.output ?? "").filter(Boolean).join("\n\n"),
|
|
1552
|
+
groupId: input.groupId,
|
|
1553
|
+
requestedJobCount: input.requestedJobCount,
|
|
1554
|
+
startedJobCount: agentIds.length,
|
|
1555
|
+
failedJobCount,
|
|
1556
|
+
agentIds,
|
|
1557
|
+
jobs: input.jobs,
|
|
1558
|
+
provenance: {
|
|
1559
|
+
source: "agent-tool-batch",
|
|
1560
|
+
groupId: input.groupId,
|
|
1561
|
+
requestedJobCount: input.requestedJobCount,
|
|
1562
|
+
startedJobCount: agentIds.length,
|
|
1563
|
+
failedJobCount
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
function createBatchGroupId() {
|
|
1568
|
+
const idRadix = 36;
|
|
1569
|
+
const randomStartIndex = 2;
|
|
1570
|
+
const randomEndIndex = 10;
|
|
1571
|
+
return `agent_group_${Date.now()}_${Math.random().toString(idRadix).slice(randomStartIndex, randomEndIndex)}`;
|
|
1572
|
+
}
|
|
1573
|
+
function normalizeJobLabel(label, fallback) {
|
|
1574
|
+
const trimmed = label?.trim();
|
|
1575
|
+
return trimmed && trimmed.length > 0 ? trimmed : fallback;
|
|
1576
|
+
}
|
|
1577
|
+
function resolveBatchJob(job, index, input) {
|
|
1578
|
+
const agentType = job.subagent_type ?? "general-purpose";
|
|
1579
|
+
const agentDef = input.resolveAgentDefinition(agentType, input.deps.customAgentRegistry);
|
|
1580
|
+
const label = normalizeJobLabel(job.label, agentDef?.name ?? agentType);
|
|
1581
|
+
return { index, job, agentType, agentDef, label };
|
|
1582
|
+
}
|
|
1583
|
+
function isValidBatchJob(job) {
|
|
1584
|
+
return job.agentDef !== void 0;
|
|
1585
|
+
}
|
|
1586
|
+
function createUnknownAgentBatchResult(job, groupId) {
|
|
1587
|
+
return {
|
|
1588
|
+
index: job.index,
|
|
1589
|
+
success: false,
|
|
1590
|
+
groupId,
|
|
1591
|
+
label: job.label,
|
|
1592
|
+
subagent_type: job.agentType,
|
|
1593
|
+
prompt: job.job.prompt,
|
|
1594
|
+
error: `Unknown agent type: ${job.agentType}`
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
async function spawnBatchJob(job, input) {
|
|
1598
|
+
try {
|
|
1599
|
+
const state = await input.manager.spawn(
|
|
1600
|
+
input.createSpawnRequest(job.job, job.agentType, job.agentDef, input.deps, job.label)
|
|
1601
|
+
);
|
|
1602
|
+
return { ...job, agentId: state.id };
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1605
|
+
return { ...job, spawnError: message };
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
function createBatchSpawnErrorResult(job, groupId) {
|
|
1609
|
+
return {
|
|
1610
|
+
index: job.index,
|
|
1611
|
+
success: false,
|
|
1612
|
+
groupId,
|
|
1613
|
+
label: job.label,
|
|
1614
|
+
subagent_type: job.agentType,
|
|
1615
|
+
prompt: job.job.prompt,
|
|
1616
|
+
error: `Sub-agent error: ${job.spawnError ?? "missing agent id"}`
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
function createBatchSuccessResult(job, groupId, result) {
|
|
1620
|
+
return {
|
|
1621
|
+
index: job.index,
|
|
1622
|
+
success: true,
|
|
1623
|
+
groupId,
|
|
1624
|
+
label: job.label,
|
|
1625
|
+
agentId: result.jobId,
|
|
1626
|
+
subagent_type: job.agentType,
|
|
1627
|
+
prompt: job.job.prompt,
|
|
1628
|
+
output: result.output,
|
|
1629
|
+
metadata: result.metadata
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
function createBatchWaitErrorResult(job, groupId, message) {
|
|
1633
|
+
return {
|
|
1634
|
+
index: job.index,
|
|
1635
|
+
success: false,
|
|
1636
|
+
groupId,
|
|
1637
|
+
label: job.label,
|
|
1638
|
+
agentId: job.agentId,
|
|
1639
|
+
subagent_type: job.agentType,
|
|
1640
|
+
prompt: job.job.prompt,
|
|
1641
|
+
error: `Sub-agent error: ${message}`
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
async function waitBatchJob(job, groupId, manager) {
|
|
1645
|
+
if (job.agentId === void 0) {
|
|
1646
|
+
return createBatchSpawnErrorResult(job, groupId);
|
|
1647
|
+
}
|
|
1648
|
+
try {
|
|
1649
|
+
const result = await manager.wait(job.agentId);
|
|
1650
|
+
return createBatchSuccessResult({ ...job, agentId: job.agentId }, groupId, result);
|
|
1651
|
+
} catch (error) {
|
|
1652
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1653
|
+
return createBatchWaitErrorResult({ ...job, agentId: job.agentId }, groupId, message);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
async function runManagedAgentBatch(input) {
|
|
1657
|
+
const groupId = createBatchGroupId();
|
|
1658
|
+
const resolvedJobs = input.jobs.map((job, index) => resolveBatchJob(job, index, input));
|
|
1659
|
+
const invalidJobs = resolvedJobs.filter((job) => !isValidBatchJob(job)).map((job) => createUnknownAgentBatchResult(job, groupId));
|
|
1660
|
+
const startedJobs = await Promise.all(
|
|
1661
|
+
resolvedJobs.filter(isValidBatchJob).map((job) => spawnBatchJob(job, input))
|
|
1662
|
+
);
|
|
1663
|
+
const terminalJobs = await Promise.all(
|
|
1664
|
+
startedJobs.map((job) => waitBatchJob(job, groupId, input.manager))
|
|
1665
|
+
);
|
|
1666
|
+
const batchJobs = [...invalidJobs, ...terminalJobs].sort(
|
|
1667
|
+
(left, right) => left.index - right.index
|
|
1668
|
+
);
|
|
1669
|
+
return stringifyAgentBatchResult({
|
|
1670
|
+
groupId,
|
|
1671
|
+
requestedJobCount: input.jobs.length,
|
|
1672
|
+
jobs: batchJobs
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1473
1676
|
// src/tools/agent-tool.ts
|
|
1474
1677
|
var AGENT_TOOL_DESCRIPTION = [
|
|
1475
1678
|
"Creates delegated subagent jobs in isolated contexts.",
|
|
1476
1679
|
"Without jobs, one tool call creates one subagent job from prompt.",
|
|
1477
|
-
"For explicit multi-agent or parallel-agent requests, use one Agent tool call with jobs containing one entry per requested role.",
|
|
1680
|
+
"For explicit multi-agent or parallel-agent requests, use one Agent tool call with jobs containing one entry per requested role and a stable label for each role.",
|
|
1478
1681
|
"When the user explicitly asks to create, run, spawn, delegate to, or use agents/subagents, start the requested subagent job immediately.",
|
|
1479
1682
|
"Do not ask a follow-up question unless execution is impossible or unsafe.",
|
|
1480
1683
|
"Subagent jobs run as background tasks by default.",
|
|
1481
|
-
"The tool waits for a terminal result and returns completed, failed, or timed-out outcome data
|
|
1684
|
+
"The tool waits for a terminal result and returns completed, failed, or timed-out outcome data with structured requested/started job counts.",
|
|
1685
|
+
"After the tool returns, base user-facing claims on returned mode and counts; do not say parallel or multiple jobs started unless the result proves those jobs started.",
|
|
1482
1686
|
"Execution is represented by a real tool call and runtime background task event."
|
|
1483
1687
|
].join(" ");
|
|
1484
1688
|
function createAgentToolPromptDescription(agentDefinitions = []) {
|
|
@@ -1486,10 +1690,11 @@ function createAgentToolPromptDescription(agentDefinitions = []) {
|
|
|
1486
1690
|
return [
|
|
1487
1691
|
"Agent \u2014 creates isolated subagent jobs.",
|
|
1488
1692
|
"Without jobs, one Agent tool call corresponds to one subagent job.",
|
|
1489
|
-
"For explicit multi-agent or parallel-agent requests, use one Agent tool call with jobs containing one entry per requested role.",
|
|
1693
|
+
"For explicit multi-agent or parallel-agent requests, use one Agent tool call with jobs containing one entry per requested role and a stable label for each role.",
|
|
1490
1694
|
"When the user explicitly asks to create, run, spawn, delegate to, or use agents/subagents, start the requested subagent job immediately.",
|
|
1491
1695
|
"Do not ask a follow-up question unless execution is impossible or unsafe.",
|
|
1492
|
-
"The tool returns terminal result data.",
|
|
1696
|
+
"The tool returns terminal result data with mode, requestedJobCount, startedJobCount, failedJobCount, and provenance.",
|
|
1697
|
+
"After the tool returns, base user-facing claims on returned mode and counts; do not say parallel or multiple jobs started unless the result proves those jobs started.",
|
|
1493
1698
|
"Runtime mode is background.",
|
|
1494
1699
|
availableAgents
|
|
1495
1700
|
].join(" ").replace(/\s+/g, " ").trim();
|
|
@@ -1504,6 +1709,7 @@ var AgentSchema = z.object({
|
|
|
1504
1709
|
isolation: z.enum(["none", "worktree"]).optional().describe('Optional runtime isolation mode. "worktree" runs in a Git worktree.'),
|
|
1505
1710
|
jobs: z.array(
|
|
1506
1711
|
z.object({
|
|
1712
|
+
label: z.string().optional().describe("Stable role label for this batch job"),
|
|
1507
1713
|
prompt: z.string().describe("The task for this subagent to perform"),
|
|
1508
1714
|
subagent_type: z.string().optional().describe("Agent type for this job"),
|
|
1509
1715
|
model: z.string().optional().describe("Optional model override for this job"),
|
|
@@ -1532,10 +1738,10 @@ function createSubagentManager(deps) {
|
|
|
1532
1738
|
runner: createInProcessSubagentRunner(deps)
|
|
1533
1739
|
});
|
|
1534
1740
|
}
|
|
1535
|
-
function createSpawnRequest(args, agentType, agentDef, deps) {
|
|
1741
|
+
function createSpawnRequest(args, agentType, agentDef, deps, label = agentDef.name) {
|
|
1536
1742
|
return {
|
|
1537
1743
|
type: agentType,
|
|
1538
|
-
label
|
|
1744
|
+
label,
|
|
1539
1745
|
parentSessionId: deps.parentSessionId ?? "unknown-session",
|
|
1540
1746
|
mode: "background",
|
|
1541
1747
|
depth: deps.subagentDepth ?? 1,
|
|
@@ -1548,39 +1754,65 @@ function createSpawnRequest(args, agentType, agentDef, deps) {
|
|
|
1548
1754
|
function stringifyUnknownAgentType(agentType) {
|
|
1549
1755
|
return JSON.stringify({
|
|
1550
1756
|
success: false,
|
|
1757
|
+
mode: "single",
|
|
1758
|
+
requestedJobCount: 1,
|
|
1759
|
+
startedJobCount: 0,
|
|
1760
|
+
failedJobCount: 1,
|
|
1551
1761
|
output: "",
|
|
1552
|
-
error: `Unknown agent type: ${agentType}
|
|
1762
|
+
error: `Unknown agent type: ${agentType}`,
|
|
1763
|
+
provenance: {
|
|
1764
|
+
source: "agent-tool-single",
|
|
1765
|
+
requestedJobCount: 1,
|
|
1766
|
+
startedJobCount: 0,
|
|
1767
|
+
failedJobCount: 1
|
|
1768
|
+
}
|
|
1553
1769
|
});
|
|
1554
1770
|
}
|
|
1555
1771
|
function stringifyAgentSuccess(result) {
|
|
1556
1772
|
const worktreePath = result.metadata?.["worktreePath"];
|
|
1557
1773
|
const branchName = result.metadata?.["branchName"];
|
|
1774
|
+
const worktreeStatus = result.metadata?.["worktreeStatus"];
|
|
1775
|
+
const worktreeNextAction = result.metadata?.["worktreeNextAction"];
|
|
1558
1776
|
return JSON.stringify({
|
|
1559
1777
|
success: true,
|
|
1778
|
+
mode: "single",
|
|
1779
|
+
requestedJobCount: 1,
|
|
1780
|
+
startedJobCount: 1,
|
|
1781
|
+
failedJobCount: 0,
|
|
1560
1782
|
output: result.output,
|
|
1561
1783
|
agentId: result.jobId,
|
|
1784
|
+
agentIds: [result.jobId],
|
|
1785
|
+
provenance: {
|
|
1786
|
+
source: "agent-tool-single",
|
|
1787
|
+
requestedJobCount: 1,
|
|
1788
|
+
startedJobCount: 1,
|
|
1789
|
+
failedJobCount: 0
|
|
1790
|
+
},
|
|
1562
1791
|
metadata: result.metadata,
|
|
1563
1792
|
...typeof worktreePath === "string" ? { worktreePath } : {},
|
|
1564
|
-
...typeof branchName === "string" ? { branchName } : {}
|
|
1793
|
+
...typeof branchName === "string" ? { branchName } : {},
|
|
1794
|
+
...typeof worktreeStatus === "string" ? { worktreeStatus } : {},
|
|
1795
|
+
...typeof worktreeNextAction === "string" ? { worktreeNextAction } : {}
|
|
1565
1796
|
});
|
|
1566
1797
|
}
|
|
1567
1798
|
function stringifyAgentError(message, agentId) {
|
|
1799
|
+
const startedJobCount = agentId === void 0 ? 0 : 1;
|
|
1568
1800
|
return JSON.stringify({
|
|
1569
1801
|
success: false,
|
|
1802
|
+
mode: "single",
|
|
1803
|
+
requestedJobCount: 1,
|
|
1804
|
+
startedJobCount,
|
|
1805
|
+
failedJobCount: 1,
|
|
1570
1806
|
output: "",
|
|
1571
1807
|
error: `Sub-agent error: ${message}`,
|
|
1572
|
-
agentId
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
output: successfulJobs.map((job) => job.output ?? "").filter(Boolean).join("\n\n"),
|
|
1581
|
-
groupId: input.groupId,
|
|
1582
|
-
agentIds,
|
|
1583
|
-
jobs: input.jobs
|
|
1808
|
+
agentId,
|
|
1809
|
+
...agentId !== void 0 ? { agentIds: [agentId] } : {},
|
|
1810
|
+
provenance: {
|
|
1811
|
+
source: "agent-tool-single",
|
|
1812
|
+
requestedJobCount: 1,
|
|
1813
|
+
startedJobCount,
|
|
1814
|
+
failedJobCount: 1
|
|
1815
|
+
}
|
|
1584
1816
|
});
|
|
1585
1817
|
}
|
|
1586
1818
|
async function runManagedAgent(args, deps, manager) {
|
|
@@ -1604,75 +1836,6 @@ async function runManagedAgent(args, deps, manager) {
|
|
|
1604
1836
|
return stringifyAgentError(message, agentId);
|
|
1605
1837
|
}
|
|
1606
1838
|
}
|
|
1607
|
-
function createBatchGroupId() {
|
|
1608
|
-
return `agent_group_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1609
|
-
}
|
|
1610
|
-
async function runManagedAgentBatch(jobs, deps, manager) {
|
|
1611
|
-
const groupId = createBatchGroupId();
|
|
1612
|
-
const resolvedJobs = jobs.map((job, index) => {
|
|
1613
|
-
const agentType = job.subagent_type ?? "general-purpose";
|
|
1614
|
-
const agentDef = resolveAgentDefinition2(agentType, deps.customAgentRegistry);
|
|
1615
|
-
return { index, job, agentType, agentDef };
|
|
1616
|
-
});
|
|
1617
|
-
const invalidJobs = resolvedJobs.filter((job) => job.agentDef === void 0).map((job) => ({
|
|
1618
|
-
index: job.index,
|
|
1619
|
-
success: false,
|
|
1620
|
-
subagent_type: job.agentType,
|
|
1621
|
-
error: `Unknown agent type: ${job.agentType}`
|
|
1622
|
-
}));
|
|
1623
|
-
const validJobs = resolvedJobs.filter(
|
|
1624
|
-
(job) => job.agentDef !== void 0
|
|
1625
|
-
);
|
|
1626
|
-
const startedJobs = await Promise.all(
|
|
1627
|
-
validJobs.map(async (job) => {
|
|
1628
|
-
try {
|
|
1629
|
-
const state = await manager.spawn(
|
|
1630
|
-
createSpawnRequest(job.job, job.agentType, job.agentDef, deps)
|
|
1631
|
-
);
|
|
1632
|
-
return { ...job, agentId: state.id, spawnError: void 0 };
|
|
1633
|
-
} catch (error) {
|
|
1634
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1635
|
-
return { ...job, agentId: void 0, spawnError: message };
|
|
1636
|
-
}
|
|
1637
|
-
})
|
|
1638
|
-
);
|
|
1639
|
-
const terminalJobs = await Promise.all(
|
|
1640
|
-
startedJobs.map(async (job) => {
|
|
1641
|
-
if (job.spawnError || !job.agentId) {
|
|
1642
|
-
return {
|
|
1643
|
-
index: job.index,
|
|
1644
|
-
success: false,
|
|
1645
|
-
subagent_type: job.agentType,
|
|
1646
|
-
error: `Sub-agent error: ${job.spawnError ?? "missing agent id"}`
|
|
1647
|
-
};
|
|
1648
|
-
}
|
|
1649
|
-
try {
|
|
1650
|
-
const result = await manager.wait(job.agentId);
|
|
1651
|
-
return {
|
|
1652
|
-
index: job.index,
|
|
1653
|
-
success: true,
|
|
1654
|
-
agentId: result.jobId,
|
|
1655
|
-
subagent_type: job.agentType,
|
|
1656
|
-
output: result.output,
|
|
1657
|
-
metadata: result.metadata
|
|
1658
|
-
};
|
|
1659
|
-
} catch (error) {
|
|
1660
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1661
|
-
return {
|
|
1662
|
-
index: job.index,
|
|
1663
|
-
success: false,
|
|
1664
|
-
agentId: job.agentId,
|
|
1665
|
-
subagent_type: job.agentType,
|
|
1666
|
-
error: `Sub-agent error: ${message}`
|
|
1667
|
-
};
|
|
1668
|
-
}
|
|
1669
|
-
})
|
|
1670
|
-
);
|
|
1671
|
-
const batchJobs = [...invalidJobs, ...terminalJobs].sort(
|
|
1672
|
-
(left, right) => left.index - right.index
|
|
1673
|
-
);
|
|
1674
|
-
return stringifyAgentBatchResult({ groupId, jobs: batchJobs });
|
|
1675
|
-
}
|
|
1676
1839
|
function createAgentTool(deps) {
|
|
1677
1840
|
const manager = createSubagentManager(deps);
|
|
1678
1841
|
return createZodFunctionTool(
|
|
@@ -1682,7 +1845,13 @@ function createAgentTool(deps) {
|
|
|
1682
1845
|
async (params) => {
|
|
1683
1846
|
const args = params;
|
|
1684
1847
|
if (Array.isArray(args.jobs) && args.jobs.length > 0) {
|
|
1685
|
-
return runManagedAgentBatch(
|
|
1848
|
+
return runManagedAgentBatch({
|
|
1849
|
+
jobs: args.jobs,
|
|
1850
|
+
deps,
|
|
1851
|
+
manager,
|
|
1852
|
+
resolveAgentDefinition: resolveAgentDefinition2,
|
|
1853
|
+
createSpawnRequest
|
|
1854
|
+
});
|
|
1686
1855
|
}
|
|
1687
1856
|
return runManagedAgent(
|
|
1688
1857
|
{
|
|
@@ -1925,13 +2094,13 @@ function extractToolSummaries(history, historyBefore) {
|
|
|
1925
2094
|
}
|
|
1926
2095
|
function buildResult(response, sessionHistory, interactiveHistory, historyBefore, contextState) {
|
|
1927
2096
|
const toolSummaries = extractToolSummaries(sessionHistory, historyBefore);
|
|
1928
|
-
const
|
|
2097
|
+
const usage = extractTurnUsage(sessionHistory, historyBefore, contextState);
|
|
1929
2098
|
return {
|
|
1930
2099
|
response,
|
|
1931
2100
|
history: interactiveHistory,
|
|
1932
2101
|
toolSummaries,
|
|
1933
2102
|
contextState,
|
|
1934
|
-
...
|
|
2103
|
+
...usage && { usage }
|
|
1935
2104
|
};
|
|
1936
2105
|
}
|
|
1937
2106
|
function buildInterruptedResult(sessionHistory, interactiveHistory, historyBefore, contextState) {
|
|
@@ -1941,22 +2110,22 @@ function buildInterruptedResult(sessionHistory, interactiveHistory, historyBefor
|
|
|
1941
2110
|
const msg = sessionHistory[i];
|
|
1942
2111
|
if (msg?.role === "assistant" && msg.content) parts.push(msg.content);
|
|
1943
2112
|
}
|
|
1944
|
-
const
|
|
2113
|
+
const usage = extractTurnUsage(sessionHistory, historyBefore, contextState);
|
|
1945
2114
|
return {
|
|
1946
2115
|
response: parts.join("\n\n"),
|
|
1947
2116
|
history: interactiveHistory,
|
|
1948
2117
|
toolSummaries,
|
|
1949
2118
|
contextState,
|
|
1950
|
-
...
|
|
2119
|
+
...usage && { usage }
|
|
1951
2120
|
};
|
|
1952
2121
|
}
|
|
1953
|
-
function createUsageSummaryEntry(
|
|
2122
|
+
function createUsageSummaryEntry(usage) {
|
|
1954
2123
|
return {
|
|
1955
2124
|
id: `usage_${randomUUID()}`,
|
|
1956
2125
|
timestamp: /* @__PURE__ */ new Date(),
|
|
1957
2126
|
category: "event",
|
|
1958
2127
|
type: "usage-summary",
|
|
1959
|
-
data:
|
|
2128
|
+
data: usage
|
|
1960
2129
|
};
|
|
1961
2130
|
}
|
|
1962
2131
|
function extractTurnUsage(sessionHistory, historyBefore, contextState) {
|
|
@@ -1966,11 +2135,11 @@ function extractTurnUsage(sessionHistory, historyBefore, contextState) {
|
|
|
1966
2135
|
let foundUsage = false;
|
|
1967
2136
|
for (const message of turnMessages) {
|
|
1968
2137
|
if (message.role !== "assistant") continue;
|
|
1969
|
-
const
|
|
1970
|
-
if (!
|
|
2138
|
+
const usage = collectAssistantUsageMetadata(message);
|
|
2139
|
+
if (!usage) continue;
|
|
1971
2140
|
foundUsage = true;
|
|
1972
|
-
promptTokens +=
|
|
1973
|
-
completionTokens +=
|
|
2141
|
+
promptTokens += usage.inputTokens;
|
|
2142
|
+
completionTokens += usage.outputTokens;
|
|
1974
2143
|
}
|
|
1975
2144
|
if (!foundUsage) return void 0;
|
|
1976
2145
|
return {
|
|
@@ -2300,6 +2469,7 @@ var MarketplaceSourceSchema = z2.object({
|
|
|
2300
2469
|
})
|
|
2301
2470
|
});
|
|
2302
2471
|
var ExtraKnownMarketplacesSchema = z2.record(MarketplaceSourceSchema).optional().catch(void 0);
|
|
2472
|
+
var AutoCompactThresholdSchema = z2.union([z2.number().gt(0).lte(1), z2.literal(false)]).optional();
|
|
2303
2473
|
var SettingsSchema = z2.object({
|
|
2304
2474
|
/** Trust level used when no --permission-mode flag is given */
|
|
2305
2475
|
defaultTrustLevel: z2.enum(["safe", "moderate", "full"]).optional(),
|
|
@@ -2317,7 +2487,9 @@ var SettingsSchema = z2.object({
|
|
|
2317
2487
|
/** Plugin enablement map: plugin name -> enabled/disabled */
|
|
2318
2488
|
enabledPlugins: EnabledPluginsSchema,
|
|
2319
2489
|
/** Extra marketplace URLs for BundlePlugin discovery */
|
|
2320
|
-
extraKnownMarketplaces: ExtraKnownMarketplacesSchema
|
|
2490
|
+
extraKnownMarketplaces: ExtraKnownMarketplacesSchema,
|
|
2491
|
+
/** Auto-compact threshold as a 0-1 fraction. Set false to disable automatic compaction. */
|
|
2492
|
+
autoCompactThreshold: AutoCompactThresholdSchema
|
|
2321
2493
|
});
|
|
2322
2494
|
|
|
2323
2495
|
// src/config/config-loader.ts
|
|
@@ -2401,7 +2573,8 @@ function mergeSettings(layers) {
|
|
|
2401
2573
|
},
|
|
2402
2574
|
providers: merged.providers !== void 0 || layer.providers !== void 0 ? mergeProviders(merged.providers, layer.providers) : void 0,
|
|
2403
2575
|
enabledPlugins: merged.enabledPlugins !== void 0 || layer.enabledPlugins !== void 0 ? { ...merged.enabledPlugins ?? {}, ...layer.enabledPlugins ?? {} } : void 0,
|
|
2404
|
-
extraKnownMarketplaces: layer.extraKnownMarketplaces ?? merged.extraKnownMarketplaces
|
|
2576
|
+
extraKnownMarketplaces: layer.extraKnownMarketplaces ?? merged.extraKnownMarketplaces,
|
|
2577
|
+
autoCompactThreshold: layer.autoCompactThreshold ?? merged.autoCompactThreshold
|
|
2405
2578
|
};
|
|
2406
2579
|
}, {});
|
|
2407
2580
|
}
|
|
@@ -2455,7 +2628,8 @@ function toResolvedConfig(merged) {
|
|
|
2455
2628
|
env: merged.env ?? DEFAULTS.env,
|
|
2456
2629
|
hooks: merged.hooks ?? void 0,
|
|
2457
2630
|
enabledPlugins: merged.enabledPlugins ?? void 0,
|
|
2458
|
-
extraKnownMarketplaces: merged.extraKnownMarketplaces ?? void 0
|
|
2631
|
+
extraKnownMarketplaces: merged.extraKnownMarketplaces ?? void 0,
|
|
2632
|
+
autoCompactThreshold: merged.autoCompactThreshold
|
|
2459
2633
|
};
|
|
2460
2634
|
}
|
|
2461
2635
|
function getSettingsPaths(cwd) {
|
|
@@ -2794,7 +2968,8 @@ var DEFAULT_TOOL_DESCRIPTIONS = [
|
|
|
2794
2968
|
"Edit \u2014 replace a string in a file",
|
|
2795
2969
|
"Glob \u2014 find files matching a pattern",
|
|
2796
2970
|
"Grep \u2014 search file contents with regex",
|
|
2797
|
-
"
|
|
2971
|
+
"WebFetch \u2014 fetch URL content as text",
|
|
2972
|
+
"WebSearch \u2014 search the internet through the configured local tool"
|
|
2798
2973
|
];
|
|
2799
2974
|
function createDefaultTools() {
|
|
2800
2975
|
return [
|
|
@@ -2960,6 +3135,200 @@ function extractFilePath(parameters) {
|
|
|
2960
3135
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
2961
3136
|
}
|
|
2962
3137
|
|
|
3138
|
+
// src/reversible-execution/reversible-execution-policy.ts
|
|
3139
|
+
var FILE_MUTATION_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit"]);
|
|
3140
|
+
var HOST_SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash", "BackgroundProcess"]);
|
|
3141
|
+
var READ_ONLY_TOOLS = /* @__PURE__ */ new Set(["Read", "Glob", "Grep", "WebFetch", "WebSearch"]);
|
|
3142
|
+
function evaluateReversibleToolSafety(input) {
|
|
3143
|
+
const toolName = input.toolName;
|
|
3144
|
+
if (READ_ONLY_TOOLS.has(toolName)) {
|
|
3145
|
+
return {
|
|
3146
|
+
toolName,
|
|
3147
|
+
reversible: true,
|
|
3148
|
+
sideEffect: "none",
|
|
3149
|
+
rollbackLayer: "none",
|
|
3150
|
+
status: "read-only",
|
|
3151
|
+
message: `${toolName} does not mutate the local workspace.`
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
if (FILE_MUTATION_TOOLS.has(toolName)) {
|
|
3155
|
+
if (input.context.checkpointAvailable) {
|
|
3156
|
+
return {
|
|
3157
|
+
toolName,
|
|
3158
|
+
reversible: true,
|
|
3159
|
+
sideEffect: "file-mutation",
|
|
3160
|
+
rollbackLayer: "edit-checkpoint",
|
|
3161
|
+
status: "reversible",
|
|
3162
|
+
message: `${toolName} is reversible through the active edit checkpoint.`
|
|
3163
|
+
};
|
|
3164
|
+
}
|
|
3165
|
+
return {
|
|
3166
|
+
toolName,
|
|
3167
|
+
reversible: false,
|
|
3168
|
+
sideEffect: "file-mutation",
|
|
3169
|
+
rollbackLayer: "none",
|
|
3170
|
+
status: "requires-checkpoint",
|
|
3171
|
+
message: `${toolName} requires an edit checkpoint before file mutation.`
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
if (HOST_SHELL_TOOLS.has(toolName)) {
|
|
3175
|
+
return evaluateIsolatedSideEffect(toolName, "shell-process", input.context);
|
|
3176
|
+
}
|
|
3177
|
+
if (toolName === "Agent") {
|
|
3178
|
+
return evaluateAgentSafety(input.toolArgs, input.context);
|
|
3179
|
+
}
|
|
3180
|
+
return {
|
|
3181
|
+
toolName,
|
|
3182
|
+
reversible: false,
|
|
3183
|
+
sideEffect: "unknown",
|
|
3184
|
+
rollbackLayer: "none",
|
|
3185
|
+
status: "unknown",
|
|
3186
|
+
message: `${toolName} has no reversible execution contract.`
|
|
3187
|
+
};
|
|
3188
|
+
}
|
|
3189
|
+
function wrapReversibleExecutionTools(tools, options) {
|
|
3190
|
+
const safetyContext = {
|
|
3191
|
+
checkpointAvailable: options.checkpointAvailable,
|
|
3192
|
+
isolation: options.isolation ?? "none"
|
|
3193
|
+
};
|
|
3194
|
+
const enforceUntrackedSideEffects = options.enforceUntrackedSideEffects ?? true;
|
|
3195
|
+
return tools.map(
|
|
3196
|
+
(tool) => new ReversibleExecutionToolWrapper(tool, {
|
|
3197
|
+
safetyContext,
|
|
3198
|
+
enforceUntrackedSideEffects
|
|
3199
|
+
})
|
|
3200
|
+
);
|
|
3201
|
+
}
|
|
3202
|
+
var ReversibleExecutionToolWrapper = class {
|
|
3203
|
+
constructor(delegate, options) {
|
|
3204
|
+
this.delegate = delegate;
|
|
3205
|
+
this.options = options;
|
|
3206
|
+
this.schema = delegate.schema;
|
|
3207
|
+
}
|
|
3208
|
+
schema;
|
|
3209
|
+
setEventService(eventService) {
|
|
3210
|
+
this.delegate.setEventService(eventService);
|
|
3211
|
+
}
|
|
3212
|
+
async execute(parameters, context) {
|
|
3213
|
+
const report = evaluateReversibleToolSafety({
|
|
3214
|
+
toolName: this.getName(),
|
|
3215
|
+
toolArgs: toToolArgs(parameters),
|
|
3216
|
+
context: this.options.safetyContext
|
|
3217
|
+
});
|
|
3218
|
+
if (!report.reversible && this.options.enforceUntrackedSideEffects) {
|
|
3219
|
+
return createBlockedResult(report);
|
|
3220
|
+
}
|
|
3221
|
+
return this.delegate.execute(parameters, context);
|
|
3222
|
+
}
|
|
3223
|
+
validate(parameters) {
|
|
3224
|
+
return this.delegate.validate(parameters);
|
|
3225
|
+
}
|
|
3226
|
+
validateParameters(parameters) {
|
|
3227
|
+
return this.delegate.validateParameters(parameters);
|
|
3228
|
+
}
|
|
3229
|
+
getDescription() {
|
|
3230
|
+
return this.delegate.getDescription();
|
|
3231
|
+
}
|
|
3232
|
+
getName() {
|
|
3233
|
+
return this.delegate.getName();
|
|
3234
|
+
}
|
|
3235
|
+
};
|
|
3236
|
+
function evaluateIsolatedSideEffect(toolName, sideEffect, context) {
|
|
3237
|
+
if (context.isolation === "worktree") {
|
|
3238
|
+
return {
|
|
3239
|
+
toolName,
|
|
3240
|
+
reversible: true,
|
|
3241
|
+
sideEffect,
|
|
3242
|
+
rollbackLayer: "worktree",
|
|
3243
|
+
status: "reversible",
|
|
3244
|
+
message: `${toolName} side effects are contained in an isolated Git worktree.`
|
|
3245
|
+
};
|
|
3246
|
+
}
|
|
3247
|
+
if (context.isolation === "provider-sandbox") {
|
|
3248
|
+
return {
|
|
3249
|
+
toolName,
|
|
3250
|
+
reversible: true,
|
|
3251
|
+
sideEffect,
|
|
3252
|
+
rollbackLayer: "provider-sandbox",
|
|
3253
|
+
status: "reversible",
|
|
3254
|
+
message: `${toolName} side effects are contained in a provider sandbox snapshot.`
|
|
3255
|
+
};
|
|
3256
|
+
}
|
|
3257
|
+
return {
|
|
3258
|
+
toolName,
|
|
3259
|
+
reversible: false,
|
|
3260
|
+
sideEffect,
|
|
3261
|
+
rollbackLayer: "none",
|
|
3262
|
+
status: "requires-isolation",
|
|
3263
|
+
message: `${toolName} can create host shell side effects that edit checkpoints cannot restore; use worktree or provider sandbox isolation.`
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
function evaluateAgentSafety(toolArgs, context) {
|
|
3267
|
+
if (context.isolation === "worktree" || context.isolation === "provider-sandbox") {
|
|
3268
|
+
return evaluateIsolatedSideEffect("Agent", "subagent", context);
|
|
3269
|
+
}
|
|
3270
|
+
if (agentRequestUsesWorktree(toolArgs)) {
|
|
3271
|
+
return {
|
|
3272
|
+
toolName: "Agent",
|
|
3273
|
+
reversible: true,
|
|
3274
|
+
sideEffect: "subagent",
|
|
3275
|
+
rollbackLayer: "worktree",
|
|
3276
|
+
status: "reversible",
|
|
3277
|
+
message: "Agent jobs request worktree isolation, so shell side effects stay outside the parent workspace."
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3280
|
+
return {
|
|
3281
|
+
toolName: "Agent",
|
|
3282
|
+
reversible: false,
|
|
3283
|
+
sideEffect: "subagent",
|
|
3284
|
+
rollbackLayer: "none",
|
|
3285
|
+
status: "requires-isolation",
|
|
3286
|
+
message: "Agent jobs must request worktree isolation to be reversible in local-first mode."
|
|
3287
|
+
};
|
|
3288
|
+
}
|
|
3289
|
+
function agentRequestUsesWorktree(toolArgs) {
|
|
3290
|
+
if (!toolArgs) return false;
|
|
3291
|
+
const jobs = toolArgs.jobs;
|
|
3292
|
+
if (Array.isArray(jobs)) {
|
|
3293
|
+
return jobs.length > 0 && jobs.every((job) => {
|
|
3294
|
+
if (!isToolArgRecord(job)) return false;
|
|
3295
|
+
return readIsolation(job) === "worktree";
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
return readIsolation(toolArgs) === "worktree";
|
|
3299
|
+
}
|
|
3300
|
+
function readIsolation(value) {
|
|
3301
|
+
const isolation = value["isolation"];
|
|
3302
|
+
return typeof isolation === "string" ? isolation : void 0;
|
|
3303
|
+
}
|
|
3304
|
+
function isToolArgRecord(value) {
|
|
3305
|
+
return value !== void 0 && typeof value === "object" && !Array.isArray(value);
|
|
3306
|
+
}
|
|
3307
|
+
function toToolArgs(parameters) {
|
|
3308
|
+
if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return void 0;
|
|
3309
|
+
return parameters;
|
|
3310
|
+
}
|
|
3311
|
+
function createBlockedResult(report) {
|
|
3312
|
+
return {
|
|
3313
|
+
success: true,
|
|
3314
|
+
data: {
|
|
3315
|
+
success: false,
|
|
3316
|
+
output: "",
|
|
3317
|
+
error: report.message,
|
|
3318
|
+
reversibleSafety: {
|
|
3319
|
+
toolName: report.toolName,
|
|
3320
|
+
sideEffect: report.sideEffect,
|
|
3321
|
+
rollbackLayer: report.rollbackLayer,
|
|
3322
|
+
status: report.status
|
|
3323
|
+
}
|
|
3324
|
+
},
|
|
3325
|
+
metadata: {
|
|
3326
|
+
reversibleSafetyStatus: report.status,
|
|
3327
|
+
rollbackLayer: report.rollbackLayer
|
|
3328
|
+
}
|
|
3329
|
+
};
|
|
3330
|
+
}
|
|
3331
|
+
|
|
2963
3332
|
// src/assembly/create-session.ts
|
|
2964
3333
|
import { BackgroundTaskManager as BackgroundTaskManager2, SubagentManager as SubagentManager2 } from "@robota-sdk/agent-runtime";
|
|
2965
3334
|
|
|
@@ -3131,7 +3500,11 @@ function createSession(options) {
|
|
|
3131
3500
|
const cwd = options.cwd ?? process.cwd();
|
|
3132
3501
|
const sessionId = options.sessionId ?? createSessionId();
|
|
3133
3502
|
const defaultTools = options.editCheckpointRecorder ? wrapEditCheckpointTools(createDefaultTools(), options.editCheckpointRecorder) : createDefaultTools();
|
|
3134
|
-
const
|
|
3503
|
+
const assembledTools = [...defaultTools, ...options.additionalTools ?? []];
|
|
3504
|
+
const tools = options.reversibleExecution ? wrapReversibleExecutionTools(assembledTools, {
|
|
3505
|
+
...options.reversibleExecution,
|
|
3506
|
+
checkpointAvailable: options.editCheckpointRecorder !== void 0
|
|
3507
|
+
}) : assembledTools;
|
|
3135
3508
|
if (options.modelCommandExecutor && options.isModelCommandInvocable) {
|
|
3136
3509
|
tools.push(
|
|
3137
3510
|
createCommandExecutionTool({
|
|
@@ -3270,7 +3643,8 @@ ${options.appendSystemPrompt}` : systemMessage;
|
|
|
3270
3643
|
allow: [...defaultAllow, ...options.config.permissions.allow ?? [], ...allowedToolPatterns],
|
|
3271
3644
|
deny: options.config.permissions.deny ?? []
|
|
3272
3645
|
};
|
|
3273
|
-
const
|
|
3646
|
+
const SessionWithAutoCompact = Session2;
|
|
3647
|
+
const session = new SessionWithAutoCompact({
|
|
3274
3648
|
tools,
|
|
3275
3649
|
provider,
|
|
3276
3650
|
systemMessage: finalSystemMessage,
|
|
@@ -3290,7 +3664,9 @@ ${options.appendSystemPrompt}` : systemMessage;
|
|
|
3290
3664
|
onToolExecution: options.onToolExecution,
|
|
3291
3665
|
promptForApproval: options.promptForApproval,
|
|
3292
3666
|
onCompact: options.onCompact,
|
|
3667
|
+
onCompactEvent: options.onCompactEvent,
|
|
3293
3668
|
compactInstructions: options.compactInstructions ?? options.context.compactInstructions,
|
|
3669
|
+
autoCompactThreshold: options.autoCompactThreshold ?? options.config.autoCompactThreshold,
|
|
3294
3670
|
sessionLogger: options.sessionLogger,
|
|
3295
3671
|
hookTypeExecutors: hookTypeExecutors.length > 0 ? hookTypeExecutors : void 0
|
|
3296
3672
|
});
|
|
@@ -4546,6 +4922,7 @@ async function createInteractiveSession(options) {
|
|
|
4546
4922
|
provider: options.provider,
|
|
4547
4923
|
onTextDelta: options.onTextDelta,
|
|
4548
4924
|
onContextUpdate: options.onContextUpdate,
|
|
4925
|
+
onCompactEvent: options.onCompactEvent,
|
|
4549
4926
|
onToolExecution: options.onToolExecution,
|
|
4550
4927
|
sessionId,
|
|
4551
4928
|
allowedTools: options.allowedTools,
|
|
@@ -4563,7 +4940,8 @@ async function createInteractiveSession(options) {
|
|
|
4563
4940
|
} : {},
|
|
4564
4941
|
modelCommandExecutor: options.modelCommandExecutor,
|
|
4565
4942
|
isModelCommandInvocable: options.isModelCommandInvocable,
|
|
4566
|
-
editCheckpointRecorder: options.editCheckpointRecorder
|
|
4943
|
+
editCheckpointRecorder: options.editCheckpointRecorder,
|
|
4944
|
+
reversibleExecution: options.reversibleExecution
|
|
4567
4945
|
});
|
|
4568
4946
|
}
|
|
4569
4947
|
function injectSavedMessage(session, msg) {
|
|
@@ -4661,7 +5039,59 @@ function isRestoredTerminalStatus(status) {
|
|
|
4661
5039
|
// src/checkpoints/edit-checkpoint-store.ts
|
|
4662
5040
|
import { access, copyFile, mkdir, rename, rm, writeFile } from "fs/promises";
|
|
4663
5041
|
import { constants, readdirSync as readdirSync7, readFileSync as readFileSync15 } from "fs";
|
|
4664
|
-
import { dirname as dirname8, join as
|
|
5042
|
+
import { dirname as dirname8, join as join18, relative as relative3, resolve as resolve3 } from "path";
|
|
5043
|
+
|
|
5044
|
+
// src/checkpoints/edit-checkpoint-inspection.ts
|
|
5045
|
+
import { statSync as statSync2 } from "fs";
|
|
5046
|
+
import { join as join17, relative as relative2 } from "path";
|
|
5047
|
+
function buildEditCheckpointInspection(input) {
|
|
5048
|
+
const later = input.manifests.filter((manifest) => manifest.sequence > input.target.sequence);
|
|
5049
|
+
const rollbackRange = input.manifests.filter(
|
|
5050
|
+
(manifest) => manifest.sequence >= input.target.sequence
|
|
5051
|
+
);
|
|
5052
|
+
return {
|
|
5053
|
+
target: toSummary(input.target),
|
|
5054
|
+
capturedFiles: input.target.files.map((file) => {
|
|
5055
|
+
const snapshotPath = file.snapshotFile ? join17(input.checkpointDir(input.sessionId, input.target.id), file.snapshotFile) : void 0;
|
|
5056
|
+
const snapshotStats = snapshotPath ? statSafe(snapshotPath) : void 0;
|
|
5057
|
+
return {
|
|
5058
|
+
originalPath: file.originalPath,
|
|
5059
|
+
relativePath: relative2(input.cwd, file.originalPath),
|
|
5060
|
+
existed: file.existed,
|
|
5061
|
+
restoreAction: file.existed ? "restore-preimage" : "delete-created-file",
|
|
5062
|
+
snapshotAvailable: file.existed ? snapshotStats !== void 0 : false,
|
|
5063
|
+
...snapshotStats ? { snapshotSizeBytes: snapshotStats.size } : {}
|
|
5064
|
+
};
|
|
5065
|
+
}),
|
|
5066
|
+
restoreToCheckpoint: toInspectionPlan(later),
|
|
5067
|
+
rollbackThroughCheckpoint: toInspectionPlan(rollbackRange)
|
|
5068
|
+
};
|
|
5069
|
+
}
|
|
5070
|
+
function toSummary(manifest) {
|
|
5071
|
+
return {
|
|
5072
|
+
id: manifest.id,
|
|
5073
|
+
sessionId: manifest.sessionId,
|
|
5074
|
+
sequence: manifest.sequence,
|
|
5075
|
+
prompt: manifest.prompt,
|
|
5076
|
+
createdAt: manifest.createdAt,
|
|
5077
|
+
fileCount: manifest.fileCount
|
|
5078
|
+
};
|
|
5079
|
+
}
|
|
5080
|
+
function toInspectionPlan(manifests) {
|
|
5081
|
+
return {
|
|
5082
|
+
checkpointIds: manifests.map((manifest) => manifest.id),
|
|
5083
|
+
fileCount: manifests.reduce((count, manifest) => count + manifest.fileCount, 0)
|
|
5084
|
+
};
|
|
5085
|
+
}
|
|
5086
|
+
function statSafe(path) {
|
|
5087
|
+
try {
|
|
5088
|
+
return statSync2(path);
|
|
5089
|
+
} catch {
|
|
5090
|
+
return void 0;
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
|
|
5094
|
+
// src/checkpoints/edit-checkpoint-store.ts
|
|
4665
5095
|
var MANIFEST_FILE = "manifest.json";
|
|
4666
5096
|
var SNAPSHOT_DIR = "files";
|
|
4667
5097
|
var ID_PAD = 4;
|
|
@@ -4682,8 +5112,8 @@ var EditCheckpointStore = class {
|
|
|
4682
5112
|
}
|
|
4683
5113
|
const nextSequence = this.nextSequence(input.sessionId);
|
|
4684
5114
|
const id = `turn-${String(nextSequence).padStart(ID_PAD, "0")}`;
|
|
4685
|
-
const dir =
|
|
4686
|
-
await mkdir(
|
|
5115
|
+
const dir = join18(this.sessionDir(input.sessionId), id);
|
|
5116
|
+
await mkdir(join18(dir, SNAPSHOT_DIR), { recursive: true });
|
|
4687
5117
|
const manifest = {
|
|
4688
5118
|
version: 1,
|
|
4689
5119
|
id,
|
|
@@ -4699,7 +5129,7 @@ var EditCheckpointStore = class {
|
|
|
4699
5129
|
dir,
|
|
4700
5130
|
capturedPaths: /* @__PURE__ */ new Set()
|
|
4701
5131
|
};
|
|
4702
|
-
return
|
|
5132
|
+
return toSummary2(manifest);
|
|
4703
5133
|
}
|
|
4704
5134
|
async captureFile(filePath) {
|
|
4705
5135
|
if (!this.activeTurn) return;
|
|
@@ -4716,10 +5146,24 @@ var EditCheckpointStore = class {
|
|
|
4716
5146
|
const active = this.activeTurn;
|
|
4717
5147
|
this.activeTurn = null;
|
|
4718
5148
|
await this.writeManifest(active.dir, active.manifest);
|
|
4719
|
-
return
|
|
5149
|
+
return toSummary2(active.manifest);
|
|
4720
5150
|
}
|
|
4721
5151
|
list(sessionId) {
|
|
4722
|
-
return this.loadManifests(sessionId).map(
|
|
5152
|
+
return this.loadManifests(sessionId).map(toSummary2);
|
|
5153
|
+
}
|
|
5154
|
+
inspect(sessionId, checkpointId) {
|
|
5155
|
+
const manifests = this.loadManifests(sessionId);
|
|
5156
|
+
const target = manifests.find((manifest) => manifest.id === checkpointId);
|
|
5157
|
+
if (!target) {
|
|
5158
|
+
throw new Error(`Unknown edit checkpoint: ${checkpointId}`);
|
|
5159
|
+
}
|
|
5160
|
+
return buildEditCheckpointInspection({
|
|
5161
|
+
cwd: this.cwd,
|
|
5162
|
+
sessionId,
|
|
5163
|
+
target,
|
|
5164
|
+
manifests,
|
|
5165
|
+
checkpointDir: (inputSessionId, inputCheckpointId) => this.checkpointDir(inputSessionId, inputCheckpointId)
|
|
5166
|
+
});
|
|
4723
5167
|
}
|
|
4724
5168
|
async restoreToCheckpoint(sessionId, checkpointId) {
|
|
4725
5169
|
const manifests = this.loadManifests(sessionId);
|
|
@@ -4739,12 +5183,36 @@ var EditCheckpointStore = class {
|
|
|
4739
5183
|
await rm(this.checkpointDir(sessionId, manifest.id), { recursive: true, force: true });
|
|
4740
5184
|
}
|
|
4741
5185
|
return {
|
|
4742
|
-
target:
|
|
5186
|
+
target: toSummary2(target),
|
|
4743
5187
|
restoredCheckpointCount: later.length,
|
|
4744
5188
|
restoredFileCount,
|
|
4745
5189
|
removedCheckpointCount: later.length
|
|
4746
5190
|
};
|
|
4747
5191
|
}
|
|
5192
|
+
async rollbackThroughCheckpoint(sessionId, checkpointId) {
|
|
5193
|
+
const manifests = this.loadManifests(sessionId);
|
|
5194
|
+
const target = manifests.find((manifest) => manifest.id === checkpointId);
|
|
5195
|
+
if (!target) {
|
|
5196
|
+
throw new Error(`Unknown edit checkpoint: ${checkpointId}`);
|
|
5197
|
+
}
|
|
5198
|
+
const rollbackRange = manifests.filter((manifest) => manifest.sequence >= target.sequence).sort((a, b) => b.sequence - a.sequence);
|
|
5199
|
+
let restoredFileCount = 0;
|
|
5200
|
+
for (const manifest of rollbackRange) {
|
|
5201
|
+
for (const file of manifest.files) {
|
|
5202
|
+
await this.restoreFile(sessionId, manifest.id, file);
|
|
5203
|
+
restoredFileCount += 1;
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
for (const manifest of rollbackRange) {
|
|
5207
|
+
await rm(this.checkpointDir(sessionId, manifest.id), { recursive: true, force: true });
|
|
5208
|
+
}
|
|
5209
|
+
return {
|
|
5210
|
+
target: toSummary2(target),
|
|
5211
|
+
restoredCheckpointCount: rollbackRange.length,
|
|
5212
|
+
restoredFileCount,
|
|
5213
|
+
removedCheckpointCount: rollbackRange.length
|
|
5214
|
+
};
|
|
5215
|
+
}
|
|
4748
5216
|
async createFileRecord(originalPath, active) {
|
|
4749
5217
|
const existed = await pathExists(originalPath);
|
|
4750
5218
|
if (!existed) {
|
|
@@ -4753,11 +5221,11 @@ var EditCheckpointStore = class {
|
|
|
4753
5221
|
existed: false
|
|
4754
5222
|
};
|
|
4755
5223
|
}
|
|
4756
|
-
const snapshotFile =
|
|
5224
|
+
const snapshotFile = join18(
|
|
4757
5225
|
SNAPSHOT_DIR,
|
|
4758
5226
|
`${String(active.manifest.files.length + 1).padStart(SNAPSHOT_PAD, "0")}.content`
|
|
4759
5227
|
);
|
|
4760
|
-
await copyFile(originalPath,
|
|
5228
|
+
await copyFile(originalPath, join18(active.dir, snapshotFile));
|
|
4761
5229
|
return {
|
|
4762
5230
|
originalPath,
|
|
4763
5231
|
existed: true,
|
|
@@ -4774,13 +5242,13 @@ var EditCheckpointStore = class {
|
|
|
4774
5242
|
}
|
|
4775
5243
|
await mkdir(dirname8(record.originalPath), { recursive: true });
|
|
4776
5244
|
await copyFile(
|
|
4777
|
-
|
|
5245
|
+
join18(this.checkpointDir(sessionId, checkpointId), record.snapshotFile),
|
|
4778
5246
|
record.originalPath
|
|
4779
5247
|
);
|
|
4780
5248
|
}
|
|
4781
5249
|
loadManifests(sessionId) {
|
|
4782
5250
|
const dir = this.sessionDir(sessionId);
|
|
4783
|
-
return readDirSyncSafe(dir).map((entry) =>
|
|
5251
|
+
return readDirSyncSafe(dir).map((entry) => join18(dir, entry, MANIFEST_FILE)).map((manifestPath) => readJsonManifest(manifestPath)).filter((manifest) => manifest !== void 0).sort((a, b) => a.sequence - b.sequence);
|
|
4784
5252
|
}
|
|
4785
5253
|
nextSequence(sessionId) {
|
|
4786
5254
|
const last = this.list(sessionId).at(-1);
|
|
@@ -4788,19 +5256,19 @@ var EditCheckpointStore = class {
|
|
|
4788
5256
|
}
|
|
4789
5257
|
async writeManifest(dir, manifest) {
|
|
4790
5258
|
await mkdir(dir, { recursive: true });
|
|
4791
|
-
const path =
|
|
5259
|
+
const path = join18(dir, MANIFEST_FILE);
|
|
4792
5260
|
const tmp = `${path}.tmp`;
|
|
4793
5261
|
await writeFile(tmp, JSON.stringify(manifest, null, 2), "utf8");
|
|
4794
5262
|
await rename(tmp, path);
|
|
4795
5263
|
}
|
|
4796
5264
|
sessionDir(sessionId) {
|
|
4797
|
-
return
|
|
5265
|
+
return join18(this.rootDir, safePathSegment(sessionId));
|
|
4798
5266
|
}
|
|
4799
5267
|
checkpointDir(sessionId, checkpointId) {
|
|
4800
|
-
return
|
|
5268
|
+
return join18(this.sessionDir(sessionId), safePathSegment(checkpointId));
|
|
4801
5269
|
}
|
|
4802
5270
|
};
|
|
4803
|
-
function
|
|
5271
|
+
function toSummary2(manifest) {
|
|
4804
5272
|
return {
|
|
4805
5273
|
id: manifest.id,
|
|
4806
5274
|
sessionId: manifest.sessionId,
|
|
@@ -4814,7 +5282,7 @@ function safePathSegment(value) {
|
|
|
4814
5282
|
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4815
5283
|
}
|
|
4816
5284
|
function isInside(parent, child) {
|
|
4817
|
-
const rel =
|
|
5285
|
+
const rel = relative3(parent, child);
|
|
4818
5286
|
return rel.length === 0 || !rel.startsWith("..") && !rel.startsWith("/");
|
|
4819
5287
|
}
|
|
4820
5288
|
async function pathExists(path) {
|
|
@@ -4873,14 +5341,19 @@ var InteractiveSession = class {
|
|
|
4873
5341
|
backgroundJobUnsubscribe = null;
|
|
4874
5342
|
backgroundJobOrchestrator = null;
|
|
4875
5343
|
commandModules;
|
|
5344
|
+
commandHostAdapters;
|
|
5345
|
+
autoCompactThresholdSource = "default";
|
|
4876
5346
|
shuttingDown = false;
|
|
4877
5347
|
shutdownPromise = null;
|
|
4878
5348
|
constructor(options) {
|
|
4879
|
-
|
|
4880
|
-
this.
|
|
4881
|
-
|
|
4882
|
-
...
|
|
4883
|
-
]
|
|
5349
|
+
const sdkBuiltinModule = createBuiltinCommandModule();
|
|
5350
|
+
this.commandModules = [
|
|
5351
|
+
sdkBuiltinModule,
|
|
5352
|
+
..."commandModules" in options ? options.commandModules ?? [] : []
|
|
5353
|
+
];
|
|
5354
|
+
this.commandExecutor = new SystemCommandExecutor(
|
|
5355
|
+
this.commandModules.flatMap((module) => module.systemCommands ?? [])
|
|
5356
|
+
);
|
|
4884
5357
|
this.sessionStore = options.sessionStore;
|
|
4885
5358
|
this.sessionName = options.sessionName;
|
|
4886
5359
|
this.cwd = ("cwd" in options ? options.cwd : void 0) ?? "";
|
|
@@ -4889,8 +5362,10 @@ var InteractiveSession = class {
|
|
|
4889
5362
|
}
|
|
4890
5363
|
this.resumeSessionId = options.resumeSessionId;
|
|
4891
5364
|
this.forkSession = options.forkSession ?? false;
|
|
5365
|
+
this.commandHostAdapters = "commandHostAdapters" in options ? options.commandHostAdapters : void 0;
|
|
4892
5366
|
if ("session" in options && options.session) {
|
|
4893
5367
|
this.session = options.session;
|
|
5368
|
+
this.autoCompactThresholdSource = "session";
|
|
4894
5369
|
this.initialized = true;
|
|
4895
5370
|
} else {
|
|
4896
5371
|
const stdOpts = options;
|
|
@@ -4917,7 +5392,8 @@ var InteractiveSession = class {
|
|
|
4917
5392
|
if (this.initialized) this.persistCurrentSession();
|
|
4918
5393
|
}
|
|
4919
5394
|
async initializeAsync(options) {
|
|
4920
|
-
const config = await loadConfig(options.cwd);
|
|
5395
|
+
const config = options.config ?? await loadConfig(options.cwd);
|
|
5396
|
+
this.autoCompactThresholdSource = config.autoCompactThreshold === void 0 ? "default" : "settings";
|
|
4921
5397
|
this.editCheckpointStore = new EditCheckpointStore({ cwd: options.cwd });
|
|
4922
5398
|
this.session = await createInteractiveSession({
|
|
4923
5399
|
cwd: options.cwd,
|
|
@@ -4930,6 +5406,7 @@ var InteractiveSession = class {
|
|
|
4930
5406
|
forkSession: this.forkSession,
|
|
4931
5407
|
onTextDelta: (delta) => this.handleTextDelta(delta),
|
|
4932
5408
|
onContextUpdate: (state) => this.emit("context_update", state),
|
|
5409
|
+
onCompactEvent: (event) => this.handleCompactEvent(event),
|
|
4933
5410
|
onToolExecution: (event) => this.handleToolExecution(event),
|
|
4934
5411
|
bare: options.bare,
|
|
4935
5412
|
allowedTools: options.allowedTools,
|
|
@@ -4938,6 +5415,7 @@ var InteractiveSession = class {
|
|
|
4938
5415
|
subagentRunnerFactory: options.subagentRunnerFactory,
|
|
4939
5416
|
...options.commandModules ? { commandModules: options.commandModules } : {},
|
|
4940
5417
|
editCheckpointRecorder: this.editCheckpointStore,
|
|
5418
|
+
...options.reversibleExecution ? { reversibleExecution: options.reversibleExecution } : {},
|
|
4941
5419
|
commandDescriptors: this.commandExecutor.listModelInvocableCommands(),
|
|
4942
5420
|
...this.commandExecutor.listModelInvocableCommands().length > 0 ? {
|
|
4943
5421
|
modelCommandExecutor: (command, args) => this.executeModelCommand(command, args),
|
|
@@ -4984,7 +5462,18 @@ var InteractiveSession = class {
|
|
|
4984
5462
|
}
|
|
4985
5463
|
async executeCommand(name, args) {
|
|
4986
5464
|
await this.ensureInitialized();
|
|
4987
|
-
|
|
5465
|
+
const command = this.commandExecutor.getCommand(name);
|
|
5466
|
+
if (!command) return null;
|
|
5467
|
+
if (this.executing) {
|
|
5468
|
+
return {
|
|
5469
|
+
success: false,
|
|
5470
|
+
message: "Another prompt or command is already running. Wait for it to finish."
|
|
5471
|
+
};
|
|
5472
|
+
}
|
|
5473
|
+
if (command.lifecycle === "blocking") {
|
|
5474
|
+
return this.executeForegroundCommand(command, args);
|
|
5475
|
+
}
|
|
5476
|
+
return this.commandExecutor.executeCommand(command, this, args);
|
|
4988
5477
|
}
|
|
4989
5478
|
async executeModelCommand(name, args) {
|
|
4990
5479
|
await this.ensureInitialized();
|
|
@@ -5077,6 +5566,30 @@ var InteractiveSession = class {
|
|
|
5077
5566
|
getContextState() {
|
|
5078
5567
|
return this.getSessionOrThrow().getContextState();
|
|
5079
5568
|
}
|
|
5569
|
+
getAutoCompactThreshold() {
|
|
5570
|
+
return this.getSessionOrThrow().getAutoCompactThreshold();
|
|
5571
|
+
}
|
|
5572
|
+
getAutoCompactThresholdSource() {
|
|
5573
|
+
return this.autoCompactThresholdSource;
|
|
5574
|
+
}
|
|
5575
|
+
setAutoCompactThreshold(threshold, source = "session") {
|
|
5576
|
+
this.getSessionOrThrow().setAutoCompactThreshold(threshold);
|
|
5577
|
+
this.autoCompactThresholdSource = source;
|
|
5578
|
+
this.emit("context_update", this.getContextState());
|
|
5579
|
+
this.persistCurrentSession();
|
|
5580
|
+
}
|
|
5581
|
+
getCommandHostAdapters() {
|
|
5582
|
+
return this.commandHostAdapters ?? {};
|
|
5583
|
+
}
|
|
5584
|
+
clearConversationHistory() {
|
|
5585
|
+
this.getSessionOrThrow().clearHistory();
|
|
5586
|
+
this.history = [];
|
|
5587
|
+
this.persistCurrentSession();
|
|
5588
|
+
this.emit("context_update", this.getContextState());
|
|
5589
|
+
}
|
|
5590
|
+
async compactContext(instructions) {
|
|
5591
|
+
await this.getSessionOrThrow().compact(instructions);
|
|
5592
|
+
}
|
|
5080
5593
|
getName() {
|
|
5081
5594
|
return this.sessionName;
|
|
5082
5595
|
}
|
|
@@ -5090,6 +5603,10 @@ var InteractiveSession = class {
|
|
|
5090
5603
|
const sessionId = this.getSessionOrThrow().getSessionId();
|
|
5091
5604
|
return this.getEditCheckpointStore().list(sessionId);
|
|
5092
5605
|
}
|
|
5606
|
+
inspectEditCheckpoint(checkpointId) {
|
|
5607
|
+
const sessionId = this.getSessionOrThrow().getSessionId();
|
|
5608
|
+
return this.getEditCheckpointStore().inspect(sessionId, checkpointId);
|
|
5609
|
+
}
|
|
5093
5610
|
async restoreEditCheckpoint(checkpointId) {
|
|
5094
5611
|
await this.ensureInitialized();
|
|
5095
5612
|
if (this.executing) {
|
|
@@ -5105,6 +5622,21 @@ var InteractiveSession = class {
|
|
|
5105
5622
|
this.persistCurrentSession();
|
|
5106
5623
|
return result;
|
|
5107
5624
|
}
|
|
5625
|
+
async rollbackEditCheckpoint(checkpointId) {
|
|
5626
|
+
await this.ensureInitialized();
|
|
5627
|
+
if (this.executing) {
|
|
5628
|
+
throw new Error("Cannot rollback edit checkpoint while a prompt is running.");
|
|
5629
|
+
}
|
|
5630
|
+
const result = await this.getEditCheckpointStore().rollbackThroughCheckpoint(
|
|
5631
|
+
this.getSessionOrThrow().getSessionId(),
|
|
5632
|
+
checkpointId
|
|
5633
|
+
);
|
|
5634
|
+
this.history.push(
|
|
5635
|
+
messageToHistoryEntry(createSystemMessage(`Rolled back edit checkpoint: ${checkpointId}`))
|
|
5636
|
+
);
|
|
5637
|
+
this.persistCurrentSession();
|
|
5638
|
+
return result;
|
|
5639
|
+
}
|
|
5108
5640
|
getUsedMemoryReferences() {
|
|
5109
5641
|
return [...this.usedMemoryReferences];
|
|
5110
5642
|
}
|
|
@@ -5413,6 +5945,33 @@ var InteractiveSession = class {
|
|
|
5413
5945
|
this.emit("complete", executionResult);
|
|
5414
5946
|
this.emit("context_update", this.getContextState());
|
|
5415
5947
|
}
|
|
5948
|
+
async executeForegroundCommand(command, args) {
|
|
5949
|
+
this.executing = true;
|
|
5950
|
+
this.clearStreaming();
|
|
5951
|
+
this.emit("thinking", true);
|
|
5952
|
+
try {
|
|
5953
|
+
const result = await this.commandExecutor.executeCommand(command, this, args);
|
|
5954
|
+
this.emit("context_update", this.getContextState());
|
|
5955
|
+
return result;
|
|
5956
|
+
} catch (err) {
|
|
5957
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
5958
|
+
return {
|
|
5959
|
+
success: false,
|
|
5960
|
+
message: `Error: ${errMsg}`
|
|
5961
|
+
};
|
|
5962
|
+
} finally {
|
|
5963
|
+
this.executing = false;
|
|
5964
|
+
this.emit("thinking", false);
|
|
5965
|
+
this.persistCurrentSession();
|
|
5966
|
+
if (!this.shuttingDown && this.pendingPrompt) {
|
|
5967
|
+
const queued = this.pendingPrompt;
|
|
5968
|
+
const queuedDisplay = this.pendingDisplayInput;
|
|
5969
|
+
const queuedRaw = this.pendingRawInput;
|
|
5970
|
+
this.clearPendingQueue();
|
|
5971
|
+
setTimeout(() => this.executePrompt(queued, queuedDisplay, queuedRaw), 0);
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
}
|
|
5416
5975
|
async executePrompt(input, displayInput, rawInput) {
|
|
5417
5976
|
this.executing = true;
|
|
5418
5977
|
this.clearStreaming();
|
|
@@ -5508,6 +6067,19 @@ var InteractiveSession = class {
|
|
|
5508
6067
|
}, STREAMING_FLUSH_INTERVAL_MS);
|
|
5509
6068
|
}
|
|
5510
6069
|
}
|
|
6070
|
+
handleCompactEvent(event) {
|
|
6071
|
+
if (event.trigger === "auto") {
|
|
6072
|
+
this.history.push(
|
|
6073
|
+
messageToHistoryEntry(
|
|
6074
|
+
createSystemMessage(
|
|
6075
|
+
`Auto compacted context: ${Math.round(event.before.usedPercentage)}% -> ${Math.round(event.after.usedPercentage)}%`
|
|
6076
|
+
)
|
|
6077
|
+
)
|
|
6078
|
+
);
|
|
6079
|
+
}
|
|
6080
|
+
this.emit("compact", event);
|
|
6081
|
+
this.emit("context_update", event.after);
|
|
6082
|
+
}
|
|
5511
6083
|
handleToolExecution(event) {
|
|
5512
6084
|
const streamingState = { activeTools: this.activeTools, history: this.history };
|
|
5513
6085
|
if (event.type === "start") {
|
|
@@ -5734,7 +6306,10 @@ async function promptForApproval(terminal, toolName, toolArgs) {
|
|
|
5734
6306
|
// src/index.ts
|
|
5735
6307
|
import { runHooks as runHooks2 } from "@robota-sdk/agent-core";
|
|
5736
6308
|
export {
|
|
6309
|
+
AUTO_COMPACT_THRESHOLD_SETTINGS_KEY,
|
|
5737
6310
|
AgentExecutor,
|
|
6311
|
+
BACKGROUND_COMMAND_DESCRIPTION,
|
|
6312
|
+
BACKGROUND_COMMAND_USAGE,
|
|
5738
6313
|
BUILT_IN_AGENTS,
|
|
5739
6314
|
BackgroundJobOrchestrator,
|
|
5740
6315
|
BackgroundTaskError,
|
|
@@ -5742,64 +6317,171 @@ export {
|
|
|
5742
6317
|
BuiltinCommandSource,
|
|
5743
6318
|
BundlePluginInstaller,
|
|
5744
6319
|
BundlePluginLoader,
|
|
6320
|
+
CLEAR_COMMAND_DESCRIPTION,
|
|
6321
|
+
COST_COMMAND_DESCRIPTION,
|
|
5745
6322
|
CommandRegistry,
|
|
6323
|
+
DEFAULT_AUTO_COMPACT_THRESHOLD,
|
|
6324
|
+
DEFAULT_STATUS_LINE_COMMAND_SETTINGS,
|
|
6325
|
+
EXIT_COMMAND_DESCRIPTION,
|
|
5746
6326
|
EditCheckpointStore,
|
|
6327
|
+
HELP_COMMAND_DESCRIPTION,
|
|
5747
6328
|
InteractiveSession,
|
|
6329
|
+
LANGUAGE_COMMAND_ARGUMENT_HINT,
|
|
6330
|
+
LANGUAGE_COMMAND_DESCRIPTION,
|
|
6331
|
+
MEMORY_COMMAND_ARGUMENT_HINT,
|
|
6332
|
+
MEMORY_COMMAND_DESCRIPTION,
|
|
6333
|
+
MEMORY_COMMAND_USAGE,
|
|
5748
6334
|
MEMORY_INDEX_MAX_BYTES,
|
|
5749
6335
|
MEMORY_INDEX_MAX_LINES,
|
|
6336
|
+
MODEL_COMMAND_ARGUMENT_HINT,
|
|
6337
|
+
MODEL_COMMAND_DESCRIPTION,
|
|
5750
6338
|
MarketplaceClient,
|
|
6339
|
+
PERMISSIONS_COMMAND_DESCRIPTION,
|
|
6340
|
+
PERMISSION_MODE_ARGUMENT_HINT,
|
|
6341
|
+
PERMISSION_MODE_COMMAND_DESCRIPTION,
|
|
6342
|
+
PLUGIN_COMMAND_ARGUMENT_HINT,
|
|
6343
|
+
PLUGIN_COMMAND_DESCRIPTION,
|
|
5751
6344
|
PluginCommandSource,
|
|
5752
6345
|
PluginSettingsStore,
|
|
5753
6346
|
ProjectMemoryStore,
|
|
5754
6347
|
PromptExecutor,
|
|
6348
|
+
RECOMMENDED_RESPONSE_LANGUAGES,
|
|
6349
|
+
RELOAD_PLUGINS_COMMAND_DESCRIPTION,
|
|
6350
|
+
RENAME_COMMAND_DESCRIPTION,
|
|
6351
|
+
RENAME_COMMAND_USAGE,
|
|
6352
|
+
RESUME_COMMAND_DESCRIPTION,
|
|
6353
|
+
REWIND_COMMAND_ARGUMENT_HINT,
|
|
6354
|
+
REWIND_COMMAND_DESCRIPTION,
|
|
6355
|
+
STATUSLINE_COMMAND_ARGUMENT_HINT,
|
|
6356
|
+
STATUSLINE_COMMAND_DESCRIPTION,
|
|
5755
6357
|
SkillCommandSource,
|
|
5756
6358
|
SubagentManager3 as SubagentManager,
|
|
5757
6359
|
SystemCommandExecutor,
|
|
5758
6360
|
TRUST_TO_MODE,
|
|
6361
|
+
VALID_PERMISSION_MODES,
|
|
5759
6362
|
WorktreeSubagentRunner,
|
|
5760
6363
|
assembleSubagentPrompt,
|
|
6364
|
+
buildBackgroundCommandSubcommands,
|
|
6365
|
+
buildLanguageCommandSubcommands,
|
|
6366
|
+
buildMemoryCommandSubcommands,
|
|
6367
|
+
buildModelCommandSubcommands,
|
|
6368
|
+
buildPermissionModeSubcommands,
|
|
6369
|
+
buildPluginCommandSubcommands,
|
|
6370
|
+
buildProviderProfile,
|
|
6371
|
+
buildProviderSetupPatch,
|
|
6372
|
+
buildRewindCommandSubcommands,
|
|
5761
6373
|
buildSkillPrompt,
|
|
6374
|
+
buildStatusLineCommandSubcommands,
|
|
6375
|
+
cancelCommandBackgroundTask,
|
|
5762
6376
|
chatEntryToMessage,
|
|
6377
|
+
clearConversationHistory,
|
|
6378
|
+
closeCommandBackgroundTask,
|
|
6379
|
+
compactCommandContext,
|
|
5763
6380
|
createAgentTool,
|
|
5764
6381
|
createBackgroundProcessTool,
|
|
6382
|
+
createBuiltinCommandModule,
|
|
5765
6383
|
createCommandExecutionTool,
|
|
6384
|
+
createCommandMemoryStores,
|
|
6385
|
+
createCommandPendingMemoryStore,
|
|
6386
|
+
createCommandProjectMemoryStore,
|
|
5766
6387
|
createDefaultTools,
|
|
6388
|
+
createPluginRegistryReloadRequestedEffect,
|
|
6389
|
+
createPluginTuiRequestedEffect,
|
|
6390
|
+
createProviderSetupFlow,
|
|
5767
6391
|
createQuery,
|
|
6392
|
+
createSessionExitRequestedEffect,
|
|
6393
|
+
createSessionPickerRequestedEffect,
|
|
6394
|
+
createSessionRenamedEffect,
|
|
5768
6395
|
createSubagentLogger,
|
|
5769
6396
|
createSubagentSession,
|
|
5770
6397
|
createSystemCommands,
|
|
5771
6398
|
createWorktreeSubagentRunner,
|
|
5772
6399
|
discoverTaskFiles,
|
|
5773
6400
|
evaluatePermission,
|
|
6401
|
+
evaluateReversibleToolSafety,
|
|
5774
6402
|
executeSkill,
|
|
6403
|
+
formatCommandBackgroundTask,
|
|
6404
|
+
formatCommandBackgroundTaskList,
|
|
6405
|
+
formatCommandHelpMessage,
|
|
6406
|
+
formatCommandPermissionsMessage,
|
|
6407
|
+
formatEnvReference,
|
|
6408
|
+
formatInvalidPermissionModeMessage,
|
|
6409
|
+
formatLanguageUsageMessage,
|
|
6410
|
+
formatProviderSetupChoiceLabel,
|
|
6411
|
+
formatProviderSetupPromptLabel,
|
|
6412
|
+
formatProviderSetupSelectionPrompt,
|
|
5775
6413
|
formatTaskContext,
|
|
5776
6414
|
getBackgroundTaskTransitions,
|
|
5777
6415
|
getBuiltInAgent,
|
|
5778
6416
|
getForkWorkerSuffix,
|
|
5779
6417
|
getMessagesForAPI,
|
|
6418
|
+
getProviderSetupStep,
|
|
5780
6419
|
getSubagentSuffix,
|
|
6420
|
+
hasSensitiveCommandMemoryContent,
|
|
6421
|
+
hasUsableSecretReference,
|
|
6422
|
+
inspectCommandEditCheckpoint,
|
|
5781
6423
|
isChatEntry,
|
|
6424
|
+
isCommandMemoryType,
|
|
6425
|
+
isEnvReference,
|
|
5782
6426
|
isMemoryType,
|
|
6427
|
+
isPermissionMode,
|
|
6428
|
+
isStatusLineCommandSettingsPatch,
|
|
5783
6429
|
isTerminalBackgroundTaskStatus2 as isTerminalBackgroundTaskStatus,
|
|
6430
|
+
listCommandBackgroundTasks,
|
|
6431
|
+
listCommandEditCheckpoints,
|
|
6432
|
+
listCommandSessionAllowedTools,
|
|
6433
|
+
listCommandUsedMemoryReferences,
|
|
5784
6434
|
loadTaskContext,
|
|
6435
|
+
mergeProviderPatch,
|
|
5785
6436
|
messageToHistoryEntry2 as messageToHistoryEntry,
|
|
6437
|
+
parseCommandBackgroundLogCursor,
|
|
5786
6438
|
parseFrontmatter,
|
|
6439
|
+
parseLanguageArgument,
|
|
6440
|
+
parsePermissionModeArgument,
|
|
6441
|
+
parseSessionNameArgument,
|
|
5787
6442
|
parseTaskFile,
|
|
5788
6443
|
planSelfHostingVerification,
|
|
5789
6444
|
preprocessShellCommands,
|
|
6445
|
+
probeProviderProfile,
|
|
5790
6446
|
projectPaths,
|
|
5791
6447
|
promptForApproval,
|
|
6448
|
+
readAutoCompactThreshold,
|
|
6449
|
+
readAutoCompactThresholdSource,
|
|
6450
|
+
readCommandBackgroundTaskLog,
|
|
6451
|
+
readCommandContextState,
|
|
6452
|
+
readCommandPermissionMode,
|
|
6453
|
+
readCommandPermissionsState,
|
|
6454
|
+
readCommandSessionInfo,
|
|
5792
6455
|
readCurrentGitBranch,
|
|
6456
|
+
recordCommandMemoryEvent,
|
|
6457
|
+
resetAutoCompactThresholdSetting,
|
|
6458
|
+
resolveEnvReference,
|
|
6459
|
+
resolvePermissionModeAdapter,
|
|
6460
|
+
resolvePluginCommandAdapter,
|
|
6461
|
+
resolveProviderSetupSelection,
|
|
5793
6462
|
resolveSubagentLogDir,
|
|
6463
|
+
restoreCommandEditCheckpoint,
|
|
5794
6464
|
retrieveAgentToolDeps,
|
|
6465
|
+
rollbackCommandEditCheckpoint,
|
|
5795
6466
|
runHooks2 as runHooks,
|
|
6467
|
+
runProviderSetupPromptFlow,
|
|
5796
6468
|
selectRelevantTasks,
|
|
6469
|
+
setCommandAutoCompactThreshold,
|
|
6470
|
+
setCurrentProvider,
|
|
5797
6471
|
storeAgentToolDeps,
|
|
6472
|
+
submitProviderSetupValue,
|
|
5798
6473
|
substituteVariables,
|
|
5799
6474
|
summarizeBackgroundJobGroup,
|
|
6475
|
+
testProviderProfileCommand,
|
|
5800
6476
|
transitionBackgroundTaskStatus,
|
|
5801
6477
|
transitionSelfHostingLoop,
|
|
5802
6478
|
updateTaskFileStatus,
|
|
6479
|
+
upsertProviderProfile,
|
|
5803
6480
|
userPaths,
|
|
5804
|
-
|
|
6481
|
+
validateProviderProfile,
|
|
6482
|
+
validateProviderSetupValue,
|
|
6483
|
+
wrapEditCheckpointTools,
|
|
6484
|
+
wrapReversibleExecutionTools,
|
|
6485
|
+
writeAutoCompactThresholdSetting,
|
|
6486
|
+
writeCommandPermissionMode
|
|
5805
6487
|
};
|