@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.
Files changed (103) hide show
  1. package/dist/builtin-extensions.js +22 -2
  2. package/dist/core/config/settings-manager.d.ts +6 -0
  3. package/dist/core/config/settings-manager.js +11 -0
  4. package/dist/core/i18n/messages.d.ts +4 -0
  5. package/dist/core/i18n/messages.js +17 -0
  6. package/dist/core/i18n/messages.zh.d.ts +4 -0
  7. package/dist/core/i18n/messages.zh.js +17 -0
  8. package/dist/core/i18n/slash-commands.d.ts +1 -0
  9. package/dist/core/i18n/slash-commands.js +1 -0
  10. package/dist/core/i18n/slash-commands.zh.d.ts +1 -0
  11. package/dist/core/i18n/slash-commands.zh.js +1 -0
  12. package/dist/core/package-manager.js +3 -1
  13. package/dist/core/runtime/agent-session.d.ts +2 -0
  14. package/dist/core/runtime/agent-session.js +6 -0
  15. package/dist/core/runtime/sdk.d.ts +2 -0
  16. package/dist/core/runtime/sdk.js +1 -0
  17. package/dist/core/skills.js +6 -2
  18. package/dist/core/slash-commands.js +1 -0
  19. package/dist/core/sub-agent/index.d.ts +9 -0
  20. package/dist/core/sub-agent/index.js +7 -0
  21. package/dist/core/sub-agent/sub-agent-backend.d.ts +14 -0
  22. package/dist/core/sub-agent/sub-agent-backend.js +119 -0
  23. package/dist/core/sub-agent/sub-agent-runtime.d.ts +38 -0
  24. package/dist/core/sub-agent/sub-agent-runtime.js +54 -0
  25. package/dist/core/sub-agent/sub-agent-types.d.ts +75 -0
  26. package/dist/core/sub-agent/sub-agent-types.js +8 -0
  27. package/dist/core/sub-agent/subprocess-backend.d.ts +36 -0
  28. package/dist/core/sub-agent/subprocess-backend.js +105 -0
  29. package/dist/core/sub-agent/subprocess-worker.d.ts +13 -0
  30. package/dist/core/sub-agent/subprocess-worker.js +41 -0
  31. package/dist/core/tools/bash.d.ts +11 -0
  32. package/dist/core/tools/bash.js +51 -0
  33. package/dist/core/tools/index.d.ts +1 -1
  34. package/dist/core/tools/index.js +1 -1
  35. package/dist/core/workspace/index.d.ts +6 -0
  36. package/dist/core/workspace/index.js +5 -0
  37. package/dist/core/workspace/worktree-manager.d.ts +72 -0
  38. package/dist/core/workspace/worktree-manager.js +356 -0
  39. package/dist/extensions/defaults/CLAUDE.md +24 -12
  40. package/dist/extensions/defaults/grub/README.md +25 -0
  41. package/dist/extensions/defaults/grub/grub-controller.d.ts +32 -0
  42. package/dist/extensions/defaults/{loop/loop-controller.js → grub/grub-controller.js} +19 -19
  43. package/dist/extensions/defaults/grub/grub-parser.d.ts +10 -0
  44. package/dist/extensions/defaults/{loop/loop-parser.js → grub/grub-parser.js} +6 -6
  45. package/dist/extensions/defaults/grub/grub-types.d.ts +54 -0
  46. package/dist/extensions/defaults/grub/grub-types.js +2 -0
  47. package/dist/extensions/defaults/grub/index.d.ts +9 -0
  48. package/dist/extensions/defaults/grub/index.js +310 -0
  49. package/dist/extensions/defaults/loop/README.md +47 -24
  50. package/dist/extensions/defaults/loop/index.d.ts +4 -4
  51. package/dist/extensions/defaults/loop/index.js +165 -346
  52. package/dist/extensions/defaults/loop/scheduler-controller.d.ts +13 -7
  53. package/dist/extensions/defaults/loop/scheduler-controller.js +84 -19
  54. package/dist/extensions/defaults/loop/scheduler-parser.d.ts +5 -5
  55. package/dist/extensions/defaults/loop/scheduler-parser.js +125 -51
  56. package/dist/extensions/defaults/loop/scheduler-types.d.ts +34 -9
  57. package/dist/extensions/defaults/loop/scheduler-types.js +3 -3
  58. package/dist/extensions/defaults/presence/index.d.ts +3 -3
  59. package/dist/extensions/defaults/presence/index.js +479 -30
  60. package/dist/extensions/defaults/subagent/index.d.ts +8 -0
  61. package/dist/extensions/defaults/subagent/index.js +190 -0
  62. package/dist/extensions/defaults/subagent/subagent-parser.d.ts +30 -0
  63. package/dist/extensions/defaults/subagent/subagent-parser.js +89 -0
  64. package/dist/extensions/defaults/subagent/subagent-runner.d.ts +48 -0
  65. package/dist/extensions/defaults/subagent/subagent-runner.js +275 -0
  66. package/dist/extensions/defaults/subagent/subagent-types.d.ts +57 -0
  67. package/dist/extensions/defaults/subagent/subagent-types.js +7 -0
  68. package/dist/extensions/defaults/team/CLAUDE.md +87 -0
  69. package/dist/extensions/defaults/team/TESTING.md +247 -0
  70. package/dist/extensions/defaults/team/index.d.ts +14 -4
  71. package/dist/extensions/defaults/team/index.js +342 -1036
  72. package/dist/extensions/defaults/team/team-mailbox.d.ts +47 -0
  73. package/dist/extensions/defaults/team/team-mailbox.js +65 -0
  74. package/dist/extensions/defaults/team/team-parser.d.ts +39 -19
  75. package/dist/extensions/defaults/team/team-parser.js +134 -43
  76. package/dist/extensions/defaults/team/team-permissions.d.ts +64 -0
  77. package/dist/extensions/defaults/team/team-permissions.js +117 -0
  78. package/dist/extensions/defaults/team/team-runtime.d.ts +116 -0
  79. package/dist/extensions/defaults/team/team-runtime.js +503 -0
  80. package/dist/extensions/defaults/team/team-state-store.d.ts +25 -0
  81. package/dist/extensions/defaults/team/team-state-store.js +78 -0
  82. package/dist/extensions/defaults/team/team-transcript.d.ts +32 -0
  83. package/dist/extensions/defaults/team/team-transcript.js +59 -0
  84. package/dist/extensions/defaults/team/team-types.d.ts +91 -51
  85. package/dist/extensions/defaults/team/team-types.js +6 -0
  86. package/dist/migrations.js +2 -2
  87. package/dist/modes/acp/acp-mode.js +18 -4
  88. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  89. package/dist/modes/interactive/components/settings-selector.js +12 -0
  90. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  91. package/dist/modes/interactive/interactive-mode.js +75 -1
  92. package/docs/AgentTeam/351/207/215/346/236/204/346/226/271/346/241/210.md +41 -8
  93. package/node_modules/@pencil-agent/ai/dist/models.generated.d.ts +166 -57
  94. package/node_modules/@pencil-agent/ai/dist/models.generated.d.ts.map +1 -1
  95. package/node_modules/@pencil-agent/ai/dist/models.generated.js +203 -99
  96. package/node_modules/@pencil-agent/ai/dist/models.generated.js.map +1 -1
  97. package/package.json +2 -1
  98. package/dist/extensions/defaults/loop/loop-controller.d.ts +0 -32
  99. package/dist/extensions/defaults/loop/loop-parser.d.ts +0 -10
  100. package/dist/extensions/defaults/loop/loop-types.d.ts +0 -54
  101. package/dist/extensions/defaults/loop/loop-types.js +0 -2
  102. package/dist/extensions/defaults/team/team-controller.d.ts +0 -24
  103. 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
- // === Loop extension (/grub autonomous task, /loop scheduler) ===
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 team extension
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
  }
@@ -49,5 +49,9 @@ export declare const messages: {
49
49
  confirmQuit: string;
50
50
  confirmNewSession: string;
51
51
  confirmDelete: string;
52
+ presence: {
53
+ opening: string[];
54
+ idle: string[];
55
+ };
52
56
  };
53
57
  //# sourceMappingURL=messages.d.ts.map
@@ -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
@@ -49,5 +49,9 @@ export declare const messages: {
49
49
  confirmQuit: string;
50
50
  confirmNewSession: string;
51
51
  confirmDelete: string;
52
+ presence: {
53
+ opening: string[];
54
+ idle: string[];
55
+ };
52
56
  };
53
57
  //# sourceMappingURL=messages.zh.d.ts.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 declare const slashCommands: {
28
28
  logout: string;
29
29
  new: string;
30
30
  update: string;
31
+ reinstall: string;
31
32
  compact: string;
32
33
  resume: string;
33
34
  reload: string;
@@ -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",
@@ -28,6 +28,7 @@ export declare const slashCommands: {
28
28
  logout: string;
29
29
  new: string;
30
30
  update: string;
31
+ reinstall: string;
31
32
  compact: string;
32
33
  resume: string;
33
34
  reload: string;
@@ -28,6 +28,7 @@ export const slashCommands = {
28
28
  logout: "从 OAuth 提供商登出",
29
29
  new: "开始新会话",
30
30
  update: "检查 NanoPencil 更新",
31
+ reinstall: "强制重新安装 NanoPencil(干净安装)",
31
32
  compact: "手动压缩会话上下文",
32
33
  resume: "恢复其他会话",
33
34
  reload: "重新加载扩展、技能、提示和主题",
@@ -69,7 +69,9 @@ function addIgnoreRules(ig, dir, rootDir) {
69
69
  ig.add(patterns);
70
70
  }
71
71
  }
72
- catch { }
72
+ catch (e) {
73
+ console.warn("Warning: Failed to read ignore file:", e instanceof Error ? e.message : e);
74
+ }
73
75
  }
74
76
  }
75
77
  function isPattern(s) {
@@ -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 {
@@ -350,6 +350,7 @@ export async function createAgentSession(options = {}) {
350
350
  extensionRunnerRef,
351
351
  soulManager,
352
352
  soulManagerFactory,
353
+ signal: options.signal,
353
354
  });
354
355
  const extensionsResult = resourceLoader.getExtensions();
355
356
  return {
@@ -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