@zelari/core 2.7.0 → 2.9.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 (57) hide show
  1. package/dist/core/AgentHarness.d.ts +52 -0
  2. package/dist/core/AgentHarness.d.ts.map +1 -1
  3. package/dist/core/AgentHarness.js +229 -17
  4. package/dist/core/AgentHarness.js.map +1 -1
  5. package/dist/core/requestSnapshot.d.ts +5 -1
  6. package/dist/core/requestSnapshot.d.ts.map +1 -1
  7. package/dist/core/requestSnapshot.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +2 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/memory/adapters.d.ts +42 -0
  12. package/dist/memory/adapters.d.ts.map +1 -0
  13. package/dist/memory/adapters.js +160 -0
  14. package/dist/memory/adapters.js.map +1 -0
  15. package/dist/memory/context.d.ts +8 -0
  16. package/dist/memory/context.d.ts.map +1 -0
  17. package/dist/memory/context.js +65 -0
  18. package/dist/memory/context.js.map +1 -0
  19. package/dist/memory/index.d.ts +10 -0
  20. package/dist/memory/index.d.ts.map +1 -0
  21. package/dist/memory/index.js +10 -0
  22. package/dist/memory/index.js.map +1 -0
  23. package/dist/memory/noop.d.ts +26 -0
  24. package/dist/memory/noop.d.ts.map +1 -0
  25. package/dist/memory/noop.js +51 -0
  26. package/dist/memory/noop.js.map +1 -0
  27. package/dist/memory/policies.d.ts +16 -0
  28. package/dist/memory/policies.d.ts.map +1 -0
  29. package/dist/memory/policies.js +144 -0
  30. package/dist/memory/policies.js.map +1 -0
  31. package/dist/memory/schemas.d.ts +235 -0
  32. package/dist/memory/schemas.d.ts.map +1 -0
  33. package/dist/memory/schemas.js +96 -0
  34. package/dist/memory/schemas.js.map +1 -0
  35. package/dist/memory/scoring.d.ts +22 -0
  36. package/dist/memory/scoring.d.ts.map +1 -0
  37. package/dist/memory/scoring.js +64 -0
  38. package/dist/memory/scoring.js.map +1 -0
  39. package/dist/memory/semantic.d.ts +30 -0
  40. package/dist/memory/semantic.d.ts.map +1 -0
  41. package/dist/memory/semantic.js +180 -0
  42. package/dist/memory/semantic.js.map +1 -0
  43. package/dist/memory/service.d.ts +45 -0
  44. package/dist/memory/service.d.ts.map +1 -0
  45. package/dist/memory/service.js +491 -0
  46. package/dist/memory/service.js.map +1 -0
  47. package/dist/memory/types.d.ts +364 -23
  48. package/dist/memory/types.d.ts.map +1 -1
  49. package/dist/memory/types.js +45 -9
  50. package/dist/memory/types.js.map +1 -1
  51. package/dist/session/modelSurface.d.ts +6 -4
  52. package/dist/session/modelSurface.d.ts.map +1 -1
  53. package/dist/session/modelSurface.js +8 -13
  54. package/dist/session/modelSurface.js.map +1 -1
  55. package/dist/session/types.js +2 -2
  56. package/dist/session/types.js.map +1 -1
  57. package/package.json +3 -3
@@ -21,6 +21,7 @@ import { EventBus } from '../shared/eventBus.js';
21
21
  import { type BrainEvent, type UsageBreakdown } from '../shared/events.js';
22
22
  import { ToolRegistry } from './tools/registry.js';
23
23
  import { type RoutedRequestSnapshot, type ProviderGenerationOptions } from './requestSnapshot.js';
24
+ import type { MemoryService } from '../memory/types.js';
24
25
  export { collapseLoopedAssistantText, detectAssistantTextLoop, detectAssistantTextLoopWindow, normalizeLoopUnit, isStatusTheaterUnit, TEXT_LOOP_RECOVERY_SYSTEM, TEXT_LOOP_RECOVERY_USER_PROMPT, type TextLoopHit, } from './textLoopDetect.js';
25
26
  /** Inline image sent to vision-capable providers (raw base64, no data: prefix). */
26
27
  export interface AgentImage {
@@ -76,6 +77,26 @@ export interface AgentToolSpec {
76
77
  /** JSON Schema for the tool's input arguments. */
77
78
  parameters: Record<string, unknown>;
78
79
  }
80
+ /** Host-owned progress evidence for mutation-required build turns. */
81
+ export interface BuildProgress {
82
+ toolCalls: number;
83
+ mutationsAttempted: number;
84
+ mutationsSucceeded: number;
85
+ verificationCalls: number;
86
+ recoveries: number;
87
+ }
88
+ /**
89
+ * Provider-neutral build-liveness policy. The host decides whether the task
90
+ * requires an on-disk mutation; the harness owns progress detection and the
91
+ * bounded recovery loop. Provider-specific forcing is serialized only by the
92
+ * provider adapter through generation.toolChoice.
93
+ */
94
+ export interface BuildLivenessPolicy {
95
+ mutationRequired: boolean;
96
+ /** Zero-mutation recovery turns before the run is failed. Default 2. */
97
+ maxRecoveries?: number;
98
+ }
99
+ export declare const BUILD_LIVENESS_RECOVERY_PROMPT: string;
79
100
  export interface AgentHarnessConfig {
80
101
  /** Model identifier (e.g. 'grok-4', 'MiniMax-M2.5'). */
81
102
  model: string;
@@ -99,6 +120,14 @@ export interface AgentHarnessConfig {
99
120
  * tool execution and emit a matching `tool_execution_end` event.
100
121
  */
101
122
  toolRegistry?: ToolRegistry;
123
+ /** Enforce successful mutation evidence before a mutate/build task completes. */
124
+ buildLiveness?: BuildLivenessPolicy;
125
+ /**
126
+ * Volatile messages appended only to the routed request. They never enter
127
+ * `config.messages` or `getMessages()` and are re-read before every call.
128
+ * Used for current resource status without rewriting persistent history.
129
+ */
130
+ requestTail?: () => readonly AgentMessage[];
102
131
  /** Optional cwd for tool execution. Defaults to process.cwd(). */
103
132
  cwd?: string;
104
133
  /**
@@ -180,6 +209,16 @@ export interface AgentHarnessConfig {
180
209
  memberId?: string;
181
210
  /** Human-readable member label (e.g. "Caronte"). See `memberId`. */
182
211
  memberName?: string;
212
+ /**
213
+ * Optional native project memory. The harness performs a bounded, fail-open
214
+ * recall before the first provider call and injects it ephemerally after the
215
+ * stable system prefix (it never mutates conversation history).
216
+ */
217
+ memoryService?: MemoryService;
218
+ /** Explicit recall objective; defaults to the latest user message. */
219
+ memoryQuery?: string;
220
+ /** Hard character budget for the ephemeral memory block. Default 2,000. */
221
+ memoryContextChars?: number;
183
222
  }
184
223
  export type ProviderStreamFn = (params: {
185
224
  messages: AgentMessage[];
@@ -187,6 +226,8 @@ export type ProviderStreamFn = (params: {
187
226
  provider: string;
188
227
  tools: AgentToolSpec[];
189
228
  signal?: AbortSignal;
229
+ /** Stable harness/session id for provider-side conversation affinity. */
230
+ conversationId?: string;
190
231
  /**
191
232
  * v1.36.0: optional generation knobs (purpose/temperature/maxTokens).
192
233
  * Providers that understand them apply them; others ignore them. Used by
@@ -256,6 +297,9 @@ export declare class AgentHarness {
256
297
  private textToolReentries;
257
298
  /** Fase M: per-run context-growth counters (reset on every run()). */
258
299
  private growth;
300
+ /** Ephemeral per-run context; never appended to `config.messages`. */
301
+ private activeMemoryContext;
302
+ private buildProgress;
259
303
  constructor(config: AgentHarnessConfig);
260
304
  /**
261
305
  * Snapshot of the live transcript (`this.config.messages`) as mutated
@@ -275,6 +319,11 @@ export declare class AgentHarness {
275
319
  * @since v1.6.0
276
320
  */
277
321
  getMessages(): readonly AgentMessage[];
322
+ /** Immutable snapshot of host-observed build progress for this run. */
323
+ getBuildProgress(): Readonly<BuildProgress>;
324
+ private toolPermissions;
325
+ private isMutationTool;
326
+ private recordSuccessfulTool;
278
327
  /**
279
328
  * Member identity fields (memberId + memberName) to merge into every
280
329
  * event payload. Returns an empty object when the run is a direct
@@ -343,6 +392,9 @@ export declare class AgentHarness {
343
392
  * Callers can subscribe via eventBus AND iterate the returned iterable.
344
393
  */
345
394
  run(): AsyncIterable<BrainEvent>;
395
+ private prepareMemoryContext;
396
+ /** Build a provider-only view with memory after the stable system prefix. */
397
+ private messagesForProvider;
346
398
  /**
347
399
  * v1.36.0: capture a deterministic snapshot of the routed request just
348
400
  * before it goes out. Never throws into the request path.
@@ -1 +1 @@
1
- {"version":3,"file":"AgentHarness.d.ts","sourceRoot":"","sources":["../../src/core/AgentHarness.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentHarness.d.ts","sourceRoot":"","sources":["../../src/core/AgentHarness.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,10 @@ import { emptyContextGrowthStats, recordRequest, recordToolResult, recordUsage,
24
24
  import { createRoutedRequestSnapshot, } from './requestSnapshot.js';
25
25
  import { collapseLoopedAssistantText, detectAssistantTextLoopWindow, TEXT_LOOP_RECOVERY_SYSTEM, TEXT_LOOP_RECOVERY_USER_PROMPT, } from './textLoopDetect.js';
26
26
  export { collapseLoopedAssistantText, detectAssistantTextLoop, detectAssistantTextLoopWindow, normalizeLoopUnit, isStatusTheaterUnit, TEXT_LOOP_RECOVERY_SYSTEM, TEXT_LOOP_RECOVERY_USER_PROMPT, } from './textLoopDetect.js';
27
+ export const BUILD_LIVENESS_RECOVERY_PROMPT = '[build-liveness] The requested task requires an on-disk implementation, ' +
28
+ 'but no successful project mutation has occurred yet. Continue working. ' +
29
+ 'Inspect only as needed, then make the required change with an available ' +
30
+ 'mutating tool. Do not merely describe a patch or claim completion.';
27
31
  // --- Harness ----------------------------------------------------------------
28
32
  export class AgentHarness {
29
33
  config;
@@ -59,6 +63,15 @@ export class AgentHarness {
59
63
  textToolReentries = 0;
60
64
  /** Fase M: per-run context-growth counters (reset on every run()). */
61
65
  growth = emptyContextGrowthStats();
66
+ /** Ephemeral per-run context; never appended to `config.messages`. */
67
+ activeMemoryContext = '';
68
+ buildProgress = {
69
+ toolCalls: 0,
70
+ mutationsAttempted: 0,
71
+ mutationsSucceeded: 0,
72
+ verificationCalls: 0,
73
+ recoveries: 0,
74
+ };
62
75
  constructor(config) {
63
76
  this.config = config;
64
77
  this.eventBus = config.eventBus;
@@ -99,6 +112,37 @@ export class AgentHarness {
99
112
  getMessages() {
100
113
  return this.config.messages;
101
114
  }
115
+ /** Immutable snapshot of host-observed build progress for this run. */
116
+ getBuildProgress() {
117
+ return { ...this.buildProgress };
118
+ }
119
+ toolPermissions(toolName) {
120
+ return this.config.toolRegistry?.get(toolName)?.permissions ?? [];
121
+ }
122
+ isMutationTool(toolName, args) {
123
+ const permissions = this.toolPermissions(toolName);
124
+ if (!permissions.includes('write'))
125
+ return false;
126
+ // Generic preview contract used by built-in and third-party mutators.
127
+ if (args.dryRun === true)
128
+ return false;
129
+ // Reuse the registry's argument-aware effect classification. In
130
+ // particular, `task agent=explore|verify` is parallel-safe/read-only even
131
+ // though the shared task definition advertises the superset of permissions.
132
+ return classifyToolConcurrency({
133
+ toolName,
134
+ args,
135
+ permissions,
136
+ registered: Boolean(this.config.toolRegistry?.get(toolName)),
137
+ }) === 'exclusive';
138
+ }
139
+ recordSuccessfulTool(toolName, args) {
140
+ const permissions = this.toolPermissions(toolName);
141
+ if (this.isMutationTool(toolName, args))
142
+ this.buildProgress.mutationsSucceeded += 1;
143
+ if (permissions.includes('execute'))
144
+ this.buildProgress.verificationCalls += 1;
145
+ }
102
146
  /**
103
147
  * Member identity fields (memberId + memberName) to merge into every
104
148
  * event payload. Returns an empty object when the run is a direct
@@ -356,6 +400,14 @@ export class AgentHarness {
356
400
  this.toolCallCounts = new Map();
357
401
  this.textToolReentries = 0;
358
402
  this.growth = emptyContextGrowthStats();
403
+ this.buildProgress = {
404
+ toolCalls: 0,
405
+ mutationsAttempted: 0,
406
+ mutationsSucceeded: 0,
407
+ verificationCalls: 0,
408
+ recoveries: 0,
409
+ };
410
+ const memoryWarning = await this.prepareMemoryContext();
359
411
  // Emit agent_start
360
412
  const startEvent = createBrainEvent('agent_start', this.sessionId, {
361
413
  model: this.config.model,
@@ -364,6 +416,15 @@ export class AgentHarness {
364
416
  });
365
417
  this.emit(startEvent);
366
418
  yield startEvent;
419
+ if (memoryWarning) {
420
+ const warning = createBrainEvent('error', this.sessionId, {
421
+ severity: 'recoverable',
422
+ message: memoryWarning,
423
+ code: 'memory_recall_failed',
424
+ });
425
+ this.emit(warning);
426
+ yield warning;
427
+ }
367
428
  // === Initial turn (always runs to preserve Phase 12.x behavior) ===
368
429
  // The harness processes the messages buffer that was provided in
369
430
  // the config. Backward compat: existing callers expect a single
@@ -375,7 +436,11 @@ export class AgentHarness {
375
436
  this.emit(initialMsgStart);
376
437
  yield initialMsgStart;
377
438
  let initialTurnLength = 0;
378
- const initialFinishRef = { value: 'stop' };
439
+ const initialFinishRef = {
440
+ value: 'stop',
441
+ clarificationRequested: false,
442
+ providerError: false,
443
+ };
379
444
  // Task G.4.3 — capture real provider usage for this turn. The ref
380
445
  // is read after runSingleTurn returns and attached to message_end.
381
446
  const initialUsageRef = { value: null };
@@ -404,7 +469,7 @@ export class AgentHarness {
404
469
  });
405
470
  this.emit(initialMsgEnd);
406
471
  yield initialMsgEnd;
407
- // === Agentic tool-call loop ===
472
+ // === Agentic tool-call + bounded build-liveness recovery loop ===
408
473
  // Soft budget (maxToolLoopIterations) can auto-extend up to the hard cap
409
474
  // when the model still wants tools — multi-step work finishes instead of
410
475
  // dying mid-task with "budget exhausted". Absolute hard cap still bounds
@@ -414,19 +479,56 @@ export class AgentHarness {
414
479
  const hardCap = this.maxToolLoopHardCap;
415
480
  let extensions = 0;
416
481
  const maxExtensions = 8;
482
+ const maxBuildRecoveries = Math.max(0, Math.min(10, this.config.buildLiveness?.maxRecoveries ?? 2));
483
+ const planBuildRecovery = (turn) => {
484
+ if (!this.config.buildLiveness?.mutationRequired)
485
+ return null;
486
+ if (this.buildProgress.mutationsSucceeded > 0)
487
+ return null;
488
+ if (turn.value !== 'stop' || turn.clarificationRequested || turn.providerError)
489
+ return null;
490
+ if (this.buildProgress.recoveries >= maxBuildRecoveries)
491
+ return null;
492
+ this.buildProgress.recoveries += 1;
493
+ this.config.messages.push({ role: 'user', content: BUILD_LIVENESS_RECOVERY_PROMPT });
494
+ return {
495
+ purpose: 'build-recovery',
496
+ toolChoice: 'required',
497
+ recoveryAttempt: this.buildProgress.recoveries,
498
+ };
499
+ };
500
+ let lastTurnState = initialFinishRef;
501
+ let recoveryGeneration = planBuildRecovery(initialFinishRef);
417
502
  while (!this.cancelled &&
418
503
  !hadError &&
419
- toolLoopTurns < softCap &&
420
- initialFinishRef.value === 'tool_calls') {
504
+ toolLoopTurns < hardCap &&
505
+ (toolLoopTurns < softCap || recoveryGeneration !== null) &&
506
+ (initialFinishRef.value === 'tool_calls' || recoveryGeneration !== null)) {
421
507
  toolLoopTurns++;
508
+ if (recoveryGeneration) {
509
+ const recoveryEvent = createBrainEvent('error', this.sessionId, {
510
+ severity: 'recoverable',
511
+ message: `BUILD liveness recovery ${recoveryGeneration.recoveryAttempt}/${maxBuildRecoveries}: ` +
512
+ 'no successful mutation observed; continuing instead of completing.',
513
+ code: 'build_liveness_recovery',
514
+ });
515
+ this.emit(recoveryEvent);
516
+ yield recoveryEvent;
517
+ }
422
518
  const turnMessageId = crypto.randomUUID();
423
519
  const msgStart = createBrainEvent('message_start', this.sessionId, { messageId: turnMessageId, role: 'assistant', ...this.memberFields() });
424
520
  this.emit(msgStart);
425
521
  yield msgStart;
426
522
  let turnLength = 0;
427
- const turnFinishRef = { value: 'stop' };
523
+ const turnFinishRef = {
524
+ value: 'stop',
525
+ clarificationRequested: false,
526
+ providerError: false,
527
+ };
428
528
  const turnUsageRef = { value: null };
429
- for await (const ev of this.runSingleTurn(turnMessageId, turnFinishRef, turnUsageRef)) {
529
+ const generation = recoveryGeneration ?? undefined;
530
+ recoveryGeneration = null;
531
+ for await (const ev of this.runSingleTurn(turnMessageId, turnFinishRef, turnUsageRef, generation)) {
430
532
  if (ev.type === 'message_delta') {
431
533
  turnLength += ev.delta.length;
432
534
  }
@@ -449,6 +551,10 @@ export class AgentHarness {
449
551
  yield msgEnd;
450
552
  // Drive the loop: keep going only if this turn again requested tool calls.
451
553
  initialFinishRef.value = turnFinishRef.value;
554
+ initialFinishRef.clarificationRequested = turnFinishRef.clarificationRequested;
555
+ initialFinishRef.providerError = turnFinishRef.providerError;
556
+ lastTurnState = turnFinishRef;
557
+ recoveryGeneration = planBuildRecovery(turnFinishRef);
452
558
  if (hadError || this.cancelled)
453
559
  break;
454
560
  // Soft-cap hit but model still wants tools → extend until hard cap.
@@ -486,6 +592,27 @@ export class AgentHarness {
486
592
  hitHardCap) {
487
593
  yield* this.runFinalAnswerTurn();
488
594
  }
595
+ // A mutate/build task may never report successful completion without a
596
+ // successful write-permission tool result. Recovery is bounded above, so
597
+ // an uncooperative provider fails explicitly instead of looping forever.
598
+ if (!this.cancelled &&
599
+ !hadError &&
600
+ this.config.buildLiveness?.mutationRequired &&
601
+ this.buildProgress.mutationsSucceeded === 0 &&
602
+ !lastTurnState.clarificationRequested) {
603
+ hadError = true;
604
+ const providerFailed = lastTurnState.providerError;
605
+ const stalled = createBrainEvent('error', this.sessionId, {
606
+ severity: 'fatal',
607
+ message: providerFailed
608
+ ? 'BUILD provider call failed before any successful on-disk mutation was observed.'
609
+ : `BUILD stalled after ${this.buildProgress.recoveries} recovery turn(s): ` +
610
+ 'the task required an on-disk mutation, but no successful mutation was observed.',
611
+ code: providerFailed ? 'build_liveness_provider_error' : 'build_liveness_stalled',
612
+ });
613
+ this.emit(stalled);
614
+ yield stalled;
615
+ }
489
616
  // === Queue drain (Task 18.1) ===
490
617
  // After the initial turn, drain queued user prompts up to
491
618
  // maxQueuedIterations. Each iteration appends the next dequeued
@@ -495,6 +622,8 @@ export class AgentHarness {
495
622
  while (turns < this.maxQueuedIterations) {
496
623
  if (this.cancelled)
497
624
  break;
625
+ if (hadError)
626
+ break;
498
627
  if (this.queue.length === 0)
499
628
  break;
500
629
  const queuedPrompt = this.dequeueNext();
@@ -511,7 +640,11 @@ export class AgentHarness {
511
640
  this.emit(msgStart);
512
641
  yield msgStart;
513
642
  let turnLength = 0;
514
- const turnFinishRef = { value: 'stop' };
643
+ const turnFinishRef = {
644
+ value: 'stop',
645
+ clarificationRequested: false,
646
+ providerError: false,
647
+ };
515
648
  const turnUsageRef = { value: null };
516
649
  for await (const ev of this.runSingleTurn(turnMessageId, turnFinishRef, turnUsageRef)) {
517
650
  if (ev.type === 'message_delta') {
@@ -551,17 +684,68 @@ export class AgentHarness {
551
684
  this.emit(agentEnd);
552
685
  yield agentEnd;
553
686
  this.activeController = null;
687
+ this.activeMemoryContext = '';
688
+ }
689
+ async prepareMemoryContext() {
690
+ this.activeMemoryContext = '';
691
+ const memory = this.config.memoryService;
692
+ if (!memory)
693
+ return null;
694
+ const latestUser = [...this.config.messages]
695
+ .reverse()
696
+ .find((message) => message.role === 'user')?.content;
697
+ const query = (this.config.memoryQuery ?? latestUser ?? '').trim();
698
+ if (!query)
699
+ return null;
700
+ try {
701
+ const context = await memory.buildContext({
702
+ text: query,
703
+ useGraph: true,
704
+ maxChars: this.config.memoryContextChars ?? 2_000,
705
+ maxMemories: 8,
706
+ });
707
+ this.activeMemoryContext = context.text;
708
+ return null;
709
+ }
710
+ catch (error) {
711
+ return `[memory] recall failed; continuing without memory (${error instanceof Error ? error.message : String(error)}).`;
712
+ }
713
+ }
714
+ /** Build a provider-only view with memory after the stable system prefix. */
715
+ messagesForProvider() {
716
+ let messages = this.config.messages;
717
+ let prefixEnd = 0;
718
+ if (this.activeMemoryContext) {
719
+ while (prefixEnd < messages.length && messages[prefixEnd]?.role === 'system') {
720
+ prefixEnd += 1;
721
+ }
722
+ messages = [
723
+ ...messages.slice(0, prefixEnd),
724
+ { role: 'system', content: this.activeMemoryContext },
725
+ ...messages.slice(prefixEnd),
726
+ ];
727
+ }
728
+ if (!this.config.requestTail)
729
+ return messages;
730
+ try {
731
+ const tail = this.config.requestTail();
732
+ return tail.length > 0 ? [...messages, ...tail] : messages;
733
+ }
734
+ catch {
735
+ // Runtime status is advisory — never fail a provider request for it.
736
+ return messages;
737
+ }
554
738
  }
555
739
  /**
556
740
  * v1.36.0: capture a deterministic snapshot of the routed request just
557
741
  * before it goes out. Never throws into the request path.
558
742
  */
559
- emitSnapshot(tools, generation) {
743
+ emitSnapshot(tools, generation, messages = this.messagesForProvider()) {
560
744
  if (!this.config.onRequestSnapshot)
561
745
  return;
562
746
  try {
563
747
  this.config.onRequestSnapshot(createRoutedRequestSnapshot({
564
- messages: this.config.messages,
748
+ messages,
565
749
  model: this.config.model,
566
750
  provider: this.config.provider,
567
751
  tools,
@@ -586,16 +770,19 @@ export class AgentHarness {
586
770
  * Extracted from the original monolithic `run()` body to enable
587
771
  * the queue-draining loop in Task 18.1.
588
772
  */
589
- async *runSingleTurn(messageId, finishRef, usageRef) {
773
+ async *runSingleTurn(messageId, finishRef, usageRef, generation) {
590
774
  try {
591
- this.emitSnapshot(this.config.tools);
592
- recordRequest(this.growth, this.config.messages);
775
+ const requestMessages = this.messagesForProvider();
776
+ this.emitSnapshot(this.config.tools, generation, requestMessages);
777
+ recordRequest(this.growth, requestMessages);
593
778
  const stream = this.config.providerStream({
594
- messages: this.config.messages,
779
+ messages: requestMessages,
595
780
  model: this.config.model,
596
781
  provider: this.config.provider,
597
782
  tools: this.config.tools,
598
783
  signal: this.activeController?.signal,
784
+ conversationId: this.sessionId,
785
+ generation,
599
786
  });
600
787
  // Per-turn tool call counter (Task G.2). Reset on every turn so
601
788
  // queue-drained turns don't accumulate across prompts.
@@ -698,6 +885,10 @@ export class AgentHarness {
698
885
  }
699
886
  else if (delta.kind === 'tool_call') {
700
887
  toolCallsThisTurn++;
888
+ this.buildProgress.toolCalls += 1;
889
+ if (this.isMutationTool(delta.toolName, delta.args)) {
890
+ this.buildProgress.mutationsAttempted += 1;
891
+ }
701
892
  // Record this tool call so the assistant message (appended after the
702
893
  // turn) carries the full tool_calls list the model emitted.
703
894
  turnToolCalls.push({ id: delta.toolCallId, name: delta.toolName, args: delta.args });
@@ -762,6 +953,12 @@ export class AgentHarness {
762
953
  this.toolCallCache.set(item.cacheKey, item.content);
763
954
  }
764
955
  }
956
+ for (let i = 0; i < executed.length; i++) {
957
+ const item = executed[i];
958
+ const pending = pendingNativeTools[i];
959
+ if (!item.isError)
960
+ this.recordSuccessfulTool(pending.toolName, pending.args);
961
+ }
765
962
  pendingNativeTools.length = 0;
766
963
  }
767
964
  // Clarification pause: if the model posed a ---QUESTION--- with choices,
@@ -771,6 +968,7 @@ export class AgentHarness {
771
968
  /"choices"\s*:\s*\[/.test(turnText);
772
969
  if (clarificationPause) {
773
970
  finishRef.value = 'stop';
971
+ finishRef.clarificationRequested = true;
774
972
  }
775
973
  // Fallback text-format tools: ---TOOLS--- JSON, MiniMax invoke XML,
776
974
  // and garbled invoke dumps. Run any text tools not already executed
@@ -812,6 +1010,10 @@ export class AgentHarness {
812
1010
  if (typeof maxToolCalls === 'number' && toolCallsThisTurn + 1 > maxToolCalls)
813
1011
  break;
814
1012
  toolCallsThisTurn++;
1013
+ this.buildProgress.toolCalls += 1;
1014
+ if (this.isMutationTool(tt.name, tt.args)) {
1015
+ this.buildProgress.mutationsAttempted += 1;
1016
+ }
815
1017
  const toolCallId = `text-${crypto.randomUUID().slice(0, 8)}`;
816
1018
  turnToolCalls.push({ id: toolCallId, name: tt.name, args: tt.args });
817
1019
  const startEv = createBrainEvent('tool_execution_start', this.sessionId, {
@@ -885,6 +1087,8 @@ export class AgentHarness {
885
1087
  this.emit(endEv);
886
1088
  yield endEv;
887
1089
  turnToolResults.push({ toolCallId, content: resultStr });
1090
+ if (!isError)
1091
+ this.recordSuccessfulTool(tt.name, tt.args);
888
1092
  executedAny = true;
889
1093
  }
890
1094
  if (executedAny) {
@@ -957,6 +1161,7 @@ export class AgentHarness {
957
1161
  break;
958
1162
  }
959
1163
  else if (delta.kind === 'error') {
1164
+ finishRef.providerError = true;
960
1165
  const errEvent = createBrainEvent('error', this.sessionId, {
961
1166
  severity: 'recoverable',
962
1167
  message: delta.message,
@@ -977,6 +1182,7 @@ export class AgentHarness {
977
1182
  }
978
1183
  }
979
1184
  catch (err) {
1185
+ finishRef.providerError = true;
980
1186
  const errorMessage = err instanceof Error ? err.message : String(err);
981
1187
  const errEvent = createBrainEvent('error', this.sessionId, {
982
1188
  severity: 'recoverable',
@@ -1024,21 +1230,27 @@ export class AgentHarness {
1024
1230
  content: '[system] Hard tool-iteration ceiling reached. Stop calling tools and give a clear status: what is DONE, what remains, and the exact next steps. Use what you already gathered. Do not apologize for the tools.',
1025
1231
  });
1026
1232
  let totalLength = 0;
1027
- const finishRef = { value: 'stop' };
1233
+ const finishRef = {
1234
+ value: 'stop',
1235
+ clarificationRequested: false,
1236
+ providerError: false,
1237
+ };
1028
1238
  const usageRef = { value: null };
1029
1239
  // Temporarily drop tools for this call by building a no-tools provider
1030
1240
  // invocation. We re-enter runSingleTurn but with an empty tools list via
1031
1241
  // a throwaway config override is not possible (config is readonly), so we
1032
1242
  // call the providerStream directly with tools: [].
1033
- recordRequest(this.growth, this.config.messages);
1243
+ const requestMessages = this.messagesForProvider();
1244
+ recordRequest(this.growth, requestMessages);
1034
1245
  try {
1035
- this.emitSnapshot([]);
1246
+ this.emitSnapshot([], undefined, requestMessages);
1036
1247
  const stream = this.config.providerStream({
1037
- messages: this.config.messages,
1248
+ messages: requestMessages,
1038
1249
  model: this.config.model,
1039
1250
  provider: this.config.provider,
1040
1251
  tools: [],
1041
1252
  signal: this.activeController?.signal,
1253
+ conversationId: this.sessionId,
1042
1254
  });
1043
1255
  for await (const delta of stream) {
1044
1256
  if (this.cancelled)