@danypops/pi-papyrus 0.57.10 → 0.58.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/index.ts
CHANGED
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
automaticPauseReason,
|
|
51
51
|
shouldResumeFocusOnHumanInput,
|
|
52
52
|
} from "./task/active-task-continuation.ts";
|
|
53
|
-
import { emitTaskFocusEvent, setTaskFocusEventBus } from "./task/task-focus-events.ts";
|
|
53
|
+
import { emitTaskFocusEvent, extractDeclaredEffort, setTaskFocusEventBus } from "./task/task-focus-events.ts";
|
|
54
54
|
import { TASK_STATUS_PRESENTATION, taskTreeConnector } from "./task/task-presentation.ts";
|
|
55
55
|
import { buildTaskWidgetProjection, type TaskWidgetProjection } from "./task/task-widget.ts";
|
|
56
56
|
import { measure as artifactCardMeasure } from "./tool-rendering/artifact-card.ts";
|
|
@@ -492,7 +492,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
492
492
|
session_id: sessionId,
|
|
493
493
|
...sessionSecretField(sessionId),
|
|
494
494
|
});
|
|
495
|
-
emitTaskFocusEvent({ taskId: paused.artifact.id, sessionId, status: "paused" });
|
|
495
|
+
emitTaskFocusEvent({ taskId: paused.artifact.id, sessionId, status: "paused", effort: extractDeclaredEffort(paused.artifact.extra) });
|
|
496
496
|
if (ctx.hasUI) ctx.ui.notify(`Papyrus task driving paused: ${decision.reason}. Human input resumes it automatically.`, "warning");
|
|
497
497
|
}
|
|
498
498
|
} catch {
|
|
@@ -896,7 +896,12 @@ export default async function (pi: ExtensionAPI) {
|
|
|
896
896
|
session_id: sessionId,
|
|
897
897
|
...sessionSecretField(sessionId),
|
|
898
898
|
});
|
|
899
|
-
emitTaskFocusEvent({
|
|
899
|
+
emitTaskFocusEvent({
|
|
900
|
+
taskId: focus.artifact.id,
|
|
901
|
+
sessionId,
|
|
902
|
+
status: "unpaused",
|
|
903
|
+
effort: extractDeclaredEffort(focus.artifact.extra),
|
|
904
|
+
});
|
|
900
905
|
}
|
|
901
906
|
} catch {
|
|
902
907
|
// The daemon may be unavailable during startup, reload, or shutdown.
|
|
@@ -3,12 +3,31 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
|
|
4
4
|
export type TaskFocusStatus = "focused" | "paused" | "unpaused" | "cleared";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* A Task's own declared effort convention (`extra.effort`) -- Papyrus's core schema and
|
|
8
|
+
* validation know nothing about this value; it is genuine free-form `extra` JSON, the same
|
|
9
|
+
* mechanism `gates`/`checklist` already use. This module only reads it, opaquely, to relay it
|
|
10
|
+
* on the task-focus broadcast when present; Papyrus never assigns or requires it.
|
|
11
|
+
*/
|
|
12
|
+
export const TASK_DECLARED_EFFORT_LEVELS = ["low", "medium", "high"] as const;
|
|
13
|
+
export type TaskDeclaredEffort = (typeof TASK_DECLARED_EFFORT_LEVELS)[number];
|
|
14
|
+
|
|
15
|
+
/** Returns the task's own declared `extra.effort` when it is one of the recognized values, or undefined otherwise -- an absent or malformed value is silently omitted, never guessed or defaulted. */
|
|
16
|
+
export function extractDeclaredEffort(extra: Record<string, unknown> | undefined): TaskDeclaredEffort | undefined {
|
|
17
|
+
const value = extra?.effort;
|
|
18
|
+
return typeof value === "string" && (TASK_DECLARED_EFFORT_LEVELS as readonly string[]).includes(value)
|
|
19
|
+
? (value as TaskDeclaredEffort)
|
|
20
|
+
: undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
6
23
|
export interface TaskFocusEvent {
|
|
7
24
|
schema: typeof PAPYRUS_TASK_FOCUS_SCHEMA;
|
|
8
25
|
taskId: string | null;
|
|
9
26
|
sessionId?: string;
|
|
10
27
|
status: TaskFocusStatus;
|
|
11
28
|
observedAt: number;
|
|
29
|
+
/** The focused task's own declared `extra.effort`, when it has one -- omitted (never null) otherwise, matching this payload's existing minimalism. */
|
|
30
|
+
effort?: TaskDeclaredEffort;
|
|
12
31
|
}
|
|
13
32
|
|
|
14
33
|
export interface TaskFocusEventInput {
|
|
@@ -16,14 +35,16 @@ export interface TaskFocusEventInput {
|
|
|
16
35
|
sessionId?: string;
|
|
17
36
|
status: TaskFocusStatus;
|
|
18
37
|
observedAt?: number;
|
|
38
|
+
effort?: TaskDeclaredEffort;
|
|
19
39
|
}
|
|
20
40
|
|
|
21
41
|
/**
|
|
22
42
|
* Pure event builder, mirroring buildContextInjection's shape: no task title, body, or any other
|
|
23
|
-
* artifact content -- only the id, session, lifecycle status,
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* token-cost router can correlate their
|
|
43
|
+
* artifact content -- only the id, session, lifecycle status, timestamp, and (when the task
|
|
44
|
+
* declares one) its own bounded effort enum, which are already public metadata a caller with the
|
|
45
|
+
* id could look up directly. This is the payload emitted on papyrus.task-focus.v1, the analogue
|
|
46
|
+
* of papyrus.context-injection.v1, so extensions such as a token-cost router can correlate their
|
|
47
|
+
* own telemetry with the currently focused task -- and now its declared effort -- without
|
|
27
48
|
* Papyrus depending on them.
|
|
28
49
|
*/
|
|
29
50
|
export function buildTaskFocusEvent(input: TaskFocusEventInput): TaskFocusEvent {
|
|
@@ -35,6 +56,7 @@ export function buildTaskFocusEvent(input: TaskFocusEventInput): TaskFocusEvent
|
|
|
35
56
|
status: input.status,
|
|
36
57
|
observedAt: input.observedAt ?? Date.now(),
|
|
37
58
|
...(input.sessionId === undefined ? {} : { sessionId: input.sessionId }),
|
|
59
|
+
...(input.effort === undefined ? {} : { effort: input.effort }),
|
|
38
60
|
};
|
|
39
61
|
}
|
|
40
62
|
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import { callService } from "../service-client.ts";
|
|
26
26
|
import { sessionSecretField } from "../session-identity.ts";
|
|
27
27
|
import { showTaskDetails } from "./task-detail-view.ts";
|
|
28
|
-
import { emitTaskFocusEvent } from "./task-focus-events.ts";
|
|
28
|
+
import { emitTaskFocusEvent, extractDeclaredEffort } from "./task-focus-events.ts";
|
|
29
29
|
import { showTaskGraph } from "./task-graph.ts";
|
|
30
30
|
|
|
31
31
|
export { taskDetailsText } from "./task-detail-format.ts";
|
|
@@ -313,7 +313,7 @@ export async function showTasks(ctx: ExtensionCommandContext): Promise<void> {
|
|
|
313
313
|
session_id: sessionId,
|
|
314
314
|
...sessionSecretField(sessionId),
|
|
315
315
|
});
|
|
316
|
-
emitTaskFocusEvent({ taskId: focused.id, sessionId, status: "focused" });
|
|
316
|
+
emitTaskFocusEvent({ taskId: focused.id, sessionId, status: "focused", effort: extractDeclaredEffort(focused.extra) });
|
|
317
317
|
ctx.ui.notify(`Active: ${action.row.title}`, "info");
|
|
318
318
|
} catch (error) {
|
|
319
319
|
ctx.ui.notify(`Focus failed: ${error instanceof Error ? error.message : error}`, "error");
|
|
@@ -336,7 +336,12 @@ export async function showTasks(ctx: ExtensionCommandContext): Promise<void> {
|
|
|
336
336
|
session_id: sessionId,
|
|
337
337
|
...sessionSecretField(sessionId),
|
|
338
338
|
});
|
|
339
|
-
emitTaskFocusEvent({
|
|
339
|
+
emitTaskFocusEvent({
|
|
340
|
+
taskId: result.artifact.id,
|
|
341
|
+
sessionId,
|
|
342
|
+
status: choice === "Pause focus" ? "paused" : "unpaused",
|
|
343
|
+
effort: extractDeclaredEffort(result.artifact.extra),
|
|
344
|
+
});
|
|
340
345
|
}
|
|
341
346
|
ctx.ui.notify(choice === "Clear focus" ? "Task focus cleared" : choice, "info");
|
|
342
347
|
} catch (error) {
|
|
@@ -35,7 +35,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
35
35
|
import { discussLiveFollowUp } from "../discuss/discuss-live-follow-up.ts";
|
|
36
36
|
import { currentVehicleClientTarget } from "../service-client.ts";
|
|
37
37
|
import { sessionSecretField } from "../session-identity.ts";
|
|
38
|
-
import { emitTaskFocusEvent } from "../task/task-focus-events.ts";
|
|
38
|
+
import { emitTaskFocusEvent, extractDeclaredEffort } from "../task/task-focus-events.ts";
|
|
39
39
|
import { recordRenderDiagnostic, shapeFingerprint } from "./render-diagnostics.ts";
|
|
40
40
|
import { papyrusVehiclePresentations, papyrusVehicleRenderers } from "./vehicle-artifact-renderers.ts";
|
|
41
41
|
|
|
@@ -210,14 +210,18 @@ export function registerNotesVehicle(pi: ExtensionAPI): Promise<RegisteredPiVehi
|
|
|
210
210
|
// timestamp against when registration actually completed.
|
|
211
211
|
recordRenderDiagnostic({ event: "invoked", operation: descriptor.name, output: shapeFingerprint(output) });
|
|
212
212
|
if (descriptor.name === "tasks.focus") {
|
|
213
|
-
const artifact = output as { id: string } | undefined;
|
|
214
|
-
if (artifact?.id) emitTaskFocusEvent({ taskId: artifact.id, status: "focused" });
|
|
213
|
+
const artifact = output as { id: string; extra?: Record<string, unknown> } | undefined;
|
|
214
|
+
if (artifact?.id) emitTaskFocusEvent({ taskId: artifact.id, status: "focused", effort: extractDeclaredEffort(artifact.extra) });
|
|
215
215
|
return;
|
|
216
216
|
}
|
|
217
217
|
if (descriptor.name === "tasks.pause" || descriptor.name === "tasks.unpause") {
|
|
218
|
-
const focus = output as { artifact: { id: string } } | undefined;
|
|
218
|
+
const focus = output as { artifact: { id: string; extra?: Record<string, unknown> } } | undefined;
|
|
219
219
|
if (focus?.artifact?.id)
|
|
220
|
-
emitTaskFocusEvent({
|
|
220
|
+
emitTaskFocusEvent({
|
|
221
|
+
taskId: focus.artifact.id,
|
|
222
|
+
status: descriptor.name === "tasks.pause" ? "paused" : "unpaused",
|
|
223
|
+
effort: extractDeclaredEffort(focus.artifact.extra),
|
|
224
|
+
});
|
|
221
225
|
return;
|
|
222
226
|
}
|
|
223
227
|
if (descriptor.name === "tasks.clear_focus") {
|
package/package.json
CHANGED