@llm4ts/core 2.45.0 → 2.46.0
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/dist/Connector.d.ts +27 -2
- package/dist/Connector.d.ts.map +1 -1
- package/dist/Connector.js +13 -4
- package/dist/Connector.js.map +1 -1
- package/dist/ConnectorConfig.d.ts +8 -0
- package/dist/ConnectorConfig.d.ts.map +1 -1
- package/dist/ConnectorConfig.js +9 -1
- package/dist/ConnectorConfig.js.map +1 -1
- package/dist/ConnectorRegistry.d.ts +2 -0
- package/dist/ConnectorRegistry.d.ts.map +1 -1
- package/dist/ConnectorRegistry.js +1 -0
- package/dist/ConnectorRegistry.js.map +1 -1
- package/dist/Models.d.ts +15 -0
- package/dist/Models.d.ts.map +1 -1
- package/dist/Models.js +15 -1
- package/dist/Models.js.map +1 -1
- package/dist/providers/AntigravityConnector.d.ts.map +1 -1
- package/dist/providers/AntigravityConnector.js +3 -1
- package/dist/providers/AntigravityConnector.js.map +1 -1
- package/dist/providers/ClaudeCliConnector.d.ts +17 -1
- package/dist/providers/ClaudeCliConnector.d.ts.map +1 -1
- package/dist/providers/ClaudeCliConnector.js +45 -4
- package/dist/providers/ClaudeCliConnector.js.map +1 -1
- package/dist/providers/CliSupport.d.ts +5 -0
- package/dist/providers/CliSupport.d.ts.map +1 -1
- package/dist/providers/CliSupport.js +5 -0
- package/dist/providers/CliSupport.js.map +1 -1
- package/dist/providers/CodexConnector.d.ts +7 -1
- package/dist/providers/CodexConnector.d.ts.map +1 -1
- package/dist/providers/CodexConnector.js +14 -1
- package/dist/providers/CodexConnector.js.map +1 -1
- package/dist/providers/CopilotConnector.d.ts.map +1 -1
- package/dist/providers/CopilotConnector.js +3 -0
- package/dist/providers/CopilotConnector.js.map +1 -1
- package/dist/providers/CursorConnector.d.ts +6 -1
- package/dist/providers/CursorConnector.d.ts.map +1 -1
- package/dist/providers/CursorConnector.js +13 -2
- package/dist/providers/CursorConnector.js.map +1 -1
- package/dist/providers/GeminiCliProvider.d.ts +20 -0
- package/dist/providers/GeminiCliProvider.d.ts.map +1 -1
- package/dist/providers/GeminiCliProvider.js +64 -3
- package/dist/providers/GeminiCliProvider.js.map +1 -1
- package/dist/providers/GrokCliConnector.d.ts.map +1 -1
- package/dist/providers/GrokCliConnector.js +1 -0
- package/dist/providers/GrokCliConnector.js.map +1 -1
- package/dist/providers/OpenCodeCliConnector.d.ts.map +1 -1
- package/dist/providers/OpenCodeCliConnector.js +2 -0
- package/dist/providers/OpenCodeCliConnector.js.map +1 -1
- package/dist/providers/PiConnector.d.ts +10 -1
- package/dist/providers/PiConnector.d.ts.map +1 -1
- package/dist/providers/PiConnector.js +31 -2
- package/dist/providers/PiConnector.js.map +1 -1
- package/package.json +1 -1
- package/src/Connector.ts +43 -9
- package/src/ConnectorConfig.ts +9 -1
- package/src/ConnectorRegistry.ts +3 -0
- package/src/Models.ts +18 -0
- package/src/providers/AntigravityConnector.ts +3 -1
- package/src/providers/ClaudeCliConnector.ts +52 -4
- package/src/providers/CliSupport.ts +7 -0
- package/src/providers/CodexConnector.ts +17 -2
- package/src/providers/CopilotConnector.ts +3 -0
- package/src/providers/CursorConnector.ts +15 -2
- package/src/providers/GeminiCliProvider.ts +76 -2
- package/src/providers/GrokCliConnector.ts +1 -0
- package/src/providers/OpenCodeCliConnector.ts +2 -0
- package/src/providers/PiConnector.ts +33 -2
|
@@ -129,6 +129,10 @@ export class GeminiCliExecutionContext extends Schema.Class<GeminiCliExecutionCo
|
|
|
129
129
|
/** The seat's own environment (a roster `env`), under llm4ts's own keys. */
|
|
130
130
|
envVars: Schema.Record(Schema.String, Schema.String).pipe(
|
|
131
131
|
Schema.withConstructorDefault(Effect.succeed<Readonly<Record<string, string>>>({}))
|
|
132
|
+
),
|
|
133
|
+
/** Globs under `cwd` the policy denies every tool (`CliConnectorConfig.deniedPaths`). */
|
|
134
|
+
deniedPaths: Schema.Array(Schema.String).pipe(
|
|
135
|
+
Schema.withConstructorDefault(Effect.succeed(emptyDirectories))
|
|
132
136
|
)
|
|
133
137
|
}) {}
|
|
134
138
|
|
|
@@ -141,6 +145,7 @@ export const geminiCliExecutionContextFrom = (
|
|
|
141
145
|
readOnly: config.readOnly,
|
|
142
146
|
isolated: config.isolated,
|
|
143
147
|
envVars: config.envVars,
|
|
148
|
+
deniedPaths: config.deniedPaths ?? emptyDirectories,
|
|
144
149
|
...(config.sandbox === undefined
|
|
145
150
|
? {}
|
|
146
151
|
: {
|
|
@@ -202,6 +207,66 @@ export const geminiHarnessPolicyToml = [
|
|
|
202
207
|
""
|
|
203
208
|
].join("\n")
|
|
204
209
|
|
|
210
|
+
const regexEscape = (text: string): string => text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")
|
|
211
|
+
|
|
212
|
+
/** A TOML basic string: backslashes and quotes escaped. */
|
|
213
|
+
const tomlString = (text: string): string =>
|
|
214
|
+
`"${text.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"')}"`
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The pattern that catches `glob` (a directory glob such as `.llm4ts/**`)
|
|
218
|
+
* in a tool call's arguments: the name as a path segment at the start of
|
|
219
|
+
* a relative path (after a quote, a space or `=` in the JSON) or
|
|
220
|
+
* right under `cwd` as an absolute one. A seat whose working directory is
|
|
221
|
+
* a shard worktree under `.llm4ts/port/shards/` therefore keeps its own
|
|
222
|
+
* tree: its paths pass through `.llm4ts` deeper down, under another root.
|
|
223
|
+
* Separators match either way, as the JSON doubles a Windows backslash.
|
|
224
|
+
* It is a match over the argument text: a path reached through `..`, a
|
|
225
|
+
* variable or a glob slips past it, which is why the grade is `partial`.
|
|
226
|
+
*/
|
|
227
|
+
export const geminiDeniedPathPattern = (glob: string, cwd: string | undefined): string => {
|
|
228
|
+
const separator = "[\\\\/]+"
|
|
229
|
+
const name = regexEscape(glob.replace(/\/\*\*$/u, ""))
|
|
230
|
+
// A path starts after a quote, whitespace, `=` or a shell operator, and
|
|
231
|
+
// may be spelled `./name`. The working directory as it appears in an
|
|
232
|
+
// argument is anchored the same way, so `/repo` never matches inside
|
|
233
|
+
// `/other/repo/`.
|
|
234
|
+
const boundary = `["'\\s=;&|(\`$]`
|
|
235
|
+
const dot = `(?:\\.${separator})?`
|
|
236
|
+
const under =
|
|
237
|
+
cwd === undefined
|
|
238
|
+
? ""
|
|
239
|
+
: `(?:${/^[\\/]/u.test(cwd) ? separator : ""}${cwd
|
|
240
|
+
.split(/[\\/]+/u)
|
|
241
|
+
.filter((segment) => segment.length > 0)
|
|
242
|
+
.map(regexEscape)
|
|
243
|
+
.join(separator)}${separator})?`
|
|
244
|
+
return `${boundary}${dot}${under}${name}(?=${separator}|["'\\s;&|)]|$)`
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The harness policy with one admin-tier deny per denied path on top: any
|
|
249
|
+
* tool call — the shell included — whose arguments name the run's own
|
|
250
|
+
* state is refused with a message that says whose it is.
|
|
251
|
+
*/
|
|
252
|
+
export const geminiHarnessPolicyFor = (context: GeminiCliExecutionContext): string =>
|
|
253
|
+
[
|
|
254
|
+
geminiHarnessPolicyToml,
|
|
255
|
+
...context.deniedPaths.map((glob) =>
|
|
256
|
+
[
|
|
257
|
+
"[[rule]]",
|
|
258
|
+
'toolName = "*"',
|
|
259
|
+
`argsPattern = ${tomlString(geminiDeniedPathPattern(glob, context.cwd))}`,
|
|
260
|
+
'decision = "deny"',
|
|
261
|
+
"priority = 999",
|
|
262
|
+
`denyMessage = ${tomlString(
|
|
263
|
+
`${glob} is the run state of llm4ts, the tool running you: traces, plans and transcripts that are not part of your task. Do not read, change or delete it.`
|
|
264
|
+
)}`,
|
|
265
|
+
""
|
|
266
|
+
].join("\n")
|
|
267
|
+
)
|
|
268
|
+
].join("\n")
|
|
269
|
+
|
|
205
270
|
/** Whether `gemini --help` lists `--admin-policy`, the flag the harness policy needs. */
|
|
206
271
|
export const geminiSupportsAdminPolicy = (help: string): boolean => help.includes("--admin-policy")
|
|
207
272
|
|
|
@@ -760,9 +825,13 @@ export const makeGeminiCliProvider = (
|
|
|
760
825
|
executor: GeminiCliExecutorShape,
|
|
761
826
|
executionContext: GeminiCliExecutionContext = GeminiCliExecutionContext.make({})
|
|
762
827
|
): CliConnectorShape => {
|
|
828
|
+
// `gemini --version` proves the install, not the login: the CLI has no auth
|
|
829
|
+
// status subcommand, and its account file records the last login rather than
|
|
830
|
+
// whether that token still works, so the login stays Unknown. `llm4ts doctor`
|
|
831
|
+
// reports the environment routes (API key, Vertex, project) separately.
|
|
763
832
|
const healthy = HealthStatus.make({
|
|
764
833
|
availability: "Healthy",
|
|
765
|
-
authStatus: "
|
|
834
|
+
authStatus: "Unknown"
|
|
766
835
|
})
|
|
767
836
|
const unhealthy = HealthStatus.make({
|
|
768
837
|
availability: "Unhealthy",
|
|
@@ -958,7 +1027,12 @@ export const makeGeminiCliProvider = (
|
|
|
958
1027
|
capabilities: ConnectorCapabilities.make({
|
|
959
1028
|
interactiveSessions: true,
|
|
960
1029
|
readOnlyEnforcement: "enforced",
|
|
961
|
-
isolatedHeadless: "partial"
|
|
1030
|
+
isolatedHeadless: "partial",
|
|
1031
|
+
// An admin-tier policy rule over every tool (`geminiHarnessPolicyFor`)
|
|
1032
|
+
// on a CLI that takes `--admin-policy` (older CLIs run without it),
|
|
1033
|
+
// matched over the argument text: a path spelled another way (`..`,
|
|
1034
|
+
// a variable, a glob) slips past, so the grade is partial.
|
|
1035
|
+
pathDeny: "partial"
|
|
962
1036
|
})
|
|
963
1037
|
})
|
|
964
1038
|
|
|
@@ -112,6 +112,7 @@ export const makeGrokCliConnector = (
|
|
|
112
112
|
buildInteractiveArgv: (_context) => ["grok", ...extraArgs],
|
|
113
113
|
complete,
|
|
114
114
|
completeStream,
|
|
115
|
+
// No auth probe: grok offers `login` but no status subcommand; Unknown.
|
|
115
116
|
versionProbe: { executor, binary: "grok", cwd }
|
|
116
117
|
})
|
|
117
118
|
}
|
|
@@ -145,6 +145,8 @@ export const makeOpenCodeCliConnector = (
|
|
|
145
145
|
buildInteractiveArgv: (_context) => ["opencode"],
|
|
146
146
|
complete,
|
|
147
147
|
completeStream,
|
|
148
|
+
// No auth probe: `opencode auth list` lists stored credentials by
|
|
149
|
+
// provider, not whether the provider this seat runs has any; Unknown.
|
|
148
150
|
versionProbe: { executor, binary: "opencode", cwd }
|
|
149
151
|
})
|
|
150
152
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect"
|
|
2
2
|
import * as Stream from "effect/Stream"
|
|
3
|
-
import { makeCliConnector, type CliConnectorShape } from "../Connector.ts"
|
|
3
|
+
import { makeCliConnector, type CliAuthProbe, type CliConnectorShape } from "../Connector.ts"
|
|
4
4
|
import type { CliConnectorConfig } from "../ConnectorConfig.ts"
|
|
5
5
|
import { InvalidRequestError, ProviderError, type LlmError } from "../Errors.ts"
|
|
6
6
|
import { ConnectorCapabilities, ConnectorIds, LlmChunk, TokenUsage } from "../Models.ts"
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
jsonStringField,
|
|
17
17
|
optionalModelArgs,
|
|
18
18
|
parseJsonLine,
|
|
19
|
+
parseJsonOutput,
|
|
19
20
|
sortedFlagArgs,
|
|
20
21
|
toolEventChunk,
|
|
21
22
|
toolResultChunk,
|
|
@@ -25,6 +26,31 @@ import {
|
|
|
25
26
|
statusChunk
|
|
26
27
|
} from "./CliSupport.ts"
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* `pi auth check --model <model> --no-refresh --json` says whether pi holds
|
|
31
|
+
* credentials for the provider that model resolves to:
|
|
32
|
+
* `{"status":"ready"}` or `{"status":"not_ready","reason":...}`. pi needs a
|
|
33
|
+
* provider or model to answer, so a seat with no model configured has no
|
|
34
|
+
* probe. `--no-refresh` keeps an expired OAuth token where it is: the check
|
|
35
|
+
* stays offline and reports it as not ready.
|
|
36
|
+
*/
|
|
37
|
+
export const piAuthProbe = (model: string): CliAuthProbe => ({
|
|
38
|
+
args: ["auth", "check", "--model", model, "--no-refresh", "--json"],
|
|
39
|
+
interpret: (result) => {
|
|
40
|
+
const json = parseJsonOutput(result.stdout)
|
|
41
|
+
switch (jsonStringField(json, "status")) {
|
|
42
|
+
case "ready":
|
|
43
|
+
return "Valid"
|
|
44
|
+
case "not_ready":
|
|
45
|
+
return jsonStringField(json, "reason") === "credentials_not_configured"
|
|
46
|
+
? "Missing"
|
|
47
|
+
: "Invalid"
|
|
48
|
+
default:
|
|
49
|
+
return "Unknown"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
28
54
|
/**
|
|
29
55
|
* The first pi whose `--tools` allowlist can be kept free of MCP tools: from
|
|
30
56
|
* 1.0.4 `--tools read` keeps every MCP tool the user configured in
|
|
@@ -284,6 +310,11 @@ export const makePiConnector = (
|
|
|
284
310
|
buildInteractiveArgv: (_context) => ["pi", ...extraArgs],
|
|
285
311
|
complete,
|
|
286
312
|
completeStream,
|
|
287
|
-
versionProbe: {
|
|
313
|
+
versionProbe: {
|
|
314
|
+
executor,
|
|
315
|
+
binary: "pi",
|
|
316
|
+
cwd,
|
|
317
|
+
...(config.model === undefined ? {} : { authProbe: piAuthProbe(config.model) })
|
|
318
|
+
}
|
|
288
319
|
})
|
|
289
320
|
}
|