@gajae-code/coding-agent 0.18.0 → 0.18.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 +35 -0
- package/dist/types/config/settings-schema.d.ts +2 -2
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +1 -0
- package/dist/types/sdk/acp/adapter.d.ts +3 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/client/client.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/task/index.d.ts +0 -1
- package/dist/types/tools/descriptors.d.ts +5 -0
- package/dist/types/tools/eval-backends.d.ts +37 -0
- package/dist/types/tools/eval.d.ts +1 -6
- package/dist/types/tools/index.d.ts +1 -36
- package/dist/types/tools/session-descriptions.d.ts +22 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/src/cli/doctor/managed-link.ts +4 -4
- package/src/cli/doctor/repairs.ts +9 -1
- package/src/config/settings-schema.ts +8 -2
- package/src/exec/bash-shell-guardian.ts +31 -15
- package/src/gjc-runtime/session-state-sidecar.ts +88 -58
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/modes/acp/acp-agent.ts +97 -16
- package/src/modes/interactive-mode.ts +10 -0
- package/src/modes/utils/context-usage.ts +2 -6
- package/src/prompts/tools/task.md +8 -27
- package/src/sdk/acp/adapter.ts +38 -3
- package/src/sdk/broker/doctor-restart.ts +38 -3
- package/src/sdk/bus/index.ts +3 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/client/client.ts +53 -35
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/session/agent-session.ts +65 -15
- package/src/task/index.ts +21 -66
- package/src/tools/descriptors.ts +27 -1
- package/src/tools/eval-backends.ts +95 -0
- package/src/tools/eval.ts +3 -15
- package/src/tools/index.ts +9 -88
- package/src/tools/search.ts +2 -7
- package/src/tools/session-descriptions.ts +135 -0
- package/src/tools/tool-catalog.generated.ts +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eval backend allowance (`GJC_PY`, legacy `PI_PY` / `PI_JS`, then `eval.py` / `eval.js` settings).
|
|
3
|
+
* Kept out of the tools index so lightweight modules, such as the pre-load tool descriptions, can
|
|
4
|
+
* resolve it without importing the whole registry.
|
|
5
|
+
*/
|
|
6
|
+
import { $env } from "@gajae-code/utils";
|
|
7
|
+
import type { ToolSession } from ".";
|
|
8
|
+
|
|
9
|
+
export interface EvalBackendsAllowance {
|
|
10
|
+
python: boolean;
|
|
11
|
+
js: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse the `GJC_PY` multi-value token into per-backend booleans.
|
|
16
|
+
*
|
|
17
|
+
* Tokens (case-insensitive):
|
|
18
|
+
* - `0` / `bash` → JavaScript only (`{ py: false, js: true }`)
|
|
19
|
+
* - `1` / `py` → Python only (`{ py: true, js: false }`)
|
|
20
|
+
* - `js` → JavaScript only (`{ py: false, js: true }`)
|
|
21
|
+
* - `mix` / `both` → both backends (`{ py: true, js: true }`)
|
|
22
|
+
*
|
|
23
|
+
* Returns `null` when `GJC_PY` is unset, empty, or holds an unrecognized
|
|
24
|
+
* token, so the caller can fall back to the legacy `PI_PY` / `PI_JS` flags or
|
|
25
|
+
* per-key settings. This matches the documented contract that invalid values
|
|
26
|
+
* are ignored.
|
|
27
|
+
*/
|
|
28
|
+
export function parseGjcPy(env: Record<string, string | undefined>): { py: boolean; js: boolean } | null {
|
|
29
|
+
const raw = env.GJC_PY;
|
|
30
|
+
if (raw === undefined) return null;
|
|
31
|
+
const token = raw.trim().toLowerCase();
|
|
32
|
+
if (token === "") return null;
|
|
33
|
+
switch (token) {
|
|
34
|
+
case "0":
|
|
35
|
+
case "bash":
|
|
36
|
+
return { py: false, js: true };
|
|
37
|
+
case "1":
|
|
38
|
+
case "py":
|
|
39
|
+
return { py: true, js: false };
|
|
40
|
+
case "js":
|
|
41
|
+
return { py: false, js: true };
|
|
42
|
+
case "mix":
|
|
43
|
+
case "both":
|
|
44
|
+
return { py: true, js: true };
|
|
45
|
+
default:
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isTruthyPythonFlag(value: string | undefined): boolean {
|
|
51
|
+
return value !== undefined && ["1", "true", "yes", "on", "y"].includes(value.trim().toLowerCase());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Parse legacy `PI_PY` / `PI_JS` boolean flags. Each is a boolean flag; unset
|
|
56
|
+
* means "not specified, defer to settings". Returns `null` when neither is set
|
|
57
|
+
* so the caller can fall through to `readEvalBackendsAllowance` per key.
|
|
58
|
+
*/
|
|
59
|
+
function parseLegacyEvalEnvFlags(env: Record<string, string | undefined>): EvalBackendsAllowance | null {
|
|
60
|
+
const pyEnv = env.PI_PY;
|
|
61
|
+
const jsEnv = env.PI_JS;
|
|
62
|
+
if (pyEnv === undefined && jsEnv === undefined) return null;
|
|
63
|
+
return {
|
|
64
|
+
python: pyEnv === undefined ? true : isTruthyPythonFlag(pyEnv),
|
|
65
|
+
js: jsEnv === undefined ? true : isTruthyPythonFlag(jsEnv),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Resolve eval-backend allowance from environment only. `GJC_PY` wins when set
|
|
71
|
+
* to a recognized token; otherwise the legacy `PI_PY` / `PI_JS` flags apply.
|
|
72
|
+
* Returns `null` when no env override is set so the caller can defer to settings.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveEvalBackendsFromEnv(env: Record<string, string | undefined>): EvalBackendsAllowance | null {
|
|
75
|
+
const gjc = parseGjcPy(env);
|
|
76
|
+
if (gjc) return { python: gjc.py, js: gjc.js };
|
|
77
|
+
return parseLegacyEvalEnvFlags(env);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Read per-backend allowance from settings (defaults true). */
|
|
81
|
+
export function readEvalBackendsAllowance(session: ToolSession): EvalBackendsAllowance {
|
|
82
|
+
return {
|
|
83
|
+
python: session.settings.get("eval.py") ?? true,
|
|
84
|
+
js: session.settings.get("eval.js") ?? true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Materialize the active eval backend allowance. `GJC_PY` takes precedence
|
|
90
|
+
* over the legacy `PI_PY` / `PI_JS` env flags, which in turn override the
|
|
91
|
+
* per-key settings (defaults true). When no env override is set, settings win.
|
|
92
|
+
*/
|
|
93
|
+
export function resolveEvalBackends(session: ToolSession): EvalBackendsAllowance {
|
|
94
|
+
return resolveEvalBackendsFromEnv($env) ?? readEvalBackendsAllowance(session);
|
|
95
|
+
}
|
package/src/tools/eval.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { ImageContent } from "@gajae-code/ai/core";
|
|
3
3
|
import type { Component } from "@gajae-code/tui";
|
|
4
4
|
import { Markdown, Text } from "@gajae-code/tui";
|
|
5
|
-
import { prompt } from "@gajae-code/utils";
|
|
6
5
|
import * as z from "zod/v4";
|
|
7
6
|
import { jsBackend, pythonBackend } from "../eval";
|
|
8
7
|
import type { ExecutorBackend } from "../eval/backend";
|
|
@@ -10,7 +9,6 @@ import type { EvalCellResult, EvalDisplayOutput, EvalLanguage, EvalStatusEvent,
|
|
|
10
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
11
10
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
12
11
|
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
13
|
-
import evalDescription from "../prompts/tools/eval.md" with { type: "text" };
|
|
14
12
|
import { DEFAULT_MAX_BYTES, OutputSink, type OutputSummary, TailBuffer } from "../session/streaming-output";
|
|
15
13
|
import { getTreeBranch, getTreeContinuePrefix, renderCodeCell } from "../tui";
|
|
16
14
|
import { resolveEvalBackends, type ToolSession } from ".";
|
|
@@ -21,6 +19,7 @@ import {
|
|
|
21
19
|
stripOutputNotice,
|
|
22
20
|
} from "./output-meta";
|
|
23
21
|
import { formatTitle, replaceTabs, shortenPath, truncateToWidth, wrapBrackets } from "./render-utils";
|
|
22
|
+
import { evalToolDescriptionForSession } from "./session-descriptions";
|
|
24
23
|
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
25
24
|
import { toolResult } from "./tool-result";
|
|
26
25
|
import { clampTimeout } from "./tool-timeouts";
|
|
@@ -134,16 +133,7 @@ function renderJsonTree(value: unknown, theme: Theme, expanded: boolean, maxDept
|
|
|
134
133
|
return renderNode(value, "", 0, true);
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
export
|
|
138
|
-
py?: boolean;
|
|
139
|
-
js?: boolean;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export function getEvalToolDescription(options: EvalToolDescriptionOptions = {}): string {
|
|
143
|
-
const py = options.py ?? true;
|
|
144
|
-
const js = options.js ?? true;
|
|
145
|
-
return prompt.render(evalDescription, { py, js });
|
|
146
|
-
}
|
|
136
|
+
export { type EvalToolDescriptionOptions, getEvalToolDescription } from "./session-descriptions";
|
|
147
137
|
|
|
148
138
|
export interface EvalToolOptions {
|
|
149
139
|
proxyExecutor?: EvalProxyExecutor;
|
|
@@ -204,9 +194,7 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
204
194
|
readonly loadMode = "discoverable";
|
|
205
195
|
readonly label = "Eval";
|
|
206
196
|
get description(): string {
|
|
207
|
-
|
|
208
|
-
const backends = resolveEvalBackends(this.session);
|
|
209
|
-
return getEvalToolDescription({ py: backends.python, js: backends.js });
|
|
197
|
+
return evalToolDescriptionForSession(this.session);
|
|
210
198
|
}
|
|
211
199
|
readonly parameters = evalSchema;
|
|
212
200
|
readonly concurrency = "exclusive";
|
package/src/tools/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTelemetryConfig, AgentTool } from "@gajae-code/agent-core";
|
|
2
2
|
import type { Model, ServiceTier, ToolChoice } from "@gajae-code/ai/core";
|
|
3
|
-
import {
|
|
3
|
+
import { logger } from "@gajae-code/utils";
|
|
4
4
|
import type { AsyncJobManager, FoldReason } from "../async";
|
|
5
5
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
6
6
|
import type { Settings } from "../config/settings";
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
resolveEffectiveDiscoveryMode,
|
|
38
38
|
type ToolAvailabilityContext,
|
|
39
39
|
} from "./descriptors";
|
|
40
|
+
import { resolveEvalBackends } from "./eval-backends";
|
|
40
41
|
import { wrapToolWithMetaNotice } from "./output-meta";
|
|
41
42
|
import type { TodoPhase } from "./todo-write";
|
|
42
43
|
|
|
@@ -491,93 +492,13 @@ const GOAL_MODE_TOOL_NAMES = [] as const;
|
|
|
491
492
|
|
|
492
493
|
export type ToolName = keyof typeof BUILTIN_TOOLS;
|
|
493
494
|
|
|
494
|
-
export
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
*
|
|
502
|
-
* Tokens (case-insensitive):
|
|
503
|
-
* - `0` / `bash` → JavaScript only (`{ py: false, js: true }`)
|
|
504
|
-
* - `1` / `py` → Python only (`{ py: true, js: false }`)
|
|
505
|
-
* - `js` → JavaScript only (`{ py: false, js: true }`)
|
|
506
|
-
* - `mix` / `both` → both backends (`{ py: true, js: true }`)
|
|
507
|
-
*
|
|
508
|
-
* Returns `null` when `GJC_PY` is unset, empty, or holds an unrecognized
|
|
509
|
-
* token, so the caller can fall back to the legacy `PI_PY` / `PI_JS` flags or
|
|
510
|
-
* per-key settings. This matches the documented contract that invalid values
|
|
511
|
-
* are ignored.
|
|
512
|
-
*/
|
|
513
|
-
export function parseGjcPy(env: Record<string, string | undefined>): { py: boolean; js: boolean } | null {
|
|
514
|
-
const raw = env.GJC_PY;
|
|
515
|
-
if (raw === undefined) return null;
|
|
516
|
-
const token = raw.trim().toLowerCase();
|
|
517
|
-
if (token === "") return null;
|
|
518
|
-
switch (token) {
|
|
519
|
-
case "0":
|
|
520
|
-
case "bash":
|
|
521
|
-
return { py: false, js: true };
|
|
522
|
-
case "1":
|
|
523
|
-
case "py":
|
|
524
|
-
return { py: true, js: false };
|
|
525
|
-
case "js":
|
|
526
|
-
return { py: false, js: true };
|
|
527
|
-
case "mix":
|
|
528
|
-
case "both":
|
|
529
|
-
return { py: true, js: true };
|
|
530
|
-
default:
|
|
531
|
-
return null;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function isTruthyPythonFlag(value: string | undefined): boolean {
|
|
536
|
-
return value !== undefined && ["1", "true", "yes", "on", "y"].includes(value.trim().toLowerCase());
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
/**
|
|
540
|
-
* Parse legacy `PI_PY` / `PI_JS` boolean flags. Each is a boolean flag; unset
|
|
541
|
-
* means "not specified, defer to settings". Returns `null` when neither is set
|
|
542
|
-
* so the caller can fall through to `readEvalBackendsAllowance` per key.
|
|
543
|
-
*/
|
|
544
|
-
function parseLegacyEvalEnvFlags(env: Record<string, string | undefined>): EvalBackendsAllowance | null {
|
|
545
|
-
const pyEnv = env.PI_PY;
|
|
546
|
-
const jsEnv = env.PI_JS;
|
|
547
|
-
if (pyEnv === undefined && jsEnv === undefined) return null;
|
|
548
|
-
return {
|
|
549
|
-
python: pyEnv === undefined ? true : isTruthyPythonFlag(pyEnv),
|
|
550
|
-
js: jsEnv === undefined ? true : isTruthyPythonFlag(jsEnv),
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Resolve eval-backend allowance from environment only. `GJC_PY` wins when set
|
|
556
|
-
* to a recognized token; otherwise the legacy `PI_PY` / `PI_JS` flags apply.
|
|
557
|
-
* Returns `null` when no env override is set so the caller can defer to settings.
|
|
558
|
-
*/
|
|
559
|
-
export function resolveEvalBackendsFromEnv(env: Record<string, string | undefined>): EvalBackendsAllowance | null {
|
|
560
|
-
const gjc = parseGjcPy(env);
|
|
561
|
-
if (gjc) return { python: gjc.py, js: gjc.js };
|
|
562
|
-
return parseLegacyEvalEnvFlags(env);
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
/** Read per-backend allowance from settings (defaults true). */
|
|
566
|
-
export function readEvalBackendsAllowance(session: ToolSession): EvalBackendsAllowance {
|
|
567
|
-
return {
|
|
568
|
-
python: session.settings.get("eval.py") ?? true,
|
|
569
|
-
js: session.settings.get("eval.js") ?? true,
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* Materialize the active eval backend allowance. `GJC_PY` takes precedence
|
|
575
|
-
* over the legacy `PI_PY` / `PI_JS` env flags, which in turn override the
|
|
576
|
-
* per-key settings (defaults true). When no env override is set, settings win.
|
|
577
|
-
*/
|
|
578
|
-
export function resolveEvalBackends(session: ToolSession): EvalBackendsAllowance {
|
|
579
|
-
return resolveEvalBackendsFromEnv($env) ?? readEvalBackendsAllowance(session);
|
|
580
|
-
}
|
|
495
|
+
export {
|
|
496
|
+
type EvalBackendsAllowance,
|
|
497
|
+
parseGjcPy,
|
|
498
|
+
readEvalBackendsAllowance,
|
|
499
|
+
resolveEvalBackends,
|
|
500
|
+
resolveEvalBackendsFromEnv,
|
|
501
|
+
} from "./eval-backends";
|
|
581
502
|
|
|
582
503
|
/**
|
|
583
504
|
* Create tools from the descriptor registry.
|
package/src/tools/search.ts
CHANGED
|
@@ -5,12 +5,10 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
5
5
|
import type { GrepMatch, GrepResult, grep as grepFn } from "@gajae-code/natives";
|
|
6
6
|
import type { Component } from "@gajae-code/tui";
|
|
7
7
|
import { Text } from "@gajae-code/tui";
|
|
8
|
-
import { prompt } from "@gajae-code/utils";
|
|
9
8
|
import * as z from "zod/v4";
|
|
10
9
|
import { getFileReadCache } from "../edit/file-read-cache";
|
|
11
10
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
11
|
import type { Theme } from "../modes/theme/theme";
|
|
13
|
-
import searchDescription from "../prompts/tools/search.md" with { type: "text" };
|
|
14
12
|
import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead } from "../session/streaming-output";
|
|
15
13
|
import { Ellipsis, fileHyperlink, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
16
14
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
@@ -35,6 +33,7 @@ import {
|
|
|
35
33
|
PREVIEW_LIMITS,
|
|
36
34
|
splitGroupsByBlankLine,
|
|
37
35
|
} from "./render-utils";
|
|
36
|
+
import { searchToolDescriptionForSession } from "./session-descriptions";
|
|
38
37
|
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
39
38
|
import { toolResult } from "./tool-result";
|
|
40
39
|
|
|
@@ -259,11 +258,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
259
258
|
readonly loadMode = "discoverable";
|
|
260
259
|
readonly summary = "Search file contents using ripgrep (fast text search)";
|
|
261
260
|
get description(): string {
|
|
262
|
-
|
|
263
|
-
return prompt.render(searchDescription, {
|
|
264
|
-
IS_HL_MODE: displayMode.hashLines,
|
|
265
|
-
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
266
|
-
});
|
|
261
|
+
return searchToolDescriptionForSession(this.session);
|
|
267
262
|
}
|
|
268
263
|
readonly parameters = searchSchema;
|
|
269
264
|
readonly strict = true;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-dependent descriptions for tools whose prompt text varies with session settings.
|
|
3
|
+
*
|
|
4
|
+
* A discoverable tool is advertised before its implementation loads. The facade renders these
|
|
5
|
+
* descriptions from the session itself, so the text sent before the first call is the text the
|
|
6
|
+
* loaded tool reports afterwards, and the provider-visible `tools` block (and with it the
|
|
7
|
+
* prompt-cache prefix) does not change when the implementation loads (#5992).
|
|
8
|
+
*/
|
|
9
|
+
import { parseFrontmatter, prompt } from "@gajae-code/utils";
|
|
10
|
+
import architectAgent from "../prompts/agents/architect.md" with { type: "text" };
|
|
11
|
+
import criticAgent from "../prompts/agents/critic.md" with { type: "text" };
|
|
12
|
+
import executorAgent from "../prompts/agents/executor.md" with { type: "text" };
|
|
13
|
+
import plannerAgent from "../prompts/agents/planner.md" with { type: "text" };
|
|
14
|
+
import evalDescription from "../prompts/tools/eval.md" with { type: "text" };
|
|
15
|
+
import searchDescription from "../prompts/tools/search.md" with { type: "text" };
|
|
16
|
+
import taskDescription from "../prompts/tools/task.md" with { type: "text" };
|
|
17
|
+
import { getTaskSimpleModeCapabilities, type TaskSimpleMode } from "../task/simple-mode";
|
|
18
|
+
import { getTaskSchema, type TaskToolSchemaInstance } from "../task/types";
|
|
19
|
+
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
20
|
+
import { resolveEvalBackends } from "./eval-backends";
|
|
21
|
+
|
|
22
|
+
interface DescriptionSettings {
|
|
23
|
+
get(
|
|
24
|
+
key:
|
|
25
|
+
| "task.maxConcurrency"
|
|
26
|
+
| "task.isolation.mode"
|
|
27
|
+
| "task.simple"
|
|
28
|
+
| "task.disabledAgents"
|
|
29
|
+
| "eval.py"
|
|
30
|
+
| "eval.js"
|
|
31
|
+
| "readLineNumbers"
|
|
32
|
+
| "readHashLines"
|
|
33
|
+
| "edit.mode",
|
|
34
|
+
): unknown;
|
|
35
|
+
getEffectiveAutorouting(): { active: boolean };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface DescriptionSession {
|
|
39
|
+
settings: DescriptionSettings;
|
|
40
|
+
getSessionSpawns?: () => string | null;
|
|
41
|
+
hasEditTool?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface BundledAgentName {
|
|
45
|
+
name: string;
|
|
46
|
+
hide?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseBundledAgentName(markdown: string): BundledAgentName {
|
|
50
|
+
const { frontmatter } = parseFrontmatter(markdown, { level: "fatal" });
|
|
51
|
+
const { name, hide } = frontmatter;
|
|
52
|
+
if (typeof name !== "string") {
|
|
53
|
+
throw new Error("Bundled task agent frontmatter must include a name");
|
|
54
|
+
}
|
|
55
|
+
return { name, hide: hide === true };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Read only each role's frontmatter; importing task/agents would also load and render the full prompts.
|
|
59
|
+
const BUNDLED_AGENT_NAMES = [
|
|
60
|
+
parseBundledAgentName(executorAgent),
|
|
61
|
+
parseBundledAgentName(architectAgent),
|
|
62
|
+
parseBundledAgentName(plannerAgent),
|
|
63
|
+
parseBundledAgentName(criticAgent),
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
export interface EvalToolDescriptionOptions {
|
|
67
|
+
py?: boolean;
|
|
68
|
+
js?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getEvalToolDescription(options: EvalToolDescriptionOptions = {}): string {
|
|
72
|
+
const py = options.py ?? true;
|
|
73
|
+
const js = options.js ?? true;
|
|
74
|
+
return prompt.render(evalDescription, { py, js });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function evalToolDescriptionForSession(session: DescriptionSession | null | undefined): string {
|
|
78
|
+
if (!session) return getEvalToolDescription();
|
|
79
|
+
const backends = resolveEvalBackends({ settings: session.settings } as Parameters<typeof resolveEvalBackends>[0]);
|
|
80
|
+
return getEvalToolDescription({ py: backends.python, js: backends.js });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function searchToolDescriptionForSession(session: DescriptionSession): string {
|
|
84
|
+
const displayMode = resolveFileDisplayMode(session);
|
|
85
|
+
return prompt.render(searchDescription, {
|
|
86
|
+
IS_HL_MODE: displayMode.hashLines,
|
|
87
|
+
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function renderTaskDescription(session: DescriptionSession): string {
|
|
92
|
+
const simpleMode = session.settings.get("task.simple") as TaskSimpleMode;
|
|
93
|
+
const { contextEnabled, customSchemaEnabled } = getTaskSimpleModeCapabilities(simpleMode);
|
|
94
|
+
const isolationMode = session.settings.get("task.isolation.mode");
|
|
95
|
+
const parentSpawns = session.getSessionSpawns?.() ?? "*";
|
|
96
|
+
const spawningDisabled = parentSpawns === "";
|
|
97
|
+
const disabledSetting = session.settings.get("task.disabledAgents");
|
|
98
|
+
const disabledAgents = Array.isArray(disabledSetting)
|
|
99
|
+
? disabledSetting.filter((agent): agent is string => typeof agent === "string")
|
|
100
|
+
: [];
|
|
101
|
+
const allowedSpawns =
|
|
102
|
+
parentSpawns === "*"
|
|
103
|
+
? undefined
|
|
104
|
+
: new Set(
|
|
105
|
+
parentSpawns
|
|
106
|
+
.split(",")
|
|
107
|
+
.map(agent => agent.trim())
|
|
108
|
+
.filter(Boolean),
|
|
109
|
+
);
|
|
110
|
+
const agents = BUNDLED_AGENT_NAMES.filter(
|
|
111
|
+
agent =>
|
|
112
|
+
agent.hide !== true &&
|
|
113
|
+
!disabledAgents.includes(agent.name) &&
|
|
114
|
+
(allowedSpawns === undefined || allowedSpawns.has(agent.name)),
|
|
115
|
+
);
|
|
116
|
+
return prompt.render(taskDescription, {
|
|
117
|
+
agents: agents.map(({ name }) => ({ name })),
|
|
118
|
+
spawningDisabled,
|
|
119
|
+
MAX_CONCURRENCY: session.settings.get("task.maxConcurrency"),
|
|
120
|
+
isolationEnabled: isolationMode !== "none",
|
|
121
|
+
asyncEnabled: true,
|
|
122
|
+
contextEnabled,
|
|
123
|
+
customSchemaEnabled,
|
|
124
|
+
defaultMode: simpleMode === "default",
|
|
125
|
+
schemaFreeMode: simpleMode === "schema-free",
|
|
126
|
+
independentMode: simpleMode === "independent",
|
|
127
|
+
autoroutingActive: session.settings.getEffectiveAutorouting().active,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function taskParametersForSession(session?: DescriptionSession): TaskToolSchemaInstance {
|
|
132
|
+
const simpleMode = session ? (session.settings.get("task.simple") as TaskSimpleMode) : "default";
|
|
133
|
+
const isolationMode = session?.settings.get("task.isolation.mode");
|
|
134
|
+
return getTaskSchema({ isolationEnabled: session !== undefined && isolationMode !== "none", simpleMode });
|
|
135
|
+
}
|
|
@@ -2095,7 +2095,7 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
2095
2095
|
"task": {
|
|
2096
2096
|
"name": "task",
|
|
2097
2097
|
"label": "Task",
|
|
2098
|
-
"description": "Launches subagents to parallelize workflows.\n\n- Results are delivered automatically when complete.\n- The tool result lists the assigned task ids (e.g. `0-AuthLoader`) — those are the live agent ids.\n- Use `subagent` action `inspect` or `list` to snapshot manager state.\n- To wait or cancel, use the `subagent` tool; its await/cancel doctrine is authoritative.\n\nSubagents have no conversation history. Every fact, file path, and direction they need MUST be explicit in `context` or `assignment`.\n\n<parameters>\n- `agent`: agent type for all tasks\n- `tasks`: tasks to execute in parallel\n - `.id`: filesystem-safe, ≤48 chars, matching `[A-Za-z0-9][A-Za-z0-9_-]*`; prefer CamelCase\n - `.description`: UI label only — subagent never sees it\n - `.assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED\n- `context`: shared background prepended to every assignment; session-specific only\n- `.inheritContext` (optional): fork-context mode for seeding the subagent with sanitized parent conversation. Omit it or set `\"none\"` for no copied context. `\"receipt\"` copies a minimal receipt-sized snapshot, `\"last-turn\"` copies only the latest exchange, `\"bounded\"` copies the bounded default snapshot, and `\"full\"` copies a larger snapshot up to the configured/model token cap. Non-`none` modes work only when global `task.forkContext.enabled` is true and the target agent declares `forkContext: allowed`; otherwise the call is rejected. Bundled agents that support it: `executor`, `architect`. Use inherited context only when the subagent's value depends on parent context; cloned tokens are billed to the child as fresh input and surfaced in task receipts as fork-context cloned-token accounting.\n\n- `schema`: JTD schema for expected structured output (do not put format rules in assignments)\n- `spawnPlan` (optional): required before any batch with more than 4 tasks; include whyParallel, whyNotLocal, independence, expectedReceiptShape, and maxInlineTokens.\n\n- `repositoryBinding`: optional authoritative repository/worktree identity. It must match the active session and cannot authorize a foreign repository from this session. For another approved repository, start a fresh session rooted there (for example, `gjc --cwd <approved-worktree>`); do not use `/move` because materialized tasks retain their original binding.\n</parameters>\n\n<rules>\n- HARD runtime gate: calls with more than 4 tasks are rejected before any child launches unless `spawnPlan` is complete.\n- NEVER assign tasks to run project-wide build/test/lint. Caller verifies after the batch.\n- **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.\n- Each task: ≤3–5 explicit files. No globs, no \"update all\", no package-wide scope.
|
|
2098
|
+
"description": "Launches subagents to parallelize workflows.\n\n- Results are delivered automatically when complete.\n- The tool result lists the assigned task ids (e.g. `0-AuthLoader`) — those are the live agent ids.\n- Coordinate with running tasks via `irc` when available, using those ids. `subagent` cancel terminates a task and **cannot carry a message**.\n- Use `subagent` action `inspect` or `list` to snapshot manager state.\n- To wait or cancel, use the `subagent` tool; its await/cancel doctrine is authoritative.\n\nSubagents have no conversation history. Every fact, file path, and direction they need MUST be explicit in `context` or `assignment`.\nUse `irc` for live coordination when it is available.\n\n<parameters>\n- `agent`: agent type for all tasks\n- `tasks`: tasks to execute in parallel\n - `.id`: filesystem-safe, ≤48 chars, matching `[A-Za-z0-9][A-Za-z0-9_-]*`; prefer CamelCase\n - `.description`: UI label only — subagent never sees it\n - `.assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED\n- `context`: shared background prepended to every assignment; session-specific only\n- `.inheritContext` (optional): fork-context mode for seeding the subagent with sanitized parent conversation. Omit it or set `\"none\"` for no copied context. `\"receipt\"` copies a minimal receipt-sized snapshot, `\"last-turn\"` copies only the latest exchange, `\"bounded\"` copies the bounded default snapshot, and `\"full\"` copies a larger snapshot up to the configured/model token cap. Non-`none` modes work only when global `task.forkContext.enabled` is true and the target agent declares `forkContext: allowed`; otherwise the call is rejected. Bundled agents that support it: `executor`, `architect`. Use inherited context only when the subagent's value depends on parent context; cloned tokens are billed to the child as fresh input and surfaced in task receipts as fork-context cloned-token accounting.\n\n- `schema`: JTD schema for expected structured output (do not put format rules in assignments)\n- `spawnPlan` (optional): required before any batch with more than 4 tasks; include whyParallel, whyNotLocal, independence, expectedReceiptShape, and maxInlineTokens.\n\n- `repositoryBinding`: optional authoritative repository/worktree identity. It must match the active session and cannot authorize a foreign repository from this session. For another approved repository, start a fresh session rooted there (for example, `gjc --cwd <approved-worktree>`); do not use `/move` because materialized tasks retain their original binding.\n</parameters>\n\n<rules>\n- HARD runtime gate: calls with more than 4 tasks are rejected before any child launches unless `spawnPlan` is complete.\n- NEVER assign tasks to run project-wide build/test/lint. Caller verifies after the batch.\n- **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.\n- Each task: ≤3–5 explicit files. No globs, no \"update all\", no package-wide scope. Use `irc` to coordinate overlapping work when it is available; otherwise keep file sets independent.\n- Pass large payloads via `local://<path>` URIs, not inline.\n- Put shared constraints in `context` once; do not duplicate across assignments.\n- Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.\n</rules>\n\n<parallelization>\nTest: can task B run correctly without seeing A's output? If no, sequence A → B, unless `irc` is available and A can answer a small clarification.\nStill sequence when one task produces a large, evolving contract (generated types, schema migration, core module API); live coordination is not a substitute for a finished contract.\nParallel when tasks touch disjoint files, are independent refactors/tests, or need only occasional peer clarification.\n</parallelization>\n\n<context-fmt>\n# Goal ← one sentence: what the batch accomplishes\n# Constraints ← MUST/NEVER rules and session decisions\n# Contract ← exact types/signatures if tasks share an interface\n</context-fmt>\n\n<assignment-fmt>\n# Target ← exact files and symbols; explicit non-goals\n# Change ← step-by-step add/remove/rename; APIs and patterns\n# Acceptance ← observable result; no project-wide commands\n</assignment-fmt>\n\n<agents>\nBundled role names: executor, architect, planner, critic. A configured agent may override a bundled role name and takes precedence.\nOther configured agents (project, user, plugin) may also be available; calling with an unknown `agent` lists the agents callable in this session.\n</agents>",
|
|
2099
2099
|
"parameters": {
|
|
2100
2100
|
"type": "object",
|
|
2101
2101
|
"properties": {
|