@awebai/oats 0.24.10 → 0.24.11
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/bin/oats.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
approveCapability, approveAvailableCapability, updatePackage, removePackage, migrateLegacyLock, applyLegacyLockMigration,
|
|
31
31
|
packageIntegrity, capabilityArtifactIntegrity, verifyCapabilityInstallation, installedCapabilityDir, installedCapabilitiesDir, ownedCapabilitiesDir, loadPackageManifestAt,
|
|
32
32
|
resolveOatsConfig, resolveWorkMode, composeInstanceAgentsMd, planInstanceResources, parseYamlNested, assertSafeConfigValue, assertSafeConfigWriteKey, stripInternalAnnotations, withConfigFile, packagedInject, teamAgentRoots,
|
|
33
|
-
findTeamAgent, findTeamInstance, findCapabilityAgent, findInstanceHome, findInstanceHomes, listCapabilityAgents, workspaceOf, stopInstanceSession, recomposeInstanceInstructions,
|
|
33
|
+
findTeamAgent, findTeamInstance, findCapabilityAgent, findInstanceHome, findInstanceHomes, listCapabilityAgents, workspaceOf, stopInstanceSession, recomposeInstanceInstructions,
|
|
34
34
|
ensureRoot, findRoot, findAgent, listAgents, listInstances, listAgentDefs, createAgent as coreCreateAgent,
|
|
35
35
|
spawnInstance, retireInstance, inspectInstanceSession, inputInstanceSession, attachInstanceSession, startInstanceSession, upsertLocalAgent, defaultRepo, RELATIONS, validateLaunchConfig, resolveLaunchSelection, resolveLaunchExecutable, checkLaunchExecutable, missingLaunchEnvRefs, renderLaunchRecipe, describeLaunchCommand, redactLaunchRecipe, LAUNCH_RUNTIMES, LAUNCH_RECIPE_VERSION, parseLaunchCommand, resolveYolo, planLaunch, redactLaunchCommand, restartInstanceSession,
|
|
36
36
|
} from "../lib/core.mjs";
|
|
@@ -4358,10 +4358,10 @@ function spawnCmd() {
|
|
|
4358
4358
|
// K6e: record the wake outcome in the home so a same-key replay can report
|
|
4359
4359
|
// it instead of leaving "saved or not?" to inference.
|
|
4360
4360
|
if (r.spawnIdempotencyKey) {
|
|
4361
|
-
try { const f = join(r.home, "instance.json"); const m = JSON.parse(readFileSync(f, "utf8")); m.wake = { requested: true, saved: !wakeScheduleError, error: wakeScheduleError ?? null }; writeFileSync(f, JSON.stringify(m, null, 2) + "\n"); r.wake = m.wake;
|
|
4361
|
+
try { const f = join(r.home, "instance.json"); const m = JSON.parse(readFileSync(f, "utf8")); m.wake = { requested: true, saved: !wakeScheduleError, error: wakeScheduleError ?? null }; writeFileSync(f, JSON.stringify(m, null, 2) + "\n"); r.wake = m.wake; } catch { /* the receipt still says it */ }
|
|
4362
4362
|
}
|
|
4363
4363
|
} else if (r.spawnIdempotencyKey && r.replayed !== true) {
|
|
4364
|
-
try { const f = join(r.home, "instance.json"); const m = JSON.parse(readFileSync(f, "utf8")); m.wake = { requested: false, saved: null, error: null }; writeFileSync(f, JSON.stringify(m, null, 2) + "\n"); r.wake = m.wake;
|
|
4364
|
+
try { const f = join(r.home, "instance.json"); const m = JSON.parse(readFileSync(f, "utf8")); m.wake = { requested: false, saved: null, error: null }; writeFileSync(f, JSON.stringify(m, null, 2) + "\n"); r.wake = m.wake; } catch { /* nothing to record */ }
|
|
4365
4365
|
}
|
|
4366
4366
|
if (JSON_MODE) {
|
|
4367
4367
|
// Desktop CLI API v1 spawn result — a FIXED shape (see docs/desktop-cli-api.md).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Purpose:** the one accurate view of every work stream in the redesign, what is on main, what is in flight, who owns it, and what blocks it. Lead: `oats-expert` (redesign lead). Updated whenever anything merges, is returned, or reality changes. Older per-lane boards are superseded by this file.
|
|
4
4
|
|
|
5
|
-
**Last update:** 2026-09-22
|
|
5
|
+
**Last update:** 2026-09-22 21:30Z · **v0.24.10 PUBLISHED** (tag `d47dda94`; tarball shasum `030bcc3e`; bump PR86 → main `27f9f334`; artifact probe: preview API 2 → keyed apply → explicit-branch same-key replay → E_IDEMPOTENCY_CONFLICT → concurrent E_PLACEMENT_TAKEN/winner → keyed home retires clean) · **6b COMPLETE**: READ (PR78) + APPLY companion (PR85) · kernel K6b–K6f (PR76/80/81/82/83/84) · engineer → **7b (K7 instance events) proposal** → 8 · open contracts: K11, attach-knowledge, auto-PR, branch enumeration
|
|
6
6
|
|
|
7
7
|
Legend: ✅ on main/published · 🔄 in flight (PR/branch) · 🟡 preserved, not adopted · ⬜ not started · ⛔ blocked
|
|
8
8
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# OATS v0.24.11 — retirement recovery: kernel-owned fields only, nothing re-blessed after launch
|
|
2
|
+
|
|
3
|
+
Kernel/Pi/Desktop **0.24.11**. Tag `v0.24.11` → the commit carrying these
|
|
4
|
+
notes; the version-bump commit lands after the tag. **Upgrade from 0.24.10 —
|
|
5
|
+
it can lose an agent's early home bytes at retire.**
|
|
6
|
+
|
|
7
|
+
## Fix
|
|
8
|
+
|
|
9
|
+
0.24.10 (PR84) made a fresh keyed spawn retire clean by re-hashing the **whole
|
|
10
|
+
home** after the kernel's own post-spawn writes (completion marker, wake
|
|
11
|
+
record). That re-stamp ran after the tmux launch, so anything the agent wrote
|
|
12
|
+
in the launch→completion interval — `STATE.md`, notes — was blessed into the
|
|
13
|
+
retirement baseline and **not recovered** at retire. Found by the Desktop
|
|
14
|
+
engineer's exact-source inert analysis of the merged fix.
|
|
15
|
+
|
|
16
|
+
Now the re-stamp is gone. The baseline fingerprint hashes the root
|
|
17
|
+
`instance.json` with exactly the kernel-owned post-spawn fields
|
|
18
|
+
(`spawnCompleted`, `wake`) removed. Kernel writes never read as the agent's
|
|
19
|
+
changes; **nothing else in the home is ever re-blessed**; there is no
|
|
20
|
+
observation of the home after launch. Regression: authored bytes written at
|
|
21
|
+
launch are recovered under `changed instance-home bytes` with the bytes
|
|
22
|
+
intact; a plain keyed spawn still retires with `workRecovery: null`.
|
|
23
|
+
|
|
24
|
+
Rule recorded for the framework: the retirement baseline may account only for
|
|
25
|
+
bytes the kernel can *prove* it wrote — never for what it *observes* after the
|
|
26
|
+
agent may have started.
|
|
27
|
+
|
|
28
|
+
## Upgrade
|
|
29
|
+
|
|
30
|
+
`npm i -g @awebai/oats@0.24.11`, then `oats doctor`.
|
package/lib/core.mjs
CHANGED
|
@@ -6898,8 +6898,7 @@ ${task.trim() ? `\n## Task\n\n${task.trim()}\n` : "\nNo task was provided at spa
|
|
|
6898
6898
|
if (o.idempotencyKey !== undefined) {
|
|
6899
6899
|
// Only now is the spawn a finished receipt a same-key retry may replay.
|
|
6900
6900
|
meta.spawnCompleted = true;
|
|
6901
|
-
writeFileSync(join(home, "instance.json"), JSON.stringify(meta, null, 2) + "\n");
|
|
6902
|
-
refreshRetirementBaselineHome(home); // a kernel write, not the agent's
|
|
6901
|
+
writeFileSync(join(home, "instance.json"), JSON.stringify(meta, null, 2) + "\n"); // a kernel-owned field: the baseline fingerprint ignores it
|
|
6903
6902
|
}
|
|
6904
6903
|
return { ...meta, ...(o.expectDecision !== undefined ? { replayed: false } : {}), launch: redactLaunchRecipe(recipe), command: redactLaunchCommand(cmdline), attach: spawnHerdr ? `HERDR_SOCKET_PATH=${shq(spawnHerdr.socket)} ${shq(spawnHerdr.binary)} terminal attach ${shq(spawnHerdr.terminalId)}` : backend === "herdr" ? "not launched" : `tmux attach -t ${session}`, warnings: spawnWarnings.length ? spawnWarnings : undefined };
|
|
6905
6904
|
} catch (error) {
|
|
@@ -7276,7 +7275,7 @@ function fingerprintTree(root, { excludeRoot = new Set(), excludeGitMetadata = f
|
|
|
7276
7275
|
const st = lstatSync(path);
|
|
7277
7276
|
hash.update(childRel); hash.update("\0"); hash.update(String(st.mode & 0o7777)); hash.update("\0");
|
|
7278
7277
|
if (st.isSymbolicLink()) { hash.update("link\0"); hash.update(readlinkSync(path)); hash.update("\0"); }
|
|
7279
|
-
else if (st.isFile()) { hash.update("file\0"); hash.update(readFileSync(path)); hash.update("\0"); }
|
|
7278
|
+
else if (st.isFile()) { hash.update("file\0"); hash.update(!rel && e.name === "instance.json" ? kernelNeutralInstanceJson(readFileSync(path)) : readFileSync(path)); hash.update("\0"); }
|
|
7280
7279
|
else if (st.isDirectory()) { hash.update("dir\0"); walk(path, childRel); }
|
|
7281
7280
|
else throw oatsError("E_WORK_INSPECTION_FAILED", `${path} has an unsupported filesystem type`);
|
|
7282
7281
|
}
|
|
@@ -7335,17 +7334,21 @@ function retirementDisposableRoots(work, workMode, capabilities) {
|
|
|
7335
7334
|
return roots;
|
|
7336
7335
|
}
|
|
7337
7336
|
|
|
7338
|
-
/**
|
|
7339
|
-
*
|
|
7340
|
-
*
|
|
7341
|
-
* not read as the agent's
|
|
7342
|
-
*
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7337
|
+
/** The fields the KERNEL writes into a home's instance.json after the spawn's
|
|
7338
|
+
* retirement baseline was taken (completion marker, wake record). The
|
|
7339
|
+
* baseline fingerprint hashes instance.json with exactly these removed, so a
|
|
7340
|
+
* kernel write does not read as the agent's change — and NOTHING ELSE in the
|
|
7341
|
+
* home is ever re-blessed: an agent's STATE.md written seconds after launch
|
|
7342
|
+
* is still recovered at retire. (Replaces a whole-home re-stamp that could
|
|
7343
|
+
* bless authored bytes written in the launch→completion interval.) */
|
|
7344
|
+
const KERNEL_POST_SPAWN_FIELDS = ["spawnCompleted", "wake"];
|
|
7345
|
+
function kernelNeutralInstanceJson(bytes) {
|
|
7346
|
+
try {
|
|
7347
|
+
const m = JSON.parse(String(bytes));
|
|
7348
|
+
if (!m || typeof m !== "object" || Array.isArray(m)) return bytes;
|
|
7349
|
+
for (const k of KERNEL_POST_SPAWN_FIELDS) delete m[k];
|
|
7350
|
+
return Buffer.from(JSON.stringify(m));
|
|
7351
|
+
} catch { return bytes; }
|
|
7349
7352
|
}
|
|
7350
7353
|
function writeRetirementBaseline(home, work, mode, workMode, capabilities, runtime, { exclusive = false, incarnationId, executionBinding } = {}) {
|
|
7351
7354
|
if (mode === "directory") assertDirectoryRoots(home);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awebai/oats",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.11",
|
|
4
4
|
"description": "OATS (Open Agent Team Specification) — durable souls, disposable instances, targetable capability packages, and the runtime-neutral oats CLI/kernel.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|