agents-handoff 0.0.0-stage → 2.0.3
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 +192 -0
- package/LICENSE +21 -0
- package/README.md +150 -2
- package/SKILL.md +147 -0
- package/capability-registry.json +27 -0
- package/docs/ARCHITECTURE.md +187 -0
- package/docs/CHANGELOG.md +196 -0
- package/docs/CLI.md +299 -0
- package/docs/COMPATIBILITY.md +124 -0
- package/docs/CONTRIBUTING.md +134 -0
- package/docs/FORMAT.md +185 -0
- package/docs/INSTALL.md +394 -0
- package/docs/INTEGRATION.md +188 -0
- package/docs/LEVEL4.md +202 -0
- package/docs/LEVEL5.md +96 -0
- package/docs/PERMISSIONS.md +145 -0
- package/docs/PROVENANCE.md +110 -0
- package/docs/SECURITY.md +93 -0
- package/docs/SESSIONS.md +97 -0
- package/docs/TROUBLESHOOTING.md +158 -0
- package/docs/UNINSTALL.md +148 -0
- package/docs/UPGRADE.md +177 -0
- package/docs/_config.yml +18 -0
- package/docs/_data/nav.yml +36 -0
- package/docs/_layouts/default.html +31 -0
- package/docs/assets/style.css +88 -0
- package/docs/index.md +92 -0
- package/docs/sessions.json +34 -0
- package/handoff.config.example.json +35 -0
- package/handoff.config.schema.json +117 -0
- package/install/CHANGELOG.md +48 -0
- package/install/README.md +76 -0
- package/install/install.mjs +1455 -0
- package/install/package.json +39 -0
- package/package.json +66 -4
- package/permission-policy.json +33 -0
- package/refs/ADAPTERS.md +33 -0
- package/refs/bootstrap.md +59 -0
- package/refs/brief-checklist.md +79 -0
- package/refs/handbook.md +58 -0
- package/refs/protocol.md +117 -0
- package/refs/roles.md +75 -0
- package/refs/validator.md +73 -0
- package/schemas/handoff.schema.json +275 -0
- package/skill.json +147 -0
- package/templates/HANDOFF.llm.schema.json +144 -0
- package/templates/HANDOFF.template.md +40 -0
- package/tests/acceptance/acceptance.yaml +209 -0
- package/tests/fixtures/minimal-transcript.jsonl +2 -0
- package/tools/agent-handoff.mjs +22 -0
- package/tools/agents-handoff.mjs +410 -0
- package/tools/capability-registry.mjs +120 -0
- package/tools/handoff.mjs +398 -0
- package/tools/handoff.test.mjs +668 -0
- package/tools/lib/handoff-root.mjs +161 -0
- package/tools/runtime-engine.mjs +330 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Provenance
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Provenance
|
|
6
|
+
|
|
7
|
+
## What this document covers
|
|
8
|
+
|
|
9
|
+
Every handoff carries a hash chain that ties its rendered files to the transcript it was built from.
|
|
10
|
+
This document states what is hashed, how to re-verify it, and what the check cannot prove.
|
|
11
|
+
|
|
12
|
+
## The chain
|
|
13
|
+
|
|
14
|
+
| Artifact | Field | Definition |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Source transcript | `raw_sha256` | SHA-256 of the whole source file, read as UTF-8 at build time |
|
|
17
|
+
| `manifest.json` | `manifest_sha256` | SHA-256 of `JSON.stringify(manifest)` with `manifest_sha256` removed |
|
|
18
|
+
| `HANDOFF.summary.json` | `provenance.raw_sha256`, `provenance.manifest_sha256` | Copies of the two hashes above |
|
|
19
|
+
| `HANDOFF.llm.json` | `provenance.manifest_sha256` | Copy of the manifest hash |
|
|
20
|
+
| `HANDOFF.md` | *Provenance* section | Both hashes and the revision, rendered as text |
|
|
21
|
+
|
|
22
|
+
`manifest.json` also records `source_paths` (every transcript ever merged into the session),
|
|
23
|
+
`watermark` (the highest turn sequence emitted), `revisions` and `turn_count`.
|
|
24
|
+
|
|
25
|
+
## Verification
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
node tools/handoff.mjs verify <id-prefix>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`verify` recomputes `manifest_sha256` from the stored manifest and compares it, then requires
|
|
32
|
+
`timeline.jsonl` to exist and to hold exactly `turn_count` lines, then parses `HANDOFF.llm.json`.
|
|
33
|
+
It prints one `PASS` line, or `FAIL` with the reason.
|
|
34
|
+
|
|
35
|
+
| Exit | Meaning |
|
|
36
|
+
|---|---|
|
|
37
|
+
| 0 | All three checks passed |
|
|
38
|
+
| 1 | Manifest mismatch, timeline missing, turn-count drift, or unparsable payload |
|
|
39
|
+
| 2 | No prefix given |
|
|
40
|
+
| 3 | Prefix matched more than one session |
|
|
41
|
+
| 4 | No session matched, or no handoffs exist |
|
|
42
|
+
|
|
43
|
+
## What the check detects
|
|
44
|
+
|
|
45
|
+
- A stored manifest whose fields have been edited or added.
|
|
46
|
+
- A truncated or padded `timeline.jsonl`, because its line count must equal `turn_count`.
|
|
47
|
+
- A missing timeline.
|
|
48
|
+
- A `HANDOFF.llm.json` that is no longer valid JSON.
|
|
49
|
+
|
|
50
|
+
## What the check cannot detect
|
|
51
|
+
|
|
52
|
+
- **Edits to unhashed renders.** `HANDOFF.md`, `HANDOFF.summary.json` and `TOOLS.md` are not hashed.
|
|
53
|
+
Their bytes can be changed freely and `verify` still passes.
|
|
54
|
+
- **Timeline content edits that preserve the line count.** A rewritten turn keeps the count.
|
|
55
|
+
- **Re-hashing by the editor.** The hashes are self-consistent, not signed. Anyone who edits the
|
|
56
|
+
manifest can recompute `manifest_sha256` and produce a folder that verifies clean. There is no key
|
|
57
|
+
material, no signature and no external trust anchor.
|
|
58
|
+
- **Anything outside the session folder.** `PROJECT.md`, `INDEX.json` and `links/*.md` carry no
|
|
59
|
+
hashes.
|
|
60
|
+
|
|
61
|
+
Treat the chain as damage detection, not as authentication.
|
|
62
|
+
|
|
63
|
+
## Install provenance
|
|
64
|
+
|
|
65
|
+
An installation carries its own record, `.agents-handoff-install.json`, written by the
|
|
66
|
+
installer into the copy it made. It is separate from every handoff folder: it says what was
|
|
67
|
+
installed and where the bytes came from, not what a session contains. `verify` run from the
|
|
68
|
+
installer re-hashes the installed file set and compares it with the record.
|
|
69
|
+
|
|
70
|
+
| Field group | What it records |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `product`, `version`, `installer_version`, `installed_at` | Which version landed, and which installer wrote the record. |
|
|
73
|
+
| `harness`, `target` | Where the copy went (`claude`, `codex`, `agents`, or `null` for a direct path). |
|
|
74
|
+
| `source` | The tree beside the installer (`kind: 'tree'`), or the tagged archive it was fetched from (`kind: 'archive'`) with that archive's sha256. |
|
|
75
|
+
| `file_count`, `files_sha256`, `files` | That the files present are the files that were installed — one hash over the set, and one per path. |
|
|
76
|
+
|
|
77
|
+
What it detects: a file edited, replaced or removed after the install, because the per-file
|
|
78
|
+
values and the set hash no longer match. `verify` exits non-zero and names the differing
|
|
79
|
+
files, and `doctor` reports the same state for every harness installation it finds.
|
|
80
|
+
|
|
81
|
+
What it cannot detect:
|
|
82
|
+
|
|
83
|
+
- **A compromised source.** The record is written from whatever tree the installer ran in, so a
|
|
84
|
+
copy made from a tampered tree carries a record that matches that tree.
|
|
85
|
+
- **Files outside the manifest.** The hash covers the manifest files, not anything added beside
|
|
86
|
+
them.
|
|
87
|
+
- **A rewritten record.** Anyone who edits an installed copy can recompute the hashes, exactly
|
|
88
|
+
as a manifest can be re-hashed. There is no signature and no external trust anchor.
|
|
89
|
+
|
|
90
|
+
## Update instead of recreate
|
|
91
|
+
|
|
92
|
+
Re-running `build` on the same session id merges rather than duplicates: turns with a sequence above
|
|
93
|
+
`watermark` are appended to `timeline.jsonl`, `revisions` increments, and the manifest hash is
|
|
94
|
+
recomputed. If the source is unchanged and no new turns exist, the run reports `up-to-date` and
|
|
95
|
+
bumps nothing. The rendered summary, payload and `HANDOFF.md` are regenerated from the full
|
|
96
|
+
timeline, so a revision never mixes stale and fresh text.
|
|
97
|
+
|
|
98
|
+
## Privacy note on `source_paths`
|
|
99
|
+
|
|
100
|
+
A manifest records the absolute path of every transcript that fed the session. A handoff folder
|
|
101
|
+
therefore contains the local paths of the machine it was built on. Review that field before sharing
|
|
102
|
+
a folder outside the machine.
|
|
103
|
+
|
|
104
|
+
## Repository provenance
|
|
105
|
+
|
|
106
|
+
- License: MIT, see [../LICENSE](https://github.com/Alot1z/agent-handoff/blob/main/LICENSE).
|
|
107
|
+
- The engine and runtime use only `node:` built-ins. No third-party source is bundled and
|
|
108
|
+
`package.json` declares no dependencies.
|
|
109
|
+
- Development notes, internal plans and research material are not part of this repository and are
|
|
110
|
+
not published with it.
|
package/docs/SECURITY.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Security
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Security
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
agents-handoff reads session transcripts you point it at and writes a handoff folder to disk. It has
|
|
10
|
+
no network code, no third-party dependencies, and no privileged operations. This document states
|
|
11
|
+
what the tool does with data, what it refuses to do, and which guarantees it does not make.
|
|
12
|
+
|
|
13
|
+
## What the tool reads
|
|
14
|
+
|
|
15
|
+
| Input | How it is used |
|
|
16
|
+
|---|---|
|
|
17
|
+
| The file passed to `build --source` | Read as UTF-8 text and parsed into turns. A `.jsonl` file is parsed line by line; anything else is parsed with role markers. |
|
|
18
|
+
| `handoff.config.json` | Walked up from the current directory and validated against `handoff.config.schema.json`. A present-but-invalid file is fatal (`exit 2`). |
|
|
19
|
+
|
|
20
|
+
Adapters that read a harness session store are external to the engine. They read stores and never
|
|
21
|
+
write to them. See [../refs/ADAPTERS.md](https://github.com/Alot1z/agent-handoff/blob/main/refs/ADAPTERS.md).
|
|
22
|
+
|
|
23
|
+
## What the tool writes
|
|
24
|
+
|
|
25
|
+
Under the resolved root (see [FORMAT.md](FORMAT.md)): `HANDOFF.md`, `HANDOFF.summary.json`,
|
|
26
|
+
`HANDOFF.llm.json`, `timeline.jsonl`, `TOOLS.md`, `manifest.json`, and per project `PROJECT.md`,
|
|
27
|
+
plus `INDEX.json` and `links/*.md`. Nothing else is written and nothing outside the root is
|
|
28
|
+
modified.
|
|
29
|
+
|
|
30
|
+
Writes go straight to the destination path with `fs.writeFileSync`. They are not atomic and are not
|
|
31
|
+
staged through a temporary file, so an interrupted run can leave a partial file. `verify` detects
|
|
32
|
+
that only indirectly — see [PROVENANCE.md](PROVENANCE.md).
|
|
33
|
+
|
|
34
|
+
## Secrets
|
|
35
|
+
|
|
36
|
+
The engine performs **no secret detection and no redaction**. A transcript containing a token or a
|
|
37
|
+
key produces a handoff containing that token or key. There is no scanning pass, no allowlist and no
|
|
38
|
+
masking.
|
|
39
|
+
|
|
40
|
+
`handoff.config.example.json` lists an `exclude_from_handoff` array. Treat that as intent, not as an
|
|
41
|
+
enforced control: the engine reads `storage.path`, `handoff_dir`, `project_name` and `linking` from a
|
|
42
|
+
config, and does not read that array.
|
|
43
|
+
|
|
44
|
+
Only hand off transcripts you have reviewed. Keep `.env` files and key material out of version
|
|
45
|
+
control, and do not commit a handoff folder that quotes them.
|
|
46
|
+
|
|
47
|
+
## Malicious input
|
|
48
|
+
|
|
49
|
+
A transcript is data. It is never evaluated, never executed, and never interpreted as configuration
|
|
50
|
+
or as a request. Concretely:
|
|
51
|
+
|
|
52
|
+
- A tool call recorded in a transcript is copied verbatim as text into `TOOLS.md`. The engine does not run it.
|
|
53
|
+
- A transcript cannot change the engine's arguments, the store root, or the exit code beyond a parse failure.
|
|
54
|
+
- A transcript with no parsable turns stops the run (`exit 4`). Individual malformed JSONL lines are skipped.
|
|
55
|
+
|
|
56
|
+
The realistic risk is not code execution but content. A handoff is a readable document that a later
|
|
57
|
+
human or agent may treat as instructions, and it inherits whatever instructions the transcript
|
|
58
|
+
contained. Review a handoff before handing it to another agent.
|
|
59
|
+
|
|
60
|
+
## Paths and the workspace boundary
|
|
61
|
+
|
|
62
|
+
The engine resolves `--source` against the current directory and writes under the resolved root. It
|
|
63
|
+
performs no workspace-boundary check of its own: a path you pass is a path it uses.
|
|
64
|
+
|
|
65
|
+
`permission-policy.json` declares the boundary for the runtime layer — approved workspaces,
|
|
66
|
+
read-only system roots, personal-data roots, per-level grants (`READ_ONLY` and `WORKSPACE_WRITE`
|
|
67
|
+
allowed; `EXTERNAL_EFFECT`, `DESTRUCTIVE` and `IRREVERSIBLE` need authorization) and the R0–R4 risk
|
|
68
|
+
mapping. See [PERMISSIONS.md](PERMISSIONS.md). The handoff engine itself does not consult that file.
|
|
69
|
+
|
|
70
|
+
## Guarantees that are not made
|
|
71
|
+
|
|
72
|
+
- No sandboxing. No process isolation, no privilege separation. The tool runs with the permissions of the user who invoked it.
|
|
73
|
+
- No encryption. Handoff files are plain text and JSON.
|
|
74
|
+
- No authentication. Hashes are self-consistent, not signed — see [PROVENANCE.md](PROVENANCE.md).
|
|
75
|
+
- No audit log and no system logging.
|
|
76
|
+
- No secret scanning, no PII detection, no redaction.
|
|
77
|
+
|
|
78
|
+
## Dependencies
|
|
79
|
+
|
|
80
|
+
None. The engine and the runtime use only `node:` built-ins and require Node 18 or newer. The
|
|
81
|
+
installer is a single script with no package dependencies.
|
|
82
|
+
|
|
83
|
+
## Reporting a vulnerability
|
|
84
|
+
|
|
85
|
+
Report it privately to the maintainer, not in a public issue. Use the repository's private
|
|
86
|
+
vulnerability reporting if it is enabled, otherwise contact the maintainer directly. Include the
|
|
87
|
+
command, the input, and the observed result.
|
|
88
|
+
|
|
89
|
+
## Checklist before publishing a handoff folder
|
|
90
|
+
|
|
91
|
+
- [ ] The transcript was reviewed and contains no credentials, tokens or personal data.
|
|
92
|
+
- [ ] The handoff quotes no private path and no machine identifier.
|
|
93
|
+
- [ ] `node tools/handoff.mjs verify <id-prefix>` passes for every session in the folder.
|
package/docs/SESSIONS.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Session index
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Session index
|
|
6
|
+
|
|
7
|
+
A handoff store is a directory of captured sessions. This page is generated from the
|
|
8
|
+
sample store in [`examples/sessions/`](https://github.com/Alot1z/agent-handoff/tree/main/examples/sessions), whose sessions the
|
|
9
|
+
engine built from the two transcripts this repository ships. Nothing here is written by hand:
|
|
10
|
+
the table below is a rendering of that store, re-checked on every build.
|
|
11
|
+
|
|
12
|
+
| Project | Session | Harness | Turns | Revision | Manifest sha256 | Integrity |
|
|
13
|
+
|---|---|---|---:|---:|---|---|
|
|
14
|
+
| demo | `typescript-project-setup` | claude-code | 11 | 1 | `141e232eb2ad…` | PASS |
|
|
15
|
+
| smoke-test | `fixture-roundtrip` | codex | 2 | 1 | `a81d076e3334…` | PASS |
|
|
16
|
+
|
|
17
|
+
**2 session(s) across 2 project(s): all verified.**
|
|
18
|
+
|
|
19
|
+
## What each column proves
|
|
20
|
+
|
|
21
|
+
| Column | Where it comes from |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Project, Session | The store layout: `projects/<project>/<session>/` |
|
|
24
|
+
| Harness | `harness` in the session manifest — what produced the transcript |
|
|
25
|
+
| Turns | Lines in `timeline.jsonl`, checked against `turn_count` in the manifest |
|
|
26
|
+
| Revision | `revisions` in the manifest. A rebuild that appends turns raises it; it never forks a session |
|
|
27
|
+
| Manifest sha256 | `manifest_sha256`, the hash of the manifest with that field removed |
|
|
28
|
+
| Integrity | `PASS` when the manifest hash matches, the turn count matches and `HANDOFF.llm.json` parses |
|
|
29
|
+
|
|
30
|
+
## Verify a session yourself
|
|
31
|
+
|
|
32
|
+
Point the engine at the sample store and ask it the same question this page answers:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# the store this page is rendered from
|
|
36
|
+
HANDOFFS_ROOT=examples/sessions node tools/handoff.mjs list
|
|
37
|
+
HANDOFFS_ROOT=examples/sessions node tools/handoff.mjs verify typescript-project-setup
|
|
38
|
+
HANDOFFS_ROOT=examples/sessions node tools/handoff.mjs verify fixture-roundtrip
|
|
39
|
+
|
|
40
|
+
# your own store
|
|
41
|
+
HANDOFFS_ROOT=/path/to/your/store node tools/handoff.mjs list
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`verify` exits non-zero the moment one of the three checks fails, so it works as a gate:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
HANDOFFS_ROOT=examples/sessions node tools/handoff.mjs verify typescript-project-setup || echo "do not resume this session"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Generate this page from your own store
|
|
51
|
+
|
|
52
|
+
The generator is part of this repository, and it reads any store in the format above:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
node .github/scripts/build-sessions-index.mjs --store examples/sessions --out docs/SESSIONS.md
|
|
56
|
+
node .github/scripts/build-sessions-index.mjs --check # exit 1 when the page is stale
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`--check` is wired into CI, so a store that changes without the page changing fails the build
|
|
60
|
+
instead of publishing a table that no longer matches what the engine can read.
|
|
61
|
+
|
|
62
|
+
The store is also published as JSON, for anything that would rather read data than
|
|
63
|
+
markdown: [`sessions.json`](sessions.json) (`1.0-session-feed`) — the same rows, with an
|
|
64
|
+
`integrity` verdict per session. The filter below runs in the browser against the table you
|
|
65
|
+
are looking at; nothing is fetched.
|
|
66
|
+
|
|
67
|
+
<p class="session-filter">
|
|
68
|
+
<label for="session-filter">Filter sessions</label>
|
|
69
|
+
<input id="session-filter" type="search" placeholder="project, session, harness, integrity…" size="34" />
|
|
70
|
+
<span id="session-filter-count" class="session-filter-count"></span>
|
|
71
|
+
</p>
|
|
72
|
+
|
|
73
|
+
<script>
|
|
74
|
+
(function () {
|
|
75
|
+
var input = document.getElementById('session-filter');
|
|
76
|
+
if (!input) return;
|
|
77
|
+
var rows = Array.prototype.slice.call(document.querySelectorAll('table tbody tr'));
|
|
78
|
+
var count = document.getElementById('session-filter-count');
|
|
79
|
+
function apply() {
|
|
80
|
+
var q = input.value.trim().toLowerCase(), shown = 0;
|
|
81
|
+
rows.forEach(function (tr) {
|
|
82
|
+
var hit = !q || tr.textContent.toLowerCase().indexOf(q) > -1;
|
|
83
|
+
tr.style.display = hit ? '' : 'none';
|
|
84
|
+
if (hit) shown += 1;
|
|
85
|
+
});
|
|
86
|
+
if (count) count.textContent = shown + ' of ' + rows.length + ' shown';
|
|
87
|
+
}
|
|
88
|
+
input.addEventListener('input', apply);
|
|
89
|
+
apply();
|
|
90
|
+
})();
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
## Next
|
|
94
|
+
|
|
95
|
+
- The file-by-file contract for a session folder is in [FORMAT.md](FORMAT.md).
|
|
96
|
+
- The commands that read and write a store are in [CLI.md](CLI.md).
|
|
97
|
+
- What the hashes prove, and what they cannot, is in [PROVENANCE.md](PROVENANCE.md).
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Troubleshooting
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Troubleshooting
|
|
6
|
+
|
|
7
|
+
Each entry names the symptom, the cause, and the fix. Exit codes are the ones documented in
|
|
8
|
+
[CLI.md](CLI.md).
|
|
9
|
+
|
|
10
|
+
## Nothing is listed
|
|
11
|
+
|
|
12
|
+
**Symptom:** `handoff: no handoffs yet`.
|
|
13
|
+
|
|
14
|
+
**Cause:** no build has run against the store being read, or the build wrote to a different
|
|
15
|
+
store.
|
|
16
|
+
|
|
17
|
+
**Fix:** check which store the engine resolved, and which rule chose it:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
node tools/handoff.mjs config
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The rule is one of `env`, `config`, `discover`, `default`. If it is not the store you
|
|
24
|
+
expected, set `HANDOFFS_ROOT` for the run, or add a `handoff.config.json` at the project
|
|
25
|
+
root. Resolution order is in [FORMAT.md](FORMAT.md#where-handoffs-live).
|
|
26
|
+
|
|
27
|
+
## `build` exits 2
|
|
28
|
+
|
|
29
|
+
**Cause:** `--source` was not given, or the value was consumed as another flag.
|
|
30
|
+
|
|
31
|
+
**Fix:** `build` requires it:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
node tools/handoff.mjs build --source transcript.jsonl --project my-project
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## `build` exits 4 after reading the file
|
|
38
|
+
|
|
39
|
+
**Cause:** no turn parsed. A `.jsonl` source is parsed one line at a time and a line that
|
|
40
|
+
does not parse is skipped; a text source needs a line starting with `user:`, `human:`,
|
|
41
|
+
`assistant:`, `ai:`, `system:` or `tool:`.
|
|
42
|
+
|
|
43
|
+
**Fix:** inspect the first few lines of the source. If the transcript is JSONL but the fields
|
|
44
|
+
are named differently, an adapter is what you want — see [ADAPTERS.md](https://github.com/Alot1z/agent-handoff/blob/main/refs/ADAPTERS.md)
|
|
45
|
+
for the canonical shape, and [INTEGRATION.md](INTEGRATION.md) for mapping a new source onto
|
|
46
|
+
it.
|
|
47
|
+
|
|
48
|
+
## `verify` prints FAIL
|
|
49
|
+
|
|
50
|
+
**Cause:** one of three things — the manifest was edited by hand after the build, the
|
|
51
|
+
timeline line count no longer matches `turn_count`, or `HANDOFF.llm.json` does not parse.
|
|
52
|
+
|
|
53
|
+
**Fix:** do not edit a handoff by hand. Rebuild from the same source:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
node tools/handoff.mjs build --source <original source> --session <id>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
A rebuild re-seals the manifest and rewrites the renders. If the original source is gone,
|
|
60
|
+
the handoff cannot be re-verified, and the honest answer is to say so rather than patch the
|
|
61
|
+
hash.
|
|
62
|
+
|
|
63
|
+
## `show`, `verify`, `rename` or `retitle` exits 3 or 4
|
|
64
|
+
|
|
65
|
+
**Cause:** 3 means the prefix matched more than one session; 4 means it matched none.
|
|
66
|
+
|
|
67
|
+
**Fix:** run `handoff.mjs list`, then use a longer prefix.
|
|
68
|
+
|
|
69
|
+
## `config` exits 2 and names a config file
|
|
70
|
+
|
|
71
|
+
**Cause:** a `handoff.config.json` was found but is not valid against
|
|
72
|
+
`handoff.config.schema.json`. The engine refuses to fall back to another store, because
|
|
73
|
+
falling back would write somewhere other than the configured location.
|
|
74
|
+
|
|
75
|
+
**Fix:** correct the file against the schema, or delete it to fall through to the
|
|
76
|
+
`handoffs/` convention.
|
|
77
|
+
|
|
78
|
+
## A runtime command exits 3 with "capture already in flight"
|
|
79
|
+
|
|
80
|
+
**Cause:** another run holds the lock for that project and session. Locks are files in
|
|
81
|
+
`<root>/.locks/`, named after a hash of the operation target.
|
|
82
|
+
|
|
83
|
+
**Fix:** wait for the other run. If a previous process was killed abruptly, it can leave a
|
|
84
|
+
lock file behind; the file is normally removed when the run finishes, so a lock that is
|
|
85
|
+
still present after every process has stopped can be deleted by hand.
|
|
86
|
+
|
|
87
|
+
## `dispatch` or `merge` produced something that fails the gate
|
|
88
|
+
|
|
89
|
+
**Cause:** by design. A `merge` writes a timeline with no brief, no payload and no
|
|
90
|
+
`TOOLS.md`, so `verify-gate` fails it on `payload` and `contract` until a brief is written
|
|
91
|
+
for the merged session. `index` reports the same sessions as stale.
|
|
92
|
+
|
|
93
|
+
**Fix:** write the brief for the merged session, then re-run `verify-gate`. A brief needs the
|
|
94
|
+
seven contract fields — `RESULT`, `WHAT_CHANGED`, `VALIDATION`, `EVIDENCE`, `BLOCKERS`,
|
|
95
|
+
`RISKS`, `FOLLOW_UP` — each starting a line.
|
|
96
|
+
|
|
97
|
+
## `verify-gate` exits 0 but the verdict says REJECTED
|
|
98
|
+
|
|
99
|
+
**Cause:** not a bug. `verify-gate` exits 0 for both verdicts so that "the gate ran" and
|
|
100
|
+
"the gate passed" stay distinguishable. Read `ok` or `verdict` from the JSON.
|
|
101
|
+
|
|
102
|
+
**Fix:** branch on the verdict in any script that calls it. The same applies to `promote`,
|
|
103
|
+
which prints the gate result but stamps the manifest either way.
|
|
104
|
+
|
|
105
|
+
## `promote` stamped a session that is not verified
|
|
106
|
+
|
|
107
|
+
**Cause:** expected behaviour, and stated in the command reference. Promotion is a local
|
|
108
|
+
stamp, not an enforcement.
|
|
109
|
+
|
|
110
|
+
**Fix:** run `verify-gate` first and only call `promote` when the verdict is `VERIFIED`.
|
|
111
|
+
|
|
112
|
+
## `capability-registry check` fails with `unknown`
|
|
113
|
+
|
|
114
|
+
**Cause:** a capability declares a probe kind with no implementation, or declares no target
|
|
115
|
+
or command for its kind. The registry reports `unknown` with the reason instead of guessing.
|
|
116
|
+
|
|
117
|
+
**Fix:** read the evidence line for the capability. `unknown` for a required capability is
|
|
118
|
+
treated as a failure on purpose: an unanswered question is not a pass.
|
|
119
|
+
|
|
120
|
+
## `runtime-engine run` exits 3
|
|
121
|
+
|
|
122
|
+
**Cause:** `DENIED`. An explicit denial, a personal-data path, an unknown risk class or an
|
|
123
|
+
unknown grant all land here. A personal-data or denied target is refused at any risk class.
|
|
124
|
+
|
|
125
|
+
**Fix:** inspect the decision before changing anything:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
node tools/runtime-engine.mjs evaluate --risk R1 --target <path> --json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The policy itself is [permission-policy.json](https://github.com/Alot1z/agent-handoff/blob/main/permission-policy.json); read
|
|
132
|
+
[PERMISSIONS.md](PERMISSIONS.md) for the levels.
|
|
133
|
+
|
|
134
|
+
## `runtime-engine resume` exits 4
|
|
135
|
+
|
|
136
|
+
**Cause:** there is no checkpoint to resume from, either because no job ran for that session
|
|
137
|
+
or because the session name does not match. Exit 5 means a checkpoint exists but is corrupt
|
|
138
|
+
or its integrity seal does not match.
|
|
139
|
+
|
|
140
|
+
**Fix:** run `status` for the session to see the durable state, then start a new job.
|
|
141
|
+
|
|
142
|
+
## The documentation site is missing a page, or a link 404s
|
|
143
|
+
|
|
144
|
+
**Cause:** the site is built from `docs/`. A page not listed in `docs/_data/nav.yml` does not
|
|
145
|
+
appear in the navigation, and a relative link that points at a file outside `docs/` does not
|
|
146
|
+
resolve in the built site.
|
|
147
|
+
|
|
148
|
+
**Fix:** add the page under `docs/`, add it to `nav.yml`, and run the check CI runs:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
node .github/scripts/check-docs.mjs
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Node version
|
|
155
|
+
|
|
156
|
+
The tools require Node.js 18 or newer and use only built-in modules. `node --version` below
|
|
157
|
+
18 is the only unsupported configuration; there is no build step and no dependencies to
|
|
158
|
+
reinstall. Platform notes are in [COMPATIBILITY.md](COMPATIBILITY.md).
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Uninstall
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Uninstall
|
|
6
|
+
|
|
7
|
+
Uninstalling removes the skill files. Your handoffs and your configuration stay where they are.
|
|
8
|
+
|
|
9
|
+
## Quick uninstall
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx agents-handoff --remove
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Removal asks for confirmation first:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
This will remove agents-handoff from:
|
|
19
|
+
<install-path>
|
|
20
|
+
|
|
21
|
+
Your handoffs and anything else you put in this directory will NOT be deleted.
|
|
22
|
+
Configuration (handoff.config.json) will NOT be deleted.
|
|
23
|
+
|
|
24
|
+
Continue? (y/N)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Answer `y` to proceed. In a non-interactive shell the prompt is skipped and nothing is removed;
|
|
28
|
+
the installer prints `Non-interactive mode, use --force to skip confirmation` instead.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx agents-handoff --remove --force
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Options
|
|
35
|
+
|
|
36
|
+
| Option | Effect |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `--remove` | Remove the installation (interactive confirmation). |
|
|
39
|
+
| `--force`, `-f` | Skip the confirmation. |
|
|
40
|
+
| `--location <global\|local\|project>` | Which installation to remove. Global is the default and is resolved the same way as for install. |
|
|
41
|
+
| `--path <dir>` | Remove the installation at exactly this directory. |
|
|
42
|
+
| `--claude`, `--codex`, `--agents` | Remove that harness's installation. Repeatable, and combinable. |
|
|
43
|
+
| `--harness <a,b>` | Named harness(es), comma separated. Repeatable. |
|
|
44
|
+
| `--all` | Every harness whose configuration directory exists on this machine. |
|
|
45
|
+
| `--skills-dir <dir>` | Remove the installation under exactly this directory. Repeatable. |
|
|
46
|
+
| `--project` | With a harness flag: the per-repository installation. |
|
|
47
|
+
|
|
48
|
+
Removing several harnesses at once is the same one run as installing them, and each target is
|
|
49
|
+
reported on its own:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx agents-handoff --remove --claude --force
|
|
53
|
+
npx agents-handoff --remove --all --force
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Flag form and bare verb are equivalent: `--remove` and `remove`, `--force` and `-f`.
|
|
57
|
+
|
|
58
|
+
## What is removed
|
|
59
|
+
|
|
60
|
+
**Only what the installation owns — the manifest.** The rule is a removal set, not a keep
|
|
61
|
+
list, so a directory nobody thought to name is kept rather than deleted:
|
|
62
|
+
|
|
63
|
+
- the engine and runtime: `tools/`, `tools/lib/`
|
|
64
|
+
- metadata: `SKILL.md`, `README.md`, `LICENSE`, `skill.json`, the manifest JSON files
|
|
65
|
+
- the install record: `.agents-handoff-install.json`
|
|
66
|
+
- `schemas/`, `refs/`, `templates/`, `docs/`, `tests/`
|
|
67
|
+
- the `package.json` stub the installer wrote, and only that stub — a `package.json` you have
|
|
68
|
+
edited is kept
|
|
69
|
+
|
|
70
|
+
Each removed entry is printed as `Removed file: <name>` or `Removed directory: <name>/`.
|
|
71
|
+
|
|
72
|
+
## What is kept
|
|
73
|
+
|
|
74
|
+
Everything else in the directory — the installer prints the list at the end:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Kept — not the installer's to delete:
|
|
78
|
+
.agent-handoff/
|
|
79
|
+
handoff-session.md
|
|
80
|
+
handoff.config.json
|
|
81
|
+
handoffs/
|
|
82
|
+
projects/
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
That includes a store under any name (`.agent-handoff/`, `projects/`, `handoffs/`, or one of
|
|
86
|
+
your own), `links/`, notes, your `handoff.config.json`, and anything else you added. Nothing is
|
|
87
|
+
deleted for being empty, and nothing outside those paths is touched; if data remains, the
|
|
88
|
+
installation directory remains with it.
|
|
89
|
+
|
|
90
|
+
## Manual uninstall
|
|
91
|
+
|
|
92
|
+
Remove the skill files and leave the data behind:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd "<install-path>"
|
|
96
|
+
|
|
97
|
+
# Exactly the manifest this same release installs. Your store, notes and
|
|
98
|
+
# handoff.config.json are not listed, so they stay.
|
|
99
|
+
rm -rf tools docs refs templates schemas tests
|
|
100
|
+
rm -f SKILL.md README.md LICENSE skill.json \
|
|
101
|
+
capability-registry.json permission-policy.json \
|
|
102
|
+
handoff.config.schema.json handoff.config.example.json \
|
|
103
|
+
.agents-handoff-install.json
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
A `package.json` the installer wrote (`"private": true`, `"name": "agents-handoff"`) can go
|
|
107
|
+
too; one you edited is yours to keep. `--remove` makes that distinction itself.
|
|
108
|
+
|
|
109
|
+
If you never store handoffs inside the installation — for example when `HANDOFFS_ROOT` points
|
|
110
|
+
somewhere else — and you do not need anything else in it, the whole directory can go:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
rm -rf "<install-path>"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Check where handoff data actually lives before doing that:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
node "<install-path>/tools/handoff.mjs" config
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`handoff: config root=<dir>` is the directory that holds your handoffs.
|
|
123
|
+
|
|
124
|
+
## After uninstalling
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npx agents-handoff --list
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The removed location should no longer appear. Handoff data that was kept still exists on disk
|
|
131
|
+
and can be read by a later installation, or by any tool that reads a handoff folder directly.
|
|
132
|
+
|
|
133
|
+
## Troubleshooting
|
|
134
|
+
|
|
135
|
+
| Symptom | Cause and fix |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `Not installed at <dir>` | That location holds no installation. Check `--list` and `where`, then retry with the right `--location` or `--path`. |
|
|
138
|
+
| Nothing was removed | The confirmation was skipped. Pass `--force`. |
|
|
139
|
+
| `EPERM` or `EBUSY` on Windows | A process is holding the files. Close it and retry, or check file attributes. |
|
|
140
|
+
| Permission denied | The installation is outside your user directory. Remove it with the privileges that created it, or use `--force` from a shell that can write there. |
|
|
141
|
+
| The directory is still there after `--remove` | It is not empty: the entries printed under `Kept — not the installer's to delete:` are still inside. Remove them yourself if you no longer want them. |
|
|
142
|
+
| Handoff data seems to be missing | `HANDOFFS_ROOT` points elsewhere. Check the root printed by `handoff.mjs config`; `--remove` never deletes a store inside the install directory. |
|
|
143
|
+
|
|
144
|
+
## See also
|
|
145
|
+
|
|
146
|
+
- [INSTALL.md](INSTALL.md)
|
|
147
|
+
- [UPGRADE.md](UPGRADE.md)
|
|
148
|
+
- [../README.md](https://github.com/Alot1z/agent-handoff/blob/main/README.md)
|