@gajae-code/coding-agent 0.9.6 → 0.10.0
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 +45 -0
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/daemon-cli.d.ts +2 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/malloc-env-guard.d.ts +65 -0
- package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +3 -1
- package/dist/types/cli/session-picker.d.ts +4 -3
- package/dist/types/cli/update-cli.d.ts +22 -3
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/commands/daemon.d.ts +6 -2
- package/dist/types/commands/launch.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +1 -1
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +47 -7
- package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
- package/dist/types/coordinator-mcp/server.d.ts +67 -6
- package/dist/types/daemon/control-types.d.ts +10 -0
- package/dist/types/daemon/operator-contract.d.ts +47 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- package/dist/types/gjc-runtime/launch-tmux.d.ts +26 -7
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +34 -3
- package/dist/types/gjc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +309 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +49 -17
- package/dist/types/internal-urls/local-protocol.d.ts +13 -9
- package/dist/types/main.d.ts +66 -2
- package/dist/types/modes/acp/acp-agent.d.ts +19 -2
- package/dist/types/modes/acp/acp-mode.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +59 -0
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
- package/dist/types/modes/components/pet-selector.d.ts +11 -0
- package/dist/types/modes/components/session-selector.d.ts +22 -9
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +4 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +24 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
- package/dist/types/modes/theme/defaults/index.d.ts +568 -568
- package/dist/types/modes/types.d.ts +28 -2
- package/dist/types/modes/utils/abort-message.d.ts +1 -1
- package/dist/types/modes/utils/irc-message.d.ts +38 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
- package/dist/types/notifications/config.d.ts +34 -0
- package/dist/types/notifications/daemon-paths.d.ts +7 -0
- package/dist/types/notifications/index.d.ts +27 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
- package/dist/types/notifications/notification-service.d.ts +144 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
- package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +130 -3
- package/dist/types/notifications/telegram-reference.d.ts +19 -2
- package/dist/types/notifications/topic-registry.d.ts +24 -4
- package/dist/types/registry/agent-registry.d.ts +2 -0
- package/dist/types/rlm/index.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +26 -2
- package/dist/types/session/cache-economics.d.ts +79 -0
- package/dist/types/session/provider-safety-stop.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +124 -4
- package/dist/types/session/session-storage.d.ts +155 -13
- package/dist/types/setup/credential-auto-import.d.ts +2 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/tools/ask-answer-registry.d.ts +5 -0
- package/dist/types/tools/ask.d.ts +25 -1
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
- package/dist/types/tools/index.d.ts +58 -6
- package/dist/types/tools/telegram-send.d.ts +7 -0
- package/dist/types/utils/external-editor.d.ts +2 -0
- package/package.json +9 -9
- package/scripts/g004-tmux-smoke.ts +196 -36
- package/src/cli/daemon-cli.ts +19 -29
- package/src/cli/malloc-env-guard.test.ts +104 -0
- package/src/cli/malloc-env-guard.ts +149 -0
- package/src/cli/notify-cli.ts +78 -14
- package/src/cli/session-picker.ts +23 -40
- package/src/cli/update-cli.ts +56 -37
- package/src/cli.ts +50 -10
- package/src/commands/daemon.ts +19 -5
- package/src/commands/harness.ts +515 -47
- package/src/commands/launch.ts +11 -3
- package/src/commands/notify.ts +5 -1
- package/src/commands/session.ts +6 -1
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-profile-activation.ts +1 -1
- package/src/config/model-profiles.ts +14 -14
- package/src/config/settings-schema.ts +41 -1
- package/src/coordinator-mcp/model-preset.ts +100 -0
- package/src/coordinator-mcp/server.ts +2653 -425
- package/src/daemon/control-types.ts +11 -0
- package/src/daemon/operator-contract.ts +121 -0
- package/src/deep-interview/render-middleware.ts +67 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
- package/src/gjc-runtime/launch-tmux.ts +702 -135
- package/src/gjc-runtime/session-state-sidecar.ts +756 -135
- package/src/gjc-runtime/team-runtime.ts +6 -0
- package/src/gjc-runtime/tmux-common.ts +26 -7
- package/src/gjc-runtime/tmux-gc.ts +52 -9
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +185 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +2060 -0
- package/src/gjc-runtime/tmux-sessions.ts +871 -68
- package/src/internal-urls/docs-index.generated.ts +9 -8
- package/src/internal-urls/local-protocol.ts +29 -10
- package/src/main.ts +298 -75
- package/src/modes/acp/acp-agent.ts +728 -98
- package/src/modes/acp/acp-mode.ts +29 -2
- package/src/modes/components/assistant-message.ts +32 -3
- package/src/modes/components/bash-execution.ts +43 -13
- package/src/modes/components/custom-editor.ts +5 -0
- package/src/modes/components/gajae-pet-widget.ts +360 -0
- package/src/modes/components/hook-selector.ts +12 -0
- package/src/modes/components/irc-sidebar.ts +206 -0
- package/src/modes/components/pet-selector.ts +55 -0
- package/src/modes/components/session-selector.ts +222 -221
- package/src/modes/components/settings-selector.ts +10 -0
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/components/user-message.ts +26 -9
- package/src/modes/controllers/command-controller.ts +28 -2
- package/src/modes/controllers/event-controller.ts +159 -36
- package/src/modes/controllers/extension-ui-controller.ts +36 -17
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +62 -15
- package/src/modes/interactive-mode.ts +183 -7
- package/src/modes/irc-observation-ledger.ts +127 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
- package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
- package/src/modes/types.ts +34 -2
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/irc-message.ts +138 -0
- package/src/modes/utils/ui-helpers.ts +216 -50
- package/src/notifications/config.ts +44 -0
- package/src/notifications/daemon-paths.ts +8 -0
- package/src/notifications/index.ts +535 -75
- package/src/notifications/lifecycle-control-runtime.ts +930 -77
- package/src/notifications/notification-service.ts +710 -0
- package/src/notifications/rate-limit-pool.ts +46 -12
- package/src/notifications/telegram-daemon-cli.ts +2 -0
- package/src/notifications/telegram-daemon-control.ts +27 -2
- package/src/notifications/telegram-daemon.ts +635 -94
- package/src/notifications/telegram-reference.ts +73 -13
- package/src/notifications/topic-registry.ts +86 -9
- package/src/prompts/system/irc-peer-roster.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/irc.md +1 -1
- package/src/registry/agent-registry.ts +3 -0
- package/src/sdk.ts +52 -1
- package/src/secrets/obfuscator.ts +4 -2
- package/src/session/agent-session.ts +396 -145
- package/src/session/cache-economics.ts +275 -0
- package/src/session/provider-safety-stop.ts +9 -0
- package/src/session/session-dump-format.ts +6 -0
- package/src/session/session-manager.ts +809 -39
- package/src/session/session-storage.ts +582 -38
- package/src/slash-commands/builtin-registry.ts +143 -9
- package/src/task/executor.ts +12 -3
- package/src/task/index.ts +8 -0
- package/src/task/receipt.ts +7 -2
- package/src/task/types.ts +26 -0
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/ask.ts +355 -33
- package/src/tools/browser/tab-supervisor.ts +63 -6
- package/src/tools/browser.ts +8 -3
- package/src/tools/index.ts +81 -6
- package/src/tools/sqlite-reader.ts +8 -4
- package/src/tui/output-block.ts +18 -6
- package/src/utils/external-editor.ts +6 -1
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { ThinkingLevel } from "@gajae-code/agent-core";
|
|
4
4
|
import { type Model, modelsAreEqual } from "@gajae-code/ai";
|
|
5
5
|
import { getOAuthProviders } from "@gajae-code/ai/utils/oauth";
|
|
6
|
-
import { Spacer, Text } from "@gajae-code/tui";
|
|
6
|
+
import { isPetMode, PET_MODE_IDS, PET_SKIN_IDS, PET_SKINS, Spacer, Text } from "@gajae-code/tui";
|
|
7
7
|
import { setProjectDir } from "@gajae-code/utils";
|
|
8
8
|
import { jobElapsedMs } from "../async";
|
|
9
9
|
import { materializeActiveModelProfileAssignments } from "../config/model-profile-activation";
|
|
@@ -22,8 +22,20 @@ import {
|
|
|
22
22
|
import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../discovery/helpers.js";
|
|
23
23
|
import { resolveMemoryBackend } from "../memory-backend";
|
|
24
24
|
import { DynamicBorder } from "../modes/components/dynamic-border";
|
|
25
|
+
import { GajaePetWidget } from "../modes/components/gajae-pet-widget";
|
|
25
26
|
import { theme } from "../modes/theme/theme";
|
|
26
27
|
import type { InteractiveModeContext } from "../modes/types";
|
|
28
|
+
import {
|
|
29
|
+
buildNotificationStatusReport,
|
|
30
|
+
checkNotificationHealth,
|
|
31
|
+
formatNotificationHealthReport,
|
|
32
|
+
formatNotificationRecoveryReport,
|
|
33
|
+
formatNotificationStatusReport,
|
|
34
|
+
formatNotificationTestResult,
|
|
35
|
+
recoverNotifications,
|
|
36
|
+
sendNotificationTest,
|
|
37
|
+
} from "../notifications/notification-service";
|
|
38
|
+
import { computeCacheMissCostSummary, formatCacheMissSummaryLines } from "../session/cache-economics";
|
|
27
39
|
import { formatModelOnboardingGuidance } from "../setup/model-onboarding-guidance";
|
|
28
40
|
import {
|
|
29
41
|
addApiCompatibleProvider,
|
|
@@ -36,7 +48,7 @@ import { getDisplayChangelogEntries } from "../utils/changelog";
|
|
|
36
48
|
import { buildContextReportText } from "./helpers/context-report";
|
|
37
49
|
import { buildFastStatusReport } from "./helpers/fast-status-report";
|
|
38
50
|
import { formatDuration } from "./helpers/format";
|
|
39
|
-
import { commandConsumed, errorMessage, parseSlashCommand, usage } from "./helpers/parse";
|
|
51
|
+
import { commandConsumed, errorMessage, parseSlashCommand, parseSubcommand, usage } from "./helpers/parse";
|
|
40
52
|
import { handleSshAcp } from "./helpers/ssh";
|
|
41
53
|
import { buildUsageReportText } from "./helpers/usage-report";
|
|
42
54
|
import type {
|
|
@@ -470,6 +482,66 @@ const shutdownHandlerTui = (_command: ParsedSlashCommand, runtime: TuiSlashComma
|
|
|
470
482
|
};
|
|
471
483
|
|
|
472
484
|
const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
485
|
+
{
|
|
486
|
+
name: "notify",
|
|
487
|
+
priority: 30,
|
|
488
|
+
description: "Notification status, health, test, recovery, and session on/off",
|
|
489
|
+
acpDescription: "Notification status, health, test, recovery, and session on/off",
|
|
490
|
+
subcommands: [
|
|
491
|
+
{ name: "on", description: "Enable notifications for this session" },
|
|
492
|
+
{ name: "off", description: "Disable notifications for this session" },
|
|
493
|
+
{ name: "status", description: "Show notification configuration (no secrets)" },
|
|
494
|
+
{ name: "health", description: "Config, daemon-ownership and endpoint health" },
|
|
495
|
+
{ name: "test", description: "Send a test notification", usage: "[message]" },
|
|
496
|
+
{ name: "recovery", description: "Clear dead-owner locks and stale endpoint files" },
|
|
497
|
+
{ name: "setup", description: "How to pair a Telegram bot (run in a terminal)" },
|
|
498
|
+
],
|
|
499
|
+
inlineHint: "[on|off|status|health|test|recovery|setup]",
|
|
500
|
+
acpInputHint: "[on|off|status|health|test|recovery|setup]",
|
|
501
|
+
allowArgs: true,
|
|
502
|
+
handle: async (command, runtime) => {
|
|
503
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
504
|
+
const action = verb || "status";
|
|
505
|
+
// `on`/`off` are session-local runtime controls owned by the
|
|
506
|
+
// notifications extension command (`api.registerCommand("notify")`),
|
|
507
|
+
// which holds the live per-session server/disable state. Pass them
|
|
508
|
+
// through untouched — never consume them — so this builtin cannot
|
|
509
|
+
// shadow that control. Everything below is config/service diagnostics
|
|
510
|
+
// the extension does not implement, so the builtin owns them exclusively
|
|
511
|
+
// (and the extension therefore never consumes them).
|
|
512
|
+
if (action === "on" || action === "off") {
|
|
513
|
+
return { prompt: command.text };
|
|
514
|
+
}
|
|
515
|
+
const stateRoot = path.join(runtime.cwd, ".gjc", "state");
|
|
516
|
+
switch (action) {
|
|
517
|
+
case "status":
|
|
518
|
+
await runtime.output(formatNotificationStatusReport(buildNotificationStatusReport(runtime.settings)));
|
|
519
|
+
return commandConsumed();
|
|
520
|
+
case "health": {
|
|
521
|
+
const report = await checkNotificationHealth({ settings: runtime.settings, stateRoot });
|
|
522
|
+
await runtime.output(formatNotificationHealthReport(report));
|
|
523
|
+
return commandConsumed();
|
|
524
|
+
}
|
|
525
|
+
case "test": {
|
|
526
|
+
const result = await sendNotificationTest({ settings: runtime.settings, text: rest || undefined });
|
|
527
|
+
await runtime.output(formatNotificationTestResult(result));
|
|
528
|
+
return commandConsumed();
|
|
529
|
+
}
|
|
530
|
+
case "recovery": {
|
|
531
|
+
const report = await recoverNotifications({ settings: runtime.settings, stateRoot });
|
|
532
|
+
await runtime.output(formatNotificationRecoveryReport(report));
|
|
533
|
+
return commandConsumed();
|
|
534
|
+
}
|
|
535
|
+
case "setup":
|
|
536
|
+
return usage(
|
|
537
|
+
"Run `gjc notify setup` in a terminal to pair a Telegram bot token with a private chat (interactive; requires a TTY).",
|
|
538
|
+
runtime,
|
|
539
|
+
);
|
|
540
|
+
default:
|
|
541
|
+
return usage(`Usage: /notify [on|off|status|health|test|recovery|setup] (got "${action}")`, runtime);
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
},
|
|
473
545
|
{
|
|
474
546
|
name: "settings",
|
|
475
547
|
priority: 40,
|
|
@@ -487,6 +559,39 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
487
559
|
runtime.ctx.editor.setText("");
|
|
488
560
|
},
|
|
489
561
|
},
|
|
562
|
+
{
|
|
563
|
+
name: "pet",
|
|
564
|
+
description: "Gajae pet living beside the composer",
|
|
565
|
+
subcommands: [
|
|
566
|
+
{ name: "off", description: "Hide the pet" },
|
|
567
|
+
...PET_SKIN_IDS.map(id => ({ name: id, description: PET_SKINS[id].description })),
|
|
568
|
+
],
|
|
569
|
+
inlineHint: `[${PET_MODE_IDS.join("|")}]`,
|
|
570
|
+
allowArgs: true,
|
|
571
|
+
handleTui: (command, runtime) => {
|
|
572
|
+
const ctx = runtime.ctx;
|
|
573
|
+
const raw = command.args?.trim().toLowerCase() ?? "";
|
|
574
|
+
const arg = raw === "on" ? "red" : raw;
|
|
575
|
+
if (!arg) {
|
|
576
|
+
ctx.showPetSelector();
|
|
577
|
+
ctx.editor.setText("");
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (arg !== "off" && !GajaePetWidget.pixelProtocol()) {
|
|
581
|
+
ctx.showStatus(
|
|
582
|
+
"Gajae pet needs a sixel/kitty-graphics terminal (Windows Terminal 1.22+, kitty, Ghostty, WezTerm)",
|
|
583
|
+
{ dim: true },
|
|
584
|
+
);
|
|
585
|
+
} else if (isPetMode(arg)) {
|
|
586
|
+
ctx.setPetMode(arg);
|
|
587
|
+
const name = arg === "off" ? "Gajae pet hidden" : `${PET_SKINS[arg].label} is here`;
|
|
588
|
+
ctx.showStatus(name);
|
|
589
|
+
} else {
|
|
590
|
+
ctx.showStatus(`Usage: /pet [${PET_MODE_IDS.join("|")}]`, { dim: true });
|
|
591
|
+
}
|
|
592
|
+
ctx.editor.setText("");
|
|
593
|
+
},
|
|
594
|
+
},
|
|
490
595
|
{
|
|
491
596
|
name: "goal",
|
|
492
597
|
priority: 84,
|
|
@@ -820,13 +925,42 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
820
925
|
allowArgs: true,
|
|
821
926
|
handle: async (command, runtime) => {
|
|
822
927
|
if (!command.args || command.args === "info") {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
928
|
+
const stats = runtime.session.getSessionStats();
|
|
929
|
+
const lines = [
|
|
930
|
+
`Session: ${runtime.session.sessionId}`,
|
|
931
|
+
`Title: ${runtime.session.sessionName}`,
|
|
932
|
+
`CWD: ${runtime.cwd}`,
|
|
933
|
+
"",
|
|
934
|
+
"Tokens",
|
|
935
|
+
`Input: ${stats.tokens.input.toLocaleString()}`,
|
|
936
|
+
`Output: ${stats.tokens.output.toLocaleString()}`,
|
|
937
|
+
];
|
|
938
|
+
if (stats.tokens.cacheRead > 0) {
|
|
939
|
+
lines.push(`Cache Read: ${stats.tokens.cacheRead.toLocaleString()}`);
|
|
940
|
+
}
|
|
941
|
+
if (stats.tokens.cacheWrite > 0) {
|
|
942
|
+
lines.push(`Cache Write: ${stats.tokens.cacheWrite.toLocaleString()}`);
|
|
943
|
+
}
|
|
944
|
+
lines.push(`Total: ${stats.tokens.total.toLocaleString()}`);
|
|
945
|
+
if (stats.cost > 0 || stats.premiumRequests > 0) {
|
|
946
|
+
lines.push("", "Cost");
|
|
947
|
+
if (stats.cost > 0) {
|
|
948
|
+
lines.push(`Total: ${stats.cost.toFixed(4)}`);
|
|
949
|
+
}
|
|
950
|
+
if (stats.premiumRequests > 0) {
|
|
951
|
+
lines.push(`Premium Requests: ${stats.premiumRequests.toLocaleString()}`);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
const cacheMissSummary = stats.costBreakdown
|
|
955
|
+
? computeCacheMissCostSummary(stats.tokens, {
|
|
956
|
+
kind: "persisted-aggregate",
|
|
957
|
+
costBreakdown: stats.costBreakdown,
|
|
958
|
+
})
|
|
959
|
+
: undefined;
|
|
960
|
+
if (cacheMissSummary) {
|
|
961
|
+
lines.push("", "Cache Miss Cost", ...formatCacheMissSummaryLines(cacheMissSummary));
|
|
962
|
+
}
|
|
963
|
+
await runtime.output(lines.join("\n"));
|
|
830
964
|
return commandConsumed();
|
|
831
965
|
}
|
|
832
966
|
if (command.args === "delete") {
|
package/src/task/executor.ts
CHANGED
|
@@ -47,6 +47,7 @@ import { persistTaskTokenLog, taskTokenLogFromUsage } from "./token-log";
|
|
|
47
47
|
import {
|
|
48
48
|
type AgentDefinition,
|
|
49
49
|
type AgentProgress,
|
|
50
|
+
hasCompleteUsageCostBreakdown,
|
|
50
51
|
MAX_OUTPUT_BYTES,
|
|
51
52
|
MAX_OUTPUT_LINES,
|
|
52
53
|
type ModelSubstitutionWarning,
|
|
@@ -86,8 +87,8 @@ function normalizeModelPatterns(value: string | string[] | undefined): string[]
|
|
|
86
87
|
.filter(Boolean);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
function renderIrcPeerRoster(selfId: string): string {
|
|
90
|
-
const peers =
|
|
90
|
+
function renderIrcPeerRoster(agentRegistry: AgentRegistry, selfId: string): string {
|
|
91
|
+
const peers = agentRegistry
|
|
91
92
|
.list()
|
|
92
93
|
.filter(ref => ref.id !== selfId && (ref.status === "running" || ref.status === "idle"));
|
|
93
94
|
if (peers.length === 0) return "- (no other live agents)";
|
|
@@ -151,6 +152,8 @@ export interface ExecutorOptions {
|
|
|
151
152
|
authStorage?: AuthStorage;
|
|
152
153
|
modelRegistry?: ModelRegistry;
|
|
153
154
|
settings?: Settings;
|
|
155
|
+
/** Parent session's registry; shared by child sessions for IRC routing and roster visibility. */
|
|
156
|
+
agentRegistry?: AgentRegistry;
|
|
154
157
|
/**
|
|
155
158
|
* Live service-tier intent of the parent session (`AgentSession.serviceTier`),
|
|
156
159
|
* used as the inherited tier when `task.serviceTier === "inherit"`. Passing the
|
|
@@ -705,6 +708,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
705
708
|
: agent.spawns.join(",");
|
|
706
709
|
|
|
707
710
|
const lspEnabled = enableLsp ?? true;
|
|
711
|
+
const agentRegistry = options.agentRegistry ?? AgentRegistry.global();
|
|
708
712
|
const ircEnabled = options.ircAvailable === true;
|
|
709
713
|
const contextFileForPrompt = ircEnabled ? undefined : options.contextFile;
|
|
710
714
|
const skipPythonPreflight = Array.isArray(toolNames) && !toolNames.includes("eval");
|
|
@@ -743,6 +747,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
743
747
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
744
748
|
};
|
|
745
749
|
let hasUsage = false;
|
|
750
|
+
let usageCostBreakdownComplete = true;
|
|
746
751
|
|
|
747
752
|
const requestAbort = (reason: AbortReason) => {
|
|
748
753
|
if (reason === "timeout") {
|
|
@@ -1109,6 +1114,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1109
1114
|
const usageRecord = messageUsage as Record<string, unknown>;
|
|
1110
1115
|
const costRecord = (messageUsage as { cost?: Record<string, unknown> }).cost;
|
|
1111
1116
|
hasUsage = true;
|
|
1117
|
+
usageCostBreakdownComplete &&= hasCompleteUsageCostBreakdown(usageRecord);
|
|
1112
1118
|
accumulatedUsage.input += getNumberField(usageRecord, "input") ?? 0;
|
|
1113
1119
|
accumulatedUsage.output += getNumberField(usageRecord, "output") ?? 0;
|
|
1114
1120
|
accumulatedUsage.cacheRead += getNumberField(usageRecord, "cacheRead") ?? 0;
|
|
@@ -1382,7 +1388,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1382
1388
|
worktree: worktree ?? "",
|
|
1383
1389
|
outputSchema: normalizedOutputSchema,
|
|
1384
1390
|
contextFile: contextFileForPrompt,
|
|
1385
|
-
ircPeers: ircEnabled ? renderIrcPeerRoster(id) : "",
|
|
1391
|
+
ircPeers: ircEnabled ? renderIrcPeerRoster(agentRegistry, id) : "",
|
|
1386
1392
|
ircSelfId: ircEnabled ? id : "",
|
|
1387
1393
|
forkContext: forkContextNotice,
|
|
1388
1394
|
});
|
|
@@ -1411,6 +1417,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1411
1417
|
parentTaskPrefix: id,
|
|
1412
1418
|
agentId: id,
|
|
1413
1419
|
agentDisplayName: agent.name,
|
|
1420
|
+
agentRosterLabel: options.description,
|
|
1414
1421
|
bashAllowedPrefixes: agent.bashAllowedPrefixes,
|
|
1415
1422
|
enableLsp: lspEnabled,
|
|
1416
1423
|
skipPythonPreflight,
|
|
@@ -1418,6 +1425,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1418
1425
|
localProtocolOptions: options.localProtocolOptions,
|
|
1419
1426
|
telemetry: subagentTelemetry,
|
|
1420
1427
|
forkContextSeed: options.forkContextSeed,
|
|
1428
|
+
agentRegistry,
|
|
1421
1429
|
shouldPause: () => pauseRequested,
|
|
1422
1430
|
}),
|
|
1423
1431
|
);
|
|
@@ -1866,6 +1874,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1866
1874
|
abortReason: finalAbortReason,
|
|
1867
1875
|
paused,
|
|
1868
1876
|
usage: hasUsage ? accumulatedUsage : undefined,
|
|
1877
|
+
usageCostBreakdownComplete: hasUsage && usageCostBreakdownComplete ? true : undefined,
|
|
1869
1878
|
outputPath,
|
|
1870
1879
|
extractedToolData: progress.extractedToolData,
|
|
1871
1880
|
retryFailure: progress.retryFailure,
|
package/src/task/index.ts
CHANGED
|
@@ -34,12 +34,14 @@ import {
|
|
|
34
34
|
type ForkContextMode,
|
|
35
35
|
type ForkContextPolicy,
|
|
36
36
|
getTaskSchema,
|
|
37
|
+
hasCompleteAggregateUsageCostBreakdown,
|
|
37
38
|
type SingleResult,
|
|
38
39
|
type TaskItem,
|
|
39
40
|
type TaskParams,
|
|
40
41
|
type TaskToolDetails,
|
|
41
42
|
type TaskToolSchemaInstance,
|
|
42
43
|
} from "./types";
|
|
44
|
+
|
|
43
45
|
// Import review tools for side effects (registers subagent tool handlers)
|
|
44
46
|
import "../tools/review";
|
|
45
47
|
import type { LocalProtocolOptions } from "../internal-urls";
|
|
@@ -53,6 +55,7 @@ import { getTaskIdValidationError, validateAllocatedTaskId } from "./id";
|
|
|
53
55
|
import { AgentOutputManager } from "./output-manager";
|
|
54
56
|
import { mapWithConcurrencyLimit, Semaphore } from "./parallel";
|
|
55
57
|
import { assertNoRawTaskFields, buildTaskReceipt, buildTaskRoiSummary } from "./receipt";
|
|
58
|
+
|
|
56
59
|
import { renderResult, renderCall as renderTaskCall } from "./render";
|
|
57
60
|
import { reconcileSpawnRoi } from "./roi-reconciliation";
|
|
58
61
|
import { getTaskSimpleModeCapabilities, type TaskSimpleMode } from "./simple-mode";
|
|
@@ -1316,6 +1319,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1316
1319
|
},
|
|
1317
1320
|
authStorage: this.session.authStorage,
|
|
1318
1321
|
modelRegistry: this.session.modelRegistry,
|
|
1322
|
+
agentRegistry: this.session.agentRegistry,
|
|
1319
1323
|
settings: this.session.settings,
|
|
1320
1324
|
inheritedServiceTier: this.session.serviceTier,
|
|
1321
1325
|
contextFiles,
|
|
@@ -1379,6 +1383,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1379
1383
|
},
|
|
1380
1384
|
authStorage: this.session.authStorage,
|
|
1381
1385
|
modelRegistry: this.session.modelRegistry,
|
|
1386
|
+
agentRegistry: this.session.agentRegistry,
|
|
1382
1387
|
settings: this.session.settings,
|
|
1383
1388
|
inheritedServiceTier: this.session.serviceTier,
|
|
1384
1389
|
contextFiles,
|
|
@@ -1529,6 +1534,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1529
1534
|
// Aggregate usage from executor results (already accumulated incrementally)
|
|
1530
1535
|
const aggregatedUsage = createUsageTotals();
|
|
1531
1536
|
let hasAggregatedUsage = false;
|
|
1537
|
+
|
|
1532
1538
|
for (const result of results) {
|
|
1533
1539
|
if (result.usage) {
|
|
1534
1540
|
addUsageTotals(aggregatedUsage, result.usage);
|
|
@@ -1734,6 +1740,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1734
1740
|
results: receipts,
|
|
1735
1741
|
totalDurationMs: totalDuration,
|
|
1736
1742
|
usage: hasAggregatedUsage ? aggregatedUsage : undefined,
|
|
1743
|
+
usageCostBreakdownComplete:
|
|
1744
|
+
hasAggregatedUsage && hasCompleteAggregateUsageCostBreakdown(results) ? true : undefined,
|
|
1737
1745
|
forkContextClonedTokens: forkContextClonedTokens > 0 ? forkContextClonedTokens : undefined,
|
|
1738
1746
|
roiSummary,
|
|
1739
1747
|
roiReconciliation,
|
package/src/task/receipt.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
1
|
+
import { hasCompleteUsageCostBreakdown, type SingleResult, type TaskToolDetails } from "./types";
|
|
3
2
|
export interface TaskRoi {
|
|
4
3
|
tokens: number;
|
|
5
4
|
contextTokens?: number;
|
|
@@ -32,6 +31,7 @@ export interface TaskResultReceipt {
|
|
|
32
31
|
modelSubstitutionWarning?: SingleResult["modelSubstitutionWarning"];
|
|
33
32
|
usage?: SingleResult["usage"];
|
|
34
33
|
cost?: number;
|
|
34
|
+
usageCostBreakdownComplete?: true;
|
|
35
35
|
branchName?: string;
|
|
36
36
|
retryFailure?: { attempt: number; errorSummary: string };
|
|
37
37
|
errorSummary?: string;
|
|
@@ -246,6 +246,8 @@ export function buildTaskReceipt(raw: SingleResult): TaskResultReceipt {
|
|
|
246
246
|
modelSubstitutionWarning: raw.modelSubstitutionWarning,
|
|
247
247
|
usage: raw.usage,
|
|
248
248
|
cost: raw.usage?.cost.total,
|
|
249
|
+
usageCostBreakdownComplete:
|
|
250
|
+
raw.usageCostBreakdownComplete === true && hasCompleteUsageCostBreakdown(raw.usage) ? true : undefined,
|
|
249
251
|
branchName: raw.branchName,
|
|
250
252
|
retryFailure: raw.retryFailure
|
|
251
253
|
? { attempt: raw.retryFailure.attempt, errorSummary: "Retry failure recorded." }
|
|
@@ -274,6 +276,7 @@ export interface RawTaskToolDetails {
|
|
|
274
276
|
results: SingleResult[];
|
|
275
277
|
totalDurationMs: number;
|
|
276
278
|
usage?: TaskToolDetails["usage"];
|
|
279
|
+
usageCostBreakdownComplete?: TaskToolDetails["usageCostBreakdownComplete"];
|
|
277
280
|
async?: TaskToolDetails["async"];
|
|
278
281
|
forkContextClonedTokens?: number;
|
|
279
282
|
roiSummary?: TaskToolDetails["roiSummary"];
|
|
@@ -286,6 +289,8 @@ export function sanitizeTaskToolDetails(raw: RawTaskToolDetails): TaskToolDetail
|
|
|
286
289
|
results: raw.results.map(buildTaskReceipt),
|
|
287
290
|
totalDurationMs: raw.totalDurationMs,
|
|
288
291
|
usage: raw.usage,
|
|
292
|
+
usageCostBreakdownComplete:
|
|
293
|
+
raw.usageCostBreakdownComplete === true && hasCompleteUsageCostBreakdown(raw.usage) ? true : undefined,
|
|
289
294
|
forkContextClonedTokens: raw.forkContextClonedTokens,
|
|
290
295
|
roiSummary: raw.roiSummary ?? buildTaskRoiSummary(raw.results.map(buildTaskReceipt)),
|
|
291
296
|
async: raw.async,
|
package/src/task/types.ts
CHANGED
|
@@ -320,6 +320,8 @@ export interface SingleResult {
|
|
|
320
320
|
paused?: boolean;
|
|
321
321
|
/** Aggregated usage from the subprocess, accumulated incrementally from message_end events. */
|
|
322
322
|
usage?: Usage;
|
|
323
|
+
/** True only when every usage-contributing assistant supplied a complete, non-negative raw cost breakdown. */
|
|
324
|
+
usageCostBreakdownComplete?: true;
|
|
323
325
|
/** Output path for the task result */
|
|
324
326
|
outputPath?: string;
|
|
325
327
|
/** Patch path for isolated worktree output */
|
|
@@ -353,6 +355,28 @@ export interface SingleResult {
|
|
|
353
355
|
forkContextAdvisory?: { recommendedMode: ForkContextMode; reasons: string[] };
|
|
354
356
|
}
|
|
355
357
|
|
|
358
|
+
/** True only for complete, factual five-bucket cost accounting. */
|
|
359
|
+
export function hasCompleteUsageCostBreakdown(usage: unknown): boolean {
|
|
360
|
+
if (!usage || typeof usage !== "object") return false;
|
|
361
|
+
const cost = (usage as { cost?: unknown }).cost;
|
|
362
|
+
if (!cost || typeof cost !== "object") return false;
|
|
363
|
+
return ["input", "output", "cacheRead", "cacheWrite", "total"].every(key => {
|
|
364
|
+
const value = (cost as Record<string, unknown>)[key];
|
|
365
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** True only when every usage-contributing result has explicit, complete cost provenance. */
|
|
370
|
+
export function hasCompleteAggregateUsageCostBreakdown(
|
|
371
|
+
results: readonly Pick<SingleResult, "usage" | "usageCostBreakdownComplete">[],
|
|
372
|
+
): boolean {
|
|
373
|
+
return results.every(
|
|
374
|
+
result =>
|
|
375
|
+
result.usage === undefined ||
|
|
376
|
+
(result.usageCostBreakdownComplete === true && hasCompleteUsageCostBreakdown(result.usage)),
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
356
380
|
/** Tool details for TUI rendering */
|
|
357
381
|
export interface TaskToolDetails {
|
|
358
382
|
projectAgentsDir: string | null;
|
|
@@ -360,6 +384,8 @@ export interface TaskToolDetails {
|
|
|
360
384
|
totalDurationMs: number;
|
|
361
385
|
/** Aggregated usage across all subagents. */
|
|
362
386
|
usage?: Usage;
|
|
387
|
+
/** True only when every usage-contributing subagent supplied a complete raw cost breakdown. */
|
|
388
|
+
usageCostBreakdownComplete?: true;
|
|
363
389
|
/** Aggregate cloned tokens copied into fork-context seeds across subagents. */
|
|
364
390
|
forkContextClonedTokens?: number;
|
|
365
391
|
roiSummary?: {
|
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
* source; registering returns a disposer.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import type { WorkflowGateEmitter } from "../modes/shared/agent-wire/unattended-session";
|
|
10
11
|
import type { AskAnswerSource } from "./index";
|
|
11
12
|
|
|
12
13
|
const sources = new Map<string, AskAnswerSource>();
|
|
14
|
+
const workflowGateEmitters = new Map<string, WorkflowGateEmitter>();
|
|
15
|
+
const workflowGateListeners = new Map<string, Set<(emitter: WorkflowGateEmitter | undefined) => void>>();
|
|
13
16
|
|
|
14
17
|
/** Register `source` for `sessionId`. Returns a disposer that clears it. */
|
|
15
18
|
export function registerAskAnswerSource(sessionId: string, source: AskAnswerSource): () => void {
|
|
@@ -23,3 +26,25 @@ export function registerAskAnswerSource(sessionId: string, source: AskAnswerSour
|
|
|
23
26
|
export function getAskAnswerSource(sessionId: string): AskAnswerSource | undefined {
|
|
24
27
|
return sources.get(sessionId);
|
|
25
28
|
}
|
|
29
|
+
|
|
30
|
+
/** Publish a session's current workflow-gate emitter after mode initialization. */
|
|
31
|
+
export function notifyWorkflowGateEmitterChanged(sessionId: string, emitter: WorkflowGateEmitter | undefined): void {
|
|
32
|
+
if (emitter) workflowGateEmitters.set(sessionId, emitter);
|
|
33
|
+
else workflowGateEmitters.delete(sessionId);
|
|
34
|
+
for (const listener of workflowGateListeners.get(sessionId) ?? []) listener(emitter);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Observe workflow-gate emitter installation even when it occurs after session_start. */
|
|
38
|
+
export function registerWorkflowGateEmitterListener(
|
|
39
|
+
sessionId: string,
|
|
40
|
+
listener: (emitter: WorkflowGateEmitter | undefined) => void,
|
|
41
|
+
): () => void {
|
|
42
|
+
const listeners = workflowGateListeners.get(sessionId) ?? new Set();
|
|
43
|
+
listeners.add(listener);
|
|
44
|
+
workflowGateListeners.set(sessionId, listeners);
|
|
45
|
+
listener(workflowGateEmitters.get(sessionId));
|
|
46
|
+
return () => {
|
|
47
|
+
listeners.delete(listener);
|
|
48
|
+
if (listeners.size === 0) workflowGateListeners.delete(sessionId);
|
|
49
|
+
};
|
|
50
|
+
}
|