zelari-code 0.1.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 (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/bin/zelari-code.js +64 -0
  4. package/dist/agents/advancedTools.js +115 -0
  5. package/dist/agents/advancedTools.js.map +1 -0
  6. package/dist/agents/councilApi.js +375 -0
  7. package/dist/agents/councilApi.js.map +1 -0
  8. package/dist/agents/councilDirectives.js +100 -0
  9. package/dist/agents/councilDirectives.js.map +1 -0
  10. package/dist/agents/promoteMember.js +210 -0
  11. package/dist/agents/promoteMember.js.map +1 -0
  12. package/dist/agents/promptModules.js +139 -0
  13. package/dist/agents/promptModules.js.map +1 -0
  14. package/dist/agents/roles.js +281 -0
  15. package/dist/agents/roles.js.map +1 -0
  16. package/dist/agents/skills/builtin/debugging.js +248 -0
  17. package/dist/agents/skills/builtin/debugging.js.map +1 -0
  18. package/dist/agents/skills/builtin/docs.js +301 -0
  19. package/dist/agents/skills/builtin/docs.js.map +1 -0
  20. package/dist/agents/skills/builtin/git-ops.js +301 -0
  21. package/dist/agents/skills/builtin/git-ops.js.map +1 -0
  22. package/dist/agents/skills/builtin/planning.js +335 -0
  23. package/dist/agents/skills/builtin/planning.js.map +1 -0
  24. package/dist/agents/skills/builtin/refactoring.js +261 -0
  25. package/dist/agents/skills/builtin/refactoring.js.map +1 -0
  26. package/dist/agents/skills/builtin/review.js +302 -0
  27. package/dist/agents/skills/builtin/review.js.map +1 -0
  28. package/dist/agents/skills/builtin/testing.js +245 -0
  29. package/dist/agents/skills/builtin/testing.js.map +1 -0
  30. package/dist/agents/skills.js +272 -0
  31. package/dist/agents/skills.js.map +1 -0
  32. package/dist/agents/systemPromptBuilder.js +151 -0
  33. package/dist/agents/systemPromptBuilder.js.map +1 -0
  34. package/dist/agents/toolSchemas.js +249 -0
  35. package/dist/agents/toolSchemas.js.map +1 -0
  36. package/dist/agents/tools.js +196 -0
  37. package/dist/agents/tools.js.map +1 -0
  38. package/dist/agents/vaultTools.js +134 -0
  39. package/dist/agents/vaultTools.js.map +1 -0
  40. package/dist/cli/app.js +1823 -0
  41. package/dist/cli/app.js.map +1 -0
  42. package/dist/cli/branchManager.js +280 -0
  43. package/dist/cli/branchManager.js.map +1 -0
  44. package/dist/cli/compaction.js +63 -0
  45. package/dist/cli/compaction.js.map +1 -0
  46. package/dist/cli/components/ChatStream.js +23 -0
  47. package/dist/cli/components/ChatStream.js.map +1 -0
  48. package/dist/cli/components/CollapsibleToolOutput.js +38 -0
  49. package/dist/cli/components/CollapsibleToolOutput.js.map +1 -0
  50. package/dist/cli/components/Header.js +31 -0
  51. package/dist/cli/components/Header.js.map +1 -0
  52. package/dist/cli/components/InputBar.js +9 -0
  53. package/dist/cli/components/InputBar.js.map +1 -0
  54. package/dist/cli/components/Sidebar.js +17 -0
  55. package/dist/cli/components/Sidebar.js.map +1 -0
  56. package/dist/cli/councilCost.js +81 -0
  57. package/dist/cli/councilCost.js.map +1 -0
  58. package/dist/cli/councilDispatcher.js +56 -0
  59. package/dist/cli/councilDispatcher.js.map +1 -0
  60. package/dist/cli/councilFeedback.js +164 -0
  61. package/dist/cli/councilFeedback.js.map +1 -0
  62. package/dist/cli/crossProviderFailover.js +83 -0
  63. package/dist/cli/crossProviderFailover.js.map +1 -0
  64. package/dist/cli/gitOps.js +96 -0
  65. package/dist/cli/gitOps.js.map +1 -0
  66. package/dist/cli/grokOAuth.js +375 -0
  67. package/dist/cli/grokOAuth.js.map +1 -0
  68. package/dist/cli/keyStore.js +263 -0
  69. package/dist/cli/keyStore.js.map +1 -0
  70. package/dist/cli/keyValidator.js +83 -0
  71. package/dist/cli/keyValidator.js.map +1 -0
  72. package/dist/cli/main.bundled.js +21428 -0
  73. package/dist/cli/main.bundled.js.map +7 -0
  74. package/dist/cli/main.js +72 -0
  75. package/dist/cli/main.js.map +1 -0
  76. package/dist/cli/metrics.js +131 -0
  77. package/dist/cli/metrics.js.map +1 -0
  78. package/dist/cli/modelDiscovery.js +234 -0
  79. package/dist/cli/modelDiscovery.js.map +1 -0
  80. package/dist/cli/modelPricing.js +112 -0
  81. package/dist/cli/modelPricing.js.map +1 -0
  82. package/dist/cli/oauthCallbackServer.js +207 -0
  83. package/dist/cli/oauthCallbackServer.js.map +1 -0
  84. package/dist/cli/provider/openai-compatible.js +236 -0
  85. package/dist/cli/provider/openai-compatible.js.map +1 -0
  86. package/dist/cli/providerConfig.js +210 -0
  87. package/dist/cli/providerConfig.js.map +1 -0
  88. package/dist/cli/providerFailover.js +90 -0
  89. package/dist/cli/providerFailover.js.map +1 -0
  90. package/dist/cli/refreshRegistry.js +77 -0
  91. package/dist/cli/refreshRegistry.js.map +1 -0
  92. package/dist/cli/safety/auditLogger.js +111 -0
  93. package/dist/cli/safety/auditLogger.js.map +1 -0
  94. package/dist/cli/safety/sandboxPath.js +62 -0
  95. package/dist/cli/safety/sandboxPath.js.map +1 -0
  96. package/dist/cli/safety/shellBlocklist.js +71 -0
  97. package/dist/cli/safety/shellBlocklist.js.map +1 -0
  98. package/dist/cli/sessionManager.js +149 -0
  99. package/dist/cli/sessionManager.js.map +1 -0
  100. package/dist/cli/skillCache.js +205 -0
  101. package/dist/cli/skillCache.js.map +1 -0
  102. package/dist/cli/skillHistory.js +178 -0
  103. package/dist/cli/skillHistory.js.map +1 -0
  104. package/dist/cli/skillSuggest.js +132 -0
  105. package/dist/cli/skillSuggest.js.map +1 -0
  106. package/dist/cli/slashCommands.js +431 -0
  107. package/dist/cli/slashCommands.js.map +1 -0
  108. package/dist/cli/toolRegistry.js +187 -0
  109. package/dist/cli/toolRegistry.js.map +1 -0
  110. package/dist/cli/updater.js +157 -0
  111. package/dist/cli/updater.js.map +1 -0
  112. package/dist/main/core/AgentHarness.js +345 -0
  113. package/dist/main/core/AgentHarness.js.map +1 -0
  114. package/dist/main/core/providerStream.js +99 -0
  115. package/dist/main/core/providerStream.js.map +1 -0
  116. package/dist/main/core/sessionJsonl.js +99 -0
  117. package/dist/main/core/sessionJsonl.js.map +1 -0
  118. package/dist/main/core/tools/auditLog.js +26 -0
  119. package/dist/main/core/tools/auditLog.js.map +1 -0
  120. package/dist/main/core/tools/builtin/filesystem.js +110 -0
  121. package/dist/main/core/tools/builtin/filesystem.js.map +1 -0
  122. package/dist/main/core/tools/builtin/search.js +56 -0
  123. package/dist/main/core/tools/builtin/search.js.map +1 -0
  124. package/dist/main/core/tools/builtin/shell.js +51 -0
  125. package/dist/main/core/tools/builtin/shell.js.map +1 -0
  126. package/dist/main/core/tools/registry.js +71 -0
  127. package/dist/main/core/tools/registry.js.map +1 -0
  128. package/dist/main/core/tools/toolTypes.js +8 -0
  129. package/dist/main/core/tools/toolTypes.js.map +1 -0
  130. package/dist/main/core/tools/zodBridge.js +48 -0
  131. package/dist/main/core/tools/zodBridge.js.map +1 -0
  132. package/dist/shared/eventBus.js +104 -0
  133. package/dist/shared/eventBus.js.map +1 -0
  134. package/dist/shared/events.js +73 -0
  135. package/dist/shared/events.js.map +1 -0
  136. package/dist/shared/index.js +8 -0
  137. package/dist/shared/index.js.map +1 -0
  138. package/dist/types/context.js +3 -0
  139. package/dist/types/context.js.map +1 -0
  140. package/dist/types/index.js +2 -0
  141. package/dist/types/index.js.map +1 -0
  142. package/dist/types/knowledge.js +2 -0
  143. package/dist/types/knowledge.js.map +1 -0
  144. package/dist/types/systemTypes.js +10 -0
  145. package/dist/types/systemTypes.js.map +1 -0
  146. package/package.json +63 -0
@@ -0,0 +1,157 @@
1
+ /**
2
+ * updater — npm registry self-update mechanism for zelari-coder.
3
+ *
4
+ * Functions:
5
+ * - getCurrentVersion(): reads bundled package.json
6
+ * - checkForUpdate(): fetches https://registry.npmjs.org/zelari-coder/latest,
7
+ * compares semver, returns { currentVersion, latestVersion, updateAvailable }
8
+ * - performUpdate(): spawns `npm install -g zelari-coder@latest`,
9
+ * captures stdout/stderr, returns { ok, output, error? }
10
+ *
11
+ * All network + spawn operations are injectable for testing (see tests).
12
+ *
13
+ * Channel: `latest` only (locked per user decision, v3-N).
14
+ * No self-restart: caller is expected to display "please restart manually".
15
+ *
16
+ * @see docs/plans/2026-06-30-anathema-coder-v3-N.md
17
+ */
18
+ import { createRequire } from 'node:module';
19
+ import { spawn } from 'node:child_process';
20
+ import path from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
22
+ const require = createRequire(import.meta.url);
23
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
+ /**
25
+ * Read the bundled package.json and return its version.
26
+ * Falls back to '0.0.0' if the file cannot be resolved (very unlikely).
27
+ */
28
+ export function getCurrentVersion() {
29
+ try {
30
+ // Resolve from the package root (this file lives at <pkg>/electron/cli/updater.ts
31
+ // or <pkg>/dist/electron/cli/updater.js after build)
32
+ const pkgPath = path.resolve(__dirname, '..', '..', 'package.json');
33
+ const pkg = require(pkgPath);
34
+ return pkg.version;
35
+ }
36
+ catch {
37
+ return '0.0.0';
38
+ }
39
+ }
40
+ /**
41
+ * Compare two semver strings. Returns:
42
+ * -1 if a < b
43
+ * 0 if a === b
44
+ * 1 if a > b
45
+ *
46
+ * Handles standard semver (MAJOR.MINOR.PATCH) with optional pre-release
47
+ * (e.g. "1.0.0-beta.1"). Pre-release is treated as LOWER than the
48
+ * release with the same MAJOR.MINOR.PATCH.
49
+ */
50
+ export function compareSemver(a, b) {
51
+ const parse = (v) => {
52
+ const m = v.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
53
+ if (!m)
54
+ return [0, 0, 0, null];
55
+ return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] ?? null];
56
+ };
57
+ const [a1, a2, a3, aPre] = parse(a);
58
+ const [b1, b2, b3, bPre] = parse(b);
59
+ if (a1 !== b1)
60
+ return a1 < b1 ? -1 : 1;
61
+ if (a2 !== b2)
62
+ return a2 < b2 ? -1 : 1;
63
+ if (a3 !== b3)
64
+ return a3 < b3 ? -1 : 1;
65
+ // Same MAJOR.MINOR.PATCH: pre-release < release
66
+ if (aPre === bPre)
67
+ return 0;
68
+ if (aPre === null)
69
+ return 1;
70
+ if (bPre === null)
71
+ return -1;
72
+ return aPre < bPre ? -1 : 1;
73
+ }
74
+ /** Public URL for the package on the npm registry. */
75
+ export const REGISTRY_URL = 'https://registry.npmjs.org/zelari-coder/latest';
76
+ /**
77
+ * Fetch the latest version from the npm registry.
78
+ * Injectable `fetcher` for tests (defaults to global fetch with 5s timeout).
79
+ */
80
+ export async function fetchLatestVersion(fetcher = fetch, registryUrl = REGISTRY_URL, timeoutMs = 5000) {
81
+ try {
82
+ const controller = new AbortController();
83
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
84
+ const response = await fetcher(registryUrl, { signal: controller.signal });
85
+ clearTimeout(timer);
86
+ if (!response.ok) {
87
+ return { error: `Registry responded ${response.status}` };
88
+ }
89
+ const data = (await response.json());
90
+ if (!data.version || typeof data.version !== 'string') {
91
+ return { error: 'Registry response missing version field' };
92
+ }
93
+ return { version: data.version };
94
+ }
95
+ catch (err) {
96
+ const message = err instanceof Error ? err.message : String(err);
97
+ return { error: message };
98
+ }
99
+ }
100
+ /**
101
+ * Compare current version against latest from the registry.
102
+ * Always succeeds (never throws) — error states surface in the result.
103
+ */
104
+ export async function checkForUpdate(fetcher = fetch, registryUrl) {
105
+ const currentVersion = getCurrentVersion();
106
+ const latest = await fetchLatestVersion(fetcher, registryUrl);
107
+ if ('error' in latest) {
108
+ return {
109
+ currentVersion,
110
+ latestVersion: currentVersion,
111
+ updateAvailable: false,
112
+ error: latest.error,
113
+ };
114
+ }
115
+ const cmp = compareSemver(currentVersion, latest.version);
116
+ return {
117
+ currentVersion,
118
+ latestVersion: latest.version,
119
+ updateAvailable: cmp < 0,
120
+ };
121
+ }
122
+ /**
123
+ * Spawn `npm install -g <package>@latest` and stream output.
124
+ * Injectable `executor` for tests.
125
+ */
126
+ export async function performUpdate(packageName = 'zelari-coder', executor = spawn) {
127
+ return new Promise((resolve) => {
128
+ const args = ['install', '-g', `${packageName}@latest`];
129
+ let stdout = '';
130
+ let stderr = '';
131
+ const child = executor('npm', args, { stdio: ['ignore', 'pipe', 'pipe'] });
132
+ child.stdout?.on('data', (chunk) => {
133
+ stdout += chunk.toString();
134
+ });
135
+ child.stderr?.on('data', (chunk) => {
136
+ stderr += chunk.toString();
137
+ });
138
+ child.on('error', (err) => {
139
+ resolve({
140
+ ok: false,
141
+ output: stdout + stderr,
142
+ error: err.message,
143
+ exitCode: null,
144
+ });
145
+ });
146
+ child.on('close', (code) => {
147
+ const ok = code === 0;
148
+ resolve({
149
+ ok,
150
+ output: stdout + stderr,
151
+ error: ok ? undefined : `npm exited with code ${code}`,
152
+ exitCode: code,
153
+ });
154
+ });
155
+ });
156
+ }
157
+ //# sourceMappingURL=updater.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updater.js","sourceRoot":"","sources":["../../src/cli/updater.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,345 @@
1
+ /**
2
+ * AgentHarness — provider-neutral agent loop.
3
+ *
4
+ * Encapsulates a single agent turn: given a (model, provider, messages, tools)
5
+ * configuration and a provider streaming function, it drives the stream and
6
+ * yields the full {@link BrainEvent} lifecycle as an `AsyncIterable<BrainEvent>`.
7
+ *
8
+ * Electron-free: this module imports ONLY from `electron/shared/` (the event
9
+ * contract + bus) and `electron/main/core/tools/` (the tool registry). It has
10
+ * zero coupling to `llm.ts` or any main-process IPC, so it can run in the
11
+ * Electron main process today and be reused from the Ink CLI in Phase 14.
12
+ *
13
+ * When `toolRegistry` is provided, tool_call deltas trigger real tool
14
+ * execution and emit a matching `tool_execution_end` event. Without it, the
15
+ * harness only emits `tool_execution_start` (legacy behavior, useful for
16
+ * streaming-only consumers).
17
+ *
18
+ * @see docs/plans/2026-06-28-zelari-code.md (Task 12.1 + 14.8)
19
+ */
20
+ import { createBrainEvent, } from '../../shared/events.js';
21
+ // --- Harness ----------------------------------------------------------------
22
+ export class AgentHarness {
23
+ config;
24
+ eventBus;
25
+ sessionId;
26
+ maxQueuedIterations;
27
+ cancelled = false;
28
+ activeController = null;
29
+ queue = [];
30
+ constructor(config) {
31
+ this.config = config;
32
+ this.eventBus = config.eventBus;
33
+ this.sessionId = config.sessionId ?? crypto.randomUUID();
34
+ this.maxQueuedIterations = config.maxQueuedIterations ?? 3;
35
+ }
36
+ /**
37
+ * Cancel the in-flight run. Events drain until end of stream.
38
+ *
39
+ * Idempotent: calling cancel() multiple times is safe — the cancelled
40
+ * flag stays true and AbortController.abort() is itself a no-op on
41
+ * already-aborted controllers. If no turn is running, this is a
42
+ * no-op (the next run() invocation will check the cancelled flag
43
+ * and emit `agent_end` with `reason: 'cancelled'` before any work).
44
+ *
45
+ * After cancel() returns, the harness should be discarded by the
46
+ * caller (the run() generator finishes after the current turn ends).
47
+ * For mid-stream interrupt + new-prompt injection, see Task C.3.2.
48
+ */
49
+ cancel() {
50
+ if (this.cancelled)
51
+ return; // idempotent — don't re-abort
52
+ this.cancelled = true;
53
+ this.activeController?.abort();
54
+ }
55
+ /** Current size of the queued user-prompt buffer. */
56
+ get queueLength() {
57
+ return this.queue.length;
58
+ }
59
+ /**
60
+ * Enqueue a user prompt to be processed at the next opportunity.
61
+ * The next prompt in the queue will be appended to the conversation
62
+ * transcript and re-enter the provider stream loop after the current
63
+ * turn finishes. Emits a `queue_update` event so UIs can refresh
64
+ * the displayed counter.
65
+ *
66
+ * If `run()` has already returned (no active loop), the queued
67
+ * prompt will be picked up on the *next* run() call — note this
68
+ * helper does NOT itself trigger a run. Callers wire the
69
+ * dequeue step inside `run()` (Task 18.1).
70
+ */
71
+ enqueue(userText) {
72
+ this.queue.push(userText);
73
+ const queueEvent = createBrainEvent('queue_update', this.sessionId, {
74
+ queuedCount: this.queue.length,
75
+ });
76
+ this.emit(queueEvent);
77
+ }
78
+ /**
79
+ * Dequeue the next queued prompt, or `null` if the queue is empty.
80
+ * Emits a `queue_update` event with the new length so UIs can
81
+ * decrement the counter.
82
+ */
83
+ dequeueNext() {
84
+ const next = this.queue.shift() ?? null;
85
+ const queueEvent = createBrainEvent('queue_update', this.sessionId, {
86
+ queuedCount: this.queue.length,
87
+ });
88
+ this.emit(queueEvent);
89
+ return next;
90
+ }
91
+ /**
92
+ * Run a single agent turn and yield provider-neutral BrainEvents.
93
+ * The returned AsyncIterable emits all BrainEvents for the run, then completes.
94
+ * Callers can subscribe via eventBus AND iterate the returned iterable.
95
+ */
96
+ async *run() {
97
+ const startTime = Date.now();
98
+ this.activeController = new AbortController();
99
+ // Emit agent_start
100
+ const startEvent = createBrainEvent('agent_start', this.sessionId, {
101
+ model: this.config.model,
102
+ provider: this.config.provider,
103
+ });
104
+ this.emit(startEvent);
105
+ yield startEvent;
106
+ // === Initial turn (always runs to preserve Phase 12.x behavior) ===
107
+ // The harness processes the messages buffer that was provided in
108
+ // the config. Backward compat: existing callers expect a single
109
+ // provider call here even when the queue is empty.
110
+ const initialMessageId = crypto.randomUUID();
111
+ let totalLength = 0;
112
+ let hadError = false;
113
+ const initialMsgStart = createBrainEvent('message_start', this.sessionId, { messageId: initialMessageId, role: 'assistant' });
114
+ this.emit(initialMsgStart);
115
+ yield initialMsgStart;
116
+ let initialTurnLength = 0;
117
+ const initialFinishRef = { value: 'stop' };
118
+ // Task G.4.3 — capture real provider usage for this turn. The ref
119
+ // is read after runSingleTurn returns and attached to message_end.
120
+ const initialUsageRef = { value: null };
121
+ for await (const ev of this.runSingleTurn(initialMessageId, initialFinishRef, initialUsageRef)) {
122
+ if (ev.type === 'message_delta') {
123
+ initialTurnLength += ev.delta.length;
124
+ }
125
+ else if (ev.type === 'error') {
126
+ // Cancellations are emitted as error events with severity='cancelled'
127
+ // (Task C.3.1) — they must NOT set hadError, otherwise the final
128
+ // agent_end would emit reason='error' instead of 'cancelled'.
129
+ if (ev.severity !== 'cancelled') {
130
+ hadError = true;
131
+ }
132
+ }
133
+ yield ev;
134
+ }
135
+ totalLength += initialTurnLength;
136
+ const initialMsgEnd = createBrainEvent('message_end', this.sessionId, {
137
+ messageId: initialMessageId,
138
+ totalLength: initialTurnLength,
139
+ finishReason: initialFinishRef.value,
140
+ ...(initialUsageRef.value ? { usage: initialUsageRef.value } : {}),
141
+ });
142
+ this.emit(initialMsgEnd);
143
+ yield initialMsgEnd;
144
+ // === Queue drain (Task 18.1) ===
145
+ // After the initial turn, drain queued user prompts up to
146
+ // maxQueuedIterations. Each iteration appends the next dequeued
147
+ // user prompt to messages, then re-enters the provider stream.
148
+ // Loop terminates on cancel, error, empty queue, or max iterations.
149
+ let turns = 0;
150
+ while (turns < this.maxQueuedIterations) {
151
+ if (this.cancelled)
152
+ break;
153
+ if (this.queue.length === 0)
154
+ break;
155
+ const queuedPrompt = this.dequeueNext();
156
+ if (queuedPrompt === null)
157
+ break;
158
+ // Append to conversation transcript for this turn
159
+ this.config.messages.push({ role: 'user', content: queuedPrompt });
160
+ const turnMessageId = crypto.randomUUID();
161
+ const msgStart = createBrainEvent('message_start', this.sessionId, {
162
+ messageId: turnMessageId,
163
+ role: 'assistant',
164
+ });
165
+ this.emit(msgStart);
166
+ yield msgStart;
167
+ let turnLength = 0;
168
+ const turnFinishRef = { value: 'stop' };
169
+ const turnUsageRef = { value: null };
170
+ for await (const ev of this.runSingleTurn(turnMessageId, turnFinishRef, turnUsageRef)) {
171
+ if (ev.type === 'message_delta') {
172
+ turnLength += ev.delta.length;
173
+ }
174
+ else if (ev.type === 'error') {
175
+ hadError = true;
176
+ }
177
+ yield ev;
178
+ }
179
+ totalLength += turnLength;
180
+ const msgEnd = createBrainEvent('message_end', this.sessionId, {
181
+ messageId: turnMessageId,
182
+ totalLength: turnLength,
183
+ finishReason: turnFinishRef.value,
184
+ ...(turnUsageRef.value ? { usage: turnUsageRef.value } : {}),
185
+ });
186
+ this.emit(msgEnd);
187
+ yield msgEnd;
188
+ turns++;
189
+ }
190
+ // Emit agent_end
191
+ const agentEnd = createBrainEvent('agent_end', this.sessionId, {
192
+ reason: hadError ? 'error' : this.cancelled ? 'cancelled' : 'completed',
193
+ durationMs: Date.now() - startTime,
194
+ });
195
+ this.emit(agentEnd);
196
+ yield agentEnd;
197
+ this.activeController = null;
198
+ }
199
+ /**
200
+ * Run a single provider turn for the current message buffer.
201
+ * Streams from the provider, dispatches deltas to events, executes
202
+ * any tool calls (if a registry was provided). Yields each
203
+ * `BrainEvent` it produces so the caller can re-emit them in
204
+ * the outer generator.
205
+ *
206
+ * Does NOT emit `message_start` / `message_end` — those are the
207
+ * caller's responsibility (so the outer generator can wrap them
208
+ * around the yielded event stream and compute the right
209
+ * `totalLength` from observed deltas).
210
+ *
211
+ * Extracted from the original monolithic `run()` body to enable
212
+ * the queue-draining loop in Task 18.1.
213
+ */
214
+ async *runSingleTurn(messageId, finishRef, usageRef) {
215
+ try {
216
+ const stream = this.config.providerStream({
217
+ messages: this.config.messages,
218
+ model: this.config.model,
219
+ provider: this.config.provider,
220
+ tools: this.config.tools,
221
+ signal: this.activeController?.signal,
222
+ });
223
+ // Per-turn tool call counter (Task G.2). Reset on every turn so
224
+ // queue-drained turns don't accumulate across prompts.
225
+ let toolCallsThisTurn = 0;
226
+ const maxToolCalls = this.config.maxToolCallsPerTurn;
227
+ for await (const delta of stream) {
228
+ if (this.cancelled) {
229
+ // Emit a cancellation error event so consumers (CLI, tests) can
230
+ // observe the mid-stream interrupt (Task C.3.1).
231
+ const cancelEvent = createBrainEvent('error', this.sessionId, {
232
+ severity: 'cancelled',
233
+ message: 'Run cancelled by user.',
234
+ code: 'cancelled',
235
+ });
236
+ this.emit(cancelEvent);
237
+ yield cancelEvent;
238
+ break;
239
+ }
240
+ if (delta.kind === 'text') {
241
+ const deltaEvent = createBrainEvent('message_delta', this.sessionId, { messageId, delta: delta.delta });
242
+ this.emit(deltaEvent);
243
+ yield deltaEvent;
244
+ }
245
+ else if (delta.kind === 'thinking') {
246
+ const thinkEvent = createBrainEvent('thinking_delta', this.sessionId, {
247
+ messageId,
248
+ delta: delta.delta,
249
+ });
250
+ this.emit(thinkEvent);
251
+ yield thinkEvent;
252
+ }
253
+ else if (delta.kind === 'tool_call') {
254
+ toolCallsThisTurn++;
255
+ const toolStartEvent = createBrainEvent('tool_execution_start', this.sessionId, {
256
+ toolCallId: delta.toolCallId,
257
+ toolName: delta.toolName,
258
+ args: delta.args,
259
+ });
260
+ this.emit(toolStartEvent);
261
+ yield toolStartEvent;
262
+ // Skip registry execution if we've exceeded the per-turn limit
263
+ // (Task G.2, carryover from v3-C C.1.5). Emit a synthetic
264
+ // `tool_execution_end` so the UI can still render the attempt
265
+ // and downstream consumers (e.g. the LLM context) see a clean
266
+ // closure. Without this guard, a council member could fire 50
267
+ // read_file calls in one turn and blow the message context.
268
+ const skipped = typeof maxToolCalls === 'number' && toolCallsThisTurn > maxToolCalls;
269
+ if (this.config.toolRegistry && !skipped) {
270
+ const startMs = Date.now();
271
+ const result = await this.config.toolRegistry.invoke(delta.toolName, delta.args, {
272
+ cwd: this.config.cwd,
273
+ sessionId: this.sessionId,
274
+ signal: this.activeController?.signal,
275
+ });
276
+ const endEvent = createBrainEvent('tool_execution_end', this.sessionId, {
277
+ toolCallId: delta.toolCallId,
278
+ result: result.ok ? String(result.value) : result.error,
279
+ isError: !result.ok,
280
+ durationMs: Date.now() - startMs,
281
+ });
282
+ this.emit(endEvent);
283
+ yield endEvent;
284
+ }
285
+ else if (skipped) {
286
+ // Synthetic end event — no registry call, just close out the
287
+ // tool_execution_start with an explicit skip reason. The LLM
288
+ // can see in the next provider turn that this tool didn't run.
289
+ const endEvent = createBrainEvent('tool_execution_end', this.sessionId, {
290
+ toolCallId: delta.toolCallId,
291
+ result: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`,
292
+ isError: true,
293
+ durationMs: 0,
294
+ });
295
+ this.emit(endEvent);
296
+ yield endEvent;
297
+ }
298
+ }
299
+ else if (delta.kind === 'finish') {
300
+ // Capture the finish reason via the shared ref so the caller
301
+ // can synthesize the matching `message_end`.
302
+ finishRef.value = delta.reason;
303
+ break;
304
+ }
305
+ else if (delta.kind === 'error') {
306
+ const errEvent = createBrainEvent('error', this.sessionId, {
307
+ severity: 'recoverable',
308
+ message: delta.message,
309
+ });
310
+ this.emit(errEvent);
311
+ yield errEvent;
312
+ }
313
+ else if (delta.kind === 'usage') {
314
+ // Provider-reported token usage (Task G.4). The breakdown is
315
+ // captured in the shared ref so the caller's `message_end`
316
+ // synthesis can attach it. The delta itself does not become a
317
+ // standalone BrainEvent (it would be noise in the chat stream);
318
+ // consumers wanting real-time token counts hook the message_end
319
+ // and read `event.usage`.
320
+ usageRef.value = delta.usage;
321
+ }
322
+ }
323
+ }
324
+ catch (err) {
325
+ const errorMessage = err instanceof Error ? err.message : String(err);
326
+ const errEvent = createBrainEvent('error', this.sessionId, {
327
+ severity: 'recoverable',
328
+ message: errorMessage,
329
+ });
330
+ this.emit(errEvent);
331
+ yield errEvent;
332
+ }
333
+ }
334
+ emit(event) {
335
+ if (this.eventBus) {
336
+ try {
337
+ this.eventBus.emit(event);
338
+ }
339
+ catch (err) {
340
+ console.error('[AgentHarness] eventBus.emit failed:', err);
341
+ }
342
+ }
343
+ }
344
+ }
345
+ //# sourceMappingURL=AgentHarness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentHarness.js","sourceRoot":"","sources":["../../../src/main/core/AgentHarness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EACL,gBAAgB,GAUjB,MAAM,wBAAwB,CAAC;AA6FhC,+EAA+E;AAE/E,MAAM,OAAO,YAAY;IACN,MAAM,CAAqB;IAC3B,QAAQ,CAAuB;IAC/B,SAAS,CAAS;IAClB,mBAAmB,CAAS;IACrC,SAAS,GAAG,KAAK,CAAC;IAClB,gBAAgB,GAA2B,IAAI,CAAC;IAChD,KAAK,GAAa,EAAE,CAAC;IAE7B,YAAY,MAA0B;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACzD,IAAI,CAAC,mBAAmB,GAAG,MAAM,CAAC,mBAAmB,IAAI,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,MAAM;QACJ,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,8BAA8B;QAC1D,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,CAAC;IACjC,CAAC;IAED,qDAAqD;IACrD,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,QAAgB;QACtB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1B,MAAM,UAAU,GAA0B,gBAAgB,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,EAAE;YACzF,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM;SAC/B,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC;QACxC,MAAM,UAAU,GAA0B,gBAAgB,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,EAAE;YACzF,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM;SAC/B,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,CAAC,GAAG;QACR,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,gBAAgB,GAAG,IAAI,eAAe,EAAE,CAAC;QAE9C,mBAAmB;QACnB,MAAM,UAAU,GAAyB,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,EAAE;YACvF,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;SAC/B,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtB,MAAM,UAAU,CAAC;QAEjB,qEAAqE;QACrE,iEAAiE;QACjE,gEAAgE;QAChE,mDAAmD;QACnD,MAAM,gBAAgB,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAC7C,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,QAAQ,GAAG,KAAK,CAAC;QAErB,MAAM,eAAe,GAA2B,gBAAgB,CAC9D,eAAe,EACf,IAAI,CAAC,SAAS,EACd,EAAE,SAAS,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,CACnD,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC3B,MAAM,eAAe,CAAC;QAEtB,IAAI,iBAAiB,GAAG,CAAC,CAAC;QAC1B,MAAM,gBAAgB,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC3C,kEAAkE;QAClE,mEAAmE;QACnE,MAAM,eAAe,GAAG,EAAE,KAAK,EAAE,IAA6B,EAAE,CAAC;QACjE,IAAI,KAAK,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC,aAAa,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,CAAC;YAC/F,IAAI,EAAE,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;gBAChC,iBAAiB,IAAK,EAA6B,CAAC,KAAK,CAAC,MAAM,CAAC;YACnE,CAAC;iBAAM,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC/B,sEAAsE;gBACtE,iEAAiE;gBACjE,8DAA8D;gBAC9D,IAAI,EAAE,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;oBAChC,QAAQ,GAAG,IAAI,CAAC;gBAClB,CAAC;YACH,CAAC;YACD,MAAM,EAAE,CAAC;QACX,CAAC;QACD,WAAW,IAAI,iBAAiB,CAAC;QAEjC,MAAM,aAAa,GAAyB,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,EAAE;YAC1F,SAAS,EAAE,gBAAgB;YAC3B,WAAW,EAAE,iBAAiB;YAC9B,YAAY,EAAE,gBAAgB,CAAC,KAAK;YACpC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnE,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzB,MAAM,aAAa,CAAC;QAEpB,kCAAkC;QAClC,0DAA0D;QAC1D,gEAAgE;QAChE,+DAA+D;QAC/D,oEAAoE;QACpE,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,OAAO,KAAK,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACxC,IAAI,IAAI,CAAC,SAAS;gBAAE,MAAM;YAC1B,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM;YAEnC,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YACxC,IAAI,YAAY,KAAK,IAAI;gBAAE,MAAM;YAEjC,kDAAkD;YAClD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;YAEnE,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAA2B,gBAAgB,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,EAAE;gBACzF,SAAS,EAAE,aAAa;gBACxB,IAAI,EAAE,WAAW;aAClB,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpB,MAAM,QAAQ,CAAC;YAEf,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,MAAM,aAAa,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YACxC,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,IAA6B,EAAE,CAAC;YAC9D,IAAI,KAAK,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC,aAAa,CAAC,aAAa,EAAE,aAAa,EAAE,YAAY,CAAC,EAAE,CAAC;gBACtF,IAAI,EAAE,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;oBAChC,UAAU,IAAK,EAA6B,CAAC,KAAK,CAAC,MAAM,CAAC;gBAC5D,CAAC;qBAAM,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC/B,QAAQ,GAAG,IAAI,CAAC;gBAClB,CAAC;gBACD,MAAM,EAAE,CAAC;YACX,CAAC;YACD,WAAW,IAAI,UAAU,CAAC;YAE1B,MAAM,MAAM,GAAyB,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,EAAE;gBACnF,SAAS,EAAE,aAAa;gBACxB,WAAW,EAAE,UAAU;gBACvB,YAAY,EAAE,aAAa,CAAC,KAAK;gBACjC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7D,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,MAAM,CAAC;YAEb,KAAK,EAAE,CAAC;QACV,CAAC;QAED,iBAAiB;QACjB,MAAM,QAAQ,GAAuB,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE;YACjF,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW;YACvE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;SACnC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpB,MAAM,QAAQ,CAAC;QAEf,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACK,KAAK,CAAC,CAAC,aAAa,CAC1B,SAAiB,EACjB,SAA4B,EAC5B,QAA0C;QAE1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;gBACxC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC9B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;gBACxB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC9B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;gBACxB,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM;aACtC,CAAC,CAAC;YAEH,gEAAgE;YAChE,uDAAuD;YACvD,IAAI,iBAAiB,GAAG,CAAC,CAAC;YAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC;YAErD,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBACjC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;oBACnB,gEAAgE;oBAChE,iDAAiD;oBACjD,MAAM,WAAW,GAAG,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE;wBAC5D,QAAQ,EAAE,WAAW;wBACrB,OAAO,EAAE,wBAAwB;wBACjC,IAAI,EAAE,WAAW;qBAClB,CAAC,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;oBACvB,MAAM,WAAW,CAAC;oBAClB,MAAM;gBACR,CAAC;gBAED,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC1B,MAAM,UAAU,GAA2B,gBAAgB,CACzD,eAAe,EACf,IAAI,CAAC,SAAS,EACd,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAClC,CAAC;oBACF,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBACtB,MAAM,UAAU,CAAC;gBACnB,CAAC;qBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;oBACrC,MAAM,UAAU,GAAG,gBAAgB,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,EAAE;wBACpE,SAAS;wBACT,KAAK,EAAE,KAAK,CAAC,KAAK;qBACnB,CAAC,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBACtB,MAAM,UAAU,CAAC;gBACnB,CAAC;qBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBACtC,iBAAiB,EAAE,CAAC;oBACpB,MAAM,cAAc,GAAG,gBAAgB,CAAC,sBAAsB,EAAE,IAAI,CAAC,SAAS,EAAE;wBAC9E,UAAU,EAAE,KAAK,CAAC,UAAU;wBAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,IAAI,EAAE,KAAK,CAAC,IAAI;qBACjB,CAAC,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;oBAC1B,MAAM,cAAc,CAAC;oBAErB,+DAA+D;oBAC/D,0DAA0D;oBAC1D,8DAA8D;oBAC9D,8DAA8D;oBAC9D,8DAA8D;oBAC9D,4DAA4D;oBAC5D,MAAM,OAAO,GAAG,OAAO,YAAY,KAAK,QAAQ,IAAI,iBAAiB,GAAG,YAAY,CAAC;oBAErF,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC;wBACzC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;wBAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAClD,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,IAAI,EACV;4BACE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG;4BACpB,SAAS,EAAE,IAAI,CAAC,SAAS;4BACzB,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM;yBACtC,CACF,CAAC;wBACF,MAAM,QAAQ,GAA+B,gBAAgB,CAC3D,oBAAoB,EACpB,IAAI,CAAC,SAAS,EACd;4BACE,UAAU,EAAE,KAAK,CAAC,UAAU;4BAC5B,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK;4BACvD,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE;4BACnB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;yBACjC,CACF,CAAC;wBACF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBACpB,MAAM,QAAQ,CAAC;oBACjB,CAAC;yBAAM,IAAI,OAAO,EAAE,CAAC;wBACnB,6DAA6D;wBAC7D,6DAA6D;wBAC7D,+DAA+D;wBAC/D,MAAM,QAAQ,GAA+B,gBAAgB,CAC3D,oBAAoB,EACpB,IAAI,CAAC,SAAS,EACd;4BACE,UAAU,EAAE,KAAK,CAAC,UAAU;4BAC5B,MAAM,EAAE,gDAAgD,YAAY,GAAG;4BACvE,OAAO,EAAE,IAAI;4BACb,UAAU,EAAE,CAAC;yBACd,CACF,CAAC;wBACF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBACpB,MAAM,QAAQ,CAAC;oBACjB,CAAC;gBACH,CAAC;qBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACnC,6DAA6D;oBAC7D,6CAA6C;oBAC7C,SAAS,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;oBAC/B,MAAM;gBACR,CAAC;qBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAClC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE;wBACzD,QAAQ,EAAE,aAAa;wBACvB,OAAO,EAAE,KAAK,CAAC,OAAO;qBACvB,CAAC,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBACpB,MAAM,QAAQ,CAAC;gBACjB,CAAC;qBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAClC,6DAA6D;oBAC7D,2DAA2D;oBAC3D,8DAA8D;oBAC9D,gEAAgE;oBAChE,gEAAgE;oBAChE,0BAA0B;oBAC1B,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;gBAC/B,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,YAAY,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACtE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE;gBACzD,QAAQ,EAAE,aAAa;gBACvB,OAAO,EAAE,YAAY;aACtB,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpB,MAAM,QAAQ,CAAC;QACjB,CAAC;IACH,CAAC;IAEO,IAAI,CAAC,KAAiB;QAC5B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Convert AgentHarness AgentMessage[] → legacy {role, content}[].
3
+ * Tool messages pass through their content (renderer fills these).
4
+ */
5
+ function toLegacyMessages(messages) {
6
+ return messages.map((m) => ({ role: m.role, content: m.content }));
7
+ }
8
+ /**
9
+ * Convert AgentHarness AgentToolSpec[] → legacy OpenAI-shaped tool descriptors.
10
+ */
11
+ function toLegacyTools(tools) {
12
+ return tools.map((t) => ({
13
+ type: 'function',
14
+ function: { name: t.name, description: t.description, parameters: t.parameters },
15
+ }));
16
+ }
17
+ /**
18
+ * Wrap a legacy stream function into a ProviderStreamFn that yields
19
+ * provider-neutral deltas. Text chunks are forwarded; finish/end is detected
20
+ * by the legacy function's resolved result (content length is the sum of deltas).
21
+ *
22
+ * NOTE: This adapter does NOT yield tool_call deltas yet — it treats tool calls
23
+ * as a single 'finish' + tools-in-result pattern. Full tool-call streaming lands
24
+ * in a later task.
25
+ */
26
+ export function wrapLegacyStream(legacyStream, params, _messages, _tools) {
27
+ return async function* (providerParams) {
28
+ const accumulatedChunks = [];
29
+ const legacyParams = {
30
+ apiKey: params.apiKey,
31
+ model: providerParams.model,
32
+ provider: providerParams.provider,
33
+ messages: toLegacyMessages(providerParams.messages),
34
+ tools: toLegacyTools(providerParams.tools),
35
+ temperature: 0.7,
36
+ stream: true,
37
+ customBaseUrl: params.customBaseUrl,
38
+ customAuthStyle: params.customAuthStyle,
39
+ onChunk: (chunk) => {
40
+ accumulatedChunks.push(chunk);
41
+ },
42
+ onRequestId: undefined,
43
+ signal: providerParams.signal,
44
+ };
45
+ // Fire the legacy stream and yield chunks as they arrive.
46
+ // We can't await the whole result before yielding — we need to yield
47
+ // chunks incrementally. Use a small async queue.
48
+ const queue = [];
49
+ let resolveNext = null;
50
+ let done = false;
51
+ let error = null;
52
+ const promise = legacyStream(legacyParams)
53
+ .then((result) => {
54
+ // After legacy stream completes, emit a tool_call event for each tool
55
+ // it returned (AgentHarness will then schedule tool execution in 12.6).
56
+ for (const tc of result.toolCalls) {
57
+ queue.push({ kind: 'tool_call', toolCallId: tc.id, toolName: tc.name, args: tc.args });
58
+ }
59
+ queue.push({ kind: 'finish', reason: 'stop' });
60
+ })
61
+ .catch((err) => {
62
+ error = err;
63
+ })
64
+ .finally(() => {
65
+ done = true;
66
+ if (resolveNext) {
67
+ resolveNext();
68
+ resolveNext = null;
69
+ }
70
+ });
71
+ // Pump chunks from accumulatedChunks every microtask
72
+ let lastEmittedIndex = 0;
73
+ while (!done || queue.length > 0 || lastEmittedIndex < accumulatedChunks.length) {
74
+ // Emit any new chunks
75
+ while (lastEmittedIndex < accumulatedChunks.length) {
76
+ yield { kind: 'text', delta: accumulatedChunks[lastEmittedIndex] };
77
+ lastEmittedIndex++;
78
+ }
79
+ // Emit any queued deltas
80
+ while (queue.length > 0) {
81
+ const d = queue.shift();
82
+ yield d;
83
+ }
84
+ // If error, throw
85
+ if (error) {
86
+ throw error;
87
+ }
88
+ // If done but nothing left, exit
89
+ if (done)
90
+ break;
91
+ // Wait for next microtask
92
+ await new Promise((resolve) => {
93
+ resolveNext = resolve;
94
+ });
95
+ }
96
+ await promise; // Ensure the legacy promise resolves (catches final errors)
97
+ };
98
+ }
99
+ //# sourceMappingURL=providerStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerStream.js","sourceRoot":"","sources":["../../../src/main/core/providerStream.ts"],"names":[],"mappings":"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"}