@danypops/pi-papyrus 0.54.0 → 0.54.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/artifact/artifact-browser.ts +6 -2
- package/extension/src/tool-rendering/render-model/artifact.ts +82 -0
- package/extension/src/tool-rendering/render-model/discussion.ts +49 -0
- package/extension/src/tool-rendering/render-model/execution-plan.ts +88 -0
- package/extension/src/tool-rendering/render-model/gate-run.ts +55 -0
- package/extension/src/tool-rendering/render-model/graph.ts +43 -0
- package/extension/src/tool-rendering/render-model/index.ts +235 -0
- package/extension/src/tool-rendering/render-model/lease.ts +40 -0
- package/extension/src/tool-rendering/render-model/misc.ts +45 -0
- package/extension/src/tool-rendering/render-model/playbook.ts +107 -0
- package/extension/src/tool-rendering/render-model/shared.ts +163 -0
- package/extension/src/tool-rendering/render-model/task-completion.ts +92 -0
- package/extension/src/tool-rendering/render-model.ts +17 -827
- package/extension/src/tools/renderers/discussion.ts +92 -0
- package/extension/src/tools/renderers/index.ts +236 -0
- package/extension/src/tools/renderers/lease.ts +57 -0
- package/extension/src/tools/renderers/playbook.ts +69 -0
- package/extension/src/tools/renderers/shared.ts +94 -0
- package/extension/src/tools/renderers/task-completion.ts +98 -0
- package/extension/src/tools/renderers/task-execution.ts +79 -0
- package/extension/src/tools/vehicle-artifact-renderers.ts +14 -669
- package/package.json +2 -2
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { TOOL_DETAILS_MAX_ITEMS } from "@danypops/papyrus";
|
|
2
|
+
import { boundedExecutionPlanNodes, boundedLayers, type ExecutionPlanNode } from "./execution-plan.ts";
|
|
3
|
+
import { boundedStringArray, completeness, PAPYRUS_TOOL_DETAILS_SCHEMA, type ResultCompleteness, type ToolDetailsBase } from "./shared.ts";
|
|
4
|
+
|
|
5
|
+
export interface ToolInvocationCreated {
|
|
6
|
+
tasks: string[];
|
|
7
|
+
docs: string[];
|
|
8
|
+
rules: string[];
|
|
9
|
+
roots: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface InvocationToolDetails extends ToolDetailsBase {
|
|
13
|
+
kind: "invocation";
|
|
14
|
+
runId: string;
|
|
15
|
+
created: ToolInvocationCreated;
|
|
16
|
+
completeness: ResultCompleteness;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createInvocationDetails(operation: string, runId: string, created: ToolInvocationCreated): InvocationToolDetails {
|
|
20
|
+
const bounded: ToolInvocationCreated = {
|
|
21
|
+
tasks: created.tasks.slice(0, TOOL_DETAILS_MAX_ITEMS),
|
|
22
|
+
docs: created.docs.slice(0, TOOL_DETAILS_MAX_ITEMS),
|
|
23
|
+
rules: created.rules.slice(0, TOOL_DETAILS_MAX_ITEMS),
|
|
24
|
+
roots: created.roots.slice(0, TOOL_DETAILS_MAX_ITEMS),
|
|
25
|
+
};
|
|
26
|
+
const total = created.tasks.length + created.docs.length + created.rules.length + created.roots.length;
|
|
27
|
+
const returned = bounded.tasks.length + bounded.docs.length + bounded.rules.length + bounded.roots.length;
|
|
28
|
+
return {
|
|
29
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
30
|
+
kind: "invocation",
|
|
31
|
+
operation,
|
|
32
|
+
runId,
|
|
33
|
+
created: bounded,
|
|
34
|
+
completeness: completeness(total, returned),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface PlaybookInvocationCreated {
|
|
39
|
+
docs: string[];
|
|
40
|
+
rules: string[];
|
|
41
|
+
tasks: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface PlaybookInvocationToolDetails extends ToolDetailsBase {
|
|
45
|
+
kind: "playbook-invocation";
|
|
46
|
+
playbookId: string;
|
|
47
|
+
runId: string;
|
|
48
|
+
created: PlaybookInvocationCreated;
|
|
49
|
+
rootTaskIds: string[];
|
|
50
|
+
entryTaskId: string;
|
|
51
|
+
execution: { nodes: ExecutionPlanNode[]; layers: string[][]; cycleIds: string[] };
|
|
52
|
+
completeness: ResultCompleteness;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createPlaybookInvocationDetails(
|
|
56
|
+
operation: string,
|
|
57
|
+
fields: {
|
|
58
|
+
playbookId: string;
|
|
59
|
+
runId: string;
|
|
60
|
+
created: PlaybookInvocationCreated;
|
|
61
|
+
rootTaskIds: readonly string[];
|
|
62
|
+
entryTaskId: string;
|
|
63
|
+
execution: { nodes: readonly ExecutionPlanNode[]; layers: readonly (readonly string[])[]; cycleIds: readonly string[] };
|
|
64
|
+
},
|
|
65
|
+
): PlaybookInvocationToolDetails {
|
|
66
|
+
const boundedNodes = boundedExecutionPlanNodes(fields.execution.nodes);
|
|
67
|
+
return {
|
|
68
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
69
|
+
kind: "playbook-invocation",
|
|
70
|
+
operation,
|
|
71
|
+
playbookId: fields.playbookId,
|
|
72
|
+
runId: fields.runId,
|
|
73
|
+
created: {
|
|
74
|
+
docs: boundedStringArray(fields.created.docs),
|
|
75
|
+
rules: boundedStringArray(fields.created.rules),
|
|
76
|
+
tasks: boundedStringArray(fields.created.tasks),
|
|
77
|
+
},
|
|
78
|
+
rootTaskIds: boundedStringArray(fields.rootTaskIds),
|
|
79
|
+
entryTaskId: fields.entryTaskId,
|
|
80
|
+
execution: {
|
|
81
|
+
nodes: boundedNodes,
|
|
82
|
+
layers: boundedLayers(fields.execution.layers),
|
|
83
|
+
cycleIds: boundedStringArray(fields.execution.cycleIds),
|
|
84
|
+
},
|
|
85
|
+
completeness: completeness(fields.execution.nodes.length, boundedNodes.length),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface PlaybookMissingArgumentsToolDetails extends ToolDetailsBase {
|
|
90
|
+
kind: "playbook-missing-arguments";
|
|
91
|
+
playbookId: string;
|
|
92
|
+
missingArguments: string[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function createPlaybookMissingArgumentsDetails(
|
|
96
|
+
operation: string,
|
|
97
|
+
playbookId: string,
|
|
98
|
+
missingArguments: readonly string[],
|
|
99
|
+
): PlaybookMissingArgumentsToolDetails {
|
|
100
|
+
return {
|
|
101
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
102
|
+
kind: "playbook-missing-arguments",
|
|
103
|
+
operation,
|
|
104
|
+
playbookId,
|
|
105
|
+
missingArguments: boundedStringArray(missingArguments),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base kernel shared by every render-model/*.ts result-kind module: the schema-version marker,
|
|
3
|
+
* the common completeness/bounding primitives, and the base validators nearly every kind's own
|
|
4
|
+
* parse case reuses. Split out of the former single render-model.ts as part of a SOLID-audit-
|
|
5
|
+
* driven decomposition (see Doc "Modularity playbook: building-block-shaped TypeScript modules
|
|
6
|
+
* for papyrus/pi-papyrus" and the "pi-papyrus render-model.ts split" child of "Epic: Modularize
|
|
7
|
+
* papyrus/pi-papyrus god-files into building-block modules").
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
type Artifact,
|
|
11
|
+
TOOL_DETAILS_BODY_MAX_CHARACTERS,
|
|
12
|
+
TOOL_DETAILS_FIELD_MAX_CHARACTERS,
|
|
13
|
+
TOOL_DETAILS_MAX_ITEMS,
|
|
14
|
+
TOOL_MODEL_CONTENT_MAX_CHARACTERS,
|
|
15
|
+
} from "@danypops/papyrus";
|
|
16
|
+
|
|
17
|
+
export const PAPYRUS_TOOL_DETAILS_SCHEMA = "papyrus.tool-details/v1" as const;
|
|
18
|
+
|
|
19
|
+
export interface ResultCompleteness {
|
|
20
|
+
truncated: boolean;
|
|
21
|
+
omitted: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ToolArtifactSummary {
|
|
25
|
+
id: string;
|
|
26
|
+
/** Optional only for a detail object persisted before this field existed -- ArtifactCard falls back to id when absent. */
|
|
27
|
+
alias?: string;
|
|
28
|
+
kind: string;
|
|
29
|
+
title: string;
|
|
30
|
+
status: string;
|
|
31
|
+
subtype: string;
|
|
32
|
+
labels: string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ToolArtifact extends ToolArtifactSummary {
|
|
36
|
+
body: string;
|
|
37
|
+
createdAt: string;
|
|
38
|
+
updatedAt: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ToolDetailsBase {
|
|
42
|
+
schemaVersion: typeof PAPYRUS_TOOL_DETAILS_SCHEMA;
|
|
43
|
+
operation: string;
|
|
44
|
+
kind: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Distinct from the artifact's own lifecycle status (todo/in-progress/done/...) --
|
|
48
|
+
* this is Task Focus's own separate active/paused dimension, carried by
|
|
49
|
+
* tasks.focused/tasks.pause/tasks.unpause's {artifact, status, updatedAt}
|
|
50
|
+
* wrapper shape. Never conflate the two: an artifact can be "in-progress"
|
|
51
|
+
* while its focus is "paused". */
|
|
52
|
+
export interface ArtifactFocusAnnotation {
|
|
53
|
+
status: string;
|
|
54
|
+
updatedAt: string;
|
|
55
|
+
pauseReason?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ModelContent {
|
|
59
|
+
text: string;
|
|
60
|
+
truncated: boolean;
|
|
61
|
+
omitted: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function completeness(total: number, returned: number): ResultCompleteness {
|
|
65
|
+
const omitted = Math.max(0, total - returned);
|
|
66
|
+
return { truncated: omitted > 0, omitted };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function boundedText(value: string, maximum: number): { value: string; completeness: ResultCompleteness } {
|
|
70
|
+
const clipped = value.slice(0, maximum);
|
|
71
|
+
return { value: clipped, completeness: completeness(value.length, clipped.length) };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type ArtifactSummarySource = Pick<Artifact, "id" | "kind" | "title" | "status" | "subtype" | "labels"> & { alias?: string };
|
|
75
|
+
|
|
76
|
+
export function artifactSummary(artifact: ArtifactSummarySource): ToolArtifactSummary {
|
|
77
|
+
return {
|
|
78
|
+
id: artifact.id,
|
|
79
|
+
alias: artifact.alias,
|
|
80
|
+
kind: artifact.kind,
|
|
81
|
+
title: artifact.title,
|
|
82
|
+
status: artifact.status,
|
|
83
|
+
subtype: artifact.subtype,
|
|
84
|
+
labels: artifact.labels.slice(0, TOOL_DETAILS_MAX_ITEMS),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function boundedStringArray(values: readonly string[]): string[] {
|
|
89
|
+
return values.slice(0, TOOL_DETAILS_MAX_ITEMS);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function createModelContent(value: string): ModelContent {
|
|
93
|
+
if (value.length <= TOOL_MODEL_CONTENT_MAX_CHARACTERS) {
|
|
94
|
+
return { text: value, truncated: false, omitted: 0 };
|
|
95
|
+
}
|
|
96
|
+
let omitted = value.length - TOOL_MODEL_CONTENT_MAX_CHARACTERS;
|
|
97
|
+
let marker = "";
|
|
98
|
+
let kept = 0;
|
|
99
|
+
for (let iteration = 0; iteration < 5; iteration += 1) {
|
|
100
|
+
const nextMarker = `\n[truncated ${omitted} characters]`;
|
|
101
|
+
const nextKept = Math.max(0, TOOL_MODEL_CONTENT_MAX_CHARACTERS - nextMarker.length);
|
|
102
|
+
const nextOmitted = value.length - nextKept;
|
|
103
|
+
marker = nextMarker;
|
|
104
|
+
kept = nextKept;
|
|
105
|
+
if (nextOmitted === omitted) break;
|
|
106
|
+
omitted = nextOmitted;
|
|
107
|
+
}
|
|
108
|
+
return { text: `${value.slice(0, kept)}${marker}`, truncated: true, omitted: value.length - kept };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
112
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function isBoundedString(value: unknown, maximum = TOOL_DETAILS_FIELD_MAX_CHARACTERS): value is string {
|
|
116
|
+
return typeof value === "string" && value.length <= maximum;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function isStringArray(value: unknown): value is string[] {
|
|
120
|
+
return Array.isArray(value) && value.length <= TOOL_DETAILS_MAX_ITEMS && value.every((item) => isBoundedString(item));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function isCompleteness(value: unknown): value is ResultCompleteness {
|
|
124
|
+
return isRecord(value) && typeof value.truncated === "boolean" && Number.isSafeInteger(value.omitted) && Number(value.omitted) >= 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function isArtifactSummary(value: unknown): value is ToolArtifactSummary {
|
|
128
|
+
return (
|
|
129
|
+
isRecord(value) &&
|
|
130
|
+
isBoundedString(value.id) &&
|
|
131
|
+
isBoundedString(value.kind) &&
|
|
132
|
+
isBoundedString(value.title) &&
|
|
133
|
+
isBoundedString(value.status) &&
|
|
134
|
+
isBoundedString(value.subtype) &&
|
|
135
|
+
isStringArray(value.labels)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function isToolArtifact(value: unknown): value is ToolArtifact {
|
|
140
|
+
if (!isRecord(value)) return false;
|
|
141
|
+
const body = value.body;
|
|
142
|
+
const createdAt = value.createdAt;
|
|
143
|
+
const updatedAt = value.updatedAt;
|
|
144
|
+
return (
|
|
145
|
+
isArtifactSummary(value) &&
|
|
146
|
+
isBoundedString(body, TOOL_DETAILS_BODY_MAX_CHARACTERS) &&
|
|
147
|
+
isBoundedString(createdAt) &&
|
|
148
|
+
isBoundedString(updatedAt)
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function isFocusAnnotation(value: unknown): value is ArtifactFocusAnnotation {
|
|
153
|
+
return (
|
|
154
|
+
isRecord(value) &&
|
|
155
|
+
isBoundedString(value.status) &&
|
|
156
|
+
isBoundedString(value.updatedAt) &&
|
|
157
|
+
(value.pauseReason === undefined || isBoundedString(value.pauseReason))
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function isBoundedArray<T>(value: unknown, maximum: number, predicate: (entry: unknown) => entry is T): value is T[] {
|
|
162
|
+
return Array.isArray(value) && value.length <= maximum && value.every(predicate);
|
|
163
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type Artifact, TOOL_DETAILS_MAX_ITEMS, TOOL_DETAILS_ROW_OUTPUT_MAX_CHARACTERS } from "@danypops/papyrus";
|
|
2
|
+
import {
|
|
3
|
+
artifactSummary,
|
|
4
|
+
boundedStringArray,
|
|
5
|
+
completeness,
|
|
6
|
+
isArtifactSummary,
|
|
7
|
+
isBoundedString,
|
|
8
|
+
isRecord,
|
|
9
|
+
isStringArray,
|
|
10
|
+
PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
11
|
+
type ResultCompleteness,
|
|
12
|
+
type ToolArtifactSummary,
|
|
13
|
+
type ToolDetailsBase,
|
|
14
|
+
} from "./shared.ts";
|
|
15
|
+
|
|
16
|
+
export interface TaskGateResultSummary {
|
|
17
|
+
passed: boolean;
|
|
18
|
+
output: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TaskChecklistReviewSummary {
|
|
22
|
+
item: string;
|
|
23
|
+
accepted: boolean;
|
|
24
|
+
reason?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TaskBlockageSummary {
|
|
28
|
+
artifact: ToolArtifactSummary;
|
|
29
|
+
dependencyIds: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface TaskCompletionToolDetails extends ToolDetailsBase {
|
|
33
|
+
kind: "task-completion";
|
|
34
|
+
artifact: ToolArtifactSummary;
|
|
35
|
+
gates: TaskGateResultSummary[];
|
|
36
|
+
checklist: TaskChecklistReviewSummary[];
|
|
37
|
+
completed: boolean;
|
|
38
|
+
focused?: ToolArtifactSummary;
|
|
39
|
+
blocked: TaskBlockageSummary[];
|
|
40
|
+
completeness: ResultCompleteness;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createTaskCompletionDetails(
|
|
44
|
+
operation: string,
|
|
45
|
+
fields: {
|
|
46
|
+
artifact: Artifact;
|
|
47
|
+
gates: readonly TaskGateResultSummary[];
|
|
48
|
+
checklist: readonly TaskChecklistReviewSummary[];
|
|
49
|
+
completed: boolean;
|
|
50
|
+
focused: Artifact | null;
|
|
51
|
+
blocked: readonly { artifact: Artifact; dependencyIds: readonly string[] }[];
|
|
52
|
+
},
|
|
53
|
+
): TaskCompletionToolDetails {
|
|
54
|
+
const boundedGates = fields.gates.slice(0, TOOL_DETAILS_MAX_ITEMS).map((gate) => ({
|
|
55
|
+
...gate,
|
|
56
|
+
output: gate.output.slice(0, TOOL_DETAILS_ROW_OUTPUT_MAX_CHARACTERS),
|
|
57
|
+
}));
|
|
58
|
+
const boundedChecklist = fields.checklist.slice(0, TOOL_DETAILS_MAX_ITEMS);
|
|
59
|
+
const boundedBlocked = fields.blocked.slice(0, TOOL_DETAILS_MAX_ITEMS).map((entry) => ({
|
|
60
|
+
artifact: artifactSummary(entry.artifact),
|
|
61
|
+
dependencyIds: boundedStringArray(entry.dependencyIds),
|
|
62
|
+
}));
|
|
63
|
+
return {
|
|
64
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
65
|
+
kind: "task-completion",
|
|
66
|
+
operation,
|
|
67
|
+
artifact: artifactSummary(fields.artifact),
|
|
68
|
+
gates: boundedGates,
|
|
69
|
+
checklist: boundedChecklist,
|
|
70
|
+
completed: fields.completed,
|
|
71
|
+
...(fields.focused ? { focused: artifactSummary(fields.focused) } : {}),
|
|
72
|
+
blocked: boundedBlocked,
|
|
73
|
+
completeness: completeness(fields.blocked.length, boundedBlocked.length),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function isTaskGateResultSummary(value: unknown): value is TaskGateResultSummary {
|
|
78
|
+
return isRecord(value) && typeof value.passed === "boolean" && isBoundedString(value.output, TOOL_DETAILS_ROW_OUTPUT_MAX_CHARACTERS);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function isTaskChecklistReviewSummary(value: unknown): value is TaskChecklistReviewSummary {
|
|
82
|
+
return (
|
|
83
|
+
isRecord(value) &&
|
|
84
|
+
isBoundedString(value.item) &&
|
|
85
|
+
typeof value.accepted === "boolean" &&
|
|
86
|
+
(value.reason === undefined || isBoundedString(value.reason))
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function isTaskBlockageSummary(value: unknown): value is TaskBlockageSummary {
|
|
91
|
+
return isRecord(value) && isArtifactSummary(value.artifact) && isStringArray(value.dependencyIds);
|
|
92
|
+
}
|