@ganglion/xacpx 0.12.1 → 0.14.0

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/cli.js CHANGED
@@ -141,6 +141,7 @@ ${detail}`,
141
141
  sessionBlockedByTasks: (alias, count) => `Session "${alias}" has ${count} unfinished task(s). Cancel or wait for them to complete first.`,
142
142
  sessionBlockedByTasksHint: "Use /tasks to list tasks, or /task cancel <id> to cancel one.",
143
143
  sessionRemoved: (alias) => `Session "${alias}" removed.`,
144
+ sessionArchived: (alias) => `Archived session "${alias}". Send a message to restore it.`,
144
145
  sessionRemovedWasActive: "This was the active session. Its chat context has been cleared.",
145
146
  sessionRemovedWasActivePromoted: (alias) => `This was the active session. Switched back to the previous session "${alias}".`,
146
147
  sessionTransportShared: (transportSession, count) => `Note: backend session "${transportSession}" is still referenced by ${count} other session(s) and was not closed.`,
@@ -1236,6 +1237,7 @@ ${detail}`,
1236
1237
  sessionBlockedByTasks: (alias, count) => `会话「${alias}」下还有 ${count} 个未结束的任务,请先取消或等待完成。`,
1237
1238
  sessionBlockedByTasksHint: "使用 /tasks 查看任务列表,或 /task cancel <id> 取消任务。",
1238
1239
  sessionRemoved: (alias) => `已删除会话「${alias}」。`,
1240
+ sessionArchived: (alias) => `已归档会话「${alias}」。发送消息即可恢复。`,
1239
1241
  sessionRemovedWasActive: "该会话是当前活跃会话,已自动清除相关聊天上下文。",
1240
1242
  sessionRemovedWasActivePromoted: (alias) => `该会话是当前活跃会话,已切换回上一个会话「${alias}」。`,
1241
1243
  sessionTransportShared: (transportSession, count) => `提示:后端会话「${transportSession}」仍被其他 ${count} 个会话引用,未关闭。`,
@@ -4695,6 +4697,7 @@ function parseConfig(raw, options = {}) {
4695
4697
  ...typeof transport.command === "string" ? { command: transport.command } : {},
4696
4698
  ...typeof transport.sessionInitTimeoutMs === "number" ? { sessionInitTimeoutMs: transport.sessionInitTimeoutMs } : {},
4697
4699
  ...typeof transport.permissionPolicy === "string" ? { permissionPolicy: transport.permissionPolicy } : {},
4700
+ ...typeof transport.preferLocalAgents === "boolean" ? { preferLocalAgents: transport.preferLocalAgents } : {},
4698
4701
  type: transportType,
4699
4702
  permissionMode,
4700
4703
  nonInteractivePermissions,
@@ -20399,6 +20402,9 @@ function parseCommand(input) {
20399
20402
  if (command === "/session" && parts[1] === "rm" && parts[2] && parts.length === 3) {
20400
20403
  return { kind: "session.rm", alias: parts[2] };
20401
20404
  }
20405
+ if (command === "/session" && parts[1] === "archive" && parts[2] && parts.length === 3) {
20406
+ return { kind: "session.archive", alias: parts[2] };
20407
+ }
20402
20408
  if (command === "/ssn") {
20403
20409
  if (parts.length === 1) {
20404
20410
  return { kind: "session.native.list" };
@@ -20645,7 +20651,7 @@ function parseCommand(input) {
20645
20651
  return { kind: "session.shortcut.new", agent: parts[2], ...shortcutTarget };
20646
20652
  }
20647
20653
  }
20648
- if (command === "/session" && parts[1] && parts[1] !== "new" && parts[1] !== "attach" && parts[1] !== "reset" && parts[1] !== "rm") {
20654
+ if (command === "/session" && parts[1] && parts[1] !== "new" && parts[1] !== "attach" && parts[1] !== "reset" && parts[1] !== "rm" && parts[1] !== "archive") {
20649
20655
  const shortcutTarget = readSessionShortcutTarget(parts, 2);
20650
20656
  if (shortcutTarget) {
20651
20657
  return { kind: "session.shortcut", agent: parts[1], ...shortcutTarget };
@@ -21109,6 +21115,7 @@ var init_command_policy = __esm(() => {
21109
21115
  COMMAND_KIND_TO_LABEL = {
21110
21116
  "session.reset": "/clear",
21111
21117
  "session.rm": "/session rm",
21118
+ "session.archive": "/session archive",
21112
21119
  "session.tail": "/session tail",
21113
21120
  "replymode.set": "/replymode",
21114
21121
  "replymode.reset": "/replymode reset",
@@ -22326,9 +22333,9 @@ async function handleSessionRemove(context, chatKey, alias) {
22326
22333
  }
22327
22334
  let transportTeardownWarning;
22328
22335
  const shouldTeardownTransport = sharedAliasCount === 0;
22329
- if (shouldTeardownTransport && context.transport.removeSession) {
22336
+ if (shouldTeardownTransport && context.transport.deleteSession) {
22330
22337
  try {
22331
- await context.transport.removeSession(session3);
22338
+ await context.transport.deleteSession(session3);
22332
22339
  } catch (error2) {
22333
22340
  const message = error2 instanceof Error ? error2.message : String(error2);
22334
22341
  transportTeardownWarning = message;
@@ -22361,7 +22368,19 @@ async function handleSessionRemove(context, chatKey, alias) {
22361
22368
  return { text: lines.join(`
22362
22369
  `) };
22363
22370
  }
22364
- async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
22371
+ async function handleSessionArchive(context, chatKey, alias, archive) {
22372
+ const internalAlias = await context.sessions.resolveAliasForChat(chatKey, alias);
22373
+ const session3 = await context.sessions.getSession(internalAlias);
22374
+ if (!session3) {
22375
+ return { text: t().session.sessionNotFound(alias) };
22376
+ }
22377
+ await archive(internalAlias);
22378
+ return { text: t().session.sessionArchived(alias) };
22379
+ }
22380
+ async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
22381
+ if (session3.archived) {
22382
+ await context.sessions.setArchived(session3.alias, false);
22383
+ }
22365
22384
  const effectiveReplyMode = resolveEffectiveReplyMode(context.config, chatKey, session3.replyMode);
22366
22385
  if (!session3.replyMode)
22367
22386
  session3.replyMode = effectiveReplyMode;
@@ -22393,7 +22412,7 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
22393
22412
  const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session3, chatKey, text, replyContextToken, accountId);
22394
22413
  try {
22395
22414
  const replyContext = transportReply && context.quota && getChannelIdFromChatKey(chatKey) === "weixin" ? { chatKey, quota: context.quota } : undefined;
22396
- const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage);
22415
+ const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands);
22397
22416
  if (claimHumanReply) {
22398
22417
  try {
22399
22418
  await context.orchestration?.claimActiveHumanReply?.(claimHumanReply);
@@ -22413,23 +22432,23 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
22413
22432
  throw error2;
22414
22433
  }
22415
22434
  }
22416
- async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
22435
+ async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
22417
22436
  try {
22418
- return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
22437
+ return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
22419
22438
  } catch (error2) {
22420
22439
  const recovered = await context.recovery.tryRecoverMissingSession(session3, error2);
22421
22440
  if (recovered) {
22422
- return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
22441
+ return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
22423
22442
  }
22424
22443
  return context.recovery.renderTransportError(session3, error2);
22425
22444
  }
22426
22445
  }
22427
- async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
22446
+ async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
22428
22447
  const session3 = metadata?.boundSessionAlias ? context.sessions.getResolvedSessionByInternalAlias(metadata.boundSessionAlias) : await context.sessions.getCurrentSession(chatKey);
22429
22448
  if (!session3) {
22430
22449
  return { text: t().session.noCurrent };
22431
22450
  }
22432
- return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
22451
+ return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
22433
22452
  }
22434
22453
  function toCoordinatorRouteChatMetadata(metadata) {
22435
22454
  if (!metadata) {
@@ -24688,7 +24707,7 @@ class CommandRouter {
24688
24707
  this.logger = logger2 ?? createNoopAppLogger();
24689
24708
  this.activeTurns = activeTurns;
24690
24709
  }
24691
- async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
24710
+ async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands) {
24692
24711
  const startedAt = Date.now();
24693
24712
  let command = parseCommand(input);
24694
24713
  if (metadata?.channel === "control" && command.kind !== "prompt") {
@@ -24795,6 +24814,8 @@ class CommandRouter {
24795
24814
  return await handleSessionTail(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.lines);
24796
24815
  case "session.rm":
24797
24816
  return await handleSessionRemove(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias);
24817
+ case "session.archive":
24818
+ return await handleSessionArchive(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias, (internalAlias) => this.archiveSessionWithTransport(internalAlias));
24798
24819
  case "groups":
24799
24820
  return await handleGroupList(this.createHandlerContext(), chatKey, command.filter);
24800
24821
  case "group.new":
@@ -24848,16 +24869,16 @@ class CommandRouter {
24848
24869
  ...this.sessions.resolveSession(descriptor.alias, descriptor.agent, descriptor.workspace, descriptor.transportSession),
24849
24870
  transient: true
24850
24871
  };
24851
- return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
24872
+ return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
24852
24873
  }
24853
24874
  if (metadata?.scheduledSessionAlias) {
24854
24875
  const scheduledSession = await this.sessions.getSession(metadata.scheduledSessionAlias);
24855
24876
  if (!scheduledSession) {
24856
24877
  throw new Error(`session "${metadata.scheduledSessionAlias}" not found for scheduled prompt`);
24857
24878
  }
24858
- return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
24879
+ return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
24859
24880
  }
24860
- return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
24881
+ return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
24861
24882
  }
24862
24883
  }
24863
24884
  });
@@ -24943,6 +24964,82 @@ class CommandRouter {
24943
24964
  await release();
24944
24965
  }
24945
24966
  }
24967
+ async removeSessionWithTransport(internalAlias) {
24968
+ const session3 = await this.sessions.getSession(internalAlias);
24969
+ if (!session3) {
24970
+ throw new Error(`session "${internalAlias}" does not exist`);
24971
+ }
24972
+ if (this.orchestration) {
24973
+ const blocking = await this.orchestration.listSessionBlockingTasks(session3.transportSession);
24974
+ if (blocking.length > 0) {
24975
+ throw new Error(`session "${internalAlias}" has ${blocking.length} blocking task(s); cancel them before deleting`);
24976
+ }
24977
+ }
24978
+ const sharedAliasCount = this.sessions.countAliasesSharingTransport(session3.transportSession, internalAlias);
24979
+ const { wasActive } = await this.sessions.removeSession(internalAlias);
24980
+ if (this.orchestration) {
24981
+ try {
24982
+ await this.orchestration.purgeSessionReferences(session3.transportSession);
24983
+ } catch (error2) {
24984
+ await this.logger.error("session.orchestration_purge_failed", "failed to purge orchestration references after web remove", {
24985
+ alias: internalAlias,
24986
+ transportSession: session3.transportSession,
24987
+ message: error2 instanceof Error ? error2.message : String(error2)
24988
+ });
24989
+ }
24990
+ }
24991
+ let transportTornDown = false;
24992
+ let transportTeardownWarning;
24993
+ if (sharedAliasCount === 0 && this.transport.deleteSession) {
24994
+ try {
24995
+ await this.transport.deleteSession(session3);
24996
+ transportTornDown = true;
24997
+ } catch (error2) {
24998
+ transportTeardownWarning = error2 instanceof Error ? error2.message : String(error2);
24999
+ await this.logger.error("session.transport_delete_failed", "failed to delete acpx session after logical remove", {
25000
+ alias: internalAlias,
25001
+ transportSession: session3.transportSession,
25002
+ message: transportTeardownWarning
25003
+ });
25004
+ }
25005
+ }
25006
+ return {
25007
+ wasActive,
25008
+ sharedAliasCount,
25009
+ transportTornDown,
25010
+ ...transportTeardownWarning ? { transportTeardownWarning } : {}
25011
+ };
25012
+ }
25013
+ async archiveSessionWithTransport(internalAlias) {
25014
+ const session3 = await this.sessions.getSession(internalAlias);
25015
+ if (!session3) {
25016
+ throw new Error(`session "${internalAlias}" does not exist`);
25017
+ }
25018
+ if (this.activeTurns?.isActiveAnywhere(internalAlias)) {
25019
+ throw new Error(`session "${internalAlias}" has a running turn; stop it before archiving`);
25020
+ }
25021
+ const shared = this.sessions.countAliasesSharingTransport(session3.transportSession, internalAlias) > 0;
25022
+ if (!shared) {
25023
+ try {
25024
+ await this.transport.cancel(session3);
25025
+ } catch {}
25026
+ if (this.transport.removeSession) {
25027
+ try {
25028
+ await this.transport.removeSession(session3);
25029
+ } catch (error2) {
25030
+ await this.logger.error("session.archive_close_failed", "failed to close acpx session on archive", {
25031
+ alias: internalAlias,
25032
+ transportSession: session3.transportSession,
25033
+ message: error2 instanceof Error ? error2.message : String(error2)
25034
+ });
25035
+ }
25036
+ }
25037
+ }
25038
+ await this.sessions.setArchived(internalAlias, true);
25039
+ }
25040
+ async unarchiveSession(internalAlias) {
25041
+ await this.sessions.setArchived(internalAlias, false);
25042
+ }
24946
25043
  async listNativeSessionsForControl(agent3, workspace3) {
24947
25044
  const listAgentSessions = this.transport.listAgentSessions?.bind(this.transport);
24948
25045
  if (!listAgentSessions)
@@ -25005,7 +25102,7 @@ class CommandRouter {
25005
25102
  setModelTransportSession: (session3, modelId) => this.setModelTransportSession(session3, modelId),
25006
25103
  getModelTransportSession: (session3) => this.getModelTransportSession(session3),
25007
25104
  cancelTransportSession: (session3) => this.cancelTransportSession(session3),
25008
- promptTransportSession: (session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride, onPlan, onUsage) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan, onPlan, onUsage)
25105
+ promptTransportSession: (session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride, onPlan, onUsage, onCommands) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan, onPlan, onUsage, onCommands)
25009
25106
  };
25010
25107
  }
25011
25108
  createSessionRenderRecoveryOps() {
@@ -25184,7 +25281,7 @@ class CommandRouter {
25184
25281
  async checkTransportSession(session3) {
25185
25282
  return await this.measureTransportCall("has_session", session3, () => this.transport.hasSession(session3));
25186
25283
  }
25187
- async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
25284
+ async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands) {
25188
25285
  session3.mcpCoordinatorSession ??= stableCoordinatorSession(session3.transportSession);
25189
25286
  let done = false;
25190
25287
  let abortRequested = false;
@@ -25243,7 +25340,8 @@ class CommandRouter {
25243
25340
  ...onToolEvent ? { onToolEvent } : {},
25244
25341
  ...onThought ? { onThought } : {},
25245
25342
  ...onPlan ? { onPlan } : {},
25246
- ...onUsage ? { onUsage } : {}
25343
+ ...onUsage ? { onUsage } : {},
25344
+ ...onCommands ? { onCommands } : {}
25247
25345
  }));
25248
25346
  } catch (error2) {
25249
25347
  localOutcome = isAbortError2(error2) || abortRequested ? "aborted" : "error";
@@ -25429,7 +25527,7 @@ class ConsoleAgent {
25429
25527
  ...m.fileName ? { fileName: m.fileName } : {}
25430
25528
  })) : undefined;
25431
25529
  request.perfSpan?.mark("agent.dispatched");
25432
- return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent, request.onThought, request.perfSpan, request.onPlan, request.onUsage);
25530
+ return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent, request.onThought, request.perfSpan, request.onPlan, request.onUsage, request.onCommands);
25433
25531
  }
25434
25532
  isKnownCommand(text) {
25435
25533
  return isKnownXacpxCommandText(text);
@@ -29659,6 +29757,10 @@ class SessionService {
29659
29757
  const previousCurrent = prevCtx?.current_session;
29660
29758
  const carriedPrevious = previousCurrent && previousCurrent !== internalAlias ? previousCurrent : prevCtx?.previous_session;
29661
29759
  session3.last_used_at = new Date().toISOString();
29760
+ if (session3.archived) {
29761
+ delete session3.archived;
29762
+ delete session3.archived_at;
29763
+ }
29662
29764
  const nextCtx = { ...prevCtx, current_session: internalAlias };
29663
29765
  if (carriedPrevious) {
29664
29766
  nextCtx.previous_session = carriedPrevious;
@@ -29860,6 +29962,22 @@ class SessionService {
29860
29962
  }
29861
29963
  return count;
29862
29964
  }
29965
+ async setArchived(alias, archived) {
29966
+ await this.mutate(async () => {
29967
+ const session3 = this.state.sessions[alias];
29968
+ if (!session3) {
29969
+ throw new Error(`session "${alias}" does not exist`);
29970
+ }
29971
+ if (archived) {
29972
+ session3.archived = true;
29973
+ session3.archived_at = new Date(this.now()).toISOString();
29974
+ } else {
29975
+ delete session3.archived;
29976
+ delete session3.archived_at;
29977
+ }
29978
+ await this.persist();
29979
+ });
29980
+ }
29863
29981
  async removeSession(alias) {
29864
29982
  return await this.mutate(async () => {
29865
29983
  const session3 = this.state.sessions[alias];
@@ -29974,7 +30092,8 @@ class SessionService {
29974
30092
  modeId: session3.mode_id,
29975
30093
  replyMode: session3.reply_mode,
29976
30094
  effectiveReplyMode,
29977
- cwd: workspaceConfig.cwd
30095
+ cwd: workspaceConfig.cwd,
30096
+ archived: session3.archived === true
29978
30097
  };
29979
30098
  }
29980
30099
  async setSessionModel(alias, modelId) {
@@ -30489,6 +30608,10 @@ function encodeBridgePromptUsageEvent(event) {
30489
30608
  return `${JSON.stringify(event)}
30490
30609
  `;
30491
30610
  }
30611
+ function encodeBridgePromptCommandsEvent(event) {
30612
+ return `${JSON.stringify(event)}
30613
+ `;
30614
+ }
30492
30615
  function encodeBridgeSessionProgressEvent(event) {
30493
30616
  return `${JSON.stringify(event)}
30494
30617
  `;
@@ -30795,7 +30918,14 @@ class AcpxBridgeClient {
30795
30918
  pending.onEvent?.({
30796
30919
  type: "prompt.usage",
30797
30920
  used: message.used,
30798
- size: message.size
30921
+ size: message.size,
30922
+ ...message.cost ? { cost: message.cost } : {},
30923
+ ...message.breakdown ? { breakdown: message.breakdown } : {}
30924
+ });
30925
+ } else if (message.event === "prompt.commands") {
30926
+ pending.onEvent?.({
30927
+ type: "prompt.commands",
30928
+ commands: message.commands
30799
30929
  });
30800
30930
  } else if (message.event === "session.progress") {
30801
30931
  pending.onEvent?.({
@@ -31258,6 +31388,8 @@ class AcpxBridgeTransport {
31258
31388
  let planChain = Promise.resolve();
31259
31389
  let usageError;
31260
31390
  let usageChain = Promise.resolve();
31391
+ let commandsError;
31392
+ let commandsChain = Promise.resolve();
31261
31393
  let toolEventMode = resolveToolEventMode(options);
31262
31394
  if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
31263
31395
  toolEventMode = "text";
@@ -31313,19 +31445,30 @@ class AcpxBridgeTransport {
31313
31445
  if (event.type === "prompt.usage") {
31314
31446
  const onUsage = options?.onUsage;
31315
31447
  if (onUsage) {
31316
- const usage = { used: event.used, size: event.size };
31448
+ const usage = { used: event.used, size: event.size, ...event.cost ? { cost: event.cost } : {}, ...event.breakdown ? { breakdown: event.breakdown } : {} };
31317
31449
  usageChain = usageChain.then(() => onUsage(usage)).catch((error2) => {
31318
31450
  usageError ??= error2;
31319
31451
  });
31320
31452
  }
31321
31453
  return;
31322
31454
  }
31455
+ if (event.type === "prompt.commands") {
31456
+ const onCommands = options?.onCommands;
31457
+ if (onCommands) {
31458
+ const commands = event.commands;
31459
+ commandsChain = commandsChain.then(() => onCommands(commands)).catch((error2) => {
31460
+ commandsError ??= error2;
31461
+ });
31462
+ }
31463
+ return;
31464
+ }
31323
31465
  });
31324
31466
  await segmentChain;
31325
31467
  await toolEventChain;
31326
31468
  await thoughtChain;
31327
31469
  await planChain;
31328
31470
  await usageChain;
31471
+ await commandsChain;
31329
31472
  if (sink) {
31330
31473
  const { overflowCount } = sink.finalize();
31331
31474
  await sink.drain({ timeoutMs: 30000 });
@@ -31349,6 +31492,9 @@ class AcpxBridgeTransport {
31349
31492
  if (usageError) {
31350
31493
  throw usageError;
31351
31494
  }
31495
+ if (commandsError) {
31496
+ throw commandsError;
31497
+ }
31352
31498
  return { text: summary ? `${summary}
31353
31499
 
31354
31500
  ${result.text}` : "" };
@@ -31368,6 +31514,9 @@ ${result.text}` : "" };
31368
31514
  if (usageError) {
31369
31515
  throw usageError;
31370
31516
  }
31517
+ if (commandsError) {
31518
+ throw commandsError;
31519
+ }
31371
31520
  return result;
31372
31521
  }
31373
31522
  async setMode(session3, modeId) {
@@ -31391,6 +31540,9 @@ ${result.text}` : "" };
31391
31540
  async removeSession(session3) {
31392
31541
  await this.client.request("removeSession", this.toParams(session3));
31393
31542
  }
31543
+ async deleteSession(session3) {
31544
+ await this.client.request("deleteSession", this.toParams(session3));
31545
+ }
31394
31546
  async getAgentSessionId(session3) {
31395
31547
  const result = await this.client.request("getAgentSessionId", this.toParams(session3));
31396
31548
  return result.agentSessionId;
@@ -31574,6 +31726,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
31574
31726
  let onThought;
31575
31727
  let onPlan;
31576
31728
  let onUsage;
31729
+ let onCommands;
31577
31730
  let rawStream = false;
31578
31731
  if (options === undefined) {
31579
31732
  toolEventMode = "text";
@@ -31586,6 +31739,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
31586
31739
  onThought = options.onThought;
31587
31740
  onPlan = options.onPlan;
31588
31741
  onUsage = options.onUsage;
31742
+ onCommands = options.onCommands;
31589
31743
  rawStream = options.rawStream ?? false;
31590
31744
  toolEventMode = resolveToolEventMode({
31591
31745
  toolEventMode: options.mode,
@@ -31605,6 +31759,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
31605
31759
  onThought,
31606
31760
  onPlan,
31607
31761
  onUsage,
31762
+ onCommands,
31608
31763
  finalize() {
31609
31764
  if (this.pendingLine.trim().length > 0) {
31610
31765
  parseStreamingChunks(this, this.pendingLine);
@@ -31672,8 +31827,17 @@ function parseStreamingChunks(state, line) {
31672
31827
  if (update.sessionUpdate === "usage_update") {
31673
31828
  const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
31674
31829
  const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
31675
- if (used !== undefined && size !== undefined && size > 0)
31676
- state.onUsage?.({ used, size });
31830
+ if (used !== undefined && size !== undefined && size > 0) {
31831
+ const cost = normalizeUsageCost(update.cost);
31832
+ const breakdown = normalizeUsageBreakdown(update._meta?.usage);
31833
+ state.onUsage?.({ used, size, ...cost ? { cost } : {}, ...breakdown ? { breakdown } : {} });
31834
+ }
31835
+ return;
31836
+ }
31837
+ if (update.sessionUpdate === "available_commands_update") {
31838
+ if (Array.isArray(update.availableCommands)) {
31839
+ state.onCommands?.(normalizeAgentCommands(update.availableCommands));
31840
+ }
31677
31841
  return;
31678
31842
  }
31679
31843
  const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
@@ -31840,6 +32004,52 @@ function readFirstStringArray(record3, keys) {
31840
32004
  }
31841
32005
  return;
31842
32006
  }
32007
+ function asFiniteNumber(value) {
32008
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
32009
+ }
32010
+ function firstFiniteNumber(record3, keys) {
32011
+ for (const key of keys) {
32012
+ const n = asFiniteNumber(record3[key]);
32013
+ if (n !== undefined)
32014
+ return n;
32015
+ }
32016
+ return;
32017
+ }
32018
+ function normalizeUsageBreakdown(value) {
32019
+ if (!isRecord3(value))
32020
+ return;
32021
+ const out = {};
32022
+ for (const [key, aliases] of USAGE_BREAKDOWN_FIELDS) {
32023
+ const n = firstFiniteNumber(value, aliases);
32024
+ if (n !== undefined)
32025
+ out[key] = n;
32026
+ }
32027
+ return Object.keys(out).length > 0 ? out : undefined;
32028
+ }
32029
+ function normalizeUsageCost(value) {
32030
+ if (!isRecord3(value))
32031
+ return;
32032
+ const amount = asFiniteNumber(value.amount);
32033
+ const currency = readString(value, "currency");
32034
+ if (amount === undefined && !currency)
32035
+ return;
32036
+ return { ...amount !== undefined ? { amount } : {}, ...currency ? { currency } : {} };
32037
+ }
32038
+ function normalizeAgentCommands(value) {
32039
+ if (!Array.isArray(value))
32040
+ return [];
32041
+ const out = [];
32042
+ for (const entry of value) {
32043
+ if (!isRecord3(entry))
32044
+ continue;
32045
+ const name = readString(entry, "name");
32046
+ if (!name)
32047
+ continue;
32048
+ const description = readString(entry, "description");
32049
+ out.push({ name, ...description ? { description } : {}, hasInput: entry.input != null });
32050
+ }
32051
+ return out;
32052
+ }
31843
32053
  function isRecord3(value) {
31844
32054
  return typeof value === "object" && value !== null && !Array.isArray(value);
31845
32055
  }
@@ -31865,8 +32075,17 @@ function isGenericToolTitle(kind, title) {
31865
32075
  }
31866
32076
  return false;
31867
32077
  }
32078
+ var USAGE_BREAKDOWN_FIELDS;
31868
32079
  var init_streaming_prompt = __esm(() => {
31869
32080
  init_tool_kind_emoji();
32081
+ USAGE_BREAKDOWN_FIELDS = [
32082
+ ["inputTokens", ["inputTokens", "input_tokens"]],
32083
+ ["outputTokens", ["outputTokens", "output_tokens"]],
32084
+ ["cachedReadTokens", ["cachedReadTokens", "cacheReadInputTokens", "cache_read_input_tokens"]],
32085
+ ["cachedWriteTokens", ["cachedWriteTokens", "cacheCreationInputTokens", "cache_creation_input_tokens"]],
32086
+ ["thoughtTokens", ["thoughtTokens", "thought_tokens"]],
32087
+ ["totalTokens", ["totalTokens", "total_tokens"]]
32088
+ ];
31870
32089
  });
31871
32090
 
31872
32091
  // src/transport/acpx-cli/node-pty-helper.ts
@@ -31959,6 +32178,31 @@ var init_agent_session_list = __esm(() => {
31959
32178
  init_path();
31960
32179
  });
31961
32180
 
32181
+ // src/transport/acpx-session-files.ts
32182
+ import { readdir as readdir3, unlink as unlink2 } from "node:fs/promises";
32183
+ import { homedir as homedir9 } from "node:os";
32184
+ import { join as join19 } from "node:path";
32185
+ async function deleteAcpxSessionFiles(options) {
32186
+ const dir = options.sessionsDir ?? join19(homedir9(), ".acpx", "sessions");
32187
+ const safeId = encodeURIComponent(options.acpxRecordId);
32188
+ await unlink2(join19(dir, `${safeId}.json`)).catch(() => {
32189
+ return;
32190
+ });
32191
+ let entries;
32192
+ try {
32193
+ entries = await readdir3(dir);
32194
+ } catch {
32195
+ return;
32196
+ }
32197
+ const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
32198
+ for (const name of streamFiles) {
32199
+ await unlink2(join19(dir, name)).catch(() => {
32200
+ return;
32201
+ });
32202
+ }
32203
+ }
32204
+ var init_acpx_session_files = () => {};
32205
+
31962
32206
  // src/transport/acpx-cli/acpx-cli-transport.ts
31963
32207
  import { createRequire as createRequire5 } from "node:module";
31964
32208
  import { spawn as spawn9 } from "node:child_process";
@@ -32217,6 +32461,16 @@ ${baseText}` : "" };
32217
32461
  const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
32218
32462
  throw new Error(detail);
32219
32463
  }
32464
+ async deleteSession(session3) {
32465
+ let acpxRecordId;
32466
+ try {
32467
+ ({ acpxRecordId } = await this.readSessionRecord(session3));
32468
+ } catch {
32469
+ return;
32470
+ }
32471
+ await this.removeSession(session3);
32472
+ await deleteAcpxSessionFiles({ acpxRecordId });
32473
+ }
32220
32474
  async hasSession(session3) {
32221
32475
  const result = await this.runCommand(this.command, this.buildArgs(session3, [
32222
32476
  "sessions",
@@ -32253,7 +32507,7 @@ ${baseText}` : "" };
32253
32507
  const parsed = JSON.parse(result.stdout);
32254
32508
  const acpxRecordId = typeof parsed.acpxRecordId === "string" ? parsed.acpxRecordId : typeof parsed.id === "string" ? parsed.id : undefined;
32255
32509
  const agentSessionId = typeof parsed.agentSessionId === "string" ? parsed.agentSessionId : undefined;
32256
- if (acpxRecordId) {
32510
+ if (acpxRecordId && /^[\w.:-]+$/.test(acpxRecordId) && acpxRecordId.length >= 8) {
32257
32511
  return { acpxRecordId, agentSessionId };
32258
32512
  }
32259
32513
  } catch {
@@ -32532,6 +32786,7 @@ var init_acpx_cli_transport = __esm(() => {
32532
32786
  init_terminate_process_tree();
32533
32787
  init_acpx_queue_owner_launcher();
32534
32788
  init_agent_session_list();
32789
+ init_acpx_session_files();
32535
32790
  require4 = createRequire5(import.meta.url);
32536
32791
  });
32537
32792
 
@@ -33003,8 +33258,8 @@ function createControlEventBus(logger2) {
33003
33258
 
33004
33259
  // src/transport/native-session-history.ts
33005
33260
  import { readFile as readFile14 } from "node:fs/promises";
33006
- import { homedir as homedir9 } from "node:os";
33007
- import { join as join19 } from "node:path";
33261
+ import { homedir as homedir10 } from "node:os";
33262
+ import { join as join20 } from "node:path";
33008
33263
  function classifyToolKind(name) {
33009
33264
  const n = name.toLowerCase();
33010
33265
  if (/(^|[^a-z])(read|cat|view|open)([^a-z]|$)/.test(n))
@@ -33116,8 +33371,8 @@ function mapAcpxMessagesToHistory(raw) {
33116
33371
  }
33117
33372
  async function readNativeSessionHistory(opts) {
33118
33373
  try {
33119
- const dir = opts.sessionsDir ?? join19(opts.homeDir ?? homedir9(), ".acpx", "sessions");
33120
- const indexRaw = await readFile14(join19(dir, "index.json"), "utf8").catch(() => null);
33374
+ const dir = opts.sessionsDir ?? join20(opts.homeDir ?? homedir10(), ".acpx", "sessions");
33375
+ const indexRaw = await readFile14(join20(dir, "index.json"), "utf8").catch(() => null);
33121
33376
  if (!indexRaw)
33122
33377
  return [];
33123
33378
  const index = JSON.parse(indexRaw);
@@ -33126,7 +33381,7 @@ async function readNativeSessionHistory(opts) {
33126
33381
  for (const entry of candidates) {
33127
33382
  if (!entry.file)
33128
33383
  continue;
33129
- const recRaw = await readFile14(join19(dir, entry.file), "utf8").catch(() => null);
33384
+ const recRaw = await readFile14(join20(dir, entry.file), "utf8").catch(() => null);
33130
33385
  if (!recRaw)
33131
33386
  continue;
33132
33387
  const record3 = JSON.parse(recRaw);
@@ -33144,14 +33399,14 @@ var init_native_session_history = () => {};
33144
33399
  // src/control/workspace-fs.ts
33145
33400
  import { execFile } from "node:child_process";
33146
33401
  import { promisify } from "node:util";
33147
- import { homedir as homedir10 } from "node:os";
33148
- import { readdir as readdir3, realpath, stat as stat3, open as open5 } from "node:fs/promises";
33402
+ import { homedir as homedir11 } from "node:os";
33403
+ import { readdir as readdir4, realpath, stat as stat3, open as open5 } from "node:fs/promises";
33149
33404
  import { isAbsolute as isAbsolute3, relative, resolve as resolve3, sep } from "node:path";
33150
33405
  function expandHome2(p) {
33151
33406
  if (p === "~")
33152
- return homedir10();
33407
+ return homedir11();
33153
33408
  if (p.startsWith("~/") || p.startsWith("~" + sep))
33154
- return resolve3(homedir10(), p.slice(2));
33409
+ return resolve3(homedir11(), p.slice(2));
33155
33410
  return p;
33156
33411
  }
33157
33412
 
@@ -33186,7 +33441,7 @@ class WorkspaceFs {
33186
33441
  }
33187
33442
  async listDirectory(workspace3, relPath) {
33188
33443
  const { abs, rel } = await this.resolve(workspace3, relPath);
33189
- const dirents = await readdir3(abs, { withFileTypes: true });
33444
+ const dirents = await readdir4(abs, { withFileTypes: true });
33190
33445
  const entries = [];
33191
33446
  for (const d of dirents.slice(0, MAX_ENTRIES)) {
33192
33447
  if (d.isDirectory()) {
@@ -33240,7 +33495,7 @@ class WorkspaceFs {
33240
33495
  const dir = queue.shift();
33241
33496
  let dirents;
33242
33497
  try {
33243
- dirents = await readdir3(dir, { withFileTypes: true });
33498
+ dirents = await readdir4(dir, { withFileTypes: true });
33244
33499
  } catch {
33245
33500
  continue;
33246
33501
  }
@@ -33340,24 +33595,29 @@ var init_workspace_fs = __esm(() => {
33340
33595
  });
33341
33596
 
33342
33597
  // src/control/control-service.ts
33598
+ import path15 from "node:path";
33599
+
33343
33600
  class ControlService {
33344
33601
  deps;
33345
33602
  constructor(deps) {
33346
33603
  this.deps = deps;
33347
33604
  }
33348
33605
  workspaceFs = new WorkspaceFs(() => this.deps.workspaces.list().map((w) => ({ name: w.name, cwd: w.cwd })));
33349
- listDirectory(workspace3, path15) {
33350
- return this.workspaceFs.listDirectory(workspace3, path15);
33606
+ listDirectory(workspace3, path16) {
33607
+ return this.workspaceFs.listDirectory(workspace3, path16);
33351
33608
  }
33352
- readWorkspaceFile(workspace3, path15) {
33353
- return this.workspaceFs.readFile(workspace3, path15);
33609
+ readWorkspaceFile(workspace3, path16) {
33610
+ return this.workspaceFs.readFile(workspace3, path16);
33354
33611
  }
33355
- workspaceGitDiff(workspace3, path15) {
33356
- return this.workspaceFs.gitDiff(workspace3, path15);
33612
+ workspaceGitDiff(workspace3, path16) {
33613
+ return this.workspaceFs.gitDiff(workspace3, path16);
33357
33614
  }
33358
33615
  searchWorkspace(workspace3, query) {
33359
33616
  return this.workspaceFs.search(workspace3, query);
33360
33617
  }
33618
+ async uploadFile(input) {
33619
+ return this.deps.uploadStore.save(input.filename, input.content, input.mimeType);
33620
+ }
33361
33621
  async getSessionModel(chatKey, alias) {
33362
33622
  const session3 = await this.resolveControlSession(chatKey, alias);
33363
33623
  if (!session3)
@@ -33389,7 +33649,8 @@ class ControlService {
33389
33649
  agent: session3.agent,
33390
33650
  workspace: session3.workspace,
33391
33651
  transportSession: session3.transportSession,
33392
- running: this.deps.activeTurns.isActiveAnywhere(session3.alias)
33652
+ running: this.deps.activeTurns.isActiveAnywhere(session3.alias),
33653
+ archived: session3.archived === true
33393
33654
  }));
33394
33655
  }
33395
33656
  async listNativeSessions(_chatKey, agent3, workspace3) {
@@ -33419,15 +33680,26 @@ class ControlService {
33419
33680
  agent: session3.agent,
33420
33681
  workspace: session3.workspace,
33421
33682
  transportSession: session3.transportSession,
33422
- running: false
33683
+ running: false,
33684
+ archived: false
33423
33685
  };
33424
33686
  }
33425
33687
  async removeSession(chatKey, alias) {
33426
33688
  const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
33427
- const result = await this.deps.sessions.removeSession(internalAlias);
33689
+ const result = await this.deps.removeSessionWithTransport(internalAlias);
33428
33690
  this.deps.events.emit({ type: "sessions-changed" });
33429
33691
  return result;
33430
33692
  }
33693
+ async archiveSession(chatKey, alias) {
33694
+ const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
33695
+ await this.deps.archiveSessionWithTransport(internalAlias);
33696
+ this.deps.events.emit({ type: "sessions-changed" });
33697
+ }
33698
+ async unarchiveSession(chatKey, alias) {
33699
+ const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
33700
+ await this.deps.unarchiveSession(internalAlias);
33701
+ this.deps.events.emit({ type: "sessions-changed" });
33702
+ }
33431
33703
  listAgents() {
33432
33704
  return this.deps.agents.list();
33433
33705
  }
@@ -33489,7 +33761,8 @@ class ControlService {
33489
33761
  text: input.text,
33490
33762
  senderId: input.senderId,
33491
33763
  ...input.isOwner !== undefined ? { isOwner: input.isOwner } : {},
33492
- ...input.accountId !== undefined ? { accountId: input.accountId } : {}
33764
+ ...input.accountId !== undefined ? { accountId: input.accountId } : {},
33765
+ ...input.media !== undefined ? { media: input.media } : {}
33493
33766
  });
33494
33767
  }
33495
33768
  async runScheduledTurn(input) {
@@ -33561,6 +33834,32 @@ ${chunk}` : chunk
33561
33834
  });
33562
33835
  emittedChunk = true;
33563
33836
  };
33837
+ const incomingMedia = params.media ?? [];
33838
+ const sandboxedMedia = incomingMedia.length ? (() => {
33839
+ const uploadRoot = path15.resolve(this.deps.uploadStore.root);
33840
+ const kept = incomingMedia.filter((ref) => {
33841
+ const resolved = path15.resolve(ref.filePath);
33842
+ return resolved === uploadRoot || resolved.startsWith(uploadRoot + path15.sep);
33843
+ });
33844
+ const dropped = incomingMedia.length - kept.length;
33845
+ if (dropped > 0) {
33846
+ console.warn(`[control] dropped ${dropped} media ref(s) with filePath outside the upload sandbox`);
33847
+ }
33848
+ return kept;
33849
+ })() : incomingMedia;
33850
+ const chatMedia = sandboxedMedia.map((ref) => ({
33851
+ kind: ref.kind,
33852
+ filePath: ref.filePath,
33853
+ mimeType: ref.mimeType,
33854
+ ...ref.fileName ? { fileName: ref.fileName } : {},
33855
+ sizeBytes: ref.size,
33856
+ source: {
33857
+ channelId: "relay",
33858
+ accountId: params.accountId ?? "control",
33859
+ chatKey: params.chatKey,
33860
+ messageId: ref.id
33861
+ }
33862
+ }));
33564
33863
  try {
33565
33864
  const response = await this.deps.agent.chat({
33566
33865
  accountId: params.accountId ?? "control",
@@ -33568,6 +33867,7 @@ ${chunk}` : chunk
33568
33867
  text: params.text,
33569
33868
  metadata: buildControlMetadata(params.senderId, params.isOwner),
33570
33869
  abortSignal: controller.signal,
33870
+ ...chatMedia.length > 0 ? { media: chatMedia } : {},
33571
33871
  reply: async (chunk) => {
33572
33872
  emitChunk(chunk);
33573
33873
  },
@@ -33601,7 +33901,17 @@ ${chunk}` : chunk
33601
33901
  chatKey: params.chatKey,
33602
33902
  sessionAlias: params.sessionAlias,
33603
33903
  used: usage.used,
33604
- size: usage.size
33904
+ size: usage.size,
33905
+ ...usage.cost ? { cost: usage.cost } : {},
33906
+ ...usage.breakdown ? { breakdown: usage.breakdown } : {}
33907
+ });
33908
+ },
33909
+ onCommands: (commands) => {
33910
+ this.deps.events.emit({
33911
+ type: "agent-commands",
33912
+ chatKey: params.chatKey,
33913
+ sessionAlias: params.sessionAlias,
33914
+ commands
33605
33915
  });
33606
33916
  }
33607
33917
  });
@@ -33690,18 +34000,97 @@ var init_control_service = __esm(() => {
33690
34000
  init_workspace_fs();
33691
34001
  });
33692
34002
 
34003
+ // src/control/upload-store.ts
34004
+ import { mkdtemp as mkdtemp2, readdir as readdir5, rm as rm10, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
34005
+ import { homedir as homedir12 } from "node:os";
34006
+ import path16 from "node:path";
34007
+ function defaultRootDir() {
34008
+ const home = process.env.HOME ?? homedir12();
34009
+ return path16.join(coreHomeDir(home), "runtime", "uploads");
34010
+ }
34011
+ function sanitizeUploadFilename(raw) {
34012
+ const base = path16.basename(raw).replace(/[/\\]/g, "").replace(/^\.+/, "");
34013
+ const cleaned = base.trim();
34014
+ return cleaned.length > 0 ? cleaned : "file";
34015
+ }
34016
+
34017
+ class UploadStore {
34018
+ rootDir;
34019
+ maxBytes;
34020
+ ttlMs;
34021
+ now;
34022
+ constructor(opts = {}) {
34023
+ this.rootDir = opts.rootDir ?? defaultRootDir();
34024
+ this.maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
34025
+ this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
34026
+ this.now = opts.now ?? (() => new Date);
34027
+ }
34028
+ get root() {
34029
+ return this.rootDir;
34030
+ }
34031
+ async save(filename, base642, mimeType) {
34032
+ if (base642.length > Math.ceil(this.maxBytes * 4 / 3) + 4)
34033
+ throw new Error("file-too-large");
34034
+ const bytes = Buffer.from(base642, "base64");
34035
+ if (bytes.byteLength === 0)
34036
+ throw new Error("empty-file");
34037
+ if (bytes.byteLength > this.maxBytes)
34038
+ throw new Error("file-too-large");
34039
+ const safeName = sanitizeUploadFilename(filename);
34040
+ const { mkdir: mkdir9 } = await import("node:fs/promises");
34041
+ await mkdir9(this.rootDir, { recursive: true });
34042
+ const dir = await mkdtemp2(path16.join(this.rootDir, "u-"));
34043
+ const filePath = path16.join(dir, safeName);
34044
+ await writeFile8(filePath, bytes);
34045
+ return {
34046
+ id: path16.basename(dir),
34047
+ path: filePath,
34048
+ filename: safeName,
34049
+ mimeType,
34050
+ size: bytes.byteLength
34051
+ };
34052
+ }
34053
+ async cleanup() {
34054
+ let entries;
34055
+ try {
34056
+ entries = await readdir5(this.rootDir);
34057
+ } catch {
34058
+ return 0;
34059
+ }
34060
+ const cutoff = this.now().getTime() - this.ttlMs;
34061
+ let removed = 0;
34062
+ for (const name of entries) {
34063
+ const dir = path16.join(this.rootDir, name);
34064
+ try {
34065
+ const info = await stat4(dir);
34066
+ if (info.mtimeMs < cutoff) {
34067
+ await rm10(dir, { recursive: true, force: true });
34068
+ removed += 1;
34069
+ }
34070
+ } catch {}
34071
+ }
34072
+ return removed;
34073
+ }
34074
+ }
34075
+ var DEFAULT_MAX_BYTES, DEFAULT_TTL_MS;
34076
+ var init_upload_store = __esm(() => {
34077
+ init_core_home();
34078
+ DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
34079
+ DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
34080
+ });
34081
+
33693
34082
  // src/config/agent-catalog.ts
33694
34083
  import { existsSync as existsSync3 } from "node:fs";
33695
- import { delimiter as delimiter2, join as join20 } from "node:path";
34084
+ import { delimiter as delimiter2, join as join21 } from "node:path";
33696
34085
  function isBinaryOnPath(binary) {
33697
- const path15 = process.env.PATH ?? "";
34086
+ const path17 = process.env.PATH ?? "";
33698
34087
  const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
33699
- for (const dir of path15.split(delimiter2)) {
34088
+ for (const dir of path17.split(delimiter2)) {
33700
34089
  if (!dir)
33701
34090
  continue;
33702
34091
  for (const ext of exts) {
33703
34092
  try {
33704
- if (existsSync3(join20(dir, binary + ext)))
34093
+ if (existsSync3(join21(dir, binary + ext)))
33705
34094
  return true;
33706
34095
  } catch {}
33707
34096
  }
@@ -33741,8 +34130,8 @@ __export(exports_main, {
33741
34130
  buildApp: () => buildApp
33742
34131
  });
33743
34132
  import { randomUUID as randomUUID3 } from "node:crypto";
33744
- import { homedir as homedir11 } from "node:os";
33745
- import { dirname as dirname12, join as join21 } from "node:path";
34133
+ import { homedir as homedir13 } from "node:os";
34134
+ import { dirname as dirname12, join as join22 } from "node:path";
33746
34135
  import { fileURLToPath as fileURLToPath5 } from "node:url";
33747
34136
  function startProgressHeartbeat(orchestration3, config4, logger2, channel) {
33748
34137
  const thresholdSeconds = config4.orchestration.progressHeartbeatSeconds;
@@ -34233,6 +34622,9 @@ async function buildApp(paths, deps = {}) {
34233
34622
  const router3 = new CommandRouter(sessions, transport, config4, configStore, logger2, undefined, orchestration3, quota, scheduledService, deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : undefined, deps.channel?.nativeSessionListFormat ? deps.channel.nativeSessionListFormat.bind(deps.channel) : undefined, activeTurns);
34234
34623
  const agent3 = new ConsoleAgent(router3, logger2);
34235
34624
  const controlEvents = createControlEventBus(logger2);
34625
+ const uploadStore = new UploadStore;
34626
+ uploadStore.cleanup();
34627
+ const uploadCleanupInterval = setInterval(() => void uploadStore.cleanup().catch(() => {}), 60 * 60 * 1000);
34236
34628
  const control = new ControlService({
34237
34629
  agent: agent3,
34238
34630
  sessions,
@@ -34240,6 +34632,9 @@ async function buildApp(paths, deps = {}) {
34240
34632
  createSessionWithTransport: (internalAlias, agent4, workspace3, model) => router3.createSessionWithTransport(internalAlias, agent4, workspace3, model),
34241
34633
  listNativeSessions: (agent4, workspace3) => router3.listNativeSessionsForControl(agent4, workspace3),
34242
34634
  attachNativeSessionWithTransport: (internalAlias, agent4, workspace3, agentSessionId, nativeMeta) => router3.attachNativeSessionWithTransport(internalAlias, agent4, workspace3, agentSessionId, nativeMeta),
34635
+ removeSessionWithTransport: (internalAlias) => router3.removeSessionWithTransport(internalAlias),
34636
+ archiveSessionWithTransport: (internalAlias) => router3.archiveSessionWithTransport(internalAlias),
34637
+ unarchiveSession: (internalAlias) => router3.unarchiveSession(internalAlias),
34243
34638
  activeTurns,
34244
34639
  scheduled: scheduledService,
34245
34640
  orchestration: orchestration3,
@@ -34272,7 +34667,8 @@ async function buildApp(paths, deps = {}) {
34272
34667
  const updated = await configStore.removeWorkspace(name);
34273
34668
  replaceRuntimeConfig(config4, updated);
34274
34669
  }
34275
- }
34670
+ },
34671
+ uploadStore
34276
34672
  });
34277
34673
  const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
34278
34674
  dispatchTask: buildScheduledDispatchTask({
@@ -34342,6 +34738,7 @@ async function buildApp(paths, deps = {}) {
34342
34738
  reapStaleQueueOwners: () => reapWarmQueueOwners("startup"),
34343
34739
  dispose: async () => {
34344
34740
  scheduledScheduler.stop();
34741
+ clearInterval(uploadCleanupInterval);
34345
34742
  if (progressHeartbeatInterval !== undefined) {
34346
34743
  clearInterval(progressHeartbeatInterval);
34347
34744
  }
@@ -34402,8 +34799,8 @@ async function main() {
34402
34799
  }
34403
34800
  }
34404
34801
  async function prepareChannelMedia(configPath, config4) {
34405
- const runtimeDir = join21(dirname12(configPath), "runtime");
34406
- const mediaRootDir = join21(runtimeDir, "media");
34802
+ const runtimeDir = join22(dirname12(configPath), "runtime");
34803
+ const mediaRootDir = join22(runtimeDir, "media");
34407
34804
  const mediaStore = new RuntimeMediaStore({ rootDir: mediaRootDir });
34408
34805
  await mediaStore.cleanupExpired().catch((error2) => {
34409
34806
  console.error("[xacpx] media cleanup failed:", error2 instanceof Error ? error2.message : String(error2));
@@ -34412,16 +34809,16 @@ async function prepareChannelMedia(configPath, config4) {
34412
34809
  return { mediaStore, channelDeps: { mediaStore, allowedMediaRoots } };
34413
34810
  }
34414
34811
  function resolveRuntimePaths() {
34415
- const home = process.env.HOME ?? homedir11();
34812
+ const home = process.env.HOME ?? homedir13();
34416
34813
  if (!home) {
34417
34814
  throw new Error("Unable to resolve the current user home directory");
34418
34815
  }
34419
- const configPath = coreEnv("CONFIG") ?? join21(coreHomeDir(home), "config.json");
34420
- const runtimeDir = join21(dirname12(configPath), "runtime");
34816
+ const configPath = coreEnv("CONFIG") ?? join22(coreHomeDir(home), "config.json");
34817
+ const runtimeDir = join22(dirname12(configPath), "runtime");
34421
34818
  return {
34422
34819
  configPath,
34423
- statePath: coreEnv("STATE") ?? join21(coreHomeDir(home), "state.json"),
34424
- perfLogPath: join21(runtimeDir, "perf.log"),
34820
+ statePath: coreEnv("STATE") ?? join22(coreHomeDir(home), "state.json"),
34821
+ perfLogPath: join22(runtimeDir, "perf.log"),
34425
34822
  orchestrationSocketPath: coreEnv("ORCHESTRATION_SOCKET") ?? resolveDaemonOrchestrationSocketPath(runtimeDir)
34426
34823
  };
34427
34824
  }
@@ -34433,13 +34830,13 @@ function resolveBridgeEntryPath() {
34433
34830
  }
34434
34831
  function resolveAppLogPath(configPath) {
34435
34832
  const rootDir = dirname12(configPath);
34436
- const runtimeDir = join21(rootDir, "runtime");
34437
- return join21(runtimeDir, "app.log");
34833
+ const runtimeDir = join22(rootDir, "runtime");
34834
+ return join22(runtimeDir, "app.log");
34438
34835
  }
34439
34836
  function resolvePerfLogPath(configPath) {
34440
34837
  const rootDir = dirname12(configPath);
34441
- const runtimeDir = join21(rootDir, "runtime");
34442
- return join21(runtimeDir, "perf.log");
34838
+ const runtimeDir = join22(rootDir, "runtime");
34839
+ return join22(runtimeDir, "perf.log");
34443
34840
  }
34444
34841
  function resolveOrchestrationSocketPathFromConfigPath(configPath) {
34445
34842
  const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
@@ -34484,6 +34881,7 @@ var init_main = __esm(async () => {
34484
34881
  init_render_text();
34485
34882
  init_quota_manager();
34486
34883
  init_control_service();
34884
+ init_upload_store();
34487
34885
  init_agent_catalog();
34488
34886
  init_perf_tracer();
34489
34887
  init_bootstrap();
@@ -34677,12 +35075,12 @@ var init_config_check = __esm(async () => {
34677
35075
  });
34678
35076
 
34679
35077
  // src/doctor/checks/daemon-check.ts
34680
- import { readdir as readdir4, readFile as readFile15, rm as rm10 } from "node:fs/promises";
35078
+ import { readdir as readdir6, readFile as readFile15, rm as rm11 } from "node:fs/promises";
34681
35079
  import { fileURLToPath as fileURLToPath6 } from "node:url";
34682
- import { homedir as homedir12 } from "node:os";
34683
- import { join as join22 } from "node:path";
35080
+ import { homedir as homedir14 } from "node:os";
35081
+ import { join as join23 } from "node:path";
34684
35082
  async function checkDaemon(options = {}) {
34685
- const home = options.home ?? process.env.HOME ?? homedir12();
35083
+ const home = options.home ?? process.env.HOME ?? homedir14();
34686
35084
  const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
34687
35085
  const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
34688
35086
  home,
@@ -34782,7 +35180,7 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
34782
35180
  if (!fileName.endsWith(CONSUMER_LOCK_SUFFIX)) {
34783
35181
  continue;
34784
35182
  }
34785
- const lockPath = join22(runtimeDir, fileName);
35183
+ const lockPath = join23(runtimeDir, fileName);
34786
35184
  const lock2 = await deps.readConsumerLock(lockPath);
34787
35185
  if (lock2 && !deps.isProcessRunning(lock2.pid)) {
34788
35186
  stalePaths.push(lockPath);
@@ -34816,22 +35214,22 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
34816
35214
  }
34817
35215
  async function defaultListConsumerLocks(runtimeDir) {
34818
35216
  try {
34819
- return await readdir4(runtimeDir);
35217
+ return await readdir6(runtimeDir);
34820
35218
  } catch {
34821
35219
  return [];
34822
35220
  }
34823
35221
  }
34824
- async function defaultReadConsumerLock(path15) {
35222
+ async function defaultReadConsumerLock(path17) {
34825
35223
  try {
34826
- const raw = await readFile15(path15, "utf8");
35224
+ const raw = await readFile15(path17, "utf8");
34827
35225
  const parsed = JSON.parse(raw);
34828
35226
  return typeof parsed.pid === "number" ? { pid: parsed.pid } : null;
34829
35227
  } catch {
34830
35228
  return null;
34831
35229
  }
34832
35230
  }
34833
- async function defaultRemoveConsumerLock(path15) {
34834
- await rm10(path15, { force: true });
35231
+ async function defaultRemoveConsumerLock(path17) {
35232
+ await rm11(path17, { force: true });
34835
35233
  }
34836
35234
  function resolveCliEntryPath() {
34837
35235
  return process.argv[1] ?? fileURLToPath6(import.meta.url);
@@ -34846,11 +35244,11 @@ var init_daemon_check = __esm(() => {
34846
35244
  });
34847
35245
 
34848
35246
  // src/doctor/checks/logs-check.ts
34849
- import { stat as stat4, readdir as readdir5 } from "node:fs/promises";
34850
- import { basename as basename3, join as join23 } from "node:path";
34851
- import { homedir as homedir13 } from "node:os";
35247
+ import { stat as stat5, readdir as readdir7 } from "node:fs/promises";
35248
+ import { basename as basename3, join as join24 } from "node:path";
35249
+ import { homedir as homedir15 } from "node:os";
34852
35250
  async function checkLogs(options = {}) {
34853
- const home = options.home ?? process.env.HOME ?? homedir13();
35251
+ const home = options.home ?? process.env.HOME ?? homedir15();
34854
35252
  const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
34855
35253
  const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
34856
35254
  home,
@@ -34882,13 +35280,13 @@ async function checkLogs(options = {}) {
34882
35280
  const matched = entries.filter((entry) => isTrackedLogName(entry, tracked));
34883
35281
  const files = [];
34884
35282
  for (const name of matched) {
34885
- const path15 = join23(paths.runtimeDir, name);
35283
+ const path17 = join24(paths.runtimeDir, name);
34886
35284
  try {
34887
- const fileStat = await probe.stat(path15);
35285
+ const fileStat = await probe.stat(path17);
34888
35286
  if (fileStat.isDirectory()) {
34889
35287
  continue;
34890
35288
  }
34891
- files.push({ name, path: path15, size: fileStat.size });
35289
+ files.push({ name, path: path17, size: fileStat.size });
34892
35290
  } catch {
34893
35291
  continue;
34894
35292
  }
@@ -34962,8 +35360,8 @@ function formatBytes(bytes) {
34962
35360
  }
34963
35361
  function createLogsFsProbe() {
34964
35362
  return {
34965
- stat: async (path15) => await stat4(path15),
34966
- readdir: async (path15) => await readdir5(path15)
35363
+ stat: async (path17) => await stat5(path17),
35364
+ readdir: async (path17) => await readdir7(path17)
34967
35365
  };
34968
35366
  }
34969
35367
  function formatError6(error2) {
@@ -35032,9 +35430,9 @@ var init_orchestration_health = __esm(() => {
35032
35430
  });
35033
35431
 
35034
35432
  // src/doctor/checks/orchestration-socket-check.ts
35035
- import { homedir as homedir14 } from "node:os";
35433
+ import { homedir as homedir16 } from "node:os";
35036
35434
  async function checkOrchestrationSocket(options = {}) {
35037
- const home = options.home ?? process.env.HOME ?? homedir14();
35435
+ const home = options.home ?? process.env.HOME ?? homedir16();
35038
35436
  const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
35039
35437
  const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
35040
35438
  home,
@@ -35207,11 +35605,11 @@ var init_plugin_check = __esm(async () => {
35207
35605
 
35208
35606
  // src/doctor/checks/runtime-check.ts
35209
35607
  import { constants } from "node:fs";
35210
- import { access as access4, stat as stat5 } from "node:fs/promises";
35608
+ import { access as access4, stat as stat6 } from "node:fs/promises";
35211
35609
  import { dirname as dirname13 } from "node:path";
35212
- import { homedir as homedir15 } from "node:os";
35610
+ import { homedir as homedir17 } from "node:os";
35213
35611
  async function checkRuntime(options = {}) {
35214
- const home = options.home ?? process.env.HOME ?? homedir15();
35612
+ const home = options.home ?? process.env.HOME ?? homedir17();
35215
35613
  const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
35216
35614
  const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
35217
35615
  home,
@@ -35305,107 +35703,107 @@ function formatMode(mode) {
35305
35703
  }
35306
35704
  function createRuntimeFsProbe() {
35307
35705
  return {
35308
- stat: async (path15) => await stat5(path15),
35309
- access: async (path15, mode) => await access4(path15, mode)
35706
+ stat: async (path17) => await stat6(path17),
35707
+ access: async (path17, mode) => await access4(path17, mode)
35310
35708
  };
35311
35709
  }
35312
- async function checkDirectoryCreatable(label, path15, probe, platform) {
35710
+ async function checkDirectoryCreatable(label, path17, probe, platform) {
35313
35711
  try {
35314
- const stats = await probe.stat(path15);
35712
+ const stats = await probe.stat(path17);
35315
35713
  if (!stats.isDirectory()) {
35316
35714
  return {
35317
35715
  ok: false,
35318
- detail: `${label}: ${path15} (exists but is not a directory)`
35716
+ detail: `${label}: ${path17} (exists but is not a directory)`
35319
35717
  };
35320
35718
  }
35321
- await probe.access(path15, directoryAccessMode(platform));
35719
+ await probe.access(path17, directoryAccessMode(platform));
35322
35720
  return {
35323
35721
  ok: true,
35324
- detail: `${label}: ${path15} (writable)`
35722
+ detail: `${label}: ${path17} (writable)`
35325
35723
  };
35326
35724
  } catch (error2) {
35327
35725
  if (!isMissingPathError2(error2)) {
35328
35726
  return {
35329
35727
  ok: false,
35330
- detail: `${label}: ${path15} (unusable: ${formatError9(error2)})`
35728
+ detail: `${label}: ${path17} (unusable: ${formatError9(error2)})`
35331
35729
  };
35332
35730
  }
35333
- const parentCheck = await checkCreatableAncestorDirectory(path15, probe, platform);
35731
+ const parentCheck = await checkCreatableAncestorDirectory(path17, probe, platform);
35334
35732
  if (!parentCheck.ok) {
35335
35733
  return {
35336
35734
  ok: false,
35337
- detail: `${label}: ${path15} (parent not writable: ${parentCheck.blockingPath})`
35735
+ detail: `${label}: ${path17} (parent not writable: ${parentCheck.blockingPath})`
35338
35736
  };
35339
35737
  }
35340
35738
  return {
35341
35739
  ok: true,
35342
- detail: `${label}: ${path15} (creatable via ${parentCheck.creatableFrom})`
35740
+ detail: `${label}: ${path17} (creatable via ${parentCheck.creatableFrom})`
35343
35741
  };
35344
35742
  }
35345
35743
  }
35346
- async function checkFileCreatable(label, path15, probe, platform) {
35744
+ async function checkFileCreatable(label, path17, probe, platform) {
35347
35745
  try {
35348
- const stats = await probe.stat(path15);
35746
+ const stats = await probe.stat(path17);
35349
35747
  if (stats.isDirectory()) {
35350
35748
  return {
35351
35749
  ok: false,
35352
- detail: `${label}: ${path15} (exists but is a directory)`
35750
+ detail: `${label}: ${path17} (exists but is a directory)`
35353
35751
  };
35354
35752
  }
35355
- await probe.access(path15, constants.W_OK);
35753
+ await probe.access(path17, constants.W_OK);
35356
35754
  return {
35357
35755
  ok: true,
35358
- detail: `${label}: ${path15} (writable)`
35756
+ detail: `${label}: ${path17} (writable)`
35359
35757
  };
35360
35758
  } catch (error2) {
35361
35759
  if (!isMissingPathError2(error2)) {
35362
35760
  return {
35363
35761
  ok: false,
35364
- detail: `${label}: ${path15} (unusable: ${formatError9(error2)})`
35762
+ detail: `${label}: ${path17} (unusable: ${formatError9(error2)})`
35365
35763
  };
35366
35764
  }
35367
- const parentCheck = await checkCreatableAncestorDirectory(dirname13(path15), probe, platform);
35765
+ const parentCheck = await checkCreatableAncestorDirectory(dirname13(path17), probe, platform);
35368
35766
  if (!parentCheck.ok) {
35369
35767
  return {
35370
35768
  ok: false,
35371
- detail: `${label}: ${path15} (parent not writable: ${parentCheck.blockingPath})`
35769
+ detail: `${label}: ${path17} (parent not writable: ${parentCheck.blockingPath})`
35372
35770
  };
35373
35771
  }
35374
35772
  return {
35375
35773
  ok: true,
35376
- detail: `${label}: ${path15} (creatable via ${parentCheck.creatableFrom})`
35774
+ detail: `${label}: ${path17} (creatable via ${parentCheck.creatableFrom})`
35377
35775
  };
35378
35776
  }
35379
35777
  }
35380
- async function checkCreatableAncestorDirectory(path15, probe, platform) {
35778
+ async function checkCreatableAncestorDirectory(path17, probe, platform) {
35381
35779
  try {
35382
- const stats = await probe.stat(path15);
35780
+ const stats = await probe.stat(path17);
35383
35781
  if (!stats.isDirectory()) {
35384
35782
  return {
35385
35783
  ok: false,
35386
- creatableFrom: path15,
35387
- blockingPath: path15
35784
+ creatableFrom: path17,
35785
+ blockingPath: path17
35388
35786
  };
35389
35787
  }
35390
- await probe.access(path15, directoryAccessMode(platform));
35788
+ await probe.access(path17, directoryAccessMode(platform));
35391
35789
  return {
35392
35790
  ok: true,
35393
- creatableFrom: path15
35791
+ creatableFrom: path17
35394
35792
  };
35395
35793
  } catch (error2) {
35396
35794
  if (!isMissingPathError2(error2)) {
35397
35795
  return {
35398
35796
  ok: false,
35399
- creatableFrom: path15,
35400
- blockingPath: path15
35797
+ creatableFrom: path17,
35798
+ blockingPath: path17
35401
35799
  };
35402
35800
  }
35403
- const parent = dirname13(path15);
35404
- if (parent === path15) {
35801
+ const parent = dirname13(path17);
35802
+ if (parent === path17) {
35405
35803
  return {
35406
35804
  ok: false,
35407
- creatableFrom: path15,
35408
- blockingPath: path15
35805
+ creatableFrom: path17,
35806
+ blockingPath: path17
35409
35807
  };
35410
35808
  }
35411
35809
  const parentCheck = await checkCreatableAncestorDirectory(parent, probe, platform);
@@ -35844,10 +36242,10 @@ var init_render_doctor = __esm(() => {
35844
36242
  });
35845
36243
 
35846
36244
  // src/doctor/doctor.ts
35847
- import { homedir as homedir16 } from "node:os";
35848
- import { join as join24 } from "node:path";
36245
+ import { homedir as homedir18 } from "node:os";
36246
+ import { join as join25 } from "node:path";
35849
36247
  async function runDoctor(options = {}, deps = {}) {
35850
- const home = deps.home ?? process.env.HOME ?? homedir16();
36248
+ const home = deps.home ?? process.env.HOME ?? homedir18();
35851
36249
  const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
35852
36250
  const sharedLoadConfig = createSharedLoadConfig(runtimePaths, deps.loadConfig ?? loadConfig);
35853
36251
  const runners = [
@@ -36000,8 +36398,8 @@ function resolveDoctorRuntimePaths(home, resolver) {
36000
36398
  return resolveRuntimePaths();
36001
36399
  }
36002
36400
  return {
36003
- configPath: join24(coreHomeDir(home), "config.json"),
36004
- statePath: join24(coreHomeDir(home), "state.json")
36401
+ configPath: join25(coreHomeDir(home), "config.json"),
36402
+ statePath: join25(coreHomeDir(home), "state.json")
36005
36403
  };
36006
36404
  }
36007
36405
  function depsUseExplicitRuntimeOverrides() {
@@ -36184,8 +36582,8 @@ var init_doctor2 = __esm(async () => {
36184
36582
  // src/cli.ts
36185
36583
  init_core_home();
36186
36584
  import { randomUUID as randomUUID4 } from "node:crypto";
36187
- import { homedir as homedir17 } from "node:os";
36188
- import { dirname as dirname14, join as join25, sep as sep2 } from "node:path";
36585
+ import { homedir as homedir19 } from "node:os";
36586
+ import { dirname as dirname14, join as join26, sep as sep2 } from "node:path";
36189
36587
  import { fileURLToPath as fileURLToPath7 } from "node:url";
36190
36588
 
36191
36589
  // src/runtime/migrate-core-home.ts
@@ -52439,7 +52837,7 @@ async function createCliScheduledTaskService() {
52439
52837
  return new ScheduledTaskService(state, stateStore);
52440
52838
  }
52441
52839
  function resolveConfigPathForCurrentEnv() {
52442
- return coreEnv("CONFIG") ?? join25(coreHomeDir(requireHome2()), "config.json");
52840
+ return coreEnv("CONFIG") ?? join26(coreHomeDir(requireHome2()), "config.json");
52443
52841
  }
52444
52842
  function resolveDaemonPathsForCurrentConfig() {
52445
52843
  const configPath = resolveConfigPathForCurrentEnv();
@@ -52790,7 +53188,7 @@ function decodeFirstRunOnboarding(raw) {
52790
53188
  return null;
52791
53189
  }
52792
53190
  function requireHome2() {
52793
- const home = process.env.HOME ?? homedir17();
53191
+ const home = process.env.HOME ?? homedir19();
52794
53192
  if (!home) {
52795
53193
  throw new Error("Unable to resolve the current user home directory");
52796
53194
  }
@@ -52814,7 +53212,7 @@ function safeDaemonLogPaths() {
52814
53212
  const configPath = resolveConfigPathForCurrentEnv();
52815
53213
  const paths = resolveDaemonPathsForCurrentConfig();
52816
53214
  return {
52817
- appLog: join25(dirname14(configPath), "runtime", "app.log"),
53215
+ appLog: join26(dirname14(configPath), "runtime", "app.log"),
52818
53216
  stderrLog: paths.stderrLog
52819
53217
  };
52820
53218
  } catch {