netizen-cli 0.10.0__py3-none-any.whl
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.
- netizen_cli/__init__.py +3 -0
- netizen_cli/__main__.py +4 -0
- netizen_cli/admin/__init__.py +1 -0
- netizen_cli/admin/auth.py +928 -0
- netizen_cli/admin/errors.py +9 -0
- netizen_cli/admin/port_config.py +115 -0
- netizen_cli/admin/presentation.py +257 -0
- netizen_cli/admin/queries.py +337 -0
- netizen_cli/admin/static/admin.css +260 -0
- netizen_cli/admin/static/admin.js +2898 -0
- netizen_cli/admin/static/index.html +327 -0
- netizen_cli/admin/transport.py +935 -0
- netizen_cli/admin/web.py +2717 -0
- netizen_cli/bindings.py +3215 -0
- netizen_cli/builtin_skills.py +93 -0
- netizen_cli/cards/__init__.py +105 -0
- netizen_cli/cards/callbacks.py +565 -0
- netizen_cli/cards/controls.py +2273 -0
- netizen_cli/cards/defaults.py +213 -0
- netizen_cli/cards/model_info.py +80 -0
- netizen_cli/cards/questions.py +220 -0
- netizen_cli/cards/reply.py +2247 -0
- netizen_cli/cards/scheduled.py +836 -0
- netizen_cli/channel/__init__.py +1 -0
- netizen_cli/channel/completion_mentions.py +60 -0
- netizen_cli/channel/input_preparation.py +644 -0
- netizen_cli/channel/messages.py +57 -0
- netizen_cli/channel/ports.py +52 -0
- netizen_cli/channel/question_inputs.py +51 -0
- netizen_cli/channel/reactions.py +293 -0
- netizen_cli/channel/reply_presenter.py +1505 -0
- netizen_cli/channel/topics.py +70 -0
- netizen_cli/channel_app.py +6593 -0
- netizen_cli/cli.py +287 -0
- netizen_cli/cli_data.py +536 -0
- netizen_cli/cli_packages.py +526 -0
- netizen_cli/cli_services.py +651 -0
- netizen_cli/cli_setup.py +242 -0
- netizen_cli/cli_update.py +303 -0
- netizen_cli/cli_update_restore.py +53 -0
- netizen_cli/cli_update_worker.py +333 -0
- netizen_cli/codex_runtime.py +7125 -0
- netizen_cli/completion_mention.py +16 -0
- netizen_cli/database_migrations.py +218 -0
- netizen_cli/defaults/__init__.py +5 -0
- netizen_cli/defaults/models.py +39 -0
- netizen_cli/defaults/service.py +232 -0
- netizen_cli/defaults/store.py +260 -0
- netizen_cli/deployment/__init__.py +1 -0
- netizen_cli/deployment/restart_worker.py +134 -0
- netizen_cli/deployment/update_executor.py +258 -0
- netizen_cli/deployment/update_protocol.py +281 -0
- netizen_cli/domain.py +416 -0
- netizen_cli/error_messages.py +124 -0
- netizen_cli/experience.py +531 -0
- netizen_cli/feishu_app_onboarding.py +187 -0
- netizen_cli/feishu_app_permissions.py +123 -0
- netizen_cli/git_status.py +63 -0
- netizen_cli/image_inputs.py +579 -0
- netizen_cli/instance.py +84 -0
- netizen_cli/lark_app.py +125 -0
- netizen_cli/main.py +903 -0
- netizen_cli/management/__init__.py +83 -0
- netizen_cli/management/blocking_io.py +352 -0
- netizen_cli/management/chat_labels.py +266 -0
- netizen_cli/management/coordination.py +32 -0
- netizen_cli/management/service.py +2187 -0
- netizen_cli/management/updates.py +214 -0
- netizen_cli/markdown_images.py +78 -0
- netizen_cli/message_content.py +786 -0
- netizen_cli/message_history.py +643 -0
- netizen_cli/message_preparation.py +60 -0
- netizen_cli/message_projection.py +923 -0
- netizen_cli/migrations/__init__.py +1 -0
- netizen_cli/migrations/schema.py +103 -0
- netizen_cli/migrations/v14.py +438 -0
- netizen_cli/model_settings.py +269 -0
- netizen_cli/package_resources.py +22 -0
- netizen_cli/projects.py +327 -0
- netizen_cli/prompt_projection.py +327 -0
- netizen_cli/quoted_context.py +312 -0
- netizen_cli/resources/config.example.yaml +35 -0
- netizen_cli/resources/skills/netizen-lark/SKILL.md +64 -0
- netizen_cli/resources/skills/netizen-user-guide/SKILL.md +37 -0
- netizen_cli/resources/skills/netizen-user-guide/references/user-guide.md +842 -0
- netizen_cli/result_images.py +123 -0
- netizen_cli/runtime/__init__.py +1 -0
- netizen_cli/runtime/contracts.py +792 -0
- netizen_cli/runtime/name_writes.py +67 -0
- netizen_cli/runtime/thread_naming.py +451 -0
- netizen_cli/schedules/__init__.py +1 -0
- netizen_cli/schedules/mcp.py +535 -0
- netizen_cli/schedules/models.py +394 -0
- netizen_cli/schedules/scheduler.py +374 -0
- netizen_cli/schedules/service.py +766 -0
- netizen_cli/schedules/store.py +771 -0
- netizen_cli/sdk_gap_adapter.py +1151 -0
- netizen_cli/service_launcher.py +583 -0
- netizen_cli/session_settings.py +126 -0
- netizen_cli/settings.py +216 -0
- netizen_cli/skill_references.py +40 -0
- netizen_cli/terminal_cleanup.py +155 -0
- netizen_cli/turn_activity.py +688 -0
- netizen_cli/turn_files.py +812 -0
- netizen_cli/turn_patch_children.py +254 -0
- netizen_cli/turn_plan_observer.py +315 -0
- netizen_cli/user_questions.py +106 -0
- netizen_cli-0.10.0.dist-info/METADATA +18 -0
- netizen_cli-0.10.0.dist-info/RECORD +112 -0
- netizen_cli-0.10.0.dist-info/WHEEL +5 -0
- netizen_cli-0.10.0.dist-info/entry_points.txt +2 -0
- netizen_cli-0.10.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
"""Runtime protocols, errors and immutable operation inputs and snapshots.
|
|
2
|
+
|
|
3
|
+
These types describe the shared boundary. They do not own execution tasks,
|
|
4
|
+
locks, subscriptions or persistence.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import AsyncIterator, Awaitable, Callable
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Protocol
|
|
14
|
+
|
|
15
|
+
from ..bindings import BindingTaskFeedback, SideTopicState
|
|
16
|
+
from ..domain import ActiveState, GoalOperationState, MessageContextAnchor
|
|
17
|
+
from ..sdk_gap_adapter import GoalSnapshot
|
|
18
|
+
from ..turn_activity import TurnActivityEntrySnapshot, TurnPlanStepSnapshot
|
|
19
|
+
from ..turn_patch_children import TaskPatchChildren
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NativeTurnHandle(Protocol):
|
|
23
|
+
id: str
|
|
24
|
+
thread_id: str
|
|
25
|
+
|
|
26
|
+
async def steer(self, input: Any) -> object: ...
|
|
27
|
+
|
|
28
|
+
async def interrupt(self) -> object: ...
|
|
29
|
+
|
|
30
|
+
async def run(self) -> object: ...
|
|
31
|
+
|
|
32
|
+
def stream(self) -> AsyncIterator[object]: ...
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NativeThread(Protocol):
|
|
36
|
+
id: str
|
|
37
|
+
|
|
38
|
+
async def turn(self, input: Any, **kwargs: object) -> NativeTurnHandle: ...
|
|
39
|
+
|
|
40
|
+
async def read(self, *, include_turns: bool = False) -> object: ...
|
|
41
|
+
|
|
42
|
+
async def compact(self) -> object: ...
|
|
43
|
+
|
|
44
|
+
async def set_name(self, name: str) -> object: ...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class NativeCodex(Protocol):
|
|
48
|
+
async def thread_start(self, **kwargs: object) -> NativeThread: ...
|
|
49
|
+
|
|
50
|
+
async def thread_resume(self, thread_id: str, **kwargs: object) -> NativeThread: ...
|
|
51
|
+
|
|
52
|
+
async def thread_list(self, **kwargs: object) -> object: ...
|
|
53
|
+
|
|
54
|
+
async def thread_archive(self, thread_id: str) -> object: ...
|
|
55
|
+
|
|
56
|
+
async def thread_unarchive(self, thread_id: str) -> NativeThread: ...
|
|
57
|
+
|
|
58
|
+
async def thread_fork(
|
|
59
|
+
self,
|
|
60
|
+
thread_id: str,
|
|
61
|
+
**kwargs: object,
|
|
62
|
+
) -> NativeThread: ...
|
|
63
|
+
|
|
64
|
+
async def models(self, *, include_hidden: bool = False) -> object: ...
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RuntimeClosed(RuntimeError):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ThreadStopping(RuntimeError):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ThreadOccupied(RuntimeError):
|
|
76
|
+
"""The exact Thread resume was rejected because another writer owns it."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ThreadRunningConfiguration(RuntimeError):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ThreadCompacting(RuntimeError):
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ThreadNotMaterialized(RuntimeError):
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class SteerRace(RuntimeError):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TerminalCleanupFailed(RuntimeError):
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TurnInterruptFailed(RuntimeError):
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class TurnStartFailed(RuntimeError):
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class ContextBoundaryCommitFailed(RuntimeError):
|
|
108
|
+
"""The native submission succeeded but its catch-up cursor did not."""
|
|
109
|
+
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ThreadCompactStartFailed(RuntimeError):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class CompactionFailed(RuntimeError):
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class CompactionStateUnknown(RuntimeError):
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class TerminalStateUnknown(RuntimeError):
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class SkillReferenceError(RuntimeError):
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class ThreadGoalActive(RuntimeError):
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class ExternalGoalActive(ThreadGoalActive):
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class GoalNotFound(RuntimeError):
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class GoalNotMaterialized(RuntimeError):
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class GoalStateUnknown(RuntimeError):
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class ThreadLifecycleError(RuntimeError):
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class TurnObservationUnavailable(ThreadLifecycleError):
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class ScheduledInitialStartConflict(ThreadLifecycleError):
|
|
162
|
+
"""An initial scheduled submission can no longer start its exact Binding."""
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class ScheduledTurnReadError(ThreadLifecycleError):
|
|
166
|
+
"""One bounded read could not establish the scheduled exact Turn state."""
|
|
167
|
+
|
|
168
|
+
def __init__(self, code: str, message: str) -> None:
|
|
169
|
+
super().__init__(message)
|
|
170
|
+
self.code = code
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ContextAnchorRequired(ThreadLifecycleError):
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class ThreadLifecycleStateUnknown(ThreadLifecycleError):
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class ThreadArchived(ThreadLifecycleError):
|
|
182
|
+
pass
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class ThreadNotArchived(ThreadLifecycleError):
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class ThreadDeleteUnavailable(ThreadLifecycleError):
|
|
190
|
+
pass
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class ThreadDeleteTargetChanged(ThreadLifecycleError):
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class ThreadActivityChanged(ThreadLifecycleError):
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class ThreadReleaseError(ThreadLifecycleError):
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class ThreadReleaseStateUnknown(ThreadReleaseError):
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class ThreadBackgroundTerminalsActive(ThreadReleaseError):
|
|
210
|
+
pass
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class ThreadCatalogError(RuntimeError):
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class ThreadCatalogDeadlineExceeded(ThreadCatalogError):
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class ThreadCatalogLimitExceeded(ThreadCatalogError):
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class ThreadCatalogIdentityMissing(ThreadCatalogError):
|
|
226
|
+
pass
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class SideUnavailable(RuntimeError):
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class SideSessionNotFound(LookupError):
|
|
234
|
+
pass
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class SideSessionConflict(RuntimeError):
|
|
238
|
+
pass
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class SideSessionClosing(RuntimeError):
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class SideStartFailed(RuntimeError):
|
|
246
|
+
pass
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class SideCloseFailed(RuntimeError):
|
|
250
|
+
pass
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class SubmitDisposition(str, Enum):
|
|
254
|
+
STARTED = "started"
|
|
255
|
+
STEERED = "steered"
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class GoalFinalizationStatus(str, Enum):
|
|
259
|
+
NOT_APPLICABLE = "not-applicable"
|
|
260
|
+
CLEARED = "cleared"
|
|
261
|
+
UNKNOWN = "unknown"
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class ThreadLifecycleState(str, Enum):
|
|
265
|
+
RENAMING = "renaming"
|
|
266
|
+
ARCHIVING = "archiving"
|
|
267
|
+
UNARCHIVING = "unarchiving"
|
|
268
|
+
DELETING = "deleting"
|
|
269
|
+
UNKNOWN = "lifecycle-unknown"
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class StopDisposition(str, Enum):
|
|
273
|
+
NOT_RUNNING = "not-running"
|
|
274
|
+
REQUESTED = "requested"
|
|
275
|
+
STOPPING = "stopping"
|
|
276
|
+
COMPACTING = "compacting"
|
|
277
|
+
GOAL_REQUESTED = "goal-requested"
|
|
278
|
+
GOAL_STOPPING = "goal-stopping"
|
|
279
|
+
EXTERNAL_GOAL = "externally-active-goal"
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class SideSessionState(str, Enum):
|
|
283
|
+
OPEN = "open"
|
|
284
|
+
CLOSING = "closing"
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class ThreadSubscriptionState(str, Enum):
|
|
288
|
+
SUBSCRIBED = "subscribed"
|
|
289
|
+
RELEASE_PENDING = "release-pending"
|
|
290
|
+
RELEASING = "releasing"
|
|
291
|
+
RELEASED = "released"
|
|
292
|
+
RELEASE_UNKNOWN = "release-unknown"
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class ReleaseDisposition(str, Enum):
|
|
296
|
+
NOT_MATERIALIZED = "not-materialized"
|
|
297
|
+
NOT_SUBSCRIBED = "not-subscribed"
|
|
298
|
+
RELEASED = "released"
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@dataclass(frozen=True, slots=True)
|
|
302
|
+
class Submission:
|
|
303
|
+
disposition: SubmitDisposition
|
|
304
|
+
binding_id: str
|
|
305
|
+
thread_id: str
|
|
306
|
+
turn_id: str
|
|
307
|
+
release_receipt_attempt: Callable[[], None] | None = None
|
|
308
|
+
task_feedback: BindingTaskFeedback = BindingTaskFeedback()
|
|
309
|
+
feedback_revision: int = 1
|
|
310
|
+
|
|
311
|
+
def __post_init__(self) -> None:
|
|
312
|
+
if self.feedback_revision < 1:
|
|
313
|
+
raise ValueError("feedback revision must be positive")
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@dataclass(frozen=True, slots=True)
|
|
317
|
+
class CompactSubmission:
|
|
318
|
+
binding_id: str
|
|
319
|
+
thread_id: str
|
|
320
|
+
release_receipt_attempt: Callable[[], None]
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@dataclass(frozen=True, slots=True)
|
|
324
|
+
class GoalSubmission:
|
|
325
|
+
binding_id: str
|
|
326
|
+
thread_id: str
|
|
327
|
+
logical_turn_id: str
|
|
328
|
+
release_receipt_attempt: Callable[[], None]
|
|
329
|
+
task_feedback: BindingTaskFeedback = BindingTaskFeedback()
|
|
330
|
+
feedback_revision: int = 1
|
|
331
|
+
|
|
332
|
+
def __post_init__(self) -> None:
|
|
333
|
+
if self.feedback_revision < 1:
|
|
334
|
+
raise ValueError("feedback revision must be positive")
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@dataclass(frozen=True, slots=True)
|
|
338
|
+
class SideSubmission:
|
|
339
|
+
disposition: SubmitDisposition
|
|
340
|
+
side_id: str
|
|
341
|
+
thread_id: str
|
|
342
|
+
turn_id: str
|
|
343
|
+
release_receipt_attempt: Callable[[], None] | None = None
|
|
344
|
+
task_feedback: BindingTaskFeedback = BindingTaskFeedback()
|
|
345
|
+
feedback_revision: int = 1
|
|
346
|
+
|
|
347
|
+
def __post_init__(self) -> None:
|
|
348
|
+
if self.feedback_revision < 1:
|
|
349
|
+
raise ValueError("feedback revision must be positive")
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@dataclass(frozen=True, slots=True)
|
|
353
|
+
class SideSubmissionAdmission:
|
|
354
|
+
side_id: str
|
|
355
|
+
revision: int
|
|
356
|
+
thread_id: str
|
|
357
|
+
turn_id: str | None
|
|
358
|
+
|
|
359
|
+
def __post_init__(self) -> None:
|
|
360
|
+
if self.revision < 0:
|
|
361
|
+
raise ValueError("Side admission revision must be non-negative")
|
|
362
|
+
if not self.side_id or not self.thread_id:
|
|
363
|
+
raise ValueError("Side admission identity must not be empty")
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@dataclass(frozen=True, slots=True)
|
|
367
|
+
class ActiveTurnSnapshot:
|
|
368
|
+
binding_id: str
|
|
369
|
+
thread_id: str
|
|
370
|
+
turn_id: str
|
|
371
|
+
owner_id: str
|
|
372
|
+
state: ActiveState
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@dataclass(frozen=True, slots=True)
|
|
376
|
+
class TurnProgressSnapshot:
|
|
377
|
+
binding_id: str
|
|
378
|
+
thread_id: str
|
|
379
|
+
turn_id: str
|
|
380
|
+
steer_count: int
|
|
381
|
+
plan_available: bool
|
|
382
|
+
plan_generated: bool
|
|
383
|
+
plan_may_be_stale: bool
|
|
384
|
+
steps: tuple[TurnPlanStepSnapshot, ...]
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
@dataclass(frozen=True, slots=True)
|
|
388
|
+
class TurnActivitySnapshot:
|
|
389
|
+
"""Latest bounded display projection for one exact active Turn.
|
|
390
|
+
|
|
391
|
+
The projection keeps only bounded, allowlisted activity and the latest full
|
|
392
|
+
plan replacement. It is process-local display data, not a Turn history or
|
|
393
|
+
a second terminal-state authority.
|
|
394
|
+
"""
|
|
395
|
+
|
|
396
|
+
binding_id: str
|
|
397
|
+
thread_id: str
|
|
398
|
+
turn_id: str
|
|
399
|
+
revision: int
|
|
400
|
+
state: ActiveState
|
|
401
|
+
steer_count: int
|
|
402
|
+
plan_available: bool
|
|
403
|
+
plan_generated: bool
|
|
404
|
+
plan_may_be_stale: bool
|
|
405
|
+
steps: tuple[TurnPlanStepSnapshot, ...]
|
|
406
|
+
commentary: tuple[TurnActivityEntrySnapshot, ...] = ()
|
|
407
|
+
operations: tuple[TurnActivityEntrySnapshot, ...] = ()
|
|
408
|
+
|
|
409
|
+
def __post_init__(self) -> None:
|
|
410
|
+
if not self.binding_id or not self.thread_id or not self.turn_id:
|
|
411
|
+
raise ValueError("Turn activity identity must not be empty")
|
|
412
|
+
if self.revision < 1:
|
|
413
|
+
raise ValueError("Turn activity revision must be positive")
|
|
414
|
+
if self.steer_count < 0:
|
|
415
|
+
raise ValueError("Turn activity steer count must be non-negative")
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
@dataclass(frozen=True, slots=True)
|
|
419
|
+
class SideTurnActivitySnapshot:
|
|
420
|
+
"""Latest bounded display projection for one exact active Side Turn."""
|
|
421
|
+
|
|
422
|
+
side_id: str
|
|
423
|
+
thread_id: str
|
|
424
|
+
turn_id: str
|
|
425
|
+
revision: int
|
|
426
|
+
state: ActiveState
|
|
427
|
+
steer_count: int
|
|
428
|
+
plan_available: bool
|
|
429
|
+
plan_generated: bool
|
|
430
|
+
plan_may_be_stale: bool
|
|
431
|
+
steps: tuple[TurnPlanStepSnapshot, ...]
|
|
432
|
+
commentary: tuple[TurnActivityEntrySnapshot, ...] = ()
|
|
433
|
+
operations: tuple[TurnActivityEntrySnapshot, ...] = ()
|
|
434
|
+
|
|
435
|
+
def __post_init__(self) -> None:
|
|
436
|
+
if not self.side_id or not self.thread_id or not self.turn_id:
|
|
437
|
+
raise ValueError("Side Turn activity identity must not be empty")
|
|
438
|
+
if self.revision < 1:
|
|
439
|
+
raise ValueError("Side Turn activity revision must be positive")
|
|
440
|
+
if self.steer_count < 0:
|
|
441
|
+
raise ValueError(
|
|
442
|
+
"Side Turn activity steer count must be non-negative"
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
@dataclass(frozen=True, slots=True)
|
|
447
|
+
class GoalActivitySnapshot:
|
|
448
|
+
"""Latest bounded display projection for one exact native Goal run."""
|
|
449
|
+
|
|
450
|
+
binding_id: str
|
|
451
|
+
thread_id: str
|
|
452
|
+
logical_turn_id: str
|
|
453
|
+
physical_turn_id: str | None
|
|
454
|
+
revision: int
|
|
455
|
+
state: GoalOperationState
|
|
456
|
+
plan_available: bool
|
|
457
|
+
plan_generated: bool
|
|
458
|
+
steps: tuple[TurnPlanStepSnapshot, ...]
|
|
459
|
+
commentary: tuple[TurnActivityEntrySnapshot, ...] = ()
|
|
460
|
+
operations: tuple[TurnActivityEntrySnapshot, ...] = ()
|
|
461
|
+
|
|
462
|
+
def __post_init__(self) -> None:
|
|
463
|
+
if not self.binding_id or not self.thread_id or not self.logical_turn_id:
|
|
464
|
+
raise ValueError("Goal activity identity must not be empty")
|
|
465
|
+
if self.revision < 1:
|
|
466
|
+
raise ValueError("Goal activity revision must be positive")
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
@dataclass(frozen=True, slots=True)
|
|
470
|
+
class NativeThreadMetadata:
|
|
471
|
+
thread_id: str
|
|
472
|
+
name: str | None
|
|
473
|
+
preview: str
|
|
474
|
+
updated_at: int | None = None # Native Unix timestamp in seconds; never persisted here.
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
class NativeThreadCatalogState(str, Enum):
|
|
478
|
+
ACTIVE = "active"
|
|
479
|
+
ARCHIVED = "archived"
|
|
480
|
+
MISSING = "missing"
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
@dataclass(frozen=True, slots=True)
|
|
484
|
+
class NativeThreadCatalog:
|
|
485
|
+
archived: bool
|
|
486
|
+
threads: tuple[NativeThreadMetadata, ...]
|
|
487
|
+
|
|
488
|
+
def by_id(self) -> dict[str, NativeThreadMetadata]:
|
|
489
|
+
return {thread.thread_id: thread for thread in self.threads}
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
@dataclass(frozen=True, slots=True)
|
|
493
|
+
class ContextWindowUsage:
|
|
494
|
+
used_tokens: int
|
|
495
|
+
context_window_tokens: int | None
|
|
496
|
+
|
|
497
|
+
def __post_init__(self) -> None:
|
|
498
|
+
if (
|
|
499
|
+
isinstance(self.used_tokens, bool)
|
|
500
|
+
or not isinstance(self.used_tokens, int)
|
|
501
|
+
or self.used_tokens < 0
|
|
502
|
+
):
|
|
503
|
+
raise ValueError("used tokens must be a non-negative integer")
|
|
504
|
+
if self.context_window_tokens is not None and (
|
|
505
|
+
isinstance(self.context_window_tokens, bool)
|
|
506
|
+
or not isinstance(self.context_window_tokens, int)
|
|
507
|
+
or self.context_window_tokens <= 0
|
|
508
|
+
):
|
|
509
|
+
raise ValueError("context window tokens must be a positive integer")
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
@dataclass(frozen=True, slots=True)
|
|
513
|
+
class ActiveGoalSnapshot:
|
|
514
|
+
binding_id: str
|
|
515
|
+
thread_id: str
|
|
516
|
+
logical_turn_id: str | None
|
|
517
|
+
owner_id: str
|
|
518
|
+
state: GoalOperationState
|
|
519
|
+
persisted: GoalSnapshot | None = None
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
@dataclass(frozen=True, slots=True)
|
|
523
|
+
class ThreadLifecycleSnapshot:
|
|
524
|
+
binding_id: str
|
|
525
|
+
thread_id: str | None
|
|
526
|
+
state: ThreadLifecycleState
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
@dataclass(frozen=True, slots=True)
|
|
530
|
+
class ThreadSubscriptionSnapshot:
|
|
531
|
+
binding_id: str
|
|
532
|
+
thread_id: str
|
|
533
|
+
state: ThreadSubscriptionState
|
|
534
|
+
release_in_seconds: float | None
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
@dataclass(frozen=True, slots=True)
|
|
538
|
+
class BindingRuntimeSnapshot:
|
|
539
|
+
"""One immutable, process-local view of an ordinary Binding's activity."""
|
|
540
|
+
|
|
541
|
+
binding_id: str
|
|
542
|
+
activity_revision: int
|
|
543
|
+
turn: ActiveTurnSnapshot | None
|
|
544
|
+
goal: ActiveGoalSnapshot | None
|
|
545
|
+
compacting: bool
|
|
546
|
+
lifecycle: ThreadLifecycleSnapshot | None
|
|
547
|
+
subscription: ThreadSubscriptionSnapshot | None
|
|
548
|
+
context_window_usage: ContextWindowUsage | None
|
|
549
|
+
|
|
550
|
+
def __post_init__(self) -> None:
|
|
551
|
+
if not self.binding_id:
|
|
552
|
+
raise ValueError("Binding runtime snapshot identity must not be empty")
|
|
553
|
+
if self.activity_revision < 0:
|
|
554
|
+
raise ValueError("Binding runtime snapshot revision must be non-negative")
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
@dataclass(frozen=True, slots=True)
|
|
558
|
+
class SideSessionSnapshot:
|
|
559
|
+
side_id: str
|
|
560
|
+
parent_binding_id: str
|
|
561
|
+
parent_thread_id: str
|
|
562
|
+
thread_id: str
|
|
563
|
+
project_alias: str
|
|
564
|
+
cwd: Path
|
|
565
|
+
creator_id: str
|
|
566
|
+
state: SideSessionState
|
|
567
|
+
topic_id: str | None
|
|
568
|
+
root_message_id: str | None
|
|
569
|
+
turn_id: str | None
|
|
570
|
+
turn_state: ActiveState | None
|
|
571
|
+
last_activity: float
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
@dataclass(frozen=True, slots=True)
|
|
575
|
+
class SubmissionAdmission:
|
|
576
|
+
"""Condition captured before asynchronous prompt preparation.
|
|
577
|
+
|
|
578
|
+
The monotonic revision prevents an idle -> running -> idle ABA from
|
|
579
|
+
turning a delayed prompt into a different native submission.
|
|
580
|
+
"""
|
|
581
|
+
|
|
582
|
+
binding_id: str
|
|
583
|
+
revision: int
|
|
584
|
+
thread_id: str | None
|
|
585
|
+
turn_id: str | None
|
|
586
|
+
settings_revision: int
|
|
587
|
+
context_revision: int = 1
|
|
588
|
+
feedback_revision: int = 1
|
|
589
|
+
|
|
590
|
+
def __post_init__(self) -> None:
|
|
591
|
+
if self.revision < 0:
|
|
592
|
+
raise ValueError("submission admission revision must be non-negative")
|
|
593
|
+
if self.settings_revision < 1:
|
|
594
|
+
raise ValueError("settings revision must be positive")
|
|
595
|
+
if self.context_revision < 1:
|
|
596
|
+
raise ValueError("context revision must be positive")
|
|
597
|
+
if self.feedback_revision < 1:
|
|
598
|
+
raise ValueError("feedback revision must be positive")
|
|
599
|
+
if (self.thread_id is None) != (self.turn_id is None):
|
|
600
|
+
raise ValueError(
|
|
601
|
+
"submission admission thread_id and turn_id must both be set or unset"
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
@dataclass(frozen=True, slots=True)
|
|
606
|
+
class ContextCursorCommit:
|
|
607
|
+
"""Catch-up boundary to commit after one exact native submission."""
|
|
608
|
+
|
|
609
|
+
expected_context_revision: int
|
|
610
|
+
anchor: MessageContextAnchor
|
|
611
|
+
|
|
612
|
+
def __post_init__(self) -> None:
|
|
613
|
+
if self.expected_context_revision < 1:
|
|
614
|
+
raise ValueError("expected context revision must be positive")
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
@dataclass(frozen=True, slots=True)
|
|
618
|
+
class TurnOutcome:
|
|
619
|
+
binding_id: str
|
|
620
|
+
thread_id: str
|
|
621
|
+
turn_id: str
|
|
622
|
+
owner_id: str
|
|
623
|
+
origin: object
|
|
624
|
+
result: object | None = None
|
|
625
|
+
error: BaseException | None = None
|
|
626
|
+
# True only means the exact backgroundTerminals/clean RPC returned
|
|
627
|
+
# successfully. It does not attest that a foreground tool process exited.
|
|
628
|
+
background_cleanup_requested: bool = False
|
|
629
|
+
# Latest aggregate snapshot observed from the public
|
|
630
|
+
# ``turn/diff/updated`` notification for this exact Turn. It is carried
|
|
631
|
+
# only through completion delivery and is never persisted by Netizen.
|
|
632
|
+
turn_diff: str | None = None
|
|
633
|
+
task_feedback: BindingTaskFeedback = BindingTaskFeedback()
|
|
634
|
+
feedback_revision: int = 1
|
|
635
|
+
activity: TurnActivitySnapshot | None = None
|
|
636
|
+
patch_children: TaskPatchChildren = TaskPatchChildren()
|
|
637
|
+
|
|
638
|
+
def __post_init__(self) -> None:
|
|
639
|
+
if self.feedback_revision < 1:
|
|
640
|
+
raise ValueError("feedback revision must be positive")
|
|
641
|
+
if self.activity is not None and (
|
|
642
|
+
self.activity.binding_id != self.binding_id
|
|
643
|
+
or self.activity.thread_id != self.thread_id
|
|
644
|
+
or self.activity.turn_id != self.turn_id
|
|
645
|
+
):
|
|
646
|
+
raise ValueError("Turn outcome activity belongs to another Turn")
|
|
647
|
+
|
|
648
|
+
@property
|
|
649
|
+
def final_response(self) -> str | None:
|
|
650
|
+
value = getattr(self.result, "final_response", None)
|
|
651
|
+
return value if isinstance(value, str) else None
|
|
652
|
+
|
|
653
|
+
@property
|
|
654
|
+
def status(self) -> str | None:
|
|
655
|
+
status = getattr(self.result, "status", None)
|
|
656
|
+
value = getattr(status, "value", status)
|
|
657
|
+
return value if isinstance(value, str) else None
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
@dataclass(frozen=True, slots=True)
|
|
661
|
+
class TurnObservationUnavailableOutcome:
|
|
662
|
+
"""One non-terminal notice that bounded exact Turn observation failed."""
|
|
663
|
+
|
|
664
|
+
binding_id: str
|
|
665
|
+
thread_id: str
|
|
666
|
+
turn_id: str
|
|
667
|
+
owner_id: str
|
|
668
|
+
origin: object
|
|
669
|
+
error: TerminalStateUnknown
|
|
670
|
+
state: ActiveState = ActiveState.OBSERVATION_UNAVAILABLE
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
@dataclass(frozen=True, slots=True)
|
|
674
|
+
class ThreadActivityDiscardedOutcome:
|
|
675
|
+
"""Presentation-only notice after native Thread removal is committed."""
|
|
676
|
+
|
|
677
|
+
binding_id: str
|
|
678
|
+
thread_id: str
|
|
679
|
+
turn_id: str | None
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
@dataclass(frozen=True, slots=True)
|
|
683
|
+
class CompactionOutcome:
|
|
684
|
+
binding_id: str
|
|
685
|
+
thread_id: str
|
|
686
|
+
owner_id: str
|
|
687
|
+
origin: object
|
|
688
|
+
compact_turn_id: str | None = None
|
|
689
|
+
status: str | None = None
|
|
690
|
+
error: BaseException | None = None
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
@dataclass(frozen=True, slots=True)
|
|
694
|
+
class GoalOutcome:
|
|
695
|
+
binding_id: str
|
|
696
|
+
thread_id: str
|
|
697
|
+
logical_turn_id: str | None
|
|
698
|
+
owner_id: str
|
|
699
|
+
origin: object
|
|
700
|
+
goal: GoalSnapshot | None = None
|
|
701
|
+
final_physical_turn_id: str | None = None
|
|
702
|
+
final_turn_status: str | None = None
|
|
703
|
+
final_items: tuple[object, ...] = ()
|
|
704
|
+
final_response: str | None = None
|
|
705
|
+
# Latest aggregate diff captured for the exact final physical Turn by the
|
|
706
|
+
# existing Goal notification consumer. It is never reconstructed from
|
|
707
|
+
# persisted Turn history or stored by Netizen.
|
|
708
|
+
turn_diff: str | None = None
|
|
709
|
+
error: BaseException | None = None
|
|
710
|
+
background_cleanup_requested: bool = False
|
|
711
|
+
task_feedback: BindingTaskFeedback = BindingTaskFeedback()
|
|
712
|
+
feedback_revision: int = 1
|
|
713
|
+
activity: GoalActivitySnapshot | None = None
|
|
714
|
+
finalization: GoalFinalizationStatus = GoalFinalizationStatus.NOT_APPLICABLE
|
|
715
|
+
finalization_error: BaseException | None = None
|
|
716
|
+
patch_children: TaskPatchChildren = TaskPatchChildren()
|
|
717
|
+
|
|
718
|
+
def __post_init__(self) -> None:
|
|
719
|
+
if self.feedback_revision < 1:
|
|
720
|
+
raise ValueError("feedback revision must be positive")
|
|
721
|
+
if self.activity is not None and (
|
|
722
|
+
self.activity.binding_id != self.binding_id
|
|
723
|
+
or self.activity.thread_id != self.thread_id
|
|
724
|
+
or self.activity.logical_turn_id != self.logical_turn_id
|
|
725
|
+
):
|
|
726
|
+
raise ValueError("Goal outcome activity belongs to another Goal run")
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
@dataclass(frozen=True, slots=True)
|
|
730
|
+
class SideTurnOutcome:
|
|
731
|
+
side_id: str
|
|
732
|
+
parent_binding_id: str
|
|
733
|
+
thread_id: str
|
|
734
|
+
turn_id: str
|
|
735
|
+
owner_id: str
|
|
736
|
+
origin: object
|
|
737
|
+
cwd: Path
|
|
738
|
+
result: object | None = None
|
|
739
|
+
error: BaseException | None = None
|
|
740
|
+
background_cleanup_requested: bool = False
|
|
741
|
+
# Captures stop/close intent even when the Turn wins the interrupt race.
|
|
742
|
+
# Unlike background_cleanup_requested, this does not attest cleanup success.
|
|
743
|
+
stop_requested: bool = False
|
|
744
|
+
task_feedback: BindingTaskFeedback = BindingTaskFeedback()
|
|
745
|
+
feedback_revision: int = 1
|
|
746
|
+
activity: SideTurnActivitySnapshot | None = None
|
|
747
|
+
patch_children: TaskPatchChildren = TaskPatchChildren()
|
|
748
|
+
|
|
749
|
+
def __post_init__(self) -> None:
|
|
750
|
+
if self.feedback_revision < 1:
|
|
751
|
+
raise ValueError("feedback revision must be positive")
|
|
752
|
+
if self.activity is not None and (
|
|
753
|
+
self.activity.side_id != self.side_id
|
|
754
|
+
or self.activity.thread_id != self.thread_id
|
|
755
|
+
or self.activity.turn_id != self.turn_id
|
|
756
|
+
):
|
|
757
|
+
raise ValueError("Side outcome activity belongs to another Side Turn")
|
|
758
|
+
|
|
759
|
+
@property
|
|
760
|
+
def final_response(self) -> str | None:
|
|
761
|
+
value = getattr(self.result, "final_response", None)
|
|
762
|
+
return value if isinstance(value, str) else None
|
|
763
|
+
|
|
764
|
+
@property
|
|
765
|
+
def status(self) -> str | None:
|
|
766
|
+
status = getattr(self.result, "status", None)
|
|
767
|
+
value = getattr(status, "value", status)
|
|
768
|
+
return value if isinstance(value, str) else None
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
@dataclass(frozen=True, slots=True)
|
|
772
|
+
class SideLifecycleOutcome:
|
|
773
|
+
side_id: str
|
|
774
|
+
state: SideTopicState
|
|
775
|
+
error: BaseException | None = None
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
RuntimeOutcome = (
|
|
779
|
+
TurnOutcome
|
|
780
|
+
| TurnObservationUnavailableOutcome
|
|
781
|
+
| ThreadActivityDiscardedOutcome
|
|
782
|
+
| CompactionOutcome
|
|
783
|
+
| GoalOutcome
|
|
784
|
+
| SideTurnOutcome
|
|
785
|
+
| SideLifecycleOutcome
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
CompletionHandler = Callable[[RuntimeOutcome], Awaitable[None]]
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
StopAcknowledger = Callable[[], Awaitable[None]]
|