larkin 0.5.4 → 0.5.5

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +7 -1
  3. package/artifacts/release/THIRD_PARTY_NOTICES.txt +1 -100
  4. package/dist/agent/context-prompt.cjs +16 -17
  5. package/dist/agent/context-prompt.mjs +15 -16
  6. package/dist/runtime/pi-state-dir.cjs +105 -0
  7. package/dist/runtime/pi-state-dir.mjs +94 -0
  8. package/dist/runtime/pi-tmux-extension.mjs +137 -0
  9. package/dist/runtime/pi-tmux-injection.mjs +49 -0
  10. package/dist/runtime/pi-tmux.bundle.js +600 -0
  11. package/dist/runtime/pi-tmux.mjs +408 -0
  12. package/dist/runtime/runtime-adapters.mjs +26 -113
  13. package/dist/runtime/runtime-host.cjs +3 -413
  14. package/dist/runtime/runtime-host.mjs +2 -412
  15. package/package.json +2 -8
  16. package/dist/runtime/pi-bash-timeout-extension.mjs +0 -60
  17. package/dist/runtime/pi-bash-timeout-injection.mjs +0 -54
  18. package/dist/runtime/pi-bash-timeout.bundle.js +0 -43
  19. package/dist/runtime/pi-extension-api.mjs +0 -1
  20. package/dist/runtime/pi-subagent-injection.mjs +0 -290
  21. package/dist/runtime/pi-subagent-ledger.cjs +0 -632
  22. package/dist/runtime/pi-subagent-ledger.mjs +0 -594
  23. package/dist/runtime/pi-subagent-record-watchdog-injection.mjs +0 -43
  24. package/dist/runtime/pi-subagent-record-watchdog.bundle.js +0 -183
  25. package/dist/runtime/pi-subagent-record-watchdog.mjs +0 -46
  26. package/dist/runtime/pi-subagents-notification.mjs +0 -175
  27. package/dist/runtime/pi-subagents.bundle.js +0 -9110
  28. package/dist/runtime/pi-supervised-command-extension.mjs +0 -72
  29. package/dist/runtime/pi-supervised-command.bundle.js +0 -4735
  30. package/dist/runtime/pi-supervised-command.mjs +0 -420
  31. package/patches/@tintinweb%2Fpi-subagents@0.14.3.patch +0 -945
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.5
4
+
5
+ Pi uses a small Larkin-owned tmux Bash extension when tmux is available on macOS/Linux. Commands preserve their working directory, including non-Git paths with spaces; a foreground wait can return while the same command continues. The returned task ID supports inspection and cancellation, and background completion resumes the Agent. Native Windows and environments without tmux retain Pi's native Bash.
6
+
7
+ Removed the injected subagent plugin, its ledger/watchdog and supervised-command infrastructure, the 60-second Bash hard-kill rule, and mandatory delegation guidance. Pi-owned turns now occupy Runtime busy state without a second host prompt. Historical subagent files are left untouched. Background commands survive Pi shutdown in tmux; notification recovery across Pi restarts is not provided. No third-party tmux plugin is installed or redistributed.
8
+
3
9
  ## 0.5.4
4
10
 
5
11
  Inbox Audit is now optional and off by default. The Dashboard and CLI support global and per-Agent switches and inspection gaps. Saving a gap updates scheduling without replacing Runtime sessions; editing a gap alone does not enable auditing. Only originally wake-eligible human group/topic work is audited.
package/README.md CHANGED
@@ -91,7 +91,13 @@ Feishu (https://open.feishu.cn) and Lark (https://open.larksuite.com) are differ
91
91
 
92
92
  During setup, choose one of the three externally installed runtimes: Pi (`pi`), Codex (`codex`), or Claude Code (`claude`). Larkin does not ship a runtime and does not store provider credentials. Install the runtime yourself and complete its own login (`pi` login flow, `codex login`, or `claude login`) before setup. Interactive setup lists each runtime as installed or not installed and refuses a missing binary; non-interactive setup requires `--runtime` and exits non-zero with the same missing-install message.
93
93
 
94
- `larkin setup --model <id>` optionally stores a catalog model for that runtime. After setup, use `larkin model` and `larkin runtime` to inspect or switch. For Pi, Larkin talks to your installed `pi --mode rpc`, verifies the RPC handshake and compaction capability contract, and injects its supported extensions through `pi -e` (background subagents and the 60-second foreground bash timeout guard).
94
+ `larkin setup --model <id>` optionally stores a catalog model for that runtime. After setup, use `larkin model` and `larkin runtime` to inspect or switch. For Pi, Larkin talks to your installed `pi --mode rpc` and verifies the RPC handshake and compaction capability contract.
95
+
96
+ ### Optional Pi tmux-backed bash
97
+
98
+ On macOS and Linux with `tmux` 3.2 or newer installed, Larkin provides a small tmux-backed Bash tool. Commands run in their requested directory, including non-Git directories and paths with spaces. By default the tool waits up to 30 seconds, then returns a `taskId` while the same command continues; `background: true` returns immediately. The `tmux` tool lists, inspects and stops tasks owned by that Pi instance. Completed background commands notify the Agent. There is no Larkin-imposed 60-second kill or ten-minute command limit.
99
+
100
+ Without a supported tmux version, or on native Windows, Pi keeps native bash. After Pi shuts down, background commands remain in tmux, but automatic completion notifications are not restored; use `tmux list-sessions` to find the session containing the task ID and attach manually. Larkin does not require a third-party tmux plugin.
95
101
 
96
102
  ### Optional Inbox Audit
97
103
 
@@ -4,7 +4,7 @@ Generated at release time from Larkin's installed runtime dependency closure.
4
4
  Development-only dependencies are intentionally excluded. Packages without a bundled license file
5
5
  use an explicitly audited, version- and hash-pinned license source and remain identified by locked package name and version.
6
6
 
7
- Runtime package versions: 101
7
+ Runtime package versions: 97
8
8
 
9
9
  | Package | Version | Relationship | Declared license | License texts (SHA-256) | Lock integrity |
10
10
  | --- | ---: | --- | --- | --- | --- |
@@ -43,8 +43,6 @@ Runtime package versions: 101
43
43
  | @radix-ui/react-use-controllable-state | 1.2.6 | runtime transitive | MIT | LICENSE (0e80a2d229d2fd4fc7e8636142ec5d0ff0bc031f14c15b682e2ac01dfd5b5138) | sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ== |
44
44
  | @radix-ui/react-use-effect-event | 0.0.5 | runtime transitive | MIT | LICENSE (0e80a2d229d2fd4fc7e8636142ec5d0ff0bc031f14c15b682e2ac01dfd5b5138) | sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg== |
45
45
  | @radix-ui/react-use-layout-effect | 1.1.4 | runtime transitive | MIT | LICENSE (0e80a2d229d2fd4fc7e8636142ec5d0ff0bc031f14c15b682e2ac01dfd5b5138) | sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw== |
46
- | @sinclair/typebox | 0.34.52 | runtime transitive | MIT | license (411f1c560755e27b3dec9622c13b8b7a3ec1c83f4ffb8c56a8847528aa96911a) | sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw== |
47
- | @tintinweb/pi-subagents | 0.14.3 | runtime direct | MIT | LICENSE (e7d9d11c28cce34f09dc7d2b0a3a6609ccba5fbc8e926e6d1bfdf060930e9f05) | sha512-iDqeadh6114AZvw8HYe1PEq8M0MZ9czJKTAIsklCPUbV9vUMS+g/LAV0vW3O9PiBXKNJh8hkrY8L6iIr8XNEqA== |
48
46
  | agent-base | 6.0.2 | runtime transitive | MIT | AUDITED-agent-base@9.0.0-LICENSE (71368fd0f5b4129191e9afcd1e1ef2dc89a9090d3e4d80bbab92dafd032b3bef) | sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== |
49
47
  | agent-base | 9.0.0 | runtime transitive | MIT | LICENSE (71368fd0f5b4129191e9afcd1e1ef2dc89a9090d3e4d80bbab92dafd032b3bef) | sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA== |
50
48
  | aria-hidden | 1.2.6 | runtime transitive | MIT | LICENSE (30f0cfddf483d1128e3610205020f2041a6c5e837aa999e0aa82e5576187d4a9) | sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA== |
@@ -53,7 +51,6 @@ Runtime package versions: 101
53
51
  | call-bind-apply-helpers | 1.0.2 | runtime transitive | MIT | LICENSE (5e325595b4ea8cfec3802f545b1def5d7b73e4a5b8e9ba63e32a320f67732292) | sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== |
54
52
  | call-bound | 1.0.4 | runtime transitive | MIT | LICENSE (5e325595b4ea8cfec3802f545b1def5d7b73e4a5b8e9ba63e32a320f67732292) | sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== |
55
53
  | combined-stream | 1.0.8 | runtime transitive | MIT | License (47eb8ca82c798246774946d1be0f9aa08f025fa8325ced0947aeeb4c05fe5547) | sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== |
56
- | croner | 10.0.1 | runtime transitive | MIT | LICENSE (ad9aa40aaab4bee08121a0180096cc6a72abf29e19e606328b66c0178f41d267) | sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g== |
57
54
  | debug | 4.4.3 | runtime transitive | MIT | LICENSE (23eb8602cd5902bcfca56b23aa08e8672a809f5a12eaf8bb56abfa9d44e1a4fd) | sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== |
58
55
  | delayed-stream | 1.0.0 | runtime transitive | MIT | License (47eb8ca82c798246774946d1be0f9aa08f025fa8325ced0947aeeb4c05fe5547) | sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== |
59
56
  | detect-node-es | 1.1.0 | runtime transitive | MIT | LICENSE (54b32293ea560d22cd9b62af9b2cda1a030d910a48c6601d227c5ff2338d95f8) | sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== |
@@ -84,7 +81,6 @@ Runtime package versions: 101
84
81
  | mime-db | 1.52.0 | runtime transitive | MIT | LICENSE (cc1dfd4dafa27271e8212cd3b274eeb3f262e40a6fdab36ddc3f9696f706f58b) | sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== |
85
82
  | mime-types | 2.1.35 | runtime transitive | MIT | LICENSE (71f83c4c0621102a56d9853812777b85751bce7e9726f686f5b056c1f8a4b0e6) | sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== |
86
83
  | ms | 2.1.3 | runtime transitive | MIT | license.md (1662fae9b5314d11cf51284e2dcd1f006a354f7343f08712a730fcff9a359801) | sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== |
87
- | nanoid | 5.1.16 | runtime transitive | MIT | LICENSE (4383cb2c3608397ce7a4159502614ed66890f8999c2a9c056dd3b1024d6721f0) | sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ== |
88
84
  | object-inspect | 1.13.4 | runtime transitive | MIT | LICENSE (bd40cc437e28a3ad7bef2ad34e6b72e757b182e67bda1acadbab4ef0476f8232) | sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== |
89
85
  | proper-lockfile | 4.1.2 | runtime direct | MIT | LICENSE (aaa78451b6fecd1b9c4594c796c133c0e90cad100372ff8bc6de615e9ef9adf1) | sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== |
90
86
  | protobufjs | 7.6.5 | runtime transitive | BSD-3-Clause | LICENSE (49d6a1c9a623784c61c6cb70f773f3457faceb1914a13c8560a9823b7631950c) | sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw== |
@@ -414,55 +410,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
414
410
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
415
411
  SOFTWARE.
416
412
 
417
- --- SHA-256 411f1c560755e27b3dec9622c13b8b7a3ec1c83f4ffb8c56a8847528aa96911a ---
418
- TypeBox
419
-
420
- Json Schema Type Builder with Static Type Resolution for TypeScript
421
-
422
- The MIT License (MIT)
423
-
424
- Copyright (c) 2017-2026 Haydn Paterson
425
-
426
- Permission is hereby granted, free of charge, to any person obtaining a copy
427
- of this software and associated documentation files (the "Software"), to deal
428
- in the Software without restriction, including without limitation the rights
429
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
430
- copies of the Software, and to permit persons to whom the Software is
431
- furnished to do so, subject to the following conditions:
432
-
433
- The above copyright notice and this permission notice shall be included in
434
- all copies or substantial portions of the Software.
435
-
436
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
437
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
438
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
439
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
440
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
441
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
442
- THE SOFTWARE.
443
-
444
- --- SHA-256 4383cb2c3608397ce7a4159502614ed66890f8999c2a9c056dd3b1024d6721f0 ---
445
- The MIT License (MIT)
446
-
447
- Copyright 2017 Andrey Sitnik <andrey@sitnik.es>
448
-
449
- Permission is hereby granted, free of charge, to any person obtaining a copy of
450
- this software and associated documentation files (the "Software"), to deal in
451
- the Software without restriction, including without limitation the rights to
452
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
453
- the Software, and to permit persons to whom the Software is furnished to do so,
454
- subject to the following conditions:
455
-
456
- The above copyright notice and this permission notice shall be included in all
457
- copies or substantial portions of the Software.
458
-
459
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
460
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
461
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
462
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
463
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
464
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
465
-
466
413
  --- SHA-256 47eb8ca82c798246774946d1be0f9aa08f025fa8325ced0947aeeb4c05fe5547 ---
467
414
  Copyright (c) 2011 Debuggable Limited <felix@debuggable.com>
468
415
 
@@ -968,29 +915,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
968
915
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
969
916
  SOFTWARE.
970
917
 
971
- --- SHA-256 ad9aa40aaab4bee08121a0180096cc6a72abf29e19e606328b66c0178f41d267 ---
972
- The MIT License (MIT)
973
-
974
- Copyright (c) 2015-2021 Hexagon <github.com/Hexagon>
975
-
976
- Permission is hereby granted, free of charge, to any person obtaining a copy
977
- of this software and associated documentation files (the "Software"), to deal
978
- in the Software without restriction, including without limitation the rights
979
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
980
- copies of the Software, and to permit persons to whom the Software is
981
- furnished to do so, subject to the following conditions:
982
-
983
- The above copyright notice and this permission notice shall be included in all
984
- copies or substantial portions of the Software.
985
-
986
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
987
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
988
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
989
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
990
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
991
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
992
- SOFTWARE.
993
-
994
918
  --- SHA-256 b495047bd93a9b06913511076f504daba17d5bbeb3e0650f3bb53a4220329c57 ---
995
919
  ISC License
996
920
 
@@ -1711,29 +1635,6 @@ Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors
1711
1635
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1712
1636
  THE SOFTWARE.
1713
1637
 
1714
- --- SHA-256 e7d9d11c28cce34f09dc7d2b0a3a6609ccba5fbc8e926e6d1bfdf060930e9f05 ---
1715
- MIT License
1716
-
1717
- Copyright (c) 2026 tintinweb
1718
-
1719
- Permission is hereby granted, free of charge, to any person obtaining a copy
1720
- of this software and associated documentation files (the "Software"), to deal
1721
- in the Software without restriction, including without limitation the rights
1722
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1723
- copies of the Software, and to permit persons to whom the Software is
1724
- furnished to do so, subject to the following conditions:
1725
-
1726
- The above copyright notice and this permission notice shall be included in all
1727
- copies or substantial portions of the Software.
1728
-
1729
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1730
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1731
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1732
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1733
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1734
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1735
- SOFTWARE.
1736
-
1737
1638
  --- SHA-256 e7dc37bf662d7f786efcb46c545615e70c1daf458a38385521c63cf6607cdfe1 ---
1738
1639
  BSD 3-Clause License
1739
1640
 
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ContextPromptBuilder = exports.AGENT_MENTION_GUIDANCE = exports.LARKIN_STANDING_PROMPT_VERSION = void 0;
3
+ exports.ContextPromptBuilder = exports.AGENT_MENTION_GUIDANCE = exports.PI_TMUX_BASH_GUIDANCE = exports.LARKIN_STANDING_PROMPT_VERSION = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const agent_cli_capabilities_js_1 = require("./agent-cli-capabilities.cjs");
6
6
  /**
@@ -16,7 +16,18 @@ const agent_cli_capabilities_js_1 = require("./agent-cli-capabilities.cjs");
16
16
  * 5. 版本化:每次实质改动 bump LARKIN_STANDING_PROMPT_VERSION,并同步 eval 数据集/graders/断言。
17
17
  * 6. 用 eval 验证:行为变化必须配套固定场景 + rubric(evals/*、test/support/*-grader.mjs、live 测试)。
18
18
  */
19
- exports.LARKIN_STANDING_PROMPT_VERSION = "larkin-standing-v28";
19
+ exports.LARKIN_STANDING_PROMPT_VERSION = "larkin-standing-v30";
20
+ /**
21
+ * Pi 长命令引导的唯一来源。仅在当前工具列表出现 tmux-backed bash 时适用;
22
+ * 不声明 tmux 一定可用,也不复述扩展的工具细节。
23
+ * 工具拒绝当前 workspace 时只要求如实报告,不因此禁止其它已授权工具。
24
+ */
25
+ exports.PI_TMUX_BASH_GUIDANCE = [
26
+ "If the current tools include a tmux-backed bash, long-running work may continue after a wait timeout. That timeout is not process failure.",
27
+ "Use the identifiers those tools return to inspect or stop the same process. When a command finishes, handle the completion notification in this originating conversation.",
28
+ "The user-installed extension owns tool names, arguments, and other details. Do not assume tmux or extra inspect/stop tools exist unless they appear in the current tool list.",
29
+ "If an available tool refuses the current workspace, report that limitation. Other authorized tools in the current list remain available. Do not assume the refusing tool works in every workspace.",
30
+ ];
20
31
  /**
21
32
  * Agent 间协作唤醒引导(issue #75):纯文本 @ 不会产生飞书 mention 事件,
22
33
  * 必须由模型直接构造真实的 mention 元素;`--mention` 扩展参数已移除。
@@ -123,21 +134,9 @@ class ContextPromptBuilder {
123
134
  "Larkin Runtime Host is the only production runtime path. Do not start a second runtime or a legacy daemon.",
124
135
  ...(input.runtime === "pi" ? [
125
136
  "",
126
- "## Background subagents (pi)",
127
- "Long-running, independent work MUST use the Agent tool with run_in_background: true. It is the ONLY supported background mechanism. nohup, `&`, disown, and shell background jobs are forbidden for delegated work.",
128
- "Foreground bash is hard-capped at 60 seconds. Never pass a bash timeout above 60. Never use nested bash to outlive 60s. If work must outlive 60s, Agent({ prompt, description, run_in_background: true }) and inside that background agent use supervised_start (executable+args, shell:false) once, then loop supervised_wait (timeout <= 60). Wait timeout returns still running and does NOT kill the process; Steer can arrive after each wait. Total lifetime is 600s. Abort/cancel/shutdown reaps the process tree. Do not use nohup / '&' / disown.",
129
- "Correct pattern:",
130
- "1. Call Agent with arguments like {\"prompt\": \"<task>\", \"description\": \"<short label>\", \"run_in_background\": true}.",
131
- "2. The tool returns an agent id immediately. Report it to the user and end the turn.",
132
- "3. Do NOT poll or sleep; a completion notification arrives automatically.",
133
- "4. On the notification, check the Inbox, then publish exactly one final summary.",
134
- "If you explicitly use get_subagent_result with wait: true, make at most one bounded wait call per turn. If it returns timedOut: true, do not loop or call wait again in the same turn; yield and let the completion notification wake you.",
135
- "Forbidden pattern (never acceptable): `nohup sh -c '...' > /tmp/x.out 2>&1 &`, `sleep N; cat ...`, disown, or any shell background substitute. These bypass subagent isolation.",
136
- "Keep corrections, approvals, short commands, and Feishu writes in the foreground; do not delegate them.",
137
- "Parallel independent tasks: when the user clearly asks for two or more independent tasks with no dependencies between them, delegate EACH task to its own background subagent in a single message with multiple Agent tool calls (one per task, all with run_in_background: true), report every job id, and end the turn. Do not run them one-by-one in the foreground and do not merge them into one subagent.",
138
- "Sequential dependent tasks: when tasks depend on each other, sending the user an order message is a REQUIRED first step: before executing any follow-up work, send a message stating the execution order (for example: first I will do A, then B; I will report back after each step). Then execute in order and report as promised; never stay silent while chaining long work.",
139
- "Reporting location: always report job ids and final summaries in the same conversation and thread where the user's message arrived (reply-in-thread when the request arrived in a thread). Never start a new conversation or DM for subagent status reports.",
140
- ""
137
+ "## Long-running commands (pi)",
138
+ ...exports.PI_TMUX_BASH_GUIDANCE,
139
+ "",
141
140
  ] : []),
142
141
  "",
143
142
  "## Available Larkin agent commands",
@@ -13,7 +13,18 @@ import { agentCliPromptCapabilities } from "./agent-cli-capabilities.mjs";
13
13
  * 5. 版本化:每次实质改动 bump LARKIN_STANDING_PROMPT_VERSION,并同步 eval 数据集/graders/断言。
14
14
  * 6. 用 eval 验证:行为变化必须配套固定场景 + rubric(evals/*、test/support/*-grader.mjs、live 测试)。
15
15
  */
16
- export const LARKIN_STANDING_PROMPT_VERSION = "larkin-standing-v28";
16
+ export const LARKIN_STANDING_PROMPT_VERSION = "larkin-standing-v30";
17
+ /**
18
+ * Pi 长命令引导的唯一来源。仅在当前工具列表出现 tmux-backed bash 时适用;
19
+ * 不声明 tmux 一定可用,也不复述扩展的工具细节。
20
+ * 工具拒绝当前 workspace 时只要求如实报告,不因此禁止其它已授权工具。
21
+ */
22
+ export const PI_TMUX_BASH_GUIDANCE = [
23
+ "If the current tools include a tmux-backed bash, long-running work may continue after a wait timeout. That timeout is not process failure.",
24
+ "Use the identifiers those tools return to inspect or stop the same process. When a command finishes, handle the completion notification in this originating conversation.",
25
+ "The user-installed extension owns tool names, arguments, and other details. Do not assume tmux or extra inspect/stop tools exist unless they appear in the current tool list.",
26
+ "If an available tool refuses the current workspace, report that limitation. Other authorized tools in the current list remain available. Do not assume the refusing tool works in every workspace.",
27
+ ];
17
28
  /**
18
29
  * Agent 间协作唤醒引导(issue #75):纯文本 @ 不会产生飞书 mention 事件,
19
30
  * 必须由模型直接构造真实的 mention 元素;`--mention` 扩展参数已移除。
@@ -120,21 +131,9 @@ export class ContextPromptBuilder {
120
131
  "Larkin Runtime Host is the only production runtime path. Do not start a second runtime or a legacy daemon.",
121
132
  ...(input.runtime === "pi" ? [
122
133
  "",
123
- "## Background subagents (pi)",
124
- "Long-running, independent work MUST use the Agent tool with run_in_background: true. It is the ONLY supported background mechanism. nohup, `&`, disown, and shell background jobs are forbidden for delegated work.",
125
- "Foreground bash is hard-capped at 60 seconds. Never pass a bash timeout above 60. Never use nested bash to outlive 60s. If work must outlive 60s, Agent({ prompt, description, run_in_background: true }) and inside that background agent use supervised_start (executable+args, shell:false) once, then loop supervised_wait (timeout <= 60). Wait timeout returns still running and does NOT kill the process; Steer can arrive after each wait. Total lifetime is 600s. Abort/cancel/shutdown reaps the process tree. Do not use nohup / '&' / disown.",
126
- "Correct pattern:",
127
- "1. Call Agent with arguments like {\"prompt\": \"<task>\", \"description\": \"<short label>\", \"run_in_background\": true}.",
128
- "2. The tool returns an agent id immediately. Report it to the user and end the turn.",
129
- "3. Do NOT poll or sleep; a completion notification arrives automatically.",
130
- "4. On the notification, check the Inbox, then publish exactly one final summary.",
131
- "If you explicitly use get_subagent_result with wait: true, make at most one bounded wait call per turn. If it returns timedOut: true, do not loop or call wait again in the same turn; yield and let the completion notification wake you.",
132
- "Forbidden pattern (never acceptable): `nohup sh -c '...' > /tmp/x.out 2>&1 &`, `sleep N; cat ...`, disown, or any shell background substitute. These bypass subagent isolation.",
133
- "Keep corrections, approvals, short commands, and Feishu writes in the foreground; do not delegate them.",
134
- "Parallel independent tasks: when the user clearly asks for two or more independent tasks with no dependencies between them, delegate EACH task to its own background subagent in a single message with multiple Agent tool calls (one per task, all with run_in_background: true), report every job id, and end the turn. Do not run them one-by-one in the foreground and do not merge them into one subagent.",
135
- "Sequential dependent tasks: when tasks depend on each other, sending the user an order message is a REQUIRED first step: before executing any follow-up work, send a message stating the execution order (for example: first I will do A, then B; I will report back after each step). Then execute in order and report as promised; never stay silent while chaining long work.",
136
- "Reporting location: always report job ids and final summaries in the same conversation and thread where the user's message arrived (reply-in-thread when the request arrived in a thread). Never start a new conversation or DM for subagent status reports.",
137
- ""
134
+ "## Long-running commands (pi)",
135
+ ...PI_TMUX_BASH_GUIDANCE,
136
+ "",
138
137
  ] : []),
139
138
  "",
140
139
  "## Available Larkin agent commands",
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.effectivePiStateDir = effectivePiStateDir;
7
+ exports.ensureOwnedTree = ensureOwnedTree;
8
+ exports.ensurePrivateDirectory = ensurePrivateDirectory;
9
+ exports.mkdirPrivateExclusive = mkdirPrivateExclusive;
10
+ exports.writePrivateExclusive = writePrivateExclusive;
11
+ exports.writePrivateAtomic = writePrivateAtomic;
12
+ const node_crypto_1 = __importDefault(require("node:crypto"));
13
+ const node_fs_1 = __importDefault(require("node:fs"));
14
+ const node_path_1 = __importDefault(require("node:path"));
15
+ /** Same implicit root the Pi adapter uses when `stateDir` is omitted. */
16
+ function effectivePiStateDir(input) {
17
+ return input.stateDir ?? node_path_1.default.join(input.workspaceDir, ".larkin");
18
+ }
19
+ /**
20
+ * 从 root 起逐层建 0700 子树。root 可以位于 /var、/tmp 等合法祖先 link 之后;
21
+ * 任一层 owned 组件自身是 symlink 则拒绝,不顺着 link 往外写。
22
+ */
23
+ function ensureOwnedTree(root, parts = [], create = true) {
24
+ const resolved = node_path_1.default.resolve(root);
25
+ if (node_fs_1.default.existsSync(resolved)) {
26
+ const stat = node_fs_1.default.lstatSync(resolved);
27
+ if (stat.isSymbolicLink() || !stat.isDirectory())
28
+ throw new Error(`unsafe private directory: ${resolved}`);
29
+ }
30
+ else if (create) {
31
+ node_fs_1.default.mkdirSync(resolved, { recursive: true, mode: 0o700 });
32
+ if (node_fs_1.default.lstatSync(resolved).isSymbolicLink() || !node_fs_1.default.lstatSync(resolved).isDirectory()) {
33
+ throw new Error(`unsafe private directory: ${resolved}`);
34
+ }
35
+ }
36
+ else {
37
+ throw new Error(`unsafe private directory: ${resolved}`);
38
+ }
39
+ let dir = resolved;
40
+ for (const part of parts) {
41
+ if (!part || part === "." || part === ".." || /[\\/]/.test(part))
42
+ throw new Error(`unsafe owned path component: ${part}`);
43
+ dir = node_path_1.default.join(dir, part);
44
+ try {
45
+ const existing = node_fs_1.default.lstatSync(dir);
46
+ if (existing.isSymbolicLink() || !existing.isDirectory())
47
+ throw new Error(`unsafe private directory: ${dir}`);
48
+ }
49
+ catch (error) {
50
+ if (error.code !== "ENOENT")
51
+ throw error;
52
+ if (!create)
53
+ throw new Error(`unsafe private directory: ${dir}`);
54
+ node_fs_1.default.mkdirSync(dir, { mode: 0o700 });
55
+ if (node_fs_1.default.lstatSync(dir).isSymbolicLink() || !node_fs_1.default.lstatSync(dir).isDirectory())
56
+ throw new Error(`unsafe private directory: ${dir}`);
57
+ }
58
+ if (process.platform !== "win32")
59
+ node_fs_1.default.chmodSync(dir, 0o700);
60
+ }
61
+ return dir;
62
+ }
63
+ function ensurePrivateDirectory(directory) {
64
+ ensureOwnedTree(directory);
65
+ }
66
+ /** 独占新建 0700 目录;已存在或变成 symlink 都拒绝。 */
67
+ function mkdirPrivateExclusive(directory) {
68
+ node_fs_1.default.mkdirSync(directory, { mode: 0o700 });
69
+ const created = node_fs_1.default.lstatSync(directory);
70
+ if (created.isSymbolicLink() || !created.isDirectory())
71
+ throw new Error(`unsafe private directory: ${directory}`);
72
+ if (process.platform !== "win32")
73
+ node_fs_1.default.chmodSync(directory, 0o700);
74
+ }
75
+ /** 独占新建 0600 文件;已存在(含 symlink)时失败,不跟随写出。 */
76
+ function writePrivateExclusive(file, content) {
77
+ node_fs_1.default.writeFileSync(file, content, { mode: 0o600, flag: "wx" });
78
+ if (process.platform !== "win32")
79
+ node_fs_1.default.chmodSync(file, 0o600);
80
+ }
81
+ /** 原子写 0600 文件;目标或父目录已是 symlink 时拒绝,避免落到目录外。 */
82
+ function writePrivateAtomic(file, content) {
83
+ try {
84
+ if (node_fs_1.default.lstatSync(node_path_1.default.dirname(file)).isSymbolicLink())
85
+ throw new Error(`unsafe private directory: ${node_path_1.default.dirname(file)}`);
86
+ if (node_fs_1.default.lstatSync(file).isSymbolicLink())
87
+ throw new Error(`${node_path_1.default.basename(file)} must not be a symlink`);
88
+ }
89
+ catch (error) {
90
+ if (error.code !== "ENOENT")
91
+ throw error;
92
+ }
93
+ const temporary = node_path_1.default.join(node_path_1.default.dirname(file), `.${node_path_1.default.basename(file)}.${process.pid}.${node_crypto_1.default.randomUUID()}.tmp`);
94
+ try {
95
+ node_fs_1.default.writeFileSync(temporary, content, { mode: 0o600, flag: "wx" });
96
+ node_fs_1.default.renameSync(temporary, file);
97
+ node_fs_1.default.chmodSync(file, 0o600);
98
+ }
99
+ finally {
100
+ try {
101
+ node_fs_1.default.unlinkSync(temporary);
102
+ }
103
+ catch { /* renamed or absent */ }
104
+ }
105
+ }
@@ -0,0 +1,94 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ /** Same implicit root the Pi adapter uses when `stateDir` is omitted. */
5
+ export function effectivePiStateDir(input) {
6
+ return input.stateDir ?? path.join(input.workspaceDir, ".larkin");
7
+ }
8
+ /**
9
+ * 从 root 起逐层建 0700 子树。root 可以位于 /var、/tmp 等合法祖先 link 之后;
10
+ * 任一层 owned 组件自身是 symlink 则拒绝,不顺着 link 往外写。
11
+ */
12
+ export function ensureOwnedTree(root, parts = [], create = true) {
13
+ const resolved = path.resolve(root);
14
+ if (fs.existsSync(resolved)) {
15
+ const stat = fs.lstatSync(resolved);
16
+ if (stat.isSymbolicLink() || !stat.isDirectory())
17
+ throw new Error(`unsafe private directory: ${resolved}`);
18
+ }
19
+ else if (create) {
20
+ fs.mkdirSync(resolved, { recursive: true, mode: 0o700 });
21
+ if (fs.lstatSync(resolved).isSymbolicLink() || !fs.lstatSync(resolved).isDirectory()) {
22
+ throw new Error(`unsafe private directory: ${resolved}`);
23
+ }
24
+ }
25
+ else {
26
+ throw new Error(`unsafe private directory: ${resolved}`);
27
+ }
28
+ let dir = resolved;
29
+ for (const part of parts) {
30
+ if (!part || part === "." || part === ".." || /[\\/]/.test(part))
31
+ throw new Error(`unsafe owned path component: ${part}`);
32
+ dir = path.join(dir, part);
33
+ try {
34
+ const existing = fs.lstatSync(dir);
35
+ if (existing.isSymbolicLink() || !existing.isDirectory())
36
+ throw new Error(`unsafe private directory: ${dir}`);
37
+ }
38
+ catch (error) {
39
+ if (error.code !== "ENOENT")
40
+ throw error;
41
+ if (!create)
42
+ throw new Error(`unsafe private directory: ${dir}`);
43
+ fs.mkdirSync(dir, { mode: 0o700 });
44
+ if (fs.lstatSync(dir).isSymbolicLink() || !fs.lstatSync(dir).isDirectory())
45
+ throw new Error(`unsafe private directory: ${dir}`);
46
+ }
47
+ if (process.platform !== "win32")
48
+ fs.chmodSync(dir, 0o700);
49
+ }
50
+ return dir;
51
+ }
52
+ export function ensurePrivateDirectory(directory) {
53
+ ensureOwnedTree(directory);
54
+ }
55
+ /** 独占新建 0700 目录;已存在或变成 symlink 都拒绝。 */
56
+ export function mkdirPrivateExclusive(directory) {
57
+ fs.mkdirSync(directory, { mode: 0o700 });
58
+ const created = fs.lstatSync(directory);
59
+ if (created.isSymbolicLink() || !created.isDirectory())
60
+ throw new Error(`unsafe private directory: ${directory}`);
61
+ if (process.platform !== "win32")
62
+ fs.chmodSync(directory, 0o700);
63
+ }
64
+ /** 独占新建 0600 文件;已存在(含 symlink)时失败,不跟随写出。 */
65
+ export function writePrivateExclusive(file, content) {
66
+ fs.writeFileSync(file, content, { mode: 0o600, flag: "wx" });
67
+ if (process.platform !== "win32")
68
+ fs.chmodSync(file, 0o600);
69
+ }
70
+ /** 原子写 0600 文件;目标或父目录已是 symlink 时拒绝,避免落到目录外。 */
71
+ export function writePrivateAtomic(file, content) {
72
+ try {
73
+ if (fs.lstatSync(path.dirname(file)).isSymbolicLink())
74
+ throw new Error(`unsafe private directory: ${path.dirname(file)}`);
75
+ if (fs.lstatSync(file).isSymbolicLink())
76
+ throw new Error(`${path.basename(file)} must not be a symlink`);
77
+ }
78
+ catch (error) {
79
+ if (error.code !== "ENOENT")
80
+ throw error;
81
+ }
82
+ const temporary = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${crypto.randomUUID()}.tmp`);
83
+ try {
84
+ fs.writeFileSync(temporary, content, { mode: 0o600, flag: "wx" });
85
+ fs.renameSync(temporary, file);
86
+ fs.chmodSync(file, 0o600);
87
+ }
88
+ finally {
89
+ try {
90
+ fs.unlinkSync(temporary);
91
+ }
92
+ catch { /* renamed or absent */ }
93
+ }
94
+ }
@@ -0,0 +1,137 @@
1
+ import crypto from "node:crypto";
2
+ import { Type } from "typebox";
3
+ import { createLarkinTmux, DEFAULT_BASH_WAIT_SECONDS, ForeignTmuxTaskError, formatTmuxTaskText, LARKIN_TMUX_COMPLETION_TYPE, tmuxAvailable, } from "./pi-tmux.mjs";
4
+ function resultOf(snapshot) {
5
+ return {
6
+ content: [{ type: "text", text: formatTmuxTaskText(snapshot) }],
7
+ details: {
8
+ taskId: snapshot.taskId,
9
+ status: snapshot.status,
10
+ exitCode: snapshot.exitCode,
11
+ output: snapshot.output,
12
+ },
13
+ };
14
+ }
15
+ function errorResult(message) {
16
+ return {
17
+ content: [{ type: "text", text: message }],
18
+ details: { taskId: "", status: "failed", exitCode: null, output: message },
19
+ };
20
+ }
21
+ export function registerLarkinTmuxExtension(pi) {
22
+ if (!tmuxAvailable())
23
+ return false;
24
+ const stateDir = process.env.LARKIN_STATE_DIR || `${process.cwd()}/.larkin`;
25
+ const agentId = process.env.LARKIN_AGENT_ID || "agent";
26
+ const instanceId = process.env.LARKIN_TMUX_INSTANCE_ID
27
+ || process.env.LARKIN_RUNTIME_OBSERVATION_GENERATION
28
+ || crypto.randomUUID();
29
+ const tmux = createLarkinTmux({ stateDir, agentId, instanceId, env: process.env });
30
+ const watchers = new Map();
31
+ const notified = new Set();
32
+ const stopWatchers = () => {
33
+ for (const timer of watchers.values())
34
+ clearInterval(timer);
35
+ watchers.clear();
36
+ };
37
+ const notifyOnce = (snapshot) => {
38
+ if (snapshot.status === "running" || notified.has(snapshot.taskId))
39
+ return;
40
+ notified.add(snapshot.taskId);
41
+ pi.sendMessage({
42
+ customType: LARKIN_TMUX_COMPLETION_TYPE,
43
+ content: formatTmuxTaskText(snapshot),
44
+ display: true,
45
+ details: {
46
+ taskId: snapshot.taskId,
47
+ exitCode: snapshot.exitCode,
48
+ output: snapshot.output,
49
+ },
50
+ }, { triggerTurn: true, deliverAs: "followUp" });
51
+ };
52
+ const watch = (taskId) => {
53
+ if (watchers.has(taskId) || notified.has(taskId))
54
+ return;
55
+ const timer = setInterval(() => {
56
+ try {
57
+ const snapshot = tmux.peek(taskId);
58
+ if (snapshot.status === "running")
59
+ return;
60
+ clearInterval(timer);
61
+ watchers.delete(taskId);
62
+ notifyOnce(snapshot);
63
+ }
64
+ catch {
65
+ clearInterval(timer);
66
+ watchers.delete(taskId);
67
+ }
68
+ }, 250);
69
+ watchers.set(taskId, timer);
70
+ };
71
+ pi.on("session_shutdown", () => {
72
+ stopWatchers();
73
+ });
74
+ pi.registerTool({
75
+ name: "bash",
76
+ label: "bash",
77
+ description: "Run a command in a detached tmux window. timeout is a wait window only and never hard-kills. background:true returns immediately.",
78
+ parameters: Type.Object({
79
+ command: Type.String(),
80
+ timeout: Type.Optional(Type.Number()),
81
+ background: Type.Optional(Type.Boolean()),
82
+ }),
83
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
84
+ const snapshot = tmux.start(params.command, ctx.cwd);
85
+ if (params.background === true) {
86
+ watch(snapshot.taskId);
87
+ return resultOf(tmux.peek(snapshot.taskId));
88
+ }
89
+ const timeout = typeof params.timeout === "number" && params.timeout >= 0 ? params.timeout : DEFAULT_BASH_WAIT_SECONDS;
90
+ const waited = await tmux.wait(snapshot.taskId, timeout, signal);
91
+ if (waited.status === "running")
92
+ watch(waited.taskId);
93
+ return resultOf(waited);
94
+ },
95
+ });
96
+ pi.registerTool({
97
+ name: "tmux",
98
+ label: "tmux",
99
+ description: "List, peek, or kill Larkin-owned tmux tasks for this Agent. Foreign task IDs are rejected.",
100
+ parameters: Type.Object({
101
+ action: Type.Union([Type.Literal("list"), Type.Literal("peek"), Type.Literal("kill")]),
102
+ taskId: Type.Optional(Type.String()),
103
+ }),
104
+ async execute(_toolCallId, params) {
105
+ try {
106
+ if (params.action === "list") {
107
+ const tasks = tmux.list();
108
+ return {
109
+ content: [{ type: "text", text: tasks.map(formatTmuxTaskText).join("\n---\n") || "no owned tmux tasks" }],
110
+ details: { taskId: "", status: "completed", exitCode: 0, output: "" },
111
+ };
112
+ }
113
+ if (!params.taskId)
114
+ return errorResult("taskId is required for peek and kill");
115
+ if (params.action === "peek")
116
+ return resultOf(tmux.peek(params.taskId));
117
+ const killed = tmux.kill(params.taskId);
118
+ notified.add(params.taskId);
119
+ const timer = watchers.get(params.taskId);
120
+ if (timer) {
121
+ clearInterval(timer);
122
+ watchers.delete(params.taskId);
123
+ }
124
+ return resultOf(killed);
125
+ }
126
+ catch (error) {
127
+ if (error instanceof ForeignTmuxTaskError)
128
+ return errorResult(error.message);
129
+ throw error;
130
+ }
131
+ },
132
+ });
133
+ return true;
134
+ }
135
+ export default function larkinTmuxExtension(pi) {
136
+ registerLarkinTmuxExtension(pi);
137
+ }