anolisa-tokenless 0.7.14 → 0.8.0
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 +206 -79
- package/adapters/tokenless/claude-code/.claude-plugin/plugin.json +1 -1
- package/adapters/tokenless/claude-code/hooks/run-hook.sh +62 -0
- package/adapters/tokenless/codex/.codex-plugin/plugin.json +1 -1
- package/adapters/tokenless/common/cosh-extension.json +4 -4
- package/adapters/tokenless/common/hooks/compress_response_hook.py +180 -96
- package/adapters/tokenless/common/hooks/compress_schema_hook.py +19 -31
- package/adapters/tokenless/common/hooks/hook_utils.py +240 -70
- package/adapters/tokenless/common/hooks/rewrite_hook.py +53 -169
- package/adapters/tokenless/dsh/dist/index.js +353 -264
- package/adapters/tokenless/dsh/package.json +2 -2
- package/adapters/tokenless/hermes/__init__.py +191 -357
- package/adapters/tokenless/hermes/plugin.yaml +2 -2
- package/adapters/tokenless/manifest.json +17 -1
- package/adapters/tokenless/openclaw/dist/index.d.ts +4 -16
- package/adapters/tokenless/openclaw/dist/index.js +291 -507
- package/adapters/tokenless/openclaw/index.ts +408 -628
- package/adapters/tokenless/openclaw/openclaw.plugin.json +4 -20
- package/adapters/tokenless/openclaw/package.json +6 -4
- package/adapters/tokenless/qoder/.qoder-plugin/plugin.json +1 -1
- package/adapters/tokenless/qwencode/hooks/run-hook.sh +62 -0
- package/adapters/tokenless/qwencode/qwen-extension.json +4 -4
- package/adapters/tokenless/qwenpaw/plugin.json +17 -0
- package/adapters/tokenless/qwenpaw/plugin.py +390 -0
- package/adapters/tokenless/qwenpaw/requirements.txt +6 -0
- package/adapters/tokenless/qwenpaw/scripts/detect.sh +131 -0
- package/adapters/tokenless/qwenpaw/scripts/install.sh +98 -0
- package/adapters/tokenless/qwenpaw/scripts/uninstall.sh +61 -0
- package/package.json +5 -5
- package/adapters/tokenless/common/hooks/compress_toon_hook.py +0 -174
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import hashlib
|
|
5
6
|
import json
|
|
6
7
|
import os
|
|
7
8
|
import re
|
|
8
9
|
import shutil
|
|
9
10
|
import subprocess
|
|
10
11
|
import sys
|
|
12
|
+
import time
|
|
11
13
|
|
|
12
14
|
# -- Binary fallback paths ----------------------------------------------------
|
|
13
15
|
#
|
|
@@ -35,16 +37,10 @@ def _user_path(*parts: str) -> str:
|
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
_TOKENLESS_FALLBACK = "/usr/bin/tokenless"
|
|
38
|
-
_TOKENLESS_LOCAL_SHARE = _user_path(
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
_TOKENLESS_LOCAL_LIB = _user_path(
|
|
42
|
-
".local", "lib", "anolisa", "tokenless", "tokenless"
|
|
43
|
-
)
|
|
40
|
+
_TOKENLESS_LOCAL_SHARE = _user_path(".local", "share", "anolisa", "tokenless", "tokenless")
|
|
41
|
+
_TOKENLESS_LOCAL_LIB = _user_path(".local", "lib", "anolisa", "tokenless", "tokenless")
|
|
44
42
|
_RTK_FALLBACK = "/usr/libexec/anolisa/tokenless/rtk"
|
|
45
|
-
_RTK_LOCAL_SHARE = _user_path(
|
|
46
|
-
".local", "share", "anolisa", "tokenless", "rtk"
|
|
47
|
-
)
|
|
43
|
+
_RTK_LOCAL_SHARE = _user_path(".local", "share", "anolisa", "tokenless", "rtk")
|
|
48
44
|
_RTK_LOCAL_LIB = _user_path(".local", "lib", "anolisa", "tokenless", "rtk")
|
|
49
45
|
|
|
50
46
|
_TOKENLESS_HELPER_BINARIES = frozenset({"rtk"})
|
|
@@ -87,9 +83,7 @@ def _known_binary_paths(name: str, home: str | None = None) -> tuple[str, ...]:
|
|
|
87
83
|
paths.append(os.path.join("/usr/local/bin", name))
|
|
88
84
|
if name in _TOKENLESS_HELPER_BINARIES:
|
|
89
85
|
# Anolisa CLI system mode.
|
|
90
|
-
paths.append(
|
|
91
|
-
os.path.join("/usr/local/libexec/anolisa/tokenless", name)
|
|
92
|
-
)
|
|
86
|
+
paths.append(os.path.join("/usr/local/libexec/anolisa/tokenless", name))
|
|
93
87
|
paths.append(os.path.join("/usr/bin", name))
|
|
94
88
|
if name in _TOKENLESS_HELPER_BINARIES:
|
|
95
89
|
paths.extend(
|
|
@@ -103,16 +97,13 @@ def _known_binary_paths(name: str, home: str | None = None) -> tuple[str, ...]:
|
|
|
103
97
|
if user_home:
|
|
104
98
|
paths.extend(
|
|
105
99
|
[
|
|
106
|
-
os.path.join(
|
|
107
|
-
|
|
108
|
-
),
|
|
109
|
-
os.path.join(
|
|
110
|
-
user_home, ".local", "lib", "anolisa", "tokenless", name
|
|
111
|
-
),
|
|
100
|
+
os.path.join(user_home, ".local", "share", "anolisa", "tokenless", name),
|
|
101
|
+
os.path.join(user_home, ".local", "lib", "anolisa", "tokenless", name),
|
|
112
102
|
]
|
|
113
103
|
)
|
|
114
104
|
return tuple(paths)
|
|
115
105
|
|
|
106
|
+
|
|
116
107
|
# -- Unified tool categorization ----------------------------------------------
|
|
117
108
|
|
|
118
109
|
# Tool categories are loaded from tool_categories.json, which serves as the
|
|
@@ -126,16 +117,38 @@ _TOOL_CATEGORIES_PATH = os.path.join(os.path.dirname(__file__), "tool_categories
|
|
|
126
117
|
# invalid. Matches the minimum safe classification from before the JSON was
|
|
127
118
|
# introduced, ensuring content-retrieval tools are never accidentally compressed.
|
|
128
119
|
_FALLBACK_SKIP_TOOLS = [
|
|
129
|
-
"Read",
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
120
|
+
"Read",
|
|
121
|
+
"read",
|
|
122
|
+
"read_file",
|
|
123
|
+
"read_many_files",
|
|
124
|
+
"Glob",
|
|
125
|
+
"glob",
|
|
126
|
+
"search_file",
|
|
127
|
+
"list_directory",
|
|
128
|
+
"list_dir",
|
|
129
|
+
"Grep",
|
|
130
|
+
"grep",
|
|
131
|
+
"grep_code",
|
|
132
|
+
"grep_search",
|
|
133
|
+
"search_files",
|
|
134
|
+
"Lsp",
|
|
135
|
+
"lsp",
|
|
136
|
+
"NotebookRead",
|
|
137
|
+
"notebook_read",
|
|
138
|
+
"notebookread",
|
|
134
139
|
]
|
|
135
140
|
_FALLBACK_SHELL_TOOLS = [
|
|
136
|
-
"Bash",
|
|
137
|
-
"
|
|
138
|
-
"
|
|
141
|
+
"Bash",
|
|
142
|
+
"bash",
|
|
143
|
+
"Shell",
|
|
144
|
+
"shell",
|
|
145
|
+
"exec",
|
|
146
|
+
"terminal",
|
|
147
|
+
"run_shell_command",
|
|
148
|
+
"run_in_terminal",
|
|
149
|
+
"get_terminal_output",
|
|
150
|
+
"execute_command",
|
|
151
|
+
"process",
|
|
139
152
|
]
|
|
140
153
|
|
|
141
154
|
|
|
@@ -187,6 +200,29 @@ SKIP_TOOLS: set[str] = set(_tool_categories.get("layer_1_skip", {}).get("tools",
|
|
|
187
200
|
# These tools produce text output that can be safely truncated if too long.
|
|
188
201
|
SHELL_TOOLS: set[str] = set(_tool_categories.get("layer_2_shell", {}).get("tools", []))
|
|
189
202
|
|
|
203
|
+
_TOKENLESS_RETRIEVE_COMMAND_RE = re.compile(
|
|
204
|
+
r"^[ \t]*(?:\"tokenless\"|'tokenless'|tokenless)[ \t]+retrieve[ \t]+"
|
|
205
|
+
r"(?:\"(?:[0-9a-f]{24}|<<tokenless:[0-9a-f]{24}>>)\"|"
|
|
206
|
+
r"'(?:[0-9a-f]{24}|<<tokenless:[0-9a-f]{24}>>)'|[0-9a-f]{24})[ \t]*$",
|
|
207
|
+
re.IGNORECASE,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def tokenless_retrieve_command_available() -> bool:
|
|
212
|
+
"""Return whether a Marker command can invoke bare ``tokenless``."""
|
|
213
|
+
return shutil.which("tokenless") is not None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def is_tokenless_retrieve_command(tool_name: str, arguments: object) -> bool:
|
|
217
|
+
"""Recognize the exact local recovery command emitted by Tokenless markers."""
|
|
218
|
+
if tool_name not in SHELL_TOOLS or not isinstance(arguments, dict):
|
|
219
|
+
return False
|
|
220
|
+
command = arguments.get("command")
|
|
221
|
+
if not isinstance(command, str):
|
|
222
|
+
return False
|
|
223
|
+
return _TOKENLESS_RETRIEVE_COMMAND_RE.fullmatch(command) is not None
|
|
224
|
+
|
|
225
|
+
|
|
190
226
|
# Layer 3: API tools (zero-truncation).
|
|
191
227
|
# These tools return structured data or API responses that should not be truncated.
|
|
192
228
|
# No explicit set needed; tools not in SKIP_TOOLS or SHELL_TOOLS are Layer 3.
|
|
@@ -196,7 +232,7 @@ SHELL_TOOLS: set[str] = set(_tool_categories.get("layer_2_shell", {}).get("tools
|
|
|
196
232
|
# is missing or the file failed to load.
|
|
197
233
|
|
|
198
234
|
# Layer 2 thresholds: moderate truncation for shell/exec output.
|
|
199
|
-
# Restores old
|
|
235
|
+
# Restores the old JSON compression defaults for shell commands (git log, ls,
|
|
200
236
|
# cat, etc.) where truncation is acceptable.
|
|
201
237
|
# 64K strings: 95% of real shell output (git diff ~63K, git log ~34K) preserved.
|
|
202
238
|
# 128 arrays: 95% of result sets (test results, audit reports) preserved.
|
|
@@ -211,11 +247,6 @@ _TRUNCATE_STRINGS_AT = _layer3_thr.get("truncate_strings_at", 1_048_576)
|
|
|
211
247
|
_TRUNCATE_ARRAYS_AT = _layer3_thr.get("truncate_arrays_at", 65_536)
|
|
212
248
|
_MAX_DEPTH = _layer3_thr.get("max_depth", 32)
|
|
213
249
|
|
|
214
|
-
# Backward-compatible alias — direct reference (not a copy) so consumers see
|
|
215
|
-
# the same set as SKIP_TOOLS. Used by compress_toon_hook.py for the standalone
|
|
216
|
-
# TOON-only path where "content retrieval" is the more descriptive name.
|
|
217
|
-
CONTENT_RETRIEVAL_TOOLS = SKIP_TOOLS
|
|
218
|
-
|
|
219
250
|
|
|
220
251
|
def get_thresholds(tool_name: str) -> tuple[int, int, int]:
|
|
221
252
|
"""Return (truncate_strings_at, truncate_arrays_at, max_depth) for a tool.
|
|
@@ -346,6 +377,9 @@ def classify_env_error(tool_response) -> tuple[str | None, str | None]:
|
|
|
346
377
|
|
|
347
378
|
_CONTEXT_DIR = os.path.join(os.path.expanduser("~"), ".tokenless")
|
|
348
379
|
_CONTEXT_FILE = os.path.join(_CONTEXT_DIR, ".rewrite-context")
|
|
380
|
+
_OPTIMIZATION_STATE_DIR = os.path.join(_CONTEXT_DIR, "hook-state")
|
|
381
|
+
_OPTIMIZATION_STATE_TTL_SECONDS = 24 * 60 * 60
|
|
382
|
+
_OPTIMIZATION_STATE_MAX_FILES = 1024
|
|
349
383
|
|
|
350
384
|
# -- Binary resolution (cached) -----------------------------------------------
|
|
351
385
|
|
|
@@ -412,9 +446,7 @@ def unwrap_string_json(raw: str) -> str | None:
|
|
|
412
446
|
# characters (code points), not \uXXXX escape sequences, so
|
|
413
447
|
# string-wrapped payloads are measured the same way as the
|
|
414
448
|
# dict/list branch and the OpenClaw adapter.
|
|
415
|
-
return json.dumps(
|
|
416
|
-
inner_obj, separators=(",", ":"), ensure_ascii=False
|
|
417
|
-
)
|
|
449
|
+
return json.dumps(inner_obj, separators=(",", ":"), ensure_ascii=False)
|
|
418
450
|
return None
|
|
419
451
|
return raw
|
|
420
452
|
|
|
@@ -474,6 +506,77 @@ def write_context(agent_id: str, session_id: str, tool_use_id: str) -> None:
|
|
|
474
506
|
secure_write_text(_CONTEXT_FILE, f"{agent_id}\n{session_id}\n{tool_use_id}\n")
|
|
475
507
|
|
|
476
508
|
|
|
509
|
+
def _optimization_state_path(agent_id: str, session_id: str, tool_use_id: str) -> str:
|
|
510
|
+
identity = "\0".join((agent_id, session_id, tool_use_id)).encode()
|
|
511
|
+
digest = hashlib.sha256(identity).hexdigest()
|
|
512
|
+
return os.path.join(_OPTIMIZATION_STATE_DIR, digest)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def _prune_optimization_states() -> None:
|
|
516
|
+
"""Bound abandoned per-call state when a host omits PostToolUse."""
|
|
517
|
+
try:
|
|
518
|
+
entries = os.scandir(_OPTIMIZATION_STATE_DIR)
|
|
519
|
+
except FileNotFoundError:
|
|
520
|
+
return
|
|
521
|
+
|
|
522
|
+
cutoff = time.time() - _OPTIMIZATION_STATE_TTL_SECONDS
|
|
523
|
+
live = []
|
|
524
|
+
with entries:
|
|
525
|
+
for entry in entries:
|
|
526
|
+
try:
|
|
527
|
+
if not entry.is_file(follow_symlinks=False):
|
|
528
|
+
continue
|
|
529
|
+
modified = entry.stat(follow_symlinks=False).st_mtime
|
|
530
|
+
if ".consuming." in entry.name and modified > cutoff:
|
|
531
|
+
continue
|
|
532
|
+
if modified <= cutoff:
|
|
533
|
+
os.unlink(entry.path)
|
|
534
|
+
else:
|
|
535
|
+
live.append((modified, entry.path))
|
|
536
|
+
except FileNotFoundError:
|
|
537
|
+
continue
|
|
538
|
+
|
|
539
|
+
excess = len(live) - _OPTIMIZATION_STATE_MAX_FILES + 1
|
|
540
|
+
if excess > 0:
|
|
541
|
+
for _, path in sorted(live)[:excess]:
|
|
542
|
+
try:
|
|
543
|
+
os.unlink(path)
|
|
544
|
+
except FileNotFoundError:
|
|
545
|
+
pass
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def mark_rtk_optimized(agent_id: str, session_id: str, tool_use_id: str) -> None:
|
|
549
|
+
"""Persist RTK ownership for one tool call before applying its rewrite."""
|
|
550
|
+
_prune_optimization_states()
|
|
551
|
+
secure_write_text(_optimization_state_path(agent_id, session_id, tool_use_id), "rtk\n")
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def consume_output_optimization(agent_id: str, session_id: str, tool_use_id: str) -> str:
|
|
555
|
+
"""Consume one tool call's optimization state for its final result."""
|
|
556
|
+
if not tool_use_id:
|
|
557
|
+
return "none"
|
|
558
|
+
path = _optimization_state_path(agent_id, session_id, tool_use_id)
|
|
559
|
+
consuming_path = f"{path}.consuming.{os.getpid()}"
|
|
560
|
+
try:
|
|
561
|
+
os.rename(path, consuming_path)
|
|
562
|
+
except FileNotFoundError:
|
|
563
|
+
return "none"
|
|
564
|
+
|
|
565
|
+
try:
|
|
566
|
+
flags = os.O_RDONLY
|
|
567
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
568
|
+
flags |= os.O_NOFOLLOW
|
|
569
|
+
fd = os.open(consuming_path, flags)
|
|
570
|
+
with os.fdopen(fd) as state_file:
|
|
571
|
+
state = state_file.read()
|
|
572
|
+
finally:
|
|
573
|
+
try:
|
|
574
|
+
os.unlink(consuming_path)
|
|
575
|
+
except FileNotFoundError:
|
|
576
|
+
pass
|
|
577
|
+
return "rtk" if state == "rtk\n" else "none"
|
|
578
|
+
|
|
579
|
+
|
|
477
580
|
def forward_stderr(proc: subprocess.CompletedProcess) -> None:
|
|
478
581
|
"""Forward subprocess stderr on failure (non-zero exit) via warn()."""
|
|
479
582
|
if proc.returncode != 0 and proc.stderr:
|
|
@@ -496,45 +599,109 @@ def run(args: list[str], input_data: str, timeout: int = 3) -> subprocess.Comple
|
|
|
496
599
|
return None
|
|
497
600
|
|
|
498
601
|
|
|
499
|
-
def
|
|
602
|
+
def _attribution(
|
|
603
|
+
agent_id: str,
|
|
604
|
+
session_id: str = "",
|
|
605
|
+
tool_use_id: str = "",
|
|
606
|
+
) -> dict:
|
|
607
|
+
"""Build the shared Protocol v2 attribution object."""
|
|
608
|
+
value = {"agent_id": agent_id}
|
|
609
|
+
if session_id:
|
|
610
|
+
value["session_id"] = session_id
|
|
611
|
+
if tool_use_id:
|
|
612
|
+
value["tool_use_id"] = tool_use_id
|
|
613
|
+
return value
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def build_before_model_request(
|
|
617
|
+
tools: list,
|
|
618
|
+
visible_context: object,
|
|
619
|
+
agent_id: str,
|
|
620
|
+
session_id: str = "",
|
|
621
|
+
tool_use_id: str = "",
|
|
622
|
+
) -> dict:
|
|
623
|
+
"""Build a Protocol v2 BeforeModel transport request."""
|
|
624
|
+
return {
|
|
625
|
+
"protocol_version": 2,
|
|
626
|
+
"operation": "before_model",
|
|
627
|
+
"attribution": _attribution(agent_id, session_id, tool_use_id),
|
|
628
|
+
"input": {
|
|
629
|
+
"tools": tools,
|
|
630
|
+
"visible_context": visible_context,
|
|
631
|
+
"capabilities": {
|
|
632
|
+
"replace_tools": True,
|
|
633
|
+
# A local CLI is not marker-scoped Agent authorization.
|
|
634
|
+
"recovery": {"kind": "none"},
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def build_pre_tool_request(
|
|
641
|
+
arguments: dict,
|
|
642
|
+
agent_id: str,
|
|
643
|
+
tool_name: str,
|
|
644
|
+
command_field: str,
|
|
645
|
+
session_id: str = "",
|
|
646
|
+
tool_use_id: str = "",
|
|
647
|
+
replace_arguments: bool = True,
|
|
648
|
+
block_and_suggest: bool = False,
|
|
649
|
+
) -> dict:
|
|
650
|
+
"""Build a Protocol v2 PreTool transport request."""
|
|
651
|
+
return {
|
|
652
|
+
"protocol_version": 2,
|
|
653
|
+
"operation": "pre_tool",
|
|
654
|
+
"attribution": _attribution(agent_id, session_id, tool_use_id),
|
|
655
|
+
"input": {
|
|
656
|
+
"tool_name": tool_name,
|
|
657
|
+
"arguments": arguments,
|
|
658
|
+
"command_field": command_field,
|
|
659
|
+
"capabilities": {
|
|
660
|
+
"replace_arguments": replace_arguments,
|
|
661
|
+
"block_and_suggest": block_and_suggest,
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def build_post_tool_request(
|
|
500
668
|
content: str,
|
|
501
669
|
agent_id: str,
|
|
502
|
-
|
|
670
|
+
tool_name: str,
|
|
671
|
+
status: str,
|
|
672
|
+
content_origin: str,
|
|
673
|
+
output_optimization: str,
|
|
674
|
+
*,
|
|
675
|
+
result_kind: str,
|
|
676
|
+
recovery: dict[str, str],
|
|
503
677
|
session_id: str = "",
|
|
504
678
|
tool_use_id: str = "",
|
|
505
|
-
tool_name: str = "",
|
|
506
679
|
replace_output: bool = False,
|
|
507
|
-
publish_retrieve_tool: bool = False,
|
|
508
680
|
replace_with_text: bool = False,
|
|
509
681
|
) -> dict:
|
|
510
|
-
"""Build a
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
"
|
|
523
|
-
|
|
524
|
-
|
|
682
|
+
"""Build a Protocol v2 PostTool transport request."""
|
|
683
|
+
return {
|
|
684
|
+
"protocol_version": 2,
|
|
685
|
+
"operation": "post_tool",
|
|
686
|
+
"attribution": _attribution(agent_id, session_id, tool_use_id),
|
|
687
|
+
"input": {
|
|
688
|
+
"result_kind": result_kind,
|
|
689
|
+
"tool_name": tool_name,
|
|
690
|
+
"content": content,
|
|
691
|
+
"status": status,
|
|
692
|
+
"content_origin": content_origin,
|
|
693
|
+
"output_optimization": output_optimization,
|
|
694
|
+
"capabilities": {
|
|
695
|
+
"replace_output": replace_output,
|
|
696
|
+
"recovery": recovery,
|
|
697
|
+
"replace_with_text": replace_with_text,
|
|
698
|
+
},
|
|
525
699
|
},
|
|
526
700
|
}
|
|
527
|
-
if session_id:
|
|
528
|
-
request["session_id"] = session_id
|
|
529
|
-
if tool_use_id:
|
|
530
|
-
request["tool_use_id"] = tool_use_id
|
|
531
|
-
if tool_name:
|
|
532
|
-
request["tool_name"] = tool_name
|
|
533
|
-
return request
|
|
534
701
|
|
|
535
702
|
|
|
536
703
|
def run_compress(
|
|
537
|
-
tokenless_bin: str, request: dict, timeout: int
|
|
704
|
+
tokenless_bin: str, request: dict, timeout: int, expected_operation: str
|
|
538
705
|
) -> dict | None:
|
|
539
706
|
"""Run ``tokenless compress`` on one request; None on any failure.
|
|
540
707
|
|
|
@@ -555,12 +722,19 @@ def run_compress(
|
|
|
555
722
|
if not isinstance(response, dict):
|
|
556
723
|
warn("tokenless compress returned malformed output")
|
|
557
724
|
return None
|
|
558
|
-
if response.get("protocol_version") !=
|
|
725
|
+
if response.get("protocol_version") != 2:
|
|
559
726
|
# Version-skewed binary: never trust a response whose contract
|
|
560
727
|
# this adapter does not speak.
|
|
561
728
|
warn("tokenless compress returned an unsupported protocol version")
|
|
562
729
|
return None
|
|
563
|
-
|
|
730
|
+
if response.get("operation") != expected_operation:
|
|
731
|
+
warn("tokenless compress returned a mismatched operation")
|
|
732
|
+
return None
|
|
733
|
+
result = response.get("result")
|
|
734
|
+
if not isinstance(result, dict):
|
|
735
|
+
warn("tokenless compress returned a malformed operation result")
|
|
736
|
+
return None
|
|
737
|
+
return result
|
|
564
738
|
|
|
565
739
|
|
|
566
740
|
def detect_cosh_ng_runtime() -> tuple | None:
|
|
@@ -614,7 +788,7 @@ def _agent_id_from_argv(argv: list[str] | None) -> str | None:
|
|
|
614
788
|
following = args[index + 1] if index + 1 < len(args) else ""
|
|
615
789
|
return following or None
|
|
616
790
|
if arg.startswith("--agent-id="):
|
|
617
|
-
return arg[len("--agent-id="):] or None
|
|
791
|
+
return arg[len("--agent-id=") :] or None
|
|
618
792
|
return None
|
|
619
793
|
|
|
620
794
|
|
|
@@ -642,11 +816,7 @@ def resolve_agent_id(default: str = "unknown", argv: list[str] | None = None) ->
|
|
|
642
816
|
"""
|
|
643
817
|
if detect_cosh_ng_runtime() is not None:
|
|
644
818
|
return "cosh-ng"
|
|
645
|
-
return (
|
|
646
|
-
_agent_id_from_argv(argv)
|
|
647
|
-
or os.environ.get("TOKENLESS_AGENT_ID")
|
|
648
|
-
or default
|
|
649
|
-
)
|
|
819
|
+
return _agent_id_from_argv(argv) or os.environ.get("TOKENLESS_AGENT_ID") or default
|
|
650
820
|
|
|
651
821
|
|
|
652
822
|
def parse_version(version_str: str) -> tuple | None:
|