@humwick/runner 0.0.0-stage → 0.130.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +167 -0
- package/agent-bin/agent-relay-browser +10 -0
- package/agent-bin/humwick +10 -0
- package/hooks/fff-search-redirect/fff-search-redirect.cjs +161 -0
- package/hooks/fff-search-redirect/hooks.json +13 -0
- package/hooks/rg-r-guard/hooks.json +18 -0
- package/hooks/rg-r-guard/rg-r-guard.cjs +144 -0
- package/hooks/web-tools-redirect/hooks.json +13 -0
- package/hooks/web-tools-redirect/web-tools-redirect.cjs +84 -0
- package/node_modules/@humwick/offload/LICENSE +167 -0
- package/node_modules/@humwick/offload/README.md +57 -0
- package/node_modules/@humwick/offload/dist/async-runner.d.ts +18 -0
- package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/async-runner.js +158 -0
- package/node_modules/@humwick/offload/dist/async-runner.js.map +1 -0
- package/node_modules/@humwick/offload/dist/envelope.d.ts +25 -0
- package/node_modules/@humwick/offload/dist/envelope.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/envelope.js +80 -0
- package/node_modules/@humwick/offload/dist/envelope.js.map +1 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +10 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +59 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +1 -0
- package/node_modules/@humwick/offload/dist/index.d.ts +5 -0
- package/node_modules/@humwick/offload/dist/index.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/index.js +5 -0
- package/node_modules/@humwick/offload/dist/index.js.map +1 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +62 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.js +295 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +1 -0
- package/node_modules/@humwick/offload/dist/types.d.ts +111 -0
- package/node_modules/@humwick/offload/dist/types.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/types.js +176 -0
- package/node_modules/@humwick/offload/dist/types.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts +23 -0
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-pool.js +435 -0
- package/node_modules/@humwick/offload/dist/worker-pool.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +2 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.js +27 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker.d.ts +5 -0
- package/node_modules/@humwick/offload/dist/worker.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker.js +1 -0
- package/node_modules/@humwick/offload/dist/worker.js.map +1 -0
- package/node_modules/@humwick/offload/package.json +42 -0
- package/node_modules/@humwick/offload/src/async-runner.ts +170 -0
- package/node_modules/@humwick/offload/src/envelope.ts +92 -0
- package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +49 -0
- package/node_modules/@humwick/offload/src/index.ts +4 -0
- package/node_modules/@humwick/offload/src/subprocess-runner.ts +334 -0
- package/node_modules/@humwick/offload/src/types.ts +235 -0
- package/node_modules/@humwick/offload/src/worker-pool.ts +463 -0
- package/node_modules/@humwick/offload/src/worker-runtime.ts +32 -0
- package/node_modules/@humwick/offload/src/worker.ts +5 -0
- package/package.json +47 -4
- package/plugins/browser/skills/playwright-cli/SKILL.md +53 -0
- package/plugins/claude/.claude-plugin/plugin.json +8 -0
- package/plugins/claude/claude-statusline-probe.cjs +144 -0
- package/plugins/claude/hooks/gate-run-guard.provisioned.mjs +914 -0
- package/plugins/claude/hooks/gate-run-guard.sh +9 -0
- package/plugins/claude/hooks/gate-run-guard.ts +237 -0
- package/plugins/claude/hooks/hooks.json +158 -0
- package/plugins/claude/hooks/kill-guard.provisioned.mjs +7027 -0
- package/plugins/claude/hooks/kill-guard.sh +341 -0
- package/plugins/claude/hooks/kill-guard.ts +337 -0
- package/plugins/claude/hooks/permission-request.sh +22 -0
- package/plugins/claude/hooks/post-compact.sh +10 -0
- package/plugins/claude/hooks/post-tool-use.sh +8 -0
- package/plugins/claude/hooks/pre-compact.sh +12 -0
- package/plugins/claude/hooks/read-only-bash-guard.sh +587 -0
- package/plugins/claude/hooks/relay-status.sh +373 -0
- package/plugins/claude/hooks/session-end.sh +26 -0
- package/plugins/claude/hooks/session-start.sh +31 -0
- package/plugins/claude/hooks/shell-trust.ts +28 -0
- package/plugins/claude/hooks/stop-failure.sh +47 -0
- package/plugins/claude/hooks/stop.sh +48 -0
- package/plugins/claude/hooks/subagent-guard.cjs +26 -0
- package/plugins/claude/hooks/subagent-skill-policy.cjs +160 -0
- package/plugins/claude/hooks/subagent-start.sh +13 -0
- package/plugins/claude/hooks/subagent-stop.sh +19 -0
- package/plugins/claude/hooks/user-prompt-submit.sh +18 -0
- package/plugins/claude/hooks/wait-loop-guard.ts +120 -0
- package/plugins/claude/monitors/monitors.json +7 -0
- package/plugins/claude/monitors/relay-monitor.provisioned.mjs +786 -0
- package/plugins/claude/monitors/relay-monitor.ts +58 -0
- package/plugins/claude/skills/disconnect/SKILL.md +16 -0
- package/plugins/claude/skills/label/SKILL.md +23 -0
- package/plugins/claude/skills/message/SKILL.md +40 -0
- package/plugins/claude/skills/pair/SKILL.md +30 -0
- package/plugins/claude/skills/react/SKILL.md +18 -0
- package/plugins/claude/skills/read-message/SKILL.md +24 -0
- package/plugins/claude/skills/reply/SKILL.md +44 -0
- package/plugins/claude/skills/send-claimable/SKILL.md +39 -0
- package/plugins/claude/skills/slice-first/SKILL.md +32 -0
- package/plugins/claude/skills/slice-first/references/plan-recipe.md +46 -0
- package/plugins/claude/skills/status/SKILL.md +16 -0
- package/plugins/claude/skills/tags/SKILL.md +25 -0
- package/plugins/claude/skills/task/SKILL.md +28 -0
- package/plugins/claude-output-style/.claude-plugin/plugin.json +5 -0
- package/plugins/claude-output-style/output-styles/agent-relay.md +5 -0
- package/plugins/codex/skills/disconnect/SKILL.md +15 -0
- package/plugins/codex/skills/guide/SKILL.md +15 -0
- package/plugins/codex/skills/label/SKILL.md +22 -0
- package/plugins/codex/skills/message/SKILL.md +40 -0
- package/plugins/codex/skills/pair/SKILL.md +29 -0
- package/plugins/codex/skills/react/SKILL.md +17 -0
- package/plugins/codex/skills/read-message/SKILL.md +23 -0
- package/plugins/codex/skills/reply/SKILL.md +44 -0
- package/plugins/codex/skills/send-claimable/SKILL.md +39 -0
- package/plugins/codex/skills/slice-first/SKILL.md +32 -0
- package/plugins/codex/skills/slice-first/references/plan-recipe.md +46 -0
- package/plugins/codex/skills/status/SKILL.md +15 -0
- package/plugins/codex/skills/tags/SKILL.md +24 -0
- package/plugins/codex/skills/task/SKILL.md +28 -0
- package/src/adapter.ts +1061 -0
- package/src/adapters/__fixtures__/codex-app-server.ts +47 -0
- package/src/adapters/claude-api.ts +243 -0
- package/src/adapters/claude-context-command.ts +16 -0
- package/src/adapters/claude-credentials.ts +22 -0
- package/src/adapters/claude-delivery-lanes.ts +28 -0
- package/src/adapters/claude-delivery.ts +166 -0
- package/src/adapters/claude-effort.ts +94 -0
- package/src/adapters/claude-exit.ts +290 -0
- package/src/adapters/claude-headless-mirror.ts +88 -0
- package/src/adapters/claude-headless-refusal.ts +51 -0
- package/src/adapters/claude-headless.ts +617 -0
- package/src/adapters/claude-monitor-delivery-plan.ts +317 -0
- package/src/adapters/claude-permission.ts +243 -0
- package/src/adapters/claude-prompt-gates.ts +269 -0
- package/src/adapters/claude-prompt-hook.ts +145 -0
- package/src/adapters/claude-session-capture.ts +293 -0
- package/src/adapters/claude-session-probe.ts +414 -0
- package/src/adapters/claude-shutdown.ts +190 -0
- package/src/adapters/claude-status-detectors.ts +426 -0
- package/src/adapters/claude-subagents.ts +93 -0
- package/src/adapters/claude-submit-confirmation.ts +287 -0
- package/src/adapters/claude-tmux.ts +548 -0
- package/src/adapters/claude-token-usage-capture.ts +253 -0
- package/src/adapters/claude-tool-policy.ts +165 -0
- package/src/adapters/claude-transcript-tail.ts +193 -0
- package/src/adapters/claude-transcript.ts +812 -0
- package/src/adapters/claude.ts +1097 -0
- package/src/adapters/codex-agent-message-capture.ts +189 -0
- package/src/adapters/codex-app-server.ts +70 -0
- package/src/adapters/codex-client.ts +362 -0
- package/src/adapters/codex-effective-config.ts +87 -0
- package/src/adapters/codex-launch-config.ts +153 -0
- package/src/adapters/codex-rollout-context.ts +272 -0
- package/src/adapters/codex-skill-discovery.ts +18 -0
- package/src/adapters/codex-token-usage-capture.ts +163 -0
- package/src/adapters/codex-tool-output.ts +53 -0
- package/src/adapters/codex-turn-outcome.ts +27 -0
- package/src/adapters/codex-version.ts +64 -0
- package/src/adapters/codex.ts +1442 -0
- package/src/adapters/incremental-file.ts +110 -0
- package/src/adapters/opencode-api.ts +71 -0
- package/src/adapters/opencode-context.ts +277 -0
- package/src/adapters/opencode-model-registry.ts +126 -0
- package/src/adapters/opencode-provider.ts +169 -0
- package/src/adapters/opencode-server-retry.ts +50 -0
- package/src/adapters/opencode-soft-steer.ts +302 -0
- package/src/adapters/opencode-spawn.ts +145 -0
- package/src/adapters/opencode-subscribe.ts +62 -0
- package/src/adapters/opencode-token-usage-capture.ts +187 -0
- package/src/adapters/opencode-tool-events.ts +302 -0
- package/src/adapters/opencode-tools-config.ts +17 -0
- package/src/adapters/opencode.ts +2264 -0
- package/src/adapters/project-shell.ts +80 -0
- package/src/adapters/provider-launch-config.ts +21 -0
- package/src/adapters/provider-quota-harvest.ts +127 -0
- package/src/adapters/service-access.ts +153 -0
- package/src/adapters/tmux-offload.ts +207 -0
- package/src/adapters/tool-summary.ts +214 -0
- package/src/agent-redact.ts +75 -0
- package/src/agent-runner/commands/dispatch.ts +185 -0
- package/src/agent-runner/commands/lifecycle.ts +84 -0
- package/src/agent-runner/commands/prompt.ts +284 -0
- package/src/agent-runner/credentials.ts +208 -0
- package/src/agent-runner/http-liveness.ts +85 -0
- package/src/agent-runner/lifecycle-milestones.ts +207 -0
- package/src/agent-runner/lifecycle-phase.ts +80 -0
- package/src/agent-runner/registration.ts +163 -0
- package/src/agent-runner/runner-context.ts +158 -0
- package/src/agent-runner/session-mirror.ts +278 -0
- package/src/agent-runner/task-claims.ts +116 -0
- package/src/agent-runner/token-usage-coverage.ts +54 -0
- package/src/agent-runner/turn-report.ts +269 -0
- package/src/api-budget-accounting.ts +91 -0
- package/src/area-memory.ts +255 -0
- package/src/artifact-path-materialize.ts +52 -0
- package/src/async-guard.ts +35 -0
- package/src/attachment-cache-sweep.ts +31 -0
- package/src/attachment-cache.ts +195 -0
- package/src/background-script-reconcile.ts +106 -0
- package/src/bounded-log.ts +59 -0
- package/src/bounded-spawn-sync.ts +18 -0
- package/src/busy-reconciler.ts +282 -0
- package/src/claim-tracker.ts +140 -0
- package/src/claude-settings.ts +167 -0
- package/src/claude-workspace-trust.ts +289 -0
- package/src/config.ts +547 -0
- package/src/context-boundary.ts +101 -0
- package/src/context-command.ts +151 -0
- package/src/context-probe-state.ts +6 -0
- package/src/continuation-archive.ts +179 -0
- package/src/control-server.ts +730 -0
- package/src/delivery-policy.ts +9 -0
- package/src/delivery-result.ts +24 -0
- package/src/effort-command.ts +125 -0
- package/src/empty-turn.ts +816 -0
- package/src/free-port.ts +25 -0
- package/src/fs-offload.ts +26 -0
- package/src/gate-run-config.ts +35 -0
- package/src/gate-run-lease-probe.ts +38 -0
- package/src/host-asset-plan.ts +450 -0
- package/src/host-claude-config-worker.ts +34 -0
- package/src/host-sources.ts +40 -0
- package/src/inbox-carrier.ts +7 -0
- package/src/index.ts +386 -0
- package/src/launch-assembly.ts +955 -0
- package/src/liveness.ts +105 -0
- package/src/logger.ts +108 -0
- package/src/mcp-outbox.ts +204 -0
- package/src/mcp-preflight.ts +86 -0
- package/src/mcp-source-selection.ts +54 -0
- package/src/mcp-tool-deferral.ts +35 -0
- package/src/mcp-vault-env.ts +25 -0
- package/src/message-body-config/config.ts +16 -0
- package/src/message-body-config.ts +1 -0
- package/src/mid-turn-delivery.ts +43 -0
- package/src/native-memory-lever.ts +329 -0
- package/src/native-self-resume.ts +244 -0
- package/src/outbox.ts +453 -0
- package/src/park-post.ts +17 -0
- package/src/pending-permission-store.ts +148 -0
- package/src/process-meta.ts +9 -0
- package/src/profile-home.ts +419 -0
- package/src/profile-projection.ts +411 -0
- package/src/project-hooks.ts +65 -0
- package/src/project-instructions.ts +119 -0
- package/src/provider-double.ts +59 -0
- package/src/provider-hold.ts +228 -0
- package/src/provider-home-marker.ts +3 -0
- package/src/provider-home-tombstone.ts +4 -0
- package/src/provider-input-queue.ts +134 -0
- package/src/provider-integration.ts +93 -0
- package/src/provider-processes.ts +17 -0
- package/src/provider-submit-failure.ts +15 -0
- package/src/providers.ts +35 -0
- package/src/provisioning.ts +703 -0
- package/src/question-decision.ts +18 -0
- package/src/quota.ts +22 -0
- package/src/rate-limit.ts +351 -0
- package/src/refused-prompt-store.ts +36 -0
- package/src/relay-injection-events.ts +161 -0
- package/src/relay-instructions.ts +107 -0
- package/src/relay-mcp-proxy.long-turn-e2e.ts +183 -0
- package/src/relay-mcp-proxy.ts +704 -0
- package/src/relay-mcp.ts +583 -0
- package/src/reply-obligation-cache.ts +211 -0
- package/src/response-capture-report.ts +111 -0
- package/src/response-routing.ts +6 -0
- package/src/runner-core.ts +2955 -0
- package/src/runner-exit.ts +12 -0
- package/src/runner-helpers.ts +582 -0
- package/src/runner-insights.ts +105 -0
- package/src/runner-mcp-proxy.ts +46 -0
- package/src/runner-spawn-config.ts +68 -0
- package/src/runner-stale-scratch-sweep.ts +21 -0
- package/src/runner.ts +9 -0
- package/src/runtime-token-file.ts +45 -0
- package/src/runtime-token-remint.ts +56 -0
- package/src/service-access.ts +88 -0
- package/src/session-destroy.ts +27 -0
- package/src/session-insights.ts +118 -0
- package/src/session-scratch.ts +296 -0
- package/src/spawn-input.ts +147 -0
- package/src/startup-config-fetch.ts +48 -0
- package/src/subreaper.ts +113 -0
- package/src/template-resolver.ts +29 -0
- package/src/test-run-dedup.ts +435 -0
- package/src/transcript-context.ts +99 -0
- package/src/transport-errors.ts +48 -0
- package/src/turn-opener.ts +55 -0
- package/src/turn-reconcile-event.ts +88 -0
- package/src/vault-proxy-hop.ts +99 -0
- package/src/version.ts +44 -0
- package/src/web-tool-call-proxy.ts +128 -0
- package/src/web-tools.ts +59 -0
- package/README.md +0 -3
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# PolyForm Shield License 1.0.0
|
|
2
|
+
|
|
3
|
+
<https://polyformproject.org/licenses/shield/1.0.0>
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
In order to get any license under these terms, you must agree
|
|
8
|
+
to them as both strict obligations and conditions to all
|
|
9
|
+
your licenses.
|
|
10
|
+
|
|
11
|
+
## Copyright License
|
|
12
|
+
|
|
13
|
+
The licensor grants you a copyright license for the
|
|
14
|
+
software to do everything you might do with the software
|
|
15
|
+
that would otherwise infringe the licensor's copyright
|
|
16
|
+
in it for any permitted purpose. However, you may
|
|
17
|
+
only distribute the software according to [Distribution
|
|
18
|
+
License](#distribution-license) and make changes or new works
|
|
19
|
+
based on the software according to [Changes and New Works
|
|
20
|
+
License](#changes-and-new-works-license).
|
|
21
|
+
|
|
22
|
+
## Distribution License
|
|
23
|
+
|
|
24
|
+
The licensor grants you an additional copyright license
|
|
25
|
+
to distribute copies of the software. Your license
|
|
26
|
+
to distribute covers distributing the software with
|
|
27
|
+
changes and new works permitted by [Changes and New Works
|
|
28
|
+
License](#changes-and-new-works-license).
|
|
29
|
+
|
|
30
|
+
## Notices
|
|
31
|
+
|
|
32
|
+
You must ensure that anyone who gets a copy of any part of
|
|
33
|
+
the software from you also gets a copy of these terms or the
|
|
34
|
+
URL for them above, as well as copies of any plain-text lines
|
|
35
|
+
beginning with `Required Notice:` that the licensor provided
|
|
36
|
+
with the software. For example:
|
|
37
|
+
|
|
38
|
+
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
|
39
|
+
|
|
40
|
+
## Changes and New Works License
|
|
41
|
+
|
|
42
|
+
The licensor grants you an additional copyright license to
|
|
43
|
+
make changes and new works based on the software for any
|
|
44
|
+
permitted purpose.
|
|
45
|
+
|
|
46
|
+
## Patent License
|
|
47
|
+
|
|
48
|
+
The licensor grants you a patent license for the software that
|
|
49
|
+
covers patent claims the licensor can license, or becomes able
|
|
50
|
+
to license, that you would infringe by using the software.
|
|
51
|
+
|
|
52
|
+
## Noncompete
|
|
53
|
+
|
|
54
|
+
Any purpose is a permitted purpose, except for providing any
|
|
55
|
+
product that competes with the software or any product the
|
|
56
|
+
licensor or any of its affiliates provides using the software.
|
|
57
|
+
|
|
58
|
+
## Competition
|
|
59
|
+
|
|
60
|
+
Goods and services compete even when they provide functionality
|
|
61
|
+
through different kinds of interfaces or for different technical
|
|
62
|
+
platforms. Applications can compete with services, libraries
|
|
63
|
+
with plugins, frameworks with development tools, and so on,
|
|
64
|
+
even if they're written in different programming languages
|
|
65
|
+
or for different computer architectures. Goods and services
|
|
66
|
+
compete even when provided free of charge. If you market a
|
|
67
|
+
product as a practical substitute for the software or another
|
|
68
|
+
product, it definitely competes.
|
|
69
|
+
|
|
70
|
+
## New Products
|
|
71
|
+
|
|
72
|
+
If you are using the software to provide a product that does
|
|
73
|
+
not compete, but the licensor or any of its affiliates brings
|
|
74
|
+
your product into competition by providing a new version of
|
|
75
|
+
the software or another product using the software, you may
|
|
76
|
+
continue using versions of the software available under these
|
|
77
|
+
terms beforehand to provide your competing product, but not
|
|
78
|
+
any later versions.
|
|
79
|
+
|
|
80
|
+
## Discontinued Products
|
|
81
|
+
|
|
82
|
+
You may begin using the software to compete with a product
|
|
83
|
+
or service that the licensor or any of its affiliates has
|
|
84
|
+
stopped providing, unless the licensor includes a plain-text
|
|
85
|
+
line beginning with `Licensor Line of Business:` with the
|
|
86
|
+
software that mentions that line of business. For example:
|
|
87
|
+
|
|
88
|
+
> Licensor Line of Business: YoyodyneCMS Content Management
|
|
89
|
+
System (http://example.com/cms)
|
|
90
|
+
|
|
91
|
+
## Sales of Business
|
|
92
|
+
|
|
93
|
+
If the licensor or any of its affiliates sells a line of
|
|
94
|
+
business developing the software or using the software
|
|
95
|
+
to provide a product, the buyer can also enforce
|
|
96
|
+
[Noncompete](#noncompete) for that product.
|
|
97
|
+
|
|
98
|
+
## Fair Use
|
|
99
|
+
|
|
100
|
+
You may have "fair use" rights for the software under the
|
|
101
|
+
law. These terms do not limit them.
|
|
102
|
+
|
|
103
|
+
## No Other Rights
|
|
104
|
+
|
|
105
|
+
These terms do not allow you to sublicense or transfer any of
|
|
106
|
+
your licenses to anyone else, or prevent the licensor from
|
|
107
|
+
granting licenses to anyone else. These terms do not imply
|
|
108
|
+
any other licenses.
|
|
109
|
+
|
|
110
|
+
## Patent Defense
|
|
111
|
+
|
|
112
|
+
If you make any written claim that the software infringes or
|
|
113
|
+
contributes to infringement of any patent, your patent license
|
|
114
|
+
for the software granted under these terms ends immediately. If
|
|
115
|
+
your company makes such a claim, your patent license ends
|
|
116
|
+
immediately for work on behalf of your company.
|
|
117
|
+
|
|
118
|
+
## Violations
|
|
119
|
+
|
|
120
|
+
The first time you are notified in writing that you have
|
|
121
|
+
violated any of these terms, or done anything with the software
|
|
122
|
+
not covered by your licenses, your licenses can nonetheless
|
|
123
|
+
continue if you come into full compliance with these terms,
|
|
124
|
+
and take practical steps to correct past violations, within
|
|
125
|
+
32 days of receiving notice. Otherwise, all your licenses
|
|
126
|
+
end immediately.
|
|
127
|
+
|
|
128
|
+
## No Liability
|
|
129
|
+
|
|
130
|
+
***As far as the law allows, the software comes as is, without
|
|
131
|
+
any warranty or condition, and the licensor will not be liable
|
|
132
|
+
to you for any damages arising out of these terms or the use
|
|
133
|
+
or nature of the software, under any kind of legal claim.***
|
|
134
|
+
|
|
135
|
+
## Definitions
|
|
136
|
+
|
|
137
|
+
The **licensor** is the individual or entity offering these
|
|
138
|
+
terms, and the **software** is the software the licensor makes
|
|
139
|
+
available under these terms.
|
|
140
|
+
|
|
141
|
+
A **product** can be a good or service, or a combination
|
|
142
|
+
of them.
|
|
143
|
+
|
|
144
|
+
**You** refers to the individual or entity agreeing to these
|
|
145
|
+
terms.
|
|
146
|
+
|
|
147
|
+
**Your company** is any legal entity, sole proprietorship,
|
|
148
|
+
or other kind of organization that you work for, plus all
|
|
149
|
+
its affiliates.
|
|
150
|
+
|
|
151
|
+
**Affiliates** means the other organizations than an
|
|
152
|
+
organization has control over, is under the control of, or is
|
|
153
|
+
under common control with.
|
|
154
|
+
|
|
155
|
+
**Control** means ownership of substantially all the assets of
|
|
156
|
+
an entity, or the power to direct its management and policies
|
|
157
|
+
by vote, contract, or otherwise. Control can be direct or
|
|
158
|
+
indirect.
|
|
159
|
+
|
|
160
|
+
**Your licenses** are all the licenses granted to you for the
|
|
161
|
+
software under these terms.
|
|
162
|
+
|
|
163
|
+
**Use** means anything you do with the software requiring one
|
|
164
|
+
of your licenses.
|
|
165
|
+
|
|
166
|
+
Required Notice: Copyright 2026 Exelerus AB
|
|
167
|
+
Licensor Line of Business: provider-independent runtimes and coordination software for teams of AI agents
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @humwick/offload
|
|
2
|
+
|
|
3
|
+
Bounded execution primitives for CPU workers, cooperative async I/O, and subprocesses.
|
|
4
|
+
All runners use `OffloadEnvelope` for timeout, abort, structured logging, labels, typed
|
|
5
|
+
`OffloadError`, and injected escalation.
|
|
6
|
+
|
|
7
|
+
## Maintenance adoption
|
|
8
|
+
|
|
9
|
+
`WorkerPool` and `SubprocessRunner` expose `offThread: true`; this is an intentional
|
|
10
|
+
marker, not an automatic scheduler integration. A maintenance job that hands its heavy
|
|
11
|
+
work to either runner must explicitly wire the existing #1757 seam immediately after
|
|
12
|
+
starting it:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
const work = runner.run("heavy-job", input, { signal: context.signal });
|
|
16
|
+
if (runner.offThread) context.markOffThread?.();
|
|
17
|
+
await work;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`AsyncRunner` is bounded but stays on the process event loop, so it does not advertise
|
|
21
|
+
`offThread`.
|
|
22
|
+
|
|
23
|
+
## Shutdown
|
|
24
|
+
|
|
25
|
+
Every runner has a teardown that cancels rather than waits: `WorkerPool.destroy()`,
|
|
26
|
+
`AsyncRunner.destroy()` and `SubprocessRunner.destroy()` reject outstanding work with an
|
|
27
|
+
`aborted` `OffloadError`, kill live subprocess groups, and refuse new work. `drain()` is
|
|
28
|
+
the opposite — it waits for accepted work to finish and cancels nothing.
|
|
29
|
+
|
|
30
|
+
Because subprocess children run in their own process group, they are reparented rather
|
|
31
|
+
than reaped when the host dies; the runner installs a single process `exit` hook that
|
|
32
|
+
SIGKILLs any group still alive. A host killed with SIGKILL cannot run that hook, so a
|
|
33
|
+
supervisor is still the only guarantee there.
|
|
34
|
+
|
|
35
|
+
## Bounds worth knowing
|
|
36
|
+
|
|
37
|
+
- `timeoutMs` (and abort) are hard for subprocesses: SIGTERM, then SIGKILL to the process
|
|
38
|
+
group after `killGraceMs` (default 2s), so a child that traps SIGTERM cannot outlive
|
|
39
|
+
its deadline. The concurrency slot is held until the child is actually reaped, so
|
|
40
|
+
`stats` never reports a slot as free while its work is still running.
|
|
41
|
+
- A non-zero exit is a failure by default (it rejects and takes an escalation strike). Probe
|
|
42
|
+
callers whose answer *is* the exit code — `tmux has-session`, `which`, `git rev-parse`, a
|
|
43
|
+
land gate — must declare it with `okExitCodes` (`[0, 1]`, or `"any"` when the failing codes
|
|
44
|
+
are not enumerable). Without it the escalation signal inverts: the sink pages on a healthy
|
|
45
|
+
fleet and then latches, so the real outage goes unreported.
|
|
46
|
+
**`"any"` also accepts signal deaths** — 137 (SIGKILL, typically the OOM killer) and 139
|
|
47
|
+
(SIGSEGV) come back as successes and re-arm the strike counter. That is deliberate for a
|
|
48
|
+
land gate, where an OOM-killed gate is just a failed gate, but it means an infrastructure
|
|
49
|
+
death reads as a healthy answer and never escalates. Prefer an explicit code list unless
|
|
50
|
+
the failing codes genuinely cannot be enumerated; timeout and abort still throw either way.
|
|
51
|
+
- Subprocess output is captured up to `maxOutputBytes` per stream (default 8 MiB) and the
|
|
52
|
+
result carries `truncated`. The stream is still drained past the cap so a chatty child
|
|
53
|
+
never blocks on a full pipe.
|
|
54
|
+
- `AsyncRunner` work that ignores its signal keeps its slot until it returns: the cap
|
|
55
|
+
degrades toward serial rather than over-admitting. `destroy()` still returns promptly.
|
|
56
|
+
- A worker round-trip costs ~110µs, so tasks below roughly 1ms of CPU are not worth
|
|
57
|
+
offloading. `run()` has no backpressure: the queue is unbounded by design.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type OffloadDefaults, type OffloadEnvelope } from "./types.js";
|
|
2
|
+
export interface AsyncRunnerOptions extends OffloadDefaults {
|
|
3
|
+
concurrency: number;
|
|
4
|
+
}
|
|
5
|
+
export interface AsyncRunner {
|
|
6
|
+
run<T>(taskName: string, fn: (signal: AbortSignal) => Promise<T> | T, env?: OffloadEnvelope): Promise<T>;
|
|
7
|
+
readonly stats: {
|
|
8
|
+
active: number;
|
|
9
|
+
queued: number;
|
|
10
|
+
};
|
|
11
|
+
/** Resolves when everything accepted so far has finished. Does not cancel anything. */
|
|
12
|
+
drain(): Promise<void>;
|
|
13
|
+
/** Cancels in-flight work, settles every outstanding caller, and refuses new work. */
|
|
14
|
+
destroy(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/** Bounded I/O runner. Work receives the composed signal and must cooperate with aborts. */
|
|
17
|
+
export declare function createAsyncRunner(options: AsyncRunnerOptions): AsyncRunner;
|
|
18
|
+
//# sourceMappingURL=async-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-runner.d.ts","sourceRoot":"","sources":["../src/async-runner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAEtI,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzG,QAAQ,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,uFAAuF;IACvF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,sFAAsF;IACtF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAYD,4FAA4F;AAC5F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CA6I1E"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import PQueue from "p-queue";
|
|
2
|
+
import { beginRun, finishLog, recordResult, startLog } from "./envelope.js";
|
|
3
|
+
import { OffloadError, StrikeCounter, normalizeError, validateTimeout } from "./types.js";
|
|
4
|
+
/** Bounded I/O runner. Work receives the composed signal and must cooperate with aborts. */
|
|
5
|
+
export function createAsyncRunner(options) {
|
|
6
|
+
if (!Number.isSafeInteger(options.concurrency) || options.concurrency < 1)
|
|
7
|
+
throw new RangeError("concurrency must be a positive integer");
|
|
8
|
+
const queue = new PQueue({ concurrency: options.concurrency });
|
|
9
|
+
const counter = new StrikeCounter(options.escalationThreshold, options.escalationWindowAttempts);
|
|
10
|
+
const live = new Set();
|
|
11
|
+
let destroyed = false;
|
|
12
|
+
const abortedError = (taskName, cause) => new OffloadError({ kind: "aborted", taskName, message: `${taskName} was aborted`, cause });
|
|
13
|
+
const destroyedError = (taskName) => new OffloadError({ kind: "aborted", taskName, message: `${taskName} was cancelled: async runner destroyed` });
|
|
14
|
+
return {
|
|
15
|
+
get stats() { return { active: queue.pending, queued: queue.size }; },
|
|
16
|
+
async run(taskName, fn, env) {
|
|
17
|
+
const labels = env?.labels ?? {};
|
|
18
|
+
const logger = env?.logger ?? options.logger;
|
|
19
|
+
const sink = env?.onEscalate ?? options.onEscalate;
|
|
20
|
+
const reportRejection = async (error) => {
|
|
21
|
+
// Rejecting before the run reaches startLog is still a terminal outcome; without this
|
|
22
|
+
// the structured stream shows nothing at all for it.
|
|
23
|
+
finishLog(logger, "async", taskName, labels, performance.now(), error);
|
|
24
|
+
await recordResult({ counter, taskName, backend: "async", labels, error, sink, logger });
|
|
25
|
+
throw error;
|
|
26
|
+
};
|
|
27
|
+
if (destroyed)
|
|
28
|
+
return reportRejection(destroyedError(taskName));
|
|
29
|
+
if (env?.signal?.aborted)
|
|
30
|
+
return reportRejection(abortedError(taskName, env.signal.reason));
|
|
31
|
+
// Eagerly validated so an invalid deadline rejects with an OffloadError rather than
|
|
32
|
+
// escaping the queue callback as a raw RangeError (#1809 P0-1, same class).
|
|
33
|
+
try {
|
|
34
|
+
validateTimeout(env?.timeoutMs ?? options.defaultTimeoutMs);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return reportRejection(normalizeError(taskName, error, "rejected"));
|
|
38
|
+
}
|
|
39
|
+
let publish;
|
|
40
|
+
let published = false;
|
|
41
|
+
const outcome = new Promise((resolve) => {
|
|
42
|
+
publish = (value) => { if (!published) {
|
|
43
|
+
published = true;
|
|
44
|
+
resolve(value);
|
|
45
|
+
} };
|
|
46
|
+
});
|
|
47
|
+
let started = false;
|
|
48
|
+
let cancelled = false;
|
|
49
|
+
const entry = {
|
|
50
|
+
taskName,
|
|
51
|
+
teardown: new AbortController(),
|
|
52
|
+
settled: outcome.then(() => undefined),
|
|
53
|
+
cancelIfQueued: (error) => {
|
|
54
|
+
if (started || cancelled)
|
|
55
|
+
return;
|
|
56
|
+
cancelled = true;
|
|
57
|
+
// The single funnel for both abort-while-queued and destroy()-cancels-queued, so
|
|
58
|
+
// the one line each terminal path owes the structured stream is emitted here. The
|
|
59
|
+
// callback bails on `cancelled` without logging, which keeps it to exactly one.
|
|
60
|
+
finishLog(logger, "async", taskName, labels, performance.now(), error);
|
|
61
|
+
publish({ ok: false, error });
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
live.add(entry);
|
|
65
|
+
// p-queue's own `signal` option used to provide this, but it also released the slot
|
|
66
|
+
// while the work kept running (#1809 P1-7). Cancelling here keeps abort prompt for
|
|
67
|
+
// queued work without lying about occupancy.
|
|
68
|
+
const signal = env?.signal;
|
|
69
|
+
const onQueuedAbort = signal ? () => entry.cancelIfQueued(abortedError(taskName, signal.reason)) : undefined;
|
|
70
|
+
if (signal && onQueuedAbort)
|
|
71
|
+
signal.addEventListener("abort", onQueuedAbort, { once: true });
|
|
72
|
+
const task = queue.add(async () => {
|
|
73
|
+
if (signal && onQueuedAbort)
|
|
74
|
+
signal.removeEventListener("abort", onQueuedAbort);
|
|
75
|
+
let work;
|
|
76
|
+
let context;
|
|
77
|
+
let onAbort;
|
|
78
|
+
try {
|
|
79
|
+
if (cancelled)
|
|
80
|
+
return;
|
|
81
|
+
if (destroyed || entry.teardown.signal.aborted) {
|
|
82
|
+
const error = destroyedError(taskName);
|
|
83
|
+
finishLog(logger, "async", taskName, labels, performance.now(), error);
|
|
84
|
+
publish({ ok: false, error });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
started = true;
|
|
88
|
+
// Match WorkerPool semantics: a task's deadline starts when it receives a slot,
|
|
89
|
+
// not while it is waiting behind unrelated bounded work.
|
|
90
|
+
context = beginRun(taskName, env, options, entry.teardown.signal);
|
|
91
|
+
const run = context;
|
|
92
|
+
const startedAt = startLog(logger, "async", taskName, labels);
|
|
93
|
+
try {
|
|
94
|
+
if (run.signal.aborted)
|
|
95
|
+
throw run.abortError();
|
|
96
|
+
const abort = new Promise((_, reject) => {
|
|
97
|
+
onAbort = () => reject(run.abortError());
|
|
98
|
+
run.signal.addEventListener("abort", onAbort, { once: true });
|
|
99
|
+
});
|
|
100
|
+
work = Promise.resolve().then(() => fn(run.signal));
|
|
101
|
+
const value = await Promise.race([work, abort]);
|
|
102
|
+
finishLog(logger, "async", taskName, labels, startedAt);
|
|
103
|
+
await recordResult({ counter, taskName, backend: "async", labels, sink, logger });
|
|
104
|
+
publish({ ok: true, value });
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const normalized = run.signal.aborted ? run.abortError() : normalizeError(taskName, error);
|
|
108
|
+
finishLog(logger, "async", taskName, labels, startedAt, normalized);
|
|
109
|
+
await recordResult({ counter, taskName, backend: "async", labels, error: normalized, sink, logger });
|
|
110
|
+
publish({ ok: false, error: normalized });
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
// The composite signal stays reachable from the caller's long-lived signal, so an
|
|
114
|
+
// un-removed listener retains this closure for the process lifetime (#1809 P0-4).
|
|
115
|
+
if (onAbort)
|
|
116
|
+
run.signal.removeEventListener("abort", onAbort);
|
|
117
|
+
run.cleanup();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
const normalized = normalizeError(taskName, error, "rejected");
|
|
122
|
+
finishLog(logger, "async", taskName, labels, performance.now(), normalized);
|
|
123
|
+
await recordResult({ counter, taskName, backend: "async", labels, error: normalized, sink, logger });
|
|
124
|
+
publish({ ok: false, error: normalized });
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
context?.cleanup();
|
|
128
|
+
// The caller may already have been settled by the abort race; the slot stays held
|
|
129
|
+
// until the work itself finishes, otherwise the cap admits a replacement while the
|
|
130
|
+
// aborted task is still running and `stats` under-reports it (#1809 P1-7).
|
|
131
|
+
if (work)
|
|
132
|
+
await work.catch(() => undefined);
|
|
133
|
+
live.delete(entry);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// The callback settles callers through `publish` and never throws, but a stray
|
|
137
|
+
// rejection here would surface as an unhandled rejection rather than a failed run.
|
|
138
|
+
void task.catch(() => undefined);
|
|
139
|
+
const result = await outcome;
|
|
140
|
+
if (!result.ok)
|
|
141
|
+
throw result.error;
|
|
142
|
+
return result.value;
|
|
143
|
+
},
|
|
144
|
+
drain: () => queue.onIdle(),
|
|
145
|
+
async destroy() {
|
|
146
|
+
destroyed = true;
|
|
147
|
+
const outstanding = [...live];
|
|
148
|
+
for (const entry of outstanding) {
|
|
149
|
+
entry.cancelIfQueued(destroyedError(entry.taskName));
|
|
150
|
+
entry.teardown.abort(new DOMException("Async runner destroyed", "AbortError"));
|
|
151
|
+
}
|
|
152
|
+
// Waits on caller settlement rather than `queue.onIdle()`: work that ignores its signal
|
|
153
|
+
// must not be able to make teardown hang. Its slot is still held until it finishes.
|
|
154
|
+
await Promise.all(outstanding.map((entry) => entry.settled));
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=async-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-runner.js","sourceRoot":"","sources":["../src/async-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OffloadError, type OffloadDefaults, type OffloadEnvelope, type OffloadLogger, type EscalationEvent, type EscalationSink, type StrikeCounter } from "./types.js";
|
|
2
|
+
export interface RunContext {
|
|
3
|
+
signal: AbortSignal;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
cleanup(): void;
|
|
6
|
+
abortError(): OffloadError;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* `teardown` is the backend's own cancellation channel (runner `destroy()`); it is composed
|
|
10
|
+
* with the caller's signal and the deadline so every backend has exactly one signal to observe.
|
|
11
|
+
*/
|
|
12
|
+
export declare function beginRun(taskName: string, env: OffloadEnvelope | undefined, defaults: OffloadDefaults, teardown?: AbortSignal, timeoutMsOverride?: number | null): RunContext;
|
|
13
|
+
export declare function startLog(logger: OffloadLogger | undefined, backend: EscalationEvent["backend"], taskName: string, labels: Record<string, string>): number;
|
|
14
|
+
export declare function finishLog(logger: OffloadLogger | undefined, backend: EscalationEvent["backend"], taskName: string, labels: Record<string, string>, startedAt: number, error?: OffloadError): void;
|
|
15
|
+
export declare function recordResult(options: {
|
|
16
|
+
counter: StrikeCounter;
|
|
17
|
+
taskName: string;
|
|
18
|
+
backend: EscalationEvent["backend"];
|
|
19
|
+
labels: Record<string, string>;
|
|
20
|
+
error?: OffloadError;
|
|
21
|
+
sink?: EscalationSink;
|
|
22
|
+
logger?: OffloadLogger;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
export declare function errorForExit(taskName: string, code?: string): OffloadError;
|
|
25
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAwB,MAAM,YAAY,CAAC;AAE/L,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,IAAI,IAAI,CAAC;IAChB,UAAU,IAAI,YAAY,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,GAAG,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,UAAU,CA8B7K;AAKD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,EAAE,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAIzJ;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,EAAE,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAIjM;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhB;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,CAE1E"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { OffloadError, log, validateTimeout } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* `teardown` is the backend's own cancellation channel (runner `destroy()`); it is composed
|
|
4
|
+
* with the caller's signal and the deadline so every backend has exactly one signal to observe.
|
|
5
|
+
*/
|
|
6
|
+
export function beginRun(taskName, env, defaults, teardown, timeoutMsOverride) {
|
|
7
|
+
// `timeoutMsOverride` lets the WorkerPool pass an ALREADY-RESOLVED timeout: a number (e.g. the
|
|
8
|
+
// remaining budget of an absolute deadline on a retry-safe re-dispatch, #1822 MED-2) or `null`
|
|
9
|
+
// meaning "no deadline, definitively." Either way it is authoritative and already validated, so
|
|
10
|
+
// the envelope AND the mutable pool default are NOT re-read — that is what keeps a post-submission
|
|
11
|
+
// mutation of either from adding or erasing this job's deadline (#1822 MED). `null` is used for
|
|
12
|
+
// "no deadline" rather than `undefined` on purpose: `undefined` is indistinguishable from "no
|
|
13
|
+
// argument passed", which is how the async/subprocess runners ask to resolve from env/default.
|
|
14
|
+
const timeoutMs = timeoutMsOverride === undefined
|
|
15
|
+
? validateTimeout(env?.timeoutMs ?? defaults.defaultTimeoutMs)
|
|
16
|
+
: (timeoutMsOverride ?? undefined);
|
|
17
|
+
const timeout = new AbortController();
|
|
18
|
+
const sources = [env?.signal, teardown].filter((source) => source !== undefined);
|
|
19
|
+
const signal = sources.length === 0 ? timeout.signal : AbortSignal.any([...sources, timeout.signal]);
|
|
20
|
+
let timedOut = false;
|
|
21
|
+
const timer = timeoutMs === undefined ? undefined : setTimeout(() => {
|
|
22
|
+
timedOut = true;
|
|
23
|
+
timeout.abort(new DOMException(`Timed out after ${timeoutMs}ms`, "TimeoutError"));
|
|
24
|
+
}, timeoutMs);
|
|
25
|
+
return {
|
|
26
|
+
signal,
|
|
27
|
+
timeoutMs,
|
|
28
|
+
cleanup: () => { if (timer)
|
|
29
|
+
clearTimeout(timer); },
|
|
30
|
+
abortError: () => new OffloadError({
|
|
31
|
+
kind: timedOut ? "timeout" : "aborted",
|
|
32
|
+
taskName,
|
|
33
|
+
message: timedOut ? `${taskName} timed out after ${timeoutMs}ms` : `${taskName} was aborted`,
|
|
34
|
+
cause: signal.reason,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Labels are spread FIRST throughout: adopters pass generic context bags, and a label that
|
|
39
|
+
// happens to be named `taskName` or `backend` must not silently replace the envelope's own
|
|
40
|
+
// fields — that would destroy exactly the uniformity this logging contract exists to provide.
|
|
41
|
+
export function startLog(logger, backend, taskName, labels) {
|
|
42
|
+
const startedAt = performance.now();
|
|
43
|
+
log(logger, "debug", "offload started", { ...labels, backend, taskName });
|
|
44
|
+
return startedAt;
|
|
45
|
+
}
|
|
46
|
+
export function finishLog(logger, backend, taskName, labels, startedAt, error) {
|
|
47
|
+
const fields = { ...labels, backend, taskName, tookMs: Math.round(performance.now() - startedAt) };
|
|
48
|
+
if (error)
|
|
49
|
+
log(logger, "warn", "offload failed", { ...fields, kind: error.kind, code: error.code, error: error.message });
|
|
50
|
+
else
|
|
51
|
+
log(logger, "debug", "offload completed", fields);
|
|
52
|
+
}
|
|
53
|
+
export async function recordResult(options) {
|
|
54
|
+
if (!options.error) {
|
|
55
|
+
options.counter.succeed(options.taskName);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Caller-directed cancellation is neither a backend failure nor a timeout. It must not
|
|
59
|
+
// page a host merely because a legitimate shutdown cancelled several queued operations.
|
|
60
|
+
if (options.error.kind === "aborted")
|
|
61
|
+
return;
|
|
62
|
+
const strikes = options.counter.fail(options.taskName);
|
|
63
|
+
if (strikes < options.counter.threshold)
|
|
64
|
+
return;
|
|
65
|
+
// Like the maintenance breaker, escalation is edge-triggered: one notification at
|
|
66
|
+
// the threshold, then a latch until a successful run re-arms the task.
|
|
67
|
+
options.counter.trip(options.taskName);
|
|
68
|
+
if (!options.sink)
|
|
69
|
+
return;
|
|
70
|
+
try {
|
|
71
|
+
await options.sink({ taskName: options.taskName, backend: options.backend, error: options.error, strikes, labels: options.labels });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
log(options.logger, "error", "offload escalation sink failed", { taskName: options.taskName, backend: options.backend, error: String(error) });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export function errorForExit(taskName, code) {
|
|
78
|
+
return new OffloadError({ kind: "worker-exit", taskName, code, message: `${taskName} worker exited${code ? ` (${code})` : ""}` });
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { OffloadWorkerTask } from "../worker.js";
|
|
2
|
+
declare const task: OffloadWorkerTask<{
|
|
3
|
+
kind: string;
|
|
4
|
+
ms?: number;
|
|
5
|
+
value?: unknown;
|
|
6
|
+
file?: string;
|
|
7
|
+
code?: number;
|
|
8
|
+
}, unknown>;
|
|
9
|
+
export default task;
|
|
10
|
+
//# sourceMappingURL=worker-task.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-task.d.ts","sourceRoot":"","sources":["../../src/fixtures/worker-task.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAMtD,QAAA,MAAM,IAAI,EAAE,iBAAiB,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,CAuClH,CAAC;eAEa,IAAI"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { appendFileSync, readFileSync } from "node:fs";
|
|
2
|
+
// `file`-based counters let a test count executions/deaths deterministically across worker
|
|
3
|
+
// threads (a shared in-process counter is not visible from a worker), so the #1822 F2/F3 tests
|
|
4
|
+
// can PROVE the mechanism (exactly-once, bounded retries, real deaths) instead of hoping on
|
|
5
|
+
// timing. Each execution/death appends one byte; the test reads the file size.
|
|
6
|
+
const task = async (input) => {
|
|
7
|
+
if (input.kind === "burn") {
|
|
8
|
+
const until = Date.now() + (input.ms ?? 0);
|
|
9
|
+
while (Date.now() < until) { /* intentionally synchronous: timeout must terminate this worker */ }
|
|
10
|
+
return input.value ?? "burned";
|
|
11
|
+
}
|
|
12
|
+
if (input.kind === "delay") {
|
|
13
|
+
await Bun.sleep(input.ms ?? 0);
|
|
14
|
+
return input.value;
|
|
15
|
+
}
|
|
16
|
+
if (input.kind === "throw")
|
|
17
|
+
throw Object.assign(new RangeError("bad worker input"), { code: "BAD_INPUT" });
|
|
18
|
+
if (input.kind === "exit")
|
|
19
|
+
process.exit(1);
|
|
20
|
+
// Returns a value now, then exits AFTER the slot has gone idle: simulates an OOM/SIGKILL
|
|
21
|
+
// that lands on a worker between jobs, which is the #1822 item 2 scenario. Records the death
|
|
22
|
+
// to `file` (if given) so a test can assert the deaths actually happened.
|
|
23
|
+
if (input.kind === "exit-when-idle") {
|
|
24
|
+
setTimeout(() => { if (input.file)
|
|
25
|
+
appendFileSync(input.file, "d"); process.exit(0); }, input.ms ?? 20);
|
|
26
|
+
return input.value ?? "ok";
|
|
27
|
+
}
|
|
28
|
+
// Optionally runs for `ms` (so a deadline can bite mid-execution), records one execution to
|
|
29
|
+
// `file`, then exits the worker every time. Exits with `code` (default 1 = abnormal); pass
|
|
30
|
+
// code 0 to model a CLEAN worker exit while a job is in flight. Used to prove at-most-once
|
|
31
|
+
// (default → one record) vs bounded retry (retrySafe → MAX+1 records), the absolute deadline,
|
|
32
|
+
// and exit-manner attribution (abnormal charges, clean does not — #1822 MED-4).
|
|
33
|
+
if (input.kind === "crash-record") {
|
|
34
|
+
if (input.ms)
|
|
35
|
+
await Bun.sleep(input.ms);
|
|
36
|
+
if (input.file)
|
|
37
|
+
appendFileSync(input.file, "x");
|
|
38
|
+
process.exit(input.code ?? 1);
|
|
39
|
+
}
|
|
40
|
+
// Records one execution, then crashes ONLY on the first execution (file was empty) and succeeds
|
|
41
|
+
// on any retry — proves a retry-safe re-dispatch recovers and is bounded (file has 2 records).
|
|
42
|
+
if (input.kind === "flaky-record") {
|
|
43
|
+
let prior = 0;
|
|
44
|
+
try {
|
|
45
|
+
prior = input.file ? readFileSync(input.file, "utf8").length : 0;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
prior = 0;
|
|
49
|
+
}
|
|
50
|
+
if (input.file)
|
|
51
|
+
appendFileSync(input.file, "x");
|
|
52
|
+
if (prior === 0)
|
|
53
|
+
process.exit(1);
|
|
54
|
+
return input.value ?? "recovered";
|
|
55
|
+
}
|
|
56
|
+
return input.value;
|
|
57
|
+
};
|
|
58
|
+
export default task;
|
|
59
|
+
//# sourceMappingURL=worker-task.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-task.js","sourceRoot":"","sources":["../../src/fixtures/worker-task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvD,2FAA2F;AAC3F,+FAA+F;AAC/F,4FAA4F;AAC5F,+EAA+E;AAC/E,MAAM,IAAI,GAA6G,KAAK,EAAE,KAAK,EAAE,EAAE;IACrI,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,mEAAmE,CAAC,CAAC;QAClG,OAAO,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC;IACjC,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,MAAM,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;QAAE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,kBAAkB,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAC3G,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3C,yFAAyF;IACzF,6FAA6F;IAC7F,0EAA0E;IAC1E,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACpC,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,IAAI;YAAE,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACxG,OAAO,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC;IAC7B,CAAC;IACD,4FAA4F;IAC5F,2FAA2F;IAC3F,2FAA2F;IAC3F,8FAA8F;IAC9F,gFAAgF;IAChF,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,EAAE;YAAE,MAAM,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,IAAI;YAAE,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,gGAAgG;IAChG,+FAA+F;IAC/F,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAClC,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,CAAC;YAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,KAAK,GAAG,CAAC,CAAC;QAAC,CAAC;QAC9F,IAAI,KAAK,CAAC,IAAI;YAAE,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAChD,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC,KAAK,IAAI,WAAW,CAAC;IACpC,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC;AACrB,CAAC,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createAsyncRunner, type AsyncRunner, type AsyncRunnerOptions } from "./async-runner.js";
|
|
2
|
+
export { createSubprocessRunner, type SubprocessResult, type SubprocessRunner, type SubprocessRunnerOptions, type SubprocessSpec } from "./subprocess-runner.js";
|
|
3
|
+
export { createWorkerPool, type WorkerPool, type WorkerPoolOptions, type WorkerPoolStats } from "./worker-pool.js";
|
|
4
|
+
export { OffloadError, StrikeCounter, type EscalationEvent, type EscalationSink, type OffloadDefaults, type OffloadEnvelope, type OffloadErrorKind, type OffloadLogger } from "./types.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACjK,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createAsyncRunner } from "./async-runner.js";
|
|
2
|
+
export { createSubprocessRunner } from "./subprocess-runner.js";
|
|
3
|
+
export { createWorkerPool } from "./worker-pool.js";
|
|
4
|
+
export { OffloadError, StrikeCounter } from "./types.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA6C,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,sBAAsB,EAAmG,MAAM,wBAAwB,CAAC;AACjK,OAAO,EAAE,gBAAgB,EAAiE,MAAM,kBAAkB,CAAC;AACnH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAoI,MAAM,YAAY,CAAC"}
|