@gajae-code/coding-agent 0.15.6 → 0.16.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 +99 -0
- package/dist/types/async/job-manager.d.ts +32 -1
- package/dist/types/browser-backend.d.ts +12 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +14 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/sdk.d.ts +2 -0
- package/dist/types/config/autorouting-tier-map.d.ts +24 -0
- package/dist/types/config/billing-path.d.ts +25 -0
- package/dist/types/config/config-schema-version.d.ts +13 -1
- package/dist/types/config/file-lock.d.ts +5 -0
- package/dist/types/config/model-registry.d.ts +22 -0
- package/dist/types/config/settings-schema.d.ts +27 -8
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -3
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/acp/acp-agent.d.ts +21 -21
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/interactive-mode.d.ts +16 -2
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +79 -2
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/ensure.d.ts +3 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +40 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
- package/dist/types/sdk/broker/state-version.d.ts +21 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/index.d.ts +9 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +12 -5
- package/dist/types/sdk/cli/session-cli.d.ts +20 -3
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/events.d.ts +1 -0
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/query/cursor.d.ts +4 -2
- package/dist/types/sdk/host/query/handlers.d.ts +1 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +70 -17
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +3 -1
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
- package/dist/types/sdk/receipt-state.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +6 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/sdk/turn-result.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +52 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +29 -2
- package/dist/types/session/session-manager.d.ts +79 -2
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +16 -0
- package/dist/types/tools/browser/launch.d.ts +10 -0
- package/dist/types/tools/browser/registry.d.ts +8 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -10
- package/dist/types/tools/job.d.ts +3 -1
- package/dist/types/utils/external-editor.d.ts +7 -0
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/pirate.ts +7 -4
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +13 -1
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +107 -9
- package/src/browser-backend.ts +29 -0
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/plugin-cli.ts +29 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +140 -23
- package/src/commands/launch.ts +86 -18
- package/src/commands/sdk.ts +135 -28
- package/src/config/autorouting-tier-map.ts +12 -0
- package/src/config/billing-path.ts +42 -0
- package/src/config/config-schema-version.ts +18 -1
- package/src/config/file-lock.ts +158 -83
- package/src/config/model-preset-registry.ts +178 -23
- package/src/config/model-profiles.ts +5 -5
- package/src/config/model-registry.ts +291 -42
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +27 -8
- package/src/config/settings.ts +51 -15
- package/src/coordinator-mcp/projection-scan.ts +27 -6
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +115 -21
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +27 -0
- package/src/extensibility/extensions/types.ts +8 -3
- package/src/extensibility/gjc-plugins/installer.ts +51 -11
- package/src/extensibility/runtime-skill-discovery.ts +110 -3
- package/src/extensibility/skills.ts +22 -12
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/launch-worktree.ts +285 -4
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +213 -14
- package/src/gjc-runtime/session-state-sidecar.ts +938 -39
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -3
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/internal-urls/docs-index.generated.ts +15 -15
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/main.ts +61 -3
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +610 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/model-selector.ts +76 -8
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/session-selector.ts +23 -15
- package/src/modes/components/sessions-dashboard.ts +8 -5
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +23 -9
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +30 -4
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +30 -2
- package/src/modes/controllers/selector-controller.ts +200 -98
- package/src/modes/interactive-mode.ts +36 -12
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/prompt-action-autocomplete.ts +30 -4
- package/src/modes/runtime-init.ts +12 -9
- package/src/modes/theme/theme.ts +5 -0
- package/src/modes/types.ts +2 -0
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/ui-helpers.ts +22 -3
- package/src/prompts/agent-fragments/browser-aside.md +22 -0
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/ask.md +1 -1
- package/src/prompts/tools/bash.md +1 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +5 -0
- package/src/sdk/broker/broker.ts +1646 -27
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +81 -3
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle-ledger.ts +6 -0
- package/src/sdk/broker/lifecycle.ts +440 -75
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +591 -0
- package/src/sdk/broker/startup-budget.ts +144 -2
- package/src/sdk/broker/state-version.ts +43 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +136 -18
- package/src/sdk/bus/discord-daemon.ts +50 -10
- package/src/sdk/bus/index.ts +84 -7
- package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
- package/src/sdk/bus/lifecycle-commands.ts +4 -0
- package/src/sdk/bus/notification-service.ts +22 -5
- package/src/sdk/bus/prompt-reconciliation.ts +7 -2
- package/src/sdk/bus/reconciliation-store.ts +55 -31
- package/src/sdk/bus/slack-conversation.ts +1 -1
- package/src/sdk/bus/slack-daemon.ts +72 -38
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +41 -15
- package/src/sdk/cli/session-cli.ts +297 -26
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/host/control/dispatch.ts +101 -40
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/events.ts +1 -0
- package/src/sdk/host/host.ts +29 -2
- package/src/sdk/host/query/cursor.ts +52 -7
- package/src/sdk/host/query/handlers.ts +33 -12
- package/src/sdk/host/query/revision-store.ts +12 -0
- package/src/sdk/host/session-runtime.test.ts +673 -14
- package/src/sdk/host/session-runtime.ts +674 -103
- package/src/sdk/host/surface-policy.ts +3 -3
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +17 -4
- package/src/sdk/prompt-deadline-manager.ts +2 -1
- package/src/sdk/protocol/operation-inventory.generated.json +105 -3
- package/src/sdk/protocol/operation-registry.ts +14 -4
- package/src/sdk/receipt-state.ts +10 -0
- package/src/sdk/router/session-router.ts +219 -118
- package/src/sdk/session.ts +416 -195
- package/src/sdk/startup-capability.ts +1 -0
- package/src/sdk/turn-result.ts +22 -0
- package/src/session/agent-session.ts +1628 -318
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +40 -3
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/session-manager.ts +850 -97
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/slash-commands/builtin-registry.ts +41 -0
- package/src/task/executor.ts +9 -4
- package/src/telemetry/events.ts +1138 -12
- package/src/tools/ask-contract.test.ts +64 -0
- package/src/tools/ask-contract.ts +36 -1
- package/src/tools/ask.ts +10 -8
- package/src/tools/bash-interactive.ts +11 -2
- package/src/tools/bash.ts +412 -75
- package/src/tools/browser/launch.ts +37 -0
- package/src/tools/browser/registry.ts +99 -9
- package/src/tools/cron.ts +6 -4
- package/src/tools/descriptors.test.ts +15 -0
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +10 -6
- package/src/tools/job.ts +4 -1
- package/src/tools/monitor.ts +12 -9
- package/src/tools/resolve.ts +30 -10
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/utils/external-editor.ts +72 -1
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
resolveCurrentBranch,
|
|
29
29
|
} from "./status-line/git-utils";
|
|
30
30
|
import { getPreset } from "./status-line/presets";
|
|
31
|
+
import { buildPriorityRow, type PriorityItemSet } from "./status-line/priority-row";
|
|
31
32
|
import { renderSegment, type SegmentContext } from "./status-line/segments";
|
|
32
33
|
import { getSeparator } from "./status-line/separators";
|
|
33
34
|
import { calculateTokensPerSecond } from "./status-line/token-rate";
|
|
@@ -132,6 +133,8 @@ interface CollectedStatusSegments {
|
|
|
132
133
|
left: string[];
|
|
133
134
|
leftSegIds: StatusLineSegmentId[];
|
|
134
135
|
right: string[];
|
|
136
|
+
/** Parallel to `right`; null for action hints, job counts and the version tag. */
|
|
137
|
+
rightSegIds: (StatusLineSegmentId | null)[];
|
|
135
138
|
previewHighlightSegment: StatusLineSegmentId | undefined;
|
|
136
139
|
sessionAccent: boolean | undefined;
|
|
137
140
|
leftSepWidth: number;
|
|
@@ -819,6 +822,7 @@ export class StatusLineComponent implements Component {
|
|
|
819
822
|
}
|
|
820
823
|
|
|
821
824
|
const right: string[] = [];
|
|
825
|
+
const rightSegIds: (StatusLineSegmentId | null)[] = [];
|
|
822
826
|
const actionHints =
|
|
823
827
|
effectiveSettings.showActionHints === false
|
|
824
828
|
? []
|
|
@@ -833,9 +837,13 @@ export class StatusLineComponent implements Component {
|
|
|
833
837
|
const rendered = renderSegment(segId, ctx);
|
|
834
838
|
if (rendered.visible && rendered.content) {
|
|
835
839
|
right.push(highlightSegment(segId, rendered.content));
|
|
840
|
+
rightSegIds.push(segId);
|
|
836
841
|
}
|
|
837
842
|
}
|
|
838
|
-
|
|
843
|
+
for (const hint of actionHints) {
|
|
844
|
+
right.push(hint.content);
|
|
845
|
+
rightSegIds.push(null);
|
|
846
|
+
}
|
|
839
847
|
|
|
840
848
|
const runningBackgroundJobs =
|
|
841
849
|
this.session.getAsyncJobSnapshot()?.running.filter(job => job.metadata?.monitor !== true).length ?? 0;
|
|
@@ -843,9 +851,11 @@ export class StatusLineComponent implements Component {
|
|
|
843
851
|
const icon = theme.icon.agents ? `${theme.icon.agents} ` : "";
|
|
844
852
|
const label = `${formatCount("job", runningBackgroundJobs)} running`;
|
|
845
853
|
right.push(theme.fg("statusLineSubagents", `${icon}${label}`));
|
|
854
|
+
rightSegIds.push(null);
|
|
846
855
|
}
|
|
847
856
|
if (this.#version) {
|
|
848
857
|
right.push(theme.fg("dim", `v${this.#version}`));
|
|
858
|
+
rightSegIds.push(null);
|
|
849
859
|
}
|
|
850
860
|
|
|
851
861
|
return {
|
|
@@ -857,6 +867,7 @@ export class StatusLineComponent implements Component {
|
|
|
857
867
|
left,
|
|
858
868
|
leftSegIds,
|
|
859
869
|
right,
|
|
870
|
+
rightSegIds,
|
|
860
871
|
previewHighlightSegment,
|
|
861
872
|
sessionAccent: effectiveSettings.sessionAccent,
|
|
862
873
|
leftSepWidth: visibleWidth(separatorDef.left),
|
|
@@ -872,22 +883,108 @@ export class StatusLineComponent implements Component {
|
|
|
872
883
|
return Math.max(1, Math.min(3, Math.trunc(raw)));
|
|
873
884
|
}
|
|
874
885
|
|
|
886
|
+
/**
|
|
887
|
+
* Which priority items this layout is carrying, and where.
|
|
888
|
+
*
|
|
889
|
+
* Context % has no segment of its own in most presets — it rides inside the
|
|
890
|
+
* `model` segment — so `model` inherits the context rank whenever it does.
|
|
891
|
+
* Items whose data is absent (no context window, no active goal) carry no
|
|
892
|
+
* priority at all, which keeps a rail with nothing to protect evicting
|
|
893
|
+
* exactly as it always did.
|
|
894
|
+
*/
|
|
895
|
+
#priorityItems(seg: CollectedStatusSegments): {
|
|
896
|
+
include: PriorityItemSet;
|
|
897
|
+
inlineContextPct: boolean;
|
|
898
|
+
} {
|
|
899
|
+
const collected = (id: StatusLineSegmentId): boolean =>
|
|
900
|
+
seg.leftSegIds.includes(id) || seg.rightSegIds.includes(id);
|
|
901
|
+
const inlineContextPct =
|
|
902
|
+
seg.ctx.options.model?.showContextPercent !== false && seg.ctx.contextPctSegmentActive !== true;
|
|
903
|
+
const goalMode = seg.ctx.goalMode;
|
|
904
|
+
|
|
905
|
+
return {
|
|
906
|
+
inlineContextPct,
|
|
907
|
+
include: {
|
|
908
|
+
context:
|
|
909
|
+
seg.ctx.contextWindow > 0 && (collected("context_pct") || (collected("model") && inlineContextPct)),
|
|
910
|
+
goal: collected("mode") && goalMode !== null && (goalMode.enabled || goalMode.paused),
|
|
911
|
+
model: collected("model"),
|
|
912
|
+
},
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Eviction rank: 0 is ordinary telemetry and goes first, 3 is context % and
|
|
918
|
+
* goes last. The model name outranks telemetry but loses to the goal
|
|
919
|
+
* indicator, matching context % > goal > model.
|
|
920
|
+
*/
|
|
921
|
+
#priorityRanker(seg: CollectedStatusSegments): (id: StatusLineSegmentId | null) => number {
|
|
922
|
+
const { include, inlineContextPct } = this.#priorityItems(seg);
|
|
923
|
+
const anyPriority = include.context || include.goal;
|
|
924
|
+
|
|
925
|
+
return (id: StatusLineSegmentId | null): number => {
|
|
926
|
+
if (id === null || !anyPriority) return 0;
|
|
927
|
+
if (id === "context_pct") return include.context ? 3 : 0;
|
|
928
|
+
if (id === "model") {
|
|
929
|
+
if (include.context && inlineContextPct) return 3;
|
|
930
|
+
return include.model ? 1 : 0;
|
|
931
|
+
}
|
|
932
|
+
if (id === "mode") return include.goal ? 2 : 0;
|
|
933
|
+
return 0;
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Priority row for a layout that could not keep everything the user needs on
|
|
939
|
+
* screen, or null when the normal rail already carries context %, the goal
|
|
940
|
+
* indicator and the model name.
|
|
941
|
+
*
|
|
942
|
+
* Context % has no segment of its own in most presets — it rides inside the
|
|
943
|
+
* `model` segment — so losing `model` silently loses the highest-priority
|
|
944
|
+
* item too. Survival is therefore resolved per item, not per segment.
|
|
945
|
+
*/
|
|
946
|
+
#priorityRowFor(
|
|
947
|
+
seg: CollectedStatusSegments,
|
|
948
|
+
survivingLeftIds: readonly (StatusLineSegmentId | null)[],
|
|
949
|
+
survivingRightIds: readonly (StatusLineSegmentId | null)[],
|
|
950
|
+
width: number,
|
|
951
|
+
): string | null {
|
|
952
|
+
const survived = (id: StatusLineSegmentId): boolean =>
|
|
953
|
+
survivingLeftIds.includes(id) || survivingRightIds.includes(id);
|
|
954
|
+
|
|
955
|
+
const { include, inlineContextPct } = this.#priorityItems(seg);
|
|
956
|
+
if (!include.context && !include.goal) return null;
|
|
957
|
+
|
|
958
|
+
const contextLost = include.context && !survived("context_pct") && !(inlineContextPct && survived("model"));
|
|
959
|
+
const goalLost = include.goal && !survived("mode");
|
|
960
|
+
const modelLost = include.model && !survived("model");
|
|
961
|
+
if (!contextLost && !goalLost && !modelLost) return null;
|
|
962
|
+
|
|
963
|
+
return buildPriorityRow(seg.ctx, width, include);
|
|
964
|
+
}
|
|
965
|
+
|
|
875
966
|
#buildStatusLine(width: number, precollected?: CollectedStatusSegments): string {
|
|
876
967
|
const seg = precollected ?? this.#collectStatusSegments(width, this.#resolveSettings());
|
|
877
968
|
const { ctx, separatorDef, bgAnsi, fgAnsi, sepAnsi, previewHighlightSegment } = seg;
|
|
878
969
|
const { leftSepWidth, rightSepWidth, leftCapWidth, rightCapWidth } = seg;
|
|
879
970
|
const topFillWidth = Math.max(0, width);
|
|
880
971
|
|
|
972
|
+
const rank = this.#priorityRanker(seg);
|
|
973
|
+
|
|
881
974
|
/**
|
|
882
|
-
* Evict against `budget
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
*
|
|
975
|
+
* Evict against `budget`: shrink the path first, then drop the
|
|
976
|
+
* lowest-priority segment still standing — right side before left, tail
|
|
977
|
+
* first within a side, which is the historical order for everything that
|
|
978
|
+
* carries no priority. Context %, the goal indicator and the model name
|
|
979
|
+
* outrank ordinary telemetry and are therefore the last to go. Runs from
|
|
980
|
+
* the original segment lists each time so the marker reservation can be
|
|
981
|
+
* recomputed without compounding evictions.
|
|
886
982
|
*/
|
|
887
983
|
const layout = (budget: number) => {
|
|
888
984
|
const left = [...seg.left];
|
|
889
985
|
const leftIds = [...seg.leftSegIds];
|
|
890
986
|
const right = [...seg.right];
|
|
987
|
+
const rightIds = [...seg.rightSegIds];
|
|
891
988
|
let leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
892
989
|
let rightWidth = this.#groupWidth(right, rightCapWidth, rightSepWidth);
|
|
893
990
|
const totalWidth = () => leftWidth + rightWidth + (left.length > 0 && right.length > 0 ? 1 : 0);
|
|
@@ -905,19 +1002,42 @@ export class StatusLineComponent implements Component {
|
|
|
905
1002
|
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
906
1003
|
}
|
|
907
1004
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1005
|
+
// Lowest rank loses first; equal ranks fall back to the historical
|
|
1006
|
+
// right-then-left, tail-first order.
|
|
1007
|
+
while (totalWidth() > budget && (left.length > 0 || right.length > 0)) {
|
|
1008
|
+
let victimSide: "left" | "right" | null = null;
|
|
1009
|
+
let victimIndex = -1;
|
|
1010
|
+
let victimRank = Number.POSITIVE_INFINITY;
|
|
1011
|
+
for (let index = right.length - 1; index >= 0; index -= 1) {
|
|
1012
|
+
const candidate = rank(rightIds[index]);
|
|
1013
|
+
if (candidate < victimRank) {
|
|
1014
|
+
victimRank = candidate;
|
|
1015
|
+
victimSide = "right";
|
|
1016
|
+
victimIndex = index;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
for (let index = left.length - 1; index >= 0; index -= 1) {
|
|
1020
|
+
const candidate = rank(leftIds[index]);
|
|
1021
|
+
if (candidate < victimRank) {
|
|
1022
|
+
victimRank = candidate;
|
|
1023
|
+
victimSide = "left";
|
|
1024
|
+
victimIndex = index;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (victimSide === null) break;
|
|
1028
|
+
if (victimSide === "right") {
|
|
1029
|
+
right.splice(victimIndex, 1);
|
|
1030
|
+
rightIds.splice(victimIndex, 1);
|
|
1031
|
+
rightWidth = this.#groupWidth(right, rightCapWidth, rightSepWidth);
|
|
1032
|
+
} else {
|
|
1033
|
+
left.splice(victimIndex, 1);
|
|
1034
|
+
leftIds.splice(victimIndex, 1);
|
|
1035
|
+
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1036
|
+
}
|
|
916
1037
|
dropped += 1;
|
|
917
|
-
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
918
1038
|
}
|
|
919
1039
|
}
|
|
920
|
-
return { left, right, leftWidth, rightWidth, dropped };
|
|
1040
|
+
return { left, right, leftIds, rightIds, leftWidth, rightWidth, dropped };
|
|
921
1041
|
};
|
|
922
1042
|
|
|
923
1043
|
// First pass with the full budget establishes whether anything is lost at
|
|
@@ -939,6 +1059,11 @@ export class StatusLineComponent implements Component {
|
|
|
939
1059
|
}
|
|
940
1060
|
}
|
|
941
1061
|
const marker = this.#renderOverflowMarker(placed.dropped, reserved);
|
|
1062
|
+
// Nothing survived that the user actually needs? Spend the rail on the
|
|
1063
|
+
// priority row instead of an overflow marker. Suppressing the marker here
|
|
1064
|
+
// is deliberate: at these widths its cells are worth more as context %.
|
|
1065
|
+
const priorityRow = this.#priorityRowFor(seg, placed.leftIds, placed.rightIds, topFillWidth);
|
|
1066
|
+
if (priorityRow !== null) return priorityRow;
|
|
942
1067
|
|
|
943
1068
|
const leftGroup = this.#renderStatusGroup(placed.left, "left", separatorDef, bgAnsi, fgAnsi, sepAnsi);
|
|
944
1069
|
const rightGroup = this.#renderStatusGroup(placed.right, "right", separatorDef, bgAnsi, fgAnsi, sepAnsi);
|
|
@@ -1078,6 +1203,17 @@ export class StatusLineComponent implements Component {
|
|
|
1078
1203
|
if (row.length === 0) packedRows.splice(index, 1);
|
|
1079
1204
|
}
|
|
1080
1205
|
|
|
1206
|
+
// Wrapping is supposed to be the lossless path. When even `maxRows` rows
|
|
1207
|
+
// cannot hold everything, the priority row beats a partial rail plus a
|
|
1208
|
+
// count of what is missing.
|
|
1209
|
+
if (dropped > 0) {
|
|
1210
|
+
const priorityRow = this.#priorityRowFor(seg, [], [], topFillWidth);
|
|
1211
|
+
if (priorityRow !== null) {
|
|
1212
|
+
this.#renderedRowsCache = { key: cacheKey, rows: [priorityRow] };
|
|
1213
|
+
return [priorityRow];
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1081
1217
|
// Reserve marker cells on whichever row ends up last, evicting from its
|
|
1082
1218
|
// tail until the cue fits. Never opens a new row: that would contradict
|
|
1083
1219
|
// maxRows. Emptying that row is fine -- the marker then occupies it alone.
|
|
@@ -1161,7 +1161,7 @@ export class CommandController {
|
|
|
1161
1161
|
}
|
|
1162
1162
|
|
|
1163
1163
|
try {
|
|
1164
|
-
await this.ctx.sessionManager.
|
|
1164
|
+
await this.ctx.sessionManager.runExclusiveCwdMoveTransition(async () => {
|
|
1165
1165
|
await this.ctx.sessionManager.flush();
|
|
1166
1166
|
await this.ctx.sessionManager.moveTo(resolvedPath);
|
|
1167
1167
|
setProjectDir(resolvedPath);
|
|
@@ -57,6 +57,17 @@ export const __eventControllerPerfCounters = {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
const COMPLETION_NOTIFY_COMMAND_TIMEOUT_MS = 10_000;
|
|
60
|
+
const TEXTLESS_LENGTH_WARNING =
|
|
61
|
+
'Response ended with stopReason "length" before producing visible text or a tool call. Check the model\'s output and context token limits (maxTokens in models.yml) or lower the reasoning level (/reasoning), then retry.';
|
|
62
|
+
|
|
63
|
+
function isTextlessLengthStop(message: AssistantMessage | undefined): boolean {
|
|
64
|
+
return (
|
|
65
|
+
message?.stopReason === "length" &&
|
|
66
|
+
!message.content.some(
|
|
67
|
+
block => (block.type === "text" && block.text.trim().length > 0) || block.type === "toolCall",
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
60
71
|
|
|
61
72
|
interface CompletionNotifyPayload {
|
|
62
73
|
type: "agent-turn-complete";
|
|
@@ -904,7 +915,7 @@ export class EventController {
|
|
|
904
915
|
}
|
|
905
916
|
}
|
|
906
917
|
|
|
907
|
-
async #handleAgentEnd(
|
|
918
|
+
async #handleAgentEnd(event: Extract<AgentSessionEvent, { type: "agent_end" }>): Promise<void> {
|
|
908
919
|
this.ctx.setWorkingMessage(undefined);
|
|
909
920
|
stopInteractiveActivityIndicator(this.ctx, { foregroundSettled: true });
|
|
910
921
|
if (this.ctx.streamingComponent) {
|
|
@@ -929,6 +940,14 @@ export class EventController {
|
|
|
929
940
|
this.ctx.ui.requestRender();
|
|
930
941
|
this.#scheduleIdleCompaction();
|
|
931
942
|
emitHostStatus("finished");
|
|
943
|
+
const lastAssistant = this.ctx.session.getLastAssistantMessage();
|
|
944
|
+
if (
|
|
945
|
+
event.stopReason !== "cancelled" &&
|
|
946
|
+
event.stopReason !== "maintenance" &&
|
|
947
|
+
isTextlessLengthStop(lastAssistant)
|
|
948
|
+
) {
|
|
949
|
+
this.ctx.showWarning(TEXTLESS_LENGTH_WARNING);
|
|
950
|
+
}
|
|
932
951
|
this.sendCompletionNotification();
|
|
933
952
|
this.ctx.promptSuggestion?.onAgentEnd();
|
|
934
953
|
}
|
|
@@ -1204,13 +1223,20 @@ export class EventController {
|
|
|
1204
1223
|
// errored — those are not "Task complete" events. Mirrors the gate
|
|
1205
1224
|
// already used by #currentContextTokens, #handleMessageEnd, and the
|
|
1206
1225
|
// retry / TTSR / compaction skip paths across agent-session.ts.
|
|
1207
|
-
const last = this.ctx.session.getLastAssistantMessage
|
|
1226
|
+
const last = this.ctx.session.getLastAssistantMessage();
|
|
1208
1227
|
if (last?.stopReason === "aborted" || last?.stopReason === "error") return;
|
|
1209
1228
|
|
|
1210
1229
|
const sessionName = this.ctx.sessionManager.getSessionName();
|
|
1211
|
-
const
|
|
1230
|
+
const textlessLengthStop = isTextlessLengthStop(last);
|
|
1231
|
+
const title = textlessLengthStop
|
|
1232
|
+
? sessionName
|
|
1233
|
+
? `${sessionName}: Output limit reached`
|
|
1234
|
+
: "Output limit reached"
|
|
1235
|
+
: sessionName
|
|
1236
|
+
? `${sessionName}: Complete`
|
|
1237
|
+
: "Complete";
|
|
1212
1238
|
const summary = summaryFromMessage(last, 1000);
|
|
1213
|
-
const body = summary ?? "Complete";
|
|
1239
|
+
const body = textlessLengthStop ? TEXTLESS_LENGTH_WARNING : (summary ?? "Complete");
|
|
1214
1240
|
const sessionManager = this.ctx.sessionManager as { getCwd?: () => string; getSessionId?: () => string };
|
|
1215
1241
|
const payload: CompletionNotifyPayload = {
|
|
1216
1242
|
type: "agent-turn-complete",
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
SYNTHETIC_PROVIDER_ID,
|
|
40
40
|
syntheticNamespaceCollision,
|
|
41
41
|
} from "../../sdk/model-profile-model";
|
|
42
|
+
import { bashBackgroundControlError } from "../../session/fold-coordinator";
|
|
42
43
|
import { createReadonlySessionManager } from "../../session/session-manager";
|
|
43
44
|
import { parseThinkingLevel } from "../../thinking";
|
|
44
45
|
import type { TodoPhase } from "../../tools/todo-write";
|
|
@@ -311,12 +312,11 @@ export class ExtensionUiController {
|
|
|
311
312
|
throw Object.assign(new Error("No retry backoff is pending."), { code: "retry_not_pending" });
|
|
312
313
|
session.retryNow();
|
|
313
314
|
return { retried: true, immediate: true };
|
|
314
|
-
case "bash.background":
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return { backgrounded: true };
|
|
315
|
+
case "bash.background": {
|
|
316
|
+
const outcome = await session.requestForegroundBashBackgroundOutcome("sdk_control");
|
|
317
|
+
if (outcome.status !== "folded") throw bashBackgroundControlError(outcome);
|
|
318
|
+
return { backgrounded: true, jobId: outcome.jobId };
|
|
319
|
+
}
|
|
320
320
|
case "compaction.auto.set":
|
|
321
321
|
session.setAutoCompactionEnabled(input.on === true);
|
|
322
322
|
return { changed: true };
|
|
@@ -591,8 +591,8 @@ export class ExtensionUiController {
|
|
|
591
591
|
this.ctx.session.setFollowUpMode(mode);
|
|
592
592
|
return true;
|
|
593
593
|
}
|
|
594
|
-
if (kind === "
|
|
595
|
-
this.ctx.session.
|
|
594
|
+
if (kind === "tool_interrupt" && (mode === "abort_tools" || mode === "finish_tools")) {
|
|
595
|
+
this.ctx.session.setToolInterruptPolicy(mode);
|
|
596
596
|
return true;
|
|
597
597
|
}
|
|
598
598
|
return false;
|
|
@@ -821,8 +821,8 @@ export class ExtensionUiController {
|
|
|
821
821
|
this.ctx.session.setFollowUpMode(mode);
|
|
822
822
|
return true;
|
|
823
823
|
}
|
|
824
|
-
if (kind === "
|
|
825
|
-
this.ctx.session.
|
|
824
|
+
if (kind === "tool_interrupt" && (mode === "abort_tools" || mode === "finish_tools")) {
|
|
825
|
+
this.ctx.session.setToolInterruptPolicy(mode);
|
|
826
826
|
return true;
|
|
827
827
|
}
|
|
828
828
|
return false;
|
|
@@ -962,8 +962,8 @@ export class ExtensionUiController {
|
|
|
962
962
|
this.ctx.session.setFollowUpMode(mode);
|
|
963
963
|
return true;
|
|
964
964
|
}
|
|
965
|
-
if (kind === "
|
|
966
|
-
this.ctx.session.
|
|
965
|
+
if (kind === "tool_interrupt" && (mode === "abort_tools" || mode === "finish_tools")) {
|
|
966
|
+
this.ctx.session.setToolInterruptPolicy(mode);
|
|
967
967
|
return true;
|
|
968
968
|
}
|
|
969
969
|
return false;
|
|
@@ -973,7 +973,7 @@ export class ExtensionUiController {
|
|
|
973
973
|
getConfigItems: () => ({
|
|
974
974
|
steeringMode: this.ctx.session.steeringMode,
|
|
975
975
|
followUpMode: this.ctx.session.followUpMode,
|
|
976
|
-
|
|
976
|
+
toolInterruptPolicy: this.ctx.session.toolInterruptPolicy,
|
|
977
977
|
}),
|
|
978
978
|
getBranchCandidates: () => this.ctx.sessionManager.getTree(),
|
|
979
979
|
getExtensions: () => this.ctx.session.extensionRunner?.getExtensionPaths() ?? [],
|
|
@@ -420,6 +420,34 @@ export class InputController {
|
|
|
420
420
|
if (this.ctx.cancelPendingSubmission()) {
|
|
421
421
|
return true;
|
|
422
422
|
}
|
|
423
|
+
if (options.processes && this.ctx.session.isBashRunning) {
|
|
424
|
+
this.ctx.session.abortBash();
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
if (options.processes && this.ctx.session.isEvalRunning) {
|
|
428
|
+
this.ctx.session.abortEval();
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
// A queued steer on a live stream is owned by the steering branch below,
|
|
432
|
+
// which silently consumes it and auto-continues. This branch counts the same
|
|
433
|
+
// queue as cancellable work, and a streaming turn always has the indicator
|
|
434
|
+
// mounted, so without this hand-off it short-circuited first and turned the
|
|
435
|
+
// documented steer-on-interrupt into a loud abort that dumped the steer back
|
|
436
|
+
// into the composer. `cancelPendingSubmission()` above still wins: a
|
|
437
|
+
// submission that has not started yet is cancelled before the stream owns
|
|
438
|
+
// the key, and one that has started returns false there by design.
|
|
439
|
+
if (
|
|
440
|
+
options.streaming === true &&
|
|
441
|
+
this.ctx.session.isStreaming &&
|
|
442
|
+
this.ctx.session.hasQueuedSteering &&
|
|
443
|
+
!this.ctx.session.isBashRunning &&
|
|
444
|
+
!this.ctx.session.isEvalRunning &&
|
|
445
|
+
!this.#steerConsumePending
|
|
446
|
+
) {
|
|
447
|
+
this.#steerConsumePending = true;
|
|
448
|
+
void this.#abortInteractive({ silent: true });
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
423
451
|
// Only count queues this handler can actually drain. The aggregate
|
|
424
452
|
// `queuedMessageCount` also counts hidden next-turn context, which
|
|
425
453
|
// `restoreQueuedMessagesToEditor()` never returns and which survives turn
|
|
@@ -1142,7 +1170,7 @@ export class InputController {
|
|
|
1142
1170
|
const promptOptions =
|
|
1143
1171
|
streamingBehavior === "followUp"
|
|
1144
1172
|
? { streamingBehavior, images, followUpQueuePolicy: "sequential" as const }
|
|
1145
|
-
: { streamingBehavior, images };
|
|
1173
|
+
: { streamingBehavior, images, steerQueuePolicy: "sequential" as const };
|
|
1146
1174
|
await this.ctx.withLocalSubmission(text, () => this.ctx.session.prompt(text, promptOptions), {
|
|
1147
1175
|
imageCount: images?.length ?? 0,
|
|
1148
1176
|
});
|
|
@@ -1655,7 +1683,7 @@ export class InputController {
|
|
|
1655
1683
|
},
|
|
1656
1684
|
streamingBehavior === "followUp"
|
|
1657
1685
|
? { streamingBehavior, followUpQueuePolicy: "sequential" }
|
|
1658
|
-
: { streamingBehavior },
|
|
1686
|
+
: { streamingBehavior, steerQueuePolicy: "sequential" },
|
|
1659
1687
|
);
|
|
1660
1688
|
}
|
|
1661
1689
|
if (this.ctx.session.isStreaming) {
|