@diff-review-system/drs 4.0.0-rc.4 → 4.0.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.
- package/.pi/agents/describe/pr-describer.md +14 -0
- package/.pi/agents/review/unified-reviewer.md +31 -1
- package/.pi/agents/task/agents-md-updater.md +3 -1
- package/.pi/agents/task/review-issue-fixer.md +18 -1
- package/.pi/agents/visual/pr-explainer.md +205 -0
- package/.pi/workflows/github-pr-describe.yaml +10 -7
- package/.pi/workflows/github-pr-fix-review-issues-stacked.yaml +148 -0
- package/.pi/workflows/github-pr-post-comment.yaml +10 -10
- package/.pi/workflows/github-pr-review-post.yaml +19 -8
- package/.pi/workflows/github-pr-review.yaml +348 -7
- package/.pi/workflows/github-pr-show-changes.yaml +8 -8
- package/.pi/workflows/github-pr-update-agents-md-stacked.yaml +103 -0
- package/.pi/workflows/github-pr-visual-explain.yaml +35 -0
- package/.pi/workflows/gitlab-mr-describe.yaml +8 -5
- package/.pi/workflows/gitlab-mr-fix-review-issues-stacked.yaml +144 -0
- package/.pi/workflows/gitlab-mr-post-comment.yaml +8 -8
- package/.pi/workflows/gitlab-mr-review.yaml +348 -5
- package/.pi/workflows/gitlab-mr-show-changes.yaml +6 -6
- package/.pi/workflows/gitlab-mr-update-agents-md-stacked.yaml +100 -0
- package/.pi/workflows/gitlab-mr-visual-explain.yaml +33 -0
- package/.pi/workflows/local-fix-review-issues.yaml +82 -13
- package/.pi/workflows/local-review.yaml +9 -2
- package/.pi/workflows/local-update-agents-md.yaml +1 -1
- package/.pi/workflows/local-visual-explain.yaml +31 -0
- package/.pi/workflows/release-changelog-finalize.yaml +47 -0
- package/.pi/workflows/tag-changelog-update.yaml +4 -4
- package/README.md +91 -27
- package/dist/ci/runner.d.ts.map +1 -1
- package/dist/ci/runner.js +3 -1
- package/dist/ci/runner.js.map +1 -1
- package/dist/cli/index.js +48 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/run-agent.d.ts +2 -0
- package/dist/cli/run-agent.d.ts.map +1 -1
- package/dist/cli/run-agent.js +4 -0
- package/dist/cli/run-agent.js.map +1 -1
- package/dist/cli/workflow.d.ts +56 -2
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +2165 -85
- package/dist/cli/workflow.js.map +1 -1
- package/dist/github/client.d.ts +12 -0
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +27 -0
- package/dist/github/client.js.map +1 -1
- package/dist/github/platform-adapter.d.ts +6 -1
- package/dist/github/platform-adapter.d.ts.map +1 -1
- package/dist/github/platform-adapter.js +84 -8
- package/dist/github/platform-adapter.js.map +1 -1
- package/dist/gitlab/client.d.ts +11 -0
- package/dist/gitlab/client.d.ts.map +1 -1
- package/dist/gitlab/client.js +11 -0
- package/dist/gitlab/client.js.map +1 -1
- package/dist/gitlab/platform-adapter.d.ts +3 -1
- package/dist/gitlab/platform-adapter.d.ts.map +1 -1
- package/dist/gitlab/platform-adapter.js +32 -1
- package/dist/gitlab/platform-adapter.js.map +1 -1
- package/dist/lib/comment-formatter.d.ts +8 -0
- package/dist/lib/comment-formatter.d.ts.map +1 -1
- package/dist/lib/comment-formatter.js +12 -4
- package/dist/lib/comment-formatter.js.map +1 -1
- package/dist/lib/comment-poster.d.ts.map +1 -1
- package/dist/lib/comment-poster.js +28 -1
- package/dist/lib/comment-poster.js.map +1 -1
- package/dist/lib/config.d.ts +50 -11
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +163 -28
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/context-compression.d.ts +10 -0
- package/dist/lib/context-compression.d.ts.map +1 -1
- package/dist/lib/context-compression.js +101 -13
- package/dist/lib/context-compression.js.map +1 -1
- package/dist/lib/context-loader.d.ts +2 -1
- package/dist/lib/context-loader.d.ts.map +1 -1
- package/dist/lib/context-loader.js +70 -1
- package/dist/lib/context-loader.js.map +1 -1
- package/dist/lib/describe-core.d.ts.map +1 -1
- package/dist/lib/describe-core.js +3 -2
- package/dist/lib/describe-core.js.map +1 -1
- package/dist/lib/diff-lines.d.ts +9 -0
- package/dist/lib/diff-lines.d.ts.map +1 -1
- package/dist/lib/diff-lines.js +17 -9
- package/dist/lib/diff-lines.js.map +1 -1
- package/dist/lib/exit.js +4 -4
- package/dist/lib/exit.js.map +1 -1
- package/dist/lib/html-artifact.d.ts +14 -0
- package/dist/lib/html-artifact.d.ts.map +1 -0
- package/dist/lib/html-artifact.js +59 -0
- package/dist/lib/html-artifact.js.map +1 -0
- package/dist/lib/issue-parser.js +3 -3
- package/dist/lib/issue-parser.js.map +1 -1
- package/dist/lib/json-output-schema.d.ts +70 -0
- package/dist/lib/json-output-schema.d.ts.map +1 -1
- package/dist/lib/json-output-schema.js +40 -0
- package/dist/lib/json-output-schema.js.map +1 -1
- package/dist/lib/platform-client.d.ts +26 -0
- package/dist/lib/platform-client.d.ts.map +1 -1
- package/dist/lib/review-artifact.d.ts +69 -0
- package/dist/lib/review-artifact.d.ts.map +1 -0
- package/dist/lib/review-artifact.js +171 -0
- package/dist/lib/review-artifact.js.map +1 -0
- package/dist/lib/review-core.d.ts +6 -4
- package/dist/lib/review-core.d.ts.map +1 -1
- package/dist/lib/review-core.js +71 -151
- package/dist/lib/review-core.js.map +1 -1
- package/dist/lib/review-orchestrator.d.ts +23 -0
- package/dist/lib/review-orchestrator.d.ts.map +1 -1
- package/dist/lib/review-orchestrator.js +20 -13
- package/dist/lib/review-orchestrator.js.map +1 -1
- package/dist/lib/review-usage.d.ts +4 -0
- package/dist/lib/review-usage.d.ts.map +1 -1
- package/dist/lib/review-usage.js +25 -0
- package/dist/lib/review-usage.js.map +1 -1
- package/dist/lib/trace-collector.d.ts +105 -0
- package/dist/lib/trace-collector.d.ts.map +1 -0
- package/dist/lib/trace-collector.js +255 -0
- package/dist/lib/trace-collector.js.map +1 -0
- package/dist/lib/trace-html.d.ts +3 -0
- package/dist/lib/trace-html.d.ts.map +1 -0
- package/dist/lib/trace-html.js +349 -0
- package/dist/lib/trace-html.js.map +1 -0
- package/dist/lib/workflow-artifacts.d.ts +54 -0
- package/dist/lib/workflow-artifacts.d.ts.map +1 -0
- package/dist/lib/workflow-artifacts.js +150 -0
- package/dist/lib/workflow-artifacts.js.map +1 -0
- package/dist/pi/sdk.d.ts.map +1 -1
- package/dist/pi/sdk.js +570 -6
- package/dist/pi/sdk.js.map +1 -1
- package/dist/runtime/agent-loader.js +2 -2
- package/dist/runtime/client.d.ts +2 -0
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +11 -5
- package/dist/runtime/client.js.map +1 -1
- package/package.json +21 -15
- package/.pi/workflows/github-pr-describe-post.yaml +0 -24
- package/.pi/workflows/gitlab-mr-describe-post.yaml +0 -22
- package/.pi/workflows/gitlab-mr-review-code-quality.yaml +0 -31
- package/.pi/workflows/gitlab-mr-review-post-code-quality.yaml +0 -40
- package/.pi/workflows/gitlab-mr-review-post.yaml +0 -30
- package/.pi/workflows/local-staged-review.yaml +0 -17
- package/dist/cli/run-agent.test.d.ts +0 -2
- package/dist/cli/run-agent.test.d.ts.map +0 -1
- package/dist/cli/run-agent.test.js +0 -204
- package/dist/cli/run-agent.test.js.map +0 -1
- package/dist/cli/workflow.test.d.ts +0 -2
- package/dist/cli/workflow.test.d.ts.map +0 -1
- package/dist/cli/workflow.test.js +0 -1410
- package/dist/cli/workflow.test.js.map +0 -1
- package/dist/github/client.test.d.ts +0 -2
- package/dist/github/client.test.d.ts.map +0 -1
- package/dist/github/client.test.js +0 -206
- package/dist/github/client.test.js.map +0 -1
- package/dist/github/platform-adapter.test.d.ts +0 -2
- package/dist/github/platform-adapter.test.d.ts.map +0 -1
- package/dist/github/platform-adapter.test.js +0 -40
- package/dist/github/platform-adapter.test.js.map +0 -1
- package/dist/gitlab/diff-parser.test.d.ts +0 -2
- package/dist/gitlab/diff-parser.test.d.ts.map +0 -1
- package/dist/gitlab/diff-parser.test.js +0 -315
- package/dist/gitlab/diff-parser.test.js.map +0 -1
- package/dist/gitlab/platform-adapter.test.d.ts +0 -2
- package/dist/gitlab/platform-adapter.test.d.ts.map +0 -1
- package/dist/gitlab/platform-adapter.test.js +0 -21
- package/dist/gitlab/platform-adapter.test.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -7
- package/dist/index.test.js.map +0 -1
- package/dist/lib/code-quality-report.test.d.ts +0 -2
- package/dist/lib/code-quality-report.test.d.ts.map +0 -1
- package/dist/lib/code-quality-report.test.js +0 -327
- package/dist/lib/code-quality-report.test.js.map +0 -1
- package/dist/lib/comment-formatter.test.d.ts +0 -2
- package/dist/lib/comment-formatter.test.d.ts.map +0 -1
- package/dist/lib/comment-formatter.test.js +0 -727
- package/dist/lib/comment-formatter.test.js.map +0 -1
- package/dist/lib/comment-manager.test.d.ts +0 -2
- package/dist/lib/comment-manager.test.d.ts.map +0 -1
- package/dist/lib/comment-manager.test.js +0 -680
- package/dist/lib/comment-manager.test.js.map +0 -1
- package/dist/lib/comment-poster.test.d.ts +0 -5
- package/dist/lib/comment-poster.test.d.ts.map +0 -1
- package/dist/lib/comment-poster.test.js +0 -255
- package/dist/lib/comment-poster.test.js.map +0 -1
- package/dist/lib/config-model-overrides.test.d.ts +0 -2
- package/dist/lib/config-model-overrides.test.d.ts.map +0 -1
- package/dist/lib/config-model-overrides.test.js +0 -218
- package/dist/lib/config-model-overrides.test.js.map +0 -1
- package/dist/lib/config.test.d.ts +0 -2
- package/dist/lib/config.test.d.ts.map +0 -1
- package/dist/lib/config.test.js +0 -353
- package/dist/lib/config.test.js.map +0 -1
- package/dist/lib/context-compression.test.d.ts +0 -2
- package/dist/lib/context-compression.test.d.ts.map +0 -1
- package/dist/lib/context-compression.test.js +0 -337
- package/dist/lib/context-compression.test.js.map +0 -1
- package/dist/lib/context-loader.test.d.ts +0 -2
- package/dist/lib/context-loader.test.d.ts.map +0 -1
- package/dist/lib/context-loader.test.js +0 -212
- package/dist/lib/context-loader.test.js.map +0 -1
- package/dist/lib/cursor-fix-link.test.d.ts +0 -2
- package/dist/lib/cursor-fix-link.test.d.ts.map +0 -1
- package/dist/lib/cursor-fix-link.test.js +0 -70
- package/dist/lib/cursor-fix-link.test.js.map +0 -1
- package/dist/lib/describe-core.test.d.ts +0 -2
- package/dist/lib/describe-core.test.d.ts.map +0 -1
- package/dist/lib/describe-core.test.js +0 -208
- package/dist/lib/describe-core.test.js.map +0 -1
- package/dist/lib/describe-output-path.test.d.ts +0 -2
- package/dist/lib/describe-output-path.test.d.ts.map +0 -1
- package/dist/lib/describe-output-path.test.js +0 -51
- package/dist/lib/describe-output-path.test.js.map +0 -1
- package/dist/lib/describe-parser.test.d.ts +0 -2
- package/dist/lib/describe-parser.test.d.ts.map +0 -1
- package/dist/lib/describe-parser.test.js +0 -282
- package/dist/lib/describe-parser.test.js.map +0 -1
- package/dist/lib/description-executor.test.d.ts +0 -2
- package/dist/lib/description-executor.test.d.ts.map +0 -1
- package/dist/lib/description-executor.test.js +0 -135
- package/dist/lib/description-executor.test.js.map +0 -1
- package/dist/lib/description-formatter.test.d.ts +0 -2
- package/dist/lib/description-formatter.test.d.ts.map +0 -1
- package/dist/lib/description-formatter.test.js +0 -57
- package/dist/lib/description-formatter.test.js.map +0 -1
- package/dist/lib/diff-lines.test.d.ts +0 -2
- package/dist/lib/diff-lines.test.d.ts.map +0 -1
- package/dist/lib/diff-lines.test.js +0 -13
- package/dist/lib/diff-lines.test.js.map +0 -1
- package/dist/lib/diff-parser.test.d.ts +0 -2
- package/dist/lib/diff-parser.test.d.ts.map +0 -1
- package/dist/lib/diff-parser.test.js +0 -335
- package/dist/lib/diff-parser.test.js.map +0 -1
- package/dist/lib/error-comment-poster.test.d.ts +0 -2
- package/dist/lib/error-comment-poster.test.d.ts.map +0 -1
- package/dist/lib/error-comment-poster.test.js +0 -128
- package/dist/lib/error-comment-poster.test.js.map +0 -1
- package/dist/lib/exit.test.d.ts +0 -2
- package/dist/lib/exit.test.d.ts.map +0 -1
- package/dist/lib/exit.test.js +0 -120
- package/dist/lib/exit.test.js.map +0 -1
- package/dist/lib/issue-parser.test.d.ts +0 -2
- package/dist/lib/issue-parser.test.d.ts.map +0 -1
- package/dist/lib/issue-parser.test.js +0 -281
- package/dist/lib/issue-parser.test.js.map +0 -1
- package/dist/lib/json-output-schema.test.d.ts +0 -2
- package/dist/lib/json-output-schema.test.d.ts.map +0 -1
- package/dist/lib/json-output-schema.test.js +0 -92
- package/dist/lib/json-output-schema.test.js.map +0 -1
- package/dist/lib/json-output.test.d.ts +0 -2
- package/dist/lib/json-output.test.d.ts.map +0 -1
- package/dist/lib/json-output.test.js +0 -141
- package/dist/lib/json-output.test.js.map +0 -1
- package/dist/lib/logger.test.d.ts +0 -2
- package/dist/lib/logger.test.d.ts.map +0 -1
- package/dist/lib/logger.test.js +0 -324
- package/dist/lib/logger.test.js.map +0 -1
- package/dist/lib/position-validator.test.d.ts +0 -2
- package/dist/lib/position-validator.test.d.ts.map +0 -1
- package/dist/lib/position-validator.test.js +0 -128
- package/dist/lib/position-validator.test.js.map +0 -1
- package/dist/lib/prompt-budget.test.d.ts +0 -2
- package/dist/lib/prompt-budget.test.d.ts.map +0 -1
- package/dist/lib/prompt-budget.test.js +0 -55
- package/dist/lib/prompt-budget.test.js.map +0 -1
- package/dist/lib/repository-validator.test.d.ts +0 -5
- package/dist/lib/repository-validator.test.d.ts.map +0 -1
- package/dist/lib/repository-validator.test.js +0 -341
- package/dist/lib/repository-validator.test.js.map +0 -1
- package/dist/lib/review-core.test.d.ts +0 -2
- package/dist/lib/review-core.test.d.ts.map +0 -1
- package/dist/lib/review-core.test.js +0 -614
- package/dist/lib/review-core.test.js.map +0 -1
- package/dist/lib/review-orchestrator.test.d.ts +0 -2
- package/dist/lib/review-orchestrator.test.d.ts.map +0 -1
- package/dist/lib/review-orchestrator.test.js +0 -552
- package/dist/lib/review-orchestrator.test.js.map +0 -1
- package/dist/lib/review-output-path.test.d.ts +0 -2
- package/dist/lib/review-output-path.test.d.ts.map +0 -1
- package/dist/lib/review-output-path.test.js +0 -83
- package/dist/lib/review-output-path.test.js.map +0 -1
- package/dist/lib/review-parser.test.d.ts +0 -2
- package/dist/lib/review-parser.test.d.ts.map +0 -1
- package/dist/lib/review-parser.test.js +0 -130
- package/dist/lib/review-parser.test.js.map +0 -1
- package/dist/lib/review-usage.test.d.ts +0 -2
- package/dist/lib/review-usage.test.d.ts.map +0 -1
- package/dist/lib/review-usage.test.js +0 -83
- package/dist/lib/review-usage.test.js.map +0 -1
- package/dist/lib/unified-review-executor.d.ts +0 -58
- package/dist/lib/unified-review-executor.d.ts.map +0 -1
- package/dist/lib/unified-review-executor.js +0 -201
- package/dist/lib/unified-review-executor.js.map +0 -1
- package/dist/lib/unified-review-executor.test.d.ts +0 -5
- package/dist/lib/unified-review-executor.test.d.ts.map +0 -1
- package/dist/lib/unified-review-executor.test.js +0 -472
- package/dist/lib/unified-review-executor.test.js.map +0 -1
- package/dist/lib/write-json-output.test.d.ts +0 -2
- package/dist/lib/write-json-output.test.d.ts.map +0 -1
- package/dist/lib/write-json-output.test.js +0 -259
- package/dist/lib/write-json-output.test.js.map +0 -1
- package/dist/pi/sdk.test.d.ts +0 -2
- package/dist/pi/sdk.test.d.ts.map +0 -1
- package/dist/pi/sdk.test.js +0 -488
- package/dist/pi/sdk.test.js.map +0 -1
- package/dist/runtime/agent-loader.test.d.ts +0 -2
- package/dist/runtime/agent-loader.test.d.ts.map +0 -1
- package/dist/runtime/agent-loader.test.js +0 -277
- package/dist/runtime/agent-loader.test.js.map +0 -1
- package/dist/runtime/client.test.d.ts +0 -2
- package/dist/runtime/client.test.d.ts.map +0 -1
- package/dist/runtime/client.test.js +0 -772
- package/dist/runtime/client.test.js.map +0 -1
- package/dist/runtime/path-config.test.d.ts +0 -2
- package/dist/runtime/path-config.test.d.ts.map +0 -1
- package/dist/runtime/path-config.test.js +0 -112
- package/dist/runtime/path-config.test.js.map +0 -1
|
@@ -1,772 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { RuntimeClient, createRuntimeClient, createRuntimeClientInstance } from './client.js';
|
|
3
|
-
function createRuntime(sessionOverrides = {}) {
|
|
4
|
-
return {
|
|
5
|
-
server: {
|
|
6
|
-
url: 'pi://in-process',
|
|
7
|
-
close: vi.fn(),
|
|
8
|
-
},
|
|
9
|
-
client: {
|
|
10
|
-
session: {
|
|
11
|
-
create: vi.fn(async () => ({ data: { id: 'session-123' } })),
|
|
12
|
-
prompt: vi.fn(async () => { }),
|
|
13
|
-
messages: vi.fn(async () => ({ data: [] })),
|
|
14
|
-
delete: vi.fn(async () => { }),
|
|
15
|
-
...sessionOverrides,
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
const mocks = vi.hoisted(() => ({
|
|
21
|
-
createPiInProcessServer: vi.fn(),
|
|
22
|
-
loadAgents: vi.fn(() => []),
|
|
23
|
-
}));
|
|
24
|
-
vi.mock('../pi/sdk.js', () => ({
|
|
25
|
-
createPiInProcessServer: mocks.createPiInProcessServer,
|
|
26
|
-
}));
|
|
27
|
-
vi.mock('./agent-loader.js', () => ({
|
|
28
|
-
loadAgents: mocks.loadAgents,
|
|
29
|
-
}));
|
|
30
|
-
describe('RuntimeClient', () => {
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
vi.clearAllMocks();
|
|
33
|
-
vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
34
|
-
vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
35
|
-
mocks.createPiInProcessServer.mockResolvedValue(createRuntime());
|
|
36
|
-
mocks.loadAgents.mockReturnValue([]);
|
|
37
|
-
});
|
|
38
|
-
describe('constructor', () => {
|
|
39
|
-
it('creates an instance with minimal config', () => {
|
|
40
|
-
const client = new RuntimeClient({
|
|
41
|
-
directory: '/test/dir',
|
|
42
|
-
});
|
|
43
|
-
expect(client).toBeInstanceOf(RuntimeClient);
|
|
44
|
-
});
|
|
45
|
-
it('supports optional model overrides and provider config', () => {
|
|
46
|
-
const client = new RuntimeClient({
|
|
47
|
-
modelOverrides: {
|
|
48
|
-
'review/security': 'anthropic/claude-opus-4-5-20251101',
|
|
49
|
-
},
|
|
50
|
-
provider: {
|
|
51
|
-
custom: {
|
|
52
|
-
baseUrl: 'https://api.custom.example/v1',
|
|
53
|
-
apiKey: 'CUSTOM_API_KEY',
|
|
54
|
-
api: 'openai-completions',
|
|
55
|
-
models: [
|
|
56
|
-
{
|
|
57
|
-
id: 'model',
|
|
58
|
-
name: 'model',
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
expect(client).toBeInstanceOf(RuntimeClient);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
describe('initialize', () => {
|
|
68
|
-
it('wires Pi runtime agent prompts and model overrides', async () => {
|
|
69
|
-
mocks.loadAgents.mockReturnValue([
|
|
70
|
-
{
|
|
71
|
-
id: 'review/security',
|
|
72
|
-
namespace: 'review',
|
|
73
|
-
name: 'review/security',
|
|
74
|
-
path: '/tmp/security.md',
|
|
75
|
-
description: 'Security specialist',
|
|
76
|
-
prompt: 'Security prompt',
|
|
77
|
-
tools: { Read: true },
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
id: 'review/quality',
|
|
81
|
-
namespace: 'review',
|
|
82
|
-
name: 'review/quality',
|
|
83
|
-
path: '/tmp/quality.md',
|
|
84
|
-
description: 'Quality specialist',
|
|
85
|
-
prompt: 'Quality prompt',
|
|
86
|
-
},
|
|
87
|
-
]);
|
|
88
|
-
const client = await createRuntimeClientInstance({
|
|
89
|
-
directory: process.cwd(),
|
|
90
|
-
config: {
|
|
91
|
-
review: {
|
|
92
|
-
agents: ['review/security', 'review/quality'],
|
|
93
|
-
},
|
|
94
|
-
agents: {
|
|
95
|
-
default: {
|
|
96
|
-
skills: [],
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
modelOverrides: {
|
|
101
|
-
'review/security': 'anthropic/claude-security',
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
105
|
-
config: expect.objectContaining({
|
|
106
|
-
agent: expect.objectContaining({
|
|
107
|
-
'review/security': expect.objectContaining({
|
|
108
|
-
prompt: 'Security prompt',
|
|
109
|
-
model: 'anthropic/claude-security',
|
|
110
|
-
tools: { Read: true },
|
|
111
|
-
}),
|
|
112
|
-
'review/quality': expect.objectContaining({
|
|
113
|
-
prompt: 'Quality prompt',
|
|
114
|
-
}),
|
|
115
|
-
}),
|
|
116
|
-
}),
|
|
117
|
-
}));
|
|
118
|
-
await client.shutdown();
|
|
119
|
-
});
|
|
120
|
-
it('passes skill search paths and agent skill configuration to Pi runtime', async () => {
|
|
121
|
-
const projectRoot = process.cwd();
|
|
122
|
-
const config = {
|
|
123
|
-
agents: {
|
|
124
|
-
default: {
|
|
125
|
-
skills: ['baseline-review'],
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
review: {
|
|
129
|
-
agents: [
|
|
130
|
-
{
|
|
131
|
-
name: 'review/security',
|
|
132
|
-
skills: ['security-audit'],
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
mocks.loadAgents.mockReturnValueOnce([
|
|
138
|
-
{
|
|
139
|
-
id: 'review/security',
|
|
140
|
-
namespace: 'review',
|
|
141
|
-
name: 'security',
|
|
142
|
-
path: '/tmp/security.md',
|
|
143
|
-
description: 'Security agent',
|
|
144
|
-
},
|
|
145
|
-
]);
|
|
146
|
-
const client = await createRuntimeClientInstance({
|
|
147
|
-
directory: projectRoot,
|
|
148
|
-
config,
|
|
149
|
-
});
|
|
150
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
151
|
-
config: expect.objectContaining({
|
|
152
|
-
skillSearchPaths: expect.any(Array),
|
|
153
|
-
agentSkills: {
|
|
154
|
-
'review/security': ['baseline-review', 'security-audit'],
|
|
155
|
-
},
|
|
156
|
-
}),
|
|
157
|
-
}));
|
|
158
|
-
await client.shutdown();
|
|
159
|
-
});
|
|
160
|
-
it('includes unified reviewer in skill configuration when configured', async () => {
|
|
161
|
-
const config = {
|
|
162
|
-
agents: {
|
|
163
|
-
default: {
|
|
164
|
-
skills: ['cli-testing'],
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
review: {
|
|
168
|
-
agents: ['review/unified-reviewer'],
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
mocks.loadAgents.mockReturnValueOnce([
|
|
172
|
-
{
|
|
173
|
-
id: 'review/unified-reviewer',
|
|
174
|
-
namespace: 'review',
|
|
175
|
-
name: 'unified-reviewer',
|
|
176
|
-
path: '/tmp/unified-reviewer.md',
|
|
177
|
-
description: 'Unified agent',
|
|
178
|
-
},
|
|
179
|
-
]);
|
|
180
|
-
const client = await createRuntimeClientInstance({
|
|
181
|
-
directory: process.cwd(),
|
|
182
|
-
config,
|
|
183
|
-
});
|
|
184
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
185
|
-
config: expect.objectContaining({
|
|
186
|
-
agentSkills: expect.objectContaining({
|
|
187
|
-
'review/unified-reviewer': ['cli-testing'],
|
|
188
|
-
}),
|
|
189
|
-
}),
|
|
190
|
-
}));
|
|
191
|
-
await client.shutdown();
|
|
192
|
-
});
|
|
193
|
-
it('applies generic default skills to non-review agents', async () => {
|
|
194
|
-
const config = {
|
|
195
|
-
agents: {
|
|
196
|
-
default: {
|
|
197
|
-
skills: ['generic-skill'],
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
review: {
|
|
201
|
-
agents: ['review/security'],
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
mocks.loadAgents.mockReturnValueOnce([
|
|
205
|
-
{
|
|
206
|
-
id: 'review/security',
|
|
207
|
-
namespace: 'review',
|
|
208
|
-
name: 'security',
|
|
209
|
-
path: '/tmp/security.md',
|
|
210
|
-
description: 'Security agent',
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
id: 'describe/pr-describer',
|
|
214
|
-
namespace: 'describe',
|
|
215
|
-
name: 'pr-describer',
|
|
216
|
-
path: '/tmp/pr-describer.md',
|
|
217
|
-
description: 'Description agent',
|
|
218
|
-
},
|
|
219
|
-
]);
|
|
220
|
-
const client = await createRuntimeClientInstance({
|
|
221
|
-
directory: process.cwd(),
|
|
222
|
-
config,
|
|
223
|
-
});
|
|
224
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
225
|
-
config: expect.objectContaining({
|
|
226
|
-
agentSkills: expect.objectContaining({
|
|
227
|
-
'describe/pr-describer': ['generic-skill'],
|
|
228
|
-
}),
|
|
229
|
-
}),
|
|
230
|
-
}));
|
|
231
|
-
await client.shutdown();
|
|
232
|
-
});
|
|
233
|
-
it('applies generic model defaults, namespaces, and overrides to all loaded agents', async () => {
|
|
234
|
-
const config = {
|
|
235
|
-
agents: {
|
|
236
|
-
default: {
|
|
237
|
-
model: 'provider/default-model',
|
|
238
|
-
skills: [],
|
|
239
|
-
},
|
|
240
|
-
namespaces: {
|
|
241
|
-
task: {
|
|
242
|
-
model: 'provider/task-model',
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
overrides: {
|
|
246
|
-
'task/specialist': {
|
|
247
|
-
model: 'provider/specialist-override',
|
|
248
|
-
},
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
review: {
|
|
252
|
-
agents: [],
|
|
253
|
-
},
|
|
254
|
-
};
|
|
255
|
-
mocks.loadAgents.mockReturnValueOnce([
|
|
256
|
-
{
|
|
257
|
-
id: 'task/docs-updater',
|
|
258
|
-
namespace: 'task',
|
|
259
|
-
name: 'docs-updater',
|
|
260
|
-
path: '/tmp/docs-updater.md',
|
|
261
|
-
description: 'Docs updater',
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
id: 'task/specialist',
|
|
265
|
-
namespace: 'task',
|
|
266
|
-
name: 'specialist',
|
|
267
|
-
path: '/tmp/specialist.md',
|
|
268
|
-
description: 'Specialist',
|
|
269
|
-
model: 'provider/frontmatter-model',
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
id: 'ops/deploy',
|
|
273
|
-
namespace: 'ops',
|
|
274
|
-
name: 'deploy',
|
|
275
|
-
path: '/tmp/deploy.md',
|
|
276
|
-
description: 'Deploy helper',
|
|
277
|
-
},
|
|
278
|
-
]);
|
|
279
|
-
const client = await createRuntimeClientInstance({
|
|
280
|
-
directory: process.cwd(),
|
|
281
|
-
config,
|
|
282
|
-
});
|
|
283
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
284
|
-
config: expect.objectContaining({
|
|
285
|
-
agent: expect.objectContaining({
|
|
286
|
-
'task/docs-updater': expect.objectContaining({
|
|
287
|
-
model: 'provider/task-model',
|
|
288
|
-
}),
|
|
289
|
-
'task/specialist': expect.objectContaining({
|
|
290
|
-
model: 'provider/specialist-override',
|
|
291
|
-
}),
|
|
292
|
-
'ops/deploy': expect.objectContaining({
|
|
293
|
-
model: 'provider/default-model',
|
|
294
|
-
}),
|
|
295
|
-
}),
|
|
296
|
-
}),
|
|
297
|
-
}));
|
|
298
|
-
await client.shutdown();
|
|
299
|
-
});
|
|
300
|
-
it('passes per-agent tool overrides to Pi runtime config', async () => {
|
|
301
|
-
mocks.loadAgents.mockReturnValue([
|
|
302
|
-
{
|
|
303
|
-
id: 'review/security',
|
|
304
|
-
namespace: 'review',
|
|
305
|
-
name: 'review/security',
|
|
306
|
-
path: '/tmp/security.md',
|
|
307
|
-
description: 'Security agent',
|
|
308
|
-
prompt: 'Security prompt',
|
|
309
|
-
tools: { Read: true, Bash: false, Edit: true },
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
id: 'review/quality',
|
|
313
|
-
namespace: 'review',
|
|
314
|
-
name: 'review/quality',
|
|
315
|
-
path: '/tmp/quality.md',
|
|
316
|
-
description: 'Quality agent',
|
|
317
|
-
prompt: 'Quality prompt',
|
|
318
|
-
// No tools override — uses global defaults
|
|
319
|
-
},
|
|
320
|
-
]);
|
|
321
|
-
const client = await createRuntimeClientInstance({
|
|
322
|
-
directory: process.cwd(),
|
|
323
|
-
config: {
|
|
324
|
-
review: {
|
|
325
|
-
agents: ['review/security', 'review/quality'],
|
|
326
|
-
},
|
|
327
|
-
agents: {
|
|
328
|
-
default: { skills: [] },
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
333
|
-
config: expect.objectContaining({
|
|
334
|
-
agent: expect.objectContaining({
|
|
335
|
-
'review/security': expect.objectContaining({
|
|
336
|
-
tools: { Read: true, Bash: false, Edit: true },
|
|
337
|
-
}),
|
|
338
|
-
'review/quality': expect.not.objectContaining({
|
|
339
|
-
tools: expect.anything(),
|
|
340
|
-
}),
|
|
341
|
-
}),
|
|
342
|
-
}),
|
|
343
|
-
}));
|
|
344
|
-
await client.shutdown();
|
|
345
|
-
});
|
|
346
|
-
it('passes provider and model headers through to Pi runtime config', async () => {
|
|
347
|
-
const client = await createRuntimeClientInstance({
|
|
348
|
-
directory: process.cwd(),
|
|
349
|
-
provider: {
|
|
350
|
-
custom: {
|
|
351
|
-
baseUrl: 'https://api.custom.example/v1',
|
|
352
|
-
apiKey: 'CUSTOM_API_KEY',
|
|
353
|
-
api: 'openai-completions',
|
|
354
|
-
headers: {
|
|
355
|
-
'X-Provider-Header': 'provider-value',
|
|
356
|
-
},
|
|
357
|
-
models: [
|
|
358
|
-
{
|
|
359
|
-
id: 'custom-model',
|
|
360
|
-
headers: {
|
|
361
|
-
'X-Model-Header': 'model-value',
|
|
362
|
-
},
|
|
363
|
-
},
|
|
364
|
-
],
|
|
365
|
-
},
|
|
366
|
-
},
|
|
367
|
-
});
|
|
368
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
369
|
-
config: expect.objectContaining({
|
|
370
|
-
provider: expect.objectContaining({
|
|
371
|
-
custom: expect.objectContaining({
|
|
372
|
-
headers: {
|
|
373
|
-
'X-Provider-Header': 'provider-value',
|
|
374
|
-
},
|
|
375
|
-
models: [
|
|
376
|
-
expect.objectContaining({
|
|
377
|
-
id: 'custom-model',
|
|
378
|
-
headers: {
|
|
379
|
-
'X-Model-Header': 'model-value',
|
|
380
|
-
},
|
|
381
|
-
}),
|
|
382
|
-
],
|
|
383
|
-
}),
|
|
384
|
-
}),
|
|
385
|
-
}),
|
|
386
|
-
}));
|
|
387
|
-
await client.shutdown();
|
|
388
|
-
});
|
|
389
|
-
it('passes configured provider retry settings to Pi runtime config', async () => {
|
|
390
|
-
const client = await createRuntimeClientInstance({
|
|
391
|
-
directory: process.cwd(),
|
|
392
|
-
providerRetry: {
|
|
393
|
-
timeoutMs: 45000,
|
|
394
|
-
maxRetries: 2,
|
|
395
|
-
maxRetryDelayMs: 15000,
|
|
396
|
-
},
|
|
397
|
-
});
|
|
398
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
399
|
-
config: expect.objectContaining({
|
|
400
|
-
retry: {
|
|
401
|
-
provider: {
|
|
402
|
-
timeoutMs: 45000,
|
|
403
|
-
maxRetries: 2,
|
|
404
|
-
maxRetryDelayMs: 15000,
|
|
405
|
-
},
|
|
406
|
-
},
|
|
407
|
-
}),
|
|
408
|
-
}));
|
|
409
|
-
await client.shutdown();
|
|
410
|
-
});
|
|
411
|
-
});
|
|
412
|
-
describe('createSession', () => {
|
|
413
|
-
it('throws error if not initialized', async () => {
|
|
414
|
-
const client = new RuntimeClient({});
|
|
415
|
-
await expect(client.createSession({
|
|
416
|
-
agent: 'review/security',
|
|
417
|
-
message: 'Review this code',
|
|
418
|
-
})).rejects.toThrow('Runtime client not initialized');
|
|
419
|
-
});
|
|
420
|
-
it('maps authentication errors to actionable messages', async () => {
|
|
421
|
-
mocks.createPiInProcessServer.mockResolvedValueOnce(createRuntime({
|
|
422
|
-
create: vi.fn(async () => {
|
|
423
|
-
throw new Error('401 Unauthorized');
|
|
424
|
-
}),
|
|
425
|
-
}));
|
|
426
|
-
const client = await createRuntimeClientInstance({
|
|
427
|
-
directory: process.cwd(),
|
|
428
|
-
});
|
|
429
|
-
await expect(client.createSession({
|
|
430
|
-
agent: 'review/security',
|
|
431
|
-
message: 'Review this code',
|
|
432
|
-
})).rejects.toThrow('Authentication failed with the configured model provider');
|
|
433
|
-
});
|
|
434
|
-
it('maps model resolution errors to actionable messages', async () => {
|
|
435
|
-
mocks.createPiInProcessServer.mockResolvedValueOnce(createRuntime({
|
|
436
|
-
create: vi.fn(async () => {
|
|
437
|
-
throw new Error('Failed to resolve model "anthropic/does-not-exist"');
|
|
438
|
-
}),
|
|
439
|
-
}));
|
|
440
|
-
const client = await createRuntimeClientInstance({
|
|
441
|
-
directory: process.cwd(),
|
|
442
|
-
});
|
|
443
|
-
await expect(client.createSession({
|
|
444
|
-
agent: 'review/security',
|
|
445
|
-
message: 'Review this code',
|
|
446
|
-
})).rejects.toThrow('Model configuration is invalid or unavailable');
|
|
447
|
-
});
|
|
448
|
-
it('includes local-runtime hint when connectivity errors occur', async () => {
|
|
449
|
-
mocks.createPiInProcessServer.mockResolvedValueOnce(createRuntime({
|
|
450
|
-
create: vi.fn(async () => {
|
|
451
|
-
throw new Error('fetch failed');
|
|
452
|
-
}),
|
|
453
|
-
}));
|
|
454
|
-
const client = await createRuntimeClientInstance({
|
|
455
|
-
directory: process.cwd(),
|
|
456
|
-
});
|
|
457
|
-
await expect(client.createSession({
|
|
458
|
-
agent: 'review/security',
|
|
459
|
-
message: 'Review this code',
|
|
460
|
-
})).rejects.toThrow('Verify local Pi runtime setup and model provider connectivity');
|
|
461
|
-
});
|
|
462
|
-
it('fails fast when session creation hangs', async () => {
|
|
463
|
-
mocks.createPiInProcessServer.mockResolvedValueOnce(createRuntime({
|
|
464
|
-
create: vi.fn(() => new Promise(() => { })),
|
|
465
|
-
}));
|
|
466
|
-
const client = await createRuntimeClientInstance({
|
|
467
|
-
directory: process.cwd(),
|
|
468
|
-
operationTimeoutMs: 25,
|
|
469
|
-
});
|
|
470
|
-
await expect(client.createSession({
|
|
471
|
-
agent: 'review/security',
|
|
472
|
-
message: 'Review this code',
|
|
473
|
-
})).rejects.toThrow('Create session timed out');
|
|
474
|
-
});
|
|
475
|
-
});
|
|
476
|
-
describe('lifecycle and helper methods', () => {
|
|
477
|
-
it('streamMessages throws if not initialized', async () => {
|
|
478
|
-
const client = new RuntimeClient({});
|
|
479
|
-
const generator = client.streamMessages('session-123');
|
|
480
|
-
await expect(generator.next()).rejects.toThrow('Runtime client not initialized');
|
|
481
|
-
});
|
|
482
|
-
it('applies configured model pricing when runtime cost is missing or zero', async () => {
|
|
483
|
-
const runtimeMessages = [
|
|
484
|
-
{
|
|
485
|
-
info: {
|
|
486
|
-
id: 'msg-1',
|
|
487
|
-
role: 'assistant',
|
|
488
|
-
time: { completed: Date.now() },
|
|
489
|
-
provider: 'opencode',
|
|
490
|
-
model: 'glm-5-free',
|
|
491
|
-
usage: {
|
|
492
|
-
input: 1000,
|
|
493
|
-
output: 100,
|
|
494
|
-
cacheRead: 0,
|
|
495
|
-
cacheWrite: 0,
|
|
496
|
-
totalTokens: 1100,
|
|
497
|
-
},
|
|
498
|
-
},
|
|
499
|
-
parts: [{ text: 'done' }],
|
|
500
|
-
},
|
|
501
|
-
];
|
|
502
|
-
mocks.createPiInProcessServer.mockResolvedValueOnce(createRuntime({
|
|
503
|
-
messages: vi.fn(async () => ({ data: runtimeMessages })),
|
|
504
|
-
}));
|
|
505
|
-
const client = await createRuntimeClientInstance({
|
|
506
|
-
directory: process.cwd(),
|
|
507
|
-
config: {
|
|
508
|
-
review: {
|
|
509
|
-
agents: [],
|
|
510
|
-
},
|
|
511
|
-
agents: {
|
|
512
|
-
default: {
|
|
513
|
-
skills: [],
|
|
514
|
-
},
|
|
515
|
-
},
|
|
516
|
-
pricing: {
|
|
517
|
-
models: {
|
|
518
|
-
'opencode/glm-5-free': {
|
|
519
|
-
input: 2,
|
|
520
|
-
output: 8,
|
|
521
|
-
},
|
|
522
|
-
},
|
|
523
|
-
},
|
|
524
|
-
},
|
|
525
|
-
});
|
|
526
|
-
const collected = [];
|
|
527
|
-
for await (const message of client.streamMessages('session-123')) {
|
|
528
|
-
collected.push(message);
|
|
529
|
-
}
|
|
530
|
-
expect(collected).toHaveLength(1);
|
|
531
|
-
expect(collected[0].usage?.cost).toBeCloseTo(0.0028, 10);
|
|
532
|
-
await client.shutdown();
|
|
533
|
-
});
|
|
534
|
-
it('fails fast when message polling exceeds stream timeout', async () => {
|
|
535
|
-
const runtimeMessages = [
|
|
536
|
-
{
|
|
537
|
-
info: {
|
|
538
|
-
id: 'msg-1',
|
|
539
|
-
role: 'assistant',
|
|
540
|
-
},
|
|
541
|
-
parts: [{ text: 'still running' }],
|
|
542
|
-
},
|
|
543
|
-
];
|
|
544
|
-
mocks.createPiInProcessServer.mockResolvedValueOnce(createRuntime({
|
|
545
|
-
messages: vi.fn(async () => ({ data: runtimeMessages })),
|
|
546
|
-
}));
|
|
547
|
-
const client = await createRuntimeClientInstance({
|
|
548
|
-
directory: process.cwd(),
|
|
549
|
-
streamTimeoutMs: 30,
|
|
550
|
-
streamPollIntervalMs: 10,
|
|
551
|
-
});
|
|
552
|
-
const collect = async () => {
|
|
553
|
-
const collected = [];
|
|
554
|
-
for await (const message of client.streamMessages('session-123')) {
|
|
555
|
-
collected.push(message);
|
|
556
|
-
}
|
|
557
|
-
return collected;
|
|
558
|
-
};
|
|
559
|
-
await expect(collect()).rejects.toThrow('Session session-123 timed out');
|
|
560
|
-
await client.shutdown();
|
|
561
|
-
});
|
|
562
|
-
it('prefers env timeout overrides over constructor values', async () => {
|
|
563
|
-
const previousOpTimeout = process.env.DRS_RUNTIME_OPERATION_TIMEOUT_MS;
|
|
564
|
-
const previousStreamTimeout = process.env.DRS_RUNTIME_STREAM_TIMEOUT_MS;
|
|
565
|
-
const previousPollInterval = process.env.DRS_RUNTIME_STREAM_POLL_INTERVAL_MS;
|
|
566
|
-
process.env.DRS_RUNTIME_OPERATION_TIMEOUT_MS = '10';
|
|
567
|
-
process.env.DRS_RUNTIME_STREAM_TIMEOUT_MS = '30';
|
|
568
|
-
process.env.DRS_RUNTIME_STREAM_POLL_INTERVAL_MS = '10';
|
|
569
|
-
try {
|
|
570
|
-
mocks.createPiInProcessServer.mockResolvedValueOnce(createRuntime({
|
|
571
|
-
create: vi.fn(() => new Promise(() => { })),
|
|
572
|
-
}));
|
|
573
|
-
const client = await createRuntimeClientInstance({
|
|
574
|
-
directory: process.cwd(),
|
|
575
|
-
operationTimeoutMs: 1000,
|
|
576
|
-
streamTimeoutMs: 1000,
|
|
577
|
-
streamPollIntervalMs: 1000,
|
|
578
|
-
});
|
|
579
|
-
await expect(client.createSession({
|
|
580
|
-
agent: 'review/security',
|
|
581
|
-
message: 'Review this code',
|
|
582
|
-
})).rejects.toThrow('Create session timed out');
|
|
583
|
-
await client.shutdown();
|
|
584
|
-
}
|
|
585
|
-
finally {
|
|
586
|
-
if (previousOpTimeout === undefined) {
|
|
587
|
-
delete process.env.DRS_RUNTIME_OPERATION_TIMEOUT_MS;
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
process.env.DRS_RUNTIME_OPERATION_TIMEOUT_MS = previousOpTimeout;
|
|
591
|
-
}
|
|
592
|
-
if (previousStreamTimeout === undefined) {
|
|
593
|
-
delete process.env.DRS_RUNTIME_STREAM_TIMEOUT_MS;
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
process.env.DRS_RUNTIME_STREAM_TIMEOUT_MS = previousStreamTimeout;
|
|
597
|
-
}
|
|
598
|
-
if (previousPollInterval === undefined) {
|
|
599
|
-
delete process.env.DRS_RUNTIME_STREAM_POLL_INTERVAL_MS;
|
|
600
|
-
}
|
|
601
|
-
else {
|
|
602
|
-
process.env.DRS_RUNTIME_STREAM_POLL_INTERVAL_MS = previousPollInterval;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
});
|
|
606
|
-
it('closeSession throws if not initialized', async () => {
|
|
607
|
-
const client = new RuntimeClient({});
|
|
608
|
-
await expect(client.closeSession('session-123')).rejects.toThrow('Runtime client not initialized');
|
|
609
|
-
});
|
|
610
|
-
it('getServerUrl throws when server is not initialized', () => {
|
|
611
|
-
const client = new RuntimeClient({});
|
|
612
|
-
expect(() => client.getServerUrl()).toThrow('Server not initialized');
|
|
613
|
-
});
|
|
614
|
-
it('shutdown does not throw when no runtime is active', async () => {
|
|
615
|
-
const client = new RuntimeClient({});
|
|
616
|
-
await expect(client.shutdown()).resolves.toBeUndefined();
|
|
617
|
-
});
|
|
618
|
-
});
|
|
619
|
-
describe('environment variable resolution', () => {
|
|
620
|
-
it('resolves provider apiKey env references before runtime init', async () => {
|
|
621
|
-
const originalEnv = process.env.TEST_API_KEY;
|
|
622
|
-
process.env.TEST_API_KEY = 'test-key-123';
|
|
623
|
-
try {
|
|
624
|
-
const client = await createRuntimeClientInstance({
|
|
625
|
-
directory: process.cwd(),
|
|
626
|
-
provider: {
|
|
627
|
-
'test-provider': {
|
|
628
|
-
baseUrl: 'https://api.test.com/v1',
|
|
629
|
-
apiKey: '{env:TEST_API_KEY}',
|
|
630
|
-
api: 'openai-completions',
|
|
631
|
-
models: [
|
|
632
|
-
{
|
|
633
|
-
id: 'test-model',
|
|
634
|
-
},
|
|
635
|
-
],
|
|
636
|
-
},
|
|
637
|
-
},
|
|
638
|
-
});
|
|
639
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
640
|
-
config: expect.objectContaining({
|
|
641
|
-
provider: expect.objectContaining({
|
|
642
|
-
'test-provider': expect.objectContaining({
|
|
643
|
-
apiKey: 'test-key-123',
|
|
644
|
-
}),
|
|
645
|
-
}),
|
|
646
|
-
}),
|
|
647
|
-
}));
|
|
648
|
-
await client.shutdown();
|
|
649
|
-
}
|
|
650
|
-
finally {
|
|
651
|
-
if (originalEnv === undefined) {
|
|
652
|
-
delete process.env.TEST_API_KEY;
|
|
653
|
-
}
|
|
654
|
-
else {
|
|
655
|
-
process.env.TEST_API_KEY = originalEnv;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
});
|
|
659
|
-
it('resolves env references inside provider and model headers', async () => {
|
|
660
|
-
const originalProviderHeaderEnv = process.env.TEST_PROVIDER_HEADER;
|
|
661
|
-
const originalModelHeaderEnv = process.env.TEST_MODEL_HEADER;
|
|
662
|
-
process.env.TEST_PROVIDER_HEADER = 'provider-header-value';
|
|
663
|
-
process.env.TEST_MODEL_HEADER = 'model-header-value';
|
|
664
|
-
try {
|
|
665
|
-
const client = await createRuntimeClientInstance({
|
|
666
|
-
directory: process.cwd(),
|
|
667
|
-
provider: {
|
|
668
|
-
'test-provider': {
|
|
669
|
-
baseUrl: 'https://api.test.com/v1',
|
|
670
|
-
apiKey: 'TEST_API_KEY',
|
|
671
|
-
api: 'openai-completions',
|
|
672
|
-
headers: {
|
|
673
|
-
'X-Provider-Header': '{env:TEST_PROVIDER_HEADER}',
|
|
674
|
-
},
|
|
675
|
-
models: [
|
|
676
|
-
{
|
|
677
|
-
id: 'test-model',
|
|
678
|
-
headers: {
|
|
679
|
-
'X-Model-Header': '{env:TEST_MODEL_HEADER}',
|
|
680
|
-
},
|
|
681
|
-
},
|
|
682
|
-
],
|
|
683
|
-
},
|
|
684
|
-
},
|
|
685
|
-
});
|
|
686
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
687
|
-
config: expect.objectContaining({
|
|
688
|
-
provider: expect.objectContaining({
|
|
689
|
-
'test-provider': expect.objectContaining({
|
|
690
|
-
headers: {
|
|
691
|
-
'X-Provider-Header': 'provider-header-value',
|
|
692
|
-
},
|
|
693
|
-
models: [
|
|
694
|
-
expect.objectContaining({
|
|
695
|
-
id: 'test-model',
|
|
696
|
-
headers: {
|
|
697
|
-
'X-Model-Header': 'model-header-value',
|
|
698
|
-
},
|
|
699
|
-
}),
|
|
700
|
-
],
|
|
701
|
-
}),
|
|
702
|
-
}),
|
|
703
|
-
}),
|
|
704
|
-
}));
|
|
705
|
-
await client.shutdown();
|
|
706
|
-
}
|
|
707
|
-
finally {
|
|
708
|
-
if (originalProviderHeaderEnv === undefined) {
|
|
709
|
-
delete process.env.TEST_PROVIDER_HEADER;
|
|
710
|
-
}
|
|
711
|
-
else {
|
|
712
|
-
process.env.TEST_PROVIDER_HEADER = originalProviderHeaderEnv;
|
|
713
|
-
}
|
|
714
|
-
if (originalModelHeaderEnv === undefined) {
|
|
715
|
-
delete process.env.TEST_MODEL_HEADER;
|
|
716
|
-
}
|
|
717
|
-
else {
|
|
718
|
-
process.env.TEST_MODEL_HEADER = originalModelHeaderEnv;
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
});
|
|
722
|
-
it('warns when referenced environment variables are missing', async () => {
|
|
723
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
724
|
-
const originalEnv = process.env.NONEXISTENT_VAR;
|
|
725
|
-
delete process.env.NONEXISTENT_VAR;
|
|
726
|
-
try {
|
|
727
|
-
const client = await createRuntimeClientInstance({
|
|
728
|
-
directory: process.cwd(),
|
|
729
|
-
provider: {
|
|
730
|
-
'test-provider': {
|
|
731
|
-
baseUrl: 'https://api.test.com/v1',
|
|
732
|
-
apiKey: '{env:NONEXISTENT_VAR}',
|
|
733
|
-
api: 'openai-completions',
|
|
734
|
-
models: [
|
|
735
|
-
{
|
|
736
|
-
id: 'test-model',
|
|
737
|
-
},
|
|
738
|
-
],
|
|
739
|
-
},
|
|
740
|
-
},
|
|
741
|
-
});
|
|
742
|
-
// Logger outputs warning via console.log (human format)
|
|
743
|
-
const allOutput = logSpy.mock.calls.map((c) => String(c[0])).join('\n');
|
|
744
|
-
expect(allOutput).toContain('NONEXISTENT_VAR is not set');
|
|
745
|
-
await client.shutdown();
|
|
746
|
-
}
|
|
747
|
-
finally {
|
|
748
|
-
logSpy.mockRestore();
|
|
749
|
-
if (originalEnv !== undefined) {
|
|
750
|
-
process.env.NONEXISTENT_VAR = originalEnv;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
});
|
|
754
|
-
});
|
|
755
|
-
describe('factory functions', () => {
|
|
756
|
-
it('createRuntimeClient returns an uninitialized client instance', () => {
|
|
757
|
-
const client = createRuntimeClient({
|
|
758
|
-
directory: process.cwd(),
|
|
759
|
-
});
|
|
760
|
-
expect(client).toBeInstanceOf(RuntimeClient);
|
|
761
|
-
});
|
|
762
|
-
it('createRuntimeClientInstance initializes in-process runtime', async () => {
|
|
763
|
-
const client = await createRuntimeClientInstance({
|
|
764
|
-
directory: process.cwd(),
|
|
765
|
-
});
|
|
766
|
-
expect(client).toBeInstanceOf(RuntimeClient);
|
|
767
|
-
expect(mocks.createPiInProcessServer).toHaveBeenCalled();
|
|
768
|
-
await client.shutdown();
|
|
769
|
-
});
|
|
770
|
-
});
|
|
771
|
-
});
|
|
772
|
-
//# sourceMappingURL=client.test.js.map
|