loopforge 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/backends/interface.d.ts +4 -3
  2. package/dist/backends/interface.d.ts.map +1 -1
  3. package/dist/backends/interface.js +4 -1
  4. package/dist/backends/interface.js.map +1 -1
  5. package/dist/cli.js +1 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/engine.d.ts +2 -24
  8. package/dist/engine.d.ts.map +1 -1
  9. package/dist/engine.js +72 -238
  10. package/dist/engine.js.map +1 -1
  11. package/dist/evidence-provider.d.ts +38 -5
  12. package/dist/evidence-provider.d.ts.map +1 -1
  13. package/dist/evidence-provider.js +131 -38
  14. package/dist/evidence-provider.js.map +1 -1
  15. package/dist/index.d.ts +3 -5
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1 -4
  18. package/dist/index.js.map +1 -1
  19. package/dist/interop.d.ts +5 -0
  20. package/dist/interop.d.ts.map +1 -1
  21. package/dist/interop.js +5 -0
  22. package/dist/interop.js.map +1 -1
  23. package/dist/loop-compiler.d.ts.map +1 -1
  24. package/dist/loop-compiler.js +4 -2
  25. package/dist/loop-compiler.js.map +1 -1
  26. package/dist/loop-extras-parser.d.ts +82 -0
  27. package/dist/loop-extras-parser.d.ts.map +1 -0
  28. package/dist/loop-extras-parser.js +217 -0
  29. package/dist/loop-extras-parser.js.map +1 -0
  30. package/dist/loop-store.d.ts +26 -4
  31. package/dist/loop-store.d.ts.map +1 -1
  32. package/dist/loop-store.js +96 -7
  33. package/dist/loop-store.js.map +1 -1
  34. package/dist/mcp/server.js +1 -1
  35. package/dist/mcp/session.d.ts +27 -2
  36. package/dist/mcp/session.d.ts.map +1 -1
  37. package/dist/mcp/session.js +166 -117
  38. package/dist/mcp/session.js.map +1 -1
  39. package/dist/mcp/tools.d.ts.map +1 -1
  40. package/dist/mcp/tools.js +1 -0
  41. package/dist/mcp/tools.js.map +1 -1
  42. package/dist/policy.d.ts +0 -8
  43. package/dist/policy.d.ts.map +1 -1
  44. package/dist/policy.js +1 -9
  45. package/dist/policy.js.map +1 -1
  46. package/dist/prompt-assembler.d.ts.map +1 -1
  47. package/dist/prompt-assembler.js +25 -4
  48. package/dist/prompt-assembler.js.map +1 -1
  49. package/dist/protocol.d.ts +2 -74
  50. package/dist/protocol.d.ts.map +1 -1
  51. package/dist/protocol.js +0 -26
  52. package/dist/protocol.js.map +1 -1
  53. package/dist/round-coordinator.d.ts +4 -4
  54. package/dist/round-coordinator.js +4 -4
  55. package/dist/self-eval.d.ts +26 -0
  56. package/dist/self-eval.d.ts.map +1 -0
  57. package/dist/self-eval.js +154 -0
  58. package/dist/self-eval.js.map +1 -0
  59. package/dist/storage.d.ts +3 -2
  60. package/dist/storage.d.ts.map +1 -1
  61. package/dist/storage.js +57 -65
  62. package/dist/storage.js.map +1 -1
  63. package/dist/verification-gate.d.ts +16 -18
  64. package/dist/verification-gate.d.ts.map +1 -1
  65. package/dist/verification-gate.js +149 -94
  66. package/dist/verification-gate.js.map +1 -1
  67. package/loop_policy.json +1 -9
  68. package/package.json +1 -1
  69. package/dist/runtime.d.ts +0 -94
  70. package/dist/runtime.d.ts.map +0 -1
  71. package/dist/runtime.js +0 -718
  72. package/dist/runtime.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/mcp/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/mcp/session.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ import { checkLoopHealth } from "../loop-compiler.js";
10
10
  import { getPolicy } from "../policy.js";
11
11
  import { Mode, makeLoopCompileRequest } from "../protocol.js";
12
12
  import { ReplayBackend } from "../replay.js";
13
- import { FileLoopStore, LoopStoreBackend } from "../loop-store.js";
13
+ import { FileLoopStore, LoopStoreBackend, VaultBackendLoopStore } from "../loop-store.js";
14
14
  import { EvidenceCollector } from "../evidence-provider.js";
15
15
  import { parseRoundTransactionSnapshot, prepareRoundTransaction, } from "../round-transaction.js";
16
16
  import { RoundDriver } from "../round-driver.js";
@@ -19,8 +19,7 @@ import { policyMetrics } from "../policy-metrics.js";
19
19
  import { SessionLeaseConflictError, VaultSessionStateStore, } from "../storage.js";
20
20
  import { createCognitiveCheckpoint } from "../interop.js";
21
21
  // ── Helpers ────────────────────────────────────────────────────────────────
22
- function buildLoopRequest(session, lastEval, _lastQuality, // deprecated — kept for backward compat
23
- verificationFlags) {
22
+ function buildLoopRequest(session, lastEval, verificationFlags) {
24
23
  const req = {
25
24
  task: session.task,
26
25
  mode: Mode.LOOP_COMPILE,
@@ -96,13 +95,16 @@ export class SessionManager {
96
95
  contextProvider;
97
96
  terminalSinks = new Set();
98
97
  constructor(storeOrBackend, sessionStore) {
99
- const resolvedBackend = storeOrBackend
100
- ? "readSession" in storeOrBackend
101
- ? new LoopStoreBackend(storeOrBackend)
102
- : storeOrBackend
103
- : new LoopStoreBackend(new FileLoopStore(getPolicy().backend.root_dir));
104
- this.backend = resolvedBackend;
105
- this.sessionStore = sessionStore ?? new VaultSessionStateStore(resolvedBackend);
98
+ const isLoopStore = (v) => "readSession" in v && typeof v.readSession === "function";
99
+ const store = storeOrBackend
100
+ ? isLoopStore(storeOrBackend)
101
+ ? storeOrBackend
102
+ : new VaultBackendLoopStore(storeOrBackend)
103
+ : new FileLoopStore(getPolicy().backend.root_dir);
104
+ this.backend = storeOrBackend && !isLoopStore(storeOrBackend)
105
+ ? storeOrBackend
106
+ : new LoopStoreBackend(store);
107
+ this.sessionStore = sessionStore ?? new VaultSessionStateStore(store);
106
108
  const mcpPolicy = getPolicy().mcp;
107
109
  this.leaseMs = Math.max(1, mcpPolicy.session_lease_ms);
108
110
  this.leaseRenewIntervalMs = Math.max(1, Math.min(mcpPolicy.session_lease_renew_interval_ms, this.leaseMs));
@@ -165,6 +167,7 @@ export class SessionManager {
165
167
  round: typeof lineage.current_round === "number" ? lineage.current_round : 0,
166
168
  prompt: null,
167
169
  stopReason: `session_owned_elsewhere:${loopId}`,
170
+ stopDetail: `Another process (PID ${entry?.loop_lineage ? entry.loop_lineage.lease_owner ?? "unknown" : "unknown"}) holds the lease for loop "${loopId}". Wait for the lease to expire or stop the other process.`,
168
171
  };
169
172
  }
170
173
  async withSessionQueue(sessionId, work) {
@@ -190,7 +193,7 @@ export class SessionManager {
190
193
  const sessionId = randomUUID();
191
194
  const loopId = input.loopId ?? randomUUID();
192
195
  const engine = new LoopForgeEngine(this.backend);
193
- const maxRounds = input.maxRounds ?? getPolicy().runtime.max_rounds;
196
+ const maxRounds = input.maxRounds ?? getPolicy().engine.max_rounds;
194
197
  // Populate extra fields for the first round
195
198
  const request = buildLoopRequest({
196
199
  sessionId, loopId, task: input.task, engine, currentRound: 1,
@@ -258,7 +261,6 @@ export class SessionManager {
258
261
  prompt: initialPrompt,
259
262
  level: initialLevel,
260
263
  roundSuccess: false,
261
- quality: 0,
262
264
  warnings: parseWarnings(initialPrompt),
263
265
  };
264
266
  }
@@ -290,7 +292,7 @@ export class SessionManager {
290
292
  if (!session)
291
293
  return false;
292
294
  session.status = "stopped";
293
- this.save(session); // Persist stopped status to vault so autoResumeAll won't resurrect
295
+ this.save(session);
294
296
  void this.notifyTerminal(session, "cancelled");
295
297
  this.sessions.delete(sessionId);
296
298
  logEvent("session_end", {
@@ -329,7 +331,6 @@ export class SessionManager {
329
331
  roundId: session.roundSnapshot?.roundId,
330
332
  prompt: session.currentPrompt,
331
333
  level: session.currentLevel ?? "l2",
332
- quality: 0,
333
334
  roundSuccess: undefined,
334
335
  warnings: parseWarnings(session.currentPrompt),
335
336
  };
@@ -377,14 +378,18 @@ export class SessionManager {
377
378
  roundId: recovered.snapshot.roundId,
378
379
  prompt: null,
379
380
  stopReason: reason,
381
+ stopDetail: reason === "completed"
382
+ ? "Task completed successfully — the agent declared should_continue=false with success=true."
383
+ : reason === "failed"
384
+ ? "Agent declared should_continue=false with success=false — the task was not achieved."
385
+ : `Loop stopped: ${reason}.`,
380
386
  roundSuccess: pr.roundSuccess,
381
- quality: pr.roundSuccess ? 5 : 1,
382
387
  };
383
388
  }
384
389
  if (pr.action !== "continue")
385
390
  return null;
386
391
  session.currentRound++;
387
- const request = buildLoopRequest(session, pr.newLastSelfEval, pr.roundSuccess ? 5 : 1, pr.verificationFlags);
392
+ const request = buildLoopRequest(session, pr.newLastSelfEval, pr.verificationFlags);
388
393
  const prepared = new RoundDriver(session.engine, this.backend).prepareSync(request, session.loopId, session.currentRound);
389
394
  if (!prepared) {
390
395
  session.status = "stalled";
@@ -394,6 +399,7 @@ export class SessionManager {
394
399
  round: session.currentRound,
395
400
  prompt: null,
396
401
  stopReason: "stalled",
402
+ stopDetail: "RoundDriver.prepareSync returned null — prompt compilation failed during crash recovery. The session state may be inconsistent.",
397
403
  };
398
404
  }
399
405
  const prompt = prepared.prompt;
@@ -410,7 +416,6 @@ export class SessionManager {
410
416
  prompt,
411
417
  level: session.currentLevel,
412
418
  roundSuccess: pr.roundSuccess,
413
- quality: pr.roundSuccess ? 5 : 1,
414
419
  warnings: parseWarnings(prompt),
415
420
  };
416
421
  }
@@ -490,7 +495,7 @@ export class SessionManager {
490
495
  session.status = "stopped";
491
496
  this.save(session);
492
497
  void this.notifyTerminal(session, "stalled");
493
- return { sessionId: session.sessionId, round: session.currentRound, prompt: null, stopReason: "stalled" };
498
+ return { sessionId: session.sessionId, round: session.currentRound, prompt: null, stopReason: "stalled", stopDetail: "RoundDriver.prepare returned null during unpause — the compiler could not produce a prompt." };
494
499
  }
495
500
  const prompt = prepared.prompt;
496
501
  const level = prepared.level;
@@ -506,7 +511,6 @@ export class SessionManager {
506
511
  roundId: session.roundSnapshot.roundId,
507
512
  prompt,
508
513
  level,
509
- quality: 0,
510
514
  roundSuccess: undefined,
511
515
  warnings: [],
512
516
  };
@@ -575,7 +579,7 @@ export class SessionManager {
575
579
  const currentRound = lineage.current_round ?? 1;
576
580
  const successTrajectory = lineage.success_trajectory ?? lineage.quality_trajectory ?? [];
577
581
  const task = entry.task ?? "";
578
- const maxRounds = lineage.max_rounds ?? getPolicy().runtime.max_rounds;
582
+ const maxRounds = lineage.max_rounds ?? getPolicy().engine.max_rounds;
579
583
  const roundSnapshot = parseRoundTransactionSnapshot(lineage.round_snapshot);
580
584
  const fallbackEvidence = EvidenceCollector.fromProviderNames(getPolicy().evidence.providers).collect();
581
585
  const persistedEval = lineage.last_self_eval;
@@ -635,6 +639,7 @@ export class SessionManager {
635
639
  round: currentRound,
636
640
  prompt: null,
637
641
  stopReason: status,
642
+ stopDetail: `Loop is not running (status: ${status}). It may have already completed or been stopped.`,
638
643
  };
639
644
  }
640
645
  this.sessions.set(session.sessionId, session);
@@ -661,7 +666,6 @@ export class SessionManager {
661
666
  prompt,
662
667
  level: session.currentLevel,
663
668
  roundSuccess: false,
664
- quality: 0,
665
669
  warnings: parseWarnings(prompt),
666
670
  };
667
671
  }
@@ -802,34 +806,30 @@ export class SessionManager {
802
806
  }
803
807
  });
804
808
  }
805
- async advanceUnlocked(sessionId, output, preExtractedEval) {
806
- const session = this.sessions.get(sessionId);
807
- if (!session)
808
- return { sessionId, round: 0, prompt: null, stopReason: "session_not_found" };
809
- if (session.status !== "running") {
810
- return { sessionId, round: session.currentRound, prompt: null, stopReason: session.status };
811
- }
812
- // 1. Extract self-evaluation (structured param preferred → regex → heuristic)
813
- let extractionFailed = false;
814
- let selfEval;
809
+ // ── advanceUnlocked pipeline ───────────────────────────────────────────
810
+ //
811
+ // The method is split into focused private helpers so each phase of the
812
+ // round-boundary decision loop is independently readable and testable.
813
+ //
814
+ // Pipeline: validate → extract → execute transaction → route disposition
815
+ // → advance to next round (continue) or return terminal result.
816
+ /** Extract a SelfEvaluation from agent output.
817
+ * Structured param preferred → regex extraction → heuristic fallback.
818
+ * Returns null only when both regex and heuristic returned null. */
819
+ extractEvaluation(output, preExtractedEval) {
815
820
  if (preExtractedEval) {
816
- selfEval = preExtractedEval;
817
- extractionFailed = false;
818
- }
819
- else {
820
- const structured = extractSelfEvaluation(output);
821
- extractionFailed = structured === null;
822
- selfEval = structured ?? heuristicSelfEvaluation(output);
821
+ return { selfEval: preExtractedEval, extractionFailed: false };
823
822
  }
824
- // Guard: if both extraction methods returned null, stop
825
- if (!selfEval) {
826
- session.status = "stalled";
827
- this.save(session);
828
- void this.notifyTerminal(session, "stalled");
829
- logEvent("session_end", { sessionId, loopId: session.loopId, stopReason: "stalled", round: session.currentRound });
830
- return { sessionId, round: session.currentRound, prompt: null, stopReason: "stalled", roundSuccess: false, quality: 0 };
831
- }
832
- // 1.5. Unified transaction: before → evaluate → verify → commit/reject.
823
+ const structured = extractSelfEvaluation(output);
824
+ const extractionFailed = structured === null;
825
+ const selfEval = structured ?? heuristicSelfEvaluation(output);
826
+ return selfEval ? { selfEval, extractionFailed } : null;
827
+ }
828
+ /** Execute the round transaction and apply per-rule rejection tracking.
829
+ * MUTATES: session.roundSnapshot, session.consecutiveRejections,
830
+ * session.lastRejectionCheck, session.lastSelfEval,
831
+ * session.successTrajectory */
832
+ async executeRoundTransaction(session, selfEval, extractionFailed) {
833
833
  const snapshot = session.roundSnapshot ?? prepareRoundTransaction(session.loopId, session.currentRound, session.evidenceBaseline ?? []);
834
834
  const completed = await new RoundDriver(session.engine, this.backend).complete({
835
835
  snapshot,
@@ -843,13 +843,12 @@ export class SessionManager {
843
843
  successTrajectory: session.successTrajectory,
844
844
  });
845
845
  const outcome = completed.outcome;
846
- const actualSnapshots = completed.actualEvidence;
846
+ const actualEvidence = completed.actualEvidence;
847
847
  session.roundSnapshot = outcome.snapshot;
848
848
  const pr = outcome.result;
849
849
  policyMetrics.recordStrategyOutcome(session.loopId, session.currentLevel, pr, outcome.replayed);
850
- const verificationFlags = pr.verificationFlags;
851
- // Track per-rule rejections: only same-check rejections
852
- // accumulate. A different rejection reason resets the counter.
850
+ // Per-rule rejection tracking: same-check rejections accumulate;
851
+ // a different rejection reason resets the counter.
853
852
  if (pr.action === "reject" && pr.rejectionCheck) {
854
853
  session.consecutiveRejections =
855
854
  pr.rejectionCheck === session.lastRejectionCheck
@@ -867,75 +866,92 @@ export class SessionManager {
867
866
  if (pr.shouldPushSuccessTrajectory) {
868
867
  session.successTrajectory.push(pr.roundSuccess);
869
868
  }
870
- // Handle enforcement actions
871
- if (pr.action === "reject") {
872
- const retryRequest = buildLoopRequest(session, undefined, undefined, verificationFlags);
873
- const preparedRetry = await new RoundDriver(session.engine, this.backend).prepareRetry(retryRequest, session.roundSnapshot, pr.rejectionPrompt ?? "", session.consecutiveRejections);
874
- if (!preparedRetry) {
875
- session.status = "stalled";
876
- session.currentPrompt = null;
877
- this.save(session);
878
- return {
879
- sessionId,
880
- round: session.currentRound,
881
- roundId: session.roundSnapshot.roundId,
882
- prompt: null,
883
- stopReason: "stalled",
884
- };
885
- }
886
- session.roundSnapshot = preparedRetry.snapshot;
887
- session.currentPrompt = preparedRetry.prompt;
888
- session.currentLevel = preparedRetry.level;
889
- this.save(session);
890
- return {
891
- sessionId,
892
- round: session.currentRound,
893
- roundId: session.roundSnapshot.roundId,
894
- prompt: preparedRetry.prompt,
895
- level: preparedRetry.level,
896
- enforcementAction: "reject",
897
- enforcementReason: pr.enforcementReason,
898
- };
899
- }
900
- // Accepted rounds advance the before-snapshot. Rejected rounds retain the
901
- // original baseline so their retry is still a zero-commit transaction.
902
- session.evidenceBaseline = actualSnapshots;
903
- if (pr.action === "terminate") {
904
- session.status = "stopped";
869
+ return { pr, verificationFlags: pr.verificationFlags, actualEvidence };
870
+ }
871
+ /** Build a rejection result: compile a retry prompt, persist, return.
872
+ * MUTATES: session.roundSnapshot, session.currentPrompt, session.currentLevel */
873
+ async buildRejectionResult(sessionId, session, pr, verificationFlags) {
874
+ const retryRequest = buildLoopRequest(session, undefined, verificationFlags);
875
+ const preparedRetry = await new RoundDriver(session.engine, this.backend).prepareRetry(retryRequest, session.roundSnapshot, pr.rejectionPrompt ?? "", session.consecutiveRejections);
876
+ if (!preparedRetry) {
877
+ session.status = "stalled";
905
878
  session.currentPrompt = null;
906
879
  this.save(session);
907
- void this.notifyTerminal(session, "enforcement_terminated");
908
- logEvent("session_end", {
909
- sessionId, loopId: session.loopId,
910
- stopReason: "enforcement_terminated", round: session.currentRound,
911
- });
912
880
  return {
913
881
  sessionId,
914
882
  round: session.currentRound,
915
- roundId: session.roundSnapshot.roundId,
883
+ roundId: session.roundSnapshot?.roundId,
916
884
  prompt: null,
917
- stopReason: "enforcement_terminated",
918
- enforcementAction: "terminate",
919
- enforcementReason: pr.enforcementReason,
885
+ stopReason: "stalled",
886
+ stopDetail: "RoundDriver.prepareRetry returned null — the retry prompt could not be compiled.",
920
887
  };
921
888
  }
922
- if (pr.action === "stop") {
923
- const reason = pr.stopReason ?? "stalled";
924
- session.status = reason === "stalled" ? "stalled" : "stopped";
925
- session.currentPrompt = null;
926
- this.save(session);
927
- void this.notifyTerminal(session, reason);
928
- logEvent("session_end", { sessionId, loopId: session.loopId, stopReason: reason, round: session.currentRound });
929
- return { sessionId, round: session.currentRound, roundId: session.roundSnapshot.roundId, prompt: null, stopReason: reason, roundSuccess: pr.roundSuccess, quality: pr.roundSuccess ? 5 : 1 };
930
- }
889
+ session.roundSnapshot = preparedRetry.snapshot;
890
+ session.currentPrompt = preparedRetry.prompt;
891
+ session.currentLevel = preparedRetry.level;
892
+ this.save(session);
893
+ return {
894
+ sessionId,
895
+ round: session.currentRound,
896
+ roundId: session.roundSnapshot.roundId,
897
+ prompt: preparedRetry.prompt,
898
+ level: preparedRetry.level,
899
+ enforcementAction: "reject",
900
+ enforcementReason: pr.enforcementReason,
901
+ };
902
+ }
903
+ /** Build a termination result: persist stopped status, notify sinks.
904
+ * MUTATES: session.status, session.currentPrompt */
905
+ buildTerminationResult(sessionId, session, pr) {
906
+ session.status = "stopped";
907
+ session.currentPrompt = null;
908
+ this.save(session);
909
+ void this.notifyTerminal(session, "enforcement_terminated");
910
+ logEvent("session_end", {
911
+ sessionId, loopId: session.loopId,
912
+ stopReason: "enforcement_terminated", round: session.currentRound,
913
+ });
914
+ return {
915
+ sessionId,
916
+ round: session.currentRound,
917
+ roundId: session.roundSnapshot?.roundId,
918
+ prompt: null,
919
+ stopReason: "enforcement_terminated",
920
+ stopDetail: pr.enforcementReason ?? "The enforcement gate terminated the loop.",
921
+ enforcementAction: "terminate",
922
+ enforcementReason: pr.enforcementReason,
923
+ };
924
+ }
925
+ /** Build a stop result: persist stopped/stalled status, notify sinks.
926
+ * MUTATES: session.status, session.currentPrompt */
927
+ buildStopResult(sessionId, session, pr) {
928
+ const reason = pr.stopReason ?? "stalled";
929
+ session.status = reason === "stalled" ? "stalled" : "stopped";
930
+ session.currentPrompt = null;
931
+ this.save(session);
932
+ void this.notifyTerminal(session, reason);
933
+ logEvent("session_end", { sessionId, loopId: session.loopId, stopReason: reason, round: session.currentRound });
934
+ return {
935
+ sessionId, round: session.currentRound,
936
+ roundId: session.roundSnapshot?.roundId,
937
+ prompt: null, stopReason: reason,
938
+ stopDetail: pr.stopReason === "circuit_breaker"
939
+ ? `${pr.roundSuccess ? "No" : "All"} recent rounds failed, triggering the circuit breaker.`
940
+ : pr.stopReason === "max_rounds"
941
+ ? "The configured maximum number of rounds has been reached."
942
+ : `Loop stopped: ${reason}.`,
943
+ roundSuccess: pr.roundSuccess,
944
+ };
945
+ }
946
+ /** Compile the next round's prompt and advance the session.
947
+ * Includes the commit fence (pause/delete race guard) and context provider.
948
+ * MUTATES: session.currentRound, session.currentPrompt, session.currentLevel,
949
+ * session.evidenceBaseline, session.roundSnapshot */
950
+ async advanceToNextRound(sessionId, session, selfEval, pr, verificationFlags, actualEvidence) {
931
951
  const roundSuccess = pr.roundSuccess;
932
- // Feedback + decision metadata were committed by the transaction layer.
933
- // Build deprecated quality alias for backward compat
934
- const deprecatedQuality = roundSuccess ? 5 : 1;
935
- // 4. Compile next round after the accepted commit.
936
952
  session.currentRound++;
937
953
  session.currentPrompt = null;
938
- // v1.8: Memory injection for phases 2/3 — tier-aware
954
+ // v1.8: Memory injection — tier-aware external context.
939
955
  let externalCtx = "";
940
956
  if (this.contextProvider) {
941
957
  try {
@@ -954,28 +970,26 @@ export class SessionManager {
954
970
  });
955
971
  }
956
972
  }
957
- // pause()/delete() are intentionally synchronous for API compatibility.
958
- // They may run while the memory provider above is awaited. Treat the
959
- // session map + status as a commit fence so an in-flight advance cannot
960
- // compile/save another round after a terminal lifecycle transition.
973
+ // Commit fence: pause()/delete() may have run while the context provider
974
+ // was awaited. Do not compile if the session is no longer running.
961
975
  if (this.sessions.get(sessionId) !== session || session.status !== "running") {
962
976
  return {
963
977
  sessionId,
964
978
  round: session.currentRound,
965
979
  prompt: null,
966
980
  stopReason: session.status,
981
+ stopDetail: `Session is no longer running (status: ${session.status}). It was paused or deleted while the context provider was running.`,
967
982
  roundSuccess,
968
- quality: deprecatedQuality,
969
983
  };
970
984
  }
971
- const request = buildLoopRequest(session, selfEval, deprecatedQuality, verificationFlags);
985
+ const request = buildLoopRequest(session, selfEval, verificationFlags);
972
986
  if (externalCtx) {
973
987
  request.external_context = externalCtx;
974
988
  }
975
989
  const prepared = await new RoundDriver(session.engine, this.backend).prepare(request, session.loopId, session.currentRound);
976
990
  const nextPrompt = prepared?.prompt ?? null;
977
991
  const nextLevel = prepared?.level ?? "l2";
978
- const nextBaseline = prepared?.evidenceBaseline ?? actualSnapshots;
992
+ const nextBaseline = prepared?.evidenceBaseline ?? actualEvidence;
979
993
  session.evidenceBaseline = nextBaseline;
980
994
  session.roundSnapshot = prepared?.snapshot ?? prepareRoundTransaction(session.loopId, session.currentRound, nextBaseline);
981
995
  session.currentPrompt = nextPrompt;
@@ -989,10 +1003,45 @@ export class SessionManager {
989
1003
  prompt: nextPrompt,
990
1004
  level: nextLevel,
991
1005
  roundSuccess,
992
- quality: deprecatedQuality,
993
1006
  warnings: parseWarnings(nextPrompt),
994
1007
  };
995
1008
  }
1009
+ async advanceUnlocked(sessionId, output, preExtractedEval) {
1010
+ // ── 1. Validate session ──────────────────────────────────────────────
1011
+ const session = this.sessions.get(sessionId);
1012
+ if (!session)
1013
+ return { sessionId, round: 0, prompt: null, stopReason: "session_not_found", stopDetail: "No session exists with this ID. It may have expired, been deleted, or never existed." };
1014
+ if (session.status !== "running") {
1015
+ return { sessionId, round: session.currentRound, prompt: null, stopReason: session.status, stopDetail: `Session is ${session.status}. Use loopforge_resume to restart a paused session, or loopforge_start for a new task.` };
1016
+ }
1017
+ // ── 2. Extract self-evaluation ───────────────────────────────────────
1018
+ const extracted = this.extractEvaluation(output, preExtractedEval);
1019
+ if (!extracted) {
1020
+ session.status = "stalled";
1021
+ this.save(session);
1022
+ void this.notifyTerminal(session, "stalled");
1023
+ logEvent("session_end", { sessionId, loopId: session.loopId, stopReason: "stalled", round: session.currentRound });
1024
+ return { sessionId, round: session.currentRound, prompt: null, stopReason: "stalled", stopDetail: "Self-evaluation could not be extracted from agent output. Both structured regex and heuristic fallback returned null.", roundSuccess: false };
1025
+ }
1026
+ const { selfEval, extractionFailed } = extracted;
1027
+ // ── 3. Execute round transaction ─────────────────────────────────────
1028
+ const tx = await this.executeRoundTransaction(session, selfEval, extractionFailed);
1029
+ // ── 4. Route disposition ─────────────────────────────────────────────
1030
+ if (tx.pr.action === "reject") {
1031
+ return this.buildRejectionResult(sessionId, session, tx.pr, tx.verificationFlags);
1032
+ }
1033
+ // Accepted rounds advance the before-snapshot baseline. Rejected rounds
1034
+ // retain the original baseline so their retry remains zero-commit.
1035
+ session.evidenceBaseline = tx.actualEvidence;
1036
+ if (tx.pr.action === "terminate") {
1037
+ return this.buildTerminationResult(sessionId, session, tx.pr);
1038
+ }
1039
+ if (tx.pr.action === "stop") {
1040
+ return this.buildStopResult(sessionId, session, tx.pr);
1041
+ }
1042
+ // ── 5. Continue — compile next round ─────────────────────────────────
1043
+ return this.advanceToNextRound(sessionId, session, selfEval, tx.pr, tx.verificationFlags, tx.actualEvidence);
1044
+ }
996
1045
  /** Write back loop knowledge to long-term memory.
997
1046
  * Uses shared base builder from policy.ts. Called when a loop terminates. */
998
1047
  async notifyTerminal(session, stopReason) {