@yycholla/pi-dynamic-workflows 3.3.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/LICENSE +22 -0
- package/README.md +325 -0
- package/assets/readme/hero.png +0 -0
- package/assets/readme/hero.svg +70 -0
- package/assets/readme/package-cover.png +0 -0
- package/assets/readme/package-cover.svg +66 -0
- package/assets/readme/workflow.png +0 -0
- package/assets/readme/workflow.svg +77 -0
- package/dist/accept-workflow-guidance.d.ts +14 -0
- package/dist/accept-workflow-guidance.js +53 -0
- package/dist/adversarial-review.d.ts +30 -0
- package/dist/adversarial-review.js +107 -0
- package/dist/agent-history.d.ts +20 -0
- package/dist/agent-history.js +122 -0
- package/dist/agent-registry.d.ts +83 -0
- package/dist/agent-registry.js +190 -0
- package/dist/agent.d.ts +364 -0
- package/dist/agent.js +714 -0
- package/dist/builtin-commands.d.ts +19 -0
- package/dist/builtin-commands.js +251 -0
- package/dist/builtin-workflows.d.ts +45 -0
- package/dist/builtin-workflows.js +121 -0
- package/dist/code-review.d.ts +26 -0
- package/dist/code-review.js +181 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.js +44 -0
- package/dist/deep-research.d.ts +30 -0
- package/dist/deep-research.js +124 -0
- package/dist/display.d.ts +134 -0
- package/dist/display.js +248 -0
- package/dist/effort-command.d.ts +28 -0
- package/dist/effort-command.js +68 -0
- package/dist/enums.d.ts +69 -0
- package/dist/enums.js +78 -0
- package/dist/errors.d.ts +113 -0
- package/dist/errors.js +140 -0
- package/dist/extension-reload.d.ts +37 -0
- package/dist/extension-reload.js +78 -0
- package/dist/fs-persistence.d.ts +63 -0
- package/dist/fs-persistence.js +102 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +35 -0
- package/dist/logger.d.ts +21 -0
- package/dist/logger.js +66 -0
- package/dist/model-routing.d.ts +30 -0
- package/dist/model-routing.js +50 -0
- package/dist/model-spec.d.ts +29 -0
- package/dist/model-spec.js +252 -0
- package/dist/model-tier-config.d.ts +133 -0
- package/dist/model-tier-config.js +249 -0
- package/dist/run-persistence.d.ts +180 -0
- package/dist/run-persistence.js +294 -0
- package/dist/saved-commands.d.ts +28 -0
- package/dist/saved-commands.js +100 -0
- package/dist/shared-store.d.ts +98 -0
- package/dist/shared-store.js +212 -0
- package/dist/structured-output.d.ts +19 -0
- package/dist/structured-output.js +30 -0
- package/dist/task-panel.d.ts +61 -0
- package/dist/task-panel.js +422 -0
- package/dist/usage-limit-scheduler.d.ts +145 -0
- package/dist/usage-limit-scheduler.js +368 -0
- package/dist/web-tools.d.ts +20 -0
- package/dist/web-tools.js +120 -0
- package/dist/workflow-authoring-coverage.d.ts +70 -0
- package/dist/workflow-authoring-coverage.js +421 -0
- package/dist/workflow-authoring-reference.d.ts +20 -0
- package/dist/workflow-authoring-reference.js +156 -0
- package/dist/workflow-capability-contract.d.ts +131 -0
- package/dist/workflow-capability-contract.js +604 -0
- package/dist/workflow-commands.d.ts +18 -0
- package/dist/workflow-commands.js +260 -0
- package/dist/workflow-comprehension.d.ts +133 -0
- package/dist/workflow-comprehension.js +1321 -0
- package/dist/workflow-context-measurement.d.ts +72 -0
- package/dist/workflow-context-measurement.js +213 -0
- package/dist/workflow-control-tool.d.ts +30 -0
- package/dist/workflow-control-tool.js +176 -0
- package/dist/workflow-delivery-choice.d.ts +20 -0
- package/dist/workflow-delivery-choice.js +48 -0
- package/dist/workflow-editor.d.ts +93 -0
- package/dist/workflow-editor.js +363 -0
- package/dist/workflow-manager.d.ts +492 -0
- package/dist/workflow-manager.js +1124 -0
- package/dist/workflow-paths.d.ts +22 -0
- package/dist/workflow-paths.js +46 -0
- package/dist/workflow-release-gate.d.ts +39 -0
- package/dist/workflow-release-gate.js +309 -0
- package/dist/workflow-saved.d.ts +38 -0
- package/dist/workflow-saved.js +126 -0
- package/dist/workflow-settings.d.ts +70 -0
- package/dist/workflow-settings.js +131 -0
- package/dist/workflow-tool.d.ts +71 -0
- package/dist/workflow-tool.js +367 -0
- package/dist/workflow-ui.d.ts +182 -0
- package/dist/workflow-ui.js +1587 -0
- package/dist/workflow.d.ts +333 -0
- package/dist/workflow.js +1151 -0
- package/dist/workflows-models-command.d.ts +31 -0
- package/dist/workflows-models-command.js +156 -0
- package/dist/worktree.d.ts +25 -0
- package/dist/worktree.js +61 -0
- package/extensions/workflow.ts +151 -0
- package/package.json +104 -0
- package/skills/workflow-authoring/SKILL.md +30 -0
- package/skills/workflow-authoring/examples/adversarial-verification.js +63 -0
- package/skills/workflow-authoring/examples/bounded-semantic-retry.js +56 -0
- package/skills/workflow-authoring/examples/classify-and-act.js +67 -0
- package/skills/workflow-authoring/examples/fan-out-and-synthesize.js +47 -0
- package/skills/workflow-authoring/examples/generate-and-filter.js +68 -0
- package/skills/workflow-authoring/examples/loop-until-done.js +68 -0
- package/skills/workflow-authoring/examples/phased-budgets.js +75 -0
- package/skills/workflow-authoring/examples/saved-nested-workflows.js +44 -0
- package/skills/workflow-authoring/examples/structured-output.js +37 -0
- package/skills/workflow-authoring/examples/tournament.js +81 -0
- package/skills/workflow-authoring/examples/validated-gate.js +63 -0
- package/skills/workflow-authoring/references/capabilities.md +41 -0
- package/skills/workflow-authoring/references/capability-details.md +357 -0
- package/skills/workflow-authoring/references/common-helpers.md +4 -0
- package/skills/workflow-authoring/references/debugging.md +27 -0
- package/skills/workflow-authoring/references/focused-recipes.md +11 -0
- package/skills/workflow-authoring/references/helpers.md +9 -0
- package/skills/workflow-authoring/references/lifecycle.md +33 -0
- package/skills/workflow-authoring/references/pattern-selection.md +15 -0
- package/skills/workflow-authoring/references/quality-helpers.md +8 -0
- package/skills/workflow-authoring/references/registry-ownership.md +15 -0
- package/skills/workflow-authoring/references/retry-helper.md +5 -0
- package/skills/workflow-authoring/references/review.md +43 -0
- package/skills/workflow-authoring/references/runtime.md +27 -0
- package/skills/workflow-authoring/references/specialized-helpers.md +19 -0
- package/skills/workflow-authoring/references/versions.md +13 -0
- package/skills/workflow-patterns/SKILL.md +51 -0
- package/src/accept-workflow-guidance.ts +71 -0
- package/src/adversarial-review.ts +120 -0
- package/src/agent-history.ts +157 -0
- package/src/agent-registry.ts +221 -0
- package/src/agent.ts +929 -0
- package/src/builtin-commands.ts +286 -0
- package/src/builtin-workflows.ts +155 -0
- package/src/code-review.ts +183 -0
- package/src/config.ts +55 -0
- package/src/deep-research.ts +135 -0
- package/src/display.ts +367 -0
- package/src/effort-command.ts +87 -0
- package/src/enums.ts +77 -0
- package/src/errors.ts +199 -0
- package/src/extension-reload.ts +100 -0
- package/src/fs-persistence.ts +124 -0
- package/src/index.ts +176 -0
- package/src/logger.ts +88 -0
- package/src/model-routing.ts +73 -0
- package/src/model-spec.ts +309 -0
- package/src/model-tier-config.ts +296 -0
- package/src/run-persistence.ts +484 -0
- package/src/saved-commands.ts +115 -0
- package/src/shared-store.ts +228 -0
- package/src/structured-output.ts +47 -0
- package/src/task-panel.ts +490 -0
- package/src/usage-limit-scheduler.ts +432 -0
- package/src/web-tools.ts +124 -0
- package/src/workflow-authoring-coverage.ts +486 -0
- package/src/workflow-authoring-reference.ts +186 -0
- package/src/workflow-capability-contract.ts +806 -0
- package/src/workflow-commands.ts +287 -0
- package/src/workflow-comprehension.ts +1673 -0
- package/src/workflow-context-measurement.ts +262 -0
- package/src/workflow-control-tool.ts +238 -0
- package/src/workflow-delivery-choice.ts +69 -0
- package/src/workflow-editor.ts +444 -0
- package/src/workflow-manager.ts +1405 -0
- package/src/workflow-paths.ts +63 -0
- package/src/workflow-release-gate.ts +529 -0
- package/src/workflow-saved.ts +180 -0
- package/src/workflow-settings.ts +194 -0
- package/src/workflow-tool.ts +464 -0
- package/src/workflow-ui.ts +1789 -0
- package/src/workflow.ts +1615 -0
- package/src/workflows-models-command.ts +211 -0
- package/src/worktree.ts +76 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration constants for pi-dynamic-workflows.
|
|
3
|
+
*/
|
|
4
|
+
/** Maximum number of agents allowed per workflow run. */
|
|
5
|
+
export declare const MAX_AGENTS_PER_RUN = 1000;
|
|
6
|
+
/** Default timeout for a single agent in milliseconds. null means no hard timeout. */
|
|
7
|
+
export declare const DEFAULT_AGENT_TIMEOUT_MS: null;
|
|
8
|
+
/** Maximum concurrent agents (matches Claude Code limit). */
|
|
9
|
+
export declare const MAX_CONCURRENCY = 16;
|
|
10
|
+
/** Maximum automatic retry attempts after a recoverable agent failure. */
|
|
11
|
+
export declare const MAX_AGENT_RETRIES = 3;
|
|
12
|
+
/** Default token budget if none specified. */
|
|
13
|
+
export declare const DEFAULT_TOKEN_BUDGET: null;
|
|
14
|
+
/** Legacy project-relative directory for persisted workflow run state. New writes use workflowProjectPaths(). */
|
|
15
|
+
export declare const WORKFLOW_RUNS_DIR = ".pi/workflows/runs";
|
|
16
|
+
/** Legacy project-relative directory for saved workflow commands. New writes use workflowProjectPaths(). */
|
|
17
|
+
export declare const WORKFLOW_SAVED_DIR = ".pi/workflows/saved";
|
|
18
|
+
/** User-level saved workflows directory. */
|
|
19
|
+
export declare const USER_WORKFLOW_SAVED_DIR = "~/.pi/workflows/saved";
|
|
20
|
+
/** User-level model tiers config file, relative to the home directory. */
|
|
21
|
+
export declare const MODEL_TIERS_FILE = ".pi/workflows/model-tiers.json";
|
|
22
|
+
/** User-level workflow extension settings file, relative to the home directory. */
|
|
23
|
+
export declare const WORKFLOW_SETTINGS_FILE = ".pi/workflows/settings.json";
|
|
24
|
+
/** Default keyword that arms workflows mode from interactive input. */
|
|
25
|
+
export declare const DEFAULT_KEYWORD_TRIGGER_WORD = "workflow";
|
|
26
|
+
/** Normalize a user-configured keyword trigger word. */
|
|
27
|
+
export declare function normalizeKeywordTriggerWord(value: unknown): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Named workflow subagent definitions directory. Resolved project-relative
|
|
30
|
+
* (cwd/.pi/agents), plus user-level at `~/.pi/agent/agents/` (the primary
|
|
31
|
+
* location, via `getAgentDir()` in agent-registry.ts) with the legacy
|
|
32
|
+
* `~/.pi/agents/` (this constant, home-relative) scanned as a deprecated
|
|
33
|
+
* fallback. Project entries win on name collision, then the primary user
|
|
34
|
+
* location, then the legacy one. Each `*.md` file is an agent definition
|
|
35
|
+
* (frontmatter + body prompt).
|
|
36
|
+
*/
|
|
37
|
+
export declare const AGENTS_DIR = ".pi/agents";
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration constants for pi-dynamic-workflows.
|
|
3
|
+
*/
|
|
4
|
+
/** Maximum number of agents allowed per workflow run. */
|
|
5
|
+
export const MAX_AGENTS_PER_RUN = 1000;
|
|
6
|
+
/** Default timeout for a single agent in milliseconds. null means no hard timeout. */
|
|
7
|
+
export const DEFAULT_AGENT_TIMEOUT_MS = null;
|
|
8
|
+
/** Maximum concurrent agents (matches Claude Code limit). */
|
|
9
|
+
export const MAX_CONCURRENCY = 16;
|
|
10
|
+
/** Maximum automatic retry attempts after a recoverable agent failure. */
|
|
11
|
+
export const MAX_AGENT_RETRIES = 3;
|
|
12
|
+
/** Default token budget if none specified. */
|
|
13
|
+
export const DEFAULT_TOKEN_BUDGET = null;
|
|
14
|
+
/** Legacy project-relative directory for persisted workflow run state. New writes use workflowProjectPaths(). */
|
|
15
|
+
export const WORKFLOW_RUNS_DIR = ".pi/workflows/runs";
|
|
16
|
+
/** Legacy project-relative directory for saved workflow commands. New writes use workflowProjectPaths(). */
|
|
17
|
+
export const WORKFLOW_SAVED_DIR = ".pi/workflows/saved";
|
|
18
|
+
/** User-level saved workflows directory. */
|
|
19
|
+
export const USER_WORKFLOW_SAVED_DIR = "~/.pi/workflows/saved";
|
|
20
|
+
/** User-level model tiers config file, relative to the home directory. */
|
|
21
|
+
export const MODEL_TIERS_FILE = ".pi/workflows/model-tiers.json";
|
|
22
|
+
/** User-level workflow extension settings file, relative to the home directory. */
|
|
23
|
+
export const WORKFLOW_SETTINGS_FILE = ".pi/workflows/settings.json";
|
|
24
|
+
/** Default keyword that arms workflows mode from interactive input. */
|
|
25
|
+
export const DEFAULT_KEYWORD_TRIGGER_WORD = "workflow";
|
|
26
|
+
/** Normalize a user-configured keyword trigger word. */
|
|
27
|
+
export function normalizeKeywordTriggerWord(value) {
|
|
28
|
+
if (typeof value !== "string")
|
|
29
|
+
return undefined;
|
|
30
|
+
const word = value.trim();
|
|
31
|
+
if (!word || word.startsWith("/") || /\s/.test(word))
|
|
32
|
+
return undefined;
|
|
33
|
+
return word;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Named workflow subagent definitions directory. Resolved project-relative
|
|
37
|
+
* (cwd/.pi/agents), plus user-level at `~/.pi/agent/agents/` (the primary
|
|
38
|
+
* location, via `getAgentDir()` in agent-registry.ts) with the legacy
|
|
39
|
+
* `~/.pi/agents/` (this constant, home-relative) scanned as a deprecated
|
|
40
|
+
* fallback. Project entries win on name collision, then the primary user
|
|
41
|
+
* location, then the legacy one. Each `*.md` file is an agent definition
|
|
42
|
+
* (frontmatter + body prompt).
|
|
43
|
+
*/
|
|
44
|
+
export const AGENTS_DIR = ".pi/agents";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep research workflow.
|
|
3
|
+
* Built-in workflow for comprehensive research across multiple sources.
|
|
4
|
+
*/
|
|
5
|
+
export interface DeepResearchConfig {
|
|
6
|
+
/** Number of distinct search angles/queries to explore. */
|
|
7
|
+
angles: number;
|
|
8
|
+
/** Minimum distinct sources required for a claim to survive cross-checking. */
|
|
9
|
+
minSupport: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate a deep-research workflow that uses the real web_search/web_fetch tools.
|
|
13
|
+
*
|
|
14
|
+
* The script is static and reads its inputs from `args` (question/angles/minSupport),
|
|
15
|
+
* so the question is never string-interpolated into source — no escaping hazards.
|
|
16
|
+
* Inject the web tools at run time via the agent's `tools` option.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateDeepResearchWorkflow(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate a codebase audit workflow.
|
|
21
|
+
*
|
|
22
|
+
* `scope` and each `checks` entry are user-supplied strings that get baked
|
|
23
|
+
* directly into the generated script's source (unlike the runtime-args-driven
|
|
24
|
+
* generators above), so every one is embedded via JSON.stringify — a proper JS
|
|
25
|
+
* string literal that can't be broken out of by a quote, backslash, or
|
|
26
|
+
* backtick in the value. Only the human-readable `meta.description` is
|
|
27
|
+
* truncated for display; the operative `scope` used by the agents is always
|
|
28
|
+
* the full, untruncated value.
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateCodebaseAuditWorkflow(scope: string, checks: string[]): string;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep research workflow.
|
|
3
|
+
* Built-in workflow for comprehensive research across multiple sources.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generate a deep-research workflow that uses the real web_search/web_fetch tools.
|
|
7
|
+
*
|
|
8
|
+
* The script is static and reads its inputs from `args` (question/angles/minSupport),
|
|
9
|
+
* so the question is never string-interpolated into source — no escaping hazards.
|
|
10
|
+
* Inject the web tools at run time via the agent's `tools` option.
|
|
11
|
+
*/
|
|
12
|
+
export function generateDeepResearchWorkflow() {
|
|
13
|
+
return `export const meta = {
|
|
14
|
+
name: 'deep_research',
|
|
15
|
+
description: 'Deep research with real web search and cross-checked claims',
|
|
16
|
+
phases: [
|
|
17
|
+
{ title: 'Queries' },
|
|
18
|
+
{ title: 'Gather' },
|
|
19
|
+
{ title: 'Verify' },
|
|
20
|
+
{ title: 'Report' },
|
|
21
|
+
],
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const question = (args && args.question) || ''
|
|
25
|
+
const angles = (args && args.angles) || 4
|
|
26
|
+
const minSupport = (args && args.minSupport) || 2
|
|
27
|
+
|
|
28
|
+
phase('Queries')
|
|
29
|
+
const plan = await agent(
|
|
30
|
+
'You are planning web research for this question:\\n' + question +
|
|
31
|
+
'\\n\\nProduce ' + angles + ' diverse, specific search queries that together cover the question from different angles.',
|
|
32
|
+
{ label: 'plan queries', schema: { type: 'object', properties: { queries: { type: 'array', items: { type: 'string' } } }, required: ['queries'] } }
|
|
33
|
+
)
|
|
34
|
+
// The planner agent() can return null (e.g. a subagent that died on a terminal
|
|
35
|
+
// provider error) or omit a usable queries array. Mirror the null-tolerance the
|
|
36
|
+
// Gather phase uses below and fall back to the original question as a single
|
|
37
|
+
// query so research still proceeds (degraded) instead of crashing on plan.queries.
|
|
38
|
+
const planned = plan && Array.isArray(plan.queries) ? plan.queries.filter((q) => typeof q === 'string' && q.trim().length > 0) : []
|
|
39
|
+
const queries = (planned.length > 0 ? planned : [question]).slice(0, angles)
|
|
40
|
+
|
|
41
|
+
phase('Gather')
|
|
42
|
+
const gathered = await parallel(queries.map((q, i) => () =>
|
|
43
|
+
agent(
|
|
44
|
+
'Research this query using the web_search and web_fetch tools.\\nQuery: ' + q +
|
|
45
|
+
'\\n\\nSteps: (1) call web_search with the query; (2) web_fetch the 2 most relevant result URLs; ' +
|
|
46
|
+
'(3) extract concrete, verifiable factual claims, each tagged with the exact source URL it came from. ' +
|
|
47
|
+
'Do NOT invent sources or claims — report only what the fetched pages actually say.',
|
|
48
|
+
{ label: 'research ' + (i + 1), schema: { type: 'object', properties: { sources: { type: 'array', items: { type: 'object', properties: { url: { type: 'string' }, claims: { type: 'array', items: { type: 'string' } } }, required: ['url', 'claims'] } } }, required: ['sources'] } }
|
|
49
|
+
)
|
|
50
|
+
))
|
|
51
|
+
const allSources = gathered.filter(Boolean).flatMap((g) => (g && g.sources) || [])
|
|
52
|
+
|
|
53
|
+
phase('Verify')
|
|
54
|
+
const verdict = await agent(
|
|
55
|
+
'Cross-check these research sources. Group claims that assert the same fact across different source URLs. ' +
|
|
56
|
+
'Keep a claim only if it is supported by at least ' + minSupport + ' distinct source URLs OR by one clearly authoritative source. ' +
|
|
57
|
+
'Discard claims found in a single weak source or that conflict with others.\\n\\nSOURCES JSON:\\n' + JSON.stringify(allSources),
|
|
58
|
+
{ label: 'cross-check', schema: { type: 'object', properties: { supported: { type: 'array', items: { type: 'object', properties: { claim: { type: 'string' }, sources: { type: 'array', items: { type: 'string' } } }, required: ['claim', 'sources'] } }, discarded: { type: 'array', items: { type: 'string' } } }, required: ['supported'] } }
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
phase('Report')
|
|
62
|
+
const report = await agent(
|
|
63
|
+
'Write a concise, well-structured research report that answers the question using ONLY the supported claims below. ' +
|
|
64
|
+
'Cite source URLs inline next to each claim. If the evidence is thin, say so explicitly.\\n\\n' +
|
|
65
|
+
'QUESTION: ' + question + '\\n\\nSUPPORTED CLAIMS JSON:\\n' + JSON.stringify((verdict && verdict.supported) || []),
|
|
66
|
+
{ label: 'write report' }
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return { question, queries, supported: (verdict && verdict.supported) || [], report }`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Generate a codebase audit workflow.
|
|
73
|
+
*
|
|
74
|
+
* `scope` and each `checks` entry are user-supplied strings that get baked
|
|
75
|
+
* directly into the generated script's source (unlike the runtime-args-driven
|
|
76
|
+
* generators above), so every one is embedded via JSON.stringify — a proper JS
|
|
77
|
+
* string literal that can't be broken out of by a quote, backslash, or
|
|
78
|
+
* backtick in the value. Only the human-readable `meta.description` is
|
|
79
|
+
* truncated for display; the operative `scope` used by the agents is always
|
|
80
|
+
* the full, untruncated value.
|
|
81
|
+
*/
|
|
82
|
+
export function generateCodebaseAuditWorkflow(scope, checks) {
|
|
83
|
+
const displayScope = scope.length > 60 ? `${scope.slice(0, 60)}…` : scope;
|
|
84
|
+
const checkAgents = checks
|
|
85
|
+
.map((check, i) => {
|
|
86
|
+
const label = check
|
|
87
|
+
.toLowerCase()
|
|
88
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
89
|
+
.replace(/^-+|-+$/g, "")
|
|
90
|
+
.slice(0, 20) || `check-${i + 1}`;
|
|
91
|
+
return ` () => agent(${JSON.stringify(`Audit ${check} across: `)} + scope, { label: ${JSON.stringify(label)} }),`;
|
|
92
|
+
})
|
|
93
|
+
.join("\n");
|
|
94
|
+
return `export const meta = {
|
|
95
|
+
name: 'codebase_audit',
|
|
96
|
+
description: ${JSON.stringify(`Codebase audit: ${displayScope}`)},
|
|
97
|
+
phases: [
|
|
98
|
+
{ title: 'Individual Checks' },
|
|
99
|
+
{ title: 'Cross-Validation' },
|
|
100
|
+
{ title: 'Report' },
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
phase('Individual Checks');
|
|
105
|
+
const scope = ${JSON.stringify(scope)};
|
|
106
|
+
const findings = await parallel([
|
|
107
|
+
${checkAgents}
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
phase('Cross-Validation');
|
|
111
|
+
const validated = await agent(
|
|
112
|
+
'Cross-validate these audit findings. Remove false positives and confirm real issues:\\n' +
|
|
113
|
+
JSON.stringify(findings),
|
|
114
|
+
{ label: 'validator' }
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
phase('Report');
|
|
118
|
+
const report = await agent(
|
|
119
|
+
'Generate a prioritized audit report with actionable recommendations:\\n' + validated,
|
|
120
|
+
{ label: 'report-writer' }
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
return { findings, validated, report };`;
|
|
124
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { AgentUsage } from "./agent.js";
|
|
3
|
+
import type { AgentHistoryEntry } from "./agent-history.js";
|
|
4
|
+
import type { WorkflowErrorCode } from "./errors.js";
|
|
5
|
+
import type { WorkflowMeta } from "./workflow.js";
|
|
6
|
+
export type WorkflowAgentStatus = "queued" | "running" | "done" | "error" | "skipped";
|
|
7
|
+
export interface WorkflowAgentSnapshot {
|
|
8
|
+
id: number;
|
|
9
|
+
/** Runtime call identity (`${runId}:${callIndex}`), used to rehydrate journaled results. */
|
|
10
|
+
callId?: string;
|
|
11
|
+
label: string;
|
|
12
|
+
phase?: string;
|
|
13
|
+
prompt: string;
|
|
14
|
+
status: WorkflowAgentStatus;
|
|
15
|
+
/** Full agent result, retained for the interactive detail pager. */
|
|
16
|
+
result?: unknown;
|
|
17
|
+
resultPreview?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
errorCode?: WorkflowErrorCode;
|
|
20
|
+
recoverable?: boolean;
|
|
21
|
+
history?: AgentHistoryEntry[];
|
|
22
|
+
/** Tokens used by this agent (a scalar estimate when the provider reports no usage). */
|
|
23
|
+
tokens?: number;
|
|
24
|
+
/** Per-agent token usage breakdown (fresh input+output vs cached), when known. */
|
|
25
|
+
tokenUsage?: AgentUsage;
|
|
26
|
+
/** The model this agent ran on (provider/id), when known. */
|
|
27
|
+
model?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface WorkflowSnapshot {
|
|
30
|
+
name: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
phases: string[];
|
|
33
|
+
currentPhase?: string;
|
|
34
|
+
logs: string[];
|
|
35
|
+
agents: WorkflowAgentSnapshot[];
|
|
36
|
+
agentCount: number;
|
|
37
|
+
runningCount: number;
|
|
38
|
+
doneCount: number;
|
|
39
|
+
errorCount: number;
|
|
40
|
+
durationMs?: number;
|
|
41
|
+
result?: unknown;
|
|
42
|
+
tokenUsage?: {
|
|
43
|
+
input: number;
|
|
44
|
+
output: number;
|
|
45
|
+
total: number;
|
|
46
|
+
cost?: number;
|
|
47
|
+
cacheRead?: number;
|
|
48
|
+
cacheWrite?: number;
|
|
49
|
+
};
|
|
50
|
+
runId?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface WorkflowDisplay {
|
|
53
|
+
update(snapshot: WorkflowSnapshot): void;
|
|
54
|
+
complete(snapshot: WorkflowSnapshot): void;
|
|
55
|
+
clear(): void;
|
|
56
|
+
}
|
|
57
|
+
export interface WorkflowDisplayOptions {
|
|
58
|
+
key?: string;
|
|
59
|
+
placement?: "aboveEditor" | "belowEditor";
|
|
60
|
+
maxAgents?: number;
|
|
61
|
+
showStatus?: boolean;
|
|
62
|
+
showResultPreviews?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Displayable fresh/cached figures from a usage breakdown and/or a scalar
|
|
66
|
+
* estimate. The token pipeline has two sources that don't always agree: the
|
|
67
|
+
* provider-reported breakdown (input/output/cacheRead/cacheWrite) and a scalar
|
|
68
|
+
* estimate (`total` at run level, `tokens` per agent) that keeps accruing even
|
|
69
|
+
* when the provider reports nothing. Two rules:
|
|
70
|
+
* - `fresh` counts input+output+cacheWrite: cache writes are first-time
|
|
71
|
+
* ingestion billed at full (or premium) price, so hiding them would
|
|
72
|
+
* under-report real spend; only cacheRead is the cheap reuse shown apart.
|
|
73
|
+
* - `fresh` is never less than what the estimate can account for after
|
|
74
|
+
* removing cache reads, so estimate-only providers, cost-only providers
|
|
75
|
+
* (billed but zero token counts), and mixed runs keep the count the display
|
|
76
|
+
* showed before the split existed, instead of a false "0 tok".
|
|
77
|
+
*/
|
|
78
|
+
export declare function tokenFigures(usage: Partial<AgentUsage> | undefined, scalarTokens?: number): {
|
|
79
|
+
fresh: number;
|
|
80
|
+
cacheRead: number;
|
|
81
|
+
};
|
|
82
|
+
/** Sum a set of agents into fresh vs cacheRead totals, via {@link tokenFigures}. */
|
|
83
|
+
export declare function aggregateAgentUsage(agents: ReadonlyArray<Pick<WorkflowAgentSnapshot, "tokens" | "tokenUsage">>): {
|
|
84
|
+
fresh: number;
|
|
85
|
+
cacheRead: number;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Format a token count for a display surface: "12.4K tok" on its own, or
|
|
89
|
+
* "89K tok · 3.0M cached" when there were cache reads. The cache segment is shown
|
|
90
|
+
* only when `cacheRead > 0`, so a non-caching provider (or a single-turn agent that
|
|
91
|
+
* never re-reads its cache) reads as a plain "tok" rather than a bare, contextless
|
|
92
|
+
* "fresh". `fmt` adapts the number style per surface (compact in panels, full in
|
|
93
|
+
* the print view).
|
|
94
|
+
*/
|
|
95
|
+
export declare function fmtTokenCount(fresh: number, cacheRead: number, fmt: (n: number) => string): string;
|
|
96
|
+
/**
|
|
97
|
+
* Like {@link fmtTokenCount}, but "" when nothing is known yet (both figures 0),
|
|
98
|
+
* so surfaces omit the segment instead of rendering a false "0 tok" — e.g. for a
|
|
99
|
+
* journal-replayed resume or a run whose agents were all skipped. Every surface
|
|
100
|
+
* should use this rather than re-implementing the zero guard.
|
|
101
|
+
*/
|
|
102
|
+
export declare function fmtTokenSegment(figures: {
|
|
103
|
+
fresh: number;
|
|
104
|
+
cacheRead: number;
|
|
105
|
+
}, fmt: (n: number) => string): string;
|
|
106
|
+
/**
|
|
107
|
+
* "$1.23" from one cent up, four decimals below it, and "<$0.0001" for
|
|
108
|
+
* anything smaller — a real cost never rounds to a zero-looking "$0.00".
|
|
109
|
+
*/
|
|
110
|
+
export declare function fmtCost(cost: number): string;
|
|
111
|
+
/** Full (non-compact) number style for print/text surfaces: locale-grouped digits. */
|
|
112
|
+
export declare const fmtFull: (n: number) => string;
|
|
113
|
+
export declare function createWorkflowSnapshot(meta: WorkflowMeta): WorkflowSnapshot;
|
|
114
|
+
export declare function recomputeWorkflowSnapshot(snapshot: WorkflowSnapshot): WorkflowSnapshot;
|
|
115
|
+
export declare function createWidgetWorkflowDisplay(ctx: Pick<ExtensionContext, "ui" | "hasUI">, options?: WorkflowDisplayOptions): WorkflowDisplay;
|
|
116
|
+
export declare function createToolUpdateWorkflowDisplay(onUpdate: ((result: {
|
|
117
|
+
content: Array<{
|
|
118
|
+
type: "text";
|
|
119
|
+
text: string;
|
|
120
|
+
}>;
|
|
121
|
+
details: unknown;
|
|
122
|
+
}) => void) | undefined, ctx?: Pick<ExtensionContext, "ui" | "hasUI">, options?: WorkflowDisplayOptions & {
|
|
123
|
+
streamToolUpdates?: boolean;
|
|
124
|
+
}): WorkflowDisplay;
|
|
125
|
+
/** Minimal theme surface so rendering works without a real Theme (tool output, tests). */
|
|
126
|
+
export interface ThemeLike {
|
|
127
|
+
fg(color: string, text: string): string;
|
|
128
|
+
bold(text: string): string;
|
|
129
|
+
}
|
|
130
|
+
export declare function renderWorkflowLines(snapshot: WorkflowSnapshot, options?: WorkflowDisplayOptions, theme?: ThemeLike): string[];
|
|
131
|
+
export declare function renderWorkflowText(snapshot: WorkflowSnapshot, completed?: boolean): string;
|
|
132
|
+
export declare function statusIcon(status: WorkflowAgentStatus): string;
|
|
133
|
+
export declare function shorten(value: string, max: number): string;
|
|
134
|
+
export declare function preview(value: unknown, max?: number): string;
|
package/dist/display.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Displayable fresh/cached figures from a usage breakdown and/or a scalar
|
|
3
|
+
* estimate. The token pipeline has two sources that don't always agree: the
|
|
4
|
+
* provider-reported breakdown (input/output/cacheRead/cacheWrite) and a scalar
|
|
5
|
+
* estimate (`total` at run level, `tokens` per agent) that keeps accruing even
|
|
6
|
+
* when the provider reports nothing. Two rules:
|
|
7
|
+
* - `fresh` counts input+output+cacheWrite: cache writes are first-time
|
|
8
|
+
* ingestion billed at full (or premium) price, so hiding them would
|
|
9
|
+
* under-report real spend; only cacheRead is the cheap reuse shown apart.
|
|
10
|
+
* - `fresh` is never less than what the estimate can account for after
|
|
11
|
+
* removing cache reads, so estimate-only providers, cost-only providers
|
|
12
|
+
* (billed but zero token counts), and mixed runs keep the count the display
|
|
13
|
+
* showed before the split existed, instead of a false "0 tok".
|
|
14
|
+
*/
|
|
15
|
+
export function tokenFigures(usage, scalarTokens) {
|
|
16
|
+
const cacheRead = usage?.cacheRead ?? 0;
|
|
17
|
+
const reported = (usage?.input ?? 0) + (usage?.output ?? 0) + (usage?.cacheWrite ?? 0);
|
|
18
|
+
const estimate = Math.max(scalarTokens ?? 0, usage?.total ?? 0);
|
|
19
|
+
return { fresh: Math.max(reported, estimate - cacheRead), cacheRead };
|
|
20
|
+
}
|
|
21
|
+
/** Sum a set of agents into fresh vs cacheRead totals, via {@link tokenFigures}. */
|
|
22
|
+
export function aggregateAgentUsage(agents) {
|
|
23
|
+
let fresh = 0;
|
|
24
|
+
let cacheRead = 0;
|
|
25
|
+
for (const a of agents) {
|
|
26
|
+
const f = tokenFigures(a.tokenUsage, a.tokens);
|
|
27
|
+
fresh += f.fresh;
|
|
28
|
+
cacheRead += f.cacheRead;
|
|
29
|
+
}
|
|
30
|
+
return { fresh, cacheRead };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Format a token count for a display surface: "12.4K tok" on its own, or
|
|
34
|
+
* "89K tok · 3.0M cached" when there were cache reads. The cache segment is shown
|
|
35
|
+
* only when `cacheRead > 0`, so a non-caching provider (or a single-turn agent that
|
|
36
|
+
* never re-reads its cache) reads as a plain "tok" rather than a bare, contextless
|
|
37
|
+
* "fresh". `fmt` adapts the number style per surface (compact in panels, full in
|
|
38
|
+
* the print view).
|
|
39
|
+
*/
|
|
40
|
+
export function fmtTokenCount(fresh, cacheRead, fmt) {
|
|
41
|
+
const f = fmt(fresh) || "0";
|
|
42
|
+
return cacheRead > 0 ? `${f} tok · ${fmt(cacheRead)} cached` : `${f} tok`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Like {@link fmtTokenCount}, but "" when nothing is known yet (both figures 0),
|
|
46
|
+
* so surfaces omit the segment instead of rendering a false "0 tok" — e.g. for a
|
|
47
|
+
* journal-replayed resume or a run whose agents were all skipped. Every surface
|
|
48
|
+
* should use this rather than re-implementing the zero guard.
|
|
49
|
+
*/
|
|
50
|
+
export function fmtTokenSegment(figures, fmt) {
|
|
51
|
+
return figures.fresh + figures.cacheRead > 0 ? fmtTokenCount(figures.fresh, figures.cacheRead, fmt) : "";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* "$1.23" from one cent up, four decimals below it, and "<$0.0001" for
|
|
55
|
+
* anything smaller — a real cost never rounds to a zero-looking "$0.00".
|
|
56
|
+
*/
|
|
57
|
+
export function fmtCost(cost) {
|
|
58
|
+
if (cost > 0 && cost < 0.0001)
|
|
59
|
+
return "<$0.0001";
|
|
60
|
+
return `$${cost.toFixed(cost >= 0.01 ? 2 : 4)}`;
|
|
61
|
+
}
|
|
62
|
+
/** Full (non-compact) number style for print/text surfaces: locale-grouped digits. */
|
|
63
|
+
export const fmtFull = (n) => n.toLocaleString();
|
|
64
|
+
export function createWorkflowSnapshot(meta) {
|
|
65
|
+
return {
|
|
66
|
+
name: meta.name,
|
|
67
|
+
description: meta.description,
|
|
68
|
+
phases: meta.phases?.map((phase) => phase.title) ?? [],
|
|
69
|
+
logs: [],
|
|
70
|
+
agents: [],
|
|
71
|
+
agentCount: 0,
|
|
72
|
+
runningCount: 0,
|
|
73
|
+
doneCount: 0,
|
|
74
|
+
errorCount: 0,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function recomputeWorkflowSnapshot(snapshot) {
|
|
78
|
+
const runningCount = snapshot.agents.filter((agent) => agent.status === "running").length;
|
|
79
|
+
const doneCount = snapshot.agents.filter((agent) => agent.status === "done").length;
|
|
80
|
+
const errorCount = snapshot.agents.filter((agent) => agent.status === "error").length;
|
|
81
|
+
return { ...snapshot, agentCount: snapshot.agents.length, runningCount, doneCount, errorCount };
|
|
82
|
+
}
|
|
83
|
+
export function createWidgetWorkflowDisplay(ctx, options = {}) {
|
|
84
|
+
const key = options.key ?? "workflow";
|
|
85
|
+
const placement = options.placement ?? "belowEditor";
|
|
86
|
+
const showStatus = options.showStatus ?? false;
|
|
87
|
+
// Mutable state captured by the component closure so re-renders
|
|
88
|
+
// always read the latest snapshot even though the factory ran once.
|
|
89
|
+
let snapshot;
|
|
90
|
+
let completed = false;
|
|
91
|
+
// Store the factory so update()/complete() can re-register it to trigger re-render.
|
|
92
|
+
const widgetFactory = (_tui, theme) => ({
|
|
93
|
+
render: () => (snapshot ? renderWorkflowLines(snapshot, options, theme) : []),
|
|
94
|
+
invalidate: () => { },
|
|
95
|
+
});
|
|
96
|
+
if (ctx.hasUI) {
|
|
97
|
+
ctx.ui.setWidget(key, widgetFactory, { placement });
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
update(s) {
|
|
101
|
+
snapshot = s;
|
|
102
|
+
if (!ctx.hasUI)
|
|
103
|
+
return;
|
|
104
|
+
if (showStatus)
|
|
105
|
+
ctx.ui.setStatus(key, statusLine(s, completed));
|
|
106
|
+
ctx.ui.setWidget(key, widgetFactory, { placement });
|
|
107
|
+
},
|
|
108
|
+
complete(s) {
|
|
109
|
+
snapshot = s;
|
|
110
|
+
completed = true;
|
|
111
|
+
if (!ctx.hasUI)
|
|
112
|
+
return;
|
|
113
|
+
if (showStatus)
|
|
114
|
+
ctx.ui.setStatus(key, statusLine(s, true));
|
|
115
|
+
ctx.ui.setWidget(key, widgetFactory, { placement });
|
|
116
|
+
},
|
|
117
|
+
clear() {
|
|
118
|
+
if (!ctx.hasUI)
|
|
119
|
+
return;
|
|
120
|
+
if (showStatus)
|
|
121
|
+
ctx.ui.setStatus(key, undefined);
|
|
122
|
+
ctx.ui.setWidget(key, undefined);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export function createToolUpdateWorkflowDisplay(onUpdate, ctx, options = {}) {
|
|
127
|
+
const widget = ctx ? createWidgetWorkflowDisplay(ctx, options) : undefined;
|
|
128
|
+
const streamToolUpdates = options.streamToolUpdates ?? !ctx?.hasUI;
|
|
129
|
+
const emit = (snapshot, completed = false) => {
|
|
130
|
+
if (streamToolUpdates) {
|
|
131
|
+
onUpdate?.({
|
|
132
|
+
content: [{ type: "text", text: renderWorkflowText(snapshot, completed) }],
|
|
133
|
+
details: snapshot,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (completed)
|
|
137
|
+
widget?.complete(snapshot);
|
|
138
|
+
else
|
|
139
|
+
widget?.update(snapshot);
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
update(snapshot) {
|
|
143
|
+
emit(snapshot, false);
|
|
144
|
+
},
|
|
145
|
+
complete(snapshot) {
|
|
146
|
+
emit(snapshot, true);
|
|
147
|
+
},
|
|
148
|
+
clear() {
|
|
149
|
+
widget?.clear();
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** Identity passthrough for contexts where no theme is available (tool text output). */
|
|
154
|
+
const NO_THEME = { fg: (_c, t) => t, bold: (t) => t };
|
|
155
|
+
/** The bracketed per-agent token cell (" [89 tok · 3,000 cached]"), or "" when nothing is known yet. */
|
|
156
|
+
function agentTokenCell(agent, theme) {
|
|
157
|
+
const segment = fmtTokenSegment(tokenFigures(agent.tokenUsage, agent.tokens), fmtFull);
|
|
158
|
+
return segment ? theme.fg("dim", ` [${segment}]`) : "";
|
|
159
|
+
}
|
|
160
|
+
export function renderWorkflowLines(snapshot, options = {}, theme = NO_THEME) {
|
|
161
|
+
const maxAgents = options.maxAgents ?? 8;
|
|
162
|
+
const showResultPreviews = options.showResultPreviews ?? false;
|
|
163
|
+
const state = snapshot.errorCount > 0
|
|
164
|
+
? `, ${snapshot.errorCount} errors`
|
|
165
|
+
: snapshot.runningCount > 0
|
|
166
|
+
? `, ${snapshot.runningCount} running`
|
|
167
|
+
: "";
|
|
168
|
+
// Build header with token info (and cost when the provider reports it)
|
|
169
|
+
const usage = snapshot.tokenUsage;
|
|
170
|
+
const costInfo = usage?.cost ? ` · ${fmtCost(usage.cost)}` : "";
|
|
171
|
+
const segment = fmtTokenSegment(tokenFigures(usage), fmtFull);
|
|
172
|
+
const tokenInfo = `${segment ? ` · ${segment}` : ""}${costInfo}`;
|
|
173
|
+
const lines = [
|
|
174
|
+
`${theme.bold(`◆ Workflow: ${snapshot.name}`)} (${snapshot.doneCount}/${snapshot.agentCount} done${state}${tokenInfo})`,
|
|
175
|
+
];
|
|
176
|
+
const phaseNames = snapshot.phases.length
|
|
177
|
+
? snapshot.phases
|
|
178
|
+
: unique(snapshot.agents.map((agent) => agent.phase).filter(Boolean));
|
|
179
|
+
const rendered = new Set();
|
|
180
|
+
for (const phase of phaseNames) {
|
|
181
|
+
const agents = snapshot.agents.filter((agent) => agent.phase === phase);
|
|
182
|
+
for (const agent of agents)
|
|
183
|
+
rendered.add(agent);
|
|
184
|
+
const done = agents.filter((agent) => agent.status === "done").length;
|
|
185
|
+
const running = agents.filter((agent) => agent.status === "running").length;
|
|
186
|
+
const errors = agents.filter((agent) => agent.status === "error").length;
|
|
187
|
+
const skipped = agents.filter((agent) => agent.status === "skipped").length;
|
|
188
|
+
const complete = agents.length > 0 && done + errors + skipped === agents.length;
|
|
189
|
+
const marker = running > 0 || (!complete && snapshot.currentPhase === phase) ? "▶" : complete ? "✓" : " ";
|
|
190
|
+
lines.push(theme.fg("accent", ` ${marker} ${phase}`) +
|
|
191
|
+
theme.fg("dim", ` ${done}/${agents.length}${running ? ` · ${running} running` : ""}${errors ? ` · ${errors} errors` : ""}${skipped ? ` · ${skipped} skipped` : ""}`));
|
|
192
|
+
const visibleAgents = agents.slice(-maxAgents);
|
|
193
|
+
for (const agent of visibleAgents) {
|
|
194
|
+
const order = `[${agent.id}]`;
|
|
195
|
+
const result = showResultPreviews && agent.resultPreview ? ` — ${agent.resultPreview}` : "";
|
|
196
|
+
lines.push(` ${order} ${statusIcon(agent.status)} ${shorten(agent.label, 48)}${agentTokenCell(agent, theme)}${result}`);
|
|
197
|
+
}
|
|
198
|
+
if (agents.length > visibleAgents.length)
|
|
199
|
+
lines.push(theme.fg("dim", ` … ${agents.length - visibleAgents.length} earlier agents`));
|
|
200
|
+
}
|
|
201
|
+
const unphased = snapshot.agents.filter((agent) => !rendered.has(agent));
|
|
202
|
+
if (unphased.length) {
|
|
203
|
+
lines.push(theme.fg("accent", " Unphased"));
|
|
204
|
+
for (const agent of unphased.slice(-maxAgents)) {
|
|
205
|
+
const result = showResultPreviews && agent.resultPreview ? ` — ${agent.resultPreview}` : "";
|
|
206
|
+
lines.push(` [${agent.id}] ${statusIcon(agent.status)} ${shorten(agent.label, 48)}${agentTokenCell(agent, theme)}${result}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return lines;
|
|
210
|
+
}
|
|
211
|
+
export function renderWorkflowText(snapshot, completed = false) {
|
|
212
|
+
const header = completed ? "Workflow completed" : "Workflow running";
|
|
213
|
+
return [header, ...renderWorkflowLines(snapshot)].join("\n");
|
|
214
|
+
}
|
|
215
|
+
function statusLine(snapshot, completed) {
|
|
216
|
+
if (completed)
|
|
217
|
+
return `workflow ✓ ${snapshot.name}: ${snapshot.doneCount}/${snapshot.agentCount}`;
|
|
218
|
+
if (snapshot.runningCount > 0)
|
|
219
|
+
return `workflow ${snapshot.name}: ${snapshot.runningCount} running, ${snapshot.doneCount}/${snapshot.agentCount} done`;
|
|
220
|
+
return `workflow ${snapshot.name}: ${snapshot.doneCount}/${snapshot.agentCount} done`;
|
|
221
|
+
}
|
|
222
|
+
export function statusIcon(status) {
|
|
223
|
+
switch (status) {
|
|
224
|
+
case "queued":
|
|
225
|
+
return "○";
|
|
226
|
+
case "running":
|
|
227
|
+
return "●";
|
|
228
|
+
case "done":
|
|
229
|
+
return "✓";
|
|
230
|
+
case "error":
|
|
231
|
+
return "✗";
|
|
232
|
+
case "skipped":
|
|
233
|
+
return "-";
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function unique(values) {
|
|
237
|
+
return [...new Set(values)];
|
|
238
|
+
}
|
|
239
|
+
export function shorten(value, max) {
|
|
240
|
+
const text = value.replace(/\s+/g, " ").trim();
|
|
241
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
242
|
+
}
|
|
243
|
+
export function preview(value, max = 80) {
|
|
244
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
245
|
+
if (!text)
|
|
246
|
+
return "";
|
|
247
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
248
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standing `/effort` opt-in (pi's answer to CC's ultracode): a session toggle that
|
|
3
|
+
* auto-arms a workflow for substantive interactive messages, with effort-tier
|
|
4
|
+
* guidance nudging fan-out breadth and the hard caps (tokenBudget / maxAgents) the
|
|
5
|
+
* model should set on the workflow tool call.
|
|
6
|
+
*
|
|
7
|
+
* Honest scope: the runtime cannot enforce "reviewer N / loop K" — those live in
|
|
8
|
+
* the script the model writes — so the tiers are guidance plus the model setting
|
|
9
|
+
* the real hard caps (tokenBudget/maxAgents are genuine runtime ceilings). The
|
|
10
|
+
* pre-flight ceiling-confirm dialog (roadmap P1-5 #4) is a downscope point: an
|
|
11
|
+
* `input` hook transforms synchronously and can't await a confirm, so it is left
|
|
12
|
+
* to a follow-up; `/effort` is explicit opt-in, which is the safety valve.
|
|
13
|
+
*/
|
|
14
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
export type EffortLevel = "off" | "high" | "ultra";
|
|
16
|
+
export interface EffortState {
|
|
17
|
+
level: EffortLevel;
|
|
18
|
+
}
|
|
19
|
+
export declare function createEffortState(): EffortState;
|
|
20
|
+
/** The extra directive appended to the forced-workflow prompt for an effort level. */
|
|
21
|
+
export declare function effortDirective(level: EffortLevel): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Whether a message should auto-arm under effort mode: a real interactive request,
|
|
24
|
+
* not a terse acknowledgement or a slash command. (hasTrigger handles the explicit
|
|
25
|
+
* "workflow(s)" keyword separately.)
|
|
26
|
+
*/
|
|
27
|
+
export declare function isSubstantive(text: string): boolean;
|
|
28
|
+
export declare function registerEffortCommand(pi: ExtensionAPI, state: EffortState): void;
|