tabc 0.2.1__tar.gz → 0.2.2__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 (40) hide show
  1. {tabc-0.2.1 → tabc-0.2.2}/CHANGELOG.md +45 -0
  2. {tabc-0.2.1/tabc.egg-info → tabc-0.2.2}/PKG-INFO +25 -7
  3. {tabc-0.2.1 → tabc-0.2.2}/PYPI.md +22 -1
  4. {tabc-0.2.1 → tabc-0.2.2}/pyproject.toml +1 -1
  5. {tabc-0.2.1 → tabc-0.2.2/tabc.egg-info}/PKG-INFO +25 -7
  6. {tabc-0.2.1 → tabc-0.2.2}/tabus/bus.py +1 -1
  7. {tabc-0.2.1 → tabc-0.2.2}/tabus/cli.py +1 -0
  8. {tabc-0.2.1 → tabc-0.2.2}/tabus/route_capture.py +3 -3
  9. {tabc-0.2.1 → tabc-0.2.2}/tabus/terminal_frontend.py +27 -7
  10. {tabc-0.2.1 → tabc-0.2.2}/GUIDE.md +0 -0
  11. {tabc-0.2.1 → tabc-0.2.2}/LICENSE +0 -0
  12. {tabc-0.2.1 → tabc-0.2.2}/MANIFEST.in +0 -0
  13. {tabc-0.2.1 → tabc-0.2.2}/MCP.md +0 -0
  14. {tabc-0.2.1 → tabc-0.2.2}/setup.cfg +0 -0
  15. {tabc-0.2.1 → tabc-0.2.2}/tabc.egg-info/SOURCES.txt +0 -0
  16. {tabc-0.2.1 → tabc-0.2.2}/tabc.egg-info/dependency_links.txt +0 -0
  17. {tabc-0.2.1 → tabc-0.2.2}/tabc.egg-info/entry_points.txt +0 -0
  18. {tabc-0.2.1 → tabc-0.2.2}/tabc.egg-info/requires.txt +0 -0
  19. {tabc-0.2.1 → tabc-0.2.2}/tabc.egg-info/top_level.txt +0 -0
  20. {tabc-0.2.1 → tabc-0.2.2}/tabus/__init__.py +0 -0
  21. {tabc-0.2.1 → tabc-0.2.2}/tabus/admin.py +0 -0
  22. {tabc-0.2.1 → tabc-0.2.2}/tabus/codex_queue.py +0 -0
  23. {tabc-0.2.1 → tabc-0.2.2}/tabus/compat_bcn.py +0 -0
  24. {tabc-0.2.1 → tabc-0.2.2}/tabus/compat_busd.py +0 -0
  25. {tabc-0.2.1 → tabc-0.2.2}/tabus/daemon.py +0 -0
  26. {tabc-0.2.1 → tabc-0.2.2}/tabus/doorbell.py +0 -0
  27. {tabc-0.2.1 → tabc-0.2.2}/tabus/doorbell_poll.py +0 -0
  28. {tabc-0.2.1 → tabc-0.2.2}/tabus/lang.py +0 -0
  29. {tabc-0.2.1 → tabc-0.2.2}/tabus/locales/en.json +0 -0
  30. {tabc-0.2.1 → tabc-0.2.2}/tabus/mcp_server.py +0 -0
  31. {tabc-0.2.1 → tabc-0.2.2}/tabus/nodekey.py +0 -0
  32. {tabc-0.2.1 → tabc-0.2.2}/tabus/paths.py +0 -0
  33. {tabc-0.2.1 → tabc-0.2.2}/tabus/process_argv.py +0 -0
  34. {tabc-0.2.1 → tabc-0.2.2}/tabus/send_refusal.py +0 -0
  35. {tabc-0.2.1 → tabc-0.2.2}/tabus/sent.py +0 -0
  36. {tabc-0.2.1 → tabc-0.2.2}/tabus/tac_identity.py +0 -0
  37. {tabc-0.2.1 → tabc-0.2.2}/tabus/tac_migration.py +0 -0
  38. {tabc-0.2.1 → tabc-0.2.2}/tabus/tac_refusal.py +0 -0
  39. {tabc-0.2.1 → tabc-0.2.2}/tabus/tac_search.py +0 -0
  40. {tabc-0.2.1 → tabc-0.2.2}/tabus/tac_store.py +0 -0
@@ -1,5 +1,50 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.2 — 2026-10-17
4
+
5
+ ### Changed
6
+
7
+ - `register` treats `--kind` case-insensitively and stores it in lowercase.
8
+ `--kind OpenCode` registers `opencode`.
9
+ - Terminal route capture at a detached session boundary no longer requires a
10
+ recognized Codex or Claude CLI. The immediate session-boundary parent
11
+ qualifies when it belongs to the same OS user, leads the target terminal's
12
+ foreground process group, and reads standard input from that terminal.
13
+ Process state, executable path, and standard input are checked again before
14
+ the route is accepted. Product names and install directories are not an
15
+ admission list.
16
+ - Codex queue selection under `--kind codex --auto-enter on` and its fallback
17
+ rules are unchanged. Because the kind is case-folded, `--kind Codex` now
18
+ selects the same queue route.
19
+
20
+ ### Notes
21
+
22
+ - The check verifies terminal ownership, not whether the program is an agent.
23
+ A foreground program that reads the terminal can qualify even if it is not
24
+ an agent. Automatic Enter remains a separate opt-in.
25
+ - No dedicated adapter for another agent product is added. The MCP adapter
26
+ still does not wake an agent.
27
+
28
+ ### Upgrade notes (from 0.2.1)
29
+
30
+ - The ledger schema is unchanged. Nodes, signing keys, messages, delivery
31
+ states, TACs, routes, and Codex queue receipts are kept.
32
+ - Stop `tabus.doorbell`, install 0.2.2, restart `tabd`, then start one
33
+ `tabus.doorbell` from the same installation:
34
+
35
+ ```bash
36
+ python -m pip install --upgrade "tabc==0.2.2"
37
+ # Or, for desktop MCP support:
38
+ python -m pip install --upgrade "tabc[mcp]==0.2.2"
39
+ ```
40
+
41
+ - Stored routes are not migrated. A failed capture does not replace an
42
+ existing route. Register again from the intended terminal, then confirm one
43
+ notification arrives.
44
+ - If a desktop client runs `tabc-mcp`, restart that MCP process from the client
45
+ after the upgrade so it loads the new installation.
46
+ - After restart, `tabc --version` and `tabd --version` report 0.2.2.
47
+
3
48
  ## 0.2.1 — 2026-10-15
4
49
 
5
50
  ### Added
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: tabc
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: A shared inbox for AI agents and programs on one machine
5
5
  License: MIT License
6
6
 
@@ -37,11 +37,8 @@ Classifier: Programming Language :: Python :: 3
37
37
  Classifier: Topic :: Communications
38
38
  Requires-Python: >=3.9
39
39
  Description-Content-Type: text/markdown
40
- License-File: LICENSE
41
- Requires-Dist: cryptography>=41
42
40
  Provides-Extra: mcp
43
- Requires-Dist: mcp<2,>=1.27.1; python_version >= "3.10" and extra == "mcp"
44
- Dynamic: license-file
41
+ License-File: LICENSE
45
42
 
46
43
  # tabc
47
44
 
@@ -108,6 +105,27 @@ from the `pull --mode full` detail block, or a unique prefix of it.
108
105
  Terminal notification is a separate process and does not start with the
109
106
  install; see the repository's guide before turning it on.
110
107
 
108
+ ## Updating from 0.2.1
109
+
110
+ The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
111
+ TACs, routes, and Codex queue receipts are kept. Stop `tabus.doorbell`, install,
112
+ restart `tabd`, then start one `tabus.doorbell` from the same installation:
113
+
114
+ ```bash
115
+ python -m pip install --upgrade "tabc==0.2.2"
116
+ # Or, when the local desktop MCP adapter is required:
117
+ python -m pip install --upgrade "tabc[mcp]==0.2.2"
118
+ ```
119
+
120
+ `--kind` is now case-insensitive; `--kind Codex` selects the same Codex queue
121
+ route as `--kind codex`. A detached command no longer needs a recognized agent
122
+ product: its immediate session-boundary parent qualifies when it owns the
123
+ terminal foreground and reads standard input from it. The check proves terminal
124
+ ownership, not that the program is an agent. Register again from the intended
125
+ terminal and confirm one notification arrives. Codex queue rules are otherwise
126
+ unchanged. See the
127
+ [changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
128
+
111
129
  ## Updating from 0.2.0
112
130
 
113
131
  The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
@@ -143,7 +161,7 @@ python -m pip install --upgrade "tabc[mcp]==0.2.0"
143
161
  Existing string TAC IDs are not converted during installation. Follow the
144
162
  repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
145
163
  to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
146
- Then follow "Updating from 0.2.0".
164
+ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
147
165
 
148
166
  ## Commands
149
167
 
@@ -63,6 +63,27 @@ from the `pull --mode full` detail block, or a unique prefix of it.
63
63
  Terminal notification is a separate process and does not start with the
64
64
  install; see the repository's guide before turning it on.
65
65
 
66
+ ## Updating from 0.2.1
67
+
68
+ The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
69
+ TACs, routes, and Codex queue receipts are kept. Stop `tabus.doorbell`, install,
70
+ restart `tabd`, then start one `tabus.doorbell` from the same installation:
71
+
72
+ ```bash
73
+ python -m pip install --upgrade "tabc==0.2.2"
74
+ # Or, when the local desktop MCP adapter is required:
75
+ python -m pip install --upgrade "tabc[mcp]==0.2.2"
76
+ ```
77
+
78
+ `--kind` is now case-insensitive; `--kind Codex` selects the same Codex queue
79
+ route as `--kind codex`. A detached command no longer needs a recognized agent
80
+ product: its immediate session-boundary parent qualifies when it owns the
81
+ terminal foreground and reads standard input from it. The check proves terminal
82
+ ownership, not that the program is an agent. Register again from the intended
83
+ terminal and confirm one notification arrives. Codex queue rules are otherwise
84
+ unchanged. See the
85
+ [changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
86
+
66
87
  ## Updating from 0.2.0
67
88
 
68
89
  The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
@@ -98,7 +119,7 @@ python -m pip install --upgrade "tabc[mcp]==0.2.0"
98
119
  Existing string TAC IDs are not converted during installation. Follow the
99
120
  repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
100
121
  to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
101
- Then follow "Updating from 0.2.0".
122
+ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
102
123
 
103
124
  ## Commands
104
125
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tabc"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "A shared inbox for AI agents and programs on one machine"
9
9
  readme = "PYPI.md"
10
10
  requires-python = ">=3.9"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: tabc
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: A shared inbox for AI agents and programs on one machine
5
5
  License: MIT License
6
6
 
@@ -37,11 +37,8 @@ Classifier: Programming Language :: Python :: 3
37
37
  Classifier: Topic :: Communications
38
38
  Requires-Python: >=3.9
39
39
  Description-Content-Type: text/markdown
40
- License-File: LICENSE
41
- Requires-Dist: cryptography>=41
42
40
  Provides-Extra: mcp
43
- Requires-Dist: mcp<2,>=1.27.1; python_version >= "3.10" and extra == "mcp"
44
- Dynamic: license-file
41
+ License-File: LICENSE
45
42
 
46
43
  # tabc
47
44
 
@@ -108,6 +105,27 @@ from the `pull --mode full` detail block, or a unique prefix of it.
108
105
  Terminal notification is a separate process and does not start with the
109
106
  install; see the repository's guide before turning it on.
110
107
 
108
+ ## Updating from 0.2.1
109
+
110
+ The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
111
+ TACs, routes, and Codex queue receipts are kept. Stop `tabus.doorbell`, install,
112
+ restart `tabd`, then start one `tabus.doorbell` from the same installation:
113
+
114
+ ```bash
115
+ python -m pip install --upgrade "tabc==0.2.2"
116
+ # Or, when the local desktop MCP adapter is required:
117
+ python -m pip install --upgrade "tabc[mcp]==0.2.2"
118
+ ```
119
+
120
+ `--kind` is now case-insensitive; `--kind Codex` selects the same Codex queue
121
+ route as `--kind codex`. A detached command no longer needs a recognized agent
122
+ product: its immediate session-boundary parent qualifies when it owns the
123
+ terminal foreground and reads standard input from it. The check proves terminal
124
+ ownership, not that the program is an agent. Register again from the intended
125
+ terminal and confirm one notification arrives. Codex queue rules are otherwise
126
+ unchanged. See the
127
+ [changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
128
+
111
129
  ## Updating from 0.2.0
112
130
 
113
131
  The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
@@ -143,7 +161,7 @@ python -m pip install --upgrade "tabc[mcp]==0.2.0"
143
161
  Existing string TAC IDs are not converted during installation. Follow the
144
162
  repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
145
163
  to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
146
- Then follow "Updating from 0.2.0".
164
+ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
147
165
 
148
166
  ## Commands
149
167
 
@@ -74,7 +74,7 @@ DB_PATH = os.environ.get(
74
74
  # drifted once already — 0.1.0.post1 shipped while this said 0.1.0. Bump
75
75
  # both together; tests/test_version_agreement.py fails the build otherwise.
76
76
  # Do not copy the number into documentation.
77
- __version__ = "0.2.1"
77
+ __version__ = "0.2.2"
78
78
  PROTOCOL_VERSION = "tabus.v1"
79
79
 
80
80
  # TTLs. Past these, a derived status counts as old.
@@ -251,6 +251,7 @@ def fn_config(a):
251
251
 
252
252
  def fn_register(a):
253
253
  node = _require_acting_node(a.node)
254
+ a.kind = a.kind.casefold()
254
255
  payload = {"node": node, "kind": a.kind}
255
256
  take_route = bool(getattr(a, "take_route", False))
256
257
  setting = getattr(a, "auto_enter", None)
@@ -6,8 +6,8 @@ that terminal. A detached shared runner can therefore carry one tab's route,
6
6
  allocate a fresh PTY, and still register the stale tab. Route capture accepts
7
7
  the inherited key only when its resolved TTY equals the controlling TTY of this
8
8
  process or a same-session ancestor. A detached command may also use its immediate
9
- session-boundary parent when that parent is a recognized native interactive CLI
10
- and the foreground process-group leader of the target terminal.
9
+ session-boundary parent when that parent owns the target terminal foreground
10
+ process group and its standard input is that same terminal, regardless of kind.
11
11
  """
12
12
 
13
13
  import os
@@ -99,7 +99,7 @@ def terminal_tty(
99
99
 
100
100
  ``ps`` is used instead of ``/proc`` so this works on both macOS and Linux.
101
101
  Any lookup failure is a negative result. The cross-session fallback is
102
- restricted to a checked native foreground CLI, not arbitrary ancestors.
102
+ restricted to a checked foreground terminal owner, not arbitrary ancestors.
103
103
  """
104
104
  inspect = process_row or _process_row
105
105
  inspect_session = process_session or _session_id
@@ -1,7 +1,7 @@
1
- """Recognize a foreground native agent CLI, never a generic terminal ancestor.
1
+ """Verify a foreground terminal owner without a product-name allowlist.
2
2
 
3
3
  This is local routing evidence, not an isolation boundary against a malicious
4
- process running as the same OS user. Unknown launch modes fail closed.
4
+ process running as the same OS user. Missing terminal evidence fails closed.
5
5
  """
6
6
 
7
7
  import ctypes
@@ -57,7 +57,7 @@ def executable_kind(executable, user_home):
57
57
 
58
58
 
59
59
  def interactive_arguments(kind, argv):
60
- """Allow inspected interactive modes only; no arbitrary prompt/subcommand."""
60
+ """Legacy argument classifier; not used by generic route registration."""
61
61
  if not argv or os.path.basename(argv[0]) != kind:
62
62
  return False
63
63
  flags = {
@@ -120,11 +120,27 @@ def process_info(pid):
120
120
  fields[4], args)
121
121
 
122
122
 
123
+ def input_terminal(pid):
124
+ """Resolve fd 0, not inherited environment variables or a process name."""
125
+ if sys.platform.startswith("linux"):
126
+ return os.path.realpath(os.readlink(f"/proc/{int(pid)}/fd/0"))
127
+ if sys.platform == "darwin":
128
+ result = subprocess.run(
129
+ ["/usr/sbin/lsof", "-a", "-p", str(int(pid)), "-d", "0", "-Fn"],
130
+ capture_output=True, text=True, timeout=1, check=False,
131
+ )
132
+ names = [line[1:] for line in result.stdout.splitlines() if line.startswith("n")]
133
+ if result.returncode == 0 and len(names) == 1:
134
+ return names[0]
135
+ return None
136
+
137
+
123
138
  def foreground_frontend_tty(pid, expected_tty, inspect=None, executable=None,
124
- uid=None, user_home=None):
139
+ uid=None, user_home=None, stdin_terminal=None):
125
140
  """Only the boundary parent itself may qualify; never walk past a server."""
126
141
  inspect = inspect or process_info
127
142
  executable = executable or native_executable
143
+ stdin_terminal = stdin_terminal or input_terminal
128
144
  uid = os.getuid() if uid is None else uid
129
145
  try:
130
146
  user_home = pwd.getpwuid(uid).pw_dir if user_home is None else user_home
@@ -135,11 +151,15 @@ def foreground_frontend_tty(pid, expected_tty, inspect=None, executable=None,
135
151
  if owner != uid or not (pid == group == foreground) or tty != expected_tty:
136
152
  return None
137
153
  path = executable(pid)
138
- kind = executable_kind(path, user_home) if path else None
139
- if kind is None or not interactive_arguments(kind, argv):
154
+ # Generic terminal routing is independent of kind and installation path.
155
+ # The immediate boundary parent must own the foreground group AND read
156
+ # from that terminal. A pipe-backed server is not a terminal recipient.
157
+ input_path = "/dev/" + expected_tty.removeprefix("/dev/")
158
+ if not path or stdin_terminal(pid) != input_path:
140
159
  return None
141
160
  # Recheck mutable process state around executable and command inspection.
142
- if inspect(pid) != before or executable(pid) != path:
161
+ if (inspect(pid) != before or executable(pid) != path
162
+ or stdin_terminal(pid) != input_path):
143
163
  return None
144
164
  return tty
145
165
  except (OSError, ValueError, TypeError, KeyError, ctypes.ArgumentError,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes