@danypops/papyrus 0.11.4 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +16 -2
  2. package/extension/src/active-task-continuation.ts +6 -0
  3. package/extension/src/artifact-browser.ts +13 -7
  4. package/extension/src/artifact-status-presentation.ts +53 -0
  5. package/extension/src/context-budget.ts +173 -0
  6. package/extension/src/context-view.ts +172 -0
  7. package/extension/src/docs.ts +6 -5
  8. package/extension/src/domain-tools.ts +108 -52
  9. package/extension/src/index.ts +124 -38
  10. package/extension/src/notes.ts +16 -4
  11. package/extension/src/rules.ts +7 -7
  12. package/extension/src/skill-catalog-footprint.ts +183 -0
  13. package/extension/src/skills.ts +2 -3
  14. package/extension/src/task-focus-events.ts +57 -0
  15. package/extension/src/task-widget.ts +13 -1
  16. package/extension/src/tasks.ts +51 -15
  17. package/extension/src/tool-rendering/artifact-card.ts +117 -0
  18. package/extension/src/tool-rendering/artifact-list.ts +179 -0
  19. package/extension/src/tool-rendering/index.ts +107 -0
  20. package/extension/src/tool-rendering/render-model.ts +406 -0
  21. package/package.json +4 -2
  22. package/src/adapters/in-memory-conversation-journal-store.ts +48 -0
  23. package/src/adapters/sqlite-artifact-scope-store.ts +36 -0
  24. package/src/adapters/sqlite-artifact-store.ts +20 -11
  25. package/src/adapters/sqlite-discourse-store.ts +325 -0
  26. package/src/adapters/sqlite-graph-projection-store.ts +41 -0
  27. package/src/adapters/sqlite-task-focus-store.ts +34 -15
  28. package/src/authority-registry.ts +115 -0
  29. package/src/cli.ts +904 -124
  30. package/src/constants.ts +77 -5
  31. package/src/conversation-journal-service.ts +87 -0
  32. package/src/db.ts +285 -33
  33. package/src/domain/artifact-event.ts +99 -0
  34. package/src/domain/conversation-journal.ts +168 -0
  35. package/src/domain/discourse-store.ts +142 -0
  36. package/src/domain/graph-projection.ts +74 -0
  37. package/src/domain/skill-definition.ts +57 -8
  38. package/src/domain/task-event.ts +4 -0
  39. package/src/domain-services.ts +201 -40
  40. package/src/graph-projection-service.ts +103 -0
  41. package/src/id-migration.ts +200 -0
  42. package/src/module-registry.ts +53 -0
  43. package/src/modules/docs.ts +77 -0
  44. package/src/modules/graph-projection.ts +82 -0
  45. package/src/modules/notes.ts +76 -0
  46. package/src/modules/rules.ts +81 -0
  47. package/src/modules/skills.ts +113 -0
  48. package/src/modules/tasks.ts +164 -0
  49. package/src/ops.ts +142 -15
  50. package/src/ports/artifact-scope-store.ts +20 -0
  51. package/src/ports/artifact-store.ts +10 -5
  52. package/src/ports/conversation-journal-store.ts +17 -0
  53. package/src/ports/graph-projection-store.ts +15 -0
  54. package/src/ports/task-focus-store.ts +62 -20
  55. package/src/service.ts +218 -223
  56. package/src/skill-execution.ts +169 -75
  57. package/src/task-service.ts +70 -38
@@ -1,3 +1,5 @@
1
+ import { TASK_FOCUS_DEFAULT_SCOPE, TASK_FOCUS_MAX_SCOPES, TASK_FOCUS_SCOPE_MAX_LENGTH } from "../constants.ts";
2
+
1
3
  export type TaskFocusStatus = "active" | "paused";
2
4
 
3
5
  export interface TaskFocusState {
@@ -7,37 +9,77 @@ export interface TaskFocusState {
7
9
  pauseReason?: string;
8
10
  }
9
11
 
12
+ export function normalizeFocusScope(scope: string | undefined): string {
13
+ const value = scope ?? TASK_FOCUS_DEFAULT_SCOPE;
14
+ if (value.length === 0 || value.length > TASK_FOCUS_SCOPE_MAX_LENGTH) {
15
+ throw new Error(`task focus scope must be between 1 and ${TASK_FOCUS_SCOPE_MAX_LENGTH} characters`);
16
+ }
17
+ return value;
18
+ }
19
+
20
+ /**
21
+ * One Task Focus per scope. A scope defaults to "global" for callers that don't supply
22
+ * a session id (CLI, legacy behavior) but is normally the requesting agent's session id,
23
+ * so concurrent agents each get their own Focus instead of clobbering a shared singleton.
24
+ */
10
25
  export interface TaskFocusStore {
11
- get(): TaskFocusState | undefined;
12
- set(taskId: string): TaskFocusState;
13
- pause(taskId: string, reason?: string): TaskFocusState;
14
- unpause(taskId: string): TaskFocusState;
15
- clear(taskId?: string): void;
26
+ get(scope?: string): TaskFocusState | undefined;
27
+ set(taskId: string, scope?: string): TaskFocusState;
28
+ pause(taskId: string, reason?: string, scope?: string): TaskFocusState;
29
+ unpause(taskId: string, scope?: string): TaskFocusState;
30
+ clear(taskId?: string, scope?: string): void;
31
+ /** Clears this task's Focus in every scope (session), not just one — for lifecycle events (e.g. cancel) that are not scoped to a single caller. */
32
+ clearEverywhere(taskId: string): void;
16
33
  }
17
34
 
18
35
  export class InMemoryTaskFocusStore implements TaskFocusStore {
19
- private state: TaskFocusState | undefined;
36
+ private readonly state = new Map<string, TaskFocusState>();
20
37
 
21
- get(): TaskFocusState | undefined { return this.state; }
38
+ get(scope?: string): TaskFocusState | undefined { return this.state.get(normalizeFocusScope(scope)); }
39
+
40
+ set(taskId: string, scope?: string): TaskFocusState {
41
+ const key = normalizeFocusScope(scope);
42
+ if (!this.state.has(key) && this.state.size >= TASK_FOCUS_MAX_SCOPES) this.evictOldest();
43
+ const focus: TaskFocusState = { taskId, status: "active", updatedAt: new Date().toISOString() };
44
+ this.state.set(key, focus);
45
+ return focus;
46
+ }
47
+
48
+ pause(taskId: string, reason?: string, scope?: string): TaskFocusState {
49
+ const key = normalizeFocusScope(scope);
50
+ const current = this.state.get(key);
51
+ if (current?.taskId !== taskId) throw new Error(`task "${taskId}" is not focused`);
52
+ const focus: TaskFocusState = { ...current, status: "paused", updatedAt: new Date().toISOString(), ...(reason ? { pauseReason: reason } : {}) };
53
+ this.state.set(key, focus);
54
+ return focus;
55
+ }
22
56
 
23
- set(taskId: string): TaskFocusState {
24
- this.state = { taskId, status: "active", updatedAt: new Date().toISOString() };
25
- return this.state;
57
+ unpause(taskId: string, scope?: string): TaskFocusState {
58
+ const key = normalizeFocusScope(scope);
59
+ const current = this.state.get(key);
60
+ if (current?.taskId !== taskId) throw new Error(`task "${taskId}" is not focused`);
61
+ const focus: TaskFocusState = { taskId, status: "active", updatedAt: new Date().toISOString() };
62
+ this.state.set(key, focus);
63
+ return focus;
26
64
  }
27
65
 
28
- pause(taskId: string, reason?: string): TaskFocusState {
29
- if (this.state?.taskId !== taskId) throw new Error(`task "${taskId}" is not focused`);
30
- this.state = { ...this.state, status: "paused", updatedAt: new Date().toISOString(), ...(reason ? { pauseReason: reason } : {}) };
31
- return this.state;
66
+ clear(taskId?: string, scope?: string): void {
67
+ const key = normalizeFocusScope(scope);
68
+ if (taskId === undefined || this.state.get(key)?.taskId === taskId) this.state.delete(key);
32
69
  }
33
70
 
34
- unpause(taskId: string): TaskFocusState {
35
- if (this.state?.taskId !== taskId) throw new Error(`task "${taskId}" is not focused`);
36
- this.state = { taskId, status: "active", updatedAt: new Date().toISOString() };
37
- return this.state;
71
+ clearEverywhere(taskId: string): void {
72
+ for (const [key, focus] of this.state) {
73
+ if (focus.taskId === taskId) this.state.delete(key);
74
+ }
38
75
  }
39
76
 
40
- clear(taskId?: string): void {
41
- if (taskId === undefined || this.state?.taskId === taskId) this.state = undefined;
77
+ private evictOldest(): void {
78
+ let oldestKey: string | undefined;
79
+ let oldestAt: string | undefined;
80
+ for (const [key, focus] of this.state) {
81
+ if (oldestAt === undefined || focus.updatedAt < oldestAt) { oldestKey = key; oldestAt = focus.updatedAt; }
82
+ }
83
+ if (oldestKey !== undefined) this.state.delete(oldestKey);
42
84
  }
43
85
  }