@pencil-agent/nano-pencil 1.11.36 → 1.11.37
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/builtin-extensions.js +10 -0
- package/dist/core/i18n/slash-commands.d.ts +1 -0
- package/dist/core/i18n/slash-commands.js +1 -0
- package/dist/core/i18n/slash-commands.zh.d.ts +1 -0
- package/dist/core/i18n/slash-commands.zh.js +1 -0
- package/dist/core/runtime/agent-session.d.ts +2 -0
- package/dist/core/runtime/agent-session.js +6 -0
- package/dist/core/runtime/sdk.d.ts +2 -0
- package/dist/core/runtime/sdk.js +1 -0
- package/dist/core/skills.js +6 -2
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/sub-agent/index.d.ts +7 -0
- package/dist/core/sub-agent/index.js +6 -0
- package/dist/core/sub-agent/sub-agent-backend.d.ts +14 -0
- package/dist/core/sub-agent/sub-agent-backend.js +119 -0
- package/dist/core/sub-agent/sub-agent-runtime.d.ts +38 -0
- package/dist/core/sub-agent/sub-agent-runtime.js +54 -0
- package/dist/core/sub-agent/sub-agent-types.d.ts +75 -0
- package/dist/core/sub-agent/sub-agent-types.js +8 -0
- package/dist/core/tools/bash.d.ts +11 -0
- package/dist/core/tools/bash.js +51 -0
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.js +1 -1
- package/dist/core/workspace/index.d.ts +6 -0
- package/dist/core/workspace/index.js +5 -0
- package/dist/core/workspace/worktree-manager.d.ts +51 -0
- package/dist/core/workspace/worktree-manager.js +124 -0
- package/dist/extensions/defaults/loop/scheduler-parser.js +6 -9
- package/dist/extensions/defaults/subagent/index.d.ts +8 -0
- package/dist/extensions/defaults/subagent/index.js +118 -0
- package/dist/extensions/defaults/subagent/subagent-parser.d.ts +25 -0
- package/dist/extensions/defaults/subagent/subagent-parser.js +66 -0
- package/dist/extensions/defaults/subagent/subagent-runner.d.ts +41 -0
- package/dist/extensions/defaults/subagent/subagent-runner.js +171 -0
- package/dist/extensions/defaults/subagent/subagent-types.d.ts +52 -0
- package/dist/extensions/defaults/subagent/subagent-types.js +7 -0
- package/dist/extensions/defaults/team/index.js +1 -1
- package/dist/migrations.js +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.js +71 -1
- package/docs/AgentTeam/351/207/215/346/236/204/346/226/271/346/241/210.md +43 -7
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ const BUNDLED_PRESENCE_EXTENSION = join(__dirname, "extensions", "defaults", "pr
|
|
|
20
20
|
const BUNDLED_INTERVIEW_EXTENSION = join(__dirname, "extensions", "defaults", "interview", "index.js");
|
|
21
21
|
const BUNDLED_LOOP_EXTENSION = join(__dirname, "extensions", "defaults", "loop", "index.js");
|
|
22
22
|
const BUNDLED_TEAM_EXTENSION = join(__dirname, "extensions", "defaults", "team", "index.js");
|
|
23
|
+
const BUNDLED_SUBAGENT_EXTENSION = join(__dirname, "extensions", "defaults", "subagent", "index.js");
|
|
23
24
|
const BUNDLED_MCP_EXTENSION = join(__dirname, "extensions", "defaults", "mcp", "index.js");
|
|
24
25
|
const BUNDLED_EXPORT_HTML_EXTENSION = join(__dirname, "extensions", "optional", "export-html", "index.js");
|
|
25
26
|
/** Find package root from current module location (containing package.json with nano-pencil related name) */
|
|
@@ -157,6 +158,15 @@ export function getBuiltinExtensionPaths() {
|
|
|
157
158
|
if (existsSync(teamTs))
|
|
158
159
|
paths.push(teamTs);
|
|
159
160
|
}
|
|
161
|
+
// Built-in SubAgent extension
|
|
162
|
+
if (existsSync(BUNDLED_SUBAGENT_EXTENSION)) {
|
|
163
|
+
paths.push(BUNDLED_SUBAGENT_EXTENSION);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const subagentTs = join(__dirname, "extensions", "defaults", "subagent", "index.ts");
|
|
167
|
+
if (existsSync(subagentTs))
|
|
168
|
+
paths.push(subagentTs);
|
|
169
|
+
}
|
|
160
170
|
// Built-in MCP extension
|
|
161
171
|
if (existsSync(BUNDLED_MCP_EXTENSION)) {
|
|
162
172
|
paths.push(BUNDLED_MCP_EXTENSION);
|
|
@@ -28,6 +28,7 @@ export const slashCommands = {
|
|
|
28
28
|
logout: "Logout from OAuth provider",
|
|
29
29
|
new: "Start a new session",
|
|
30
30
|
update: "Check for NanoPencil updates",
|
|
31
|
+
reinstall: "Force reinstall NanoPencil (clean install)",
|
|
31
32
|
compact: "Manually compact the session context",
|
|
32
33
|
resume: "Resume a different session",
|
|
33
34
|
reload: "Reload extensions, skills, prompts, and themes",
|
|
@@ -90,6 +90,8 @@ export interface AgentSessionConfig {
|
|
|
90
90
|
extensionRunnerRef?: {
|
|
91
91
|
current?: ExtensionRunner;
|
|
92
92
|
};
|
|
93
|
+
/** External abort signal for stopping the session (e.g., from SubAgent runtime) */
|
|
94
|
+
signal?: AbortSignal;
|
|
93
95
|
}
|
|
94
96
|
export interface ExtensionBindings {
|
|
95
97
|
uiContext?: ExtensionUIContext;
|
|
@@ -154,6 +154,12 @@ export class AgentSession {
|
|
|
154
154
|
// Always subscribe to agent events for internal handling
|
|
155
155
|
// (session persistence, extensions, auto-compaction, retry logic)
|
|
156
156
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
157
|
+
// Listen to external abort signal (e.g., from SubAgent runtime)
|
|
158
|
+
if (config.signal) {
|
|
159
|
+
config.signal.addEventListener("abort", () => {
|
|
160
|
+
this.abort();
|
|
161
|
+
});
|
|
162
|
+
}
|
|
157
163
|
this._buildRuntime({
|
|
158
164
|
activeToolNames: this._initialActiveToolNames,
|
|
159
165
|
includeAllExtensionTools: true,
|
|
@@ -40,6 +40,8 @@ export interface CreateAgentSessionOptions {
|
|
|
40
40
|
sessionManager?: SessionManager;
|
|
41
41
|
/** Settings manager. Default: SettingsManager.create(cwd, agentDir) */
|
|
42
42
|
settingsManager?: SettingsManager;
|
|
43
|
+
/** External abort signal for stopping the session (e.g., from SubAgent runtime) */
|
|
44
|
+
signal?: AbortSignal;
|
|
43
45
|
}
|
|
44
46
|
/** Result from createAgentSession */
|
|
45
47
|
export interface CreateAgentSessionResult {
|
package/dist/core/runtime/sdk.js
CHANGED
package/dist/core/skills.js
CHANGED
|
@@ -56,7 +56,9 @@ function addIgnoreRules(ig, dir, rootDir) {
|
|
|
56
56
|
ig.add(patterns);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
catch {
|
|
59
|
+
catch (e) {
|
|
60
|
+
console.warn("Warning: Failed to read ignore file:", e instanceof Error ? e.message : e);
|
|
61
|
+
}
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
@@ -166,7 +168,9 @@ function loadSkillsFromDirInternal(dir, source, includeRootFiles, ignoreMatcher,
|
|
|
166
168
|
diagnostics.push(...result.diagnostics);
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
|
-
catch {
|
|
171
|
+
catch (e) {
|
|
172
|
+
console.warn("Warning: Failed to read skills directory:", e instanceof Error ? e.message : e);
|
|
173
|
+
}
|
|
170
174
|
return { skills, diagnostics };
|
|
171
175
|
}
|
|
172
176
|
function loadSkillFromFile(filePath, source) {
|
|
@@ -22,6 +22,7 @@ export const BUILTIN_SLASH_COMMANDS = [
|
|
|
22
22
|
{ name: "logout", descriptionKey: "slash.logout" },
|
|
23
23
|
{ name: "new", descriptionKey: "slash.new" },
|
|
24
24
|
{ name: "update", descriptionKey: "slash.update" },
|
|
25
|
+
{ name: "reinstall", descriptionKey: "slash.reinstall" },
|
|
25
26
|
{ name: "compact", descriptionKey: "slash.compact" },
|
|
26
27
|
{ name: "resume", descriptionKey: "slash.resume" },
|
|
27
28
|
{ name: "reload", descriptionKey: "slash.reload" },
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SubAgent runtime exports.
|
|
3
|
+
*/
|
|
4
|
+
export { SubAgentRuntime, subAgentRuntime } from "./sub-agent-runtime.js";
|
|
5
|
+
export { InProcessSubAgentBackend } from "./sub-agent-backend.js";
|
|
6
|
+
export type { SubAgentSpec, SubAgentResult, SubAgentHandle, SubAgentBackend, } from "./sub-agent-types.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [UPSTREAM]: Depends on sub-agent-types.ts, core/runtime/sdk.ts
|
|
3
|
+
* [SURFACE]: InProcessSubAgentBackend
|
|
4
|
+
* [LOCUS]: core/sub-agent/sub-agent-backend.ts
|
|
5
|
+
*/
|
|
6
|
+
import type { SubAgentBackend, SubAgentHandle, SubAgentSpec } from "./sub-agent-types.js";
|
|
7
|
+
/**
|
|
8
|
+
* In-process SubAgent backend.
|
|
9
|
+
* Wraps createAgentSession() to run SubAgent in the same process.
|
|
10
|
+
*/
|
|
11
|
+
export declare class InProcessSubAgentBackend implements SubAgentBackend {
|
|
12
|
+
spawn(spec: SubAgentSpec): Promise<SubAgentHandle>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=sub-agent-backend.d.ts.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [UPSTREAM]: Depends on sub-agent-types.ts, core/runtime/sdk.ts
|
|
3
|
+
* [SURFACE]: InProcessSubAgentBackend
|
|
4
|
+
* [LOCUS]: core/sub-agent/sub-agent-backend.ts
|
|
5
|
+
*/
|
|
6
|
+
import { createAgentSession } from "../runtime/sdk.js";
|
|
7
|
+
/**
|
|
8
|
+
* In-process SubAgent backend.
|
|
9
|
+
* Wraps createAgentSession() to run SubAgent in the same process.
|
|
10
|
+
*/
|
|
11
|
+
export class InProcessSubAgentBackend {
|
|
12
|
+
async spawn(spec) {
|
|
13
|
+
const id = crypto.randomUUID();
|
|
14
|
+
// Create an internal AbortController that can be triggered by external signal or timeout
|
|
15
|
+
const internalAbortController = new AbortController();
|
|
16
|
+
// Forward external signal abort to internal controller
|
|
17
|
+
const signalHandler = () => {
|
|
18
|
+
if (!internalAbortController.signal.aborted) {
|
|
19
|
+
internalAbortController.abort();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
spec.signal.addEventListener("abort", signalHandler);
|
|
23
|
+
// Create agent session with our internal signal
|
|
24
|
+
const options = {
|
|
25
|
+
cwd: spec.cwd,
|
|
26
|
+
tools: spec.tools,
|
|
27
|
+
signal: internalAbortController.signal,
|
|
28
|
+
model: spec.model,
|
|
29
|
+
};
|
|
30
|
+
const { session } = await createAgentSession(options);
|
|
31
|
+
const timeoutMs = spec.timeoutMs;
|
|
32
|
+
let status = "running";
|
|
33
|
+
let result;
|
|
34
|
+
// Set up timeout if specified
|
|
35
|
+
let timeoutId;
|
|
36
|
+
if (timeoutMs !== undefined) {
|
|
37
|
+
timeoutId = setTimeout(() => {
|
|
38
|
+
if (status === "running") {
|
|
39
|
+
internalAbortController.abort();
|
|
40
|
+
}
|
|
41
|
+
}, timeoutMs);
|
|
42
|
+
}
|
|
43
|
+
// Extract text from assistant message content
|
|
44
|
+
const extractTextFromContent = (content) => {
|
|
45
|
+
if (typeof content === "string")
|
|
46
|
+
return content;
|
|
47
|
+
if (Array.isArray(content)) {
|
|
48
|
+
return content
|
|
49
|
+
.filter((part) => typeof part === "object" && part !== null && "type" in part && part.type === "text" && typeof part.text === "string")
|
|
50
|
+
.map((part) => part.text)
|
|
51
|
+
.join("\n");
|
|
52
|
+
}
|
|
53
|
+
return "";
|
|
54
|
+
};
|
|
55
|
+
// Start the prompt
|
|
56
|
+
const promptPromise = (async () => {
|
|
57
|
+
try {
|
|
58
|
+
await session.prompt(spec.prompt, {
|
|
59
|
+
images: spec.images,
|
|
60
|
+
});
|
|
61
|
+
status = "done";
|
|
62
|
+
// Extract the last assistant message as the result
|
|
63
|
+
const messages = session.messages ?? [];
|
|
64
|
+
const assistantMessages = messages.filter((m) => m.role === "assistant");
|
|
65
|
+
const lastAssistant = assistantMessages[assistantMessages.length - 1];
|
|
66
|
+
const responseText = lastAssistant ? extractTextFromContent(lastAssistant.content) : "";
|
|
67
|
+
result = {
|
|
68
|
+
success: true,
|
|
69
|
+
response: responseText,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
74
|
+
status = "aborted";
|
|
75
|
+
result = {
|
|
76
|
+
success: false,
|
|
77
|
+
error: "Aborted",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
status = "error";
|
|
82
|
+
result = {
|
|
83
|
+
success: false,
|
|
84
|
+
error: error instanceof Error ? error.message : String(error),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
if (timeoutId !== undefined) {
|
|
90
|
+
clearTimeout(timeoutId);
|
|
91
|
+
}
|
|
92
|
+
// Clean up signal handler
|
|
93
|
+
spec.signal.removeEventListener("abort", signalHandler);
|
|
94
|
+
}
|
|
95
|
+
})();
|
|
96
|
+
return {
|
|
97
|
+
id,
|
|
98
|
+
get status() {
|
|
99
|
+
return status;
|
|
100
|
+
},
|
|
101
|
+
async result() {
|
|
102
|
+
await promptPromise;
|
|
103
|
+
return (result ?? {
|
|
104
|
+
success: false,
|
|
105
|
+
error: "No result available",
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
async abort() {
|
|
109
|
+
internalAbortController.abort();
|
|
110
|
+
await session.abort();
|
|
111
|
+
},
|
|
112
|
+
async terminate() {
|
|
113
|
+
internalAbortController.abort();
|
|
114
|
+
await session.abort();
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=sub-agent-backend.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [UPSTREAM]: Depends on sub-agent-types.ts, sub-agent-backend.ts
|
|
3
|
+
* [SURFACE]: SubAgentRuntime - spawn, abort, lifecycle management
|
|
4
|
+
* [LOCUS]: core/sub-agent/sub-agent-runtime.ts
|
|
5
|
+
*/
|
|
6
|
+
import type { SubAgentBackend, SubAgentHandle, SubAgentSpec } from "./sub-agent-types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Runtime for managing SubAgents.
|
|
9
|
+
* Provides spawn/abort/lifecycle operations.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SubAgentRuntime {
|
|
12
|
+
private backend;
|
|
13
|
+
private activeAgents;
|
|
14
|
+
constructor(backend?: SubAgentBackend);
|
|
15
|
+
/**
|
|
16
|
+
* Spawn a new SubAgent with the given specification.
|
|
17
|
+
* @param spec The SubAgent specification
|
|
18
|
+
* @returns A handle to the spawned SubAgent
|
|
19
|
+
*/
|
|
20
|
+
spawn(spec: SubAgentSpec): Promise<SubAgentHandle>;
|
|
21
|
+
/**
|
|
22
|
+
* Get all active SubAgent handles.
|
|
23
|
+
*/
|
|
24
|
+
getActiveAgents(): SubAgentHandle[];
|
|
25
|
+
/**
|
|
26
|
+
* Abort all active SubAgents.
|
|
27
|
+
*/
|
|
28
|
+
abortAll(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Terminate all active SubAgents.
|
|
31
|
+
*/
|
|
32
|
+
terminateAll(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Default global SubAgent runtime instance.
|
|
36
|
+
*/
|
|
37
|
+
export declare const subAgentRuntime: SubAgentRuntime;
|
|
38
|
+
//# sourceMappingURL=sub-agent-runtime.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [UPSTREAM]: Depends on sub-agent-types.ts, sub-agent-backend.ts
|
|
3
|
+
* [SURFACE]: SubAgentRuntime - spawn, abort, lifecycle management
|
|
4
|
+
* [LOCUS]: core/sub-agent/sub-agent-runtime.ts
|
|
5
|
+
*/
|
|
6
|
+
import { InProcessSubAgentBackend } from "./sub-agent-backend.js";
|
|
7
|
+
/**
|
|
8
|
+
* Runtime for managing SubAgents.
|
|
9
|
+
* Provides spawn/abort/lifecycle operations.
|
|
10
|
+
*/
|
|
11
|
+
export class SubAgentRuntime {
|
|
12
|
+
backend;
|
|
13
|
+
activeAgents = new Map();
|
|
14
|
+
constructor(backend = new InProcessSubAgentBackend()) {
|
|
15
|
+
this.backend = backend;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Spawn a new SubAgent with the given specification.
|
|
19
|
+
* @param spec The SubAgent specification
|
|
20
|
+
* @returns A handle to the spawned SubAgent
|
|
21
|
+
*/
|
|
22
|
+
async spawn(spec) {
|
|
23
|
+
const handle = await this.backend.spawn(spec);
|
|
24
|
+
this.activeAgents.set(handle.id, handle);
|
|
25
|
+
// Clean up when the agent finishes
|
|
26
|
+
handle.result().finally(() => {
|
|
27
|
+
this.activeAgents.delete(handle.id);
|
|
28
|
+
});
|
|
29
|
+
return handle;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get all active SubAgent handles.
|
|
33
|
+
*/
|
|
34
|
+
getActiveAgents() {
|
|
35
|
+
return Array.from(this.activeAgents.values());
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Abort all active SubAgents.
|
|
39
|
+
*/
|
|
40
|
+
async abortAll() {
|
|
41
|
+
await Promise.all(Array.from(this.activeAgents.values()).map((agent) => agent.abort()));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Terminate all active SubAgents.
|
|
45
|
+
*/
|
|
46
|
+
async terminateAll() {
|
|
47
|
+
await Promise.all(Array.from(this.activeAgents.values()).map((agent) => agent.terminate()));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Default global SubAgent runtime instance.
|
|
52
|
+
*/
|
|
53
|
+
export const subAgentRuntime = new SubAgentRuntime();
|
|
54
|
+
//# sourceMappingURL=sub-agent-runtime.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [UPSTREAM]: Depends on core/runtime/sdk.ts, core/tools/*
|
|
3
|
+
* [SURFACE]: SubAgentSpec, SubAgentHandle, SubAgentBackend
|
|
4
|
+
* [LOCUS]: core/sub-agent/sub-agent-types.ts
|
|
5
|
+
* [COVENANT]: Change these types → update P1 architecture diagram
|
|
6
|
+
*/
|
|
7
|
+
import type { ImageContent, Model } from "@pencil-agent/ai";
|
|
8
|
+
import type { Tool } from "../tools/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Specification for spawning a SubAgent.
|
|
11
|
+
*/
|
|
12
|
+
export interface SubAgentSpec {
|
|
13
|
+
/** Task prompt for the SubAgent */
|
|
14
|
+
prompt: string;
|
|
15
|
+
/** Tools available to the SubAgent (determined by caller, not guessed by core) */
|
|
16
|
+
tools: Tool[];
|
|
17
|
+
/** Working directory for the SubAgent (can be a worktree) */
|
|
18
|
+
cwd: string;
|
|
19
|
+
/** Abort signal for stopping the SubAgent (required) */
|
|
20
|
+
signal: AbortSignal;
|
|
21
|
+
/** Optional timeout in milliseconds */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
/** Images to include in the prompt */
|
|
24
|
+
images?: ImageContent[];
|
|
25
|
+
/** Model to use (reuses main session's model and auth) */
|
|
26
|
+
model?: Model<any>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Result from a completed SubAgent run.
|
|
30
|
+
*/
|
|
31
|
+
export interface SubAgentResult {
|
|
32
|
+
/** Whether the run was successful */
|
|
33
|
+
success: boolean;
|
|
34
|
+
/** The response text from the SubAgent */
|
|
35
|
+
response?: string;
|
|
36
|
+
/** Error message if unsuccessful */
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Handle to a running SubAgent.
|
|
41
|
+
*/
|
|
42
|
+
export interface SubAgentHandle {
|
|
43
|
+
/** Unique identifier for this SubAgent */
|
|
44
|
+
readonly id: string;
|
|
45
|
+
/** Current status of the SubAgent */
|
|
46
|
+
readonly status: "running" | "done" | "aborted" | "error";
|
|
47
|
+
/**
|
|
48
|
+
* Wait for the SubAgent to complete and get its result.
|
|
49
|
+
* Resolves when the SubAgent finishes (successfully or with error).
|
|
50
|
+
*/
|
|
51
|
+
result(): Promise<SubAgentResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Abort the current turn (stops after current LLM response).
|
|
54
|
+
* The SubAgent may still be in "running" state if it can accept more turns.
|
|
55
|
+
*/
|
|
56
|
+
abort(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Terminate the SubAgent completely.
|
|
59
|
+
* This stops all work and disposes of resources.
|
|
60
|
+
*/
|
|
61
|
+
terminate(): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Backend for spawning SubAgents.
|
|
65
|
+
* Core provides in-process backend; subprocess backend can be added in phase B.
|
|
66
|
+
*/
|
|
67
|
+
export interface SubAgentBackend {
|
|
68
|
+
/**
|
|
69
|
+
* Spawn a new SubAgent with the given specification.
|
|
70
|
+
* @param spec The SubAgent specification
|
|
71
|
+
* @returns A handle to the spawned SubAgent
|
|
72
|
+
*/
|
|
73
|
+
spawn(spec: SubAgentSpec): Promise<SubAgentHandle>;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=sub-agent-types.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [UPSTREAM]: Depends on core/runtime/sdk.ts, core/tools/*
|
|
3
|
+
* [SURFACE]: SubAgentSpec, SubAgentHandle, SubAgentBackend
|
|
4
|
+
* [LOCUS]: core/sub-agent/sub-agent-types.ts
|
|
5
|
+
* [COVENANT]: Change these types → update P1 architecture diagram
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=sub-agent-types.js.map
|
|
@@ -51,5 +51,16 @@ export declare const bashTool: AgentTool<import("@sinclair/typebox").TObject<{
|
|
|
51
51
|
command: import("@sinclair/typebox").TString;
|
|
52
52
|
timeout: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
53
53
|
}>, any>;
|
|
54
|
+
export interface BashSandboxOptions {
|
|
55
|
+
/** Additional patterns to block (in addition to default blocked patterns) */
|
|
56
|
+
additionalBlockedPatterns?: RegExp[];
|
|
57
|
+
/** Custom error message for blocked commands */
|
|
58
|
+
blockedMessage?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a sandboxed bash hook that blocks dangerous write operations.
|
|
62
|
+
* This is a defense-in-depth measure for read-only SubAgents.
|
|
63
|
+
*/
|
|
64
|
+
export declare function createSandboxHook(options?: BashSandboxOptions): BashSpawnHook;
|
|
54
65
|
export {};
|
|
55
66
|
//# sourceMappingURL=bash.d.ts.map
|
package/dist/core/tools/bash.js
CHANGED
|
@@ -245,4 +245,55 @@ export function createBashTool(cwd, options) {
|
|
|
245
245
|
}
|
|
246
246
|
/** Default bash tool using process.cwd() - for backwards compatibility */
|
|
247
247
|
export const bashTool = createBashTool(process.cwd());
|
|
248
|
+
/**
|
|
249
|
+
* Patterns that indicate write operations in bash commands.
|
|
250
|
+
* These are blocked in sandbox mode.
|
|
251
|
+
*/
|
|
252
|
+
const SANDBOX_BLOCKED_PATTERNS = [
|
|
253
|
+
// Output redirection
|
|
254
|
+
/^\s*>|\s>|\s>>|\s&\d>/,
|
|
255
|
+
// File deletion
|
|
256
|
+
/\brm\s+/,
|
|
257
|
+
// Move/copy
|
|
258
|
+
/\bmv\s+/,
|
|
259
|
+
/\bcp\s+.*\s+\//,
|
|
260
|
+
// Git write operations
|
|
261
|
+
/\bgit\s+(commit|push|pull|add|checkout\s+[^-|]|reset|rebase\s+[^--]|merge)/,
|
|
262
|
+
// Create directories
|
|
263
|
+
/\bmkdir\s+[^-]/,
|
|
264
|
+
// chmod/chown
|
|
265
|
+
/\bchmod\s+/,
|
|
266
|
+
/\bchown\s+/,
|
|
267
|
+
//ln -s
|
|
268
|
+
/\bln\s+.*-s/,
|
|
269
|
+
// tee
|
|
270
|
+
/\btee\s+/,
|
|
271
|
+
// wget/curl with output
|
|
272
|
+
/\bwget\s+.*-O/,
|
|
273
|
+
/\bcurl\s+.*-o/,
|
|
274
|
+
];
|
|
275
|
+
/**
|
|
276
|
+
* Create a sandboxed bash hook that blocks dangerous write operations.
|
|
277
|
+
* This is a defense-in-depth measure for read-only SubAgents.
|
|
278
|
+
*/
|
|
279
|
+
export function createSandboxHook(options) {
|
|
280
|
+
const blockedPatterns = [
|
|
281
|
+
...SANDBOX_BLOCKED_PATTERNS,
|
|
282
|
+
...(options?.additionalBlockedPatterns ?? []),
|
|
283
|
+
];
|
|
284
|
+
const blockedMessage = options?.blockedMessage ?? "Write operations are not allowed in sandbox mode";
|
|
285
|
+
return (context) => {
|
|
286
|
+
const command = context.command.trim();
|
|
287
|
+
// Check if command contains any blocked patterns
|
|
288
|
+
for (const pattern of blockedPatterns) {
|
|
289
|
+
if (pattern.test(command)) {
|
|
290
|
+
return {
|
|
291
|
+
...context,
|
|
292
|
+
command: `echo "${blockedMessage}" >&2; exit 1`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return context;
|
|
297
|
+
};
|
|
298
|
+
}
|
|
248
299
|
//# sourceMappingURL=bash.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* [TO]: Consumed by index.ts, main.ts, cli/args.ts, modes/interactive/components/tool-execution.ts, extensions/defaults/team/index.ts, and test files
|
|
5
5
|
* [HERE]: Tool system public API; consumed by SDK and orchestrator
|
|
6
6
|
*/
|
|
7
|
-
export { type BashOperations, type BashSpawnContext, type BashSpawnHook, type BashToolDetails, type BashToolInput, type BashToolOptions, bashTool, createBashTool, } from "./bash.js";
|
|
7
|
+
export { type BashOperations, type BashSandboxOptions, type BashSpawnContext, type BashSpawnHook, type BashToolDetails, type BashToolInput, type BashToolOptions, bashTool, createBashTool, createSandboxHook, } from "./bash.js";
|
|
8
8
|
export { createEditTool, type EditOperations, type EditToolDetails, type EditToolInput, type EditToolOptions, editTool, } from "./edit.js";
|
|
9
9
|
export { createFindTool, type FindOperations, type FindToolDetails, type FindToolInput, type FindToolOptions, findTool, } from "./find.js";
|
|
10
10
|
export { createGrepTool, type GrepOperations, type GrepToolDetails, type GrepToolInput, type GrepToolOptions, grepTool, } from "./grep.js";
|
package/dist/core/tools/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* [TO]: Consumed by index.ts, main.ts, cli/args.ts, modes/interactive/components/tool-execution.ts, extensions/defaults/team/index.ts, and test files
|
|
5
5
|
* [HERE]: Tool system public API; consumed by SDK and orchestrator
|
|
6
6
|
*/
|
|
7
|
-
export { bashTool, createBashTool, } from "./bash.js";
|
|
7
|
+
export { bashTool, createBashTool, createSandboxHook, } from "./bash.js";
|
|
8
8
|
export { createEditTool, editTool, } from "./edit.js";
|
|
9
9
|
export { createFindTool, findTool, } from "./find.js";
|
|
10
10
|
export { createGrepTool, grepTool, } from "./grep.js";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [UPSTREAM]: Depends on node:fs/promises, node:path
|
|
3
|
+
* [SURFACE]: WorktreeManager - temporary workspace management
|
|
4
|
+
* [LOCUS]: core/workspace/worktree-manager.ts
|
|
5
|
+
*/
|
|
6
|
+
export interface WorkspacePath {
|
|
7
|
+
/** Absolute path to the workspace */
|
|
8
|
+
readonly path: string;
|
|
9
|
+
/** Type of workspace */
|
|
10
|
+
readonly type: "temp" | "worktree";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Manager for creating and disposing workspaces for SubAgents.
|
|
14
|
+
* Provides temporary directories and git worktrees.
|
|
15
|
+
*/
|
|
16
|
+
export declare class WorktreeManager {
|
|
17
|
+
private tempDirs;
|
|
18
|
+
private worktrees;
|
|
19
|
+
/**
|
|
20
|
+
* Create a temporary workspace.
|
|
21
|
+
* Copies seed files to a new temp directory.
|
|
22
|
+
* @param seedFiles Files to copy to the temp workspace
|
|
23
|
+
* @param prefix Prefix for the temp directory name
|
|
24
|
+
*/
|
|
25
|
+
createTempWorkspace(seedFiles?: string[], prefix?: string): Promise<WorkspacePath>;
|
|
26
|
+
/**
|
|
27
|
+
* Create a git worktree for the given branch.
|
|
28
|
+
* @param branch Branch name for the worktree
|
|
29
|
+
* @param cwd Working directory for git operations
|
|
30
|
+
*/
|
|
31
|
+
createGitWorktree(branch?: string, cwd?: string): Promise<WorkspacePath>;
|
|
32
|
+
/**
|
|
33
|
+
* Dispose of a workspace.
|
|
34
|
+
* Removes temp directories and worktrees.
|
|
35
|
+
* @param workspace The workspace to dispose
|
|
36
|
+
*/
|
|
37
|
+
dispose(workspace: WorkspacePath): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Get all active temp directories.
|
|
40
|
+
*/
|
|
41
|
+
getActiveTempDirs(): string[];
|
|
42
|
+
/**
|
|
43
|
+
* Clean up all temp directories.
|
|
44
|
+
*/
|
|
45
|
+
disposeAll(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Default global worktree manager instance.
|
|
49
|
+
*/
|
|
50
|
+
export declare const worktreeManager: WorktreeManager;
|
|
51
|
+
//# sourceMappingURL=worktree-manager.d.ts.map
|