chainlesschain 0.166.132 → 0.166.133

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.
@@ -14,7 +14,12 @@ export class WSChatHandler {
14
14
  * @param {import("./ws-session-manager.js").Session} options.session
15
15
  * @param {import("./interaction-adapter.js").WebSocketInteractionAdapter} options.interaction
16
16
  */
17
- constructor({ session, interaction, evolutionCompositionFactory = null }) {
17
+ constructor({
18
+ session,
19
+ interaction,
20
+ evolutionCompositionFactory = null,
21
+ prepareUiModelTurn = null,
22
+ }) {
18
23
  if (
19
24
  evolutionCompositionFactory !== null &&
20
25
  typeof evolutionCompositionFactory !== "function"
@@ -22,6 +27,9 @@ export class WSChatHandler {
22
27
  throw new TypeError("evolutionCompositionFactory must be a function");
23
28
  }
24
29
  this._evolutionCompositionFactory = evolutionCompositionFactory;
30
+ if (prepareUiModelTurn !== null && typeof prepareUiModelTurn !== "function")
31
+ throw new TypeError("prepareUiModelTurn must be a function");
32
+ this._prepareUiModelTurn = prepareUiModelTurn;
25
33
  this.session = session;
26
34
  this.interaction = interaction;
27
35
  this._processing = false;
@@ -64,17 +72,22 @@ export class WSChatHandler {
64
72
  });
65
73
  runRecorded = true;
66
74
 
67
- const turn = await prepareGovernedModelTurn(
68
- this._evolutionCompositionFactory,
69
- {
70
- mode: "ws-chat-session",
71
- messages: session.messages,
72
- },
73
- );
75
+ const turn = await (
76
+ this._prepareUiModelTurn ||
77
+ ((context) =>
78
+ prepareGovernedModelTurn(this._evolutionCompositionFactory, context))
79
+ )({
80
+ mode: "ws-chat-session",
81
+ messages: session.messages,
82
+ });
74
83
  const options = {
75
84
  provider: session.provider,
76
85
  model: session.model,
77
- baseUrl: session.baseUrl || "http://localhost:11434",
86
+ baseUrl:
87
+ session.baseUrl ||
88
+ (session.provider === "ollama"
89
+ ? "http://localhost:11434"
90
+ : undefined),
78
91
  apiKey: session.apiKey,
79
92
  // Phase J — pipe WS session id so chat-core records token_usage
80
93
  // into the JSONL session store; visible via `cc session usage`.
@@ -10,7 +10,10 @@ import {
10
10
  normalizeProviderBaseUrl,
11
11
  openAICompatibleBaseUrl,
12
12
  } from "../lib/provider-protocol.js";
13
- import { BUILT_IN_PROVIDERS } from "../lib/llm-providers.js";
13
+ import {
14
+ BUILT_IN_PROVIDERS,
15
+ resolveProviderDefinition,
16
+ } from "../lib/llm-providers.js";
14
17
  import { resolveLlmConnection } from "../lib/llm-connection-resolution.js";
15
18
  /**
16
19
  * Agent Core — transport-independent agentic logic
@@ -12122,11 +12125,65 @@ function getEffectiveToolDefinitions(options = {}) {
12122
12125
  });
12123
12126
  }
12124
12127
 
12128
+ function projectExactFileMutationToolSchemas(
12129
+ definitions,
12130
+ scope,
12131
+ externalToolExecutors,
12132
+ ) {
12133
+ if (scope == null) return definitions;
12134
+ return definitions.map((tool) => {
12135
+ const name = tool?.function?.name;
12136
+ if (
12137
+ !["write_file", "edit_file", "edit_file_hashed"].includes(name) ||
12138
+ !AGENT_TOOLS.includes(tool) ||
12139
+ externalToolExecutors?.[name]
12140
+ ) {
12141
+ return tool;
12142
+ }
12143
+ const parameters = tool.function.parameters;
12144
+ return {
12145
+ ...tool,
12146
+ function: {
12147
+ ...tool.function,
12148
+ parameters: {
12149
+ ...parameters,
12150
+ properties: {
12151
+ ...parameters.properties,
12152
+ path: {
12153
+ ...parameters.properties.path,
12154
+ // Complete host-authorized set: never truncate this enum or
12155
+ // derive it from a read result. Normal tool context budgeting
12156
+ // sees the same schema that is sent to the provider.
12157
+ enum: [...scope.allowedPaths],
12158
+ description:
12159
+ "Use exactly one listed canonical repo-relative path. Read tools may return absolute paths; those are not valid mutation arguments in this run. Do not use an absolute path, ./ prefix, .. segment or backslash. This schema is guidance; all execution-time permission and file-identity checks still apply.",
12160
+ },
12161
+ },
12162
+ },
12163
+ },
12164
+ };
12165
+ });
12166
+ }
12167
+
12125
12168
  function getProviderToolDefinitions(options = {}) {
12126
- return projectMcpToolDefinitions(
12169
+ const definitions = projectMcpToolDefinitions(
12127
12170
  getEffectiveToolDefinitions(options),
12128
12171
  options.externalToolExecutors,
12129
12172
  );
12173
+ if (options.fileMutationScope == null) return definitions;
12174
+ if (options.exactToolNames !== true || options.hermeticExecution !== true) {
12175
+ throw new Error(
12176
+ "exact file mutation scope requires exactToolNames=true and hermeticExecution=true",
12177
+ );
12178
+ }
12179
+ const scope = normalizeExactFileMutationScope(options.fileMutationScope, {
12180
+ cwd: options.cwd || process.cwd(),
12181
+ });
12182
+ return projectExactFileMutationToolSchemas(
12183
+ definitions,
12184
+ scope,
12185
+ options.externalToolExecutors,
12186
+ );
12130
12187
  }
12131
12188
 
12132
12189
  const PROVIDER_REQUEST_ID_RE = /^[\x21-\x7e]{1,512}$/;
@@ -12540,7 +12597,7 @@ export async function chatWithTools(rawMessages, options) {
12540
12597
  }
12541
12598
 
12542
12599
  // OpenAI-compatible providers
12543
- const providerDefinition = BUILT_IN_PROVIDERS[provider];
12600
+ const providerDefinition = resolveProviderDefinition(provider);
12544
12601
  const url = openAICompatibleBaseUrl(provider, baseUrl);
12545
12602
 
12546
12603
  if (!providerDefinition || !url) {
@@ -12555,7 +12612,8 @@ export async function chatWithTools(rawMessages, options) {
12555
12612
 
12556
12613
  const envKey = providerDefinition.apiKeyEnv;
12557
12614
  const key = apiKey;
12558
- if (!key) throw new Error(`${envKey} required for provider ${provider}`);
12615
+ if (!key && provider !== "custom")
12616
+ throw new Error(`${envKey} required for provider ${provider}`);
12559
12617
  const effectiveModel = model || providerDefinition.models?.[0];
12560
12618
  const modelProfile = resolveModelCapabilityProfile({
12561
12619
  provider,
@@ -12695,7 +12753,7 @@ export async function chatWithTools(rawMessages, options) {
12695
12753
  method: "POST",
12696
12754
  headers: {
12697
12755
  "Content-Type": "application/json",
12698
- Authorization: `Bearer ${key}`,
12756
+ ...(key ? { Authorization: `Bearer ${key}` } : {}),
12699
12757
  ...(supportsOpenAIRequestIdentity && providerRequestId
12700
12758
  ? { "X-Client-Request-Id": providerRequestId }
12701
12759
  : {}),
@@ -13315,7 +13373,7 @@ async function _chatOpenAIStreaming(
13315
13373
  method: "POST",
13316
13374
  headers: {
13317
13375
  "Content-Type": "application/json",
13318
- Authorization: `Bearer ${apiKey}`,
13376
+ ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
13319
13377
  ...(providerRequestId
13320
13378
  ? { "X-Client-Request-Id": providerRequestId }
13321
13379
  : {}),
@@ -15018,6 +15076,7 @@ export async function* agentLoop(messages, options) {
15018
15076
  let effectiveToolOptions = hermeticExecution
15019
15077
  ? {
15020
15078
  ...options,
15079
+ fileMutationScope,
15021
15080
  hostManagedToolPolicy: null,
15022
15081
  hostManagedToolPolicyAuthority: null,
15023
15082
  extraToolDefinitions: [],