@danypops/papyrus 0.34.3 → 0.35.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 (47) hide show
  1. package/README.md +5 -189
  2. package/package.json +8 -16
  3. package/src/cli.ts +0 -0
  4. package/src/index.ts +32 -0
  5. package/src/modules/discuss.ts +6 -1
  6. package/extension/src/active-task-continuation.ts +0 -131
  7. package/extension/src/artifact-browser.ts +0 -229
  8. package/extension/src/artifact-detail-format.ts +0 -38
  9. package/extension/src/artifact-detail-view.ts +0 -121
  10. package/extension/src/artifact-format.ts +0 -84
  11. package/extension/src/artifact-relationship-lines.ts +0 -24
  12. package/extension/src/artifact-status-presentation.ts +0 -71
  13. package/extension/src/base-prompt-breakdown.ts +0 -55
  14. package/extension/src/beautiful-mermaid-renderer.ts +0 -68
  15. package/extension/src/bounded-poll.ts +0 -20
  16. package/extension/src/context-budget.ts +0 -503
  17. package/extension/src/context-injection-telemetry.ts +0 -88
  18. package/extension/src/context-view.ts +0 -222
  19. package/extension/src/discuss-ask-layout.ts +0 -193
  20. package/extension/src/discuss-ask-view.ts +0 -1301
  21. package/extension/src/discuss.ts +0 -134
  22. package/extension/src/discussion-detail-view.ts +0 -136
  23. package/extension/src/docs.ts +0 -58
  24. package/extension/src/domain-tools.ts +0 -886
  25. package/extension/src/index.ts +0 -776
  26. package/extension/src/markdown.ts +0 -60
  27. package/extension/src/note-widget.ts +0 -8
  28. package/extension/src/notes.ts +0 -102
  29. package/extension/src/playbook-bridge.ts +0 -91
  30. package/extension/src/playbooks.ts +0 -97
  31. package/extension/src/rules.ts +0 -51
  32. package/extension/src/service-client.ts +0 -29
  33. package/extension/src/session-identity.ts +0 -22
  34. package/extension/src/skill-catalog-footprint.ts +0 -183
  35. package/extension/src/skills.ts +0 -127
  36. package/extension/src/task-context.ts +0 -1
  37. package/extension/src/task-detail-format.ts +0 -110
  38. package/extension/src/task-detail-view.ts +0 -139
  39. package/extension/src/task-focus-events.ts +0 -57
  40. package/extension/src/task-graph.ts +0 -116
  41. package/extension/src/task-presentation.ts +0 -26
  42. package/extension/src/task-widget.ts +0 -70
  43. package/extension/src/tasks.ts +0 -418
  44. package/extension/src/tool-rendering/artifact-card.ts +0 -117
  45. package/extension/src/tool-rendering/artifact-list.ts +0 -179
  46. package/extension/src/tool-rendering/index.ts +0 -109
  47. package/extension/src/tool-rendering/render-model.ts +0 -410
package/README.md CHANGED
@@ -1,24 +1,6 @@
1
- # Papyrus
1
+ # @danypops/papyrus
2
2
 
3
- Graph artifact service for Pi enforced SQLite schema, domain tools and services, and native interactive frontends.
4
-
5
- Artifacts are rows in SQLite. Edges are typed relations. Kinds and relations are **registered and enforced**—the schema is the protocol. A supervised Bun daemon is the sole database owner; Pi extensions and other clients use its authenticated loopback service API.
6
-
7
- ## Architecture
8
-
9
- ```text
10
- Pi tools + TUI
11
-
12
- tasks / notes / docs / rules / skills domain tools
13
-
14
- Papyrus client → authenticated loopback daemon
15
-
16
- operation registry + lifecycle services
17
-
18
- graph-store operations → SQLite (WAL)
19
- ```
20
-
21
- The `papyrus_*` tools remain low-level administration escape hatches. Normal agent work should use the domain tools.
3
+ The daemon, CLI, and domain services behind Papyrus's graph artifact store. Installable and runnable standalone, with no Pi dependency — the Pi extension lives in the sibling `@danypops/pi-papyrus` package.
22
4
 
23
5
  ## Storage and service
24
6
 
@@ -46,7 +28,7 @@ papyrus notes capture "Review release provenance later"
46
28
  papyrus notes list --json
47
29
  ```
48
30
 
49
- For repository work, install the versioned ownership guard once:
31
+ For repository work, install the versioned ownership guard once (from the workspace root):
50
32
 
51
33
  ```bash
52
34
  bun run guard:install
@@ -106,27 +88,6 @@ Artifacts are never hard-deleted on request: every artifact gets a permanent, im
106
88
 
107
89
  Once the deadline passes, the daemon's periodic sweep performs a real, cascading, irreversible deletion — the one deliberate, narrow exception to Papyrus's otherwise-absolute append-only history, enforced by the database itself (not merely application code) via a trigger condition checked at delete time.
108
90
 
109
- ## Tools
110
-
111
- The `papyrus_*` tools are the low-level graph-store API:
112
-
113
- - **`papyrus_query`** — filter by kind/status or search title and body
114
- - **`papyrus_graph`** — link artifacts, perform bounded traversal, or read the mutation event log
115
- - **`papyrus_show`** — read nested metadata and bounded edges, optionally running gates
116
-
117
- Agent-facing domain tools own lifecycle invariants and sit above this store API:
118
-
119
- - **`tasks`** — create/update/list/show/plan, manage the singleton active focus, replace evidence-bearing checklists, hierarchy/dependencies, lifecycle transitions, non-blocking gates, and review completion that focuses one deterministic ready successor without claiming effort
120
- - **`notes`** — capture/list/show deferred human intent, mark it consumed, promote it to an existing Task/Doc/Rule/Skill, or archive it with an explicit disposition
121
- - **`docs`** — create/update/list/show, activate/archive/reopen, and document-safe graph links; Note mutations remain behind the Notes facade
122
- - **`rules`** — create/update/list/show/preview, enable/disable, and attach governance gates to tasks
123
- - **`skills`** — create/update/list/show/invoke/run, enable/disable, create compatibility templates, and atomically instantiate parameterized workflow runs
124
- - **`playbooks`** — a completely different beast from Skills, not a subtype: a trigger and an ordered list of steps an agent reads and follows, never mechanically instantiated and never composed the way Skills call other Skills. create/update/list/show/invoke, enable/disable. A Playbook can declare named arguments (`{name, description?, required?}`, required defaults true); invoking with some unsupplied lists exactly which required ones are still missing and directs the agent to ask via `discuss` with `live:true` rather than guess
125
-
126
- Every tool operation is registered in the daemon’s `/api/v1/ops` registry; parity is verified in tests. The task consumer uses the `tasks.graph` operation, which returns task nodes with explicit parent, child, and dependency IDs rather than leaking SQLite rows or asking the UI to reconstruct relationships.
127
-
128
- Internally, application services depend on the `ArtifactStore` and `GateRunner` ports. SQLite and subprocess execution are adapters composed only by the daemon; task behavior is unit-tested against fakes without a database. Task visualization projects the same `TaskGraph` into semantic display graphs and sends them through a `GraphRenderer` port; the Pi adapter uses `beautiful-mermaid` for terminal Unicode output without leaking Mermaid syntax into the task domain.
129
-
130
91
  ### Naming vs. ids
131
92
 
132
93
  Every agent domain tool (tasks, docs, rules, skills, notes, discuss) addresses its artifacts by `name` (the exact title) wherever `id` would otherwise be required -- `dependency_name`/`parent_name`/`child_name`/`root_task_name`/`depends_on_names` (tasks), `target_name` (docs link, searches every kind since a link target can be any of them), `task_name` (rules gate, discuss block/unblock), `template_name` (skills instantiate), and `blocks_task_names` (discuss open) are the name-based equivalents of their `*_id` counterparts. Resolution is an exact, case-insensitive, trimmed title match scoped like a plain list call; an unmatched or ambiguous name fails with a clear error (ambiguous names list the real ids, since that's the one point disambiguation genuinely needs them). Results returned to the agent likewise lead with name and status, never id, unless two artifacts in the same result share a title -- id is a backend implementation detail, not a conversational handle. `id` itself still works exactly as before for every action, in every tool.
@@ -135,151 +96,6 @@ Every agent domain tool (tasks, docs, rules, skills, notes, discuss) addresses i
135
96
 
136
97
  Tasks, Docs, Rules, and Skills all support first-class `update` (title/body/labels, at least one required) alongside creation -- a Doc's body is no longer immutable once created. Every update is bounded the same way creation is (Rules keep their own stricter combined condition+action+body ceiling; Docs/Skills share Tasks' own length bounds) and recorded on the artifact's append-only mutation history, queryable via `graph.history`. An artifact carrying a `source:<system>` label (e.g. `source:web-spider` on an ingested page) is a read-only projection from a system Papyrus doesn't own the source of; updating one is refused with a clear error rather than silently forking it -- capture a correction as a new linked Doc instead until a write-back capability to that system exists. Notes stay behind their own facade for any content change, same as every other Notes mutation.
137
98
 
138
- ## Interactive frontends
139
-
140
- - `/tasks` — project/focused-graph scope, task lifecycle, append-only history, gates, dependencies, and nested metadata
141
- - `/note <request>` — directly capture one project-scoped deferred request without creating a Task
142
- - `/notes` — searchable project Notes inbox with consume, promote, and disposition-aware archive actions
143
- - `/docs` — searchable non-Note documents, lifecycle, details, edit, and graph links
144
- - `/rules` — severity/condition rows, exact injection preview, edit, enable/disable, and task gating
145
- - `/skills` — trigger/tools rows, edit, invocation into the editor, and artifact templates
146
- - `/playbooks` — trigger/tools rows, edit, invocation into the editor, and graph links
147
- - `/playbook <name>` — tab-completes active playbook titles and places that one's invocation directly in the editor, one step instead of browse-then-select; no argument falls back to the full `/playbooks` browser
148
-
149
- All frontends use daemon-backed domain operations; none opens SQLite from the Pi process. **Show details** opens a bounded navigable view across Tasks, Notes, Docs, Rules, legacy Skills, templates, and workflow Skills. User-authored bodies render as width-aware Markdown with headings, emphasis, links, quotes, lists, tables, inline/fenced code, syntax highlighting, and every color/decorative style derived dynamically from the active Pi theme. Generated lifecycle, metadata, checklist, gate, history, and relationship sections keep explicit semantic theme colors. `↑/↓` scrolls, `←/→` pans wide relationships, and Esc returns to the browser; non-interactive clients receive stable source text.
150
-
151
- ## Notes
152
-
153
- Notes are project-scoped `doc/note` artifacts for human requests that should be considered later. Capturing a Note does not create work, inject the entire inbox into prompts, or imply acceptance. The agent can use the `notes` domain tool to list and consume open Notes, decide whether to create a Task, Doc, Rule, or Skill through its owning domain tool, then promote the Note by linking that artifact. Archive requires one of `completed`, `duplicate`, `declined`, or `superseded`; promote archives with a `promoted` disposition and target ID. Capture, consumption, and disposition provenance remain in bounded Note history.
154
-
155
- The default inbox contains draft and consumed/active Notes, is bounded to 50 rows, and has a hard limit of 200. Bodies are capped at 10,000 characters. Generic document and graph lifecycle operations reject Note mutations so they cannot bypass disposition provenance.
156
-
157
- ```bash
158
- papyrus notes capture "Investigate the retry policy" --json
159
- papyrus notes list --limit 25 --json
160
- papyrus notes show <note-id> --json
161
- papyrus notes consume <note-id> --json
162
- # Create the resulting artifact with tasks/docs/rules/skills first, then:
163
- papyrus notes promote <note-id> <target-id> --reason "Converted to tracked work" --json
164
- papyrus notes archive <note-id> declined --reason "No longer relevant" --json
165
- ```
166
-
167
- ## Discuss
168
-
169
- Discuss is a native, persistent deliberation, distinct from a one-shot ask: it survives across turns and sessions, takes multiple rounds, and can genuinely block a Task's completion until settled or deferred. A Discussion is a `doc` artifact with `subtype: "discussion"` -- real graph citizenship (edges, show/list) without a fifth enforced artifact kind. Its fine-grained lifecycle (`active`/`deferred`/`settled`) lives in `extra.discussion`, since Papyrus enforces status vocabulary per kind, not per subtype.
170
-
171
- Rounds are a dedicated append-only child table (mirroring Task history's own shape): `open` records round 1, `reply` appends further rounds, refused once the Discussion is `deferred` or `settled` -- resume first. `defer` is explicitly non-blocking (paused, reason optional, resumable); `settle` is terminal, records an outcome, and archives the Doc. `block`/`unblock` manage the blocking relationship to a Task independently of `open`.
172
-
173
- Blocking is real: `tasks.complete` is refused while any `active` Discussion has a `blocks` edge to that Task. A `deferred` Discussion does not block -- "we will get back to this" is distinct from "resolved."
174
-
175
- `open`/`reply` can also pose a structured choice instead of (or alongside) free text: `options` (2-10 entries) plus `options_mode` -- `single` is mutually exclusive (exactly one pick), `multi` allows several. The Discussion remembers the pending choice (`extra.discussion.pendingOptions`/`pendingOptionsMode`) until a `reply` answers it with `selected`, validated against exactly what was offered and the mode's cardinality; a reply can also pose the *next* round's choice in the same call.
176
-
177
- Run `/discuss` for the interactive panel: browse every Discussion (the real `active`/`deferred`/`settled` state shown per row, alongside any choice awaiting an answer), open a scrollable transcript showing what was posed and picked in each round, and reply/defer/resume/settle or block/unblock a task without leaving the TUI. Replying to a pending choice shows a real picker -- the native single-select list for `single`, or a checkbox multi-select for `multi`, since no built-in multi-select exists in the Pi extension UI. Both modes append a numbered "type your own answer" row -- a genuinely open answer is exactly as valid as any posed option. The multi-select picker supports a number key as a direct quick-select (jump straight to that row instead of scrolling), and steadily highlights checked rows while dimming the rest so the eye reads "what's chosen" independent of cursor position; its cursor row blinks to mark focus. It also auto-cancels after 30s of zero input -- the very first keystroke of any kind stops that countdown permanently for that prompt. Opening a *new* Discussion is left to the agent (same as Docs/Rules/Skills) -- `/discuss` browses and drives existing ones.
178
-
179
- ```bash
180
- papyrus discuss open --title "Naming" --actor alice --content "Should we rename this?" --blocks-json '["task-id"]' --json
181
- papyrus discuss open --title "Which approach" --actor alice --content "Pick one" --options-json '["A","B"]' --options-mode single --json
182
- papyrus discuss reply <discussion-id> --actor bob --content "I think so, here's why..." --json
183
- papyrus discuss reply <discussion-id> --actor bob --content "Going with B" --selected-json '["B"]' --json
184
- papyrus discuss defer <discussion-id> --reason "Waiting on design review" --json
185
- papyrus discuss resume <discussion-id> --json
186
- papyrus discuss settle <discussion-id> --settlement "Agreed: renaming to X" --json
187
- papyrus discuss show <discussion-id> --json
188
- ```
189
-
190
- ## Tasks
99
+ Internally, application services depend on the `ArtifactStore` and `GateRunner` ports. SQLite and subprocess execution are adapters composed only by the daemon; task behavior is unit-tested against fakes without a database. Task visualization projects the same `TaskGraph` into semantic display graphs and sends them through a `GraphRenderer` port -- the Pi adapter (in `@danypops/pi-papyrus`) uses `beautiful-mermaid` for terminal Unicode output without leaking Mermaid syntax into the task domain.
191
100
 
192
- Run `/tasks` for the interactive task panel:
193
-
194
- - `/` filters; arrow keys navigate; Enter opens task actions; `s` switches among the persisted current-project, focused-root graph, and explicit all-projects views
195
- - `g` opens the programmatic Unicode graph; Tab switches dependency/composition views and arrow keys pan
196
- - routed graph layouts are bounded to 48 nodes/96 edges; larger graphs use a deterministic, box-drawn line fallback, and renderer failures are contained inside the viewport rather than escaping Pi
197
- - advance the `todo → in-progress → review → done` lifecycle; failed review becomes `rejected`, retry returns to `in-progress`, and `canceled` is terminal
198
- - use **focus** as the independent singleton Task selection that automatic continuation follows; focusing, pausing, or resuming never changes lifecycle
199
- - starting nested effort moves todo ancestors to in-progress; submitting enters review; completing review checks both typed checklist proofs and executable gates
200
- - passing review marks only that task done and focuses one deterministic ready successor while leaving the successor todo until effort starts
201
- - successors are never auto-completed; fan-in, fan-out, diamonds, and disconnected DAGs remain explicit
202
- - inspect deterministic execution layers, readiness, a box-drawn nested hierarchy, composition, dependencies, evidence-bearing checklists, and verification gates
203
- - lifecycle colors are semantic and redundant with text/glyphs: To-Do grey, in-progress yellow, review blue, rejected orange, done green, and canceled red; `▶` marks active focus
204
- - Show details keeps Checklist and Validation gates separate from incidental Metadata, renders bounded post-migration lifecycle history with actor/source/reason and gate evidence, then renders relationships as a Unicode box-drawing graph footer; `↑/↓` scrolls and `←/→` pans wide graphs
205
- - the compact persistent widget shows the current scope label plus bounded open work in containment order and always retains active focus when it belongs to that scope
206
-
207
- Authenticated CLI parity covers the changed lifecycle and focus operations:
208
-
209
- ```bash
210
- papyrus tasks graph --json
211
- papyrus tasks scope --json
212
- papyrus tasks scope project --json
213
- papyrus tasks scope graph <root-id> --json
214
- papyrus tasks scope all --json
215
- papyrus tasks assign-project <task-id> [project-root] --json
216
- papyrus tasks active --json
217
- papyrus tasks history <id> --json
218
- papyrus tasks focus <id> --json
219
- papyrus tasks focused --json
220
- papyrus tasks pause --json
221
- papyrus tasks unpause --json
222
- papyrus tasks clear-focus --json
223
- papyrus tasks update <id> --title "Revised title" --body "Revised body" --json
224
- papyrus tasks update <id> --status todo --reason "created with legacy default" --json
225
- papyrus tasks start <id> --json
226
- papyrus tasks submit <id> --json
227
- papyrus tasks complete <id> --json
228
- papyrus tasks reject <id> --json
229
- papyrus tasks retry <id> --json
230
- papyrus tasks cancel <id> --json
231
- ```
232
-
233
- ### Focus-driven automatic continuation
234
-
235
- Automatic continuation is a property of the singleton Task focus, not a per-Task automation flag. An active focus continues at Pi's public `agent_settled` boundary when Pi is idle and has no queued messages. `tasks pause` preserves the focused Task while stopping continuation; `tasks unpause` resumes it; `tasks clear-focus` removes it. Replacing focus selects an existing Task rather than creating or canceling one.
236
-
237
- Continuation is single-flight and bounded to 20 automatic turns or 6 unchanged Task snapshots. Reaching either bound persists a paused focus and records the reason in append-only Task history. Human input resumes only these automatically paused focuses; an explicit user pause remains paused.
238
-
239
- Checklist criteria are an item-to-proof map. Every new item requires one or more typed references to inspectable evidence; proof presence does not imply that the evidence passed an executable gate:
240
-
241
- ```ts
242
- checklist: {
243
- "Write failing skill-row tests": {
244
- proof: [
245
- { type: "file", target: "test/frontends.test.ts" },
246
- { type: "symbol", target: "test/frontends.test.ts#skill row test" }
247
- ]
248
- }
249
- }
250
- ```
251
-
252
- Proof types are `file`, `symbol`, `code`, `test`, `command`, `artifact`, and `url`. Existing array checklists remain readable as legacy items with `proof: missing`; Papyrus does not invent evidence.
253
-
254
- Papyrus also injects an Alef-style reconciliation block at `before_agent_start` while work remains: `Current`, `Desired`, `Verify`, and `Next`. The agent is explicitly instructed to ask **“Did we accomplish this task?”** and run review before marking it done. The injection disappears when every task is done or canceled.
255
-
256
- After assembling each system-prompt addition, Papyrus emits a versioned `papyrus.context-injection.v1` observation on Pi's shared extension event bus. It contains only exact byte/character sizes, Rule count, a labeled token estimate, prompt share, sequence, and a SHA-256 payload fingerprint; Rule/Task text, prompts, project paths, and credentials are never included. Jittor can persist and assess these observations without Papyrus maintaining a second telemetry store.
257
-
258
- Task edits mutate the existing Papyrus-owned Task identity and append an `updated` event; title, body, and labels can be revised without canceling the Task or creating a replacement. Lifecycle, relationships, gates, checklist metadata, scope, and focus remain intact. The same `update` action provides a narrowly guarded recovery for Tasks accidentally created terminal by a legacy default: `status=todo` requires an audit reason, cannot be combined with content edits, only applies when `created` is the sole lifecycle event, and appends `creation_recovered` rather than rewriting history.
259
-
260
- ## Why
261
-
262
- Papyrus keeps SQLite’s local simplicity while centralizing writes, migrations, lifecycle invariants, gate execution, and maintenance in one small supervised process. The loopback bearer token prevents unrelated local HTTP callers from mutating the graph, while the native Pi extension provides richer domain tools and TUI integration.
263
-
264
- ## Install
265
-
266
- Install the published Pi package, then install its supervised user service:
267
-
268
- ```bash
269
- packed install npm:@danypops/papyrus
270
- ~/.pi/agent/npm/node_modules/.bin/papyrus service install
271
- ```
272
-
273
- Existing databases are never migrated on daemon boot. After upgrading to a newer schema, run the authenticated CLI migration explicitly. Older databases receive prerequisite schemas—including Task continuation and Context Mesh extensions—in one transaction. Existing Tasks are deliberately marked **unscoped**: Papyrus does not guess ownership from titles, labels, historical cwd, or repository names. They remain visible in **All projects** until explicitly assigned with `papyrus tasks assign-project <task-id> [project-root]`:
274
-
275
- ```bash
276
- ~/.pi/agent/npm/node_modules/.bin/papyrus migrate schema
277
- ```
278
-
279
- Until that command succeeds, health reports `migrationRequired` and normal domain operations are rejected with actionable guidance. Migration is not exposed as a Pi tool or MCP action. New empty databases bootstrap directly at the current schema.
280
-
281
- Reload Pi once the service is active. Git installs remain available for development builds:
282
-
283
- ```bash
284
- packed install git:github.com/DanyPops/papyrus
285
- ```
101
+ `src/index.ts` is this package's public surface for `@danypops/pi-papyrus` (and any other real npm consumer): explicit, named exports only, not a blanket re-export of internal daemon plumbing.
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.34.3",
3
+ "version": "0.35.1",
4
4
  "description": "Daemon-backed graph artifacts, evidence-bearing tasks, rules, skills, and native TUI workflows for Pi",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package"],
7
+ "main": "./src/index.ts",
8
+ "types": "./src/index.ts",
7
9
  "bin": {
8
10
  "papyrus": "src/cli.ts"
9
11
  },
@@ -12,25 +14,16 @@
12
14
  "typecheck": "tsc --noEmit -p tsconfig.json",
13
15
  "cli": "bun src/cli.ts",
14
16
  "serve": "bun src/cli.ts serve",
15
- "service:install": "bun src/cli.ts service install",
16
- "guard:install": "git config core.hooksPath .githooks"
17
- },
18
- "pi": {
19
- "extensions": ["extension/src/index.ts"]
20
- },
21
- "peerDependencies": {
22
- "@earendil-works/pi-coding-agent": "*",
23
- "@earendil-works/pi-tui": "*",
24
- "typebox": "*"
17
+ "service:install": "bun src/cli.ts service install"
25
18
  },
26
19
  "devDependencies": {
27
- "@earendil-works/pi-coding-agent": "^0.80.10",
28
20
  "bun-types": "latest",
29
21
  "typescript": "^5.7.3"
30
22
  },
31
23
  "repository": {
32
24
  "type": "git",
33
- "url": "git+https://github.com/DanyPops/papyrus.git"
25
+ "url": "git+https://github.com/DanyPops/papyrus.git",
26
+ "directory": "packages/papyrus"
34
27
  },
35
28
  "homepage": "https://github.com/DanyPops/papyrus#readme",
36
29
  "bugs": {
@@ -39,9 +32,8 @@
39
32
  "publishConfig": {
40
33
  "access": "public"
41
34
  },
42
- "files": ["src", "extension", "README.md"],
35
+ "files": ["src", "README.md"],
43
36
  "dependencies": {
44
- "beautiful-mermaid": "1.1.3",
45
- "@danypops/daemon-kit": "^0.4.0"
37
+ "@danypops/daemon-kit": "^0.10.0"
46
38
  }
47
39
  }
package/src/cli.ts CHANGED
File without changes
package/src/index.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Public surface for @danypops/pi-papyrus (and any other real npm consumer): every type or
3
+ * value the Pi extension package needs across the package boundary, named explicitly rather
4
+ * than a blanket `export *` on service-heavy modules -- matches ports-and-adapters practice of a
5
+ * purposeful API, not "everything happens to be reachable."
6
+ */
7
+ export * from "./constants.ts";
8
+
9
+ export type { Artifact, ArtifactEdge } from "./domain/artifact.ts";
10
+ export { PROOF_TYPES, checklistEntries, type ProofReference } from "./domain/checklist.ts";
11
+ export { DISCUSSION_SUBTYPE, readDiscussionExtra, type DiscussionRound } from "./domain/discussion.ts";
12
+ export type { DisplayGraph, DisplayGraphEdge, DisplayGraphNode, RenderedGraph } from "./domain/display-graph.ts";
13
+ export type { GateResult } from "./domain/gate.ts";
14
+ export type { NoteHistoryPage } from "./domain/note-event.ts";
15
+ export type { TaskEvent, TaskHistoryPage } from "./domain/task-event.ts";
16
+ export type { TaskLease } from "./domain/task-lease.ts";
17
+ export type { TaskViewSelection } from "./domain/task-scope.ts";
18
+
19
+ export type { ArtifactStore } from "./ports/artifact-store.ts";
20
+ export type { GraphRenderer } from "./ports/graph-renderer.ts";
21
+
22
+ export { connectPapyrusClient, type PapyrusClient } from "./client.ts";
23
+ export type { DiscussionAndRounds } from "./discussion-service.ts";
24
+ export { NOTE_DISPOSITIONS } from "./note-service.ts";
25
+ export type { OperationName, SchemaState } from "./service.ts";
26
+ export type { SkillWorkflowRunResult } from "./skill-execution.ts";
27
+ export { projectArtifactRelationships } from "./artifact-relationship-view.ts";
28
+ export { taskContext } from "./task-context.ts";
29
+ export { projectTaskExecution, type TaskExecutionPlan, type TaskExecutionState } from "./task-execution.ts";
30
+ export { projectTaskGraph, type TaskGraphView } from "./task-graph-view.ts";
31
+ export { fallbackLabel, projectTaskRelationships } from "./task-relationship-view.ts";
32
+ export type { TaskCompletion, TaskGraph, TaskNode, TaskStatus } from "./task-service.ts";
@@ -12,10 +12,15 @@ type OperationInput = Record<string, unknown>;
12
12
 
13
13
  function string(input: OperationInput, key: string): string {
14
14
  const value = input[key];
15
- if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
15
+ if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required${REQUIRED_FIELD_HINTS[key] ? ` (${REQUIRED_FIELD_HINTS[key]})` : ""}`);
16
16
  return value;
17
17
  }
18
18
 
19
+ /** Fields whose name alone doesn't say what a valid value looks like -- everything else (title, content, id, settlement) is self-explanatory. */
20
+ const REQUIRED_FIELD_HINTS: Record<string, string> = {
21
+ actor: 'a display name for who is posting, e.g. "alice" or "agent"',
22
+ };
23
+
19
24
  function optionalString(input: OperationInput, key: string): string | undefined {
20
25
  const value = input[key];
21
26
  if (value === undefined) return undefined;
@@ -1,131 +0,0 @@
1
- export interface ActiveTaskMarker {
2
- id: string;
3
- title: string;
4
- updated_at: string;
5
- }
6
-
7
- export interface ActiveTaskContinuationOptions {
8
- maxTurns: number;
9
- maxUnchangedTurns: number;
10
- }
11
-
12
- export interface ActiveTaskContinuationState {
13
- queued: boolean;
14
- consecutiveTurns: number;
15
- unchangedTurns: number;
16
- pausedReason?: string;
17
- }
18
-
19
- export interface ActiveTaskContinuationDecision {
20
- action: "continue" | "wait" | "pause";
21
- reason: string;
22
- prompt?: string;
23
- }
24
-
25
- const TITLE_LIMIT = 120;
26
- const AUTOMATIC_PAUSE_PREFIX = "automatic continuation paused:";
27
-
28
- export function automaticPauseReason(reason: string): string {
29
- return `${AUTOMATIC_PAUSE_PREFIX} ${reason}`;
30
- }
31
-
32
- export function shouldResumeFocusOnHumanInput(status: string, pauseReason?: string): boolean {
33
- return status === "paused" && pauseReason?.startsWith(AUTOMATIC_PAUSE_PREFIX) === true;
34
- }
35
-
36
- function fingerprint(task: ActiveTaskMarker): string {
37
- return `${task.id}:${task.updated_at}`;
38
- }
39
-
40
- function continuationPrompt(task: ActiveTaskMarker): string {
41
- return [
42
- "Continue the active Papyrus Task now; do not hand off merely because the previous Pi run settled.",
43
- "Reconcile its lifecycle, take the next concrete action, use tools, submit it for review when implementation effort is ready, and run gates plus checklist review before completion.",
44
- "Do not shrink the task's scope to whatever fits in this turn, and do not treat a status update or summary as a substitute for doing the work or as proof of completion.",
45
- "If something blocks progress, do not reject or pause on the first obstacle -- only after it genuinely recurs, and only when the task truly cannot proceed without external input.",
46
- `Active task: ${task.title.slice(0, TITLE_LIMIT)}`,
47
- ].join("\n");
48
- }
49
-
50
- export class ActiveTaskContinuation {
51
- private queued = false;
52
- private consecutiveTurns = 0;
53
- private unchangedTurns = 0;
54
- private lastFingerprint: string | undefined;
55
- private pausedReason: string | undefined;
56
-
57
- constructor(private readonly options: ActiveTaskContinuationOptions) {
58
- if (!Number.isInteger(options.maxTurns) || options.maxTurns < 1) throw new Error("maxTurns must be a positive integer");
59
- if (!Number.isInteger(options.maxUnchangedTurns) || options.maxUnchangedTurns < 1) {
60
- throw new Error("maxUnchangedTurns must be a positive integer");
61
- }
62
- }
63
-
64
- evaluate(task: ActiveTaskMarker | null, context: { idle: boolean; pendingMessages: boolean }): ActiveTaskContinuationDecision {
65
- if (!context.idle) return { action: "wait", reason: "Pi is not settled" };
66
- if (context.pendingMessages) return { action: "wait", reason: "Pi already has pending messages" };
67
- if (this.queued) return { action: "wait", reason: "continuation already queued" };
68
- if (!task) {
69
- this.resetProgress();
70
- return { action: "wait", reason: "no active task" };
71
- }
72
-
73
- const currentFingerprint = fingerprint(task);
74
- if (currentFingerprint !== this.lastFingerprint) {
75
- this.lastFingerprint = currentFingerprint;
76
- this.unchangedTurns = 0;
77
- this.pausedReason = undefined;
78
- } else {
79
- this.unchangedTurns += 1;
80
- }
81
-
82
- if (this.consecutiveTurns >= this.options.maxTurns) {
83
- return this.pause(`automatic turn limit reached (${this.options.maxTurns})`);
84
- }
85
- if (this.unchangedTurns >= this.options.maxUnchangedTurns) {
86
- return this.pause(`no task progress after ${this.options.maxUnchangedTurns} automatic turns`);
87
- }
88
-
89
- this.queued = true;
90
- this.consecutiveTurns += 1;
91
- return {
92
- action: "continue",
93
- reason: "an active task remains",
94
- prompt: continuationPrompt(task),
95
- };
96
- }
97
-
98
- onAgentStart(): void {
99
- this.queued = false;
100
- }
101
-
102
- onCompaction(): void {
103
- this.queued = false;
104
- }
105
-
106
- onHumanInput(): void {
107
- this.resetProgress();
108
- }
109
-
110
- status(): ActiveTaskContinuationState {
111
- return {
112
- queued: this.queued,
113
- consecutiveTurns: this.consecutiveTurns,
114
- unchangedTurns: this.unchangedTurns,
115
- ...(this.pausedReason ? { pausedReason: this.pausedReason } : {}),
116
- };
117
- }
118
-
119
- private pause(reason: string): ActiveTaskContinuationDecision {
120
- this.pausedReason = reason;
121
- return { action: "pause", reason };
122
- }
123
-
124
- private resetProgress(): void {
125
- this.queued = false;
126
- this.consecutiveTurns = 0;
127
- this.unchangedTurns = 0;
128
- this.lastFingerprint = undefined;
129
- this.pausedReason = undefined;
130
- }
131
- }