salmon-loop 0.3.2 → 0.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 (121) hide show
  1. package/dist/cli/authorization/non-interactive.js +9 -13
  2. package/dist/cli/chat.js +12 -6
  3. package/dist/cli/commands/allowlist.js +1 -1
  4. package/dist/cli/commands/chat.js +13 -13
  5. package/dist/cli/commands/parallel.js +1 -1
  6. package/dist/cli/commands/run/handler.js +6 -3
  7. package/dist/cli/commands/run/loop-params.js +1 -0
  8. package/dist/cli/commands/run/parse-options.js +14 -26
  9. package/dist/cli/commands/run/runtime-llm.js +15 -12
  10. package/dist/cli/headless/openai-responses-canonical-applier.js +1 -7
  11. package/dist/cli/reporters/standard.js +2 -3
  12. package/dist/cli/reporters/stream-json.js +2 -1
  13. package/dist/cli/slash/runtime.js +2 -2
  14. package/dist/cli/ui/hooks/useLoopEvents.js +1 -1
  15. package/dist/cli/ui/hooks/useLoopState.js +1 -1
  16. package/dist/core/ast/parser.js +18 -9
  17. package/dist/core/config/schema.js +738 -0
  18. package/dist/core/config/validate.js +11 -922
  19. package/dist/core/context/gatherers/ast-gatherer.js +4 -12
  20. package/dist/core/context/gatherers/ghost-dependency-gatherer.js +0 -1
  21. package/dist/core/context/gatherers/knowledge-gatherer.js +3 -0
  22. package/dist/core/context/service.js +8 -0
  23. package/dist/core/context/token/encoding-registry.js +7 -6
  24. package/dist/core/extensions/index.js +48 -3
  25. package/dist/core/extensions/load.js +3 -2
  26. package/dist/core/extensions/merge.js +5 -1
  27. package/dist/core/extensions/paths.js +6 -0
  28. package/dist/core/extensions/schemas.js +21 -0
  29. package/dist/core/facades/cli-command-chat.js +2 -0
  30. package/dist/core/facades/cli-run-handler.js +1 -0
  31. package/dist/core/facades/cli-utils-serialize.js +2 -0
  32. package/dist/core/grizzco/dsl/llm-strategy.js +3 -2
  33. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +15 -10
  34. package/dist/core/grizzco/engine/pipeline/pipeline.js +149 -240
  35. package/dist/core/grizzco/engine/transaction/attempt-failure.js +5 -4
  36. package/dist/core/grizzco/engine/transaction/authorization-summary.js +2 -1
  37. package/dist/core/grizzco/runtime/apply-back-runtime.js +2 -1
  38. package/dist/core/grizzco/services/registry.js +18 -0
  39. package/dist/core/grizzco/steps/audit.js +20 -10
  40. package/dist/core/grizzco/steps/display-report.js +4 -11
  41. package/dist/core/grizzco/steps/explore.js +9 -2
  42. package/dist/core/grizzco/steps/patch/prompt-input.js +4 -1
  43. package/dist/core/grizzco/steps/patch.js +1 -0
  44. package/dist/core/grizzco/steps/plan.js +58 -49
  45. package/dist/core/grizzco/steps/tool-runtime.js +3 -0
  46. package/dist/core/grizzco/workers/strata-sync-worker.js +2 -1
  47. package/dist/core/llm/ai-sdk/message-mapper.js +24 -18
  48. package/dist/core/llm/ai-sdk/request-params.js +1 -3
  49. package/dist/core/llm/ai-sdk/result-mapper.js +14 -8
  50. package/dist/core/llm/ai-sdk/retry-classifier.js +6 -4
  51. package/dist/core/llm/contracts/repair.js +16 -8
  52. package/dist/core/llm/errors.js +13 -10
  53. package/dist/core/llm/output-policy.js +8 -0
  54. package/dist/core/llm/redact.js +1 -3
  55. package/dist/core/llm/sub-agent-factory.js +48 -0
  56. package/dist/core/llm/tool-calling-stub.js +48 -0
  57. package/dist/core/llm/utils.js +17 -6
  58. package/dist/core/mcp/bridge/prompt-command-provider.js +4 -3
  59. package/dist/core/mcp/bridge/tool-bridge.js +5 -14
  60. package/dist/core/mcp/client/connection-manager.js +3 -2
  61. package/dist/core/mcp/host/sampling-provider.js +1 -1
  62. package/dist/core/mcp/schema/json-schema-to-zod.js +2 -1
  63. package/dist/core/memory/relevant-retrieval.js +6 -4
  64. package/dist/core/observability/authorization-decisions.js +13 -12
  65. package/dist/core/observability/error-mapping.js +2 -1
  66. package/dist/core/observability/token-usage.js +5 -4
  67. package/dist/core/plugin/loader.js +5 -4
  68. package/dist/core/prompts/registry.js +11 -29
  69. package/dist/core/protocols/a2a/sdk/server.js +2 -3
  70. package/dist/core/protocols/acp/formal-agent.js +10 -4
  71. package/dist/core/protocols/acp/stdio-server.js +6 -6
  72. package/dist/core/runtime/agent-server-runtime.js +3 -2
  73. package/dist/core/runtime/initialize.js +70 -6
  74. package/dist/core/session/compaction/index.js +4 -3
  75. package/dist/core/session/manager.js +24 -37
  76. package/dist/core/session/token-tracker.js +18 -7
  77. package/dist/core/skills/parser.js +3 -2
  78. package/dist/core/skills/runtime/MicroTaskRunner.js +1 -1
  79. package/dist/core/skills/runtime/SkillRunner.js +5 -2
  80. package/dist/core/slash/steps/slash-execute.js +7 -5
  81. package/dist/core/slash/strategy.js +1 -1
  82. package/dist/core/strata/layers/worktree.js +7 -9
  83. package/dist/core/strata/runtime/synchronizer.js +10 -9
  84. package/dist/core/streaming/canonical/parts-from-llm-stream-chunk.js +1 -11
  85. package/dist/core/structured-output/json-schema-validator.js +1 -13
  86. package/dist/core/sub-agent/context-snapshot.js +12 -6
  87. package/dist/core/sub-agent/controller.js +70 -1
  88. package/dist/core/sub-agent/core/loop.js +25 -3
  89. package/dist/core/sub-agent/core/manager.js +319 -116
  90. package/dist/core/sub-agent/registry-defaults.js +12 -0
  91. package/dist/core/sub-agent/registry.js +8 -0
  92. package/dist/core/sub-agent/team.js +98 -0
  93. package/dist/core/sub-agent/tools/task-await.js +109 -0
  94. package/dist/core/sub-agent/tools/task-spawn.js +49 -7
  95. package/dist/core/sub-agent/tools/team.js +92 -0
  96. package/dist/core/sub-agent/types.js +11 -2
  97. package/dist/core/tools/budget.js +4 -11
  98. package/dist/core/tools/builtin/code-search/executor.js +46 -43
  99. package/dist/core/tools/builtin/fs.js +14 -6
  100. package/dist/core/tools/builtin/index.js +41 -107
  101. package/dist/core/tools/builtin/interaction.js +13 -15
  102. package/dist/core/tools/builtin/proposal.js +11 -2
  103. package/dist/core/tools/capability/executor.js +5 -5
  104. package/dist/core/tools/headless-payload.js +1 -3
  105. package/dist/core/tools/mapper.js +8 -42
  106. package/dist/core/tools/parallel/persistence.js +17 -5
  107. package/dist/core/tools/parallel/scheduler.js +23 -21
  108. package/dist/core/tools/permissions/permission-rules.js +66 -114
  109. package/dist/core/tools/plugins/loader.js +4 -3
  110. package/dist/core/tools/router.js +24 -53
  111. package/dist/core/tools/session.js +54 -97
  112. package/dist/core/tools/streaming/ToolCallAccumulator.js +1 -3
  113. package/dist/core/tools/tool-visibility.js +2 -1
  114. package/dist/core/tools/types.js +10 -0
  115. package/dist/core/utils/error.js +79 -0
  116. package/dist/core/utils/serialize.js +63 -0
  117. package/dist/core/utils/zod.js +29 -0
  118. package/dist/core/workspace/capabilities.js +3 -2
  119. package/dist/integrations/langfuse/litellm-langfuse-outcome-reporter.js +9 -8
  120. package/dist/locales/en.js +2 -1
  121. package/package.json +1 -1
@@ -181,25 +181,17 @@ export class AstGatherer {
181
181
  async gatherCallNames(parsedTree, lang, callsQuery) {
182
182
  if (!parsedTree || !lang || !callsQuery)
183
183
  return [];
184
- const queryFn = AstParser.queryCapturesFromQuery;
185
- if (typeof queryFn !== 'function')
186
- return [];
187
- const captures = (await queryFn(parsedTree, lang, callsQuery));
184
+ const captures = await AstParser.queryCapturesFromQuery(parsedTree, lang, callsQuery);
188
185
  return Array.from(new Set(captures
189
186
  .filter((c) => c.name === 'callee' && c.text.trim().length > 0)
190
187
  .map((c) => c.text.trim())));
191
188
  }
192
189
  async gatherDeepAnalysis(primaryText, parsedTree, lang, flowPack) {
193
- const queryFn = AstParser.queryCapturesFromQuery;
194
- if (!parsedTree ||
195
- !lang ||
196
- !flowPack?.control ||
197
- !flowPack?.exceptions ||
198
- typeof queryFn !== 'function') {
190
+ if (!parsedTree || !lang || !flowPack?.control || !flowPack?.exceptions) {
199
191
  return summarizeControlFlow(primaryText);
200
192
  }
201
- const controlCaptures = (await queryFn(parsedTree, lang, flowPack.control));
202
- const exceptionCaptures = (await queryFn(parsedTree, lang, flowPack.exceptions));
193
+ const controlCaptures = await AstParser.queryCapturesFromQuery(parsedTree, lang, flowPack.control);
194
+ const exceptionCaptures = await AstParser.queryCapturesFromQuery(parsedTree, lang, flowPack.exceptions);
203
195
  const branchCount = controlCaptures.filter((c) => c.name === 'branch').length;
204
196
  const loopCount = controlCaptures.filter((c) => c.name === 'loop').length;
205
197
  const asyncBoundaryCount = controlCaptures.filter((c) => c.name === 'async').length;
@@ -39,7 +39,6 @@ export class GhostDependencyGatherer {
39
39
  mode: 'outline',
40
40
  content: `// Ghost Dependency: Shares tokens [${Array.from(data.tokens).join(', ')}]
41
41
  `,
42
- reason: 'ghost_dependency',
43
42
  }));
44
43
  }
45
44
  }
@@ -50,6 +50,9 @@ export class KnowledgeGatherer {
50
50
  data.deprecated_rules.forEach((r) => allDeprecated.add(r));
51
51
  }
52
52
  if (data.architectural_decisions) {
53
+ if (!Array.isArray(aggregated.architectural_decisions)) {
54
+ aggregated.architectural_decisions = [];
55
+ }
53
56
  aggregated.architectural_decisions.push(...data.architectural_decisions);
54
57
  }
55
58
  if (data.user_preferences) {
@@ -119,6 +119,7 @@ export class ContextService {
119
119
  expectedTargetSetSignature;
120
120
  if (recordedTargetSetSignature !== expectedTargetSetSignature) {
121
121
  await this.cacheStore.delete(cacheKey);
122
+ this.deleteUpdater(cacheKey);
122
123
  this.cacheMetrics.misses += 1;
123
124
  return {
124
125
  missReason: 'target_signature_mismatch',
@@ -128,6 +129,7 @@ export class ContextService {
128
129
  const nextSignature = await this.computeTrackedFilesSignature(repoPath, entry.trackedFiles);
129
130
  if (nextSignature !== entry.signature) {
130
131
  await this.cacheStore.delete(cacheKey);
132
+ this.deleteUpdater(cacheKey);
131
133
  this.cacheMetrics.misses += 1;
132
134
  return { missReason: 'signature_mismatch', targetSetSignature: expectedTargetSetSignature };
133
135
  }
@@ -210,6 +212,7 @@ export class ContextService {
210
212
  if (!last || Date.now() - last <= this.cacheTtlMs)
211
213
  return false;
212
214
  await this.cacheStore.delete(cacheKey);
215
+ this.deleteUpdater(cacheKey);
213
216
  this.cacheMetrics.evictions += 1;
214
217
  return true;
215
218
  }
@@ -243,6 +246,7 @@ export class ContextService {
243
246
  }
244
247
  if (victimKey) {
245
248
  await this.cacheStore.delete(victimKey);
249
+ this.deleteUpdater(victimKey);
246
250
  this.cacheMetrics.evictions += 1;
247
251
  }
248
252
  }
@@ -253,6 +257,7 @@ export class ContextService {
253
257
  const chunk = victims.slice(i, i + 10);
254
258
  await Promise.all(chunk.map(async ([key]) => {
255
259
  await this.cacheStore.delete(key);
260
+ this.deleteUpdater(key);
256
261
  this.cacheMetrics.evictions += 1;
257
262
  }));
258
263
  }
@@ -285,6 +290,9 @@ export class ContextService {
285
290
  }
286
291
  return updater;
287
292
  }
293
+ deleteUpdater(key) {
294
+ this.updaters.delete(key);
295
+ }
288
296
  logDiff(key, diff) {
289
297
  if (!diff.addedFiles.length && !diff.modifiedFiles.length && !diff.removedFiles.length) {
290
298
  return;
@@ -24,13 +24,13 @@ class TiktokenEncoding {
24
24
  this.initialized = true;
25
25
  }
26
26
  encode(text) {
27
- this.ensureInitialized();
28
- return Array.from(this.encoder.encode(text));
27
+ const encoder = this.getEncoder();
28
+ return Array.from(encoder.encode(text));
29
29
  }
30
30
  decode(tokens) {
31
- this.ensureInitialized();
31
+ const encoder = this.getEncoder();
32
32
  const uint32Tokens = new Uint32Array(tokens);
33
- const decoded = this.encoder.decode(uint32Tokens);
33
+ const decoded = encoder.decode(uint32Tokens);
34
34
  return new TextDecoder().decode(decoded);
35
35
  }
36
36
  count(text) {
@@ -43,10 +43,11 @@ class TiktokenEncoding {
43
43
  }
44
44
  this.initialized = false;
45
45
  }
46
- ensureInitialized() {
47
- if (!this.initialized || !this.encoder) {
46
+ getEncoder() {
47
+ if (!this.encoder) {
48
48
  throw new Error(`Encoding ${this.name} not initialized. Call initialize() first.`);
49
49
  }
50
+ return this.encoder;
50
51
  }
51
52
  }
52
53
  /**
@@ -3,9 +3,9 @@ import { buildResolvedMcpServersV2 } from '../mcp/config/index.js';
3
3
  import { getLogger } from '../observability/logger.js';
4
4
  import { loadConfig } from './load.js';
5
5
  import { mergeScopedEntries } from './merge.js';
6
- import { expandHome, getRepoMcpConfigPath, getRepoSkillConfigPath, getRepoToolConfigPath, getUserMcpConfigPath, getUserSkillConfigPath, getUserToolConfigPath, isWithinRoot, resolveRepoRelative, resolveUserRelative, } from './paths.js';
6
+ import { expandHome, getRepoAgentsConfigPath, getRepoMcpConfigPath, getRepoSkillConfigPath, getRepoToolConfigPath, getUserAgentsConfigPath, getUserMcpConfigPath, getUserSkillConfigPath, getUserToolConfigPath, isWithinRoot, resolveRepoRelative, resolveUserRelative, } from './paths.js';
7
7
  import { redactExtensions } from './redact.js';
8
- import { McpConfigSchema, SkillsConfigSchema, ToolsConfigSchema } from './schemas.js';
8
+ import { AgentsConfigSchema, McpConfigSchema, SkillsConfigSchema, ToolsConfigSchema, } from './schemas.js';
9
9
  function defaultEnabled(scope) {
10
10
  return scope === 'repo';
11
11
  }
@@ -29,6 +29,47 @@ function buildResolvedPlugins(entries, repoRoot) {
29
29
  };
30
30
  });
31
31
  }
32
+ function buildResolvedAgentProfiles(user, repo) {
33
+ const seen = new Map();
34
+ // User profiles first (lower priority)
35
+ if (user) {
36
+ for (const agent of user.agents) {
37
+ if (agent.enabled === false)
38
+ continue;
39
+ seen.set(agent.id, toResolvedProfile(agent, 'user'));
40
+ }
41
+ }
42
+ // Repo profiles override user profiles (higher priority)
43
+ if (repo) {
44
+ for (const agent of repo.agents) {
45
+ if (agent.enabled === false) {
46
+ seen.delete(agent.id);
47
+ continue;
48
+ }
49
+ seen.set(agent.id, toResolvedProfile(agent, 'repo'));
50
+ }
51
+ }
52
+ return Array.from(seen.values());
53
+ }
54
+ function toResolvedProfile(raw, scope) {
55
+ return {
56
+ id: raw.id,
57
+ name: raw.name,
58
+ role: raw.role,
59
+ description: raw.description,
60
+ allowedTools: raw.allowedTools ?? ['code.search', 'fs.read'],
61
+ readOnly: raw.readOnly ?? false,
62
+ stratagem: raw.stratagem ?? 'investigator',
63
+ toolInheritance: raw.toolInheritance,
64
+ permissionMode: raw.permissionMode,
65
+ systemPrompt: raw.systemPrompt,
66
+ maxTokens: raw.maxTokens,
67
+ maxAttempts: raw.maxAttempts,
68
+ timeoutMs: raw.timeoutMs,
69
+ model: raw.model,
70
+ scope,
71
+ };
72
+ }
32
73
  function buildResolvedSkills(user, repo, repoRoot) {
33
74
  const repoDiscovery = repo?.discovery;
34
75
  const userDiscovery = user?.discovery;
@@ -73,13 +114,15 @@ function buildResolvedSkills(user, repo, repoRoot) {
73
114
  }
74
115
  export async function resolveExtensions(options) {
75
116
  const { repoRoot } = options;
76
- const [userMcp, repoMcp, userTools, repoTools, userSkills, repoSkills] = await Promise.all([
117
+ const [userMcp, repoMcp, userTools, repoTools, userSkills, repoSkills, userAgents, repoAgents] = await Promise.all([
77
118
  loadConfig(getUserMcpConfigPath(), McpConfigSchema),
78
119
  loadConfig(getRepoMcpConfigPath(repoRoot), McpConfigSchema),
79
120
  loadConfig(getUserToolConfigPath(), ToolsConfigSchema),
80
121
  loadConfig(getRepoToolConfigPath(repoRoot), ToolsConfigSchema),
81
122
  loadConfig(getUserSkillConfigPath(), SkillsConfigSchema),
82
123
  loadConfig(getRepoSkillConfigPath(repoRoot), SkillsConfigSchema),
124
+ loadConfig(getUserAgentsConfigPath(), AgentsConfigSchema),
125
+ loadConfig(getRepoAgentsConfigPath(repoRoot), AgentsConfigSchema),
83
126
  ]);
84
127
  const mergedServers = mergeScopedEntries(userMcp?.config.servers, repoMcp?.config.servers);
85
128
  const mergedPlugins = mergeScopedEntries(userTools?.config.plugins, repoTools?.config.plugins);
@@ -87,11 +130,13 @@ export async function resolveExtensions(options) {
87
130
  mcpServers: buildResolvedMcpServersV2(mergedServers, repoRoot),
88
131
  toolPlugins: buildResolvedPlugins(mergedPlugins, repoRoot),
89
132
  skillDiscovery: buildResolvedSkills(userSkills?.config, repoSkills?.config, repoRoot),
133
+ agentProfiles: buildResolvedAgentProfiles(userAgents?.config, repoAgents?.config),
90
134
  };
91
135
  const rawEffective = {
92
136
  mcp: repoMcp?.config ?? userMcp?.config ?? null,
93
137
  tools: repoTools?.config ?? userTools?.config ?? null,
94
138
  skills: repoSkills?.config ?? userSkills?.config ?? null,
139
+ agents: repoAgents?.config ?? userAgents?.config ?? null,
95
140
  };
96
141
  return {
97
142
  resolved,
@@ -1,4 +1,5 @@
1
1
  import { syncFs as fs } from '../adapters/fs/node-fs.js';
2
+ import { errorMessage } from '../utils/error.js';
2
3
  export class ExtensionConfigError extends Error {
3
4
  path;
4
5
  constructor(path, message) {
@@ -18,7 +19,7 @@ export async function tryLoadJsonFile(path) {
18
19
  : undefined) === 'ENOENT') {
19
20
  return { exists: false };
20
21
  }
21
- throw new ExtensionConfigError(path, (error instanceof Error ? error.message : String(error)) || 'Unable to read file');
22
+ throw new ExtensionConfigError(path, errorMessage(error) || 'Unable to read file');
22
23
  }
23
24
  }
24
25
  export async function loadConfig(path, schema) {
@@ -30,7 +31,7 @@ export async function loadConfig(path, schema) {
30
31
  return { path, config };
31
32
  }
32
33
  catch (error) {
33
- throw new ExtensionConfigError(path, (error instanceof Error ? error.message : String(error)) || 'Schema validation failed');
34
+ throw new ExtensionConfigError(path, errorMessage(error) || 'Schema validation failed');
34
35
  }
35
36
  }
36
37
  //# sourceMappingURL=load.js.map
@@ -11,7 +11,10 @@ export function mergeScopedEntries(user, repo) {
11
11
  if (previous) {
12
12
  merged.set(key, {
13
13
  key,
14
- entry: { ...previous.entry, ...entry },
14
+ entry: {
15
+ ...previous.entry,
16
+ ...entry,
17
+ },
15
18
  scope: 'repo',
16
19
  });
17
20
  }
@@ -38,6 +41,7 @@ export function mergeResolvedExtensions(base, overlay) {
38
41
  : base.skillDiscovery.scope,
39
42
  paths: [...base.skillDiscovery.paths, ...overlay.skillDiscovery.paths],
40
43
  },
44
+ agentProfiles: [...base.agentProfiles, ...overlay.agentProfiles],
41
45
  };
42
46
  }
43
47
  //# sourceMappingURL=merge.js.map
@@ -39,6 +39,12 @@ export function getUserToolConfigPath() {
39
39
  export function getUserSkillConfigPath() {
40
40
  return path.join(USER_CONFIG_DIR, 'skills-user.json');
41
41
  }
42
+ export function getRepoAgentsConfigPath(repoRoot) {
43
+ return path.join(repoRoot, REPO_CONFIG_DIR, 'agents.json');
44
+ }
45
+ export function getUserAgentsConfigPath() {
46
+ return path.join(USER_CONFIG_DIR, 'agents-user.json');
47
+ }
42
48
  /**
43
49
  * Check whether a candidate path resides within (or equals) a given root directory.
44
50
  *
@@ -21,4 +21,25 @@ export const SkillsConfigSchema = z
21
21
  discovery: skillDiscoverySchema.optional().default({}),
22
22
  })
23
23
  .strict();
24
+ export const AgentProfileConfigSchema = z.object({
25
+ id: z.string().min(1),
26
+ name: z.string().min(1),
27
+ role: z.string().min(1),
28
+ description: z.string(),
29
+ allowedTools: z.array(z.string()).optional(),
30
+ toolInheritance: z.enum(['none', 'safe', 'all']).optional(),
31
+ permissionMode: z.enum(['default', 'plan', 'bypassPermissions']).optional(),
32
+ systemPrompt: z.string().optional(),
33
+ readOnly: z.boolean().optional(),
34
+ stratagem: z.enum(['investigator', 'surgeon', 'janitor']).optional(),
35
+ maxTokens: z.number().positive().optional(),
36
+ maxAttempts: z.number().positive().optional(),
37
+ timeoutMs: z.number().positive().optional(),
38
+ model: z.string().optional(),
39
+ enabled: z.boolean().optional(),
40
+ });
41
+ export const AgentsConfigSchema = z.object({
42
+ version: z.literal(1),
43
+ agents: z.array(AgentProfileConfigSchema),
44
+ });
24
45
  //# sourceMappingURL=schemas.js.map
@@ -1,9 +1,11 @@
1
1
  export { ConfigError, normalizePermissionMode, resolveConfig } from '../config/index.js';
2
2
  export { ExtensionConfigError, resolveExtensions } from '../extensions/index.js';
3
3
  export { createRuntimeLlm } from '../llm/factory.js';
4
+ export { createSubAgentLlmFactory } from '../llm/sub-agent-factory.js';
4
5
  export { getLogger } from '../observability/logger.js';
5
6
  export { PluginLoader } from '../plugin/loader.js';
6
7
  export { resolveExecutionProfile } from '../runtime/execution-profile.js';
7
8
  export { clearPluginRegistry, createPluginRegistry, setPluginRegistry, } from '../plugin/registry.js';
8
9
  export { clearPromptRegistry, createPromptRegistry, setPromptRegistry, } from '../prompts/registry.js';
10
+ export { getString } from '../utils/serialize.js';
9
11
  //# sourceMappingURL=cli-command-chat.js.map
@@ -1,4 +1,5 @@
1
1
  export { normalizePermissionMode } from '../config/index.js';
2
+ export { createSubAgentLlmFactory } from '../llm/sub-agent-factory.js';
2
3
  export { getLogger, PlainReporter, SilentReporter } from '../observability/logger.js';
3
4
  export { createPluginRegistry, setPluginRegistry, } from '../plugin/registry.js';
4
5
  export { createPromptRegistry, setPromptRegistry, } from '../prompts/registry.js';
@@ -0,0 +1,2 @@
1
+ export { isRecord } from '../utils/serialize.js';
2
+ //# sourceMappingURL=cli-utils-serialize.js.map
@@ -1,5 +1,6 @@
1
1
  import { resolveLlmCapabilities } from '../../llm/capabilities.js';
2
2
  import { Phase } from '../../types/runtime.js';
3
+ import { FileStatus, OpType } from '../domain/grizzco-types.js';
3
4
  import { DecisionEngine, PlanBuilder } from './DecisionEngine.js';
4
5
  function defaultMaxRoundsForPhase(phase) {
5
6
  // Explore needs more rounds to navigate the codebase
@@ -24,7 +25,7 @@ export function resolveLlmToolCallingPolicy(phase, llm) {
24
25
  repoRoot: '',
25
26
  file: {
26
27
  path: '',
27
- status: 0,
28
+ status: FileStatus.CLEAN,
28
29
  isBinary: false,
29
30
  isSymlink: false,
30
31
  isIgnored: false,
@@ -32,7 +33,7 @@ export function resolveLlmToolCallingPolicy(phase, llm) {
32
33
  size: 0,
33
34
  },
34
35
  operation: {
35
- type: 0,
36
+ type: OpType.PATCH,
36
37
  path: '',
37
38
  },
38
39
  options: {
@@ -5,6 +5,7 @@ import { buildFailureEnvelope } from '../../../observability/error-envelope.js';
5
5
  import { getTokenUsageFromAuditTrail } from '../../../observability/token-usage.js';
6
6
  import { resolveExecutionProfile } from '../../../runtime/execution-profile.js';
7
7
  import { ErrorType, Phase } from '../../../types/runtime.js';
8
+ import { isRecord } from '../../../utils/serialize.js';
8
9
  const ROOT_CAUSE_CODES = [
9
10
  'LLM_RATE_LIMITED',
10
11
  'LLM_UPSTREAM_5XX',
@@ -34,17 +35,19 @@ export function buildLoopResultFromTransaction({ executionReport, flowMode, opti
34
35
  : 'NON_RETRYABLE_FAILURE';
35
36
  const ctx = executionReport.lastContext ??
36
37
  executionReport.flowReport.data;
38
+ const ctxObj = isRecord(ctx) ? ctx : null;
37
39
  const contextHash = (() => {
38
- const hashFromBudget = ctx?.contextResult?.meta?.contextHash;
40
+ const contextResult = isRecord(ctxObj?.contextResult) ? ctxObj.contextResult : null;
41
+ const meta = isRecord(contextResult?.meta) ? contextResult.meta : null;
42
+ const hashFromBudget = meta?.contextHash;
39
43
  if (typeof hashFromBudget === 'string')
40
44
  return hashFromBudget;
41
- const hashFromContext = ctx && typeof ctx === 'object' && 'context' in ctx
42
- ? ctx.context?.contextHash
43
- : undefined;
45
+ const context = isRecord(ctxObj?.context) ? ctxObj.context : null;
46
+ const hashFromContext = context?.contextHash;
44
47
  return typeof hashFromContext === 'string' ? hashFromContext : undefined;
45
48
  })();
46
- const verifyArtifact = ctx && typeof ctx === 'object' && 'verifyArtifact' in ctx
47
- ? ctx.verifyArtifact
49
+ const verifyArtifact = ctxObj && typeof ctxObj.verifyArtifact !== 'undefined'
50
+ ? ctxObj.verifyArtifact
48
51
  : executionReport.lastVerifyArtifact;
49
52
  const artifactHints = (() => {
50
53
  const hints = {
@@ -71,12 +74,14 @@ export function buildLoopResultFromTransaction({ executionReport, flowMode, opti
71
74
  }
72
75
  return hints;
73
76
  })();
77
+ const report = isRecord(ctxObj?.report) ? ctxObj.report : null;
74
78
  const assistantMessage = ((flowMode === 'answer' || profile.driver === 'agent') &&
75
- ctx?.report?.summary?.trim?.()
76
- ? String(ctx.report.summary).trim()
79
+ typeof report?.summary === 'string' &&
80
+ report.summary.trim()
81
+ ? report.summary.trim()
77
82
  : undefined) ?? undefined;
78
- const finalPatch = ctx && typeof ctx === 'object' && 'diff' in ctx ? ctx.diff : undefined;
79
- const changedFiles = ctx && typeof ctx === 'object' && 'changedFiles' in ctx ? ctx.changedFiles : undefined;
83
+ const finalPatch = ctxObj && typeof ctxObj.diff === 'string' ? ctxObj.diff : undefined;
84
+ const changedFiles = Array.isArray(ctxObj?.changedFiles) ? ctxObj.changedFiles : undefined;
80
85
  const authorizationDecisions = (() => {
81
86
  if (!options.eventPayload?.includeAuthorizationDecisions)
82
87
  return undefined;