@sleep2agi/agent-network 2.3.0-preview.74 → 2.3.0-preview.76

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.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * #1648 — 名册上「刚停的节点」和「掉了三天没人发现的节点」渲染成同一个数字。
3
+ *
4
+ * `anet status` 原先只印 `N offline`。实测(2026-08-31,84 台 TM 相关节点):
5
+ * 45 台 offline 里 27 台 >3 天、18 台 1–3 天、**近 6 小时内 0 台** ——
6
+ * 「当前没有活故障」和「有 45 台掉了」是完全不同的两个结论,而屏幕上只有后者。
7
+ */
8
+ /**
9
+ * 🔴 hub 存的 `last_seen_at` 是 **UTC**,但字符串里**没有时区标记**
10
+ * (形如 `2026-08-30 21:11:24`)。直接 `new Date(raw)` 会被当成**本地时间**:
11
+ * 在 UTC+8 上就是整整 8 小时的偏差,而且它**不会报错** —— 只会把「4 分钟前」
12
+ * 读成「8 小时前」。这个错本人在 2026-08-31 亲自犯过一次,差点把
13
+ * 「舰队心跳陈旧」报出去。
14
+ */
15
+ export declare function parseHubTimestamp(raw: unknown): number | null;
16
+ export type OfflineAges = {
17
+ under1h: number;
18
+ h1to24: number;
19
+ d1to3: number;
20
+ over3d: number;
21
+ /** 🔴 没有可用时间戳的那些必须单独一格,不能并进任何一档 —— 见下 */
22
+ unknown: number;
23
+ total: number;
24
+ };
25
+ export declare function summarizeOfflineAges(offline: ReadonlyArray<{
26
+ last_seen_at?: unknown;
27
+ }>, nowMs: number): OfflineAges;
28
+ /** 返回空串表示「没有值得多说一句的东西」,调用方据此决定印不印。 */
29
+ export declare function formatOfflineAges(a: OfflineAges): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * CLI 里那几张对齐表(`anet status` 的 needs-attention / working,以及 Recent Tasks
3
+ * 和 `anet tasks`)把**用户提供的文本**直接 `slice(0, N)` 塞进一行。
4
+ *
5
+ * 🔴 任务正文里有换行时,截出来的那段会**把整张表打断**。实测(2026-08-31,
6
+ * 真跑 `anet status` 才看见,读代码看不出来):
7
+ *
8
+ * grok-v1 blocked 【把你自己的名册状态修正过来 —— 两步,别做别的】
9
+ *
10
+ * 你今天两次在 22 秒内答了我的探测(G
11
+ *
12
+ * 第二行完全脱离了对齐,而且读起来像另一个节点的输出。
13
+ *
14
+ * 这里把所有空白(含 \n \r \t 和连续空格)折成单个空格再截。
15
+ *
16
+ * 🔴 **有意不加省略号**:加了会改变列宽,而列宽是这几张表现有的对齐依据。
17
+ * 「截断了却看不出来」是另一个问题(先存在于这里,本次不动),
18
+ * 不要顺手在一个修显示错位的改动里改掉它。
19
+ */
20
+ export declare function oneLineCell(text: unknown, max: number): string;
@@ -1,9 +1,9 @@
1
- export declare const PAIRED_AGENT_NETWORK_VERSION = "2.3.0-preview.74";
2
- export declare const PAIRED_AGENT_NODE_VERSION = "2.5.0-preview.56";
3
- export declare const PAIRED_AGENT_NODE_SPEC = "@sleep2agi/agent-node@2.5.0-preview.56";
4
- export declare const OPENCODE_AGENT_NETWORK_VERSION = "2.3.0-preview.74";
5
- export declare const OPENCODE_AGENT_NODE_VERSION = "2.5.0-preview.56";
6
- export declare const OPENCODE_AGENT_NODE_SPEC = "@sleep2agi/agent-node@2.5.0-preview.56";
1
+ export declare const PAIRED_AGENT_NETWORK_VERSION = "2.3.0-preview.76";
2
+ export declare const PAIRED_AGENT_NODE_VERSION = "2.5.0-preview.58";
3
+ export declare const PAIRED_AGENT_NODE_SPEC = "@sleep2agi/agent-node@2.5.0-preview.58";
4
+ export declare const OPENCODE_AGENT_NETWORK_VERSION = "2.3.0-preview.76";
5
+ export declare const OPENCODE_AGENT_NODE_VERSION = "2.5.0-preview.58";
6
+ export declare const OPENCODE_AGENT_NODE_SPEC = "@sleep2agi/agent-node@2.5.0-preview.58";
7
7
  export type PairedAgentNodeResolution = {
8
8
  spec: string;
9
9
  args: string[];
@@ -16,3 +16,33 @@
16
16
  */
17
17
  export type SessionClass = "idle" | "working" | "attention" | "offline";
18
18
  export declare function classifySessionStatus(raw: unknown): SessionClass;
19
+ /**
20
+ * `anet status` 顶部那三/四个数字。
21
+ *
22
+ * 🔴 为什么它必须和上面的分类器用同一套判据(#1625):
23
+ * 原先 `cli.ts` 是 `statusRes.summary || sessions.reduce(…)`,而
24
+ * `/api/status` **总是**返回 summary ⇒ 右边那支从不执行,屏幕上的数字
25
+ * 来自**服务端**一份还停在 #1548 之前的分类(它把 `blocked`/`error`
26
+ * 折进 `working`)。于是一个 blocked 节点同时出现在 `working` 和
27
+ * `needs attention` 两格里,四个数加起来比总数多一个。
28
+ *
29
+ * 🔴 为什么本地算是安全的:`/api/status` 只加 `addNetworkScope`,没有任何
30
+ * 状态/别名过滤,而它的 summary 就是从**同一个 sessions 数组** reduce 出来的
31
+ * —— 两者范围逐字相同。(这一点对 MCP 的 `get_all_status` **不成立**:
32
+ * 那边 summary 走独立的 `GROUP BY status` 查询、无视 filter,所以别照搬。)
33
+ *
34
+ * 🔴 `attention` 必须显式初始化为 0。原来的累加器只有
35
+ * `{idle, working, offline, total}`,`acc["attention"]++` 会得到 **NaN**,
36
+ * 而 `summary.attention ?? attention.length` 里的 `??` **不接 NaN**,
37
+ * 屏幕会印 `NaN needs attention`。那一支从不执行,所以从没人见过。
38
+ */
39
+ export type SessionSummary = {
40
+ idle: number;
41
+ working: number;
42
+ attention: number;
43
+ offline: number;
44
+ total: number;
45
+ };
46
+ export declare function summarizeSessions(sessions: Array<{
47
+ status?: unknown;
48
+ }>): SessionSummary;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `anet daemon <sub>` 认不出子命令时,原先只走 `suggestSimilar`,而它的候选集
3
+ * 是 `["init","start","restart","up","list"]` —— **全是会改变状态的命令**。
4
+ *
5
+ * 🔴 实测(用仓里那个 `suggestSimilar` 本身跑的,不是推的):
6
+ *
7
+ * anet daemon rm → 建议 "up" 想删,被指去「创建 + 启动」
8
+ * anet daemon state → 建议 "start" 想看状态,被指去「启动」
9
+ * anet daemon stat → 建议 "start" 同上
10
+ * anet daemon stop → 建议 null (levenshtein("stop","start") = 3)
11
+ *
12
+ * 一个「你是不是想 X」的提示,把只读/销毁意图导向一个会动世界的命令,
13
+ * 比不给提示更贵。
14
+ *
15
+ * 而这些动作**本来就有**:daemon 就是一个 role=host_supervisor 的 agent-node,
16
+ * `anet daemon restart` 内部调的正是 `anet node stop` 用的那个 stopCommand()。
17
+ * 缺的从来不是功能,是**可发现性**。
18
+ */
19
+ /** 这些是 `anet daemon` 自己的子命令 —— 它们**会改变状态**,所以任何
20
+ * 只读/销毁意图都不该被导向它们。测试用它做对照。 */
21
+ export declare const DAEMON_STATE_CHANGING: readonly ["init", "start", "restart", "up"];
22
+ export declare const PROJECT_STATE_CHANGING: readonly ["up", "restart"];
23
+ export declare const NODE_STATE_CHANGING: readonly ["create", "start", "restart", "resume", "rename", "loop"];
24
+ export declare const daemonSubcommandRedirect: (sub: unknown, name?: unknown) => string[] | null;
25
+ export declare const projectSubcommandRedirect: (sub: unknown, name?: unknown) => string[] | null;
26
+ export declare const nodeSubcommandRedirect: (sub: unknown, name?: unknown) => string[] | null;
@@ -27,6 +27,25 @@ export declare function probeWindowsCreationDate(pid: number): string | null;
27
27
  * an unrelated local client cannot satisfy it.
28
28
  */
29
29
  export declare function probeWindowsOwnedLoopbackConnection(rootPid: number, expectedCreationDate: string, port: number, runPowerShell?: (script: string) => string): boolean;
30
+ /**
31
+ * #1342 —— 只在健康检查**即将失败**时跑一次,用来回答一个此前没人量过的问题:
32
+ * 那 10.5 秒到底花在 **PowerShell 启动**上,还是花在 `Get-CimInstance Win32_Process`
33
+ * 这个全表查询上?
34
+ *
35
+ * 实测背景(由 #1628/#1637 加的 probeMs 量到,2026-08-31):
36
+ * probes=1 waited=10897ms probeMsLast=10490 ← 单次探测吃掉 96% 的预算
37
+ * 而 #1636 把全表枚举提到循环外之后这个数**没有变小**(9963 → 10490),
38
+ * 说明成本不在"重复枚举",而在那一次调用本身。但"那一次调用"包含两段:
39
+ * 起一个 powershell 进程,和在里面跑 CIM 查询。**这两段要查的东西完全不同** ——
40
+ * 前者是 runner/镜像的问题,后者是查询写法的问题。
41
+ *
42
+ * 🔴 这里跑的是一个**什么都不做**的脚本(`'x'`),所以它测到的就是启动那一段的下界。
43
+ * 只在失败路径调用一次,不进探测循环 —— 不给正常路径增加任何成本,
44
+ * 也**不可能**改变探测的返回值。
45
+ *
46
+ * 拿不到就返回 null(未知),不返回 0 —— 0 会被读成「启动不花时间」。
47
+ */
48
+ export declare function measurePowerShellStartupMs(): number | null;
30
49
  export declare function writeWindowsCopresenceRecord(nodesDir: string, nodeId: string, processes: WindowsManagedProcess[], marker?: string): void;
31
50
  export declare function readWindowsCopresenceRecord(nodesDir: string, nodeId: string): WindowsCopresenceRecord | null;
32
51
  export interface WindowsStopDecision {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network",
3
- "version": "2.3.0-preview.74",
3
+ "version": "2.3.0-preview.76",
4
4
  "description": "AI Agent Network CLI — Local-first multi-agent orchestration across 6 runtimes (Claude Code CLI / Claude Agent SDK / Codex SDK / Codex app-server / Grok Build ACP / OpenCode CLI) and 8+ LLM providers. Apache 2.0.",
5
5
  "type": "module",
6
6
  "main": "dist/src/client.js",