@nocoo/eagle-agent 0.3.0 → 0.5.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.
@@ -0,0 +1,100 @@
1
+ import { z } from "zod";
2
+ const id = z.string().min(1).max(160);
3
+ export const SubscriptionSchema = z.strictObject({
4
+ spaceId: id,
5
+ subscriptionId: id,
6
+ });
7
+ export const LivePaneSchema = z.strictObject({
8
+ id,
9
+ terminalId: id,
10
+ title: z.string().max(240),
11
+ rect: z.strictObject({
12
+ x: z.number().min(0).max(1),
13
+ y: z.number().min(0).max(1),
14
+ width: z.number().positive().max(1),
15
+ height: z.number().positive().max(1),
16
+ }),
17
+ });
18
+ export const TopologySchema = SubscriptionSchema.extend({
19
+ type: z.literal("topology"),
20
+ tabs: z
21
+ .array(z.strictObject({
22
+ id,
23
+ name: z.string().max(240),
24
+ panes: z.array(LivePaneSchema).max(32),
25
+ }))
26
+ .max(16),
27
+ }).refine((v) => v.tabs.reduce((n, t) => n + t.panes.length, 0) <= 32);
28
+ export const InputSchema = z
29
+ .strictObject({
30
+ type: z.literal("input"),
31
+ seq: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
32
+ paneId: id,
33
+ terminalId: id,
34
+ text: z.string().max(8000).default(""),
35
+ keys: z
36
+ .array(z.enum([
37
+ "enter",
38
+ "esc",
39
+ "tab",
40
+ "shift+tab",
41
+ "backspace",
42
+ "ctrl+c",
43
+ "ctrl+d",
44
+ "ctrl+l",
45
+ ]))
46
+ .max(4)
47
+ .default([]),
48
+ })
49
+ .refine((v) => v.text.length > 0 || v.keys.length > 0);
50
+ export const ViewerMessageSchema = z.union([
51
+ InputSchema,
52
+ z.strictObject({
53
+ type: z.literal("rendered"),
54
+ deliveryId: z.number().int().positive(),
55
+ deliveryBytes: z.number().int().positive(),
56
+ }),
57
+ z.strictObject({ type: z.enum(["ping", "control", "release"]) }),
58
+ ]);
59
+ export const FrameSchema = SubscriptionSchema.extend({
60
+ type: z.literal("frame"),
61
+ paneId: id,
62
+ terminalId: id,
63
+ revision: z.number().int().nonnegative(),
64
+ text: z.string().max(32000),
65
+ observedAt: z.string().datetime(),
66
+ deliveryId: z.number().int().positive().optional(),
67
+ deliveryBytes: z.number().int().positive().optional(),
68
+ });
69
+ export const AckSchema = z.strictObject({
70
+ type: z.literal("ack"),
71
+ clientId: id,
72
+ seq: z.number().int().positive(),
73
+ status: z.enum(["submitted", "rejected", "unknown"]),
74
+ });
75
+ export const AgentMessageSchema = z.union([
76
+ TopologySchema,
77
+ FrameSchema,
78
+ AckSchema,
79
+ SubscriptionSchema.extend({ type: z.literal("unavailable") }),
80
+ z.strictObject({ type: z.literal("ping") }),
81
+ ]);
82
+ export const BridgeMessageSchema = z.union([
83
+ z.strictObject({
84
+ type: z.literal("subscriptions"),
85
+ spaces: z.array(SubscriptionSchema).max(4),
86
+ }),
87
+ InputSchema.safeExtend({ ...SubscriptionSchema.shape, clientId: id }),
88
+ z.strictObject({ type: z.literal("pong") }),
89
+ ]);
90
+ export const LiveServerMessageSchema = z.union([
91
+ TopologySchema,
92
+ FrameSchema,
93
+ AckSchema.omit({ clientId: true }),
94
+ z.strictObject({
95
+ type: z.literal("status"),
96
+ online: z.boolean(),
97
+ control: z.boolean(),
98
+ }),
99
+ z.strictObject({ type: z.literal("pong") }),
100
+ ]);
@@ -0,0 +1,114 @@
1
+ import { z } from "zod";
2
+ const id = z
3
+ .string()
4
+ .min(1)
5
+ .max(160)
6
+ .regex(/^[\w.:/-]+$/);
7
+ const timestamp = z.iso.datetime().transform((v) => new Date(v).toISOString());
8
+ const text = z.string().trim().min(1).max(1200);
9
+ const refs = z.array(z.string().regex(/^[a-f0-9]{64}$/)).max(30);
10
+ export const SummaryCheckSchema = z.strictObject({
11
+ spaceId: id,
12
+ paneId: id,
13
+ taskId: id,
14
+ basis: refs,
15
+ observedAt: timestamp,
16
+ });
17
+ export const SemanticSummarySchema = z.strictObject({
18
+ task: text,
19
+ phase: z.enum([
20
+ "understand",
21
+ "implement",
22
+ "verify",
23
+ "deliver",
24
+ "waiting",
25
+ "complete",
26
+ "unknown",
27
+ ]),
28
+ progress: text,
29
+ outcomes: z
30
+ .array(z.strictObject({
31
+ kind: z.enum(["result", "test", "commit", "deployment"]),
32
+ text,
33
+ evidenceRefs: refs,
34
+ }))
35
+ .max(12),
36
+ blocker: text.nullable(),
37
+ nextStep: text,
38
+ rationale: text,
39
+ evidenceRefs: refs,
40
+ });
41
+ export const SummaryUpdateSchema = SummaryCheckSchema.extend({
42
+ summary: SemanticSummarySchema,
43
+ });
44
+ export const SummaryBatchSchema = z
45
+ .strictObject({
46
+ protocolVersion: z.literal(1),
47
+ machineId: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,79}$/),
48
+ managerId: id,
49
+ sequence: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
50
+ sentAt: timestamp,
51
+ updates: z.array(SummaryUpdateSchema).max(1000),
52
+ checks: z.array(SummaryCheckSchema).max(1000),
53
+ })
54
+ .refine((b) => {
55
+ const keys = [...b.updates, ...b.checks].map(taskKey);
56
+ return keys.length <= 1000 && new Set(keys).size === keys.length;
57
+ }, "Duplicate or too many pane entries");
58
+ export const paneKey = (p) => `${encodeURIComponent(p.spaceId)}/${encodeURIComponent(p.paneId)}`;
59
+ export const taskKey = (p) => `${paneKey(p)}/${encodeURIComponent(p.taskId)}`;
60
+ export function canonical(value) {
61
+ if (Array.isArray(value))
62
+ return `[${value.map(canonical).join(",")}]`;
63
+ if (value && typeof value === "object")
64
+ return `{${Object.entries(value)
65
+ .sort(([a], [b]) => a.localeCompare(b))
66
+ .map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`)
67
+ .join(",")}}`;
68
+ return JSON.stringify(value);
69
+ }
70
+ export async function digest(value) {
71
+ return Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonical(value)))), (b) => b.toString(16).padStart(2, "0")).join("");
72
+ }
73
+ function deterministicEvidence(evidence) {
74
+ return evidence.filter((e) => /^(git:HEAD\+status$|herdr:process-info|codex:(thread-goal|final-message|turn-event|tool-event)|grok:final-message|pi:final-message)/.test(e.source));
75
+ }
76
+ const stableEvidence = (e) => ({
77
+ ...e,
78
+ observedAt: ["git", "process"].includes(e.kind) ? null : e.observedAt,
79
+ });
80
+ export async function evidenceKeys(evidence) {
81
+ return Object.fromEntries(await Promise.all(deterministicEvidence(evidence).map(async (e) => [
82
+ await digest(stableEvidence(e)),
83
+ e,
84
+ ])));
85
+ }
86
+ export function semanticContent(entry) {
87
+ return canonical({ taskId: entry.taskId, summary: entry.summary });
88
+ }
89
+ export const PHASE_LABEL = {
90
+ understand: "梳理需求",
91
+ implement: "实施中",
92
+ verify: "验证中",
93
+ deliver: "交付中",
94
+ waiting: "等待处理",
95
+ complete: "声称完成",
96
+ unknown: "待判断",
97
+ };
98
+ export function summaryFreshness(summary, pane, lastSeen, now, capturedAt = now, availability) {
99
+ if (summary.taskId !== pane.task.id)
100
+ return "superseded";
101
+ if (!lastSeen || Date.parse(now) - Date.parse(lastSeen) > 90_000)
102
+ return "disconnected";
103
+ if (Date.parse(now) - Date.parse(summary.checkedAt) > 300_000)
104
+ return "stale";
105
+ if (availability || Date.parse(now) - Date.parse(capturedAt) > 90_000)
106
+ return "stale";
107
+ const facts = (items) => deterministicEvidence(items)
108
+ .filter((e) => e.taskId === pane.task.id)
109
+ .map((e) => canonical(stableEvidence(e)))
110
+ .sort();
111
+ if (canonical(facts(pane.evidence)) !== canonical(facts(summary.evidence)))
112
+ return "stale";
113
+ return "current";
114
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocoo/eagle-agent",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Report all local Herdr spaces and machine resources to Eagle",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -24,6 +24,7 @@
24
24
  "prepack": "npm run build"
25
25
  },
26
26
  "dependencies": {
27
+ "ws": "^8.21.3",
27
28
  "zod": "^4.1.0"
28
29
  },
29
30
  "publishConfig": {