@nextclaw/x-harness 0.1.1 → 0.2.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/LICENSE +231 -6
- package/README.md +15 -1
- package/SOURCE.md +9 -0
- package/cli/main.js +232 -200
- package/dist/api.d.ts +56 -2
- package/dist/api.js +130 -79
- package/dist/boundary.js +1 -0
- package/dist/browser-common.d.ts +11 -0
- package/dist/browser-common.js +34 -18
- package/dist/browser-dom-capabilities.d.ts +7 -2
- package/dist/browser-dom-capabilities.js +123 -102
- package/dist/browser-dom-dependencies.js +1 -0
- package/dist/browser-dom-editing.d.ts +20 -2
- package/dist/browser-dom-editing.js +148 -116
- package/dist/browser-dom-navigation.d.ts +11 -2
- package/dist/browser-dom-navigation.js +66 -50
- package/dist/browser-dom-submission.d.ts +6 -1
- package/dist/browser-dom-submission.js +37 -18
- package/dist/browser-dom.d.ts +13 -1
- package/dist/browser-dom.js +113 -66
- package/dist/browser-native.d.ts +14 -1
- package/dist/browser-native.js +161 -128
- package/dist/creator-native.d.ts +3 -0
- package/dist/creator-native.js +113 -0
- package/dist/diagnostics.d.ts +11 -0
- package/dist/diagnostics.js +10 -0
- package/dist/growth-batch.d.ts +6 -3
- package/dist/growth-batch.js +157 -135
- package/dist/growth-dependencies.js +1 -0
- package/dist/growth-execution.d.ts +8 -2
- package/dist/growth-execution.js +68 -37
- package/dist/growth-parallel.d.ts +6 -2
- package/dist/growth-parallel.js +78 -47
- package/dist/growth-plan.d.ts +8 -3
- package/dist/growth-plan.js +45 -23
- package/dist/growth-policy.d.ts +11 -2
- package/dist/growth-policy.js +80 -53
- package/dist/growth-publishing.d.ts +7 -2
- package/dist/growth-publishing.js +72 -46
- package/dist/growth-reading.d.ts +6 -2
- package/dist/growth-reading.js +47 -25
- package/dist/growth-recovery.d.ts +7 -4
- package/dist/growth-recovery.js +177 -148
- package/dist/growth-state.d.ts +10 -2
- package/dist/growth-state.js +67 -30
- package/dist/growth-tasks.d.ts +19 -16
- package/dist/growth-tasks.js +153 -90
- package/dist/growth.d.ts +25 -1
- package/dist/growth.js +207 -124
- package/dist/platform-capabilities.d.ts +6 -2
- package/dist/platform-capabilities.js +18 -8
- package/dist/platform-collections.d.ts +10 -3
- package/dist/platform-collections.js +62 -35
- package/dist/platform-dependencies.js +1 -0
- package/dist/platform-diagnostics.d.ts +7 -2
- package/dist/platform-diagnostics.js +31 -19
- package/dist/platform-observation.d.ts +10 -2
- package/dist/platform-observation.js +44 -23
- package/dist/platform-targets.d.ts +10 -3
- package/dist/platform-targets.js +39 -14
- package/dist/platform-writing.d.ts +10 -2
- package/dist/platform-writing.js +63 -32
- package/dist/platform.d.ts +22 -1
- package/dist/platform.js +120 -52
- package/dist/readers-ax-host.d.ts +11 -1
- package/dist/readers-ax-host.js +108 -99
- package/dist/readers-ax-program.js +5 -0
- package/dist/readers-ax-tree.js +1 -0
- package/dist/readers-cli.js +79 -0
- package/dist/readers.js +9 -0
- package/dist/round-state.js +18 -1
- package/dist/store.d.ts +22 -4
- package/dist/store.js +173 -151
- package/dist/types.js +1 -0
- package/generated/observation.mjs +1 -1
- package/package.json +23 -9
- package/skill/SKILL.md +2 -2
- package/skill/references/reads.md +1 -1
- package/skill/references/setup.md +1 -1
package/dist/growth-parallel.js
CHANGED
|
@@ -1,54 +1,83 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { same } from "./growth-constants.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export class ParallelRound {
|
|
4
|
+
#load;
|
|
5
|
+
#scope;
|
|
6
|
+
#result;
|
|
7
|
+
#store;
|
|
8
|
+
#executor;
|
|
9
|
+
#runtimeId;
|
|
10
|
+
#stamp;
|
|
11
|
+
#evidenceExists;
|
|
12
|
+
#execute;
|
|
13
|
+
#clock;
|
|
14
|
+
#releaseSession;
|
|
15
|
+
#session;
|
|
16
|
+
parallelRound;
|
|
17
|
+
constructor(dependencies) {
|
|
18
|
+
const { load, scope, result, store, executor, runtimeId, stamp, evidenceExists, execute, clock, releaseSession, session, } = dependencies;
|
|
19
|
+
this.#load = load;
|
|
20
|
+
this.#scope = scope;
|
|
21
|
+
this.#result = result;
|
|
22
|
+
this.#store = store;
|
|
23
|
+
this.#executor = executor;
|
|
24
|
+
this.#runtimeId = runtimeId;
|
|
25
|
+
this.#stamp = stamp;
|
|
26
|
+
this.#evidenceExists = evidenceExists;
|
|
27
|
+
this.#execute = execute;
|
|
28
|
+
this.#clock = clock;
|
|
29
|
+
this.#releaseSession = releaseSession;
|
|
30
|
+
this.#session = session;
|
|
31
|
+
this.parallelRound = this.handleParallelRound.bind(this);
|
|
32
|
+
}
|
|
33
|
+
async handleParallelRound({ taskId, decision, selector, requestedKind, maxActions = 10, } = {}) {
|
|
34
|
+
const executor = this.#executor;
|
|
6
35
|
if (!executor)
|
|
7
36
|
throw new Error("parallel-executor-required");
|
|
8
|
-
if (session.running)
|
|
37
|
+
if (this.#session.running)
|
|
9
38
|
return {
|
|
10
39
|
status: "needsDecision",
|
|
11
40
|
mayEnd: false,
|
|
12
41
|
taskId,
|
|
13
42
|
decision: [{ reason: "same-tab-operation-running" }],
|
|
14
43
|
};
|
|
15
|
-
session.running = true;
|
|
44
|
+
this.#session.running = true;
|
|
16
45
|
let completed = 0;
|
|
17
46
|
const needs = [];
|
|
18
47
|
try {
|
|
19
48
|
if (!Number.isInteger(maxActions) || maxActions < 1 || maxActions > 100)
|
|
20
49
|
throw new Error("maxActions须为1–100");
|
|
21
|
-
let snapshot = await load(taskId);
|
|
22
|
-
const activeKinds = scope(snapshot.state);
|
|
50
|
+
let snapshot = await this.#load(taskId);
|
|
51
|
+
const activeKinds = this.#scope(snapshot.state);
|
|
23
52
|
if (requestedKind && !activeKinds.includes(requestedKind))
|
|
24
53
|
return {
|
|
25
|
-
...result(snapshot),
|
|
54
|
+
...this.#result(snapshot),
|
|
26
55
|
status: "needsDecision",
|
|
27
56
|
decision: [{ reason: "kind-outside-round-scope" }],
|
|
28
57
|
};
|
|
29
|
-
snapshot = await store.transaction((latest) => {
|
|
58
|
+
snapshot = await this.#store.transaction((latest) => {
|
|
30
59
|
if (latest.state.batchId !== taskId)
|
|
31
60
|
throw new Error("original-task-changed");
|
|
32
61
|
const next = structuredClone(latest.state);
|
|
33
62
|
next.executions ??= {};
|
|
34
63
|
if (Object.values(next.executions).some((e) => (e.tabId === executor.tabId || e.id === executor.id) &&
|
|
35
|
-
e.runtimeId !== runtimeId &&
|
|
64
|
+
e.runtimeId !== this.#runtimeId &&
|
|
36
65
|
(e.activity || e.actionId) &&
|
|
37
|
-
store.ownerAlive(e.pid)))
|
|
66
|
+
this.#store.ownerAlive(e.pid)))
|
|
38
67
|
throw new Error("same-tab-or-executor-claimed");
|
|
39
68
|
next.executions[executor.id] = {
|
|
40
69
|
...next.executions[executor.id],
|
|
41
70
|
...executor,
|
|
42
|
-
runtimeId,
|
|
43
|
-
pid: store.processId,
|
|
71
|
+
runtimeId: this.#runtimeId,
|
|
72
|
+
pid: this.#store.processId,
|
|
44
73
|
activity: "round",
|
|
45
74
|
status: "RUNNING",
|
|
46
|
-
updatedAt: stamp(),
|
|
75
|
+
updatedAt: this.#stamp(),
|
|
47
76
|
};
|
|
48
77
|
if (decision?.type === "prepareActions") {
|
|
49
78
|
if (!Array.isArray(decision.actions) ||
|
|
50
79
|
!same(next.accountHandle, decision.accountHandle) ||
|
|
51
|
-
!evidenceExists(decision.ownershipEvidenceRef))
|
|
80
|
+
!this.#evidenceExists(decision.ownershipEvidenceRef))
|
|
52
81
|
throw new Error("需原账号核验和完整动作队列");
|
|
53
82
|
next.actions ??= [];
|
|
54
83
|
for (const action of decision.actions) {
|
|
@@ -72,7 +101,7 @@ export function createParallelRound(dependencies) {
|
|
|
72
101
|
}
|
|
73
102
|
if (decision.attemptsCoverage) {
|
|
74
103
|
if (!decision.attemptsCoverage.complete ||
|
|
75
|
-
!evidenceExists(decision.attemptsCoverage.evidenceRef))
|
|
104
|
+
!this.#evidenceExists(decision.attemptsCoverage.evidenceRef))
|
|
76
105
|
throw new Error("共享历史缺证据");
|
|
77
106
|
next.attemptsCoverage = structuredClone(decision.attemptsCoverage);
|
|
78
107
|
}
|
|
@@ -87,7 +116,7 @@ export function createParallelRound(dependencies) {
|
|
|
87
116
|
action.status !== "prepared" ||
|
|
88
117
|
action.claim ||
|
|
89
118
|
typeof decision.approved !== "boolean" ||
|
|
90
|
-
!evidenceExists(decision.evidenceRef))
|
|
119
|
+
!this.#evidenceExists(decision.evidenceRef))
|
|
91
120
|
throw new Error("审查只绑定职责内未领取的原prepared动作");
|
|
92
121
|
if (!decision.approved)
|
|
93
122
|
action.status = "skipped";
|
|
@@ -109,7 +138,7 @@ export function createParallelRound(dependencies) {
|
|
|
109
138
|
}),
|
|
110
139
|
};
|
|
111
140
|
}
|
|
112
|
-
next.updatedAt = stamp();
|
|
141
|
+
next.updatedAt = this.#stamp();
|
|
113
142
|
return next;
|
|
114
143
|
});
|
|
115
144
|
for (const kind of activeKinds) {
|
|
@@ -132,7 +161,7 @@ export function createParallelRound(dependencies) {
|
|
|
132
161
|
if (["verified", "preexisting", "skipped"].includes(original.status))
|
|
133
162
|
continue;
|
|
134
163
|
try {
|
|
135
|
-
snapshot = await store.transaction((latest) => {
|
|
164
|
+
snapshot = await this.#store.transaction((latest) => {
|
|
136
165
|
const next = structuredClone(latest.state), action = next.actions.find((a) => a.id === original.id);
|
|
137
166
|
if (next.batchId !== taskId)
|
|
138
167
|
throw new Error("original-task-changed");
|
|
@@ -150,29 +179,29 @@ export function createParallelRound(dependencies) {
|
|
|
150
179
|
(a.claim ||
|
|
151
180
|
["intent", "unknown", "verified"].includes(a.status))))
|
|
152
181
|
throw new Error("same-target-already-claimed-or-submitted");
|
|
153
|
-
if (action.claim && store.ownerAlive(action.claim.pid))
|
|
182
|
+
if (action.claim && this.#store.ownerAlive(action.claim.pid))
|
|
154
183
|
throw new Error("same-action-claimed");
|
|
155
184
|
if (Object.values(next.executions).some((e) => e.tabId === executor.tabId &&
|
|
156
185
|
e.actionId &&
|
|
157
|
-
e.runtimeId !== runtimeId &&
|
|
158
|
-
store.ownerAlive(e.pid)))
|
|
186
|
+
e.runtimeId !== this.#runtimeId &&
|
|
187
|
+
this.#store.ownerAlive(e.pid)))
|
|
159
188
|
throw new Error("same-tab-claimed");
|
|
160
|
-
session.claim = {
|
|
189
|
+
this.#session.claim = {
|
|
161
190
|
actionId: action.id,
|
|
162
191
|
token: randomUUID(),
|
|
163
192
|
executorId: executor.id,
|
|
164
|
-
runtimeId,
|
|
165
|
-
pid: store.processId,
|
|
166
|
-
acquiredAt: stamp(),
|
|
193
|
+
runtimeId: this.#runtimeId,
|
|
194
|
+
pid: this.#store.processId,
|
|
195
|
+
acquiredAt: this.#stamp(),
|
|
167
196
|
};
|
|
168
|
-
action.claim = session.claim;
|
|
197
|
+
action.claim = this.#session.claim;
|
|
169
198
|
next.executions[executor.id].actionId = action.id;
|
|
170
|
-
next.updatedAt = stamp();
|
|
199
|
+
next.updatedAt = this.#stamp();
|
|
171
200
|
return next;
|
|
172
201
|
});
|
|
173
|
-
if (!session.claim)
|
|
202
|
+
if (!this.#session.claim)
|
|
174
203
|
continue;
|
|
175
|
-
const outcome = await execute(snapshot, snapshot.state.actions.find((a) => a.id === original.id));
|
|
204
|
+
const outcome = await this.#execute(snapshot, snapshot.state.actions.find((a) => a.id === original.id));
|
|
176
205
|
snapshot = outcome.snapshot;
|
|
177
206
|
completed++;
|
|
178
207
|
if (outcome.need) {
|
|
@@ -195,24 +224,24 @@ export function createParallelRound(dependencies) {
|
|
|
195
224
|
break;
|
|
196
225
|
}
|
|
197
226
|
finally {
|
|
198
|
-
if (session.claim) {
|
|
199
|
-
const owned = session.claim;
|
|
200
|
-
session.claim = null;
|
|
201
|
-
snapshot = await store.transaction((latest) => {
|
|
227
|
+
if (this.#session.claim) {
|
|
228
|
+
const owned = this.#session.claim;
|
|
229
|
+
this.#session.claim = null;
|
|
230
|
+
snapshot = await this.#store.transaction((latest) => {
|
|
202
231
|
const next = structuredClone(latest.state), action = next.actions?.find((a) => a.id === owned.actionId);
|
|
203
232
|
if (action?.claim?.token !== owned.token)
|
|
204
233
|
return null;
|
|
205
234
|
delete action.claim;
|
|
206
|
-
if (next.executions[executor.id]?.runtimeId === runtimeId)
|
|
235
|
+
if (next.executions[executor.id]?.runtimeId === this.#runtimeId)
|
|
207
236
|
delete next.executions[executor.id].actionId;
|
|
208
|
-
next.updatedAt = stamp();
|
|
237
|
+
next.updatedAt = this.#stamp();
|
|
209
238
|
return next;
|
|
210
239
|
});
|
|
211
240
|
}
|
|
212
241
|
}
|
|
213
242
|
}
|
|
214
243
|
}
|
|
215
|
-
snapshot = await store.transaction(async (latest) => {
|
|
244
|
+
snapshot = await this.#store.transaction(async (latest) => {
|
|
216
245
|
const next = structuredClone(latest.state), own = next.executions[executor.id];
|
|
217
246
|
const pending = next.items.filter((i) => activeKinds.includes(i.kind) &&
|
|
218
247
|
(i.verified < i.planned || i.unknown));
|
|
@@ -227,11 +256,11 @@ export function createParallelRound(dependencies) {
|
|
|
227
256
|
pending.every((i) => !i.unknown &&
|
|
228
257
|
next.actions.filter((a) => a.kind === i.kind && a.status === "prepared").length >=
|
|
229
258
|
i.planned - i.verified) &&
|
|
230
|
-
waits.every((w) => w && Date.parse(w.resumeAt) > clock()) &&
|
|
231
|
-
Date.parse(scheduled) > clock()) {
|
|
259
|
+
waits.every((w) => w && Date.parse(w.resumeAt) > this.#clock()) &&
|
|
260
|
+
Date.parse(scheduled) > this.#clock()) {
|
|
232
261
|
for (let i = 0; i < pending.length; i++) {
|
|
233
|
-
const proof = await store.recordProof({
|
|
234
|
-
observedAt: stamp(),
|
|
262
|
+
const proof = await this.#store.recordProof({
|
|
263
|
+
observedAt: this.#stamp(),
|
|
235
264
|
...waits[i],
|
|
236
265
|
attempts: next.attempts,
|
|
237
266
|
});
|
|
@@ -253,10 +282,10 @@ export function createParallelRound(dependencies) {
|
|
|
253
282
|
next.status = next.items.every((i) => i.verified === i.planned && !i.unknown)
|
|
254
283
|
? "COMPLETE"
|
|
255
284
|
: "RUNNING";
|
|
256
|
-
own.updatedAt = next.updatedAt = stamp();
|
|
285
|
+
own.updatedAt = next.updatedAt = this.#stamp();
|
|
257
286
|
return next;
|
|
258
287
|
});
|
|
259
|
-
const out = result(snapshot, needs);
|
|
288
|
+
const out = this.#result(snapshot, needs);
|
|
260
289
|
if (!out.mayEnd && !needs.length && completed >= maxActions)
|
|
261
290
|
return {
|
|
262
291
|
...out,
|
|
@@ -279,7 +308,7 @@ export function createParallelRound(dependencies) {
|
|
|
279
308
|
status: "needsDecision",
|
|
280
309
|
mayEnd: false,
|
|
281
310
|
taskId,
|
|
282
|
-
evidenceRef: store.path,
|
|
311
|
+
evidenceRef: this.#store.path,
|
|
283
312
|
decision: [
|
|
284
313
|
{
|
|
285
314
|
reason: "harness-boundary-error",
|
|
@@ -290,12 +319,14 @@ export function createParallelRound(dependencies) {
|
|
|
290
319
|
}
|
|
291
320
|
finally {
|
|
292
321
|
try {
|
|
293
|
-
await releaseSession(taskId);
|
|
322
|
+
await this.#releaseSession(taskId);
|
|
294
323
|
}
|
|
295
324
|
finally {
|
|
296
|
-
session.running = false;
|
|
325
|
+
this.#session.running = false;
|
|
297
326
|
}
|
|
298
327
|
}
|
|
299
328
|
}
|
|
300
|
-
|
|
329
|
+
}
|
|
330
|
+
export function createParallelRound(...args) {
|
|
331
|
+
return new ParallelRound(...args);
|
|
301
332
|
}
|
package/dist/growth-plan.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { GrowthDependencies } from "./growth-dependencies.js";
|
|
2
|
-
export declare
|
|
3
|
-
|
|
4
|
-
}
|
|
2
|
+
export declare class PlannedRound {
|
|
3
|
+
#private;
|
|
4
|
+
round: ({ taskId, decision, selector, requestedKind }?: any) => Promise<any>;
|
|
5
|
+
constructor(dependencies: Pick<GrowthDependencies, "store" | "load" | "result" | "evidenceExists" | "save" | "execute" | "clock" | "stamp">);
|
|
6
|
+
private applyRoundDecision;
|
|
7
|
+
private handleRound;
|
|
8
|
+
}
|
|
9
|
+
export declare function createPlannedRound(...args: ConstructorParameters<typeof PlannedRound>): PlannedRound;
|
package/dist/growth-plan.js
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { kinds, same } from "./growth-constants.js";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
export class PlannedRound {
|
|
3
|
+
#store;
|
|
4
|
+
#load;
|
|
5
|
+
#result;
|
|
6
|
+
#evidenceExists;
|
|
7
|
+
#save;
|
|
8
|
+
#execute;
|
|
9
|
+
#clock;
|
|
10
|
+
#stamp;
|
|
11
|
+
round;
|
|
12
|
+
constructor(dependencies) {
|
|
13
|
+
const { store, load, result, evidenceExists, save, execute, clock, stamp } = dependencies;
|
|
14
|
+
this.#store = store;
|
|
15
|
+
this.#load = load;
|
|
16
|
+
this.#result = result;
|
|
17
|
+
this.#evidenceExists = evidenceExists;
|
|
18
|
+
this.#save = save;
|
|
19
|
+
this.#execute = execute;
|
|
20
|
+
this.#clock = clock;
|
|
21
|
+
this.#stamp = stamp;
|
|
22
|
+
this.round = this.handleRound.bind(this);
|
|
23
|
+
}
|
|
24
|
+
async applyRoundDecision(snapshot, decision, activeKinds) {
|
|
5
25
|
if (decision?.type === "prepareActions") {
|
|
6
26
|
if (!Array.isArray(decision.actions) ||
|
|
7
27
|
!/^[A-Za-z0-9_]{1,15}$/.test(decision.accountHandle ?? "") ||
|
|
8
|
-
!evidenceExists(decision.ownershipEvidenceRef) ||
|
|
28
|
+
!this.#evidenceExists(decision.ownershipEvidenceRef) ||
|
|
9
29
|
(snapshot.state.execution?.owner &&
|
|
10
30
|
snapshot.state.execution.owner !== "harness"))
|
|
11
31
|
throw new Error("需原账号执行权核验和完整动作队列;不接管已有执行者");
|
|
@@ -37,7 +57,7 @@ export function createPlannedRound(dependencies) {
|
|
|
37
57
|
};
|
|
38
58
|
if (decision.attemptsCoverage) {
|
|
39
59
|
if (!decision.attemptsCoverage.complete ||
|
|
40
|
-
!evidenceExists(decision.attemptsCoverage.evidenceRef))
|
|
60
|
+
!this.#evidenceExists(decision.attemptsCoverage.evidenceRef))
|
|
41
61
|
throw new Error("共享历史须完整取证;不能默认为零");
|
|
42
62
|
next.attemptsCoverage = structuredClone(decision.attemptsCoverage);
|
|
43
63
|
}
|
|
@@ -49,7 +69,7 @@ export function createPlannedRound(dependencies) {
|
|
|
49
69
|
...structuredClone(decision.sharedAttempts),
|
|
50
70
|
];
|
|
51
71
|
}
|
|
52
|
-
snapshot = await save(snapshot, next);
|
|
72
|
+
snapshot = await this.#save(snapshot, next);
|
|
53
73
|
}
|
|
54
74
|
else if (decision) {
|
|
55
75
|
const action = snapshot.state.actions?.find((a) => a.id === decision.actionId);
|
|
@@ -58,7 +78,7 @@ export function createPlannedRound(dependencies) {
|
|
|
58
78
|
if (!action ||
|
|
59
79
|
action.status !== "prepared" ||
|
|
60
80
|
typeof decision.approved !== "boolean" ||
|
|
61
|
-
!evidenceExists(decision.evidenceRef))
|
|
81
|
+
!this.#evidenceExists(decision.evidenceRef))
|
|
62
82
|
throw new Error("决策仅能绑定原prepared动作及真实审查证据");
|
|
63
83
|
const next = structuredClone(snapshot.state), target = next.actions.find((a) => a.id === action.id);
|
|
64
84
|
if (!decision.approved)
|
|
@@ -80,25 +100,25 @@ export function createPlannedRound(dependencies) {
|
|
|
80
100
|
: {}),
|
|
81
101
|
}),
|
|
82
102
|
};
|
|
83
|
-
snapshot = await save(snapshot, next);
|
|
103
|
+
snapshot = await this.#save(snapshot, next);
|
|
84
104
|
}
|
|
85
105
|
return snapshot;
|
|
86
106
|
}
|
|
87
|
-
async
|
|
107
|
+
async handleRound({ taskId, decision, selector, requestedKind, } = {}) {
|
|
88
108
|
try {
|
|
89
|
-
return await store.withLock(async () => {
|
|
90
|
-
let snapshot = await load(taskId);
|
|
109
|
+
return await this.#store.withLock(async () => {
|
|
110
|
+
let snapshot = await this.#load(taskId);
|
|
91
111
|
const activeKinds = snapshot.state.scopeKinds ?? kinds;
|
|
92
112
|
if (requestedKind && !activeKinds.includes(requestedKind))
|
|
93
113
|
return {
|
|
94
|
-
...result(snapshot),
|
|
114
|
+
...this.#result(snapshot),
|
|
95
115
|
status: "needsDecision",
|
|
96
116
|
mayEnd: false,
|
|
97
117
|
decision: [
|
|
98
118
|
{ kind: requestedKind, reason: "kind-outside-round-scope" },
|
|
99
119
|
],
|
|
100
120
|
};
|
|
101
|
-
snapshot = await applyRoundDecision(snapshot, decision, activeKinds);
|
|
121
|
+
snapshot = await this.applyRoundDecision(snapshot, decision, activeKinds);
|
|
102
122
|
const needs = [];
|
|
103
123
|
for (const kind of activeKinds) {
|
|
104
124
|
const queue = (snapshot.state.actions ?? [])
|
|
@@ -123,7 +143,7 @@ export function createPlannedRound(dependencies) {
|
|
|
123
143
|
continue;
|
|
124
144
|
if (item.verified >= item.planned && action.status === "prepared")
|
|
125
145
|
continue;
|
|
126
|
-
const outcome = await execute(snapshot, action);
|
|
146
|
+
const outcome = await this.#execute(snapshot, action);
|
|
127
147
|
snapshot = outcome.snapshot;
|
|
128
148
|
if (outcome.need) {
|
|
129
149
|
needs.push(outcome.need);
|
|
@@ -136,7 +156,7 @@ export function createPlannedRound(dependencies) {
|
|
|
136
156
|
.every((i) => i.verified === i.planned && i.unknown === 0)) {
|
|
137
157
|
const next = structuredClone(snapshot.state);
|
|
138
158
|
next.status = "COMPLETE";
|
|
139
|
-
snapshot = await save(snapshot, next);
|
|
159
|
+
snapshot = await this.#save(snapshot, next);
|
|
140
160
|
}
|
|
141
161
|
else {
|
|
142
162
|
const pending = snapshot.state.items.filter((i) => activeKinds.includes(i.kind) &&
|
|
@@ -150,13 +170,13 @@ export function createPlannedRound(dependencies) {
|
|
|
150
170
|
(snapshot.state.actions ?? []).filter((a) => a.kind === item.kind && a.status === "prepared").length >=
|
|
151
171
|
item.planned - item.verified);
|
|
152
172
|
if (covered &&
|
|
153
|
-
waits.every((w) => w && Date.parse(w.resumeAt) > clock()) &&
|
|
154
|
-
Date.parse(scheduled) > clock()) {
|
|
173
|
+
waits.every((w) => w && Date.parse(w.resumeAt) > this.#clock()) &&
|
|
174
|
+
Date.parse(scheduled) > this.#clock()) {
|
|
155
175
|
const next = structuredClone(snapshot.state);
|
|
156
176
|
for (let i = 0; i < pending.length; i++) {
|
|
157
177
|
const item = next.items.find((a) => a.kind === pending[i].kind), wait = waits[i];
|
|
158
|
-
const proof = await store.recordProof({
|
|
159
|
-
observedAt: stamp(),
|
|
178
|
+
const proof = await this.#store.recordProof({
|
|
179
|
+
observedAt: this.#stamp(),
|
|
160
180
|
reason: wait.reason,
|
|
161
181
|
resumeAt: wait.resumeAt,
|
|
162
182
|
attempts: next.attempts,
|
|
@@ -175,7 +195,7 @@ export function createPlannedRound(dependencies) {
|
|
|
175
195
|
nextScheduledCheckAt: scheduled,
|
|
176
196
|
nextEligibleCheckAt: waits.map((w) => w.resumeAt).sort()[0],
|
|
177
197
|
};
|
|
178
|
-
snapshot = await save(snapshot, next);
|
|
198
|
+
snapshot = await this.#save(snapshot, next);
|
|
179
199
|
}
|
|
180
200
|
else if (!needs.length)
|
|
181
201
|
needs.push({
|
|
@@ -183,7 +203,7 @@ export function createPlannedRound(dependencies) {
|
|
|
183
203
|
reason: "prepared-queue-does-not-cover-original-targets",
|
|
184
204
|
});
|
|
185
205
|
}
|
|
186
|
-
return result(snapshot, needs);
|
|
206
|
+
return this.#result(snapshot, needs);
|
|
187
207
|
});
|
|
188
208
|
}
|
|
189
209
|
catch (error) {
|
|
@@ -191,7 +211,7 @@ export function createPlannedRound(dependencies) {
|
|
|
191
211
|
status: "needsDecision",
|
|
192
212
|
mayEnd: false,
|
|
193
213
|
taskId,
|
|
194
|
-
evidenceRef: store.path,
|
|
214
|
+
evidenceRef: this.#store.path,
|
|
195
215
|
decision: [
|
|
196
216
|
{
|
|
197
217
|
reason: error.code === "CONFLICT"
|
|
@@ -203,5 +223,7 @@ export function createPlannedRound(dependencies) {
|
|
|
203
223
|
};
|
|
204
224
|
}
|
|
205
225
|
}
|
|
206
|
-
|
|
226
|
+
}
|
|
227
|
+
export function createPlannedRound(...args) {
|
|
228
|
+
return new PlannedRound(...args);
|
|
207
229
|
}
|
package/dist/growth-policy.d.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { GrowthDependencies } from "./growth-dependencies.js";
|
|
2
|
-
export declare
|
|
2
|
+
export declare class GrowthPolicy {
|
|
3
|
+
#private;
|
|
3
4
|
authorizedWindow: (state: any, kind: string) => boolean;
|
|
4
5
|
scope: (state: any) => any;
|
|
5
6
|
project: (state: any) => any;
|
|
6
7
|
capability: (operation: any, input?: any) => any;
|
|
7
8
|
waitBudget: (state: any) => any;
|
|
8
9
|
guardInput: (state: any, action: any, independentExecution?: any) => any;
|
|
9
|
-
|
|
10
|
+
constructor(dependencies: Pick<GrowthDependencies, "executor" | "clock" | "evidenceExists" | "platform" | "store">);
|
|
11
|
+
private handleCapability;
|
|
12
|
+
private handleWaitBudget;
|
|
13
|
+
private handleGuardInput;
|
|
14
|
+
private handleAuthorizedWindow;
|
|
15
|
+
private handleScope;
|
|
16
|
+
private handleProject;
|
|
17
|
+
}
|
|
18
|
+
export declare function createGrowthPolicy(...args: ConstructorParameters<typeof GrowthPolicy>): GrowthPolicy;
|