@hybridlabor-api/aos 4.7.0 → 4.7.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.
Files changed (37) hide show
  1. package/.agents/agents.md +8 -3
  2. package/.agents/nodes.json +2 -1
  3. package/.agents/state.schema.json +24 -0
  4. package/.agents/vendor-manifest.json +6 -0
  5. package/.agents/workflows/startcycle-graph.md +2 -2
  6. package/.claude/agents/architect.md +5 -4
  7. package/.claude/agents/techlead.md +2 -0
  8. package/.claude/workflows/startcycle-dispatch.mjs +175 -17
  9. package/.opencode/agents/architect.md +5 -4
  10. package/.opencode/agents/techlead.md +2 -0
  11. package/THIRD_PARTY_NOTICES.md +9 -1
  12. package/bin/aos-doctor.mjs +386 -0
  13. package/bin/aos-store.mjs +171 -0
  14. package/docs/AUDIT_BRIEF_ECC_STORE.md +122 -0
  15. package/docs/PLAN_AOS_STORE_PHASE1.md +190 -0
  16. package/installer.js +68 -15
  17. package/lib/aos-archify-contract.mjs +277 -0
  18. package/lib/ecc-store-index.json +1 -0
  19. package/mcp_config.json +4 -0
  20. package/mcps/mcsc/packages/core/src/adapters/codex.js +160 -37
  21. package/package.json +7 -2
  22. package/scripts/build-ecc-store-index.mjs +147 -0
  23. package/skills/basic/startcycle-graph/SKILL.md +13 -0
  24. package/skills/global_config/agenttrail/bin/agenttrail.mjs +153 -42
  25. package/skills/global_config/agenttrail/public/index.html +5 -4
  26. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +8 -2
  27. package/skills/global_config/archify/examples/dataflow-product-analytics.html +15048 -0
  28. package/skills/global_config/archify/examples/lifecycle-agent-run.html +14983 -0
  29. package/skills/global_config/archify/examples/sequence-cache-miss-request.html +15063 -0
  30. package/skills/global_config/archify/examples/web-app-rendered.html +15012 -0
  31. package/skills/global_config/archify/examples/workflow-agent-tool-call-rendered.html +15054 -0
  32. package/skills/global_config/ask-tim/SKILL.md +10 -0
  33. package/skills/global_config/mcsc/SKILL.md +60 -0
  34. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +31 -6
  35. package/skills/global_config/plan-canvas/scripts/plan-canvas.js +4 -2
  36. package/docs/handover/agenttrail-for-ao.md +0 -58
  37. package/docs/handover/deja-for-ao.md +0 -51
@@ -41,6 +41,16 @@ Reach for `test-driven-development` or `tdd-workflow` on their own when you want
41
41
 
42
42
  **Forcing a specific skill into a run:** `--skill=<name>` on any startcycle variant makes it a hard requirement — for a private skill of your own no node would otherwise reach for. Validated before the run starts; a name that does not resolve halts rather than proceeding without it.
43
43
 
44
+ ## Outside the core: the ECC store
45
+
46
+ When no native AOS skill fits, search the markdown-only ECC store before starting a pipeline:
47
+
48
+ - `aos store search <query>` finds available fallback skills and agents.
49
+ - `aos store install <name> --net` installs a verified item after explicit user action.
50
+ - `aos store list --type=skills` and `aos store list --type=agents` show the catalogue.
51
+ - The core remains the trusted AOS skill set; the store is a fallback, not a reason to bypass the startcycle validation.
52
+ - A missing `--skill` is reported by the dispatcher with the exact install command. It never downloads during a run.
53
+
44
54
  ---
45
55
 
46
56
  ## On-ramps
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: mcsc
3
+ description: Delegate a task to another installed CLI harness (Antigravity/agy, OpenCode, Codex) from inside an AOS repo. Use this instead of shelling out to that CLI directly whenever one is installed — it wraps the wrapper flags for you and streams live tool-call telemetry to agenttrail, which is why the board updates in real time. Reach for it any time you're about to delegate work to another harness.
4
+ category: bdb-core
5
+ metadata:
6
+ version: "0.1.0"
7
+ ---
8
+
9
+ # mcsc — Multi-CLI Subagent Configurator
10
+
11
+ mcsc (`mcps/mcsc/`) is AOS's own cross-harness delegation engine. It is the
12
+ mandatory path for delegating work to another CLI harness inside an
13
+ AOS-installed repo — not a Claude-Code-only convenience.
14
+
15
+ ## Why this exists
16
+
17
+ Three separate, harness-specific delegation paths already exist (the Claude
18
+ Code plugins `antigravity:antigravity-delegate`, `opencode:opencode-rescue`,
19
+ `codex:codex-rescue` — see AGENTS.md's "Delegating to an external CLI"). Those
20
+ are fine for interactive delegation *from a Claude Code session*, but they are
21
+ Claude-Code-only, and none of them tell agenttrail's live board what is
22
+ happening. mcsc's adapters
23
+ (`mcps/mcsc/packages/core/src/adapters/{agy,codex,opencode}.js`) wrap each CLI
24
+ once, work from any harness, and call `emitTrail()` around every delegated run
25
+ — `SessionStart`/`SessionEnd`, plus live `PreToolUse`/`PostToolUse` where the
26
+ underlying CLI supports structured/streaming output — so a delegated run shows
27
+ up on the board the moment it starts, without any extra setup.
28
+
29
+ Skipping mcsc doesn't just lose convenience — it makes the delegated agent
30
+ invisible to anyone watching the live map, and to anyone in this repo who asks
31
+ "what are the other agents doing right now."
32
+
33
+ ## How to use it
34
+
35
+ Once installed (`mcsc` in `mcp_config.json`, entry point
36
+ `mcps/mcsc/server.js`), it registers up to four MCP tools — one per
37
+ installed CLI, only exposed when that CLI is detected as `ready` in the local
38
+ inventory, and never offered back to a caller that's already running as that
39
+ same CLI (`MCSC_CALLER` env var, avoids self-delegation loops):
40
+
41
+ - `delegate_agy` — `{ prompt, model? }` → Antigravity / Gemini
42
+ - `delegate_opencode` — `{ prompt, model?, variant? }` → OpenCode
43
+ - `delegate_codex` — `{ prompt, model? }` → Codex
44
+ - `delegate_smart` — `{ task_type, prompt }` → reads `rulebook.yaml` (or the
45
+ bundled default) and picks the best CLI for that task type automatically
46
+
47
+ Call the tool that matches the harness you want, with a complete,
48
+ self-contained `prompt` — the adapter underneath handles the CLI's actual
49
+ flags, streams tool-call telemetry to agenttrail as it runs, and returns the
50
+ final output. `variant` (OpenCode only) maps to `--variant` (reasoning
51
+ effort).
52
+
53
+ ## What it is not
54
+
55
+ - Not a replacement for the Claude Code delegation plugins for a quick,
56
+ interactive, single-turn ask inside a Claude Code session — those remain
57
+ fine for that case.
58
+ - Not itself a review/QA step — verify a delegated result the same way
59
+ AGENTS.md already says to: read the actual diff, never trust the returned
60
+ status field alone.
@@ -97,6 +97,23 @@ function readJsonBody(req) {
97
97
  });
98
98
  }
99
99
 
100
+ function isWithin(root, candidate) {
101
+ const relativePath = path.relative(root, candidate);
102
+ return relativePath === '' || (relativePath !== '..' && !relativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(relativePath));
103
+ }
104
+
105
+ function confinedArtifactPath(file, workspaceRoot) {
106
+ if (typeof file !== 'string' || file.length === 0 || /^[a-z][a-z0-9+.-]*:\/\//i.test(file)) return null;
107
+ try {
108
+ const root = fs.realpathSync(path.resolve(workspaceRoot));
109
+ const candidate = fs.realpathSync(path.isAbsolute(file) ? file : path.resolve(root, file));
110
+ if (!isWithin(root, candidate) || !fs.statSync(candidate).isFile()) return null;
111
+ return candidate;
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+
100
117
  function sendJson(res, statusCode, payload) {
101
118
  const body = JSON.stringify(payload);
102
119
  res.writeHead(statusCode, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' });
@@ -120,6 +137,7 @@ function createPlanCanvasServer({
120
137
  idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS,
121
138
  heartbeatMs = 15000,
122
139
  thinkingStaleMs = DEFAULT_THINKING_STALE_MS,
140
+ workspaceRoot = process.cwd(),
123
141
  typingExpiryMs = DEFAULT_TYPING_EXPIRY_MS,
124
142
  presenceSweepMs = DEFAULT_PRESENCE_SWEEP_MS,
125
143
  onIdleShutdown = null,
@@ -287,10 +305,11 @@ function createPlanCanvasServer({
287
305
  if (!body.file || typeof body.file !== 'string') {
288
306
  return sendJson(res, 400, { error: 'file is required' });
289
307
  }
290
- if (!fs.existsSync(path.resolve(body.file))) {
291
- return sendJson(res, 404, { error: `artifact not found: ${body.file}` });
308
+ const artifactPath = confinedArtifactPath(body.file, workspaceRoot);
309
+ if (!artifactPath) {
310
+ return sendJson(res, 403, { error: 'artifact path is outside the workspace' });
292
311
  }
293
- const { session, refused } = store.open(body.file, { reopen: Boolean(body.reopen) });
312
+ const { session, refused } = store.open(artifactPath, { reopen: Boolean(body.reopen) });
294
313
  if (refused) {
295
314
  return sendJson(res, 409, {
296
315
  status: 'user-ended',
@@ -514,9 +533,15 @@ function createPlanCanvasServer({
514
533
 
515
534
  // Sibling assets resolve relative to the artifact's directory and must
516
535
  // stay confined to it.
517
- const baseDir = path.dirname(session.file);
518
- const resolved = path.resolve(baseDir, assetPath);
519
- if (resolved !== baseDir && !resolved.startsWith(baseDir + path.sep)) {
536
+ let baseDir;
537
+ let resolved;
538
+ try {
539
+ baseDir = fs.realpathSync(path.dirname(session.file));
540
+ resolved = fs.realpathSync(path.resolve(baseDir, assetPath));
541
+ } catch {
542
+ return sendJson(res, 404, { error: 'asset not found' });
543
+ }
544
+ if (!isWithin(baseDir, resolved)) {
520
545
  return sendJson(res, 403, { error: 'asset path escapes artifact directory' });
521
546
  }
522
547
  let data;
@@ -186,7 +186,8 @@ async function ensureServer({ stateDir, port }) {
186
186
  const child = spawn(process.execPath, [__filename, 'server', '--port', String(port)], {
187
187
  detached: true,
188
188
  stdio: ['ignore', logFd, logFd],
189
- env: { ...process.env, AOS_PLAN_CANVAS_STATE_DIR: stateDir }
189
+ env: { ...process.env, AOS_PLAN_CANVAS_STATE_DIR: stateDir },
190
+ shell: false
190
191
  });
191
192
  child.unref();
192
193
  fs.closeSync(logFd);
@@ -204,7 +205,7 @@ function openBrowser(url) {
204
205
  : platform === 'win32' ? ['cmd', ['/c', 'start', '', url]]
205
206
  : ['xdg-open', [url]];
206
207
  try {
207
- spawn(cmd, args, { detached: true, stdio: 'ignore' }).unref();
208
+ spawn(cmd, args, { detached: true, stdio: 'ignore', shell: false }).unref();
208
209
  return true;
209
210
  } catch {
210
211
  return false;
@@ -357,6 +358,7 @@ async function cmdServer(args, { stateDir, port }) {
357
358
  };
358
359
  const canvas = createPlanCanvasServer({
359
360
  store,
361
+ workspaceRoot: process.cwd(),
360
362
  host: hostArg || DEFAULT_HOST,
361
363
  version: VERSION,
362
364
  idleTimeoutMs: resolveIdleTimeoutMs(),
@@ -1,58 +0,0 @@
1
- # agenttrail for AO — waiting questions & GO/Nein handover
2
-
3
- AO (the Go daemon shipped as `@hybridlabor-api/bdb-agent-orchestrator`, binary at `~/.local/bin/ao`) can mirror what the agenttrail live map shows: an agent that waits on the human appears as a question with GO / Nein, and AO surfaces "Needs you" in its own UI — then types the human's decision into the waiting session (approved_plan_go.md:25, :57). agenttrail is the live-map daemon vendored into AOS at `skills/global_config/agenttrail/bin/agenttrail.mjs`, run as `aos-trail`; one daemon per repo, bound to 127.0.0.1 only. This document lists the HTTP surfaces AO may call and the things it must never do. The ask/answer endpoints are the AOS patch defined in production_artifacts/00_execution_plan.md ({#ask-engine}, lines 8-13) and production_artifacts/approved_plan_go.md; `/whoami` and `/model` already exist in the vendored daemon (agenttrail.mjs:583-584, :674-675).
4
-
5
- ## What AO calls
6
-
7
- ### 1. Find the repo's daemon — `GET /whoami`
8
-
9
- Probe `http://127.0.0.1:5330` through `5344` and match `repoPath` against the repo AO is working in — the same pattern the daemon itself uses for boot dedup (agenttrail.mjs:733-741):
10
-
11
- ```sh
12
- curl -s http://127.0.0.1:$p/whoami
13
- # {"project":"aos","port":5330,"repoPath":"/Users/tim/dev/bdb-dev/aos-wt-go"}
14
- ```
15
-
16
- Stop at the first match. If no daemon answers there is nothing to mirror — do not spawn a daemon from AO; the asker's CLI already prints `start: aos-trail . --plan …` on stderr (00_execution_plan.md:5). The `AGENTTRAIL_PORT` env var pins a single port and wins over the probe range (agenttrail.mjs:91).
17
-
18
- ### 2. Read open questions — `GET /model`, `GET /ask/<id>`, `GET /events`
19
-
20
- - `GET /model` carries `asks`: one object per question `{id, question, askedBy, askedAt, expiryAt, status: 'pending' | 'go' | 'deny', decidedAt, by, expired}`. Show every ask, oldest first (`askedAt` ascending — approved_plan_go.md:36).
21
- - `GET /ask/<id>` returns one ask plus the last 10 decisions — the "wer · wann · was" log (00_execution_plan.md:13).
22
- - `GET /events` is the SSE stream; activity ticks carry `asks` too (00_execution_plan.md:10), so AO can mirror live instead of polling.
23
-
24
- A pending question past `expiryAt` flips to `status: 'deny'` with `expired: true` — render it greyed out (abgelaufen), never as a live GO/Nein pair (00_execution_plan.md:52).
25
-
26
- ### 3. Answer — `POST /answer`
27
-
28
- ```sh
29
- curl -s -X POST http://127.0.0.1:$port/answer \
30
- -H 'Content-Type: application/json' \
31
- --data '{"id":"<ask-id>","decision":"go"}' # or "deny"
32
- ```
33
-
34
- - The `content-type` header must be exactly `application/json` — a missing or different header (`text/plain`, JSON with a charset suffix) is rejected (00_execution_plan.md:13).
35
- - The body `{id, decision}` is capped at 4 KB.
36
- - `origin`: browser callers always send it, and it must equal the daemon's own `http://localhost:<port>` or `http://127.0.0.1:<port>`; AO is a local non-browser caller and may omit the header entirely (00_execution_plan.md:51 — the curl above does). If AO sends an `origin`, it must be the daemon's own.
37
- - A valid answer sets the ask's status + `decidedAt` + `by: 'you'` and appends `{question, decision, by, at}` to the timestamped decision log; timeout denials land there too, as `by: 'timeout'` (00_execution_plan.md:13).
38
- - The daemon binds 127.0.0.1 only (agenttrail.mjs:700) and sets no `Access-Control-*` header and no `OPTIONS` handler anywhere in its route chain (agenttrail.mjs:572-689) — that absence is the CSRF protection for the map's browser buttons. AO must not ask for CORS to be relaxed.
39
-
40
- That is the whole surface: AO does not call the daemon's other mutating endpoints (`/spawn`, `/setup`, `/setup-board`, `/hook`) and never writes into the repo — the daemon itself never touches the repo; state lives under `~/.agenttrail` (agenttrail.mjs:307).
41
-
42
- ## What the answer means
43
-
44
- `aos-trail ask "<question>"` blocks until the question is answered or expires and exits 0 on go, 1 on deny/timeout (00_execution_plan.md:5). The default deadline is 30 minutes, overridable per question via the asker's `--timeout <10s|30m|2h|N>`; expiry auto-denies and is logged like a human decision (00_execution_plan.md:52). Questions and the decision log persist across daemon restarts in `~/.agenttrail/<repo-hash>.json` (agenttrail.mjs:308).
45
-
46
- ## Mirroring in AO's UI
47
-
48
- - Pending ask → a pinned "Needs you" card with the question, the asking agent (`askedBy`), elapsed time, and one GO and one Nein button.
49
- - AO answers `POST /answer` only after the human clicked or said the decision for that exact ask id, and only then may it type that decision into the waiting session (approved_plan_go.md:57 — AO already recognizes "Needs You GO" and can write to the terminal, :25).
50
- - Re-check `GET /ask/<id>` immediately before typing: the human may have answered on the map instead, or the question may have expired meanwhile.
51
-
52
- ## Hard rules
53
-
54
- - The AO button never authorizes `git push`, `npm publish`, `npm version` or recursive `rm` (approved_plan_go.md:30). Those four stay under go-gate's sole authority (`.claude/hooks/go-gate.mjs:43-49`), which opens only for the literal, human-typed `GO` as the last message in the session transcript (go-gate.mjs:10-13, transcript-only check in main() :116-151). The ask/answer flow only resolves explicit `aos-trail ask` questions and never writes to any session transcript — the map and AO remain "an observer and an answer relay, never a permission system" (00_execution_plan.md:50, approved_plan_go.md:49).
55
- - Never answer without the human. No batching, no defaults, no "the agent seemed sure" — one answer per explicit human decision, per question id.
56
- - Never auto-retry a timed-out question. Expiry → deny is final (`by: 'timeout'` in the log); only the human asking again starts a new question (approved_plan_go.md:57; the same "no silent retries / a fresh GO per action" rule the gate runs on, go-gate.mjs:13).
57
- - Never read `~/.agenttrail/<repo-hash>.json` or anything else inside `~/.agenttrail/` directly (agenttrail.mjs:307-308). Those files are internal state with no format contract — use the HTTP surfaces above.
58
- - Keep the CSRF contract intact: exact `Content-Type: application/json`, own-or-absent `origin`, and never relay an answer through web content the agent loaded.
@@ -1,51 +0,0 @@
1
- # deja for AO — session-memory handover
2
-
3
- AO (the Go daemon shipped as `@hybridlabor-api/bdb-agent-orchestrator`, binary at `~/.local/bin/ao`) can ask deja — the local session-memory engine installed together with memB by the AOS installer — for project history. This document lists the surfaces AO may call and the two things it must not do. Every command below is verified against deja-vu 0.21.1.
4
-
5
- ## What AO calls at session start
6
-
7
- Run in the project directory:
8
-
9
- ```sh
10
- deja wip --json
11
- ```
12
-
13
- The answer is what the last session in that directory was doing (deja-vu docs/json-output.md:1192-1223):
14
-
15
- ```json
16
- {
17
- "schema_version": 2,
18
- "session": "ses_fb76fff…",
19
- "harness": "opencode",
20
- "asked": "the orders worker is exhausting its database connections under load",
21
- "decision": "bound every orders-worker query with a context",
22
- "files": ["internal/worker/orders.go"],
23
- "command": "go test ./internal/worker/...",
24
- "command_failed": true,
25
- "lines": ["working on: …", "settled: …"]
26
- }
27
- ```
28
-
29
- - `schema_version: 2` is the only guaranteed field. Every other field is optional and omitted when the transcript does not carry it — treat all of them as nullable.
30
- - `lines` is the inject-verbatim form: one string per fact, in display order. A caller that wants to show the digest reads `lines` and does not re-assemble the fields.
31
-
32
- ## Untrusted historical data
33
-
34
- Everything deja returns is transcript-derived and may carry text an attacker influenced: a directive copied from a web page persists in the index and replays into later sessions. deja-vu wraps its own agent-facing recall in `<deja-recall>` markers with an untrusted-data preamble (deja-vu docs/SECURITY-MODEL.md:235-245). AO must label or wrap the content the same way before it reaches a model, and instructions inside it must not be followed.
35
-
36
- ## Other surfaces AO may use
37
-
38
- - `deja fix "<error>" --json` — what this machine ran after that error. Rows carry `command` or `edit`; `candidate: true` marks ran-next-but-unconfirmed evidence, not a guaranteed fix (deja-vu docs/json-output.md:763).
39
- - `deja last --json` — recent session metadata, never message bodies (deja-vu docs/json-output.md:200).
40
- - `deja handoff [id-prefix]` — the handoff digest for a session (deja-vu CLI reference, docs/guide/commands.html).
41
- - `deja blame <path> --json` — which sessions discussed a file; a top-level JSON array, no envelope (deja-vu docs/json-output.md:1107).
42
- - `deja doctor --json --offline` — index and store health; `--offline` keeps the version check off the network (deja-vu docs/json-output.md:356).
43
-
44
- ## Stability contract
45
-
46
- Envelope-shaped answers carry `schema_version: 2`. Within a version, changes are additive only: existing field names, types and meanings stay the same. Bumping the version signals a breaking change, so branch on `schema_version` before parsing the rest of the envelope (deja-vu docs/json-output.md:7-28). `deja blame --json` returns a bare array and carries no version; its element shapes are stable.
47
-
48
- ## Hard rules
49
-
50
- - Never read `~/.cache/deja/index.db` or anything else inside the index directory (`manifest.gob`, `sessions.gob`, `records.bin`, the buckets, the sidecars) directly. The format has moved forty-odd times and is explicitly not a contract (deja-vu docs/INTEGRATING.md:102-106). Use the CLI surfaces above.
51
- - AO must NOT run `deja install --auto` and must not wire session-start auto-recall. The approved AOS plan rules both out (production_artifacts/approved_plan_deja.md:61-66): a second writer into agent configs, and old history injected automatically at every session start, are exactly the injection vectors AOS installed deja to avoid. AO calls the `deja` CLI itself, on demand, in the project directory.