@google/gemini-cli-core 0.15.0-preview.3 → 0.16.0-nightly.20251113.ad1f0d99
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/google-gemini-cli-core-0.16.0-nightly.20251112.c961f274.tgz +0 -0
- package/dist/src/agents/codebase-investigator.test.d.ts +6 -0
- package/dist/src/agents/codebase-investigator.test.js +35 -0
- package/dist/src/agents/codebase-investigator.test.js.map +1 -0
- package/dist/src/agents/executor.test.js +181 -1
- package/dist/src/agents/executor.test.js.map +1 -1
- package/dist/src/code_assist/codeAssist.test.d.ts +6 -0
- package/dist/src/code_assist/codeAssist.test.js +99 -0
- package/dist/src/code_assist/codeAssist.test.js.map +1 -0
- package/dist/src/code_assist/experiments/client_metadata.js +2 -1
- package/dist/src/code_assist/experiments/client_metadata.js.map +1 -1
- package/dist/src/code_assist/experiments/client_metadata.test.d.ts +6 -0
- package/dist/src/code_assist/experiments/client_metadata.test.js +99 -0
- package/dist/src/code_assist/experiments/client_metadata.test.js.map +1 -0
- package/dist/src/code_assist/experiments/experiments.test.d.ts +6 -0
- package/dist/src/code_assist/experiments/experiments.test.js +92 -0
- package/dist/src/code_assist/experiments/experiments.test.js.map +1 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js +49 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -1
- package/dist/src/code_assist/server.js +5 -8
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +109 -28
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/config/defaultModelConfigs.js +6 -0
- package/dist/src/config/defaultModelConfigs.js.map +1 -1
- package/dist/src/confirmation-bus/message-bus.d.ts +1 -1
- package/dist/src/confirmation-bus/message-bus.js +2 -2
- package/dist/src/confirmation-bus/message-bus.js.map +1 -1
- package/dist/src/confirmation-bus/message-bus.test.js +30 -24
- package/dist/src/confirmation-bus/message-bus.test.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.test.d.ts +6 -0
- package/dist/src/core/loggingContentGenerator.test.js +180 -0
- package/dist/src/core/loggingContentGenerator.test.js.map +1 -0
- package/dist/src/core/tokenLimits.test.d.ts +6 -0
- package/dist/src/core/tokenLimits.test.js +26 -0
- package/dist/src/core/tokenLimits.test.js.map +1 -0
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/hooks/hookAggregator.d.ts +68 -0
- package/dist/src/hooks/hookAggregator.js +262 -0
- package/dist/src/hooks/hookAggregator.js.map +1 -0
- package/dist/src/hooks/hookAggregator.test.d.ts +6 -0
- package/dist/src/hooks/hookAggregator.test.js +387 -0
- package/dist/src/hooks/hookAggregator.test.js.map +1 -0
- package/dist/src/hooks/types.js +1 -1
- package/dist/src/hooks/types.js.map +1 -1
- package/dist/src/hooks/types.test.js +280 -2
- package/dist/src/hooks/types.test.js.map +1 -1
- package/dist/src/ide/ide-client.test.js +159 -0
- package/dist/src/ide/ide-client.test.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +177 -0
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/policy/config.js +3 -1
- package/dist/src/policy/config.js.map +1 -1
- package/dist/src/policy/config.test.js +118 -1
- package/dist/src/policy/config.test.js.map +1 -1
- package/dist/src/policy/policies/write.toml +10 -0
- package/dist/src/policy/policy-engine.d.ts +12 -3
- package/dist/src/policy/policy-engine.js +61 -7
- package/dist/src/policy/policy-engine.js.map +1 -1
- package/dist/src/policy/policy-engine.test.js +422 -86
- package/dist/src/policy/policy-engine.test.js.map +1 -1
- package/dist/src/policy/toml-loader.d.ts +2 -1
- package/dist/src/policy/toml-loader.js +103 -6
- package/dist/src/policy/toml-loader.js.map +1 -1
- package/dist/src/policy/toml-loader.test.js +32 -88
- package/dist/src/policy/toml-loader.test.js.map +1 -1
- package/dist/src/policy/types.d.ts +65 -0
- package/dist/src/policy/types.js +4 -0
- package/dist/src/policy/types.js.map +1 -1
- package/dist/src/prompts/mcp-prompts.test.d.ts +6 -0
- package/dist/src/prompts/mcp-prompts.test.js +40 -0
- package/dist/src/prompts/mcp-prompts.test.js.map +1 -0
- package/dist/src/prompts/prompt-registry.test.d.ts +6 -0
- package/dist/src/prompts/prompt-registry.test.js +111 -0
- package/dist/src/prompts/prompt-registry.test.js.map +1 -0
- package/dist/src/safety/built-in.d.ts +21 -0
- package/dist/src/safety/built-in.js +106 -0
- package/dist/src/safety/built-in.js.map +1 -0
- package/dist/src/safety/built-in.test.d.ts +6 -0
- package/dist/src/safety/built-in.test.js +199 -0
- package/dist/src/safety/built-in.test.js.map +1 -0
- package/dist/src/safety/checker-runner.d.ts +48 -0
- package/dist/src/safety/checker-runner.js +208 -0
- package/dist/src/safety/checker-runner.js.map +1 -0
- package/dist/src/safety/checker-runner.test.d.ts +6 -0
- package/dist/src/safety/checker-runner.test.js +238 -0
- package/dist/src/safety/checker-runner.test.js.map +1 -0
- package/dist/src/safety/context-builder.d.ts +23 -0
- package/dist/src/safety/context-builder.js +47 -0
- package/dist/src/safety/context-builder.js.map +1 -0
- package/dist/src/safety/context-builder.test.d.ts +6 -0
- package/dist/src/safety/context-builder.test.js +49 -0
- package/dist/src/safety/context-builder.test.js.map +1 -0
- package/dist/src/safety/protocol.d.ts +88 -0
- package/dist/src/safety/protocol.js +15 -0
- package/dist/src/safety/protocol.js.map +1 -0
- package/dist/src/safety/registry.d.ts +26 -0
- package/dist/src/safety/registry.js +65 -0
- package/dist/src/safety/registry.js.map +1 -0
- package/dist/src/safety/registry.test.d.ts +6 -0
- package/dist/src/safety/registry.test.js +31 -0
- package/dist/src/safety/registry.test.js.map +1 -0
- package/dist/src/services/loopDetectionService.d.ts +3 -0
- package/dist/src/services/loopDetectionService.js +81 -41
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/loopDetectionService.test.js +96 -4
- package/dist/src/services/loopDetectionService.test.js.map +1 -1
- package/dist/src/services/test-data/resolved-aliases.golden.json +7 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +4 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +29 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +31 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +5 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +12 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +2 -1
- package/dist/src/telemetry/loggers.js +11 -0
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +15 -2
- package/dist/src/telemetry/types.js +42 -3
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/tools/base-tool-invocation.test.js +2 -2
- package/dist/src/tools/base-tool-invocation.test.js.map +1 -1
- package/dist/src/tools/memoryTool.js +1 -1
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/memoryTool.test.js +1 -1
- package/dist/src/tools/memoryTool.test.js.map +1 -1
- package/dist/src/tools/ripGrep.d.ts +24 -7
- package/dist/src/tools/ripGrep.js +125 -145
- package/dist/src/tools/ripGrep.js.map +1 -1
- package/dist/src/tools/ripGrep.test.js +144 -20
- package/dist/src/tools/ripGrep.test.js.map +1 -1
- package/dist/src/tools/tools.js +1 -1
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/write-todos.js +1 -1
- package/dist/src/tools/write-todos.js.map +1 -1
- package/dist/src/utils/llm-edit-fixer.test.js +8 -1
- package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/google-gemini-cli-core-0.15.0-preview.2.tgz +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { FunctionCall } from '@google/genai';
|
|
7
|
+
import type { SafetyCheckerConfig } from '../policy/types.js';
|
|
8
|
+
import type { SafetyCheckResult } from './protocol.js';
|
|
9
|
+
import type { CheckerRegistry } from './registry.js';
|
|
10
|
+
import type { ContextBuilder } from './context-builder.js';
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for the checker runner.
|
|
13
|
+
*/
|
|
14
|
+
export interface CheckerRunnerConfig {
|
|
15
|
+
/**
|
|
16
|
+
* Maximum time (in milliseconds) to wait for a checker to complete.
|
|
17
|
+
* Default: 5000 (5 seconds)
|
|
18
|
+
*/
|
|
19
|
+
timeout?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Path to the directory containing external checkers.
|
|
22
|
+
*/
|
|
23
|
+
checkersPath: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Service for executing safety checker processes.
|
|
27
|
+
*/
|
|
28
|
+
export declare class CheckerRunner {
|
|
29
|
+
private static readonly DEFAULT_TIMEOUT;
|
|
30
|
+
private readonly registry;
|
|
31
|
+
private readonly contextBuilder;
|
|
32
|
+
private readonly timeout;
|
|
33
|
+
constructor(contextBuilder: ContextBuilder, registry: CheckerRegistry, config: CheckerRunnerConfig);
|
|
34
|
+
/**
|
|
35
|
+
* Runs a safety checker and returns the result.
|
|
36
|
+
*/
|
|
37
|
+
runChecker(toolCall: FunctionCall, checkerConfig: SafetyCheckerConfig): Promise<SafetyCheckResult>;
|
|
38
|
+
private runInProcessChecker;
|
|
39
|
+
private runExternalChecker;
|
|
40
|
+
/**
|
|
41
|
+
* Executes an external checker process and handles its lifecycle.
|
|
42
|
+
*/
|
|
43
|
+
private executeCheckerProcess;
|
|
44
|
+
/**
|
|
45
|
+
* Executes a promise with a timeout.
|
|
46
|
+
*/
|
|
47
|
+
private executeWithTimeout;
|
|
48
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { SafetyCheckDecision } from './protocol.js';
|
|
8
|
+
/**
|
|
9
|
+
* Service for executing safety checker processes.
|
|
10
|
+
*/
|
|
11
|
+
export class CheckerRunner {
|
|
12
|
+
static DEFAULT_TIMEOUT = 5000; // 5 seconds
|
|
13
|
+
registry;
|
|
14
|
+
contextBuilder;
|
|
15
|
+
timeout;
|
|
16
|
+
constructor(contextBuilder, registry, config) {
|
|
17
|
+
this.contextBuilder = contextBuilder;
|
|
18
|
+
this.registry = registry;
|
|
19
|
+
this.timeout = config.timeout ?? CheckerRunner.DEFAULT_TIMEOUT;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Runs a safety checker and returns the result.
|
|
23
|
+
*/
|
|
24
|
+
async runChecker(toolCall, checkerConfig) {
|
|
25
|
+
if (checkerConfig.type === 'in-process') {
|
|
26
|
+
return this.runInProcessChecker(toolCall, checkerConfig);
|
|
27
|
+
}
|
|
28
|
+
return this.runExternalChecker(toolCall, checkerConfig);
|
|
29
|
+
}
|
|
30
|
+
async runInProcessChecker(toolCall, checkerConfig) {
|
|
31
|
+
try {
|
|
32
|
+
const checker = this.registry.resolveInProcess(checkerConfig.name);
|
|
33
|
+
const context = checkerConfig.required_context
|
|
34
|
+
? this.contextBuilder.buildMinimalContext(checkerConfig.required_context)
|
|
35
|
+
: this.contextBuilder.buildFullContext();
|
|
36
|
+
const input = {
|
|
37
|
+
protocolVersion: '1.0.0',
|
|
38
|
+
toolCall,
|
|
39
|
+
context,
|
|
40
|
+
config: checkerConfig.config,
|
|
41
|
+
};
|
|
42
|
+
// In-process checkers can be async, but we'll also apply a timeout
|
|
43
|
+
// for safety, in case of infinite loops or unexpected delays.
|
|
44
|
+
return await this.executeWithTimeout(checker.check(input));
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
decision: SafetyCheckDecision.DENY,
|
|
49
|
+
reason: `Failed to run in-process checker "${checkerConfig.name}": ${error instanceof Error ? error.message : String(error)}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async runExternalChecker(toolCall, checkerConfig) {
|
|
54
|
+
try {
|
|
55
|
+
// Resolve the checker executable path
|
|
56
|
+
const checkerPath = this.registry.resolveExternal(checkerConfig.name);
|
|
57
|
+
// Build the appropriate context
|
|
58
|
+
const context = checkerConfig.required_context
|
|
59
|
+
? this.contextBuilder.buildMinimalContext(checkerConfig.required_context)
|
|
60
|
+
: this.contextBuilder.buildFullContext();
|
|
61
|
+
// Create the input payload
|
|
62
|
+
const input = {
|
|
63
|
+
protocolVersion: '1.0.0',
|
|
64
|
+
toolCall,
|
|
65
|
+
context,
|
|
66
|
+
config: checkerConfig.config,
|
|
67
|
+
};
|
|
68
|
+
// Run the checker process
|
|
69
|
+
return await this.executeCheckerProcess(checkerPath, input, checkerConfig.name);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// If anything goes wrong, deny the operation
|
|
73
|
+
return {
|
|
74
|
+
decision: SafetyCheckDecision.DENY,
|
|
75
|
+
reason: `Failed to run safety checker "${checkerConfig.name}": ${error instanceof Error ? error.message : String(error)}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Executes an external checker process and handles its lifecycle.
|
|
81
|
+
*/
|
|
82
|
+
executeCheckerProcess(checkerPath, input, checkerName) {
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
const child = spawn(checkerPath, [], {
|
|
85
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
86
|
+
});
|
|
87
|
+
let stdout = '';
|
|
88
|
+
let stderr = '';
|
|
89
|
+
let timeoutHandle = null;
|
|
90
|
+
let killed = false;
|
|
91
|
+
let exited = false;
|
|
92
|
+
// Set up timeout
|
|
93
|
+
timeoutHandle = setTimeout(() => {
|
|
94
|
+
killed = true;
|
|
95
|
+
child.kill('SIGTERM');
|
|
96
|
+
resolve({
|
|
97
|
+
decision: SafetyCheckDecision.DENY,
|
|
98
|
+
reason: `Safety checker "${checkerName}" timed out after ${this.timeout}ms`,
|
|
99
|
+
});
|
|
100
|
+
// Fallback: if process doesn't exit after 5s, force kill
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
if (!exited) {
|
|
103
|
+
child.kill('SIGKILL');
|
|
104
|
+
}
|
|
105
|
+
}, 5000).unref();
|
|
106
|
+
}, this.timeout);
|
|
107
|
+
// Collect output
|
|
108
|
+
if (child.stdout) {
|
|
109
|
+
child.stdout.on('data', (data) => {
|
|
110
|
+
stdout += data.toString();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (child.stderr) {
|
|
114
|
+
child.stderr.on('data', (data) => {
|
|
115
|
+
stderr += data.toString();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Handle process completion
|
|
119
|
+
child.on('close', (code) => {
|
|
120
|
+
exited = true;
|
|
121
|
+
if (timeoutHandle) {
|
|
122
|
+
clearTimeout(timeoutHandle);
|
|
123
|
+
}
|
|
124
|
+
// If we already killed it due to timeout, don't process the result
|
|
125
|
+
if (killed) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Non-zero exit code is a failure
|
|
129
|
+
if (code !== 0) {
|
|
130
|
+
resolve({
|
|
131
|
+
decision: SafetyCheckDecision.DENY,
|
|
132
|
+
reason: `Safety checker "${checkerName}" exited with code ${code}${stderr ? `: ${stderr}` : ''}`,
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Try to parse the output
|
|
137
|
+
try {
|
|
138
|
+
const result = JSON.parse(stdout);
|
|
139
|
+
// Validate the result structure
|
|
140
|
+
if (!result.decision ||
|
|
141
|
+
!Object.values(SafetyCheckDecision).includes(result.decision)) {
|
|
142
|
+
throw new Error('Invalid result: missing or invalid "decision" field');
|
|
143
|
+
}
|
|
144
|
+
resolve(result);
|
|
145
|
+
}
|
|
146
|
+
catch (parseError) {
|
|
147
|
+
resolve({
|
|
148
|
+
decision: SafetyCheckDecision.DENY,
|
|
149
|
+
reason: `Failed to parse output from safety checker "${checkerName}": ${parseError instanceof Error
|
|
150
|
+
? parseError.message
|
|
151
|
+
: String(parseError)}`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
// Handle process errors
|
|
156
|
+
child.on('error', (error) => {
|
|
157
|
+
if (timeoutHandle) {
|
|
158
|
+
clearTimeout(timeoutHandle);
|
|
159
|
+
}
|
|
160
|
+
if (!killed) {
|
|
161
|
+
resolve({
|
|
162
|
+
decision: SafetyCheckDecision.DENY,
|
|
163
|
+
reason: `Failed to spawn safety checker "${checkerName}": ${error.message}`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
// Send input to the checker
|
|
168
|
+
try {
|
|
169
|
+
if (child.stdin) {
|
|
170
|
+
child.stdin.write(JSON.stringify(input));
|
|
171
|
+
child.stdin.end();
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
throw new Error('Failed to open stdin for checker process');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (writeError) {
|
|
178
|
+
if (timeoutHandle) {
|
|
179
|
+
clearTimeout(timeoutHandle);
|
|
180
|
+
}
|
|
181
|
+
child.kill();
|
|
182
|
+
resolve({
|
|
183
|
+
decision: SafetyCheckDecision.DENY,
|
|
184
|
+
reason: `Failed to write to stdin of safety checker "${checkerName}": ${writeError instanceof Error
|
|
185
|
+
? writeError.message
|
|
186
|
+
: String(writeError)}`,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Executes a promise with a timeout.
|
|
193
|
+
*/
|
|
194
|
+
executeWithTimeout(promise) {
|
|
195
|
+
return new Promise((resolve, reject) => {
|
|
196
|
+
const timeoutHandle = setTimeout(() => {
|
|
197
|
+
reject(new Error(`Checker timed out after ${this.timeout}ms`));
|
|
198
|
+
}, this.timeout);
|
|
199
|
+
promise
|
|
200
|
+
.then(resolve)
|
|
201
|
+
.catch(reject)
|
|
202
|
+
.finally(() => {
|
|
203
|
+
clearTimeout(timeoutHandle);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=checker-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checker-runner.js","sourceRoot":"","sources":["../../../src/safety/checker-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import { spawn } from 'node:child_process';
|
|
8
|
+
import { CheckerRunner } from './checker-runner.js';
|
|
9
|
+
import { ContextBuilder } from './context-builder.js';
|
|
10
|
+
import { CheckerRegistry } from './registry.js';
|
|
11
|
+
import { InProcessCheckerType, } from '../policy/types.js';
|
|
12
|
+
import { SafetyCheckDecision } from './protocol.js';
|
|
13
|
+
// Mock dependencies
|
|
14
|
+
vi.mock('./registry.js');
|
|
15
|
+
vi.mock('./context-builder.js');
|
|
16
|
+
vi.mock('node:child_process');
|
|
17
|
+
describe('CheckerRunner', () => {
|
|
18
|
+
let runner;
|
|
19
|
+
let mockContextBuilder;
|
|
20
|
+
let mockRegistry;
|
|
21
|
+
const mockToolCall = { name: 'test_tool', args: {} };
|
|
22
|
+
const mockInProcessConfig = {
|
|
23
|
+
type: 'in-process',
|
|
24
|
+
name: InProcessCheckerType.ALLOWED_PATH,
|
|
25
|
+
};
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
mockContextBuilder = new ContextBuilder({});
|
|
28
|
+
mockRegistry = new CheckerRegistry('/mock/dist');
|
|
29
|
+
CheckerRegistry.prototype.resolveInProcess = vi.fn();
|
|
30
|
+
runner = new CheckerRunner(mockContextBuilder, mockRegistry, {
|
|
31
|
+
checkersPath: '/mock/dist',
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
vi.restoreAllMocks();
|
|
36
|
+
});
|
|
37
|
+
it('should run in-process checker successfully', async () => {
|
|
38
|
+
const mockResult = {
|
|
39
|
+
decision: SafetyCheckDecision.ALLOW,
|
|
40
|
+
};
|
|
41
|
+
const mockChecker = {
|
|
42
|
+
check: vi.fn().mockResolvedValue(mockResult),
|
|
43
|
+
};
|
|
44
|
+
vi.mocked(mockRegistry.resolveInProcess).mockReturnValue(mockChecker);
|
|
45
|
+
vi.mocked(mockContextBuilder.buildFullContext).mockReturnValue({
|
|
46
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
47
|
+
});
|
|
48
|
+
const result = await runner.runChecker(mockToolCall, mockInProcessConfig);
|
|
49
|
+
expect(result).toEqual(mockResult);
|
|
50
|
+
expect(mockRegistry.resolveInProcess).toHaveBeenCalledWith(InProcessCheckerType.ALLOWED_PATH);
|
|
51
|
+
expect(mockChecker.check).toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
it('should handle in-process checker errors', async () => {
|
|
54
|
+
const mockChecker = {
|
|
55
|
+
check: vi.fn().mockRejectedValue(new Error('Checker failed')),
|
|
56
|
+
};
|
|
57
|
+
vi.mocked(mockRegistry.resolveInProcess).mockReturnValue(mockChecker);
|
|
58
|
+
vi.mocked(mockContextBuilder.buildFullContext).mockReturnValue({
|
|
59
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
60
|
+
});
|
|
61
|
+
const result = await runner.runChecker(mockToolCall, mockInProcessConfig);
|
|
62
|
+
expect(result.decision).toBe(SafetyCheckDecision.DENY);
|
|
63
|
+
expect(result.reason).toContain('Failed to run in-process checker');
|
|
64
|
+
expect(result.reason).toContain('Checker failed');
|
|
65
|
+
});
|
|
66
|
+
it('should respect timeout for in-process checkers', async () => {
|
|
67
|
+
vi.useFakeTimers();
|
|
68
|
+
const mockChecker = {
|
|
69
|
+
check: vi.fn().mockImplementation(async () => {
|
|
70
|
+
await new Promise((resolve) => setTimeout(resolve, 6000)); // Longer than default 5s timeout
|
|
71
|
+
return { decision: SafetyCheckDecision.ALLOW };
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
vi.mocked(mockRegistry.resolveInProcess).mockReturnValue(mockChecker);
|
|
75
|
+
vi.mocked(mockContextBuilder.buildFullContext).mockReturnValue({
|
|
76
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
77
|
+
});
|
|
78
|
+
const runPromise = runner.runChecker(mockToolCall, mockInProcessConfig);
|
|
79
|
+
vi.advanceTimersByTime(5001);
|
|
80
|
+
const result = await runPromise;
|
|
81
|
+
expect(result.decision).toBe(SafetyCheckDecision.DENY);
|
|
82
|
+
expect(result.reason).toContain('timed out');
|
|
83
|
+
vi.useRealTimers();
|
|
84
|
+
});
|
|
85
|
+
it('should use minimal context when requested', async () => {
|
|
86
|
+
const configWithContext = {
|
|
87
|
+
...mockInProcessConfig,
|
|
88
|
+
required_context: ['environment'],
|
|
89
|
+
};
|
|
90
|
+
const mockChecker = {
|
|
91
|
+
check: vi.fn().mockResolvedValue({ decision: SafetyCheckDecision.ALLOW }),
|
|
92
|
+
};
|
|
93
|
+
vi.mocked(mockRegistry.resolveInProcess).mockReturnValue(mockChecker);
|
|
94
|
+
vi.mocked(mockContextBuilder.buildMinimalContext).mockReturnValue({
|
|
95
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
96
|
+
});
|
|
97
|
+
await runner.runChecker(mockToolCall, configWithContext);
|
|
98
|
+
expect(mockContextBuilder.buildMinimalContext).toHaveBeenCalledWith([
|
|
99
|
+
'environment',
|
|
100
|
+
]);
|
|
101
|
+
expect(mockContextBuilder.buildFullContext).not.toHaveBeenCalled();
|
|
102
|
+
});
|
|
103
|
+
it('should pass config to in-process checker via toolCall', async () => {
|
|
104
|
+
const mockConfig = { included_args: ['foo'] };
|
|
105
|
+
const configWithConfig = {
|
|
106
|
+
...mockInProcessConfig,
|
|
107
|
+
config: mockConfig,
|
|
108
|
+
};
|
|
109
|
+
const mockResult = {
|
|
110
|
+
decision: SafetyCheckDecision.ALLOW,
|
|
111
|
+
};
|
|
112
|
+
const mockChecker = {
|
|
113
|
+
check: vi.fn().mockResolvedValue(mockResult),
|
|
114
|
+
};
|
|
115
|
+
vi.mocked(mockRegistry.resolveInProcess).mockReturnValue(mockChecker);
|
|
116
|
+
vi.mocked(mockContextBuilder.buildFullContext).mockReturnValue({
|
|
117
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
118
|
+
});
|
|
119
|
+
await runner.runChecker(mockToolCall, configWithConfig);
|
|
120
|
+
expect(mockChecker.check).toHaveBeenCalledWith(expect.objectContaining({
|
|
121
|
+
toolCall: mockToolCall,
|
|
122
|
+
config: mockConfig,
|
|
123
|
+
}));
|
|
124
|
+
});
|
|
125
|
+
describe('External Checkers', () => {
|
|
126
|
+
const mockExternalConfig = {
|
|
127
|
+
type: 'external',
|
|
128
|
+
name: 'python-checker',
|
|
129
|
+
};
|
|
130
|
+
it('should spawn external checker directly', async () => {
|
|
131
|
+
const mockCheckerPath = '/mock/dist/python-checker';
|
|
132
|
+
vi.mocked(mockRegistry.resolveExternal).mockReturnValue(mockCheckerPath);
|
|
133
|
+
vi.mocked(mockContextBuilder.buildFullContext).mockReturnValue({
|
|
134
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
135
|
+
});
|
|
136
|
+
const mockStdout = {
|
|
137
|
+
on: vi.fn().mockImplementation((event, callback) => {
|
|
138
|
+
if (event === 'data') {
|
|
139
|
+
callback(Buffer.from(JSON.stringify({ decision: SafetyCheckDecision.ALLOW })));
|
|
140
|
+
}
|
|
141
|
+
}),
|
|
142
|
+
};
|
|
143
|
+
const mockChildProcess = {
|
|
144
|
+
stdin: { write: vi.fn(), end: vi.fn() },
|
|
145
|
+
stdout: mockStdout,
|
|
146
|
+
stderr: { on: vi.fn() },
|
|
147
|
+
on: vi.fn().mockImplementation((event, callback) => {
|
|
148
|
+
if (event === 'close') {
|
|
149
|
+
// Defer the close callback slightly to allow stdout 'data' to be registered
|
|
150
|
+
setTimeout(() => callback(0), 0);
|
|
151
|
+
}
|
|
152
|
+
}),
|
|
153
|
+
kill: vi.fn(),
|
|
154
|
+
};
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
+
vi.mocked(spawn).mockReturnValue(mockChildProcess);
|
|
157
|
+
const result = await runner.runChecker(mockToolCall, mockExternalConfig);
|
|
158
|
+
expect(result.decision).toBe(SafetyCheckDecision.ALLOW);
|
|
159
|
+
expect(spawn).toHaveBeenCalledWith(mockCheckerPath, [], expect.anything());
|
|
160
|
+
});
|
|
161
|
+
it('should include checker name in timeout error message', async () => {
|
|
162
|
+
vi.useFakeTimers();
|
|
163
|
+
const mockCheckerPath = '/mock/dist/python-checker';
|
|
164
|
+
vi.mocked(mockRegistry.resolveExternal).mockReturnValue(mockCheckerPath);
|
|
165
|
+
vi.mocked(mockContextBuilder.buildFullContext).mockReturnValue({
|
|
166
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
167
|
+
});
|
|
168
|
+
const mockChildProcess = {
|
|
169
|
+
stdin: { write: vi.fn(), end: vi.fn() },
|
|
170
|
+
stdout: { on: vi.fn() },
|
|
171
|
+
stderr: { on: vi.fn() },
|
|
172
|
+
on: vi.fn(), // Never calls 'close'
|
|
173
|
+
kill: vi.fn(),
|
|
174
|
+
};
|
|
175
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
176
|
+
vi.mocked(spawn).mockReturnValue(mockChildProcess);
|
|
177
|
+
const runPromise = runner.runChecker(mockToolCall, mockExternalConfig);
|
|
178
|
+
vi.advanceTimersByTime(5001);
|
|
179
|
+
const result = await runPromise;
|
|
180
|
+
expect(result.decision).toBe(SafetyCheckDecision.DENY);
|
|
181
|
+
expect(result.reason).toContain('Safety checker "python-checker" timed out');
|
|
182
|
+
vi.useRealTimers();
|
|
183
|
+
});
|
|
184
|
+
it('should send SIGKILL if process ignores SIGTERM', async () => {
|
|
185
|
+
vi.useFakeTimers();
|
|
186
|
+
const mockCheckerPath = '/mock/dist/python-checker';
|
|
187
|
+
vi.mocked(mockRegistry.resolveExternal).mockReturnValue(mockCheckerPath);
|
|
188
|
+
vi.mocked(mockContextBuilder.buildFullContext).mockReturnValue({
|
|
189
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
190
|
+
});
|
|
191
|
+
const mockChildProcess = {
|
|
192
|
+
stdin: { write: vi.fn(), end: vi.fn() },
|
|
193
|
+
stdout: { on: vi.fn() },
|
|
194
|
+
stderr: { on: vi.fn() },
|
|
195
|
+
on: vi.fn(), // Never calls 'close' automatically
|
|
196
|
+
kill: vi.fn(),
|
|
197
|
+
};
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
199
|
+
vi.mocked(spawn).mockReturnValue(mockChildProcess);
|
|
200
|
+
const runPromise = runner.runChecker(mockToolCall, mockExternalConfig);
|
|
201
|
+
// Trigger main timeout
|
|
202
|
+
vi.advanceTimersByTime(5001);
|
|
203
|
+
// Should have sent SIGTERM
|
|
204
|
+
expect(mockChildProcess.kill).toHaveBeenCalledWith('SIGTERM');
|
|
205
|
+
// Advance past cleanup timeout (5000ms)
|
|
206
|
+
vi.advanceTimersByTime(5000);
|
|
207
|
+
// Should have sent SIGKILL
|
|
208
|
+
expect(mockChildProcess.kill).toHaveBeenCalledWith('SIGKILL');
|
|
209
|
+
// Clean up promise
|
|
210
|
+
await runPromise;
|
|
211
|
+
vi.useRealTimers();
|
|
212
|
+
});
|
|
213
|
+
it('should include checker name in non-zero exit code error message', async () => {
|
|
214
|
+
const mockCheckerPath = '/mock/dist/python-checker';
|
|
215
|
+
vi.mocked(mockRegistry.resolveExternal).mockReturnValue(mockCheckerPath);
|
|
216
|
+
vi.mocked(mockContextBuilder.buildFullContext).mockReturnValue({
|
|
217
|
+
environment: { cwd: '/tmp', workspaces: [] },
|
|
218
|
+
});
|
|
219
|
+
const mockChildProcess = {
|
|
220
|
+
stdin: { write: vi.fn(), end: vi.fn() },
|
|
221
|
+
stdout: { on: vi.fn() },
|
|
222
|
+
stderr: { on: vi.fn() },
|
|
223
|
+
on: vi.fn().mockImplementation((event, callback) => {
|
|
224
|
+
if (event === 'close') {
|
|
225
|
+
callback(1); // Exit code 1
|
|
226
|
+
}
|
|
227
|
+
}),
|
|
228
|
+
kill: vi.fn(),
|
|
229
|
+
};
|
|
230
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
231
|
+
vi.mocked(spawn).mockReturnValue(mockChildProcess);
|
|
232
|
+
const result = await runner.runChecker(mockToolCall, mockExternalConfig);
|
|
233
|
+
expect(result.decision).toBe(SafetyCheckDecision.DENY);
|
|
234
|
+
expect(result.reason).toContain('Safety checker "python-checker" exited with code 1');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
//# sourceMappingURL=checker-runner.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checker-runner.test.js","sourceRoot":"","sources":["../../../src/safety/checker-runner.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { SafetyCheckInput, ConversationTurn } from './protocol.js';
|
|
7
|
+
import type { Config } from '../config/config.js';
|
|
8
|
+
/**
|
|
9
|
+
* Builds context objects for safety checkers, ensuring sensitive data is filtered.
|
|
10
|
+
*/
|
|
11
|
+
export declare class ContextBuilder {
|
|
12
|
+
private readonly config;
|
|
13
|
+
private readonly conversationHistory;
|
|
14
|
+
constructor(config: Config, conversationHistory?: ConversationTurn[]);
|
|
15
|
+
/**
|
|
16
|
+
* Builds the full context object with all available data.
|
|
17
|
+
*/
|
|
18
|
+
buildFullContext(): SafetyCheckInput['context'];
|
|
19
|
+
/**
|
|
20
|
+
* Builds a minimal context with only the specified keys.
|
|
21
|
+
*/
|
|
22
|
+
buildMinimalContext(requiredKeys: Array<keyof SafetyCheckInput['context']>): SafetyCheckInput['context'];
|
|
23
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Builds context objects for safety checkers, ensuring sensitive data is filtered.
|
|
8
|
+
*/
|
|
9
|
+
export class ContextBuilder {
|
|
10
|
+
config;
|
|
11
|
+
conversationHistory;
|
|
12
|
+
constructor(config, conversationHistory = []) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.conversationHistory = conversationHistory;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builds the full context object with all available data.
|
|
18
|
+
*/
|
|
19
|
+
buildFullContext() {
|
|
20
|
+
return {
|
|
21
|
+
environment: {
|
|
22
|
+
cwd: process.cwd(),
|
|
23
|
+
workspaces: this.config
|
|
24
|
+
.getWorkspaceContext()
|
|
25
|
+
.getDirectories(),
|
|
26
|
+
},
|
|
27
|
+
history: {
|
|
28
|
+
turns: this.conversationHistory,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Builds a minimal context with only the specified keys.
|
|
34
|
+
*/
|
|
35
|
+
buildMinimalContext(requiredKeys) {
|
|
36
|
+
const fullContext = this.buildFullContext();
|
|
37
|
+
const minimalContext = {};
|
|
38
|
+
for (const key of requiredKeys) {
|
|
39
|
+
if (key in fullContext) {
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
minimalContext[key] = fullContext[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return minimalContext;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=context-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-builder.js","sourceRoot":"","sources":["../../../src/safety/context-builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;GAEG;AACH,MAAM,OAAO,cAAc;IAEN;IACA;IAFnB,YACmB,MAAc,EACd,sBAA0C,EAAE;QAD5C,WAAM,GAAN,MAAM,CAAQ;QACd,wBAAmB,GAAnB,mBAAmB,CAAyB;IAC5D,CAAC;IAEJ;;OAEG;IACH,gBAAgB;QACd,OAAO;YACL,WAAW,EAAE;gBACX,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;gBAClB,UAAU,EAAE,IAAI,CAAC,MAAM;qBACpB,mBAAmB,EAAE;qBACrB,cAAc,EAAc;aAChC;YACD,OAAO,EAAE;gBACP,KAAK,EAAE,IAAI,CAAC,mBAAmB;aAChC;SACF,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,mBAAmB,CACjB,YAAsD;QAEtD,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC5C,MAAM,cAAc,GAAyC,EAAE,CAAC;QAEhE,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;YAC/B,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;gBACvB,8DAA8D;gBAC7D,cAAsB,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;QAED,OAAO,cAA6C,CAAC;IACvD,CAAC;CACF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
+
import { ContextBuilder } from './context-builder.js';
|
|
8
|
+
describe('ContextBuilder', () => {
|
|
9
|
+
let contextBuilder;
|
|
10
|
+
let mockConfig;
|
|
11
|
+
const mockHistory = [
|
|
12
|
+
{ user: { text: 'hello' }, model: { text: 'hi' } },
|
|
13
|
+
];
|
|
14
|
+
const mockCwd = '/home/user/project';
|
|
15
|
+
const mockWorkspaces = ['/home/user/project'];
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
vi.spyOn(process, 'cwd').mockReturnValue(mockCwd);
|
|
18
|
+
mockConfig = {
|
|
19
|
+
getWorkspaceContext: vi.fn().mockReturnValue({
|
|
20
|
+
getDirectories: vi.fn().mockReturnValue(mockWorkspaces),
|
|
21
|
+
}),
|
|
22
|
+
apiKey: 'secret-api-key',
|
|
23
|
+
somePublicConfig: 'public-value',
|
|
24
|
+
nested: {
|
|
25
|
+
secretToken: 'hidden',
|
|
26
|
+
public: 'visible',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
contextBuilder = new ContextBuilder(mockConfig, mockHistory);
|
|
30
|
+
});
|
|
31
|
+
it('should build full context with all fields', () => {
|
|
32
|
+
const context = contextBuilder.buildFullContext();
|
|
33
|
+
expect(context.environment.cwd).toBe(mockCwd);
|
|
34
|
+
expect(context.environment.workspaces).toEqual(mockWorkspaces);
|
|
35
|
+
expect(context.history?.turns).toEqual(mockHistory);
|
|
36
|
+
});
|
|
37
|
+
it('should build minimal context with only required keys', () => {
|
|
38
|
+
const context = contextBuilder.buildMinimalContext(['environment']);
|
|
39
|
+
expect(context).toHaveProperty('environment');
|
|
40
|
+
expect(context).not.toHaveProperty('config');
|
|
41
|
+
expect(context).not.toHaveProperty('history');
|
|
42
|
+
});
|
|
43
|
+
it('should handle missing history', () => {
|
|
44
|
+
contextBuilder = new ContextBuilder(mockConfig);
|
|
45
|
+
const context = contextBuilder.buildFullContext();
|
|
46
|
+
expect(context.history?.turns).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=context-builder.test.js.map
|