@marcoscale98/pi-extensible-workflows 5.14.1-fork.1
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/CHANGELOG.md +536 -0
- package/README.md +64 -0
- package/dist/src/agent-execution.d.ts +338 -0
- package/dist/src/agent-execution.js +1633 -0
- package/dist/src/background-widget.d.ts +74 -0
- package/dist/src/background-widget.js +1340 -0
- package/dist/src/budget.d.ts +38 -0
- package/dist/src/budget.js +188 -0
- package/dist/src/changelog.d.ts +6 -0
- package/dist/src/changelog.js +112 -0
- package/dist/src/decoders.d.ts +96 -0
- package/dist/src/decoders.js +629 -0
- package/dist/src/execution.d.ts +19 -0
- package/dist/src/execution.js +749 -0
- package/dist/src/herdr.d.ts +53 -0
- package/dist/src/herdr.js +270 -0
- package/dist/src/host-delivery.d.ts +95 -0
- package/dist/src/host-delivery.js +376 -0
- package/dist/src/host-navigator.d.ts +83 -0
- package/dist/src/host-navigator.js +1087 -0
- package/dist/src/host-phases.d.ts +89 -0
- package/dist/src/host-phases.js +320 -0
- package/dist/src/host-recovery.d.ts +141 -0
- package/dist/src/host-recovery.js +356 -0
- package/dist/src/host-runtime.d.ts +78 -0
- package/dist/src/host-runtime.js +378 -0
- package/dist/src/host-view.d.ts +116 -0
- package/dist/src/host-view.js +868 -0
- package/dist/src/host.d.ts +40 -0
- package/dist/src/host.js +1641 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.js +13725 -0
- package/dist/src/index.js.map +6 -0
- package/dist/src/io.d.ts +14 -0
- package/dist/src/io.js +82 -0
- package/dist/src/package-identity.d.ts +4 -0
- package/dist/src/package-identity.js +6 -0
- package/dist/src/paths.d.ts +14 -0
- package/dist/src/paths.js +51 -0
- package/dist/src/persistence.d.ts +5 -0
- package/dist/src/persistence.js +5 -0
- package/dist/src/pi-runtime-adapter.d.ts +27 -0
- package/dist/src/pi-runtime-adapter.js +243 -0
- package/dist/src/pi-runtime-runner.d.ts +42 -0
- package/dist/src/pi-runtime-runner.js +505 -0
- package/dist/src/registry.d.ts +48 -0
- package/dist/src/registry.js +379 -0
- package/dist/src/retention.d.ts +17 -0
- package/dist/src/retention.js +195 -0
- package/dist/src/runtime/agent-runner.d.ts +143 -0
- package/dist/src/runtime/agent-runner.js +12 -0
- package/dist/src/runtime/index.d.ts +2 -0
- package/dist/src/runtime/index.js +2 -0
- package/dist/src/runtime/namespace.d.ts +14 -0
- package/dist/src/runtime/namespace.js +1 -0
- package/dist/src/runtime/workflow-result.d.ts +2 -0
- package/dist/src/runtime/workflow-result.js +6 -0
- package/dist/src/session-handoff.d.ts +2 -0
- package/dist/src/session-handoff.js +82 -0
- package/dist/src/session-lease.d.ts +12 -0
- package/dist/src/session-lease.js +180 -0
- package/dist/src/store.d.ts +95 -0
- package/dist/src/store.js +951 -0
- package/dist/src/subagent-manager-handle.d.ts +4 -0
- package/dist/src/subagent-manager-handle.js +6 -0
- package/dist/src/tool-timing.d.ts +12 -0
- package/dist/src/tool-timing.js +33 -0
- package/dist/src/trajectory-contracts.d.ts +8 -0
- package/dist/src/trajectory-contracts.js +9 -0
- package/dist/src/trajectory-host-handle.d.ts +12 -0
- package/dist/src/trajectory-host-handle.js +6 -0
- package/dist/src/trajectory.d.ts +128 -0
- package/dist/src/trajectory.js +594 -0
- package/dist/src/types.d.ts +960 -0
- package/dist/src/types.js +60 -0
- package/dist/src/utils.d.ts +57 -0
- package/dist/src/utils.js +225 -0
- package/dist/src/validation.d.ts +29 -0
- package/dist/src/validation.js +1100 -0
- package/dist/src/workflow-artifacts.d.ts +14 -0
- package/dist/src/workflow-artifacts.js +40 -0
- package/dist/starter/index.d.ts +1 -0
- package/dist/starter/index.js +103 -0
- package/dist/starter/index.js.map +6 -0
- package/dist/starter/review-loop.d.ts +72 -0
- package/dist/starter/review-loop.js +43 -0
- package/dist/starter/roles/developer.md +6 -0
- package/dist/starter/roles/oracle.md +18 -0
- package/dist/starter/roles/researcher.md +20 -0
- package/dist/starter/roles/reviewer.md +19 -0
- package/dist/starter/roles/scout.md +18 -0
- package/dist/subagents/index.d.ts +8 -0
- package/dist/subagents/index.js +8021 -0
- package/dist/subagents/index.js.map +6 -0
- package/dist/subagents/src/contracts.d.ts +173 -0
- package/dist/subagents/src/contracts.js +68 -0
- package/dist/subagents/src/decode.d.ts +15 -0
- package/dist/subagents/src/decode.js +182 -0
- package/dist/subagents/src/index.d.ts +12 -0
- package/dist/subagents/src/index.js +180 -0
- package/dist/subagents/src/manager.d.ts +3 -0
- package/dist/subagents/src/manager.js +1636 -0
- package/dist/subagents/src/navigator.d.ts +5 -0
- package/dist/subagents/src/navigator.js +627 -0
- package/dist/subagents/src/view.d.ts +70 -0
- package/dist/subagents/src/view.js +387 -0
- package/dist/subagents/src/worktree.d.ts +34 -0
- package/dist/subagents/src/worktree.js +68 -0
- package/dist/trajectory/assets/favicon.png +0 -0
- package/dist/trajectory/assets/index.html +772 -0
- package/dist/trajectory/assets/marked.min.js +6 -0
- package/dist/trajectory/assets/morphdom.min.js +1 -0
- package/dist/trajectory/index.d.ts +5 -0
- package/dist/trajectory/index.js +3459 -0
- package/dist/trajectory/index.js.map +6 -0
- package/dist/trajectory/src/assets/favicon.png +0 -0
- package/dist/trajectory/src/assets/index.html +772 -0
- package/dist/trajectory/src/assets/marked.min.js +6 -0
- package/dist/trajectory/src/assets/morphdom.min.js +1 -0
- package/dist/trajectory/src/export.d.ts +25 -0
- package/dist/trajectory/src/export.js +80 -0
- package/dist/trajectory/src/index.d.ts +26 -0
- package/dist/trajectory/src/index.js +732 -0
- package/dist/trajectory/src/server.d.ts +7 -0
- package/dist/trajectory/src/server.js +2534 -0
- package/dist/trajectory/src/server.js.map +6 -0
- package/examples/advanced-patterns/auto-model-router.mjs +78 -0
- package/examples/workflow-extension-template/README.md +37 -0
- package/examples/workflow-extension-template/extension.test.mjs +59 -0
- package/examples/workflow-extension-template/index.js +51 -0
- package/examples/workflow-extension-template/roles/reviewer.md +4 -0
- package/package.json +105 -0
- package/skills/pi-extensible-workflows/SKILL.md +160 -0
- package/src/agent-execution.ts +1533 -0
- package/src/background-widget.ts +1496 -0
- package/src/budget.ts +109 -0
- package/src/changelog.ts +114 -0
- package/src/decoders.ts +567 -0
- package/src/execution.ts +652 -0
- package/src/herdr.ts +240 -0
- package/src/host-delivery.ts +346 -0
- package/src/host-navigator.ts +871 -0
- package/src/host-phases.ts +264 -0
- package/src/host-recovery.ts +333 -0
- package/src/host-runtime.ts +373 -0
- package/src/host-view.ts +828 -0
- package/src/host.ts +1502 -0
- package/src/index.ts +29 -0
- package/src/io.ts +73 -0
- package/src/package-identity.ts +7 -0
- package/src/paths.ts +49 -0
- package/src/persistence.ts +11 -0
- package/src/pi-runtime-adapter.ts +247 -0
- package/src/pi-runtime-runner.ts +436 -0
- package/src/registry.ts +321 -0
- package/src/retention.ts +178 -0
- package/src/runtime/agent-runner.ts +158 -0
- package/src/runtime/index.ts +2 -0
- package/src/runtime/namespace.ts +16 -0
- package/src/runtime/workflow-result.ts +8 -0
- package/src/session-handoff.ts +78 -0
- package/src/session-lease.ts +125 -0
- package/src/store.ts +832 -0
- package/src/subagent-manager-handle.ts +8 -0
- package/src/tool-timing.ts +47 -0
- package/src/trajectory-contracts.ts +13 -0
- package/src/trajectory-host-handle.ts +16 -0
- package/src/trajectory.ts +543 -0
- package/src/types.ts +374 -0
- package/src/utils.ts +192 -0
- package/src/validation.ts +910 -0
- package/src/workflow-artifacts.ts +35 -0
- package/starter/index.ts +35 -0
- package/starter/prompts/council.md +16 -0
- package/starter/prompts/deep-research.md +16 -0
- package/starter/prompts/oracle.md +14 -0
- package/starter/prompts/parallel-review.md +22 -0
- package/starter/prompts/parallel-scout.md +16 -0
- package/starter/prompts/review-loop.md +14 -0
- package/starter/prompts/review.md +14 -0
- package/starter/prompts/scout.md +14 -0
- package/starter/review-loop.ts +73 -0
- package/starter/roles/developer.md +6 -0
- package/starter/roles/oracle.md +18 -0
- package/starter/roles/researcher.md +20 -0
- package/starter/roles/reviewer.md +19 -0
- package/starter/roles/scout.md +18 -0
- package/subagents/README.md +145 -0
- package/subagents/index.ts +8 -0
- package/subagents/src/contracts.ts +176 -0
- package/subagents/src/decode.ts +158 -0
- package/subagents/src/index.ts +194 -0
- package/subagents/src/manager.ts +1560 -0
- package/subagents/src/navigator.ts +488 -0
- package/subagents/src/view.ts +376 -0
- package/subagents/src/worktree.ts +99 -0
- package/trajectory/index.ts +5 -0
- package/trajectory/src/assets/favicon.png +0 -0
- package/trajectory/src/assets/index.html +772 -0
- package/trajectory/src/assets/marked.min.js +6 -0
- package/trajectory/src/assets/morphdom.min.js +1 -0
- package/trajectory/src/export.ts +85 -0
- package/trajectory/src/index.ts +604 -0
- package/trajectory/src/server.ts +447 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export * from "./package-identity.js";
|
|
3
|
+
export { createToolTimingExtension, TOOL_TIMING_ENTRY_TYPE } from "./tool-timing.js";
|
|
4
|
+
export type { ToolTimingEntry } from "./tool-timing.js";
|
|
5
|
+
export * from "./utils.js";
|
|
6
|
+
export { canonicalPath, sameFilesystemPath } from "./paths.js";
|
|
7
|
+
export * from "./budget.js";
|
|
8
|
+
export * from "./validation.js";
|
|
9
|
+
export * from "./registry.js";
|
|
10
|
+
export * from "./execution.js";
|
|
11
|
+
export * from "./host.js";
|
|
12
|
+
export * from "./workflow-artifacts.js";
|
|
13
|
+
export { formatCost } from "./background-widget.js";
|
|
14
|
+
export { agentActionLabels, formatAgentDetail, navigatorAttentionSortByState, themeWorkflowProgressStyles, visibleStandaloneAgentAttemptActions } from "./host-view.js";
|
|
15
|
+
export type { AgentDetailPresentation, WorkflowProgressStyles } from "./host-view.js";
|
|
16
|
+
export { default } from "./host.js";
|
|
17
|
+
export { acquireSessionLease, hasLiveSessionLease, listPersistedSessionIds, projectSessionsDirectory, projectStorageKey, RunStore, runsDirectory, SessionLease, structuralPath } from "./persistence.js";
|
|
18
|
+
export type { AwaitingCheckpoint, CompletedOperation, PendingWorkflowDecision, PersistedOwnershipNode, PersistedRun, RunSummary, RunSummaryAgent, RunSummaryArtifacts, WorktreeReference } from "./persistence.js";
|
|
19
|
+
export { FairAgentScheduler, WorkflowAgentExecutor, createLocalPiSession, createLocalWorkflowAgentSession, localAgentTransport, prepareAgentSetupForInspection } from "./agent-execution.js";
|
|
20
|
+
export type { AgentAttempt, AgentBudgetHooks, AgentExecutionOptions, AgentExecutionResult, AgentExecutionRoot, AgentInspectionMode, AgentProgress, AgentProviderFailure, AgentProviderRecovery, AgentToolCallProgress, AgentSetup, AgentSetupContext, AgentSetupHook, AgentTransport, AgentTransportContext, PiPromptInspection, PiResourceInspection, PiRuntimeLaunchInfo, PiSession, PreparedAgentSession, PreparedAgentSetup, RegisteredAgentSetupHook, SessionInput, WorkflowAgentMessage, WorkflowAgentSession, WorkflowAgentSessionEvent, WorkflowAgentSessionReference, WorkflowAgentSessionState, WorkflowAgentSessionStats, WorkflowAgentTurnResult } from "./agent-execution.js";
|
|
21
|
+
export { createLiveSessionHandoff } from "./session-handoff.js";
|
|
22
|
+
export { createPiRuntimeSessionAdapter, createRuntimeHandoffAdapter, isTurnBoundaryEnd, isTurnBoundaryStart, isTurnEnd, normalizePiMessage, normalizePiSessionEvent, runtimeProgressToAgentProgress } from "./pi-runtime-adapter.js";
|
|
23
|
+
export type { PiRuntimeProgressObservation, PiRuntimeSessionAdapter } from "./pi-runtime-adapter.js";
|
|
24
|
+
export { PiRuntimeAgentRunner, createPiRuntimeAgentRunner, isRuntimeAgentProviderError, normalizePiRuntimeError } from "./pi-runtime-runner.js";
|
|
25
|
+
export type { PiRuntimeAgentRunnerCallbacks, PiRuntimeAgentRunnerOptions } from "./pi-runtime-runner.js";
|
|
26
|
+
export * from "./herdr.js";
|
|
27
|
+
export * from "./runtime/index.js";
|
|
28
|
+
export { retainTerminalRuns } from "./retention.js";
|
|
29
|
+
export type { RetentionCleanupOptions, RetentionCleanupReport } from "./retention.js";
|
package/src/io.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { WorkflowError } from "./types.js";
|
|
7
|
+
import { isNodeError } from "./utils.js";
|
|
8
|
+
|
|
9
|
+
const execute = promisify(execFile);
|
|
10
|
+
// ponytail: retry window is capped at 1.585s; use durable lock coordination if holders outlive it.
|
|
11
|
+
const RENAME_RETRY_DELAYS_MS = [10, 25, 50, 100, 200, 400, 800] as const;
|
|
12
|
+
const syncSleepBuffer = new Int32Array(new SharedArrayBuffer(4));
|
|
13
|
+
function retryableRenameError(error: unknown): boolean { return isNodeError(error, "EPERM") || isNodeError(error, "EBUSY") || isNodeError(error, "EACCES"); }
|
|
14
|
+
async function renameWithRetry(from: string, to: string): Promise<void> {
|
|
15
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
16
|
+
try { await rename(from, to); return; }
|
|
17
|
+
catch (error) {
|
|
18
|
+
const delayMs = RENAME_RETRY_DELAYS_MS[attempt];
|
|
19
|
+
if (!retryableRenameError(error) || delayMs === undefined) throw error;
|
|
20
|
+
await new Promise<void>((resolve) => setTimeout(resolve, delayMs));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function renameSyncWithRetry(from: string, to: string): void {
|
|
25
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
26
|
+
try { renameSync(from, to); return; }
|
|
27
|
+
catch (error) {
|
|
28
|
+
const delayMs = RENAME_RETRY_DELAYS_MS[attempt];
|
|
29
|
+
if (!retryableRenameError(error) || delayMs === undefined) throw error;
|
|
30
|
+
Atomics.wait(syncSleepBuffer, 0, 0, delayMs);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export const gitIdentity = {
|
|
35
|
+
GIT_AUTHOR_NAME: "pi-extensible-workflows", GIT_AUTHOR_EMAIL: "pi-extensible-workflows@localhost", GIT_COMMITTER_NAME: "pi-extensible-workflows", GIT_COMMITTER_EMAIL: "pi-extensible-workflows@localhost",
|
|
36
|
+
GIT_AUTHOR_DATE: "2000-01-01T00:00:00Z", GIT_COMMITTER_DATE: "2000-01-01T00:00:00Z",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function atomicWriteFile(path: string, content: string): Promise<void>;
|
|
40
|
+
export function atomicWriteFile(path: string, content: string, sync: true): void;
|
|
41
|
+
export function atomicWriteFile(path: string, content: string, sync = false): Promise<void> | void {
|
|
42
|
+
const temporary = `${path}.${String(process.pid)}.${randomUUID()}.tmp`;
|
|
43
|
+
if (sync) {
|
|
44
|
+
try {
|
|
45
|
+
writeFileSync(temporary, content, { encoding: "utf8", mode: 0o600 });
|
|
46
|
+
renameSyncWithRetry(temporary, path);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
try { rmSync(temporary, { force: true }); } catch { /* Preserve the original write error. */ }
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
return writeFile(temporary, content, { encoding: "utf8", mode: 0o600 }).then(() => renameWithRetry(temporary, path)).catch(async (error: unknown) => {
|
|
54
|
+
try { await rm(temporary, { force: true }); } catch { /* Preserve the original write error. */ }
|
|
55
|
+
throw error;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function atomicJson(path: string, value: unknown): Promise<void> {
|
|
60
|
+
const serialized = JSON.stringify(value);
|
|
61
|
+
if (typeof serialized !== "string") throw new WorkflowError("INTERNAL_ERROR", `Cannot serialize JSON for ${path}`);
|
|
62
|
+
await atomicWriteFile(path, `${serialized}\n`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function atomicPrettyJson(path: string, value: unknown): Promise<void> {
|
|
66
|
+
await atomicWriteFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function json(path: string): Promise<unknown> { return JSON.parse(await readFile(path, "utf8")); }
|
|
70
|
+
export async function git(cwd: string, args: readonly string[], extraEnv: NodeJS.ProcessEnv = {}): Promise<string> {
|
|
71
|
+
const { stdout } = await execute("git", ["-c", "core.hooksPath=/dev/null", "-c", "commit.gpgSign=false", ...args], { cwd, env: { ...process.env, ...extraEnv }, encoding: "utf8", maxBuffer: 10 * 1024 * 1024 });
|
|
72
|
+
return stdout;
|
|
73
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const CORE_PACKAGE_NAME = "@marcoscale98/pi-extensible-workflows";
|
|
2
|
+
export const CORE_PACKAGE_COMPATIBILITY_NAME = "pi-extensible-workflows";
|
|
3
|
+
export const CORE_PACKAGE_NAMES = [CORE_PACKAGE_NAME, CORE_PACKAGE_COMPATIBILITY_NAME] as const;
|
|
4
|
+
|
|
5
|
+
export function isCorePackageName(value: unknown): boolean {
|
|
6
|
+
return typeof value === "string" && CORE_PACKAGE_NAMES.includes(value as (typeof CORE_PACKAGE_NAMES)[number]);
|
|
7
|
+
}
|
package/src/paths.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { readdir } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { basename, join, resolve } from "node:path";
|
|
6
|
+
import { WorkflowError } from "./types.js";
|
|
7
|
+
import { isNodeError } from "./utils.js";
|
|
8
|
+
|
|
9
|
+
export function safePart(value: string): string { return value.replace(/[^a-zA-Z0-9._-]/g, "_"); }
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Resolves a path to its filesystem spelling, including symlink and platform aliases.
|
|
13
|
+
* A missing path safely falls back to its absolute lexical spelling rather than
|
|
14
|
+
* guessing at the identity of an object that does not exist.
|
|
15
|
+
*/
|
|
16
|
+
export function canonicalPath(path: string): string {
|
|
17
|
+
const absolute = resolve(path);
|
|
18
|
+
try { return realpathSync(absolute); } catch { return absolute; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function sameFilesystemPath(left: string, right: string): boolean { return canonicalPath(left) === canonicalPath(right); }
|
|
22
|
+
export function workflowProjectSettingsPath(cwd: string): string { return join(cwd, ".pi", "pi-extensible-workflows", "settings.json"); }
|
|
23
|
+
|
|
24
|
+
export function projectStorageKey(cwd: string): string {
|
|
25
|
+
const exact = resolve(cwd);
|
|
26
|
+
const slug = safePart(basename(exact)) || "root";
|
|
27
|
+
return `${slug}-${createHash("sha256").update(exact).digest("hex").slice(0, 12)}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function projectSessionsDirectory(cwd: string, home = homedir()): string {
|
|
31
|
+
return join(home, ".pi", "workflows", "projects", projectStorageKey(cwd), "sessions");
|
|
32
|
+
}
|
|
33
|
+
export function runsDirectory(cwd: string, sessionId: string, home = homedir()): string {
|
|
34
|
+
return join(projectSessionsDirectory(cwd, home), safePart(sessionId), "runs");
|
|
35
|
+
}
|
|
36
|
+
export async function listPersistedSessionIds(cwd: string, home = homedir()): Promise<string[]> {
|
|
37
|
+
try {
|
|
38
|
+
const entries = await readdir(projectSessionsDirectory(cwd, home), { withFileTypes: true });
|
|
39
|
+
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map(({ name }) => name);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (isNodeError(error, "ENOENT")) return [];
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function structuralPath(...names: string[]): string {
|
|
47
|
+
if (names.length === 0 || names.some((name) => name.trim() === "")) throw new WorkflowError("INVALID_METADATA", "Structural paths require non-empty explicit names");
|
|
48
|
+
return names.map((name) => encodeURIComponent(name)).join("/");
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { atomicJson, atomicWriteFile, json } from "./io.js";
|
|
2
|
+
export {
|
|
3
|
+
projectStorageKey, projectSessionsDirectory, runsDirectory, listPersistedSessionIds, structuralPath,
|
|
4
|
+
} from "./paths.js";
|
|
5
|
+
export { hasLiveSessionLease, SessionLease, acquireSessionLease, listRunIds, processAlive } from "./session-lease.js";
|
|
6
|
+
export { RunStore } from "./store.js";
|
|
7
|
+
export {
|
|
8
|
+
EffectiveSystemPrompt, PersistedRun, RunSummaryAgent, RunSummaryArtifacts, RunSummary,
|
|
9
|
+
CompletedOperation, AwaitingCheckpoint, PendingWorkflowDecision, PersistedOwnershipNode,
|
|
10
|
+
WorktreeReference, BorrowedWorktreeBinding, isPersistedRun,
|
|
11
|
+
} from "./decoders.js";
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import type { RuntimeAgentHandoff, RuntimeAgentProgress, RuntimeAgentState, RuntimeHandoffState, RuntimeToolCallProgress } from "./runtime/agent-runner.js";
|
|
2
|
+
import type { AgentProgress } from "./agent-execution.js";
|
|
3
|
+
import type { LiveSessionHandoff, WorkflowAgentMessage, WorkflowAgentSession, WorkflowAgentSessionEvent, WorkflowAgentSessionState } from "./types.js";
|
|
4
|
+
import { isThinkingLevel, sanitizeDisplayText } from "./utils.js";
|
|
5
|
+
const TURN_START_EVENTS = new Set(["turn_start", "turn_started", "turnStarted"]);
|
|
6
|
+
const MAX_ACTIVITY_TEXT_CHARS = 200;
|
|
7
|
+
const AGENT_START_EVENTS = new Set(["agent_start"]);
|
|
8
|
+
const TURN_END_EVENTS = new Set(["turn_end", "turnEnded", "agent_end", "agent_settled"]);
|
|
9
|
+
type RecordValue = Record<string, unknown>;
|
|
10
|
+
|
|
11
|
+
function record(value: unknown): RecordValue | undefined { return typeof value === "object" && value !== null && !Array.isArray(value) ? value as RecordValue : undefined; }
|
|
12
|
+
function stringValue(value: unknown): string | undefined { return typeof value === "string" ? value : undefined; }
|
|
13
|
+
function finiteNumber(value: unknown): number | undefined { return typeof value === "number" && Number.isFinite(value) ? value : undefined; }
|
|
14
|
+
|
|
15
|
+
function normalizeMessage(value: unknown): WorkflowAgentMessage | undefined {
|
|
16
|
+
const candidate = record(value);
|
|
17
|
+
const role = stringValue(candidate?.role);
|
|
18
|
+
if (!candidate || role === undefined) return undefined;
|
|
19
|
+
const usage = record(candidate.usage);
|
|
20
|
+
const cost = record(usage?.cost);
|
|
21
|
+
const input = finiteNumber(usage?.input);
|
|
22
|
+
const output = finiteNumber(usage?.output);
|
|
23
|
+
const cacheRead = finiteNumber(usage?.cacheRead);
|
|
24
|
+
const cacheWrite = finiteNumber(usage?.cacheWrite);
|
|
25
|
+
const totalCost = finiteNumber(cost?.total);
|
|
26
|
+
const normalizedUsage = input === undefined || output === undefined || cacheRead === undefined || cacheWrite === undefined || totalCost === undefined ? undefined : { input, output, cacheRead, cacheWrite, cost: { total: totalCost } };
|
|
27
|
+
const stopReason = stringValue(candidate.stopReason);
|
|
28
|
+
const errorMessage = stringValue(candidate.errorMessage);
|
|
29
|
+
return {
|
|
30
|
+
role,
|
|
31
|
+
...(candidate.content === undefined ? {} : { content: candidate.content }),
|
|
32
|
+
...(stopReason === undefined ? {} : { stopReason }),
|
|
33
|
+
...(errorMessage === undefined ? {} : { errorMessage }),
|
|
34
|
+
...(normalizedUsage === undefined ? {} : { usage: normalizedUsage }),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function normalizePiMessage(value: unknown): WorkflowAgentMessage | undefined { return normalizeMessage(value); }
|
|
39
|
+
/** An assistant turn Pi aborted before it produced any content; it carries no usable result. */
|
|
40
|
+
export function isEmptyAbortedAssistant(message: WorkflowAgentMessage | undefined): boolean { return message?.stopReason === "aborted" && Array.isArray(message.content) && message.content.length === 0; }
|
|
41
|
+
|
|
42
|
+
function normalizeState(value: unknown): WorkflowAgentSessionState | undefined {
|
|
43
|
+
const candidate = record(value);
|
|
44
|
+
const model = record(candidate?.model);
|
|
45
|
+
const provider = stringValue(model?.provider);
|
|
46
|
+
const modelName = stringValue(model?.model);
|
|
47
|
+
const rawTools = candidate?.tools;
|
|
48
|
+
const tools = Array.isArray(rawTools) ? rawTools.filter((tool): tool is string => typeof tool === "string") : undefined;
|
|
49
|
+
if (!candidate || !model || provider === undefined || modelName === undefined || tools === undefined || !Array.isArray(rawTools) || tools.length !== rawTools.length) return undefined;
|
|
50
|
+
const thinking = candidate.thinking === undefined ? model.thinking : candidate.thinking;
|
|
51
|
+
if (thinking !== undefined && !isThinkingLevel(thinking)) return undefined;
|
|
52
|
+
const systemPrompt = candidate.systemPrompt;
|
|
53
|
+
if (systemPrompt !== undefined && typeof systemPrompt !== "string") return undefined;
|
|
54
|
+
return {
|
|
55
|
+
model: { provider, model: modelName, ...(thinking === undefined ? {} : { thinking }) },
|
|
56
|
+
...(thinking === undefined ? {} : { thinking }),
|
|
57
|
+
tools: [...tools],
|
|
58
|
+
...(systemPrompt === undefined ? {} : { systemPrompt }),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizedEvent(value: unknown): WorkflowAgentSessionEvent | undefined {
|
|
63
|
+
const candidate = record(value);
|
|
64
|
+
const type = stringValue(candidate?.type);
|
|
65
|
+
if (!candidate || type === undefined) return undefined;
|
|
66
|
+
const state = normalizeState(candidate.state);
|
|
67
|
+
const message = normalizeMessage(candidate.message);
|
|
68
|
+
const assistantMessageEvent = record(candidate.assistantMessageEvent);
|
|
69
|
+
const assistantType = stringValue(assistantMessageEvent?.type);
|
|
70
|
+
const assistantDelta = stringValue(assistantMessageEvent?.delta);
|
|
71
|
+
const toolCallId = stringValue(candidate.toolCallId);
|
|
72
|
+
const toolName = stringValue(candidate.toolName);
|
|
73
|
+
const isError = candidate.isError;
|
|
74
|
+
return {
|
|
75
|
+
type,
|
|
76
|
+
...(state === undefined ? {} : { state }),
|
|
77
|
+
...(message === undefined ? {} : { message }),
|
|
78
|
+
...(assistantType === undefined ? {} : { assistantMessageEvent: { type: assistantType, ...(assistantDelta === undefined ? {} : { delta: assistantDelta }) } }),
|
|
79
|
+
...(toolCallId === undefined ? {} : { toolCallId }),
|
|
80
|
+
...(toolName === undefined ? {} : { toolName }),
|
|
81
|
+
...(typeof isError === "boolean" ? { isError } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function normalizePiSessionEvent(value: unknown): WorkflowAgentSessionEvent | undefined { return normalizedEvent(value); }
|
|
86
|
+
|
|
87
|
+
function stateFromSession(session: WorkflowAgentSession): RuntimeAgentState {
|
|
88
|
+
const current = session.getState();
|
|
89
|
+
return {
|
|
90
|
+
model: { ...current.model },
|
|
91
|
+
...(current.thinking === undefined ? {} : { thinking: current.thinking }),
|
|
92
|
+
tools: [...current.tools],
|
|
93
|
+
...(current.systemPrompt === undefined ? {} : { systemPrompt: current.systemPrompt }),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function runtimeState(state: WorkflowAgentSessionState): RuntimeAgentState { return { model: { ...state.model }, ...(state.thinking === undefined ? {} : { thinking: state.thinking }), tools: [...state.tools], ...(state.systemPrompt === undefined ? {} : { systemPrompt: state.systemPrompt }) }; }
|
|
98
|
+
|
|
99
|
+
function runtimeUsage(session: WorkflowAgentSession): RuntimeAgentProgress["usage"] {
|
|
100
|
+
const stats = session.getSessionStats();
|
|
101
|
+
const values = [stats.tokens.input, stats.tokens.output, stats.tokens.cacheRead, stats.tokens.cacheWrite, stats.cost];
|
|
102
|
+
if (values.some((value) => !Number.isFinite(value))) return { availability: "unavailable" };
|
|
103
|
+
return { availability: "complete", input: stats.tokens.input, output: stats.tokens.output, cacheRead: stats.tokens.cacheRead, cacheWrite: stats.tokens.cacheWrite, costUsd: stats.cost };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function sameActivity(left: RuntimeAgentProgress["activity"], right: RuntimeAgentProgress["activity"]): boolean { return left?.kind === right?.kind && left?.text === right?.text; }
|
|
107
|
+
|
|
108
|
+
function runtimeHandoffState(state: LiveSessionHandoff["state"]): RuntimeHandoffState {
|
|
109
|
+
if (state === "handoff-pending") return "takeover-pending";
|
|
110
|
+
if (state === "herdr-running") return "remote-running";
|
|
111
|
+
return state;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function isTurnBoundaryStart(type: string): boolean { return TURN_START_EVENTS.has(type); }
|
|
115
|
+
/** A turn boundary start or an agent start: either one means the session is busy until the next turn end. */
|
|
116
|
+
export function isTurnActivityStart(type: string): boolean { return TURN_START_EVENTS.has(type) || AGENT_START_EVENTS.has(type); }
|
|
117
|
+
export function isTurnBoundaryEnd(type: string): boolean { return TURN_END_EVENTS.has(type); }
|
|
118
|
+
export function isTurnEnd(type: string): boolean { return type === "turn_end" || type === "turnEnded"; }
|
|
119
|
+
|
|
120
|
+
function boundaryEvent(type: string): "turn_started" | "turn_end" | undefined {
|
|
121
|
+
if (isTurnActivityStart(type)) return "turn_started";
|
|
122
|
+
if (isTurnBoundaryEnd(type)) return "turn_end";
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function createRuntimeHandoffAdapter(handoff: LiveSessionHandoff): RuntimeAgentHandoff {
|
|
127
|
+
return {
|
|
128
|
+
get state() { return runtimeHandoffState(handoff.state); },
|
|
129
|
+
get transferred() { return handoff.transferred; },
|
|
130
|
+
observe(event) {
|
|
131
|
+
const type = boundaryEvent(event.type);
|
|
132
|
+
if (type !== undefined) handoff.observe({ type });
|
|
133
|
+
},
|
|
134
|
+
request: (launch) => handoff.request(launch),
|
|
135
|
+
waitForTakeover: () => handoff.waitForTakeover(),
|
|
136
|
+
takeover: () => { handoff.takeover(); },
|
|
137
|
+
waitForResume: () => handoff.waitForResume(),
|
|
138
|
+
release: (reason?: string) => { handoff.release(reason); },
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface PiRuntimeProgressObservation {
|
|
143
|
+
readonly event: WorkflowAgentSessionEvent;
|
|
144
|
+
readonly progress: RuntimeAgentProgress;
|
|
145
|
+
readonly report: boolean;
|
|
146
|
+
readonly persist: boolean;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface PiRuntimeSessionAdapter {
|
|
150
|
+
readonly handoff: RuntimeAgentHandoff;
|
|
151
|
+
observe(event: WorkflowAgentSessionEvent): PiRuntimeProgressObservation | undefined;
|
|
152
|
+
snapshot(persist: boolean): RuntimeAgentProgress;
|
|
153
|
+
dispose(): void;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function createPiRuntimeSessionAdapter(session: WorkflowAgentSession, handoff: LiveSessionHandoff, clock: () => number = Date.now): PiRuntimeSessionAdapter {
|
|
157
|
+
const neutralHandoff = createRuntimeHandoffAdapter(handoff);
|
|
158
|
+
const toolCalls = new Map<string, RuntimeToolCallProgress>();
|
|
159
|
+
let activity: RuntimeAgentProgress["activity"];
|
|
160
|
+
let streamText = "";
|
|
161
|
+
let lastEventAt: number | undefined;
|
|
162
|
+
let lastReportedEventAt: number | undefined;
|
|
163
|
+
let disposed = false;
|
|
164
|
+
|
|
165
|
+
let toolCallsView: readonly RuntimeToolCallProgress[] = [];
|
|
166
|
+
const progressAt = (persist: boolean, stateOverride: RuntimeAgentState | undefined, toolCallsValue: readonly RuntimeToolCallProgress[], activityValue: RuntimeAgentProgress["activity"], eventAtValue: number | undefined): RuntimeAgentProgress => ({ usage: runtimeUsage(session), toolCalls: toolCallsValue, state: stateOverride ?? stateFromSession(session), ...(activityValue === undefined ? {} : { activity: activityValue }), ...(eventAtValue === undefined ? {} : { lastEventAt: eventAtValue }), persist });
|
|
167
|
+
const snapshot = (persist: boolean): RuntimeAgentProgress => progressAt(persist, undefined, [...toolCalls.values()], activity, lastEventAt);
|
|
168
|
+
return {
|
|
169
|
+
handoff: neutralHandoff,
|
|
170
|
+
observe(event) {
|
|
171
|
+
if (disposed) return undefined;
|
|
172
|
+
neutralHandoff.observe(event);
|
|
173
|
+
const eventAt = clock();
|
|
174
|
+
lastEventAt = eventAt;
|
|
175
|
+
const eventState = event.state === undefined ? undefined : runtimeState(event.state);
|
|
176
|
+
let report = false;
|
|
177
|
+
let persist = false;
|
|
178
|
+
const previousActivity = activity;
|
|
179
|
+
let eventToolCalls = toolCallsView;
|
|
180
|
+
if (event.type === "state_changed") { report = true; persist = true; }
|
|
181
|
+
if (event.type === "message_start" && event.message?.role === "assistant") { streamText = ""; activity = { kind: "text", text: "" }; report = true; }
|
|
182
|
+
if (event.type === "message_update") {
|
|
183
|
+
const updateType = event.assistantMessageEvent?.type;
|
|
184
|
+
if (updateType === "thinking_start" || updateType === "text_start") streamText = "";
|
|
185
|
+
const delta = event.assistantMessageEvent?.delta;
|
|
186
|
+
if ((updateType === "thinking_delta" || updateType === "text_delta") && delta) streamText = `${streamText}${delta}`.replace(/\s+/g, " ").slice(-MAX_ACTIVITY_TEXT_CHARS);
|
|
187
|
+
if (updateType && ["thinking_start", "thinking_delta", "thinking_end"].includes(updateType)) activity = { kind: "reasoning", text: streamText };
|
|
188
|
+
else if (updateType && ["text_start", "text_delta", "text_end", "toolcall_start", "toolcall_delta", "toolcall_end"].includes(updateType)) activity = { kind: "text", text: streamText };
|
|
189
|
+
report = previousActivity?.kind !== activity?.kind;
|
|
190
|
+
}
|
|
191
|
+
if (event.type === "message_end") {
|
|
192
|
+
streamText = "";
|
|
193
|
+
activity = undefined;
|
|
194
|
+
report = !sameActivity(previousActivity, activity);
|
|
195
|
+
if (event.message?.role === "assistant") { persist = true; report = true; }
|
|
196
|
+
}
|
|
197
|
+
if (event.type === "tool_execution_start" && event.toolCallId && event.toolName) {
|
|
198
|
+
toolCalls.set(event.toolCallId, { id: event.toolCallId, name: event.toolName, state: "running" });
|
|
199
|
+
toolCallsView = [...toolCalls.values()];
|
|
200
|
+
activity = { kind: "tool", text: event.toolName };
|
|
201
|
+
report = true;
|
|
202
|
+
}
|
|
203
|
+
if (event.type === "tool_execution_update" && event.toolName) {
|
|
204
|
+
activity = { kind: "tool", text: event.toolName };
|
|
205
|
+
report = !sameActivity(previousActivity, activity);
|
|
206
|
+
}
|
|
207
|
+
let removeToolCallId: string | undefined;
|
|
208
|
+
if (event.type === "tool_execution_end" && event.toolCallId && event.toolName) {
|
|
209
|
+
toolCalls.set(event.toolCallId, { id: event.toolCallId, name: event.toolName, state: event.isError ? "failed" : "completed" });
|
|
210
|
+
eventToolCalls = [...toolCalls.values()];
|
|
211
|
+
if (activity?.kind === "tool" && activity.text === event.toolName) activity = undefined;
|
|
212
|
+
report = true;
|
|
213
|
+
removeToolCallId = event.toolCallId;
|
|
214
|
+
}
|
|
215
|
+
if (removeToolCallId === undefined) eventToolCalls = toolCallsView;
|
|
216
|
+
if (!report && (lastReportedEventAt === undefined || eventAt - lastReportedEventAt >= 1000)) report = true;
|
|
217
|
+
const eventActivity = activity;
|
|
218
|
+
let materialized: RuntimeAgentProgress | undefined;
|
|
219
|
+
const result = { event, get progress() { return materialized ??= progressAt(persist, eventState, eventToolCalls, eventActivity, eventAt); }, report, persist };
|
|
220
|
+
if (report) lastReportedEventAt = eventAt;
|
|
221
|
+
if (removeToolCallId !== undefined) {
|
|
222
|
+
toolCalls.delete(removeToolCallId);
|
|
223
|
+
toolCallsView = [...toolCalls.values()];
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
},
|
|
227
|
+
snapshot,
|
|
228
|
+
dispose() { disposed = true; toolCalls.clear(); toolCallsView = []; activity = undefined; },
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function requiredUsage(value: number | undefined, name: string): number {
|
|
233
|
+
if (value === undefined || !Number.isFinite(value)) throw new Error(`Pi runtime usage is missing ${name}`);
|
|
234
|
+
return value;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function runtimeProgressToAgentProgress(value: RuntimeAgentProgress): AgentProgress {
|
|
238
|
+
if (value.usage.availability !== "complete") throw new Error(`Cannot convert ${value.usage.availability} runtime usage to Pi accounting`);
|
|
239
|
+
return {
|
|
240
|
+
accounting: { input: requiredUsage(value.usage.input, "input"), output: requiredUsage(value.usage.output, "output"), cacheRead: requiredUsage(value.usage.cacheRead, "cacheRead"), cacheWrite: requiredUsage(value.usage.cacheWrite, "cacheWrite"), cost: requiredUsage(value.usage.costUsd, "costUsd") },
|
|
241
|
+
toolCalls: value.toolCalls.map(({ id, name, state }) => ({ id, name, state })),
|
|
242
|
+
...(value.state === undefined ? {} : { state: { model: { ...value.state.model }, ...(value.state.thinking === undefined ? {} : { thinking: value.state.thinking }), tools: [...value.state.tools], ...(value.state.systemPrompt === undefined ? {} : { systemPrompt: value.state.systemPrompt }) } }),
|
|
243
|
+
...(value.activity === undefined ? {} : { activity: { ...value.activity, text: sanitizeDisplayText(value.activity.text) } }),
|
|
244
|
+
...(value.lastEventAt === undefined ? {} : { lastEventAt: value.lastEventAt }),
|
|
245
|
+
persist: value.persist,
|
|
246
|
+
};
|
|
247
|
+
}
|