@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
|
@@ -4,7 +4,10 @@ import {
|
|
|
4
4
|
type Api,
|
|
5
5
|
type AuthCredentialSelector,
|
|
6
6
|
type CredentialRemovalTarget,
|
|
7
|
+
isSqliteCorruptionError,
|
|
8
|
+
isSqliteError,
|
|
7
9
|
type Model,
|
|
10
|
+
OAuthCredentialSelectorError,
|
|
8
11
|
resolveOAuthStorageProvider,
|
|
9
12
|
} from "@gajae-code/ai/core";
|
|
10
13
|
import { getOAuthProviders } from "@gajae-code/ai/utils/oauth";
|
|
@@ -106,6 +109,7 @@ import { TelegramDaemonController } from "../../sdk/bus/telegram-daemon-control"
|
|
|
106
109
|
import { runTelegramSetup, type TelegramSetupPreflight } from "../../sdk/bus/telegram-setup";
|
|
107
110
|
import { clearPersistentPinForRemovedRows } from "../../session/account-inventory";
|
|
108
111
|
import type { DefaultFallbackRuntimeState } from "../../session/agent-session";
|
|
112
|
+
import { CREDENTIAL_STORE_UNREADABLE_MESSAGE } from "../../session/credential-store-errors";
|
|
109
113
|
import { type SessionInfo, SessionManager } from "../../session/session-manager";
|
|
110
114
|
import { getTreeForInternalRead } from "../../session/session-manager-internal";
|
|
111
115
|
import { FileSessionStorage } from "../../session/session-storage";
|
|
@@ -125,6 +129,12 @@ import {
|
|
|
125
129
|
logCredentialAutoImportFailures,
|
|
126
130
|
runExternalCredentialAutoImport,
|
|
127
131
|
} from "../../setup/credential-auto-import";
|
|
132
|
+
|
|
133
|
+
function credentialStoreOperationError(action: "Login" | "Logout", error: unknown): string {
|
|
134
|
+
const code = error && typeof error === "object" ? (error as { code?: unknown }).code : undefined;
|
|
135
|
+
return `${action} failed: credential store operation failed${typeof code === "string" ? ` (${code})` : ""}.`;
|
|
136
|
+
}
|
|
137
|
+
|
|
128
138
|
import {
|
|
129
139
|
filterAutoImportOAuthCredentials,
|
|
130
140
|
formatDiscoverySummary,
|
|
@@ -172,6 +182,7 @@ import {
|
|
|
172
182
|
FrictionlessOnboardingSelectorComponent,
|
|
173
183
|
type FrictionlessOnboardingStage,
|
|
174
184
|
getFrictionlessOnboardingCopy,
|
|
185
|
+
InterfaceLanguageSelectorComponent,
|
|
175
186
|
} from "../components/frictionless-onboarding-selector";
|
|
176
187
|
import type { PetMode } from "../components/gajae-pet-widget";
|
|
177
188
|
import { HistorySearchComponent } from "../components/history-search";
|
|
@@ -216,6 +227,7 @@ import type { SessionObserverRegistry } from "../session-observer-registry";
|
|
|
216
227
|
import { buildOAuthLoginAnchor, createOAuthUrlCopyLease } from "../shared/oauth-url-copy";
|
|
217
228
|
import type { TasksAggregator } from "../tasks-aggregator";
|
|
218
229
|
import type { TranscriptItemRegistry } from "../transcript-item-registry";
|
|
230
|
+
import { resolveExplicitUiLanguage, type UiLanguage } from "../ui-language";
|
|
219
231
|
import { acquireResumeProgressLease, type ResumeProgressLease } from "../utils/ui-helpers";
|
|
220
232
|
|
|
221
233
|
const CALLBACK_SERVER_PROVIDERS = new Set<string>([
|
|
@@ -1366,11 +1378,16 @@ export class SelectorController {
|
|
|
1366
1378
|
async #refreshOAuthProviderAuthState(): Promise<void> {
|
|
1367
1379
|
const oauthProviders = getOAuthProviders();
|
|
1368
1380
|
await Promise.all(
|
|
1369
|
-
oauthProviders.map(provider =>
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1381
|
+
oauthProviders.map(async provider => {
|
|
1382
|
+
try {
|
|
1383
|
+
await this.ctx.session.modelRegistry.getApiKeyForProvider(
|
|
1384
|
+
provider.id,
|
|
1385
|
+
this.ctx.session.credentialSessionId,
|
|
1386
|
+
);
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
if (isSqliteError(error)) throw error;
|
|
1389
|
+
}
|
|
1390
|
+
}),
|
|
1374
1391
|
);
|
|
1375
1392
|
}
|
|
1376
1393
|
/**
|
|
@@ -1469,11 +1486,57 @@ export class SelectorController {
|
|
|
1469
1486
|
|
|
1470
1487
|
async showFrictionlessOnboarding(): Promise<void> {
|
|
1471
1488
|
const agentDir = this.ctx.session.getSessionAgentDir();
|
|
1489
|
+
const languagePreference = resolveExplicitUiLanguage(
|
|
1490
|
+
this.ctx.settings.has("ui.language") ? this.ctx.settings.get("ui.language") : undefined,
|
|
1491
|
+
process.env.GJC_UI_LANGUAGE,
|
|
1492
|
+
);
|
|
1493
|
+
let selectedLanguage: UiLanguage = languagePreference.language;
|
|
1494
|
+
if (!languagePreference.hasPreference && this.ctx.settings.canPersistDurableConfig()) {
|
|
1495
|
+
const decision = Promise.withResolvers<UiLanguage | undefined>();
|
|
1496
|
+
let settled = false;
|
|
1497
|
+
let unregisterStop: (() => void) | undefined;
|
|
1498
|
+
const finish = (language: UiLanguage | undefined): void => {
|
|
1499
|
+
if (settled) return;
|
|
1500
|
+
settled = true;
|
|
1501
|
+
unregisterStop?.();
|
|
1502
|
+
decision.resolve(language);
|
|
1503
|
+
};
|
|
1504
|
+
this.showSelector(done => {
|
|
1505
|
+
unregisterStop = this.ctx.onStop(() => {
|
|
1506
|
+
done();
|
|
1507
|
+
finish(undefined);
|
|
1508
|
+
});
|
|
1509
|
+
const selector = new InterfaceLanguageSelectorComponent(
|
|
1510
|
+
language => {
|
|
1511
|
+
done();
|
|
1512
|
+
finish(language);
|
|
1513
|
+
},
|
|
1514
|
+
() => {
|
|
1515
|
+
done();
|
|
1516
|
+
finish(undefined);
|
|
1517
|
+
},
|
|
1518
|
+
);
|
|
1519
|
+
return { component: selector, focus: selector };
|
|
1520
|
+
});
|
|
1521
|
+
selectedLanguage = (await decision.promise) ?? "en";
|
|
1522
|
+
try {
|
|
1523
|
+
await this.ctx.settings.commitAtomicBatch([{ path: "ui.language", op: "set", value: selectedLanguage }]);
|
|
1524
|
+
} catch (error) {
|
|
1525
|
+
this.ctx.showError(
|
|
1526
|
+
error instanceof Error
|
|
1527
|
+
? `${error.message}; continuing in English.`
|
|
1528
|
+
: "Cannot save the interface language; continuing in English.",
|
|
1529
|
+
);
|
|
1530
|
+
selectedLanguage = "en";
|
|
1531
|
+
}
|
|
1532
|
+
} else if (!languagePreference.hasPreference) {
|
|
1533
|
+
this.ctx.showError("Cannot save the interface language; continuing in English.");
|
|
1534
|
+
}
|
|
1472
1535
|
const presence = await discoverOnboardingRootPresence();
|
|
1473
1536
|
// An explicit `/language` (or settings) selection outranks locale and transcript evidence.
|
|
1474
1537
|
const profileOptions = {
|
|
1475
1538
|
osLocale: Intl.DateTimeFormat().resolvedOptions().locale,
|
|
1476
|
-
|
|
1539
|
+
preferredLanguage: selectedLanguage,
|
|
1477
1540
|
};
|
|
1478
1541
|
const initialProfile = deriveOnboardingProfile([], profileOptions);
|
|
1479
1542
|
|
|
@@ -2181,8 +2244,8 @@ export class SelectorController {
|
|
|
2181
2244
|
case "followUpMode":
|
|
2182
2245
|
this.ctx.session.setFollowUpMode(value as "all" | "one-at-a-time");
|
|
2183
2246
|
break;
|
|
2184
|
-
case "
|
|
2185
|
-
this.ctx.session.
|
|
2247
|
+
case "toolInterruptPolicy":
|
|
2248
|
+
this.ctx.session.setToolInterruptPolicy(value as "abort_tools" | "finish_tools");
|
|
2186
2249
|
break;
|
|
2187
2250
|
case "thinkingLevel":
|
|
2188
2251
|
case "defaultThinkingLevel":
|
|
@@ -3557,7 +3620,8 @@ export class SelectorController {
|
|
|
3557
3620
|
}
|
|
3558
3621
|
this.ctx.ui.requestRender();
|
|
3559
3622
|
} catch (error: unknown) {
|
|
3560
|
-
this.ctx.showError(
|
|
3623
|
+
if (isSqliteCorruptionError(error)) this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
|
|
3624
|
+
else this.ctx.showError(`Login failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3561
3625
|
} finally {
|
|
3562
3626
|
oauthUrlCopyLease.release();
|
|
3563
3627
|
if (useManualInput) {
|
|
@@ -3599,7 +3663,8 @@ export class SelectorController {
|
|
|
3599
3663
|
`Successfully removed ${result.ids.length} stored credential${result.ids.length === 1 ? "" : "s"} from ${providerId}.`,
|
|
3600
3664
|
);
|
|
3601
3665
|
} catch (error: unknown) {
|
|
3602
|
-
this.ctx.showError(
|
|
3666
|
+
if (isSqliteCorruptionError(error)) this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
|
|
3667
|
+
else this.ctx.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3603
3668
|
}
|
|
3604
3669
|
}
|
|
3605
3670
|
|
|
@@ -3616,75 +3681,96 @@ export class SelectorController {
|
|
|
3616
3681
|
return;
|
|
3617
3682
|
}
|
|
3618
3683
|
const authStorage = this.ctx.session.modelRegistry.authStorage;
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
done();
|
|
3639
|
-
this.ctx.ui.requestRender();
|
|
3640
|
-
},
|
|
3641
|
-
{
|
|
3642
|
-
accountProviderId: selectedProviderId,
|
|
3643
|
-
onAccountSelect: async (selectorValue: AuthCredentialSelector) => {
|
|
3644
|
-
await this.ctx.session.setCredentialPin(selectedProviderId, selectorValue);
|
|
3645
|
-
selector.stopValidation();
|
|
3646
|
-
done();
|
|
3647
|
-
this.ctx.showStatus(`Pinned OAuth account for ${selectedProviderId} to this session.`);
|
|
3648
|
-
},
|
|
3649
|
-
onAutoSelect: async () => {
|
|
3650
|
-
await this.ctx.session.setCredentialAuto(selectedProviderId);
|
|
3651
|
-
selector.stopValidation();
|
|
3652
|
-
done();
|
|
3653
|
-
this.ctx.showStatus(`Using AUTO (ranked) OAuth accounts for ${selectedProviderId}.`);
|
|
3654
|
-
},
|
|
3655
|
-
onAddAccount: async () => {
|
|
3684
|
+
try {
|
|
3685
|
+
const hasOAuthInventory = authStorage
|
|
3686
|
+
.listCredentialInventory(selectedProviderId)
|
|
3687
|
+
.some(row => row.provider === selectedProviderId && row.credentialKind === "oauth");
|
|
3688
|
+
const hasRemovableCredential = authStorage
|
|
3689
|
+
.listCredentialRemovalTargets(selectedProviderId)
|
|
3690
|
+
.some(target => target.provider === selectedProviderId);
|
|
3691
|
+
if (mode === "logout" && hasOAuthInventory && !hasRemovableCredential) {
|
|
3692
|
+
await this.#handleLogout(selectedProviderId);
|
|
3693
|
+
return;
|
|
3694
|
+
}
|
|
3695
|
+
if (hasOAuthInventory && !options?.manualCode) {
|
|
3696
|
+
this.showSelector(done => {
|
|
3697
|
+
let selector: OAuthSelectorComponent;
|
|
3698
|
+
selector = new OAuthSelectorComponent(
|
|
3699
|
+
mode,
|
|
3700
|
+
authStorage,
|
|
3701
|
+
() => undefined,
|
|
3702
|
+
() => {
|
|
3656
3703
|
selector.stopValidation();
|
|
3657
3704
|
done();
|
|
3658
|
-
|
|
3705
|
+
this.ctx.ui.requestRender();
|
|
3659
3706
|
},
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3707
|
+
{
|
|
3708
|
+
accountProviderId: selectedProviderId,
|
|
3709
|
+
onAccountSelect: async (selectorValue: AuthCredentialSelector) => {
|
|
3710
|
+
try {
|
|
3711
|
+
await this.ctx.session.setCredentialPin(selectedProviderId, selectorValue);
|
|
3712
|
+
} catch (error: unknown) {
|
|
3713
|
+
if (!(error instanceof OAuthCredentialSelectorError)) throw error;
|
|
3714
|
+
this.ctx.showError(error.message);
|
|
3715
|
+
return;
|
|
3716
|
+
}
|
|
3717
|
+
selector.stopValidation();
|
|
3718
|
+
done();
|
|
3719
|
+
this.ctx.showStatus(`Pinned OAuth account for ${selectedProviderId} to this session.`);
|
|
3720
|
+
},
|
|
3721
|
+
onAutoSelect: async () => {
|
|
3722
|
+
await this.ctx.session.setCredentialAuto(selectedProviderId);
|
|
3723
|
+
selector.stopValidation();
|
|
3724
|
+
done();
|
|
3725
|
+
this.ctx.showStatus(`Using AUTO (ranked) OAuth accounts for ${selectedProviderId}.`);
|
|
3726
|
+
},
|
|
3727
|
+
onAddAccount: async () => {
|
|
3728
|
+
selector.stopValidation();
|
|
3729
|
+
done();
|
|
3730
|
+
await this.#handleOAuthLogin(selectedProviderId, options);
|
|
3731
|
+
},
|
|
3732
|
+
onAccountRemove: async targets => {
|
|
3733
|
+
await this.#handleLogout(selectedProviderId, targets);
|
|
3734
|
+
selector.stopValidation();
|
|
3735
|
+
done();
|
|
3736
|
+
},
|
|
3664
3737
|
},
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3738
|
+
);
|
|
3739
|
+
return { component: selector, focus: selector };
|
|
3740
|
+
});
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
if (mode === "login") {
|
|
3744
|
+
await this.#handleOAuthLogin(selectedProviderId, options);
|
|
3745
|
+
return;
|
|
3746
|
+
}
|
|
3747
|
+
// mode === "logout" with no OAuth accounts: remove stored api-key credentials directly.
|
|
3748
|
+
await this.#handleLogout(selectedProviderId);
|
|
3669
3749
|
return;
|
|
3670
|
-
}
|
|
3671
|
-
|
|
3672
|
-
|
|
3750
|
+
} catch (error: unknown) {
|
|
3751
|
+
if (isSqliteCorruptionError(error)) this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
|
|
3752
|
+
else if (isSqliteError(error))
|
|
3753
|
+
this.ctx.showError(credentialStoreOperationError(mode === "login" ? "Login" : "Logout", error));
|
|
3754
|
+
else throw error;
|
|
3673
3755
|
return;
|
|
3674
3756
|
}
|
|
3675
|
-
// mode === "logout" with no OAuth accounts: remove stored api-key credentials directly.
|
|
3676
|
-
await this.#handleLogout(selectedProviderId);
|
|
3677
|
-
return;
|
|
3678
3757
|
}
|
|
3679
3758
|
|
|
3680
3759
|
if (mode === "logout") {
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3760
|
+
try {
|
|
3761
|
+
await this.#refreshOAuthProviderAuthState();
|
|
3762
|
+
const oauthProviders = getOAuthProviders();
|
|
3763
|
+
const loggedInProviders = oauthProviders.filter(provider =>
|
|
3764
|
+
this.ctx.session.modelRegistry.authStorage.hasAuth(provider.id),
|
|
3765
|
+
);
|
|
3766
|
+
if (loggedInProviders.length === 0) {
|
|
3767
|
+
this.ctx.showStatus("No OAuth providers logged in. Use /login first.");
|
|
3768
|
+
return;
|
|
3769
|
+
}
|
|
3770
|
+
} catch (error: unknown) {
|
|
3771
|
+
if (isSqliteCorruptionError(error)) this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
|
|
3772
|
+
else if (isSqliteError(error)) this.ctx.showError(credentialStoreOperationError("Logout", error));
|
|
3773
|
+
else throw error;
|
|
3688
3774
|
return;
|
|
3689
3775
|
}
|
|
3690
3776
|
}
|
|
@@ -3807,37 +3893,53 @@ export class SelectorController {
|
|
|
3807
3893
|
}
|
|
3808
3894
|
}
|
|
3809
3895
|
}
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
() => {
|
|
3821
|
-
selector.stopValidation();
|
|
3822
|
-
done();
|
|
3823
|
-
this.ctx.ui.requestLayoutRender("oauth-selector-close");
|
|
3824
|
-
},
|
|
3825
|
-
{
|
|
3826
|
-
validateAuth: async (selectedProviderId: string) => {
|
|
3827
|
-
const apiKey = await this.ctx.session.modelRegistry.getApiKeyForProvider(
|
|
3828
|
-
selectedProviderId,
|
|
3829
|
-
this.ctx.session.credentialSessionId,
|
|
3830
|
-
);
|
|
3831
|
-
return !!apiKey;
|
|
3896
|
+
try {
|
|
3897
|
+
this.showSelector(done => {
|
|
3898
|
+
let selector: OAuthSelectorComponent;
|
|
3899
|
+
selector = new OAuthSelectorComponent(
|
|
3900
|
+
mode,
|
|
3901
|
+
this.ctx.session.modelRegistry.authStorage,
|
|
3902
|
+
async (selectedProviderId: string) => {
|
|
3903
|
+
selector.stopValidation();
|
|
3904
|
+
done();
|
|
3905
|
+
await this.showOAuthSelector(mode, selectedProviderId);
|
|
3832
3906
|
},
|
|
3833
|
-
|
|
3834
|
-
|
|
3907
|
+
() => {
|
|
3908
|
+
selector.stopValidation();
|
|
3909
|
+
done();
|
|
3910
|
+
this.ctx.ui.requestLayoutRender("oauth-selector-close");
|
|
3835
3911
|
},
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3912
|
+
{
|
|
3913
|
+
validateAuth: async (selectedProviderId: string) => {
|
|
3914
|
+
const apiKey = await this.ctx.session.modelRegistry.getApiKeyForProvider(
|
|
3915
|
+
selectedProviderId,
|
|
3916
|
+
this.ctx.session.credentialSessionId,
|
|
3917
|
+
);
|
|
3918
|
+
return !!apiKey;
|
|
3919
|
+
},
|
|
3920
|
+
onValidationError: error => {
|
|
3921
|
+
if (!isSqliteError(error)) return false;
|
|
3922
|
+
selector.stopValidation();
|
|
3923
|
+
done();
|
|
3924
|
+
this.ctx.showError(
|
|
3925
|
+
isSqliteCorruptionError(error)
|
|
3926
|
+
? CREDENTIAL_STORE_UNREADABLE_MESSAGE
|
|
3927
|
+
: credentialStoreOperationError("Login", error),
|
|
3928
|
+
);
|
|
3929
|
+
return true;
|
|
3930
|
+
},
|
|
3931
|
+
requestRender: () => {
|
|
3932
|
+
this.ctx.ui.requestLayoutRender("oauth-selector-spinner");
|
|
3933
|
+
},
|
|
3934
|
+
externalCredentialCandidates,
|
|
3935
|
+
},
|
|
3936
|
+
);
|
|
3937
|
+
return { component: selector, focus: selector };
|
|
3938
|
+
});
|
|
3939
|
+
} catch (error: unknown) {
|
|
3940
|
+
if (!isSqliteCorruptionError(error)) throw error;
|
|
3941
|
+
this.ctx.showError(CREDENTIAL_STORE_UNREADABLE_MESSAGE);
|
|
3942
|
+
}
|
|
3841
3943
|
}
|
|
3842
3944
|
|
|
3843
3945
|
showDebugSelector(): void {
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
starReminderLaunchGate,
|
|
46
46
|
} from "../reminders/star-reminder";
|
|
47
47
|
import type { NotificationSessionReconcileResult, NotificationSessionStatus } from "../sdk/bus/session-control";
|
|
48
|
-
import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
|
|
48
|
+
import type { AgentSession, AgentSessionEvent, AsyncJobSnapshotItem } from "../session/agent-session";
|
|
49
49
|
import type { HistoryStorage } from "../session/history-storage";
|
|
50
50
|
import type { SessionContext, SessionManager } from "../session/session-manager";
|
|
51
51
|
import { getRecentSessions, getSessionMessageEntryId } from "../session/session-manager";
|
|
@@ -186,18 +186,42 @@ export function getComposerPlaceholder(
|
|
|
186
186
|
return buildComposerPlaceholder(keybindings, context, options);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
/** Running background work split into the three kinds the activity indicator names. */
|
|
190
|
+
export interface BackgroundActivityTally {
|
|
191
|
+
subagents: number;
|
|
192
|
+
backgroundBash: number;
|
|
193
|
+
monitors: number;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Classify running jobs for the activity indicator using the three locked
|
|
198
|
+
* predicates: subagents are `task` jobs carrying subagent metadata, monitors
|
|
199
|
+
* are flagged by `metadata.monitor`, and background bash is a `bash` job that
|
|
200
|
+
* is not a monitor. A running job matching none of them (a task job without
|
|
201
|
+
* subagent metadata) is outside this indicator and is not counted.
|
|
202
|
+
*/
|
|
203
|
+
export function tallyBackgroundActivity(running: readonly AsyncJobSnapshotItem[]): BackgroundActivityTally {
|
|
204
|
+
const tally: BackgroundActivityTally = { subagents: 0, backgroundBash: 0, monitors: 0 };
|
|
205
|
+
for (const item of running) {
|
|
206
|
+
if (item.type === "task" && item.metadata?.subagent !== undefined) tally.subagents += 1;
|
|
207
|
+
else if (item.metadata?.monitor === true) tally.monitors += 1;
|
|
208
|
+
else if (item.type === "bash" && item.metadata?.backgrounded === true) tally.backgroundBash += 1;
|
|
209
|
+
}
|
|
210
|
+
return tally;
|
|
211
|
+
}
|
|
212
|
+
|
|
189
213
|
export function resolveActivityIndicatorMessage(
|
|
190
214
|
foregroundActive: boolean,
|
|
191
|
-
|
|
215
|
+
activity: BackgroundActivityTally,
|
|
192
216
|
foregroundMessage: string,
|
|
193
217
|
): string | undefined {
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
return
|
|
218
|
+
const parts: string[] = [];
|
|
219
|
+
if (activity.subagents > 0) parts.push(`${activity.subagents} subagent${activity.subagents === 1 ? "" : "s"}`);
|
|
220
|
+
if (activity.backgroundBash > 0) parts.push(`${activity.backgroundBash} background bash`);
|
|
221
|
+
if (activity.monitors > 0) parts.push(`${activity.monitors} monitor${activity.monitors === 1 ? "" : "s"}`);
|
|
222
|
+
|
|
223
|
+
if (foregroundActive) return parts.length === 0 ? foregroundMessage : `${foregroundMessage} · ${parts.join(", ")}`;
|
|
224
|
+
return parts.length === 0 ? undefined : `Background: ${parts.join(", ")}…`;
|
|
201
225
|
}
|
|
202
226
|
const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;
|
|
203
227
|
const COMPOSER_RIGHT_GUTTER_WIDTH = 1;
|
|
@@ -1926,8 +1950,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1926
1950
|
return main && dim ? { main, dim } : undefined;
|
|
1927
1951
|
}
|
|
1928
1952
|
|
|
1929
|
-
#
|
|
1930
|
-
return this.session.getAsyncJobSnapshot()?.running
|
|
1953
|
+
#activeBackgroundActivity(): BackgroundActivityTally {
|
|
1954
|
+
return tallyBackgroundActivity(this.session.getAsyncJobSnapshot()?.running ?? []);
|
|
1931
1955
|
}
|
|
1932
1956
|
|
|
1933
1957
|
#stopLoadingAnimation(): void {
|
|
@@ -1946,7 +1970,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1946
1970
|
}
|
|
1947
1971
|
const message = resolveActivityIndicatorMessage(
|
|
1948
1972
|
foregroundActive,
|
|
1949
|
-
this.#
|
|
1973
|
+
this.#activeBackgroundActivity(),
|
|
1950
1974
|
this.#pendingWorkingMessage ?? this.#defaultWorkingMessage,
|
|
1951
1975
|
);
|
|
1952
1976
|
if (!message) {
|
package/src/modes/print-mode.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* - `gjc -p "prompt"` - text output
|
|
6
6
|
* - `gjc --mode json "prompt"` - JSON event stream
|
|
7
7
|
*/
|
|
8
|
+
import type { AgentMessage } from "@gajae-code/agent-core";
|
|
8
9
|
import { type AssistantMessage, type ImageContent, isContextOverflow } from "@gajae-code/ai/core";
|
|
9
10
|
import { isKnownSinkPeerClosedError, logger, sanitizeText } from "@gajae-code/utils";
|
|
10
11
|
import { loadSlashCommands } from "../extensibility/slash-commands";
|
|
@@ -46,6 +47,48 @@ export interface PrintModeOptions {
|
|
|
46
47
|
*/
|
|
47
48
|
export const CONTEXT_OVERFLOW_EXIT_CODE = 78;
|
|
48
49
|
|
|
50
|
+
const EMPTY_TURN_ERROR = {
|
|
51
|
+
code: "empty_response",
|
|
52
|
+
message: "Turn completed without assistant text.",
|
|
53
|
+
} as const;
|
|
54
|
+
|
|
55
|
+
export type PrintTerminalDisposition =
|
|
56
|
+
| { status: "success"; assistantText: true }
|
|
57
|
+
| { status: "failure"; error: typeof EMPTY_TURN_ERROR | { code: "assistant_error"; message: string } };
|
|
58
|
+
|
|
59
|
+
function assistantText(message: AssistantMessage): string {
|
|
60
|
+
return message.content
|
|
61
|
+
.filter((content): content is { type: "text"; text: string } => content.type === "text")
|
|
62
|
+
.map(content => content.text)
|
|
63
|
+
.join("");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resolvePrintTerminalDisposition(
|
|
67
|
+
messages: readonly AgentMessage[],
|
|
68
|
+
submittedPrompt: boolean,
|
|
69
|
+
turnStartIndex?: number,
|
|
70
|
+
): PrintTerminalDisposition | undefined {
|
|
71
|
+
if (!submittedPrompt) return undefined;
|
|
72
|
+
const turnMessages = turnStartIndex === undefined ? messages : messages.slice(turnStartIndex);
|
|
73
|
+
const lastAssistant = turnMessages.findLast((message): message is AssistantMessage => message.role === "assistant");
|
|
74
|
+
if (!lastAssistant) return { status: "failure", error: EMPTY_TURN_ERROR };
|
|
75
|
+
if (
|
|
76
|
+
(lastAssistant.stopReason === "error" || lastAssistant.stopReason === "aborted") &&
|
|
77
|
+
!isSilentAbort(lastAssistant.errorMessage)
|
|
78
|
+
) {
|
|
79
|
+
return {
|
|
80
|
+
status: "failure",
|
|
81
|
+
error: {
|
|
82
|
+
code: "assistant_error",
|
|
83
|
+
message: sanitizeText(lastAssistant.errorMessage || `Request ${lastAssistant.stopReason}`),
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (isSilentAbort(lastAssistant.errorMessage)) return undefined;
|
|
88
|
+
if (assistantText(lastAssistant).trim().length === 0) return { status: "failure", error: EMPTY_TURN_ERROR };
|
|
89
|
+
return { status: "success", assistantText: true };
|
|
90
|
+
}
|
|
91
|
+
|
|
49
92
|
/**
|
|
50
93
|
* Build an actionable stderr diagnostic for a terminal context-overflow error in
|
|
51
94
|
* text mode. The raw provider message is preserved (appended) for debugging, but
|
|
@@ -201,6 +244,8 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
|
|
|
201
244
|
let unsubscribe: (() => void) | undefined;
|
|
202
245
|
let submittedPrompt = false;
|
|
203
246
|
let consumedCommand = false;
|
|
247
|
+
let jsonFailureObserved = false;
|
|
248
|
+
let lastPromptMessageStart: number | undefined;
|
|
204
249
|
|
|
205
250
|
try {
|
|
206
251
|
for (const warning of new Set(session.configWarnings)) {
|
|
@@ -237,6 +282,14 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
|
|
|
237
282
|
// when it must render the JSON event stream.
|
|
238
283
|
if (mode === "json") {
|
|
239
284
|
unsubscribe = session.subscribe(event => {
|
|
285
|
+
if (
|
|
286
|
+
event.type === "agent_failed" ||
|
|
287
|
+
(event.type === "message_end" &&
|
|
288
|
+
event.message.role === "assistant" &&
|
|
289
|
+
(event.message.stopReason === "error" || event.message.stopReason === "aborted"))
|
|
290
|
+
) {
|
|
291
|
+
jsonFailureObserved = true;
|
|
292
|
+
}
|
|
240
293
|
stdout.write(`${JSON.stringify(event)}\n`);
|
|
241
294
|
});
|
|
242
295
|
}
|
|
@@ -265,6 +318,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
|
|
|
265
318
|
if (command !== false) text = command.prompt;
|
|
266
319
|
}
|
|
267
320
|
submittedPrompt = true;
|
|
321
|
+
lastPromptMessageStart = session.state.messages.length;
|
|
268
322
|
await session.prompt(text, images ? { images } : undefined);
|
|
269
323
|
};
|
|
270
324
|
|
|
@@ -277,6 +331,16 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
|
|
|
277
331
|
await logger.time("print:prompt:next", () => submitInput(message));
|
|
278
332
|
}
|
|
279
333
|
|
|
334
|
+
const terminal = resolvePrintTerminalDisposition(session.state.messages, submittedPrompt, lastPromptMessageStart);
|
|
335
|
+
if (terminal?.status === "failure") {
|
|
336
|
+
if (!options.suppressProcessExit) process.exitCode = 1;
|
|
337
|
+
if (mode === "json" && !jsonFailureObserved) {
|
|
338
|
+
stdout.write(`${JSON.stringify({ type: "agent_failed", error: terminal.error })}\n`);
|
|
339
|
+
} else if (mode === "text" && terminal.error.code === EMPTY_TURN_ERROR.code) {
|
|
340
|
+
await writeStderrAndQuiesce(`${terminal.error.message}\n`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
280
344
|
// In text mode, output final response.
|
|
281
345
|
if (mode === "text" && (!consumedCommand || submittedPrompt)) {
|
|
282
346
|
const lastMessage = session.state.messages.findLast(message => message.role === "assistant");
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
CombinedAutocompleteProvider,
|
|
7
7
|
extractSlashCommandTokenPrefix,
|
|
8
8
|
getSlashCommandMatchRank,
|
|
9
|
+
hasInlineCodeDelimiter,
|
|
10
|
+
isInsideInlineCodeSpan,
|
|
9
11
|
normalizeFuzzyText,
|
|
10
12
|
type SlashCommand,
|
|
11
13
|
} from "@gajae-code/tui";
|
|
@@ -140,7 +142,7 @@ function withoutSkillCommandSuggestions(
|
|
|
140
142
|
suggestions: { items: AutocompleteItem[]; prefix: string } | null,
|
|
141
143
|
): { items: AutocompleteItem[]; prefix: string } | null {
|
|
142
144
|
if (!suggestions) return null;
|
|
143
|
-
const items = suggestions.items.filter(item => !item.value.startsWith("skill:"));
|
|
145
|
+
const items = suggestions.items.filter(item => !item.value.toLowerCase().startsWith("skill:"));
|
|
144
146
|
return items.length > 0 ? { ...suggestions, items } : null;
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -160,6 +162,21 @@ function getSlashTokenPrefix(textBeforeCursor: string): string | null {
|
|
|
160
162
|
return extractSlashCommandTokenPrefix(textBeforeCursor);
|
|
161
163
|
}
|
|
162
164
|
|
|
165
|
+
function getRawSlashTokenPrefix(textBeforeCursor: string): string | null {
|
|
166
|
+
return textBeforeCursor.match(/(?:^|\s)(\/[^\s]*)$/)?.[1] ?? null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function isSlashTokenInsideInlineCodeSpan(
|
|
170
|
+
lines: string[],
|
|
171
|
+
cursorLine: number,
|
|
172
|
+
textBeforeCursor: string,
|
|
173
|
+
prefix: string,
|
|
174
|
+
): boolean {
|
|
175
|
+
const tokenStart = textBeforeCursor.length - prefix.length;
|
|
176
|
+
const textBeforeToken = [...lines.slice(0, cursorLine), textBeforeCursor.slice(0, tokenStart)].join("\n");
|
|
177
|
+
return isInsideInlineCodeSpan(textBeforeToken);
|
|
178
|
+
}
|
|
179
|
+
|
|
163
180
|
export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
164
181
|
#baseProvider: CombinedAutocompleteProvider;
|
|
165
182
|
#actions: PromptActionDefinition[];
|
|
@@ -211,8 +228,13 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
211
228
|
}
|
|
212
229
|
}
|
|
213
230
|
|
|
231
|
+
const rawSlashPrefix = getRawSlashTokenPrefix(textBeforeCursor);
|
|
214
232
|
const slashPrefix = getSlashTokenPrefix(textBeforeCursor);
|
|
215
|
-
|
|
233
|
+
const slashTokenInsideCode =
|
|
234
|
+
rawSlashPrefix !== null &&
|
|
235
|
+
(hasInlineCodeDelimiter(rawSlashPrefix) ||
|
|
236
|
+
isSlashTokenInsideInlineCodeSpan(lines, cursorLine, textBeforeCursor, rawSlashPrefix));
|
|
237
|
+
if (slashPrefix && !slashTokenInsideCode) {
|
|
216
238
|
const baseSuggestions = withoutSkillCommandSuggestions(
|
|
217
239
|
await this.#baseProvider.getSuggestions(lines, cursorLine, cursorCol),
|
|
218
240
|
);
|
|
@@ -225,6 +247,9 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
225
247
|
this.#commands,
|
|
226
248
|
);
|
|
227
249
|
}
|
|
250
|
+
if (rawSlashPrefix && slashTokenInsideCode) {
|
|
251
|
+
return withoutSkillCommandSuggestions(await this.#baseProvider.getSuggestions(lines, cursorLine, cursorCol));
|
|
252
|
+
}
|
|
228
253
|
|
|
229
254
|
if (!isSettingsInitialized() || settings.get("emojiAutocomplete")) {
|
|
230
255
|
const emojiSuggestions = getEmojiSuggestions(textBeforeCursor);
|
|
@@ -282,12 +307,13 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
282
307
|
const currentLine = lines[cursorLine] || "";
|
|
283
308
|
const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
|
|
284
309
|
const afterCursor = currentLine.slice(cursorCol);
|
|
310
|
+
const separator = afterCursor.length === 0 ? " " : "";
|
|
285
311
|
const newLines = [...lines];
|
|
286
|
-
newLines[cursorLine] = `${beforePrefix}/${item.value}
|
|
312
|
+
newLines[cursorLine] = `${beforePrefix}/${item.value}${separator}${afterCursor}`;
|
|
287
313
|
return {
|
|
288
314
|
lines: newLines,
|
|
289
315
|
cursorLine,
|
|
290
|
-
cursorCol: beforePrefix.length + item.value.length +
|
|
316
|
+
cursorCol: beforePrefix.length + item.value.length + 1 + separator.length,
|
|
291
317
|
};
|
|
292
318
|
}
|
|
293
319
|
return this.#baseProvider.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
|