@gajae-code/coding-agent 0.6.4 → 0.7.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 +51 -0
- package/dist/types/async/job-manager.d.ts +3 -1
- package/dist/types/cli/daemon-cli.d.ts +25 -0
- package/dist/types/cli/migrate-cli.d.ts +20 -0
- package/dist/types/cli/notify-cli.d.ts +23 -0
- package/dist/types/cli/setup-cli.d.ts +20 -1
- package/dist/types/commands/daemon.d.ts +41 -0
- package/dist/types/commands/migrate.d.ts +33 -0
- package/dist/types/commands/notify.d.ts +41 -0
- package/dist/types/config/keybindings.d.ts +4 -0
- package/dist/types/config/model-profile-activation.d.ts +12 -0
- package/dist/types/config/model-profiles.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +38 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/daemon/builtin.d.ts +20 -0
- package/dist/types/daemon/control-types.d.ts +57 -0
- package/dist/types/daemon/runtime.d.ts +25 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -2
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/gjc-runtime/session-layout.d.ts +59 -0
- package/dist/types/gjc-runtime/session-resolution.d.ts +47 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +1 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +5 -4
- package/dist/types/gjc-runtime/state-schema.d.ts +2 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +38 -7
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +15 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +21 -4
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +1 -1
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +1 -1
- package/dist/types/harness-control-plane/storage.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +12 -4
- package/dist/types/migrate/action-planner.d.ts +11 -0
- package/dist/types/migrate/adapters/claude-code.d.ts +2 -0
- package/dist/types/migrate/adapters/codex.d.ts +5 -0
- package/dist/types/migrate/adapters/index.d.ts +45 -0
- package/dist/types/migrate/adapters/opencode.d.ts +2 -0
- package/dist/types/migrate/executor.d.ts +2 -0
- package/dist/types/migrate/mcp-mapper.d.ts +20 -0
- package/dist/types/migrate/report.d.ts +18 -0
- package/dist/types/migrate/skill-normalizer.d.ts +27 -0
- package/dist/types/migrate/types.d.ts +126 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/oauth-selector.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -2
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +10 -0
- package/dist/types/modes/types.d.ts +7 -1
- package/dist/types/notifications/config-commands.d.ts +26 -0
- package/dist/types/notifications/config.d.ts +61 -0
- package/dist/types/notifications/helpers.d.ts +55 -0
- package/dist/types/notifications/html-format.d.ts +62 -0
- package/dist/types/notifications/index.d.ts +28 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +93 -0
- package/dist/types/notifications/telegram-cli.d.ts +19 -0
- package/dist/types/notifications/telegram-daemon-cli.d.ts +11 -0
- package/dist/types/notifications/telegram-daemon-control.d.ts +56 -0
- package/dist/types/notifications/telegram-daemon.d.ts +276 -0
- package/dist/types/notifications/telegram-reference.d.ts +111 -0
- package/dist/types/notifications/threaded-inbound.d.ts +58 -0
- package/dist/types/notifications/threaded-render.d.ts +66 -0
- package/dist/types/notifications/topic-registry.d.ts +67 -0
- package/dist/types/research-plan/index.d.ts +1 -0
- package/dist/types/research-plan/ledger.d.ts +33 -0
- package/dist/types/rlm/artifacts.d.ts +1 -1
- package/dist/types/rlm/index.d.ts +12 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +26 -0
- package/dist/types/session/agent-session.d.ts +39 -2
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/setup/credential-auto-import.d.ts +63 -0
- package/dist/types/setup/credential-import.d.ts +3 -0
- package/dist/types/setup/host-plugin-setup.d.ts +39 -0
- package/dist/types/skill-state/active-state.d.ts +6 -11
- package/dist/types/skill-state/canonical-skills.d.ts +3 -0
- package/dist/types/skill-state/workflow-hud.d.ts +2 -0
- package/dist/types/task/spawn-gate.d.ts +1 -10
- package/dist/types/tools/ask-answer-registry.d.ts +13 -0
- package/dist/types/tools/index.d.ts +18 -0
- package/dist/types/tools/subagent.d.ts +3 -0
- package/package.json +7 -7
- package/scripts/build-binary.ts +3 -0
- package/src/async/job-manager.ts +5 -1
- package/src/cli/daemon-cli.ts +122 -0
- package/src/cli/migrate-cli.ts +106 -0
- package/src/cli/notify-cli.ts +274 -0
- package/src/cli/setup-cli.ts +173 -84
- package/src/cli.ts +3 -0
- package/src/commands/daemon.ts +47 -0
- package/src/commands/deep-interview.ts +2 -2
- package/src/commands/migrate.ts +46 -0
- package/src/commands/notify.ts +61 -0
- package/src/commands/setup.ts +11 -1
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +7 -3
- package/src/config/model-profile-activation.ts +74 -5
- package/src/config/model-profiles.ts +7 -4
- package/src/config/model-registry.ts +6 -3
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +29 -0
- package/src/coordinator/contract.ts +3 -0
- package/src/coordinator-mcp/policy.ts +10 -2
- package/src/coordinator-mcp/server.ts +270 -1
- package/src/daemon/builtin.ts +46 -0
- package/src/daemon/control-types.ts +65 -0
- package/src/daemon/runtime.ts +51 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +0 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +28 -24
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/team/SKILL.md +51 -47
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +33 -13
- package/src/extensibility/custom-commands/loader.ts +0 -7
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +8 -0
- package/src/extensibility/gjc-plugins/injection.ts +23 -4
- package/src/extensibility/gjc-plugins/state.ts +16 -1
- package/src/gjc-runtime/deep-interview-recorder.ts +51 -18
- package/src/gjc-runtime/deep-interview-runtime.ts +49 -23
- package/src/gjc-runtime/goal-mode-request.ts +26 -11
- package/src/gjc-runtime/launch-tmux.ts +6 -1
- package/src/gjc-runtime/ralplan-runtime.ts +79 -50
- package/src/gjc-runtime/session-layout.ts +180 -0
- package/src/gjc-runtime/session-resolution.ts +217 -0
- package/src/gjc-runtime/state-graph.ts +1 -2
- package/src/gjc-runtime/state-migrations.ts +1 -0
- package/src/gjc-runtime/state-runtime.ts +247 -124
- package/src/gjc-runtime/state-schema.ts +2 -0
- package/src/gjc-runtime/state-writer.ts +289 -41
- package/src/gjc-runtime/team-runtime.ts +43 -19
- package/src/gjc-runtime/tmux-sessions.ts +7 -1
- package/src/gjc-runtime/ultragoal-guard.ts +102 -4
- package/src/gjc-runtime/ultragoal-runtime.ts +226 -60
- package/src/gjc-runtime/workflow-command-ref.ts +1 -2
- package/src/gjc-runtime/workflow-manifest.generated.json +27 -2
- package/src/gjc-runtime/workflow-manifest.ts +12 -3
- package/src/goals/tools/goal-tool.ts +11 -2
- package/src/harness-control-plane/storage.ts +14 -4
- package/src/hooks/native-skill-hook.ts +38 -12
- package/src/hooks/skill-state.ts +178 -83
- package/src/internal-urls/docs-index.generated.ts +9 -6
- package/src/main.ts +30 -0
- package/src/migrate/action-planner.ts +318 -0
- package/src/migrate/adapters/claude-code.ts +39 -0
- package/src/migrate/adapters/codex.ts +70 -0
- package/src/migrate/adapters/index.ts +277 -0
- package/src/migrate/adapters/opencode.ts +52 -0
- package/src/migrate/executor.ts +81 -0
- package/src/migrate/mcp-mapper.ts +152 -0
- package/src/migrate/report.ts +104 -0
- package/src/migrate/skill-normalizer.ts +80 -0
- package/src/migrate/types.ts +163 -0
- package/src/modes/acp/acp-event-mapper.ts +1 -0
- package/src/modes/bridge/bridge-mode.ts +2 -2
- package/src/modes/components/custom-editor.ts +30 -20
- package/src/modes/components/hook-editor.ts +7 -2
- package/src/modes/components/oauth-selector.ts +19 -0
- package/src/modes/controllers/event-controller.ts +20 -0
- package/src/modes/controllers/selector-controller.ts +80 -17
- package/src/modes/interactive-mode.ts +6 -2
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/event-contract.ts +1 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +1 -0
- package/src/modes/shared/agent-wire/event-observation.ts +16 -0
- package/src/modes/shared/agent-wire/unattended-audit.ts +3 -2
- package/src/modes/shared/agent-wire/unattended-session.ts +22 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/utils/ui-helpers.ts +23 -0
- package/src/notifications/config-commands.ts +50 -0
- package/src/notifications/config.ts +107 -0
- package/src/notifications/helpers.ts +135 -0
- package/src/notifications/html-format.ts +389 -0
- package/src/notifications/index.ts +663 -0
- package/src/notifications/rate-limit-pool.ts +179 -0
- package/src/notifications/telegram-cli.ts +194 -0
- package/src/notifications/telegram-daemon-cli.ts +74 -0
- package/src/notifications/telegram-daemon-control.ts +370 -0
- package/src/notifications/telegram-daemon.ts +1370 -0
- package/src/notifications/telegram-reference.ts +335 -0
- package/src/notifications/threaded-inbound.ts +80 -0
- package/src/notifications/threaded-render.ts +155 -0
- package/src/notifications/topic-registry.ts +133 -0
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +1 -2
- package/src/research-plan/index.ts +1 -0
- package/src/research-plan/ledger.ts +177 -0
- package/src/rlm/artifacts.ts +12 -3
- package/src/rlm/index.ts +26 -0
- package/src/runtime-mcp/config-writer.ts +46 -0
- package/src/sdk.ts +16 -0
- package/src/session/agent-session.ts +128 -24
- package/src/session/auth-storage.ts +3 -0
- package/src/session/session-dump-format.ts +43 -2
- package/src/session/session-manager.ts +39 -5
- package/src/setup/credential-auto-import.ts +258 -0
- package/src/setup/credential-import.ts +17 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +10 -0
- package/src/setup/hermes-setup.ts +1 -1
- package/src/setup/host-plugin-setup.ts +142 -0
- package/src/skill-state/active-state.ts +72 -108
- package/src/skill-state/canonical-skills.ts +4 -0
- package/src/skill-state/deep-interview-mutation-guard.ts +28 -109
- package/src/skill-state/workflow-hud.ts +4 -2
- package/src/skill-state/workflow-state-contract.ts +3 -3
- package/src/slash-commands/builtin-registry.ts +4 -1
- package/src/task/agents.ts +1 -22
- package/src/task/executor.ts +5 -1
- package/src/task/index.ts +1 -41
- package/src/task/spawn-gate.ts +1 -38
- package/src/task/types.ts +1 -1
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/ask.ts +108 -16
- package/src/tools/computer.ts +58 -4
- package/src/tools/image-gen.ts +5 -8
- package/src/tools/index.ts +19 -0
- package/src/tools/inspect-image.ts +16 -11
- package/src/tools/subagent-render.ts +7 -0
- package/src/tools/subagent.ts +38 -7
- package/dist/types/extensibility/custom-commands/bundled/review/index.d.ts +0 -10
- package/src/extensibility/custom-commands/bundled/review/index.ts +0 -456
- package/src/prompts/agents/explore.md +0 -58
- package/src/prompts/agents/plan.md +0 -49
- package/src/prompts/agents/reviewer.md +0 -141
- package/src/prompts/agents/task.md +0 -16
- package/src/prompts/review-request.md +0 -70
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import MCP servers and skills from other coding agents into GJC.
|
|
3
|
+
*/
|
|
4
|
+
import { Command, Flags } from "@gajae-code/utils/cli";
|
|
5
|
+
import { type MigrateCommandArgs, runMigrateCommand } from "../cli/migrate-cli";
|
|
6
|
+
|
|
7
|
+
export default class Migrate extends Command {
|
|
8
|
+
static description = "Import MCP servers and skills from Claude Code, Codex, or OpenCode";
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
"gjc migrate --from claude-code",
|
|
12
|
+
"gjc migrate --from codex --from opencode",
|
|
13
|
+
"gjc migrate --from all --dry-run --json",
|
|
14
|
+
"gjc migrate --from claude-code --project --force",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
static flags = {
|
|
18
|
+
from: Flags.string({
|
|
19
|
+
description: "Source agent to import from (repeatable): claude-code | codex | opencode | all",
|
|
20
|
+
multiple: true,
|
|
21
|
+
required: true,
|
|
22
|
+
}),
|
|
23
|
+
project: Flags.boolean({
|
|
24
|
+
description: "Write to the project scope (./.gjc) instead of the user scope (~/.gjc)",
|
|
25
|
+
default: false,
|
|
26
|
+
}),
|
|
27
|
+
force: Flags.boolean({
|
|
28
|
+
description: "Overwrite existing skills/MCP servers instead of skipping them",
|
|
29
|
+
default: false,
|
|
30
|
+
}),
|
|
31
|
+
"dry-run": Flags.boolean({ description: "Preview the migration without writing anything", default: false }),
|
|
32
|
+
json: Flags.boolean({ char: "j", description: "Emit a machine-readable JSON report", default: false }),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async run(): Promise<void> {
|
|
36
|
+
const { flags } = await this.parse(Migrate);
|
|
37
|
+
const cmd: MigrateCommandArgs = {
|
|
38
|
+
from: flags.from ?? [],
|
|
39
|
+
project: flags.project,
|
|
40
|
+
force: flags.force,
|
|
41
|
+
dryRun: flags["dry-run"],
|
|
42
|
+
json: flags.json,
|
|
43
|
+
};
|
|
44
|
+
await runMigrateCommand(cmd);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configure Telegram notifications.
|
|
3
|
+
*/
|
|
4
|
+
import { Args, Command, Flags } from "@gajae-code/utils/cli";
|
|
5
|
+
import { type NotifyAction, type NotifyCommandArgs, runNotifyCommand } from "../cli/notify-cli";
|
|
6
|
+
import { initTheme } from "../modes/theme/theme";
|
|
7
|
+
|
|
8
|
+
const ACTIONS: NotifyAction[] = ["setup", "status", "daemon-internal"];
|
|
9
|
+
|
|
10
|
+
export default class Notify extends Command {
|
|
11
|
+
static description = "Configure Telegram notifications";
|
|
12
|
+
|
|
13
|
+
static args = {
|
|
14
|
+
action: Args.string({
|
|
15
|
+
description: "Notify action",
|
|
16
|
+
required: false,
|
|
17
|
+
options: ACTIONS,
|
|
18
|
+
}),
|
|
19
|
+
extra: Args.string({
|
|
20
|
+
description: "Additional internal args",
|
|
21
|
+
required: false,
|
|
22
|
+
multiple: true,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
static flags = {
|
|
27
|
+
smoke: Flags.boolean({ description: "Run hidden daemon smoke" }),
|
|
28
|
+
token: Flags.string({ description: "Telegram bot token (non-interactive setup)" }),
|
|
29
|
+
"chat-id": Flags.string({ description: "Telegram chat id to pair (non-interactive setup)" }),
|
|
30
|
+
redact: Flags.boolean({ description: "Enable redaction of remote notification content" }),
|
|
31
|
+
"owner-id": Flags.string({ description: "Internal: daemon owner id" }),
|
|
32
|
+
"agent-dir": Flags.string({ description: "Internal: agent dir for the daemon" }),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async run(): Promise<void> {
|
|
36
|
+
const { args, flags } = await this.parse(Notify);
|
|
37
|
+
const action = (args.action ?? "status") as NotifyAction;
|
|
38
|
+
const extra = Array.isArray(args.extra) ? args.extra : args.extra ? [args.extra] : [];
|
|
39
|
+
const flagRec = flags as Record<string, unknown>;
|
|
40
|
+
const ownerId = flagRec["owner-id"] as string | undefined;
|
|
41
|
+
const agentDir = flagRec["agent-dir"] as string | undefined;
|
|
42
|
+
const rawArgs = [
|
|
43
|
+
...(flags.smoke ? ["--smoke"] : []),
|
|
44
|
+
...(ownerId ? ["--owner-id", ownerId] : []),
|
|
45
|
+
...(agentDir ? ["--agent-dir", agentDir] : []),
|
|
46
|
+
...extra,
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const cmd: NotifyCommandArgs = {
|
|
50
|
+
action,
|
|
51
|
+
smoke: flags.smoke,
|
|
52
|
+
rawArgs,
|
|
53
|
+
token: flags.token as string | undefined,
|
|
54
|
+
chatId: (flags as Record<string, unknown>)["chat-id"] as string | undefined,
|
|
55
|
+
redact: Boolean(flags.redact),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
await initTheme();
|
|
59
|
+
await runNotifyCommand(cmd);
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/commands/setup.ts
CHANGED
|
@@ -5,7 +5,17 @@ import { Args, Command, Flags } from "@gajae-code/utils/cli";
|
|
|
5
5
|
import { runSetupCommand, type SetupCommandArgs, type SetupComponent } from "../cli/setup-cli";
|
|
6
6
|
import { initTheme } from "../modes/theme/theme";
|
|
7
7
|
|
|
8
|
-
const COMPONENTS: SetupComponent[] = [
|
|
8
|
+
const COMPONENTS: SetupComponent[] = [
|
|
9
|
+
"claude",
|
|
10
|
+
"codex",
|
|
11
|
+
"credentials",
|
|
12
|
+
"defaults",
|
|
13
|
+
"hermes",
|
|
14
|
+
"hooks",
|
|
15
|
+
"provider",
|
|
16
|
+
"python",
|
|
17
|
+
"stt",
|
|
18
|
+
];
|
|
9
19
|
|
|
10
20
|
export default class Setup extends Command {
|
|
11
21
|
static description = "Install GJC defaults or optional feature dependencies";
|
package/src/commands/state.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { Command } from "@gajae-code/utils/cli";
|
|
|
2
2
|
import { runNativeStateCommand } from "../gjc-runtime/state-runtime";
|
|
3
3
|
|
|
4
4
|
export default class State extends Command {
|
|
5
|
-
static description =
|
|
5
|
+
static description =
|
|
6
|
+
"Read or update current-session GJC workflow state receipts under .gjc/_session-{sessionid}/state";
|
|
6
7
|
static strict = false;
|
|
7
8
|
static examples = [
|
|
8
9
|
'$ gjc state read --input \'{"mode":"deep-interview"}\' --json',
|
package/src/commands/team.ts
CHANGED
|
@@ -75,7 +75,7 @@ function parseInputFlag(argv: string[]): Record<string, unknown> {
|
|
|
75
75
|
|
|
76
76
|
export default class Team extends Command {
|
|
77
77
|
static description =
|
|
78
|
-
"Run native GJC tmux team orchestration from inside an existing tmux/GJC --tmux session; --dry-run writes ephemeral .gjc/state/team state only";
|
|
78
|
+
"Run native GJC tmux team orchestration from inside an existing tmux/GJC --tmux session; --dry-run writes ephemeral .gjc/_session-{sessionid}/state/team state only";
|
|
79
79
|
static strict = false;
|
|
80
80
|
|
|
81
81
|
static args = {
|
|
@@ -89,7 +89,7 @@ export default class Team extends Command {
|
|
|
89
89
|
json: Flags.boolean({ char: "j", description: "Emit machine-readable JSON", default: false }),
|
|
90
90
|
"dry-run": Flags.boolean({
|
|
91
91
|
description:
|
|
92
|
-
"Create ephemeral .gjc/state/team state without starting tmux panes; do not commit generated state",
|
|
92
|
+
"Create ephemeral .gjc/_session-{sessionid}/state/team state without starting tmux panes; do not commit generated state",
|
|
93
93
|
default: false,
|
|
94
94
|
}),
|
|
95
95
|
};
|
|
@@ -210,7 +210,11 @@ export default class Team extends Command {
|
|
|
210
210
|
`tmux: ${snapshot.tmux_session}`,
|
|
211
211
|
`state: ${snapshot.state_dir}`,
|
|
212
212
|
`workers: ${snapshot.workers.length}`,
|
|
213
|
-
...(dryRun
|
|
213
|
+
...(dryRun
|
|
214
|
+
? [
|
|
215
|
+
"dry-run: wrote ephemeral .gjc/_session-{sessionid}/state/team state only; do not commit generated .gjc state",
|
|
216
|
+
]
|
|
217
|
+
: []),
|
|
214
218
|
]);
|
|
215
219
|
}
|
|
216
220
|
}
|
|
@@ -17,6 +17,8 @@ type ModelProfileActivationSession = Pick<AgentSession, "model" | "thinkingLevel
|
|
|
17
17
|
setModelTemporary?: AgentSession["setModelTemporary"];
|
|
18
18
|
setActiveModelProfile?: (name: string | undefined) => void;
|
|
19
19
|
getActiveModelProfile?: () => string | undefined;
|
|
20
|
+
getSessionDefaultModelSelector?: () => string | undefined;
|
|
21
|
+
recordResumeDefaultModel?: (selector: string) => void;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
export interface PrepareModelProfileActivationOptions {
|
|
@@ -43,10 +45,20 @@ export interface PreparedModelProfileActivation {
|
|
|
43
45
|
previousModel: Model<Api> | undefined;
|
|
44
46
|
previousThinkingLevel: ThinkingLevel | undefined;
|
|
45
47
|
previousAgentModelOverrides: Record<string, string>;
|
|
48
|
+
previousModelRoles: Record<string, string>;
|
|
46
49
|
defaultModel: Model<Api> | undefined;
|
|
47
50
|
defaultThinkingLevel: ThinkingLevel | undefined;
|
|
51
|
+
modelRoles: Record<string, string>;
|
|
48
52
|
agentModelOverrides: Record<string, string>;
|
|
49
53
|
previousActiveModelProfile: string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* The session resume default ("provider/id") captured BEFORE activation —
|
|
56
|
+
* the model resume would restore prior to this profile. Snapshotted
|
|
57
|
+
* separately from `previousModel` (the live runtime model, which may be a
|
|
58
|
+
* transient switch) so a failed-activation rollback restores the correct
|
|
59
|
+
* resume default without promoting a transient model to it.
|
|
60
|
+
*/
|
|
61
|
+
previousSessionDefaultModel: string | undefined;
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
export function formatModelProfileCredentialError(profileName: string, providers: readonly string[]): string {
|
|
@@ -87,14 +99,24 @@ function rewriteSelectorProvider(
|
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
function rewriteBindingsProviders(
|
|
90
|
-
bindings: {
|
|
102
|
+
bindings: {
|
|
103
|
+
defaultSelector?: string;
|
|
104
|
+
modelRoles: Record<string, string>;
|
|
105
|
+
agentModelOverrides: Record<string, string>;
|
|
106
|
+
},
|
|
91
107
|
authenticatedProviders: ReadonlySet<string>,
|
|
92
108
|
alternativeGroups: readonly (readonly string[])[],
|
|
93
|
-
): { defaultSelector?: string; agentModelOverrides: Record<string, string> } {
|
|
109
|
+
): { defaultSelector?: string; modelRoles: Record<string, string>; agentModelOverrides: Record<string, string> } {
|
|
94
110
|
return {
|
|
95
111
|
defaultSelector: bindings.defaultSelector
|
|
96
112
|
? rewriteSelectorProvider(bindings.defaultSelector, authenticatedProviders, alternativeGroups)
|
|
97
113
|
: undefined,
|
|
114
|
+
modelRoles: Object.fromEntries(
|
|
115
|
+
Object.entries(bindings.modelRoles).map(([role, sel]) => [
|
|
116
|
+
role,
|
|
117
|
+
rewriteSelectorProvider(sel, authenticatedProviders, alternativeGroups),
|
|
118
|
+
]),
|
|
119
|
+
),
|
|
98
120
|
agentModelOverrides: Object.fromEntries(
|
|
99
121
|
Object.entries(bindings.agentModelOverrides).map(([role, sel]) => [
|
|
100
122
|
role,
|
|
@@ -165,6 +187,18 @@ export async function prepareModelProfileActivation(
|
|
|
165
187
|
);
|
|
166
188
|
}
|
|
167
189
|
|
|
190
|
+
const modelRoles: Record<string, string> = {};
|
|
191
|
+
for (const [role, selector] of Object.entries(bindings.modelRoles) as [GjcModelAssignmentTargetId, string][]) {
|
|
192
|
+
const resolved = resolveModelRoleValue(selector, availableModels, {
|
|
193
|
+
settings: options.settings as Settings,
|
|
194
|
+
modelRegistry: options.modelRegistry,
|
|
195
|
+
});
|
|
196
|
+
if (!resolved.model) {
|
|
197
|
+
throw new Error(`Model profile "${options.profileName}" ${role} selector did not resolve: ${selector}`);
|
|
198
|
+
}
|
|
199
|
+
modelRoles[role] = formatClampedModelSelector(selector, resolved.model);
|
|
200
|
+
}
|
|
201
|
+
|
|
168
202
|
const agentModelOverrides: Record<string, string> = {};
|
|
169
203
|
for (const [role, selector] of Object.entries(bindings.agentModelOverrides) as [
|
|
170
204
|
GjcModelAssignmentTargetId,
|
|
@@ -187,10 +221,13 @@ export async function prepareModelProfileActivation(
|
|
|
187
221
|
previousModel: options.session.model,
|
|
188
222
|
previousThinkingLevel: options.session.thinkingLevel,
|
|
189
223
|
previousAgentModelOverrides: { ...options.settings.get("task.agentModelOverrides") },
|
|
224
|
+
previousModelRoles: { ...options.settings.get("modelRoles") },
|
|
190
225
|
defaultModel: resolvedDefault?.model,
|
|
191
226
|
defaultThinkingLevel: resolvedDefault?.thinkingLevel,
|
|
227
|
+
modelRoles,
|
|
192
228
|
agentModelOverrides,
|
|
193
229
|
previousActiveModelProfile: options.session.getActiveModelProfile?.(),
|
|
230
|
+
previousSessionDefaultModel: options.session.getSessionDefaultModelSelector?.(),
|
|
194
231
|
};
|
|
195
232
|
}
|
|
196
233
|
|
|
@@ -201,17 +238,29 @@ export async function applyPreparedModelProfileActivation(
|
|
|
201
238
|
const previousModel = prepared.previousModel;
|
|
202
239
|
const previousThinkingLevel = prepared.previousThinkingLevel;
|
|
203
240
|
const previousAgentModelOverrides = prepared.previousAgentModelOverrides;
|
|
241
|
+
const previousModelRoles = prepared.previousModelRoles;
|
|
204
242
|
const previousPersistedDefault = prepared.settings.get("modelProfile.default");
|
|
205
243
|
const previousActiveModelProfile = prepared.previousActiveModelProfile;
|
|
244
|
+
const previousSessionDefaultModel = prepared.previousSessionDefaultModel;
|
|
206
245
|
let modelChanged = false;
|
|
207
246
|
let overridesChanged = false;
|
|
208
247
|
let defaultChanged = false;
|
|
248
|
+
let modelRolesChanged = false;
|
|
209
249
|
|
|
210
250
|
try {
|
|
211
251
|
if (prepared.defaultModel) {
|
|
212
|
-
await prepared.session.setModelTemporary(prepared.defaultModel, prepared.defaultThinkingLevel
|
|
252
|
+
await prepared.session.setModelTemporary(prepared.defaultModel, prepared.defaultThinkingLevel, {
|
|
253
|
+
persistAsSessionDefault: true,
|
|
254
|
+
});
|
|
213
255
|
modelChanged = true;
|
|
214
256
|
}
|
|
257
|
+
if (Object.keys(prepared.modelRoles).length > 0) {
|
|
258
|
+
prepared.settings.override("modelRoles", {
|
|
259
|
+
...prepared.settings.get("modelRoles"),
|
|
260
|
+
...prepared.modelRoles,
|
|
261
|
+
});
|
|
262
|
+
modelRolesChanged = true;
|
|
263
|
+
}
|
|
215
264
|
if (Object.keys(prepared.agentModelOverrides).length > 0) {
|
|
216
265
|
prepared.settings.override("task.agentModelOverrides", {
|
|
217
266
|
...prepared.settings.get("task.agentModelOverrides"),
|
|
@@ -229,12 +278,32 @@ export async function applyPreparedModelProfileActivation(
|
|
|
229
278
|
if (defaultChanged) {
|
|
230
279
|
prepared.settings.set("modelProfile.default", previousPersistedDefault);
|
|
231
280
|
}
|
|
281
|
+
if (modelRolesChanged) {
|
|
282
|
+
prepared.settings.override("modelRoles", previousModelRoles);
|
|
283
|
+
}
|
|
232
284
|
if (overridesChanged) {
|
|
233
285
|
prepared.settings.override("task.agentModelOverrides", previousAgentModelOverrides);
|
|
234
286
|
}
|
|
235
287
|
prepared.session.setActiveModelProfile?.(previousActiveModelProfile);
|
|
236
|
-
if (modelChanged
|
|
237
|
-
|
|
288
|
+
if (modelChanged) {
|
|
289
|
+
// Runtime rolls back to the pre-activation live model. That model may
|
|
290
|
+
// itself be a transient retry/fallback/context-promotion/plan switch,
|
|
291
|
+
// so it is recorded as role:"temporary" (NOT the resume default) to
|
|
292
|
+
// preserve the issue #849 protection.
|
|
293
|
+
if (previousModel) {
|
|
294
|
+
await prepared.session.setModelTemporary(previousModel, previousThinkingLevel);
|
|
295
|
+
}
|
|
296
|
+
// The happy path already appended the profile main model as the resume
|
|
297
|
+
// default (role:"default"). Re-assert the pre-activation resume default
|
|
298
|
+
// so a failed activation does not poison future resume. Fall back to the
|
|
299
|
+
// live model only when there was no explicit pre-activation default
|
|
300
|
+
// (nothing to protect). Append-only — never touches the runtime model.
|
|
301
|
+
const restoreDefaultSelector =
|
|
302
|
+
previousSessionDefaultModel ??
|
|
303
|
+
(previousModel ? `${previousModel.provider}/${previousModel.id}` : undefined);
|
|
304
|
+
if (restoreDefaultSelector) {
|
|
305
|
+
prepared.session.recordResumeDefaultModel?.(restoreDefaultSelector);
|
|
306
|
+
}
|
|
238
307
|
}
|
|
239
308
|
throw error;
|
|
240
309
|
}
|
|
@@ -23,7 +23,8 @@ export interface ModelProfileDefinition {
|
|
|
23
23
|
|
|
24
24
|
export interface ResolvedProfileBinding {
|
|
25
25
|
defaultSelector?: string;
|
|
26
|
-
|
|
26
|
+
modelRoles: Partial<Record<"vision", string>>;
|
|
27
|
+
agentModelOverrides: Partial<Record<Exclude<ModelProfileRole, "default" | "vision">, string>>;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
function parseModelSelectorProvider(selector: string): string | undefined {
|
|
@@ -56,7 +57,7 @@ export function aggregateModelProfileRequiredProviders(
|
|
|
56
57
|
const profile = (
|
|
57
58
|
name: string,
|
|
58
59
|
requiredProviders: string[],
|
|
59
|
-
modelMapping: Record<ModelProfileRole, string
|
|
60
|
+
modelMapping: Partial<Record<ModelProfileRole, string>>,
|
|
60
61
|
alternativeProviderGroups?: readonly (readonly string[])[],
|
|
61
62
|
): ModelProfileDefinition => ({
|
|
62
63
|
name,
|
|
@@ -382,13 +383,15 @@ export function mergeModelProfiles(userProfiles?: ModelsConfig["profiles"]): Map
|
|
|
382
383
|
}
|
|
383
384
|
|
|
384
385
|
export function resolveProfileBindings(definition: ModelProfileDefinition): ResolvedProfileBinding {
|
|
385
|
-
const { default: defaultSelector, executor, architect, planner, critic } = definition.modelMapping;
|
|
386
|
+
const { default: defaultSelector, vision, executor, architect, planner, critic } = definition.modelMapping;
|
|
387
|
+
const modelRoles: ResolvedProfileBinding["modelRoles"] = {};
|
|
388
|
+
if (vision !== undefined) modelRoles.vision = vision;
|
|
386
389
|
const agentModelOverrides: ResolvedProfileBinding["agentModelOverrides"] = {};
|
|
387
390
|
if (executor !== undefined) agentModelOverrides.executor = executor;
|
|
388
391
|
if (architect !== undefined) agentModelOverrides.architect = architect;
|
|
389
392
|
if (planner !== undefined) agentModelOverrides.planner = planner;
|
|
390
393
|
if (critic !== undefined) agentModelOverrides.critic = critic;
|
|
391
|
-
return { defaultSelector, agentModelOverrides };
|
|
394
|
+
return { defaultSelector, modelRoles, agentModelOverrides };
|
|
392
395
|
}
|
|
393
396
|
|
|
394
397
|
export function formatAvailableProfileNames(profiles: ReadonlyMap<string, ModelProfileDefinition>): string {
|
|
@@ -70,7 +70,7 @@ export function isAuthenticated(apiKey: string | undefined | null): apiKey is st
|
|
|
70
70
|
return Boolean(apiKey) && apiKey !== kNoAuth;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
export type ModelRole = "default";
|
|
73
|
+
export type ModelRole = "default" | "vision";
|
|
74
74
|
|
|
75
75
|
export interface ModelRoleInfo {
|
|
76
76
|
tag?: string;
|
|
@@ -80,11 +80,12 @@ export interface ModelRoleInfo {
|
|
|
80
80
|
|
|
81
81
|
export const MODEL_ROLES: Record<ModelRole, ModelRoleInfo> = {
|
|
82
82
|
default: { tag: "DEFAULT", name: "Default", color: "success" },
|
|
83
|
+
vision: { tag: "VISION", name: "Vision", color: "accent" },
|
|
83
84
|
};
|
|
84
85
|
|
|
85
|
-
export const MODEL_ROLE_IDS: ModelRole[] = ["default"];
|
|
86
|
+
export const MODEL_ROLE_IDS: ModelRole[] = ["default", "vision"];
|
|
86
87
|
|
|
87
|
-
export type GjcModelAssignmentTargetId = "default" | "executor" | "architect" | "planner" | "critic";
|
|
88
|
+
export type GjcModelAssignmentTargetId = "default" | "vision" | "executor" | "architect" | "planner" | "critic";
|
|
88
89
|
|
|
89
90
|
export interface GjcModelAssignmentTargetInfo extends ModelRoleInfo {
|
|
90
91
|
id: GjcModelAssignmentTargetId;
|
|
@@ -93,6 +94,7 @@ export interface GjcModelAssignmentTargetInfo extends ModelRoleInfo {
|
|
|
93
94
|
|
|
94
95
|
export const GJC_MODEL_ASSIGNMENT_TARGET_IDS: GjcModelAssignmentTargetId[] = [
|
|
95
96
|
"default",
|
|
97
|
+
"vision",
|
|
96
98
|
"executor",
|
|
97
99
|
"architect",
|
|
98
100
|
"planner",
|
|
@@ -101,6 +103,7 @@ export const GJC_MODEL_ASSIGNMENT_TARGET_IDS: GjcModelAssignmentTargetId[] = [
|
|
|
101
103
|
|
|
102
104
|
export const GJC_MODEL_ASSIGNMENT_TARGETS: Record<GjcModelAssignmentTargetId, GjcModelAssignmentTargetInfo> = {
|
|
103
105
|
default: { id: "default", tag: "DEFAULT", name: "Default", color: "success", settingsPath: "modelRoles" },
|
|
106
|
+
vision: { id: "vision", tag: "VISION", name: "Vision", color: "accent", settingsPath: "modelRoles" },
|
|
104
107
|
executor: {
|
|
105
108
|
id: "executor",
|
|
106
109
|
tag: "EXECUTOR",
|
|
@@ -81,7 +81,7 @@ const ModelBindingsSchema = z.object({
|
|
|
81
81
|
modelRoles: z.record(z.string(), z.string().min(1)).optional(),
|
|
82
82
|
agentModelOverrides: z.record(z.string(), z.string().min(1)).optional(),
|
|
83
83
|
});
|
|
84
|
-
export const ProfileRoleSchema = z.enum(["default", "executor", "architect", "planner", "critic"]);
|
|
84
|
+
export const ProfileRoleSchema = z.enum(["default", "vision", "executor", "architect", "planner", "critic"]);
|
|
85
85
|
|
|
86
86
|
function isValidProfileModelSelector(value: string): boolean {
|
|
87
87
|
if (value.includes(",")) return false;
|
|
@@ -254,6 +254,22 @@ export const SETTINGS_SCHEMA = {
|
|
|
254
254
|
"auth.broker.url": { type: "string", default: undefined },
|
|
255
255
|
"auth.broker.token": { type: "string", default: undefined },
|
|
256
256
|
|
|
257
|
+
// Notifications (Telegram bundled reference client)
|
|
258
|
+
"notifications.enabled": { type: "boolean", default: false },
|
|
259
|
+
"notifications.telegram.botToken": { type: "string", default: undefined },
|
|
260
|
+
"notifications.telegram.chatId": { type: "string", default: undefined },
|
|
261
|
+
"notifications.redact": { type: "boolean", default: false },
|
|
262
|
+
"notifications.verbosity": {
|
|
263
|
+
type: "string",
|
|
264
|
+
default: "lean",
|
|
265
|
+
validate: (value: string) => value === "lean" || value === "verbose",
|
|
266
|
+
},
|
|
267
|
+
"notifications.daemon.idleTimeoutMs": {
|
|
268
|
+
type: "number",
|
|
269
|
+
default: 60000,
|
|
270
|
+
validate: (value: number) => Number.isFinite(value) && value > 0,
|
|
271
|
+
},
|
|
272
|
+
|
|
257
273
|
autoResume: {
|
|
258
274
|
type: "boolean",
|
|
259
275
|
default: false,
|
|
@@ -3155,6 +3171,18 @@ export interface ShellMinimizerSettings {
|
|
|
3155
3171
|
maxCaptureBytes: number;
|
|
3156
3172
|
}
|
|
3157
3173
|
|
|
3174
|
+
export interface NotificationsSettings {
|
|
3175
|
+
enabled: boolean;
|
|
3176
|
+
telegram: {
|
|
3177
|
+
botToken: string | undefined;
|
|
3178
|
+
chatId: string | undefined;
|
|
3179
|
+
};
|
|
3180
|
+
redact: boolean;
|
|
3181
|
+
daemon: {
|
|
3182
|
+
idleTimeoutMs: number;
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3158
3186
|
/** Map group prefix -> typed settings interface */
|
|
3159
3187
|
export interface GroupTypeMap {
|
|
3160
3188
|
compaction: CompactionSettings;
|
|
@@ -3173,6 +3201,7 @@ export interface GroupTypeMap {
|
|
|
3173
3201
|
modelTags: ModelTagsSettings;
|
|
3174
3202
|
cycleOrder: string[];
|
|
3175
3203
|
shellMinimizer: ShellMinimizerSettings;
|
|
3204
|
+
notifications: NotificationsSettings;
|
|
3176
3205
|
}
|
|
3177
3206
|
|
|
3178
3207
|
export type GroupPrefix = keyof GroupTypeMap;
|
|
@@ -17,6 +17,9 @@ export const COORDINATOR_MCP_TOOL_NAMES = [
|
|
|
17
17
|
"gjc_coordinator_read_turn",
|
|
18
18
|
"gjc_coordinator_await_turn",
|
|
19
19
|
"gjc_coordinator_report_status",
|
|
20
|
+
"gjc_delegate_plan",
|
|
21
|
+
"gjc_delegate_execute",
|
|
22
|
+
"gjc_delegate_team",
|
|
20
23
|
] as const;
|
|
21
24
|
|
|
22
25
|
export type CoordinatorToolName = (typeof COORDINATOR_MCP_TOOL_NAMES)[number];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { coordinatorMcpStateRoot, gjcRoot } from "../gjc-runtime/session-layout";
|
|
3
4
|
|
|
4
5
|
export type CoordinatorMutationClass = "sessions" | "questions" | "reports";
|
|
5
6
|
|
|
@@ -73,9 +74,16 @@ function cleanScope(value: string | undefined): string | null {
|
|
|
73
74
|
return trimmed.replace(/[^a-zA-Z0-9_.-]+/g, "-").slice(0, 100) || null;
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
function defaultCoordinatorMcpStateRoot(cwd: string, gjcSessionId?: string): string {
|
|
78
|
+
return gjcSessionId
|
|
79
|
+
? coordinatorMcpStateRoot(cwd, gjcSessionId)
|
|
80
|
+
: path.join(gjcRoot(cwd), "state", "coordinator-mcp");
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
export function buildCoordinatorMcpConfig(env: NodeJS.ProcessEnv = process.env): CoordinatorMcpConfig {
|
|
77
|
-
const
|
|
78
|
-
|
|
84
|
+
const stateRootOverride = env.GJC_COORDINATOR_MCP_STATE_ROOT?.trim();
|
|
85
|
+
const gjcSessionId = env.GJC_SESSION_ID?.trim();
|
|
86
|
+
const stateRoot = stateRootOverride || defaultCoordinatorMcpStateRoot(process.cwd(), gjcSessionId);
|
|
79
87
|
return {
|
|
80
88
|
allowedRoots: parseRootList(env.GJC_COORDINATOR_MCP_WORKDIR_ROOTS).map(root => path.resolve(root)),
|
|
81
89
|
mutationClasses: parseMutationClasses(
|