@gajae-code/coding-agent 0.13.0 → 0.13.2
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 +34 -0
- package/dist/types/config/file-lock.d.ts +2 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/models-config-schema.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +17 -5
- package/dist/types/config/settings.d.ts +10 -1
- package/dist/types/edit/streaming.d.ts +5 -5
- package/dist/types/eval/py/kernel.d.ts +2 -2
- package/dist/types/eval/py/runtime.d.ts +5 -1
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +3 -1
- package/dist/types/modes/components/model-selector.d.ts +3 -2
- package/dist/types/runtime/process-lifecycle.d.ts +12 -31
- package/dist/types/sdk/broker/identity.d.ts +2 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +17 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +11 -6
- package/dist/types/sdk/broker/startup-failure.d.ts +32 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +4 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +10 -1
- package/dist/types/sdk/bus/reconciliation-store.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +13 -2
- package/dist/types/sdk/bus/telegram-daemon.d.ts +17 -1
- package/dist/types/sdk/host/query/cursor.d.ts +4 -0
- package/dist/types/sdk/host/query/handlers.d.ts +6 -0
- package/dist/types/sdk/host/session-runtime.d.ts +7 -6
- package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/turn-result.d.ts +37 -0
- package/dist/types/session/agent-session.d.ts +8 -11
- package/dist/types/session/blob-store.d.ts +6 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +13 -0
- package/dist/types/slash-commands/helpers/fast-status-report.d.ts +12 -3
- package/dist/types/task/executor.d.ts +4 -7
- package/dist/types/tools/index.d.ts +1 -1
- package/dist/types/tools/read.d.ts +1 -1
- package/dist/types/tools/search.d.ts +1 -1
- package/dist/types/utils/edit-mode.d.ts +69 -0
- package/package.json +8 -8
- package/scripts/generate-hotkeys-docs.ts +8 -6
- package/src/commands/sdk.ts +23 -2
- package/src/config/file-lock.ts +25 -5
- package/src/config/keybindings.ts +4 -2
- package/src/config/model-registry.ts +20 -0
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +29 -22
- package/src/config/settings.ts +42 -10
- package/src/coordinator-mcp/server.ts +87 -106
- package/src/edit/index.ts +5 -19
- package/src/edit/renderer.ts +3 -2
- package/src/edit/streaming.ts +28 -15
- package/src/eval/py/executor.ts +108 -10
- package/src/eval/py/kernel.ts +86 -12
- package/src/eval/py/runtime.ts +200 -16
- package/src/exec/bash-executor.ts +36 -37
- package/src/gjc-runtime/gc-runtime.ts +25 -3
- package/src/gjc-runtime/managed-owner-supervisor.ts +27 -1
- package/src/gjc-runtime/session-state-sidecar.ts +110 -4
- package/src/gjc-runtime/tmux-sessions.ts +34 -5
- package/src/internal-urls/docs-index.generated.ts +9 -9
- package/src/modes/acp/acp-agent.ts +10 -2
- package/src/modes/components/model-selector.ts +28 -8
- package/src/modes/controllers/plan-mode-controller.ts +3 -3
- package/src/modes/controllers/selector-controller.ts +4 -2
- package/src/modes/interactive-mode.ts +4 -0
- package/src/runtime/process-lifecycle.ts +126 -27
- package/src/runtime-mcp/transports/stdio.ts +11 -6
- package/src/sdk/acp/adapter.ts +18 -10
- package/src/sdk/broker/broker.ts +55 -4
- package/src/sdk/broker/ensure.ts +10 -7
- package/src/sdk/broker/identity.ts +10 -0
- package/src/sdk/broker/lifecycle-ledger.ts +56 -6
- package/src/sdk/broker/lifecycle.ts +53 -10
- package/src/sdk/broker/process-incarnation.ts +6 -1
- package/src/sdk/broker/session-index.ts +28 -11
- package/src/sdk/broker/startup-failure.ts +101 -0
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/chat-daemon-runtime.ts +8 -1
- package/src/sdk/bus/conversation-store.ts +171 -50
- package/src/sdk/bus/index.ts +101 -47
- package/src/sdk/bus/kind-aware-reconciliation.ts +52 -2
- package/src/sdk/bus/notification-service.ts +3 -3
- package/src/sdk/bus/reconciliation-store.ts +16 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +13 -2
- package/src/sdk/bus/telegram-daemon.ts +151 -41
- package/src/sdk/bus/topic-registry.ts +38 -9
- package/src/sdk/cli/session-cli.ts +20 -7
- package/src/sdk/client/discovery.ts +23 -2
- package/src/sdk/host/query/cursor.ts +4 -0
- package/src/sdk/host/query/handlers.ts +99 -100
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
- package/src/sdk/host/session-runtime.test.ts +98 -1
- package/src/sdk/host/session-runtime.ts +57 -15
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +192 -2
- package/src/sdk/host/websocket-transport.ts +54 -30
- package/src/sdk/index.ts +1 -0
- package/src/sdk/mcp/server.ts +19 -6
- package/src/sdk/protocol/operation-inventory.generated.json +1 -1
- package/src/sdk/protocol/operation-registry.ts +8 -12
- package/src/sdk/session.ts +5 -14
- package/src/sdk/turn-result.ts +53 -0
- package/src/session/agent-session.ts +112 -69
- package/src/session/blob-store.ts +91 -21
- package/src/session/internal/managed-session-storage.ts +136 -21
- package/src/session/session-manager.ts +408 -81
- package/src/session/session-storage.ts +68 -10
- package/src/slash-commands/builtin-registry.ts +43 -5
- package/src/slash-commands/helpers/fast-status-report.ts +24 -8
- package/src/task/executor.ts +25 -8
- package/src/task/index.ts +5 -4
- package/src/tools/bash.ts +11 -9
- package/src/tools/index.ts +1 -1
- package/src/tools/read.ts +12 -11
- package/src/tools/search.ts +7 -7
- package/src/utils/edit-mode.ts +180 -9
- package/src/web/search/providers/insane.ts +55 -1
|
@@ -748,14 +748,47 @@ function fsyncDirectorySync(pathname: string): void {
|
|
|
748
748
|
|
|
749
749
|
/** Best-effort identity-checked removal of one of our own staged temp names. */
|
|
750
750
|
function unlinkOwnedStagedSync(stagingPath: string, expected: { dev: bigint; ino: bigint }): void {
|
|
751
|
+
let fd: number | undefined;
|
|
751
752
|
try {
|
|
752
753
|
const named = fs.lstatSync(stagingPath, { bigint: true });
|
|
753
754
|
if (!named.isFile() || named.isSymbolicLink() || named.dev !== expected.dev || named.ino !== expected.ino) return;
|
|
754
|
-
|
|
755
|
-
|
|
755
|
+
fd = fs.openSync(stagingPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
756
|
+
const opened = fs.fstatSync(fd, { bigint: true });
|
|
757
|
+
if (
|
|
758
|
+
!opened.isFile() ||
|
|
759
|
+
opened.dev !== named.dev ||
|
|
760
|
+
opened.ino !== named.ino ||
|
|
761
|
+
opened.nlink !== named.nlink ||
|
|
762
|
+
opened.size !== named.size ||
|
|
763
|
+
opened.mtimeNs !== named.mtimeNs ||
|
|
764
|
+
opened.ctimeNs !== named.ctimeNs
|
|
765
|
+
)
|
|
766
|
+
return;
|
|
767
|
+
const hash = createHash("sha256");
|
|
768
|
+
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
769
|
+
let offset = 0;
|
|
770
|
+
while (offset < Number(opened.size)) {
|
|
771
|
+
const length = Math.min(buffer.byteLength, Number(opened.size) - offset);
|
|
772
|
+
const count = fs.readSync(fd, buffer, 0, length, offset);
|
|
773
|
+
if (count === 0) return;
|
|
774
|
+
hash.update(buffer.subarray(0, count));
|
|
775
|
+
offset += count;
|
|
776
|
+
}
|
|
777
|
+
const after = fs.fstatSync(fd, { bigint: true });
|
|
778
|
+
if (
|
|
779
|
+
after.dev !== opened.dev ||
|
|
780
|
+
after.ino !== opened.ino ||
|
|
781
|
+
after.nlink !== opened.nlink ||
|
|
782
|
+
after.size !== opened.size ||
|
|
783
|
+
after.mtimeNs !== opened.mtimeNs ||
|
|
784
|
+
after.ctimeNs !== opened.ctimeNs
|
|
785
|
+
)
|
|
786
|
+
return;
|
|
787
|
+
exactRemoveSessionStorageLockPath(stagingPath, after, hash.digest("hex"), after.nlink > 1n);
|
|
756
788
|
} catch {
|
|
757
789
|
// ENOENT means the name was already consumed or removed; cleanup is best-effort.
|
|
758
|
-
|
|
790
|
+
} finally {
|
|
791
|
+
if (fd !== undefined) fs.closeSync(fd);
|
|
759
792
|
}
|
|
760
793
|
}
|
|
761
794
|
|
|
@@ -1601,16 +1634,16 @@ function parseSessionStorageLockOwner(bytes: Buffer): SessionStorageLockOwner |
|
|
|
1601
1634
|
}
|
|
1602
1635
|
}
|
|
1603
1636
|
|
|
1604
|
-
function
|
|
1637
|
+
function exactRemoveSessionStorageLockPathResult(
|
|
1605
1638
|
lockPath: string,
|
|
1606
1639
|
expected: fs.BigIntStats,
|
|
1607
1640
|
sha256: string,
|
|
1608
1641
|
allowHardLink = false,
|
|
1609
|
-
):
|
|
1642
|
+
): native.NativeExactUnlinkResult {
|
|
1610
1643
|
try {
|
|
1611
1644
|
const parent = fs.lstatSync(path.dirname(lockPath), { bigint: true });
|
|
1612
|
-
if (!parent.isDirectory() || parent.isSymbolicLink()) return false;
|
|
1613
|
-
|
|
1645
|
+
if (!parent.isDirectory() || parent.isSymbolicLink()) return { ok: false, code: "parent_mismatch" };
|
|
1646
|
+
return nativeSessionStorage().exactUnlink(lockPath, {
|
|
1614
1647
|
dev: expected.dev,
|
|
1615
1648
|
ino: expected.ino,
|
|
1616
1649
|
nlink: expected.nlink,
|
|
@@ -1622,12 +1655,27 @@ function exactRemoveSessionStorageLockPath(
|
|
|
1622
1655
|
quarantineName: `${path.basename(lockPath)}.reap-${randomUUID()}`,
|
|
1623
1656
|
allowHardLink,
|
|
1624
1657
|
});
|
|
1625
|
-
return result.ok || (result.code === "cleanup_pending" && typeof result.detachedPath === "string");
|
|
1626
1658
|
} catch {
|
|
1627
|
-
return false;
|
|
1659
|
+
return { ok: false, code: "native_failure" };
|
|
1628
1660
|
}
|
|
1629
1661
|
}
|
|
1630
1662
|
|
|
1663
|
+
function exactRemoveSessionStorageLockPath(
|
|
1664
|
+
lockPath: string,
|
|
1665
|
+
expected: fs.BigIntStats,
|
|
1666
|
+
sha256: string,
|
|
1667
|
+
allowHardLink = false,
|
|
1668
|
+
): boolean {
|
|
1669
|
+
const result = exactRemoveSessionStorageLockPathResult(lockPath, expected, sha256, allowHardLink);
|
|
1670
|
+
return (
|
|
1671
|
+
result.ok ||
|
|
1672
|
+
(result.code === "cleanup_pending" &&
|
|
1673
|
+
result.payloadDurable === true &&
|
|
1674
|
+
!result.retainedSuccessorPath &&
|
|
1675
|
+
!result.retainedUnknownPath)
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1631
1679
|
function exactRemoveOwnedSessionStorageLockPath(
|
|
1632
1680
|
lockPath: string,
|
|
1633
1681
|
expected: Pick<fs.BigIntStats, "dev" | "ino">,
|
|
@@ -1836,7 +1884,17 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
1836
1884
|
return {
|
|
1837
1885
|
releaseSync: () => {
|
|
1838
1886
|
if (released) return;
|
|
1839
|
-
|
|
1887
|
+
const removal = exactRemoveSessionStorageLockPathResult(lockPath, expected, ownerDigest!);
|
|
1888
|
+
if (!removal.ok) {
|
|
1889
|
+
if (
|
|
1890
|
+
removal.code === "cleanup_pending" &&
|
|
1891
|
+
removal.payloadDurable === true &&
|
|
1892
|
+
!removal.retainedSuccessorPath &&
|
|
1893
|
+
!removal.retainedUnknownPath
|
|
1894
|
+
) {
|
|
1895
|
+
released = true;
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1840
1898
|
try {
|
|
1841
1899
|
const current = fs.lstatSync(lockPath, { bigint: true });
|
|
1842
1900
|
if (current.dev === expected.dev && current.ino === expected.ino) {
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "../config/model-resolver";
|
|
24
24
|
import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../discovery/helpers.js";
|
|
25
25
|
import { DynamicBorder } from "../modes/components/dynamic-border";
|
|
26
|
-
import { theme } from "../modes/theme/theme";
|
|
26
|
+
import { getAvailableThemes, getDetectedThemeSettingsPath, setTheme, theme } from "../modes/theme/theme";
|
|
27
27
|
import {
|
|
28
28
|
type ComposerSubmissionOptions,
|
|
29
29
|
canApplyComposerSubmission,
|
|
@@ -709,10 +709,48 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
709
709
|
},
|
|
710
710
|
{
|
|
711
711
|
name: "theme",
|
|
712
|
-
description: "
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
712
|
+
description: "Change theme immediately, or open the theme selector without args",
|
|
713
|
+
inlineHint: "[theme]",
|
|
714
|
+
allowArgs: true,
|
|
715
|
+
handleTui: async (command, runtime) => {
|
|
716
|
+
const ctx = runtime.ctx;
|
|
717
|
+
const name = command.args?.trim();
|
|
718
|
+
if (!name) {
|
|
719
|
+
ctx.showThemeSelector();
|
|
720
|
+
ctx.editor.setText("");
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
const available = await getAvailableThemes();
|
|
724
|
+
if (!available.includes(name)) {
|
|
725
|
+
ctx.showError(`Unknown theme "${name}". Available themes: ${available.join(", ")}`);
|
|
726
|
+
ctx.editor.setText("");
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (!ctx.settings.canWriteDurableConfig()) {
|
|
730
|
+
ctx.showError(
|
|
731
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
732
|
+
);
|
|
733
|
+
ctx.editor.setText("");
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
try {
|
|
737
|
+
ctx.settings.set(getDetectedThemeSettingsPath(), name);
|
|
738
|
+
} catch (error) {
|
|
739
|
+
ctx.showError(error instanceof Error ? error.message : String(error));
|
|
740
|
+
ctx.editor.setText("");
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
const result = await setTheme(name, true, { shouldApply: () => !ctx.isStopped?.() });
|
|
744
|
+
if (ctx.isStopped?.()) return;
|
|
745
|
+
ctx.statusLine.invalidate();
|
|
746
|
+
ctx.updateEditorTopBorder();
|
|
747
|
+
ctx.ui.invalidate();
|
|
748
|
+
if (result.success) {
|
|
749
|
+
ctx.showStatus(`Theme changed to ${name}`);
|
|
750
|
+
} else {
|
|
751
|
+
ctx.showError(`Failed to load theme "${name}": ${result.error}\nFell back to dark theme.`);
|
|
752
|
+
}
|
|
753
|
+
ctx.editor.setText("");
|
|
716
754
|
},
|
|
717
755
|
},
|
|
718
756
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Model, modelSupportsServiceTier, modelsAreEqual } from "@gajae-code/ai/core";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A single line in the `/fast status` report: a labelled model and whether fast
|
|
@@ -55,7 +55,7 @@ export function formatFastStatusReport(args: FormatFastStatusReportArgs): string
|
|
|
55
55
|
export interface FastStatusSessionLike {
|
|
56
56
|
readonly model?: Model;
|
|
57
57
|
/** Fast predicate against the main session tier (current model + `modelRoles`). */
|
|
58
|
-
isFastForProvider(provider?: string): boolean;
|
|
58
|
+
isFastForProvider(provider?: string, supportsServiceTier?: boolean): boolean;
|
|
59
59
|
/**
|
|
60
60
|
* Current-model EFFECTIVE fast state (intent minus any provider auto-disable).
|
|
61
61
|
* Used for the current-model row so it matches what the next request does.
|
|
@@ -63,8 +63,13 @@ export interface FastStatusSessionLike {
|
|
|
63
63
|
*/
|
|
64
64
|
isFastModeActive?(): boolean;
|
|
65
65
|
/** Fast predicate against the effective subagent tier (`task.agentModelOverrides` roles). */
|
|
66
|
-
isFastForSubagentProvider(provider?: string): boolean;
|
|
66
|
+
isFastForSubagentProvider(provider?: string, supportsServiceTier?: boolean): boolean;
|
|
67
67
|
resolveRoleModelWithThinking(role: string): { model?: Model };
|
|
68
|
+
/** Runtime fallback position for the configured DEFAULT chain. */
|
|
69
|
+
getDefaultFallbackRuntimeState?(): {
|
|
70
|
+
chain: { entries: readonly string[] };
|
|
71
|
+
controller: { activeIndex: number };
|
|
72
|
+
};
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
/** A role to enumerate in the report, with the tier source its subagent runs under. */
|
|
@@ -106,15 +111,26 @@ export function buildFastStatusReport(args: BuildFastStatusReportArgs): string {
|
|
|
106
111
|
// on pure intent. Fall back to intent when a fake omits `isFastModeActive`.
|
|
107
112
|
const currentFast = session.isFastModeActive
|
|
108
113
|
? session.isFastModeActive()
|
|
109
|
-
: session.isFastForProvider(session.model?.provider);
|
|
114
|
+
: session.isFastForProvider(session.model?.provider, modelSupportsServiceTier(session.model));
|
|
110
115
|
const rows: FastStatusRow[] = [{ label: "현재 모델", model: session.model, fast: currentFast }];
|
|
111
116
|
for (const target of roleTargets) {
|
|
112
117
|
const resolved = session.resolveRoleModelWithThinking(target.id);
|
|
113
|
-
|
|
118
|
+
const fallbackState = target.id === "default" ? session.getDefaultFallbackRuntimeState?.() : undefined;
|
|
119
|
+
const activeDefaultFallback =
|
|
120
|
+
fallbackState !== undefined &&
|
|
121
|
+
fallbackState.chain.entries.length > 1 &&
|
|
122
|
+
fallbackState.controller.activeIndex > 0 &&
|
|
123
|
+
fallbackState.controller.activeIndex < fallbackState.chain.entries.length;
|
|
124
|
+
const rowModel = activeDefaultFallback && session.model ? session.model : resolved.model;
|
|
125
|
+
if (rowModel) {
|
|
126
|
+
const supportsServiceTier = modelSupportsServiceTier(rowModel);
|
|
127
|
+
const isCurrentModel = session.model !== undefined && modelsAreEqual(session.model, rowModel);
|
|
114
128
|
const fast = target.isSubagentRole
|
|
115
|
-
? session.isFastForSubagentProvider(
|
|
116
|
-
:
|
|
117
|
-
|
|
129
|
+
? session.isFastForSubagentProvider(rowModel.provider, supportsServiceTier)
|
|
130
|
+
: isCurrentModel
|
|
131
|
+
? currentFast
|
|
132
|
+
: session.isFastForProvider(rowModel.provider, supportsServiceTier);
|
|
133
|
+
rows.push({ label: target.label, model: rowModel, fast });
|
|
118
134
|
}
|
|
119
135
|
}
|
|
120
136
|
return formatFastStatusReport({ rows, iconFast, formatInactive });
|
package/src/task/executor.ts
CHANGED
|
@@ -15,7 +15,14 @@ import type {
|
|
|
15
15
|
} from "@gajae-code/agent-core";
|
|
16
16
|
import { recordHandoff, resolveTelemetry } from "@gajae-code/agent-core";
|
|
17
17
|
import { estimateMessageTokensHeuristic } from "@gajae-code/agent-core/compaction";
|
|
18
|
-
import
|
|
18
|
+
import {
|
|
19
|
+
type AssistantMessage,
|
|
20
|
+
isFastModeEffectiveForProvider,
|
|
21
|
+
type Message,
|
|
22
|
+
type Model,
|
|
23
|
+
modelSupportsServiceTier,
|
|
24
|
+
type ServiceTier,
|
|
25
|
+
} from "@gajae-code/ai/core";
|
|
19
26
|
import { type JsonSchemaValidationIssue, validateJsonSchemaValue } from "@gajae-code/ai/utils/schema";
|
|
20
27
|
import { logger, prompt, untilAborted } from "@gajae-code/utils";
|
|
21
28
|
import { AsyncJobManager } from "../async";
|
|
@@ -248,14 +255,11 @@ export interface ExecutorOptions {
|
|
|
248
255
|
/** Parent session's registry; shared by child sessions for IRC routing and roster visibility. */
|
|
249
256
|
agentRegistry?: AgentRegistry;
|
|
250
257
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* subagents, and a main-model fast-mode auto-disable does not clobber it.
|
|
258
|
+
* Parent service-tier intent captured when the child is spawned. Used when
|
|
259
|
+
* `task.serviceTier === "inherit"` so request serialization and reporting use
|
|
260
|
+
* the same immutable child settings snapshot.
|
|
255
261
|
*/
|
|
256
262
|
inheritedServiceTier?: ServiceTier;
|
|
257
|
-
/** Resolve whether the effective subagent tier grants fast mode for the selected provider. */
|
|
258
|
-
isFastForSubagentProvider?: (provider?: string) => boolean;
|
|
259
263
|
/** Override local:// protocol options so subagent shares parent's local:// root */
|
|
260
264
|
localProtocolOptions?: LocalProtocolOptions;
|
|
261
265
|
/**
|
|
@@ -865,6 +869,11 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
865
869
|
|
|
866
870
|
const settings = options.settings ?? Settings.isolated();
|
|
867
871
|
const subagentSettings = createSubagentSettings(settings, options.inheritedServiceTier);
|
|
872
|
+
const configuredSubagentServiceTier = subagentSettings.get("serviceTier");
|
|
873
|
+
const subagentServiceTier = configuredSubagentServiceTier === "none" ? undefined : configuredSubagentServiceTier;
|
|
874
|
+
const isFastForModel = (candidate: Model | undefined): boolean =>
|
|
875
|
+
candidate !== undefined &&
|
|
876
|
+
isFastModeEffectiveForProvider(subagentServiceTier, candidate.provider, modelSupportsServiceTier(candidate));
|
|
868
877
|
const maxRecursionDepth = settings.get("task.maxRecursionDepth") ?? 2;
|
|
869
878
|
const maxRuntimeMs = Math.max(0, Math.trunc(Number(settings.get("task.maxRuntimeMs") ?? 0) || 0));
|
|
870
879
|
const parentDepth = options.taskDepth ?? 0;
|
|
@@ -1533,7 +1542,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1533
1542
|
resolvedModelString = formatModelString(model);
|
|
1534
1543
|
activeProviderModelString = resolvedModelString;
|
|
1535
1544
|
}
|
|
1536
|
-
progress.fastMode =
|
|
1545
|
+
progress.fastMode = isFastForModel(model);
|
|
1537
1546
|
if (authFallbackUsed && model && requestedModel) {
|
|
1538
1547
|
modelSubstitutionWarning = {
|
|
1539
1548
|
requested: formatModelString(requestedModel),
|
|
@@ -1954,6 +1963,14 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1954
1963
|
}
|
|
1955
1964
|
if (event.type === "model_fallback_switched") {
|
|
1956
1965
|
activeProviderModelString = event.to;
|
|
1966
|
+
progress.fastMode = isFastForModel(session.model);
|
|
1967
|
+
AsyncJobManager.instance()?.updateSubagentModel?.(options.subagentId ?? id, {
|
|
1968
|
+
requestedModel: modelSubstitutionWarning?.requested ?? resolvedModelString,
|
|
1969
|
+
effectiveModel: event.to,
|
|
1970
|
+
modelFellBack: true,
|
|
1971
|
+
fastMode: progress.fastMode,
|
|
1972
|
+
});
|
|
1973
|
+
scheduleProgress(true);
|
|
1957
1974
|
forwardSubagentEvent(event);
|
|
1958
1975
|
return;
|
|
1959
1976
|
}
|
package/src/task/index.ts
CHANGED
|
@@ -1900,6 +1900,9 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1900
1900
|
file => path.basename(file.path).toLowerCase() !== "agents.md",
|
|
1901
1901
|
);
|
|
1902
1902
|
const promptTemplates = this.session.promptTemplates;
|
|
1903
|
+
const parentMcpManager = this.session.getMcpManager?.();
|
|
1904
|
+
// Exact-config tools-only managers belong to the top-level ACP session and cannot be reused by sub-sessions.
|
|
1905
|
+
const reusableParentMcpManager = parentMcpManager?.isToolsOnly() ? undefined : parentMcpManager;
|
|
1903
1906
|
|
|
1904
1907
|
// Initialize progress for all tasks
|
|
1905
1908
|
for (let i = 0; i < tasksWithUniqueIds.length; i++) {
|
|
@@ -2027,7 +2030,6 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2027
2030
|
agentRegistry: this.session.agentRegistry,
|
|
2028
2031
|
settings: this.session.settings,
|
|
2029
2032
|
inheritedServiceTier: this.session.serviceTier,
|
|
2030
|
-
isFastForSubagentProvider: provider => this.session.isFastForSubagentProvider?.(provider) ?? false,
|
|
2031
2033
|
contextFiles,
|
|
2032
2034
|
skills: availableSkills,
|
|
2033
2035
|
autoloadSkills: resolvedAutoloadSkills,
|
|
@@ -2037,7 +2039,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2037
2039
|
parentArtifactManager,
|
|
2038
2040
|
parentHindsightSessionState: this.session.getHindsightSessionState?.(),
|
|
2039
2041
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
2040
|
-
parentMcpManager:
|
|
2042
|
+
parentMcpManager: reusableParentMcpManager,
|
|
2041
2043
|
forkContextSeed,
|
|
2042
2044
|
});
|
|
2043
2045
|
return {
|
|
@@ -2105,7 +2107,6 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2105
2107
|
agentRegistry: this.session.agentRegistry,
|
|
2106
2108
|
settings: this.session.settings,
|
|
2107
2109
|
inheritedServiceTier: this.session.serviceTier,
|
|
2108
|
-
isFastForSubagentProvider: provider => this.session.isFastForSubagentProvider?.(provider) ?? false,
|
|
2109
2110
|
contextFiles,
|
|
2110
2111
|
skills: availableSkills,
|
|
2111
2112
|
autoloadSkills: resolvedAutoloadSkills,
|
|
@@ -2115,7 +2116,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2115
2116
|
parentArtifactManager,
|
|
2116
2117
|
parentHindsightSessionState: this.session.getHindsightSessionState?.(),
|
|
2117
2118
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
2118
|
-
parentMcpManager:
|
|
2119
|
+
parentMcpManager: reusableParentMcpManager,
|
|
2119
2120
|
forkContextSeed,
|
|
2120
2121
|
});
|
|
2121
2122
|
let capturedResult = {
|
package/src/tools/bash.ts
CHANGED
|
@@ -862,26 +862,28 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
862
862
|
return { kind: "aborted" };
|
|
863
863
|
}
|
|
864
864
|
|
|
865
|
+
const threshold = Promise.withResolvers<{ kind: "running" }>();
|
|
866
|
+
const thresholdTimer = setTimeout(() => threshold.resolve({ kind: "running" }), Math.max(0, thresholdMs));
|
|
865
867
|
const waiters: Array<Promise<ManagedBashJobCompletion | { kind: "running" } | { kind: "aborted" }>> = [
|
|
866
868
|
job.completion,
|
|
867
|
-
|
|
869
|
+
threshold.promise,
|
|
868
870
|
];
|
|
869
871
|
if (backgroundRequest) {
|
|
870
872
|
waiters.push(backgroundRequest.then(() => ({ kind: "running" as const })));
|
|
871
873
|
}
|
|
872
874
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
+
let onAbort: (() => void) | undefined;
|
|
876
|
+
if (signal) {
|
|
877
|
+
const aborted = Promise.withResolvers<{ kind: "aborted" }>();
|
|
878
|
+
onAbort = () => aborted.resolve({ kind: "aborted" });
|
|
879
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
880
|
+
waiters.push(aborted.promise);
|
|
875
881
|
}
|
|
876
|
-
|
|
877
|
-
const { promise: abortedPromise, resolve: resolveAborted } = Promise.withResolvers<{ kind: "aborted" }>();
|
|
878
|
-
const onAbort = () => resolveAborted({ kind: "aborted" });
|
|
879
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
880
|
-
waiters.push(abortedPromise);
|
|
881
882
|
try {
|
|
882
883
|
return await Promise.race(waiters);
|
|
883
884
|
} finally {
|
|
884
|
-
|
|
885
|
+
clearTimeout(thresholdTimer);
|
|
886
|
+
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
885
887
|
}
|
|
886
888
|
}
|
|
887
889
|
|
package/src/tools/index.ts
CHANGED
|
@@ -282,7 +282,7 @@ export interface ToolSession {
|
|
|
282
282
|
/** Live service-tier intent of the parent session, inherited by `inherit` subagents. */
|
|
283
283
|
serviceTier?: ServiceTier;
|
|
284
284
|
/** Whether the effective subagent tier grants fast mode for a resolved provider. */
|
|
285
|
-
isFastForSubagentProvider?: (provider?: string) => boolean;
|
|
285
|
+
isFastForSubagentProvider?: (provider?: string, supportsServiceTier?: boolean) => boolean;
|
|
286
286
|
/** Plan mode state (if active) */
|
|
287
287
|
getPlanModeState?: () => PlanModeState | undefined;
|
|
288
288
|
/** Goal mode state (if active or paused) */
|
package/src/tools/read.ts
CHANGED
|
@@ -1447,7 +1447,18 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1447
1447
|
readonly name = "read";
|
|
1448
1448
|
readonly label = "Read";
|
|
1449
1449
|
readonly loadMode = "essential";
|
|
1450
|
-
|
|
1450
|
+
get description(): string {
|
|
1451
|
+
const displayMode = resolveFileDisplayMode(this.session);
|
|
1452
|
+
return prompt.render(readDescription, {
|
|
1453
|
+
DEFAULT_LIMIT: String(this.#defaultLimit),
|
|
1454
|
+
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
|
1455
|
+
TRUNCATION_DEFAULT: String(this.session.settings.get("read.truncation") ?? "last"),
|
|
1456
|
+
RECEIPT_LINES: String(this.session.settings.get("read.receiptBudgetLines")),
|
|
1457
|
+
RECEIPT_KIB: String(this.session.settings.get("read.receiptBudgetBytes")),
|
|
1458
|
+
IS_HL_MODE: displayMode.hashLines,
|
|
1459
|
+
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1451
1462
|
readonly parameters = readSchema;
|
|
1452
1463
|
readonly nonAbortable = true;
|
|
1453
1464
|
readonly strict = true;
|
|
@@ -1457,21 +1468,11 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1457
1468
|
readonly #defaultLimit: number;
|
|
1458
1469
|
|
|
1459
1470
|
constructor(private readonly session: ToolSession) {
|
|
1460
|
-
const displayMode = resolveFileDisplayMode(session);
|
|
1461
1471
|
this.#autoResizeImages = session.settings.get("images.autoResize");
|
|
1462
1472
|
this.#defaultLimit = Math.max(
|
|
1463
1473
|
1,
|
|
1464
1474
|
Math.min(session.settings.get("read.defaultLimit") ?? DEFAULT_MAX_LINES, DEFAULT_MAX_LINES),
|
|
1465
1475
|
);
|
|
1466
|
-
this.description = prompt.render(readDescription, {
|
|
1467
|
-
DEFAULT_LIMIT: String(this.#defaultLimit),
|
|
1468
|
-
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
|
1469
|
-
TRUNCATION_DEFAULT: String(this.session.settings.get("read.truncation") ?? "last"),
|
|
1470
|
-
RECEIPT_LINES: String(this.session.settings.get("read.receiptBudgetLines")),
|
|
1471
|
-
RECEIPT_KIB: String(this.session.settings.get("read.receiptBudgetBytes")),
|
|
1472
|
-
IS_HL_MODE: displayMode.hashLines,
|
|
1473
|
-
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
1474
|
-
});
|
|
1475
1476
|
}
|
|
1476
1477
|
|
|
1477
1478
|
async #resolveArchiveReadPath(readPath: string, signal?: AbortSignal): Promise<ResolvedArchiveReadPath | null> {
|
package/src/tools/search.ts
CHANGED
|
@@ -237,17 +237,17 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
237
237
|
readonly label = "Search";
|
|
238
238
|
readonly loadMode = "discoverable";
|
|
239
239
|
readonly summary = "Search file contents using ripgrep (fast text search)";
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
constructor(private readonly session: ToolSession) {
|
|
245
|
-
const displayMode = resolveFileDisplayMode(session);
|
|
246
|
-
this.description = prompt.render(searchDescription, {
|
|
240
|
+
get description(): string {
|
|
241
|
+
const displayMode = resolveFileDisplayMode(this.session);
|
|
242
|
+
return prompt.render(searchDescription, {
|
|
247
243
|
IS_HL_MODE: displayMode.hashLines,
|
|
248
244
|
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
249
245
|
});
|
|
250
246
|
}
|
|
247
|
+
readonly parameters = searchSchema;
|
|
248
|
+
readonly strict = true;
|
|
249
|
+
|
|
250
|
+
constructor(private readonly session: ToolSession) {}
|
|
251
251
|
|
|
252
252
|
async execute(
|
|
253
253
|
_toolCallId: string,
|