pi-crew 0.9.12 → 0.9.14
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 +86 -0
- package/README.md +1 -135
- package/docs/bugs/bug-021-notification-badge-counter-misleading.md +293 -0
- package/docs/bugs/bug-023-chain-windows-path-resolution.md +106 -0
- package/package.json +1 -1
- package/src/config/defaults.ts +3 -1
- package/src/extension/knowledge-injection.ts +236 -15
- package/src/extension/pi-api.ts +1 -1
- package/src/extension/team-tool/chain-dispatch.ts +103 -0
- package/src/extension/team-tool/chain-executor.ts +400 -0
- package/src/extension/team-tool/run.ts +9 -0
- package/src/runtime/agent-memory.ts +5 -2
- package/src/runtime/background-runner.ts +49 -0
- package/src/runtime/chain-runner.ts +47 -8
- package/src/runtime/child-pi.ts +119 -0
- package/src/runtime/goal-achievement.ts +131 -0
- package/src/runtime/handoff-manager.ts +7 -0
- package/src/runtime/live-session-runtime.ts +10 -4
- package/src/runtime/pi-json-output.ts +5 -1
- package/src/runtime/recovery-recipes.ts +35 -0
- package/src/runtime/task-output-context.ts +36 -7
- package/src/runtime/task-runner/prompt-builder.ts +5 -1
- package/src/runtime/task-runner.ts +13 -0
- package/src/runtime/team-runner.ts +47 -2
- package/src/schema/team-tool-schema.ts +9 -0
- package/src/state/types.ts +3 -0
- package/src/ui/widget/index.ts +1 -1
- package/src/ui/widget/widget-formatters.ts +14 -1
- package/src/ui/widget/widget-renderer.ts +13 -2
- package/src/utils/redaction.ts +20 -0
|
@@ -114,6 +114,12 @@ export const TeamToolParams = Type.Object({
|
|
|
114
114
|
goal: Type.Optional(
|
|
115
115
|
Type.String({ description: "High-level objective for a team run." }),
|
|
116
116
|
),
|
|
117
|
+
chain: Type.Optional(
|
|
118
|
+
Type.String({
|
|
119
|
+
description:
|
|
120
|
+
'Chain expression: "step1 -> step2 -> step3". Runs each step as a sequential team run, passing handoff context forward via the goal text. Supports inline goals ("...") and @team references. e.g. chain=\'"Research X" -> "Analyze" -> "Write report"\'.',
|
|
121
|
+
}),
|
|
122
|
+
),
|
|
117
123
|
task: Type.Optional(
|
|
118
124
|
Type.String({
|
|
119
125
|
description: "Concrete task text for direct role/agent execution.",
|
|
@@ -370,6 +376,9 @@ export interface TeamToolParamsValue {
|
|
|
370
376
|
role?: string;
|
|
371
377
|
agent?: string;
|
|
372
378
|
goal?: string;
|
|
379
|
+
/** Chain expression: "step1 -> step2 -> step3". Runs each step as a sequential
|
|
380
|
+
* team run with handoff context passed forward via the goal. */
|
|
381
|
+
chain?: string;
|
|
373
382
|
task?: string;
|
|
374
383
|
singleAgent?: boolean;
|
|
375
384
|
runId?: string;
|
package/src/state/types.ts
CHANGED
|
@@ -201,6 +201,9 @@ export interface TeamRunManifest {
|
|
|
201
201
|
args?: unknown;
|
|
202
202
|
summary?: string;
|
|
203
203
|
policyDecisions?: PolicyDecision[];
|
|
204
|
+
/** #2 (assessment): goal-achievement verdict — kills the silent false-green. */
|
|
205
|
+
goalAchieved?: boolean | "unknown";
|
|
206
|
+
goalAchievementNote?: string;
|
|
204
207
|
}
|
|
205
208
|
|
|
206
209
|
export interface UsageState {
|
package/src/ui/widget/index.ts
CHANGED
|
@@ -42,7 +42,7 @@ export function getRenderWidth(width?: number): number {
|
|
|
42
42
|
if (Number.isFinite(stdoutCols) && stdoutCols! > 0) return Math.floor(stdoutCols!);
|
|
43
43
|
return DEFAULT_WIDGET_WIDTH;
|
|
44
44
|
}
|
|
45
|
-
export { notificationBadge } from "./widget-formatters.ts";
|
|
45
|
+
export { notificationBadge, NOTIFICATION_BADGE_CAP } from "./widget-formatters.ts";
|
|
46
46
|
|
|
47
47
|
// ── Constants ─────────────────────────────────────────────────────────
|
|
48
48
|
|
|
@@ -144,9 +144,22 @@ export function agentStats(agent: CrewAgentRecord, liveHandle?: LiveAgentHandle)
|
|
|
144
144
|
|
|
145
145
|
// ── Notification badge ────────────────────────────────────────────────
|
|
146
146
|
|
|
147
|
+
// Bug 021: the bell glyph 🔔 was misread as "queued messages" — users saw
|
|
148
|
+
// `🔔227` and concluded there were 227 pending items, when the value is a
|
|
149
|
+
// CUMULATIVE warning/error/critical count with zero actual queue behind it.
|
|
150
|
+
// Fix: relabel to an explicit "alerts" segment (no bell) and cap the display
|
|
151
|
+
// at 99+ (standard badge practice). The cumulative count stays accurate
|
|
152
|
+
// internally (widgetState.notificationCount) and remains fully logged in
|
|
153
|
+
// .crew/state/notifications/YYYY-MM-DD.jsonl — this bounds presentation only.
|
|
154
|
+
// Deeper fixes (decay window, owner-scope, auto-reset on all-runs-terminal,
|
|
155
|
+
// full deprecation) are product decisions documented in
|
|
156
|
+
// docs/bugs/bug-021-notification-badge-counter-misleading.md.
|
|
157
|
+
export const NOTIFICATION_BADGE_CAP = 99;
|
|
158
|
+
|
|
147
159
|
export function notificationBadge(count: number | undefined, env: NodeJS.ProcessEnv = process.env): string {
|
|
148
160
|
if (!count || count <= 0) return "";
|
|
149
161
|
const term = `${env.TERM ?? ""} ${env.WT_SESSION ?? ""} ${env.TERM_PROGRAM ?? ""}`.toLowerCase();
|
|
150
162
|
const supportsEmoji = !term.includes("dumb") && env.NO_COLOR !== "1";
|
|
151
|
-
|
|
163
|
+
const label = count > NOTIFICATION_BADGE_CAP ? `${NOTIFICATION_BADGE_CAP}+ alerts` : `${count} alerts`;
|
|
164
|
+
return supportsEmoji ? ` · ${label}` : ` [${label}]`;
|
|
152
165
|
}
|
|
@@ -15,6 +15,7 @@ import { computeLiveDurationMs } from "../live-duration.ts";
|
|
|
15
15
|
import { getTaskUsage } from "../../runtime/usage-tracker.ts";
|
|
16
16
|
import { agentActivity, agentStats, elapsed, formatTokensCompact, notificationBadge } from "./widget-formatters.ts";
|
|
17
17
|
import { activeWidgetRuns, shortRunLabel } from "./widget-model.ts";
|
|
18
|
+
import { isFinishedRunStatus } from "../../runtime/process-status.ts";
|
|
18
19
|
import type { WidgetRun } from "./widget-types.ts";
|
|
19
20
|
|
|
20
21
|
const MAX_AGENTS_DISPLAY = 3;
|
|
@@ -69,6 +70,7 @@ export function buildWidgetLines(cwd: string, frame = 0, maxLines = 8, providedR
|
|
|
69
70
|
});
|
|
70
71
|
const completed = agents.filter((a) => a.status === "completed").length;
|
|
71
72
|
const runGlyph = iconForStatus(run.status, { runningGlyph });
|
|
73
|
+
const isTerminal = isFinishedRunStatus(run.status);
|
|
72
74
|
// Run progress line. v1–v3 flickered on snapshot.tasks state, v4 was
|
|
73
75
|
// too minimal (`0/1 agents` only), v5 duplicated the worker activity
|
|
74
76
|
// line (tools/tokens/duration already shown one row below). v6 (this)
|
|
@@ -81,10 +83,19 @@ export function buildWidgetLines(cwd: string, frame = 0, maxLines = 8, providedR
|
|
|
81
83
|
// for a healthy run), and `run.createdAt` is immutable. The format
|
|
82
84
|
// shape `"X/Y agents · Ns"` is therefore truly invariant: same number
|
|
83
85
|
// of `·`-separated fields, same field meanings, every render tick.
|
|
86
|
+
//
|
|
87
|
+
// Bug 022 (timer-fix + label): for TERMINAL runs (failed/cancelled/
|
|
88
|
+
// completed) the elapsed counter previously kept ticking up forever
|
|
89
|
+
// from createdAt (a failed run showed `2028s` and climbing, read as
|
|
90
|
+
// "still running"). Now it FREEZES at updatedAt (when the run
|
|
91
|
+
// reached its terminal status). The status label is also surfaced
|
|
92
|
+
// explicitly so the row cannot be misread as an active run.
|
|
84
93
|
const agentCountText = `${completed}/${agents.length} agents`;
|
|
85
|
-
const
|
|
94
|
+
const runEndMs = isTerminal ? new Date(run.updatedAt).getTime() : Date.now();
|
|
95
|
+
const runElapsedMs = Math.max(0, Number.isFinite(runEndMs) ? runEndMs - new Date(run.createdAt).getTime() : 0);
|
|
86
96
|
const runElapsedText = `${Math.floor(runElapsedMs / 1000)}s`;
|
|
87
|
-
const
|
|
97
|
+
const statusLabel = isTerminal ? ` · ${run.status}` : "";
|
|
98
|
+
const progressPart = `${agentCountText} · ${runElapsedText}${statusLabel}`;
|
|
88
99
|
lines.push(truncate(`├─ ${runGlyph} ${shortRunLabel(run)} · ${progressPart} · ${run.runId.slice(-8)}`, width));
|
|
89
100
|
|
|
90
101
|
const liveForRun = listLiveAgents().filter((a) => a.runId === run.runId);
|
package/src/utils/redaction.ts
CHANGED
|
@@ -19,6 +19,24 @@ export const PEM_PRIVATE_KEY_PATTERN = /-----BEGIN [A-Z ]+PRIVATE KEY-----[\s\S]
|
|
|
19
19
|
// Full mitigation ladder: (1) redaction here + at artifact-write; (2) Phase 1.5
|
|
20
20
|
// sanitized-env verification; (3) sandbox (deferred).
|
|
21
21
|
|
|
22
|
+
// Exclusion list: LLM usage-count key names that contain "token" but are
|
|
23
|
+
// observable metrics, NOT credentials. These must NEVER be redacted so that
|
|
24
|
+
// token-usage observability in events.jsonl is preserved. The isSecretKey()
|
|
25
|
+
// keyword-scan matches '_token' in 'prompt_tokens' (underscore + "token"),
|
|
26
|
+
// falsely classifying usage counts as secrets. See performance/quality
|
|
27
|
+
// assessment fix #5.
|
|
28
|
+
const TOKEN_COUNT_KEYS = new Set([
|
|
29
|
+
"prompt_tokens",
|
|
30
|
+
"completion_tokens",
|
|
31
|
+
"total_tokens",
|
|
32
|
+
"cached_tokens",
|
|
33
|
+
"reasoning_tokens",
|
|
34
|
+
"cached_read_tokens",
|
|
35
|
+
"cached_write_tokens",
|
|
36
|
+
"input_tokens",
|
|
37
|
+
"output_tokens",
|
|
38
|
+
]);
|
|
39
|
+
|
|
22
40
|
// JWT — three base64url segments separated by dots, distinctive "eyJ" headers.
|
|
23
41
|
// Linear: single + on [A-Za-z0-9_-] per segment, no nesting.
|
|
24
42
|
export const JWT_PATTERN = /(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g;
|
|
@@ -43,6 +61,8 @@ export const STRIPE_KEY_PATTERN = /(?<![A-Za-z0-9_])sk_live_[0-9a-zA-Z]{24}(?![0
|
|
|
43
61
|
// a more complex regex, catastrophic backtracking (ReDoS) could result.
|
|
44
62
|
// Any modifications must preserve O(n) complexity where n = keyName.length.
|
|
45
63
|
export function isSecretKey(keyName: string): boolean {
|
|
64
|
+
// Fast path: known token-count keys are never secrets.
|
|
65
|
+
if (TOKEN_COUNT_KEYS.has(keyName.toLowerCase())) return false;
|
|
46
66
|
// Fast path: common secret key names (safe anchored regex, no backtracking)
|
|
47
67
|
const lower = keyName.toLowerCase();
|
|
48
68
|
if (/^(token|apikey|api_key|password|secret|credential|authorization|privatekey|private_key)$/.test(lower)) {
|