@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.
Files changed (88) hide show
  1. package/CHANGELOG.md +1278 -472
  2. package/README.md +49 -0
  3. package/dist-lib/index.d.ts +42 -2
  4. package/dist-lib/index.js +447 -58
  5. package/dist-lib/lib/audio-transcode-bare.d.ts +1 -1
  6. package/dist-lib/lib/audio-transcode-bare.js +22 -25
  7. package/dist-lib/lib/audio-transcode-node.d.ts +1 -1
  8. package/dist-lib/lib/audio-transcode-node.js +1 -3
  9. package/dist-lib/lib/capabilities/audio-resample.d.ts +1 -1
  10. package/dist-lib/lib/capabilities/audio-resample.js +7 -8
  11. package/dist-lib/lib/capabilities/completion.d.ts +6 -3
  12. package/dist-lib/lib/capabilities/completion.js +96 -49
  13. package/dist-lib/lib/capabilities/download.d.ts +3 -1
  14. package/dist-lib/lib/capabilities/download.js +1 -1
  15. package/dist-lib/lib/capabilities/preview-model-source.d.ts +1 -0
  16. package/dist-lib/lib/capabilities/preview-model-source.js +1 -0
  17. package/dist-lib/lib/capabilities/tool-selection.d.ts +1 -3
  18. package/dist-lib/lib/capabilities/tool-selection.js +2 -4
  19. package/dist-lib/lib/capabilities/tool-turns.d.ts +0 -1
  20. package/dist-lib/lib/capabilities/tool-turns.js +0 -6
  21. package/dist-lib/lib/capabilities/usage-breakdown.js +4 -1
  22. package/dist-lib/lib/capabilities/voice.d.ts +5 -1
  23. package/dist-lib/lib/capabilities/voice.js +94 -26
  24. package/dist-lib/lib/connect.d.ts +4 -1
  25. package/dist-lib/lib/connect.js +16 -1
  26. package/dist-lib/lib/contract-types.d.ts +12 -0
  27. package/dist-lib/lib/helpers/tool-call-markup.d.ts +1 -0
  28. package/dist-lib/lib/helpers/tool-call-markup.js +3 -0
  29. package/dist-lib/lib/models/context-choice.d.ts +15 -0
  30. package/dist-lib/lib/models/context-choice.js +55 -0
  31. package/dist-lib/lib/models/download-gate.d.ts +20 -0
  32. package/dist-lib/lib/models/download-gate.js +73 -0
  33. package/dist-lib/lib/models/memory-cap.d.ts +13 -0
  34. package/dist-lib/lib/models/memory-cap.js +21 -0
  35. package/dist-lib/lib/models/model-catalog-constants.d.ts +3 -1
  36. package/dist-lib/lib/models/model-catalog-constants.js +9 -1
  37. package/dist-lib/lib/models/model-catalog.d.ts +6 -2
  38. package/dist-lib/lib/models/model-catalog.js +35 -3
  39. package/dist-lib/lib/models/model-handle.d.ts +8 -1
  40. package/dist-lib/lib/models/model-handle.js +20 -2
  41. package/dist-lib/lib/models/model-list.d.ts +8 -1
  42. package/dist-lib/lib/models/model-list.js +23 -0
  43. package/dist-lib/lib/models/model-pool.d.ts +35 -5
  44. package/dist-lib/lib/models/model-pool.js +97 -32
  45. package/dist-lib/lib/models/sdk-faults.d.ts +1 -0
  46. package/dist-lib/lib/models/sdk-faults.js +6 -0
  47. package/dist-lib/lib/office/engine/pptx/picture-area.d.ts +23 -0
  48. package/dist-lib/lib/office/engine/pptx/picture-area.js +84 -0
  49. package/dist-lib/lib/office/engine/pptx/presentation.d.ts +2 -0
  50. package/dist-lib/lib/office/engine/pptx/presentation.js +55 -17
  51. package/dist-lib/lib/office/engine/pptx/types.d.ts +1 -0
  52. package/dist-lib/lib/office/engine/xlsx/workbook.d.ts +1 -0
  53. package/dist-lib/lib/office/engine/xlsx/workbook.js +34 -3
  54. package/dist-lib/lib/serve.js +23 -3
  55. package/dist-lib/lib/spawn.js +4 -10
  56. package/dist-lib/lib/tools/exec.d.ts +4 -1
  57. package/dist-lib/lib/tools/exec.js +55 -11
  58. package/dist-lib/lib/tools/generate-image.d.ts +4 -2
  59. package/dist-lib/lib/tools/generate-image.js +5 -2
  60. package/dist-lib/lib/tools/generate-music.d.ts +4 -2
  61. package/dist-lib/lib/tools/generate-music.js +5 -2
  62. package/dist-lib/lib/tools/office/ops.js +1 -1
  63. package/dist-lib/lib/tools/office.js +29 -6
  64. package/dist-lib/lib/tools/skills/catalog.d.ts +1 -0
  65. package/dist-lib/lib/tools/skills/catalog.js +2 -0
  66. package/dist-lib/lib/tools/skills/cli-schema-check.d.ts +1 -0
  67. package/dist-lib/lib/tools/skills/cli-schema-check.js +3 -0
  68. package/dist-lib/lib/tools/skills/cli-schema-load.js +10 -0
  69. package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +2 -0
  70. package/dist-lib/lib/tools/skills/cli-schema.js +6 -3
  71. package/dist-lib/lib/tools/skills/frontmatter-parser.d.ts +1 -0
  72. package/dist-lib/lib/tools/skills/manifest-parse.d.ts +1 -0
  73. package/dist-lib/lib/tools/skills/manifest-parse.js +12 -0
  74. package/dist-lib/lib/tools/skills/mention.d.ts +0 -1
  75. package/dist-lib/lib/tools/skills/mention.js +0 -3
  76. package/dist-lib/lib/tools/translate.d.ts +3 -1
  77. package/dist-lib/lib/tools/translate.js +6 -1
  78. package/dist-lib/schema/harness.js +43 -4
  79. package/dist-lib/spec/bind.js +9 -0
  80. package/dist-lib/spec/calls.js +3 -0
  81. package/dist-lib/spec/hrpc/hrpc.json +39 -0
  82. package/dist-lib/spec/hrpc/index.js +39 -3
  83. package/dist-lib/spec/hrpc/messages.js +101 -13
  84. package/dist-lib/spec/hyperschema/index.js +101 -13
  85. package/dist-lib/spec/hyperschema/schema.json +62 -1
  86. package/dist-lib/spec/hyperschema/types.d.ts +20 -2
  87. package/package.json +8 -10
  88. package/worker/harness-sidecar.mjs +2 -1
@@ -7,7 +7,7 @@ export const PPTX_OPS = {
7
7
  setText: { slots: [['slide', 'shape'], 'text'] },
8
8
  setCellText: { slots: [['slide', 'shape', 'row', 'column'], 'text'] },
9
9
  setStyle: { slots: [['slide', 'shape'], 'style', ['paragraph']] },
10
- addSlide: { slots: [['like', 'layout', 'at', 'title', 'body']] },
10
+ addSlide: { slots: [['like', 'layout', 'at', 'title', 'body', 'image']] },
11
11
  addPicture: { slots: ['slide', 'image', [...BOX, 'name', 'description']], inches: true },
12
12
  addTextBox: { slots: ['slide', [...BOX, 'text', 'style', 'fill', 'name']], inches: true },
13
13
  addTable: { slots: ['slide', [...BOX, 'rows', 'header', 'name']], inches: true },
@@ -1,12 +1,13 @@
1
1
  import b4a from 'b4a';
2
2
  import { errorMessage, isRecord } from '../guards.js';
3
- import { docx, pptx, units, xlsx } from '../office/engine/index.js';
3
+ import { DocsError, docx, pptx, units, xlsx } from '../office/engine/index.js';
4
4
  import { stageInputs } from './exec/adapters/python/attachments.js';
5
5
  import { checkOutputBytes, RUN_OUTPUT_BUDGET_BYTES } from './exec/adapters/python/output-policy.js';
6
6
  import { DOCX_OPS, opArguments, opKeys, PPTX_OPS, withAliases, XLSX_OPS } from './office/ops.js';
7
7
  const READ_CELL_LIMIT = 10_000;
8
8
  const attachedThisTurn = new WeakMap();
9
9
  const FIRST_SHEET = /^sheet ?1$/i;
10
+ const NEW_DECK_HINT = '; a new deck has only the slides its addSlide ops make, slides count from 0, and an op on a slide goes after the addSlide that makes it';
10
11
  const SHAPE_ROLES = new Set(['title', 'subtitle', 'body']);
11
12
  const HOISTED = ['attachmentId', 'output'];
12
13
  const WHERE_IDS_ARE = 'copy the id from the [Attached file "…" — attachmentId: …] line or an earlier tool result';
@@ -25,6 +26,9 @@ export function shapeFor(deck, op) {
25
26
  const found = deck.read()[slide]?.shapes.find((entry) => entry.name.toLowerCase() === wanted);
26
27
  return found && found.id !== null ? { ...op, shape: found.id } : op;
27
28
  }
29
+ function newDeckHint(err, created) {
30
+ return created && err instanceof DocsError && err.code === 'SLIDE_NOT_FOUND' ? NEW_DECK_HINT : '';
31
+ }
28
32
  export function sheetFor(book, op, created) {
29
33
  if (typeof op.sheet !== 'string')
30
34
  return op;
@@ -70,7 +74,8 @@ const PPTX_FORMAT = {
70
74
  create: (options) => pptx.create(options),
71
75
  read: (deck) => deck.read(),
72
76
  count: (deck) => ({ slides: deck.slideCount }),
73
- prepare: (deck, op) => shapeFor(deck, op)
77
+ prepare: (deck, op) => shapeFor(deck, op),
78
+ hint: newDeckHint
74
79
  };
75
80
  const DOCX_FORMAT = {
76
81
  name: 'docx',
@@ -141,7 +146,11 @@ function parseOp(format, op, i) {
141
146
  return `ops[${i}] sheets lists the sheets and takes no arguments; to add a sheet use { op: "addSheet", name }`;
142
147
  }
143
148
  const takes = keys.length ? `it takes ${keys.join(', ')}` : 'it takes no arguments';
144
- return `ops[${i}] ${op.op} does not take ${unknown.join(', ')}; ${takes}`;
149
+ const nested = unknown.filter((key) => format.ops[key]);
150
+ const apart = nested.length
151
+ ? `; ${nested.map((name) => `{ "op": "${name}", ... }`).join(' and ')} goes in ops as its own entry, next to this one, not inside it`
152
+ : '';
153
+ return `ops[${i}] ${op.op} does not take ${unknown.join(', ')}; ${takes}${apart}`;
145
154
  }
146
155
  function parseOps(format, ops) {
147
156
  if (!Array.isArray(ops)) {
@@ -252,8 +261,11 @@ async function imageInput(op, i, ctx) {
252
261
  }
253
262
  return { attachmentId: id ? await imageId(ctx, id) : undefined, path: `image-${i}.png` };
254
263
  }
264
+ function takesImage(op) {
265
+ return op.spec.slots.includes('image') || op.args.image !== undefined;
266
+ }
255
267
  async function stagedImages(request, ctx) {
256
- const wanted = request.ops.flatMap((op, i) => (opKeys(op.spec).includes('image') ? [i] : []));
268
+ const wanted = request.ops.flatMap((op, i) => (takesImage(op) ? [i] : []));
257
269
  const images = new Map();
258
270
  if (!wanted.length) {
259
271
  return images;
@@ -358,7 +370,8 @@ function runOps({ format, document, request, images }) {
358
370
  results.push(runOp(document, op, input));
359
371
  }
360
372
  catch (err) {
361
- return { error: `ops[${i}] ${op.name}: ${errorMessage(err)}` };
373
+ const hint = format.hint?.(err, created) ?? '';
374
+ return { error: `ops[${i}] ${op.name}: ${errorMessage(err)}${hint}` };
362
375
  }
363
376
  }
364
377
  return results;
@@ -452,6 +465,7 @@ async function execute(format, args, ctx) {
452
465
  const renamed = output !== request.output
453
466
  ? `Saved as ${output}, so the attached ${fileName} keeps its own name. `
454
467
  : '';
468
+ const unplaced = !!ctx.turnImages?.length && !images.size;
455
469
  return {
456
470
  results,
457
471
  attachments: [attachment],
@@ -459,9 +473,18 @@ async function execute(format, args, ctx) {
459
473
  reply: `${attachment.fileName} — ${countLine(counts)}`,
460
474
  next: renamed +
461
475
  `Done: ${attachment.fileName} ${repeated ? 'was already' : 'is'} attached to the chat and shown to the user. ` +
462
- `Do not call ${format.name} again for it. Your answer is the reply value alone, word for word.`
476
+ afterSave(format, attachment.attachmentId, unplaced)
463
477
  };
464
478
  }
479
+ function afterSave(format, attachmentId, unplaced) {
480
+ const reply = 'Your answer is the reply value alone, word for word.';
481
+ if (!unplaced) {
482
+ return `Do not call ${format.name} again for it. ${reply}`;
483
+ }
484
+ return (`The image the user attached to their message is not in it. If they asked for that image in the ${format.noun}, ` +
485
+ `call ${format.name} once more with attachmentId "${attachmentId}" and one op that adds it with "image": {}. ` +
486
+ `Otherwise: ${reply}`);
487
+ }
465
488
  function createOfficeTool(format) {
466
489
  return {
467
490
  name: format.name,
@@ -20,6 +20,7 @@ export interface SkillCatalogEntry {
20
20
  requires?: SkillRequires;
21
21
  install?: InstallStep[];
22
22
  setup?: SkillSetup;
23
+ env?: Readonly<Record<string, string>>;
23
24
  cliValidator?: CliValidator;
24
25
  stub?: boolean;
25
26
  minCapabilityProfile?: string;
@@ -82,6 +82,7 @@ function mergeOverlay(base, overlay) {
82
82
  credentials: base.credentials,
83
83
  allowList: base.allowList,
84
84
  platform: base.platform,
85
+ env: base.env,
85
86
  operations: base.operations,
86
87
  mcpReads: base.mcpReads
87
88
  };
@@ -166,6 +167,7 @@ async function loadEntry(skillsDir, dir, origin) {
166
167
  requires: meta.requires,
167
168
  install: meta.install,
168
169
  setup: meta.setup,
170
+ env: origin === 'overlay' ? undefined : meta.env,
169
171
  cliValidator: cli ? buildCliValidator(cli) : undefined,
170
172
  stub: meta.stub,
171
173
  minCapabilityProfile: meta.minCapabilityProfile || undefined,
@@ -1,3 +1,4 @@
1
1
  import type { JsonSchema } from './cli-schema-types.ts';
2
2
  export declare function buildSchemaAnnotation(schema: JsonSchema): (argv: readonly string[]) => 'read' | 'write';
3
+ export declare function buildSchemaOutputCap(schema: JsonSchema): (argv: readonly string[]) => number | undefined;
3
4
  export declare function buildSchemaCheck(schema: JsonSchema, resourcePaths: readonly string[] | undefined): (argv: readonly string[]) => string | null;
@@ -13,6 +13,9 @@ export function buildSchemaAnnotation(schema) {
13
13
  return walkCommands(argv, schema).node['x-effect'] === 'read' ? 'read' : 'write';
14
14
  };
15
15
  }
16
+ export function buildSchemaOutputCap(schema) {
17
+ return (argv) => walkCommands(argv, schema).node['x-max-output-chars'];
18
+ }
16
19
  function basename(p) {
17
20
  return p.slice(Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\')) + 1);
18
21
  }
@@ -28,6 +28,9 @@ export async function resolveCliSchema(skillDir, bin, readFile = (p) => fs.promi
28
28
  if (!resourceEffectsValid(schema)) {
29
29
  return { bin, schemaFile: CLI_SCHEMA_FILENAME };
30
30
  }
31
+ if (!outputCapsValid(schema)) {
32
+ return { bin, schemaFile: CLI_SCHEMA_FILENAME };
33
+ }
31
34
  const resourcePaths = resolveResourcePaths(skillDir, schema);
32
35
  return {
33
36
  bin,
@@ -63,6 +66,13 @@ function resourceEffectsValid(schema) {
63
66
  const files = new Set(schema['x-resource-files'] ?? []);
64
67
  return Object.entries(effects).every(([file, effect]) => effect === 'read' && files.has(file));
65
68
  }
69
+ function outputCapsValid(schema) {
70
+ const cap = schema['x-max-output-chars'];
71
+ if (cap !== undefined && !(Number.isInteger(cap) && cap > 0)) {
72
+ return false;
73
+ }
74
+ return Object.values(schema.properties ?? {}).every(outputCapsValid);
75
+ }
66
76
  function resolveResourcePaths(skillDir, schema) {
67
77
  const resources = schema['x-resource-files'] ?? [];
68
78
  return resources
@@ -13,6 +13,7 @@ export interface JsonSchema {
13
13
  readonly 'x-resource-files'?: readonly string[];
14
14
  readonly 'x-resource-effects'?: Readonly<Record<string, string>>;
15
15
  readonly 'x-effect'?: string;
16
+ readonly 'x-max-output-chars'?: number;
16
17
  readonly [key: string]: unknown;
17
18
  }
18
19
  export interface SkillCli {
@@ -27,4 +28,5 @@ export interface CliValidator {
27
28
  readonly check: (argv: readonly string[]) => string | null;
28
29
  readonly annotation: (argv: readonly string[]) => 'read' | 'write';
29
30
  readonly effect: (argv: readonly string[]) => 'read' | 'write';
31
+ readonly outputCap: (argv: readonly string[]) => number | undefined;
30
32
  }
@@ -1,7 +1,7 @@
1
1
  export { CLI_SCHEMA_FILENAME } from './cli-schema-types.js';
2
2
  export { argvToObject } from './cli-argv.js';
3
3
  export { loadSkillCli, resolveCliSchema, execBinFromTools } from './cli-schema-load.js';
4
- import { buildSchemaAnnotation, buildSchemaCheck } from './cli-schema-check.js';
4
+ import { buildSchemaAnnotation, buildSchemaCheck, buildSchemaOutputCap } from './cli-schema-check.js';
5
5
  export function buildCliValidator(def) {
6
6
  const bin = def.bin.toLowerCase();
7
7
  if (!def.schema) {
@@ -9,16 +9,19 @@ export function buildCliValidator(def) {
9
9
  bin,
10
10
  check: () => 'skill cli schema is missing',
11
11
  annotation: () => 'write',
12
- effect: () => 'write'
12
+ effect: () => 'write',
13
+ outputCap: () => undefined
13
14
  };
14
15
  }
15
16
  const check = buildSchemaCheck(def.schema, def.resourcePaths);
16
17
  const annotation = buildSchemaAnnotation(def.schema);
18
+ const outputCap = buildSchemaOutputCap(def.schema);
17
19
  return {
18
20
  bin,
19
21
  check,
20
22
  annotation,
21
- effect: (argv) => (check(argv) === null ? annotation(argv) : 'write')
23
+ effect: (argv) => (check(argv) === null ? annotation(argv) : 'write'),
24
+ outputCap: (argv) => (check(argv) === null ? outputCap(argv) : undefined)
22
25
  };
23
26
  }
24
27
  export function checkCliOps(argv, compiled) {
@@ -12,6 +12,7 @@ export interface SkillFrontMatter {
12
12
  requires?: SkillRequires;
13
13
  install?: InstallStep[];
14
14
  setup?: SkillSetup;
15
+ env?: Readonly<Record<string, string>>;
15
16
  stub?: boolean;
16
17
  minCapabilityProfile?: string;
17
18
  emoji?: string;
@@ -3,6 +3,7 @@ export interface ParsedManifest {
3
3
  requires?: SkillRequires;
4
4
  install?: InstallStep[];
5
5
  setup?: SkillSetup;
6
+ env?: Readonly<Record<string, string>>;
6
7
  stub?: boolean;
7
8
  minCapabilityProfile?: string;
8
9
  emoji?: string;
@@ -12,11 +12,23 @@ export function parseManifestBlock(block, key = 'metadata') {
12
12
  requires,
13
13
  install,
14
14
  setup,
15
+ env: readEnv(oc.env),
15
16
  stub: typeof oc.stub === 'boolean' ? oc.stub : undefined,
16
17
  minCapabilityProfile: typeof oc.minCapabilityProfile === 'string' ? oc.minCapabilityProfile : undefined,
17
18
  emoji: typeof oc.emoji === 'string' ? oc.emoji : undefined
18
19
  };
19
20
  }
21
+ function readEnv(v) {
22
+ if (!isObj(v))
23
+ return undefined;
24
+ const env = {};
25
+ for (const [name, value] of Object.entries(v)) {
26
+ if (typeof value === 'string' && /^[A-Z_][A-Z0-9_]*$/.test(name)) {
27
+ env[name] = value;
28
+ }
29
+ }
30
+ return Object.keys(env).length > 0 ? env : undefined;
31
+ }
20
32
  function extractJsonBlock(block, key) {
21
33
  const at = block.indexOf(`${key}:`);
22
34
  if (at === -1) {
@@ -3,4 +3,3 @@ import { skillMentionName } from './mention-name.ts';
3
3
  export { skillMentionName };
4
4
  export declare function resolveSkillMention(text: string, catalog: readonly SkillCatalogEntry[]): SkillCatalogEntry | null;
5
5
  export declare function resolveImplicitSkill(text: string, activeSkills: readonly SkillCatalogEntry[]): SkillCatalogEntry | null;
6
- export declare function skillPreloadMessage(content: string, name: string, body: string): string;
@@ -38,6 +38,3 @@ export function resolveImplicitSkill(text, activeSkills) {
38
38
  }
39
39
  return best;
40
40
  }
41
- export function skillPreloadMessage(content, name, body) {
42
- return `[Skill invoked by user: ${name}]\n${body}\n\nThe user's message:\n${content}`;
43
- }
@@ -3,6 +3,7 @@ import type { ToolDefinition } from '../capabilities/completion.ts';
3
3
  import type { ModelSrc } from '../models/context.ts';
4
4
  import { type NmtPlan } from '../models/nmt-catalog.ts';
5
5
  import { type LoadTracker, type TrackedLoad } from '../models/model-pool.ts';
6
+ import type { HoldFn } from '../models/download-gate.ts';
6
7
  import { type ModelRegistrySdk } from '../models/model-source.ts';
7
8
  interface BergamotConfig {
8
9
  engine: 'Bergamot';
@@ -42,6 +43,7 @@ export interface TranslationRuntimeOptions {
42
43
  loadSdk: () => Promise<TranslationSdk>;
43
44
  loads?: LoadTracker;
44
45
  onProgress?: (model: string, progress: Record<string, unknown>) => void;
46
+ hold?: HoldFn;
45
47
  stallMs?: number;
46
48
  }
47
49
  export interface TranslationRuntime {
@@ -53,7 +55,7 @@ export interface TranslationRuntime {
53
55
  enter: (signal?: BareAbortSignal) => Promise<void>;
54
56
  exit: () => void;
55
57
  }
56
- export declare function createTranslationRuntime({ loadSdk, loads, onProgress, stallMs }: TranslationRuntimeOptions): TranslationRuntime;
58
+ export declare function createTranslationRuntime({ loadSdk, loads, onProgress, hold, stallMs }: TranslationRuntimeOptions): TranslationRuntime;
57
59
  export type SlmTranslateFn = (model: string, params: {
58
60
  text: string;
59
61
  from: string;
@@ -26,7 +26,7 @@ function bergamotConfig(plan) {
26
26
  }
27
27
  return config;
28
28
  }
29
- export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(), onProgress, stallMs = TRANSLATION_LOAD_STALL_MS }) {
29
+ export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(), onProgress, hold, stallMs = TRANSLATION_LOAD_STALL_MS }) {
30
30
  let loading = null;
31
31
  let residentFingerprint = null;
32
32
  const lock = new ScopeLock();
@@ -95,6 +95,11 @@ export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(),
95
95
  loadSdk,
96
96
  ensureLoaded: async (sdk, plan, signal) => {
97
97
  const fingerprint = fingerprintOf(plan);
98
+ if (hold && !(loading && residentFingerprint === fingerprint)) {
99
+ for (const model of plan.pivot ? [plan.model, plan.pivot] : [plan.model]) {
100
+ await hold(model, 'nmt', signal);
101
+ }
102
+ }
98
103
  await lock.lock();
99
104
  try {
100
105
  if (loading && residentFingerprint !== fingerprint) {
@@ -113,7 +113,8 @@ export function registerHarnessTypes(ns) {
113
113
  { name: 'captureInferenceCalls', type: 'bool' },
114
114
  { name: 'capability', type: 'string' },
115
115
  { name: 'arguments', type: 'json' },
116
- { name: 'enhance', type: 'string' }
116
+ { name: 'enhance', type: 'string' },
117
+ { name: 'session', type: 'string' }
117
118
  ]
118
119
  });
119
120
  ns.register({
@@ -175,7 +176,16 @@ export function registerHarnessTypes(ns) {
175
176
  });
176
177
  ns.register({
177
178
  name: 'model-event-status',
178
- enum: ['loading', 'downloading', 'ready', 'error', 'unloading', 'unloaded', 'downloaded'],
179
+ enum: [
180
+ 'loading',
181
+ 'downloading',
182
+ 'ready',
183
+ 'error',
184
+ 'unloading',
185
+ 'unloaded',
186
+ 'downloaded',
187
+ 'held'
188
+ ],
179
189
  strings: true
180
190
  });
181
191
  ns.register({
@@ -195,7 +205,8 @@ export function registerHarnessTypes(ns) {
195
205
  { name: 'loaded', type: '@harness/loaded-model', array: true },
196
206
  { name: 'catalogChanged', type: 'bool' },
197
207
  { name: 'downloaded', type: 'uint' },
198
- { name: 'total', type: 'uint' }
208
+ { name: 'total', type: 'uint' },
209
+ { name: 'addon', type: 'string' }
199
210
  ]
200
211
  });
201
212
  ns.register({
@@ -340,7 +351,8 @@ export function registerHarnessTypes(ns) {
340
351
  { name: 'expectedSize', type: 'uint', required: true },
341
352
  { name: 'actualSize', type: 'uint' },
342
353
  { name: 'label', type: 'string' },
343
- { name: 'isLoading', type: 'bool' }
354
+ { name: 'isLoading', type: 'bool' },
355
+ { name: 'memoryBytes', type: 'uint' }
344
356
  ]
345
357
  });
346
358
  ns.register({
@@ -738,6 +750,18 @@ export function registerHarnessTypes(ns) {
738
750
  name: 'unload-model-response',
739
751
  fields: [{ name: 'unloaded', type: 'bool', required: true }]
740
752
  });
753
+ ns.register({
754
+ name: 'set-model-memory-cap-request',
755
+ fields: [{ name: 'percent', type: 'uint', required: true }]
756
+ });
757
+ ns.register({
758
+ name: 'memory-usage',
759
+ fields: [{ name: 'usedBytes', type: 'uint', required: true }]
760
+ });
761
+ ns.register({
762
+ name: 'set-downloads-allowed-request',
763
+ fields: [{ name: 'allowed', type: 'bool', required: true }]
764
+ });
741
765
  }
742
766
  export function registerHarnessApi(api) {
743
767
  api.register({
@@ -905,4 +929,19 @@ export function registerHarnessApi(api) {
905
929
  request: { name: '@harness/unload-model-request', stream: false },
906
930
  response: { name: '@harness/unload-model-response', stream: false }
907
931
  });
932
+ api.register({
933
+ name: 'set-model-memory-cap',
934
+ request: { name: '@harness/set-model-memory-cap-request', stream: false },
935
+ response: { name: '@harness/ok-response', stream: false }
936
+ });
937
+ api.register({
938
+ name: 'memory-usage',
939
+ request: { name: '@harness/empty', stream: false },
940
+ response: { name: '@harness/memory-usage', stream: false }
941
+ });
942
+ api.register({
943
+ name: 'set-downloads-allowed',
944
+ request: { name: '@harness/set-downloads-allowed-request', stream: false },
945
+ response: { name: '@harness/ok-response', stream: false }
946
+ });
908
947
  }
@@ -139,4 +139,13 @@ export function bindApi(rpc, api) {
139
139
  if (api.unloadModel) {
140
140
  rpc.onUnloadModel((req) => api.unloadModel(req));
141
141
  }
142
+ if (api.setModelMemoryCap) {
143
+ rpc.onSetModelMemoryCap((req) => api.setModelMemoryCap(req));
144
+ }
145
+ if (api.memoryUsage) {
146
+ rpc.onMemoryUsage((req) => api.memoryUsage(req));
147
+ }
148
+ if (api.setDownloadsAllowed) {
149
+ rpc.onSetDownloadsAllowed((req) => api.setDownloadsAllowed(req));
150
+ }
142
151
  }
@@ -43,5 +43,8 @@ export function harnessClient(rpc) {
43
43
  setModelIdleTimeout: (input) => rpc.setModelIdleTimeout(input),
44
44
  loadModel: (input) => rpc.loadModel(input),
45
45
  unloadModel: (input) => rpc.unloadModel(input),
46
+ setModelMemoryCap: (input) => rpc.setModelMemoryCap(input),
47
+ memoryUsage: (input) => rpc.memoryUsage(input),
48
+ setDownloadsAllowed: (input) => rpc.setDownloadsAllowed(input),
46
49
  };
47
50
  }
@@ -429,6 +429,45 @@
429
429
  "stream": false
430
430
  },
431
431
  "version": 1
432
+ },
433
+ {
434
+ "id": 33,
435
+ "name": "@harness/set-model-memory-cap",
436
+ "request": {
437
+ "name": "@harness/set-model-memory-cap-request",
438
+ "stream": false
439
+ },
440
+ "response": {
441
+ "name": "@harness/ok-response",
442
+ "stream": false
443
+ },
444
+ "version": 1
445
+ },
446
+ {
447
+ "id": 34,
448
+ "name": "@harness/memory-usage",
449
+ "request": {
450
+ "name": "@harness/empty",
451
+ "stream": false
452
+ },
453
+ "response": {
454
+ "name": "@harness/memory-usage",
455
+ "stream": false
456
+ },
457
+ "version": 1
458
+ },
459
+ {
460
+ "id": 35,
461
+ "name": "@harness/set-downloads-allowed",
462
+ "request": {
463
+ "name": "@harness/set-downloads-allowed-request",
464
+ "stream": false
465
+ },
466
+ "response": {
467
+ "name": "@harness/ok-response",
468
+ "stream": false
469
+ },
470
+ "version": 1
432
471
  }
433
472
  ]
434
473
  }
@@ -71,7 +71,13 @@ const methods = new Map([
71
71
  ['@harness/load-model', 31],
72
72
  [31, '@harness/load-model'],
73
73
  ['@harness/unload-model', 32],
74
- [32, '@harness/unload-model']
74
+ [32, '@harness/unload-model'],
75
+ ['@harness/set-model-memory-cap', 33],
76
+ [33, '@harness/set-model-memory-cap'],
77
+ ['@harness/memory-usage', 34],
78
+ [34, '@harness/memory-usage'],
79
+ ['@harness/set-downloads-allowed', 35],
80
+ [35, '@harness/set-downloads-allowed']
75
81
  ])
76
82
 
77
83
  class HRPC {
@@ -111,7 +117,10 @@ class HRPC {
111
117
  ['@harness/preview-model-source', getEncoding('@harness/preview-model-source-request')],
112
118
  ['@harness/set-model-idle-timeout', getEncoding('@harness/set-model-idle-timeout-request')],
113
119
  ['@harness/load-model', getEncoding('@harness/warm-model-request')],
114
- ['@harness/unload-model', getEncoding('@harness/unload-model-request')]
120
+ ['@harness/unload-model', getEncoding('@harness/unload-model-request')],
121
+ ['@harness/set-model-memory-cap', getEncoding('@harness/set-model-memory-cap-request')],
122
+ ['@harness/memory-usage', getEncoding('@harness/empty')],
123
+ ['@harness/set-downloads-allowed', getEncoding('@harness/set-downloads-allowed-request')]
115
124
  ])
116
125
  this._responseEncodings = new Map([
117
126
  ['@harness/completion', getEncoding('@harness/completion-event')],
@@ -146,7 +155,10 @@ class HRPC {
146
155
  ['@harness/preview-model-source', getEncoding('@harness/preview-model-source-response')],
147
156
  ['@harness/set-model-idle-timeout', getEncoding('@harness/ok-response')],
148
157
  ['@harness/load-model', getEncoding('@harness/load-model-response')],
149
- ['@harness/unload-model', getEncoding('@harness/unload-model-response')]
158
+ ['@harness/unload-model', getEncoding('@harness/unload-model-response')],
159
+ ['@harness/set-model-memory-cap', getEncoding('@harness/ok-response')],
160
+ ['@harness/memory-usage', getEncoding('@harness/memory-usage')],
161
+ ['@harness/set-downloads-allowed', getEncoding('@harness/ok-response')]
150
162
  ])
151
163
  this._rpc = new RPC(stream, async (req) => {
152
164
  const command = methods.get(req.command)
@@ -376,6 +388,18 @@ class HRPC {
376
388
  return this._call('@harness/unload-model', args)
377
389
  }
378
390
 
391
+ async setModelMemoryCap(args) {
392
+ return this._call('@harness/set-model-memory-cap', args)
393
+ }
394
+
395
+ async memoryUsage(args) {
396
+ return this._call('@harness/memory-usage', args)
397
+ }
398
+
399
+ async setDownloadsAllowed(args) {
400
+ return this._call('@harness/set-downloads-allowed', args)
401
+ }
402
+
379
403
  onCompletion(responseFn) {
380
404
  this._handlers['@harness/completion'] = responseFn
381
405
  }
@@ -508,6 +532,18 @@ class HRPC {
508
532
  this._handlers['@harness/unload-model'] = responseFn
509
533
  }
510
534
 
535
+ onSetModelMemoryCap(responseFn) {
536
+ this._handlers['@harness/set-model-memory-cap'] = responseFn
537
+ }
538
+
539
+ onMemoryUsage(responseFn) {
540
+ this._handlers['@harness/memory-usage'] = responseFn
541
+ }
542
+
543
+ onSetDownloadsAllowed(responseFn) {
544
+ this._handlers['@harness/set-downloads-allowed'] = responseFn
545
+ }
546
+
511
547
  _requestIsStream(command) {
512
548
  return [
513
549
  '@harness/completion',