@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.
Files changed (79) 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/setup.md +1 -1
@@ -1,17 +1,73 @@
1
+ import { collectCreatorNative } from "./creator-native.js";
1
2
  import { createAXHarness } from "./readers-ax-host.js";
2
3
  import { readAX } from "./readers-ax-parser.js";
3
4
  import { xURL } from "./platform-rules.js";
4
5
  import { serialized, missingMethods, plain, valueOf, } from "./browser-common.js";
5
- export function createNativeXBrowser(app) {
6
- async function navigateNativeTarget(command) {
6
+ export class NativeXBrowser {
7
+ #app;
8
+ #latestAX;
9
+ #delegated;
10
+ #low;
11
+ #current;
12
+ #modes;
13
+ observe;
14
+ constructor(app) {
15
+ this.screenshot = this.screenshot.bind(this);
16
+ this.act = this.act.bind(this);
17
+ this.capabilities = this.capabilities.bind(this);
18
+ this.collectCreator = this.collectCreator.bind(this);
19
+ this.#app = app;
20
+ this.#delegated = {};
21
+ for (const name of [
22
+ "getAXState",
23
+ "click",
24
+ "pressKey",
25
+ "paste",
26
+ "scroll",
27
+ "performSecondaryAction",
28
+ "getScreenshot",
29
+ ]) {
30
+ Object.defineProperty(this.#delegated, name, {
31
+ get: () => {
32
+ if (typeof this.#app?.[name] !== "function")
33
+ return undefined;
34
+ return name === "getAXState"
35
+ ? async (options) => {
36
+ this.#latestAX = await this.#app.getAXState(options);
37
+ return this.#latestAX;
38
+ }
39
+ : (...args) => this.#app[name](...args);
40
+ },
41
+ });
42
+ }
43
+ this.#low = createAXHarness(this.#delegated);
44
+ this.#modes = {
45
+ account: "navigation",
46
+ person: "profile",
47
+ posts: "posts",
48
+ composer: "composer",
49
+ };
50
+ this.observe = this.handleObserve.bind(this);
51
+ Object.assign(this, serialized({
52
+ collectCreator: (args) => collectCreatorNative(this.#low, args),
53
+ capabilities: this.capabilities,
54
+ observe: this.handleObserve.bind(this),
55
+ act: this.act,
56
+ screenshot: () => {
57
+ this.#current = null;
58
+ return this.#low.screenshot();
59
+ },
60
+ }));
61
+ }
62
+ async navigateNativeTarget(command) {
7
63
  xURL(command.url);
8
64
  if (!command.evidenceRef)
9
65
  throw new Error("导航URL缺原观察证据");
10
- await low.observe({ mode: "navigation" }).then((raw) => {
11
- current = { raw };
66
+ await this.#low.observe({ mode: "navigation" }).then((raw) => {
67
+ this.#current = { raw };
12
68
  });
13
- let index = one((e) => /^文本栏 .*地址和搜索栏/.test(e.label));
14
- await low
69
+ let index = this.one((e) => /^文本栏 .*地址和搜索栏/.test(e.label));
70
+ await this.#low
15
71
  .act({
16
72
  index,
17
73
  operation: "fill",
@@ -19,63 +75,33 @@ export function createNativeXBrowser(app) {
19
75
  read: { mode: "navigation" },
20
76
  })
21
77
  .then((raw) => {
22
- current = { raw };
78
+ this.#current = { raw };
23
79
  });
24
- index = one((e) => /^文本栏 .*地址和搜索栏/.test(e.label));
25
- await low.act({
80
+ index = this.one((e) => /^文本栏 .*地址和搜索栏/.test(e.label));
81
+ await this.#low.act({
26
82
  index,
27
83
  operation: "pressKey",
28
84
  value: "Return",
29
85
  read: { mode: "navigation" },
30
86
  });
31
- current = null;
87
+ this.#current = null;
32
88
  return;
33
89
  }
34
- let latestAX;
35
- const delegated = {};
36
- for (const name of [
37
- "getAXState",
38
- "click",
39
- "pressKey",
40
- "paste",
41
- "scroll",
42
- "performSecondaryAction",
43
- "getScreenshot",
44
- ]) {
45
- Object.defineProperty(delegated, name, {
46
- get() {
47
- if (typeof app?.[name] !== "function")
48
- return undefined;
49
- return name === "getAXState"
50
- ? async (options) => {
51
- latestAX = await app.getAXState(options);
52
- return latestAX;
53
- }
54
- : (...args) => app[name](...args);
55
- },
56
- });
57
- }
58
- const low = createAXHarness(delegated);
59
- let current;
60
- const modes = {
61
- account: "navigation",
62
- person: "profile",
63
- posts: "posts",
64
- composer: "composer",
65
- };
66
- async function observe(query) {
67
- current = null;
68
- if (!modes[query.view])
90
+ async handleObserve(query) {
91
+ this.#current = null;
92
+ if (!this.#modes[query.view])
69
93
  throw new Error("未封装的业务视图");
70
- const raw = await low.observe({
71
- mode: modes[query.view],
94
+ const raw = await this.#low.observe({
95
+ mode: this.#modes[query.view],
72
96
  ...(query.targetId ? { targetId: query.targetId } : {}),
73
97
  limit: query.limit ?? 1,
74
98
  });
75
- current = { query, raw };
99
+ this.#current = { query, raw };
76
100
  const entries = raw.evidence ?? [];
77
101
  // Bind actor and target to the SAME suppressed observation; no additional tool round trip.
78
- const ownLinks = readAX(latestAX, { mode: "navigation" }).evidence.filter((e) => /^link 个人资料, Value: x\.com\/\w+$/.test(e.label));
102
+ const ownLinks = readAX(this.#latestAX, {
103
+ mode: "navigation",
104
+ }).evidence.filter((e) => /^link 个人资料, Value: x\.com\/\w+$/.test(e.label));
79
105
  const base = {
80
106
  page: raw.page,
81
107
  alerts: raw.alerts,
@@ -129,87 +155,94 @@ export function createNativeXBrowser(app) {
129
155
  })),
130
156
  };
131
157
  }
132
- const one = (predicate) => {
133
- const entries = (current?.raw.evidence ?? [])
134
- .concat(current?.raw.navigation ?? [])
158
+ one(predicate) {
159
+ const entries = (this.#current?.raw.evidence ?? [])
160
+ .concat(this.#current?.raw.navigation ?? [])
135
161
  .filter(predicate);
136
162
  if (entries.length !== 1)
137
163
  throw new Error("语义控件未唯一观察:先补能力/证据");
138
164
  return entries[0].index;
139
- };
140
- return serialized({
141
- capabilities(operation) {
142
- if (["readBookmarks", "searchSources", "readFeedback"].includes(operation))
143
- return [`native.${operation}`];
144
- const names = operation === "read"
145
- ? ["getAXState"]
146
- : ["getAXState", "click", "pressKey", "paste"];
147
- return operation === "attachAssets"
148
- ? ["native.attachAssets"]
149
- : operation === "refreshPosts"
150
- ? ["native.refreshPosts"]
151
- : missingMethods(app, names);
152
- },
153
- observe,
154
- async act(command) {
155
- if (command.operation === "openObserved") {
156
- return navigateNativeTarget(command);
157
- }
158
- if (command.operation === "refreshPerson") {
159
- xURL(command.url);
160
- await low.observe({ mode: "navigation" }).then((raw) => {
161
- current = { raw };
162
- });
163
- const index = one((e) => /^按钮 (重新加载|刷新)(?:,|$)/.test(e.label));
164
- current = null;
165
- return low.act({
166
- index,
167
- operation: "click",
168
- read: { mode: "profile" },
169
- });
170
- }
171
- if (!current)
172
- throw new Error("语义操作必须先observe");
173
- let index, operation = "click", value;
174
- if (command.operation === "follow")
175
- index = one((e) => [
176
- `按钮 关注 @${command.handle}`,
177
- `按钮 回关 @${command.handle}`,
178
- ].includes(e.label));
179
- else if (command.operation === "openReply") {
180
- if (current.query?.targetId !== command.postId)
181
- throw new Error("回复目标不匹配");
182
- const target = current.raw.posts?.filter((p) => p.id === command.postId);
183
- if (target?.length !== 1)
184
- throw new Error("回复主帖不唯一");
185
- const buttons = target[0].evidence.filter((e) => /^按钮 .*回复/.test(e.label));
186
- if (buttons.length !== 1)
187
- throw new Error("回复按钮不唯一");
188
- index = buttons[0].index;
189
- }
190
- else if (command.operation === "fillComposer") {
191
- index = one((e) => /^(?:文本输入区(?:域)?|文本区域|文本栏) /.test(e.label));
192
- operation = "fill";
193
- value = command.text;
194
- }
195
- else if (["submitPost", "submitReply"].includes(command.operation)) {
196
- index = one((e) => new RegExp(command.operation === "submitReply"
197
- ? "^按钮 (回复|Reply)(?:,|$)"
198
- : "^按钮 (发帖|发布|Post)(?:,|$)", "i").test(e.label));
199
- }
200
- else
201
- throw new Error("未封装的原生动作");
202
- const read = {
203
- mode: command.operation === "follow" ? "profile" : "composer",
204
- };
205
- if (["submitPost", "submitReply"].includes(command.operation))
206
- await command.beforeSubmit?.();
207
- current = null;
208
- return low.act({ index, operation, value, read });
209
- },
210
- screenshot: () => {
211
- current = null;
212
- return low.screenshot();
213
- },
214
- });
165
+ }
166
+ collectCreator(args) {
167
+ return collectCreatorNative(this.#low, args);
168
+ }
169
+ capabilities(operation) {
170
+ if (["readBookmarks", "searchSources", "readFeedback"].includes(operation))
171
+ return [`native.${operation}`];
172
+ const names = operation === "read"
173
+ ? ["getAXState"]
174
+ : ["getAXState", "click", "pressKey", "paste"];
175
+ return operation === "attachAssets"
176
+ ? ["native.attachAssets"]
177
+ : operation === "refreshPosts"
178
+ ? ["native.refreshPosts"]
179
+ : missingMethods(this.#app, names);
180
+ }
181
+ async act(command) {
182
+ if (command.operation === "waitForPosts") {
183
+ this.#current = null;
184
+ return this.#low.observe({ mode: "posts", limit: 5 });
185
+ }
186
+ if (command.operation === "openObserved") {
187
+ return this.navigateNativeTarget(command);
188
+ }
189
+ if (command.operation === "refreshPerson") {
190
+ xURL(command.url);
191
+ await this.#low.observe({ mode: "navigation" }).then((raw) => {
192
+ this.#current = { raw };
193
+ });
194
+ const index = this.one((e) => /^按钮 (重新加载|刷新)(?:,|$)/.test(e.label));
195
+ this.#current = null;
196
+ return this.#low.act({
197
+ index,
198
+ operation: "click",
199
+ read: { mode: "profile" },
200
+ });
201
+ }
202
+ if (!this.#current)
203
+ throw new Error("语义操作必须先observe");
204
+ let index, operation = "click", value;
205
+ if (command.operation === "follow")
206
+ index = this.one((e) => [
207
+ `按钮 关注 @${command.handle}`,
208
+ `按钮 回关 @${command.handle}`,
209
+ ].includes(e.label));
210
+ else if (command.operation === "openReply") {
211
+ if (this.#current.query?.targetId !== command.postId)
212
+ throw new Error("回复目标不匹配");
213
+ const target = this.#current.raw.posts?.filter((p) => p.id === command.postId);
214
+ if (target?.length !== 1)
215
+ throw new Error("回复主帖不唯一");
216
+ const buttons = target[0].evidence.filter((e) => /^按钮 .*回复/.test(e.label));
217
+ if (buttons.length !== 1)
218
+ throw new Error("回复按钮不唯一");
219
+ index = buttons[0].index;
220
+ }
221
+ else if (command.operation === "fillComposer") {
222
+ index = this.one((e) => /^(?:文本输入区(?:域)?|文本区域|文本栏) /.test(e.label));
223
+ operation = "fill";
224
+ value = command.text;
225
+ }
226
+ else if (["submitPost", "submitReply"].includes(command.operation)) {
227
+ index = this.one((e) => new RegExp(command.operation === "submitReply"
228
+ ? "^按钮 (回复|Reply)(?:,|$)"
229
+ : "^按钮 (发帖|发布|Post)(?:,|$)", "i").test(e.label));
230
+ }
231
+ else
232
+ throw new Error("未封装的原生动作");
233
+ const read = {
234
+ mode: command.operation === "follow" ? "profile" : "composer",
235
+ };
236
+ if (["submitPost", "submitReply"].includes(command.operation))
237
+ await command.beforeSubmit?.();
238
+ this.#current = null;
239
+ return this.#low.act({ index, operation, value, read });
240
+ }
241
+ screenshot() {
242
+ this.#current = null;
243
+ return this.#low.screenshot();
244
+ }
245
+ }
246
+ export function createNativeXBrowser(...args) {
247
+ return new NativeXBrowser(...args);
215
248
  }
@@ -0,0 +1,3 @@
1
+ import type { BoundaryRecord } from "./boundary.js";
2
+ import type { CreatorOptions } from "./types.js";
3
+ export declare function collectCreatorNative(low: BoundaryRecord, { handle, target, maxScrolls, navigate }: CreatorOptions): Promise<BoundaryRecord>;
@@ -0,0 +1,113 @@
1
+ // Bounded collection on an already selected profile. Relative AX times stay unknown.
2
+ export async function collectCreatorNative(low, { handle, target = 10, maxScrolls = 18, navigate = true }) {
3
+ if (!/^[A-Za-z0-9_]{1,15}$/.test(handle) ||
4
+ !Number.isInteger(target) ||
5
+ target < 1 ||
6
+ target > 50 ||
7
+ !Number.isInteger(maxScrolls) ||
8
+ maxScrolls < 0 ||
9
+ maxScrolls > 50)
10
+ throw new Error("Invalid creator collection input");
11
+ if (navigate)
12
+ return {
13
+ status: "needsCapability",
14
+ complete: false,
15
+ posts: [],
16
+ missing: ["native.creator-navigation"],
17
+ };
18
+ const profile = await low.observe({ mode: "profile" });
19
+ const expected = new RegExp(`^(?:https?://)?x\\.com/${handle}/?$`, "i");
20
+ if (!expected.test(profile.page ?? "") || profile.needsStateCheck)
21
+ throw new Error("creator-url-mismatch");
22
+ const posts = new Map();
23
+ const missing = new Set();
24
+ const labels = (profile.evidence ?? [])
25
+ .filter((e) => /^(?:文本|text|link) /.test(e.label))
26
+ .map((e) => e.label.replace(/^(?:文本|text|link) /, ""));
27
+ const meta = {
28
+ headerText: labels.join("\n"),
29
+ totalPostsText: labels.find((label) => /^[\d,.万亿KMB]+ 帖子$/.test(label)) ??
30
+ null,
31
+ missing: [],
32
+ };
33
+ let scrolls = 0, stagnant = 0, last;
34
+ const result = () => {
35
+ const selected = [...posts.values()].slice(0, target);
36
+ if (selected.length < target)
37
+ missing.add("creator-target-not-reached");
38
+ for (const post of selected) {
39
+ if (!post.datetime)
40
+ missing.add("absolute-time-missing");
41
+ if (post.needsExpansion)
42
+ missing.add("post-needs-expansion");
43
+ if (!post.body)
44
+ missing.add("post-body-missing");
45
+ }
46
+ if (profile.alerts?.length)
47
+ missing.add("platform-alert");
48
+ return {
49
+ profile: { ...profile, handle },
50
+ meta,
51
+ posts: selected,
52
+ complete: missing.size === 0,
53
+ missing: [...missing],
54
+ scrolls,
55
+ observedAt: new Date().toISOString(),
56
+ };
57
+ };
58
+ for (;;) {
59
+ const before = posts.size;
60
+ for (let offset = 0; offset < 250; offset += 5) {
61
+ last = await low.observe({ mode: "posts", offset, limit: 5 });
62
+ if (!expected.test(last.page ?? "") ||
63
+ last.needsStateCheck ||
64
+ last.alerts?.length) {
65
+ missing.add("creator-window-unconfirmed");
66
+ return { ...result(), alerts: last.alerts ?? [] };
67
+ }
68
+ for (const post of last.posts ?? []) {
69
+ if (post.id && post.author?.toLowerCase() === handle.toLowerCase())
70
+ posts.set(post.id, {
71
+ ...post,
72
+ body: post.text,
73
+ primary: `https://x.com/${post.author}/status/${post.id}`,
74
+ links: (post.evidence ?? []).flatMap((e) => {
75
+ const href = e.label.match(/Value: ((?:https?:\/\/)?(?:[\w.-]+\.[a-z]+)\/[^\s,]*)/i)?.[1];
76
+ return href
77
+ ? [href.startsWith("http") ? href : `https://${href}`]
78
+ : [];
79
+ }),
80
+ metricDescriptions: (post.evidence ?? []).map((e) => e.label),
81
+ });
82
+ }
83
+ if (!last.moreInDOM || posts.size >= target)
84
+ break;
85
+ }
86
+ stagnant = posts.size === before ? stagnant + 1 : 0;
87
+ if (posts.size >= target || scrolls >= maxScrolls || stagnant >= 3)
88
+ break;
89
+ // Restore a fresh visible window so the scroll index is never from an old read.
90
+ last = await low.observe({ mode: "posts", limit: 5 });
91
+ if (!expected.test(last.page ?? "") ||
92
+ last.needsStateCheck ||
93
+ last.alerts?.length) {
94
+ missing.add("creator-window-unconfirmed");
95
+ return result();
96
+ }
97
+ const control = last.posts
98
+ ?.flatMap((post) => post.evidence ?? [])
99
+ .find((entry) => /^link .*Value: x\.com\/\w+\/status\/\d+(?:,|$)/.test(entry.label));
100
+ if (!control) {
101
+ missing.add("native.creator-scroll-target");
102
+ break;
103
+ }
104
+ await low.act({
105
+ index: control.index,
106
+ operation: "scroll",
107
+ value: "down",
108
+ read: { mode: "posts", limit: 5 },
109
+ });
110
+ scrolls++;
111
+ }
112
+ return result();
113
+ }
@@ -0,0 +1,11 @@
1
+ /** Explicit lower-level diagnostics and legacy consumers. Normal tasks use the root. */
2
+ export { createGrowthHarness } from "./growth.js";
3
+ export { createBusinessHarness } from "./api.js";
4
+ export { createFileRoundStore, HarnessConflict } from "./store.js";
5
+ export { createXPlatform, xURL } from "./platform.js";
6
+ export { createDOMXBrowser, createNativeXBrowser, readXSurface, atomicObservationCode, readXNetworkDiagnostics, } from "./browser.js";
7
+ export { readXObservation } from "./observation.js";
8
+ export * from "./readers.js";
9
+ export { checkRound, readRound, runRoundCLI } from "./round-state.js";
10
+ export { readCreatorMeta, readCreatorPostExtras } from "./creator.js";
11
+ export type { BoundaryRecord } from "./boundary.js";
@@ -0,0 +1,10 @@
1
+ /** Explicit lower-level diagnostics and legacy consumers. Normal tasks use the root. */
2
+ export { createGrowthHarness } from "./growth.js";
3
+ export { createBusinessHarness } from "./api.js";
4
+ export { createFileRoundStore, HarnessConflict } from "./store.js";
5
+ export { createXPlatform, xURL } from "./platform.js";
6
+ export { createDOMXBrowser, createNativeXBrowser, readXSurface, atomicObservationCode, readXNetworkDiagnostics, } from "./browser.js";
7
+ export { readXObservation } from "./observation.js";
8
+ export * from "./readers.js";
9
+ export { checkRound, readRound, runRoundCLI } from "./round-state.js";
10
+ export { readCreatorMeta, readCreatorPostExtras } from "./creator.js";
@@ -1,5 +1,8 @@
1
1
  import type { BoundaryRecord } from "./boundary.js";
2
2
  import type { GrowthDependencies } from "./growth-dependencies.js";
3
- export declare function createFollowBatch(dependencies: Pick<GrowthDependencies, "prepareActionsForCall" | "store" | "scope" | "stamp" | "clock" | "evidenceExists" | "executor" | "parallelRound" | "round">): {
4
- followBatch: ({ taskId, handles, preparedActions }?: any) => Promise<BoundaryRecord>;
5
- };
3
+ export declare class FollowBatch {
4
+ #private;
5
+ constructor(dependencies: Pick<GrowthDependencies, "prepareActionsForCall" | "store" | "scope" | "stamp" | "clock" | "evidenceExists" | "executor" | "parallelRound" | "round">);
6
+ followBatch({ taskId, handles, preparedActions }?: any): Promise<BoundaryRecord>;
7
+ }
8
+ export declare function createFollowBatch(...args: ConstructorParameters<typeof FollowBatch>): FollowBatch;