pi-crew 0.9.61 → 0.9.64
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 +99 -0
- package/README.md +1 -0
- package/agents/critic.md +1 -1
- package/agents/explorer.md +1 -1
- package/agents/planner.md +1 -1
- package/agents/reviewer.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/test-engineer.md +1 -1
- package/agents/writer.md +1 -1
- package/dist/index.mjs +256 -81
- package/package.json +5 -2
- package/src/agents/agent-config.ts +4 -0
- package/src/agents/agent-serializer.ts +1 -0
- package/src/agents/discover-agents.ts +8 -0
- package/src/config/role-tools.ts +49 -1
- package/src/extension/notification-router.ts +25 -0
- package/src/extension/registration/lifecycle-handlers.ts +47 -6
- package/src/extension/registration/lifecycle.ts +13 -7
- package/src/prompt/prompt-runtime.ts +6 -0
- package/src/prompt/scratchpad-lifecycle.ts +605 -0
- package/src/runtime/child-pi/child-pi-spawn.ts +42 -1
- package/src/runtime/child-pi/child-pi.ts +5 -0
- package/src/runtime/live-session/live-session-runtime.ts +12 -1
- package/src/runtime/model/pi-args.ts +1 -1
- package/src/runtime/model/session-model.ts +76 -1
- package/src/runtime/recovery/crash-recovery.ts +1 -1
- package/src/runtime/scratchpad/README.md +184 -0
- package/src/runtime/scratchpad/engine.ts +610 -0
- package/src/runtime/scratchpad/guest.ts +360 -0
- package/src/runtime/scratchpad/index.ts +22 -0
- package/src/runtime/scratchpad/protocol.ts +88 -0
- package/src/runtime/scratchpad/snapshot-lookup.ts +74 -0
- package/src/runtime/scratchpad/transform.ts +363 -0
- package/src/runtime/task-runner/child-executor.ts +48 -31
package/dist/index.mjs
CHANGED
|
@@ -11602,15 +11602,51 @@ var init_frontmatter = __esm({
|
|
|
11602
11602
|
}
|
|
11603
11603
|
});
|
|
11604
11604
|
|
|
11605
|
+
// src/runtime/role-permission.ts
|
|
11606
|
+
function permissionForRole(role) {
|
|
11607
|
+
if (READ_ONLY_ROLES.has(role)) return "read_only";
|
|
11608
|
+
if (WRITE_ROLES.has(role)) return "workspace_write";
|
|
11609
|
+
return "read_only";
|
|
11610
|
+
}
|
|
11611
|
+
function currentCrewRole(env = process.env) {
|
|
11612
|
+
return env.PI_CREW_ROLE?.trim() || env.PI_TEAMS_ROLE?.trim() || void 0;
|
|
11613
|
+
}
|
|
11614
|
+
function checkSubagentSpawnPermission(role) {
|
|
11615
|
+
if (!role) return { allowed: true, mode: "workspace_write" };
|
|
11616
|
+
const mode = permissionForRole(role);
|
|
11617
|
+
if (mode === "read_only")
|
|
11618
|
+
return {
|
|
11619
|
+
allowed: false,
|
|
11620
|
+
mode,
|
|
11621
|
+
reason: `Role '${role}' is read-only and cannot spawn additional subagents.`
|
|
11622
|
+
};
|
|
11623
|
+
return { allowed: true, mode };
|
|
11624
|
+
}
|
|
11625
|
+
var READ_ONLY_ROLES, WRITE_ROLES;
|
|
11626
|
+
var init_role_permission = __esm({
|
|
11627
|
+
"src/runtime/role-permission.ts"() {
|
|
11628
|
+
"use strict";
|
|
11629
|
+
READ_ONLY_ROLES = /* @__PURE__ */ new Set(["explorer", "reviewer", "security-reviewer", "analyst", "critic", "planner"]);
|
|
11630
|
+
WRITE_ROLES = /* @__PURE__ */ new Set(["executor", "test-engineer", "writer", "verifier", "agent", "cold-verifier", "chain-executor", "worker"]);
|
|
11631
|
+
}
|
|
11632
|
+
});
|
|
11633
|
+
|
|
11605
11634
|
// src/config/role-tools.ts
|
|
11606
11635
|
function getToolConfig(role) {
|
|
11607
11636
|
const key = role.includes("_") ? role.replaceAll("_", "-") : role;
|
|
11608
11637
|
return ROLE_TOOL_CONFIGS[key] ?? ROLE_TOOL_CONFIGS[role] ?? {};
|
|
11609
11638
|
}
|
|
11639
|
+
function isScratchpadEnabledForRole(role, agent) {
|
|
11640
|
+
const normalized = role.includes("_") ? role.replaceAll("_", "-") : role;
|
|
11641
|
+
if (permissionForRole(normalized) === "read_only") return false;
|
|
11642
|
+
if (agent?.scratchpad === false) return false;
|
|
11643
|
+
return agent?.scratchpad === true || getToolConfig(normalized).scratchpad === true;
|
|
11644
|
+
}
|
|
11610
11645
|
var ROLE_TOOL_CONFIGS;
|
|
11611
11646
|
var init_role_tools = __esm({
|
|
11612
11647
|
"src/config/role-tools.ts"() {
|
|
11613
11648
|
"use strict";
|
|
11649
|
+
init_role_permission();
|
|
11614
11650
|
ROLE_TOOL_CONFIGS = {
|
|
11615
11651
|
// Explorer - Read-only exploration; bash is included for git log/show
|
|
11616
11652
|
// (decisions stream needs commit-history mining) but edit/write stay
|
|
@@ -11641,9 +11677,11 @@ var init_role_tools = __esm({
|
|
|
11641
11677
|
tools: ["read", "grep", "find", "ls", "glob"],
|
|
11642
11678
|
excludeTools: ["edit", "write", "bash", "web"]
|
|
11643
11679
|
},
|
|
11644
|
-
// Executor - Full access (default)
|
|
11680
|
+
// Executor - Full access (default). Phase 1 scratchpad-enabled (stateful
|
|
11681
|
+
// evaluator compounds intermediate results across execute calls).
|
|
11645
11682
|
executor: {
|
|
11646
11683
|
// No restrictions - full tool access
|
|
11684
|
+
scratchpad: true
|
|
11647
11685
|
},
|
|
11648
11686
|
// Reviewer - Read and review, no write
|
|
11649
11687
|
reviewer: {
|
|
@@ -11670,12 +11708,16 @@ var init_role_tools = __esm({
|
|
|
11670
11708
|
// integrity is preserved during verification. Mirrors cold-verifier behavior.
|
|
11671
11709
|
verifier: {
|
|
11672
11710
|
tools: ["read", "grep", "find", "ls", "bash"],
|
|
11673
|
-
excludeTools: ["edit", "write", "web"]
|
|
11711
|
+
excludeTools: ["edit", "write", "web"],
|
|
11712
|
+
// Phase 1 scratchpad: multi-cell test/verify flows reuse parsed state.
|
|
11713
|
+
scratchpad: true
|
|
11674
11714
|
},
|
|
11675
11715
|
// Test Engineer - Can write tests (F1: hyphenated key)
|
|
11676
11716
|
"test-engineer": {
|
|
11677
11717
|
tools: ["read", "edit", "write", "bash", "ls"],
|
|
11678
|
-
excludeTools: ["web"]
|
|
11718
|
+
excludeTools: ["web"],
|
|
11719
|
+
// Phase 1 scratchpad: build/run test suites with state across cells.
|
|
11720
|
+
scratchpad: true
|
|
11679
11721
|
}
|
|
11680
11722
|
};
|
|
11681
11723
|
}
|
|
@@ -11891,6 +11933,14 @@ function parseAgentFile(filePath, source) {
|
|
|
11891
11933
|
fallbackModels: parseCsv(frontmatter.fallbackModels),
|
|
11892
11934
|
thinking: frontmatter.thinking === "false" ? void 0 : frontmatter.thinking || void 0,
|
|
11893
11935
|
tools: parseToolsField(frontmatter.tools),
|
|
11936
|
+
// Phase 1 scratchpad opt-in (Q3: pi ignores unknown frontmatter keys; pi-crew
|
|
11937
|
+
// is the sole consumer in the worker path — task arrives via -p, agent file
|
|
11938
|
+
// is not re-read by pi). 3-STATE parse (NOT `=== "true"` like
|
|
11939
|
+
// inheritProjectContext): omitted/malformed → undefined so the F6 kill-switch
|
|
11940
|
+
// (`agent.scratchpad === false`) only fires on an EXPLICIT `scratchpad: false`,
|
|
11941
|
+
// not on every agent without the key (which would wrongly kill role
|
|
11942
|
+
// default-on). Only the literal "true"/"false" are honored.
|
|
11943
|
+
scratchpad: frontmatter.scratchpad === "true" ? true : frontmatter.scratchpad === "false" ? false : void 0,
|
|
11894
11944
|
// SEC-1: Strip extensions/excludeExtensions for untrusted project-sourced
|
|
11895
11945
|
// agents (RCE prevention). Both `project` (.crew/agents/) and
|
|
11896
11946
|
// `project-pi` (.pi/agents/) are repo-adjacent / untrusted sources —
|
|
@@ -14860,6 +14910,55 @@ var init_env_filter = __esm({
|
|
|
14860
14910
|
}
|
|
14861
14911
|
});
|
|
14862
14912
|
|
|
14913
|
+
// src/runtime/scratchpad/snapshot-lookup.ts
|
|
14914
|
+
import { lstatSync as lstatSync5, readdirSync as readdirSync8 } from "node:fs";
|
|
14915
|
+
import { join as join18 } from "node:path";
|
|
14916
|
+
function findLatestScratchpadSnapshot(artifactsRoot, agentId) {
|
|
14917
|
+
const scratchpadDir = join18(artifactsRoot, "scratchpad");
|
|
14918
|
+
let dirStat;
|
|
14919
|
+
try {
|
|
14920
|
+
dirStat = lstatSync5(scratchpadDir);
|
|
14921
|
+
} catch {
|
|
14922
|
+
return null;
|
|
14923
|
+
}
|
|
14924
|
+
if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) return null;
|
|
14925
|
+
let entries;
|
|
14926
|
+
try {
|
|
14927
|
+
entries = readdirSync8(scratchpadDir, { withFileTypes: true });
|
|
14928
|
+
} catch {
|
|
14929
|
+
return null;
|
|
14930
|
+
}
|
|
14931
|
+
const prefix = `${agentId}.attempt-`;
|
|
14932
|
+
let best = null;
|
|
14933
|
+
for (const dirent of entries) {
|
|
14934
|
+
if (dirent.isSymbolicLink() || !dirent.isFile()) continue;
|
|
14935
|
+
const name = dirent.name;
|
|
14936
|
+
if (!name.startsWith(prefix) || !name.endsWith(SNAPSHOT_SUFFIX)) continue;
|
|
14937
|
+
const attemptPart = name.slice(prefix.length, name.length - SNAPSHOT_SUFFIX.length);
|
|
14938
|
+
if (!/^\d+$/.test(attemptPart)) continue;
|
|
14939
|
+
const attempt = Number.parseInt(attemptPart, 10);
|
|
14940
|
+
let stat2;
|
|
14941
|
+
try {
|
|
14942
|
+
stat2 = lstatSync5(join18(scratchpadDir, name));
|
|
14943
|
+
} catch {
|
|
14944
|
+
continue;
|
|
14945
|
+
}
|
|
14946
|
+
if (!stat2.isFile()) continue;
|
|
14947
|
+
const hit = { path: join18(scratchpadDir, name), attempt, mtimeMs: stat2.mtimeMs };
|
|
14948
|
+
if (best === null || hit.mtimeMs > best.mtimeMs || hit.mtimeMs === best.mtimeMs && hit.attempt < best.attempt) {
|
|
14949
|
+
best = hit;
|
|
14950
|
+
}
|
|
14951
|
+
}
|
|
14952
|
+
return best;
|
|
14953
|
+
}
|
|
14954
|
+
var SNAPSHOT_SUFFIX;
|
|
14955
|
+
var init_snapshot_lookup = __esm({
|
|
14956
|
+
"src/runtime/scratchpad/snapshot-lookup.ts"() {
|
|
14957
|
+
"use strict";
|
|
14958
|
+
SNAPSHOT_SUFFIX = ".snapshot.json";
|
|
14959
|
+
}
|
|
14960
|
+
});
|
|
14961
|
+
|
|
14863
14962
|
// src/runtime/child-pi/child-pi-spawn.ts
|
|
14864
14963
|
import * as fs18 from "node:fs";
|
|
14865
14964
|
import * as path18 from "node:path";
|
|
@@ -14956,6 +15055,21 @@ function prepareSpawnContext(input, effectiveTask) {
|
|
|
14956
15055
|
if (input.runId) built.env.PI_CREW_BROKER_RUN_ID = input.runId;
|
|
14957
15056
|
if (input.agentId) built.env.PI_CREW_BROKER_TASK_ID = input.agentId;
|
|
14958
15057
|
}
|
|
15058
|
+
if (input.agentId && isScratchpadEnabledForRole(input.role ?? input.agent.name, input.agent)) {
|
|
15059
|
+
built.env.PI_CREW_SCRATCHPAD = "1";
|
|
15060
|
+
built.env.PI_CREW_TASK_ID = input.agentId;
|
|
15061
|
+
built.env.PI_CREW_ATTEMPT = String(input.attempt ?? 0);
|
|
15062
|
+
if (input.artifactsRoot) {
|
|
15063
|
+
built.env.PI_CREW_ARTIFACTS_ROOT = input.artifactsRoot;
|
|
15064
|
+
}
|
|
15065
|
+
const scratchTempDir = built.tempDir ?? createSafeTempDir(getPiTempBase(), "pi-crew-scratchpad-");
|
|
15066
|
+
built.env.PI_CREW_SCRATCHPAD_SNAPSHOT = resolveRealContainedPath(scratchTempDir, `${input.agentId}.snapshot.json`);
|
|
15067
|
+
const restoreHit = input.artifactsRoot ? findLatestScratchpadSnapshot(input.artifactsRoot, input.agentId) : null;
|
|
15068
|
+
if (restoreHit) {
|
|
15069
|
+
built.env.PI_CREW_SCRATCHPAD_RESTORE = restoreHit.path;
|
|
15070
|
+
built.env.PI_CREW_SCRATCHPAD_RESTORE_MTIME = String(restoreHit.mtimeMs);
|
|
15071
|
+
}
|
|
15072
|
+
}
|
|
14959
15073
|
if (input.signal?.aborted) {
|
|
14960
15074
|
return {
|
|
14961
15075
|
kind: "aborted",
|
|
@@ -14983,11 +15097,14 @@ var BASE_ALLOWLIST;
|
|
|
14983
15097
|
var init_child_pi_spawn = __esm({
|
|
14984
15098
|
"src/runtime/child-pi/child-pi-spawn.ts"() {
|
|
14985
15099
|
"use strict";
|
|
15100
|
+
init_role_tools();
|
|
14986
15101
|
init_env_allowlist();
|
|
14987
15102
|
init_env_filter();
|
|
14988
15103
|
init_internal_error();
|
|
15104
|
+
init_safe_paths();
|
|
14989
15105
|
init_pi_args();
|
|
14990
15106
|
init_pi_spawn();
|
|
15107
|
+
init_snapshot_lookup();
|
|
14991
15108
|
BASE_ALLOWLIST = [
|
|
14992
15109
|
"PATH",
|
|
14993
15110
|
"HOME",
|
|
@@ -22303,6 +22420,32 @@ var init_model_fallback = __esm({
|
|
|
22303
22420
|
});
|
|
22304
22421
|
|
|
22305
22422
|
// src/runtime/model/session-model.ts
|
|
22423
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
22424
|
+
function registerLiveAgentModel(agentId, model) {
|
|
22425
|
+
if (liveAgentModels.size >= MAX_LIVE_AGENT_MODELS && !liveAgentModels.has(agentId)) {
|
|
22426
|
+
const oldestKey = liveAgentModels.keys().next().value;
|
|
22427
|
+
if (oldestKey !== void 0) {
|
|
22428
|
+
logInternalError(
|
|
22429
|
+
"session-model.liveAgentModels.cap",
|
|
22430
|
+
new Error(`liveAgentModels at cap ${MAX_LIVE_AGENT_MODELS}; evicting oldest ${oldestKey}`)
|
|
22431
|
+
);
|
|
22432
|
+
liveAgentModels.delete(oldestKey);
|
|
22433
|
+
}
|
|
22434
|
+
}
|
|
22435
|
+
liveAgentModels.set(agentId, model);
|
|
22436
|
+
}
|
|
22437
|
+
function unregisterLiveAgentModel(agentId) {
|
|
22438
|
+
liveAgentModels.delete(agentId);
|
|
22439
|
+
}
|
|
22440
|
+
function hasActiveLiveAgents() {
|
|
22441
|
+
return liveAgentModels.size > 0;
|
|
22442
|
+
}
|
|
22443
|
+
function resolveProviderForResponse() {
|
|
22444
|
+
const ctx = liveAgentContext.getStore();
|
|
22445
|
+
if (ctx) return providerOfModelRef(ctx.modelRef);
|
|
22446
|
+
if (hasActiveLiveAgents()) return void 0;
|
|
22447
|
+
return providerOfModelRef(currentSessionModel());
|
|
22448
|
+
}
|
|
22306
22449
|
function noteSessionModel(model, source = "model_select") {
|
|
22307
22450
|
const normalized = modelRefToString(model);
|
|
22308
22451
|
if (!normalized) return;
|
|
@@ -22342,12 +22485,16 @@ function captureRunModelContext(ctx, override) {
|
|
|
22342
22485
|
function sessionModelSnapshot() {
|
|
22343
22486
|
return { ...state };
|
|
22344
22487
|
}
|
|
22345
|
-
var state;
|
|
22488
|
+
var state, liveAgentContext, liveAgentModels, MAX_LIVE_AGENT_MODELS;
|
|
22346
22489
|
var init_session_model = __esm({
|
|
22347
22490
|
"src/runtime/model/session-model.ts"() {
|
|
22348
22491
|
"use strict";
|
|
22492
|
+
init_internal_error();
|
|
22349
22493
|
init_model_fallback();
|
|
22350
22494
|
state = { source: "none" };
|
|
22495
|
+
liveAgentContext = new AsyncLocalStorage2();
|
|
22496
|
+
liveAgentModels = /* @__PURE__ */ new Map();
|
|
22497
|
+
MAX_LIVE_AGENT_MODELS = 5e3;
|
|
22351
22498
|
}
|
|
22352
22499
|
});
|
|
22353
22500
|
|
|
@@ -22975,12 +23122,12 @@ var init_crew_hooks = __esm({
|
|
|
22975
23122
|
|
|
22976
23123
|
// src/runtime/skill-effectiveness.ts
|
|
22977
23124
|
import { existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync4 } from "node:fs";
|
|
22978
|
-
import { dirname as dirname16, join as
|
|
23125
|
+
import { dirname as dirname16, join as join30 } from "node:path";
|
|
22979
23126
|
function getSkillMetricsPath(cwd, runId) {
|
|
22980
|
-
return
|
|
23127
|
+
return join30(projectCrewRoot(cwd), `state/runs/${runId}/skill-metrics.jsonl`);
|
|
22981
23128
|
}
|
|
22982
23129
|
function getSkillActivationsPath(cwd, runId) {
|
|
22983
|
-
return
|
|
23130
|
+
return join30(projectCrewRoot(cwd), `state/runs/${runId}/skill-activations.jsonl`);
|
|
22984
23131
|
}
|
|
22985
23132
|
function ensureSkillMetricsDir(cwd, runId) {
|
|
22986
23133
|
const dir = dirname16(getSkillMetricsPath(cwd, runId));
|
|
@@ -33237,6 +33384,7 @@ async function runLiveSessionTask(input) {
|
|
|
33237
33384
|
scopeModelsPatterns: await resolveScopeModelsPatterns(input.manifest.cwd)
|
|
33238
33385
|
});
|
|
33239
33386
|
const resolvedModel = modelFromRegistry(input.modelRegistry, modelRouting.candidates[0] ?? modelRouting.requested) ?? input.parentModel;
|
|
33387
|
+
const resolvedModelRef = modelRefToString(resolvedModel) ?? modelRouting.candidates[0];
|
|
33240
33388
|
if (modelRouting.droppedRequested) {
|
|
33241
33389
|
appendEventFireAndForget(input.manifest.eventsPath, {
|
|
33242
33390
|
type: "task.model_dropped",
|
|
@@ -33335,6 +33483,7 @@ async function runLiveSessionTask(input) {
|
|
|
33335
33483
|
appendEvent,
|
|
33336
33484
|
input.manifest.eventsPath
|
|
33337
33485
|
);
|
|
33486
|
+
registerLiveAgentModel(agentId, resolvedModelRef ?? "");
|
|
33338
33487
|
streamOut = createStreamingOutput(input.manifest, input.task.id);
|
|
33339
33488
|
let controlCursor = { offset: 0 };
|
|
33340
33489
|
const seenControlRequestIds = /* @__PURE__ */ new Set();
|
|
@@ -33470,7 +33619,10 @@ ${input.prompt}` : input.prompt;
|
|
|
33470
33619
|
});
|
|
33471
33620
|
const sessionTimeoutMs = DEFAULT_LIVE_SESSION.responseTimeoutMs;
|
|
33472
33621
|
try {
|
|
33473
|
-
await
|
|
33622
|
+
await liveAgentContext.run(
|
|
33623
|
+
{ agentId, modelRef: resolvedModelRef ?? "" },
|
|
33624
|
+
() => promptWithTimeout(session, effectivePrompt, sessionTimeoutMs, "Live-session")
|
|
33625
|
+
);
|
|
33474
33626
|
} catch (promptError) {
|
|
33475
33627
|
const msg = promptError instanceof Error ? promptError.message : String(promptError);
|
|
33476
33628
|
appendEventFireAndForget(input.manifest.eventsPath, {
|
|
@@ -33643,6 +33795,7 @@ ${input.prompt}` : input.prompt;
|
|
|
33643
33795
|
error: message
|
|
33644
33796
|
};
|
|
33645
33797
|
} finally {
|
|
33798
|
+
unregisterLiveAgentModel(agentId);
|
|
33646
33799
|
unsubscribe?.();
|
|
33647
33800
|
unsubscribeControlRealtime?.();
|
|
33648
33801
|
if (onSignalAbort) input.signal?.removeEventListener("abort", onSignalAbort);
|
|
@@ -33681,6 +33834,7 @@ var init_live_session_runtime = __esm({
|
|
|
33681
33834
|
init_model_scope();
|
|
33682
33835
|
init_runtime_resolver();
|
|
33683
33836
|
init_runtime_warmup();
|
|
33837
|
+
init_session_model();
|
|
33684
33838
|
init_sidechain_output();
|
|
33685
33839
|
init_streaming_output();
|
|
33686
33840
|
init_sensitive_paths();
|
|
@@ -33706,35 +33860,6 @@ var init_live_session_runtime = __esm({
|
|
|
33706
33860
|
}
|
|
33707
33861
|
});
|
|
33708
33862
|
|
|
33709
|
-
// src/runtime/role-permission.ts
|
|
33710
|
-
function permissionForRole(role) {
|
|
33711
|
-
if (READ_ONLY_ROLES.has(role)) return "read_only";
|
|
33712
|
-
if (WRITE_ROLES.has(role)) return "workspace_write";
|
|
33713
|
-
return "read_only";
|
|
33714
|
-
}
|
|
33715
|
-
function currentCrewRole(env = process.env) {
|
|
33716
|
-
return env.PI_CREW_ROLE?.trim() || env.PI_TEAMS_ROLE?.trim() || void 0;
|
|
33717
|
-
}
|
|
33718
|
-
function checkSubagentSpawnPermission(role) {
|
|
33719
|
-
if (!role) return { allowed: true, mode: "workspace_write" };
|
|
33720
|
-
const mode = permissionForRole(role);
|
|
33721
|
-
if (mode === "read_only")
|
|
33722
|
-
return {
|
|
33723
|
-
allowed: false,
|
|
33724
|
-
mode,
|
|
33725
|
-
reason: `Role '${role}' is read-only and cannot spawn additional subagents.`
|
|
33726
|
-
};
|
|
33727
|
-
return { allowed: true, mode };
|
|
33728
|
-
}
|
|
33729
|
-
var READ_ONLY_ROLES, WRITE_ROLES;
|
|
33730
|
-
var init_role_permission = __esm({
|
|
33731
|
-
"src/runtime/role-permission.ts"() {
|
|
33732
|
-
"use strict";
|
|
33733
|
-
READ_ONLY_ROLES = /* @__PURE__ */ new Set(["explorer", "reviewer", "security-reviewer", "analyst", "critic", "planner"]);
|
|
33734
|
-
WRITE_ROLES = /* @__PURE__ */ new Set(["executor", "test-engineer", "writer", "verifier", "agent", "cold-verifier", "chain-executor", "worker"]);
|
|
33735
|
-
}
|
|
33736
|
-
});
|
|
33737
|
-
|
|
33738
33863
|
// src/state/coordination/task-claims.ts
|
|
33739
33864
|
import { randomUUID as randomUUID4, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
33740
33865
|
function createTaskClaim(owner, leaseMs = 5 * 6e4, now = /* @__PURE__ */ new Date()) {
|
|
@@ -39389,6 +39514,7 @@ function serializeAgent(agent) {
|
|
|
39389
39514
|
line("fallbackModels", agent.fallbackModels),
|
|
39390
39515
|
line("thinking", agent.thinking),
|
|
39391
39516
|
line("tools", agent.tools),
|
|
39517
|
+
line("scratchpad", agent.scratchpad),
|
|
39392
39518
|
agent.extensions !== void 0 ? line("extensions", agent.extensions) ?? "extensions:" : void 0,
|
|
39393
39519
|
line("skills", agent.skills),
|
|
39394
39520
|
line("systemPromptMode", agent.systemPromptMode),
|
|
@@ -40735,9 +40861,9 @@ var init_handle_settings = __esm({
|
|
|
40735
40861
|
|
|
40736
40862
|
// src/extension/team-tool/workflow-manage.ts
|
|
40737
40863
|
import { existsSync as existsSync41, readFileSync as readFileSync40, rmSync as rmSync14, writeFileSync as writeFileSync7 } from "node:fs";
|
|
40738
|
-
import { dirname as dirname29, join as
|
|
40864
|
+
import { dirname as dirname29, join as join46 } from "node:path";
|
|
40739
40865
|
function allowedWorkflowDirs(cwd) {
|
|
40740
|
-
return [
|
|
40866
|
+
return [join46(projectCrewRoot(cwd), "workflows"), join46(userPiRoot(), "workflows"), join46(packageRoot(), "workflows")];
|
|
40741
40867
|
}
|
|
40742
40868
|
function validateScriptContent(content) {
|
|
40743
40869
|
for (const pattern of FORBIDDEN_PATTERNS) {
|
|
@@ -40749,7 +40875,7 @@ function validateScriptContent(content) {
|
|
|
40749
40875
|
}
|
|
40750
40876
|
function resolveWorkflowWritePath(cwd, name, scope = "project") {
|
|
40751
40877
|
assertSafePathId("workflowName", name);
|
|
40752
|
-
const base = scope === "user" ?
|
|
40878
|
+
const base = scope === "user" ? join46(userPiRoot(), "workflows") : join46(projectCrewRoot(cwd), "workflows");
|
|
40753
40879
|
return resolveRealContainedPath(base, `${name}.dwf.ts`);
|
|
40754
40880
|
}
|
|
40755
40881
|
function handleWorkflowCreate(params, ctx) {
|
|
@@ -41603,7 +41729,7 @@ var init_async_runner = __esm({
|
|
|
41603
41729
|
});
|
|
41604
41730
|
|
|
41605
41731
|
// src/runtime/goal-workflow/goal-state-store.ts
|
|
41606
|
-
import { closeSync as closeSync10, existsSync as existsSync44, mkdirSync as mkdirSync26, openSync as openSync10, readdirSync as
|
|
41732
|
+
import { closeSync as closeSync10, existsSync as existsSync44, mkdirSync as mkdirSync26, openSync as openSync10, readdirSync as readdirSync21, readFileSync as readFileSync42, statSync as statSync33, unlinkSync as unlinkSync7 } from "node:fs";
|
|
41607
41733
|
import { dirname as dirname32 } from "node:path";
|
|
41608
41734
|
function resolveGoalsRoot(cwd) {
|
|
41609
41735
|
const crewRoot = projectCrewRoot(cwd) ?? userCrewRoot();
|
|
@@ -41763,7 +41889,7 @@ var init_goal_state_store = __esm({
|
|
|
41763
41889
|
try {
|
|
41764
41890
|
const root = resolveGoalsRoot(this.cwd);
|
|
41765
41891
|
if (!existsSync44(root)) return [];
|
|
41766
|
-
const entries =
|
|
41892
|
+
const entries = readdirSync21(root);
|
|
41767
41893
|
const goals = [];
|
|
41768
41894
|
for (const entry of entries) {
|
|
41769
41895
|
if (!entry.endsWith(".json")) continue;
|
|
@@ -41827,7 +41953,7 @@ var init_verification_integrity = __esm({
|
|
|
41827
41953
|
|
|
41828
41954
|
// src/runtime/workspace-lock.ts
|
|
41829
41955
|
import { createHash as createHash7 } from "node:crypto";
|
|
41830
|
-
import { closeSync as closeSync11, existsSync as existsSync45, mkdirSync as mkdirSync27, openSync as openSync11, readdirSync as
|
|
41956
|
+
import { closeSync as closeSync11, existsSync as existsSync45, mkdirSync as mkdirSync27, openSync as openSync11, readdirSync as readdirSync22, readFileSync as readFileSync44, statSync as statSync35, unlinkSync as unlinkSync8, writeFileSync as writeFileSync8 } from "node:fs";
|
|
41831
41957
|
import * as path50 from "node:path";
|
|
41832
41958
|
function workspaceLockPath(cwd) {
|
|
41833
41959
|
const absCwd = path50.resolve(cwd);
|
|
@@ -45032,7 +45158,8 @@ __export(crash_recovery_exports, {
|
|
|
45032
45158
|
detectInterruptedRuns: () => detectInterruptedRuns,
|
|
45033
45159
|
purgeStaleActiveRunIndex: () => purgeStaleActiveRunIndex,
|
|
45034
45160
|
readManifestWithTransientRetry: () => readManifestWithTransientRetry,
|
|
45035
|
-
reconcileAllStaleRuns: () => reconcileAllStaleRuns
|
|
45161
|
+
reconcileAllStaleRuns: () => reconcileAllStaleRuns,
|
|
45162
|
+
shouldRecoverTask: () => shouldRecoverTask
|
|
45036
45163
|
});
|
|
45037
45164
|
import * as fs63 from "node:fs";
|
|
45038
45165
|
import * as path52 from "node:path";
|
|
@@ -49170,7 +49297,7 @@ var init_dispatch = __esm({
|
|
|
49170
49297
|
});
|
|
49171
49298
|
|
|
49172
49299
|
// src/observability/correlation.ts
|
|
49173
|
-
import { AsyncLocalStorage as
|
|
49300
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
|
|
49174
49301
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
49175
49302
|
function withCorrelation(ctx, fn) {
|
|
49176
49303
|
return storage.run(ctx, fn);
|
|
@@ -49194,7 +49321,7 @@ var storage;
|
|
|
49194
49321
|
var init_correlation = __esm({
|
|
49195
49322
|
"src/observability/correlation.ts"() {
|
|
49196
49323
|
"use strict";
|
|
49197
|
-
storage = new
|
|
49324
|
+
storage = new AsyncLocalStorage3();
|
|
49198
49325
|
}
|
|
49199
49326
|
});
|
|
49200
49327
|
|
|
@@ -53151,6 +53278,20 @@ function detectRetryableModelFailureFromOutput(parsed) {
|
|
|
53151
53278
|
}
|
|
53152
53279
|
return void 0;
|
|
53153
53280
|
}
|
|
53281
|
+
function evidenceStatusFor(childResult) {
|
|
53282
|
+
return childResult.exitStatus?.cancelled ? "cancelled" : childResult.error || childResult.exitCode && childResult.exitCode !== 0 ? "failed" : "completed";
|
|
53283
|
+
}
|
|
53284
|
+
function attemptErrorFor(childResult, parsedOutput, taskId) {
|
|
53285
|
+
let err2 = childResult.error || (childResult.exitCode && childResult.exitCode !== 0 ? childResult.stderr || `Child Pi exited with ${childResult.exitCode}` : void 0);
|
|
53286
|
+
if (childResult.exitStatus?.timedOut) {
|
|
53287
|
+
err2 = errors.childTimeout({ taskId, stderr: childResult.stderr }).message;
|
|
53288
|
+
}
|
|
53289
|
+
if (!err2 && parsedOutput) {
|
|
53290
|
+
const rateLimitErr = detectRetryableModelFailureFromOutput(parsedOutput);
|
|
53291
|
+
if (rateLimitErr) err2 = rateLimitErr;
|
|
53292
|
+
}
|
|
53293
|
+
return err2;
|
|
53294
|
+
}
|
|
53154
53295
|
async function runChildProcessTask(ctx) {
|
|
53155
53296
|
const input = ctx.input;
|
|
53156
53297
|
const manifest = ctx.manifest;
|
|
@@ -53334,6 +53475,7 @@ async function runChildProcessTask(ctx) {
|
|
|
53334
53475
|
runId: manifest.runId,
|
|
53335
53476
|
agentId: task.id,
|
|
53336
53477
|
artifactsRoot: manifest.artifactsRoot,
|
|
53478
|
+
attempt: i,
|
|
53337
53479
|
steeringFile: resolveRealContainedPath(`${manifest.artifactsRoot}/steering`, `${task.id}.jsonl`),
|
|
53338
53480
|
onSpawn: (pid) => {
|
|
53339
53481
|
try {
|
|
@@ -53422,7 +53564,7 @@ async function runChildProcessTask(ctx) {
|
|
|
53422
53564
|
input.signal.removeEventListener("abort", externalAbortListener);
|
|
53423
53565
|
}
|
|
53424
53566
|
}
|
|
53425
|
-
const evidenceStatus = childResult
|
|
53567
|
+
const evidenceStatus = evidenceStatusFor(childResult);
|
|
53426
53568
|
terminalEvidence = [
|
|
53427
53569
|
...terminalEvidence,
|
|
53428
53570
|
{
|
|
@@ -53468,17 +53610,7 @@ async function runChildProcessTask(ctx) {
|
|
|
53468
53610
|
parsedOutput = parsePiJsonOutput(transcriptText2);
|
|
53469
53611
|
rawFinalText = childResult.rawFinalText;
|
|
53470
53612
|
intermediateFindings = childResult.intermediateFindings;
|
|
53471
|
-
error =
|
|
53472
|
-
if (childResult.exitStatus?.timedOut) {
|
|
53473
|
-
error = errors.childTimeout({
|
|
53474
|
-
taskId: task.id,
|
|
53475
|
-
stderr: childResult.stderr
|
|
53476
|
-
}).message;
|
|
53477
|
-
}
|
|
53478
|
-
if (!error && parsedOutput) {
|
|
53479
|
-
const rateLimitErr = detectRetryableModelFailureFromOutput(parsedOutput);
|
|
53480
|
-
if (rateLimitErr) error = rateLimitErr;
|
|
53481
|
-
}
|
|
53613
|
+
error = attemptErrorFor(childResult, parsedOutput, task.id);
|
|
53482
53614
|
persistHeartbeat(true);
|
|
53483
53615
|
persistChildProgress({ type: "attempt_finished" }, true);
|
|
53484
53616
|
const attempt = {
|
|
@@ -61036,7 +61168,7 @@ __export(dynamic_workflow_runner_exports, {
|
|
|
61036
61168
|
runDynamicWorkflow: () => runDynamicWorkflow
|
|
61037
61169
|
});
|
|
61038
61170
|
import { readFileSync as readFileSync67 } from "node:fs";
|
|
61039
|
-
import { join as
|
|
61171
|
+
import { join as join72 } from "node:path";
|
|
61040
61172
|
import { transformSync } from "esbuild";
|
|
61041
61173
|
function assertStructuredCloneable(value, name) {
|
|
61042
61174
|
try {
|
|
@@ -61048,7 +61180,7 @@ function assertStructuredCloneable(value, name) {
|
|
|
61048
61180
|
}
|
|
61049
61181
|
function resolveScriptPath(workflow, cwd) {
|
|
61050
61182
|
const crewRoot = projectCrewRoot(cwd);
|
|
61051
|
-
const allowedBases = [
|
|
61183
|
+
const allowedBases = [join72(projectCrewRoot(cwd), "workflows"), join72(userPiRoot(), "workflows"), join72(packageRoot(), "workflows")];
|
|
61052
61184
|
for (const base of allowedBases) {
|
|
61053
61185
|
try {
|
|
61054
61186
|
const real = resolveRealContainedPath(base, workflow.filePath);
|
|
@@ -69560,6 +69692,19 @@ var init_notification_router = __esm({
|
|
|
69560
69692
|
...notification,
|
|
69561
69693
|
timestamp: notification.timestamp ?? now
|
|
69562
69694
|
};
|
|
69695
|
+
if (withTime.clear) {
|
|
69696
|
+
const clearKey = notificationKey(withTime);
|
|
69697
|
+
const wasInSeen = this.seen.delete(clearKey);
|
|
69698
|
+
if (wasInSeen) {
|
|
69699
|
+
try {
|
|
69700
|
+
this.opts.sink?.(withTime);
|
|
69701
|
+
} catch (sinkError) {
|
|
69702
|
+
logInternalError("notification-sink", sinkError);
|
|
69703
|
+
}
|
|
69704
|
+
this.deliver(withTime);
|
|
69705
|
+
}
|
|
69706
|
+
return true;
|
|
69707
|
+
}
|
|
69563
69708
|
try {
|
|
69564
69709
|
this.opts.sink?.(withTime);
|
|
69565
69710
|
} catch (sinkError) {
|
|
@@ -70092,11 +70237,15 @@ async function configureNotifications(ctx, state2, deps) {
|
|
|
70092
70237
|
sink: (notification) => state2.notificationSink?.write(notification)
|
|
70093
70238
|
},
|
|
70094
70239
|
(notification) => {
|
|
70095
|
-
|
|
70096
|
-
|
|
70097
|
-
|
|
70098
|
-
|
|
70099
|
-
|
|
70240
|
+
if (notification.clear) {
|
|
70241
|
+
deps.widgetState.notificationCount = Math.max(0, (deps.widgetState.notificationCount ?? 0) - 1);
|
|
70242
|
+
} else {
|
|
70243
|
+
deps.widgetState.notificationCount = (deps.widgetState.notificationCount ?? 0) + 1;
|
|
70244
|
+
sendFollowUp2(
|
|
70245
|
+
deps.pi,
|
|
70246
|
+
[notification.title, notification.body, notification.runId ? `Run: ${notification.runId}` : void 0].filter((line4) => Boolean(line4)).join("\n")
|
|
70247
|
+
);
|
|
70248
|
+
}
|
|
70100
70249
|
const currentCtx = deps.getCurrentCtx();
|
|
70101
70250
|
if (currentCtx) {
|
|
70102
70251
|
const uiConfig = loadConfig(currentCtx.cwd).config.ui;
|
|
@@ -72184,20 +72333,20 @@ init_internal_error();
|
|
|
72184
72333
|
|
|
72185
72334
|
// src/extension/crew-vibes/config.ts
|
|
72186
72335
|
import { existsSync as existsSync75, mkdirSync as mkdirSync42, readFileSync as readFileSync74, writeFileSync as writeFileSync9 } from "node:fs";
|
|
72187
|
-
import { dirname as dirname39, join as
|
|
72336
|
+
import { dirname as dirname39, join as join78 } from "node:path";
|
|
72188
72337
|
|
|
72189
72338
|
// src/extension/crew-vibes/font-detect.ts
|
|
72190
72339
|
import { existsSync as existsSync74, readFileSync as readFileSync73 } from "node:fs";
|
|
72191
72340
|
import { homedir as homedir11, platform } from "node:os";
|
|
72192
|
-
import { join as
|
|
72341
|
+
import { join as join77 } from "node:path";
|
|
72193
72342
|
function fontPath() {
|
|
72194
72343
|
const os18 = platform();
|
|
72195
72344
|
const home = homedir11();
|
|
72196
|
-
if (os18 === "darwin") return
|
|
72197
|
-
if (os18 === "linux") return
|
|
72345
|
+
if (os18 === "darwin") return join77(home, "Library", "Fonts", "crew-vibes.ttf");
|
|
72346
|
+
if (os18 === "linux") return join77(home, ".local", "share", "fonts", "crew-vibes.ttf");
|
|
72198
72347
|
if (os18 === "win32") {
|
|
72199
|
-
const local = process.env.LOCALAPPDATA ??
|
|
72200
|
-
return
|
|
72348
|
+
const local = process.env.LOCALAPPDATA ?? join77(home, "AppData", "Local");
|
|
72349
|
+
return join77(local, "Microsoft", "Windows", "Fonts", "crew-vibes.ttf");
|
|
72201
72350
|
}
|
|
72202
72351
|
return "";
|
|
72203
72352
|
}
|
|
@@ -72246,7 +72395,7 @@ function resolveHome() {
|
|
|
72246
72395
|
return (process.env.PI_TEAMS_HOME ?? process.env.PI_CREW_HOME)?.trim() || process.env.HOME || process.env.USERPROFILE || "";
|
|
72247
72396
|
}
|
|
72248
72397
|
function configPath2() {
|
|
72249
|
-
return
|
|
72398
|
+
return join78(resolveHome(), ".pi", "agent", "pi-crew-vibes.json");
|
|
72250
72399
|
}
|
|
72251
72400
|
var DEFAULT_CONFIG2 = {
|
|
72252
72401
|
enabled: true,
|
|
@@ -72746,14 +72895,14 @@ function createCrewVibesFooter(deps) {
|
|
|
72746
72895
|
// src/extension/crew-vibes/provider-usage.ts
|
|
72747
72896
|
import { readFileSync as readFileSync75 } from "node:fs";
|
|
72748
72897
|
import { homedir as homedir12 } from "node:os";
|
|
72749
|
-
import { join as
|
|
72898
|
+
import { join as join79 } from "node:path";
|
|
72750
72899
|
function withTimeout(ms, fn) {
|
|
72751
72900
|
const controller = new AbortController();
|
|
72752
72901
|
const timeoutId = setTimeout(() => controller.abort(), ms);
|
|
72753
72902
|
return fn(controller.signal).finally(() => clearTimeout(timeoutId));
|
|
72754
72903
|
}
|
|
72755
72904
|
function piAuthPath() {
|
|
72756
|
-
return
|
|
72905
|
+
return join79(homedir12(), ".pi", "agent", "auth.json");
|
|
72757
72906
|
}
|
|
72758
72907
|
function loadAnthropicToken() {
|
|
72759
72908
|
const envToken = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
|
|
@@ -72798,8 +72947,8 @@ function tokenFromHostEntry(entry) {
|
|
|
72798
72947
|
return void 0;
|
|
72799
72948
|
}
|
|
72800
72949
|
function loadLegacyCopilotToken() {
|
|
72801
|
-
const configHome = process.env.XDG_CONFIG_HOME?.trim() ||
|
|
72802
|
-
const candidates = [
|
|
72950
|
+
const configHome = process.env.XDG_CONFIG_HOME?.trim() || join79(homedir12(), ".config");
|
|
72951
|
+
const candidates = [join79(configHome, "github-copilot", "hosts.json"), join79(homedir12(), ".github-copilot", "hosts.json")];
|
|
72803
72952
|
for (const hostsPath of candidates) {
|
|
72804
72953
|
try {
|
|
72805
72954
|
const data = JSON.parse(readFileSync75(hostsPath, "utf8"));
|
|
@@ -76664,7 +76813,6 @@ function safeStringify(value) {
|
|
|
76664
76813
|
// src/extension/registration/lifecycle-handlers.ts
|
|
76665
76814
|
init_child_pi();
|
|
76666
76815
|
init_live_agent_manager();
|
|
76667
|
-
init_model_fallback();
|
|
76668
76816
|
init_pi_args();
|
|
76669
76817
|
init_provider_quota();
|
|
76670
76818
|
init_session_model();
|
|
@@ -76963,8 +77111,7 @@ function installModelTrackingHandlers(pi) {
|
|
|
76963
77111
|
noteSessionThinking(event.level);
|
|
76964
77112
|
});
|
|
76965
77113
|
pi.on("after_provider_response", (event) => {
|
|
76966
|
-
const
|
|
76967
|
-
const provider = model ? providerOfModelRef(model) : void 0;
|
|
77114
|
+
const provider = resolveProviderForResponse();
|
|
76968
77115
|
if (provider) noteProviderResponse(provider, event.status, event.headers);
|
|
76969
77116
|
});
|
|
76970
77117
|
}
|
|
@@ -77361,12 +77508,40 @@ function setupRenderLoop(pi, ctx, extensionCtx, loadedConfig) {
|
|
|
77361
77508
|
const currentSessionId = ctx.currentCtx?.sessionManager?.getSessionId();
|
|
77362
77509
|
const sessionManifests = filterManifestsForHealthNotifications(manifests, currentSessionId);
|
|
77363
77510
|
const now = Date.now();
|
|
77511
|
+
const clearHealthNotifications = (runId) => {
|
|
77512
|
+
for (const kind of ["recovery_dead_workers", "recovery_missing_heartbeat"]) {
|
|
77513
|
+
const key = `${kind}_${runId}`;
|
|
77514
|
+
ctx.autoRecoveryLast.delete(key);
|
|
77515
|
+
ctx.notifyOperator({
|
|
77516
|
+
id: key,
|
|
77517
|
+
clear: true,
|
|
77518
|
+
severity: "info",
|
|
77519
|
+
source: "health",
|
|
77520
|
+
runId,
|
|
77521
|
+
title: `Cleared ${kind} for ${runId}`
|
|
77522
|
+
});
|
|
77523
|
+
}
|
|
77524
|
+
};
|
|
77364
77525
|
for (const run of sessionManifests) {
|
|
77365
|
-
if (run.status !== "running")
|
|
77526
|
+
if (run.status !== "running") {
|
|
77527
|
+
snapshotCache.invalidate(run.runId);
|
|
77528
|
+
clearHealthNotifications(run.runId);
|
|
77529
|
+
continue;
|
|
77530
|
+
}
|
|
77366
77531
|
try {
|
|
77532
|
+
const freshManifest = ctx.getManifestCache(extensionCtx.cwd).get(run.runId);
|
|
77533
|
+
if (freshManifest?.status !== "running") {
|
|
77534
|
+
snapshotCache.invalidate(run.runId);
|
|
77535
|
+
clearHealthNotifications(run.runId);
|
|
77536
|
+
continue;
|
|
77537
|
+
}
|
|
77367
77538
|
const snapshot = snapshotCache.get(run.runId);
|
|
77368
77539
|
if (!snapshot) continue;
|
|
77369
|
-
if (snapshot.manifest.status !== "running")
|
|
77540
|
+
if (snapshot.manifest.status !== "running") {
|
|
77541
|
+
snapshotCache.invalidate(run.runId);
|
|
77542
|
+
clearHealthNotifications(run.runId);
|
|
77543
|
+
continue;
|
|
77544
|
+
}
|
|
77370
77545
|
const summary = summarizeHeartbeats(snapshot, { now });
|
|
77371
77546
|
const maybeNotifyHealth = (kind, count2, title, body) => {
|
|
77372
77547
|
if (count2 <= 0) return;
|