witbitz-code 1.9.2__tar.gz → 1.9.3__tar.gz

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 (117) hide show
  1. {witbitz_code-1.9.2/src/witbitz_code.egg-info → witbitz_code-1.9.3}/PKG-INFO +1 -1
  2. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/pyproject.toml +1 -1
  3. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/__init__.py +1 -1
  4. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/confidential.py +46 -1
  5. {witbitz_code-1.9.2 → witbitz_code-1.9.3/src/witbitz_code.egg-info}/PKG-INFO +1 -1
  6. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_confidential.py +32 -0
  7. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/LICENSE +0 -0
  8. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/README.md +0 -0
  9. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/setup.cfg +0 -0
  10. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/__main__.py +0 -0
  11. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/_js.py +0 -0
  12. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/account.py +0 -0
  13. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/asks.py +0 -0
  14. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/attachments.py +0 -0
  15. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/auto.py +0 -0
  16. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/auto_runner.py +0 -0
  17. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/browse.py +0 -0
  18. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/browsetree.py +0 -0
  19. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/cli.py +0 -0
  20. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/codekeys.py +0 -0
  21. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/codetools.py +0 -0
  22. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/compress.py +0 -0
  23. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/computers.py +0 -0
  24. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/confidential_models.json +0 -0
  25. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/connector.py +0 -0
  26. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/devicelink.py +0 -0
  27. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/dirs.py +0 -0
  28. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/folders.py +0 -0
  29. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/gateway.py +0 -0
  30. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/icons/witbitz-code-wsl.ico +0 -0
  31. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/icons/witbitz-code.ico +0 -0
  32. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/images.py +0 -0
  33. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/keys.py +0 -0
  34. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/link.py +0 -0
  35. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/mkdir.py +0 -0
  36. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/models.py +0 -0
  37. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/net.py +0 -0
  38. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/offered_models.json +0 -0
  39. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/notes-init.md +0 -0
  40. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/witbitz-browse-cdp.mjs +0 -0
  41. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/witbitz-browse-local.mjs +0 -0
  42. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/witbitz-browse-publisher.mjs +0 -0
  43. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/witbitz-browse-tree.mjs +0 -0
  44. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/witbitz-browser.js +0 -0
  45. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/witbitz-media-server.mjs +0 -0
  46. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/witbitz-notes.js +0 -0
  47. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/opencode_plugins/witbitz-progress.js +0 -0
  48. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/outputs.py +0 -0
  49. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/pair.py +0 -0
  50. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/pairings.py +0 -0
  51. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/payers.py +0 -0
  52. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/plugins.py +0 -0
  53. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/policy.py +0 -0
  54. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/ports.py +0 -0
  55. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/qr.py +0 -0
  56. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/receipt.py +0 -0
  57. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/relay.py +0 -0
  58. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/remind.py +0 -0
  59. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/seccheck.py +0 -0
  60. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/seen.py +0 -0
  61. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/selfupdate.py +0 -0
  62. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/tinfoil_read.py +0 -0
  63. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/tools_catalog.json +0 -0
  64. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/tools_probe.py +0 -0
  65. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/toolsetup.py +0 -0
  66. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/ui.py +0 -0
  67. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/ui_static/app.css +0 -0
  68. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/ui_static/app.js +0 -0
  69. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/ui_static/index.html +0 -0
  70. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/uitools.py +0 -0
  71. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/uninstall.py +0 -0
  72. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/winagent.py +0 -0
  73. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/winbg.py +0 -0
  74. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/winconsole.py +0 -0
  75. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/wizard.py +0 -0
  76. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code/wslstart.py +0 -0
  77. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code.egg-info/SOURCES.txt +0 -0
  78. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code.egg-info/dependency_links.txt +0 -0
  79. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code.egg-info/entry_points.txt +0 -0
  80. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code.egg-info/requires.txt +0 -0
  81. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/src/witbitz_code.egg-info/top_level.txt +0 -0
  82. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_asks.py +0 -0
  83. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_attachments.py +0 -0
  84. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_auto.py +0 -0
  85. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_browse.py +0 -0
  86. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_browsetree.py +0 -0
  87. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_cli.py +0 -0
  88. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_codekeys.py +0 -0
  89. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_codetools.py +0 -0
  90. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_connector_e2e.py +0 -0
  91. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_devicelink.py +0 -0
  92. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_dirs.py +0 -0
  93. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_files_and_compress.py +0 -0
  94. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_folders.py +0 -0
  95. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_images.py +0 -0
  96. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_outputs.py +0 -0
  97. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_pair_flows.py +0 -0
  98. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_plugins.py +0 -0
  99. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_policy.py +0 -0
  100. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_ports.py +0 -0
  101. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_ports_win.py +0 -0
  102. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_registry.py +0 -0
  103. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_relay_codec.py +0 -0
  104. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_remind.py +0 -0
  105. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_seccheck.py +0 -0
  106. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_selfupdate.py +0 -0
  107. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_setup.py +0 -0
  108. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_tools_probe.py +0 -0
  109. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_toolsetup.py +0 -0
  110. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_ui.py +0 -0
  111. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_uitools.py +0 -0
  112. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_uninstall.py +0 -0
  113. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_winagent.py +0 -0
  114. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_winbg.py +0 -0
  115. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_winconsole.py +0 -0
  116. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_windows_files.py +0 -0
  117. {witbitz_code-1.9.2 → witbitz_code-1.9.3}/tests/test_wslstart.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: witbitz-code
3
- Version: 1.9.2
3
+ Version: 1.9.3
4
4
  Summary: Reach OpenCode on this computer from the Witbitz Spaces Code section, through an end-to-end sealed relay.
5
5
  Author: Witbitz
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "witbitz-code"
7
- version = "1.9.2"
7
+ version = "1.9.3"
8
8
  description = "Reach OpenCode on this computer from the Witbitz Spaces Code section, through an end-to-end sealed relay."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -4,4 +4,4 @@ The Python implementation of tools/witbitz-code.mjs (docs/opencode-relay.md phas
4
4
  vectors and cross-implementation tests.
5
5
  """
6
6
 
7
- __version__ = "1.9.2"
7
+ __version__ = "1.9.3"
@@ -261,6 +261,28 @@ def without_words(frame: str) -> str:
261
261
  return f"data: {dumps({**ev, 'choices': [{**ch, 'delta': rest}, *ev['choices'][1:]]})}\n\n"
262
262
 
263
263
 
264
+ # The confidential default (⇄ tools/code-confidential.mjs CONFIDENTIAL_DEFAULT): what answers when a pick has no verified-enclave
265
+ # route right now. The note says so once per session — a Code task makes many calls.
266
+ CONFIDENTIAL_DEFAULT = "deepseek/deepseek-v4.1-flash"
267
+ _NOTED: dict = {}
268
+
269
+
270
+ def fallback_note(frm: str, by: str, session_id: str = "") -> str:
271
+ if not frm:
272
+ return ""
273
+ k = session_id + "\0" + frm
274
+ if k in _NOTED:
275
+ return ""
276
+ _NOTED[k] = True
277
+ if len(_NOTED) > 500:
278
+ _NOTED.pop(next(iter(_NOTED)))
279
+ return f"\n\n_Answered by {by} — {frm} isn't available confidentially right now._"
280
+
281
+
282
+ def note_frame(note: str, model: str) -> str:
283
+ return "data: " + dumps({"id": "witbitz-note", "object": "chat.completion.chunk", "model": model, "choices": [{"index": 0, "delta": {"content": note}}]}) + "\n\n"
284
+
285
+
264
286
  def completion_of(events: list, model: str) -> dict:
265
287
  """The streamed events as one chat.completion — for a caller that asked without `stream`."""
266
288
  content = reasoning = ""
@@ -652,7 +674,10 @@ class ConfidentialProxy:
652
674
  except Exception: # noqa: BLE001 — progress must never fail a call
653
675
  pass
654
676
 
677
+ fell = {"from": ""} # the pick's label when TrustedRouter had no verified-enclave route for it and the default answered
678
+
655
679
  async def once(words_shown: bool = False) -> None:
680
+ nonlocal body, conf, label
656
681
  ends = self._proof_ends.get(body["model"])
657
682
  hold = attempt["n"] > 1 or (ends is not None and self._now() / 1000 >= ends - HOLD_BEFORE_LAPSE_SEC)
658
683
  n = self._nonce()
@@ -670,6 +695,16 @@ class ConfidentialProxy:
670
695
  used = allowance_used(up.status_code, text)
671
696
  if used:
672
697
  raise used
698
+ # No verified-enclave route for the pick (2026-10-20, ⇄ tools/code-confidential.mjs): refused BEFORE any upstream
699
+ # saw the request — the same request goes again as the confidential default, attested and checked, once.
700
+ if (up.status_code == 400 and "no route candidates" in text.lower() and not fell["from"] and body.get("model") != CONFIDENTIAL_DEFAULT
701
+ and CONFIDENTIAL_DEFAULT in self.models and not res.headers_sent):
702
+ fell["from"] = conf["label"]
703
+ self.log(f"code-confidential: {body['model']} has no verified-enclave route now → {CONFIDENTIAL_DEFAULT} answers")
704
+ body = {**body, "model": CONFIDENTIAL_DEFAULT}
705
+ conf = self.models[CONFIDENTIAL_DEFAULT]
706
+ label = f"{conf['label']} (confidential)"
707
+ return await once(words_shown)
673
708
  if res.headers_sent:
674
709
  raise Refusal(502, f"{label}: TrustedRouter answered {up.status_code} when asked again ({text[:160]})", "upstream_error")
675
710
  await res.head(up.status_code, {"content-type": up.headers.get("content-type", "application/json")})
@@ -710,15 +745,25 @@ class ConfidentialProxy:
710
745
  exp = ((v.get("claims") or {}).get("upstream") or {}).get("verification_expires_at")
711
746
  if isinstance(exp, (int, float)):
712
747
  self._proof_ends[body["model"]] = exp
748
+ note = fallback_note(fell["from"], conf["label"], session_id)
713
749
  if wants_stream:
750
+ noted = not note
714
751
  for f in answer.held():
752
+ if not noted and re.search(r'"finish_reason":"|^data: \[DONE\]', f, re.M):
753
+ await res.write(note_frame(note, body["model"])) # the note goes in before the answer ends
754
+ noted = True
715
755
  out = without_words(f) if words_shown else f
716
756
  if out:
717
757
  await res.write(out)
758
+ if not noted:
759
+ await res.write(note_frame(note, body["model"]))
718
760
  await res.end()
719
761
  return
720
762
  await res.head(200, {"content-type": "application/json"})
721
- await res.end(dumps(completion_of(answer.events(), body["model"])))
763
+ done = completion_of(answer.events(), body["model"])
764
+ if note and done.get("choices") and done["choices"][0].get("message") is not None:
765
+ done["choices"][0]["message"]["content"] = (done["choices"][0]["message"].get("content") or "") + note
766
+ await res.end(dumps(done))
722
767
 
723
768
  try:
724
769
  if not conf.get("vision"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: witbitz-code
3
- Version: 1.9.2
3
+ Version: 1.9.3
4
4
  Summary: Reach OpenCode on this computer from the Witbitz Spaces Code section, through an end-to-end sealed relay.
5
5
  Author: Witbitz
6
6
  License-Expression: MIT
@@ -410,3 +410,35 @@ def test_the_months_allowance_used_is_a_402_that_says_so_never_a_429_opencode_wo
410
410
  with_proxy(t, net=LimitNet(meta, stream.decode("utf-8")), models=models)
411
411
  assert cf.allowance_used(429, '{"error":{"message":"rate limited"}}') is None, "a real rate limit stays a rate limit"
412
412
  assert cf.allowance_used(400, json.dumps(SPEND_LIMIT)) is None
413
+
414
+
415
+ class NoRouteThenReplay(FakeNet):
416
+ """TrustedRouter refuses any model but the default ("No route candidates" — before any upstream sees it); the default
417
+ answers with the captured stream."""
418
+
419
+ def handler(self, req: httpx.Request):
420
+ if "googleapis.com/service_accounts" not in str(req.url) and json.loads(req.content.decode("utf-8")).get("model") != cf.CONFIDENTIAL_DEFAULT:
421
+ self.calls.append({"url": str(req.url), "headers": dict(req.headers), "raw": list(req.headers.raw), "body": req.content.decode("utf-8")})
422
+ return httpx.Response(400, json={"error": {"message": "No route candidates match the requested provider filters"}})
423
+ return super().handler(req)
424
+
425
+
426
+ @needs_fixture
427
+ def test_a_pick_with_no_verified_enclave_route_is_answered_by_the_default_with_one_note_per_session():
428
+ meta, stream, request = fixture()
429
+ net = NoRouteThenReplay(meta, stream.decode("utf-8"))
430
+ logs = []
431
+ glm = {**SAY_OK, "model": "z-ai/glm-5.3"}
432
+
433
+ async def t(post, net_, _p):
434
+ r = await post(glm, {"x-session-id": "ses_fb"})
435
+ text = r.text
436
+ assert [json.loads(c["body"])["model"] for c in net.calls] == ["z-ai/glm-5.3", "deepseek/deepseek-v4.1-flash"], "the pick, then the default — once"
437
+ assert net.calls[1]["body"].encode() == request, "the same request, only the model changed — the receipt signs it"
438
+ assert '"content":"OK"' in text
439
+ assert "Answered by DeepSeek V4.1 Flash — GLM 5.3 isn't available confidentially right now." in text
440
+ assert text.index("Answered by") < text.index("[DONE]")
441
+ again = (await post(glm, {"x-session-id": "ses_fb"})).text
442
+ assert "Answered by" not in again and '"content":"OK"' in again, "said once per session"
443
+ assert any("has no verified-enclave route now" in line for line in logs)
444
+ with_proxy(t, net=net, log=logs.append)
File without changes
File without changes
File without changes