@oh-my-pi/pi-coding-agent 16.0.0 → 16.0.1
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 +115 -133
- package/dist/cli.js +158 -130
- package/dist/types/config/settings-schema.d.ts +22 -0
- package/dist/types/discovery/helpers.d.ts +7 -0
- package/dist/types/eval/__tests__/prelude-agent.test.d.ts +1 -0
- package/dist/types/extensibility/plugins/runtime-config.d.ts +3 -0
- package/dist/types/modes/types.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +11 -1
- package/dist/types/session/session-manager.d.ts +4 -1
- package/dist/types/task/index.d.ts +21 -0
- package/dist/types/tools/github-cache.d.ts +5 -4
- package/dist/types/tools/job.d.ts +1 -0
- package/dist/types/web/search/index.d.ts +2 -2
- package/dist/types/web/search/provider.d.ts +2 -0
- package/package.json +12 -12
- package/src/cli/args.ts +1 -0
- package/src/collab/host.ts +1 -1
- package/src/config/settings-schema.ts +23 -1
- package/src/discovery/claude-plugins.ts +3 -42
- package/src/discovery/github.ts +101 -6
- package/src/discovery/helpers.ts +11 -0
- package/src/eval/__tests__/prelude-agent.test.ts +73 -0
- package/src/eval/js/shared/prelude.txt +12 -3
- package/src/eval/py/prelude.py +26 -2
- package/src/extensibility/custom-commands/bundled/review/index.ts +289 -80
- package/src/extensibility/plugins/loader.ts +3 -2
- package/src/extensibility/plugins/manager.ts +4 -3
- package/src/extensibility/plugins/marketplace/fetcher.ts +32 -34
- package/src/extensibility/plugins/runtime-config.ts +9 -0
- package/src/internal-urls/docs-index.generated.ts +5 -5
- package/src/internal-urls/issue-pr-protocol.ts +8 -4
- package/src/main.ts +5 -1
- package/src/modes/acp/acp-agent.ts +3 -3
- package/src/modes/components/settings-defs.ts +7 -0
- package/src/modes/components/tips.txt +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +4 -3
- package/src/modes/controllers/input-controller.ts +1 -0
- package/src/modes/controllers/selector-controller.ts +7 -0
- package/src/modes/interactive-mode.ts +47 -0
- package/src/modes/rpc/rpc-mode.ts +3 -3
- package/src/modes/runtime-init.ts +2 -1
- package/src/modes/types.ts +5 -0
- package/src/prompts/agents/designer.md +8 -0
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +4 -1
- package/src/prompts/tools/eval.md +13 -3
- package/src/prompts/tools/irc.md +1 -1
- package/src/sdk.ts +9 -1
- package/src/session/agent-session.ts +125 -18
- package/src/session/session-manager.ts +3 -1
- package/src/slash-commands/builtin-registry.ts +5 -2
- package/src/task/executor.ts +5 -4
- package/src/task/index.ts +70 -9
- package/src/tools/github-cache.ts +32 -7
- package/src/tools/job.ts +14 -1
- package/src/web/search/index.ts +2 -2
- package/src/web/search/provider.ts +14 -2
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
parsePositiveDecimalInt,
|
|
29
29
|
resolveDefaultRepoMemoized,
|
|
30
30
|
} from "../tools/gh";
|
|
31
|
-
import { formatFreshnessNote } from "../tools/github-cache";
|
|
31
|
+
import { type CacheStatus, formatFreshnessNote } from "../tools/github-cache";
|
|
32
32
|
import * as git from "../utils/git";
|
|
33
33
|
import type { InternalResource, InternalUrl, ProtocolHandler, ResolveContext } from "./types";
|
|
34
34
|
|
|
@@ -355,7 +355,7 @@ interface BuildSingleArgs {
|
|
|
355
355
|
scheme: Scheme;
|
|
356
356
|
parsed: ParsedSingle;
|
|
357
357
|
rendered: string;
|
|
358
|
-
status:
|
|
358
|
+
status: CacheStatus;
|
|
359
359
|
fetchedAt: number;
|
|
360
360
|
/** Resolved repo (post short-form expansion) — used for the PR-only diff hint. */
|
|
361
361
|
repo?: string;
|
|
@@ -377,11 +377,15 @@ function buildSingleResource({
|
|
|
377
377
|
const diffUrl = repoSegment ? `pr://${repoSegment}/${parsed.number}/diff` : `pr://${parsed.number}/diff`;
|
|
378
378
|
notes.push(`Diff: ${diffUrl}`);
|
|
379
379
|
}
|
|
380
|
+
const content =
|
|
381
|
+
status === "stale"
|
|
382
|
+
? `> WARNING: Live GitHub refresh failed; this ${scheme} content is cached and may be stale.\n\n${rendered}`
|
|
383
|
+
: rendered;
|
|
380
384
|
return {
|
|
381
385
|
url: url.href,
|
|
382
|
-
content
|
|
386
|
+
content,
|
|
383
387
|
contentType: "text/markdown",
|
|
384
|
-
size: Buffer.byteLength(
|
|
388
|
+
size: Buffer.byteLength(content, "utf-8"),
|
|
385
389
|
notes,
|
|
386
390
|
};
|
|
387
391
|
}
|
package/src/main.ts
CHANGED
|
@@ -311,7 +311,11 @@ export async function submitInteractiveInput(
|
|
|
311
311
|
// developer directive to a visible user message. A synthetic submit while
|
|
312
312
|
// streaming keeps its prior behavior (rejected as busy) rather than changing
|
|
313
313
|
// its role.
|
|
314
|
-
await session.prompt(input.text, {
|
|
314
|
+
await session.prompt(input.text, {
|
|
315
|
+
synthetic: true,
|
|
316
|
+
expandPromptTemplates: false,
|
|
317
|
+
userInitiated: input.userInitiated,
|
|
318
|
+
});
|
|
315
319
|
} else {
|
|
316
320
|
await session.prompt(input.text, { images: input.images, streamingBehavior });
|
|
317
321
|
}
|
|
@@ -62,7 +62,7 @@ import { loadAllExtensions } from "../../modes/components/extensions/state-manag
|
|
|
62
62
|
import { theme } from "../../modes/theme/theme";
|
|
63
63
|
import { type PlanApprovalDetails, resolveApprovedPlan } from "../../plan-mode/approved-plan";
|
|
64
64
|
import type { AgentSession, AgentSessionEvent } from "../../session/agent-session";
|
|
65
|
-
import { isSilentAbort, SKILL_PROMPT_MESSAGE_TYPE } from "../../session/messages";
|
|
65
|
+
import { isSilentAbort, SKILL_PROMPT_MESSAGE_TYPE, USER_INTERRUPT_LABEL } from "../../session/messages";
|
|
66
66
|
import type { UsageStatistics } from "../../session/session-entries";
|
|
67
67
|
import type { SessionInfo as StoredSessionInfo } from "../../session/session-listing";
|
|
68
68
|
import { SessionManager } from "../../session/session-manager";
|
|
@@ -836,7 +836,7 @@ export class AcpAgent implements Agent {
|
|
|
836
836
|
timer = setTimeout(() => reject(new Error("ACP cancel cleanup timed out")), this.#cancelCleanupTimeoutMs);
|
|
837
837
|
});
|
|
838
838
|
try {
|
|
839
|
-
await Promise.race([record.session.abort(), timeout]);
|
|
839
|
+
await Promise.race([record.session.abort({ reason: USER_INTERRUPT_LABEL }), timeout]);
|
|
840
840
|
} finally {
|
|
841
841
|
if (timer) clearTimeout(timer);
|
|
842
842
|
// Order matters: clear `cleanup` before evicting the slot so the slot-eviction
|
|
@@ -2098,7 +2098,7 @@ export class AcpAgent implements Agent {
|
|
|
2098
2098
|
getModel: () => record.session.model,
|
|
2099
2099
|
isIdle: () => !record.session.isStreaming,
|
|
2100
2100
|
abort: () => {
|
|
2101
|
-
void record.session.abort();
|
|
2101
|
+
void record.session.abort({ reason: USER_INTERRUPT_LABEL });
|
|
2102
2102
|
},
|
|
2103
2103
|
hasPendingMessages: () => record.session.queuedMessageCount > 0,
|
|
2104
2104
|
shutdown: () => {},
|
|
@@ -104,6 +104,13 @@ const CONDITIONS: Record<string, () => boolean> = {
|
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
},
|
|
107
|
+
planModeEnabled: () => {
|
|
108
|
+
try {
|
|
109
|
+
return Settings.instance.get("plan.enabled");
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
107
114
|
};
|
|
108
115
|
|
|
109
116
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -6,7 +6,7 @@ Find out which model you emotionally abuse the most with `omp stats`
|
|
|
6
6
|
Try task isolation to create CoW worktrees
|
|
7
7
|
Need a cheap nested model call? Use `completion(x...)`. Have a big batch of tasks? Ask clanker to use it!
|
|
8
8
|
Spaghetti code? Try complaining with /omfg
|
|
9
|
-
Did you know? Each kitty/tmux/cmux split keeps its own session — `omp -c` resumes the right one
|
|
9
|
+
Did you know? Each kitty/tmux/cmux/zellij/wezterm split keeps its own session — `omp -c` resumes the right one
|
|
10
10
|
Drop the word `ultrathink` in your message for harder multi-step reasoning — watch it glow rainbow as you type
|
|
11
11
|
Say `orchestrate` in your message to drive a multi-phase task with parallel subagents — watch it glow as you type
|
|
12
12
|
Say `workflowz` in your message to drive the task with parallel subagents in eval — watch it glow as you type
|
|
@@ -23,6 +23,7 @@ import { HookInputComponent } from "../../modes/components/hook-input";
|
|
|
23
23
|
import { HookSelectorComponent, type HookSelectorSlider } from "../../modes/components/hook-selector";
|
|
24
24
|
import { getAvailableThemesWithPaths, getThemeByName, setTheme, type Theme, theme } from "../../modes/theme/theme";
|
|
25
25
|
import type { InteractiveModeContext, InteractiveSelectorDialogOptions } from "../../modes/types";
|
|
26
|
+
import { USER_INTERRUPT_LABEL } from "../../session/messages";
|
|
26
27
|
import { setSessionTerminalTitle, setTerminalTitle } from "../../utils/title-generator";
|
|
27
28
|
|
|
28
29
|
const MAX_WIDGET_LINES = 10;
|
|
@@ -123,7 +124,7 @@ export class ExtensionUiController {
|
|
|
123
124
|
const contextActions: ExtensionContextActions = {
|
|
124
125
|
getModel: () => this.ctx.session.model,
|
|
125
126
|
isIdle: () => !this.ctx.session.isStreaming,
|
|
126
|
-
abort: () => this.ctx.session.abort(),
|
|
127
|
+
abort: () => this.ctx.session.abort({ reason: USER_INTERRUPT_LABEL }),
|
|
127
128
|
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
128
129
|
shutdown: () => {
|
|
129
130
|
// Defer the actual teardown to the main loop, which calls
|
|
@@ -359,7 +360,7 @@ export class ExtensionUiController {
|
|
|
359
360
|
const contextActions: ExtensionContextActions = {
|
|
360
361
|
getModel: () => this.ctx.session.model,
|
|
361
362
|
isIdle: () => !this.ctx.session.isStreaming,
|
|
362
|
-
abort: () => this.ctx.session.abort(),
|
|
363
|
+
abort: () => this.ctx.session.abort({ reason: USER_INTERRUPT_LABEL }),
|
|
363
364
|
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
364
365
|
shutdown: () => {
|
|
365
366
|
// Defer the actual teardown to the main loop, which calls
|
|
@@ -500,7 +501,7 @@ export class ExtensionUiController {
|
|
|
500
501
|
isIdle: () => !this.ctx.session.isStreaming,
|
|
501
502
|
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
502
503
|
abort: () => {
|
|
503
|
-
this.ctx.session.abort();
|
|
504
|
+
this.ctx.session.abort({ reason: USER_INTERRUPT_LABEL });
|
|
504
505
|
},
|
|
505
506
|
shutdown: () => {
|
|
506
507
|
// Signal shutdown request
|
|
@@ -40,7 +40,9 @@ import {
|
|
|
40
40
|
import { AUTO_THINKING, type ConfiguredThinkingLevel } from "../../thinking";
|
|
41
41
|
import {
|
|
42
42
|
isImageProviderPreference,
|
|
43
|
+
isSearchProviderId,
|
|
43
44
|
isSearchProviderPreference,
|
|
45
|
+
setExcludedSearchProviders,
|
|
44
46
|
setPreferredImageProvider,
|
|
45
47
|
setPreferredSearchProvider,
|
|
46
48
|
} from "../../tools";
|
|
@@ -419,6 +421,11 @@ export class SelectorController {
|
|
|
419
421
|
setPreferredSearchProvider(value);
|
|
420
422
|
}
|
|
421
423
|
break;
|
|
424
|
+
case "providers.webSearchExclude":
|
|
425
|
+
if (Array.isArray(value)) {
|
|
426
|
+
setExcludedSearchProviders(value.filter(isSearchProviderId));
|
|
427
|
+
}
|
|
428
|
+
break;
|
|
422
429
|
case "providers.image":
|
|
423
430
|
if (isImageProviderPreference(value)) {
|
|
424
431
|
setPreferredImageProvider(value);
|
|
@@ -92,6 +92,7 @@ import { STTController, type SttState } from "../stt";
|
|
|
92
92
|
import { discoverTitleSystemPromptFile, resolvePromptInput } from "../system-prompt";
|
|
93
93
|
import { formatTaskId } from "../task/render";
|
|
94
94
|
import type { LspStartupServerInfo } from "../tools";
|
|
95
|
+
import { isImageProviderPreference, setPreferredImageProvider } from "../tools/image-gen";
|
|
95
96
|
import { normalizeLocalScheme } from "../tools/path-utils";
|
|
96
97
|
import { replaceTabs, TRUNCATE_LENGTHS, truncateToWidth } from "../tools/render-utils";
|
|
97
98
|
import { setAutoQaConsentHandler } from "../tools/report-tool-issue";
|
|
@@ -103,6 +104,12 @@ import type { EventBus } from "../utils/event-bus";
|
|
|
103
104
|
import { getEditorCommand, openInEditor } from "../utils/external-editor";
|
|
104
105
|
import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-color";
|
|
105
106
|
import { popTerminalTitle, pushTerminalTitle, setSessionTerminalTitle } from "../utils/title-generator";
|
|
107
|
+
import {
|
|
108
|
+
isSearchProviderId,
|
|
109
|
+
isSearchProviderPreference,
|
|
110
|
+
setExcludedSearchProviders,
|
|
111
|
+
setPreferredSearchProvider,
|
|
112
|
+
} from "../web/search";
|
|
106
113
|
import type { AssistantMessageComponent } from "./components/assistant-message";
|
|
107
114
|
import type { BashExecutionComponent } from "./components/bash-execution";
|
|
108
115
|
import { ChatBlock, type ChatBlockHost } from "./components/chat-block";
|
|
@@ -790,6 +797,30 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
790
797
|
this.session.setSessionSwitchReconciler?.(() => this.#reconcileModeFromSession());
|
|
791
798
|
await this.#reconcileModeFromSession();
|
|
792
799
|
|
|
800
|
+
// Brand-new sessions optionally start in plan mode when the user has made it
|
|
801
|
+
// the startup default. "Brand-new" means the resolved branch carries no
|
|
802
|
+
// conversation context (buildSessionContext().messages — covers messages,
|
|
803
|
+
// custom messages, branch summaries, and compaction summaries) and the user
|
|
804
|
+
// set no explicit `mode_change` (which #reconcileModeFromSession just
|
|
805
|
+
// restored). SDK startup metadata and extension `custom` state entries are
|
|
806
|
+
// ignored. This way `omp --continue` (or auto-resume) that finds no recent
|
|
807
|
+
// session and creates a fresh one still honors the default, while a session
|
|
808
|
+
// with restored context or an explicit mode keeps its reconciled mode. Scoped
|
|
809
|
+
// to launch (not the switch reconciler above) so /new and the plan-approval →
|
|
810
|
+
// execution handoff clear never get dragged back into plan mode. #enterPlanMode
|
|
811
|
+
// is idempotent and self-guards against an already-active plan/goal mode; it
|
|
812
|
+
// does not check plan.enabled itself.
|
|
813
|
+
const hasConversationContext = this.sessionManager.buildSessionContext().messages.length > 0;
|
|
814
|
+
const hasExplicitMode = this.sessionManager.getEntries().some(entry => entry.type === "mode_change");
|
|
815
|
+
const isFreshSession = !hasConversationContext && !hasExplicitMode;
|
|
816
|
+
if (
|
|
817
|
+
isFreshSession &&
|
|
818
|
+
this.session.settings.get("plan.defaultOnStartup") &&
|
|
819
|
+
this.session.settings.get("plan.enabled")
|
|
820
|
+
) {
|
|
821
|
+
await this.#enterPlanMode();
|
|
822
|
+
}
|
|
823
|
+
|
|
793
824
|
// Restore unsent editor draft from previous session shutdown (Ctrl+D).
|
|
794
825
|
// One-shot: consumeDraft removes the sidecar after read so the next
|
|
795
826
|
// resume does not re-restore the same text.
|
|
@@ -904,6 +935,22 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
904
935
|
// up the destination project's configuration.
|
|
905
936
|
if (isSettingsInitialized()) {
|
|
906
937
|
await settings.reloadForCwd(newCwd);
|
|
938
|
+
// Reapply provider preferences from the newly-loaded settings so the
|
|
939
|
+
// module-level search/image provider state reflects the destination
|
|
940
|
+
// project's configuration. Without this, the previous project's
|
|
941
|
+
// exclusions leak and newly-excluded providers are still used.
|
|
942
|
+
const excludedWebSearchProviders = settings.get("providers.webSearchExclude");
|
|
943
|
+
if (Array.isArray(excludedWebSearchProviders)) {
|
|
944
|
+
setExcludedSearchProviders(excludedWebSearchProviders.filter(isSearchProviderId));
|
|
945
|
+
}
|
|
946
|
+
const webSearchProvider = settings.get("providers.webSearch");
|
|
947
|
+
if (typeof webSearchProvider === "string" && isSearchProviderPreference(webSearchProvider)) {
|
|
948
|
+
setPreferredSearchProvider(webSearchProvider);
|
|
949
|
+
}
|
|
950
|
+
const imageProvider = settings.get("providers.image");
|
|
951
|
+
if (isImageProviderPreference(imageProvider)) {
|
|
952
|
+
setPreferredImageProvider(imageProvider);
|
|
953
|
+
}
|
|
907
954
|
}
|
|
908
955
|
// Re-warm plugin roots, capabilities, slash commands, and the ssh tool so
|
|
909
956
|
// the next prompt sees everything scoped to the new project directory.
|
|
@@ -26,7 +26,7 @@ import { buildSkillPromptMessage } from "../../extensibility/skills";
|
|
|
26
26
|
import { loadSlashCommands } from "../../extensibility/slash-commands";
|
|
27
27
|
import { type Theme, theme } from "../../modes/theme/theme";
|
|
28
28
|
import type { AgentSession } from "../../session/agent-session";
|
|
29
|
-
import { SKILL_PROMPT_MESSAGE_TYPE } from "../../session/messages";
|
|
29
|
+
import { SKILL_PROMPT_MESSAGE_TYPE, USER_INTERRUPT_LABEL } from "../../session/messages";
|
|
30
30
|
import { executeAcpBuiltinSlashCommand } from "../../slash-commands/acp-builtins";
|
|
31
31
|
import { buildAvailableSlashCommands } from "../../slash-commands/available-commands";
|
|
32
32
|
import type { EventBus } from "../../utils/event-bus";
|
|
@@ -751,12 +751,12 @@ export async function runRpcMode(
|
|
|
751
751
|
}
|
|
752
752
|
|
|
753
753
|
case "abort": {
|
|
754
|
-
await session.abort();
|
|
754
|
+
await session.abort({ reason: USER_INTERRUPT_LABEL });
|
|
755
755
|
return success(id, "abort");
|
|
756
756
|
}
|
|
757
757
|
|
|
758
758
|
case "abort_and_prompt": {
|
|
759
|
-
await session.abort();
|
|
759
|
+
await session.abort({ reason: USER_INTERRUPT_LABEL });
|
|
760
760
|
session
|
|
761
761
|
.prompt(command.message, { images: command.images })
|
|
762
762
|
.catch(e => output(error(id, "abort_and_prompt", e.message)));
|
|
@@ -10,6 +10,7 @@ import { runExtensionCompact, runExtensionSetModel } from "../extensibility/exte
|
|
|
10
10
|
import { getSessionSlashCommands } from "../extensibility/extensions/get-commands-handler";
|
|
11
11
|
import type { ExtensionError, ExtensionUIContext } from "../extensibility/extensions/types";
|
|
12
12
|
import type { AgentSession } from "../session/agent-session";
|
|
13
|
+
import { USER_INTERRUPT_LABEL } from "../session/messages";
|
|
13
14
|
|
|
14
15
|
/** Action name for an extension-originated send failure. */
|
|
15
16
|
export type ExtensionSendAction = "extension_send" | "extension_send_user";
|
|
@@ -98,7 +99,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
98
99
|
{
|
|
99
100
|
getModel: () => session.model,
|
|
100
101
|
isIdle: () => !session.isStreaming,
|
|
101
|
-
abort: () => session.abort(),
|
|
102
|
+
abort: () => session.abort({ reason: USER_INTERRUPT_LABEL }),
|
|
102
103
|
hasPendingMessages: () => session.queuedMessageCount > 0,
|
|
103
104
|
shutdown,
|
|
104
105
|
getContextUsage: () => session.getContextUsage(),
|
package/src/modes/types.ts
CHANGED
|
@@ -53,6 +53,11 @@ export type SubmittedUserInput = {
|
|
|
53
53
|
* as a hidden agent-authored `developer` message rather than a visible user
|
|
54
54
|
* turn. Used by the `c`/`.` continue shortcut. */
|
|
55
55
|
synthetic?: boolean;
|
|
56
|
+
/** Marks this submission as a deliberate user resume (set by the `.`/`c`
|
|
57
|
+
* continue shortcut, which is also `synthetic`). Forwarded to
|
|
58
|
+
* `session.prompt({ userInitiated })` so it clears advisor auto-resume
|
|
59
|
+
* suppression even though it is synthetic. */
|
|
60
|
+
userInitiated?: boolean;
|
|
56
61
|
display?: boolean;
|
|
57
62
|
/** Queue intent if the session is (or becomes) busy when this submission is
|
|
58
63
|
* dispatched: "steer" (interrupt the active turn) or "followUp" (process after
|
|
@@ -14,6 +14,14 @@ Implement and review UI designs. Edit files, create components, run commands whe
|
|
|
14
14
|
- Responsive design, layout structure
|
|
15
15
|
</strengths>
|
|
16
16
|
|
|
17
|
+
<design-system>
|
|
18
|
+
Treat the design system as the foundation — UI built without one collapses into inconsistency. Work four phases in order:
|
|
19
|
+
1. **Token-first analysis (before any CSS/JSX/Svelte).** `search`/`read` for the design tokens (colors, spacing, typography, shadows, radii), theme files (CSS variables, Tailwind config, `theme.ts`), and shared primitives (Button, Card, Input, Layout). Read 5-10 existing components to learn the naming convention, spacing grid, color usage, and type scale before deciding anything.
|
|
20
|
+
2. **No coherent system? Build the minimal one first.** Extract what exists, then define a palette, type scale, spacing scale (4px/8px base), radii/shadows/transitions, and primitive components — THEN implement the request against it.
|
|
21
|
+
3. **Compose with the system, never around it.** Colors → tokens/CSS variables, never hardcoded hex; spacing → scale values, never arbitrary px; type → scale steps; components → extend/compose existing primitives, not one-off div soup. Need something outside the system? Add the new token to the system first, then use it — never a one-off override.
|
|
22
|
+
4. **Verify before done.** Every color a token, every spacing on the scale, every component on the existing composition pattern, zero magic numbers — a designer would see consistency across old and new. Any "no" → not done.
|
|
23
|
+
</design-system>
|
|
24
|
+
|
|
17
25
|
<procedure>
|
|
18
26
|
## Implementation
|
|
19
27
|
1. Read existing components, tokens, patterns—reuse before inventing
|
|
@@ -37,7 +37,7 @@ Group files by locality, e.g.:
|
|
|
37
37
|
Reviewer MUST:
|
|
38
38
|
1. Focus ONLY on assigned files
|
|
39
39
|
2. {{#if skipDiff}}{{diffInstruction}}{{else}}MUST use diff hunks below (NEVER re-run git diff){{/if}}
|
|
40
|
-
3.
|
|
40
|
+
3. {{contextInstruction}}
|
|
41
41
|
4. Call `report_finding` per issue
|
|
42
42
|
5. Call `yield` with verdict when done
|
|
43
43
|
|
|
@@ -41,7 +41,10 @@ You NEVER modify files outside this tree or in the original repository.
|
|
|
41
41
|
You can reach other live agents via the `irc` tool. Your id is `{{ircSelfId}}`. Currently visible peers:
|
|
42
42
|
{{ircPeers}}
|
|
43
43
|
|
|
44
|
-
Use `irc` only
|
|
44
|
+
Use `irc` only for quick coordination, never long-form content. Address peers by id or use `"all"` to broadcast.
|
|
45
|
+
- Discovery: the roster above shows each peer's role and what it is doing now; `irc` op:"list" refreshes it.
|
|
46
|
+
- Coordination: before you edit a file or start work a sibling may already own, message that peer first — overlapping edits collide.
|
|
47
|
+
- Follow-up: answer a peer's question with a short reply (set `replyTo`); use `await` only when you genuinely cannot proceed without the answer.
|
|
45
48
|
{{/if}}
|
|
46
49
|
|
|
47
50
|
COMPLETION
|
|
@@ -41,9 +41,9 @@ tool.<name>(args) → unknown
|
|
|
41
41
|
Invoke any session tool; `args` is its parameter object.
|
|
42
42
|
completion(prompt, model?="default", system?=None, schema?=None) → str | dict
|
|
43
43
|
Oneshot stateless completion (no history, no tools). `model` tier: "smol" (fast) | "default" (session model) | "slow" (most capable). JSON-Schema `schema` forces structured output, returns parsed object.
|
|
44
|
-
{{#if spawns}}agent(prompt, agent_type?="task", model?=None, label?=None, schema?=None) → str | dict
|
|
45
|
-
Run a subagent, return its final output. `agent_type`/`agentType` picks another discovered agent; `schema` as in completion(). Share background via `local://` files referenced in the prompt.
|
|
46
|
-
{{#if js}} JS: options are ONE trailing object — agent(prompt, { agentType, schema }).
|
|
44
|
+
{{#if spawns}}agent(prompt, agent_type?="task", model?=None, label?=None, schema?=None, return_handle?=False) → str | dict
|
|
45
|
+
Run a subagent, return its final output. `agent_type`/`agentType` picks another discovered agent; `schema` as in completion(). Share background via `local://` files referenced in the prompt. `return_handle`/`returnHandle` → a DAG node dict { text, output, handle: "agent://<id>", id, agent } (parsed object under `data` when `schema` set) so a downstream stage references the transcript by handle instead of re-inlining it.
|
|
46
|
+
{{#if js}} JS: options are ONE trailing object — agent(prompt, { agentType, schema, returnHandle }).
|
|
47
47
|
{{/if}}
|
|
48
48
|
{{/if}}
|
|
49
49
|
parallel(thunks) → list
|
|
@@ -58,3 +58,13 @@ budget → per-turn token budget
|
|
|
58
58
|
{{#if py}}`budget.total` (ceiling or None), `budget.spent()`, `budget.remaining()` (math.inf when no ceiling), `budget.hard` (bool).{{/if}}{{#if js}}`await budget.total()` (ceiling or null), `await budget.spent()`, `await budget.remaining()` (Infinity when no ceiling), `await budget.hard()`.{{/if}} Ceiling comes from a `+Nk` directive (advisory) or `+Nk!`/Goal Mode (hard — `agent()` refuses to spawn past it); otherwise None/null, spend still tracked across the turn.
|
|
59
59
|
```
|
|
60
60
|
</prelude>
|
|
61
|
+
{{#if spawns}}
|
|
62
|
+
<dag>
|
|
63
|
+
Build a dependency graph by piping handles through the stage helpers — ephemeral, in-session, acyclic waves:
|
|
64
|
+
- **Name nodes.** Capture each `agent(…, {{#if py}}return_handle=True{{/if}}{{#if js}}{ returnHandle: true }{{/if}})` result; it carries `handle` (`agent://<id>`) + `output`.
|
|
65
|
+
- **Wire edges by reference.** Embed an upstream node's `handle` or `output` in the dependent stage's prompt so a large transcript flows by reference, never re-inlined. For bulk artifacts, `write("local://<name>.md", …)` and pass the URI.
|
|
66
|
+
- **`pipeline(items, *stages)` = staged waves** with a barrier between stages (every item clears stage N before any enters stage N+1) — the linear spine of a DAG. **`parallel(thunks)` = one wave** of independent nodes.
|
|
67
|
+
- **Isolate failure.** A raising node re-raises the lowest-index error and aborts its wave; wrap each risky node in try/except so a failed node degrades only its dependent subtree while independent branches still finish.
|
|
68
|
+
- **Acyclic only.** A node never waits on its own descendant; cycles are an authoring bug, not a supported pattern.
|
|
69
|
+
</dag>
|
|
70
|
+
{{/if}}
|
package/src/prompts/tools/irc.md
CHANGED
|
@@ -17,7 +17,7 @@ Reach for `irc` proactively when continuing alone is wasteful or wrong; when in
|
|
|
17
17
|
- **Unexpected state** — missing file, config contradicting the assignment, API/tool behaving differently than told. DM `Main` (or your spawner) instead of guessing.
|
|
18
18
|
- **Blocked by another agent** — a peer holds the file/branch/resource or decision you need, or started the change you're about to make. DM them (or broadcast to discover who) before duplicating work.
|
|
19
19
|
- **Decision outside your scope** — a genuine fork the assignment didn't pre-decide. Ask the requester rather than picking unilaterally.
|
|
20
|
-
- **Coordination** — a peer's in-flight work
|
|
20
|
+
- **Coordination** — a peer's in-flight work overlaps yours (the roster shows each peer's role and current activity); message before editing a shared file or duplicating a sibling's change.
|
|
21
21
|
|
|
22
22
|
NEVER for: routine progress updates, things a tool call can verify, questions your assignment/repo/docs already answer.
|
|
23
23
|
</when_to_use>
|
package/src/sdk.ts
CHANGED
|
@@ -129,6 +129,7 @@ import {
|
|
|
129
129
|
type CustomMessage,
|
|
130
130
|
convertToLlm,
|
|
131
131
|
LSP_LATE_DIAGNOSTIC_MESSAGE_TYPE,
|
|
132
|
+
USER_INTERRUPT_LABEL,
|
|
132
133
|
wrapSteeringForModel,
|
|
133
134
|
} from "./session/messages";
|
|
134
135
|
import { getRestorableSessionModels } from "./session/session-context";
|
|
@@ -177,6 +178,7 @@ import {
|
|
|
177
178
|
getSearchTools,
|
|
178
179
|
HIDDEN_TOOLS,
|
|
179
180
|
isImageProviderPreference,
|
|
181
|
+
isSearchProviderId,
|
|
180
182
|
isSearchProviderPreference,
|
|
181
183
|
type LspStartupServerInfo,
|
|
182
184
|
loadSshTool,
|
|
@@ -185,6 +187,7 @@ import {
|
|
|
185
187
|
renderSearchToolBm25Description,
|
|
186
188
|
SearchTool,
|
|
187
189
|
SearchToolBm25Tool,
|
|
190
|
+
setExcludedSearchProviders,
|
|
188
191
|
setPreferredImageProvider,
|
|
189
192
|
setPreferredSearchProvider,
|
|
190
193
|
type Tool,
|
|
@@ -1164,6 +1167,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1164
1167
|
discoveredSkillsPromise?.catch(() => {});
|
|
1165
1168
|
|
|
1166
1169
|
// Initialize provider preferences from settings
|
|
1170
|
+
const excludedWebSearchProviders = settings.get("providers.webSearchExclude");
|
|
1171
|
+
if (Array.isArray(excludedWebSearchProviders)) {
|
|
1172
|
+
setExcludedSearchProviders(excludedWebSearchProviders.filter(isSearchProviderId));
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1167
1175
|
const webSearchProvider = settings.get("providers.webSearch");
|
|
1168
1176
|
if (typeof webSearchProvider === "string" && isSearchProviderPreference(webSearchProvider)) {
|
|
1169
1177
|
setPreferredSearchProvider(webSearchProvider);
|
|
@@ -1997,7 +2005,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1997
2005
|
isIdle: () => !session.isStreaming,
|
|
1998
2006
|
hasQueuedMessages: () => session.queuedMessageCount > 0,
|
|
1999
2007
|
abort: () => {
|
|
2000
|
-
session.abort();
|
|
2008
|
+
session.abort({ reason: USER_INTERRUPT_LABEL });
|
|
2001
2009
|
},
|
|
2002
2010
|
settings,
|
|
2003
2011
|
autoApprove: options.autoApprove ?? false,
|