Skip to content

feat(hooks): workspace events subscription hook#36302

Closed
JoshuaLelon wants to merge 4 commits intoopenclaw:mainfrom
JoshuaLelon:gws/07-events-hook
Closed

feat(hooks): workspace events subscription hook#36302
JoshuaLelon wants to merge 4 commits intoopenclaw:mainfrom
JoshuaLelon:gws/07-events-hook

Conversation

@JoshuaLelon
Copy link
Copy Markdown
Contributor

Summary

  • Adds Google Workspace Events subscription hook via gws events +subscribe, giving agents real-time awareness of Calendar changes, Drive file updates, Chat messages, etc.
  • Follows the Gmail gws pattern exactly: config → runtime resolver → arg builder → bridge (NDJSON→hook POST) → watcher (spawn + restart) → lifecycle wrapper → gateway startup/reload
  • Adds openclaw webhooks events setup and openclaw webhooks events run CLI commands

Changes

New files (8):

  • src/hooks/ws-events.ts — runtime config types, resolver, arg builder
  • src/hooks/ws-events-bridge.ts — CloudEvent NDJSON → hook payload transform
  • src/hooks/ws-events-watcher.ts — process lifecycle (spawn, restart, stop)
  • src/hooks/ws-events-watcher-lifecycle.ts — thin wrapper with env skip + logging
  • src/hooks/ws-events-ops.ts — setup + run command implementations
  • src/hooks/ws-events.test.ts — 13 tests for config resolver + arg builder
  • src/hooks/ws-events-bridge.test.ts — 13 tests for CloudEvent transform + NDJSON handler
  • docs/automation/workspace-events.md — documentation

Modified files (8):

  • src/config/types.hooks.tsHooksWorkspaceEventsConfig type
  • src/config/zod-schema.hooks.tsHooksWorkspaceEventsSchema
  • src/config/zod-schema.ts — wire schema into parent hooks object
  • src/gateway/hooks-mapping.ts"workspace-events" preset + allowUnsafeExternalContent
  • src/gateway/config-reload-plan.tsrestartWsEventsWatcher plan field + reload rule
  • src/gateway/server-startup.ts — start watcher on boot
  • src/gateway/server-reload-handlers.ts — handle watcher restart on config change
  • src/cli/webhooks-cli.tsevents setup + events run subcommands

Dependencies

Test plan

  • pnpm build — no type errors
  • pnpm check — lint/format clean
  • 26/26 workspace events tests pass
  • 41/41 existing Gmail tests still pass
  • 21/21 hooks-mapping tests still pass
  • Manual: openclaw webhooks events setup --target '//chat.googleapis.com/spaces/X' --event-types 'google.workspace.chat.message.v1.created' --project P

🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation gateway Gateway runtime cli CLI command changes size: XL labels Mar 5, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 5, 2026

Greptile Summary

This PR adds a Google Workspace Events subscription hook that wires real-time Calendar, Drive, and Chat changes into OpenClaw's agent hook system via the gws events +subscribe CLI. It follows the existing Gmail hook pattern (config types → resolver → arg builder → NDJSON bridge → watcher → lifecycle wrapper → gateway wiring) and adds openclaw webhooks events setup / run CLI commands. The implementation is well-structured and tests provide good coverage of the config resolver and bridge transform.

Key issues found:

  • Logic bug — openclaw webhooks events run only restarts once after a crash (src/hooks/ws-events-ops.ts): The restart handler in runWsEventsService is attached as a closure to the first child object. When the process crashes and a new child is spawned, the handler is not re-attached. The module-level fallback from spawnGwsEventsSubscribe is also a no-op in this path because currentConfig (module-level) is never set. After the second crash, gws stays down silently.
  • Dead config fields (src/config/types.hooks.ts): model and thinking are declared on HooksWorkspaceEventsConfig and exposed in the Zod schema, but are never read at runtime (unlike Gmail, which routes them through resolveHooksGmailModel). Users who configure these will see no effect.
  • Misleading ?? "unknown" guard (src/hooks/ws-events-bridge.ts): "".split(".").pop() returns "" rather than undefined, so the fallback never fires for empty event types, producing a malformed summary string with a leading space.

Confidence Score: 3/5

  • Safe to merge with caution — the gateway-managed watcher path is correct, but the CLI events run command has a restart reliability bug that will leave gws down after a second crash.
  • The core architecture (config, schema, gateway startup/reload, NDJSON bridge) is sound and well-tested. The single critical issue is confined to runWsEventsService in the CLI path: the restart loop breaks silently after the first recovery. This doesn't affect the gateway-managed watcher (which uses the correct module-level lifecycle), but makes openclaw webhooks events run unreliable for long-running use. Two additional style issues (dead model/thinking config fields, misleading pop fallback) are minor but should be addressed before the feature is considered complete.
  • src/hooks/ws-events-ops.ts — restart loop logic needs fixing for the CLI events run command.

Last reviewed commit: b908f48

Copy link
Copy Markdown

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

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: b908f48174

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

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

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: e3e6dba2c6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@openclaw-barnacle openclaw-barnacle bot added the app: macos App: macos label Mar 8, 2026
@openclaw-barnacle openclaw-barnacle bot added the agents Agent runtime and tooling label Mar 8, 2026
Copy link
Copy Markdown

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

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: 0fcdd5fe06

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

defaultRuntime.log(`Starting gws ${args.join(" ")}`);

let shuttingDown = false;
let child = spawnGwsEventsSubscribe(runtimeConfig);
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 Replace gateway watcher spawner in CLI run loop

runWsEventsService calls spawnGwsEventsSubscribe, but that helper is wired for gateway mode and always schedules a 5s restart timer on child exit unless the watcher module’s own global shuttingDown flag is set. In the CLI path, SIGINT/SIGTERM only flips a local shuttingDown, so an intentional stop of openclaw webhooks events run still leaves the gateway timer alive, delaying clean process exit (and potentially racing with the CLI restart loop). Use a CLI-specific spawner (or share shutdown state) so manual shutdown does not queue watcher restarts.

Useful? React with 👍 / 👎.

- Fix restart-loop: respawned child now gets exit handler via attachExitHandler pattern
- Fix ?? vs || in buildEventSummary so empty string falls back to 'unknown'
- Add stopWsEventsWatcher() call to gateway shutdown handler
- Add hooks.workspaceEvents.model startup validation (mirrors gmail model check)
- Factor out resolveHooksModel helper shared by gmail and ws-events
@openclaw-barnacle openclaw-barnacle bot removed the app: macos App: macos label Mar 9, 2026
Copy link
Copy Markdown

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

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: 9adcf031f1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +83 to +87
setTimeout(() => {
if (shuttingDown || !currentConfig) {
return;
}
watcherProcess = spawnGwsEventsSubscribe(currentConfig);
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 Clear pending restart timer before respawning watcher

The exit handler schedules an unconditional 5s restart timer, but that timer is never cancelled when stopWsEventsWatcher() runs. If gws crashes, then a config reload triggers stop/start before the 5s elapses, startWsEventsWatcher() resets shuttingDown and currentConfig, so the stale timer also spawns another child process; this creates duplicate subscriptions and leaves one process untracked by watcherProcess on later shutdowns.

Useful? React with 👍 / 👎.

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

Labels

agents Agent runtime and tooling cli CLI command changes docs Improvements or additions to documentation gateway Gateway runtime size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant