@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
@@ -1,18 +1,23 @@
1
1
  /**
2
2
  * playbook-execution.ts — playbooks.invoke's real implementation: compile the Playbook's
3
- * composition tree (playbook-definition.ts) into a SkillDefinition, materialize it through
3
+ * composition tree (playbook-definition.ts) into a BlueprintDefinition, materialize it through
4
4
  * workflow-execution.ts's shared engine, mirror any pre-existing Rule/Doc links onto the
5
5
  * generated root task, and report which task to focus. No text is rendered here -- every
6
6
  * step is its own Task, and only the currently-focused one is ever surfaced to an agent
7
7
  * (via the existing Task Focus system-prompt pointer), which is what actually avoids the
8
8
  * old text-dump problem: one page at a time, not the whole book at once.
9
9
  */
10
- import type { SkillArgumentValue } from "./domain/skill-definition.ts";
10
+ import type { BlueprintArgumentValue } from "./domain/blueprint-definition.ts";
11
11
  import { compilePlaybookDefinition } from "./playbook-definition.ts";
12
12
  import type { ArtifactStore } from "./ports/artifact-store.ts";
13
13
  import { requireAtomicArtifactStore } from "./ports/atomic-artifact-store.ts";
14
- import { materializeWorkflowDefinition, type WorkflowRunHistory } from "./workflow-execution.ts";
15
14
  import type { TaskExecutionPlan } from "./task-execution.ts";
15
+ import {
16
+ applyPlaybookExternalLinks,
17
+ materializeWorkflowDefinition,
18
+ resolveRefToTaskId,
19
+ type WorkflowRunHistory,
20
+ } from "./workflow-execution.ts";
16
21
 
17
22
  export interface InvokePlaybookInput {
18
23
  runId?: string;
@@ -22,7 +27,7 @@ export interface InvokePlaybookInput {
22
27
  export interface PlaybookInvocationResult {
23
28
  playbookId: string;
24
29
  runId: string;
25
- arguments: Record<string, SkillArgumentValue>;
30
+ arguments: Record<string, BlueprintArgumentValue>;
26
31
  created: { docs: string[]; rules: string[]; tasks: string[] };
27
32
  rootTaskIds: string[];
28
33
  /** The one task to focus -- the first real leaf in the whole composition tree's reading order (deepest prerequisite's own first step, or this playbook's own first step, or its first nested child's, or the container task itself when there is nothing else). */
@@ -36,24 +41,15 @@ export interface PlaybookMissingArguments {
36
41
  missingArguments: string[];
37
42
  }
38
43
 
39
- function missingRequiredInputs(inputs: Record<string, { required?: boolean; default?: SkillArgumentValue }>, provided: Record<string, unknown>): string[] {
44
+ function missingRequiredInputs(
45
+ inputs: Record<string, { required?: boolean; default?: BlueprintArgumentValue }>,
46
+ provided: Record<string, unknown>,
47
+ ): string[] {
40
48
  return Object.entries(inputs)
41
49
  .filter(([name, input]) => input.required && provided[name] === undefined && input.default === undefined)
42
50
  .map(([name]) => name);
43
51
  }
44
52
 
45
- /** Reads each newly-created task's own playbookRun.ref tag back off the store -- avoids threading an extra ref-to-id map out of materializeWorkflowDefinition's existing, already-stable return shape. */
46
- function resolveRefToTaskId(artifacts: ArtifactStore, taskIds: string[]): Map<string, string> {
47
- const map = new Map<string, string>();
48
- for (const taskId of taskIds) {
49
- const lineage = artifacts.get(taskId)?.extra["playbookRun"];
50
- if (typeof lineage !== "object" || lineage === null || Array.isArray(lineage)) continue;
51
- const ref = (lineage as Record<string, unknown>)["ref"];
52
- if (typeof ref === "string") map.set(ref, taskId);
53
- }
54
- return map;
55
- }
56
-
57
53
  export function invokePlaybook(
58
54
  artifacts: ArtifactStore,
59
55
  playbookId: string,
@@ -64,7 +60,9 @@ export function invokePlaybook(
64
60
  const missingArguments = missingRequiredInputs(compiled.definition.inputs, input.arguments ?? {});
65
61
  if (missingArguments.length > 0) return { playbookId, missingArguments };
66
62
 
67
- const atomic = history ? history.events.atomic.bind(history.events) : requireAtomicArtifactStore(artifacts).atomic.bind(requireAtomicArtifactStore(artifacts));
63
+ const atomic = history
64
+ ? history.events.atomic.bind(history.events)
65
+ : requireAtomicArtifactStore(artifacts).atomic.bind(requireAtomicArtifactStore(artifacts));
68
66
  return atomic(() => {
69
67
  const result = materializeWorkflowDefinition(
70
68
  artifacts,
@@ -75,13 +73,8 @@ export function invokePlaybook(
75
73
  new Set(),
76
74
  0,
77
75
  );
78
- const refToTaskId = resolveRefToTaskId(artifacts, result.created.tasks);
79
- for (const link of compiled.externalLinks) {
80
- const taskId = refToTaskId.get(link.rootRef);
81
- if (!taskId) continue; // defensive -- every rootRef this compiler emits is always materialized
82
- if (link.ownerIsFrom) artifacts.link({ from: taskId, relation: link.relation, to: link.otherArtifactId });
83
- else artifacts.link({ from: link.otherArtifactId, relation: link.relation, to: taskId });
84
- }
76
+ const refToTaskId = resolveRefToTaskId(artifacts, result.created.tasks, "playbookRun");
77
+ applyPlaybookExternalLinks(artifacts, compiled.externalLinks, refToTaskId);
85
78
  return {
86
79
  playbookId,
87
80
  runId: result.runId,
@@ -1,7 +1,7 @@
1
1
  import type { TaskScopeSource } from "../domain/task-scope.ts";
2
2
 
3
3
  /**
4
- * Project scoping for Docs/Rules/Skills, mirroring TaskScopeStore's shape (task_scopes) but
4
+ * Project scoping for Docs/Rules/Playbooks, mirroring TaskScopeStore's shape (task_scopes) but
5
5
  * kept as its own table/port rather than folding non-Task kinds into Task-named
6
6
  * infrastructure. TaskScopeSource ("cwd" | "explicit" | "unscoped") is already kind-agnostic
7
7
  * and reused as-is -- no reason to redefine the same three values under a new name.
@@ -1,10 +1,10 @@
1
1
  import {
2
- normalizeNoteHistoryQuery,
3
- validateNoteEvent,
4
2
  type AppendNoteEvent,
5
3
  type NoteEvent,
6
4
  type NoteHistoryPage,
7
5
  type NoteHistoryQuery,
6
+ normalizeNoteHistoryQuery,
7
+ validateNoteEvent,
8
8
  } from "../domain/note-event.ts";
9
9
 
10
10
  export interface NoteEventStore {
@@ -30,8 +30,10 @@ export class InMemoryNoteEventStore implements NoteEventStore {
30
30
  history(noteId: string, query: NoteHistoryQuery = {}): NoteHistoryPage {
31
31
  const { direction, limit, cursor } = normalizeNoteHistoryQuery(query);
32
32
  const ordered = this.events
33
- .filter((event) => event.noteId === noteId && (cursor === undefined || (direction === "desc" ? event.id < cursor : event.id > cursor)))
34
- .sort((left, right) => direction === "desc" ? right.id - left.id : left.id - right.id);
33
+ .filter(
34
+ (event) => event.noteId === noteId && (cursor === undefined || (direction === "desc" ? event.id < cursor : event.id > cursor)),
35
+ )
36
+ .sort((left, right) => (direction === "desc" ? right.id - left.id : left.id - right.id));
35
37
  const events = ordered.slice(0, limit);
36
38
  return { events, ...(ordered.length > limit ? { nextCursor: events.at(-1)!.id } : {}) };
37
39
  }
@@ -1,13 +1,13 @@
1
1
  import {
2
+ type AppendTaskEvent,
2
3
  normalizeTaskEventFeedQuery,
3
4
  normalizeTaskHistoryQuery,
4
- validateTaskEvent,
5
- type AppendTaskEvent,
6
5
  type TaskEvent,
7
6
  type TaskEventFeedPage,
8
7
  type TaskEventFeedQuery,
9
8
  type TaskHistoryPage,
10
9
  type TaskHistoryQuery,
10
+ validateTaskEvent,
11
11
  } from "../domain/task-event.ts";
12
12
 
13
13
  export interface TaskEventStore {
@@ -25,8 +25,9 @@ export class InMemoryTaskEventStore implements TaskEventStore {
25
25
  atomic<T>(operation: () => T): T {
26
26
  const length = this.events.length;
27
27
  const nextId = this.nextId;
28
- try { return operation(); }
29
- catch (error) {
28
+ try {
29
+ return operation();
30
+ } catch (error) {
30
31
  this.events.length = length;
31
32
  this.nextId = nextId;
32
33
  throw error;
@@ -47,8 +48,10 @@ export class InMemoryTaskEventStore implements TaskEventStore {
47
48
  history(taskId: string, query: TaskHistoryQuery = {}): TaskHistoryPage {
48
49
  const { direction, limit, cursor } = normalizeTaskHistoryQuery(query);
49
50
  const ordered = this.events
50
- .filter((event) => event.taskId === taskId && (cursor === undefined || (direction === "desc" ? event.id < cursor : event.id > cursor)))
51
- .sort((left, right) => direction === "desc" ? right.id - left.id : left.id - right.id);
51
+ .filter(
52
+ (event) => event.taskId === taskId && (cursor === undefined || (direction === "desc" ? event.id < cursor : event.id > cursor)),
53
+ )
54
+ .sort((left, right) => (direction === "desc" ? right.id - left.id : left.id - right.id));
52
55
  const events = ordered.slice(0, limit);
53
56
  return { events, ...(ordered.length > limit ? { nextCursor: events.at(-1)!.id } : {}) };
54
57
  }
@@ -37,7 +37,9 @@ export interface TaskFocusStore {
37
37
  export class InMemoryTaskFocusStore implements TaskFocusStore {
38
38
  private readonly state = new Map<string, TaskFocusState>();
39
39
 
40
- get(scope?: string): TaskFocusState | undefined { return this.state.get(normalizeFocusScope(scope)); }
40
+ get(scope?: string): TaskFocusState | undefined {
41
+ return this.state.get(normalizeFocusScope(scope));
42
+ }
41
43
 
42
44
  set(taskId: string, scope?: string): TaskFocusState {
43
45
  const key = normalizeFocusScope(scope);
@@ -51,7 +53,12 @@ export class InMemoryTaskFocusStore implements TaskFocusStore {
51
53
  const key = normalizeFocusScope(scope);
52
54
  const current = this.state.get(key);
53
55
  if (current?.taskId !== taskId) throw new Error(`task "${taskId}" is not focused`);
54
- const focus: TaskFocusState = { ...current, status: "paused", updatedAt: new Date().toISOString(), ...(reason ? { pauseReason: reason } : {}) };
56
+ const focus: TaskFocusState = {
57
+ ...current,
58
+ status: "paused",
59
+ updatedAt: new Date().toISOString(),
60
+ ...(reason ? { pauseReason: reason } : {}),
61
+ };
55
62
  this.state.set(key, focus);
56
63
  return focus;
57
64
  }
@@ -79,7 +86,10 @@ export class InMemoryTaskFocusStore implements TaskFocusStore {
79
86
  reapStale(olderThanIso: string): number {
80
87
  let removed = 0;
81
88
  for (const [key, focus] of this.state) {
82
- if (focus.updatedAt < olderThanIso) { this.state.delete(key); removed++; }
89
+ if (focus.updatedAt < olderThanIso) {
90
+ this.state.delete(key);
91
+ removed++;
92
+ }
83
93
  }
84
94
  return removed;
85
95
  }
@@ -88,7 +98,10 @@ export class InMemoryTaskFocusStore implements TaskFocusStore {
88
98
  let oldestKey: string | undefined;
89
99
  let oldestAt: string | undefined;
90
100
  for (const [key, focus] of this.state) {
91
- if (oldestAt === undefined || focus.updatedAt < oldestAt) { oldestKey = key; oldestAt = focus.updatedAt; }
101
+ if (oldestAt === undefined || focus.updatedAt < oldestAt) {
102
+ oldestKey = key;
103
+ oldestAt = focus.updatedAt;
104
+ }
92
105
  }
93
106
  if (oldestKey !== undefined) this.state.delete(oldestKey);
94
107
  }
@@ -1,5 +1,5 @@
1
1
  import { TASK_LEASE_DEFAULT_TTL_MS } from "../constants.ts";
2
- import { isLeaseExpired, validateLeaseNote, validateLeaseOwner, validateLeaseTtlMs, type TaskLease } from "../domain/task-lease.ts";
2
+ import { isLeaseExpired, type TaskLease, validateLeaseNote, validateLeaseOwner, validateLeaseTtlMs } from "../domain/task-lease.ts";
3
3
 
4
4
  export interface TaskLeaseStore {
5
5
  /**
@@ -51,7 +51,8 @@ export class InMemoryTaskLeaseStore implements TaskLeaseStore {
51
51
  const nowIso = new Date().toISOString();
52
52
  const current = this.leases.get(taskId);
53
53
  if (!current || isLeaseExpired(current, nowIso)) throw new Error(`task "${taskId}" has no live lease to renew`);
54
- if (current.owner !== owner || current.token !== token) throw new Error(`lease for task "${taskId}" is held by a different owner/token`);
54
+ if (current.owner !== owner || current.token !== token)
55
+ throw new Error(`lease for task "${taskId}" is held by a different owner/token`);
55
56
  const renewed: TaskLease = { ...current, leaseExpiresAt: new Date(Date.now() + ttlMs).toISOString(), heartbeatAt: nowIso };
56
57
  this.leases.set(taskId, renewed);
57
58
  return renewed;
@@ -61,7 +62,8 @@ export class InMemoryTaskLeaseStore implements TaskLeaseStore {
61
62
  const nowIso = new Date().toISOString();
62
63
  const current = this.leases.get(taskId);
63
64
  if (!current || isLeaseExpired(current, nowIso)) return { released: false };
64
- if (current.owner !== owner || current.token !== token) throw new Error(`lease for task "${taskId}" is held by a different owner/token`);
65
+ if (current.owner !== owner || current.token !== token)
66
+ throw new Error(`lease for task "${taskId}" is held by a different owner/token`);
65
67
  this.leases.delete(taskId);
66
68
  return { released: true };
67
69
  }
@@ -75,7 +77,10 @@ export class InMemoryTaskLeaseStore implements TaskLeaseStore {
75
77
  reapExpired(olderThanIso: string): number {
76
78
  let removed = 0;
77
79
  for (const [taskId, lease] of this.leases) {
78
- if (lease.leaseExpiresAt < olderThanIso) { this.leases.delete(taskId); removed++; }
80
+ if (lease.leaseExpiresAt < olderThanIso) {
81
+ this.leases.delete(taskId);
82
+ removed++;
83
+ }
79
84
  }
80
85
  return removed;
81
86
  }
@@ -18,7 +18,9 @@ export class InMemoryTaskScopeStore implements TaskScopeStore {
18
18
  return scope;
19
19
  }
20
20
 
21
- get(taskId: string): TaskProjectScope | undefined { return this.scopes.get(taskId); }
21
+ get(taskId: string): TaskProjectScope | undefined {
22
+ return this.scopes.get(taskId);
23
+ }
22
24
 
23
25
  taskIds(projectRoot: string | undefined, limit: number): string[] {
24
26
  return [...this.scopes.values()]