Skip to content

feat(types): add hook contracts and policies - #1153

Open
taltas wants to merge 1 commit into
mainfrom
fm/zoo-hooks-contracts
Open

feat(types): add hook contracts and policies#1153
taltas wants to merge 1 commit into
mainfrom
fm/zoo-hooks-contracts

Conversation

@taltas

@taltas taltas commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Stack

This is 1 of 4 in the Zoo Code hooks MVP stack. Review and merge from the bottom upward. Do not merge a later PR before its predecessor.

Position Pull request Base Scope
1 (this PR) #1153 main Hook contracts and policies
2 #1154 fm/zoo-hooks-contracts Global settings and Hooks panel
3 #1156 fm/zoo-hooks-settings Runner and sessionStart integration
4 #1155 fm/zoo-hooks-session-start preToolUse, E2E, and documentation

Summary

Define the shared, runtime-validated contract for the hooks MVP without executing hooks or exposing settings yet.

Scope

  • Add schemas and TypeScript types for hook definitions, phases, invocations, decisions, and persisted run results.
  • Support sessionStart and preToolUse phases with exact tool-name matching.
  • Centralize fixed output limits, exit-code policy, truncation behavior, and safe summary generation.
  • Add structured say: "hook" chat-message data to the shared message contract.
  • Export the hook API through @roo-code/types.
  • Cover valid definitions, malformed input, matching, output bounds, and exit decisions with pure unit tests.

Data Flow

flowchart LR
    A[Hook definition] --> B[Schema validation]
    B --> C{Phase}
    C -->|sessionStart| D[Lifecycle match]
    C -->|preToolUse| E[Exact tool-name match]
    D --> F[Invocation contract]
    E --> F
    F --> G[Exit and output policy]
    G --> H[Structured hook result]
Loading

Tests

  • packages/types/src/__tests__/hooks.test.ts
  • pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 on all subsequently touched core test files, with no suppression-count increase.
  • Full stack validation at the top branch: pnpm test (437 files passed, 7,356 tests passed, 39 skipped).
  • Full stack validation at the top branch: pnpm check-types, pnpm lint, and pnpm build passed.
  • Full stack VS Code smoke validation: USE_MOCK=true TEST_FILE=hooks.test pnpm --filter @roo-code/vscode-e2e test:run passed.

Risks

  • These exported schemas become the contract consumed by every later layer. The definitions are deliberately narrow so unsupported phases and ambiguous matcher behavior fail validation instead of being inferred.
  • Output and summary limits are fixed policy constants. Later changes to those values can affect persisted records and extension-host memory use.
  • This PR has no process execution or settings UI behavior by itself; those behaviors are isolated in later PRs.

Review Notes

  • The PR is independently buildable against main.
  • No changeset is included, per repository guidance.
  • Merge order is #1153 -> #1154 -> #1156 -> #1155.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Added hook schemas and public types, phase and tool matching, exit classification, UTF-8-safe output handling, sanitization, top-level exports, hook message support, and comprehensive contract tests.

Changes

Hook support

Layer / File(s) Summary
Hook contracts and message integration
packages/types/src/hooks.ts, packages/types/src/message.ts, packages/types/src/index.ts, packages/types/src/__tests__/hooks.test.ts
Defines hook configuration, invocation, result, and message schemas. Adds duplicate-ID validation, immutable defaults, hook message support, package exports, and contract tests.
Hook matching, exit, and output policies
packages/types/src/hooks.ts, packages/types/src/__tests__/hooks.test.ts
Adds phase and tool matching, phase-specific exit classification, UTF-8-safe capture and model-output truncation, byte-count reporting, and ANSI/non-printable character sanitization. Tests cover these policies and edge cases.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: edelauna

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains scope, implementation, tests, risks, and review order, but it omits the required linked issue and pre-submission checklist. Add the Related GitHub Issue section with an approved issue number and complete the required pre-submission checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the addition of hook contracts and policies.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fm/zoo-hooks-contracts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/types/src/hooks.ts`:
- Around line 261-264: Update sanitizeHookOutput so its non-printable-character
regex excludes the C1 control range U+0080–U+009F while continuing to preserve
valid Unicode characters above it. Add regression coverage verifying that an
8-bit CSI sequence such as \u009b31m is removed from sanitized output.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ca8ee0f6-9e93-412a-8d18-43342c6d2f93

📥 Commits

Reviewing files that changed from the base of the PR and between 64d6e37 and 3c60674.

📒 Files selected for processing (4)
  • packages/types/src/__tests__/hooks.test.ts
  • packages/types/src/hooks.ts
  • packages/types/src/index.ts
  • packages/types/src/message.ts

Comment on lines +261 to +264
export function sanitizeHookOutput(output: string): string {
// ANSI control sequences are removed before other non-printable characters.
// eslint-disable-next-line no-control-regex
return output.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, "").replace(/[^\t\n\r\x20-\x7E\u0080-\uFFFF]/g, "")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove C1 control characters from sanitized output.

The second regex preserves U+0080 through U+009F. These are C1 control characters. For example, sanitizeHookOutput("\u009b31mred") retains an 8-bit CSI sequence. Exclude this range and add regression coverage.

Proposed fix
-	return output.replace(/\u001B\[[0-?]*[ -/]*[`@-`~]/g, "").replace(/[^\t\n\r\x20-\x7E\u0080-\uFFFF]/g, "")
+	return output.replace(/\u001B\[[0-?]*[ -/]*[`@-`~]/g, "").replace(/[^\t\n\r\x20-\x7E\u00A0-\uFFFF]/g, "")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function sanitizeHookOutput(output: string): string {
// ANSI control sequences are removed before other non-printable characters.
// eslint-disable-next-line no-control-regex
return output.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, "").replace(/[^\t\n\r\x20-\x7E\u0080-\uFFFF]/g, "")
export function sanitizeHookOutput(output: string): string {
// ANSI control sequences are removed before other non-printable characters.
// eslint-disable-next-line no-control-regex
return output.replace(/\u001B\[[0-?]*[ -/]*[`@-`~]/g, "").replace(/[^\t\n\r\x20-\x7E\u00A0-\uFFFF]/g, "")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/types/src/hooks.ts` around lines 261 - 264, Update
sanitizeHookOutput so its non-printable-character regex excludes the C1 control
range U+0080–U+009F while continuing to preserve valid Unicode characters above
it. Add regression coverage verifying that an 8-bit CSI sequence such as
\u009b31m is removed from sanitized output.

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-review PR changes are ready and waiting for maintainer re-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant