bazaar-compute-node 0.1.10__tar.gz → 0.1.12__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.
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/PKG-INFO +2 -2
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/README.md +1 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/pyproject.toml +2 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/pyproject.toml.orig +2 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/agent.py +4 -3
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/transport.py +2 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/wrapper.py +4 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/cli.py +4 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/codex/client.py +3 -3
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/codex/process.py +2 -2
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/codex/protocol.py +1 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/codex/runtime.py +1 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/codec.py +21 -21
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/reminder_repository.py +32 -32
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/repository.py +58 -59
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/scoped_repository.py +47 -45
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/approval.py +52 -19
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/channel.py +3 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/markdown.py +1 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/outbound.py +1 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/wecom/channel.py +2 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/wecom/markdown.py +1 -3
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/channel.py +2 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/models/states.py +2 -2
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/reminder.py +1 -1
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/reminder_schedule.py +3 -3
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/i18n/catalog.py +1 -1
- bazaar_compute_node-0.1.12/src/bazaar_compute_node/i18n/english.py +26 -0
- bazaar_compute_node-0.1.12/src/bazaar_compute_node/i18n/schinese.py +26 -0
- bazaar_compute_node-0.1.10/src/bazaar_compute_node/i18n/english.py +0 -10
- bazaar_compute_node-0.1.10/src/bazaar_compute_node/i18n/schinese.py +0 -10
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/LICENSE +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/agent_management.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/application.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/attachments.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/command.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/config.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/registry.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/reminder_dispatch.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/windows_pipe.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/bcc.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/codex/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/codex/approval.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/codex/events.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/codex/plugin.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/logging/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/logging/audit.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/logging/plugin.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/agent_migration.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/database.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/migrations.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/plugin.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/reminder_codec.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/reminder_database.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/sqlite/reminder_migration.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/api.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/attachments.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/identity.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/telegram/plugin.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/wecom/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/wecom/outbound.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/contrib/wecom/plugin.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/approval.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/audit.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/client.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/command.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/concurrency.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/correlation.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/instruction.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/lifecycle.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/models/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/models/entities.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/models/reminder.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/models/reminder_owner.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/observability.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/__init__.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/command.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/delivery.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/error_feedback.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/reminder.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/reminder_command.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/services.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/session.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/orchestration/turn.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/outcomes.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/paths.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/runtime.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/storage.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/core/timerwheel.py +0 -0
- {bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/i18n/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bazaar-compute-node
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12
|
|
4
4
|
Summary: Runtime-agnostic computer node daemon for agents and channels.
|
|
5
5
|
License-Expression: AGPL-3.0-only
|
|
6
6
|
License-File: LICENSE
|
|
@@ -67,7 +67,7 @@ bcn stop
|
|
|
67
67
|
| --- | --- |
|
|
68
68
|
| ✅ | Reminders / 定时器 |
|
|
69
69
|
| ✅ | Attachments / 附件 |
|
|
70
|
-
|
|
|
70
|
+
| ✅ | Multi-Agents / 多 Agent |
|
|
71
71
|
| 🚧 | Teams / 团队协作 |
|
|
72
72
|
|
|
73
73
|
### Channels / 渠道
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "bazaar-compute-node"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.12"
|
|
4
4
|
description = "Runtime-agnostic computer node daemon for agents and channels."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "AGPL-3.0-only"
|
|
@@ -35,6 +35,7 @@ logging = "bazaar_compute_node.contrib.logging.plugin:create_audit"
|
|
|
35
35
|
[dependency-groups]
|
|
36
36
|
dev = [
|
|
37
37
|
"bcn-test-support",
|
|
38
|
+
"pyright==1.1.408",
|
|
38
39
|
"pytest>=8.3.0",
|
|
39
40
|
"pytest-asyncio>=0.24.0",
|
|
40
41
|
"ruff>=0.9.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "bazaar-compute-node"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.12"
|
|
4
4
|
description = "Runtime-agnostic computer node daemon for agents and channels."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "AGPL-3.0-only"
|
|
@@ -35,6 +35,7 @@ logging = "bazaar_compute_node.contrib.logging.plugin:create_audit"
|
|
|
35
35
|
[dependency-groups]
|
|
36
36
|
dev = [
|
|
37
37
|
"bcn-test-support",
|
|
38
|
+
"pyright==1.1.408",
|
|
38
39
|
"pytest>=8.3.0",
|
|
39
40
|
"pytest-asyncio>=0.24.0",
|
|
40
41
|
"ruff>=0.9.0",
|
{bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/agent.py
RENAMED
|
@@ -101,6 +101,7 @@ class AgentApplication:
|
|
|
101
101
|
attachments=self._attachment_materializer,
|
|
102
102
|
options=dict(configuration.channel.options),
|
|
103
103
|
workspace=self.workspace_path,
|
|
104
|
+
translator=self.translator,
|
|
104
105
|
)
|
|
105
106
|
)
|
|
106
107
|
self.channel: IChannel = AgentScopedChannel(self.agent_id, provider_channel)
|
|
@@ -443,8 +444,8 @@ class AgentApplication:
|
|
|
443
444
|
|
|
444
445
|
def _error_feedback_detail(
|
|
445
446
|
self,
|
|
446
|
-
session_id:
|
|
447
|
-
error_message:
|
|
447
|
+
session_id: object,
|
|
448
|
+
error_message: object,
|
|
448
449
|
) -> str:
|
|
449
450
|
if not isinstance(session_id, str) or not session_id:
|
|
450
451
|
raise ValueError("session_id must be a non-empty string")
|
|
@@ -490,7 +491,7 @@ class AgentApplication:
|
|
|
490
491
|
)
|
|
491
492
|
input_data = body.encode() if body is not None else None
|
|
492
493
|
try:
|
|
493
|
-
|
|
494
|
+
_, stderr = await process.communicate(input=input_data)
|
|
494
495
|
except asyncio.CancelledError:
|
|
495
496
|
if process.returncode is None:
|
|
496
497
|
process.terminate()
|
{bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/transport.py
RENAMED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
|
+
import platform
|
|
6
7
|
import secrets
|
|
7
8
|
import sys
|
|
8
9
|
import tempfile
|
|
@@ -223,7 +224,7 @@ class LocalCommandClient:
|
|
|
223
224
|
parsed = urlsplit(endpoint)
|
|
224
225
|
request = dict(payload)
|
|
225
226
|
if parsed.scheme == "pipe":
|
|
226
|
-
if
|
|
227
|
+
if platform.system() != "Windows":
|
|
227
228
|
raise ValueError(
|
|
228
229
|
"Windows named pipe endpoints are not supported on this platform"
|
|
229
230
|
)
|
{bazaar_compute_node-0.1.10 → bazaar_compute_node-0.1.12}/src/bazaar_compute_node/app/wrapper.py
RENAMED
|
@@ -32,11 +32,15 @@ def install_bcc_wrapper(bin_dir: Path, *, agent_id: str) -> Path:
|
|
|
32
32
|
command_path.write_text(
|
|
33
33
|
"@echo off\n"
|
|
34
34
|
f'set "BCN_AGENT_ID={agent_id}"\n'
|
|
35
|
+
'set "PYTHONUTF8=1"\n'
|
|
36
|
+
'set "PYTHONIOENCODING=utf-8"\n'
|
|
35
37
|
f'"{python_executable}" -m bazaar_compute_node.bcc %*\n',
|
|
36
38
|
encoding="utf-8",
|
|
37
39
|
)
|
|
38
40
|
(bin_dir / "bcc.ps1").write_text(
|
|
39
41
|
f"$env:BCN_AGENT_ID = '{agent_id}'\n"
|
|
42
|
+
"$env:PYTHONUTF8 = '1'\n"
|
|
43
|
+
"$env:PYTHONIOENCODING = 'utf-8'\n"
|
|
40
44
|
f'& "{python_executable}" -m bazaar_compute_node.bcc @args\n'
|
|
41
45
|
"exit $LASTEXITCODE\n",
|
|
42
46
|
encoding="utf-8",
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import argparse
|
|
4
4
|
import asyncio
|
|
5
5
|
import os
|
|
6
|
+
import platform
|
|
6
7
|
import subprocess
|
|
7
8
|
import sys
|
|
8
9
|
from collections.abc import Mapping, Sequence
|
|
@@ -334,7 +335,9 @@ async def _start_daemon(
|
|
|
334
335
|
await asyncio.to_thread(data_dir.mkdir, parents=True, exist_ok=True)
|
|
335
336
|
endpoint_path = _endpoint_path(args, data_dir)
|
|
336
337
|
endpoint = local_endpoint_for_path(endpoint_path)
|
|
337
|
-
if
|
|
338
|
+
if platform.system() == "Windows" and await _endpoint_is_reachable(
|
|
339
|
+
endpoint, timeout=0.5
|
|
340
|
+
):
|
|
338
341
|
parser.error("bcn is already running")
|
|
339
342
|
if os.name != "nt" and await asyncio.to_thread(endpoint_path.exists):
|
|
340
343
|
parser.error(f"bcn endpoint already exists: {endpoint_path}")
|
|
@@ -63,7 +63,7 @@ def build_initialize_params(
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
def build_fs_watch_params(path:
|
|
66
|
+
def build_fs_watch_params(path: object, watch_id: str) -> dict[str, object]:
|
|
67
67
|
if not isinstance(path, Path):
|
|
68
68
|
raise TypeError("path must be a Path")
|
|
69
69
|
if not path.is_absolute():
|
|
@@ -78,7 +78,7 @@ def build_thread_start_params(
|
|
|
78
78
|
model: str | None = None,
|
|
79
79
|
approval_policy: str | None = None,
|
|
80
80
|
cwd: Path | None = None,
|
|
81
|
-
ephemeral:
|
|
81
|
+
ephemeral: object = None,
|
|
82
82
|
) -> dict[str, object]:
|
|
83
83
|
"""Build provider-local parameters for a Codex ``thread/start`` request."""
|
|
84
84
|
|
|
@@ -509,7 +509,7 @@ def _optional_text(value: object, field_name: str) -> str | None:
|
|
|
509
509
|
return value
|
|
510
510
|
|
|
511
511
|
|
|
512
|
-
def _path_text(value:
|
|
512
|
+
def _path_text(value: object, field_name: str) -> str:
|
|
513
513
|
if not isinstance(value, Path):
|
|
514
514
|
raise TypeError(f"{field_name} must be a Path")
|
|
515
515
|
return str(value)
|
|
@@ -253,8 +253,8 @@ class JsonlProcessSupervisor:
|
|
|
253
253
|
self,
|
|
254
254
|
request_id: JsonlRequestId,
|
|
255
255
|
*,
|
|
256
|
-
result:
|
|
257
|
-
error:
|
|
256
|
+
result: object = None,
|
|
257
|
+
error: object = None,
|
|
258
258
|
timeout: float,
|
|
259
259
|
) -> None:
|
|
260
260
|
"""Write one response to a provider-initiated JSON-RPC request."""
|
|
@@ -76,7 +76,7 @@ class AppServerProtocolError(ValueError):
|
|
|
76
76
|
"""The Codex App Server response does not match its provider contract."""
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def validate_message(payload:
|
|
79
|
+
def validate_message(payload: object) -> JsonlMessage:
|
|
80
80
|
"""Copy a mapping into the transport's provider-local message shape."""
|
|
81
81
|
|
|
82
82
|
if not isinstance(payload, Mapping):
|
|
@@ -120,7 +120,7 @@ class Runtime(IRuntime, IAsyncLifecycle):
|
|
|
120
120
|
self._stopping = True
|
|
121
121
|
connections = tuple(self._connections.items())
|
|
122
122
|
self._connections.clear()
|
|
123
|
-
for
|
|
123
|
+
for _, connection in connections:
|
|
124
124
|
try:
|
|
125
125
|
await connection.supervisor.stop(timeout=timeout)
|
|
126
126
|
except asyncio.CancelledError:
|
|
@@ -22,7 +22,7 @@ from ...core.models import (
|
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def
|
|
25
|
+
def channel_session_from_row(row: aiosqlite.Row) -> ChannelSession:
|
|
26
26
|
following = row["following"]
|
|
27
27
|
if (
|
|
28
28
|
isinstance(following, bool)
|
|
@@ -54,7 +54,7 @@ def _channel_session_from_row(row: aiosqlite.Row) -> ChannelSession:
|
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def
|
|
57
|
+
def bcn_session_from_row(row: aiosqlite.Row) -> BcnSession:
|
|
58
58
|
return BcnSession(
|
|
59
59
|
id=_required_text(row["id"], "id"),
|
|
60
60
|
channel_session_id=_required_text(
|
|
@@ -70,7 +70,7 @@ def _bcn_session_from_row(row: aiosqlite.Row) -> BcnSession:
|
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def
|
|
73
|
+
def runtime_attempt_from_row(row: aiosqlite.Row) -> RuntimeAttempt:
|
|
74
74
|
return RuntimeAttempt(
|
|
75
75
|
turn_id=_required_text(row["turn_id"], "turn_id"),
|
|
76
76
|
session_id=_required_text(row["session_id"], "session_id"),
|
|
@@ -81,7 +81,7 @@ def _runtime_attempt_from_row(row: aiosqlite.Row) -> RuntimeAttempt:
|
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
def
|
|
84
|
+
def inbound_message_from_row(
|
|
85
85
|
row: aiosqlite.Row,
|
|
86
86
|
attachments: tuple[InboundAttachment, ...] = (),
|
|
87
87
|
) -> InboundMessage:
|
|
@@ -133,7 +133,7 @@ def _inbound_message_from_row(
|
|
|
133
133
|
)
|
|
134
134
|
|
|
135
135
|
|
|
136
|
-
def
|
|
136
|
+
def inbound_attachment_from_row(row: aiosqlite.Row) -> InboundAttachment:
|
|
137
137
|
return InboundAttachment(
|
|
138
138
|
attachment_id=_required_text(row["attachment_id"], "attachment_id"),
|
|
139
139
|
name=_required_text(row["name"], "attachment.name"),
|
|
@@ -148,7 +148,7 @@ def _inbound_attachment_from_row(row: aiosqlite.Row) -> InboundAttachment:
|
|
|
148
148
|
)
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
def
|
|
151
|
+
def outbound_message_from_row(row: aiosqlite.Row) -> OutboundMessage:
|
|
152
152
|
raw_attachments = row["attachments_json"]
|
|
153
153
|
if not isinstance(raw_attachments, str):
|
|
154
154
|
raise TypeError("attachments_json must be text")
|
|
@@ -231,7 +231,7 @@ def _outbound_message_from_row(row: aiosqlite.Row) -> OutboundMessage:
|
|
|
231
231
|
)
|
|
232
232
|
|
|
233
233
|
|
|
234
|
-
def
|
|
234
|
+
def consumer_cursor_from_row(row: aiosqlite.Row) -> ConsumerCursor:
|
|
235
235
|
return ConsumerCursor(
|
|
236
236
|
session_id=_required_text(row["session_id"], "session_id"),
|
|
237
237
|
delivered_through_seq=_required_non_negative_int(
|
|
@@ -258,12 +258,12 @@ def _consumer_cursor_from_row(row: aiosqlite.Row) -> ConsumerCursor:
|
|
|
258
258
|
)
|
|
259
259
|
|
|
260
260
|
|
|
261
|
-
def
|
|
261
|
+
def validate_inbound_message_input(message: object) -> None:
|
|
262
262
|
if not isinstance(message, InboundMessage):
|
|
263
263
|
raise TypeError("message must be an InboundMessage")
|
|
264
264
|
|
|
265
265
|
|
|
266
|
-
def
|
|
266
|
+
def validate_outbound_message_input(message: object) -> None:
|
|
267
267
|
if not isinstance(message, OutboundMessage):
|
|
268
268
|
raise TypeError("message must be an OutboundMessage")
|
|
269
269
|
if not isinstance(message.state, OutboundDeliveryState):
|
|
@@ -369,7 +369,7 @@ def _validate_outbound_message_input(message: OutboundMessage) -> None:
|
|
|
369
369
|
raise ValueError("delivered outbound message requires a provider receipt")
|
|
370
370
|
|
|
371
371
|
|
|
372
|
-
def
|
|
372
|
+
def validate_outbound_insert(message: OutboundMessage) -> None:
|
|
373
373
|
if message.state is not OutboundDeliveryState.DRAFT:
|
|
374
374
|
raise ValueError("a new outbound message must start in draft state")
|
|
375
375
|
if message.fresh_check_state is not FreshCheckState.REQUIRED:
|
|
@@ -387,7 +387,7 @@ def _validate_outbound_insert(message: OutboundMessage) -> None:
|
|
|
387
387
|
raise ValueError("a new outbound draft cannot contain delivery timestamps")
|
|
388
388
|
|
|
389
389
|
|
|
390
|
-
def
|
|
390
|
+
def validate_outbound_update(
|
|
391
391
|
existing: OutboundMessage,
|
|
392
392
|
incoming: OutboundMessage,
|
|
393
393
|
) -> OutboundMessage:
|
|
@@ -496,7 +496,7 @@ def _validate_optional_input_text(value: object, field_name: str) -> None:
|
|
|
496
496
|
raise ValueError(f"{field_name} must be a non-empty string when present")
|
|
497
497
|
|
|
498
498
|
|
|
499
|
-
def
|
|
499
|
+
def validate_consumer_cursor_input(cursor: object) -> None:
|
|
500
500
|
if not isinstance(cursor, ConsumerCursor):
|
|
501
501
|
raise TypeError("cursor must be a ConsumerCursor")
|
|
502
502
|
source = cursor.inbox_snapshot_source
|
|
@@ -511,14 +511,14 @@ def _validate_consumer_cursor_input(cursor: ConsumerCursor) -> None:
|
|
|
511
511
|
raise ValueError("inbox snapshot sequence requires a snapshot time")
|
|
512
512
|
|
|
513
513
|
|
|
514
|
-
def
|
|
514
|
+
def validate_cursor_bounds(cursor: ConsumerCursor, latest_seq: int) -> None:
|
|
515
515
|
if cursor.delivered_through_seq > latest_seq:
|
|
516
516
|
raise ValueError("delivered cursor cannot exceed the latest inbound sequence")
|
|
517
517
|
if cursor.inbox_snapshot_seq is not None and cursor.inbox_snapshot_seq > latest_seq:
|
|
518
518
|
raise ValueError("inbox snapshot cannot exceed the latest inbound sequence")
|
|
519
519
|
|
|
520
520
|
|
|
521
|
-
def
|
|
521
|
+
def validate_consumer_cursor_update(
|
|
522
522
|
existing: ConsumerCursor,
|
|
523
523
|
incoming: ConsumerCursor,
|
|
524
524
|
) -> None:
|
|
@@ -553,12 +553,12 @@ def _validate_consumer_cursor_update(
|
|
|
553
553
|
raise ValueError(f"{field_name} cannot move backwards")
|
|
554
554
|
|
|
555
555
|
|
|
556
|
-
def
|
|
556
|
+
def validate_channel_session_input(session: ChannelSession) -> None:
|
|
557
557
|
if not isinstance(session.following, bool):
|
|
558
558
|
raise TypeError("channel session following must be a boolean")
|
|
559
559
|
|
|
560
560
|
|
|
561
|
-
def
|
|
561
|
+
def validate_channel_session_update(
|
|
562
562
|
existing: ChannelSession,
|
|
563
563
|
incoming: ChannelSession,
|
|
564
564
|
) -> ChannelSession:
|
|
@@ -580,7 +580,7 @@ def _validate_channel_session_update(
|
|
|
580
580
|
)
|
|
581
581
|
|
|
582
582
|
|
|
583
|
-
def
|
|
583
|
+
def validate_bcn_session_update(
|
|
584
584
|
existing: BcnSession,
|
|
585
585
|
incoming: BcnSession,
|
|
586
586
|
) -> BcnSession:
|
|
@@ -608,15 +608,15 @@ def _required_text(value: object, field_name: str) -> str:
|
|
|
608
608
|
return _string_value(value, field_name, allow_empty=False)
|
|
609
609
|
|
|
610
610
|
|
|
611
|
-
def
|
|
611
|
+
def validate_non_empty_text(value: object, field_name: str) -> None:
|
|
612
612
|
_required_text(value, field_name)
|
|
613
613
|
|
|
614
614
|
|
|
615
|
-
def
|
|
615
|
+
def validate_non_negative_int(value: object, field_name: str) -> None:
|
|
616
616
|
_required_non_negative_int(value, field_name)
|
|
617
617
|
|
|
618
618
|
|
|
619
|
-
def
|
|
619
|
+
def validate_positive_int(value: object, field_name: str) -> None:
|
|
620
620
|
_required_positive_int(value, field_name)
|
|
621
621
|
|
|
622
622
|
|
|
@@ -669,7 +669,7 @@ def _optional_non_negative_int(value: object, field_name: str) -> int | None:
|
|
|
669
669
|
return _required_non_negative_int(value, field_name)
|
|
670
670
|
|
|
671
671
|
|
|
672
|
-
def
|
|
672
|
+
def encode_metadata(metadata: object) -> str:
|
|
673
673
|
if not isinstance(metadata, Mapping):
|
|
674
674
|
raise TypeError("metadata must be a mapping")
|
|
675
675
|
if any(not isinstance(key, str) for key in metadata):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import replace
|
|
4
|
+
from typing import cast
|
|
4
5
|
from uuid import uuid7
|
|
5
6
|
|
|
6
7
|
from ...core.models import (
|
|
@@ -14,12 +15,12 @@ from ...core.models import (
|
|
|
14
15
|
)
|
|
15
16
|
from ...core.reminder import canonical_id_reference
|
|
16
17
|
from .codec import (
|
|
17
|
-
_inbound_message_from_row,
|
|
18
18
|
_required_non_negative_int,
|
|
19
19
|
_required_text,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
inbound_message_from_row,
|
|
21
|
+
validate_non_empty_text,
|
|
22
|
+
validate_non_negative_int,
|
|
23
|
+
validate_positive_int,
|
|
23
24
|
)
|
|
24
25
|
from .reminder_codec import reminder_from_row, reminder_occurrence_from_row
|
|
25
26
|
from .repository import SqliteTransaction
|
|
@@ -50,7 +51,7 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
50
51
|
session_id: str,
|
|
51
52
|
message_id_or_prefix: str,
|
|
52
53
|
) -> InboundMessage | None:
|
|
53
|
-
|
|
54
|
+
validate_non_empty_text(session_id, "session_id")
|
|
54
55
|
reference = canonical_id_reference(message_id_or_prefix)
|
|
55
56
|
if len(reference) == 8:
|
|
56
57
|
rows = await self.fetchall(
|
|
@@ -69,16 +70,14 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
69
70
|
)
|
|
70
71
|
if row is None:
|
|
71
72
|
return None
|
|
72
|
-
return
|
|
73
|
-
row, await self._attachments(row["message_id"])
|
|
74
|
-
)
|
|
73
|
+
return inbound_message_from_row(row, await self._attachments(row["message_id"]))
|
|
75
74
|
|
|
76
75
|
async def get_reminder(
|
|
77
76
|
self,
|
|
78
77
|
owner_session_id: str,
|
|
79
78
|
reminder_id_or_prefix: str,
|
|
80
79
|
) -> Reminder | None:
|
|
81
|
-
|
|
80
|
+
validate_non_empty_text(owner_session_id, "owner_session_id")
|
|
82
81
|
reference = canonical_id_reference(reminder_id_or_prefix)
|
|
83
82
|
if len(reference) == 8:
|
|
84
83
|
rows = await self.fetchall(
|
|
@@ -103,7 +102,7 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
103
102
|
owner_session_id: str,
|
|
104
103
|
statuses: frozenset[ReminderState],
|
|
105
104
|
) -> tuple[Reminder, ...]:
|
|
106
|
-
|
|
105
|
+
validate_non_empty_text(owner_session_id, "owner_session_id")
|
|
107
106
|
if not isinstance(statuses, frozenset) or not statuses:
|
|
108
107
|
raise ValueError("statuses must be a non-empty frozenset")
|
|
109
108
|
if not all(isinstance(status, ReminderState) for status in statuses):
|
|
@@ -118,7 +117,7 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
118
117
|
)
|
|
119
118
|
return tuple(reminder_from_row(row) for row in rows)
|
|
120
119
|
|
|
121
|
-
async def save_new_reminder(self, reminder:
|
|
120
|
+
async def save_new_reminder(self, reminder: object) -> Reminder:
|
|
122
121
|
if not isinstance(reminder, Reminder):
|
|
123
122
|
raise TypeError("reminder must be a Reminder")
|
|
124
123
|
if reminder.state is not ReminderState.SCHEDULED:
|
|
@@ -166,9 +165,9 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
166
165
|
async def save_reminder_transition(
|
|
167
166
|
self,
|
|
168
167
|
expected_revision: int,
|
|
169
|
-
reminder:
|
|
168
|
+
reminder: object,
|
|
170
169
|
) -> Reminder:
|
|
171
|
-
|
|
170
|
+
validate_positive_int(expected_revision, "expected_revision")
|
|
172
171
|
if not isinstance(reminder, Reminder):
|
|
173
172
|
raise TypeError("reminder must be a Reminder")
|
|
174
173
|
existing = await self.get_reminder(
|
|
@@ -216,8 +215,8 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
216
215
|
*,
|
|
217
216
|
limit: int,
|
|
218
217
|
) -> tuple[Reminder, ...]:
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
validate_non_negative_int(now_ms, "now_ms")
|
|
219
|
+
validate_positive_int(limit, "limit")
|
|
221
220
|
rows = await self.fetchall(
|
|
222
221
|
f"SELECT {_REMINDER_COLUMNS} FROM reminders "
|
|
223
222
|
"WHERE state = ? AND next_fire_at_ms <= ? "
|
|
@@ -232,9 +231,9 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
232
231
|
owner_session_id: str,
|
|
233
232
|
reminder_id: str,
|
|
234
233
|
) -> OwnedReminder | None:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
validate_non_empty_text(agent_id, "agent_id")
|
|
235
|
+
validate_non_empty_text(owner_session_id, "owner_session_id")
|
|
236
|
+
validate_non_empty_text(reminder_id, "reminder_id")
|
|
238
237
|
bound_agent_id = self._bound_agent_id()
|
|
239
238
|
if bound_agent_id is not None and bound_agent_id != agent_id:
|
|
240
239
|
return None
|
|
@@ -267,8 +266,8 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
267
266
|
*,
|
|
268
267
|
limit: int,
|
|
269
268
|
) -> tuple[OwnedReminder, ...]:
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
validate_non_negative_int(now_ms, "now_ms")
|
|
270
|
+
validate_positive_int(limit, "limit")
|
|
272
271
|
predicates = ["state = ?", "next_fire_at_ms <= ?"]
|
|
273
272
|
parameters: list[object] = [ReminderState.SCHEDULED.value, now_ms]
|
|
274
273
|
bound_agent_id = self._bound_agent_id()
|
|
@@ -286,10 +285,10 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
286
285
|
async def save_owned_fired_occurrence(
|
|
287
286
|
self,
|
|
288
287
|
expected_revision: int,
|
|
289
|
-
reminder:
|
|
290
|
-
occurrence:
|
|
288
|
+
reminder: object,
|
|
289
|
+
occurrence: object,
|
|
291
290
|
) -> OwnedReminderOccurrence:
|
|
292
|
-
|
|
291
|
+
validate_positive_int(expected_revision, "expected_revision")
|
|
293
292
|
if not isinstance(reminder, OwnedReminder):
|
|
294
293
|
raise TypeError("reminder must be an OwnedReminder")
|
|
295
294
|
if not isinstance(occurrence, OwnedReminderOccurrence):
|
|
@@ -434,10 +433,10 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
434
433
|
async def save_fired_occurrence(
|
|
435
434
|
self,
|
|
436
435
|
expected_revision: int,
|
|
437
|
-
reminder:
|
|
438
|
-
occurrence:
|
|
436
|
+
reminder: object,
|
|
437
|
+
occurrence: object,
|
|
439
438
|
) -> ReminderOccurrence:
|
|
440
|
-
|
|
439
|
+
validate_positive_int(expected_revision, "expected_revision")
|
|
441
440
|
if not isinstance(reminder, Reminder):
|
|
442
441
|
raise TypeError("reminder must be a Reminder")
|
|
443
442
|
if not isinstance(occurrence, ReminderOccurrence):
|
|
@@ -516,8 +515,8 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
516
515
|
*,
|
|
517
516
|
limit: int,
|
|
518
517
|
) -> tuple[ReminderOccurrence, ...]:
|
|
519
|
-
|
|
520
|
-
|
|
518
|
+
validate_non_empty_text(owner_session_id, "owner_session_id")
|
|
519
|
+
validate_positive_int(limit, "limit")
|
|
521
520
|
rows = await self.fetchall(
|
|
522
521
|
f"SELECT {_OCCURRENCE_COLUMNS} FROM reminder_occurrences "
|
|
523
522
|
"WHERE owner_session_id = ? AND read_at_ms IS NULL "
|
|
@@ -527,7 +526,7 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
527
526
|
return tuple(reminder_occurrence_from_row(row) for row in rows)
|
|
528
527
|
|
|
529
528
|
async def count_pending_reminder_occurrences(self, owner_session_id: str) -> int:
|
|
530
|
-
|
|
529
|
+
validate_non_empty_text(owner_session_id, "owner_session_id")
|
|
531
530
|
row = await self.fetchone(
|
|
532
531
|
"SELECT COUNT(*) AS pending_count FROM reminder_occurrences "
|
|
533
532
|
"WHERE owner_session_id = ? AND read_at_ms IS NULL",
|
|
@@ -540,14 +539,15 @@ class ReminderTransaction(SqliteTransaction):
|
|
|
540
539
|
async def mark_reminder_occurrences_read(
|
|
541
540
|
self,
|
|
542
541
|
owner_session_id: str,
|
|
543
|
-
occurrence_ids:
|
|
542
|
+
occurrence_ids: object,
|
|
544
543
|
*,
|
|
545
544
|
read_at_ms: int,
|
|
546
545
|
) -> tuple[ReminderOccurrence, ...]:
|
|
547
|
-
|
|
548
|
-
|
|
546
|
+
validate_non_empty_text(owner_session_id, "owner_session_id")
|
|
547
|
+
validate_non_negative_int(read_at_ms, "read_at_ms")
|
|
549
548
|
if not isinstance(occurrence_ids, tuple):
|
|
550
549
|
raise TypeError("occurrence_ids must be a tuple")
|
|
550
|
+
occurrence_ids = cast(tuple[str, ...], occurrence_ids)
|
|
551
551
|
if not occurrence_ids:
|
|
552
552
|
return ()
|
|
553
553
|
if len(set(occurrence_ids)) != len(occurrence_ids):
|