@danypops/papyrus 0.41.0 → 0.42.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 (78) hide show
  1. package/README.md +8 -11
  2. package/package.json +2 -2
  3. package/src/adapters/sqlite-artifact-scope-store.ts +18 -9
  4. package/src/adapters/sqlite-artifact-store.ts +7 -5
  5. package/src/adapters/sqlite-discussion-round-store.ts +26 -17
  6. package/src/adapters/sqlite-gate-runner.ts +1 -1
  7. package/src/adapters/sqlite-graph-projection-store.ts +14 -10
  8. package/src/adapters/sqlite-log-store.ts +36 -17
  9. package/src/adapters/sqlite-note-event-store.ts +20 -16
  10. package/src/adapters/sqlite-session-identity-store.ts +13 -7
  11. package/src/adapters/sqlite-task-event-store.ts +29 -21
  12. package/src/adapters/sqlite-task-focus-store.ts +36 -10
  13. package/src/adapters/sqlite-task-lease-store.ts +12 -6
  14. package/src/adapters/sqlite-task-scope-store.ts +25 -14
  15. package/src/artifact-relationship-view.ts +4 -4
  16. package/src/artifact-subtree.ts +4 -2
  17. package/src/authority-registry.ts +2 -1
  18. package/src/cli.ts +794 -354
  19. package/src/client.ts +6 -3
  20. package/src/constants.ts +34 -56
  21. package/src/daemon-state.ts +4 -12
  22. package/src/daemon.ts +31 -9
  23. package/src/db.ts +153 -105
  24. package/src/discussion-service.ts +109 -44
  25. package/src/domain/artifact-event.ts +18 -5
  26. package/src/domain/artifact.ts +3 -1
  27. package/src/domain/blueprint-definition.ts +268 -0
  28. package/src/domain/checklist.ts +20 -17
  29. package/src/domain/discussion.ts +37 -18
  30. package/src/domain/gate.ts +7 -7
  31. package/src/domain/log-entry.ts +1 -1
  32. package/src/domain/note-event.ts +20 -7
  33. package/src/domain/task-event.ts +17 -7
  34. package/src/domain-services.ts +362 -288
  35. package/src/graph-projection-service.ts +34 -8
  36. package/src/id-migration.ts +17 -4
  37. package/src/index.ts +16 -11
  38. package/src/log-service.ts +6 -5
  39. package/src/log.ts +19 -0
  40. package/src/modules/discuss.ts +63 -28
  41. package/src/modules/docs.ts +74 -17
  42. package/src/modules/graph-projection.ts +20 -9
  43. package/src/modules/logs.ts +34 -22
  44. package/src/modules/notes.ts +66 -28
  45. package/src/modules/playbooks.ts +93 -32
  46. package/src/modules/rules.ts +57 -15
  47. package/src/modules/session-identity.ts +6 -2
  48. package/src/modules/tasks.ts +142 -67
  49. package/src/note-service.ts +11 -7
  50. package/src/ops.ts +134 -69
  51. package/src/playbook-definition.ts +124 -39
  52. package/src/playbook-execution.ts +18 -25
  53. package/src/ports/artifact-scope-store.ts +1 -1
  54. package/src/ports/note-event-store.ts +6 -4
  55. package/src/ports/task-event-store.ts +9 -6
  56. package/src/ports/task-focus-store.ts +17 -4
  57. package/src/ports/task-lease-store.ts +9 -4
  58. package/src/ports/task-scope-store.ts +3 -1
  59. package/src/service.ts +148 -110
  60. package/src/session-identity-service.ts +10 -2
  61. package/src/task-context.ts +28 -16
  62. package/src/task-execution.ts +4 -12
  63. package/src/task-graph-view.ts +12 -12
  64. package/src/task-relationship-view.ts +1 -3
  65. package/src/task-service.ts +168 -73
  66. package/src/vehicle/artifact-trash-vehicle.ts +26 -14
  67. package/src/vehicle/artifact-vehicle-shared.ts +32 -13
  68. package/src/vehicle/docs-vehicle.ts +50 -18
  69. package/src/vehicle/notes-vehicle.ts +26 -8
  70. package/src/vehicle/papyrus-vehicle.ts +16 -8
  71. package/src/vehicle/playbooks-vehicle.ts +88 -19
  72. package/src/vehicle/rules-vehicle.ts +58 -21
  73. package/src/vehicle/tasks-vehicle.ts +366 -54
  74. package/src/version.ts +1 -1
  75. package/src/workflow-execution.ts +198 -109
  76. package/src/domain/skill-definition.ts +0 -270
  77. package/src/modules/skills.ts +0 -158
  78. package/src/vehicle/skills-vehicle.ts +0 -194
@@ -21,20 +21,27 @@
21
21
  */
22
22
  import { bindVehicleOperation, defineVehicleOperation, type VehicleOperationContext } from "@danypops/vehicle-core";
23
23
  import type { VehicleRegistry } from "@danypops/vehicle-server";
24
- import type { TaskExecutionPlan } from "../task-execution.ts";
25
- import type { TaskCompletion, Tasks } from "../task-service.ts";
26
24
  import type { TaskViewMode } from "../domain/task-scope.ts";
27
25
  import { tasksOperations } from "../modules/tasks.ts";
28
26
  import type { ArtifactStore } from "../ports/artifact-store.ts";
29
27
  import type { SessionIdentity } from "../session-identity-service.ts";
30
- import { labelsById, looseObjectSchema, numberProp, passthroughOutput, resolveArtifactIdWidened, stringProp } from "./artifact-vehicle-shared.ts";
28
+ import type { TaskExecutionPlan } from "../task-execution.ts";
29
+ import type { TaskCompletion, Tasks } from "../task-service.ts";
30
+ import {
31
+ labelsById,
32
+ looseObjectSchema,
33
+ numberProp,
34
+ passthroughOutput,
35
+ resolveArtifactIdWidened,
36
+ stringProp,
37
+ } from "./artifact-vehicle-shared.ts";
31
38
 
32
39
  const OWNER = "tasks";
33
40
  const LIMITS = { defaultTimeoutMs: 5_000, maxTimeoutMs: 30_000, maxRequestBytes: 65_536, maxResponseBytes: 262_144 };
34
41
 
35
42
  const objectProp = { type: "object" } as unknown as { type: string };
36
43
  const arrayProp = { type: "array" } as unknown as { type: string };
37
- const boolProp = { type: "boolean" } as unknown as { type: string };
44
+ const _boolProp = { type: "boolean" } as unknown as { type: string };
38
45
 
39
46
  export interface TasksVehicleDeps {
40
47
  tasks: Tasks;
@@ -79,22 +86,40 @@ function resolveRootTaskId(tasks: Tasks, projectRoot: string | undefined, rootTa
79
86
  return resolveTaskId(tasks, { projectRoot, scope: "project" }, undefined, rootTaskName);
80
87
  }
81
88
 
82
- function resolveArrayField(tasks: Tasks, filter: { projectRoot?: string; scope?: TaskViewMode; rootTaskId?: string }, ids: unknown, names: unknown): string[] | undefined {
89
+ function resolveArrayField(
90
+ tasks: Tasks,
91
+ filter: { projectRoot?: string; scope?: TaskViewMode; rootTaskId?: string },
92
+ ids: unknown,
93
+ names: unknown,
94
+ ): string[] | undefined {
83
95
  if (Array.isArray(ids)) return ids as string[];
84
96
  if (!Array.isArray(names) || names.length === 0) return undefined;
85
97
  return names.map((entry) => resolveTaskId(tasks, filter, undefined, String(entry)));
86
98
  }
87
99
 
88
- const readSchemaProps = { status: stringProp, text: stringProp, limit: numberProp, project_root: stringProp, scope: { type: "string", enum: ["project", "graph", "all"] }, root_task_id: stringProp, root_task_name: stringProp, session_id: stringProp, labels: arrayProp };
100
+ const readSchemaProps = {
101
+ status: stringProp,
102
+ text: stringProp,
103
+ limit: numberProp,
104
+ project_root: stringProp,
105
+ scope: { type: "string", enum: ["project", "graph", "all"] },
106
+ root_task_id: stringProp,
107
+ root_task_name: stringProp,
108
+ session_id: stringProp,
109
+ labels: arrayProp,
110
+ };
89
111
 
90
112
  /** Same gate/checklist narrative lines the removed tool built client-side. */
91
113
  function completionContentText(labels: Map<string, string>, result: TaskCompletion): string {
92
114
  const gates = result.gates.map((gate) => `${gate.passed ? "✓" : "✗"} ${gate.gate.type}: ${gate.gate.target} — ${gate.output}`).join("\n");
93
- const checklist = result.checklist.map((item) => `${item.accepted ? "✓" : "✗"} proof: ${item.item}${item.reason ? ` — ${item.reason}` : ""}`).join("\n");
115
+ const checklist = result.checklist
116
+ .map((item) => `${item.accepted ? "✓" : "✗"} proof: ${item.item}${item.reason ? ` — ${item.reason}` : ""}`)
117
+ .join("\n");
94
118
  const focused = result.focused ? `\nActive: ${result.focused.title} (${result.focused.id})` : "";
95
- const blocked = result.blocked.length > 0
96
- ? `\nBlocked: ${result.blocked.map((entry) => `${entry.artifact.title} (${entry.artifact.id}) waits for ${entry.dependencyIds.map((id) => labels.get(id) ?? "unknown task").join(", ")}`).join("; ")}`
97
- : "";
119
+ const blocked =
120
+ result.blocked.length > 0
121
+ ? `\nBlocked: ${result.blocked.map((entry) => `${entry.artifact.title} (${entry.artifact.id}) waits for ${entry.dependencyIds.map((id) => labels.get(id) ?? "unknown task").join(", ")}`).join("; ")}`
122
+ : "";
98
123
  return `${result.completed ? "Completed" : "Rejected"}: ${result.artifact.title} (${result.artifact.id})${focused}${blocked}${checklist ? `\n${checklist}` : ""}${gates ? `\n${gates}` : ""}`;
99
124
  }
100
125
 
@@ -140,7 +165,14 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
140
165
  idempotency: { mode: effect === "read" ? "safe" : "unsafe" },
141
166
  limits: LIMITS,
142
167
  });
143
- registry.register(OWNER, bindVehicleOperation(operation, () => async (context) => (execute ?? ((input: Record<string, unknown>) => call(`tasks.${action}`, input)))(resolve(context.input), context)));
168
+ registry.register(
169
+ OWNER,
170
+ bindVehicleOperation(
171
+ operation,
172
+ () => async (context) =>
173
+ (execute ?? ((input: Record<string, unknown>) => call(`tasks.${action}`, input)))(resolve(context.input), context),
174
+ ),
175
+ );
144
176
  };
145
177
 
146
178
  /** Shared by every action taking a single id/name: resolves root_task_name first, then name -> id against the final scope. */
@@ -160,12 +192,32 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
160
192
  "create",
161
193
  "Creates a Task -- work: desired outcomes, gates, checklists, and dependencies. project_root is required (no ambient cwd server-side). Prefer parent_name/depends_on_names over parent_id/depends_on -- resolved server-side.",
162
194
  "local-write",
163
- { title: stringProp, body: stringProp, status: stringProp, labels: arrayProp, extra: objectProp, gates: arrayProp, checklist: objectProp, template_id: stringProp, parent_id: stringProp, parent_name: stringProp, depends_on: arrayProp, depends_on_names: arrayProp, project_root: stringProp, session_id: stringProp },
195
+ {
196
+ title: stringProp,
197
+ body: stringProp,
198
+ status: stringProp,
199
+ labels: arrayProp,
200
+ extra: objectProp,
201
+ gates: arrayProp,
202
+ checklist: objectProp,
203
+ template_id: stringProp,
204
+ parent_id: stringProp,
205
+ parent_name: stringProp,
206
+ depends_on: arrayProp,
207
+ depends_on_names: arrayProp,
208
+ project_root: stringProp,
209
+ session_id: stringProp,
210
+ },
164
211
  ["title", "project_root"],
165
212
  (input) => {
166
213
  const projectRoot = input.project_root as string;
167
214
  const filter = { projectRoot };
168
- const parentId = typeof input.parent_id === "string" && input.parent_id.length > 0 ? input.parent_id : (typeof input.parent_name === "string" && input.parent_name.length > 0 ? resolveTaskId(tasks, filter, undefined, input.parent_name) : undefined);
215
+ const parentId =
216
+ typeof input.parent_id === "string" && input.parent_id.length > 0
217
+ ? input.parent_id
218
+ : typeof input.parent_name === "string" && input.parent_name.length > 0
219
+ ? resolveTaskId(tasks, filter, undefined, input.parent_name)
220
+ : undefined;
169
221
  const dependsOn = resolveArrayField(tasks, filter, input.depends_on, input.depends_on_names);
170
222
  return { ...input, ...(parentId ? { parent_id: parentId } : {}), ...(dependsOn ? { depends_on: dependsOn } : {}) };
171
223
  },
@@ -175,15 +227,32 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
175
227
  "update",
176
228
  "Recovers an accidentally-terminal task via status=todo + reason, or changes title/body/labels, without rewriting real history. Never touches gates -- use set_gates.",
177
229
  "local-write",
178
- { id: stringProp, name: stringProp, title: stringProp, body: stringProp, labels: arrayProp, status: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp },
230
+ {
231
+ id: stringProp,
232
+ name: stringProp,
233
+ title: stringProp,
234
+ body: stringProp,
235
+ labels: arrayProp,
236
+ status: stringProp,
237
+ reason: stringProp,
238
+ session_id: stringProp,
239
+ project_root: stringProp,
240
+ },
179
241
  [],
180
242
  resolveIdAndScope,
181
243
  );
182
244
 
183
- define("list", "Lists Tasks matching an optional status/text/labels filter, scoped to project_root. project_root is required (no ambient cwd server-side).", "read", readSchemaProps, ["project_root"], (input) => {
184
- const rootTaskId = resolveRootTaskId(tasks, input.project_root as string, input.root_task_id, input.root_task_name);
185
- return { ...input, ...(rootTaskId ? { root_task_id: rootTaskId } : {}) };
186
- });
245
+ define(
246
+ "list",
247
+ "Lists Tasks matching an optional status/text/labels filter, scoped to project_root. project_root is required (no ambient cwd server-side).",
248
+ "read",
249
+ readSchemaProps,
250
+ ["project_root"],
251
+ (input) => {
252
+ const rootTaskId = resolveRootTaskId(tasks, input.project_root as string, input.root_task_id, input.root_task_name);
253
+ return { ...input, ...(rootTaskId ? { root_task_id: rootTaskId } : {}) };
254
+ },
255
+ );
187
256
 
188
257
  define(
189
258
  "graph",
@@ -213,24 +282,60 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
213
282
  },
214
283
  );
215
284
 
216
- define("show", "Shows one Task by id or title.", "read", { id: stringProp, name: stringProp, project_root: stringProp, scope: { type: "string", enum: ["project", "graph", "all"] }, root_task_id: stringProp, root_task_name: stringProp }, [], resolveIdAndScope);
285
+ define(
286
+ "show",
287
+ "Shows one Task by id or title.",
288
+ "read",
289
+ {
290
+ id: stringProp,
291
+ name: stringProp,
292
+ project_root: stringProp,
293
+ scope: { type: "string", enum: ["project", "graph", "all"] },
294
+ root_task_id: stringProp,
295
+ root_task_name: stringProp,
296
+ },
297
+ [],
298
+ resolveIdAndScope,
299
+ );
217
300
 
218
301
  define(
219
302
  "history",
220
303
  "Task's append-only lifecycle event history, cursor-paginated.",
221
304
  "read",
222
- { id: stringProp, name: stringProp, limit: numberProp, cursor: numberProp, direction: { type: "string", enum: ["asc", "desc"] }, project_root: stringProp, scope: { type: "string", enum: ["project", "graph", "all"] }, root_task_id: stringProp, root_task_name: stringProp },
305
+ {
306
+ id: stringProp,
307
+ name: stringProp,
308
+ limit: numberProp,
309
+ cursor: numberProp,
310
+ direction: { type: "string", enum: ["asc", "desc"] },
311
+ project_root: stringProp,
312
+ scope: { type: "string", enum: ["project", "graph", "all"] },
313
+ root_task_id: stringProp,
314
+ root_task_name: stringProp,
315
+ },
223
316
  [],
224
317
  resolveIdAndScope,
225
318
  );
226
319
 
227
- define("scope", "Describes the current task-view scope selection for project_root.", "read", { project_root: stringProp }, ["project_root"], (input) => input);
320
+ define(
321
+ "scope",
322
+ "Describes the current task-view scope selection for project_root.",
323
+ "read",
324
+ { project_root: stringProp },
325
+ ["project_root"],
326
+ (input) => input,
327
+ );
228
328
 
229
329
  define(
230
330
  "set_scope",
231
331
  "Sets the task-view scope (project/graph/all) for project_root, optionally pinned to root_task_id.",
232
332
  "local-write",
233
- { project_root: stringProp, scope: { type: "string", enum: ["project", "graph", "all"] }, root_task_id: stringProp, root_task_name: stringProp },
333
+ {
334
+ project_root: stringProp,
335
+ scope: { type: "string", enum: ["project", "graph", "all"] },
336
+ root_task_id: stringProp,
337
+ root_task_name: stringProp,
338
+ },
234
339
  ["project_root", "scope"],
235
340
  (input) => {
236
341
  const rootTaskId = resolveRootTaskId(tasks, input.project_root as string, input.root_task_id, input.root_task_name);
@@ -247,8 +352,22 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
247
352
  (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }),
248
353
  );
249
354
 
250
- define("active", "The current active Task (the one being worked on) for this scope. project_root is required.", "read", readSchemaProps, ["project_root"], (input) => input);
251
- define("focused", "The current focused Task and its focus status (focused/paused) for this session's scope. project_root is required.", "read", readSchemaProps, ["project_root"], (input) => input);
355
+ define(
356
+ "active",
357
+ "The current active Task (the one being worked on) for this scope. project_root is required.",
358
+ "read",
359
+ readSchemaProps,
360
+ ["project_root"],
361
+ (input) => input,
362
+ );
363
+ define(
364
+ "focused",
365
+ "The current focused Task and its focus status (focused/paused) for this session's scope. project_root is required.",
366
+ "read",
367
+ readSchemaProps,
368
+ ["project_root"],
369
+ (input) => input,
370
+ );
252
371
 
253
372
  const focusOperation = (
254
373
  action: "focus" | "pause" | "unpause" | "clear_focus",
@@ -268,28 +387,81 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
268
387
  idempotency: { mode: "unsafe" },
269
388
  limits: LIMITS,
270
389
  });
271
- registry.register(OWNER, bindVehicleOperation(operation, () => async (context) => {
272
- const claims = context.principal?.claims as { sessionId?: string; sessionSecret?: string } | undefined;
273
- return call(`tasks.${action}`, { ...resolve(context.input), session_id: claims?.sessionId, session_secret: claims?.sessionSecret });
274
- }));
390
+ registry.register(
391
+ OWNER,
392
+ bindVehicleOperation(operation, () => async (context) => {
393
+ const claims = context.principal?.claims as { sessionId?: string; sessionSecret?: string } | undefined;
394
+ return call(`tasks.${action}`, { ...resolve(context.input), session_id: claims?.sessionId, session_secret: claims?.sessionSecret });
395
+ }),
396
+ );
275
397
  };
276
398
 
277
- focusOperation("focus", "Sets the active Task Focus (singular per scope) to this Task. Multiple sessions can focus the same task while only one holds its lease.", { id: stringProp, name: stringProp, project_root: stringProp }, [], (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }));
399
+ focusOperation(
400
+ "focus",
401
+ "Sets the active Task Focus (singular per scope) to this Task. Multiple sessions can focus the same task while only one holds its lease.",
402
+ { id: stringProp, name: stringProp, project_root: stringProp },
403
+ [],
404
+ (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }),
405
+ );
278
406
  focusOperation("pause", "Pauses the active Task Focus without clearing it.", { reason: stringProp }, [], (input) => input);
279
407
  focusOperation("unpause", "Resumes a paused Task Focus.", {}, [], (input) => input);
280
408
  focusOperation("clear_focus", "Clears the active Task Focus.", {}, [], (input) => input);
281
409
 
282
- define("start", "Lifecycle transition: todo -> in-progress.", "local-write", { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp }, [], resolveIdAndScope);
283
- define("submit", "Lifecycle transition: in-progress -> review.", "local-write", { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp }, [], resolveIdAndScope);
284
- define("reject", "Lifecycle transition: review -> rejected.", "local-write", { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp }, [], resolveIdAndScope);
285
- define("retry", "Lifecycle transition: rejected -> in-progress.", "local-write", { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp }, [], resolveIdAndScope);
286
- define("cancel", "Lifecycle transition to canceled (terminal) from todo/in-progress/review/rejected.", "local-write", { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp }, [], resolveIdAndScope);
410
+ define(
411
+ "start",
412
+ "Lifecycle transition: todo -> in-progress.",
413
+ "local-write",
414
+ { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp },
415
+ [],
416
+ resolveIdAndScope,
417
+ );
418
+ define(
419
+ "submit",
420
+ "Lifecycle transition: in-progress -> review.",
421
+ "local-write",
422
+ { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp },
423
+ [],
424
+ resolveIdAndScope,
425
+ );
426
+ define(
427
+ "reject",
428
+ "Lifecycle transition: review -> rejected.",
429
+ "local-write",
430
+ { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp },
431
+ [],
432
+ resolveIdAndScope,
433
+ );
434
+ define(
435
+ "retry",
436
+ "Lifecycle transition: rejected -> in-progress.",
437
+ "local-write",
438
+ { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp },
439
+ [],
440
+ resolveIdAndScope,
441
+ );
442
+ define(
443
+ "cancel",
444
+ "Lifecycle transition to canceled (terminal) from todo/in-progress/review/rejected.",
445
+ "local-write",
446
+ { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp },
447
+ [],
448
+ resolveIdAndScope,
449
+ );
287
450
 
288
451
  define(
289
452
  "complete",
290
453
  "Runs gates + checklist-proof review, then focuses one deterministic ready successor without claiming effort. Rejects (not completes) on gate/checklist failure.",
291
454
  "local-write",
292
- { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp, scope: { type: "string", enum: ["project", "graph", "all"] }, root_task_id: stringProp, root_task_name: stringProp },
455
+ {
456
+ id: stringProp,
457
+ name: stringProp,
458
+ reason: stringProp,
459
+ session_id: stringProp,
460
+ project_root: stringProp,
461
+ scope: { type: "string", enum: ["project", "graph", "all"] },
462
+ root_task_id: stringProp,
463
+ root_task_name: stringProp,
464
+ },
293
465
  [],
294
466
  resolveIdAndScope,
295
467
  async (input) => {
@@ -304,18 +476,46 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
304
476
  "run_gates",
305
477
  "Runs a Task's configured gates without transitioning its status -- for checking readiness before submit/complete.",
306
478
  "read",
307
- { id: stringProp, name: stringProp, session_id: stringProp, project_root: stringProp, scope: { type: "string", enum: ["project", "graph", "all"] }, root_task_id: stringProp, root_task_name: stringProp },
479
+ {
480
+ id: stringProp,
481
+ name: stringProp,
482
+ session_id: stringProp,
483
+ project_root: stringProp,
484
+ scope: { type: "string", enum: ["project", "graph", "all"] },
485
+ root_task_id: stringProp,
486
+ root_task_name: stringProp,
487
+ },
308
488
  [],
309
489
  resolveIdAndScope,
310
490
  async (input) => {
311
- const gates = (await call("tasks.run_gates", input)) as Array<{ gate: { type: string; target: string }; passed: boolean; output: string }>;
312
- const text = gates.map((gate) => `${gate.passed ? "✓" : "✗"} ${gate.gate.type}: ${gate.gate.target} — ${gate.output}`).join("\n") || "No gates configured.";
491
+ const gates = (await call("tasks.run_gates", input)) as Array<{
492
+ gate: { type: string; target: string };
493
+ passed: boolean;
494
+ output: string;
495
+ }>;
496
+ const text =
497
+ gates.map((gate) => `${gate.passed ? "✓" : "✗"} ${gate.gate.type}: ${gate.gate.target} — ${gate.output}`).join("\n") ||
498
+ "No gates configured.";
313
499
  return { gates, content: [{ type: "text" as const, text }] };
314
500
  },
315
501
  );
316
502
 
317
- define("set_checklist", "Replaces a Task's evidence-bearing checklist (proof requirements) in full.", "local-write", { id: stringProp, name: stringProp, checklist: objectProp, project_root: stringProp }, ["checklist"], resolveIdAndScope);
318
- define("set_gates", "Replaces a Task's gate commands in full.", "local-write", { id: stringProp, name: stringProp, gates: arrayProp, project_root: stringProp }, ["gates"], resolveIdAndScope);
503
+ define(
504
+ "set_checklist",
505
+ "Replaces a Task's evidence-bearing checklist (proof requirements) in full.",
506
+ "local-write",
507
+ { id: stringProp, name: stringProp, checklist: objectProp, project_root: stringProp },
508
+ ["checklist"],
509
+ resolveIdAndScope,
510
+ );
511
+ define(
512
+ "set_gates",
513
+ "Replaces a Task's gate commands in full.",
514
+ "local-write",
515
+ { id: stringProp, name: stringProp, gates: arrayProp, project_root: stringProp },
516
+ ["gates"],
517
+ resolveIdAndScope,
518
+ );
319
519
 
320
520
  define(
321
521
  "context",
@@ -335,7 +535,16 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
335
535
  "cancel_subtree",
336
536
  "Cancels a Task and its whole containment subtree in one call, skipping tasks already done/canceled.",
337
537
  "local-write",
338
- { id: stringProp, name: stringProp, reason: stringProp, session_id: stringProp, project_root: stringProp, scope: { type: "string", enum: ["project", "graph", "all"] }, root_task_id: stringProp, root_task_name: stringProp },
538
+ {
539
+ id: stringProp,
540
+ name: stringProp,
541
+ reason: stringProp,
542
+ session_id: stringProp,
543
+ project_root: stringProp,
544
+ scope: { type: "string", enum: ["project", "graph", "all"] },
545
+ root_task_id: stringProp,
546
+ root_task_name: stringProp,
547
+ },
339
548
  [],
340
549
  resolveIdAndScope,
341
550
  (input) => {
@@ -351,11 +560,23 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
351
560
  "depend",
352
561
  "Adds a dependency edge (this task waits for dependency_id/dependency_name). Dependency edges form an executable DAG -- self-dependencies and cycles are rejected. A name resolved outside project_root's own scope is retried once against every project before failing, unless scope is pinned explicitly.",
353
562
  "local-write",
354
- { id: stringProp, name: stringProp, dependency_id: stringProp, dependency_name: stringProp, project_root: stringProp, scope: scopeProp, session_id: stringProp },
563
+ {
564
+ id: stringProp,
565
+ name: stringProp,
566
+ dependency_id: stringProp,
567
+ dependency_name: stringProp,
568
+ project_root: stringProp,
569
+ scope: scopeProp,
570
+ session_id: stringProp,
571
+ },
355
572
  [],
356
573
  (input) => {
357
574
  const filter = { projectRoot: input.project_root as string | undefined, scope: input.scope as TaskViewMode | undefined };
358
- return { ...input, id: resolveTaskId(tasks, filter, input.id, input.name), dependency_id: resolveTaskId(tasks, filter, input.dependency_id, input.dependency_name) };
575
+ return {
576
+ ...input,
577
+ id: resolveTaskId(tasks, filter, input.id, input.name),
578
+ dependency_id: resolveTaskId(tasks, filter, input.dependency_id, input.dependency_name),
579
+ };
359
580
  },
360
581
  );
361
582
 
@@ -363,11 +584,23 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
363
584
  "undepend",
364
585
  "Removes a dependency edge. Idempotent -- a no-op if the edge is already absent.",
365
586
  "local-write",
366
- { id: stringProp, name: stringProp, dependency_id: stringProp, dependency_name: stringProp, project_root: stringProp, scope: scopeProp, session_id: stringProp },
587
+ {
588
+ id: stringProp,
589
+ name: stringProp,
590
+ dependency_id: stringProp,
591
+ dependency_name: stringProp,
592
+ project_root: stringProp,
593
+ scope: scopeProp,
594
+ session_id: stringProp,
595
+ },
367
596
  [],
368
597
  (input) => {
369
598
  const filter = { projectRoot: input.project_root as string | undefined, scope: input.scope as TaskViewMode | undefined };
370
- return { ...input, id: resolveTaskId(tasks, filter, input.id, input.name), dependency_id: resolveTaskId(tasks, filter, input.dependency_id, input.dependency_name) };
599
+ return {
600
+ ...input,
601
+ id: resolveTaskId(tasks, filter, input.id, input.name),
602
+ dependency_id: resolveTaskId(tasks, filter, input.dependency_id, input.dependency_name),
603
+ };
371
604
  },
372
605
  );
373
606
 
@@ -375,11 +608,23 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
375
608
  "contain",
376
609
  "Nests a child Task inside a parent (parent_id/parent_name contains child_id/child_name) -- explicit hierarchy, distinct from depends_on execution ordering. A name resolved outside project_root's own scope is retried once against every project before failing, unless scope is pinned explicitly.",
377
610
  "local-write",
378
- { parent_id: stringProp, parent_name: stringProp, child_id: stringProp, child_name: stringProp, project_root: stringProp, scope: scopeProp, session_id: stringProp },
611
+ {
612
+ parent_id: stringProp,
613
+ parent_name: stringProp,
614
+ child_id: stringProp,
615
+ child_name: stringProp,
616
+ project_root: stringProp,
617
+ scope: scopeProp,
618
+ session_id: stringProp,
619
+ },
379
620
  [],
380
621
  (input) => {
381
622
  const filter = { projectRoot: input.project_root as string | undefined, scope: input.scope as TaskViewMode | undefined };
382
- return { ...input, parent_id: resolveTaskId(tasks, filter, input.parent_id, input.parent_name), child_id: resolveTaskId(tasks, filter, input.child_id, input.child_name) };
623
+ return {
624
+ ...input,
625
+ parent_id: resolveTaskId(tasks, filter, input.parent_id, input.parent_name),
626
+ child_id: resolveTaskId(tasks, filter, input.child_id, input.child_name),
627
+ };
383
628
  },
384
629
  );
385
630
 
@@ -387,20 +632,87 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
387
632
  "uncontain",
388
633
  "Removes a parent/child nesting. Idempotent -- a no-op if the edge is already absent.",
389
634
  "local-write",
390
- { parent_id: stringProp, parent_name: stringProp, child_id: stringProp, child_name: stringProp, project_root: stringProp, scope: scopeProp, session_id: stringProp },
635
+ {
636
+ parent_id: stringProp,
637
+ parent_name: stringProp,
638
+ child_id: stringProp,
639
+ child_name: stringProp,
640
+ project_root: stringProp,
641
+ scope: scopeProp,
642
+ session_id: stringProp,
643
+ },
391
644
  [],
392
645
  (input) => {
393
646
  const filter = { projectRoot: input.project_root as string | undefined, scope: input.scope as TaskViewMode | undefined };
394
- return { ...input, parent_id: resolveTaskId(tasks, filter, input.parent_id, input.parent_name), child_id: resolveTaskId(tasks, filter, input.child_id, input.child_name) };
647
+ return {
648
+ ...input,
649
+ parent_id: resolveTaskId(tasks, filter, input.parent_id, input.parent_name),
650
+ child_id: resolveTaskId(tasks, filter, input.child_id, input.child_name),
651
+ };
395
652
  },
396
653
  );
397
654
 
398
- define("claim", "Claims this Task's lease under owner (defaults to session_id). Throws if a different owner already holds one.", "local-write", { id: stringProp, name: stringProp, owner: stringProp, ttl_ms: numberProp, note: stringProp, project_root: stringProp, session_id: stringProp }, [], (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name), owner: input.owner ?? input.session_id }));
399
- define("heartbeat_lease", "Extends this Task's lease -- needs the exact owner/token claim() returned.", "local-write", { id: stringProp, name: stringProp, owner: stringProp, token: stringProp, ttl_ms: numberProp, project_root: stringProp, session_id: stringProp }, ["owner", "token"], (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }));
400
- define("release_lease", "Releases this Task's lease -- needs the exact owner/token claim() returned.", "local-write", { id: stringProp, name: stringProp, owner: stringProp, token: stringProp, project_root: stringProp, session_id: stringProp }, ["owner", "token"], (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }));
401
- define("lease", "Shows this Task's current lease, if any.", "read", { id: stringProp, name: stringProp, project_root: stringProp }, [], (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }));
655
+ define(
656
+ "claim",
657
+ "Claims this Task's lease under owner (defaults to session_id). Throws if a different owner already holds one.",
658
+ "local-write",
659
+ {
660
+ id: stringProp,
661
+ name: stringProp,
662
+ owner: stringProp,
663
+ ttl_ms: numberProp,
664
+ note: stringProp,
665
+ project_root: stringProp,
666
+ session_id: stringProp,
667
+ },
668
+ [],
669
+ (input) => ({
670
+ ...input,
671
+ id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name),
672
+ owner: input.owner ?? input.session_id,
673
+ }),
674
+ );
675
+ define(
676
+ "heartbeat_lease",
677
+ "Extends this Task's lease -- needs the exact owner/token claim() returned.",
678
+ "local-write",
679
+ {
680
+ id: stringProp,
681
+ name: stringProp,
682
+ owner: stringProp,
683
+ token: stringProp,
684
+ ttl_ms: numberProp,
685
+ project_root: stringProp,
686
+ session_id: stringProp,
687
+ },
688
+ ["owner", "token"],
689
+ (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }),
690
+ );
691
+ define(
692
+ "release_lease",
693
+ "Releases this Task's lease -- needs the exact owner/token claim() returned.",
694
+ "local-write",
695
+ { id: stringProp, name: stringProp, owner: stringProp, token: stringProp, project_root: stringProp, session_id: stringProp },
696
+ ["owner", "token"],
697
+ (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }),
698
+ );
699
+ define(
700
+ "lease",
701
+ "Shows this Task's current lease, if any.",
702
+ "read",
703
+ { id: stringProp, name: stringProp, project_root: stringProp },
704
+ [],
705
+ (input) => ({ ...input, id: resolveTaskId(tasks, { projectRoot: input.project_root as string | undefined }, input.id, input.name) }),
706
+ );
402
707
 
403
- define("event_feed", "Cursor-paginated feed of raw Task lifecycle events across every task, optionally filtered by event_types.", "read", { cursor: numberProp, limit: numberProp, event_types: arrayProp }, [], (input) => input);
708
+ define(
709
+ "event_feed",
710
+ "Cursor-paginated feed of raw Task lifecycle events across every task, optionally filtered by event_types.",
711
+ "read",
712
+ { cursor: numberProp, limit: numberProp, event_types: arrayProp },
713
+ [],
714
+ (input) => input,
715
+ );
404
716
  }
405
717
 
406
718
  /** Kept out of the registry deliberately, matching the removed tool's own ACTIONS list -- system maintenance, not an agent-facing action. Exposed via reapStale* CLI/cron paths, not a Vehicle operation. */
package/src/version.ts CHANGED
@@ -5,7 +5,7 @@ function packageVersion(): string {
5
5
  if (typeof manifest !== "object" || manifest === null || Array.isArray(manifest)) {
6
6
  throw new Error("Papyrus package manifest must be an object");
7
7
  }
8
- const version = (manifest as Record<string, unknown>)["version"];
8
+ const version = (manifest as Record<string, unknown>).version;
9
9
  if (typeof version !== "string" || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
10
10
  throw new Error("Papyrus package manifest has an invalid version");
11
11
  }