@symerian/symi 2.6.5 → 2.6.7

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.
Files changed (45) hide show
  1. package/dist/{audio-preflight-DHTaS5U1.js → audio-preflight-BVaaZWkg.js} +4 -4
  2. package/dist/build-info.json +3 -3
  3. package/dist/bundled/boot-md/handler.js +6 -6
  4. package/dist/bundled/session-memory/handler.js +6 -6
  5. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  6. package/dist/{chrome-DYZwl5Gv.js → chrome-DkaXoP36.js} +7 -7
  7. package/dist/{command-registry-DeOD2MdC.js → command-registry-DJbyskBr.js} +4 -4
  8. package/dist/{completion-cli-B6IpFueu.js → completion-cli-Dfw9JhkN.js} +2 -2
  9. package/dist/{completion-cli-D9rF4Hsw.js → completion-cli-DosOadUD.js} +1 -1
  10. package/dist/control-ui/js/app.js +8 -0
  11. package/dist/{deliver-dODxSv3b.js → deliver-C46-vyqg.js} +1 -1
  12. package/dist/{doctor-completion-DIKftJUc.js → doctor-completion-DRucwWSo.js} +1 -1
  13. package/dist/{doctor-completion-CmxVAo2o.js → doctor-completion-Xx92Sz9s.js} +1 -1
  14. package/dist/entry.js +1 -1
  15. package/dist/{gateway-cli-DEPCdrmX.js → gateway-cli-D3kGHw5M.js} +165 -30
  16. package/dist/{gateway-cli-CSOiySht.js → gateway-cli-SlbXm0f7.js} +165 -30
  17. package/dist/{image-CXu8W39c.js → image-CuzFLQWC.js} +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/llm-slug-generator.js +6 -6
  20. package/dist/{onboard-z3fzzRA3.js → onboard-DFkXqe5P.js} +1 -1
  21. package/dist/{onboard-DZRuFOui.js → onboard-DlY7trcj.js} +1 -1
  22. package/dist/{onboarding-C-CKVquN.js → onboarding-BEDqTyYQ.js} +1 -1
  23. package/dist/{onboarding-Dt44Sq16.js → onboarding-mXPGwLNG.js} +1 -1
  24. package/dist/{onboarding.finalize-BS2mkyHV.js → onboarding.finalize-Ca-aQ6eE.js} +4 -4
  25. package/dist/{onboarding.finalize-Brx3PR8v.js → onboarding.finalize-DmeEphAd.js} +3 -3
  26. package/dist/{pi-embedded-helpers-pubKo8HQ.js → pi-embedded-helpers-CfqDGQ9J.js} +4 -4
  27. package/dist/{program-CLUzfgIi.js → program-T144qFaw.js} +2 -2
  28. package/dist/{program-context-Dy0GIFHq.js → program-context-xpDrT9eG.js} +6 -6
  29. package/dist/{prompt-select-styled-qBayGUev.js → prompt-select-styled-BN0aOmtR.js} +1 -1
  30. package/dist/{prompt-select-styled-u4kisg-4.js → prompt-select-styled-Bn4zyJmn.js} +1 -1
  31. package/dist/{pw-ai-B5asscAD.js → pw-ai-m0mj2KWK.js} +1 -1
  32. package/dist/{register.maintenance-CdLPllzi.js → register.maintenance-C9K829tk.js} +4 -4
  33. package/dist/{register.maintenance-Bo0Biy_N.js → register.maintenance-Ck1jwY_N.js} +5 -5
  34. package/dist/{register.onboard-ChIcWJCu.js → register.onboard-BHUh0Xk1.js} +2 -2
  35. package/dist/{register.onboard-C7dTjfHL.js → register.onboard-Byt2S-D0.js} +2 -2
  36. package/dist/{register.setup-CBpAYN-J.js → register.setup-BvB9oNh3.js} +2 -2
  37. package/dist/{register.setup-BtALSMOO.js → register.setup-DCG9bst7.js} +2 -2
  38. package/dist/{register.subclis-ffG-1Diq.js → register.subclis-DnWR9l5V.js} +3 -3
  39. package/dist/{run-main-D1Qgb9_t.js → run-main-B1aB8I5W.js} +3 -3
  40. package/dist/{runner-WAG0M5s9.js → runner-CU9l0uJh.js} +1 -1
  41. package/dist/{unified-runner-DpWGASP3.js → unified-runner-CxscxYKm.js} +16 -16
  42. package/dist/{update-cli-Kauib3Oa.js → update-cli-Bgrhor0g.js} +5 -5
  43. package/dist/{update-cli-BrS9rZHi.js → update-cli-D5QWigS0.js} +4 -4
  44. package/dist/{web-DhofKbBh.js → web-DShKO-0L.js} +6 -6
  45. package/package.json +1 -1
@@ -54,7 +54,7 @@ import { i as formatPortDiagnostics, n as inspectPortUsage, t as ensurePortAvail
54
54
  import "./trash-CWQQXWX3.js";
55
55
  import "./dock-DKxQXuAg.js";
56
56
  import "./accounts-D51FXYdA.js";
57
- import { c as resolveStorePath, i as resolveSessionTranscriptPath } from "./paths-DNdWAq7b.js";
57
+ import { c as resolveStorePath, i as resolveSessionTranscriptPath, s as resolveSessionTranscriptsDirForAgent } from "./paths-DNdWAq7b.js";
58
58
  import "./tool-images-CVLISeRT.js";
59
59
  import { d as supportsXHighThinking, l as normalizeVerboseLevel, s as normalizeThinkLevel } from "./thinking-8sKPnzpp.js";
60
60
  import "./models-config-CCMpqFyr.js";
@@ -77,7 +77,7 @@ import "./image-CO4meYzg.js";
77
77
  import "./tool-display-CXwOC-qw.js";
78
78
  import { d as registerUnhandledRejectionHandler } from "./runner-BORIO-D3.js";
79
79
  import { n as loadModelCatalog } from "./model-catalog-CqCsARJX.js";
80
- import { o as loadSessionEntry, u as lookupContextTokens } from "./session-utils-Bka9dR4m.js";
80
+ import { a as loadCombinedSessionStoreForGateway, i as listSessionsFromStore, o as loadSessionEntry, u as lookupContextTokens } from "./session-utils-Bka9dR4m.js";
81
81
  import { S as registerSkillsChangeListener, c as recordRemoteNodeInfo, d as removeRemoteNodeInfo, f as setSkillsRemoteRegistry, l as refreshRemoteBinsForConnectedNodes, o as getRemoteSkillEligibility, s as primeRemoteSkillsCache, u as refreshRemoteNodeBins, v as updatePairedNodeMetadata, x as getSkillsSnapshotVersion } from "./skill-commands-C8BcwE33.js";
82
82
  import "./workspace-dirs-CPNL2Acu.js";
83
83
  import { a as readChannelAllowFromStoreSync } from "./pairing-store-vpO8vXVN.js";
@@ -134,7 +134,7 @@ import { i as pickGatewaySelfPresence } from "./status-CfLq5R5j.js";
134
134
  import { a as styleHealthChannelLine, l as startHeartbeatRunner, n as getHealthSnapshot, s as runHeartbeatOnce, t as formatHealthChannelLines } from "./health-DUKLANXu.js";
135
135
  import { a as resolveControlUiRootSync, i as resolveControlUiRootOverrideSync, t as ensureControlUiAssetsBuilt } from "./control-ui-assets-BseSWee1.js";
136
136
  import { a as resolveNpmChannelTag, c as DEFAULT_PACKAGE_CHANNEL, m as normalizeUpdateChannel, n as compareSemverStrings, t as checkUpdateStatus } from "./update-check-ZdimP1aU.js";
137
- import { t as runOnboardingWizard } from "./onboarding-Dt44Sq16.js";
137
+ import { t as runOnboardingWizard } from "./onboarding-mXPGwLNG.js";
138
138
  import { _ as getHandshakeTimeoutMs, a as DEFAULT_ASSISTANT_IDENTITY, b as resolveCronRunLogPath, c as upsertPresence, d as DEDUPE_MAX, f as DEDUPE_TTL_MS, g as TICK_INTERVAL_MS, h as MAX_PAYLOAD_BYTES, i as safeParseJson, l as formatError, m as MAX_BUFFERED_BYTES, n as handleGatewayRequest, o as resolveAssistantIdentity, p as HEALTH_REFRESH_INTERVAL_MS, r as broadcastPresenceSnapshot, s as listSystemPresence, t as coreGatewayHandlers, u as loadVoiceWakeConfig, v as abortChatRunById, x as startGatewayConfigReloader, y as appendCronRunLog } from "./server-methods-D1W-tViM.js";
139
139
  import { d as shouldLogWs, f as summarizeAgentEventForWsLog, l as formatForLog, p as setGatewayWsLogStyle, u as logWs } from "./push-apns-CVRC-O3Q.js";
140
140
  import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-CzWeiE-Y.js";
@@ -2131,10 +2131,12 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
2131
2131
  runId: eventRunId
2132
2132
  } : evt;
2133
2133
  const isAborted = chatRunState.abortedRuns.has(clientRunId) || chatRunState.abortedRuns.has(evt.runId);
2134
- const agentPayload = sessionKey ? {
2134
+ const heartbeatAgentCtx = resolveHeartbeatContext(clientRunId, evt.runId);
2135
+ const agentPayload = {
2135
2136
  ...eventForClients,
2136
- sessionKey
2137
- } : eventForClients;
2137
+ ...sessionKey ? { sessionKey } : {},
2138
+ ...heartbeatAgentCtx?.isHeartbeat ? { isHeartbeat: true } : {}
2139
+ };
2138
2140
  const last = agentRunSeq.get(evt.runId) ?? 0;
2139
2141
  const isToolEvent = evt.stream === "tool";
2140
2142
  const toolVerbose = isToolEvent ? resolveToolVerboseLevel(evt.runId, sessionKey) : "off";
@@ -7319,7 +7321,7 @@ function applyControlUiSecurityHeaders(res) {
7319
7321
  res.setHeader("X-Content-Type-Options", "nosniff");
7320
7322
  res.setHeader("Referrer-Policy", "no-referrer");
7321
7323
  }
7322
- function sendJson$2(res, status, body) {
7324
+ function sendJson$3(res, status, body) {
7323
7325
  res.statusCode = status;
7324
7326
  res.setHeader("Content-Type", "application/json; charset=utf-8");
7325
7327
  res.setHeader("Cache-Control", "no-cache");
@@ -7346,7 +7348,7 @@ function handleControlUiAvatarRequest(req, res, opts) {
7346
7348
  }
7347
7349
  if (url.searchParams.get("meta") === "1") {
7348
7350
  const resolved = opts.resolveAvatar(agentId);
7349
- sendJson$2(res, 200, { avatarUrl: resolved.kind === "local" ? buildControlUiAvatarUrl(basePath, agentId) : resolved.kind === "remote" || resolved.kind === "data" ? resolved.url : null });
7351
+ sendJson$3(res, 200, { avatarUrl: resolved.kind === "local" ? buildControlUiAvatarUrl(basePath, agentId) : resolved.kind === "remote" || resolved.kind === "data" ? resolved.url : null });
7350
7352
  return true;
7351
7353
  }
7352
7354
  const resolved = opts.resolveAvatar(agentId);
@@ -7442,7 +7444,7 @@ function handleControlUiHttpRequest(req, res, opts) {
7442
7444
  assistantAgentId: identity.agentId
7443
7445
  };
7444
7446
  if (opts?.isLocalClient && opts?.gatewayToken) bootstrapConfig.gatewayToken = opts.gatewayToken;
7445
- sendJson$2(res, 200, bootstrapConfig);
7447
+ sendJson$3(res, 200, bootstrapConfig);
7446
7448
  return true;
7447
7449
  }
7448
7450
  const rootState = opts?.root;
@@ -7519,7 +7521,7 @@ function setDefaultSecurityHeaders(res) {
7519
7521
  res.setHeader("X-Content-Type-Options", "nosniff");
7520
7522
  res.setHeader("Referrer-Policy", "no-referrer");
7521
7523
  }
7522
- function sendJson$1(res, status, body) {
7524
+ function sendJson$2(res, status, body) {
7523
7525
  res.statusCode = status;
7524
7526
  res.setHeader("Content-Type", "application/json; charset=utf-8");
7525
7527
  res.end(JSON.stringify(body));
@@ -7534,14 +7536,14 @@ function sendMethodNotAllowed(res, allow = "POST") {
7534
7536
  sendText(res, 405, "Method Not Allowed");
7535
7537
  }
7536
7538
  function sendUnauthorized(res) {
7537
- sendJson$1(res, 401, { error: {
7539
+ sendJson$2(res, 401, { error: {
7538
7540
  message: "Unauthorized",
7539
7541
  type: "unauthorized"
7540
7542
  } });
7541
7543
  }
7542
7544
  function sendRateLimited(res, retryAfterMs) {
7543
7545
  if (retryAfterMs && retryAfterMs > 0) res.setHeader("Retry-After", String(Math.ceil(retryAfterMs / 1e3)));
7544
- sendJson$1(res, 429, { error: {
7546
+ sendJson$2(res, 429, { error: {
7545
7547
  message: "Too many failed authentication attempts. Please try again later.",
7546
7548
  type: "rate_limited"
7547
7549
  } });
@@ -7554,7 +7556,7 @@ function sendGatewayAuthFailure(res, authResult) {
7554
7556
  sendUnauthorized(res);
7555
7557
  }
7556
7558
  function sendInvalidRequest(res, message) {
7557
- sendJson$1(res, 400, { error: {
7559
+ sendJson$2(res, 400, { error: {
7558
7560
  message,
7559
7561
  type: "invalid_request_error"
7560
7562
  } });
@@ -7563,14 +7565,14 @@ async function readJsonBodyOrError(req, res, maxBytes) {
7563
7565
  const body = await readJsonBody(req, maxBytes);
7564
7566
  if (!body.ok) {
7565
7567
  if (body.error === "payload too large") {
7566
- sendJson$1(res, 413, { error: {
7568
+ sendJson$2(res, 413, { error: {
7567
7569
  message: "Payload too large",
7568
7570
  type: "invalid_request_error"
7569
7571
  } });
7570
7572
  return;
7571
7573
  }
7572
7574
  if (body.error === "request body timeout") {
7573
- sendJson$1(res, 408, { error: {
7575
+ sendJson$2(res, 408, { error: {
7574
7576
  message: "Request body timeout",
7575
7577
  type: "invalid_request_error"
7576
7578
  } });
@@ -7800,7 +7802,7 @@ async function handleOpenAiHttpRequest(req, res, opts) {
7800
7802
  });
7801
7803
  const prompt = buildAgentPrompt$1(payload.messages);
7802
7804
  if (!prompt.message) {
7803
- sendJson$1(res, 400, { error: {
7805
+ sendJson$2(res, 400, { error: {
7804
7806
  message: "Missing user message in `messages`.",
7805
7807
  type: "invalid_request_error"
7806
7808
  } });
@@ -7819,7 +7821,7 @@ async function handleOpenAiHttpRequest(req, res, opts) {
7819
7821
  messageChannel: "webchat",
7820
7822
  bestEffortDeliver: false
7821
7823
  }, defaultRuntime, deps));
7822
- sendJson$1(res, 200, {
7824
+ sendJson$2(res, 200, {
7823
7825
  id: runId,
7824
7826
  object: "chat.completion",
7825
7827
  created: Math.floor(Date.now() / 1e3),
@@ -7840,7 +7842,7 @@ async function handleOpenAiHttpRequest(req, res, opts) {
7840
7842
  });
7841
7843
  } catch (err) {
7842
7844
  logWarn(`openai-compat: chat completion failed: ${String(err)}`);
7843
- sendJson$1(res, 500, { error: {
7845
+ sendJson$2(res, 500, { error: {
7844
7846
  message: "internal error",
7845
7847
  type: "api_error"
7846
7848
  } });
@@ -8404,7 +8406,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
8404
8406
  const parseResult = CreateResponseBodySchema.safeParse(handled.body);
8405
8407
  if (!parseResult.success) {
8406
8408
  const issue = parseResult.error.issues[0];
8407
- sendJson$1(res, 400, { error: {
8409
+ sendJson$2(res, 400, { error: {
8408
8410
  message: issue ? `${issue.path.join(".")}: ${issue.message}` : "Invalid request body",
8409
8411
  type: "invalid_request_error"
8410
8412
  } });
@@ -8461,7 +8463,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
8461
8463
  }
8462
8464
  } catch (err) {
8463
8465
  logWarn(`openresponses: request parsing failed: ${String(err)}`);
8464
- sendJson$1(res, 400, { error: {
8466
+ sendJson$2(res, 400, { error: {
8465
8467
  message: "invalid request",
8466
8468
  type: "invalid_request_error"
8467
8469
  } });
@@ -8479,7 +8481,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
8479
8481
  toolChoicePrompt = toolChoiceResult.extraSystemPrompt;
8480
8482
  } catch (err) {
8481
8483
  logWarn(`openresponses: tool configuration failed: ${String(err)}`);
8482
- sendJson$1(res, 400, { error: {
8484
+ sendJson$2(res, 400, { error: {
8483
8485
  message: "invalid tool configuration",
8484
8486
  type: "invalid_request_error"
8485
8487
  } });
@@ -8503,7 +8505,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
8503
8505
  fileContext
8504
8506
  ].filter(Boolean).join("\n\n");
8505
8507
  if (!prompt.message) {
8506
- sendJson$1(res, 400, { error: {
8508
+ sendJson$2(res, 400, { error: {
8507
8509
  message: "Missing user message in `input`.",
8508
8510
  type: "invalid_request_error"
8509
8511
  } });
@@ -8532,7 +8534,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
8532
8534
  const pendingToolCalls = meta && typeof meta === "object" ? meta.pendingToolCalls : void 0;
8533
8535
  if (stopReason === "tool_calls" && pendingToolCalls && pendingToolCalls.length > 0) {
8534
8536
  const functionCall = pendingToolCalls[0];
8535
- sendJson$1(res, 200, createResponseResource({
8537
+ sendJson$2(res, 200, createResponseResource({
8536
8538
  id: responseId,
8537
8539
  model,
8538
8540
  status: "incomplete",
@@ -8547,7 +8549,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
8547
8549
  }));
8548
8550
  return true;
8549
8551
  }
8550
- sendJson$1(res, 200, createResponseResource({
8552
+ sendJson$2(res, 200, createResponseResource({
8551
8553
  id: responseId,
8552
8554
  model,
8553
8555
  status: "completed",
@@ -8560,7 +8562,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
8560
8562
  }));
8561
8563
  } catch (err) {
8562
8564
  logWarn(`openresponses: non-stream response failed: ${String(err)}`);
8563
- sendJson$1(res, 500, createResponseResource({
8565
+ sendJson$2(res, 500, createResponseResource({
8564
8566
  id: responseId,
8565
8567
  model,
8566
8568
  status: "failed",
@@ -8827,6 +8829,138 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
8827
8829
  return true;
8828
8830
  }
8829
8831
 
8832
+ //#endregion
8833
+ //#region src/gateway/server-sessions-api.ts
8834
+ /**
8835
+ * HTTP API for session history — serves the Glass UI History drawer.
8836
+ *
8837
+ * GET /api/sessions — List all sessions with metadata
8838
+ * GET /api/transcript?file=<filename> — Read a session transcript
8839
+ *
8840
+ * Both endpoints are read-only and serve data from the existing session
8841
+ * store and .jsonl transcript files.
8842
+ *
8843
+ * @module
8844
+ */
8845
+ /**
8846
+ * Handle /api/sessions and /api/transcript HTTP requests.
8847
+ * Returns true if the request was handled, false otherwise.
8848
+ */
8849
+ function handleSessionsApiRequest(req, res, cfg) {
8850
+ const url = new URL(req.url ?? "/", "http://localhost");
8851
+ if (url.pathname === "/api/sessions" && req.method === "GET") {
8852
+ handleListSessions(res, cfg);
8853
+ return true;
8854
+ }
8855
+ if (url.pathname === "/api/transcript" && req.method === "GET") {
8856
+ handleTranscript(res, cfg, url.searchParams.get("file"));
8857
+ return true;
8858
+ }
8859
+ return false;
8860
+ }
8861
+ function sendJson$1(res, data, status = 200) {
8862
+ res.writeHead(status, { "Content-Type": "application/json" });
8863
+ res.end(JSON.stringify(data));
8864
+ }
8865
+ function handleListSessions(res, cfg) {
8866
+ try {
8867
+ const sessionsDir = resolveSessionTranscriptsDirForAgent(resolveDefaultAgentId(cfg));
8868
+ const { storePath, store } = loadCombinedSessionStoreForGateway(cfg);
8869
+ const storeSessions = listSessionsFromStore({
8870
+ cfg,
8871
+ storePath,
8872
+ store,
8873
+ opts: {
8874
+ includeDerivedTitles: true,
8875
+ includeLastMessage: true
8876
+ }
8877
+ }).sessions.map((s) => ({
8878
+ file: s.sessionId ? `${s.sessionId}.jsonl` : void 0,
8879
+ sessionId: s.sessionId,
8880
+ sessionKey: s.key,
8881
+ isArchived: false,
8882
+ mtime: s.updatedAt ? new Date(s.updatedAt).toISOString() : void 0,
8883
+ preview: s.lastMessagePreview || s.derivedTitle || s.displayName || s.label || void 0,
8884
+ msgCount: (s.inputTokens ?? 0) > 0 || (s.outputTokens ?? 0) > 0 ? void 0 : void 0,
8885
+ size: void 0,
8886
+ model: s.model,
8887
+ modelProvider: s.modelProvider
8888
+ }));
8889
+ const archivedSessions = [];
8890
+ if (fs.existsSync(sessionsDir)) {
8891
+ const files = fs.readdirSync(sessionsDir);
8892
+ for (const file of files) {
8893
+ if (!file.includes(".jsonl.")) continue;
8894
+ const parts = file.split(".jsonl.");
8895
+ if (parts.length < 2 || !parts[1]) continue;
8896
+ const filePath = path.join(sessionsDir, file);
8897
+ try {
8898
+ const stat = fs.statSync(filePath);
8899
+ const lines = fs.readFileSync(filePath, "utf-8").split(/\r?\n/).filter((l) => l.trim());
8900
+ let preview;
8901
+ let msgCount = 0;
8902
+ for (const line of lines) try {
8903
+ const parsed = JSON.parse(line);
8904
+ if (parsed?.message?.role === "user" || parsed?.message?.role === "assistant") msgCount++;
8905
+ if (!preview && parsed?.message?.role === "user") preview = (typeof parsed.message.content === "string" ? parsed.message.content : Array.isArray(parsed.message.content) ? parsed.message.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join(" ") : "").trim().slice(0, 80) || void 0;
8906
+ } catch {}
8907
+ const suffix = parts[1];
8908
+ const dotIdx = suffix.indexOf(".");
8909
+ const archivedAt = dotIdx >= 0 ? suffix.slice(dotIdx + 1).replace(/-(?=\d{2}[T:-])/g, ":") : void 0;
8910
+ archivedSessions.push({
8911
+ file,
8912
+ sessionId: parts[0],
8913
+ isArchived: true,
8914
+ archivedAt: archivedAt || stat.mtime.toISOString(),
8915
+ mtime: stat.mtime.toISOString(),
8916
+ preview,
8917
+ msgCount,
8918
+ size: stat.size
8919
+ });
8920
+ } catch {}
8921
+ }
8922
+ }
8923
+ sendJson$1(res, [...storeSessions.filter((s) => s.sessionId), ...archivedSessions].toSorted((a, b) => {
8924
+ const aArchived = Boolean(a.isArchived);
8925
+ const bArchived = Boolean(b.isArchived);
8926
+ if (!aArchived && bArchived) return -1;
8927
+ if (aArchived && !bArchived) return 1;
8928
+ const aDate = typeof a.mtime === "string" ? a.mtime : typeof a.archivedAt === "string" ? a.archivedAt : "";
8929
+ return (typeof b.mtime === "string" ? b.mtime : typeof b.archivedAt === "string" ? b.archivedAt : "").localeCompare(aDate);
8930
+ }));
8931
+ } catch (err) {
8932
+ sendJson$1(res, { error: String(err) }, 500);
8933
+ }
8934
+ }
8935
+ function handleTranscript(res, cfg, file) {
8936
+ if (!file) {
8937
+ sendJson$1(res, { error: "Missing file parameter" }, 400);
8938
+ return;
8939
+ }
8940
+ const basename = path.basename(file);
8941
+ if (basename !== file || file.includes("..")) {
8942
+ sendJson$1(res, { error: "Invalid file parameter" }, 400);
8943
+ return;
8944
+ }
8945
+ try {
8946
+ const sessionsDir = resolveSessionTranscriptsDirForAgent(resolveDefaultAgentId(cfg));
8947
+ const filePath = path.join(sessionsDir, basename);
8948
+ if (!fs.existsSync(filePath)) {
8949
+ sendJson$1(res, { error: "Session file not found" }, 404);
8950
+ return;
8951
+ }
8952
+ const lines = fs.readFileSync(filePath, "utf-8").split(/\r?\n/).filter((l) => l.trim());
8953
+ const messages = [];
8954
+ for (const line of lines) try {
8955
+ const parsed = JSON.parse(line);
8956
+ if (parsed?.message?.role) messages.push(parsed.message);
8957
+ } catch {}
8958
+ sendJson$1(res, { messages });
8959
+ } catch (err) {
8960
+ sendJson$1(res, { error: String(err) }, 500);
8961
+ }
8962
+ }
8963
+
8830
8964
  //#endregion
8831
8965
  //#region src/gateway/tools-invoke-http.ts
8832
8966
  const DEFAULT_BODY_BYTES = 2 * 1024 * 1024;
@@ -8909,7 +9043,7 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
8909
9043
  if (process.env.VITEST && MEMORY_TOOL_NAMES.has(toolName)) {
8910
9044
  const reasons = resolveMemoryToolDisableReasons(cfg);
8911
9045
  if (reasons.length > 0) {
8912
- sendJson$1(res, 400, {
9046
+ sendJson$2(res, 400, {
8913
9047
  ok: false,
8914
9048
  error: {
8915
9049
  type: "invalid_request",
@@ -8981,7 +9115,7 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
8981
9115
  const gatewayDenySet = new Set(gatewayDenyNames);
8982
9116
  const tool = subagentFiltered.filter((t) => !gatewayDenySet.has(t.name)).find((t) => t.name === toolName);
8983
9117
  if (!tool) {
8984
- sendJson$1(res, 404, {
9118
+ sendJson$2(res, 404, {
8985
9119
  ok: false,
8986
9120
  error: {
8987
9121
  type: "not_found",
@@ -8996,14 +9130,14 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
8996
9130
  action,
8997
9131
  args
8998
9132
  });
8999
- sendJson$1(res, 200, {
9133
+ sendJson$2(res, 200, {
9000
9134
  ok: true,
9001
9135
  result: await tool.execute?.(`http-${Date.now()}`, toolArgs)
9002
9136
  });
9003
9137
  } catch (err) {
9004
9138
  const inputStatus = resolveToolInputErrorStatus(err);
9005
9139
  if (inputStatus !== null) {
9006
- sendJson$1(res, inputStatus, {
9140
+ sendJson$2(res, inputStatus, {
9007
9141
  ok: false,
9008
9142
  error: {
9009
9143
  type: "tool_error",
@@ -9013,7 +9147,7 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
9013
9147
  return true;
9014
9148
  }
9015
9149
  logWarn(`tools-invoke: tool execution failed: ${String(err)}`);
9016
- sendJson$1(res, 500, {
9150
+ sendJson$2(res, 500, {
9017
9151
  ok: false,
9018
9152
  error: {
9019
9153
  type: "tool_error",
@@ -9433,6 +9567,7 @@ function createGatewayHttpServer(opts) {
9433
9567
  if (await handleA2uiHttpRequest(req, res)) return;
9434
9568
  if (await canvasHost.handleHttpRequest(req, res)) return;
9435
9569
  }
9570
+ if (controlUiEnabled && handleSessionsApiRequest(req, res, configSnapshot)) return;
9436
9571
  if (controlUiEnabled) {
9437
9572
  if (handleControlUiAvatarRequest(req, res, {
9438
9573
  basePath: controlUiBasePath,