@oh-my-pi/pi-coding-agent 14.9.2 → 14.9.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 +89 -0
- package/package.json +7 -7
- package/scripts/format-prompts.ts +3 -3
- package/src/async/job-manager.ts +66 -9
- package/src/capability/rule.ts +20 -0
- package/src/config/model-registry.ts +13 -0
- package/src/config/model-resolver.ts +8 -2
- package/src/config/prompt-templates.ts +0 -5
- package/src/config/settings-schema.ts +39 -1
- package/src/edit/index.ts +8 -0
- package/src/edit/renderer.ts +6 -1
- package/src/edit/streaming.ts +53 -2
- package/src/eval/eval.lark +10 -31
- package/src/eval/index.ts +1 -0
- package/src/eval/js/context-manager.ts +1 -38
- package/src/eval/js/prelude.txt +0 -2
- package/src/eval/parse.ts +156 -255
- package/src/eval/py/executor.ts +24 -8
- package/src/eval/py/index.ts +1 -0
- package/src/eval/py/prelude.py +11 -80
- package/src/eval/sniff.ts +28 -0
- package/src/export/html/template.css +50 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +229 -17
- package/src/extensibility/plugins/loader.ts +31 -6
- package/src/extensibility/skills.ts +20 -0
- package/src/hashline/constants.ts +20 -0
- package/src/hashline/grammar.lark +16 -23
- package/src/hashline/hash.ts +4 -34
- package/src/hashline/input.ts +16 -2
- package/src/hashline/parser.ts +12 -1
- package/src/internal-urls/agent-protocol.ts +64 -52
- package/src/internal-urls/artifact-protocol.ts +52 -51
- package/src/internal-urls/docs-index.generated.ts +34 -1
- package/src/internal-urls/index.ts +6 -19
- package/src/internal-urls/local-protocol.ts +50 -7
- package/src/internal-urls/mcp-protocol.ts +3 -8
- package/src/internal-urls/memory-protocol.ts +90 -59
- package/src/internal-urls/pi-protocol.ts +1 -0
- package/src/internal-urls/router.ts +40 -23
- package/src/internal-urls/rule-protocol.ts +3 -20
- package/src/internal-urls/skill-protocol.ts +5 -27
- package/src/internal-urls/types.ts +18 -2
- package/src/main.ts +1 -1
- package/src/mcp/manager.ts +17 -0
- package/src/modes/components/session-observer-overlay.ts +2 -2
- package/src/modes/components/tool-execution.ts +6 -0
- package/src/modes/components/tree-selector.ts +4 -0
- package/src/modes/controllers/event-controller.ts +23 -2
- package/src/modes/controllers/mcp-command-controller.ts +7 -10
- package/src/modes/interactive-mode.ts +2 -2
- package/src/modes/theme/theme.ts +27 -27
- package/src/modes/types.ts +1 -1
- package/src/modes/utils/ui-helpers.ts +14 -9
- package/src/prompts/commands/orchestrate.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +0 -2
- package/src/prompts/system/project-prompt.md +10 -0
- package/src/prompts/system/subagent-system-prompt.md +18 -9
- package/src/prompts/system/subagent-user-prompt.md +1 -10
- package/src/prompts/system/system-prompt.md +159 -232
- package/src/prompts/tools/ask.md +0 -1
- package/src/prompts/tools/bash.md +0 -34
- package/src/prompts/tools/eval.md +27 -16
- package/src/prompts/tools/github.md +6 -5
- package/src/prompts/tools/hashline.md +1 -0
- package/src/prompts/tools/job.md +14 -6
- package/src/prompts/tools/task.md +20 -3
- package/src/registry/agent-registry.ts +2 -1
- package/src/sdk.ts +87 -89
- package/src/session/agent-session.ts +107 -37
- package/src/session/artifacts.ts +7 -4
- package/src/session/session-manager.ts +30 -1
- package/src/ssh/connection-manager.ts +32 -16
- package/src/ssh/sshfs-mount.ts +10 -7
- package/src/system-prompt.ts +3 -9
- package/src/task/executor.ts +23 -7
- package/src/task/index.ts +57 -36
- package/src/tool-discovery/tool-index.ts +21 -8
- package/src/tools/ast-edit.ts +3 -2
- package/src/tools/ast-grep.ts +3 -2
- package/src/tools/bash.ts +30 -50
- package/src/tools/browser/tab-supervisor.ts +12 -2
- package/src/tools/eval.ts +59 -44
- package/src/tools/fetch.ts +1 -1
- package/src/tools/gh.ts +140 -4
- package/src/tools/index.ts +12 -11
- package/src/tools/job.ts +48 -12
- package/src/tools/path-utils.ts +21 -1
- package/src/tools/read.ts +74 -31
- package/src/tools/search.ts +16 -3
- package/src/tools/todo-write.ts +1 -1
- package/src/utils/file-display-mode.ts +11 -5
- package/src/web/scrapers/mastodon.ts +1 -1
- package/src/web/scrapers/repology.ts +7 -7
- package/src/internal-urls/jobs-protocol.ts +0 -119
- package/src/task/template.ts +0 -47
- package/src/tools/bash-normalize.ts +0 -107
|
@@ -9,11 +9,12 @@ Pick the operation via `op`. Each op uses a subset of the parameters:
|
|
|
9
9
|
- `pr_diff` — Read one or more pull request diffs. Optional `pr` (single identifier or array for batch). Optional `repo`. Set `nameOnly: true` for changed file names. Use `exclude` to drop generated paths from the diff.
|
|
10
10
|
- `pr_checkout` — Check one or more pull requests out into dedicated git worktrees. Optional `pr` (number, URL, branch, or array of any of those — pass an array to batch-check-out multiple PRs in one call), `repo`, `force` (reset existing local branch).
|
|
11
11
|
- `pr_push` — Push a checked-out PR branch back to its source branch. Requires the branch to have been checked out via `op: pr_checkout` (carries push metadata). Optional `branch`; defaults to the current checked-out git branch. Optional `forceWithLease`.
|
|
12
|
-
- `search_issues` — Search issues using normal GitHub issue search syntax.
|
|
13
|
-
- `search_prs` — Search pull requests using normal GitHub PR search syntax.
|
|
14
|
-
- `search_code` — Search code with GitHub code search syntax. Required `query`. Optional `repo`, `limit`. Returns matching paths with surrounding fragments.
|
|
15
|
-
- `search_commits` — Search commits across GitHub.
|
|
16
|
-
- `search_repos` — Search repositories across GitHub.
|
|
12
|
+
- `search_issues` — Search issues using normal GitHub issue search syntax. Optional `query` (required unless `since`/`until` is set), `repo`, `limit`, `since`, `until`, `dateField`.
|
|
13
|
+
- `search_prs` — Search pull requests using normal GitHub PR search syntax. Optional `query` (required unless `since`/`until` is set), `repo`, `limit`, `since`, `until`, `dateField`.
|
|
14
|
+
- `search_code` — Search code with GitHub code search syntax. Required `query`. Optional `repo`, `limit`. Returns matching paths with surrounding fragments. Date filtering (`since`/`until`) is **not** supported by GitHub code search.
|
|
15
|
+
- `search_commits` — Search commits across GitHub. Optional `query` (required unless `since`/`until` is set), `repo`, `limit`, `since`, `until`. `dateField` is ignored — always uses `committer-date`.
|
|
16
|
+
- `search_repos` — Search repositories across GitHub. Optional `query` (required unless `since`/`until` is set), `limit`, `since`, `until`, `dateField` (use query qualifiers like `org:`, `language:` instead of `repo`).
|
|
17
|
+
- Date filter format for `since` / `until`: relative duration `<n><unit>` (`m`/`h`/`d`/`w`/`mo`/`y`, e.g. `3d`, `12h`, `2w`), an ISO date `YYYY-MM-DD`, or an ISO datetime. Translated to a single GitHub-search qualifier (`created:≥…`, `created:≤…`, or `created:since..until`). `dateField: "updated"` maps to `updated:` for issues/prs and `pushed:` for repos. When you only want a date filter and no keywords, omit `query` entirely.
|
|
17
18
|
- `run_watch` — Watch a GitHub Actions workflow run. Optional `run` (id or URL). Omitting `run` watches all workflow runs for the current HEAD commit; `branch` falls back to the current branch. Optional `tail` (log lines per failed job). Streams snapshots, fast-fails on the first detected job failure (with a brief grace period to capture concurrent failures), then fetches tailed logs for the failed jobs. The full failed-job logs are saved as a session artifact for on-demand reads.
|
|
18
19
|
</instruction>
|
|
19
20
|
|
|
@@ -17,6 +17,7 @@ Purely textual format. The tool has NO awareness of language, indentation, brack
|
|
|
17
17
|
<rules>
|
|
18
18
|
- Every line of inserted/replacement content **MUST** be emitted as a payload line starting with `{{hsep}}`.
|
|
19
19
|
- `{{hsep}}` is syntax, not content. The inserted text begins after the first `{{hsep}}`; use a bare `{{hsep}}` to insert a blank line.
|
|
20
|
+
- Payload is verbatim — don't escape unicode (write `—`, not `\u2014`).
|
|
20
21
|
- `< A` inserts before line A; `+ A` inserts after line A. `< BOF` / `+ BOF` both prepend; `< EOF` / `+ EOF` both append.
|
|
21
22
|
- `= A..B` replaces the inclusive range with the following payload lines. `= A..B` with no payload blanks the range to a single empty line.
|
|
22
23
|
- `- A..B` deletes the inclusive range; `A..A` for one line.
|
package/src/prompts/tools/job.md
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
Inspects, waits, or cancels async jobs.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Background job results are delivered automatically when complete. Reach for this tool only when you need to intervene.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Operations
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## `list: true`
|
|
8
|
+
Use to inspect what's running.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
## `poll: [id, …]`
|
|
11
|
+
Block until the specified jobs finish or the wait window elapses.
|
|
12
|
+
- Use when you are genuinely blocked on a result and have no other work to do.
|
|
13
|
+
- Returns the current snapshot when the timer elapses; running jobs remain running.
|
|
14
|
+
- Completed jobs include their final output in the returned snapshot.
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
## `cancel: [id, …]`
|
|
17
|
+
Stop running jobs.
|
|
18
|
+
- Use when a job is stalled, hung, or no longer needed.
|
|
19
|
+
- Returns immediately after cancelling.
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
Launches subagents to parallelize workflows.
|
|
2
2
|
|
|
3
3
|
{{#if asyncEnabled}}
|
|
4
|
-
-
|
|
5
|
-
-
|
|
4
|
+
- Results are delivered automatically when complete.
|
|
5
|
+
- If genuinely blocked on task completion, wait with `job` using `poll`; otherwise continue with another task when possible.
|
|
6
|
+
- Call `job` with `list: true` to snapshot manager state; pass `poll: [id]` to wait or `cancel: [id]` to stop \u2014 only when inspection or intervention is useful.
|
|
6
7
|
{{/if}}
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
{{#if ircEnabled}}
|
|
10
|
+
Subagents have no conversation history, but they can reach you and their siblings live via the `irc` tool. Front-load every fact, file path, and direction they need in {{#if contextEnabled}}`context` or `assignment`{{else}}each `assignment`{{/if}}.
|
|
11
|
+
{{else}}
|
|
12
|
+
Subagents have no conversation history. Every fact, file path, and direction they need **MUST** be explicit in {{#if contextEnabled}}`context` or `assignment`{{else}}each `assignment`{{/if}}.
|
|
13
|
+
{{/if}}
|
|
9
14
|
|
|
10
15
|
<parameters>
|
|
11
16
|
- `agent`: agent type for all tasks
|
|
@@ -20,16 +25,28 @@ Subagents have no conversation history. Every fact, file path, and decision they
|
|
|
20
25
|
|
|
21
26
|
<rules>
|
|
22
27
|
- **MUST NOT** assign tasks to run project-wide build/test/lint. Caller verifies after the batch.
|
|
28
|
+
- **Subagents do not verify, lint, or format.** Every assignment **MUST** instruct the subagent to skip all gates and formatters. You run them once at the end across the union of changed files — avoids redundant runs and racing formatter passes.
|
|
29
|
+
{{#if ircEnabled}}
|
|
30
|
+
- Each task: ≤3–5 explicit files. Overlapping file sets are tolerable when peers can coordinate via `irc`, but still fan out to a cluster when the scopes are cleanly separable.
|
|
31
|
+
- No globs, no "update all", no package-wide scope.
|
|
32
|
+
{{else}}
|
|
23
33
|
- Each task: ≤3–5 explicit files. No globs, no "update all", no package-wide scope. Fan out to a cluster instead.
|
|
34
|
+
{{/if}}
|
|
24
35
|
- Pass large payloads via `local://<path>` URIs, not inline.
|
|
25
36
|
{{#if contextEnabled}}- Put shared constraints in `context` once; do not duplicate across assignments.{{/if}}
|
|
26
37
|
- Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.
|
|
27
38
|
</rules>
|
|
28
39
|
|
|
29
40
|
<parallelization>
|
|
41
|
+
{{#if ircEnabled}}
|
|
42
|
+
Test: can task B run correctly without seeing A's output? If no, sequence A → B — **unless** B can reasonably ask A for the missing piece over `irc`. Live coordination beats a serial waterfall when the contract is small and easy to describe in a DM.
|
|
43
|
+
Still sequence when one task produces a large, evolving contract (generated types, schema migration, core module API) the other consumes wholesale — IRC round-trips do not replace a finished artifact.
|
|
44
|
+
Parallel when tasks touch disjoint files, are independent refactors/tests, or only need occasional clarification that can be resolved peer-to-peer.
|
|
45
|
+
{{else}}
|
|
30
46
|
Test: can task B run correctly without seeing A's output? If no, sequence A → B.
|
|
31
47
|
Sequential when one task produces a contract (types, API, schema, core module) the other consumes.
|
|
32
48
|
Parallel when tasks touch disjoint files or are independent refactors/tests.
|
|
49
|
+
{{/if}}
|
|
33
50
|
</parallelization>
|
|
34
51
|
|
|
35
52
|
{{#if contextEnabled}}
|
|
@@ -86,10 +86,11 @@ export class AgentRegistry {
|
|
|
86
86
|
this.#emit({ type: "status_changed", ref });
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
attachSession(id: string, session: AgentSession): void {
|
|
89
|
+
attachSession(id: string, session: AgentSession, sessionFile?: string | null): void {
|
|
90
90
|
const ref = this.#refs.get(id);
|
|
91
91
|
if (!ref) return;
|
|
92
92
|
ref.session = session;
|
|
93
|
+
if (sessionFile !== undefined) ref.sessionFile = sessionFile;
|
|
93
94
|
ref.lastActivity = Date.now();
|
|
94
95
|
}
|
|
95
96
|
|
package/src/sdk.ts
CHANGED
|
@@ -27,7 +27,7 @@ import chalk from "chalk";
|
|
|
27
27
|
import { AsyncJobManager, isBackgroundJobSupportEnabled } from "./async";
|
|
28
28
|
import { createAutoresearchExtension } from "./autoresearch";
|
|
29
29
|
import { loadCapability } from "./capability";
|
|
30
|
-
import { type Rule, ruleCapability } from "./capability/rule";
|
|
30
|
+
import { type Rule, ruleCapability, setActiveRules } from "./capability/rule";
|
|
31
31
|
import { ModelRegistry } from "./config/model-registry";
|
|
32
32
|
import { formatModelString, parseModelPattern, parseModelString, resolveModelRoleValue } from "./config/model-resolver";
|
|
33
33
|
import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
|
|
@@ -59,30 +59,22 @@ import {
|
|
|
59
59
|
type ToolDefinition,
|
|
60
60
|
wrapRegisteredTools,
|
|
61
61
|
} from "./extensibility/extensions";
|
|
62
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
loadSkills as loadSkillsInternal,
|
|
64
|
+
type Skill,
|
|
65
|
+
type SkillWarning,
|
|
66
|
+
setActiveSkills,
|
|
67
|
+
} from "./extensibility/skills";
|
|
63
68
|
import { type FileSlashCommand, loadSlashCommands as loadSlashCommandsInternal } from "./extensibility/slash-commands";
|
|
64
69
|
import type { HindsightSessionState } from "./hindsight/state";
|
|
65
|
-
import {
|
|
66
|
-
AgentProtocolHandler,
|
|
67
|
-
ArtifactProtocolHandler,
|
|
68
|
-
InternalUrlRouter,
|
|
69
|
-
JobsProtocolHandler,
|
|
70
|
-
LocalProtocolHandler,
|
|
71
|
-
type LocalProtocolOptions,
|
|
72
|
-
McpProtocolHandler,
|
|
73
|
-
MemoryProtocolHandler,
|
|
74
|
-
PiProtocolHandler,
|
|
75
|
-
RuleProtocolHandler,
|
|
76
|
-
SkillProtocolHandler,
|
|
77
|
-
} from "./internal-urls";
|
|
70
|
+
import { LocalProtocolHandler, type LocalProtocolOptions } from "./internal-urls";
|
|
78
71
|
import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "./lsp/startup-events";
|
|
79
|
-
import { discoverAndLoadMCPTools,
|
|
72
|
+
import { discoverAndLoadMCPTools, MCPManager, type MCPToolsLoadResult } from "./mcp";
|
|
80
73
|
import {
|
|
81
74
|
collectDiscoverableMCPTools,
|
|
82
75
|
formatDiscoverableMCPToolServerSummary,
|
|
83
76
|
selectDiscoverableMCPToolNamesByServer,
|
|
84
77
|
} from "./mcp/discoverable-tool-metadata";
|
|
85
|
-
import { getMemoryRoot } from "./memories";
|
|
86
78
|
import { resolveMemoryBackend } from "./memory-backend";
|
|
87
79
|
import asyncResultTemplate from "./prompts/tools/async-result.md" with { type: "text" };
|
|
88
80
|
import { AgentRegistry, MAIN_AGENT_ID } from "./registry/agent-registry";
|
|
@@ -917,6 +909,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
917
909
|
let agent: Agent;
|
|
918
910
|
let session!: AgentSession;
|
|
919
911
|
let hasSession = false;
|
|
912
|
+
let hasRegistered = false;
|
|
920
913
|
const enableLsp = options.enableLsp ?? true;
|
|
921
914
|
const backgroundJobsEnabled = isBackgroundJobSupportEnabled(settings);
|
|
922
915
|
const asyncMaxJobs = Math.min(100, Math.max(1, settings.get("async.maxJobs") ?? 100));
|
|
@@ -942,34 +935,39 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
942
935
|
|
|
943
936
|
return preview;
|
|
944
937
|
};
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
938
|
+
// Only top-level sessions own an AsyncJobManager. Subagents reach the
|
|
939
|
+
// parent's manager via `AsyncJobManager.instance()` (set below), so creating
|
|
940
|
+
// a second instance here just to leave it orphaned wastes a constructor and
|
|
941
|
+
// risks accidental disposal of the parent's manager on subagent teardown.
|
|
942
|
+
const asyncJobManager =
|
|
943
|
+
backgroundJobsEnabled && !options.parentTaskPrefix
|
|
944
|
+
? new AsyncJobManager({
|
|
945
|
+
maxRunningJobs: asyncMaxJobs,
|
|
946
|
+
onJobComplete: async (jobId, result, job) => {
|
|
947
|
+
if (!session || asyncJobManager!.isDeliverySuppressed(jobId)) return;
|
|
948
|
+
const formattedResult = await formatAsyncResultForFollowUp(result);
|
|
949
|
+
if (asyncJobManager!.isDeliverySuppressed(jobId)) return;
|
|
950
|
+
|
|
951
|
+
const message = prompt.render(asyncResultTemplate, { jobId, result: formattedResult });
|
|
952
|
+
const durationMs = job ? Math.max(0, Date.now() - job.startTime) : undefined;
|
|
953
|
+
await session.sendCustomMessage(
|
|
954
|
+
{
|
|
955
|
+
customType: "async-result",
|
|
956
|
+
content: message,
|
|
957
|
+
display: true,
|
|
958
|
+
attribution: "agent",
|
|
959
|
+
details: {
|
|
960
|
+
jobId,
|
|
961
|
+
type: job?.type,
|
|
962
|
+
label: job?.label,
|
|
963
|
+
durationMs,
|
|
964
|
+
},
|
|
966
965
|
},
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
: undefined;
|
|
966
|
+
{ deliverAs: "followUp", triggerTurn: true },
|
|
967
|
+
);
|
|
968
|
+
},
|
|
969
|
+
})
|
|
970
|
+
: undefined;
|
|
973
971
|
|
|
974
972
|
const agentRegistry = options.agentRegistry ?? AgentRegistry.global();
|
|
975
973
|
const resolvedAgentId = options.agentId ?? options.parentTaskPrefix ?? MAIN_AGENT_ID;
|
|
@@ -1055,44 +1053,27 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1055
1053
|
return {};
|
|
1056
1054
|
}
|
|
1057
1055
|
},
|
|
1056
|
+
getArtifactManager: () => sessionManager.getArtifactManager(),
|
|
1058
1057
|
settings,
|
|
1059
1058
|
authStorage,
|
|
1060
1059
|
modelRegistry,
|
|
1061
|
-
asyncJobManager,
|
|
1062
1060
|
};
|
|
1063
1061
|
|
|
1064
|
-
//
|
|
1065
|
-
|
|
1062
|
+
// Wire process-wide internal URL singletons owned by their real classes.
|
|
1063
|
+
// Top-level sessions install the active snapshots; subagents inherit them.
|
|
1064
|
+
// Artifact and agent-output URLs resolve via `AgentRegistry.global()` —
|
|
1065
|
+
// the protocol handlers walk each ref's `sessionManager.getArtifactsDir()`,
|
|
1066
|
+
// which collapses to the parent's dir for subagents (they adopt the
|
|
1067
|
+
// parent's ArtifactManager) so one lookup hits everything.
|
|
1066
1068
|
const getArtifactsDir = () => sessionManager.getArtifactsDir();
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
new LocalProtocolHandler(
|
|
1076
|
-
options.localProtocolOptions ?? {
|
|
1077
|
-
getArtifactsDir,
|
|
1078
|
-
getSessionId: () => sessionManager.getSessionId(),
|
|
1079
|
-
},
|
|
1080
|
-
),
|
|
1081
|
-
);
|
|
1082
|
-
internalRouter.register(
|
|
1083
|
-
new SkillProtocolHandler({
|
|
1084
|
-
getSkills: () => skills,
|
|
1085
|
-
}),
|
|
1086
|
-
);
|
|
1087
|
-
internalRouter.register(
|
|
1088
|
-
new RuleProtocolHandler({
|
|
1089
|
-
getRules: () => [...rulebookRules, ...alwaysApplyRules],
|
|
1090
|
-
}),
|
|
1091
|
-
);
|
|
1092
|
-
internalRouter.register(new PiProtocolHandler());
|
|
1093
|
-
internalRouter.register(new JobsProtocolHandler({ getAsyncJobManager: () => asyncJobManager }));
|
|
1094
|
-
internalRouter.register(new McpProtocolHandler({ getMcpManager: () => mcpManager }));
|
|
1095
|
-
toolSession.internalRouter = internalRouter;
|
|
1069
|
+
if (!options.parentTaskPrefix) {
|
|
1070
|
+
setActiveSkills(skills);
|
|
1071
|
+
setActiveRules([...rulebookRules, ...alwaysApplyRules]);
|
|
1072
|
+
if (asyncJobManager) AsyncJobManager.setInstance(asyncJobManager);
|
|
1073
|
+
}
|
|
1074
|
+
if (options.localProtocolOptions) {
|
|
1075
|
+
LocalProtocolHandler.setOverride(options.localProtocolOptions);
|
|
1076
|
+
}
|
|
1096
1077
|
toolSession.getArtifactsDir = getArtifactsDir;
|
|
1097
1078
|
toolSession.agentOutputManager = new AgentOutputManager(
|
|
1098
1079
|
getArtifactsDir,
|
|
@@ -1141,7 +1122,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1141
1122
|
customTools.push(...mcpResult.tools.map(loaded => loaded.tool));
|
|
1142
1123
|
}
|
|
1143
1124
|
}
|
|
1144
|
-
|
|
1125
|
+
// Only top-level sessions own the global MCPManager. Subagents already
|
|
1126
|
+
// receive the parent's manager via `options.mcpManager`, and reassigning
|
|
1127
|
+
// the singleton to the same value is a no-op \u2014 keep the gate explicit
|
|
1128
|
+
// to mirror the AsyncJobManager ownership rule.
|
|
1129
|
+
if (mcpManager && !options.parentTaskPrefix) MCPManager.setInstance(mcpManager);
|
|
1145
1130
|
|
|
1146
1131
|
// Add image tools when the active model or configured image providers can generate images.
|
|
1147
1132
|
const imageGenTools = await logger.time("getImageGenTools", () => getImageGenTools(modelRegistry, model));
|
|
@@ -1552,6 +1537,21 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1552
1537
|
});
|
|
1553
1538
|
}
|
|
1554
1539
|
|
|
1540
|
+
// Pre-register in the global agent registry BEFORE building the system prompt,
|
|
1541
|
+
// so that subagents launched in the same parallel batch can see each other in
|
|
1542
|
+
// their initial `# IRC Peers` block (rendered inside `rebuildSystemPrompt`).
|
|
1543
|
+
// The session reference is attached after construction below.
|
|
1544
|
+
agentRegistry.register({
|
|
1545
|
+
id: resolvedAgentId,
|
|
1546
|
+
displayName: resolvedAgentDisplayName,
|
|
1547
|
+
kind: (options.taskDepth ?? 0) > 0 || options.parentTaskPrefix ? "sub" : "main",
|
|
1548
|
+
parentId: options.parentTaskPrefix,
|
|
1549
|
+
session: null,
|
|
1550
|
+
sessionFile: sessionManager.getSessionFile() ?? null,
|
|
1551
|
+
status: "running",
|
|
1552
|
+
});
|
|
1553
|
+
hasRegistered = true;
|
|
1554
|
+
|
|
1555
1555
|
const { systemPrompt } = await logger.time(
|
|
1556
1556
|
"buildSystemPrompt",
|
|
1557
1557
|
rebuildSystemPrompt,
|
|
@@ -1708,6 +1708,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1708
1708
|
sessionManager,
|
|
1709
1709
|
settings,
|
|
1710
1710
|
evalKernelOwnerId,
|
|
1711
|
+
// Defined only for top-level sessions (creation is gated above).
|
|
1712
|
+
// AgentSession uses this to decide whether it may dispose the global
|
|
1713
|
+
// AsyncJobManager on teardown; subagents inherit the parent's and
|
|
1714
|
+
// **MUST NOT** tear it down.
|
|
1715
|
+
ownedAsyncJobManager: asyncJobManager,
|
|
1711
1716
|
scopedModels: options.scopedModels,
|
|
1712
1717
|
promptTemplates,
|
|
1713
1718
|
slashCommands,
|
|
@@ -1744,24 +1749,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1744
1749
|
defaultSelectedMCPServerNames: [...discoveryDefaultServers],
|
|
1745
1750
|
ttsrManager,
|
|
1746
1751
|
obfuscator,
|
|
1747
|
-
asyncJobManager,
|
|
1748
1752
|
agentId: resolvedAgentId,
|
|
1749
1753
|
agentRegistry,
|
|
1750
1754
|
providerSessionId: options.providerSessionId,
|
|
1751
1755
|
});
|
|
1752
1756
|
hasSession = true;
|
|
1753
1757
|
|
|
1754
|
-
//
|
|
1755
|
-
//
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
displayName: resolvedAgentDisplayName,
|
|
1759
|
-
kind: (options.taskDepth ?? 0) > 0 || options.parentTaskPrefix ? "sub" : "main",
|
|
1760
|
-
parentId: options.parentTaskPrefix,
|
|
1761
|
-
session,
|
|
1762
|
-
sessionFile: sessionManager.getSessionFile() ?? null,
|
|
1763
|
-
status: "running",
|
|
1764
|
-
});
|
|
1758
|
+
// Attach the live session to the pre-registered ref so peers can route IRC
|
|
1759
|
+
// messages here. Refresh sessionFile in case it was unavailable at pre-register
|
|
1760
|
+
// time. The dispose wrapper below unregisters on teardown.
|
|
1761
|
+
agentRegistry.attachSession(resolvedAgentId, session, sessionManager.getSessionFile() ?? null);
|
|
1765
1762
|
{
|
|
1766
1763
|
const originalDispose = session.dispose.bind(session);
|
|
1767
1764
|
session.dispose = async () => {
|
|
@@ -1908,6 +1905,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1908
1905
|
if (hasSession) {
|
|
1909
1906
|
await session.dispose();
|
|
1910
1907
|
} else {
|
|
1908
|
+
if (hasRegistered) agentRegistry.unregister(resolvedAgentId);
|
|
1911
1909
|
await disposeKernelSessionsByOwner(evalKernelOwnerId);
|
|
1912
1910
|
}
|
|
1913
1911
|
} catch (cleanupError) {
|