@awebai/oats 0.22.19 → 0.23.0

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.
Files changed (38) hide show
  1. package/README.md +6 -2
  2. package/bin/oats.mjs +24 -10
  3. package/docs/design/2026-09-08-expert-assisted-deployment-proposal.md +558 -0
  4. package/docs/design/2026-09-13-knowledge-and-memory-direction.md +744 -0
  5. package/docs/design/2026-09-13-knowledge-implementation.md +127 -0
  6. package/docs/design/2026-09-13-knowledge-location-contract.md +340 -0
  7. package/docs/design/package-runtime-api.md +177 -3
  8. package/docs/desktop-cli-api.md +1 -1
  9. package/docs/execution-targets.md +16 -0
  10. package/docs/knowledge-capability-authoring.md +98 -0
  11. package/docs/knowledge-reference/acceptance.md +108 -0
  12. package/docs/knowledge-reference/adoption.md +61 -0
  13. package/docs/knowledge-reference/harvester.md +107 -0
  14. package/docs/knowledge-reference/model.md +84 -0
  15. package/docs/knowledge-reference/package-craft.md +126 -0
  16. package/docs/knowledge-reference/provider-mapping.md +77 -0
  17. package/docs/knowledge-reference/reader-capture.md +87 -0
  18. package/docs/knowledge-theory.md +20 -6
  19. package/docs/layers.md +8 -7
  20. package/docs/oats-config.schema.json +5 -2
  21. package/docs/release-notes/v0.23.0.md +93 -0
  22. package/docs/souls-and-instances.md +17 -1
  23. package/injects/work-directory.md +18 -0
  24. package/lib/core.mjs +279 -56
  25. package/lib/schedule.mjs +12 -2
  26. package/package.json +2 -2
  27. package/packages/record/README.md +19 -0
  28. package/packages/record/bin/capture.mjs +96 -48
  29. package/packages/record/bin/recall.mjs +17 -11
  30. package/packages/record/bin/record-native-start.mjs +11 -0
  31. package/packages/record/lib/capture-cc.mjs +82 -27
  32. package/packages/record/lib/capture-lock.mjs +15 -2
  33. package/packages/record/lib/formats.mjs +108 -21
  34. package/packages/record/lib/native-history.mjs +87 -0
  35. package/packages/record/lib/session-roots.mjs +90 -0
  36. package/packages/record/lib/session-snapshot.mjs +61 -0
  37. package/packages/record/lib/sessions-for-home.mjs +88 -56
  38. package/skills/oats/SKILL.md +3 -1
package/README.md CHANGED
@@ -137,8 +137,11 @@ workspace view where reading, editing, Git, builds, tests, and commits happen.
137
137
 
138
138
  Work modes: `worktree` (isolated branch for implementation), `checkout` (the
139
139
  repository's shared checkout), `attached` (another instance's tree, for
140
- service agents and reviewers), and `workspace` (read-only multi-repository
141
- context). Placement that cannot be proved fails closed.
140
+ service agents and reviewers), `workspace` (read-only multi-repository context),
141
+ and explicit `directory` (owned non-Git execution for independent workers;
142
+ `repo` supplies configuration only). Directory mode rejects `--work-dir` and
143
+ `--branch`, and retirement preserves nonempty work in verified recovery storage.
144
+ Placement that cannot be proved fails closed.
142
145
 
143
146
  Provider behavior stays deliberate. Pi runs with ambient skill, context, and
144
147
  template discovery curtailed while operator-configured extensions remain
@@ -307,6 +310,7 @@ forms. Do not hand-edit the lock or installed stores.
307
310
  - [Migration from OAS](docs/migration-from-oas.md)
308
311
  - [Release notes](docs/release-notes/)
309
312
  - [Architecture proposal, 2026-09-03](docs/2026-09-03-architecture-proposal.md): components, contracts, and what may be replaced (proposal, not shipped behavior)
313
+ - [Expert-assisted deployment proposal, 2026-09-08](docs/design/2026-09-08-expert-assisted-deployment-proposal.md): setup/repair skills, packaged preparation, live maintenance, and implementation handoff (proposal, not shipped behavior)
310
314
  - [iPhone agent management proposal](docs/design/2026-09-07-mobile-agent-management-proposal.md): private server access through Tailscale, mobile UX, and delivery phases (proposal, not shipped behavior)
311
315
 
312
316
  ## Contributing
package/bin/oats.mjs CHANGED
@@ -21,7 +21,7 @@ import { createHash } from "node:crypto";
21
21
  import { fileURLToPath } from "node:url";
22
22
  import { enableTmuxMouse, tmuxConfigPath, tmuxMouseEnabled } from "../lib/tmux-config.mjs";
23
23
  import {
24
- LAYERS, LEGACY_HOME_CAPABILITIES_DIR, OATS_LOCK_FILE, OATS_VERSION, OAS_SCOPE_REMEDY, RETIRED_CAPABILITIES, detectOasScopes, retiredCapabilityReason, configChain, configCapabilityEntries, manifestOperations,
24
+ LAYERS, WORK_MODES, LEGACY_HOME_CAPABILITIES_DIR, OATS_LOCK_FILE, OATS_VERSION, OAS_SCOPE_REMEDY, RETIRED_CAPABILITIES, detectOasScopes, retiredCapabilityReason, configChain, configCapabilityEntries, manifestOperations,
25
25
  acquireCapability, restoreCapabilities, marketplaceCapabilities,
26
26
  capabilityManifests, capabilityManifest, capabilityMissingRequires, capabilityIntegrity, capabilityTrust, capabilityExecutablePath,
27
27
  readCapabilityLocks, writeCapabilityLock,
@@ -988,7 +988,7 @@ function doctor(dir) {
988
988
  if (r.injects.length === 0) console.log(" (none)");
989
989
  for (const inj of r.injects) console.log(` ${inj.source}: ${shortPath(inj.file)}`);
990
990
 
991
- for (const mode of ["worktree", "checkout", "attached", "workspace"]) {
991
+ for (const mode of WORK_MODES) {
992
992
  const wm = resolveWorkMode(ctx, mode);
993
993
  console.log(`\nWork mode ${mode}: inject ${wm.inject ? shortPath(wm.inject) : "none"}${wm.setup ? `, setup ${shortPath(wm.setup)}` : ""}`);
994
994
  }
@@ -3532,8 +3532,14 @@ function spawnCmd() {
3532
3532
  const yolo = yoloFlag();
3533
3533
  const backend = valueFlag("backend"), herdrSocket = valueFlag("herdr-socket");
3534
3534
  if (backend !== undefined && !["tmux", "herdr"].includes(backend)) bail("E_BAD_ARGS", "--backend must be tmux or herdr");
3535
+ const requestedWork = valueFlag("work");
3536
+ const workDir = valueFlag("work-dir"), branch = valueFlag("branch"), repo = valueFlag("repo");
3537
+ const checkDirectoryOptions = (work) => {
3538
+ if (work === "directory" && (workDir !== undefined || branch !== undefined)) bail("E_BAD_ARGS", "--work directory owns only <home>/work; --work-dir and --branch are not allowed");
3539
+ };
3540
+ checkDirectoryOptions(requestedWork); // before a local soul could be upserted
3535
3541
  const name = args[1];
3536
- if (!name || name.startsWith("--")) bail("E_USAGE", "usage: oats spawn <agent> [--task <text>|--task-file <f>] [--purpose <slug>] [--relation child|sibling|parent|unrelated --relative-to <instance> [--relative-root <agents-root>]] [--parent <instance>] [--repo <r>] [--work worktree|checkout|attached|workspace] [--work-dir <owner-work>] [--runtime pi|claude|codex] [--backend tmux|herdr] [--herdr-socket <path>] [--yolo|--no-yolo] [--model <m>] [--branch <b>] [--instructions-file <f>|--def-file <f>] [--no-launch] [--json]");
3542
+ if (!name || name.startsWith("--")) bail("E_USAGE", "usage: oats spawn <agent> [--task <text>|--task-file <f>] [--purpose <slug>] [--relation child|sibling|parent|unrelated --relative-to <instance> [--relative-root <agents-root>]] [--parent <instance>] [--repo <r>] [--work worktree|checkout|attached|workspace|directory] [--work-dir <owner-work>] [--runtime pi|claude|codex] [--backend tmux|herdr] [--herdr-socket <path>] [--yolo|--no-yolo] [--model <m>] [--branch <b>] [--instructions-file <f>|--def-file <f>] [--no-launch] [--json]");
3537
3543
  // Retired boundary flags (maintainer transport ruling): fail LOUDLY before
3538
3544
  // ANY side effect — including root discovery and local-agent upsert (an
3539
3545
  // --instructions-file spawn must not scaffold/overwrite a local soul before
@@ -3566,6 +3572,7 @@ function spawnCmd() {
3566
3572
  note(`(cross-repo: soul "${name}" found at ${shortPath(root)} — instance homes there)`);
3567
3573
  }
3568
3574
  }
3575
+ checkDirectoryOptions(requestedWork || agent?.work);
3569
3576
  // local agents: create/update from raw instructions or a single-file def
3570
3577
  if (instrFile || defFile || !agent) {
3571
3578
  if (!agent && !instrFile && !defFile) {
@@ -3651,8 +3658,11 @@ function spawnCmd() {
3651
3658
  try {
3652
3659
  r = spawnInstance(root, agent, {
3653
3660
  purpose: flag("purpose"), task: taskText, taskFile: taskFileFlag, relation, relativeTo, relativeRoot,
3654
- repo: flag("repo") || agent.repo || defaultRepo(workspaceOf(root)) || defaultRepo(process.cwd()),
3655
- work: flag("work"), workDir: flag("work-dir"), runtime: flag("runtime"), backend, herdrSocket, yolo, model: flag("model"), branch: flag("branch"),
3661
+ // Directory execution uses deployment configuration, not an ambient Git
3662
+ // checkout (especially when invoked via --dir from a source instance).
3663
+ repo: (requestedWork || agent.work) === "directory"
3664
+ ? (repo ?? agent.repo) : repo || agent.repo || defaultRepo(workspaceOf(root)) || defaultRepo(process.cwd()),
3665
+ work: requestedWork, workDir, runtime: flag("runtime"), backend, herdrSocket, yolo, model: flag("model"), branch,
3656
3666
  launchConfig: valueFlag("launch-config"),
3657
3667
  launch: !args.includes("--no-launch"),
3658
3668
  });
@@ -3666,7 +3676,7 @@ function spawnCmd() {
3666
3676
  // model, runtime) is a fact about the selection, not a spawn-mechanism
3667
3677
  // failure: it keeps its own code so a GUI can act on it.
3668
3678
  if (typeof e?.code === "string" && /^E_LAUNCH_|^E_MODEL_UNKNOWN$|^E_UNSUPPORTED_RUNTIME$/.test(e.code)) { bail(e.code, e.message); throw e; }
3669
- bail(e.code === "E_RELATIVE_AMBIGUOUS" ? "E_RELATIVE_AMBIGUOUS" : "E_SPAWN_FAILED", e.message || e); throw e;
3679
+ bail(["E_BAD_ARGS", "E_RELATIVE_AMBIGUOUS"].includes(e.code) ? e.code : "E_SPAWN_FAILED", e.message || e); throw e;
3670
3680
  }
3671
3681
  // The instance exists from here on: a failed wake save is reported beside
3672
3682
  // the full receipt, never hidden, and never causes a second spawn.
@@ -3863,7 +3873,7 @@ async function paneCmd() {
3863
3873
  function createCmd() {
3864
3874
  const yolo = yoloFlag();
3865
3875
  const name = args[1];
3866
- if (!name || name.startsWith("--")) die("usage: oats create <name> [--local] [--description <d>] [--type <agent-type>] [--repo <r>] [--work worktree|checkout|attached|workspace] [--runtime pi|claude|codex] [--model <m>] [--yolo|--no-yolo] [--instructions-file <f>]");
3876
+ if (!name || name.startsWith("--")) die("usage: oats create <name> [--local] [--description <d>] [--type <agent-type>] [--repo <r>] [--work worktree|checkout|attached|workspace|directory] [--runtime pi|claude|codex] [--model <m>] [--yolo|--no-yolo] [--instructions-file <f>]");
3867
3877
  const local = args.includes("--local");
3868
3878
  const startDir = dirFlag();
3869
3879
  // `create` BOOTSTRAPS a deployment: with no agents/ or local-agents/ yet,
@@ -3873,14 +3883,16 @@ function createCmd() {
3873
3883
  // `oats init` (a raw stack trace from ensureRoot). Local and committed souls
3874
3884
  // anchor the same way; writeSoul creates the directories.
3875
3885
  let root = findRoot(startDir);
3876
- let bootstrapped = false;
3886
+ // A configured package-only scope may resolve its future agents root before
3887
+ // that directory exists. Preserve create's bootstrap receipt/message.
3888
+ let bootstrapped = !!root && !existsSync(root) && !existsSync(join(dirname(root), "local-agents"));
3877
3889
  if (!root) {
3878
3890
  root = join(defaultRepo(startDir) || resolve(startDir), "agents");
3879
3891
  bootstrapped = true;
3880
3892
  }
3881
3893
  const instrFile = flag("instructions-file");
3882
3894
  const r = coreCreateAgent(root, {
3883
- name, local, description: flag("description"), type: flag("type"), repo: flag("repo") || defaultRepo(process.cwd()),
3895
+ name, local, description: flag("description"), type: flag("type"), repo: flag("repo") || (flag("work") === "directory" ? undefined : defaultRepo(process.cwd())),
3884
3896
  work: flag("work"), runtime: flag("runtime"), model: flag("model"), yolo,
3885
3897
  instructions: instrFile ? readFileSync(instrFile, "utf8") : undefined,
3886
3898
  });
@@ -4661,11 +4673,13 @@ Usage:
4661
4673
  [--relation child|sibling|parent|unrelated] --relation + --relative-to anchor the
4662
4674
  [--relative-to <instance>] new instance to an existing one; --parent X
4663
4675
  [--relative-root <agents-root>] disambiguates same-named team anchors
4664
- [--work worktree|checkout|attached|workspace] = sugar for --relative-to X --relation
4676
+ [--work worktree|checkout|attached|workspace|directory] = sugar for --relative-to X --relation
4665
4677
  [--work-dir <owner-work>] [--runtime pi|claude|codex] [--backend tmux|herdr] [--herdr-socket <path>] [--yolo|--no-yolo] [--model <m>] [--branch <b>] child (default: unrelated, top-level)
4666
4678
  [--instructions-file <f>|--def-file <f>] [--no-launch] [--json]
4667
4679
  with team: declared, unknown local souls
4668
4680
  resolve across the team scope's repos
4681
+ directory: owned home/work, config context may
4682
+ be non-Git; rejects --work-dir and --branch
4669
4683
  oats retire <instance> [--force] retire an instance (window, hooks,
4670
4684
  [--self] [--delete-branch] worktree, home); --self = retire the
4671
4685
  [--keep-dir] [--json] CALLING instance: the window dies, then