@pi-unipi/background-tasks 2.6.3 → 2.9.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/package.json +6 -5
- package/src/__tests__/anthropic-attribution.test.ts +0 -195
- package/src/__tests__/config.test.ts +0 -137
- package/src/__tests__/core.test.ts +0 -493
- package/src/__tests__/delegate-artifacts.test.ts +0 -528
- package/src/__tests__/delegate-budget.test.ts +0 -456
- package/src/__tests__/delegate-launch.test.ts +0 -676
- package/src/__tests__/delegate-result-package.test.ts +0 -350
- package/src/__tests__/delegate-seed.test.ts +0 -392
- package/src/__tests__/durable-fs.test.ts +0 -559
- package/src/__tests__/extension-api.test.ts +0 -579
- package/src/__tests__/fusion-artifacts.test.ts +0 -1039
- package/src/__tests__/fusion-budget.test.ts +0 -1356
- package/src/__tests__/fusion-claude-cache.test.ts +0 -320
- package/src/__tests__/fusion-config.test.ts +0 -335
- package/src/__tests__/fusion-context-prompts.test.ts +0 -670
- package/src/__tests__/fusion-evaluation.test.ts +0 -315
- package/src/__tests__/fusion-extraction-equivalence.test.ts +0 -58
- package/src/__tests__/fusion-golden-bytes.test.ts +0 -35
- package/src/__tests__/fusion-high-cardinality.test.ts +0 -192
- package/src/__tests__/fusion-model-selector.test.ts +0 -205
- package/src/__tests__/fusion-orchestrator.test.ts +0 -1194
- package/src/__tests__/fusion-rpc.test.ts +0 -369
- package/src/__tests__/fusion-sdk.test.ts +0 -1226
- package/src/__tests__/fusion-v5-core.test.ts +0 -219
- package/src/__tests__/fusion-validate-orchestrator.test.ts +0 -240
- package/src/__tests__/fusion-web-fetch.test.ts +0 -485
- package/src/__tests__/fusion-workflows.test.ts +0 -59
- package/src/__tests__/helpers/delegate-deterministic-seed.ts +0 -109
- package/src/__tests__/helpers/delegate-seed-subprocess.ts +0 -10
- package/src/__tests__/helpers/fusion-canonical-subprocess.ts +0 -21
- package/src/__tests__/helpers/fusion-canonical.ts +0 -140
- package/src/__tests__/helpers/fusion-fake-pi.ts +0 -279
- package/src/__tests__/helpers/fusion-golden-corpus.ts +0 -500
- package/src/__tests__/helpers/fusion-high-cardinality.ts +0 -140
- package/src/__tests__/helpers/normalize.ts +0 -22
- package/src/__tests__/helpers/pi-hook-contract-evidence.json +0 -18
- package/src/__tests__/pi-launch.test.ts +0 -202
- package/src/__tests__/registry.test.ts +0 -1580
- package/src/__tests__/scripted-provider/delegate-ambient-provider.test.ts +0 -130
- package/src/__tests__/scripted-provider/delegate-child-guard.test.ts +0 -631
- package/src/__tests__/scripted-provider/delegate-guard-provider.ts +0 -403
- package/src/__tests__/scripted-provider/follow-up.test.ts +0 -448
- package/src/__tests__/scripted-provider/fusion-output-recovery.test.ts +0 -132
- package/src/__tests__/scripted-provider/fusion-reason.test.ts +0 -310
- package/src/__tests__/scripted-provider/fusion-runtime-guard.test.ts +0 -163
- package/src/__tests__/scripted-provider/hook-contract-provider.ts +0 -179
- package/src/__tests__/scripted-provider/hook-probe-a.ts +0 -3
- package/src/__tests__/scripted-provider/hook-probe-b.ts +0 -3
- package/src/__tests__/scripted-provider/hook-probe-extension.ts +0 -126
- package/src/__tests__/scripted-provider/output-recovery-provider.ts +0 -153
- package/src/__tests__/scripted-provider/pi-hook-contract-evidence.json +0 -18
- package/src/__tests__/scripted-provider/pi-hook-contract.test.ts +0 -477
- package/src/__tests__/scripted-provider/runtime-guard-probe.ts +0 -28
- package/src/__tests__/scripted-provider/runtime-guard-provider.ts +0 -49
- package/src/__tests__/scripted-provider/scripted-provider-extension.ts +0 -408
- package/src/__tests__/task-manager.test.ts +0 -479
- package/src/__tests__/windows-taskkill.test.ts +0 -161
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/background-tasks",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Background tasks for UniPi \u2014 durable shell jobs, delegated agents, attested Pi runs, and fixed-purpose Fusion workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"test": "npx tsx --test src/__tests__/*.test.ts"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@earendil-works/pi-ai": "^0.
|
|
13
|
-
"@pi-unipi/core": "2.
|
|
12
|
+
"@earendil-works/pi-ai": "^0.84.0",
|
|
13
|
+
"@pi-unipi/core": "2.9.0",
|
|
14
14
|
"turndown": "^7.2.4"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
"typescript": "^5.8.3"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
22
|
-
"@earendil-works/pi-tui": "^0.
|
|
21
|
+
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
22
|
+
"@earendil-works/pi-tui": "^0.84.0",
|
|
23
23
|
"typebox": "^1.1.38"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"src/**/*.ts",
|
|
27
27
|
"src/**/*.json",
|
|
28
|
+
"!src/__tests__/**",
|
|
28
29
|
"extensions/*.ts",
|
|
29
30
|
"README.md"
|
|
30
31
|
],
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import spawnAnthropicAttribution, {
|
|
4
|
-
ANTHROPIC_ATTRIBUTION_CLAIM_CHANNEL,
|
|
5
|
-
rewriteAnthropicRequestPayload,
|
|
6
|
-
type PiExtensionHost,
|
|
7
|
-
type PiContextLike,
|
|
8
|
-
} from '../anthropic-attribution.js';
|
|
9
|
-
import { isJsonObject } from '../types.js';
|
|
10
|
-
import { buildAttestedPiArgv } from '../attested-pi-run.js';
|
|
11
|
-
|
|
12
|
-
const BAD_SYSTEM_LINES = [
|
|
13
|
-
'- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md)',
|
|
14
|
-
'- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md), environment variables (docs/environment-variables.md)',
|
|
15
|
-
'- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing',
|
|
16
|
-
] as const;
|
|
17
|
-
|
|
18
|
-
function context(provider = 'anthropic'): PiContextLike {
|
|
19
|
-
return {
|
|
20
|
-
model: {
|
|
21
|
-
provider,
|
|
22
|
-
id: provider === 'anthropic' ? 'claude-sonnet-4-5' : 'gpt-5.5',
|
|
23
|
-
maxTokens: 64_000,
|
|
24
|
-
reasoning: true,
|
|
25
|
-
},
|
|
26
|
-
sessionManager: {
|
|
27
|
-
getSessionId: () => '11111111-2222-4333-8444-555555555555',
|
|
28
|
-
getBranch: () => [],
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
class SynchronousTestBus {
|
|
34
|
-
private readonly handlers = new Map<string, Array<(data: unknown) => void>>();
|
|
35
|
-
|
|
36
|
-
emit(channel: string, data: unknown): void {
|
|
37
|
-
for (const handler of this.handlers.get(channel) ?? []) handler(data);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
on(channel: string, handler: (data: unknown) => void): () => void {
|
|
41
|
-
const handlers = this.handlers.get(channel) ?? [];
|
|
42
|
-
handlers.push(handler);
|
|
43
|
-
this.handlers.set(channel, handlers);
|
|
44
|
-
return () => {
|
|
45
|
-
this.handlers.set(
|
|
46
|
-
channel,
|
|
47
|
-
(this.handlers.get(channel) ?? []).filter((candidate) => candidate !== handler),
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
listenerCount(channel: string): number {
|
|
53
|
-
return this.handlers.get(channel)?.length ?? 0;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function recordingHost(bus: SynchronousTestBus): {
|
|
58
|
-
host: PiExtensionHost;
|
|
59
|
-
registrations: { commands: number; handlers: number; providers: number; entries: number };
|
|
60
|
-
} {
|
|
61
|
-
const registrations = { commands: 0, handlers: 0, providers: 0, entries: 0 };
|
|
62
|
-
const on: PiExtensionHost['on'] = () => {
|
|
63
|
-
registrations.handlers += 1;
|
|
64
|
-
};
|
|
65
|
-
return {
|
|
66
|
-
host: {
|
|
67
|
-
events: bus,
|
|
68
|
-
on,
|
|
69
|
-
registerCommand: () => {
|
|
70
|
-
registrations.commands += 1;
|
|
71
|
-
},
|
|
72
|
-
registerProvider: () => {
|
|
73
|
-
registrations.providers += 1;
|
|
74
|
-
},
|
|
75
|
-
appendEntry: () => {
|
|
76
|
-
registrations.entries += 1;
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
registrations,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
void describe('global Anthropic attribution extension', () => {
|
|
84
|
-
void it('matches all SPS exact-line variants while preserving unrelated blocks and cache controls', () => {
|
|
85
|
-
const original = {
|
|
86
|
-
model: 'claude-sonnet-4-5',
|
|
87
|
-
max_tokens: 64_000,
|
|
88
|
-
system: [
|
|
89
|
-
{
|
|
90
|
-
type: 'text',
|
|
91
|
-
text: ['keep before', ...BAD_SYSTEM_LINES, 'keep after'].join('\n'),
|
|
92
|
-
cache_control: { type: 'ephemeral', ttl: '1h' },
|
|
93
|
-
custom_field: 'preserved',
|
|
94
|
-
},
|
|
95
|
-
{ type: 'custom', payload: 'unchanged' },
|
|
96
|
-
],
|
|
97
|
-
messages: [{ role: 'user', content: 'hello' }],
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const rewritten = rewriteAnthropicRequestPayload({
|
|
101
|
-
payload: original,
|
|
102
|
-
ctx: context(),
|
|
103
|
-
account: {
|
|
104
|
-
deviceId: 'd'.repeat(64),
|
|
105
|
-
accountUuid: 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee',
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
assert.ok(isJsonObject(rewritten));
|
|
109
|
-
const system: unknown = rewritten['system'];
|
|
110
|
-
assert.ok(Array.isArray(system));
|
|
111
|
-
const retained = system.find(
|
|
112
|
-
(block) => isJsonObject(block) && block['custom_field'] === 'preserved',
|
|
113
|
-
);
|
|
114
|
-
assert.ok(isJsonObject(retained));
|
|
115
|
-
assert.equal(retained['text'], 'keep before\nkeep after');
|
|
116
|
-
assert.deepEqual(retained['cache_control'], { type: 'ephemeral', ttl: '1h' });
|
|
117
|
-
assert.equal(retained['custom_field'], 'preserved');
|
|
118
|
-
assert.deepEqual(system.at(-1), { type: 'custom', payload: 'unchanged' });
|
|
119
|
-
assert.deepEqual(original.system[0]?.cache_control, { type: 'ephemeral', ttl: '1h' });
|
|
120
|
-
for (const rejected of BAD_SYSTEM_LINES) {
|
|
121
|
-
assert.equal(JSON.stringify(rewritten).includes(rejected), false);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
void it('leaves non-Anthropic payloads untouched', () => {
|
|
126
|
-
const payload = { model: 'gpt-5.5', metadata: { untouched: true } };
|
|
127
|
-
assert.equal(
|
|
128
|
-
rewriteAnthropicRequestPayload({
|
|
129
|
-
payload,
|
|
130
|
-
ctx: context('openai-codex'),
|
|
131
|
-
account: { deviceId: 'd', accountUuid: 'a' },
|
|
132
|
-
}),
|
|
133
|
-
undefined,
|
|
134
|
-
);
|
|
135
|
-
assert.deepEqual(payload, { model: 'gpt-5.5', metadata: { untouched: true } });
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
void it('adds package attribution to attested Anthropic argv only', () => {
|
|
139
|
-
const base = {
|
|
140
|
-
name: 'Attested child',
|
|
141
|
-
model: 'model',
|
|
142
|
-
prompt: 'write report.md',
|
|
143
|
-
reportPath: 'report.md',
|
|
144
|
-
};
|
|
145
|
-
assert.deepEqual(
|
|
146
|
-
buildAttestedPiArgv(
|
|
147
|
-
{ ...base, provider: 'anthropic' },
|
|
148
|
-
'/pkg/extensions/anthropic-attribution.ts',
|
|
149
|
-
),
|
|
150
|
-
[
|
|
151
|
-
'pi',
|
|
152
|
-
'--mode',
|
|
153
|
-
'json',
|
|
154
|
-
'--provider',
|
|
155
|
-
'anthropic',
|
|
156
|
-
'--model',
|
|
157
|
-
'model',
|
|
158
|
-
'--extension',
|
|
159
|
-
'/pkg/extensions/anthropic-attribution.ts',
|
|
160
|
-
'write report.md',
|
|
161
|
-
],
|
|
162
|
-
);
|
|
163
|
-
assert.deepEqual(buildAttestedPiArgv({ ...base, provider: 'openai-codex' }), [
|
|
164
|
-
'pi',
|
|
165
|
-
'--mode',
|
|
166
|
-
'json',
|
|
167
|
-
'--provider',
|
|
168
|
-
'openai-codex',
|
|
169
|
-
'--model',
|
|
170
|
-
'model',
|
|
171
|
-
'write report.md',
|
|
172
|
-
]);
|
|
173
|
-
assert.throws(
|
|
174
|
-
() => buildAttestedPiArgv({ ...base, provider: 'anthropic' }),
|
|
175
|
-
/require the package attribution extension/,
|
|
176
|
-
);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
void it('allows exactly one independently loaded copy to own global registration', () => {
|
|
180
|
-
const bus = new SynchronousTestBus();
|
|
181
|
-
const first = recordingHost(bus);
|
|
182
|
-
const second = recordingHost(bus);
|
|
183
|
-
|
|
184
|
-
spawnAnthropicAttribution(first.host);
|
|
185
|
-
spawnAnthropicAttribution(second.host);
|
|
186
|
-
|
|
187
|
-
assert.equal(first.registrations.commands, 1);
|
|
188
|
-
assert.equal(first.registrations.handlers, 4);
|
|
189
|
-
assert.equal(first.registrations.providers, 1);
|
|
190
|
-
assert.equal(second.registrations.commands, 0);
|
|
191
|
-
assert.equal(second.registrations.handlers, 0);
|
|
192
|
-
assert.equal(second.registrations.providers, 0);
|
|
193
|
-
assert.equal(bus.listenerCount(ANTHROPIC_ATTRIBUTION_CLAIM_CHANNEL), 1);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Config layering + validation tests for @pi-unipi/background-tasks.
|
|
3
|
-
* Adapted from reference config/operations specs to OUR paths and layering.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import test from "node:test";
|
|
7
|
-
import assert from "node:assert/strict";
|
|
8
|
-
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
-
import { homedir, tmpdir } from "node:os";
|
|
10
|
-
import { join } from "node:path";
|
|
11
|
-
import {
|
|
12
|
-
DEFAULT_CONFIG,
|
|
13
|
-
loadBackgroundTasksConfig,
|
|
14
|
-
validateBackgroundTasksConfig,
|
|
15
|
-
} from "../config.js";
|
|
16
|
-
|
|
17
|
-
/** Run loadBackgroundTasksConfig with HOME pointed at a temp dir. */
|
|
18
|
-
function withTempHome<T>(fn: (home: string, cwd: string) => T): T {
|
|
19
|
-
const home = mkdtempSync(join(tmpdir(), "unipi-bg-cfg-home-"));
|
|
20
|
-
const cwd = mkdtempSync(join(tmpdir(), "unipi-bg-cfg-ws-"));
|
|
21
|
-
const realHome = process.env.HOME;
|
|
22
|
-
const realUserProfile = process.env.USERPROFILE;
|
|
23
|
-
process.env.HOME = home;
|
|
24
|
-
process.env.USERPROFILE = home;
|
|
25
|
-
try {
|
|
26
|
-
return fn(home, cwd);
|
|
27
|
-
} finally {
|
|
28
|
-
if (realHome === undefined) delete process.env.HOME;
|
|
29
|
-
else process.env.HOME = realHome;
|
|
30
|
-
if (realUserProfile === undefined) delete process.env.USERPROFILE;
|
|
31
|
-
else process.env.USERPROFILE = realUserProfile;
|
|
32
|
-
rmSync(home, { recursive: true, force: true });
|
|
33
|
-
rmSync(cwd, { recursive: true, force: true });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
test("defaults: enabled true, notify+wake on, 20MiB cap", () => {
|
|
38
|
-
assert.equal(DEFAULT_CONFIG.enabled, true);
|
|
39
|
-
assert.equal(DEFAULT_CONFIG.notifyOnCompletion, true);
|
|
40
|
-
assert.equal(DEFAULT_CONFIG.triggerOnCompletion, true);
|
|
41
|
-
assert.equal(DEFAULT_CONFIG.maxOutputBytes, 20 * 1024 * 1024);
|
|
42
|
-
assert.equal(DEFAULT_CONFIG.delegate.extensionMode, "isolated");
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("first run creates the global config file", () => {
|
|
46
|
-
withTempHome((home) => {
|
|
47
|
-
const { config } = loadBackgroundTasksConfig("");
|
|
48
|
-
assert.equal(config.enabled, true);
|
|
49
|
-
const written = JSON.parse(readFileSync(join(home, ".unipi", "config", "background-tasks.json"), "utf-8"));
|
|
50
|
-
assert.equal(written.enabled, true);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("workspace config wins over global", () => {
|
|
55
|
-
withTempHome((home, cwd) => {
|
|
56
|
-
const globalPath = join(home, ".unipi", "config", "background-tasks.json");
|
|
57
|
-
mkdirSync(join(globalPath, ".."), { recursive: true });
|
|
58
|
-
writeFileSync(globalPath, JSON.stringify({ maxFinishedTasks: 10 }));
|
|
59
|
-
const wsPath = join(cwd, ".unipi", "config", "background-tasks.json");
|
|
60
|
-
mkdirSync(join(wsPath, ".."), { recursive: true });
|
|
61
|
-
writeFileSync(wsPath, JSON.stringify({ maxFinishedTasks: 7, triggerOnCompletion: false }));
|
|
62
|
-
|
|
63
|
-
const { config } = loadBackgroundTasksConfig(cwd);
|
|
64
|
-
assert.equal(config.maxFinishedTasks, 7);
|
|
65
|
-
assert.equal(config.triggerOnCompletion, false);
|
|
66
|
-
assert.equal(config.enabled, true); // untouched default
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("corrupt layers are skipped with warnings, not crashes", () => {
|
|
71
|
-
withTempHome((home, cwd) => {
|
|
72
|
-
const globalPath = join(home, ".unipi", "config", "background-tasks.json");
|
|
73
|
-
mkdirSync(join(globalPath, ".."), { recursive: true });
|
|
74
|
-
writeFileSync(globalPath, "{not json");
|
|
75
|
-
const wsPath = join(cwd, ".unipi", "config", "background-tasks.json");
|
|
76
|
-
mkdirSync(join(wsPath, ".."), { recursive: true });
|
|
77
|
-
writeFileSync(wsPath, "[1,2,3]");
|
|
78
|
-
|
|
79
|
-
const { config, warnings } = loadBackgroundTasksConfig(cwd);
|
|
80
|
-
assert.equal(config.enabled, true);
|
|
81
|
-
assert.equal(config.maxFinishedTasks, DEFAULT_CONFIG.maxFinishedTasks);
|
|
82
|
-
assert.ok(warnings.length >= 1);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("validation rejects bad values with visible messages", () => {
|
|
87
|
-
const problems = validateBackgroundTasksConfig({
|
|
88
|
-
enabled: "yes",
|
|
89
|
-
maxOutputBytes: 10,
|
|
90
|
-
delegate: { extensionMode: "wild" },
|
|
91
|
-
fusion: { candidates: ["a"] },
|
|
92
|
-
});
|
|
93
|
-
assert.ok(problems.some((p) => p.includes("enabled")));
|
|
94
|
-
assert.ok(problems.some((p) => p.includes("maxOutputBytes")));
|
|
95
|
-
assert.ok(problems.some((p) => p.includes("extensionMode")));
|
|
96
|
-
assert.ok(problems.some((p) => p.includes("candidates")));
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("validation accepts a full valid config", () => {
|
|
100
|
-
const problems = validateBackgroundTasksConfig({
|
|
101
|
-
enabled: false,
|
|
102
|
-
notifyOnCompletion: false,
|
|
103
|
-
triggerOnCompletion: false,
|
|
104
|
-
defaultTimeoutSeconds: 600,
|
|
105
|
-
maxFinishedTasks: 5,
|
|
106
|
-
maxOutputBytes: 1024 * 1024,
|
|
107
|
-
delegate: { extensionMode: "ambient", autoDeliver: "always", maxTurns: 10, maxToolCalls: 20, timeoutSeconds: 60 },
|
|
108
|
-
fusion: { candidates: ["m1", "m2", "m3"], evaluator: "e", merger: "g" },
|
|
109
|
-
});
|
|
110
|
-
assert.deepEqual(problems, []);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("invalid values sanitize to defaults instead of crashing the loader", () => {
|
|
114
|
-
withTempHome((home) => {
|
|
115
|
-
const globalPath = join(home, ".unipi", "config", "background-tasks.json");
|
|
116
|
-
mkdirSync(join(globalPath, ".."), { recursive: true });
|
|
117
|
-
writeFileSync(
|
|
118
|
-
globalPath,
|
|
119
|
-
JSON.stringify({ enabled: "nope", maxFinishedTasks: -1, delegate: { extensionMode: "wild" } }),
|
|
120
|
-
);
|
|
121
|
-
const { config, warnings } = loadBackgroundTasksConfig("");
|
|
122
|
-
assert.equal(config.enabled, true);
|
|
123
|
-
assert.equal(config.maxFinishedTasks, DEFAULT_CONFIG.maxFinishedTasks);
|
|
124
|
-
assert.equal(config.delegate.extensionMode, "isolated");
|
|
125
|
-
assert.ok(warnings.length >= 1);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test("master toggle off is preserved through load", () => {
|
|
130
|
-
withTempHome((home) => {
|
|
131
|
-
const globalPath = join(home, ".unipi", "config", "background-tasks.json");
|
|
132
|
-
mkdirSync(join(globalPath, ".."), { recursive: true });
|
|
133
|
-
writeFileSync(globalPath, JSON.stringify({ enabled: false }));
|
|
134
|
-
const { config } = loadBackgroundTasksConfig("");
|
|
135
|
-
assert.equal(config.enabled, false);
|
|
136
|
-
});
|
|
137
|
-
});
|