@voybio/ace-swarm 2.4.0 → 2.4.1
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 +8 -0
- package/README.md +1 -0
- package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +43 -0
- package/assets/agent-state/runtime-tool-specs.json +70 -2
- package/assets/instructions/ACE_Coder.instructions.md +13 -0
- package/assets/instructions/ACE_UI.instructions.md +11 -0
- package/dist/ace-context.js +70 -11
- package/dist/ace-internal-tools.d.ts +3 -1
- package/dist/ace-internal-tools.js +10 -2
- package/dist/agent-runtime/role-adapters.d.ts +18 -1
- package/dist/agent-runtime/role-adapters.js +49 -5
- package/dist/astgrep-index.d.ts +48 -0
- package/dist/astgrep-index.js +126 -1
- package/dist/cli.js +205 -15
- package/dist/discovery-runtime-wrappers.d.ts +108 -0
- package/dist/discovery-runtime-wrappers.js +615 -0
- package/dist/helpers/bootstrap.js +1 -1
- package/dist/helpers/constants.d.ts +2 -2
- package/dist/helpers/constants.js +7 -0
- package/dist/helpers/path-utils.d.ts +8 -1
- package/dist/helpers/path-utils.js +27 -8
- package/dist/helpers/store-resolution.js +7 -3
- package/dist/job-scheduler.js +30 -4
- package/dist/json-sanitizer.d.ts +16 -0
- package/dist/json-sanitizer.js +26 -0
- package/dist/local-model-policy.d.ts +27 -0
- package/dist/local-model-policy.js +84 -0
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +21 -20
- package/dist/model-bridge.d.ts +6 -1
- package/dist/model-bridge.js +338 -21
- package/dist/orchestrator-supervisor.d.ts +42 -0
- package/dist/orchestrator-supervisor.js +110 -3
- package/dist/plan-proposal.d.ts +115 -0
- package/dist/plan-proposal.js +1073 -0
- package/dist/runtime-executor.d.ts +6 -1
- package/dist/runtime-executor.js +72 -5
- package/dist/runtime-tool-specs.d.ts +19 -1
- package/dist/runtime-tool-specs.js +67 -26
- package/dist/schemas.js +29 -1
- package/dist/server.js +51 -0
- package/dist/shared.d.ts +1 -0
- package/dist/shared.js +2 -0
- package/dist/store/bootstrap-store.d.ts +1 -0
- package/dist/store/bootstrap-store.js +8 -2
- package/dist/store/repositories/local-model-runtime-repository.d.ts +1 -1
- package/dist/store/repositories/local-model-runtime-repository.js +1 -1
- package/dist/store/repositories/vericify-repository.d.ts +1 -1
- package/dist/tools-agent.d.ts +20 -0
- package/dist/tools-agent.js +538 -28
- package/dist/tools-discovery.js +135 -0
- package/dist/tools-files.js +768 -66
- package/dist/tools-framework.js +80 -61
- package/dist/tui/index.js +10 -1
- package/dist/tui/ollama.d.ts +8 -1
- package/dist/tui/ollama.js +53 -12
- package/dist/tui/openai-compatible.d.ts +13 -0
- package/dist/tui/openai-compatible.js +305 -5
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +35 -11
- package/dist/vericify-bridge.d.ts +1 -1
- package/package.json +1 -1
|
@@ -71,6 +71,7 @@ export const ALL_AGENTS = [
|
|
|
71
71
|
"observability",
|
|
72
72
|
"eval",
|
|
73
73
|
"release",
|
|
74
|
+
"planner",
|
|
74
75
|
];
|
|
75
76
|
export const SWARM_AGENTS = ["orchestrator", "vos", "ui", "coders"];
|
|
76
77
|
export const COMPOSABLE_AGENTS = [
|
|
@@ -87,6 +88,7 @@ export const COMPOSABLE_AGENTS = [
|
|
|
87
88
|
"observability",
|
|
88
89
|
"eval",
|
|
89
90
|
"release",
|
|
91
|
+
"planner",
|
|
90
92
|
];
|
|
91
93
|
export const SWARM_SUBAGENT_MAP = {
|
|
92
94
|
orchestrator: COMPOSABLE_AGENTS,
|
|
@@ -145,6 +147,7 @@ export const AGENT_FILES = {
|
|
|
145
147
|
],
|
|
146
148
|
eval: [".agents/ACE/agent-eval/instructions.md", ".agents/ACE/agent-eval/AGENTS.md"],
|
|
147
149
|
release: [".agents/ACE/agent-release/instructions.md", ".agents/ACE/agent-release/AGENTS.md"],
|
|
150
|
+
planner: [".agents/ACE/agent-planner/instructions.md", ".agents/ACE/agent-planner/AGENTS.md"],
|
|
148
151
|
};
|
|
149
152
|
export const AGENT_MANIFEST_FILES = {
|
|
150
153
|
orchestrator: [
|
|
@@ -167,6 +170,7 @@ export const AGENT_MANIFEST_FILES = {
|
|
|
167
170
|
observability: [".agents/ACE/agent-observability/AGENTS.md"],
|
|
168
171
|
eval: [".agents/ACE/agent-eval/AGENTS.md"],
|
|
169
172
|
release: [".agents/ACE/agent-release/AGENTS.md"],
|
|
173
|
+
planner: [".agents/ACE/agent-planner/AGENTS.md"],
|
|
170
174
|
};
|
|
171
175
|
export const TASK_FILES = {
|
|
172
176
|
todo: [`${ACE_TASKS_ROOT_REL}/todo.md`],
|
|
@@ -208,6 +212,7 @@ export const DEFAULT_AGENT_INSTRUCTION_FILES = {
|
|
|
208
212
|
observability: resolve(DEFAULTS_ROOT, ".agents", "ACE", "agent-observability", "instructions.md"),
|
|
209
213
|
eval: resolve(DEFAULTS_ROOT, ".agents", "ACE", "agent-eval", "instructions.md"),
|
|
210
214
|
release: resolve(DEFAULTS_ROOT, ".agents", "ACE", "agent-release", "instructions.md"),
|
|
215
|
+
planner: resolve(DEFAULTS_ROOT, ".agents", "ACE", "agent-planner", "instructions.md"),
|
|
211
216
|
};
|
|
212
217
|
export const DEFAULT_AGENT_MANIFEST_FILES = {
|
|
213
218
|
orchestrator: resolve(DEFAULTS_ROOT, ".agents", "ACE", "orchestrator", "AGENTS.md"),
|
|
@@ -227,6 +232,7 @@ export const DEFAULT_AGENT_MANIFEST_FILES = {
|
|
|
227
232
|
observability: resolve(DEFAULTS_ROOT, ".agents", "ACE", "agent-observability", "AGENTS.md"),
|
|
228
233
|
eval: resolve(DEFAULTS_ROOT, ".agents", "ACE", "agent-eval", "AGENTS.md"),
|
|
229
234
|
release: resolve(DEFAULTS_ROOT, ".agents", "ACE", "agent-release", "AGENTS.md"),
|
|
235
|
+
planner: resolve(DEFAULTS_ROOT, ".agents", "ACE", "agent-planner", "AGENTS.md"),
|
|
230
236
|
};
|
|
231
237
|
export const DEFAULT_TASK_FILES = {
|
|
232
238
|
todo: resolve(DEFAULTS_TASKS_ROOT, "todo.md"),
|
|
@@ -269,6 +275,7 @@ export const STORE_AGENT_FILES = {
|
|
|
269
275
|
},
|
|
270
276
|
eval: { agent: "eval", instructions: ["instructions.md", "AGENTS.md"], manifests: ["AGENTS.md"] },
|
|
271
277
|
release: { agent: "release", instructions: ["instructions.md", "AGENTS.md"], manifests: ["AGENTS.md"] },
|
|
278
|
+
planner: { agent: "planner", instructions: ["instructions.md", "AGENTS.md"], manifests: ["AGENTS.md"] },
|
|
272
279
|
};
|
|
273
280
|
export const STORE_TASK_FILES = {
|
|
274
281
|
todo: "knowledge/tasks/todo.md",
|
|
@@ -8,7 +8,14 @@ export declare function normalizePathForValidation(path: string): string;
|
|
|
8
8
|
export declare function isSwarmRole(role: string): role is (typeof SWARM_AGENTS)[number];
|
|
9
9
|
export declare function atomicWrite(filePath: string, content: string): string;
|
|
10
10
|
export declare function fileExists(filePath: string): boolean;
|
|
11
|
-
export
|
|
11
|
+
export interface WorkspacePathError extends Error {
|
|
12
|
+
reason_code: "path_escape" | "missing_workspace_path";
|
|
13
|
+
payload: {
|
|
14
|
+
reason_code: "path_escape" | "missing_workspace_path";
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveWorkspaceWritePath(filePath: string, workspaceRoot?: string): string;
|
|
12
19
|
export declare function resolveWorkspaceReadPath(filePath: string): string;
|
|
13
20
|
export declare function resolveWorkspaceArtifactPath(filePath: string, mode?: "read" | "write"): string;
|
|
14
21
|
export declare function toAbsoluteWorkspaceCandidates(candidates: string[]): string[];
|
|
@@ -54,18 +54,37 @@ export function fileExists(filePath) {
|
|
|
54
54
|
const abs = isAbsolute(filePath) ? filePath : resolveWorkspaceReadPath(filePath);
|
|
55
55
|
return existsSync(abs);
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
const
|
|
57
|
+
function workspacePathError(reasonCode, message) {
|
|
58
|
+
const error = new Error(message);
|
|
59
|
+
error.reason_code = reasonCode;
|
|
60
|
+
error.payload = {
|
|
61
|
+
reason_code: reasonCode,
|
|
62
|
+
message,
|
|
63
|
+
};
|
|
64
|
+
return error;
|
|
65
|
+
}
|
|
66
|
+
function looksLikeWindowsAbsolutePath(filePath) {
|
|
67
|
+
return /^[a-zA-Z]:[\\/]/.test(filePath) || /^\\\\/.test(filePath);
|
|
68
|
+
}
|
|
69
|
+
export function resolveWorkspaceWritePath(filePath, workspaceRoot = currentWorkspaceRoot()) {
|
|
70
|
+
const root = resolve(workspaceRoot);
|
|
59
71
|
if (dirname(root) === root) {
|
|
60
72
|
throw new Error("Workspace root resolved to filesystem root. Set ACE_WORKSPACE_ROOT or run ACE from the target workspace.");
|
|
61
73
|
}
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!isInside(root, abs)) {
|
|
66
|
-
throw new Error(`Path escapes workspace root: ${filePath}`);
|
|
74
|
+
const trimmed = filePath.trim();
|
|
75
|
+
if (!trimmed) {
|
|
76
|
+
throw workspacePathError("path_escape", "Workspace write path is empty.");
|
|
67
77
|
}
|
|
68
|
-
|
|
78
|
+
if (trimmed.startsWith("~") || isAbsolute(trimmed) || looksLikeWindowsAbsolutePath(trimmed)) {
|
|
79
|
+
throw workspacePathError("path_escape", `Workspace write path must be relative to workspace root: ${filePath}`);
|
|
80
|
+
}
|
|
81
|
+
const normalizedPath = mapAceWorkspaceRelativePath(normalizeRelPath(trimmed));
|
|
82
|
+
const target = resolve(root, normalizedPath);
|
|
83
|
+
const rel = relative(root, target);
|
|
84
|
+
if (rel.startsWith("..") || isAbsolute(rel) || !isInside(root, target)) {
|
|
85
|
+
throw workspacePathError("path_escape", `Path escapes workspace root: ${filePath}`);
|
|
86
|
+
}
|
|
87
|
+
return target;
|
|
69
88
|
}
|
|
70
89
|
export function resolveWorkspaceReadPath(filePath) {
|
|
71
90
|
const root = currentWorkspaceRoot();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { mkdir, rename, writeFile } from "node:fs/promises";
|
|
3
|
-
import { dirname } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute } from "node:path";
|
|
4
4
|
import { isInside, normalizeRelPath } from "../shared.js";
|
|
5
5
|
import { getWorkspaceStorePath, listStoreKeysSync, parseVirtualStorePath, readStoreBlobSync, readVirtualStorePathSync, toVirtualStorePath, } from "../store/store-snapshot.js";
|
|
6
6
|
import { isOperationalArtifactPath, operationalArtifactKey, writeOperationalArtifactQueued, writeStoreBlobQueued, writeStoreBlobSync, } from "../store/store-artifacts.js";
|
|
@@ -234,7 +234,9 @@ export function safeWrite(filePath, content) {
|
|
|
234
234
|
return storeVirtualPath;
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
-
const abs =
|
|
237
|
+
const abs = isAbsolute(filePath) && isInside(root, filePath)
|
|
238
|
+
? filePath
|
|
239
|
+
: resolveWorkspaceWritePath(filePath, root);
|
|
238
240
|
mkdirSync(dirname(abs), { recursive: true });
|
|
239
241
|
const tmpPath = `${abs}.${process.pid}.${Date.now()}.tmp`;
|
|
240
242
|
writeFileSync(tmpPath, content, "utf-8");
|
|
@@ -271,7 +273,9 @@ export async function safeWriteAsync(filePath, content) {
|
|
|
271
273
|
return storeVirtualPath;
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
|
-
const abs =
|
|
276
|
+
const abs = isAbsolute(filePath) && isInside(root, filePath)
|
|
277
|
+
? filePath
|
|
278
|
+
: resolveWorkspaceWritePath(filePath, root);
|
|
275
279
|
await mkdir(dirname(abs), { recursive: true });
|
|
276
280
|
const tmpPath = `${abs}.${process.pid}.${Date.now()}.tmp`;
|
|
277
281
|
await writeFile(tmpPath, content, "utf-8");
|
package/dist/job-scheduler.js
CHANGED
|
@@ -114,6 +114,30 @@ function defaultJobLockFile() {
|
|
|
114
114
|
locks: [],
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
|
+
function coerceJobQueueFile(value) {
|
|
118
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
119
|
+
return undefined;
|
|
120
|
+
const candidate = value;
|
|
121
|
+
if (candidate.version !== 1)
|
|
122
|
+
return undefined;
|
|
123
|
+
return {
|
|
124
|
+
version: 1,
|
|
125
|
+
updated_at: typeof candidate.updated_at === "string" ? candidate.updated_at : nowIso(),
|
|
126
|
+
jobs: Array.isArray(candidate.jobs) ? candidate.jobs : [],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function coerceJobLockTableFile(value) {
|
|
130
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
131
|
+
return undefined;
|
|
132
|
+
const candidate = value;
|
|
133
|
+
if (candidate.version !== 1)
|
|
134
|
+
return undefined;
|
|
135
|
+
return {
|
|
136
|
+
version: 1,
|
|
137
|
+
updated_at: typeof candidate.updated_at === "string" ? candidate.updated_at : nowIso(),
|
|
138
|
+
locks: Array.isArray(candidate.locks) ? candidate.locks : [],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
117
141
|
function defaultLease(owner = "scheduler") {
|
|
118
142
|
const now = new Date();
|
|
119
143
|
const expires = new Date(now.getTime() + 30_000);
|
|
@@ -278,8 +302,9 @@ export function readJobQueue() {
|
|
|
278
302
|
const root = resolveWorkspaceRoot();
|
|
279
303
|
if (storeExistsSync(root)) {
|
|
280
304
|
const stored = readStoreJsonSync(root, "state/scheduler/queue");
|
|
281
|
-
|
|
282
|
-
|
|
305
|
+
const normalized = coerceJobQueueFile(stored);
|
|
306
|
+
if (normalized)
|
|
307
|
+
return normalized;
|
|
283
308
|
}
|
|
284
309
|
const raw = safeRead(JOB_QUEUE_REL);
|
|
285
310
|
if (isReadError(raw))
|
|
@@ -290,8 +315,9 @@ export function readJobLockTable() {
|
|
|
290
315
|
const root = resolveWorkspaceRoot();
|
|
291
316
|
if (storeExistsSync(root)) {
|
|
292
317
|
const stored = readStoreJsonSync(root, "state/scheduler/locks");
|
|
293
|
-
|
|
294
|
-
|
|
318
|
+
const normalized = coerceJobLockTableFile(stored);
|
|
319
|
+
if (normalized)
|
|
320
|
+
return normalized;
|
|
295
321
|
}
|
|
296
322
|
const raw = safeRead(JOB_LOCK_TABLE_REL);
|
|
297
323
|
if (isReadError(raw))
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface JsonTextSanitizationResult {
|
|
2
|
+
text: string;
|
|
3
|
+
removed_control_bytes: number;
|
|
4
|
+
had_bom: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function sanitizeJsonLikeText(raw: string): JsonTextSanitizationResult;
|
|
7
|
+
export declare function parseJsonLikeText(raw: string): {
|
|
8
|
+
ok: true;
|
|
9
|
+
value: unknown;
|
|
10
|
+
sanitized: JsonTextSanitizationResult;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
error: string;
|
|
14
|
+
sanitized: JsonTextSanitizationResult;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=json-sanitizer.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function sanitizeJsonLikeText(raw) {
|
|
2
|
+
const hadBom = raw.startsWith("\uFEFF");
|
|
3
|
+
let removed = 0;
|
|
4
|
+
const text = raw
|
|
5
|
+
.replace(/^\uFEFF/, "")
|
|
6
|
+
.replace(/\r\n?/g, "\n")
|
|
7
|
+
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, () => {
|
|
8
|
+
removed += 1;
|
|
9
|
+
return "";
|
|
10
|
+
});
|
|
11
|
+
return { text, removed_control_bytes: removed, had_bom: hadBom };
|
|
12
|
+
}
|
|
13
|
+
export function parseJsonLikeText(raw) {
|
|
14
|
+
const sanitized = sanitizeJsonLikeText(raw);
|
|
15
|
+
try {
|
|
16
|
+
return { ok: true, value: JSON.parse(sanitized.text), sanitized };
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
error: error instanceof Error ? error.message : String(error),
|
|
22
|
+
sanitized,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=json-sanitizer.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AceContextTier } from "./ace-context.js";
|
|
2
|
+
import { type RuntimeModelClass } from "./runtime-profile.js";
|
|
3
|
+
export interface LocalModelPolicyInput {
|
|
4
|
+
provider: string;
|
|
5
|
+
model: string;
|
|
6
|
+
role: string;
|
|
7
|
+
task?: string;
|
|
8
|
+
requested_tier?: "auto" | AceContextTier;
|
|
9
|
+
requested_model_class?: RuntimeModelClass;
|
|
10
|
+
}
|
|
11
|
+
export interface LocalModelExecutionPolicy {
|
|
12
|
+
model_class: RuntimeModelClass;
|
|
13
|
+
tier: AceContextTier;
|
|
14
|
+
mutation_lane: "structural_edit" | "raw_write_allowed";
|
|
15
|
+
structural_tools_required: boolean;
|
|
16
|
+
read_file_lines_max_lines: number | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveLocalModelClass(input: {
|
|
19
|
+
provider: string;
|
|
20
|
+
model: string;
|
|
21
|
+
requested_model_class?: RuntimeModelClass;
|
|
22
|
+
}): RuntimeModelClass;
|
|
23
|
+
export declare function tierForModelClass(role: string, modelClass: RuntimeModelClass, requestedTier?: "auto" | AceContextTier): AceContextTier;
|
|
24
|
+
export declare function defaultTurnsForPolicy(role: string, policy: LocalModelExecutionPolicy): number;
|
|
25
|
+
export declare function defaultToolScopeForPolicy(role: string, policy: LocalModelExecutionPolicy): string[] | undefined;
|
|
26
|
+
export declare function resolveLocalModelExecutionPolicy(input: LocalModelPolicyInput): LocalModelExecutionPolicy;
|
|
27
|
+
//# sourceMappingURL=local-model-policy.d.ts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { DEFAULT_SURGICAL_READ_BUDGETS, } from "./runtime-profile.js";
|
|
2
|
+
const FRONTIER_MODEL_PATTERN = /\b(70b|72b|90b|405b|claude|gpt-|codex|gemini|o[1345])\b/i;
|
|
3
|
+
const SMALL_LOCAL_MODEL_PATTERN = /\b(1\.5b|2b|3b|4b|5b|6b|7b|8b|mini|small|phi|tiny|qwen2\.5-coder|llama-3\.2-3b)\b/i;
|
|
4
|
+
const LOCAL_PROVIDER_PATTERN = /\b(ollama|llama\.cpp|llamacpp|openai-compatible|local)\b/i;
|
|
5
|
+
const HOSTED_PROVIDER_PATTERN = /\b(codex|claude|gemini|copilot)\b/i;
|
|
6
|
+
function normalize(value) {
|
|
7
|
+
return (value ?? "").trim().toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
function isMutationTask(task, role) {
|
|
10
|
+
if (role === "coders" || role === "builder")
|
|
11
|
+
return true;
|
|
12
|
+
return /\b(write|create|mutate|edit|patch|modify|persist|save|generate|implement)\b/i.test(task ?? "");
|
|
13
|
+
}
|
|
14
|
+
export function resolveLocalModelClass(input) {
|
|
15
|
+
if (input.requested_model_class)
|
|
16
|
+
return input.requested_model_class;
|
|
17
|
+
const provider = normalize(input.provider);
|
|
18
|
+
const model = normalize(input.model);
|
|
19
|
+
const joined = `${provider} ${model}`;
|
|
20
|
+
if (FRONTIER_MODEL_PATTERN.test(joined) || HOSTED_PROVIDER_PATTERN.test(provider) || provider === "openai") {
|
|
21
|
+
return "frontier";
|
|
22
|
+
}
|
|
23
|
+
if (SMALL_LOCAL_MODEL_PATTERN.test(model)) {
|
|
24
|
+
return "small_local";
|
|
25
|
+
}
|
|
26
|
+
if (LOCAL_PROVIDER_PATTERN.test(provider)) {
|
|
27
|
+
return "mid";
|
|
28
|
+
}
|
|
29
|
+
return "mid";
|
|
30
|
+
}
|
|
31
|
+
export function tierForModelClass(role, modelClass, requestedTier) {
|
|
32
|
+
if (requestedTier && requestedTier !== "auto")
|
|
33
|
+
return requestedTier;
|
|
34
|
+
if (role === "orchestrator")
|
|
35
|
+
return "compressed";
|
|
36
|
+
if (modelClass === "frontier")
|
|
37
|
+
return "full";
|
|
38
|
+
if (modelClass === "small_local")
|
|
39
|
+
return "brief";
|
|
40
|
+
return "compressed";
|
|
41
|
+
}
|
|
42
|
+
export function defaultTurnsForPolicy(role, policy) {
|
|
43
|
+
if (role === "orchestrator")
|
|
44
|
+
return 6;
|
|
45
|
+
return policy.model_class === "small_local" ? 4 : 4;
|
|
46
|
+
}
|
|
47
|
+
export function defaultToolScopeForPolicy(role, policy) {
|
|
48
|
+
if (!policy.structural_tools_required || !(role === "coders" || role === "builder")) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return [
|
|
52
|
+
"outline_file",
|
|
53
|
+
"astgrep_query",
|
|
54
|
+
"astgrep_locate",
|
|
55
|
+
"read_file_lines",
|
|
56
|
+
"compile_structural_edit",
|
|
57
|
+
"preview_structural_edit",
|
|
58
|
+
"astgrep_rewrite",
|
|
59
|
+
"safe_edit_file",
|
|
60
|
+
"write_workspace_file",
|
|
61
|
+
"run_tests",
|
|
62
|
+
"git_diff",
|
|
63
|
+
"git_status",
|
|
64
|
+
"execute_gates",
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
export function resolveLocalModelExecutionPolicy(input) {
|
|
68
|
+
const role = normalize(input.role);
|
|
69
|
+
const modelClass = resolveLocalModelClass({
|
|
70
|
+
provider: input.provider,
|
|
71
|
+
model: input.model,
|
|
72
|
+
requested_model_class: input.requested_model_class,
|
|
73
|
+
});
|
|
74
|
+
const mutationIntent = isMutationTask(input.task, role);
|
|
75
|
+
const structuralToolsRequired = modelClass === "small_local" && mutationIntent;
|
|
76
|
+
return {
|
|
77
|
+
model_class: modelClass,
|
|
78
|
+
tier: tierForModelClass(role, modelClass, input.requested_tier),
|
|
79
|
+
mutation_lane: structuralToolsRequired ? "structural_edit" : "raw_write_allowed",
|
|
80
|
+
structural_tools_required: structuralToolsRequired,
|
|
81
|
+
read_file_lines_max_lines: DEFAULT_SURGICAL_READ_BUDGETS[modelClass],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=local-model-policy.js.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type BridgeResult, type ModelBridgeClients } from "./model-bridge.js";
|
|
2
2
|
import type { AceContextTier } from "./ace-context.js";
|
|
3
|
+
import { type LocalModelExecutionPolicy } from "./local-model-policy.js";
|
|
4
|
+
import type { RuntimeModelClass } from "./runtime-profile.js";
|
|
3
5
|
export interface LocalModelRuntimeConfig {
|
|
4
6
|
workspaceRoot: string;
|
|
5
7
|
provider: string;
|
|
@@ -19,6 +21,7 @@ export interface RunLocalModelTaskOptions {
|
|
|
19
21
|
ollamaUrl?: string;
|
|
20
22
|
maxTurns?: number;
|
|
21
23
|
tier?: AceContextTier | "auto";
|
|
24
|
+
modelClass?: RuntimeModelClass;
|
|
22
25
|
toolScope?: string[];
|
|
23
26
|
clients?: ModelBridgeClients;
|
|
24
27
|
}
|
|
@@ -26,8 +29,10 @@ export interface RunLocalModelTaskResult {
|
|
|
26
29
|
runtime: LocalModelRuntimeConfig;
|
|
27
30
|
role: string;
|
|
28
31
|
routingSummary?: string;
|
|
32
|
+
policy: LocalModelExecutionPolicy;
|
|
29
33
|
result: BridgeResult;
|
|
30
34
|
}
|
|
35
|
+
export declare function resolveTier(requested: RunLocalModelTaskOptions["tier"], provider: string, model: string, role: string): AceContextTier;
|
|
31
36
|
export declare function createDefaultModelBridgeClients(runtime: Pick<LocalModelRuntimeConfig, "providerBaseUrls">): ModelBridgeClients;
|
|
32
37
|
export declare function resolveLocalModelRuntime(input: {
|
|
33
38
|
workspaceRoot?: string;
|
|
@@ -37,4 +42,5 @@ export declare function resolveLocalModelRuntime(input: {
|
|
|
37
42
|
ollamaUrl?: string;
|
|
38
43
|
}): LocalModelRuntimeConfig;
|
|
39
44
|
export declare function runLocalModelTask(options: RunLocalModelTaskOptions): Promise<RunLocalModelTaskResult>;
|
|
45
|
+
export { resolveLocalModelExecutionPolicy, resolveLocalModelClass } from "./local-model-policy.js";
|
|
40
46
|
//# sourceMappingURL=local-model-runtime.d.ts.map
|
|
@@ -6,6 +6,7 @@ import { ModelBridge } from "./model-bridge.js";
|
|
|
6
6
|
import { OllamaClient } from "./tui/ollama.js";
|
|
7
7
|
import { OpenAICompatibleClient, diagnoseChatRuntimeConfig, } from "./tui/openai-compatible.js";
|
|
8
8
|
import { discoverProviderContext } from "./tui/provider-discovery.js";
|
|
9
|
+
import { defaultToolScopeForPolicy, defaultTurnsForPolicy, resolveLocalModelExecutionPolicy, } from "./local-model-policy.js";
|
|
9
10
|
function extractTextContent(result) {
|
|
10
11
|
if (!result || typeof result !== "object")
|
|
11
12
|
return "";
|
|
@@ -50,22 +51,13 @@ async function resolveRole(task, sessionId, requestedRole) {
|
|
|
50
51
|
routingSummary,
|
|
51
52
|
};
|
|
52
53
|
}
|
|
53
|
-
function resolveTier(requested, provider, model, role) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (normalizedProvider !== "ollama")
|
|
61
|
-
return "full";
|
|
62
|
-
if (/(70b|72b|90b|405b|claude|gpt-|codex|gemini|o[1345])/.test(normalizedModel)) {
|
|
63
|
-
return "full";
|
|
64
|
-
}
|
|
65
|
-
if (/(7b|8b|mini|small|phi|tiny)/.test(normalizedModel)) {
|
|
66
|
-
return "brief";
|
|
67
|
-
}
|
|
68
|
-
return "compressed";
|
|
54
|
+
export function resolveTier(requested, provider, model, role) {
|
|
55
|
+
return resolveLocalModelExecutionPolicy({
|
|
56
|
+
provider,
|
|
57
|
+
model,
|
|
58
|
+
role,
|
|
59
|
+
requested_tier: requested,
|
|
60
|
+
}).tier;
|
|
69
61
|
}
|
|
70
62
|
export function createDefaultModelBridgeClients(runtime) {
|
|
71
63
|
const providerConfigs = {};
|
|
@@ -119,22 +111,31 @@ export async function runLocalModelTask(options) {
|
|
|
119
111
|
});
|
|
120
112
|
const bridge = new ModelBridge(options.clients ?? createDefaultModelBridgeClients(runtime));
|
|
121
113
|
const { role, routingSummary } = await resolveRole(options.task, undefined, options.role);
|
|
122
|
-
const
|
|
114
|
+
const policy = resolveLocalModelExecutionPolicy({
|
|
115
|
+
provider: runtime.provider,
|
|
116
|
+
model: runtime.model,
|
|
117
|
+
role,
|
|
118
|
+
task: options.task,
|
|
119
|
+
requested_tier: options.tier,
|
|
120
|
+
requested_model_class: options.modelClass,
|
|
121
|
+
});
|
|
123
122
|
const result = await bridge.run({
|
|
124
123
|
task: options.task,
|
|
125
124
|
role,
|
|
126
125
|
workspace: runtime.workspaceRoot,
|
|
127
|
-
tier,
|
|
128
|
-
maxTurns: options.maxTurns ?? (role
|
|
126
|
+
tier: policy.tier,
|
|
127
|
+
maxTurns: options.maxTurns ?? defaultTurnsForPolicy(role, policy),
|
|
129
128
|
provider: runtime.provider,
|
|
130
129
|
model: runtime.model,
|
|
131
|
-
toolScope: options.toolScope,
|
|
130
|
+
toolScope: options.toolScope ?? defaultToolScopeForPolicy(role, policy),
|
|
132
131
|
});
|
|
133
132
|
return {
|
|
134
133
|
runtime,
|
|
135
134
|
role,
|
|
136
135
|
routingSummary,
|
|
136
|
+
policy,
|
|
137
137
|
result,
|
|
138
138
|
};
|
|
139
139
|
}
|
|
140
|
+
export { resolveLocalModelExecutionPolicy, resolveLocalModelClass } from "./local-model-policy.js";
|
|
140
141
|
//# sourceMappingURL=local-model-runtime.js.map
|
package/dist/model-bridge.d.ts
CHANGED
|
@@ -19,12 +19,13 @@ export interface BridgeProgressEvent {
|
|
|
19
19
|
export interface BridgeResult {
|
|
20
20
|
bridge_id: string;
|
|
21
21
|
role: string;
|
|
22
|
-
status: "completed" | "needs_input" | "failed" | "max_turns";
|
|
22
|
+
status: "completed" | "blocked" | "needs_input" | "failed" | "max_turns";
|
|
23
23
|
summary: string;
|
|
24
24
|
turns: number;
|
|
25
25
|
tool_calls: BridgeToolResult[];
|
|
26
26
|
child_results: BridgeResult[];
|
|
27
27
|
evidence_refs?: string[];
|
|
28
|
+
reason_code?: string;
|
|
28
29
|
}
|
|
29
30
|
export interface ModelBridgeClients {
|
|
30
31
|
ollama: Pick<OllamaClient, "chat" | "abort">;
|
|
@@ -40,6 +41,10 @@ export interface ModelBridgeRunOptions {
|
|
|
40
41
|
provider: string;
|
|
41
42
|
model: string;
|
|
42
43
|
toolScope?: string[];
|
|
44
|
+
expectedArtifacts?: {
|
|
45
|
+
path: string;
|
|
46
|
+
required?: boolean;
|
|
47
|
+
}[];
|
|
43
48
|
parentBridge?: string;
|
|
44
49
|
onToolCall?: (tool: string, args: Record<string, unknown>) => void;
|
|
45
50
|
onToolResult?: (tool: string, result: BridgeToolResult) => void;
|