@sleep2agi/agent-network 2.3.0-preview.1 → 2.3.0-preview.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +35 -324
  2. package/dist/bin/anet.cjs +101 -0
  3. package/dist/bin/cli.d.ts +3 -1
  4. package/dist/bin/cli.js +20 -1
  5. package/dist/bin/goal-wake-log-render.d.ts +31 -0
  6. package/dist/src/active-network-task.d.ts +28 -0
  7. package/dist/src/batch-workdir.d.ts +9 -0
  8. package/dist/src/bootstrap-password-db.d.ts +13 -0
  9. package/dist/src/channel-attachments.d.ts +29 -0
  10. package/dist/src/channel-meta.d.ts +10 -0
  11. package/dist/src/channel-task-trace.d.ts +14 -0
  12. package/dist/src/claude-vendor-env.d.ts +28 -0
  13. package/dist/src/cli-args.d.ts +8 -0
  14. package/dist/src/cli-version-display.d.ts +16 -0
  15. package/dist/src/client-task-trace.d.ts +9 -0
  16. package/dist/src/client.js +1 -1
  17. package/dist/src/codex-auth-fingerprint.d.ts +211 -0
  18. package/dist/src/codex-copresence-preflight.d.ts +65 -0
  19. package/dist/src/codex-copresence-profile.d.ts +125 -0
  20. package/dist/src/codex-copresence-recovery.d.ts +43 -0
  21. package/dist/src/codex-copresence-thread.d.ts +13 -0
  22. package/dist/src/codex-lifecycle-account.d.ts +104 -0
  23. package/dist/src/codex-lifecycle-canary.d.ts +20 -0
  24. package/dist/src/codex-lifecycle-facts.d.ts +53 -0
  25. package/dist/src/codex-lifecycle-fork.d.ts +53 -0
  26. package/dist/src/codex-lifecycle-preflight.d.ts +77 -0
  27. package/dist/src/codex-lifecycle-receipt.d.ts +44 -0
  28. package/dist/src/codex-lifecycle-restart.d.ts +86 -0
  29. package/dist/src/codex-model-default.d.ts +6 -0
  30. package/dist/src/codex-tui-client-health.d.ts +17 -0
  31. package/dist/src/commhub-response.d.ts +1 -0
  32. package/dist/src/controlled-upload.d.ts +83 -0
  33. package/dist/src/copresence-deps.d.ts +22 -0
  34. package/dist/src/copresence-identity.d.ts +353 -0
  35. package/dist/src/copresence-startup-diagnosis.d.ts +17 -0
  36. package/dist/src/daemon-capability-display.d.ts +110 -0
  37. package/dist/src/daemon-capability-display.js +21 -0
  38. package/dist/src/daemon-runtime-path-preflight.d.ts +22 -0
  39. package/dist/src/daemon-runtime-staleness.d.ts +13 -0
  40. package/dist/src/dashboard-managed-process.d.ts +35 -0
  41. package/dist/src/display-width.d.ts +27 -0
  42. package/dist/src/doctor-node-count.d.ts +28 -0
  43. package/dist/src/environ-alias.d.ts +14 -0
  44. package/dist/src/grok-attach-client.d.ts +129 -0
  45. package/dist/src/grok-build-drift.d.ts +15 -0
  46. package/dist/src/grok-copresence-disclosure.d.ts +11 -0
  47. package/dist/src/grok-copresence-orchestration.d.ts +75 -0
  48. package/dist/src/grok-copresence-profile.d.ts +65 -0
  49. package/dist/src/host-telemetry.d.ts +14 -0
  50. package/dist/src/hub-health-timeout.d.ts +4 -0
  51. package/dist/src/hub-time-display.d.ts +20 -0
  52. package/dist/src/hub-version-skew.d.ts +19 -0
  53. package/dist/src/im/access-resolve.d.ts +103 -0
  54. package/dist/src/im/correlation-store.d.ts +34 -0
  55. package/dist/src/im/feishu/adapter.d.ts +149 -1
  56. package/dist/src/im/feishu/bridge.d.ts +85 -14
  57. package/dist/src/im/feishu/config.d.ts +31 -0
  58. package/dist/src/im/feishu/hub-upload.d.ts +88 -0
  59. package/dist/src/im/feishu/markdown-image-renderer.d.ts +61 -0
  60. package/dist/src/im/feishu/outbound-marker.d.ts +140 -0
  61. package/dist/src/im/feishu/outbound-paths.d.ts +50 -0
  62. package/dist/src/im/feishu/outbound-route.d.ts +62 -0
  63. package/dist/src/im/feishu/worker-lifecycle.d.ts +13 -0
  64. package/dist/src/im/feishu/worker.js +403 -16
  65. package/dist/src/im/types.d.ts +38 -1
  66. package/dist/src/inbox-drain.d.ts +20 -0
  67. package/dist/src/local-process-state.d.ts +12 -0
  68. package/dist/src/locale-diagnostic.d.ts +12 -0
  69. package/dist/src/ls-columns.d.ts +15 -0
  70. package/dist/src/mock-llm.d.ts +12 -0
  71. package/dist/src/node-activity-log.d.ts +4 -0
  72. package/dist/src/node-not-found.d.ts +1 -0
  73. package/dist/src/node-running-guard.d.ts +9 -0
  74. package/dist/src/node-server-payload.d.ts +66 -0
  75. package/dist/src/node-server.js +7 -1
  76. package/dist/src/normalize-runtime.d.ts +36 -0
  77. package/dist/src/offline-age.d.ts +29 -0
  78. package/dist/src/one-line-cell.d.ts +20 -0
  79. package/dist/src/opencode-agent-node-pair.d.ts +25 -0
  80. package/dist/src/opencode-auth-login.d.ts +43 -0
  81. package/dist/src/opencode-launch-env.d.ts +13 -0
  82. package/dist/src/opencode-owner-mode.d.ts +6 -0
  83. package/dist/src/opencode-package-binary.d.ts +26 -0
  84. package/dist/src/opencode-pin.d.ts +44 -0
  85. package/dist/src/opencode-preset.d.ts +73 -0
  86. package/dist/src/opencode-runtime-binding.d.ts +30 -0
  87. package/dist/src/opencode-safe-root.d.ts +36 -0
  88. package/dist/src/opencode-smoke-env.d.ts +1 -0
  89. package/dist/src/outbound-attachments.d.ts +60 -0
  90. package/dist/src/outbound-tool-names.d.ts +1 -0
  91. package/dist/src/owned-roots.d.ts +58 -0
  92. package/dist/src/owner-env-file.d.ts +2 -0
  93. package/dist/src/package-mode-preflight.d.ts +41 -0
  94. package/dist/src/posix-codex-copresence.d.ts +8 -0
  95. package/dist/src/posix-modes.d.ts +36 -0
  96. package/dist/src/primary-network.d.ts +23 -0
  97. package/dist/src/private-state.d.ts +10 -0
  98. package/dist/src/process-telemetry.d.ts +12 -0
  99. package/dist/src/profile-serialize.d.ts +1 -0
  100. package/dist/src/project-key.d.ts +1 -0
  101. package/dist/src/reply-local-links.d.ts +47 -0
  102. package/dist/src/reply-originator.d.ts +20 -0
  103. package/dist/src/resume-runtime-infer.d.ts +18 -0
  104. package/dist/src/secret-shell-guidance.d.ts +3 -0
  105. package/dist/src/session-status-class.d.ts +48 -0
  106. package/dist/src/sibling-agent-node.d.ts +23 -0
  107. package/dist/src/stale-socket.d.ts +118 -0
  108. package/dist/src/subcommand-redirect.d.ts +26 -0
  109. package/dist/src/supervise-child.d.ts +69 -0
  110. package/dist/src/task-trace.d.ts +33 -0
  111. package/dist/src/tmux-attach.d.ts +8 -0
  112. package/dist/src/tmux-capability.d.ts +58 -0
  113. package/dist/src/tmux-exact-target.d.ts +34 -0
  114. package/dist/src/tmux-pane-prompt.d.ts +22 -0
  115. package/dist/src/token-cli.d.ts +13 -0
  116. package/dist/src/tool-allowlist.d.ts +28 -0
  117. package/dist/src/unsafe-package-path-reason.d.ts +17 -0
  118. package/dist/src/win-launcher.d.ts +20 -0
  119. package/dist/src/windows-codex-copresence.d.ts +60 -0
  120. package/package.json +14 -6
@@ -0,0 +1,35 @@
1
+ export type DashboardLaunchSource = "npx" | "global";
2
+ export interface DashboardLaunchRecord {
3
+ schema: 1;
4
+ port: number;
5
+ listener_pid: number;
6
+ listener_birth: string;
7
+ source: DashboardLaunchSource;
8
+ source_key: string;
9
+ recorded_at: string;
10
+ }
11
+ export type DashboardListenerDecision = {
12
+ action: "start";
13
+ } | {
14
+ action: "already_running";
15
+ pid: number;
16
+ } | {
17
+ action: "terminate_owned_stale";
18
+ pid: number;
19
+ reason: "unhealthy" | "version_changed";
20
+ } | {
21
+ action: "refuse";
22
+ reason: string;
23
+ };
24
+ export declare function isDashboardProcessCommand(command: string): boolean;
25
+ export declare function decideDashboardListener(input: {
26
+ port: number;
27
+ listenerPids: number[];
28
+ record: DashboardLaunchRecord | null;
29
+ listenerBirth: string | null;
30
+ listenerCommand: string | null;
31
+ desiredSource: DashboardLaunchSource;
32
+ desiredSourceKey: string;
33
+ healthy: boolean;
34
+ }): DashboardListenerDecision;
35
+ export declare function parseDashboardLaunchRecord(value: unknown): DashboardLaunchRecord | null;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 终端里的**显示列宽** —— CJK 字符占 2 列,而 `String.length` / `padEnd` 只数码元。
3
+ *
4
+ * 🔴 这份知识仓里本来就有,但它只活在**一个测试文件**里
5
+ * (`daemon-capability-display.test.ts` 的 `displayWidth`,2026-08-30 由 macOS 真机
6
+ * 上「合成首行约 99 列、80 列终端折行且折点落在句子中间」催生)。
7
+ * 生产代码没有它,于是所有用 `padEnd` 对齐的表在中文别名下都是歪的。
8
+ *
9
+ * 实测(2026-08-31,`anet node ls` 对着一支全中文别名的军团):
10
+ *
11
+ * NAME RUNTIME STATUS
12
+ * ──────────────────── ────────────── ────────
13
+ * 通信工程马 claude-code-cli idle ← RUNTIME 列起点
14
+ * 通信IM马 claude-code-cli idle ← 不一样
15
+ * 通信SDK牛 codex-sdk idle ← 又不一样
16
+ *
17
+ * `通信工程马`.length 是 5,`padEnd(20)` 补 15 个空格 —— 但它渲染出来是 10 列,
18
+ * 于是这一行实际占 25 列而不是 20。**别名里 CJK 越多,歪得越远。**
19
+ */
20
+ export declare function displayWidth(s: unknown): number;
21
+ /**
22
+ * 按**显示列**右补空格。已经不短于目标宽度时原样返回(与 `padEnd` 一致,不截断)。
23
+ *
24
+ * 🔴 不截断是有意的:截断会引出「截到一半的宽字符」这个新问题,
25
+ * 而调用方要的只是对齐。**一个修对齐的改动不该顺手引入截断语义。**
26
+ */
27
+ export declare function padDisplayEnd(s: unknown, width: number): string;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `anet doctor` 原先对「本机配置了几个节点」的判定是:
3
+ *
4
+ * check("Nodes configured", ids.length > 0, `${ids.length} node(s)`);
5
+ *
6
+ * 于是 **0 个节点 → ❌**。而一个刚装好、还没建节点的人跑 `anet doctor`,
7
+ * 看到的是:
8
+ *
9
+ * ❌ Nodes configured — 0 node(s)
10
+ * Result: 9 ok, 3 warnings, 2 errors
11
+ *
12
+ * 🔴 「还没有节点」是**全新安装的预期状态**,不是故障。把它报成 error,
13
+ * 等于在新用户的第一次诊断里制造一个假警报 —— 而这条命令存在的意义
14
+ * 正是告诉他「你这台机器现在好不好」。
15
+ *
16
+ * 🔴 但也不能反过来直接判成「一切正常」:同一个 0 对一个**本来有节点**的人
17
+ * 意味着配置目录不见了。doctor 手上只有一个数字,**分不出这两种现实**。
18
+ * 所以这里既不报错也不报好 —— 把两种都说出来,让读的人自己对号,
19
+ * 并给出各自的下一步。
20
+ */
21
+ export type NodeCountLine = {
22
+ ok: true;
23
+ detail: string;
24
+ } | {
25
+ ok: false;
26
+ info: string;
27
+ };
28
+ export declare function nodeCountLine(count: number): NodeCountLine;
@@ -0,0 +1,14 @@
1
+ /** Parse the environ blob (NUL-separated key=val) and return the
2
+ * COMMHUB_ALIAS value if present, else null. Exported for testing. */
3
+ export declare function parseEnvironAlias(environBlob: string): string | null;
4
+ /** Read /proc/<pid>/environ and return the COMMHUB_ALIAS value.
5
+ * Any file-read error (not-a-Linux, permission denied, race with
6
+ * process exit) returns null — caller must not fail-closed on that
7
+ * (the process is stale/gone/inaccessible, not our problem). */
8
+ export declare function readEnvironAlias(pid: number): string | null;
9
+ /** Scan /proc for pids whose COMMHUB_ALIAS env matches any of the
10
+ * target aliases. Linux-only (procfs); returns null on non-Linux or
11
+ * when /proc is unreadable — caller MUST fail-closed on null per
12
+ * the same #180 R2 fail-closed contract as findNodeProcessesByAlias.
13
+ * Self-pid + init (PID 1) are excluded. */
14
+ export declare function findEnvironAliasMatches(aliases: Iterable<string>, selfPid: number): number[] | null;
@@ -0,0 +1,129 @@
1
+ import type { Duplex } from "node:stream";
2
+ export declare const GROK_ATTACH_PROTOCOL = "anet-grok-copresence-attach";
3
+ export declare const GROK_ATTACH_PROTOCOL_VERSION = 1;
4
+ export declare const GROK_ATTACH_DEFAULT_MAX_FRAME_BYTES: number;
5
+ export declare const GROK_ATTACH_DEFAULT_MAX_BUFFER_BYTES: number;
6
+ export declare const GROK_ATTACH_DEFAULT_HANDSHAKE_TIMEOUT_MS = 5000;
7
+ export declare const GROK_ATTACH_DEFAULT_CLOSE_TIMEOUT_MS = 1000;
8
+ export type GrokAttachJsonValue = null | boolean | number | string | GrokAttachJsonValue[] | {
9
+ [key: string]: GrokAttachJsonValue;
10
+ };
11
+ export interface GrokAttachHelloFrame {
12
+ type: "hello";
13
+ protocol: typeof GROK_ATTACH_PROTOCOL;
14
+ version: typeof GROK_ATTACH_PROTOCOL_VERSION;
15
+ alias: string;
16
+ sessionId: string;
17
+ [key: string]: unknown;
18
+ }
19
+ export interface GrokAttachOutputFrame {
20
+ type: "output";
21
+ data: string;
22
+ encoding: "base64";
23
+ }
24
+ export interface GrokAttachStatusFrame {
25
+ type: "status";
26
+ status: GrokAttachJsonValue;
27
+ }
28
+ export interface GrokAttachErrorFrame {
29
+ type: "error";
30
+ code: string;
31
+ message: string;
32
+ fatal: boolean;
33
+ }
34
+ export interface GrokAttachDetachFrame {
35
+ type: "detach";
36
+ }
37
+ export type GrokAttachServerFrame = GrokAttachHelloFrame | GrokAttachOutputFrame | GrokAttachStatusFrame | GrokAttachErrorFrame | GrokAttachDetachFrame;
38
+ export interface GrokAttachInputFrame {
39
+ type: "input";
40
+ data: string;
41
+ encoding: "base64";
42
+ }
43
+ export interface GrokAttachResizeFrame {
44
+ type: "resize";
45
+ cols: number;
46
+ rows: number;
47
+ }
48
+ /** Asks the node to switch models (issue #879); allowed on control connections. */
49
+ export interface GrokAttachSetModelFrame {
50
+ type: "set-model";
51
+ model: string;
52
+ }
53
+ export type GrokAttachClientFrame = GrokAttachInputFrame | GrokAttachResizeFrame | GrokAttachSetModelFrame | GrokAttachDetachFrame;
54
+ export interface GrokAttachInputSource {
55
+ on(event: "data", listener: (chunk: unknown) => void): unknown;
56
+ on(event: "end", listener: () => void): unknown;
57
+ off?(event: "data" | "end", listener: (...args: any[]) => void): unknown;
58
+ removeListener?(event: "data" | "end", listener: (...args: any[]) => void): unknown;
59
+ pause?(): unknown;
60
+ resume?(): unknown;
61
+ }
62
+ export interface GrokAttachOutputSink {
63
+ write(chunk: Uint8Array): boolean | void;
64
+ columns?: number;
65
+ rows?: number;
66
+ once?(event: "drain", listener: () => void): unknown;
67
+ }
68
+ export interface GrokAttachSignalSource {
69
+ on(event: "SIGWINCH", listener: () => void): unknown;
70
+ off?(event: "SIGWINCH", listener: () => void): unknown;
71
+ removeListener?(event: "SIGWINCH", listener: () => void): unknown;
72
+ }
73
+ export interface GrokAttachSocketStat {
74
+ uid: number;
75
+ isSocket(): boolean;
76
+ isSymbolicLink(): boolean;
77
+ }
78
+ export interface GrokAttachDependencies {
79
+ lstat?: (socketPath: string) => Promise<GrokAttachSocketStat>;
80
+ getuid?: () => number | undefined;
81
+ connect?: (socketPath: string) => Duplex;
82
+ }
83
+ export interface GrokAttachClientOptions {
84
+ socketPath: string;
85
+ input: GrokAttachInputSource;
86
+ output: GrokAttachOutputSink;
87
+ signalSource?: GrokAttachSignalSource;
88
+ terminalSize?: () => {
89
+ cols: number | undefined;
90
+ rows: number | undefined;
91
+ };
92
+ maxFrameBytes?: number;
93
+ maxBufferBytes?: number;
94
+ handshakeTimeoutMs?: number;
95
+ closeTimeoutMs?: number;
96
+ detachOnInputEnd?: boolean;
97
+ onHello?: (frame: GrokAttachHelloFrame) => void;
98
+ onStatus?: (frame: GrokAttachStatusFrame) => void;
99
+ onError?: (error: Error, frame?: GrokAttachErrorFrame) => void;
100
+ onDetach?: (frame: GrokAttachDetachFrame) => void;
101
+ dependencies?: GrokAttachDependencies;
102
+ }
103
+ export type GrokAttachCloseReason = "local-detach" | "input-end" | "remote-detach" | "socket-close" | "socket-error" | "protocol-error";
104
+ export interface GrokAttachCloseInfo {
105
+ reason: GrokAttachCloseReason;
106
+ error?: Error;
107
+ }
108
+ export interface GrokAttachSession {
109
+ readonly socketPath: string;
110
+ readonly closed: Promise<GrokAttachCloseInfo>;
111
+ detach(): void;
112
+ resize(cols?: number, rows?: number): void;
113
+ /**
114
+ * Ask the node to switch models (issue #879).
115
+ *
116
+ * The outcome does not come back from this call — it arrives as a `status`
117
+ * frame carrying `modelSwitch`, because the node may accept, refuse
118
+ * (busy / unchanged / invalid), or fail while re-spawning. Callers read it
119
+ * through `onStatus`.
120
+ */
121
+ setModel(model: string): void;
122
+ }
123
+ export declare class GrokAttachRemoteError extends Error {
124
+ readonly code: string;
125
+ readonly fatal: boolean;
126
+ constructor(frame: GrokAttachErrorFrame);
127
+ }
128
+ export declare function validateGrokAttachSocket(socketPath: string, dependencies?: GrokAttachDependencies): Promise<void>;
129
+ export declare function connectGrokAttach(options: GrokAttachClientOptions): Promise<GrokAttachSession>;
@@ -0,0 +1,15 @@
1
+ /** 从一段日志文本里取出**最后一次**出现的 grok 版本串。
2
+ * 🔴 取最后一次,不是第一次:日志按时间追加,最后一次才是这个进程最近一次
3
+ * 启动时用的那个。取第一次会在日志轮转/多次重启后指向历史。 */
4
+ export declare function parseGrokBuildFromLog(text: string): string | undefined;
5
+ /** 从 `grok --version` 的输出里取版本串(同一形状)。 */
6
+ export declare function parseGrokBuildFromVersionOutput(out: string): string | undefined;
7
+ export type GrokBuildDriftKind = "match" | "drift" | "unknown-current" | "unknown-started";
8
+ export interface GrokBuildDrift {
9
+ readonly kind: GrokBuildDriftKind;
10
+ readonly started?: string;
11
+ readonly current?: string;
12
+ /** 一句人读的话。四种 kind 四句不同的话 —— 含糊化等于把这一格的功能删掉。 */
13
+ readonly line: string;
14
+ }
15
+ export declare function describeGrokBuildDrift(started: string | undefined, current: string | undefined): GrokBuildDrift;
@@ -0,0 +1,11 @@
1
+ export type GrokCopresenceSessionDisclosure = "configured" | "new" | "resume";
2
+ export type GrokCopresenceDisclosure = {
3
+ profile: "commhub-only" | "x-search" | "repo-read" | "invalid";
4
+ lines: readonly string[];
5
+ };
6
+ /**
7
+ * Describe only the exact tool profiles accepted by the pinned Grok TUI
8
+ * runtime. This is deliberately exact: a near-miss must never be presented as
9
+ * either reviewed capability set.
10
+ */
11
+ export declare function grokCopresenceDisclosure(tools: unknown, session?: GrokCopresenceSessionDisclosure): GrokCopresenceDisclosure;
@@ -0,0 +1,75 @@
1
+ /** tmux session names. The attachable TUI owns the bare alias so that
2
+ * `tmux attach -t '=<alias>'` lands a human on the TUI, matching the codex
3
+ * lane exactly — an operator should not have to remember which runtime a node
4
+ * uses in order to know where to attach. */
5
+ export declare function grokCopresenceSessions(displayName: string): {
6
+ node: string;
7
+ tui: string;
8
+ };
9
+ export type GrokAttachSocketState = "missing" | "not_a_socket" | "ready";
10
+ /** Readiness is "the attach socket exists AND is a socket", never "the child
11
+ * process started". A grok leader that dies during startup leaves either no
12
+ * socket at all or a stale regular file from an aborted run; both used to read
13
+ * as ready because nothing looked. */
14
+ export declare function grokAttachSocketState(entry: {
15
+ isSocket(): boolean;
16
+ } | null | undefined): GrokAttachSocketState;
17
+ /** Set on the node child so that `anet node start` inside the tmux session does
18
+ * not re-enter this orchestration and fork bomb. The codex lane guards on the
19
+ * same variable (cli.ts: `process.env.ANET_COPRESENCE_BRIDGE !== "1"`); using a
20
+ * second name would leave two guards that must be kept in sync by hand. */
21
+ export declare const GROK_COPRESENCE_CHILD_ENV = "ANET_COPRESENCE_BRIDGE";
22
+ export interface GrokCopresenceDiagnosis {
23
+ readonly ok: boolean;
24
+ readonly lines: readonly string[];
25
+ /** #1768 —— 非阻断提示:平台能跑,但 agent-node 的能力表说它少了内核层保证。
26
+ * ok=true 时也可能非空;调用方打印它,不退出。 */
27
+ readonly notices: readonly string[];
28
+ }
29
+ /** One block naming every reason this node cannot run the shared TUI, not one
30
+ * exit per reason. A node that is both the wrong runtime AND missing its
31
+ * attach socket should learn both in one run. */
32
+ /** Platforms whose PTY / IPC / isolation primitives the grok co-presence lane
33
+ * has actually been validated on. agent-node refuses anywhere else, and it does
34
+ * so only after the node has been created and started once:
35
+ *
36
+ * [agent-node] grok-build-cli co-presence 无法在 darwin 上运行,缺少:
37
+ * 平台 darwin 尚未验证过共存所需的 PTY / IPC / 隔离原语
38
+ *
39
+ * Learned the expensive way on a Mac mini: install anet, install agent-node,
40
+ * create the node, start it — and only then find out the platform is refused.
41
+ * A one-command launcher that lets someone get that far has not saved them
42
+ * anything, so say it in the same breath as every other gap. */
43
+ export declare const GROK_COPRESENCE_PLATFORMS: readonly NodeJS.Platform[];
44
+ /** 能跑但没有内核层强制的平台 —— agent-node 启动时逐条打印 reducedGuarantees,这里只提前说一句。 */
45
+ export declare const GROK_COPRESENCE_REDUCED_GUARANTEE_PLATFORMS: readonly NodeJS.Platform[];
46
+ export declare function diagnoseGrokCopresence(input: {
47
+ runtime: string;
48
+ displayName: string;
49
+ grokCopresence?: boolean;
50
+ grokAttachSocket?: string;
51
+ platform?: NodeJS.Platform;
52
+ }): GrokCopresenceDiagnosis;
53
+ /** True when a plain `anet node start <name>` (no flag) should bring the shared
54
+ * TUI up by itself.
55
+ *
56
+ * 🔴 Deliberately NOT keyed on `grokCopresence`. That field is set to true at
57
+ * CREATE time for every grok-build-cli node, so reading it here would silently
58
+ * change what `anet node start` does for every grok node that already exists —
59
+ * they would stop running in the foreground and fork into tmux, unasked. Caught
60
+ * exactly that way: a start without the flag entered the orchestration.
61
+ *
62
+ * The codex lane looks the same but is not: `codexCopresence` is written only
63
+ * when the operator passes the flag. So grok needs its own record of the
64
+ * operator's choice, and `grokCopresenceAuto` is it. */
65
+ export declare function grokCopresenceRequested(flagPassed: boolean, profile: {
66
+ runtime?: unknown;
67
+ grokCopresence?: unknown;
68
+ grokCopresenceAuto?: unknown;
69
+ }): boolean;
70
+ /** Record the operator's opt-in so the NEXT start needs no flag — and only then.
71
+ * Never infer it from `grokCopresence`, which create sets by default. */
72
+ export declare function shouldPersistGrokCopresence(flagPassed: boolean, profile: {
73
+ runtime?: unknown;
74
+ grokCopresenceAuto?: unknown;
75
+ }): boolean;
@@ -0,0 +1,65 @@
1
+ export declare const GROK_UNIX_SOCKET_PATH_MAX_BYTES = 100;
2
+ export declare const GROK_COPRESENCE_CAPABILITY_MARKER = "ANET_CAPABILITY_GROK_COPRESENCE_V2";
3
+ export declare const GROK_PREVIEW_RESOLVER_INHERITED_ENV_KEYS: readonly ["PATH", "TMPDIR", "TMP", "TEMP", "LANG", "LC_ALL", "LC_CTYPE", "TZ"];
4
+ export declare const GROK_AGENT_NODE_INHERITED_ENV_KEYS: readonly ["PATH", "HOME", "TMPDIR", "TMP", "TEMP", "LANG", "LC_ALL", "LC_CTYPE", "TZ", "SHELL", "USER", "LOGNAME", "TERM", "COLORTERM", "NO_COLOR"];
5
+ export declare const GROK_AGENT_NODE_OPTIONAL_ENV_KEYS: readonly ["GROK_BINARY", "GROK_HOME", "FLOCK_BINARY", "SETPRIV_BINARY", "UNSHARE_BINARY", "GROK_CLI_TIMEOUT_MS", "GROK_HANDSHAKE_TIMEOUT_MS", "LOG_LEVEL", "ANET_GOAL_TICK_MS", "COMMHUB_MAX_GOALS_PER_NODE"];
6
+ /** Exact environment for the long-lived agent-node parent of the Grok TUI. */
7
+ export declare function buildGrokAgentNodeEnv(parentEnv: NodeJS.ProcessEnv): Record<string, string>;
8
+ export declare function grokPreviewResolverConfigPaths(home: string): {
9
+ directory: string;
10
+ userConfig: string;
11
+ globalConfig: string;
12
+ };
13
+ /**
14
+ * npm rejects loading the same file as both user and global config. Prepare
15
+ * two distinct, empty, owner-only files without following a final symlink so
16
+ * the resolver cannot inherit a user's ordinary npmrc credentials.
17
+ */
18
+ export declare function prepareGrokPreviewResolverConfigs(home: string): void;
19
+ /** Exact environment for the short-lived npm resolver and capability probe. */
20
+ export declare function buildGrokPreviewResolverEnv(parentEnv: NodeJS.ProcessEnv, home: string): Record<string, string>;
21
+ /** Old headless-only agent-node builds already advertised grok-build-cli. */
22
+ export declare function agentNodeHelpSupportsGrokCopresence(help: string): boolean;
23
+ export interface GrokCopresenceProfileFields {
24
+ grokCopresence: boolean;
25
+ grokLeaderSocket?: string;
26
+ grokAttachSocket?: string;
27
+ }
28
+ export interface GrokSocketPathOptions {
29
+ cwd?: string;
30
+ home?: string;
31
+ xdgRuntimeDir?: string;
32
+ uid?: number;
33
+ platform?: NodeJS.Platform;
34
+ }
35
+ /**
36
+ * Allocate deterministic Unix socket paths without creating anything.
37
+ *
38
+ * Grok's workspace sandbox does not admit an otherwise owner-controlled
39
+ * XDG_RUNTIME_DIR such as /run/user/<uid>. Keep the primary sockets under the
40
+ * node's owner-bound state home, which the runtime already admits, and use a
41
+ * short private tmp path only when the Unix socket length limit requires it.
42
+ * The runtime owns directory creation and permissions; `anet node create`
43
+ * only persists the identity of the two sockets.
44
+ */
45
+ export declare function grokCopresenceSocketPaths(nodeId: string, options?: GrokSocketPathOptions): {
46
+ leaderSocket: string;
47
+ attachSocket: string;
48
+ };
49
+ export type GrokAttachTarget = {
50
+ ok: true;
51
+ socketPath: string;
52
+ } | {
53
+ ok: false;
54
+ reason: "not_grok_build_cli" | "headless" | "missing_attach_socket";
55
+ };
56
+ /**
57
+ * `anet grok attach` eligibility. The CLI is the only human join path;
58
+ * this is the shipped decision so tests can drive it without a TTY.
59
+ */
60
+ export declare function resolveGrokAttachTarget(input: {
61
+ runtime: string;
62
+ grokCopresence?: unknown;
63
+ grokAttachSocket?: unknown;
64
+ }): GrokAttachTarget;
65
+ export declare function grokBuildCliCreationFields(runtime: string, nodeId: string, headless?: boolean, options?: GrokSocketPathOptions): GrokCopresenceProfileFields | Record<string, never>;
@@ -0,0 +1,14 @@
1
+ export interface HostTelemetry {
2
+ hostname: string;
3
+ ip: string | null;
4
+ cpu_load_1min: number | null;
5
+ cpu_cores: number | null;
6
+ mem_total_gb: number | null;
7
+ mem_used_gb: number | null;
8
+ mem_avail_gb: number | null;
9
+ disk_total_gb: number | null;
10
+ disk_used_gb: number | null;
11
+ disk_avail_gb: number | null;
12
+ }
13
+ export declare function getHostTelemetry(): HostTelemetry;
14
+ export declare function _clearHostTelemetryCache(): void;
@@ -0,0 +1,4 @@
1
+ export declare const LOOPBACK_HUB_HEALTH_TIMEOUT_MS = 2000;
2
+ export declare const REMOTE_HUB_HEALTH_TIMEOUT_MS = 10000;
3
+ export declare const HUB_HEALTH_TIMEOUT_ENV = "ANET_HUB_HEALTH_TIMEOUT_MS";
4
+ export declare function hubHealthTimeoutMs(hub: string, env?: NodeJS.ProcessEnv): number;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 把 hub 的 TEXT 时间戳显示给人看。
3
+ *
4
+ * 🔴 原先是**原样打印**:
5
+ *
6
+ * updated: 2026-08-31 00:20:11
7
+ *
8
+ * 那是 UTC,**没有任何时区标记**。同一时刻本地是 `08:22:41 CST` ——
9
+ * 于是一个 **2 分钟前**刚心跳过的节点,在 UTC+8 的用户眼里像是 **8 小时前**的。
10
+ *
11
+ * 这不是假想:2026-08-31 我自己就这么读错过一次,据此差点报出「舰队心跳陈旧
12
+ * 8.3 小时」。**CLI 在主动制造这个误读。**
13
+ *
14
+ * 所以显示两样东西:
15
+ * - **相对时长**(人真正想知道的那个)
16
+ * - 绝对值,并**显式标 UTC**(便于和日志、issue 里的时间对齐)
17
+ */
18
+ export declare function formatHubTime(raw: unknown, nowMs?: number): string;
19
+ /** 人读的时长。负数(时钟偏差/未来时间)如实说,不折成 0。 */
20
+ export declare function formatAgo(deltaMs: number): string;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * #1595 —— `anet doctor` 原先只印 hub 自报的版本:
3
+ *
4
+ * ✅ CommHub reachable (http://127.0.0.1:9200 v0.9.0-preview.38)
5
+ *
6
+ * 读的人**看不到自己这台 CLI 钉的是哪个**。实测(2026-08-31):生产 hub 在
7
+ * `.38`,而 `anet hub start` 的 `PINNED_SERVER_VERSION` 是 `.44` —— 两个数
8
+ * 差 6 个版本,而屏幕上只有一个。今天有一整条排查(agent→用户消息为什么
9
+ * 验不了端到端)就卡在这个差上:`.44` 之前 `send_desktop_message` 是
10
+ * fire-and-forget 且仍返回 `ok:true`,所以在 `.38` 上拿到的 `ok:true`
11
+ * 不是「用户看到了」的证据。
12
+ *
13
+ * 🔴 **只并排摆出两个数,不判断谁对、不给阈值、不发警告。**
14
+ * hub 比 CLI 的 pin 老或新都可能完全合理 —— 连的是别人运维的 hub、
15
+ * 本机 hub 还没重启、或者故意钉在旧版。给一个猜出来的「应该一致」
16
+ * 判据,会在这些正常情况下变成误报,而一个会误报的检查第一周就会被关掉。
17
+ * 让读的人自己看见差在哪,是这一格能诚实做到的全部。
18
+ */
19
+ export declare function formatHubVersionDetail(hubUrl: string, hubVersion: unknown, pinnedVersion: string): string;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Tiny loader that mirrors what `initTelegramChannel` does at boot —
3
+ * read the parsed access.json blob and produce both the raw allowFrom
4
+ * payload (handed to the resolver verbatim) AND a boot-time warning
5
+ * string for the empty / malformed case.
6
+ *
7
+ * Extracted so wiring-level regression tests (e.g. "channel state
8
+ * built from `{allowFrom: [123]}` MUST fail-closed when a message
9
+ * arrives") run without spinning up a real telegram channel + fs
10
+ * mocks. The previous bug — `.map(String)` rewriting [123] into
11
+ * ["123"] at init time — slipped past resolver-only unit tests
12
+ * because the resolver never saw the offending shape; the channel
13
+ * state did.
14
+ */
15
+ export interface LoadedTelegramAccess {
16
+ /** Raw value from access.json — handed verbatim to resolveTelegramAccess. */
17
+ allowFromRaw: unknown;
18
+ /** Boot-time warning string when allowFrom is empty / malformed; null otherwise. */
19
+ bootWarn: string | null;
20
+ }
21
+ export declare function loadTelegramAccess(opts: {
22
+ channelDir: string;
23
+ parsedAccess: {
24
+ allowFrom?: unknown;
25
+ } | null | undefined;
26
+ }): LoadedTelegramAccess;
27
+ /**
28
+ * Discriminated reason for an access decision. Callers can switch on
29
+ * `kind` for routing (e.g. `empty-fail-closed` warrants a louder boot
30
+ * warning than a simple `denied` mid-stream).
31
+ */
32
+ export type AccessKind = "wildcard-allow" | "explicit-allow" | "empty-fail-closed" | "denied";
33
+ export interface AccessDecision {
34
+ allow: boolean;
35
+ kind: AccessKind;
36
+ /** Short, machine-readable reason for logging. */
37
+ reason: string;
38
+ }
39
+ /**
40
+ * Normalise an allowFrom input into a deduped string[] suitable for
41
+ * the lookup checks. Accepts: a real string[] (the happy case), null /
42
+ * undefined (treated as empty), a non-array shape (defensive — treated
43
+ * as empty + reason surfaced).
44
+ */
45
+ export declare function normalizeAllowFrom(raw: unknown): {
46
+ list: string[];
47
+ malformed: boolean;
48
+ };
49
+ /**
50
+ * Resolve whether an inbound Telegram message should be processed.
51
+ *
52
+ * Fail-closed: empty / missing / malformed `allowFrom` always denies.
53
+ * Wildcard `"*"` always allows. Otherwise the sender's id OR username
54
+ * (after non-empty check) must match a list entry.
55
+ *
56
+ * @param opts.allowFrom the access.json `allowFrom` array
57
+ * @param opts.senderId numeric Telegram user id as string
58
+ * (callers already coerce; we trust)
59
+ * @param opts.senderUsername optional `@username` (no `@` prefix)
60
+ */
61
+ export declare function resolveTelegramAccess(opts: {
62
+ allowFrom: unknown;
63
+ senderId: string;
64
+ senderUsername?: string | null;
65
+ }): AccessDecision;
66
+ /**
67
+ * Resolve whether an inbound Feishu/Lark event should be processed.
68
+ *
69
+ * Same fail-closed contract as telegram. DM path checks `allowFrom`;
70
+ * group path checks `allowChats` first, then `groupPolicy` decides
71
+ * whether listed chats trigger the agent (`all` → trigger any message;
72
+ * `observe` → never trigger; `mention` → only on @-mention — caller
73
+ * still has to honour that flag, this helper just returns allow=true).
74
+ *
75
+ * @param opts.conversationType "dm" | "group" | "channel" | "thread"
76
+ * @param opts.allowFrom access.json `allowFrom` array
77
+ * @param opts.allowChats access.json `allowChats` array
78
+ * @param opts.senderId feishu open_id of the sender
79
+ * @param opts.conversationId feishu chat_id of the conversation
80
+ * @param opts.groupPolicy "all" | "mention" | "observe"
81
+ */
82
+ export declare function resolveFeishuAccess(opts: {
83
+ conversationType: "dm" | "group" | "channel" | "thread" | string;
84
+ allowFrom: unknown;
85
+ allowChats: unknown;
86
+ senderId: string;
87
+ conversationId: string;
88
+ groupPolicy: "all" | "mention" | "observe" | string;
89
+ }): AccessDecision;
90
+ /**
91
+ * Build the boot-time warning string when a channel starts with an
92
+ * empty / malformed allowlist. Caller emits this via `warn()` once at
93
+ * startup so operators see the fail-closed posture immediately rather
94
+ * than discovering it the first time a message gets denied.
95
+ *
96
+ * Returns `null` when the allowlist has at least one entry (no boot
97
+ * warning needed).
98
+ */
99
+ export declare function buildEmptyAllowlistWarn(opts: {
100
+ channel: string;
101
+ channelDir: string;
102
+ allowFrom: unknown;
103
+ }): string | null;
@@ -0,0 +1,34 @@
1
+ import type { IMCorrelationStore, IMTaskCorrelation } from "./types";
2
+ export interface JsonIMCorrelationStoreOptions {
3
+ now?: () => number;
4
+ /** How long idempotency keys remain valid. Defaults to 24h. */
5
+ seenTtlMs?: number;
6
+ /** How long terminal correlations remain queryable. Defaults to 24h. */
7
+ terminalTtlMs?: number;
8
+ }
9
+ /**
10
+ * Small durable store for RFC-020 §2.9④ / §4.4 correlation state.
11
+ *
12
+ * The first gateway PR needs this as a standalone data structure before the
13
+ * bridge is rewired: idempotency keys survive process restart, and task replies
14
+ * can be routed back to their originating IM conversation.
15
+ */
16
+ export declare class JsonIMCorrelationStore implements IMCorrelationStore {
17
+ private readonly path;
18
+ private readonly now;
19
+ private readonly seenTtlMs;
20
+ private readonly terminalTtlMs;
21
+ constructor(path: string, options?: JsonIMCorrelationStoreOptions);
22
+ hasSeen(idempotencyKey: string): Promise<string | null>;
23
+ recordSeen(idempotencyKey: string, taskId: string): Promise<void>;
24
+ getCorrelation(taskId: string): Promise<IMTaskCorrelation | null>;
25
+ putCorrelation(taskId: string, correlation: IMTaskCorrelation): Promise<void>;
26
+ updateStatus(taskId: string, status: IMTaskCorrelation["status"]): Promise<void>;
27
+ gc(now: number): Promise<{
28
+ removed: number;
29
+ }>;
30
+ private isExpired;
31
+ private load;
32
+ private save;
33
+ }
34
+ export declare function createJsonIMCorrelationStore(path: string, options?: JsonIMCorrelationStoreOptions): IMCorrelationStore;