@awebai/oats 0.34.0 → 0.34.2
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 +53 -28
- package/docs/capabilities.md +6 -1
- package/docs/desktop-cli-api.md +6 -6
- package/docs/execution-targets.md +17 -5
- package/docs/implementation.md +24 -2
- package/docs/integrations.md +2 -2
- package/docs/knowledge.md +7 -4
- package/docs/official-catalog.md +4 -4
- package/docs/packages.md +10 -10
- package/docs/release-notes/v0.34.1.md +57 -0
- package/docs/release-notes/v0.34.2.md +68 -0
- package/docs/souls-and-instances.md +18 -11
- package/docs/workspaces.md +13 -2
- package/lib/canonical-json.mjs +3 -1
- package/lib/capability-contract.mjs +1 -1
- package/lib/config-data.mjs +4 -2
- package/lib/core.mjs +67 -15
- package/lib/instance-resolution.mjs +26 -4
- package/lib/remote.mjs +116 -9
- package/lib/workspace.mjs +3 -2
- package/package-catalog.json +3 -3
- package/package.json +1 -1
- package/skills/oats-getting-started/SKILL.md +1 -1
package/bin/oats.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
LAYERS, OATS_VERSION, manifestOperations, upgradeHomeMeta,
|
|
31
31
|
capabilityManifests, capabilityTrust, capabilityExecutablePath,
|
|
32
32
|
officialPackageCatalog, officialCatalogFile, officialCapabilityAliases, resolvedFromHome, resolvedFromPrepared, teamEnv, isWorkspaceHome, preWorkspaceHome, isCapturedHome, capturedHomeRefusal, composeInstanceAgentsMd, parseYamlNested, withConfigFile,
|
|
33
|
-
findInstanceHome, findInstanceHomes, enclosingInstanceHome, logicalCwd, workspaceOf, stopInstanceSession, ensureRoot, findRoot, findAgent, findAgentAt, legacyLocalAgents, legacyCapturedHomes, listAgents, listInstances, servedIdentityLine, spawnInstanceAsync, instanceSoulDir, recordedKernelBin, launchConfigsAt, launchReportFor, explicitInstanceName, retireInstance, inspectInstanceSession, inputInstanceSession, attachInstanceSession, startInstanceSession, defaultRepo, RELATIONS, validateLaunchConfig, validateLaunchConfigDefaults, renderLaunchRecipe, describeLaunchCommand, redactLaunchRecipe, LAUNCH_HARNESSES, planLaunch, redactLaunchCommand, restartInstanceSession,
|
|
33
|
+
findInstanceHome, findInstanceHomes, enclosingInstanceHome, logicalCwd, readableInstanceHomes, workspaceOf, stopInstanceSession, ensureRoot, findRoot, findAgent, findAgentAt, legacyLocalAgents, legacyCapturedHomes, listAgents, listInstances, servedIdentityLine, spawnInstanceAsync, instanceSoulDir, recordedKernelBin, launchConfigsAt, launchReportFor, explicitInstanceName, retireInstance, inspectInstanceSession, inputInstanceSession, attachInstanceSession, startInstanceSession, defaultRepo, RELATIONS, validateLaunchConfig, validateLaunchConfigDefaults, renderLaunchRecipe, describeLaunchCommand, redactLaunchRecipe, withSafeTaskPrompt, LAUNCH_HARNESSES, planLaunch, redactLaunchCommand, restartInstanceSession,
|
|
34
34
|
} from "../lib/core.mjs";
|
|
35
35
|
import {
|
|
36
36
|
writeFileAtomic, LOCK_FILE, readLock, readLockIfPresent, writeLock, resolvePackages, memoizedRemote,
|
|
@@ -391,7 +391,7 @@ async function workspaceTarget(bail, { command, liveTeams = true }) {
|
|
|
391
391
|
try { meta = JSON.parse(readFileSync(join(homeFlag, "instance.json"), "utf8")); } catch (e) { return bail("E_SESSION_UNKNOWN", `${homeFlag} is not an OATS instance home (${e.code === "ENOENT" ? "no instance.json" : e.message})`); }
|
|
392
392
|
if (isCapturedHome(meta)) { const e = capturedHomeRefusal(homeFlag, "nothing was read"); return bail(e.code, e.message, e.details); }
|
|
393
393
|
if (!meta || typeof meta.modules !== "object" || meta.modules === null) return bail("E_UNSUPPORTED_MODE", `${homeFlag} is not a workspace-model home (it records no modules): it was spawned by an earlier kernel — re-spawn it from the deployment`);
|
|
394
|
-
if (soulFlag && soulFlag
|
|
394
|
+
if (soulFlag && !(await import("../lib/instance-resolution.mjs")).homeSoulMatches(soulFlag, meta)) return bail("E_HOME_MISMATCH", `--soul ${soulFlag} is not the soul of ${homeFlag} (${meta.agent})`);
|
|
395
395
|
const deployment = dirname(dirname(dirname(dirname(realOrResolved(homeFlag)))));
|
|
396
396
|
// A v2 home lives at <deployment>/agents/<soul>/instances/<name>: its deployment is
|
|
397
397
|
// derived, so it must hold oats-local.yaml EXACTLY there (never found by walking up).
|
|
@@ -537,8 +537,9 @@ async function workspaceOperation(t, { bail, address, layer, opName }) {
|
|
|
537
537
|
const settings = lp.settings;
|
|
538
538
|
const cwd = op.context === "home" ? t.home : t.deployment;
|
|
539
539
|
const env = { ...lp.env(mod.name, settings), OATS_OPERATION: address, OATS_CONTEXT: t.deployment, OATS_ROOT: t.agentsRoot, PI_AGENTS_ROOT: t.agentsRoot };
|
|
540
|
-
|
|
541
|
-
|
|
540
|
+
// The home's identity, never one inherited from the caller (the reserved PI_AGENT_* names included).
|
|
541
|
+
for (const k of ["OATS_INSTANCE", "OATS_INSTANCE_HOME", "OATS_HOME", "PI_AGENT_INSTANCE", "PI_AGENT_HOME"]) delete env[k];
|
|
542
|
+
if (op.context === "home") Object.assign(env, { OATS_INSTANCE: t.meta.instance, OATS_INSTANCE_HOME: t.home, OATS_HOME: t.home });
|
|
542
543
|
await readSession?.closeBatches(); // no idle `git cat-file --batch` child held for the provider's whole run
|
|
543
544
|
const r = spawnSync("node", [abs, ...rest, ...argFlags, "--json"], { cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], maxBuffer: 16 * 1024 * 1024, timeout: OPERATION_TIMEOUT_MS, killSignal: "SIGTERM" });
|
|
544
545
|
finishOperation({ r, bail, address, provider, op, argFlags, cwd, home: t.home, meta: t.meta, api: INSPECT_OPERATIONS_API });
|
|
@@ -582,7 +583,8 @@ function legacyLayoutProblems(root) {
|
|
|
582
583
|
}
|
|
583
584
|
async function doctorWorkspaceJson(ctx, soulName, ws) {
|
|
584
585
|
const composition = await doctorComposition(ctx, soulName, ws, (code, msg, details) => jsonFail(code, msg, details));
|
|
585
|
-
const
|
|
586
|
+
const agentsRoot = join(dirname(ws.local.path), "agents");
|
|
587
|
+
const problems = [...legacyLayoutProblems(agentsRoot), readableInstanceHomes(agentsRoot)].filter(Boolean);
|
|
586
588
|
return {
|
|
587
589
|
schemaVersion: 1, workspaceApi: 2, context: ctx,
|
|
588
590
|
workspace: { file: ws.local.path, ref: ws.local.workspace },
|
|
@@ -621,7 +623,8 @@ async function doctor(dir) {
|
|
|
621
623
|
doctorVersionSkew();
|
|
622
624
|
const composition = await doctorComposition(ctx, soulName, ws, (code, msg) => die(`${msg} [${code}]`));
|
|
623
625
|
printDoctorWorkspace(ws);
|
|
624
|
-
|
|
626
|
+
const agentsRoot = join(dirname(ws.local.path), "agents");
|
|
627
|
+
for (const p of [...legacyLayoutProblems(agentsRoot), readableInstanceHomes(agentsRoot)].filter(Boolean)) console.log(`\n! ${p.code}: ${p.message}`);
|
|
625
628
|
if (soulName) {
|
|
626
629
|
const information = operationalKnowledgeNote(composition, soulName);
|
|
627
630
|
if (information) console.log(`\nINFO: ${information}`);
|
|
@@ -768,11 +771,11 @@ function launchPreview(bail) {
|
|
|
768
771
|
instance = meta.instance || basename(home);
|
|
769
772
|
if (!(meta.launch && typeof meta.launch === "object") && !selectionGiven) {
|
|
770
773
|
// A home that predates recipes, asked nothing: its frozen command is
|
|
771
|
-
// described as
|
|
772
|
-
// (E_LAUNCH_LEGACY: re-spawn it from the deployment).
|
|
773
|
-
let d;
|
|
774
|
-
try {
|
|
775
|
-
jsonOk({ context, selected, selection: { source: "frozen-command", launchConfig: null, harness: null, model: null, yolo: null }, harness: meta.harness, model: meta.model || null, modelSource: meta.model ? "recorded" : "native default", yolo: meta.yolo ?? null, launchConfig: null, launchConfigSource: null, launchConfigDefault: false, executable: { path: d.executable, declared: null, resolvedFrom: "recorded" }, argv: d.argv, environment: d.environment, command: redactLaunchCommand(
|
|
774
|
+
// described as a start runs it (with its harness's safe task prompt). Under a
|
|
775
|
+
// selection the planner refuses it (E_LAUNCH_LEGACY: re-spawn it from the deployment).
|
|
776
|
+
let d, frozen;
|
|
777
|
+
try { frozen = withSafeTaskPrompt(meta.command, meta.harness); d = describeLaunchCommand(frozen); } catch (e) { bail(e.code || "E_LAUNCH_COMMAND_UNSUPPORTED", e.message); }
|
|
778
|
+
jsonOk({ context, selected, selection: { source: "frozen-command", launchConfig: null, harness: null, model: null, yolo: null }, harness: meta.harness, model: meta.model || null, modelSource: meta.model ? "recorded" : "native default", yolo: meta.yolo ?? null, launchConfig: null, launchConfigSource: null, launchConfigDefault: false, executable: { path: d.executable, declared: null, resolvedFrom: "recorded" }, argv: d.argv, environment: d.environment, command: redactLaunchCommand(frozen), prompt: { kind: "task-file", file: "TASK.md" }, hooks: null, preflight: [{ check: "recipe", ok: true, detail: "frozen command; a selection is refused (E_LAUNCH_LEGACY): re-spawn it" }], ok: true });
|
|
776
779
|
return;
|
|
777
780
|
}
|
|
778
781
|
const agentsRoot = agentsRootOfHome(home);
|
|
@@ -2324,8 +2327,8 @@ function retireCmd() {
|
|
|
2324
2327
|
}
|
|
2325
2328
|
// The calling instance knows its own home: self-retire never needs to
|
|
2326
2329
|
// disambiguate a same-named twin by hand.
|
|
2327
|
-
if (homeFlag === undefined && process.env.OATS_INSTANCE_HOME &&
|
|
2328
|
-
const isSelf = process.env.
|
|
2330
|
+
if (homeFlag === undefined && process.env.OATS_INSTANCE_HOME && process.env.OATS_INSTANCE === name) homeFlag = process.env.OATS_INSTANCE_HOME;
|
|
2331
|
+
const isSelf = process.env.OATS_INSTANCE === name;
|
|
2329
2332
|
if (isSelf && !args.includes("--self")) die(`"${name}" is the calling instance — self-retire is irreversible; if your task is complete and you were told to retire, re-run with --self (finish your memory files FIRST; your session dies ~8s after)`);
|
|
2330
2333
|
if (!isSelf && args.includes("--self")) die(`--self given but "${name}" is not the calling instance`);
|
|
2331
2334
|
const root = ensureRoot(dirFlag());
|
|
@@ -2837,23 +2840,32 @@ async function capabilityCommand() {
|
|
|
2837
2840
|
if (!hit) return NOT_DISPATCHED;
|
|
2838
2841
|
// The same team/workspace facts a spawn hook receives (lead decision c3-7).
|
|
2839
2842
|
const teamCtx = teamEnv(resolvedFromPrepared(hit.prepared, hit.deployment));
|
|
2840
|
-
// No home, so no recorded soul: the soul's
|
|
2841
|
-
//
|
|
2842
|
-
//
|
|
2843
|
-
//
|
|
2844
|
-
const
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2843
|
+
// No home, so no recorded soul: OATS_SOUL is the soul's source at the resolved commit, read
|
|
2844
|
+
// as a spawn preview reads it (the per-commit copy a spawn left under the agents root, else a
|
|
2845
|
+
// temporary fetch removed when the command ends). Read only when the command runs (after its
|
|
2846
|
+
// --help). A soul that cannot be read refuses the command: it never runs with OATS_SOUL unset.
|
|
2847
|
+
const soul = async () => {
|
|
2848
|
+
const { previewWorkspaceSoul } = await import("../lib/instance-resolution.mjs");
|
|
2849
|
+
const entry = hit.prepared?.soulEntry;
|
|
2850
|
+
try { const p = await previewWorkspaceSoul(hit.prepared, join(hit.deployment, "agents")); return { dir: p.soulDir, cleanup: p.cleanup }; }
|
|
2851
|
+
catch (e) {
|
|
2852
|
+
throw Object.assign(new Error(`oats ${cmd}: cannot read soul ${flag("soul")} at ${String(entry?.commit ?? "?").slice(0, 12)}, so the command would run without OATS_SOUL; nothing was run: ${e.message}`),
|
|
2853
|
+
{ code: typeof e?.code === "string" && e.code.startsWith("E_") ? e.code : "E_REMOTE_UNREADABLE", details: { soul: flag("soul"), repoKey: entry?.repoKey ?? null, commit: entry?.commit ?? null } });
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
return runManifestCommand({ capability: hit.module.name, ...hit.manifest }, { settings: hit.settings, origins: hit.resolution?.payloadOrigins?.[hit.module.name] }, teamCtx, hit.ensureTree, soul);
|
|
2848
2857
|
}
|
|
2849
2858
|
|
|
2850
2859
|
async function dispatch() {
|
|
2851
2860
|
let activeIds;
|
|
2852
2861
|
let context = process.cwd();
|
|
2853
2862
|
let teamCtx, homeMeta, homeTeamCtx;
|
|
2854
|
-
// OATS_INSTANCE_HOME is the
|
|
2855
|
-
//
|
|
2856
|
-
|
|
2863
|
+
// OATS_INSTANCE_HOME is the identity; OATS_HOME still counts. With neither set (a harness that
|
|
2864
|
+
// strips the session env), the home enclosing the cwd. What chose the home is named in every
|
|
2865
|
+
// refusal about it.
|
|
2866
|
+
const homeVariable = ["OATS_INSTANCE_HOME", "OATS_HOME"].find((name) => process.env[name]);
|
|
2867
|
+
const instanceHome = homeVariable ? process.env[homeVariable] : enclosingInstanceHome(logicalCwd());
|
|
2868
|
+
const chosenBy = homeVariable ?? "the working directory";
|
|
2857
2869
|
const metaFile = instanceHome && join(instanceHome, "instance.json");
|
|
2858
2870
|
// Capability-id keyed — never answer for `constructor`/`toString`. Belt and
|
|
2859
2871
|
// braces: the ids come from instance.json, which spawn wrote from resolved
|
|
@@ -2872,11 +2884,19 @@ async function capabilityCommand() {
|
|
|
2872
2884
|
if (!isWorkspaceHome(meta)) { const e = preWorkspaceHome(instanceHome, "nothing was dispatched"); bail(e.code, e.message); }
|
|
2873
2885
|
context = meta.repo || context;
|
|
2874
2886
|
soulDir = instanceSoulDir(instanceHome, meta);
|
|
2887
|
+
const ws = meta.workspace && typeof meta.workspace === "object" ? meta.workspace : {};
|
|
2888
|
+
// Inside a home the namespace is the home's, so a --soul for another soul is refused, never
|
|
2889
|
+
// ignored (as inspect's --soul against --home is). The home's own soul, by any of its names, is fine.
|
|
2890
|
+
const soulFlag = flag("soul");
|
|
2891
|
+
if (soulFlag === true) bail("E_BAD_ARGS", "--soul needs a soul name", { flag: "--soul" });
|
|
2892
|
+
if (typeof soulFlag === "string" && !(await import("../lib/instance-resolution.mjs")).homeSoulMatches(soulFlag, meta)) {
|
|
2893
|
+
bail("E_HOME_MISMATCH", `--soul ${soulFlag} is not the soul of the instance home ${instanceHome} (${meta.agent}), which ${chosenBy} chose; to run "${cmd}" as a spawn of ${soulFlag} would, run it outside the instance home with OATS_INSTANCE_HOME and OATS_HOME unset`,
|
|
2894
|
+
{ home: instanceHome, soul: meta.agent, chosenBy, flag: "--soul" });
|
|
2895
|
+
}
|
|
2875
2896
|
// The team/workspace facts the home recorded at spawn, as its hooks got them, with the
|
|
2876
2897
|
// recorded eligible teams (OATS_TEAMS_SOURCE=recorded). Only the home's MESSAGING module
|
|
2877
2898
|
// gets them live (below): its team verbs (join/leave/teams) must see what the workspace
|
|
2878
2899
|
// allows now, and no other command pays a remote read for them.
|
|
2879
|
-
const ws = meta.workspace && typeof meta.workspace === "object" ? meta.workspace : {};
|
|
2880
2900
|
const messaging = (meta.capabilities || []).find((c) => c.layer === "messaging")?.id;
|
|
2881
2901
|
homeMeta = { meta, messaging };
|
|
2882
2902
|
homeTeamCtx = (t) => teamEnv({ workspace: { key: ws.key, name: ws.name, deployment: ws.deployment }, teams: t.teams, defaultTeam: t.defaultTeam, teamsSource: t.source });
|
|
@@ -2894,7 +2914,7 @@ async function capabilityCommand() {
|
|
|
2894
2914
|
// Workspace model: an instance's own materialized modules are the command
|
|
2895
2915
|
// namespaces available to it (instance.json.modules → <home>/.oats/modules).
|
|
2896
2916
|
const mans = Object.values(capabilityManifests(instanceHome)).filter((m) => m.command === cmd && m.commands);
|
|
2897
|
-
if (!mans.length)
|
|
2917
|
+
if (!mans.length) bail("E_UNKNOWN_COMMAND", `oats ${cmd}: no capability of the instance home ${instanceHome} (soul ${homeMeta.meta.agent}), which ${chosenBy} chose, provides "${cmd}"`, { home: instanceHome, chosenBy, namespace: cmd });
|
|
2898
2918
|
if (mans.length > 1) bail("E_DUPLICATE_NAMESPACE", `duplicate operational command namespace "${cmd}": ${mans.map((m) => m.capability).join(", ")}`);
|
|
2899
2919
|
const m = mans[0];
|
|
2900
2920
|
if (!activeIds.includes(m.capability)) bail("E_CAPABILITY_INACTIVE", `${m.capability} command namespace is not active in the current context/instance`);
|
|
@@ -2905,14 +2925,14 @@ async function capabilityCommand() {
|
|
|
2905
2925
|
const live = await liveTeams(instanceHome, homeMeta.meta, { remoteOptions: remoteOptionsFromEnv() });
|
|
2906
2926
|
teamCtx = homeTeamCtx(live);
|
|
2907
2927
|
}
|
|
2908
|
-
return runManifestCommand(m, { settings: capSettings[m.capability] || {}, origins: capOrigins[m.capability] }, teamCtx, () => m._dir, soulDir);
|
|
2928
|
+
return runManifestCommand(m, { settings: capSettings[m.capability] || {}, origins: capOrigins[m.capability] }, teamCtx, () => m._dir, () => ({ dir: soulDir, cleanup: () => {} }));
|
|
2909
2929
|
}
|
|
2910
2930
|
|
|
2911
2931
|
/** Help / unknown-command / spec validation / exec — shared by every context.
|
|
2912
2932
|
* `m` is the manifest (with `capability`; `_dir` may be absent until `ensureDir`
|
|
2913
2933
|
* resolves the directory holding the executable — the operator branch fetches
|
|
2914
2934
|
* the module tree only when a command is actually going to run). */
|
|
2915
|
-
async function runManifestCommand(m, { settings, origins }, teamCtx, ensureDir,
|
|
2935
|
+
async function runManifestCommand(m, { settings, origins }, teamCtx, ensureDir, soul) {
|
|
2916
2936
|
const sub = args[1];
|
|
2917
2937
|
const cmds = Object.keys(m.commands);
|
|
2918
2938
|
// `oats <ns> --help` and `oats <ns> <cmd> --help` answer from the manifest
|
|
@@ -2946,6 +2966,11 @@ async function capabilityCommand() {
|
|
|
2946
2966
|
try { abs = capabilityExecutablePath(withDir, script); }
|
|
2947
2967
|
catch (e) { bail("E_CAPABILITY_BROKEN", e.message); }
|
|
2948
2968
|
if (!abs) bail("E_CAPABILITY_BROKEN", `${cmd} ${sub}: script not found (${join(dir, script)})`);
|
|
2969
|
+
// The soul the command acts for → { dir, cleanup }: a home's recorded soul directory, or (operator
|
|
2970
|
+
// dispatch) the soul read at its resolved commit, whose temporary copy goes when this process exits.
|
|
2971
|
+
let soulDir;
|
|
2972
|
+
try { const read = await soul(); soulDir = read.dir; process.once("exit", read.cleanup); }
|
|
2973
|
+
catch (e) { if (typeof e?.code === "string" && e.code.startsWith("E_")) bail(e.code, e.message, e.details); throw e; }
|
|
2949
2974
|
// OATS_SOUL is the recorded soul or nothing: an ambient value inherited from the
|
|
2950
2975
|
// invoking process names some other soul (a coordinator's own), never this one.
|
|
2951
2976
|
const { OATS_SOUL: _ambientSoul, ...inherited } = process.env;
|
package/docs/capabilities.md
CHANGED
|
@@ -533,7 +533,12 @@ passed as arguments; no shell is involved.
|
|
|
533
533
|
- `OATS_SOUL`, the soul directory: a home's recorded one, or for a soul
|
|
534
534
|
(`readiness --soul`, `inspect --soul`) its copy at the resolved commit,
|
|
535
535
|
which the kernel materialises first as a spawn would (0.30; a copy that
|
|
536
|
-
cannot be made fails readiness under `installed`, producer `soul copy`)
|
|
536
|
+
cannot be made fails readiness under `installed`, producer `soul copy`).
|
|
537
|
+
An operator command (`oats <namespace> … --soul <name>` from the
|
|
538
|
+
deployment) gets the soul's source at the resolved commit too: the copy a
|
|
539
|
+
spawn left under the agents root, else a temporary copy removed when the
|
|
540
|
+
command ends. A soul that cannot be read refuses the command; it never
|
|
541
|
+
runs without `OATS_SOUL`;
|
|
537
542
|
- for a home, `OATS_INSTANCE` and `OATS_INSTANCE_HOME`.
|
|
538
543
|
- `OATS_TEAM_SCOPE` is the deployment directory; `OATS_TEAM_NAME` is always
|
|
539
544
|
empty.
|
package/docs/desktop-cli-api.md
CHANGED
|
@@ -723,7 +723,7 @@ Read-only (it writes no lock):
|
|
|
723
723
|
"souls":["rm"],"capabilities":["nw-house-style"],"publishes":null,"url":"https://github.com/nw/agents/tree/66566512…",
|
|
724
724
|
"membershipFile":{"path":"oats-membership.yaml","url":"https://github.com/nw/agents/blob/66566512…/oats-membership.yaml"}}],
|
|
725
725
|
"packages":[{"id":"oats.okf","version":"3.0.0","source":"catalog:oats.okf","commit":"ab897841…","integrity":"sha256-bada35…",
|
|
726
|
-
"capabilities":["oats.okf"],"souls":[],"latest":{"version":"4.0.
|
|
726
|
+
"capabilities":["oats.okf"],"souls":[],"latest":{"version":"4.0.6","ref":"v4.0.6"}}],
|
|
727
727
|
"declaredPackages":["oats.framework","oats.okf"],"unsynced":["oats.framework"],"stale":[],
|
|
728
728
|
"external":[{"source":"git:github.com/oss/experts@3c606e09…","soul":"security-reviewer"}],
|
|
729
729
|
"problems":[],"warnings":[],
|
|
@@ -782,8 +782,8 @@ packages' capabilities and souls, sorted by name, then origin. Both carry
|
|
|
782
782
|
"defaultTeam":{"label":"mine","team":"mine:ana.aweb.ai","from":"deployment"},
|
|
783
783
|
"private":false,"path":"souls/writer","work":"directory","description":"Drafts campaigns.","harness":"pi","model":null,"harnessFrom":"kernel-default",
|
|
784
784
|
"file":{"path":"souls/writer/soul.yaml","url":null},"spawnable":true,"problem":null},
|
|
785
|
-
{"name":"knowledge-maintainer","qualifiedName":"oats.okf/knowledge-maintainer","origin":"package oats.okf v4.0.
|
|
786
|
-
"version":"4.0.
|
|
785
|
+
{"name":"knowledge-maintainer","qualifiedName":"oats.okf/knowledge-maintainer","origin":"package oats.okf v4.0.6","kind":"package","package":"oats.okf",
|
|
786
|
+
"version":"4.0.6","repoKey":"github.com/awebai/oats-okf","commit":"2a62df8e…","teams":null,"defaultTeam":null,"private":false,
|
|
787
787
|
"path":"oats-package/souls/knowledge-maintainer","work":"directory","description":"Reviews harvested knowledge.","harness":"pi","model":null,
|
|
788
788
|
"harnessFrom":"kernel-default","file":{"path":"oats-package/souls/knowledge-maintainer/soul.yaml","url":null},
|
|
789
789
|
"spawnable":false,"problem":{"code":"E_TEAM_UNKNOWN","message":"team \"reviewers\" is not declared (oats-local.yaml#/souls/teams/…)"}}],
|
|
@@ -872,8 +872,8 @@ nothing reads a working clone.
|
|
|
872
872
|
**The show:**
|
|
873
873
|
|
|
874
874
|
```json
|
|
875
|
-
{"capabilityShowApi":1,"name":"oats.okf","kind":"package","repoKey":"github.com/awebai/oats-okf","package":"oats.okf","version":"4.0.
|
|
876
|
-
"commit":"
|
|
875
|
+
{"capabilityShowApi":1,"name":"oats.okf","kind":"package","repoKey":"github.com/awebai/oats-okf","package":"oats.okf","version":"4.0.6",
|
|
876
|
+
"commit":"2a62df8e…","path":"oats-package/capabilities/oats-okf",
|
|
877
877
|
"inject":{"path":"injects/okf.md","bytes":2422,"text":"## Knowledge: OKF\n\nYou have two kinds of knowledge. …","binary":false,"truncated":false},
|
|
878
878
|
"skills":[{"name":"okf-consultation","path":"skills/okf-consultation","description":"Consulting your soul's knowledge with the `oats okf` CLI: …",
|
|
879
879
|
"files":[{"path":"skills/okf-consultation/SKILL.md","bytes":6947},{"path":"skills/okf-consultation/references/consult.md","bytes":4465}],
|
|
@@ -899,7 +899,7 @@ nothing reads a working clone.
|
|
|
899
899
|
**The `--file` answer:**
|
|
900
900
|
|
|
901
901
|
```json
|
|
902
|
-
{"capabilityShowApi":1,"name":"oats.okf","kind":"package","commit":"
|
|
902
|
+
{"capabilityShowApi":1,"name":"oats.okf","kind":"package","commit":"2a62df8e…",
|
|
903
903
|
"file":{"path":"skills/okf-instance-knowledge/SKILL.md","bytes":4787,"text":"---\nname: okf-instance-knowledge\n…","binary":false,"truncated":false}}
|
|
904
904
|
```
|
|
905
905
|
|
|
@@ -13,8 +13,8 @@ home. To run them on another machine, see [servers.md](servers.md).
|
|
|
13
13
|
| Harness | `--harness` | Launched as |
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| pi | `pi` (the default) | `pi --append-system-prompt <home>/AGENTS.md --approve --name <instance> [--model m] @TASK.md` |
|
|
16
|
-
| Claude Code | `claude` | `claude [--model m] --
|
|
17
|
-
| Codex | `codex` | `codex --cd <home> [--model m] --
|
|
16
|
+
| Claude Code | `claude` | `claude [--model m] -- '@TASK.md'` |
|
|
17
|
+
| Codex | `codex` | `codex --cd <home> [--model m] -- 'Read TASK.md in this directory first: it is your briefing and your task.'` |
|
|
18
18
|
|
|
19
19
|
Each harness starts in the instance home with its own native settings,
|
|
20
20
|
authentication and skill discovery. The command line also carries the
|
|
@@ -27,9 +27,9 @@ named launch configuration (`--launch-config <name>`); see
|
|
|
27
27
|
[configuration.md](configuration.md#launch-configurations).
|
|
28
28
|
`--model @native-default` uses the harness's own default model.
|
|
29
29
|
|
|
30
|
-
The launch command sets `OATS_INSTANCE
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
The launch command sets `OATS_INSTANCE` and `OATS_INSTANCE_HOME`, plus the
|
|
31
|
+
environment that the launch configuration and capabilities contribute. No
|
|
32
|
+
`PI_AGENT_*` name is set, for any harness. The home's layout and what those
|
|
33
33
|
variables point at are described in
|
|
34
34
|
[souls-and-instances.md](souls-and-instances.md#instance-anatomy).
|
|
35
35
|
|
|
@@ -166,6 +166,18 @@ and `restart`, opens a new harness conversation on `TASK.md`. The instance
|
|
|
166
166
|
resumes its work from its own state files, as its knowledge capability
|
|
167
167
|
prescribes.
|
|
168
168
|
|
|
169
|
+
The task's text never travels on a command line, where any local user could
|
|
170
|
+
read it in the process list:
|
|
171
|
+
|
|
172
|
+
- pi and Claude Code get `@TASK.md`, which each harness reads as the file.
|
|
173
|
+
- Codex gets a fixed pointer to the file and reads it with a tool.
|
|
174
|
+
- A home whose recorded command still hands over `"$(cat TASK.md)"` starts
|
|
175
|
+
with its harness's safe prompt instead, and the command is saved that way.
|
|
176
|
+
|
|
177
|
+
The home is created `0700` and `TASK.md` `0600`. `oats doctor` reports an
|
|
178
|
+
instance home that other users can read (`home-readable`), with the exact
|
|
179
|
+
`chmod`; it never changes a home's mode itself.
|
|
180
|
+
|
|
169
181
|
## Permissions (yolo)
|
|
170
182
|
|
|
171
183
|
Yolo is chosen per launch: `--yolo` / `--no-yolo` on `oats spawn`,
|
package/docs/implementation.md
CHANGED
|
@@ -72,6 +72,27 @@ Every CLI command owns one read session (`createReadSession` in
|
|
|
72
72
|
CLI closes it when the command ends). A library caller without a session
|
|
73
73
|
gets the plain per-call behaviour. Within a session:
|
|
74
74
|
|
|
75
|
+
- a HEAD observation speaks protocol v0 when the operator has not pinned
|
|
76
|
+
`protocol.version` (`git config --get`, read once per command): the whole
|
|
77
|
+
ref advertisement in one round trip, resolved exactly as v2's filtered
|
|
78
|
+
answer. `V0_ADVERTISEMENT_BUDGET` (4 MiB, git's `maxBuffer`) bounds what
|
|
79
|
+
is kept, not the transfer: git reads the whole advertisement before it
|
|
80
|
+
prints a ref, so a remote over budget costs its advertisement once, then
|
|
81
|
+
git is killed, the remote observed again under v2 and recorded. A v0
|
|
82
|
+
timeout stays today's error (no retry) and is recorded too, unless the
|
|
83
|
+
session's `deadline` cut that read's timeout (the deadline, not the remote,
|
|
84
|
+
may have ended it). The
|
|
85
|
+
v0 read and its `protocol.version` check go through `sessionExec` like every
|
|
86
|
+
other git call. The record is
|
|
87
|
+
`<cacheRoot>/.ls-remote/<sha256(key)>.<reason>.json`, `{ protocol: "v2",
|
|
88
|
+
reason: "overflow" | "timeout", recordedAt }`, one file per reason (an
|
|
89
|
+
in-flight timeout never replaces an overflow), written atomically with no lock;
|
|
90
|
+
`overflow` is permanent, `timeout` expires after 7 days, and an unreadable,
|
|
91
|
+
corrupt or expired record is no record (v0 is tried). Another v0 failure
|
|
92
|
+
that is not final (auth, not-found, cache, an abort) is retried once under
|
|
93
|
+
v2. Each is a session notice. The v2 argv (`lsRemoteArgs`) stays the
|
|
94
|
+
observation's identity: records and memo keys do not depend on the
|
|
95
|
+
protocol;
|
|
75
96
|
- a head is observed once per (cache repo, ref), and a commit peeled once; at
|
|
76
97
|
most eight observations run at once (`OBSERVE_LIMIT`), each holding its slot
|
|
77
98
|
for all its git work (the `ls-remote` and the fetch of the commit it names,
|
|
@@ -107,7 +128,8 @@ gets the plain per-call behaviour. Within a session:
|
|
|
107
128
|
exit hook (`closeNow`), and the system reaps them once the process is gone;
|
|
108
129
|
- a session may have a `deadline` (`READ_REMOTE_BUDGET_MS`, 12 s after it
|
|
109
130
|
starts): the CLI gives one to `status` and `workspace status` only
|
|
110
|
-
(`readBudgetMs`; `OATS_READ_REMOTE_BUDGET_MS`
|
|
131
|
+
(`readBudgetMs`; `OATS_READ_REMOTE_BUDGET_MS` is a test and ops override,
|
|
132
|
+
not a contract). Every
|
|
111
133
|
remote step then gets what is left of it instead of its own default: each
|
|
112
134
|
git call's timeout (`sessionExec`: ls-remote, fetch, ls-tree, the cache's
|
|
113
135
|
plumbing; none starts once nothing is left), the git version probe
|
|
@@ -203,7 +225,7 @@ scaling by call count.
|
|
|
203
225
|
| `npm test` | every suite under `test/` (`node --test`), through `scripts/run-tests.mjs` |
|
|
204
226
|
| `npm run check` | syntax of every shipped file |
|
|
205
227
|
| `npm run check:pi` | the pi adapter's TypeScript |
|
|
206
|
-
| `npm run validate` | the JSON schemas, the example manifests and configs,
|
|
228
|
+
| `npm run validate` | the JSON schemas, the example manifests and configs, every local link and anchor in the public docs, the repository's own `oats-workspace.yaml` and `package-catalog.json` read by the kernel's readers, and no unresolved merge-conflict marker in a tracked text file (`scripts/check-workspace-files.mjs`) |
|
|
207
229
|
| `npm run pack:check` | an `npm pack` dry run of both packages: nothing missing, nothing leaked |
|
|
208
230
|
| `npm run smoke:tarball` | installs the packed tarballs outside the checkout and exercises them |
|
|
209
231
|
|
package/docs/integrations.md
CHANGED
package/docs/knowledge.md
CHANGED
|
@@ -26,7 +26,7 @@ The workspace pins the package and fills the slot for every soul by default:
|
|
|
26
26
|
```yaml
|
|
27
27
|
# oats-workspace.yaml (excerpt)
|
|
28
28
|
packages:
|
|
29
|
-
oats.okf: v4.0.
|
|
29
|
+
oats.okf: v4.0.6
|
|
30
30
|
defaults:
|
|
31
31
|
knowledge: { oats.okf: { from: package } }
|
|
32
32
|
stores:
|
|
@@ -220,10 +220,13 @@ identical copies in both role capabilities.
|
|
|
220
220
|
- **From an instance home**, `oats okf …` runs the home's copy of the module
|
|
221
221
|
with the settings recorded at spawn.
|
|
222
222
|
- **From the deployment directory** (holding `oats-local.yaml`), in a shell
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
where neither `OATS_INSTANCE_HOME` nor `OATS_HOME` is set (either one pins
|
|
224
|
+
the command to that instance home), every capability command needs
|
|
225
|
+
`--soul <name>` (`E_BAD_ARGS` without it). Inside an instance home, a
|
|
226
|
+
`--soul` naming another soul is refused (`E_HOME_MISMATCH`). The kernel resolves the
|
|
225
227
|
soul as a spawn would, fetches its module at the locked commit into
|
|
226
|
-
`<deployment>/.oats/modules/` and runs it with the soul's merged settings
|
|
228
|
+
`<deployment>/.oats/modules/` and runs it with the soul's merged settings
|
|
229
|
+
and `OATS_SOUL`, the soul's source at that commit.
|
|
227
230
|
An unlocked package is `E_PACKAGE_MISSING` until `oats sync`.
|
|
228
231
|
|
|
229
232
|
### Consult
|
package/docs/official-catalog.md
CHANGED
|
@@ -10,9 +10,9 @@ or workspace membership alone does not make a package official.
|
|
|
10
10
|
| package | release | capabilities | package souls |
|
|
11
11
|
|---|---|---|---|
|
|
12
12
|
| `oats.framework` | `oats-framework/v1.4.1` (this repository) | `oats.core`, `oats.setup`, `oats.knowledge-theory` | `knowledge-theory-expert` |
|
|
13
|
-
| `oats.okf` | `v4.0.
|
|
14
|
-
| `oats.aweb` | `v1.17.
|
|
15
|
-
| `oats.engineering` | `v1.
|
|
13
|
+
| `oats.okf` | `v4.0.6` | `oats.okf` (knowledge), `oats.okf-harvest`, `oats.okf-maintenance` | `knowledge-harvester`, `knowledge-maintainer` |
|
|
14
|
+
| `oats.aweb` | `v1.17.6` | `oats.aweb` (messaging) | |
|
|
15
|
+
| `oats.engineering` | `v1.5.0` | `oats.engineering-expert`, `oats.developer`, `oats.code-review` | `code-reviewer` |
|
|
16
16
|
| `oats.authoring` | `v1.0.3` | `oats.authoring` | |
|
|
17
17
|
| `oats.jira` | `v1.0.1` | `oats.jira` (tasks) | |
|
|
18
18
|
| `oats.linear` | `v1.0.1` | `oats.linear` (tasks) | |
|
|
@@ -27,7 +27,7 @@ no lock and adds nothing to an existing workspace.
|
|
|
27
27
|
## Find and use packages
|
|
28
28
|
|
|
29
29
|
- A workspace pins an official package by **bare version** in its
|
|
30
|
-
`packages:` map (`oats.okf: v4.0.
|
|
30
|
+
`packages:` map (`oats.okf: v4.0.6`); `oats sync` resolves it through the
|
|
31
31
|
catalog to an exact commit, fetches it, verifies its integrity and locks it.
|
|
32
32
|
A package outside the catalog is written `git:<repo>@<ref>`. Pinning does
|
|
33
33
|
not join a team or adopt the publisher's workspace. See
|
package/docs/packages.md
CHANGED
|
@@ -44,14 +44,14 @@ whole organisation:
|
|
|
44
44
|
|
|
45
45
|
```yaml
|
|
46
46
|
packages:
|
|
47
|
-
oats.okf: v4.0.
|
|
47
|
+
oats.okf: v4.0.6 # bare version → the official catalog
|
|
48
48
|
acme.tools: git:github.com/acme/tools@v0.4.0 # direct ref: git:<repo>@<tag or full OID>
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
- **Bare version** (`v4.0.
|
|
51
|
+
- **Bare version** (`v4.0.6`, `4.0.6`, `1.0.0-rc.1`): the id is looked up in
|
|
52
52
|
the official catalog — `package-catalog.json` in the `oats` repo, or the file
|
|
53
53
|
named by `OATS_PACKAGE_CATALOG` — which supplies the repo url, the tag
|
|
54
|
-
convention (`v4.0.
|
|
54
|
+
convention (`v4.0.6` or `oats-framework/v1.4.1`) and the payload path. An id
|
|
55
55
|
the catalog does not know is `E_PACKAGE_MISSING` ("use `git:<repo>@<ref>` for
|
|
56
56
|
a package outside the catalog"). The catalog is the reviewed official list
|
|
57
57
|
([official-catalog.md](official-catalog.md)) and the only way a
|
|
@@ -75,8 +75,8 @@ members:
|
|
|
75
75
|
- git:github.com/acme/platform
|
|
76
76
|
packages:
|
|
77
77
|
oats.framework: v1.4.1
|
|
78
|
-
oats.okf: v4.0.
|
|
79
|
-
oats.aweb: v1.17.
|
|
78
|
+
oats.okf: v4.0.6
|
|
79
|
+
oats.aweb: v1.17.6
|
|
80
80
|
teams:
|
|
81
81
|
platform: { team: "platform:acme.aweb.ai", description: Platform engineering }
|
|
82
82
|
defaults:
|
|
@@ -105,7 +105,7 @@ decision recorded in the lock.
|
|
|
105
105
|
$ oats sync
|
|
106
106
|
workspace acme (github.com/acme/agents @ 3f2a9c1e)
|
|
107
107
|
members agents ✓↔ (@ 3f2a9c1e) platform ✓↔ (@ 77c0a1b2) billing ✗ (no-backlink)
|
|
108
|
-
packages acme.tools 0.4.0 ✓ (@ 47f4b816) oats.okf 4.0.
|
|
108
|
+
packages acme.tools 0.4.0 ✓ (@ 47f4b816) oats.okf 4.0.6 ✓ (@ 2a62df8e)
|
|
109
109
|
changed acme.tools — → 0.4.0 (@ 47f4b816)
|
|
110
110
|
souls 9 discovered (6 members, 1 external, 2 package, 0 disabled here) · 0 private capabilities
|
|
111
111
|
teams platform (shared) · this deployment's: oats teams
|
|
@@ -134,7 +134,7 @@ Declaring a package in the workspace's `packages:` is the trust decision
|
|
|
134
134
|
## `oats package add | remove`
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
|
-
oats package add oats.aweb v1.17.
|
|
137
|
+
oats package add oats.aweb v1.17.6 # a catalog version
|
|
138
138
|
oats package add acme.tools git:github.com/acme/tools@v0.4.0
|
|
139
139
|
oats package remove acme.tools
|
|
140
140
|
```
|
|
@@ -160,8 +160,8 @@ same workspace commit hold identical locks.
|
|
|
160
160
|
"source": "catalog:oats.okf",
|
|
161
161
|
"url": "https://github.com/awebai/oats-okf.git",
|
|
162
162
|
"path": "oats-package",
|
|
163
|
-
"version": "4.0.
|
|
164
|
-
"commit": "
|
|
163
|
+
"version": "4.0.6",
|
|
164
|
+
"commit": "2a62df8eb247b58b3d89b2ce38d84c49c2d8ae23",
|
|
165
165
|
"integrity": "sha256-…",
|
|
166
166
|
"capabilities": ["oats.okf", "oats.okf-harvest", "oats.okf-maintenance"]
|
|
167
167
|
},
|
|
@@ -331,7 +331,7 @@ A soul that names one of the package's capabilities with
|
|
|
331
331
|
{
|
|
332
332
|
"policy": "docs/official-catalog.md",
|
|
333
333
|
"packages": {
|
|
334
|
-
"oats.okf": { "url": "https://github.com/awebai/oats-okf.git", "ref": "v4.0.
|
|
334
|
+
"oats.okf": { "url": "https://github.com/awebai/oats-okf.git", "ref": "v4.0.6", "path": "oats-package" },
|
|
335
335
|
"oats.framework": { "url": "https://github.com/awebai/oats.git", "ref": "oats-framework/v1.4.1", "path": "oats-package" }
|
|
336
336
|
}
|
|
337
337
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# OATS 0.34.1
|
|
2
|
+
|
|
3
|
+
## Changed
|
|
4
|
+
|
|
5
|
+
- **Reading a remote's current head takes one round trip instead of two.**
|
|
6
|
+
`oats spawn` (preview and apply) and the read verbs ask each workspace
|
|
7
|
+
member for its head with Git's protocol v0, which returns the whole ref list
|
|
8
|
+
in one request, where protocol v2 takes two. On a 6-member deployment the
|
|
9
|
+
live phase of a spawn preview went from 0.77 s to 0.49 s (median of 5). Tags
|
|
10
|
+
and branches are read as before. If your Git configuration sets
|
|
11
|
+
`protocol.version`, OATS uses it as set. A remote whose ref list is over
|
|
12
|
+
4 MiB is read with protocol v2 from then on (remembered in the remote
|
|
13
|
+
cache). A remote that times out under v0 fails as before and is read with
|
|
14
|
+
v2 for the next 7 days; a read ended by the 12 s budget of `oats status` or
|
|
15
|
+
`oats workspace status` is not counted as the remote's timeout. One that
|
|
16
|
+
fails under v0 for another reason than an
|
|
17
|
+
authentication refusal, a missing repository or the local cache is read
|
|
18
|
+
again with v2. Each case is one `oats: warning`. What a read observes is
|
|
19
|
+
unchanged, and an apply still observes every member live. Known limit: the
|
|
20
|
+
4 MiB bounds what OATS keeps, not what Git downloads, so a remote with a
|
|
21
|
+
larger ref list transfers it once before OATS switches to protocol v2.
|
|
22
|
+
- **oats.engineering 1.5.0** (catalog and workspace pin, and the bundled
|
|
23
|
+
mirrors): developers keep each dynamic workflow under 10 agents and ask
|
|
24
|
+
their human for permission first for more. Within the cap they run
|
|
25
|
+
workflows without asking where the harness allows it; where it requires the
|
|
26
|
+
human's opt-in (Claude Code's workflow tool does), they use a standing
|
|
27
|
+
opt-in the human configured, or ask once per task.
|
|
28
|
+
- **The `oats-setup-admin` soul has knowledge and is harvested.** It drops
|
|
29
|
+
`oats.developer` and `knowledge: none` and takes the workspace's oats.okf
|
|
30
|
+
knowledge slot, owning the new `oats/oats-setup-admin` node (the judgement
|
|
31
|
+
of administering a workspace's config, never deployment state) and reading
|
|
32
|
+
the operator, expert and kernel nodes. This reverses the 0.29.1 opt-out:
|
|
33
|
+
deployment specifics are kept out of the base at promotion, by the node's
|
|
34
|
+
charter and the reviewed harvest PR, instead of by not harvesting the soul.
|
|
35
|
+
It needs `oats/oats-setup-admin` in the bound `oats` base
|
|
36
|
+
(awebai/oats-knowledge#51); new instances only.
|
|
37
|
+
|
|
38
|
+
- **`PI_AGENT_INSTANCE` and `PI_AGENT_HOME` are gone.** No launch sets them,
|
|
39
|
+
for any harness, pi included. (A home that records only its launch command,
|
|
40
|
+
from before launch recipes, keeps running that command as recorded.) The identity is `OATS_INSTANCE` and
|
|
41
|
+
`OATS_INSTANCE_HOME`, and the pi bridge (`@awebai/oats-pi`) reads
|
|
42
|
+
`OATS_INSTANCE_HOME`.
|
|
43
|
+
- Upgrade the CLI and the pi bridge together (`oats update` does both). A
|
|
44
|
+
0.34.0 pi bridge under a 0.34.1 kernel does not find its instance home.
|
|
45
|
+
- Capability commands no longer read `PI_AGENT_HOME`. A leftover one in an
|
|
46
|
+
operator's shell no longer pins a command to that instance.
|
|
47
|
+
- The names stay reserved: a launch configuration still cannot set them.
|
|
48
|
+
|
|
49
|
+
## Fixed
|
|
50
|
+
|
|
51
|
+
- **A capability command inside an instance home says which home, and why.**
|
|
52
|
+
- A `--soul` naming another soul than the home's was silently ignored; it
|
|
53
|
+
is now refused with `E_HOME_MISMATCH`.
|
|
54
|
+
- A namespace the home does not have answered `unknown command`; it is now
|
|
55
|
+
`E_UNKNOWN_COMMAND` naming the home.
|
|
56
|
+
- Both name what chose the home: `OATS_INSTANCE_HOME`, `OATS_HOME` or the
|
|
57
|
+
working directory.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# OATS 0.34.2
|
|
2
|
+
|
|
3
|
+
## Changed
|
|
4
|
+
|
|
5
|
+
- **oats.aweb 1.17.6** (catalog and workspace pin, and the bundled mirror):
|
|
6
|
+
session-delivery instructions name the aw 1.36.21+ full-mail wake form
|
|
7
|
+
(`aweb mail event received.` with metadata, sender body, the `Use the aw CLI`
|
|
8
|
+
line and a Recovery line), say that body and subject are untrusted sender
|
|
9
|
+
content that never overrides the task or human, and remind operators that
|
|
10
|
+
delivered mail may be marked read and absent from unread `aw mail inbox`.
|
|
11
|
+
The aw floor is unchanged (`>=1.36.13`).
|
|
12
|
+
- **oats.okf 4.0.6** (catalog and workspace pin, and the bundled mirrors):
|
|
13
|
+
harvest completion works on a real host (awebai/oats-okf#27, #28, #29,
|
|
14
|
+
#30).
|
|
15
|
+
- `oats okf complete` persists the harvester's judgment first, then delivers
|
|
16
|
+
in one detached worker per run. After persisting the judgment, it waits
|
|
17
|
+
up to 30 s: it answers with the final receipt, or with
|
|
18
|
+
`status: delivering` and the progress.
|
|
19
|
+
- A killed `complete`, or a killed worker, loses nothing. Rerunning
|
|
20
|
+
`complete` resumes, never judges again, and never pushes or opens a PR
|
|
21
|
+
twice.
|
|
22
|
+
- A `worker.lock` whose owner died is reclaimed automatically.
|
|
23
|
+
- `oats okf-harvest harvest-status` reports the delivery as in progress,
|
|
24
|
+
failed or stopped.
|
|
25
|
+
- Captured completions still deliver inline.
|
|
26
|
+
- A Git base whose head moved outside its knowledge root is no longer a
|
|
27
|
+
baseline change. A read-only base records the new head. A written base is
|
|
28
|
+
committed onto it. Only changed root bytes fail with `E_BASELINE`, and the
|
|
29
|
+
judgment stays persisted (`oats okf retry --rejudge`).
|
|
30
|
+
- Staging a Git base fetches its root's blobs in one batch. Delivery never
|
|
31
|
+
fetches the blobs outside the root. Staging over SSH, from GitHub:
|
|
32
|
+
|
|
33
|
+
| Base | 4.0.5 | 4.0.6 |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| oats-knowledge | 237 s | 9.9 s |
|
|
36
|
+
| aweb | 41 s | 9.1 s |
|
|
37
|
+
|
|
38
|
+
- `oats okf harvest-status` reports `unknown`, with the reason, when it
|
|
39
|
+
cannot read a soul's opt-out.
|
|
40
|
+
|
|
41
|
+
## Fixed
|
|
42
|
+
|
|
43
|
+
- **An operator command with `--soul` always gets the soul**
|
|
44
|
+
(awebai/oats#423). `oats <namespace> … --soul <name>` from the deployment
|
|
45
|
+
passed `OATS_SOUL` only when a spawn had already fetched that soul at the
|
|
46
|
+
resolved commit, so `oats okf harvest-status --soul <s>` reported harvest
|
|
47
|
+
"off" for a soul no spawn had fetched yet. The soul is now read at the
|
|
48
|
+
resolved commit as a spawn preview reads it: the copy a spawn left, else a
|
|
49
|
+
temporary copy removed when the command ends. A soul that cannot be read
|
|
50
|
+
refuses the command; it never runs without `OATS_SOUL`.
|
|
51
|
+
|
|
52
|
+
- **A task's text no longer appears in the process list** (awebai/oats#427).
|
|
53
|
+
Claude Code and Codex instances were launched with the whole `TASK.md` as a
|
|
54
|
+
command-line argument, which any local user can read. Now:
|
|
55
|
+
- Claude Code gets `@TASK.md` (it reads the file).
|
|
56
|
+
- Codex gets a fixed instruction to read `TASK.md`.
|
|
57
|
+
- A home recorded the old way starts with the safe prompt, and its saved
|
|
58
|
+
command is updated.
|
|
59
|
+
- New instance homes are created `0700` and `TASK.md` `0600`.
|
|
60
|
+
- `oats doctor` reports a home other users can read (`home-readable`) with
|
|
61
|
+
the exact `chmod`. Existing homes are not changed: run that `chmod`.
|
|
62
|
+
|
|
63
|
+
- **`npm run validate` catches a broken workspace file** (a development
|
|
64
|
+
check). It reads the repository's own `oats-workspace.yaml` and
|
|
65
|
+
`package-catalog.json` with the kernel's readers, and fails on an unresolved
|
|
66
|
+
merge-conflict marker in any tracked text file, naming the file and the
|
|
67
|
+
line. A conflicted `oats-workspace.yaml` had passed check, validate, pack
|
|
68
|
+
and smoke. The kernel's YAML and catalog errors now name the line too.
|