{"_id":"@buckeyestudio/toh-hooks-codex","name":"@buckeyestudio/toh-hooks-codex","dist-tags":{"next":"0.1.1-rc.2","latest":"0.1.1-rc.2"},"versions":{"0.1.1-rc.2":{"name":"@buckeyestudio/toh-hooks-codex","description":"Bridge plugin: run a Codex hooks.json hook config on the TheOpen Harness interception seams","version":"0.1.1-rc.2","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/dustinwloring1988/theopen-harness.git","directory":"packages/hooks/hooks-codex"},"type":"module","main":"lib/index.js","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./invariant":{"types":"./lib/types/invariant.d.ts","default":"./lib/invariant.js"},"./src/*":"./src/*","./package.json":"./package.json"},"license":"MIT","author":{"name":"buckeyestudio"},"dependencies":{"@buckeyestudio/schemastery":"^3.18.1"},"peerDependencies":{"@buckeyestudio/toh-agent":"^0.1.1-rc.2","@buckeyestudio/toh-hook-protocol":"^0.1.1-rc.2","@buckeyestudio/toh-llm":"^0.1.1-rc.2","@buckeyestudio/toh-invariants":"^0.1.1-rc.2","@buckeyestudio/toh-session-persistence":"^0.1.1-rc.2","@buckeyestudio/toh-session":"^0.1.1-rc.2","@buckeyestudio/cordis":"^4.0.1","@buckeyestudio/toh-tools":"^0.1.1-rc.2"},"devDependencies":{"@buckeyestudio/toh-agent":"^0.1.1-rc.2","@buckeyestudio/toh-agent-loop":"^0.1.1-rc.2","@buckeyestudio/toh-agent-loop-testkit":"^0.1.1-rc.2","@buckeyestudio/toh-shell":"^0.1.1-rc.2","@buckeyestudio/toh-bash-local":"^0.1.1-rc.2","@buckeyestudio/toh-subprocess-local":"^0.1.1-rc.2","@buckeyestudio/toh-hook-protocol":"^0.1.1-rc.2","@buckeyestudio/toh-invariants":"^0.1.1-rc.2","@buckeyestudio/toh-llm":"^0.1.1-rc.2","@buckeyestudio/toh-session-persistence":"^0.1.1-rc.2","@buckeyestudio/toh-session-persistence-jsonl":"^0.1.1-rc.2","@buckeyestudio/cordis":"^4.0.1","@buckeyestudio/toh-session":"^0.1.1-rc.2","@buckeyestudio/toh-tools":"^0.1.1-rc.2"},"_id":"@buckeyestudio/toh-hooks-codex@0.1.1-rc.2","bugs":{"url":"https://github.com/dustinwloring1988/theopen-harness/issues"},"homepage":"https://github.com/dustinwloring1988/theopen-harness#readme","_integrity":"sha512-aQDAbQF8ebTmLmGDN58xBVwRRIQuOAhx8rPQOpVMNTPFVth34u7LauW4qwC80R5H9xh8T6Se89cFSiW6CEClTw==","_resolved":"/home/runner/work/theopen-harness/theopen-harness/dist/npm/buckeyestudio-toh-hooks-codex-0.1.1-rc.2.tgz","_from":"file:/home/runner/work/theopen-harness/theopen-harness/dist/npm/buckeyestudio-toh-hooks-codex-0.1.1-rc.2.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-aQDAbQF8ebTmLmGDN58xBVwRRIQuOAhx8rPQOpVMNTPFVth34u7LauW4qwC80R5H9xh8T6Se89cFSiW6CEClTw==","shasum":"bfa6e69375bb1af6b5c2c60967c7482a24a7768f","tarball":"https://registry.npmjs.org/@buckeyestudio/toh-hooks-codex/-/toh-hooks-codex-0.1.1-rc.2.tgz","fileCount":10,"unpackedSize":38628,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIA5N5+w38fY+r3KQvH/fWJ0SoZf46UzkSss+gGZNd7fsAiEA8MyugNpdIl28HswItVhWWjzLxh8ta6VScOYRwYzp9Xg="}]},"_npmUser":{"name":"buckeyestudio","email":"dustinwloring1988@gmail.com"},"directories":{},"maintainers":[{"name":"buckeyestudio","email":"dustinwloring1988@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/toh-hooks-codex_0.1.1-rc.2_1787489799737_0.8894776052467765"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-23T12:56:39.410Z","0.1.1-rc.2":"2026-08-23T12:56:39.883Z","modified":"2026-08-23T12:56:40.184Z"},"maintainers":[{"name":"buckeyestudio","email":"dustinwloring1988@gmail.com"}],"description":"Bridge plugin: run a Codex hooks.json hook config on the TheOpen Harness interception seams","homepage":"https://github.com/dustinwloring1988/theopen-harness#readme","repository":{"type":"git","url":"git+https://github.com/dustinwloring1988/theopen-harness.git","directory":"packages/hooks/hooks-codex"},"author":{"name":"buckeyestudio"},"bugs":{"url":"https://github.com/dustinwloring1988/theopen-harness/issues"},"license":"MIT","readme":"# @buckeyestudio/toh-hooks-codex\n\n[English](README.md) | 中文\n\n一个 Cordis 插件，在 harness 的规范拦截点上运行用户现有 **Codex** hook 配置的受支持子集。它是 hooks 子系统中采用 **Codex 方言** 的一侧。方言无关原语来自 [`@buckeyestudio/toh-hook-protocol`](../hook-protocol/README.zh.md)；该桥接负责处理 Codex 形状的 payload、matcher 模式和决策映射。\n\n该桥接实现 Codex 当前 hook 协议的一个有意选取的子集：\n\n- **10 个 hook 点中的 5 个：** `PreToolUse`、`PostToolUse`、`SessionStart`、`UserPromptSubmit` 和 `Stop`。\n- **仅使用正则的 matcher**（没有字面量快速路径；matcher 始终是未锚定正则）。\n- **snake_case stdin payload**，携带 `turn_id`／`model` 额外字段，写入时**不带**尾随换行符。\n- **没有 Codex 插件 env 注入，也没有配置时 placeholder 替换**（命令仍会接收执行器环境，并通过其 shell 运行）。\n- **没有工具前审批或改写路径**：hook 可以阻塞，但桥接不会预审批或替换工具输入。\n\n原生 Cordis 插件可以完成此桥接的所有工作，并且功能更强；该桥接只是已映射 Codex 子集的兼容路径（见 [拦截扩展点 Agent Note](../../../.agents/notes/implemented/feature/2026-06-30-interception-extension-points.zh.md)）。\n\n## 配置\n\n```ts\nimport type { Config } from '@buckeyestudio/toh-hooks-codex'\nconst config: Config = {\n  configPath: '/path/to/.codex/hooks.json', // required\n  model: 'deepseek-v4',                      // optional: stamped on every payload (Codex includes `model`)\n  defaultTimeoutMs: 600_000,                 // optional: per-hook timeout when a hook sets none\n  stderrSummaryMaxChars: 500,                // optional: char cap on the hook/result event's persisted stderr summary\n}\n```\n\n在 `cordis.yml` 中：\n\n```yaml\n- toh-hooks-codex:\n    configPath: ./.codex/hooks.json\n    model: deepseek-v4\n```\n\n配置只在加载时解析**一次**。`configPath` 是**进程级**配置：相对路径在加载时根据进程启动 cwd 解析，而非每会话解析（`TODO(per-session-hook-config)`）。读取／解析失败会被隔离处理（记录 + 不注册任何内容）；实际消费 matcher 的事件所带的无效 matcher 正则属于此类失败，并报告其 pattern 与事件。只运行同步 `type: 'command'` hook；非 command 或 `async: true` hook 会被解析并跳过，同时记录警告。hook 接受 `timeout` 或 `timeoutSec` alias；两者都未设置时，使用协议参考默认值 `DEFAULT_HOOK_TIMEOUT_MS`（来自 `toh-hook-protocol`，10 分钟）。五个桥接支持点之外的事件会在解析时丢弃。\n\nhook 本身会在 agent（智能体）的会话工作区中运行：对 agent scope 点，桥接会将会话 `cwd` 作为 hook 进程工作目录，因此 hook 作用于用户项目树，而非服务器启动目录。\n\n## Hook 点 → 类型化 Decision\n\n| Codex hook | Harness 点 | 映射 |\n|---|---|---|\n| `SessionStart` | `agent/session-start`（emit） | 纯 stdout hook 的输出 → additionalContext → `agent.inject()` |\n| `UserPromptSubmit` | `agent/pre-step`（waterfall，瀑布式事件） | `block`（退出码 2）→ `PreStepDecision.reject`；仅 additionalContext → 通过 `next()` 委托，再向下游 `enter` 决策追加一条单独标记来源的消息 |\n| `PreToolUse` | `tools/pre-execute`（waterfall） | `block` → `PreToolDecision.deny`（没有 `allow`／`ask`） |\n| `PostToolUse` | `tools/post-execute`（waterfall） | `block` → 带反馈的 `block`；仅 additionalContext → 通过 `next()` 委托，再将一个单独标记源的上下文前置到下游决策；Code Mode 将子调用上下文延迟到外层 `run_code` 结果 |\n| `Stop` | `agent/turn-stopping`（serial） | 阻塞 Stop hook 通过 `steer()` 送入其原因，强制再执行一步 |\n\n工具调用的 payload 携带真实 `tool_name`（matcher 测试的相同值）与 Codex `tool_input: { command }` 形状（存在 `command` arg 时使用该值，否则使用 `''`）。matcher subject 是工具名称（`PreToolUse`／`PostToolUse`）或会话源（`SessionStart`）；`UserPromptSubmit`／`Stop` 忽略 matcher。\n\n每个 agent scope stdin payload 都携带 `session_id` 和 `transcript_path`。可用时，桥接通过 `ctx.sessionPersistence.locate(session.header)` 解析后者，否则发送 `null`，保留 Codex `string | null` 形状。查找不会创建或 flush 产物，因此在第一个轮次结束检查点之前，路径可能尚不存在，或其指向的 transcript（文本记录）可能尚未包含当前未结束的轮次。\n\n`SessionStart` 是唯一的 emit 点，它会脱离运行。每条运行链都会被跟踪；对桥接执行 dispose（资源释放）会中止仍在运行的 hook 进程，再排空 continuation，之后 dispose 才会完成（`createDetachedRuns`，位于 `toh-hook-protocol`）。\n\n## 上下文源\n\n注入上下文携带显式 `{ kind: 'plugin', plugin: 'hooks-codex' }` 来源，因此持久消息绝不会被误认为用户提示词。\n\n## 模型体验\n\n### Hook 提供的上下文\n\n#### 模型看到的内容\n\n`SessionStart`、已接受提示词和工具后 hook 可以添加带源归因的上下文消息；阻塞 `Stop` hook 将其原因添加为下一步 steering（中途引导）。\n\n#### Token 影响\n\nhook 不返回上下文时没有成本。Hook 文本取决于数据，会被记录，并重发直到压缩（compaction）。\n\n#### KV Cache 影响\n\n仅追加；新可见内容位于可复用请求前缀之后，不会使现有 KV Cache 条目失效。\n\n### 已阻塞提示词或工具结果\n\n#### 模型看到的内容\n\n提供方提供的原因逐字传递。缺失原因时，已阻塞提示词精确使用 `blocked by UserPromptSubmit hook`，已拒绝工具变为 `Error: blocked by PreToolUse hook`，已阻塞工具后反馈精确为 `blocked by PostToolUse hook`，阻塞 stop 则精确添加 steering `continue: blocked by Stop hook`。Codex `systemMessage` 不会呈现。\n\n#### Token 影响\n\n阻塞提示词不会产生该提示词对应的模型请求 token；拒绝或反馈会添加保留的回退或提供方文本；强制 continuation 需要另一个完整请求。\n\n#### KV Cache 影响\n\n已阻塞提示词不发送请求，不会导致失效。拒绝、反馈与强制 continuation 上下文会追加在可复用前缀之后，不改写前缀。\n\n## 已知限制与暂缓事项\n\n- **不支持的 hook 事件（Codex 当前 10 项中的 5 项）：** `PermissionRequest`、`PreCompact`、`PostCompact`、`SubagentStart` 和 `SubagentStop`。这些事件的配置会在解析期间静默丢弃。比较基线是 Codex [官方 hook 参考](https://learn.chatgpt.com/docs/hooks)。\n- **`SessionStart` 只支持部分功能：** 支持纯 stdout 与 JSON `additionalContext`，但 hook 脱离运行，因此上下文可能错过第一个请求（`TODO(session-start-gating)`）。\n- **`UserPromptSubmit` 只支持部分功能：** 支持阻塞加纯 stdout 或 JSON 上下文，但不会强制执行通用 `systemMessage` 和 `{\"continue\": false}` 控制。\n- **`PreToolUse` 只支持部分功能：** 支持阻塞，但会忽略 `additionalContext`、`permissionDecision: \"allow\"` 和 `updatedInput`。每个工具都表示为 `tool_input: { command }`，因此非 shell 工具参数不会如实公开给 hook。\n- **`PostToolUse` 只支持部分功能：** 支持阻塞反馈与 JSON `additionalContext`，但不会强制执行 `{\"continue\": false}`，非 shell 工具参数会缩减为 `{ command }`，结构化工具输出会在 `tool_response` 中展平为文本。\n- **`Stop` 只支持部分功能：** 阻塞会强制另一个模型轮次，但 `stop_hook_active` 始终为 `false`，`last_assistant_message` 始终为 `null`，且不会强制执行 `{\"continue\": false}`。因此，无条件阻塞 hook 会在每个步骤中强制 continuation，除非它自我限制（`TODO(stop-loop-guard)`）。\n- **通用 payload 与输出字段只支持部分功能：** 每个已映射事件都报告静态配置的 `model` 与 `permission_mode: \"default\"`，而非当前 Codex 运行时值。`systemMessage` 会被记录并触发警告，但不呈现，`{\"continue\": false}` 会被记录但不会应用 Codex 事件特定停止行为（`TODO(hook-continue-false)`）。\n- **配置加载与执行只支持部分功能：** 一个进程级 `configPath` 会在加载时解析；尚未实现 Codex 的活动用户层、项目层、会话层、系统／托管层和插件层、信任控制与内联 `config.toml` hook 形式（`TODO(per-session-hook-config)`）。只运行同步 `command` handler，忽略 `statusMessage` 与 `commandWindows` 等当前元数据，匹配 handler 串行运行，而非使用 Codex 的并发启动语义。\n","readmeFilename":"README.zh.md","_rev":"1-cd05385ac75d7ef92d7754d4085d92ae"}