codebee 0.1.101 → 0.1.103

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.
Files changed (134) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -21
  3. package/README.md +3 -6
  4. package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +88 -0
  5. package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +126 -0
  6. package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
  7. package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +210 -0
  8. package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
  9. package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
  10. package/app/.playwright-cli/run-detail-after.png +0 -0
  11. package/app/core/acceptance.py +100 -100
  12. package/app/core/acp_client.py +457 -426
  13. package/app/core/aiflavor.py +197 -197
  14. package/app/core/analytics.py +38 -38
  15. package/app/core/artifacts.py +148 -148
  16. package/app/core/assets.py +110 -110
  17. package/app/core/attachments.py +623 -623
  18. package/app/core/audit.py +30 -30
  19. package/app/core/backend_profiles.py +188 -188
  20. package/app/core/backup.py +470 -470
  21. package/app/core/beekeeper.py +183 -183
  22. package/app/core/benchstore.py +102 -102
  23. package/app/core/board.py +21 -17
  24. package/app/core/bookmeta.py +1036 -1036
  25. package/app/core/bookmeta_catalog.py +180 -180
  26. package/app/core/branching.py +137 -137
  27. package/app/core/builtin_agent.py +13 -4
  28. package/app/core/capability.py +144 -144
  29. package/app/core/catalog.py +28 -6
  30. package/app/core/chaptersafe.py +32 -32
  31. package/app/core/checkpoints.py +117 -31
  32. package/app/core/cleanup.py +303 -303
  33. package/app/core/command_auth.py +302 -302
  34. package/app/core/compaction.py +201 -201
  35. package/app/core/contracts.py +255 -255
  36. package/app/core/covergen.py +356 -356
  37. package/app/core/defectretro.py +43 -43
  38. package/app/core/diagnostics.py +114 -114
  39. package/app/core/dispatch.py +1 -1
  40. package/app/core/dispatch_log.py +128 -128
  41. package/app/core/env_scrub.py +85 -85
  42. package/app/core/errorlog.py +137 -137
  43. package/app/core/eval_matrix.py +91 -82
  44. package/app/core/evaluation.py +174 -174
  45. package/app/core/failure_notify.py +290 -290
  46. package/app/core/flow_graph.py +33 -33
  47. package/app/core/goal_service.py +167 -167
  48. package/app/core/health.py +389 -389
  49. package/app/core/history.py +38 -38
  50. package/app/core/hooks.py +162 -162
  51. package/app/core/knowledge_pipeline.py +86 -86
  52. package/app/core/manager.py +2385 -2385
  53. package/app/core/mcp_client.py +7 -2
  54. package/app/core/mocks.py +64 -64
  55. package/app/core/notify.py +201 -201
  56. package/app/core/novel_quality.py +181 -181
  57. package/app/core/openapi.py +217 -209
  58. package/app/core/operations.py +228 -228
  59. package/app/core/paihang.py +145 -145
  60. package/app/core/pipeline.py +6 -3
  61. package/app/core/policy.py +9 -0
  62. package/app/core/portguard.py +111 -111
  63. package/app/core/presence.py +77 -77
  64. package/app/core/preview.py +293 -293
  65. package/app/core/project_memory.py +206 -206
  66. package/app/core/prompt_guard.py +78 -78
  67. package/app/core/publish/__init__.py +7 -7
  68. package/app/core/publish/auto.py +659 -562
  69. package/app/core/publish/chaprows.py +138 -138
  70. package/app/core/publish/fanqie.py +16 -10
  71. package/app/core/publish/flow.py +29 -1
  72. package/app/core/publish/flows-fanqie-calibrated.json +442 -442
  73. package/app/core/publish/manager.py +1185 -1177
  74. package/app/core/publish/qimao.py +193 -193
  75. package/app/core/publish/volumes.py +108 -108
  76. package/app/core/publish/ws.py +139 -139
  77. package/app/core/quality_gate.py +201 -201
  78. package/app/core/redact.py +38 -38
  79. package/app/core/repeat_guard.py +129 -129
  80. package/app/core/retrieval.py +137 -137
  81. package/app/core/sandbox_runtime.py +95 -0
  82. package/app/core/selfupdate.py +384 -384
  83. package/app/core/session_log.py +161 -161
  84. package/app/core/sessions.py +312 -312
  85. package/app/core/settings.py +215 -215
  86. package/app/core/settings_schema.py +282 -282
  87. package/app/core/share_page.py +114 -114
  88. package/app/core/skill_scan.py +139 -139
  89. package/app/core/skillpacks/market/character-bible.md +66 -66
  90. package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
  91. package/app/core/skillpacks/market/git-workflow.md +57 -57
  92. package/app/core/skillpacks/market/release-notes.md +72 -72
  93. package/app/core/skillpacks/market/weekly-report.md +71 -71
  94. package/app/core/skillpacks/market/worldview-consistency.md +70 -70
  95. package/app/core/step_runner.py +132 -132
  96. package/app/core/store.py +13 -3
  97. package/app/core/story_tracking.py +275 -275
  98. package/app/core/task_compile.py +199 -199
  99. package/app/core/telemetry.py +292 -292
  100. package/app/core/tlsctx.py +64 -64
  101. package/app/core/token_meter.py +147 -147
  102. package/app/core/tool_outputs.py +107 -107
  103. package/app/core/tracing.py +207 -207
  104. package/app/core/upstream.py +41 -41
  105. package/app/core/usage.py +1038 -1038
  106. package/app/core/volumes.py +389 -389
  107. package/app/core/webhooks.py +89 -89
  108. package/app/core/wxdigest.py +865 -865
  109. package/app/core/zentao.py +2692 -2692
  110. package/app/main.py +1 -0
  111. package/app/mcp_server.py +244 -244
  112. package/app/output/_createbook_body.json +1 -0
  113. package/app/output/_probe_fq_click.py +50 -0
  114. package/app/output/_probe_fq_form.py +42 -0
  115. package/app/output/_probe_fq_modal.py +55 -0
  116. package/app/output/_probe_fq_points.py +71 -0
  117. package/app/output/_probe_fq_submit.py +34 -0
  118. package/app/output/_probe_fq_watch.py +62 -0
  119. package/app/output/workbench-clean-preview.jpg +0 -0
  120. package/app/output/workbench-reference-render.png +0 -0
  121. package/app/output/workbench-shell.png +0 -0
  122. package/app/pick_dialog.py +110 -110
  123. package/app/ui/app.js +165 -29
  124. package/app/ui/artdiff.js +74 -74
  125. package/app/ui/hive3d.js +491 -223
  126. package/app/ui/i18n.js +18 -1
  127. package/app/ui/icons/bee.svg +79 -79
  128. package/app/ui/index.html +24 -4
  129. package/app/ui/manifest.json +16 -16
  130. package/app/ui/qrcode.js +2297 -2297
  131. package/app/ui/style.css +548 -18
  132. package/bin/tutti.js +147 -147
  133. package/package.json +1 -1
  134. package/tools/wx_reader.py +261 -261
@@ -1,162 +1,162 @@
1
- # -*- coding: utf-8 -*-
2
- """Surface 会话日志:只追加事件流 + surface 派生模型输入。
3
-
4
- 设计稿:docs/migration/02-context-compaction.md §1A。
5
- 参考 dsh packages/core/session/src/types.ts:222-271(SessionEvent)与
6
- surface.ts:140-167(append 同步校验 + replace generation)。
7
-
8
- 核心不变量:
9
- 1. 只追加:append 是唯一写入口,seq 单调,写入即落盘(JSONL)。
10
- 2. 模型可见即已记录:模型输入一律由 derive_messages() 从日志折叠派生,
11
- 不存在"日志外的模型输入"。
12
- 3. surface replace:压缩通过 surface_op={"op":"replace",start,end} 把一段
13
- 事件折叠为单条摘要消息,replace_generation 单调递增(1D 重试守门用)。
14
- """
15
- from __future__ import annotations
16
-
17
- import json
18
- import threading
19
- import time
20
-
21
-
22
- class SessionEvent:
23
- __slots__ = ("seq", "type", "data", "ts", "surface_op", "turn_id")
24
-
25
- def __init__(self, seq, ev_type, data, ts=None, surface_op=None, turn_id=None):
26
- self.seq = seq
27
- self.type = ev_type
28
- self.data = data
29
- self.ts = ts if ts is not None else time.time()
30
- self.surface_op = surface_op
31
- self.turn_id = turn_id
32
-
33
- def to_dict(self):
34
- d = {"seq": self.seq, "type": self.type, "data": self.data, "ts": self.ts}
35
- if self.surface_op is not None:
36
- d["surface_op"] = self.surface_op
37
- if self.turn_id:
38
- d["turn_id"] = self.turn_id
39
- return d
40
-
41
- @classmethod
42
- def from_dict(cls, d):
43
- return cls(d["seq"], d["type"], d.get("data") or {},
44
- ts=d.get("ts"), surface_op=d.get("surface_op"),
45
- turn_id=d.get("turn_id"))
46
-
47
-
48
- # 进入模型 surface 的事件类型(其余仅记录,不派生)
49
- _SURFACE_TYPES = ("system_message", "user_message", "assistant_message")
50
-
51
-
52
- class Session:
53
- """一次 run 的会话日志。线程安全;落盘路径缺省不持久化(仅内存)。"""
54
-
55
- def __init__(self, run_id: str, store_path=None):
56
- self.run_id = run_id
57
- self.store_path = str(store_path) if store_path else None
58
- self._lock = threading.Lock()
59
- self._events = []
60
- self._seq = 0
61
- self._replace_generation = 0
62
- # surface 视图:list[SessionEvent](已折叠);惰性重建
63
- self._surface = []
64
- self._surface_dirty = True
65
- if self.store_path:
66
- self._load()
67
-
68
- # ---- 写入 ----
69
-
70
- def append(self, ev_type, data, *, surface_op=None, turn_id=None):
71
- """追加事件。surface_op:
72
- None -> 追加进 surface(若是 surface 类型)
73
- "shadow" -> 仅记录,不进 surface(审计/中间产物)
74
- {"op":"replace","start_seq":N,"end_seq":M} -> 把 [N,M] 折叠为本条
75
- """
76
- with self._lock:
77
- self._seq += 1
78
- ev = SessionEvent(self._seq, ev_type, data,
79
- surface_op=surface_op, turn_id=turn_id)
80
- self._events.append(ev)
81
- if isinstance(surface_op, dict) and surface_op.get("op") == "replace":
82
- self._replace_generation += 1
83
- self._surface_dirty = True
84
- self._persist_one(ev)
85
- return ev
86
-
87
- # ---- 派生 ----
88
-
89
- def _fold(self):
90
- """按事件序重放 surface。replace 折叠 [start_seq, end_seq] 区间为该条摘要。"""
91
- surface = [] # list[SessionEvent]
92
- for ev in self._events:
93
- op = ev.surface_op
94
- if op == "shadow":
95
- continue
96
- if isinstance(op, dict) and op.get("op") == "replace":
97
- start, end = op["start_seq"], op["end_seq"]
98
- surface = [e for e in surface if not (start <= e.seq <= end)]
99
- surface.append(ev)
100
- continue
101
- if ev.type in _SURFACE_TYPES:
102
- surface.append(ev)
103
- return surface
104
-
105
- def derive_messages(self):
106
- """模型输入消息列表 [{role, content}]。system 节点去重合并(保留最后一条同 role)。"""
107
- with self._lock:
108
- if self._surface_dirty:
109
- self._surface = self._fold()
110
- self._surface_dirty = False
111
- snap = list(self._surface)
112
- msgs = []
113
- for ev in snap:
114
- role = "system" if ev.type == "system_message" else (
115
- "assistant" if ev.type == "assistant_message" else "user")
116
- msgs.append({"role": role, "content": str(ev.data.get("content", "")),
117
- "seq": ev.seq})
118
- # 同 role 连续合并(CLI 提示词偏好单条 system/user)
119
- merged = []
120
- for m in msgs:
121
- if merged and merged[-1]["role"] == m["role"]:
122
- merged[-1]["content"] += "\n\n" + m["content"]
123
- merged[-1]["seq"] = m["seq"]
124
- else:
125
- merged.append(dict(m))
126
- return merged
127
-
128
- def replace_generation(self):
129
- with self._lock:
130
- return self._replace_generation
131
-
132
- def events(self):
133
- with self._lock:
134
- return list(self._events)
135
-
136
- # ---- 持久化 ----
137
-
138
- def _persist_one(self, ev):
139
- if not self.store_path:
140
- return
141
- with open(self.store_path, "a", encoding="utf-8") as f:
142
- f.write(json.dumps(ev.to_dict(), ensure_ascii=False) + "\n")
143
-
144
- def _load(self):
145
- """启动恢复:逐行读 JSONL,坏行跳过(lazy skip)。"""
146
- try:
147
- with open(self.store_path, encoding="utf-8") as f:
148
- for line in f:
149
- line = line.strip()
150
- if not line.startswith("{"):
151
- continue
152
- try:
153
- self._events.append(SessionEvent.from_dict(json.loads(line)))
154
- except Exception:
155
- continue
156
- if self._events:
157
- self._seq = max(e.seq for e in self._events)
158
- self._replace_generation = sum(
159
- 1 for e in self._events
160
- if isinstance(e.surface_op, dict) and e.surface_op.get("op") == "replace")
161
- except OSError:
1
+ # -*- coding: utf-8 -*-
2
+ """Surface 会话日志:只追加事件流 + surface 派生模型输入。
3
+
4
+ 设计稿:docs/migration/02-context-compaction.md §1A。
5
+ 参考 dsh packages/core/session/src/types.ts:222-271(SessionEvent)与
6
+ surface.ts:140-167(append 同步校验 + replace generation)。
7
+
8
+ 核心不变量:
9
+ 1. 只追加:append 是唯一写入口,seq 单调,写入即落盘(JSONL)。
10
+ 2. 模型可见即已记录:模型输入一律由 derive_messages() 从日志折叠派生,
11
+ 不存在"日志外的模型输入"。
12
+ 3. surface replace:压缩通过 surface_op={"op":"replace",start,end} 把一段
13
+ 事件折叠为单条摘要消息,replace_generation 单调递增(1D 重试守门用)。
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import threading
19
+ import time
20
+
21
+
22
+ class SessionEvent:
23
+ __slots__ = ("seq", "type", "data", "ts", "surface_op", "turn_id")
24
+
25
+ def __init__(self, seq, ev_type, data, ts=None, surface_op=None, turn_id=None):
26
+ self.seq = seq
27
+ self.type = ev_type
28
+ self.data = data
29
+ self.ts = ts if ts is not None else time.time()
30
+ self.surface_op = surface_op
31
+ self.turn_id = turn_id
32
+
33
+ def to_dict(self):
34
+ d = {"seq": self.seq, "type": self.type, "data": self.data, "ts": self.ts}
35
+ if self.surface_op is not None:
36
+ d["surface_op"] = self.surface_op
37
+ if self.turn_id:
38
+ d["turn_id"] = self.turn_id
39
+ return d
40
+
41
+ @classmethod
42
+ def from_dict(cls, d):
43
+ return cls(d["seq"], d["type"], d.get("data") or {},
44
+ ts=d.get("ts"), surface_op=d.get("surface_op"),
45
+ turn_id=d.get("turn_id"))
46
+
47
+
48
+ # 进入模型 surface 的事件类型(其余仅记录,不派生)
49
+ _SURFACE_TYPES = ("system_message", "user_message", "assistant_message")
50
+
51
+
52
+ class Session:
53
+ """一次 run 的会话日志。线程安全;落盘路径缺省不持久化(仅内存)。"""
54
+
55
+ def __init__(self, run_id: str, store_path=None):
56
+ self.run_id = run_id
57
+ self.store_path = str(store_path) if store_path else None
58
+ self._lock = threading.Lock()
59
+ self._events = []
60
+ self._seq = 0
61
+ self._replace_generation = 0
62
+ # surface 视图:list[SessionEvent](已折叠);惰性重建
63
+ self._surface = []
64
+ self._surface_dirty = True
65
+ if self.store_path:
66
+ self._load()
67
+
68
+ # ---- 写入 ----
69
+
70
+ def append(self, ev_type, data, *, surface_op=None, turn_id=None):
71
+ """追加事件。surface_op:
72
+ None -> 追加进 surface(若是 surface 类型)
73
+ "shadow" -> 仅记录,不进 surface(审计/中间产物)
74
+ {"op":"replace","start_seq":N,"end_seq":M} -> 把 [N,M] 折叠为本条
75
+ """
76
+ with self._lock:
77
+ self._seq += 1
78
+ ev = SessionEvent(self._seq, ev_type, data,
79
+ surface_op=surface_op, turn_id=turn_id)
80
+ self._events.append(ev)
81
+ if isinstance(surface_op, dict) and surface_op.get("op") == "replace":
82
+ self._replace_generation += 1
83
+ self._surface_dirty = True
84
+ self._persist_one(ev)
85
+ return ev
86
+
87
+ # ---- 派生 ----
88
+
89
+ def _fold(self):
90
+ """按事件序重放 surface。replace 折叠 [start_seq, end_seq] 区间为该条摘要。"""
91
+ surface = [] # list[SessionEvent]
92
+ for ev in self._events:
93
+ op = ev.surface_op
94
+ if op == "shadow":
95
+ continue
96
+ if isinstance(op, dict) and op.get("op") == "replace":
97
+ start, end = op["start_seq"], op["end_seq"]
98
+ surface = [e for e in surface if not (start <= e.seq <= end)]
99
+ surface.append(ev)
100
+ continue
101
+ if ev.type in _SURFACE_TYPES:
102
+ surface.append(ev)
103
+ return surface
104
+
105
+ def derive_messages(self):
106
+ """模型输入消息列表 [{role, content}]。system 节点去重合并(保留最后一条同 role)。"""
107
+ with self._lock:
108
+ if self._surface_dirty:
109
+ self._surface = self._fold()
110
+ self._surface_dirty = False
111
+ snap = list(self._surface)
112
+ msgs = []
113
+ for ev in snap:
114
+ role = "system" if ev.type == "system_message" else (
115
+ "assistant" if ev.type == "assistant_message" else "user")
116
+ msgs.append({"role": role, "content": str(ev.data.get("content", "")),
117
+ "seq": ev.seq})
118
+ # 同 role 连续合并(CLI 提示词偏好单条 system/user)
119
+ merged = []
120
+ for m in msgs:
121
+ if merged and merged[-1]["role"] == m["role"]:
122
+ merged[-1]["content"] += "\n\n" + m["content"]
123
+ merged[-1]["seq"] = m["seq"]
124
+ else:
125
+ merged.append(dict(m))
126
+ return merged
127
+
128
+ def replace_generation(self):
129
+ with self._lock:
130
+ return self._replace_generation
131
+
132
+ def events(self):
133
+ with self._lock:
134
+ return list(self._events)
135
+
136
+ # ---- 持久化 ----
137
+
138
+ def _persist_one(self, ev):
139
+ if not self.store_path:
140
+ return
141
+ with open(self.store_path, "a", encoding="utf-8") as f:
142
+ f.write(json.dumps(ev.to_dict(), ensure_ascii=False) + "\n")
143
+
144
+ def _load(self):
145
+ """启动恢复:逐行读 JSONL,坏行跳过(lazy skip)。"""
146
+ try:
147
+ with open(self.store_path, encoding="utf-8") as f:
148
+ for line in f:
149
+ line = line.strip()
150
+ if not line.startswith("{"):
151
+ continue
152
+ try:
153
+ self._events.append(SessionEvent.from_dict(json.loads(line)))
154
+ except Exception:
155
+ continue
156
+ if self._events:
157
+ self._seq = max(e.seq for e in self._events)
158
+ self._replace_generation = sum(
159
+ 1 for e in self._events
160
+ if isinstance(e.surface_op, dict) and e.surface_op.get("op") == "replace")
161
+ except OSError:
162
162
  pass