pi-crew 0.10.5 → 0.10.6
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 +193 -0
- package/agents/analyst.md +37 -2
- package/agents/cold-verifier.md +10 -1
- package/agents/councillor-critic.md +39 -0
- package/agents/councillor-pragmatist.md +39 -0
- package/agents/councillor-skeptic.md +41 -0
- package/agents/critic.md +40 -2
- package/agents/designer.md +58 -0
- package/agents/executor.md +39 -2
- package/agents/explorer.md +38 -2
- package/agents/librarian.md +49 -0
- package/agents/oracle.md +54 -0
- package/agents/orchestrator.md +48 -0
- package/agents/planner.md +41 -2
- package/agents/reviewer.md +39 -2
- package/agents/security-reviewer.md +43 -2
- package/agents/test-engineer.md +48 -2
- package/agents/verifier.md +14 -1
- package/agents/writer.md +32 -2
- package/dist/index.mjs +520 -74
- package/package.json +1 -1
- package/skills/async-worker-recovery/SKILL.md +4 -1
- package/skills/child-pi-spawning/SKILL.md +4 -1
- package/skills/context-artifact-hygiene/SKILL.md +4 -1
- package/skills/council/SKILL.md +24 -45
- package/skills/delegation-patterns/SKILL.md +18 -1
- package/skills/distill-persona/SKILL.md +4 -1
- package/skills/distill-software/SKILL.md +4 -1
- package/skills/event-log-tracing/SKILL.md +4 -1
- package/skills/git-master/SKILL.md +4 -1
- package/skills/iterative-audit/SKILL.md +4 -1
- package/skills/live-agent-lifecycle/SKILL.md +4 -1
- package/skills/mailbox-interactive/SKILL.md +4 -1
- package/skills/model-routing-context/SKILL.md +10 -1
- package/skills/multi-perspective-review/SKILL.md +18 -1
- package/skills/observability-reliability/SKILL.md +4 -1
- package/skills/orchestration/SKILL.md +18 -1
- package/skills/ownership-session-security/SKILL.md +4 -1
- package/skills/pi-extension-lifecycle/SKILL.md +4 -1
- package/skills/post-mortem/SKILL.md +4 -1
- package/skills/read-only-explorer/SKILL.md +4 -1
- package/skills/real-test-pi-crew/SKILL.md +165 -12
- package/skills/requirements-to-task-packet/SKILL.md +10 -1
- package/skills/research/SKILL.md +4 -1
- package/skills/resource-discovery-config/SKILL.md +10 -1
- package/skills/runtime-state-reader/SKILL.md +4 -1
- package/skills/safe-bash/SKILL.md +4 -1
- package/skills/scrutinize/SKILL.md +24 -1
- package/skills/secure-agent-orchestration-review/SKILL.md +4 -1
- package/skills/state-mutation-locking/SKILL.md +4 -1
- package/skills/systematic-debugging/SKILL.md +4 -1
- package/skills/verification-before-done/SKILL.md +18 -1
- package/skills/widget-rendering/SKILL.md +4 -1
- package/skills/workspace-isolation/SKILL.md +4 -1
- package/skills/worktree-isolation/SKILL.md +4 -1
- package/src/config/config-validation.ts +1 -0
- package/src/config/types.ts +8 -0
- package/src/errors.ts +1 -1
- package/src/extension/context-status-injection.ts +2 -2
- package/src/extension/knowledge-injection.ts +19 -7
- package/src/extension/post-init-skill-check.ts +32 -0
- package/src/extension/register.ts +9 -1
- package/src/extension/registration/hook-registration.ts +20 -3
- package/src/extension/registration/tool-loop-guard.ts +243 -0
- package/src/extension/team-tool/handle-settings.ts +10 -0
- package/src/extension/team-tool/run.ts +42 -1
- package/src/extension/team-tool-types.ts +6 -0
- package/src/prompt/prompt-runtime.ts +25 -6
- package/src/runtime/async-runner.ts +75 -11
- package/src/runtime/background-runner.ts +41 -0
- package/src/runtime/broker/crew-broker-client.ts +45 -2
- package/src/runtime/broker/crew-broker.ts +22 -27
- package/src/runtime/broker/protocol/request-parsers.ts +10 -2
- package/src/runtime/broker/stdin-handshake.ts +87 -0
- package/src/runtime/broker/wait-push.ts +45 -0
- package/src/runtime/detached-run-results.ts +25 -1
- package/src/runtime/foreground-watchdog.ts +24 -5
- package/src/runtime/live-session/live-session-runtime.ts +1 -1
- package/src/runtime/model/model-scope.ts +2 -2
- package/src/runtime/run-tracker.ts +65 -11
- package/src/runtime/skill-instructions.ts +20 -4
- package/src/runtime/task-runner/child-executor.ts +1 -1
- package/src/runtime/task-runner/prompt-builder.ts +22 -9
- package/src/schema/config-schema.ts +1 -0
- package/src/skills/discover-skills.ts +2 -2
- package/src/ui/settings-overlay.ts +40 -0
- package/src/utils/frontmatter.ts +7 -1
- package/src/utils/ndjson.ts +9 -1
package/dist/index.mjs
CHANGED
|
@@ -10606,7 +10606,8 @@ var init_config_schema = __esm({
|
|
|
10606
10606
|
forcePreflight: Type.Optional(Type.Boolean()),
|
|
10607
10607
|
ambientStatusInjection: Type.Optional(Type.Boolean()),
|
|
10608
10608
|
perWriteValidation: Type.Optional(Type.Boolean()),
|
|
10609
|
-
scopeModels: Type.Optional(Type.Boolean())
|
|
10609
|
+
scopeModels: Type.Optional(Type.Boolean()),
|
|
10610
|
+
loopGuard: Type.Optional(Type.Boolean())
|
|
10610
10611
|
},
|
|
10611
10612
|
{ additionalProperties: false }
|
|
10612
10613
|
);
|
|
@@ -11253,6 +11254,7 @@ function parseReliabilityConfig(value) {
|
|
|
11253
11254
|
forcePreflight: parseWithSchema(Type.Boolean(), obj.forcePreflight),
|
|
11254
11255
|
ambientStatusInjection: parseWithSchema(Type.Boolean(), obj.ambientStatusInjection),
|
|
11255
11256
|
perWriteValidation: parseWithSchema(Type.Boolean(), obj.perWriteValidation),
|
|
11257
|
+
loopGuard: parseWithSchema(Type.Boolean(), obj.loopGuard),
|
|
11256
11258
|
scopeModels: parseWithSchema(Type.Boolean(), obj.scopeModels)
|
|
11257
11259
|
};
|
|
11258
11260
|
return Object.values(reliability).some((entry) => entry !== void 0) ? reliability : void 0;
|
|
@@ -12592,7 +12594,10 @@ function parseLines(raw) {
|
|
|
12592
12594
|
const separator = trimmed.indexOf(":");
|
|
12593
12595
|
if (separator === -1) continue;
|
|
12594
12596
|
const key = trimmed.slice(0, separator).trim();
|
|
12595
|
-
|
|
12597
|
+
let value = trimmed.slice(separator + 1).trim();
|
|
12598
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
12599
|
+
value = value.slice(1, -1);
|
|
12600
|
+
}
|
|
12596
12601
|
if (key) frontmatter[key] = value;
|
|
12597
12602
|
}
|
|
12598
12603
|
return frontmatter;
|
|
@@ -15396,7 +15401,7 @@ var init_errors3 = __esm({
|
|
|
15396
15401
|
[ErrorCode.EventLogLockTimeout]: "Another process holds the event-log lock. Check for orphaned `.mkdirlock` directories or stale pi-crew processes, then retry.",
|
|
15397
15402
|
[ErrorCode.DepthLimitExceeded]: "A pipeline/chain exceeded the recursion depth limit, which usually indicates a circular stage dependency. Review step `dependsOn` chains.",
|
|
15398
15403
|
[ErrorCode.RunStale]: "The worker stopped heartbeating and was treated as a zombie. Re-run the team (resume or fresh); if it recurs, check `runtime.executeWorkers` / system load.",
|
|
15399
|
-
[ErrorCode.ModelOutOfScope]: "The requested model is not in your pi `enabledModels` allowlist. Either pick a model listed in `enabledModels` (settings.json) or extend the allowlist. The scope gate is opt-in \u2014 disable `
|
|
15404
|
+
[ErrorCode.ModelOutOfScope]: "The requested model is not in your pi `enabledModels` allowlist. Either pick a model listed in `enabledModels` (settings.json) or extend the allowlist. The scope gate is opt-in \u2014 disable `reliability.scopeModels` to allow any model."
|
|
15400
15405
|
};
|
|
15401
15406
|
CrewError = class extends Error {
|
|
15402
15407
|
code;
|
|
@@ -17345,10 +17350,10 @@ function dedupeTerminalEvents(events) {
|
|
|
17345
17350
|
const seen = /* @__PURE__ */ new Set();
|
|
17346
17351
|
const output = [];
|
|
17347
17352
|
for (const event of events) {
|
|
17348
|
-
const
|
|
17349
|
-
if (
|
|
17350
|
-
if (seen.has(
|
|
17351
|
-
seen.add(
|
|
17353
|
+
const fingerprint2 = event.metadata?.fingerprint;
|
|
17354
|
+
if (fingerprint2 && TERMINAL_EVENT_TYPES.has(event.type)) {
|
|
17355
|
+
if (seen.has(fingerprint2)) continue;
|
|
17356
|
+
seen.add(fingerprint2);
|
|
17352
17357
|
}
|
|
17353
17358
|
output.push(event);
|
|
17354
17359
|
}
|
|
@@ -17692,6 +17697,13 @@ var init_run_event_bus = __esm({
|
|
|
17692
17697
|
});
|
|
17693
17698
|
|
|
17694
17699
|
// src/runtime/broker/broker-issuer.ts
|
|
17700
|
+
var broker_issuer_exports = {};
|
|
17701
|
+
__export(broker_issuer_exports, {
|
|
17702
|
+
getActiveBrokerIssuer: () => getActiveBrokerIssuer,
|
|
17703
|
+
getActiveBrokerRevoker: () => getActiveBrokerRevoker,
|
|
17704
|
+
setActiveBrokerIssuer: () => setActiveBrokerIssuer,
|
|
17705
|
+
setActiveBrokerRevoker: () => setActiveBrokerRevoker
|
|
17706
|
+
});
|
|
17695
17707
|
function setActiveBrokerIssuer(issuer) {
|
|
17696
17708
|
activeIssuer = issuer;
|
|
17697
17709
|
}
|
|
@@ -29423,8 +29435,21 @@ var init_i18n = __esm({
|
|
|
29423
29435
|
});
|
|
29424
29436
|
|
|
29425
29437
|
// src/runtime/run-tracker.ts
|
|
29438
|
+
var run_tracker_exports = {};
|
|
29439
|
+
__export(run_tracker_exports, {
|
|
29440
|
+
clearRunPromisesForTest: () => clearRunPromisesForTest,
|
|
29441
|
+
detachRunPromise: () => detachRunPromise,
|
|
29442
|
+
hasActiveRunPromise: () => hasActiveRunPromise,
|
|
29443
|
+
hasPendingRunDetach: () => hasPendingRunDetach,
|
|
29444
|
+
registerRunPromise: () => registerRunPromise,
|
|
29445
|
+
rejectRunPromise: () => rejectRunPromise,
|
|
29446
|
+
resolveRunPromise: () => resolveRunPromise,
|
|
29447
|
+
waitForRun: () => waitForRun
|
|
29448
|
+
});
|
|
29426
29449
|
import * as fs49 from "node:fs";
|
|
29427
29450
|
function registerRunPromise(runId) {
|
|
29451
|
+
const existing = activeRunPromises.get(runId);
|
|
29452
|
+
if (existing) return existing;
|
|
29428
29453
|
detachRequests.delete(runId);
|
|
29429
29454
|
let resolve27;
|
|
29430
29455
|
let reject;
|
|
@@ -29436,12 +29461,33 @@ function registerRunPromise(runId) {
|
|
|
29436
29461
|
activeRunPromises.set(runId, entry);
|
|
29437
29462
|
return entry;
|
|
29438
29463
|
}
|
|
29464
|
+
function detachRunPromise(runId, cwd) {
|
|
29465
|
+
const loaded = loadRunManifestById(cwd, runId);
|
|
29466
|
+
if (!loaded) return false;
|
|
29467
|
+
const entry = activeRunPromises.get(runId);
|
|
29468
|
+
if (entry) {
|
|
29469
|
+
activeRunPromises.delete(runId);
|
|
29470
|
+
detachRequests.delete(runId);
|
|
29471
|
+
entry.resolve({ ...loaded, detached: true });
|
|
29472
|
+
return true;
|
|
29473
|
+
}
|
|
29474
|
+
detachRequests.add(runId);
|
|
29475
|
+
return true;
|
|
29476
|
+
}
|
|
29477
|
+
function hasPendingRunDetach(runId) {
|
|
29478
|
+
return detachRequests.has(runId);
|
|
29479
|
+
}
|
|
29439
29480
|
function resolveRunPromise(runId, result4) {
|
|
29440
29481
|
const entry = activeRunPromises.get(runId);
|
|
29441
29482
|
if (entry) {
|
|
29442
29483
|
entry.resolve(result4);
|
|
29443
29484
|
activeRunPromises.delete(runId);
|
|
29444
29485
|
}
|
|
29486
|
+
resolvedRunResults.set(runId, result4);
|
|
29487
|
+
if (resolvedRunResults.size > RESOLVED_TOMBSTONE_LIMIT) {
|
|
29488
|
+
const oldest = resolvedRunResults.keys().next().value;
|
|
29489
|
+
if (oldest !== void 0) resolvedRunResults.delete(oldest);
|
|
29490
|
+
}
|
|
29445
29491
|
}
|
|
29446
29492
|
function rejectRunPromise(runId, reason) {
|
|
29447
29493
|
const entry = activeRunPromises.get(runId);
|
|
@@ -29450,6 +29496,16 @@ function rejectRunPromise(runId, reason) {
|
|
|
29450
29496
|
activeRunPromises.delete(runId);
|
|
29451
29497
|
}
|
|
29452
29498
|
}
|
|
29499
|
+
function raceRunPromise(entry, timeoutMs, deadline) {
|
|
29500
|
+
let timer;
|
|
29501
|
+
const remaining = Math.max(0, deadline - Date.now());
|
|
29502
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
29503
|
+
timer = setTimeout(() => reject(new Error(`waitForRun timed out after ${timeoutMs}ms`)), remaining);
|
|
29504
|
+
});
|
|
29505
|
+
return Promise.race([entry.promise, timeoutPromise]).finally(() => {
|
|
29506
|
+
if (timer) clearTimeout(timer);
|
|
29507
|
+
});
|
|
29508
|
+
}
|
|
29453
29509
|
async function waitForRun(runId, cwd, options = {}) {
|
|
29454
29510
|
const { timeoutMs = 3e5, pollIntervalMs = 500 } = options;
|
|
29455
29511
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -29462,23 +29518,15 @@ async function waitForRun(runId, cwd, options = {}) {
|
|
|
29462
29518
|
return { ...loaded, detached: true };
|
|
29463
29519
|
}
|
|
29464
29520
|
const entry = activeRunPromises.get(runId);
|
|
29465
|
-
if (entry)
|
|
29466
|
-
let timer;
|
|
29467
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
29468
|
-
timer = setTimeout(() => reject(new Error(`waitForRun timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
29469
|
-
});
|
|
29470
|
-
try {
|
|
29471
|
-
return await Promise.race([entry.promise, timeoutPromise]);
|
|
29472
|
-
} finally {
|
|
29473
|
-
if (timer) clearTimeout(timer);
|
|
29474
|
-
}
|
|
29475
|
-
}
|
|
29521
|
+
if (entry) return await raceRunPromise(entry, timeoutMs, deadline);
|
|
29476
29522
|
let attempt = 0;
|
|
29477
29523
|
while (Date.now() < deadline) {
|
|
29478
29524
|
if (detachRequests.delete(runId)) {
|
|
29479
29525
|
const current = loadRunManifestById(cwd, runId);
|
|
29480
29526
|
if (current) return { ...current, detached: true };
|
|
29481
29527
|
}
|
|
29528
|
+
const entryNow = activeRunPromises.get(runId);
|
|
29529
|
+
if (entryNow) return await raceRunPromise(entryNow, timeoutMs, deadline);
|
|
29482
29530
|
if (attempt === 0) {
|
|
29483
29531
|
const runDir = createRunPaths(cwd, runId).stateRoot;
|
|
29484
29532
|
if (!fs49.existsSync(runDir)) {
|
|
@@ -29489,13 +29537,29 @@ async function waitForRun(runId, cwd, options = {}) {
|
|
|
29489
29537
|
if (fresh && isFinishedRunStatus(fresh.manifest.status)) {
|
|
29490
29538
|
return fresh;
|
|
29491
29539
|
}
|
|
29540
|
+
const tombstone = resolvedRunResults.get(runId);
|
|
29541
|
+
if (tombstone) {
|
|
29542
|
+
resolvedRunResults.delete(runId);
|
|
29543
|
+
return tombstone;
|
|
29544
|
+
}
|
|
29492
29545
|
const delay = Math.min(pollIntervalMs, 50 * 2 ** Math.min(attempt, 6));
|
|
29493
29546
|
await new Promise((r) => setTimeout(r, delay));
|
|
29494
29547
|
attempt++;
|
|
29495
29548
|
}
|
|
29496
29549
|
throw new Error(`waitForRun timed out after ${timeoutMs}ms`);
|
|
29497
29550
|
}
|
|
29498
|
-
|
|
29551
|
+
function hasActiveRunPromise(runId) {
|
|
29552
|
+
return activeRunPromises.has(runId);
|
|
29553
|
+
}
|
|
29554
|
+
function clearRunPromisesForTest() {
|
|
29555
|
+
detachRequests.clear();
|
|
29556
|
+
resolvedRunResults.clear();
|
|
29557
|
+
for (const entry of activeRunPromises.values()) {
|
|
29558
|
+
entry.reject(new Error("Cleared by test"));
|
|
29559
|
+
}
|
|
29560
|
+
activeRunPromises.clear();
|
|
29561
|
+
}
|
|
29562
|
+
var activeRunPromises, detachRequests, resolvedRunResults, RESOLVED_TOMBSTONE_LIMIT;
|
|
29499
29563
|
var init_run_tracker = __esm({
|
|
29500
29564
|
"src/runtime/run-tracker.ts"() {
|
|
29501
29565
|
"use strict";
|
|
@@ -29503,6 +29567,8 @@ var init_run_tracker = __esm({
|
|
|
29503
29567
|
init_process_status();
|
|
29504
29568
|
activeRunPromises = /* @__PURE__ */ new Map();
|
|
29505
29569
|
detachRequests = /* @__PURE__ */ new Set();
|
|
29570
|
+
resolvedRunResults = /* @__PURE__ */ new Map();
|
|
29571
|
+
RESOLVED_TOMBSTONE_LIMIT = 32;
|
|
29506
29572
|
}
|
|
29507
29573
|
});
|
|
29508
29574
|
|
|
@@ -29874,7 +29940,6 @@ __export(skill_instructions_exports, {
|
|
|
29874
29940
|
});
|
|
29875
29941
|
import * as fs50 from "node:fs";
|
|
29876
29942
|
import * as path38 from "node:path";
|
|
29877
|
-
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
29878
29943
|
import * as os11 from "node:os";
|
|
29879
29944
|
function isValidSkillName(name) {
|
|
29880
29945
|
return name.length > 0 && name.length <= MAX_SKILL_NAME_CHARS && isSafePathId(name);
|
|
@@ -29912,8 +29977,19 @@ function collectTaskSkillNames(input) {
|
|
|
29912
29977
|
if (input.agent?.skills?.length) names.push(...input.agent.skills);
|
|
29913
29978
|
if (Array.isArray(input.teamRole?.skills)) names.push(...input.teamRole.skills);
|
|
29914
29979
|
if (Array.isArray(input.step?.skills)) names.push(...input.step.skills);
|
|
29915
|
-
|
|
29916
|
-
|
|
29980
|
+
const denylist = /* @__PURE__ */ new Set();
|
|
29981
|
+
if (Array.isArray(input.override)) {
|
|
29982
|
+
for (const item of input.override) {
|
|
29983
|
+
if (item === "*") {
|
|
29984
|
+
} else if (item.startsWith("!")) {
|
|
29985
|
+
denylist.add(item.slice(1));
|
|
29986
|
+
} else {
|
|
29987
|
+
names.push(item);
|
|
29988
|
+
}
|
|
29989
|
+
}
|
|
29990
|
+
}
|
|
29991
|
+
if (denylist.size === 0) return unique(names);
|
|
29992
|
+
return unique(names).filter((n) => !denylist.has(n));
|
|
29917
29993
|
}
|
|
29918
29994
|
function resolveTaskSkillNames(input) {
|
|
29919
29995
|
return collectTaskSkillNames(input).slice(0, MAX_SELECTED_SKILLS);
|
|
@@ -30124,10 +30200,11 @@ var init_skill_instructions = __esm({
|
|
|
30124
30200
|
"src/runtime/skill-instructions.ts"() {
|
|
30125
30201
|
"use strict";
|
|
30126
30202
|
init_internal_error();
|
|
30203
|
+
init_paths();
|
|
30127
30204
|
init_safe_paths();
|
|
30128
30205
|
init_skill_effectiveness();
|
|
30129
30206
|
init_peer_dep();
|
|
30130
|
-
PACKAGE_SKILLS_DIR = path38.
|
|
30207
|
+
PACKAGE_SKILLS_DIR = path38.join(packageRoot(), "skills");
|
|
30131
30208
|
MAX_SKILL_CHARS = 1500;
|
|
30132
30209
|
MAX_TOTAL_CHARS = 6e3;
|
|
30133
30210
|
MAX_SKILL_NAME_CHARS = 80;
|
|
@@ -32305,7 +32382,6 @@ var init_validate = __esm({
|
|
|
32305
32382
|
import * as fs54 from "node:fs";
|
|
32306
32383
|
import * as os12 from "node:os";
|
|
32307
32384
|
import * as path40 from "node:path";
|
|
32308
|
-
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
32309
32385
|
function listSkillDirs(cwd) {
|
|
32310
32386
|
return [
|
|
32311
32387
|
{ root: PACKAGE_SKILLS_DIR2, source: "package" },
|
|
@@ -32423,9 +32499,10 @@ var init_discover_skills = __esm({
|
|
|
32423
32499
|
"use strict";
|
|
32424
32500
|
init_peer_dep();
|
|
32425
32501
|
init_internal_error();
|
|
32502
|
+
init_paths();
|
|
32426
32503
|
init_safe_paths();
|
|
32427
32504
|
init_validate();
|
|
32428
|
-
PACKAGE_SKILLS_DIR2 = path40.
|
|
32505
|
+
PACKAGE_SKILLS_DIR2 = path40.join(packageRoot(), "skills");
|
|
32429
32506
|
CACHE_TTL_MS = 3e4;
|
|
32430
32507
|
cache2 = null;
|
|
32431
32508
|
lastDiagnostics = [];
|
|
@@ -46534,14 +46611,14 @@ var init_protocol = __esm({
|
|
|
46534
46611
|
});
|
|
46535
46612
|
|
|
46536
46613
|
// src/runtime/scratchpad/engine.ts
|
|
46537
|
-
import { fileURLToPath as
|
|
46614
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
46538
46615
|
var SNAPSHOT_MAX_BYTES, GUEST_PATH;
|
|
46539
46616
|
var init_engine = __esm({
|
|
46540
46617
|
"src/runtime/scratchpad/engine.ts"() {
|
|
46541
46618
|
"use strict";
|
|
46542
46619
|
init_protocol();
|
|
46543
46620
|
SNAPSHOT_MAX_BYTES = 4 * 1024 * 1024;
|
|
46544
|
-
GUEST_PATH =
|
|
46621
|
+
GUEST_PATH = fileURLToPath4(new URL("./guest.ts", import.meta.url));
|
|
46545
46622
|
}
|
|
46546
46623
|
});
|
|
46547
46624
|
|
|
@@ -48535,6 +48612,7 @@ function buildKnowledgeFragment(cwd, query) {
|
|
|
48535
48612
|
}
|
|
48536
48613
|
function registerKnowledgeInjection(pi) {
|
|
48537
48614
|
pi.on("before_agent_start", (event) => {
|
|
48615
|
+
if (process.env.PI_CREW_KIND === "subagent") return;
|
|
48538
48616
|
const options = event.systemPromptOptions ?? {};
|
|
48539
48617
|
const cwd = typeof options.cwd === "string" ? options.cwd : process.cwd();
|
|
48540
48618
|
const fragment = buildKnowledgeFragment(cwd);
|
|
@@ -49079,10 +49157,11 @@ function readOnlyRoleInstructions(role) {
|
|
|
49079
49157
|
"- Your final RESULT TEXT is persisted automatically by the runner (as a result artifact and, if the step declares `output:`, to a shared file). To deliver a plan, report, or findings, EMIT THEM AS TEXT in your final result \u2014 do NOT try to write a file yourself."
|
|
49080
49158
|
].join("\n");
|
|
49081
49159
|
}
|
|
49082
|
-
function coordinationBridgeInstructions(task) {
|
|
49160
|
+
function coordinationBridgeInstructions(task, opts) {
|
|
49161
|
+
const includeMailboxTarget = opts?.includeMailboxTarget ?? true;
|
|
49083
49162
|
return [
|
|
49084
49163
|
"# Crew Coordination Channel",
|
|
49085
|
-
`Mailbox target for this task: ${task.id}
|
|
49164
|
+
...includeMailboxTarget ? [`Mailbox target for this task: ${task.id}`] : [],
|
|
49086
49165
|
"Use the run mailbox contract for coordination with the leader/orchestrator:",
|
|
49087
49166
|
"- If blocked or uncertain, report the blocker in your final result and, when mailbox tools/API are available, send an inbox/outbox message addressed to the leader.",
|
|
49088
49167
|
"- Never guess implementation details that materially affect decisions. If the `ask` tool is available and you need a clarification, a decision, or a missing requirement before you can proceed safely, call `ask` and wait \u2014 a parked question is cheaper than a wrong build.",
|
|
@@ -49212,8 +49291,6 @@ async function renderTaskPrompt(manifest, step, task, agent, skillBlock = "", pr
|
|
|
49212
49291
|
`State root: ${manifest.stateRoot}`,
|
|
49213
49292
|
`Artifacts root: ${manifest.artifactsRoot}`,
|
|
49214
49293
|
`Events path: ${manifest.eventsPath}`,
|
|
49215
|
-
`Task ID: ${task.id}`,
|
|
49216
|
-
`Task cwd: ${task.cwd}`,
|
|
49217
49294
|
`Workspace mode: ${manifest.workspaceMode}`,
|
|
49218
49295
|
"",
|
|
49219
49296
|
"Protocol:",
|
|
@@ -49221,10 +49298,14 @@ async function renderTaskPrompt(manifest, step, task, agent, skillBlock = "", pr
|
|
|
49221
49298
|
"- Report blockers and verification evidence in the final result.",
|
|
49222
49299
|
"- Do not claim completion without evidence.",
|
|
49223
49300
|
"- Follow the Task Packet contract below; escalate if any contract field is impossible to satisfy.",
|
|
49301
|
+
// PROMPT-2 (port of OMO-slim task-rejection, improved phrasing): a
|
|
49302
|
+
// universal lane-guard for every role — complements the per-agent reject
|
|
49303
|
+
// sections in agents/*.md with a scaffold-level instruction.
|
|
49304
|
+
"- If a task falls outside your role, do not attempt partial work. Return a concise rejection to the leader naming the lane that should own it.",
|
|
49224
49305
|
"",
|
|
49225
49306
|
readOnlyRoleInstructions(task.role),
|
|
49226
49307
|
"",
|
|
49227
|
-
coordinationBridgeInstructions(task),
|
|
49308
|
+
coordinationBridgeInstructions(task, { includeMailboxTarget: false }),
|
|
49228
49309
|
"",
|
|
49229
49310
|
stableComponents.treeBlock,
|
|
49230
49311
|
"",
|
|
@@ -49232,12 +49313,19 @@ async function renderTaskPrompt(manifest, step, task, agent, skillBlock = "", pr
|
|
|
49232
49313
|
"",
|
|
49233
49314
|
toolGuidanceBlock(agent),
|
|
49234
49315
|
"",
|
|
49235
|
-
// O4: project knowledge (.crew/knowledge.md)
|
|
49236
|
-
// pi-crew extension (
|
|
49237
|
-
//
|
|
49316
|
+
// O4 (ARCH-2 corrected): project knowledge (.crew/knowledge.md). Builtin
|
|
49317
|
+
// workers don't load the pi-crew extension (agents declare no `extensions:`
|
|
49318
|
+
// in frontmatter), so before_agent_start knowledge injection doesn't fire
|
|
49319
|
+
// for them — and the knowledge-injection hook now early-returns on
|
|
49320
|
+
// PI_CREW_KIND=subagent, so even agents that DO declare the extension
|
|
49321
|
+
// can't double-inject. This prompt-builder fragment is the single source
|
|
49322
|
+
// of worker project knowledge.
|
|
49238
49323
|
stableComponents.knowledgeFragment
|
|
49239
49324
|
].filter(Boolean).join("\n");
|
|
49240
49325
|
const dynamicSuffix = [
|
|
49326
|
+
`Task ID: ${task.id}`,
|
|
49327
|
+
`Task cwd: ${task.cwd}`,
|
|
49328
|
+
`Mailbox target: ${task.id}`,
|
|
49241
49329
|
`Goal:
|
|
49242
49330
|
${manifest.goal}`,
|
|
49243
49331
|
"",
|
|
@@ -56505,6 +56593,11 @@ var init_handle_settings = __esm({
|
|
|
56505
56593
|
"reliability.autoRetry": false,
|
|
56506
56594
|
"reliability.autoRecover": false,
|
|
56507
56595
|
"reliability.cleanupOrphanedTempDirs": true,
|
|
56596
|
+
"reliability.loopGuard": true,
|
|
56597
|
+
"reliability.perWriteValidation": true,
|
|
56598
|
+
"reliability.ambientStatusInjection": true,
|
|
56599
|
+
"reliability.forcePreflight": false,
|
|
56600
|
+
"reliability.scopeModels": false,
|
|
56508
56601
|
"telemetry.enabled": false,
|
|
56509
56602
|
"notifications.enabled": false
|
|
56510
56603
|
};
|
|
@@ -56584,6 +56677,11 @@ var init_handle_settings = __esm({
|
|
|
56584
56677
|
"reliability.autoRetry",
|
|
56585
56678
|
"reliability.autoRecover",
|
|
56586
56679
|
"reliability.cleanupOrphanedTempDirs",
|
|
56680
|
+
"reliability.loopGuard",
|
|
56681
|
+
"reliability.perWriteValidation",
|
|
56682
|
+
"reliability.ambientStatusInjection",
|
|
56683
|
+
"reliability.forcePreflight",
|
|
56684
|
+
"reliability.scopeModels",
|
|
56587
56685
|
"reliability.deadletterThreshold",
|
|
56588
56686
|
"reliability.retryPolicy.maxAttempts",
|
|
56589
56687
|
"reliability.retryPolicy.backoffMs",
|
|
@@ -57252,15 +57350,13 @@ import { spawn as spawn6 } from "node:child_process";
|
|
|
57252
57350
|
import * as fs89 from "node:fs";
|
|
57253
57351
|
import { createRequire as createRequire6 } from "node:module";
|
|
57254
57352
|
import * as path69 from "node:path";
|
|
57255
|
-
import {
|
|
57256
|
-
function packageRootFromRuntime() {
|
|
57257
|
-
return path69.resolve(path69.dirname(fileURLToPath7(import.meta.url)), "..", "..");
|
|
57258
|
-
}
|
|
57353
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
57259
57354
|
function jitiRegisterPathFromPackageJson(packageJsonPath) {
|
|
57260
57355
|
return path69.join(path69.dirname(packageJsonPath), "lib", "jiti-register.mjs");
|
|
57261
57356
|
}
|
|
57262
|
-
function resolveJitiRegisterPath(
|
|
57263
|
-
|
|
57357
|
+
function resolveJitiRegisterPath(pkgRoot, exists = fs89.existsSync) {
|
|
57358
|
+
const effectiveRoot = pkgRoot ?? packageRoot();
|
|
57359
|
+
let current = path69.resolve(effectiveRoot);
|
|
57264
57360
|
const root = path69.parse(current).root;
|
|
57265
57361
|
while (true) {
|
|
57266
57362
|
const candidate = path69.join(current, "node_modules", "jiti", "lib", "jiti-register.mjs");
|
|
@@ -57313,7 +57409,7 @@ function buildLoaderUnavailableMessage(searchedFrom) {
|
|
|
57313
57409
|
}
|
|
57314
57410
|
function getBackgroundRunnerCommand(runnerPath, cwd, runId, loaderInput = resolveTypeScriptLoader(), reportDirectory) {
|
|
57315
57411
|
const loader = normalizeLoaderInput(loaderInput);
|
|
57316
|
-
if (!loader) throw new Error(buildLoaderUnavailableMessage(
|
|
57412
|
+
if (!loader) throw new Error(buildLoaderUnavailableMessage(packageRoot()));
|
|
57317
57413
|
const memoryLimit = "--max-old-space-size=512";
|
|
57318
57414
|
const reportOn = !(getCrewEnv("PI_CREW_BG_REPORT_ON_FATAL") === "0" || getCrewEnv("PI_TEAMS_BG_REPORT_ON_FATAL") === "0");
|
|
57319
57415
|
const reportDir = reportDirectory ?? path69.dirname(runnerPath);
|
|
@@ -57343,6 +57439,10 @@ function getBackgroundRunnerCommand(runnerPath, cwd, runId, loaderInput = resolv
|
|
|
57343
57439
|
function buildBackgroundRunnerEnv(env) {
|
|
57344
57440
|
return { ...env, PI_CREW_ASYNC_RUN: "1" };
|
|
57345
57441
|
}
|
|
57442
|
+
function buildBrokerStdinLine(runId, socketPath, tasks) {
|
|
57443
|
+
return `${JSON.stringify({ v: 2, runId, socketPath, tasks })}
|
|
57444
|
+
`;
|
|
57445
|
+
}
|
|
57346
57446
|
async function spawnBackgroundTeamRun(manifest) {
|
|
57347
57447
|
const runnerPath = path69.join(packageRoot(), "src", "runtime", "background-runner.ts");
|
|
57348
57448
|
const logPath = path69.join(manifest.stateRoot, "background.log");
|
|
@@ -57354,7 +57454,7 @@ async function spawnBackgroundTeamRun(manifest) {
|
|
|
57354
57454
|
const childEnv = buildBackgroundRunnerEnv(peerDepDir ? { ...filteredEnv, [PEER_DEP_DIR_ENV]: peerDepDir } : filteredEnv);
|
|
57355
57455
|
const loader = resolveTypeScriptLoader();
|
|
57356
57456
|
if (!loader) {
|
|
57357
|
-
const message = buildLoaderUnavailableMessage(
|
|
57457
|
+
const message = buildLoaderUnavailableMessage(packageRoot());
|
|
57358
57458
|
await appendEventAsync(manifest.eventsPath, {
|
|
57359
57459
|
type: "async.failed",
|
|
57360
57460
|
runId: manifest.runId,
|
|
@@ -57369,11 +57469,49 @@ async function spawnBackgroundTeamRun(manifest) {
|
|
|
57369
57469
|
cwd: manifest.cwd,
|
|
57370
57470
|
detached: true,
|
|
57371
57471
|
setsid: true,
|
|
57372
|
-
|
|
57472
|
+
// F4 (2026-09-12 live battery): stdin is a PIPE — broker credentials
|
|
57473
|
+
// travel heap → pipe → heap right after spawn (see below). Previously
|
|
57474
|
+
// "ignore", which (together with the env allowlist and the missing
|
|
57475
|
+
// runner-side issuer) left EVERY async worker broker-less: ask/message
|
|
57476
|
+
// fell back to "proceed with best judgment" silently.
|
|
57477
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
57373
57478
|
env: childEnv,
|
|
57374
57479
|
windowsHide: true
|
|
57375
57480
|
};
|
|
57376
57481
|
const child = spawn6(process.execPath, command.args, spawnOpts);
|
|
57482
|
+
try {
|
|
57483
|
+
let line4;
|
|
57484
|
+
const { getActiveBrokerIssuer: getActiveBrokerIssuer2 } = await Promise.resolve().then(() => (init_broker_issuer(), broker_issuer_exports));
|
|
57485
|
+
const issuer = getActiveBrokerIssuer2();
|
|
57486
|
+
if (issuer) {
|
|
57487
|
+
const { loadRunManifestByIdAsync: loadRunManifestByIdAsync2 } = await Promise.resolve().then(() => (init_state_store(), state_store_exports));
|
|
57488
|
+
const loaded = await loadRunManifestByIdAsync2(manifest.cwd, manifest.runId);
|
|
57489
|
+
const runTasks = loaded?.tasks ?? [];
|
|
57490
|
+
const tasks = {};
|
|
57491
|
+
let socketPath;
|
|
57492
|
+
for (const task of runTasks) {
|
|
57493
|
+
if (!task?.id) continue;
|
|
57494
|
+
const creds = await issuer(manifest.runId, task.id);
|
|
57495
|
+
if (creds) {
|
|
57496
|
+
tasks[task.id] = creds.token;
|
|
57497
|
+
socketPath ??= creds.socketPath;
|
|
57498
|
+
}
|
|
57499
|
+
}
|
|
57500
|
+
if (socketPath && Object.keys(tasks).length > 0) {
|
|
57501
|
+
line4 = buildBrokerStdinLine(manifest.runId, socketPath, tasks);
|
|
57502
|
+
child.stdin?.write(line4);
|
|
57503
|
+
} else {
|
|
57504
|
+
child.stdin?.write("\n");
|
|
57505
|
+
}
|
|
57506
|
+
} else {
|
|
57507
|
+
child.stdin?.write("\n");
|
|
57508
|
+
}
|
|
57509
|
+
} catch {
|
|
57510
|
+
}
|
|
57511
|
+
try {
|
|
57512
|
+
child.stdin?.end();
|
|
57513
|
+
} catch {
|
|
57514
|
+
}
|
|
57377
57515
|
child.stdout?.destroy();
|
|
57378
57516
|
const STDERR_CAPTURE_LIMIT = 256 * 1024;
|
|
57379
57517
|
const stderrChunks = [];
|
|
@@ -67218,7 +67356,7 @@ __export(team_runner_exports, {
|
|
|
67218
67356
|
import { spawn as spawn7 } from "node:child_process";
|
|
67219
67357
|
import * as fs104 from "node:fs";
|
|
67220
67358
|
import * as path83 from "node:path";
|
|
67221
|
-
import { fileURLToPath as
|
|
67359
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
67222
67360
|
function startTeamRunHeartbeat(stateRoot, runId) {
|
|
67223
67361
|
const heartbeatPath = path83.join(stateRoot, "heartbeat.json");
|
|
67224
67362
|
const writeHeartbeat = () => {
|
|
@@ -67245,8 +67383,8 @@ function startTeamRunHeartbeat(stateRoot, runId) {
|
|
|
67245
67383
|
function perfScriptPath(scriptName) {
|
|
67246
67384
|
try {
|
|
67247
67385
|
const candidates = [
|
|
67248
|
-
|
|
67249
|
-
|
|
67386
|
+
fileURLToPath5(new URL(`../../scripts/${scriptName}`, import.meta.url)),
|
|
67387
|
+
fileURLToPath5(new URL(`../scripts/${scriptName}`, import.meta.url))
|
|
67250
67388
|
];
|
|
67251
67389
|
return candidates.find((p) => fs104.existsSync(p));
|
|
67252
67390
|
} catch {
|
|
@@ -71699,6 +71837,7 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
71699
71837
|
const executeWorkers = runtime.kind !== "scaffold";
|
|
71700
71838
|
if (executeWorkers && ctx.startForegroundRun) {
|
|
71701
71839
|
const fgDeadline = resolveRunDeadline(ctx, params, executedConfig);
|
|
71840
|
+
registerRunPromise(updatedManifest.runId);
|
|
71702
71841
|
ctx.onRunStarted?.(updatedManifest.runId);
|
|
71703
71842
|
const fgSignal = fgDeadline.signal;
|
|
71704
71843
|
let fgAbortListener;
|
|
@@ -71746,6 +71885,36 @@ ${dwfResult.manifest.summary ?? ""}`,
|
|
|
71746
71885
|
}, updatedManifest.runId);
|
|
71747
71886
|
try {
|
|
71748
71887
|
const completed = await waitForRun(updatedManifest.runId, resolvedCtx.cwd, { timeoutMs: fgDeadline.deadlineMs });
|
|
71888
|
+
if (completed.waiting) {
|
|
71889
|
+
const w = completed.waiting;
|
|
71890
|
+
const secondsLeft = Math.max(0, Math.round((w.deadline - Date.now()) / 1e3));
|
|
71891
|
+
const lines = [
|
|
71892
|
+
`pi-crew run WAITING for your answer: ${updatedManifest.runId}`,
|
|
71893
|
+
`Team: ${team.name} \xB7 Workflow: ${workflow.name}`,
|
|
71894
|
+
`Task ${w.taskId} (${w.questionId.substring(0, 8)}) parked on ask \u2014 ${secondsLeft}s until the deadline (then the worker proceeds with best judgment):`,
|
|
71895
|
+
"",
|
|
71896
|
+
`Q: ${w.question}`
|
|
71897
|
+
];
|
|
71898
|
+
if (w.options?.length) {
|
|
71899
|
+
lines.push("", "Options:", ...w.options.map((o, i) => ` ${i + 1}. ${o}`));
|
|
71900
|
+
}
|
|
71901
|
+
lines.push(
|
|
71902
|
+
"",
|
|
71903
|
+
"Answer now (run keeps executing):",
|
|
71904
|
+
` team action='respond' taskId='${w.taskId}' message='<your answer>'`,
|
|
71905
|
+
"then re-block until the run finishes:",
|
|
71906
|
+
` team action='wait' runId='${updatedManifest.runId}'`
|
|
71907
|
+
);
|
|
71908
|
+
return result(lines.join("\n"), {
|
|
71909
|
+
action: "run",
|
|
71910
|
+
status: "ok",
|
|
71911
|
+
runId: updatedManifest.runId,
|
|
71912
|
+
artifactsRoot: updatedManifest.artifactsRoot,
|
|
71913
|
+
taskId: w.taskId,
|
|
71914
|
+
questionId: w.questionId,
|
|
71915
|
+
waiting: true
|
|
71916
|
+
});
|
|
71917
|
+
}
|
|
71749
71918
|
if (completed.detached) {
|
|
71750
71919
|
return result(
|
|
71751
71920
|
[
|
|
@@ -75309,6 +75478,41 @@ var init_settings_overlay = __esm({
|
|
|
75309
75478
|
tab: "advanced",
|
|
75310
75479
|
description: "Remove /tmp/pi-crew-* directories after reconciliation (1h age threshold)."
|
|
75311
75480
|
},
|
|
75481
|
+
{
|
|
75482
|
+
id: "reliability.loopGuard",
|
|
75483
|
+
label: "Tool Loop Guard",
|
|
75484
|
+
type: "boolean",
|
|
75485
|
+
tab: "advanced",
|
|
75486
|
+
description: "Warn at 3 / block at 5 identical consecutive read-only tool results (ARCH-1)."
|
|
75487
|
+
},
|
|
75488
|
+
{
|
|
75489
|
+
id: "reliability.perWriteValidation",
|
|
75490
|
+
label: "Per-Write Validation",
|
|
75491
|
+
type: "boolean",
|
|
75492
|
+
tab: "advanced",
|
|
75493
|
+
description: "Validate state writes as they are made (default on)."
|
|
75494
|
+
},
|
|
75495
|
+
{
|
|
75496
|
+
id: "reliability.ambientStatusInjection",
|
|
75497
|
+
label: "Ambient Status Injection",
|
|
75498
|
+
type: "boolean",
|
|
75499
|
+
tab: "advanced",
|
|
75500
|
+
description: "Inject a compact crew-status note into context on every LLM call while runs are in-flight."
|
|
75501
|
+
},
|
|
75502
|
+
{
|
|
75503
|
+
id: "reliability.forcePreflight",
|
|
75504
|
+
label: "Force Preflight (audit override)",
|
|
75505
|
+
type: "boolean",
|
|
75506
|
+
tab: "advanced",
|
|
75507
|
+
description: "Skip usage-threshold BLOCK/WARN preflight. Default false (enforce). Audit/debug only."
|
|
75508
|
+
},
|
|
75509
|
+
{
|
|
75510
|
+
id: "reliability.scopeModels",
|
|
75511
|
+
label: "Scope Models (F7)",
|
|
75512
|
+
type: "boolean",
|
|
75513
|
+
tab: "advanced",
|
|
75514
|
+
description: "Enforce user enabledModels allowlist on subagent model choices. Default false."
|
|
75515
|
+
},
|
|
75312
75516
|
{
|
|
75313
75517
|
id: "telemetry.enabled",
|
|
75314
75518
|
label: "Telemetry",
|
|
@@ -75359,6 +75563,11 @@ var init_settings_overlay = __esm({
|
|
|
75359
75563
|
"reliability.autoRetry": false,
|
|
75360
75564
|
"reliability.autoRecover": false,
|
|
75361
75565
|
"reliability.cleanupOrphanedTempDirs": true,
|
|
75566
|
+
"reliability.loopGuard": true,
|
|
75567
|
+
"reliability.perWriteValidation": true,
|
|
75568
|
+
"reliability.ambientStatusInjection": true,
|
|
75569
|
+
"reliability.forcePreflight": false,
|
|
75570
|
+
"reliability.scopeModels": false,
|
|
75362
75571
|
"telemetry.enabled": false,
|
|
75363
75572
|
"notifications.enabled": false
|
|
75364
75573
|
};
|
|
@@ -78475,6 +78684,8 @@ function startForegroundWatchdog(opts) {
|
|
|
78475
78684
|
const maxMonitorMs = opts.maxMonitorMs ?? DEFAULT_MAX_MONITOR_MS;
|
|
78476
78685
|
const startTime = Date.now();
|
|
78477
78686
|
if (activeWatchdogs.has(runId)) return;
|
|
78687
|
+
let consecutiveHungNotices = 0;
|
|
78688
|
+
const HUNG_NOTICE_CAP = 2;
|
|
78478
78689
|
const check = () => {
|
|
78479
78690
|
if (Date.now() - startTime > maxMonitorMs) {
|
|
78480
78691
|
activeWatchdogs.delete(runId);
|
|
@@ -78502,13 +78713,23 @@ function startForegroundWatchdog(opts) {
|
|
|
78502
78713
|
const now = Date.now();
|
|
78503
78714
|
if (isLikelyOrphanedActiveRun(manifest, agents, now)) {
|
|
78504
78715
|
const detail = `status=${manifest.status}, updatedAt=${manifest.updatedAt}, agents=${agents.length}`;
|
|
78716
|
+
consecutiveHungNotices += 1;
|
|
78505
78717
|
try {
|
|
78506
|
-
|
|
78507
|
-
|
|
78508
|
-
|
|
78509
|
-
|
|
78718
|
+
if (consecutiveHungNotices <= HUNG_NOTICE_CAP) {
|
|
78719
|
+
pi.sendUserMessage(
|
|
78720
|
+
`pi-crew watchdog: run ${runId} appears hung (${detail}). Consider running team action='cancel' runId='${runId}' or team action='doctor'.`,
|
|
78721
|
+
{ deliverAs: "followUp" }
|
|
78722
|
+
);
|
|
78723
|
+
} else if (consecutiveHungNotices === HUNG_NOTICE_CAP + 1) {
|
|
78724
|
+
pi.sendUserMessage(
|
|
78725
|
+
`pi-crew watchdog: run ${runId} is still hung after ${consecutiveHungNotices} checks \u2014 going quiet now. Intervene via team action='cancel' runId='${runId}', team action='doctor', or leave it; a completion notice will still fire.`,
|
|
78726
|
+
{ deliverAs: "followUp" }
|
|
78727
|
+
);
|
|
78728
|
+
}
|
|
78510
78729
|
} catch {
|
|
78511
78730
|
}
|
|
78731
|
+
} else {
|
|
78732
|
+
consecutiveHungNotices = 0;
|
|
78512
78733
|
}
|
|
78513
78734
|
} catch {
|
|
78514
78735
|
}
|
|
@@ -82945,6 +83166,29 @@ function registerCrewMessageRenderers(pi) {
|
|
|
82945
83166
|
pi.registerMessageRenderer?.("crew:resume-directive", renderResumeDirective);
|
|
82946
83167
|
}
|
|
82947
83168
|
|
|
83169
|
+
// src/extension/post-init-skill-check.ts
|
|
83170
|
+
init_skill_instructions();
|
|
83171
|
+
async function runPostInitSkillCheck(cwd) {
|
|
83172
|
+
const result4 = renderSkillInstructions({ cwd, role: "executor" });
|
|
83173
|
+
const total = result4.names.length;
|
|
83174
|
+
const missingMatches = result4.block.match(/Skill '([^']+)' was selected but no SKILL\.md file was found/g);
|
|
83175
|
+
const missing = missingMatches ? missingMatches.map((m) => m.match(/'([^']+)'/)[1]) : [];
|
|
83176
|
+
const resolved = total - missing.length;
|
|
83177
|
+
let severity;
|
|
83178
|
+
let message;
|
|
83179
|
+
if (resolved === total) {
|
|
83180
|
+
severity = "ok";
|
|
83181
|
+
message = `All ${total} default skills resolved`;
|
|
83182
|
+
} else if (resolved === 0) {
|
|
83183
|
+
severity = "error";
|
|
83184
|
+
message = `0/${total} default skills resolved \u2014 likely bundle stale. Run \`npm run build:bundle\`.`;
|
|
83185
|
+
} else {
|
|
83186
|
+
severity = "warn";
|
|
83187
|
+
message = `${resolved}/${total} default skills resolved \u2014 degraded: ${missing.join(", ")}`;
|
|
83188
|
+
}
|
|
83189
|
+
return { total, resolved, missing, severity, message };
|
|
83190
|
+
}
|
|
83191
|
+
|
|
82948
83192
|
// src/extension/registration/command-registration.ts
|
|
82949
83193
|
init_config();
|
|
82950
83194
|
init_powerbar_publisher();
|
|
@@ -84551,7 +84795,6 @@ function startForegroundRunImpl(pi, ctx, extensionCtx, runner, runId) {
|
|
|
84551
84795
|
init_config();
|
|
84552
84796
|
import * as fs118 from "node:fs";
|
|
84553
84797
|
import * as path94 from "node:path";
|
|
84554
|
-
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
84555
84798
|
|
|
84556
84799
|
// src/runtime/per-write-validator.ts
|
|
84557
84800
|
import { readFileSync as readFileSync92 } from "node:fs";
|
|
@@ -84622,6 +84865,7 @@ function buildValidationBlocker(filePath, error) {
|
|
|
84622
84865
|
}
|
|
84623
84866
|
|
|
84624
84867
|
// src/extension/registration/hook-registration.ts
|
|
84868
|
+
init_paths();
|
|
84625
84869
|
init_safe_paths();
|
|
84626
84870
|
|
|
84627
84871
|
// src/extension/team-tool/destructive-gate.ts
|
|
@@ -84643,18 +84887,177 @@ function shouldBlockDestructiveTeamAction(action, input) {
|
|
|
84643
84887
|
return `Destructive action '${action}' requires confirm=true${action === "delete" ? " (or force=true to bypass reference checks)" : ""}.`;
|
|
84644
84888
|
}
|
|
84645
84889
|
|
|
84890
|
+
// src/extension/registration/tool-loop-guard.ts
|
|
84891
|
+
var LOOP_GUARD_WARN_AT = 3;
|
|
84892
|
+
var LOOP_GUARD_BLOCK_AT = 5;
|
|
84893
|
+
var LOOP_GUARD_EXEMPT = {
|
|
84894
|
+
team: true,
|
|
84895
|
+
crew_agent: true,
|
|
84896
|
+
Agent: true,
|
|
84897
|
+
get_subagent_result: true
|
|
84898
|
+
};
|
|
84899
|
+
var LOOP_GUARD_BLOCK_TOOLS = {
|
|
84900
|
+
read: true,
|
|
84901
|
+
grep: true,
|
|
84902
|
+
glob: true,
|
|
84903
|
+
find: true,
|
|
84904
|
+
ls: true
|
|
84905
|
+
};
|
|
84906
|
+
var WAIT_TOOL = "ask";
|
|
84907
|
+
var WAIT_GUARD_WARN_AT = 2;
|
|
84908
|
+
var WAIT_GUARD_BLOCK_AT = 2;
|
|
84909
|
+
var MAX_TRACKED_FINGERPRINTS = 512;
|
|
84910
|
+
var LOOP_GUARD_MARKER = "[REPEATED TOOL CALLS - STOP]";
|
|
84911
|
+
var WAIT_GUARD_MARKER = "[REPEATED WAIT TOOL - END TURN]";
|
|
84912
|
+
var LOOP_GUARD_WARNING = `
|
|
84913
|
+
${LOOP_GUARD_MARKER}
|
|
84914
|
+
|
|
84915
|
+
You have issued the exact same tool call with identical arguments ${LOOP_GUARD_WARN_AT} times in a row and received identical results. This is an infinite loop and you are making no progress.
|
|
84916
|
+
|
|
84917
|
+
STOP repeating this call. Instead:
|
|
84918
|
+
1. Reconsider what you are looking for \u2014 the result above already contains what this call can tell you.
|
|
84919
|
+
2. If you need different information, make a DIFFERENT call (different path, pattern, or tool).
|
|
84920
|
+
3. If the task is actually done, produce your final answer now instead of calling more tools.
|
|
84921
|
+
`;
|
|
84922
|
+
var WAIT_GUARD_WARNING = `
|
|
84923
|
+
${WAIT_GUARD_MARKER}
|
|
84924
|
+
|
|
84925
|
+
You have called \`ask\` ${WAIT_GUARD_WARN_AT} times in this turn. Its contract is to stop and wait \u2014 do not call it again in the same turn.
|
|
84926
|
+
|
|
84927
|
+
STOP calling tools that wait. Continue with what you can do, or produce your final answer; the reply arrives as a separate message later.
|
|
84928
|
+
`;
|
|
84929
|
+
function stableStringify(value) {
|
|
84930
|
+
return JSON.stringify(sortValue(value));
|
|
84931
|
+
}
|
|
84932
|
+
function sortValue(value) {
|
|
84933
|
+
if (Array.isArray(value)) return value.map(sortValue);
|
|
84934
|
+
if (value && typeof value === "object") {
|
|
84935
|
+
const record = value;
|
|
84936
|
+
const out = {};
|
|
84937
|
+
for (const key of Object.keys(record).sort()) {
|
|
84938
|
+
out[key] = sortValue(record[key]);
|
|
84939
|
+
}
|
|
84940
|
+
return out;
|
|
84941
|
+
}
|
|
84942
|
+
return value;
|
|
84943
|
+
}
|
|
84944
|
+
function fingerprint(tool, args) {
|
|
84945
|
+
return `${tool.toLowerCase()}:${stableStringify(args ?? null)}`;
|
|
84946
|
+
}
|
|
84947
|
+
function createLoopGuardState() {
|
|
84948
|
+
const runs = /* @__PURE__ */ new Map();
|
|
84949
|
+
let lastFingerprint = null;
|
|
84950
|
+
let waitRunCount = 0;
|
|
84951
|
+
function evictIfNeeded() {
|
|
84952
|
+
while (runs.size > MAX_TRACKED_FINGERPRINTS) {
|
|
84953
|
+
const oldest = runs.keys().next().value;
|
|
84954
|
+
if (oldest === void 0) break;
|
|
84955
|
+
runs.delete(oldest);
|
|
84956
|
+
}
|
|
84957
|
+
}
|
|
84958
|
+
function onToolResult(tool, args, output) {
|
|
84959
|
+
const toolLower = tool.toLowerCase();
|
|
84960
|
+
if (toolLower === WAIT_TOOL) {
|
|
84961
|
+
waitRunCount += 1;
|
|
84962
|
+
if (waitRunCount === WAIT_GUARD_WARN_AT) return [{ type: "text", text: WAIT_GUARD_WARNING }];
|
|
84963
|
+
return [];
|
|
84964
|
+
}
|
|
84965
|
+
waitRunCount = 0;
|
|
84966
|
+
if (LOOP_GUARD_EXEMPT[tool]) return [];
|
|
84967
|
+
const fp = fingerprint(tool, args);
|
|
84968
|
+
const outputKey = stableStringify(output);
|
|
84969
|
+
const state2 = runs.get(fp) ?? { runCount: 0, lastOutput: null };
|
|
84970
|
+
if (fp !== lastFingerprint) {
|
|
84971
|
+
state2.runCount = 0;
|
|
84972
|
+
}
|
|
84973
|
+
if (state2.lastOutput === outputKey) {
|
|
84974
|
+
state2.runCount += 1;
|
|
84975
|
+
} else {
|
|
84976
|
+
state2.runCount = 1;
|
|
84977
|
+
state2.lastOutput = outputKey;
|
|
84978
|
+
}
|
|
84979
|
+
runs.set(fp, state2);
|
|
84980
|
+
evictIfNeeded();
|
|
84981
|
+
lastFingerprint = fp;
|
|
84982
|
+
if (state2.runCount === LOOP_GUARD_WARN_AT) {
|
|
84983
|
+
return [{ type: "text", text: LOOP_GUARD_WARNING }];
|
|
84984
|
+
}
|
|
84985
|
+
return [];
|
|
84986
|
+
}
|
|
84987
|
+
function onToolCall(tool, args) {
|
|
84988
|
+
const toolLower = tool.toLowerCase();
|
|
84989
|
+
if (toolLower === WAIT_TOOL) {
|
|
84990
|
+
if (waitRunCount >= WAIT_GUARD_BLOCK_AT) {
|
|
84991
|
+
return {
|
|
84992
|
+
block: true,
|
|
84993
|
+
reason: `pi-crew loop guard: \`ask\` called ${waitRunCount} times this turn \u2014 its contract is to wait. End your turn; the reply arrives as a separate message.`
|
|
84994
|
+
};
|
|
84995
|
+
}
|
|
84996
|
+
return {};
|
|
84997
|
+
}
|
|
84998
|
+
if (LOOP_GUARD_EXEMPT[tool]) return {};
|
|
84999
|
+
if (!LOOP_GUARD_BLOCK_TOOLS[toolLower]) return {};
|
|
85000
|
+
const fp = fingerprint(tool, args);
|
|
85001
|
+
const state2 = runs.get(fp);
|
|
85002
|
+
if (state2 && state2.runCount >= LOOP_GUARD_BLOCK_AT) {
|
|
85003
|
+
return {
|
|
85004
|
+
block: true,
|
|
85005
|
+
reason: `pi-crew loop guard: this exact ${tool} call (identical arguments) has returned identical results ${state2.runCount} times in a row. Make a DIFFERENT call (different path, pattern, or tool), or produce your final answer.`
|
|
85006
|
+
};
|
|
85007
|
+
}
|
|
85008
|
+
return {};
|
|
85009
|
+
}
|
|
85010
|
+
function reset() {
|
|
85011
|
+
runs.clear();
|
|
85012
|
+
lastFingerprint = null;
|
|
85013
|
+
waitRunCount = 0;
|
|
85014
|
+
}
|
|
85015
|
+
return { onToolResult, onToolCall, reset };
|
|
85016
|
+
}
|
|
85017
|
+
function installToolLoopGuard(pi) {
|
|
85018
|
+
const state2 = createLoopGuardState();
|
|
85019
|
+
try {
|
|
85020
|
+
pi.on("tool_call", async (event) => {
|
|
85021
|
+
const verdict = state2.onToolCall(event.toolName, event.input);
|
|
85022
|
+
if (verdict.block) {
|
|
85023
|
+
return { block: true, reason: verdict.reason };
|
|
85024
|
+
}
|
|
85025
|
+
return void 0;
|
|
85026
|
+
});
|
|
85027
|
+
} catch {
|
|
85028
|
+
}
|
|
85029
|
+
try {
|
|
85030
|
+
pi.on("tool_result", (event) => {
|
|
85031
|
+
const appends = state2.onToolResult(event.toolName, event.input, event.content);
|
|
85032
|
+
if (appends.length === 0) return void 0;
|
|
85033
|
+
const existing = Array.isArray(event.content) ? event.content : [];
|
|
85034
|
+
return { content: [...existing, ...appends] };
|
|
85035
|
+
});
|
|
85036
|
+
} catch {
|
|
85037
|
+
}
|
|
85038
|
+
}
|
|
85039
|
+
|
|
84646
85040
|
// src/extension/registration/hook-registration.ts
|
|
84647
85041
|
function installPiHooks(pi, ctx) {
|
|
84648
85042
|
installResourcesDiscoverHook(pi, ctx);
|
|
84649
85043
|
installToolCallHook(pi, ctx);
|
|
84650
85044
|
installToolResultHook(pi, ctx);
|
|
85045
|
+
installToolLoopGuardIfEnabled(pi, ctx);
|
|
85046
|
+
}
|
|
85047
|
+
function installToolLoopGuardIfEnabled(pi, ctx) {
|
|
85048
|
+
try {
|
|
85049
|
+
const cwd = ctx.currentCtx?.cwd ?? process.cwd();
|
|
85050
|
+
if (loadConfig(cwd).config.reliability?.loopGuard === false) return;
|
|
85051
|
+
} catch {
|
|
85052
|
+
}
|
|
85053
|
+
installToolLoopGuard(pi);
|
|
84651
85054
|
}
|
|
84652
85055
|
function installResourcesDiscoverHook(pi, ctx) {
|
|
84653
85056
|
try {
|
|
84654
85057
|
pi.on("resources_discover", () => {
|
|
84655
85058
|
const sessionCwd = ctx.currentCtx?.cwd ?? process.cwd();
|
|
84656
85059
|
const skillDir = path94.resolve(sessionCwd, "skills");
|
|
84657
|
-
const extSkillDir = path94.
|
|
85060
|
+
const extSkillDir = path94.join(packageRoot(), "skills");
|
|
84658
85061
|
const paths = [];
|
|
84659
85062
|
if (fs118.existsSync(extSkillDir)) paths.push(extSkillDir);
|
|
84660
85063
|
if (skillDir !== extSkillDir && fs118.existsSync(skillDir)) {
|
|
@@ -85244,7 +85647,7 @@ function safeStringify(value) {
|
|
|
85244
85647
|
}
|
|
85245
85648
|
}
|
|
85246
85649
|
var WAIT_REQUEST_TIMEOUT_SEC_MAX = 3600;
|
|
85247
|
-
var WAIT_REQUEST_TIMEOUT_SEC_DEFAULT =
|
|
85650
|
+
var WAIT_REQUEST_TIMEOUT_SEC_DEFAULT = 480;
|
|
85248
85651
|
var WAIT_QUESTION_MAX_CHARS = 8192;
|
|
85249
85652
|
var WAIT_OPTIONS_MAX = 16;
|
|
85250
85653
|
var WAIT_OPTION_MAX_CHARS = 256;
|
|
@@ -85352,6 +85755,29 @@ function recordWaitPolicyRejection(manifest, taskId, method) {
|
|
|
85352
85755
|
);
|
|
85353
85756
|
}
|
|
85354
85757
|
|
|
85758
|
+
// src/runtime/broker/wait-push.ts
|
|
85759
|
+
init_state_store();
|
|
85760
|
+
async function pushWaitingToForegroundWaiter(params) {
|
|
85761
|
+
try {
|
|
85762
|
+
const { resolveRunPromise: resolveRunPromise2 } = await Promise.resolve().then(() => (init_run_tracker(), run_tracker_exports));
|
|
85763
|
+
const freshPark = loadRunManifestById(params.cwd ?? process.cwd(), params.runId);
|
|
85764
|
+
if (freshPark) {
|
|
85765
|
+
resolveRunPromise2(params.runId, {
|
|
85766
|
+
manifest: freshPark.manifest,
|
|
85767
|
+
tasks: freshPark.tasks,
|
|
85768
|
+
waiting: {
|
|
85769
|
+
taskId: params.taskId,
|
|
85770
|
+
questionId: params.questionId,
|
|
85771
|
+
question: params.question,
|
|
85772
|
+
deadline: params.deadline,
|
|
85773
|
+
...params.options ? { options: params.options } : {}
|
|
85774
|
+
}
|
|
85775
|
+
});
|
|
85776
|
+
}
|
|
85777
|
+
} catch {
|
|
85778
|
+
}
|
|
85779
|
+
}
|
|
85780
|
+
|
|
85355
85781
|
// src/runtime/broker/wait-status-cache.ts
|
|
85356
85782
|
init_state_store();
|
|
85357
85783
|
import * as fs123 from "node:fs";
|
|
@@ -86309,22 +86735,13 @@ var CrewBroker = class {
|
|
|
86309
86735
|
});
|
|
86310
86736
|
await pollUntilDone();
|
|
86311
86737
|
}
|
|
86312
|
-
/**
|
|
86313
|
-
*
|
|
86314
|
-
*
|
|
86315
|
-
*
|
|
86316
|
-
*
|
|
86317
|
-
*
|
|
86318
|
-
*
|
|
86319
|
-
* 2. Steering-file append — writes the steer body to
|
|
86320
|
-
* ${artifactsRoot}/steering/${taskId}.jsonl, the same file the
|
|
86321
|
-
* child's pollSteering() polls via PI_CREW_STEERING_FILE. This is
|
|
86322
|
-
* the durable fallback: even if the recipient child's broker connection is down, the
|
|
86323
|
-
* child picks up the steer on its next poll tick.
|
|
86324
|
-
*
|
|
86325
|
-
* A steering-file write failure does NOT fail the steer push — the
|
|
86326
|
-
* mailbox write (1) has already succeeded.
|
|
86327
|
-
*/
|
|
86738
|
+
/** Phase 3: steer.push — push steering message to a running worker.
|
|
86739
|
+
* Dual-write for durability: (1) mailbox append feeds the live broker
|
|
86740
|
+
* fanout AND persists to the inbox JSONL; (2) steering-file append writes
|
|
86741
|
+
* ${artifactsRoot}/steering/${taskId}.jsonl — the durable fallback the
|
|
86742
|
+
* child's pollSteering() polls via PI_CREW_STEERING_FILE even when its
|
|
86743
|
+
* broker connection is down. A steering-file write failure does NOT fail
|
|
86744
|
+
* the push — the mailbox write has already succeeded. */
|
|
86328
86745
|
async handleSteerPush(conn, id, params) {
|
|
86329
86746
|
if (conn.role !== "orchestrator") {
|
|
86330
86747
|
this.sendError(conn, id, "forbidden", "steer.push requires orchestrator role");
|
|
@@ -86879,6 +87296,15 @@ ${sanitizedText}
|
|
|
86879
87296
|
timeoutSec: clampSec,
|
|
86880
87297
|
clamped
|
|
86881
87298
|
});
|
|
87299
|
+
await pushWaitingToForegroundWaiter({
|
|
87300
|
+
cwd: this.options.cwd,
|
|
87301
|
+
runId,
|
|
87302
|
+
taskId,
|
|
87303
|
+
questionId,
|
|
87304
|
+
question: parsed.question,
|
|
87305
|
+
deadline,
|
|
87306
|
+
...parsed.options ? { options: parsed.options } : {}
|
|
87307
|
+
});
|
|
86882
87308
|
}
|
|
86883
87309
|
/** WP-2/R2: terminal report of the parked `ask` tool — flips the task
|
|
86884
87310
|
* waiting→running and clears the park coordination state. Scoped to
|
|
@@ -86989,7 +87415,9 @@ init_child_pi();
|
|
|
86989
87415
|
|
|
86990
87416
|
// src/runtime/detached-run-results.ts
|
|
86991
87417
|
init_state_store();
|
|
87418
|
+
init_internal_error();
|
|
86992
87419
|
init_process_status();
|
|
87420
|
+
var MAX_DELIVERY_ATTEMPTS = 3;
|
|
86993
87421
|
var detachedRuns = /* @__PURE__ */ new Map();
|
|
86994
87422
|
function hasDetachedRuns() {
|
|
86995
87423
|
return detachedRuns.size > 0;
|
|
@@ -87025,6 +87453,17 @@ function peekFinishedDetachedRunResults(options = {}) {
|
|
|
87025
87453
|
continue;
|
|
87026
87454
|
}
|
|
87027
87455
|
if (!isFinishedRunStatus(loaded.manifest.status)) continue;
|
|
87456
|
+
entry.attempts += 1;
|
|
87457
|
+
if (entry.attempts > MAX_DELIVERY_ATTEMPTS) {
|
|
87458
|
+
detachedRuns.delete(entry.runId);
|
|
87459
|
+
logInternalError(
|
|
87460
|
+
"detached-run-results.delivery-gave-up",
|
|
87461
|
+
new Error("delivery attempts exceeded"),
|
|
87462
|
+
`runId=${entry.runId} attempts=${entry.attempts} \u2014 dropped after ${MAX_DELIVERY_ATTEMPTS} failed sends`,
|
|
87463
|
+
"warn"
|
|
87464
|
+
);
|
|
87465
|
+
continue;
|
|
87466
|
+
}
|
|
87028
87467
|
ready.push({ runId: entry.runId, text: formatDetachedRunResult(loaded.manifest, loaded.tasks) });
|
|
87029
87468
|
}
|
|
87030
87469
|
return ready;
|
|
@@ -87500,10 +87939,10 @@ var CrewAgentPane = class {
|
|
|
87500
87939
|
this.lastTranscriptReadAt = Date.now();
|
|
87501
87940
|
}
|
|
87502
87941
|
const items = this.lastItems;
|
|
87503
|
-
const
|
|
87504
|
-
if (
|
|
87942
|
+
const fingerprint2 = this.bodyFingerprint(items, width);
|
|
87943
|
+
if (fingerprint2 !== this.bodyKey) {
|
|
87505
87944
|
this.cachedBody = this.buildBody(items, width);
|
|
87506
|
-
this.bodyKey =
|
|
87945
|
+
this.bodyKey = fingerprint2;
|
|
87507
87946
|
}
|
|
87508
87947
|
const body = this.cachedBody;
|
|
87509
87948
|
const header = this.headerLines(manifest, width);
|
|
@@ -90299,7 +90738,7 @@ function installCrossExtensionWiring(pi, ctx) {
|
|
|
90299
90738
|
}
|
|
90300
90739
|
|
|
90301
90740
|
// src/extension/register.ts
|
|
90302
|
-
function registerPiTeams(pi) {
|
|
90741
|
+
async function registerPiTeams(pi) {
|
|
90303
90742
|
resetTimings();
|
|
90304
90743
|
time("register:start");
|
|
90305
90744
|
installChildProcessAbortShield();
|
|
@@ -90349,6 +90788,12 @@ function registerPiTeams(pi) {
|
|
|
90349
90788
|
} catch (err2) {
|
|
90350
90789
|
console.warn("[pi-crew] crew-vibes initialization failed:", err2 instanceof Error ? err2.message : err2);
|
|
90351
90790
|
}
|
|
90791
|
+
const skillCheck = await runPostInitSkillCheck(process.cwd());
|
|
90792
|
+
if (skillCheck.severity === "error") {
|
|
90793
|
+
console.error(`[pi-crew] ${skillCheck.message}`);
|
|
90794
|
+
} else if (skillCheck.severity === "warn") {
|
|
90795
|
+
console.warn(`[pi-crew] ${skillCheck.message}`);
|
|
90796
|
+
}
|
|
90352
90797
|
}
|
|
90353
90798
|
|
|
90354
90799
|
// index.bundle.ts
|
|
@@ -90359,6 +90804,7 @@ function index_bundle_default(pi) {
|
|
|
90359
90804
|
export {
|
|
90360
90805
|
index_bundle_default as default,
|
|
90361
90806
|
registerPiTeams,
|
|
90807
|
+
runPostInitSkillCheck,
|
|
90362
90808
|
waitForRun
|
|
90363
90809
|
};
|
|
90364
90810
|
//# sourceMappingURL=index.mjs.map
|