@voybio/ace-swarm 2.4.4 → 2.4.6
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/CHANGELOG.md +69 -0
- package/README.md +72 -532
- package/assets/eval-battery/battery.json +85 -0
- package/assets/eval-battery/fixtures/edit-target.js +3 -0
- package/assets/scripts/render-mcp-configs.sh +12 -0
- package/dist/ace-context.d.ts +18 -0
- package/dist/ace-context.js +161 -16
- package/dist/ace-server-instructions.js +36 -9
- package/dist/bridge-trace.d.ts +17 -0
- package/dist/bridge-trace.js +23 -0
- package/dist/cli.d.ts +9 -1
- package/dist/cli.js +619 -10
- package/dist/discovery-runtime-wrappers.d.ts +16 -1
- package/dist/discovery-runtime-wrappers.js +65 -9
- package/dist/eval-harness.d.ts +110 -0
- package/dist/eval-harness.js +254 -0
- package/dist/experience-reflector.d.ts +13 -0
- package/dist/experience-reflector.js +62 -0
- package/dist/goal-engine.d.ts +155 -0
- package/dist/goal-engine.js +795 -0
- package/dist/helpers/store-resolution.js +40 -39
- package/dist/hermes/session-manager.d.ts +10 -0
- package/dist/hermes/session-manager.js +130 -27
- package/dist/job-scheduler.d.ts +58 -0
- package/dist/job-scheduler.js +202 -2
- package/dist/job-worker.d.ts +87 -0
- package/dist/job-worker.js +182 -0
- package/dist/local-model-policy.d.ts +8 -2
- package/dist/local-model-policy.js +31 -17
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +130 -2
- package/dist/measured-classing.d.ts +66 -0
- package/dist/measured-classing.js +165 -0
- package/dist/model-bridge.d.ts +45 -0
- package/dist/model-bridge.js +502 -44
- package/dist/orchestrator-supervisor.js +1 -1
- package/dist/plan-proposal.d.ts +7 -0
- package/dist/plan-proposal.js +74 -4
- package/dist/playbook-adaptation.d.ts +37 -0
- package/dist/playbook-adaptation.js +159 -0
- package/dist/playbook-curator.d.ts +16 -0
- package/dist/playbook-curator.js +106 -0
- package/dist/playbook-llm-reflector.d.ts +26 -0
- package/dist/playbook-llm-reflector.js +157 -0
- package/dist/public-surface.d.ts +2 -0
- package/dist/public-surface.js +26 -14
- package/dist/safe-edit.d.ts +20 -0
- package/dist/safe-edit.js +355 -78
- package/dist/server.d.ts +11 -2
- package/dist/server.js +55 -5
- package/dist/shared.d.ts +12 -0
- package/dist/shared.js +13 -1
- package/dist/source-mutation-coordinator.d.ts +54 -0
- package/dist/source-mutation-coordinator.js +259 -0
- package/dist/status-events.d.ts +4 -1
- package/dist/status-events.js +41 -6
- package/dist/store/ace-packed-store.d.ts +28 -9
- package/dist/store/ace-packed-store.js +324 -57
- package/dist/store/bgzf.d.ts +4 -0
- package/dist/store/bgzf.js +94 -0
- package/dist/store/bootstrap-store.d.ts +4 -1
- package/dist/store/bootstrap-store.js +57 -3
- package/dist/store/cache-workspace.d.ts +8 -0
- package/dist/store/cache-workspace.js +67 -1
- package/dist/store/crc32.d.ts +2 -0
- package/dist/store/crc32.js +16 -0
- package/dist/store/fsck.d.ts +16 -0
- package/dist/store/fsck.js +240 -0
- package/dist/store/materializers/context-snapshot-materializer.d.ts +1 -0
- package/dist/store/materializers/context-snapshot-materializer.js +4 -1
- package/dist/store/materializers/projection-manager.d.ts +7 -0
- package/dist/store/materializers/projection-manager.js +37 -0
- package/dist/store/materializers/scheduler-projection-materializer.js +10 -3
- package/dist/store/repositories/goal-repository.d.ts +112 -0
- package/dist/store/repositories/goal-repository.js +157 -0
- package/dist/store/repositories/playbook-repository.d.ts +107 -0
- package/dist/store/repositories/playbook-repository.js +314 -0
- package/dist/store/store-artifacts.js +44 -2
- package/dist/store/store-authority-audit.js +1 -0
- package/dist/store/store-cli.d.ts +30 -0
- package/dist/store/store-cli.js +242 -0
- package/dist/store/store-snapshot.d.ts +12 -0
- package/dist/store/store-snapshot.js +74 -11
- package/dist/store/types.d.ts +1 -0
- package/dist/store/write-coordinator.d.ts +7 -0
- package/dist/store/write-coordinator.js +11 -0
- package/dist/todo-state.d.ts +21 -0
- package/dist/todo-state.js +150 -53
- package/dist/tools-agent.js +86 -22
- package/dist/tools-discovery.js +5 -3
- package/dist/tools-files.js +39 -18
- package/dist/tools-framework.js +31 -3
- package/dist/tools-goal.d.ts +11 -0
- package/dist/tools-goal.js +108 -0
- package/dist/tools-handoff.js +7 -11
- package/dist/tools-lifecycle.js +2 -0
- package/dist/tools-scheduler.js +44 -1
- package/dist/tools-skills.js +3 -2
- package/dist/tools-todo.js +64 -3
- package/dist/tools.js +2 -0
- package/dist/tui/chat.js +4 -0
- package/dist/tui/index.js +4 -1
- package/dist/tui/ollama.d.ts +2 -0
- package/dist/tui/openai-compatible.d.ts +14 -0
- package/dist/tui/openai-compatible.js +15 -2
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +2 -2
- package/docs/cli-reference.md +125 -0
- package/docs/development.md +46 -0
- package/docs/evaluation-safety.md +27 -0
- package/docs/experience-playbook.md +35 -0
- package/docs/hermes.md +129 -0
- package/docs/library-api.md +31 -0
- package/docs/mcp-tool-reference.md +290 -0
- package/docs/provider-security.md +85 -0
- package/docs/state-model.md +113 -0
- package/docs/troubleshooting.md +95 -0
- package/docs/tui-operator-guide.md +75 -0
- package/package.json +3 -1
- package/scripts/dev/run_hermes_crispr_live.mjs +212 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# ACEPACK State Model
|
|
2
|
+
|
|
3
|
+
> Companion to the [ACEPACK summary in the README](../README.md#acepack-state-model). This document describes the implemented v2 format, its authority boundary, and safe maintenance behavior.
|
|
4
|
+
|
|
5
|
+
ACE's canonical generated workflow state is stored in `agent-state/ace-state.ace`. Older workspaces may still contain `.agents/ACE/ace-state.ace`; that path is a read fallback, not a second authority. If both files exist, `ace fsck` reports a dual-store conflict so the operator can migrate deliberately instead of guessing which file to delete.
|
|
6
|
+
|
|
7
|
+
One intentional exception remains: active goal records and plans under `agent-state/goals/` are authoritative loose files so detached goal processes can coordinate without rewriting the packed store. A complete backup therefore includes both `agent-state/ace-state.ace` and `agent-state/goals/` when that directory exists.
|
|
8
|
+
|
|
9
|
+
## On-disk regions
|
|
10
|
+
|
|
11
|
+
ACEPACK v2 has four regions:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
ace-state.ace
|
|
15
|
+
├── Header (128 bytes, big-endian)
|
|
16
|
+
│ magic · version · flags
|
|
17
|
+
│ kv_index_offset · kv_index_length · kv_chunk_end
|
|
18
|
+
│ evt_offset · evt_length · evt_count · evt_base_id
|
|
19
|
+
├── KV Chunk Region
|
|
20
|
+
│ knowledge/agents/{name}/{file} → agent instruction text
|
|
21
|
+
│ knowledge/skills/{name}/{file} → skill content
|
|
22
|
+
│ topology/{kind} → swarm registry, route tables
|
|
23
|
+
│ state/{...} → handoffs, todo, ledger, scheduler
|
|
24
|
+
│ meta/{...} → schema version, project name
|
|
25
|
+
│ each chunk: [uint32 length][bytes]
|
|
26
|
+
├── Columnar Event Section (rewritten on commit)
|
|
27
|
+
│ uint32 event count
|
|
28
|
+
│ int64[N] timestamps (epoch ms, big-endian)
|
|
29
|
+
│ uint8[N] kinds (EntityKind enum)
|
|
30
|
+
│ uint8[N] sources (ContentSource enum)
|
|
31
|
+
│ uint8[N] flags (0x01 = deleted)
|
|
32
|
+
│ padding to 4-byte alignment
|
|
33
|
+
│ uint32[N] payload offsets (relative to payload pool)
|
|
34
|
+
│ uint32[N] payload lengths
|
|
35
|
+
│ uint32 pool length
|
|
36
|
+
│ bytes[M] payload pool (UTF-8 JSON blobs)
|
|
37
|
+
└── KV Index (JSON, rewritten on every store write)
|
|
38
|
+
key → { offset, length, crc? }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The header field definitions live in `src/store/types.ts`; serialization and bounds checks live in `src/store/ace-packed-store.ts` and `src/store/fsck.ts`.
|
|
42
|
+
|
|
43
|
+
## KV reads and writes
|
|
44
|
+
|
|
45
|
+
The KV index makes a single-key read an indexed seek plus the selected chunk read: approximately `O(chunk_length)` after the index is loaded. CRC-bearing entries are verified on read, and `ace fsck` also verifies region ordering, length prefixes, bounds, and CRCs.
|
|
46
|
+
|
|
47
|
+
The writer is append-structured, but the file is **not appended in place**:
|
|
48
|
+
|
|
49
|
+
- Synchronous operational writes retain old KV chunks, append replacement chunks in the new KV region, copy the current event section, write a new index, and atomically rename a complete temporary file.
|
|
50
|
+
- Async `AcePackedStore.commit()` rebuilds the live KV region, serializes the complete hot event set, writes a complete temporary file, and atomically renames it.
|
|
51
|
+
- `compact()` similarly rewrites the complete container with only live KV chunks.
|
|
52
|
+
|
|
53
|
+
Consequently, filesystem write cost is proportional to the retained KV bytes plus hot event bytes and the index—not `O(1)`. Atomic rename prevents readers from observing a partially promoted file on supported local filesystems, but it also changes the inode. Writers are serialized through the store write coordinator; bypassing that coordinator is unsupported.
|
|
54
|
+
|
|
55
|
+
## Columnar events
|
|
56
|
+
|
|
57
|
+
The physical event layout separates fixed-width columns from JSON payloads and uses about 19 fixed bytes per hot event plus payload bytes. The current JavaScript reader eagerly loads and parses hot-event payloads when opening/scanning a store, so the layout's theoretical timestamp/kind-only scan is not yet exposed by the production API. Do not size current scans as 9 bytes per event.
|
|
58
|
+
|
|
59
|
+
Events survive `compact()`. `archive/events/*` may contain older contiguous event batches compressed with BGZF and indexed by `archive/events/__index`; `AcePackedStore.archiveColdEvents()` implements this tier, while the normal CLI does not invoke it automatically.
|
|
60
|
+
|
|
61
|
+
## Authority and projections
|
|
62
|
+
|
|
63
|
+
The packed store is authoritative for generated operational artifacts, agent instructions, skills, topology, schemas, scheduler state, handoffs, todos, run ledger, and event history. Host-facing files are materialized only through explicit projection allowlists.
|
|
64
|
+
|
|
65
|
+
Exceptions and boundaries:
|
|
66
|
+
|
|
67
|
+
- `agent-state/goals/*.json`, plan JSON, and detached goal logs are authoritative loose goal state.
|
|
68
|
+
- Source code and user-authored project files remain ordinary workspace files.
|
|
69
|
+
- `.agents/ACE/ace-state.ace` is legacy fallback state and should not coexist indefinitely with the canonical file.
|
|
70
|
+
- `ACE_STORE_FIRST=0` is a legacy diagnostic escape hatch; it can expose stale disk projections and is not the normal precedence.
|
|
71
|
+
|
|
72
|
+
## Maintenance and recovery
|
|
73
|
+
|
|
74
|
+
### Inspect before changing
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
ace fsck --json
|
|
78
|
+
ace ls agent-state
|
|
79
|
+
ace read agent-state/STATUS.md --source
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`ace fsck` diagnoses binary structure, CRCs, stale write leases, orphan temporary files, and dual-store conflicts. `--repair` removes only repairable stale/orphan coordination artifacts; it does not reconstruct corrupt application data.
|
|
83
|
+
|
|
84
|
+
### Compact
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
ace compact --no-sweep # store rewrite only
|
|
88
|
+
ace compact # store rewrite plus loose generated-artifact sweep
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The default command runs a sweep after compaction: eligible loose generated ACE artifacts are ingested into the store and removed from disk. Use `--no-sweep` when only the container rewrite is intended. Back up the store and active goal directory before maintenance on a critical workspace.
|
|
92
|
+
|
|
93
|
+
### Repair
|
|
94
|
+
|
|
95
|
+
`ace repair` re-bakes packaged assets, repairs known store-backed projections, and restores host files. It is not a general corrupt-ACEPACK byte repair tool. Review every warning and rerun `ace fsck`; recover unrecoverable corruption from a known-good backup or a quarantined pre-write copy.
|
|
96
|
+
|
|
97
|
+
When a write refuses corrupt source bytes, ACE leaves the original in place and writes a timestamped `.corrupt-*` quarantine copy. Do not delete either copy until the cause and recovery source are established.
|
|
98
|
+
|
|
99
|
+
### Migrate
|
|
100
|
+
|
|
101
|
+
`ace migrate [store-path]` imports the supported legacy ACE state families: handoffs, todos, run-ledger entries, status events, and Vericify posts. It is not an arbitrary workspace-file importer. Verify the resulting canonical store with `ace fsck`, then archive the legacy store rather than leaving both authorities active.
|
|
102
|
+
|
|
103
|
+
## Backup contract
|
|
104
|
+
|
|
105
|
+
For a recoverable snapshot:
|
|
106
|
+
|
|
107
|
+
1. Stop active ACE writers and detached goal workers.
|
|
108
|
+
2. Copy `agent-state/ace-state.ace`.
|
|
109
|
+
3. Copy `agent-state/goals/` if present.
|
|
110
|
+
4. Record `ace fsck --json` output and the store checksum.
|
|
111
|
+
5. Preserve any `.corrupt-*` quarantine until recovery is verified.
|
|
112
|
+
|
|
113
|
+
Restoring only the `.ace` file while omitting active loose goal records is not a complete workspace-state restore.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
Start with the smallest command that distinguishes configuration, packed-state, provider, and model failures:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
ace paths
|
|
7
|
+
ace fsck
|
|
8
|
+
ace doctor --scan
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Store and bootstrap
|
|
12
|
+
|
|
13
|
+
### No ACE store found
|
|
14
|
+
|
|
15
|
+
Run `ace init` or `ace turnkey` in the intended workspace. Confirm `ace paths` reports that directory as `WORKSPACE_ROOT`.
|
|
16
|
+
|
|
17
|
+
### Canonical and legacy stores both exist
|
|
18
|
+
|
|
19
|
+
`ace fsck` reports `store:legacy-dual-store`. Do not delete either file reflexively. Back up both, inspect their logical state with `ace ls`/`ace read`, migrate deliberately, verify the canonical store, then archive the legacy copy.
|
|
20
|
+
|
|
21
|
+
### Store/disk copies differ
|
|
22
|
+
|
|
23
|
+
Use full values, not abbreviations:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ace read agent-state/STATUS.md --source
|
|
27
|
+
ace read agent-state/STATUS.md --prefer store
|
|
28
|
+
ace read agent-state/STATUS.md --prefer disk
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Store-first is normal. `ACE_STORE_FIRST=0` is a legacy diagnostic escape hatch.
|
|
32
|
+
|
|
33
|
+
### Corruption or quarantine
|
|
34
|
+
|
|
35
|
+
Run `ace fsck --json`. A `.corrupt-*` file is a preserved copy, not disposable litter. `ace repair` re-bakes known assets and projections; it does not reconstruct arbitrary corrupt bytes. Restore from a verified backup when structural checks cannot be repaired safely.
|
|
36
|
+
|
|
37
|
+
## Providers
|
|
38
|
+
|
|
39
|
+
### Ollama unreachable or model missing
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
ollama serve
|
|
43
|
+
ollama pull <model>
|
|
44
|
+
ace doctor --llm ollama --model <model> --base-url http://127.0.0.1:11434
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`--repair-ollama` pulls a missing model and re-probes availability. Treat a nonzero doctor result as unresolved.
|
|
48
|
+
|
|
49
|
+
### llama.cpp endpoint mismatch
|
|
50
|
+
|
|
51
|
+
Use the OpenAI-compatible base, normally `http://127.0.0.1:8080/v1`, and check its model list before starting ACE.
|
|
52
|
+
|
|
53
|
+
### Hosted provider is discovered but chat fails
|
|
54
|
+
|
|
55
|
+
Discovery does not prove credentials. Confirm the provider's API key/base URL and run a provider-specific `ace doctor` command.
|
|
56
|
+
|
|
57
|
+
## Hermes
|
|
58
|
+
|
|
59
|
+
### Missing Python or Hermes checkout
|
|
60
|
+
|
|
61
|
+
Set `ACE_HERMES_ROOT` and either `HERMES_PYTHON`, `ACE_HERMES_UV_PROJECT`, or the advanced `ACE_HERMES_COMMAND_JSON`. Run `ace doctor ... --hermes` before the TUI or live driver.
|
|
62
|
+
|
|
63
|
+
### Live driver writes a report but no artifact
|
|
64
|
+
|
|
65
|
+
Inspect each report row's `status`, `job_done`, `protocol_done`, and `tool_write_done`. Any failed selected row makes the driver exit nonzero. A report path alone is not success.
|
|
66
|
+
|
|
67
|
+
### Hung turn
|
|
68
|
+
|
|
69
|
+
Interrupt the TUI with `Ctrl+C`. Hermes-local has an executor-owned turn timeout and child termination path; if the process survives, capture diagnostics and stop the session rather than launching a second concurrent mutation.
|
|
70
|
+
|
|
71
|
+
## Goals
|
|
72
|
+
|
|
73
|
+
### Goal remains planning or reports `contract_uncheckable`
|
|
74
|
+
|
|
75
|
+
Declare a required artifact or verification command:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
ace goal start "Implement the change" --verify "npm test"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Detached goal failure
|
|
82
|
+
|
|
83
|
+
Use `ace goal status <goal_id>` and inspect `agent-state/goals/<goal_id>.log`. Active goal files are authoritative loose state and must be included in backups.
|
|
84
|
+
|
|
85
|
+
## Evaluation
|
|
86
|
+
|
|
87
|
+
The packaged battery is mutating. `ace eval` creates isolated per-run workspaces and records provenance; do not point an explicit unsafe-workspace option at a valuable checkout. See [Evaluation Safety and Reproducibility](evaluation-safety.md).
|
|
88
|
+
|
|
89
|
+
## Package and development
|
|
90
|
+
|
|
91
|
+
If `npm pack` fails under `~/.npm/_cacache` with ownership errors, use a writable temporary npm cache for diagnosis. Do not change ownership or delete a shared cache without understanding the host policy.
|
|
92
|
+
|
|
93
|
+
## Escalation evidence
|
|
94
|
+
|
|
95
|
+
Include the exact command, exit code, relevant doctor/fsck checks, provider/model/base URL with secrets removed, and the logical artifact paths retrievable with `ace read`.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# TUI Operator Guide
|
|
2
|
+
|
|
3
|
+
The ACE terminal UI is an interactive operator surface over the same workspace state used by the MCP server.
|
|
4
|
+
|
|
5
|
+
## Start and preflight
|
|
6
|
+
|
|
7
|
+
Run doctor first so provider failures are separated from display or session failures:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ace doctor --scan
|
|
11
|
+
ace tui
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Pin a provider when auto-discovery is ambiguous:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
ace tui --provider ollama --model qwen3:8b --base-url http://127.0.0.1:11434
|
|
18
|
+
ace tui --provider llama.cpp --model local-model --base-url http://127.0.0.1:8080/v1
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For Codex, Claude, Gemini, and Copilot credentials, endpoints, default models, and provider-specific doctor commands, follow the [hosted provider quick setup](provider-security.md#hosted-provider-quick-setup) before starting a chat.
|
|
22
|
+
|
|
23
|
+
Hermes-local is opt-in and has additional prerequisites; see [Hermes Companion Engine](hermes.md).
|
|
24
|
+
|
|
25
|
+
## Input modes
|
|
26
|
+
|
|
27
|
+
- In normal mode, typing a printable character enters chat mode and preserves that character.
|
|
28
|
+
- `/` enters command mode.
|
|
29
|
+
- `Esc` returns to normal mode.
|
|
30
|
+
- In chat mode, a leading `/command` is routed to the command registry. Prefix with `//` to send a literal slash to the model.
|
|
31
|
+
- `Tab` and `Shift+Tab` cycle tabs.
|
|
32
|
+
- `Alt+1` through `Alt+9` switch directly to a tab.
|
|
33
|
+
|
|
34
|
+
Dashboard controls use Up/Down to select, Left/Right to change a value, and Enter to apply.
|
|
35
|
+
|
|
36
|
+
## Global shortcuts
|
|
37
|
+
|
|
38
|
+
| Shortcut | Action |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `Ctrl+C` | Interrupt the active stream; it does not quit an idle TUI |
|
|
41
|
+
| `Ctrl+D` | Quit |
|
|
42
|
+
| `Ctrl+N` | Open a chat tab |
|
|
43
|
+
| `Ctrl+R` | Refresh |
|
|
44
|
+
| `Ctrl+L` | Clear the current view |
|
|
45
|
+
| `Ctrl+W` | Close the current tab |
|
|
46
|
+
| `Ctrl+Z` | Suspend; resume with the shell's foreground command |
|
|
47
|
+
|
|
48
|
+
## Commands
|
|
49
|
+
|
|
50
|
+
Run `/help` for the authoritative command list. Current command families include:
|
|
51
|
+
|
|
52
|
+
- Provider/model: `/provider`, `/providers`, `/model`, `/models`, `/pull`, `/ollama`
|
|
53
|
+
- Agents/tasks: `/agent`, `/agents`, `/kill`, `/task`, `/tasks`, `/handoff`
|
|
54
|
+
- Approval/config: `/approve`, `/reject`, `/trust`, `/config`, `/theme`
|
|
55
|
+
- Skills: `/skill`, `/skills`
|
|
56
|
+
- Tabs/display: `/chat`, `/logs`, `/close`, `/archive`, `/clear`
|
|
57
|
+
- Lifecycle: `/status`, `/help`, `/quit`
|
|
58
|
+
|
|
59
|
+
Command completion is available while typing a slash command.
|
|
60
|
+
|
|
61
|
+
## Provider switching
|
|
62
|
+
|
|
63
|
+
`/provider <name>` changes the active provider and `/model <name>` changes the active model. A provider shown by discovery is not necessarily authenticated or reachable. Run `ace doctor` for the selected provider after changing credentials or endpoints.
|
|
64
|
+
|
|
65
|
+
## Failure and recovery
|
|
66
|
+
|
|
67
|
+
- If chat is rejected before a request, read the on-screen configuration diagnosis and run its suggested `ace doctor` command.
|
|
68
|
+
- If a direct stream stalls, `Ctrl+C` requests interruption. Confirm that the UI leaves streaming state before sending another task.
|
|
69
|
+
- Hermes-local owns a subprocess and uses its own timeout/abort path; a terminal result must contain a valid final frame and pass completion evidence checks.
|
|
70
|
+
- If the UI display is stale, use `Ctrl+R`; if the underlying state is suspect, exit and run `ace fsck`.
|
|
71
|
+
- Session state survives in ACEPACK, but an unverified model response is not executable authority.
|
|
72
|
+
|
|
73
|
+
## Non-interactive alternative
|
|
74
|
+
|
|
75
|
+
Use the CLI or MCP tools for automation. The TUI is interactive and should not be used as a CI success signal.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voybio/ace-swarm",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.6",
|
|
4
4
|
"description": "ACE Framework MCP server and CLI — single-file ACEPACK state, local-model serving, agent orchestration, and host compliance enforcement.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"dist/**/*.js",
|
|
23
23
|
"dist/**/*.d.ts",
|
|
24
24
|
"scripts/hermes_bridge_worker.py",
|
|
25
|
+
"scripts/dev/run_hermes_crispr_live.mjs",
|
|
25
26
|
"assets",
|
|
27
|
+
"docs",
|
|
26
28
|
"README.md",
|
|
27
29
|
"CHANGELOG.md"
|
|
28
30
|
],
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
const workspaceRoot = process.env.ACE_WORKSPACE_ROOT || resolve(process.cwd(), "..");
|
|
8
|
+
const packageRoot = process.cwd();
|
|
9
|
+
const distRoot = resolve(packageRoot, "dist");
|
|
10
|
+
const reportDir = resolve(workspaceRoot, "agent-state", "hermes-crispr-live");
|
|
11
|
+
mkdirSync(reportDir, { recursive: true });
|
|
12
|
+
|
|
13
|
+
const timestamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\..+$/, "Z");
|
|
14
|
+
const reportPath = resolve(reportDir, `${timestamp}.json`);
|
|
15
|
+
const ollamaArtifact = "agent-state/hermes-crispr-live/crispr-ollama.html";
|
|
16
|
+
const llamaArtifact = "agent-state/hermes-crispr-live/crispr-llama-cpp.html";
|
|
17
|
+
const pythonCommand =
|
|
18
|
+
process.env.HERMES_PYTHON;
|
|
19
|
+
if (!pythonCommand) {
|
|
20
|
+
console.error("HERMES_PYTHON is required; point it at the Python environment containing the operator-supplied Hermes checkout.");
|
|
21
|
+
process.exit(2);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { runLocalModelTask } = await import(pathToFileURL(resolve(distRoot, "local-model-runtime.js")).href);
|
|
25
|
+
const { HermesSubprocessExecutor } = await import(pathToFileURL(resolve(distRoot, "hermes", "session-manager.js")).href);
|
|
26
|
+
|
|
27
|
+
function htmlTask({ provider, model, artifact }) {
|
|
28
|
+
if (process.env.ACE_LIVE_OUTPUT_ONLY === "1") {
|
|
29
|
+
return [
|
|
30
|
+
"Output only a complete self-contained HTML document about CRISPR.",
|
|
31
|
+
"Start with <!DOCTYPE html> and end with </html>.",
|
|
32
|
+
"No markdown fences. No explanation. No tool code.",
|
|
33
|
+
`Runtime label to include visibly: ${provider} / ${model}`,
|
|
34
|
+
"Include inline CSS in <style> and inline JavaScript in <script>.",
|
|
35
|
+
"Include short sections: mechanism, applications, risks/ethics.",
|
|
36
|
+
"Include a small interactive quiz or toggle implemented in JavaScript.",
|
|
37
|
+
"No external assets, CDNs, or network URLs.",
|
|
38
|
+
"Keep it concise.",
|
|
39
|
+
`ACE will save your output to ${artifact}.`,
|
|
40
|
+
].join("\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
"You are running as an ACE coder in Hermes-local mode.",
|
|
45
|
+
`Provider: ${provider}`,
|
|
46
|
+
`Model: ${model}`,
|
|
47
|
+
"",
|
|
48
|
+
"Create a concise, complete, self-contained HTML file about CRISPR.",
|
|
49
|
+
`Use the ACE MCP tool write_workspace_file to write exactly this workspace path: ${artifact}`,
|
|
50
|
+
"",
|
|
51
|
+
"Hard requirements for the file:",
|
|
52
|
+
"- It must be a single .html document with inline CSS and inline JavaScript.",
|
|
53
|
+
"- It must include sections on mechanism, applications, risks/ethics, and a short interactive quiz or toggle implemented in JavaScript.",
|
|
54
|
+
"- It must visibly identify which runtime produced it.",
|
|
55
|
+
"- It must not reference external assets, CDNs, or network resources.",
|
|
56
|
+
"- Keep it under 180 lines.",
|
|
57
|
+
"- Return completed only after write_workspace_file succeeds, and cite the tool evidence.",
|
|
58
|
+
"- If the tool is unavailable, output only the full HTML document starting with <!DOCTYPE html> and ending with </html> so ACE can materialize it for inspection.",
|
|
59
|
+
].join("\n");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function artifactStats(relativePath) {
|
|
63
|
+
const absolutePath = resolve(workspaceRoot, relativePath);
|
|
64
|
+
if (!existsSync(absolutePath)) {
|
|
65
|
+
return { path: relativePath, exists: false };
|
|
66
|
+
}
|
|
67
|
+
const content = readFileSync(absolutePath, "utf-8");
|
|
68
|
+
return {
|
|
69
|
+
path: relativePath,
|
|
70
|
+
exists: true,
|
|
71
|
+
bytes: Buffer.byteLength(content),
|
|
72
|
+
hasHtml: /<html[\s>]/i.test(content),
|
|
73
|
+
hasCss: /<style[\s>]/i.test(content),
|
|
74
|
+
hasJs: /<script[\s>]/i.test(content),
|
|
75
|
+
mentionsCrispr: /crispr/i.test(content),
|
|
76
|
+
externalRefs: [...content.matchAll(/\b(?:https?:)?\/\//gi)].length,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function extractHtml(text) {
|
|
81
|
+
if (typeof text !== "string") return "";
|
|
82
|
+
const start = text.search(/<!doctype html|<html[\s>]/i);
|
|
83
|
+
const end = text.search(/<\/html>/i);
|
|
84
|
+
if (start === -1 || end === -1 || end <= start) return "";
|
|
85
|
+
return text.slice(start, end + "</html>".length);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function materializeHermesHtmlIfNeeded(row, relativePath) {
|
|
89
|
+
const stats = artifactStats(relativePath);
|
|
90
|
+
if (stats.exists && !row.artifact_preexisting) return stats;
|
|
91
|
+
const html = extractHtml(row.summary);
|
|
92
|
+
if (!html) return { ...stats, stale: Boolean(row.artifact_preexisting) };
|
|
93
|
+
writeFileSync(resolve(workspaceRoot, relativePath), html, "utf-8");
|
|
94
|
+
row.materialized_from_hermes_final = true;
|
|
95
|
+
row.materialization_note =
|
|
96
|
+
"Hermes/local model produced a complete HTML payload but did not execute the ACE write tool; the live driver materialized the final payload so the job artifact can be inspected separately from tool-call success.";
|
|
97
|
+
return artifactStats(relativePath);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const selectedProviders = String(process.env.ACE_LIVE_PROVIDERS || "ollama,llama.cpp")
|
|
101
|
+
.split(",")
|
|
102
|
+
.map((entry) => entry.trim())
|
|
103
|
+
.filter(Boolean);
|
|
104
|
+
|
|
105
|
+
const runs = [
|
|
106
|
+
{
|
|
107
|
+
label: "ollama",
|
|
108
|
+
provider: "ollama",
|
|
109
|
+
model:
|
|
110
|
+
process.env.ACE_LIVE_OLLAMA_MODEL ||
|
|
111
|
+
"hf.co/Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF:Q2_K",
|
|
112
|
+
baseUrl: process.env.OLLAMA_HOST || "http://127.0.0.1:11434",
|
|
113
|
+
artifact: ollamaArtifact,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
label: "llama.cpp",
|
|
117
|
+
provider: "llama.cpp",
|
|
118
|
+
model: process.env.ACE_LIVE_LLAMA_CPP_MODEL || "gemma-4-E2B-it-UD-Q2_K_XL.gguf",
|
|
119
|
+
baseUrl: process.env.ACE_LIVE_LLAMA_CPP_BASE_URL || "http://127.0.0.1:8080/v1",
|
|
120
|
+
artifact: llamaArtifact,
|
|
121
|
+
},
|
|
122
|
+
].filter((run) => selectedProviders.includes(run.provider) || selectedProviders.includes(run.label));
|
|
123
|
+
|
|
124
|
+
const report = {
|
|
125
|
+
schema: "ace-hermes-crispr-live@1",
|
|
126
|
+
generated_at: new Date().toISOString(),
|
|
127
|
+
workspace_root: workspaceRoot,
|
|
128
|
+
package_root: packageRoot,
|
|
129
|
+
python_command: pythonCommand,
|
|
130
|
+
rows: [],
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
function persist() {
|
|
134
|
+
writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const run of runs) {
|
|
138
|
+
const started = Date.now();
|
|
139
|
+
const row = {
|
|
140
|
+
...run,
|
|
141
|
+
engine: "hermes_local",
|
|
142
|
+
status: "not_run",
|
|
143
|
+
started_at: new Date(started).toISOString(),
|
|
144
|
+
artifact_preexisting: artifactStats(run.artifact).exists,
|
|
145
|
+
};
|
|
146
|
+
report.rows.push(row);
|
|
147
|
+
persist();
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const result = await runLocalModelTask({
|
|
151
|
+
workspaceRoot,
|
|
152
|
+
provider: run.provider,
|
|
153
|
+
model: run.model,
|
|
154
|
+
baseUrl: run.baseUrl,
|
|
155
|
+
engine: "hermes_local",
|
|
156
|
+
modelClass: "small_local",
|
|
157
|
+
role: "coders",
|
|
158
|
+
maxTurns: Number(process.env.ACE_LIVE_HERMES_MAX_TURNS || 8),
|
|
159
|
+
tier: "brief",
|
|
160
|
+
toolScope: ["write_workspace_file"],
|
|
161
|
+
task: htmlTask(run),
|
|
162
|
+
hermesExecutor: new HermesSubprocessExecutor({ pythonCommand }),
|
|
163
|
+
});
|
|
164
|
+
row.status = result.result.status;
|
|
165
|
+
row.summary = result.result.summary;
|
|
166
|
+
row.turns = result.result.turns;
|
|
167
|
+
row.tool_calls = result.result.tool_calls;
|
|
168
|
+
row.evidence_refs = result.result.evidence_refs || [];
|
|
169
|
+
row.policy = result.policy;
|
|
170
|
+
row.runtime = result.runtime;
|
|
171
|
+
row.hermes = {
|
|
172
|
+
bridge_protocol_version: result.hermes?.bridge_protocol_version,
|
|
173
|
+
hermes_session_id: result.hermes?.hermes_session_id,
|
|
174
|
+
shadow_mcp_session_id: result.hermes?.shadow_mcp_session_id,
|
|
175
|
+
execution_engine: result.hermes?.execution_engine,
|
|
176
|
+
provider: result.hermes?.provider,
|
|
177
|
+
model: result.hermes?.model,
|
|
178
|
+
event_count: result.hermes?.events?.length || 0,
|
|
179
|
+
event_types: [...new Set((result.hermes?.events || []).map((event) => event.type))],
|
|
180
|
+
};
|
|
181
|
+
} catch (error) {
|
|
182
|
+
row.status = "error";
|
|
183
|
+
row.summary = error instanceof Error ? error.stack || error.message : String(error);
|
|
184
|
+
} finally {
|
|
185
|
+
row.duration_ms = Date.now() - started;
|
|
186
|
+
row.finished_at = new Date().toISOString();
|
|
187
|
+
row.artifact = materializeHermesHtmlIfNeeded(row, run.artifact);
|
|
188
|
+
row.job_done = Boolean(
|
|
189
|
+
row.artifact?.exists &&
|
|
190
|
+
!row.artifact?.stale &&
|
|
191
|
+
row.artifact.hasHtml &&
|
|
192
|
+
row.artifact.hasCss &&
|
|
193
|
+
row.artifact.hasJs &&
|
|
194
|
+
row.artifact.mentionsCrispr &&
|
|
195
|
+
row.artifact.externalRefs === 0
|
|
196
|
+
);
|
|
197
|
+
row.protocol_done = Boolean(
|
|
198
|
+
row.hermes?.event_count > 0 &&
|
|
199
|
+
row.hermes.event_types?.includes("session_open") &&
|
|
200
|
+
row.hermes.event_types?.includes("session_close")
|
|
201
|
+
);
|
|
202
|
+
row.tool_write_done = Boolean(
|
|
203
|
+
row.tool_calls?.some((tool) => tool.tool === "write_workspace_file" && tool.ok)
|
|
204
|
+
);
|
|
205
|
+
persist();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log(reportPath);
|
|
210
|
+
if (report.rows.length === 0 || report.rows.some((row) => row.status === "error" || !row.job_done || !row.protocol_done)) {
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|