@pencil-agent/nano-pencil 1.11.36 → 1.11.38
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 +22 -2
- package/dist/core/config/settings-manager.d.ts +6 -0
- package/dist/core/config/settings-manager.js +11 -0
- package/dist/core/i18n/messages.d.ts +4 -0
- package/dist/core/i18n/messages.js +17 -0
- package/dist/core/i18n/messages.zh.d.ts +4 -0
- package/dist/core/i18n/messages.zh.js +17 -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/package-manager.js +3 -1
- 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 +9 -0
- package/dist/core/sub-agent/index.js +7 -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/sub-agent/subprocess-backend.d.ts +36 -0
- package/dist/core/sub-agent/subprocess-backend.js +105 -0
- package/dist/core/sub-agent/subprocess-worker.d.ts +13 -0
- package/dist/core/sub-agent/subprocess-worker.js +41 -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 +72 -0
- package/dist/core/workspace/worktree-manager.js +356 -0
- package/dist/extensions/defaults/CLAUDE.md +24 -12
- package/dist/extensions/defaults/grub/README.md +25 -0
- package/dist/extensions/defaults/grub/grub-controller.d.ts +32 -0
- package/dist/extensions/defaults/{loop/loop-controller.js → grub/grub-controller.js} +19 -19
- package/dist/extensions/defaults/grub/grub-parser.d.ts +10 -0
- package/dist/extensions/defaults/{loop/loop-parser.js → grub/grub-parser.js} +6 -6
- package/dist/extensions/defaults/grub/grub-types.d.ts +54 -0
- package/dist/extensions/defaults/grub/grub-types.js +2 -0
- package/dist/extensions/defaults/grub/index.d.ts +9 -0
- package/dist/extensions/defaults/grub/index.js +310 -0
- package/dist/extensions/defaults/loop/README.md +47 -24
- package/dist/extensions/defaults/loop/index.d.ts +4 -4
- package/dist/extensions/defaults/loop/index.js +165 -346
- package/dist/extensions/defaults/loop/scheduler-controller.d.ts +13 -7
- package/dist/extensions/defaults/loop/scheduler-controller.js +84 -19
- package/dist/extensions/defaults/loop/scheduler-parser.d.ts +5 -5
- package/dist/extensions/defaults/loop/scheduler-parser.js +125 -51
- package/dist/extensions/defaults/loop/scheduler-types.d.ts +34 -9
- package/dist/extensions/defaults/loop/scheduler-types.js +3 -3
- package/dist/extensions/defaults/presence/index.d.ts +3 -3
- package/dist/extensions/defaults/presence/index.js +479 -30
- package/dist/extensions/defaults/subagent/index.d.ts +8 -0
- package/dist/extensions/defaults/subagent/index.js +190 -0
- package/dist/extensions/defaults/subagent/subagent-parser.d.ts +30 -0
- package/dist/extensions/defaults/subagent/subagent-parser.js +89 -0
- package/dist/extensions/defaults/subagent/subagent-runner.d.ts +48 -0
- package/dist/extensions/defaults/subagent/subagent-runner.js +275 -0
- package/dist/extensions/defaults/subagent/subagent-types.d.ts +57 -0
- package/dist/extensions/defaults/subagent/subagent-types.js +7 -0
- package/dist/extensions/defaults/team/CLAUDE.md +87 -0
- package/dist/extensions/defaults/team/TESTING.md +247 -0
- package/dist/extensions/defaults/team/index.d.ts +14 -4
- package/dist/extensions/defaults/team/index.js +342 -1036
- package/dist/extensions/defaults/team/team-mailbox.d.ts +47 -0
- package/dist/extensions/defaults/team/team-mailbox.js +65 -0
- package/dist/extensions/defaults/team/team-parser.d.ts +39 -19
- package/dist/extensions/defaults/team/team-parser.js +134 -43
- package/dist/extensions/defaults/team/team-permissions.d.ts +64 -0
- package/dist/extensions/defaults/team/team-permissions.js +117 -0
- package/dist/extensions/defaults/team/team-runtime.d.ts +116 -0
- package/dist/extensions/defaults/team/team-runtime.js +503 -0
- package/dist/extensions/defaults/team/team-state-store.d.ts +25 -0
- package/dist/extensions/defaults/team/team-state-store.js +78 -0
- package/dist/extensions/defaults/team/team-transcript.d.ts +32 -0
- package/dist/extensions/defaults/team/team-transcript.js +59 -0
- package/dist/extensions/defaults/team/team-types.d.ts +91 -51
- package/dist/extensions/defaults/team/team-types.js +6 -0
- package/dist/migrations.js +2 -2
- package/dist/modes/acp/acp-mode.js +18 -4
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.js +12 -0
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.js +75 -1
- package/docs/AgentTeam/351/207/215/346/236/204/346/226/271/346/241/210.md +41 -8
- package/node_modules/@pencil-agent/ai/dist/models.generated.d.ts +166 -57
- package/node_modules/@pencil-agent/ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@pencil-agent/ai/dist/models.generated.js +203 -99
- package/node_modules/@pencil-agent/ai/dist/models.generated.js.map +1 -1
- package/package.json +2 -1
- package/dist/extensions/defaults/loop/loop-controller.d.ts +0 -32
- package/dist/extensions/defaults/loop/loop-parser.d.ts +0 -10
- package/dist/extensions/defaults/loop/loop-types.d.ts +0 -54
- package/dist/extensions/defaults/loop/loop-types.js +0 -2
- package/dist/extensions/defaults/team/team-controller.d.ts +0 -24
- package/dist/extensions/defaults/team/team-controller.js +0 -98
|
@@ -19,6 +19,8 @@ const BUNDLED_SOUL_EXTENSION = join(__dirname, "extensions", "defaults", "soul",
|
|
|
19
19
|
const BUNDLED_PRESENCE_EXTENSION = join(__dirname, "extensions", "defaults", "presence", "index.js");
|
|
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
|
+
const BUNDLED_GRUB_EXTENSION = join(__dirname, "extensions", "defaults", "grub", "index.js");
|
|
23
|
+
const BUNDLED_SUBAGENT_EXTENSION = join(__dirname, "extensions", "defaults", "subagent", "index.js");
|
|
22
24
|
const BUNDLED_TEAM_EXTENSION = join(__dirname, "extensions", "defaults", "team", "index.js");
|
|
23
25
|
const BUNDLED_MCP_EXTENSION = join(__dirname, "extensions", "defaults", "mcp", "index.js");
|
|
24
26
|
const BUNDLED_EXPORT_HTML_EXTENSION = join(__dirname, "extensions", "optional", "export-html", "index.js");
|
|
@@ -138,7 +140,16 @@ export function getBuiltinExtensionPaths() {
|
|
|
138
140
|
if (existsSync(interviewTs))
|
|
139
141
|
paths.push(interviewTs);
|
|
140
142
|
}
|
|
141
|
-
// ===
|
|
143
|
+
// === Grub extension (/grub autonomous iterative task) ===
|
|
144
|
+
if (existsSync(BUNDLED_GRUB_EXTENSION)) {
|
|
145
|
+
paths.push(BUNDLED_GRUB_EXTENSION);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const grubTs = join(__dirname, "extensions", "defaults", "grub", "index.ts");
|
|
149
|
+
if (existsSync(grubTs))
|
|
150
|
+
paths.push(grubTs);
|
|
151
|
+
}
|
|
152
|
+
// === Loop extension (/loop recurring scheduler) ===
|
|
142
153
|
if (existsSync(BUNDLED_LOOP_EXTENSION)) {
|
|
143
154
|
paths.push(BUNDLED_LOOP_EXTENSION);
|
|
144
155
|
}
|
|
@@ -148,7 +159,16 @@ export function getBuiltinExtensionPaths() {
|
|
|
148
159
|
paths.push(loopTs);
|
|
149
160
|
}
|
|
150
161
|
// === MCP extension (MCP tool protocol adapter) ===
|
|
151
|
-
// Built-in
|
|
162
|
+
// Built-in SubAgent extension
|
|
163
|
+
if (existsSync(BUNDLED_SUBAGENT_EXTENSION)) {
|
|
164
|
+
paths.push(BUNDLED_SUBAGENT_EXTENSION);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const subagentTs = join(__dirname, "extensions", "defaults", "subagent", "index.ts");
|
|
168
|
+
if (existsSync(subagentTs))
|
|
169
|
+
paths.push(subagentTs);
|
|
170
|
+
}
|
|
171
|
+
// Built-in AgentTeam extension (Phase B - persistent teammates)
|
|
152
172
|
if (existsSync(BUNDLED_TEAM_EXTENSION)) {
|
|
153
173
|
paths.push(BUNDLED_TEAM_EXTENSION);
|
|
154
174
|
}
|
|
@@ -97,6 +97,10 @@ export interface Settings {
|
|
|
97
97
|
lockStaleMinutes?: number;
|
|
98
98
|
};
|
|
99
99
|
};
|
|
100
|
+
/** Presence extension settings - AI greeting and idle messages */
|
|
101
|
+
presence?: {
|
|
102
|
+
enabled?: boolean;
|
|
103
|
+
};
|
|
100
104
|
/** Auto-update setting: 'always' = auto-update on startup, 'prompt' = ask user (default), 'never' = never check */
|
|
101
105
|
autoUpdate?: "always" | "prompt" | "never";
|
|
102
106
|
/** Last skipped version for update prompts */
|
|
@@ -240,6 +244,8 @@ export declare class SettingsManager {
|
|
|
240
244
|
setShowWorkingTrace(enabled: boolean): void;
|
|
241
245
|
getShowMemoryTrace(): boolean;
|
|
242
246
|
setShowMemoryTrace(enabled: boolean): void;
|
|
247
|
+
getPresenceEnabled(): boolean;
|
|
248
|
+
setPresenceEnabled(enabled: boolean): void;
|
|
243
249
|
getImageAutoResize(): boolean;
|
|
244
250
|
setImageAutoResize(enabled: boolean): void;
|
|
245
251
|
getBlockImages(): boolean;
|
|
@@ -624,6 +624,17 @@ export class SettingsManager {
|
|
|
624
624
|
this.markModified("terminal", "showMemoryTrace");
|
|
625
625
|
this.save();
|
|
626
626
|
}
|
|
627
|
+
getPresenceEnabled() {
|
|
628
|
+
return this.settings.presence?.enabled ?? true;
|
|
629
|
+
}
|
|
630
|
+
setPresenceEnabled(enabled) {
|
|
631
|
+
if (!this.globalSettings.presence) {
|
|
632
|
+
this.globalSettings.presence = {};
|
|
633
|
+
}
|
|
634
|
+
this.globalSettings.presence.enabled = enabled;
|
|
635
|
+
this.markModified("presence", "enabled");
|
|
636
|
+
this.save();
|
|
637
|
+
}
|
|
627
638
|
getImageAutoResize() {
|
|
628
639
|
return this.settings.images?.autoResize ?? true;
|
|
629
640
|
}
|
|
@@ -58,5 +58,22 @@ export const messages = {
|
|
|
58
58
|
confirmQuit: "Are you sure you want to quit?",
|
|
59
59
|
confirmNewSession: "Start a new session? Current session will be saved.",
|
|
60
60
|
confirmDelete: "Are you sure you want to delete?",
|
|
61
|
+
// Presence (AI generation fails or memory is empty - fallback messages)
|
|
62
|
+
presence: {
|
|
63
|
+
opening: [
|
|
64
|
+
"Hey, what's up?",
|
|
65
|
+
"Ready when you are.",
|
|
66
|
+
"What do you want to work on?",
|
|
67
|
+
"Any ideas?",
|
|
68
|
+
"Let's do this.",
|
|
69
|
+
],
|
|
70
|
+
idle: [
|
|
71
|
+
"Still here when you need me.",
|
|
72
|
+
"No rush, take your time.",
|
|
73
|
+
"Ready when you are.",
|
|
74
|
+
"I'll be here.",
|
|
75
|
+
"Whenever you're ready.",
|
|
76
|
+
],
|
|
77
|
+
},
|
|
61
78
|
};
|
|
62
79
|
//# sourceMappingURL=messages.js.map
|
|
@@ -58,5 +58,22 @@ export const messages = {
|
|
|
58
58
|
confirmQuit: "确定要退出吗?",
|
|
59
59
|
confirmNewSession: "开始新会话?当前会话将被保存。",
|
|
60
60
|
confirmDelete: "确定要删除吗?",
|
|
61
|
+
// Presence (AI生成失败或记忆为空时的备用消息)
|
|
62
|
+
presence: {
|
|
63
|
+
opening: [
|
|
64
|
+
"来了啊。",
|
|
65
|
+
"嘿,有什么想做的吗?",
|
|
66
|
+
"准备开始吧。",
|
|
67
|
+
"随时可以开始。",
|
|
68
|
+
"有什么要聊聊的吗?",
|
|
69
|
+
],
|
|
70
|
+
idle: [
|
|
71
|
+
"还在,有需要随时说。",
|
|
72
|
+
"不急,慢慢来。",
|
|
73
|
+
"我在,随时继续。",
|
|
74
|
+
"有空了就继续吧。",
|
|
75
|
+
"没关系的,想什么时候继续都行。",
|
|
76
|
+
],
|
|
77
|
+
},
|
|
61
78
|
};
|
|
62
79
|
//# sourceMappingURL=messages.zh.js.map
|
|
@@ -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,9 @@
|
|
|
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 { SubprocessSubAgentBackend } from "./subprocess-backend.js";
|
|
7
|
+
export type { SubprocessBackendOptions } from "./subprocess-backend.js";
|
|
8
|
+
export type { SubAgentSpec, SubAgentResult, SubAgentHandle, SubAgentBackend, } from "./sub-agent-types.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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 { SubprocessSubAgentBackend } from "./subprocess-backend.js";
|
|
7
|
+
//# sourceMappingURL=index.js.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
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: SubprocessSubAgentBackend
|
|
3
|
+
* [FROM]: Depends on node:worker_threads, ./sub-agent-types
|
|
4
|
+
* [TO]: Consumed by SubAgentRuntime when caller wants crash isolation
|
|
5
|
+
* [HERE]: core/sub-agent/subprocess-backend.ts - Phase B B.6 multi-backend
|
|
6
|
+
*
|
|
7
|
+
* Crash-isolated SubAgent backend built on top of node:worker_threads.
|
|
8
|
+
*
|
|
9
|
+
* The worker thread runs the in-process backend in its own V8 isolate, so
|
|
10
|
+
* an unhandled error inside a teammate cannot tear down the main session.
|
|
11
|
+
* The Tool[] surface in `SubAgentSpec` is NOT serializable across the
|
|
12
|
+
* worker boundary, so the caller must pre-stage tools by name and resolve
|
|
13
|
+
* them inside the worker via `toolFactoryName`. For v1 we accept the same
|
|
14
|
+
* profiles the team extension already uses ("read-only" / "sandboxed").
|
|
15
|
+
*
|
|
16
|
+
* Status: SHIPPED with the worker harness in place; the worker entry
|
|
17
|
+
* itself is intentionally minimal (echo + abort) so the interface is real
|
|
18
|
+
* and exercisable. The full agent loop inside the worker is deferred to a
|
|
19
|
+
* follow-up because it requires re-creating the model registry inside the
|
|
20
|
+
* isolate, which is out of scope for the AgentTeam Phase B milestone.
|
|
21
|
+
*
|
|
22
|
+
* The backend therefore documents itself honestly: callers that need real
|
|
23
|
+
* LLM execution should keep using `InProcessSubAgentBackend`; callers that
|
|
24
|
+
* just need an isolated bash/echo worker can use this one today.
|
|
25
|
+
*/
|
|
26
|
+
import type { SubAgentBackend, SubAgentHandle, SubAgentSpec } from "./sub-agent-types.js";
|
|
27
|
+
export interface SubprocessBackendOptions {
|
|
28
|
+
/** Override the worker entry path (for tests). */
|
|
29
|
+
workerEntry?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class SubprocessSubAgentBackend implements SubAgentBackend {
|
|
32
|
+
private readonly workerEntry;
|
|
33
|
+
constructor(options?: SubprocessBackendOptions);
|
|
34
|
+
spawn(spec: SubAgentSpec): Promise<SubAgentHandle>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=subprocess-backend.d.ts.map
|