@odla-ai/harness 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2259,6 +2259,32 @@ function createCodeRuntimeToolBroker(input, lease, role) {
2259
2259
  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" }) };
2260
2260
  }
2261
2261
 
2262
+ // src/code-memory.ts
2263
+ var import_graph2 = require("@odla-ai/graph");
2264
+ var MAX_MEMORY_BODY = 4e3;
2265
+ function validateMemory(memory) {
2266
+ if (!memory.subject.includes(":")) {
2267
+ throw new TypeError(`memory subject must be a graph node id, got "${memory.subject}"`);
2268
+ }
2269
+ const body = memory.body.trim();
2270
+ if (!body) throw new TypeError("a memory needs a body");
2271
+ if (body.length > MAX_MEMORY_BODY) throw new TypeError("memory body exceeds its bound");
2272
+ if (!memory.authorId.trim()) throw new TypeError("a memory needs an author");
2273
+ }
2274
+ function hazardFromAttempt(input) {
2275
+ const body = [
2276
+ `Attempt ${input.attempt} at "${input.goal.slice(0, 200)}" failed its proof.`,
2277
+ input.feedback.replace(/\s+/g, " ").slice(0, MAX_MEMORY_BODY - 300)
2278
+ ].join(" ");
2279
+ return input.touched.slice(0, 10).map((path) => ({
2280
+ subject: path.includes(":") ? path : `file:${path}`,
2281
+ kind: "hazard",
2282
+ body,
2283
+ evidence: { kind: "gate", ref: input.verificationId },
2284
+ authorId: input.authorId
2285
+ }));
2286
+ }
2287
+
2262
2288
  // src/code-goal-runner.ts
2263
2289
  async function runGoal(spec, attempt) {
2264
2290
  assertBudget(spec.budget);
@@ -2443,6 +2469,9 @@ function pursueRuntimeGoal(input) {
2443
2469
  return { gatePassed: false, feedback: "", tokens: outcome.tokens, error: outcome.error };
2444
2470
  }
2445
2471
  const verdict = await input.gate(attempt);
2472
+ if (!verdict.passed && input.memory) {
2473
+ await rememberFailure(input, attempt, verdict.feedback);
2474
+ }
2446
2475
  return {
2447
2476
  gatePassed: verdict.passed,
2448
2477
  feedback: verdict.feedback,
@@ -2453,6 +2482,25 @@ function pursueRuntimeGoal(input) {
2453
2482
  }
2454
2483
  );
2455
2484
  }
2485
+ async function rememberFailure(input, attempt, feedback) {
2486
+ if (!input.memory || !feedback.trim()) return;
2487
+ try {
2488
+ const touched = await input.touched?.(attempt) ?? [];
2489
+ if (touched.length === 0) return;
2490
+ for (const memory of hazardFromAttempt({
2491
+ goal: input.spec.goal,
2492
+ attempt,
2493
+ feedback,
2494
+ touched,
2495
+ verificationId: `goal-${attempt}`,
2496
+ authorId: input.memory.authorId
2497
+ })) {
2498
+ validateMemory(memory);
2499
+ await input.memory.store.remember(memory);
2500
+ }
2501
+ } catch {
2502
+ }
2503
+ }
2456
2504
  function goalEventLine(event) {
2457
2505
  if (event.type === "attempt_started") return `Goal attempt ${event.attempt} starting.`;
2458
2506
  if (event.type === "attempt_failed") return `Attempt ${event.attempt} did not satisfy the proof.`;