@qvac/harness 0.3.1 → 0.3.2

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 (27) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/dist/harness.bundle +7 -5
  3. package/dist-lib/lib/capabilities/completion.d.ts +2 -1
  4. package/dist-lib/lib/capabilities/completion.js +3 -3
  5. package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +3 -1
  6. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +5 -0
  7. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +28 -2
  8. package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +5 -0
  9. package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.js +29 -0
  10. package/dist-lib/lib/tools/exec/adapters/python/named-hosts.d.ts +2 -0
  11. package/dist-lib/lib/tools/exec/adapters/python/named-hosts.js +15 -0
  12. package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +3 -1
  13. package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +41 -7
  14. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +9 -3
  15. package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +2 -0
  16. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +19 -11
  17. package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +2 -1
  18. package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +16 -3
  19. package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +15 -2
  20. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +6 -0
  21. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +6 -2
  22. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-bare.d.ts +1 -0
  23. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-bare.js +8 -0
  24. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +1 -0
  25. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +8 -0
  26. package/dist-lib/lib/tools/exec.js +36 -3
  27. package/package.json +11 -1
@@ -4,6 +4,7 @@ import { AbortedError } from '../helpers/race-abort.js';
4
4
  import { spawnEnv } from '../helpers/spawn-path.js';
5
5
  import { checkPdfBuildSource, checkBareLengthArgs, checkPptxBuildSource, checkPythonSource, checkUnstagedRead, createPythonAdapter, isPythonLanguage, parseInputs, parseOutputs, parsePackages, parseWorkdir, pinned, runsThroughPyodide, unwrapPythonSource } from './exec/adapters/python/python-adapter.js';
6
6
  import { pythonCreateEffect } from './exec/adapters/python/create-effect.js';
7
+ import { namedHosts } from './exec/adapters/python/named-hosts.js';
7
8
  import { pythonReachesNetwork } from './exec/adapters/python/net-shim.js';
8
9
  import { createShellAdapter } from './exec/adapters/shell/shell-adapter.js';
9
10
  import { routeObsidianCommand } from './exec/obsidian/route.js';
@@ -82,6 +83,34 @@ function checkFileArgs(args, python) {
82
83
  function truncateLabel(text) {
83
84
  return text.length > 120 ? `${text.slice(0, 120)}…` : text;
84
85
  }
86
+ const CONNECT_ANSWER_MS = 60_000;
87
+ function connectAsker(askUser) {
88
+ return async (host) => {
89
+ let timer;
90
+ const late = new Promise((resolve) => {
91
+ timer = setTimeout(() => resolve(null), CONNECT_ANSWER_MS);
92
+ });
93
+ try {
94
+ const answer = await Promise.race([
95
+ askUser({
96
+ question: `This Python run wants to connect to ${host}, which its approved code does not name. Allow it for this run?`,
97
+ options: ['Allow', 'Deny']
98
+ }),
99
+ late
100
+ ]);
101
+ return /^allow/i.test((answer ?? '').trim());
102
+ }
103
+ finally {
104
+ clearTimeout(timer);
105
+ }
106
+ };
107
+ }
108
+ function networkPrefix(source) {
109
+ const hosts = namedHosts(source);
110
+ if (hosts.length === 0)
111
+ return ' · network: no host named in the code';
112
+ return ` · network: ${hosts.join(', ')}`;
113
+ }
85
114
  function packageSuffix(args) {
86
115
  if (!Array.isArray(args.packages) || args.packages.length === 0)
87
116
  return '';
@@ -428,7 +457,7 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
428
457
  return PACKAGES_NEED_PYTHON_ERROR;
429
458
  return checkFileArgs(rawArgs, python);
430
459
  },
431
- resource(rawArgs) {
460
+ resource(rawArgs, ctx) {
432
461
  const args = withInferredLanguage(rawArgs, disablePyodide);
433
462
  const script = typeof args.script === 'string' ? args.script : '';
434
463
  if (script.trim() && !script.includes('\n')) {
@@ -438,8 +467,10 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
438
467
  if (!command)
439
468
  return null;
440
469
  const workdir = typeof args.workdir === 'string' && args.workdir.trim() ? args.workdir.trim() : null;
470
+ const source = unwrapPythonSource(command) ?? command;
471
+ const online = pythonNetwork && ctx?.sandboxMode !== true;
441
472
  const body = runsThroughPyodide(args.language, disablePyodide)
442
- ? `python${packageSuffix(args)}${fileSuffix(args)}: ${(unwrapPythonSource(command) ?? command).slice(0, 120)}`
473
+ ? `python${online ? networkPrefix(source) : ''}${packageSuffix(args)}${fileSuffix(args)}: ${source.slice(0, 120)}`
443
474
  : command;
444
475
  return {
445
476
  kind: 'command',
@@ -515,7 +546,9 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
515
546
  turnImages: ctx?.turnImages,
516
547
  runBudget: ctx?.runBudget,
517
548
  sandboxMode: ctx?.sandboxMode,
518
- toolNames: ctx?.toolNames
549
+ toolNames: ctx?.toolNames,
550
+ approval: ctx?.approval,
551
+ confirmConnect: ctx?.askUser ? connectAsker(ctx.askUser) : undefined
519
552
  });
520
553
  return await execution.run({
521
554
  cwd,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qvac/harness",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
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": {
@@ -220,6 +220,10 @@
220
220
  "types": "./dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts",
221
221
  "default": "./dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js"
222
222
  },
223
+ "./lib/tools/exec/adapters/python/interrupt-timer.ts": {
224
+ "types": "./dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts",
225
+ "default": "./dist-lib/lib/tools/exec/adapters/python/interrupt-timer.js"
226
+ },
223
227
  "./lib/tools/exec/adapters/python/loader.ts": {
224
228
  "types": "./dist-lib/lib/tools/exec/adapters/python/loader.d.ts",
225
229
  "default": "./dist-lib/lib/tools/exec/adapters/python/loader.js"
@@ -232,6 +236,10 @@
232
236
  "types": "./dist-lib/lib/tools/exec/adapters/python/module-distributions.d.ts",
233
237
  "default": "./dist-lib/lib/tools/exec/adapters/python/module-distributions.js"
234
238
  },
239
+ "./lib/tools/exec/adapters/python/named-hosts.ts": {
240
+ "types": "./dist-lib/lib/tools/exec/adapters/python/named-hosts.d.ts",
241
+ "default": "./dist-lib/lib/tools/exec/adapters/python/named-hosts.js"
242
+ },
235
243
  "./lib/tools/exec/adapters/python/net-shim.ts": {
236
244
  "types": "./dist-lib/lib/tools/exec/adapters/python/net-shim.d.ts",
237
245
  "default": "./dist-lib/lib/tools/exec/adapters/python/net-shim.js"
@@ -748,7 +756,9 @@
748
756
  "corestore": "^7.11.0",
749
757
  "holepunch-types": "^0.2.1",
750
758
  "hyperdrive": "^13.3.3",
759
+ "jsdom": "~30.0.1",
751
760
  "lunte": "^1.8.3",
761
+ "mermaid": "~11.16.1",
752
762
  "prettier": "^3.6.2",
753
763
  "prettier-config-holepunch": "^2.0.0",
754
764
  "prettytap": "^0.2.0",