@danypops/papyrus 0.33.0 → 0.33.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/index.ts +20 -0
- package/extension/src/service-client.ts +6 -22
- package/package.json +2 -2
- package/src/constants.ts +6 -0
package/extension/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
TASK_DRIVER_MAX_UNCHANGED_TURNS,
|
|
17
17
|
PAPYRUS_CONTEXT_INJECTION_CHANNEL,
|
|
18
18
|
CONTEXT_ESTIMATE_CHARACTERS_PER_TOKEN,
|
|
19
|
+
TASK_WIDGET_POLL_INTERVAL_MS,
|
|
19
20
|
} from "../../src/constants.ts";
|
|
20
21
|
import type { Artifact } from "../../src/domain/artifact.ts";
|
|
21
22
|
import type { GateResult } from "../../src/domain/gate.ts";
|
|
@@ -102,6 +103,7 @@ export class TaskOverlay {
|
|
|
102
103
|
private snapshot: TaskGraph = { nodes: [], rootIds: [] };
|
|
103
104
|
private projectRoot: string | undefined;
|
|
104
105
|
private sessionId: string | undefined;
|
|
106
|
+
private pollTimer: ReturnType<typeof setInterval> | undefined;
|
|
105
107
|
|
|
106
108
|
setUI(ctx: ExtensionUIContext): void {
|
|
107
109
|
if (ctx !== this.uiCtx) {
|
|
@@ -175,7 +177,24 @@ export class TaskOverlay {
|
|
|
175
177
|
return renderTaskWidgetLines(theme, buildTaskWidgetProjection(this.snapshot), width);
|
|
176
178
|
}
|
|
177
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Fallback for a Task mutation no event announces -- the CLI run directly from a shell, or
|
|
182
|
+
* a second concurrent Pi session against the same daemon. Idempotent: a second call is a
|
|
183
|
+
* no-op rather than starting a competing timer.
|
|
184
|
+
*/
|
|
185
|
+
startPolling(intervalMs: number = TASK_WIDGET_POLL_INTERVAL_MS): void {
|
|
186
|
+
if (this.pollTimer) return;
|
|
187
|
+
this.pollTimer = setInterval(() => { void this.refresh(); }, intervalMs);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
stopPolling(): void {
|
|
191
|
+
if (!this.pollTimer) return;
|
|
192
|
+
clearInterval(this.pollTimer);
|
|
193
|
+
this.pollTimer = undefined;
|
|
194
|
+
}
|
|
195
|
+
|
|
178
196
|
dispose(): void {
|
|
197
|
+
this.stopPolling();
|
|
179
198
|
this.uiCtx?.setWidget(WIDGET_KEY, undefined);
|
|
180
199
|
this.registered = false;
|
|
181
200
|
this.tui = undefined;
|
|
@@ -549,6 +568,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
549
568
|
overlay.setProjectRoot(ctx.cwd);
|
|
550
569
|
overlay.setSessionId(ctx.sessionManager.getSessionId());
|
|
551
570
|
await overlay.refresh();
|
|
571
|
+
overlay.startPolling(TASK_WIDGET_POLL_INTERVAL_MS);
|
|
552
572
|
});
|
|
553
573
|
|
|
554
574
|
pi.on("session_before_compact", () => { taskContinuation.onCompaction(); });
|
|
@@ -1,45 +1,29 @@
|
|
|
1
|
+
import { createRetryingClient, type RetryingClient } from "@danypops/daemon-kit/pi-client";
|
|
1
2
|
import { connectPapyrusClient, type PapyrusClient } from "../../src/client.ts";
|
|
2
3
|
import type { OperationName } from "../../src/service.ts";
|
|
3
4
|
|
|
4
5
|
type ClientConnector = () => Promise<PapyrusClient>;
|
|
5
6
|
|
|
6
7
|
let connector: ClientConnector = () => connectPapyrusClient();
|
|
7
|
-
|
|
8
|
+
const client: RetryingClient<PapyrusClient> = createRetryingClient<PapyrusClient>(() => connector(), { label: "Papyrus" });
|
|
8
9
|
|
|
9
10
|
export async function papyrusClient(): Promise<PapyrusClient> {
|
|
10
|
-
|
|
11
|
-
cached = await connector();
|
|
12
|
-
return cached;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function staleConnection(error: unknown): boolean {
|
|
16
|
-
if (error instanceof TypeError) return true;
|
|
17
|
-
if (!(error instanceof Error)) return false;
|
|
18
|
-
if (error.name === "AbortError" || error.name === "TimeoutError") return true;
|
|
19
|
-
return /fetch failed|network|socket|ECONNRESET|ECONNREFUSED|connection refused/i.test(error.message);
|
|
11
|
+
return client.call(async (resolved) => resolved);
|
|
20
12
|
}
|
|
21
13
|
|
|
22
14
|
export async function callService<Input extends Record<string, unknown>, Output>(
|
|
23
15
|
operation: OperationName,
|
|
24
16
|
input: Input,
|
|
25
17
|
): Promise<Output> {
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
return await (await papyrusClient()).call<Input, Output>(operation, input);
|
|
29
|
-
} catch (error) {
|
|
30
|
-
cached = undefined;
|
|
31
|
-
if (attempt === 1 || !staleConnection(error)) throw error;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
throw new Error("Papyrus daemon client retry exhausted");
|
|
18
|
+
return client.call((resolved) => resolved.call<Input, Output>(operation, input));
|
|
35
19
|
}
|
|
36
20
|
|
|
37
21
|
export function setPapyrusClientConnectorForTests(value: ClientConnector): void {
|
|
38
|
-
cached = undefined;
|
|
39
22
|
connector = value;
|
|
23
|
+
client.reset();
|
|
40
24
|
}
|
|
41
25
|
|
|
42
26
|
export function resetPapyrusClientForTests(): void {
|
|
43
|
-
cached = undefined;
|
|
44
27
|
connector = () => connectPapyrusClient();
|
|
28
|
+
client.reset();
|
|
45
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/papyrus",
|
|
3
|
-
"version": "0.33.
|
|
3
|
+
"version": "0.33.2",
|
|
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"],
|
|
@@ -42,6 +42,6 @@
|
|
|
42
42
|
"files": ["src", "extension", "README.md"],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"beautiful-mermaid": "1.1.3",
|
|
45
|
-
"@danypops/daemon-kit": "^0.
|
|
45
|
+
"@danypops/daemon-kit": "^0.4.0"
|
|
46
46
|
}
|
|
47
47
|
}
|
package/src/constants.ts
CHANGED
|
@@ -65,6 +65,12 @@ export const TASK_CONTEXT_CURRENT_LIMIT = 3;
|
|
|
65
65
|
export const TASK_CONTEXT_REJECTED_LIMIT = 3;
|
|
66
66
|
export const TASK_WIDGET_OPEN_LIMIT = 3;
|
|
67
67
|
export const TASK_DETAIL_MIN_VISIBLE_LINES = 8;
|
|
68
|
+
/**
|
|
69
|
+
* Event-triggered refresh (tool_execution_end, session_compact/tree) can't see a Task
|
|
70
|
+
* mutation from outside this Pi session -- another concurrent session, or the CLI run
|
|
71
|
+
* directly from a shell. This bounded poll is the fallback for exactly that gap.
|
|
72
|
+
*/
|
|
73
|
+
export const TASK_WIDGET_POLL_INTERVAL_MS = 20_000;
|
|
68
74
|
export const TASK_DETAIL_MAX_VISIBLE_LINES = 24;
|
|
69
75
|
export const TASK_DETAIL_RESERVED_ROWS = 8;
|
|
70
76
|
export const TASK_DETAIL_HORIZONTAL_PAN_COLUMNS = 4;
|