@qvac/harness 0.8.9 → 0.8.10
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/CHANGELOG.md +62 -1
- package/dist-lib/index.d.ts +1 -1
- package/dist-lib/index.js +51 -50
- package/dist-lib/lib/audio-transcode-bare.js +2 -2
- package/dist-lib/lib/capabilities/completion.d.ts +1 -2
- package/dist-lib/lib/capabilities/completion.js +29 -25
- package/dist-lib/lib/capabilities/tool-prompt-log.js +3 -3
- package/dist-lib/lib/capabilities/voice.js +7 -6
- package/dist-lib/lib/connect.js +18 -17
- package/dist-lib/lib/errors.d.ts +266 -0
- package/dist-lib/lib/errors.js +189 -0
- package/dist-lib/lib/helpers/load-stall.d.ts +1 -1
- package/dist-lib/lib/helpers/load-stall.js +2 -2
- package/dist-lib/lib/helpers/race-abort.d.ts +2 -1
- package/dist-lib/lib/helpers/race-abort.js +3 -4
- package/dist-lib/lib/hooks/built-in/arg-repair.js +2 -2
- package/dist-lib/lib/hooks/pipeline.js +4 -4
- package/dist-lib/lib/hooks/run.js +2 -2
- package/dist-lib/lib/log.d.ts +26 -2
- package/dist-lib/lib/log.js +66 -16
- package/dist-lib/lib/models/model-pool.js +11 -6
- package/dist-lib/lib/models/model-source.js +3 -2
- package/dist-lib/lib/no-redirect-fetch-bare.js +2 -1
- package/dist-lib/lib/no-redirect-fetch-impl.js +2 -1
- package/dist-lib/lib/office/extract.js +4 -3
- package/dist-lib/lib/office/index.js +2 -1
- package/dist-lib/lib/resolution-detector.js +3 -3
- package/dist-lib/lib/rpc-session.js +2 -1
- package/dist-lib/lib/sdk-loader.js +2 -2
- package/dist-lib/lib/serve.js +33 -29
- package/dist-lib/lib/spawn.d.ts +2 -4
- package/dist-lib/lib/spawn.js +14 -7
- package/dist-lib/lib/tools/edit-image.js +2 -1
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +8 -8
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +7 -6
- package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +8 -8
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +11 -7
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +5 -5
- package/dist-lib/lib/tools/exec/adapters/python/worker/active.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +9 -8
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/serve.js +4 -4
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn.d.ts +2 -1
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/note-name.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +5 -4
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +3 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-fs.js +2 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +3 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +6 -5
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +13 -12
- package/dist-lib/lib/tools/exec/runner.js +2 -1
- package/dist-lib/lib/tools/files/edit-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/edit-file.js +73 -0
- package/dist-lib/lib/tools/files/glob.d.ts +3 -0
- package/dist-lib/lib/tools/files/glob.js +63 -0
- package/dist-lib/lib/tools/files/grep.d.ts +3 -0
- package/dist-lib/lib/tools/files/grep.js +114 -0
- package/dist-lib/lib/tools/files/index.d.ts +3 -0
- package/dist-lib/lib/tools/files/index.js +23 -0
- package/dist-lib/lib/tools/files/list-dir.d.ts +5 -0
- package/dist-lib/lib/tools/files/list-dir.js +130 -0
- package/dist-lib/lib/tools/files/make-dir.d.ts +3 -0
- package/dist-lib/lib/tools/files/make-dir.js +47 -0
- package/dist-lib/lib/tools/files/read-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/read-file.js +103 -0
- package/dist-lib/lib/tools/files/transfer.d.ts +4 -0
- package/dist-lib/lib/tools/files/transfer.js +82 -0
- package/dist-lib/lib/tools/files/trash-file.d.ts +4 -0
- package/dist-lib/lib/tools/files/trash-file.js +61 -0
- package/dist-lib/lib/tools/files/write-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/write-file.js +55 -0
- package/dist-lib/lib/tools/fs/document-text.d.ts +2 -0
- package/dist-lib/lib/tools/fs/document-text.js +38 -0
- package/dist-lib/lib/tools/fs/edit-match.d.ts +11 -0
- package/dist-lib/lib/tools/fs/edit-match.js +85 -0
- package/dist-lib/lib/tools/fs/remote.d.ts +2 -1
- package/dist-lib/lib/tools/fs/remote.js +4 -4
- package/dist-lib/lib/tools/fs/scope.d.ts +1 -1
- package/dist-lib/lib/tools/fs/scope.js +3 -2
- package/dist-lib/lib/tools/fs/targets.d.ts +10 -0
- package/dist-lib/lib/tools/fs/targets.js +32 -0
- package/dist-lib/lib/tools/fs/tool-path.d.ts +24 -0
- package/dist-lib/lib/tools/fs/tool-path.js +58 -0
- package/dist-lib/lib/tools/fs/walk.js +3 -3
- package/dist-lib/lib/tools/generate-image.d.ts +2 -2
- package/dist-lib/lib/tools/generate-image.js +5 -6
- package/dist-lib/lib/tools/office.js +2 -1
- package/dist-lib/lib/tools/skill.js +2 -1
- package/dist-lib/lib/tools/skills/bundled-dir.js +4 -4
- package/dist-lib/lib/tools/skills/catalog.js +2 -2
- package/dist-lib/lib/tools/skills/credential-health.js +3 -3
- package/dist-lib/lib/tools/skills/overlay-crud.js +3 -2
- package/dist-lib/lib/tools/translate.d.ts +2 -2
- package/dist-lib/lib/tools/translate.js +5 -6
- package/dist-lib/lib/tools/weather-lookup/geocode.js +5 -4
- package/dist-lib/lib/web-search/engines/bing.js +2 -1
- package/dist-lib/lib/web-search/engines/google.js +2 -1
- package/dist-lib/lib/web-search/fetch-html.d.ts +5 -5
- package/dist-lib/lib/web-search/fetch-html.js +12 -15
- package/package.json +66 -24
- package/worker/harness-sidecar.mjs +12 -13
- package/dist-lib/lib/tools/create-documents.d.ts +0 -3
- package/dist-lib/lib/tools/create-documents.js +0 -95
- package/dist-lib/lib/tools/edit-documents.d.ts +0 -3
- package/dist-lib/lib/tools/edit-documents.js +0 -132
- package/dist-lib/lib/tools/file-glob.d.ts +0 -14
- package/dist-lib/lib/tools/file-glob.js +0 -118
- package/dist-lib/lib/tools/grep.d.ts +0 -5
- package/dist-lib/lib/tools/grep.js +0 -184
- package/dist-lib/lib/tools/read-files.d.ts +0 -5
- package/dist-lib/lib/tools/read-files.js +0 -157
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import process from 'process';
|
|
2
|
+
import { HarnessError } from '../errors.js';
|
|
2
3
|
import { contextCandidates } from '../context-window.js';
|
|
3
4
|
import { errorCode, errorMessage, isRecord } from '../guards.js';
|
|
4
5
|
import { bufferBurst } from '../helpers/buffer-burst.js';
|
|
@@ -12,7 +13,7 @@ import { clipRawCall, hasMarkupCall, normalizeToolArgs, recoverMarkupToolCalls,
|
|
|
12
13
|
import { INTENT_PARAM, INTENT_PARAM_DESCRIPTION, takeToolIntent } from '../helpers/tool-intent.js';
|
|
13
14
|
import { HookPipeline } from '../hooks/pipeline.js';
|
|
14
15
|
import { fallbackToolLabel } from '../helpers/tool-label.js';
|
|
15
|
-
import {
|
|
16
|
+
import { log } from '../log.js';
|
|
16
17
|
import { resolveLlmGenerationParams } from '../models/llm-presets.js';
|
|
17
18
|
import { toolDialectFor } from '../models/model-catalog.js';
|
|
18
19
|
import { isContextOverflowError, isGrammarOverrunError, isSdkModelWedgedError } from '../models/sdk-faults.js';
|
|
@@ -55,7 +56,7 @@ function runWallClockBudgetMs() {
|
|
|
55
56
|
const ms = Number(process.env.QVAC_RUN_BUDGET_MS);
|
|
56
57
|
return ms > 0 ? ms : RUN_WALL_CLOCK_BUDGET_MS;
|
|
57
58
|
}
|
|
58
|
-
export
|
|
59
|
+
export { CONTEXT_OVERFLOW_MESSAGE } from '../errors.js';
|
|
59
60
|
const MAX_MUTE_ROUNDS = 1;
|
|
60
61
|
const NARRATED_INTENT = /\b(I['’]ll|I will|Now I|Let me(?! know)|Next,? I)\b[^.!?]*[.!?]?\s*$/i;
|
|
61
62
|
const MAX_WEB_SEARCHES_PER_TURN = 3;
|
|
@@ -282,7 +283,7 @@ function deniedCall() {
|
|
|
282
283
|
async function executeCall(tool, args, { ctx, approve, signal, denied, sources = [], effect: classified, skipApprovals, onToolActivity, intent, display }) {
|
|
283
284
|
const validationError = tool.validate?.(args, ctx);
|
|
284
285
|
if (validationError) {
|
|
285
|
-
|
|
286
|
+
log.info('tool rejected: arguments failed validation', { tool: tool.name });
|
|
286
287
|
return guidanceResult(validationError);
|
|
287
288
|
}
|
|
288
289
|
const refusal = guardToolCall(tool.name, args, {
|
|
@@ -290,7 +291,7 @@ async function executeCall(tool, args, { ctx, approve, signal, denied, sources =
|
|
|
290
291
|
externalWrite: tool.externalWrite === true
|
|
291
292
|
});
|
|
292
293
|
if (refusal) {
|
|
293
|
-
|
|
294
|
+
log.info('tool refused: unverifiable value in payload', { tool: tool.name });
|
|
294
295
|
onToolActivity?.({
|
|
295
296
|
type: 'tool-activity',
|
|
296
297
|
name: tool.name,
|
|
@@ -303,13 +304,13 @@ async function executeCall(tool, args, { ctx, approve, signal, denied, sources =
|
|
|
303
304
|
}
|
|
304
305
|
const key = callKey(tool.name, args);
|
|
305
306
|
if (denied?.has(key)) {
|
|
306
|
-
|
|
307
|
+
log.info('tool denied already, not asking again', { tool: tool.name });
|
|
307
308
|
return deniedCall();
|
|
308
309
|
}
|
|
309
310
|
const effect = classified ?? tool.effect(args, ctx);
|
|
310
311
|
const resource = tool.resource?.(args, ctx) ?? null;
|
|
311
312
|
const autoRun = effect === 'read' || tool.autoApprove === true || skipApprovals === true;
|
|
312
|
-
|
|
313
|
+
log.info('tool decision', {
|
|
313
314
|
tool: tool.name,
|
|
314
315
|
effect,
|
|
315
316
|
auto: autoRun,
|
|
@@ -326,7 +327,7 @@ async function executeCall(tool, args, { ctx, approve, signal, denied, sources =
|
|
|
326
327
|
display: display ?? null
|
|
327
328
|
}), signal)
|
|
328
329
|
: false);
|
|
329
|
-
|
|
330
|
+
log.info(`tool ${approved ? 'approved' : 'denied'}`, { tool: tool.name });
|
|
330
331
|
const decision = toolDecision({
|
|
331
332
|
auto: tool.autoApprove === true || effect === 'read',
|
|
332
333
|
skipAll: skipApprovals === true,
|
|
@@ -348,11 +349,11 @@ async function executeCall(tool, args, { ctx, approve, signal, denied, sources =
|
|
|
348
349
|
let outcome = { kind: 'success' };
|
|
349
350
|
try {
|
|
350
351
|
output = await tool.execute(args, { ...ctx, signal, approval: decision });
|
|
351
|
-
|
|
352
|
+
log.debug('tool done', { tool: tool.name });
|
|
352
353
|
}
|
|
353
354
|
catch (err) {
|
|
354
355
|
const message = errorMessage(err);
|
|
355
|
-
|
|
356
|
+
log.warn('tool failed', { tool: tool.name, err: message });
|
|
356
357
|
output = { error: message, code: errorCode(err) };
|
|
357
358
|
outcome = { kind: 'error', message, error: err };
|
|
358
359
|
}
|
|
@@ -524,7 +525,7 @@ async function* runRoundTokens(result, state, { sdk, signal, startInThink = fals
|
|
|
524
525
|
if (state.overrun) {
|
|
525
526
|
throw state.overrun;
|
|
526
527
|
}
|
|
527
|
-
|
|
528
|
+
log.info('stream ended', { events: tokenEvents, stopToken: state.stopToken });
|
|
528
529
|
if (state.stopToken) {
|
|
529
530
|
cancelThisRound();
|
|
530
531
|
}
|
|
@@ -642,7 +643,7 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
|
|
|
642
643
|
};
|
|
643
644
|
}
|
|
644
645
|
const replay = unprovenReplay(state, final);
|
|
645
|
-
|
|
646
|
+
log.debug('final', {
|
|
646
647
|
stopReason: final.stopReason,
|
|
647
648
|
toolCalls: final.toolCalls?.length ?? 0,
|
|
648
649
|
stopToken: state.stopToken,
|
|
@@ -678,7 +679,7 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
|
|
|
678
679
|
};
|
|
679
680
|
}
|
|
680
681
|
if (recovered) {
|
|
681
|
-
|
|
682
|
+
log.info('grammar overrun recovered', { round: args.round });
|
|
682
683
|
return {
|
|
683
684
|
contentText,
|
|
684
685
|
final: acceptedFinal(state.rawContentText, state.rawText),
|
|
@@ -692,13 +693,13 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
|
|
|
692
693
|
throw err;
|
|
693
694
|
}
|
|
694
695
|
if (emitted) {
|
|
695
|
-
throw new
|
|
696
|
+
throw new HarnessError('CONTEXT_WINDOW_EXCEEDED', [], { cause: err });
|
|
696
697
|
}
|
|
697
698
|
lastOverflow = err;
|
|
698
699
|
}
|
|
699
700
|
}
|
|
700
701
|
if (lastOverflow) {
|
|
701
|
-
throw new
|
|
702
|
+
throw new HarnessError('CONTEXT_WINDOW_EXCEEDED', [], { cause: lastOverflow });
|
|
702
703
|
}
|
|
703
704
|
throw new Error('context recovery produced no completion candidate');
|
|
704
705
|
}
|
|
@@ -997,7 +998,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
997
998
|
promptParts = promptUsageParts({ system, toolSchemas: sdkTools });
|
|
998
999
|
logSelection();
|
|
999
1000
|
}
|
|
1000
|
-
|
|
1001
|
+
log.debug('round start', {
|
|
1001
1002
|
round,
|
|
1002
1003
|
turns: history.length,
|
|
1003
1004
|
tools: sdkTools?.length ?? 0,
|
|
@@ -1025,7 +1026,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1025
1026
|
}
|
|
1026
1027
|
history = recovered.history;
|
|
1027
1028
|
collectStats(stats, final.stats, final.stopReason);
|
|
1028
|
-
|
|
1029
|
+
log.debug('round stats', { round, wallMs: Date.now() - roundStarted, ...final.stats });
|
|
1029
1030
|
const markup = stripToolResponseBlocks(truncateAtControlToken(final.raw.fullText) || recovered.unproven || '');
|
|
1030
1031
|
let toolCalls = final.toolCalls?.length
|
|
1031
1032
|
? final.toolCalls
|
|
@@ -1040,7 +1041,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1040
1041
|
toolCalls = rescued;
|
|
1041
1042
|
}
|
|
1042
1043
|
if (toolCalls.length) {
|
|
1043
|
-
|
|
1044
|
+
log.info('recovered printed skill load', { round });
|
|
1044
1045
|
}
|
|
1045
1046
|
}
|
|
1046
1047
|
if (!toolCalls.length && enabled.some((tool) => tool.name === SKILL_TOOL_NAME)) {
|
|
@@ -1051,7 +1052,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1051
1052
|
nudgedSkills.add(unquoteArg(call.arguments.name));
|
|
1052
1053
|
}
|
|
1053
1054
|
if (toolCalls.length) {
|
|
1054
|
-
|
|
1055
|
+
log.info('recovered skill-named call', { round });
|
|
1055
1056
|
}
|
|
1056
1057
|
}
|
|
1057
1058
|
if (!toolCalls.length) {
|
|
@@ -1061,7 +1062,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1061
1062
|
}
|
|
1062
1063
|
const spoken = recovered.contentText.trim();
|
|
1063
1064
|
if (enabled.length && !narrated && !replay && NARRATED_INTENT.test(spoken)) {
|
|
1064
|
-
|
|
1065
|
+
log.info('round narrated a tool call instead of making it', { round });
|
|
1065
1066
|
narrated = true;
|
|
1066
1067
|
history = [
|
|
1067
1068
|
...history,
|
|
@@ -1075,7 +1076,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1075
1076
|
outcome = 'answered';
|
|
1076
1077
|
break;
|
|
1077
1078
|
}
|
|
1078
|
-
|
|
1079
|
+
log.info('round produced no answer', { round, mute });
|
|
1079
1080
|
history = [
|
|
1080
1081
|
...history,
|
|
1081
1082
|
{ role: 'assistant', content: spoken },
|
|
@@ -1090,7 +1091,10 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1090
1091
|
mute = 0;
|
|
1091
1092
|
narrated = false;
|
|
1092
1093
|
if (toolCalls.length > MAX_CALLS_PER_ROUND) {
|
|
1093
|
-
|
|
1094
|
+
log.info('round over the call cap; the rest are dropped', {
|
|
1095
|
+
round,
|
|
1096
|
+
calls: toolCalls.length
|
|
1097
|
+
});
|
|
1094
1098
|
toolCalls = toolCalls.slice(0, MAX_CALLS_PER_ROUND);
|
|
1095
1099
|
}
|
|
1096
1100
|
const resolved = await resolveToolCalls(toolCalls, {
|
|
@@ -1161,7 +1165,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1161
1165
|
let execution;
|
|
1162
1166
|
if (priorFailure) {
|
|
1163
1167
|
blockedRepeats++;
|
|
1164
|
-
|
|
1168
|
+
log.info('repeat of a call that already failed; answered without running it', {
|
|
1165
1169
|
tool: call.name,
|
|
1166
1170
|
blockedRepeats
|
|
1167
1171
|
});
|
|
@@ -1169,7 +1173,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1169
1173
|
}
|
|
1170
1174
|
else if (priorSuccess) {
|
|
1171
1175
|
blockedRepeats++;
|
|
1172
|
-
|
|
1176
|
+
log.info('repeat of a read that already answered; not run again', {
|
|
1173
1177
|
tool: call.name,
|
|
1174
1178
|
blockedRepeats
|
|
1175
1179
|
});
|
|
@@ -1241,7 +1245,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1241
1245
|
runOutcomes.record(output, executionOutcome);
|
|
1242
1246
|
if (searchCapped) {
|
|
1243
1247
|
cappedCalls++;
|
|
1244
|
-
|
|
1248
|
+
log.info('web_search capped', { searches: webSearches });
|
|
1245
1249
|
}
|
|
1246
1250
|
else if (tool?.name === WEB_SEARCH_TOOL_NAME &&
|
|
1247
1251
|
!rejection &&
|
|
@@ -1362,7 +1366,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1362
1366
|
}
|
|
1363
1367
|
said = `${recovered.contentText}\n${answer}`;
|
|
1364
1368
|
if (!said.trim()) {
|
|
1365
|
-
|
|
1369
|
+
log.info('degenerate wrap-up', { outcome });
|
|
1366
1370
|
yield {
|
|
1367
1371
|
type: 'content',
|
|
1368
1372
|
text: degenerateAnswer(outcome, history, runOutcomes.facts())
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log, logEnabled } from '../log.js';
|
|
2
2
|
export function logToolPrompt({ promptPrefix, modelId, cacheKey, agentId, messageCount, responseFormat, tools = [], definitions, activeSkills, skillsSelected, enabledSkills, mentionedSkill, loadedSkills, toolOps }) {
|
|
3
|
-
if (!
|
|
3
|
+
if (!logEnabled('debug'))
|
|
4
4
|
return;
|
|
5
|
-
|
|
5
|
+
log.debug('sdk tool definitions', {
|
|
6
6
|
promptPrefix,
|
|
7
7
|
modelId,
|
|
8
8
|
cacheKey: cacheKey ?? null,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
|
+
import { HarnessError } from '../errors.js';
|
|
2
3
|
import { errorMessage } from '../guards.js';
|
|
3
4
|
import { AbortedError } from '../helpers/race-abort.js';
|
|
4
|
-
import {
|
|
5
|
+
import { log } from '../log.js';
|
|
5
6
|
import { buildWavBuffer } from '../wav-encode.js';
|
|
6
7
|
import { normalizeForSpeech } from './speech-text.js';
|
|
7
8
|
export const SUPERTONIC_SAMPLE_RATE = 44100;
|
|
@@ -82,7 +83,7 @@ async function readHead(seconds, signal) {
|
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
catch (err) {
|
|
85
|
-
|
|
86
|
+
log.warn('audio decode/resample failed', { error: errorMessage(err) });
|
|
86
87
|
return null;
|
|
87
88
|
}
|
|
88
89
|
return head;
|
|
@@ -190,7 +191,7 @@ export async function* speakStreamOnce(sdk, modelId, text) {
|
|
|
190
191
|
session.end();
|
|
191
192
|
}
|
|
192
193
|
catch (err) {
|
|
193
|
-
|
|
194
|
+
log.warn('tts stream input failed', { err: errorMessage(err) });
|
|
194
195
|
inputError = err;
|
|
195
196
|
destroyQuietly(session);
|
|
196
197
|
}
|
|
@@ -208,7 +209,7 @@ export async function* speakStreamOnce(sdk, modelId, text) {
|
|
|
208
209
|
throw inputError;
|
|
209
210
|
}
|
|
210
211
|
if (wrotePronounceable && !yieldedAudio) {
|
|
211
|
-
throw new
|
|
212
|
+
throw new HarnessError('TTS_NO_AUDIO');
|
|
212
213
|
}
|
|
213
214
|
}
|
|
214
215
|
catch (err) {
|
|
@@ -236,7 +237,7 @@ function writeSpeakable(session, fragment) {
|
|
|
236
237
|
export function speakable(text) {
|
|
237
238
|
const { text: spoken, dropped } = normalizeForSpeech(text);
|
|
238
239
|
if (dropped.length) {
|
|
239
|
-
|
|
240
|
+
log.info('tts dropped unspeakable characters', { chars: [...new Set(dropped)].join('') });
|
|
240
241
|
}
|
|
241
242
|
return spoken;
|
|
242
243
|
}
|
|
@@ -251,7 +252,7 @@ export async function speakOnce(sdk, modelId, text) {
|
|
|
251
252
|
samples.push(sample);
|
|
252
253
|
}
|
|
253
254
|
if (!(await result.done)) {
|
|
254
|
-
throw new
|
|
255
|
+
throw new HarnessError('TTS_INCOMPLETE');
|
|
255
256
|
}
|
|
256
257
|
return samples;
|
|
257
258
|
}
|
package/dist-lib/lib/connect.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
+
import { HarnessError } from './errors.js';
|
|
4
5
|
import { settleOnClose } from './rpc-session.js';
|
|
5
6
|
import { harnessClient } from '../spec/calls.js';
|
|
6
7
|
import HRPC from '../spec/hrpc/index.js';
|
|
7
8
|
import { unsupportedKvCacheDeletion } from './capabilities/kv-cache.js';
|
|
8
9
|
import { readModelSourcePreview } from './capabilities/preview-model-source.js';
|
|
9
|
-
import {
|
|
10
|
+
import { log } from './log.js';
|
|
10
11
|
import { assertEmbedDevice } from './models/embed-config.js';
|
|
11
12
|
import { addonTaskType } from './models/model-task.js';
|
|
12
13
|
import { errorCode, errorMessage, isRecord } from './guards.js';
|
|
@@ -91,7 +92,7 @@ export class RemoteHarness extends EventEmitter {
|
|
|
91
92
|
await session.client.setCustomModels({ models: this._customModels });
|
|
92
93
|
}
|
|
93
94
|
catch (err) {
|
|
94
|
-
|
|
95
|
+
log.warn('custom model restore failed', { message: errorMessage(err) });
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
return session;
|
|
@@ -159,11 +160,11 @@ export class RemoteHarness extends EventEmitter {
|
|
|
159
160
|
return;
|
|
160
161
|
}
|
|
161
162
|
context.signal?.addEventListener?.('abort', onAbort, { once: true });
|
|
162
|
-
|
|
163
|
+
log.debug('client wire open', { model: context.model, capability: open.capability });
|
|
163
164
|
for await (const event of frames(duplex)) {
|
|
164
165
|
received++;
|
|
165
166
|
if (event.type !== 'content' && event.type !== 'thinking') {
|
|
166
|
-
|
|
167
|
+
log.debug('client frame', { type: event.type, n: received });
|
|
167
168
|
}
|
|
168
169
|
if (event.type === 'done') {
|
|
169
170
|
break;
|
|
@@ -188,7 +189,7 @@ export class RemoteHarness extends EventEmitter {
|
|
|
188
189
|
duplex.write({ type: 'decision', callId, approved: false });
|
|
189
190
|
}
|
|
190
191
|
})
|
|
191
|
-
.catch((err) =>
|
|
192
|
+
.catch((err) => log.warn('approval reply failed', { callId, message: errorMessage(err) }));
|
|
192
193
|
continue;
|
|
193
194
|
}
|
|
194
195
|
if (event.type === 'attach-ask') {
|
|
@@ -207,7 +208,7 @@ export class RemoteHarness extends EventEmitter {
|
|
|
207
208
|
});
|
|
208
209
|
}
|
|
209
210
|
})
|
|
210
|
-
.catch((err) =>
|
|
211
|
+
.catch((err) => log.warn('attach reply failed', { callId, message: errorMessage(err) }));
|
|
211
212
|
continue;
|
|
212
213
|
}
|
|
213
214
|
if (event.type === 'fetch-ask') {
|
|
@@ -224,7 +225,7 @@ export class RemoteHarness extends EventEmitter {
|
|
|
224
225
|
}
|
|
225
226
|
duplex.write({ type: 'fetch-result', callId, message: errorMessage(err) });
|
|
226
227
|
})
|
|
227
|
-
.catch((err) =>
|
|
228
|
+
.catch((err) => log.warn('fetch reply failed', { callId, message: errorMessage(err) }));
|
|
228
229
|
continue;
|
|
229
230
|
}
|
|
230
231
|
if (event.type === 'drive-ask') {
|
|
@@ -247,7 +248,7 @@ export class RemoteHarness extends EventEmitter {
|
|
|
247
248
|
});
|
|
248
249
|
}
|
|
249
250
|
})
|
|
250
|
-
.catch((err) =>
|
|
251
|
+
.catch((err) => log.warn('drive reply failed', { callId, message: errorMessage(err) }));
|
|
251
252
|
continue;
|
|
252
253
|
}
|
|
253
254
|
if (event.type === 'rag-ask') {
|
|
@@ -266,7 +267,7 @@ export class RemoteHarness extends EventEmitter {
|
|
|
266
267
|
});
|
|
267
268
|
}
|
|
268
269
|
})
|
|
269
|
-
.catch((err) =>
|
|
270
|
+
.catch((err) => log.warn('rag reply failed', { callId, message: errorMessage(err) }));
|
|
270
271
|
continue;
|
|
271
272
|
}
|
|
272
273
|
if (event.type === 'question-ask') {
|
|
@@ -290,7 +291,7 @@ export class RemoteHarness extends EventEmitter {
|
|
|
290
291
|
}
|
|
291
292
|
yield wire;
|
|
292
293
|
}
|
|
293
|
-
|
|
294
|
+
log.info('client wire ended', { received });
|
|
294
295
|
}
|
|
295
296
|
catch (err) {
|
|
296
297
|
if (context.signal?.aborted) {
|
|
@@ -302,7 +303,7 @@ export class RemoteHarness extends EventEmitter {
|
|
|
302
303
|
throw died(`harness died mid-run: ${errorMessage(err)}`);
|
|
303
304
|
}
|
|
304
305
|
finally {
|
|
305
|
-
|
|
306
|
+
log.debug('client run end', { received, aborted: Boolean(context.signal?.aborted) });
|
|
306
307
|
context.signal?.removeEventListener?.('abort', onAbort);
|
|
307
308
|
duplex?.destroy();
|
|
308
309
|
closed = true;
|
|
@@ -705,10 +706,10 @@ function abortable(promise, signal) {
|
|
|
705
706
|
return new Promise((resolve, reject) => {
|
|
706
707
|
if (signal.aborted) {
|
|
707
708
|
promise.then(noop, noop);
|
|
708
|
-
reject(new
|
|
709
|
+
reject(new HarnessError('EMBED_ABORTED'));
|
|
709
710
|
return;
|
|
710
711
|
}
|
|
711
|
-
const onAbort = () => reject(new
|
|
712
|
+
const onAbort = () => reject(new HarnessError('EMBED_ABORTED'));
|
|
712
713
|
signal.addEventListener?.('abort', onAbort, { once: true });
|
|
713
714
|
promise.then(resolve, reject).finally(() => signal.removeEventListener?.('abort', onAbort));
|
|
714
715
|
});
|
|
@@ -853,7 +854,7 @@ function answerQuestion(event, { askUser, reply }) {
|
|
|
853
854
|
options: Array.isArray(event.options) ? event.options.map(String) : undefined
|
|
854
855
|
}))
|
|
855
856
|
.then((answer) => reply({ type: 'question-result', callId, answer: answer ?? '' }), (err) => reply({ type: 'question-result', callId, message: errorMessage(err) }))
|
|
856
|
-
.catch((err) =>
|
|
857
|
+
.catch((err) => log.warn('question reply failed', { callId, message: errorMessage(err) }));
|
|
857
858
|
}
|
|
858
859
|
async function bridgeRag(event, context) {
|
|
859
860
|
if (!context.ragSearch) {
|
|
@@ -878,15 +879,15 @@ async function* frames(stream) {
|
|
|
878
879
|
let error = null;
|
|
879
880
|
stream.on('data', (frame) => queue.push(frame));
|
|
880
881
|
stream.on('end', () => {
|
|
881
|
-
|
|
882
|
+
log.debug('client stream event', { event: 'end' });
|
|
882
883
|
queue.end();
|
|
883
884
|
});
|
|
884
885
|
stream.on('close', () => {
|
|
885
|
-
|
|
886
|
+
log.debug('client stream event', { event: 'close' });
|
|
886
887
|
queue.end();
|
|
887
888
|
});
|
|
888
889
|
stream.on('error', (err) => {
|
|
889
|
-
|
|
890
|
+
log.warn('client stream event', { event: 'error', err: err.message });
|
|
890
891
|
error = err;
|
|
891
892
|
queue.end();
|
|
892
893
|
});
|