Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 56 additions & 53 deletions README.md

Large diffs are not rendered by default.

37 changes: 19 additions & 18 deletions docs/ACCEPTANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ installed from the package under test, and a working `~/.codex`.
1. Attach Codex and confirm the marker landed in the file Desktop reads:

```sh
hyp attach codex
hyp client attach codex
grep -n 'model_providers.hypaware' "${CODEX_HOME:-$HOME/.codex}/config.toml"
hyp status
```
Expand Down Expand Up @@ -99,7 +99,7 @@ installed from the package under test, and a working `~/.codex`.
NEWEST=$(find "${CODEX_HOME:-$HOME/.codex}/sessions" -name '*.jsonl' -print0 \
| xargs -0 ls -t | head -1)
grep -m1 session_meta "$NEWEST"
hyp backfill codex --since "$(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ)" --json
hyp client history import codex --since "$(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ)" --json
```

Pass condition: the newest rollout file's `session_meta.originator`
Expand All @@ -119,7 +119,7 @@ installed from the package under test, and a working `~/.codex`.
so turn dev telemetry on for this one run and read the JSONL it writes:

```sh
HYP_DEV_TELEMETRY=1 hyp backfill codex --dry-run --json >/dev/null
HYP_DEV_TELEMETRY=1 hyp client history import codex --dry-run --json >/dev/null
grep -h unsupported_location "${HYP_HOME:-$HOME/.hyp}"/hypaware/dev-telemetry/logs-*.jsonl | tail -3
```

Expand All @@ -136,15 +136,15 @@ installed from the package under test, and a working `~/.codex`.
7. Detach and confirm the file is left clean:

```sh
hyp detach codex
hyp client detach codex
grep -n 'hypaware' "${CODEX_HOME:-$HOME/.codex}/config.toml" || echo 'clean'
```

Then, if this is your working machine, re-attach so you do not silently
leave Codex capture off:

```sh
hyp attach codex
hyp client attach codex
```

### If it fails
Expand Down Expand Up @@ -180,7 +180,7 @@ itself, no separate package to install or link), and a periodic sweep of
local session transcripts that backfills every OpenClaw provider within the
sweep interval. It proves the rows name the real upstream, that a turn both
lanes observe settles to exactly one row rather than two, and that live
capture is reversible via `hyp detach`.
capture is reversible via `hyp client detach`.

**What it does not prove:** anything about OpenClaw's CLI backends (a
Claude Code or Codex turn run through OpenClaw belongs to the sibling
Expand Down Expand Up @@ -247,18 +247,18 @@ procedure checks, R11 in particular), [LLP 0172](../llp/0172-openclaw-two-lane-c

```sh
hyp query sql "select count(*) from ai_gateway_messages where conversation_source = 'openclaw'"
hyp attach --client openclaw
hyp client attach openclaw
openclaw agent --agent <agent-id> --model anthropic/<a-claude-model> \
--message "pre-restart probe, should not route through the gateway"
hyp query sql "select count(*) from ai_gateway_messages where conversation_source = 'openclaw'"
```

Pass condition for item 4: the two counts are equal. `hyp attach` wrote
Pass condition for item 4: the two counts are equal. `hyp client attach` wrote
the config, but a running OpenClaw gateway does not pick up
`models.providers` changes until restarted, so the probe turn above
still went out at OpenClaw's original `baseUrl`, not the gateway's.

Now run the restart instruction `hyp attach` printed:
Now run the restart instruction `hyp client attach` printed:

```sh
openclaw gateway restart
Expand Down Expand Up @@ -296,7 +296,7 @@ procedure checks, R11 in particular), [LLP 0172](../llp/0172-openclaw-two-lane-c
where conversation_source = 'openclaw'"
```

`$SINCE` is the ISO instant `hyp backfill --since` takes; `$SINCE_SQL`
`$SINCE` is the ISO instant `hyp client history import --since` takes; `$SINCE_SQL`
is the same instant without the zone suffix, which is what compares
cleanly against the `message_created_at` TIMESTAMP column.

Expand Down Expand Up @@ -371,15 +371,15 @@ procedure checks, R11 in particular), [LLP 0172](../llp/0172-openclaw-two-lane-c
session file's native identity and wrote nothing new), and the second
query returns zero rows (no `part_id` in the window appears more than
once). This is R11 proven against the daemon's own automatic scheduler
rather than a manually-invoked `hyp backfill`, which is the whole point
rather than a manually-invoked `hyp client history import`, which is the whole point
of Lane B being *scheduled*, not just present.

6. Sweep step: prove a turn Lane A never saw still lands, at transcript
fidelity, within one sweep interval. Detach first, so the turn below
has no live route to travel:

```sh
hyp detach --client openclaw
hyp client detach openclaw
openclaw gateway restart
SINCE2=$(date -u +%Y-%m-%dT%H:%M:%SZ)
SINCE2_SQL=${SINCE2%Z}
Expand All @@ -405,7 +405,7 @@ procedure checks, R11 in particular), [LLP 0172](../llp/0172-openclaw-two-lane-c
grep -rl 'x-hypaware-upstream' "${OPENCLAW_HOME:-$HOME/.openclaw}"/agents/*/agent/models.json
```

Pass condition: no matches. `hyp detach` best-effort purges every
Pass condition: no matches. `hyp client detach` best-effort purges every
`agents/<id>/agent/models.json`; a leftover match here means the purge
missed a cache, not that self-heal happened on its own.

Expand All @@ -429,7 +429,7 @@ procedure checks, R11 in particular), [LLP 0172](../llp/0172-openclaw-two-lane-c
lane, and restart once more:

```sh
hyp attach --client openclaw
hyp client attach openclaw
openclaw gateway restart
```

Expand All @@ -441,7 +441,7 @@ procedure checks, R11 in particular), [LLP 0172](../llp/0172-openclaw-two-lane-c
1's `jq` check and `openclaw models list --all` still showing the
full catalog.
- **Item 3** ("no self-heal"): confirmed by step 6's cache-purge grep
returning no matches after `hyp detach`.
returning no matches after `hyp client detach`.
- **Item 4** ("no pickup without restart"): confirmed by step 1's
pre-restart probe turn producing no new row.

Expand Down Expand Up @@ -505,7 +505,7 @@ procedure checks, R11 in particular), [LLP 0172](../llp/0172-openclaw-two-lane-c
[LLP 0159](../llp/0159-openclaw-route-agreement-by-settlement.decision.md)'s
open question about append timing, not a dedupe bug.
- Step 6 finds a row immediately after the detached turn (should be `0`):
`hyp detach` did not actually remove the override entries, most likely
`hyp client detach` did not actually remove the override entries, most likely
because the entry on disk was not one this gateway wrote (a hand-edited
`baseUrl`, or `models` non-empty) and the detach backed it up instead of
deleting it, per
Expand Down Expand Up @@ -572,7 +572,7 @@ two-layer drift detection this discharges),
```sh
SETTINGS="${CLAUDE_HOME:-$HOME/.claude}/settings.json"
claude --version
hyp attach claude
hyp client attach claude
jq '.env, ._hypaware' "$SETTINGS"
hyp status
```
Expand Down Expand Up @@ -802,7 +802,8 @@ two-layer drift detection this discharges),
consuming. Check `hyp status` for a `@hypaware/claude` source error, confirm
the daemon restarted after step 4, and confirm the port in
`OTEL_EXPORTER_OTLP_ENDPOINT` is the one the listener actually bound (a
dynamic port moves across daemon restarts; `hyp attach claude` rewrites it).
dynamic port moves across daemon restarts; `hyp client attach claude`
rewrites it).
- Step 6 finds rows with `with_system = 0` and `with_tools = 0` while step 4
passed: the bodies are being written but not joined. Check whether the body
files are landing somewhere other than the attach-written spool, since a
Expand Down
2 changes: 1 addition & 1 deletion docs/PLUGIN_AUTHORING.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ Only clients whose manifest declares `contributes.client.agent_dir`
receive agents; a target without one is skipped.

Skills and agents are both **client assets** and share one install path
(LLP 0138): attaching a client materializes them, and `hyp skills
(LLP 0138): attaching a client materializes them, and `hyp client skills
install` re-copies both on demand. There is no separate `agents`
command.

Expand Down
61 changes: 31 additions & 30 deletions docs/PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ you enroll.

## What gets recorded

Each capture source you enable during `hyp init` records into the local
Each capture source you enable during `hyp setup` records into the local
query cache under `~/.hyp` (`HYP_HOME`):

| Source | What lands in the cache |
Expand All @@ -21,7 +21,7 @@ query cache under `~/.hyp` (`HYP_HOME`):
Recording is content-level: conversation rows include the actual message
text, not just metadata. Rows age out of the local cache after the
retention window init set (90 days on a team install, 120 on a
local-only one; `hyp init --retention-days <N>` overrides).
local-only one; `hyp setup --retention-days <N>` overrides).

### The raw-body spool

Expand All @@ -41,7 +41,7 @@ Three things keep it from becoming a second record:
`@hypaware/claude` config). Past it the oldest files go first, so a stopped
daemon costs detail, never disk.
- `hyp purge` empties it, whatever else you asked that purge to delete, and
`hyp detach claude` empties it on the way out.
`hyp client detach claude` empties it on the way out.

### If you turned on proxy mode

Expand Down Expand Up @@ -90,15 +90,16 @@ re-runs that one command at each login. What it runs is `/bin/launchctl`
itself, once, which sets the variable and exits: there is no resident
process, no HypAware code in it, and nothing is sent anywhere. It is still a
login item on your machine, and a session-wide variable that other Node
programs will also read. `hyp detach claude` unsets the variable and removes
the agent, as do `hyp detach claude --purge` and `hyp daemon uninstall`.
programs will also read. `hyp client detach claude` unsets the variable and
removes the agent, as do `hyp client detach claude --purge` and
`hyp daemon uninstall`.

**Its lifetime.** `hyp status` shows the fingerprint, every host the CA is
permitted to vouch for, whether the keychain still trusts it, and whether
the launchd variable is live. `hyp detach claude` deliberately keeps the CA
and the trust in place, so re-attaching later does not ask for your password
again; `hyp detach claude --purge` and `hyp daemon uninstall` remove the CA
and its keychain trust.
the launchd variable is live. `hyp client detach claude` deliberately keeps
the CA and the trust in place, so re-attaching later does not ask for your
password again; `hyp client detach claude --purge` and `hyp daemon uninstall`
remove the CA and its keychain trust.

## Where it goes

Expand Down Expand Up @@ -132,48 +133,48 @@ There are two authoring surfaces for the same classes:
travels with the repo, so it covers every clone:

```sh
hyp ignore # write a .hypignore at the repo root (or cwd)
hyp ignore <path> # ignore a specific subtree
hyp unignore # remove it, re-enabling recording
hyp privacy ignore # write a .hypignore at the repo root (or cwd)
hyp privacy ignore <path> # ignore a specific subtree
hyp privacy unignore # remove it, re-enabling recording
```

An empty or comment-only `.hypignore` also means `ignore`.

- **A machine-local store** (`hyp policy`) records the class privately on
- **A machine-local store** (`hyp privacy`) records the class privately on
this machine, never as a file in the repo. Use it when the marking itself
is sensitive (a dotfile in a hidden directory is a breadcrumb pointing at
exactly the thing you are hiding), or when the path is not a repo:

```sh
hyp policy set <path> ignore # never recorded, no dotfile
hyp policy set <path> local-only # recorded, never forwarded
hyp policy set <path> sync # explicitly synced (not asked again)
hyp policy show [path] # which class governs, and why
hyp policy list # every machine-local entry
hyp policy unset <path> [class] # back to the implicit default
hyp privacy set <path> ignore # never recorded, no dotfile
hyp privacy set <path> local-only # recorded, never forwarded
hyp privacy set <path> sync # explicitly synced (not asked again)
hyp privacy show [path] # which class governs, and why
hyp privacy list # every machine-local entry
hyp privacy unset <path> [class] # back to the implicit default
```

On a machine connected to a server, folders you have not marked sync
without asking. You can instead be asked, once per new folder, how to
handle it, at the moment you open a session there:

```sh
hyp policy folders ask # ask once per new folder
hyp policy folders sync # back to syncing without asking (the default)
hyp policy folders # report which is in force
hyp privacy folders ask # ask once per new folder
hyp privacy folders sync # back to syncing without asking (the default)
hyp privacy folders # report which is in force
```

This gates the question only. In either setting, folders you already
marked keep their class, `.hypignore` files are unaffected, and nothing
already local-only or ignored starts syncing. The setting is machine-local
and reversible, `hyp init` asks for it in its own step, and `hyp status`
and reversible, `hyp setup` asks for it in its own step, and `hyp status`
names it on an enrolled machine.

Two caveats apply to both surfaces:

- **Prospective only.** A marking gates future recording and forwarding.
Rows captured before it existed stay in the cache; deleting them is the
separate, explicit `hyp purge` step below.
separate, explicit `hyp privacy purge` step below.
- **Class resolution needs a working directory.** Only the Claude and
Codex pathways supply one, so directory markings are a no-op for the
`raw-anthropic` / `raw-openai` proxy and OTEL sources.
Expand All @@ -197,15 +198,15 @@ the id.

## Deleting what was already recorded

`hyp purge` permanently deletes rows from this machine's local cache. It
`hyp privacy purge` permanently deletes rows from this machine's local cache. It
never contacts a sink or the remote, and never deletes copies that were
already exported or forwarded:

```sh
hyp purge <path> # rows whose cwd is at or under the path
hyp purge --session <id> # one session's rows
hyp purge --ignored # every row whose directory now resolves to ignore
hyp purge --all # everything, wholesale
hyp privacy purge <path> # rows whose cwd is at or under the path
hyp privacy purge --session <id> # one session's rows
hyp privacy purge --ignored # every row whose directory now resolves to ignore
hyp privacy purge --all # everything, wholesale
```

It prompts on a TTY; pass `--yes` for non-interactive use.
Expand Down Expand Up @@ -239,7 +240,7 @@ already forwarded to a server.
`hyp leave` disconnects the machine from its central server: forwarding and
config pull stop, org-driven client attaches are undone, and the forward
credential is removed. Local recordings, config, and the daemon stay; use
`hyp purge` and the uninstall steps in the [README](../README.md#uninstalling)
`hyp privacy purge` and the uninstall steps in the [README](../README.md#uninstalling)
to remove those too.

## The daemon's own telemetry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
"datasets": [{ "name": "ai_gateway_messages", "attribution_column": "client_name" }],
"config_sections": [{ "section": "ai-gateway" }],
"commands": [
{ "name": "session ignore", "summary": "Stop recording this AI session (in-memory, until the gateway restarts)" },
{ "name": "session unignore", "summary": "Resume recording this AI session" },
{ "name": "session status", "summary": "Report whether this AI session is being dropped right now (fails closed)" }
{ "name": "session ignore", "category": "capture-movement", "audience": "everyday", "summary": "Stop recording this AI session (in-memory, until the gateway restarts)" },
{ "name": "session unignore", "category": "capture-movement", "audience": "everyday", "summary": "Resume recording this AI session" },
{ "name": "session status", "category": "capture-movement", "audience": "everyday", "summary": "Report whether this AI session is being dropped right now (fails closed)" }
],
"picker": [
{
Expand Down
14 changes: 10 additions & 4 deletions hypaware-core/plugins-workspace/ai-gateway/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export async function activate(ctx) {
ctx.commands.register({
name: 'session ignore',
plugin: PLUGIN_NAME,
category: 'capture-movement',
audience: 'everyday',
summary: 'Stop recording this AI session on every local recorder (in-memory, until the daemon restarts)',
usage: 'hyp session ignore [session-id] [--json]',
run: runSessionIgnore,
Expand All @@ -74,6 +76,8 @@ export async function activate(ctx) {
ctx.commands.register({
name: 'session unignore',
plugin: PLUGIN_NAME,
category: 'capture-movement',
audience: 'everyday',
summary: 'Resume recording this AI session',
usage: 'hyp session unignore [session-id] [--json]',
run: runSessionUnignore,
Expand All @@ -82,16 +86,18 @@ export async function activate(ctx) {
ctx.commands.register({
name: 'session status',
plugin: PLUGIN_NAME,
category: 'capture-movement',
audience: 'everyday',
summary: 'Report whether this AI session is being dropped right now (fails closed)',
usage: 'hyp session status [session-id] [--json]',
help: [
'Reads the gateway\'s in-memory ignored-session set. Exit codes:',
' 0 confirmed ignored - the gateway is dropping this session',
' 1 confirmed NOT ignored - this session is being recorded',
'Reads every live recorder advertising the shared in-memory session set. Exit codes:',
' 0 confirmed ignored - every advertised recorder holds this session',
' 1 confirmed NOT ignored - at least one recorder is recording it',
' 3 unknown - the check could not be completed; assume you ARE recorded',
'',
'This verb reports the session set only. The folder governor (.hypignore)',
'is independent and either match suppresses: see `hyp policy show`.',
'is independent and either match suppresses: see `hyp privacy show`.',
].join('\n'),
run: runSessionStatus,
})
Expand Down
Loading
Loading