@openacp/cli 2026.330.3 → 2026.331.1

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 (145) hide show
  1. package/README.md +17 -0
  2. package/dist/adapter-ELG3VRZ3.js +14 -0
  3. package/dist/{agent-catalog-SZQQERV7.js → agent-catalog-UYD26QDK.js} +3 -3
  4. package/dist/{api-client-XTLRRFPX.js → api-client-PEMHYL5U.js} +2 -2
  5. package/dist/{api-server-JLBDKCU4.js → api-server-DATG2KBR.js} +3 -3
  6. package/dist/api-server-L5Z7XACW.js +7 -0
  7. package/dist/chunk-23SRIVG4.js +50 -0
  8. package/dist/chunk-23SRIVG4.js.map +1 -0
  9. package/dist/{chunk-2HEFALTZ.js → chunk-7GXEMMEV.js} +15 -15
  10. package/dist/{chunk-QWVHCTCA.js → chunk-7U6IZIJP.js} +37 -23
  11. package/dist/chunk-7U6IZIJP.js.map +1 -0
  12. package/dist/{chunk-FCTC7KDT.js → chunk-7YIKTRSM.js} +14 -10
  13. package/dist/chunk-7YIKTRSM.js.map +1 -0
  14. package/dist/{chunk-MITTQMGZ.js → chunk-BYCJQPMN.js} +5 -5
  15. package/dist/chunk-BYCJQPMN.js.map +1 -0
  16. package/dist/{chunk-XBZIHNKV.js → chunk-EWVXSTQK.js} +183 -49
  17. package/dist/chunk-EWVXSTQK.js.map +1 -0
  18. package/dist/{chunk-UWH7KIAA.js → chunk-FPKQYCQS.js} +88 -13
  19. package/dist/chunk-FPKQYCQS.js.map +1 -0
  20. package/dist/{chunk-GEOXPGCO.js → chunk-K6UY5M75.js} +12 -9
  21. package/dist/chunk-K6UY5M75.js.map +1 -0
  22. package/dist/{chunk-KDU3ZEWT.js → chunk-KGAQW6F4.js} +12 -3
  23. package/dist/chunk-KGAQW6F4.js.map +1 -0
  24. package/dist/{chunk-UCIZM5SW.js → chunk-LRV56K2M.js} +205 -16
  25. package/dist/chunk-LRV56K2M.js.map +1 -0
  26. package/dist/{chunk-V2YZWYXT.js → chunk-MDJHCCFS.js} +18 -17
  27. package/dist/chunk-MDJHCCFS.js.map +1 -0
  28. package/dist/chunk-NHD5XDD2.js +686 -0
  29. package/dist/chunk-NHD5XDD2.js.map +1 -0
  30. package/dist/{chunk-APS6UEFU.js → chunk-NJX75BLK.js} +1 -1
  31. package/dist/chunk-NJX75BLK.js.map +1 -0
  32. package/dist/{chunk-5HKQCYOI.js → chunk-NOEAJNTK.js} +14 -3
  33. package/dist/chunk-NOEAJNTK.js.map +1 -0
  34. package/dist/chunk-ON7HB5O7.js +58 -0
  35. package/dist/chunk-ON7HB5O7.js.map +1 -0
  36. package/dist/{chunk-5OCGO27U.js → chunk-OSBZXY2W.js} +2 -1
  37. package/dist/chunk-OSBZXY2W.js.map +1 -0
  38. package/dist/{chunk-PA6MNBG4.js → chunk-P3HHJANC.js} +32 -13
  39. package/dist/chunk-P3HHJANC.js.map +1 -0
  40. package/dist/{chunk-BTJHGSLM.js → chunk-R2YLDQLI.js} +9 -10
  41. package/dist/chunk-R2YLDQLI.js.map +1 -0
  42. package/dist/{chunk-237WYH6H.js → chunk-SSLVNCEA.js} +3 -2
  43. package/dist/chunk-SSLVNCEA.js.map +1 -0
  44. package/dist/{chunk-MPGEHTGE.js → chunk-TGP34LQN.js} +9 -7
  45. package/dist/chunk-TGP34LQN.js.map +1 -0
  46. package/dist/{chunk-TMVTSWVH.js → chunk-VUSCVRJL.js} +2 -1
  47. package/dist/chunk-VUSCVRJL.js.map +1 -0
  48. package/dist/chunk-XRJUS6FE.js +53 -0
  49. package/dist/chunk-XRJUS6FE.js.map +1 -0
  50. package/dist/{chunk-W4LK6WJP.js → chunk-YZCKSNRN.js} +24 -17
  51. package/dist/chunk-YZCKSNRN.js.map +1 -0
  52. package/dist/{chunk-3NAFXVQM.js → chunk-ZIRH6QWW.js} +7 -5
  53. package/dist/chunk-ZIRH6QWW.js.map +1 -0
  54. package/dist/cli.d.ts +11 -0
  55. package/dist/cli.js +334 -140
  56. package/dist/cli.js.map +1 -1
  57. package/dist/config-X4UP7H6R.js +13 -0
  58. package/dist/config-editor-7BENRVG5.js +11 -0
  59. package/dist/{config-registry-ZXAIJNYB.js → config-registry-M3FFWEVM.js} +3 -2
  60. package/dist/context-FVGCU5TI.js +9 -0
  61. package/dist/core-plugins-JSY2I44L.js +25 -0
  62. package/dist/{daemon-XFEMMJSZ.js → daemon-UOSRDEXW.js} +8 -3
  63. package/dist/doctor-6DLACBR4.js +10 -0
  64. package/dist/{file-service-HHB3JQIO.js → file-service-FQQYME7M.js} +2 -2
  65. package/dist/index.d.ts +259 -30
  66. package/dist/index.js +44 -33
  67. package/dist/index.js.map +1 -1
  68. package/dist/{install-cloudflared-JRJ4BSOM.js → install-cloudflared-LNS5L5FR.js} +5 -4
  69. package/dist/install-cloudflared-LNS5L5FR.js.map +1 -0
  70. package/dist/{install-context-EHYV5WRY.js → install-context-KZO5FR4D.js} +4 -3
  71. package/dist/install-context-KZO5FR4D.js.map +1 -0
  72. package/dist/{install-jq-ISTGT263.js → install-jq-SN4IA5K4.js} +3 -3
  73. package/dist/instance-context-FLCE7VZ4.js +13 -0
  74. package/dist/instance-registry-SW5FWKHO.js +7 -0
  75. package/dist/{main-VEJCG5PY.js → main-D7M2AKRM.js} +91 -48
  76. package/dist/main-D7M2AKRM.js.map +1 -0
  77. package/dist/{plugin-create-EHL76ZZG.js → plugin-create-HFKS23JY.js} +4 -2
  78. package/dist/{plugin-create-EHL76ZZG.js.map → plugin-create-HFKS23JY.js.map} +1 -1
  79. package/dist/{post-upgrade-Y26S2ZQ7.js → post-upgrade-F4YPMTUT.js} +6 -6
  80. package/dist/{security-2BA265LN.js → security-O4XGN2CM.js} +2 -2
  81. package/dist/{setup-DISPNDEK.js → setup-44WLBIOT.js} +209 -22
  82. package/dist/setup-44WLBIOT.js.map +1 -0
  83. package/dist/{speech-SG62JYIF.js → speech-GHTSWDAN.js} +2 -2
  84. package/dist/telegram-D7ASLVEB.js +7 -0
  85. package/dist/telegram-D7ASLVEB.js.map +1 -0
  86. package/dist/tunnel-ALJDPFDQ.js +10 -0
  87. package/dist/tunnel-ALJDPFDQ.js.map +1 -0
  88. package/dist/{tunnel-service-ZMO4THKE.js → tunnel-service-TBAHDXMF.js} +41 -547
  89. package/dist/tunnel-service-TBAHDXMF.js.map +1 -0
  90. package/package.json +1 -1
  91. package/dist/adapter-AWSI4GML.js +0 -13
  92. package/dist/api-server-5VNYFWJE.js +0 -7
  93. package/dist/chunk-237WYH6H.js.map +0 -1
  94. package/dist/chunk-3NAFXVQM.js.map +0 -1
  95. package/dist/chunk-4WXALZA3.js +0 -45
  96. package/dist/chunk-4WXALZA3.js.map +0 -1
  97. package/dist/chunk-5HKQCYOI.js.map +0 -1
  98. package/dist/chunk-5OCGO27U.js.map +0 -1
  99. package/dist/chunk-APS6UEFU.js.map +0 -1
  100. package/dist/chunk-BTJHGSLM.js.map +0 -1
  101. package/dist/chunk-FCTC7KDT.js.map +0 -1
  102. package/dist/chunk-GEOXPGCO.js.map +0 -1
  103. package/dist/chunk-KDU3ZEWT.js.map +0 -1
  104. package/dist/chunk-MITTQMGZ.js.map +0 -1
  105. package/dist/chunk-MPGEHTGE.js.map +0 -1
  106. package/dist/chunk-PA6MNBG4.js.map +0 -1
  107. package/dist/chunk-QWVHCTCA.js.map +0 -1
  108. package/dist/chunk-TMVTSWVH.js.map +0 -1
  109. package/dist/chunk-UCIZM5SW.js.map +0 -1
  110. package/dist/chunk-UWH7KIAA.js.map +0 -1
  111. package/dist/chunk-V2YZWYXT.js.map +0 -1
  112. package/dist/chunk-W4LK6WJP.js.map +0 -1
  113. package/dist/chunk-XBZIHNKV.js.map +0 -1
  114. package/dist/config-KN6NKKPF.js +0 -20
  115. package/dist/config-editor-76RVZS4B.js +0 -10
  116. package/dist/context-NXXW62NJ.js +0 -9
  117. package/dist/core-plugins-BPZY7SEB.js +0 -22
  118. package/dist/doctor-AV6AUO22.js +0 -9
  119. package/dist/install-cloudflared-JRJ4BSOM.js.map +0 -1
  120. package/dist/install-context-EHYV5WRY.js.map +0 -1
  121. package/dist/main-VEJCG5PY.js.map +0 -1
  122. package/dist/setup-DISPNDEK.js.map +0 -1
  123. package/dist/telegram-L3YM6SQJ.js +0 -7
  124. package/dist/tunnel-HWJ27WDH.js +0 -7
  125. package/dist/tunnel-service-ZMO4THKE.js.map +0 -1
  126. /package/dist/{adapter-AWSI4GML.js.map → adapter-ELG3VRZ3.js.map} +0 -0
  127. /package/dist/{agent-catalog-SZQQERV7.js.map → agent-catalog-UYD26QDK.js.map} +0 -0
  128. /package/dist/{api-client-XTLRRFPX.js.map → api-client-PEMHYL5U.js.map} +0 -0
  129. /package/dist/{api-server-5VNYFWJE.js.map → api-server-DATG2KBR.js.map} +0 -0
  130. /package/dist/{api-server-JLBDKCU4.js.map → api-server-L5Z7XACW.js.map} +0 -0
  131. /package/dist/{chunk-2HEFALTZ.js.map → chunk-7GXEMMEV.js.map} +0 -0
  132. /package/dist/{config-KN6NKKPF.js.map → config-X4UP7H6R.js.map} +0 -0
  133. /package/dist/{config-editor-76RVZS4B.js.map → config-editor-7BENRVG5.js.map} +0 -0
  134. /package/dist/{config-registry-ZXAIJNYB.js.map → config-registry-M3FFWEVM.js.map} +0 -0
  135. /package/dist/{context-NXXW62NJ.js.map → context-FVGCU5TI.js.map} +0 -0
  136. /package/dist/{core-plugins-BPZY7SEB.js.map → core-plugins-JSY2I44L.js.map} +0 -0
  137. /package/dist/{daemon-XFEMMJSZ.js.map → daemon-UOSRDEXW.js.map} +0 -0
  138. /package/dist/{doctor-AV6AUO22.js.map → doctor-6DLACBR4.js.map} +0 -0
  139. /package/dist/{file-service-HHB3JQIO.js.map → file-service-FQQYME7M.js.map} +0 -0
  140. /package/dist/{install-jq-ISTGT263.js.map → install-jq-SN4IA5K4.js.map} +0 -0
  141. /package/dist/{security-2BA265LN.js.map → instance-context-FLCE7VZ4.js.map} +0 -0
  142. /package/dist/{speech-SG62JYIF.js.map → instance-registry-SW5FWKHO.js.map} +0 -0
  143. /package/dist/{post-upgrade-Y26S2ZQ7.js.map → post-upgrade-F4YPMTUT.js.map} +0 -0
  144. /package/dist/{telegram-L3YM6SQJ.js.map → security-O4XGN2CM.js.map} +0 -0
  145. /package/dist/{tunnel-HWJ27WDH.js.map → speech-GHTSWDAN.js.map} +0 -0
@@ -1,14 +1,13 @@
1
1
  import {
2
2
  ContextManager,
3
3
  EntireProvider
4
- } from "./chunk-MITTQMGZ.js";
4
+ } from "./chunk-BYCJQPMN.js";
5
5
  import {
6
6
  DEFAULT_MAX_TOKENS,
7
7
  TOKENS_PER_TURN_ESTIMATE
8
- } from "./chunk-APS6UEFU.js";
8
+ } from "./chunk-NJX75BLK.js";
9
9
 
10
10
  // src/plugins/context/index.ts
11
- import * as os from "os";
12
11
  import * as path2 from "path";
13
12
 
14
13
  // src/plugins/context/history/history-context-builder.ts
@@ -243,19 +242,20 @@ var HistoryProvider = class {
243
242
  return { ...EMPTY_RESULT };
244
243
  }
245
244
  const totalTurns = loaded.reduce((sum, s) => sum + s.history.turns.length, 0);
245
+ const labelAgent = options?.labelAgent ?? false;
246
246
  let mode = selectLevel(totalTurns);
247
- let markdown = this.buildMergedMarkdown(loaded, mode, query);
247
+ let markdown = this.buildMergedMarkdown(loaded, mode, query, labelAgent);
248
248
  let tokenEstimate = estimateTokens(markdown);
249
249
  if (tokenEstimate > maxTokens && mode !== "compact") {
250
250
  mode = "compact";
251
- markdown = this.buildMergedMarkdown(loaded, mode, query);
251
+ markdown = this.buildMergedMarkdown(loaded, mode, query, labelAgent);
252
252
  tokenEstimate = estimateTokens(markdown);
253
253
  }
254
254
  let truncated = false;
255
255
  let activeSessions = [...loaded];
256
256
  while (tokenEstimate > maxTokens && activeSessions.length > 1) {
257
257
  activeSessions = activeSessions.slice(0, activeSessions.length - 1);
258
- markdown = this.buildMergedMarkdown(activeSessions, mode, query);
258
+ markdown = this.buildMergedMarkdown(activeSessions, mode, query, labelAgent);
259
259
  tokenEstimate = estimateTokens(markdown);
260
260
  truncated = true;
261
261
  }
@@ -300,7 +300,7 @@ var HistoryProvider = class {
300
300
  sessionId: record.sessionId
301
301
  };
302
302
  }
303
- buildMergedMarkdown(sessions, mode, query) {
303
+ buildMergedMarkdown(sessions, mode, query, labelAgent = false) {
304
304
  if (sessions.length === 0) return "";
305
305
  const totalTurns = sessions.reduce((sum, s) => sum + s.history.turns.length, 0);
306
306
  const title = query.type === "session" ? query.value : `latest ${sessions.length} sessions`;
@@ -310,11 +310,19 @@ var HistoryProvider = class {
310
310
  parts.push("");
311
311
  for (let i = 0; i < sessions.length; i++) {
312
312
  const { record, history } = sessions[i];
313
- const sessionMd = buildHistoryMarkdown(history.turns, mode);
314
313
  parts.push(`## Session ${i + 1} \u2014 ${record.agentName} \xB7 ${record.sessionId} (${history.turns.length} turns)`);
315
314
  parts.push("");
316
- if (sessionMd) {
317
- parts.push(sessionMd);
315
+ if (labelAgent && history.turns.length > 0) {
316
+ const agentTimeline = this.buildAgentTimeline(record);
317
+ const labeledMd = this.buildLabeledHistoryMarkdown(history.turns, mode, agentTimeline);
318
+ if (labeledMd) {
319
+ parts.push(labeledMd);
320
+ }
321
+ } else {
322
+ const sessionMd = buildHistoryMarkdown(history.turns, mode);
323
+ if (sessionMd) {
324
+ parts.push(sessionMd);
325
+ }
318
326
  }
319
327
  }
320
328
  parts.push(
@@ -322,6 +330,72 @@ var HistoryProvider = class {
322
330
  );
323
331
  return parts.join("\n");
324
332
  }
333
+ /**
334
+ * Build a timeline of agent boundaries from the session record.
335
+ * Returns sorted entries: [{ agentName, startedAt }] where startedAt is the
336
+ * ISO timestamp when that agent started handling the session.
337
+ *
338
+ * The first agent runs from session creation until the first switch.
339
+ * Each agentSwitchHistory entry records when the *previous* agent was switched away,
340
+ * so the next agent starts at that switchedAt timestamp.
341
+ */
342
+ buildAgentTimeline(record) {
343
+ const timeline = [];
344
+ const firstAgentName = record.firstAgent ?? record.agentName;
345
+ timeline.push({ agentName: firstAgentName, switchedAt: 0 });
346
+ if (record.agentSwitchHistory && record.agentSwitchHistory.length > 0) {
347
+ for (let i = 0; i < record.agentSwitchHistory.length; i++) {
348
+ const entry = record.agentSwitchHistory[i];
349
+ const switchTime = new Date(entry.switchedAt).getTime();
350
+ const nextAgent = i < record.agentSwitchHistory.length - 1 ? record.agentSwitchHistory[i + 1].agentName : record.agentName;
351
+ timeline.push({ agentName: nextAgent, switchedAt: switchTime });
352
+ }
353
+ }
354
+ return timeline;
355
+ }
356
+ /**
357
+ * Determine which agent produced a turn based on its timestamp and the agent timeline.
358
+ */
359
+ resolveAgentForTurn(turnTimestamp, timeline) {
360
+ const turnTime = new Date(turnTimestamp).getTime();
361
+ for (let i = timeline.length - 1; i >= 0; i--) {
362
+ if (turnTime >= timeline[i].switchedAt) {
363
+ return timeline[i].agentName;
364
+ }
365
+ }
366
+ return timeline[0].agentName;
367
+ }
368
+ /**
369
+ * Build history markdown with agent labels inserted at agent boundaries.
370
+ */
371
+ buildLabeledHistoryMarkdown(turns, mode, agentTimeline) {
372
+ if (agentTimeline.length <= 1) {
373
+ const label = `### [${agentTimeline[0]?.agentName ?? "unknown"}]
374
+ `;
375
+ const md = buildHistoryMarkdown(turns, mode);
376
+ return md ? label + "\n" + md : label;
377
+ }
378
+ const segments = [];
379
+ let currentAgent = "";
380
+ for (const turn of turns) {
381
+ const agent = this.resolveAgentForTurn(turn.timestamp, agentTimeline);
382
+ if (agent !== currentAgent) {
383
+ segments.push({ agentName: agent, turns: [] });
384
+ currentAgent = agent;
385
+ }
386
+ segments[segments.length - 1].turns.push(turn);
387
+ }
388
+ const parts = [];
389
+ for (const segment of segments) {
390
+ parts.push(`### [${segment.agentName}]`);
391
+ parts.push("");
392
+ const md = buildHistoryMarkdown(segment.turns, mode);
393
+ if (md) {
394
+ parts.push(md);
395
+ }
396
+ }
397
+ return parts.join("\n");
398
+ }
325
399
  computeTimeRange(records) {
326
400
  if (records.length === 0) return { start: "", end: "" };
327
401
  const dates = records.map((r) => r.createdAt).filter(Boolean);
@@ -646,12 +720,13 @@ var contextPlugin = {
646
720
  }
647
721
  },
648
722
  async setup(ctx) {
649
- const historyDir = path2.join(os.homedir(), ".openacp", "history");
723
+ const historyDir = path2.join(ctx.instanceRoot, "history");
650
724
  const store = new HistoryStore(historyDir);
651
725
  const recorder = new HistoryRecorder(store);
652
726
  const sessionManager = ctx.sessions;
653
727
  const getRecords = () => sessionManager.listRecords();
654
- const manager = new ContextManager();
728
+ const cachePath = path2.join(ctx.instanceRoot, "cache", "entire");
729
+ const manager = new ContextManager(cachePath);
655
730
  manager.register(new HistoryProvider(store, getRecords));
656
731
  manager.register(new EntireProvider());
657
732
  ctx.registerService("context", manager);
@@ -698,4 +773,4 @@ var context_default = contextPlugin;
698
773
  export {
699
774
  context_default
700
775
  };
701
- //# sourceMappingURL=chunk-UWH7KIAA.js.map
776
+ //# sourceMappingURL=chunk-FPKQYCQS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugins/context/index.ts","../../src/plugins/context/history/history-context-builder.ts","../../src/plugins/context/history/history-provider.ts","../../src/plugins/context/history/history-recorder.ts","../../src/plugins/context/history/history-store.ts"],"sourcesContent":["import * as path from 'node:path'\nimport type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js'\nimport type { SessionRecord } from '../../core/types.js'\nimport { ContextManager } from './context-manager.js'\nimport { EntireProvider } from './entire/entire-provider.js'\nimport { HistoryProvider } from './history/history-provider.js'\nimport { HistoryRecorder } from './history/history-recorder.js'\nimport { HistoryStore } from './history/history-store.js'\n\nconst contextPlugin: OpenACPPlugin = {\n name: '@openacp/context',\n version: '1.0.0',\n description: 'Conversation context management with pluggable providers',\n essential: false,\n permissions: ['services:register', 'middleware:register', 'kernel:access'],\n\n async install(ctx: InstallContext) {\n const { settings, terminal } = ctx\n\n // No interactive prompts needed — save defaults\n await settings.setAll({ enabled: true })\n terminal.log.success('Context defaults saved')\n },\n\n async configure(ctx: InstallContext) {\n const { terminal, settings } = ctx\n const current = await settings.getAll()\n\n const toggle = await terminal.confirm({\n message: `Context service is ${current.enabled !== false ? 'enabled' : 'disabled'}. Toggle?`,\n initialValue: false,\n })\n if (toggle) {\n const newState = current.enabled === false\n await settings.set('enabled', newState)\n terminal.log.success(`Context service ${newState ? 'enabled' : 'disabled'}`)\n }\n },\n\n async uninstall(ctx: InstallContext, opts: { purge: boolean }) {\n if (opts.purge) {\n await ctx.settings.clear()\n ctx.terminal.log.success('Context settings cleared')\n }\n },\n\n async setup(ctx) {\n // History recording and context\n const historyDir = path.join(ctx.instanceRoot, 'history')\n const store = new HistoryStore(historyDir)\n const recorder = new HistoryRecorder(store)\n\n // Access session records via SessionManager (kernel:access)\n const sessionManager = ctx.sessions as { listRecords(): SessionRecord[] }\n const getRecords = () => sessionManager.listRecords()\n\n // Register providers — local first (priority), entire as fallback\n const cachePath = path.join(ctx.instanceRoot, 'cache', 'entire')\n const manager = new ContextManager(cachePath)\n manager.register(new HistoryProvider(store, getRecords))\n manager.register(new EntireProvider())\n ctx.registerService('context', manager)\n\n // Middleware: capture user prompts\n ctx.registerMiddleware('agent:beforePrompt', {\n priority: 200,\n handler: async (payload, next) => {\n recorder.onBeforePrompt(payload.sessionId, payload.text, payload.attachments)\n return next()\n },\n })\n\n // Middleware: capture agent events\n ctx.registerMiddleware('agent:afterEvent', {\n priority: 200,\n handler: async (payload, next) => {\n recorder.onAfterEvent(payload.sessionId, payload.event)\n return next()\n },\n })\n\n // Middleware: finalize turn and write to disk\n ctx.registerMiddleware('turn:end', {\n priority: 200,\n handler: async (payload, next) => {\n await recorder.onTurnEnd(payload.sessionId, payload.stopReason)\n return next()\n },\n })\n\n // Middleware: capture permission resolutions\n ctx.registerMiddleware('permission:afterResolve', {\n priority: 200,\n handler: async (payload, next) => {\n recorder.onPermissionResolved(payload.sessionId, payload.requestId, payload.decision)\n return next()\n },\n })\n\n // Middleware: clean up recorder memory on session destroy\n ctx.registerMiddleware('session:afterDestroy', {\n priority: 200,\n handler: async (payload, next) => {\n recorder.finalize(payload.sessionId)\n return next()\n },\n })\n\n ctx.log.info('Context service ready (local history + entire providers)')\n },\n}\n\nexport default contextPlugin\n","import type { ContextMode } from \"../context-provider.js\";\nimport type { Turn, Step, ToolCallStep } from \"./types.js\";\n\nexport function selectLevel(turnCount: number): ContextMode {\n if (turnCount <= 10) return \"full\";\n if (turnCount <= 25) return \"balanced\";\n return \"compact\";\n}\n\nexport function estimateTokens(text: string): number {\n return Math.floor(text.length / 4);\n}\n\nexport function buildHistoryMarkdown(turns: Turn[], mode: ContextMode): string {\n if (turns.length === 0) return \"\";\n switch (mode) {\n case \"full\":\n return buildFull(turns);\n case \"balanced\":\n return buildBalanced(turns);\n case \"compact\":\n return buildCompact(turns);\n }\n}\n\n// ─── Full Mode ───────────────────────────────────────────────────────────────\n\nfunction buildFull(turns: Turn[]): string {\n const out: string[] = [];\n let userIndex = 0;\n\n for (const turn of turns) {\n if (turn.role === \"user\") {\n userIndex++;\n out.push(`**User [${userIndex}]:**`);\n out.push(turn.content ?? \"\");\n if (turn.attachments?.length) {\n out.push(turn.attachments.map((a) => `[${a.type}: ${a.fileName}]`).join(\" \"));\n }\n out.push(\"\");\n } else if (turn.role === \"assistant\" && turn.steps?.length) {\n out.push(\"**Assistant:**\");\n\n for (const step of turn.steps) {\n out.push(renderStepFull(step));\n }\n\n if (turn.usage) {\n const parts: string[] = [];\n if (turn.usage.tokensUsed) parts.push(`${turn.usage.tokensUsed.toLocaleString()} tokens`);\n if (turn.usage.cost) parts.push(`$${turn.usage.cost.amount.toFixed(4)}`);\n if (parts.length) out.push(`**Usage**: ${parts.join(\", \")}`);\n }\n\n out.push(\"\");\n out.push(\"---\");\n out.push(\"\");\n }\n }\n\n return out.join(\"\\n\");\n}\n\nfunction renderStepFull(step: Step): string {\n switch (step.type) {\n case \"thinking\":\n return `> **Thinking**: ${step.content}\\n`;\n case \"text\":\n return `${step.content}\\n`;\n case \"tool_call\":\n return renderToolCallFull(step);\n case \"plan\":\n return renderPlan(step.entries);\n case \"image\":\n return `[Image: ${step.mimeType}]\\n`;\n case \"audio\":\n return `[Audio: ${step.mimeType}]\\n`;\n case \"resource\":\n return `[Resource: ${step.name}] ${step.uri}\\n`;\n case \"resource_link\":\n return `[Resource Link: ${step.name}] ${step.uri}\\n`;\n case \"mode_change\":\n return `*Mode changed to: ${step.modeId}*\\n`;\n case \"config_change\":\n return `*Config ${step.configId} set to: ${step.value}*\\n`;\n }\n}\n\nfunction renderToolCallFull(step: ToolCallStep): string {\n const lines: string[] = [];\n const loc = step.locations?.[0];\n const locStr = loc ? (loc.line ? `${loc.path}:${loc.line}` : loc.path) : \"\";\n\n if (step.diff) {\n lines.push(`**[${step.name}]** \\`${locStr || step.diff.path}\\``);\n lines.push(\"```diff\");\n if (step.diff.oldText) {\n for (const line of step.diff.oldText.split(\"\\n\")) lines.push(`- ${line}`);\n }\n for (const line of step.diff.newText.split(\"\\n\")) lines.push(`+ ${line}`);\n lines.push(\"```\");\n } else {\n lines.push(`**[${step.name}]** \\`${locStr}\\``);\n }\n\n if (step.permission) {\n lines.push(`*Permission: ${step.permission.outcome}*`);\n }\n\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nfunction renderPlan(entries: { content: string; priority: string; status: string }[]): string {\n const lines = [\"**Plan:**\"];\n for (const e of entries) {\n const icon = e.status === \"completed\" || e.status === \"done\" ? \"✅\" : e.status === \"in_progress\" ? \"🔄\" : \"⬜\";\n lines.push(`${icon} ${e.content} (${e.priority})`);\n }\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\n// ─── Balanced Mode ───────────────────────────────────────────────────────────\n\nfunction buildBalanced(turns: Turn[]): string {\n const out: string[] = [];\n let userIndex = 0;\n\n for (const turn of turns) {\n if (turn.role === \"user\") {\n userIndex++;\n out.push(`**User [${userIndex}]:**`);\n out.push(turn.content ?? \"\");\n out.push(\"\");\n } else if (turn.role === \"assistant\" && turn.steps?.length) {\n out.push(\"**Assistant:**\");\n\n for (const step of turn.steps) {\n if (step.type === \"thinking\") continue;\n\n if (step.type === \"text\") {\n out.push(step.content);\n } else if (step.type === \"tool_call\") {\n out.push(renderToolCallBalanced(step));\n } else if (step.type === \"plan\") {\n out.push(renderPlan(step.entries));\n } else {\n out.push(renderStepFull(step));\n }\n }\n\n out.push(\"\");\n out.push(\"---\");\n out.push(\"\");\n }\n }\n\n return out.join(\"\\n\");\n}\n\nfunction renderToolCallBalanced(step: ToolCallStep): string {\n const loc = step.locations?.[0];\n const locStr = loc ? (loc.line ? `${loc.path}:${loc.line}` : loc.path) : \"\";\n\n if (step.diff) {\n const oldLines = step.diff.oldText?.split(\"\\n\").length ?? 0;\n const newLines = step.diff.newText.split(\"\\n\").length;\n return `- ${step.name} \\`${locStr || step.diff.path}\\` (-${oldLines}/+${newLines} lines)`;\n }\n\n return `- ${step.name} \\`${locStr}\\``;\n}\n\n// ─── Compact Mode ────────────────────────────────────────────────────────────\n\nfunction buildCompact(turns: Turn[]): string {\n const out: string[] = [];\n let i = 0;\n\n while (i < turns.length) {\n const turn = turns[i];\n if (turn.role === \"user\") {\n const userText = (turn.content ?? \"\").slice(0, 100);\n const nextTurn = turns[i + 1];\n if (nextTurn?.role === \"assistant\" && nextTurn.steps?.length) {\n const tools = nextTurn.steps\n .filter((s) => s.type === \"tool_call\")\n .map((s) => (s as ToolCallStep).name);\n const texts = nextTurn.steps\n .filter((s) => s.type === \"text\")\n .map((s) => (s as { content: string }).content.slice(0, 80));\n const parts: string[] = [];\n if (tools.length) parts.push(tools.join(\", \"));\n if (texts.length) parts.push(texts.join(\" \"));\n out.push(`User: ${userText} → Assistant: ${parts.join(\" | \")}`);\n i += 2;\n } else {\n out.push(`User: ${userText}`);\n i++;\n }\n } else {\n i++;\n }\n }\n\n return out.join(\"\\n\");\n}\n","import type { SessionRecord } from \"../../../core/types.js\";\nimport type {\n ContextProvider,\n ContextQuery,\n ContextOptions,\n ContextResult,\n ContextMode,\n SessionInfo,\n SessionListResult,\n} from \"../context-provider.js\";\nimport { DEFAULT_MAX_TOKENS, TOKENS_PER_TURN_ESTIMATE } from \"../context-provider.js\";\nimport { HistoryStore } from \"./history-store.js\";\nimport {\n buildHistoryMarkdown,\n selectLevel,\n estimateTokens,\n} from \"./history-context-builder.js\";\n\nconst EMPTY_RESULT: ContextResult = {\n markdown: \"\",\n tokenEstimate: 0,\n sessionCount: 0,\n totalTurns: 0,\n mode: \"full\",\n truncated: false,\n timeRange: { start: \"\", end: \"\" },\n};\n\nexport class HistoryProvider implements ContextProvider {\n readonly name = \"local\";\n\n constructor(\n private readonly store: HistoryStore,\n private readonly getSessionRecords: () => SessionRecord[]\n ) {}\n\n async isAvailable(_repoPath: string): Promise<boolean> {\n return true;\n }\n\n async listSessions(query: ContextQuery): Promise<SessionListResult> {\n if (!this.isSupportedType(query.type)) {\n return { sessions: [], estimatedTokens: 0 };\n }\n\n const candidates = await this.resolveCandidates(query);\n const sessions: SessionInfo[] = [];\n let estimatedTokens = 0;\n\n for (const record of candidates) {\n const history = await this.store.read(record.sessionId);\n if (!history) continue;\n const turnCount = history.turns.length;\n const tokenEstimate = turnCount * TOKENS_PER_TURN_ESTIMATE;\n sessions.push(this.toSessionInfo(record, turnCount));\n estimatedTokens += tokenEstimate;\n }\n\n return { sessions, estimatedTokens };\n }\n\n async buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult> {\n if (!this.isSupportedType(query.type)) {\n return { ...EMPTY_RESULT };\n }\n\n const maxTokens = options?.maxTokens ?? DEFAULT_MAX_TOKENS;\n const candidates = await this.resolveCandidates(query, options?.limit);\n\n // Load histories for sessions that have files\n type LoadedSession = {\n record: SessionRecord;\n history: import(\"./types.js\").SessionHistory;\n };\n\n const loaded: LoadedSession[] = [];\n for (const record of candidates) {\n const history = await this.store.read(record.sessionId);\n if (history) {\n loaded.push({ record, history });\n }\n }\n\n if (loaded.length === 0) {\n return { ...EMPTY_RESULT };\n }\n\n const totalTurns = loaded.reduce((sum, s) => sum + s.history.turns.length, 0);\n const labelAgent = options?.labelAgent ?? false;\n\n // Auto-select mode based on total turn count\n let mode: ContextMode = selectLevel(totalTurns);\n\n // Build markdown with selected mode\n let markdown = this.buildMergedMarkdown(loaded, mode, query, labelAgent);\n let tokenEstimate = estimateTokens(markdown);\n\n // Downgrade to compact if over budget\n if (tokenEstimate > maxTokens && mode !== \"compact\") {\n mode = \"compact\";\n markdown = this.buildMergedMarkdown(loaded, mode, query, labelAgent);\n tokenEstimate = estimateTokens(markdown);\n }\n\n // Truncate oldest sessions if still over budget\n let truncated = false;\n let activeSessions = [...loaded];\n while (tokenEstimate > maxTokens && activeSessions.length > 1) {\n // Remove the oldest session (last in list, sorted newest-first)\n activeSessions = activeSessions.slice(0, activeSessions.length - 1);\n markdown = this.buildMergedMarkdown(activeSessions, mode, query, labelAgent);\n tokenEstimate = estimateTokens(markdown);\n truncated = true;\n }\n\n const timeRange = this.computeTimeRange(activeSessions.map((s) => s.record));\n\n return {\n markdown,\n tokenEstimate,\n sessionCount: activeSessions.length,\n totalTurns: activeSessions.reduce((sum, s) => sum + s.history.turns.length, 0),\n mode,\n truncated,\n timeRange,\n };\n }\n\n // ─── Private helpers ─────────────────────────────────────────────────────────\n\n private isSupportedType(type: ContextQuery[\"type\"]): boolean {\n return type === \"session\" || type === \"latest\";\n }\n\n private async resolveCandidates(query: ContextQuery, limit?: number): Promise<SessionRecord[]> {\n const all = this.getSessionRecords();\n\n if (query.type === \"session\") {\n const found = all.find((r) => r.sessionId === query.value);\n return found ? [found] : [];\n }\n\n // latest: sort by lastActiveAt descending, take N\n const n = limit ?? (parseInt(query.value, 10) || 5);\n const sorted = [...all].sort(\n (a, b) => new Date(b.lastActiveAt).getTime() - new Date(a.lastActiveAt).getTime()\n );\n return sorted.slice(0, n);\n }\n\n private toSessionInfo(record: SessionRecord, turnCount: number): SessionInfo {\n return {\n checkpointId: \"\",\n sessionIndex: \"\",\n transcriptPath: \"\",\n createdAt: record.createdAt,\n endedAt: record.lastActiveAt,\n branch: \"\",\n agent: record.agentName,\n turnCount,\n filesTouched: [],\n sessionId: record.sessionId,\n };\n }\n\n private buildMergedMarkdown(\n sessions: Array<{ record: SessionRecord; history: import(\"./types.js\").SessionHistory }>,\n mode: ContextMode,\n query: ContextQuery,\n labelAgent = false\n ): string {\n if (sessions.length === 0) return \"\";\n\n const totalTurns = sessions.reduce((sum, s) => sum + s.history.turns.length, 0);\n const title = query.type === \"session\" ? query.value : `latest ${sessions.length} sessions`;\n\n const parts: string[] = [];\n parts.push(`# Conversation History — ${title}`);\n parts.push(`${sessions.length} sessions | ${totalTurns} turns | mode: ${mode}`);\n parts.push(\"\");\n\n for (let i = 0; i < sessions.length; i++) {\n const { record, history } = sessions[i];\n\n parts.push(`## Session ${i + 1} — ${record.agentName} · ${record.sessionId} (${history.turns.length} turns)`);\n parts.push(\"\");\n\n if (labelAgent && history.turns.length > 0) {\n const agentTimeline = this.buildAgentTimeline(record);\n const labeledMd = this.buildLabeledHistoryMarkdown(history.turns, mode, agentTimeline);\n if (labeledMd) {\n parts.push(labeledMd);\n }\n } else {\n const sessionMd = buildHistoryMarkdown(history.turns, mode);\n if (sessionMd) {\n parts.push(sessionMd);\n }\n }\n }\n\n parts.push(\n \"> **Note:** This conversation history may contain outdated information. Verify current state before acting on past context.\"\n );\n\n return parts.join(\"\\n\");\n }\n\n /**\n * Build a timeline of agent boundaries from the session record.\n * Returns sorted entries: [{ agentName, startedAt }] where startedAt is the\n * ISO timestamp when that agent started handling the session.\n *\n * The first agent runs from session creation until the first switch.\n * Each agentSwitchHistory entry records when the *previous* agent was switched away,\n * so the next agent starts at that switchedAt timestamp.\n */\n private buildAgentTimeline(record: SessionRecord): Array<{ agentName: string; switchedAt: number }> {\n const timeline: Array<{ agentName: string; switchedAt: number }> = [];\n\n // The first agent starts at the beginning of time (0)\n const firstAgentName = record.firstAgent ?? record.agentName;\n timeline.push({ agentName: firstAgentName, switchedAt: 0 });\n\n if (record.agentSwitchHistory && record.agentSwitchHistory.length > 0) {\n // Each entry in agentSwitchHistory records a *completed* agent stint:\n // { agentName: \"claude\", switchedAt: \"...\", ... } means claude was active\n // and was switched away at switchedAt. The next agent in sequence starts at that time.\n //\n // To reconstruct: after the last switchHistory entry, the current record.agentName is active.\n // But we need to map turns to agents, so we build boundaries.\n\n for (let i = 0; i < record.agentSwitchHistory.length; i++) {\n const entry = record.agentSwitchHistory[i];\n const switchTime = new Date(entry.switchedAt).getTime();\n\n // Determine which agent comes after this switch\n const nextAgent = i < record.agentSwitchHistory.length - 1\n ? record.agentSwitchHistory[i + 1].agentName\n : record.agentName; // current agent is the last one\n\n timeline.push({ agentName: nextAgent, switchedAt: switchTime });\n }\n }\n\n return timeline;\n }\n\n /**\n * Determine which agent produced a turn based on its timestamp and the agent timeline.\n */\n private resolveAgentForTurn(\n turnTimestamp: string,\n timeline: Array<{ agentName: string; switchedAt: number }>\n ): string {\n const turnTime = new Date(turnTimestamp).getTime();\n\n // Walk backward through the timeline to find the last boundary before this turn\n for (let i = timeline.length - 1; i >= 0; i--) {\n if (turnTime >= timeline[i].switchedAt) {\n return timeline[i].agentName;\n }\n }\n\n // Fallback to first agent\n return timeline[0].agentName;\n }\n\n /**\n * Build history markdown with agent labels inserted at agent boundaries.\n */\n private buildLabeledHistoryMarkdown(\n turns: import(\"./types.js\").Turn[],\n mode: ContextMode,\n agentTimeline: Array<{ agentName: string; switchedAt: number }>\n ): string {\n // If there's only one agent (no switches), just add a single label\n if (agentTimeline.length <= 1) {\n const label = `### [${agentTimeline[0]?.agentName ?? \"unknown\"}]\\n`;\n const md = buildHistoryMarkdown(turns, mode);\n return md ? label + \"\\n\" + md : label;\n }\n\n // Group turns by agent segments, then render each segment with a label\n const segments: Array<{ agentName: string; turns: import(\"./types.js\").Turn[] }> = [];\n let currentAgent = \"\";\n\n for (const turn of turns) {\n const agent = this.resolveAgentForTurn(turn.timestamp, agentTimeline);\n if (agent !== currentAgent) {\n segments.push({ agentName: agent, turns: [] });\n currentAgent = agent;\n }\n segments[segments.length - 1].turns.push(turn);\n }\n\n const parts: string[] = [];\n for (const segment of segments) {\n parts.push(`### [${segment.agentName}]`);\n parts.push(\"\");\n const md = buildHistoryMarkdown(segment.turns, mode);\n if (md) {\n parts.push(md);\n }\n }\n\n return parts.join(\"\\n\");\n }\n\n private computeTimeRange(\n records: SessionRecord[]\n ): { start: string; end: string } {\n if (records.length === 0) return { start: \"\", end: \"\" };\n\n const dates = records.map((r) => r.createdAt).filter(Boolean);\n const ends = records.map((r) => r.lastActiveAt).filter(Boolean);\n\n const start = dates.sort()[0] ?? \"\";\n const end = ends.sort().reverse()[0] ?? \"\";\n\n return { start, end };\n }\n}\n","import type { AgentEvent, Attachment } from \"../../../core/types.js\";\nimport type { HistoryStore } from \"./history-store.js\";\nimport type {\n HistoryAttachment,\n ResourceLinkStep,\n ResourceStep,\n SessionHistory,\n Step,\n ToolCallStep,\n Turn,\n} from \"./types.js\";\n\nexport interface RecorderState {\n history: SessionHistory;\n currentAssistantTurn: Turn | null;\n}\n\nfunction toHistoryAttachment(att: Attachment): HistoryAttachment {\n return {\n type: att.type,\n fileName: att.fileName,\n mimeType: att.mimeType,\n size: att.size,\n };\n}\n\nfunction extractDiff(\n content: unknown,\n): { path: string; oldText?: string; newText: string } | null {\n if (!Array.isArray(content)) return null;\n for (const item of content) {\n if (\n item &&\n typeof item === \"object\" &&\n (item as Record<string, unknown>).type === \"diff\"\n ) {\n const d = item as Record<string, unknown>;\n if (typeof d.path === \"string\" && typeof d.newText === \"string\") {\n const result: { path: string; oldText?: string; newText: string } = {\n path: d.path,\n newText: d.newText,\n };\n if (typeof d.oldText === \"string\") result.oldText = d.oldText;\n return result;\n }\n }\n }\n return null;\n}\n\nfunction extractLocations(\n locations: unknown,\n): { path: string; line?: number }[] | undefined {\n if (!Array.isArray(locations)) return undefined;\n const result: { path: string; line?: number }[] = [];\n for (const loc of locations) {\n if (loc && typeof loc === \"object\" && typeof (loc as any).path === \"string\") {\n const entry: { path: string; line?: number } = { path: (loc as any).path };\n if (typeof (loc as any).line === \"number\") entry.line = (loc as any).line;\n result.push(entry);\n }\n }\n return result.length > 0 ? result : undefined;\n}\n\nconst IGNORED_TYPES = new Set([\n \"session_end\",\n \"error\",\n \"system_message\",\n \"commands_update\",\n \"session_info_update\",\n \"model_update\",\n \"user_message_chunk\",\n \"tts_strip\",\n]);\n\nexport class HistoryRecorder {\n private states = new Map<string, RecorderState>();\n\n constructor(private readonly store: HistoryStore) {}\n\n onBeforePrompt(\n sessionId: string,\n text: string,\n attachments: Attachment[] | undefined,\n ): void {\n let state = this.states.get(sessionId);\n if (!state) {\n state = {\n history: { version: 1, sessionId, turns: [] },\n currentAssistantTurn: null,\n };\n this.states.set(sessionId, state);\n }\n\n const userTurn: Turn = {\n index: state.history.turns.length,\n role: \"user\",\n timestamp: new Date().toISOString(),\n content: text,\n };\n if (attachments && attachments.length > 0) {\n userTurn.attachments = attachments.map(toHistoryAttachment);\n }\n state.history.turns.push(userTurn);\n\n const assistantTurn: Turn = {\n index: state.history.turns.length,\n role: \"assistant\",\n timestamp: new Date().toISOString(),\n steps: [],\n };\n state.history.turns.push(assistantTurn);\n state.currentAssistantTurn = assistantTurn;\n }\n\n onAfterEvent(sessionId: string, event: AgentEvent): void {\n const state = this.states.get(sessionId);\n if (!state || !state.currentAssistantTurn) return;\n\n const turn = state.currentAssistantTurn;\n const steps = turn.steps!;\n\n if (IGNORED_TYPES.has(event.type)) return;\n\n switch (event.type) {\n case \"text\": {\n const last = steps[steps.length - 1];\n if (last && last.type === \"text\") {\n last.content += event.content;\n } else {\n steps.push({ type: \"text\", content: event.content });\n }\n break;\n }\n\n case \"thought\": {\n const last = steps[steps.length - 1];\n if (last && last.type === \"thinking\") {\n last.content += event.content;\n } else {\n steps.push({ type: \"thinking\", content: event.content });\n }\n break;\n }\n\n case \"tool_call\": {\n const step: ToolCallStep = {\n type: \"tool_call\",\n id: event.id,\n name: event.name,\n status: event.status,\n };\n if (event.kind) step.kind = event.kind;\n steps.push(step);\n break;\n }\n\n case \"tool_update\": {\n const existing = this.findToolCall(steps, event.id);\n if (!existing) break;\n existing.status = event.status;\n if (event.rawInput !== undefined) existing.input = event.rawInput;\n if (event.rawOutput !== undefined) existing.output = event.rawOutput;\n if (event.content !== undefined) {\n const diff = extractDiff(event.content);\n if (diff) existing.diff = diff;\n }\n if (event.locations !== undefined) {\n const locs = extractLocations(event.locations);\n if (locs) existing.locations = locs;\n }\n break;\n }\n\n case \"plan\": {\n steps.push({\n type: \"plan\",\n entries: event.entries.map((e) => ({\n content: e.content,\n priority: e.priority,\n status: e.status,\n })),\n });\n break;\n }\n\n case \"usage\": {\n turn.usage = {};\n if (event.tokensUsed !== undefined) turn.usage.tokensUsed = event.tokensUsed;\n if (event.contextSize !== undefined) turn.usage.contextSize = event.contextSize;\n if (event.cost) turn.usage.cost = event.cost;\n break;\n }\n\n case \"image_content\": {\n steps.push({\n type: \"image\",\n mimeType: event.mimeType,\n filePath: \"\",\n });\n break;\n }\n\n case \"audio_content\": {\n steps.push({\n type: \"audio\",\n mimeType: event.mimeType,\n filePath: \"\",\n });\n break;\n }\n\n case \"resource_content\": {\n const step: ResourceStep = {\n type: \"resource\",\n uri: event.uri,\n name: event.name,\n };\n if (event.text !== undefined) step.text = event.text;\n steps.push(step);\n break;\n }\n\n case \"resource_link\": {\n const step: ResourceLinkStep = {\n type: \"resource_link\",\n uri: event.uri,\n name: event.name,\n };\n if (event.title !== undefined) step.title = event.title;\n if (event.description !== undefined)\n step.description = event.description;\n steps.push(step);\n break;\n }\n\n case \"current_mode_update\": {\n steps.push({ type: \"mode_change\", modeId: event.modeId });\n break;\n }\n\n case \"config_option_update\": {\n for (const opt of event.options) {\n steps.push({\n type: \"config_change\",\n configId: opt.id,\n value: String(opt.currentValue),\n });\n }\n break;\n }\n }\n }\n\n onPermissionResolved(\n sessionId: string,\n requestId: string,\n decision: string,\n ): void {\n const state = this.states.get(sessionId);\n if (!state || !state.currentAssistantTurn) return;\n const step = this.findToolCall(state.currentAssistantTurn.steps!, requestId);\n if (!step) return;\n step.permission = { requested: true, outcome: decision };\n }\n\n async onTurnEnd(sessionId: string, stopReason: string): Promise<void> {\n const state = this.states.get(sessionId);\n if (!state || !state.currentAssistantTurn) return;\n state.currentAssistantTurn.stopReason = stopReason;\n state.currentAssistantTurn = null;\n await this.store.write(state.history);\n }\n\n finalize(sessionId: string): void {\n this.states.delete(sessionId);\n }\n\n getState(sessionId: string): RecorderState | undefined {\n return this.states.get(sessionId);\n }\n\n private findToolCall(steps: Step[], id: string): ToolCallStep | undefined {\n for (let i = steps.length - 1; i >= 0; i--) {\n const s = steps[i];\n if (s.type === \"tool_call\" && s.id === id) return s;\n }\n return undefined;\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { SessionHistory } from \"./types.js\";\n\nexport class HistoryStore {\n constructor(private readonly dir: string) {}\n\n async write(history: SessionHistory): Promise<void> {\n await fs.promises.mkdir(this.dir, { recursive: true });\n const filePath = this.filePath(history.sessionId);\n await fs.promises.writeFile(filePath, JSON.stringify(history, null, 2));\n }\n\n async read(sessionId: string): Promise<SessionHistory | null> {\n const filePath = this.filePath(sessionId);\n try {\n const raw = await fs.promises.readFile(filePath, \"utf-8\");\n return JSON.parse(raw) as SessionHistory;\n } catch {\n return null;\n }\n }\n\n async exists(sessionId: string): Promise<boolean> {\n try {\n await fs.promises.access(this.filePath(sessionId));\n return true;\n } catch {\n return false;\n }\n }\n\n async list(): Promise<string[]> {\n try {\n const files = await fs.promises.readdir(this.dir);\n return files\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => f.replace(/\\.json$/, \"\"));\n } catch {\n return [];\n }\n }\n\n async delete(sessionId: string): Promise<void> {\n try {\n await fs.promises.unlink(this.filePath(sessionId));\n } catch {\n // file may not exist — safe to ignore\n }\n }\n\n private filePath(sessionId: string): string {\n return path.join(this.dir, `${sessionId}.json`);\n }\n}\n"],"mappings":"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","names":["path"]}
@@ -3,12 +3,14 @@ import {
3
3
  ConfigSchema,
4
4
  applyMigrations,
5
5
  expandHome
6
- } from "./chunk-W4LK6WJP.js";
6
+ } from "./chunk-YZCKSNRN.js";
7
+ import {
8
+ getGlobalRoot
9
+ } from "./chunk-ON7HB5O7.js";
7
10
 
8
11
  // src/core/doctor/index.ts
9
12
  import * as fs8 from "fs";
10
13
  import * as path4 from "path";
11
- import * as os2 from "os";
12
14
 
13
15
  // src/core/doctor/checks/config.ts
14
16
  import * as fs from "fs";
@@ -493,9 +495,6 @@ import * as fs7 from "fs";
493
495
  import * as path3 from "path";
494
496
  import * as os from "os";
495
497
  import { execFileSync as execFileSync2 } from "child_process";
496
- var BIN_DIR = path3.join(os.homedir(), ".openacp", "bin");
497
- var BIN_NAME = os.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
498
- var BIN_PATH = path3.join(BIN_DIR, BIN_NAME);
499
498
  var tunnelCheck = {
500
499
  name: "Tunnel",
501
500
  order: 8,
@@ -512,8 +511,10 @@ var tunnelCheck = {
512
511
  const provider = ctx.config.tunnel.provider;
513
512
  results.push({ status: "pass", message: `Tunnel provider: ${provider}` });
514
513
  if (provider === "cloudflare") {
514
+ const binName = os.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
515
+ const binPath = path3.join(ctx.dataDir, "bin", binName);
515
516
  let found = false;
516
- if (fs7.existsSync(BIN_PATH)) {
517
+ if (fs7.existsSync(binPath)) {
517
518
  found = true;
518
519
  } else {
519
520
  try {
@@ -532,7 +533,7 @@ var tunnelCheck = {
532
533
  fixRisk: "safe",
533
534
  fix: async () => {
534
535
  try {
535
- const { ensureCloudflared } = await import("./install-cloudflared-JRJ4BSOM.js");
536
+ const { ensureCloudflared } = await import("./install-cloudflared-LNS5L5FR.js");
536
537
  await ensureCloudflared();
537
538
  return { success: true, message: "installed cloudflared" };
538
539
  } catch (err) {
@@ -566,8 +567,10 @@ var ALL_CHECKS = [
566
567
  var CHECK_TIMEOUT_MS = 1e4;
567
568
  var DoctorEngine = class {
568
569
  dryRun;
570
+ dataDir;
569
571
  constructor(options) {
570
572
  this.dryRun = options?.dryRun ?? false;
573
+ this.dataDir = options?.dataDir ?? getGlobalRoot();
571
574
  }
572
575
  async runAll() {
573
576
  const ctx = await this.buildContext();
@@ -617,7 +620,7 @@ var DoctorEngine = class {
617
620
  return { categories, summary, pendingFixes };
618
621
  }
619
622
  async buildContext() {
620
- const dataDir = path4.join(os2.homedir(), ".openacp");
623
+ const dataDir = this.dataDir;
621
624
  const configPath = process.env.OPENACP_CONFIG_PATH || path4.join(dataDir, "config.json");
622
625
  let config = null;
623
626
  let rawConfig = null;
@@ -647,4 +650,4 @@ var DoctorEngine = class {
647
650
  export {
648
651
  DoctorEngine
649
652
  };
650
- //# sourceMappingURL=chunk-GEOXPGCO.js.map
653
+ //# sourceMappingURL=chunk-K6UY5M75.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/doctor/index.ts","../../src/core/doctor/checks/config.ts","../../src/core/doctor/checks/agents.ts","../../src/core/doctor/checks/telegram.ts","../../src/core/doctor/checks/storage.ts","../../src/core/doctor/checks/workspace.ts","../../src/core/doctor/checks/plugins.ts","../../src/core/doctor/checks/daemon.ts","../../src/core/doctor/checks/tunnel.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { DoctorCheck, DoctorContext, DoctorReport, CategoryResult, PendingFix, CheckResult } from \"./types.js\";\nimport { ConfigManager, expandHome } from \"../config/config.js\";\nimport { getGlobalRoot } from \"../instance-context.js\";\n\nimport { configCheck } from \"./checks/config.js\";\nimport { agentsCheck } from \"./checks/agents.js\";\nimport { telegramCheck } from \"./checks/telegram.js\";\nimport { storageCheck } from \"./checks/storage.js\";\nimport { workspaceCheck } from \"./checks/workspace.js\";\nimport { pluginsCheck } from \"./checks/plugins.js\";\nimport { daemonCheck } from \"./checks/daemon.js\";\nimport { tunnelCheck } from \"./checks/tunnel.js\";\n\nconst ALL_CHECKS: DoctorCheck[] = [\n configCheck,\n agentsCheck,\n telegramCheck,\n storageCheck,\n workspaceCheck,\n pluginsCheck,\n daemonCheck,\n tunnelCheck,\n];\n\nconst CHECK_TIMEOUT_MS = 10_000;\n\nexport class DoctorEngine {\n private dryRun: boolean;\n private dataDir: string;\n\n constructor(options?: { dryRun?: boolean; dataDir?: string }) {\n this.dryRun = options?.dryRun ?? false;\n this.dataDir = options?.dataDir ?? getGlobalRoot();\n }\n\n async runAll(): Promise<DoctorReport> {\n const ctx = await this.buildContext();\n const checks = [...ALL_CHECKS].sort((a, b) => a.order - b.order);\n\n const categories: CategoryResult[] = [];\n const pendingFixes: PendingFix[] = [];\n const summary = { passed: 0, warnings: 0, failed: 0, fixed: 0 };\n\n for (const check of checks) {\n let results: CheckResult[];\n try {\n results = await Promise.race([\n check.run(ctx),\n new Promise<CheckResult[]>((_, reject) =>\n setTimeout(() => reject(new Error(\"timeout\")), CHECK_TIMEOUT_MS),\n ),\n ]);\n } catch {\n results = [{ status: \"fail\", message: `${check.name} check timed out` }];\n }\n\n for (const result of results) {\n if (result.fixable && result.fix) {\n if (result.fixRisk === \"safe\" && !this.dryRun) {\n try {\n const fixResult = await result.fix();\n if (fixResult.success) {\n result.message += ` → Fixed (${fixResult.message})`;\n result.status = \"warn\";\n delete result.fix;\n summary.fixed++;\n }\n } catch {\n // Fix failed, leave as-is\n }\n } else if (result.fixRisk === \"risky\") {\n pendingFixes.push({\n category: check.name,\n message: result.message,\n fix: result.fix,\n });\n }\n }\n\n if (result.status === \"pass\") summary.passed++;\n else if (result.status === \"warn\") summary.warnings++;\n else if (result.status === \"fail\") summary.failed++;\n }\n\n categories.push({ name: check.name, results });\n }\n\n return { categories, summary, pendingFixes };\n }\n\n private async buildContext(): Promise<DoctorContext> {\n const dataDir = this.dataDir;\n const configPath = process.env.OPENACP_CONFIG_PATH || path.join(dataDir, \"config.json\");\n\n let config = null;\n let rawConfig: unknown = null;\n\n try {\n const content = fs.readFileSync(configPath, \"utf-8\");\n rawConfig = JSON.parse(content);\n const cm = new ConfigManager();\n await cm.load();\n config = cm.get();\n } catch {\n // Config may not exist or may be invalid — checks will handle this\n }\n\n const logsDir = config\n ? expandHome(config.logging.logDir)\n : path.join(dataDir, \"logs\");\n\n return {\n config,\n rawConfig,\n configPath,\n dataDir,\n sessionsPath: path.join(dataDir, \"sessions.json\"),\n pidPath: path.join(dataDir, \"openacp.pid\"),\n portFilePath: path.join(dataDir, \"api.port\"),\n pluginsDir: path.join(dataDir, \"plugins\"),\n logsDir,\n };\n }\n}\n\nexport type { DoctorReport, CategoryResult, PendingFix, CheckResult, DoctorContext } from \"./types.js\";\n","import * as fs from \"node:fs\";\nimport { ConfigSchema } from \"../../config/config.js\";\nimport { applyMigrations } from \"../../config/config-migrations.js\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const configCheck: DoctorCheck = {\n name: \"Config\",\n order: 1,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!fs.existsSync(ctx.configPath)) {\n results.push({ status: \"fail\", message: \"Config file not found\" });\n return results;\n }\n results.push({ status: \"pass\", message: \"Config file exists\" });\n\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(fs.readFileSync(ctx.configPath, \"utf-8\"));\n } catch (err) {\n results.push({\n status: \"fail\",\n message: `Config JSON invalid: ${err instanceof Error ? err.message : String(err)}`,\n });\n return results;\n }\n results.push({ status: \"pass\", message: \"JSON valid\" });\n\n const testRaw = structuredClone(raw);\n const { changed } = applyMigrations(testRaw);\n if (changed) {\n results.push({\n status: \"warn\",\n message: \"Pending config migrations\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n applyMigrations(raw);\n fs.writeFileSync(ctx.configPath, JSON.stringify(raw, null, 2));\n return { success: true, message: \"applied migrations\" };\n },\n });\n }\n\n const result = ConfigSchema.safeParse(raw);\n if (!result.success) {\n for (const issue of result.error.issues) {\n results.push({\n status: \"fail\",\n message: `Validation: ${issue.path.join(\".\")} — ${issue.message}`,\n });\n }\n } else {\n results.push({ status: \"pass\", message: \"Schema valid\" });\n }\n\n return results;\n },\n};\n","import { execFileSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nfunction commandExists(cmd: string): boolean {\n try {\n execFileSync(\"which\", [cmd], { stdio: \"pipe\" });\n return true;\n } catch {\n // not in PATH\n }\n let dir = process.cwd();\n while (true) {\n const binPath = path.join(dir, \"node_modules\", \".bin\", cmd);\n if (fs.existsSync(binPath)) return true;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return false;\n}\n\nexport const agentsCheck: DoctorCheck = {\n name: \"Agents\",\n order: 2,\n async run(ctx) {\n const results: CheckResult[] = [];\n if (!ctx.config) {\n results.push({ status: \"fail\", message: \"Cannot check agents — config not loaded\" });\n return results;\n }\n\n const agents = ctx.config.agents;\n const defaultAgent = ctx.config.defaultAgent;\n\n if (!agents[defaultAgent]) {\n results.push({\n status: \"fail\",\n message: `Default agent \"${defaultAgent}\" not found in agents config`,\n });\n }\n\n for (const [name, agent] of Object.entries(agents)) {\n const isDefault = name === defaultAgent;\n if (commandExists(agent.command)) {\n results.push({\n status: \"pass\",\n message: `${agent.command} found${isDefault ? \" (default)\" : \"\"}`,\n });\n } else {\n results.push({\n status: isDefault ? \"fail\" : \"warn\",\n message: `${agent.command} not found in PATH${isDefault ? \" (default agent!)\" : \"\"}`,\n });\n }\n }\n\n return results;\n },\n};\n","import type { DoctorCheck, CheckResult } from \"../types.js\";\n\nconst BOT_TOKEN_REGEX = /^\\d+:[A-Za-z0-9_-]{35,}$/;\n\nexport const telegramCheck: DoctorCheck = {\n name: \"Telegram\",\n order: 3,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!ctx.config) {\n results.push({ status: \"fail\", message: \"Cannot check Telegram — config not loaded\" });\n return results;\n }\n\n const tgConfig = ctx.config.channels.telegram as Record<string, unknown> | undefined;\n if (!tgConfig || !tgConfig.enabled) {\n results.push({ status: \"pass\", message: \"Telegram not enabled (skipped)\" });\n return results;\n }\n\n const botToken = tgConfig.botToken as string | undefined;\n const chatId = tgConfig.chatId as number | undefined;\n\n if (!botToken || !BOT_TOKEN_REGEX.test(botToken)) {\n results.push({ status: \"fail\", message: \"Bot token format invalid\" });\n return results;\n }\n results.push({ status: \"pass\", message: \"Bot token format valid\" });\n\n let botId: number | undefined;\n try {\n const res = await fetch(`https://api.telegram.org/bot${botToken}/getMe`);\n const data = (await res.json()) as { ok: boolean; result?: { id: number; username: string }; description?: string };\n if (data.ok && data.result) {\n botId = data.result.id;\n results.push({ status: \"pass\", message: `Bot token valid (@${data.result.username})` });\n } else {\n results.push({ status: \"fail\", message: `Bot token rejected: ${data.description || \"unknown error\"}` });\n return results;\n }\n } catch (err) {\n results.push({ status: \"fail\", message: `Cannot reach Telegram API: ${err instanceof Error ? err.message : String(err)}` });\n return results;\n }\n\n if (!chatId || chatId === 0) {\n results.push({ status: \"fail\", message: \"Chat ID not configured\" });\n return results;\n }\n\n try {\n const res = await fetch(`https://api.telegram.org/bot${botToken}/getChat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ chat_id: chatId }),\n });\n const data = (await res.json()) as {\n ok: boolean;\n result?: { type: string; is_forum?: boolean; title: string };\n description?: string;\n };\n if (!data.ok || !data.result) {\n results.push({ status: \"fail\", message: `Chat ID invalid: ${data.description || \"unknown error\"}` });\n return results;\n }\n if (data.result.type !== \"supergroup\") {\n results.push({ status: \"fail\", message: `Chat is \"${data.result.type}\", must be a supergroup` });\n return results;\n }\n if (!data.result.is_forum) {\n results.push({ status: \"warn\", message: \"Chat does not have topics enabled\" });\n } else {\n results.push({ status: \"pass\", message: `Chat is supergroup with topics (\"${data.result.title}\")` });\n }\n } catch (err) {\n results.push({ status: \"fail\", message: `Cannot validate chat: ${err instanceof Error ? err.message : String(err)}` });\n return results;\n }\n\n try {\n const res = await fetch(`https://api.telegram.org/bot${botToken}/getChatMember`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ chat_id: chatId, user_id: botId }),\n });\n const data = (await res.json()) as { ok: boolean; result?: { status: string }; description?: string };\n if (!data.ok || !data.result) {\n results.push({ status: \"fail\", message: `Cannot check bot membership: ${data.description || \"unknown\"}` });\n } else if (data.result.status === \"administrator\" || data.result.status === \"creator\") {\n results.push({ status: \"pass\", message: \"Bot is admin in group\" });\n } else {\n results.push({\n status: \"fail\",\n message: `Bot is \"${data.result.status}\" — must be admin. Promote bot in group settings.`,\n });\n }\n } catch (err) {\n results.push({ status: \"fail\", message: `Admin check failed: ${err instanceof Error ? err.message : String(err)}` });\n }\n\n return results;\n },\n};\n","import * as fs from \"node:fs\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const storageCheck: DoctorCheck = {\n name: \"Storage\",\n order: 4,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!fs.existsSync(ctx.dataDir)) {\n results.push({\n status: \"fail\",\n message: \"Data directory ~/.openacp does not exist\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.mkdirSync(ctx.dataDir, { recursive: true });\n return { success: true, message: \"created directory\" };\n },\n });\n } else {\n try {\n fs.accessSync(ctx.dataDir, fs.constants.W_OK);\n results.push({ status: \"pass\", message: \"Data directory exists and writable\" });\n } catch {\n results.push({ status: \"fail\", message: \"Data directory not writable\" });\n }\n }\n\n if (fs.existsSync(ctx.sessionsPath)) {\n try {\n const content = fs.readFileSync(ctx.sessionsPath, \"utf-8\");\n const data = JSON.parse(content);\n if (typeof data === \"object\" && data !== null && \"sessions\" in data) {\n results.push({ status: \"pass\", message: \"Sessions file valid\" });\n } else {\n results.push({\n status: \"fail\",\n message: \"Sessions file has invalid structure\",\n fixable: true,\n fixRisk: \"risky\",\n fix: async () => {\n fs.writeFileSync(ctx.sessionsPath, JSON.stringify({ version: 1, sessions: {} }, null, 2));\n return { success: true, message: \"reset sessions file\" };\n },\n });\n }\n } catch {\n results.push({\n status: \"fail\",\n message: \"Sessions file corrupt (invalid JSON)\",\n fixable: true,\n fixRisk: \"risky\",\n fix: async () => {\n fs.writeFileSync(ctx.sessionsPath, JSON.stringify({ version: 1, sessions: {} }, null, 2));\n return { success: true, message: \"reset sessions file\" };\n },\n });\n }\n } else {\n results.push({ status: \"pass\", message: \"Sessions file not present yet (created on first session)\" });\n }\n\n if (!fs.existsSync(ctx.logsDir)) {\n results.push({\n status: \"warn\",\n message: \"Log directory does not exist\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.mkdirSync(ctx.logsDir, { recursive: true });\n return { success: true, message: \"created log directory\" };\n },\n });\n } else {\n try {\n fs.accessSync(ctx.logsDir, fs.constants.W_OK);\n results.push({ status: \"pass\", message: \"Log directory exists and writable\" });\n } catch {\n results.push({ status: \"fail\", message: \"Log directory not writable\" });\n }\n }\n\n return results;\n },\n};\n","import * as fs from \"node:fs\";\nimport { expandHome } from \"../../config/config.js\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const workspaceCheck: DoctorCheck = {\n name: \"Workspace\",\n order: 5,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!ctx.config) {\n results.push({ status: \"fail\", message: \"Cannot check workspace — config not loaded\" });\n return results;\n }\n\n const baseDir = expandHome(ctx.config.workspace.baseDir);\n\n if (!fs.existsSync(baseDir)) {\n results.push({\n status: \"warn\",\n message: `Workspace directory does not exist: ${baseDir}`,\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.mkdirSync(baseDir, { recursive: true });\n return { success: true, message: \"created directory\" };\n },\n });\n } else {\n try {\n fs.accessSync(baseDir, fs.constants.W_OK);\n results.push({ status: \"pass\", message: `Workspace directory exists: ${baseDir}` });\n } catch {\n results.push({ status: \"fail\", message: `Workspace directory not writable: ${baseDir}` });\n }\n }\n\n return results;\n },\n};\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const pluginsCheck: DoctorCheck = {\n name: \"Plugins\",\n order: 6,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!fs.existsSync(ctx.pluginsDir)) {\n results.push({\n status: \"warn\",\n message: \"Plugins directory does not exist\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.mkdirSync(ctx.pluginsDir, { recursive: true });\n fs.writeFileSync(\n path.join(ctx.pluginsDir, \"package.json\"),\n JSON.stringify({ name: \"openacp-plugins\", private: true, dependencies: {} }, null, 2),\n );\n return { success: true, message: \"initialized plugins directory\" };\n },\n });\n return results;\n }\n results.push({ status: \"pass\", message: \"Plugins directory exists\" });\n\n const pkgPath = path.join(ctx.pluginsDir, \"package.json\");\n if (!fs.existsSync(pkgPath)) {\n results.push({\n status: \"warn\",\n message: \"Plugins package.json missing\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.writeFileSync(\n pkgPath,\n JSON.stringify({ name: \"openacp-plugins\", private: true, dependencies: {} }, null, 2),\n );\n return { success: true, message: \"created package.json\" };\n },\n });\n return results;\n }\n\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\"));\n const deps = pkg.dependencies || {};\n const count = Object.keys(deps).length;\n results.push({ status: \"pass\", message: `Plugins package.json valid (${count} plugins)` });\n } catch {\n results.push({\n status: \"fail\",\n message: \"Plugins package.json is invalid JSON\",\n fixable: true,\n fixRisk: \"risky\",\n fix: async () => {\n fs.writeFileSync(\n pkgPath,\n JSON.stringify({ name: \"openacp-plugins\", private: true, dependencies: {} }, null, 2),\n );\n return { success: true, message: \"reset package.json\" };\n },\n });\n }\n\n return results;\n },\n};\n","import * as fs from \"node:fs\";\nimport * as net from \"node:net\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction checkPortInUse(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer();\n server.once(\"error\", () => resolve(true));\n server.once(\"listening\", () => {\n server.close();\n resolve(false);\n });\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nexport const daemonCheck: DoctorCheck = {\n name: \"Daemon\",\n order: 7,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (fs.existsSync(ctx.pidPath)) {\n const content = fs.readFileSync(ctx.pidPath, \"utf-8\").trim();\n const pid = parseInt(content, 10);\n if (isNaN(pid)) {\n results.push({\n status: \"warn\",\n message: \"PID file contains invalid data\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.unlinkSync(ctx.pidPath);\n return { success: true, message: \"removed invalid PID file\" };\n },\n });\n } else if (!isProcessAlive(pid)) {\n results.push({\n status: \"warn\",\n message: `Stale PID file (PID ${pid} not running)`,\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.unlinkSync(ctx.pidPath);\n return { success: true, message: \"removed stale PID file\" };\n },\n });\n } else {\n results.push({ status: \"pass\", message: `Daemon running (PID ${pid})` });\n }\n }\n\n if (fs.existsSync(ctx.portFilePath)) {\n const content = fs.readFileSync(ctx.portFilePath, \"utf-8\").trim();\n const port = parseInt(content, 10);\n if (isNaN(port)) {\n results.push({\n status: \"warn\",\n message: \"Port file contains invalid data\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n fs.unlinkSync(ctx.portFilePath);\n return { success: true, message: \"removed invalid port file\" };\n },\n });\n } else {\n results.push({ status: \"pass\", message: `Port file valid (port ${port})` });\n }\n }\n\n if (ctx.config) {\n const apiPort = ctx.config.api.port;\n const inUse = await checkPortInUse(apiPort);\n if (inUse) {\n if (fs.existsSync(ctx.pidPath)) {\n const pid = parseInt(fs.readFileSync(ctx.pidPath, \"utf-8\").trim(), 10);\n if (!isNaN(pid) && isProcessAlive(pid)) {\n results.push({ status: \"pass\", message: `API port ${apiPort} in use by OpenACP daemon` });\n } else {\n results.push({ status: \"warn\", message: `API port ${apiPort} in use by another process` });\n }\n } else {\n results.push({ status: \"warn\", message: `API port ${apiPort} in use by another process` });\n }\n } else {\n results.push({ status: \"pass\", message: `API port ${apiPort} available` });\n }\n }\n\n return results;\n },\n};\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { execFileSync } from \"node:child_process\";\nimport type { DoctorCheck, CheckResult } from \"../types.js\";\n\nexport const tunnelCheck: DoctorCheck = {\n name: \"Tunnel\",\n order: 8,\n async run(ctx) {\n const results: CheckResult[] = [];\n\n if (!ctx.config) {\n results.push({ status: \"fail\", message: \"Cannot check tunnel — config not loaded\" });\n return results;\n }\n\n if (!ctx.config.tunnel.enabled) {\n results.push({ status: \"pass\", message: \"Tunnel not enabled (skipped)\" });\n return results;\n }\n\n const provider = ctx.config.tunnel.provider;\n results.push({ status: \"pass\", message: `Tunnel provider: ${provider}` });\n\n if (provider === \"cloudflare\") {\n const binName = os.platform() === \"win32\" ? \"cloudflared.exe\" : \"cloudflared\";\n const binPath = path.join(ctx.dataDir, \"bin\", binName);\n let found = false;\n if (fs.existsSync(binPath)) {\n found = true;\n } else {\n try {\n execFileSync(\"which\", [\"cloudflared\"], { stdio: \"pipe\" });\n found = true;\n } catch {\n // not found\n }\n }\n\n if (found) {\n results.push({ status: \"pass\", message: \"cloudflared binary found\" });\n } else {\n results.push({\n status: \"warn\",\n message: \"cloudflared binary not found\",\n fixable: true,\n fixRisk: \"safe\",\n fix: async () => {\n try {\n const { ensureCloudflared } = await import(\"../../../plugins/tunnel/providers/install-cloudflared.js\");\n await ensureCloudflared();\n return { success: true, message: \"installed cloudflared\" };\n } catch (err) {\n return { success: false, message: err instanceof Error ? err.message : String(err) };\n }\n },\n });\n }\n }\n\n const tunnelPort = ctx.config.tunnel.port;\n if (tunnelPort < 1 || tunnelPort > 65535) {\n results.push({ status: \"fail\", message: `Invalid tunnel port: ${tunnelPort}` });\n } else {\n results.push({ status: \"pass\", message: `Tunnel port: ${tunnelPort}` });\n }\n\n return results;\n },\n};\n"],"mappings":"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","names":["fs","path","fs","fs","fs","fs","path","fs","fs","path","execFileSync"]}
@@ -1,4 +1,5 @@
1
1
  // src/plugins/api-server/index.ts
2
+ import path from "path";
2
3
  function createApiServerPlugin() {
3
4
  let server = null;
4
5
  return {
@@ -64,14 +65,22 @@ function createApiServerPlugin() {
64
65
  ctx.terminal.log.success("API server settings cleared");
65
66
  }
66
67
  },
68
+ inheritableKeys: ["host"],
67
69
  async setup(ctx) {
68
70
  const config = ctx.pluginConfig;
69
- const { ApiServer } = await import("./api-server-JLBDKCU4.js");
71
+ const { ApiServer } = await import("./api-server-DATG2KBR.js");
70
72
  const apiConfig = {
71
73
  port: config.port ?? 0,
72
74
  host: config.host ?? "127.0.0.1"
73
75
  };
74
- server = new ApiServer(ctx.core, apiConfig);
76
+ const instanceRoot = ctx.instanceRoot;
77
+ server = new ApiServer(
78
+ ctx.core,
79
+ apiConfig,
80
+ path.join(instanceRoot, "api.port"),
81
+ void 0,
82
+ path.join(instanceRoot, "api-secret")
83
+ );
75
84
  ctx.registerService("api-server", server);
76
85
  ctx.on("system:ready", async () => {
77
86
  try {
@@ -94,4 +103,4 @@ var api_server_default = createApiServerPlugin();
94
103
  export {
95
104
  api_server_default
96
105
  };
97
- //# sourceMappingURL=chunk-KDU3ZEWT.js.map
106
+ //# sourceMappingURL=chunk-KGAQW6F4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugins/api-server/index.ts"],"sourcesContent":["import path from 'node:path'\nimport type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js'\nimport type { OpenACPCore } from '../../core/core.js'\nimport type { ApiConfig } from './api-server.js'\n\nfunction createApiServerPlugin(): OpenACPPlugin {\n let server: { start(): Promise<void>; stop?(): Promise<void> } | null = null\n\n return {\n name: '@openacp/api-server',\n version: '1.0.0',\n description: 'REST API + SSE streaming server',\n essential: false,\n permissions: ['services:register', 'kernel:access', 'events:read'],\n\n async install(ctx: InstallContext) {\n const { settings, legacyConfig, terminal } = ctx\n\n // Migrate from legacy config if present\n if (legacyConfig) {\n const apiCfg = legacyConfig.api as Record<string, unknown> | undefined\n if (apiCfg) {\n await settings.setAll({\n port: apiCfg.port ?? 21420,\n host: apiCfg.host ?? '127.0.0.1',\n })\n terminal.log.success('API server settings migrated from legacy config')\n return\n }\n }\n\n // Save defaults\n await settings.setAll({\n port: 21420,\n host: '127.0.0.1',\n })\n terminal.log.success('API server defaults saved')\n },\n\n async configure(ctx: InstallContext) {\n const { terminal, settings } = ctx\n const current = await settings.getAll()\n\n const choice = await terminal.select({\n message: 'What to configure?',\n options: [\n { value: 'port', label: `Change port (current: ${current.port ?? 21420})` },\n { value: 'host', label: `Change host (current: ${current.host ?? '127.0.0.1'})` },\n { value: 'done', label: 'Done' },\n ],\n })\n\n if (choice === 'port') {\n const val = await terminal.text({\n message: 'API port:',\n defaultValue: String(current.port ?? 21420),\n validate: (v) => {\n const n = Number(v.trim())\n if (isNaN(n) || n < 1 || n > 65535) return 'Port must be 1-65535'\n return undefined\n },\n })\n await settings.set('port', Number(val.trim()))\n terminal.log.success('Port updated')\n } else if (choice === 'host') {\n const val = await terminal.text({\n message: 'API host:',\n defaultValue: (current.host as string) ?? '127.0.0.1',\n })\n await settings.set('host', val.trim())\n terminal.log.success('Host updated')\n }\n },\n\n async uninstall(ctx: InstallContext, opts: { purge: boolean }) {\n if (opts.purge) {\n await ctx.settings.clear()\n ctx.terminal.log.success('API server settings cleared')\n }\n },\n\n inheritableKeys: ['host'],\n\n async setup(ctx) {\n const config = ctx.pluginConfig as Record<string, unknown>\n\n // Lazy import to avoid loading unless needed\n const { ApiServer } = await import('./api-server.js')\n\n const apiConfig: ApiConfig = {\n port: (config.port as number) ?? 0,\n host: (config.host as string) ?? '127.0.0.1',\n }\n\n const instanceRoot = ctx.instanceRoot\n server = new ApiServer(\n ctx.core as OpenACPCore,\n apiConfig,\n path.join(instanceRoot, 'api.port'),\n undefined,\n path.join(instanceRoot, 'api-secret'),\n )\n\n ctx.registerService('api-server', server)\n\n // Start on system:ready\n ctx.on('system:ready', async () => {\n try {\n await server!.start()\n ctx.log.info('API server started')\n } catch (err) {\n ctx.log.error(`API server failed to start: ${err}`)\n }\n })\n },\n\n async teardown() {\n if (server) {\n await server.stop?.()\n }\n },\n }\n}\n\nexport default createApiServerPlugin()\n"],"mappings":"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","names":[]}