@arbiterforge/ca-pi 0.16.0 → 0.16.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/README.md +1 -1
- package/package.json +1 -1
- package/plugins/ca-pi/CHANGELOG.md +7 -0
- package/plugins/ca-pi/helpers/artifacts/release.json +18 -18
- package/plugins/ca-pi/hooks/_artifactauthoritylib.py +107 -74
- package/plugins/ca-pi/hooks/_host.py +1 -1
- package/plugins/ca-pi/hooks/hostapi.py +1 -1
- package/plugins/ca-pi/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ project context. You decide. codeArbiter enforces.
|
|
|
10
10
|
<img alt="Claude Code plugin" src="https://img.shields.io/badge/Claude_Code-plugin-d97757">
|
|
11
11
|
<img alt="Codex plugin" src="https://img.shields.io/badge/OpenAI_Codex-plugin-10a37f">
|
|
12
12
|
<img alt="Pi Feature Forge preview" src="https://img.shields.io/badge/ca--pi-Feature_Forge_preview-d97757">
|
|
13
|
-
<img alt="version 2.24.
|
|
13
|
+
<img alt="version 2.24.1" src="https://img.shields.io/badge/version-2.24.1-2b7489">
|
|
14
14
|
<img alt="core lanes" src="https://img.shields.io/badge/core_lanes-18-555">
|
|
15
15
|
<img alt="skills" src="https://img.shields.io/badge/skills-22-555">
|
|
16
16
|
<img alt="agents" src="https://img.shields.io/badge/agents-20-555">
|
package/package.json
CHANGED
|
@@ -4,6 +4,13 @@ All notable changes to `ca-pi` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.16.1] - 2026-10-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Carry the shared Claude review-join fix in the vendored authority library. Pi
|
|
12
|
+
behavior is unchanged.
|
|
13
|
+
|
|
7
14
|
## [0.16.0] - 2026-10-03
|
|
8
15
|
|
|
9
16
|
### Added
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
},
|
|
38
38
|
"context_qualification": {
|
|
39
39
|
"darwin/amd64": {
|
|
40
|
-
"source_commit": "
|
|
40
|
+
"source_commit": "fa94c56afa59aaa3e429bf15613989bd444090f6",
|
|
41
41
|
"workflow": ".github/workflows/ci.yml",
|
|
42
|
-
"run_id": "
|
|
42
|
+
"run_id": "37253197914",
|
|
43
43
|
"binary_sha256": "27fc51465a79c1525b1300b347cee4d09afde3c7b0d845414659302f5bbca2f7",
|
|
44
|
-
"observation_sha256": "
|
|
44
|
+
"observation_sha256": "f6a8c71afa0e06521de263ae49934ce33f4272ed40d0fec6d9a17f7a68357c87",
|
|
45
45
|
"module_sha256": "d5cccd13647e2c3a2a752b94b34dec4d4fc6aec5ced8fb0abce6ec9346ba1a7e",
|
|
46
46
|
"native_cases": [
|
|
47
47
|
"TestContextBoundedRender",
|
|
@@ -53,11 +53,11 @@
|
|
|
53
53
|
]
|
|
54
54
|
},
|
|
55
55
|
"darwin/arm64": {
|
|
56
|
-
"source_commit": "
|
|
56
|
+
"source_commit": "fa94c56afa59aaa3e429bf15613989bd444090f6",
|
|
57
57
|
"workflow": ".github/workflows/ci.yml",
|
|
58
|
-
"run_id": "
|
|
58
|
+
"run_id": "37253197914",
|
|
59
59
|
"binary_sha256": "3a3d61be18eba426db4f260b9cbae0d3d3c89f47efe184395d7f011cc3b4f59a",
|
|
60
|
-
"observation_sha256": "
|
|
60
|
+
"observation_sha256": "2ca37cd149e0efdf73929f0398334089cd3bbb1bba03c61407a485eb8edaecad",
|
|
61
61
|
"module_sha256": "d5cccd13647e2c3a2a752b94b34dec4d4fc6aec5ced8fb0abce6ec9346ba1a7e",
|
|
62
62
|
"native_cases": [
|
|
63
63
|
"TestContextBoundedRender",
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
]
|
|
70
70
|
},
|
|
71
71
|
"linux/amd64": {
|
|
72
|
-
"source_commit": "
|
|
72
|
+
"source_commit": "fa94c56afa59aaa3e429bf15613989bd444090f6",
|
|
73
73
|
"workflow": ".github/workflows/ci.yml",
|
|
74
|
-
"run_id": "
|
|
74
|
+
"run_id": "37253197914",
|
|
75
75
|
"binary_sha256": "3b6178bb54d6a19fc56d99f8fced862ae151605a0373297a69e130d3857a8406",
|
|
76
|
-
"observation_sha256": "
|
|
76
|
+
"observation_sha256": "85f789dc87517f96042a8e88da689100bcd561ddda863e0986ace221f18dbbab",
|
|
77
77
|
"module_sha256": "d5cccd13647e2c3a2a752b94b34dec4d4fc6aec5ced8fb0abce6ec9346ba1a7e",
|
|
78
78
|
"native_cases": [
|
|
79
79
|
"TestContextBoundedRender",
|
|
@@ -85,11 +85,11 @@
|
|
|
85
85
|
]
|
|
86
86
|
},
|
|
87
87
|
"linux/arm64": {
|
|
88
|
-
"source_commit": "
|
|
88
|
+
"source_commit": "fa94c56afa59aaa3e429bf15613989bd444090f6",
|
|
89
89
|
"workflow": ".github/workflows/ci.yml",
|
|
90
|
-
"run_id": "
|
|
90
|
+
"run_id": "37253197914",
|
|
91
91
|
"binary_sha256": "a5259074098932f815f33d3e1dea98e6d9a04277d1ada958469905da67d29437",
|
|
92
|
-
"observation_sha256": "
|
|
92
|
+
"observation_sha256": "50afdbe9ce2e9893cc3557fc230010baf5c093da6f0839f17ed15a3c45c69905",
|
|
93
93
|
"module_sha256": "d5cccd13647e2c3a2a752b94b34dec4d4fc6aec5ced8fb0abce6ec9346ba1a7e",
|
|
94
94
|
"native_cases": [
|
|
95
95
|
"TestContextBoundedRender",
|
|
@@ -101,11 +101,11 @@
|
|
|
101
101
|
]
|
|
102
102
|
},
|
|
103
103
|
"windows/amd64": {
|
|
104
|
-
"source_commit": "
|
|
104
|
+
"source_commit": "fa94c56afa59aaa3e429bf15613989bd444090f6",
|
|
105
105
|
"workflow": ".github/workflows/ci.yml",
|
|
106
|
-
"run_id": "
|
|
106
|
+
"run_id": "37253197914",
|
|
107
107
|
"binary_sha256": "8339c077262f861631631de49bca7cdafec5e0730b9e3676fd2db36f3c119036",
|
|
108
|
-
"observation_sha256": "
|
|
108
|
+
"observation_sha256": "56e2f848ba306bbf4acbb2febdfd298e08988761a53b87d9dded7bbf23c9a0ea",
|
|
109
109
|
"module_sha256": "d5cccd13647e2c3a2a752b94b34dec4d4fc6aec5ced8fb0abce6ec9346ba1a7e",
|
|
110
110
|
"native_cases": [
|
|
111
111
|
"TestContextBoundedRender",
|
|
@@ -117,11 +117,11 @@
|
|
|
117
117
|
]
|
|
118
118
|
},
|
|
119
119
|
"windows/arm64": {
|
|
120
|
-
"source_commit": "
|
|
120
|
+
"source_commit": "fa94c56afa59aaa3e429bf15613989bd444090f6",
|
|
121
121
|
"workflow": ".github/workflows/ci.yml",
|
|
122
|
-
"run_id": "
|
|
122
|
+
"run_id": "37253197914",
|
|
123
123
|
"binary_sha256": "f7b193cf0d1eb9ecd233bf1588f6290c2fb8bc41ad327918a740beb2099f719c",
|
|
124
|
-
"observation_sha256": "
|
|
124
|
+
"observation_sha256": "5db05023f4fe8441c0e2f664fc0704f42967285a54566f801f9e37f9ad4819a5",
|
|
125
125
|
"module_sha256": "d5cccd13647e2c3a2a752b94b34dec4d4fc6aec5ced8fb0abce6ec9346ba1a7e",
|
|
126
126
|
"native_cases": [
|
|
127
127
|
"TestContextBoundedRender",
|
|
@@ -29,7 +29,7 @@ than one SubagentStop; only the first is a review result.
|
|
|
29
29
|
|
|
30
30
|
from __future__ import annotations
|
|
31
31
|
|
|
32
|
-
from contextlib import contextmanager
|
|
32
|
+
from contextlib import ExitStack, contextmanager
|
|
33
33
|
import base64
|
|
34
34
|
import errno
|
|
35
35
|
import hashlib
|
|
@@ -77,6 +77,9 @@ MAX_STATE = 1 << 20
|
|
|
77
77
|
# Only terminal abandonment can read a legacy inline-context review this large.
|
|
78
78
|
MAX_RECOVERY_STATE = 2 << 20
|
|
79
79
|
REQUEST_LOCK_WAIT_SECONDS = 5.0
|
|
80
|
+
# The Claude join lock is per user, not per request: every SubagentStart waits
|
|
81
|
+
# on it while any review is LAUNCHING, and the holder scans the registry.
|
|
82
|
+
CLAUDE_JOIN_LOCK_WAIT_SECONDS = 30.0
|
|
80
83
|
MAX_OUTPUT = 8 << 20
|
|
81
84
|
MAX_DECISION = 65536
|
|
82
85
|
MAX_COMPLETION_FILES = 16
|
|
@@ -325,7 +328,7 @@ def _spool_root(root: Path) -> Path:
|
|
|
325
328
|
|
|
326
329
|
|
|
327
330
|
@contextmanager
|
|
328
|
-
def _request_lock(spool: Path, relative: Path):
|
|
331
|
+
def _request_lock(spool: Path, relative: Path, wait: float | None = None):
|
|
329
332
|
"""Keep one OS-owned lock inode for all writers of this request."""
|
|
330
333
|
path = spool / f"{relative.name}.lock"
|
|
331
334
|
fd = None
|
|
@@ -355,7 +358,7 @@ def _request_lock(spool: Path, relative: Path):
|
|
|
355
358
|
locked = False
|
|
356
359
|
try:
|
|
357
360
|
check_path()
|
|
358
|
-
deadline = time.monotonic() + REQUEST_LOCK_WAIT_SECONDS
|
|
361
|
+
deadline = time.monotonic() + (REQUEST_LOCK_WAIT_SECONDS if wait is None else wait)
|
|
359
362
|
if os.name == "nt":
|
|
360
363
|
import msvcrt
|
|
361
364
|
else:
|
|
@@ -3216,6 +3219,91 @@ def _claude_bind_child(root: Path, request: dict[str, Any]) -> dict[str, Any]:
|
|
|
3216
3219
|
return {"request_id": request["request_id"], "state": "RUNNING"}
|
|
3217
3220
|
|
|
3218
3221
|
|
|
3222
|
+
@contextmanager
|
|
3223
|
+
def _claude_join_lock():
|
|
3224
|
+
"""Serialize the Claude launch/child join across hook processes.
|
|
3225
|
+
|
|
3226
|
+
SubagentStart and an async Agent launch result fire back to back as
|
|
3227
|
+
separate hook processes. Each reads the request and then writes its half
|
|
3228
|
+
of the join; unserialized, either can read before the other writes and
|
|
3229
|
+
both miss, leaving the review LAUNCHING with no start marker."""
|
|
3230
|
+
with ExitStack() as stack:
|
|
3231
|
+
try:
|
|
3232
|
+
stack.enter_context(
|
|
3233
|
+
_request_lock(_registry_root(), Path("claude-join"), wait=CLAUDE_JOIN_LOCK_WAIT_SECONDS)
|
|
3234
|
+
)
|
|
3235
|
+
except OSError as exc:
|
|
3236
|
+
# Only acquisition maps here; the joined body reports its own errors.
|
|
3237
|
+
raise AuthorityError("AUTHORITY_BUSY", "Claude review join is unavailable") from exc
|
|
3238
|
+
yield
|
|
3239
|
+
|
|
3240
|
+
|
|
3241
|
+
def _claude_launch_result(event: dict[str, Any]) -> dict[str, Any] | None:
|
|
3242
|
+
session_id = event.get("session_id")
|
|
3243
|
+
tool_use_id = event.get("tool_use_id")
|
|
3244
|
+
matches = [
|
|
3245
|
+
(candidate_root, candidate)
|
|
3246
|
+
for candidate_root, candidate in _claude_requests({"LAUNCHING"})
|
|
3247
|
+
if (candidate.get("launch") or {}).get("tool_use_id") == tool_use_id
|
|
3248
|
+
and candidate["launch"].get("parent_session_id") == session_id
|
|
3249
|
+
]
|
|
3250
|
+
if not matches:
|
|
3251
|
+
return None
|
|
3252
|
+
if len(matches) != 1:
|
|
3253
|
+
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "review launch correlation is ambiguous")
|
|
3254
|
+
root, request = matches[0]
|
|
3255
|
+
response = event.get("tool_response")
|
|
3256
|
+
try:
|
|
3257
|
+
prompt_id = _host_id(event.get("prompt_id"), "prompt_id")
|
|
3258
|
+
if request["launch"].get("parent_prompt_id") != prompt_id or request["launch"]["post_confirmed"]:
|
|
3259
|
+
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "review launch result is out of order")
|
|
3260
|
+
if not isinstance(response, dict):
|
|
3261
|
+
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "agent result is malformed")
|
|
3262
|
+
agent_id = _host_id(response.get("agentId"), "agentId")
|
|
3263
|
+
resolved = _model_id(response.get("resolvedModel"), "resolvedModel")
|
|
3264
|
+
except AuthorityError:
|
|
3265
|
+
_reject(root, request, "uncorrelated-launch-result")
|
|
3266
|
+
raise
|
|
3267
|
+
request["launch"].update(post_confirmed=True, agent_id=agent_id, resolved_model=resolved)
|
|
3268
|
+
return _claude_bind_child(root, request)
|
|
3269
|
+
|
|
3270
|
+
|
|
3271
|
+
def _claude_subagent_start(event: dict[str, Any]) -> dict[str, Any] | None:
|
|
3272
|
+
agent_id = _host_id(event.get("agent_id"), "agent_id")
|
|
3273
|
+
agent_type = _host_id(event.get("agent_type"), "agent_type")
|
|
3274
|
+
pending = _claude_requests({"LAUNCHING"})
|
|
3275
|
+
for candidate_root, candidate in pending:
|
|
3276
|
+
launch = candidate.get("launch") or {}
|
|
3277
|
+
if launch.get("agent_id") == agent_id:
|
|
3278
|
+
if agent_type != CLAUDE_REVIEWER:
|
|
3279
|
+
_reject(candidate_root, candidate, "unpinned-reviewer-agent")
|
|
3280
|
+
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "subagent is not the pinned reviewer")
|
|
3281
|
+
launch["agent_type"] = agent_type
|
|
3282
|
+
candidate["state"] = "RUNNING"
|
|
3283
|
+
_save(candidate_root, candidate)
|
|
3284
|
+
return {"request_id": candidate["request_id"], "state": "RUNNING"}
|
|
3285
|
+
if any((candidate.get("launch") or {}).get("post_confirmed") is False for _root, candidate in pending):
|
|
3286
|
+
# The launch result may not have arrived yet; remember this start.
|
|
3287
|
+
marker = _claude_start_marker(agent_id)
|
|
3288
|
+
data = _canonical({"agent_id": agent_id, "agent_type": agent_type})
|
|
3289
|
+
# Written aside and linked into place: a reader must never see an
|
|
3290
|
+
# empty or partial marker.
|
|
3291
|
+
temporary = marker.with_name(f".{marker.name}.{secrets.token_hex(8)}.tmp")
|
|
3292
|
+
fd = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
|
3293
|
+
try:
|
|
3294
|
+
os.write(fd, data)
|
|
3295
|
+
os.fsync(fd)
|
|
3296
|
+
finally:
|
|
3297
|
+
os.close(fd)
|
|
3298
|
+
try:
|
|
3299
|
+
os.link(temporary, marker)
|
|
3300
|
+
except FileExistsError:
|
|
3301
|
+
return None
|
|
3302
|
+
finally:
|
|
3303
|
+
temporary.unlink(missing_ok=True)
|
|
3304
|
+
return None
|
|
3305
|
+
|
|
3306
|
+
|
|
3219
3307
|
def observe_claude_hook(root: str | Path, event: dict[str, Any]) -> dict[str, Any] | None:
|
|
3220
3308
|
"""Correlate a Claude Code reviewer launch, child lifecycle, and first result.
|
|
3221
3309
|
|
|
@@ -3275,77 +3363,14 @@ def observe_claude_hook(root: str | Path, event: dict[str, Any]) -> dict[str, An
|
|
|
3275
3363
|
return {"request_id": request["request_id"], "state": "LAUNCHING"}
|
|
3276
3364
|
|
|
3277
3365
|
if name == "PostToolUse" and tool == "Agent":
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
matches = [
|
|
3281
|
-
(candidate_root, candidate)
|
|
3282
|
-
for candidate_root, candidate in _claude_requests({"LAUNCHING"})
|
|
3283
|
-
if (candidate.get("launch") or {}).get("tool_use_id") == tool_use_id
|
|
3284
|
-
and candidate["launch"].get("parent_session_id") == session_id
|
|
3285
|
-
]
|
|
3286
|
-
if not matches:
|
|
3287
|
-
return None
|
|
3288
|
-
if len(matches) != 1:
|
|
3289
|
-
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "review launch correlation is ambiguous")
|
|
3290
|
-
root, request = matches[0]
|
|
3291
|
-
response = event.get("tool_response")
|
|
3292
|
-
try:
|
|
3293
|
-
prompt_id = _host_id(event.get("prompt_id"), "prompt_id")
|
|
3294
|
-
if request["launch"].get("parent_prompt_id") != prompt_id or request["launch"]["post_confirmed"]:
|
|
3295
|
-
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "review launch result is out of order")
|
|
3296
|
-
if not isinstance(response, dict):
|
|
3297
|
-
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "agent result is malformed")
|
|
3298
|
-
agent_id = _host_id(response.get("agentId"), "agentId")
|
|
3299
|
-
resolved = _model_id(response.get("resolvedModel"), "resolvedModel")
|
|
3300
|
-
except AuthorityError:
|
|
3301
|
-
_reject(root, request, "uncorrelated-launch-result")
|
|
3302
|
-
raise
|
|
3303
|
-
request["launch"].update(post_confirmed=True, agent_id=agent_id, resolved_model=resolved)
|
|
3304
|
-
return _claude_bind_child(root, request)
|
|
3366
|
+
with _claude_join_lock():
|
|
3367
|
+
return _claude_launch_result(event)
|
|
3305
3368
|
|
|
3306
3369
|
if name == "SubagentStart":
|
|
3307
3370
|
if not _claude_requests({"LAUNCHING"}):
|
|
3308
3371
|
return None
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
for candidate_root, candidate in _claude_requests({"LAUNCHING"}):
|
|
3312
|
-
launch = candidate.get("launch") or {}
|
|
3313
|
-
if launch.get("agent_id") == agent_id:
|
|
3314
|
-
if agent_type != CLAUDE_REVIEWER:
|
|
3315
|
-
_reject(candidate_root, candidate, "unpinned-reviewer-agent")
|
|
3316
|
-
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "subagent is not the pinned reviewer")
|
|
3317
|
-
launch["agent_type"] = agent_type
|
|
3318
|
-
candidate["state"] = "RUNNING"
|
|
3319
|
-
_save(candidate_root, candidate)
|
|
3320
|
-
return {"request_id": candidate["request_id"], "state": "RUNNING"}
|
|
3321
|
-
if any(
|
|
3322
|
-
(candidate.get("launch") or {}).get("post_confirmed") is False
|
|
3323
|
-
for _root, candidate in _claude_requests({"LAUNCHING"})
|
|
3324
|
-
):
|
|
3325
|
-
# The launch result may not have arrived yet; remember this start.
|
|
3326
|
-
marker = _claude_start_marker(agent_id)
|
|
3327
|
-
data = _canonical({"agent_id": agent_id, "agent_type": agent_type})
|
|
3328
|
-
# Written aside and linked into place: a concurrent launch result
|
|
3329
|
-
# joining this agent must never read an empty or partial marker.
|
|
3330
|
-
temporary = marker.with_name(f".{marker.name}.{secrets.token_hex(8)}.tmp")
|
|
3331
|
-
fd = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
|
3332
|
-
try:
|
|
3333
|
-
os.write(fd, data)
|
|
3334
|
-
os.fsync(fd)
|
|
3335
|
-
finally:
|
|
3336
|
-
os.close(fd)
|
|
3337
|
-
try:
|
|
3338
|
-
os.link(temporary, marker)
|
|
3339
|
-
except FileExistsError:
|
|
3340
|
-
return None
|
|
3341
|
-
finally:
|
|
3342
|
-
temporary.unlink(missing_ok=True)
|
|
3343
|
-
# The launch result may have bound this agent while the marker was
|
|
3344
|
-
# being written; finish the join now rather than strand it.
|
|
3345
|
-
for candidate_root, candidate in _claude_requests({"LAUNCHING"}):
|
|
3346
|
-
if (candidate.get("launch") or {}).get("agent_id") == agent_id:
|
|
3347
|
-
return _claude_bind_child(candidate_root, candidate)
|
|
3348
|
-
return None
|
|
3372
|
+
with _claude_join_lock():
|
|
3373
|
+
return _claude_subagent_start(event)
|
|
3349
3374
|
|
|
3350
3375
|
if name == "PostToolUse" and tool == CLAUDE_HANDBACK_TOOL:
|
|
3351
3376
|
# Every child's handback lands here; only the bound reviewer's counts.
|
|
@@ -3376,8 +3401,12 @@ def observe_claude_hook(root: str | Path, event: dict[str, Any]) -> dict[str, An
|
|
|
3376
3401
|
_reject(root, request, "repeated-handback")
|
|
3377
3402
|
raise AuthorityError("UNSUPPORTED_HOST_SEAM", "reviewer reported more than once")
|
|
3378
3403
|
if request["state"] == "LAUNCHING" and request["launch"].get("post_confirmed") is True:
|
|
3379
|
-
#
|
|
3380
|
-
|
|
3404
|
+
# Defensive: the join lock leaves no late-marker race in this
|
|
3405
|
+
# adapter, but bind any marker still waiting under the same lock.
|
|
3406
|
+
with _claude_join_lock():
|
|
3407
|
+
current = _load(root, request["request_id"])
|
|
3408
|
+
if current["state"] == "LAUNCHING":
|
|
3409
|
+
_claude_bind_child(root, current)
|
|
3381
3410
|
request = _load(root, request["request_id"])
|
|
3382
3411
|
candidate = {**request, "handback": record}
|
|
3383
3412
|
if (
|
|
@@ -3414,8 +3443,12 @@ def observe_claude_hook(root: str | Path, event: dict[str, Any]) -> dict[str, An
|
|
|
3414
3443
|
root, request = matches[0]
|
|
3415
3444
|
launch = request["launch"]
|
|
3416
3445
|
if request["state"] == "LAUNCHING" and launch.get("post_confirmed") is True:
|
|
3417
|
-
#
|
|
3418
|
-
|
|
3446
|
+
# Defensive: the join lock leaves no late-marker race in this
|
|
3447
|
+
# adapter, but bind any marker still waiting under the same lock.
|
|
3448
|
+
with _claude_join_lock():
|
|
3449
|
+
current = _load(root, request["request_id"])
|
|
3450
|
+
if current["state"] == "LAUNCHING":
|
|
3451
|
+
_claude_bind_child(root, current)
|
|
3419
3452
|
request = _load(root, request["request_id"])
|
|
3420
3453
|
launch = request["launch"]
|
|
3421
3454
|
try:
|
|
@@ -11,7 +11,7 @@ import hostapi # noqa: E402
|
|
|
11
11
|
class PiHost(hostapi.Host):
|
|
12
12
|
name = "pi"
|
|
13
13
|
adapter_name = "@arbiterforge/ca-pi"
|
|
14
|
-
adapter_version = "0.16.
|
|
14
|
+
adapter_version = "0.16.1"
|
|
15
15
|
update_target = "ca-pi"
|
|
16
16
|
update_tag_prefix = "ca-pi-v"
|
|
17
17
|
update_command = "pi update npm:@arbiterforge/ca-pi"
|
|
@@ -313,7 +313,7 @@ class Host:
|
|
|
313
313
|
|
|
314
314
|
name = "claude"
|
|
315
315
|
adapter_name = "ca"
|
|
316
|
-
adapter_version = "2.24.
|
|
316
|
+
adapter_version = "2.24.1"
|
|
317
317
|
|
|
318
318
|
# Update-notifier descriptor. Each independently versioned host overrides
|
|
319
319
|
# these three values in its per-plugin _host.py. Keeping the target,
|