@danypops/pi-papyrus 0.54.1 → 0.55.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.
- 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/extension/src/tools/vehicle-notes-client.ts +4 -4
- package/package.json +2 -2
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type Artifact, TOOL_DETAILS_BODY_MAX_CHARACTERS, TOOL_DETAILS_MAX_ITEMS } from "@danypops/papyrus";
|
|
2
|
+
import {
|
|
3
|
+
type ArtifactFocusAnnotation,
|
|
4
|
+
artifactSummary,
|
|
5
|
+
boundedText,
|
|
6
|
+
completeness,
|
|
7
|
+
PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
8
|
+
type ResultCompleteness,
|
|
9
|
+
type ToolArtifact,
|
|
10
|
+
type ToolArtifactSummary,
|
|
11
|
+
type ToolDetailsBase,
|
|
12
|
+
} from "./shared.ts";
|
|
13
|
+
|
|
14
|
+
export interface ArtifactToolDetails extends ToolDetailsBase {
|
|
15
|
+
kind: "artifact";
|
|
16
|
+
artifact: ToolArtifact;
|
|
17
|
+
completeness: ResultCompleteness;
|
|
18
|
+
focus?: ArtifactFocusAnnotation;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ArtifactListToolDetails extends ToolDetailsBase {
|
|
22
|
+
kind: "artifact-list";
|
|
23
|
+
rows: ToolArtifactSummary[];
|
|
24
|
+
total: number;
|
|
25
|
+
completeness: ResultCompleteness;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TransitionToolDetails extends ToolDetailsBase {
|
|
29
|
+
kind: "transition";
|
|
30
|
+
artifact: ToolArtifactSummary;
|
|
31
|
+
fromStatus: string;
|
|
32
|
+
toStatus: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createArtifactDetails(operation: string, artifact: Artifact, focus?: ArtifactFocusAnnotation): ArtifactToolDetails {
|
|
36
|
+
const body = boundedText(artifact.body, TOOL_DETAILS_BODY_MAX_CHARACTERS);
|
|
37
|
+
return {
|
|
38
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
39
|
+
kind: "artifact",
|
|
40
|
+
operation,
|
|
41
|
+
artifact: {
|
|
42
|
+
...artifactSummary(artifact),
|
|
43
|
+
body: body.value,
|
|
44
|
+
createdAt: artifact.created_at,
|
|
45
|
+
updatedAt: artifact.updated_at,
|
|
46
|
+
},
|
|
47
|
+
completeness: body.completeness,
|
|
48
|
+
...(focus ? { focus } : {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createArtifactListDetails(
|
|
53
|
+
operation: string,
|
|
54
|
+
artifacts: readonly Artifact[],
|
|
55
|
+
total = artifacts.length,
|
|
56
|
+
): ArtifactListToolDetails {
|
|
57
|
+
const rows = artifacts.slice(0, TOOL_DETAILS_MAX_ITEMS).map(artifactSummary);
|
|
58
|
+
return {
|
|
59
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
60
|
+
kind: "artifact-list",
|
|
61
|
+
operation,
|
|
62
|
+
rows,
|
|
63
|
+
total,
|
|
64
|
+
completeness: completeness(Math.max(total, artifacts.length), rows.length),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function createTransitionDetails(
|
|
69
|
+
operation: string,
|
|
70
|
+
artifact: Artifact,
|
|
71
|
+
fromStatus: string,
|
|
72
|
+
toStatus: string,
|
|
73
|
+
): TransitionToolDetails {
|
|
74
|
+
return {
|
|
75
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
76
|
+
kind: "transition",
|
|
77
|
+
operation,
|
|
78
|
+
artifact: artifactSummary(artifact),
|
|
79
|
+
fromStatus,
|
|
80
|
+
toStatus,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { TOOL_DETAILS_MAX_ITEMS, TOOL_DETAILS_ROW_OUTPUT_MAX_CHARACTERS } from "@danypops/papyrus";
|
|
2
|
+
import {
|
|
3
|
+
type ArtifactSummarySource,
|
|
4
|
+
artifactSummary,
|
|
5
|
+
completeness,
|
|
6
|
+
isBoundedString,
|
|
7
|
+
isRecord,
|
|
8
|
+
PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
9
|
+
type ResultCompleteness,
|
|
10
|
+
type ToolArtifactSummary,
|
|
11
|
+
type ToolDetailsBase,
|
|
12
|
+
} from "./shared.ts";
|
|
13
|
+
|
|
14
|
+
export interface DiscussionRoundSummary {
|
|
15
|
+
roundNumber: number;
|
|
16
|
+
actor: string;
|
|
17
|
+
content: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DiscussionToolDetails extends ToolDetailsBase {
|
|
21
|
+
kind: "discussion";
|
|
22
|
+
/** Absent for discuss.rounds, which returns rounds with no parent discussion in its own output. */
|
|
23
|
+
discussion?: ToolArtifactSummary;
|
|
24
|
+
rounds: DiscussionRoundSummary[];
|
|
25
|
+
completeness: ResultCompleteness;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createDiscussionDetails(
|
|
29
|
+
operation: string,
|
|
30
|
+
rounds: readonly DiscussionRoundSummary[],
|
|
31
|
+
discussion?: ArtifactSummarySource,
|
|
32
|
+
): DiscussionToolDetails {
|
|
33
|
+
const boundedRounds = rounds.slice(0, TOOL_DETAILS_MAX_ITEMS).map((round) => ({
|
|
34
|
+
...round,
|
|
35
|
+
content: round.content.slice(0, TOOL_DETAILS_ROW_OUTPUT_MAX_CHARACTERS),
|
|
36
|
+
}));
|
|
37
|
+
return {
|
|
38
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
39
|
+
kind: "discussion",
|
|
40
|
+
operation,
|
|
41
|
+
...(discussion ? { discussion: artifactSummary(discussion) } : {}),
|
|
42
|
+
rounds: boundedRounds,
|
|
43
|
+
completeness: completeness(rounds.length, boundedRounds.length),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isDiscussionRoundSummary(value: unknown): value is DiscussionRoundSummary {
|
|
48
|
+
return isRecord(value) && Number.isSafeInteger(value.roundNumber) && isBoundedString(value.actor) && isBoundedString(value.content);
|
|
49
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { TOOL_DETAILS_MAX_ITEMS } from "@danypops/papyrus";
|
|
2
|
+
import {
|
|
3
|
+
boundedStringArray,
|
|
4
|
+
completeness,
|
|
5
|
+
isBoundedArray,
|
|
6
|
+
isBoundedString,
|
|
7
|
+
isRecord,
|
|
8
|
+
isStringArray,
|
|
9
|
+
PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
10
|
+
type ResultCompleteness,
|
|
11
|
+
type ToolDetailsBase,
|
|
12
|
+
} from "./shared.ts";
|
|
13
|
+
|
|
14
|
+
export interface ExecutionPlanNode {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
status: string;
|
|
18
|
+
active: boolean;
|
|
19
|
+
state: string;
|
|
20
|
+
layer: number | null;
|
|
21
|
+
prerequisiteIds: string[];
|
|
22
|
+
successorIds: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ExecutionPlanToolDetails extends ToolDetailsBase {
|
|
26
|
+
kind: "execution-plan";
|
|
27
|
+
nodes: ExecutionPlanNode[];
|
|
28
|
+
layers: string[][];
|
|
29
|
+
cycleIds: string[];
|
|
30
|
+
completeness: ResultCompleteness;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function boundedExecutionPlanNodes(nodes: readonly ExecutionPlanNode[]): ExecutionPlanNode[] {
|
|
34
|
+
return nodes.slice(0, TOOL_DETAILS_MAX_ITEMS).map((node) => ({
|
|
35
|
+
...node,
|
|
36
|
+
prerequisiteIds: boundedStringArray(node.prerequisiteIds),
|
|
37
|
+
successorIds: boundedStringArray(node.successorIds),
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function boundedLayers(layers: readonly (readonly string[])[]): string[][] {
|
|
42
|
+
return layers.slice(0, TOOL_DETAILS_MAX_ITEMS).map((layer) => boundedStringArray(layer));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function createExecutionPlanDetails(
|
|
46
|
+
operation: string,
|
|
47
|
+
nodes: readonly ExecutionPlanNode[],
|
|
48
|
+
layers: readonly (readonly string[])[],
|
|
49
|
+
cycleIds: readonly string[],
|
|
50
|
+
): ExecutionPlanToolDetails {
|
|
51
|
+
const boundedNodes = boundedExecutionPlanNodes(nodes);
|
|
52
|
+
return {
|
|
53
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
54
|
+
kind: "execution-plan",
|
|
55
|
+
operation,
|
|
56
|
+
nodes: boundedNodes,
|
|
57
|
+
layers: boundedLayers(layers),
|
|
58
|
+
cycleIds: boundedStringArray(cycleIds),
|
|
59
|
+
completeness: completeness(nodes.length, boundedNodes.length),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function isExecutionPlanNode(value: unknown): value is ExecutionPlanNode {
|
|
64
|
+
return (
|
|
65
|
+
isRecord(value) &&
|
|
66
|
+
isBoundedString(value.id) &&
|
|
67
|
+
isBoundedString(value.title) &&
|
|
68
|
+
isBoundedString(value.status) &&
|
|
69
|
+
typeof value.active === "boolean" &&
|
|
70
|
+
isBoundedString(value.state) &&
|
|
71
|
+
(value.layer === null || Number.isSafeInteger(value.layer)) &&
|
|
72
|
+
isStringArray(value.prerequisiteIds) &&
|
|
73
|
+
isStringArray(value.successorIds)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function isLayers(value: unknown): value is string[][] {
|
|
78
|
+
return isBoundedArray(value, TOOL_DETAILS_MAX_ITEMS, (entry): entry is string[] => isStringArray(entry));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function isExecution(value: unknown): value is { nodes: ExecutionPlanNode[]; layers: string[][]; cycleIds: string[] } {
|
|
82
|
+
return (
|
|
83
|
+
isRecord(value) &&
|
|
84
|
+
isBoundedArray(value.nodes, TOOL_DETAILS_MAX_ITEMS, isExecutionPlanNode) &&
|
|
85
|
+
isLayers(value.layers) &&
|
|
86
|
+
isStringArray(value.cycleIds)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { TOOL_DETAILS_MAX_ITEMS, TOOL_DETAILS_ROW_OUTPUT_MAX_CHARACTERS } from "@danypops/papyrus";
|
|
2
|
+
import {
|
|
3
|
+
completeness,
|
|
4
|
+
isBoundedString,
|
|
5
|
+
isRecord,
|
|
6
|
+
PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
7
|
+
type ResultCompleteness,
|
|
8
|
+
type ToolDetailsBase,
|
|
9
|
+
} from "./shared.ts";
|
|
10
|
+
|
|
11
|
+
export interface ToolGateRow {
|
|
12
|
+
passed: boolean;
|
|
13
|
+
type: string;
|
|
14
|
+
target: string;
|
|
15
|
+
output: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface GateRunToolDetails extends ToolDetailsBase {
|
|
19
|
+
kind: "gate-run";
|
|
20
|
+
artifactId: string;
|
|
21
|
+
artifactTitle: string;
|
|
22
|
+
gates: ToolGateRow[];
|
|
23
|
+
completeness: ResultCompleteness;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createGateRunDetails(
|
|
27
|
+
operation: string,
|
|
28
|
+
artifactId: string,
|
|
29
|
+
artifactTitle: string,
|
|
30
|
+
gates: readonly ToolGateRow[],
|
|
31
|
+
): GateRunToolDetails {
|
|
32
|
+
const boundedGates = gates.slice(0, TOOL_DETAILS_MAX_ITEMS).map((gate) => ({
|
|
33
|
+
...gate,
|
|
34
|
+
output: gate.output.slice(0, TOOL_DETAILS_ROW_OUTPUT_MAX_CHARACTERS),
|
|
35
|
+
}));
|
|
36
|
+
return {
|
|
37
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
38
|
+
kind: "gate-run",
|
|
39
|
+
operation,
|
|
40
|
+
artifactId,
|
|
41
|
+
artifactTitle,
|
|
42
|
+
gates: boundedGates,
|
|
43
|
+
completeness: completeness(gates.length, boundedGates.length),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isGateRow(value: unknown): value is ToolGateRow {
|
|
48
|
+
return (
|
|
49
|
+
isRecord(value) &&
|
|
50
|
+
typeof value.passed === "boolean" &&
|
|
51
|
+
isBoundedString(value.type) &&
|
|
52
|
+
isBoundedString(value.target) &&
|
|
53
|
+
isBoundedString(value.output, TOOL_DETAILS_ROW_OUTPUT_MAX_CHARACTERS)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Artifact, TOOL_DETAILS_MAX_EDGES, TOOL_DETAILS_MAX_ITEMS } from "@danypops/papyrus";
|
|
2
|
+
import {
|
|
3
|
+
artifactSummary,
|
|
4
|
+
completeness,
|
|
5
|
+
isBoundedString,
|
|
6
|
+
isRecord,
|
|
7
|
+
PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
8
|
+
type ResultCompleteness,
|
|
9
|
+
type ToolArtifactSummary,
|
|
10
|
+
type ToolDetailsBase,
|
|
11
|
+
} from "./shared.ts";
|
|
12
|
+
|
|
13
|
+
export interface ToolGraphEdge {
|
|
14
|
+
from: string;
|
|
15
|
+
relation: string;
|
|
16
|
+
to: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface GraphToolDetails extends ToolDetailsBase {
|
|
20
|
+
kind: "graph";
|
|
21
|
+
nodes: ToolArtifactSummary[];
|
|
22
|
+
edges: ToolGraphEdge[];
|
|
23
|
+
nodeCompleteness: ResultCompleteness;
|
|
24
|
+
edgeCompleteness: ResultCompleteness;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createGraphDetails(operation: string, artifacts: readonly Artifact[], edges: readonly ToolGraphEdge[]): GraphToolDetails {
|
|
28
|
+
const nodes = artifacts.slice(0, TOOL_DETAILS_MAX_ITEMS).map(artifactSummary);
|
|
29
|
+
const boundedEdges = edges.slice(0, TOOL_DETAILS_MAX_EDGES).map((edge) => ({ ...edge }));
|
|
30
|
+
return {
|
|
31
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
32
|
+
kind: "graph",
|
|
33
|
+
operation,
|
|
34
|
+
nodes,
|
|
35
|
+
edges: boundedEdges,
|
|
36
|
+
nodeCompleteness: completeness(artifacts.length, nodes.length),
|
|
37
|
+
edgeCompleteness: completeness(edges.length, boundedEdges.length),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isGraphEdge(value: unknown): value is ToolGraphEdge {
|
|
42
|
+
return isRecord(value) && isBoundedString(value.from) && isBoundedString(value.relation) && isBoundedString(value.to);
|
|
43
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render-model/ -- typed, bounded Vehicle-result render details, one file per result-kind
|
|
3
|
+
* (already Strategy-shaped in spirit: one create-and-is-guard pair per kind), split out of the former
|
|
4
|
+
* single 827-line render-model.ts as part of a SOLID-audit-driven decomposition (see Doc
|
|
5
|
+
* "Modularity playbook: building-block-shaped TypeScript modules for papyrus/pi-papyrus" and the
|
|
6
|
+
* "pi-papyrus render-model.ts split" child of "Epic: Modularize papyrus/pi-papyrus god-files into
|
|
7
|
+
* building-block modules"). This file is the one place that assembles every kind into the
|
|
8
|
+
* PapyrusToolDetails union and validates a persisted one back (parsePapyrusToolDetails) -- the
|
|
9
|
+
* "wide internal, narrow public" shape: each sibling module owns one kind's own real complexity,
|
|
10
|
+
* this one only ever needs to know each kind's own type + guard, never re-derive it.
|
|
11
|
+
*/
|
|
12
|
+
import {
|
|
13
|
+
TOOL_DETAILS_BODY_MAX_CHARACTERS,
|
|
14
|
+
TOOL_DETAILS_MAX_EDGES,
|
|
15
|
+
TOOL_DETAILS_MAX_ITEMS,
|
|
16
|
+
TOOL_DETAILS_MAX_SERIALIZED_CHARACTERS,
|
|
17
|
+
} from "@danypops/papyrus";
|
|
18
|
+
import type { ArtifactListToolDetails, ArtifactToolDetails, TransitionToolDetails } from "./artifact.ts";
|
|
19
|
+
import { type DiscussionToolDetails, isDiscussionRoundSummary } from "./discussion.ts";
|
|
20
|
+
import { type ExecutionPlanToolDetails, isExecution, isExecutionPlanNode, isLayers } from "./execution-plan.ts";
|
|
21
|
+
import { type GateRunToolDetails, isGateRow } from "./gate-run.ts";
|
|
22
|
+
import { type GraphToolDetails, isGraphEdge } from "./graph.ts";
|
|
23
|
+
import type { LeaseToolDetails } from "./lease.ts";
|
|
24
|
+
import type { ErrorToolDetails, NoFocusToolDetails, PreviewToolDetails } from "./misc.ts";
|
|
25
|
+
import type { InvocationToolDetails, PlaybookInvocationToolDetails, PlaybookMissingArgumentsToolDetails } from "./playbook.ts";
|
|
26
|
+
import {
|
|
27
|
+
isArtifactSummary,
|
|
28
|
+
isBoundedArray,
|
|
29
|
+
isBoundedString,
|
|
30
|
+
isCompleteness,
|
|
31
|
+
isFocusAnnotation,
|
|
32
|
+
isRecord,
|
|
33
|
+
isStringArray,
|
|
34
|
+
isToolArtifact,
|
|
35
|
+
PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
36
|
+
} from "./shared.ts";
|
|
37
|
+
import {
|
|
38
|
+
isTaskBlockageSummary,
|
|
39
|
+
isTaskChecklistReviewSummary,
|
|
40
|
+
isTaskGateResultSummary,
|
|
41
|
+
type TaskCompletionToolDetails,
|
|
42
|
+
} from "./task-completion.ts";
|
|
43
|
+
|
|
44
|
+
export type PapyrusToolDetails =
|
|
45
|
+
| ArtifactToolDetails
|
|
46
|
+
| ArtifactListToolDetails
|
|
47
|
+
| TransitionToolDetails
|
|
48
|
+
| GraphToolDetails
|
|
49
|
+
| GateRunToolDetails
|
|
50
|
+
| InvocationToolDetails
|
|
51
|
+
| PreviewToolDetails
|
|
52
|
+
| ErrorToolDetails
|
|
53
|
+
| ExecutionPlanToolDetails
|
|
54
|
+
| PlaybookInvocationToolDetails
|
|
55
|
+
| PlaybookMissingArgumentsToolDetails
|
|
56
|
+
| DiscussionToolDetails
|
|
57
|
+
| TaskCompletionToolDetails
|
|
58
|
+
| NoFocusToolDetails
|
|
59
|
+
| LeaseToolDetails;
|
|
60
|
+
|
|
61
|
+
/** Validate renderer details restored from session history before using them as typed presentation state. */
|
|
62
|
+
export function parsePapyrusToolDetails(value: unknown): PapyrusToolDetails | undefined {
|
|
63
|
+
let serializedLength: number;
|
|
64
|
+
try {
|
|
65
|
+
serializedLength = JSON.stringify(value).length;
|
|
66
|
+
} catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if (
|
|
70
|
+
serializedLength > TOOL_DETAILS_MAX_SERIALIZED_CHARACTERS ||
|
|
71
|
+
!isRecord(value) ||
|
|
72
|
+
value.schemaVersion !== PAPYRUS_TOOL_DETAILS_SCHEMA ||
|
|
73
|
+
!isBoundedString(value.operation) ||
|
|
74
|
+
!isBoundedString(value.kind)
|
|
75
|
+
)
|
|
76
|
+
return undefined;
|
|
77
|
+
|
|
78
|
+
switch (value.kind) {
|
|
79
|
+
case "artifact":
|
|
80
|
+
return isToolArtifact(value.artifact) &&
|
|
81
|
+
isCompleteness(value.completeness) &&
|
|
82
|
+
(value.focus === undefined || isFocusAnnotation(value.focus))
|
|
83
|
+
? (value as unknown as ArtifactToolDetails)
|
|
84
|
+
: undefined;
|
|
85
|
+
case "artifact-list":
|
|
86
|
+
return isBoundedArray(value.rows, TOOL_DETAILS_MAX_ITEMS, isArtifactSummary) &&
|
|
87
|
+
Number.isSafeInteger(value.total) &&
|
|
88
|
+
Number(value.total) >= value.rows.length &&
|
|
89
|
+
isCompleteness(value.completeness)
|
|
90
|
+
? (value as unknown as ArtifactListToolDetails)
|
|
91
|
+
: undefined;
|
|
92
|
+
case "transition":
|
|
93
|
+
return isArtifactSummary(value.artifact) && isBoundedString(value.fromStatus) && isBoundedString(value.toStatus)
|
|
94
|
+
? (value as unknown as TransitionToolDetails)
|
|
95
|
+
: undefined;
|
|
96
|
+
case "graph":
|
|
97
|
+
return isBoundedArray(value.nodes, TOOL_DETAILS_MAX_ITEMS, isArtifactSummary) &&
|
|
98
|
+
isBoundedArray(value.edges, TOOL_DETAILS_MAX_EDGES, isGraphEdge) &&
|
|
99
|
+
isCompleteness(value.nodeCompleteness) &&
|
|
100
|
+
isCompleteness(value.edgeCompleteness)
|
|
101
|
+
? (value as unknown as GraphToolDetails)
|
|
102
|
+
: undefined;
|
|
103
|
+
case "gate-run":
|
|
104
|
+
return isBoundedString(value.artifactId) &&
|
|
105
|
+
isBoundedString(value.artifactTitle) &&
|
|
106
|
+
isBoundedArray(value.gates, TOOL_DETAILS_MAX_ITEMS, isGateRow) &&
|
|
107
|
+
isCompleteness(value.completeness)
|
|
108
|
+
? (value as unknown as GateRunToolDetails)
|
|
109
|
+
: undefined;
|
|
110
|
+
case "invocation": {
|
|
111
|
+
if (!isRecord(value.created)) return undefined;
|
|
112
|
+
return isBoundedString(value.runId) &&
|
|
113
|
+
isStringArray(value.created.tasks) &&
|
|
114
|
+
isStringArray(value.created.docs) &&
|
|
115
|
+
isStringArray(value.created.rules) &&
|
|
116
|
+
isStringArray(value.created.roots) &&
|
|
117
|
+
isCompleteness(value.completeness)
|
|
118
|
+
? (value as unknown as InvocationToolDetails)
|
|
119
|
+
: undefined;
|
|
120
|
+
}
|
|
121
|
+
case "preview":
|
|
122
|
+
return isBoundedString(value.title) &&
|
|
123
|
+
isBoundedString(value.content, TOOL_DETAILS_BODY_MAX_CHARACTERS) &&
|
|
124
|
+
isCompleteness(value.completeness)
|
|
125
|
+
? (value as unknown as PreviewToolDetails)
|
|
126
|
+
: undefined;
|
|
127
|
+
case "error":
|
|
128
|
+
return isBoundedString(value.code) && isBoundedString(value.message, TOOL_DETAILS_BODY_MAX_CHARACTERS)
|
|
129
|
+
? (value as unknown as ErrorToolDetails)
|
|
130
|
+
: undefined;
|
|
131
|
+
case "execution-plan":
|
|
132
|
+
return isBoundedArray(value.nodes, TOOL_DETAILS_MAX_ITEMS, isExecutionPlanNode) &&
|
|
133
|
+
isLayers(value.layers) &&
|
|
134
|
+
isStringArray(value.cycleIds) &&
|
|
135
|
+
isCompleteness(value.completeness)
|
|
136
|
+
? (value as unknown as ExecutionPlanToolDetails)
|
|
137
|
+
: undefined;
|
|
138
|
+
case "playbook-invocation": {
|
|
139
|
+
if (!isRecord(value.created)) return undefined;
|
|
140
|
+
return isBoundedString(value.playbookId) &&
|
|
141
|
+
isBoundedString(value.runId) &&
|
|
142
|
+
isStringArray(value.created.docs) &&
|
|
143
|
+
isStringArray(value.created.rules) &&
|
|
144
|
+
isStringArray(value.created.tasks) &&
|
|
145
|
+
isStringArray(value.rootTaskIds) &&
|
|
146
|
+
isBoundedString(value.entryTaskId) &&
|
|
147
|
+
isExecution(value.execution) &&
|
|
148
|
+
isCompleteness(value.completeness)
|
|
149
|
+
? (value as unknown as PlaybookInvocationToolDetails)
|
|
150
|
+
: undefined;
|
|
151
|
+
}
|
|
152
|
+
case "playbook-missing-arguments":
|
|
153
|
+
return isBoundedString(value.playbookId) && isStringArray(value.missingArguments)
|
|
154
|
+
? (value as unknown as PlaybookMissingArgumentsToolDetails)
|
|
155
|
+
: undefined;
|
|
156
|
+
case "discussion":
|
|
157
|
+
return (value.discussion === undefined || isArtifactSummary(value.discussion)) &&
|
|
158
|
+
isBoundedArray(value.rounds, TOOL_DETAILS_MAX_ITEMS, isDiscussionRoundSummary) &&
|
|
159
|
+
isCompleteness(value.completeness)
|
|
160
|
+
? (value as unknown as DiscussionToolDetails)
|
|
161
|
+
: undefined;
|
|
162
|
+
case "task-completion":
|
|
163
|
+
return isArtifactSummary(value.artifact) &&
|
|
164
|
+
isBoundedArray(value.gates, TOOL_DETAILS_MAX_ITEMS, isTaskGateResultSummary) &&
|
|
165
|
+
isBoundedArray(value.checklist, TOOL_DETAILS_MAX_ITEMS, isTaskChecklistReviewSummary) &&
|
|
166
|
+
typeof value.completed === "boolean" &&
|
|
167
|
+
(value.focused === undefined || isArtifactSummary(value.focused)) &&
|
|
168
|
+
isBoundedArray(value.blocked, TOOL_DETAILS_MAX_ITEMS, isTaskBlockageSummary) &&
|
|
169
|
+
isCompleteness(value.completeness)
|
|
170
|
+
? (value as unknown as TaskCompletionToolDetails)
|
|
171
|
+
: undefined;
|
|
172
|
+
case "no-focus":
|
|
173
|
+
return value as unknown as NoFocusToolDetails;
|
|
174
|
+
case "lease":
|
|
175
|
+
return isBoundedString(value.taskName) &&
|
|
176
|
+
isBoundedString(value.taskTitle) &&
|
|
177
|
+
isBoundedString(value.owner) &&
|
|
178
|
+
isBoundedString(value.claimedAt) &&
|
|
179
|
+
isBoundedString(value.leaseExpiresAt) &&
|
|
180
|
+
(value.heartbeatAt === undefined || isBoundedString(value.heartbeatAt)) &&
|
|
181
|
+
(value.note === undefined || isBoundedString(value.note))
|
|
182
|
+
? (value as unknown as LeaseToolDetails)
|
|
183
|
+
: undefined;
|
|
184
|
+
default:
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export {
|
|
190
|
+
type ArtifactListToolDetails,
|
|
191
|
+
type ArtifactToolDetails,
|
|
192
|
+
createArtifactDetails,
|
|
193
|
+
createArtifactListDetails,
|
|
194
|
+
createTransitionDetails,
|
|
195
|
+
type TransitionToolDetails,
|
|
196
|
+
} from "./artifact.ts";
|
|
197
|
+
export { createDiscussionDetails, type DiscussionRoundSummary, type DiscussionToolDetails } from "./discussion.ts";
|
|
198
|
+
export { createExecutionPlanDetails, type ExecutionPlanNode, type ExecutionPlanToolDetails } from "./execution-plan.ts";
|
|
199
|
+
export { createGateRunDetails, type GateRunToolDetails, type ToolGateRow } from "./gate-run.ts";
|
|
200
|
+
export { createGraphDetails, type GraphToolDetails, type ToolGraphEdge } from "./graph.ts";
|
|
201
|
+
export { createLeaseDetails, type LeaseToolDetails } from "./lease.ts";
|
|
202
|
+
export {
|
|
203
|
+
createErrorDetails,
|
|
204
|
+
createNoFocusDetails,
|
|
205
|
+
createPreviewDetails,
|
|
206
|
+
type ErrorToolDetails,
|
|
207
|
+
type NoFocusToolDetails,
|
|
208
|
+
type PreviewToolDetails,
|
|
209
|
+
} from "./misc.ts";
|
|
210
|
+
export {
|
|
211
|
+
createInvocationDetails,
|
|
212
|
+
createPlaybookInvocationDetails,
|
|
213
|
+
createPlaybookMissingArgumentsDetails,
|
|
214
|
+
type InvocationToolDetails,
|
|
215
|
+
type PlaybookInvocationCreated,
|
|
216
|
+
type PlaybookInvocationToolDetails,
|
|
217
|
+
type PlaybookMissingArgumentsToolDetails,
|
|
218
|
+
type ToolInvocationCreated,
|
|
219
|
+
} from "./playbook.ts";
|
|
220
|
+
export {
|
|
221
|
+
type ArtifactFocusAnnotation,
|
|
222
|
+
createModelContent,
|
|
223
|
+
type ModelContent,
|
|
224
|
+
PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
225
|
+
type ResultCompleteness,
|
|
226
|
+
type ToolArtifact,
|
|
227
|
+
type ToolArtifactSummary,
|
|
228
|
+
} from "./shared.ts";
|
|
229
|
+
export {
|
|
230
|
+
createTaskCompletionDetails,
|
|
231
|
+
type TaskBlockageSummary,
|
|
232
|
+
type TaskChecklistReviewSummary,
|
|
233
|
+
type TaskCompletionToolDetails,
|
|
234
|
+
type TaskGateResultSummary,
|
|
235
|
+
} from "./task-completion.ts";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { TOOL_DETAILS_FIELD_MAX_CHARACTERS } from "@danypops/papyrus";
|
|
2
|
+
import { PAPYRUS_TOOL_DETAILS_SCHEMA, type ToolDetailsBase } from "./shared.ts";
|
|
3
|
+
|
|
4
|
+
/** tasks.claim/heartbeat_lease/release_lease/lease's own name-first view -- deliberately never carries the raw lease token; the model channel (which needs the real token for a later heartbeat/release call) is a separate, independent channel from this persisted, human-facing one. */
|
|
5
|
+
export interface LeaseToolDetails extends ToolDetailsBase {
|
|
6
|
+
kind: "lease";
|
|
7
|
+
taskName: string;
|
|
8
|
+
taskTitle: string;
|
|
9
|
+
owner: string;
|
|
10
|
+
claimedAt: string;
|
|
11
|
+
leaseExpiresAt: string;
|
|
12
|
+
heartbeatAt?: string;
|
|
13
|
+
note?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createLeaseDetails(
|
|
17
|
+
operation: string,
|
|
18
|
+
lease: {
|
|
19
|
+
taskName: string;
|
|
20
|
+
taskTitle: string;
|
|
21
|
+
owner: string;
|
|
22
|
+
claimedAt: string;
|
|
23
|
+
leaseExpiresAt: string;
|
|
24
|
+
heartbeatAt?: string;
|
|
25
|
+
note?: string;
|
|
26
|
+
},
|
|
27
|
+
): LeaseToolDetails {
|
|
28
|
+
return {
|
|
29
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
30
|
+
kind: "lease",
|
|
31
|
+
operation,
|
|
32
|
+
taskName: lease.taskName,
|
|
33
|
+
taskTitle: lease.taskTitle,
|
|
34
|
+
owner: lease.owner,
|
|
35
|
+
claimedAt: lease.claimedAt,
|
|
36
|
+
leaseExpiresAt: lease.leaseExpiresAt,
|
|
37
|
+
...(lease.heartbeatAt ? { heartbeatAt: lease.heartbeatAt } : {}),
|
|
38
|
+
...(lease.note ? { note: lease.note.slice(0, TOOL_DETAILS_FIELD_MAX_CHARACTERS) } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { TOOL_DETAILS_BODY_MAX_CHARACTERS, TOOL_DETAILS_FIELD_MAX_CHARACTERS } from "@danypops/papyrus";
|
|
2
|
+
import { boundedText, PAPYRUS_TOOL_DETAILS_SCHEMA, type ResultCompleteness, type ToolDetailsBase } from "./shared.ts";
|
|
3
|
+
|
|
4
|
+
export interface PreviewToolDetails extends ToolDetailsBase {
|
|
5
|
+
kind: "preview";
|
|
6
|
+
title: string;
|
|
7
|
+
content: string;
|
|
8
|
+
completeness: ResultCompleteness;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createPreviewDetails(operation: string, title: string, content: string): PreviewToolDetails {
|
|
12
|
+
const bounded = boundedText(content, TOOL_DETAILS_BODY_MAX_CHARACTERS);
|
|
13
|
+
return {
|
|
14
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
15
|
+
kind: "preview",
|
|
16
|
+
operation,
|
|
17
|
+
title,
|
|
18
|
+
content: bounded.value,
|
|
19
|
+
completeness: bounded.completeness,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ErrorToolDetails extends ToolDetailsBase {
|
|
24
|
+
kind: "error";
|
|
25
|
+
code: string;
|
|
26
|
+
message: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createErrorDetails(operation: string, code: string, message: string): ErrorToolDetails {
|
|
30
|
+
return {
|
|
31
|
+
schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
|
|
32
|
+
kind: "error",
|
|
33
|
+
operation,
|
|
34
|
+
code: code.slice(0, TOOL_DETAILS_FIELD_MAX_CHARACTERS),
|
|
35
|
+
message: message.slice(0, TOOL_DETAILS_BODY_MAX_CHARACTERS),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface NoFocusToolDetails extends ToolDetailsBase {
|
|
40
|
+
kind: "no-focus";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createNoFocusDetails(operation: string): NoFocusToolDetails {
|
|
44
|
+
return { schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA, kind: "no-focus", operation };
|
|
45
|
+
}
|