coze_lab 0.1.15 → 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 CHANGED
@@ -61,6 +61,11 @@ For cloud Codex with `--cloud --agent-id=<agentId>`, Codex hooks are written to
61
61
  does not already exist, so callers do not need to pass `--codex-home` for the
62
62
  standard coze-bridge layout.
63
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
+
64
69
  ## Token lifecycle
65
70
 
66
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,7 @@ 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 };
4619
4621
  }
4620
4622
 
4621
4623
  function resolveCodexHome(args) {
@@ -5325,6 +5327,7 @@ async function main() {
5325
5327
  summaryLines.push(`Hook script: ${written.hookScript || '~/.codex/hooks/cozeloop_hook.py'}`);
5326
5328
  summaryLines.push(`Config: ${written.hooksJson || '~/.codex/hooks.json'}`);
5327
5329
  summaryLines.push(`Credentials: ${written.envFile || '~/.codex/hooks/cozeloop.env'}`);
5330
+ if (written.logFile) summaryLines.push(`Hook log: ${written.logFile}`);
5328
5331
  } else {
5329
5332
  summaryLines.push(`Config: ~/.openclaw/openclaw.json`);
5330
5333
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coze_lab",
3
- "version": "0.1.15",
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",
@@ -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
@@ -255,8 +258,27 @@ else:
255
258
  DEBUG = os.environ.get("CC_COZELOOP_DEBUG", "").lower() == "true"
256
259
 
257
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
+
258
279
  def debug_log(message: str):
259
280
  """Print debug message if debug mode is enabled."""
281
+ hook_log(f"DEBUG {message}")
260
282
  if DEBUG:
261
283
  print(f"[COZELOOP_HOOK_DEBUG] {datetime.now().isoformat()} - {message}", file=sys.stderr)
262
284
 
@@ -690,12 +712,15 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
690
712
  token = get_fresh_token()
691
713
  if token:
692
714
  os.environ["COZELOOP_API_TOKEN"] = token
715
+ hook_log(f"token resolved prefix={token[:12]}...")
693
716
  print(f"[CozeLoop] Token 获取成功 ({token[:12]}...)", file=sys.stderr)
694
717
  else:
718
+ hook_log("token missing")
695
719
  print("[CozeLoop] 警告: 未找到有效 Token,上报可能失败", file=sys.stderr)
696
720
  creds = _load_credentials()
697
721
  workspace_id = (creds or {}).get("workspace_id") or os.environ.get("COZELOOP_WORKSPACE_ID", "") or _DEFAULT_WORKSPACE_ID
698
722
  os.environ["COZELOOP_WORKSPACE_ID"] = workspace_id
723
+ hook_log(f"workspace_id={workspace_id}")
699
724
  client_kwargs = {
700
725
  "ultra_large_report": True,
701
726
  "upload_timeout": 120,
@@ -1018,13 +1043,16 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
1018
1043
  debug_log(f"Error processing turn {i}: {e}")
1019
1044
  continue
1020
1045
 
1046
+ hook_log(f"processed turns={len(turns)} session_id={session_id}")
1021
1047
  debug_log(f"Successfully processed {len(turns)} turn(s) for session {session_id}")
1022
1048
 
1023
1049
  except Exception as e:
1050
+ hook_log(f"send exception={repr(e)}")
1024
1051
  debug_log(f"An error occurred while sending traces to CozeLoop: {e}")
1025
1052
  return None
1026
1053
  finally:
1027
1054
  client.close()
1055
+ hook_log("client closed")
1028
1056
  debug_log("CozeLoop client closed.")
1029
1057
 
1030
1058
  return ctx
@@ -1035,10 +1063,12 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
1035
1063
  def main():
1036
1064
  """Main entry point for the Codex CozeLoop hook."""
1037
1065
  print("[CozeLoop] Hook triggered (Codex).", file=sys.stderr)
1066
+ hook_log("hook triggered")
1038
1067
  debug_log("Codex CozeLoop hook started.")
1039
1068
 
1040
1069
  # Check if tracing is enabled
1041
1070
  if os.environ.get("TRACE_TO_COZELOOP", "").lower() == "false":
1071
+ hook_log("skip trace disabled")
1042
1072
  debug_log("TRACE_TO_COZELOOP is set to 'false', skipping")
1043
1073
  return
1044
1074
 
@@ -1046,10 +1076,12 @@ def main():
1046
1076
  try:
1047
1077
  raw_input = sys.stdin.read().strip()
1048
1078
  if not raw_input:
1079
+ hook_log("skip no stdin")
1049
1080
  debug_log("No input received from stdin")
1050
1081
  return
1051
1082
  hook_input = json.loads(raw_input)
1052
1083
  except Exception as e:
1084
+ hook_log(f"skip stdin parse error={repr(e)}")
1053
1085
  debug_log(f"Error reading hook input from stdin: {e}")
1054
1086
  return
1055
1087
 
@@ -1058,10 +1090,12 @@ def main():
1058
1090
  # Get transcript path
1059
1091
  transcript_path = hook_input.get("transcript_path")
1060
1092
  if not transcript_path:
1093
+ hook_log("skip missing transcript_path")
1061
1094
  debug_log("No transcript_path in hook input")
1062
1095
  return
1063
1096
 
1064
1097
  if not os.path.exists(transcript_path):
1098
+ hook_log(f"skip transcript not found path={transcript_path}")
1065
1099
  debug_log(f"Transcript file not found: {transcript_path}")
1066
1100
  return
1067
1101
 
@@ -1073,9 +1107,11 @@ def main():
1073
1107
  entries = read_rollout_messages(transcript_path, state["last_processed_line"])
1074
1108
 
1075
1109
  if not entries:
1110
+ hook_log(f"skip no new entries transcript={transcript_path}")
1076
1111
  debug_log("No new entries to process")
1077
1112
  return
1078
1113
 
1114
+ hook_log(f"read entries={len(entries)} from_line={state['last_processed_line']} transcript={transcript_path}")
1079
1115
  debug_log(f"Read {len(entries)} new entries from line {state['last_processed_line']}")
1080
1116
 
1081
1117
  # Parse session identity
@@ -1125,6 +1161,7 @@ def main():
1125
1161
  last_line = max(e.get("_line_number", 0) for e in entries) + 1
1126
1162
  state["last_processed_line"] = last_line
1127
1163
  save_state(state_file, state)
1164
+ hook_log(f"subagent saved session_id={session_id} turns={len(turns[-1:])} last_line={last_line}")
1128
1165
  debug_log("Subagent data saved, hook completed")
1129
1166
  return
1130
1167
 
@@ -1135,6 +1172,7 @@ def main():
1135
1172
  for t in turns
1136
1173
  )
1137
1174
  if not has_coze_ctx:
1175
+ hook_log(f"skip no coze-context turns={len(turns)} session_id={session_id}")
1138
1176
  debug_log("No coze-context found in any turn, skipping upload.")
1139
1177
  return
1140
1178
  history_context = state.get("conversation_history", [])
@@ -1147,12 +1185,16 @@ def main():
1147
1185
  state["last_processed_line"] = last_line
1148
1186
  state["conversation_history"] = updated_history
1149
1187
  save_state(state_file, state)
1188
+ hook_log(f"state advanced last_line={last_line} session_id={session_id}")
1150
1189
  debug_log(f"State updated, last processed line: {last_line}")
1151
1190
  else:
1191
+ hook_log(f"send failed state not advanced session_id={session_id}")
1152
1192
  debug_log("Send failed, state not advanced")
1153
1193
  else:
1194
+ hook_log(f"skip no turns session_id={session_id}")
1154
1195
  debug_log("No turns to send")
1155
1196
 
1197
+ hook_log("hook completed")
1156
1198
  debug_log("Codex CozeLoop hook completed.")
1157
1199
 
1158
1200