@qvac/harness 0.8.5 → 0.8.7
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 +1278 -472
- package/README.md +49 -0
- package/dist-lib/index.d.ts +42 -2
- package/dist-lib/index.js +447 -58
- package/dist-lib/lib/audio-transcode-bare.d.ts +1 -1
- package/dist-lib/lib/audio-transcode-bare.js +22 -25
- package/dist-lib/lib/audio-transcode-node.d.ts +1 -1
- package/dist-lib/lib/audio-transcode-node.js +1 -3
- package/dist-lib/lib/capabilities/audio-resample.d.ts +1 -1
- package/dist-lib/lib/capabilities/audio-resample.js +7 -8
- package/dist-lib/lib/capabilities/completion.d.ts +6 -3
- package/dist-lib/lib/capabilities/completion.js +96 -49
- package/dist-lib/lib/capabilities/download.d.ts +3 -1
- package/dist-lib/lib/capabilities/download.js +1 -1
- package/dist-lib/lib/capabilities/preview-model-source.d.ts +1 -0
- package/dist-lib/lib/capabilities/preview-model-source.js +1 -0
- package/dist-lib/lib/capabilities/tool-selection.d.ts +1 -3
- package/dist-lib/lib/capabilities/tool-selection.js +2 -4
- package/dist-lib/lib/capabilities/tool-turns.d.ts +0 -1
- package/dist-lib/lib/capabilities/tool-turns.js +0 -6
- package/dist-lib/lib/capabilities/usage-breakdown.js +4 -1
- package/dist-lib/lib/capabilities/voice.d.ts +5 -1
- package/dist-lib/lib/capabilities/voice.js +94 -26
- package/dist-lib/lib/connect.d.ts +4 -1
- package/dist-lib/lib/connect.js +16 -1
- package/dist-lib/lib/contract-types.d.ts +12 -0
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +1 -0
- package/dist-lib/lib/helpers/tool-call-markup.js +3 -0
- package/dist-lib/lib/models/context-choice.d.ts +15 -0
- package/dist-lib/lib/models/context-choice.js +55 -0
- package/dist-lib/lib/models/download-gate.d.ts +20 -0
- package/dist-lib/lib/models/download-gate.js +73 -0
- package/dist-lib/lib/models/memory-cap.d.ts +13 -0
- package/dist-lib/lib/models/memory-cap.js +21 -0
- package/dist-lib/lib/models/model-catalog-constants.d.ts +3 -1
- package/dist-lib/lib/models/model-catalog-constants.js +9 -1
- package/dist-lib/lib/models/model-catalog.d.ts +6 -2
- package/dist-lib/lib/models/model-catalog.js +35 -3
- package/dist-lib/lib/models/model-handle.d.ts +8 -1
- package/dist-lib/lib/models/model-handle.js +20 -2
- package/dist-lib/lib/models/model-list.d.ts +8 -1
- package/dist-lib/lib/models/model-list.js +23 -0
- package/dist-lib/lib/models/model-pool.d.ts +35 -5
- package/dist-lib/lib/models/model-pool.js +97 -32
- package/dist-lib/lib/models/sdk-faults.d.ts +1 -0
- package/dist-lib/lib/models/sdk-faults.js +6 -0
- package/dist-lib/lib/office/engine/pptx/picture-area.d.ts +23 -0
- package/dist-lib/lib/office/engine/pptx/picture-area.js +84 -0
- package/dist-lib/lib/office/engine/pptx/presentation.d.ts +2 -0
- package/dist-lib/lib/office/engine/pptx/presentation.js +55 -17
- package/dist-lib/lib/office/engine/pptx/types.d.ts +1 -0
- package/dist-lib/lib/office/engine/xlsx/workbook.d.ts +1 -0
- package/dist-lib/lib/office/engine/xlsx/workbook.js +34 -3
- package/dist-lib/lib/serve.js +23 -3
- package/dist-lib/lib/spawn.js +4 -10
- package/dist-lib/lib/tools/exec.d.ts +4 -1
- package/dist-lib/lib/tools/exec.js +55 -11
- package/dist-lib/lib/tools/generate-image.d.ts +4 -2
- package/dist-lib/lib/tools/generate-image.js +5 -2
- package/dist-lib/lib/tools/generate-music.d.ts +4 -2
- package/dist-lib/lib/tools/generate-music.js +5 -2
- package/dist-lib/lib/tools/office/ops.js +1 -1
- package/dist-lib/lib/tools/office.js +29 -6
- package/dist-lib/lib/tools/skills/catalog.d.ts +1 -0
- package/dist-lib/lib/tools/skills/catalog.js +2 -0
- package/dist-lib/lib/tools/skills/cli-schema-check.d.ts +1 -0
- package/dist-lib/lib/tools/skills/cli-schema-check.js +3 -0
- package/dist-lib/lib/tools/skills/cli-schema-load.js +10 -0
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +2 -0
- package/dist-lib/lib/tools/skills/cli-schema.js +6 -3
- package/dist-lib/lib/tools/skills/frontmatter-parser.d.ts +1 -0
- package/dist-lib/lib/tools/skills/manifest-parse.d.ts +1 -0
- package/dist-lib/lib/tools/skills/manifest-parse.js +12 -0
- package/dist-lib/lib/tools/skills/mention.d.ts +0 -1
- package/dist-lib/lib/tools/skills/mention.js +0 -3
- package/dist-lib/lib/tools/translate.d.ts +3 -1
- package/dist-lib/lib/tools/translate.js +6 -1
- package/dist-lib/schema/harness.js +43 -4
- package/dist-lib/spec/bind.js +9 -0
- package/dist-lib/spec/calls.js +3 -0
- package/dist-lib/spec/hrpc/hrpc.json +39 -0
- package/dist-lib/spec/hrpc/index.js +39 -3
- package/dist-lib/spec/hrpc/messages.js +101 -13
- package/dist-lib/spec/hyperschema/index.js +101 -13
- package/dist-lib/spec/hyperschema/schema.json +62 -1
- package/dist-lib/spec/hyperschema/types.d.ts +20 -2
- package/package.json +8 -10
- package/worker/harness-sidecar.mjs +2 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
+
export declare function decodeToPcm16kMono(input: Uint8Array): AsyncGenerator<Uint8Array>;
|
|
1
2
|
export declare function transcodeToWav16kMono(input: Uint8Array): Promise<Uint8Array | null>;
|
|
2
|
-
export declare function transcodeToPcm16kMono(input: Uint8Array): Promise<Uint8Array | null>;
|
|
@@ -92,13 +92,12 @@ const YIELD_EVERY_PACKETS = 32;
|
|
|
92
92
|
function yieldToLoop() {
|
|
93
93
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
94
94
|
}
|
|
95
|
-
async function decodeToPcmChunks(inputFormat, { stream, decoder, rate }) {
|
|
96
|
-
const chunks = [];
|
|
95
|
+
async function* decodeToPcmChunks(inputFormat, { stream, decoder, rate }) {
|
|
97
96
|
const packet = new ffmpeg.Packet();
|
|
98
97
|
const flush = new ffmpeg.Packet();
|
|
99
98
|
const frame = new ffmpeg.Frame();
|
|
100
99
|
const samples = new ffmpeg.Samples();
|
|
101
|
-
const target = { frame, samples, chunks, rate };
|
|
100
|
+
const target = { frame, samples, chunks: [], rate };
|
|
102
101
|
let resampler = null;
|
|
103
102
|
let packets = 0;
|
|
104
103
|
try {
|
|
@@ -107,13 +106,15 @@ async function decodeToPcmChunks(inputFormat, { stream, decoder, rate }) {
|
|
|
107
106
|
resampler = drainDecodedFrames(decoder, resampler, target);
|
|
108
107
|
}
|
|
109
108
|
packet.unref();
|
|
109
|
+
yield* target.chunks.splice(0);
|
|
110
110
|
if (++packets % YIELD_EVERY_PACKETS === 0) {
|
|
111
111
|
await yieldToLoop();
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
decoder.sendPacket(flush);
|
|
115
115
|
resampler = drainDecodedFrames(decoder, resampler, target);
|
|
116
|
-
chunks.
|
|
116
|
+
yield* target.chunks.splice(0);
|
|
117
|
+
yield* flushResampler(resampler, samples, rate);
|
|
117
118
|
}
|
|
118
119
|
finally {
|
|
119
120
|
resampler?.destroy();
|
|
@@ -121,35 +122,31 @@ async function decodeToPcmChunks(inputFormat, { stream, decoder, rate }) {
|
|
|
121
122
|
flush.destroy();
|
|
122
123
|
packet.destroy();
|
|
123
124
|
}
|
|
124
|
-
return chunks;
|
|
125
125
|
}
|
|
126
|
-
async function
|
|
127
|
-
|
|
126
|
+
export async function* decodeToPcm16kMono(input) {
|
|
127
|
+
const inputFormat = new ffmpeg.InputFormatContext(bufferReadIO(input));
|
|
128
128
|
let decoder = null;
|
|
129
129
|
try {
|
|
130
|
-
const
|
|
131
|
-
inputFormat = format;
|
|
132
|
-
const stream = format.getBestStream(ffmpeg.constants.mediaTypes.AUDIO);
|
|
130
|
+
const stream = inputFormat.getBestStream(ffmpeg.constants.mediaTypes.AUDIO);
|
|
133
131
|
decoder = stream.decoder();
|
|
134
132
|
decoder.open();
|
|
135
|
-
|
|
136
|
-
return chunks.length === 0 ? null : b4a.concat(chunks);
|
|
137
|
-
}
|
|
138
|
-
catch (e) {
|
|
139
|
-
hlog('audio decode/resample failed', { error: errorMessage(e) });
|
|
140
|
-
return null;
|
|
133
|
+
yield* decodeToPcmChunks(inputFormat, { stream, decoder, rate: TARGET_SAMPLE_RATE });
|
|
141
134
|
}
|
|
142
135
|
finally {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
inputFormat?.destroy();
|
|
136
|
+
decoder?.destroy();
|
|
137
|
+
inputFormat.destroy();
|
|
147
138
|
}
|
|
148
139
|
}
|
|
149
140
|
export async function transcodeToWav16kMono(input) {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
141
|
+
const chunks = [];
|
|
142
|
+
try {
|
|
143
|
+
for await (const chunk of decodeToPcm16kMono(input)) {
|
|
144
|
+
chunks.push(chunk);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
hlog('audio decode/resample failed', { error: errorMessage(e) });
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
return chunks.length === 0 ? null : buildWavBuffer(b4a.concat(chunks));
|
|
155
152
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function transcodeToWav16kMono(_input: Uint8Array): Promise<Uint8Array | null>;
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function decodeToPcm16kMono(_input: Uint8Array): AsyncGenerator<Uint8Array>;
|
|
@@ -11,5 +11,5 @@ export interface ResampledAudio {
|
|
|
11
11
|
readonly transcoded: boolean;
|
|
12
12
|
}
|
|
13
13
|
export declare function resampleForTranscription(bytes: Uint8Array): Promise<ResampledAudio>;
|
|
14
|
-
export declare function pcmForTranscription(bytes: Uint8Array):
|
|
14
|
+
export declare function pcmForTranscription(bytes: Uint8Array): AsyncGenerator<Uint8Array>;
|
|
15
15
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
|
-
import {
|
|
2
|
+
import { decodeToPcm16kMono, transcodeToWav16kMono } from '#audio-transcode';
|
|
3
3
|
const TARGET_SAMPLE_RATE = 16000;
|
|
4
4
|
const TARGET_CHANNELS = 1;
|
|
5
5
|
const TARGET_BITS_PER_SAMPLE = 16;
|
|
@@ -73,12 +73,11 @@ function wavPcmData(bytes) {
|
|
|
73
73
|
}
|
|
74
74
|
return null;
|
|
75
75
|
}
|
|
76
|
-
export async function pcmForTranscription(bytes) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
76
|
+
export async function* pcmForTranscription(bytes) {
|
|
77
|
+
const pcm = wavFormat(bytes) && !needsResample(bytes) ? wavPcmData(bytes) : null;
|
|
78
|
+
if (pcm) {
|
|
79
|
+
yield pcm;
|
|
80
|
+
return;
|
|
82
81
|
}
|
|
83
|
-
|
|
82
|
+
yield* decodeToPcm16kMono(bytes);
|
|
84
83
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompletionFinal, Tool as SdkTool, ToolDialect } from '@qvac/sdk';
|
|
1
|
+
import type { CompletionEvent as SdkCompletionEvent, CompletionFinal, Tool as SdkTool, ToolDialect } from '@qvac/sdk';
|
|
2
2
|
import type { AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
3
3
|
import { type TextEvent } from '../helpers/thinking.ts';
|
|
4
4
|
import type { ToolCallDisplay } from '../tool-call-display.ts';
|
|
@@ -125,6 +125,7 @@ export interface ToolExecuteContext {
|
|
|
125
125
|
skills?: readonly string[] | undefined;
|
|
126
126
|
skillsLoadedThisTurn?: readonly string[] | undefined;
|
|
127
127
|
ctxSize?: number | undefined;
|
|
128
|
+
session?: string | undefined;
|
|
128
129
|
image?: ImageSettings | undefined;
|
|
129
130
|
settings?: Record<string, unknown> | undefined;
|
|
130
131
|
turnImages?: {
|
|
@@ -266,7 +267,7 @@ export interface InferenceTokens {
|
|
|
266
267
|
export type CompletionEvent = TextEvent | ToolCallEvent | ToolResultEvent | ToolActivityEvent | ErrorEvent | AbortedEvent | MetricsEvent | InferenceCallEvent | InferenceSentEvent;
|
|
267
268
|
export interface CompletionSdkResult {
|
|
268
269
|
requestId?: string;
|
|
269
|
-
|
|
270
|
+
events: AsyncIterable<SdkCompletionEvent>;
|
|
270
271
|
final: Promise<CompletionFinal>;
|
|
271
272
|
}
|
|
272
273
|
export type ResponseFormat = {
|
|
@@ -308,10 +309,12 @@ interface CompletionArgs {
|
|
|
308
309
|
agent?: Agent;
|
|
309
310
|
ctxSize?: number;
|
|
310
311
|
cacheKey?: string;
|
|
312
|
+
session?: string;
|
|
311
313
|
thinkingDefault?: string;
|
|
312
314
|
generationParams?: LlmGenerationParams;
|
|
313
315
|
responseFormat?: ResponseFormat;
|
|
314
316
|
messages: Message[];
|
|
317
|
+
typedText?: string;
|
|
315
318
|
tools?: ToolDefinition[];
|
|
316
319
|
skillCatalog?: readonly CompletionSkillEntry[];
|
|
317
320
|
approve?: ApproveFn;
|
|
@@ -332,5 +335,5 @@ export declare function toSdkTool(tool: ToolDefinition): SdkTool;
|
|
|
332
335
|
export type CompletionSkillEntry = SkillCatalogEntry & {
|
|
333
336
|
readonly status?: SkillState;
|
|
334
337
|
};
|
|
335
|
-
export declare function completion(sdk: CompletionSdk, { modelId, acquireRound, model, agent, ctxSize, cacheKey, thinkingDefault, generationParams: generationParamsOverride, responseFormat, messages: storedMessages, tools: allTools, skillCatalog, approve, attach, fetchAttachment, ragSearch, progress, askUser, signal, toolConfigs, fsRoots, fsWriteRoots, captureInferenceCalls }: CompletionArgs): AsyncGenerator<CompletionEvent>;
|
|
338
|
+
export declare function completion(sdk: CompletionSdk, { modelId, acquireRound, model, agent, ctxSize, cacheKey, session, thinkingDefault, generationParams: generationParamsOverride, responseFormat, messages: storedMessages, typedText, tools: allTools, skillCatalog, approve, attach, fetchAttachment, ragSearch, progress, askUser, signal, toolConfigs, fsRoots, fsWriteRoots, captureInferenceCalls }: CompletionArgs): AsyncGenerator<CompletionEvent>;
|
|
336
339
|
export {};
|
|
@@ -8,14 +8,14 @@ import { AbortedError, raceAbort } from '../helpers/race-abort.js';
|
|
|
8
8
|
import { raceTimeout } from '../helpers/race-timeout.js';
|
|
9
9
|
import { createRunOutcomeTracker, degenerateAnswer, unnamedWrites, writeReport } from '../helpers/run-outcome.js';
|
|
10
10
|
import { splitThinking } from '../helpers/thinking.js';
|
|
11
|
-
import { clipRawCall, normalizeToolArgs, recoverMarkupToolCalls, recoverPrintedSkillLoad, recoverSkillNamedCall, stripToolCallMarkup, stripToolResponseBlocks, toolCallMarkup } from '../helpers/tool-call-markup.js';
|
|
11
|
+
import { clipRawCall, hasMarkupCall, normalizeToolArgs, recoverMarkupToolCalls, recoverPrintedSkillLoad, recoverSkillNamedCall, stripToolCallMarkup, stripToolResponseBlocks, toolCallMarkup } from '../helpers/tool-call-markup.js';
|
|
12
12
|
import { INTENT_PARAM, INTENT_PARAM_DESCRIPTION, takeToolIntent } from '../helpers/tool-intent.js';
|
|
13
13
|
import { HookPipeline } from '../hooks/pipeline.js';
|
|
14
14
|
import { fallbackToolLabel } from '../helpers/tool-label.js';
|
|
15
15
|
import { hlog } from '../log.js';
|
|
16
16
|
import { resolveLlmGenerationParams } from '../models/llm-presets.js';
|
|
17
|
-
import {
|
|
18
|
-
import { isContextOverflowError, isSdkModelWedgedError } from '../models/sdk-faults.js';
|
|
17
|
+
import { toolDialectFor } from '../models/model-catalog.js';
|
|
18
|
+
import { isContextOverflowError, isGrammarOverrunError, isSdkModelWedgedError } from '../models/sdk-faults.js';
|
|
19
19
|
import { annotateLastUserTime, skillsPromptSection, systemPromptParts } from '../prompt.js';
|
|
20
20
|
import { isMetadataMethod } from '../tools/mcp/protocol.js';
|
|
21
21
|
import { unquoteArg } from '../tools/model-args.js';
|
|
@@ -25,11 +25,11 @@ import { promptUsageParts, usageBreakdown } from './usage-breakdown.js';
|
|
|
25
25
|
import { collectToolGrants, enabledTools, isSelected, skillIsActive, skillsAreSelected } from './tool-selection.js';
|
|
26
26
|
import { guardToolCall } from '../tools/outbound-guard.js';
|
|
27
27
|
import { isAvailableState } from '../tools/skills/skill-state-constants.js';
|
|
28
|
-
import { resolveImplicitSkill, resolveSkillMention
|
|
28
|
+
import { resolveImplicitSkill, resolveSkillMention } from '../tools/skills/mention.js';
|
|
29
29
|
import { NETWORK_TOOLSETS, permittedTools } from '../tools/toolsets.js';
|
|
30
30
|
import { promptPrefixHash } from './prompt-prefix.js';
|
|
31
31
|
import { logToolPrompt } from './tool-prompt-log.js';
|
|
32
|
-
import { DIRECT_CALL_ID, deliversFile, FILE_DELIVERED_NUDGE, loadedSkillFilesFrom, loadedSkillsFrom, TOOL_CONTINUATION_NUDGE, toolResultTurn
|
|
32
|
+
import { DIRECT_CALL_ID, deliversFile, FILE_DELIVERED_NUDGE, loadedSkillFilesFrom, loadedSkillsFrom, TOOL_CONTINUATION_NUDGE, toolResultTurn } from './tool-turns.js';
|
|
33
33
|
export function imageSettingsFrom(agent) {
|
|
34
34
|
if (!agent)
|
|
35
35
|
return undefined;
|
|
@@ -139,14 +139,11 @@ function explicitlyDisablesWebSearch(text) {
|
|
|
139
139
|
return (/\b(?:do\s+not|don't|dont|never)\s+(?:search|browse|look\s+up|find|use)\s+(?:the\s+)?(?:web|internet|online)\b/i.test(text) ||
|
|
140
140
|
/\b(?:avoid|without)\s+(?:searching|browsing|using)\s+(?:the\s+)?(?:web|internet|online)\b/i.test(text));
|
|
141
141
|
}
|
|
142
|
-
function directToolHistory(history,
|
|
142
|
+
function directToolHistory(history, call, output) {
|
|
143
143
|
return [
|
|
144
144
|
...history,
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
content: toolCallMarkup([{ name: WEB_SEARCH_TOOL_NAME, arguments: args }])
|
|
148
|
-
},
|
|
149
|
-
{ role: 'tool', content: toolResultTurn(WEB_SEARCH_TOOL_NAME, DIRECT_CALL_ID, output) }
|
|
145
|
+
{ role: 'assistant', content: toolCallMarkup([call]) },
|
|
146
|
+
{ role: 'tool', content: toolResultTurn(call.name, DIRECT_CALL_ID, output) }
|
|
150
147
|
];
|
|
151
148
|
}
|
|
152
149
|
function assistantToolTurn(contentText, calls) {
|
|
@@ -155,18 +152,6 @@ function assistantToolTurn(contentText, calls) {
|
|
|
155
152
|
const markup = toolCallMarkup(calls);
|
|
156
153
|
return contentText ? `${contentText}\n\n${markup}` : markup;
|
|
157
154
|
}
|
|
158
|
-
function preloadLastUser(messages, name, body) {
|
|
159
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
160
|
-
const turn = messages[i];
|
|
161
|
-
if (turn?.role !== 'user') {
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
const next = [...messages];
|
|
165
|
-
next[i] = { ...turn, content: skillPreloadMessage(turn.content, name, body) };
|
|
166
|
-
return next;
|
|
167
|
-
}
|
|
168
|
-
return messages;
|
|
169
|
-
}
|
|
170
155
|
function collectStats(state, stats, stopReason) {
|
|
171
156
|
state.stopReason = stopReason;
|
|
172
157
|
if (!stats) {
|
|
@@ -362,10 +347,11 @@ async function executeCall(tool, args, { ctx, approve, signal, denied, sources =
|
|
|
362
347
|
return executedCall(output, outcome);
|
|
363
348
|
}
|
|
364
349
|
function runToolContext(scope) {
|
|
365
|
-
const { agent, attach, fetchAttachment, ragSearch, progress, askUser, signal, runBudget } = scope;
|
|
350
|
+
const { agent, session, attach, fetchAttachment, ragSearch, progress, askUser, signal, runBudget } = scope;
|
|
366
351
|
const image = imageSettingsFrom(agent);
|
|
367
352
|
return (turnImages = [], ctxSize) => ({
|
|
368
353
|
agentId: agent?.id ?? null,
|
|
354
|
+
session,
|
|
369
355
|
ctxSize,
|
|
370
356
|
image,
|
|
371
357
|
turnImages: turnImages.length ? turnImages : undefined,
|
|
@@ -459,6 +445,27 @@ function unprovenReplay(state, final) {
|
|
|
459
445
|
}
|
|
460
446
|
return state.unproven;
|
|
461
447
|
}
|
|
448
|
+
async function* contentDeltas(events) {
|
|
449
|
+
for await (const event of events) {
|
|
450
|
+
if (event.type === 'contentDelta') {
|
|
451
|
+
yield event.text;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async function* endAtGrammarOverrun(tokens, state) {
|
|
456
|
+
try {
|
|
457
|
+
for await (const token of tokens) {
|
|
458
|
+
state.rawText += token;
|
|
459
|
+
yield token;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
if (!isGrammarOverrunError(err)) {
|
|
464
|
+
throw err;
|
|
465
|
+
}
|
|
466
|
+
state.overrun = err;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
462
469
|
async function* runRoundTokens(result, state, { sdk, signal, startInThink = false }) {
|
|
463
470
|
const cancelThisRound = () => {
|
|
464
471
|
if (result.requestId) {
|
|
@@ -466,7 +473,8 @@ async function* runRoundTokens(result, state, { sdk, signal, startInThink = fals
|
|
|
466
473
|
}
|
|
467
474
|
};
|
|
468
475
|
signal?.addEventListener('abort', cancelThisRound, { once: true });
|
|
469
|
-
const
|
|
476
|
+
const deltas = endAtGrammarOverrun(contentDeltas(result.events), state);
|
|
477
|
+
const tokens = stopAtControlToken(deltas, (token) => {
|
|
470
478
|
state.stopToken = token;
|
|
471
479
|
});
|
|
472
480
|
let tokenEvents = 0;
|
|
@@ -501,6 +509,9 @@ async function* runRoundTokens(result, state, { sdk, signal, startInThink = fals
|
|
|
501
509
|
if (contentTail) {
|
|
502
510
|
yield { type: 'content', text: contentTail };
|
|
503
511
|
}
|
|
512
|
+
if (state.overrun) {
|
|
513
|
+
throw state.overrun;
|
|
514
|
+
}
|
|
504
515
|
hlog('stream ended', { events: tokenEvents, stopToken: state.stopToken });
|
|
505
516
|
if (state.stopToken) {
|
|
506
517
|
cancelThisRound();
|
|
@@ -526,9 +537,12 @@ function roundTokens(stats) {
|
|
|
526
537
|
};
|
|
527
538
|
return Object.values(tokens).some((value) => value !== undefined) ? tokens : undefined;
|
|
528
539
|
}
|
|
540
|
+
function acceptedFinal(contentText, fullText = contentText) {
|
|
541
|
+
return { contentText, toolCalls: [], raw: { fullText } };
|
|
542
|
+
}
|
|
529
543
|
const TRUNCATED_FINAL_MS = 2000;
|
|
530
544
|
function truncatedFinal(final, contentText) {
|
|
531
|
-
const accepted =
|
|
545
|
+
const accepted = acceptedFinal(contentText);
|
|
532
546
|
const guarded = final.catch((err) => {
|
|
533
547
|
if (isSdkModelWedgedError(err))
|
|
534
548
|
throw err;
|
|
@@ -553,7 +567,13 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
|
|
|
553
567
|
throw new AbortedError();
|
|
554
568
|
let contentText = '';
|
|
555
569
|
let emitted = false;
|
|
556
|
-
const state = {
|
|
570
|
+
const state = {
|
|
571
|
+
stopToken: null,
|
|
572
|
+
unproven: null,
|
|
573
|
+
rawContentText: '',
|
|
574
|
+
rawText: '',
|
|
575
|
+
overrun: null
|
|
576
|
+
};
|
|
557
577
|
const cacheable = candidate.history === args.history;
|
|
558
578
|
const params = {
|
|
559
579
|
modelId,
|
|
@@ -626,6 +646,14 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
|
|
|
626
646
|
};
|
|
627
647
|
}
|
|
628
648
|
catch (err) {
|
|
649
|
+
const recovered = isGrammarOverrunError(err) && hasMarkupCall(state.rawText);
|
|
650
|
+
let outcome = 'failed';
|
|
651
|
+
if (recovered) {
|
|
652
|
+
outcome = 'completed';
|
|
653
|
+
}
|
|
654
|
+
else if (isContextOverflowError(err)) {
|
|
655
|
+
outcome = 'overflowed';
|
|
656
|
+
}
|
|
629
657
|
if (args.capture) {
|
|
630
658
|
yield {
|
|
631
659
|
type: 'inference-sent',
|
|
@@ -633,10 +661,21 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
|
|
|
633
661
|
droppedTurns: candidate.droppedTurns,
|
|
634
662
|
messages: undefined,
|
|
635
663
|
tokens: undefined,
|
|
636
|
-
outcome
|
|
664
|
+
outcome,
|
|
637
665
|
reason: errorMessage(err)
|
|
638
666
|
};
|
|
639
667
|
}
|
|
668
|
+
if (recovered) {
|
|
669
|
+
hlog('grammar overrun recovered', { round: args.round });
|
|
670
|
+
return {
|
|
671
|
+
contentText,
|
|
672
|
+
final: acceptedFinal(state.rawContentText, state.rawText),
|
|
673
|
+
history: candidate.history,
|
|
674
|
+
cacheable,
|
|
675
|
+
unproven: state.unproven,
|
|
676
|
+
replay: null
|
|
677
|
+
};
|
|
678
|
+
}
|
|
640
679
|
if (!isContextOverflowError(err)) {
|
|
641
680
|
throw err;
|
|
642
681
|
}
|
|
@@ -657,7 +696,7 @@ function* reportUnnamedWrites(said, landed) {
|
|
|
657
696
|
yield { type: 'content', text: unreported };
|
|
658
697
|
}
|
|
659
698
|
}
|
|
660
|
-
export async function* completion(sdk, { modelId, acquireRound, model, agent, ctxSize, cacheKey, thinkingDefault, generationParams: generationParamsOverride, responseFormat, messages: storedMessages, tools: allTools = [], skillCatalog = [], approve, attach, fetchAttachment, ragSearch, progress, askUser, signal, toolConfigs, fsRoots = [], fsWriteRoots = [], captureInferenceCalls = false }) {
|
|
699
|
+
export async function* completion(sdk, { modelId, acquireRound, model, agent, ctxSize, cacheKey, session, thinkingDefault, generationParams: generationParamsOverride, responseFormat, messages: storedMessages, typedText, tools: allTools = [], skillCatalog = [], approve, attach, fetchAttachment, ragSearch, progress, askUser, signal, toolConfigs, fsRoots = [], fsWriteRoots = [], captureInferenceCalls = false }) {
|
|
661
700
|
const pipeline = new HookPipeline();
|
|
662
701
|
const agentId = agent?.id ?? null;
|
|
663
702
|
const disabledToolsets = agent?.sandboxMode
|
|
@@ -669,6 +708,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
669
708
|
...fsRoots.filter((root) => root.kind === 'hyper' && root.writable && !fsWriteRoots.includes(root))
|
|
670
709
|
];
|
|
671
710
|
const messages = storedMessages.map(sanitizeTurn);
|
|
711
|
+
const userText = typedText === undefined ? lastUserContent(messages) : stripControlTokens(typedText);
|
|
672
712
|
const stats = { tokens: 0, timedTokens: 0, durationMs: 0, ctxSize, backends: [] };
|
|
673
713
|
let runOutcome = 'abandoned';
|
|
674
714
|
let runError;
|
|
@@ -684,6 +724,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
684
724
|
};
|
|
685
725
|
const toolContext = runToolContext({
|
|
686
726
|
agent,
|
|
727
|
+
session: session ?? sessionOf(cacheKey),
|
|
687
728
|
attach,
|
|
688
729
|
fetchAttachment,
|
|
689
730
|
ragSearch,
|
|
@@ -696,16 +737,14 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
696
737
|
const toolsSelected = isSelected(agent?.toolsScope, agent?.enabledTools);
|
|
697
738
|
const skillsSelected = skillsAreSelected(agent?.skillsScope, Boolean(agent));
|
|
698
739
|
const inContextSkills = skillCatalog.filter(skillInContext);
|
|
699
|
-
const mention = resolveSkillMention(lastUserContent(messages), inContextSkills);
|
|
700
740
|
const loadedSkills = loadedSkillsFrom(messages);
|
|
701
741
|
const loadedSkillFiles = loadedSkillFilesFrom(messages);
|
|
702
742
|
const activation = {
|
|
703
743
|
skillsSelected,
|
|
704
|
-
enabledSkills: agent?.enabledSkills ?? []
|
|
705
|
-
mention: mention?.name ?? null,
|
|
706
|
-
loadedSkills
|
|
744
|
+
enabledSkills: agent?.enabledSkills ?? []
|
|
707
745
|
};
|
|
708
746
|
const activeSkills = inContextSkills.filter((skill) => skillIsActive(skill.name, activation));
|
|
747
|
+
const mention = resolveSkillMention(userText, activeSkills);
|
|
709
748
|
const { grantedNames, grantedOwners, grantedOps } = collectToolGrants(activeSkills);
|
|
710
749
|
function selectEnabled() {
|
|
711
750
|
if (responseFormat)
|
|
@@ -753,17 +792,16 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
753
792
|
}
|
|
754
793
|
skillIcon = skillEmoji(skillName) ?? skillIcon;
|
|
755
794
|
}
|
|
756
|
-
const toolDialect = model ?
|
|
757
|
-
let turns = messages;
|
|
795
|
+
const toolDialect = model ? toolDialectFor(model) : undefined;
|
|
758
796
|
let preloadedSkill = null;
|
|
759
|
-
|
|
797
|
+
let preloadedResult = null;
|
|
798
|
+
const preload = mention ?? resolveImplicitSkill(userText, activeSkills);
|
|
760
799
|
if (preload) {
|
|
761
800
|
const loader = tools.find((tool) => tool.name === SKILL_TOOL_NAME);
|
|
762
801
|
const loaded = loader ? await loader.execute({ name: preload.name }, toolContext()) : null;
|
|
763
802
|
if (loaded && typeof loaded === 'object' && 'content' in loaded) {
|
|
764
803
|
preloadedSkill = String(loaded.content);
|
|
765
|
-
|
|
766
|
-
turns = withoutSkillLoadTurns(inlined, preload.name);
|
|
804
|
+
preloadedResult = loaded;
|
|
767
805
|
if (!loadedSkills.has(preload.name)) {
|
|
768
806
|
const preloadArgs = { name: preload.name };
|
|
769
807
|
const preloadEffect = 'read';
|
|
@@ -791,6 +829,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
791
829
|
effect: preloadEffect,
|
|
792
830
|
resource: preloadResource
|
|
793
831
|
});
|
|
832
|
+
preloadedResult = settled.result;
|
|
794
833
|
yield {
|
|
795
834
|
type: 'tool-result',
|
|
796
835
|
name: SKILL_TOOL_NAME,
|
|
@@ -833,18 +872,19 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
833
872
|
});
|
|
834
873
|
}
|
|
835
874
|
logSelection();
|
|
836
|
-
let history = annotateLastUserTime(systemContent ? [{ role: 'system', content: systemContent }, ...
|
|
837
|
-
|
|
875
|
+
let history = annotateLastUserTime(systemContent ? [{ role: 'system', content: systemContent }, ...messages] : messages);
|
|
876
|
+
if (preload && preloadedSkill !== null) {
|
|
877
|
+
history = directToolHistory(history, { name: SKILL_TOOL_NAME, arguments: { name: preload.name } }, preloadedResult);
|
|
878
|
+
history.push({ role: 'user', content: TOOL_CONTINUATION_NUDGE });
|
|
879
|
+
}
|
|
880
|
+
let roundCacheKey = cacheKey;
|
|
838
881
|
const runUsage = () => usageBreakdown(promptParts, stats.cacheTokens, model);
|
|
839
882
|
const denied = new Set();
|
|
840
883
|
const failed = new Map();
|
|
841
884
|
let blockedRepeats = 0;
|
|
842
885
|
let webSearches = 0;
|
|
843
|
-
const sources = trustedSourcesFrom(
|
|
844
|
-
|
|
845
|
-
sources.push({ origin: 'skill', text: preloadedSkill });
|
|
846
|
-
}
|
|
847
|
-
const query = explicitWebSearchQuery(lastUserContent(messages), enabled, {
|
|
886
|
+
const sources = trustedSourcesFrom(history);
|
|
887
|
+
const query = explicitWebSearchQuery(userText, enabled, {
|
|
848
888
|
skillPreloaded: preloadedSkill !== null
|
|
849
889
|
});
|
|
850
890
|
const webSearch = enabled.find((tool) => tool.name === WEB_SEARCH_TOOL_NAME);
|
|
@@ -917,7 +957,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
917
957
|
name: webSearch.name,
|
|
918
958
|
result: output
|
|
919
959
|
};
|
|
920
|
-
history = directToolHistory(history, args, output);
|
|
960
|
+
history = directToolHistory(history, { name: webSearch.name, arguments: args }, output);
|
|
921
961
|
}
|
|
922
962
|
let outcome = 'exhausted';
|
|
923
963
|
let said = '';
|
|
@@ -1021,6 +1061,11 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1021
1061
|
break;
|
|
1022
1062
|
}
|
|
1023
1063
|
hlog('round produced no answer', { round, mute });
|
|
1064
|
+
history = [
|
|
1065
|
+
...history,
|
|
1066
|
+
{ role: 'assistant', content: spoken },
|
|
1067
|
+
{ role: 'user', content: TOOL_CONTINUATION_NUDGE }
|
|
1068
|
+
];
|
|
1024
1069
|
if (mute++ < MAX_MUTE_ROUNDS) {
|
|
1025
1070
|
continue;
|
|
1026
1071
|
}
|
|
@@ -1058,9 +1103,7 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1058
1103
|
const searchCapped = tool?.name === WEB_SEARCH_TOOL_NAME && webSearches >= MAX_WEB_SEARCHES_PER_TURN;
|
|
1059
1104
|
const callCtx = {
|
|
1060
1105
|
...toolContext(turnImages, ctxSize),
|
|
1061
|
-
skills: [...loadedSkills].filter((name) =>
|
|
1062
|
-
(agent?.enabledSkills ?? []).includes(name) ||
|
|
1063
|
-
name === mention?.name),
|
|
1106
|
+
skills: [...loadedSkills].filter((name) => skillIsActive(name, activation)),
|
|
1064
1107
|
skillsLoadedThisTurn: [...loadedThisTurn],
|
|
1065
1108
|
settings: toolConfigs?.[call.name],
|
|
1066
1109
|
toolOps: grantedOps,
|
|
@@ -1325,6 +1368,10 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
|
|
|
1325
1368
|
});
|
|
1326
1369
|
}
|
|
1327
1370
|
}
|
|
1371
|
+
function sessionOf(cacheKey) {
|
|
1372
|
+
const base = cacheKey?.split('/')[0];
|
|
1373
|
+
return base ? base : undefined;
|
|
1374
|
+
}
|
|
1328
1375
|
function toolDecision({ auto, skipAll, approved }) {
|
|
1329
1376
|
if (auto)
|
|
1330
1377
|
return 'auto';
|
|
@@ -45,4 +45,6 @@ export type SdkOrLoader = DownloadSdk | (() => Promise<DownloadSdk>);
|
|
|
45
45
|
export interface DownloadEventStream extends Readable {
|
|
46
46
|
[Symbol.asyncIterator](): AsyncIterableIterator<DownloadEvent>;
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
type TerminalEvent = Exclude<DownloadEvent, DownloadProgressEvent>;
|
|
49
|
+
export declare function download(sdkOrLoader: SdkOrLoader, args: DownloadArgs, onSettled?: (event: TerminalEvent) => void): DownloadEventStream;
|
|
50
|
+
export {};
|
|
@@ -365,6 +365,7 @@ async function assess({ file, header }, taskType, deps) {
|
|
|
365
365
|
}
|
|
366
366
|
try {
|
|
367
367
|
return await deps.assessFit({
|
|
368
|
+
src: file.src,
|
|
368
369
|
sha256Checksum: file.sha256,
|
|
369
370
|
name: header.name ?? file.label,
|
|
370
371
|
contextTokens: defaultContextSize(file.src, header.contextLength)
|
|
@@ -18,11 +18,9 @@ export declare function collectToolGrants<T extends {
|
|
|
18
18
|
export interface SkillActivation {
|
|
19
19
|
skillsSelected: boolean;
|
|
20
20
|
enabledSkills: ReadonlyArray<string>;
|
|
21
|
-
mention: string | null;
|
|
22
|
-
loadedSkills: ReadonlySet<string>;
|
|
23
21
|
}
|
|
24
22
|
export declare function skillsAreSelected(scope: 'all' | 'selected' | null | undefined, hasAgent: boolean): boolean;
|
|
25
|
-
export declare function skillIsActive(name: string, { skillsSelected, enabledSkills
|
|
23
|
+
export declare function skillIsActive(name: string, { skillsSelected, enabledSkills }: SkillActivation): boolean;
|
|
26
24
|
export interface SelectableTool {
|
|
27
25
|
name: string;
|
|
28
26
|
skillGated?: boolean | undefined;
|
|
@@ -25,10 +25,8 @@ export function collectToolGrants(skills) {
|
|
|
25
25
|
export function skillsAreSelected(scope, hasAgent) {
|
|
26
26
|
return hasAgent && scope !== 'all';
|
|
27
27
|
}
|
|
28
|
-
export function skillIsActive(name, { skillsSelected, enabledSkills
|
|
29
|
-
|
|
30
|
-
return true;
|
|
31
|
-
return enabledSkills.includes(name) || name === mention || loadedSkills.has(name);
|
|
28
|
+
export function skillIsActive(name, { skillsSelected, enabledSkills }) {
|
|
29
|
+
return !skillsSelected || enabledSkills.includes(name);
|
|
32
30
|
}
|
|
33
31
|
export function isSelected(scope, list) {
|
|
34
32
|
return (scope ?? (list?.length ? 'selected' : 'all')) === 'selected';
|
|
@@ -9,4 +9,3 @@ export declare function skillLoadedTurn(name: string): string;
|
|
|
9
9
|
export declare function attachmentTurn(name: string, result: unknown): string | null;
|
|
10
10
|
export declare function loadedSkillsFrom(messages: readonly Message[]): Set<string>;
|
|
11
11
|
export declare function loadedSkillFilesFrom(messages: readonly Message[]): Map<string, Set<string>>;
|
|
12
|
-
export declare function withoutSkillLoadTurns(messages: readonly Message[], name: string): Message[];
|
|
@@ -71,12 +71,6 @@ export function loadedSkillFilesFrom(messages) {
|
|
|
71
71
|
}
|
|
72
72
|
return loaded;
|
|
73
73
|
}
|
|
74
|
-
export function withoutSkillLoadTurns(messages, name) {
|
|
75
|
-
return messages.filter((turn) => {
|
|
76
|
-
const payload = skillLoadPayload(turn);
|
|
77
|
-
return !payload || payload.name !== name || payload.file !== undefined;
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
74
|
function* skillLoadPayloads(messages) {
|
|
81
75
|
for (const turn of messages) {
|
|
82
76
|
const payload = skillLoadPayload(turn);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { toolDialectFor } from '../models/model-catalog.js';
|
|
1
2
|
import { detectToolDialect } from '../models/tool-dialect.js';
|
|
2
3
|
const QWEN35 = { prose: 4.38, schema: 4.08, toolBlockTokens: 189 };
|
|
3
4
|
const DENSITY_BY_DIALECT = new Map([['qwen35', QWEN35]]);
|
|
4
5
|
export function densityFor(modelName) {
|
|
5
|
-
const dialect = modelName
|
|
6
|
+
const dialect = modelName
|
|
7
|
+
? (toolDialectFor(modelName) ?? detectToolDialect(modelName, modelName))
|
|
8
|
+
: undefined;
|
|
6
9
|
return (dialect && DENSITY_BY_DIALECT.get(dialect)) ?? QWEN35;
|
|
7
10
|
}
|
|
8
11
|
export function promptUsageParts({ system, toolSchemas }) {
|
|
@@ -75,7 +75,11 @@ export interface TranscribeStreamingOptions {
|
|
|
75
75
|
modelId: string;
|
|
76
76
|
signal?: BareAbortSignal | undefined;
|
|
77
77
|
}
|
|
78
|
-
export
|
|
78
|
+
export interface TranscribeAudioOptions extends TranscribeStreamingOptions {
|
|
79
|
+
audio: Buffer;
|
|
80
|
+
}
|
|
81
|
+
export declare function transcribeAudio(sdk: VoiceSdk, pcm: AsyncIterable<Uint8Array>, { modelId, signal, audio }: TranscribeAudioOptions): Promise<string>;
|
|
82
|
+
export declare function transcribeStreaming(sdk: VoiceSdk, pcm: AsyncIterable<Uint8Array>, opts: TranscribeStreamingOptions): Promise<string>;
|
|
79
83
|
export declare function speakStreamOnce(sdk: VoiceSdk, modelId: string, text: AsyncIterable<string>): AsyncGenerator<SpeakResult>;
|
|
80
84
|
export declare function speakable(text: string): string;
|
|
81
85
|
export declare function speakOnce(sdk: VoiceSdk, modelId: string, text: string): Promise<number[]>;
|