codebee 0.1.69 → 0.1.71
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/CHANGELOG.md +589 -543
- package/LICENSE +21 -21
- package/README.md +509 -508
- package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +88 -0
- package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +126 -0
- package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
- package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +210 -0
- package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
- package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
- package/app/.playwright-cli/run-detail-after.png +0 -0
- package/app/browser-final-1440.png +0 -0
- package/app/browser-final-390.png +0 -0
- package/app/browser-google-fallback.png +0 -0
- package/app/browser-google-test.png +0 -0
- package/app/browser-workbench-1440.png +0 -0
- package/app/browser-workbench-390.png +0 -0
- package/app/browser-workspace-google.png +0 -0
- package/app/browser-workspace-smoke.png +0 -0
- package/app/browser-workspace.png +0 -0
- package/app/core/acceptance.py +100 -100
- package/app/core/aiflavor.py +197 -197
- package/app/core/artifacts.py +148 -148
- package/app/core/assets.py +110 -110
- package/app/core/attachments.py +623 -623
- package/app/core/audit.py +30 -0
- package/app/core/automation.py +107 -12
- package/app/core/backup.py +470 -470
- package/app/core/beekeeper.py +183 -183
- package/app/core/benchstore.py +95 -0
- package/app/core/board.py +209 -209
- package/app/core/bookmeta.py +935 -930
- package/app/core/bookmeta_catalog.py +180 -180
- package/app/core/branching.py +137 -137
- package/app/core/builtin_agent.py +1673 -1234
- package/app/core/capability.py +144 -144
- package/app/core/catalog.py +7 -1
- package/app/core/chaptersafe.py +32 -32
- package/app/core/cleanup.py +303 -303
- package/app/core/compaction.py +201 -201
- package/app/core/covergen.py +356 -356
- package/app/core/defectretro.py +43 -43
- package/app/core/diagnostics.py +114 -114
- package/app/core/dispatch.py +190 -173
- package/app/core/dispatch_log.py +128 -128
- package/app/core/env_scrub.py +85 -85
- package/app/core/error_codes.py +11 -2
- package/app/core/errorlog.py +137 -137
- package/app/core/evalbench.py +720 -0
- package/app/core/evaluation.py +174 -174
- package/app/core/failure_notify.py +290 -290
- package/app/core/flows.py +234 -9
- package/app/core/goal_service.py +167 -167
- package/app/core/health.py +374 -374
- package/app/core/history.py +38 -38
- package/app/core/hooks.py +162 -162
- package/app/core/jobs.py +22 -1
- package/app/core/manager.py +2143 -2122
- package/app/core/market_remote.py +10 -0
- package/app/core/mcp_client.py +287 -0
- package/app/core/mocks.py +64 -64
- package/app/core/modelhub.py +185 -2
- package/app/core/notify.py +201 -106
- package/app/core/novel_quality.py +189 -0
- package/app/core/operations.py +228 -228
- package/app/core/paihang.py +138 -138
- package/app/core/paths.py +4 -0
- package/app/core/pipeline.py +426 -28
- package/app/core/planner.py +243 -22
- package/app/core/plugins.py +525 -0
- package/app/core/portguard.py +111 -111
- package/app/core/preview.py +293 -293
- package/app/core/publish/__init__.py +7 -7
- package/app/core/publish/auto.py +367 -367
- package/app/core/publish/browser.py +655 -539
- package/app/core/publish/fanqie.py +165 -159
- package/app/core/publish/flow.py +589 -440
- package/app/core/publish/flows-fanqie-calibrated.json +336 -332
- package/app/core/publish/ledger.py +229 -203
- package/app/core/publish/manager.py +824 -692
- package/app/core/publish/qimao.py +146 -141
- package/app/core/publish/ws.py +139 -139
- package/app/core/redact.py +38 -38
- package/app/core/remotesync.py +121 -0
- package/app/core/repeat_guard.py +129 -129
- package/app/core/router.py +398 -398
- package/app/core/runner.py +2142 -2067
- package/app/core/selfupdate.py +384 -384
- package/app/core/session_log.py +161 -161
- package/app/core/sessions.py +312 -312
- package/app/core/settings.py +42 -4
- package/app/core/settings_schema.py +270 -262
- package/app/core/share_page.py +114 -114
- package/app/core/skill_scan.py +122 -122
- package/app/core/skillpacks/fanqie-novel.md +80 -80
- package/app/core/skillpacks/market/character-bible.md +66 -66
- package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
- package/app/core/skillpacks/market/git-workflow.md +57 -57
- package/app/core/skillpacks/market/release-notes.md +72 -72
- package/app/core/skillpacks/market/weekly-report.md +71 -71
- package/app/core/skillpacks/market/worldview-consistency.md +70 -70
- package/app/core/step_runner.py +132 -132
- package/app/core/store.py +30 -8
- package/app/core/task_compile.py +199 -199
- package/app/core/telemetry.py +292 -292
- package/app/core/tlsctx.py +64 -64
- package/app/core/token_meter.py +147 -147
- package/app/core/tracing.py +144 -144
- package/app/core/upstream.py +41 -41
- package/app/core/usage.py +1035 -862
- package/app/core/volumes.py +389 -389
- package/app/core/wxdigest.py +858 -858
- package/app/core/zentao.py +2614 -2465
- package/app/current-browser.png +0 -0
- package/app/main.py +316 -17
- package/app/mcp_server.py +175 -0
- package/app/pet.py +83 -22
- package/app/pick_dialog.py +110 -110
- package/app/plugins/code-review-helper/.codex-plugin/plugin.json +18 -0
- package/app/plugins/code-review-helper/skills/review.md +16 -0
- package/app/ui/app.js +1391 -77
- package/app/ui/artdiff.js +74 -74
- package/app/ui/board.html +162 -162
- package/app/ui/board.js +305 -305
- package/app/ui/i18n.js +249 -7
- package/app/ui/icons/bee.svg +79 -79
- package/app/ui/index.html +277 -13
- package/app/ui/manifest.json +16 -16
- package/app/ui/qrcode.js +2297 -2297
- package/app/ui/style.css +845 -12
- package/app//344/275/234/345/223/201/344/277/241/346/201/257-/344/270/203/347/214/253.md +18 -0
- package/bin/tutti.js +147 -147
- package/package.json +47 -46
- package/tools/wx_reader.py +261 -261
package/app/core/step_runner.py
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""Step 执行器:压缩触发的重试守门(复用已渲染 prompt,不重复组装)。
|
|
3
|
-
|
|
4
|
-
设计稿:docs/migration/02-context-compaction.md §1D。
|
|
5
|
-
参考 dsh packages/core/agent-loop/src/agent.ts:307-378:
|
|
6
|
-
失败重试只在「前进 surface generation」时返回 {kind:'retry'},
|
|
7
|
-
复用已渲染 PromptAssembly,不重跑 pre-step / 组装 / 用户消息准入。
|
|
8
|
-
|
|
9
|
-
Tutti 语境:runner.run_agent 自带 prompt(已渲染);重试的风险不是重复渲染
|
|
10
|
-
(prompt 是同一个字符串),而是「不压缩直接重试」必然再次撑爆。
|
|
11
|
-
本模块的守门:仅当 maybe_compact 真正前进了 surface generation 才重试一次。
|
|
12
|
-
"""
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
import logging
|
|
16
|
-
|
|
17
|
-
from .compaction import maybe_compact, DEFAULT_PRESSURE_THRESHOLD
|
|
18
|
-
from .error_codes import ErrorCode
|
|
19
|
-
|
|
20
|
-
log = logging.getLogger(__name__)
|
|
21
|
-
|
|
22
|
-
# 触发「压缩后重试」的错误码
|
|
23
|
-
_OVERFLOW_CODES = {ErrorCode.CONTEXT_OVERFLOW, ErrorCode.MAX_TOKENS}
|
|
24
|
-
|
|
25
|
-
# 事前预检阈值:最近一次调用的上下文锚点已占本步模型容量九成 → 第一个请求
|
|
26
|
-
# 大概率被供应商拒。只拦「大概率必死」,不提前压缩平时的高压(used() 是累计
|
|
27
|
-
# 口径,比真实上下文偏大;0.8 的响应式阈值语义与此不同,勿混用)。
|
|
28
|
-
_PRECHECK_RATIO = 0.9
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _v2_compaction_tuning():
|
|
32
|
-
"""settings_v2 orchestrator.compaction 微调 → (threshold|None, retain|None)。
|
|
33
|
-
|
|
34
|
-
None = 未配置(走 maybe_compact 的模块默认);读取/注册失败静默回落。
|
|
35
|
-
retain 允许 0(尾部不保留),与 None(未配置)语义不同,勿合并。"""
|
|
36
|
-
try:
|
|
37
|
-
from .settings_schema import get as ss_get, register_default_namespaces
|
|
38
|
-
register_default_namespaces()
|
|
39
|
-
c = (ss_get("orchestrator") or {}).get("compaction") or {}
|
|
40
|
-
th = c.get("pressure_threshold")
|
|
41
|
-
rt = c.get("retain_tail_tokens")
|
|
42
|
-
return (float(th) if th else None,
|
|
43
|
-
int(rt) if rt is not None else None)
|
|
44
|
-
except Exception:
|
|
45
|
-
return None, None
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def execute_step(session, run_agent_fn, prompt, *, model: str = "",
|
|
49
|
-
llm_caller=None, retain_tail_tokens=None, **kwargs):
|
|
50
|
-
"""执行一次 step;撑爆时压缩并守门重试一次。
|
|
51
|
-
|
|
52
|
-
事前预检(借鉴 freebuff base-chat 每步容量重估):换将切到小窗口模型后,
|
|
53
|
-
响应式路径要等供应商报 CONTEXT_OVERFLOW 才压缩——报得不干净就直接卡死。
|
|
54
|
-
先用「最近一次调用上下文 vs 本步模型容量」判一次,超阈先压缩再发。
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
session: session_log.Session(记录 replace_generation)
|
|
58
|
-
run_agent_fn: runner.run_agent(或测试替身),签名 (prompt, **kwargs) -> dict
|
|
59
|
-
prompt: 已渲染的提示词(重试时原样复用,不重新渲染)
|
|
60
|
-
model: 本步将要使用的模型名(容量与压力估算都按它算)
|
|
61
|
-
llm_caller: 压缩摘要 LLM(None = 不压缩,永不重试)
|
|
62
|
-
**kwargs: 透传 run_agent_fn
|
|
63
|
-
|
|
64
|
-
Returns:
|
|
65
|
-
(result, retried: bool)
|
|
66
|
-
"""
|
|
67
|
-
v2_th, v2_rt = _v2_compaction_tuning()
|
|
68
|
-
compact_kwargs = {}
|
|
69
|
-
if retain_tail_tokens is not None:
|
|
70
|
-
compact_kwargs["retain_tail_tokens"] = retain_tail_tokens
|
|
71
|
-
elif v2_rt is not None:
|
|
72
|
-
compact_kwargs["retain_tail_tokens"] = v2_rt
|
|
73
|
-
if v2_th is not None:
|
|
74
|
-
compact_kwargs["threshold"] = v2_th
|
|
75
|
-
if llm_caller is not None and model:
|
|
76
|
-
try:
|
|
77
|
-
from .token_meter import token_meter
|
|
78
|
-
ctx = token_meter.last_context(session.run_id)
|
|
79
|
-
cap = token_meter.capacity(model)
|
|
80
|
-
if ctx > 0 and cap > 0 and ctx > cap * _PRECHECK_RATIO:
|
|
81
|
-
gen_p0 = session.replace_generation()
|
|
82
|
-
if maybe_compact(session, model=model, llm_caller=llm_caller,
|
|
83
|
-
run_id=session.run_id, **compact_kwargs) \
|
|
84
|
-
and session.replace_generation() > gen_p0:
|
|
85
|
-
log.info("precheck: last ctx~%d > %.0f%% of cap(%s)=%d, "
|
|
86
|
-
"compacted before step", ctx, _PRECHECK_RATIO * 100,
|
|
87
|
-
model, cap)
|
|
88
|
-
except Exception:
|
|
89
|
-
log.debug("precheck skipped", exc_info=True)
|
|
90
|
-
gen0 = session.replace_generation()
|
|
91
|
-
result = run_agent_fn(prompt, **kwargs)
|
|
92
|
-
code = result.get("error_code") or ""
|
|
93
|
-
if code not in _OVERFLOW_CODES or llm_caller is None:
|
|
94
|
-
return result, False
|
|
95
|
-
# 撑爆 → 尝试压缩
|
|
96
|
-
compacted = maybe_compact(session, model=model, llm_caller=llm_caller,
|
|
97
|
-
run_id=session.run_id, **compact_kwargs)
|
|
98
|
-
gen1 = session.replace_generation()
|
|
99
|
-
if not compacted or gen1 <= gen0:
|
|
100
|
-
# 压缩未发生或未前进 generation:重试必然再次撑爆,不重试
|
|
101
|
-
log.info("overflow but no compaction progress (gen %s->%s), no retry",
|
|
102
|
-
gen0, gen1)
|
|
103
|
-
return result, False
|
|
104
|
-
# 压缩生效:surface 已折叠。**真正替换 CLI 请求上下文**(2026-09-21
|
|
105
|
-
# 清最老待深挖项:此前重试仍发原 prompt + 续旧 resume 会话——摘要只进
|
|
106
|
-
# 审计日志,CLI 看到的上下文一点没小,重试必然再爆):
|
|
107
|
-
# ①重试 prompt 改用 session 派生转录(折叠摘要 + 近尾消息,体量骤降);
|
|
108
|
-
# ②弃 resume(旧会话本体仍是膨胀态,续上就前功尽弃)。
|
|
109
|
-
msgs = []
|
|
110
|
-
try:
|
|
111
|
-
msgs = session.derive_messages()
|
|
112
|
-
except Exception:
|
|
113
|
-
log.debug("derive_messages failed on retry", exc_info=True)
|
|
114
|
-
kwargs2 = dict(kwargs)
|
|
115
|
-
if msgs:
|
|
116
|
-
transcript = "\n\n".join("[%s] %s" % (m.get("role"), m.get("content"))
|
|
117
|
-
for m in msgs)
|
|
118
|
-
if len(transcript) > 24_000:
|
|
119
|
-
transcript = transcript[-24_000:]
|
|
120
|
-
retry_prompt = transcript
|
|
121
|
-
# 本步指令必须在场:被折叠/截断掉就显式补在尾部(缺失指令比重复严重)
|
|
122
|
-
tail = transcript[-2000:]
|
|
123
|
-
if prompt[:400] not in tail:
|
|
124
|
-
retry_prompt = transcript + "\n\n" + prompt
|
|
125
|
-
kwargs2.pop("resume", None)
|
|
126
|
-
log.info("compaction advanced gen %s->%s, retrying with derived "
|
|
127
|
-
"context (%d msgs, resume dropped)", gen0, gen1, len(msgs))
|
|
128
|
-
result2 = run_agent_fn(retry_prompt, **kwargs2)
|
|
129
|
-
else:
|
|
130
|
-
log.info("compaction advanced gen %s->%s but no surface derived, "
|
|
131
|
-
"retrying same prompt", gen0, gen1)
|
|
132
|
-
result2 = run_agent_fn(prompt, **kwargs)
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Step 执行器:压缩触发的重试守门(复用已渲染 prompt,不重复组装)。
|
|
3
|
+
|
|
4
|
+
设计稿:docs/migration/02-context-compaction.md §1D。
|
|
5
|
+
参考 dsh packages/core/agent-loop/src/agent.ts:307-378:
|
|
6
|
+
失败重试只在「前进 surface generation」时返回 {kind:'retry'},
|
|
7
|
+
复用已渲染 PromptAssembly,不重跑 pre-step / 组装 / 用户消息准入。
|
|
8
|
+
|
|
9
|
+
Tutti 语境:runner.run_agent 自带 prompt(已渲染);重试的风险不是重复渲染
|
|
10
|
+
(prompt 是同一个字符串),而是「不压缩直接重试」必然再次撑爆。
|
|
11
|
+
本模块的守门:仅当 maybe_compact 真正前进了 surface generation 才重试一次。
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
from .compaction import maybe_compact, DEFAULT_PRESSURE_THRESHOLD
|
|
18
|
+
from .error_codes import ErrorCode
|
|
19
|
+
|
|
20
|
+
log = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
# 触发「压缩后重试」的错误码
|
|
23
|
+
_OVERFLOW_CODES = {ErrorCode.CONTEXT_OVERFLOW, ErrorCode.MAX_TOKENS}
|
|
24
|
+
|
|
25
|
+
# 事前预检阈值:最近一次调用的上下文锚点已占本步模型容量九成 → 第一个请求
|
|
26
|
+
# 大概率被供应商拒。只拦「大概率必死」,不提前压缩平时的高压(used() 是累计
|
|
27
|
+
# 口径,比真实上下文偏大;0.8 的响应式阈值语义与此不同,勿混用)。
|
|
28
|
+
_PRECHECK_RATIO = 0.9
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _v2_compaction_tuning():
|
|
32
|
+
"""settings_v2 orchestrator.compaction 微调 → (threshold|None, retain|None)。
|
|
33
|
+
|
|
34
|
+
None = 未配置(走 maybe_compact 的模块默认);读取/注册失败静默回落。
|
|
35
|
+
retain 允许 0(尾部不保留),与 None(未配置)语义不同,勿合并。"""
|
|
36
|
+
try:
|
|
37
|
+
from .settings_schema import get as ss_get, register_default_namespaces
|
|
38
|
+
register_default_namespaces()
|
|
39
|
+
c = (ss_get("orchestrator") or {}).get("compaction") or {}
|
|
40
|
+
th = c.get("pressure_threshold")
|
|
41
|
+
rt = c.get("retain_tail_tokens")
|
|
42
|
+
return (float(th) if th else None,
|
|
43
|
+
int(rt) if rt is not None else None)
|
|
44
|
+
except Exception:
|
|
45
|
+
return None, None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def execute_step(session, run_agent_fn, prompt, *, model: str = "",
|
|
49
|
+
llm_caller=None, retain_tail_tokens=None, **kwargs):
|
|
50
|
+
"""执行一次 step;撑爆时压缩并守门重试一次。
|
|
51
|
+
|
|
52
|
+
事前预检(借鉴 freebuff base-chat 每步容量重估):换将切到小窗口模型后,
|
|
53
|
+
响应式路径要等供应商报 CONTEXT_OVERFLOW 才压缩——报得不干净就直接卡死。
|
|
54
|
+
先用「最近一次调用上下文 vs 本步模型容量」判一次,超阈先压缩再发。
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
session: session_log.Session(记录 replace_generation)
|
|
58
|
+
run_agent_fn: runner.run_agent(或测试替身),签名 (prompt, **kwargs) -> dict
|
|
59
|
+
prompt: 已渲染的提示词(重试时原样复用,不重新渲染)
|
|
60
|
+
model: 本步将要使用的模型名(容量与压力估算都按它算)
|
|
61
|
+
llm_caller: 压缩摘要 LLM(None = 不压缩,永不重试)
|
|
62
|
+
**kwargs: 透传 run_agent_fn
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
(result, retried: bool)
|
|
66
|
+
"""
|
|
67
|
+
v2_th, v2_rt = _v2_compaction_tuning()
|
|
68
|
+
compact_kwargs = {}
|
|
69
|
+
if retain_tail_tokens is not None:
|
|
70
|
+
compact_kwargs["retain_tail_tokens"] = retain_tail_tokens
|
|
71
|
+
elif v2_rt is not None:
|
|
72
|
+
compact_kwargs["retain_tail_tokens"] = v2_rt
|
|
73
|
+
if v2_th is not None:
|
|
74
|
+
compact_kwargs["threshold"] = v2_th
|
|
75
|
+
if llm_caller is not None and model:
|
|
76
|
+
try:
|
|
77
|
+
from .token_meter import token_meter
|
|
78
|
+
ctx = token_meter.last_context(session.run_id)
|
|
79
|
+
cap = token_meter.capacity(model)
|
|
80
|
+
if ctx > 0 and cap > 0 and ctx > cap * _PRECHECK_RATIO:
|
|
81
|
+
gen_p0 = session.replace_generation()
|
|
82
|
+
if maybe_compact(session, model=model, llm_caller=llm_caller,
|
|
83
|
+
run_id=session.run_id, **compact_kwargs) \
|
|
84
|
+
and session.replace_generation() > gen_p0:
|
|
85
|
+
log.info("precheck: last ctx~%d > %.0f%% of cap(%s)=%d, "
|
|
86
|
+
"compacted before step", ctx, _PRECHECK_RATIO * 100,
|
|
87
|
+
model, cap)
|
|
88
|
+
except Exception:
|
|
89
|
+
log.debug("precheck skipped", exc_info=True)
|
|
90
|
+
gen0 = session.replace_generation()
|
|
91
|
+
result = run_agent_fn(prompt, **kwargs)
|
|
92
|
+
code = result.get("error_code") or ""
|
|
93
|
+
if code not in _OVERFLOW_CODES or llm_caller is None:
|
|
94
|
+
return result, False
|
|
95
|
+
# 撑爆 → 尝试压缩
|
|
96
|
+
compacted = maybe_compact(session, model=model, llm_caller=llm_caller,
|
|
97
|
+
run_id=session.run_id, **compact_kwargs)
|
|
98
|
+
gen1 = session.replace_generation()
|
|
99
|
+
if not compacted or gen1 <= gen0:
|
|
100
|
+
# 压缩未发生或未前进 generation:重试必然再次撑爆,不重试
|
|
101
|
+
log.info("overflow but no compaction progress (gen %s->%s), no retry",
|
|
102
|
+
gen0, gen1)
|
|
103
|
+
return result, False
|
|
104
|
+
# 压缩生效:surface 已折叠。**真正替换 CLI 请求上下文**(2026-09-21
|
|
105
|
+
# 清最老待深挖项:此前重试仍发原 prompt + 续旧 resume 会话——摘要只进
|
|
106
|
+
# 审计日志,CLI 看到的上下文一点没小,重试必然再爆):
|
|
107
|
+
# ①重试 prompt 改用 session 派生转录(折叠摘要 + 近尾消息,体量骤降);
|
|
108
|
+
# ②弃 resume(旧会话本体仍是膨胀态,续上就前功尽弃)。
|
|
109
|
+
msgs = []
|
|
110
|
+
try:
|
|
111
|
+
msgs = session.derive_messages()
|
|
112
|
+
except Exception:
|
|
113
|
+
log.debug("derive_messages failed on retry", exc_info=True)
|
|
114
|
+
kwargs2 = dict(kwargs)
|
|
115
|
+
if msgs:
|
|
116
|
+
transcript = "\n\n".join("[%s] %s" % (m.get("role"), m.get("content"))
|
|
117
|
+
for m in msgs)
|
|
118
|
+
if len(transcript) > 24_000:
|
|
119
|
+
transcript = transcript[-24_000:]
|
|
120
|
+
retry_prompt = transcript
|
|
121
|
+
# 本步指令必须在场:被折叠/截断掉就显式补在尾部(缺失指令比重复严重)
|
|
122
|
+
tail = transcript[-2000:]
|
|
123
|
+
if prompt[:400] not in tail:
|
|
124
|
+
retry_prompt = transcript + "\n\n" + prompt
|
|
125
|
+
kwargs2.pop("resume", None)
|
|
126
|
+
log.info("compaction advanced gen %s->%s, retrying with derived "
|
|
127
|
+
"context (%d msgs, resume dropped)", gen0, gen1, len(msgs))
|
|
128
|
+
result2 = run_agent_fn(retry_prompt, **kwargs2)
|
|
129
|
+
else:
|
|
130
|
+
log.info("compaction advanced gen %s->%s but no surface derived, "
|
|
131
|
+
"retrying same prompt", gen0, gen1)
|
|
132
|
+
result2 = run_agent_fn(prompt, **kwargs)
|
|
133
133
|
return result2, True
|
package/app/core/store.py
CHANGED
|
@@ -18,7 +18,7 @@ import threading
|
|
|
18
18
|
import time
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
|
-
from . import artifacts, paths, runner, tracing, volumes
|
|
21
|
+
from . import artifacts, audit, paths, runner, tracing, volumes
|
|
22
22
|
|
|
23
23
|
LOCK = threading.RLock()
|
|
24
24
|
_TASKS = {}
|
|
@@ -148,7 +148,7 @@ def create_task(payload):
|
|
|
148
148
|
raise ValueError("目标描述不能为空")
|
|
149
149
|
title = title or goal.splitlines()[0][:30] # 标题可省略,自动取目标首行
|
|
150
150
|
if not workdir:
|
|
151
|
-
# 未指定目录 → 用「默认保存路径」(设置里可改;内置回落
|
|
151
|
+
# 未指定目录 → 用「默认保存路径」(设置里可改;内置回落 <数据目录>/workspace)。
|
|
152
152
|
# 默认路径允许自动创建;用户显式给的目录仍必须已存在。
|
|
153
153
|
from . import settings as settings_mod
|
|
154
154
|
workdir = settings_mod.default_workdir()
|
|
@@ -487,8 +487,8 @@ def get_task(task_id):
|
|
|
487
487
|
return None
|
|
488
488
|
|
|
489
489
|
|
|
490
|
-
def list_tasks(limit=
|
|
491
|
-
"""archived=None
|
|
490
|
+
def list_tasks(limit=None, archived=None):
|
|
491
|
+
"""按 id 倒序列任务。archived 筛选归档态;limit=None 返回全部。"""
|
|
492
492
|
with LOCK:
|
|
493
493
|
ids = sorted(_TASKS.keys(), reverse=True)
|
|
494
494
|
out = []
|
|
@@ -497,7 +497,7 @@ def list_tasks(limit=100, archived=None):
|
|
|
497
497
|
if archived is not None and bool(t.get("archived")) != archived:
|
|
498
498
|
continue
|
|
499
499
|
out.append(t)
|
|
500
|
-
if len(out) >= limit:
|
|
500
|
+
if limit is not None and len(out) >= limit:
|
|
501
501
|
break
|
|
502
502
|
return out
|
|
503
503
|
|
|
@@ -1435,8 +1435,12 @@ def archive_task(task_id, archived=True):
|
|
|
1435
1435
|
return True, ""
|
|
1436
1436
|
|
|
1437
1437
|
|
|
1438
|
-
def delete_task(task_id):
|
|
1439
|
-
"""删除任务及其全部运行记录(含日志与报告目录)。返回 (ok, 错误信息)。
|
|
1438
|
+
def delete_task(task_id, actor=""):
|
|
1439
|
+
"""删除任务及其全部运行记录(含日志与报告目录)。返回 (ok, 错误信息)。
|
|
1440
|
+
|
|
1441
|
+
不可逆且无回收站,故落审计(谁/何时/删了什么——2026-09-28 连载根任务
|
|
1442
|
+
被删案:事后无人能回答这两问)。连载链的前序任务被后续任务 serial.continues
|
|
1443
|
+
依赖,删了会把整条链的「继续连载/断点续跑」地基抽掉——这类任务只许归档。"""
|
|
1440
1444
|
if not _valid_id(task_id):
|
|
1441
1445
|
return False, "非法的任务 ID"
|
|
1442
1446
|
with LOCK:
|
|
@@ -1445,6 +1449,11 @@ def delete_task(task_id):
|
|
|
1445
1449
|
return False, "任务不存在"
|
|
1446
1450
|
if task.get("status") in ("queued", "running"):
|
|
1447
1451
|
return False, "运行中的任务不能删除,请先取消"
|
|
1452
|
+
if task.get("type") == "serial_novel":
|
|
1453
|
+
for other in _TASKS.values():
|
|
1454
|
+
if other.get("id") != task_id and \
|
|
1455
|
+
(other.get("serial") or {}).get("continues") == task_id:
|
|
1456
|
+
return False, "该任务是连载链的前序任务(后续任务依赖它),请用「归档」隐藏而不是删除"
|
|
1448
1457
|
run_ids = []
|
|
1449
1458
|
for rid, r in _RUNS.items():
|
|
1450
1459
|
if r.get("task_id") != task_id:
|
|
@@ -1458,6 +1467,15 @@ def delete_task(task_id):
|
|
|
1458
1467
|
for rid in run_ids:
|
|
1459
1468
|
_schedule_run_dir_cleanup(rid)
|
|
1460
1469
|
(paths.TASKS_DIR / (task_id + ".json")).unlink(missing_ok=True)
|
|
1470
|
+
audit.record("task_delete", {
|
|
1471
|
+
"task_id": task_id,
|
|
1472
|
+
"title": task.get("title") or "",
|
|
1473
|
+
"type": task.get("type") or "",
|
|
1474
|
+
"status": task.get("status") or "",
|
|
1475
|
+
"workdir": task.get("workdir") or "",
|
|
1476
|
+
"run_ids": run_ids,
|
|
1477
|
+
"actor": actor or "local",
|
|
1478
|
+
})
|
|
1461
1479
|
bump_state()
|
|
1462
1480
|
return True, ""
|
|
1463
1481
|
|
|
@@ -1880,7 +1898,7 @@ def clear_stream_state(run_id=None, n=None):
|
|
|
1880
1898
|
|
|
1881
1899
|
def finish_step(run_id, n, status, summary="", exit_code=None,
|
|
1882
1900
|
cost_usd=0.0, tokens=0.0, duration_s=None, model=None, output=None,
|
|
1883
|
-
followups=None, thinking=None):
|
|
1901
|
+
followups=None, thinking=None, partial=False):
|
|
1884
1902
|
with LOCK:
|
|
1885
1903
|
run = _RUNS.get(run_id)
|
|
1886
1904
|
if not run:
|
|
@@ -1911,6 +1929,10 @@ def finish_step(run_id, n, status, summary="", exit_code=None,
|
|
|
1911
1929
|
s["thinking"] = runner.strip_ansi(str(thinking))[:LIVE_TEXT_MAX]
|
|
1912
1930
|
if followups:
|
|
1913
1931
|
s["followups"] = list(followups)[:3]
|
|
1932
|
+
if partial:
|
|
1933
|
+
s["partial"] = True
|
|
1934
|
+
else:
|
|
1935
|
+
s.pop("partial", None)
|
|
1914
1936
|
# 收尾即清运行中态:stream/activity/live 只是过程快照,留着会让
|
|
1915
1937
|
# 前端把「已结束」的步骤仍当实时流渲染(也白占 run.json 体积)
|
|
1916
1938
|
s.pop("stream", None)
|