@selesai/code 0.1.19 → 0.2.1

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 (157) hide show
  1. package/dist/agents/architect.md +4 -3
  2. package/dist/agents/builder.md +5 -4
  3. package/dist/agents/commentator.md +3 -2
  4. package/dist/agents/explorer.md +4 -4
  5. package/dist/agents/recapper.md +4 -3
  6. package/dist/config.d.ts +6 -7
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +7 -9
  9. package/dist/config.js.map +1 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +3 -1
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/compaction/compaction.d.ts.map +1 -1
  14. package/dist/core/compaction/compaction.js +18 -24
  15. package/dist/core/compaction/compaction.js.map +1 -1
  16. package/dist/core/extensions/loader.d.ts.map +1 -1
  17. package/dist/core/extensions/loader.js +2 -0
  18. package/dist/core/extensions/loader.js.map +1 -1
  19. package/dist/core/extensions/types.d.ts +8 -1
  20. package/dist/core/extensions/types.d.ts.map +1 -1
  21. package/dist/core/extensions/types.js.map +1 -1
  22. package/dist/core/skills.d.ts.map +1 -1
  23. package/dist/core/skills.js +25 -11
  24. package/dist/core/skills.js.map +1 -1
  25. package/dist/defaults/models.json +143 -39
  26. package/dist/defaults/settings.json +2 -29
  27. package/dist/extensions/package.json +5 -4
  28. package/dist/extensions/pi-intercom/index.ts +12 -5
  29. package/dist/extensions/pi-powerline-footer/index.ts +4 -1
  30. package/dist/extensions/pi-powerline-footer/tps.ts +286 -0
  31. package/dist/extensions/pi-powerline-footer/welcome.ts +47 -46
  32. package/dist/extensions/pi-subagents/README.md +12 -5
  33. package/dist/extensions/pi-subagents/agents/context-builder.md +2 -2
  34. package/dist/extensions/pi-subagents/agents/researcher.md +4 -5
  35. package/dist/extensions/pi-subagents/package.json +8 -5
  36. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
  37. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
  38. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +1 -1
  39. package/dist/extensions/pi-subagents/src/agents/agents.ts +3 -3
  40. package/dist/extensions/pi-subagents/src/extension/control-notices.ts +1 -1
  41. package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
  42. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +1 -1
  43. package/dist/extensions/pi-subagents/src/extension/index.ts +29 -2
  44. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +2 -2
  45. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +72 -13
  46. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +1 -1
  47. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +1 -1
  48. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +85 -41
  49. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +1 -1
  50. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +1 -1
  51. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +46 -3
  52. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +2 -2
  53. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +1 -3
  54. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +2 -2
  55. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +18 -18
  56. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +24 -24
  57. package/dist/extensions/pi-subagents/src/runs/shared/spawn-timing.ts +46 -0
  58. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +2 -2
  59. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +4 -4
  60. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +1 -1
  61. package/dist/extensions/pi-subagents/src/shared/types.ts +6 -6
  62. package/dist/extensions/pi-subagents/src/shared/utils.ts +3 -3
  63. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -1
  64. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
  65. package/dist/extensions/pi-subagents/src/tui/render-helpers.ts +1 -1
  66. package/dist/extensions/pi-subagents/src/tui/render.ts +1 -1
  67. package/dist/extensions/pi-subagents/test/unit/spawn-timing.test.ts +45 -0
  68. package/dist/extensions/pi-subagents/tsconfig.runner.json +17 -0
  69. package/dist/extensions/pi-web-agent/package.json +31 -0
  70. package/dist/extensions/pi-web-agent/src/backends/config.ts +205 -0
  71. package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -0
  72. package/dist/extensions/pi-web-agent/src/backends/factory.ts +152 -0
  73. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +25 -0
  74. package/dist/extensions/pi-web-agent/src/cache/ttl-cache.ts +28 -0
  75. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +133 -0
  76. package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +946 -0
  77. package/dist/extensions/pi-web-agent/src/extension.ts +126 -0
  78. package/dist/extensions/pi-web-agent/src/extract/readability.ts +118 -0
  79. package/dist/extensions/pi-web-agent/src/fetch/browser-resolution.ts +199 -0
  80. package/dist/extensions/pi-web-agent/src/fetch/firecrawl-fetch.ts +100 -0
  81. package/dist/extensions/pi-web-agent/src/fetch/headless-fetch.ts +117 -0
  82. package/dist/extensions/pi-web-agent/src/fetch/http-fetch.ts +67 -0
  83. package/dist/extensions/pi-web-agent/src/orchestration/answer-synthesizer.ts +60 -0
  84. package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +50 -0
  85. package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +52 -0
  86. package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +105 -0
  87. package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +45 -0
  88. package/dist/extensions/pi-web-agent/src/orchestration/index.ts +28 -0
  89. package/dist/extensions/pi-web-agent/src/orchestration/query-planner.ts +47 -0
  90. package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +376 -0
  91. package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +64 -0
  92. package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +181 -0
  93. package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +101 -0
  94. package/dist/extensions/pi-web-agent/src/orchestration/stop-decider.ts +81 -0
  95. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +208 -0
  96. package/dist/extensions/pi-web-agent/src/presentation/config.ts +75 -0
  97. package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +61 -0
  98. package/dist/extensions/pi-web-agent/src/presentation/fetch-presentation.ts +54 -0
  99. package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +41 -0
  100. package/dist/extensions/pi-web-agent/src/presentation/select-view.ts +20 -0
  101. package/dist/extensions/pi-web-agent/src/presentation/types.ts +63 -0
  102. package/dist/extensions/pi-web-agent/src/search/brave.ts +114 -0
  103. package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +72 -0
  104. package/dist/extensions/pi-web-agent/src/search/searxng.ts +96 -0
  105. package/dist/extensions/pi-web-agent/src/tools/web-explore.ts +71 -0
  106. package/dist/extensions/pi-web-agent/src/tools/web-fetch-headless.ts +31 -0
  107. package/dist/extensions/pi-web-agent/src/tools/web-fetch.ts +31 -0
  108. package/dist/extensions/pi-web-agent/src/tools/web-search.ts +157 -0
  109. package/dist/extensions/pi-web-agent/src/types.ts +88 -0
  110. package/dist/extensions/ponytail/index.js +189 -0
  111. package/dist/extensions/ponytail/package.json +8 -0
  112. package/dist/extensions/ponytail/ponytail-config.cjs +122 -0
  113. package/dist/extensions/ponytail/ponytail-instructions.cjs +94 -0
  114. package/dist/extensions/ponytail/test/extension.test.js +167 -0
  115. package/dist/extensions/ponytail/test/helpers.test.js +92 -0
  116. package/dist/extensions/question/constants.ts +38 -0
  117. package/dist/extensions/question/dialog-adapter.ts +74 -0
  118. package/dist/extensions/question/helpers.ts +102 -0
  119. package/dist/extensions/question/index.ts +671 -0
  120. package/dist/extensions/question/navigation.ts +14 -0
  121. package/dist/extensions/question/package.json +18 -0
  122. package/dist/extensions/question/question-list.ts +498 -0
  123. package/dist/extensions/question/row-layout.ts +162 -0
  124. package/dist/extensions/question/schemas.ts +69 -0
  125. package/dist/extensions/question/selection-mode.ts +49 -0
  126. package/dist/extensions/question/shortcuts.ts +53 -0
  127. package/dist/extensions/question/tests/helpers.test.ts +178 -0
  128. package/dist/extensions/question/tests/question-list.test.ts +230 -0
  129. package/dist/extensions/question/tests/row-layout.test.ts +136 -0
  130. package/dist/extensions/question/tests/shortcuts.test.ts +85 -0
  131. package/dist/extensions/question/tests/ui-protocol.test.ts +143 -0
  132. package/dist/extensions/question/tui-adapter.ts +33 -0
  133. package/dist/extensions/question/types.ts +102 -0
  134. package/dist/extensions/question/ui-protocol.ts +46 -0
  135. package/dist/extensions/tokenin-onboarding.ts +265 -2
  136. package/dist/extensions/web-agent-onboarding.ts +222 -0
  137. package/dist/extensions/workflow/adapter.ts +588 -0
  138. package/dist/extensions/workflow/extension.ts +24 -0
  139. package/dist/extensions/workflow/modes/prototype.ts +156 -0
  140. package/dist/extensions/workflow/modes/quick.ts +138 -0
  141. package/dist/extensions/workflow/package.json +17 -0
  142. package/dist/extensions/workflow/state-machine.ts +386 -0
  143. package/dist/index.d.ts +1 -1
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +1 -1
  146. package/dist/index.js.map +1 -1
  147. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  148. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  149. package/dist/modes/interactive/interactive-mode.js +57 -13
  150. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  151. package/dist/utils/mime.d.ts.map +1 -1
  152. package/dist/utils/mime.js +24 -1
  153. package/dist/utils/mime.js.map +1 -1
  154. package/docs/docs.json +4 -0
  155. package/docs/workflows.md +206 -0
  156. package/package.json +5 -1
  157. package/README.md +0 -198
@@ -2,36 +2,36 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
5
- export const PI_CODING_AGENT_PACKAGE = "@earendil-works/pi-coding-agent";
5
+ export const SELESAI_PACKAGE = "@selesai/code";
6
6
 
7
- export function findPiPackageRootFromEntry(entryPoint: string): string | undefined {
7
+ export function findSelesaiPackageRootFromEntry(entryPoint: string): string | undefined {
8
8
  let dir = path.dirname(entryPoint);
9
9
  while (dir !== path.dirname(dir)) {
10
10
  const packageJsonPath = path.join(dir, "package.json");
11
11
  if (fs.existsSync(packageJsonPath)) {
12
12
  const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as { name?: unknown };
13
- if (pkg.name === PI_CODING_AGENT_PACKAGE) return dir;
13
+ if (pkg.name === SELESAI_PACKAGE) return dir;
14
14
  }
15
15
  dir = path.dirname(dir);
16
16
  }
17
17
  return undefined;
18
18
  }
19
19
 
20
- export function resolveInstalledPiPackageRoot(): string | undefined {
21
- return findPiPackageRootFromEntry(fileURLToPath(import.meta.resolve(PI_CODING_AGENT_PACKAGE)));
20
+ export function resolveInstalledSelesaiPackageRoot(): string | undefined {
21
+ return findSelesaiPackageRootFromEntry(fileURLToPath(import.meta.resolve(SELESAI_PACKAGE)));
22
22
  }
23
23
 
24
- export function resolvePiPackageRoot(): string | undefined {
24
+ export function resolveSelesaiPackageRoot(): string | undefined {
25
25
  try {
26
26
  const entry = process.argv[1];
27
- return entry ? findPiPackageRootFromEntry(fs.realpathSync(entry)) : undefined;
27
+ return entry ? findSelesaiPackageRootFromEntry(fs.realpathSync(entry)) : undefined;
28
28
  } catch {
29
29
  // process.argv[1] probing is best-effort; callers can fall back to PATH/package resolution.
30
30
  return undefined;
31
31
  }
32
32
  }
33
33
 
34
- export interface PiSpawnDeps {
34
+ export interface SelesaiSpawnDeps {
35
35
  platform?: NodeJS.Platform;
36
36
  execPath?: string;
37
37
  argv1?: string;
@@ -42,7 +42,7 @@ export interface PiSpawnDeps {
42
42
  piPackageRoot?: string;
43
43
  }
44
44
 
45
- interface PiSpawnCommand {
45
+ interface SelesaiSpawnCommand {
46
46
  command: string;
47
47
  args: string[];
48
48
  }
@@ -56,7 +56,7 @@ function normalizePath(filePath: string): string {
56
56
  return path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
57
57
  }
58
58
 
59
- export function resolveWindowsPiCliScript(deps: PiSpawnDeps = {}): string | undefined {
59
+ export function resolveWindowsSelesaiCliScript(deps: SelesaiSpawnDeps = {}): string | undefined {
60
60
  const existsSync = deps.existsSync ?? fs.existsSync;
61
61
  const readFileSync = deps.readFileSync ?? ((filePath, encoding) => fs.readFileSync(filePath, encoding));
62
62
  const argv1 = deps.argv1 ?? process.argv[1];
@@ -82,12 +82,12 @@ export function resolveWindowsPiCliScript(deps: PiSpawnDeps = {}): string | unde
82
82
 
83
83
  try {
84
84
  const resolvePackageJson = deps.resolvePackageJson ?? (() => {
85
- const root = deps.piPackageRoot ?? resolvePiPackageRoot();
85
+ const root = deps.piPackageRoot ?? resolveSelesaiPackageRoot();
86
86
  if (root) return path.join(root, "package.json");
87
87
  const packageRoot = deps.resolvePackageEntry
88
- ? findPiPackageRootFromEntry(deps.resolvePackageEntry())
89
- : resolveInstalledPiPackageRoot();
90
- if (!packageRoot) throw new Error(`Could not resolve ${PI_CODING_AGENT_PACKAGE} package root`);
88
+ ? findSelesaiPackageRootFromEntry(deps.resolvePackageEntry())
89
+ : resolveInstalledSelesaiPackageRoot();
90
+ if (!packageRoot) throw new Error(`Could not resolve ${SELESAI_PACKAGE} package root`);
91
91
  return path.join(packageRoot, "package.json");
92
92
  });
93
93
  const packageJsonPath = resolvePackageJson();
@@ -97,33 +97,33 @@ export function resolveWindowsPiCliScript(deps: PiSpawnDeps = {}): string | unde
97
97
  const binField = packageJson.bin;
98
98
  const binPath = typeof binField === "string"
99
99
  ? binField
100
- : binField?.pi ?? Object.values(binField ?? {})[0];
100
+ : binField?.selesai ?? Object.values(binField ?? {})[0];
101
101
  if (!binPath) return undefined;
102
102
  const candidate = path.resolve(path.dirname(packageJsonPath), binPath);
103
103
  if (isRunnableNodeScript(candidate, existsSync)) {
104
104
  return candidate;
105
105
  }
106
106
  } catch {
107
- // Windows CLI resolution is optional; falling back to `pi` lets PATH handle execution.
107
+ // Windows CLI resolution is optional; falling back to `selesai` lets PATH handle execution.
108
108
  return undefined;
109
109
  }
110
110
 
111
111
  return undefined;
112
112
  }
113
113
 
114
- export function getPiSpawnCommand(args: string[], deps: PiSpawnDeps = {}): PiSpawnCommand {
114
+ export function getSelesaiSpawnCommand(args: string[], deps: SelesaiSpawnDeps = {}): SelesaiSpawnCommand {
115
115
  // On all platforms, prefer the running CLI script (process.argv[1]) so a
116
116
  // renamed fork like "selesai" spawns its own binary instead of the upstream
117
- // "pi" from PATH (which has a different agent dir / providers / auth).
118
- const piCliPath = resolveWindowsPiCliScript(deps);
119
- if (piCliPath) {
117
+ // "selesai" from PATH (which has a different agent dir / providers / auth).
118
+ const selesaiCliPath = resolveWindowsSelesaiCliScript(deps);
119
+ if (selesaiCliPath) {
120
120
  return {
121
121
  command: deps.execPath ?? process.execPath,
122
- args: [piCliPath, ...args],
122
+ args: [selesaiCliPath, ...args],
123
123
  };
124
124
  }
125
125
 
126
126
  // Fall back to the upstream binary name only if the CLI script could not be
127
- // resolved. "pi" remains the historical default for stock pi installs.
128
- return { command: "pi", args };
129
- }
127
+ // resolved. "selesai" is the default for selesai installs.
128
+ return { command: "selesai", args };
129
+ }
@@ -0,0 +1,46 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { performance } from "node:perf_hooks";
4
+ import { appendJsonl } from "../../shared/artifacts.ts";
5
+
6
+ export type SpawnTimingMarker =
7
+ | "spawn_start"
8
+ | "spawn_command_resolved"
9
+ | "child_first_event"
10
+ | "child_first_jsonl_event";
11
+
12
+ export interface SpawnTimingMarkers {
13
+ spawnStart: number;
14
+ }
15
+
16
+ export function createSpawnTimingMarkers(): SpawnTimingMarkers {
17
+ return { spawnStart: performance.now() };
18
+ }
19
+
20
+ export function emitSpawnTiming(options: {
21
+ eventsPath: string | undefined;
22
+ marker: SpawnTimingMarker;
23
+ markers: SpawnTimingMarkers;
24
+ runId?: string;
25
+ stepIndex?: number;
26
+ agent?: string;
27
+ }): void {
28
+ if (!options.eventsPath) return;
29
+ try {
30
+ fs.mkdirSync(path.dirname(options.eventsPath), { recursive: true });
31
+ appendJsonl(
32
+ options.eventsPath,
33
+ JSON.stringify({
34
+ type: "subagent.spawn.timing",
35
+ ts: Date.now(),
36
+ runId: options.runId ?? "",
37
+ stepIndex: options.stepIndex ?? 0,
38
+ agent: options.agent ?? "runner",
39
+ marker: options.marker,
40
+ elapsedMs: Math.round(performance.now() - options.markers.spawnStart),
41
+ }),
42
+ );
43
+ } catch {
44
+ // Timing diagnostics must never fail the run.
45
+ }
46
+ }
@@ -4,8 +4,8 @@ import * as path from "node:path";
4
4
  import { Compile } from "typebox/compile";
5
5
  import type { JsonSchemaObject } from "../../shared/types.ts";
6
6
 
7
- export const STRUCTURED_OUTPUT_SCHEMA_ENV = "PI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA";
8
- export const STRUCTURED_OUTPUT_CAPTURE_ENV = "PI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE";
7
+ export const STRUCTURED_OUTPUT_SCHEMA_ENV = "SELESAI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA";
8
+ export const STRUCTURED_OUTPUT_CAPTURE_ENV = "SELESAI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE";
9
9
 
10
10
  export interface StructuredOutputRuntime {
11
11
  schema: JsonSchemaObject;
@@ -1,13 +1,13 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ import type { ExtensionAPI } from "@selesai/code";
4
4
  import { SUBAGENT_FANOUT_CHILD_ENV } from "./pi-args.ts";
5
5
  import { STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV, validateStructuredOutputValue } from "./structured-output.ts";
6
6
  import type { JsonSchemaObject } from "../../shared/types.ts";
7
7
 
8
- const SUBAGENT_INHERIT_PROJECT_CONTEXT_ENV = "PI_SUBAGENT_INHERIT_PROJECT_CONTEXT";
9
- const SUBAGENT_INHERIT_SKILLS_ENV = "PI_SUBAGENT_INHERIT_SKILLS";
10
- export const SUBAGENT_INTERCOM_SESSION_NAME_ENV = "PI_SUBAGENT_INTERCOM_SESSION_NAME";
8
+ const SUBAGENT_INHERIT_PROJECT_CONTEXT_ENV = "SELESAI_SUBAGENT_INHERIT_PROJECT_CONTEXT";
9
+ const SUBAGENT_INHERIT_SKILLS_ENV = "SELESAI_SUBAGENT_INHERIT_SKILLS";
10
+ export const SUBAGENT_INTERCOM_SESSION_NAME_ENV = "SELESAI_SUBAGENT_INTERCOM_SESSION_NAME";
11
11
 
12
12
  const STRUCTURED_OUTPUT_INSTRUCTIONS = [
13
13
  "This subagent step has a strict structured output contract.",
@@ -1,5 +1,5 @@
1
1
  import * as fs from "node:fs";
2
- import { SessionManager } from "@earendil-works/pi-coding-agent";
2
+ import { SessionManager } from "@selesai/code";
3
3
 
4
4
  type SubagentExecutionContext = "fresh" | "fork";
5
5
 
@@ -6,7 +6,7 @@ import * as os from "node:os";
6
6
  import * as path from "node:path";
7
7
  import type { Message } from "@earendil-works/pi-ai";
8
8
  import type { FSWatcher } from "node:fs";
9
- import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
9
+ import type { ExtensionContext } from "@selesai/code";
10
10
 
11
11
  // ============================================================================
12
12
  // Basic Types
@@ -982,7 +982,7 @@ export function normalizeMaxSubagentDepth(value: unknown): number | undefined {
982
982
  }
983
983
 
984
984
  export function resolveCurrentMaxSubagentDepth(configMaxDepth?: number): number {
985
- return normalizeMaxSubagentDepth(process.env.PI_SUBAGENT_MAX_DEPTH)
985
+ return normalizeMaxSubagentDepth(process.env.SELESAI_SUBAGENT_MAX_DEPTH)
986
986
  ?? normalizeMaxSubagentDepth(configMaxDepth)
987
987
  ?? DEFAULT_SUBAGENT_MAX_DEPTH;
988
988
  }
@@ -994,18 +994,18 @@ export function resolveChildMaxSubagentDepth(parentMaxDepth: number, agentMaxDep
994
994
  }
995
995
 
996
996
  export function checkSubagentDepth(configMaxDepth?: number): { blocked: boolean; depth: number; maxDepth: number } {
997
- const depth = Number(process.env.PI_SUBAGENT_DEPTH ?? "0");
997
+ const depth = Number(process.env.SELESAI_SUBAGENT_DEPTH ?? "0");
998
998
  const maxDepth = resolveCurrentMaxSubagentDepth(configMaxDepth);
999
999
  const blocked = Number.isFinite(depth) && depth >= maxDepth;
1000
1000
  return { blocked, depth, maxDepth };
1001
1001
  }
1002
1002
 
1003
1003
  export function getSubagentDepthEnv(maxDepth?: number): Record<string, string> {
1004
- const parentDepth = Number(process.env.PI_SUBAGENT_DEPTH ?? "0");
1004
+ const parentDepth = Number(process.env.SELESAI_SUBAGENT_DEPTH ?? "0");
1005
1005
  const nextDepth = Number.isFinite(parentDepth) ? parentDepth + 1 : 1;
1006
1006
  return {
1007
- PI_SUBAGENT_DEPTH: String(nextDepth),
1008
- PI_SUBAGENT_MAX_DEPTH: String(normalizeMaxSubagentDepth(maxDepth) ?? resolveCurrentMaxSubagentDepth()),
1007
+ SELESAI_SUBAGENT_DEPTH: String(nextDepth),
1008
+ SELESAI_SUBAGENT_MAX_DEPTH: String(normalizeMaxSubagentDepth(maxDepth) ?? resolveCurrentMaxSubagentDepth()),
1009
1009
  };
1010
1010
  }
1011
1011
 
@@ -5,7 +5,7 @@
5
5
  import * as fs from "node:fs";
6
6
  import * as os from "node:os";
7
7
  import * as path from "node:path";
8
- import * as piCodingAgent from "@earendil-works/pi-coding-agent";
8
+ import * as piCodingAgent from "@selesai/code";
9
9
  import type { Message } from "@earendil-works/pi-ai";
10
10
  import { formatToolCall } from "./formatters.ts";
11
11
  import type { AgentProgress, AsyncStatus, Details, DisplayItem, ErrorInfo, SingleResult, ToolCallSummary } from "./types.ts";
@@ -14,7 +14,7 @@ import type { AgentProgress, AsyncStatus, Details, DisplayItem, ErrorInfo, Singl
14
14
  // File System Utilities
15
15
  // ============================================================================
16
16
 
17
- const DEFAULT_CONFIG_DIR_NAME = ".pi";
17
+ const DEFAULT_CONFIG_DIR_NAME = ".selesai";
18
18
 
19
19
  export function resolveConfigDirName(codingAgentModule: unknown = piCodingAgent): string {
20
20
  const value = codingAgentModule && typeof codingAgentModule === "object"
@@ -32,7 +32,7 @@ export function getProjectConfigDir(projectRoot: string): string {
32
32
  }
33
33
 
34
34
  export function getAgentDir(): string {
35
- const configured = process.env.PI_CODING_AGENT_DIR;
35
+ const configured = process.env.SELESAI_CODING_AGENT_DIR;
36
36
  if (configured === "~") return os.homedir();
37
37
  if (configured?.startsWith("~/")) return path.join(os.homedir(), configured.slice(2));
38
38
  return configured || path.join(os.homedir(), getConfigDirName(), "agent");
@@ -1,5 +1,5 @@
1
1
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
2
- import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { ExtensionContext } from "@selesai/code";
3
3
  import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
4
4
  import {
5
5
  SLASH_SUBAGENT_CANCEL_EVENT,
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
4
+ import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
5
5
  import { Key, matchesKey } from "@earendil-works/pi-tui";
6
6
  import { BUILTIN_AGENT_NAMES, discoverAgents, discoverAgentsAll, type ChainConfig } from "../agents/agents.ts";
7
7
  import {
@@ -1,4 +1,4 @@
1
- import type { Theme } from "@earendil-works/pi-coding-agent";
1
+ import type { Theme } from "@selesai/code";
2
2
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
3
 
4
4
  function fuzzyScore(query: string, text: string): number {
@@ -4,7 +4,7 @@
4
4
 
5
5
  import * as path from "node:path";
6
6
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
7
- import { getMarkdownTheme, type ExtensionContext } from "@earendil-works/pi-coding-agent";
7
+ import { getMarkdownTheme, type ExtensionContext } from "@selesai/code";
8
8
  import { Container, Markdown, Spacer, Text, visibleWidth, type Component } from "@earendil-works/pi-tui";
9
9
  import {
10
10
  type AgentProgress,
@@ -0,0 +1,45 @@
1
+ import { describe, it } from "node:test";
2
+ import * as assert from "node:assert";
3
+ import * as fs from "node:fs";
4
+ import * as os from "node:os";
5
+ import * as path from "node:path";
6
+ import { createSpawnTimingMarkers, emitSpawnTiming } from "../../src/runs/shared/spawn-timing.ts";
7
+
8
+ describe("spawn-timing", () => {
9
+ it("creates markers with a start time", () => {
10
+ const markers = createSpawnTimingMarkers();
11
+ assert.ok(markers.spawnStart > 0);
12
+ });
13
+
14
+ it("emits a timing line when an eventsPath is provided", () => {
15
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-timing-"));
16
+ const eventsPath = path.join(dir, "events.jsonl");
17
+ const markers = createSpawnTimingMarkers();
18
+ emitSpawnTiming({
19
+ eventsPath,
20
+ marker: "spawn_start",
21
+ markers,
22
+ runId: "run-1",
23
+ stepIndex: 2,
24
+ agent: "scout",
25
+ });
26
+ const lines = fs.readFileSync(eventsPath, "utf-8").trim().split("\n");
27
+ assert.equal(lines.length, 1);
28
+ const event = JSON.parse(lines[0]!);
29
+ assert.equal(event.type, "subagent.spawn.timing");
30
+ assert.equal(event.runId, "run-1");
31
+ assert.equal(event.stepIndex, 2);
32
+ assert.equal(event.agent, "scout");
33
+ assert.equal(event.marker, "spawn_start");
34
+ assert.ok(typeof event.elapsedMs === "number");
35
+ assert.ok(event.ts > 0);
36
+ fs.rmSync(dir, { recursive: true, force: true });
37
+ });
38
+
39
+ it("skips emission when eventsPath is missing", () => {
40
+ const markers = createSpawnTimingMarkers();
41
+ assert.doesNotThrow(() => {
42
+ emitSpawnTiming({ eventsPath: undefined, marker: "spawn_start", markers });
43
+ });
44
+ });
45
+ });
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist/runner",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "target": "ES2022",
8
+ "sourceMap": true,
9
+ "noCheck": true,
10
+ "noEmitOnError": false,
11
+ "declaration": false,
12
+ "declarationMap": false,
13
+ "inlineSources": false
14
+ },
15
+ "include": ["src/**/*"],
16
+ "exclude": ["test", "**/*.test.ts"]
17
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@selesai/bundled-pi-web-agent",
3
+ "version": "1.5.1",
4
+ "private": true,
5
+ "description": "Bundled pi-web-agent extension for reliable web access with explicit search, fetch, and headless boundaries. Vendored source-first from @demigodmode/pi-web-agent.",
6
+ "type": "module",
7
+ "keywords": [
8
+ "pi-package",
9
+ "pi",
10
+ "extension",
11
+ "web-search",
12
+ "web-fetch"
13
+ ],
14
+ "license": "AGPL-3.0-only",
15
+ "pi": {
16
+ "extensions": [
17
+ "./src/extension.ts"
18
+ ]
19
+ },
20
+ "dependencies": {
21
+ "@mozilla/readability": "^0.6.0",
22
+ "cheerio": "^1.1.0",
23
+ "jsdom": "^26.0.0",
24
+ "playwright": "^1.60.0",
25
+ "typebox": "^1.1.37"
26
+ },
27
+ "peerDependencies": {
28
+ "@earendil-works/pi-coding-agent": "*",
29
+ "@earendil-works/pi-tui": "*"
30
+ }
31
+ }
@@ -0,0 +1,205 @@
1
+ export type SearxngOptions = {
2
+ categories?: string[];
3
+ language?: string;
4
+ safesearch?: 0 | 1 | 2;
5
+ };
6
+
7
+ export type FirecrawlOptions = {
8
+ formats?: string[];
9
+ onlyMainContent?: boolean;
10
+ };
11
+
12
+ export type SearchBackendConfig = {
13
+ provider: 'duckduckgo' | 'searxng' | 'brave';
14
+ baseUrl?: string;
15
+ fallback?: 'duckduckgo';
16
+ options?: SearxngOptions;
17
+ };
18
+
19
+ export type FetchBackendConfig = {
20
+ provider: 'http' | 'firecrawl';
21
+ baseUrl?: string;
22
+ apiKey?: string;
23
+ fallback?: 'http';
24
+ options?: FirecrawlOptions;
25
+ };
26
+ export type HeadlessBackendConfig = { provider: 'local-browser' };
27
+
28
+ export type BackendConfig = {
29
+ search: SearchBackendConfig;
30
+ fetch: FetchBackendConfig;
31
+ headless: HeadlessBackendConfig;
32
+ };
33
+
34
+ export type BackendConfigOverride = {
35
+ search?: Partial<SearchBackendConfig>;
36
+ fetch?: Partial<FetchBackendConfig>;
37
+ headless?: Partial<HeadlessBackendConfig>;
38
+ };
39
+
40
+ export type BackendConfigFile = {
41
+ backends?: {
42
+ search?: { provider?: unknown; baseUrl?: unknown; fallback?: unknown; options?: unknown };
43
+ fetch?: { provider?: unknown; baseUrl?: unknown; apiKey?: unknown; fallback?: unknown; options?: unknown };
44
+ headless?: { provider?: unknown };
45
+ };
46
+ };
47
+
48
+ export const DEFAULT_BACKEND_CONFIG: BackendConfig = {
49
+ search: { provider: 'duckduckgo' },
50
+ fetch: { provider: 'http' },
51
+ headless: { provider: 'local-browser' }
52
+ };
53
+
54
+ function extractStringArray(value: unknown): string[] | undefined {
55
+ if (!Array.isArray(value)) return undefined;
56
+ const strings = value.filter((item): item is string => typeof item === 'string');
57
+ return strings.length === value.length ? strings : undefined;
58
+ }
59
+
60
+ function extractSearxngOptions(value: unknown): SearxngOptions | undefined {
61
+ if (!value || typeof value !== 'object') return undefined;
62
+ const raw = value as { categories?: unknown; language?: unknown; safesearch?: unknown };
63
+ const options: SearxngOptions = {};
64
+ const categories = extractStringArray(raw.categories);
65
+ if (categories) options.categories = categories;
66
+ if (typeof raw.language === 'string') options.language = raw.language;
67
+ if (raw.safesearch === 0 || raw.safesearch === 1 || raw.safesearch === 2) options.safesearch = raw.safesearch;
68
+ return Object.keys(options).length > 0 ? options : undefined;
69
+ }
70
+
71
+ function extractFirecrawlOptions(value: unknown): FirecrawlOptions | undefined {
72
+ if (!value || typeof value !== 'object') return undefined;
73
+ const raw = value as { formats?: unknown; onlyMainContent?: unknown };
74
+ const options: FirecrawlOptions = {};
75
+ const formats = extractStringArray(raw.formats);
76
+ if (formats) options.formats = formats;
77
+ if (typeof raw.onlyMainContent === 'boolean') options.onlyMainContent = raw.onlyMainContent;
78
+ return Object.keys(options).length > 0 ? options : undefined;
79
+ }
80
+
81
+ export function extractBackendConfigOverride(
82
+ file: BackendConfigFile | null | undefined
83
+ ): BackendConfigOverride {
84
+ const backends = file?.backends;
85
+ const override: BackendConfigOverride = {};
86
+
87
+ if (
88
+ backends?.search?.provider === 'duckduckgo' ||
89
+ backends?.search?.provider === 'searxng' ||
90
+ backends?.search?.provider === 'brave'
91
+ ) {
92
+ override.search = { provider: backends.search.provider };
93
+ if (backends.search.provider === 'searxng' && typeof backends.search.baseUrl === 'string') {
94
+ override.search.baseUrl = backends.search.baseUrl;
95
+ }
96
+ if (backends.search.fallback === 'duckduckgo') {
97
+ override.search.fallback = 'duckduckgo';
98
+ }
99
+ if (backends.search.provider === 'searxng') {
100
+ const options = extractSearxngOptions(backends.search.options);
101
+ if (options) {
102
+ override.search.options = options;
103
+ }
104
+ }
105
+ }
106
+
107
+ if (backends?.fetch?.provider === 'http' || backends?.fetch?.provider === 'firecrawl') {
108
+ override.fetch = { provider: backends.fetch.provider };
109
+ if (typeof backends.fetch.baseUrl === 'string') {
110
+ override.fetch.baseUrl = backends.fetch.baseUrl;
111
+ }
112
+ if (typeof backends.fetch.apiKey === 'string') {
113
+ override.fetch.apiKey = backends.fetch.apiKey;
114
+ }
115
+ if (backends.fetch.fallback === 'http') {
116
+ override.fetch.fallback = 'http';
117
+ }
118
+ const options = extractFirecrawlOptions(backends.fetch.options);
119
+ if (options) {
120
+ override.fetch.options = options;
121
+ }
122
+ }
123
+
124
+ if (backends?.headless?.provider === 'local-browser') {
125
+ override.headless = { provider: 'local-browser' };
126
+ }
127
+
128
+ return override;
129
+ }
130
+
131
+ export function validateBackendConfig(config: BackendConfig): string[] {
132
+ const issues: string[] = [];
133
+
134
+ if (config.search.provider === 'searxng' && !config.search.baseUrl) {
135
+ issues.push('search provider searxng requires backends.search.baseUrl');
136
+ }
137
+
138
+ if (config.fetch.provider === 'firecrawl' && !config.fetch.baseUrl) {
139
+ issues.push('fetch provider firecrawl requires backends.fetch.baseUrl');
140
+ }
141
+
142
+ if (config.search.fallback === 'duckduckgo' && config.search.provider !== 'searxng' && config.search.provider !== 'brave') {
143
+ issues.push('search fallback duckduckgo is only supported when search provider is searxng or brave');
144
+ }
145
+
146
+ if (config.fetch.fallback === 'http' && config.fetch.provider !== 'firecrawl') {
147
+ issues.push('fetch fallback http is only supported when fetch provider is firecrawl');
148
+ }
149
+
150
+ if (config.search.options?.categories && config.search.options.categories.length === 0) {
151
+ issues.push('search options.categories must contain at least one category when provided');
152
+ }
153
+
154
+ if (config.search.options?.language !== undefined && !config.search.options.language.trim()) {
155
+ issues.push('search options.language must not be empty when provided');
156
+ }
157
+
158
+ if (
159
+ config.search.options?.safesearch !== undefined &&
160
+ ![0, 1, 2].includes(config.search.options.safesearch)
161
+ ) {
162
+ issues.push('search options.safesearch must be 0, 1, or 2 when provided');
163
+ }
164
+
165
+ if (config.fetch.options?.formats && config.fetch.options.formats.length === 0) {
166
+ issues.push('fetch options.formats must contain at least one format when provided');
167
+ }
168
+
169
+ return issues;
170
+ }
171
+
172
+ function mergeSearchConfig(
173
+ current: SearchBackendConfig,
174
+ override: Partial<SearchBackendConfig> | undefined
175
+ ): SearchBackendConfig {
176
+ if (!override) return current;
177
+ if (override.provider && override.provider !== current.provider) {
178
+ return { ...override, provider: override.provider };
179
+ }
180
+ return { ...current, ...override };
181
+ }
182
+
183
+ function mergeFetchConfig(
184
+ current: FetchBackendConfig,
185
+ override: Partial<FetchBackendConfig> | undefined
186
+ ): FetchBackendConfig {
187
+ if (!override) return current;
188
+ if (override.provider && override.provider !== current.provider) {
189
+ return { ...override, provider: override.provider };
190
+ }
191
+ return { ...current, ...override };
192
+ }
193
+
194
+ export function mergeBackendConfigLayers(
195
+ ...layers: Array<BackendConfig | BackendConfigOverride | undefined>
196
+ ): BackendConfig {
197
+ return layers.reduce<BackendConfig>(
198
+ (merged, layer) => ({
199
+ search: mergeSearchConfig(merged.search, layer?.search),
200
+ fetch: mergeFetchConfig(merged.fetch, layer?.fetch),
201
+ headless: { ...merged.headless, ...layer?.headless }
202
+ }),
203
+ DEFAULT_BACKEND_CONFIG
204
+ );
205
+ }