@roackb2/heddle 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +9 -1
  2. package/dist/src/cli/ask.d.ts +11 -1
  3. package/dist/src/cli/ask.d.ts.map +1 -1
  4. package/dist/src/cli/ask.js +168 -204
  5. package/dist/src/cli/ask.js.map +1 -1
  6. package/dist/src/cli/chat/App.js +7 -4
  7. package/dist/src/cli/chat/App.js.map +1 -1
  8. package/dist/src/cli/chat/hooks/controllers/run/tui-agent-turn-result.d.ts +5 -18
  9. package/dist/src/cli/chat/hooks/controllers/run/tui-agent-turn-result.d.ts.map +1 -1
  10. package/dist/src/cli/chat/hooks/controllers/run/tui-agent-turn-result.js +10 -122
  11. package/dist/src/cli/chat/hooks/controllers/run/tui-agent-turn-result.js.map +1 -1
  12. package/dist/src/cli/chat/hooks/controllers/run/tui-compaction-status.d.ts +6 -6
  13. package/dist/src/cli/chat/hooks/controllers/run/tui-compaction-status.d.ts.map +1 -1
  14. package/dist/src/cli/chat/hooks/controllers/run/tui-compaction-status.js +12 -24
  15. package/dist/src/cli/chat/hooks/controllers/run/tui-compaction-status.js.map +1 -1
  16. package/dist/src/cli/chat/hooks/controllers/run/tui-direct-shell-result.d.ts +3 -3
  17. package/dist/src/cli/chat/hooks/controllers/run/tui-direct-shell-result.d.ts.map +1 -1
  18. package/dist/src/cli/chat/hooks/controllers/run/tui-direct-shell-result.js +10 -10
  19. package/dist/src/cli/chat/hooks/controllers/run/tui-direct-shell-result.js.map +1 -1
  20. package/dist/src/cli/chat/hooks/controllers/run/tui-direct-shell.d.ts +4 -4
  21. package/dist/src/cli/chat/hooks/controllers/run/tui-direct-shell.d.ts.map +1 -1
  22. package/dist/src/cli/chat/hooks/controllers/run/tui-direct-shell.js +34 -30
  23. package/dist/src/cli/chat/hooks/controllers/run/tui-direct-shell.js.map +1 -1
  24. package/dist/src/cli/chat/hooks/controllers/run/tui-ordinary-turn.d.ts +3 -0
  25. package/dist/src/cli/chat/hooks/controllers/run/tui-ordinary-turn.d.ts.map +1 -1
  26. package/dist/src/cli/chat/hooks/controllers/run/tui-ordinary-turn.js +13 -12
  27. package/dist/src/cli/chat/hooks/controllers/run/tui-ordinary-turn.js.map +1 -1
  28. package/dist/src/cli/chat/hooks/controllers/run/tui-run-loop-events.d.ts +3 -3
  29. package/dist/src/cli/chat/hooks/controllers/run/tui-run-loop-events.d.ts.map +1 -1
  30. package/dist/src/cli/chat/hooks/controllers/run/tui-run-loop-events.js +23 -23
  31. package/dist/src/cli/chat/hooks/controllers/run/tui-run-loop-events.js.map +1 -1
  32. package/dist/src/cli/chat/hooks/controllers/useAgentRunController.d.ts +5 -2
  33. package/dist/src/cli/chat/hooks/controllers/useAgentRunController.d.ts.map +1 -1
  34. package/dist/src/cli/chat/hooks/controllers/useAgentRunController.js +21 -10
  35. package/dist/src/cli/chat/hooks/controllers/useAgentRunController.js.map +1 -1
  36. package/dist/src/cli/chat/hooks/controllers/useChatAppController.d.ts +2 -0
  37. package/dist/src/cli/chat/hooks/controllers/useChatAppController.d.ts.map +1 -1
  38. package/dist/src/cli/chat/hooks/controllers/useChatAppController.js +13 -23
  39. package/dist/src/cli/chat/hooks/controllers/useChatAppController.js.map +1 -1
  40. package/dist/src/cli/chat/hooks/controllers/usePromptSubmissionController.d.ts +4 -3
  41. package/dist/src/cli/chat/hooks/controllers/usePromptSubmissionController.d.ts.map +1 -1
  42. package/dist/src/cli/chat/hooks/controllers/usePromptSubmissionController.js +10 -9
  43. package/dist/src/cli/chat/hooks/controllers/usePromptSubmissionController.js.map +1 -1
  44. package/dist/src/cli/chat/hooks/useChatDrift.d.ts +4 -4
  45. package/dist/src/cli/chat/hooks/useChatDrift.d.ts.map +1 -1
  46. package/dist/src/cli/chat/hooks/useChatDrift.js +7 -6
  47. package/dist/src/cli/chat/hooks/useChatDrift.js.map +1 -1
  48. package/dist/src/cli/chat/hooks/useChatSessions.d.ts +3 -0
  49. package/dist/src/cli/chat/hooks/useChatSessions.d.ts.map +1 -1
  50. package/dist/src/cli/chat/hooks/useChatSessions.js +30 -23
  51. package/dist/src/cli/chat/hooks/useChatSessions.js.map +1 -1
  52. package/dist/src/cli/chat/submit.d.ts +3 -4
  53. package/dist/src/cli/chat/submit.d.ts.map +1 -1
  54. package/dist/src/cli/chat/submit.js +17 -55
  55. package/dist/src/cli/chat/submit.js.map +1 -1
  56. package/dist/src/cli/chat/utils/runtime.d.ts.map +1 -1
  57. package/dist/src/cli/chat/utils/runtime.js.map +1 -1
  58. package/dist/src/cli/daemon.d.ts +2 -2
  59. package/dist/src/cli/daemon.d.ts.map +1 -1
  60. package/dist/src/cli/daemon.js +2 -2
  61. package/dist/src/cli/daemon.js.map +1 -1
  62. package/dist/src/cli/main.js +3 -3
  63. package/dist/src/cli/main.js.map +1 -1
  64. package/dist/src/cli/remote/control-plane-client.d.ts +8 -4
  65. package/dist/src/cli/remote/control-plane-client.d.ts.map +1 -1
  66. package/dist/src/core/chat/engine/conversation-engine.d.ts.map +1 -1
  67. package/dist/src/core/chat/engine/conversation-engine.js +4 -8
  68. package/dist/src/core/chat/engine/conversation-engine.js.map +1 -1
  69. package/dist/src/core/chat/engine/index.d.ts +1 -1
  70. package/dist/src/core/chat/engine/index.d.ts.map +1 -1
  71. package/dist/src/core/chat/engine/sessions/repository/file-chat-session-repository.d.ts +2 -1
  72. package/dist/src/core/chat/engine/sessions/repository/file-chat-session-repository.d.ts.map +1 -1
  73. package/dist/src/core/chat/engine/sessions/repository/file-chat-session-repository.js +8 -0
  74. package/dist/src/core/chat/engine/sessions/repository/file-chat-session-repository.js.map +1 -1
  75. package/dist/src/core/chat/engine/sessions/service.d.ts +39 -5
  76. package/dist/src/core/chat/engine/sessions/service.d.ts.map +1 -1
  77. package/dist/src/core/chat/engine/sessions/service.js +246 -106
  78. package/dist/src/core/chat/engine/sessions/service.js.map +1 -1
  79. package/dist/src/core/chat/engine/sessions/session-record.d.ts +2 -1
  80. package/dist/src/core/chat/engine/sessions/session-record.d.ts.map +1 -1
  81. package/dist/src/core/chat/engine/sessions/session-record.js +1 -0
  82. package/dist/src/core/chat/engine/sessions/session-record.js.map +1 -1
  83. package/dist/src/core/chat/engine/turns/context.d.ts +2 -0
  84. package/dist/src/core/chat/engine/turns/context.d.ts.map +1 -1
  85. package/dist/src/core/chat/engine/turns/context.js +2 -2
  86. package/dist/src/core/chat/engine/turns/context.js.map +1 -1
  87. package/dist/src/core/chat/engine/turns/run-conversation-turn.d.ts +10 -0
  88. package/dist/src/core/chat/engine/turns/run-conversation-turn.d.ts.map +1 -1
  89. package/dist/src/core/chat/engine/turns/run-conversation-turn.js +10 -0
  90. package/dist/src/core/chat/engine/turns/run-conversation-turn.js.map +1 -1
  91. package/dist/src/core/chat/engine/turns/service.d.ts +9 -4
  92. package/dist/src/core/chat/engine/turns/service.d.ts.map +1 -1
  93. package/dist/src/core/chat/engine/turns/service.js +29 -28
  94. package/dist/src/core/chat/engine/turns/service.js.map +1 -1
  95. package/dist/src/core/chat/engine/types.d.ts +48 -3
  96. package/dist/src/core/chat/engine/types.d.ts.map +1 -1
  97. package/dist/src/core/chat/types.d.ts +2 -0
  98. package/dist/src/core/chat/types.d.ts.map +1 -1
  99. package/dist/src/core/prompts/system-prompt.d.ts.map +1 -1
  100. package/dist/src/core/prompts/system-prompt.js +1 -0
  101. package/dist/src/core/prompts/system-prompt.js.map +1 -1
  102. package/dist/src/index.d.ts +1 -1
  103. package/dist/src/index.d.ts.map +1 -1
  104. package/dist/src/index.js.map +1 -1
  105. package/dist/src/server/app.js +3 -3
  106. package/dist/src/server/app.js.map +1 -1
  107. package/dist/src/server/features/control-plane/{services → controllers}/ask.d.ts +5 -1
  108. package/dist/src/server/features/control-plane/controllers/ask.d.ts.map +1 -0
  109. package/dist/src/server/features/control-plane/controllers/ask.js +68 -0
  110. package/dist/src/server/features/control-plane/controllers/ask.js.map +1 -0
  111. package/dist/src/server/features/control-plane/controllers/chat-session-events.d.ts +26 -0
  112. package/dist/src/server/features/control-plane/controllers/chat-session-events.d.ts.map +1 -0
  113. package/dist/src/server/features/control-plane/controllers/chat-session-events.js +49 -0
  114. package/dist/src/server/features/control-plane/controllers/chat-session-events.js.map +1 -0
  115. package/dist/src/server/features/control-plane/controllers/chat-session-presenter.d.ts +12 -0
  116. package/dist/src/server/features/control-plane/controllers/chat-session-presenter.d.ts.map +1 -0
  117. package/dist/src/server/features/control-plane/controllers/chat-session-presenter.js +181 -0
  118. package/dist/src/server/features/control-plane/controllers/chat-session-presenter.js.map +1 -0
  119. package/dist/src/server/features/control-plane/controllers/chat-sessions-controller.d.ts +63 -0
  120. package/dist/src/server/features/control-plane/controllers/chat-sessions-controller.d.ts.map +1 -0
  121. package/dist/src/server/features/control-plane/controllers/chat-sessions-controller.js +285 -0
  122. package/dist/src/server/features/control-plane/controllers/chat-sessions-controller.js.map +1 -0
  123. package/dist/src/server/features/control-plane/controllers/chat-turn-review-presenter.d.ts +13 -0
  124. package/dist/src/server/features/control-plane/controllers/chat-turn-review-presenter.d.ts.map +1 -0
  125. package/dist/src/server/features/control-plane/controllers/chat-turn-review-presenter.js +196 -0
  126. package/dist/src/server/features/control-plane/controllers/chat-turn-review-presenter.js.map +1 -0
  127. package/dist/src/server/features/control-plane/controllers/control-plane-state.d.ts +7 -0
  128. package/dist/src/server/features/control-plane/controllers/control-plane-state.d.ts.map +1 -0
  129. package/dist/src/server/features/control-plane/controllers/control-plane-state.js +64 -0
  130. package/dist/src/server/features/control-plane/controllers/control-plane-state.js.map +1 -0
  131. package/dist/src/server/features/control-plane/controllers/heartbeat.d.ts +12 -0
  132. package/dist/src/server/features/control-plane/controllers/heartbeat.d.ts.map +1 -0
  133. package/dist/src/server/features/control-plane/controllers/heartbeat.js +59 -0
  134. package/dist/src/server/features/control-plane/controllers/heartbeat.js.map +1 -0
  135. package/dist/src/server/features/control-plane/controllers/layout-snapshots.d.ts +13 -0
  136. package/dist/src/server/features/control-plane/controllers/layout-snapshots.d.ts.map +1 -0
  137. package/dist/src/server/features/control-plane/controllers/layout-snapshots.js +70 -0
  138. package/dist/src/server/features/control-plane/controllers/layout-snapshots.js.map +1 -0
  139. package/dist/src/server/features/control-plane/controllers/memory.d.ts +25 -0
  140. package/dist/src/server/features/control-plane/controllers/memory.d.ts.map +1 -0
  141. package/dist/src/server/features/control-plane/controllers/memory.js +44 -0
  142. package/dist/src/server/features/control-plane/controllers/memory.js.map +1 -0
  143. package/dist/src/server/features/control-plane/controllers/workspace-diff.d.ts +18 -0
  144. package/dist/src/server/features/control-plane/controllers/workspace-diff.d.ts.map +1 -0
  145. package/dist/src/server/features/control-plane/controllers/workspace-diff.js +260 -0
  146. package/dist/src/server/features/control-plane/controllers/workspace-diff.js.map +1 -0
  147. package/dist/src/server/features/control-plane/controllers/workspace-files.d.ts +38 -0
  148. package/dist/src/server/features/control-plane/controllers/workspace-files.d.ts.map +1 -0
  149. package/dist/src/server/features/control-plane/controllers/workspace-files.js +139 -0
  150. package/dist/src/server/features/control-plane/controllers/workspace-files.js.map +1 -0
  151. package/dist/src/server/features/control-plane/helpers/read-values.d.ts +7 -0
  152. package/dist/src/server/features/control-plane/helpers/read-values.d.ts.map +1 -0
  153. package/dist/src/server/features/control-plane/helpers/read-values.js +26 -0
  154. package/dist/src/server/features/control-plane/helpers/read-values.js.map +1 -0
  155. package/dist/src/server/features/control-plane/router.d.ts +8 -4
  156. package/dist/src/server/features/control-plane/router.d.ts.map +1 -1
  157. package/dist/src/server/features/control-plane/router.js +59 -40
  158. package/dist/src/server/features/control-plane/router.js.map +1 -1
  159. package/dist/src/server/features/control-plane/types.d.ts +2 -0
  160. package/dist/src/server/features/control-plane/types.d.ts.map +1 -1
  161. package/dist/src/server/index.d.ts +1 -1
  162. package/dist/src/server/index.d.ts.map +1 -1
  163. package/dist/src/server/index.js +1 -1
  164. package/dist/src/server/index.js.map +1 -1
  165. package/dist/src/server/router.d.ts +8 -4
  166. package/dist/src/server/router.d.ts.map +1 -1
  167. package/dist/src/web/assets/{MonacoDiffViewer-vifX8i2F.js → MonacoDiffViewer-D0Ed6GCN.js} +1 -1
  168. package/dist/src/web/assets/{index-vEln1ViW.js → index-B_9nk8kS.js} +2 -2
  169. package/dist/src/web/index.html +1 -1
  170. package/package.json +6 -2
  171. package/dist/src/server/features/control-plane/services/ask.d.ts.map +0 -1
  172. package/dist/src/server/features/control-plane/services/ask.js +0 -66
  173. package/dist/src/server/features/control-plane/services/ask.js.map +0 -1
  174. package/dist/src/server/features/control-plane/services/chat-session-events.d.ts +0 -26
  175. package/dist/src/server/features/control-plane/services/chat-session-events.d.ts.map +0 -1
  176. package/dist/src/server/features/control-plane/services/chat-session-events.js +0 -61
  177. package/dist/src/server/features/control-plane/services/chat-session-events.js.map +0 -1
  178. package/dist/src/server/features/control-plane/services/chat-sessions.d.ts +0 -59
  179. package/dist/src/server/features/control-plane/services/chat-sessions.d.ts.map +0 -1
  180. package/dist/src/server/features/control-plane/services/chat-sessions.js +0 -661
  181. package/dist/src/server/features/control-plane/services/chat-sessions.js.map +0 -1
  182. package/dist/src/server/features/control-plane/services/control-plane-state.d.ts +0 -4
  183. package/dist/src/server/features/control-plane/services/control-plane-state.d.ts.map +0 -1
  184. package/dist/src/server/features/control-plane/services/control-plane-state.js +0 -56
  185. package/dist/src/server/features/control-plane/services/control-plane-state.js.map +0 -1
  186. package/dist/src/server/features/control-plane/services/heartbeat.d.ts +0 -9
  187. package/dist/src/server/features/control-plane/services/heartbeat.d.ts.map +0 -1
  188. package/dist/src/server/features/control-plane/services/heartbeat.js +0 -57
  189. package/dist/src/server/features/control-plane/services/heartbeat.js.map +0 -1
  190. package/dist/src/server/features/control-plane/services/layout-snapshots.d.ts +0 -6
  191. package/dist/src/server/features/control-plane/services/layout-snapshots.d.ts.map +0 -1
  192. package/dist/src/server/features/control-plane/services/layout-snapshots.js +0 -68
  193. package/dist/src/server/features/control-plane/services/layout-snapshots.js.map +0 -1
  194. package/dist/src/server/features/control-plane/services/memory.d.ts +0 -22
  195. package/dist/src/server/features/control-plane/services/memory.d.ts.map +0 -1
  196. package/dist/src/server/features/control-plane/services/memory.js +0 -36
  197. package/dist/src/server/features/control-plane/services/memory.js.map +0 -1
  198. package/dist/src/server/features/control-plane/services/workspace-diff.d.ts +0 -4
  199. package/dist/src/server/features/control-plane/services/workspace-diff.d.ts.map +0 -1
  200. package/dist/src/server/features/control-plane/services/workspace-diff.js +0 -258
  201. package/dist/src/server/features/control-plane/services/workspace-diff.js.map +0 -1
  202. package/dist/src/server/features/control-plane/services/workspace-files.d.ts +0 -27
  203. package/dist/src/server/features/control-plane/services/workspace-files.d.ts.map +0 -1
  204. package/dist/src/server/features/control-plane/services/workspace-files.js +0 -137
  205. package/dist/src/server/features/control-plane/services/workspace-files.js.map +0 -1
@@ -1,661 +0,0 @@
1
- /**
2
- * Control-plane chat session application service.
3
- *
4
- * Current compromise:
5
- * some session create/update/read flows in this host service still call the
6
- * file session repository directly. The intended direction is for shared
7
- * session semantics to move into the core session service, with this layer
8
- * limited to control-plane-specific orchestration and projections.
9
- */
10
- import { EventEmitter } from 'node:events';
11
- import { existsSync, readFileSync } from 'node:fs';
12
- import { join } from 'node:path';
13
- import { createChatSession } from '../../../../core/chat/engine/sessions/session-record.js';
14
- import { readChatSession, readChatSessionCatalog, saveChatSessions } from '../../../../core/chat/engine/sessions/repository/file-chat-session-repository.js';
15
- import { DEFAULT_OPENAI_MODEL } from '../../../../core/config.js';
16
- import { credentialModeFromSource, resolveCompatibleActiveModel } from '../../../../core/llm/model-policy.js';
17
- import { inferProviderFromModel } from '../../../../core/llm/providers.js';
18
- import { parseUnifiedDiffFiles } from '../../../../core/review/diff-domain.js';
19
- import { hasProviderCredentialForModel, resolveProviderCredentialSourceForModel } from '../../../../core/runtime/api-keys.js';
20
- import { buildConversationMessages } from '../../../../core/chat/engine/sessions/conversation-lines.js';
21
- import { runConversationTurn } from '../../../../core/chat/engine/index.js';
22
- import { requestToolApproval } from '../../../../core/approvals/surface.js';
23
- import { createControlPlanePendingApprovalView, createControlPlaneSessionEventPublisher, } from './chat-session-events.js';
24
- export function createControlPlaneChatSession(args) {
25
- const existing = readChatSessionViews(args.sessionStoragePath);
26
- const nextNumber = existing.length + 1;
27
- const model = resolveControlPlaneSessionCreationModel(args);
28
- const credentialRuntime = {
29
- preferApiKey: args.preferApiKey,
30
- credentialStorePath: args.credentialStorePath,
31
- };
32
- const session = createChatSession({
33
- id: `session-${Date.now()}`,
34
- name: args.suggestedName?.trim() || `Session ${nextNumber}`,
35
- apiKeyPresent: args.apiKeyPresent ?? hasProviderCredentialForModel(model, credentialRuntime),
36
- model,
37
- workspaceId: args.workspaceId,
38
- });
39
- const currentSessions = readChatSessionCatalog(args.sessionStoragePath)
40
- .map((entry) => readChatSession(args.sessionStoragePath, entry.id, hasProviderCredentialForModel(model, credentialRuntime)))
41
- .filter((candidate) => Boolean(candidate));
42
- saveChatSessions(args.sessionStoragePath, [session, ...currentSessions]);
43
- return projectChatSessionDetail(session)[0];
44
- }
45
- function resolveControlPlaneSessionCreationModel(args) {
46
- const activeModel = firstNonEmpty(args.model, process.env.OPENAI_MODEL, process.env.ANTHROPIC_MODEL) ?? DEFAULT_OPENAI_MODEL;
47
- const provider = inferProviderFromModel(activeModel);
48
- const credentialMode = credentialModeFromSource(resolveProviderCredentialSourceForModel(activeModel, {
49
- preferApiKey: args.preferApiKey,
50
- credentialStorePath: args.credentialStorePath,
51
- }));
52
- return resolveCompatibleActiveModel({
53
- activeModel,
54
- provider,
55
- credentialMode,
56
- }).model;
57
- }
58
- function firstNonEmpty(...values) {
59
- return values.find((value) => typeof value === 'string' && value.trim().length > 0);
60
- }
61
- export function updateControlPlaneChatSessionSettings(args) {
62
- const currentSessions = readChatSessionCatalog(args.sessionStoragePath)
63
- .map((entry) => readChatSession(args.sessionStoragePath, entry.id, true))
64
- .filter((candidate) => Boolean(candidate));
65
- const nextSessions = currentSessions.map((session) => (session.id === args.sessionId ?
66
- {
67
- ...session,
68
- model: args.model ?? session.model,
69
- reasoningEffort: args.reasoningEffort === null ? undefined : args.reasoningEffort ?? session.reasoningEffort,
70
- driftEnabled: args.driftEnabled ?? session.driftEnabled,
71
- updatedAt: new Date().toISOString(),
72
- }
73
- : session));
74
- const updated = nextSessions.find((session) => session.id === args.sessionId);
75
- if (!updated) {
76
- throw new Error(`Chat session not found: ${args.sessionId}`);
77
- }
78
- saveChatSessions(args.sessionStoragePath, nextSessions);
79
- return projectChatSessionDetail(updated)[0];
80
- }
81
- const sessionEventBus = new EventEmitter();
82
- const pendingApprovals = new Map();
83
- const inFlightRuns = new Map();
84
- export async function submitChatPrompt(args) {
85
- if (inFlightRuns.has(args.sessionId)) {
86
- throw new Error('A run is already in progress for this session.');
87
- }
88
- if (process.env.HEDDLE_E2E_FAKE_AGENT === '1') {
89
- return runFakeE2eSessionPrompt(args);
90
- }
91
- const controller = new AbortController();
92
- inFlightRuns.set(args.sessionId, controller);
93
- const events = createControlPlaneSessionEventPublisher({
94
- eventBus: sessionEventBus,
95
- sessionId: args.sessionId,
96
- });
97
- try {
98
- const result = await runConversationTurn({
99
- workspaceRoot: args.workspaceRoot,
100
- stateRoot: args.stateRoot,
101
- sessionStoragePath: args.sessionStoragePath,
102
- traceDir: join(args.stateRoot, 'traces'),
103
- sessionId: args.sessionId,
104
- prompt: args.continuePrompt ?? args.prompt,
105
- apiKey: args.apiKey,
106
- preferApiKey: args.preferApiKey,
107
- systemContext: args.systemContext,
108
- memoryMaintenanceMode: args.memoryMaintenanceMode,
109
- abortSignal: controller.signal,
110
- leaseOwner: args.leaseOwner,
111
- host: {
112
- events: {
113
- onAgentLoopEvent: events.hostPort.events?.onAgentLoopEvent,
114
- },
115
- compaction: {
116
- onPreflightCompactionStatus: events.hostPort.compaction?.onPreflightCompactionStatus,
117
- onFinalCompactionStatus: events.hostPort.compaction?.onFinalCompactionStatus,
118
- },
119
- approvals: {
120
- requestToolApproval: async ({ call, tool }) => {
121
- const decision = await requestToolApproval({
122
- call,
123
- tool,
124
- createView: createControlPlanePendingApprovalView,
125
- storePending: ({ view, resolve }) => {
126
- pendingApprovals.set(args.sessionId, {
127
- approval: view,
128
- resolve,
129
- });
130
- },
131
- publish: (_view, callForEvent) => {
132
- events.publishApprovalRequested(callForEvent);
133
- },
134
- });
135
- pendingApprovals.delete(args.sessionId);
136
- return decision;
137
- },
138
- },
139
- },
140
- });
141
- return {
142
- ...result,
143
- session: projectChatSessionDetail(result.session)[0] ?? null,
144
- };
145
- }
146
- finally {
147
- pendingApprovals.delete(args.sessionId);
148
- inFlightRuns.delete(args.sessionId);
149
- }
150
- }
151
- export async function continueChatPrompt(args) {
152
- const session = readChatSession(args.sessionStoragePath, args.sessionId, true);
153
- if (!session) {
154
- throw new Error(`Chat session not found: ${args.sessionId}`);
155
- }
156
- if (!session.history.length || !session.lastContinuePrompt) {
157
- throw new Error('There is no interrupted or prior run to continue yet.');
158
- }
159
- return await submitChatPrompt({
160
- ...args,
161
- prompt: 'Continue from where you left off.',
162
- continuePrompt: session.lastContinuePrompt,
163
- });
164
- }
165
- async function runFakeE2eSessionPrompt(args) {
166
- const session = readChatSession(args.sessionStoragePath, args.sessionId, true);
167
- if (!session) {
168
- throw new Error(`Chat session not found: ${args.sessionId}`);
169
- }
170
- const timestamp = new Date().toISOString();
171
- const assistantText = `Mocked E2E agent response: ${args.prompt}`;
172
- const nextHistory = [
173
- ...session.history,
174
- { role: 'user', content: args.prompt },
175
- { role: 'assistant', content: assistantText },
176
- ];
177
- const nextTurn = {
178
- id: `e2e-turn-${Date.now()}`,
179
- prompt: args.prompt,
180
- outcome: 'done',
181
- summary: assistantText,
182
- steps: 1,
183
- traceFile: 'e2e-fake-trace.jsonl',
184
- events: ['Mocked E2E session run completed.'],
185
- };
186
- const updatedSession = {
187
- ...session,
188
- history: nextHistory,
189
- messages: buildConversationMessages(nextHistory),
190
- turns: [...session.turns, nextTurn].slice(-8),
191
- updatedAt: timestamp,
192
- lastContinuePrompt: args.prompt,
193
- lease: undefined,
194
- };
195
- saveChatSessions(args.sessionStoragePath, readChatSessionCatalog(args.sessionStoragePath)
196
- .map((entry) => readChatSession(args.sessionStoragePath, entry.id, true))
197
- .filter((candidate) => Boolean(candidate))
198
- .map((candidate) => candidate.id === session.id ? updatedSession : candidate));
199
- sessionEventBus.emit(args.sessionId, {
200
- sessionId: args.sessionId,
201
- timestamp,
202
- event: {
203
- type: 'trace',
204
- runId: `e2e-${args.sessionId}`,
205
- timestamp,
206
- event: {
207
- type: 'run.finished',
208
- outcome: 'done',
209
- summary: assistantText,
210
- step: 1,
211
- timestamp,
212
- },
213
- },
214
- });
215
- return {
216
- outcome: 'done',
217
- summary: assistantText,
218
- session: projectChatSessionDetail(updatedSession)[0] ?? null,
219
- };
220
- }
221
- export function subscribeToControlPlaneSessionEvents(sessionId, listener) {
222
- sessionEventBus.on(sessionId, listener);
223
- return () => {
224
- sessionEventBus.off(sessionId, listener);
225
- };
226
- }
227
- export function getPendingControlPlaneApproval(sessionId) {
228
- return pendingApprovals.get(sessionId)?.approval;
229
- }
230
- export function isControlPlaneSessionRunning(sessionId) {
231
- return inFlightRuns.has(sessionId);
232
- }
233
- export function cancelControlPlaneSessionRun(sessionId) {
234
- const controller = inFlightRuns.get(sessionId);
235
- if (!controller) {
236
- return false;
237
- }
238
- controller.abort();
239
- pendingApprovals.delete(sessionId);
240
- return true;
241
- }
242
- export function resolvePendingControlPlaneApproval(sessionId, decision) {
243
- const pending = pendingApprovals.get(sessionId);
244
- if (!pending) {
245
- return false;
246
- }
247
- pendingApprovals.delete(sessionId);
248
- pending.resolve(decision);
249
- return true;
250
- }
251
- export function readChatSessionViews(sessionStoragePath) {
252
- return readChatSessionCatalog(sessionStoragePath)
253
- .flatMap(projectChatSessionView)
254
- .sort((left, right) => (right.updatedAt ?? '').localeCompare(left.updatedAt ?? ''));
255
- }
256
- export function readChatSessionDetail(sessionStoragePath, id) {
257
- const session = readChatSession(sessionStoragePath, id, true);
258
- return session ? projectChatSessionDetail(session)[0] : undefined;
259
- }
260
- export function readChatTurnReview(sessionStoragePath, sessionId, turnId) {
261
- const session = readChatSessionDetail(sessionStoragePath, sessionId);
262
- const turn = session?.turns.find((candidate) => candidate.id === turnId);
263
- if (!turn) {
264
- return undefined;
265
- }
266
- return loadChatTurnReview(turn.traceFile);
267
- }
268
- export function resolveChatSessionFilePath(stateRoot, sessionId) {
269
- return join(stateRoot, 'chat-sessions', `${sessionId}.json`);
270
- }
271
- export function projectChatSessionView(raw) {
272
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
273
- return [];
274
- }
275
- const candidate = raw;
276
- const id = typeof candidate.id === 'string' ? candidate.id : undefined;
277
- const name = typeof candidate.name === 'string' ? candidate.name : undefined;
278
- if (!id || !name) {
279
- return [];
280
- }
281
- const turns = Array.isArray(candidate.turns) ? candidate.turns : [];
282
- const messages = Array.isArray(candidate.messages) ? candidate.messages : [];
283
- const lastTurn = readObject(turns.at(-1));
284
- const context = readObject(candidate.context);
285
- const archives = Array.isArray(candidate.archives) ? candidate.archives.map(readObject).filter(Boolean) : [];
286
- const rawCompactionStatus = readString(context?.compactionStatus);
287
- const compactionStatus = rawCompactionStatus === 'idle' || rawCompactionStatus === 'running' || rawCompactionStatus === 'failed' ?
288
- rawCompactionStatus
289
- : undefined;
290
- const contextView = context ?
291
- omitUndefined({
292
- estimatedHistoryTokens: readNumber(context.estimatedHistoryTokens),
293
- estimatedRequestTokens: readNumber(context.estimatedRequestTokens),
294
- lastRunInputTokens: readNumber(context.lastRunInputTokens),
295
- lastRunOutputTokens: readNumber(context.lastRunOutputTokens),
296
- lastRunTotalTokens: readNumber(context.lastRunTotalTokens),
297
- compactedMessages: readNumber(context.compactedMessages),
298
- compactedAt: readString(context.compactedAt),
299
- compactionStatus,
300
- compactionError: readString(context.compactionError),
301
- archiveCount: readNumber(context.archiveCount),
302
- currentSummaryPath: readString(context.currentSummaryPath),
303
- lastArchivePath: readString(context.lastArchivePath),
304
- })
305
- : undefined;
306
- const archiveViews = archives.flatMap((archive) => {
307
- const archiveObject = readObject(archive);
308
- if (!archiveObject) {
309
- return [];
310
- }
311
- const id = readString(archiveObject.id);
312
- const path = readString(archiveObject.path);
313
- const summaryPath = readString(archiveObject.summaryPath);
314
- const messageCount = readNumber(archiveObject.messageCount);
315
- const createdAt = readString(archiveObject.createdAt);
316
- if (!id || !path || !summaryPath || messageCount === undefined || !createdAt) {
317
- return [];
318
- }
319
- return [omitUndefined({
320
- id,
321
- path,
322
- summaryPath,
323
- shortDescription: readString(archiveObject.shortDescription),
324
- messageCount,
325
- createdAt,
326
- summaryModel: readString(archiveObject.summaryModel),
327
- })];
328
- });
329
- return [omitUndefined({
330
- id,
331
- name,
332
- workspaceId: readString(candidate.workspaceId),
333
- createdAt: readString(candidate.createdAt),
334
- updatedAt: readString(candidate.updatedAt),
335
- model: readString(candidate.model),
336
- reasoningEffort: readReasoningEffort(candidate.reasoningEffort),
337
- driftEnabled: typeof candidate.driftEnabled === 'boolean' ? candidate.driftEnabled : undefined,
338
- driftLevel: readLatestDriftLevel(turns),
339
- messageCount: messages.length,
340
- turnCount: turns.length,
341
- lastPrompt: readString(lastTurn?.prompt),
342
- lastOutcome: readString(lastTurn?.outcome),
343
- lastSummary: readString(lastTurn?.summary),
344
- context: contextView && Object.keys(contextView).length > 0 ? contextView : undefined,
345
- archives: archiveViews.length > 0 ? archiveViews : undefined,
346
- })];
347
- }
348
- function readReasoningEffort(value) {
349
- return value === 'low' || value === 'medium' || value === 'high' || value === 'ultrahigh' ? value : undefined;
350
- }
351
- function readLatestDriftLevel(turns) {
352
- for (let index = turns.length - 1; index >= 0; index--) {
353
- const turn = readObject(turns[index]);
354
- const traceFile = readString(turn?.traceFile);
355
- const driftLevel = traceFile ? readLatestDriftLevelFromTrace(traceFile) : undefined;
356
- if (driftLevel) {
357
- return driftLevel;
358
- }
359
- }
360
- return undefined;
361
- }
362
- function omitUndefined(value) {
363
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
364
- }
365
- function readLatestDriftLevelFromTrace(traceFile) {
366
- if (!traceFile || !existsSync(traceFile)) {
367
- return undefined;
368
- }
369
- try {
370
- const parsed = JSON.parse(readFileSync(traceFile, 'utf8'));
371
- if (!Array.isArray(parsed)) {
372
- return undefined;
373
- }
374
- for (let index = parsed.length - 1; index >= 0; index--) {
375
- const event = readObject(parsed[index]);
376
- if (event?.type !== 'cyberloop.annotation') {
377
- continue;
378
- }
379
- const driftLevel = readString(event.driftLevel);
380
- if (driftLevel === 'unknown' || driftLevel === 'low' || driftLevel === 'medium' || driftLevel === 'high') {
381
- return driftLevel;
382
- }
383
- }
384
- }
385
- catch {
386
- return undefined;
387
- }
388
- return undefined;
389
- }
390
- export function projectChatSessionDetail(raw) {
391
- const base = projectChatSessionView(raw)[0];
392
- if (!base || !raw || typeof raw !== 'object' || Array.isArray(raw)) {
393
- return [];
394
- }
395
- const candidate = raw;
396
- const messages = Array.isArray(candidate.messages) ? candidate.messages.flatMap(projectChatSessionMessage) : [];
397
- const turns = Array.isArray(candidate.turns) ? candidate.turns.flatMap(projectChatTurnView) : [];
398
- return [{
399
- ...base,
400
- messages,
401
- turns,
402
- lastContinuePrompt: readString(candidate.lastContinuePrompt),
403
- }];
404
- }
405
- function projectChatSessionMessage(raw) {
406
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
407
- return [];
408
- }
409
- const candidate = raw;
410
- const id = readString(candidate.id);
411
- const role = candidate.role === 'user' || candidate.role === 'assistant' ? candidate.role : undefined;
412
- const text = readString(candidate.text);
413
- if (!id || !role || !text) {
414
- return [];
415
- }
416
- return [{
417
- id,
418
- role,
419
- text,
420
- isStreaming: readBoolean(candidate.isStreaming),
421
- isPending: readBoolean(candidate.isPending),
422
- }];
423
- }
424
- function projectChatTurnView(raw) {
425
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
426
- return [];
427
- }
428
- const candidate = raw;
429
- const id = readString(candidate.id);
430
- const prompt = readString(candidate.prompt);
431
- const outcome = readString(candidate.outcome);
432
- const summary = readString(candidate.summary);
433
- const traceFile = readString(candidate.traceFile);
434
- const steps = readNumber(candidate.steps);
435
- const events = Array.isArray(candidate.events) ? candidate.events.filter((event) => typeof event === 'string') : [];
436
- if (!id || !prompt || !outcome || !summary || !traceFile || steps === undefined) {
437
- return [];
438
- }
439
- return [{
440
- id,
441
- prompt,
442
- outcome,
443
- summary,
444
- steps,
445
- traceFile,
446
- events,
447
- }];
448
- }
449
- function loadChatTurnReview(traceFile) {
450
- if (!traceFile || !existsSync(traceFile)) {
451
- return undefined;
452
- }
453
- try {
454
- const parsed = JSON.parse(readFileSync(traceFile, 'utf8'));
455
- if (!Array.isArray(parsed)) {
456
- return undefined;
457
- }
458
- const reviewCommands = [];
459
- const verificationCommands = [];
460
- const mutationCommands = [];
461
- const approvals = [];
462
- const files = new Map();
463
- let diffExcerpt;
464
- let finalSummary;
465
- for (const entry of parsed) {
466
- const event = readObject(entry);
467
- const type = readString(event?.type);
468
- if (!event || !type) {
469
- continue;
470
- }
471
- if (type === 'tool.result') {
472
- const tool = readString(event.tool) ?? 'unknown';
473
- const result = readObject(event.result);
474
- if (tool === 'edit_file' && readBoolean(result?.ok) === true) {
475
- const output = readObject(result?.output);
476
- const file = projectEditFileReview(output);
477
- if (file) {
478
- addChangedFile(files, file);
479
- }
480
- }
481
- const output = readObject(result?.output);
482
- const evidence = output ? projectCommandEvidence(tool, output) : undefined;
483
- if (!evidence) {
484
- continue;
485
- }
486
- if (isReviewCommand(evidence.command)) {
487
- reviewCommands.push(evidence);
488
- if (!diffExcerpt && /^git diff(?:\s|$)/.test(evidence.command) && evidence.stdout) {
489
- diffExcerpt = evidence.stdout;
490
- }
491
- if (/^git diff(?:\s|$)/.test(evidence.command) && evidence.stdout) {
492
- for (const file of parseGitDiffFiles(evidence.stdout)) {
493
- addChangedFile(files, file);
494
- }
495
- }
496
- continue;
497
- }
498
- if (isVerificationCommand(evidence.command)) {
499
- verificationCommands.push(evidence);
500
- continue;
501
- }
502
- if (tool === 'run_shell_mutate') {
503
- mutationCommands.push(evidence);
504
- }
505
- continue;
506
- }
507
- if (type === 'tool.call') {
508
- const call = readObject(event.call);
509
- const tool = readString(call?.tool);
510
- if (tool !== 'edit_file') {
511
- continue;
512
- }
513
- const input = readObject(call?.input);
514
- const path = readString(input?.path);
515
- mutationCommands.push({
516
- tool,
517
- command: path ? `edit_file ${path}` : 'edit_file',
518
- });
519
- continue;
520
- }
521
- if (type === 'tool.approval_resolved') {
522
- const call = readObject(event.call);
523
- const tool = readString(call?.tool);
524
- if (!tool) {
525
- continue;
526
- }
527
- const input = readObject(call?.input);
528
- approvals.push({
529
- tool,
530
- command: readString(input?.command) ?? readString(input?.path),
531
- approved: readBoolean(event.approved) ?? false,
532
- reason: readString(event.reason),
533
- timestamp: readString(event.timestamp),
534
- });
535
- continue;
536
- }
537
- if (type === 'run.finished') {
538
- finalSummary = readString(event.summary) ?? finalSummary;
539
- }
540
- }
541
- return {
542
- traceFile,
543
- diffExcerpt,
544
- finalSummary,
545
- files: [...files.values()],
546
- reviewCommands: dedupeEvidence(reviewCommands),
547
- verificationCommands: dedupeEvidence(verificationCommands),
548
- mutationCommands: dedupeEvidence(mutationCommands),
549
- approvals,
550
- };
551
- }
552
- catch {
553
- return undefined;
554
- }
555
- }
556
- function projectCommandEvidence(tool, output) {
557
- const command = readString(output.command);
558
- if (!command) {
559
- return undefined;
560
- }
561
- return {
562
- tool,
563
- command,
564
- exitCode: readNumber(output.exitCode),
565
- stdout: normalizeCommandText(readString(output.stdout)),
566
- stderr: normalizeCommandText(readString(output.stderr)),
567
- };
568
- }
569
- function dedupeEvidence(evidence) {
570
- const seen = new Set();
571
- return evidence.filter((entry) => {
572
- const key = `${entry.tool}:${entry.command}`;
573
- if (seen.has(key)) {
574
- return false;
575
- }
576
- seen.add(key);
577
- return true;
578
- });
579
- }
580
- function isReviewCommand(command) {
581
- return /^git (?:status\b|diff\b|show\b|branch\b|remote\b)/.test(command);
582
- }
583
- function isVerificationCommand(command) {
584
- return /^(?:yarn|npm|pnpm|bun|vitest|jest|tsx|tsc|go test|cargo test|pytest|ruff|eslint)\b/.test(command);
585
- }
586
- function projectEditFileReview(output) {
587
- if (!output) {
588
- return undefined;
589
- }
590
- const path = readString(output.path);
591
- const diff = readObject(output.diff);
592
- const patch = normalizeCommandText(readString(diff?.diff));
593
- if (!path) {
594
- return undefined;
595
- }
596
- return {
597
- path,
598
- status: statusFromEditAction(readString(output.action)),
599
- source: 'edit_file',
600
- patch,
601
- diff: patch ? parseUnifiedDiffFiles(patch).find((file) => file.path === path || file.oldPath === path) : undefined,
602
- truncated: readBoolean(diff?.truncated),
603
- };
604
- }
605
- function statusFromEditAction(action) {
606
- switch (action) {
607
- case 'created':
608
- return 'added';
609
- case 'overwritten':
610
- case 'replaced':
611
- return 'modified';
612
- default:
613
- return 'unknown';
614
- }
615
- }
616
- function parseGitDiffFiles(diff) {
617
- return parseUnifiedDiffFiles(diff).map((file) => ({
618
- path: file.path,
619
- status: file.status === 'copied' ? 'modified' : file.status,
620
- source: 'git_diff',
621
- patch: normalizeCommandText(file.patch),
622
- diff: file,
623
- truncated: file.binary === true,
624
- }));
625
- }
626
- function addChangedFile(files, file) {
627
- const existing = files.get(file.path);
628
- if (!existing) {
629
- files.set(file.path, file);
630
- return;
631
- }
632
- if (existing.source === 'edit_file') {
633
- return;
634
- }
635
- if (file.source === 'edit_file' || (!existing.patch && file.patch)) {
636
- files.set(file.path, file);
637
- }
638
- }
639
- function normalizeCommandText(value) {
640
- if (!value) {
641
- return undefined;
642
- }
643
- const trimmed = value.trim();
644
- if (!trimmed) {
645
- return undefined;
646
- }
647
- return trimmed.length > 2400 ? `${trimmed.slice(0, 2399)}…` : trimmed;
648
- }
649
- function readObject(value) {
650
- return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
651
- }
652
- function readString(value) {
653
- return typeof value === 'string' ? value : undefined;
654
- }
655
- function readBoolean(value) {
656
- return typeof value === 'boolean' ? value : undefined;
657
- }
658
- function readNumber(value) {
659
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
660
- }
661
- //# sourceMappingURL=chat-sessions.js.map