@mebular/fleet 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agentdir.d.ts +43 -0
- package/dist/agentdir.d.ts.map +1 -0
- package/dist/agentdir.js +107 -0
- package/dist/agentdir.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1135 -0
- package/dist/cli.js.map +1 -0
- package/dist/collab/chatter.d.ts +54 -0
- package/dist/collab/chatter.d.ts.map +1 -0
- package/dist/collab/chatter.js +94 -0
- package/dist/collab/chatter.js.map +1 -0
- package/dist/collab/dag.d.ts +27 -0
- package/dist/collab/dag.d.ts.map +1 -0
- package/dist/collab/dag.js +107 -0
- package/dist/collab/dag.js.map +1 -0
- package/dist/collab/negotiation.d.ts +60 -0
- package/dist/collab/negotiation.d.ts.map +1 -0
- package/dist/collab/negotiation.js +90 -0
- package/dist/collab/negotiation.js.map +1 -0
- package/dist/collab/tree.d.ts +39 -0
- package/dist/collab/tree.d.ts.map +1 -0
- package/dist/collab/tree.js +177 -0
- package/dist/collab/tree.js.map +1 -0
- package/dist/config.d.ts +123 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +206 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/join.d.ts +50 -0
- package/dist/join.d.ts.map +1 -0
- package/dist/join.js +167 -0
- package/dist/join.js.map +1 -0
- package/dist/jointoken.d.ts +144 -0
- package/dist/jointoken.d.ts.map +1 -0
- package/dist/jointoken.js +472 -0
- package/dist/jointoken.js.map +1 -0
- package/dist/model.d.ts +64 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +158 -0
- package/dist/model.js.map +1 -0
- package/dist/onboard.d.ts +90 -0
- package/dist/onboard.d.ts.map +1 -0
- package/dist/onboard.js +510 -0
- package/dist/onboard.js.map +1 -0
- package/dist/protocol/envelope.d.ts +94 -0
- package/dist/protocol/envelope.d.ts.map +1 -0
- package/dist/protocol/envelope.js +169 -0
- package/dist/protocol/envelope.js.map +1 -0
- package/dist/protocol/events.d.ts +51 -0
- package/dist/protocol/events.d.ts.map +1 -0
- package/dist/protocol/events.js +87 -0
- package/dist/protocol/events.js.map +1 -0
- package/dist/qr.d.ts +15 -0
- package/dist/qr.d.ts.map +1 -0
- package/dist/qr.js +52 -0
- package/dist/qr.js.map +1 -0
- package/dist/quickstart.d.ts +167 -0
- package/dist/quickstart.d.ts.map +1 -0
- package/dist/quickstart.js +482 -0
- package/dist/quickstart.js.map +1 -0
- package/dist/quota.d.ts +41 -0
- package/dist/quota.d.ts.map +1 -0
- package/dist/quota.js +84 -0
- package/dist/quota.js.map +1 -0
- package/dist/runtime/agent.d.ts +100 -0
- package/dist/runtime/agent.d.ts.map +1 -0
- package/dist/runtime/agent.js +260 -0
- package/dist/runtime/agent.js.map +1 -0
- package/dist/runtime/executor.d.ts +42 -0
- package/dist/runtime/executor.d.ts.map +1 -0
- package/dist/runtime/executor.js +87 -0
- package/dist/runtime/executor.js.map +1 -0
- package/dist/runtime/fairness.d.ts +25 -0
- package/dist/runtime/fairness.d.ts.map +1 -0
- package/dist/runtime/fairness.js +79 -0
- package/dist/runtime/fairness.js.map +1 -0
- package/dist/runtime/node.d.ts +91 -0
- package/dist/runtime/node.d.ts.map +1 -0
- package/dist/runtime/node.js +203 -0
- package/dist/runtime/node.js.map +1 -0
- package/dist/runtime/openchamber-http.d.ts +27 -0
- package/dist/runtime/openchamber-http.d.ts.map +1 -0
- package/dist/runtime/openchamber-http.js +118 -0
- package/dist/runtime/openchamber-http.js.map +1 -0
- package/dist/runtime/openchamber.d.ts +38 -0
- package/dist/runtime/openchamber.d.ts.map +1 -0
- package/dist/runtime/openchamber.js +43 -0
- package/dist/runtime/openchamber.js.map +1 -0
- package/dist/runtime/planner.d.ts +23 -0
- package/dist/runtime/planner.d.ts.map +1 -0
- package/dist/runtime/planner.js +17 -0
- package/dist/runtime/planner.js.map +1 -0
- package/dist/runtime/worker.d.ts +96 -0
- package/dist/runtime/worker.d.ts.map +1 -0
- package/dist/runtime/worker.js +296 -0
- package/dist/runtime/worker.js.map +1 -0
- package/dist/security.d.ts +462 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +1197 -0
- package/dist/security.js.map +1 -0
- package/dist/store/daemon-store.d.ts +33 -0
- package/dist/store/daemon-store.d.ts.map +1 -0
- package/dist/store/daemon-store.js +115 -0
- package/dist/store/daemon-store.js.map +1 -0
- package/dist/store/file-store.d.ts +22 -0
- package/dist/store/file-store.d.ts.map +1 -0
- package/dist/store/file-store.js +63 -0
- package/dist/store/file-store.js.map +1 -0
- package/dist/store/mebular-store.d.ts +22 -0
- package/dist/store/mebular-store.d.ts.map +1 -0
- package/dist/store/mebular-store.js +53 -0
- package/dist/store/mebular-store.js.map +1 -0
- package/dist/store/message-store.d.ts +26 -0
- package/dist/store/message-store.d.ts.map +1 -0
- package/dist/store/message-store.js +51 -0
- package/dist/store/message-store.js.map +1 -0
- package/dist/surface.d.ts +43 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +515 -0
- package/dist/surface.js.map +1 -0
- package/dist/transport/null.d.ts +7 -0
- package/dist/transport/null.d.ts.map +1 -0
- package/dist/transport/null.js +16 -0
- package/dist/transport/null.js.map +1 -0
- package/dist/transport/spool.d.ts +12 -0
- package/dist/transport/spool.d.ts.map +1 -0
- package/dist/transport/spool.js +54 -0
- package/dist/transport/spool.js.map +1 -0
- package/dist/transport/types.d.ts +21 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +6 -0
- package/dist/transport/types.js.map +1 -0
- package/dist/version.json +6 -0
- package/package.json +37 -4
- package/protocol/collab.example.json +23 -0
- package/protocol/daemon-home.example.json +66 -0
- package/protocol/envelope.example.json +12 -0
- package/protocol/state-machine.json +14 -0
- package/protocol/task-tree.example.json +72 -0
- package/src/agentdir.ts +138 -0
- package/src/cli.ts +1192 -0
- package/src/collab/chatter.ts +116 -0
- package/src/collab/dag.ts +124 -0
- package/src/collab/negotiation.ts +130 -0
- package/src/collab/tree.ts +184 -0
- package/src/config.ts +286 -0
- package/src/index.ts +39 -0
- package/src/join.ts +227 -0
- package/src/jointoken.ts +609 -0
- package/src/model.ts +203 -0
- package/src/onboard.ts +601 -0
- package/src/protocol/envelope.ts +228 -0
- package/src/protocol/events.ts +135 -0
- package/src/qr.ts +68 -0
- package/src/quickstart.ts +632 -0
- package/src/quota.ts +112 -0
- package/src/runtime/agent.ts +313 -0
- package/src/runtime/executor.ts +114 -0
- package/src/runtime/fairness.ts +90 -0
- package/src/runtime/node.ts +250 -0
- package/src/runtime/openchamber-http.ts +137 -0
- package/src/runtime/openchamber.ts +66 -0
- package/src/runtime/planner.ts +36 -0
- package/src/runtime/worker.ts +354 -0
- package/src/security.ts +1660 -0
- package/src/store/daemon-store.ts +144 -0
- package/src/store/file-store.ts +75 -0
- package/src/store/mebular-store.ts +66 -0
- package/src/store/message-store.ts +62 -0
- package/src/surface.ts +535 -0
- package/src/transport/null.ts +20 -0
- package/src/transport/spool.ts +62 -0
- package/src/transport/types.ts +27 -0
- package/README.md +0 -3
package/src/model.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// Fleet 任务模型(M1):由**显式状态事件**确定性推导权威 `TaskState`。
|
|
2
|
+
//
|
|
3
|
+
// 关键性质(见 PROTOCOL-INVARIANTS.md):
|
|
4
|
+
// - **顺序无关收敛**:输入事件数组任意排列、任意重复,结果一致;
|
|
5
|
+
// - **至少一次 + 幂等应用**:按 `eventId` 去重,重复投递不改变状态、不重复执行;
|
|
6
|
+
// - **墙钟无关**:`at` / `expiresAt` 不参与权威判定(只做本机展示)。
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
STATUS_RANK,
|
|
10
|
+
TERMINAL_PRECEDENCE,
|
|
11
|
+
isTerminal,
|
|
12
|
+
canTransition,
|
|
13
|
+
type FleetEndpoint,
|
|
14
|
+
type FleetTrace,
|
|
15
|
+
type TaskBudget,
|
|
16
|
+
type TaskDispatch,
|
|
17
|
+
type TaskStatus,
|
|
18
|
+
} from './protocol/envelope.js';
|
|
19
|
+
import type { TaskEvent } from './protocol/events.js';
|
|
20
|
+
|
|
21
|
+
/** 权威任务状态(由事件推导;与到达顺序无关)。 */
|
|
22
|
+
export interface TaskState {
|
|
23
|
+
taskId: string;
|
|
24
|
+
from: FleetEndpoint;
|
|
25
|
+
to: FleetEndpoint;
|
|
26
|
+
intent: string;
|
|
27
|
+
/** 任务输入负载引用(取自 `created` 事件) */
|
|
28
|
+
payloadRef?: string;
|
|
29
|
+
status: TaskStatus;
|
|
30
|
+
/** 已尝试次数(= 去重后 `claimed` 事件数) */
|
|
31
|
+
attempts: number;
|
|
32
|
+
/** **软约定**:仅本机展示;不参与权威判定 */
|
|
33
|
+
expiresAt?: number;
|
|
34
|
+
trace: FleetTrace;
|
|
35
|
+
terminal: boolean;
|
|
36
|
+
/** 决定 `status` 的事件 id(确定性) */
|
|
37
|
+
lastEventId: string;
|
|
38
|
+
/** `done` 时的结果引用 */
|
|
39
|
+
resultRef?: string;
|
|
40
|
+
/** `failed` 时的原因 */
|
|
41
|
+
reason?: string;
|
|
42
|
+
/** **W1**:子树预算(声明值;缺省 root 用默认、子任务按父推导——见 `collab/tree.ts`) */
|
|
43
|
+
budget?: TaskBudget;
|
|
44
|
+
/** **W1**:root 派发策略(仅 root 有意义;子任务上不参与判定) */
|
|
45
|
+
dispatch?: TaskDispatch;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** 状态迁移是否合法(显式迁移表;`from === to` 视为幂等自迁移)。 */
|
|
49
|
+
export function validateTaskTransition(from: TaskStatus, to: TaskStatus): boolean {
|
|
50
|
+
return canTransition(from, to);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 非法迁移则抛错(本地发起侧用;负例测试)。 */
|
|
54
|
+
export function assertTaskTransition(from: TaskStatus, to: TaskStatus): void {
|
|
55
|
+
if (!validateTaskTransition(from, to)) {
|
|
56
|
+
throw new Error(`非法任务状态迁移:${from} → ${to}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 事件“更权威”判定(确定性全序):状态秩 ↑,再终态平局裁决 ↑,再 `eventId` 字典序 ↑。 */
|
|
61
|
+
function isStronger(a: TaskEvent, b: TaskEvent): boolean {
|
|
62
|
+
const rankA = STATUS_RANK[a.toStatus];
|
|
63
|
+
const rankB = STATUS_RANK[b.toStatus];
|
|
64
|
+
if (rankA !== rankB) return rankA > rankB;
|
|
65
|
+
const precA = TERMINAL_PRECEDENCE[a.toStatus];
|
|
66
|
+
const precB = TERMINAL_PRECEDENCE[b.toStatus];
|
|
67
|
+
if (precA !== precB) return precA > precB;
|
|
68
|
+
return a.eventId > b.eventId;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** 确定性序列化:对象键递归排序,保证结果与对象构造/解析顺序无关。 */
|
|
72
|
+
function stableStringify(value: unknown): string {
|
|
73
|
+
if (value === undefined) return 'null';
|
|
74
|
+
if (value === null || typeof value !== 'object') return JSON.stringify(value);
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
return `[${value.map((item) => stableStringify(item)).join(',')}]`;
|
|
77
|
+
}
|
|
78
|
+
const record = value as Record<string, unknown>;
|
|
79
|
+
const keys = Object.keys(record).sort();
|
|
80
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(',')}}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 去重(按 `eventId`)。同一 `eventId` 对应**不同内容**时(违约/对抗输入),
|
|
85
|
+
* 按稳定序列化取字典序较大者做**确定性裁决**——使「去重后的集合」本身与输入
|
|
86
|
+
* 顺序无关,堵住同一 id 不同内容造成的顺序相关分叉(§2.1 顺序无关收敛)。
|
|
87
|
+
* 契约上 `eventId` 仍应内容寻址或全局唯一;本裁决只是兜底,不改变正常路径。
|
|
88
|
+
*/
|
|
89
|
+
export function dedupeEvents(events: readonly TaskEvent[]): TaskEvent[] {
|
|
90
|
+
const byId = new Map<string, TaskEvent>();
|
|
91
|
+
for (const event of events) {
|
|
92
|
+
const existing = byId.get(event.eventId);
|
|
93
|
+
if (existing === undefined) {
|
|
94
|
+
byId.set(event.eventId, event);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (stableStringify(event) > stableStringify(existing)) {
|
|
98
|
+
byId.set(event.eventId, event);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return [...byId.values()];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 由事件集合推导权威状态(**顺序无关、幂等**)。无 `created` 事件 → 返回 `null`(任务未知)。
|
|
106
|
+
* 不读取 `at` / `now`(墙钟无关)。
|
|
107
|
+
*/
|
|
108
|
+
export function reduceTaskEvents(events: readonly TaskEvent[]): TaskState | null {
|
|
109
|
+
const unique = dedupeEvents(events);
|
|
110
|
+
if (unique.length === 0) return null;
|
|
111
|
+
|
|
112
|
+
let created: TaskEvent | null = null;
|
|
113
|
+
for (const event of unique) {
|
|
114
|
+
if (event.type !== 'created') continue;
|
|
115
|
+
if (created === null || event.eventId < created.eventId) created = event;
|
|
116
|
+
}
|
|
117
|
+
if (created === null) return null;
|
|
118
|
+
|
|
119
|
+
let winner = unique[0]!;
|
|
120
|
+
for (const event of unique) if (isStronger(event, winner)) winner = event;
|
|
121
|
+
|
|
122
|
+
const attempts = unique.reduce((n, e) => (e.type === 'claimed' ? n + 1 : n), 0);
|
|
123
|
+
|
|
124
|
+
const chainSet = new Set<string>();
|
|
125
|
+
for (const event of unique) for (const id of event.trace.chain) chainSet.add(id);
|
|
126
|
+
if (created.trace.causedBy !== undefined) chainSet.add(created.trace.causedBy);
|
|
127
|
+
const chain = [...chainSet].sort();
|
|
128
|
+
|
|
129
|
+
const status = winner.toStatus;
|
|
130
|
+
const state: TaskState = {
|
|
131
|
+
taskId: created.taskId,
|
|
132
|
+
from: created.actor,
|
|
133
|
+
to: created.to ?? created.actor,
|
|
134
|
+
intent: created.intent ?? '',
|
|
135
|
+
status,
|
|
136
|
+
attempts,
|
|
137
|
+
trace: {
|
|
138
|
+
...(created.trace.causedBy !== undefined ? { causedBy: created.trace.causedBy } : {}),
|
|
139
|
+
chain,
|
|
140
|
+
},
|
|
141
|
+
terminal: isTerminal(status),
|
|
142
|
+
lastEventId: winner.eventId,
|
|
143
|
+
};
|
|
144
|
+
if (created.payloadRef !== undefined) state.payloadRef = created.payloadRef;
|
|
145
|
+
if (created.expiresAt !== undefined) state.expiresAt = created.expiresAt;
|
|
146
|
+
if (winner.type === 'done' && winner.payloadRef !== undefined) state.resultRef = winner.payloadRef;
|
|
147
|
+
if (winner.type === 'failed' && winner.reason !== undefined) state.reason = winner.reason;
|
|
148
|
+
if (created.budget !== undefined) state.budget = created.budget;
|
|
149
|
+
if (created.dispatch !== undefined) state.dispatch = created.dispatch;
|
|
150
|
+
return state;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** 幂等应用器:按 `eventId` 去重;重复投递不改变状态、不重复执行。 */
|
|
154
|
+
export interface ApplyOutcome {
|
|
155
|
+
/** 是否为首次应用(false = 重复投递,被幂等忽略) */
|
|
156
|
+
applied: boolean;
|
|
157
|
+
state: TaskState | null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export class IdempotentTaskApplier {
|
|
161
|
+
private readonly byTask = new Map<string, Map<string, TaskEvent>>();
|
|
162
|
+
|
|
163
|
+
/** 应用一个事件(首次返回 applied=true;重复返回 applied=false)。 */
|
|
164
|
+
apply(event: TaskEvent): ApplyOutcome {
|
|
165
|
+
let map = this.byTask.get(event.taskId);
|
|
166
|
+
if (map === undefined) {
|
|
167
|
+
map = new Map();
|
|
168
|
+
this.byTask.set(event.taskId, map);
|
|
169
|
+
}
|
|
170
|
+
if (map.has(event.eventId)) {
|
|
171
|
+
return { applied: false, state: reduceTaskEvents([...map.values()]) };
|
|
172
|
+
}
|
|
173
|
+
map.set(event.eventId, event);
|
|
174
|
+
return { applied: true, state: reduceTaskEvents([...map.values()]) };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
hasApplied(taskId: string, eventId: string): boolean {
|
|
178
|
+
return this.byTask.get(taskId)?.has(eventId) ?? false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** 是否应执行该事件对应的工作(非 `created` 且未应用过)——保证「重复投递不重复执行」。 */
|
|
182
|
+
shouldExecute(event: TaskEvent): boolean {
|
|
183
|
+
return event.type !== 'created' && !this.hasApplied(event.taskId, event.eventId);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
eventsOf(taskId: string): TaskEvent[] {
|
|
187
|
+
return [...(this.byTask.get(taskId)?.values() ?? [])];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
state(taskId: string): TaskState | null {
|
|
191
|
+
const events = this.byTask.get(taskId);
|
|
192
|
+
return events === undefined ? null : reduceTaskEvents([...events.values()]);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
taskIds(): string[] {
|
|
196
|
+
return [...this.byTask.keys()].sort();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** 本机展示用:是否已过期(**advisory**,不参与权威判定)。 */
|
|
201
|
+
export function isExpiredLocally(expiresAt: number | undefined, now: number): boolean {
|
|
202
|
+
return expiresAt !== undefined && now >= expiresAt;
|
|
203
|
+
}
|