Skip to content

feat(tapd): long-lived daemon + web UI, with CLI/Hermes/OpenClaw as thin clients - #80

Merged
ggonzalez94 merged 146 commits into
mainfrom
gnhf/simplify-the-changes-38356d
Apr 21, 2026
Merged

ggonzalez94 merged 146 commits into
mainfrom
gnhf/simplify-the-changes-38356d

Conversation

@ggonzalez94

@ggonzalez94 ggonzalez94 commented Apr 14, 2026 •

Copy link
Copy Markdown
Owner

Fixes #32

Adds tapd, a long-lived TAP daemon that owns the XMTP transport, runtime state, and conversation storage behind an HTTP API over a Unix socket. The CLI, Hermes plugin, and OpenClaw plugin are now all thin clients that forward commands to tapd, so only one process holds the transport-ownership lock at a time and the three surfaces stay in sync by construction instead of manual feature parity.

The daemon also serves a Next.js web UI from / for browsing conversations, approving pending actions, and watching a live SSE event feed (bearer token passed via URL hash). Conversation logs move from per-file JSON into a single SQLite store with a one-shot migration on first startup and canonical timestamps so lexical ORDER BY is safe.

The standalone Hermes sidecar daemon is deleted — the Hermes hook now starts tapd and the plugin speaks to it over the socket. The OpenClaw plugin loses its own notification queue, event translator, and main-session state machine, and replaces them with an SSE escalation watcher against tapd. Core gains a typed TapEvent discriminated union emitted at every state transition so tapd can classify notifications centrally.

Seventeen simplification passes on top (gnhf #2–#18) extract helpers, collapse duplicate validators and well-known file-name constants, centralize event construction in TapMessagingService, and trim verbose patterns. Those commits were independently reviewed and all kept.

Test plan

  • bun run lint
  • bun run typecheck
  • bun run test
  • Smoke: tap daemon start → tap connect → tap message send round-trip against a real peer
  • Smoke: launch tap ui, verify SSE event feed and approve a pending action from the web UI
  • Smoke: Hermes hook-driven tapd start + tap_gateway send_message round-trip
  • Smoke: OpenClaw plugin escalation → [TAP Notifications] injected on next agent turn

Note

Medium Risk
Introduces new workspace packages (trusted-agents-tapd, trusted-agents-ui) and a native SQLite dependency (better-sqlite3), which can impact runtime behavior and installation/build reliability across platforms.

Overview
Adds new workspaces for a daemon (trusted-agents-tapd) and web UI (trusted-agents-ui), and bumps the repo package versions to 0.2.0-beta.6 with updated dependency graph.

Updates conversation log persistence to use a single SQLite database (conversations.db) via better-sqlite3, including a migration flow that moves legacy per-peer JSON logs into SQLite and leaves conversations.bak/ backups on success. Also expands .gitignore to exclude additional local/dev artifacts (e.g. .superpowers/, .superset/, __pycache__/).

Reviewed by Cursor Bugbot for commit d7ff06e. Bugbot is set up for automated code reviews on this repo. Configure here.

ggonzalez94 and others added 30 commits April 13, 2026 23:13
Designs a long-lived tapd daemon that owns transport across all hosts
and a Next.js static-export web UI that renders agent-to-agent
conversations as a Slack-style chat with inline action cards. Refactors
OpenClaw plugin and Hermes daemon to be thin clients of tapd while
preserving all current user behavior. Documents v2 storage substrate
work (SQLite + event log unification + channel primitive) as the next
spec to write.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
20-task TDD plan for building packages/tapd: workspace scaffold,
event bus with ring buffer, runtime wrapper bridging emitEvent to
typed events, HTTP server with unix socket + localhost TCP dual
bind, bearer token auth, all REST routes (identity/contacts/
conversations/pending/notifications/daemon-control), SSE event
stream with Last-Event-ID replay, daemon lifecycle and bin
entrypoint, plus end-to-end and SSE replay integration tests.

No user-facing changes; the daemon binary stands alone and nothing
else in the repo references it yet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
13-task plan for packages/ui: Next.js 15 static export, Tailwind +
hand-written shadcn primitives, typed REST/SSE clients, sidebar
and chat thread components matching the chat-metaphor mockup,
SWR + EventSource wiring, tapd-side static asset serving, and
Playwright golden-path end-to-end test against an in-process
tapd seeded with one contact and one conversation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Native browser EventSource cannot set custom headers, so the bearer
token must travel via query string for any client that uses it. The
auth check still uses constant-time comparison against the same
expected token, so there is no security regression — the query string
is just an alternative carrier for the same secret. The Last-Event-ID
fallback mirrors the same constraint for resume-on-reconnect.
…s (PORT_FILE, PID_FILE, TOKEN_FILE) across 5 files into 3 canonical exports from tapd/config.ts, extracted shared response base in connect.ts to deduplicate 2 identical success() calls, and extracted requireTimestamp() helper in sqlite-logger.ts to collapse 3 repetitive timestamp validation blocks (-21 net lines).
…emitActionCompleted, emitActionFailed) in TapMessagingService, replacing 6 remaining inline event-construction blocks with named methods that centralize each event type's schema in one place — completing the emit helper suite started in iteration 9.
…rings with TAPD_PORT_FILE/TAPD_PID_FILE/TAPD_TOKEN_FILE constants in daemon-start.ts, daemon-restart.ts, and the in-process-tapd test helper, and extracted getQueryParam() helper in auth.ts to deduplicate the 6-line query-string parsing pattern between auth.ts and sse.ts (-7 lines in sse.ts).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b0536bbb20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/lib/tapd-spawn.ts Resolved
Comment thread packages/cli/src/lib/tapd-spawn.ts Resolved
The CLI imports types from `trusted-agents-tapd` (TAPD_PID_FILE,
TAPD_PORT_FILE, etc.), but the root typecheck script only ran
`packages/tapd typecheck` (tsc --noEmit) without building. Without
tapd's `dist/*.d.ts`, CLI typecheck failed with TS2307 "Cannot find
module 'trusted-agents-tapd'". Added `packages/tapd build` after
tapd typecheck, matching the same pattern already used for core and
sdk.

Fixed 5 spell-check errors (crate-ci/typos): `unparseable` →
`unparsable`, `mis-shaped` → `malformed`, `mis-ordered` →
`out-of-order`, `mis-render` → `render... incorrectly`, and renamed
test IDs `conv-noo`/`conn-noo` → `conv-no-offset`/`conn-no-offset`
for clarity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6149b11ac3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/lib/tapd-spawn.ts Resolved
Comment thread packages/cli/src/commands/hermes.ts Resolved
Comment thread packages/openclaw-plugin/src/plugin.ts Resolved
@ggonzalez94

Copy link
Copy Markdown
Owner Author

CI blocker: semantic merge conflict with `main`

After the typecheck ordering fix in 6149b11, CI gets past the previous `trusted-agents-tapd` module error and now surfaces a deeper issue:

```
src/commands/status.ts(17,42): error TS2307: Cannot find module '../hermes/client.js' or its corresponding type declarations.
```

Why this is happening

GitHub tests `refs/pull/80/merge` (a virtual merge of this branch into `main`). That virtual merge contains:

  • `packages/cli/src/commands/status.ts` — added by feat(cli): add host-aware tap status for first-step debugging #81 on `main`, imports `readHermesTapDaemonState` from `../hermes/client.js`.
  • Deletion of `packages/cli/src/hermes/client.ts` — this branch removed it as part of the `tapd` refactor (client/IPC layer moved into `packages/tapd`).

Neither side touches the same file, so Git doesn't flag a text conflict — but the combined tree won't typecheck.

Proposed fix (mechanical)

When `main` is merged into this branch, update `status.ts` to import the underlying function directly from `hermes/config.ts`, which is still exported on the branch:

```ts
// Before
import { readHermesTapDaemonState } from "../hermes/client.js";
// ...
daemonState = await readHermesTapDaemonState(hermesHome);

// After
import { loadTapHermesDaemonState } from "../hermes/config.js";
// ...
daemonState = await loadTapHermesDaemonState(hermesHome);
```

On `main`, `readHermesTapDaemonState` is a one-line wrapper around exactly that function, so this is a no-op behavior change that removes a dead indirection. The other export from `hermes/client.ts` (`sendHermesTapDaemonRequest`) is obsolete in the new tapd architecture — the CLI now talks to the tapd HTTP client via `lib/tapd-client.ts` instead of the old Hermes Unix-socket IPC. No consumer of `sendHermesTapDaemonRequest` remains once `status.ts` is updated.

I'm skipping this fix in this babysit iteration because:

  1. It requires a `git merge origin/main` commit, which is a significant history change on an open PR.
  2. The worktree currently has in-flight uncommitted work on the tapd refactor that I don't want to disturb.

Leaving this for the maintainer to decide — merge with the fix above, or rebase onto `main` and include the `status.ts` update.

ggonzalez94 and others added 4 commits April 21, 2026 07:27
PR #81 added tap status on main importing readHermesTapDaemonState
from ../hermes/client.js, but this branch removed hermes/client.ts
when folding Hermes's standalone daemon into tapd. That wrapper was
one line over loadTapHermesDaemonState, which is still exported from
hermes/config.ts, so swap the import and call site. Pure rename, no
behavioral change to tap status.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ermes identity scoping

- openclaw-plugin: start the escalation watcher unconditionally in the
  plugin service `start`. The watcher already has a built-in reconnect
  loop (escalation-watcher.ts handleEnd); gating it on a one-shot tapd
  health check meant Gateway booting before tapd left the agent blind
  to escalations until the plugin service was restarted.

- cli hermes aliases: `tap hermes status/sync/restart` were parsing
  `--identity` and `--hermes-home` and throwing them away, so
  multi-identity Hermes setups always hit the default data dir. Resolve
  the identity via loadTapHermesPluginConfig and scope the forwarded
  GlobalOptions.dataDir to the selected identity before dispatching to
  the underlying daemon/sync command. Architecturally: identity lookup
  is a CLI-side concern (fat CLI, thin plugin — CLAUDE.md), and the
  alias commands stay thin wrappers over daemon-status / message-sync /
  daemon-restart.

Addresses codex bot review comments #3114838436 and #3114838438.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
spawnTapdDetached removed the pidfile on any catch, including the
waitForPortFileAndLiveness timeout path where the child process is
still alive. That left a detached tapd running with no pidfile
pointing at it: tap daemon stop/restart could no longer control it,
and the next tap daemon start could unlink its port file mid-flight
while it was still serving. SIGKILL the child first when it's still
alive, then clean up.

Addresses codex bot review comment #3114838432.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 834ab46bb9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/openclaw-plugin/src/tool.ts Resolved
… of silent outage

Both the OpenClaw plugin (tap_gateway) and the Hermes Python client
had `action="restart"` wired to `/daemon/shutdown` with no respawn
path. In those contexts — thin transport-active adapters per
CLAUDE.md §16 — neither process can spawn tapd back up, so what looks
like a recovery action silently leaves tapd down: OpenClaw Gateway
has no supervisor for tapd, and Hermes notification drain runs with
auto_start=False so drain stops until the next user action.

Rather than pretend to restart, both surfaces now refuse the action
and point the operator at `tap daemon restart` / `tap hermes restart`
on the CLI, which actually does stop+start. Keeps the thin-plugin
rule intact (no new spawn paradigm in either adapter) and turns a
silent outage into a visible "wrong tool" error.

Addresses codex bot review comments #3117241110 and #3117241114.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3727c77911

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/commands/ui.ts Resolved
Comment thread packages/tapd/src/notification-queue.ts Resolved
…rt behavior

3727c77 made the restart action throw with operator guidance instead
of dispatching to client.shutdown(). The dispatch-table test was still
asserting the old behavior, so it failed in CI. Drop the old assertion
and add a focused test that restart rejects with the `tap daemon
restart` hint while never touching client.shutdown.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0455e29c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/commands/ui.ts Resolved
Comment thread packages/tapd/src/http/server.ts Resolved
ggonzalez94 and others added 2 commits April 21, 2026 08:24
- tool.test.ts: biome wanted the single-arg rejects.toThrow call on one
  line; collapse it. This fixes the CI lint failure from a0455e2.

- ui.ts: `tap ui` was echoing a URL containing the bearer token to
  stdout via info() AND including it in the structured success()
  payload. The hash-based token placement only protects against
  browser history / referrer leaks — terminal logs, --json capture, CI
  collectors are separate attack surfaces, and that token authorizes
  the daemon control and write APIs. Now hand the token-bearing URL
  directly to openInBrowser(), echo only the base URL on the happy
  path, and only surface the full URL when auto-open fails (the user
  has no other way to reach it).

- escalation-watcher.ts: `stop()` set `stopped=true` permanently, and
  `start()` bailed if that flag was true, so one stop disabled the
  watcher for the life of the instance. The plugin service lifecycle
  can call start() after stop() on a Gateway reload without process
  replacement, which would silently leave escalations dark. `stopped`
  is now a signal to the reconnect loop, not terminal state: start()
  resets it and reconnects.

- notification-queue.ts: the queue had no upper bound. If
  /api/notifications/drain wasn't being called (consumer offline),
  memory grew without limit. Added a maxSize cap (default 1000,
  matching the event bus ring-buffer shape) with FIFO eviction on
  overflow.

Addresses codex bot review comments #3117308353, #3117308361,
#3117308367 (and the CI lint regression from a0455e2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, 4xx for client errors

- client.py resolve_pending: the verb was derived via
  `bool(params.get("approve"))`, so a missing approve silently coerced
  to `/deny` and could reject a pending action the caller never meant
  to reject. Require `approve` to be an explicit boolean and return a
  validation error otherwise.

- ui.ts openInBrowser: `spawn()` command-lookup failures are emitted on
  the child as an async `"error"` event, not caught by the surrounding
  try/catch. On systems without `open`/`xdg-open` the process would
  crash from an unhandled error after `tap ui` had already returned.
  Attach a no-op `error` handler so we stay crash-safe; return value
  stays optimistic since the caller's fallback messaging already tells
  the user how to reach the URL manually.

- server.ts handle(): the catch-all mapped every async rejection to
  HTTP 500, so malformed JSON bodies and payload-too-large came back
  as internal_error and triggered client retry behavior that would
  never succeed. Introduced HttpError (status + code) and plumbed it
  through readJsonBody (400 invalid_json, 413 payload_too_large);
  unknown exceptions still fall through to 500 because those are
  genuine server bugs.

Addresses codex bot review comments #3117358151, #3117358156,
#3117358161.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec7c53a090

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tapd/src/http/validation.ts Resolved
Comment thread packages/cli/src/commands/ui.ts Resolved
…mapping

Per review comment 3117464873: ec7c53a surfaced 4xx for readJsonBody
errors but left requireParam/requireBody throwing plain Error, which
fell through to the catch-all 500 path and reported client input
mistakes as internal_error. Throw HttpError(400, ...) from the two
validation helpers so the server's top-level catch maps them to their
intended 4xx status and caller retry logic stays correct.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e771f97684

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tapd/src/http/router.ts Resolved
Completes the 4xx-mapping sweep from e771f97:

- invites route: parseInviteBody throws HttpError(400, invalid_body /
  invalid_param) for non-object bodies and bad expiresInSeconds values
  (review comment 3119936389).
- contacts-write revoke: unknown connectionId throws HttpError(404,
  contact_not_found) so stale UI/CLI state surfaces as a 4xx instead
  of an internal_error 500 (review comment 3119936394).
- router: decodeURIComponent on route params now throws HttpError(400,
  malformed_param) on URIError, so malformed percent-encoding in a path
  no longer leaks as an internal server fault (review comment 3119936397).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0764116d6a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…y, spawn race hardening

Ships three review-driven changes that had been queued in the worktree:

1. Unix-socket migration + per-dataDir auth token
   - tapd drops the TCP-bypass shortcut; the Unix socket requires the same
     bearer token as the TCP listener. Token is persisted to `.tapd-token`
     next to the socket so CLI/Hermes/OpenClaw clients can read it once at
     startup instead of fishing it out of the pidfile.
   - Default TCP port switches to 0 (OS-assigned ephemeral) so multi-
     identity Hermes setups stop colliding on :6810. The bundled web UI
     reads the bound port from `.tapd.port` the same way.
   - `TapdConnectionInfo` is now `{ socketPath, token }`; `discoverTapd` no
     longer returns a baseUrl. A dedicated `discoverTapdUiUrl` returns the
     TCP URL for `tap ui` and `tap daemon status`.

2. Meetings route — single flat body shape (comments #3119988721, #3119988723)
   - Collapses the dual-shape (full SchedulingProposal vs flat kwargs) POST
     body into a single flat shape. All clients send the same thing now.
   - `respond` throws HttpError(404) for unknown schedulingId so stale CLI
     state surfaces as a 4xx rather than the catch-all 500.

3. tapd-spawn race hardening (comments #3081753465, #3081753473)
   - Atomic `wx` pidfile write closes the check-then-act window between
     racing `tap daemon start` calls.
   - Pidfile now carries `{ pid, binPath, ownerToken }`; a UUID owner token
     is embedded in the child's argv so `inspectTapdProcess` can confirm
     the live pid still belongs to our tapd (via `/proc/<pid>/cmdline` on
     Linux, `ps -o command=` on macOS).
   - `daemonStartCommand` blocks only when the owner is actually alive;
     stale port/pid files are cleaned up and the start proceeds.

Comment #3081753468 (Windows package-dir) is addressed by switching the
resolver to `dirname` from `node:path`, which handles the platform the
host runs on. Explicit Windows support (PowerShell command-line lookup,
win32 path normalization) is intentionally out of scope — tap targets
Linux + macOS and keeping the Windows branches would add surface area for
no supported use case.

Reactive mode for Claude Code (new SKILL.md section, `--filter`/`--quiet`
on `tap message listen`, auto-reconnect cursor) is deferred to a follow-up
PR; it is a net-new feature rather than a review fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8dc474a1c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tapd/src/http/auth.ts Resolved
… only

Per review comment 3120267621: `authorizeRequest` accepted `?token=...`
for every route/method, which widened the blast radius of any token
leak (shell history, referrer, log capture). A cross-site form POST to
`/daemon/shutdown?token=...` would bypass CORS preflight because the
request would carry no custom `Authorization` header.

Restrict the query-token fallback to `GET /api/events/stream` (the only
route whose client — the browser's native `EventSource` — literally
cannot set a header). Everything else now requires header-based auth.

Also:
- `authorizeRequest` takes `method` and `requestPath` from the caller
  rather than re-parsing the URL; `TapdHttpServer.handle` already has
  both.
- Test coverage updates to reflect the narrower contract: the SSE
  route still accepts the query token, `/api/identity` and
  `/daemon/shutdown` reject it with 401 even when the value is correct.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ab94eeb35

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tapd/src/notification-queue.ts Resolved
…ng success

Per review comment 3117464878: openInBrowser returned true synchronously
after child_process.spawn, but command-lookup failures on POSIX arrive
asynchronously as an "error" event on the child. On systems without
open/xdg-open the command optimistically reported success and the
token-bearing URL (intentionally suppressed from stdout/--json on the
happy path, see 3117308353) was lost to the user.

Make openInBrowser async and wait BROWSER_OPEN_ERROR_WINDOW_MS (250ms)
for an error event before resolving true. Fast enough to feel instant,
long enough to catch ENOENT in practice; anything slower is out of scope
(we only care about "did the opener process launch").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

chain: optionalString(params.chain),
toAddress: normalizeAddress(params.toAddress),

P2 Badge Populate request_funds defaults before forwarding to tapd

tap_gateway forwards request_funds with chain/toAddress left undefined when the model omits them, but tapd validates both fields as required in /api/funds-requests (isFundsRequestBody requires non-empty chain and toAddress). In practice this turns an advertised optional-input flow into a 400 failure path for OpenClaw users unless they always supply both fields manually; this should be defaulted in the tool layer (as the CLI does) before calling tapd.


const conversationsDir = join(resolveDataDirPath(dataDir), "conversations");
let entries: string[];
try {
entries = await readdir(conversationsDir);
} catch (err) {
if (fsErrorCode(err) === "ENOENT") {
return { value: [] };

P2 Badge Read conversation metrics from SQLite in tap status

tap status still reads message/conversation summaries from <dataDir>/conversations/*.json, but this change moved runtime logging to conversations.db via SqliteConversationLogger (and migration renames legacy JSON to conversations.bak*). On migrated installs, this path returns ENOENT and the command reports zero inbound/outbound counts and no last-message data even when conversations exist, so status output becomes misleading for active agents.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ggonzalez94
ggonzalez94 merged commit 290a8af into main Apr 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dedicated UI/App

1 participant