@adelie/goal 0.5.8 → 0.5.10

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.
package/hooks/lib.mjs CHANGED
@@ -94,8 +94,7 @@ export function uncachedTokens(record) {
94
94
  }
95
95
 
96
96
  /**
97
- * The records of the round that just ended: everything after the last round input, or the
98
- * whole file when there is none in it (a compaction rotated the file mid-round).
97
+ * Everything after the last round input, or all available records if it is missing.
99
98
  */
100
99
  export function lastRoundRecords(records) {
101
100
  let start = 0;
@@ -105,6 +104,28 @@ export function lastRoundRecords(records) {
105
104
  return records.slice(start);
106
105
  }
107
106
 
107
+ /** Read preceding compaction shards only as far as this round's input. */
108
+ export function readRoundRecords(file, sessionId, records) {
109
+ const match = /^(.*)_(\d+)\.jsonl$/.exec(path.basename(file));
110
+ if (!match || match[1] !== sessionId) return lastRoundRecords(records);
111
+ let index = Number(match[2]);
112
+ if (!Number.isSafeInteger(index)) return lastRoundRecords(records);
113
+ const chunks = [records];
114
+ let chunk = records;
115
+ while (!chunk.some(isRoundInput) && index > 1) {
116
+ index -= 1;
117
+ const previous = readTrace(
118
+ path.join(path.dirname(file), `${sessionId}_${String(index).padStart(3, "0")}.jsonl`),
119
+ );
120
+ const meta = previous.find((r) => r && r.type === "session_meta");
121
+ // Missing/unreadable shards or another session are not a reason to count older rounds.
122
+ if (meta?.payload?.session_id !== sessionId) break;
123
+ chunks.unshift(previous);
124
+ chunk = previous;
125
+ }
126
+ return lastRoundRecords(chunks.flat());
127
+ }
128
+
108
129
  /**
109
130
  * How the round's Task ended, read off its records: an `abort` event is the user's
110
131
  * interruption; a last `request_end` that is not completed is a failure the run gave up on;
package/hooks/stop.mjs CHANGED
@@ -21,7 +21,7 @@ import path from "node:path";
21
21
  import {
22
22
  GOAL_FILE,
23
23
  MAX_ROUNDS,
24
- lastRoundRecords,
24
+ readRoundRecords,
25
25
  readGoal,
26
26
  readInput,
27
27
  readTrace,
@@ -76,7 +76,7 @@ if (goal === "broken") {
76
76
  // A goal that already ended on this Session (an earlier run) is not this run's business.
77
77
  if (goal.ended) process.exit(0);
78
78
 
79
- const round = lastRoundRecords(records);
79
+ const round = readRoundRecords(tracePath, sessionId, records);
80
80
  goal.tokens_used += round.reduce((n, r) => n + uncachedTokens(r), 0);
81
81
 
82
82
  if (goal.status === "complete" || goal.status === "blocked") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adelie/goal",
3
- "version": "0.5.8",
3
+ "version": "0.5.10",
4
4
  "description": "Goal mode: a stop hook that keeps the session working toward an objective — re-injecting it every round and checking the goal file — until the goal is complete, blocked, or out of token budget.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/plugin.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description_zh": "目标模式:一个 stop hook,让会话持续朝目标工作——每轮重新注入目标并检查目标文件——直到目标完成、受阻或 token 预算耗尽。",
4
4
  "short_description": "Loop the session on an objective until it is done.",
5
5
  "short_description_zh": "让会话循环推进一个目标直到完成。",
6
- "version": "2026.09.29.1",
6
+ "version": "2026.10.28.1",
7
7
  "category": "office-productivity",
8
8
  "hooks": {
9
9
  "user_prompt": [