@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.
@@ -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, path) {
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(path);
551
+ return context.addContextReference(path3);
552
552
  }
553
- function removeCommandContextReference(context, path) {
554
- return context.removeContextReference?.(path) ?? {};
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 path = this.getIndexPath();
1465
- if (!existsSync3(path)) {
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(path, "utf8");
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 path = join5(this.getTopicsPath(), `${normalized}${TOPIC_EXTENSION}`);
1494
- if (!existsSync3(path)) return "";
1495
- return readFileSync3(path, "utf8").trimEnd();
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 cloneGroup(group) {
2546
- return {
2547
- ...group,
2548
- taskIds: [...group.taskIds],
2549
- results: group.results.map((result) => ({
2550
- ...result,
2551
- ...result.error ? { error: { ...result.error } } : {}
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
- return resolveLegacyProvider(merged);
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 ${path}: ${result.error.message}`);
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((path) => parseTaskFile(path, cwd));
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 on failure. */
5168
- readManifest(path) {
5169
- try {
5170
- const raw = readFileSync11(path, "utf-8");
5171
- const data = JSON.parse(raw);
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
- try {
5252
- const raw = readFileSync11(hooksPath, "utf-8");
5253
- const data = JSON.parse(raw);
5254
- if (typeof data === "object" && data !== null) {
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 if present. Checks `.mcp.json` at plugin root first. */
5574
+ /** Load MCP server configuration from `.mcp.json` at the plugin root if present. */
5263
5575
  loadMcpConfig(pluginDir) {
5264
- const primaryPath = join12(pluginDir, ".mcp.json");
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
- try {
5269
- const raw = readFileSync11(mcpPath, "utf-8");
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
- try {
5280
- const entries = readdirSync6(agentsDir, { withFileTypes: true });
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(path) {
5729
- const raw = readFileSync14(path, "utf-8");
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
- ...config,
6132
+ ...mergedConfig,
5831
6133
  hooks: mergeHooksIntoConfig(
5832
- config.hooks,
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, path, cwd) {
6001
- const result = await resolvePromptFileReferencePaths([path], {
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(path) {
6396
+ function statSafe(path3) {
6095
6397
  try {
6096
- return statSync2(path);
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 path = join19(dir, MANIFEST_FILE);
6268
- const tmp = `${path}.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, path);
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(path) {
6598
+ async function pathExists(path3) {
6297
6599
  try {
6298
- await access(path, constants.F_OK);
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(path) {
6613
+ function readJsonManifest(path3) {
6312
6614
  try {
6313
- const raw = readFileSync15(path, "utf8");
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(path) {
7093
+ async addContextReference(path3) {
6780
7094
  const { references, result } = await addInteractiveContextReference(
6781
7095
  this.contextReferences,
6782
- path,
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(path) {
6790
- const result = removeContextReference(this.contextReferences, path);
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: this.getSessionOrThrow().getSessionId(),
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
- isTerminalBackgroundTaskStatus2 as isTerminalBackgroundTaskStatus,
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,