@odla-ai/harness 0.2.0 → 0.4.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.
@@ -6,7 +6,7 @@ import {
6
6
  assertCodeBuildRecipe,
7
7
  createCodeRuntimeControlClient,
8
8
  runCodeRuntimeHeartbeatLoop
9
- } from "./chunk-5FFR7U4L.js";
9
+ } from "./chunk-ANNX7VGK.js";
10
10
  import {
11
11
  assertPinnedImage,
12
12
  selectContainerEngine
package/dist/node.cjs CHANGED
@@ -26,6 +26,7 @@ __export(node_exports, {
26
26
  CodeRuntimeControlError: () => CodeRuntimeControlError,
27
27
  CodeRuntimeReconciler: () => CodeRuntimeReconciler,
28
28
  DecompositionError: () => DecompositionError,
29
+ MAX_MEMORY_BODY: () => MAX_MEMORY_BODY,
29
30
  MEASURED_PREMIUM: () => MEASURED_PREMIUM,
30
31
  SYSTEM_PROMPT_FOR: () => SYSTEM_PROMPT_FOR,
31
32
  V1_SYSTEM_PROMPT: () => V1_SYSTEM_PROMPT,
@@ -48,17 +49,23 @@ __export(node_exports, {
48
49
  describePatchFailure: () => describePatchFailure,
49
50
  digestStagedWorkspace: () => digestStagedWorkspace,
50
51
  feedbackIsActionable: () => feedbackIsActionable,
52
+ hazardFromAttempt: () => hazardFromAttempt,
51
53
  installedDependencies: () => installedDependencies,
52
54
  integrateSubGoals: () => integrateSubGoals,
53
55
  isCheckpointEffectCompleted: () => isCheckpointEffectCompleted,
54
56
  materializeCodeRuntimeSource: () => materializeCodeRuntimeSource,
55
57
  materializeCommandWorkspace: () => materializeCommandWorkspace,
56
58
  materializeGitTree: () => materializeGitTree,
59
+ outcomeCloses: () => outcomeCloses,
60
+ outcomeMemory: () => outcomeMemory,
57
61
  patchPaths: () => patchPaths,
58
62
  planReachCollisions: () => planReachCollisions,
59
63
  prepareRuntimeCheckpoint: () => prepareRuntimeCheckpoint,
60
64
  racedAttempt: () => racedAttempt,
65
+ recallAbout: () => recallAbout,
61
66
  registeredFiles: () => registeredFiles,
67
+ renderMemories: () => renderMemories,
68
+ renderOutcome: () => renderOutcome,
62
69
  resolveCodePath: () => resolveCodePath,
63
70
  restoreCodeWorkspaceCheckpoint: () => restoreCodeWorkspaceCheckpoint,
64
71
  runCodeAgent: () => runCodeAgent,
@@ -68,6 +75,7 @@ __export(node_exports, {
68
75
  runGoal: () => runGoal,
69
76
  runHarnessRunner: () => runHarnessRunner,
70
77
  runLeasedAttempt: () => runLeasedAttempt,
78
+ runtimeMemoryStore: () => runtimeMemoryStore,
71
79
  safeWorkspaceLabel: () => safeWorkspaceLabel,
72
80
  selectContainerEngine: () => selectContainerEngine,
73
81
  selectWinner: () => selectWinner,
@@ -76,6 +84,7 @@ __export(node_exports, {
76
84
  straySubGoalFiles: () => straySubGoalFiles,
77
85
  stripPatchEnvelope: () => stripPatchEnvelope,
78
86
  validateCodePatch: () => validateCodePatch,
87
+ validateMemory: () => validateMemory,
79
88
  validateRelativePath: () => validateRelativePath,
80
89
  verifyCodeCandidate: () => verifyCodeCandidate,
81
90
  verifyContainerEngineBoundary: () => verifyContainerEngineBoundary,
@@ -974,6 +983,16 @@ function createCodeRuntimeControlClient(options) {
974
983
  }
975
984
  await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
976
985
  },
986
+ recallMemories: async (sessionId, subjects, limit) => {
987
+ const response2 = await call(
988
+ `/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
989
+ { subjects: [...subjects], limit }
990
+ );
991
+ return Array.isArray(response2.memories) ? response2.memories : [];
992
+ },
993
+ rememberMemory: async (sessionId, memory) => {
994
+ await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
995
+ },
977
996
  reportSessionFailure: async (sessionId, message2) => {
978
997
  if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
979
998
  await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
@@ -2832,6 +2851,55 @@ function createCodeRuntimeToolBroker(input, lease, role) {
2832
2851
  return role === "coding" ? broker : { execute: (context, request) => request.tool === "sandbox.read" ? broker.execute(context, request) : Promise.resolve({ requestId: request.requestId, ok: false, content: "review sessions are read-only" }) };
2833
2852
  }
2834
2853
 
2854
+ // src/code-memory.ts
2855
+ var import_graph2 = require("@odla-ai/graph");
2856
+ var MAX_MEMORY_BODY = 4e3;
2857
+ function validateMemory(memory) {
2858
+ if (!memory.subject.includes(":")) {
2859
+ throw new TypeError(`memory subject must be a graph node id, got "${memory.subject}"`);
2860
+ }
2861
+ const body = memory.body.trim();
2862
+ if (!body) throw new TypeError("a memory needs a body");
2863
+ if (body.length > MAX_MEMORY_BODY) throw new TypeError("memory body exceeds its bound");
2864
+ if (!memory.authorId.trim()) throw new TypeError("a memory needs an author");
2865
+ }
2866
+ async function recallAbout(store, subjects, options = {}) {
2867
+ const limit = options.limit ?? 20;
2868
+ const wanted = options.graph ? [...(0, import_graph2.closure)(options.graph, subjects, {
2869
+ direction: "out",
2870
+ maxDepth: options.depth ?? 1,
2871
+ ...options.kinds ? { kinds: options.kinds } : {}
2872
+ })] : [...subjects];
2873
+ const found = await store.recall(wanted, limit * 2);
2874
+ return found.filter((memory) => !memory.supersededBy).slice(0, limit);
2875
+ }
2876
+ function renderMemories(memories) {
2877
+ if (memories.length === 0) return "";
2878
+ const lines = memories.map((memory) => {
2879
+ const source = memory.evidence ? ` [${memory.evidence.kind}:${memory.evidence.ref}]` : " [unverified]";
2880
+ return `- (${memory.kind}) ${memory.subject}${source}
2881
+ ${memory.body.replace(/\s+/g, " ").slice(0, 400)}`;
2882
+ });
2883
+ return [
2884
+ "What previous runs learned about this code. A hazard cost an attempt to find;",
2885
+ "an unverified note is one agent's opinion. Treat them accordingly.",
2886
+ ...lines
2887
+ ].join("\n");
2888
+ }
2889
+ function hazardFromAttempt(input) {
2890
+ const body = [
2891
+ `Attempt ${input.attempt} at "${input.goal.slice(0, 200)}" failed its proof.`,
2892
+ input.feedback.replace(/\s+/g, " ").slice(0, MAX_MEMORY_BODY - 300)
2893
+ ].join(" ");
2894
+ return input.touched.slice(0, 10).map((path) => ({
2895
+ subject: path.includes(":") ? path : `file:${path}`,
2896
+ kind: "hazard",
2897
+ body,
2898
+ evidence: { kind: "gate", ref: input.verificationId },
2899
+ authorId: input.authorId
2900
+ }));
2901
+ }
2902
+
2835
2903
  // src/code-goal-runner.ts
2836
2904
  async function runGoal(spec, attempt) {
2837
2905
  assertBudget(spec.budget);
@@ -3016,6 +3084,9 @@ function pursueRuntimeGoal(input) {
3016
3084
  return { gatePassed: false, feedback: "", tokens: outcome.tokens, error: outcome.error };
3017
3085
  }
3018
3086
  const verdict = await input.gate(attempt);
3087
+ if (!verdict.passed && input.memory) {
3088
+ await rememberFailure(input, attempt, verdict.feedback);
3089
+ }
3019
3090
  return {
3020
3091
  gatePassed: verdict.passed,
3021
3092
  feedback: verdict.feedback,
@@ -3026,6 +3097,25 @@ function pursueRuntimeGoal(input) {
3026
3097
  }
3027
3098
  );
3028
3099
  }
3100
+ async function rememberFailure(input, attempt, feedback) {
3101
+ if (!input.memory || !feedback.trim()) return;
3102
+ try {
3103
+ const touched = await input.touched?.(attempt) ?? [];
3104
+ if (touched.length === 0) return;
3105
+ for (const memory of hazardFromAttempt({
3106
+ goal: input.spec.goal,
3107
+ attempt,
3108
+ feedback,
3109
+ touched,
3110
+ verificationId: `goal-${attempt}`,
3111
+ authorId: input.memory.authorId
3112
+ })) {
3113
+ validateMemory(memory);
3114
+ await input.memory.store.remember(memory);
3115
+ }
3116
+ } catch {
3117
+ }
3118
+ }
3029
3119
  function goalEventLine(event) {
3030
3120
  if (event.type === "attempt_started") return `Goal attempt ${event.attempt} starting.`;
3031
3121
  if (event.type === "attempt_failed") return `Attempt ${event.attempt} did not satisfy the proof.`;
@@ -3351,6 +3441,56 @@ var CodePiRuntimeEngine = class {
3351
3441
  }
3352
3442
  };
3353
3443
 
3444
+ // src/code-runtime-memory.ts
3445
+ async function mutationId(memory) {
3446
+ const source = `${memory.subject} ${memory.kind} ${memory.body}`;
3447
+ const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(source));
3448
+ return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 40);
3449
+ }
3450
+ function runtimeMemoryStore(control, sessionId) {
3451
+ const recall = control.recallMemories;
3452
+ const remember = control.rememberMemory;
3453
+ if (!recall || !remember) return null;
3454
+ return {
3455
+ recall: async (subjects, limit) => await recall.call(control, sessionId, subjects, limit),
3456
+ remember: async (memory) => {
3457
+ await remember.call(control, sessionId, {
3458
+ subject: memory.subject,
3459
+ kind: memory.kind,
3460
+ body: memory.body,
3461
+ ...memory.evidence ? { evidence: memory.evidence } : {},
3462
+ mutationId: await mutationId(memory)
3463
+ });
3464
+ return { ...memory, id: "", createdAt: Date.now() };
3465
+ }
3466
+ };
3467
+ }
3468
+
3469
+ // src/code-goal-outcome.ts
3470
+ function outcomeCloses(outcome) {
3471
+ return outcome.met && outcome.evidence !== void 0;
3472
+ }
3473
+ function renderOutcome(outcome) {
3474
+ const spend = outcome.costUsd === void 0 ? "cost unknown" : `$${outcome.costUsd.toFixed(4)}`;
3475
+ const scale = `${outcome.attempts} attempt(s), ${outcome.tokens.toLocaleString()} tokens, ${spend}`;
3476
+ if (!outcome.met) {
3477
+ return `Goal not met (${outcome.stoppedReason}) after ${scale}. Proof: ${outcome.proof}`;
3478
+ }
3479
+ if (!outcome.evidence) {
3480
+ return `Goal reported met after ${scale}, but no verification receipt was produced. Treat as unverified. Proof: ${outcome.proof}`;
3481
+ }
3482
+ return `Goal met after ${scale}. Verified by ${outcome.evidence.kind}:${outcome.evidence.ref}. Proof: ${outcome.proof}`;
3483
+ }
3484
+ function outcomeMemory(outcome, subject, authorId) {
3485
+ return {
3486
+ subject,
3487
+ kind: "outcome",
3488
+ body: renderOutcome(outcome),
3489
+ ...outcome.evidence ? { evidence: outcome.evidence } : {},
3490
+ authorId
3491
+ };
3492
+ }
3493
+
3354
3494
  // src/code-goal-race.ts
3355
3495
  function selectWinner(outcomes) {
3356
3496
  const ranked = [...outcomes].sort((left, right) => {
@@ -3411,7 +3551,7 @@ function racedAttempt(options) {
3411
3551
  }
3412
3552
 
3413
3553
  // src/code-goal-decompose.ts
3414
- var import_graph2 = require("@odla-ai/graph");
3554
+ var import_graph3 = require("@odla-ai/graph");
3415
3555
  var import_code5 = require("@odla-ai/graph/code");
3416
3556
  var DecompositionError = class extends Error {
3417
3557
  constructor(message2) {
@@ -3499,7 +3639,7 @@ async function integrateSubGoals(options) {
3499
3639
  }
3500
3640
  async function planReachCollisions(plan, workspace) {
3501
3641
  const graph = await (0, import_code5.buildCodeGraph)({ paths: workspace.paths, read: workspace.read });
3502
- return (0, import_graph2.partition)(graph, plan.map((sub) => sub.files.map((file) => `${import_code5.FILE}:${file}`)), {
3642
+ return (0, import_graph3.partition)(graph, plan.map((sub) => sub.files.map((file) => `${import_code5.FILE}:${file}`)), {
3503
3643
  kinds: [import_code5.IMPORTS, import_code5.READS, import_code5.WRITES],
3504
3644
  direction: "out"
3505
3645
  });
@@ -3609,6 +3749,7 @@ async function installedDependencies(repoRoot) {
3609
3749
  CodeRuntimeControlError,
3610
3750
  CodeRuntimeReconciler,
3611
3751
  DecompositionError,
3752
+ MAX_MEMORY_BODY,
3612
3753
  MEASURED_PREMIUM,
3613
3754
  SYSTEM_PROMPT_FOR,
3614
3755
  V1_SYSTEM_PROMPT,
@@ -3631,17 +3772,23 @@ async function installedDependencies(repoRoot) {
3631
3772
  describePatchFailure,
3632
3773
  digestStagedWorkspace,
3633
3774
  feedbackIsActionable,
3775
+ hazardFromAttempt,
3634
3776
  installedDependencies,
3635
3777
  integrateSubGoals,
3636
3778
  isCheckpointEffectCompleted,
3637
3779
  materializeCodeRuntimeSource,
3638
3780
  materializeCommandWorkspace,
3639
3781
  materializeGitTree,
3782
+ outcomeCloses,
3783
+ outcomeMemory,
3640
3784
  patchPaths,
3641
3785
  planReachCollisions,
3642
3786
  prepareRuntimeCheckpoint,
3643
3787
  racedAttempt,
3788
+ recallAbout,
3644
3789
  registeredFiles,
3790
+ renderMemories,
3791
+ renderOutcome,
3645
3792
  resolveCodePath,
3646
3793
  restoreCodeWorkspaceCheckpoint,
3647
3794
  runCodeAgent,
@@ -3651,6 +3798,7 @@ async function installedDependencies(repoRoot) {
3651
3798
  runGoal,
3652
3799
  runHarnessRunner,
3653
3800
  runLeasedAttempt,
3801
+ runtimeMemoryStore,
3654
3802
  safeWorkspaceLabel,
3655
3803
  selectContainerEngine,
3656
3804
  selectWinner,
@@ -3659,6 +3807,7 @@ async function installedDependencies(repoRoot) {
3659
3807
  straySubGoalFiles,
3660
3808
  stripPatchEnvelope,
3661
3809
  validateCodePatch,
3810
+ validateMemory,
3662
3811
  validateRelativePath,
3663
3812
  verifyCodeCandidate,
3664
3813
  verifyContainerEngineBoundary,