@danypops/pi-papyrus 0.60.1 → 0.60.2
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.
- package/extension/src/tool-rendering/artifact-list.ts +15 -1
- package/extension/src/tool-rendering/render-model/artifact.ts +1 -0
- package/extension/src/tool-rendering/render-model/index.ts +1 -0
- package/extension/src/tools/renderers/index.ts +9 -2
- package/extension/src/tools/renderers/task-collections.ts +64 -0
- package/package.json +4 -4
|
@@ -76,6 +76,7 @@ export class ArtifactListCard implements Component {
|
|
|
76
76
|
const omitted = Math.max(0, this.details.total - display.length);
|
|
77
77
|
if (omitted > 0) lines.push(truncateToWidth(this.theme.fg("dim", `${omitted} more · ${expandHint()}`), safeWidth));
|
|
78
78
|
}
|
|
79
|
+
if (this.details.hasMore) lines.push(truncateToWidth(this.theme.fg("dim", "More pages available"), safeWidth));
|
|
79
80
|
this.cachedWidth = safeWidth;
|
|
80
81
|
this.cachedLines = lines;
|
|
81
82
|
return lines;
|
|
@@ -188,7 +189,20 @@ export class TaskHierarchyPreview implements Component {
|
|
|
188
189
|
safeWidth,
|
|
189
190
|
);
|
|
190
191
|
const tree = new TreeView({ nodes: toTreeNodes(this.details, this.theme, this.expanded) });
|
|
191
|
-
const
|
|
192
|
+
const rendered = tree.render(safeWidth);
|
|
193
|
+
const displayed = rendered.slice(0, this.expanded ? 200 : TOOL_COLLAPSED_ROW_LIMIT);
|
|
194
|
+
const lines = [header, ...displayed];
|
|
195
|
+
if (rendered.length > displayed.length) {
|
|
196
|
+
lines.push(
|
|
197
|
+
truncateToWidth(
|
|
198
|
+
this.theme.fg("dim", `${rendered.length - displayed.length} more lines · ${this.expanded ? "display limit" : expandHint()}`),
|
|
199
|
+
safeWidth,
|
|
200
|
+
),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
if (this.details.nodeCompleteness.truncated || this.details.edgeCompleteness.truncated) {
|
|
204
|
+
lines.push(truncateToWidth(this.theme.fg("dim", "Partial graph · presentation limit"), safeWidth));
|
|
205
|
+
}
|
|
192
206
|
this.cachedWidth = safeWidth;
|
|
193
207
|
this.cachedLines = lines;
|
|
194
208
|
return lines;
|
|
@@ -83,6 +83,7 @@ export function parsePapyrusToolDetails(value: unknown): PapyrusToolDetails | un
|
|
|
83
83
|
return isBoundedArray(value.rows, TOOL_DETAILS_MAX_ITEMS, isArtifactSummary) &&
|
|
84
84
|
Number.isSafeInteger(value.total) &&
|
|
85
85
|
Number(value.total) >= value.rows.length &&
|
|
86
|
+
(value.hasMore === undefined || typeof value.hasMore === "boolean") &&
|
|
86
87
|
isCompleteness(value.completeness)
|
|
87
88
|
? (value as unknown as ArtifactListToolDetails)
|
|
88
89
|
: undefined;
|
|
@@ -23,7 +23,7 @@ import type { JsonValue, VehicleOperationDescriptor } from "@danypops/vehicle-co
|
|
|
23
23
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
24
24
|
import { type Component, Text } from "@earendil-works/pi-tui";
|
|
25
25
|
import { ArtifactCard } from "../../tool-rendering/artifact-card.ts";
|
|
26
|
-
import { ArtifactListCard } from "../../tool-rendering/artifact-list.ts";
|
|
26
|
+
import { ArtifactListCard, TaskHierarchyPreview } from "../../tool-rendering/artifact-list.ts";
|
|
27
27
|
import {
|
|
28
28
|
createArtifactDetails,
|
|
29
29
|
createArtifactListDetails,
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
renderNoFocusedTask,
|
|
68
68
|
semanticText,
|
|
69
69
|
} from "./shared.ts";
|
|
70
|
+
import { taskGraphPresentation, taskPagePresentation } from "./task-collections.ts";
|
|
70
71
|
import { isTaskCompletion, renderTaskCompletion } from "./task-completion.ts";
|
|
71
72
|
import { isTaskExecutionPlan, renderTaskExecutionPlan } from "./task-execution.ts";
|
|
72
73
|
|
|
@@ -155,6 +156,11 @@ export function papyrusVehicleRenderers(descriptor: VehicleOperationDescriptor):
|
|
|
155
156
|
* instead of silently persisting and rendering raw JSON.
|
|
156
157
|
*/
|
|
157
158
|
function projectPapyrusPresentation(descriptor: VehicleOperationDescriptor, output: unknown): PapyrusToolDetails {
|
|
159
|
+
if (descriptor.name === "tasks.graph" || descriptor.name === "tasks.list_page") {
|
|
160
|
+
const presentation = descriptor.name === "tasks.graph" ? taskGraphPresentation(output) : taskPagePresentation(output);
|
|
161
|
+
if (presentation) return presentation;
|
|
162
|
+
throw new Error(`${descriptor.name} produced no legal presentation variant`);
|
|
163
|
+
}
|
|
158
164
|
if (descriptor.name === "batch.execute") {
|
|
159
165
|
const summary = batchOutcomeSummary(output);
|
|
160
166
|
if (summary !== undefined) return createSemanticTextDetails(descriptor.name, summary);
|
|
@@ -222,8 +228,9 @@ function renderFromPapyrusPresentation(
|
|
|
222
228
|
return new Text(theme.fg("toolOutput", presentation.content), 0, 0);
|
|
223
229
|
case "semantic-text":
|
|
224
230
|
return new SemanticTextCard(presentation, theme, expanded);
|
|
225
|
-
case "transition":
|
|
226
231
|
case "graph":
|
|
232
|
+
return new TaskHierarchyPreview(presentation, theme, expanded);
|
|
233
|
+
case "transition":
|
|
227
234
|
case "gate-run":
|
|
228
235
|
case "invocation":
|
|
229
236
|
case "error":
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { TASK_EXECUTION_MAX_EDGES, TASK_EXECUTION_MAX_NODES, TASK_LIST_PAGE_MAX_LIMIT, type TaskNode } from "@danypops/papyrus";
|
|
2
|
+
import { type ArtifactListToolDetails, createArtifactListDetails } from "../../tool-rendering/render-model/artifact.ts";
|
|
3
|
+
import { createGraphDetails, type GraphToolDetails, type ToolGraphEdge } from "../../tool-rendering/render-model/graph.ts";
|
|
4
|
+
import { isRecord } from "../../tool-rendering/render-model/shared.ts";
|
|
5
|
+
import { isArtifact, isArtifactArray } from "./shared.ts";
|
|
6
|
+
|
|
7
|
+
function identifiers(value: unknown): value is string[] {
|
|
8
|
+
return (
|
|
9
|
+
Array.isArray(value) &&
|
|
10
|
+
value.length <= TASK_EXECUTION_MAX_NODES &&
|
|
11
|
+
value.every((id) => typeof id === "string" && id.length > 0 && id.length <= 500)
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function graphNode(value: unknown): value is TaskNode {
|
|
16
|
+
return (
|
|
17
|
+
isRecord(value) &&
|
|
18
|
+
isArtifact(value.task) &&
|
|
19
|
+
identifiers(value.parentIds) &&
|
|
20
|
+
identifiers(value.childIds) &&
|
|
21
|
+
identifiers(value.dependencyIds)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Projects one cursor page while keeping its continuation token in the operation result only. */
|
|
26
|
+
export function taskPagePresentation(output: unknown): ArtifactListToolDetails | undefined {
|
|
27
|
+
if (!isRecord(output) || !Array.isArray(output.items) || output.items.length > TASK_LIST_PAGE_MAX_LIMIT || !isArtifactArray(output.items))
|
|
28
|
+
return undefined;
|
|
29
|
+
if (
|
|
30
|
+
output.nextCursor !== undefined &&
|
|
31
|
+
(typeof output.nextCursor !== "string" || output.nextCursor.length === 0 || output.nextCursor.length > 16384)
|
|
32
|
+
)
|
|
33
|
+
return undefined;
|
|
34
|
+
return { ...createArtifactListDetails("tasks.list_page", output.items), hasMore: output.nextCursor !== undefined };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Projects selected tasks and their typed relationships into a bounded hierarchy. */
|
|
38
|
+
export function taskGraphPresentation(output: unknown): GraphToolDetails | undefined {
|
|
39
|
+
if (
|
|
40
|
+
!isRecord(output) ||
|
|
41
|
+
!Array.isArray(output.nodes) ||
|
|
42
|
+
output.nodes.length > TASK_EXECUTION_MAX_NODES ||
|
|
43
|
+
!identifiers(output.rootIds) ||
|
|
44
|
+
!output.nodes.every(graphNode)
|
|
45
|
+
)
|
|
46
|
+
return undefined;
|
|
47
|
+
const edges = new Map<string, ToolGraphEdge>();
|
|
48
|
+
let linkCount = 0;
|
|
49
|
+
for (const node of output.nodes) {
|
|
50
|
+
linkCount += node.parentIds.length + node.childIds.length + node.dependencyIds.length;
|
|
51
|
+
if (linkCount > TASK_EXECUTION_MAX_EDGES * 3) return undefined;
|
|
52
|
+
const add = (from: string, relation: string, to: string) => {
|
|
53
|
+
edges.set(JSON.stringify([from, relation, to]), { from, relation, to });
|
|
54
|
+
};
|
|
55
|
+
for (const parent of node.parentIds) add(parent, "contains", node.task.id);
|
|
56
|
+
for (const child of node.childIds) add(node.task.id, "contains", child);
|
|
57
|
+
for (const dependency of node.dependencyIds) add(dependency, "unlocks", node.task.id);
|
|
58
|
+
}
|
|
59
|
+
return createGraphDetails(
|
|
60
|
+
"tasks.graph",
|
|
61
|
+
output.nodes.map((node) => node.task),
|
|
62
|
+
[...edges.values()],
|
|
63
|
+
);
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-papyrus",
|
|
3
|
-
"version": "0.60.
|
|
3
|
+
"version": "0.60.2",
|
|
4
4
|
"description": "Pi host extension for Papyrus: native tools, TUI panels, and context injection over the daemon-backed graph store",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"@danypops/vehicle-client-pi": "^0.
|
|
16
|
+
"@danypops/vehicle-client-pi": "^0.48.3",
|
|
17
17
|
"@earendil-works/pi-coding-agent": "*",
|
|
18
18
|
"@earendil-works/pi-tui": "*",
|
|
19
19
|
"typebox": "*"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@danypops/jittor": "^0.19.2",
|
|
23
|
-
"@danypops/papyrus": "^0.60.
|
|
23
|
+
"@danypops/papyrus": "^0.60.13",
|
|
24
24
|
"@danypops/vehicle-client": "^0.10.8",
|
|
25
25
|
"@danypops/vehicle-core": "^0.19.1",
|
|
26
26
|
"@danypops/vehicle-server": "^0.27.1",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@danypops/pi-extension-harness": "^0.8.3",
|
|
32
32
|
"@danypops/pi-tui-harness": "^0.0.2",
|
|
33
|
-
"@danypops/vehicle-client-pi": "^0.
|
|
33
|
+
"@danypops/vehicle-client-pi": "^0.48.3",
|
|
34
34
|
"@danypops/vehicle-conformance": "^0.3.0",
|
|
35
35
|
"@earendil-works/pi-coding-agent": "^0.80.10",
|
|
36
36
|
"bun-types": "latest",
|