Skip to content

refactor(hook): add typed notification pipeline - #2029

Merged
yyhhyyyyyy merged 13 commits into
devfrom
refactor/typed-hook-pipeline
Jul 26, 2026
Merged

refactor(hook): add typed notification pipeline#2029
yyhhyyyyyy merged 13 commits into
devfrom
refactor/typed-hook-pipeline

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR replaces the untyped hook fan-out with a typed, deterministic notification pipeline.

  • Add a closed HookEvent discriminated union with compile-time coverage and invalid-combination guards.
  • Introduce producer-bound RuntimeHookScope emitters and a single StopSessionEnd projection.
  • Move hook configuration and the derived subscription index into HookService.
  • Bind events to subscribers eligible at acceptance, then revalidate them immediately before spawning.
  • Serialize command starts per session without coupling sessions or awaiting external commands.
  • Project and truncate tool payloads before cloning; clone only the permission record.
  • Remove the incorrect messageId → prompt fallback that exposed assistant output as user input.
  • Resolve only unanswered session facts, preserving explicit null project directories.
  • Make command timeouts settle independently of process close and bound captured stdout/stderr.
  • Keep configuration snapshots detached without freezing values owned by the settings port.

The design and deferred decisions are recorded in:

docs/architecture/deepchat-agent-harness-boundaries/

Motivation

The previous hook contract used an event name plus an all-optional context, so invalid combinations compiled and adding an event could silently escape exhaustive handling.

The delivery path also had several correctness and reliability problems:

  • Stop and SessionEnd were assembled independently in four runtime paths.
  • Independent asynchronous enrichment could reorder command starts within a session.
  • Hook configuration was read and normalized for every event, even with no subscribers.
  • Large tool parameters and responses were cloned repeatedly before truncation.
  • Tool and resumed-session events could report assistant output as user.promptPreview.
  • Configuration changes could backfill an already accepted event into a newly enabled or edited command.
  • A timed-out shell could keep the settings test request pending forever.
  • Captured command output could grow without bound until process exit.

Delivery semantics

  • An unsubscribed event performs one subscription lookup and no envelope resolution, clone, database read, or spawn.
  • Events within one session start their commands in emission order.
  • Different sessions remain independent.
  • External command completion order is intentionally not serialized.
  • Hooks never block Agent execution.
  • A hook enabled or edited after an event occurred does not receive that event.
  • A hook disabled before delivery no longer receives queued events.

Compatibility

The following public behavior remains unchanged:

  • payloadVersion: 1
  • stdin payload shape
  • environment variable names
  • command placeholders
  • 30-second timeout value
  • redaction behavior
  • settings and IPC contracts

Intentional value corrections:

  • user.promptPreview is populated only when supplied by the producer; tool events and resumed SessionStart events no longer contain assistant output.
  • time is captured when the event is accepted rather than after asynchronous enrichment.
  • ACP continues to report usage: null because its terminal port has no canonical token accounting.

Summary by CodeRabbit

  • New Features

    • Introduced a typed, session-scoped hook notification pipeline with deterministic per-session ordering and isolated delivery across sessions.
    • Added hook configuration update support and subscription-aware notification delivery.
    • Updated the model catalog by adding Anthropic Claude Opus 5.
  • Bug Fixes

    • Improved hook command timeout handling, output truncation, failure isolation, and consistent terminal event reporting.
    • Notifications now skip processing when observers are not interested in a given event.
  • Documentation

    • Expanded the hook pipeline architecture/specs, tasks, and acceptance criteria; regenerated architecture baseline metadata.
  • Chores

    • Updated the Dirac agent registry to 0.4.25; refreshed model/pricing/availability data.

Record the hook slice in the existing harness architecture goal instead of
opening a second record.

The slice covers the contract defect (event name and payload are two
uncorrelated types), the four drifted Stop/SessionEnd implementations, the
per-event settings and session reads paid by installations with no hook
configured, the triple clone of untruncated tool payloads, and the
non-deterministic command start order.

It explicitly excludes hook decision semantics. Letting a user-configured shell
command block, replace, cancel, or rewrite provider requests and tool arguments
needs a versioned stdout protocol, a conflict and timeout policy, argument
revalidation, a permission recheck after rewriting, and a trust surface. That is
a product feature with its own security model, and DeepChat has no in-process
participant that could use it today.
The hook contract was one flat all-optional context, so the event name and its
payload were uncorrelated: dispatch('Stop', { tool }) compiled, and adding an
event name failed no build. Replace it with a discriminated HookEvent union plus
two compile-time guards, one that fails when the union and HOOK_EVENT_NAMES
drift apart and one that pins the combinations the union must refuse.

RuntimeHookSink becomes a scope factory that binds the immutable session facts of
one turn, so no call site assembles an envelope and Stop plus SessionEnd has a
single implementation. The four previous copies had already drifted: the ACP path
dropped usage entirely.

Delivery defects fixed:

- HookService re-read hooksNotifications per event. That key is a sensitive app
  setting, so each read was a database read, a JSON deep clone, a Zod
  normalization and two JSON.stringify calls, paid twice per tool call even with
  no hook configured. The service now owns its configuration and derived
  subscription index, refreshed atomically on every write path.
- An unsubscribed event now costs one set lookup: no envelope resolution, no
  projection, no clone, no settings read, no session read, no spawn.
- Tool events were structuredCloned three times before truncation, copying
  multi-megabyte arguments and responses for a payload that keeps 1200
  characters. Projection now truncates first and clones only the permission
  record.
- Events were delivered through independent microtasks with per-event async
  enrichment, so command start order did not match event order. Delivery is now
  serialized per session and independent across sessions.
- Payload enrichment read the session row whenever workdir was falsy, so a
  session with no project directory paid a read per event. Only an unanswered
  field triggers a lookup now.
- The messageId to promptPreview fallback read a message row and reported the
  assistant's own output as the user's prompt, because every producer passes an
  assistant message id. The lookup and its query port are removed; previews come
  from the producer that owns the prompt.
- observeTerminal resolved the project directory outside the sink's guard, so a
  stale-instance assertion could propagate into run settlement. Resolution now
  happens inside the guard and degrades to an unanswered field.

payloadVersion, the stdin payload shape, environment variables, command
placeholders, the command timeout and the settings contract are unchanged.
user.promptPreview is now empty where it previously carried assistant output,
and time is captured when the event occurs rather than when its payload is built.
Regenerate after the hook contract change. The report records two structural
effects: the hook/observer.ts to hook/index.ts cycle is gone because the observer
now depends on the neutral event contract instead of the service module, and
interactionCoordinator drops one outgoing dependency after losing the session
settings port it only used to resolve a hook envelope.
The subscription index and the delivery filter each decided independently
whether a hook can run. A future edit to one and not the other would let the
index advertise fewer events than the filter accepts, and an unadvertised event
is dropped at the producer without any log line. Both now read the same
predicate.
notify only decided whether the event was observed and then queued a projection;
deliver re-read the current configuration. A hook enabled or edited between
those two points therefore ran an event that happened before it existed.

Reproduction: hook A subscribes to PostToolUse, notify accepts an event, the
same tick calls updateConfig adding hook B, and B receives the earlier event
with its prompt, tool arguments and response. Slow enrichment or a queued
session widens the window.

An accepted event now carries the id and command of every hook eligible at that
moment, and a hook runs only when it is still eligible under the same command.
A hook enabled or edited afterwards never receives an earlier event, one
disabled meanwhile stops receiving queued ones, and editing a command while
keeping the hook id does not run old events under the new command.

Revalidation happens immediately before spawning rather than when the delivery
is dequeued, because enrichment is asynchronous and eligibility can change
across that await. A cheap pre-enrichment check keeps an event that nothing can
consume from paying for a session lookup.
The command timeout only set a flag and sent SIGKILL; the promise still resolved
on close or error. A SIGKILLed shell can leave a process tree holding the
inherited stdio, so close may never arrive and testHookCommand from the settings
page would stay pending forever. The timeout now settles its own result, and a
later close only clears the timer and unregisters the child.

Captured stdout and stderr also grew without limit for the whole timeout window
before being truncated to 2000 characters, so a chatty hook could hold thirty
seconds of output in the main process and concurrent hooks multiplied it.
Capture now stops at the same limit the diagnostics are truncated to, which
leaves the reported value identical because truncation keeps the head.

The previous timeout test emitted close by hand, so it proved the kill and not
the settlement. It now asserts the promise resolves with no close event at all.
buildConfigSnapshot froze the object the settings port returned, along with its
hooks and their event arrays. HookSettings currently normalizes into a fresh
object every read, so nothing observed it, but the freeze was an input side
effect no signature declared: a port that returned shared or cached state would
have had that state silently frozen from under it.

The snapshot now copies before freezing, and getConfigSnapshot returns a mutable
copy rather than the frozen internal one, which restores the pre-cache property
that a caller could do anything with the value it received.
The union relied on excess property checking, which only refuses surplus fields
on a fresh literal. Assigned to a variable first, an object carrying both stop
and tool still satisfied HookEventBody, so the central contract of this slice
did not hold on the one path a caller is most likely to take when building an
event dynamically.

Each variant is now closed against every fact it does not declare. The rejection
guards previously proved only that a required fact cannot be missing; they now
also pin foreign facts, including SessionEnd carrying stop and SessionStart
carrying usage, neither of which the open union could reject.
Two claims overstated what the code does.

The record described one scope binding the immutable facts of a turn. A turn
actually creates three, because settlement reaches the sink from
RunLifecycleCoordinator without the turn's scope, so envelopes are resolved at
three points and a mid-turn setting change can still be observed by later events.
Threading one scope through the run lifecycle adds a state carrier to that owner
and is recorded as deferred instead.

The terminal correction claimed usage no longer depends on the entry point. ACP
still reports null because AcpObserverPort.terminal carries no token accounting;
what the single projection removes is the drift between four implementations.

Also records the configuration ownership rule, the timeout settlement and output
bound, and adds acceptance criteria for each.
The review fixes changed no module graph, so the regenerated report differs only
in the commit it was measured from.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Typed Hook Notification Pipeline

Layer / File(s) Summary
Typed event contract and delivery pipeline
src/main/hook/events.ts, src/main/hook/observer.ts, src/main/hook/index.ts
Hook events now use discriminated payload facts, scoped projections, subscription gating, per-session ordering, configuration snapshots, bounded diagnostics, and deterministic command delivery.
Scoped runtime emission and terminal projection
src/main/agent/deepchat/runtime/runtimeHookSink.ts, src/main/agent/deepchat/loop/*
RuntimeHookScope binds session facts, filters events, adapts tool notifications, and emits normalized Stop and SessionEnd events.
Agent producer migration
src/main/agent/acp/compatibility/dependencies.ts, src/main/agent/deepchat/runtime/*, src/main/agent/deepchat/harness/createDeepChatAgentHarness.ts
ACP, loop, coordinator, and turn paths use scoped hook emission instead of dispatch callbacks.
Service ownership, route wiring, and validation
src/main/hook/routes.ts, src/main/app/composition.ts, test/main/hook/*, test/main/agent/deepchat/**/*
Hook routes and composition depend on HookService, while tests cover observer contracts, selective observation, envelopes, terminal projection, ordering, timeouts, payload projection, and detachment.

Model Provider Catalog

Layer / File(s) Summary
Provider metadata and model entries
resources/model-db/providers.json
Claude Opus 5 is added, GLM-5 pricing and embedding limits are updated, multiple chat models are removed, and Claude Opus 5 capabilities are adjusted.

Architecture Plans and Baselines

Layer / File(s) Summary
Typed hook architecture documentation
docs/architecture/deepchat-agent-harness-boundaries/*
Planning, specification, task, non-goal, and acceptance-criteria sections describe the typed hook pipeline and defer hook decision semantics.
Generated architecture baseline refresh
docs/architecture/baselines/*
Baseline timestamps, hashes, dependency metrics, outgoing dependencies, and cycle samples are regenerated.

ACP Registry Update

Layer / File(s) Summary
Dirac release metadata
resources/acp-registry/registry.json
The Dirac agent and its npx package are updated from version 0.4.24 to 0.4.25.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentRuntime
  participant RuntimeHookScope
  participant HookService
  participant HookCommand
  AgentRuntime->>RuntimeHookScope: scope(session facts)
  RuntimeHookScope->>HookService: emit typed HookEvent
  HookService->>HookService: project, gate, and queue event
  HookService->>HookCommand: execute accepted hook
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing the hook notification flow with a typed pipeline.
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.
✨ 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 refactor/typed-hook-pipeline

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

🧹 Nitpick comments (3)
test/main/hook/hookService.test.ts (2)

552-591: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

structuredClone spy can leak when an assertion fails.

clone.mockRestore() is the last statement in both tests, so a failing expect above it leaves the global spy installed for every subsequent test in the file. Restore in a hook instead.

♻️ Proposed change
 describe('HookService payload projection', () => {
   beforeEach(() => {
     spawnMock.mockReset()
     spawnMock.mockImplementation(() => {
       const child = new FakeChild()
       queueMicrotask(() => child.emit('close', 0))
       return child
     })
   })
+
+  afterEach(() => {
+    vi.restoreAllMocks()
+  })

Then drop the trailing clone.mockRestore() calls (remember to add afterEach to the vitest import).

🤖 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 `@test/main/hook/hookService.test.ts` around lines 552 - 591, Update the tests
around “truncates tool previews before any clone runs” and “clones only the
permission record” to restore the global structuredClone spy from an afterEach
hook, adding afterEach to the Vitest imports. Remove the trailing
clone.mockRestore() calls so cleanup still runs when an assertion fails.

238-245: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The same spawnMock beforeEach is repeated in all four describes.

Hoisting it to a single file-level beforeEach (or a setupSpawn() helper) removes four identical copies.

Also applies to: 402-409, 543-550, 731-738

🤖 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 `@test/main/hook/hookService.test.ts` around lines 238 - 245, Deduplicate the
repeated spawnMock setup across the four describe blocks by moving the shared
mockReset/mockImplementation logic into one file-level beforeEach or a
setupSpawn helper. Preserve creation of FakeChild instances and asynchronous
close emission for every test, and remove the duplicate per-describe hooks.
test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts (1)

52-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

createHookObserver is duplicated verbatim across two test files. Both files hand-roll the same HookEvent → dispatcher mapping and the same noopHookObserver, so every new HookEvent variant must be mirrored in both.

  • test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts#L52-L73: move createHookObserver and noopHookObserver into a shared test helper module and import them here.
  • test/main/session/runtimeIntegration.test.ts#L23-L44: import the shared helper instead of redefining it.
🤖 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 `@test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts` around lines
52 - 73, Move the shared createHookObserver mapping and noopHookObserver
definition into a common test helper, then remove the duplicated definitions and
import the helper in
test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts lines 52-73 and
test/main/session/runtimeIntegration.test.ts lines 23-44. Preserve the existing
HookObserver behavior and dispatcher payload mapping at both sites.
🤖 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 `@src/main/hook/index.ts`:
- Around line 479-480: Update the finalize paths in the child error handler and
stdin-write failure handler to wrap their truncateText(...) output with
redactSensitiveText(...), using the existing secrets value. Keep redaction
consistent with the timeout and close handlers before passing command output to
finalize(...).

---

Nitpick comments:
In `@test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts`:
- Around line 52-73: Move the shared createHookObserver mapping and
noopHookObserver definition into a common test helper, then remove the
duplicated definitions and import the helper in
test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts lines 52-73 and
test/main/session/runtimeIntegration.test.ts lines 23-44. Preserve the existing
HookObserver behavior and dispatcher payload mapping at both sites.

In `@test/main/hook/hookService.test.ts`:
- Around line 552-591: Update the tests around “truncates tool previews before
any clone runs” and “clones only the permission record” to restore the global
structuredClone spy from an afterEach hook, adding afterEach to the Vitest
imports. Remove the trailing clone.mockRestore() calls so cleanup still runs
when an assertion fails.
- Around line 238-245: Deduplicate the repeated spawnMock setup across the four
describe blocks by moving the shared mockReset/mockImplementation logic into one
file-level beforeEach or a setupSpawn helper. Preserve creation of FakeChild
instances and asynchronous close emission for every test, and remove the
duplicate per-describe hooks.
🪄 Autofix (Beta)

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: 15d1fdc4-f7b4-41d6-8164-03bdb5cab43c

📥 Commits

Reviewing files that changed from the base of the PR and between 2b70bb3 and 092b424.

📒 Files selected for processing (33)
  • docs/architecture/baselines/agent-system-layered-runtime-baseline.json
  • docs/architecture/baselines/dependency-report.md
  • docs/architecture/baselines/main-kernel-boundary-baseline.md
  • docs/architecture/baselines/main-kernel-migration-scoreboard.json
  • docs/architecture/baselines/main-kernel-migration-scoreboard.md
  • docs/architecture/baselines/zero-inbound-candidates.md
  • docs/architecture/deepchat-agent-harness-boundaries/plan.md
  • docs/architecture/deepchat-agent-harness-boundaries/spec.md
  • docs/architecture/deepchat-agent-harness-boundaries/tasks.md
  • resources/acp-registry/registry.json
  • resources/model-db/providers.json
  • src/main/agent/acp/compatibility/dependencies.ts
  • src/main/agent/deepchat/harness/createDeepChatAgentHarness.ts
  • src/main/agent/deepchat/loop/notificationObserver.ts
  • src/main/agent/deepchat/loop/ports.ts
  • src/main/agent/deepchat/runtime/deepChatLoopRunner.ts
  • src/main/agent/deepchat/runtime/interactionCoordinator.ts
  • src/main/agent/deepchat/runtime/runtimeHookSink.ts
  • src/main/agent/deepchat/runtime/turnCoordinator.ts
  • src/main/app/composition.ts
  • src/main/hook/events.ts
  • src/main/hook/index.ts
  • src/main/hook/observer.ts
  • src/main/hook/routes.ts
  • test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts
  • test/main/agent/deepchat/runtime/dispatch.test.ts
  • test/main/agent/deepchat/runtime/process.test.ts
  • test/main/agent/deepchat/runtime/runtimeHookSink.test.ts
  • test/main/app/compositionBoundaries.test.ts
  • test/main/evals/nativeAgent/harness.ts
  • test/main/hook/hookService.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/session/runtimeIntegration.test.ts

Comment thread src/main/hook/index.ts
Only the close and timeout paths ran hook stdout and stderr through
redactSensitiveText. The spawn-error path and the stdin-write-failure path
formatted the same output with truncateText alone, so a hook that had already
written a bearer token, a Discord or Telegram webhook URL, the session id or the
workspace path to stderr surfaced it unredacted in the settings test panel when
the command then failed to start or could not be fed its payload.

Hoisting `secrets` in the timeout change made the omission reachable but not
obvious, so all four paths now report through one diagnostics() helper rather
than repeating the formatting. Repetition is what let the two paths drift apart.
The structuredClone spies were restored by a trailing mockRestore(), so any
failing assertion above it left the spy installed on globalThis for every later
case in the file. Restoration moves to an afterEach hook that runs regardless.

The identical spawn stub was also declared in four describe blocks, and the
HookEvent to dispatcher mapping was duplicated verbatim between the harness and
runtime integration suites, where every new event variant had to be mirrored in
both. The stub becomes one file-level hook and the mapping one shared fixture.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/hook/index.ts (1)

510-525: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Release children on early settlement.

The timeout and stdin-write-failure paths kill the child and call finalize, but never remove it from activeChildren. If close does not arrive—as this timeout path explicitly anticipates—repeated failures retain stale ChildProcess references and make stop() revisit them indefinitely.

Add this.activeChildren.delete(child) in both paths, or centralize that cleanup in finalize.

🧹 Proposed cleanup
       const timeout = setTimeout(() => {
         timedOut = true
         try {
           child.kill('SIGKILL')
         } catch {
           // ignore
         }
+        this.activeChildren.delete(child)
         // A killed shell can keep its process tree alive and never emit `close`, so the timeout
         // settles the result itself instead of waiting for an exit that may never arrive.

Apply the equivalent cleanup after the stdin-write-failure child.kill(...) call.

Also applies to: 559-580

🤖 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 `@src/main/hook/index.ts` around lines 510 - 525, Remove the child from
activeChildren whenever execution settles before the close event: add
this.activeChildren.delete(child) in both the timeout handler around finalize
and the stdin-write-failure path after child.kill(...), or centralize equivalent
cleanup in finalize while preserving normal close handling.
🤖 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.

Outside diff comments:
In `@src/main/hook/index.ts`:
- Around line 510-525: Remove the child from activeChildren whenever execution
settles before the close event: add this.activeChildren.delete(child) in both
the timeout handler around finalize and the stdin-write-failure path after
child.kill(...), or centralize equivalent cleanup in finalize while preserving
normal close handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cbd12251-fad3-4136-8e45-5bb74992c7e7

📥 Commits

Reviewing files that changed from the base of the PR and between 092b424 and ec46e44.

📒 Files selected for processing (5)
  • src/main/hook/index.ts
  • test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts
  • test/main/hook/hookObserverFixture.ts
  • test/main/hook/hookService.test.ts
  • test/main/session/runtimeIntegration.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/main/hook/hookService.test.ts

@yyhhyyyyyy
yyhhyyyyyy merged commit c44c1fc into dev Jul 26, 2026
12 checks passed
@zhangmo8
zhangmo8 deleted the refactor/typed-hook-pipeline branch July 27, 2026 02:08
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.

1 participant