@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.
@@ -96,6 +96,16 @@ function createCodeRuntimeControlClient(options) {
96
96
  }
97
97
  await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
98
98
  },
99
+ recallMemories: async (sessionId, subjects, limit) => {
100
+ const response2 = await call(
101
+ `/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
102
+ { subjects: [...subjects], limit }
103
+ );
104
+ return Array.isArray(response2.memories) ? response2.memories : [];
105
+ },
106
+ rememberMemory: async (sessionId, memory) => {
107
+ await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
108
+ },
99
109
  reportSessionFailure: async (sessionId, message2) => {
100
110
  if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
101
111
  await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
@@ -2259,6 +2269,32 @@ function createCodeRuntimeToolBroker(input, lease, role) {
2259
2269
  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
2270
  }
2261
2271
 
2272
+ // src/code-memory.ts
2273
+ var import_graph2 = require("@odla-ai/graph");
2274
+ var MAX_MEMORY_BODY = 4e3;
2275
+ function validateMemory(memory) {
2276
+ if (!memory.subject.includes(":")) {
2277
+ throw new TypeError(`memory subject must be a graph node id, got "${memory.subject}"`);
2278
+ }
2279
+ const body = memory.body.trim();
2280
+ if (!body) throw new TypeError("a memory needs a body");
2281
+ if (body.length > MAX_MEMORY_BODY) throw new TypeError("memory body exceeds its bound");
2282
+ if (!memory.authorId.trim()) throw new TypeError("a memory needs an author");
2283
+ }
2284
+ function hazardFromAttempt(input) {
2285
+ const body = [
2286
+ `Attempt ${input.attempt} at "${input.goal.slice(0, 200)}" failed its proof.`,
2287
+ input.feedback.replace(/\s+/g, " ").slice(0, MAX_MEMORY_BODY - 300)
2288
+ ].join(" ");
2289
+ return input.touched.slice(0, 10).map((path) => ({
2290
+ subject: path.includes(":") ? path : `file:${path}`,
2291
+ kind: "hazard",
2292
+ body,
2293
+ evidence: { kind: "gate", ref: input.verificationId },
2294
+ authorId: input.authorId
2295
+ }));
2296
+ }
2297
+
2262
2298
  // src/code-goal-runner.ts
2263
2299
  async function runGoal(spec, attempt) {
2264
2300
  assertBudget(spec.budget);
@@ -2443,6 +2479,9 @@ function pursueRuntimeGoal(input) {
2443
2479
  return { gatePassed: false, feedback: "", tokens: outcome.tokens, error: outcome.error };
2444
2480
  }
2445
2481
  const verdict = await input.gate(attempt);
2482
+ if (!verdict.passed && input.memory) {
2483
+ await rememberFailure(input, attempt, verdict.feedback);
2484
+ }
2446
2485
  return {
2447
2486
  gatePassed: verdict.passed,
2448
2487
  feedback: verdict.feedback,
@@ -2453,6 +2492,25 @@ function pursueRuntimeGoal(input) {
2453
2492
  }
2454
2493
  );
2455
2494
  }
2495
+ async function rememberFailure(input, attempt, feedback) {
2496
+ if (!input.memory || !feedback.trim()) return;
2497
+ try {
2498
+ const touched = await input.touched?.(attempt) ?? [];
2499
+ if (touched.length === 0) return;
2500
+ for (const memory of hazardFromAttempt({
2501
+ goal: input.spec.goal,
2502
+ attempt,
2503
+ feedback,
2504
+ touched,
2505
+ verificationId: `goal-${attempt}`,
2506
+ authorId: input.memory.authorId
2507
+ })) {
2508
+ validateMemory(memory);
2509
+ await input.memory.store.remember(memory);
2510
+ }
2511
+ } catch {
2512
+ }
2513
+ }
2456
2514
  function goalEventLine(event) {
2457
2515
  if (event.type === "attempt_started") return `Goal attempt ${event.attempt} starting.`;
2458
2516
  if (event.type === "attempt_failed") return `Attempt ${event.attempt} did not satisfy the proof.`;