@robota-sdk/agent-sdk 3.0.0-beta.61 → 3.0.0-beta.63
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/dist/node/index.cjs +1201 -336
- package/dist/node/index.d.cts +315 -20
- package/dist/node/index.d.ts +315 -20
- package/dist/node/index.js +1182 -338
- package/package.json +6 -5
package/dist/node/index.js
CHANGED
|
@@ -541,17 +541,17 @@ async function compactCommandContext(context, instructions) {
|
|
|
541
541
|
function listCommandContextReferences(context) {
|
|
542
542
|
return context.listContextReferences?.() ?? [];
|
|
543
543
|
}
|
|
544
|
-
async function addCommandContextReference(context,
|
|
544
|
+
async function addCommandContextReference(context, path3) {
|
|
545
545
|
if (!context.addContextReference) {
|
|
546
546
|
return {
|
|
547
547
|
evicted: [],
|
|
548
548
|
diagnostics: ["Command host does not support context reference additions."]
|
|
549
549
|
};
|
|
550
550
|
}
|
|
551
|
-
return context.addContextReference(
|
|
551
|
+
return context.addContextReference(path3);
|
|
552
552
|
}
|
|
553
|
-
function removeCommandContextReference(context,
|
|
554
|
-
return context.removeContextReference?.(
|
|
553
|
+
function removeCommandContextReference(context, path3) {
|
|
554
|
+
return context.removeContextReference?.(path3) ?? {};
|
|
555
555
|
}
|
|
556
556
|
function clearCommandContextReferences(context) {
|
|
557
557
|
return context.clearContextReferences?.() ?? { removed: [] };
|
|
@@ -1461,17 +1461,17 @@ var ProjectMemoryStore = class {
|
|
|
1461
1461
|
return join5(memoryRoot2(this.cwd), TOPICS_DIRNAME);
|
|
1462
1462
|
}
|
|
1463
1463
|
loadStartupMemory() {
|
|
1464
|
-
const
|
|
1465
|
-
if (!existsSync3(
|
|
1466
|
-
return { content: "", path, lineCount: 0, truncated: false };
|
|
1464
|
+
const path3 = this.getIndexPath();
|
|
1465
|
+
if (!existsSync3(path3)) {
|
|
1466
|
+
return { content: "", path: path3, lineCount: 0, truncated: false };
|
|
1467
1467
|
}
|
|
1468
|
-
const raw = readFileSync3(
|
|
1468
|
+
const raw = readFileSync3(path3, "utf8");
|
|
1469
1469
|
const byBytes = truncateToUtf8Bytes(raw, MEMORY_INDEX_MAX_BYTES);
|
|
1470
1470
|
const byteTruncated = Buffer.byteLength(raw, "utf8") > MEMORY_INDEX_MAX_BYTES;
|
|
1471
1471
|
const byLines = limitLines(byBytes, MEMORY_INDEX_MAX_LINES);
|
|
1472
1472
|
return {
|
|
1473
1473
|
content: byLines.content,
|
|
1474
|
-
path,
|
|
1474
|
+
path: path3,
|
|
1475
1475
|
lineCount: byLines.content.length === 0 ? 0 : byLines.content.split(/\r?\n/).length,
|
|
1476
1476
|
truncated: byteTruncated || byLines.truncated
|
|
1477
1477
|
};
|
|
@@ -1490,9 +1490,9 @@ var ProjectMemoryStore = class {
|
|
|
1490
1490
|
}
|
|
1491
1491
|
readTopic(topic) {
|
|
1492
1492
|
const normalized = sanitizeTopic(topic);
|
|
1493
|
-
const
|
|
1494
|
-
if (!existsSync3(
|
|
1495
|
-
return readFileSync3(
|
|
1493
|
+
const path3 = join5(this.getTopicsPath(), `${normalized}${TOPIC_EXTENSION}`);
|
|
1494
|
+
if (!existsSync3(path3)) return "";
|
|
1495
|
+
return readFileSync3(path3, "utf8").trimEnd();
|
|
1496
1496
|
}
|
|
1497
1497
|
append(input) {
|
|
1498
1498
|
const topic = sanitizeTopic(input.topic);
|
|
@@ -2050,6 +2050,516 @@ function createInProcessSubagentRunner(deps) {
|
|
|
2050
2050
|
};
|
|
2051
2051
|
}
|
|
2052
2052
|
|
|
2053
|
+
// src/background-tasks/index.ts
|
|
2054
|
+
import { BackgroundTaskManager } from "@robota-sdk/agent-runtime";
|
|
2055
|
+
|
|
2056
|
+
// src/background-tasks/background-job-orchestrator.ts
|
|
2057
|
+
import {
|
|
2058
|
+
isTerminalBackgroundTaskStatus
|
|
2059
|
+
} from "@robota-sdk/agent-runtime";
|
|
2060
|
+
var DEFAULT_SUMMARY_LENGTH = 1e3;
|
|
2061
|
+
var BackgroundJobOrchestrator = class {
|
|
2062
|
+
manager;
|
|
2063
|
+
now;
|
|
2064
|
+
idFactory;
|
|
2065
|
+
unsubscribeManager;
|
|
2066
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2067
|
+
groups = /* @__PURE__ */ new Map();
|
|
2068
|
+
sequence = 0;
|
|
2069
|
+
constructor(options) {
|
|
2070
|
+
this.manager = options.manager;
|
|
2071
|
+
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
2072
|
+
this.idFactory = options.idFactory ?? (() => this.nextGroupId());
|
|
2073
|
+
this.sequence = options.initialGroups?.length ?? 0;
|
|
2074
|
+
for (const group of options.initialGroups ?? []) this.restoreGroup(group);
|
|
2075
|
+
this.unsubscribeManager = this.manager.subscribe((event) => this.handleTaskEvent(event));
|
|
2076
|
+
}
|
|
2077
|
+
createGroup(request) {
|
|
2078
|
+
const now = this.now();
|
|
2079
|
+
const state = {
|
|
2080
|
+
id: this.idFactory(request),
|
|
2081
|
+
parentSessionId: request.parentSessionId,
|
|
2082
|
+
waitPolicy: request.waitPolicy,
|
|
2083
|
+
taskIds: [...request.taskIds],
|
|
2084
|
+
status: "running",
|
|
2085
|
+
createdAt: now,
|
|
2086
|
+
updatedAt: now,
|
|
2087
|
+
results: [],
|
|
2088
|
+
...request.label ? { label: request.label } : {}
|
|
2089
|
+
};
|
|
2090
|
+
const record = this.createRecord(state);
|
|
2091
|
+
this.groups.set(state.id, record);
|
|
2092
|
+
this.captureExistingTerminalTasks(record);
|
|
2093
|
+
this.emit({ type: "background_job_group_created", group: cloneGroup(record.state) });
|
|
2094
|
+
this.evaluateCompletion(record);
|
|
2095
|
+
return cloneGroup(record.state);
|
|
2096
|
+
}
|
|
2097
|
+
listGroups() {
|
|
2098
|
+
return [...this.groups.values()].map((record) => cloneGroup(record.state));
|
|
2099
|
+
}
|
|
2100
|
+
getGroup(groupId) {
|
|
2101
|
+
const record = this.groups.get(groupId);
|
|
2102
|
+
return record ? cloneGroup(record.state) : void 0;
|
|
2103
|
+
}
|
|
2104
|
+
waitGroup(groupId) {
|
|
2105
|
+
const record = this.groups.get(groupId);
|
|
2106
|
+
if (!record) return Promise.reject(new Error(`Unknown background job group: ${groupId}`));
|
|
2107
|
+
return record.completion;
|
|
2108
|
+
}
|
|
2109
|
+
subscribe(listener) {
|
|
2110
|
+
this.listeners.add(listener);
|
|
2111
|
+
return () => {
|
|
2112
|
+
this.listeners.delete(listener);
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
dispose() {
|
|
2116
|
+
this.unsubscribeManager();
|
|
2117
|
+
this.listeners.clear();
|
|
2118
|
+
}
|
|
2119
|
+
nextGroupId() {
|
|
2120
|
+
this.sequence += 1;
|
|
2121
|
+
return `group_${this.sequence}`;
|
|
2122
|
+
}
|
|
2123
|
+
restoreGroup(group) {
|
|
2124
|
+
const record = this.createRecord(cloneGroup(group));
|
|
2125
|
+
this.groups.set(group.id, record);
|
|
2126
|
+
if (group.status === "completed") record.resolve(cloneGroup(group));
|
|
2127
|
+
}
|
|
2128
|
+
createRecord(state) {
|
|
2129
|
+
let resolveGroup = () => {
|
|
2130
|
+
};
|
|
2131
|
+
const completion = new Promise((resolve6) => {
|
|
2132
|
+
resolveGroup = resolve6;
|
|
2133
|
+
});
|
|
2134
|
+
return { state, completion, resolve: resolveGroup };
|
|
2135
|
+
}
|
|
2136
|
+
captureExistingTerminalTasks(record) {
|
|
2137
|
+
for (const taskId of record.state.taskIds) {
|
|
2138
|
+
const task = this.manager.get(taskId);
|
|
2139
|
+
if (task && isTerminalBackgroundTaskStatus(task.status)) this.captureTask(record, task);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
handleTaskEvent(event) {
|
|
2143
|
+
const task = getTerminalTask(event);
|
|
2144
|
+
if (!task) return;
|
|
2145
|
+
for (const record of this.groups.values()) {
|
|
2146
|
+
if (!record.state.taskIds.includes(task.id)) continue;
|
|
2147
|
+
if (!this.captureTask(record, task)) continue;
|
|
2148
|
+
if (record.state.status === "running") this.evaluateCompletion(record);
|
|
2149
|
+
else this.emit({ type: "background_job_group_updated", group: cloneGroup(record.state) });
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
captureTask(record, task) {
|
|
2153
|
+
if (record.state.results.some((result) => result.taskId === task.id)) return false;
|
|
2154
|
+
record.state.results = [...record.state.results, createResultEnvelope(task)];
|
|
2155
|
+
record.state.updatedAt = this.now();
|
|
2156
|
+
return true;
|
|
2157
|
+
}
|
|
2158
|
+
evaluateCompletion(record) {
|
|
2159
|
+
if (record.state.status === "completed") return;
|
|
2160
|
+
if (!shouldComplete(record.state)) {
|
|
2161
|
+
this.emit({ type: "background_job_group_updated", group: cloneGroup(record.state) });
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
const now = this.now();
|
|
2165
|
+
record.state.status = "completed";
|
|
2166
|
+
record.state.completedAt = now;
|
|
2167
|
+
record.state.updatedAt = now;
|
|
2168
|
+
const completed = cloneGroup(record.state);
|
|
2169
|
+
record.resolve(completed);
|
|
2170
|
+
this.emit({ type: "background_job_group_completed", group: completed });
|
|
2171
|
+
}
|
|
2172
|
+
emit(event) {
|
|
2173
|
+
for (const listener of this.listeners) listener(event);
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2176
|
+
function getTerminalTask(event) {
|
|
2177
|
+
if (event.type === "background_task_completed" || event.type === "background_task_failed" || event.type === "background_task_cancelled") {
|
|
2178
|
+
return event.task;
|
|
2179
|
+
}
|
|
2180
|
+
return void 0;
|
|
2181
|
+
}
|
|
2182
|
+
function shouldComplete(group) {
|
|
2183
|
+
if (group.waitPolicy === "manual") return false;
|
|
2184
|
+
if (group.waitPolicy === "wait_any") return group.results.length > 0;
|
|
2185
|
+
return group.taskIds.every((taskId) => group.results.some((result) => result.taskId === taskId));
|
|
2186
|
+
}
|
|
2187
|
+
function createResultEnvelope(task) {
|
|
2188
|
+
return {
|
|
2189
|
+
taskId: task.id,
|
|
2190
|
+
label: task.label,
|
|
2191
|
+
status: task.status,
|
|
2192
|
+
...task.result?.output ? { summary: summarizeOutput(task.result.output) } : {},
|
|
2193
|
+
...task.transcriptPath || task.logPath ? { outputRef: task.transcriptPath ?? task.logPath } : {},
|
|
2194
|
+
...task.error ? { error: { ...task.error } } : {},
|
|
2195
|
+
...task.startedAt ? { startedAt: task.startedAt } : {},
|
|
2196
|
+
...task.completedAt ? { completedAt: task.completedAt } : {}
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
function summarizeOutput(output) {
|
|
2200
|
+
const trimmed = output.trim();
|
|
2201
|
+
if (trimmed.length <= DEFAULT_SUMMARY_LENGTH) return trimmed;
|
|
2202
|
+
return `${trimmed.slice(0, DEFAULT_SUMMARY_LENGTH)}...`;
|
|
2203
|
+
}
|
|
2204
|
+
function summarizeBackgroundJobGroup(group) {
|
|
2205
|
+
const completed = countResults(group, "completed");
|
|
2206
|
+
const failed = countResults(group, "failed");
|
|
2207
|
+
const cancelled = countResults(group, "cancelled");
|
|
2208
|
+
return {
|
|
2209
|
+
groupId: group.id,
|
|
2210
|
+
status: group.status,
|
|
2211
|
+
total: group.taskIds.length,
|
|
2212
|
+
completed,
|
|
2213
|
+
failed,
|
|
2214
|
+
cancelled,
|
|
2215
|
+
pending: Math.max(group.taskIds.length - group.results.length, 0),
|
|
2216
|
+
lines: group.results.map((result) => formatResultLine(result))
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
function countResults(group, status) {
|
|
2220
|
+
return group.results.filter((result) => result.status === status).length;
|
|
2221
|
+
}
|
|
2222
|
+
function formatResultLine(result) {
|
|
2223
|
+
const detail = normalizeResultDetail(result);
|
|
2224
|
+
const output = result.outputRef && result.summary ? ` (output: ${result.outputRef})` : "";
|
|
2225
|
+
return `[${result.status}] ${result.label} ${result.taskId}: ${detail}${output}`;
|
|
2226
|
+
}
|
|
2227
|
+
function normalizeResultDetail(result) {
|
|
2228
|
+
const detail = result.error?.message ?? result.summary ?? "";
|
|
2229
|
+
const normalized = detail.replace(/\s+/g, " ").trim();
|
|
2230
|
+
return normalized.length > 0 ? normalized : "(no summary)";
|
|
2231
|
+
}
|
|
2232
|
+
function cloneGroup(group) {
|
|
2233
|
+
return {
|
|
2234
|
+
...group,
|
|
2235
|
+
taskIds: [...group.taskIds],
|
|
2236
|
+
results: group.results.map((result) => ({
|
|
2237
|
+
...result,
|
|
2238
|
+
...result.error ? { error: { ...result.error } } : {}
|
|
2239
|
+
}))
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
// src/background-tasks/execution-workspace-projection.ts
|
|
2244
|
+
import { isTerminalBackgroundTaskStatus as isTerminalBackgroundTaskStatus2 } from "@robota-sdk/agent-runtime";
|
|
2245
|
+
|
|
2246
|
+
// src/background-tasks/execution-workspace-types.ts
|
|
2247
|
+
var MAIN_THREAD_ENTRY_PREFIX = "main";
|
|
2248
|
+
var BACKGROUND_TASK_ENTRY_PREFIX = "task";
|
|
2249
|
+
var BACKGROUND_GROUP_ENTRY_PREFIX = "group";
|
|
2250
|
+
var ENTRY_ID_SEPARATOR = ":";
|
|
2251
|
+
var EXECUTION_ORIGIN_METADATA_KEYS = {
|
|
2252
|
+
kind: "executionOriginKind",
|
|
2253
|
+
sessionId: "executionOriginSessionId",
|
|
2254
|
+
turnId: "executionOriginTurnId",
|
|
2255
|
+
commandName: "executionOriginCommandName",
|
|
2256
|
+
toolCallId: "executionOriginToolCallId",
|
|
2257
|
+
skillId: "executionOriginSkillId",
|
|
2258
|
+
label: "executionOriginLabel"
|
|
2259
|
+
};
|
|
2260
|
+
function createMainThreadExecutionEntryId(sessionId) {
|
|
2261
|
+
return [MAIN_THREAD_ENTRY_PREFIX, sessionId].join(ENTRY_ID_SEPARATOR);
|
|
2262
|
+
}
|
|
2263
|
+
function createBackgroundTaskExecutionEntryId(taskId) {
|
|
2264
|
+
return [BACKGROUND_TASK_ENTRY_PREFIX, taskId].join(ENTRY_ID_SEPARATOR);
|
|
2265
|
+
}
|
|
2266
|
+
function createBackgroundGroupExecutionEntryId(groupId) {
|
|
2267
|
+
return [BACKGROUND_GROUP_ENTRY_PREFIX, groupId].join(ENTRY_ID_SEPARATOR);
|
|
2268
|
+
}
|
|
2269
|
+
function parseExecutionWorkspaceEntryId(entryId) {
|
|
2270
|
+
const [prefix, sourceId] = entryId.split(ENTRY_ID_SEPARATOR, 2);
|
|
2271
|
+
if (!sourceId) return void 0;
|
|
2272
|
+
if (prefix === MAIN_THREAD_ENTRY_PREFIX) return { kind: "main_thread", sourceId };
|
|
2273
|
+
if (prefix === BACKGROUND_TASK_ENTRY_PREFIX) return { kind: "background_task", sourceId };
|
|
2274
|
+
if (prefix === BACKGROUND_GROUP_ENTRY_PREFIX) return { kind: "background_group", sourceId };
|
|
2275
|
+
return void 0;
|
|
2276
|
+
}
|
|
2277
|
+
function createExecutionOriginMetadata(origin) {
|
|
2278
|
+
return {
|
|
2279
|
+
[EXECUTION_ORIGIN_METADATA_KEYS.kind]: origin.kind,
|
|
2280
|
+
[EXECUTION_ORIGIN_METADATA_KEYS.sessionId]: origin.sessionId,
|
|
2281
|
+
...origin.turnId ? { [EXECUTION_ORIGIN_METADATA_KEYS.turnId]: origin.turnId } : {},
|
|
2282
|
+
...origin.commandName ? { [EXECUTION_ORIGIN_METADATA_KEYS.commandName]: origin.commandName } : {},
|
|
2283
|
+
...origin.toolCallId ? { [EXECUTION_ORIGIN_METADATA_KEYS.toolCallId]: origin.toolCallId } : {},
|
|
2284
|
+
...origin.skillId ? { [EXECUTION_ORIGIN_METADATA_KEYS.skillId]: origin.skillId } : {},
|
|
2285
|
+
...origin.label ? { [EXECUTION_ORIGIN_METADATA_KEYS.label]: origin.label } : {}
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
// src/background-tasks/execution-workspace-projection.ts
|
|
2290
|
+
var PREVIEW_MAX_LENGTH = 120;
|
|
2291
|
+
var SUCCESS_EXIT_CODE = 0;
|
|
2292
|
+
function createExecutionWorkspaceSnapshot(input) {
|
|
2293
|
+
const taskGroupIds = createTaskGroupIdMap(input.groups);
|
|
2294
|
+
const entries = [
|
|
2295
|
+
createMainThreadEntry(input.mainThread),
|
|
2296
|
+
...sortGroups(input.groups).map((group) => createBackgroundGroupEntry(group)),
|
|
2297
|
+
...sortTasks(input.tasks).map(
|
|
2298
|
+
(task) => createBackgroundTaskEntry(task, taskGroupIds.get(task.id))
|
|
2299
|
+
)
|
|
2300
|
+
].filter((entry) => matchesExecutionWorkspaceFilter(entry, input.filter));
|
|
2301
|
+
return {
|
|
2302
|
+
sessionId: input.sessionId,
|
|
2303
|
+
selectedEntryId: input.selectedEntryId ?? entries.find((entry) => entry.kind === "main_thread")?.id ?? createMainThreadExecutionEntryId(input.sessionId),
|
|
2304
|
+
updatedAt: entries[0]?.updatedAt ?? input.mainThread.updatedAt,
|
|
2305
|
+
entries
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
function createMainThreadEntry(input) {
|
|
2309
|
+
return {
|
|
2310
|
+
id: createMainThreadExecutionEntryId(input.sessionId),
|
|
2311
|
+
sourceId: input.sessionId,
|
|
2312
|
+
kind: "main_thread",
|
|
2313
|
+
origin: { kind: "user_prompt", sessionId: input.sessionId },
|
|
2314
|
+
status: input.isExecuting ? "active" : "idle",
|
|
2315
|
+
title: "Main thread",
|
|
2316
|
+
subtitle: input.hasPendingPrompt ? "prompt queued" : `${input.historyLength} history entries`,
|
|
2317
|
+
preview: trimPreview(input.preview),
|
|
2318
|
+
unread: false,
|
|
2319
|
+
attention: "none",
|
|
2320
|
+
visibility: "default",
|
|
2321
|
+
updatedAt: input.updatedAt,
|
|
2322
|
+
controls: ["select"]
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
function createBackgroundTaskEntry(state, groupId) {
|
|
2326
|
+
return {
|
|
2327
|
+
id: createBackgroundTaskExecutionEntryId(state.id),
|
|
2328
|
+
sourceId: state.id,
|
|
2329
|
+
kind: "background_task",
|
|
2330
|
+
parentId: state.parentTaskId ? createBackgroundTaskExecutionEntryId(state.parentTaskId) : createMainThreadExecutionEntryId(state.parentSessionId),
|
|
2331
|
+
...groupId ? { groupId: createBackgroundGroupExecutionEntryId(groupId) } : {},
|
|
2332
|
+
origin: readExecutionOrigin(state.metadata, {
|
|
2333
|
+
kind: "system",
|
|
2334
|
+
sessionId: state.parentSessionId
|
|
2335
|
+
}),
|
|
2336
|
+
taskKind: state.kind,
|
|
2337
|
+
status: state.status,
|
|
2338
|
+
title: state.label,
|
|
2339
|
+
subtitle: createTaskSubtitle(state),
|
|
2340
|
+
preview: createTaskPreview(state),
|
|
2341
|
+
currentAction: state.currentAction,
|
|
2342
|
+
unread: state.unread,
|
|
2343
|
+
attention: createTaskAttention(state),
|
|
2344
|
+
visibility: createTaskVisibility(state),
|
|
2345
|
+
updatedAt: state.lastActivityAt ?? state.updatedAt,
|
|
2346
|
+
controls: createTaskControls(state)
|
|
2347
|
+
};
|
|
2348
|
+
}
|
|
2349
|
+
function createBackgroundGroupEntry(group) {
|
|
2350
|
+
const preview = trimPreview(
|
|
2351
|
+
group.results.map((result) => result.summary ?? result.error?.message).join(" ")
|
|
2352
|
+
);
|
|
2353
|
+
return {
|
|
2354
|
+
id: createBackgroundGroupExecutionEntryId(group.id),
|
|
2355
|
+
sourceId: group.id,
|
|
2356
|
+
kind: "background_group",
|
|
2357
|
+
parentId: createMainThreadExecutionEntryId(group.parentSessionId),
|
|
2358
|
+
origin: { kind: "system", sessionId: group.parentSessionId, label: group.label },
|
|
2359
|
+
status: group.status,
|
|
2360
|
+
title: group.label ?? group.id,
|
|
2361
|
+
subtitle: `${group.results.length}/${group.taskIds.length} tasks`,
|
|
2362
|
+
preview,
|
|
2363
|
+
unread: false,
|
|
2364
|
+
attention: createGroupAttention(group),
|
|
2365
|
+
visibility: group.status === "completed" ? "collapsed" : "default",
|
|
2366
|
+
updatedAt: group.updatedAt,
|
|
2367
|
+
controls: group.status === "running" ? ["select", "wait"] : ["select"]
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
function readExecutionOrigin(metadata, fallback) {
|
|
2371
|
+
const kind = toExecutionOriginKind(metadata?.[EXECUTION_ORIGIN_METADATA_KEYS.kind]);
|
|
2372
|
+
const sessionId = toStringValue(metadata?.[EXECUTION_ORIGIN_METADATA_KEYS.sessionId]);
|
|
2373
|
+
return {
|
|
2374
|
+
kind: kind ?? fallback.kind,
|
|
2375
|
+
sessionId: sessionId ?? fallback.sessionId,
|
|
2376
|
+
turnId: toStringValue(metadata?.[EXECUTION_ORIGIN_METADATA_KEYS.turnId]) ?? fallback.turnId,
|
|
2377
|
+
commandName: toStringValue(metadata?.[EXECUTION_ORIGIN_METADATA_KEYS.commandName]) ?? fallback.commandName,
|
|
2378
|
+
toolCallId: toStringValue(metadata?.[EXECUTION_ORIGIN_METADATA_KEYS.toolCallId]) ?? fallback.toolCallId,
|
|
2379
|
+
skillId: toStringValue(metadata?.[EXECUTION_ORIGIN_METADATA_KEYS.skillId]) ?? fallback.skillId,
|
|
2380
|
+
label: toStringValue(metadata?.[EXECUTION_ORIGIN_METADATA_KEYS.label]) ?? fallback.label
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
function createTaskGroupIdMap(groups) {
|
|
2384
|
+
return new Map(groups.flatMap((group) => group.taskIds.map((taskId) => [taskId, group.id])));
|
|
2385
|
+
}
|
|
2386
|
+
function createTaskControls(state) {
|
|
2387
|
+
const controls = ["select"];
|
|
2388
|
+
if (isTerminalBackgroundTaskStatus2(state.status)) controls.push("close");
|
|
2389
|
+
else controls.push("cancel");
|
|
2390
|
+
if (state.logPath || state.transcriptPath) controls.push("read_log");
|
|
2391
|
+
return controls;
|
|
2392
|
+
}
|
|
2393
|
+
function createTaskSubtitle(state) {
|
|
2394
|
+
if (state.kind === "agent") return state.agentType ?? state.cwd;
|
|
2395
|
+
return state.cwd;
|
|
2396
|
+
}
|
|
2397
|
+
function createTaskPreview(state) {
|
|
2398
|
+
if (state.status === "failed") return trimPreview(state.error?.message);
|
|
2399
|
+
if (state.status === "completed") return trimPreview(state.result?.output);
|
|
2400
|
+
return trimPreview(state.promptPreview ?? state.commandPreview);
|
|
2401
|
+
}
|
|
2402
|
+
function createTaskAttention(state) {
|
|
2403
|
+
if (state.status === "failed") return "failed";
|
|
2404
|
+
if (state.status === "waiting_permission") return "permission";
|
|
2405
|
+
if (state.unread) return "unread";
|
|
2406
|
+
if (state.status === "completed") return "completed";
|
|
2407
|
+
return "none";
|
|
2408
|
+
}
|
|
2409
|
+
function createTaskVisibility(state) {
|
|
2410
|
+
if (state.status === "completed" && !state.unread && !state.error && (state.result?.exitCode ?? SUCCESS_EXIT_CODE) === SUCCESS_EXIT_CODE && !state.result?.signalCode && !state.worktreePath && !state.branchName) {
|
|
2411
|
+
return "collapsed";
|
|
2412
|
+
}
|
|
2413
|
+
return "default";
|
|
2414
|
+
}
|
|
2415
|
+
function createGroupAttention(group) {
|
|
2416
|
+
if (group.results.some((result) => result.status === "failed")) return "failed";
|
|
2417
|
+
if (group.status === "completed") return "completed";
|
|
2418
|
+
return "none";
|
|
2419
|
+
}
|
|
2420
|
+
function matchesExecutionWorkspaceFilter(entry, filter) {
|
|
2421
|
+
if (!filter) return true;
|
|
2422
|
+
if (filter.includeMainThread === false && entry.kind === "main_thread") return false;
|
|
2423
|
+
if (filter.kinds && !filter.kinds.includes(entry.kind)) return false;
|
|
2424
|
+
if (filter.visibility && !filter.visibility.includes(entry.visibility)) return false;
|
|
2425
|
+
return true;
|
|
2426
|
+
}
|
|
2427
|
+
function sortTasks(tasks) {
|
|
2428
|
+
return [...tasks].sort(
|
|
2429
|
+
(left, right) => (right.lastActivityAt ?? right.updatedAt).localeCompare(left.lastActivityAt ?? left.updatedAt)
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
function sortGroups(groups) {
|
|
2433
|
+
return [...groups].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
2434
|
+
}
|
|
2435
|
+
function trimPreview(value) {
|
|
2436
|
+
const normalized = value?.trim().replace(/\s+/g, " ");
|
|
2437
|
+
if (!normalized) return void 0;
|
|
2438
|
+
return normalized.length > PREVIEW_MAX_LENGTH ? `${normalized.slice(0, PREVIEW_MAX_LENGTH)}...` : normalized;
|
|
2439
|
+
}
|
|
2440
|
+
function toStringValue(value) {
|
|
2441
|
+
return typeof value === "string" ? value : void 0;
|
|
2442
|
+
}
|
|
2443
|
+
function toExecutionOriginKind(value) {
|
|
2444
|
+
if (value === "user_prompt" || value === "slash_command" || value === "model_command" || value === "tool_call" || value === "skill" || value === "transport" || value === "system") {
|
|
2445
|
+
return value;
|
|
2446
|
+
}
|
|
2447
|
+
return void 0;
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
// src/background-tasks/execution-workspace-detail.ts
|
|
2451
|
+
var EXECUTION_DETAIL_PAGE_SIZE = 80;
|
|
2452
|
+
function createMainThreadDetailPage(input) {
|
|
2453
|
+
const offset = normalizeOffset(input.cursor?.offset);
|
|
2454
|
+
const page = input.history.slice(offset, offset + EXECUTION_DETAIL_PAGE_SIZE);
|
|
2455
|
+
const records = page.map((entry) => ({
|
|
2456
|
+
id: entry.id,
|
|
2457
|
+
kind: entry.category === "chat" ? "message" : "progress",
|
|
2458
|
+
text: formatHistoryEntry(entry),
|
|
2459
|
+
timestamp: entry.timestamp.toISOString(),
|
|
2460
|
+
sourceId: entry.type
|
|
2461
|
+
}));
|
|
2462
|
+
return {
|
|
2463
|
+
entryId: input.entryId,
|
|
2464
|
+
...input.cursor ? { cursor: input.cursor } : {},
|
|
2465
|
+
...offset + page.length < input.history.length ? { nextCursor: { offset: offset + page.length } } : {},
|
|
2466
|
+
records
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
function createLineDetailPage(input) {
|
|
2470
|
+
const offset = input.cursor?.offset ?? 0;
|
|
2471
|
+
const records = input.lines.map((line, index) => ({
|
|
2472
|
+
id: `${input.entryId}:${offset}:${index}`,
|
|
2473
|
+
kind: input.kind ?? "process_output",
|
|
2474
|
+
text: line
|
|
2475
|
+
}));
|
|
2476
|
+
return {
|
|
2477
|
+
entryId: input.entryId,
|
|
2478
|
+
...input.cursor ? { cursor: input.cursor } : {},
|
|
2479
|
+
...input.nextCursor ? { nextCursor: input.nextCursor } : {},
|
|
2480
|
+
records
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
function normalizeOffset(offset) {
|
|
2484
|
+
return typeof offset === "number" && Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;
|
|
2485
|
+
}
|
|
2486
|
+
function formatHistoryEntry(entry) {
|
|
2487
|
+
if (typeof entry.data === "string") return entry.data;
|
|
2488
|
+
return entry.type;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
// src/background-tasks/execution-workspace-spawner.ts
|
|
2492
|
+
function createExecutionWorkspaceTaskSpawner(options) {
|
|
2493
|
+
return {
|
|
2494
|
+
spawnAgent: (request) => options.manager.spawn(createAgentRequest(options, request)),
|
|
2495
|
+
spawnProcess: (request) => options.manager.spawn(createProcessRequest(options, request)),
|
|
2496
|
+
createGroup: (request) => options.groupOrchestrator.createGroup({
|
|
2497
|
+
parentSessionId: options.sessionId,
|
|
2498
|
+
waitPolicy: request.waitPolicy,
|
|
2499
|
+
taskIds: [...request.taskIds],
|
|
2500
|
+
label: request.label
|
|
2501
|
+
})
|
|
2502
|
+
};
|
|
2503
|
+
}
|
|
2504
|
+
function createAgentRequest(options, request) {
|
|
2505
|
+
return {
|
|
2506
|
+
kind: "agent",
|
|
2507
|
+
label: request.label,
|
|
2508
|
+
mode: request.mode ?? "background",
|
|
2509
|
+
parentSessionId: options.sessionId,
|
|
2510
|
+
parentTaskId: request.parentTaskId,
|
|
2511
|
+
depth: request.depth ?? 1,
|
|
2512
|
+
cwd: request.cwd ?? options.cwd,
|
|
2513
|
+
agentType: request.agentType,
|
|
2514
|
+
prompt: request.prompt,
|
|
2515
|
+
model: request.model,
|
|
2516
|
+
isolation: request.isolation,
|
|
2517
|
+
allowedTools: request.allowedTools ? [...request.allowedTools] : void 0,
|
|
2518
|
+
disallowedTools: request.disallowedTools ? [...request.disallowedTools] : void 0,
|
|
2519
|
+
permissionPolicy: request.permissionPolicy ?? "inherit-allowlist",
|
|
2520
|
+
timeoutMs: request.timeoutMs,
|
|
2521
|
+
idleTimeoutMs: request.idleTimeoutMs,
|
|
2522
|
+
maxRuntimeMs: request.maxRuntimeMs,
|
|
2523
|
+
outputLimitBytes: request.outputLimitBytes,
|
|
2524
|
+
maxTextDeltas: request.maxTextDeltas,
|
|
2525
|
+
repetitionWindow: request.repetitionWindow,
|
|
2526
|
+
repetitionThreshold: request.repetitionThreshold,
|
|
2527
|
+
metadata: createExecutionOriginMetadata(options.origin)
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
function createProcessRequest(options, request) {
|
|
2531
|
+
return {
|
|
2532
|
+
kind: "process",
|
|
2533
|
+
label: request.label ?? request.command,
|
|
2534
|
+
mode: request.mode ?? "background",
|
|
2535
|
+
parentSessionId: options.sessionId,
|
|
2536
|
+
parentTaskId: request.parentTaskId,
|
|
2537
|
+
depth: request.depth ?? 0,
|
|
2538
|
+
cwd: request.cwd ?? options.cwd,
|
|
2539
|
+
command: request.command,
|
|
2540
|
+
shell: request.shell,
|
|
2541
|
+
env: request.env,
|
|
2542
|
+
stdin: request.stdin,
|
|
2543
|
+
timeoutMs: request.timeoutMs,
|
|
2544
|
+
idleTimeoutMs: request.idleTimeoutMs,
|
|
2545
|
+
maxRuntimeMs: request.maxRuntimeMs,
|
|
2546
|
+
outputLimitBytes: request.outputLimitBytes,
|
|
2547
|
+
metadata: createExecutionOriginMetadata(options.origin)
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
// src/background-tasks/index.ts
|
|
2552
|
+
import {
|
|
2553
|
+
getBackgroundTaskTransitions,
|
|
2554
|
+
isTerminalBackgroundTaskStatus as isTerminalBackgroundTaskStatus3,
|
|
2555
|
+
transitionBackgroundTaskStatus,
|
|
2556
|
+
appendPrefixedLogLines,
|
|
2557
|
+
createBackgroundTaskLogPage,
|
|
2558
|
+
createLimitedOutputCapture,
|
|
2559
|
+
DEFAULT_BACKGROUND_TASK_LOG_PAGE_SIZE
|
|
2560
|
+
} from "@robota-sdk/agent-runtime";
|
|
2561
|
+
import { BackgroundTaskError } from "@robota-sdk/agent-runtime";
|
|
2562
|
+
|
|
2053
2563
|
// src/tools/agent-tool-batch.ts
|
|
2054
2564
|
function stringifyAgentBatchResult(input) {
|
|
2055
2565
|
const successfulJobs = input.jobs.filter((job) => job.success);
|
|
@@ -2244,7 +2754,12 @@ function createSpawnRequest(args, agentType, agentDef, deps, label = agentDef.na
|
|
|
2244
2754
|
cwd: deps.cwd ?? process.cwd(),
|
|
2245
2755
|
prompt: args.prompt,
|
|
2246
2756
|
model: args.model,
|
|
2247
|
-
isolation: args.isolation
|
|
2757
|
+
isolation: args.isolation,
|
|
2758
|
+
metadata: createExecutionOriginMetadata({
|
|
2759
|
+
kind: "tool_call",
|
|
2760
|
+
sessionId: deps.parentSessionId ?? "unknown-session",
|
|
2761
|
+
label
|
|
2762
|
+
})
|
|
2248
2763
|
};
|
|
2249
2764
|
}
|
|
2250
2765
|
function stringifyUnknownAgentType(agentType) {
|
|
@@ -2311,260 +2826,58 @@ function stringifyAgentError(message, agentId) {
|
|
|
2311
2826
|
}
|
|
2312
2827
|
});
|
|
2313
2828
|
}
|
|
2314
|
-
async function runManagedAgent(args, deps, manager) {
|
|
2315
|
-
if (typeof args.prompt !== "string" || args.prompt.length === 0) {
|
|
2316
|
-
return stringifyAgentError("prompt is required when jobs is omitted");
|
|
2317
|
-
}
|
|
2318
|
-
const singleArgs = { ...args, prompt: args.prompt };
|
|
2319
|
-
const agentType = args.subagent_type ?? "general-purpose";
|
|
2320
|
-
const agentDef = resolveAgentDefinition2(agentType, deps.customAgentRegistry);
|
|
2321
|
-
if (!agentDef) {
|
|
2322
|
-
return stringifyUnknownAgentType(agentType);
|
|
2323
|
-
}
|
|
2324
|
-
let agentId;
|
|
2325
|
-
try {
|
|
2326
|
-
const state = await manager.spawn(createSpawnRequest(singleArgs, agentType, agentDef, deps));
|
|
2327
|
-
agentId = state.id;
|
|
2328
|
-
const response = await manager.wait(state.id);
|
|
2329
|
-
return stringifyAgentSuccess(response);
|
|
2330
|
-
} catch (err) {
|
|
2331
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2332
|
-
return stringifyAgentError(message, agentId);
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
function createAgentTool(deps) {
|
|
2336
|
-
const manager = createSubagentManager(deps);
|
|
2337
|
-
return createZodFunctionTool2(
|
|
2338
|
-
"Agent",
|
|
2339
|
-
AGENT_TOOL_DESCRIPTION,
|
|
2340
|
-
asZodSchema2(AgentSchema),
|
|
2341
|
-
async (params) => {
|
|
2342
|
-
const args = params;
|
|
2343
|
-
if (Array.isArray(args.jobs) && args.jobs.length > 0) {
|
|
2344
|
-
return runManagedAgentBatch({
|
|
2345
|
-
jobs: args.jobs,
|
|
2346
|
-
deps,
|
|
2347
|
-
manager,
|
|
2348
|
-
resolveAgentDefinition: resolveAgentDefinition2,
|
|
2349
|
-
createSpawnRequest
|
|
2350
|
-
});
|
|
2351
|
-
}
|
|
2352
|
-
return runManagedAgent(
|
|
2353
|
-
{
|
|
2354
|
-
prompt: args.prompt,
|
|
2355
|
-
...args.subagent_type !== void 0 ? { subagent_type: args.subagent_type } : {},
|
|
2356
|
-
...args.model !== void 0 ? { model: args.model } : {},
|
|
2357
|
-
...args.isolation !== void 0 ? { isolation: args.isolation } : {}
|
|
2358
|
-
},
|
|
2359
|
-
deps,
|
|
2360
|
-
manager
|
|
2361
|
-
);
|
|
2362
|
-
}
|
|
2363
|
-
);
|
|
2364
|
-
}
|
|
2365
|
-
|
|
2366
|
-
// src/background-tasks/index.ts
|
|
2367
|
-
import { BackgroundTaskManager } from "@robota-sdk/agent-runtime";
|
|
2368
|
-
|
|
2369
|
-
// src/background-tasks/background-job-orchestrator.ts
|
|
2370
|
-
import {
|
|
2371
|
-
isTerminalBackgroundTaskStatus
|
|
2372
|
-
} from "@robota-sdk/agent-runtime";
|
|
2373
|
-
var DEFAULT_SUMMARY_LENGTH = 1e3;
|
|
2374
|
-
var BackgroundJobOrchestrator = class {
|
|
2375
|
-
manager;
|
|
2376
|
-
now;
|
|
2377
|
-
idFactory;
|
|
2378
|
-
unsubscribeManager;
|
|
2379
|
-
listeners = /* @__PURE__ */ new Set();
|
|
2380
|
-
groups = /* @__PURE__ */ new Map();
|
|
2381
|
-
sequence = 0;
|
|
2382
|
-
constructor(options) {
|
|
2383
|
-
this.manager = options.manager;
|
|
2384
|
-
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
2385
|
-
this.idFactory = options.idFactory ?? (() => this.nextGroupId());
|
|
2386
|
-
this.sequence = options.initialGroups?.length ?? 0;
|
|
2387
|
-
for (const group of options.initialGroups ?? []) this.restoreGroup(group);
|
|
2388
|
-
this.unsubscribeManager = this.manager.subscribe((event) => this.handleTaskEvent(event));
|
|
2389
|
-
}
|
|
2390
|
-
createGroup(request) {
|
|
2391
|
-
const now = this.now();
|
|
2392
|
-
const state = {
|
|
2393
|
-
id: this.idFactory(request),
|
|
2394
|
-
parentSessionId: request.parentSessionId,
|
|
2395
|
-
waitPolicy: request.waitPolicy,
|
|
2396
|
-
taskIds: [...request.taskIds],
|
|
2397
|
-
status: "running",
|
|
2398
|
-
createdAt: now,
|
|
2399
|
-
updatedAt: now,
|
|
2400
|
-
results: [],
|
|
2401
|
-
...request.label ? { label: request.label } : {}
|
|
2402
|
-
};
|
|
2403
|
-
const record = this.createRecord(state);
|
|
2404
|
-
this.groups.set(state.id, record);
|
|
2405
|
-
this.captureExistingTerminalTasks(record);
|
|
2406
|
-
this.emit({ type: "background_job_group_created", group: cloneGroup(record.state) });
|
|
2407
|
-
this.evaluateCompletion(record);
|
|
2408
|
-
return cloneGroup(record.state);
|
|
2409
|
-
}
|
|
2410
|
-
listGroups() {
|
|
2411
|
-
return [...this.groups.values()].map((record) => cloneGroup(record.state));
|
|
2412
|
-
}
|
|
2413
|
-
getGroup(groupId) {
|
|
2414
|
-
const record = this.groups.get(groupId);
|
|
2415
|
-
return record ? cloneGroup(record.state) : void 0;
|
|
2416
|
-
}
|
|
2417
|
-
waitGroup(groupId) {
|
|
2418
|
-
const record = this.groups.get(groupId);
|
|
2419
|
-
if (!record) return Promise.reject(new Error(`Unknown background job group: ${groupId}`));
|
|
2420
|
-
return record.completion;
|
|
2421
|
-
}
|
|
2422
|
-
subscribe(listener) {
|
|
2423
|
-
this.listeners.add(listener);
|
|
2424
|
-
return () => {
|
|
2425
|
-
this.listeners.delete(listener);
|
|
2426
|
-
};
|
|
2427
|
-
}
|
|
2428
|
-
dispose() {
|
|
2429
|
-
this.unsubscribeManager();
|
|
2430
|
-
this.listeners.clear();
|
|
2431
|
-
}
|
|
2432
|
-
nextGroupId() {
|
|
2433
|
-
this.sequence += 1;
|
|
2434
|
-
return `group_${this.sequence}`;
|
|
2435
|
-
}
|
|
2436
|
-
restoreGroup(group) {
|
|
2437
|
-
const record = this.createRecord(cloneGroup(group));
|
|
2438
|
-
this.groups.set(group.id, record);
|
|
2439
|
-
if (group.status === "completed") record.resolve(cloneGroup(group));
|
|
2440
|
-
}
|
|
2441
|
-
createRecord(state) {
|
|
2442
|
-
let resolveGroup = () => {
|
|
2443
|
-
};
|
|
2444
|
-
const completion = new Promise((resolve6) => {
|
|
2445
|
-
resolveGroup = resolve6;
|
|
2446
|
-
});
|
|
2447
|
-
return { state, completion, resolve: resolveGroup };
|
|
2448
|
-
}
|
|
2449
|
-
captureExistingTerminalTasks(record) {
|
|
2450
|
-
for (const taskId of record.state.taskIds) {
|
|
2451
|
-
const task = this.manager.get(taskId);
|
|
2452
|
-
if (task && isTerminalBackgroundTaskStatus(task.status)) this.captureTask(record, task);
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
handleTaskEvent(event) {
|
|
2456
|
-
const task = getTerminalTask(event);
|
|
2457
|
-
if (!task) return;
|
|
2458
|
-
for (const record of this.groups.values()) {
|
|
2459
|
-
if (!record.state.taskIds.includes(task.id)) continue;
|
|
2460
|
-
if (!this.captureTask(record, task)) continue;
|
|
2461
|
-
if (record.state.status === "running") this.evaluateCompletion(record);
|
|
2462
|
-
else this.emit({ type: "background_job_group_updated", group: cloneGroup(record.state) });
|
|
2463
|
-
}
|
|
2464
|
-
}
|
|
2465
|
-
captureTask(record, task) {
|
|
2466
|
-
if (record.state.results.some((result) => result.taskId === task.id)) return false;
|
|
2467
|
-
record.state.results = [...record.state.results, createResultEnvelope(task)];
|
|
2468
|
-
record.state.updatedAt = this.now();
|
|
2469
|
-
return true;
|
|
2470
|
-
}
|
|
2471
|
-
evaluateCompletion(record) {
|
|
2472
|
-
if (record.state.status === "completed") return;
|
|
2473
|
-
if (!shouldComplete(record.state)) {
|
|
2474
|
-
this.emit({ type: "background_job_group_updated", group: cloneGroup(record.state) });
|
|
2475
|
-
return;
|
|
2476
|
-
}
|
|
2477
|
-
const now = this.now();
|
|
2478
|
-
record.state.status = "completed";
|
|
2479
|
-
record.state.completedAt = now;
|
|
2480
|
-
record.state.updatedAt = now;
|
|
2481
|
-
const completed = cloneGroup(record.state);
|
|
2482
|
-
record.resolve(completed);
|
|
2483
|
-
this.emit({ type: "background_job_group_completed", group: completed });
|
|
2484
|
-
}
|
|
2485
|
-
emit(event) {
|
|
2486
|
-
for (const listener of this.listeners) listener(event);
|
|
2487
|
-
}
|
|
2488
|
-
};
|
|
2489
|
-
function getTerminalTask(event) {
|
|
2490
|
-
if (event.type === "background_task_completed" || event.type === "background_task_failed" || event.type === "background_task_cancelled") {
|
|
2491
|
-
return event.task;
|
|
2492
|
-
}
|
|
2493
|
-
return void 0;
|
|
2494
|
-
}
|
|
2495
|
-
function shouldComplete(group) {
|
|
2496
|
-
if (group.waitPolicy === "manual") return false;
|
|
2497
|
-
if (group.waitPolicy === "wait_any") return group.results.length > 0;
|
|
2498
|
-
return group.taskIds.every((taskId) => group.results.some((result) => result.taskId === taskId));
|
|
2499
|
-
}
|
|
2500
|
-
function createResultEnvelope(task) {
|
|
2501
|
-
return {
|
|
2502
|
-
taskId: task.id,
|
|
2503
|
-
label: task.label,
|
|
2504
|
-
status: task.status,
|
|
2505
|
-
...task.result?.output ? { summary: summarizeOutput(task.result.output) } : {},
|
|
2506
|
-
...task.transcriptPath || task.logPath ? { outputRef: task.transcriptPath ?? task.logPath } : {},
|
|
2507
|
-
...task.error ? { error: { ...task.error } } : {},
|
|
2508
|
-
...task.startedAt ? { startedAt: task.startedAt } : {},
|
|
2509
|
-
...task.completedAt ? { completedAt: task.completedAt } : {}
|
|
2510
|
-
};
|
|
2511
|
-
}
|
|
2512
|
-
function summarizeOutput(output) {
|
|
2513
|
-
const trimmed = output.trim();
|
|
2514
|
-
if (trimmed.length <= DEFAULT_SUMMARY_LENGTH) return trimmed;
|
|
2515
|
-
return `${trimmed.slice(0, DEFAULT_SUMMARY_LENGTH)}...`;
|
|
2516
|
-
}
|
|
2517
|
-
function summarizeBackgroundJobGroup(group) {
|
|
2518
|
-
const completed = countResults(group, "completed");
|
|
2519
|
-
const failed = countResults(group, "failed");
|
|
2520
|
-
const cancelled = countResults(group, "cancelled");
|
|
2521
|
-
return {
|
|
2522
|
-
groupId: group.id,
|
|
2523
|
-
status: group.status,
|
|
2524
|
-
total: group.taskIds.length,
|
|
2525
|
-
completed,
|
|
2526
|
-
failed,
|
|
2527
|
-
cancelled,
|
|
2528
|
-
pending: Math.max(group.taskIds.length - group.results.length, 0),
|
|
2529
|
-
lines: group.results.map((result) => formatResultLine(result))
|
|
2530
|
-
};
|
|
2531
|
-
}
|
|
2532
|
-
function countResults(group, status) {
|
|
2533
|
-
return group.results.filter((result) => result.status === status).length;
|
|
2534
|
-
}
|
|
2535
|
-
function formatResultLine(result) {
|
|
2536
|
-
const detail = normalizeResultDetail(result);
|
|
2537
|
-
const output = result.outputRef && result.summary ? ` (output: ${result.outputRef})` : "";
|
|
2538
|
-
return `[${result.status}] ${result.label} ${result.taskId}: ${detail}${output}`;
|
|
2539
|
-
}
|
|
2540
|
-
function normalizeResultDetail(result) {
|
|
2541
|
-
const detail = result.error?.message ?? result.summary ?? "";
|
|
2542
|
-
const normalized = detail.replace(/\s+/g, " ").trim();
|
|
2543
|
-
return normalized.length > 0 ? normalized : "(no summary)";
|
|
2829
|
+
async function runManagedAgent(args, deps, manager) {
|
|
2830
|
+
if (typeof args.prompt !== "string" || args.prompt.length === 0) {
|
|
2831
|
+
return stringifyAgentError("prompt is required when jobs is omitted");
|
|
2832
|
+
}
|
|
2833
|
+
const singleArgs = { ...args, prompt: args.prompt };
|
|
2834
|
+
const agentType = args.subagent_type ?? "general-purpose";
|
|
2835
|
+
const agentDef = resolveAgentDefinition2(agentType, deps.customAgentRegistry);
|
|
2836
|
+
if (!agentDef) {
|
|
2837
|
+
return stringifyUnknownAgentType(agentType);
|
|
2838
|
+
}
|
|
2839
|
+
let agentId;
|
|
2840
|
+
try {
|
|
2841
|
+
const state = await manager.spawn(createSpawnRequest(singleArgs, agentType, agentDef, deps));
|
|
2842
|
+
agentId = state.id;
|
|
2843
|
+
const response = await manager.wait(state.id);
|
|
2844
|
+
return stringifyAgentSuccess(response);
|
|
2845
|
+
} catch (err) {
|
|
2846
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2847
|
+
return stringifyAgentError(message, agentId);
|
|
2848
|
+
}
|
|
2544
2849
|
}
|
|
2545
|
-
function
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2850
|
+
function createAgentTool(deps) {
|
|
2851
|
+
const manager = createSubagentManager(deps);
|
|
2852
|
+
return createZodFunctionTool2(
|
|
2853
|
+
"Agent",
|
|
2854
|
+
AGENT_TOOL_DESCRIPTION,
|
|
2855
|
+
asZodSchema2(AgentSchema),
|
|
2856
|
+
async (params) => {
|
|
2857
|
+
const args = params;
|
|
2858
|
+
if (Array.isArray(args.jobs) && args.jobs.length > 0) {
|
|
2859
|
+
return runManagedAgentBatch({
|
|
2860
|
+
jobs: args.jobs,
|
|
2861
|
+
deps,
|
|
2862
|
+
manager,
|
|
2863
|
+
resolveAgentDefinition: resolveAgentDefinition2,
|
|
2864
|
+
createSpawnRequest
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
return runManagedAgent(
|
|
2868
|
+
{
|
|
2869
|
+
prompt: args.prompt,
|
|
2870
|
+
...args.subagent_type !== void 0 ? { subagent_type: args.subagent_type } : {},
|
|
2871
|
+
...args.model !== void 0 ? { model: args.model } : {},
|
|
2872
|
+
...args.isolation !== void 0 ? { isolation: args.isolation } : {}
|
|
2873
|
+
},
|
|
2874
|
+
deps,
|
|
2875
|
+
manager
|
|
2876
|
+
);
|
|
2877
|
+
}
|
|
2878
|
+
);
|
|
2554
2879
|
}
|
|
2555
2880
|
|
|
2556
|
-
// src/background-tasks/index.ts
|
|
2557
|
-
import {
|
|
2558
|
-
getBackgroundTaskTransitions,
|
|
2559
|
-
isTerminalBackgroundTaskStatus as isTerminalBackgroundTaskStatus2,
|
|
2560
|
-
transitionBackgroundTaskStatus,
|
|
2561
|
-
appendPrefixedLogLines,
|
|
2562
|
-
createBackgroundTaskLogPage,
|
|
2563
|
-
createLimitedOutputCapture,
|
|
2564
|
-
DEFAULT_BACKGROUND_TASK_LOG_PAGE_SIZE
|
|
2565
|
-
} from "@robota-sdk/agent-runtime";
|
|
2566
|
-
import { BackgroundTaskError } from "@robota-sdk/agent-runtime";
|
|
2567
|
-
|
|
2568
2881
|
// src/background-tasks/session-background-store.ts
|
|
2569
2882
|
var sessionBackgroundTaskManagers = /* @__PURE__ */ new WeakMap();
|
|
2570
2883
|
function storeSessionBackgroundTaskManager(key, manager) {
|
|
@@ -3424,6 +3737,10 @@ var MarketplaceSourceSchema = z3.object({
|
|
|
3424
3737
|
});
|
|
3425
3738
|
var ExtraKnownMarketplacesSchema = z3.record(MarketplaceSourceSchema).optional().catch(void 0);
|
|
3426
3739
|
var AutoCompactThresholdSchema = z3.union([z3.number().gt(0).lte(1), z3.literal(false)]).optional();
|
|
3740
|
+
var TransportSettingsSchema = z3.object({
|
|
3741
|
+
enabled: z3.boolean().optional(),
|
|
3742
|
+
options: z3.record(UniversalValueSchema).optional()
|
|
3743
|
+
});
|
|
3427
3744
|
var SettingsSchema = z3.object({
|
|
3428
3745
|
/** Trust level used when no --permission-mode flag is given */
|
|
3429
3746
|
defaultTrustLevel: z3.enum(["safe", "moderate", "full"]).optional(),
|
|
@@ -3443,7 +3760,9 @@ var SettingsSchema = z3.object({
|
|
|
3443
3760
|
/** Extra marketplace URLs for BundlePlugin discovery */
|
|
3444
3761
|
extraKnownMarketplaces: ExtraKnownMarketplacesSchema,
|
|
3445
3762
|
/** Auto-compact threshold as a 0-1 fraction. Set false to disable automatic compaction. */
|
|
3446
|
-
autoCompactThreshold: AutoCompactThresholdSchema
|
|
3763
|
+
autoCompactThreshold: AutoCompactThresholdSchema,
|
|
3764
|
+
/** Transport enable/disable + options: transport name -> config */
|
|
3765
|
+
transports: z3.record(TransportSettingsSchema).optional()
|
|
3447
3766
|
});
|
|
3448
3767
|
|
|
3449
3768
|
// src/config/config-loader.ts
|
|
@@ -3546,7 +3865,12 @@ function resolveProvider(merged) {
|
|
|
3546
3865
|
if (merged.currentProvider !== void 0) {
|
|
3547
3866
|
return resolveActiveProviderProfile2(merged);
|
|
3548
3867
|
}
|
|
3549
|
-
|
|
3868
|
+
if (merged.provider !== void 0) {
|
|
3869
|
+
throw new Error(
|
|
3870
|
+
'Legacy flat "provider" settings are not supported. Migrate to "currentProvider" + "providers" format.'
|
|
3871
|
+
);
|
|
3872
|
+
}
|
|
3873
|
+
return { ...DEFAULTS.provider };
|
|
3550
3874
|
}
|
|
3551
3875
|
function resolveActiveProviderProfile2(merged) {
|
|
3552
3876
|
const currentProvider = merged.currentProvider;
|
|
@@ -3569,16 +3893,6 @@ function resolveActiveProviderProfile2(merged) {
|
|
|
3569
3893
|
...profile.options !== void 0 && { options: profile.options }
|
|
3570
3894
|
};
|
|
3571
3895
|
}
|
|
3572
|
-
function resolveLegacyProvider(merged) {
|
|
3573
|
-
return {
|
|
3574
|
-
name: merged.provider?.name ?? DEFAULTS.provider.name,
|
|
3575
|
-
model: merged.provider?.model ?? DEFAULTS.provider.model,
|
|
3576
|
-
apiKey: merged.provider?.apiKey ?? DEFAULTS.provider.apiKey,
|
|
3577
|
-
...merged.provider?.baseURL !== void 0 && { baseURL: merged.provider.baseURL },
|
|
3578
|
-
...merged.provider?.timeout !== void 0 && { timeout: merged.provider.timeout },
|
|
3579
|
-
...merged.provider?.options !== void 0 && { options: merged.provider.options }
|
|
3580
|
-
};
|
|
3581
|
-
}
|
|
3582
3896
|
function toResolvedConfig(merged) {
|
|
3583
3897
|
return {
|
|
3584
3898
|
defaultTrustLevel: merged.defaultTrustLevel ?? DEFAULTS.defaultTrustLevel,
|
|
@@ -3622,10 +3936,10 @@ async function loadConfig(cwd) {
|
|
|
3622
3936
|
rawEntries.push({ raw, path: filePath });
|
|
3623
3937
|
}
|
|
3624
3938
|
}
|
|
3625
|
-
const parsedLayers = rawEntries.map(({ raw, path }) => {
|
|
3939
|
+
const parsedLayers = rawEntries.map(({ raw, path: path3 }) => {
|
|
3626
3940
|
const result = SettingsSchema.safeParse(raw);
|
|
3627
3941
|
if (!result.success) {
|
|
3628
|
-
throw new Error(`Invalid settings in ${
|
|
3942
|
+
throw new Error(`Invalid settings in ${path3}: ${result.error.message}`);
|
|
3629
3943
|
}
|
|
3630
3944
|
return resolveEnvRefs(result.data);
|
|
3631
3945
|
});
|
|
@@ -3998,7 +4312,8 @@ async function startBackgroundProcess(args, deps) {
|
|
|
3998
4312
|
command: args.command,
|
|
3999
4313
|
stdin: args.stdin,
|
|
4000
4314
|
timeoutMs: args.timeout ?? DEFAULT_PROCESS_TIMEOUT_MS,
|
|
4001
|
-
outputLimitBytes: args.outputLimitBytes
|
|
4315
|
+
outputLimitBytes: args.outputLimitBytes,
|
|
4316
|
+
metadata: deps.metadata
|
|
4002
4317
|
});
|
|
4003
4318
|
return stringifyStarted(state.id, state.status, args.command);
|
|
4004
4319
|
} catch (error) {
|
|
@@ -4541,7 +4856,12 @@ function createSession(options) {
|
|
|
4541
4856
|
backgroundProcessToolDeps = {
|
|
4542
4857
|
backgroundTaskManager,
|
|
4543
4858
|
cwd,
|
|
4544
|
-
parentSessionId: sessionId
|
|
4859
|
+
parentSessionId: sessionId,
|
|
4860
|
+
metadata: createExecutionOriginMetadata({
|
|
4861
|
+
kind: "tool_call",
|
|
4862
|
+
sessionId,
|
|
4863
|
+
label: "BackgroundProcess"
|
|
4864
|
+
})
|
|
4545
4865
|
};
|
|
4546
4866
|
tools.push(createBackgroundProcessTool(backgroundProcessToolDeps));
|
|
4547
4867
|
}
|
|
@@ -4817,7 +5137,7 @@ function formatTaskContext(tasks) {
|
|
|
4817
5137
|
}
|
|
4818
5138
|
function loadTaskContext(cwd, options = {}) {
|
|
4819
5139
|
const currentBranch = options.currentBranch ?? readCurrentGitBranch(cwd);
|
|
4820
|
-
const tasks = discoverTaskFiles(cwd).map((
|
|
5140
|
+
const tasks = discoverTaskFiles(cwd).map((path3) => parseTaskFile(path3, cwd));
|
|
4821
5141
|
return formatTaskContext(selectRelevantTasks(tasks, { ...options, currentBranch }));
|
|
4822
5142
|
}
|
|
4823
5143
|
function updateTaskFileStatus(taskPath, status, options = {}) {
|
|
@@ -5164,15 +5484,11 @@ var BundlePluginLoader = class {
|
|
|
5164
5484
|
}
|
|
5165
5485
|
return results;
|
|
5166
5486
|
}
|
|
5167
|
-
/** Read and validate a plugin.json manifest. Returns null
|
|
5168
|
-
readManifest(
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
return validateManifest(data);
|
|
5173
|
-
} catch {
|
|
5174
|
-
return null;
|
|
5175
|
-
}
|
|
5487
|
+
/** Read and validate a plugin.json manifest. Returns null if the manifest structure is invalid. */
|
|
5488
|
+
readManifest(path3) {
|
|
5489
|
+
const raw = readFileSync11(path3, "utf-8");
|
|
5490
|
+
const data = JSON.parse(raw);
|
|
5491
|
+
return validateManifest(data);
|
|
5176
5492
|
}
|
|
5177
5493
|
/**
|
|
5178
5494
|
* Check if a plugin is explicitly disabled.
|
|
@@ -5248,40 +5564,26 @@ var BundlePluginLoader = class {
|
|
|
5248
5564
|
loadHooks(pluginDir) {
|
|
5249
5565
|
const hooksPath = join12(pluginDir, "hooks", "hooks.json");
|
|
5250
5566
|
if (!existsSync11(hooksPath)) return {};
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
return data;
|
|
5256
|
-
}
|
|
5257
|
-
return {};
|
|
5258
|
-
} catch {
|
|
5259
|
-
return {};
|
|
5567
|
+
const raw = readFileSync11(hooksPath, "utf-8");
|
|
5568
|
+
const data = JSON.parse(raw);
|
|
5569
|
+
if (typeof data === "object" && data !== null) {
|
|
5570
|
+
return data;
|
|
5260
5571
|
}
|
|
5572
|
+
return {};
|
|
5261
5573
|
}
|
|
5262
|
-
/** Load MCP server configuration
|
|
5574
|
+
/** Load MCP server configuration from `.mcp.json` at the plugin root if present. */
|
|
5263
5575
|
loadMcpConfig(pluginDir) {
|
|
5264
|
-
const
|
|
5265
|
-
const fallbackPath = join12(pluginDir, ".claude-plugin", "mcp.json");
|
|
5266
|
-
const mcpPath = existsSync11(primaryPath) ? primaryPath : fallbackPath;
|
|
5576
|
+
const mcpPath = join12(pluginDir, ".mcp.json");
|
|
5267
5577
|
if (!existsSync11(mcpPath)) return void 0;
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
return JSON.parse(raw);
|
|
5271
|
-
} catch {
|
|
5272
|
-
return void 0;
|
|
5273
|
-
}
|
|
5578
|
+
const raw = readFileSync11(mcpPath, "utf-8");
|
|
5579
|
+
return JSON.parse(raw);
|
|
5274
5580
|
}
|
|
5275
5581
|
/** Load agent definitions from agents/ directory if present. */
|
|
5276
5582
|
loadAgents(pluginDir) {
|
|
5277
5583
|
const agentsDir = join12(pluginDir, "agents");
|
|
5278
5584
|
if (!existsSync11(agentsDir)) return [];
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
return entries.filter((e) => e.isDirectory() || e.name.endsWith(".md")).map((e) => e.name.replace(/\.md$/, ""));
|
|
5282
|
-
} catch {
|
|
5283
|
-
return [];
|
|
5284
|
-
}
|
|
5585
|
+
const entries = readdirSync6(agentsDir, { withFileTypes: true });
|
|
5586
|
+
return entries.filter((e) => e.isDirectory() || e.name.endsWith(".md")).map((e) => e.name.replace(/\.md$/, ""));
|
|
5285
5587
|
}
|
|
5286
5588
|
};
|
|
5287
5589
|
|
|
@@ -5725,8 +6027,8 @@ var MarketplaceClient = class {
|
|
|
5725
6027
|
}
|
|
5726
6028
|
}
|
|
5727
6029
|
/** Read and parse a marketplace.json from a file path. */
|
|
5728
|
-
readManifestFromPath(
|
|
5729
|
-
const raw = readFileSync14(
|
|
6030
|
+
readManifestFromPath(path3) {
|
|
6031
|
+
const raw = readFileSync14(path3, "utf-8");
|
|
5730
6032
|
const data = JSON.parse(raw);
|
|
5731
6033
|
if (typeof data !== "object" || data === null) {
|
|
5732
6034
|
throw new Error("Invalid marketplace manifest: not an object");
|
|
@@ -5818,18 +6120,18 @@ async function createInteractiveSession(options) {
|
|
|
5818
6120
|
options.bare ? Promise.resolve({ agentsMd: "", claudeMd: "" }) : loadContext(cwd),
|
|
5819
6121
|
options.bare ? Promise.resolve({ type: "unknown", language: "unknown" }) : detectProject(cwd)
|
|
5820
6122
|
]);
|
|
6123
|
+
let mergedConfig = options.language ? { ...config, language: options.language } : config;
|
|
5821
6124
|
const pluginsDir = join17(homedir5(), ".robota", "plugins");
|
|
5822
6125
|
const pluginLoader = new BundlePluginLoader(pluginsDir);
|
|
5823
|
-
let mergedConfig = config;
|
|
5824
6126
|
if (!options.bare) {
|
|
5825
6127
|
try {
|
|
5826
6128
|
const plugins = pluginLoader.loadPluginsSync();
|
|
5827
6129
|
if (plugins.length > 0) {
|
|
5828
6130
|
const pluginHooks = mergePluginHooks(plugins);
|
|
5829
6131
|
mergedConfig = {
|
|
5830
|
-
...
|
|
6132
|
+
...mergedConfig,
|
|
5831
6133
|
hooks: mergeHooksIntoConfig(
|
|
5832
|
-
|
|
6134
|
+
mergedConfig.hooks,
|
|
5833
6135
|
pluginHooks
|
|
5834
6136
|
)
|
|
5835
6137
|
};
|
|
@@ -5997,8 +6299,8 @@ function isRestoredTerminalStatus(status) {
|
|
|
5997
6299
|
}
|
|
5998
6300
|
|
|
5999
6301
|
// src/interactive/interactive-session-context-references.ts
|
|
6000
|
-
async function addInteractiveContextReference(references,
|
|
6001
|
-
const result = await resolvePromptFileReferencePaths([
|
|
6302
|
+
async function addInteractiveContextReference(references, path3, cwd) {
|
|
6303
|
+
const result = await resolvePromptFileReferencePaths([path3], {
|
|
6002
6304
|
cwd,
|
|
6003
6305
|
reason: "manual"
|
|
6004
6306
|
});
|
|
@@ -6091,9 +6393,9 @@ function toInspectionPlan(manifests) {
|
|
|
6091
6393
|
fileCount: manifests.reduce((count, manifest) => count + manifest.fileCount, 0)
|
|
6092
6394
|
};
|
|
6093
6395
|
}
|
|
6094
|
-
function statSafe(
|
|
6396
|
+
function statSafe(path3) {
|
|
6095
6397
|
try {
|
|
6096
|
-
return statSync2(
|
|
6398
|
+
return statSync2(path3);
|
|
6097
6399
|
} catch {
|
|
6098
6400
|
return void 0;
|
|
6099
6401
|
}
|
|
@@ -6264,10 +6566,10 @@ var EditCheckpointStore = class {
|
|
|
6264
6566
|
}
|
|
6265
6567
|
async writeManifest(dir, manifest) {
|
|
6266
6568
|
await mkdir(dir, { recursive: true });
|
|
6267
|
-
const
|
|
6268
|
-
const tmp = `${
|
|
6569
|
+
const path3 = join19(dir, MANIFEST_FILE);
|
|
6570
|
+
const tmp = `${path3}.tmp`;
|
|
6269
6571
|
await writeFile(tmp, JSON.stringify(manifest, null, 2), "utf8");
|
|
6270
|
-
await rename(tmp,
|
|
6572
|
+
await rename(tmp, path3);
|
|
6271
6573
|
}
|
|
6272
6574
|
sessionDir(sessionId) {
|
|
6273
6575
|
return join19(this.rootDir, safePathSegment(sessionId));
|
|
@@ -6293,9 +6595,9 @@ function isInside(parent, child) {
|
|
|
6293
6595
|
const rel = relative3(parent, child);
|
|
6294
6596
|
return rel.length === 0 || !rel.startsWith("..") && !rel.startsWith("/");
|
|
6295
6597
|
}
|
|
6296
|
-
async function pathExists(
|
|
6598
|
+
async function pathExists(path3) {
|
|
6297
6599
|
try {
|
|
6298
|
-
await access(
|
|
6600
|
+
await access(path3, constants.F_OK);
|
|
6299
6601
|
return true;
|
|
6300
6602
|
} catch {
|
|
6301
6603
|
return false;
|
|
@@ -6308,9 +6610,9 @@ function readDirSyncSafe(dir) {
|
|
|
6308
6610
|
return [];
|
|
6309
6611
|
}
|
|
6310
6612
|
}
|
|
6311
|
-
function readJsonManifest(
|
|
6613
|
+
function readJsonManifest(path3) {
|
|
6312
6614
|
try {
|
|
6313
|
-
const raw = readFileSync15(
|
|
6615
|
+
const raw = readFileSync15(path3, "utf8");
|
|
6314
6616
|
return JSON.parse(raw);
|
|
6315
6617
|
} catch {
|
|
6316
6618
|
return void 0;
|
|
@@ -6333,6 +6635,11 @@ function getQualifiedSkillName(rawInput) {
|
|
|
6333
6635
|
const firstToken = rawInput.slice(1).trim().split(/\s+/)[0];
|
|
6334
6636
|
return firstToken && firstToken.length > 0 ? firstToken : void 0;
|
|
6335
6637
|
}
|
|
6638
|
+
function getBackgroundTaskEventEntryId(event) {
|
|
6639
|
+
if ("task" in event) return createBackgroundTaskExecutionEntryId(event.task.id);
|
|
6640
|
+
if ("taskId" in event) return createBackgroundTaskExecutionEntryId(event.taskId);
|
|
6641
|
+
return void 0;
|
|
6642
|
+
}
|
|
6336
6643
|
var InteractiveSession = class {
|
|
6337
6644
|
session = null;
|
|
6338
6645
|
commandExecutor;
|
|
@@ -6450,6 +6757,7 @@ var InteractiveSession = class {
|
|
|
6450
6757
|
bare: options.bare,
|
|
6451
6758
|
allowedTools: options.allowedTools,
|
|
6452
6759
|
appendSystemPrompt: options.appendSystemPrompt,
|
|
6760
|
+
language: options.language,
|
|
6453
6761
|
backgroundTaskRunners: options.backgroundTaskRunners,
|
|
6454
6762
|
subagentRunnerFactory: options.subagentRunnerFactory,
|
|
6455
6763
|
...options.commandModules ? { commandModules: options.commandModules } : {},
|
|
@@ -6481,6 +6789,9 @@ var InteractiveSession = class {
|
|
|
6481
6789
|
throw new Error("InteractiveSession not initialized. Call submit() or await initialization.");
|
|
6482
6790
|
return this.session;
|
|
6483
6791
|
}
|
|
6792
|
+
get sessionId() {
|
|
6793
|
+
return this.session?.getSessionId() ?? "";
|
|
6794
|
+
}
|
|
6484
6795
|
on(event, handler) {
|
|
6485
6796
|
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
6486
6797
|
this.listeners.get(event).add(handler);
|
|
@@ -6692,6 +7003,9 @@ var InteractiveSession = class {
|
|
|
6692
7003
|
getActiveTools() {
|
|
6693
7004
|
return this.activeTools;
|
|
6694
7005
|
}
|
|
7006
|
+
get isInitialized() {
|
|
7007
|
+
return this.initialized;
|
|
7008
|
+
}
|
|
6695
7009
|
getContextState() {
|
|
6696
7010
|
return this.getSessionOrThrow().getContextState();
|
|
6697
7011
|
}
|
|
@@ -6776,18 +7090,18 @@ var InteractiveSession = class {
|
|
|
6776
7090
|
listContextReferences() {
|
|
6777
7091
|
return [...this.contextReferences];
|
|
6778
7092
|
}
|
|
6779
|
-
async addContextReference(
|
|
7093
|
+
async addContextReference(path3) {
|
|
6780
7094
|
const { references, result } = await addInteractiveContextReference(
|
|
6781
7095
|
this.contextReferences,
|
|
6782
|
-
|
|
7096
|
+
path3,
|
|
6783
7097
|
this.getCwd()
|
|
6784
7098
|
);
|
|
6785
7099
|
this.contextReferences = references;
|
|
6786
7100
|
this.persistCurrentSession();
|
|
6787
7101
|
return result;
|
|
6788
7102
|
}
|
|
6789
|
-
removeContextReference(
|
|
6790
|
-
const result = removeContextReference(this.contextReferences,
|
|
7103
|
+
removeContextReference(path3) {
|
|
7104
|
+
const result = removeContextReference(this.contextReferences, path3);
|
|
6791
7105
|
this.contextReferences = result.references;
|
|
6792
7106
|
this.persistCurrentSession();
|
|
6793
7107
|
return result.result;
|
|
@@ -6837,6 +7151,53 @@ var InteractiveSession = class {
|
|
|
6837
7151
|
await this.ensureInitialized();
|
|
6838
7152
|
return this.getBackgroundJobOrchestratorOrThrow().waitGroup(groupId);
|
|
6839
7153
|
}
|
|
7154
|
+
getExecutionWorkspaceSnapshot(options = {}) {
|
|
7155
|
+
const session = this.getSessionOrThrow();
|
|
7156
|
+
const sessionId = session.getSessionId();
|
|
7157
|
+
return createExecutionWorkspaceSnapshot({
|
|
7158
|
+
sessionId,
|
|
7159
|
+
mainThread: {
|
|
7160
|
+
sessionId,
|
|
7161
|
+
isExecuting: this.executing,
|
|
7162
|
+
hasPendingPrompt: this.pendingPrompt !== null,
|
|
7163
|
+
historyLength: this.history.length,
|
|
7164
|
+
updatedAt: this.getMainThreadUpdatedAt(),
|
|
7165
|
+
preview: this.getMainThreadPreview()
|
|
7166
|
+
},
|
|
7167
|
+
tasks: this.getBackgroundTaskSnapshots(),
|
|
7168
|
+
groups: this.getBackgroundJobGroupSnapshots(),
|
|
7169
|
+
selectedEntryId: options.selectedEntryId,
|
|
7170
|
+
filter: options.filter
|
|
7171
|
+
});
|
|
7172
|
+
}
|
|
7173
|
+
listExecutionWorkspaceEntries(filter) {
|
|
7174
|
+
return [...this.getExecutionWorkspaceSnapshot({ filter }).entries];
|
|
7175
|
+
}
|
|
7176
|
+
getExecutionWorkspaceEntry(entryId) {
|
|
7177
|
+
return this.getExecutionWorkspaceSnapshot().entries.find((entry) => entry.id === entryId);
|
|
7178
|
+
}
|
|
7179
|
+
async readExecutionWorkspaceDetail(entryId, cursor) {
|
|
7180
|
+
await this.ensureInitialized();
|
|
7181
|
+
const entryRef = parseExecutionWorkspaceEntryId(entryId);
|
|
7182
|
+
if (!entryRef) throw new Error(`Unknown execution workspace entry: ${entryId}`);
|
|
7183
|
+
if (entryRef.kind === "main_thread") {
|
|
7184
|
+
return createMainThreadDetailPage({ entryId, history: this.history, cursor });
|
|
7185
|
+
}
|
|
7186
|
+
if (entryRef.kind === "background_group") {
|
|
7187
|
+
return this.readBackgroundGroupDetail(entryId, entryRef.sourceId);
|
|
7188
|
+
}
|
|
7189
|
+
return this.readBackgroundTaskDetail(entryId, entryRef.sourceId, cursor);
|
|
7190
|
+
}
|
|
7191
|
+
createExecutionWorkspaceTaskSpawner(origin) {
|
|
7192
|
+
const sessionId = this.getSessionOrThrow().getSessionId();
|
|
7193
|
+
return createExecutionWorkspaceTaskSpawner({
|
|
7194
|
+
manager: this.getBackgroundTaskManagerOrThrow(),
|
|
7195
|
+
groupOrchestrator: this.getBackgroundJobOrchestratorOrThrow(),
|
|
7196
|
+
sessionId,
|
|
7197
|
+
cwd: this.getCwd(),
|
|
7198
|
+
origin: { ...origin, sessionId: origin.sessionId || sessionId }
|
|
7199
|
+
});
|
|
7200
|
+
}
|
|
6840
7201
|
listAgentDefinitions() {
|
|
6841
7202
|
const deps = retrieveAgentToolDeps(this.getSessionOrThrow());
|
|
6842
7203
|
return (deps?.agentDefinitions ?? []).map((agent) => ({
|
|
@@ -6851,10 +7212,11 @@ var InteractiveSession = class {
|
|
|
6851
7212
|
await this.ensureInitialized();
|
|
6852
7213
|
const deps = this.getAgentToolDepsOrThrow();
|
|
6853
7214
|
const definition = this.resolveAgentDefinition(input.agentType, deps);
|
|
7215
|
+
const sessionId = this.getSessionOrThrow().getSessionId();
|
|
6854
7216
|
return this.getSubagentManagerOrThrow().spawn({
|
|
6855
7217
|
type: input.agentType,
|
|
6856
7218
|
label: input.label,
|
|
6857
|
-
parentSessionId:
|
|
7219
|
+
parentSessionId: sessionId,
|
|
6858
7220
|
mode: input.mode,
|
|
6859
7221
|
depth: (deps.subagentDepth ?? 0) + 1,
|
|
6860
7222
|
cwd: deps.cwd ?? this.cwd ?? process.cwd(),
|
|
@@ -6862,7 +7224,13 @@ var InteractiveSession = class {
|
|
|
6862
7224
|
model: input.model ?? definition.model,
|
|
6863
7225
|
isolation: input.isolation,
|
|
6864
7226
|
allowedTools: definition.tools,
|
|
6865
|
-
disallowedTools: definition.disallowedTools
|
|
7227
|
+
disallowedTools: definition.disallowedTools,
|
|
7228
|
+
metadata: createExecutionOriginMetadata({
|
|
7229
|
+
kind: this.commandInvocationSource === "model" ? "model_command" : "slash_command",
|
|
7230
|
+
sessionId,
|
|
7231
|
+
commandName: "agent",
|
|
7232
|
+
label: input.label
|
|
7233
|
+
})
|
|
6866
7234
|
});
|
|
6867
7235
|
}
|
|
6868
7236
|
async waitAgentJob(jobId) {
|
|
@@ -6965,6 +7333,51 @@ var InteractiveSession = class {
|
|
|
6965
7333
|
this.finishForkSkillExecution();
|
|
6966
7334
|
}
|
|
6967
7335
|
}
|
|
7336
|
+
async readBackgroundTaskDetail(entryId, taskId, cursor) {
|
|
7337
|
+
const task = this.getBackgroundTaskManagerOrThrow().get(taskId);
|
|
7338
|
+
if (!task) throw new Error(`Unknown background task: ${taskId}`);
|
|
7339
|
+
if (task.logPath || task.transcriptPath) {
|
|
7340
|
+
const page = await this.getBackgroundTaskManagerOrThrow().readLog(taskId, cursor);
|
|
7341
|
+
return createLineDetailPage({
|
|
7342
|
+
entryId,
|
|
7343
|
+
lines: page.lines,
|
|
7344
|
+
cursor: page.cursor,
|
|
7345
|
+
nextCursor: page.nextCursor,
|
|
7346
|
+
kind: task.kind === "process" ? "process_output" : "progress"
|
|
7347
|
+
});
|
|
7348
|
+
}
|
|
7349
|
+
const detailKind = task.status === "failed" ? "error" : task.status === "completed" ? "result" : "progress";
|
|
7350
|
+
const text = task.error?.message ?? task.result?.output ?? task.currentAction ?? task.promptPreview ?? task.commandPreview ?? task.status;
|
|
7351
|
+
return createLineDetailPage({ entryId, lines: [text], cursor, kind: detailKind });
|
|
7352
|
+
}
|
|
7353
|
+
readBackgroundGroupDetail(entryId, groupId) {
|
|
7354
|
+
const group = this.getBackgroundJobOrchestratorOrThrow().getGroup(groupId);
|
|
7355
|
+
if (!group) throw new Error(`Unknown background job group: ${groupId}`);
|
|
7356
|
+
const summary = summarizeBackgroundJobGroup(group);
|
|
7357
|
+
return createLineDetailPage({
|
|
7358
|
+
entryId,
|
|
7359
|
+
lines: summary.lines,
|
|
7360
|
+
kind: "group_summary"
|
|
7361
|
+
});
|
|
7362
|
+
}
|
|
7363
|
+
getMainThreadUpdatedAt() {
|
|
7364
|
+
return this.history.at(-1)?.timestamp.toISOString() ?? (/* @__PURE__ */ new Date(0)).toISOString();
|
|
7365
|
+
}
|
|
7366
|
+
getMainThreadPreview() {
|
|
7367
|
+
if (this.streamingText.trim().length > 0) return this.streamingText;
|
|
7368
|
+
const latest = this.history.at(-1);
|
|
7369
|
+
if (!latest) return void 0;
|
|
7370
|
+
return latest.type;
|
|
7371
|
+
}
|
|
7372
|
+
emitExecutionWorkspaceUpdated(cause, entryId) {
|
|
7373
|
+
if (!this.session) return;
|
|
7374
|
+
this.emit("execution_workspace_event", {
|
|
7375
|
+
type: "execution_workspace_updated",
|
|
7376
|
+
cause,
|
|
7377
|
+
...entryId ? { entryId } : {},
|
|
7378
|
+
snapshot: this.getExecutionWorkspaceSnapshot()
|
|
7379
|
+
});
|
|
7380
|
+
}
|
|
6968
7381
|
getBackgroundTaskManagerOrThrow() {
|
|
6969
7382
|
const manager = this.getBackgroundTaskManager();
|
|
6970
7383
|
if (!manager) {
|
|
@@ -7030,11 +7443,16 @@ var InteractiveSession = class {
|
|
|
7030
7443
|
this.backgroundTasks = this.getBackgroundTaskSnapshots();
|
|
7031
7444
|
this.backgroundTaskEvents.push(event);
|
|
7032
7445
|
this.persistCurrentSession();
|
|
7446
|
+
this.emitExecutionWorkspaceUpdated("background_task", getBackgroundTaskEventEntryId(event));
|
|
7033
7447
|
}
|
|
7034
7448
|
recordBackgroundJobGroupEvent(event) {
|
|
7035
7449
|
this.backgroundJobGroups = this.getBackgroundJobGroupSnapshots();
|
|
7036
7450
|
this.backgroundJobGroupEvents.push(event);
|
|
7037
7451
|
this.persistCurrentSession();
|
|
7452
|
+
this.emitExecutionWorkspaceUpdated(
|
|
7453
|
+
"background_group",
|
|
7454
|
+
createBackgroundGroupExecutionEntryId(event.group.id)
|
|
7455
|
+
);
|
|
7038
7456
|
}
|
|
7039
7457
|
getBackgroundTaskSnapshots() {
|
|
7040
7458
|
try {
|
|
@@ -7098,10 +7516,12 @@ var InteractiveSession = class {
|
|
|
7098
7516
|
this.clearStreaming();
|
|
7099
7517
|
this.emit("thinking", true);
|
|
7100
7518
|
this.history.push(messageToHistoryEntry(createUserMessage(displayInput)));
|
|
7519
|
+
this.emitExecutionWorkspaceUpdated("main_thread");
|
|
7101
7520
|
}
|
|
7102
7521
|
finishForkSkillExecution() {
|
|
7103
7522
|
this.executing = false;
|
|
7104
7523
|
this.emit("thinking", false);
|
|
7524
|
+
this.emitExecutionWorkspaceUpdated("main_thread");
|
|
7105
7525
|
this.persistCurrentSession();
|
|
7106
7526
|
if (!this.shuttingDown && this.pendingPrompt) {
|
|
7107
7527
|
const queued = this.pendingPrompt;
|
|
@@ -7171,6 +7591,7 @@ var InteractiveSession = class {
|
|
|
7171
7591
|
this.executing = true;
|
|
7172
7592
|
this.clearStreaming();
|
|
7173
7593
|
this.emit("thinking", true);
|
|
7594
|
+
this.emitExecutionWorkspaceUpdated("main_thread");
|
|
7174
7595
|
try {
|
|
7175
7596
|
const result = await this.commandExecutor.executeCommand(command, this, args);
|
|
7176
7597
|
this.emit("context_update", this.getContextState());
|
|
@@ -7184,6 +7605,7 @@ var InteractiveSession = class {
|
|
|
7184
7605
|
} finally {
|
|
7185
7606
|
this.executing = false;
|
|
7186
7607
|
this.emit("thinking", false);
|
|
7608
|
+
this.emitExecutionWorkspaceUpdated("main_thread");
|
|
7187
7609
|
this.persistCurrentSession();
|
|
7188
7610
|
if (!this.shuttingDown && this.pendingPrompt) {
|
|
7189
7611
|
const queued = this.pendingPrompt;
|
|
@@ -7197,8 +7619,10 @@ var InteractiveSession = class {
|
|
|
7197
7619
|
async executePrompt(input, displayInput, rawInput) {
|
|
7198
7620
|
this.executing = true;
|
|
7199
7621
|
this.clearStreaming();
|
|
7622
|
+
this.emit("user_message", displayInput ?? input);
|
|
7200
7623
|
this.emit("thinking", true);
|
|
7201
7624
|
this.history.push(messageToHistoryEntry(createUserMessage(displayInput ?? input)));
|
|
7625
|
+
this.emitExecutionWorkspaceUpdated("main_thread");
|
|
7202
7626
|
const historyBefore = this.getSessionOrThrow().getHistory().length;
|
|
7203
7627
|
this.usedMemoryReferences = [];
|
|
7204
7628
|
try {
|
|
@@ -7260,6 +7684,7 @@ var InteractiveSession = class {
|
|
|
7260
7684
|
await this.finalizeEditCheckpointTurn();
|
|
7261
7685
|
this.executing = false;
|
|
7262
7686
|
this.emit("thinking", false);
|
|
7687
|
+
this.emitExecutionWorkspaceUpdated("main_thread");
|
|
7263
7688
|
this.persistCurrentSession();
|
|
7264
7689
|
if (!this.shuttingDown && this.pendingPrompt) {
|
|
7265
7690
|
const queued = this.pendingPrompt;
|
|
@@ -7558,6 +7983,404 @@ function createQuery(options) {
|
|
|
7558
7983
|
};
|
|
7559
7984
|
}
|
|
7560
7985
|
|
|
7986
|
+
// src/user-local/storage.ts
|
|
7987
|
+
import { promises as fs } from "fs";
|
|
7988
|
+
import { homedir as homedir6 } from "os";
|
|
7989
|
+
import path from "path";
|
|
7990
|
+
var USER_LOCAL_STORAGE_CATEGORIES = [
|
|
7991
|
+
"preferences",
|
|
7992
|
+
"view-state",
|
|
7993
|
+
"memory-projections",
|
|
7994
|
+
"task-associations",
|
|
7995
|
+
"workflow-metadata",
|
|
7996
|
+
"inspection-index"
|
|
7997
|
+
];
|
|
7998
|
+
var USER_LOCAL_STORAGE_CATEGORY_DEFINITIONS = [
|
|
7999
|
+
{
|
|
8000
|
+
category: "preferences",
|
|
8001
|
+
purpose: "User-local UI and display preferences.",
|
|
8002
|
+
mayExecuteCommands: false
|
|
8003
|
+
},
|
|
8004
|
+
{
|
|
8005
|
+
category: "view-state",
|
|
8006
|
+
purpose: "Last selected panels, filters, and navigation state.",
|
|
8007
|
+
mayExecuteCommands: false
|
|
8008
|
+
},
|
|
8009
|
+
{
|
|
8010
|
+
category: "memory-projections",
|
|
8011
|
+
purpose: "Inspectable local memory item projections and user choices.",
|
|
8012
|
+
mayExecuteCommands: false
|
|
8013
|
+
},
|
|
8014
|
+
{
|
|
8015
|
+
category: "task-associations",
|
|
8016
|
+
purpose: "User-local associations between sessions, tasks, and background items.",
|
|
8017
|
+
mayExecuteCommands: false
|
|
8018
|
+
},
|
|
8019
|
+
{
|
|
8020
|
+
category: "workflow-metadata",
|
|
8021
|
+
purpose: "Transparent workflow metadata that is not repo-owned.",
|
|
8022
|
+
mayExecuteCommands: false
|
|
8023
|
+
},
|
|
8024
|
+
{
|
|
8025
|
+
category: "inspection-index",
|
|
8026
|
+
purpose: "Category and item summaries for user inspection and deletion.",
|
|
8027
|
+
mayExecuteCommands: false
|
|
8028
|
+
}
|
|
8029
|
+
];
|
|
8030
|
+
function formatIsoDate(date) {
|
|
8031
|
+
return date.toISOString();
|
|
8032
|
+
}
|
|
8033
|
+
function assertAbsolutePath(name, value) {
|
|
8034
|
+
if (value.trim().length === 0) {
|
|
8035
|
+
throw new Error(`${name} must not be empty.`);
|
|
8036
|
+
}
|
|
8037
|
+
if (!path.isAbsolute(value)) {
|
|
8038
|
+
throw new Error(`${name} must be an absolute path: ${value}`);
|
|
8039
|
+
}
|
|
8040
|
+
}
|
|
8041
|
+
function resolveDefaultHomeDir() {
|
|
8042
|
+
return process.env.HOME ?? homedir6();
|
|
8043
|
+
}
|
|
8044
|
+
function isEqualOrInside(parentPath, candidatePath) {
|
|
8045
|
+
const relative4 = path.relative(parentPath, candidatePath);
|
|
8046
|
+
return relative4 === "" || !relative4.startsWith("..") && !path.isAbsolute(relative4);
|
|
8047
|
+
}
|
|
8048
|
+
async function resolveForComparison(absPath) {
|
|
8049
|
+
let current = absPath;
|
|
8050
|
+
while (path.dirname(current) !== current) {
|
|
8051
|
+
try {
|
|
8052
|
+
const realCurrent = await fs.realpath(current);
|
|
8053
|
+
const relativeMissingPath = path.relative(current, absPath);
|
|
8054
|
+
return path.resolve(realCurrent, relativeMissingPath);
|
|
8055
|
+
} catch {
|
|
8056
|
+
current = path.dirname(current);
|
|
8057
|
+
}
|
|
8058
|
+
}
|
|
8059
|
+
try {
|
|
8060
|
+
return await fs.realpath(current);
|
|
8061
|
+
} catch {
|
|
8062
|
+
return path.resolve(absPath);
|
|
8063
|
+
}
|
|
8064
|
+
}
|
|
8065
|
+
async function resolveUserLocalStorageRoot(options) {
|
|
8066
|
+
const activeRepositoryRoot = path.resolve(options.activeRepositoryRoot);
|
|
8067
|
+
assertAbsolutePath("activeRepositoryRoot", activeRepositoryRoot);
|
|
8068
|
+
const candidateRoot = options.storageRoot !== void 0 ? options.storageRoot : path.join(options.homeDir ?? resolveDefaultHomeDir(), ".robota");
|
|
8069
|
+
assertAbsolutePath("userLocalStorageRoot", candidateRoot);
|
|
8070
|
+
const resolvedRoot = path.resolve(candidateRoot);
|
|
8071
|
+
const comparableRoot = await resolveForComparison(resolvedRoot);
|
|
8072
|
+
const comparableRepositoryRoot = await resolveForComparison(activeRepositoryRoot);
|
|
8073
|
+
if (isEqualOrInside(comparableRepositoryRoot, comparableRoot)) {
|
|
8074
|
+
throw new Error(
|
|
8075
|
+
`User-local storage root must be outside the active repository: ${resolvedRoot}`
|
|
8076
|
+
);
|
|
8077
|
+
}
|
|
8078
|
+
return resolvedRoot;
|
|
8079
|
+
}
|
|
8080
|
+
function resolveCategoryLocation(root, category) {
|
|
8081
|
+
return path.join(root, category);
|
|
8082
|
+
}
|
|
8083
|
+
async function listItemSummaries(root, category) {
|
|
8084
|
+
const storageLocation = resolveCategoryLocation(root, category);
|
|
8085
|
+
let entries;
|
|
8086
|
+
try {
|
|
8087
|
+
entries = await fs.readdir(storageLocation, { withFileTypes: true });
|
|
8088
|
+
} catch {
|
|
8089
|
+
return [];
|
|
8090
|
+
}
|
|
8091
|
+
const summaries = await Promise.all(
|
|
8092
|
+
entries.map(async (entry) => {
|
|
8093
|
+
const itemLocation = path.join(storageLocation, entry.name);
|
|
8094
|
+
const stats = await fs.stat(itemLocation);
|
|
8095
|
+
const key = entry.name;
|
|
8096
|
+
return {
|
|
8097
|
+
root,
|
|
8098
|
+
category,
|
|
8099
|
+
key,
|
|
8100
|
+
summary: `${category}/${key}`,
|
|
8101
|
+
source: "user-local-storage",
|
|
8102
|
+
scope: "user",
|
|
8103
|
+
storageLocation: itemLocation,
|
|
8104
|
+
createdAt: formatIsoDate(stats.birthtime),
|
|
8105
|
+
lastUsedAt: formatIsoDate(stats.mtime),
|
|
8106
|
+
enabled: true,
|
|
8107
|
+
deleteAvailable: true,
|
|
8108
|
+
disableAvailable: false
|
|
8109
|
+
};
|
|
8110
|
+
})
|
|
8111
|
+
);
|
|
8112
|
+
return summaries.sort((left, right) => left.key.localeCompare(right.key));
|
|
8113
|
+
}
|
|
8114
|
+
async function inspectUserLocalStorage(options) {
|
|
8115
|
+
const root = await resolveUserLocalStorageRoot(options);
|
|
8116
|
+
const activeRepositoryRoot = path.resolve(options.activeRepositoryRoot);
|
|
8117
|
+
const createDirectories = options.createDirectories ?? true;
|
|
8118
|
+
if (createDirectories) {
|
|
8119
|
+
await fs.mkdir(root, { recursive: true });
|
|
8120
|
+
}
|
|
8121
|
+
const categories = await Promise.all(
|
|
8122
|
+
USER_LOCAL_STORAGE_CATEGORY_DEFINITIONS.map(
|
|
8123
|
+
async (definition) => {
|
|
8124
|
+
const storageLocation = resolveCategoryLocation(root, definition.category);
|
|
8125
|
+
if (createDirectories) {
|
|
8126
|
+
await fs.mkdir(storageLocation, { recursive: true });
|
|
8127
|
+
}
|
|
8128
|
+
const items = await listItemSummaries(root, definition.category);
|
|
8129
|
+
return {
|
|
8130
|
+
category: definition.category,
|
|
8131
|
+
purpose: definition.purpose,
|
|
8132
|
+
mayExecuteCommands: definition.mayExecuteCommands,
|
|
8133
|
+
storageLocation,
|
|
8134
|
+
itemCount: items.length,
|
|
8135
|
+
items
|
|
8136
|
+
};
|
|
8137
|
+
}
|
|
8138
|
+
)
|
|
8139
|
+
);
|
|
8140
|
+
return {
|
|
8141
|
+
root,
|
|
8142
|
+
activeRepositoryRoot,
|
|
8143
|
+
categories,
|
|
8144
|
+
generatedAt: formatIsoDate((options.now ?? (() => /* @__PURE__ */ new Date()))())
|
|
8145
|
+
};
|
|
8146
|
+
}
|
|
8147
|
+
|
|
8148
|
+
// src/user-local/memory.ts
|
|
8149
|
+
import { promises as fs2 } from "fs";
|
|
8150
|
+
import path2 from "path";
|
|
8151
|
+
|
|
8152
|
+
// src/user-local/memory-types.ts
|
|
8153
|
+
var USER_LOCAL_MEMORY_CATEGORIES = [
|
|
8154
|
+
"view-preference",
|
|
8155
|
+
"last-visible-cwd",
|
|
8156
|
+
"background-selection",
|
|
8157
|
+
"task-association",
|
|
8158
|
+
"display-preference",
|
|
8159
|
+
"inspection-choice"
|
|
8160
|
+
];
|
|
8161
|
+
|
|
8162
|
+
// src/user-local/memory.ts
|
|
8163
|
+
var MEMORY_STORAGE_CATEGORY = "memory-projections";
|
|
8164
|
+
var FILE_EXTENSION = ".json";
|
|
8165
|
+
var MEMORY_SCHEMA_VERSION = 1;
|
|
8166
|
+
var MAX_SEGMENT_LENGTH = 80;
|
|
8167
|
+
var MAX_SUMMARY_LENGTH = 240;
|
|
8168
|
+
var MAX_SOURCE_LENGTH = 80;
|
|
8169
|
+
var MAX_SCOPE_LENGTH = 120;
|
|
8170
|
+
var MAX_VALUE_SUMMARY_LENGTH = 240;
|
|
8171
|
+
var DEFAULT_SCOPE = "user";
|
|
8172
|
+
var SAFE_SEGMENT_PATTERN = /^[a-z0-9][a-z0-9._-]*$/u;
|
|
8173
|
+
var DISPLAY_NAVIGATION_RULES = {
|
|
8174
|
+
"view-preference": "May affect UI panel, filter, density, or sorting display/navigation only.",
|
|
8175
|
+
"last-visible-cwd": "May display or preselect an already visible workspace context only.",
|
|
8176
|
+
"background-selection": "May restore the selected background entry in local UI only.",
|
|
8177
|
+
"task-association": "May group visible tasks by a local association only.",
|
|
8178
|
+
"display-preference": "May affect local text wrapping, compactness, or visibility only.",
|
|
8179
|
+
"inspection-choice": "May affect inspection display choices only."
|
|
8180
|
+
};
|
|
8181
|
+
function formatIsoDate2(date) {
|
|
8182
|
+
return date.toISOString();
|
|
8183
|
+
}
|
|
8184
|
+
function isUserLocalMemoryCategory(value) {
|
|
8185
|
+
return USER_LOCAL_MEMORY_CATEGORIES.includes(value);
|
|
8186
|
+
}
|
|
8187
|
+
function assertUserLocalMemoryCategory(value) {
|
|
8188
|
+
if (!isUserLocalMemoryCategory(value)) {
|
|
8189
|
+
throw new Error(`Unsupported user-local memory category: ${value}`);
|
|
8190
|
+
}
|
|
8191
|
+
return value;
|
|
8192
|
+
}
|
|
8193
|
+
function assertSafeSegment(name, value) {
|
|
8194
|
+
const trimmed = value.trim();
|
|
8195
|
+
if (trimmed.length === 0) {
|
|
8196
|
+
throw new Error(`${name} must not be empty.`);
|
|
8197
|
+
}
|
|
8198
|
+
if (trimmed.length > MAX_SEGMENT_LENGTH || !SAFE_SEGMENT_PATTERN.test(trimmed)) {
|
|
8199
|
+
throw new Error(
|
|
8200
|
+
`${name} must use lowercase letters, numbers, dots, underscores, or hyphens: ${value}`
|
|
8201
|
+
);
|
|
8202
|
+
}
|
|
8203
|
+
return trimmed;
|
|
8204
|
+
}
|
|
8205
|
+
function boundedText(name, value, maxLength) {
|
|
8206
|
+
const normalized = value.trim().replace(/\s+/g, " ");
|
|
8207
|
+
if (normalized.length === 0) {
|
|
8208
|
+
throw new Error(`${name} must not be empty.`);
|
|
8209
|
+
}
|
|
8210
|
+
if (normalized.length > maxLength) {
|
|
8211
|
+
return normalized.slice(0, maxLength);
|
|
8212
|
+
}
|
|
8213
|
+
return normalized;
|
|
8214
|
+
}
|
|
8215
|
+
function summarizeValue(value) {
|
|
8216
|
+
return boundedText("value", value, MAX_VALUE_SUMMARY_LENGTH);
|
|
8217
|
+
}
|
|
8218
|
+
function memoryFileName(category, key) {
|
|
8219
|
+
return `${category}__${key}${FILE_EXTENSION}`;
|
|
8220
|
+
}
|
|
8221
|
+
async function resolveMemoryRoot(options) {
|
|
8222
|
+
const root = await resolveUserLocalStorageRoot(options);
|
|
8223
|
+
return {
|
|
8224
|
+
root,
|
|
8225
|
+
memoryRoot: path2.join(root, MEMORY_STORAGE_CATEGORY)
|
|
8226
|
+
};
|
|
8227
|
+
}
|
|
8228
|
+
function parseMemoryRecord(raw, storageLocation) {
|
|
8229
|
+
const record = JSON.parse(raw);
|
|
8230
|
+
const category = readString(record, "category");
|
|
8231
|
+
const schemaVersion = record["schemaVersion"];
|
|
8232
|
+
if (schemaVersion !== MEMORY_SCHEMA_VERSION) {
|
|
8233
|
+
throw new Error(`Unsupported user-local memory schema at ${storageLocation}`);
|
|
8234
|
+
}
|
|
8235
|
+
return {
|
|
8236
|
+
schemaVersion: MEMORY_SCHEMA_VERSION,
|
|
8237
|
+
category: assertUserLocalMemoryCategory(category),
|
|
8238
|
+
key: readString(record, "key"),
|
|
8239
|
+
value: readString(record, "value"),
|
|
8240
|
+
summary: readString(record, "summary"),
|
|
8241
|
+
source: readString(record, "source"),
|
|
8242
|
+
scope: readString(record, "scope"),
|
|
8243
|
+
createdAt: readString(record, "createdAt"),
|
|
8244
|
+
lastUsedAt: readString(record, "lastUsedAt"),
|
|
8245
|
+
enabled: readBoolean(record, "enabled")
|
|
8246
|
+
};
|
|
8247
|
+
}
|
|
8248
|
+
function readString(record, key) {
|
|
8249
|
+
const value = record[key];
|
|
8250
|
+
if (typeof value !== "string") {
|
|
8251
|
+
throw new Error(`Invalid user-local memory field: ${key}`);
|
|
8252
|
+
}
|
|
8253
|
+
return value;
|
|
8254
|
+
}
|
|
8255
|
+
function readBoolean(record, key) {
|
|
8256
|
+
const value = record[key];
|
|
8257
|
+
if (typeof value !== "boolean") {
|
|
8258
|
+
throw new Error(`Invalid user-local memory field: ${key}`);
|
|
8259
|
+
}
|
|
8260
|
+
return value;
|
|
8261
|
+
}
|
|
8262
|
+
function projectMemoryItem(root, storageLocation, item) {
|
|
8263
|
+
return {
|
|
8264
|
+
root,
|
|
8265
|
+
category: item.category,
|
|
8266
|
+
key: item.key,
|
|
8267
|
+
summary: item.summary,
|
|
8268
|
+
valueSummary: summarizeValue(item.value),
|
|
8269
|
+
source: item.source,
|
|
8270
|
+
scope: item.scope,
|
|
8271
|
+
storageLocation,
|
|
8272
|
+
createdAt: item.createdAt,
|
|
8273
|
+
lastUsedAt: item.lastUsedAt,
|
|
8274
|
+
enabled: item.enabled,
|
|
8275
|
+
displayNavigationRule: DISPLAY_NAVIGATION_RULES[item.category],
|
|
8276
|
+
commandExecutionEffect: "none",
|
|
8277
|
+
deleteAvailable: true,
|
|
8278
|
+
disableAvailable: true
|
|
8279
|
+
};
|
|
8280
|
+
}
|
|
8281
|
+
async function readMemoryFile(root, storageLocation) {
|
|
8282
|
+
return projectMemoryItem(
|
|
8283
|
+
root,
|
|
8284
|
+
storageLocation,
|
|
8285
|
+
parseMemoryRecord(await fs2.readFile(storageLocation, "utf8"), storageLocation)
|
|
8286
|
+
);
|
|
8287
|
+
}
|
|
8288
|
+
async function resolveMemoryFile(options) {
|
|
8289
|
+
const category = assertUserLocalMemoryCategory(options.category);
|
|
8290
|
+
const key = assertSafeSegment("key", options.key);
|
|
8291
|
+
const { root, memoryRoot: memoryRoot3 } = await resolveMemoryRoot(options);
|
|
8292
|
+
return {
|
|
8293
|
+
root,
|
|
8294
|
+
storageLocation: path2.join(memoryRoot3, memoryFileName(category, key))
|
|
8295
|
+
};
|
|
8296
|
+
}
|
|
8297
|
+
async function setUserLocalMemoryItem(options) {
|
|
8298
|
+
const category = assertUserLocalMemoryCategory(options.category);
|
|
8299
|
+
const key = assertSafeSegment("key", options.key);
|
|
8300
|
+
const summary = boundedText("summary", options.summary, MAX_SUMMARY_LENGTH);
|
|
8301
|
+
const source = boundedText("source", options.source, MAX_SOURCE_LENGTH);
|
|
8302
|
+
const scope = boundedText("scope", options.scope ?? DEFAULT_SCOPE, MAX_SCOPE_LENGTH);
|
|
8303
|
+
const value = summarizeValue(options.value);
|
|
8304
|
+
const now = formatIsoDate2((options.now ?? (() => /* @__PURE__ */ new Date()))());
|
|
8305
|
+
const { root, memoryRoot: memoryRoot3 } = await resolveMemoryRoot(options);
|
|
8306
|
+
const storageLocation = path2.join(memoryRoot3, memoryFileName(category, key));
|
|
8307
|
+
let createdAt = now;
|
|
8308
|
+
try {
|
|
8309
|
+
const existing = parseMemoryRecord(await fs2.readFile(storageLocation, "utf8"), storageLocation);
|
|
8310
|
+
createdAt = existing.createdAt;
|
|
8311
|
+
} catch (error) {
|
|
8312
|
+
if (error instanceof Error && error.message.includes("ENOENT")) {
|
|
8313
|
+
createdAt = now;
|
|
8314
|
+
} else {
|
|
8315
|
+
throw error;
|
|
8316
|
+
}
|
|
8317
|
+
}
|
|
8318
|
+
const item = {
|
|
8319
|
+
schemaVersion: MEMORY_SCHEMA_VERSION,
|
|
8320
|
+
category,
|
|
8321
|
+
key,
|
|
8322
|
+
value,
|
|
8323
|
+
summary,
|
|
8324
|
+
source,
|
|
8325
|
+
scope,
|
|
8326
|
+
createdAt,
|
|
8327
|
+
lastUsedAt: now,
|
|
8328
|
+
enabled: true
|
|
8329
|
+
};
|
|
8330
|
+
await fs2.mkdir(memoryRoot3, { recursive: true });
|
|
8331
|
+
await fs2.writeFile(storageLocation, `${JSON.stringify(item, null, 2)}
|
|
8332
|
+
`, "utf8");
|
|
8333
|
+
return projectMemoryItem(root, storageLocation, item);
|
|
8334
|
+
}
|
|
8335
|
+
async function listUserLocalMemoryItems(options) {
|
|
8336
|
+
const { root, memoryRoot: memoryRoot3 } = await resolveMemoryRoot(options);
|
|
8337
|
+
let entries;
|
|
8338
|
+
try {
|
|
8339
|
+
entries = await fs2.readdir(memoryRoot3, { withFileTypes: true });
|
|
8340
|
+
} catch {
|
|
8341
|
+
entries = [];
|
|
8342
|
+
}
|
|
8343
|
+
const items = await Promise.all(
|
|
8344
|
+
entries.filter((entry) => entry.isFile() && entry.name.endsWith(FILE_EXTENSION)).map((entry) => readMemoryFile(root, path2.join(memoryRoot3, entry.name)))
|
|
8345
|
+
);
|
|
8346
|
+
return {
|
|
8347
|
+
root,
|
|
8348
|
+
activeRepositoryRoot: path2.resolve(options.activeRepositoryRoot),
|
|
8349
|
+
items: items.sort(
|
|
8350
|
+
(left, right) => `${left.category}/${left.key}`.localeCompare(`${right.category}/${right.key}`)
|
|
8351
|
+
)
|
|
8352
|
+
};
|
|
8353
|
+
}
|
|
8354
|
+
async function inspectUserLocalMemoryItem(options) {
|
|
8355
|
+
const { root, storageLocation } = await resolveMemoryFile(options);
|
|
8356
|
+
return readMemoryFile(root, storageLocation);
|
|
8357
|
+
}
|
|
8358
|
+
async function disableUserLocalMemoryItem(options) {
|
|
8359
|
+
const { root, storageLocation } = await resolveMemoryFile(options);
|
|
8360
|
+
const existing = parseMemoryRecord(await fs2.readFile(storageLocation, "utf8"), storageLocation);
|
|
8361
|
+
const disabled = {
|
|
8362
|
+
...existing,
|
|
8363
|
+
enabled: false,
|
|
8364
|
+
lastUsedAt: formatIsoDate2((options.now ?? (() => /* @__PURE__ */ new Date()))())
|
|
8365
|
+
};
|
|
8366
|
+
await fs2.writeFile(storageLocation, `${JSON.stringify(disabled, null, 2)}
|
|
8367
|
+
`, "utf8");
|
|
8368
|
+
return projectMemoryItem(root, storageLocation, disabled);
|
|
8369
|
+
}
|
|
8370
|
+
async function deleteUserLocalMemoryItem(options) {
|
|
8371
|
+
const { storageLocation } = await resolveMemoryFile(options);
|
|
8372
|
+
await fs2.rm(storageLocation);
|
|
8373
|
+
return {
|
|
8374
|
+
category: options.category,
|
|
8375
|
+
key: options.key,
|
|
8376
|
+
deleted: true
|
|
8377
|
+
};
|
|
8378
|
+
}
|
|
8379
|
+
async function readEnabledUserLocalMemoryItem(options) {
|
|
8380
|
+
const item = await inspectUserLocalMemoryItem(options);
|
|
8381
|
+
return item.enabled ? item : null;
|
|
8382
|
+
}
|
|
8383
|
+
|
|
7561
8384
|
// src/self-hosting/self-hosting-verification.ts
|
|
7562
8385
|
var DEFAULT_BASE_REF = "origin/develop";
|
|
7563
8386
|
var PACKAGE_VERIFY_COMMANDS = ["test", "typecheck", "build"];
|
|
@@ -7776,6 +8599,7 @@ export {
|
|
|
7776
8599
|
DEFAULT_AUTO_COMPACT_THRESHOLD,
|
|
7777
8600
|
DEFAULT_BACKGROUND_TASK_LOG_PAGE_SIZE,
|
|
7778
8601
|
DEFAULT_STATUS_LINE_COMMAND_SETTINGS,
|
|
8602
|
+
EXECUTION_ORIGIN_METADATA_KEYS,
|
|
7779
8603
|
EXIT_COMMAND_DESCRIPTION,
|
|
7780
8604
|
EditCheckpointStore,
|
|
7781
8605
|
HELP_COMMAND_DESCRIPTION,
|
|
@@ -7813,6 +8637,9 @@ export {
|
|
|
7813
8637
|
SkillCommandSource,
|
|
7814
8638
|
SubagentManager3 as SubagentManager,
|
|
7815
8639
|
SystemCommandExecutor,
|
|
8640
|
+
USER_LOCAL_MEMORY_CATEGORIES,
|
|
8641
|
+
USER_LOCAL_STORAGE_CATEGORIES,
|
|
8642
|
+
USER_LOCAL_STORAGE_CATEGORY_DEFINITIONS,
|
|
7816
8643
|
VALIDATE_SESSION_COMMAND_DESCRIPTION,
|
|
7817
8644
|
VALID_PERMISSION_MODES,
|
|
7818
8645
|
WorktreeSubagentRunner,
|
|
@@ -7837,7 +8664,9 @@ export {
|
|
|
7837
8664
|
closeCommandBackgroundTask,
|
|
7838
8665
|
compactCommandContext,
|
|
7839
8666
|
createAgentTool,
|
|
8667
|
+
createBackgroundGroupExecutionEntryId,
|
|
7840
8668
|
createBackgroundProcessTool,
|
|
8669
|
+
createBackgroundTaskExecutionEntryId,
|
|
7841
8670
|
createBackgroundTaskLogPage,
|
|
7842
8671
|
createBuiltinCommandModule,
|
|
7843
8672
|
createCommandExecutionTool,
|
|
@@ -7846,7 +8675,13 @@ export {
|
|
|
7846
8675
|
createCommandProjectMemoryStore,
|
|
7847
8676
|
createContextReferenceItem,
|
|
7848
8677
|
createDefaultTools,
|
|
8678
|
+
createExecutionOriginMetadata,
|
|
8679
|
+
createExecutionWorkspaceSnapshot,
|
|
8680
|
+
createExecutionWorkspaceTaskSpawner,
|
|
7849
8681
|
createLimitedOutputCapture,
|
|
8682
|
+
createLineDetailPage,
|
|
8683
|
+
createMainThreadDetailPage,
|
|
8684
|
+
createMainThreadExecutionEntryId,
|
|
7850
8685
|
createModelCommandToolProjection,
|
|
7851
8686
|
createPluginRegistryReloadRequestedEffect,
|
|
7852
8687
|
createPluginTuiRequestedEffect,
|
|
@@ -7864,6 +8699,8 @@ export {
|
|
|
7864
8699
|
createSystemCommands,
|
|
7865
8700
|
createWorktreeSubagentRunner,
|
|
7866
8701
|
deleteProviderProfile,
|
|
8702
|
+
deleteUserLocalMemoryItem,
|
|
8703
|
+
disableUserLocalMemoryItem,
|
|
7867
8704
|
discoverTaskFiles,
|
|
7868
8705
|
evaluateReversibleToolSafety,
|
|
7869
8706
|
executeSkill,
|
|
@@ -7893,12 +8730,14 @@ export {
|
|
|
7893
8730
|
hasSensitiveCommandMemoryContent,
|
|
7894
8731
|
hasUsableSecretReference,
|
|
7895
8732
|
inspectCommandEditCheckpoint,
|
|
8733
|
+
inspectUserLocalMemoryItem,
|
|
8734
|
+
inspectUserLocalStorage,
|
|
7896
8735
|
isCommandMemoryType,
|
|
7897
8736
|
isEnvReference,
|
|
7898
8737
|
isMemoryType,
|
|
7899
8738
|
isPermissionMode,
|
|
7900
8739
|
isStatusLineCommandSettingsPatch,
|
|
7901
|
-
|
|
8740
|
+
isTerminalBackgroundTaskStatus3 as isTerminalBackgroundTaskStatus,
|
|
7902
8741
|
listActiveContextReferences,
|
|
7903
8742
|
listCommandBackgroundTasks,
|
|
7904
8743
|
listCommandContextReferences,
|
|
@@ -7906,10 +8745,12 @@ export {
|
|
|
7906
8745
|
listCommandSessionAllowedTools,
|
|
7907
8746
|
listCommandUsedMemoryReferences,
|
|
7908
8747
|
listResumableSessionSummaries,
|
|
8748
|
+
listUserLocalMemoryItems,
|
|
7909
8749
|
loadTaskContext,
|
|
7910
8750
|
mergeProviderPatch,
|
|
7911
8751
|
normalizeModelCommandName,
|
|
7912
8752
|
parseCommandBackgroundLogCursor,
|
|
8753
|
+
parseExecutionWorkspaceEntryId,
|
|
7913
8754
|
parseFrontmatter,
|
|
7914
8755
|
parseLanguageArgument,
|
|
7915
8756
|
parsePermissionModeArgument,
|
|
@@ -7929,6 +8770,7 @@ export {
|
|
|
7929
8770
|
readCommandPermissionsState,
|
|
7930
8771
|
readCommandSessionInfo,
|
|
7931
8772
|
readCurrentGitBranch,
|
|
8773
|
+
readEnabledUserLocalMemoryItem,
|
|
7932
8774
|
recordCommandMemoryEvent,
|
|
7933
8775
|
removeCommandContextReference,
|
|
7934
8776
|
removeContextReference,
|
|
@@ -7944,6 +8786,7 @@ export {
|
|
|
7944
8786
|
resolveProviderSetupSelection,
|
|
7945
8787
|
resolveSessionIdByIdOrName,
|
|
7946
8788
|
resolveSubagentLogDir,
|
|
8789
|
+
resolveUserLocalStorageRoot,
|
|
7947
8790
|
restoreCommandEditCheckpoint,
|
|
7948
8791
|
retrieveAgentToolDeps,
|
|
7949
8792
|
rollbackCommandEditCheckpoint,
|
|
@@ -7952,6 +8795,7 @@ export {
|
|
|
7952
8795
|
selectRelevantTasks,
|
|
7953
8796
|
setCommandAutoCompactThreshold,
|
|
7954
8797
|
setCurrentProvider,
|
|
8798
|
+
setUserLocalMemoryItem,
|
|
7955
8799
|
storeAgentToolDeps,
|
|
7956
8800
|
submitProviderSetupValue,
|
|
7957
8801
|
substituteVariables,
|