{"_id":"@buckeyestudio/toh-timeout","name":"@buckeyestudio/toh-timeout","dist-tags":{"next":"0.1.1-rc.2","latest":"0.1.1-rc.2"},"versions":{"0.1.1-rc.2":{"name":"@buckeyestudio/toh-timeout","description":"Zero-dependency timeout/deadline primitive: clampTimeout, deadline, timeoutOf, TimeoutReason (timing + classification only, no termination)","version":"0.1.1-rc.2","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/dustinwloring1988/theopen-harness.git","directory":"packages/util/timeout"},"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"},"peerDependencies":{"@buckeyestudio/toh-invariants":"^0.1.1-rc.2","@buckeyestudio/cordis":"^4.0.1"},"devDependencies":{"@buckeyestudio/toh-invariants":"^0.1.1-rc.2","@buckeyestudio/cordis":"^4.0.1"},"_id":"@buckeyestudio/toh-timeout@0.1.1-rc.2","bugs":{"url":"https://github.com/dustinwloring1988/theopen-harness/issues"},"homepage":"https://github.com/dustinwloring1988/theopen-harness#readme","_integrity":"sha512-iJ2GaLfgEBg/CBSFyDhYaUVCLYLMWuRiGl3b7fgOW+EtGwT8lmPV35wfWN8Orxj2qwbl1R0tusAm+tpWRrcayA==","_resolved":"/home/runner/work/theopen-harness/theopen-harness/dist/npm/buckeyestudio-toh-timeout-0.1.1-rc.2.tgz","_from":"file:/home/runner/work/theopen-harness/theopen-harness/dist/npm/buckeyestudio-toh-timeout-0.1.1-rc.2.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-iJ2GaLfgEBg/CBSFyDhYaUVCLYLMWuRiGl3b7fgOW+EtGwT8lmPV35wfWN8Orxj2qwbl1R0tusAm+tpWRrcayA==","shasum":"5276e7dbfb69ccc1a6e829d73a29c4b74344dcc0","tarball":"https://registry.npmjs.org/@buckeyestudio/toh-timeout/-/toh-timeout-0.1.1-rc.2.tgz","fileCount":9,"unpackedSize":26936,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGhJy/ib9rBBgzPYbCKtRVXJXYeb6vL4ZfA7yuJhAtxNAiApudOm6s2Jh7dYWL3P8hCBf8nUKosKO1KTdTpZjm5v1w=="}]},"_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-timeout_0.1.1-rc.2_1787488925137_0.45989250868596976"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-23T12:42:04.964Z","0.1.1-rc.2":"2026-08-23T12:42:05.271Z","modified":"2026-08-23T12:42:05.540Z"},"maintainers":[{"name":"buckeyestudio","email":"dustinwloring1988@gmail.com"}],"description":"Zero-dependency timeout/deadline primitive: clampTimeout, deadline, timeoutOf, TimeoutReason (timing + classification only, no termination)","homepage":"https://github.com/dustinwloring1988/theopen-harness#readme","repository":{"type":"git","url":"git+https://github.com/dustinwloring1988/theopen-harness.git","directory":"packages/util/timeout"},"author":{"name":"buckeyestudio"},"bugs":{"url":"https://github.com/dustinwloring1988/theopen-harness/issues"},"license":"MIT","readme":"# toh-timeout\n\n[English](README.md) | 中文\n\n超时的**时序与分类**部分：一个零依赖纯函数库（无运行时 harness 依赖），由每个需要限制调用方超时提示、启动 deadline，并在之后区分「已超时」与「已取消」的能力共享。\n\n它**不负责终止**。它发出的信号只会*通知*；真正停止工作仍由各能力负责，因为机制各不相同：bash 对操作系统进程组发送 SIGKILL，web 关闭 `fetch` 套接字，没有任何共享层能够承担全部终止机制。[Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.zh.md) 将边界划定为：共享时序/分类，将强制终止保留在本地。\n\n它是**库，而非服务或插件**：没有 `ctx`，不注册任何内容，不持有状态，也不发出事件。「超时服务」必须了解如何停止每项能力的工作，这正是微内核要排除在共享层之外的知识。\n\n## 对外接口\n\n```ts\nimport { clampTimeout, deadline, idleWatchdog, MAX_TIMER_DELAY_MS, timeoutOf, TimeoutReason } from '@buckeyestudio/toh-timeout'\n```\n\n| 导出项 | 职责 |\n|---|---|\n| `clampTimeout(requested, def, max, name?)` | 验证调用方可选的、值为正且有限的提示，从 `def` 填充，并限制在 `max` 以内。如果提示为非正数或非有限数，则抛出错误（包含 `name`）。 |\n| `deadline(upstream, timeoutMs, code)` | 将 `upstream` 取消与超时融合为一个 `AbortSignal`（`AbortSignal.any`）；超时携带 `TimeoutReason`。`[Symbol.dispose]` 清除 timer。 |\n| `idleWatchdog(upstream, timeoutMs, code)` | 保持一个稳定的融合信号，并且只在受保护的异步迭代器 `next()` 尚未完成时启动 timer。完成后停止 timer；后续需求或 `pulse()` 活动会重新启动 timer；dispose（资源释放）时清除；并发需求被拒绝。 |\n| `MAX_TIMER_DELAY_MS` | Node 在不将延迟限制为 1 毫秒时可调度的最大延迟（`2_147_483_647`）。负责 timer 的配置不得超过该值。 |\n| `timeoutOf(signal \\| { reason }, code?)` | 从已中止的信号/错误中恢复 `TimeoutReason`，否则返回 `undefined`，即超时与取消的分类器。传入 `code` 可仅匹配这个 deadline 的 timer（见下文的嵌套）。 |\n| `TimeoutReason` | 标记在超时中止上的内部原因（`code` + `timeoutMs`）。它不是公开错误；提供方将其转换为自己的错误/字段。 |\n\n## `timeoutMs <= 0` 哨兵值\n\n`0` 是后端自有后台工作（bash `start()`）使用的**内部**「无超时」值。`deadline()` 不启动 timer，只转发 `upstream`；如果也没有 upstream，它将返回永不中止的信号和无操作 disposer，因此每个调用方都能保持同一种调用形态。外部请求提示会通过 `clampTimeout` 验证为**正有限数**，之后才进入 `deadline`，因此 `0` 绝不是面向模型/插件的「禁用超时」值。\n\n## 使用形态\n\n```ts\nimport { deadline, timeoutOf } from '@buckeyestudio/toh-timeout'\n\ndeclare function runWork(options: { signal: AbortSignal }): Promise<unknown>\n\n// Scope-lifetime consumer (foreground bash, one fetch): `using` disposes the timer.\nexport async function runWithDeadline(upstream: AbortSignal | undefined, timeoutMs: number): Promise<unknown> {\n  using d = deadline(upstream, timeoutMs, 'BASH_TIMEOUT')\n  const outcome = await runWork({ signal: d.signal })               // work listens on d.signal and terminates itself\n  const timedOut = timeoutOf(d.signal, 'BASH_TIMEOUT') !== undefined // classify the first abort, scoped to OUR code\n  const aborted = d.signal.aborted && !timedOut                     // mutually exclusive: timeout won, or cancel did\n  return { outcome, timedOut, aborted }\n}\n```\n\n该信号只会*通知*；调用方必须接入自己的终止机制（`d.signal.addEventListener('abort', kill)`，或将 `d.signal` 传给 `fetch`）。让 promise 与 timer 竞速，会在子进程或套接字仍在泄漏时就让工具调用完成；发出信号则会强制要求存在真正的终止路径。\n\n将你自己的 `code` 传给 `timeoutOf`，使分类可在嵌套场景中正确组合。当 `upstream` 本身是 deadline 信号时，如果该 timer 先触发，`AbortSignal.any` 会保留它的 `TimeoutReason`。将匹配范围限定为你的 code，会把外部超时视为普通的 upstream 取消，而不会声称本地 timer 已到期。\n\n对于流式传输，创建一个 `idleWatchdog`，将其稳定的 `signal` 传给传输层，并为提供方的每次读取调用 `watchdog.next(iterator)`。当传输活动不产生迭代器值时，调用 `watchdog.pulse()`。间隔必须为正有限数，且不得超过 `MAX_TIMER_DELAY_MS`；否则 Node 会将其限制为 1 毫秒。它只对尚未完成的读取请求计时，因此当下游代码进行渲染或在请求下一个分片前以其他方式等待时，timer 不会运行。该原语仍然只会通知，因此传输层必须观察稳定信号；DeepSeek 和 pi-ai 适配器证明，超时会关闭它们的真实响应正文或 SDK 请求。\n\n## 哪些操作不设置超时\n\n本地文件 `read`/`write`/`edit` 不接受 `timeoutMs`：文件 IO 不设时限地运行，因为截止时间会中止操作系统仍会完成的工作。详见[文件系统子系统页面](../../../docs/subsystems/filesystem.zh.md)。\n\n## 模型体验\n\n通过 `toh-tool-call-timeout-policy` 等消费方间接影响模型；消费方可能会将提供方结果替换为已保留的超时错误，或抑制延迟结果。\n\n#### KV Cache 影响\n\n不会直接导致 KV Cache 失效；请求前缀变更由上述消费方负责。\n\n## 已知限制与暂缓事项\n\n- **只发出通知**：deadline 无法停止忽略其信号的工作；每项能力仍需要自己的 socket/进程/任务终止路径。\n- **`timeoutMs <= 0` 是内部词汇**：只有在所属后端已解析策略后，它才会禁用本地 timer；绝不会作为面向模型/插件的公开开关。\n- **第一个中止原因决定分类**：当 upstream 取消早于本地 timer 发生时，即使自己的超时之后也会到期，该层也无法再报告。\n- **空闲 watchdog 不是总 deadline**：它针对每个尚未完成的迭代器需求重新启动，并刻意排除消费方的处理时间。\n","readmeFilename":"README.zh.md","_rev":"1-9afe3cc6a05c19d599b6b69016d9175a"}