coze_lab 0.1.14 → 0.1.16
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 +16 -0
- package/index.js +16 -3
- package/package.json +7 -2
- package/scripts/claude-code/cozeloop_hook.py +41 -4
- package/scripts/codex/cozeloop_hook.py +83 -4
package/README.md
CHANGED
|
@@ -8,6 +8,9 @@ Configure local AI agents (Claude Code, Codex, OpenClaw) to report traces to Coz
|
|
|
8
8
|
# First-time setup — triggers browser OAuth authorization
|
|
9
9
|
npx coze_lab --agent=<type>
|
|
10
10
|
|
|
11
|
+
# Cloud setup for a managed agent
|
|
12
|
+
npx coze_lab --cloud --agent-id=<agentId>
|
|
13
|
+
|
|
11
14
|
# Auth-only commands (no agent configuration)
|
|
12
15
|
npx coze_lab --login # Device Code login only
|
|
13
16
|
npx coze_lab --status # Show current authorization status
|
|
@@ -20,6 +23,9 @@ npx coze_lab --logout # Clear cached credentials
|
|
|
20
23
|
| Parameter | Required | Values / Effect |
|
|
21
24
|
|-----------|----------|-----------------|
|
|
22
25
|
| `--agent` | ✓ (for setup) | `claude-code`, `codex`, `openclaw` |
|
|
26
|
+
| `--agent-id` | — | Resolve `~/.coze/agents/<agentId>/config.json` and write per-agent config |
|
|
27
|
+
| `--cloud` | — | Cloud mode: read token from env and emit `COZE_LAB_RESULT=...` |
|
|
28
|
+
| `--codex-home` | — | Override Codex config home for non-cloud/custom runs |
|
|
23
29
|
| `--login` | — | Run the Device Code login flow only |
|
|
24
30
|
| `--status` | — | Print local token status (valid / expiring / expired) |
|
|
25
31
|
| `--refresh` | — | Force-refresh the access token via `refresh_token` |
|
|
@@ -50,6 +56,16 @@ npx coze_lab --logout # Clear cached credentials
|
|
|
50
56
|
| `codex` | `~/.codex/hooks/cozeloop_hook.py` | `~/.codex/hooks.json` | `~/.codex/hooks/cozeloop.env` |
|
|
51
57
|
| `openclaw` | — (Node.js plugin) | `~/.openclaw/openclaw.json` | inline in config |
|
|
52
58
|
|
|
59
|
+
For cloud Codex with `--cloud --agent-id=<agentId>`, Codex hooks are written to
|
|
60
|
+
`~/.coze/agents/<agentId>/codex-home` by default. The directory is created if it
|
|
61
|
+
does not already exist, so callers do not need to pass `--codex-home` for the
|
|
62
|
+
standard coze-bridge layout.
|
|
63
|
+
|
|
64
|
+
Codex hook diagnostics are appended to `hooks/cozeloop.log` under the same
|
|
65
|
+
Codex home. For cloud Codex, check
|
|
66
|
+
`~/.coze/agents/<agentId>/codex-home/hooks/cozeloop.log`. If that file is not
|
|
67
|
+
created after a new Codex turn, Codex did not load or execute the hook.
|
|
68
|
+
|
|
53
69
|
## Token lifecycle
|
|
54
70
|
|
|
55
71
|
OAuth tokens are stored in `~/.cozeloop/credentials.json` (mode 600).
|
package/index.js
CHANGED
|
@@ -4554,6 +4554,7 @@ function writeCodexHook(token, workspaceId, pythonCmd, codexHome, cloud) {
|
|
|
4554
4554
|
const hooksDir = path.join(home, 'hooks');
|
|
4555
4555
|
const hookScript = path.join(hooksDir, 'cozeloop_hook.py');
|
|
4556
4556
|
const envFile = path.join(hooksDir, 'cozeloop.env');
|
|
4557
|
+
const logFile = path.join(hooksDir, 'cozeloop.log');
|
|
4557
4558
|
const hooksJson = path.join(home, 'hooks.json');
|
|
4558
4559
|
|
|
4559
4560
|
// 1. Write Python hook scripts (trace + refresh)
|
|
@@ -4571,6 +4572,7 @@ function writeCodexHook(token, workspaceId, pythonCmd, codexHome, cloud) {
|
|
|
4571
4572
|
if (!cloud) {
|
|
4572
4573
|
envLines.push(`COZELOOP_API_TOKEN=${token}`);
|
|
4573
4574
|
}
|
|
4575
|
+
envLines.push(`COZELOOP_HOOK_LOG=${logFile}`);
|
|
4574
4576
|
envLines.push('TRACE_TO_COZELOOP=true');
|
|
4575
4577
|
// PPE 泳道:cozeloop SDK 读这两个环境变量,自动注入 x-tt-env / x-use-ppe header
|
|
4576
4578
|
envLines.push(`x_tt_env=${PPE_TT_ENV}`);
|
|
@@ -4615,7 +4617,14 @@ function writeCodexHook(token, workspaceId, pythonCmd, codexHome, cloud) {
|
|
|
4615
4617
|
ok(`Hook registered in ${hooksJson}`);
|
|
4616
4618
|
warn('Codex hook trust: 首次启动 Codex 会提示 "Hooks need review"。在该提示按 t(Trust all and continue),或在会话内运行 /hooks 后按 t,即可一次性信任全部 hook 启用 trace 上报。');
|
|
4617
4619
|
|
|
4618
|
-
return { hookScript, envFile, hooksJson };
|
|
4620
|
+
return { hookScript, envFile, hooksJson, logFile };
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4623
|
+
function resolveCodexHome(args) {
|
|
4624
|
+
if (args.cloud && args.agentId) {
|
|
4625
|
+
return path.join(os.homedir(), '.coze', 'agents', args.agentId, 'codex-home');
|
|
4626
|
+
}
|
|
4627
|
+
return args['codex-home'] || process.env.CODEX_HOME || undefined;
|
|
4619
4628
|
}
|
|
4620
4629
|
|
|
4621
4630
|
// writeOpenClawHook 配置 OpenClaw 的 cozeloop-trace 插件(全局装在 ~/.openclaw)。
|
|
@@ -5290,8 +5299,11 @@ async function main() {
|
|
|
5290
5299
|
}
|
|
5291
5300
|
written = writeClaudeCodeHook(token, WORKSPACE_ID, pythonCmd, args.agentId ? agentWorkspace : undefined, args.cloud);
|
|
5292
5301
|
} else if (agent === 'codex') {
|
|
5293
|
-
|
|
5294
|
-
|
|
5302
|
+
const codexHome = resolveCodexHome(args);
|
|
5303
|
+
if (args.cloud && args.agentId && codexHome && !fs.existsSync(codexHome)) {
|
|
5304
|
+
ensureDir(codexHome);
|
|
5305
|
+
info(`已创建云端 Codex 配置目录: ${codexHome}`);
|
|
5306
|
+
}
|
|
5295
5307
|
if (codexHome) info(`Codex 配置目录: ${codexHome}`);
|
|
5296
5308
|
written = writeCodexHook(token, WORKSPACE_ID, pythonCmd, codexHome, args.cloud);
|
|
5297
5309
|
} else {
|
|
@@ -5315,6 +5327,7 @@ async function main() {
|
|
|
5315
5327
|
summaryLines.push(`Hook script: ${written.hookScript || '~/.codex/hooks/cozeloop_hook.py'}`);
|
|
5316
5328
|
summaryLines.push(`Config: ${written.hooksJson || '~/.codex/hooks.json'}`);
|
|
5317
5329
|
summaryLines.push(`Credentials: ${written.envFile || '~/.codex/hooks/cozeloop.env'}`);
|
|
5330
|
+
if (written.logFile) summaryLines.push(`Hook log: ${written.logFile}`);
|
|
5318
5331
|
} else {
|
|
5319
5332
|
summaryLines.push(`Config: ~/.openclaw/openclaw.json`);
|
|
5320
5333
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coze_lab",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Configure local AI agents (Claude Code, Codex, OpenClaw) to report traces to CozeLoop",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cozeloop",
|
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"index.js",
|
|
19
|
-
"scripts/"
|
|
19
|
+
"scripts/claude-code/cozeloop_hook.py",
|
|
20
|
+
"scripts/codex/cozeloop_hook.py",
|
|
21
|
+
"scripts/shared/cozeloop_refresh.py",
|
|
22
|
+
"scripts/openclaw/dist/",
|
|
23
|
+
"scripts/openclaw/openclaw.plugin.json",
|
|
24
|
+
"scripts/openclaw/package.json"
|
|
20
25
|
],
|
|
21
26
|
"engines": {
|
|
22
27
|
"node": ">=18"
|
|
@@ -29,15 +29,53 @@ from pathlib import Path
|
|
|
29
29
|
from typing import Optional, List, Dict, Any
|
|
30
30
|
|
|
31
31
|
# --- SDK Import ---
|
|
32
|
-
|
|
32
|
+
def _ensure_cozeloop_sdk():
|
|
33
|
+
try:
|
|
34
|
+
import cozeloop # noqa: F401
|
|
35
|
+
return True
|
|
36
|
+
except ImportError:
|
|
37
|
+
pass
|
|
38
|
+
import subprocess
|
|
39
|
+
import importlib
|
|
40
|
+
import site
|
|
41
|
+
attempts = (
|
|
42
|
+
["--quiet", "--disable-pip-version-check", "cozeloop"],
|
|
43
|
+
["--quiet", "--disable-pip-version-check", "--break-system-packages", "cozeloop"],
|
|
44
|
+
["--quiet", "--disable-pip-version-check", "--break-system-packages", "--user", "cozeloop"],
|
|
45
|
+
)
|
|
46
|
+
for extra in attempts:
|
|
47
|
+
try:
|
|
48
|
+
subprocess.run(
|
|
49
|
+
[sys.executable, "-m", "pip", "install", *extra],
|
|
50
|
+
timeout=180, check=True,
|
|
51
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
52
|
+
)
|
|
53
|
+
except Exception:
|
|
54
|
+
continue
|
|
55
|
+
try:
|
|
56
|
+
importlib.reload(site)
|
|
57
|
+
user_site = site.getusersitepackages()
|
|
58
|
+
for p in ([user_site] if isinstance(user_site, str) else list(user_site)):
|
|
59
|
+
if p and p not in sys.path:
|
|
60
|
+
sys.path.insert(0, p)
|
|
61
|
+
importlib.invalidate_caches()
|
|
62
|
+
import cozeloop # noqa: F401
|
|
63
|
+
print("[CozeLoop] cozeloop SDK auto-installed at runtime.", file=sys.stderr)
|
|
64
|
+
return True
|
|
65
|
+
except ImportError:
|
|
66
|
+
continue
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if _ensure_cozeloop_sdk():
|
|
33
71
|
import cozeloop
|
|
34
72
|
from cozeloop.spec.tracespec import (
|
|
35
73
|
Runtime, ModelInput, ModelMessage, ModelToolChoice,
|
|
36
74
|
ModelOutput, ModelChoice, ModelToolCall, ModelToolCallFunction,
|
|
37
75
|
ModelMessagePart, ModelMessagePartType
|
|
38
76
|
)
|
|
39
|
-
|
|
40
|
-
print("Error: cozeloop SDK not found
|
|
77
|
+
else:
|
|
78
|
+
print("Error: cozeloop SDK not found and auto-install failed. Try: pip install cozeloop", file=sys.stderr)
|
|
41
79
|
sys.exit(1)
|
|
42
80
|
|
|
43
81
|
# --- Configuration ---
|
|
@@ -1412,4 +1450,3 @@ if __name__ == "__main__":
|
|
|
1412
1450
|
main()
|
|
1413
1451
|
|
|
1414
1452
|
|
|
1415
|
-
|
|
@@ -130,12 +130,15 @@ def _make_finish_event_processor():
|
|
|
130
130
|
def _processor(info):
|
|
131
131
|
try:
|
|
132
132
|
if not getattr(info, "is_event_fail", False):
|
|
133
|
+
hook_log("upload success")
|
|
133
134
|
return
|
|
134
135
|
detail = getattr(info, "detail_msg", "") or ""
|
|
135
136
|
logid = _extract_logid(detail)
|
|
136
137
|
if logid:
|
|
138
|
+
hook_log(f"upload failed logid={logid} detail={detail[:500]}")
|
|
137
139
|
print(f"[CozeLoop] 上报失败 logid={logid} (可用 bytedcli log get-logid-log {logid} 排查)", file=sys.stderr)
|
|
138
140
|
else:
|
|
141
|
+
hook_log(f"upload failed detail={detail[:500]}")
|
|
139
142
|
print(f"[CozeLoop] 上报失败: {detail[:300]}", file=sys.stderr)
|
|
140
143
|
except Exception:
|
|
141
144
|
pass
|
|
@@ -202,23 +205,80 @@ def get_fresh_token():
|
|
|
202
205
|
# -------------------------------------------------------------------------
|
|
203
206
|
|
|
204
207
|
# --- SDK Import ---
|
|
205
|
-
|
|
208
|
+
def _ensure_cozeloop_sdk():
|
|
209
|
+
try:
|
|
210
|
+
import cozeloop # noqa: F401
|
|
211
|
+
return True
|
|
212
|
+
except ImportError:
|
|
213
|
+
pass
|
|
214
|
+
import subprocess
|
|
215
|
+
import importlib
|
|
216
|
+
import site
|
|
217
|
+
attempts = (
|
|
218
|
+
["--quiet", "--disable-pip-version-check", "cozeloop"],
|
|
219
|
+
["--quiet", "--disable-pip-version-check", "--break-system-packages", "cozeloop"],
|
|
220
|
+
["--quiet", "--disable-pip-version-check", "--break-system-packages", "--user", "cozeloop"],
|
|
221
|
+
)
|
|
222
|
+
for extra in attempts:
|
|
223
|
+
try:
|
|
224
|
+
subprocess.run(
|
|
225
|
+
[sys.executable, "-m", "pip", "install", *extra],
|
|
226
|
+
timeout=180, check=True,
|
|
227
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
228
|
+
)
|
|
229
|
+
except Exception:
|
|
230
|
+
continue
|
|
231
|
+
try:
|
|
232
|
+
importlib.reload(site)
|
|
233
|
+
user_site = site.getusersitepackages()
|
|
234
|
+
for p in ([user_site] if isinstance(user_site, str) else list(user_site)):
|
|
235
|
+
if p and p not in sys.path:
|
|
236
|
+
sys.path.insert(0, p)
|
|
237
|
+
importlib.invalidate_caches()
|
|
238
|
+
import cozeloop # noqa: F401
|
|
239
|
+
print("[CozeLoop] cozeloop SDK auto-installed at runtime.", file=sys.stderr)
|
|
240
|
+
return True
|
|
241
|
+
except ImportError:
|
|
242
|
+
continue
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
if _ensure_cozeloop_sdk():
|
|
206
247
|
import cozeloop
|
|
207
248
|
from cozeloop.spec.tracespec import (
|
|
208
249
|
Runtime, ModelInput, ModelMessage, ModelToolChoice,
|
|
209
250
|
ModelOutput, ModelChoice, ModelToolCall, ModelToolCallFunction,
|
|
210
251
|
ModelMessagePart, ModelMessagePartType
|
|
211
252
|
)
|
|
212
|
-
|
|
213
|
-
print("Error: cozeloop SDK not found
|
|
253
|
+
else:
|
|
254
|
+
print("Error: cozeloop SDK not found and auto-install failed. Try: pip install cozeloop", file=sys.stderr)
|
|
214
255
|
sys.exit(1)
|
|
215
256
|
|
|
216
257
|
# --- Configuration ---
|
|
217
258
|
DEBUG = os.environ.get("CC_COZELOOP_DEBUG", "").lower() == "true"
|
|
218
259
|
|
|
219
260
|
|
|
261
|
+
def _log_file_path() -> str:
|
|
262
|
+
return os.environ.get("COZELOOP_HOOK_LOG", "").strip()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def hook_log(message: str):
|
|
266
|
+
"""Append one diagnostic line to the hook log, if configured."""
|
|
267
|
+
log_path = _log_file_path()
|
|
268
|
+
if not log_path:
|
|
269
|
+
return
|
|
270
|
+
try:
|
|
271
|
+
p = Path(log_path).expanduser()
|
|
272
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
273
|
+
with p.open("a", encoding="utf-8") as f:
|
|
274
|
+
f.write(f"{datetime.now().isoformat()} {message}\n")
|
|
275
|
+
except Exception:
|
|
276
|
+
pass
|
|
277
|
+
|
|
278
|
+
|
|
220
279
|
def debug_log(message: str):
|
|
221
280
|
"""Print debug message if debug mode is enabled."""
|
|
281
|
+
hook_log(f"DEBUG {message}")
|
|
222
282
|
if DEBUG:
|
|
223
283
|
print(f"[COZELOOP_HOOK_DEBUG] {datetime.now().isoformat()} - {message}", file=sys.stderr)
|
|
224
284
|
|
|
@@ -652,12 +712,15 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
652
712
|
token = get_fresh_token()
|
|
653
713
|
if token:
|
|
654
714
|
os.environ["COZELOOP_API_TOKEN"] = token
|
|
715
|
+
hook_log(f"token resolved prefix={token[:12]}...")
|
|
655
716
|
print(f"[CozeLoop] Token 获取成功 ({token[:12]}...)", file=sys.stderr)
|
|
656
717
|
else:
|
|
718
|
+
hook_log("token missing")
|
|
657
719
|
print("[CozeLoop] 警告: 未找到有效 Token,上报可能失败", file=sys.stderr)
|
|
658
720
|
creds = _load_credentials()
|
|
659
721
|
workspace_id = (creds or {}).get("workspace_id") or os.environ.get("COZELOOP_WORKSPACE_ID", "") or _DEFAULT_WORKSPACE_ID
|
|
660
722
|
os.environ["COZELOOP_WORKSPACE_ID"] = workspace_id
|
|
723
|
+
hook_log(f"workspace_id={workspace_id}")
|
|
661
724
|
client_kwargs = {
|
|
662
725
|
"ultra_large_report": True,
|
|
663
726
|
"upload_timeout": 120,
|
|
@@ -980,13 +1043,16 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
980
1043
|
debug_log(f"Error processing turn {i}: {e}")
|
|
981
1044
|
continue
|
|
982
1045
|
|
|
1046
|
+
hook_log(f"processed turns={len(turns)} session_id={session_id}")
|
|
983
1047
|
debug_log(f"Successfully processed {len(turns)} turn(s) for session {session_id}")
|
|
984
1048
|
|
|
985
1049
|
except Exception as e:
|
|
1050
|
+
hook_log(f"send exception={repr(e)}")
|
|
986
1051
|
debug_log(f"An error occurred while sending traces to CozeLoop: {e}")
|
|
987
1052
|
return None
|
|
988
1053
|
finally:
|
|
989
1054
|
client.close()
|
|
1055
|
+
hook_log("client closed")
|
|
990
1056
|
debug_log("CozeLoop client closed.")
|
|
991
1057
|
|
|
992
1058
|
return ctx
|
|
@@ -997,10 +1063,12 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
997
1063
|
def main():
|
|
998
1064
|
"""Main entry point for the Codex CozeLoop hook."""
|
|
999
1065
|
print("[CozeLoop] Hook triggered (Codex).", file=sys.stderr)
|
|
1066
|
+
hook_log("hook triggered")
|
|
1000
1067
|
debug_log("Codex CozeLoop hook started.")
|
|
1001
1068
|
|
|
1002
1069
|
# Check if tracing is enabled
|
|
1003
1070
|
if os.environ.get("TRACE_TO_COZELOOP", "").lower() == "false":
|
|
1071
|
+
hook_log("skip trace disabled")
|
|
1004
1072
|
debug_log("TRACE_TO_COZELOOP is set to 'false', skipping")
|
|
1005
1073
|
return
|
|
1006
1074
|
|
|
@@ -1008,10 +1076,12 @@ def main():
|
|
|
1008
1076
|
try:
|
|
1009
1077
|
raw_input = sys.stdin.read().strip()
|
|
1010
1078
|
if not raw_input:
|
|
1079
|
+
hook_log("skip no stdin")
|
|
1011
1080
|
debug_log("No input received from stdin")
|
|
1012
1081
|
return
|
|
1013
1082
|
hook_input = json.loads(raw_input)
|
|
1014
1083
|
except Exception as e:
|
|
1084
|
+
hook_log(f"skip stdin parse error={repr(e)}")
|
|
1015
1085
|
debug_log(f"Error reading hook input from stdin: {e}")
|
|
1016
1086
|
return
|
|
1017
1087
|
|
|
@@ -1020,10 +1090,12 @@ def main():
|
|
|
1020
1090
|
# Get transcript path
|
|
1021
1091
|
transcript_path = hook_input.get("transcript_path")
|
|
1022
1092
|
if not transcript_path:
|
|
1093
|
+
hook_log("skip missing transcript_path")
|
|
1023
1094
|
debug_log("No transcript_path in hook input")
|
|
1024
1095
|
return
|
|
1025
1096
|
|
|
1026
1097
|
if not os.path.exists(transcript_path):
|
|
1098
|
+
hook_log(f"skip transcript not found path={transcript_path}")
|
|
1027
1099
|
debug_log(f"Transcript file not found: {transcript_path}")
|
|
1028
1100
|
return
|
|
1029
1101
|
|
|
@@ -1035,9 +1107,11 @@ def main():
|
|
|
1035
1107
|
entries = read_rollout_messages(transcript_path, state["last_processed_line"])
|
|
1036
1108
|
|
|
1037
1109
|
if not entries:
|
|
1110
|
+
hook_log(f"skip no new entries transcript={transcript_path}")
|
|
1038
1111
|
debug_log("No new entries to process")
|
|
1039
1112
|
return
|
|
1040
1113
|
|
|
1114
|
+
hook_log(f"read entries={len(entries)} from_line={state['last_processed_line']} transcript={transcript_path}")
|
|
1041
1115
|
debug_log(f"Read {len(entries)} new entries from line {state['last_processed_line']}")
|
|
1042
1116
|
|
|
1043
1117
|
# Parse session identity
|
|
@@ -1087,6 +1161,7 @@ def main():
|
|
|
1087
1161
|
last_line = max(e.get("_line_number", 0) for e in entries) + 1
|
|
1088
1162
|
state["last_processed_line"] = last_line
|
|
1089
1163
|
save_state(state_file, state)
|
|
1164
|
+
hook_log(f"subagent saved session_id={session_id} turns={len(turns[-1:])} last_line={last_line}")
|
|
1090
1165
|
debug_log("Subagent data saved, hook completed")
|
|
1091
1166
|
return
|
|
1092
1167
|
|
|
@@ -1097,6 +1172,7 @@ def main():
|
|
|
1097
1172
|
for t in turns
|
|
1098
1173
|
)
|
|
1099
1174
|
if not has_coze_ctx:
|
|
1175
|
+
hook_log(f"skip no coze-context turns={len(turns)} session_id={session_id}")
|
|
1100
1176
|
debug_log("No coze-context found in any turn, skipping upload.")
|
|
1101
1177
|
return
|
|
1102
1178
|
history_context = state.get("conversation_history", [])
|
|
@@ -1109,15 +1185,18 @@ def main():
|
|
|
1109
1185
|
state["last_processed_line"] = last_line
|
|
1110
1186
|
state["conversation_history"] = updated_history
|
|
1111
1187
|
save_state(state_file, state)
|
|
1188
|
+
hook_log(f"state advanced last_line={last_line} session_id={session_id}")
|
|
1112
1189
|
debug_log(f"State updated, last processed line: {last_line}")
|
|
1113
1190
|
else:
|
|
1191
|
+
hook_log(f"send failed state not advanced session_id={session_id}")
|
|
1114
1192
|
debug_log("Send failed, state not advanced")
|
|
1115
1193
|
else:
|
|
1194
|
+
hook_log(f"skip no turns session_id={session_id}")
|
|
1116
1195
|
debug_log("No turns to send")
|
|
1117
1196
|
|
|
1197
|
+
hook_log("hook completed")
|
|
1118
1198
|
debug_log("Codex CozeLoop hook completed.")
|
|
1119
1199
|
|
|
1120
1200
|
|
|
1121
1201
|
if __name__ == "__main__":
|
|
1122
1202
|
main()
|
|
1123
|
-
|