@aexol/spectral 0.7.1 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/agent/agents.js +1 -1
- package/dist/agent/index.js +199 -184
- package/dist/commands/serve.js +0 -3
- package/dist/designer/data/systems/renault/DESIGN.md +1 -1
- package/dist/designer/philosophies.js +668 -0
- package/dist/mcp/sampling-handler.js +1 -1
- package/dist/memory/commands/status.js +1 -1
- package/dist/memory/compaction.js +2 -2
- package/dist/memory/config.js +1 -1
- package/dist/memory/debug-log.js +1 -1
- package/dist/memory/hooks/compaction-hook.js +29 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/observer.js +2 -2
- package/dist/memory/project-observations-store.js +14 -0
- package/dist/memory/tokens.js +1 -1
- package/dist/memory/tools/read-project-observations.js +82 -0
- package/dist/memory/tools/recall-observation.js +2 -2
- package/dist/pi/agent-core/agent-loop.js +501 -0
- package/dist/pi/agent-core/agent.js +401 -0
- package/dist/pi/agent-core/harness/agent-harness.js +899 -0
- package/dist/pi/agent-core/harness/compaction/branch-summarization.js +173 -0
- package/dist/pi/agent-core/harness/compaction/compaction.js +532 -0
- package/dist/pi/agent-core/harness/compaction/utils.js +130 -0
- package/dist/pi/agent-core/harness/env/nodejs.js +485 -0
- package/dist/pi/agent-core/harness/messages.js +101 -0
- package/dist/pi/agent-core/harness/prompt-templates.js +229 -0
- package/dist/pi/agent-core/harness/session/jsonl-repo.js +100 -0
- package/dist/pi/agent-core/harness/session/jsonl-storage.js +230 -0
- package/dist/pi/agent-core/harness/session/memory-repo.js +41 -0
- package/dist/pi/agent-core/harness/session/memory-storage.js +113 -0
- package/dist/pi/agent-core/harness/session/repo-utils.js +38 -0
- package/dist/pi/agent-core/harness/session/session.js +196 -0
- package/dist/pi/agent-core/harness/session/uuid.js +49 -0
- package/dist/pi/agent-core/harness/skills.js +310 -0
- package/dist/pi/agent-core/harness/system-prompt.js +29 -0
- package/dist/pi/agent-core/harness/types.js +93 -0
- package/dist/pi/agent-core/harness/utils/shell-output.js +125 -0
- package/dist/pi/agent-core/harness/utils/truncate.js +289 -0
- package/dist/pi/agent-core/index.js +24 -0
- package/dist/pi/agent-core/node.js +2 -0
- package/dist/pi/agent-core/proxy.js +277 -0
- package/dist/pi/agent-core/types.js +1 -0
- package/dist/pi/ai/api-registry.js +43 -0
- package/dist/pi/ai/cli.js +120 -0
- package/dist/pi/ai/env-api-keys.js +169 -0
- package/dist/pi/ai/image-models.generated.js +441 -0
- package/dist/pi/ai/image-models.js +22 -0
- package/dist/pi/ai/images-api-registry.js +21 -0
- package/dist/pi/ai/images.js +13 -0
- package/dist/pi/ai/index.js +18 -0
- package/dist/pi/ai/models.generated.js +16220 -0
- package/dist/pi/ai/models.js +70 -0
- package/dist/pi/ai/oauth.js +1 -0
- package/dist/pi/ai/providers/anthropic.js +945 -0
- package/dist/pi/ai/providers/faux.js +367 -0
- package/dist/pi/ai/providers/github-copilot-headers.js +28 -0
- package/dist/pi/ai/providers/openai-completions.js +945 -0
- package/dist/pi/ai/providers/openai-prompt-cache.js +9 -0
- package/dist/pi/ai/providers/register-builtins.js +97 -0
- package/dist/pi/ai/providers/simple-options.js +40 -0
- package/dist/pi/ai/providers/transform-messages.js +183 -0
- package/dist/pi/ai/session-resources.js +21 -0
- package/dist/pi/ai/stream.js +26 -0
- package/dist/pi/ai/types.js +1 -0
- package/dist/pi/ai/utils/diagnostics.js +24 -0
- package/dist/pi/ai/utils/event-stream.js +80 -0
- package/dist/pi/ai/utils/hash.js +13 -0
- package/dist/pi/ai/utils/headers.js +7 -0
- package/dist/pi/ai/utils/json-parse.js +112 -0
- package/dist/pi/ai/utils/node-http-proxy.js +96 -0
- package/dist/pi/ai/utils/oauth/anthropic.js +334 -0
- package/dist/pi/ai/utils/oauth/device-code.js +54 -0
- package/dist/pi/ai/utils/oauth/github-copilot.js +270 -0
- package/dist/pi/ai/utils/oauth/index.js +121 -0
- package/dist/pi/ai/utils/oauth/oauth-page.js +104 -0
- package/dist/pi/ai/utils/oauth/openai-codex.js +384 -0
- package/dist/pi/ai/utils/oauth/pkce.js +30 -0
- package/dist/pi/ai/utils/oauth/types.js +1 -0
- package/dist/pi/ai/utils/overflow.js +150 -0
- package/dist/pi/ai/utils/sanitize-unicode.js +25 -0
- package/dist/pi/ai/utils/typebox-helpers.js +20 -0
- package/dist/pi/ai/utils/validation.js +280 -0
- package/dist/pi/coding-agent/bun/cli.js +7 -0
- package/dist/pi/coding-agent/bun/restore-sandbox-env.js +31 -0
- package/dist/pi/coding-agent/cli/args.js +340 -0
- package/dist/pi/coding-agent/cli/file-processor.js +82 -0
- package/dist/pi/coding-agent/cli/initial-message.js +21 -0
- package/dist/pi/coding-agent/cli.js +17 -0
- package/dist/pi/coding-agent/config.js +414 -0
- package/dist/pi/coding-agent/core/agent-session-runtime.js +299 -0
- package/dist/pi/coding-agent/core/agent-session-services.js +117 -0
- package/dist/pi/coding-agent/core/agent-session.js +2498 -0
- package/dist/pi/coding-agent/core/auth-guidance.js +20 -0
- package/dist/pi/coding-agent/core/auth-storage.js +441 -0
- package/dist/pi/coding-agent/core/bash-executor.js +110 -0
- package/dist/pi/coding-agent/core/compaction/branch-summarization.js +242 -0
- package/dist/pi/coding-agent/core/compaction/compaction.js +624 -0
- package/dist/pi/coding-agent/core/compaction/index.js +6 -0
- package/dist/pi/coding-agent/core/compaction/utils.js +152 -0
- package/dist/pi/coding-agent/core/defaults.js +1 -0
- package/dist/pi/coding-agent/core/diagnostics.js +1 -0
- package/dist/pi/coding-agent/core/event-bus.js +24 -0
- package/dist/pi/coding-agent/core/exec.js +74 -0
- package/dist/pi/coding-agent/core/export-html/ansi-to-html.js +248 -0
- package/dist/pi/coding-agent/core/export-html/index.js +225 -0
- package/dist/pi/coding-agent/core/export-html/tool-renderer.js +107 -0
- package/dist/pi/coding-agent/core/extensions/index.js +8 -0
- package/dist/pi/coding-agent/core/extensions/loader.js +485 -0
- package/dist/pi/coding-agent/core/extensions/runner.js +824 -0
- package/dist/pi/coding-agent/core/extensions/types.js +44 -0
- package/dist/pi/coding-agent/core/extensions/wrapper.js +21 -0
- package/dist/pi/coding-agent/core/footer-data-provider.js +309 -0
- package/dist/pi/coding-agent/core/http-dispatcher.js +47 -0
- package/dist/pi/coding-agent/core/index.js +11 -0
- package/dist/pi/coding-agent/core/keybindings.js +294 -0
- package/dist/pi/coding-agent/core/messages.js +122 -0
- package/dist/pi/coding-agent/core/model-registry.js +728 -0
- package/dist/pi/coding-agent/core/model-resolver.js +494 -0
- package/dist/pi/coding-agent/core/output-guard.js +58 -0
- package/dist/pi/coding-agent/core/package-manager.js +2020 -0
- package/dist/pi/coding-agent/core/prompt-templates.js +237 -0
- package/dist/pi/coding-agent/core/provider-display-names.js +32 -0
- package/dist/pi/coding-agent/core/resolve-config-value.js +125 -0
- package/dist/pi/coding-agent/core/resource-loader.js +733 -0
- package/dist/pi/coding-agent/core/sdk.js +282 -0
- package/dist/pi/coding-agent/core/session-cwd.js +37 -0
- package/dist/pi/coding-agent/core/session-manager.js +1146 -0
- package/dist/pi/coding-agent/core/settings-manager.js +794 -0
- package/dist/pi/coding-agent/core/skills.js +386 -0
- package/dist/pi/coding-agent/core/slash-commands.js +24 -0
- package/dist/pi/coding-agent/core/source-info.js +18 -0
- package/dist/pi/coding-agent/core/system-prompt.js +122 -0
- package/dist/pi/coding-agent/core/telemetry.js +8 -0
- package/dist/pi/coding-agent/core/timings.js +30 -0
- package/dist/pi/coding-agent/core/tools/bash.js +341 -0
- package/dist/pi/coding-agent/core/tools/edit-diff.js +344 -0
- package/dist/pi/coding-agent/core/tools/edit.js +324 -0
- package/dist/pi/coding-agent/core/tools/file-mutation-queue.js +36 -0
- package/dist/pi/coding-agent/core/tools/find.js +297 -0
- package/dist/pi/coding-agent/core/tools/grep.js +303 -0
- package/dist/pi/coding-agent/core/tools/index.js +111 -0
- package/dist/pi/coding-agent/core/tools/ls.js +168 -0
- package/dist/pi/coding-agent/core/tools/output-accumulator.js +183 -0
- package/dist/pi/coding-agent/core/tools/path-utils.js +61 -0
- package/dist/pi/coding-agent/core/tools/read.js +288 -0
- package/dist/pi/coding-agent/core/tools/render-utils.js +48 -0
- package/dist/pi/coding-agent/core/tools/tool-definition-wrapper.js +33 -0
- package/dist/pi/coding-agent/core/tools/truncate.js +214 -0
- package/dist/pi/coding-agent/core/tools/write.js +212 -0
- package/dist/pi/coding-agent/index.js +41 -0
- package/dist/pi/coding-agent/main.js +5 -0
- package/dist/pi/coding-agent/migrations.js +280 -0
- package/dist/pi/coding-agent/modes/index.js +7 -0
- package/dist/pi/coding-agent/modes/interactive/components/diff.js +132 -0
- package/dist/pi/coding-agent/modes/interactive/components/keybinding-hints.js +35 -0
- package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +32 -0
- package/dist/pi/coding-agent/modes/interactive/interactive-mode.js +3 -0
- package/dist/pi/coding-agent/modes/interactive/theme/theme.js +1023 -0
- package/dist/pi/coding-agent/modes/print-mode.js +130 -0
- package/dist/pi/coding-agent/modes/rpc/jsonl.js +48 -0
- package/dist/pi/coding-agent/modes/rpc/rpc-client.js +409 -0
- package/dist/pi/coding-agent/modes/rpc/rpc-mode.js +600 -0
- package/dist/pi/coding-agent/modes/rpc/rpc-types.js +7 -0
- package/dist/pi/coding-agent/utils/ansi.js +51 -0
- package/dist/pi/coding-agent/utils/changelog.js +86 -0
- package/dist/pi/coding-agent/utils/child-process.js +87 -0
- package/dist/pi/coding-agent/utils/clipboard-image.js +244 -0
- package/dist/pi/coding-agent/utils/clipboard-native.js +13 -0
- package/dist/pi/coding-agent/utils/clipboard.js +116 -0
- package/dist/pi/coding-agent/utils/exif-orientation.js +157 -0
- package/dist/pi/coding-agent/utils/frontmatter.js +25 -0
- package/dist/pi/coding-agent/utils/fs-watch.js +24 -0
- package/dist/pi/coding-agent/utils/git.js +162 -0
- package/dist/pi/coding-agent/utils/html.js +39 -0
- package/dist/pi/coding-agent/utils/image-convert.js +38 -0
- package/dist/pi/coding-agent/utils/image-resize.js +136 -0
- package/dist/pi/coding-agent/utils/mime.js +68 -0
- package/dist/pi/coding-agent/utils/paths.js +91 -0
- package/dist/pi/coding-agent/utils/photon.js +120 -0
- package/dist/pi/coding-agent/utils/pi-user-agent.js +4 -0
- package/dist/pi/coding-agent/utils/shell.js +194 -0
- package/dist/pi/coding-agent/utils/sleep.js +16 -0
- package/dist/pi/coding-agent/utils/syntax-highlight.js +117 -0
- package/dist/pi/coding-agent/utils/tools-manager.js +327 -0
- package/dist/pi/coding-agent/utils/version-check.js +81 -0
- package/dist/pi/coding-agent/utils/windows-self-update.js +76 -0
- package/dist/pi/tui/autocomplete.js +631 -0
- package/dist/pi/tui/components/box.js +103 -0
- package/dist/pi/tui/components/cancellable-loader.js +34 -0
- package/dist/pi/tui/components/editor.js +1915 -0
- package/dist/pi/tui/components/image.js +88 -0
- package/dist/pi/tui/components/input.js +425 -0
- package/dist/pi/tui/components/loader.js +68 -0
- package/dist/pi/tui/components/markdown.js +633 -0
- package/dist/pi/tui/components/select-list.js +158 -0
- package/dist/pi/tui/components/settings-list.js +184 -0
- package/dist/pi/tui/components/spacer.js +22 -0
- package/dist/pi/tui/components/text.js +88 -0
- package/dist/pi/tui/components/truncated-text.js +50 -0
- package/dist/pi/tui/editor-component.js +1 -0
- package/dist/pi/tui/fuzzy.js +109 -0
- package/dist/pi/tui/index.js +31 -0
- package/dist/pi/tui/keybindings.js +173 -0
- package/dist/pi/tui/keys.js +1172 -0
- package/dist/pi/tui/kill-ring.js +43 -0
- package/dist/pi/tui/stdin-buffer.js +360 -0
- package/dist/pi/tui/terminal-image.js +335 -0
- package/dist/pi/tui/terminal.js +324 -0
- package/dist/pi/tui/tui.js +1076 -0
- package/dist/pi/tui/undo-stack.js +24 -0
- package/dist/pi/tui/utils.js +1016 -0
- package/dist/relay/dispatcher.js +30 -0
- package/dist/server/handlers/queue.js +52 -0
- package/dist/server/pi-bridge.js +9 -1
- package/dist/server/session-stream.js +76 -111
- package/dist/server/storage.js +154 -2
- package/dist/server/title-generator.js +14 -153
- package/package.json +24 -6
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension system types.
|
|
3
|
+
*
|
|
4
|
+
* Extensions are TypeScript modules that can:
|
|
5
|
+
* - Subscribe to agent lifecycle events
|
|
6
|
+
* - Register LLM-callable tools
|
|
7
|
+
* - Register commands, keyboard shortcuts, and CLI flags
|
|
8
|
+
* - Interact with the user via UI primitives
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Preserve parameter inference for standalone tool definitions.
|
|
12
|
+
*
|
|
13
|
+
* Use this when assigning a tool to a variable or passing it through arrays such
|
|
14
|
+
* as `customTools`, where contextual typing would otherwise widen params to
|
|
15
|
+
* `unknown`.
|
|
16
|
+
*/
|
|
17
|
+
export function defineTool(tool) {
|
|
18
|
+
return tool;
|
|
19
|
+
}
|
|
20
|
+
// Type guards for ToolResultEvent
|
|
21
|
+
export function isBashToolResult(e) {
|
|
22
|
+
return e.toolName === "bash";
|
|
23
|
+
}
|
|
24
|
+
export function isReadToolResult(e) {
|
|
25
|
+
return e.toolName === "read";
|
|
26
|
+
}
|
|
27
|
+
export function isEditToolResult(e) {
|
|
28
|
+
return e.toolName === "edit";
|
|
29
|
+
}
|
|
30
|
+
export function isWriteToolResult(e) {
|
|
31
|
+
return e.toolName === "write";
|
|
32
|
+
}
|
|
33
|
+
export function isGrepToolResult(e) {
|
|
34
|
+
return e.toolName === "grep";
|
|
35
|
+
}
|
|
36
|
+
export function isFindToolResult(e) {
|
|
37
|
+
return e.toolName === "find";
|
|
38
|
+
}
|
|
39
|
+
export function isLsToolResult(e) {
|
|
40
|
+
return e.toolName === "ls";
|
|
41
|
+
}
|
|
42
|
+
export function isToolCallEventType(toolName, event) {
|
|
43
|
+
return event.toolName === toolName;
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool wrappers for extension-registered tools.
|
|
3
|
+
*
|
|
4
|
+
* These wrappers only adapt tool execution so extension tools receive the runner context.
|
|
5
|
+
* Tool call and tool result interception is handled by AgentSession via agent hooks.
|
|
6
|
+
*/
|
|
7
|
+
import { wrapToolDefinition, wrapToolDefinitions } from "../tools/tool-definition-wrapper.js";
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a RegisteredTool into an AgentTool.
|
|
10
|
+
* Uses the runner's createContext() for consistent context across tools and event handlers.
|
|
11
|
+
*/
|
|
12
|
+
export function wrapRegisteredTool(registeredTool, runner) {
|
|
13
|
+
return wrapToolDefinition(registeredTool.definition, () => runner.createContext());
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Wrap all registered tools into AgentTools.
|
|
17
|
+
* Uses the runner's createContext() for consistent context across tools and event handlers.
|
|
18
|
+
*/
|
|
19
|
+
export function wrapRegisteredTools(registeredTools, runner) {
|
|
20
|
+
return wrapToolDefinitions(registeredTools.map((registeredTool) => registeredTool.definition), () => runner.createContext());
|
|
21
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { execFile, spawnSync } from "child_process";
|
|
2
|
+
import { existsSync, readFileSync, statSync, unwatchFile, watchFile } from "fs";
|
|
3
|
+
import { dirname, join, resolve } from "path";
|
|
4
|
+
import { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from "../utils/fs-watch.js";
|
|
5
|
+
/**
|
|
6
|
+
* Find git metadata paths by walking up from cwd.
|
|
7
|
+
* Handles both regular git repos (.git is a directory) and worktrees (.git is a file).
|
|
8
|
+
*/
|
|
9
|
+
function findGitPaths(cwd) {
|
|
10
|
+
let dir = cwd;
|
|
11
|
+
while (true) {
|
|
12
|
+
const gitPath = join(dir, ".git");
|
|
13
|
+
if (existsSync(gitPath)) {
|
|
14
|
+
try {
|
|
15
|
+
const stat = statSync(gitPath);
|
|
16
|
+
if (stat.isFile()) {
|
|
17
|
+
const content = readFileSync(gitPath, "utf8").trim();
|
|
18
|
+
if (content.startsWith("gitdir: ")) {
|
|
19
|
+
const gitDir = resolve(dir, content.slice(8).trim());
|
|
20
|
+
const headPath = join(gitDir, "HEAD");
|
|
21
|
+
if (!existsSync(headPath))
|
|
22
|
+
return null;
|
|
23
|
+
const commonDirPath = join(gitDir, "commondir");
|
|
24
|
+
const commonGitDir = existsSync(commonDirPath)
|
|
25
|
+
? resolve(gitDir, readFileSync(commonDirPath, "utf8").trim())
|
|
26
|
+
: gitDir;
|
|
27
|
+
return { repoDir: dir, commonGitDir, headPath };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else if (stat.isDirectory()) {
|
|
31
|
+
const headPath = join(gitPath, "HEAD");
|
|
32
|
+
if (!existsSync(headPath))
|
|
33
|
+
return null;
|
|
34
|
+
return { repoDir: dir, commonGitDir: gitPath, headPath };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const parent = dirname(dir);
|
|
42
|
+
if (parent === dir)
|
|
43
|
+
return null;
|
|
44
|
+
dir = parent;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Ask git for the current branch. Returns null on detached HEAD or if git is unavailable. */
|
|
48
|
+
function resolveBranchWithGitSync(repoDir) {
|
|
49
|
+
const result = spawnSync("git", ["--no-optional-locks", "symbolic-ref", "--quiet", "--short", "HEAD"], {
|
|
50
|
+
cwd: repoDir,
|
|
51
|
+
encoding: "utf8",
|
|
52
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
53
|
+
});
|
|
54
|
+
const branch = result.status === 0 ? result.stdout.trim() : "";
|
|
55
|
+
return branch || null;
|
|
56
|
+
}
|
|
57
|
+
/** Ask git for the current branch asynchronously. Returns null on detached HEAD or if git is unavailable. */
|
|
58
|
+
function resolveBranchWithGitAsync(repoDir) {
|
|
59
|
+
return new Promise((resolvePromise) => {
|
|
60
|
+
execFile("git", ["--no-optional-locks", "symbolic-ref", "--quiet", "--short", "HEAD"], {
|
|
61
|
+
cwd: repoDir,
|
|
62
|
+
encoding: "utf8",
|
|
63
|
+
}, (error, stdout) => {
|
|
64
|
+
if (error) {
|
|
65
|
+
resolvePromise(null);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const branch = stdout.trim();
|
|
69
|
+
resolvePromise(branch || null);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Provides git branch and extension statuses - data not otherwise accessible to extensions.
|
|
75
|
+
* Token stats, model info available via ctx.sessionManager and ctx.model.
|
|
76
|
+
*/
|
|
77
|
+
export class FooterDataProvider {
|
|
78
|
+
cwd;
|
|
79
|
+
static WATCH_DEBOUNCE_MS = 500;
|
|
80
|
+
extensionStatuses = new Map();
|
|
81
|
+
cachedBranch = undefined;
|
|
82
|
+
gitPaths = undefined;
|
|
83
|
+
headWatcher = null;
|
|
84
|
+
reftableWatcher = null;
|
|
85
|
+
reftableTablesListWatcher = null;
|
|
86
|
+
reftableTablesListPath = null;
|
|
87
|
+
branchChangeCallbacks = new Set();
|
|
88
|
+
availableProviderCount = 0;
|
|
89
|
+
refreshTimer = null;
|
|
90
|
+
gitWatcherRetryTimer = null;
|
|
91
|
+
refreshInFlight = false;
|
|
92
|
+
refreshPending = false;
|
|
93
|
+
disposed = false;
|
|
94
|
+
constructor(cwd) {
|
|
95
|
+
this.cwd = cwd;
|
|
96
|
+
this.gitPaths = findGitPaths(cwd);
|
|
97
|
+
this.setupGitWatcher();
|
|
98
|
+
}
|
|
99
|
+
/** Current git branch, null if not in repo, "detached" if detached HEAD */
|
|
100
|
+
getGitBranch() {
|
|
101
|
+
if (this.cachedBranch === undefined) {
|
|
102
|
+
this.cachedBranch = this.resolveGitBranchSync();
|
|
103
|
+
}
|
|
104
|
+
return this.cachedBranch;
|
|
105
|
+
}
|
|
106
|
+
/** Extension status texts set via ctx.ui.setStatus() */
|
|
107
|
+
getExtensionStatuses() {
|
|
108
|
+
return this.extensionStatuses;
|
|
109
|
+
}
|
|
110
|
+
/** Subscribe to git branch changes. Returns unsubscribe function. */
|
|
111
|
+
onBranchChange(callback) {
|
|
112
|
+
this.branchChangeCallbacks.add(callback);
|
|
113
|
+
return () => this.branchChangeCallbacks.delete(callback);
|
|
114
|
+
}
|
|
115
|
+
/** Internal: set extension status */
|
|
116
|
+
setExtensionStatus(key, text) {
|
|
117
|
+
if (text === undefined) {
|
|
118
|
+
this.extensionStatuses.delete(key);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this.extensionStatuses.set(key, text);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Internal: clear extension statuses */
|
|
125
|
+
clearExtensionStatuses() {
|
|
126
|
+
this.extensionStatuses.clear();
|
|
127
|
+
}
|
|
128
|
+
/** Number of unique providers with available models (for footer display) */
|
|
129
|
+
getAvailableProviderCount() {
|
|
130
|
+
return this.availableProviderCount;
|
|
131
|
+
}
|
|
132
|
+
/** Internal: update available provider count */
|
|
133
|
+
setAvailableProviderCount(count) {
|
|
134
|
+
this.availableProviderCount = count;
|
|
135
|
+
}
|
|
136
|
+
setCwd(cwd) {
|
|
137
|
+
if (this.cwd === cwd) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.cwd = cwd;
|
|
141
|
+
if (this.refreshTimer) {
|
|
142
|
+
clearTimeout(this.refreshTimer);
|
|
143
|
+
this.refreshTimer = null;
|
|
144
|
+
}
|
|
145
|
+
this.clearGitWatchers();
|
|
146
|
+
this.cachedBranch = undefined;
|
|
147
|
+
this.gitPaths = findGitPaths(cwd);
|
|
148
|
+
this.setupGitWatcher();
|
|
149
|
+
this.notifyBranchChange();
|
|
150
|
+
}
|
|
151
|
+
/** Internal: cleanup */
|
|
152
|
+
dispose() {
|
|
153
|
+
this.disposed = true;
|
|
154
|
+
if (this.refreshTimer) {
|
|
155
|
+
clearTimeout(this.refreshTimer);
|
|
156
|
+
this.refreshTimer = null;
|
|
157
|
+
}
|
|
158
|
+
this.clearGitWatchers();
|
|
159
|
+
this.branchChangeCallbacks.clear();
|
|
160
|
+
}
|
|
161
|
+
notifyBranchChange() {
|
|
162
|
+
for (const cb of this.branchChangeCallbacks)
|
|
163
|
+
cb();
|
|
164
|
+
}
|
|
165
|
+
scheduleRefresh() {
|
|
166
|
+
if (this.disposed || this.refreshTimer)
|
|
167
|
+
return;
|
|
168
|
+
if (this.refreshInFlight) {
|
|
169
|
+
this.refreshPending = true;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.refreshTimer = setTimeout(() => {
|
|
173
|
+
this.refreshTimer = null;
|
|
174
|
+
void this.refreshGitBranchAsync();
|
|
175
|
+
}, FooterDataProvider.WATCH_DEBOUNCE_MS);
|
|
176
|
+
}
|
|
177
|
+
async refreshGitBranchAsync() {
|
|
178
|
+
if (this.disposed)
|
|
179
|
+
return;
|
|
180
|
+
if (this.refreshInFlight) {
|
|
181
|
+
this.refreshPending = true;
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.refreshInFlight = true;
|
|
185
|
+
try {
|
|
186
|
+
const nextBranch = await this.resolveGitBranchAsync();
|
|
187
|
+
if (this.disposed)
|
|
188
|
+
return;
|
|
189
|
+
if (this.cachedBranch !== undefined && this.cachedBranch !== nextBranch) {
|
|
190
|
+
this.cachedBranch = nextBranch;
|
|
191
|
+
this.notifyBranchChange();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
this.cachedBranch = nextBranch;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
this.refreshInFlight = false;
|
|
198
|
+
if (this.refreshPending && !this.disposed) {
|
|
199
|
+
this.refreshPending = false;
|
|
200
|
+
this.scheduleRefresh();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
resolveGitBranchSync() {
|
|
205
|
+
try {
|
|
206
|
+
if (!this.gitPaths)
|
|
207
|
+
return null;
|
|
208
|
+
const content = readFileSync(this.gitPaths.headPath, "utf8").trim();
|
|
209
|
+
if (content.startsWith("ref: refs/heads/")) {
|
|
210
|
+
const branch = content.slice(16);
|
|
211
|
+
return branch === ".invalid" ? (resolveBranchWithGitSync(this.gitPaths.repoDir) ?? "detached") : branch;
|
|
212
|
+
}
|
|
213
|
+
return "detached";
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async resolveGitBranchAsync() {
|
|
220
|
+
try {
|
|
221
|
+
if (!this.gitPaths)
|
|
222
|
+
return null;
|
|
223
|
+
const content = readFileSync(this.gitPaths.headPath, "utf8").trim();
|
|
224
|
+
if (content.startsWith("ref: refs/heads/")) {
|
|
225
|
+
const branch = content.slice(16);
|
|
226
|
+
return branch === ".invalid"
|
|
227
|
+
? ((await resolveBranchWithGitAsync(this.gitPaths.repoDir)) ?? "detached")
|
|
228
|
+
: branch;
|
|
229
|
+
}
|
|
230
|
+
return "detached";
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
clearGitWatchers() {
|
|
237
|
+
closeWatcher(this.headWatcher);
|
|
238
|
+
this.headWatcher = null;
|
|
239
|
+
closeWatcher(this.reftableWatcher);
|
|
240
|
+
this.reftableWatcher = null;
|
|
241
|
+
closeWatcher(this.reftableTablesListWatcher);
|
|
242
|
+
this.reftableTablesListWatcher = null;
|
|
243
|
+
if (this.reftableTablesListPath) {
|
|
244
|
+
unwatchFile(this.reftableTablesListPath);
|
|
245
|
+
this.reftableTablesListPath = null;
|
|
246
|
+
}
|
|
247
|
+
if (this.gitWatcherRetryTimer) {
|
|
248
|
+
clearTimeout(this.gitWatcherRetryTimer);
|
|
249
|
+
this.gitWatcherRetryTimer = null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
scheduleGitWatcherRetry() {
|
|
253
|
+
if (this.disposed || this.gitWatcherRetryTimer) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
this.gitWatcherRetryTimer = setTimeout(() => {
|
|
257
|
+
this.gitWatcherRetryTimer = null;
|
|
258
|
+
this.setupGitWatcher();
|
|
259
|
+
}, FS_WATCH_RETRY_DELAY_MS);
|
|
260
|
+
}
|
|
261
|
+
handleGitWatcherError() {
|
|
262
|
+
this.clearGitWatchers();
|
|
263
|
+
this.scheduleGitWatcherRetry();
|
|
264
|
+
}
|
|
265
|
+
setupGitWatcher() {
|
|
266
|
+
this.clearGitWatchers();
|
|
267
|
+
if (!this.gitPaths)
|
|
268
|
+
return;
|
|
269
|
+
// Watch the directory containing HEAD, not HEAD itself.
|
|
270
|
+
// Git uses atomic writes (write temp, rename over HEAD), which changes the inode.
|
|
271
|
+
// fs.watch on a file stops working after the inode changes.
|
|
272
|
+
this.headWatcher = watchWithErrorHandler(dirname(this.gitPaths.headPath), (_eventType, filename) => {
|
|
273
|
+
if (!filename || filename === "HEAD") {
|
|
274
|
+
this.scheduleRefresh();
|
|
275
|
+
}
|
|
276
|
+
}, () => this.handleGitWatcherError());
|
|
277
|
+
if (!this.headWatcher) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// In reftable repos, branch switches update files in the reftable directory
|
|
281
|
+
// instead of HEAD. Watch it separately so the footer picks up those changes.
|
|
282
|
+
const reftableDir = join(this.gitPaths.commonGitDir, "reftable");
|
|
283
|
+
if (existsSync(reftableDir)) {
|
|
284
|
+
this.reftableWatcher = watchWithErrorHandler(reftableDir, () => {
|
|
285
|
+
this.scheduleRefresh();
|
|
286
|
+
}, () => this.handleGitWatcherError());
|
|
287
|
+
if (!this.reftableWatcher) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const tablesListPath = join(reftableDir, "tables.list");
|
|
291
|
+
if (existsSync(tablesListPath)) {
|
|
292
|
+
this.reftableTablesListPath = tablesListPath;
|
|
293
|
+
this.reftableTablesListWatcher = watchWithErrorHandler(tablesListPath, () => {
|
|
294
|
+
this.scheduleRefresh();
|
|
295
|
+
}, () => this.handleGitWatcherError());
|
|
296
|
+
if (!this.reftableTablesListWatcher) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
watchFile(tablesListPath, { interval: 250 }, (current, previous) => {
|
|
300
|
+
if (current.mtimeMs !== previous.mtimeMs ||
|
|
301
|
+
current.ctimeMs !== previous.ctimeMs ||
|
|
302
|
+
current.size !== previous.size) {
|
|
303
|
+
this.scheduleRefresh();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as undici from "undici";
|
|
2
|
+
export const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
|
|
3
|
+
export const HTTP_IDLE_TIMEOUT_CHOICES = [
|
|
4
|
+
{ label: "30 sec", timeoutMs: 30_000 },
|
|
5
|
+
{ label: "1 min", timeoutMs: 60_000 },
|
|
6
|
+
{ label: "2 min", timeoutMs: 120_000 },
|
|
7
|
+
{ label: "5 min", timeoutMs: 300_000 },
|
|
8
|
+
{ label: "disabled", timeoutMs: 0 },
|
|
9
|
+
];
|
|
10
|
+
export function parseHttpIdleTimeoutMs(value) {
|
|
11
|
+
if (typeof value === "string") {
|
|
12
|
+
const trimmed = value.trim();
|
|
13
|
+
if (trimmed.toLowerCase() === "disabled") {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
if (trimmed.length === 0) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return parseHttpIdleTimeoutMs(Number(trimmed));
|
|
20
|
+
}
|
|
21
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return Math.floor(value);
|
|
25
|
+
}
|
|
26
|
+
export function formatHttpIdleTimeoutMs(timeoutMs) {
|
|
27
|
+
const choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs);
|
|
28
|
+
if (choice) {
|
|
29
|
+
return choice.label;
|
|
30
|
+
}
|
|
31
|
+
return `${timeoutMs / 1000} sec`;
|
|
32
|
+
}
|
|
33
|
+
export function configureHttpDispatcher(timeoutMs = DEFAULT_HTTP_IDLE_TIMEOUT_MS) {
|
|
34
|
+
const normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);
|
|
35
|
+
if (normalizedTimeoutMs === undefined) {
|
|
36
|
+
throw new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);
|
|
37
|
+
}
|
|
38
|
+
undici.setGlobalDispatcher(new undici.EnvHttpProxyAgent({
|
|
39
|
+
allowH2: false,
|
|
40
|
+
bodyTimeout: normalizedTimeoutMs,
|
|
41
|
+
headersTimeout: normalizedTimeoutMs,
|
|
42
|
+
}));
|
|
43
|
+
// Keep fetch and the dispatcher on the same undici implementation. Node 26.0's
|
|
44
|
+
// bundled fetch can otherwise consume compressed responses through npm undici's
|
|
45
|
+
// dispatcher without decompressing them, causing response.json() failures.
|
|
46
|
+
undici.install?.();
|
|
47
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core modules shared between all run modes.
|
|
3
|
+
*/
|
|
4
|
+
export { AgentSession, } from "./agent-session.js";
|
|
5
|
+
export { AgentSessionRuntime, createAgentSessionRuntime, } from "./agent-session-runtime.js";
|
|
6
|
+
export { createAgentSessionFromServices, createAgentSessionServices, } from "./agent-session-services.js";
|
|
7
|
+
export { executeBashWithOperations } from "./bash-executor.js";
|
|
8
|
+
export { createEventBus } from "./event-bus.js";
|
|
9
|
+
// Extensions system
|
|
10
|
+
export { defineTool, discoverAndLoadExtensions, ExtensionRunner, } from "./extensions/index.js";
|
|
11
|
+
export { createSyntheticSourceInfo } from "./source-info.js";
|