@gajae-code/coding-agent 0.3.1 → 0.4.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 +46 -0
- package/README.md +1 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/config/model-profile-activation.d.ts +30 -0
- package/dist/types/config/model-profiles.d.ts +19 -0
- package/dist/types/config/model-registry.d.ts +25 -10
- package/dist/types/config/model-resolver.d.ts +1 -1
- package/dist/types/config/models-config-schema.d.ts +84 -0
- package/dist/types/config/settings-schema.d.ts +15 -0
- package/dist/types/edit/diff.d.ts +16 -0
- package/dist/types/edit/modes/replace.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/activation.d.ts +14 -0
- package/dist/types/extensibility/gjc-plugins/index.d.ts +9 -0
- package/dist/types/extensibility/gjc-plugins/injection.d.ts +31 -0
- package/dist/types/extensibility/gjc-plugins/loader.d.ts +3 -0
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +8 -0
- package/dist/types/extensibility/gjc-plugins/schema.d.ts +3 -0
- package/dist/types/extensibility/gjc-plugins/state.d.ts +9 -0
- package/dist/types/extensibility/gjc-plugins/tools.d.ts +8 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/validation.d.ts +4 -0
- package/dist/types/extensibility/skills.d.ts +9 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +22 -0
- package/dist/types/harness-control-plane/storage.d.ts +7 -0
- package/dist/types/lsp/client.d.ts +1 -0
- package/dist/types/main.d.ts +10 -1
- package/dist/types/modes/bridge/bridge-mode.d.ts +2 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +10 -0
- package/dist/types/modes/components/model-selector.d.ts +6 -1
- package/dist/types/modes/components/provider-onboarding-selector.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -2
- package/dist/types/modes/rpc/rpc-client.d.ts +9 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +179 -2
- package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +57 -0
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +16 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +47 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +7 -0
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +11 -1
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +3 -1
- package/dist/types/modes/shared/agent-wire/responses.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +27 -0
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +68 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +161 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +61 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +114 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +39 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
- package/dist/types/sdk.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +10 -0
- package/dist/types/session/blob-store.d.ts +17 -0
- package/dist/types/session/messages.d.ts +3 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/skill-state/active-state.d.ts +13 -0
- package/dist/types/task/executor.d.ts +1 -0
- package/dist/types/thinking.d.ts +3 -2
- package/dist/types/tools/hindsight-recall.d.ts +0 -2
- package/dist/types/tools/hindsight-reflect.d.ts +0 -2
- package/dist/types/tools/hindsight-retain.d.ts +0 -2
- package/dist/types/tools/index.d.ts +7 -4
- package/package.json +9 -7
- package/src/cli/args.ts +10 -0
- package/src/cli.ts +14 -0
- package/src/commands/harness.ts +192 -7
- package/src/commands/launch.ts +8 -0
- package/src/commands/ultragoal.ts +1 -21
- package/src/config/model-equivalence.ts +1 -1
- package/src/config/model-profile-activation.ts +157 -0
- package/src/config/model-profiles.ts +155 -0
- package/src/config/model-registry.ts +51 -5
- package/src/config/model-resolver.ts +3 -2
- package/src/config/models-config-schema.ts +42 -1
- package/src/config/settings-schema.ts +14 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +11 -1
- package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +61 -0
- package/src/defaults/gjc-defaults.ts +7 -0
- package/src/discovery/claude-plugins.ts +25 -5
- package/src/edit/diff.ts +64 -1
- package/src/edit/modes/replace.ts +60 -2
- package/src/extensibility/gjc-plugins/activation.ts +87 -0
- package/src/extensibility/gjc-plugins/index.ts +9 -0
- package/src/extensibility/gjc-plugins/injection.ts +114 -0
- package/src/extensibility/gjc-plugins/loader.ts +131 -0
- package/src/extensibility/gjc-plugins/paths.ts +66 -0
- package/src/extensibility/gjc-plugins/schema.ts +79 -0
- package/src/extensibility/gjc-plugins/state.ts +29 -0
- package/src/extensibility/gjc-plugins/tools.ts +47 -0
- package/src/extensibility/gjc-plugins/types.ts +97 -0
- package/src/extensibility/gjc-plugins/validation.ts +76 -0
- package/src/extensibility/skills.ts +39 -7
- package/src/gjc-runtime/state-runtime.ts +93 -2
- package/src/gjc-runtime/state-writer.ts +17 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +62 -2
- package/src/gjc-runtime/workflow-manifest.generated.json +5 -0
- package/src/gjc-runtime/workflow-manifest.ts +2 -2
- package/src/harness-control-plane/storage.ts +144 -2
- package/src/hashline/hash.ts +23 -0
- package/src/hooks/skill-state.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +8 -11
- package/src/lsp/client.ts +7 -0
- package/src/main.ts +67 -1
- package/src/modes/acp/acp-agent.ts +25 -2
- package/src/modes/bridge/bridge-mode.ts +124 -2
- package/src/modes/components/custom-provider-wizard.ts +318 -0
- package/src/modes/components/model-selector.ts +108 -18
- package/src/modes/components/provider-onboarding-selector.ts +6 -1
- package/src/modes/controllers/input-controller.ts +14 -2
- package/src/modes/controllers/selector-controller.ts +57 -1
- package/src/modes/prompt-action-autocomplete.ts +49 -10
- package/src/modes/rpc/rpc-client.ts +57 -3
- package/src/modes/rpc/rpc-mode.ts +67 -0
- package/src/modes/rpc/rpc-types.ts +224 -2
- package/src/modes/shared/agent-wire/approval-gate.ts +151 -0
- package/src/modes/shared/agent-wire/command-dispatch.ts +97 -4
- package/src/modes/shared/agent-wire/command-validation.ts +25 -1
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +222 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +13 -0
- package/src/modes/shared/agent-wire/handshake.ts +43 -3
- package/src/modes/shared/agent-wire/protocol.ts +7 -0
- package/src/modes/shared/agent-wire/responses.ts +2 -2
- package/src/modes/shared/agent-wire/scopes.ts +2 -0
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +341 -0
- package/src/modes/shared/agent-wire/unattended-audit.ts +175 -0
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +406 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +180 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +324 -0
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +331 -0
- package/src/modes/theme/theme.ts +6 -0
- package/src/modes/types.ts +1 -0
- package/src/prompts/memories/consolidation.md +1 -1
- package/src/prompts/memories/read-path.md +6 -7
- package/src/prompts/memories/unavailable.md +2 -2
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/recall.md +1 -0
- package/src/prompts/tools/reflect.md +1 -0
- package/src/prompts/tools/retain.md +1 -0
- package/src/runtime-mcp/client.ts +7 -4
- package/src/runtime-mcp/manager.ts +45 -13
- package/src/runtime-mcp/transports/http.ts +40 -14
- package/src/runtime-mcp/transports/stdio.ts +11 -10
- package/src/sdk.ts +48 -1
- package/src/session/agent-session.ts +211 -2
- package/src/session/blob-store.ts +84 -0
- package/src/session/messages.ts +3 -0
- package/src/session/session-manager.ts +390 -33
- package/src/session/session-storage.ts +26 -0
- package/src/setup/provider-onboarding.ts +2 -2
- package/src/skill-state/active-state.ts +89 -1
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/task/discovery.ts +7 -1
- package/src/task/executor.ts +18 -2
- package/src/task/index.ts +2 -0
- package/src/thinking.ts +8 -2
- package/src/tools/ask.ts +39 -9
- package/src/tools/hindsight-recall.ts +0 -2
- package/src/tools/hindsight-reflect.ts +0 -2
- package/src/tools/hindsight-retain.ts +0 -2
- package/src/tools/index.ts +7 -18
- package/src/tools/read.ts +3 -3
- package/src/tools/skill.ts +15 -3
- package/src/utils/edit-mode.ts +1 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { GjcModelAssignmentTargetId } from "./model-registry";
|
|
2
|
+
import type { ModelsConfig } from "./models-config-schema";
|
|
3
|
+
|
|
4
|
+
export type ModelProfileRole = GjcModelAssignmentTargetId;
|
|
5
|
+
|
|
6
|
+
export interface ModelProfileDefinition {
|
|
7
|
+
name: string;
|
|
8
|
+
requiredProviders: string[];
|
|
9
|
+
modelMapping: Partial<Record<ModelProfileRole, string>>;
|
|
10
|
+
source: "builtin" | "user";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ResolvedProfileBinding {
|
|
14
|
+
defaultSelector?: string;
|
|
15
|
+
agentModelOverrides: Partial<Record<Exclude<ModelProfileRole, "default">, string>>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseModelSelectorProvider(selector: string): string | undefined {
|
|
19
|
+
const slashIdx = selector.indexOf("/");
|
|
20
|
+
if (slashIdx <= 0) return undefined;
|
|
21
|
+
return selector.slice(0, slashIdx);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function deriveModelProfileMappedProviders(definition: Pick<ModelProfileDefinition, "modelMapping">): string[] {
|
|
25
|
+
const providers = new Set<string>();
|
|
26
|
+
for (const selector of Object.values(definition.modelMapping)) {
|
|
27
|
+
if (!selector) continue;
|
|
28
|
+
const provider = parseModelSelectorProvider(selector);
|
|
29
|
+
if (provider) providers.add(provider);
|
|
30
|
+
}
|
|
31
|
+
return [...providers].sort((a, b) => a.localeCompare(b));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function aggregateModelProfileRequiredProviders(
|
|
35
|
+
requiredProviders: readonly string[],
|
|
36
|
+
definition: Pick<ModelProfileDefinition, "modelMapping">,
|
|
37
|
+
): string[] {
|
|
38
|
+
const providers = new Set(requiredProviders);
|
|
39
|
+
for (const provider of deriveModelProfileMappedProviders(definition)) {
|
|
40
|
+
providers.add(provider);
|
|
41
|
+
}
|
|
42
|
+
return [...providers];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const profile = (
|
|
46
|
+
name: string,
|
|
47
|
+
requiredProviders: string[],
|
|
48
|
+
modelMapping: Record<ModelProfileRole, string>,
|
|
49
|
+
): ModelProfileDefinition => ({
|
|
50
|
+
name,
|
|
51
|
+
requiredProviders: aggregateModelProfileRequiredProviders(requiredProviders, { modelMapping }),
|
|
52
|
+
modelMapping,
|
|
53
|
+
source: "builtin",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
57
|
+
profile("opencode-go-eco", ["opencode-go"], {
|
|
58
|
+
default: "opencode-go/deepseek-v4-flash",
|
|
59
|
+
executor: "opencode-go/qwen3.5-plus",
|
|
60
|
+
architect: "opencode-go/glm-5",
|
|
61
|
+
planner: "opencode-go/minimax-m2.5",
|
|
62
|
+
critic: "opencode-go/kimi-k2.5",
|
|
63
|
+
}),
|
|
64
|
+
profile("opencode-go-standard", ["opencode-go"], {
|
|
65
|
+
default: "opencode-go/kimi-k2.6",
|
|
66
|
+
executor: "opencode-go/qwen3.6-plus",
|
|
67
|
+
architect: "opencode-go/glm-5.1",
|
|
68
|
+
planner: "opencode-go/minimax-m2.7",
|
|
69
|
+
critic: "opencode-go/deepseek-v4-pro",
|
|
70
|
+
}),
|
|
71
|
+
profile("opencode-go-pro", ["opencode-go"], {
|
|
72
|
+
default: "opencode-go/qwen3.7-max",
|
|
73
|
+
executor: "opencode-go/kimi-k2.6",
|
|
74
|
+
architect: "opencode-go/deepseek-v4-pro:high",
|
|
75
|
+
planner: "opencode-go/glm-5.1:high",
|
|
76
|
+
critic: "opencode-go/minimax-m2.7:high",
|
|
77
|
+
}),
|
|
78
|
+
profile("codex-eco", ["openai-codex"], {
|
|
79
|
+
default: "openai-codex/gpt-5.4-mini",
|
|
80
|
+
executor: "openai-codex/gpt-5.4-nano",
|
|
81
|
+
architect: "openai-codex/gpt-5.4-mini",
|
|
82
|
+
planner: "openai-codex/gpt-5.4-mini",
|
|
83
|
+
critic: "openai-codex/gpt-5.4-mini",
|
|
84
|
+
}),
|
|
85
|
+
profile("codex-standard", ["openai-codex"], {
|
|
86
|
+
default: "openai-codex/gpt-5.4:medium",
|
|
87
|
+
executor: "openai-codex/gpt-5.4:low",
|
|
88
|
+
architect: "openai-codex/gpt-5.4:xhigh",
|
|
89
|
+
planner: "openai-codex/gpt-5.4:medium",
|
|
90
|
+
critic: "openai-codex/gpt-5.4:high",
|
|
91
|
+
}),
|
|
92
|
+
profile("codex-pro", ["openai-codex"], {
|
|
93
|
+
default: "openai-codex/gpt-5.5",
|
|
94
|
+
executor: "openai-codex/gpt-5.2-codex",
|
|
95
|
+
architect: "openai-codex/gpt-5.1-codex-max:high",
|
|
96
|
+
planner: "openai-codex/gpt-5.5:high",
|
|
97
|
+
critic: "openai-codex/gpt-5.3-codex-spark:high",
|
|
98
|
+
}),
|
|
99
|
+
profile("opencode-go-codex-eco", ["opencode-go", "openai-codex"], {
|
|
100
|
+
default: "opencode-go/deepseek-v4-flash",
|
|
101
|
+
executor: "opencode-go/qwen3.5-plus",
|
|
102
|
+
architect: "openai-codex/gpt-5.4-mini",
|
|
103
|
+
planner: "openai-codex/gpt-5.4-mini",
|
|
104
|
+
critic: "openai-codex/gpt-5.4-mini",
|
|
105
|
+
}),
|
|
106
|
+
profile("opencode-go-codex-standard", ["opencode-go", "openai-codex"], {
|
|
107
|
+
default: "opencode-go/kimi-k2.6",
|
|
108
|
+
executor: "opencode-go/qwen3.6-plus",
|
|
109
|
+
architect: "openai-codex/gpt-5.4",
|
|
110
|
+
planner: "openai-codex/gpt-5.4",
|
|
111
|
+
critic: "openai-codex/gpt-5.4",
|
|
112
|
+
}),
|
|
113
|
+
profile("opencode-go-codex-pro", ["opencode-go", "openai-codex"], {
|
|
114
|
+
default: "opencode-go/qwen3.7-max",
|
|
115
|
+
executor: "opencode-go/kimi-k2.6",
|
|
116
|
+
architect: "openai-codex/gpt-5.1-codex-max:high",
|
|
117
|
+
planner: "openai-codex/gpt-5.5:high",
|
|
118
|
+
critic: "openai-codex/gpt-5.3-codex-spark:high",
|
|
119
|
+
}),
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
export function mergeModelProfiles(userProfiles?: ModelsConfig["profiles"]): Map<string, ModelProfileDefinition> {
|
|
123
|
+
const profiles = new Map<string, ModelProfileDefinition>();
|
|
124
|
+
for (const definition of BUILTIN_MODEL_PROFILES) {
|
|
125
|
+
profiles.set(definition.name, {
|
|
126
|
+
...definition,
|
|
127
|
+
requiredProviders: [...definition.requiredProviders],
|
|
128
|
+
modelMapping: { ...definition.modelMapping },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
for (const [name, definition] of Object.entries(userProfiles ?? {})) {
|
|
132
|
+
const modelMapping = { ...definition.model_mapping };
|
|
133
|
+
profiles.set(name, {
|
|
134
|
+
name,
|
|
135
|
+
requiredProviders: aggregateModelProfileRequiredProviders(definition.required_providers, { modelMapping }),
|
|
136
|
+
modelMapping,
|
|
137
|
+
source: "user",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return profiles;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function resolveProfileBindings(definition: ModelProfileDefinition): ResolvedProfileBinding {
|
|
144
|
+
const { default: defaultSelector, executor, architect, planner, critic } = definition.modelMapping;
|
|
145
|
+
const agentModelOverrides: ResolvedProfileBinding["agentModelOverrides"] = {};
|
|
146
|
+
if (executor !== undefined) agentModelOverrides.executor = executor;
|
|
147
|
+
if (architect !== undefined) agentModelOverrides.architect = architect;
|
|
148
|
+
if (planner !== undefined) agentModelOverrides.planner = planner;
|
|
149
|
+
if (critic !== undefined) agentModelOverrides.critic = critic;
|
|
150
|
+
return { defaultSelector, agentModelOverrides };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function formatAvailableProfileNames(profiles: ReadonlyMap<string, ModelProfileDefinition>): string {
|
|
154
|
+
return [...profiles.keys()].sort((a, b) => a.localeCompare(b)).join(", ");
|
|
155
|
+
}
|
|
@@ -2,6 +2,7 @@ import * as path from "node:path";
|
|
|
2
2
|
import {
|
|
3
3
|
type Api,
|
|
4
4
|
type AssistantMessageEventStream,
|
|
5
|
+
type CacheRetention,
|
|
5
6
|
type Context,
|
|
6
7
|
createModelManager,
|
|
7
8
|
enrichModelThinking,
|
|
@@ -43,6 +44,7 @@ import {
|
|
|
43
44
|
formatCanonicalVariantSelector,
|
|
44
45
|
type ModelEquivalenceConfig,
|
|
45
46
|
} from "./model-equivalence";
|
|
47
|
+
import { type ModelProfileDefinition, mergeModelProfiles } from "./model-profiles";
|
|
46
48
|
import {
|
|
47
49
|
type ModelOverride,
|
|
48
50
|
type ModelsConfig,
|
|
@@ -239,6 +241,7 @@ interface ProviderValidationConfig {
|
|
|
239
241
|
compat?: Model<Api>["compat"];
|
|
240
242
|
requestTransform?: ModelRequestTransform;
|
|
241
243
|
disableStrictTools?: boolean;
|
|
244
|
+
cacheRetention?: CacheRetention;
|
|
242
245
|
modelOverrides?: Record<string, unknown>;
|
|
243
246
|
models: ProviderValidationModel[];
|
|
244
247
|
}
|
|
@@ -266,11 +269,12 @@ function validateProviderConfiguration(
|
|
|
266
269
|
!config.apiKeyEnv &&
|
|
267
270
|
!config.disableStrictTools &&
|
|
268
271
|
!config.requestTransform &&
|
|
272
|
+
!config.cacheRetention &&
|
|
269
273
|
!hasModelOverrides &&
|
|
270
274
|
!config.discovery
|
|
271
275
|
) {
|
|
272
276
|
throw new Error(
|
|
273
|
-
`Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "compat", "requestTransform", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
|
|
277
|
+
`Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "compat", "requestTransform", "cacheRetention", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
|
|
274
278
|
);
|
|
275
279
|
}
|
|
276
280
|
}
|
|
@@ -383,6 +387,7 @@ export const ModelsConfigFile = new ConfigFile<ModelsConfig>("models", ModelsCon
|
|
|
383
387
|
compat: providerConfig.compat,
|
|
384
388
|
requestTransform: providerConfig.requestTransform,
|
|
385
389
|
disableStrictTools: providerConfig.disableStrictTools,
|
|
390
|
+
cacheRetention: providerConfig.cacheRetention,
|
|
386
391
|
modelOverrides: providerConfig.modelOverrides,
|
|
387
392
|
models: (providerConfig.models ?? []) as ProviderValidationModel[],
|
|
388
393
|
},
|
|
@@ -401,6 +406,7 @@ interface ProviderOverride {
|
|
|
401
406
|
compat?: Model<Api>["compat"];
|
|
402
407
|
transport?: Model<Api>["transport"];
|
|
403
408
|
requestTransform?: ModelRequestTransform;
|
|
409
|
+
cacheRetention?: CacheRetention;
|
|
404
410
|
}
|
|
405
411
|
|
|
406
412
|
const PROVIDER_BASE_URL_ENV_ALIASES: Record<string, readonly string[]> = {
|
|
@@ -448,7 +454,10 @@ function resolveProviderBaseUrlFromEnv(provider: string): string | undefined {
|
|
|
448
454
|
export function mergeDiscoveredModel<TApi extends Api>(
|
|
449
455
|
model: Model<TApi>,
|
|
450
456
|
existing: Model<Api> | undefined,
|
|
451
|
-
providerOverride?: Pick<
|
|
457
|
+
providerOverride?: Pick<
|
|
458
|
+
ProviderOverride,
|
|
459
|
+
"baseUrl" | "headers" | "transport" | "requestTransform" | "cacheRetention"
|
|
460
|
+
>,
|
|
452
461
|
): Model<TApi> {
|
|
453
462
|
if (existing) {
|
|
454
463
|
return {
|
|
@@ -459,6 +468,7 @@ export function mergeDiscoveredModel<TApi extends Api>(
|
|
|
459
468
|
mergeRequestTransform(existing.requestTransform, model.requestTransform),
|
|
460
469
|
providerOverride?.requestTransform,
|
|
461
470
|
),
|
|
471
|
+
cacheRetention: model.cacheRetention ?? existing.cacheRetention ?? providerOverride?.cacheRetention,
|
|
462
472
|
};
|
|
463
473
|
}
|
|
464
474
|
if (providerOverride) {
|
|
@@ -480,6 +490,7 @@ interface DiscoveryProviderConfig {
|
|
|
480
490
|
headers?: Record<string, string>;
|
|
481
491
|
compat?: Model<Api>["compat"];
|
|
482
492
|
requestTransform?: ModelRequestTransform;
|
|
493
|
+
cacheRetention?: CacheRetention;
|
|
483
494
|
discovery: ProviderDiscovery;
|
|
484
495
|
optional?: boolean;
|
|
485
496
|
}
|
|
@@ -511,6 +522,7 @@ interface CustomModelsResult {
|
|
|
511
522
|
configuredProviders?: Set<string>;
|
|
512
523
|
equivalence?: ModelEquivalenceConfig;
|
|
513
524
|
modelBindings?: NonNullable<ModelsConfig["modelBindings"]>;
|
|
525
|
+
profiles?: ModelsConfig["profiles"];
|
|
514
526
|
error?: ConfigError;
|
|
515
527
|
found: boolean;
|
|
516
528
|
}
|
|
@@ -676,6 +688,7 @@ function applyModelOverride(model: Model<Api>, override: ModelOverride): Model<A
|
|
|
676
688
|
if (override.reasoning !== undefined) result.reasoning = override.reasoning;
|
|
677
689
|
if (override.thinking !== undefined) result.thinking = override.thinking as ThinkingConfig;
|
|
678
690
|
if (override.input !== undefined) result.input = override.input as ("text" | "image")[];
|
|
691
|
+
if (override.cacheRetention !== undefined) result.cacheRetention = override.cacheRetention;
|
|
679
692
|
if (override.contextWindow !== undefined) result.contextWindow = override.contextWindow;
|
|
680
693
|
if (override.maxTokens !== undefined) result.maxTokens = override.maxTokens;
|
|
681
694
|
if (override.contextPromotionTarget !== undefined) result.contextPromotionTarget = override.contextPromotionTarget;
|
|
@@ -714,6 +727,7 @@ interface CustomModelDefinitionLike {
|
|
|
714
727
|
premiumMultiplier?: number;
|
|
715
728
|
wireModelId?: string;
|
|
716
729
|
requestTransform?: ModelRequestTransform;
|
|
730
|
+
cacheRetention?: CacheRetention;
|
|
717
731
|
}
|
|
718
732
|
|
|
719
733
|
interface CustomModelBuildOptions {
|
|
@@ -738,6 +752,7 @@ type CustomModelOverlay = {
|
|
|
738
752
|
premiumMultiplier?: number;
|
|
739
753
|
wireModelId?: string;
|
|
740
754
|
requestTransform?: ModelRequestTransform;
|
|
755
|
+
cacheRetention?: CacheRetention;
|
|
741
756
|
isOAuth?: boolean;
|
|
742
757
|
};
|
|
743
758
|
|
|
@@ -789,6 +804,7 @@ function buildCustomModelOverlay(
|
|
|
789
804
|
providerCompat: Model<Api>["compat"] | undefined,
|
|
790
805
|
providerRequestTransform: ModelRequestTransform | undefined,
|
|
791
806
|
providerAuth: ProviderAuthMode | undefined,
|
|
807
|
+
providerCacheRetention: CacheRetention | undefined,
|
|
792
808
|
modelDef: CustomModelDefinitionLike,
|
|
793
809
|
): CustomModelOverlay | undefined {
|
|
794
810
|
const api = modelDef.api ?? providerApi;
|
|
@@ -811,6 +827,7 @@ function buildCustomModelOverlay(
|
|
|
811
827
|
wireModelId: modelDef.wireModelId,
|
|
812
828
|
contextPromotionTarget: modelDef.contextPromotionTarget,
|
|
813
829
|
premiumMultiplier: modelDef.premiumMultiplier,
|
|
830
|
+
cacheRetention: modelDef.cacheRetention ?? providerCacheRetention,
|
|
814
831
|
isOAuth: resolveCustomModelIsOAuth(api, providerAuth),
|
|
815
832
|
};
|
|
816
833
|
}
|
|
@@ -911,6 +928,7 @@ function finalizeCustomModel(model: CustomModelOverlay, options: CustomModelBuil
|
|
|
911
928
|
contextPromotionTarget: resolvedModel.contextPromotionTarget,
|
|
912
929
|
wireModelId: resolvedModel.wireModelId,
|
|
913
930
|
requestTransform: resolvedModel.requestTransform,
|
|
931
|
+
cacheRetention: resolvedModel.cacheRetention ?? reference?.cacheRetention,
|
|
914
932
|
premiumMultiplier: resolvedModel.premiumMultiplier,
|
|
915
933
|
isOAuth: resolvedModel.isOAuth,
|
|
916
934
|
} as Model<Api>);
|
|
@@ -954,6 +972,7 @@ export class ModelRegistry {
|
|
|
954
972
|
#modelOverrides: Map<string, Map<string, ModelOverride>> = new Map();
|
|
955
973
|
#equivalenceConfig: ModelEquivalenceConfig | undefined;
|
|
956
974
|
#configuredModelBindings: NonNullable<ModelsConfig["modelBindings"]> | undefined;
|
|
975
|
+
#modelProfiles: Map<string, ModelProfileDefinition> = mergeModelProfiles();
|
|
957
976
|
#modelBindingsTargetSettings: Settings | undefined;
|
|
958
977
|
#appliedModelBindingRoles = new Set<string>();
|
|
959
978
|
#appliedAgentModelBindingOverrides = new Set<string>();
|
|
@@ -1093,6 +1112,7 @@ export class ModelRegistry {
|
|
|
1093
1112
|
configuredProviders = new Set(),
|
|
1094
1113
|
equivalence,
|
|
1095
1114
|
modelBindings,
|
|
1115
|
+
profiles,
|
|
1096
1116
|
error: configError,
|
|
1097
1117
|
} = this.#loadCustomModels();
|
|
1098
1118
|
this.#configError = configError;
|
|
@@ -1103,6 +1123,7 @@ export class ModelRegistry {
|
|
|
1103
1123
|
this.#modelOverrides = modelOverrides;
|
|
1104
1124
|
this.#equivalenceConfig = equivalence;
|
|
1105
1125
|
this.#configuredModelBindings = modelBindings;
|
|
1126
|
+
this.#modelProfiles = mergeModelProfiles(profiles);
|
|
1106
1127
|
|
|
1107
1128
|
this.#addImplicitDiscoverableProviders(configuredProviders);
|
|
1108
1129
|
const builtInModels = this.#applyHardcodedModelPolicies(this.#loadBuiltInModels(overrides));
|
|
@@ -1134,6 +1155,7 @@ export class ModelRegistry {
|
|
|
1134
1155
|
return {
|
|
1135
1156
|
...withTransportOverride,
|
|
1136
1157
|
compat: mergeCompat(m.compat, providerOverride.compat),
|
|
1158
|
+
cacheRetention: m.cacheRetention ?? providerOverride.cacheRetention,
|
|
1137
1159
|
};
|
|
1138
1160
|
});
|
|
1139
1161
|
});
|
|
@@ -1290,6 +1312,7 @@ export class ModelRegistry {
|
|
|
1290
1312
|
requestTransform: providerConfig.requestTransform
|
|
1291
1313
|
? mergeRequestTransform(undefined, providerConfig.requestTransform)
|
|
1292
1314
|
: undefined,
|
|
1315
|
+
cacheRetention: normalized.cacheRetention ?? providerConfig.cacheRetention,
|
|
1293
1316
|
};
|
|
1294
1317
|
});
|
|
1295
1318
|
}
|
|
@@ -1343,6 +1366,7 @@ export class ModelRegistry {
|
|
|
1343
1366
|
discoverableProviders: [],
|
|
1344
1367
|
configuredProviders: new Set(),
|
|
1345
1368
|
error,
|
|
1369
|
+
profiles: undefined,
|
|
1346
1370
|
found: true,
|
|
1347
1371
|
};
|
|
1348
1372
|
} else if (status === "not-found") {
|
|
@@ -1353,6 +1377,7 @@ export class ModelRegistry {
|
|
|
1353
1377
|
keylessProviders: new Set(),
|
|
1354
1378
|
discoverableProviders: [],
|
|
1355
1379
|
configuredProviders: new Set(),
|
|
1380
|
+
profiles: undefined,
|
|
1356
1381
|
found: false,
|
|
1357
1382
|
};
|
|
1358
1383
|
}
|
|
@@ -1376,7 +1401,8 @@ export class ModelRegistry {
|
|
|
1376
1401
|
providerConfig.compat ||
|
|
1377
1402
|
providerConfig.disableStrictTools ||
|
|
1378
1403
|
providerConfig.requestTransform ||
|
|
1379
|
-
providerConfig.transport
|
|
1404
|
+
providerConfig.transport ||
|
|
1405
|
+
providerConfig.cacheRetention
|
|
1380
1406
|
) {
|
|
1381
1407
|
const disableStrictCompat = providerConfig.disableStrictTools ? { disableStrictTools: true } : undefined;
|
|
1382
1408
|
overrides.set(providerName, {
|
|
@@ -1387,6 +1413,7 @@ export class ModelRegistry {
|
|
|
1387
1413
|
compat: mergeCompat(providerConfig.compat, disableStrictCompat),
|
|
1388
1414
|
transport: providerConfig.transport,
|
|
1389
1415
|
requestTransform: providerConfig.requestTransform,
|
|
1416
|
+
cacheRetention: providerConfig.cacheRetention,
|
|
1390
1417
|
});
|
|
1391
1418
|
}
|
|
1392
1419
|
|
|
@@ -1403,6 +1430,7 @@ export class ModelRegistry {
|
|
|
1403
1430
|
headers: providerConfig.headers,
|
|
1404
1431
|
compat: providerConfig.compat,
|
|
1405
1432
|
requestTransform: providerConfig.requestTransform,
|
|
1433
|
+
cacheRetention: providerConfig.cacheRetention,
|
|
1406
1434
|
discovery: providerConfig.discovery,
|
|
1407
1435
|
optional: false,
|
|
1408
1436
|
});
|
|
@@ -1441,10 +1469,22 @@ export class ModelRegistry {
|
|
|
1441
1469
|
configuredProviders,
|
|
1442
1470
|
equivalence: value.equivalence,
|
|
1443
1471
|
modelBindings: value.modelBindings,
|
|
1472
|
+
profiles: value.profiles,
|
|
1444
1473
|
found: true,
|
|
1445
1474
|
};
|
|
1446
1475
|
}
|
|
1447
1476
|
|
|
1477
|
+
getModelProfiles(): Map<string, ModelProfileDefinition> {
|
|
1478
|
+
return new Map(this.#modelProfiles);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
getModelProfile(name: string): ModelProfileDefinition | undefined {
|
|
1482
|
+
return this.#modelProfiles.get(name);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
getAvailableModelProfileNames(): string[] {
|
|
1486
|
+
return [...this.#modelProfiles.keys()].sort((a, b) => a.localeCompare(b));
|
|
1487
|
+
}
|
|
1448
1488
|
applyConfiguredModelBindings(targetSettings: Settings): void {
|
|
1449
1489
|
this.#modelBindingsTargetSettings = targetSettings;
|
|
1450
1490
|
this.#applyConfiguredModelBindingsToTarget();
|
|
@@ -2082,13 +2122,16 @@ export class ModelRegistry {
|
|
|
2082
2122
|
compat: override.compat ? mergeCompat(baseOverride?.compat, override.compat) : baseOverride?.compat,
|
|
2083
2123
|
transport: override.transport ?? baseOverride?.transport,
|
|
2084
2124
|
requestTransform: mergeRequestTransform(baseOverride?.requestTransform, override.requestTransform),
|
|
2125
|
+
cacheRetention: override.cacheRetention ?? baseOverride?.cacheRetention,
|
|
2085
2126
|
};
|
|
2086
2127
|
}
|
|
2087
|
-
#applyProviderTransportOverride<
|
|
2128
|
+
#applyProviderTransportOverride<
|
|
2129
|
+
T extends { baseUrl?: string; headers?: Record<string, string>; cacheRetention?: CacheRetention },
|
|
2130
|
+
>(
|
|
2088
2131
|
entry: T,
|
|
2089
2132
|
override: Pick<
|
|
2090
2133
|
ProviderOverride,
|
|
2091
|
-
"baseUrl" | "headers" | "authHeader" | "apiKey" | "transport" | "requestTransform"
|
|
2134
|
+
"baseUrl" | "headers" | "authHeader" | "apiKey" | "transport" | "requestTransform" | "cacheRetention"
|
|
2092
2135
|
>,
|
|
2093
2136
|
): T {
|
|
2094
2137
|
const headers = mergeAuthHeader(
|
|
@@ -2107,6 +2150,7 @@ export class ModelRegistry {
|
|
|
2107
2150
|
(entry as { requestTransform?: ModelRequestTransform }).requestTransform,
|
|
2108
2151
|
override.requestTransform,
|
|
2109
2152
|
),
|
|
2153
|
+
cacheRetention: entry.cacheRetention ?? override.cacheRetention,
|
|
2110
2154
|
};
|
|
2111
2155
|
}
|
|
2112
2156
|
#applyRuntimeProviderOverrides(models: Model<Api>[]): Model<Api>[] {
|
|
@@ -2195,6 +2239,7 @@ export class ModelRegistry {
|
|
|
2195
2239
|
providerCompat,
|
|
2196
2240
|
providerConfig.requestTransform,
|
|
2197
2241
|
(providerConfig.auth as ProviderAuthMode | undefined) ?? undefined,
|
|
2242
|
+
providerConfig.cacheRetention,
|
|
2198
2243
|
modelDef as CustomModelDefinitionLike,
|
|
2199
2244
|
);
|
|
2200
2245
|
if (!model) continue;
|
|
@@ -2557,6 +2602,7 @@ export class ModelRegistry {
|
|
|
2557
2602
|
config.compat,
|
|
2558
2603
|
config.requestTransform,
|
|
2559
2604
|
undefined,
|
|
2605
|
+
undefined,
|
|
2560
2606
|
modelDef as CustomModelDefinitionLike,
|
|
2561
2607
|
);
|
|
2562
2608
|
if (!overlay) {
|
|
@@ -764,6 +764,7 @@ export async function resolveModelOverrideWithAuthFallback(
|
|
|
764
764
|
parentActiveModelPattern: string | undefined,
|
|
765
765
|
modelRegistry: ModelLookupRegistry & Pick<ModelRegistry, "getApiKey">,
|
|
766
766
|
settings?: Settings,
|
|
767
|
+
sessionId?: string,
|
|
767
768
|
): Promise<{
|
|
768
769
|
model?: Model<Api>;
|
|
769
770
|
thinkingLevel?: ThinkingLevel;
|
|
@@ -775,7 +776,7 @@ export async function resolveModelOverrideWithAuthFallback(
|
|
|
775
776
|
return { ...primary, authFallbackUsed: false };
|
|
776
777
|
}
|
|
777
778
|
|
|
778
|
-
const primaryKey = await modelRegistry.getApiKey(primary.model);
|
|
779
|
+
const primaryKey = await modelRegistry.getApiKey(primary.model, sessionId);
|
|
779
780
|
if (primaryKey === kNoAuth || isAuthenticated(primaryKey)) {
|
|
780
781
|
return { ...primary, authFallbackUsed: false };
|
|
781
782
|
}
|
|
@@ -787,7 +788,7 @@ export async function resolveModelOverrideWithAuthFallback(
|
|
|
787
788
|
if (modelsAreEqual(fallback.model, primary.model)) {
|
|
788
789
|
return { ...primary, authFallbackUsed: false };
|
|
789
790
|
}
|
|
790
|
-
const fallbackKey = await modelRegistry.getApiKey(fallback.model);
|
|
791
|
+
const fallbackKey = await modelRegistry.getApiKey(fallback.model, sessionId);
|
|
791
792
|
if (!isAuthenticated(fallbackKey)) {
|
|
792
793
|
return { ...primary, authFallbackUsed: false };
|
|
793
794
|
}
|
|
@@ -16,6 +16,7 @@ const ReasoningEffortMapSchema = z.object({
|
|
|
16
16
|
medium: z.string().optional(),
|
|
17
17
|
high: z.string().optional(),
|
|
18
18
|
xhigh: z.string().optional(),
|
|
19
|
+
max: z.string().optional(),
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
export const OpenAICompatSchema = z.object({
|
|
@@ -45,7 +46,8 @@ export const OpenAICompatSchema = z.object({
|
|
|
45
46
|
toolStrictMode: z.enum(["all_strict", "none"]).optional(),
|
|
46
47
|
});
|
|
47
48
|
|
|
48
|
-
const EffortSchema = z.enum(["minimal", "low", "medium", "high", "xhigh"]);
|
|
49
|
+
const EffortSchema = z.enum(["minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
50
|
+
const CacheRetentionSchema = z.enum(["none", "short", "long"]);
|
|
49
51
|
|
|
50
52
|
const ThinkingControlModeSchema = z.enum([
|
|
51
53
|
"effort",
|
|
@@ -76,6 +78,39 @@ const ModelBindingsSchema = z.object({
|
|
|
76
78
|
modelRoles: z.record(z.string(), z.string().min(1)).optional(),
|
|
77
79
|
agentModelOverrides: z.record(z.string(), z.string().min(1)).optional(),
|
|
78
80
|
});
|
|
81
|
+
export const ProfileRoleSchema = z.enum(["default", "executor", "architect", "planner", "critic"]);
|
|
82
|
+
|
|
83
|
+
function isValidProfileModelSelector(value: string): boolean {
|
|
84
|
+
if (value.includes(",")) return false;
|
|
85
|
+
const slashIdx = value.indexOf("/");
|
|
86
|
+
if (slashIdx <= 0 || slashIdx === value.length - 1) return false;
|
|
87
|
+
const provider = value.slice(0, slashIdx);
|
|
88
|
+
const modelId = value.slice(slashIdx + 1);
|
|
89
|
+
if (!provider || !modelId) return false;
|
|
90
|
+
const parts = modelId.split(":");
|
|
91
|
+
if (parts.length > 2) return false;
|
|
92
|
+
const [base, suffix] = parts;
|
|
93
|
+
if (!base) return false;
|
|
94
|
+
return suffix === undefined || ["minimal", "low", "medium", "high", "xhigh", "max"].includes(suffix);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const ProfileModelSelectorSchema = z
|
|
98
|
+
.string()
|
|
99
|
+
.min(1)
|
|
100
|
+
.refine(value => isValidProfileModelSelector(value), {
|
|
101
|
+
message: "Expected provider/modelId with optional :effort suffix",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export const ProfileModelMappingSchema = z.partialRecord(ProfileRoleSchema, ProfileModelSelectorSchema);
|
|
105
|
+
|
|
106
|
+
export const ProfileDefinitionSchema = z
|
|
107
|
+
.object({
|
|
108
|
+
required_providers: z.array(z.string().min(1)).min(1),
|
|
109
|
+
model_mapping: ProfileModelMappingSchema,
|
|
110
|
+
})
|
|
111
|
+
.strict();
|
|
112
|
+
|
|
113
|
+
export const ProfilesSchema = z.record(z.string().min(1), ProfileDefinitionSchema);
|
|
79
114
|
|
|
80
115
|
const ModelDefinitionSchema = z
|
|
81
116
|
.object({
|
|
@@ -116,6 +151,7 @@ const ModelDefinitionSchema = z
|
|
|
116
151
|
contextPromotionTarget: z.string().min(1).optional(),
|
|
117
152
|
wireModelId: z.string().min(1).optional(),
|
|
118
153
|
requestTransform: RequestTransformSchema.optional(),
|
|
154
|
+
cacheRetention: CacheRetentionSchema.optional(),
|
|
119
155
|
})
|
|
120
156
|
.strict();
|
|
121
157
|
|
|
@@ -141,6 +177,7 @@ export const ModelOverrideSchema = z
|
|
|
141
177
|
contextPromotionTarget: z.string().min(1).optional(),
|
|
142
178
|
wireModelId: z.string().min(1).optional(),
|
|
143
179
|
requestTransform: RequestTransformSchema.optional(),
|
|
180
|
+
cacheRetention: CacheRetentionSchema.optional(),
|
|
144
181
|
})
|
|
145
182
|
.strict();
|
|
146
183
|
|
|
@@ -192,6 +229,7 @@ const ProviderConfigSchema = z
|
|
|
192
229
|
* and `apiKey` must carry the gateway bearer.
|
|
193
230
|
*/
|
|
194
231
|
transport: z.literal("pi-native").optional(),
|
|
232
|
+
cacheRetention: CacheRetentionSchema.optional(),
|
|
195
233
|
})
|
|
196
234
|
.strict();
|
|
197
235
|
|
|
@@ -205,7 +243,10 @@ export const ModelsConfigSchema = z
|
|
|
205
243
|
providers: z.record(z.string(), ProviderConfigSchema).optional(),
|
|
206
244
|
modelBindings: ModelBindingsSchema.optional(),
|
|
207
245
|
equivalence: EquivalenceConfigSchema.optional(),
|
|
246
|
+
profiles: ProfilesSchema.optional(),
|
|
208
247
|
})
|
|
209
248
|
.strict();
|
|
210
249
|
|
|
211
250
|
export type ModelsConfig = z.infer<typeof ModelsConfigSchema>;
|
|
251
|
+
export type ModelProfileConfig = z.infer<typeof ProfileDefinitionSchema>;
|
|
252
|
+
export type ModelProfilesConfig = z.infer<typeof ProfilesSchema>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { THINKING_EFFORTS } from "@gajae-code/ai";
|
|
1
|
+
import { THINKING_EFFORTS } from "@gajae-code/ai/model-thinking";
|
|
2
2
|
import { TASK_SIMPLE_MODES } from "../task/simple-mode";
|
|
3
3
|
import { getThinkingLevelMetadata } from "../thinking";
|
|
4
4
|
import { EDIT_MODES } from "../utils/edit-mode";
|
|
@@ -314,6 +314,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
314
314
|
disabledExtensions: { type: "array", default: DEFAULT_DISABLED_EXTENSIONS },
|
|
315
315
|
|
|
316
316
|
modelRoles: { type: "record", default: EMPTY_STRING_RECORD },
|
|
317
|
+
"modelProfile.default": {
|
|
318
|
+
type: "string",
|
|
319
|
+
default: undefined,
|
|
320
|
+
ui: {
|
|
321
|
+
tab: "model",
|
|
322
|
+
label: "Default Model Profile",
|
|
323
|
+
description: "Model profile applied automatically at startup",
|
|
324
|
+
options: "runtime",
|
|
325
|
+
},
|
|
326
|
+
},
|
|
317
327
|
|
|
318
328
|
modelTags: { type: "record", default: EMPTY_MODEL_TAGS_RECORD },
|
|
319
329
|
|
|
@@ -2765,6 +2775,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
2765
2775
|
"thinkingBudgets.high": { type: "number", default: 16384 },
|
|
2766
2776
|
|
|
2767
2777
|
"thinkingBudgets.xhigh": { type: "number", default: 32768 },
|
|
2778
|
+
|
|
2779
|
+
"thinkingBudgets.max": { type: "number", default: 65536 },
|
|
2768
2780
|
} as const;
|
|
2769
2781
|
|
|
2770
2782
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -2945,6 +2957,7 @@ export interface ThinkingBudgetsSettings {
|
|
|
2945
2957
|
medium: number;
|
|
2946
2958
|
high: number;
|
|
2947
2959
|
xhigh: number;
|
|
2960
|
+
max: number;
|
|
2948
2961
|
}
|
|
2949
2962
|
|
|
2950
2963
|
export interface SttSettings {
|
|
@@ -161,12 +161,22 @@ gjc ultragoal checkpoint --goal-id <id> --status complete --evidence "<team evid
|
|
|
161
161
|
|
|
162
162
|
Workers do not own ultragoal goal state, do not create worker ultragoal ledgers, and do not checkpoint Ultragoal. Workers must not run `gjc ultragoal checkpoint`; checkpoint authority stays with the leader after worker tasks are terminal. Team launch remains explicit; Ultragoal does not auto-launch Team and performs no hidden goal mutation.
|
|
163
163
|
|
|
164
|
+
## Internal Ultragoal sub-skill fragments
|
|
165
|
+
|
|
166
|
+
The completion-gate cleanup sweep is driven by `ai-slop-cleaner`, an internal Ultragoal sub-skill bundled as a `kind: "skill-fragment"` prompt with parent skill `ultragoal` (installed at `skill-fragments/ultragoal/ai-slop-cleaner.md`). It is analogous to deep-interview's auto-research fragment: loaded on demand for one specific hook, never a user-facing skill.
|
|
167
|
+
|
|
168
|
+
- It is not slash-command discoverable, has no public skill-listing entry, and is never resolvable through `skill://`.
|
|
169
|
+
- It is a read-only detector+reporter over the active story's changed files only: it never edits code, writes files, mutates `.gjc/`, checkpoints, calls goal tools, or spawns workflows.
|
|
170
|
+
- It classifies every finding as blocking or advisory across the full taxonomy (fallback-like masking vs. grounded, duplication, dead code, needless abstraction, boundary violations, UI/design slop, missing tests).
|
|
171
|
+
- The leader and a leader-spawned `executor` own all fixes; the cleaner reruns until zero blocking findings remain. Advisory findings live in the gate report only.
|
|
172
|
+
- Recursion guard: it must not spawn nested `ralplan`/`team`/`deep-interview`/`ultragoal`; broad or architectural findings are handed back to the leader as review blockers.
|
|
173
|
+
|
|
164
174
|
## Mandatory completion cleanup and review gate
|
|
165
175
|
|
|
166
176
|
An ultragoal story cannot be checkpointed `complete` until the active agent has run the quality gate. The gate is plan-first, contract-driven, and surface-based:
|
|
167
177
|
|
|
168
178
|
1. Run targeted implementation verification for the story.
|
|
169
|
-
2. Run
|
|
179
|
+
2. Run the internal ai-slop-cleaner skill fragment as the final cleanup sweep on the story's changed files only, before verification and red-team so only clean code is reviewed. It is a read-only detector that emits an `AI SLOP CLEANUP REPORT`; if there are no relevant edits it still runs and records a passed/no-op report. Every BLOCKING cleaner finding is a completion blocker: the leader spawns an `executor` to fix blocking findings only, then reruns the cleaner until blocking findings are zero. Advisory findings are included in the gate report only and are not written to the Ultragoal ledger. Carry the report through the existing `qualityGate.iteration.evidence` field; do not add a new top-level quality-gate key.
|
|
170
180
|
3. Rerun verification after the cleaner pass.
|
|
171
181
|
4. Delegate an `architect` review covering all three lanes:
|
|
172
182
|
- architecture-side: system boundaries, layering, data/control flow, operational risks.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Ultragoal AI Slop Cleaner Fragment
|
|
2
|
+
|
|
3
|
+
You are the AI slop cleaner for the Ultragoal completion gate. This is an internal Ultragoal sub-skill, loaded on demand as a `kind: "skill-fragment"` prompt with parent skill `ultragoal`. It is never user-facing: not slash-command discoverable, no public skill listing entry, and never resolvable through `skill://`.
|
|
4
|
+
|
|
5
|
+
You are a **read-only detector and reporter**. You never edit code, write files, run formatters, mutate `.gjc/` state, checkpoint, call goal tools, or spawn workflows. You detect slop in the active Ultragoal story's changed files, classify each finding, and emit a report. The Ultragoal leader spawns an `executor` to fix BLOCKING findings; you do not fix anything yourself.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
- Inspect ONLY the active Ultragoal story's changed-files list. No broad rewrites, no inspection outside that scope, no new dependencies.
|
|
10
|
+
- Allow only narrow supporting reads needed to understand the contracts of changed files; if you need broader context, report that need to the leader instead of expanding scope.
|
|
11
|
+
- If there are no relevant edits, emit a passed/no-op report (`Gate Result: PASS`, `Changed Files Reviewed` listing the files as "no relevant edits").
|
|
12
|
+
- Recursion guard: you are already inside an Ultragoal workflow. Do NOT spawn nested `ralplan`, `team`, `deep-interview`, or `ultragoal` workflows. Broad, ambiguous, cross-layer, or architectural findings are handed to the leader as review blockers, not resolved here.
|
|
13
|
+
|
|
14
|
+
## Taxonomy
|
|
15
|
+
|
|
16
|
+
Classify every finding against the full taxonomy:
|
|
17
|
+
|
|
18
|
+
1. **Fallback-like code** — classify each as **masking fallback slop** or **grounded compatibility/fail-safe fallback**.
|
|
19
|
+
- Masking signals (blocking): swallowed errors, silent defaults, bypassed validation/tests, untested alternate execution paths, primary-contract suppression.
|
|
20
|
+
- Grounded signals (advisory): scoped to an external/version/fail-safe boundary, documented rationale, preserved failure evidence, and regression tests covering both primary and fallback behavior.
|
|
21
|
+
2. **Duplication** — repeated logic, copy-paste branches, redundant helpers.
|
|
22
|
+
3. **Dead code** — unused code, unreachable branches, stale flags, debug leftovers.
|
|
23
|
+
4. **Needless abstraction** — pass-through wrappers, speculative indirection, single-use helper layers.
|
|
24
|
+
5. **Boundary violations** — hidden coupling, leaky responsibilities, wrong-layer imports or side effects.
|
|
25
|
+
6. **UI/design slop** — context-sensitive signals, not absolute bans; preserve intentional brand/design-system/accessibility/product rationale. Signals: small Korean body copy (challenge 11-12px; Korean body text generally needs 14px+ unless a dense accessible system supports smaller), gratuitous shadows/depth, repetitive eyebrow+title+description scaffolding and filler/emoji badges, default blue/purple palettes (e.g. #3B82F6) without rationale, over-perfect uniform 3/4-column grids, and extreme "AI demo" gradients.
|
|
26
|
+
7. **Missing tests** — behavior not locked, weak regression coverage, missing edge/failure-mode cases.
|
|
27
|
+
|
|
28
|
+
## Blocking vs advisory
|
|
29
|
+
|
|
30
|
+
- **Blocking** if it can mask failures, violate accepted contracts, weaken boundaries, leave changed behavior untested, create maintenance traps, or make later verification unsafe.
|
|
31
|
+
- **Advisory** if it is nice-to-have, stylistic/contextual, or outside safe story scope.
|
|
32
|
+
- Advisory findings stay in the gate report only; they are NOT written to the Ultragoal ledger.
|
|
33
|
+
|
|
34
|
+
## Report
|
|
35
|
+
|
|
36
|
+
Emit exactly this text block with these mandated labels:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
AI SLOP CLEANUP REPORT
|
|
40
|
+
======================
|
|
41
|
+
|
|
42
|
+
Scope: [changed files inspected]
|
|
43
|
+
Mode: read-only detector/report; no edits performed
|
|
44
|
+
Blocking Findings: [none, or numbered findings with file, category, evidence, required executor fix]
|
|
45
|
+
Advisory Findings: [none, or numbered findings with file, category, evidence, why advisory]
|
|
46
|
+
Fallback Findings: [none, or finding -> masking fallback slop / grounded compatibility/fail-safe fallback -> blocking/advisory]
|
|
47
|
+
UI/Design Findings: [none/N/A, or signal -> blocking/advisory -> rationale]
|
|
48
|
+
Missing Test Findings: [none, or gap -> blocking/advisory -> required coverage]
|
|
49
|
+
Recursion Guard: [confirmed no nested ralplan/team/deep-interview/ultragoal spawned; broad findings handed to leader]
|
|
50
|
+
Changed Files Reviewed:
|
|
51
|
+
- [path] - [reviewed / no relevant edits]
|
|
52
|
+
|
|
53
|
+
Gate Result: PASS | BLOCKED
|
|
54
|
+
Leader Action:
|
|
55
|
+
- PASS: continue to verification, architect review, and executor red-team QA.
|
|
56
|
+
- BLOCKED: spawn executor to fix BLOCKING findings only, then rerun this sweep until Blocking Findings is none.
|
|
57
|
+
Remaining Risks:
|
|
58
|
+
- [none, or advisory/deferred risks]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Port the oh-my-codex taxonomy and report shape, not its editing workflow. Do not instruct yourself to execute cleanup passes — detect and report only.
|