Skip to content

feat(btw): /btw context-shielded side-question command#5521

Open
MoerAI wants to merge 2 commits into
devfrom
feat/btw-command
Open

feat(btw): /btw context-shielded side-question command#5521
MoerAI wants to merge 2 commits into
devfrom
feat/btw-command

Conversation

@MoerAI

@MoerAI MoerAI commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a /btw <question> builtin slash command. The question is answered inline by the current session model (read-only, no tools, single response), then the /btw question and its answer are stripped from every FUTURE model-request payload, so a quick side question never consumes future token budget or changes what the model "remembers".

This takes a transform-strip approach (a different design from the child-session isolation in #3804).

What's included

  • Command registration/btw builtin (template + BuiltinCommandName union + disabled_commands enum), argumentHint: "<question>".
  • btw-context-strip transform hook — marker-driven (non-forgeable __omoBtwAutoSlashCommand metadata), fail-closed / no-throw, deterministic positional pairing with pending-turn retention, tool-pair safe, in-place mutation of the model-request payload.
  • btw-tool-guard — hard no-tools enforcement on the primary-session /btw answer turn (dispatched from tool-execute-before).
  • auto-slash marking on both the chat.message and command.execute.before routes; empty/whitespace /btw flows through auto-slash and is stripped too (no special-case).
  • docsdocs/guide/btw.md, including the compaction caveat.

Behavior / guarantee

/btw removes the Q&A from future model-request payloads (token budget + model memory stay clean). It is not a secrecy feature: the Q&A stays visible in TUI scrollback and session storage by design. Compaction summaries may retain /btw content (compaction does not route through the message-transform pipeline), documented as a known caveat.

QA & review

  • bun run typecheck clean; full btw + adjacent suites + meta-audits green (153 tests); bun run build + bun run build:schema green (schema adds only the btw command enum).
  • A codex review pass caught a real wiring gap on the refactored tree (the tool guard was constructed but not dispatched by the new explicit tool.execute.before sequence), now fixed with a RED to GREEN dispatch regression test (tool-execute-before-btw-guard.test.ts).
  • Live /btw behavior confirmed: a normal follow-up turn after a /btw side question cannot see the side-question content, while normal context is preserved.

@code-yeongyu — a feature I have found genuinely needed day to day; would love your review.

Special thanks to @IYENTeam for surfacing the need and the original /btw attempt in #3804; this PR takes a transform-strip approach rather than child-session isolation.


Summary by cubic

Adds a new /btw <question> slash command for quick side questions answered inline by the current session model. The /btw Q&A is stripped from all future model-request payloads, keeping token budget and model “memory” clean.

  • New Features

    • Built-in btw command with argumentHint: "<question>"; uses the current session model; single response; no tools.
    • btw-context-strip transform: uses non-forgeable __omoBtwAutoSlashCommand markers to strip the /btw user message and its answer from future payloads; retains a pending /btw turn; preserves adjacent tool pairs; mutates the payload in place.
    • btw-tool-guard: blocks tool calls during the /btw answer in the primary interactive session; dispatched from tool.execute.before.
    • Auto-slash marking on chat.message and command.execute.before; empty or whitespace-only /btw is still marked and stripped.
    • Docs: docs/guide/btw.md (includes how to disable via disabled_commands: ["btw"] and the compaction caveat that summaries may retain /btw content).
  • Bug Fixes

    • Restored the export * from "@oh-my-opencode/skills-loader-core/auto-slash-command/types" shim and added an optional message field to CommandExecuteBeforeOutput to support /btw marking and fix the registration audit.

Written for commit 6eadf0c. Summary will update on new commits.

Review in cubic

Add a `/btw <question>` builtin slash command. The question is answered
inline by the current session model (read-only, no tools, single response),
then the `/btw` question and its answer are stripped from every future
model-request payload, so a quick side question never consumes future token
budget or alters what the model remembers.

- Register /btw builtin (template + BuiltinCommandName union + disabled_commands enum)
- btw-context-strip transform hook: marker-driven (non-forgeable metadata),
  fail-closed, deterministic positional pairing with pending-turn retention,
  tool-pair safe, in-place mutation of the model-request payload
- btw-tool-guard: hard no-tools enforcement on the primary-session answer turn,
  dispatched from tool-execute-before
- auto-slash marking on both chat.message and command.execute.before routes;
  empty/whitespace /btw flows through auto-slash and is stripped (no special-case)
- compaction caveat documented; docs/guide/btw.md
@github-actions github-actions Bot added the opencode OpenCode edition: packages/omo-opencode label Jun 23, 2026

@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: 9b4822e57d

ℹ️ 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".

await hooks.atlasHook?.["tool.execute.before"]?.(input, output)
await hooks.compactionTodoPreserver?.["tool.execute.before"]?.(input, output)
await hooks.teamToolGating?.["tool.execute.before"]?.(input, output)
await hooks.btwToolGuard?.["tool.execute.before"]?.(input, output)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Move /btw guard before side-effectful pre-tool hooks

When a model attempts a tool while answering /btw, this denial runs only after earlier tool.execute.before hooks such as claudeCodeHooks; that handler appends transcript/cache entries and can execute user-configured Claude Code PreToolUse hooks before the /btw guard throws. In environments with PreToolUse hooks, the advertised read-only/no-tools /btw turn can still run side-effectful hook code, so the guard needs to happen before those pre-tool hooks.

Useful? React with 👍 / 👎.

</user-request>`,
argumentHint: "[goal]",
},
btw: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor disabled_commands for /btw auto expansion

Registering btw here makes the chat auto-slash path able to expand /btw even when users set disabled_commands: ["btw"]: createAutoSlashCommandHook is constructed without the disabled list, and discoverCommandsSync() loads built-ins with loadBuiltinCommands() unfiltered. That means disabling only removes the native command entry, while typing /btw ... in chat still injects the template, marks the message, and strips context unless the user disables the entire auto-slash hook.

Useful? React with 👍 / 👎.

Comment on lines +137 to +138
if (!await isPrimaryInteractiveSession(deps, input.sessionID)) {
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate /btw before non-primary sessions can expand it

Returning here only skips the tool guard for subagent/team/child sessions; it does not prevent /btw from being expanded there, because the command is registered globally and the auto-slash hook marks /btw without checking session type. If a subagent/team/background prompt starts with /btw, it still receives the BTW template but tools are not blocked, violating the documented primary-only/read-only behavior.

Useful? React with 👍 / 👎.

The btw port inlined the auto-slash-command type definitions, clobbering the
`export * from "@oh-my-opencode/skills-loader-core/auto-slash-command/types"`
re-export shim. That dropped the tracked re-export-shim count 316 -> 315 and
broke the package-registration-audit (re-export shim inventory).

Restore the shim and add the optional `message` field that btw command marking
needs to the shared CommandExecuteBeforeOutput type in skills-loader-core
(mirrors AutoSlashCommandHookOutput, which already carries message).
@github-actions github-actions Bot added the skills-loader-core Changes under packages/skills-loader-core label Jun 23, 2026

@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: 6eadf0c7e7

ℹ️ 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".

continue
}

safeStripIndices.delete(index)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Strip tool-bearing /btw answers instead of retaining them

If a /btw answer ever contains a tool call (for example when the guard is disabled, fails open, or an older marked transcript is replayed), this deletes the tool-bearing answer messages from the strip set, so the later model request still sees the side question's tool use/result even though the /btw Q&A is supposed to be removed from future context. Preserve API tool-pair validity by stripping the whole paired exchange rather than keeping it in the payload.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

opencode OpenCode edition: packages/omo-opencode skills-loader-core Changes under packages/skills-loader-core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant