@sayknow-cli/coding-agent 0.5.24 → 0.5.25
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
CHANGED
|
@@ -2457,6 +2457,20 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
2457
2457
|
readonly type: "string";
|
|
2458
2458
|
readonly default: "";
|
|
2459
2459
|
};
|
|
2460
|
+
/**
|
|
2461
|
+
* Frontend planning model — the **domain** axis.
|
|
2462
|
+
*
|
|
2463
|
+
* When a planning role (planner/architect) is handed work that clearly reads
|
|
2464
|
+
* as frontend/UI, it takes this model instead of the ladder's answer. A
|
|
2465
|
+
* design-strong model is not "more capable" than a code-strong one; this is a
|
|
2466
|
+
* lateral swap. Unset disables it entirely, and implementation roles are
|
|
2467
|
+
* never swapped by domain — the user asked for the design model to plan the
|
|
2468
|
+
* frontend, not to write it.
|
|
2469
|
+
*/
|
|
2470
|
+
readonly "task.modelRouting.frontendModel": {
|
|
2471
|
+
readonly type: "string";
|
|
2472
|
+
readonly default: "";
|
|
2473
|
+
};
|
|
2460
2474
|
readonly "ttsr.enabled": {
|
|
2461
2475
|
readonly type: "boolean";
|
|
2462
2476
|
readonly default: true;
|
|
@@ -19,8 +19,31 @@ export interface TaskRoutingPolicy {
|
|
|
19
19
|
* this bar sits higher than the upgrade bar on purpose.
|
|
20
20
|
*/
|
|
21
21
|
minDowngradeConfidence: number;
|
|
22
|
+
/**
|
|
23
|
+
* Model for frontend planning, when the assignment reads as frontend work.
|
|
24
|
+
*
|
|
25
|
+
* This is the **domain** axis, not a rung on the ladder: a design-strong model
|
|
26
|
+
* is not "better" than a code-strong one, it is a different specialty. Only
|
|
27
|
+
* planning roles ever take it, and only laterally — the implementation roles
|
|
28
|
+
* stay on the difficulty ladder.
|
|
29
|
+
*/
|
|
30
|
+
frontendModel?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Bar for the lateral swap above. Directional bars do not apply here because
|
|
33
|
+
* neither direction is "spending more": being wrong either way costs quality,
|
|
34
|
+
* symmetrically, so one bar is the whole story.
|
|
35
|
+
*/
|
|
36
|
+
minDomainConfidence: number;
|
|
22
37
|
}
|
|
23
38
|
export declare const DEFAULT_TASK_ROUTING_POLICY: Omit<TaskRoutingPolicy, "tiers">;
|
|
39
|
+
/**
|
|
40
|
+
* Roles whose output is a plan or a design review.
|
|
41
|
+
*
|
|
42
|
+
* These are the only roles the domain swap applies to — the user's intent is
|
|
43
|
+
* "a design-strong model *plans* the frontend; implementation stays where it
|
|
44
|
+
* is". Executor keeps the difficulty ladder regardless of domain.
|
|
45
|
+
*/
|
|
46
|
+
export declare const PLANNING_ROLES: ReadonlySet<string>;
|
|
24
47
|
export interface TaskRoutingRequest {
|
|
25
48
|
agentName: string;
|
|
26
49
|
/** The assignment text the subagent will act on. */
|
|
@@ -31,7 +54,8 @@ export interface TaskRoutingRequest {
|
|
|
31
54
|
}
|
|
32
55
|
export interface TaskRoutingResult {
|
|
33
56
|
model: string;
|
|
34
|
-
|
|
57
|
+
/** Null when the move was a domain swap — that axis has no ladder. */
|
|
58
|
+
tier: TaskTier | null;
|
|
35
59
|
reason: string;
|
|
36
60
|
}
|
|
37
61
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@sayknow-cli/coding-agent",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.25",
|
|
5
5
|
"description": "Sayknow-CLI CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://sayknow-cli.com",
|
|
7
7
|
"author": "jaybeyond",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
55
55
|
"@babel/parser": "^7.29.3",
|
|
56
56
|
"@mozilla/readability": "^0.6.0",
|
|
57
|
-
"@sayknow-cli/stats": "0.5.
|
|
58
|
-
"@sayknow-cli/agent-core": "0.5.
|
|
59
|
-
"@sayknow-cli/ai": "0.5.
|
|
60
|
-
"@sayknow-cli/natives": "0.5.
|
|
61
|
-
"@sayknow-cli/tui": "0.5.
|
|
62
|
-
"@sayknow-cli/utils": "0.5.
|
|
57
|
+
"@sayknow-cli/stats": "0.5.25",
|
|
58
|
+
"@sayknow-cli/agent-core": "0.5.25",
|
|
59
|
+
"@sayknow-cli/ai": "0.5.25",
|
|
60
|
+
"@sayknow-cli/natives": "0.5.25",
|
|
61
|
+
"@sayknow-cli/tui": "0.5.25",
|
|
62
|
+
"@sayknow-cli/utils": "0.5.25",
|
|
63
63
|
"@puppeteer/browsers": "^2.13.0",
|
|
64
64
|
"@types/turndown": "5.0.6",
|
|
65
65
|
"@xterm/headless": "^6.0.0",
|
|
@@ -2039,6 +2039,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
2039
2039
|
"task.modelRouting.balancedModel": { type: "string", default: "" },
|
|
2040
2040
|
/** Most capable tier. Unclear cause, cross-cutting design, hard to undo. */
|
|
2041
2041
|
"task.modelRouting.deepModel": { type: "string", default: "" },
|
|
2042
|
+
/**
|
|
2043
|
+
* Frontend planning model — the **domain** axis.
|
|
2044
|
+
*
|
|
2045
|
+
* When a planning role (planner/architect) is handed work that clearly reads
|
|
2046
|
+
* as frontend/UI, it takes this model instead of the ladder's answer. A
|
|
2047
|
+
* design-strong model is not "more capable" than a code-strong one; this is a
|
|
2048
|
+
* lateral swap. Unset disables it entirely, and implementation roles are
|
|
2049
|
+
* never swapped by domain — the user asked for the design model to plan the
|
|
2050
|
+
* frontend, not to write it.
|
|
2051
|
+
*/
|
|
2052
|
+
"task.modelRouting.frontendModel": { type: "string", default: "" },
|
|
2042
2053
|
|
|
2043
2054
|
// TTSR
|
|
2044
2055
|
"ttsr.enabled": {
|
|
@@ -37,6 +37,21 @@ export interface TaskRoutingPolicy {
|
|
|
37
37
|
* this bar sits higher than the upgrade bar on purpose.
|
|
38
38
|
*/
|
|
39
39
|
minDowngradeConfidence: number;
|
|
40
|
+
/**
|
|
41
|
+
* Model for frontend planning, when the assignment reads as frontend work.
|
|
42
|
+
*
|
|
43
|
+
* This is the **domain** axis, not a rung on the ladder: a design-strong model
|
|
44
|
+
* is not "better" than a code-strong one, it is a different specialty. Only
|
|
45
|
+
* planning roles ever take it, and only laterally — the implementation roles
|
|
46
|
+
* stay on the difficulty ladder.
|
|
47
|
+
*/
|
|
48
|
+
frontendModel?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Bar for the lateral swap above. Directional bars do not apply here because
|
|
51
|
+
* neither direction is "spending more": being wrong either way costs quality,
|
|
52
|
+
* symmetrically, so one bar is the whole story.
|
|
53
|
+
*/
|
|
54
|
+
minDomainConfidence: number;
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
export const DEFAULT_TASK_ROUTING_POLICY: Omit<TaskRoutingPolicy, "tiers"> = {
|
|
@@ -46,8 +61,18 @@ export const DEFAULT_TASK_ROUTING_POLICY: Omit<TaskRoutingPolicy, "tiers"> = {
|
|
|
46
61
|
// overriding one needs a stronger signal in either direction.
|
|
47
62
|
minUpgradeConfidence: 0.5,
|
|
48
63
|
minDowngradeConfidence: 0.75,
|
|
64
|
+
minDomainConfidence: 0.6,
|
|
49
65
|
};
|
|
50
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Roles whose output is a plan or a design review.
|
|
69
|
+
*
|
|
70
|
+
* These are the only roles the domain swap applies to — the user's intent is
|
|
71
|
+
* "a design-strong model *plans* the frontend; implementation stays where it
|
|
72
|
+
* is". Executor keeps the difficulty ladder regardless of domain.
|
|
73
|
+
*/
|
|
74
|
+
export const PLANNING_ROLES: ReadonlySet<string> = new Set(["planner", "architect"]);
|
|
75
|
+
|
|
51
76
|
/**
|
|
52
77
|
* The questions describe the *work*, never a model name.
|
|
53
78
|
*
|
|
@@ -71,6 +96,11 @@ function buildQuestions(): Record<string, Question> {
|
|
|
71
96
|
instructions:
|
|
72
97
|
"Does this assignment touch production, money, credentials, published releases, or state that cannot be undone?",
|
|
73
98
|
},
|
|
99
|
+
domain: {
|
|
100
|
+
type: "noul",
|
|
101
|
+
instructions:
|
|
102
|
+
"Is this assignment frontend/UI work — interfaces, components, visual design, styling or interaction — rather than data, APIs, infrastructure or business logic?",
|
|
103
|
+
},
|
|
74
104
|
};
|
|
75
105
|
}
|
|
76
106
|
|
|
@@ -85,7 +115,8 @@ export interface TaskRoutingRequest {
|
|
|
85
115
|
|
|
86
116
|
export interface TaskRoutingResult {
|
|
87
117
|
model: string;
|
|
88
|
-
|
|
118
|
+
/** Null when the move was a domain swap — that axis has no ladder. */
|
|
119
|
+
tier: TaskTier | null;
|
|
89
120
|
reason: string;
|
|
90
121
|
}
|
|
91
122
|
|
|
@@ -140,9 +171,9 @@ export async function routeTaskModel(
|
|
|
140
171
|
request: TaskRoutingRequest,
|
|
141
172
|
): Promise<TaskRoutingResult | null> {
|
|
142
173
|
const configured = TASK_TIERS.filter(tier => policy.tiers[tier]);
|
|
143
|
-
|
|
144
|
-
//
|
|
145
|
-
if (configured.length < 2) return null;
|
|
174
|
+
const frontendModel = policy.frontendModel?.trim() || undefined;
|
|
175
|
+
// Neither axis has anything to move on: no ladder and no domain model.
|
|
176
|
+
if (configured.length < 2 && !frontendModel) return null;
|
|
146
177
|
|
|
147
178
|
const assignment = request.assignment.trim();
|
|
148
179
|
if (assignment.length < 24) return null;
|
|
@@ -154,6 +185,37 @@ export async function routeTaskModel(
|
|
|
154
185
|
});
|
|
155
186
|
if (!result) return null;
|
|
156
187
|
|
|
188
|
+
// --- Domain axis: lateral swap for planning roles ---
|
|
189
|
+
//
|
|
190
|
+
// A design-strong model is not "more capable" than a code-strong one, so this
|
|
191
|
+
// is not a rung on the ladder and the directional bars do not apply. When the
|
|
192
|
+
// assignment clearly reads as frontend work and a frontend model is
|
|
193
|
+
// configured, planning roles take it — that is the whole of the user's
|
|
194
|
+
// intent: the design model *plans* the frontend, implementation stays put.
|
|
195
|
+
// When the swap fires, the difficulty ladder is skipped entirely for this
|
|
196
|
+
// spawn; for planning, design judgment is the point, not raw capability.
|
|
197
|
+
const domainAnswer = result.answers.domain;
|
|
198
|
+
if (
|
|
199
|
+
frontendModel &&
|
|
200
|
+
PLANNING_ROLES.has(request.agentName) &&
|
|
201
|
+
domainAnswer?.type === "noul" &&
|
|
202
|
+
domainAnswer.noul >= policy.minDomainConfidence
|
|
203
|
+
) {
|
|
204
|
+
if (request.currentModel && matchesModel(frontendModel, request.currentModel)) return null;
|
|
205
|
+
logger.debug("decisions/task-routing: routed", {
|
|
206
|
+
agent: request.agentName,
|
|
207
|
+
model: frontendModel,
|
|
208
|
+
reason: `frontend (domain ${domainAnswer.noul.toFixed(2)})`,
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
model: frontendModel,
|
|
212
|
+
tier: null,
|
|
213
|
+
reason: `frontend (domain ${domainAnswer.noul.toFixed(2)})`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// --- Difficulty axis: the ladder ---
|
|
218
|
+
if (configured.length < 2) return null;
|
|
157
219
|
const answer = result.answers.tier;
|
|
158
220
|
if (answer?.type !== "choice") return null;
|
|
159
221
|
let tier = TASK_TIERS.find(candidate => candidate === answer.choice);
|
package/src/task/index.ts
CHANGED
|
@@ -484,7 +484,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
484
484
|
balanced: this.session.settings.get("task.modelRouting.balancedModel") || undefined,
|
|
485
485
|
deep: this.session.settings.get("task.modelRouting.deepModel") || undefined,
|
|
486
486
|
};
|
|
487
|
-
|
|
487
|
+
const frontendModel = this.session.settings.get("task.modelRouting.frontendModel") || undefined;
|
|
488
|
+
if (Object.values(tiers).filter(Boolean).length < 2 && !frontendModel) return undefined;
|
|
488
489
|
|
|
489
490
|
const assignment = (tasks ?? [])
|
|
490
491
|
.map(task => [task.description, task.assignment].filter(Boolean).join("\n"))
|
|
@@ -499,7 +500,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
499
500
|
if (!registry) return undefined;
|
|
500
501
|
const routed = await routeTaskModel(
|
|
501
502
|
createDecisionService({ registry, settings: this.session.settings, enabled: true }),
|
|
502
|
-
{ ...DEFAULT_TASK_ROUTING_POLICY, tiers },
|
|
503
|
+
{ ...DEFAULT_TASK_ROUTING_POLICY, tiers, frontendModel },
|
|
503
504
|
// A role may be configured with a fallback chain; the first entry is what it
|
|
504
505
|
// actually runs on, so that is the baseline the direction is measured from.
|
|
505
506
|
{
|