@skrr-ai/cli 0.1.108 → 0.1.109

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 (174) hide show
  1. package/README.md +10 -0
  2. package/dist/base-command.d.ts +59 -0
  3. package/dist/base-command.js +118 -7
  4. package/dist/commands/agents/update.d.ts +14 -0
  5. package/dist/commands/agents/update.js +40 -1
  6. package/dist/commands/commitments/accounting.js +7 -2
  7. package/dist/commands/commitments/cost.d.ts +11 -0
  8. package/dist/commands/commitments/cost.js +26 -3
  9. package/dist/commands/commitments/create.js +2 -1
  10. package/dist/commands/commitments/operating-review.d.ts +15 -0
  11. package/dist/commands/commitments/operating-review.js +103 -50
  12. package/dist/commands/commitments/update.js +12 -5
  13. package/dist/commands/doctor.js +9 -0
  14. package/dist/commands/goals/list.js +6 -1
  15. package/dist/commands/grants/rotate.js +4 -0
  16. package/dist/commands/login.js +42 -14
  17. package/dist/commands/machines/list.js +28 -6
  18. package/dist/commands/projects/show.js +1 -1
  19. package/dist/commands/revoke-daemon.js +2 -1
  20. package/dist/commands/spaces/workflows/archive.d.ts +1 -0
  21. package/dist/commands/spaces/workflows/archive.js +1 -0
  22. package/dist/commands/spaces/workflows/artifact.d.ts +1 -0
  23. package/dist/commands/spaces/workflows/artifact.js +1 -0
  24. package/dist/commands/spaces/workflows/cancel-node.d.ts +1 -0
  25. package/dist/commands/spaces/workflows/cancel-node.js +1 -0
  26. package/dist/commands/spaces/workflows/cancel.d.ts +1 -0
  27. package/dist/commands/spaces/workflows/cancel.js +6 -2
  28. package/dist/commands/spaces/workflows/create.d.ts +1 -0
  29. package/dist/commands/spaces/workflows/create.js +7 -1
  30. package/dist/commands/spaces/workflows/events.d.ts +1 -0
  31. package/dist/commands/spaces/workflows/events.js +1 -0
  32. package/dist/commands/spaces/workflows/fixtures/create.d.ts +1 -0
  33. package/dist/commands/spaces/workflows/fixtures/create.js +1 -0
  34. package/dist/commands/spaces/workflows/fixtures/list.d.ts +1 -0
  35. package/dist/commands/spaces/workflows/fixtures/list.js +1 -0
  36. package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +1 -0
  37. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +1 -0
  38. package/dist/commands/spaces/workflows/list.d.ts +1 -0
  39. package/dist/commands/spaces/workflows/list.js +1 -0
  40. package/dist/commands/spaces/workflows/node-types.d.ts +2 -0
  41. package/dist/commands/spaces/workflows/node-types.js +24 -6
  42. package/dist/commands/spaces/workflows/pause.d.ts +1 -0
  43. package/dist/commands/spaces/workflows/pause.js +1 -0
  44. package/dist/commands/spaces/workflows/plan/show.d.ts +1 -0
  45. package/dist/commands/spaces/workflows/plan/show.js +1 -0
  46. package/dist/commands/spaces/workflows/plan/update.d.ts +1 -0
  47. package/dist/commands/spaces/workflows/plan/update.js +1 -0
  48. package/dist/commands/spaces/workflows/proposals/apply.d.ts +1 -0
  49. package/dist/commands/spaces/workflows/proposals/apply.js +1 -0
  50. package/dist/commands/spaces/workflows/proposals/confirm.d.ts +1 -0
  51. package/dist/commands/spaces/workflows/proposals/confirm.js +1 -0
  52. package/dist/commands/spaces/workflows/proposals/create.d.ts +1 -0
  53. package/dist/commands/spaces/workflows/proposals/create.js +1 -0
  54. package/dist/commands/spaces/workflows/proposals/dismiss.d.ts +1 -0
  55. package/dist/commands/spaces/workflows/proposals/dismiss.js +1 -0
  56. package/dist/commands/spaces/workflows/proposals/list.d.ts +1 -0
  57. package/dist/commands/spaces/workflows/proposals/list.js +1 -0
  58. package/dist/commands/spaces/workflows/publish.d.ts +1 -0
  59. package/dist/commands/spaces/workflows/publish.js +9 -0
  60. package/dist/commands/spaces/workflows/reconcile.d.ts +1 -0
  61. package/dist/commands/spaces/workflows/reconcile.js +1 -0
  62. package/dist/commands/spaces/workflows/resume.d.ts +1 -0
  63. package/dist/commands/spaces/workflows/resume.js +1 -0
  64. package/dist/commands/spaces/workflows/retry.d.ts +1 -0
  65. package/dist/commands/spaces/workflows/retry.js +1 -0
  66. package/dist/commands/spaces/workflows/run.d.ts +1 -0
  67. package/dist/commands/spaces/workflows/run.js +14 -7
  68. package/dist/commands/spaces/workflows/runs.d.ts +1 -0
  69. package/dist/commands/spaces/workflows/runs.js +1 -0
  70. package/dist/commands/spaces/workflows/show.d.ts +25 -0
  71. package/dist/commands/spaces/workflows/show.js +42 -4
  72. package/dist/commands/spaces/workflows/signal.d.ts +12 -0
  73. package/dist/commands/spaces/workflows/signal.js +40 -2
  74. package/dist/commands/spaces/workflows/start.d.ts +1 -0
  75. package/dist/commands/spaces/workflows/start.js +1 -0
  76. package/dist/commands/spaces/workflows/task-execution/create.d.ts +1 -0
  77. package/dist/commands/spaces/workflows/task-execution/create.js +1 -0
  78. package/dist/commands/spaces/workflows/test.d.ts +1 -0
  79. package/dist/commands/spaces/workflows/test.js +1 -0
  80. package/dist/commands/spaces/workflows/triggers/create.d.ts +1 -0
  81. package/dist/commands/spaces/workflows/triggers/create.js +1 -0
  82. package/dist/commands/spaces/workflows/triggers/fire.d.ts +1 -0
  83. package/dist/commands/spaces/workflows/triggers/fire.js +1 -0
  84. package/dist/commands/spaces/workflows/triggers/list.d.ts +1 -0
  85. package/dist/commands/spaces/workflows/triggers/list.js +1 -0
  86. package/dist/commands/spaces/workflows/triggers/update.d.ts +1 -0
  87. package/dist/commands/spaces/workflows/triggers/update.js +1 -0
  88. package/dist/commands/spaces/workflows/update.d.ts +1 -0
  89. package/dist/commands/spaces/workflows/update.js +4 -0
  90. package/dist/commands/spaces/workflows/validate.d.ts +1 -0
  91. package/dist/commands/spaces/workflows/validate.js +15 -0
  92. package/dist/commands/tasks/activity.js +6 -2
  93. package/dist/commands/tasks/bulk-update.d.ts +1 -0
  94. package/dist/commands/tasks/bulk-update.js +7 -0
  95. package/dist/commands/tasks/complete.js +2 -1
  96. package/dist/commands/tasks/create-tree.d.ts +69 -0
  97. package/dist/commands/tasks/create-tree.js +413 -47
  98. package/dist/commands/tasks/list.d.ts +2 -0
  99. package/dist/commands/tasks/list.js +19 -1
  100. package/dist/commands/tasks/rearm-recurrence.d.ts +22 -0
  101. package/dist/commands/tasks/rearm-recurrence.js +68 -0
  102. package/dist/commands/tasks/show.d.ts +1 -0
  103. package/dist/commands/tasks/show.js +17 -3
  104. package/dist/commands/tasks/timeline.js +4 -0
  105. package/dist/commands/tasks/update.d.ts +1 -0
  106. package/dist/commands/tasks/update.js +6 -1
  107. package/dist/commands/triggers/create.js +8 -2
  108. package/dist/commands/triggers/parse-intent.d.ts +7 -0
  109. package/dist/commands/triggers/parse-intent.js +31 -9
  110. package/dist/commands/workspaces/grants.js +23 -3
  111. package/dist/commands/workspaces/members.d.ts +20 -0
  112. package/dist/commands/workspaces/members.js +64 -0
  113. package/dist/commands/workspaces/stewardship.js +14 -2
  114. package/dist/help.js +12 -0
  115. package/dist/hooks/command-not-found.js +27 -1
  116. package/dist/lib/command-miss.d.ts +52 -2
  117. package/dist/lib/command-miss.js +187 -18
  118. package/dist/lib/commitment-loop-status.d.ts +8 -0
  119. package/dist/lib/commitment-loop-status.js +45 -0
  120. package/dist/lib/commitments.d.ts +16 -2
  121. package/dist/lib/commitments.js +48 -8
  122. package/dist/lib/daemon-command-hint.d.ts +15 -0
  123. package/dist/lib/daemon-command-hint.js +21 -0
  124. package/dist/lib/daemon-setup.js +10 -1
  125. package/dist/lib/daemonBrokerRefusal.js +3 -2
  126. package/dist/lib/daemonHandoff.js +2 -1
  127. package/dist/lib/deployed-build.d.ts +43 -0
  128. package/dist/lib/deployed-build.js +66 -0
  129. package/dist/lib/grants.d.ts +27 -1
  130. package/dist/lib/grants.js +39 -21
  131. package/dist/lib/json-mode.d.ts +11 -0
  132. package/dist/lib/json-mode.js +19 -0
  133. package/dist/lib/json-output.d.ts +25 -0
  134. package/dist/lib/json-output.js +30 -0
  135. package/dist/lib/list-output.d.ts +9 -0
  136. package/dist/lib/list-output.js +20 -1
  137. package/dist/lib/local-daemon-readiness.d.ts +90 -0
  138. package/dist/lib/local-daemon-readiness.js +143 -0
  139. package/dist/lib/machines.d.ts +117 -0
  140. package/dist/lib/machines.js +139 -0
  141. package/dist/lib/managed-instruction-local.d.ts +2 -0
  142. package/dist/lib/managed-instruction-local.js +2 -0
  143. package/dist/lib/profile-machine-identity-guard.d.ts +18 -0
  144. package/dist/lib/profile-machine-identity-guard.js +29 -0
  145. package/dist/lib/request-field-flags.d.ts +20 -0
  146. package/dist/lib/request-field-flags.js +34 -0
  147. package/dist/lib/stewardship-output.d.ts +1 -1
  148. package/dist/lib/stewardship-output.js +26 -30
  149. package/dist/lib/task-batch.d.ts +61 -4
  150. package/dist/lib/task-batch.js +316 -80
  151. package/dist/lib/task-economics.d.ts +5 -0
  152. package/dist/lib/task-economics.js +15 -0
  153. package/dist/lib/task-recurrence-outcome.d.ts +6 -6
  154. package/dist/lib/task-recurrence-outcome.js +27 -3
  155. package/dist/lib/tasks.d.ts +39 -0
  156. package/dist/lib/tasks.js +132 -0
  157. package/dist/lib/triggers.d.ts +31 -1
  158. package/dist/lib/triggers.js +55 -1
  159. package/dist/lib/workflow-issues.d.ts +14 -0
  160. package/dist/lib/workflow-issues.js +32 -0
  161. package/dist/lib/workflow-run-view.d.ts +55 -0
  162. package/dist/lib/workflow-run-view.js +105 -0
  163. package/dist/lib/workspaces.d.ts +31 -4
  164. package/dist/lib/workspaces.js +43 -0
  165. package/dist/lib/write-target.d.ts +29 -0
  166. package/dist/lib/write-target.js +72 -0
  167. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.d.ts +51 -0
  168. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.js +50 -0
  169. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.d.ts +51 -0
  170. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.js +46 -0
  171. package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
  172. package/dist/node_modules/@skrr-ai/data-provider/index.js +6718 -6370
  173. package/oclif.manifest.json +376 -52
  174. package/package.json +19 -1
@@ -5,6 +5,8 @@ exports.rerunWarning = rerunWarning;
5
5
  exports.derivedTreePrefix = derivedTreePrefix;
6
6
  exports.derivedPrefixWarning = derivedPrefixWarning;
7
7
  exports.partialFailureResult = partialFailureResult;
8
+ exports.orderForCreation = orderForCreation;
9
+ exports.describeDefaultAgentAssignment = describeDefaultAgentAssignment;
8
10
  exports.normalizeTreeRoots = normalizeTreeRoots;
9
11
  exports.planTree = planTree;
10
12
  const core_1 = require("@oclif/core");
@@ -13,6 +15,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
13
15
  const base_command_1 = require("../../base-command");
14
16
  const prompt_1 = require("../../lib/prompt");
15
17
  const task_batch_1 = require("../../lib/task-batch");
18
+ const write_target_1 = require("../../lib/write-target");
16
19
  const tasks_1 = require("../../lib/tasks");
17
20
  /**
18
21
  * Nodes a re-run could not recognize: no externalKey of their own and no
@@ -52,7 +55,7 @@ function derivedPrefixWarning(prefix) {
52
55
  return {
53
56
  code: 'TREE_PREFIX_DERIVED',
54
57
  value: prefix,
55
- hint: `No --external-key-prefix was given, so nodes are keyed "${prefix}/<tempId>" from the file's content: ` +
58
+ hint: `No --external-key-prefix was given, so nodes without their own externalKey are keyed "${prefix}/<tempId>" from the file's content: ` +
56
59
  're-running the same file updates these tasks instead of creating copies. Pass --fresh to create a second copy, ' +
57
60
  'or --external-key-prefix to choose your own keys.',
58
61
  };
@@ -60,14 +63,27 @@ function derivedPrefixWarning(prefix) {
60
63
  /**
61
64
  * What a half-applied tree reports (E4). The ids already created are the only
62
65
  * way to continue or clean up, and the failing call used to print none of them.
66
+ *
67
+ * Every confirmed id is in `mapping` however the run ended, and `summary` counts
68
+ * the three things a failure can leave: confirmed, UNKNOWN (submitted, outcome
69
+ * not established — it may exist) and not attempted. They are different next
70
+ * steps, and a count that folded them together was the thing the first report
71
+ * of OSK-S-440 could not rely on.
63
72
  */
64
73
  function partialFailureResult(args) {
65
74
  const { results, mapping, failedAt, total, prefix, warnings } = args;
75
+ const outcomes = (args.failures ?? []).map((f) => String(f.outcome));
66
76
  return {
67
77
  object: 'task_create_tree_result',
68
78
  ok: false,
69
79
  count: results.length,
70
80
  total,
81
+ summary: {
82
+ confirmed: results.length,
83
+ unknown: outcomes.filter((o) => o === 'unknown').length,
84
+ notAttempted: outcomes.filter((o) => o === 'not_attempted').length,
85
+ failed: outcomes.filter((o) => o === 'failed').length,
86
+ },
71
87
  created: results.map((r) => ({
72
88
  tempId: r.tempId,
73
89
  parentTempId: r.parentTempId,
@@ -80,18 +96,84 @@ function partialFailureResult(args) {
80
96
  failedAt,
81
97
  ...(args.failures ? { failures: args.failures } : {}),
82
98
  rerun: prefix || args.keyed
83
- ? 'Re-run the same keyed file with --resume to confirm existing IDs and create only missing nodes. A submitted write may still finish; stable keys prevent duplicates.'
99
+ ? 'Re-run the same keyed file with --resume to confirm existing IDs and create only missing nodes; --verify lists what exists and what is missing without writing. A submitted write may still finish; stable keys prevent duplicates.'
84
100
  : 'Re-running creates un-keyed nodes again. Use mapping to continue by hand, or re-run with --external-key-prefix.',
101
+ ...(args.next && (prefix || args.keyed) ? { next: args.next } : {}),
85
102
  ...(warnings.length > 0 ? { warnings } : {}),
86
103
  };
87
104
  }
105
+ /**
106
+ * Order nodes so everything a node depends on — its parent and every blocker —
107
+ * comes BEFORE it, keeping input order otherwise. The batch and the serial path
108
+ * both create in this order, so a parent id and a blocker id exist by the time a
109
+ * node that needs them is written.
110
+ *
111
+ * Also the place a bad `blockedByTempIds` is caught: before any request, naming
112
+ * the node, rather than as a per-row refusal after half the tree exists.
113
+ */
114
+ function orderForCreation(planned) {
115
+ const byKey = new Map(planned.map((node) => [node.key, node]));
116
+ const errors = [];
117
+ const dependenciesOf = (node) => {
118
+ const out = [];
119
+ if (node.parentKey)
120
+ out.push(node.parentKey);
121
+ for (const blocker of node.blockerKeys ?? []) {
122
+ if (blocker === node.key) {
123
+ errors.push(`${node.key}: a node cannot block itself`);
124
+ }
125
+ else if (!byKey.has(blocker)) {
126
+ errors.push(`${node.key}: blockedByTempIds "${blocker}" does not match any node's tempId in this file ` +
127
+ '(to block on a task that already exists, give its id in blockedByIds)');
128
+ }
129
+ else {
130
+ out.push(blocker);
131
+ }
132
+ }
133
+ return out;
134
+ };
135
+ const ordered = [];
136
+ const state = new Map();
137
+ const path = [];
138
+ const visit = (node) => {
139
+ const current = state.get(node.key);
140
+ if (current === 'done')
141
+ return;
142
+ if (current === 'visiting') {
143
+ const loop = [...path.slice(path.indexOf(node.key)), node.key];
144
+ errors.push(`dependency cycle: ${loop.join(' -> ')}`);
145
+ return;
146
+ }
147
+ state.set(node.key, 'visiting');
148
+ path.push(node.key);
149
+ for (const dependency of dependenciesOf(node)) {
150
+ const next = byKey.get(dependency);
151
+ if (next)
152
+ visit(next);
153
+ }
154
+ path.pop();
155
+ state.set(node.key, 'done');
156
+ ordered.push(node);
157
+ };
158
+ for (const node of planned)
159
+ visit(node);
160
+ return { ordered: errors.length > 0 ? planned : ordered, errors: [...new Set(errors)] };
161
+ }
88
162
  class TasksCreateTree extends base_command_1.BaseCommand {
89
- static description = 'Create a task tree from JSON. Use {"nodes":[{"tempId":"root","title":"Task","executionMode":"human","assigneeUserIds":["<user-id>"]}]} to avoid inheriting a Space lead agent.';
163
+ static description = 'Create a task tree from JSON and make every node exist. Recommended shape: ' +
164
+ '{"nodes":[{"tempId":"root","title":"Task","executionMode":"human","assigneeUserIds":["<user-id>"]}]} ' +
165
+ '(a "nodes" array, each node with optional "children"; {"root":{...,"children":[...]}} is accepted too). ' +
166
+ 'Put "me" in assigneeUserIds for yourself and "blockedByTempIds":["<tempId>"] for dependencies. ' +
167
+ 'Setting executionMode human with an explicit assignee avoids inheriting a Space lead agent. ' +
168
+ 'Nodes with an externalKey (or under --external-key-prefix) are created once: a re-run, --resume or --verify finds them again.';
90
169
  static examples = [
91
170
  '<%= config.bin %> tasks create-tree --from-json tree.json --space <space-id> --json',
92
171
  '<%= config.bin %> tasks create-tree --from-json tree.json --space <space-id> --default-agent auto',
93
172
  '<%= config.bin %> tasks create-tree --from-json tree.json --external-key-prefix recursive-learning/ws0',
94
173
  '<%= config.bin %> tasks create-tree --from-json tree.json --dry-run --json',
174
+ '<%= config.bin %> tasks create-tree --from-json tree.json --external-key-prefix mine/ --verify --json',
175
+ '<%= config.bin %> tasks create-tree --from-json tree.json --external-key-prefix mine/ --resume',
176
+ '<%= config.bin %> tasks create-tree --from-json tree.json --space <space-id> --project OSK-P-1 --expect-target skrr.ai',
95
177
  ];
96
178
  static flags = {
97
179
  json: core_1.Flags.boolean({ description: 'Output the create-tree result as JSON' }),
@@ -99,7 +181,13 @@ class TasksCreateTree extends base_command_1.BaseCommand {
99
181
  description: 'Path to a JSON file (or "-" for stdin) describing the tree',
100
182
  required: true,
101
183
  }),
102
- 'dry-run': core_1.Flags.boolean({ description: 'Validate and print the plan without creating tasks' }),
184
+ 'dry-run': core_1.Flags.boolean({
185
+ description: 'Validate and print the plan without creating tasks. Each node says whether it would be created or already exists.',
186
+ }),
187
+ verify: core_1.Flags.boolean({
188
+ description: 'Read-only: report which nodes of this tree already exist (matched by externalKey) and which are missing, and write nothing. Exits 1 when any is missing or cannot be checked.',
189
+ exclusive: ['fresh', 'resume', 'dry-run', 'no-batch'],
190
+ }),
103
191
  'external-key-prefix': core_1.Flags.string({
104
192
  description: 'Generate externalKey for nodes that omit one; enables idempotent re-runs. Defaults to a prefix derived from the file content (see --fresh)',
105
193
  }),
@@ -110,11 +198,18 @@ class TasksCreateTree extends base_command_1.BaseCommand {
110
198
  description: 'Confirm existing stable-key nodes without patching them, then create only missing nodes. Use the unchanged keyed file after an incomplete result.',
111
199
  exclusive: ['fresh'],
112
200
  }),
201
+ 'no-batch': core_1.Flags.boolean({
202
+ description: 'Create one task per request instead of through the batch route. Slower; use it to rule the batch route out. Nothing else needs it: a batch that the server answers in part is continued automatically.',
203
+ }),
113
204
  'default-agent': core_1.Flags.string({
114
205
  description: 'Override the automatic assignee for nodes with no assignee. Use "auto" to inherit the space lead agent; children inherit parent assignees first.',
115
206
  }),
116
207
  space: core_1.Flags.string({ description: 'Default space ID for all nodes' }),
117
208
  goal: core_1.Flags.string({ description: 'Default goal ID for all nodes' }),
209
+ project: core_1.Flags.string({
210
+ description: "Default project for all nodes: its id, its ref (OSK-P-3) or the externalKey it was filed under. A ref or key is read inside the node's space, so give --space (or a spaceId on the nodes).",
211
+ }),
212
+ ...write_target_1.expectTargetFlag,
118
213
  };
119
214
  async run() {
120
215
  this.requireAuth();
@@ -140,49 +235,82 @@ class TasksCreateTree extends base_command_1.BaseCommand {
140
235
  ? undefined
141
236
  : derivedTreePrefix(input, topLevelDefaults);
142
237
  const keyPrefix = flags['external-key-prefix'] ?? derivedPrefix;
143
- const planned = planTree(roots, {
144
- defaults: topLevelDefaults,
145
- externalKeyPrefix: keyPrefix,
146
- });
238
+ let planned;
239
+ try {
240
+ planned = planTree(roots, {
241
+ defaults: topLevelDefaults,
242
+ externalKeyPrefix: keyPrefix,
243
+ });
244
+ }
245
+ catch (err) {
246
+ this.error(err.message, { exit: 1 });
247
+ }
248
+ // Only a node that actually took its key from the derived prefix makes that
249
+ // prefix worth a warning. A file whose every node names its own key (the
250
+ // shape an agent writes) was told about a prefix it never used.
251
+ const usedDerivedPrefix = Boolean(derivedPrefix) && planned.some((node) => node.keyDerived);
147
252
  const defaultAgentErrors = [];
253
+ const ordering = orderForCreation(planned);
254
+ defaultAgentErrors.push(...ordering.errors);
255
+ planned = ordering.ordered;
148
256
  const defaultAgentResolutions = {};
149
257
  const plannedByKey = new Map(planned.map((item) => [item.key, item]));
150
258
  const spaceCache = new Map();
151
259
  const parentTaskCache = new Map();
152
260
  const projectCache = new Map();
153
261
  const existingByKey = new Map();
262
+ // 1. Where does each node land? A parent or a project can decide the Space,
263
+ // and a key means a task only within its Space — so scope comes first.
264
+ const scopes = new Map();
154
265
  for (const item of planned) {
155
- let scope;
156
266
  try {
157
- scope = await (0, tasks_1.resolveTaskCreateScope)(item.payload, {
158
- parentTaskCache,
159
- projectCache,
160
- });
267
+ scopes.set(item.key, await (0, tasks_1.resolveTaskCreateScope)(item.payload, { parentTaskCache, projectCache }));
161
268
  }
162
269
  catch (err) {
163
270
  defaultAgentErrors.push(`${item.key}: ${err.message}`);
164
- continue;
165
271
  }
166
- let existingTask = null;
167
- if (typeof item.payload.externalKey === 'string' && item.payload.externalKey.trim()) {
168
- try {
169
- const existing = await (0, tasks_1.findTaskByExternalKey)({
170
- externalKey: item.payload.externalKey,
171
- spaceId: item.payload.spaceId,
172
- goalId: item.payload.goalId,
173
- });
174
- if (existing.duplicates.length > 0) {
175
- defaultAgentErrors.push(`${item.key}: externalKey ${item.payload.externalKey} matched multiple tasks`);
176
- continue;
177
- }
178
- existingTask = existing.task;
179
- }
180
- catch (err) {
181
- defaultAgentErrors.push(`${item.key}: ${err.message}`);
182
- continue;
183
- }
272
+ }
273
+ // 2. Which nodes already exist? One bulk read, not one request per node.
274
+ const lookups = planned
275
+ .filter((item) => scopes.has(item.key) &&
276
+ typeof item.payload.externalKey === 'string' &&
277
+ item.payload.externalKey.trim())
278
+ .map((item) => ({
279
+ key: item.key,
280
+ lookup: {
281
+ externalKey: item.payload.externalKey,
282
+ spaceId: item.payload.spaceId,
283
+ goalId: item.payload.goalId,
284
+ },
285
+ }));
286
+ const hits = await (0, tasks_1.findTasksByExternalKeys)(lookups.map((entry) => entry.lookup));
287
+ const unverifiable = [];
288
+ for (const { key, lookup } of lookups) {
289
+ const hit = hits.get((0, tasks_1.externalKeyLookupId)(lookup));
290
+ if (hit?.error) {
291
+ defaultAgentErrors.push(`${key}: ${hit.error.message}`);
292
+ unverifiable.push({ tempId: key, reason: hit.error.message });
184
293
  }
185
- existingByKey.set(item.key, existingTask);
294
+ else if (hit && hit.duplicates.length > 0) {
295
+ defaultAgentErrors.push(`${key}: externalKey ${lookup.externalKey} matched multiple tasks`);
296
+ unverifiable.push({ tempId: key, reason: 'externalKey matched multiple tasks' });
297
+ }
298
+ else {
299
+ existingByKey.set(key, hit?.task ?? null);
300
+ }
301
+ }
302
+ if (flags.verify) {
303
+ await this.verifyTree(planned, existingByKey, unverifiable, flags);
304
+ return;
305
+ }
306
+ // 3. Who owns each node? `me`, names and emails become ids first, so every
307
+ // decision below sees what will actually be written.
308
+ for (const item of planned) {
309
+ const scope = scopes.get(item.key);
310
+ if (!scope)
311
+ continue;
312
+ await this.resolveAssigneesInPayload(item.payload);
313
+ const existingTask = existingByKey.get(item.key) ?? null;
186
314
  const parentPayload = item.parentKey
187
315
  ? (existingByKey.get(item.parentKey) ?? plannedByKey.get(item.parentKey)?.payload)
188
316
  : scope.parentTask;
@@ -218,7 +346,10 @@ class TasksCreateTree extends base_command_1.BaseCommand {
218
346
  const errors = [
219
347
  ...defaultAgentErrors,
220
348
  ...(flags.resume && unkeyedNodes(planned).length
221
- ? ['--resume requires an externalKey for every node; supply --external-key-prefix and do not use --fresh'] : []),
349
+ ? [
350
+ '--resume requires an externalKey for every node; supply --external-key-prefix and do not use --fresh',
351
+ ]
352
+ : []),
222
353
  ...planned.flatMap(({ key, payload }) => (0, tasks_1.validateTaskCreatePayload)(payload).map((error) => `${key}: ${error}`)),
223
354
  ];
224
355
  if (errors.length > 0) {
@@ -230,6 +361,12 @@ class TasksCreateTree extends base_command_1.BaseCommand {
230
361
  this.exit(1);
231
362
  return;
232
363
  }
364
+ // An agent that would be put in charge by DEFAULT — the session's own agent,
365
+ // a parent's, the space lead — is the one assignment a person did not write
366
+ // and may not expect, and an agent with auto-execution may start work on it.
367
+ // Say so before anything is written, not only inside a dry run (OSK-S-440 #9).
368
+ const agentAssignment = describeDefaultAgentAssignment(defaultAgentResolutions);
369
+ const noteworthyAssignment = agentAssignment ? [agentAssignment] : [];
233
370
  if (flags['dry-run']) {
234
371
  const result = {
235
372
  object: 'task_create_tree_dry_run',
@@ -238,16 +375,23 @@ class TasksCreateTree extends base_command_1.BaseCommand {
238
375
  ...(() => {
239
376
  const dryWarnings = [
240
377
  rerunWarning(planned, keyPrefix),
241
- derivedPrefix ? derivedPrefixWarning(derivedPrefix) : undefined,
378
+ usedDerivedPrefix && derivedPrefix ? derivedPrefixWarning(derivedPrefix) : undefined,
379
+ ...noteworthyAssignment,
242
380
  ].filter((w) => w !== undefined);
243
381
  return dryWarnings.length > 0 ? { warnings: dryWarnings } : {};
244
382
  })(),
383
+ summary: {
384
+ create: planned.filter((item) => !existingByKey.get(item.key)).length,
385
+ [flags.resume ? 'existing' : 'update']: planned.filter((item) => existingByKey.get(item.key)).length,
386
+ },
245
387
  defaultAgents: defaultAgentResolutions,
246
388
  nodes: planned.map((item) => ({
247
389
  tempId: item.key,
248
390
  parentTempId: item.parentKey,
391
+ ...(item.blockerKeys?.length ? { blockedByTempIds: item.blockerKeys } : {}),
249
392
  title: item.payload.title,
250
393
  externalKey: item.payload.externalKey,
394
+ action: plannedAction(existingByKey.get(item.key), Boolean(flags.resume)),
251
395
  payload: item.payload,
252
396
  })),
253
397
  };
@@ -262,17 +406,27 @@ class TasksCreateTree extends base_command_1.BaseCommand {
262
406
  return;
263
407
  }
264
408
  const treeWarning = rerunWarning(planned, keyPrefix);
265
- const derivedWarning = derivedPrefix ? derivedPrefixWarning(derivedPrefix) : undefined;
409
+ const derivedWarning = usedDerivedPrefix && derivedPrefix ? derivedPrefixWarning(derivedPrefix) : undefined;
266
410
  const warnings = [
267
411
  ...(treeWarning ? [treeWarning] : []),
268
412
  ...(derivedWarning ? [derivedWarning] : []),
413
+ ...noteworthyAssignment,
269
414
  ];
270
415
  if (treeWarning && !flags.json)
271
416
  process.stderr.write(`Warning: ${treeWarning.hint}\n`);
272
417
  if (derivedWarning && !flags.json)
273
418
  process.stderr.write(`Note: ${derivedWarning.hint}\n`);
419
+ if (agentAssignment && process.stderr.isTTY) {
420
+ process.stderr.write(`Warning: ${agentAssignment.hint}\n`);
421
+ }
422
+ // Everything above only read. From here a request writes, so say where, and
423
+ // honour a caller's `--expect-target` before the first one.
424
+ const target = await this.assertWriteTarget(flags['expect-target']);
274
425
  const mapping = {};
275
426
  const results = [];
427
+ const startedAt = Date.now();
428
+ let rounds = 0;
429
+ const next = this.nextCommand(flags);
276
430
  // A half-applied tree reports the ids it did create; the only way back.
277
431
  const reportPartial = (item, failure, err, failures) => {
278
432
  if (results.length === 0 && !failures)
@@ -285,6 +439,7 @@ class TasksCreateTree extends base_command_1.BaseCommand {
285
439
  keyed: unkeyedNodes(planned).length === 0,
286
440
  warnings,
287
441
  failures,
442
+ next,
288
443
  failedAt: {
289
444
  tempId: item.key,
290
445
  ...(item.parentKey ? { parentTempId: item.parentKey } : {}),
@@ -293,7 +448,7 @@ class TasksCreateTree extends base_command_1.BaseCommand {
293
448
  },
294
449
  });
295
450
  if (flags.json) {
296
- this.log(JSON.stringify(partial, null, 2));
451
+ this.log(JSON.stringify({ ...partial, target }, null, 2));
297
452
  process.exitCode = partial.failedAt.error.retryable ? 5 : 1;
298
453
  this.exit(partial.failedAt.error.retryable ? 5 : 1);
299
454
  }
@@ -301,6 +456,8 @@ class TasksCreateTree extends base_command_1.BaseCommand {
301
456
  for (const [tempId, id] of Object.entries(mapping))
302
457
  this.log(`- ${tempId} -> ${id}`);
303
458
  this.log(partial.rerun);
459
+ if (partial.next)
460
+ this.log(`Next: ${partial.next}`);
304
461
  if (failures) {
305
462
  for (const row of failures) {
306
463
  const detail = row.error;
@@ -311,8 +468,23 @@ class TasksCreateTree extends base_command_1.BaseCommand {
311
468
  }
312
469
  this.handleApiError(err);
313
470
  };
471
+ // Blockers a node names by tempId, as the real ids the serial path needs.
472
+ const blockerIdsFor = (item) => {
473
+ const ids = [];
474
+ for (const blockerKey of item.blockerKeys ?? []) {
475
+ const id = mapping[blockerKey];
476
+ if (!id) {
477
+ this.error(`Blocker tempId "${blockerKey}" was not created before "${item.key}"`, {
478
+ exit: 1,
479
+ });
480
+ }
481
+ ids.push(id);
482
+ }
483
+ return ids;
484
+ };
314
485
  // The serial path: one request per node. Used for updates of existing keyed
315
- // nodes, and for every create against a server without the batch route.
486
+ // nodes, for every create against a server without the batch route, and for
487
+ // everything under --no-batch.
316
488
  const applySerial = async (item) => {
317
489
  const payload = { ...item.payload };
318
490
  if (item.parentKey) {
@@ -323,6 +495,7 @@ class TasksCreateTree extends base_command_1.BaseCommand {
323
495
  });
324
496
  }
325
497
  }
498
+ const blockerIds = blockerIdsFor(item);
326
499
  let action;
327
500
  let task;
328
501
  try {
@@ -331,6 +504,9 @@ class TasksCreateTree extends base_command_1.BaseCommand {
331
504
  if (existingTask) {
332
505
  action = 'updated';
333
506
  const patch = (0, tasks_1.removeUndefinedFields)((0, tasks_1.createPatchFromCreatePayload)(payload));
507
+ // Adding is idempotent, so a re-run never duplicates or drops an edge.
508
+ if (blockerIds.length > 0)
509
+ patch.addBlockedByIds = blockerIds;
334
510
  task = await data_provider_1.dataService.updateTask({
335
511
  taskId: (0, tasks_1.taskId)(existingTask),
336
512
  data: patch,
@@ -338,11 +514,27 @@ class TasksCreateTree extends base_command_1.BaseCommand {
338
514
  }
339
515
  else {
340
516
  action = 'created';
517
+ if (blockerIds.length > 0) {
518
+ payload.blockedByIds = [
519
+ ...new Set([
520
+ ...(payload.blockedByIds ?? []),
521
+ ...blockerIds,
522
+ ]),
523
+ ];
524
+ }
341
525
  task = await data_provider_1.dataService.createTask(payload);
342
526
  }
343
527
  }
344
528
  else {
345
529
  action = 'created';
530
+ if (blockerIds.length > 0) {
531
+ payload.blockedByIds = [
532
+ ...new Set([
533
+ ...(payload.blockedByIds ?? []),
534
+ ...blockerIds,
535
+ ]),
536
+ ];
537
+ }
346
538
  task = await data_provider_1.dataService.createTask(payload);
347
539
  }
348
540
  }
@@ -362,9 +554,19 @@ class TasksCreateTree extends base_command_1.BaseCommand {
362
554
  task,
363
555
  });
364
556
  };
365
- // New nodes go to POST /api/tasks/batch in chunks. `batchAvailable` flips off
366
- // the first time the server answers 404/405, and every later node is serial.
367
- let batchAvailable = true;
557
+ // A person watching sees the tree being built; anything capturing output
558
+ // sees nothing extra (the answer is the JSON / summary at the end).
559
+ const onProgress = process.stderr.isTTY
560
+ ? (p) => {
561
+ process.stderr.write(p.waiting
562
+ ? `Waiting ${Math.max(1, Math.round(p.waiting.ms / 1000))}s before trying again (${p.waiting.reason}); ${p.confirmed} of ${p.total} confirmed.\n`
563
+ : `Confirmed ${p.confirmed} of ${p.total} (request ${p.round}, ${Math.round(p.elapsedMs / 1000)}s).\n`);
564
+ }
565
+ : undefined;
566
+ // New nodes go to POST /api/tasks/batch, and the batch is driven until every
567
+ // one exists (createNodesViaBatch). `batchAvailable` flips off the first time
568
+ // the server answers 404/405, or under --no-batch, and every later node is serial.
569
+ let batchAvailable = !flags['no-batch'];
368
570
  let pending = [];
369
571
  const flushPending = async () => {
370
572
  const nodes = pending;
@@ -372,8 +574,14 @@ class TasksCreateTree extends base_command_1.BaseCommand {
372
574
  if (nodes.length === 0)
373
575
  return;
374
576
  if (batchAvailable) {
375
- const outcome = await (0, task_batch_1.createNodesViaBatch)(nodes.map((n) => ({ key: n.key, parentKey: n.parentKey, payload: n.payload })), mapping);
577
+ const outcome = await (0, task_batch_1.createNodesViaBatch)(nodes.map((n) => ({
578
+ key: n.key,
579
+ parentKey: n.parentKey,
580
+ blockerKeys: n.blockerKeys,
581
+ payload: n.payload,
582
+ })), mapping, { onProgress });
376
583
  if (outcome.supported) {
584
+ rounds += outcome.rounds;
377
585
  const byKey = new Map(nodes.map((n) => [n.key, n]));
378
586
  for (const row of outcome.rows) {
379
587
  const node = byKey.get(row.key);
@@ -399,7 +607,8 @@ class TasksCreateTree extends base_command_1.BaseCommand {
399
607
  ...(failure.error.code ? { code: failure.error.code } : {}),
400
608
  });
401
609
  reportPartial(node, failure.error, err, outcome.failures.map((f) => ({
402
- tempId: f.key, outcome: f.outcome ?? (f.skipped ? 'not_attempted' : 'failed'),
610
+ tempId: f.key,
611
+ outcome: f.outcome ?? (f.skipped ? 'not_attempted' : 'failed'),
403
612
  externalKey: f.externalKey ?? byKey.get(f.key)?.payload.externalKey,
404
613
  error: f.error,
405
614
  })));
@@ -419,8 +628,15 @@ class TasksCreateTree extends base_command_1.BaseCommand {
419
628
  if (flags.resume) {
420
629
  const id = (0, tasks_1.taskId)(existingTask);
421
630
  mapping[item.key] = id;
422
- results.push({ tempId: item.key, parentTempId: item.parentKey, id, action: 'existing',
423
- externalKey: item.payload.externalKey, title: existingTask.title ?? item.payload.title, task: existingTask });
631
+ results.push({
632
+ tempId: item.key,
633
+ parentTempId: item.parentKey,
634
+ id,
635
+ action: 'existing',
636
+ externalKey: item.payload.externalKey,
637
+ title: existingTask.title ?? item.payload.title,
638
+ task: existingTask,
639
+ });
424
640
  }
425
641
  else
426
642
  await applySerial(item);
@@ -436,6 +652,8 @@ class TasksCreateTree extends base_command_1.BaseCommand {
436
652
  ...(warnings.length > 0 ? { warnings } : {}),
437
653
  count: results.length,
438
654
  defaultAgents: defaultAgentResolutions,
655
+ target,
656
+ timing: { elapsedMs: Date.now() - startedAt, requests: rounds },
439
657
  mapping,
440
658
  results,
441
659
  };
@@ -449,8 +667,135 @@ class TasksCreateTree extends base_command_1.BaseCommand {
449
667
  this.log(`- ${item.tempId}: ${item.action} ${(0, tasks_1.formatTaskLabel)(task)}`);
450
668
  }
451
669
  }
670
+ /**
671
+ * `--verify`: which nodes of this tree exist, and which do not. Read-only by
672
+ * construction — it is reached before anything that writes — so it is safe to
673
+ * run at any time, as often as wanted, against a tree of any size.
674
+ */
675
+ async verifyTree(planned, existingByKey, unverifiable, flags) {
676
+ const cannotCheck = new Map(unverifiable.map((u) => [u.tempId, u.reason]));
677
+ for (const key of unkeyedNodes(planned)) {
678
+ if (!cannotCheck.has(key)) {
679
+ cannotCheck.set(key, 'no externalKey, so nothing can recognise it; give an externalKey or --external-key-prefix');
680
+ }
681
+ }
682
+ const existing = [];
683
+ const missing = [];
684
+ for (const item of planned) {
685
+ if (cannotCheck.has(item.key))
686
+ continue;
687
+ const task = existingByKey.get(item.key);
688
+ if (task) {
689
+ existing.push({
690
+ tempId: item.key,
691
+ id: (0, tasks_1.taskId)(task),
692
+ ...(typeof task.identifier === 'string' ? { identifier: task.identifier } : {}),
693
+ externalKey: item.payload.externalKey,
694
+ title: task.title ?? item.payload.title,
695
+ });
696
+ }
697
+ else {
698
+ missing.push({
699
+ tempId: item.key,
700
+ ...(item.parentKey ? { parentTempId: item.parentKey } : {}),
701
+ externalKey: item.payload.externalKey,
702
+ title: item.payload.title,
703
+ });
704
+ }
705
+ }
706
+ const ok = missing.length === 0 && cannotCheck.size === 0;
707
+ const result = {
708
+ object: 'task_create_tree_verification',
709
+ ok,
710
+ total: planned.length,
711
+ summary: {
712
+ existing: existing.length,
713
+ missing: missing.length,
714
+ unverifiable: cannotCheck.size,
715
+ },
716
+ existing,
717
+ missing,
718
+ unverifiable: [...cannotCheck].map(([tempId, reason]) => ({ tempId, reason })),
719
+ target: await this.describeWriteTarget(),
720
+ ...(missing.length > 0 ? { next: this.nextCommand(flags) } : {}),
721
+ };
722
+ if (flags.json) {
723
+ this.log(JSON.stringify(result, null, 2));
724
+ }
725
+ else {
726
+ this.log(`${existing.length} of ${planned.length} node(s) exist; ${missing.length} missing` +
727
+ (cannotCheck.size ? `; ${cannotCheck.size} cannot be checked` : '') +
728
+ '.');
729
+ for (const row of missing)
730
+ this.log(`- missing: ${String(row.tempId)} (${String(row.externalKey)})`);
731
+ for (const [tempId, reason] of cannotCheck)
732
+ this.log(`- cannot check: ${tempId} — ${reason}`);
733
+ if (result.next)
734
+ this.log(`Next: ${result.next}`);
735
+ }
736
+ if (!ok)
737
+ this.exit(1);
738
+ }
739
+ /**
740
+ * The command that continues this tree, as it should be typed: this
741
+ * invocation's own input and scope, plus `--resume`. Rebuilt from the parsed
742
+ * flags rather than echoed from argv, so it is the same whether the person
743
+ * typed `--space x` or `--space=x`, and so it can say plainly when the input
744
+ * came from stdin and cannot be replayed from a path.
745
+ */
746
+ nextCommand(flags) {
747
+ const quote = (value) => /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
748
+ const input = flags['from-json'];
749
+ const parts = [
750
+ this.config.bin,
751
+ 'tasks',
752
+ 'create-tree',
753
+ '--from-json',
754
+ input === '-' ? '<the same JSON on stdin>' : quote(String(input ?? '<file>')),
755
+ ];
756
+ for (const name of ['space', 'goal', 'project', 'external-key-prefix', 'default-agent']) {
757
+ const value = flags[name];
758
+ if (typeof value === 'string' && value)
759
+ parts.push(`--${name}`, quote(value));
760
+ }
761
+ for (const value of flags['expect-target'] ?? []) {
762
+ parts.push('--expect-target', quote(value));
763
+ }
764
+ if (flags.json)
765
+ parts.push('--json');
766
+ parts.push('--resume');
767
+ return parts.join(' ');
768
+ }
452
769
  }
453
770
  exports.default = TasksCreateTree;
771
+ /** What a dry run says will happen to a node: create it, update it, or leave it (`--resume`). */
772
+ function plannedAction(existing, resume) {
773
+ if (!existing)
774
+ return 'create';
775
+ return resume ? 'existing' : 'update';
776
+ }
777
+ /**
778
+ * The note that names an Agent put in charge of nodes by DEFAULT, or undefined
779
+ * when no node was. Sources a person wrote themselves (`explicit-assignee`,
780
+ * `provided` — an operator's own `--default-agent`) are not surprises; the
781
+ * session's agent, a parent's, or the space lead are.
782
+ */
783
+ function describeDefaultAgentAssignment(resolutions) {
784
+ const surprising = Object.values(resolutions).filter((r) => r?.applied &&
785
+ (r.agentIds?.length ?? 0) > 0 &&
786
+ (r.source === 'caller-agent' || r.source === 'parent' || r.source === 'space-lead'));
787
+ if (surprising.length === 0)
788
+ return undefined;
789
+ const agentIds = [...new Set(surprising.flatMap((r) => r.agentIds ?? []))];
790
+ const sources = [...new Set(surprising.map((r) => r.source))];
791
+ return {
792
+ code: 'DEFAULT_AGENT_ASSIGNED',
793
+ value: { count: surprising.length, agentIds, sources },
794
+ hint: `${surprising.length} node(s) will be assigned to agent ${agentIds.join(', ')} by default (${sources.join(', ')}), ` +
795
+ 'and a Space with auto-execution may start them. To keep them with you instead, put "executionMode":"human" and ' +
796
+ '"assigneeUserIds":["me"] on the nodes, or pass --default-agent with the assignee you mean.',
797
+ };
798
+ }
454
799
  function normalizeTreeRoots(input) {
455
800
  const root = input.root;
456
801
  const nodes = input.nodes ?? input.tasks;
@@ -486,6 +831,7 @@ function normalizeDefaults(input, flags) {
486
831
  ...defaults,
487
832
  spaceId: flags.space ?? defaults.spaceId,
488
833
  goalId: flags.goal ?? defaults.goalId,
834
+ projectId: flags.project ?? defaults.projectId,
489
835
  });
490
836
  }
491
837
  function planTree(roots, opts) {
@@ -497,17 +843,37 @@ function planTree(roots, opts) {
497
843
  throw new Error(`Duplicate tree tempId/path key: ${key}`);
498
844
  }
499
845
  seen.add(key);
846
+ const blockerKeys = readBlockerKeys(node, key);
500
847
  const payload = nodePayload(node, opts.defaults);
848
+ let keyDerived = false;
501
849
  if (!payload.externalKey && opts.externalKeyPrefix) {
502
850
  payload.externalKey = `${opts.externalKeyPrefix.replace(/\/+$/, '')}/${key}`;
851
+ keyDerived = true;
503
852
  }
504
- planned.push({ key, parentKey, node, payload });
853
+ planned.push({
854
+ key,
855
+ parentKey,
856
+ ...(blockerKeys.length ? { blockerKeys } : {}),
857
+ ...(keyDerived ? { keyDerived } : {}),
858
+ node,
859
+ payload,
860
+ });
505
861
  const children = Array.isArray(node.children) ? node.children : [];
506
862
  children.forEach((child, index) => visit(child, [...path, index], key));
507
863
  };
508
864
  roots.forEach((root, index) => visit(root, [index]));
509
865
  return planned;
510
866
  }
867
+ /** `blockedByTempIds` as a clean list of the tempIds a node names, or a precise refusal. */
868
+ function readBlockerKeys(node, key) {
869
+ const raw = node.blockedByTempIds;
870
+ if (raw === undefined)
871
+ return [];
872
+ if (!Array.isArray(raw) || raw.some((entry) => typeof entry !== 'string' || !entry.trim())) {
873
+ throw new Error(`Node "${key}": blockedByTempIds must be an array of tempIds`);
874
+ }
875
+ return [...new Set(raw.map((entry) => entry.trim()))];
876
+ }
511
877
  function nodeKey(node, path) {
512
878
  const temp = node.tempId ?? node.temp_id ?? node.id;
513
879
  if (typeof temp === 'string' && temp.trim())
@@ -515,6 +881,6 @@ function nodeKey(node, path) {
515
881
  return path.join('.');
516
882
  }
517
883
  function nodePayload(node, defaults) {
518
- const { children: _children, tempId: _tempId, temp_id: _tempIdSnake, id: _id, ...rest } = node;
884
+ const { children: _children, tempId: _tempId, temp_id: _tempIdSnake, id: _id, blockedByTempIds: _blockedByTempIds, ...rest } = node;
519
885
  return (0, tasks_1.buildTaskCreatePayload)({ ...defaults, ...rest }, {});
520
886
  }