@nextclaw/x-harness 0.1.1 → 0.2.1
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/recovery.md +4 -4
- package/skill/references/setup.md +1 -1
package/dist/growth-recovery.js
CHANGED
|
@@ -1,158 +1,187 @@
|
|
|
1
1
|
import { xURL } from "./platform-rules.js";
|
|
2
2
|
import { same } from "./growth-constants.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
3
|
+
export class GrowthRecovery {
|
|
4
|
+
#capability;
|
|
5
|
+
#platform;
|
|
6
|
+
#load;
|
|
7
|
+
#evidenceExists;
|
|
8
|
+
#clock;
|
|
9
|
+
#store;
|
|
10
|
+
#settleProof;
|
|
11
|
+
#executor;
|
|
12
|
+
#parallelRound;
|
|
13
|
+
#round;
|
|
14
|
+
#readTask;
|
|
15
|
+
constructor(dependencies) {
|
|
16
|
+
this.verifyFollow = this.verifyFollow.bind(this);
|
|
17
|
+
this.verifySubmission = this.verifySubmission.bind(this);
|
|
18
|
+
this.verifyPublishedPost = this.verifyPublishedPost.bind(this);
|
|
19
|
+
const { capability, platform, load, evidenceExists, clock, store, settleProof, executor, parallelRound, round, readTask, } = dependencies;
|
|
20
|
+
this.#capability = capability;
|
|
21
|
+
this.#platform = platform;
|
|
22
|
+
this.#load = load;
|
|
23
|
+
this.#evidenceExists = evidenceExists;
|
|
24
|
+
this.#clock = clock;
|
|
25
|
+
this.#store = store;
|
|
26
|
+
this.#settleProof = settleProof;
|
|
27
|
+
this.#executor = executor;
|
|
28
|
+
this.#parallelRound = parallelRound;
|
|
29
|
+
this.#round = round;
|
|
30
|
+
this.#readTask = readTask;
|
|
31
|
+
}
|
|
32
|
+
async verifyPublishedPost({ taskId, draftPath, publicURL, evidenceRef, sourceLink, }) {
|
|
33
|
+
// Read-only recovery of a single submitted original; never opens a composer.
|
|
34
|
+
const caps = this.#capability("read");
|
|
35
|
+
if (!caps.covered || typeof this.#platform.readPost !== "function")
|
|
36
|
+
return {
|
|
37
|
+
status: "needsCapability",
|
|
38
|
+
missing: ["platform.readPost", ...caps.missing],
|
|
39
|
+
};
|
|
40
|
+
const initial = await this.#load(taskId);
|
|
41
|
+
const action = initial.state.actions.find((a) => a.kind === "original" && a.draftPath === draftPath);
|
|
42
|
+
if (!action ||
|
|
43
|
+
!["intent", "unknown"].includes(action.status) ||
|
|
44
|
+
!this.#evidenceExists(evidenceRef))
|
|
45
|
+
throw new Error("submitted-original-and-public-proof-required");
|
|
46
|
+
const url = xURL(publicURL), postId = url.pathname.match(/\/status\/(\d+)$/)?.[1];
|
|
47
|
+
if (!postId ||
|
|
48
|
+
url.pathname.toLowerCase() !==
|
|
49
|
+
`/${action.accountHandle}/status/${postId}`.toLowerCase())
|
|
50
|
+
throw new Error("public-account-mismatch");
|
|
51
|
+
const observation = await this.#platform.readPost({
|
|
52
|
+
postId,
|
|
53
|
+
url: publicURL,
|
|
54
|
+
evidenceRef,
|
|
55
|
+
});
|
|
56
|
+
const trailing = action.text.match(/\n\n(https:\/\/\S+)$/);
|
|
57
|
+
const matches = (observation.posts ?? []).filter((p) => {
|
|
58
|
+
const exact = p.body === action.text;
|
|
59
|
+
const card = trailing &&
|
|
60
|
+
sourceLink &&
|
|
61
|
+
this.#evidenceExists(sourceLink.evidenceRef) &&
|
|
62
|
+
sourceLink.resolvedURL === trailing[1] &&
|
|
63
|
+
p.links?.includes(sourceLink.href) &&
|
|
64
|
+
p.body === action.text.slice(0, -trailing[0].length);
|
|
65
|
+
let renderedSource = false;
|
|
66
|
+
if (trailing &&
|
|
67
|
+
sourceLink?.renderedText &&
|
|
68
|
+
this.#evidenceExists(sourceLink.evidenceRef) &&
|
|
69
|
+
sourceLink.resolvedURL === trailing[1] &&
|
|
70
|
+
p.links?.includes(sourceLink.href)) {
|
|
71
|
+
try {
|
|
72
|
+
renderedSource =
|
|
73
|
+
new URL(sourceLink.renderedText.replace(/\s/g, "")).href ===
|
|
74
|
+
sourceLink.resolvedURL &&
|
|
75
|
+
p.body?.endsWith(sourceLink.renderedText) &&
|
|
76
|
+
p.body.slice(0, -sourceLink.renderedText.length).trimEnd() ===
|
|
77
|
+
action.text.slice(0, -trailing[0].length);
|
|
56
78
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
taskId,
|
|
87
|
-
actionId: action.id,
|
|
88
|
-
mayEnd: false,
|
|
89
|
-
};
|
|
79
|
+
catch {
|
|
80
|
+
/* Invalid rendered URL cannot verify publication. */
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return (p.id === postId &&
|
|
84
|
+
same(p.author, action.accountHandle) &&
|
|
85
|
+
(exact || card || renderedSource) &&
|
|
86
|
+
!p.needsExpansion &&
|
|
87
|
+
!(action.beforeIds ?? []).includes(p.id) &&
|
|
88
|
+
Date.parse(p.datetime) >= Date.parse(action.intentAt) &&
|
|
89
|
+
Date.parse(p.datetime) <= this.#clock() + 5000 &&
|
|
90
|
+
(!action.assets?.length ||
|
|
91
|
+
action.assets.every((a) => p.images?.some((i) => i.alt === a.alt))));
|
|
92
|
+
});
|
|
93
|
+
Object.assign(observation, {
|
|
94
|
+
verified: !observation.needsStateCheck &&
|
|
95
|
+
!observation.alerts?.length &&
|
|
96
|
+
matches.length === 1,
|
|
97
|
+
publicId: matches.length === 1 ? postId : null,
|
|
98
|
+
sourceLink,
|
|
99
|
+
});
|
|
100
|
+
return this.#store.withLock(async () => {
|
|
101
|
+
const latest = await this.#load(taskId), target = latest.state.actions.find((a) => a.id === action.id);
|
|
102
|
+
if (!["intent", "unknown"].includes(target?.status) ||
|
|
103
|
+
target.intentAt !== action.intentAt ||
|
|
104
|
+
target.text !== action.text)
|
|
105
|
+
throw new Error("recovery-action-changed");
|
|
106
|
+
await this.#settleProof(latest, target, observation, {
|
|
107
|
+
preserveStatus: true,
|
|
90
108
|
});
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (!action || !["intent", "unknown"].includes(action.status))
|
|
95
|
-
return {
|
|
96
|
-
status: "needsDecision",
|
|
97
|
-
reason: "verification-requires-original-unknown-or-intent",
|
|
98
|
-
mayEnd: false,
|
|
99
|
-
};
|
|
100
|
-
return (executor ? parallelRound : round)({
|
|
109
|
+
return {
|
|
110
|
+
status: observation.verified ? "verified" : "unknown",
|
|
111
|
+
publicId: observation.publicId,
|
|
101
112
|
taskId,
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
actionId: action.id,
|
|
114
|
+
mayEnd: false,
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async verifySubmission({ taskId, actionId }) {
|
|
119
|
+
const executor = this.#executor;
|
|
120
|
+
const snapshot = await this.#load(taskId), action = snapshot.state.actions?.find((a) => a.id === actionId);
|
|
121
|
+
if (!action || !["intent", "unknown"].includes(action.status))
|
|
122
|
+
return {
|
|
123
|
+
status: "needsDecision",
|
|
124
|
+
reason: "verification-requires-original-unknown-or-intent",
|
|
125
|
+
mayEnd: false,
|
|
126
|
+
};
|
|
127
|
+
return (executor ? this.#parallelRound : this.#round)({
|
|
128
|
+
taskId,
|
|
129
|
+
requestedKind: action.kind,
|
|
130
|
+
selector: (a) => a.id === actionId,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async verifyFollow(target) {
|
|
134
|
+
const mode = target.mode ?? "receipt";
|
|
135
|
+
if (!["receipt", "decision"].includes(mode))
|
|
136
|
+
throw new Error("关注核验模式须为receipt/decision");
|
|
137
|
+
const caps = this.#platform.preflight({ operation: "read" });
|
|
138
|
+
if (!caps.covered)
|
|
139
|
+
return {
|
|
140
|
+
handle: target.handle,
|
|
141
|
+
verified: false,
|
|
142
|
+
status: "needsCapability",
|
|
143
|
+
...caps,
|
|
144
|
+
};
|
|
145
|
+
if (!target.url || !target.evidenceRef) {
|
|
146
|
+
const original = (await this.#store.load()).state.actions?.find((a) => a.kind === "follow" && same(a.handle, target.handle));
|
|
147
|
+
if (!original)
|
|
112
148
|
return {
|
|
113
149
|
handle: target.handle,
|
|
114
150
|
verified: false,
|
|
115
|
-
status: "
|
|
116
|
-
|
|
151
|
+
status: "needsDecision",
|
|
152
|
+
reason: "original-person-observation-required",
|
|
117
153
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
},
|
|
152
|
-
}
|
|
153
|
-
: {}),
|
|
154
|
-
...(mode === "decision" ? { observation: value } : {}),
|
|
155
|
-
};
|
|
156
|
-
},
|
|
157
|
-
};
|
|
154
|
+
target = original;
|
|
155
|
+
}
|
|
156
|
+
const value = await this.#readTask("readPerson", {
|
|
157
|
+
...target,
|
|
158
|
+
mode: "decision",
|
|
159
|
+
});
|
|
160
|
+
return {
|
|
161
|
+
handle: target.handle,
|
|
162
|
+
status: value.status,
|
|
163
|
+
fresh: value.fresh,
|
|
164
|
+
verified: value.status === "observed" &&
|
|
165
|
+
!value.needsStateCheck &&
|
|
166
|
+
!value.alerts?.length &&
|
|
167
|
+
value.relationship === "following" &&
|
|
168
|
+
(!target.accountHandle ||
|
|
169
|
+
same(value.accountHandle, target.accountHandle)),
|
|
170
|
+
...(value.evidenceRef ? { evidenceRef: value.evidenceRef } : {}),
|
|
171
|
+
...(value.missing?.length ? { missing: value.missing } : {}),
|
|
172
|
+
...(value.diagnostic
|
|
173
|
+
? {
|
|
174
|
+
diagnostic: {
|
|
175
|
+
operation: value.diagnostic.operation,
|
|
176
|
+
category: value.diagnostic.category,
|
|
177
|
+
code: value.diagnostic.code,
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
: {}),
|
|
181
|
+
...(mode === "decision" ? { observation: value } : {}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export function createGrowthRecovery(...args) {
|
|
186
|
+
return new GrowthRecovery(...args);
|
|
158
187
|
}
|
package/dist/growth-state.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { GrowthDependencies } from "./growth-dependencies.js";
|
|
2
|
-
export declare
|
|
2
|
+
export declare class GrowthState {
|
|
3
|
+
#private;
|
|
3
4
|
load: (taskId: any) => Promise<any>;
|
|
4
5
|
save: (snapshot: any, state: any) => Promise<any>;
|
|
5
6
|
result: (snapshot: any, needs?: any) => any;
|
|
6
7
|
settleProof: (snapshot: any, action: any, observation: any, { preserveStatus }?: any) => Promise<any>;
|
|
7
8
|
releaseSession: (taskId?: any) => Promise<any>;
|
|
8
|
-
|
|
9
|
+
constructor(dependencies: Pick<GrowthDependencies, "store" | "project" | "clock" | "evidenceExists" | "stamp" | "executor" | "waitBudget" | "platform" | "runtimeId" | "session">);
|
|
10
|
+
private handleLoad;
|
|
11
|
+
private handleSave;
|
|
12
|
+
private handleResult;
|
|
13
|
+
private handleSettleProof;
|
|
14
|
+
private handleReleaseSession;
|
|
15
|
+
}
|
|
16
|
+
export declare function createGrowthState(...args: ConstructorParameters<typeof GrowthState>): GrowthState;
|
package/dist/growth-state.js
CHANGED
|
@@ -1,13 +1,45 @@
|
|
|
1
1
|
import { checkRound } from "./round-state.js";
|
|
2
2
|
import { kinds } from "./growth-constants.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export class GrowthState {
|
|
4
|
+
#store;
|
|
5
|
+
#project;
|
|
6
|
+
#clock;
|
|
7
|
+
#evidenceExists;
|
|
8
|
+
#stamp;
|
|
9
|
+
#executor;
|
|
10
|
+
#waitBudget;
|
|
11
|
+
#platform;
|
|
12
|
+
#runtimeId;
|
|
13
|
+
#session;
|
|
14
|
+
load;
|
|
15
|
+
save;
|
|
16
|
+
result;
|
|
17
|
+
settleProof;
|
|
18
|
+
releaseSession;
|
|
19
|
+
constructor(dependencies) {
|
|
20
|
+
const { store, project, clock, evidenceExists, stamp, executor, waitBudget, platform, runtimeId, session, } = dependencies;
|
|
21
|
+
this.#store = store;
|
|
22
|
+
this.#project = project;
|
|
23
|
+
this.#clock = clock;
|
|
24
|
+
this.#evidenceExists = evidenceExists;
|
|
25
|
+
this.#stamp = stamp;
|
|
26
|
+
this.#executor = executor;
|
|
27
|
+
this.#waitBudget = waitBudget;
|
|
28
|
+
this.#platform = platform;
|
|
29
|
+
this.#runtimeId = runtimeId;
|
|
30
|
+
this.#session = session;
|
|
31
|
+
this.load = this.handleLoad.bind(this);
|
|
32
|
+
this.save = this.handleSave.bind(this);
|
|
33
|
+
this.result = this.handleResult.bind(this);
|
|
34
|
+
this.settleProof = this.handleSettleProof.bind(this);
|
|
35
|
+
this.releaseSession = this.handleReleaseSession.bind(this);
|
|
36
|
+
}
|
|
37
|
+
async handleLoad(taskId) {
|
|
38
|
+
const snapshot = await this.#store.load();
|
|
7
39
|
if (snapshot.state.batchId !== taskId)
|
|
8
40
|
throw new Error("任务ID不是原台账当前批次");
|
|
9
41
|
// Expired WAITING is actionable, not a malformed ledger. Validate structure separately from stopping.
|
|
10
|
-
const gate = checkRound({ ...project(snapshot.state), status: "RUNNING" }, { now: clock(), evidenceExists });
|
|
42
|
+
const gate = checkRound({ ...this.#project(snapshot.state), status: "RUNNING" }, { now: this.#clock(), evidenceExists: this.#evidenceExists });
|
|
11
43
|
if (!gate.valid)
|
|
12
44
|
throw new Error(`原轮次标记无效:${gate.errors.join(";")}`);
|
|
13
45
|
const actions = snapshot.state.actions ?? [];
|
|
@@ -28,18 +60,19 @@ export function createGrowthState(dependencies) {
|
|
|
28
60
|
}
|
|
29
61
|
return snapshot;
|
|
30
62
|
}
|
|
31
|
-
async
|
|
32
|
-
|
|
63
|
+
async handleSave(snapshot, state) {
|
|
64
|
+
const executor = this.#executor;
|
|
65
|
+
state.updatedAt = this.#stamp();
|
|
33
66
|
if (!executor)
|
|
34
|
-
return store.save(snapshot, state);
|
|
67
|
+
return this.#store.save(snapshot, state);
|
|
35
68
|
// Batch metadata still uses a short withLock transaction; it never contains browser I/O.
|
|
36
|
-
if (!session.claim)
|
|
37
|
-
return store.save(snapshot, state);
|
|
38
|
-
return store.transaction((latest) => {
|
|
39
|
-
const next = structuredClone(latest.state), before = snapshot.state.actions.find((a) => a.id === session.claim.actionId);
|
|
40
|
-
const entry = next.actions.find((a) => a.id === session.claim.actionId), after = state.actions.find((a) => a.id === session.claim.actionId);
|
|
69
|
+
if (!this.#session.claim)
|
|
70
|
+
return this.#store.save(snapshot, state);
|
|
71
|
+
return this.#store.transaction((latest) => {
|
|
72
|
+
const next = structuredClone(latest.state), before = snapshot.state.actions.find((a) => a.id === this.#session.claim.actionId);
|
|
73
|
+
const entry = next.actions.find((a) => a.id === this.#session.claim.actionId), after = state.actions.find((a) => a.id === this.#session.claim.actionId);
|
|
41
74
|
if (next.batchId !== snapshot.state.batchId ||
|
|
42
|
-
entry?.claim?.token !== session.claim.token ||
|
|
75
|
+
entry?.claim?.token !== this.#session.claim.token ||
|
|
43
76
|
JSON.stringify({ ...entry, claim: undefined }) !==
|
|
44
77
|
JSON.stringify({ ...before, claim: undefined }))
|
|
45
78
|
throw new Error("action-claim-or-version-conflict");
|
|
@@ -49,7 +82,7 @@ export function createGrowthState(dependencies) {
|
|
|
49
82
|
if (reservation) {
|
|
50
83
|
if (!item.actionable || item.verified + item.unknown >= item.planned)
|
|
51
84
|
throw new Error("shared-target-capacity-exhausted");
|
|
52
|
-
if (entry.kind === "follow" && waitBudget(next))
|
|
85
|
+
if (entry.kind === "follow" && this.#waitBudget(next))
|
|
53
86
|
throw new Error("shared-follow-budget-exhausted");
|
|
54
87
|
}
|
|
55
88
|
if (after.status === "verified" &&
|
|
@@ -91,7 +124,8 @@ export function createGrowthState(dependencies) {
|
|
|
91
124
|
next.execution ??= {};
|
|
92
125
|
const old = next.execution.followGroup;
|
|
93
126
|
next.execution.followGroup = {
|
|
94
|
-
attempts: old?.attempts < 10 &&
|
|
127
|
+
attempts: old?.attempts < 10 &&
|
|
128
|
+
this.#clock() < Date.parse(old.lastAt) + 180000
|
|
95
129
|
? old.attempts + 1
|
|
96
130
|
: 1,
|
|
97
131
|
lastAt: attempt.at,
|
|
@@ -99,14 +133,14 @@ export function createGrowthState(dependencies) {
|
|
|
99
133
|
}
|
|
100
134
|
}
|
|
101
135
|
next.status = "RUNNING";
|
|
102
|
-
next.updatedAt = stamp();
|
|
136
|
+
next.updatedAt = this.#stamp();
|
|
103
137
|
return next;
|
|
104
138
|
});
|
|
105
139
|
}
|
|
106
|
-
|
|
107
|
-
const gate = checkRound(project(snapshot.state), {
|
|
108
|
-
now: clock(),
|
|
109
|
-
evidenceExists,
|
|
140
|
+
handleResult(snapshot, needs = []) {
|
|
141
|
+
const gate = checkRound(this.#project(snapshot.state), {
|
|
142
|
+
now: this.#clock(),
|
|
143
|
+
evidenceExists: this.#evidenceExists,
|
|
110
144
|
});
|
|
111
145
|
return {
|
|
112
146
|
status: gate.mayEnd
|
|
@@ -125,12 +159,12 @@ export function createGrowthState(dependencies) {
|
|
|
125
159
|
])),
|
|
126
160
|
gaps: gate.pending,
|
|
127
161
|
...(needs.length ? { decision: needs } : {}),
|
|
128
|
-
evidenceRef: snapshot.path ?? store.path,
|
|
162
|
+
evidenceRef: snapshot.path ?? this.#store.path,
|
|
129
163
|
...(gate.errors.length ? { settlementErrors: gate.errors } : {}),
|
|
130
164
|
};
|
|
131
165
|
}
|
|
132
|
-
async
|
|
133
|
-
const proof = await store.recordProof(await platform.captureProof({ observation }));
|
|
166
|
+
async handleSettleProof(snapshot, action, observation, { preserveStatus = false } = {}) {
|
|
167
|
+
const proof = await this.#store.recordProof(await this.#platform.captureProof({ observation }));
|
|
134
168
|
const state = structuredClone(snapshot.state);
|
|
135
169
|
const entry = state.actions.find((a) => a.id === action.id);
|
|
136
170
|
const item = state.items.find((i) => i.kind === action.kind);
|
|
@@ -162,22 +196,25 @@ export function createGrowthState(dependencies) {
|
|
|
162
196
|
throw new Error("结算越界:不覆盖原目标或重复计数");
|
|
163
197
|
if (!preserveStatus)
|
|
164
198
|
state.status = "RUNNING";
|
|
165
|
-
return
|
|
199
|
+
return this.handleSave(snapshot, state);
|
|
166
200
|
}
|
|
167
|
-
async
|
|
201
|
+
async handleReleaseSession(taskId = undefined) {
|
|
202
|
+
const executor = this.#executor;
|
|
168
203
|
if (!executor)
|
|
169
204
|
return;
|
|
170
|
-
await store.transaction((latest) => {
|
|
205
|
+
await this.#store.transaction((latest) => {
|
|
171
206
|
const own = latest.state.executions?.[executor.id];
|
|
172
207
|
if ((taskId && latest.state.batchId !== taskId) ||
|
|
173
|
-
own?.runtimeId !== runtimeId ||
|
|
208
|
+
own?.runtimeId !== this.#runtimeId ||
|
|
174
209
|
!own.activity)
|
|
175
210
|
return null;
|
|
176
211
|
const next = structuredClone(latest.state);
|
|
177
212
|
delete next.executions[executor.id].activity;
|
|
178
|
-
next.updatedAt = stamp();
|
|
213
|
+
next.updatedAt = this.#stamp();
|
|
179
214
|
return next;
|
|
180
215
|
});
|
|
181
216
|
}
|
|
182
|
-
|
|
217
|
+
}
|
|
218
|
+
export function createGrowthState(...args) {
|
|
219
|
+
return new GrowthState(...args);
|
|
183
220
|
}
|
package/dist/growth-tasks.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import type { GrowthDependencies } from "./growth-dependencies.js";
|
|
2
|
-
export declare
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
2
|
+
export declare class GrowthTasks {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(dependencies: Pick<GrowthDependencies, "platform" | "capability" | "executor" | "readTask" | "parallelRound" | "round" | "load" | "result" | "publishIndependent" | "prepareActionsForCall">);
|
|
5
|
+
preflight({ operation, input }: any): any;
|
|
6
|
+
readCreator(args: any): Promise<any>;
|
|
7
|
+
inspectAccount(args: any): Promise<any>;
|
|
8
|
+
readPost(args: any): Promise<any>;
|
|
9
|
+
readPerson(args: any): Promise<any>;
|
|
10
|
+
readBookmarks(args: any): Promise<any>;
|
|
11
|
+
searchSources(args: any): Promise<any>;
|
|
12
|
+
readFeedback(args: any): Promise<any>;
|
|
13
|
+
runGrowthRound(args: any): Promise<any>;
|
|
14
|
+
resumeGrowthRound(args: any): Promise<any>;
|
|
15
|
+
inspectRound({ taskId }: any): Promise<any>;
|
|
16
|
+
followOne({ taskId, handle }: any): Promise<any>;
|
|
17
|
+
publishPost(input: any): Promise<any>;
|
|
18
|
+
replyToPost({ taskId, postId, text, preparedAction }: any): Promise<any>;
|
|
19
|
+
}
|
|
20
|
+
export declare function createGrowthTasks(...args: ConstructorParameters<typeof GrowthTasks>): GrowthTasks;
|