@whyshu/argent-engine 0.7.5 → 0.7.7
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/MANIFEST.json +39 -35
- package/engine/VERSION +1 -1
- package/engine/core/contracts/argent.contracts.json +12 -4
- package/engine/scripts/appointment-create.mjs +37 -2
- package/engine/scripts/appointment-worker.mjs +72 -15
- package/engine/scripts/appointment_grab.py +141 -20
- package/engine/scripts/argent-machine-setup.mjs +118 -0
- package/engine/scripts/argent-runner.mjs +63 -10
- package/engine/scripts/capability-packs.lock.json +54 -51
- package/engine/scripts/ci-run.sh +14 -2
- package/engine/scripts/collect-evidence.mjs +1 -1
- package/engine/scripts/customer-preflight.py +33 -0
- package/engine/scripts/flow-auto-retry.py +3 -0
- package/engine/scripts/flow-requirements.json +23 -13
- package/engine/scripts/flow-session-retention.mjs +88 -0
- package/engine/scripts/flow-trigger-wake.mjs +13 -1
- package/engine/scripts/flow-trigger.sh +2 -1
- package/engine/scripts/flow_config.py +4 -0
- package/engine/scripts/flow_failure.py +9 -4
- package/engine/scripts/flow_preflight.py +30 -0
- package/engine/scripts/flow_retry.py +22 -7
- package/engine/scripts/gen-agent-bundles.mjs +1 -1
- package/engine/scripts/gen-preset-rows.mjs +2 -2
- package/engine/scripts/invoice_lip_runner.py +30 -2
- package/engine/scripts/invoice_stageb.py +101 -0
- package/engine/scripts/login-readiness.py +220 -0
- package/engine/scripts/ml-oauth-setup.mjs +1 -1
- package/engine/scripts/orchestrator.py +416 -30
- package/engine/scripts/probe_ml_site.py +103 -0
- package/engine/scripts/release-argent.sh +26 -2
- package/engine/skills/argent-machine-onboarding/SKILL.md +143 -0
- package/engine/skills/internal.json +4 -0
- package/engine/skills/ml-full-appointment/SKILL.md +32 -13
- package/engine/skills/ml-invoice/SKILL.md +1 -1
- package/engine/skills/ml-invoice-xzh/SKILL.md +2 -2
- package/engine/version.mjs +1 -1
- package/engine/verticals/_engine-words.baseline.txt +42 -23
- package/engine/verticals/mercadolibre/flows.json +9 -5
- package/engine/verticals/mercadolibre/manifest.json +5 -4
- package/engine/verticals/mercadolibre/prompts/daily-report.failure.md +1 -1
- package/engine/verticals/mercadolibre/prompts/daily-report.md +7 -10
- package/package.json +1 -1
- package/engine/scripts/ml-ads.mjs +0 -181
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"缺(?:[失了少]|\\s)|未填|为空|空值|missing|不存在|未就绪|闸门|校验不通过|需运营|等运营|请补|路由守卫|货代.*[≠!=]|不匹配|非.{0,6}货代|选错|人工",
|
|
44
44
|
"未授权|未登录|登录失效|登录墙|login_expired|无权限|permission",
|
|
45
45
|
"SKU.{0,40}在店铺",
|
|
46
|
-
"
|
|
46
|
+
"疑似缺陷⑰|bad_shipment_id|货件号不可解析"
|
|
47
47
|
]
|
|
48
48
|
},
|
|
49
49
|
"pageFlaky": {
|
|
@@ -75,6 +75,15 @@
|
|
|
75
75
|
"opsAction": {
|
|
76
76
|
"_why": "失败备注末尾给**运营**的下一步提示(Rule R,2026-09-21 用户口径:「在 FULL备注时加上一句让运营改完之后勾选就绪,这样运营看到提示就知道如何做了」)。只在流程开了 `pick.releaseReDispatch` 时追加(关着还写「勾了就绪会自动跑」= 说谎);`{release}` = 注册表里的放行字段名(pick.release.field)。只对 **needsHuman 类**失败追加:retryable(网络类)脚本自重试,写这句是噪音;needs_agent(门户业务类/需裁定)勾了就绪也不会跑,写这句会误导。",
|
|
77
77
|
"text": "改好后勾「{release}」即可,系统下一轮会自动重跑(无需通知运维)"
|
|
78
|
+
},
|
|
79
|
+
"appointmentPageNotReady": {
|
|
80
|
+
"_why": "2026-10-21 用户口径:预约页未渲染日历(div.day=0)属于页面慢/未就绪,后续刷新可恢复;应自动续跑,不直接判人工。货件号可疑/疑似缺陷⑰仍归 needsHuman。",
|
|
81
|
+
"patterns": [
|
|
82
|
+
"预约页形态异常",
|
|
83
|
+
"未渲染出日历",
|
|
84
|
+
"div\\.day\\s*命中\\s*0",
|
|
85
|
+
"div\\.day.*可用=0"
|
|
86
|
+
]
|
|
78
87
|
}
|
|
79
88
|
},
|
|
80
89
|
"_requiredRoles": {
|
|
@@ -148,7 +157,7 @@
|
|
|
148
157
|
},
|
|
149
158
|
"minPlan": "enterprise",
|
|
150
159
|
"pack": {
|
|
151
|
-
"version": "1.2.
|
|
160
|
+
"version": "1.2.183",
|
|
152
161
|
"scripts": [
|
|
153
162
|
"scripts/wenyao-invoice-writeback.mjs",
|
|
154
163
|
"scripts/invoice_lip_runner.py",
|
|
@@ -185,7 +194,7 @@
|
|
|
185
194
|
"缺产品图缺口文案统一(用户口径 2026-10-13):另一条发票链此前把**表头名**当缺口文案,现统一为「缺产品图(运营补)」(两条链同句),表头名改记入 notes 溯源;落点该链填报器的 image 分支;测试同步断言(含\"不再是表头名\")。",
|
|
186
195
|
"**W7.7(2026-10-14,尾巴收口:智能体标题去掉「角色」字样 + 死指针改指新落点)**:本批改了 `pack.engine` 里的两件(`agents/catalog.json` 的两个 `title`、`configs/hr.json` 的 label/help)与本注册表自身的一处(`hr` 设置面板的 `hint`)⇒ 清单本身变了、且每个包的 tgz 都带一份注册表投影 ⇒ 按「内容变 ⇒ 版本必变(含无 flow 的包)」四个能力包一起提版本并重盖章。口径:**角色 = DSH preset**,Argent 侧不复述角色清单(会话身份 ⇒ 官方「Agent 预设」区;安装 / 升级 ⇒ 设置 → Argent →「我的智能体」)。⛔ 本能力自身的流程 / 提示词 / 脚本一字未改。",
|
|
187
196
|
"**1.2.160(2026-10-14,P1-B `--no-wake` + 外部时序运营文案)**:共用引擎变更(`scripts/flow-trigger.sh` 新增 `--no-wake` —— 只认领 + 只写台账、不唤起会话;`scripts/lib/schedule-mechanism.mjs` 的 native 静态指令 `promptCommand` 带上该开关(宿主排程那条命令);`scripts/flow_triage.py` 的外部时序阻塞文案补齐两要素 —— 自动再试节奏(N 取声明面 `externalTiming.cooldownMin`)+ 人工接管方式(取消勾选放行位即停取);`scripts/run-tests.sh` 的干净树自检按判据 `[ -d .git ]` 跳过「需要 git 索引」的用例)⇒ 版本随引擎前进。本能力自身:发票链运行器的表字段文案同批更新(同一句、纯文本版),两份发票技能文档同步。⚠️ 盖章说明:本版内容在同一工作树里经过**二次收口**(先落实现,再按 `vertical-lint` 的 engine-words 判据改写文案 —— ⛔ **不抬基线**)⇒ 以**最终内容**盖章;版本号 1.2.160 从未发布过,不存在「内容变了版本没动」的对外窗口。",
|
|
188
|
-
"0.6.0 发版:平台 VERSION 0.4.0→0.6.0、契约 argentVersion 同步、宿主窗口晋级 0.2.0-rc.2;agent bundle BUNDLE_VERSION 0.1.0→0.1.1(内容随\"标题去角色\"变化,避免同版本换字节)。",
|
|
197
|
+
"历史记录:0.6.0 发版:平台 VERSION 0.4.0→0.6.0、契约 argentVersion 同步、宿主窗口晋级 0.2.0-rc.2;agent bundle BUNDLE_VERSION 0.1.0→0.1.1(内容随\"标题去角色\"变化,避免同版本换字节)。",
|
|
189
198
|
"0.7.0 W2:流程声明补 `runner`(三条流程)+ 门禁 flow-runner-declared(脚本须存在、args 须带 {ticket})。",
|
|
190
199
|
"0.7.0 W2:daily-report 补 runner 声明(ticketless)⇒ 全部流程 runner 齐;门禁收紧到\"0 未声明\"。",
|
|
191
200
|
"0.7.0 W2:`runner` 登记进 flow_config 顶层直通段与 _derive(否则 --json 不透出 ⇒ CLI 读不到声明)。",
|
|
@@ -193,7 +202,7 @@
|
|
|
193
202
|
"0.7.0 W8/S1:路径统一(22 个脚本改走唯一判据)触及本包成员 ⇒ 按纪律提版重盖章。",
|
|
194
203
|
"0.6.1:引擎版本(VERSION)随包内容 ⇒ 按纪律提版重发。",
|
|
195
204
|
"0.7.0:修正脚本里指向不存在命令的文案(init-ml / install-license / self-sign-license / vertical-lint)⇒ 包内容变,按纪律提版。",
|
|
196
|
-
"0.7.1:版本线提升(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
205
|
+
"历史记录:0.7.1:版本线提升(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
197
206
|
"0.7.2:修 `flow_config.py --list --json`(忽略 --json 的契约违背)⇒ 包内容变,按纪律提版。"
|
|
198
207
|
],
|
|
199
208
|
"hostAssets": {
|
|
@@ -251,7 +260,7 @@
|
|
|
251
260
|
],
|
|
252
261
|
"minPlan": "enterprise",
|
|
253
262
|
"pack": {
|
|
254
|
-
"version": "1.3.
|
|
263
|
+
"version": "1.3.189",
|
|
255
264
|
"scripts": [
|
|
256
265
|
"scripts/poll-fulfillment.sh",
|
|
257
266
|
"scripts/orchestrator.py",
|
|
@@ -285,7 +294,7 @@
|
|
|
285
294
|
"修 flow_retry.py 的**死守卫**(2026-10-13):清号守卫原写 `one(row.get(f_ship))` 而本文件无 `one()` 定义 ⇒ NameError 被 `except Exception` 静默吞 ⇒ 已有货件号也会被清号重置(红线从未生效);改用唯一取值助手 `first()` 并去掉吞噬;新门禁 scripts/tests/flow-retry-guard4.test.py(15 断言,含两条有牙)。",
|
|
286
295
|
"**W7.7(2026-10-14,尾巴收口:智能体标题去掉「角色」字样 + 死指针改指新落点)**:本批改了 `pack.engine` 里的两件(`agents/catalog.json` 的两个 `title`、`configs/hr.json` 的 label/help)与本注册表自身的一处(`hr` 设置面板的 `hint`)⇒ 清单本身变了、且每个包的 tgz 都带一份注册表投影 ⇒ 按「内容变 ⇒ 版本必变(含无 flow 的包)」四个能力包一起提版本并重盖章。口径:**角色 = DSH preset**,Argent 侧不复述角色清单(会话身份 ⇒ 官方「Agent 预设」区;安装 / 升级 ⇒ 设置 → Argent →「我的智能体」)。⛔ 本能力自身的流程 / 提示词 / 脚本一字未改。",
|
|
287
296
|
"**1.3.165(2026-10-14,P1-B `--no-wake` + 外部时序运营文案)**:共用引擎变更(`scripts/flow-trigger.sh` 新增 `--no-wake` —— 只认领 + 只写台账、不唤起会话;`scripts/lib/schedule-mechanism.mjs` 的 native 静态指令 `promptCommand` 带上该开关(宿主排程那条命令);`scripts/flow_triage.py` 的外部时序阻塞文案补齐两要素 —— 自动再试节奏(N 取声明面 `externalTiming.cooldownMin`)+ 人工接管方式(取消勾选放行位即停取);`scripts/run-tests.sh` 的干净树自检按判据 `[ -d .git ]` 跳过「需要 git 索引」的用例)⇒ 版本随引擎前进。本能力自身:流程 / 提示词 / 脚本一字未改(仅注册表投影与共用引擎前进)。⚠️ 盖章说明:本版内容在同一工作树里经过**二次收口**(先落实现,再按 `vertical-lint` 的 engine-words 判据改写文案 —— ⛔ **不抬基线**)⇒ 以**最终内容**盖章;版本号 1.3.165 从未发布过,不存在「内容变了版本没动」的对外窗口。",
|
|
288
|
-
"0.6.0 发版:平台 VERSION 0.4.0→0.6.0、契约 argentVersion 同步、宿主窗口晋级 0.2.0-rc.2;agent bundle BUNDLE_VERSION 0.1.0→0.1.1(内容随\"标题去角色\"变化,避免同版本换字节)。",
|
|
297
|
+
"历史记录:0.6.0 发版:平台 VERSION 0.4.0→0.6.0、契约 argentVersion 同步、宿主窗口晋级 0.2.0-rc.2;agent bundle BUNDLE_VERSION 0.1.0→0.1.1(内容随\"标题去角色\"变化,避免同版本换字节)。",
|
|
289
298
|
"0.7.0 W2:流程声明补 `runner`(三条流程)+ 门禁 flow-runner-declared(脚本须存在、args 须带 {ticket})。",
|
|
290
299
|
"0.7.0 W2:daily-report 补 runner 声明(ticketless)⇒ 全部流程 runner 齐;门禁收紧到\"0 未声明\"。",
|
|
291
300
|
"0.7.0 W2:`runner` 登记进 flow_config 顶层直通段与 _derive(否则 --json 不透出 ⇒ CLI 读不到声明)。",
|
|
@@ -293,7 +302,7 @@
|
|
|
293
302
|
"0.7.0 W8/S1:路径统一(22 个脚本改走唯一判据)触及本包成员 ⇒ 按纪律提版重盖章。",
|
|
294
303
|
"0.6.1:引擎版本(VERSION)随包内容 ⇒ 按纪律提版重发。",
|
|
295
304
|
"0.7.0:修正脚本里指向不存在命令的文案(init-ml / install-license / self-sign-license / vertical-lint)⇒ 包内容变,按纪律提版。",
|
|
296
|
-
"0.7.1:版本线提升(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
305
|
+
"历史记录:0.7.1:版本线提升(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
297
306
|
"0.7.2:修 `flow_config.py --list --json`(忽略 --json 的契约违背)⇒ 包内容变,按纪律提版。"
|
|
298
307
|
],
|
|
299
308
|
"hostAssets": {
|
|
@@ -331,7 +340,7 @@
|
|
|
331
340
|
"minPlan": "pro",
|
|
332
341
|
"_minPlanNote": "**2026-10-09(变现分层落地)**:`enterprise` → **`pro`**。判据 = 用户已确认的产品模型「分界线 = **价值更高 / 风险更高 ⇒ 企业版**」:日报是**只读**能力(官方 API 只读查询 + 生成推送,⛔ 不写客户任何系统、⛔ 不动浏览器),与 `full`(箱唛/写操作)、`invoice`(发票/门户写操作)不同档 ⇒ 归 **Pro**。⛔ 只改**档位下限**,⛔ 不改流程行为、⛔ 不改价格口径;`daily-report` 的能力包版本随引擎前进(见 pack.version)。已装机器**不因本改动被卸载/停用**(缩权只影响后续下发,见 entitlements.plans._why 与 docs 策划案 §4.4)。",
|
|
333
342
|
"pack": {
|
|
334
|
-
"version": "1.1.
|
|
343
|
+
"version": "1.1.180",
|
|
335
344
|
"scripts": [
|
|
336
345
|
"scripts/daily-report.mjs",
|
|
337
346
|
"scripts/daily-report.sh",
|
|
@@ -359,7 +368,7 @@
|
|
|
359
368
|
"修 flow_retry.py 的**死守卫**(2026-10-13):清号守卫原写 `one(row.get(f_ship))` 而本文件无 `one()` 定义 ⇒ NameError 被 `except Exception` 静默吞 ⇒ 已有货件号也会被清号重置(红线从未生效);改用唯一取值助手 `first()` 并去掉吞噬;新门禁 scripts/tests/flow-retry-guard4.test.py(15 断言,含两条有牙)。",
|
|
360
369
|
"**W7.7(2026-10-14,尾巴收口:智能体标题去掉「角色」字样 + 死指针改指新落点)**:本批改了 `pack.engine` 里的两件(`agents/catalog.json` 的两个 `title`、`configs/hr.json` 的 label/help)与本注册表自身的一处(`hr` 设置面板的 `hint`)⇒ 清单本身变了、且每个包的 tgz 都带一份注册表投影 ⇒ 按「内容变 ⇒ 版本必变(含无 flow 的包)」四个能力包一起提版本并重盖章。口径:**角色 = DSH preset**,Argent 侧不复述角色清单(会话身份 ⇒ 官方「Agent 预设」区;安装 / 升级 ⇒ 设置 → Argent →「我的智能体」)。⛔ 本能力自身的流程 / 提示词 / 脚本一字未改。",
|
|
361
370
|
"**1.1.158(2026-10-14,P1-B `--no-wake` + 外部时序运营文案)**:共用引擎变更(`scripts/flow-trigger.sh` 新增 `--no-wake` —— 只认领 + 只写台账、不唤起会话;`scripts/lib/schedule-mechanism.mjs` 的 native 静态指令 `promptCommand` 带上该开关(宿主排程那条命令);`scripts/flow_triage.py` 的外部时序阻塞文案补齐两要素 —— 自动再试节奏(N 取声明面 `externalTiming.cooldownMin`)+ 人工接管方式(取消勾选放行位即停取);`scripts/run-tests.sh` 的干净树自检按判据 `[ -d .git ]` 跳过「需要 git 索引」的用例)⇒ 版本随引擎前进。本能力自身:业务线内的日报**静态轮次指令**(`prompts/daily-report.scheduled.md`)更新为「P1-B 已落地」的现状(该静态指令本身就要求带 `--no-wake`)。⚠️ 盖章说明:本版内容在同一工作树里经过**二次收口**(先落实现,再按 `vertical-lint` 的 engine-words 判据改写文案 —— ⛔ **不抬基线**)⇒ 以**最终内容**盖章;版本号 1.1.158 从未发布过,不存在「内容变了版本没动」的对外窗口。",
|
|
362
|
-
"0.6.0 发版:平台 VERSION 0.4.0→0.6.0、契约 argentVersion 同步、宿主窗口晋级 0.2.0-rc.2;agent bundle BUNDLE_VERSION 0.1.0→0.1.1(内容随\"标题去角色\"变化,避免同版本换字节)。",
|
|
371
|
+
"历史记录:0.6.0 发版:平台 VERSION 0.4.0→0.6.0、契约 argentVersion 同步、宿主窗口晋级 0.2.0-rc.2;agent bundle BUNDLE_VERSION 0.1.0→0.1.1(内容随\"标题去角色\"变化,避免同版本换字节)。",
|
|
363
372
|
"0.7.0 W2:流程声明补 `runner`(三条流程)+ 门禁 flow-runner-declared(脚本须存在、args 须带 {ticket})。",
|
|
364
373
|
"0.7.0 W2:daily-report 补 runner 声明(ticketless)⇒ 全部流程 runner 齐;门禁收紧到\"0 未声明\"。",
|
|
365
374
|
"0.7.0 W2:`runner` 登记进 flow_config 顶层直通段与 _derive(否则 --json 不透出 ⇒ CLI 读不到声明)。",
|
|
@@ -367,7 +376,7 @@
|
|
|
367
376
|
"0.7.0 W8/S1:路径统一(22 个脚本改走唯一判据)触及本包成员 ⇒ 按纪律提版重盖章。",
|
|
368
377
|
"0.6.1:引擎版本(VERSION)随包内容 ⇒ 按纪律提版重发。",
|
|
369
378
|
"0.7.0:修正脚本里指向不存在命令的文案(init-ml / install-license / self-sign-license / vertical-lint)⇒ 包内容变,按纪律提版。",
|
|
370
|
-
"0.7.1:版本线提升(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
379
|
+
"历史记录:0.7.1:版本线提升(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
371
380
|
"0.7.2:修 `flow_config.py --list --json`(忽略 --json 的契约违背)⇒ 包内容变,按纪律提版。"
|
|
372
381
|
],
|
|
373
382
|
"hostAssets": {
|
|
@@ -433,7 +442,7 @@
|
|
|
433
442
|
}
|
|
434
443
|
]
|
|
435
444
|
},
|
|
436
|
-
"version": "0.1.
|
|
445
|
+
"version": "0.1.84",
|
|
437
446
|
"_note": [
|
|
438
447
|
"能力包版本(语义化):改了该能力的流程/提示词/脚本/技能 ⇒ 必须提版本并重新打包 |**0.1.40(2026-10-04,角色退役·墓碑)**:包内容变更(`presets/argent-hr-office/agent.cordis.yml` 的 persona 改成退役告知、`preset.yml` 的 name/description 标注「已退役」)—— 角色 `hr-office` 已于 2026-10-04 退役(方案 §3.3.1:gateway.json 条目与 install-dsh.sh 安装期入口已撤),**本能力包保留**(装机内容不变、仍可安装;入口 preset 作为 web 老会话的墓碑保留)⇒ 版本随内容前进。|**0.1.41(2026-10-09,版本随内容前进)**:`0.1.40` **发布之后其字节被原地覆盖过一次**(服务端清单 `sha256` `15cf4f43…` → `058ccead…` 而 `version` 原地不动,见 whyshu-svr `docs/ops/deploy-fe02231-packs-align.md`)⇒ 同一个 URL 下的内容变过 ⇒ \"内容变 ⇒ 版本变\"这条不变式被破坏,客户机无法靠版本号判断自己拿到的是哪一份。本次**只提版本、包内容一字未改**(重盖章后内容摘要与 0.1.40 时逐字相同)⇒ `hr-0.1.41.tgz` 是该内容**首次以某个版本号**对外发布。⛔ 未改共用引擎、未新增运行期文件,故 `pack.engine` 清单不变。|**0.1.42(2026-10-09,配置地基:配置目录 + 通用读取器 —— hr 包内容随之改变)**:本能力**无 flow**,按 `pack.engine` 的分发规矩**不背共用引擎**(`files` 仍只有 `presets/argent-hr-office/**` 三件,`integrity` 逐字节未变)。但**每个包的 tgz 里都带一份注册表投影**(`scripts/flow-requirements.json` 的引擎级段),而本批改了它的 `pack.engine`(新增 `core/lib/config-catalog.mjs` 与 5 个 `configs/*.json`)⇒ **hr 的包字节变了** ⇒ 按「内容变 ⇒ 版本必变(含无 flow 的包)」这条不变式**必须一起提版本**,否则会出现\"同一个版本号 0.1.41 对应两份不同字节\"(这正是 `release-argent.sh --check-dl` 的能力包档当场抓到的形态:`✗ hr: 版本相同(0.1.41)但内容摘要不同 —— 仓库 ad3599c5 vs 线上 09e1fef9`;提版后该条转为 ⚠「发布在途」,不再判红)。⛔ 本轮 hr 的**能力内容一字未改**。|**0.1.44(2026-10-09,预约失败判定链 + 口径统一)**:共用引擎 `scripts/flow_failure.py` 变更(新增 `canonical_reason()` 与 `--case` —— 打印某 case 的**规范失败原因**:优先读台账 `failed.reason`,老台账回落到 `error` 去身份前缀;给角色/运维一个**逐字照抄**的取数口,杜绝\"表备注写 A、账本写 B\")⇒ 所有能力包的注册表投影同批变化。本能力自身:⛔ 本能力内容一字未改;但 `flow_failure.py` 在 `pack.engine` 内,且包内 `scripts/flow-requirements.json` 投影**含 `failureClasses`**(本次新增的两条 needsHuman 判据就在其中)⇒ **包字节变了**,按「内容变 ⇒ 版本必变(含无 flow 的包)」这条不变式必须一起提版本。(⚠️ **盖章说明**:本版内容在同一工作树里经过**多轮收口**(先落实现,再按 vertical-lint 的引擎业务词约束改注册表文案、再收口文案)⇒ 以**最终内容**盖章;版本号从未发布过,不存在\"内容变了版本没动\"的对外窗口。)|**0.1.45(2026-10-09,三处生产实测收口:票不许静默消失 · 失败必写原因+动作 · 发票流程 Rule R)**:共用引擎变更 ⇒ 四个能力包一起提版本并重盖章。① `scripts/flow_triage.py`:判定改为**有效末条事件**(`INFO_EVENTS = phase/wake/policy_apply/policy_new` 回退到最近一条状态性事件;`failed_ack` = 有意不动且**不参与回退**)、`blocked` 从「静默 pass」改为可见的 `needs_human`,并加**循环后对账兜底**(任何未归桶的 case ⇒ `needs_human`,kind=unclassified)——修的是客户机生产实测 P0(某发票票):其尾部是 `blocked → phase` ⇒ 分诊摘要全 0、运营勾了放行也不派发、且**不在「需人工」里**(静默卡死)。② `scripts/flow_failure.py`:新增运营文案**唯一来源** `ops_message()`(`结论:<业务类|系统类|我方缺陷类>|<原因>|动作:…`,≤300 字)与 `release_hint()`(放行位/提示的**唯一实现**:变体与**顶层** pick 都认;`orchestrator._ops_action_hint` 改为委托它);③ **新增** `scripts/invoice_fail_closeout.py`(属**发票能力专属脚本** `capabilities.invoice.pack.scripts`,⛔ 不进共用引擎 —— 它只写发票那一列):失败/成功收口 —— 把**同一串**写进表「发票错误信息」与台账(`detail` 逐字相同、`error` 放规范原因),取不到原因 ⇒ **exit 2 拒绝执行**,代码级异常归「我方缺陷类」且**不回原文**,成功 ⇒ `--clear-error` 清空;④ `scripts/flow_return_to_ops.py`:缺口回填文案改用同一来源(表字段 == 台账 `detail`);④b `scripts/flow_config.py`:归一化的 `guards` 增**标量透传**(`writer`=写手路径、`errorField`=失败原因列 —— 不透出就只能各自把路径/列名写死,那正是「判据第二份」与业务词泄漏进引擎的来源);⑤ 业务线声明(`verticals/<id>/flows.json`):两个发票流程新增 `pick.release` / `pick.releaseReDispatch`(放行位 = **各自的门控列**「发票就绪」;上限 3 / 冷却 30)与 `guards.errorField`。本能力自身:⛔ 内容一字未改(无 flow ⇒ 不背共用引擎);但包内注册表投影含 `failureClasses`/`pack.engine` ⇒ 包字节变了,按「内容变 ⇒ 版本必变(含无 flow 的包)」一起提版本。|**0.1.46(2026-10-09,配置「生效来源」可见化 + 冲突告警,批 1)**:本能力无 flow ⇒ **不背共用引擎**(`files` 仍只有 3 件 preset,逐字节未变);但包内注册表投影含 `pack.engine` ⇒ 包字节变了,按「内容变 ⇒ 版本必变(含无 flow 的包)」一起提版本(同 `0.1.42` 先例)。本能力内容一字未改。共用引擎 `core/lib/config-catalog.mjs` 新增**纯函数** `resolveEffective()`(按规范链算「谁赢了 / 谁被压住」)、密钥脱敏 `redactSecret()` 与读取器 `effectiveOf()/levelsOf()`,并新增只读 CLI `scripts/config-explain.mjs`(`--key/--all/--list/--json`)⇒ 能力包内容变化,按纪律提版本并重盖章。⛔ **零行为变化**:读取器的取值路径(`resolveOne`)与任何 `todayChain` 一个字节都没改,只是「如实展示」;同批回归 `scripts/tests/config-effective.test.mjs`(63 断言)+ 端点 / 页面两条既有回归扩测。|**0.1.47(2026-10-09,同批收口:脱敏闸幂等)**:只读 CLI `scripts/config-explain.mjs` 的\"再脱敏\"兜底闸原先**无条件**执行 ⇒ `❰已设置(长度N)❱` 里的 N 变成**占位串自己的长度**(恒 11,实测),而用户正是拿它比对\"密钥换没换\" ⇒ 已改为**幂等**(含被压住的那级)并补回归;引擎面同一份 CLI 变了 ⇒ 包内容变化,按纪律再提版本并重盖章。本能力自身内容一字未改。|**0.1.49(2026-10-10,A 批:按智能体安装清单 —— 客户从「智能体库」选一个装,只装它的东西)**:① **`pack.engine` 新增两项**:`agents/catalog.json`(L0 货架声明:agent → capability + flows 子集 + 角色 + 插件/技能/集成/凭据槽)与 `scripts/argent-agents.mjs`(声明解析/校验/清单构造;安装器与门禁**共用一份**判据)⇒ 引擎清单与每包内的注册表投影都变了(**无 flow 的 hr 也变**,按「内容变 ⇒ 版本必变(含无 flow 的包)」这条不变式一起提版本);② **共用引擎行为变更**(`install-argent.mjs` / `scripts/lib/preset-install.mjs` / `scripts/argent-upgrade.mjs`):新增 `--agent <id>[,<id>…]` / `--list-agents` / `--internal-roles` / `--capability remove <id>`(写前备份 + 审计);preset **行落盘集合**改为「按清单收窄」(`--agent` ⇒ 恒装 + 本智能体角色 + 已装集;默认 ⇒ 客户可见集 + 已装集;**内部角色默认不落**);升级器按 `<dshHome>/argent/agents.json` **重新施加**同一集合(否则一次升级就把收窄还原);**未给 `--agent` 时 CLI 契约与计划清单输出与改动前逐字一致**(差分回归钉住);③ 本能力自身:**新增活的 HR 角色** `presets/argent-hr/`(招聘支持 / 员工关系 / PPT 与文档产出 / 员工隐私严格保密)+ `pack.presets` 收它 ⇒ 本能力从「只有墓碑」变成「有入口」;设置页入口字段由 `hrOfficeRole`(hr-office) 改为 `hrRole`(argent-hr),旧角色改用 `hrOfficeTombstone` 字段如实报告(⛔ 墓碑不复活、⛔ 不当入口,仍随包落位供老会话 agentPreset 外键解析)。回归:`scripts/tests/agent-catalog.test.mjs`(110 断言,七条判据:只落本智能体 / 子集过滤 / 向后兼容差分 / 未知输入零写入 / remove 备份+审计 / 目录 schema / 内部角色默认不落)。|**0.1.50(2026-10-10,同批二次收口:宿主资产声明 + 门禁)**:同一工作树里第二次收口(先落 `--agent` 实现并盖章,再补 `capabilities.<c>.pack.hostAssets`(**宿主侧资产声明位**:定时器/登录自启/桌面项)与它的门禁 C12,并把 `scripts/argent-agents.mjs` 的清单/回执接上宿主资产)⇒ 引擎面文件内容变了,按「内容变 ⇒ 版本必变」纪律**再提一版**(先例同 1.3.104→1.3.105、`docs/Argent-智能体平台-设计.md` §8.3 第 3 条)。版本号 0.1.49 从未发布过,不存在「内容变了版本没动」的对外窗口。⛔ 本能力的流程/提示词/脚本内容未再改动。|**0.1.51(2026-10-10,同批三次收口:安装器用法文案 · 门禁登记 · 引擎面去业务词)**:同一工作树里第三次收口 —— ① `install-argent.mjs` 的**用法头注释**补上 `--agent` / `--list-agents` / `--internal-roles` / `--capability remove` 四个入口(内容变 ⇒ 引擎 sha 变);② `scripts/tests/is-direct-run.test.mjs` 的**唯一登记处**加入 `scripts/check-agent-catalog.mjs`(新增 CLI 必须登记,否则端到端判据覆盖不到它;测试文件不在包内,本条只为留痕);③ 本文件里本批新加的 `_note` 文案去掉业务词(`vertical-lint` 的 engine-words 档按**基线**只拦新增,⛔ 不抬基线 ⇒ 改写文案而不是改基线)⇒ 引擎面内容再变,按纪律**再提一版**。版本号 0.1.50 从未发布过,不存在「内容变了版本没动」的对外窗口。⛔ 本能力的流程/提示词/脚本内容未再改动。|**0.1.52(2026-10-10,仓容前置探测批 —— 注册表投影变化,hr 必须跟随提版)**:本批改了共用引擎面 `scripts/flow-requirements.json`(新增 `failureClasses.capacityFull`)、`scripts/flow_capacity*`/`ml_capacity_probe.py`、`flow_failure.py` 分类顺序、`configs/advanced.json`(`advanced.appointment.max_day` 默认 5)与编排器接入点 ⇒ **每个包的 tgz 里都带一份注册表投影**(含 `failureClasses`/`pack.engine`)⇒ hr 的包字节随之改变。本能力自身内容一字未改(`files` 仍只有两个 preset 目录)。⚠️ **教训**:本批曾按 `--describe hr`(只列 6 个 preset 文件)判定\"hr 不含 pack.engine ⇒ 内容没变\",漏掉了\"注册表投影在包内\"这一层 ⇒ 被 `release-argent.sh --check-dl` 当场抓成 `✗ hr: 版本相同(0.1.51)但内容摘要不同 ⇒ 同版本覆盖发布的形态`;按不变式提版后该条转为 ⚠「发布在途」。先例:0.1.42 / 0.1.44 / 0.1.46(均为\"改了引擎基表 ⇒ 无 flow 的包也提版本\")。|**0.1.55(2026-10-11,同批:引擎面新增 `scripts/lib/cdp_ports.py`)**:本能力**无 flow ⇒ 不背共用引擎**(`files` 仍只有两个 preset 目录,逐字节未变);但包内注册表投影含 `pack.engine` ⇒ 包字节变了,按「内容变 ⇒ 版本必变(含无 flow 的包)」提版本并重盖章(同 `0.1.42` / `0.1.52` 先例)。本能力内容一字未改。|**0.1.56(2026-10-12,Windows 安装路径三缺陷修复批)**:共用引擎变更(引擎面文件 ⇒ 每个包的注册表投影都变)——① `scripts/lib/preset-install.mjs`:**「已装集」判据改为选择痕迹** `presets.selection.json#kept`(⛔ 不再读 `package.json#dsh.bundle.patch` —— pnpm 对 `file:` 依赖建的是指向货架的链,读到的永远是货架自带的 14 条 ⇒ 收窄被自己的安装步骤抹掉),新增 `installedSelectionIds()` / `materializeBundleDir()`(软链**实体化**后再改写,⛔ 不走链写进仓库)/ `checkPresetSelectionConsistency()`(patch 实体 == presets.json 行 == dsh.bundle.patch == 痕迹);② `install-argent.mjs`:新增**第 7c2 步收尾重施**(第 7 步 `pnpm add file:…` 会把 profile 副本换回货架 14 个 ⇒ 旧实现在 winpc 真机\"声称只落 7/14、装完却是 14\",7 个内部角色永久留下)、版本体检改**两个候选落点**(克隆目录 + 安装器自己那棵树 —— Windows 的 boot 克隆场景,此前永远退回内置列表 ⇒ 现役 `0.1.7-rc.2` 被报\"未验证\")、`profile-doctor` 与升级器子进程**显式传 `DSH_HOME`**(否则 `--dsh-home <临时>` 时改的是真实 `~/.dsh`)、完成提示**按平台**给启动/重启指引(Windows 不再打 bash 的 `DSH_HOME=… dsh web …`);③ `scripts/lib/dsh-versions.mjs`:内置基线补 `0.1.7-rc.2` + 新增 `verifiedListGaps()` **防漂移门禁**;④ `scripts/argent-upgrade.mjs`:同一判据 + 收尾重施。⇒ 按「内容变 ⇒ 版本必变(含无 flow 的包)」四个包一起提版本并重盖章。本能力内容**一字未改**(无 flow ⇒ 不背共用引擎,仍因包内注册表投影而变)。回归:`scripts/tests/preset-landing-selection.test.mjs`(48 断言:全新机器恰 7 个客户可见角色 / 客户机的 7 再跑安装**仍 7**(垫片 pnpm 中途真把它放大过)/ 14 坏态收窄到 7 / 三处集合+痕迹不变量 / 软链不走链改写仓库 / 真 pnpm 的机制证据)、`scripts/tests/dsh-verified-list.test.mjs`(21 断言:lock 里已晋级版本必须全在列表内(构造缺项 ⇒ 红)、`candidate/` 不参与、列表唯一真源、boot 克隆场景真跑一次)。⛔ 本批**未发布**(发布留给交付方)。|**0.1.57(2026-10-12,升级路径角色落盘追修批:已装集唯一来源 + 内部角色不变式)**:本能力**无 flow ⇒ 不背共用引擎**(`files` 仍只有 preset 目录,逐字节未变);但包内注册表投影含 `pack.engine` ⇒ 包字节变了,按「内容变 ⇒ 版本必变(含无 flow 的包)」一起提版本并重盖章(同 `0.1.42` / `0.1.52` 先例)。本能力内容**一字未改**。① `scripts/lib/preset-install.mjs`:「已装集」的**唯一来源**收口到 `resolveInstalledSelection()`(**可信痕迹** `presets.selection.json#kept` → 退回 `agents.json` 里各 agent 的 `roles` 并集 → 两级都没有 ⇒ **空集**=首次安装语义;⛔ **绝不**把 profile 里的 patch 实体 / `presets.json` 行 / `dsh.bundle.patch` 条目当已装集 —— 那三份铺完恒是 14,「客户可见 ∪ 已装 ∪ 本智能体」于是恒等于 14,收窄被自己的安装步骤抹掉),并给「可信」加了**判据**:痕迹含 `visibility: internal` 的角色而本机 `internalRoles ≠ true`(且未给 `--internal-roles`)⇒ 判不可信、**响亮报告**后按「痕迹缺失」处理(现场那份 `kept:14/excluded:[]` 的痕迹正是放大缺陷写出来的);新增**不变式** `checkNoInternalLanding()` —— 落盘集合不得含内部角色(除非 `--internal-roles` 或 `agents.json#internalRoles === true`),违反 ⇒ `applyPresetSelection()` **判红并拒绝写出**(⛔ 不静默剔除、不静默修);新增 `reseedBundleDir()` 与 `applyPresetSelection({reseedFromShelf})`(收尾重施必须**按货架重播再收窄**:副本已被削到 7 时就地再过滤永远只能得 7,`--internal-roles` 装不回来、自检还会拿「期望 14 ≠ 落盘 7」判**假红**);② `scripts/argent-upgrade.mjs`:**升级档与安装档同一条公式**(客户可见集 ∪ 可信已装集 ∪ 本智能体角色),⛔ 不再用 `agentScoped: agents.length>0`(它把客户可见集整个排除掉 ⇒ 现场那台只落 2 个角色);第 7.6 步收尾重施带 `allowInternal` + `reseedFromShelf`,判红即该步 `ok:false`;③ `install-argent.mjs`:第 4 步默认档 / `--agent` 子集 / 第 7c2 步收尾重施 / `--capability remove` 四处统一走可信已装集与授权口径(`--capability remove` 只为「本机已合法装了内部角色」时不误判红);④ **新增只读 CLI** `scripts/check-preset-landing.mjs`(四件套 + 已装集来源 + 两条不变式,任何一台机器可照抄自查,⛔ 不写任何文件)⇒ `pack.engine` 登记项 +1。**现场(winpc 真机,2026-10-12)**:profile 14 个 patch 实体 + 痕迹 `kept:14/excluded:[]` + `agents.json=[hr]` + `internalRoles=false`,重跑官网一条命令(升级路径)修前打的是 `preset 同步(行式): **14** 个角色` + `角色落盘集合收尾:14 个;三处集合 + 痕迹一致`(7 个内部角色一个没摘),修后同一台机器 ⇒ **7**(`excluded` 列出 7 个内部角色)并打印 `选择痕迹不可信 ⇒ 已装集退回 agents.json 角色`。回归:`scripts/tests/preset-landing-upgrade-path.test.mjs`(43 断言:现场复刻升级路径 ⇒ 7 且 **pre-fix 判据重建得 14**(鉴别力)/ `agents.json` 缺失 ⇒ 7 / 可信痕迹 7 不许放大 / `internalRoles:true` ⇒ 14 放行 / **不变式负样本判红且零写入** / 「铺货架 ≠ 选择」/ 现场真形态 `kept:14` ⇒ 7 且响亮报告 / 真 home 零增量)。⛔ 本批**未发布**(发布留给交付方)。|**0.1.59(2026-10-12,P1-A 宿主排程批:四道闸防线 + 日报试点声明)**:本能力的流程/提示词/脚本**一字未改**;共用引擎面变更(`scripts/flow_config.py` 新增 trigger.schedule 解析、`scripts/vertical-lint.mjs` 新增排程声明门禁、`scripts/lib/registry.mjs` 新增只读 flowSchedule())与**包内注册表投影**变化(业务线声明只动了日报;新增的 L1 `scripts/lib/schedule-probe.mjs` 与 L2 `scripts/argent-schedule.mjs` **刻意不进 pack.engine**,理由见 daily-report 的 `_note`)⇒ 按「内容变 ⇒ 版本必变(含无 flow 的包)」一起提版本并重盖章。新增回归 `scripts/tests/schedule-probe.test.mjs` / `scripts/tests/schedule-declaration.test.mjs`。|**0.1.62(2026-10-12,宿主门禁追修批:⛔ 不代装宿主 + npx 形态纳入宿主解析)**:共用引擎变更(引擎面文件 ⇒ 每个包的注册表投影都变,**含无 flow 的 hr**)——① `install-argent.mjs`:**删除** `--install-host` 代装入口(连同旧自动代装函数与宿主离线包的下载/解包/bin 链接逻辑;开关若仍被传入 ⇒ 报「该开关已废弃」+ **非零退出**(1 = 用法错误),⛔ 不静默忽略),被拒文案改为按 dsh **官方**运行方式给指引(首选 `npx @deepseek-ai/dsh@<窗口内版本> web`;备选官方「从源码运行」;四要素:① 你的版本不在支持窗口(列窗口)② 官方方式换成窗口内版本的一行命令 ③ 我们正在适配你的版本(⛔ 不给对外 ETA)④ 未改动你的 dsh);② **新增** `scripts/lib/dsh-host-resolve.mjs`(**进 `pack.engine`**):宿主解析链 = **PATH 上的 `dsh` → `DSH_BIN` → npx 缓存形态**(读 `<npm cache>/_npx/<hash>/node_modules/@deepseek-ai/dsh/package.json` 的 version,纯只读零网络;多候选按「最近使用优先(mtime 最新)+ 同刻路径升序」确定并**打印选了哪个**)⇒ **npx 形态算宿主**(旧口径「npx 不等于已安装宿主」作废),且 npx 形态下插件装配走**同一条 launcher**(`npx --yes @deepseek-ai/dsh@<版本> plugin --profile web add file:…`),⛔ 不许「检测到 npx 却去用 PATH 上的另一个 dsh」;③ `install.sh` / `install.ps1` 口径同步(ps1 仍声明并透传 `-InstallHost`,只为让旧命令行得到**可读**的「已废弃」原因而不是参数绑定错误)。本能力自身流程 / 提示词 / 脚本**一字未改**。回归 `scripts/tests/install-host-gate.test.mjs`(**219 断言**,纯夹具:垫片 dsh/npx + 本地裸库 + 临时 home,⛔ 零外网、⛔ 不碰真 `~/.dsh`;⚠️ `dsh plugin` 会绕过 `DSH_HOME`/`HOME` ⇒ 夹具只断言命令行、绝不真跑)。⛔ 本批**未发布**(发布留给交付方)。",
|
|
439
448
|
"2026-10-12:仓容前置探测**误报**修复(判据收紧为「严格阻断形态」+ 手动/CLI 跑完落终态台账);注册表投影在每个包内 ⇒ 四包一起提版重盖。",
|
|
@@ -443,8 +452,8 @@
|
|
|
443
452
|
"**0.1.67(2026-10-13,失败自动重试(方案 a)批 · **同批二次收口**)**:首次以最终内容盖章(c99a04f)后又落了一处**在 `pack.engine` 里**的收口 —— `scripts/flow-auto-retry.py` 的次数上限改为按**尝试**计(台账 `retry` 事件数 + 本脚本审计里 `ok:false` 的被拒次数):委托侧拒绝时**不写**台账事件,只数台账会让\"每轮都会被拒\"的票(表里状态已不是失败 / 表备注分类或预约留痕与台账不一致…)**每轮**再试一次、长期白占\"每轮上限\"的名额并每轮刷卡片/审计 ⇒ 现在最多 `max` 次后明确交人工(日志点名\"其中 n 次被委托侧拒绝\")。题外(⛔ 本批不改):`scripts/flow_retry.py` 的\"缺陷④ 已有货件号默认拒绝清号\"闸门因调用了**未定义**的 `one()` 而被 `except Exception` 静默吞掉(实测 `NameError` ⇒ `ship_now=None` ⇒ 闸门事实上不生效)—— 属该文件的既存缺陷,**未动**(改它 = 改变守卫语义,需专门一批)。⚠️ 按「内容变 ⇒ 版本必变(含无 flow 的包)」四包一起再提一版;版本号 1.3.155 / 1.2.145 / 1.1.148 / 0.1.66 从未发布过(同批收口 ⇒ 以**最终内容**盖章,不存在\"内容变了版本没动\"的对外窗口)。",
|
|
444
453
|
"**W7.7(2026-10-14,尾巴收口:智能体标题去掉「角色」字样 + 死指针改指新落点)**:本批改了 `pack.engine` 里的两件(`agents/catalog.json` 的两个 `title`、`configs/hr.json` 的 label/help)与本注册表自身的一处(`hr` 设置面板的 `hint`)⇒ 清单本身变了、且每个包的 tgz 都带一份注册表投影 ⇒ 按「内容变 ⇒ 版本必变(含无 flow 的包)」四个能力包一起提版本并重盖章。口径:**角色 = DSH preset**,Argent 侧不复述角色清单(会话身份 ⇒ 官方「Agent 预设」区;安装 / 升级 ⇒ 设置 → Argent →「我的智能体」)。⛔ 本能力自身的流程 / 提示词 / 脚本一字未改。",
|
|
445
454
|
"0.6.1:引擎版本(VERSION)随包内容 ⇒ 按纪律提版重发。",
|
|
446
|
-
"0.7.0:版本线升至 0.7.0(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
447
|
-
"0.7.1:版本线提升(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
455
|
+
"历史记录:0.7.0:版本线升至 0.7.0(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
456
|
+
"历史记录:0.7.1:版本线提升(VERSION 参与包内容)⇒ 按纪律提版重发。",
|
|
448
457
|
"0.7.2:修 `flow_config.py --list --json`(忽略 --json 的契约违背)⇒ 包内容变,按纪律提版。"
|
|
449
458
|
],
|
|
450
459
|
"presets": [
|
|
@@ -551,6 +560,7 @@
|
|
|
551
560
|
"scripts/lib/preset-install.mjs",
|
|
552
561
|
"scripts/lib/preset-mechanism.mjs",
|
|
553
562
|
"scripts/lib/preset-sync.mjs",
|
|
563
|
+
"scripts/lib/ml-ads-core.mjs",
|
|
554
564
|
"scripts/lib/profile-retire.mjs",
|
|
555
565
|
"scripts/lib/python-runtime.mjs",
|
|
556
566
|
"scripts/lib/registry.mjs",
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* flow-session-retention —— 定时流程会话的保留/归档(只处理 flow-trigger 登记的会话)。
|
|
4
|
+
*
|
|
5
|
+
* 背景(2026-10-21 用户口径):定时流程改 freshSession 后会话数会增长;必须配套保留策略。
|
|
6
|
+
* 安全边界:
|
|
7
|
+
* · 只读 `<数据目录>/flow-sessions.jsonl`(flow-trigger-wake 新建会话时登记);
|
|
8
|
+
* · ⛔ 不按目录瞎扫、不删人工 BOT 对话(未登记的一律不碰);
|
|
9
|
+
* · 默认 dry-run;`--apply` 才把超过保留期的会话目录移到 `<数据目录>/session-archive/`,
|
|
10
|
+
* 不直接删除;同时清 projcache 条目、写审计 JSONL;
|
|
11
|
+
* · 最近 60 分钟内有修改的会话一律跳过(防误动在飞执行)。
|
|
12
|
+
*
|
|
13
|
+
* 用法:
|
|
14
|
+
* node scripts/flow-session-retention.mjs [--days 7] [--apply] [--json]
|
|
15
|
+
* DSH_HOME=~/.dsh-argent ARGENT_DATA_DIR=~/.dsh-argent/argent node scripts/flow-session-retention.mjs --apply
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, appendFileSync, readdirSync } from 'node:fs'
|
|
18
|
+
import { dirname, join } from 'node:path'
|
|
19
|
+
import { homedir } from 'node:os'
|
|
20
|
+
|
|
21
|
+
const arg = (n, d = '') => { const i = process.argv.indexOf(n); return i >= 0 ? (process.argv[i + 1] ?? d) : d }
|
|
22
|
+
const APPLY = process.argv.includes('--apply')
|
|
23
|
+
const JSON_OUT = process.argv.includes('--json')
|
|
24
|
+
const DAYS = Math.max(1, Number(arg('--days', '7')) || 7)
|
|
25
|
+
const DSH_HOME = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
26
|
+
const DATA = process.env.ARGENT_DATA_DIR || join(DSH_HOME, 'argent')
|
|
27
|
+
const REGISTRY = join(DATA, 'flow-sessions.jsonl')
|
|
28
|
+
const SESSIONS = join(DSH_HOME, 'sessions')
|
|
29
|
+
const ARCHIVE = join(DATA, 'session-archive')
|
|
30
|
+
const AUDIT = join(DATA, 'session-retention-audit.jsonl')
|
|
31
|
+
const CUTOFF = Date.now() - DAYS * 86400 * 1000
|
|
32
|
+
const RECENT = Date.now() - 60 * 60 * 1000
|
|
33
|
+
|
|
34
|
+
function readRegistry() {
|
|
35
|
+
if (!existsSync(REGISTRY)) return []
|
|
36
|
+
return readFileSync(REGISTRY, 'utf8').split('\n').filter(Boolean).map((l) => {
|
|
37
|
+
try { return JSON.parse(l) } catch { return null }
|
|
38
|
+
}).filter(Boolean)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function findSessionDir(sid) {
|
|
42
|
+
if (!existsSync(SESSIONS)) return ''
|
|
43
|
+
for (const proj of readdirSync(SESSIONS)) {
|
|
44
|
+
const p = join(SESSIONS, proj, sid)
|
|
45
|
+
if (existsSync(p)) return p
|
|
46
|
+
}
|
|
47
|
+
return ''
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const rows = []
|
|
51
|
+
let seen = new Set()
|
|
52
|
+
for (const rec of readRegistry()) {
|
|
53
|
+
const sid = String(rec.sessionId || '')
|
|
54
|
+
if (!sid || seen.has(sid)) continue
|
|
55
|
+
seen.add(sid)
|
|
56
|
+
const ts = Date.parse(rec.createdAt || '')
|
|
57
|
+
const dir = findSessionDir(sid)
|
|
58
|
+
const mtime = dir ? statSync(dir).mtimeMs : 0
|
|
59
|
+
const why = []
|
|
60
|
+
if (!dir) why.push('session_dir_missing')
|
|
61
|
+
if (Number.isFinite(ts) && ts >= CUTOFF) why.push('not_expired')
|
|
62
|
+
if (mtime && mtime >= RECENT) why.push('recently_active')
|
|
63
|
+
const action = (!dir || why.length) ? 'skip' : 'archive'
|
|
64
|
+
const row = { sessionId: sid, flow: rec.flow || '', createdAt: rec.createdAt || '', action, why, dir };
|
|
65
|
+
if (action === 'archive' && APPLY) {
|
|
66
|
+
try {
|
|
67
|
+
const dest = join(ARCHIVE, sid)
|
|
68
|
+
mkdirSync(ARCHIVE, { recursive: true })
|
|
69
|
+
renameSync(dir, dest)
|
|
70
|
+
const cache = join(DSH_HOME, 'storages', 'session_projcache', 'sessions', `${sid}.json`)
|
|
71
|
+
if (existsSync(cache)) rmSync(cache, { force: true })
|
|
72
|
+
appendFileSync(AUDIT, JSON.stringify({ at: new Date().toISOString(), sessionId: sid, flow: rec.flow || '', archivedTo: dest }) + '\n')
|
|
73
|
+
row.archivedTo = dest
|
|
74
|
+
} catch (e) {
|
|
75
|
+
row.action = 'error'; row.why.push(String(e.message || e))
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
rows.push(row)
|
|
79
|
+
}
|
|
80
|
+
const summary = { days: DAYS, registered: rows.length, archive: rows.filter((r) => r.action === 'archive').length, skip: rows.filter((r) => r.action === 'skip').length, error: rows.filter((r) => r.action === 'error').length, apply: APPLY };
|
|
81
|
+
if (JSON_OUT) {
|
|
82
|
+
console.log(JSON.stringify({ summary, rows }, null, 2))
|
|
83
|
+
} else {
|
|
84
|
+
console.log(`flow-session-retention days=${DAYS} apply=${APPLY} registered=${summary.registered} archive=${summary.archive} skip=${summary.skip} error=${summary.error}`)
|
|
85
|
+
for (const r of rows) console.log(`${r.action === 'archive' ? '📦' : r.action === 'error' ? '❌' : '⏭'} ${r.sessionId} ${r.flow} ${r.why.join(',')}`)
|
|
86
|
+
if (!APPLY) console.log('[dry-run] 未移动任何会话;加 --apply 才归档')
|
|
87
|
+
}
|
|
88
|
+
process.exit(summary.error ? 1 : 0)
|
|
@@ -222,10 +222,20 @@ const rpc = async (method, args) => {
|
|
|
222
222
|
|
|
223
223
|
// ── 会话复用 ────────────────────────────────────────────────────
|
|
224
224
|
const readSid = () => { try { return JSON.parse(readFileSync(cfg.paths.sessionIdFile, 'utf8')).sessionId || '' } catch { return '' } }
|
|
225
|
+
const flowSessionsFile = () => join(dirname(cfg.paths.ledger), 'flow-sessions.jsonl')
|
|
226
|
+
const recordSession = (sid) => {
|
|
227
|
+
// 只登记本触发链路新建的会话;供 session-retention 按登记表清理,绝不误删人工对话。
|
|
228
|
+
try {
|
|
229
|
+
appendFileSync(flowSessionsFile(), JSON.stringify({
|
|
230
|
+
sessionId: sid, flow: FLOW, createdAt: new Date().toISOString(), preset: cfg.dispatch?.preset || '',
|
|
231
|
+
}) + '\n')
|
|
232
|
+
} catch (e) { log(`⚠️ 会话登记失败(不影响本轮执行):${e.message}`) }
|
|
233
|
+
}
|
|
225
234
|
const writeSid = (sid) => {
|
|
226
235
|
try {
|
|
227
236
|
mkdirSync(dirname(cfg.paths.sessionIdFile), { recursive: true })
|
|
228
237
|
writeFileSync(cfg.paths.sessionIdFile, JSON.stringify({ sessionId: sid, flow: FLOW, updatedAt: new Date().toISOString() }, null, 2) + '\n')
|
|
238
|
+
recordSession(sid)
|
|
229
239
|
} catch (e) { log(`⚠️ 会话 id 落盘失败(下轮会新建会话):${e.message}`) }
|
|
230
240
|
}
|
|
231
241
|
async function createSession() {
|
|
@@ -240,11 +250,13 @@ const promptOf = (sid, text) => rpc('session/prompt', {
|
|
|
240
250
|
})
|
|
241
251
|
|
|
242
252
|
// ── dry-run:只解析配置/模板并预览,**不鉴权、不建会话、不投递** ──
|
|
243
|
-
|
|
253
|
+
const FRESH_SESSION = cfg.dispatch?.freshSession === true
|
|
254
|
+
let sid = FRESH_SESSION ? '' : readSid()
|
|
244
255
|
|
|
245
256
|
// ── --notice:投递一条自由文本指令给流程角色(不派单、不改台账)──────────
|
|
246
257
|
// 为什么需要(2026-09-23):美客多限流时角色仍在重跑手头的票,"让角色知道平台慢/别再试"
|
|
247
258
|
// 此前没有通道 —— 只能等它自己撞够失败。有了它就能一句话叫停/叫等。
|
|
259
|
+
if (FRESH_SESSION) log('🗂️ 本流程声明 freshSession=true ⇒ 本轮强制新建会话(不复用旧会话)')
|
|
248
260
|
if (NOTICE) {
|
|
249
261
|
const text = `【流程通知 · ${new Date().toLocaleString('zh-CN')}】\n${NOTICE}`
|
|
250
262
|
if (DRY) { log(`✅ [dry-run] 将向 ${sid || '(新会话)'} 投递通知:\n${text}`); process.exit(0) }
|
|
@@ -558,7 +558,8 @@ echo "[$ts] 流程 ${FLOW}:已认领 ${tickets} ⇒ 唤起角色会话(本
|
|
|
558
558
|
# 现在:显式取唤醒端的退出码(PIPESTATUS[0],不是 tee 的),非零就响亮播报并**原样透出退出码**。
|
|
559
559
|
# 台账/遥测由唤醒端自己写(它才知道失败分类与尝试过的来源)——这里不重复写,避免同一故障两条信号。
|
|
560
560
|
set +e
|
|
561
|
-
|
|
561
|
+
WAKE_BASE="${ARGENT_WAKE_BASE:-http://127.0.0.1:3080}"
|
|
562
|
+
node "$REPO/scripts/flow-trigger-wake.mjs" --flow "$FLOW" --max "$MAX" --tickets "$tickets" --base "$WAKE_BASE" $DRY 2>&1 | tee -a "$LOG"
|
|
562
563
|
WAKE_CODE=${PIPESTATUS[0]}
|
|
563
564
|
set -e
|
|
564
565
|
if [ "$WAKE_CODE" -ne 0 ]; then
|
|
@@ -880,6 +880,10 @@ def load_config(flow_id: str, registry=None, registry_path=None, strict: bool =
|
|
|
880
880
|
'cwd': str(raw_d.get('cwd') or d['cwd']),
|
|
881
881
|
'sessionIdFile': str(raw_d.get('sessionIdFile') or d['sessionIdFile']),
|
|
882
882
|
}
|
|
883
|
+
# 2026-10-21:日报等无状态流程可声明 freshSession=true ⇒ 每轮新建会话,
|
|
884
|
+
# 不复用旧会话(避免旧上下文/审批/工具状态影响本轮)。缺省 false = 保持既有复用行为。
|
|
885
|
+
if raw_d.get('freshSession') is True:
|
|
886
|
+
dispatch['freshSession'] = True
|
|
883
887
|
concurrency = {
|
|
884
888
|
'max': int(raw_c.get('max') if raw_c.get('max') is not None else DEFAULTS['concurrency']['max']),
|
|
885
889
|
'leaseMin': int(raw_c.get('leaseMin') if raw_c.get('leaseMin') is not None else DEFAULTS['concurrency']['leaseMin']),
|
|
@@ -69,12 +69,14 @@ def rule_patterns(group: str) -> list:
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
_BUILTIN = {
|
|
72
|
+
'appointmentPageNotReady': (
|
|
73
|
+
r'预约页形态异常|未渲染出日历|div\.day\s*命中\s*0|div\.day.*可用=0'),
|
|
72
74
|
'needs_human': (
|
|
73
75
|
r'缺(?:[失了少]|\s)|未填|为空|空值|missing|不存在|未就绪|闸门|校验不通过|需运营|等运营|请补|'
|
|
74
76
|
r'路由守卫|货代.*[≠!=]|不匹配|非.{0,6}货代|选错|人工|'
|
|
75
77
|
# 2026-09-17 门禁金样例暴露:这类是**可人工处理**的运行前提问题(有人去授权/登录即可),
|
|
76
78
|
# 不是"需要角色判断"的模糊故障 ⇒ 归 needs_human(落卡等人),不惊动角色白跑一趟。
|
|
77
|
-
r'未授权|未登录|登录失效|登录墙|login_expired|无权限|permission'),
|
|
79
|
+
r'未授权|未登录|登录失效|登录墙|login_expired|无权限|permission|疑似缺陷⑰|bad_shipment_id|货件号不可解析'),
|
|
78
80
|
# 2026-09-18:页面/选择器类(UI 选择器超时 ≠ 网络超时)。旧架构既定规则 = 选择器/业务类
|
|
79
81
|
# **绝不自动重置**;但下面的 retryable 里有 `超时|timeout` 会把它吞掉 ⇒ 允许清号重建 = 重复建单。
|
|
80
82
|
# 本组在 retryable **之前**匹配 ⇒ 归 needs_agent(交掌舵者判断)。
|
|
@@ -104,6 +106,7 @@ _BUILTIN = {
|
|
|
104
106
|
# 注册表优先(P3 判据收敛):注册表有该组规则就用它,否则用内置兜底(保证老部署/离线也能跑)
|
|
105
107
|
EFFECTIVE = {
|
|
106
108
|
'capacityFull': rule_patterns('capacityFull') or [_BUILTIN['capacityFull']],
|
|
109
|
+
'appointmentPageNotReady': rule_patterns('appointmentPageNotReady') or [_BUILTIN['appointmentPageNotReady']],
|
|
107
110
|
'pageFlaky': rule_patterns('pageFlaky') or [_BUILTIN['pageFlaky']],
|
|
108
111
|
'needs_human': rule_patterns('needsHuman') or [_BUILTIN['needs_human']],
|
|
109
112
|
'retryable': rule_patterns('flowRetryable') or [_BUILTIN['retryable']],
|
|
@@ -115,11 +118,13 @@ EFFECTIVE = {
|
|
|
115
118
|
# 预检证据下解除 defer。⚠️ 它**不**抢 retryable 的活:本组只认平台原文与显式标记。
|
|
116
119
|
# 分组 → **派发用的 kind**:pageFlaky 派发为 needs_agent(唤醒掌舵者判断),
|
|
117
120
|
# 但它的理由单独给(GROUP_WHY),避免与"未知类兜底"混为一谈。
|
|
118
|
-
GROUPS = (('capacityFull', 'needs_human'), ('
|
|
119
|
-
('needs_human', 'needs_human'), ('retryable', 'retryable'))
|
|
121
|
+
GROUPS = (('capacityFull', 'needs_human'), ('appointmentPageNotReady', 'retryable'),
|
|
122
|
+
('needs_human', 'needs_human'), ('pageFlaky', 'needs_agent'), ('retryable', 'retryable'))
|
|
120
123
|
RULES = tuple((g, k, re.compile('|'.join(EFFECTIVE[g]), re.I)) for g, k in GROUPS)
|
|
121
124
|
DEFAULT_KIND = 'needs_agent'
|
|
122
125
|
GROUP_WHY = {
|
|
126
|
+
'appointmentPageNotReady': '预约页未就绪/日历未渲染(页面慢或瞬时形态)⇒ 保留货件号自动续跑,'
|
|
127
|
+
'不直接判人工(除非疑似缺陷⑰/货件号不可解析)',
|
|
123
128
|
'pageFlaky': '页面/选择器类(UI 选择器超时,**非网络类**)⇒ 交掌舵者判断;'
|
|
124
129
|
'**禁止自动清号重建**(旧架构规则:选择器/业务类绝不自动重置,防重复建单)',
|
|
125
130
|
'capacityFull': '仓容(库容)满(平台原文拒绝继续发货)⇒ 前提问题,正确动作**唯一**'
|
|
@@ -143,7 +148,7 @@ def classify(error: str, attempt: int | None = None, max_attempts: int = 3) -> d
|
|
|
143
148
|
break
|
|
144
149
|
if kind == 'retryable' and attempt is not None and attempt >= max_attempts:
|
|
145
150
|
kind, why = 'needs_agent', f'已达自动重试上限(attempt={attempt})⇒ 需角色判断'
|
|
146
|
-
return {'kind': kind, 'reason': why, 'error': text[:300]}
|
|
151
|
+
return {'kind': kind, 'reason': why, 'error': text[:300], 'group': grp if 'grp' in locals() else ''}
|
|
147
152
|
|
|
148
153
|
|
|
149
154
|
# ── 运营可见文案:**唯一措辞来源**(2026-10-09,缺陷 B)────────────────────────
|
|
@@ -51,6 +51,12 @@ GUIDE = {
|
|
|
51
51
|
"把 appId 告知开发 → 改 `~/.dsh/argent/gateway.json` 的该角色 botAppId + 复核 `roles.json` 映射 → 重启 dsh web",
|
|
52
52
|
"不需要独立入口时:**可共用主 bot**(通知走 `NOTIFY_PROFILE`),流程照跑",
|
|
53
53
|
]},
|
|
54
|
+
"login": {"who": "用户(桌面窗口 / 滑块 / 标签页)", "steps": [
|
|
55
|
+
"文鳐:`bash scripts/wenyao-browser.sh start`,在弹出的 Chrome 里拖滑块并登录,成功后保持窗口/标签页不关",
|
|
56
|
+
"鑫中航:保留常驻浏览器/标签页(`com.whyshu.xzh-browser`)",
|
|
57
|
+
"紫鸟:每个店铺窗口保留一个标签页;安装后不要关闭这些窗口",
|
|
58
|
+
"复核:`python3 scripts/login-readiness.py`",
|
|
59
|
+
]},
|
|
54
60
|
"model-auth": {"who": "用户(问述账号登录 / DeepSeek Key)", "steps": [
|
|
55
61
|
"**问述积分模式**:客户机浏览器打开 http://127.0.0.1:<port> → 设置 → Argent → 「✦ 一键登录问述科技账号」(设备流授权一次,token 90 天)",
|
|
56
62
|
"**DeepSeek 官方直连模式**:确认本机 dsh credentials 里有 `DEEPSEEK_API_KEY`,且 `bash scripts/provider-switch.sh status` 显示「DeepSeek 官方直连」",
|
|
@@ -204,6 +210,28 @@ def check_write_guards(cfg: dict, r: "R") -> None:
|
|
|
204
210
|
r.add(f"guards.{key}", "ok", f"{label}:注册表与写手一致({bool(posture.get(key))})")
|
|
205
211
|
|
|
206
212
|
|
|
213
|
+
def check_login_portal(r: "R", flow_id: str) -> None:
|
|
214
|
+
"""安装/生产前的登录态节点:文鳐滑块窗口、鑫中航标签页、紫鸟店铺窗口。
|
|
215
|
+
|
|
216
|
+
不把登录态留到第一票才暴露;本检查只读,warn 不阻断触发器(fail 才阻断)。
|
|
217
|
+
"""
|
|
218
|
+
script = HERE / "login-readiness.py"
|
|
219
|
+
if not script.exists():
|
|
220
|
+
r.add("login", "warn", "缺 scripts/login-readiness.py(旧克隆)", "", "login")
|
|
221
|
+
return
|
|
222
|
+
code, out, err = run([sys.executable, str(script), "--flow", flow_id, "--json"], timeout=120)
|
|
223
|
+
try:
|
|
224
|
+
doc = json.loads(out)
|
|
225
|
+
except Exception:
|
|
226
|
+
r.add("login", "warn", "登录态检查未返回 JSON",
|
|
227
|
+
((err or out).strip().splitlines()[-1:] or [""])[0][:200], "login")
|
|
228
|
+
return
|
|
229
|
+
for it in (doc.get("checks") or []):
|
|
230
|
+
level = str(it.get("level") or "warn")
|
|
231
|
+
r.add(str(it.get("id") or "login"), level, str(it.get("title") or ""),
|
|
232
|
+
str(it.get("detail") or ""), "login" if level != "ok" else None)
|
|
233
|
+
|
|
234
|
+
|
|
207
235
|
def main(argv=None) -> int:
|
|
208
236
|
ap = argparse.ArgumentParser(description="单流程前置检查 + 操作指南")
|
|
209
237
|
ap.add_argument("--flow", required=True)
|
|
@@ -315,6 +343,8 @@ def main(argv=None) -> int:
|
|
|
315
343
|
check_lease(cfg, r) # 批 C4:能力租约(未启用强制时不制造噪音)
|
|
316
344
|
# ⑦ 模型凭证(2026-09-29 日报事故固化):缺/死 ⇒ 跑起来也只会 401 空转 ⇒ 提前 FAIL
|
|
317
345
|
check_model_auth(r)
|
|
346
|
+
# ⑧ 登录态(安装/生产前置;warn 不阻断,fail 阻断)
|
|
347
|
+
check_login_portal(r, a.flow)
|
|
318
348
|
|
|
319
349
|
return _emit(r, a.json)
|
|
320
350
|
|
|
@@ -172,6 +172,8 @@ def main(argv=None) -> int:
|
|
|
172
172
|
ap.add_argument("--yes", action="store_true", help="真写(默认 dry-run)")
|
|
173
173
|
ap.add_argument("--allow-clear-shipment", action="store_true",
|
|
174
174
|
help="显式放行**清空已有货件号**(缺陷④ 闸门;默认拒绝——清号后重建会在平台留孤儿货件)")
|
|
175
|
+
ap.add_argument("--resume-existing", action="store_true",
|
|
176
|
+
help="保留已有货件号,状态重置为待执行/就绪=true,让下一轮从原进度续跑(预约页未就绪类自动重试用)")
|
|
175
177
|
ap.add_argument("--override", action="store_true",
|
|
176
178
|
help="人工授权覆盖分类闸门(仅当人已判断应重跑;必须配 --reason,会记审计)")
|
|
177
179
|
a = ap.parse_args(argv)
|
|
@@ -250,7 +252,7 @@ def main(argv=None) -> int:
|
|
|
250
252
|
# ⇒ 将来再写错名字会**响亮炸**,而不是静默退化成"守卫不生效"。
|
|
251
253
|
# 门禁:`scripts/tests/flow-retry-guard4.test.py` 钉住"本块不得再出现未定义助手 / 不得再被 except 吞"。
|
|
252
254
|
ship_now = first(row.get(f_ship))
|
|
253
|
-
if ship_now and not getattr(a, "allow_clear_shipment", False):
|
|
255
|
+
if ship_now and not getattr(a, "allow_clear_shipment", False) and not getattr(a, "resume_existing", False):
|
|
254
256
|
print(f"❌ 拒绝清号重置:本行已有货件号 {ship_now!r}(record={record})。", file=sys.stderr)
|
|
255
257
|
print(" 货件号是找回平台半成品货件的唯一线索;清掉再重建会在平台留下**孤儿货件**。", file=sys.stderr)
|
|
256
258
|
print(" 正确路径:① 若号码错位(缺陷⑰)⇒ 改号后保留货件号续跑;"
|
|
@@ -258,9 +260,14 @@ def main(argv=None) -> int:
|
|
|
258
260
|
"③ 否则交人工裁定。", file=sys.stderr)
|
|
259
261
|
return 3
|
|
260
262
|
|
|
261
|
-
# lip
|
|
263
|
+
# lip 语义:默认清货件号 + 清备注 + 状态→待执行 + 就绪=true(下一轮从头重建);
|
|
264
|
+
# --resume-existing:保留货件号,让 orchestrator 从已有货件号续跑(预约页未就绪类不重建)
|
|
262
265
|
pending = "待执行"
|
|
263
|
-
|
|
266
|
+
resume_existing = bool(getattr(a, "resume_existing", False) and ship_now)
|
|
267
|
+
if resume_existing:
|
|
268
|
+
patch = {fmap[f_status]: pending, fmap[f_ready]: True, fmap[f_note]: ""}
|
|
269
|
+
else:
|
|
270
|
+
patch = {fmap[f_status]: pending, fmap[f_ready]: True, fmap[f_note]: "", fmap[f_ship]: ""}
|
|
264
271
|
allowed = {fmap[f_status], fmap[f_ready], fmap[f_note], fmap[f_ship]}
|
|
265
272
|
assert set(patch) <= allowed, "内部错误:补丁越界"
|
|
266
273
|
|
|
@@ -283,8 +290,12 @@ def main(argv=None) -> int:
|
|
|
283
290
|
lark(profile, ["+record-batch-update", "--base-token", base, "--table-id", table,
|
|
284
291
|
"--json", json.dumps({"update_records": {record: patch}}, ensure_ascii=False)])
|
|
285
292
|
after = read_row(profile, base, table, record, fmap)
|
|
286
|
-
|
|
287
|
-
|
|
293
|
+
if resume_existing:
|
|
294
|
+
ok = (first(after.get(f_status)) == pending and str(first(after.get(f_ship)) or "") == ship_now
|
|
295
|
+
and not first(after.get(f_note)))
|
|
296
|
+
else:
|
|
297
|
+
ok = (first(after.get(f_status)) == pending and str(after.get(f_ship) or "") == ""
|
|
298
|
+
and not first(after.get(f_note)))
|
|
288
299
|
with AUDIT.open("a", encoding="utf-8") as fh:
|
|
289
300
|
fh.write(json.dumps({"at": at, "flow": a.flow, "record": record, "caseId": case_id,
|
|
290
301
|
"action": "reset-rebuild", "kind": kind.get("kind"), "reason": a.reason,
|
|
@@ -293,10 +304,14 @@ def main(argv=None) -> int:
|
|
|
293
304
|
"backup": str(bk), "ok": bool(ok)}, ensure_ascii=False) + "\n")
|
|
294
305
|
subprocess.run([sys.executable, str(HERE / "flow_ledger.py"), "--ledger", str(LEDGER), "--case-id", case_id,
|
|
295
306
|
"--event", "retry", "--attempt", str(retry_count(case_id) + 1), "--by", "flow_retry",
|
|
296
|
-
"--detail", f"
|
|
307
|
+
"--detail", (f"保留货件号续跑({kind.get('kind')}):状态→{pending}|货件号 {ship_now}|清备注|就绪=true"
|
|
308
|
+
if resume_existing else f"重置后重建({kind.get('kind')}):状态→{pending}|清货件号/备注|就绪=true")
|
|
297
309
|
+ (f"|{a.reason}" if a.reason else "")], check=False)
|
|
298
310
|
print(f" ✅ 已重置并读回{'一致' if ok else '**不一致**'}|备份 {bk.name}|审计 flow-retry-audit.jsonl")
|
|
299
|
-
|
|
311
|
+
if resume_existing:
|
|
312
|
+
print(f" 下一轮该行会以「{pending}」+ 就绪=true + 原货件号 {ship_now} 被识别为续跑(无需运营操作)")
|
|
313
|
+
else:
|
|
314
|
+
print(f" 下一轮该行会以「{pending}」+ 就绪=true 被识别为执行相,从头重建新货件(无需运营操作)")
|
|
300
315
|
return 0 if ok else 4
|
|
301
316
|
|
|
302
317
|
|
|
@@ -65,7 +65,7 @@ export const ARGENT_PLUGIN_RANGE = '>=0.1.0 <0.3'
|
|
|
65
65
|
/** 宿主 node 门槛(与安装器 `MIN_NODE` 对齐;与既有插件 `engines.node` 的写法逐字同源)。 */
|
|
66
66
|
export const ENGINES_NODE = '>=24.2.0'
|
|
67
67
|
/** npm 元数据(与既有 6 个插件逐字同源)。 */
|
|
68
|
-
export const REPO_URL = 'git+https://
|
|
68
|
+
export const REPO_URL = 'git+https://github.com/cstcen/argent-dsh.git'
|
|
69
69
|
|
|
70
70
|
/** 读货架声明(读不到 ⇒ null,由调用方响亮报错,⛔ 不静默补一份);`root` 可注入(夹具用)。 */
|
|
71
71
|
export function loadCatalogDoc(root = REPO) {
|
|
@@ -165,7 +165,7 @@ export function renderPatch({ id, meta, composition, order }) {
|
|
|
165
165
|
export function renderPackageJson(ids) {
|
|
166
166
|
return JSON.stringify({
|
|
167
167
|
name: '@whyshu/argent-presets',
|
|
168
|
-
version: '0.1.
|
|
168
|
+
version: '0.1.1',
|
|
169
169
|
description: 'Argent on DSH — 角色 preset 行式声明(dsh 0.1.7+;bundle patch 形态)',
|
|
170
170
|
type: 'module',
|
|
171
171
|
main: 'src/index.js',
|
|
@@ -173,7 +173,7 @@ export function renderPackageJson(ids) {
|
|
|
173
173
|
license: 'MIT',
|
|
174
174
|
repository: {
|
|
175
175
|
type: 'git',
|
|
176
|
-
url: 'git+https://
|
|
176
|
+
url: 'git+https://github.com/cstcen/argent-dsh.git',
|
|
177
177
|
directory: 'plugins/argent-presets',
|
|
178
178
|
},
|
|
179
179
|
engines: { node: '>=24.2.0' },
|
|
@@ -1215,7 +1215,8 @@ def _upload_and_finish(row, out, generate_only=False, dry=False):
|
|
|
1215
1215
|
# 弹窗偶发未捕获(15s 等待不足)→ 判定缺失时重试一次再下结论。
|
|
1216
1216
|
up_out = _run_stageb(["--upload", str(out)])
|
|
1217
1217
|
if ("全部完成" not in up_out and "成功" not in up_out
|
|
1218
|
-
and "重复" not in up_out and "参考单号" not in up_out
|
|
1218
|
+
and "重复" not in up_out and "参考单号" not in up_out
|
|
1219
|
+
and "上传结果: True" not in up_out):
|
|
1219
1220
|
log(f"{rid} 上传结果弹窗未捕获,重试一次…")
|
|
1220
1221
|
up_out = _run_stageb(["--upload", str(out)])
|
|
1221
1222
|
if "参考单号" in up_out and "重复" in up_out:
|
|
@@ -1237,7 +1238,8 @@ def _upload_and_finish(row, out, generate_only=False, dry=False):
|
|
|
1237
1238
|
_confirm_dict(sku, _first(row.get(F_HS)),
|
|
1238
1239
|
translate_material(_first(row.get(F_MAT))))
|
|
1239
1240
|
return f"{rid} 已存在(鑫中航有单)→ 已完成+固化"
|
|
1240
|
-
if "全部完成" not in up_out and "成功" not in up_out and "完成 1/1" not in up_out
|
|
1241
|
+
if ("全部完成" not in up_out and "成功" not in up_out and "完成 1/1" not in up_out
|
|
1242
|
+
and "上传结果: True" not in up_out):
|
|
1241
1243
|
lip_update([rid], {F_ST: ST_FAIL, F_ERR: f"上传失败(鑫中航)——请人工检查"})
|
|
1242
1244
|
log(f"{rid} 上传失败: {up_out[-300:]}")
|
|
1243
1245
|
return f"{rid} 上传失败"
|
|
@@ -1285,6 +1287,7 @@ def handle_row(row, generate_only=False, dry=False, stats=None):
|
|
|
1285
1287
|
sku = _first(row.get(F_SKU))
|
|
1286
1288
|
st = _first(row.get(F_ST))
|
|
1287
1289
|
freight = _first(row.get(F_FREIGHT))
|
|
1290
|
+
ship = _first(row.get(F_SHIP))
|
|
1288
1291
|
|
|
1289
1292
|
# PM 接管:历史「大货标签下载失败」的失败行视为可续跑(发票已上传,只差标签)
|
|
1290
1293
|
if st == ST_FAIL and "大货标签" in (_first(row.get(F_ERR)) or ""):
|
|
@@ -1302,6 +1305,31 @@ def handle_row(row, generate_only=False, dry=False, stats=None):
|
|
|
1302
1305
|
# 发票已生成/已上传(填写中 / 已上传 / 标签已下载)= 只差大货标签 → 只补标签
|
|
1303
1306
|
# (幂等:不重复生成、不重复上传;避免每轮重复出票)
|
|
1304
1307
|
if st in (ST_FILL, ST_UPLOADED, ST_LABEL):
|
|
1308
|
+
# 2026-10-22 修复:`填写中` 不等于「已上传」。
|
|
1309
|
+
# 旧逻辑直接 `_finish_with_label`,只会找标签,不会登录门户上传;
|
|
1310
|
+
# generate-only 停在填写中后,真跑会静默卡死(80062147 实测 1h40m)。
|
|
1311
|
+
if st == ST_FILL:
|
|
1312
|
+
out = OUTPUT_DIR / f"{ship}-发票.xlsx"
|
|
1313
|
+
if dry:
|
|
1314
|
+
return f"{rid} [dry] 填写中:先探测标签;无标签则幂等上传"
|
|
1315
|
+
if out.exists():
|
|
1316
|
+
# 先探测门户是否已有该单/标签;有标签直接完成,避免重复上传。
|
|
1317
|
+
label_path, _dl_out, _from_local = _download_label_or_local(row, ship)
|
|
1318
|
+
if label_path and os.path.exists(label_path):
|
|
1319
|
+
log(f"{rid} 填写中但门户已有标签 → 直接补标签状态")
|
|
1320
|
+
return _finish_with_label(row, dry=False)
|
|
1321
|
+
log(f"{rid} 填写中且标签未就绪 → 先执行幂等上传,再补标签")
|
|
1322
|
+
return _upload_and_finish(row, out, generate_only=False)
|
|
1323
|
+
# 本地发票缺失:重新生成后再上传(不把填写中误判成已上传)
|
|
1324
|
+
try:
|
|
1325
|
+
log(f"{rid} 填写中但本地发票缺失 → 重新生成")
|
|
1326
|
+
out = generate_for_row(row)
|
|
1327
|
+
lip_update([rid], {F_ST: ST_FILL, F_ERR: ""})
|
|
1328
|
+
lip_upload_attachment(rid, F_FILE, out, row=row)
|
|
1329
|
+
return _upload_and_finish(row, out, generate_only=False)
|
|
1330
|
+
except Exception as exc:
|
|
1331
|
+
lip_update([rid], {F_ST: ST_FAIL, F_ERR: f"填写中续跑失败:{exc}"})
|
|
1332
|
+
return f"{rid} 填写中续跑失败: {exc}"
|
|
1305
1333
|
return _finish_with_label(row, dry=dry)
|
|
1306
1334
|
if st == ST_PICK and row.get(F_READY) is not True:
|
|
1307
1335
|
# 表格文案即行动指令(用户口径 2026-09-10):就绪未勾时也保持缺口文案准确
|