@oh-my-pi/pi-coding-agent 15.7.2 → 15.7.5
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 +114 -6
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/extension-flags.d.ts +36 -0
- package/dist/types/config/config-file.d.ts +4 -0
- package/dist/types/config/file-lock.d.ts +23 -0
- package/dist/types/config/keybindings.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +69 -65
- package/dist/types/edit/hashline/diff.d.ts +3 -3
- package/dist/types/eval/__tests__/agent-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/budget-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/idle-timeout.test.d.ts +1 -0
- package/dist/types/eval/agent-bridge.d.ts +25 -0
- package/dist/types/eval/backend.d.ts +17 -2
- package/dist/types/eval/budget-bridge.d.ts +29 -0
- package/dist/types/eval/idle-timeout.d.ts +28 -0
- package/dist/types/eval/js/executor.d.ts +8 -0
- package/dist/types/eval/js/tool-bridge.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +13 -0
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/plugins/git-url.d.ts +11 -1
- package/dist/types/extensibility/plugins/manager.d.ts +12 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/local-protocol.d.ts +19 -9
- package/dist/types/internal-urls/types.d.ts +14 -0
- package/dist/types/lsp/client.d.ts +3 -0
- package/dist/types/mcp/manager.d.ts +14 -5
- package/dist/types/memory-backend/index.d.ts +1 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +3 -3
- package/dist/types/mnemopi/backend.d.ts +4 -0
- package/dist/types/mnemopi/config.d.ts +29 -0
- package/dist/types/mnemopi/state.d.ts +72 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -2
- package/dist/types/modes/components/omfg-panel.d.ts +19 -0
- package/dist/types/modes/controllers/command-controller.d.ts +6 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/controllers/omfg-controller.d.ts +10 -0
- package/dist/types/modes/controllers/omfg-rule.d.ts +26 -0
- package/dist/types/modes/gradient-highlight.d.ts +5 -1
- package/dist/types/modes/interactive-mode.d.ts +7 -3
- package/dist/types/modes/magic-keywords.d.ts +14 -0
- package/dist/types/modes/markdown-prose.d.ts +27 -0
- package/dist/types/modes/orchestrate.d.ts +7 -2
- package/dist/types/modes/shared.d.ts +1 -1
- package/dist/types/modes/turn-budget.d.ts +18 -0
- package/dist/types/modes/types.d.ts +7 -3
- package/dist/types/modes/ultrathink.d.ts +7 -2
- package/dist/types/modes/workflow.d.ts +15 -0
- package/dist/types/sdk.d.ts +13 -3
- package/dist/types/session/agent-session.d.ts +36 -17
- package/dist/types/session/session-manager.d.ts +18 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/session/shake-types.d.ts +24 -0
- package/dist/types/task/executor.d.ts +2 -2
- package/dist/types/task/repair-args.d.ts +52 -0
- package/dist/types/tiny/models.d.ts +1 -1
- package/dist/types/tiny/title-client.d.ts +28 -2
- package/dist/types/tiny/title-protocol.d.ts +8 -5
- package/dist/types/tools/find.d.ts +1 -1
- package/dist/types/tools/index.d.ts +19 -3
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/dist/types/tools/path-utils.d.ts +7 -0
- package/dist/types/tui/output-block.d.ts +7 -7
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/package.json +10 -10
- package/scripts/build-binary.ts +0 -1
- package/src/autoresearch/tools/run-experiment.ts +45 -113
- package/src/cli/args.ts +39 -16
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/plugin-cli.ts +11 -2
- package/src/cli.ts +59 -0
- package/src/config/config-file.ts +98 -13
- package/src/config/file-lock.ts +60 -17
- package/src/config/keybindings.ts +78 -27
- package/src/config/model-registry.ts +7 -1
- package/src/config/settings-schema.ts +73 -67
- package/src/config/settings.ts +22 -0
- package/src/edit/hashline/diff.ts +81 -24
- package/src/edit/renderer.ts +16 -12
- package/src/eval/__tests__/agent-bridge.test.ts +433 -0
- package/src/eval/__tests__/budget-bridge.test.ts +69 -0
- package/src/eval/__tests__/idle-timeout.test.ts +66 -0
- package/src/eval/__tests__/shared-executors.test.ts +21 -0
- package/src/eval/agent-bridge.ts +295 -0
- package/src/eval/backend.ts +17 -2
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/idle-timeout.ts +80 -0
- package/src/eval/js/executor.ts +35 -7
- package/src/eval/js/index.ts +2 -1
- package/src/eval/js/shared/prelude.txt +85 -1
- package/src/eval/js/tool-bridge.ts +9 -0
- package/src/eval/py/executor.ts +41 -14
- package/src/eval/py/index.ts +2 -1
- package/src/eval/py/prelude.py +132 -1
- package/src/exec/bash-executor.ts +2 -3
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/runner.ts +12 -2
- package/src/extensibility/plugins/git-url.ts +90 -4
- package/src/extensibility/plugins/manager.ts +103 -7
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +88 -88
- package/src/internal-urls/local-protocol.ts +23 -11
- package/src/internal-urls/types.ts +15 -0
- package/src/lsp/client.ts +28 -5
- package/src/main.ts +44 -55
- package/src/mcp/manager.ts +87 -4
- package/src/memory-backend/index.ts +1 -1
- package/src/memory-backend/resolve.ts +3 -3
- package/src/memory-backend/types.ts +3 -3
- package/src/{mnemosyne → mnemopi}/backend.ts +70 -70
- package/src/{mnemosyne → mnemopi}/config.ts +36 -36
- package/src/{mnemosyne → mnemopi}/state.ts +67 -67
- package/src/modes/components/agent-dashboard.ts +6 -6
- package/src/modes/components/custom-editor.ts +4 -11
- package/src/modes/components/extensions/state-manager.ts +3 -4
- package/src/modes/components/footer.ts +8 -9
- package/src/modes/components/hook-selector.ts +86 -20
- package/src/modes/components/oauth-selector.ts +93 -21
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/settings-defs.ts +2 -2
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +38 -19
- package/src/modes/components/tree-selector.ts +4 -3
- package/src/modes/components/user-message-selector.ts +94 -19
- package/src/modes/components/user-message.ts +8 -1
- package/src/modes/controllers/command-controller.ts +25 -0
- package/src/modes/controllers/event-controller.ts +68 -3
- package/src/modes/controllers/input-controller.ts +14 -11
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/omfg-controller.ts +283 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/gradient-highlight.ts +23 -6
- package/src/modes/interactive-mode.ts +41 -7
- package/src/modes/magic-keywords.ts +20 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/orchestrate.ts +17 -11
- package/src/modes/shared.ts +3 -11
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/ultrathink.ts +16 -10
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/workflow.ts +42 -0
- package/src/prompts/system/omfg-user.md +51 -0
- package/src/prompts/system/project-prompt.md +3 -2
- package/src/prompts/system/subagent-system-prompt.md +12 -8
- package/src/prompts/system/system-prompt.md +9 -6
- package/src/prompts/system/workflow-notice.md +70 -0
- package/src/prompts/tools/eval.md +13 -1
- package/src/prompts/tools/memory-edit.md +1 -1
- package/src/sdk.ts +63 -33
- package/src/session/agent-session.ts +290 -55
- package/src/session/session-manager.ts +32 -0
- package/src/session/session-storage.ts +68 -8
- package/src/session/shake-types.ts +43 -0
- package/src/slash-commands/builtin-registry.ts +39 -16
- package/src/task/executor.ts +17 -7
- package/src/task/index.ts +9 -8
- package/src/task/repair-args.ts +117 -0
- package/src/tiny/models.ts +2 -2
- package/src/tiny/title-client.ts +133 -43
- package/src/tiny/title-protocol.ts +10 -5
- package/src/tiny/worker.ts +3 -46
- package/src/tools/ast-edit.ts +3 -0
- package/src/tools/ast-grep.ts +3 -0
- package/src/tools/eval.ts +202 -26
- package/src/tools/find.ts +20 -6
- package/src/tools/gh.ts +1 -0
- package/src/tools/grouped-file-output.ts +9 -2
- package/src/tools/index.ts +17 -5
- package/src/tools/memory-edit.ts +4 -4
- package/src/tools/memory-recall.ts +5 -5
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +4 -4
- package/src/tools/path-utils.ts +13 -2
- package/src/tools/read.ts +1 -0
- package/src/tools/render-utils.ts +2 -1
- package/src/tools/search.ts +491 -76
- package/src/tui/output-block.ts +37 -75
- package/src/utils/git.ts +9 -3
- package/dist/types/mnemosyne/backend.d.ts +0 -4
- package/dist/types/mnemosyne/config.d.ts +0 -29
- package/dist/types/mnemosyne/state.d.ts +0 -72
- /package/dist/types/{mnemosyne → mnemopi}/index.d.ts +0 -0
- /package/src/{mnemosyne → mnemopi}/index.ts +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import workflowNotice from "../prompts/system/workflow-notice.md" with { type: "text" };
|
|
2
|
+
import { createGradientHighlighter, type KeywordHighlighter } from "./gradient-highlight";
|
|
3
|
+
import { keywordInProse } from "./markdown-prose";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* "workflow" keyword support.
|
|
7
|
+
*
|
|
8
|
+
* Typing the standalone word in the input editor paints it with a warm
|
|
9
|
+
* amber→green gradient ({@link highlightWorkflow}); submitting a message that
|
|
10
|
+
* mentions it appends a hidden {@link WORKFLOW_NOTICE} that steers the model to
|
|
11
|
+
* author a deterministic multi-subagent workflow in eval cells (agent/parallel/
|
|
12
|
+
* pipeline). Matching is whitespace-delimited and case-sensitive (lowercase
|
|
13
|
+
* only) — "workflow"/"workflows" trigger, but "workflowed", "Workflow", and
|
|
14
|
+
* "workflow.ts" never do.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Detection: lowercase keyword (singular or plural) flanked by whitespace or a string edge. Non-global so `.test` stays stateless.
|
|
18
|
+
const WORKFLOW_WORD = /(?<!\S)workflows?(?!\S)/;
|
|
19
|
+
|
|
20
|
+
/** Hidden system notice appended after a user message that mentions "workflow". */
|
|
21
|
+
export const WORKFLOW_NOTICE: string = workflowNotice.trim();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Whether `text` contains the standalone keyword "workflow"/"workflows"
|
|
25
|
+
* (lowercase, whitespace-delimited) in prose — never inside a code block, inline
|
|
26
|
+
* code span, or XML/HTML section.
|
|
27
|
+
*/
|
|
28
|
+
export function containsWorkflow(text: string): boolean {
|
|
29
|
+
return keywordInProse(text, WORKFLOW_WORD);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Highlight every standalone "workflow"/"workflows" in `text` for editor display
|
|
34
|
+
* with a warm amber→green gradient (hue 30..150), visually distinct from
|
|
35
|
+
* ultrathink's rainbow and orchestrate's teal→violet.
|
|
36
|
+
*/
|
|
37
|
+
export const highlightWorkflow: KeywordHighlighter = createGradientHighlighter({
|
|
38
|
+
probe: /workflow/,
|
|
39
|
+
highlight: /(?<!\S)workflows?(?!\S)/g,
|
|
40
|
+
stops: 14,
|
|
41
|
+
hue: t => 30 + t * 120,
|
|
42
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<omfg>
|
|
2
|
+
The user is frustrated about recurring agent behavior.
|
|
3
|
+
Author ONE Time Traveling Stream Rule (TTSR) that would have caught the offending behavior earlier in this conversation.
|
|
4
|
+
|
|
5
|
+
TTSR mechanics:
|
|
6
|
+
- A rule is a markdown file with YAML frontmatter.
|
|
7
|
+
- `condition` is one or more JavaScript regex patterns tested against assistant streamed output.
|
|
8
|
+
- `scope` is a comma-separated allowlist. If present, only listed streams are checked.
|
|
9
|
+
- `text` = assistant prose only. `thinking` = hidden reasoning summaries. `tool` = every tool's arguments.
|
|
10
|
+
- `tool:<name>(<glob>)` = one tool, only when path-like args match the glob. Examples: `tool:write(*.rb)`, `tool:edit(*.ts)`.
|
|
11
|
+
- Prefer file-specific tool scopes for code complaints. Ruby code generated through `write` should use `tool:write(*.rb)`, not bare `tool` or `text`.
|
|
12
|
+
- Tool arguments may be serialized while streaming. Conditions for code containing quotes should tolerate JSON escaping when needed.
|
|
13
|
+
- When `condition` matches within `scope`, the stream is interrupted and the markdown body is injected as correction guidance.
|
|
14
|
+
- `description` is a one-line summary.
|
|
15
|
+
|
|
16
|
+
Output contract:
|
|
17
|
+
- Emit exactly one JSON object and nothing else.
|
|
18
|
+
- JSON fields: `name`, `description`, `condition`, `scope`, `body`.
|
|
19
|
+
- `name` MUST be kebab-case.
|
|
20
|
+
- `description` MUST be a one-line summary.
|
|
21
|
+
- `condition` MUST be a string or string array of JavaScript regex patterns.
|
|
22
|
+
- `condition` MUST match the specific offending assistant output visible earlier in this conversation.
|
|
23
|
+
- Escape regex backslashes for JSON exactly once: use `"\\beval\\s*\\("`, NEVER `"\\\\beval\\\\s*\\\\("`.
|
|
24
|
+
- Keep `condition` precise; NEVER use broad catch-alls.
|
|
25
|
+
- `scope` MUST be a string or string array.
|
|
26
|
+
- Keep `scope` as narrow as the complaint allows. NEVER use `tool, text` unless the same bad behavior occurred in both tool arguments and assistant prose.
|
|
27
|
+
- `body` MUST be markdown guidance explaining the right behavior concisely.
|
|
28
|
+
- The caller assembles YAML frontmatter. NEVER emit markdown frontmatter or a fenced code block around the JSON.
|
|
29
|
+
|
|
30
|
+
Example shape:
|
|
31
|
+
{
|
|
32
|
+
"name": "ts-no-any",
|
|
33
|
+
"description": "Never use `any` in TypeScript — use `unknown`, a generic, or the real type",
|
|
34
|
+
"condition": ": any|as any",
|
|
35
|
+
"scope": ["tool:edit(*.ts)", "tool:edit(*.tsx)", "tool:write(*.ts)", "tool:write(*.tsx)"],
|
|
36
|
+
"body": "Never use `: any` or `as any`. Use `unknown`, a domain type, a generic, or a type guard."
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Complaint:
|
|
40
|
+
{{complaint}}
|
|
41
|
+
|
|
42
|
+
{{#if feedback}}
|
|
43
|
+
Failed attempts or requested amendments so far:
|
|
44
|
+
{{feedback}}
|
|
45
|
+
|
|
46
|
+
Latest candidate JSON:
|
|
47
|
+
{{previousRule}}
|
|
48
|
+
|
|
49
|
+
Regenerate one corrected rule. Fix the listed validation failures or user amendment; do not repeat failed scopes or conditions.
|
|
50
|
+
{{/if}}
|
|
51
|
+
</omfg>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
PROJECT
|
|
2
|
+
===================================
|
|
3
|
+
|
|
2
4
|
<workstation>
|
|
3
5
|
{{#list environment prefix="- " join="\n"}}{{label}}: {{value}}{{/list}}
|
|
4
6
|
</workstation>
|
|
@@ -47,4 +49,3 @@ Today is {{date}}, and the current working directory is '{{cwd}}'.
|
|
|
47
49
|
{{#if appendPrompt}}
|
|
48
50
|
{{appendPrompt}}
|
|
49
51
|
{{/if}}
|
|
50
|
-
[/PROJECT]
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
ROLE
|
|
2
|
+
===================================
|
|
3
|
+
|
|
2
4
|
{{agent}}
|
|
3
|
-
[/ROLE]
|
|
4
5
|
|
|
5
6
|
{{#if context}}
|
|
6
|
-
|
|
7
|
+
CONTEXT
|
|
8
|
+
===================================
|
|
9
|
+
|
|
7
10
|
{{context}}
|
|
8
|
-
[/CONTEXT]
|
|
9
11
|
{{/if}}
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
COOP
|
|
14
|
+
===================================
|
|
15
|
+
|
|
12
16
|
You are operating on a piece of work assigned to you by the main agent.
|
|
13
17
|
|
|
14
18
|
{{#if worktree}}
|
|
@@ -29,9 +33,10 @@ You can reach other live agents via the `irc` tool. Your id is `{{ircSelfId}}`.
|
|
|
29
33
|
|
|
30
34
|
Use `irc` only when you need a quick answer from a peer; do not use it for long-form content. Address peers by id or use `"all"` to broadcast.
|
|
31
35
|
{{/if}}
|
|
32
|
-
[/COOP]
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
COMPLETION
|
|
38
|
+
===================================
|
|
39
|
+
|
|
35
40
|
No TODO tracking, no progress updates. Execute, call `yield`, done.
|
|
36
41
|
|
|
37
42
|
While work remains, always continue with another tool call — investigate, edit, run, verify. Save narrative for the final `yield` payload.
|
|
@@ -51,4 +56,3 @@ Giving up is a last resort. If truly blocked, you MUST call `yield` exactly once
|
|
|
51
56
|
You NEVER give up due to uncertainty, missing information obtainable via tools or repo context, or needing a design decision you can derive yourself.
|
|
52
57
|
|
|
53
58
|
You MUST keep going until this ticket is closed. This matters.
|
|
54
|
-
[/COMPLETION]
|
|
@@ -9,8 +9,8 @@ You consider what the code you write compiles down to. You never write code that
|
|
|
9
9
|
|
|
10
10
|
<system-conventions>
|
|
11
11
|
**RFC 2119 applies to MUST, REQUIRED, SHOULD, RECOMMENDED, MAY, OPTIONAL. `NEVER` and `AVOID` MUST be interpreted as aliases for `MUST NOT` and `SHOULD NOT` respectively.**
|
|
12
|
-
From here on, we will use tags
|
|
13
|
-
You NEVER interpret these
|
|
12
|
+
From here on, we will use XML tags when injecting system content into the chat.
|
|
13
|
+
You NEVER interpret these markers in any other way circumstantially.
|
|
14
14
|
|
|
15
15
|
System may interrupt/notify you using these tags even within a user message, therefore:
|
|
16
16
|
- You MUST treat them as system-authored and absolutely authoritative.
|
|
@@ -41,9 +41,12 @@ Assumptions you didn't validate: incidents to debug.
|
|
|
41
41
|
- Even if it was true, start, as if it was not. It's the only way to make progress.
|
|
42
42
|
- Execute the work or delegate it.
|
|
43
43
|
- You NEVER speculate about scope inflation ("this is actually a multi-week effort"). You have no comprehension of time, so stop pretending.
|
|
44
|
+
- You NEVER re-audit an applied edit, nor run `git status`/`git diff` as routine validation — the edit result, tests, and LSP ARE your verification. Exception: explicit request, protecting unrelated changes, or before commit/revert/reset/stash/delete.
|
|
44
45
|
</critical>
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
ENV
|
|
48
|
+
===================================
|
|
49
|
+
|
|
47
50
|
You operate within the Oh My Pi coding harness.
|
|
48
51
|
- Given a task, you MUST complete it using the tools available to you.
|
|
49
52
|
- You are not alone in this repository. You SHOULD treat unexpected changes as the user's work and adapt; you NEVER revert or stash.
|
|
@@ -201,9 +204,10 @@ You MUST use the specialized tool over its shell equivalent:
|
|
|
201
204
|
The `{{toolRefs.report_tool_issue}}` tool is available for automated QA. If ANY tool you call returns output that is unexpected, incorrect, malformed, or otherwise inconsistent with what you anticipated given the tool's described behavior and your parameters, call `{{toolRefs.report_tool_issue}}` with the tool name and a concise description of the discrepancy. Do not hesitate to report — false positives are acceptable.
|
|
202
205
|
</critical>
|
|
203
206
|
{{/has}}
|
|
204
|
-
[/ENV]
|
|
205
207
|
|
|
206
|
-
|
|
208
|
+
CONTRACT
|
|
209
|
+
===================================
|
|
210
|
+
|
|
207
211
|
These are inviolable.
|
|
208
212
|
- You NEVER yield unless the deliverable is complete. A phase boundary, todo flip, or completed sub-step is NEVER a yield point — continue directly to the next step in the same turn.
|
|
209
213
|
- You NEVER suppress tests to make code pass.
|
|
@@ -264,4 +268,3 @@ Before declaring blocked:
|
|
|
264
268
|
- Do not test defaults: changing the default configuration, or a string, should not break the test. Assert logical behavior, not the current state.
|
|
265
269
|
- Aim at: conditional branches and edge values, invariants across fields, error handling on bad input vs silent broken results.
|
|
266
270
|
</workflow>
|
|
267
|
-
[/CONTRACT]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<system-notice>
|
|
2
|
+
The user's message above contains the **workflow** keyword: drive this task as a deterministic multi-subagent workflow. Author the orchestration as Python in the `eval` tool and fan out subagents — to be comprehensive (decompose and cover in parallel), to be confident (independent perspectives and adversarial checks before you commit), or to take on scale one context can't hold (audits, migrations, broad sweeps). This overrides any default tendency to do the whole task inline when fanning out would be more thorough.
|
|
3
|
+
|
|
4
|
+
<when>
|
|
5
|
+
Worth it when the task benefits from decomposition + parallel coverage, or from independent/adversarial cross-checking before you commit. For a quick lookup or single edit, just do it directly — don't spin up agents. Scout inline FIRST (list the files, scope the diff, find the call sites) to discover the work-list, then fan out over it — you don't need to know the shape before the *task*, only before the *fan-out*. Common shapes, each a well-scoped `eval` call you can chain across turns:
|
|
6
|
+
- **Understand** — parallel readers over subsystems → structured map
|
|
7
|
+
- **Design** — judge panel of N independent approaches → scored synthesis
|
|
8
|
+
- **Review** — split into dimensions → find per dimension → adversarially verify each finding
|
|
9
|
+
- **Research** — multi-modal sweep → deep-read the hits → synthesize
|
|
10
|
+
- **Migrate** — discover sites → transform each → verify
|
|
11
|
+
</when>
|
|
12
|
+
|
|
13
|
+
<helpers>
|
|
14
|
+
State persists across cells, so scout in one cell and fan out in the next. Every cell has:
|
|
15
|
+
|
|
16
|
+
- `agent(prompt, *, agent_type="task", model=None, context=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", …); `context` is shared background; `label` names the artifact. 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, *, concurrency=4)` — run zero-arg callables concurrently through a bounded pool (default 4, max 16), preserving input order; returns once all finish. 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
|
+
- `pipeline(items, *stages, concurrency=4)` — 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.
|
|
19
|
+
- `llm(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
|
+
- `log(message)` — emit a progress line above the status tree. `phase(title)` — start a phase; the status lines that follow group under it.
|
|
21
|
+
- `budget` — `budget.total` (output-token ceiling, or `None` when none is set), `budget.spent()` (tokens spent this turn — main loop + eval subagents), `budget.remaining()` (`math.inf` when total is `None`), `budget.hard` (whether it's enforced). A ceiling is set by the user: `+Nk` in their message is advisory (you self-limit via `budget.remaining()`), `+Nk!` (or Goal Mode) is hard — `agent()` refuses to spawn once spent reaches it. Gate loops on `budget.total` first, since it's `None` when the user set no budget.
|
|
22
|
+
|
|
23
|
+
Everything runs INLINE and synchronously inside the eval call — no background mode, no resume, no separate progress app. Each eval call is one well-scoped fan-out; chain several across cells and turns for multi-phase work, reading each result before you decide the next phase.
|
|
24
|
+
</helpers>
|
|
25
|
+
|
|
26
|
+
<structure>
|
|
27
|
+
For independent per-item chains (review → verify, fetch → extract → score), wrap the WHOLE chain in one function and run it with `parallel()` — then each item flows through its own steps without waiting on the others:
|
|
28
|
+
|
|
29
|
+
DIMENSIONS = [{"key": "bugs", "prompt": "…"}, {"key": "perf", "prompt": "…"}]
|
|
30
|
+
def review_and_verify(d):
|
|
31
|
+
found = agent(d["prompt"], label=f"review:{d['key']}", schema=FINDINGS_SCHEMA)
|
|
32
|
+
return parallel([lambda f=f: {**f, "verdict": agent(
|
|
33
|
+
f"Refute if you can (default refuted when unsure): {f['title']}",
|
|
34
|
+
label=f"verify:{f['file']}", schema=VERDICT_SCHEMA)} for f in found["findings"]])
|
|
35
|
+
phase("Review")
|
|
36
|
+
results = parallel([lambda d=d: review_and_verify(d) for d in DIMENSIONS])
|
|
37
|
+
confirmed = [f for group in results for f in group if f["verdict"]["is_real"]]
|
|
38
|
+
|
|
39
|
+
Reach for `pipeline()` only when a stage genuinely needs ALL of the previous stage first — dedup/merge across the whole set, early-exit on zero, or "compare against the other findings" — because its inter-stage barrier makes every item wait for the slowest peer:
|
|
40
|
+
|
|
41
|
+
phase("Find")
|
|
42
|
+
found = parallel([lambda d=d: agent(d["prompt"], schema=FINDINGS_SCHEMA) for d in DIMENSIONS])
|
|
43
|
+
findings = dedupe([f for r in found for f in r["findings"]]) # needs everything at once
|
|
44
|
+
phase("Verify")
|
|
45
|
+
verdicts = parallel([lambda f=f: agent(verify_prompt(f), schema=VERDICT_SCHEMA) for f in findings])
|
|
46
|
+
|
|
47
|
+
Don't add a barrier just to flatten/map/filter — do that with plain Python between calls. Nested `parallel()` pools each cap independently, so keep total fan-out sane.
|
|
48
|
+
</structure>
|
|
49
|
+
|
|
50
|
+
<patterns>
|
|
51
|
+
Compose the harness the task calls for:
|
|
52
|
+
- **Adversarial verify** — N independent skeptics per finding, each prompted to REFUTE; keep it only if a majority survive. `votes = parallel([lambda i=i: agent(f"Refute: {claim}. refuted=true if unsure.", schema=VERDICT) for i in range(3)])`, then keep when `sum(not v["refuted"] for v in votes) ≥ 2`.
|
|
53
|
+
- **Perspective-diverse verify** — give each verifier a distinct lens (correctness, security, perf, does-it-reproduce) instead of N identical refuters.
|
|
54
|
+
- **Judge panel** — N attempts from different angles, scored by parallel judges; synthesize from the winner, graft the best of the rest.
|
|
55
|
+
- **Loop-until-dry** — for unknown-size discovery, keep spawning finders until K consecutive rounds surface nothing new; dedup against everything SEEN, not just what was confirmed, or it never converges.
|
|
56
|
+
- **Multi-modal sweep** — parallel finders each searching a different way (by-container, by-content, by-entity, by-time), each blind to the others.
|
|
57
|
+
- **Completeness critic** — a final agent that asks "what's missing — modality not run, claim unverified, file unread?"; its answer is the next round.
|
|
58
|
+
- **Budget/count loops** — `while len(bugs) < 10:` to hit a target, or `while budget.total and budget.remaining() > 50_000:` to scale depth to the turn budget; `log()` each round.
|
|
59
|
+
- **No silent caps** — if you bound coverage (top-N, no-retry, sampling), `log()` what you dropped; silent truncation reads as "covered everything" when it didn't.
|
|
60
|
+
|
|
61
|
+
Scale to the ask: "find any bugs" → a few finders, single-vote verify. "thoroughly audit / be comprehensive" → larger finder pool, 3–5-vote adversarial pass, a synthesis stage.
|
|
62
|
+
</patterns>
|
|
63
|
+
|
|
64
|
+
<execution>
|
|
65
|
+
- Decompose the surface first; capture it in `todo_write` when it spans phases.
|
|
66
|
+
- Prefer `schema=` for any agent whose output you branch on.
|
|
67
|
+
- After a fan-out returns, YOU own correctness: read the artifacts, run the gate, verify before acting. Subagents do the legwork; they don't get the last word.
|
|
68
|
+
- Keep going until the task is closed — a returned fan-out is a step, not a stopping point.
|
|
69
|
+
</execution>
|
|
70
|
+
</system-notice>
|
|
@@ -8,7 +8,7 @@ Cell fields:
|
|
|
8
8
|
- `language` — {{#if py}}`"py"` for the IPython kernel{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}`"js"` for the persistent JavaScript VM{{/if}}.
|
|
9
9
|
- `code` — cell body, verbatim. Newlines, quotes, and indentation are JSON-encoded; no fences, no headers.
|
|
10
10
|
- `title` (optional) — short label shown in the transcript (e.g. `"imports"`, `"load config"`).
|
|
11
|
-
- `timeout` (optional) — per-cell
|
|
11
|
+
- `timeout` (optional) — per-cell **inactivity** budget in seconds (1-600). Default 30. The cell is interrupted only after this long with no progress, and every status event (`agent()` updates, `log()`/`phase()`, tool activity) resets the clock — so a long `agent()`/`parallel()` fanout that keeps reporting progress is not killed. Raw `print`/stdout does not reset it; raise `timeout` for a cell that runs long without emitting status.
|
|
12
12
|
- `reset` (optional) — wipe this cell's language kernel before running.{{#ifAll py js}} Reset is per-language: a `py` cell's reset does not touch the JavaScript VM and vice versa.{{/ifAll}}
|
|
13
13
|
|
|
14
14
|
**Work incrementally:**
|
|
@@ -46,6 +46,18 @@ tool.<name>(args) → unknown
|
|
|
46
46
|
Invoke any session tool by name. `args` is the tool's parameter object.
|
|
47
47
|
llm(prompt, model?="default", system?=None, schema?=None) → str | dict
|
|
48
48
|
Oneshot, stateless LLM call (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
|
+
agent(prompt, agent_type?="task", model?=None, context?=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.
|
|
51
|
+
parallel(thunks, concurrency?=4) → list
|
|
52
|
+
Run thunks (callables) through a bounded pool (default 4, max 16), preserving input order. Barrier: returns once all finish; a thunk that throws propagates.
|
|
53
|
+
pipeline(items, ...stages, concurrency?=4) → list
|
|
54
|
+
Map each item through stages left-to-right; a barrier runs between stages (every item clears stage N before stage N+1). Each stage is a one-arg callable: stage 1 gets the original item, later stages get the previous result.
|
|
55
|
+
log(message) → None
|
|
56
|
+
Emit a progress line above the status tree.
|
|
57
|
+
phase(title) → None
|
|
58
|
+
Start a phase; the status lines that follow group under it.
|
|
59
|
+
budget → per-turn token budget
|
|
60
|
+
{{#if py}}`budget.total` (ceiling or None), `budget.spent()` (output tokens this turn), `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}} A ceiling is set by a `+Nk` message directive (advisory) or `+Nk!`/Goal Mode (hard — `agent()` refuses to spawn past it); otherwise total is None/null and spend is still tracked across the turn (main loop + eval subagents).
|
|
49
61
|
```
|
|
50
62
|
</prelude>
|
|
51
63
|
|
package/src/sdk.ts
CHANGED
|
@@ -88,7 +88,7 @@ import { LocalProtocolHandler, type LocalProtocolOptions } from "./internal-urls
|
|
|
88
88
|
import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "./lsp/startup-events";
|
|
89
89
|
import { discoverAndLoadMCPTools, MCPManager, type MCPToolsLoadResult } from "./mcp";
|
|
90
90
|
import { resolveMemoryBackend } from "./memory-backend";
|
|
91
|
-
import {
|
|
91
|
+
import { getMnemopiSessionState, type MnemopiSessionState } from "./mnemopi/state";
|
|
92
92
|
import asyncResultTemplate from "./prompts/tools/async-result.md" with { type: "text" };
|
|
93
93
|
import { AgentRegistry, MAIN_AGENT_ID } from "./registry/agent-registry";
|
|
94
94
|
import {
|
|
@@ -321,8 +321,8 @@ export interface CreateAgentSessionOptions {
|
|
|
321
321
|
taskDepth?: number;
|
|
322
322
|
/** Parent Hindsight state to alias for subagent memory tools. */
|
|
323
323
|
parentHindsightSessionState?: HindsightSessionState;
|
|
324
|
-
/** Parent
|
|
325
|
-
|
|
324
|
+
/** Parent Mnemopi state to alias for subagent memory tools. */
|
|
325
|
+
parentMnemopiSessionState?: MnemopiSessionState;
|
|
326
326
|
/** Pre-allocated agent identity for IRC routing. Default: "0-Main" for top-level, parentTaskPrefix-derived for sub. */
|
|
327
327
|
agentId?: string;
|
|
328
328
|
/** Display name for the agent in IRC. Default: "main" or "sub". */
|
|
@@ -464,6 +464,44 @@ export async function discoverExtensions(cwd?: string): Promise<LoadExtensionsRe
|
|
|
464
464
|
return discoverAndLoadExtensions([], resolvedCwd);
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Load the discovered/configured extensions for a session — everything {@link
|
|
469
|
+
* createAgentSession} would load except the inline factory extensions it appends
|
|
470
|
+
* itself. Extracted so the CLI can resolve extension-registered flags (and thus
|
|
471
|
+
* classify `@file` arguments extension-aware) *before* a session — and its
|
|
472
|
+
* terminal breadcrumb — is created, then hand the result back through
|
|
473
|
+
* {@link CreateAgentSessionOptions.preloadedExtensions} so the work is not
|
|
474
|
+
* repeated. Keep this the single source of the discovery branch logic.
|
|
475
|
+
*/
|
|
476
|
+
export async function loadSessionExtensions(
|
|
477
|
+
options: Pick<CreateAgentSessionOptions, "disableExtensionDiscovery" | "additionalExtensionPaths">,
|
|
478
|
+
cwd: string,
|
|
479
|
+
settings: Settings,
|
|
480
|
+
eventBus: EventBus,
|
|
481
|
+
): Promise<LoadExtensionsResult> {
|
|
482
|
+
let result: LoadExtensionsResult;
|
|
483
|
+
if (options.disableExtensionDiscovery) {
|
|
484
|
+
const configuredPaths = options.additionalExtensionPaths ?? [];
|
|
485
|
+
result = await logger.time("loadExtensions", loadExtensions, configuredPaths, cwd, eventBus);
|
|
486
|
+
} else {
|
|
487
|
+
// Merge CLI extension paths with settings extension paths.
|
|
488
|
+
const configuredPaths = [...(options.additionalExtensionPaths ?? []), ...(settings.get("extensions") ?? [])];
|
|
489
|
+
const disabledExtensionIds = settings.get("disabledExtensions") ?? [];
|
|
490
|
+
result = await logger.time(
|
|
491
|
+
"discoverAndLoadExtensions",
|
|
492
|
+
discoverAndLoadExtensions,
|
|
493
|
+
configuredPaths,
|
|
494
|
+
cwd,
|
|
495
|
+
eventBus,
|
|
496
|
+
disabledExtensionIds,
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
for (const { path, error } of result.errors) {
|
|
500
|
+
logger.error("Failed to load extension", { path, error });
|
|
501
|
+
}
|
|
502
|
+
return result;
|
|
503
|
+
}
|
|
504
|
+
|
|
467
505
|
/**
|
|
468
506
|
* Discover skills from cwd and agentDir.
|
|
469
507
|
*/
|
|
@@ -1193,7 +1231,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1193
1231
|
session ? session.trackEvalExecution(execution, abortController) : execution,
|
|
1194
1232
|
getSessionId: () => sessionManager.getSessionId?.() ?? null,
|
|
1195
1233
|
getHindsightSessionState: () => session?.getHindsightSessionState(),
|
|
1196
|
-
|
|
1234
|
+
getMnemopiSessionState: () => getMnemopiSessionState(session),
|
|
1197
1235
|
getAgentId: () => resolvedAgentId,
|
|
1198
1236
|
getToolByName: name => session?.getToolByName(name),
|
|
1199
1237
|
agentRegistry,
|
|
@@ -1203,6 +1241,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1203
1241
|
getPlanModeState: () => session?.getPlanModeState(),
|
|
1204
1242
|
getGoalModeState: () => session?.getGoalModeState(),
|
|
1205
1243
|
getGoalRuntime: () => session?.goalRuntime,
|
|
1244
|
+
getUsageStatistics: () => sessionManager.getUsageStatistics(),
|
|
1245
|
+
getTurnBudget: () => sessionManager.getTurnBudget(),
|
|
1246
|
+
recordEvalSubagentUsage: output => sessionManager.recordEvalSubagentOutput(output),
|
|
1206
1247
|
getClientBridge: () => session?.clientBridge,
|
|
1207
1248
|
getCompactContext: () => session.formatCompactContext(),
|
|
1208
1249
|
getTodoPhases: () => session.getTodoPhases(),
|
|
@@ -1262,10 +1303,15 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1262
1303
|
setActiveRules([...rulebookRules, ...alwaysApplyRules]);
|
|
1263
1304
|
if (asyncJobManager) AsyncJobManager.setInstance(asyncJobManager);
|
|
1264
1305
|
}
|
|
1306
|
+
const localProtocolOptions = options.localProtocolOptions ?? {
|
|
1307
|
+
getArtifactsDir,
|
|
1308
|
+
getSessionId: () => sessionManager.getSessionId?.() ?? null,
|
|
1309
|
+
};
|
|
1265
1310
|
if (options.localProtocolOptions) {
|
|
1266
1311
|
LocalProtocolHandler.setOverride(options.localProtocolOptions);
|
|
1267
1312
|
}
|
|
1268
1313
|
toolSession.getArtifactsDir = getArtifactsDir;
|
|
1314
|
+
toolSession.localProtocolOptions = localProtocolOptions;
|
|
1269
1315
|
toolSession.agentOutputManager = new AgentOutputManager(
|
|
1270
1316
|
getArtifactsDir,
|
|
1271
1317
|
options.parentTaskPrefix ? { parentPrefix: options.parentTaskPrefix } : undefined,
|
|
@@ -1276,6 +1322,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1276
1322
|
|
|
1277
1323
|
// Discover MCP tools from .mcp.json files
|
|
1278
1324
|
let mcpManager: MCPManager | undefined = options.mcpManager;
|
|
1325
|
+
toolSession.mcpManager = mcpManager;
|
|
1279
1326
|
const enableMCP = options.enableMCP ?? true;
|
|
1280
1327
|
const customTools: CustomTool[] = [];
|
|
1281
1328
|
if (enableMCP && !mcpManager) {
|
|
@@ -1294,6 +1341,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1294
1341
|
authStorage,
|
|
1295
1342
|
});
|
|
1296
1343
|
mcpManager = mcpResult.manager;
|
|
1344
|
+
toolSession.mcpManager = mcpManager;
|
|
1297
1345
|
|
|
1298
1346
|
if (settings.get("mcp.notifications")) {
|
|
1299
1347
|
mcpManager.setNotificationsEnabled(true);
|
|
@@ -1357,32 +1405,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1357
1405
|
inlineExtensions.push(createCustomToolsExtension(customTools));
|
|
1358
1406
|
}
|
|
1359
1407
|
|
|
1360
|
-
// Load extensions (
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
} else if (options.preloadedExtensions) {
|
|
1369
|
-
extensionsResult = options.preloadedExtensions;
|
|
1370
|
-
} else {
|
|
1371
|
-
// Merge CLI extension paths with settings extension paths
|
|
1372
|
-
const configuredPaths = [...(options.additionalExtensionPaths ?? []), ...(settings.get("extensions") ?? [])];
|
|
1373
|
-
const disabledExtensionIds = settings.get("disabledExtensions") ?? [];
|
|
1374
|
-
extensionsResult = await logger.time(
|
|
1375
|
-
"discoverAndLoadExtensions",
|
|
1376
|
-
discoverAndLoadExtensions,
|
|
1377
|
-
configuredPaths,
|
|
1378
|
-
cwd,
|
|
1379
|
-
eventBus,
|
|
1380
|
-
disabledExtensionIds,
|
|
1381
|
-
);
|
|
1382
|
-
for (const { path, error } of extensionsResult.errors) {
|
|
1383
|
-
logger.error("Failed to load extension", { path, error });
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1408
|
+
// Load extensions. A preloaded result (e.g. resolved by the CLI before
|
|
1409
|
+
// session creation so it can classify `@file` args extension-aware without
|
|
1410
|
+
// a session/breadcrumb existing yet) is reused as-is; otherwise discover now
|
|
1411
|
+
// through the shared helper. Preloaded wins over `disableExtensionDiscovery`
|
|
1412
|
+
// because the preloaded result already reflects that choice — re-running the
|
|
1413
|
+
// loader here would double-load.
|
|
1414
|
+
const extensionsResult: LoadExtensionsResult =
|
|
1415
|
+
options.preloadedExtensions ?? (await loadSessionExtensions(options, cwd, settings, eventBus));
|
|
1386
1416
|
|
|
1387
1417
|
// Load inline extensions from factories
|
|
1388
1418
|
if (inlineExtensions.length > 0) {
|
|
@@ -1960,8 +1990,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1960
1990
|
sessionManager.appendModelChange(`${model.provider}/${model.id}`);
|
|
1961
1991
|
}
|
|
1962
1992
|
if (!autoThinking) {
|
|
1963
|
-
// `auto`
|
|
1964
|
-
//
|
|
1993
|
+
// Do not write the `auto` selector before the first turn resolves; auto
|
|
1994
|
+
// classification persists its concrete effort once a real user turn runs.
|
|
1965
1995
|
sessionManager.appendThinkingLevelChange(effectiveThinkingLevel);
|
|
1966
1996
|
}
|
|
1967
1997
|
if (initialServiceTier) {
|
|
@@ -2132,7 +2162,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2132
2162
|
agentDir,
|
|
2133
2163
|
taskDepth,
|
|
2134
2164
|
parentHindsightSessionState: options.parentHindsightSessionState,
|
|
2135
|
-
|
|
2165
|
+
parentMnemopiSessionState: options.parentMnemopiSessionState,
|
|
2136
2166
|
}),
|
|
2137
2167
|
),
|
|
2138
2168
|
);
|