@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.
- package/dist/agents/architect.md +4 -3
- package/dist/agents/builder.md +5 -4
- package/dist/agents/commentator.md +3 -2
- package/dist/agents/explorer.md +4 -4
- package/dist/agents/recapper.md +4 -3
- package/dist/config.d.ts +6 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -9
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +3 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +18 -24
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +2 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +8 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +25 -11
- package/dist/core/skills.js.map +1 -1
- package/dist/defaults/models.json +143 -39
- package/dist/defaults/settings.json +2 -29
- package/dist/extensions/package.json +5 -4
- package/dist/extensions/pi-intercom/index.ts +12 -5
- package/dist/extensions/pi-powerline-footer/index.ts +4 -1
- package/dist/extensions/pi-powerline-footer/tps.ts +286 -0
- package/dist/extensions/pi-powerline-footer/welcome.ts +47 -46
- package/dist/extensions/pi-subagents/README.md +12 -5
- package/dist/extensions/pi-subagents/agents/context-builder.md +2 -2
- package/dist/extensions/pi-subagents/agents/researcher.md +4 -5
- package/dist/extensions/pi-subagents/package.json +8 -5
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +1 -1
- package/dist/extensions/pi-subagents/src/agents/agents.ts +3 -3
- package/dist/extensions/pi-subagents/src/extension/control-notices.ts +1 -1
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +1 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +29 -2
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +72 -13
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +85 -41
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +46 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +1 -3
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +18 -18
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +24 -24
- package/dist/extensions/pi-subagents/src/runs/shared/spawn-timing.ts +46 -0
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +4 -4
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +1 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +6 -6
- package/dist/extensions/pi-subagents/src/shared/utils.ts +3 -3
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
- package/dist/extensions/pi-subagents/src/tui/render-helpers.ts +1 -1
- package/dist/extensions/pi-subagents/src/tui/render.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/spawn-timing.test.ts +45 -0
- package/dist/extensions/pi-subagents/tsconfig.runner.json +17 -0
- package/dist/extensions/pi-web-agent/package.json +31 -0
- package/dist/extensions/pi-web-agent/src/backends/config.ts +205 -0
- package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -0
- package/dist/extensions/pi-web-agent/src/backends/factory.ts +152 -0
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +25 -0
- package/dist/extensions/pi-web-agent/src/cache/ttl-cache.ts +28 -0
- package/dist/extensions/pi-web-agent/src/changelog-notice.ts +133 -0
- package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +946 -0
- package/dist/extensions/pi-web-agent/src/extension.ts +126 -0
- package/dist/extensions/pi-web-agent/src/extract/readability.ts +118 -0
- package/dist/extensions/pi-web-agent/src/fetch/browser-resolution.ts +199 -0
- package/dist/extensions/pi-web-agent/src/fetch/firecrawl-fetch.ts +100 -0
- package/dist/extensions/pi-web-agent/src/fetch/headless-fetch.ts +117 -0
- package/dist/extensions/pi-web-agent/src/fetch/http-fetch.ts +67 -0
- package/dist/extensions/pi-web-agent/src/orchestration/answer-synthesizer.ts +60 -0
- package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +50 -0
- package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +52 -0
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +105 -0
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +45 -0
- package/dist/extensions/pi-web-agent/src/orchestration/index.ts +28 -0
- package/dist/extensions/pi-web-agent/src/orchestration/query-planner.ts +47 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +376 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +64 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +181 -0
- package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +101 -0
- package/dist/extensions/pi-web-agent/src/orchestration/stop-decider.ts +81 -0
- package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +208 -0
- package/dist/extensions/pi-web-agent/src/presentation/config.ts +75 -0
- package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +61 -0
- package/dist/extensions/pi-web-agent/src/presentation/fetch-presentation.ts +54 -0
- package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +41 -0
- package/dist/extensions/pi-web-agent/src/presentation/select-view.ts +20 -0
- package/dist/extensions/pi-web-agent/src/presentation/types.ts +63 -0
- package/dist/extensions/pi-web-agent/src/search/brave.ts +114 -0
- package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +72 -0
- package/dist/extensions/pi-web-agent/src/search/searxng.ts +96 -0
- package/dist/extensions/pi-web-agent/src/tools/web-explore.ts +71 -0
- package/dist/extensions/pi-web-agent/src/tools/web-fetch-headless.ts +31 -0
- package/dist/extensions/pi-web-agent/src/tools/web-fetch.ts +31 -0
- package/dist/extensions/pi-web-agent/src/tools/web-search.ts +157 -0
- package/dist/extensions/pi-web-agent/src/types.ts +88 -0
- package/dist/extensions/ponytail/index.js +189 -0
- package/dist/extensions/ponytail/package.json +8 -0
- package/dist/extensions/ponytail/ponytail-config.cjs +122 -0
- package/dist/extensions/ponytail/ponytail-instructions.cjs +94 -0
- package/dist/extensions/ponytail/test/extension.test.js +167 -0
- package/dist/extensions/ponytail/test/helpers.test.js +92 -0
- package/dist/extensions/question/constants.ts +38 -0
- package/dist/extensions/question/dialog-adapter.ts +74 -0
- package/dist/extensions/question/helpers.ts +102 -0
- package/dist/extensions/question/index.ts +671 -0
- package/dist/extensions/question/navigation.ts +14 -0
- package/dist/extensions/question/package.json +18 -0
- package/dist/extensions/question/question-list.ts +498 -0
- package/dist/extensions/question/row-layout.ts +162 -0
- package/dist/extensions/question/schemas.ts +69 -0
- package/dist/extensions/question/selection-mode.ts +49 -0
- package/dist/extensions/question/shortcuts.ts +53 -0
- package/dist/extensions/question/tests/helpers.test.ts +178 -0
- package/dist/extensions/question/tests/question-list.test.ts +230 -0
- package/dist/extensions/question/tests/row-layout.test.ts +136 -0
- package/dist/extensions/question/tests/shortcuts.test.ts +85 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +143 -0
- package/dist/extensions/question/tui-adapter.ts +33 -0
- package/dist/extensions/question/types.ts +102 -0
- package/dist/extensions/question/ui-protocol.ts +46 -0
- package/dist/extensions/tokenin-onboarding.ts +265 -2
- package/dist/extensions/web-agent-onboarding.ts +222 -0
- package/dist/extensions/workflow/adapter.ts +588 -0
- package/dist/extensions/workflow/extension.ts +24 -0
- package/dist/extensions/workflow/modes/prototype.ts +156 -0
- package/dist/extensions/workflow/modes/quick.ts +138 -0
- package/dist/extensions/workflow/package.json +17 -0
- package/dist/extensions/workflow/state-machine.ts +386 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +57 -13
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js +24 -1
- package/dist/utils/mime.js.map +1 -1
- package/docs/docs.json +4 -0
- package/docs/workflows.md +206 -0
- package/package.json +5 -1
- 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
|
|
5
|
+
export const SELESAI_PACKAGE = "@selesai/code";
|
|
6
6
|
|
|
7
|
-
export function
|
|
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 ===
|
|
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
|
|
21
|
-
return
|
|
20
|
+
export function resolveInstalledSelesaiPackageRoot(): string | undefined {
|
|
21
|
+
return findSelesaiPackageRootFromEntry(fileURLToPath(import.meta.resolve(SELESAI_PACKAGE)));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function
|
|
24
|
+
export function resolveSelesaiPackageRoot(): string | undefined {
|
|
25
25
|
try {
|
|
26
26
|
const entry = process.argv[1];
|
|
27
|
-
return entry ?
|
|
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
|
|
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
|
|
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
|
|
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 ??
|
|
85
|
+
const root = deps.piPackageRoot ?? resolveSelesaiPackageRoot();
|
|
86
86
|
if (root) return path.join(root, "package.json");
|
|
87
87
|
const packageRoot = deps.resolvePackageEntry
|
|
88
|
-
?
|
|
89
|
-
:
|
|
90
|
-
if (!packageRoot) throw new Error(`Could not resolve ${
|
|
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?.
|
|
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 `
|
|
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
|
|
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
|
-
// "
|
|
118
|
-
const
|
|
119
|
-
if (
|
|
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: [
|
|
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. "
|
|
128
|
-
return { command: "
|
|
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 = "
|
|
8
|
-
export const STRUCTURED_OUTPUT_CAPTURE_ENV = "
|
|
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 "@
|
|
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 = "
|
|
9
|
-
const SUBAGENT_INHERIT_SKILLS_ENV = "
|
|
10
|
-
export const SUBAGENT_INTERCOM_SESSION_NAME_ENV = "
|
|
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.",
|
|
@@ -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 "@
|
|
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.
|
|
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.
|
|
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.
|
|
1004
|
+
const parentDepth = Number(process.env.SELESAI_SUBAGENT_DEPTH ?? "0");
|
|
1005
1005
|
const nextDepth = Number.isFinite(parentDepth) ? parentDepth + 1 : 1;
|
|
1006
1006
|
return {
|
|
1007
|
-
|
|
1008
|
-
|
|
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 "@
|
|
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 = ".
|
|
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.
|
|
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 "@
|
|
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 "@
|
|
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 {
|
|
@@ -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 "@
|
|
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
|
+
}
|