@gajae-code/coding-agent 0.11.9 → 0.11.11
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 +56 -1
- package/dist/types/cli.d.ts +8 -1
- 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 +2 -0
- package/dist/types/config/settings-schema.d.ts +57 -2
- package/dist/types/config/settings.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +21 -0
- package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +60 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +14 -9
- 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/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +16 -8
- package/dist/types/modes/interactive-mode.d.ts +5 -1
- 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/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 +2 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +11 -2
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +14 -7
- package/dist/types/sdk/bus/telegram-daemon.d.ts +97 -8
- package/dist/types/sdk/host/control/operations.d.ts +1 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/session.d.ts +7 -0
- package/dist/types/sdk/startup-capability.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +11 -11
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +28 -1
- package/dist/types/session/session-manager.d.ts +34 -0
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/provider-retry-status.d.ts +13 -0
- package/dist/types/task/types.d.ts +39 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/index.d.ts +3 -1
- package/dist/types/tools/resource-gc.d.ts +19 -0
- package/package.json +8 -8
- package/scripts/generate-sdk-operation-inventory.ts +3 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/src/cli/args.ts +14 -8
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/notify-cli.ts +1 -0
- package/src/cli.ts +48 -2
- package/src/commands/sdk.ts +91 -7
- package/src/config/model-profile-activation.ts +1 -0
- package/src/config/model-profile-contract.ts +13 -0
- package/src/config/model-profiles.ts +31 -7
- package/src/config/model-registry.ts +4 -2
- package/src/config/settings-schema.ts +61 -2
- package/src/config/settings.ts +8 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/eval/js/tool-bridge.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +14 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +16 -2
- package/src/gjc-runtime/deep-interview-draft.ts +2 -1
- package/src/gjc-runtime/deep-interview-recorder.ts +16 -1
- package/src/gjc-runtime/deep-interview-state.ts +25 -3
- package/src/gjc-runtime/linux-proc.ts +70 -36
- package/src/gjc-runtime/ralplan-runtime.ts +252 -1
- package/src/gjc-runtime/state-runtime.ts +48 -11
- package/src/gjc-runtime/state-writer.ts +1 -1
- package/src/hooks/skill-state.ts +23 -1
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/internal-urls/local-protocol.ts +23 -4
- package/src/modes/acp/acp-agent.ts +554 -60
- package/src/modes/acp/acp-event-mapper.ts +93 -6
- package/src/modes/components/model-selector.ts +15 -1
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- 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/extension-ui-controller.ts +4 -10
- package/src/modes/controllers/input-controller.ts +44 -3
- package/src/modes/controllers/selector-controller.ts +38 -8
- package/src/modes/interactive-mode.ts +65 -26
- package/src/modes/runtime-init.ts +1 -0
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/architect.md +8 -1
- package/src/prompts/agents/critic.md +8 -1
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/agents/planner.md +8 -1
- 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/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/identity.ts +22 -12
- package/src/sdk/broker/lifecycle.ts +82 -1
- 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/daemon-paths.ts +2 -0
- package/src/sdk/bus/index.ts +222 -27
- package/src/sdk/bus/notification-service.ts +19 -5
- package/src/sdk/bus/operator-runtime.ts +31 -5
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/telegram-cli.ts +6 -13
- package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +15 -7
- package/src/sdk/bus/telegram-daemon-control.ts +14 -5
- package/src/sdk/bus/telegram-daemon.ts +1191 -164
- package/src/sdk/host/control/dispatch.ts +2 -0
- package/src/sdk/host/control/operations.ts +1 -0
- package/src/sdk/host/host.ts +2 -2
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/protocol/operation-inventory.generated.json +58 -0
- package/src/sdk/protocol/operation-registry.ts +1 -0
- package/src/sdk/session.ts +24 -1
- package/src/sdk/startup-capability.ts +6 -0
- package/src/session/agent-session.ts +290 -132
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +173 -127
- package/src/session/internal/native-publish-outcome.ts +8 -3
- package/src/session/session-manager.ts +608 -25
- package/src/skill-state/workflow-mutation-guard.ts +41 -7
- package/src/task/executor.ts +152 -42
- package/src/task/index.ts +2 -0
- package/src/task/provider-retry-status.ts +88 -0
- package/src/task/render.ts +45 -26
- package/src/task/types.ts +25 -11
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/bash-allowed-prefixes.ts +45 -1
- package/src/tools/index.ts +3 -1
- package/src/tools/resource-gc.ts +496 -120
- package/src/tools/subagent-render.ts +74 -16
- package/src/tools/subagent.ts +2 -0
- package/src/workflow/workflow-intent-diff.ts +4 -1
- package/vendor/insane-search/MANIFEST.json +4 -1
- package/vendor/insane-search/engine/bias_check.py +5 -0
- package/vendor/insane-search/engine/fetch_chain.py +67 -1
- package/vendor/insane-search/engine/transport.py +13 -2
- package/vendor/insane-search/engine/validators.py +4 -1
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 {
|
|
@@ -174,6 +176,46 @@ async function runChatDaemonInternalFastPath(argv: string[]): Promise<void> {
|
|
|
174
176
|
await runChatDaemonInternal(action === "discord-internal" ? "discord" : "slack", argv.slice(2));
|
|
175
177
|
}
|
|
176
178
|
|
|
179
|
+
type MemoryGuardNativeSmokeLoad = () => Record<string, unknown>;
|
|
180
|
+
type WindowsJobMemoryProbeResult = Record<string, unknown> & { kind: string };
|
|
181
|
+
type MemoryGuardNativeSmokeReceipt = {
|
|
182
|
+
api: "memory_guard_windows_job_probe_v1";
|
|
183
|
+
source: "pi_natives";
|
|
184
|
+
result: WindowsJobMemoryProbeResult;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export function isMemoryGuardNativeSmokeFastPath(argv: readonly string[]): boolean {
|
|
188
|
+
return (
|
|
189
|
+
argv.length === 3 && argv[0] === "internal" && argv[1] === "memory-guard-native-smoke" && argv[2] === "--json"
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function parseWindowsJobMemoryProbeResult(value: unknown): WindowsJobMemoryProbeResult {
|
|
194
|
+
if (!value || typeof value !== "object") {
|
|
195
|
+
throw new Error("memory-guard-native-smoke: native probe returned a non-object result");
|
|
196
|
+
}
|
|
197
|
+
const result = value as Record<string, unknown>;
|
|
198
|
+
if (typeof result.kind !== "string") {
|
|
199
|
+
throw new Error("memory-guard-native-smoke: native probe result is missing a string kind tag");
|
|
200
|
+
}
|
|
201
|
+
return result as WindowsJobMemoryProbeResult;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function runMemoryGuardNativeSmokeFastPath(
|
|
205
|
+
options: { loadNative?: MemoryGuardNativeSmokeLoad; writeStdout?: (text: string) => void } = {},
|
|
206
|
+
): void {
|
|
207
|
+
const probe = (options.loadNative ?? loadNativeBindings)().probeWindowsJobMemory;
|
|
208
|
+
if (typeof probe !== "function") {
|
|
209
|
+
throw new Error("memory-guard-native-smoke: probeWindowsJobMemory export missing from native addon");
|
|
210
|
+
}
|
|
211
|
+
const receipt: MemoryGuardNativeSmokeReceipt = {
|
|
212
|
+
api: "memory_guard_windows_job_probe_v1",
|
|
213
|
+
source: "pi_natives",
|
|
214
|
+
result: parseWindowsJobMemoryProbeResult((probe as () => unknown)()),
|
|
215
|
+
};
|
|
216
|
+
(options.writeStdout ?? (text => process.stdout.write(text)))(`${JSON.stringify(receipt)}\n`);
|
|
217
|
+
}
|
|
218
|
+
|
|
177
219
|
function rootFixtureArg(argv: string[]): { present: boolean; id: string | undefined } {
|
|
178
220
|
for (let i = 0; i < argv.length; i++) {
|
|
179
221
|
const arg = argv[i];
|
|
@@ -244,8 +286,8 @@ export class RootHelpCommand extends Command {
|
|
|
244
286
|
tmux: Flags.boolean({ description: "Launch interactive startup inside tmux" }),
|
|
245
287
|
tools: Flags.string({ description: "Comma-separated list of tools to enable (default: all)" }),
|
|
246
288
|
thinking: Flags.string({
|
|
247
|
-
description:
|
|
248
|
-
options: [
|
|
289
|
+
description: `Set thinking level: ${THINKING_EFFORTS.join(", ")}`,
|
|
290
|
+
options: [...THINKING_EFFORTS],
|
|
249
291
|
}),
|
|
250
292
|
hook: Flags.string({ description: "Load a hook/extension file (can be used multiple times)", multiple: true }),
|
|
251
293
|
extension: Flags.string({
|
|
@@ -382,6 +424,10 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
382
424
|
}
|
|
383
425
|
// Re-exec could not be spawned; fall through and run in this process.
|
|
384
426
|
}
|
|
427
|
+
if (isMemoryGuardNativeSmokeFastPath(argv)) {
|
|
428
|
+
runMemoryGuardNativeSmokeFastPath();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
385
431
|
if (isTmuxOwnerIsolationCliArgv(argv)) {
|
|
386
432
|
await runTmuxOwnerIsolationCliFromStdin();
|
|
387
433
|
return;
|
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) {
|
|
@@ -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;
|
|
@@ -100,11 +100,11 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
100
100
|
architect: "opencode-go/deepseek-v4-pro",
|
|
101
101
|
}),
|
|
102
102
|
profile("claude-opus", ["anthropic"], {
|
|
103
|
-
default: "anthropic/claude-opus-
|
|
103
|
+
default: "anthropic/claude-opus-5:xhigh",
|
|
104
104
|
executor: "anthropic/claude-sonnet-5",
|
|
105
|
-
planner: "anthropic/claude-opus-
|
|
106
|
-
critic: "anthropic/claude-opus-
|
|
107
|
-
architect: "anthropic/claude-opus-
|
|
105
|
+
planner: "anthropic/claude-opus-5:low",
|
|
106
|
+
critic: "anthropic/claude-opus-5:high",
|
|
107
|
+
architect: "anthropic/claude-opus-5:xhigh",
|
|
108
108
|
}),
|
|
109
109
|
profile("claude-fable", ["anthropic"], {
|
|
110
110
|
default: "anthropic/claude-fable-5:xhigh",
|
|
@@ -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",
|
|
@@ -271,7 +292,7 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
271
292
|
critic: "alibaba-token-plan/qwen3.8-max-preview:xhigh",
|
|
272
293
|
}),
|
|
273
294
|
profile("opus-codex", ["anthropic", "openai-codex"], {
|
|
274
|
-
default: "anthropic/claude-opus-
|
|
295
|
+
default: "anthropic/claude-opus-5:xhigh",
|
|
275
296
|
executor: "openai-codex/gpt-5.6-terra:low",
|
|
276
297
|
planner: "anthropic/claude-sonnet-5",
|
|
277
298
|
critic: "openai-codex/gpt-5.6-sol:xhigh",
|
|
@@ -287,8 +308,8 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
287
308
|
profile("fable-opus-codex", ["anthropic", "openai-codex"], {
|
|
288
309
|
default: "anthropic/claude-fable-5:high",
|
|
289
310
|
executor: "openai-codex/gpt-5.6-terra:medium",
|
|
290
|
-
planner: "anthropic/claude-opus-
|
|
291
|
-
critic: "anthropic/claude-opus-
|
|
311
|
+
planner: "anthropic/claude-opus-5:medium",
|
|
312
|
+
critic: "anthropic/claude-opus-5:high",
|
|
292
313
|
architect: "openai-codex/gpt-5.6-sol:xhigh",
|
|
293
314
|
}),
|
|
294
315
|
];
|
|
@@ -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" },
|
|
@@ -2564,13 +2564,14 @@ export class ModelRegistry {
|
|
|
2564
2564
|
async getApiKey(
|
|
2565
2565
|
model: Model<Api>,
|
|
2566
2566
|
sessionId?: string,
|
|
2567
|
-
options: { credentialSelector?: AuthCredentialSelector } = {},
|
|
2567
|
+
options: { credentialSelector?: AuthCredentialSelector; signal?: AbortSignal } = {},
|
|
2568
2568
|
): Promise<string | undefined> {
|
|
2569
2569
|
return this.#getApiKeyOrNoAuth(model.provider, () =>
|
|
2570
2570
|
this.authStorage.getApiKey(model.provider, sessionId, {
|
|
2571
2571
|
baseUrl: model.baseUrl,
|
|
2572
2572
|
modelId: model.id,
|
|
2573
2573
|
credentialSelector: options.credentialSelector,
|
|
2574
|
+
signal: options.signal,
|
|
2574
2575
|
}),
|
|
2575
2576
|
);
|
|
2576
2577
|
}
|
|
@@ -2582,12 +2583,13 @@ export class ModelRegistry {
|
|
|
2582
2583
|
provider: string,
|
|
2583
2584
|
sessionId?: string,
|
|
2584
2585
|
baseUrl?: string,
|
|
2585
|
-
options: { credentialSelector?: AuthCredentialSelector } = {},
|
|
2586
|
+
options: { credentialSelector?: AuthCredentialSelector; signal?: AbortSignal } = {},
|
|
2586
2587
|
): Promise<string | undefined> {
|
|
2587
2588
|
return this.#getApiKeyOrNoAuth(provider, () =>
|
|
2588
2589
|
this.authStorage.getApiKey(provider, sessionId, {
|
|
2589
2590
|
baseUrl,
|
|
2590
2591
|
credentialSelector: options.credentialSelector,
|
|
2592
|
+
signal: options.signal,
|
|
2591
2593
|
}),
|
|
2592
2594
|
);
|
|
2593
2595
|
}
|
|
@@ -309,7 +309,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
309
309
|
},
|
|
310
310
|
"notifications.telegram.toolActivity.enabled": {
|
|
311
311
|
type: "boolean",
|
|
312
|
-
default:
|
|
312
|
+
default: false,
|
|
313
313
|
ui: {
|
|
314
314
|
tab: "notifications",
|
|
315
315
|
label: "Telegram Tool Activity",
|
|
@@ -501,6 +501,11 @@ export const SETTINGS_SCHEMA = {
|
|
|
501
501
|
default: 0.05,
|
|
502
502
|
validate: (value: number) => Number.isFinite(value) && value > 0 && value <= 1,
|
|
503
503
|
},
|
|
504
|
+
"gjc.ralplan.maxIterations": {
|
|
505
|
+
type: "number",
|
|
506
|
+
default: 5,
|
|
507
|
+
validate: (value: number) => Number.isInteger(value) && value >= 1 && value <= 20,
|
|
508
|
+
},
|
|
504
509
|
|
|
505
510
|
// ────────────────────────────────────────────────────────────────────────
|
|
506
511
|
// Appearance
|
|
@@ -1244,7 +1249,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
1244
1249
|
ui: {
|
|
1245
1250
|
tab: "interaction",
|
|
1246
1251
|
label: "Mouse Support",
|
|
1247
|
-
description:
|
|
1252
|
+
description:
|
|
1253
|
+
"Enable GJC session scrolling, drag-to-copy text selection, and overlay row selection with the mouse. Disabled by default to preserve native terminal or tmux scrollback and selection.",
|
|
1248
1254
|
},
|
|
1249
1255
|
},
|
|
1250
1256
|
// Conversation flow
|
|
@@ -2653,6 +2659,47 @@ export const SETTINGS_SCHEMA = {
|
|
|
2653
2659
|
description: "How often the resource GC sweeps browser tabs and stale screenshot directories.",
|
|
2654
2660
|
},
|
|
2655
2661
|
},
|
|
2662
|
+
"memoryGuard.enabled": {
|
|
2663
|
+
type: "boolean",
|
|
2664
|
+
default: false,
|
|
2665
|
+
},
|
|
2666
|
+
"memoryGuard.checkIntervalMs": {
|
|
2667
|
+
type: "number",
|
|
2668
|
+
default: 30_000,
|
|
2669
|
+
validate: (value: number) => Number.isFinite(value) && value > 0,
|
|
2670
|
+
},
|
|
2671
|
+
"memoryGuard.gcThresholdPercent": {
|
|
2672
|
+
type: "number",
|
|
2673
|
+
default: 70,
|
|
2674
|
+
validate: (value: number) => Number.isFinite(value) && value >= 0 && value <= 100,
|
|
2675
|
+
},
|
|
2676
|
+
"memoryGuard.restartThresholdPercent": {
|
|
2677
|
+
type: "number",
|
|
2678
|
+
default: 85,
|
|
2679
|
+
validate: (value: number) => Number.isFinite(value) && value >= 0 && value <= 100,
|
|
2680
|
+
},
|
|
2681
|
+
"memoryGuard.restartThresholdWindowMs": {
|
|
2682
|
+
type: "number",
|
|
2683
|
+
default: 90_000,
|
|
2684
|
+
validate: (value: number) => Number.isFinite(value) && value > 0,
|
|
2685
|
+
},
|
|
2686
|
+
"memoryGuard.cooldownMs": {
|
|
2687
|
+
type: "number",
|
|
2688
|
+
default: 600_000,
|
|
2689
|
+
validate: (value: number) => Number.isFinite(value) && value >= 0,
|
|
2690
|
+
},
|
|
2691
|
+
"memoryGuard.parentReserveMb": {
|
|
2692
|
+
type: "number",
|
|
2693
|
+
default: 1024,
|
|
2694
|
+
validate: (value: number) =>
|
|
2695
|
+
Number.isFinite(value) && value >= 0 && value <= Number.MAX_SAFE_INTEGER / (1024 * 1024),
|
|
2696
|
+
},
|
|
2697
|
+
"memoryGuard.policyLimitMb": {
|
|
2698
|
+
type: "number",
|
|
2699
|
+
default: 0,
|
|
2700
|
+
validate: (value: number) =>
|
|
2701
|
+
Number.isFinite(value) && value >= 0 && value <= Number.MAX_SAFE_INTEGER / (1024 * 1024),
|
|
2702
|
+
},
|
|
2656
2703
|
|
|
2657
2704
|
"computer.enabled": {
|
|
2658
2705
|
type: "boolean",
|
|
@@ -3844,6 +3891,17 @@ export interface ShellMinimizerSettings {
|
|
|
3844
3891
|
maxCaptureBytes: number;
|
|
3845
3892
|
}
|
|
3846
3893
|
|
|
3894
|
+
export interface MemoryGuardSettings {
|
|
3895
|
+
enabled: boolean;
|
|
3896
|
+
checkIntervalMs: number;
|
|
3897
|
+
gcThresholdPercent: number;
|
|
3898
|
+
restartThresholdPercent: number;
|
|
3899
|
+
restartThresholdWindowMs: number;
|
|
3900
|
+
cooldownMs: number;
|
|
3901
|
+
parentReserveMb: number;
|
|
3902
|
+
policyLimitMb: number;
|
|
3903
|
+
}
|
|
3904
|
+
|
|
3847
3905
|
export interface NotificationsSettings {
|
|
3848
3906
|
enabled: boolean;
|
|
3849
3907
|
telegram: {
|
|
@@ -3902,6 +3960,7 @@ export interface GroupTypeMap {
|
|
|
3902
3960
|
statusLine: StatusLineSettings;
|
|
3903
3961
|
thinkingBudgets: ThinkingBudgetsSettings;
|
|
3904
3962
|
stt: SttSettings;
|
|
3963
|
+
memoryGuard: MemoryGuardSettings;
|
|
3905
3964
|
modelRoles: Record<string, ModelSelectorValue>;
|
|
3906
3965
|
modelTags: ModelTagsSettings;
|
|
3907
3966
|
cycleOrder: string[];
|
package/src/config/settings.ts
CHANGED
|
@@ -363,6 +363,7 @@ export class Settings implements NotificationSettingsReader {
|
|
|
363
363
|
/** Pending debounced ordinary save; its queue slot is reserved immediately. */
|
|
364
364
|
#saveTimer?: NodeJS.Timeout;
|
|
365
365
|
#savePromise?: Promise<void>;
|
|
366
|
+
#changeListeners = new Set<(path: SettingPath) => void>();
|
|
366
367
|
#pendingSaveSlot?: PendingSaveSlot;
|
|
367
368
|
|
|
368
369
|
/** Legacy fallback migration warnings emitted once per settings instance. */
|
|
@@ -522,6 +523,11 @@ export class Settings implements NotificationSettingsReader {
|
|
|
522
523
|
return structuredClone(this.#schemaReport);
|
|
523
524
|
}
|
|
524
525
|
|
|
526
|
+
onChanged(listener: (path: SettingPath) => void): () => void {
|
|
527
|
+
this.#changeListeners.add(listener);
|
|
528
|
+
return () => this.#changeListeners.delete(listener);
|
|
529
|
+
}
|
|
530
|
+
|
|
525
531
|
/**
|
|
526
532
|
* Set a setting value (sync).
|
|
527
533
|
* Updates global settings and reserves its background persistence slot before
|
|
@@ -555,6 +561,7 @@ export class Settings implements NotificationSettingsReader {
|
|
|
555
561
|
|
|
556
562
|
const hook = SETTING_HOOKS[path];
|
|
557
563
|
if (hook) hook(value, prev);
|
|
564
|
+
for (const listener of this.#changeListeners) listener(path);
|
|
558
565
|
}
|
|
559
566
|
|
|
560
567
|
/**
|
|
@@ -579,6 +586,7 @@ export class Settings implements NotificationSettingsReader {
|
|
|
579
586
|
|
|
580
587
|
const hook = SETTING_HOOKS[path];
|
|
581
588
|
if (hook) hook(this.get(path), prev);
|
|
589
|
+
for (const listener of this.#changeListeners) listener(path);
|
|
582
590
|
}
|
|
583
591
|
|
|
584
592
|
/**
|
|
@@ -67,14 +67,15 @@ The consensus workflow:
|
|
|
67
67
|
- **Plan-only Critic lane**: independently check quality, principle-option consistency, alternatives, risks, acceptance criteria, and verification; when the plan is thin, request concrete expansion rather than only defects. Persist with `gjc ralplan --write --stage critic --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`, then return receipt/path plus `OKAY`/`ITERATE`/`REJECT`.
|
|
68
68
|
- **Sequential fallback**: if Critic must evaluate Architect findings, verdict, antithesis, tradeoffs, synthesis, status, or any Architect-produced artifact, await the Architect result before issuing that Architect-dependent Critic pass.
|
|
69
69
|
4. **Review join gate**: before consensus, revision, reconciliation, finalization, or approval, verify both Architect and Critic receipts/verdicts exist for the same Planner artifact/pass (`path`, `sha256`, `stage_n`). A non-`CLEAR` Architect verdict, non-`APPROVE` Architect decision, or any non-`OKAY` Critic verdict routes back to Planner revision; do not finalize from only one review lane.
|
|
70
|
-
5. **Re-review loop** (max 5 iterations): Any non-`OKAY` Critic verdict (`ITERATE` or `REJECT`) or Architect result that is not `CLEAR`/`APPROVE` MUST run the same full closed loop:
|
|
70
|
+
5. **Re-review loop** (max 5 iterations; **runtime-enforced**): Any non-`OKAY` Critic verdict (`ITERATE` or `REJECT`) or Architect result that is not `CLEAR`/`APPROVE` MUST run the same full closed loop:
|
|
71
71
|
a. Collect Architect + Critic feedback
|
|
72
72
|
b. Revise the plan by resuming the SAME persisted Planner subagent with consolidated Architect + Critic feedback (see **Persisted Planner** below); fall back to a fresh Planner spawn only per the fallback routing table
|
|
73
73
|
c. Return to the review fan-out or sequential fallback path above
|
|
74
74
|
- Persist each Planner revision with `gjc ralplan --write --stage revision --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json` before re-review, then pass the receipt/path forward instead of duplicating the full revision markdown in the parent conversation.
|
|
75
75
|
d. Re-join Architect and Critic verdicts for the same revised Planner artifact/pass
|
|
76
76
|
e. Repeat this loop until Critic returns `OKAY` **and** Architect is `CLEAR`/`APPROVE` for the same Planner artifact/pass, or 5 iterations are reached
|
|
77
|
-
f. If 5 iterations are reached without Critic `OKAY` plus Architect `CLEAR`/`APPROVE`,
|
|
77
|
+
f. If 5 iterations are reached without Critic `OKAY` plus Architect `CLEAR`/`APPROVE`, **stop opening further planner/revision passes**. Present the best version to the user (interactive) or surface `PLANNING-STUCK` (headless). Do **not** auto-start implementation.
|
|
78
|
+
g. **Runtime budget (#3165):** native `gjc ralplan --write` refuses a new `planner`/`revision` that would open consensus iteration **> max** (default **5**, overridable via `gjc.ralplan.maxIterations` in project/user `.gjc/settings.json`, integer 1..20). Cap uses the same iteration definition as the HUD (`planner`/`revision` openers in `index.jsonl`). Overflow exits **3**, prints operator-visible **`PLANNING-STUCK`** on stdout (and stderr detail; JSON includes `planning_stuck: true`), and still allows `architect`/`critic` within an already-opened pass plus `post-interview`/`adr`/`final` so the best plan can be escalated to `pending approval` without auto-execution. A new `--run-id` starts a fresh budget.
|
|
78
79
|
6. **Post-ralplan interview** (intent reconciliation gate): After the review join gate has both Critic `OKAY` and Architect `CLEAR`/`APPROVE` for the same Planner artifact/pass, and before the plan is finalized, reconcile the consensus plan against the user's actual intent. The goal is to make sure ralplan did not silently bake in assumptions that conflict with what the user wants.
|
|
79
80
|
a. **Collect open items** from the run: every assumption the Planner/Architect/Critic resolved by assumption rather than by stated fact, every ambiguity flagged during review, and every decision the loop made without explicit user input. Source these from the persisted `planner`/`architect`/`critic`/`revision` stage artifacts, not from memory.
|
|
80
81
|
b. **Cross-check prior context for conflicts**: glob `.gjc/_session-{sessionid}/specs/deep-interview-*.md` and other prior specs/plans/context relevant by topic. For each, list points where the consensus plan contradicts, weakens, or expands beyond a previously crystallized decision, constraint, or non-goal. Cite the conflicting artifact and line/section.
|
|
@@ -103,6 +104,24 @@ The consensus workflow:
|
|
|
103
104
|
|
|
104
105
|
> **Important:** Architect and Critic MAY run in the same parallel batch only for the plan-only Critic lane after Planner persistence. Any Architect-dependent Critic pass MUST remain sequential: await Architect before issuing Critic, then apply the same review join gate before consensus.
|
|
105
106
|
|
|
107
|
+
## Consensus iteration cap (operator contract)
|
|
108
|
+
|
|
109
|
+
- Default max consensus iterations: **5** (`gjc.ralplan.maxIterations`).
|
|
110
|
+
- On cap: exit code **3**, marker **`PLANNING-STUCK`** (stdout), no silent re-loop, no automatic ultragoal/team handoff. Opener budget is `max(index.jsonl openers, on-disk stage-*-{planner,revision}.md count)` so a missing/empty/malformed ledger cannot fail open after prior openers.
|
|
111
|
+
- Headless/CI: treat `PLANNING-STUCK` / exit 3 as terminal planning failure for orchestration/watchdogs.
|
|
112
|
+
- Interactive: present best existing plan via the final approval gate; residual critic findings stay as caveats.
|
|
113
|
+
- Override example (project `.gjc/settings.json`):
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"gjc": {
|
|
118
|
+
"ralplan": {
|
|
119
|
+
"maxIterations": 3
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
106
125
|
|
|
107
126
|
Follow this ralplan-internal consensus workflow for consensus mode details.
|
|
108
127
|
|
|
@@ -279,7 +279,7 @@ An ultragoal story cannot be checkpointed `complete` until the active agent has
|
|
|
279
279
|
- architecture-side: system boundaries, layering, data/control flow, operational risks.
|
|
280
280
|
- product-side: user-visible behavior, acceptance criteria, edge cases, regressions.
|
|
281
281
|
- code-side: maintainability, tests, integration points, and unsafe shortcuts.
|
|
282
|
-
5. Delegate an `executor` QA/red-team lane to build and run the e2e/
|
|
282
|
+
5. Delegate an `executor` QA/red-team lane with typed `executionMode: "ultragoal-red-team"` (preferred) — or assignment text that explicitly labels Ultragoal completion QA/red-team — to build and run the e2e/red-teaming QA suite appropriate for the story. A bare `executorQa` field-name mention is not enough to activate the mode. This lane must try to break the change, not just confirm the happy path. It must start from the approved plan/spec/acceptance criteria, then user-facing contracts, and only then implementation code as supporting evidence. Plan/code mismatches are blockers, not items to paper over with implementation intent.
|
|
283
283
|
6. The executor QA/red-team lane must prove evidence by the real surface under test:
|
|
284
284
|
- GUI/web surfaces require a valid automation transcript plus a non-uniform screenshot. Bare `inlineEvidence` text or typed receipts never prove live GUI/web execution.
|
|
285
285
|
- CLI surfaces require runtime argv replay: `schemaVersion: 1`, `kind: "cli-replay"`, `replaySafe: true`, an allowlisted argv `command`, and replayed output validation. The complete field-by-field replay schema, command allowlist, and `replayExempt` audit contract are specified once in the "For CLI replay artifacts" paragraph below the quality-gate JSON; follow it exactly.
|
|
@@ -30,7 +30,7 @@ function toolResultHasError(result: AgentToolResult): boolean {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function getTool(session: ToolSession, name: string): AgentTool {
|
|
33
|
-
const tool = session.getToolByName?.(name);
|
|
33
|
+
const tool = session.getToolForExecution ? session.getToolForExecution(name) : session.getToolByName?.(name);
|
|
34
34
|
if (!tool) {
|
|
35
35
|
throw new ToolError(`Unknown tool from js runtime: ${name}`);
|
|
36
36
|
}
|
|
@@ -201,6 +201,7 @@ export class ExtensionRunner {
|
|
|
201
201
|
#getAllToolsFn: ExtensionContext["getAllTools"] = () => [];
|
|
202
202
|
#getResolveToolFn: ExtensionContext["resolveTool"] = () => undefined;
|
|
203
203
|
#cycleModelFn: ExtensionContextActions["cycleModel"] = undefined;
|
|
204
|
+
#setModelProfileFn: ExtensionContextActions["setModelProfile"] = undefined;
|
|
204
205
|
#cycleThinkingLevelFn: ExtensionContextActions["cycleThinkingLevel"] = undefined;
|
|
205
206
|
#setQueueModeFn: ExtensionContextActions["setQueueMode"] = undefined;
|
|
206
207
|
#getSkillStateFn: ExtensionContextActions["getSkillState"] = undefined;
|
|
@@ -313,6 +314,7 @@ export class ExtensionRunner {
|
|
|
313
314
|
this.#getAllToolsFn = contextActions.getAllTools ?? (() => []);
|
|
314
315
|
this.#getResolveToolFn = contextActions.resolveTool ?? (() => undefined);
|
|
315
316
|
this.#cycleModelFn = contextActions.cycleModel;
|
|
317
|
+
this.#setModelProfileFn = contextActions.setModelProfile;
|
|
316
318
|
this.#cycleThinkingLevelFn = contextActions.cycleThinkingLevel;
|
|
317
319
|
this.#setQueueModeFn = contextActions.setQueueMode;
|
|
318
320
|
this.#getSkillStateFn = contextActions.getSkillState;
|
|
@@ -562,6 +564,7 @@ export class ExtensionRunner {
|
|
|
562
564
|
getAllTools: () => this.#getAllToolsFn(),
|
|
563
565
|
resolveTool: name => this.#getResolveToolFn(name),
|
|
564
566
|
cycleModel: async () => await this.#cycleModelFn?.(),
|
|
567
|
+
setModelProfile: async name => (await this.#setModelProfileFn?.(name)) ?? false,
|
|
565
568
|
cycleThinkingLevel: () => this.#cycleThinkingLevelFn?.(),
|
|
566
569
|
setQueueMode: (kind, mode) => this.#setQueueModeFn?.(kind, mode) ?? false,
|
|
567
570
|
invokeSkill: async (name, args) => await this.#invokeSkillFn?.(name, args),
|
|
@@ -581,6 +584,7 @@ export class ExtensionRunner {
|
|
|
581
584
|
setSdkPermissionProvider: provider => this.#setSdkPermissionProviderFn?.(provider),
|
|
582
585
|
sdkBindings: () => [
|
|
583
586
|
...(this.#cycleModelFn ? ["cycleModel"] : []),
|
|
587
|
+
...(this.#setModelProfileFn ? ["setModelProfile"] : []),
|
|
584
588
|
...(this.#cycleThinkingLevelFn ? ["cycleThinkingLevel"] : []),
|
|
585
589
|
...(this.#setQueueModeFn ? ["setQueueMode"] : []),
|
|
586
590
|
...(this.#getSkillStateFn ? ["getSkillState"] : []),
|
|
@@ -290,6 +290,18 @@ export interface ExtensionTranscriptEntry {
|
|
|
290
290
|
textSummary: string;
|
|
291
291
|
ts: string;
|
|
292
292
|
body?: string;
|
|
293
|
+
/**
|
|
294
|
+
* Durable, image-free message blocks used by rich transcript consumers such
|
|
295
|
+
* as ACP replay. Binary image payloads intentionally remain unavailable.
|
|
296
|
+
*/
|
|
297
|
+
content?: Array<
|
|
298
|
+
| { type: "text"; text: string }
|
|
299
|
+
| { type: "thinking"; thinking: string }
|
|
300
|
+
| { type: "toolCall"; id: string; name: string; arguments: unknown }
|
|
301
|
+
>;
|
|
302
|
+
toolCallId?: string;
|
|
303
|
+
toolName?: string;
|
|
304
|
+
isError?: boolean;
|
|
293
305
|
}
|
|
294
306
|
|
|
295
307
|
export interface ContextUsage {
|
|
@@ -361,6 +373,7 @@ export interface ExtensionContext {
|
|
|
361
373
|
resolveTool(name: string): Pick<Tool, "safeSummary" | "safeSummaryFields"> | undefined;
|
|
362
374
|
/** Session control seams used by the SDK host. */
|
|
363
375
|
cycleModel(): Promise<{ model: Model; thinkingLevel: ThinkingLevel | undefined } | undefined>;
|
|
376
|
+
setModelProfile?(name: string): Promise<boolean>;
|
|
364
377
|
cycleThinkingLevel(): ThinkingLevel | undefined;
|
|
365
378
|
setQueueMode(kind: "steering" | "follow_up" | "interrupt", mode: unknown): boolean;
|
|
366
379
|
getSkillState(): unknown;
|
|
@@ -1429,6 +1442,7 @@ export interface ExtensionContextActions {
|
|
|
1429
1442
|
clearContext?: () => Promise<boolean>;
|
|
1430
1443
|
/** Session control and query seams exposed to the per-session SDK host. */
|
|
1431
1444
|
cycleModel?: () => Promise<{ model: Model; thinkingLevel: ThinkingLevel | undefined } | undefined>;
|
|
1445
|
+
setModelProfile?: (name: string) => Promise<boolean>;
|
|
1432
1446
|
cycleThinkingLevel?: () => ThinkingLevel | undefined;
|
|
1433
1447
|
setQueueMode?: (kind: "steering" | "follow_up" | "interrupt", mode: unknown) => boolean;
|
|
1434
1448
|
getSkillState?: () => unknown;
|
|
@@ -207,8 +207,22 @@ export function deriveAmbiguityMilestone(
|
|
|
207
207
|
|
|
208
208
|
export function scoreToUnits(score: number): number {
|
|
209
209
|
if (!Number.isFinite(score) || score < 0 || score > 1) throw new RangeError("score must be finite in [0, 1]");
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Scores carry at most four decimal places, but `score * 10_000` misses the
|
|
212
|
+
* integer grid for ordinary values: `0.69 * 10_000` is `6900.000000000001` and
|
|
213
|
+
* `0.07 * 10_000` is `700.0000000000001`. Ambiguity is fed straight back in as
|
|
214
|
+
* `units / 10_000`, so testing the float product rejected most real scores.
|
|
215
|
+
*
|
|
216
|
+
* Decide from the shortest round-trip decimal instead. That is exact: every
|
|
217
|
+
* genuine 1e-4 value has at most four fraction digits, while off-grid
|
|
218
|
+
* precision keeps its digits (`0.00005`, `0.05000000000000001`) and is
|
|
219
|
+
* rejected. An epsilon on the product cannot separate those two cases -- both
|
|
220
|
+
* residuals are ~1e-13. The `[0, 1]` guard plus the four-digit cap already
|
|
221
|
+
* bound the result to an integer in `[0, 10_000]`.
|
|
222
|
+
*/
|
|
223
|
+
const decimal = /^(\d+)(?:\.(\d{1,4}))?$/.exec(String(score));
|
|
224
|
+
if (!decimal) throw new RangeError("score must be expressed in integral 1e-4 units");
|
|
225
|
+
const units = Number(decimal[1]) * 10_000 + Number((decimal[2] ?? "").padEnd(4, "0"));
|
|
212
226
|
return units;
|
|
213
227
|
}
|
|
214
228
|
|