billion-context-pi 0.1.69 → 0.1.70-pr.444.239

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/README.md CHANGED
@@ -79,6 +79,8 @@ That's it. The extension auto-loads on next Pi startup. No configuration needed
79
79
  > **Using another sub-agent extension?** billion-context-pi ships its own `acp_delegate` sub-agent tool (see below) at a fraction of the context cost (~600 tok vs ~7K tok/turn). Two delegation tools in one session only make the model's choice noisier, so pick one:
80
80
  > - **Use ACP's delegate** — remove the other extension: `pi remove npm:pi-subagents`
81
81
  > - **Keep your own sub-agent** — turn ACP's delegate off in `acp.json`: `{ "delegate": false }` (see *Using your own sub-agent instead* below)
82
+ >
83
+ > If you keep `pi-subagents` installed, billion-context-pi detects it at session start: a **project-level** install (`<cwd>/.pi/npm` or the project extensions dir) automatically stands `acp_delegate` down for that project — a reminder then tells you how to give pi-subagents' agents ACP compression via `/acp-subagents`. A **user-level-only** install (`~/.pi/npm`, user extensions dir) leaves `acp_delegate` active and logs a warning instead. Set `"delegate": { "forceEnable": true }` in `acp.json` to keep `acp_delegate` active regardless of detection.
82
84
 
83
85
  ## How it works
84
86
 
package/README.zh-CN.md CHANGED
@@ -78,6 +78,8 @@ pi install npm:billion-context-pi
78
78
  > **你另有子代理扩展?** billion-context-pi 自带 `acp_delegate` 子代理工具(见下文),上下文成本极低(~600 tok vs ~7K tok/轮)。同一会话里两套委派工具只会让模型的选择更混乱,二选一:
79
79
  > - **用 ACP 的 delegate** —— 卸载另一个扩展:`pi remove npm:pi-subagents`
80
80
  > - **保留你自己的子代理** —— 在 `acp.json` 里关掉 ACP 的 delegate:`{ "delegate": false }`(见下文*改用你自己的子代理*)
81
+ >
82
+ > 若保留已安装的 `pi-subagents`,billion-context-pi 会在会话启动时检测:**项目级**安装(`<cwd>/.pi/npm` 或项目内 extensions 目录)会自动停用该项目的 `acp_delegate`,并提醒你运行 `/acp-subagents` 让 pi-subagents 的子代理获得 ACP 压缩;仅**用户级**(全局)安装时只记一条警告日志,`acp_delegate` 保持启用。在 `acp.json` 中设置 `"delegate": { "forceEnable": true }` 可在检测到第三方子代理时仍强制保留 `acp_delegate`。
81
83
 
82
84
  ## 工作原理
83
85
 
package/dist/config.d.ts CHANGED
@@ -29,6 +29,13 @@ export interface DelegateConfig {
29
29
  /** Enable acp_delegate tools (delegate/wait/cancel) and their system-prompt
30
30
  * section. Default: true. Set `enabled: false` to skip registering them. */
31
31
  enabled?: boolean;
32
+ /** Keep acp_delegate active even when a third-party subagent extension
33
+ * (pi-subagents) is installed. Default: false — when pi-subagents is
34
+ * detected at session start, acp_delegate stands down (tools, fleet
35
+ * shortcut and system-prompt section skipped) to avoid two overlapping
36
+ * sub-agent systems, and a reminder points at /acp-subagents so the
37
+ * third-party agents can still get ACP compression tools (#415). */
38
+ forceEnable?: boolean;
32
39
  /** How delegate usage is reported back to the main session.
33
40
  * "separate" (default) — delegate tokens tracked in a separate accumulator;
34
41
  * main session totals stay clean, delegate usage shows as its own block in
@@ -89,6 +96,9 @@ export interface DelegateConfig {
89
96
  * corresponding timeout/watchdog is disabled. */
90
97
  export interface DelegatePolicy {
91
98
  enabled: boolean;
99
+ /** Resolved delegate.forceEnable (default false): keep acp_delegate even
100
+ * when a third-party subagent extension (pi-subagents) is installed (#415). */
101
+ forceEnable: boolean;
92
102
  displayUsage: "merged" | "separate";
93
103
  maxDepth: number;
94
104
  syncTimeoutMs: number | null;
package/dist/index.js CHANGED
@@ -4532,6 +4532,7 @@ var debug = {
4532
4532
  var DEFAULT_FLEET_SHORTCUT = "ctrl+alt+d";
4533
4533
  var DEFAULT_DELEGATE_POLICY = {
4534
4534
  enabled: true,
4535
+ forceEnable: false,
4535
4536
  displayUsage: "separate",
4536
4537
  maxDepth: 2,
4537
4538
  syncTimeoutMs: 5 * 6e4,
@@ -4565,19 +4566,28 @@ function resolveDelegate(adapter) {
4565
4566
  DEFAULT_DELEGATE_POLICY.asyncTimeoutMs
4566
4567
  );
4567
4568
  const maxConcurrent = resolveMaxConcurrent(process.env.PI_ACP_DELEGATE_MAX_CONCURRENT, cfg.maxConcurrent);
4569
+ const forceEnable = resolveForceEnable(process.env.PI_ACP_DELEGATE_FORCE_ENABLE, cfg.forceEnable);
4568
4570
  if (idleMs === null) {
4569
4571
  logWarn("config", {
4570
4572
  event: "delegate-idle-watchdog-disabled",
4571
4573
  hint: "no-output watchdog is off; hung async runs must be cancelled manually via acp_delegate_cancel"
4572
4574
  });
4573
4575
  }
4574
- return { enabled, displayUsage, maxDepth, syncTimeoutMs, idleMs, asyncTimeoutMs, maxConcurrent, thinkingLevel: cfg.thinkingLevel, agents: cfg.agents, notifyIfRead: cfg.notifyIfRead ?? "skip", fleetShortcut: resolveFleetShortcut(cfg.fleetShortcut) };
4576
+ return { enabled, forceEnable, displayUsage, maxDepth, syncTimeoutMs, idleMs, asyncTimeoutMs, maxConcurrent, thinkingLevel: cfg.thinkingLevel, agents: cfg.agents, notifyIfRead: cfg.notifyIfRead ?? "skip", fleetShortcut: resolveFleetShortcut(cfg.fleetShortcut) };
4575
4577
  }
4576
4578
  function resolveFleetShortcut(value) {
4577
4579
  if (typeof value === "string") return value;
4578
4580
  if (value !== void 0) logWarn("config", { event: "delegate-config-invalid", field: "fleetShortcut", value: String(value), fallback: DEFAULT_FLEET_SHORTCUT });
4579
4581
  return DEFAULT_FLEET_SHORTCUT;
4580
4582
  }
4583
+ function resolveForceEnable(envValue, cfgValue) {
4584
+ if (envValue === "true") return true;
4585
+ if (envValue === "false") return false;
4586
+ if (envValue !== void 0) {
4587
+ logWarn("config", { event: "delegate-config-invalid", field: "forceEnable", value: envValue, fallback: cfgValue === true });
4588
+ }
4589
+ return cfgValue === true;
4590
+ }
4581
4591
  function resolveMaxDepth(value) {
4582
4592
  if (value === void 0) return DEFAULT_DELEGATE_POLICY.maxDepth;
4583
4593
  const n = Number(value);
@@ -4992,7 +5002,9 @@ function coreOutToAgentMessages(coreOut, originalById) {
4992
5002
  const emittedSplit = /* @__PURE__ */ new Set();
4993
5003
  const kernelTextByCallId = /* @__PURE__ */ new Map();
4994
5004
  for (const core of coreOut) {
4995
- if (core.toolCallId && core.text) kernelTextByCallId.set(core.toolCallId, core.text);
5005
+ if (core.contentType === "tool-call" && core.toolCallId && core.text) {
5006
+ kernelTextByCallId.set(core.toolCallId, core.text);
5007
+ }
4996
5008
  }
4997
5009
  for (const core of coreOut) {
4998
5010
  if (core.id.startsWith("acp_summary_")) continue;
@@ -6377,6 +6389,7 @@ function createRuntime(adapter) {
6377
6389
  }
6378
6390
  let refused = false;
6379
6391
  let refusalMessage = null;
6392
+ let delegateStoodDown = false;
6380
6393
  return { core, store, get refused() {
6381
6394
  return refused;
6382
6395
  }, set refused(v2) {
@@ -6385,6 +6398,10 @@ function createRuntime(adapter) {
6385
6398
  return refusalMessage;
6386
6399
  }, set refusalMessage(v2) {
6387
6400
  refusalMessage = v2;
6401
+ }, get delegateStoodDown() {
6402
+ return delegateStoodDown;
6403
+ }, set delegateStoodDown(v2) {
6404
+ delegateStoodDown = v2;
6388
6405
  }, get adapter() {
6389
6406
  return adapterRef;
6390
6407
  }, setAdapter: (a) => {
@@ -11600,7 +11617,7 @@ function resolveActivePack(adapter, cwd, provider, modelId, resolver) {
11600
11617
  }
11601
11618
  var HOST_VERSION = (() => {
11602
11619
  try {
11603
- return "0.1.69";
11620
+ return "0.1.70";
11604
11621
  } catch {
11605
11622
  return "dev";
11606
11623
  }
@@ -17895,6 +17912,11 @@ import * as fs6 from "fs";
17895
17912
  import * as os2 from "os";
17896
17913
  import * as path11 from "path";
17897
17914
  var ACP_TOOLS3 = ["compress", "decompress", "search_context", "acp_status"];
17915
+ var DELEGATE_STAND_DOWN_MESSAGE = [
17916
+ "[billion-context-pi] pi-subagents detected \u2014 acp_delegate has been automatically disabled to avoid two overlapping sub-agent systems.",
17917
+ "pi-subagents' agents do NOT get ACP context compression by default. Run /acp-subagents to inject compress/decompress/search_context/acp_status into its agent overrides.",
17918
+ 'To keep acp_delegate despite pi-subagents being installed, set "delegate": { "forceEnable": true } in acp.json.'
17919
+ ].join("\n");
17898
17920
  function resolveAgentDir() {
17899
17921
  const envDir = process.env.PI_CODING_AGENT_DIR;
17900
17922
  if (envDir) {
@@ -17925,38 +17947,39 @@ function parseFrontmatterTools(content) {
17925
17947
  if (!name) return null;
17926
17948
  return tools ? { name, tools } : { name };
17927
17949
  }
17928
- function findPiSubagentsInstall(agentDir, cwd) {
17929
- const candidates = [
17930
- path11.join(agentDir, "npm", "node_modules", "pi-subagents"),
17931
- path11.join(cwd, CONFIG_DIR_NAME2, "npm", "node_modules", "pi-subagents")
17932
- ];
17933
- const extensionRoots = [
17934
- path11.join(agentDir, "extensions"),
17935
- path11.join(cwd, CONFIG_DIR_NAME2, "extensions")
17936
- ];
17937
- for (const dir of candidates) {
17938
- if (fs6.existsSync(path11.join(dir, "package.json"))) return dir;
17950
+ function npmInstallAt(base) {
17951
+ const dir = path11.join(base, "npm", "node_modules", "pi-subagents");
17952
+ return fs6.existsSync(path11.join(dir, "package.json")) ? dir : null;
17953
+ }
17954
+ function extensionInstallUnder(root) {
17955
+ let entries;
17956
+ try {
17957
+ entries = fs6.readdirSync(root, { withFileTypes: true });
17958
+ } catch {
17959
+ return null;
17939
17960
  }
17940
- for (const root of extensionRoots) {
17941
- let entries;
17961
+ for (const entry of entries) {
17962
+ if (!entry.isDirectory()) continue;
17963
+ const pkgPath = path11.join(root, entry.name, "package.json");
17942
17964
  try {
17943
- entries = fs6.readdirSync(root, { withFileTypes: true });
17944
- } catch {
17945
- continue;
17946
- }
17947
- for (const entry of entries) {
17948
- if (!entry.isDirectory()) continue;
17949
- const pkgPath = path11.join(root, entry.name, "package.json");
17950
- try {
17951
- if (JSON.parse(fs6.readFileSync(pkgPath, "utf-8")).name === "pi-subagents") {
17952
- return path11.join(root, entry.name);
17953
- }
17954
- } catch {
17965
+ if (JSON.parse(fs6.readFileSync(pkgPath, "utf-8")).name === "pi-subagents") {
17966
+ return path11.join(root, entry.name);
17955
17967
  }
17968
+ } catch {
17956
17969
  }
17957
17970
  }
17958
17971
  return null;
17959
17972
  }
17973
+ function findPiSubagentsInstall(agentDir, cwd) {
17974
+ return npmInstallAt(agentDir) ?? npmInstallAt(path11.join(cwd, CONFIG_DIR_NAME2)) ?? extensionInstallUnder(path11.join(agentDir, "extensions")) ?? extensionInstallUnder(path11.join(cwd, CONFIG_DIR_NAME2, "extensions"));
17975
+ }
17976
+ function findPiSubagentsInstalls(agentDir, cwd) {
17977
+ const pick = (paths) => paths.filter((p) => p !== null);
17978
+ return {
17979
+ user: pick([npmInstallAt(agentDir), extensionInstallUnder(path11.join(agentDir, "extensions"))]),
17980
+ project: pick([npmInstallAt(path11.join(cwd, CONFIG_DIR_NAME2)), extensionInstallUnder(path11.join(cwd, CONFIG_DIR_NAME2, "extensions"))])
17981
+ };
17982
+ }
17960
17983
  function discoverBuiltinAgents(installDir) {
17961
17984
  const agentsDir = path11.join(installDir, "agents");
17962
17985
  let entries;
@@ -18254,7 +18277,7 @@ async function statusReport(runtime, ctx) {
18254
18277
  const sentTokens = estimateTokens(coreMessages, coveredIds, imageTokens) + systemPromptTokens;
18255
18278
  const viewSentTokens = adjustedTokenCount(runtime.core, coreMessages, state, config, sentTokens, imageTokens, systemPromptTokens);
18256
18279
  const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: anchorStale ? viewSentTokens : Math.max(viewSentTokens, realUsage?.tokens ?? 0) });
18257
- const versionStr = "0.1.69" ? `billion-context-pi@${"0.1.69"} \xB7 pack: ${resolveSurfaceMeta(runtime.adapter, ctx?.cwd ?? process.cwd(), ctx?.model?.provider, ctx?.model?.id).pack}` : void 0;
18280
+ const versionStr = "0.1.70" ? `billion-context-pi@${"0.1.70"} \xB7 pack: ${resolveSurfaceMeta(runtime.adapter, ctx?.cwd ?? process.cwd(), ctx?.model?.provider, ctx?.model?.id).pack}` : void 0;
18258
18281
  let text = buildStatusPanel({
18259
18282
  version: versionStr,
18260
18283
  tokenCount: sessionTokens,
@@ -18866,7 +18889,7 @@ async function autoInstallLatest(latest, extDirOverride) {
18866
18889
  "--no-fund",
18867
18890
  "--no-save"
18868
18891
  ];
18869
- const prevVersion = (await readPackageJson(join15(extDir, "package.json")))?.version ?? "0.1.69";
18892
+ const prevVersion = (await readPackageJson(join15(extDir, "package.json")))?.version ?? "0.1.70";
18870
18893
  const { code, stderr } = await runNpmImpl(installArgs(latest), { cwd: npmDir, timeout: 6e4 });
18871
18894
  if (code !== 0) {
18872
18895
  logWarn("update", {
@@ -18884,7 +18907,7 @@ async function autoInstallLatest(latest, extDirOverride) {
18884
18907
  }
18885
18908
  const verify = await verifyInstall(npmDir, latest);
18886
18909
  if (!verify.ok) {
18887
- const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.1.69";
18910
+ const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.1.70";
18888
18911
  logWarn("update", { event: "auto-install-verify-failed", latest, reason: verify.reason, rollbackTo });
18889
18912
  const rb = await runNpmImpl(installArgs(rollbackTo), { cwd: npmDir, timeout: 6e4 });
18890
18913
  logInfo("update", { event: "rollback", from: latest, to: rollbackTo, ok: rb.code === 0 });
@@ -18959,7 +18982,7 @@ async function checkForUpdate(autoUpdate, notify) {
18959
18982
  }
18960
18983
  const latest = await fetchLatestVersion(tag);
18961
18984
  if (!latest) return;
18962
- const current = runtimeVersion ?? "0.1.69";
18985
+ const current = runtimeVersion ?? "0.1.70";
18963
18986
  const hasUpdate = isVersionNewer(latest, current);
18964
18987
  debug.event("update-check", {
18965
18988
  current,
@@ -19125,6 +19148,7 @@ function wireDelegateReadTracking(pi) {
19125
19148
  }
19126
19149
  function wireSessionLifecycle(pi, runtime, standDownIfProxied) {
19127
19150
  let ompWarned = false;
19151
+ let subagentStandDownWarned = false;
19128
19152
  pi.on("session_start", async (_event, ctx) => {
19129
19153
  if (isUnsupportedHost(ctx.sessionManager)) {
19130
19154
  runtime.refused = true;
@@ -19147,7 +19171,8 @@ function wireSessionLifecycle(pi, runtime, standDownIfProxied) {
19147
19171
  setDelegatePolicy(DEFAULT_DELEGATE_POLICY);
19148
19172
  const sid = ctx.sessionManager.getSessionId();
19149
19173
  const modelInfo = ctx.model;
19150
- logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.69" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
19174
+ logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.70" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
19175
+ let delegateStoodDown = false;
19151
19176
  try {
19152
19177
  await runtime.reloadConfig(ctx.cwd);
19153
19178
  const delegateCfg = resolveDelegate(runtime.adapter);
@@ -19155,9 +19180,24 @@ function wireSessionLifecycle(pi, runtime, standDownIfProxied) {
19155
19180
  setDelegatePolicy(delegateCfg);
19156
19181
  setDelegateDefaults({ thinkingLevel: delegateCfg.thinkingLevel, agents: delegateCfg.agents });
19157
19182
  setDelegateNotifyIfRead(delegateCfg.notifyIfRead);
19183
+ if (delegateCfg.enabled && !delegateCfg.forceEnable) {
19184
+ const scopes = findPiSubagentsInstalls(resolveAgentDir(), ctx.cwd ?? process.cwd());
19185
+ if (scopes.project[0] !== void 0) {
19186
+ delegateStoodDown = true;
19187
+ logWarn("delegate", { event: "delegate-auto-disabled", sid, install: scopes.project[0], scope: "project", hint: "run /acp-subagents to give its agents ACP compression tools; delegate.forceEnable=true keeps acp_delegate" });
19188
+ if (!subagentStandDownWarned) {
19189
+ subagentStandDownWarned = true;
19190
+ if (ctx.hasUI) ctx.ui.notify(DELEGATE_STAND_DOWN_MESSAGE, "warning");
19191
+ else console.error(DELEGATE_STAND_DOWN_MESSAGE);
19192
+ }
19193
+ } else if (scopes.user[0] !== void 0) {
19194
+ logWarn("delegate", { event: "delegate-user-scope-detected", sid, install: scopes.user[0], action: "warn-only", hint: "user-level pi-subagents does not disable acp_delegate; run /acp-subagents to give its agents ACP compression tools" });
19195
+ }
19196
+ }
19158
19197
  } catch (e) {
19159
19198
  logThrow("config", e, { sid, phase: "session_start" });
19160
19199
  }
19200
+ runtime.delegateStoodDown = delegateStoodDown;
19161
19201
  try {
19162
19202
  runtime.setPrompts(resolvePrompts(runtime.adapter.prompts, { acknowledgeRisk: runtime.adapter.acknowledgePromptsRisk === true }));
19163
19203
  } catch (e) {
@@ -19165,7 +19205,7 @@ function wireSessionLifecycle(pi, runtime, standDownIfProxied) {
19165
19205
  runtime.setPrompts(defaultPrompts);
19166
19206
  }
19167
19207
  const delegatePolicy2 = resolveDelegate(runtime.adapter);
19168
- if (delegatePolicy2.enabled) {
19208
+ if (delegatePolicy2.enabled && !runtime.delegateStoodDown) {
19169
19209
  pi.registerTool(makeDelegateTool(pi));
19170
19210
  pi.registerTool(makeDelegateWaitTool(pi));
19171
19211
  pi.registerTool(makeDelegateCancelTool(pi));
@@ -19412,7 +19452,7 @@ function wireSystemPrompt(pi, runtime) {
19412
19452
  }
19413
19453
  runtime.setPrompts(defaultPrompts);
19414
19454
  }
19415
- const delegate = resolveDelegate(runtime.adapter).enabled;
19455
+ const delegate = resolveDelegate(runtime.adapter).enabled && !runtime.delegateStoodDown;
19416
19456
  const acp = buildAcpSystemPrompt(runtime.prompts, merged.promptSections);
19417
19457
  const delegateText = merged.delegatePrompt !== void 0 ? merged.delegatePrompt : ACP_DELEGATE_PROMPT;
19418
19458
  const prompt = delegate && delegateText !== null ? `${acp}