@qvac/harness 0.2.8 → 0.2.9

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.
Files changed (47) hide show
  1. package/dist/harness.bundle +65 -12
  2. package/dist/prebuilds-manifest.json +8 -0
  3. package/dist-lib/index.d.ts +2 -1
  4. package/dist-lib/index.js +2 -1
  5. package/dist-lib/lib/capabilities/completion.d.ts +35 -2
  6. package/dist-lib/lib/capabilities/completion.js +84 -18
  7. package/dist-lib/lib/connect.js +1 -0
  8. package/dist-lib/lib/contract-types.d.ts +1 -0
  9. package/dist-lib/lib/serve.js +1 -0
  10. package/dist-lib/lib/wire.js +28 -0
  11. package/dist-lib/schema/harness.js +5 -2
  12. package/dist-lib/spec/hrpc/messages.js +22 -7
  13. package/dist-lib/spec/hyperschema/index.js +22 -7
  14. package/dist-lib/spec/hyperschema/schema.json +14 -1
  15. package/dist-lib/spec/hyperschema/types.d.ts +3 -2
  16. package/package.json +6 -6
  17. package/.claude/skills/asana/SKILL.md +0 -86
  18. package/.claude/skills/gh-stack/SKILL.md +0 -181
  19. package/.claude/skills/gh-stack/references/commands.md +0 -177
  20. package/.claude/skills/gh-stack/references/stack-design.md +0 -97
  21. package/.claude/skills/gh-stack/references/troubleshooting.md +0 -159
  22. package/.claude/skills/js-house-style/SKILL.md +0 -671
  23. package/.claude/skills/kvcache/SKILL.md +0 -171
  24. package/.claude/skills/pr-create/SKILL.md +0 -180
  25. package/.claude/skills/pr-review/SKILL.md +0 -414
  26. package/CLAUDE.md +0 -39
  27. package/docs/decisions/2026-05-29-kvcache-tool-turns.md +0 -75
  28. package/docs/decisions/2026-06-02-kvcache-tool-turns-reenabled.md +0 -127
  29. package/docs/decisions/2026-06-23-android-cpu-llm-defaults.md +0 -126
  30. package/docs/decisions/2026-07-29-assistant-owned-storage-layout.md +0 -88
  31. package/docs/decisions/2026-08-04-sidecar-orphan-reaping.md +0 -79
  32. package/docs/decisions/2026-08-11-pyodide-worker-thread.md +0 -68
  33. package/docs/decisions/2026-08-17-harness-hook-pipeline.md +0 -84
  34. package/docs/decisions/2026-08-17-harness-outbound-egress-guard.md +0 -176
  35. package/docs/decisions/2026-08-20-chat-prompt-prefix-is-stable.md +0 -95
  36. package/docs/decisions/2026-08-20-kvcache-static-placement.md +0 -109
  37. package/docs/decisions/2026-08-21-vendored-python-wheels.md +0 -85
  38. package/docs/decisions/2026-08-26-exec-is-not-gated-by-skills.md +0 -64
  39. package/docs/decisions/2026-08-27-writable-skills-overlay.md +0 -56
  40. package/docs/decisions/2026-09-01-skill-operation-manifests.md +0 -53
  41. package/docs/decisions/2026-09-01-usage-breakdown-is-measured.md +0 -78
  42. package/docs/decisions/2026-09-03-usage-breakdown-reported-by-the-harness.md +0 -136
  43. package/docs/decisions/2026-09-10-skill-wording-is-measured-offline.md +0 -63
  44. package/docs/decisions/2026-09-16-a-lookup-reports-the-place-it-resolved.md +0 -70
  45. package/docs/decisions/INDEX.md +0 -40
  46. package/docs/decisions/TEMPLATE.md +0 -32
  47. package/docs/kvcache-reference.md +0 -655
@@ -272,6 +272,14 @@
272
272
  "win32-arm64",
273
273
  "win32-x64"
274
274
  ],
275
+ "bare-tty/node_modules/bare-signals": [
276
+ "darwin-arm64",
277
+ "darwin-x64",
278
+ "linux-arm64",
279
+ "linux-x64",
280
+ "win32-arm64",
281
+ "win32-x64"
282
+ ],
275
283
  "bare-tty": [
276
284
  "darwin-arm64",
277
285
  "darwin-x64",
@@ -93,6 +93,7 @@ export interface CompletionContext {
93
93
  toolConfigs?: Record<string, Record<string, unknown>>;
94
94
  generationParams?: LlmGenerationParams;
95
95
  responseFormat?: ResponseFormat;
96
+ captureInferenceCalls?: boolean;
96
97
  grantedDirs?: string[];
97
98
  grantedDrives?: GrantedDrive[];
98
99
  driveOp?: DriveOpFn;
@@ -239,7 +240,7 @@ export declare class Harness extends ReadyResource {
239
240
  agent?: Agent;
240
241
  signal?: BareAbortSignal;
241
242
  }): Promise<void>;
242
- completion({ model, agent, messages, cacheKey, thinkingDefault, toolConfigs, generationParams, responseFormat, grantedDirs, grantedDrives, driveOp, grantedWriteDirs, approve, attach, fetchAttachment, ragSearch, progress, askUser, signal }: CompletionContext): AsyncGenerator<CompletionEvent>;
243
+ completion({ model, agent, messages, cacheKey, thinkingDefault, toolConfigs, generationParams, responseFormat, captureInferenceCalls, grantedDirs, grantedDrives, driveOp, grantedWriteDirs, approve, attach, fetchAttachment, ragSearch, progress, askUser, signal }: CompletionContext): AsyncGenerator<CompletionEvent>;
243
244
  private _makeRoomForCompletion;
244
245
  private _evictWedgedModel;
245
246
  private _evictAfterLeases;
package/dist-lib/index.js CHANGED
@@ -943,7 +943,7 @@ export class Harness extends ReadyResource {
943
943
  releaseModel(pool, key);
944
944
  }
945
945
  }
946
- async *completion({ model, agent, messages, cacheKey, thinkingDefault, toolConfigs, generationParams, responseFormat, grantedDirs, grantedDrives, driveOp, grantedWriteDirs, approve, attach, fetchAttachment, ragSearch, progress, askUser, signal }) {
946
+ async *completion({ model, agent, messages, cacheKey, thinkingDefault, toolConfigs, generationParams, responseFormat, captureInferenceCalls, grantedDirs, grantedDrives, driveOp, grantedWriteDirs, approve, attach, fetchAttachment, ragSearch, progress, askUser, signal }) {
947
947
  await this.ready();
948
948
  if (this.suspended)
949
949
  throw new Error('harness is suspended');
@@ -1016,6 +1016,7 @@ export class Harness extends ReadyResource {
1016
1016
  thinkingDefault,
1017
1017
  generationParams,
1018
1018
  responseFormat,
1019
+ captureInferenceCalls,
1019
1020
  messages: turns,
1020
1021
  tools: this._tools,
1021
1022
  skillCatalog: await this._completionSkills(agent?.id ?? null, model),
@@ -221,8 +221,40 @@ export type MetricsEvent = {
221
221
  ctxSize?: number | undefined;
222
222
  backends?: RunBackendUse[] | undefined;
223
223
  usage?: UsageBreakdown | undefined;
224
+ stopReason?: string | undefined;
224
225
  };
225
- export type CompletionEvent = TextEvent | ToolCallEvent | ToolResultEvent | ToolActivityEvent | ErrorEvent | AbortedEvent | MetricsEvent;
226
+ export interface InferenceCall {
227
+ round: number;
228
+ droppedTurns: number;
229
+ modelId: string;
230
+ messages: Message[];
231
+ tools: SdkTool[] | undefined;
232
+ generationParams: LlmGenerationParams | undefined;
233
+ responseFormat: ResponseFormat | undefined;
234
+ toolDialect: ToolDialect | undefined;
235
+ cacheKey: string | undefined;
236
+ promptPrefix: string;
237
+ }
238
+ export type InferenceCallEvent = {
239
+ type: 'inference-call';
240
+ call: InferenceCall;
241
+ };
242
+ export type InferenceSentEvent = {
243
+ type: 'inference-sent';
244
+ round: number;
245
+ droppedTurns: number;
246
+ messages: Message[] | undefined;
247
+ tokens: InferenceTokens | undefined;
248
+ outcome: 'completed' | 'overflowed' | 'failed';
249
+ reason: string | undefined;
250
+ };
251
+ export interface InferenceTokens {
252
+ prompt?: number | undefined;
253
+ generated?: number | undefined;
254
+ cached?: number | undefined;
255
+ timeToFirstToken?: number | undefined;
256
+ }
257
+ export type CompletionEvent = TextEvent | ToolCallEvent | ToolResultEvent | ToolActivityEvent | ErrorEvent | AbortedEvent | MetricsEvent | InferenceCallEvent | InferenceSentEvent;
226
258
  export interface CompletionSdkResult {
227
259
  requestId?: string;
228
260
  tokenStream: AsyncIterable<string>;
@@ -283,6 +315,7 @@ interface CompletionArgs {
283
315
  toolConfigs?: Record<string, Record<string, unknown>>;
284
316
  fsRoots?: readonly DriveRoot[];
285
317
  fsWriteRoots?: readonly DriveRoot[];
318
+ captureInferenceCalls?: boolean;
286
319
  }
287
320
  export declare const CONTEXT_OVERFLOW_MESSAGE = "This message is too large for the model context window.";
288
321
  export declare function trustedSourcesFrom(history: readonly Message[]): TrustedSource[];
@@ -290,5 +323,5 @@ export declare function toSdkTool(tool: ToolDefinition): SdkTool;
290
323
  export type CompletionSkillEntry = SkillCatalogEntry & {
291
324
  readonly status?: SkillState;
292
325
  };
293
- 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 }: CompletionArgs): AsyncGenerator<CompletionEvent>;
326
+ 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>;
294
327
  export {};
@@ -157,7 +157,8 @@ function preloadLastUser(messages, name, body) {
157
157
  }
158
158
  return messages;
159
159
  }
160
- function collectStats(state, stats) {
160
+ function collectStats(state, stats, stopReason) {
161
+ state.stopReason = stopReason;
161
162
  if (!stats)
162
163
  return;
163
164
  if (state.ttft === undefined && stats.timeToFirstToken)
@@ -191,7 +192,8 @@ function metricsEvent(state, usage) {
191
192
  cacheTokens: state.cacheTokens,
192
193
  ...(state.ctxSize ? { ctxSize: state.ctxSize } : {}),
193
194
  ...(state.backends.length ? { backends: state.backends } : {}),
194
- ...(usage ? { usage } : {})
195
+ ...(usage ? { usage } : {}),
196
+ ...(state.stopReason ? { stopReason: state.stopReason } : {})
195
197
  };
196
198
  }
197
199
  const TOOL_DENIED_MESSAGE = 'The user declined this call. Do not retry it or call it with different arguments to get ' +
@@ -463,6 +465,21 @@ async function* runRoundTokens(sdk, result, state, signal, startInThink = false)
463
465
  await events.return(null);
464
466
  }
465
467
  }
468
+ function sentMessages(stats) {
469
+ const sent = stats?.sentMessages;
470
+ return Array.isArray(sent) ? sent : null;
471
+ }
472
+ function roundTokens(stats) {
473
+ if (!stats)
474
+ return undefined;
475
+ const tokens = {
476
+ prompt: stats.promptTokens,
477
+ generated: stats.generatedTokens,
478
+ cached: stats.cacheTokens,
479
+ timeToFirstToken: stats.timeToFirstToken
480
+ };
481
+ return Object.values(tokens).some((value) => value !== undefined) ? tokens : undefined;
482
+ }
466
483
  const TRUNCATED_FINAL_MS = 2000;
467
484
  function truncatedFinal(final, contentText) {
468
485
  const accepted = { contentText, toolCalls: [], raw: { fullText: contentText } };
@@ -496,17 +513,35 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
496
513
  let emitted = false;
497
514
  const state = { stopToken: null, unproven: null, rawContentText: '' };
498
515
  const cacheable = candidate.history === args.history;
516
+ const params = {
517
+ modelId,
518
+ history: projectVisionHistory(candidate.history),
519
+ stream: true,
520
+ tools: args.tools,
521
+ generationParams: args.generationParams,
522
+ responseFormat: args.responseFormat,
523
+ toolDialect: args.toolDialect,
524
+ kvCache: cacheable ? args.cacheKey : undefined
525
+ };
526
+ if (args.capture) {
527
+ yield {
528
+ type: 'inference-call',
529
+ call: {
530
+ round: args.round,
531
+ droppedTurns: candidate.droppedTurns,
532
+ modelId,
533
+ messages: params.history,
534
+ tools: params.tools,
535
+ generationParams: params.generationParams,
536
+ responseFormat: params.responseFormat,
537
+ toolDialect: params.toolDialect,
538
+ cacheKey: params.kvCache,
539
+ promptPrefix: args.promptPrefix
540
+ }
541
+ };
542
+ }
499
543
  try {
500
- const result = sdk.completion({
501
- modelId,
502
- history: projectVisionHistory(candidate.history),
503
- stream: true,
504
- tools: args.tools,
505
- generationParams: args.generationParams,
506
- ...(args.responseFormat ? { responseFormat: args.responseFormat } : {}),
507
- toolDialect: args.toolDialect,
508
- ...(cacheable && args.cacheKey ? { kvCache: args.cacheKey } : {})
509
- });
544
+ const result = sdk.completion(params);
510
545
  result.final.catch(noop);
511
546
  const startInThink = args.generationParams?.reasoning_budget === -1;
512
547
  for await (const event of runRoundTokens(sdk, result, state, args.signal, startInThink)) {
@@ -518,6 +553,17 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
518
553
  const final = state.stopToken
519
554
  ? await truncatedFinal(result.final, state.rawContentText)
520
555
  : await raceAbort(result.final, args.signal);
556
+ if (args.capture) {
557
+ yield {
558
+ type: 'inference-sent',
559
+ round: args.round,
560
+ droppedTurns: candidate.droppedTurns,
561
+ messages: sentMessages(final.stats) ?? undefined,
562
+ tokens: roundTokens(final.stats),
563
+ outcome: 'completed',
564
+ reason: undefined
565
+ };
566
+ }
521
567
  const replay = unprovenReplay(state, final);
522
568
  hlog('final', {
523
569
  stopReason: final.stopReason,
@@ -535,6 +581,17 @@ async function* runRecoveredRoundWith(sdk, args, modelId) {
535
581
  };
536
582
  }
537
583
  catch (err) {
584
+ if (args.capture) {
585
+ yield {
586
+ type: 'inference-sent',
587
+ round: args.round,
588
+ droppedTurns: candidate.droppedTurns,
589
+ messages: undefined,
590
+ tokens: undefined,
591
+ outcome: isContextOverflowError(err) ? 'overflowed' : 'failed',
592
+ reason: err instanceof Error ? err.message : String(err)
593
+ };
594
+ }
538
595
  if (!isContextOverflowError(err))
539
596
  throw err;
540
597
  if (emitted)
@@ -551,7 +608,7 @@ function* reportUnnamedWrites(said, landed) {
551
608
  if (unreported)
552
609
  yield { type: 'content', text: unreported };
553
610
  }
554
- 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 = [] }) {
611
+ 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 }) {
555
612
  const pipeline = new HookPipeline();
556
613
  const agentId = agent?.id ?? null;
557
614
  const tools = permittedTools(allTools, agent?.disabledToolsets);
@@ -789,7 +846,9 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
789
846
  let mute = 0;
790
847
  let narrated = false;
791
848
  let cappedRounds = 0;
792
- for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
849
+ let roundsSent = 0;
850
+ let round = 0;
851
+ for (; round < MAX_TOOL_ROUNDS; round++) {
793
852
  if (signal?.aborted)
794
853
  throw new AbortedError();
795
854
  if (Date.now() >= runBudget.deadlineMs) {
@@ -820,13 +879,17 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
820
879
  responseFormat,
821
880
  toolDialect,
822
881
  cacheKey: roundCacheKey,
823
- signal
882
+ signal,
883
+ round,
884
+ promptPrefix,
885
+ capture: captureInferenceCalls
824
886
  });
887
+ roundsSent = round + 1;
825
888
  const final = recovered.final;
826
889
  if (!recovered.cacheable)
827
890
  roundCacheKey = undefined;
828
891
  history = recovered.history;
829
- collectStats(stats, final.stats);
892
+ collectStats(stats, final.stats, final.stopReason);
830
893
  hlog('round stats', { round, wallMs: Date.now() - roundStarted, ...(final.stats ?? {}) });
831
894
  const markup = stripToolResponseBlocks(truncateAtControlToken(final.raw.fullText) || recovered.unproven || '');
832
895
  let toolCalls = final.toolCalls?.length
@@ -1060,9 +1123,12 @@ export async function* completion(sdk, { modelId, acquireRound, model, agent, ct
1060
1123
  tools: undefined,
1061
1124
  generationParams,
1062
1125
  cacheKey: roundCacheKey,
1063
- signal
1126
+ signal,
1127
+ round: roundsSent,
1128
+ promptPrefix: promptPrefixHash({ system: systemContent, tools: undefined }),
1129
+ capture: captureInferenceCalls
1064
1130
  });
1065
- collectStats(stats, recovered.final.stats);
1131
+ collectStats(stats, recovered.final.stats, recovered.final.stopReason);
1066
1132
  const answer = recovered.replay ? stripToolCallMarkup(recovered.replay) : '';
1067
1133
  if (answer)
1068
1134
  yield { type: 'content', text: answer };
@@ -112,6 +112,7 @@ export class RemoteHarness extends EventEmitter {
112
112
  thinkingDefault: context.thinkingDefault,
113
113
  generationParams: context.generationParams,
114
114
  responseFormat: context.responseFormat,
115
+ captureInferenceCalls: context.captureInferenceCalls,
115
116
  messages: context.messages.map(({ role, content, attachments }) => ({
116
117
  role,
117
118
  content,
@@ -48,6 +48,7 @@ export interface CompletionContext {
48
48
  toolConfigs?: Record<string, Record<string, unknown>>;
49
49
  generationParams?: LlmGenerationParams;
50
50
  responseFormat?: ResponseFormat;
51
+ captureInferenceCalls?: boolean;
51
52
  grantedDirs?: string[];
52
53
  grantedDrives?: GrantedDrive[];
53
54
  driveOp?: DriveOpFn;
@@ -414,6 +414,7 @@ function completion(harness) {
414
414
  thinkingDefault: frame.thinkingDefault ?? undefined,
415
415
  generationParams: frame.generationParams ?? undefined,
416
416
  responseFormat: frame.responseFormat ?? undefined,
417
+ captureInferenceCalls: frame.captureInferenceCalls ?? undefined,
417
418
  messages: (frame.messages ?? []).map(({ role, content, attachments }) => ({
418
419
  role,
419
420
  content,
@@ -32,6 +32,20 @@ export function toWire(event) {
32
32
  const { type, ...metrics } = event;
33
33
  return { type, metrics };
34
34
  }
35
+ case 'inference-call':
36
+ return { type: 'inference-call', payload: event.call };
37
+ case 'inference-sent':
38
+ return {
39
+ type: 'inference-sent',
40
+ payload: {
41
+ round: event.round,
42
+ droppedTurns: event.droppedTurns,
43
+ messages: event.messages,
44
+ tokens: event.tokens,
45
+ outcome: event.outcome,
46
+ reason: event.reason
47
+ }
48
+ };
35
49
  case 'error':
36
50
  return { type: 'error', message: event.message };
37
51
  case 'aborted':
@@ -73,6 +87,20 @@ export function fromWire(event) {
73
87
  };
74
88
  case 'metrics':
75
89
  return { type: 'metrics', ...event.metrics };
90
+ case 'inference-call':
91
+ return { type: 'inference-call', call: event.payload };
92
+ case 'inference-sent': {
93
+ const sent = (event.payload ?? {});
94
+ return {
95
+ type: 'inference-sent',
96
+ messages: sent.messages,
97
+ round: Number(sent.round ?? 0),
98
+ droppedTurns: Number(sent.droppedTurns ?? 0),
99
+ tokens: sent.tokens,
100
+ outcome: sent.outcome || 'completed',
101
+ reason: sent.reason || undefined
102
+ };
103
+ }
76
104
  case 'error':
77
105
  return { type: 'error', message: event.message ?? 'harness error' };
78
106
  case 'aborted':
@@ -107,7 +107,8 @@ export function registerHarnessTypes(ns) {
107
107
  { name: 'grantedDirs', type: 'string', array: true },
108
108
  { name: 'grantedDrives', type: '@harness/granted-drive', array: true },
109
109
  { name: 'payload', type: 'json' },
110
- { name: 'grantedWriteDirs', type: 'string', array: true }
110
+ { name: 'grantedWriteDirs', type: 'string', array: true },
111
+ { name: 'captureInferenceCalls', type: 'bool' }
111
112
  ]
112
113
  });
113
114
  ns.register({
@@ -128,7 +129,9 @@ export function registerHarnessTypes(ns) {
128
129
  'question-ask',
129
130
  'done',
130
131
  'tool-activity',
131
- 'drive-ask'
132
+ 'drive-ask',
133
+ 'inference-call',
134
+ 'inference-sent'
132
135
  ],
133
136
  strings: true
134
137
  });
@@ -1,12 +1,12 @@
1
1
  // This file is autogenerated by the hyperschema compiler
2
- // Schema Version: 8
2
+ // Schema Version: 9
3
3
  /* eslint-disable camelcase */
4
4
  /* eslint-disable quotes */
5
5
  /* eslint-disable space-before-function-paren */
6
6
 
7
7
  import { c } from 'hyperschema/runtime'
8
8
 
9
- const VERSION = 8
9
+ const VERSION = 9
10
10
 
11
11
  // eslint-disable-next-line no-unused-vars
12
12
  let version = VERSION
@@ -478,7 +478,8 @@ const encoding11 = {
478
478
  ((version >= 5 && m.grantedDirs) ? 2097152 : 0) |
479
479
  ((version >= 5 && m.grantedDrives) ? 4194304 : 0) |
480
480
  ((version >= 5 && m.payload) ? 8388608 : 0) |
481
- ((version >= 5 && m.grantedWriteDirs) ? 16777216 : 0)
481
+ ((version >= 5 && m.grantedWriteDirs) ? 16777216 : 0) |
482
+ ((version >= 9 && m.captureInferenceCalls) ? 33554432 : 0)
482
483
 
483
484
  encoding10.preencode(state, m.type)
484
485
  c.uint.preencode(state, flags)
@@ -533,7 +534,8 @@ const encoding11 = {
533
534
  ((version >= 5 && m.grantedDirs) ? 2097152 : 0) |
534
535
  ((version >= 5 && m.grantedDrives) ? 4194304 : 0) |
535
536
  ((version >= 5 && m.payload) ? 8388608 : 0) |
536
- ((version >= 5 && m.grantedWriteDirs) ? 16777216 : 0)
537
+ ((version >= 5 && m.grantedWriteDirs) ? 16777216 : 0) |
538
+ ((version >= 9 && m.captureInferenceCalls) ? 33554432 : 0)
537
539
 
538
540
  encoding10.encode(state, m.type)
539
541
  c.uint.encode(state, flags)
@@ -592,7 +594,8 @@ const encoding11 = {
592
594
  grantedDirs: (version >= 5 && (flags & 2097152) !== 0) ? encoding11_22.decode(state) : null,
593
595
  grantedDrives: (version >= 5 && (flags & 4194304) !== 0) ? encoding11_23.decode(state) : null,
594
596
  payload: (version >= 5 && (flags & 8388608) !== 0) ? c.json.decode(state) : null,
595
- grantedWriteDirs: (version >= 5 && (flags & 16777216) !== 0) ? encoding11_25.decode(state) : null
597
+ grantedWriteDirs: (version >= 5 && (flags & 16777216) !== 0) ? encoding11_25.decode(state) : null,
598
+ captureInferenceCalls: (version >= 9 && (flags & 33554432) !== 0)
596
599
  }
597
600
  }
598
601
  }
@@ -613,13 +616,15 @@ const encoding12_enum = {
613
616
  "question-ask": 'question-ask',
614
617
  done: 'done',
615
618
  "tool-activity": 'tool-activity',
616
- "drive-ask": 'drive-ask'
619
+ "drive-ask": 'drive-ask',
620
+ "inference-call": 'inference-call',
621
+ "inference-sent": 'inference-sent'
617
622
  }
618
623
 
619
624
  // @harness/completion-event-type enum
620
625
  const encoding12 = {
621
626
  preencode (state, m) {
622
- state.end++ // max enum is 16 so always one byte
627
+ state.end++ // max enum is 18 so always one byte
623
628
  },
624
629
  encode (state, m) {
625
630
  switch (m) {
@@ -671,6 +676,12 @@ const encoding12 = {
671
676
  case 'drive-ask':
672
677
  c.uint.encode(state, 16)
673
678
  break
679
+ case 'inference-call':
680
+ c.uint.encode(state, 17)
681
+ break
682
+ case 'inference-sent':
683
+ c.uint.encode(state, 18)
684
+ break
674
685
  default:
675
686
  throw new Error('Unknown enum')
676
687
  }
@@ -709,6 +720,10 @@ const encoding12 = {
709
720
  return 'tool-activity'
710
721
  case 16:
711
722
  return 'drive-ask'
723
+ case 17:
724
+ return 'inference-call'
725
+ case 18:
726
+ return 'inference-sent'
712
727
  default: return null
713
728
  }
714
729
  }
@@ -1,12 +1,12 @@
1
1
  // This file is autogenerated by the hyperschema compiler
2
- // Schema Version: 8
2
+ // Schema Version: 9
3
3
  /* eslint-disable camelcase */
4
4
  /* eslint-disable quotes */
5
5
  /* eslint-disable space-before-function-paren */
6
6
 
7
7
  import { c } from 'hyperschema/runtime'
8
8
 
9
- const VERSION = 8
9
+ const VERSION = 9
10
10
 
11
11
  // eslint-disable-next-line no-unused-vars
12
12
  let version = VERSION
@@ -478,7 +478,8 @@ const encoding11 = {
478
478
  ((version >= 5 && m.grantedDirs) ? 2097152 : 0) |
479
479
  ((version >= 5 && m.grantedDrives) ? 4194304 : 0) |
480
480
  ((version >= 5 && m.payload) ? 8388608 : 0) |
481
- ((version >= 5 && m.grantedWriteDirs) ? 16777216 : 0)
481
+ ((version >= 5 && m.grantedWriteDirs) ? 16777216 : 0) |
482
+ ((version >= 9 && m.captureInferenceCalls) ? 33554432 : 0)
482
483
 
483
484
  encoding10.preencode(state, m.type)
484
485
  c.uint.preencode(state, flags)
@@ -533,7 +534,8 @@ const encoding11 = {
533
534
  ((version >= 5 && m.grantedDirs) ? 2097152 : 0) |
534
535
  ((version >= 5 && m.grantedDrives) ? 4194304 : 0) |
535
536
  ((version >= 5 && m.payload) ? 8388608 : 0) |
536
- ((version >= 5 && m.grantedWriteDirs) ? 16777216 : 0)
537
+ ((version >= 5 && m.grantedWriteDirs) ? 16777216 : 0) |
538
+ ((version >= 9 && m.captureInferenceCalls) ? 33554432 : 0)
537
539
 
538
540
  encoding10.encode(state, m.type)
539
541
  c.uint.encode(state, flags)
@@ -592,7 +594,8 @@ const encoding11 = {
592
594
  grantedDirs: (version >= 5 && (flags & 2097152) !== 0) ? encoding11_22.decode(state) : null,
593
595
  grantedDrives: (version >= 5 && (flags & 4194304) !== 0) ? encoding11_23.decode(state) : null,
594
596
  payload: (version >= 5 && (flags & 8388608) !== 0) ? c.json.decode(state) : null,
595
- grantedWriteDirs: (version >= 5 && (flags & 16777216) !== 0) ? encoding11_25.decode(state) : null
597
+ grantedWriteDirs: (version >= 5 && (flags & 16777216) !== 0) ? encoding11_25.decode(state) : null,
598
+ captureInferenceCalls: (version >= 9 && (flags & 33554432) !== 0)
596
599
  }
597
600
  }
598
601
  }
@@ -613,13 +616,15 @@ const encoding12_enum = {
613
616
  "question-ask": 'question-ask',
614
617
  done: 'done',
615
618
  "tool-activity": 'tool-activity',
616
- "drive-ask": 'drive-ask'
619
+ "drive-ask": 'drive-ask',
620
+ "inference-call": 'inference-call',
621
+ "inference-sent": 'inference-sent'
617
622
  }
618
623
 
619
624
  // @harness/completion-event-type enum
620
625
  const encoding12 = {
621
626
  preencode (state, m) {
622
- state.end++ // max enum is 16 so always one byte
627
+ state.end++ // max enum is 18 so always one byte
623
628
  },
624
629
  encode (state, m) {
625
630
  switch (m) {
@@ -671,6 +676,12 @@ const encoding12 = {
671
676
  case 'drive-ask':
672
677
  c.uint.encode(state, 16)
673
678
  break
679
+ case 'inference-call':
680
+ c.uint.encode(state, 17)
681
+ break
682
+ case 'inference-sent':
683
+ c.uint.encode(state, 18)
684
+ break
674
685
  default:
675
686
  throw new Error('Unknown enum')
676
687
  }
@@ -709,6 +720,10 @@ const encoding12 = {
709
720
  return 'tool-activity'
710
721
  case 16:
711
722
  return 'drive-ask'
723
+ case 17:
724
+ return 'inference-call'
725
+ case 18:
726
+ return 'inference-sent'
712
727
  default: return null
713
728
  }
714
729
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": 8,
2
+ "version": 9,
3
3
  "schema": [
4
4
  {
5
5
  "name": "empty",
@@ -446,6 +446,11 @@
446
446
  "array": true,
447
447
  "type": "string",
448
448
  "version": 5
449
+ },
450
+ {
451
+ "name": "captureInferenceCalls",
452
+ "type": "bool",
453
+ "version": 9
449
454
  }
450
455
  ]
451
456
  },
@@ -517,6 +522,14 @@
517
522
  {
518
523
  "key": "drive-ask",
519
524
  "version": 5
525
+ },
526
+ {
527
+ "key": "inference-call",
528
+ "version": 9
529
+ },
530
+ {
531
+ "key": "inference-sent",
532
+ "version": 9
520
533
  }
521
534
  ],
522
535
  "strings": true
@@ -1,5 +1,5 @@
1
1
  // This file is autogenerated by hyperschema-ts
2
- // Schema version: 8
2
+ // Schema version: 9
3
3
  // Do not edit manually.
4
4
 
5
5
  export interface HarnessEmpty {
@@ -86,9 +86,10 @@ export interface HarnessCompletionFrame {
86
86
  grantedDrives?: HarnessGrantedDrive[] | null
87
87
  payload?: unknown | null
88
88
  grantedWriteDirs?: string[] | null
89
+ captureInferenceCalls?: boolean
89
90
  }
90
91
 
91
- export type HarnessCompletionEventType = "content" | "thinking" | "tool-call" | "tool-result" | "approval-ask" | "metrics" | "error" | "aborted" | "attach-ask" | "fetch-ask" | "progress" | "rag-ask" | "question-ask" | "done" | "tool-activity" | "drive-ask"
92
+ export type HarnessCompletionEventType = "content" | "thinking" | "tool-call" | "tool-result" | "approval-ask" | "metrics" | "error" | "aborted" | "attach-ask" | "fetch-ask" | "progress" | "rag-ask" | "question-ask" | "done" | "tool-activity" | "drive-ask" | "inference-call" | "inference-sent"
92
93
 
93
94
  export interface HarnessCompletionEvent {
94
95
  type: HarnessCompletionEventType
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qvac/harness",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "type": "module",
5
5
  "description": "An AI harness implementing generate(context) via @qvac/sdk. Knows nothing about workbench, hyperdb, or chunks — just inference in, events out.",
6
6
  "exports": {
@@ -555,9 +555,8 @@
555
555
  },
556
556
  "scripts": {
557
557
  "build:schema": "bare schema/build.ts",
558
- "test": "npm run vendor:wheels && npm run test:node && npm run test:bare && npm run test:addons",
559
- "test:node": "QVAC_NO_SKILL_BIN_PROBE=1 brittle-node test/all.ts | prettytap",
560
- "test:bare": "QVAC_NO_SKILL_BIN_PROBE=1 brittle-bare test/all.ts | prettytap",
558
+ "test": "npm run vendor:wheels && npm run test:bare && npm run test:addons",
559
+ "test:bare": "QVAC_NO_SKILL_BIN_PROBE=1 prettytap -- brittle-bare test/all.ts",
561
560
  "format": "prettier . --write",
562
561
  "lint": "prettier . --check && lunte && tsc --noEmit",
563
562
  "build:bundle": "node scripts/build-bundle.mjs",
@@ -714,7 +713,7 @@
714
713
  "lunte": "^1.8.3",
715
714
  "prettier": "^3.6.2",
716
715
  "prettier-config-holepunch": "^2.0.0",
717
- "prettytap": "^0.1.3",
716
+ "prettytap": "^0.2.0",
718
717
  "terser": "^5.51.2",
719
718
  "typescript": "^7.0.2"
720
719
  },
@@ -724,7 +723,8 @@
724
723
  "localwatch@1.4.3": "patches/localwatch@1.4.3.patch",
725
724
  "hypercore-blob-server@1.15.0": "patches/hypercore-blob-server@1.15.0.patch",
726
725
  "bare-stow@0.1.5": "patches/bare-stow@0.1.5.patch",
727
- "@qvac/inference@0.19.0": "patches/@qvac%2Finference@0.19.0.patch"
726
+ "@qvac/inference@0.19.0": "patches/@qvac%2Finference@0.19.0.patch",
727
+ "brittle@4.1.0": "patches/brittle@4.1.0.patch"
728
728
  },
729
729
  "repository": {
730
730
  "type": "git",