gcf-aif-ext 0.7.4 → 0.7.5

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/README.md CHANGED
@@ -52,7 +52,7 @@ On Windows, run `extension add` and `extension update` from PowerShell or cmd, n
52
52
 
53
53
  ### 0.7.1
54
54
 
55
- - `gcf board --json`: a ready item's worktree with no run lock and no session mark is a run that stopped (a timeout, a stop, a crash), not a run: the item reads `running: false`, `resumable: true`, and keeps its moves; its next run resumes the worktree.
55
+ - `gcf board --json`: a ready item's worktree with no run lock and no session mark is a run that stopped (a timeout, a stop, a crash), not a run: the item reads `running: false`, `resumable: true`, and keeps its moves; its next run resumes the worktree. An agent session the harness kept (`.gcf-session/runs/<id>.agent.json`) makes a ready item `resumable` too, in either run mode, and shows as `resume: {ended, resets?}` (null without one).
56
56
 
57
57
  ### 0.7.0
58
58
 
package/extension.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/lee-to/ai-factory/2.x/schemas/extension.schema.json",
3
3
  "name": "gcf-aif-ext",
4
- "version": "0.7.4",
4
+ "version": "0.7.5",
5
5
  "description": "Game Context Framework for AI Factory",
6
6
  "skills": [
7
7
  "skills/gcf-init",
package/lib/board.js CHANGED
@@ -120,6 +120,25 @@ function logOf(projectDir, id) {
120
120
  return existsSync(path.join(projectDir, log)) ? log : null;
121
121
  }
122
122
 
123
+ /**
124
+ * How the agent session of the item's last run ended, when the harness kept it for a rerun to
125
+ * resume (`<id>.agent.json`): `{ended, resets?}`, or null. A file that is not a record is none.
126
+ */
127
+ async function resumeOf(projectDir, id) {
128
+ try {
129
+ const file = path.join(projectDir, '.gcf-session', 'runs', `${id}.agent.json`);
130
+ const rec = JSON.parse(await readFile(file, 'utf8'));
131
+ if (typeof rec?.session_id !== 'string') return null;
132
+
133
+ return {
134
+ ended: String(rec.ended ?? ''),
135
+ ...(rec.resets ? { resets: String(rec.resets) } : {}),
136
+ };
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+
123
142
  // Fields of the item file a board shows as they are: null or [] when the file has none.
124
143
  const SCALARS = [
125
144
  'title',
@@ -163,10 +182,15 @@ async function itemOf(projectDir, c, verified) {
163
182
  const ready = c.data.status === 'ready' && verified.has(id);
164
183
  // `running` and `verify` are not stored (§4): a live harness run lock or a fresh session mark
165
184
  // is a run; a trailer is a passed DoD. A worktree with neither is a run that stopped (a
166
- // timeout, a stop, a crash): the item is ready, and its next run resumes that worktree.
185
+ // timeout, a stop, a crash): the item is ready, and its next run resumes that worktree. A
186
+ // session the harness kept (`resume`) is resumed by the next run in either mode.
167
187
  const running = runHolder(projectDir, id) !== null || sessionActive(projectDir, id);
188
+ const resume = c.data.status === 'ready' ? await resumeOf(projectDir, id) : null;
168
189
  const resumable =
169
- !running && c.data.status === 'ready' && !ready && existsSync(worktreeOf(projectDir, id));
190
+ !running &&
191
+ c.data.status === 'ready' &&
192
+ !ready &&
193
+ (resume !== null || existsSync(worktreeOf(projectDir, id)));
170
194
 
171
195
  return {
172
196
  id,
@@ -179,6 +203,7 @@ async function itemOf(projectDir, c, verified) {
179
203
  ...(c.data.blocked ? { blocked: c.data.blocked } : {}),
180
204
  running,
181
205
  resumable,
206
+ resume,
182
207
  verified: ready,
183
208
  // What a board may offer (§4.1): nothing while a run drives the item.
184
209
  moves: running ? [] : movesOf(c.data),
@@ -40,6 +40,14 @@ export function releaseSession(projectDir, id) {
40
40
  rmSync(markOf(projectDir, id), { force: true });
41
41
  }
42
42
 
43
+ /**
44
+ * Forget the agent session the harness kept for a rerun of `id` (`<id>.agent.json`): a move,
45
+ * by hand or by an agent, ends the work it would resume.
46
+ */
47
+ export function dropAgentSession(projectDir, id) {
48
+ rmSync(path.join(path.dirname(markOf(projectDir, id)), `${id}.agent.json`), { force: true });
49
+ }
50
+
43
51
  /** Whether a session works the item now: a mark refreshed within SESSION_TTL_MS. */
44
52
  export function sessionActive(projectDir, id) {
45
53
  const file = markOf(projectDir, id);
package/lib/work-ops.js CHANGED
@@ -38,7 +38,7 @@ import {
38
38
  import { dropInbox } from './inbox.js';
39
39
  import { refuseByPhase } from './phase.js';
40
40
  import { runHolder, withMainLock } from './main-lock.js';
41
- import { releaseSession, sessionActive } from './session-mark.js';
41
+ import { dropAgentSession, releaseSession, sessionActive } from './session-mark.js';
42
42
 
43
43
  /**
44
44
  * Options of a committed move; each move reads the ones it needs.
@@ -228,8 +228,10 @@ async function finish(m, move, tail, commit) {
228
228
  commit,
229
229
  inputs: [m.item.file],
230
230
  });
231
- // A move on the item ends the session that worked it: the board stops showing it running.
231
+ // A move on the item ends the session that worked it: the board stops showing it running,
232
+ // and no rerun resumes it.
232
233
  releaseSession(m.projectDir, String(m.item.data.id));
234
+ dropAgentSession(m.projectDir, String(m.item.data.id));
233
235
 
234
236
  return hash;
235
237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gcf-aif-ext",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Game Context Framework for AI Factory",
5
5
  "type": "module",
6
6
  "license": "MIT",