@dyyz1993/pi-coding-agent 0.69.17 → 0.69.23

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.
Files changed (113) hide show
  1. package/dist/core/agent-session.d.ts +12 -1
  2. package/dist/core/agent-session.d.ts.map +1 -1
  3. package/dist/core/agent-session.js +208 -0
  4. package/dist/core/agent-session.js.map +1 -1
  5. package/dist/core/extensions/client-channel.d.ts +61 -0
  6. package/dist/core/extensions/client-channel.d.ts.map +1 -0
  7. package/dist/core/extensions/client-channel.js +67 -0
  8. package/dist/core/extensions/client-channel.js.map +1 -0
  9. package/dist/core/extensions/index.d.ts +3 -2
  10. package/dist/core/extensions/index.d.ts.map +1 -1
  11. package/dist/core/extensions/index.js +1 -0
  12. package/dist/core/extensions/index.js.map +1 -1
  13. package/dist/core/extensions/loader.d.ts.map +1 -1
  14. package/dist/core/extensions/loader.js +13 -0
  15. package/dist/core/extensions/loader.js.map +1 -1
  16. package/dist/core/extensions/runner.d.ts +1 -0
  17. package/dist/core/extensions/runner.d.ts.map +1 -1
  18. package/dist/core/extensions/runner.js +8 -0
  19. package/dist/core/extensions/runner.js.map +1 -1
  20. package/dist/core/extensions/types.d.ts +49 -0
  21. package/dist/core/extensions/types.d.ts.map +1 -1
  22. package/dist/core/extensions/types.js.map +1 -1
  23. package/dist/core/include-resolver.d.ts +18 -0
  24. package/dist/core/include-resolver.d.ts.map +1 -0
  25. package/dist/core/include-resolver.js +304 -0
  26. package/dist/core/include-resolver.js.map +1 -0
  27. package/dist/core/resource-loader.d.ts.map +1 -1
  28. package/dist/core/resource-loader.js +17 -4
  29. package/dist/core/resource-loader.js.map +1 -1
  30. package/dist/core/session-manager.d.ts +8 -4
  31. package/dist/core/session-manager.d.ts.map +1 -1
  32. package/dist/core/session-manager.js +29 -6
  33. package/dist/core/session-manager.js.map +1 -1
  34. package/dist/core/storage.d.ts +24 -0
  35. package/dist/core/storage.d.ts.map +1 -0
  36. package/dist/core/storage.js +55 -0
  37. package/dist/core/storage.js.map +1 -0
  38. package/dist/core/tools/path-security.d.ts +15 -0
  39. package/dist/core/tools/path-security.d.ts.map +1 -0
  40. package/dist/core/tools/path-security.js +76 -0
  41. package/dist/core/tools/path-security.js.map +1 -0
  42. package/dist/core/tools/strip-markdown.d.ts +2 -0
  43. package/dist/core/tools/strip-markdown.d.ts.map +1 -0
  44. package/dist/core/tools/strip-markdown.js +8 -0
  45. package/dist/core/tools/strip-markdown.js.map +1 -0
  46. package/dist/index.d.ts +5 -4
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +3 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  51. package/dist/modes/interactive/interactive-mode.js +1 -0
  52. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  53. package/dist/modes/rpc/rpc-client-types.d.ts +1 -0
  54. package/dist/modes/rpc/rpc-client-types.d.ts.map +1 -1
  55. package/dist/modes/rpc/rpc-client-types.js.map +1 -1
  56. package/dist/modes/rpc/rpc-client.d.ts +1 -0
  57. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  58. package/dist/modes/rpc/rpc-client.js +3 -0
  59. package/dist/modes/rpc/rpc-client.js.map +1 -1
  60. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  61. package/dist/modes/rpc/rpc-mode.js +5 -0
  62. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  63. package/dist/modes/rpc/rpc-types.d.ts +9 -0
  64. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  65. package/dist/modes/rpc/rpc-types.js.map +1 -1
  66. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  67. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  68. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  69. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  70. package/examples/extensions/with-deps/package-lock.json +2 -2
  71. package/examples/extensions/with-deps/package.json +1 -1
  72. package/package.json +9 -5
  73. package/dist/rules-engine/cache.d.ts +0 -4
  74. package/dist/rules-engine/cache.d.ts.map +0 -1
  75. package/dist/rules-engine/cache.js +0 -32
  76. package/dist/rules-engine/cache.js.map +0 -1
  77. package/dist/rules-engine/config.d.ts +0 -8
  78. package/dist/rules-engine/config.d.ts.map +0 -1
  79. package/dist/rules-engine/config.js +0 -56
  80. package/dist/rules-engine/config.js.map +0 -1
  81. package/dist/rules-engine/index.d.ts +0 -10
  82. package/dist/rules-engine/index.d.ts.map +0 -1
  83. package/dist/rules-engine/index.js +0 -393
  84. package/dist/rules-engine/index.js.map +0 -1
  85. package/dist/rules-engine/injector.d.ts +0 -5
  86. package/dist/rules-engine/injector.d.ts.map +0 -1
  87. package/dist/rules-engine/injector.js +0 -57
  88. package/dist/rules-engine/injector.js.map +0 -1
  89. package/dist/rules-engine/loader.d.ts +0 -8
  90. package/dist/rules-engine/loader.d.ts.map +0 -1
  91. package/dist/rules-engine/loader.js +0 -190
  92. package/dist/rules-engine/loader.js.map +0 -1
  93. package/dist/rules-engine/matcher.d.ts +0 -3
  94. package/dist/rules-engine/matcher.d.ts.map +0 -1
  95. package/dist/rules-engine/matcher.js +0 -48
  96. package/dist/rules-engine/matcher.js.map +0 -1
  97. package/dist/rules-engine/types.d.ts +0 -150
  98. package/dist/rules-engine/types.d.ts.map +0 -1
  99. package/dist/rules-engine/types.js +0 -2
  100. package/dist/rules-engine/types.js.map +0 -1
  101. package/examples/extensions/auto-session-title.ts +0 -82
  102. package/examples/extensions/file-snapshot.ts +0 -417
  103. package/examples/extensions/subagent/README.md +0 -172
  104. package/examples/extensions/subagent/agents/planner.md +0 -37
  105. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  106. package/examples/extensions/subagent/agents/scout.md +0 -50
  107. package/examples/extensions/subagent/agents/worker.md +0 -24
  108. package/examples/extensions/subagent/agents.ts +0 -126
  109. package/examples/extensions/subagent/index.ts +0 -987
  110. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  111. package/examples/extensions/subagent/prompts/implement.md +0 -10
  112. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
  113. package/examples/extensions/subagent-v2/index.ts +0 -849
@@ -12,10 +12,13 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
+ import { randomUUID } from "node:crypto";
15
16
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
17
  import { basename, dirname, join, resolve } from "node:path";
17
18
  import { Agent, } from "@dyyz1993/pi-agent-core";
18
19
  import { complete, isContextOverflow, modelsAreEqual, resetApiProviders, supportsXhigh } from "@dyyz1993/pi-ai";
20
+ import { Compile } from "typebox/compile";
21
+ import { Value } from "typebox/value";
19
22
  import { getDocsPath } from "../config.js";
20
23
  import { theme } from "../modes/interactive/theme/theme.js";
21
24
  import { stripFrontmatter } from "../utils/frontmatter.js";
@@ -33,6 +36,7 @@ import { createSyntheticSourceInfo } from "./source-info.js";
33
36
  import { buildSystemPrompt } from "./system-prompt.js";
34
37
  import { createLocalBashOperations } from "./tools/bash.js";
35
38
  import { createAllToolDefinitions, createTool } from "./tools/index.js";
39
+ import { stripMarkdownCodeBlock } from "./tools/strip-markdown.js";
36
40
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
37
41
  /**
38
42
  * Parse a skill block from message text.
@@ -106,6 +110,8 @@ export class AgentSession {
106
110
  _extensionErrorListener;
107
111
  _extensionErrorUnsubscriber;
108
112
  _registerChannel;
113
+ _sessionAbortController = new AbortController();
114
+ _backgroundTasks = new Set();
109
115
  // Model registry for API key resolution
110
116
  _modelRegistry;
111
117
  // Tool registry for extension getTools/setTools
@@ -494,6 +500,12 @@ export class AgentSession {
494
500
  */
495
501
  dispose() {
496
502
  this._extensionRunner.invalidate("This extension instance is stale after session replacement or reload. Use the provided replacement-session context instead.");
503
+ this._sessionAbortController.abort();
504
+ for (const task of this._backgroundTasks) {
505
+ task.cancel();
506
+ }
507
+ void Promise.allSettled([...this._backgroundTasks].map((t) => t.promise));
508
+ this._backgroundTasks.clear();
497
509
  this._disconnectFromAgent();
498
510
  this._eventListeners = [];
499
511
  }
@@ -516,6 +528,10 @@ export class AgentSession {
516
528
  get isStreaming() {
517
529
  return this.agent.state.isStreaming;
518
530
  }
531
+ /** Signal that aborts on session shutdown */
532
+ get sessionSignal() {
533
+ return this._sessionAbortController.signal;
534
+ }
519
535
  /** Current effective system prompt (includes any per-turn extension modifications) */
520
536
  get systemPrompt() {
521
537
  return this.agent.state.systemPrompt;
@@ -1774,10 +1790,14 @@ export class AgentSession {
1774
1790
  throw new Error(`registerChannel("${name}") is only available in RPC mode`);
1775
1791
  }),
1776
1792
  callLLM: (options) => this.callLLM(options),
1793
+ callLLMStructured: (opts) => this.callLLMStructured(opts),
1794
+ forkAgent: (prompt, options) => this.forkAgent(prompt, options),
1795
+ background: (fn) => this.background(fn),
1777
1796
  }, {
1778
1797
  getModel: () => this.model,
1779
1798
  isIdle: () => !this.isStreaming,
1780
1799
  getSignal: () => this.agent.signal,
1800
+ getSessionSignal: () => this._sessionAbortController.signal,
1781
1801
  abort: () => this.abort(),
1782
1802
  hasPendingMessages: () => this.pendingMessageCount > 0,
1783
1803
  shutdown: () => {
@@ -1912,6 +1932,186 @@ export class AgentSession {
1912
1932
  }
1913
1933
  return resultText;
1914
1934
  }
1935
+ async callLLMStructured(options) {
1936
+ const maxRetries = options.maxRetries ?? 0;
1937
+ let lastError;
1938
+ const schemaJson = JSON.stringify(options.schema);
1939
+ const structuredSystemPrompt = (options.systemPrompt ?? "") +
1940
+ `\n\nRespond with valid JSON matching this schema:\n${schemaJson}\n\nRespond with JSON only, no markdown.`;
1941
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1942
+ const messages = attempt === 0
1943
+ ? options.messages
1944
+ : [
1945
+ ...options.messages,
1946
+ { role: "assistant", content: lastError?.raw ?? "" },
1947
+ {
1948
+ role: "user",
1949
+ content: `Your previous response was invalid: ${lastError?.message}. Please respond with valid JSON matching the schema.`,
1950
+ },
1951
+ ];
1952
+ const raw = await this.callLLM({
1953
+ ...options,
1954
+ systemPrompt: structuredSystemPrompt,
1955
+ messages,
1956
+ });
1957
+ try {
1958
+ const cleaned = stripMarkdownCodeBlock(raw);
1959
+ const parsed = JSON.parse(cleaned);
1960
+ const check = Compile(options.schema);
1961
+ const coerced = Value.Convert(options.schema, parsed);
1962
+ if (!check.Check(coerced)) {
1963
+ const errors = check
1964
+ .Errors(coerced)
1965
+ .map((e) => `${e.instancePath}: ${e.message}`)
1966
+ .join("; ");
1967
+ const err = new Error(`Schema validation failed: ${errors}`);
1968
+ err.raw = raw;
1969
+ err.reason = "schema_validation";
1970
+ lastError = err;
1971
+ if (attempt >= maxRetries)
1972
+ throw err;
1973
+ continue;
1974
+ }
1975
+ return coerced;
1976
+ }
1977
+ catch (e) {
1978
+ if (e instanceof SyntaxError) {
1979
+ const err = new Error(`JSON parse failed: ${e.message}`);
1980
+ err.raw = raw;
1981
+ err.reason = "json_parse";
1982
+ lastError = err;
1983
+ if (attempt >= maxRetries)
1984
+ throw err;
1985
+ continue;
1986
+ }
1987
+ if (e.reason) {
1988
+ lastError = e;
1989
+ if (attempt >= maxRetries)
1990
+ throw lastError;
1991
+ continue;
1992
+ }
1993
+ throw e;
1994
+ }
1995
+ }
1996
+ throw lastError ?? new Error("callLLMStructured failed");
1997
+ }
1998
+ async forkAgent(promptText, options) {
1999
+ const model = this.model;
2000
+ if (!model)
2001
+ throw new Error("No model selected");
2002
+ const auth = await this._modelRegistry.getApiKeyAndHeaders(model);
2003
+ if (!auth?.ok) {
2004
+ throw new Error("error" in auth ? auth.error : `No API key configured for ${model.provider}`);
2005
+ }
2006
+ if (!auth.apiKey) {
2007
+ throw new Error(`No API key configured for ${model.provider}`);
2008
+ }
2009
+ if (options?.signal?.aborted)
2010
+ throw new Error("Aborted");
2011
+ const opts = options ?? {};
2012
+ let toolNames = opts.tools ?? ["read", "grep", "find", "ls"];
2013
+ if (opts.bash === "deny") {
2014
+ toolNames = toolNames.filter((t) => t !== "bash");
2015
+ }
2016
+ const toolInstances = toolNames
2017
+ .map((name) => {
2018
+ try {
2019
+ const registered = this._toolRegistry.get(name);
2020
+ if (registered)
2021
+ return registered;
2022
+ return createTool(name, this._cwd);
2023
+ }
2024
+ catch {
2025
+ return undefined;
2026
+ }
2027
+ })
2028
+ .filter((t) => t !== undefined);
2029
+ const effectiveSystemPrompt = opts.inheritSystemPrompt
2030
+ ? (this.agent.state.systemPrompt ?? opts.systemPrompt ?? "")
2031
+ : (opts.systemPrompt ?? "");
2032
+ const messages = opts.shareContext ? [...this.agent.state.messages] : [];
2033
+ const maxTurns = opts.maxTurns ?? 5;
2034
+ let turnCount = 0;
2035
+ const forkedAgent = new Agent({
2036
+ getApiKey: () => auth.apiKey,
2037
+ initialState: {
2038
+ systemPrompt: effectiveSystemPrompt,
2039
+ model,
2040
+ thinkingLevel: "off",
2041
+ tools: toolInstances,
2042
+ messages,
2043
+ },
2044
+ sessionId: opts.shareContext ? this.agent.sessionId : undefined,
2045
+ });
2046
+ let resultText = "";
2047
+ const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
2048
+ const abortHandler = opts.signal ? () => forkedAgent.abort() : undefined;
2049
+ if (abortHandler && opts.signal) {
2050
+ opts.signal.addEventListener("abort", abortHandler, { once: true });
2051
+ }
2052
+ const unsub = forkedAgent.subscribe((event) => {
2053
+ if (event.type === "turn_end") {
2054
+ turnCount++;
2055
+ if (turnCount >= maxTurns) {
2056
+ forkedAgent.abort();
2057
+ }
2058
+ }
2059
+ if (event.type === "message_end") {
2060
+ const msg = event.message;
2061
+ if (msg.role === "assistant") {
2062
+ const asst = msg;
2063
+ const content = asst.content;
2064
+ if (Array.isArray(content)) {
2065
+ resultText = content
2066
+ .filter((c) => c.type === "text")
2067
+ .map((c) => c.text)
2068
+ .join("\n");
2069
+ }
2070
+ if (asst.usage) {
2071
+ usage.input = asst.usage.input ?? 0;
2072
+ usage.output = asst.usage.output ?? 0;
2073
+ usage.cacheRead = asst.usage.cacheRead ?? 0;
2074
+ usage.cacheWrite = asst.usage.cacheWrite ?? 0;
2075
+ if (model.cost) {
2076
+ usage.cost =
2077
+ (usage.input * model.cost.input +
2078
+ usage.output * model.cost.output +
2079
+ usage.cacheRead * model.cost.cacheRead +
2080
+ usage.cacheWrite * model.cost.cacheWrite) /
2081
+ 1_000_000;
2082
+ }
2083
+ }
2084
+ }
2085
+ }
2086
+ });
2087
+ try {
2088
+ await forkedAgent.prompt({
2089
+ role: "user",
2090
+ content: [{ type: "text", text: promptText }],
2091
+ timestamp: Date.now(),
2092
+ });
2093
+ }
2094
+ finally {
2095
+ unsub();
2096
+ if (abortHandler && opts.signal) {
2097
+ opts.signal.removeEventListener("abort", abortHandler);
2098
+ }
2099
+ }
2100
+ return { text: resultText, usage };
2101
+ }
2102
+ background(fn) {
2103
+ const controller = new AbortController();
2104
+ const promise = fn(controller.signal);
2105
+ const task = {
2106
+ id: randomUUID(),
2107
+ signal: controller.signal,
2108
+ promise: promise,
2109
+ cancel: () => controller.abort(),
2110
+ };
2111
+ this._backgroundTasks.add(task);
2112
+ promise.finally(() => this._backgroundTasks.delete(task));
2113
+ return task;
2114
+ }
1915
2115
  _refreshToolRegistry(options) {
1916
2116
  const previousRegistryNames = new Set(this._toolRegistry.keys());
1917
2117
  const previousActiveToolNames = this.getActiveToolNames();
@@ -2043,6 +2243,14 @@ export class AgentSession {
2043
2243
  await this.extendResourcesFromExtensions("reload");
2044
2244
  }
2045
2245
  }
2246
+ async setCwd(newCwd) {
2247
+ this._cwd = newCwd;
2248
+ this._buildRuntime({
2249
+ activeToolNames: this.getActiveToolNames(),
2250
+ flagValues: this._extensionRunner.getFlagValues(),
2251
+ includeAllExtensionTools: true,
2252
+ });
2253
+ }
2046
2254
  // =========================================================================
2047
2255
  // Auto-Retry
2048
2256
  // =========================================================================