@voybio/ace-swarm 0.2.5 → 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.
Files changed (144) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/README.md +21 -13
  3. package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
  4. package/assets/agent-state/EVIDENCE_LOG.md +1 -1
  5. package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
  6. package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
  7. package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
  8. package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
  9. package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
  10. package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +43 -0
  11. package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
  12. package/assets/agent-state/STATUS.md +2 -2
  13. package/assets/agent-state/runtime-tool-specs.json +70 -2
  14. package/assets/instructions/ACE_Coder.instructions.md +13 -0
  15. package/assets/instructions/ACE_UI.instructions.md +11 -0
  16. package/assets/scripts/ace-hook-dispatch.mjs +70 -6
  17. package/assets/scripts/render-mcp-configs.sh +19 -5
  18. package/dist/ace-context.js +91 -11
  19. package/dist/ace-internal-tools.d.ts +3 -1
  20. package/dist/ace-internal-tools.js +10 -2
  21. package/dist/ace-server-instructions.js +3 -3
  22. package/dist/ace-state-resolver.js +5 -3
  23. package/dist/agent-runtime/role-adapters.d.ts +18 -1
  24. package/dist/agent-runtime/role-adapters.js +49 -5
  25. package/dist/astgrep-index.d.ts +57 -1
  26. package/dist/astgrep-index.js +140 -4
  27. package/dist/cli.js +232 -35
  28. package/dist/discovery-runtime-wrappers.d.ts +108 -0
  29. package/dist/discovery-runtime-wrappers.js +615 -0
  30. package/dist/handoff-registry.js +5 -5
  31. package/dist/helpers/artifacts.d.ts +19 -0
  32. package/dist/helpers/artifacts.js +152 -0
  33. package/dist/helpers/bootstrap.d.ts +24 -0
  34. package/dist/helpers/bootstrap.js +894 -0
  35. package/dist/helpers/constants.d.ts +53 -0
  36. package/dist/helpers/constants.js +295 -0
  37. package/dist/helpers/drift.d.ts +13 -0
  38. package/dist/helpers/drift.js +45 -0
  39. package/dist/helpers/path-utils.d.ts +24 -0
  40. package/dist/helpers/path-utils.js +123 -0
  41. package/dist/helpers/store-resolution.d.ts +19 -0
  42. package/dist/helpers/store-resolution.js +305 -0
  43. package/dist/helpers/workspace-root.d.ts +3 -0
  44. package/dist/helpers/workspace-root.js +80 -0
  45. package/dist/helpers.d.ts +8 -125
  46. package/dist/helpers.js +8 -1768
  47. package/dist/job-scheduler.js +33 -7
  48. package/dist/json-sanitizer.d.ts +16 -0
  49. package/dist/json-sanitizer.js +26 -0
  50. package/dist/local-model-policy.d.ts +27 -0
  51. package/dist/local-model-policy.js +84 -0
  52. package/dist/local-model-runtime.d.ts +6 -0
  53. package/dist/local-model-runtime.js +33 -21
  54. package/dist/model-bridge.d.ts +13 -1
  55. package/dist/model-bridge.js +410 -23
  56. package/dist/orchestrator-supervisor.d.ts +56 -0
  57. package/dist/orchestrator-supervisor.js +179 -1
  58. package/dist/plan-proposal.d.ts +115 -0
  59. package/dist/plan-proposal.js +1073 -0
  60. package/dist/run-ledger.js +3 -3
  61. package/dist/runtime-command.d.ts +8 -0
  62. package/dist/runtime-command.js +38 -6
  63. package/dist/runtime-executor.d.ts +20 -1
  64. package/dist/runtime-executor.js +737 -172
  65. package/dist/runtime-profile.d.ts +32 -0
  66. package/dist/runtime-profile.js +89 -13
  67. package/dist/runtime-tool-specs.d.ts +39 -0
  68. package/dist/runtime-tool-specs.js +144 -28
  69. package/dist/safe-edit.d.ts +7 -0
  70. package/dist/safe-edit.js +163 -37
  71. package/dist/schemas.js +48 -1
  72. package/dist/server.js +51 -0
  73. package/dist/shared.d.ts +3 -2
  74. package/dist/shared.js +2 -0
  75. package/dist/status-events.js +9 -6
  76. package/dist/store/ace-packed-store.d.ts +3 -2
  77. package/dist/store/ace-packed-store.js +188 -110
  78. package/dist/store/bootstrap-store.d.ts +2 -1
  79. package/dist/store/bootstrap-store.js +102 -83
  80. package/dist/store/cache-workspace.js +11 -5
  81. package/dist/store/materializers/context-snapshot-materializer.js +6 -2
  82. package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
  83. package/dist/store/materializers/hook-context-materializer.js +11 -21
  84. package/dist/store/materializers/host-file-materializer.js +6 -0
  85. package/dist/store/materializers/projection-manager.d.ts +0 -1
  86. package/dist/store/materializers/projection-manager.js +5 -13
  87. package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
  88. package/dist/store/materializers/vericify-projector.d.ts +7 -7
  89. package/dist/store/materializers/vericify-projector.js +11 -11
  90. package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
  91. package/dist/store/repositories/local-model-runtime-repository.js +242 -6
  92. package/dist/store/repositories/vericify-repository.d.ts +1 -1
  93. package/dist/store/skills-install.d.ts +4 -0
  94. package/dist/store/skills-install.js +21 -12
  95. package/dist/store/state-reader.d.ts +2 -0
  96. package/dist/store/state-reader.js +20 -0
  97. package/dist/store/store-artifacts.d.ts +7 -0
  98. package/dist/store/store-artifacts.js +27 -1
  99. package/dist/store/store-authority-audit.d.ts +18 -1
  100. package/dist/store/store-authority-audit.js +115 -5
  101. package/dist/store/store-snapshot.d.ts +3 -0
  102. package/dist/store/store-snapshot.js +22 -2
  103. package/dist/store/workspace-store-paths.d.ts +39 -0
  104. package/dist/store/workspace-store-paths.js +94 -0
  105. package/dist/store/write-coordinator.d.ts +65 -0
  106. package/dist/store/write-coordinator.js +386 -0
  107. package/dist/todo-state.js +5 -5
  108. package/dist/tools-agent.d.ts +20 -0
  109. package/dist/tools-agent.js +789 -25
  110. package/dist/tools-discovery.js +136 -1
  111. package/dist/tools-files.d.ts +7 -0
  112. package/dist/tools-files.js +1002 -11
  113. package/dist/tools-framework.js +105 -66
  114. package/dist/tools-handoff.js +2 -2
  115. package/dist/tools-lifecycle.js +4 -4
  116. package/dist/tools-memory.js +6 -6
  117. package/dist/tools-todo.js +2 -2
  118. package/dist/tracker-adapters.d.ts +1 -1
  119. package/dist/tracker-adapters.js +13 -18
  120. package/dist/tracker-sync.js +5 -3
  121. package/dist/tui/agent-runner.js +3 -1
  122. package/dist/tui/chat.js +103 -7
  123. package/dist/tui/dashboard.d.ts +1 -0
  124. package/dist/tui/dashboard.js +43 -0
  125. package/dist/tui/index.js +10 -1
  126. package/dist/tui/layout.d.ts +20 -0
  127. package/dist/tui/layout.js +31 -1
  128. package/dist/tui/local-model-contract.d.ts +6 -2
  129. package/dist/tui/local-model-contract.js +16 -3
  130. package/dist/tui/ollama.d.ts +8 -1
  131. package/dist/tui/ollama.js +53 -12
  132. package/dist/tui/openai-compatible.d.ts +13 -0
  133. package/dist/tui/openai-compatible.js +305 -5
  134. package/dist/tui/provider-discovery.d.ts +1 -0
  135. package/dist/tui/provider-discovery.js +35 -11
  136. package/dist/vericify-bridge.d.ts +6 -1
  137. package/dist/vericify-bridge.js +27 -3
  138. package/dist/workspace-manager.d.ts +30 -3
  139. package/dist/workspace-manager.js +257 -27
  140. package/package.json +1 -2
  141. package/dist/internal-tool-runtime.d.ts +0 -21
  142. package/dist/internal-tool-runtime.js +0 -136
  143. package/dist/store/workspace-snapshot.d.ts +0 -26
  144. package/dist/store/workspace-snapshot.js +0 -107
@@ -8,6 +8,31 @@ export declare const DEFAULT_RUNTIME_TOOL_REGISTRY_REL_PATH = "agent-state/runti
8
8
  export declare const DEFAULT_EXECUTOR_MAX_TURNS = 6;
9
9
  export declare const DEFAULT_EXECUTOR_TURN_TIMEOUT_MS = 300000;
10
10
  export declare const DEFAULT_VERICIFY_BRIDGE_REL_PATH = "agent-state/vericify/ace-bridge.json";
11
+ export type RuntimeModelClass = "frontier" | "mid" | "small_local";
12
+ export declare const DEFAULT_STALL_WINDOW_MS = 300000;
13
+ export declare const DEFAULT_TURN_BUDGET_MS = 1800000;
14
+ export declare const DEFAULT_MAX_STALL_RESTARTS = 3;
15
+ export declare const DEFAULT_INITIAL_BACKOFF_MS = 5000;
16
+ export declare const DEFAULT_MAX_RETRY_BACKOFF_MS = 300000;
17
+ export interface ModelClassLivenessDefaults {
18
+ turn_budget_ms: number;
19
+ stall_window_ms: number;
20
+ max_stall_restarts: number;
21
+ initial_backoff_ms: number;
22
+ max_retry_backoff_ms: number;
23
+ }
24
+ export interface SurgicalReadBudgets {
25
+ small_local: number;
26
+ mid: number;
27
+ frontier: number | null;
28
+ }
29
+ export interface ResolvedSurgicalReadBudget {
30
+ model_class: RuntimeModelClass;
31
+ read_file_lines_max_lines: number | null;
32
+ }
33
+ export declare const DEFAULT_SURGICAL_READ_BUDGETS: SurgicalReadBudgets;
34
+ export declare function getLivenessDefaults(modelClass?: RuntimeModelClass): ModelClassLivenessDefaults;
35
+ export declare function resolveEffectiveSurgicalReadBudget(profile: RuntimeProfile | undefined, modelClass?: RuntimeModelClass): ResolvedSurgicalReadBudget;
11
36
  export declare const DEFAULT_VERICIFY_PROCESS_POST_LOG_REL_PATH = "agent-state/vericify/process-posts.json";
12
37
  declare const runtimeProfileSchema: z.ZodObject<{
13
38
  ace_runtime_version: z.ZodLiteral<"1.0.0">;
@@ -41,6 +66,11 @@ declare const runtimeProfileSchema: z.ZodObject<{
41
66
  }, z.core.$strict>;
42
67
  tools: z.ZodDefault<z.ZodOptional<z.ZodObject<{
43
68
  registry_path: z.ZodDefault<z.ZodOptional<z.ZodString>>;
69
+ surgical_read_budgets: z.ZodDefault<z.ZodOptional<z.ZodObject<{
70
+ small_local: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
71
+ mid: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
72
+ frontier: z.ZodDefault<z.ZodOptional<z.ZodNullable<z.ZodNumber>>>;
73
+ }, z.core.$strict>>>;
44
74
  }, z.core.$strict>>>;
45
75
  autonomy: z.ZodDefault<z.ZodOptional<z.ZodObject<{
46
76
  orchestrator_preflight: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
@@ -110,7 +140,9 @@ export declare function readRuntimeProfile(): RuntimeProfile;
110
140
  export declare function readRuntimePromptTemplate(): string;
111
141
  export declare function readRuntimeProfileState(): RuntimeProfileSnapshot;
112
142
  export declare function renderRuntimePrompt(templateContext: Record<string, unknown>): string;
143
+ export declare function renderRuntimePromptFromSnapshot(snapshot: RuntimeProfileSnapshot, templateContext: Record<string, unknown>): string;
113
144
  export declare function getRuntimeProfilePath(): string;
114
145
  export declare function getRuntimeProfileSchemaPath(): string;
146
+ export declare function getSurgicalReadBudget(modelClass: "frontier" | "mid" | "small_local" | undefined, profile?: RuntimeProfile): number | null;
115
147
  export {};
116
148
  //# sourceMappingURL=runtime-profile.d.ts.map
@@ -4,7 +4,7 @@ import { resolve } from "node:path";
4
4
  import { z } from "zod";
5
5
  import { DEFAULTS_ROOT, resolveWorkspaceArtifactPath, resolveWorkspaceRoot, } from "./helpers.js";
6
6
  import { getStoreStatSync, getWorkspaceStorePath, parseVirtualStorePath, readStoreBlobSync, readStoreBlobByPathSync, toVirtualStorePath, } from "./store/store-snapshot.js";
7
- import { appendStatusEvent } from "./status-events.js";
7
+ import { appendStatusEventSafe } from "./status-events.js";
8
8
  import { DEFAULT_AUTONOMY_POLICY, DEFAULT_CONTINUITY_POLICY, } from "./ace-autonomy.js";
9
9
  export const RUNTIME_PROFILE_REL_PATH = "agent-state/ACE_WORKFLOW.md";
10
10
  export const RUNTIME_PROFILE_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json";
@@ -13,6 +13,57 @@ export const DEFAULT_RUNTIME_TOOL_REGISTRY_REL_PATH = "agent-state/runtime-tool-
13
13
  export const DEFAULT_EXECUTOR_MAX_TURNS = 6;
14
14
  export const DEFAULT_EXECUTOR_TURN_TIMEOUT_MS = 300_000;
15
15
  export const DEFAULT_VERICIFY_BRIDGE_REL_PATH = "agent-state/vericify/ace-bridge.json";
16
+ // Liveness defaults per model class
17
+ export const DEFAULT_STALL_WINDOW_MS = 300_000; // 5 min — inter-progress cap
18
+ export const DEFAULT_TURN_BUDGET_MS = 1_800_000; // 30 min — per-turn soft cap
19
+ export const DEFAULT_MAX_STALL_RESTARTS = 3;
20
+ export const DEFAULT_INITIAL_BACKOFF_MS = 5_000;
21
+ export const DEFAULT_MAX_RETRY_BACKOFF_MS = 300_000;
22
+ export const DEFAULT_SURGICAL_READ_BUDGETS = {
23
+ small_local: 200,
24
+ mid: 800,
25
+ frontier: null,
26
+ };
27
+ export function getLivenessDefaults(modelClass) {
28
+ switch (modelClass) {
29
+ case "small_local":
30
+ return {
31
+ turn_budget_ms: 3_600_000, // 60 min — slow models need generous cap
32
+ stall_window_ms: 180_000, // 3 min stall window
33
+ max_stall_restarts: 3,
34
+ initial_backoff_ms: 10_000,
35
+ max_retry_backoff_ms: 300_000,
36
+ };
37
+ case "mid":
38
+ return {
39
+ turn_budget_ms: 1_800_000, // 30 min
40
+ stall_window_ms: 300_000, // 5 min
41
+ max_stall_restarts: 3,
42
+ initial_backoff_ms: 5_000,
43
+ max_retry_backoff_ms: 180_000,
44
+ };
45
+ case "frontier":
46
+ default:
47
+ return {
48
+ turn_budget_ms: DEFAULT_TURN_BUDGET_MS,
49
+ stall_window_ms: DEFAULT_STALL_WINDOW_MS,
50
+ max_stall_restarts: DEFAULT_MAX_STALL_RESTARTS,
51
+ initial_backoff_ms: DEFAULT_INITIAL_BACKOFF_MS,
52
+ max_retry_backoff_ms: DEFAULT_MAX_RETRY_BACKOFF_MS,
53
+ };
54
+ }
55
+ }
56
+ function resolveSurgicalReadBudgetValue(budgets, modelClass) {
57
+ const resolved = budgets ?? DEFAULT_SURGICAL_READ_BUDGETS;
58
+ return resolved[modelClass] ?? null;
59
+ }
60
+ export function resolveEffectiveSurgicalReadBudget(profile, modelClass) {
61
+ const effectiveModelClass = modelClass ?? "frontier";
62
+ return {
63
+ model_class: effectiveModelClass,
64
+ read_file_lines_max_lines: resolveSurgicalReadBudgetValue(profile?.tools?.surgical_read_budgets, effectiveModelClass),
65
+ };
66
+ }
16
67
  export const DEFAULT_VERICIFY_PROCESS_POST_LOG_REL_PATH = "agent-state/vericify/process-posts.json";
17
68
  const DEFAULT_RUNTIME_PROFILE_PATH = resolve(DEFAULTS_ROOT, RUNTIME_PROFILE_REL_PATH);
18
69
  const DEFAULT_RUNTIME_PROFILE_SCHEMA_PATH = resolve(DEFAULTS_ROOT, RUNTIME_PROFILE_SCHEMA_REL_PATH);
@@ -63,10 +114,22 @@ const runtimeProfileSchema = z
63
114
  .min(1)
64
115
  .optional()
65
116
  .default(DEFAULT_RUNTIME_TOOL_REGISTRY_REL_PATH),
117
+ surgical_read_budgets: z
118
+ .object({
119
+ small_local: z.number().int().positive().optional().default(DEFAULT_SURGICAL_READ_BUDGETS.small_local),
120
+ mid: z.number().int().positive().optional().default(DEFAULT_SURGICAL_READ_BUDGETS.mid),
121
+ frontier: z.number().int().positive().nullable().optional().default(DEFAULT_SURGICAL_READ_BUDGETS.frontier),
122
+ })
123
+ .strict()
124
+ .optional()
125
+ .default(DEFAULT_SURGICAL_READ_BUDGETS),
66
126
  })
67
127
  .strict()
68
128
  .optional()
69
- .default({ registry_path: DEFAULT_RUNTIME_TOOL_REGISTRY_REL_PATH }),
129
+ .default({
130
+ registry_path: DEFAULT_RUNTIME_TOOL_REGISTRY_REL_PATH,
131
+ surgical_read_budgets: DEFAULT_SURGICAL_READ_BUDGETS,
132
+ }),
70
133
  autonomy: z
71
134
  .object({
72
135
  orchestrator_preflight: z.boolean().optional().default(true),
@@ -336,7 +399,7 @@ function parseRuntimeProfileContent(raw) {
336
399
  }
337
400
  function emitRuntimeProfileEvent(eventType, input) {
338
401
  try {
339
- appendStatusEvent({
402
+ void appendStatusEventSafe({
340
403
  source_module: "capability-framework",
341
404
  event_type: eventType,
342
405
  status: eventType === "RUNTIME_PROFILE_INVALID" ? "fail" : "pass",
@@ -349,6 +412,8 @@ function emitRuntimeProfileEvent(eventType, input) {
349
412
  signature: input.signature,
350
413
  errors: input.errors,
351
414
  },
415
+ }).catch(() => {
416
+ // Runtime profile reads must not fail due to observability side effects.
352
417
  });
353
418
  }
354
419
  catch {
@@ -357,13 +422,16 @@ function emitRuntimeProfileEvent(eventType, input) {
357
422
  }
358
423
  function ensureLastGoodSnapshot() {
359
424
  const current = loadRuntimeProfile();
360
- if (current.ok && lastGoodSnapshot)
361
- return lastGoodSnapshot;
362
- if (lastGoodSnapshot)
425
+ if (current.ok)
426
+ return current;
427
+ if (lastGoodSnapshot &&
428
+ lastGoodSnapshot.path === current.path &&
429
+ lastGoodSnapshot.source === current.source) {
363
430
  return lastGoodSnapshot;
431
+ }
364
432
  const packaged = loadRuntimeProfile(DEFAULT_RUNTIME_PROFILE_PATH);
365
- if (packaged.ok && lastGoodSnapshot)
366
- return lastGoodSnapshot;
433
+ if (packaged.ok)
434
+ return packaged;
367
435
  throw new Error(`Packaged ACE runtime profile is invalid: ${(packaged.ok ? [] : packaged.errors).join("; ")}`);
368
436
  }
369
437
  function resolveTemplatePath(context, path) {
@@ -511,14 +579,17 @@ export function readRuntimeProfileState() {
511
579
  }
512
580
  export function renderRuntimePrompt(templateContext) {
513
581
  const active = ensureLastGoodSnapshot();
582
+ return renderRuntimePromptFromSnapshot(active, templateContext);
583
+ }
584
+ export function renderRuntimePromptFromSnapshot(snapshot, templateContext) {
514
585
  const renderContext = {
515
- ...active.profile,
516
- runtime_profile_path: active.path,
517
- runtime_profile_source: active.source,
518
- prompt_template: active.prompt_template,
586
+ ...snapshot.profile,
587
+ runtime_profile_path: snapshot.path,
588
+ runtime_profile_source: snapshot.source,
589
+ prompt_template: snapshot.prompt_template,
519
590
  ...templateContext,
520
591
  };
521
- return active.prompt_template.replace(/{{\s*([A-Za-z0-9_.-]+)\s*}}/g, (match, token) => {
592
+ return snapshot.prompt_template.replace(/{{\s*([A-Za-z0-9_.-]+)\s*}}/g, (match, token) => {
522
593
  const value = resolveTemplatePath(renderContext, token);
523
594
  return value === undefined ? match : stringifyTemplateValue(value);
524
595
  });
@@ -529,4 +600,9 @@ export function getRuntimeProfilePath() {
529
600
  export function getRuntimeProfileSchemaPath() {
530
601
  return DEFAULT_RUNTIME_PROFILE_SCHEMA_PATH;
531
602
  }
603
+ export function getSurgicalReadBudget(modelClass, profile) {
604
+ const effectiveProfile = profile ?? readRuntimeProfile();
605
+ const budgets = effectiveProfile.tools?.surgical_read_budgets ?? DEFAULT_SURGICAL_READ_BUDGETS;
606
+ return budgets[modelClass ?? "frontier"] ?? null;
607
+ }
532
608
  //# sourceMappingURL=runtime-profile.js.map
@@ -1,4 +1,6 @@
1
+ import type { ToolCostClass } from "./store/repositories/local-model-runtime-repository.js";
1
2
  import { type ArtifactSource } from "./helpers.js";
3
+ import { type RuntimeModelClass } from "./runtime-profile.js";
2
4
  import { type ValidationResult } from "./schemas.js";
3
5
  export declare const RUNTIME_TOOL_SPEC_REGISTRY_REL_PATH = "agent-state/runtime-tool-specs.json";
4
6
  export declare const RUNTIME_TOOL_SPEC_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json";
@@ -20,6 +22,14 @@ export interface RuntimeToolExecutorConfig {
20
22
  timeout_ms?: number;
21
23
  env?: Record<string, string>;
22
24
  }
25
+ export interface RuntimeToolMcpServerConfig {
26
+ transport: "stdio" | "http";
27
+ command?: string;
28
+ args?: string[];
29
+ url?: string;
30
+ env?: Record<string, string>;
31
+ tool_allowlist?: string[];
32
+ }
23
33
  export interface RuntimeToolSpec {
24
34
  name: string;
25
35
  description: string;
@@ -27,6 +37,7 @@ export interface RuntimeToolSpec {
27
37
  success_schema?: RuntimeToolSchema;
28
38
  failure_schema?: RuntimeToolSchema;
29
39
  executor: RuntimeToolExecutorConfig;
40
+ mcp_server?: RuntimeToolMcpServerConfig;
30
41
  }
31
42
  export interface RuntimeToolSpecRegistry {
32
43
  version: 1;
@@ -58,11 +69,39 @@ export interface RuntimeToolExecutionResult {
58
69
  error?: unknown;
59
70
  validation_errors?: string[];
60
71
  }
72
+ export declare function buildRuntimeToolEnvironment(input: {
73
+ base_env?: NodeJS.ProcessEnv;
74
+ spec_env?: Record<string, string>;
75
+ injected_env: Record<string, string>;
76
+ }): NodeJS.ProcessEnv;
77
+ export declare function persistRuntimeToolInvocationArtifacts(callId: string, requestContent: string, responseContent: string): Promise<{
78
+ requestPath: string;
79
+ responsePath: string;
80
+ }>;
61
81
  export declare function validateRuntimeToolRegistryContent(raw: string): ValidationResult;
62
82
  export declare function loadRuntimeToolRegistry(explicitPath?: string): RuntimeToolSpecRegistryResult;
63
83
  export declare function readRuntimeToolRegistry(): RuntimeToolSpecRegistry;
64
84
  export declare function listRuntimeToolSpecs(): RuntimeToolSpec[];
65
85
  export declare function getRuntimeToolRegistryPath(): string;
66
86
  export declare function executeRuntimeTool(name: string, input: unknown, context?: RuntimeToolExecutionContext): Promise<RuntimeToolExecutionResult>;
87
+ export type ModelClass = RuntimeModelClass;
88
+ export declare const SMALL_LOCAL_PRIORITY_TOOL_NAMES: readonly ["outline_file", "astgrep_query", "astgrep_locate", "read_file_lines", "compile_structural_edit", "preview_structural_edit", "list_workspace", "recall_context", "validate_framework", "run_orchestrator"];
89
+ export interface FilteredToolCatalogEntry {
90
+ name: string;
91
+ description: string;
92
+ input_schema: RuntimeToolSchema;
93
+ cost_class: ToolCostClass;
94
+ }
95
+ export interface FilteredToolCatalog {
96
+ entries: FilteredToolCatalogEntry[];
97
+ model_class: ModelClass;
98
+ unavailable_tools: {
99
+ name: string;
100
+ reason: string;
101
+ reason_code: string;
102
+ }[];
103
+ tool_cost_class: Record<string, ToolCostClass>;
104
+ }
105
+ export declare function buildFilteredToolCatalog(modelClass: ModelClass | undefined, allowedTools?: string[]): FilteredToolCatalog;
67
106
  export {};
68
107
  //# sourceMappingURL=runtime-tool-specs.d.ts.map
@@ -12,7 +12,8 @@ import { appendStatusEventSafe } from "./status-events.js";
12
12
  import { openStore } from "./store/ace-packed-store.js";
13
13
  import { getWorkspaceStorePath, readStoreBlobSync, storeExistsSync, toVirtualStorePath, } from "./store/store-snapshot.js";
14
14
  import { operationalArtifactKey, } from "./store/store-artifacts.js";
15
- import { withStoreWriteQueue } from "./store/write-queue.js";
15
+ import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
16
+ import { parseJsonLikeText } from "./json-sanitizer.js";
16
17
  export const RUNTIME_TOOL_SPEC_REGISTRY_REL_PATH = "agent-state/runtime-tool-specs.json";
17
18
  export const RUNTIME_TOOL_SPEC_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json";
18
19
  export const RUNTIME_TOOL_SPEC_SCHEMA_NAME = "runtime-tool-spec-registry@1.0.0";
@@ -96,13 +97,46 @@ function buildTempToolPaths(callId) {
96
97
  responsePath: join(tempDir, "response.json"),
97
98
  };
98
99
  }
100
+ const SAFE_RUNTIME_ENV_KEYS = new Set([
101
+ "PATH",
102
+ "HOME",
103
+ "TMPDIR",
104
+ "TEMP",
105
+ "TMP",
106
+ "USER",
107
+ "LANG",
108
+ "LC_ALL",
109
+ "SHELL",
110
+ "NODE_PATH",
111
+ ]);
112
+ function isSecretEnvKey(key) {
113
+ return /(SECRET|TOKEN|KEY|PASSWORD|PASS|CREDENTIAL|AUTH|COOKIE|SESSION)/i.test(key);
114
+ }
115
+ export function buildRuntimeToolEnvironment(input) {
116
+ const base = input.base_env ?? process.env;
117
+ const env = {};
118
+ for (const key of SAFE_RUNTIME_ENV_KEYS) {
119
+ const value = base[key];
120
+ if (typeof value === "string")
121
+ env[key] = value;
122
+ }
123
+ for (const [key, value] of Object.entries(input.spec_env ?? {})) {
124
+ if (isSecretEnvKey(key))
125
+ continue;
126
+ env[key] = value;
127
+ }
128
+ for (const [key, value] of Object.entries(input.injected_env)) {
129
+ env[key] = value;
130
+ }
131
+ return env;
132
+ }
99
133
  function runtimeToolArtifactKey(callId, kind) {
100
134
  return `state/runtime/tools/invocations/${callId}/${kind}.json`;
101
135
  }
102
136
  function runtimeToolArtifactVirtualPath(callId, kind) {
103
137
  return toVirtualStorePath(getWorkspaceStorePath(resolveWorkspaceRoot()), runtimeToolArtifactKey(callId, kind));
104
138
  }
105
- async function persistToolInvocationArtifacts(callId, requestContent, responseContent) {
139
+ export async function persistRuntimeToolInvocationArtifacts(callId, requestContent, responseContent) {
106
140
  const workspaceRoot = resolveWorkspaceRoot();
107
141
  if (!storeExistsSync(workspaceRoot)) {
108
142
  const requestPath = resolveWorkspaceArtifactPath(`.ace-runtime/tools/${callId}.request.json`);
@@ -113,7 +147,7 @@ async function persistToolInvocationArtifacts(callId, requestContent, responseCo
113
147
  return { requestPath, responsePath };
114
148
  }
115
149
  const storePath = getWorkspaceStorePath(workspaceRoot);
116
- await withStoreWriteQueue(storePath, async () => {
150
+ await withStoreWriteCoordinator(storePath, async () => {
117
151
  const store = await openStore(storePath);
118
152
  try {
119
153
  await store.setBlob(runtimeToolArtifactKey(callId, "request"), requestContent);
@@ -123,25 +157,25 @@ async function persistToolInvocationArtifacts(callId, requestContent, responseCo
123
157
  finally {
124
158
  await store.close();
125
159
  }
126
- });
160
+ }, { operation_label: "persistToolInvocationArtifacts" });
127
161
  return {
128
162
  requestPath: runtimeToolArtifactVirtualPath(callId, "request"),
129
163
  responsePath: runtimeToolArtifactVirtualPath(callId, "response"),
130
164
  };
131
165
  }
132
166
  function parseRuntimeToolRegistry(raw) {
133
- let parsed;
134
- try {
135
- parsed = JSON.parse(raw);
167
+ const parsed = parseJsonLikeText(raw);
168
+ if (!parsed.ok) {
169
+ throw new Error(`Runtime tool registry contains invalid JSON: ${parsed.error}`);
136
170
  }
137
- catch (error) {
138
- throw new Error(`Runtime tool registry contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
171
+ if (parsed.sanitized.removed_control_bytes > 0) {
172
+ throw new Error(`Runtime tool registry contains control-byte corruption: control_bytes_removed=${parsed.sanitized.removed_control_bytes}`);
139
173
  }
140
- const validation = validateRuntimeToolSpecRegistryPayload(parsed);
174
+ const validation = validateRuntimeToolSpecRegistryPayload(parsed.value);
141
175
  if (!validation.ok) {
142
176
  throw new Error(`Runtime tool registry failed validation (${validation.schema}): ${validation.errors.join("; ")}`);
143
177
  }
144
- return parsed;
178
+ return parsed.value;
145
179
  }
146
180
  function ensureToolSpec(spec, name) {
147
181
  if (spec)
@@ -258,17 +292,20 @@ export function loadRuntimeToolRegistry(explicitPath) {
258
292
  };
259
293
  }
260
294
  catch (error) {
295
+ const errorMessage = error instanceof Error ? error.message : String(error);
296
+ const controlBytesRemoved = Number(errorMessage.match(/control_bytes_removed=(\d+)/)?.[1] ?? 0);
261
297
  void emitRuntimeToolEvent("RUNTIME_TOOL_REGISTRY_INVALID", `Runtime tool registry validation failed for ${target.path}`, {
262
298
  path: target.path,
263
299
  source: target.source,
264
- errors: [error instanceof Error ? error.message : String(error)],
300
+ ...(controlBytesRemoved > 0 ? { control_bytes_removed: controlBytesRemoved } : {}),
301
+ errors: [errorMessage],
265
302
  });
266
303
  return {
267
304
  ok: false,
268
305
  schema: RUNTIME_TOOL_SPEC_SCHEMA_NAME,
269
306
  path: target.path,
270
307
  source: target.source,
271
- errors: [error instanceof Error ? error.message : String(error)],
308
+ errors: [errorMessage],
272
309
  };
273
310
  }
274
311
  }
@@ -317,7 +354,7 @@ export async function executeRuntimeTool(name, input, context = {}) {
317
354
  },
318
355
  validation_errors: inputErrors,
319
356
  }, null, 2);
320
- ({ requestPath, responsePath } = await persistToolInvocationArtifacts(callId, validationRequest, validationResponse));
357
+ ({ requestPath, responsePath } = await persistRuntimeToolInvocationArtifacts(callId, validationRequest, validationResponse));
321
358
  rmSync(tempPaths.tempDir, { recursive: true, force: true });
322
359
  const result = {
323
360
  ok: false,
@@ -368,16 +405,17 @@ export async function executeRuntimeTool(name, input, context = {}) {
368
405
  : workspacePath;
369
406
  const shellResult = await runShellCommand(spec.executor.command, {
370
407
  cwd: cwdBase,
371
- env: {
372
- ...process.env,
373
- ...(spec.executor.env ?? {}),
374
- ACE_RUNTIME_TOOL_NAME: spec.name,
375
- ACE_RUNTIME_TOOL_REQUEST_FILE: tempPaths.requestPath,
376
- ACE_RUNTIME_TOOL_RESPONSE_FILE: tempPaths.responsePath,
377
- ACE_RUNTIME_SESSION_ID: context.session_id ?? "",
378
- ACE_RUNTIME_TURN: context.turn_number ? String(context.turn_number) : "",
379
- ACE_RUNTIME_WORKSPACE_PATH: workspacePath,
380
- },
408
+ env: buildRuntimeToolEnvironment({
409
+ spec_env: spec.executor.env,
410
+ injected_env: {
411
+ ACE_RUNTIME_TOOL_NAME: spec.name,
412
+ ACE_RUNTIME_TOOL_REQUEST_FILE: tempPaths.requestPath,
413
+ ACE_RUNTIME_TOOL_RESPONSE_FILE: tempPaths.responsePath,
414
+ ACE_RUNTIME_SESSION_ID: context.session_id ?? "",
415
+ ACE_RUNTIME_TURN: context.turn_number ? String(context.turn_number) : "",
416
+ ACE_RUNTIME_WORKSPACE_PATH: workspacePath,
417
+ },
418
+ }),
381
419
  timeout_ms: spec.executor.timeout_ms ?? 60_000,
382
420
  });
383
421
  let responseRaw;
@@ -386,7 +424,7 @@ export async function executeRuntimeTool(name, input, context = {}) {
386
424
  }
387
425
  if (!responseRaw) {
388
426
  const summary = `Runtime tool '${name}' did not produce a response payload`;
389
- ({ requestPath, responsePath } = await persistToolInvocationArtifacts(callId, requestRaw, JSON.stringify({
427
+ ({ requestPath, responsePath } = await persistRuntimeToolInvocationArtifacts(callId, requestRaw, JSON.stringify({
390
428
  ok: false,
391
429
  summary,
392
430
  error: {
@@ -436,7 +474,7 @@ export async function executeRuntimeTool(name, input, context = {}) {
436
474
  }
437
475
  catch (error) {
438
476
  const summary = `Runtime tool '${name}' returned invalid JSON`;
439
- ({ requestPath, responsePath } = await persistToolInvocationArtifacts(callId, requestRaw, responseRaw));
477
+ ({ requestPath, responsePath } = await persistRuntimeToolInvocationArtifacts(callId, requestRaw, responseRaw));
440
478
  await emitRuntimeToolEvent("RUNTIME_TOOL_FAILED", summary, {
441
479
  tool_name: name,
442
480
  error: error instanceof Error ? error.message : String(error),
@@ -458,7 +496,7 @@ export async function executeRuntimeTool(name, input, context = {}) {
458
496
  const envelope = runtimeToolResponseSchema.safeParse(parsed);
459
497
  if (!envelope.success) {
460
498
  const summary = `Runtime tool '${name}' returned an invalid response envelope`;
461
- ({ requestPath, responsePath } = await persistToolInvocationArtifacts(callId, requestRaw, responseRaw));
499
+ ({ requestPath, responsePath } = await persistRuntimeToolInvocationArtifacts(callId, requestRaw, responseRaw));
462
500
  rmSync(tempPaths.tempDir, { recursive: true, force: true });
463
501
  return {
464
502
  ok: false,
@@ -483,7 +521,7 @@ export async function executeRuntimeTool(name, input, context = {}) {
483
521
  const success = envelope.data.ok && validationErrors.length === 0;
484
522
  const summary = envelope.data.summary ??
485
523
  (success ? `Runtime tool '${name}' executed successfully` : `Runtime tool '${name}' failed`);
486
- ({ requestPath, responsePath } = await persistToolInvocationArtifacts(callId, requestRaw, responseRaw));
524
+ ({ requestPath, responsePath } = await persistRuntimeToolInvocationArtifacts(callId, requestRaw, responseRaw));
487
525
  rmSync(tempPaths.tempDir, { recursive: true, force: true });
488
526
  const result = {
489
527
  ok: success,
@@ -524,4 +562,82 @@ export async function executeRuntimeTool(name, input, context = {}) {
524
562
  });
525
563
  return result;
526
564
  }
565
+ export const SMALL_LOCAL_PRIORITY_TOOL_NAMES = [
566
+ "outline_file",
567
+ "astgrep_query",
568
+ "astgrep_locate",
569
+ "read_file_lines",
570
+ "compile_structural_edit",
571
+ "preview_structural_edit",
572
+ "list_workspace",
573
+ "recall_context",
574
+ "validate_framework",
575
+ "run_orchestrator",
576
+ ];
577
+ const HEAVY_TOOL_NAMES = new Set([
578
+ "read_workspace_file",
579
+ "write_workspace_file",
580
+ "safe_edit_file",
581
+ "astgrep_rewrite",
582
+ ]);
583
+ function extractCostClass(tool) {
584
+ const desc = (tool.description ?? "").toLowerCase();
585
+ if (desc.includes("cost: cheap"))
586
+ return "cheap";
587
+ if (desc.includes("cost: moderate"))
588
+ return "moderate";
589
+ if (desc.includes("cost: heavy"))
590
+ return "heavy";
591
+ if (HEAVY_TOOL_NAMES.has(tool.name))
592
+ return "heavy";
593
+ if (["read_file_lines", "apply_patch", "diff_files"].includes(tool.name))
594
+ return "moderate";
595
+ return "cheap";
596
+ }
597
+ export function buildFilteredToolCatalog(modelClass, allowedTools) {
598
+ const allTools = listRuntimeToolSpecs();
599
+ const effectiveModelClass = modelClass ?? "frontier";
600
+ const allowed = allowedTools ? new Set(allowedTools) : null;
601
+ const unavailable = [];
602
+ let entries = [];
603
+ for (const tool of allTools) {
604
+ if (allowed !== null && !allowed.has(tool.name)) {
605
+ unavailable.push({
606
+ name: tool.name,
607
+ reason: "Not in allowed_tools for this session",
608
+ reason_code: "workspace_disabled",
609
+ });
610
+ continue;
611
+ }
612
+ const costClass = extractCostClass(tool);
613
+ let description = tool.description;
614
+ if (effectiveModelClass === "small_local" && costClass === "heavy") {
615
+ description = `[cost: heavy — prefer surgical alternatives] ${description}`;
616
+ }
617
+ entries.push({
618
+ name: tool.name,
619
+ description,
620
+ input_schema: tool.input_schema,
621
+ cost_class: costClass,
622
+ });
623
+ }
624
+ if (effectiveModelClass === "small_local" || effectiveModelClass === "mid") {
625
+ const priority = new Map(SMALL_LOCAL_PRIORITY_TOOL_NAMES.map((name, idx) => [name, idx]));
626
+ entries.sort((a, b) => {
627
+ const pa = priority.get(a.name) ?? 999;
628
+ const pb = priority.get(b.name) ?? 999;
629
+ return pa - pb;
630
+ });
631
+ }
632
+ const toolCostClass = {};
633
+ for (const e of entries) {
634
+ toolCostClass[e.name] = e.cost_class;
635
+ }
636
+ return {
637
+ entries,
638
+ model_class: effectiveModelClass,
639
+ unavailable_tools: unavailable,
640
+ tool_cost_class: toolCostClass,
641
+ };
642
+ }
527
643
  //# sourceMappingURL=runtime-tool-specs.js.map
@@ -49,4 +49,11 @@ export declare function diffContents(original: string, updated: string): DiffRes
49
49
  export declare function diffFiles(pathA: string, pathB: string): DiffResult & {
50
50
  error?: string;
51
51
  };
52
+ export interface ApplyPatchInput {
53
+ path: string;
54
+ patch: string;
55
+ validation_command?: string;
56
+ test_command?: string;
57
+ }
58
+ export declare function applyPatch(input: ApplyPatchInput): SafeEditResult;
52
59
  //# sourceMappingURL=safe-edit.d.ts.map