pi-crew 0.9.35 → 0.9.37
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 +48 -0
- package/dist/build-meta.json +54 -16
- package/dist/index.mjs +308 -9
- package/dist/index.mjs.map +4 -4
- package/package.json +1 -1
- package/src/extension/register.ts +6 -0
- package/src/extension/registration/subagent-tools.ts +18 -1
- package/src/extension/team-tool/context.ts +60 -4
- package/src/extension/team-tool/handle-settings.ts +1 -1
- package/src/runtime/async-runner.ts +1 -1
- package/src/runtime/progress-tracker.ts +140 -0
- package/src/runtime/verification-gates.ts +9 -4
- package/src/state/instinct-store.ts +5 -0
- package/src/ui/settings-overlay.ts +1 -1
- package/src/workflows/intermediate-store.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.37] — cold context + subagent streaming (2026-07-14)
|
|
4
|
+
|
|
5
|
+
Two performance improvements targeting perceived subagent slowness:
|
|
6
|
+
|
|
7
|
+
### Phase 1: Cold Context (inheritContext default → true)
|
|
8
|
+
|
|
9
|
+
- **`inheritContext` default changed from `false` → `true`** — subagents now receive parent conversation context by default, eliminating redundant re-exploration of files the main session already processed. Configurable via `runtime.inheritContext`.
|
|
10
|
+
- **`buildParentContext()` token budget** — 12K char budget with most-recent-first retention prevents token bloat on large parent sessions.
|
|
11
|
+
- **Noisy content filtering** — skips file dumps (code blocks, `ls` output, import lists >1K chars) and truncates long assistant messages to 200 chars. Compaction summaries kept in full.
|
|
12
|
+
- **Tests**: `test/unit/build-parent-context.test.ts` (10 cases).
|
|
13
|
+
|
|
14
|
+
### Phase 2: Subagent Streaming (child-process → widget)
|
|
15
|
+
|
|
16
|
+
- **`ProgressTracker.handleWorkerEvent()`** — bridges child-process JSON events (from `onJsonEvent`) into `crewEventBus`, enabling real-time widget display of tool calls, assistant text, and token usage for BOTH runtimes (previously only live-session had streaming).
|
|
17
|
+
- **`register.ts` onJsonEvent** — now forwards events to `globalProgressTracker` (was only feeding `overflowTracker`).
|
|
18
|
+
- **`subagent-tools.ts` inline progress** — enriched with real-time worker progress: ⚡ current tool, 📊 tokens, 💬 partial assistant text.
|
|
19
|
+
- **Throttled to 500ms per worker** to prevent widget flooding.
|
|
20
|
+
- **Tests**: `test/unit/progress-tracker-worker.test.ts` (8 cases).
|
|
21
|
+
|
|
22
|
+
## [0.9.36] — security audit round 1 (2026-07-14)
|
|
23
|
+
|
|
24
|
+
Three security hardening fixes from the first formal security audit pass (run via `team_20260714045239_bb49d72211b966a2` + verified via `team_20260714062932_638dbb0310cc9f77`, 12/12 targeted tests pass, typecheck clean):
|
|
25
|
+
|
|
26
|
+
- **`writeIntermediate()` path-traversal guard** — `src/workflows/intermediate-store.ts`. `writeIntermediate()` previously had no validation on `phase`/`stepId` before constructing the filename, while the sibling `readIntermediate()` was already hardened (M-2 fix 2026-06-23). An attacker-controlled `phase` or `stepId` could write JSON to arbitrary paths. Fix: added `isSafePathId(phase) && isSafePathId(stepId)` guard at the top of `writeIntermediate()` — throws `Error("Invalid phase or stepId for intermediate store")` on bad input. Closes the write/read asymmetry.
|
|
27
|
+
- **Tests**: `test/unit/intermediate-store-traversal.test.ts` (+3 cases).
|
|
28
|
+
- **`InstinctStore` `projectId` validation** — `src/state/instinct-store.ts`. All 4 public methods that accept `projectId` (`saveInstinct`, `getProjectInstincts`, `promoteInstinct`, `deleteInstinct`) now call `assertSafePathId("projectId", projectId)` before using the value in `path.join()`.
|
|
29
|
+
- **Tests**: `test/unit/instinct-store-projectid-guard.test.ts` (+4 cases).
|
|
30
|
+
- **Verification env sanitization default → opt-out** — `src/runtime/verification-gates.ts`. `isVerificationEnvSanitizeEnabled()` flipped from opt-in (`PI_CREW_VERIFICATION_SANITIZE_ENV=1`) to opt-out (default `true`, explicitly disabled via env var set to `"0"`). Prevents accidental secret leakage to verification commands in untrusted repos — the safe path is now the default. `PI_CREW_VERIFICATION_PRESERVE_ENV=KEY1,KEY2,...` remains the escape hatch for tests/flows that legitimately need specific secrets.
|
|
31
|
+
- **Tests**: `test/unit/verification-gates-env-sanitize.test.ts` (5 cases) + `test/unit/verification-env-sanitize.test.ts` (7 integration cases).
|
|
32
|
+
|
|
33
|
+
**False positives eliminated during the audit** (documented so they don't reappear):
|
|
34
|
+
|
|
35
|
+
- `runtime/run-cache.ts` — cache keys are SHA-256 hex hashes (16 chars) — inherently safe from path injection.
|
|
36
|
+
- `runtime/task-runner/retrieval-orchestrator.ts` — `rg` invocations use `node:child_process.spawn()` with structured args, no shell. No injection vector.
|
|
37
|
+
- `runtime/pi-spawn.ts:159` `execSync("npm root -g")` — hardcoded command, `execSync` is intentional for Windows `PATHEXT` resolution.
|
|
38
|
+
|
|
39
|
+
**Files changed**: 4 src files, 3 test files, 1 comment-only update. +100 / -13 net.
|
|
40
|
+
|
|
41
|
+
### Audit findings (security survey, see `.gsd/AUDIT-PLAN.md` for full detail)
|
|
42
|
+
|
|
43
|
+
Surveyed 36 process-execution call sites (`execSync`/`spawn`/`execFileSync`), 100+ `import()` sites, 13 `require()` sites, 9 `__proto__` references, path-traversal patterns, env sanitization, and unbounded data structures.
|
|
44
|
+
|
|
45
|
+
- **Path traversal**: 2 real issues fixed (`intermediate-store.ts`, `instinct-store.ts`); 1 false positive (`run-cache.ts`).
|
|
46
|
+
- **Process execution**: 1 critical-pattern (`sh -c` in `verification-gates.ts` — properly guarded by `validateGateCommand()`), 1 `execSync` with hardcoded command (intentional), 34 safe `spawn`/`execFileSync` with args arrays.
|
|
47
|
+
- **Env handling**: 1 real issue fixed (verification env default flipped to opt-out); 2 low/info items (rg inherits full env, `diagnostic-export` writes non-secret env vars — both by design).
|
|
48
|
+
- **Code injection**: 0 `eval`, 0 `new Function`, 0 `vm.*` usage, 0 `prototype[`. 9 `__proto__` references — all prototype-pollution defenses (`POLLUTED_KEYS` deny-lists).
|
|
49
|
+
- **Memory safety**: all major Maps/Sets have MAX_* caps + LRU eviction (workflowCache=32, teamCache=32, asyncAgentReaderCache=128, agentEventSeqCache=1000, liveAgents=5000, NotificationRouter.seen=10000, etc.).
|
|
50
|
+
|
|
3
51
|
## [0.9.35] — performance plan Phases 1–3 + 2 wiring fixes (2026-07-13)
|
|
4
52
|
|
|
5
53
|
Shipped the **forward performance optimization plan** in 3 phases (`docs/performance-optimization-execution-plan.md`) plus 2 follow-up wiring fixes for the `pipeline` workflow and `schedule` action. 8 performance optimizations landed + 1 bundle rebuild. End-to-end verified post-restart via `team action='run', workflow='pipeline', team='research'` (`team_20260713102348_529c8e4e79e90e20`, 4/4 tasks, 142,686 tokens, 6.4 min, pipeline-summary.md emitted with "PIPELINE_WORKFLOW_OK" + scheduled job successfully removed via new subAction). Typecheck clean, 63 + 58 + 92 targeted unit tests pass across the modified suites.
|
package/dist/build-meta.json
CHANGED
|
@@ -8410,7 +8410,7 @@
|
|
|
8410
8410
|
"format": "esm"
|
|
8411
8411
|
},
|
|
8412
8412
|
"src/extension/team-tool/context.ts": {
|
|
8413
|
-
"bytes":
|
|
8413
|
+
"bytes": 5581,
|
|
8414
8414
|
"imports": [
|
|
8415
8415
|
{
|
|
8416
8416
|
"path": "src/extension/tool-result.ts",
|
|
@@ -8577,7 +8577,7 @@
|
|
|
8577
8577
|
"format": "esm"
|
|
8578
8578
|
},
|
|
8579
8579
|
"src/extension/team-tool/handle-settings.ts": {
|
|
8580
|
-
"bytes":
|
|
8580
|
+
"bytes": 17989,
|
|
8581
8581
|
"imports": [
|
|
8582
8582
|
{
|
|
8583
8583
|
"path": "src/config/config.ts",
|
|
@@ -12891,7 +12891,7 @@
|
|
|
12891
12891
|
"format": "esm"
|
|
12892
12892
|
},
|
|
12893
12893
|
"src/runtime/async-runner.ts": {
|
|
12894
|
-
"bytes":
|
|
12894
|
+
"bytes": 14705,
|
|
12895
12895
|
"imports": [
|
|
12896
12896
|
{
|
|
12897
12897
|
"path": "node:child_process",
|
|
@@ -14759,7 +14759,7 @@
|
|
|
14759
14759
|
"format": "esm"
|
|
14760
14760
|
},
|
|
14761
14761
|
"src/runtime/verification-gates.ts": {
|
|
14762
|
-
"bytes":
|
|
14762
|
+
"bytes": 17191,
|
|
14763
14763
|
"imports": [
|
|
14764
14764
|
{
|
|
14765
14765
|
"path": "node:child_process",
|
|
@@ -16950,7 +16950,7 @@
|
|
|
16950
16950
|
"format": "esm"
|
|
16951
16951
|
},
|
|
16952
16952
|
"src/ui/settings-overlay.ts": {
|
|
16953
|
-
"bytes":
|
|
16953
|
+
"bytes": 31025,
|
|
16954
16954
|
"imports": [
|
|
16955
16955
|
{
|
|
16956
16956
|
"path": "src/utils/visual.ts",
|
|
@@ -17720,8 +17720,30 @@
|
|
|
17720
17720
|
],
|
|
17721
17721
|
"format": "esm"
|
|
17722
17722
|
},
|
|
17723
|
+
"src/observability/event-bus.ts": {
|
|
17724
|
+
"bytes": 2219,
|
|
17725
|
+
"imports": [
|
|
17726
|
+
{
|
|
17727
|
+
"path": "src/utils/internal-error.ts",
|
|
17728
|
+
"kind": "import-statement",
|
|
17729
|
+
"original": "../utils/internal-error.ts"
|
|
17730
|
+
}
|
|
17731
|
+
],
|
|
17732
|
+
"format": "esm"
|
|
17733
|
+
},
|
|
17734
|
+
"src/runtime/progress-tracker.ts": {
|
|
17735
|
+
"bytes": 7543,
|
|
17736
|
+
"imports": [
|
|
17737
|
+
{
|
|
17738
|
+
"path": "src/observability/event-bus.ts",
|
|
17739
|
+
"kind": "import-statement",
|
|
17740
|
+
"original": "../observability/event-bus.ts"
|
|
17741
|
+
}
|
|
17742
|
+
],
|
|
17743
|
+
"format": "esm"
|
|
17744
|
+
},
|
|
17723
17745
|
"src/extension/registration/subagent-tools.ts": {
|
|
17724
|
-
"bytes":
|
|
17746
|
+
"bytes": 18292,
|
|
17725
17747
|
"imports": [
|
|
17726
17748
|
{
|
|
17727
17749
|
"path": "node_modules/@sinclair/typebox/build/esm/index.mjs",
|
|
@@ -17753,6 +17775,11 @@
|
|
|
17753
17775
|
"kind": "import-statement",
|
|
17754
17776
|
"original": "../../runtime/crew-agent-records.ts"
|
|
17755
17777
|
},
|
|
17778
|
+
{
|
|
17779
|
+
"path": "src/runtime/progress-tracker.ts",
|
|
17780
|
+
"kind": "import-statement",
|
|
17781
|
+
"original": "../../runtime/progress-tracker.ts"
|
|
17782
|
+
},
|
|
17756
17783
|
{
|
|
17757
17784
|
"path": "src/runtime/role-permission.ts",
|
|
17758
17785
|
"kind": "import-statement",
|
|
@@ -17993,7 +18020,7 @@
|
|
|
17993
18020
|
"format": "esm"
|
|
17994
18021
|
},
|
|
17995
18022
|
"src/extension/register.ts": {
|
|
17996
|
-
"bytes":
|
|
18023
|
+
"bytes": 68402,
|
|
17997
18024
|
"imports": [
|
|
17998
18025
|
{
|
|
17999
18026
|
"path": "node:fs",
|
|
@@ -18315,6 +18342,11 @@
|
|
|
18315
18342
|
"kind": "import-statement",
|
|
18316
18343
|
"original": "../i18n.ts"
|
|
18317
18344
|
},
|
|
18345
|
+
{
|
|
18346
|
+
"path": "src/runtime/progress-tracker.ts",
|
|
18347
|
+
"kind": "import-statement",
|
|
18348
|
+
"original": "../runtime/progress-tracker.ts"
|
|
18349
|
+
},
|
|
18318
18350
|
{
|
|
18319
18351
|
"path": "src/runtime/session-resources.ts",
|
|
18320
18352
|
"kind": "import-statement",
|
|
@@ -18415,7 +18447,7 @@
|
|
|
18415
18447
|
"imports": [],
|
|
18416
18448
|
"exports": [],
|
|
18417
18449
|
"inputs": {},
|
|
18418
|
-
"bytes":
|
|
18450
|
+
"bytes": 6717985
|
|
18419
18451
|
},
|
|
18420
18452
|
"dist/index.mjs": {
|
|
18421
18453
|
"imports": [
|
|
@@ -20931,7 +20963,7 @@
|
|
|
20931
20963
|
"bytesInOutput": 1541
|
|
20932
20964
|
},
|
|
20933
20965
|
"src/extension/team-tool/context.ts": {
|
|
20934
|
-
"bytesInOutput":
|
|
20966
|
+
"bytesInOutput": 3237
|
|
20935
20967
|
},
|
|
20936
20968
|
"src/extension/team-tool/intent-policy.ts": {
|
|
20937
20969
|
"bytesInOutput": 1245
|
|
@@ -20952,7 +20984,7 @@
|
|
|
20952
20984
|
"bytesInOutput": 1876
|
|
20953
20985
|
},
|
|
20954
20986
|
"src/extension/team-tool/handle-settings.ts": {
|
|
20955
|
-
"bytesInOutput":
|
|
20987
|
+
"bytesInOutput": 16051
|
|
20956
20988
|
},
|
|
20957
20989
|
"src/workflows/validate-workflow.ts": {
|
|
20958
20990
|
"bytesInOutput": 1927
|
|
@@ -21546,7 +21578,7 @@
|
|
|
21546
21578
|
"bytesInOutput": 2699
|
|
21547
21579
|
},
|
|
21548
21580
|
"src/runtime/async-runner.ts": {
|
|
21549
|
-
"bytesInOutput":
|
|
21581
|
+
"bytesInOutput": 8943
|
|
21550
21582
|
},
|
|
21551
21583
|
"src/subagents/async-entry.ts": {
|
|
21552
21584
|
"bytesInOutput": 119
|
|
@@ -21792,7 +21824,7 @@
|
|
|
21792
21824
|
"bytesInOutput": 810
|
|
21793
21825
|
},
|
|
21794
21826
|
"src/runtime/verification-gates.ts": {
|
|
21795
|
-
"bytesInOutput":
|
|
21827
|
+
"bytesInOutput": 7597
|
|
21796
21828
|
},
|
|
21797
21829
|
"src/runtime/worker-startup.ts": {
|
|
21798
21830
|
"bytesInOutput": 2344
|
|
@@ -21960,7 +21992,7 @@
|
|
|
21960
21992
|
"bytesInOutput": 15659
|
|
21961
21993
|
},
|
|
21962
21994
|
"src/ui/settings-overlay.ts": {
|
|
21963
|
-
"bytesInOutput":
|
|
21995
|
+
"bytesInOutput": 30588
|
|
21964
21996
|
},
|
|
21965
21997
|
"src/extension/registration/commands.ts": {
|
|
21966
21998
|
"bytesInOutput": 40488
|
|
@@ -22020,7 +22052,7 @@
|
|
|
22020
22052
|
"bytesInOutput": 3150
|
|
22021
22053
|
},
|
|
22022
22054
|
"src/extension/register.ts": {
|
|
22023
|
-
"bytesInOutput":
|
|
22055
|
+
"bytesInOutput": 45685
|
|
22024
22056
|
},
|
|
22025
22057
|
"src/runtime/batch-barrier.ts": {
|
|
22026
22058
|
"bytesInOutput": 3060
|
|
@@ -22104,11 +22136,17 @@
|
|
|
22104
22136
|
"bytesInOutput": 647
|
|
22105
22137
|
},
|
|
22106
22138
|
"src/extension/registration/subagent-tools.ts": {
|
|
22107
|
-
"bytesInOutput":
|
|
22139
|
+
"bytesInOutput": 16197
|
|
22108
22140
|
},
|
|
22109
22141
|
"src/i18n.ts": {
|
|
22110
22142
|
"bytesInOutput": 6837
|
|
22111
22143
|
},
|
|
22144
|
+
"src/observability/event-bus.ts": {
|
|
22145
|
+
"bytesInOutput": 1170
|
|
22146
|
+
},
|
|
22147
|
+
"src/runtime/progress-tracker.ts": {
|
|
22148
|
+
"bytesInOutput": 6660
|
|
22149
|
+
},
|
|
22112
22150
|
"src/extension/session-summary.ts": {
|
|
22113
22151
|
"bytesInOutput": 512
|
|
22114
22152
|
},
|
|
@@ -22125,7 +22163,7 @@
|
|
|
22125
22163
|
"bytesInOutput": 81
|
|
22126
22164
|
}
|
|
22127
22165
|
},
|
|
22128
|
-
"bytes":
|
|
22166
|
+
"bytes": 3182689
|
|
22129
22167
|
}
|
|
22130
22168
|
}
|
|
22131
22169
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -24757,6 +24757,7 @@ var init_workflow_serializer = __esm({
|
|
|
24757
24757
|
// src/extension/team-tool/context.ts
|
|
24758
24758
|
var context_exports = {};
|
|
24759
24759
|
__export(context_exports, {
|
|
24760
|
+
MAX_PARENT_CONTEXT_CHARS: () => MAX_PARENT_CONTEXT_CHARS,
|
|
24760
24761
|
buildParentContext: () => buildParentContext,
|
|
24761
24762
|
configRecord: () => configRecord,
|
|
24762
24763
|
formatScoped: () => formatScoped,
|
|
@@ -24780,6 +24781,10 @@ function extractTextContent(content) {
|
|
|
24780
24781
|
(part) => part && typeof part === "object" && !Array.isArray(part) && typeof part.text === "string" ? part.text : ""
|
|
24781
24782
|
).filter(Boolean).join("\n");
|
|
24782
24783
|
}
|
|
24784
|
+
function isNoisyContent(text) {
|
|
24785
|
+
if (text.length < NOISY_THRESHOLD) return false;
|
|
24786
|
+
return NOISY_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
24787
|
+
}
|
|
24783
24788
|
function buildParentContext(ctx) {
|
|
24784
24789
|
const branch = ctx.sessionManager?.getBranch?.();
|
|
24785
24790
|
if (!Array.isArray(branch) || branch.length === 0) return void 0;
|
|
@@ -24787,28 +24792,50 @@ function buildParentContext(ctx) {
|
|
|
24787
24792
|
for (const entry of branch.slice(-20)) {
|
|
24788
24793
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
24789
24794
|
const record = entry;
|
|
24790
|
-
if (record.type === "compaction" && typeof record.summary === "string")
|
|
24795
|
+
if (record.type === "compaction" && typeof record.summary === "string") {
|
|
24796
|
+
parts.push(`[Summary]: ${record.summary}`);
|
|
24797
|
+
continue;
|
|
24798
|
+
}
|
|
24791
24799
|
const message = record.message && typeof record.message === "object" && !Array.isArray(record.message) ? record.message : void 0;
|
|
24792
24800
|
if (!message || message.role !== "user" && message.role !== "assistant") continue;
|
|
24793
|
-
|
|
24794
|
-
if (text)
|
|
24801
|
+
let text = extractTextContent(message.content).trim();
|
|
24802
|
+
if (!text) continue;
|
|
24803
|
+
if (isNoisyContent(text)) continue;
|
|
24804
|
+
if (message.role === "assistant" && text.length > MAX_ASSISTANT_MSG_CHARS) {
|
|
24805
|
+
text = `${text.slice(0, TRUNCATED_ASSISTANT_CHARS)}\u2026`;
|
|
24806
|
+
}
|
|
24807
|
+
parts.push(`[${message.role === "user" ? "User" : "Assistant"}]: ${text}`);
|
|
24795
24808
|
}
|
|
24796
24809
|
if (!parts.length) return void 0;
|
|
24810
|
+
let totalChars = 0;
|
|
24811
|
+
const budgeted = [];
|
|
24812
|
+
for (const part of [...parts].reverse()) {
|
|
24813
|
+
if (totalChars + part.length > MAX_PARENT_CONTEXT_CHARS) break;
|
|
24814
|
+
budgeted.unshift(part);
|
|
24815
|
+
totalChars += part.length;
|
|
24816
|
+
}
|
|
24817
|
+
if (!budgeted.length) return void 0;
|
|
24797
24818
|
return [
|
|
24798
24819
|
`# Parent Conversation Context`,
|
|
24799
24820
|
"The following context was inherited from the parent Pi session. Treat it as reference-only.",
|
|
24800
24821
|
"",
|
|
24801
|
-
|
|
24822
|
+
budgeted.join("\n\n")
|
|
24802
24823
|
].join("\n");
|
|
24803
24824
|
}
|
|
24804
24825
|
function configRecord(config) {
|
|
24805
24826
|
if (!config || typeof config !== "object" || Array.isArray(config)) return {};
|
|
24806
24827
|
return config;
|
|
24807
24828
|
}
|
|
24829
|
+
var MAX_PARENT_CONTEXT_CHARS, MAX_ASSISTANT_MSG_CHARS, TRUNCATED_ASSISTANT_CHARS, NOISY_THRESHOLD, NOISY_PREFIXES;
|
|
24808
24830
|
var init_context = __esm({
|
|
24809
24831
|
"src/extension/team-tool/context.ts"() {
|
|
24810
24832
|
"use strict";
|
|
24811
24833
|
init_tool_result();
|
|
24834
|
+
MAX_PARENT_CONTEXT_CHARS = 12e3;
|
|
24835
|
+
MAX_ASSISTANT_MSG_CHARS = 500;
|
|
24836
|
+
TRUNCATED_ASSISTANT_CHARS = 200;
|
|
24837
|
+
NOISY_THRESHOLD = 1e3;
|
|
24838
|
+
NOISY_PREFIXES = ["```", "total ", "drwx", "-rw", "import ", "export "];
|
|
24812
24839
|
}
|
|
24813
24840
|
});
|
|
24814
24841
|
|
|
@@ -26191,7 +26218,7 @@ var init_handle_settings = __esm({
|
|
|
26191
26218
|
"runtime.mode": "auto",
|
|
26192
26219
|
"runtime.maxTurns": 1e4,
|
|
26193
26220
|
"runtime.graceTurns": 5,
|
|
26194
|
-
"runtime.inheritContext":
|
|
26221
|
+
"runtime.inheritContext": true,
|
|
26195
26222
|
"runtime.promptMode": "replace",
|
|
26196
26223
|
"runtime.completionMutationGuard": "warn",
|
|
26197
26224
|
"runtime.isolationPolicy": void 0,
|
|
@@ -48581,7 +48608,7 @@ var init_async_runner = __esm({
|
|
|
48581
48608
|
// Phase 1.5: worker-thread atomic writer opt-in (RFC 15).
|
|
48582
48609
|
"PI_CREW_WORKER_ATOMIC_WRITER",
|
|
48583
48610
|
"PI_TEAMS_WORKER_ATOMIC_WRITER",
|
|
48584
|
-
// Phase 1.5 #1: verification env sanitization opt-
|
|
48611
|
+
// Phase 1.5 #1: verification env sanitization opt-out (RFC 13 §6).
|
|
48585
48612
|
"PI_CREW_VERIFICATION_SANITIZE_ENV",
|
|
48586
48613
|
"PI_TEAMS_VERIFICATION_SANITIZE_ENV",
|
|
48587
48614
|
"PI_CREW_VERIFICATION_PRESERVE_ENV",
|
|
@@ -58026,7 +58053,10 @@ import { spawn as spawn4 } from "node:child_process";
|
|
|
58026
58053
|
import * as fs82 from "node:fs";
|
|
58027
58054
|
import * as path69 from "node:path";
|
|
58028
58055
|
function isVerificationEnvSanitizeEnabled() {
|
|
58029
|
-
|
|
58056
|
+
if (process.env.PI_CREW_VERIFICATION_SANITIZE_ENV === "0" || process.env.PI_TEAMS_VERIFICATION_SANITIZE_ENV === "0") {
|
|
58057
|
+
return false;
|
|
58058
|
+
}
|
|
58059
|
+
return true;
|
|
58030
58060
|
}
|
|
58031
58061
|
function buildVerificationEnv() {
|
|
58032
58062
|
if (!isVerificationEnvSanitizeEnabled()) {
|
|
@@ -75791,7 +75821,7 @@ var init_settings_overlay = __esm({
|
|
|
75791
75821
|
"runtime.mode": "auto",
|
|
75792
75822
|
"runtime.maxTurns": 1e4,
|
|
75793
75823
|
"runtime.graceTurns": 5,
|
|
75794
|
-
"runtime.inheritContext":
|
|
75824
|
+
"runtime.inheritContext": true,
|
|
75795
75825
|
"runtime.promptMode": "replace",
|
|
75796
75826
|
"runtime.completionMutationGuard": "warn",
|
|
75797
75827
|
"runtime.isolationPolicy": void 0,
|
|
@@ -84088,6 +84118,259 @@ function initI18n(pi) {
|
|
|
84088
84118
|
|
|
84089
84119
|
// src/extension/registration/subagent-tools.ts
|
|
84090
84120
|
init_crew_agent_records();
|
|
84121
|
+
|
|
84122
|
+
// src/observability/event-bus.ts
|
|
84123
|
+
init_internal_error();
|
|
84124
|
+
var EventBus = class _EventBus {
|
|
84125
|
+
listeners = /* @__PURE__ */ new Map();
|
|
84126
|
+
static _instance;
|
|
84127
|
+
static getInstance() {
|
|
84128
|
+
if (!_EventBus._instance) {
|
|
84129
|
+
_EventBus._instance = new _EventBus();
|
|
84130
|
+
}
|
|
84131
|
+
return _EventBus._instance;
|
|
84132
|
+
}
|
|
84133
|
+
/**
|
|
84134
|
+
* Dispose of the EventBus instance and clear all listeners.
|
|
84135
|
+
* Resets the singleton so a new instance can be created.
|
|
84136
|
+
*/
|
|
84137
|
+
dispose() {
|
|
84138
|
+
this.listeners.clear();
|
|
84139
|
+
_EventBus._instance = void 0;
|
|
84140
|
+
}
|
|
84141
|
+
emit(event) {
|
|
84142
|
+
const listeners2 = this.listeners.get(event.type);
|
|
84143
|
+
if (listeners2) {
|
|
84144
|
+
for (const listener of listeners2) {
|
|
84145
|
+
try {
|
|
84146
|
+
listener(event);
|
|
84147
|
+
} catch (e) {
|
|
84148
|
+
logInternalError("event-bus.listener", e, `type=${event.type} runId=${event.runId}`);
|
|
84149
|
+
}
|
|
84150
|
+
}
|
|
84151
|
+
}
|
|
84152
|
+
}
|
|
84153
|
+
on(type, listener) {
|
|
84154
|
+
if (!this.listeners.has(type)) {
|
|
84155
|
+
this.listeners.set(type, /* @__PURE__ */ new Set());
|
|
84156
|
+
}
|
|
84157
|
+
this.listeners.get(type).add(listener);
|
|
84158
|
+
return () => {
|
|
84159
|
+
this.listeners.get(type)?.delete(listener);
|
|
84160
|
+
};
|
|
84161
|
+
}
|
|
84162
|
+
off(type, listener) {
|
|
84163
|
+
this.listeners.get(type)?.delete(listener);
|
|
84164
|
+
}
|
|
84165
|
+
};
|
|
84166
|
+
var crewEventBus = EventBus.getInstance();
|
|
84167
|
+
|
|
84168
|
+
// src/runtime/progress-tracker.ts
|
|
84169
|
+
var ProgressTracker = class _ProgressTracker {
|
|
84170
|
+
sessions = /* @__PURE__ */ new Map();
|
|
84171
|
+
track(session, agentId, runId) {
|
|
84172
|
+
if (this.sessions.has(agentId)) {
|
|
84173
|
+
return this.sessions.get(agentId).progress;
|
|
84174
|
+
}
|
|
84175
|
+
const progress = {
|
|
84176
|
+
toolCalls: 0,
|
|
84177
|
+
currentTool: null,
|
|
84178
|
+
toolStartTime: null,
|
|
84179
|
+
errors: [],
|
|
84180
|
+
turns: 0,
|
|
84181
|
+
tokens: { input: 0, output: 0 },
|
|
84182
|
+
status: "running"
|
|
84183
|
+
};
|
|
84184
|
+
const unsubscribe = session.subscribe((event) => {
|
|
84185
|
+
this.handleEvent(event, progress, agentId, runId);
|
|
84186
|
+
});
|
|
84187
|
+
this.sessions.set(agentId, { unsubscribe, progress });
|
|
84188
|
+
return progress;
|
|
84189
|
+
}
|
|
84190
|
+
handleEvent(event, progress, agentId, runId) {
|
|
84191
|
+
switch (event.type) {
|
|
84192
|
+
case "tool_execution_start":
|
|
84193
|
+
progress.toolCalls++;
|
|
84194
|
+
progress.currentTool = event.toolName;
|
|
84195
|
+
progress.toolStartTime = Date.now();
|
|
84196
|
+
crewEventBus.emit({
|
|
84197
|
+
type: "agent:progress",
|
|
84198
|
+
runId,
|
|
84199
|
+
agentId,
|
|
84200
|
+
payload: { ...progress },
|
|
84201
|
+
timestamp: Date.now()
|
|
84202
|
+
});
|
|
84203
|
+
break;
|
|
84204
|
+
case "tool_execution_end":
|
|
84205
|
+
progress.currentTool = null;
|
|
84206
|
+
progress.toolStartTime = null;
|
|
84207
|
+
if (event.isError) {
|
|
84208
|
+
progress.errors.push(String(event.result ?? "Unknown error"));
|
|
84209
|
+
crewEventBus.emit({
|
|
84210
|
+
type: "agent:error",
|
|
84211
|
+
runId,
|
|
84212
|
+
agentId,
|
|
84213
|
+
payload: String(event.result ?? "Unknown error"),
|
|
84214
|
+
timestamp: Date.now()
|
|
84215
|
+
});
|
|
84216
|
+
}
|
|
84217
|
+
crewEventBus.emit({
|
|
84218
|
+
type: "agent:progress",
|
|
84219
|
+
runId,
|
|
84220
|
+
agentId,
|
|
84221
|
+
payload: { ...progress },
|
|
84222
|
+
timestamp: Date.now()
|
|
84223
|
+
});
|
|
84224
|
+
break;
|
|
84225
|
+
case "turn_start":
|
|
84226
|
+
progress.turns++;
|
|
84227
|
+
break;
|
|
84228
|
+
case "agent_end":
|
|
84229
|
+
progress.status = "completed";
|
|
84230
|
+
crewEventBus.emit({
|
|
84231
|
+
type: "agent:complete",
|
|
84232
|
+
runId,
|
|
84233
|
+
agentId,
|
|
84234
|
+
payload: { ...progress },
|
|
84235
|
+
timestamp: Date.now()
|
|
84236
|
+
});
|
|
84237
|
+
break;
|
|
84238
|
+
case "agent_start":
|
|
84239
|
+
progress.status = "running";
|
|
84240
|
+
break;
|
|
84241
|
+
}
|
|
84242
|
+
}
|
|
84243
|
+
untrack(agentId) {
|
|
84244
|
+
const tracked = this.sessions.get(agentId);
|
|
84245
|
+
if (tracked) {
|
|
84246
|
+
tracked.unsubscribe();
|
|
84247
|
+
this.sessions.delete(agentId);
|
|
84248
|
+
}
|
|
84249
|
+
}
|
|
84250
|
+
getProgress(agentId) {
|
|
84251
|
+
return this.sessions.get(agentId)?.progress;
|
|
84252
|
+
}
|
|
84253
|
+
// ── Child-process worker event bridge ──────────────────────────────────
|
|
84254
|
+
//
|
|
84255
|
+
// For child-process runtime, events arrive as raw JSON from the child pi
|
|
84256
|
+
// process's stdout (via onJsonEvent). These methods bridge those events into
|
|
84257
|
+
// the same crewEventBus stream that live-session uses, so the widget shows
|
|
84258
|
+
// real-time tool calls and assistant text for BOTH runtimes.
|
|
84259
|
+
workerProgress = /* @__PURE__ */ new Map();
|
|
84260
|
+
/** Throttle: don't emit more than 1 progress event per 500ms per worker. */
|
|
84261
|
+
lastEmitTs = /* @__PURE__ */ new Map();
|
|
84262
|
+
static EMIT_THROTTLE_MS = 500;
|
|
84263
|
+
/**
|
|
84264
|
+
* Handle a raw child-process JSON event (from onJsonEvent callback).
|
|
84265
|
+
* Processes tool_execution_start/end, agent_start/end, and assistant text.
|
|
84266
|
+
*/
|
|
84267
|
+
handleWorkerEvent(taskId, runId, event) {
|
|
84268
|
+
let progress = this.workerProgress.get(taskId);
|
|
84269
|
+
if (!progress) {
|
|
84270
|
+
progress = {
|
|
84271
|
+
toolCalls: 0,
|
|
84272
|
+
currentTool: null,
|
|
84273
|
+
toolStartTime: null,
|
|
84274
|
+
errors: [],
|
|
84275
|
+
turns: 0,
|
|
84276
|
+
tokens: { input: 0, output: 0 },
|
|
84277
|
+
status: "running"
|
|
84278
|
+
};
|
|
84279
|
+
this.workerProgress.set(taskId, progress);
|
|
84280
|
+
}
|
|
84281
|
+
const eventType = typeof event.type === "string" ? event.type : void 0;
|
|
84282
|
+
switch (eventType) {
|
|
84283
|
+
case "tool_execution_start": {
|
|
84284
|
+
progress.toolCalls++;
|
|
84285
|
+
progress.currentTool = typeof event.toolName === "string" ? event.toolName : "unknown";
|
|
84286
|
+
progress.toolStartTime = Date.now();
|
|
84287
|
+
this.emitThrottled(taskId, runId, progress);
|
|
84288
|
+
break;
|
|
84289
|
+
}
|
|
84290
|
+
case "tool_execution_end": {
|
|
84291
|
+
progress.currentTool = null;
|
|
84292
|
+
progress.toolStartTime = null;
|
|
84293
|
+
if (event.isError) {
|
|
84294
|
+
progress.errors.push(String(event.result ?? "Unknown error"));
|
|
84295
|
+
crewEventBus.emit({
|
|
84296
|
+
type: "agent:error",
|
|
84297
|
+
runId,
|
|
84298
|
+
agentId: taskId,
|
|
84299
|
+
payload: String(event.result ?? "Unknown error"),
|
|
84300
|
+
timestamp: Date.now()
|
|
84301
|
+
});
|
|
84302
|
+
}
|
|
84303
|
+
this.emitThrottled(taskId, runId, progress);
|
|
84304
|
+
break;
|
|
84305
|
+
}
|
|
84306
|
+
case "turn_start":
|
|
84307
|
+
case "turn_end":
|
|
84308
|
+
progress.turns++;
|
|
84309
|
+
break;
|
|
84310
|
+
case "agent_start":
|
|
84311
|
+
progress.status = "running";
|
|
84312
|
+
break;
|
|
84313
|
+
case "agent_end":
|
|
84314
|
+
case "agent_settled":
|
|
84315
|
+
progress.status = "completed";
|
|
84316
|
+
this.emitThrottled(taskId, runId, progress);
|
|
84317
|
+
break;
|
|
84318
|
+
case "message":
|
|
84319
|
+
case "message_end": {
|
|
84320
|
+
const message = event.message;
|
|
84321
|
+
if (message?.role === "assistant") {
|
|
84322
|
+
const text = extractWorkerText(message.content);
|
|
84323
|
+
if (text) {
|
|
84324
|
+
progress.partialText = text.slice(-2e3);
|
|
84325
|
+
this.emitThrottled(taskId, runId, progress);
|
|
84326
|
+
}
|
|
84327
|
+
}
|
|
84328
|
+
if (eventType === "message_end" && event.usage && typeof event.usage === "object") {
|
|
84329
|
+
const usage = event.usage;
|
|
84330
|
+
if (typeof usage.input === "number") progress.tokens.input += usage.input;
|
|
84331
|
+
if (typeof usage.output === "number") progress.tokens.output += usage.output;
|
|
84332
|
+
}
|
|
84333
|
+
break;
|
|
84334
|
+
}
|
|
84335
|
+
}
|
|
84336
|
+
}
|
|
84337
|
+
/** Get the accumulated progress for a child-process worker task. */
|
|
84338
|
+
getWorkerProgress(taskId) {
|
|
84339
|
+
return this.workerProgress.get(taskId);
|
|
84340
|
+
}
|
|
84341
|
+
/** Remove a worker from tracking after completion. */
|
|
84342
|
+
untrackWorker(taskId) {
|
|
84343
|
+
this.workerProgress.delete(taskId);
|
|
84344
|
+
this.lastEmitTs.delete(taskId);
|
|
84345
|
+
}
|
|
84346
|
+
/**
|
|
84347
|
+
* Emit a progress event to crewEventBus, throttled to avoid flooding
|
|
84348
|
+
* the widget with re-renders (max 1 event per EMIT_THROTTLE_MS per worker).
|
|
84349
|
+
*/
|
|
84350
|
+
emitThrottled(taskId, runId, progress) {
|
|
84351
|
+
const now = Date.now();
|
|
84352
|
+
const last = this.lastEmitTs.get(taskId) ?? 0;
|
|
84353
|
+
if (now - last < _ProgressTracker.EMIT_THROTTLE_MS) return;
|
|
84354
|
+
this.lastEmitTs.set(taskId, now);
|
|
84355
|
+
crewEventBus.emit({
|
|
84356
|
+
type: "agent:progress",
|
|
84357
|
+
runId,
|
|
84358
|
+
agentId: taskId,
|
|
84359
|
+
payload: { ...progress },
|
|
84360
|
+
timestamp: now
|
|
84361
|
+
});
|
|
84362
|
+
}
|
|
84363
|
+
};
|
|
84364
|
+
var globalProgressTracker = new ProgressTracker();
|
|
84365
|
+
function extractWorkerText(content) {
|
|
84366
|
+
if (typeof content === "string") return content;
|
|
84367
|
+
if (!Array.isArray(content)) return "";
|
|
84368
|
+
return content.map(
|
|
84369
|
+
(part) => part && typeof part === "object" && !Array.isArray(part) && typeof part.text === "string" ? part.text : ""
|
|
84370
|
+
).filter(Boolean).join("\n");
|
|
84371
|
+
}
|
|
84372
|
+
|
|
84373
|
+
// src/extension/registration/subagent-tools.ts
|
|
84091
84374
|
init_role_permission();
|
|
84092
84375
|
init_state_store();
|
|
84093
84376
|
init_manager();
|
|
@@ -84455,7 +84738,20 @@ function startAgentToolProgress(cwd, agentRecordId, onUpdate, manager) {
|
|
|
84455
84738
|
agents,
|
|
84456
84739
|
error: record.error
|
|
84457
84740
|
});
|
|
84458
|
-
|
|
84741
|
+
let progressLine = text;
|
|
84742
|
+
if (tasks && tasks.length > 0) {
|
|
84743
|
+
const wp = globalProgressTracker.getWorkerProgress(tasks[0].id);
|
|
84744
|
+
if (wp) {
|
|
84745
|
+
const toolLine = wp.currentTool ? `
|
|
84746
|
+
\u26A1 tool: ${wp.currentTool}` : "";
|
|
84747
|
+
const tokenLine = wp.tokens.input + wp.tokens.output > 0 ? `
|
|
84748
|
+
\u{1F4CA} tokens: ${(wp.tokens.input / 1e3).toFixed(1)}K in, ${(wp.tokens.output / 1e3).toFixed(1)}K out` : "";
|
|
84749
|
+
const textLine = wp.partialText ? `
|
|
84750
|
+
\u{1F4AC} ${wp.partialText.slice(-150).replace(/\n/g, " ")}` : "";
|
|
84751
|
+
progressLine = `${text}${toolLine}${tokenLine}${textLine}`;
|
|
84752
|
+
}
|
|
84753
|
+
}
|
|
84754
|
+
onUpdate({ content: [{ type: "text", text: progressLine }] });
|
|
84459
84755
|
} catch (error) {
|
|
84460
84756
|
logInternalError("subagent-tools.progress", error, `agentId=${agentRecordId}`);
|
|
84461
84757
|
}
|
|
@@ -85587,6 +85883,9 @@ Subagent may need manual intervention.`
|
|
|
85587
85883
|
const record = event;
|
|
85588
85884
|
const eventType = typeof record.type === "string" ? record.type : void 0;
|
|
85589
85885
|
if (eventType) lifecycleState.overflowTracker?.feedEvent(taskId, runId, eventType);
|
|
85886
|
+
if (record && typeof record === "object") {
|
|
85887
|
+
globalProgressTracker.handleWorkerEvent(taskId, runId, record);
|
|
85888
|
+
}
|
|
85590
85889
|
}
|
|
85591
85890
|
});
|
|
85592
85891
|
registerSubagentTools(pi, subagentManager, {
|