@oh-my-pi/pi-coding-agent 15.10.12 → 15.11.0
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 +60 -3
- package/dist/cli.js +841 -803
- package/dist/types/async/index.d.ts +0 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +5 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/settings-schema.d.ts +56 -33
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/history-protocol.d.ts +14 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/irc/bus.d.ts +66 -0
- package/dist/types/modes/components/agent-hub.d.ts +30 -0
- package/dist/types/modes/components/compaction-summary-message.d.ts +10 -4
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/ttsr-notification.d.ts +5 -1
- package/dist/types/modes/components/welcome.d.ts +3 -9
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +3 -2
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +3 -2
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
- package/dist/types/registry/agent-lifecycle.d.ts +51 -0
- package/dist/types/registry/agent-registry.d.ts +16 -5
- package/dist/types/session/agent-session.d.ts +35 -30
- package/dist/types/session/messages.d.ts +2 -4
- package/dist/types/session/session-history-format.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +21 -3
- package/dist/types/session/streaming-output.d.ts +23 -0
- package/dist/types/task/executor.d.ts +11 -2
- package/dist/types/task/index.d.ts +11 -4
- package/dist/types/task/output-manager.d.ts +0 -7
- package/dist/types/task/repair-args.d.ts +8 -7
- package/dist/types/task/types.d.ts +55 -51
- package/dist/types/tools/browser/tab-worker.d.ts +3 -1
- package/dist/types/tools/find.d.ts +0 -11
- package/dist/types/tools/grouped-file-output.d.ts +0 -49
- package/dist/types/tools/index.d.ts +1 -3
- package/dist/types/tools/irc.d.ts +76 -38
- package/dist/types/tools/job.d.ts +7 -1
- package/examples/extensions/with-deps/package.json +1 -0
- package/package.json +11 -10
- package/scripts/bundle-dist.ts +28 -19
- package/src/async/index.ts +0 -1
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/agentic.ts +230 -115
- package/src/cli/gallery-fixtures/types.ts +5 -0
- package/src/cli.ts +20 -6
- package/src/commit/agentic/tools/analyze-file.ts +38 -19
- package/src/config/keybindings.ts +6 -1
- package/src/config/settings-schema.ts +56 -40
- package/src/config/settings.ts +7 -0
- package/src/eval/__tests__/agent-bridge.test.ts +5 -3
- package/src/eval/agent-bridge.ts +3 -16
- package/src/eval/js/shared/prelude.txt +1 -1
- package/src/eval/py/prelude.py +5 -6
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +38 -13
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +8 -8
- package/src/internal-urls/history-protocol.ts +113 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/router.ts +3 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/irc/bus.ts +292 -0
- package/src/main.ts +8 -60
- package/src/modes/components/{session-observer-overlay.ts → agent-hub.ts} +586 -367
- package/src/modes/components/compaction-summary-message.ts +68 -32
- package/src/modes/components/custom-editor.ts +10 -0
- package/src/modes/components/tool-execution.ts +31 -1
- package/src/modes/components/ttsr-notification.ts +72 -30
- package/src/modes/components/welcome.ts +9 -33
- package/src/modes/controllers/event-controller.ts +65 -0
- package/src/modes/controllers/extension-ui-controller.ts +8 -8
- package/src/modes/controllers/input-controller.ts +18 -2
- package/src/modes/controllers/selector-controller.ts +21 -17
- package/src/modes/interactive-mode.ts +8 -13
- package/src/modes/theme/theme.ts +18 -5
- package/src/modes/types.ts +3 -5
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +51 -49
- package/src/prompts/system/irc-incoming.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +0 -5
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/system/workflow-notice.md +2 -2
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/irc.md +29 -19
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/task-summary.md +5 -16
- package/src/prompts/tools/task.md +38 -29
- package/src/registry/agent-lifecycle.ts +218 -0
- package/src/registry/agent-registry.ts +16 -5
- package/src/sdk.ts +29 -9
- package/src/session/agent-session.ts +243 -237
- package/src/session/messages.ts +11 -78
- package/src/session/session-history-format.ts +246 -0
- package/src/session/session-manager.ts +59 -5
- package/src/session/streaming-output.ts +60 -0
- package/src/task/executor.ts +855 -466
- package/src/task/index.ts +718 -794
- package/src/task/output-manager.ts +0 -11
- package/src/task/render.ts +133 -63
- package/src/task/repair-args.ts +21 -9
- package/src/task/types.ts +73 -66
- package/src/tools/ask.ts +4 -2
- package/src/tools/bash.ts +15 -5
- package/src/tools/browser/tab-worker.ts +26 -7
- package/src/tools/browser.ts +28 -1
- package/src/tools/find.ts +2 -27
- package/src/tools/grouped-file-output.ts +1 -118
- package/src/tools/index.ts +4 -12
- package/src/tools/irc.ts +596 -171
- package/src/tools/job.ts +41 -7
- package/src/tools/read.ts +57 -1
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +4 -1
- package/dist/types/async/support.d.ts +0 -2
- package/dist/types/modes/components/session-observer-overlay.d.ts +0 -11
- package/dist/types/task/simple-mode.d.ts +0 -8
- package/src/async/support.ts +0 -5
- package/src/task/simple-mode.ts +0 -27
|
@@ -140,7 +140,7 @@ export class ExtensionUiController {
|
|
|
140
140
|
reload: async () => {
|
|
141
141
|
await this.ctx.session.reload();
|
|
142
142
|
this.ctx.chatContainer.clear();
|
|
143
|
-
this.ctx.renderInitialMessages(
|
|
143
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
144
144
|
await this.ctx.reloadTodos();
|
|
145
145
|
this.ctx.showStatus("Reloaded session");
|
|
146
146
|
},
|
|
@@ -197,7 +197,7 @@ export class ExtensionUiController {
|
|
|
197
197
|
|
|
198
198
|
// Update UI
|
|
199
199
|
this.ctx.chatContainer.clear();
|
|
200
|
-
this.ctx.renderInitialMessages(
|
|
200
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
201
201
|
await this.ctx.reloadTodos();
|
|
202
202
|
this.ctx.editor.setText(result.selectedText);
|
|
203
203
|
this.ctx.showStatus("Branched to new session");
|
|
@@ -212,7 +212,7 @@ export class ExtensionUiController {
|
|
|
212
212
|
|
|
213
213
|
// Update UI
|
|
214
214
|
this.ctx.chatContainer.clear();
|
|
215
|
-
this.ctx.renderInitialMessages(
|
|
215
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
216
216
|
await this.ctx.reloadTodos();
|
|
217
217
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
218
218
|
this.ctx.editor.setText(result.editorText);
|
|
@@ -230,7 +230,7 @@ export class ExtensionUiController {
|
|
|
230
230
|
}
|
|
231
231
|
setSessionTerminalTitle(this.ctx.sessionManager.getSessionName(), this.ctx.sessionManager.getCwd());
|
|
232
232
|
this.ctx.chatContainer.clear();
|
|
233
|
-
this.ctx.renderInitialMessages(
|
|
233
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
234
234
|
await this.ctx.reloadTodos();
|
|
235
235
|
return { cancelled: false };
|
|
236
236
|
},
|
|
@@ -376,7 +376,7 @@ export class ExtensionUiController {
|
|
|
376
376
|
reload: async () => {
|
|
377
377
|
await this.ctx.session.reload();
|
|
378
378
|
this.ctx.chatContainer.clear();
|
|
379
|
-
this.ctx.renderInitialMessages(
|
|
379
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
380
380
|
await this.ctx.reloadTodos();
|
|
381
381
|
this.ctx.showStatus("Reloaded session");
|
|
382
382
|
},
|
|
@@ -426,7 +426,7 @@ export class ExtensionUiController {
|
|
|
426
426
|
|
|
427
427
|
// Update UI
|
|
428
428
|
this.ctx.chatContainer.clear();
|
|
429
|
-
this.ctx.renderInitialMessages(
|
|
429
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
430
430
|
await this.ctx.reloadTodos();
|
|
431
431
|
this.ctx.editor.setText(result.selectedText);
|
|
432
432
|
this.ctx.showStatus("Branched to new session");
|
|
@@ -441,7 +441,7 @@ export class ExtensionUiController {
|
|
|
441
441
|
|
|
442
442
|
// Update UI
|
|
443
443
|
this.ctx.chatContainer.clear();
|
|
444
|
-
this.ctx.renderInitialMessages(
|
|
444
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
445
445
|
await this.ctx.reloadTodos();
|
|
446
446
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
447
447
|
this.ctx.editor.setText(result.editorText);
|
|
@@ -458,7 +458,7 @@ export class ExtensionUiController {
|
|
|
458
458
|
return { cancelled: true };
|
|
459
459
|
}
|
|
460
460
|
this.ctx.chatContainer.clear();
|
|
461
|
-
this.ctx.renderInitialMessages(
|
|
461
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
462
462
|
await this.ctx.reloadTodos();
|
|
463
463
|
return { cancelled: false };
|
|
464
464
|
},
|
|
@@ -235,10 +235,26 @@ export class InputController {
|
|
|
235
235
|
for (const key of this.ctx.keybindings.getKeys("app.clipboard.copyLine")) {
|
|
236
236
|
this.ctx.editor.setCustomKeyHandler(key, () => this.handleCopyCurrentLine());
|
|
237
237
|
}
|
|
238
|
-
|
|
239
|
-
this.ctx.
|
|
238
|
+
const hubKeys = new Set([
|
|
239
|
+
...this.ctx.keybindings.getKeys("app.agents.hub"),
|
|
240
|
+
...this.ctx.keybindings.getKeys("app.session.observe"),
|
|
241
|
+
]);
|
|
242
|
+
for (const key of hubKeys) {
|
|
243
|
+
this.ctx.editor.setCustomKeyHandler(key, () => this.ctx.showAgentHub());
|
|
240
244
|
}
|
|
241
245
|
|
|
246
|
+
// Double-tap left arrow on an empty editor opens the agent hub — same
|
|
247
|
+
// 500ms window as the double-escape state machine above.
|
|
248
|
+
this.ctx.editor.onLeftAtStart = () => {
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
if (now - this.ctx.lastLeftTapTime < 500) {
|
|
251
|
+
this.ctx.lastLeftTapTime = 0;
|
|
252
|
+
this.ctx.showAgentHub();
|
|
253
|
+
} else {
|
|
254
|
+
this.ctx.lastLeftTapTime = now;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
242
258
|
this.#setupEnhancedPaste();
|
|
243
259
|
|
|
244
260
|
this.ctx.editor.onChange = (text: string) => {
|
|
@@ -40,6 +40,7 @@ import { shortenPath } from "../../tools/render-utils";
|
|
|
40
40
|
import { copyToClipboard } from "../../utils/clipboard";
|
|
41
41
|
import { setSessionTerminalTitle } from "../../utils/title-generator";
|
|
42
42
|
import { AgentDashboard } from "../components/agent-dashboard";
|
|
43
|
+
import { AgentHubOverlayComponent } from "../components/agent-hub";
|
|
43
44
|
import { AssistantMessageComponent } from "../components/assistant-message";
|
|
44
45
|
import { CopySelectorComponent } from "../components/copy-selector";
|
|
45
46
|
import { ExtensionDashboard } from "../components/extensions";
|
|
@@ -47,7 +48,6 @@ import { HistorySearchComponent } from "../components/history-search";
|
|
|
47
48
|
import { ModelSelectorComponent } from "../components/model-selector";
|
|
48
49
|
import { OAuthSelectorComponent } from "../components/oauth-selector";
|
|
49
50
|
import { PluginSelectorComponent } from "../components/plugin-selector";
|
|
50
|
-
import { SessionObserverOverlayComponent } from "../components/session-observer-overlay";
|
|
51
51
|
import { SessionSelectorComponent } from "../components/session-selector";
|
|
52
52
|
import { SettingsSelectorComponent } from "../components/settings-selector";
|
|
53
53
|
import { ToolExecutionComponent } from "../components/tool-execution";
|
|
@@ -578,7 +578,7 @@ export class SelectorController {
|
|
|
578
578
|
}
|
|
579
579
|
|
|
580
580
|
this.ctx.chatContainer.clear();
|
|
581
|
-
this.ctx.renderInitialMessages(
|
|
581
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
582
582
|
this.ctx.editor.setText(result.selectedText);
|
|
583
583
|
done();
|
|
584
584
|
this.ctx.showStatus("Branched to new session");
|
|
@@ -719,9 +719,10 @@ export class SelectorController {
|
|
|
719
719
|
return;
|
|
720
720
|
}
|
|
721
721
|
|
|
722
|
-
// Update UI —
|
|
722
|
+
// Update UI — rebuild the display transcript for the new leaf (the
|
|
723
|
+
// context from navigateTree is the LLM context, not the transcript).
|
|
723
724
|
this.ctx.chatContainer.clear();
|
|
724
|
-
this.ctx.renderInitialMessages(
|
|
725
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
725
726
|
await this.ctx.reloadTodos();
|
|
726
727
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
727
728
|
this.ctx.editor.setText(result.editorText);
|
|
@@ -846,7 +847,7 @@ export class SelectorController {
|
|
|
846
847
|
this.ctx.statusLine.setSessionStartTime(Date.now());
|
|
847
848
|
this.ctx.updateEditorTopBorder();
|
|
848
849
|
this.ctx.updateEditorBorderColor();
|
|
849
|
-
this.ctx.renderInitialMessages(
|
|
850
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
850
851
|
await this.ctx.reloadTodos();
|
|
851
852
|
this.ctx.ui.requestRender(true, { clearScrollback: true });
|
|
852
853
|
return true;
|
|
@@ -871,7 +872,7 @@ export class SelectorController {
|
|
|
871
872
|
|
|
872
873
|
// Clear and re-render the chat
|
|
873
874
|
this.ctx.chatContainer.clear();
|
|
874
|
-
this.ctx.renderInitialMessages(
|
|
875
|
+
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
875
876
|
await this.ctx.reloadTodos();
|
|
876
877
|
this.ctx.showStatus(movedProject ? `Resumed session in ${shortenPath(newCwd)}` : "Resumed session");
|
|
877
878
|
}
|
|
@@ -1074,31 +1075,34 @@ export class SelectorController {
|
|
|
1074
1075
|
});
|
|
1075
1076
|
}
|
|
1076
1077
|
|
|
1077
|
-
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1078
|
+
showAgentHub(observers: SessionObserverRegistry): void {
|
|
1079
|
+
const hubKeys = [
|
|
1080
|
+
...this.ctx.keybindings.getKeys("app.agents.hub"),
|
|
1081
|
+
...this.ctx.keybindings.getKeys("app.session.observe"),
|
|
1082
|
+
];
|
|
1083
|
+
let hub: AgentHubOverlayComponent | undefined;
|
|
1080
1084
|
let overlayHandle: OverlayHandle | undefined;
|
|
1081
1085
|
|
|
1082
1086
|
const done = () => {
|
|
1083
|
-
|
|
1087
|
+
hub?.dispose();
|
|
1084
1088
|
overlayHandle?.hide();
|
|
1085
1089
|
this.ctx.ui.requestRender();
|
|
1086
1090
|
};
|
|
1087
1091
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
this.ctx.ui.requestRender()
|
|
1092
|
+
hub = new AgentHubOverlayComponent({
|
|
1093
|
+
observers,
|
|
1094
|
+
hubKeys,
|
|
1095
|
+
onDone: done,
|
|
1096
|
+
requestRender: () => this.ctx.ui.requestRender(),
|
|
1093
1097
|
});
|
|
1094
1098
|
|
|
1095
|
-
overlayHandle = this.ctx.ui.showOverlay(
|
|
1099
|
+
overlayHandle = this.ctx.ui.showOverlay(hub, {
|
|
1096
1100
|
anchor: "bottom-center",
|
|
1097
1101
|
width: "100%",
|
|
1098
1102
|
maxHeight: "100%",
|
|
1099
1103
|
margin: 0,
|
|
1100
1104
|
});
|
|
1101
|
-
this.ctx.ui.setFocus(
|
|
1105
|
+
this.ctx.ui.setFocus(hub);
|
|
1102
1106
|
this.ctx.ui.requestRender();
|
|
1103
1107
|
}
|
|
1104
1108
|
}
|
|
@@ -327,6 +327,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
327
327
|
#pendingSubmissionDispose: (() => void) | undefined;
|
|
328
328
|
lastSigintTime = 0;
|
|
329
329
|
lastEscapeTime = 0;
|
|
330
|
+
lastLeftTapTime = 0;
|
|
330
331
|
shutdownRequested = false;
|
|
331
332
|
#isShuttingDown = false;
|
|
332
333
|
hookSelector: HookSelectorComponent | undefined = undefined;
|
|
@@ -1088,7 +1089,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1088
1089
|
|
|
1089
1090
|
rebuildChatFromMessages(): void {
|
|
1090
1091
|
this.chatContainer.clear();
|
|
1091
|
-
|
|
1092
|
+
// Full-history transcript: compactions render as inline dividers instead
|
|
1093
|
+
// of restarting the visible conversation (the LLM context still resets).
|
|
1094
|
+
const context = this.session.buildTranscriptSessionContext();
|
|
1092
1095
|
this.renderSessionContext(context);
|
|
1093
1096
|
}
|
|
1094
1097
|
|
|
@@ -2880,11 +2883,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2880
2883
|
this.#uiHelpers.renderSessionContext(sessionContext, options);
|
|
2881
2884
|
}
|
|
2882
2885
|
|
|
2883
|
-
renderInitialMessages(
|
|
2884
|
-
|
|
2885
|
-
options?: { preserveExistingChat?: boolean; clearTerminalHistory?: boolean },
|
|
2886
|
-
): void {
|
|
2887
|
-
this.#uiHelpers.renderInitialMessages(prebuiltContext, options);
|
|
2886
|
+
renderInitialMessages(options?: { preserveExistingChat?: boolean; clearTerminalHistory?: boolean }): void {
|
|
2887
|
+
this.#uiHelpers.renderInitialMessages(options);
|
|
2888
2888
|
}
|
|
2889
2889
|
|
|
2890
2890
|
getUserMessageText(message: Message): string {
|
|
@@ -3068,13 +3068,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3068
3068
|
await this.#selectorController.showDebugSelector();
|
|
3069
3069
|
}
|
|
3070
3070
|
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
if (sessions.length <= 1) {
|
|
3074
|
-
this.showStatus("No active subagent sessions");
|
|
3075
|
-
return;
|
|
3076
|
-
}
|
|
3077
|
-
this.#selectorController.showSessionObserver(this.#observerRegistry);
|
|
3071
|
+
showAgentHub(): void {
|
|
3072
|
+
this.#selectorController.showAgentHub(this.#observerRegistry);
|
|
3078
3073
|
}
|
|
3079
3074
|
|
|
3080
3075
|
resetObserverRegistry(): void {
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -129,6 +129,8 @@ export type SymbolKey =
|
|
|
129
129
|
| "icon.extensionInstruction"
|
|
130
130
|
// STT
|
|
131
131
|
| "icon.mic"
|
|
132
|
+
// Compaction divider
|
|
133
|
+
| "icon.camera"
|
|
132
134
|
// Thinking Levels
|
|
133
135
|
| "thinking.minimal"
|
|
134
136
|
| "thinking.low"
|
|
@@ -220,7 +222,8 @@ export type SymbolKey =
|
|
|
220
222
|
| "tool.resolve"
|
|
221
223
|
| "tool.review"
|
|
222
224
|
| "tool.inspectImage"
|
|
223
|
-
| "tool.goal"
|
|
225
|
+
| "tool.goal"
|
|
226
|
+
| "tool.irc";
|
|
224
227
|
|
|
225
228
|
type SymbolMap = Record<SymbolKey, string>;
|
|
226
229
|
|
|
@@ -322,13 +325,15 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
322
325
|
"icon.extensionInstruction": "📘",
|
|
323
326
|
// STT
|
|
324
327
|
"icon.mic": "🎤",
|
|
328
|
+
// Compaction divider
|
|
329
|
+
"icon.camera": "📷",
|
|
325
330
|
// Thinking levels
|
|
326
331
|
"thinking.minimal": "◔ min",
|
|
327
332
|
"thinking.low": "◑ low",
|
|
328
333
|
"thinking.medium": "◒ med",
|
|
329
334
|
"thinking.high": "◕ high",
|
|
330
335
|
"thinking.xhigh": "◉ xhigh",
|
|
331
|
-
"thinking.autoPending": "
|
|
336
|
+
"thinking.autoPending": "⟳",
|
|
332
337
|
// Checkboxes
|
|
333
338
|
"checkbox.checked": "☑",
|
|
334
339
|
"checkbox.unchecked": "☐",
|
|
@@ -414,6 +419,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
414
419
|
"tool.review": "◉",
|
|
415
420
|
"tool.inspectImage": "🖼",
|
|
416
421
|
"tool.goal": "◎",
|
|
422
|
+
"tool.irc": "✉",
|
|
417
423
|
};
|
|
418
424
|
|
|
419
425
|
const NERD_SYMBOLS: SymbolMap = {
|
|
@@ -599,6 +605,8 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
599
605
|
"icon.extensionInstruction": "\uf02d",
|
|
600
606
|
// STT - fa-microphone
|
|
601
607
|
"icon.mic": "\uf130",
|
|
608
|
+
// Compaction divider - fa-camera-retro
|
|
609
|
+
"icon.camera": "\uf083",
|
|
602
610
|
// Thinking Levels - emoji labels
|
|
603
611
|
// pick: 🤨 min | alt: min min
|
|
604
612
|
"thinking.minimal": "\u{F0E7} min",
|
|
@@ -610,8 +618,8 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
610
618
|
"thinking.high": "\u{F111} high",
|
|
611
619
|
// pick: 🧠 xhi | alt: xhi xhi
|
|
612
620
|
"thinking.xhigh": "\u{F06D} xhi",
|
|
613
|
-
// pick:
|
|
614
|
-
"thinking.autoPending": "\
|
|
621
|
+
// pick: (fa-circle-o-notch) | alt: (nf-md-cached) ⟳
|
|
622
|
+
"thinking.autoPending": "\uf1ce",
|
|
615
623
|
// Checkboxes
|
|
616
624
|
// pick: | alt:
|
|
617
625
|
"checkbox.checked": "\uf14a",
|
|
@@ -708,6 +716,7 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
708
716
|
"tool.review": "\uEA70",
|
|
709
717
|
"tool.inspectImage": "\uEAEA",
|
|
710
718
|
"tool.goal": "\uEBF8",
|
|
719
|
+
"tool.irc": "\uF086",
|
|
711
720
|
};
|
|
712
721
|
|
|
713
722
|
const ASCII_SYMBOLS: SymbolMap = {
|
|
@@ -808,13 +817,15 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
808
817
|
"icon.extensionInstruction": "IN",
|
|
809
818
|
// STT
|
|
810
819
|
"icon.mic": "MIC",
|
|
820
|
+
// Compaction divider
|
|
821
|
+
"icon.camera": "[o]",
|
|
811
822
|
// Thinking Levels
|
|
812
823
|
"thinking.minimal": "[min]",
|
|
813
824
|
"thinking.low": "[low]",
|
|
814
825
|
"thinking.medium": "[med]",
|
|
815
826
|
"thinking.high": "[high]",
|
|
816
827
|
"thinking.xhigh": "[xhi]",
|
|
817
|
-
"thinking.autoPending": "[
|
|
828
|
+
"thinking.autoPending": "[~]",
|
|
818
829
|
// Checkboxes
|
|
819
830
|
"checkbox.checked": "[x]",
|
|
820
831
|
"checkbox.unchecked": "[ ]",
|
|
@@ -898,6 +909,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
898
909
|
"tool.review": "rev",
|
|
899
910
|
"tool.inspectImage": "[i]",
|
|
900
911
|
"tool.goal": "(o)",
|
|
912
|
+
"tool.irc": "irc",
|
|
901
913
|
};
|
|
902
914
|
|
|
903
915
|
const SYMBOL_PRESETS: Record<SymbolPreset, SymbolMap> = {
|
|
@@ -1686,6 +1698,7 @@ export class Theme {
|
|
|
1686
1698
|
extensionContextFile: this.#symbols["icon.extensionContextFile"],
|
|
1687
1699
|
extensionInstruction: this.#symbols["icon.extensionInstruction"],
|
|
1688
1700
|
mic: this.#symbols["icon.mic"],
|
|
1701
|
+
camera: this.#symbols["icon.camera"],
|
|
1689
1702
|
};
|
|
1690
1703
|
}
|
|
1691
1704
|
|
package/src/modes/types.ts
CHANGED
|
@@ -136,6 +136,7 @@ export interface InteractiveModeContext {
|
|
|
136
136
|
locallySubmittedUserSignatures: Set<string>;
|
|
137
137
|
lastSigintTime: number;
|
|
138
138
|
lastEscapeTime: number;
|
|
139
|
+
lastLeftTapTime: number;
|
|
139
140
|
shutdownRequested: boolean;
|
|
140
141
|
hookSelector: HookSelectorComponent | undefined;
|
|
141
142
|
hookInput: HookInputComponent | undefined;
|
|
@@ -225,10 +226,7 @@ export interface InteractiveModeContext {
|
|
|
225
226
|
sessionContext: SessionContext,
|
|
226
227
|
options?: { updateFooter?: boolean; populateHistory?: boolean },
|
|
227
228
|
): void;
|
|
228
|
-
renderInitialMessages(
|
|
229
|
-
prebuiltContext?: SessionContext,
|
|
230
|
-
options?: { preserveExistingChat?: boolean; clearTerminalHistory?: boolean },
|
|
231
|
-
): void;
|
|
229
|
+
renderInitialMessages(options?: { preserveExistingChat?: boolean; clearTerminalHistory?: boolean }): void;
|
|
232
230
|
getUserMessageText(message: Message): string;
|
|
233
231
|
findLastAssistantMessage(): AssistantMessage | undefined;
|
|
234
232
|
extractAssistantText(message: AssistantMessage): string;
|
|
@@ -292,7 +290,7 @@ export interface InteractiveModeContext {
|
|
|
292
290
|
showProviderSetup(): Promise<void>;
|
|
293
291
|
showHookConfirm(title: string, message: string): Promise<boolean>;
|
|
294
292
|
showDebugSelector(): Promise<void>;
|
|
295
|
-
|
|
293
|
+
showAgentHub(): void;
|
|
296
294
|
resetObserverRegistry(): void;
|
|
297
295
|
|
|
298
296
|
// Input handling
|
|
@@ -50,6 +50,7 @@ export function buildHotkeysMarkdown(bindings: HotkeysMarkdownBindings): string
|
|
|
50
50
|
`| \`${appKey(bindings, "app.editor.external")}\` | Edit message in external editor |`,
|
|
51
51
|
`| \`${appKey(bindings, "app.clipboard.pasteImage")}\` | Paste image from clipboard |`,
|
|
52
52
|
`| \`${appKey(bindings, "app.stt.toggle")}\` | Toggle speech-to-text recording |`,
|
|
53
|
+
`| \`${appKey(bindings, "app.agents.hub")}\` / \`${appKey(bindings, "app.session.observe")}\` / double-tap \`←\` (empty editor) | Open the agent hub |`,
|
|
53
54
|
"| `#` | Open prompt actions |",
|
|
54
55
|
"| `/` | Slash commands |",
|
|
55
56
|
"| `!` | Run bash command |",
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
type SkillPromptDetails,
|
|
36
36
|
} from "../../session/messages";
|
|
37
37
|
import type { SessionContext } from "../../session/session-manager";
|
|
38
|
+
import { createIrcMessageCard } from "../../tools/irc";
|
|
38
39
|
import { formatBytes, formatDuration } from "../../tools/render-utils";
|
|
39
40
|
|
|
40
41
|
type TextBlock = { type: "text"; text: string };
|
|
@@ -190,49 +191,31 @@ export class UiHelpers {
|
|
|
190
191
|
this.ctx.chatContainer.addChild(component);
|
|
191
192
|
break;
|
|
192
193
|
}
|
|
193
|
-
if (
|
|
194
|
-
message.customType === "irc:incoming" ||
|
|
195
|
-
message.customType === "irc:autoreply" ||
|
|
196
|
-
message.customType === "irc:relay"
|
|
197
|
-
) {
|
|
194
|
+
if (message.customType === "irc:incoming" || message.customType === "irc:relay") {
|
|
198
195
|
const details = (
|
|
199
196
|
message as CustomMessage<{
|
|
200
197
|
from?: string;
|
|
201
198
|
to?: string;
|
|
202
199
|
message?: string;
|
|
203
|
-
reply?: string;
|
|
204
200
|
body?: string;
|
|
205
|
-
|
|
201
|
+
replyTo?: string;
|
|
206
202
|
}>
|
|
207
203
|
).details;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
const block = new TranscriptBlock();
|
|
225
|
-
const header = `${theme.fg("accent", `[IRC] ${arrow}`)}`;
|
|
226
|
-
const headerComponent = new Text(header, 1, 0);
|
|
227
|
-
block.addChild(headerComponent);
|
|
228
|
-
if (body) {
|
|
229
|
-
for (const line of body.split("\n")) {
|
|
230
|
-
const lineComponent = new Text(theme.fg("muted", ` ${line}`), 0, 0);
|
|
231
|
-
block.addChild(lineComponent);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
this.ctx.chatContainer.addChild(block);
|
|
235
|
-
return [block];
|
|
204
|
+
const incoming = message.customType === "irc:incoming";
|
|
205
|
+
const card = createIrcMessageCard(
|
|
206
|
+
{
|
|
207
|
+
kind: incoming ? "incoming" : "relay",
|
|
208
|
+
from: details?.from,
|
|
209
|
+
to: details?.to,
|
|
210
|
+
body: incoming ? details?.message : details?.body,
|
|
211
|
+
replyTo: details?.replyTo,
|
|
212
|
+
timestamp: message.timestamp,
|
|
213
|
+
},
|
|
214
|
+
() => this.ctx.toolOutputExpanded,
|
|
215
|
+
theme,
|
|
216
|
+
);
|
|
217
|
+
this.ctx.chatContainer.addChild(card);
|
|
218
|
+
return [card];
|
|
236
219
|
}
|
|
237
220
|
const renderer = this.ctx.session.extensionRunner?.getMessageRenderer(message.customType);
|
|
238
221
|
// Both HookMessage and CustomMessage have the same structure, cast for compatibility
|
|
@@ -337,13 +320,23 @@ export class UiHelpers {
|
|
|
337
320
|
let readGroup: ReadToolGroupComponent | null = null;
|
|
338
321
|
const readToolCallArgs = new Map<string, Record<string, unknown>>();
|
|
339
322
|
const readToolCallAssistantComponents = new Map<string, AssistantMessageComponent>();
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
323
|
+
// Rebuild-time mirror of the event controller's displaceable-poll
|
|
324
|
+
// bookkeeping: a `job` poll that found every watched job still running is
|
|
325
|
+
// superseded by the next `job` call, so a rebuilt transcript collapses a
|
|
326
|
+
// repeated-poll run to its final snapshot instead of replaying the spam.
|
|
327
|
+
let waitingPoll: ToolExecutionComponent | null = null;
|
|
328
|
+
const resolveWaitingPoll = (nextToolName?: string) => {
|
|
329
|
+
const previous = waitingPoll;
|
|
330
|
+
if (!previous) return;
|
|
331
|
+
waitingPoll = null;
|
|
332
|
+
if (nextToolName === "job" && previous.isDisplaceableBlock()) {
|
|
333
|
+
this.ctx.chatContainer.removeChild(previous);
|
|
346
334
|
}
|
|
335
|
+
// Sealing freezes the block and stops the waiting-poll spinner that
|
|
336
|
+
// updateResult armed.
|
|
337
|
+
previous.seal();
|
|
338
|
+
};
|
|
339
|
+
for (const message of sessionContext.messages) {
|
|
347
340
|
// Assistant messages need special handling for tool calls
|
|
348
341
|
if (message.role === "assistant") {
|
|
349
342
|
this.ctx.addMessageToChat(message);
|
|
@@ -379,6 +372,7 @@ export class UiHelpers {
|
|
|
379
372
|
if (content.type !== "toolCall") {
|
|
380
373
|
continue;
|
|
381
374
|
}
|
|
375
|
+
resolveWaitingPoll(content.name);
|
|
382
376
|
|
|
383
377
|
if (
|
|
384
378
|
content.name === "read" &&
|
|
@@ -493,8 +487,17 @@ export class UiHelpers {
|
|
|
493
487
|
if (component) {
|
|
494
488
|
component.updateResult(message, false, message.toolCallId);
|
|
495
489
|
this.ctx.pendingTools.delete(message.toolCallId);
|
|
490
|
+
if (
|
|
491
|
+
message.toolName === "job" &&
|
|
492
|
+
component instanceof ToolExecutionComponent &&
|
|
493
|
+
component.isDisplaceableBlock()
|
|
494
|
+
) {
|
|
495
|
+
waitingPoll = component;
|
|
496
|
+
}
|
|
496
497
|
}
|
|
497
498
|
} else {
|
|
499
|
+
// A user prompt closes the displacement window, same as the live path.
|
|
500
|
+
if (message.role === "user") resolveWaitingPoll();
|
|
498
501
|
// All other messages use standard rendering
|
|
499
502
|
this.ctx.addMessageToChat(message, options);
|
|
500
503
|
}
|
|
@@ -504,17 +507,15 @@ export class UiHelpers {
|
|
|
504
507
|
// rebuilt group freezes (even with a never-persisted result) and commits to
|
|
505
508
|
// native scrollback like every other historical block.
|
|
506
509
|
readGroup?.seal();
|
|
507
|
-
|
|
508
|
-
//
|
|
509
|
-
|
|
510
|
-
this.ctx.addMessageToChat(message, options);
|
|
511
|
-
}
|
|
510
|
+
// A trailing waiting poll is final history on rebuild; seal it so it
|
|
511
|
+
// freezes (and its spinner timer stops) like every other block.
|
|
512
|
+
resolveWaitingPoll();
|
|
512
513
|
|
|
513
514
|
this.ctx.pendingTools.clear();
|
|
514
515
|
this.ctx.ui.requestRender();
|
|
515
516
|
}
|
|
516
517
|
|
|
517
|
-
renderInitialMessages(
|
|
518
|
+
renderInitialMessages(options: RenderInitialMessagesOptions = {}): void {
|
|
518
519
|
// This path is used to rebuild the visible chat transcript (e.g. after custom/debug UI).
|
|
519
520
|
// Clear existing rendered chat first to avoid duplicating the full session in the container.
|
|
520
521
|
// On a non-preserving rebuild the existing blocks are discarded for good, so
|
|
@@ -530,8 +531,9 @@ export class UiHelpers {
|
|
|
530
531
|
this.ctx.pendingBashComponents = [];
|
|
531
532
|
this.ctx.pendingPythonComponents = [];
|
|
532
533
|
|
|
533
|
-
//
|
|
534
|
-
|
|
534
|
+
// Display always uses the full-history transcript: compactions show as
|
|
535
|
+
// inline dividers instead of restarting the visible conversation.
|
|
536
|
+
const context = this.ctx.session.buildTranscriptSessionContext();
|
|
535
537
|
this.ctx.renderSessionContext(context, {
|
|
536
538
|
updateFooter: true,
|
|
537
539
|
populateHistory: true,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<irc>
|
|
2
|
-
|
|
2
|
+
Incoming IRC message from agent `{{from}}`{{#if replyTo}} (replying to {{replyTo}}){{/if}}:
|
|
3
3
|
|
|
4
|
-
Reply briefly and directly using the conversation context already available to you. NEVER call tools. The reply you write is delivered back to `{{from}}` as your answer.
|
|
5
|
-
|
|
6
|
-
Message:
|
|
7
4
|
{{message}}
|
|
5
|
+
|
|
6
|
+
If a response is expected, reply with the `irc` tool (`op: "send"`, `to: "{{from}}"`) — you may finish your current step first. Nobody replies on your behalf.
|
|
8
7
|
</irc>
|
|
@@ -8,7 +8,7 @@ You decompose, dispatch, verify, and iterate. Substantial and parallelizable wor
|
|
|
8
8
|
<rules>
|
|
9
9
|
1. **NEVER yield until everything is closed.** A phase finishing is *not* a yield point — launch the next phase in the same turn. Stop only when every requested item is verifiably done, or you hit a concrete [blocked] state that genuinely requires the user.
|
|
10
10
|
2. **Enumerate the full surface before dispatching.** If the request references audits, plans, checklists, phase lists, or file lists, expand them into a flat set of items in `todo`. "Most of them" or "the important ones" is failure. Re-read the source documents — NEVER work from memory.
|
|
11
|
-
3. **Parallelize maximally; NEVER launch a one-off task.** Every set of edits with disjoint file scope MUST ship as
|
|
11
|
+
3. **Parallelize maximally; NEVER launch a one-off task.** Every set of edits with disjoint file scope MUST ship as parallel `task` calls in one message — fan the work as wide as it decomposes. Dispatching divisible work one call at a time, serially, is a failure: split it and dispatch together. If you are about to dispatch exactly one subagent, stop — either there is more to run alongside it (find it and dispatch them together) or the change is small enough to make inline yourself (do it). Serialize only when one subagent produces a contract (types, schema, shared module) the next consumes — and state the dependency when you do.
|
|
12
12
|
4. **Each `task` assignment is self-contained.** Subagents have no shared context. Spell out: target files (≤3–5 explicit paths, no globs), the change with APIs and patterns, edge cases, and observable acceptance criteria. NEVER assume they read the same plan you did.
|
|
13
13
|
5. **Verify after every phase before launching the next.** Run the appropriate gate: `bun check` for types, package-scoped `bun test` for behavior, `lsp diagnostics` for changed files. If a phase introduced breakage, dispatch fix-up subagents *before* moving on. NEVER declare a phase done on a red tree.
|
|
14
14
|
6. **Commit policy.** If the request asks for commits or the repo workflow expects them, commit after each green phase with a focused message. NEVER commit a red tree. NEVER commit work the user did not ask to commit.
|
|
@@ -21,7 +21,7 @@ You decompose, dispatch, verify, and iterate. Substantial and parallelizable wor
|
|
|
21
21
|
<workflow>
|
|
22
22
|
1. **Ingest.** Read every referenced file (audits, plans, prior agent output, current branch state). Run `git status` to see uncommitted changes.
|
|
23
23
|
2. **Plan.** Materialize the full work surface in `todo` as ordered phases. Within each phase, list the parallelizable units.
|
|
24
|
-
3. **Dispatch phase.** Launch all parallel `task` subagents in one
|
|
24
|
+
3. **Dispatch phase.** Launch all parallel `task` subagents in one message, then collect every result (async results / `job poll`) before moving on.
|
|
25
25
|
4. **Verify phase.** Run the gates. On failure, dispatch fix-up subagents and re-verify. Do not advance with a red gate.
|
|
26
26
|
5. **Commit phase** (if applicable). Focused message naming the phase.
|
|
27
27
|
6. **Advance.** Mark the phase done in `todo`, immediately start the next phase. No summary message between phases — keep going.
|
|
@@ -32,11 +32,6 @@ You are working in an isolated working tree at `{{worktree}}` for this sub-task.
|
|
|
32
32
|
You NEVER modify files outside this tree or in the original repository.
|
|
33
33
|
{{/if}}
|
|
34
34
|
|
|
35
|
-
{{#if contextFile}}
|
|
36
|
-
# Conversation Context
|
|
37
|
-
If you need additional information, your conversation with the user is in {{contextFile}} — `read` its tail or `search` it for relevant terms.
|
|
38
|
-
{{/if}}
|
|
39
|
-
|
|
40
35
|
{{#if ircPeers}}
|
|
41
36
|
# IRC Peers
|
|
42
37
|
You can reach other live agents via the `irc` tool. Your id is `{{ircSelfId}}`. Currently visible peers:
|
|
@@ -149,6 +149,7 @@ With most FS/bash-like tools, static references to them will automatically resol
|
|
|
149
149
|
- `agent://<id>`: full agent output artifact
|
|
150
150
|
- `/<path>`: JSON field extraction
|
|
151
151
|
- `artifact://<id>`: Artifact content
|
|
152
|
+
- `history://<agentId>`: agent transcript as concise markdown; bare `history://` lists agents
|
|
152
153
|
- `local://<name>.md`: Plan artifacts and shared content with subagents
|
|
153
154
|
{{#if hasObsidian}}
|
|
154
155
|
- `vault://<vault>/<path>`: Obsidian vault content (read/edit). `vault://` lists vaults; `vault://_/…` targets the active vault. File-scoped `?op=outline|backlinks|links|tags|properties|tasks|base|…`; vault-scoped `?op=search&q=…|daily|tasks|orphans|unresolved|bases|…`.
|
|
@@ -13,8 +13,8 @@ Worth it when the task benefits from decomposition + parallel coverage, or from
|
|
|
13
13
|
<helpers>
|
|
14
14
|
State persists across cells, so scout in one cell and fan out in the next. Every cell has:
|
|
15
15
|
|
|
16
|
-
- `agent(prompt, *, agent_type="task", model=None,
|
|
17
|
-
- `parallel(thunks)` — run zero-arg callables concurrently through a bounded pool, preserving input order; returns once all finish. The pool
|
|
16
|
+
- `agent(prompt, *, agent_type="task", model=None, label=None, schema=None)` — run ONE subagent; returns its final text, or the validated object when `schema` (a JSON Schema dict) is given. With `schema` the subagent is forced to emit structured output that is validated for you — branch on the object, not on parsed prose. `agent_type` picks a discovered agent ("explore", "reviewer", "oracle", …); `label` names the artifact. Shared background goes in a `local://` file referenced from each prompt, not a parameter. Subagents are told their final text IS the return value, so they hand back raw data. `agent()` blocks until the subagent finishes; eval-spawned agents nest at most 3 deep.
|
|
17
|
+
- `parallel(thunks)` — run zero-arg callables concurrently through a bounded pool, preserving input order; returns once all finish. The pool is bounded by the session's `task` concurrency — don't hand-tune it; fan out as wide as the work divides. A thunk that raises propagates — wrap risky work in `try/except` inside the thunk to keep partial results. In a loop, bind each closure's value with a default arg (`lambda d=d: …`) or every thunk captures the last one.
|
|
18
18
|
- `pipeline(items, *stages)` — map items through `stages` left-to-right. There is a BARRIER between stages: ALL items clear stage N before stage N+1 begins. Each stage is a one-arg callable; stage 1 gets the original item, later stages get the previous result. Same pool width as `parallel()`.
|
|
19
19
|
- `completion(prompt, *, model="default", system=None, schema=None)` — oneshot, stateless model call (no tools, no history). Tiers: "smol", "default", "slow". Cheap classification/scoring inside a fan-out.
|
|
20
20
|
- `log(message)` — emit a progress line above the status tree. `phase(title)` — start a phase; the status lines that follow group under it.
|
|
@@ -46,9 +46,9 @@ tool.<name>(args) → unknown
|
|
|
46
46
|
Invoke any session tool by name. `args` is the tool's parameter object.
|
|
47
47
|
completion(prompt, model?="default", system?=None, schema?=None) → str | dict
|
|
48
48
|
Oneshot, stateless completion (no history, no tools). `model` picks a tier: "smol" (fast), "default" (this session's model), "slow" (most capable). Pass `system` for a system prompt. Pass a JSON-Schema `schema` to force structured output and get the parsed object back; otherwise returns the completion text.
|
|
49
|
-
{{#if spawns}}agent(prompt, agent_type?="task", model?=None,
|
|
50
|
-
Run a subagent and return its final output. Defaults to the bundled "task" agent; pass `agent_type`/`agentType` for another discovered agent. Pass a JSON-Schema `schema` to force structured output and get the parsed object back.
|
|
51
|
-
{{#if js}} In JS, pass options as one trailing object — never positional: agent(prompt, { agentType,
|
|
49
|
+
{{#if spawns}}agent(prompt, agent_type?="task", model?=None, label?=None, schema?=None) → str | dict
|
|
50
|
+
Run a subagent and return its final output. Defaults to the bundled "task" agent; pass `agent_type`/`agentType` for another discovered agent. Pass a JSON-Schema `schema` to force structured output and get the parsed object back. Share background by writing a `local://` file and referencing it in the prompt.
|
|
51
|
+
{{#if js}} In JS, pass options as one trailing object — never positional: agent(prompt, { agentType, schema }).
|
|
52
52
|
{{/if}}
|
|
53
53
|
{{/if}}
|
|
54
54
|
parallel(thunks) → list
|