@awebai/oats 0.40.1 → 0.40.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 +6 -6
- package/docs/capabilities.md +35 -2
- package/docs/desktop-cli-api.md +89 -7
- package/docs/execution-targets.md +30 -29
- package/docs/implementation.md +48 -5
- package/docs/oats-local.schema.json +2 -2
- package/docs/official-catalog.md +1 -1
- package/docs/packages.md +5 -5
- package/docs/release-lane.md +8 -4
- package/docs/release-notes/v0.40.2.md +120 -0
- package/docs/schedules.md +40 -11
- package/docs/servers.md +3 -1
- package/docs/workspaces.md +1 -1
- package/lib/core.mjs +27 -12
- package/lib/dir-lock.mjs +7 -4
- package/lib/instance-events.mjs +130 -46
- package/lib/schedule-command-child.mjs +39 -9
- package/lib/schedule.mjs +57 -28
- package/lib/servers.mjs +7 -1
- package/lib/session-input.mjs +42 -47
- package/package-catalog.json +1 -1
- package/package.json +1 -1
- package/skills/oats-getting-started/SKILL.md +1 -1
package/bin/oats.mjs
CHANGED
|
@@ -490,7 +490,7 @@ function finishOperation({ r, bail, address, provider, op, argFlags, cwd, home,
|
|
|
490
490
|
// (a partial receipt such as result.instance of something it launched
|
|
491
491
|
// before failing, and any details it gave) travels in error.details so a
|
|
492
492
|
// scheduler can keep an unconfirmed outcome and reconcile that target.
|
|
493
|
-
if (!envelope.ok) bail(envelope.error?.code || "E_OPERATION_FAILED", `${address}: ${envelope.error?.message || "failed"}`, { exit: r.status, envelope, ...(reportsRetainedEffectsText(envelope.error?.message) ? { unconfirmed: true } : {}) });
|
|
493
|
+
if (!envelope.ok) bail(envelope.error?.code || "E_OPERATION_FAILED", `${address}: ${envelope.error?.message || "failed"}`, { exit: r.status, envelope, ...(envelope.error?.details?.unconfirmed === true || reportsRetainedEffectsText(envelope.error?.message) ? { unconfirmed: true } : {}) });
|
|
494
494
|
if (r.status !== 0) bail("E_OPERATION_RESULT", `${address} (${provider.capability} ${op.command}) answered ok but exited ${r.status}; the receipt is not trusted and its effects are unconfirmed${stderr ? `: ${stderr.slice(0, 400)}` : ""}`, observed(envelope));
|
|
495
495
|
const result = envelope.result && typeof envelope.result === "object" ? envelope.result : {};
|
|
496
496
|
if (op.kind === "view") {
|
|
@@ -2413,8 +2413,8 @@ async function spawnCmd() {
|
|
|
2413
2413
|
if (e?.code === "E_PLACEMENT_TAKEN") { bail(e.code, e.message, { instance: e.instance, home: e.home }); throw e; }
|
|
2414
2414
|
if (e?.code === "E_INSTANCE_NAME_TAKEN") { bail(e.code, e.message, { instance: e.instance, home: e.home ?? null, ...(e.session ? { session: e.session } : {}) }); throw e; }
|
|
2415
2415
|
if (e?.code === "E_INSTANCE_NAME_INVALID") { bail(e.code, e.message, e.details); throw e; }
|
|
2416
|
-
if (e?.code === "E_SPAWN_INCOMPLETE") { bail(e.code, e.message, { instance: e.instance, home: e.home, launched: e.launched }); throw e; }
|
|
2417
|
-
bail(["E_BAD_ARGS", "E_RELATIVE_AMBIGUOUS"].includes(e.code) ? e.code : "E_SPAWN_FAILED", e.message || e); throw e;
|
|
2416
|
+
if (e?.code === "E_SPAWN_INCOMPLETE") { bail(e.code, e.message, { ...e.details, instance: e.instance, home: e.home, launched: e.launched }); throw e; }
|
|
2417
|
+
bail(["E_BAD_ARGS", "E_RELATIVE_AMBIGUOUS"].includes(e.code) ? e.code : "E_SPAWN_FAILED", e.message || e, e.details?.unconfirmed === true ? e.details : undefined); throw e;
|
|
2418
2418
|
}
|
|
2419
2419
|
// The instance exists from here on: a failed wake save is reported beside
|
|
2420
2420
|
// the full receipt, never hidden, and never causes a second spawn.
|
|
@@ -3818,7 +3818,7 @@ else if (cmd === "session") await sessionCmd();
|
|
|
3818
3818
|
else if (cmd === "schedule") await scheduleCmd();
|
|
3819
3819
|
else if (cmd === "trigger") await triggerCmd();
|
|
3820
3820
|
else if (cmd === "automations") await automationsCmd();
|
|
3821
|
-
else if (cmd === "spawn") { try { await spawnCmd(); } catch (e) { if (TYPED_CLI_FAILURES.has(e?.code)) throw e; if (JSON_MODE) jsonFail("E_SPAWN_FAILED", e.message || e); throw e; } }
|
|
3821
|
+
else if (cmd === "spawn") { try { await spawnCmd(); } catch (e) { if (TYPED_CLI_FAILURES.has(e?.code)) throw e; if (JSON_MODE) jsonFail("E_SPAWN_FAILED", e.message || e, e.details?.unconfirmed === true ? e.details : undefined); throw e; } }
|
|
3822
3822
|
else if (cmd === "retire") retireCmd();
|
|
3823
3823
|
else if (cmd === "capture" || cmd === "recall" || cmd === "setup") await recordCmd(cmd);
|
|
3824
3824
|
else if (cmd === "experimental") await experimentalCmd();
|
|
@@ -4062,7 +4062,7 @@ Usage:
|
|
|
4062
4062
|
typed lifecycle events (spawned, launched, stopped,
|
|
4063
4063
|
restarted, retired, worktree-retained…) written by
|
|
4064
4064
|
the action that made them true; nothing inferred
|
|
4065
|
-
oats instance waiting <set|clear> --producer <id> [--reason permission|question|attention] [--message <text>] [--home <abs>] [--json]
|
|
4065
|
+
oats instance waiting <set|clear> --producer <id> [--reason permission|question|attention] [--message <text>] [--home <abs>] [--dir <d>] [--json]
|
|
4066
4066
|
a producer's claim that the instance needs input
|
|
4067
4067
|
from a human; appended only on change; display only
|
|
4068
4068
|
oats instance attention [--message <text>] [--clear] [--json]
|
|
@@ -4135,7 +4135,7 @@ Layers: ${LAYERS.join(", ")}. Workspace model v2: docs/design/2026-09-23-workspa
|
|
|
4135
4135
|
// Same two renderings as every other typed failure: one envelope on stdout in
|
|
4136
4136
|
// --json mode, one `oats: <message>` line on stderr otherwise. The message
|
|
4137
4137
|
// already names the offending file — the readers re-raise it with one.
|
|
4138
|
-
if (JSON_MODE) jsonFail(e.code, e.message);
|
|
4138
|
+
if (JSON_MODE) jsonFail(e.code, e.message, e.details?.unconfirmed === true ? e.details : undefined);
|
|
4139
4139
|
die(e.message);
|
|
4140
4140
|
} finally {
|
|
4141
4141
|
// The command's read session: every `git cat-file --batch` child ends before the process does.
|
package/docs/capabilities.md
CHANGED
|
@@ -544,6 +544,18 @@ runs `bin/claude-waiting.sh` from the home's module copy, which calls
|
|
|
544
544
|
| `PostToolUseFailure` | `*` | clear, unless a subagent made the call |
|
|
545
545
|
| `UserPromptSubmit`, `Stop`, `SessionEnd` | none | clear, not debounced (a turn boundary) |
|
|
546
546
|
|
|
547
|
+
**One home per settings file.** The launch hook bakes the home it writes the
|
|
548
|
+
settings for into every command, as its real path (oats.core 2.4.1). The
|
|
549
|
+
script acts only when the session's `$OATS_INSTANCE_HOME` names that same
|
|
550
|
+
home, through any spelling (a symlinked deployment resolves to the same
|
|
551
|
+
path). A Claude process that loads one home's settings while carrying
|
|
552
|
+
another instance's environment (a nested `claude -p`, a `claude -p` started
|
|
553
|
+
with its working directory in another home, a pane that inherited the
|
|
554
|
+
variables) does nothing at all: no CLI call and no marker write, for either
|
|
555
|
+
home. Before 2.4.1 it set and cleared the claim of the home its environment
|
|
556
|
+
named, and its `Stop` and `SessionEnd` clears could erase that instance's
|
|
557
|
+
real claim.
|
|
558
|
+
|
|
547
559
|
Claude Code shows an AskUserQuestion through its permission dialog, so that
|
|
548
560
|
dialog's own `permission_prompt` follows the question's set: the script keeps
|
|
549
561
|
the current reason in its marker, and a permission prompt never relabels an
|
|
@@ -597,7 +609,8 @@ clears it.
|
|
|
597
609
|
after the hook began, the worst case is about 4 s, well under Claude's 5 s
|
|
598
610
|
hook timeout.
|
|
599
611
|
- **Debounce.** The script keeps private state outside the home, in a file
|
|
600
|
-
per home: `<dir>/<first 16 hex of sha256(home)>.claude
|
|
612
|
+
per home: `<dir>/<first 16 hex of sha256(home)>.claude` (the home's real
|
|
613
|
+
path, so each spelling of a home has the same file), where `<dir>`
|
|
601
614
|
is per user: `$XDG_RUNTIME_DIR/oats-waiting` when that is set and
|
|
602
615
|
absolute, else `$TMPDIR/oats-waiting-<uid>` when `TMPDIR` is absolute,
|
|
603
616
|
else `/tmp/oats-waiting-<uid>`. The spawn and launch
|
|
@@ -715,6 +728,11 @@ again" permission rules there.
|
|
|
715
728
|
- A `Stop` or `UserPromptSubmit` always clears, even one the main thread
|
|
716
729
|
produces while a subagent's prompt is open (a background subagent's
|
|
717
730
|
completion is submitted as a prompt).
|
|
731
|
+
- The one-home rule separates homes, not two sessions of one home: a second
|
|
732
|
+
Claude process started inside the same home with that home's own
|
|
733
|
+
`$OATS_INSTANCE_HOME` (a `claude -p` the agent runs there) still matches,
|
|
734
|
+
so its `Stop` and `SessionEnd` clears can erase the home's own live
|
|
735
|
+
`oats.core` claim ([#557](https://github.com/awebai/oats/issues/557)).
|
|
718
736
|
- A Claude instance spawned before the upgrade gets the emitter only when
|
|
719
737
|
it is respawned. Its launch hook comes from its recorded module copy.
|
|
720
738
|
|
|
@@ -752,7 +770,22 @@ with no provider or a home operation without `--home`
|
|
|
752
770
|
The provider must exit 0 with exactly one JSON envelope on stdout. Otherwise
|
|
753
771
|
the outcome is **unconfirmed**: `E_OPERATION_TIMEOUT` (after 240 s) or
|
|
754
772
|
`E_OPERATION_RESULT`, with `error.details { unconfirmed: true, exit, envelope?, stderr? }`.
|
|
755
|
-
A provider's own `ok: false` is relayed with its code
|
|
773
|
+
A provider's own `ok: false` is relayed with its code and full envelope. When
|
|
774
|
+
the provider sets `error.details.unconfirmed: true` (the boolean), the operation
|
|
775
|
+
wrapper also sets its outer `error.details.unconfirmed: true`. Providers should
|
|
776
|
+
set that marker when dispatched effects or their compensation cannot be
|
|
777
|
+
confirmed, and preserve it through wrappers. Producers should leave ordinary
|
|
778
|
+
refusals and fully compensated failures unmarked: naming a home or retained
|
|
779
|
+
evidence is not itself uncertainty. The operation wrapper and scheduler still
|
|
780
|
+
apply their existing message-based compatibility checks during this additive
|
|
781
|
+
migration, including for copied providers in older homes; their text-based
|
|
782
|
+
false positives are not removed by this change.
|
|
783
|
+
|
|
784
|
+
The kernel marks incomplete keyed spawns (`E_SPAWN_INCOMPLETE`) and spawn
|
|
785
|
+
failures whose rollback cannot finish with the same field, through the CLI.
|
|
786
|
+
A completed rollback remains an unmarked failure. This adds structural evidence;
|
|
787
|
+
it does not remove text fallbacks or change scheduler slot and retry rules.
|
|
788
|
+
A schedule of kind
|
|
756
789
|
`operation` runs the same command ([schedules.md](schedules.md)). The JSON
|
|
757
790
|
shapes are in [desktop-cli-api.md](desktop-cli-api.md#inspect-readiness-and-operation-run-on-the-workspace-model-operationsapi-2-soulsapi-2-readinessapi-2-oats-0260).
|
|
758
791
|
|
package/docs/desktop-cli-api.md
CHANGED
|
@@ -543,7 +543,10 @@ oats operation run <layer>:<name> (--home <abs> | --soul <name> [--dir <d>]) [--
|
|
|
543
543
|
home operation without `--home`), `E_CAPABILITY_REQUIRES`, `E_BAD_ARGS`
|
|
544
544
|
(undeclared or missing `--arg`), `E_CAPABILITY_BROKEN`. A provider's `ok:
|
|
545
545
|
false` is relayed with its code and `details: {exit, envelope,
|
|
546
|
-
unconfirmed?}`.
|
|
546
|
+
unconfirmed?}`. A literal provider `error.details.unconfirmed: true` is
|
|
547
|
+
promoted to the outer details while its full envelope stays nested. Existing
|
|
548
|
+
retained-effect text checks remain for compatibility during migration.
|
|
549
|
+
`E_OPERATION_TIMEOUT` (240 s) and `E_OPERATION_RESULT` are
|
|
547
550
|
unconfirmed outcomes: `details: {exit, signal, unconfirmed: true,
|
|
548
551
|
envelope?, stderr?, cleanup?}`.
|
|
549
552
|
|
|
@@ -1617,7 +1620,7 @@ with `--expect-decision` records the key and decision in `instance.json`.
|
|
|
1617
1620
|
`E_IDEMPOTENCY_CONFLICT {instance, home}`.
|
|
1618
1621
|
- `spawnCompleted` is `false` until launch, lineage and events are done; a
|
|
1619
1622
|
retry of an unfinished spawn is `E_SPAWN_INCOMPLETE {instance, home,
|
|
1620
|
-
launched}` (recover through the session surface).
|
|
1623
|
+
launched, unconfirmed: true}` (recover through the session surface).
|
|
1621
1624
|
- The key lives in the home. Mint it on the first confirmation and keep it
|
|
1622
1625
|
for that intent's retries.
|
|
1623
1626
|
- `wake: {requested, saved, error}` is recorded and replayed; `saved: null`
|
|
@@ -1686,11 +1689,17 @@ Feature `spawn-name`. `--name <slug>` is the exact name, with no prefix.
|
|
|
1686
1689
|
| `E_INSTANCE_NAME_INVALID`, `E_INSTANCE_NAME_TAKEN` | see above | |
|
|
1687
1690
|
| `E_DECISION_STALE` | `{decision}` | |
|
|
1688
1691
|
| `E_PLACEMENT_TAKEN`, `E_IDEMPOTENCY_CONFLICT` | `{instance, home}` | |
|
|
1689
|
-
| `E_SPAWN_INCOMPLETE` | `{instance, home, launched}` | |
|
|
1692
|
+
| `E_SPAWN_INCOMPLETE` | `{instance, home, launched, unconfirmed: true}` | |
|
|
1690
1693
|
| `E_LAUNCH_*`, `E_MODEL_UNKNOWN`, `E_UNSUPPORTED_HARNESS` | | the launch selection is refused |
|
|
1691
1694
|
| `E_LAUNCH_SHIM` | | the home's `oats` (`<home>/.oats/bin/oats`) cannot be written; the spawn is rolled back |
|
|
1692
1695
|
| `E_SCHEDULE_INVALID` | | a bad wake (`--wake-json`, `--wake-file`, `--wake-*`) |
|
|
1693
|
-
| `E_SPAWN_FAILED` | | anything else |
|
|
1696
|
+
| `E_SPAWN_FAILED` | `{unconfirmed: true}` when compensation cannot finish | anything else |
|
|
1697
|
+
|
|
1698
|
+
Spawn failure envelopes carry `error.details.unconfirmed: true` when an existing
|
|
1699
|
+
keyed spawn is incomplete (`E_SPAWN_INCOMPLETE`) or compensation cannot confirm
|
|
1700
|
+
cleanup. The keyed-spawn details retain `instance`, `home` and `launched`.
|
|
1701
|
+
Confirmed completed compensation does not set this marker. This is an additive
|
|
1702
|
+
producer migration; existing text-based compatibility checks remain.
|
|
1694
1703
|
|
|
1695
1704
|
## `instance.json` and the roster
|
|
1696
1705
|
|
|
@@ -1922,6 +1931,15 @@ route target:
|
|
|
1922
1931
|
`relativeTo` and `spawnOrigin` are always present, `null` when the host
|
|
1923
1932
|
does not supply them (a host before 0.31, a fact it never recorded, or a
|
|
1924
1933
|
saved route the host no longer lists). Nothing is derived on this side.
|
|
1934
|
+
- **`waitingOnYou`** (0.40.2, [Waiting on you](#waiting-on-you)) is on a row
|
|
1935
|
+
only when the host's kernel reports it: a row from a host before 0.40.0,
|
|
1936
|
+
and a saved route the host did not list, have no such key. Absent means
|
|
1937
|
+
"not reported", which is not `null` ("no claim"). When present it is `null`
|
|
1938
|
+
or `{since, producer, reason, message}`, passed through the kernel's read
|
|
1939
|
+
rule again on this side: a value that is not a claim (no valid `since` or
|
|
1940
|
+
`producer`) is `null`, and an unknown `reason` or an invalid `message` is
|
|
1941
|
+
`null` inside a claim that still counts. As on a local row, the host
|
|
1942
|
+
reports a claim only for a running instance.
|
|
1925
1943
|
- **`addressable`** (0.31): `true` for every row the host reports. Routed
|
|
1926
1944
|
session and lifecycle commands reach it by `--home`, or by name when the
|
|
1927
1945
|
name is unique on the host ([addressing](servers.md#run-there); a shared
|
|
@@ -2160,7 +2178,9 @@ oats instance events <instance> [--limit <n>] [--since <iso>] [--home <abs>] [--
|
|
|
2160
2178
|
|
|
2161
2179
|
- **Sources.** `home` is `<home>/.oats-events.jsonl`; `workspace` is
|
|
2162
2180
|
`<deployment>/.agents/events/<agent>--<instance>.jsonl` (it survives the
|
|
2163
|
-
home).
|
|
2181
|
+
home). The deployment is the one the home's spawn recorded, when that
|
|
2182
|
+
directory really holds the home at `agents/<agent>/instances/<instance>`;
|
|
2183
|
+
otherwise it is the fourth ancestor of the home as it was addressed. Each is `{path, status: "ok" | "absent" | "refused" | "tail",
|
|
2164
2184
|
bytes}`. Only a regular file is opened (no symlinks, same device and inode
|
|
2165
2185
|
after open), and at most its last 4 MiB is read (`"tail"`).
|
|
2166
2186
|
- **Kinds:** `spawned`, `launched`, `restarted`, `stopped`, `stop-refused`,
|
|
@@ -2182,12 +2202,35 @@ oats instance events <instance> [--limit <n>] [--since <iso>] [--home <abs>] [--
|
|
|
2182
2202
|
`null` for old rows); the top-level `incarnation` is the current home's (or
|
|
2183
2203
|
`null`). Earlier incarnations are returned as this address's history.
|
|
2184
2204
|
- **Address.** `--home` must be a home of `<instance>` (`E_HOME_MISMATCH`).
|
|
2205
|
+
A home has one address in storage, its real path (since 0.40.2): rows are
|
|
2206
|
+
written and matched under it, whatever spelling a writer or reader used (a
|
|
2207
|
+
deployment reached through a symlink, a symlinked agents root). The answer
|
|
2208
|
+
keeps the spelling it was asked in: the top-level `home` and every
|
|
2209
|
+
returned row's `home` are the home as the caller addressed it (`--home`,
|
|
2210
|
+
or the home found under `--dir`), the same string a status row carries.
|
|
2185
2211
|
Rows for another address are dropped and counted in
|
|
2186
2212
|
`integrity.foreignRows`; torn or invalid lines are counted in
|
|
2187
2213
|
`integrity.unreadableRows`. A row present in both logs is returned once;
|
|
2188
2214
|
identical rows repeated within one log (a set, a clear and the same set in
|
|
2189
2215
|
one millisecond) are all returned, as many as the log holding the most
|
|
2190
2216
|
copies has.
|
|
2217
|
+
- **Rows from before 0.40.2**, in a deployment addressed through a symlink
|
|
2218
|
+
only. A stored `home` is never rewritten, and never matched under another
|
|
2219
|
+
spelling. A row an earlier kernel wrote under the lexical spelling (a
|
|
2220
|
+
spawn's rows, and the claims of a session that was spawned and never
|
|
2221
|
+
restarted) is foreign after the upgrade, and counted in
|
|
2222
|
+
`integrity.foreignRows`. What that means for a claim:
|
|
2223
|
+
- A claim that was live under the lexical spelling stops showing. Nothing
|
|
2224
|
+
brings that row back: the claim shows again only when a producer makes it
|
|
2225
|
+
anew (the next permission prompt or question, the agent's next
|
|
2226
|
+
`oats instance attention`). A restart starts a new session with no
|
|
2227
|
+
claim, as always.
|
|
2228
|
+
- A claim that stayed set because the restart that should have voided it
|
|
2229
|
+
was recorded under the real path (#583) is gone.
|
|
2230
|
+
- The rows a started or restarted session wrote under the real path, which
|
|
2231
|
+
`oats status --dir <symlink>` could not see, are read now. They cannot
|
|
2232
|
+
surface a stale claim: such a session wrote its clears and its session
|
|
2233
|
+
boundaries under the real path too, so that history is complete.
|
|
2191
2234
|
- **Window.** `count` is the rows after `--since`; `returned` the window
|
|
2192
2235
|
(`--limit`, default 200, 1–2000); `truncated` means rows were cut or a
|
|
2193
2236
|
source was a tail. `lastEvent` is `{kind, at, producer, incarnation}` of
|
|
@@ -2251,14 +2294,17 @@ oats instance attention [--message <text>] [--clear] --json
|
|
|
2251
2294
|
message (a hand-edited log) reads as `null`, and the claim still counts.
|
|
2252
2295
|
A stored `reason` outside `permission`, `question`, `attention` reads as
|
|
2253
2296
|
`null` too, and a row whose `producer` is neither `kernel` nor a valid
|
|
2254
|
-
producer id is no claim at all.
|
|
2297
|
+
producer id, or whose `at` is not a date, is no claim at all.
|
|
2255
2298
|
- **Idempotent.** The verb reads the producer's live claim first and appends
|
|
2256
2299
|
only on a change: a `set` whose reason or message differs from the live
|
|
2257
2300
|
positive claim appends (`changed: true`), an identical one does not; a
|
|
2258
2301
|
`clear` appends only over a live positive claim. The answer is
|
|
2259
2302
|
`{eventsApi, instance, home, producer, changed, waitingOnYou}`, where
|
|
2260
2303
|
`waitingOnYou` is that producer's resulting claim (`null` when it holds
|
|
2261
|
-
none)
|
|
2304
|
+
none) and `home` is the home as the caller addressed it (`--home`,
|
|
2305
|
+
`$OATS_INSTANCE_HOME` or the enclosing home); the row is stored under the
|
|
2306
|
+
real path, so a set and a clear through different spellings of one home
|
|
2307
|
+
meet. Concurrent writers append whole lines; the latest row decides.
|
|
2262
2308
|
Each log is judged on its own, and success means both took the row: a
|
|
2263
2309
|
write either log refused is `E_EVENTS_FAILED` naming it, and the next call
|
|
2264
2310
|
(a retry) appends again to repair it.
|
|
@@ -2461,6 +2507,42 @@ stderr, not envelopes.
|
|
|
2461
2507
|
|
|
2462
2508
|
## Sessions and launch configurations
|
|
2463
2509
|
|
|
2510
|
+
### Input
|
|
2511
|
+
|
|
2512
|
+
```text
|
|
2513
|
+
oats session input --home <abs> [--text-file <path>] --json
|
|
2514
|
+
```
|
|
2515
|
+
|
|
2516
|
+
Input bytes come from stdin or the named file. The existing version-1 success
|
|
2517
|
+
answer is `{schemaVersion: 1, ok: true, result: {home, backend: "tmux",
|
|
2518
|
+
present: true, state, paneId, submitted: true, verified}}`. Session input runs
|
|
2519
|
+
on the execution host, including when the wake broker invokes it there;
|
|
2520
|
+
`--server` is not supported for input. The adapter sends one literal
|
|
2521
|
+
bracketed paste and one Enter after the existing input/authority/target checks.
|
|
2522
|
+
|
|
2523
|
+
`submitted` means terminal-operation success, **not model acceptance or
|
|
2524
|
+
processing**. `verified` is display observation only: `true` means a bounded
|
|
2525
|
+
look changed, possibly because of unrelated output or a dialog; `false` means
|
|
2526
|
+
unchanged, unreadable or exhausted observation. False never authorizes retry
|
|
2527
|
+
and is not proof of a pending draft or absence of effects. No `reason` is
|
|
2528
|
+
emitted; the `enter-not-taken` result from 0.39.4 is removed.
|
|
2529
|
+
|
|
2530
|
+
Read-only settling before Enter shares one monotonic 2-second budget starting
|
|
2531
|
+
when paste returns; up to two post-Enter looks share a 1-second budget. Probe
|
|
2532
|
+
timeouts and sleeps use the remaining budget. Observation failures produce
|
|
2533
|
+
`verified: false`, not input errors or extra keys. The original paste/key
|
|
2534
|
+
command timeouts and `E_SESSION_INPUT_FAILED` errors remain; the observation
|
|
2535
|
+
budgets do not bound those commands, failed buffer cleanup or OS scheduling.
|
|
2536
|
+
No busy-pane submission or exactly-once guarantee is provided. Generic command
|
|
2537
|
+
errors can still be uncertain after partial effects.
|
|
2538
|
+
|
|
2539
|
+
The Desktop terminal's authorized PTY writes are a separate stream; they do not
|
|
2540
|
+
consume this `verified` field. The Pi bridge does not interpret this result.
|
|
2541
|
+
Scheduler wake still records a nonthrowing input operation as delivered without
|
|
2542
|
+
adding acceptance/history fields. Actual broker acknowledgement and retry
|
|
2543
|
+
policy require their own consumer qualification; this result is not a native
|
|
2544
|
+
harness receipt. See [execution targets](execution-targets.md).
|
|
2545
|
+
|
|
2464
2546
|
### Start and restart
|
|
2465
2547
|
|
|
2466
2548
|
```text
|
|
@@ -143,35 +143,36 @@ oats session attach --home /abs/home
|
|
|
143
143
|
live claim that the instance needs input from a human, `{since, producer,
|
|
144
144
|
reason, message}`, or `null`; it is non-null only for a running harness
|
|
145
145
|
(docs/desktop-cli-api.md, "Waiting on you").
|
|
146
|
-
- **input**
|
|
147
|
-
NUL)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
146
|
+
- **input** sends UTF-8 text (stdin or `--text-file`, at most 256 KiB, no
|
|
147
|
+
NUL) as exactly one bracketed paste followed by exactly one Enter. The text
|
|
148
|
+
is never run by a shell. Existing endpoint authority, fallback-shell,
|
|
149
|
+
stopped-session and split-window checks still refuse before input.
|
|
150
|
+
|
|
151
|
+
Before Enter, a size-based floor (200 ms plus 3 ms per KiB) and read-only
|
|
152
|
+
settling polls share one **monotonic 2-second observation budget**, starting
|
|
153
|
+
immediately after the paste command returns, before buffer cleanup. Sleeps
|
|
154
|
+
and capture timeouts are clipped to the remaining budget; a failed capture
|
|
155
|
+
or exhausted budget ends settling and proceeds to the single Enter. After
|
|
156
|
+
Enter, at most two read-only looks share a separate 1-second observation
|
|
157
|
+
budget. These deadlines do not bound the original paste/key commands, buffer
|
|
158
|
+
cleanup or OS scheduling. They authorize no further keys.
|
|
159
|
+
|
|
160
|
+
Successful terminal commands return `submitted: true` and `verified`, with
|
|
161
|
+
no `reason`. `verified: true` means only that the bounded display comparison
|
|
162
|
+
saw a changed look; `false` means unchanged, unreadable or exhausted
|
|
163
|
+
observation. The comparison retains its whitespace and resize/reflow
|
|
164
|
+
handling. Display movement can be unrelated output, a spinner or a dialog;
|
|
165
|
+
an unchanged display is not proof that no effects occurred or that a draft
|
|
166
|
+
is pending. **Neither value authorizes retry or proves model acceptance.**
|
|
167
|
+
|
|
168
|
+
A failed paste or key command remains `E_SESSION_INPUT_FAILED`; an
|
|
169
|
+
observational failure does not turn successful terminal operations into a
|
|
170
|
+
refusal. Command errors may themselves be uncertain after partial effects.
|
|
171
|
+
This transport offers no exactly-once guarantee and does not guarantee that
|
|
172
|
+
a busy pane accepts the input. Wake schedules retain their existing rule:
|
|
173
|
+
any nonthrowing input answer is recorded as delivered, meaning terminal
|
|
174
|
+
operations, not model processing. Broker delivery/ack policy and harness
|
|
175
|
+
acceptance evidence remain separate contracts.
|
|
175
176
|
- **attach** is interactive and takes no `--json`. It opens a temporary tmux
|
|
176
177
|
session linked to the agent's window alone.
|
|
177
178
|
Closing the viewer leaves the agent running.
|
package/docs/implementation.md
CHANGED
|
@@ -65,11 +65,54 @@ published to npm. Its developer docs are in
|
|
|
65
65
|
| `harness-trust.mjs` | reading (never writing) Claude's and Codex's folder trust for a launch |
|
|
66
66
|
|
|
67
67
|
The schedule registry stores explicit concurrency caps, leaving the schedule
|
|
68
|
-
cap absent for its effective default of five.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
cap absent for its effective default of five. `readRegistry()` is a pure,
|
|
69
|
+
bounded atomic-file snapshot: it takes no lock and writes nothing, projecting
|
|
70
|
+
legacy implicit one to absent and `capsVersion: 2` only in memory. Its no-follow
|
|
71
|
+
regular-file descriptor accepts an atomic rename between stat and open, so it
|
|
72
|
+
returns a complete old or new snapshot; other bounded readers retain their
|
|
73
|
+
identity check. Invalid present schedule or trigger caps are refused.
|
|
74
|
+
Registration, unregistration and cap updates reread under `registry.lock` and
|
|
75
|
+
compare against the raw snapshot, so even unchanged membership persists the
|
|
76
|
+
migration. A successful write removing legacy one emits a stderr notice naming
|
|
77
|
+
the one-slot restoration command. `writeRegistry()` remains a raw atomic writer;
|
|
78
|
+
read-modify-write callers must supply the lock and reread. Later explicit one
|
|
79
|
+
stays explicit, including one reintroduced by an older writer: the stored
|
|
80
|
+
marker cannot distinguish its provenance. The trigger cap stays independent.
|
|
81
|
+
|
|
82
|
+
The shared mkdir lock retries every occupied-directory window within the
|
|
83
|
+
caller's retry deadline, including owner-file publication and removal. It never
|
|
84
|
+
removes a contender's lock or steals from a live, dead or unreadable owner;
|
|
85
|
+
persistent contention ends in the caller's existing busy error.
|
|
86
|
+
|
|
87
|
+
Spawn compensation returns its diagnostic and a structural uncertainty flag;
|
|
88
|
+
only an incomplete result marks the original error's `details.unconfirmed`.
|
|
89
|
+
CLI wrappers preserve that field, and the operation runner promotes a provider's
|
|
90
|
+
literal true marker while retaining the nested envelope. Message-based consumers
|
|
91
|
+
remain during the additive producer migration; do not replace stage evidence
|
|
92
|
+
with a substring check or infer uncertainty from a retained home alone.
|
|
93
|
+
|
|
94
|
+
The schedule child supervisor installs SIGINT/SIGTERM/SIGHUP handlers before
|
|
95
|
+
launch and removes them on settlement. All catchable shutdown signals share
|
|
96
|
+
its idempotent TERM/KILL path; the first stop cause is retained. The private
|
|
97
|
+
receipt records interruption independently of both the first stop cause and
|
|
98
|
+
the direct child's observed exit, so a signal during overflow cleanup still
|
|
99
|
+
keeps an otherwise valid envelope unconfirmed. Group probes
|
|
100
|
+
start at leader exit; an observed-empty group is permanently excluded from
|
|
101
|
+
later probes/signals. Polling cannot eliminate the gap before observation or
|
|
102
|
+
prove away PID reuse. Escaped sessions are outside the owned group, and
|
|
103
|
+
SIGKILL/OOM or unrecoverable supervisor death cannot be cleaned up by handlers;
|
|
104
|
+
a missing receipt supplies no child-exit evidence and cannot release a slot.
|
|
105
|
+
|
|
106
|
+
Session input keeps terminal operations separate from display observation.
|
|
107
|
+
The pre-Enter budget starts at paste completion, before best-effort buffer
|
|
108
|
+
cleanup; post-Enter observation starts after the single key command returns.
|
|
109
|
+
Both use a monotonic clock, clip sleeps and read-only capture subprocess
|
|
110
|
+
budgets, and stop observing on failure or exhaustion. Capture subprocesses use
|
|
111
|
+
SIGKILL on timeout so an ignored TERM cannot extend a probe; terminal command
|
|
112
|
+
timeouts/errors are unchanged. Screen comparison can set only the observational
|
|
113
|
+
`verified` boolean, never send another key or report terminal failure. Tests
|
|
114
|
+
use inert command runners and clocks; they do not qualify broker delivery or
|
|
115
|
+
harness acceptance.
|
|
73
116
|
|
|
74
117
|
The kernel is runtime-neutral: nothing in `lib/` depends on a harness or on
|
|
75
118
|
a provider. Provider behaviour lives in capabilities; the kernel supplies
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"disabled": {
|
|
100
100
|
"description": "Workspace schedules this host does not run, by qualified id <member>/<id>, without a commit (`oats schedule disable <member>/<id>` writes it). Local schedules are enabled and disabled in oats-schedules.json.",
|
|
101
101
|
"type": "array", "uniqueItems": true,
|
|
102
|
-
"items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*/[a-z0-9-]{1,
|
|
102
|
+
"items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*/[a-z0-9-]{1,100}$" }
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
},
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"description": "The workspace triggers and schedules this host agrees to run, by qualified id <member>/<id>, or \"*\" for every one the workspace places on this host (0.30). A workspace automation runs only when its runsOn is host.name, its owner is this host's gh account AND trust admits it; absent or empty, none runs. A host fact: the committed workspace file refuses it. Local triggers and schedules need no trust.",
|
|
112
112
|
"oneOf": [
|
|
113
113
|
{ "const": "*" },
|
|
114
|
-
{ "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*/[a-z0-9-]{1,
|
|
114
|
+
{ "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*/[a-z0-9-]{1,100}$" } }
|
|
115
115
|
]
|
|
116
116
|
}
|
|
117
117
|
}
|
package/docs/official-catalog.md
CHANGED
|
@@ -9,7 +9,7 @@ or workspace membership alone does not make a package official.
|
|
|
9
9
|
|
|
10
10
|
| package | release | capabilities | package souls |
|
|
11
11
|
|---|---|---|---|
|
|
12
|
-
| `oats.framework` | `oats-framework/v1.6.
|
|
12
|
+
| `oats.framework` | `oats-framework/v1.6.1` (this repository) | `oats.core`, `oats.setup`, `oats.knowledge-theory` | `knowledge-theory-expert` |
|
|
13
13
|
| `oats.okf` | `v4.1.1` | `oats.okf` (knowledge), `oats.okf-harvest`, `oats.okf-maintenance` | `knowledge-harvester`, `knowledge-maintainer` |
|
|
14
14
|
| `oats.aweb` | `v1.21.1` | `oats.aweb` (messaging) | |
|
|
15
15
|
| `oats.engineering` | `v1.8.1` | `oats.engineering-expert`, `oats.developer`, `oats.code-review`, `oats.maintainer` | `code-reviewer` |
|
package/docs/packages.md
CHANGED
|
@@ -51,7 +51,7 @@ packages:
|
|
|
51
51
|
- **Bare version** (`v4.1.1`, `4.1.1`, `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.1.1` or `oats-framework/v1.6.
|
|
54
|
+
convention (`v4.1.1` or `oats-framework/v1.6.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
|
|
@@ -74,7 +74,7 @@ members:
|
|
|
74
74
|
- git:github.com/acme/agents
|
|
75
75
|
- git:github.com/acme/platform
|
|
76
76
|
packages:
|
|
77
|
-
oats.framework: v1.6.
|
|
77
|
+
oats.framework: v1.6.1
|
|
78
78
|
oats.okf: v4.1.1
|
|
79
79
|
oats.aweb: v1.21.1
|
|
80
80
|
teams:
|
|
@@ -333,11 +333,11 @@ A soul that names one of the package's capabilities with
|
|
|
333
333
|
"policy": "docs/official-catalog.md",
|
|
334
334
|
"packages": {
|
|
335
335
|
"oats.okf": { "url": "https://github.com/awebai/oats-okf.git", "ref": "v4.1.1", "path": "oats-package" },
|
|
336
|
-
"oats.framework": { "url": "https://github.com/awebai/oats.git", "ref": "oats-framework/v1.6.
|
|
336
|
+
"oats.framework": { "url": "https://github.com/awebai/oats.git", "ref": "oats-framework/v1.6.1", "path": "oats-package" }
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
```
|
|
340
340
|
|
|
341
|
-
`ref` carries the tag convention: a workspace's `oats.framework: v1.6.
|
|
342
|
-
resolves to tag `oats-framework/v1.6.
|
|
341
|
+
`ref` carries the tag convention: a workspace's `oats.framework: v1.6.1`
|
|
342
|
+
resolves to tag `oats-framework/v1.6.1`. Resolving through the catalog never
|
|
343
343
|
advances a lock by itself: `oats sync` does, and says so.
|
package/docs/release-lane.md
CHANGED
|
@@ -47,8 +47,8 @@ override; report the risk you accepted). It never touches the checkout it
|
|
|
47
47
|
runs from: it exports the SHA into a detached worktree under the system
|
|
48
48
|
temporary directory (recorded in `MANIFEST.json`, `--export <dir>` overrides)
|
|
49
49
|
and runs every build step there. The bumped manifests exist only in that
|
|
50
|
-
export; the version-bump commit
|
|
51
|
-
manual
|
|
50
|
+
export; the version-bump commit reaches `main` through the pull request the
|
|
51
|
+
workflow opens (or a manual one), merged by a maintainer.
|
|
52
52
|
|
|
53
53
|
`publish-npm` and `release-github` print their plan and refuse without
|
|
54
54
|
`--yes`. `tag` creates the local tag without `--yes` but pushes only with
|
|
@@ -117,8 +117,12 @@ Everything after `build` reads `MANIFEST.json` and the files already staged:
|
|
|
117
117
|
runs `release.yml`, whose steps are idempotent: it skips the live npm
|
|
118
118
|
versions, re-uploads the same assets, and attaches the attestations. That
|
|
119
119
|
later pass is the way to add provenance; nothing is republished.
|
|
120
|
-
- **The version-bump PR.** The workflow's final step
|
|
121
|
-
|
|
120
|
+
- **The version-bump PR.** The workflow's final step opens it and stops: the
|
|
121
|
+
run never merges into `main`. A maintainer reviews that the diff is the
|
|
122
|
+
version lines only and merges it. The PR is opened with the workflow token,
|
|
123
|
+
so no checks run on it by themselves; close and reopen it to run them, and
|
|
124
|
+
do not read missing checks as green. Open the PR by hand if the workflow
|
|
125
|
+
does not run.
|
|
122
126
|
- **Legs for hosts you do not have.** The Linux AppImage/DEB need a Linux
|
|
123
127
|
host; the lane says so and `stage` lists what is missing.
|
|
124
128
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# OATS 0.40.2
|
|
2
|
+
|
|
3
|
+
## Added
|
|
4
|
+
|
|
5
|
+
- **Unconfirmed spawn and operation failures carry structural evidence**
|
|
6
|
+
([#548](https://github.com/awebai/oats/issues/548)). Incomplete keyed spawns
|
|
7
|
+
and failed spawn compensation set `error.details.unconfirmed: true`; the
|
|
8
|
+
operation wrapper promotes a provider's literal true marker while retaining
|
|
9
|
+
its complete envelope. Completed compensation stays unmarked. Existing
|
|
10
|
+
text fallbacks and scheduler slot/reconcile behavior remain during this
|
|
11
|
+
additive migration; older copied providers are not upgraded by this change.
|
|
12
|
+
|
|
13
|
+
## Fixed
|
|
14
|
+
|
|
15
|
+
- **Schedule registry reads are lock-free and do not mutate state**
|
|
16
|
+
([#579](https://github.com/awebai/oats/issues/579)). Status and other readers
|
|
17
|
+
use coherent atomic-file snapshots, including during registry writes. Legacy
|
|
18
|
+
cap migration is projected in memory and persisted only by locked mutations,
|
|
19
|
+
with a one-slot restoration notice on stderr. Invalid stored schedule caps
|
|
20
|
+
now refuse instead of silently increasing capacity; explicit CLI cap options
|
|
21
|
+
can repair them. Old hand-set and implicit ones cannot be distinguished, nor
|
|
22
|
+
can current kernels identify a one reintroduced by an older writer sharing
|
|
23
|
+
the registry; stop mixed-version writes and explicitly set the desired cap.
|
|
24
|
+
Shared directory locks wait through transient owner publication/removal within
|
|
25
|
+
their retry deadline without stealing locks.
|
|
26
|
+
- **OATS Desktop: "Needs input" shows on a remote instance, and a withheld
|
|
27
|
+
note reads as the reason.** Desktop hid every remote claim: a remote roster
|
|
28
|
+
row carries no `runtimeState`, and Desktop read "not reported" as "not
|
|
29
|
+
running" (#582). An unreported state no longer hides the claim; a row
|
|
30
|
+
reported stopped, unreachable or unsupported, or one whose server did not
|
|
31
|
+
answer, still shows none. When a claim's note holds a link or text such as
|
|
32
|
+
`token: …`, Desktop does not show it. The row's description, the terminal
|
|
33
|
+
tab's title and the hover card now show the reason in words, such as "Asked
|
|
34
|
+
you a question", where they showed "[Detail withheld]" (#584). The activity
|
|
35
|
+
view still marks such a note as withheld, and `oats status` prints it in
|
|
36
|
+
full.
|
|
37
|
+
- **A remote instance's "needs input" reaches the roster**
|
|
38
|
+
([#582](https://github.com/awebai/oats/issues/582)). `oats server roster`
|
|
39
|
+
built a remote row from a fixed list of facts and dropped the host's
|
|
40
|
+
`waitingOnYou`. A row now carries it when, and only when, the host's kernel
|
|
41
|
+
reports it: a row from a host before 0.40.0, and a saved route the host did
|
|
42
|
+
not list, have no such key, so "not reported" stays distinct from `null`
|
|
43
|
+
("no claim"). The value passes the kernel's read rule again on this side;
|
|
44
|
+
anything that is not a claim is `null`. See
|
|
45
|
+
[the remote roster](../desktop-cli-api.md#the-remote-roster-oats-server-roster---json).
|
|
46
|
+
- **"Needs input" in a deployment addressed through a symlink**
|
|
47
|
+
([#583](https://github.com/awebai/oats/issues/583)). A home there has two
|
|
48
|
+
spellings: `oats status --dir <symlink>` addressed it lexically, while a
|
|
49
|
+
started or restarted session carries the real path. Event rows are keyed by
|
|
50
|
+
the home, so the session's claims never showed on status, a restart did not
|
|
51
|
+
void a claim written under the other spelling, and a clear through one
|
|
52
|
+
spelling found nothing. Rows are now stored and matched under the home's
|
|
53
|
+
real path, whichever spelling a writer or reader uses, and both spellings
|
|
54
|
+
use one workspace log, the deployment's own `.agents/events`. Answers keep
|
|
55
|
+
the spelling they were asked in: the `home` in `oats instance events --json`
|
|
56
|
+
(the top-level field and each row's) and in the `oats instance waiting` and
|
|
57
|
+
`oats instance attention` answers is the home as the caller addressed it.
|
|
58
|
+
Deployments not reached through a symlink see no change.
|
|
59
|
+
|
|
60
|
+
Rows an earlier kernel wrote under the lexical spelling are not rewritten
|
|
61
|
+
and not matched: after the upgrade they are foreign, counted in
|
|
62
|
+
`integrity.foreignRows`. In a symlinked deployment that means:
|
|
63
|
+
- a claim that was live under the lexical spelling (a session that was
|
|
64
|
+
spawned and never restarted) stops showing. It shows again only when it
|
|
65
|
+
is made anew: at the agent's next permission prompt, question or
|
|
66
|
+
`oats instance attention`. A restart starts a new session with no claim;
|
|
67
|
+
- a claim that stayed set because its restart was recorded under the real
|
|
68
|
+
path is gone, not stuck;
|
|
69
|
+
- the rows a started or restarted session wrote under the real path are
|
|
70
|
+
read now. They cannot show a stale claim: such a session wrote its clears
|
|
71
|
+
and its session boundaries under the real path too.
|
|
72
|
+
- **oats.core 2.4.1: the Claude Code emitter no longer speaks for another
|
|
73
|
+
instance** ([#584](https://github.com/awebai/oats/issues/584)). Its hook
|
|
74
|
+
script took the home from `$OATS_INSTANCE_HOME`. A Claude process that
|
|
75
|
+
loaded one home's `.claude/settings.json` while carrying another instance's
|
|
76
|
+
environment (a nested `claude -p`, a `claude -p` started with its working
|
|
77
|
+
directory in another home, a pane that inherited the variables) set and
|
|
78
|
+
cleared that other instance's claim, and its `Stop` and `SessionEnd` clears
|
|
79
|
+
could erase a real one. The launch hook now writes the home's real path
|
|
80
|
+
into each hook command, and the script acts only when `$OATS_INSTANCE_HOME`
|
|
81
|
+
names that home, through any spelling; otherwise it does nothing. Ships in
|
|
82
|
+
oats.framework 1.6.1 (compatibility unchanged; catalog and workspace pin
|
|
83
|
+
`oats-framework/v1.6.1`): a workspace's `oats.framework: v1.6.1` resolves
|
|
84
|
+
to that tag through the official catalog, and a workspace pinned to v1.6.0
|
|
85
|
+
keeps oats.core 2.4.0 until it moves the pin and syncs. A home picks it up
|
|
86
|
+
at its next spawn. See [capabilities](../capabilities.md).
|
|
87
|
+
- **A waiting row whose time is not a date is no claim**
|
|
88
|
+
([#584](https://github.com/awebai/oats/issues/584)). The reader validated a
|
|
89
|
+
stored claim's producer, reason and message but passed its time through.
|
|
90
|
+
And `oats help` lists `[--dir <d>]` for `oats instance waiting`.
|
|
91
|
+
- **Catchable scheduler-supervisor shutdown cleans up its owned child group**
|
|
92
|
+
([#580](https://github.com/awebai/oats/issues/580)). SIGINT, SIGTERM and SIGHUP
|
|
93
|
+
enter the existing bounded TERM/KILL cleanup once, preserving observed child
|
|
94
|
+
exit evidence and treating interrupted envelopes as unconfirmed. The group
|
|
95
|
+
is checked when its leader exits and never signalled again after it is seen
|
|
96
|
+
empty, reducing but not eliminating process-group ID reuse races. Escaped
|
|
97
|
+
sessions and unrecoverable supervisor deaths such as SIGKILL/OOM remain
|
|
98
|
+
outside that cleanup guarantee; no receipt means no proven child exit, so
|
|
99
|
+
the unknown attempt and host slot remain pending reconciliation.
|
|
100
|
+
- **Workspace schedule opt-outs and named trust accept 100-character IDs**
|
|
101
|
+
([#581](https://github.com/awebai/oats/issues/581)). Host configuration now
|
|
102
|
+
accepts the full schedule name limit in `schedules.disabled` and
|
|
103
|
+
`automations.trust`, so enabling/disabling and trusting schedules with
|
|
104
|
+
41–100-character IDs works through the CLI and workspace placement checks.
|
|
105
|
+
Trigger definitions and `triggers.disabled` retain their 40-character limit;
|
|
106
|
+
member syntax, allowed characters and uniqueness rules are unchanged.
|
|
107
|
+
|
|
108
|
+
## Changed
|
|
109
|
+
|
|
110
|
+
- **Session input restores single-paste, single-Enter terminal semantics**
|
|
111
|
+
([#562](https://github.com/awebai/oats/issues/562)). Corrects 0.39.4's
|
|
112
|
+
screen-derived extra Enters and `enter-not-taken` refusal after successful
|
|
113
|
+
terminal commands. Pre-Enter settling remains, with a shared monotonic
|
|
114
|
+
2-second observation budget; at most two read-only post-Enter looks share
|
|
115
|
+
1 second. Each probe and sleep is limited by its remaining budget.
|
|
116
|
+
`submitted: true` reports terminal-operation success; `verified` reports
|
|
117
|
+
display change only, and false never authorizes retry. Neither proves model
|
|
118
|
+
acceptance, pending draft state or absence of effects. Command errors and
|
|
119
|
+
authority checks are unchanged; busy-pane submission, exactly-once delivery
|
|
120
|
+
and generic-error retry uncertainty are not solved by this correction.
|