@gajae-code/coding-agent 0.11.10 → 0.12.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 +86 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-profile-contract.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +86 -3
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
- package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +2 -1
- package/dist/types/gjc-runtime/team-store.d.ts +12 -0
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -1
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/manager.d.ts +8 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +7 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +18 -2
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +23 -3
- package/dist/types/sdk/host/control/operations.d.ts +2 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/startup-capability.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +51 -19
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +27 -0
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +59 -13
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/skill-state/active-state.d.ts +0 -4
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/types.d.ts +36 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +17 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/package.json +9 -13
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +8 -0
- package/src/cli/args.ts +14 -8
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/read-cli.ts +2 -1
- package/src/cli.ts +49 -2
- package/src/commands/deep-interview.ts +29 -9
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +91 -7
- package/src/commands/team.ts +1 -1
- package/src/config/model-profile-activation.ts +1 -0
- package/src/config/model-profile-contract.ts +13 -0
- package/src/config/model-profiles.ts +24 -0
- package/src/config/model-registry.ts +21 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +84 -3
- package/src/config/settings.ts +254 -108
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +229 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +78 -26
- package/src/defaults/gjc/skills/team/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +26 -16
- package/src/eval/py/executor.ts +7 -2
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -68
- package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
- package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
- package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
- package/src/gjc-runtime/deep-interview-state.ts +12 -921
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/linux-proc.ts +70 -36
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +979 -71
- package/src/gjc-runtime/state-runtime.ts +50 -12
- package/src/gjc-runtime/state-writer.ts +70 -222
- package/src/gjc-runtime/team-launch.ts +34 -5
- package/src/gjc-runtime/team-runtime.ts +970 -33
- package/src/gjc-runtime/team-store.ts +32 -0
- package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/gjc-runtime/team-workers.ts +1 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +13 -7
- package/src/gjc-runtime/workflow-manifest.generated.json +97 -350
- package/src/gjc-runtime/workflow-manifest.ts +34 -179
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/hooks/skill-state.ts +1 -23
- package/src/internal-urls/docs-index.generated.ts +10 -11
- package/src/internal-urls/local-protocol.ts +23 -4
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/main.ts +9 -5
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +47 -0
- package/src/modes/acp/acp-agent.ts +597 -67
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +76 -5
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +1 -4
- package/src/modes/components/welcome.ts +18 -3
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +62 -6
- package/src/modes/controllers/selector-controller.ts +83 -37
- package/src/modes/interactive-mode.ts +127 -29
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +94 -1
- package/src/prompts/agents/architect.md +9 -1
- package/src/prompts/agents/critic.md +9 -0
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/manager.ts +26 -6
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/lifecycle.ts +107 -7
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/config.ts +1 -1
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +503 -60
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -4
- package/src/sdk/bus/telegram-daemon.ts +531 -53
- package/src/sdk/host/control/dispatch.ts +7 -1
- package/src/sdk/host/control/operations.ts +2 -1
- package/src/sdk/host/host.ts +25 -3
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/protocol/operation-inventory.generated.json +76 -0
- package/src/sdk/protocol/operation-registry.ts +8 -2
- package/src/sdk/session.ts +25 -2
- package/src/sdk/startup-capability.ts +6 -0
- package/src/session/agent-session.ts +720 -190
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +40 -10
- package/src/session/internal/native-publish-outcome.ts +8 -3
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +1372 -42
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/provider-onboarding.ts +19 -4
- package/src/skill-state/active-state.ts +0 -10
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +65 -7
- package/src/task/executor.ts +11 -3
- package/src/task/index.ts +2 -0
- package/src/task/types.ts +22 -11
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/ask.ts +89 -69
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/fetch.ts +114 -23
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1542 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/tool-result.ts +14 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
- package/src/gjc-runtime/deep-interview-draft.ts +0 -843
- package/src/gjc-runtime/deep-interview-payload.ts +0 -262
- package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
package/src/cli/read-cli.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { renderError } from "../tools/tool-errors";
|
|
|
15
15
|
|
|
16
16
|
export interface ReadCommandArgs {
|
|
17
17
|
path: string;
|
|
18
|
+
truncation?: "head" | "last" | "both";
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export async function runReadCommand(cmd: ReadCommandArgs): Promise<void> {
|
|
@@ -37,7 +38,7 @@ export async function runReadCommand(cmd: ReadCommandArgs): Promise<void> {
|
|
|
37
38
|
const tool = wrapToolWithMetaNotice(new ReadTool(session));
|
|
38
39
|
|
|
39
40
|
try {
|
|
40
|
-
const result = await tool.execute("gjc-read", { path: cmd.path });
|
|
41
|
+
const result = await tool.execute("gjc-read", { path: cmd.path, truncation: cmd.truncation });
|
|
41
42
|
|
|
42
43
|
for (const block of result.content) {
|
|
43
44
|
if (block.type === "text") {
|
package/src/cli.ts
CHANGED
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* lightweight CLI runner from pi-utils.
|
|
6
6
|
*/
|
|
7
7
|
import "@gajae-code/utils/postmortem";
|
|
8
|
+
import { THINKING_EFFORTS } from "@gajae-code/ai";
|
|
8
9
|
import { Args, type CliConfig, Command, type CommandEntry, Flags, run } from "@gajae-code/utils/cli";
|
|
9
10
|
import { APP_NAME, formatBunRuntimeError, MIN_BUN_VERSION, VERSION } from "@gajae-code/utils/dirs";
|
|
11
|
+
import { loadNative as loadNativeBindings } from "../../natives/native/loader-state.js";
|
|
10
12
|
import { runFixtureReport } from "./cli/fixture-report";
|
|
11
13
|
import { admitManagedOwnerBeforeCli, completeManagedOwnerRecovery } from "./gjc-runtime/managed-owner-admission";
|
|
12
14
|
import {
|
|
@@ -63,6 +65,7 @@ export const commands: CommandEntry[] = [
|
|
|
63
65
|
{ name: "migrate", load: () => import("./commands/migrate").then(m => m.default) },
|
|
64
66
|
{ name: "rlm", load: () => import("./commands/rlm").then(m => m.default) },
|
|
65
67
|
{ name: "update", load: () => import("./commands/update").then(m => m.default) },
|
|
68
|
+
{ name: "read", load: () => import("./commands/read").then(m => m.default) },
|
|
66
69
|
{ name: "plugin", load: () => import("./commands/plugin").then(m => m.default) },
|
|
67
70
|
{ name: "completion", load: () => import("./commands/completion").then(m => m.default) },
|
|
68
71
|
{ name: "launch", load: () => import("./commands/launch").then(m => m.default) },
|
|
@@ -174,6 +177,46 @@ async function runChatDaemonInternalFastPath(argv: string[]): Promise<void> {
|
|
|
174
177
|
await runChatDaemonInternal(action === "discord-internal" ? "discord" : "slack", argv.slice(2));
|
|
175
178
|
}
|
|
176
179
|
|
|
180
|
+
type MemoryGuardNativeSmokeLoad = () => Record<string, unknown>;
|
|
181
|
+
type WindowsJobMemoryProbeResult = Record<string, unknown> & { kind: string };
|
|
182
|
+
type MemoryGuardNativeSmokeReceipt = {
|
|
183
|
+
api: "memory_guard_windows_job_probe_v1";
|
|
184
|
+
source: "pi_natives";
|
|
185
|
+
result: WindowsJobMemoryProbeResult;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export function isMemoryGuardNativeSmokeFastPath(argv: readonly string[]): boolean {
|
|
189
|
+
return (
|
|
190
|
+
argv.length === 3 && argv[0] === "internal" && argv[1] === "memory-guard-native-smoke" && argv[2] === "--json"
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function parseWindowsJobMemoryProbeResult(value: unknown): WindowsJobMemoryProbeResult {
|
|
195
|
+
if (!value || typeof value !== "object") {
|
|
196
|
+
throw new Error("memory-guard-native-smoke: native probe returned a non-object result");
|
|
197
|
+
}
|
|
198
|
+
const result = value as Record<string, unknown>;
|
|
199
|
+
if (typeof result.kind !== "string") {
|
|
200
|
+
throw new Error("memory-guard-native-smoke: native probe result is missing a string kind tag");
|
|
201
|
+
}
|
|
202
|
+
return result as WindowsJobMemoryProbeResult;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function runMemoryGuardNativeSmokeFastPath(
|
|
206
|
+
options: { loadNative?: MemoryGuardNativeSmokeLoad; writeStdout?: (text: string) => void } = {},
|
|
207
|
+
): void {
|
|
208
|
+
const probe = (options.loadNative ?? loadNativeBindings)().probeWindowsJobMemory;
|
|
209
|
+
if (typeof probe !== "function") {
|
|
210
|
+
throw new Error("memory-guard-native-smoke: probeWindowsJobMemory export missing from native addon");
|
|
211
|
+
}
|
|
212
|
+
const receipt: MemoryGuardNativeSmokeReceipt = {
|
|
213
|
+
api: "memory_guard_windows_job_probe_v1",
|
|
214
|
+
source: "pi_natives",
|
|
215
|
+
result: parseWindowsJobMemoryProbeResult((probe as () => unknown)()),
|
|
216
|
+
};
|
|
217
|
+
(options.writeStdout ?? (text => process.stdout.write(text)))(`${JSON.stringify(receipt)}\n`);
|
|
218
|
+
}
|
|
219
|
+
|
|
177
220
|
function rootFixtureArg(argv: string[]): { present: boolean; id: string | undefined } {
|
|
178
221
|
for (let i = 0; i < argv.length; i++) {
|
|
179
222
|
const arg = argv[i];
|
|
@@ -244,8 +287,8 @@ export class RootHelpCommand extends Command {
|
|
|
244
287
|
tmux: Flags.boolean({ description: "Launch interactive startup inside tmux" }),
|
|
245
288
|
tools: Flags.string({ description: "Comma-separated list of tools to enable (default: all)" }),
|
|
246
289
|
thinking: Flags.string({
|
|
247
|
-
description:
|
|
248
|
-
options: [
|
|
290
|
+
description: `Set thinking level: ${THINKING_EFFORTS.join(", ")}`,
|
|
291
|
+
options: [...THINKING_EFFORTS],
|
|
249
292
|
}),
|
|
250
293
|
hook: Flags.string({ description: "Load a hook/extension file (can be used multiple times)", multiple: true }),
|
|
251
294
|
extension: Flags.string({
|
|
@@ -382,6 +425,10 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
382
425
|
}
|
|
383
426
|
// Re-exec could not be spawned; fall through and run in this process.
|
|
384
427
|
}
|
|
428
|
+
if (isMemoryGuardNativeSmokeFastPath(argv)) {
|
|
429
|
+
runMemoryGuardNativeSmokeFastPath();
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
385
432
|
if (isTmuxOwnerIsolationCliArgv(argv)) {
|
|
386
433
|
await runTmuxOwnerIsolationCliFromStdin();
|
|
387
434
|
return;
|
|
@@ -2,8 +2,22 @@ import { Command, Flags } from "@gajae-code/utils/cli";
|
|
|
2
2
|
import { runNativeDeepInterviewCommand } from "../gjc-runtime/deep-interview-runtime";
|
|
3
3
|
|
|
4
4
|
export default class DeepInterview extends Command {
|
|
5
|
-
static description =
|
|
6
|
-
|
|
5
|
+
static description = `Run native GJC deep-interview workflow.
|
|
6
|
+
|
|
7
|
+
All deep-interview state operations go through this command — no gjc state needed:
|
|
8
|
+
read Print the persisted envelope, revision, content sha, and any pending draft
|
|
9
|
+
write One-shot incremental JSON merge into state (--reset replaces; the locked
|
|
10
|
+
intent contract survives a reset)
|
|
11
|
+
stage Stage one JSON transition draft (--for <transition> --input '<json>'|@file)
|
|
12
|
+
check Dry-run the staged draft against current state (same merge apply performs)
|
|
13
|
+
apply Commit the staged draft with runtime-owned revision+sha CAS
|
|
14
|
+
discard Remove the pending draft
|
|
15
|
+
clear Clear deep-interview state for the session (lifecycle passthrough)
|
|
16
|
+
handoff Hand off to the next workflow skill (lifecycle passthrough)
|
|
17
|
+
|
|
18
|
+
Ambiguity is runtime-owned: apply/write derive current_ambiguity from the latest valid scored
|
|
19
|
+
round and clamp it to the deterministic floor. Sessions resolve from --session-id, payload
|
|
20
|
+
session_id, or GJC_SESSION_ID.`;
|
|
7
21
|
static strict = false;
|
|
8
22
|
static flags = {
|
|
9
23
|
quick: Flags.boolean({ description: "Seed a quick deep-interview run" }),
|
|
@@ -15,6 +29,11 @@ export default class DeepInterview extends Command {
|
|
|
15
29
|
"session-id": Flags.string({
|
|
16
30
|
description: "Route state/spec handoff through a session-scoped .gjc/_session-{sessionid} directory",
|
|
17
31
|
}),
|
|
32
|
+
input: Flags.string({ description: "JSON payload (or @file) for the write/stage verbs" }),
|
|
33
|
+
for: Flags.string({
|
|
34
|
+
description: "Transition for stage: initialize-context | record-round | update-facts | merge-state",
|
|
35
|
+
}),
|
|
36
|
+
reset: Flags.boolean({ description: "With write: replace state instead of incremental merge" }),
|
|
18
37
|
write: Flags.boolean({ description: "Persist a final deep-interview spec through the sanctioned GJC CLI/API" }),
|
|
19
38
|
stage: Flags.string({ description: 'Spec stage for --write (currently "final")' }),
|
|
20
39
|
slug: Flags.string({ description: "Safe slug for .gjc/_session-{sessionid}/specs/deep-interview-<slug>.md" }),
|
|
@@ -27,13 +46,14 @@ export default class DeepInterview extends Command {
|
|
|
27
46
|
json: Flags.boolean({ description: "Output JSON" }),
|
|
28
47
|
};
|
|
29
48
|
static examples = [
|
|
30
|
-
'$ gjc deep-interview --trace --standard "<idea>"
|
|
31
|
-
"$ gjc deep-interview
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"$ gjc deep-interview
|
|
35
|
-
"$ gjc deep-interview
|
|
36
|
-
"$ gjc deep-interview
|
|
49
|
+
'$ gjc deep-interview --trace --standard "<idea>"',
|
|
50
|
+
"$ gjc deep-interview read --json",
|
|
51
|
+
'$ gjc deep-interview write --input \'{"state":{"threshold":0.05}}\' --json',
|
|
52
|
+
'$ gjc deep-interview stage --for record-round --input \'{"state":{"rounds":[{"round":1,"round_key":"r1"}]}}\' --json',
|
|
53
|
+
"$ gjc deep-interview check --json",
|
|
54
|
+
"$ gjc deep-interview apply --json",
|
|
55
|
+
"$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md",
|
|
56
|
+
"$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md --deliberate",
|
|
37
57
|
];
|
|
38
58
|
|
|
39
59
|
async run(): Promise<void> {
|
package/src/commands/harness.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
14
14
|
import * as fs from "node:fs/promises";
|
|
15
15
|
import * as path from "node:path";
|
|
16
16
|
import { Args, Command, Flags } from "@gajae-code/utils/cli";
|
|
17
|
+
import { $credentialEnv } from "@gajae-code/utils/env";
|
|
17
18
|
import {
|
|
18
19
|
GJC_TMUX_OWNER_GENERATION_ENV,
|
|
19
20
|
GJC_TMUX_OWNER_SERVER_KEY_ENV,
|
|
@@ -565,20 +566,42 @@ function ownerIsolationPlatform(): NodeJS.Platform {
|
|
|
565
566
|
: "linux";
|
|
566
567
|
}
|
|
567
568
|
|
|
569
|
+
/**
|
|
570
|
+
* Operator override for the process-start probe command, resolved from trusted
|
|
571
|
+
* environment sources only.
|
|
572
|
+
*
|
|
573
|
+
* The result is spawned, so whatever can set it chooses which binary runs.
|
|
574
|
+
* `$env` merges the caller's `cwd/.env` into `process.env`, so reading it there
|
|
575
|
+
* would let repository content pick the command; resolve it the same way
|
|
576
|
+
* provider credentials are (launching shell plus GJC/user-owned `.env` files,
|
|
577
|
+
* never the project `.env`). A malformed override stays fatal rather than
|
|
578
|
+
* silently falling back to `ps`, matching the previous behavior.
|
|
579
|
+
*/
|
|
580
|
+
type ProcessStartCommandOverride = { kind: "none" } | { kind: "invalid" } | { kind: "command"; command: string[] };
|
|
581
|
+
|
|
582
|
+
function processStartCommandOverride(): ProcessStartCommandOverride {
|
|
583
|
+
const configured = $credentialEnv("GJC_HARNESS_PROCESS_START_COMMAND");
|
|
584
|
+
if (!configured) return { kind: "none" };
|
|
585
|
+
try {
|
|
586
|
+
const parsed = JSON.parse(configured) as unknown;
|
|
587
|
+
if (!Array.isArray(parsed) || parsed.length === 0 || parsed.some(value => typeof value !== "string" || !value))
|
|
588
|
+
return { kind: "invalid" };
|
|
589
|
+
return { kind: "command", command: parsed as string[] };
|
|
590
|
+
} catch {
|
|
591
|
+
return { kind: "invalid" };
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** Test seam: the process-start command override as resolved from trusted env. */
|
|
596
|
+
export function processStartCommandOverrideForTest(): ProcessStartCommandOverride {
|
|
597
|
+
return processStartCommandOverride();
|
|
598
|
+
}
|
|
599
|
+
|
|
568
600
|
function portableProcessStartTime(pid: number): string | null {
|
|
569
601
|
if (process.platform === "linux") return null;
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
try {
|
|
574
|
-
const parsed = JSON.parse(configured) as unknown;
|
|
575
|
-
if (!Array.isArray(parsed) || parsed.length === 0 || parsed.some(value => typeof value !== "string" || !value))
|
|
576
|
-
return null;
|
|
577
|
-
command = parsed;
|
|
578
|
-
} catch {
|
|
579
|
-
return null;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
602
|
+
const override = processStartCommandOverride();
|
|
603
|
+
if (override.kind === "invalid") return null;
|
|
604
|
+
const command: string[] = override.kind === "command" ? override.command : ["ps", "-o", "lstart=", "-p"];
|
|
582
605
|
const result = Bun.spawnSync([...command, String(pid)], {
|
|
583
606
|
stdout: "pipe",
|
|
584
607
|
stderr: "ignore",
|
package/src/commands/read.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Show what the read tool will return for a given path.
|
|
3
3
|
*/
|
|
4
|
-
import { Args, Command } from "@gajae-code/utils/cli";
|
|
4
|
+
import { Args, Command, Flags } from "@gajae-code/utils/cli";
|
|
5
5
|
import { type ReadCommandArgs, runReadCommand } from "../cli/read-cli";
|
|
6
6
|
import { initTheme } from "../modes/theme/theme";
|
|
7
7
|
|
|
@@ -15,8 +15,16 @@ export default class Read extends Command {
|
|
|
15
15
|
}),
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
static flags = {
|
|
19
|
+
truncation: Flags.string({
|
|
20
|
+
options: ["head", "last", "both"],
|
|
21
|
+
description: "Which end of an over-budget result to keep",
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
|
|
18
25
|
static examples = [
|
|
19
26
|
"gjc read src/foo.ts",
|
|
27
|
+
"gjc read src/foo.ts --truncation head",
|
|
20
28
|
"gjc read src/foo.ts:50-100",
|
|
21
29
|
"gjc read src/foo.ts:raw",
|
|
22
30
|
"gjc read https://example.com",
|
|
@@ -25,9 +33,10 @@ export default class Read extends Command {
|
|
|
25
33
|
];
|
|
26
34
|
|
|
27
35
|
async run(): Promise<void> {
|
|
28
|
-
const { args } = await this.parse(Read);
|
|
36
|
+
const { args, flags } = await this.parse(Read);
|
|
29
37
|
const cmd: ReadCommandArgs = {
|
|
30
38
|
path: args.path ?? "",
|
|
39
|
+
truncation: flags.truncation as ReadCommandArgs["truncation"],
|
|
31
40
|
};
|
|
32
41
|
await initTheme();
|
|
33
42
|
await runReadCommand(cmd);
|
package/src/commands/sdk.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as os from "node:os";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import { Args, CliParseError, Command, Flags, renderCommandHelp } from "@gajae-code/utils/cli";
|
|
5
6
|
import type { Args as ParsedArgs } from "../cli/args";
|
|
6
7
|
import { Settings } from "../config/settings";
|
|
7
8
|
import { applyStartupModelProfiles, createSessionManager } from "../main";
|
|
8
9
|
import { initializeExtensions } from "../modes/runtime-init";
|
|
10
|
+
import { ACP_MCP_REQUEST_TIMEOUT_MS, ACP_MCP_STARTUP_HEADROOM_MS } from "../sdk/acp/mcp";
|
|
9
11
|
import { Broker } from "../sdk/broker/broker";
|
|
10
12
|
import { completeBrokerProcess } from "../sdk/broker/internal";
|
|
11
13
|
import {
|
|
@@ -285,17 +287,99 @@ export async function runSessionHost(
|
|
|
285
287
|
throw new Error("SDK startup did not complete before readiness cutoff.");
|
|
286
288
|
}
|
|
287
289
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
created = await createLifecycleAgentSession({ cwd, agentDir, sessionManager: opened.sessionManager });
|
|
293
|
-
} catch (error) {
|
|
290
|
+
// Inlined rather than extracted to a helper: TypeScript's definite-assignment
|
|
291
|
+
// analysis does not see a `Promise<never>` helper as terminating, so hoisting
|
|
292
|
+
// this would make `opened`/`created` "used before assigned" below.
|
|
293
|
+
const registrationFailure = async (error: unknown): Promise<SdkStartupFailure> => {
|
|
294
294
|
const rollback = new SdkStartupRollbackTracker();
|
|
295
295
|
rollback.recordAbsent();
|
|
296
296
|
const failure = normalizeSdkStartupFailure("registration", "failed", error);
|
|
297
297
|
await writeFailure(failure, rollback.result);
|
|
298
|
-
|
|
298
|
+
return failure;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
let opened: { parsed: ParsedArgs; sessionManager: SessionManager | undefined };
|
|
302
|
+
let created: CreateLifecycleAgentSessionResult;
|
|
303
|
+
let mcpConfigDirectory: string | undefined;
|
|
304
|
+
try {
|
|
305
|
+
let mcpConfigPath: string | undefined;
|
|
306
|
+
try {
|
|
307
|
+
opened = await openLifecycleSessionManager(request, cwd, agentDir);
|
|
308
|
+
if (request.mcpServers && request.mcpServers.length > 0) {
|
|
309
|
+
mcpConfigDirectory = await fs.realpath(await fs.mkdtemp(path.join(os.tmpdir(), "gjc-acp-mcp-")));
|
|
310
|
+
mcpConfigPath = path.join(mcpConfigDirectory, "mcp.json");
|
|
311
|
+
await Bun.write(
|
|
312
|
+
mcpConfigPath,
|
|
313
|
+
JSON.stringify({
|
|
314
|
+
mcpServers: Object.fromEntries(
|
|
315
|
+
request.mcpServers.map(server => [
|
|
316
|
+
server.name,
|
|
317
|
+
"url" in server
|
|
318
|
+
? {
|
|
319
|
+
type: server.type,
|
|
320
|
+
url: server.url,
|
|
321
|
+
...(server.headers ? { headers: server.headers } : {}),
|
|
322
|
+
timeout: ACP_MCP_REQUEST_TIMEOUT_MS,
|
|
323
|
+
}
|
|
324
|
+
: {
|
|
325
|
+
type: "stdio",
|
|
326
|
+
command: server.command,
|
|
327
|
+
args: server.args,
|
|
328
|
+
...(server.env ? { env: server.env } : {}),
|
|
329
|
+
noInheritEnv: true,
|
|
330
|
+
timeout: ACP_MCP_REQUEST_TIMEOUT_MS,
|
|
331
|
+
},
|
|
332
|
+
]),
|
|
333
|
+
),
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
} catch (error) {
|
|
338
|
+
throw await registrationFailure(error);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// The longer MCP startup ceiling is scoped to ACP lifecycle launches only:
|
|
342
|
+
// it applies when this request actually carried `mcpServers`. Ordinary
|
|
343
|
+
// CLI/SDK `mcpConfigPath` consumers keep the manager's short default.
|
|
344
|
+
//
|
|
345
|
+
// This recheck deliberately sits OUTSIDE the registration catch above.
|
|
346
|
+
// Inside it, the throw would be caught, reclassified as
|
|
347
|
+
// `registration`/`failed`, and written a second time, losing the
|
|
348
|
+
// `startup`/`pending` outcome the readiness cutoff is supposed to report.
|
|
349
|
+
// Session-manager open and MCP config write already consumed part of the
|
|
350
|
+
// budget, so re-read the clock here rather than reusing the earlier check.
|
|
351
|
+
let mcpStartupTimeoutMs: number | undefined;
|
|
352
|
+
if (mcpConfigPath !== undefined) {
|
|
353
|
+
const remaining = request.semanticReadyDeadlineAt - now() - ACP_MCP_STARTUP_HEADROOM_MS;
|
|
354
|
+
if (remaining <= 0) {
|
|
355
|
+
const absent = new SdkStartupRollbackTracker();
|
|
356
|
+
absent.recordAbsent();
|
|
357
|
+
await writeFailure(
|
|
358
|
+
{
|
|
359
|
+
phase: "startup",
|
|
360
|
+
reason: "pending",
|
|
361
|
+
message: "SDK startup did not complete before readiness cutoff.",
|
|
362
|
+
},
|
|
363
|
+
absent.result,
|
|
364
|
+
);
|
|
365
|
+
throw new Error("SDK startup did not complete before readiness cutoff.");
|
|
366
|
+
}
|
|
367
|
+
mcpStartupTimeoutMs = remaining;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
created = await createLifecycleAgentSession({
|
|
372
|
+
cwd,
|
|
373
|
+
agentDir,
|
|
374
|
+
sessionManager: opened.sessionManager,
|
|
375
|
+
...(mcpConfigPath ? { mcpConfigPath } : {}),
|
|
376
|
+
...(mcpStartupTimeoutMs !== undefined ? { mcpStartupTimeoutMs } : {}),
|
|
377
|
+
});
|
|
378
|
+
} catch (error) {
|
|
379
|
+
throw await registrationFailure(error);
|
|
380
|
+
}
|
|
381
|
+
} finally {
|
|
382
|
+
if (mcpConfigDirectory) await fs.rm(mcpConfigDirectory, { recursive: true, force: true });
|
|
299
383
|
}
|
|
300
384
|
const { parsed } = opened;
|
|
301
385
|
if ("failure" in created) {
|
package/src/commands/team.ts
CHANGED
|
@@ -173,7 +173,7 @@ export default class Team extends Command {
|
|
|
173
173
|
"Supported operations:",
|
|
174
174
|
"send-message broadcast mailbox-list mailbox-mark-delivered mailbox-mark-notified notification-list notification-read notification-replay notification-mark-pane-attempt worker-startup-ack",
|
|
175
175
|
"create-task read-task list-tasks update-task claim-task transition-task-status release-task-claim",
|
|
176
|
-
"read-config read-manifest read-worker-status update-worker-status read-worker-heartbeat recover-stale-claims update-worker-heartbeat write-worker-inbox write-worker-identity",
|
|
176
|
+
"read-config read-manifest read-worker-status update-worker-status read-worker-heartbeat recover-stale-claims update-worker-heartbeat read-worker-memory-guard update-worker-memory-guard apply-worker-memory-guard write-worker-inbox write-worker-identity",
|
|
177
177
|
"append-event read-events read-traces await-event write-shutdown-request read-shutdown-ack read-monitor-snapshot write-monitor-snapshot read-task-approval write-task-approval",
|
|
178
178
|
"Completion example:",
|
|
179
179
|
'transition-task-status --input \'{"team_name":"demo","task_id":"task-1","to":"completed","claim_token":"...","completion_evidence":{"summary":"done","items":[{"kind":"command","status":"passed","summary":"focused tests passed","command":"bun test packages/coding-agent/test/gjc-runtime/team-runtime.test.ts"}]}}\' --json',
|
|
@@ -222,6 +222,7 @@ export function materializeActiveModelProfileAssignments(options: MaterializeMod
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
export class ModelProfileCredentialError extends Error {
|
|
225
|
+
readonly code = "authentication_failed";
|
|
225
226
|
readonly profileLabel: string;
|
|
226
227
|
readonly providers: readonly string[];
|
|
227
228
|
|
|
@@ -10,6 +10,7 @@ export interface ModelProfileCatalogItem {
|
|
|
10
10
|
id: string;
|
|
11
11
|
displayName: string;
|
|
12
12
|
source: "builtin" | "configured";
|
|
13
|
+
available?: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export interface UnknownModelProfileDetails {
|
|
@@ -124,6 +125,18 @@ export function projectModelProfileCatalog(
|
|
|
124
125
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
export function isModelProfileProviderAvailable(
|
|
129
|
+
profile: ModelProfileDefinition,
|
|
130
|
+
authenticatedProviders: ReadonlySet<string>,
|
|
131
|
+
): boolean {
|
|
132
|
+
const alternativeGroups = profile.alternativeProviderGroups ?? [];
|
|
133
|
+
const alternativeProviders = new Set(alternativeGroups.flat());
|
|
134
|
+
for (const provider of profile.requiredProviders) {
|
|
135
|
+
if (!alternativeProviders.has(provider) && !authenticatedProviders.has(provider)) return false;
|
|
136
|
+
}
|
|
137
|
+
return alternativeGroups.every(group => group.some(provider => authenticatedProviders.has(provider)));
|
|
138
|
+
}
|
|
139
|
+
|
|
127
140
|
export function isModelProfileError(value: unknown): value is {
|
|
128
141
|
code: ModelProfileErrorCode;
|
|
129
142
|
message: string;
|
|
@@ -207,6 +207,27 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
207
207
|
critic: "xai/grok-4.3:xhigh",
|
|
208
208
|
architect: "xai/grok-4.3:xhigh",
|
|
209
209
|
}),
|
|
210
|
+
profile("grok-45-eco", ["xai"], {
|
|
211
|
+
default: "xai/grok-4.5:low",
|
|
212
|
+
executor: "xai/grok-4.5:minimal",
|
|
213
|
+
planner: "xai/grok-4.5:low",
|
|
214
|
+
critic: "xai/grok-4.5:medium",
|
|
215
|
+
architect: "xai/grok-4.5:high",
|
|
216
|
+
}),
|
|
217
|
+
profile("grok-45-medium", ["xai"], {
|
|
218
|
+
default: "xai/grok-4.5:medium",
|
|
219
|
+
executor: "xai/grok-4.5:low",
|
|
220
|
+
planner: "xai/grok-4.5:medium",
|
|
221
|
+
critic: "xai/grok-4.5:high",
|
|
222
|
+
architect: "xai/grok-4.5:high",
|
|
223
|
+
}),
|
|
224
|
+
profile("grok-45-pro", ["xai"], {
|
|
225
|
+
default: "xai/grok-4.5:high",
|
|
226
|
+
executor: "xai/grok-4.5:medium",
|
|
227
|
+
planner: "xai/grok-4.5:high",
|
|
228
|
+
critic: "xai/grok-4.5:high",
|
|
229
|
+
architect: "xai/grok-4.5:high",
|
|
230
|
+
}),
|
|
210
231
|
profile("grok-build-pro", ["grok-build"], {
|
|
211
232
|
default: "grok-build/grok-composer-2.5-fast",
|
|
212
233
|
executor: "grok-build/grok-build",
|
|
@@ -321,6 +342,9 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
|
|
|
321
342
|
"grok-eco": { displayName: "Grok Eco", providerGroup: "GROK" },
|
|
322
343
|
"grok-medium": { displayName: "Grok Medium", providerGroup: "GROK" },
|
|
323
344
|
"grok-pro": { displayName: "Grok Pro", providerGroup: "GROK" },
|
|
345
|
+
"grok-45-eco": { displayName: "Grok 4.5 Eco", providerGroup: "GROK" },
|
|
346
|
+
"grok-45-medium": { displayName: "Grok 4.5 Medium", providerGroup: "GROK" },
|
|
347
|
+
"grok-45-pro": { displayName: "Grok 4.5 Pro", providerGroup: "GROK" },
|
|
324
348
|
"grok-build-pro": { displayName: "Grok Build Pro", providerGroup: "GROK" },
|
|
325
349
|
"cursor-eco": { displayName: "Cursor Eco", providerGroup: "CURSOR" },
|
|
326
350
|
"cursor-medium": { displayName: "Cursor Medium", providerGroup: "CURSOR" },
|
|
@@ -34,7 +34,7 @@ const DEFAULT_LOCAL_TOKEN = "lm-studio-local";
|
|
|
34
34
|
|
|
35
35
|
import { registerOAuthProvider, unregisterOAuthProviders } from "@gajae-code/ai/utils/oauth";
|
|
36
36
|
import type { OAuthCredentials, OAuthLoginCallbacks } from "@gajae-code/ai/utils/oauth/types";
|
|
37
|
-
import { $
|
|
37
|
+
import { $pickCredentialEnv, isRecord, logger } from "@gajae-code/utils";
|
|
38
38
|
import { parseModelString, resolveProviderModelReference } from "../config/model-resolver";
|
|
39
39
|
import { isValidThemeColor, type ThemeColor } from "../modes/theme/theme";
|
|
40
40
|
import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
|
|
@@ -450,8 +450,20 @@ function getProviderBaseUrlEnvKeys(provider: string): string[] {
|
|
|
450
450
|
return keys;
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
+
/**
|
|
454
|
+
* Provider base URL from the environment, trusted sources only.
|
|
455
|
+
*
|
|
456
|
+
* The result is baked into the provider override and reaches `model.baseUrl`,
|
|
457
|
+
* which the provider resolvers use as the request endpoint that carries the
|
|
458
|
+
* provider credential. `$env` (and therefore `$pickenv`) merges the caller's
|
|
459
|
+
* `cwd/.env`, so reading it there would let repository content redirect
|
|
460
|
+
* authenticated traffic for any provider — including re-admitting a redirect
|
|
461
|
+
* that the provider-level resolvers already reject. Resolve it the same way
|
|
462
|
+
* provider credentials are: launching shell plus GJC/user-owned `.env` files,
|
|
463
|
+
* never the project `.env`.
|
|
464
|
+
*/
|
|
453
465
|
function resolveProviderBaseUrlFromEnv(provider: string): string | undefined {
|
|
454
|
-
return $
|
|
466
|
+
return $pickCredentialEnv(...getProviderBaseUrlEnvKeys(provider));
|
|
455
467
|
}
|
|
456
468
|
|
|
457
469
|
function normalizeLocalOpenAICompatBaseUrl(baseUrl: string): string {
|
|
@@ -2507,6 +2519,13 @@ export class ModelRegistry {
|
|
|
2507
2519
|
return this.#keylessProviders.has(model.provider) || this.authStorage.hasAuth(model.provider);
|
|
2508
2520
|
}
|
|
2509
2521
|
|
|
2522
|
+
/**
|
|
2523
|
+
* Check whether auth is configured for a provider.
|
|
2524
|
+
*/
|
|
2525
|
+
hasConfiguredProviderAuth(provider: string): boolean {
|
|
2526
|
+
return this.#keylessProviders.has(provider) || this.authStorage.hasAuth(provider);
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2510
2529
|
getDiscoverableProviders(): string[] {
|
|
2511
2530
|
const disabledProviders = getDisabledProviderIdsFromSettings();
|
|
2512
2531
|
return this.#discoveryManager.providers
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AuthStorage } from "@gajae-code/ai/auth-storage";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Most recent OAuth validation outcome per provider, used only as an ordering
|
|
5
|
+
* hint so `/model` can rank a provider whose stored credentials failed
|
|
6
|
+
* validation below one that is healthy, without re-running validation itself.
|
|
7
|
+
*
|
|
8
|
+
* Entries are scoped to the `AuthStorage` that produced them and to its
|
|
9
|
+
* credential generation, so any login, logout, import, or deletion discards the
|
|
10
|
+
* stale result instead of overriding current auth state.
|
|
11
|
+
*/
|
|
12
|
+
export type ProviderAuthHealth = "valid" | "invalid";
|
|
13
|
+
|
|
14
|
+
interface HealthEntry {
|
|
15
|
+
generation: number;
|
|
16
|
+
health: ProviderAuthHealth;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const healthByStorage = new WeakMap<AuthStorage, Map<string, HealthEntry>>();
|
|
20
|
+
|
|
21
|
+
export function recordProviderAuthHealth(
|
|
22
|
+
authStorage: AuthStorage,
|
|
23
|
+
providerId: string,
|
|
24
|
+
health: ProviderAuthHealth,
|
|
25
|
+
): void {
|
|
26
|
+
let entries = healthByStorage.get(authStorage);
|
|
27
|
+
if (!entries) {
|
|
28
|
+
entries = new Map();
|
|
29
|
+
healthByStorage.set(authStorage, entries);
|
|
30
|
+
}
|
|
31
|
+
entries.set(providerId, { generation: authStorage.getGeneration(), health });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getProviderAuthHealth(authStorage: AuthStorage, providerId: string): ProviderAuthHealth | undefined {
|
|
35
|
+
const entry = healthByStorage.get(authStorage)?.get(providerId);
|
|
36
|
+
if (!entry || entry.generation !== authStorage.getGeneration()) return undefined;
|
|
37
|
+
return entry.health;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function clearProviderAuthHealth(authStorage: AuthStorage): void {
|
|
41
|
+
healthByStorage.delete(authStorage);
|
|
42
|
+
}
|