@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.
Files changed (80) hide show
  1. package/LICENSE +231 -6
  2. package/README.md +15 -1
  3. package/SOURCE.md +9 -0
  4. package/cli/main.js +232 -200
  5. package/dist/api.d.ts +56 -2
  6. package/dist/api.js +130 -79
  7. package/dist/boundary.js +1 -0
  8. package/dist/browser-common.d.ts +11 -0
  9. package/dist/browser-common.js +34 -18
  10. package/dist/browser-dom-capabilities.d.ts +7 -2
  11. package/dist/browser-dom-capabilities.js +123 -102
  12. package/dist/browser-dom-dependencies.js +1 -0
  13. package/dist/browser-dom-editing.d.ts +20 -2
  14. package/dist/browser-dom-editing.js +148 -116
  15. package/dist/browser-dom-navigation.d.ts +11 -2
  16. package/dist/browser-dom-navigation.js +66 -50
  17. package/dist/browser-dom-submission.d.ts +6 -1
  18. package/dist/browser-dom-submission.js +37 -18
  19. package/dist/browser-dom.d.ts +13 -1
  20. package/dist/browser-dom.js +113 -66
  21. package/dist/browser-native.d.ts +14 -1
  22. package/dist/browser-native.js +161 -128
  23. package/dist/creator-native.d.ts +3 -0
  24. package/dist/creator-native.js +113 -0
  25. package/dist/diagnostics.d.ts +11 -0
  26. package/dist/diagnostics.js +10 -0
  27. package/dist/growth-batch.d.ts +6 -3
  28. package/dist/growth-batch.js +157 -135
  29. package/dist/growth-dependencies.js +1 -0
  30. package/dist/growth-execution.d.ts +8 -2
  31. package/dist/growth-execution.js +68 -37
  32. package/dist/growth-parallel.d.ts +6 -2
  33. package/dist/growth-parallel.js +78 -47
  34. package/dist/growth-plan.d.ts +8 -3
  35. package/dist/growth-plan.js +45 -23
  36. package/dist/growth-policy.d.ts +11 -2
  37. package/dist/growth-policy.js +80 -53
  38. package/dist/growth-publishing.d.ts +7 -2
  39. package/dist/growth-publishing.js +72 -46
  40. package/dist/growth-reading.d.ts +6 -2
  41. package/dist/growth-reading.js +47 -25
  42. package/dist/growth-recovery.d.ts +7 -4
  43. package/dist/growth-recovery.js +177 -148
  44. package/dist/growth-state.d.ts +10 -2
  45. package/dist/growth-state.js +67 -30
  46. package/dist/growth-tasks.d.ts +19 -16
  47. package/dist/growth-tasks.js +153 -90
  48. package/dist/growth.d.ts +25 -1
  49. package/dist/growth.js +207 -124
  50. package/dist/platform-capabilities.d.ts +6 -2
  51. package/dist/platform-capabilities.js +18 -8
  52. package/dist/platform-collections.d.ts +10 -3
  53. package/dist/platform-collections.js +62 -35
  54. package/dist/platform-dependencies.js +1 -0
  55. package/dist/platform-diagnostics.d.ts +7 -2
  56. package/dist/platform-diagnostics.js +31 -19
  57. package/dist/platform-observation.d.ts +10 -2
  58. package/dist/platform-observation.js +44 -23
  59. package/dist/platform-targets.d.ts +10 -3
  60. package/dist/platform-targets.js +39 -14
  61. package/dist/platform-writing.d.ts +10 -2
  62. package/dist/platform-writing.js +63 -32
  63. package/dist/platform.d.ts +22 -1
  64. package/dist/platform.js +120 -52
  65. package/dist/readers-ax-host.d.ts +11 -1
  66. package/dist/readers-ax-host.js +108 -99
  67. package/dist/readers-ax-program.js +5 -0
  68. package/dist/readers-ax-tree.js +1 -0
  69. package/dist/readers-cli.js +79 -0
  70. package/dist/readers.js +9 -0
  71. package/dist/round-state.js +18 -1
  72. package/dist/store.d.ts +22 -4
  73. package/dist/store.js +173 -151
  74. package/dist/types.js +1 -0
  75. package/generated/observation.mjs +1 -1
  76. package/package.json +23 -9
  77. package/skill/SKILL.md +2 -2
  78. package/skill/references/reads.md +1 -1
  79. package/skill/references/recovery.md +4 -4
  80. package/skill/references/setup.md +1 -1
@@ -1,158 +1,187 @@
1
1
  import { xURL } from "./platform-rules.js";
2
2
  import { same } from "./growth-constants.js";
3
- export function createGrowthRecovery(dependencies) {
4
- const { capability, platform, load, evidenceExists, clock, store, settleProof, executor, parallelRound, round, readTask, } = dependencies;
5
- return {
6
- async verifyPublishedPost({ taskId, draftPath, publicURL, evidenceRef, sourceLink, }) {
7
- // Read-only recovery of a single submitted original; never opens a composer.
8
- const caps = capability("read");
9
- if (!caps.covered || typeof platform.readPost !== "function")
10
- return {
11
- status: "needsCapability",
12
- missing: ["platform.readPost", ...caps.missing],
13
- };
14
- const initial = await load(taskId);
15
- const action = initial.state.actions.find((a) => a.kind === "original" && a.draftPath === draftPath);
16
- if (!action ||
17
- !["intent", "unknown"].includes(action.status) ||
18
- !evidenceExists(evidenceRef))
19
- throw new Error("submitted-original-and-public-proof-required");
20
- const url = xURL(publicURL), postId = url.pathname.match(/\/status\/(\d+)$/)?.[1];
21
- if (!postId ||
22
- url.pathname.toLowerCase() !==
23
- `/${action.accountHandle}/status/${postId}`.toLowerCase())
24
- throw new Error("public-account-mismatch");
25
- const observation = await platform.readPost({
26
- postId,
27
- url: publicURL,
28
- evidenceRef,
29
- });
30
- const trailing = action.text.match(/\n\n(https:\/\/\S+)$/);
31
- const matches = (observation.posts ?? []).filter((p) => {
32
- const exact = p.body === action.text;
33
- const card = trailing &&
34
- sourceLink &&
35
- evidenceExists(sourceLink.evidenceRef) &&
36
- sourceLink.resolvedURL === trailing[1] &&
37
- p.links?.includes(sourceLink.href) &&
38
- p.body === action.text.slice(0, -trailing[0].length);
39
- let renderedSource = false;
40
- if (trailing &&
41
- sourceLink?.renderedText &&
42
- evidenceExists(sourceLink.evidenceRef) &&
43
- sourceLink.resolvedURL === trailing[1] &&
44
- p.links?.includes(sourceLink.href)) {
45
- try {
46
- renderedSource =
47
- new URL(sourceLink.renderedText.replace(/\s/g, "")).href ===
48
- sourceLink.resolvedURL &&
49
- p.body?.endsWith(sourceLink.renderedText) &&
50
- p.body.slice(0, -sourceLink.renderedText.length).trimEnd() ===
51
- action.text.slice(0, -trailing[0].length);
52
- }
53
- catch {
54
- /* Invalid rendered URL cannot verify publication. */
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
- return (p.id === postId &&
58
- same(p.author, action.accountHandle) &&
59
- (exact || card || renderedSource) &&
60
- !p.needsExpansion &&
61
- !(action.beforeIds ?? []).includes(p.id) &&
62
- Date.parse(p.datetime) >= Date.parse(action.intentAt) &&
63
- Date.parse(p.datetime) <= clock() + 5000 &&
64
- (!action.assets?.length ||
65
- action.assets.every((a) => p.images?.some((i) => i.alt === a.alt))));
66
- });
67
- Object.assign(observation, {
68
- verified: !observation.needsStateCheck &&
69
- !observation.alerts?.length &&
70
- matches.length === 1,
71
- publicId: matches.length === 1 ? postId : null,
72
- sourceLink,
73
- });
74
- return store.withLock(async () => {
75
- const latest = await load(taskId), target = latest.state.actions.find((a) => a.id === action.id);
76
- if (!["intent", "unknown"].includes(target?.status) ||
77
- target.intentAt !== action.intentAt ||
78
- target.text !== action.text)
79
- throw new Error("recovery-action-changed");
80
- await settleProof(latest, target, observation, {
81
- preserveStatus: true,
82
- });
83
- return {
84
- status: observation.verified ? "verified" : "unknown",
85
- publicId: observation.publicId,
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
- verifySubmission: async ({ taskId, actionId }) => {
93
- const snapshot = await load(taskId), action = snapshot.state.actions?.find((a) => a.id === actionId);
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
- requestedKind: action.kind,
103
- selector: (a) => a.id === actionId,
104
- });
105
- },
106
- verifyFollow: async (target) => {
107
- const mode = target.mode ?? "receipt";
108
- if (!["receipt", "decision"].includes(mode))
109
- throw new Error("关注核验模式须为receipt/decision");
110
- const caps = platform.preflight({ operation: "read" });
111
- if (!caps.covered)
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: "needsCapability",
116
- ...caps,
151
+ status: "needsDecision",
152
+ reason: "original-person-observation-required",
117
153
  };
118
- if (!target.url || !target.evidenceRef) {
119
- const original = (await store.load()).state.actions?.find((a) => a.kind === "follow" && same(a.handle, target.handle));
120
- if (!original)
121
- return {
122
- handle: target.handle,
123
- verified: false,
124
- status: "needsDecision",
125
- reason: "original-person-observation-required",
126
- };
127
- target = original;
128
- }
129
- const value = await readTask("readPerson", {
130
- ...target,
131
- mode: "decision",
132
- });
133
- return {
134
- handle: target.handle,
135
- status: value.status,
136
- fresh: value.fresh,
137
- verified: value.status === "observed" &&
138
- !value.needsStateCheck &&
139
- !value.alerts?.length &&
140
- value.relationship === "following" &&
141
- (!target.accountHandle ||
142
- same(value.accountHandle, target.accountHandle)),
143
- ...(value.evidenceRef ? { evidenceRef: value.evidenceRef } : {}),
144
- ...(value.missing?.length ? { missing: value.missing } : {}),
145
- ...(value.diagnostic
146
- ? {
147
- diagnostic: {
148
- operation: value.diagnostic.operation,
149
- category: value.diagnostic.category,
150
- code: value.diagnostic.code,
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
  }
@@ -1,8 +1,16 @@
1
1
  import type { GrowthDependencies } from "./growth-dependencies.js";
2
- export declare function createGrowthState(dependencies: Pick<GrowthDependencies, "store" | "project" | "clock" | "evidenceExists" | "stamp" | "executor" | "waitBudget" | "platform" | "runtimeId" | "session">): {
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;
@@ -1,13 +1,45 @@
1
1
  import { checkRound } from "./round-state.js";
2
2
  import { kinds } from "./growth-constants.js";
3
- export function createGrowthState(dependencies) {
4
- const { store, project, clock, evidenceExists, stamp, executor, waitBudget, platform, runtimeId, session, } = dependencies;
5
- async function load(taskId) {
6
- const snapshot = await store.load();
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 function save(snapshot, state) {
32
- state.updatedAt = stamp();
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 && clock() < Date.parse(old.lastAt) + 180000
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
- function result(snapshot, needs = []) {
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 function settleProof(snapshot, action, observation, { preserveStatus = false } = {}) {
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 save(snapshot, state);
199
+ return this.handleSave(snapshot, state);
166
200
  }
167
- async function releaseSession(taskId = undefined) {
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
- return { load, save, result, settleProof, releaseSession };
217
+ }
218
+ export function createGrowthState(...args) {
219
+ return new GrowthState(...args);
183
220
  }
@@ -1,17 +1,20 @@
1
1
  import type { GrowthDependencies } from "./growth-dependencies.js";
2
- export declare function createGrowthTasks(dependencies: Pick<GrowthDependencies, "platform" | "capability" | "executor" | "readTask" | "parallelRound" | "round" | "load" | "result" | "publishIndependent" | "prepareActionsForCall">): {
3
- preflight: ({ operation, input }: any) => any;
4
- readCreator: (args: any) => Promise<any>;
5
- inspectAccount: (args: any) => Promise<any>;
6
- readPost: (args: any) => Promise<any>;
7
- readPerson: (args: any) => Promise<any>;
8
- readBookmarks: (args: any) => Promise<any>;
9
- searchSources: (args: any) => Promise<any>;
10
- readFeedback: (args: any) => Promise<any>;
11
- runGrowthRound: (args: any) => Promise<any>;
12
- resumeGrowthRound: (args: any) => Promise<any>;
13
- inspectRound: ({ taskId }: any) => Promise<any>;
14
- followOne: ({ taskId, handle }: any) => Promise<any>;
15
- publishPost: (input: any) => Promise<any>;
16
- replyToPost: ({ taskId, postId, text, preparedAction }: any) => Promise<any>;
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;