@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
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.7.2] - 2026-05-25
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Version bump for monorepo release
|
|
12
|
+
|
|
8
13
|
### Added
|
|
9
14
|
- Admin OpenRouter catalog picker in `/studio/admin/models` (Base Models tab):
|
|
10
15
|
search across 368 models from `https://openrouter.ai/api/v1/models` and add
|
package/dist/agent/agents.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import * as fs from "node:fs";
|
|
19
19
|
import * as path from "node:path";
|
|
20
|
-
import { getAgentDir, parseFrontmatter } from "
|
|
20
|
+
import { getAgentDir, parseFrontmatter } from "../pi/coding-agent/index.js";
|
|
21
21
|
function loadAgentsFromDir(dir, source) {
|
|
22
22
|
const agents = [];
|
|
23
23
|
if (!fs.existsSync(dir)) {
|
package/dist/agent/index.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Subagent Delegation Extension for Spectral
|
|
3
3
|
*
|
|
4
4
|
* Registers a `subagent` tool that allows the coding agent to delegate tasks
|
|
5
|
-
* to specialized sub-agents running in
|
|
5
|
+
* to specialized sub-agents running in-process via `agentLoop()`.
|
|
6
6
|
*
|
|
7
|
-
* Each subagent invocation
|
|
8
|
-
* window, keeping the main conversation lean and focused.
|
|
7
|
+
* Each subagent invocation runs with its own fresh AgentContext (isolated
|
|
8
|
+
* context window), keeping the main conversation lean and focused.
|
|
9
9
|
*
|
|
10
10
|
* Three execution modes:
|
|
11
11
|
* - Single: { agent: "name", task: "..." }
|
|
@@ -16,20 +16,25 @@
|
|
|
16
16
|
* - ~/.pi/agent/agents/*.md (user-level, always loaded)
|
|
17
17
|
* - .pi/agents/*.md (project-level, opt-in via agentScope)
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
* -
|
|
21
|
-
* -
|
|
19
|
+
* Subagents use an in-process `agentLoop()` for:
|
|
20
|
+
* - Clean context isolation (fresh messages array)
|
|
21
|
+
* - Combined system prompt (main agent + subagent-specific instructions)
|
|
22
|
+
* - Tool filtering (only the tools specified in agent config)
|
|
23
|
+
* - Model selection (session model preferred, agent-configured as fallback)
|
|
24
|
+
* - Signal propagation (abort passthrough)
|
|
25
|
+
* - Access to observational memory via the recall tool
|
|
22
26
|
*/
|
|
23
|
-
import {
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import * as path from "node:path";
|
|
27
|
-
import { StringEnum } from "@mariozechner/pi-ai";
|
|
28
|
-
import { withFileMutationQueue, } from "@mariozechner/pi-coding-agent";
|
|
29
|
-
import { Text } from "@mariozechner/pi-tui";
|
|
27
|
+
import { agentLoop } from "../pi/agent-core/index.js";
|
|
28
|
+
import { StringEnum } from "../pi/ai/index.js";
|
|
29
|
+
import { Text } from "../pi/tui/index.js";
|
|
30
30
|
import { Type } from "typebox";
|
|
31
31
|
import { discoverAgents } from "./agents.js";
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
|
+
// Built-in tool resolution (no spawn needed — tools are registered in-process)
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
import { createTool, allToolNames } from "../pi/coding-agent/core/tools/index.js";
|
|
36
|
+
import { wrapToolDefinition } from "../pi/coding-agent/core/tools/tool-definition-wrapper.js";
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
33
38
|
// Constants
|
|
34
39
|
// ---------------------------------------------------------------------------
|
|
35
40
|
const MAX_PARALLEL_TASKS = 8;
|
|
@@ -83,7 +88,72 @@ function getFinalOutput(messages) {
|
|
|
83
88
|
return "";
|
|
84
89
|
}
|
|
85
90
|
// ---------------------------------------------------------------------------
|
|
86
|
-
// Helpers —
|
|
91
|
+
// Helpers — model resolution
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a model string (e.g. "claude-sonnet-4-5" or "anthropic/claude-sonnet-4-5")
|
|
95
|
+
* to a Model object from the registry.
|
|
96
|
+
*/
|
|
97
|
+
function resolveModelString(modelStr, modelRegistry) {
|
|
98
|
+
if (!modelStr)
|
|
99
|
+
return undefined;
|
|
100
|
+
const allModels = modelRegistry.getAll();
|
|
101
|
+
// Try "provider/modelId" format
|
|
102
|
+
const slashIdx = modelStr.indexOf("/");
|
|
103
|
+
if (slashIdx !== -1) {
|
|
104
|
+
const provider = modelStr.substring(0, slashIdx);
|
|
105
|
+
const modelId = modelStr.substring(slashIdx + 1);
|
|
106
|
+
const match = allModels.find((m) => m.provider.toLowerCase() === provider.toLowerCase() && m.id.toLowerCase() === modelId.toLowerCase());
|
|
107
|
+
if (match)
|
|
108
|
+
return match;
|
|
109
|
+
}
|
|
110
|
+
// Try exact model ID match
|
|
111
|
+
const idMatch = allModels.find((m) => m.id.toLowerCase() === modelStr.toLowerCase());
|
|
112
|
+
if (idMatch)
|
|
113
|
+
return idMatch;
|
|
114
|
+
// Try fuzzy match (model ID contains the string)
|
|
115
|
+
const fuzzy = allModels.find((m) => m.id.toLowerCase().includes(modelStr.toLowerCase()));
|
|
116
|
+
return fuzzy;
|
|
117
|
+
}
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Helpers — tool resolution
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
/**
|
|
122
|
+
* Resolve tool names from an agent definition into AgentTool objects.
|
|
123
|
+
*
|
|
124
|
+
* Built-in tools (read, bash, edit, write, grep, find, ls) are created via
|
|
125
|
+
* `createTool()`. Extension-registered tools (e.g., recall) are resolved
|
|
126
|
+
* via the ExtensionAPI and wrapped.
|
|
127
|
+
*/
|
|
128
|
+
function resolveSubagentTools(toolNames, cwd, pi, ctx) {
|
|
129
|
+
if (!toolNames || toolNames.length === 0)
|
|
130
|
+
return [];
|
|
131
|
+
const tools = [];
|
|
132
|
+
for (const name of toolNames) {
|
|
133
|
+
// Built-in tools
|
|
134
|
+
if (allToolNames.has(name)) {
|
|
135
|
+
try {
|
|
136
|
+
const tool = createTool(name, cwd);
|
|
137
|
+
tools.push(tool);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Tool creation failed — skip
|
|
141
|
+
}
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
// Extension-registered tools (recall, designer_*, etc.)
|
|
145
|
+
// pi at runtime is the embedded ExtensionAPI which has getToolDefinition;
|
|
146
|
+
// the npm types don't expose it, so we cast.
|
|
147
|
+
const richPi = pi;
|
|
148
|
+
const toolDef = richPi.getToolDefinition(name);
|
|
149
|
+
if (toolDef) {
|
|
150
|
+
tools.push(wrapToolDefinition(toolDef, () => ctx));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return tools;
|
|
154
|
+
}
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Helpers — concurrency
|
|
87
157
|
// ---------------------------------------------------------------------------
|
|
88
158
|
async function mapWithConcurrencyLimit(items, concurrency, fn) {
|
|
89
159
|
if (items.length === 0)
|
|
@@ -102,65 +172,10 @@ async function mapWithConcurrencyLimit(items, concurrency, fn) {
|
|
|
102
172
|
await Promise.all(workers);
|
|
103
173
|
return results;
|
|
104
174
|
}
|
|
105
|
-
async function writePromptToTempFile(agentName, prompt) {
|
|
106
|
-
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "spectral-subagent-"));
|
|
107
|
-
const safeName = agentName.replace(/[^\w.-]+/g, "_");
|
|
108
|
-
const filePath = path.join(tmpDir, `prompt-${safeName}.md`);
|
|
109
|
-
await withFileMutationQueue(filePath, async () => {
|
|
110
|
-
await fs.promises.writeFile(filePath, prompt, { encoding: "utf-8", mode: 0o600 });
|
|
111
|
-
});
|
|
112
|
-
return { dir: tmpDir, filePath };
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Detect the correct binary for spawning subagent processes.
|
|
116
|
-
*
|
|
117
|
-
* Strategy:
|
|
118
|
-
* 1. If `process.argv[1]` points to an existing script (unlikely in bundled
|
|
119
|
-
* env but possible during dev with tsx), use that script with node.
|
|
120
|
-
* 2. If the executor is named `spectral` or `aexol`, use it directly (the
|
|
121
|
-
* wrapper ensures MCP + memory extensions are auto-loaded).
|
|
122
|
-
* 3. Otherwise fall back to `pi`.
|
|
123
|
-
*/
|
|
124
|
-
function getPiInvocation(subagentArgs) {
|
|
125
|
-
const currentScript = process.argv[1];
|
|
126
|
-
// Case 1: running via tsx with an existing script file
|
|
127
|
-
if (currentScript && fs.existsSync(currentScript)) {
|
|
128
|
-
return { command: process.execPath, args: [currentScript, ...subagentArgs] };
|
|
129
|
-
}
|
|
130
|
-
// Case 2: check if this is the spectral/aexol wrapper binary
|
|
131
|
-
const execName = path.basename(process.execPath).toLowerCase();
|
|
132
|
-
if (execName === "spectral" || execName === "aexol") {
|
|
133
|
-
return { command: process.execPath, args: subagentArgs };
|
|
134
|
-
}
|
|
135
|
-
// Case 3: generic node — try spectral first, then pi
|
|
136
|
-
function hasBin(name) {
|
|
137
|
-
const PATH = process.env.PATH || "";
|
|
138
|
-
const envPathSep = process.platform === "win32" ? ";" : ":";
|
|
139
|
-
for (const dir of PATH.split(envPathSep)) {
|
|
140
|
-
const candidate = path.join(dir, name);
|
|
141
|
-
try {
|
|
142
|
-
if (fs.existsSync(candidate))
|
|
143
|
-
return true;
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
/* skip */
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
if (hasBin("spectral")) {
|
|
152
|
-
return { command: "spectral", args: subagentArgs };
|
|
153
|
-
}
|
|
154
|
-
if (hasBin("pi")) {
|
|
155
|
-
return { command: "pi", args: subagentArgs };
|
|
156
|
-
}
|
|
157
|
-
// Last resort: use node with pi from node_modules (matches pi's own fallback)
|
|
158
|
-
return { command: "pi", args: subagentArgs };
|
|
159
|
-
}
|
|
160
175
|
// ---------------------------------------------------------------------------
|
|
161
|
-
// Core — run a single subagent
|
|
176
|
+
// Core — run a single subagent in-process via agentLoop
|
|
162
177
|
// ---------------------------------------------------------------------------
|
|
163
|
-
async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
|
|
178
|
+
async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, pi, ctx) {
|
|
164
179
|
const agent = agents.find((a) => a.name === agentName);
|
|
165
180
|
if (!agent) {
|
|
166
181
|
const available = agents.map((a) => `"${a.name}"`).join(", ") || "none";
|
|
@@ -175,13 +190,47 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
175
190
|
step,
|
|
176
191
|
};
|
|
177
192
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
193
|
+
// Build combined system prompt: main agent's + subagent's
|
|
194
|
+
const mainSystemPrompt = ctx.getSystemPrompt();
|
|
195
|
+
const combinedPrompt = mainSystemPrompt
|
|
196
|
+
? `${mainSystemPrompt}\n\n---\n\nSubagent: ${agent.name}\n${agent.systemPrompt}`
|
|
197
|
+
: agent.systemPrompt;
|
|
198
|
+
// Resolve model — session model preferred, agent-configured as fallback
|
|
199
|
+
const sessionModel = ctx.model && ctx.modelRegistry.hasConfiguredAuth(ctx.model) ? ctx.model : undefined;
|
|
200
|
+
const model = sessionModel ?? resolveModelString(agent.model, ctx.modelRegistry);
|
|
201
|
+
if (!model) {
|
|
202
|
+
return {
|
|
203
|
+
agent: agentName,
|
|
204
|
+
agentSource: agent.source,
|
|
205
|
+
task,
|
|
206
|
+
exitCode: 1,
|
|
207
|
+
messages: [],
|
|
208
|
+
stderr: `Cannot resolve model "${agent.model ?? "(none specified)"}". Make sure the model is configured.`,
|
|
209
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
210
|
+
model: agent.model,
|
|
211
|
+
step,
|
|
212
|
+
errorMessage: `Model not found: ${agent.model ?? "unspecified"}`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
// Resolve auth for the model
|
|
216
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
217
|
+
if (!auth.ok || !auth.apiKey) {
|
|
218
|
+
return {
|
|
219
|
+
agent: agentName,
|
|
220
|
+
agentSource: agent.source,
|
|
221
|
+
task,
|
|
222
|
+
exitCode: 1,
|
|
223
|
+
messages: [],
|
|
224
|
+
stderr: `No API key configured for model "${model.provider}/${model.id}".`,
|
|
225
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
226
|
+
model: agent.model,
|
|
227
|
+
step,
|
|
228
|
+
errorMessage: `No API key for ${model.provider}/${model.id}`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
// Resolve tools
|
|
232
|
+
const effectiveCwd = cwd ?? defaultCwd;
|
|
233
|
+
const tools = resolveSubagentTools(agent.tools, effectiveCwd, pi, ctx);
|
|
185
234
|
const currentResult = {
|
|
186
235
|
agent: agentName,
|
|
187
236
|
agentSource: agent.source,
|
|
@@ -190,7 +239,7 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
190
239
|
messages: [],
|
|
191
240
|
stderr: "",
|
|
192
241
|
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
193
|
-
model:
|
|
242
|
+
model: model.id,
|
|
194
243
|
step,
|
|
195
244
|
};
|
|
196
245
|
const emitUpdate = () => {
|
|
@@ -202,113 +251,79 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
202
251
|
}
|
|
203
252
|
};
|
|
204
253
|
try {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
currentResult.usage.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (msg.stopReason)
|
|
248
|
-
currentResult.stopReason = msg.stopReason;
|
|
249
|
-
if (msg.errorMessage)
|
|
250
|
-
currentResult.errorMessage = msg.errorMessage;
|
|
254
|
+
const config = {
|
|
255
|
+
model,
|
|
256
|
+
apiKey: auth.apiKey,
|
|
257
|
+
headers: auth.headers,
|
|
258
|
+
convertToLlm: (msgs) => msgs.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult"),
|
|
259
|
+
toolExecution: "parallel",
|
|
260
|
+
};
|
|
261
|
+
const stream = agentLoop([{ role: "user", content: [{ type: "text", text: task }], timestamp: Date.now() }], {
|
|
262
|
+
systemPrompt: combinedPrompt,
|
|
263
|
+
messages: [],
|
|
264
|
+
tools,
|
|
265
|
+
}, config, signal);
|
|
266
|
+
// Collect messages from the stream
|
|
267
|
+
const collectedMessages = [];
|
|
268
|
+
let hadError = false;
|
|
269
|
+
for await (const event of stream) {
|
|
270
|
+
if (signal?.aborted) {
|
|
271
|
+
hadError = true;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
if (event.type === "message_end" && event.message) {
|
|
275
|
+
const msg = event.message;
|
|
276
|
+
collectedMessages.push(msg);
|
|
277
|
+
if (msg.role === "assistant") {
|
|
278
|
+
currentResult.usage.turns++;
|
|
279
|
+
const usage = msg.usage;
|
|
280
|
+
if (usage) {
|
|
281
|
+
currentResult.usage.input += usage.input || 0;
|
|
282
|
+
currentResult.usage.output += usage.output || 0;
|
|
283
|
+
currentResult.usage.cacheRead += usage.cacheRead || 0;
|
|
284
|
+
currentResult.usage.cacheWrite += usage.cacheWrite || 0;
|
|
285
|
+
currentResult.usage.cost += usage.cost?.total || 0;
|
|
286
|
+
currentResult.usage.contextTokens = usage.totalTokens || 0;
|
|
287
|
+
}
|
|
288
|
+
if (!currentResult.model && msg.model)
|
|
289
|
+
currentResult.model = msg.model;
|
|
290
|
+
if (msg.stopReason)
|
|
291
|
+
currentResult.stopReason = msg.stopReason;
|
|
292
|
+
if (msg.errorMessage)
|
|
293
|
+
currentResult.errorMessage = msg.errorMessage;
|
|
294
|
+
if (msg.stopReason === "error" || msg.stopReason === "aborted") {
|
|
295
|
+
hadError = true;
|
|
251
296
|
}
|
|
252
|
-
emitUpdate();
|
|
253
|
-
}
|
|
254
|
-
if (event.type === "tool_result_end" && event.message) {
|
|
255
|
-
currentResult.messages.push(event.message);
|
|
256
|
-
emitUpdate();
|
|
257
297
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
for (const line of lines)
|
|
264
|
-
processLine(line);
|
|
265
|
-
});
|
|
266
|
-
proc.stderr.on("data", (data) => {
|
|
267
|
-
currentResult.stderr += data.toString();
|
|
268
|
-
});
|
|
269
|
-
proc.on("close", (code) => {
|
|
270
|
-
if (buffer.trim())
|
|
271
|
-
processLine(buffer);
|
|
272
|
-
resolve(code ?? 0);
|
|
273
|
-
});
|
|
274
|
-
proc.on("error", () => {
|
|
275
|
-
resolve(1);
|
|
276
|
-
});
|
|
277
|
-
if (signal) {
|
|
278
|
-
const killProc = () => {
|
|
279
|
-
wasAborted = true;
|
|
280
|
-
proc.kill("SIGTERM");
|
|
281
|
-
setTimeout(() => {
|
|
282
|
-
if (!proc.killed)
|
|
283
|
-
proc.kill("SIGKILL");
|
|
284
|
-
}, 5000);
|
|
285
|
-
};
|
|
286
|
-
if (signal.aborted)
|
|
287
|
-
killProc();
|
|
288
|
-
else
|
|
289
|
-
signal.addEventListener("abort", killProc, { once: true });
|
|
298
|
+
currentResult.messages = [...collectedMessages];
|
|
299
|
+
emitUpdate();
|
|
300
|
+
}
|
|
301
|
+
if (event.type === "tool_execution_end" && event.isError) {
|
|
302
|
+
emitUpdate();
|
|
290
303
|
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
304
|
+
}
|
|
305
|
+
// Drain the stream result (already consumed by iteration, but ensure cleanup)
|
|
306
|
+
try {
|
|
307
|
+
await stream.result();
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// Stream result error — already handled by message events
|
|
311
|
+
}
|
|
312
|
+
currentResult.exitCode = hadError ? 1 : 0;
|
|
313
|
+
if (signal?.aborted) {
|
|
314
|
+
currentResult.exitCode = 1;
|
|
315
|
+
currentResult.stopReason = "aborted";
|
|
316
|
+
currentResult.errorMessage = "Subagent was aborted";
|
|
317
|
+
}
|
|
295
318
|
return currentResult;
|
|
296
319
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
if (tmpPromptDir)
|
|
306
|
-
try {
|
|
307
|
-
fs.rmdirSync(tmpPromptDir);
|
|
308
|
-
}
|
|
309
|
-
catch {
|
|
310
|
-
/* ignore */
|
|
311
|
-
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
322
|
+
currentResult.exitCode = 1;
|
|
323
|
+
currentResult.stopReason = "error";
|
|
324
|
+
currentResult.errorMessage = errorMsg;
|
|
325
|
+
currentResult.stderr = errorMsg;
|
|
326
|
+
return currentResult;
|
|
312
327
|
}
|
|
313
328
|
}
|
|
314
329
|
// ---------------------------------------------------------------------------
|
|
@@ -443,7 +458,7 @@ export default function subagentExtension(pi) {
|
|
|
443
458
|
}
|
|
444
459
|
}
|
|
445
460
|
: undefined;
|
|
446
|
-
const result = await runSingleAgent(ctx.cwd, agents, step.agent, taskWithContext, step.cwd, i + 1, signal, chainUpdate, makeDetails("chain"));
|
|
461
|
+
const result = await runSingleAgent(ctx.cwd, agents, step.agent, taskWithContext, step.cwd, i + 1, signal, chainUpdate, makeDetails("chain"), pi, ctx);
|
|
447
462
|
results.push(result);
|
|
448
463
|
const isError = result.exitCode !== 0 ||
|
|
449
464
|
result.stopReason === "error" ||
|
|
@@ -521,7 +536,7 @@ export default function subagentExtension(pi) {
|
|
|
521
536
|
allResults[index] = partial.details.results[0];
|
|
522
537
|
emitParallelUpdate();
|
|
523
538
|
}
|
|
524
|
-
}, makeDetails("parallel"));
|
|
539
|
+
}, makeDetails("parallel"), pi, ctx);
|
|
525
540
|
allResults[index] = result;
|
|
526
541
|
emitParallelUpdate();
|
|
527
542
|
return result;
|
|
@@ -544,7 +559,7 @@ export default function subagentExtension(pi) {
|
|
|
544
559
|
}
|
|
545
560
|
// ── Single mode ─────────────────────────────────────────────
|
|
546
561
|
if (params.agent && params.task) {
|
|
547
|
-
const result = await runSingleAgent(ctx.cwd, agents, params.agent, params.task, params.cwd, undefined, signal, onUpdate, makeDetails("single"));
|
|
562
|
+
const result = await runSingleAgent(ctx.cwd, agents, params.agent, params.task, params.cwd, undefined, signal, onUpdate, makeDetails("single"), pi, ctx);
|
|
548
563
|
const isError = result.exitCode !== 0 ||
|
|
549
564
|
result.stopReason === "error" ||
|
|
550
565
|
result.stopReason === "aborted";
|
package/dist/commands/serve.js
CHANGED
|
@@ -181,9 +181,6 @@ export async function runServe(opts = {}) {
|
|
|
181
181
|
backendUrl,
|
|
182
182
|
machineJwt: registration.record.machineJwt,
|
|
183
183
|
bridgeFactory: opts.bridgeFactory,
|
|
184
|
-
titleLlmCall: opts.titleLlmCall,
|
|
185
|
-
disableAutoTitle: opts.disableAutoTitle,
|
|
186
|
-
publishMetaEvent: deferredPublishMetaEvent,
|
|
187
184
|
});
|
|
188
185
|
if (!silent) {
|
|
189
186
|
const action = registration.reused ? "Reusing" : "Registered";
|
|
@@ -25,7 +25,7 @@ Typography is unified under NouvelR — a proprietary geometric sans-serif desig
|
|
|
25
25
|
## 2. Color Palette & Roles
|
|
26
26
|
|
|
27
27
|
### Primary
|
|
28
|
-
- **Renault Yellow** (`#EFDF00`): The brand's signature Pantone — a vivid, saturated yellow used for super-primary CTAs and the highest-priority action buttons. Appears as `--
|
|
28
|
+
- **Renault Yellow** (`#EFDF00`): The brand's signature Pantone — a vivid, saturated yellow used for super-primary CTAs and the highest-priority action buttons. Appears as `--CtaButton-background-color` on `.is-cta-super-primary` class. Carries the energy of the diamond logo
|
|
29
29
|
- **Absolute Black** (`#000000`): Primary button background, heading text on light surfaces, and the dominant dark section surface. The structural anchor of the entire interface
|
|
30
30
|
- **Pure White** (`#FFFFFF`): Primary surface for editorial content, inverted button backgrounds, hero text color, and the dominant light-mode canvas (--rt-color-white)
|
|
31
31
|
|