coze_lab 0.1.16 → 0.1.17
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/index.js +1 -0
- package/package.json +1 -1
- package/scripts/codex/cozeloop_hook.py +117 -9
package/index.js
CHANGED
|
@@ -4572,6 +4572,7 @@ function writeCodexHook(token, workspaceId, pythonCmd, codexHome, cloud) {
|
|
|
4572
4572
|
if (!cloud) {
|
|
4573
4573
|
envLines.push(`COZELOOP_API_TOKEN=${token}`);
|
|
4574
4574
|
}
|
|
4575
|
+
envLines.push(`CODEX_HOME=${home}`);
|
|
4575
4576
|
envLines.push(`COZELOOP_HOOK_LOG=${logFile}`);
|
|
4576
4577
|
envLines.push('TRACE_TO_COZELOOP=true');
|
|
4577
4578
|
// PPE 泳道:cozeloop SDK 读这两个环境变量,自动注入 x-tt-env / x-use-ppe header
|
package/package.json
CHANGED
|
@@ -366,6 +366,101 @@ def read_rollout_messages(transcript_path: str, start_line: int = 0) -> List[Dic
|
|
|
366
366
|
return entries
|
|
367
367
|
|
|
368
368
|
|
|
369
|
+
def _add_unique_path(paths: List[Path], p: Optional[Path]):
|
|
370
|
+
if not p:
|
|
371
|
+
return
|
|
372
|
+
try:
|
|
373
|
+
resolved = p.expanduser().resolve()
|
|
374
|
+
except Exception:
|
|
375
|
+
return
|
|
376
|
+
if resolved not in paths:
|
|
377
|
+
paths.append(resolved)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def _candidate_codex_homes() -> List[Path]:
|
|
381
|
+
homes: List[Path] = []
|
|
382
|
+
_add_unique_path(homes, Path(os.environ["CODEX_HOME"]) if os.environ.get("CODEX_HOME") else None)
|
|
383
|
+
|
|
384
|
+
log_path = _log_file_path()
|
|
385
|
+
if log_path:
|
|
386
|
+
try:
|
|
387
|
+
log_parent = Path(log_path).expanduser().resolve().parent
|
|
388
|
+
if log_parent.name == "hooks":
|
|
389
|
+
_add_unique_path(homes, log_parent.parent)
|
|
390
|
+
except Exception:
|
|
391
|
+
pass
|
|
392
|
+
|
|
393
|
+
_add_unique_path(homes, Path.home() / ".codex")
|
|
394
|
+
|
|
395
|
+
agents_root = Path.home() / ".coze" / "agents"
|
|
396
|
+
try:
|
|
397
|
+
if agents_root.exists():
|
|
398
|
+
for child in agents_root.iterdir():
|
|
399
|
+
_add_unique_path(homes, child / "codex-home")
|
|
400
|
+
except Exception:
|
|
401
|
+
pass
|
|
402
|
+
|
|
403
|
+
return homes
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _latest_file(paths: List[Path]) -> Optional[Path]:
|
|
407
|
+
existing = []
|
|
408
|
+
for p in paths:
|
|
409
|
+
try:
|
|
410
|
+
if p.is_file():
|
|
411
|
+
existing.append(p)
|
|
412
|
+
except Exception:
|
|
413
|
+
pass
|
|
414
|
+
if not existing:
|
|
415
|
+
return None
|
|
416
|
+
return max(existing, key=lambda p: p.stat().st_mtime)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def find_latest_transcript() -> Optional[str]:
|
|
420
|
+
"""Best-effort fallback when Codex does not pass hook stdin."""
|
|
421
|
+
candidates: List[Path] = []
|
|
422
|
+
for codex_home in _candidate_codex_homes():
|
|
423
|
+
sessions_dir = codex_home / "sessions"
|
|
424
|
+
if not sessions_dir.exists():
|
|
425
|
+
continue
|
|
426
|
+
try:
|
|
427
|
+
candidates.extend(sessions_dir.rglob("rollout-*.jsonl"))
|
|
428
|
+
except Exception as e:
|
|
429
|
+
hook_log(f"fallback scan failed dir={sessions_dir} error={repr(e)}")
|
|
430
|
+
|
|
431
|
+
latest = _latest_file(candidates)
|
|
432
|
+
if latest:
|
|
433
|
+
return str(latest)
|
|
434
|
+
|
|
435
|
+
for codex_home in _candidate_codex_homes():
|
|
436
|
+
sessions_dir = codex_home / "sessions"
|
|
437
|
+
if not sessions_dir.exists():
|
|
438
|
+
continue
|
|
439
|
+
try:
|
|
440
|
+
candidates.extend(sessions_dir.rglob("*.jsonl"))
|
|
441
|
+
except Exception:
|
|
442
|
+
pass
|
|
443
|
+
|
|
444
|
+
latest = _latest_file(candidates)
|
|
445
|
+
return str(latest) if latest else None
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def recover_hook_input(reason: str) -> Optional[Dict[str, Any]]:
|
|
449
|
+
transcript_path = find_latest_transcript()
|
|
450
|
+
if not transcript_path:
|
|
451
|
+
hook_log(f"fallback failed reason={reason} no transcript found")
|
|
452
|
+
print(f"[CozeLoop] Hook input missing ({reason}); no Codex transcript found.", file=sys.stderr)
|
|
453
|
+
return None
|
|
454
|
+
hook_log(f"fallback transcript reason={reason} path={transcript_path}")
|
|
455
|
+
print(f"[CozeLoop] Hook input missing ({reason}); fallback transcript: {transcript_path}", file=sys.stderr)
|
|
456
|
+
return {
|
|
457
|
+
"hook_event_name": "Stop",
|
|
458
|
+
"session_id": "",
|
|
459
|
+
"transcript_path": transcript_path,
|
|
460
|
+
"input_fallback": reason,
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
|
|
369
464
|
def parse_session_meta(entries: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
370
465
|
"""Extract session identity from session_meta entry."""
|
|
371
466
|
result = {
|
|
@@ -1076,28 +1171,41 @@ def main():
|
|
|
1076
1171
|
try:
|
|
1077
1172
|
raw_input = sys.stdin.read().strip()
|
|
1078
1173
|
if not raw_input:
|
|
1079
|
-
hook_log("
|
|
1174
|
+
hook_log("stdin empty, trying fallback")
|
|
1080
1175
|
debug_log("No input received from stdin")
|
|
1081
|
-
|
|
1082
|
-
|
|
1176
|
+
hook_input = recover_hook_input("empty_stdin")
|
|
1177
|
+
if not hook_input:
|
|
1178
|
+
return
|
|
1179
|
+
else:
|
|
1180
|
+
hook_input = json.loads(raw_input)
|
|
1083
1181
|
except Exception as e:
|
|
1084
|
-
hook_log(f"
|
|
1182
|
+
hook_log(f"stdin parse error={repr(e)}, trying fallback")
|
|
1085
1183
|
debug_log(f"Error reading hook input from stdin: {e}")
|
|
1086
|
-
|
|
1184
|
+
hook_input = recover_hook_input("stdin_parse_error")
|
|
1185
|
+
if not hook_input:
|
|
1186
|
+
return
|
|
1087
1187
|
|
|
1088
1188
|
debug_log(f"Hook input: {json.dumps(hook_input, ensure_ascii=False)}")
|
|
1089
1189
|
|
|
1090
1190
|
# Get transcript path
|
|
1091
1191
|
transcript_path = hook_input.get("transcript_path")
|
|
1092
1192
|
if not transcript_path:
|
|
1093
|
-
hook_log("
|
|
1193
|
+
hook_log("missing transcript_path, trying fallback")
|
|
1094
1194
|
debug_log("No transcript_path in hook input")
|
|
1095
|
-
|
|
1195
|
+
recovered = recover_hook_input("missing_transcript_path")
|
|
1196
|
+
if not recovered:
|
|
1197
|
+
return
|
|
1198
|
+
hook_input = recovered
|
|
1199
|
+
transcript_path = hook_input.get("transcript_path")
|
|
1096
1200
|
|
|
1097
1201
|
if not os.path.exists(transcript_path):
|
|
1098
|
-
hook_log(f"
|
|
1202
|
+
hook_log(f"transcript not found path={transcript_path}, trying fallback")
|
|
1099
1203
|
debug_log(f"Transcript file not found: {transcript_path}")
|
|
1100
|
-
|
|
1204
|
+
recovered = recover_hook_input("transcript_not_found")
|
|
1205
|
+
if not recovered:
|
|
1206
|
+
return
|
|
1207
|
+
hook_input = recovered
|
|
1208
|
+
transcript_path = hook_input.get("transcript_path")
|
|
1101
1209
|
|
|
1102
1210
|
# Load state
|
|
1103
1211
|
state_file = get_state_file_path(transcript_path)
|