@tea-agent/loop-agent 0.29.2 → 0.30.0
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 +56 -24
- package/bin/agent-worker.js +0 -0
- package/dist/commands/client-recovery.js +2 -10
- package/dist/commands/init.js +1 -1
- package/dist/executors/dag-pi-executor.js +15 -4
- package/dist/executors/pi-executor.js +5 -3
- package/dist/executors/pi-playwright-cli-tool.js +9 -14
- package/dist/executors/pi-sdk-executor.js +16 -0
- package/dist/shared/operator/capabilities.js +9 -9
- package/dist/shared/pi-retry-settings.js +23 -0
- package/dist/worker/console/chat/pi-runtime.js +167 -41
- package/dist/worker/console/chat/routes.js +21 -2
- package/dist/worker/console/chat/runtime-context.js +50 -8
- package/dist/worker/console/chat/tool-preview.js +90 -0
- package/dist/worker/console/chat/turn-process.js +178 -0
- package/dist/worker/console/chat/usage.js +144 -16
- package/dist/worker/console/night-aux-ticker.js +141 -0
- package/dist/worker/console/operation-runner.js +21 -4
- package/dist/worker/console/operator-user-error.js +12 -0
- package/dist/worker/console/recovery-cta.js +6 -6
- package/dist/worker/console/routes.js +61 -0
- package/dist/worker/console/server.js +7 -0
- package/dist/worker/console/static/assets/index-D9gnJn_l.js +29 -0
- package/dist/worker/console/static/assets/index-rajoXwkM.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/night-prepare-result.js +118 -0
- package/dist/worker/scheduler/clock-install/win32-schtasks.js +70 -15
- package/dist/workflows/dag/backend-test-writer-completeness.js +398 -26
- package/dist/workflows/dag/dynamic-runtime/map.js +10 -4
- package/dist/workflows/dag/frontend-test-case-checklist.js +3 -3
- package/dist/workflows/dag/init-hybrid.js +247 -239
- package/dist/workflows/dag/node-execution.js +13 -2
- package/dist/workflows/dag/types.js +21 -7
- package/docs/templates/backend-test-dag.json +664 -460
- package/docs/templates/frontend-test-case-checklist.md +1 -1
- package/docs/templates/frontend-test-dag.generate-cases.prompt.md +1 -1
- package/docs/templates/frontend-test-dag.json +1 -1
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/frontend-test-dag.review-cases.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +6 -3
- package/harness.json +2 -2
- package/package.json +1 -1
- package/skills/playwright-cli/SKILL.md +1 -1
- package/skills/playwright-cli-case-generator/SKILL.md +1 -1
- package/dist/worker/console/static/assets/index-Cwx-ZVEQ.js +0 -29
- package/dist/worker/console/static/assets/index-Yyn3ynVv.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,52 +2,84 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
## [0.
|
|
5
|
+
## [0.30.0] - 2026-08-08
|
|
6
6
|
|
|
7
7
|
### 重点更新
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
9
|
+
- 后端测试生成引入 map-shard 拓扑,将大型单节点拆分为 plan + manifest-shell + map_agent,每个模块用例获得独立的 16K 输出预算,消除大文件多轮写入被截断或完整性误报的风险
|
|
10
|
+
- 修复 map_agent 单个子节点超出 token 限制导致后续所有子节点被错误级联跳过的问题,确保各模块测试用例独立生成互不干扰
|
|
11
|
+
- Completeness Gate 升级为返回结构化缺陷原因,帮助模型精准定位并修复格式问题,有效结束续写死循环
|
|
12
|
+
- 后端测试用例生成全面转向需求驱动覆盖,以产品需求为基准,API 契约作为场景补充,提升测试覆盖的精准度
|
|
13
|
+
|
|
14
|
+
### 新增
|
|
15
|
+
|
|
16
|
+
- 新增 plan 节点专属完整性评估器,仅校验 README 覆盖范围和模块索引,不再错误要求下游 map 子节点产出的文件
|
|
17
|
+
- 新增 `scripts/regen-backend-test-dag.mjs` 脚本,用于从 runtime builder 重建打包镜像,保持 runtime 与 packaged template 字节对齐
|
|
18
|
+
- Completeness Gate 的 `moduleStructurallyComplete` 新增结构化原因输出,明确指出空文件、标题层级错误、ID 格式非法或缺失必选章节等具体缺陷
|
|
19
|
+
- 新增 `looksLikeValidPythonModule` 校验逻辑,用于准确识别共享辅助代码和工厂函数,兼容包含合法不平衡分隔符的 fixture 字符串
|
|
11
20
|
|
|
12
21
|
### 改进
|
|
13
22
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
23
|
+
- N2/N3 提示词改为需求驱动覆盖:Coverage Scope 固定为 new-operation/full-contract,以产品需求为基准全覆盖,API 契约补充场景维度,并禁止使用导致用例降级的 `## 测试类` 分组
|
|
24
|
+
- dagDynamicExpansionChildTaskSchema 扩展 `writerOutcomePolicy` 和 `retryPolicy`,使 map_agent 子节点能正确声明实现产出并受 Completeness Gate 保护
|
|
25
|
+
- Completeness Gate 白名单泛化,通过识别子节点 ID 前缀准确匹配 map 分片子节点
|
|
26
|
+
- DAG 执行器模型矩阵统一路由至当前环境实际可用的 wizard-local 模型,避免因 API Key 缺失导致节点大面积失败
|
|
16
27
|
|
|
17
28
|
### 修复
|
|
18
29
|
|
|
19
|
-
- 修复
|
|
20
|
-
-
|
|
21
|
-
-
|
|
30
|
+
- 修复 map_agent 中单个子节点超出 `maxTokensPerCase` 被误当作全局预算耗尽,导致后续兄弟子节点全部被跳过(SKIPPED)的问题
|
|
31
|
+
- 修复 plan 节点 Completeness Gate 错误要求下游 map 子节点产出文件齐全,导致计划节点始终判定为不完整并陷入重试死循环的问题
|
|
32
|
+
- 修复 manifest shell 命令因正则反引号和转义符在多层 shell 解析中被破坏,导致确定性 manifest 节点执行报错的问题,现采用 base64 编码彻底规避 bash 转义干扰
|
|
33
|
+
- 修复 pytest-plan 资源检查因严格括号配平启发式算法,误判包含合法不平衡分隔符的 fixture 字符串为不完整的问题
|
|
22
34
|
|
|
23
|
-
## [0.29.
|
|
35
|
+
## [0.29.3] - 2026-08-07
|
|
24
36
|
|
|
25
37
|
### 重点更新
|
|
26
38
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
39
|
+
- 夜间任务控制台升级为引导式工作台,支持点选预约、审阅写入集与任务看板,并保留人工审批门禁
|
|
40
|
+
- 失败 DAG 默认改为从失败节点安全续跑,避免因服务波动无理由新建任务
|
|
41
|
+
- Operator Chat 支持内联展示工具执行过程,并改进 Pi 用量统计的准确解析与状态栏展示
|
|
42
|
+
- 增强 Pi 网关限流容错,遇到 429 状态码时自动按 60 秒间隔退避重试
|
|
31
43
|
|
|
32
44
|
### 新增
|
|
33
45
|
|
|
34
|
-
-
|
|
35
|
-
- 新增前端测试标准场景规范模板(frontend-test-standard-scenarios.v1.json)
|
|
36
|
-
- 前端测试独占节点支持 tools-only 写保护策略,在保留工具级写沙箱的同时跳过 Git 基线的硬失败限制
|
|
46
|
+
- 控制台新增前台辅助夜间任务调度开关,在控制台开启时可按间隔触发调度(仅前台有效,过夜仍需系统定时器)
|
|
37
47
|
|
|
38
48
|
### 改进
|
|
39
49
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
50
|
+
- 失败 DAG 恢复策略改为先诊断再从失败节点续跑,统一对齐系统提示、恢复入口与工具说明
|
|
51
|
+
- Operator Chat 将工具执行过程内联至消息时间线,支持折叠查看,切会话后可恢复工具记录
|
|
52
|
+
- Operator Chat 用量栏改进 Pi 用量解析,准确展示输入、输出、缓存读取与费用信息
|
|
53
|
+
- 注入上下文面板升级为更大可读面板,展示脱敏后的完整系统提示、技能正文与激活工具列表
|
|
44
54
|
|
|
45
55
|
### 修复
|
|
46
56
|
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
57
|
+
- 修复 Windows 计划任务因 /TR 参数超过 261 字符限制导致创建失败的问题,改用短 .cmd 路径绕过
|
|
58
|
+
- 修复 package-lock 中硬编码的镜像源地址导致发布链路 npm ci 失败的问题,统一指向官方源
|
|
59
|
+
- 修复 playwright-cli open 传入不支持的 --headless/--headed 参数报错的问题
|
|
60
|
+
- 修复 Pi 网关返回 429 限流时重试过快导致任务失败的问题,现自动识别并按 60 秒间隔退避
|
|
61
|
+
- 修复标准 DAG 节点将任务相对路径误当作仓库根路径读取,导致找文件超时的问题
|
|
62
|
+
- 修复 Operator Chat 重开会话时因 JSONL 丢失而静默新建会话 ID 导致会话不匹配的问题
|
|
63
|
+
- 修复夜间任务异步执行结果丢失及 agent-worker 可执行文件权限的问题
|
|
64
|
+
|
|
65
|
+
## [0.29.2] - 2026-08-07
|
|
66
|
+
|
|
67
|
+
### 重点更新
|
|
68
|
+
|
|
69
|
+
- 优化后端测试生成 writer 的续写重试机制,直接内嵌具体目标路径以恢复输出,不再依赖读取受限路径
|
|
70
|
+
- 修复三处完整性门禁(Completeness Gate)误报,避免将正确的测试生成误判为残缺写集
|
|
71
|
+
- 修复标准 Happy Path / `contract-pi` 等节点 source prompt:只暴露仓库相对可读路径(`.harness/tasks/<taskId>/source/...`),并明确禁止把 task-relative 的 `source/需求.md` 当工具读路径
|
|
72
|
+
|
|
73
|
+
### 改进
|
|
74
|
+
|
|
75
|
+
- 后端测试生成 writer(N2 MD / N5 pytest)在输出截断或残缺写集后,续写重试直接内嵌具体目标路径清单,不再依赖读取 `.harness/**` 路径
|
|
76
|
+
- 即使遇到结果为空或输出失败的异常,也会评估完整性门禁,将可恢复的部分写入升格为不完整写集,避免重试空转
|
|
77
|
+
|
|
78
|
+
### 修复
|
|
79
|
+
|
|
80
|
+
- 修复 README 正文提及的编号式文件名被误判为缺失模块的问题
|
|
81
|
+
- 修复覆盖率矩阵(Coverage Matrix)表头识别被覆盖率范围(Coverage Scope)数据行遮蔽的问题
|
|
82
|
+
- 修复合法多行 `def` 的 pytest 脚本被误判为截断的问题(真正截断现仍由括号配平计数准确捕获)
|
|
51
83
|
|
|
52
84
|
## [0.29.0] - 2026-08-07
|
|
53
85
|
|
package/bin/agent-worker.js
CHANGED
|
File without changes
|
|
@@ -18,16 +18,8 @@ export const BACKOFF_MS = [2000, 4000, 8000, 16000, 30000];
|
|
|
18
18
|
export const MAX_SESSION_RETRIES = 5;
|
|
19
19
|
/** Hard cap for OpenCode overflow compact+resume recoveries (≤ 2). */
|
|
20
20
|
export const MAX_OVERFLOW_RECOVERIES = 1;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
maxRetries: 5,
|
|
24
|
-
baseDelayMs: 3000,
|
|
25
|
-
provider: {
|
|
26
|
-
maxRetries: 0,
|
|
27
|
-
maxRetryDelayMs: 60000,
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
export const PI_RECOMMENDED_COMPACTION = { enabled: true };
|
|
21
|
+
import { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY, } from "../shared/pi-retry-settings.js";
|
|
22
|
+
export { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY };
|
|
31
23
|
/**
|
|
32
24
|
* Shared overflow phrase sources used by isContextOverflow and generated plugins.
|
|
33
25
|
* Keep English + Chinese intranet gateway messages in one place to avoid drift.
|
package/dist/commands/init.js
CHANGED
|
@@ -2217,7 +2217,7 @@ function buildTargetFeatureWorkflow(input) {
|
|
|
2217
2217
|
'- `taskKind: "backend-test"` selects the dedicated backend test DAG. Its Pi nodes analyze requirements, generate and review backend cases, generate pytest, and retrospect on results; shell gate/execution nodes enforce the review verdict and run the target project\'s pytest. The backend test templates (`backend-test-dag.json` and the `backend-test-dag.*.prompt.md` files) ship inside the loop-agent package as static references and are projected to target projects under the governance `templates/` directory.',
|
|
2218
2218
|
'- `taskKind: "knowledge-sync"` selects the Feature-scoped test-knowledge write-back DAG (collect → draft → validate → apply → pointer). Bind `featureId` in `task.json` (or hardConstraints / requirement text). It writes only under `features/<featureId>/…` after final verification evidence exists.',
|
|
2219
2219
|
'- `taskKind: "knowledge-graph-bootstrap"` selects the business knowledge-graph bootstrap DAG (preflight → inventory → propose → validate → review → gate → promote → materialize). AI writes only `knowledge/bootstrap/staging/**`; promote is merge-new-only.',
|
|
2220
|
-
'- `taskKind: "frontend-test"` selects the FE-test RAG DAG. It writes a traceable frontend RAG package and Markdown case manifest, then executes manifest cases serially with `playwright-cli` in isolated test environments and retains per-case evidence. It never generates pytest or Playwright source code. `frontendTest.maxCasesPerBatch` defaults to 20 (maximum 50); optional `maxTokensPerCase` and `maxTotalTokens` stop only later cases after a completed case\'s token usage is recorded, marking them `blocked: token-budget-exhausted`. Generated browser startup uses `playwright-cli open --browser=chrome
|
|
2220
|
+
'- `taskKind: "frontend-test"` selects the FE-test RAG DAG. It writes a traceable frontend RAG package and Markdown case manifest, then executes manifest cases serially with `playwright-cli` in isolated test environments and retains per-case evidence. It never generates pytest or Playwright source code. `frontendTest.maxCasesPerBatch` defaults to 20 (maximum 50); optional `maxTokensPerCase` and `maxTotalTokens` stop only later cases after a completed case\'s token usage is recorded, marking them `blocked: token-budget-exhausted`. Generated browser startup uses `playwright-cli open --browser=chrome <base-url>`; the generic playwright-cli skill is unchanged.',
|
|
2221
2221
|
"- Only eligible read-only Pi nodes (planner, scout, reviewer, verifier, closeout with no write-capable tool profile) receive the conservative automatic retry policy. Supervisor, implementer, writer, docs-only, dynamic, shell, static, and decision-gate nodes are not retried automatically. Eligible nodes cannot write repository files; the controller only records immutable attempt evidence under `.harness/dag-runs/<state>/<run-id>/<node-id>/attempt-<n>.json`.",
|
|
2222
2222
|
"",
|
|
2223
2223
|
"Use the package-backed public knowledge CLI for graph operations. Do not require target projects to run package-only kb runtime scripts:",
|
|
@@ -9,7 +9,7 @@ import { dagCommandPolicyAllows, resolveDagCommandPolicy, } from "../workflows/d
|
|
|
9
9
|
import { redactPromptForLog, truncateOutput, } from "../shared/output-truncation.js";
|
|
10
10
|
import { GitStatusUnavailableError, pathsChangedDuringRun, readGitStatusPorcelain, recoverRootNulArtifact, snapshotGitStatusPathFingerprints, snapshotGitStatusPorcelain, validateShellWriteGuard, } from "./shell-write-guard.js";
|
|
11
11
|
import { isWriterEmptyDiffRetryCandidate, INCOMPLETE_WRITE_SET_RETRY_CATEGORY, WRITER_EMPTY_DIFF_RETRY_CATEGORY, } from "../workflows/dag/retry-policy.js";
|
|
12
|
-
import { assessBackendTestMdWriterCompleteness, assessBackendTestPytestWriterCompleteness, classifyBackendTestWriterCompletenessFailure, isBackendTestCompletenessRetryCandidate, writeBackendTestWriterProgressArtifacts, } from "../workflows/dag/backend-test-writer-completeness.js";
|
|
12
|
+
import { assessBackendTestMdPlanCompleteness, assessBackendTestMdWriterCompleteness, assessBackendTestPytestPlanCompleteness, assessBackendTestPytestWriterCompleteness, assessBackendTestShardChildCompleteness, backendTestWriterProgressRoleForTask, classifyBackendTestWriterCompletenessFailure, isBackendTestCompletenessRetryCandidate, isBackendTestMdPlanTask, isBackendTestPytestPlanTask, isBackendTestShardChildTask, writeBackendTestWriterProgressArtifacts, } from "../workflows/dag/backend-test-writer-completeness.js";
|
|
13
13
|
import { redactSecrets, truncateUtf8Preview } from "../shared/preview.js";
|
|
14
14
|
export const DAG_PI_READONLY_TOOLS = ["read", "grep", "find", "ls"];
|
|
15
15
|
/** Bounded writer tools: no unrestricted bash; edit/write are policy-wrapped via SDK customTools. */
|
|
@@ -688,9 +688,20 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
688
688
|
if (writeGuardOk &&
|
|
689
689
|
isBackendTestCompletenessRetryCandidate(input.task)) {
|
|
690
690
|
try {
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
691
|
+
const writerRole = backendTestWriterProgressRoleForTask(input.task.id);
|
|
692
|
+
const progress = isBackendTestShardChildTask(input.task)
|
|
693
|
+
? await assessBackendTestShardChildCompleteness({
|
|
694
|
+
workspaceRoot: input.cwd,
|
|
695
|
+
role: writerRole === "pytest-generate" ? "pytest-generate" : "md-generate",
|
|
696
|
+
writeSet: input.task.writeSet ?? [],
|
|
697
|
+
})
|
|
698
|
+
: isBackendTestMdPlanTask(input.task)
|
|
699
|
+
? await assessBackendTestMdPlanCompleteness(input.cwd)
|
|
700
|
+
: isBackendTestPytestPlanTask(input.task)
|
|
701
|
+
? await assessBackendTestPytestPlanCompleteness(input.cwd)
|
|
702
|
+
: writerRole === "pytest-generate"
|
|
703
|
+
? await assessBackendTestPytestWriterCompleteness(input.cwd)
|
|
704
|
+
: await assessBackendTestMdWriterCompleteness(input.cwd);
|
|
694
705
|
await writeBackendTestWriterProgressArtifacts({
|
|
695
706
|
runDir: meta.runDir,
|
|
696
707
|
progress,
|
|
@@ -961,10 +961,12 @@ export function classifyPiFailure(input) {
|
|
|
961
961
|
if (input.exitCode === 0 && input.assistantText.trim())
|
|
962
962
|
return "success";
|
|
963
963
|
const combined = `${input.stderr}\n${input.stdout}`.toLowerCase();
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (/rate.?limit/.test(combined))
|
|
964
|
+
// Prefer explicit rate-limit / HTTP 429 before generic "limit" quota phrases.
|
|
965
|
+
// Chinese gateways often say "请求频率已达到限制" without English "rate limit".
|
|
966
|
+
if (/rate.?limit|too many requests|\b429\b|请求频率|频率.*限制|rpm|tpm/.test(combined))
|
|
967
967
|
return "rate-limit";
|
|
968
|
+
if (/quota|usage limit|reached.*limit|5\s*小时|5小时|insufficient_quota|billing/.test(combined))
|
|
969
|
+
return "quota";
|
|
968
970
|
if (/\bunauthorized\b|\bhttp\s*40[13]\b|invalid api key|authentication (?:failed|required|error)|auth(?:entication)? failed/.test(combined))
|
|
969
971
|
return "auth";
|
|
970
972
|
if (/unknown provider|unknown model|model.*unavailable|provider.*unavailable|\bunavailable\b|overloaded|capacity|temporarily unavailable/.test(combined))
|
|
@@ -197,21 +197,19 @@ function validateOpenArgs(args, baseUrl) {
|
|
|
197
197
|
for (const arg of args) {
|
|
198
198
|
assertNoControlMeta(arg);
|
|
199
199
|
assertNoSessionFlag(arg);
|
|
200
|
-
if (arg === "--browser=chrome"
|
|
200
|
+
if (arg === "--browser=chrome") {
|
|
201
201
|
flags.add(arg);
|
|
202
202
|
normalized.push(arg);
|
|
203
203
|
continue;
|
|
204
204
|
}
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (!normalized.includes("--headless"))
|
|
209
|
-
normalized.push("--headless");
|
|
205
|
+
// playwright-cli defaults to headless and does not accept --headless/--headed.
|
|
206
|
+
// Strip legacy flags so model/docs remnants never reach the binary.
|
|
207
|
+
if (arg === "--headless" || arg === "--headed") {
|
|
210
208
|
continue;
|
|
211
209
|
}
|
|
212
210
|
if (arg.startsWith("--browser=") ||
|
|
213
211
|
arg === "--browser") {
|
|
214
|
-
throw new PlaywrightCliPolicyError("open-browser-flags", "open must use --browser=chrome --headless
|
|
212
|
+
throw new PlaywrightCliPolicyError("open-browser-flags", "open must use --browser=chrome only (default headless; do not pass --headless/--headed)");
|
|
215
213
|
}
|
|
216
214
|
if (/^[a-z][a-z0-9+.-]*:/i.test(arg) || arg.startsWith("http")) {
|
|
217
215
|
url = arg;
|
|
@@ -219,12 +217,9 @@ function validateOpenArgs(args, baseUrl) {
|
|
|
219
217
|
}
|
|
220
218
|
throw new PlaywrightCliPolicyError("open-args", `unsupported open argument: ${arg}`);
|
|
221
219
|
}
|
|
222
|
-
if (!flags.has("--browser=chrome")
|
|
223
|
-
// controller injects required
|
|
224
|
-
|
|
225
|
-
normalized.unshift("--browser=chrome");
|
|
226
|
-
if (!flags.has("--headless"))
|
|
227
|
-
normalized.push("--headless");
|
|
220
|
+
if (!flags.has("--browser=chrome")) {
|
|
221
|
+
// controller injects required browser flag when missing from model args
|
|
222
|
+
normalized.unshift("--browser=chrome");
|
|
228
223
|
}
|
|
229
224
|
if (!url) {
|
|
230
225
|
throw new PlaywrightCliPolicyError("open-url-required", "open requires an absolute http(s) URL");
|
|
@@ -515,7 +510,7 @@ export function preparePlaywrightCliArgv(input, ctx) {
|
|
|
515
510
|
catch {
|
|
516
511
|
throw new PlaywrightCliPolicyError("goto-url-invalid", "goto requires a URL relative to the controller baseUrl");
|
|
517
512
|
}
|
|
518
|
-
const openLike = validateOpenArgs(["--browser=chrome",
|
|
513
|
+
const openLike = validateOpenArgs(["--browser=chrome", target], ctx.baseUrl);
|
|
519
514
|
return openLike[openLike.length - 1];
|
|
520
515
|
});
|
|
521
516
|
if (args.length !== 1) {
|
|
@@ -2,6 +2,7 @@ import { appendFile, mkdir } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { BoundedTextPreview, classifyPiFailure, createPiJsonlStreamCollector, DEFAULT_ABORT_GRACE_MS, DEFAULT_STALL_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, extractAssistantTextFromPiJson, } from "./pi-executor.js";
|
|
4
4
|
import { serializeSessionEvent } from "./pi-event-serializer.js";
|
|
5
|
+
import { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY, } from "../shared/pi-retry-settings.js";
|
|
5
6
|
let sdkSessionFactoryOverride;
|
|
6
7
|
let sdkImportOverrideForTests;
|
|
7
8
|
let sdkModuleOverrideForTests;
|
|
@@ -154,6 +155,7 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
154
155
|
const createAgentSession = sdk.createAgentSession;
|
|
155
156
|
const SessionManager = sdk.SessionManager;
|
|
156
157
|
const DefaultResourceLoader = sdk.DefaultResourceLoader;
|
|
158
|
+
const SettingsManager = sdk.SettingsManager;
|
|
157
159
|
const getAgentDir = sdk.getAgentDir;
|
|
158
160
|
const ModelRuntime = sdk.ModelRuntime;
|
|
159
161
|
const agentDir = shared?.agentDir ?? getAgentDir();
|
|
@@ -168,12 +170,25 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
168
170
|
const model = input.provider && input.model
|
|
169
171
|
? await resolveModel(modelRuntime, input.provider, input.model)
|
|
170
172
|
: undefined;
|
|
173
|
+
// Inject rate-limit-tolerant auto-retry settings for the session lifecycle.
|
|
174
|
+
// Prefer SettingsManager.inMemory so harness runs do not depend on ~/.pi or
|
|
175
|
+
// project .pi/settings.json being present/trusted.
|
|
176
|
+
const settingsManager = typeof SettingsManager?.inMemory === "function"
|
|
177
|
+
? SettingsManager.inMemory({
|
|
178
|
+
retry: {
|
|
179
|
+
...PI_RECOMMENDED_RETRY,
|
|
180
|
+
provider: { ...PI_RECOMMENDED_RETRY.provider },
|
|
181
|
+
},
|
|
182
|
+
compaction: { ...PI_RECOMMENDED_COMPACTION },
|
|
183
|
+
}, { projectTrusted: true })
|
|
184
|
+
: undefined;
|
|
171
185
|
const loader = new DefaultResourceLoader({
|
|
172
186
|
cwd: input.cwd,
|
|
173
187
|
agentDir,
|
|
174
188
|
noContextFiles: true,
|
|
175
189
|
noSkills: true,
|
|
176
190
|
noExtensions: true,
|
|
191
|
+
...(settingsManager ? { settingsManager } : {}),
|
|
177
192
|
appendSystemPromptOverride: (base) => [
|
|
178
193
|
...base,
|
|
179
194
|
input.appendSystemPrompt,
|
|
@@ -191,6 +206,7 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
191
206
|
resourceLoader: loader,
|
|
192
207
|
tools: input.toolNames,
|
|
193
208
|
modelRuntime,
|
|
209
|
+
...(settingsManager ? { settingsManager } : {}),
|
|
194
210
|
...(model ? { model } : {}),
|
|
195
211
|
...(input.thinking ? { thinkingLevel: input.thinking } : {}),
|
|
196
212
|
...(Array.isArray(input.customTools) && input.customTools.length > 0
|
|
@@ -776,7 +776,7 @@ export function buildOperatorCapabilitiesDocument() {
|
|
|
776
776
|
resultSchemaVersion: 1,
|
|
777
777
|
envelopeSchemaVersion: 1,
|
|
778
778
|
requiredErrorCodes: ["NOT_FOUND", "INVALID_INPUT"],
|
|
779
|
-
description: "DAG run handoff report by run id.",
|
|
779
|
+
description: "DAG run handoff report by run id. On failure, read primaryFailure.nodeId + primaryRecovery first; prefer dagRerunPlan from that node before standaloneTaskRerun or a new task.",
|
|
780
780
|
inputParams: [
|
|
781
781
|
{
|
|
782
782
|
name: "runId",
|
|
@@ -919,19 +919,19 @@ export function buildOperatorCapabilitiesDocument() {
|
|
|
919
919
|
resultSchemaVersion: 1,
|
|
920
920
|
envelopeSchemaVersion: 1,
|
|
921
921
|
requiredErrorCodes: ["INVALID_INPUT", "NOT_FOUND"],
|
|
922
|
-
description: "Read-only subgraph
|
|
922
|
+
description: "Read-only R1 subgraph plan (no mutation). Call before dagRerun. Prefer primaryFailure.nodeId from dagReport. If eligible=false (writer/decision/fingerprint), do not force execute — fall back to standaloneTaskRerun or same-task advance. Returns planHash required by execute.",
|
|
923
923
|
inputParams: [
|
|
924
924
|
{
|
|
925
925
|
name: "runId",
|
|
926
926
|
type: "string",
|
|
927
927
|
required: true,
|
|
928
|
-
description: "dag run id",
|
|
928
|
+
description: "completed failed|partial_failed parent dag run id",
|
|
929
929
|
},
|
|
930
930
|
{
|
|
931
931
|
name: "fromNode",
|
|
932
932
|
type: "string",
|
|
933
933
|
required: true,
|
|
934
|
-
description: "node id to rerun from",
|
|
934
|
+
description: "node id to rerun from (prefer failed/attention node from dagReport primaryFailure.nodeId)",
|
|
935
935
|
},
|
|
936
936
|
],
|
|
937
937
|
modelCallable: "always",
|
|
@@ -949,31 +949,31 @@ export function buildOperatorCapabilitiesDocument() {
|
|
|
949
949
|
"BINDING_DRIFT",
|
|
950
950
|
"INVALID_INPUT",
|
|
951
951
|
],
|
|
952
|
-
description: "Execute continuation
|
|
952
|
+
description: "Execute R1 continuation from effective node. Requires planHash from a fresh dagRerunPlan (eligible=true). Prefer this over new task / standaloneTaskRerun for provider flake and safe downstream failures. Human Gate required.",
|
|
953
953
|
inputParams: [
|
|
954
954
|
{
|
|
955
955
|
name: "runId",
|
|
956
956
|
type: "string",
|
|
957
957
|
required: true,
|
|
958
|
-
description: "dag run id",
|
|
958
|
+
description: "parent dag run id (same as dagRerunPlan)",
|
|
959
959
|
},
|
|
960
960
|
{
|
|
961
961
|
name: "fromNode",
|
|
962
962
|
type: "string",
|
|
963
963
|
required: true,
|
|
964
|
-
description: "node id
|
|
964
|
+
description: "node id matching the planned dagRerunPlan",
|
|
965
965
|
},
|
|
966
966
|
{
|
|
967
967
|
name: "planHash",
|
|
968
968
|
type: "string",
|
|
969
969
|
required: true,
|
|
970
|
-
description: "
|
|
970
|
+
description: "sha256 plan hash from dagRerunPlan (must match exactly)",
|
|
971
971
|
},
|
|
972
972
|
{
|
|
973
973
|
name: "reason",
|
|
974
974
|
type: "string",
|
|
975
975
|
required: true,
|
|
976
|
-
description: "
|
|
976
|
+
description: "why continue from this node (e.g. provider flake; resume from review-pi)",
|
|
977
977
|
},
|
|
978
978
|
],
|
|
979
979
|
modelCallable: "prepare-only",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recommended Pi auto-retry settings for gateway rate limits.
|
|
3
|
+
*
|
|
4
|
+
* Work-hours gateways often enforce rolling windows (e.g. 100 req / 10 min).
|
|
5
|
+
* Pi's built-in default (2s / 4s / 8s) exhausts before the window recovers;
|
|
6
|
+
* a 60s base delay yields 1m → 2m → 4m … and tolerates transient 429s.
|
|
7
|
+
*
|
|
8
|
+
* Used by:
|
|
9
|
+
* - project / user `.pi` settings merge (`client-recovery`)
|
|
10
|
+
* - loop-agent Pi SDK executor (injected SettingsManager, independent of ~/.pi)
|
|
11
|
+
*/
|
|
12
|
+
export const PI_RECOMMENDED_RETRY = {
|
|
13
|
+
enabled: true,
|
|
14
|
+
maxRetries: 5,
|
|
15
|
+
/** First retry wait; subsequent attempts use exponential backoff. */
|
|
16
|
+
baseDelayMs: 60_000,
|
|
17
|
+
provider: {
|
|
18
|
+
/** Disable nested SDK provider retries; outer auto-retry owns the budget. */
|
|
19
|
+
maxRetries: 0,
|
|
20
|
+
maxRetryDelayMs: 60_000,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export const PI_RECOMMENDED_COMPACTION = { enabled: true };
|