omega-coding-agent 0.1.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.
Files changed (67) hide show
  1. omega_agent/__init__.py +19 -0
  2. omega_agent/agent_events.py +166 -0
  3. omega_agent/cancellation.py +52 -0
  4. omega_agent/events.py +157 -0
  5. omega_agent/harness.py +497 -0
  6. omega_agent/hooks.py +112 -0
  7. omega_agent/loop.py +190 -0
  8. omega_agent/provider.py +56 -0
  9. omega_agent/session/__init__.py +34 -0
  10. omega_agent/session/entries.py +48 -0
  11. omega_agent/session/jsonl.py +99 -0
  12. omega_agent/session/store.py +310 -0
  13. omega_agent/session/tree.py +101 -0
  14. omega_agent/tool_runner.py +116 -0
  15. omega_agent/tools.py +97 -0
  16. omega_agent/types.py +222 -0
  17. omega_ai/__init__.py +14 -0
  18. omega_ai/anthropic.py +709 -0
  19. omega_ai/fake.py +184 -0
  20. omega_ai/openai.py +580 -0
  21. omega_ai/openai_codex.py +720 -0
  22. omega_ai/provider.py +22 -0
  23. omega_ai/retry.py +146 -0
  24. omega_coding/__init__.py +18 -0
  25. omega_coding/approval.py +374 -0
  26. omega_coding/auth.py +384 -0
  27. omega_coding/builtin_tools.py +812 -0
  28. omega_coding/cli.py +1001 -0
  29. omega_coding/clipboard.py +224 -0
  30. omega_coding/commands.py +904 -0
  31. omega_coding/compact.py +377 -0
  32. omega_coding/context.py +187 -0
  33. omega_coding/cost.py +123 -0
  34. omega_coding/env.py +86 -0
  35. omega_coding/evals.py +137 -0
  36. omega_coding/eventlog.py +160 -0
  37. omega_coding/file_lock.py +57 -0
  38. omega_coding/headless.py +125 -0
  39. omega_coding/history.py +42 -0
  40. omega_coding/models.py +560 -0
  41. omega_coding/oauth.py +519 -0
  42. omega_coding/paths.py +159 -0
  43. omega_coding/redact.py +194 -0
  44. omega_coding/status.py +316 -0
  45. omega_coding/subagent.py +185 -0
  46. omega_coding/system_prompt.py +125 -0
  47. omega_coding/truncate.py +175 -0
  48. omega_coding/tui/__init__.py +13 -0
  49. omega_coding/tui/adapter.py +130 -0
  50. omega_coding/tui/app.py +1111 -0
  51. omega_coding/tui/approval.py +146 -0
  52. omega_coding/tui/banner.py +272 -0
  53. omega_coding/tui/config.py +81 -0
  54. omega_coding/tui/login.py +161 -0
  55. omega_coding/tui/state.py +299 -0
  56. omega_coding/tui/themes/__init__.py +267 -0
  57. omega_coding/tui/themes/high-contrast.json +33 -0
  58. omega_coding/tui/themes/oxblood-dark.json +33 -0
  59. omega_coding/tui/themes/oxblood-light.json +33 -0
  60. omega_coding/tui/themes/slate.json +33 -0
  61. omega_coding/tui/widgets.py +766 -0
  62. omega_coding/version.py +42 -0
  63. omega_coding_agent-0.1.0.dist-info/METADATA +447 -0
  64. omega_coding_agent-0.1.0.dist-info/RECORD +67 -0
  65. omega_coding_agent-0.1.0.dist-info/WHEEL +4 -0
  66. omega_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
  67. omega_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,19 @@
1
+ """omega_agent — Layer 2, the portable core.
2
+
3
+ The loop, the harness, the two event vocabularies, the message model, and the
4
+ provider *contract*. What it knows about: messages, events, tools, turns.
5
+
6
+ What it does not know about, and must never learn: **files, shells, terminals,
7
+ and vendors.** Those are `omega_coding` and `omega_ai` respectively, and both of
8
+ them import this package rather than the other way round.
9
+
10
+ `provider.py` living here is the point of the whole arrangement. The consumer
11
+ defines the interface and adapters conform to it — reverse that and Anthropic's
12
+ shape becomes the shape of the system. `omega_ai/provider.py` is a re-export, so
13
+ adapters can import from their own package without owning the contract, which is
14
+ exactly what Tau does.
15
+
16
+ `tests/test_layers.py` enforces all of this rather than trusting it.
17
+ """
18
+
19
+ __version__ = "0.2.0"
@@ -0,0 +1,166 @@
1
+ """The ten agent events — Layer 2's vocabulary.
2
+
3
+ Tier 1 had one event vocabulary and the loop handed it straight upward. That was
4
+ honest for a tier whose only consumer was a `print` statement, but it has a cost
5
+ the CLI made visible: **tool results had no event at all.** `cli.py` reported them
6
+ by watching the transcript list grow, after the fact.
7
+
8
+ So there are two vocabularies, and the split is the point:
9
+
10
+ | | the **12** (`events.py`) | the **10** (this file) |
11
+ |---|---|---|
12
+ | about | one model response arriving | the whole run's progress |
13
+ | granularity | one token | "a tool started" |
14
+ | audience | Layer 2 | Layer 4 — a UI |
15
+
16
+ They nest four deep: **agent ▸ turn ▸ message ▸ tool execution.** A renderer can
17
+ indent on that structure without tracking any state of its own.
18
+
19
+ Nothing is lost by moving up a level. Every one of the twelve is carried by
20
+ exactly one of these ten, on the `stream_event` field of the three `message_*`
21
+ events — so a consumer that wants token deltas still gets them, and a consumer
22
+ that only wants "what is happening" can ignore them.
23
+
24
+ Pi and Tau converged on these ten names, as they did on the twelve. Copied
25
+ verbatim rather than improved.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ from typing import Annotated, Any, Literal
31
+
32
+ from pydantic import Field
33
+
34
+ from omega_agent.events import AssistantMessageEvent
35
+ from omega_agent.types import AssistantMessage, ToolCall, ToolResultMessage, WireModel
36
+
37
+ #: The four nesting levels, outermost first. Exposed so a renderer can derive
38
+ #: indentation from the vocabulary instead of hardcoding it.
39
+ AGENT_EVENT_NESTING = ("agent", "turn", "message", "tool_execution")
40
+
41
+ #: Why a run ended. `stop` is the only success: the model stopped asking for
42
+ #: tools. The other three are all "it did not finish", distinguished because a
43
+ #: UI should say which.
44
+ AgentEndReason = Literal["stop", "max_turns", "error", "aborted"]
45
+
46
+
47
+ # ----------------------------------------------------------------- agent scope
48
+
49
+ class AgentStartEvent(WireModel):
50
+ """The run begins. Carries nothing — the loop's inputs are the caller's."""
51
+
52
+ type: Literal["agent_start"] = "agent_start"
53
+
54
+
55
+ class AgentEndEvent(WireModel):
56
+ """The run is over, for any reason.
57
+
58
+ Exactly one of these is emitted per run, including on failure. Tier 1
59
+ signalled failure by yielding a bare provider `error` event, which meant a
60
+ consumer had to distinguish "this turn failed" from "the run is over".
61
+ """
62
+
63
+ type: Literal["agent_end"] = "agent_end"
64
+ reason: AgentEndReason
65
+ error_message: str | None = None
66
+
67
+
68
+ # ------------------------------------------------------------------ turn scope
69
+
70
+ class TurnStartEvent(WireModel):
71
+ type: Literal["turn_start"] = "turn_start"
72
+ turn: int
73
+
74
+
75
+ class TurnEndEvent(WireModel):
76
+ type: Literal["turn_end"] = "turn_end"
77
+ turn: int
78
+
79
+
80
+ # --------------------------------------------------------------- message scope
81
+
82
+ # All three carry `stream_event`, the underlying one of the twelve. That is what
83
+ # keeps the coarser vocabulary from being a downgrade: a renderer wanting text
84
+ # deltas reads `stream_event`, one wanting progress ignores it.
85
+
86
+ class MessageStartEvent(WireModel):
87
+ type: Literal["message_start"] = "message_start"
88
+ message: AssistantMessage
89
+ stream_event: AssistantMessageEvent
90
+
91
+
92
+ class MessageUpdateEvent(WireModel):
93
+ type: Literal["message_update"] = "message_update"
94
+ message: AssistantMessage
95
+ stream_event: AssistantMessageEvent
96
+
97
+
98
+ class MessageEndEvent(WireModel):
99
+ """The response is complete — or failed, which is also complete.
100
+
101
+ `message` is the final assistant message either way. A stream that produced
102
+ 500 tokens and then died reports both, as in Tier 1.
103
+ """
104
+
105
+ type: Literal["message_end"] = "message_end"
106
+ message: AssistantMessage
107
+ stream_event: AssistantMessageEvent
108
+
109
+
110
+ # -------------------------------------------------------- tool execution scope
111
+
112
+ class ToolExecutionStartEvent(WireModel):
113
+ """A tool is about to run. **The event Tier 1 could not emit.**"""
114
+
115
+ type: Literal["tool_execution_start"] = "tool_execution_start"
116
+ tool_call: ToolCall
117
+
118
+
119
+ class ToolExecutionUpdateEvent(WireModel):
120
+ """Progress from a tool that is still running.
121
+
122
+ Nothing emits this in Tier 2 — no built-in tool reports progress yet. It is
123
+ declared because the vocabulary is ten, not nine, and because a long
124
+ `run_shell` streaming its output is the obvious next use. Same reasoning as
125
+ Tier 1's `CancellationToken`: the seam ships before the thing that fills it.
126
+ """
127
+
128
+ type: Literal["tool_execution_update"] = "tool_execution_update"
129
+ tool_call_id: str
130
+ details: dict[str, Any] | None = None
131
+
132
+
133
+ class ToolExecutionEndEvent(WireModel):
134
+ type: Literal["tool_execution_end"] = "tool_execution_end"
135
+ tool_call: ToolCall
136
+ result: ToolResultMessage
137
+
138
+
139
+ AgentEvent = Annotated[
140
+ AgentStartEvent
141
+ | AgentEndEvent
142
+ | TurnStartEvent
143
+ | TurnEndEvent
144
+ | MessageStartEvent
145
+ | MessageUpdateEvent
146
+ | MessageEndEvent
147
+ | ToolExecutionStartEvent
148
+ | ToolExecutionUpdateEvent
149
+ | ToolExecutionEndEvent,
150
+ Field(discriminator="type"),
151
+ ]
152
+
153
+ AGENT_EVENT_TYPES = frozenset(
154
+ {
155
+ "agent_start",
156
+ "agent_end",
157
+ "turn_start",
158
+ "turn_end",
159
+ "message_start",
160
+ "message_update",
161
+ "message_end",
162
+ "tool_execution_start",
163
+ "tool_execution_update",
164
+ "tool_execution_end",
165
+ }
166
+ )
@@ -0,0 +1,52 @@
1
+ """A cancellation token you can actually set.
2
+
3
+ `types.py` defines `CancellationToken` as a Protocol — one method, "should I
4
+ stop?" — and Tier 1 threaded it through the provider interface, the loop, and
5
+ every tool executor without ever creating one. The seam was complete and inert.
6
+ This is the twenty lines that fill it.
7
+
8
+ Kept separate from `harness.py` on purpose. The harness *owns* an instance, but
9
+ the token itself has no dependencies and no knowledge of a conversation, which
10
+ makes it trivially testable and reusable by anything that needs to be
11
+ interruptible.
12
+
13
+ **It latches.** Once cancelled it stays cancelled until `reset()`, so a check
14
+ that happens slightly late still sees the cancellation. The alternative — a
15
+ signal that clears itself when read — loses interrupts that arrive between two
16
+ checks, which is the worst kind of bug to debug.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+
22
+ class CancelSignal:
23
+ """Set by whoever handles the interrupt; read by whoever can act on it.
24
+
25
+ Deliberately not `asyncio.Event`. An Event's natural use is `await
26
+ event.wait()`, which invites code that blocks until cancellation. What is
27
+ wanted here is the opposite: a cheap, synchronous "am I still wanted?" that
28
+ a tool can poll inside a loop, and that a signal handler can set without
29
+ needing an event loop to hand.
30
+ """
31
+
32
+ __slots__ = ("_cancelled",)
33
+
34
+ def __init__(self) -> None:
35
+ self._cancelled = False
36
+
37
+ def is_cancelled(self) -> bool:
38
+ """The whole `CancellationToken` protocol."""
39
+ return self._cancelled
40
+
41
+ def cancel(self) -> None:
42
+ """Request a stop. Idempotent, and safe to call from a signal handler."""
43
+ self._cancelled = True
44
+
45
+ def reset(self) -> None:
46
+ """Clear the request, ready for the next turn.
47
+
48
+ The harness calls this before every run. Without it the first Ctrl-C
49
+ would cancel every later turn too — an agent that appears to break
50
+ permanently the first time you interrupt it.
51
+ """
52
+ self._cancelled = False
omega_agent/events.py ADDED
@@ -0,0 +1,157 @@
1
+ """The twelve stream events — Layer 1's vocabulary.
2
+
3
+ This file is the contract between the provider layer and everything above it.
4
+ Pi and Tau, written in different languages by different people, converged on
5
+ exactly these twelve names and these exact `type` strings. That convergence is
6
+ the strongest evidence in either codebase that the vocabulary *is* the
7
+ architecture, so the names are copied verbatim rather than improved.
8
+
9
+ Deliberately a separate module from `types.py`. Tier 2 introduces a second,
10
+ coarser vocabulary — the ten *agent* events — which will live in its own file
11
+ beside this one. Keeping them apart means you can always tell at a glance which
12
+ layer an event belongs to.
13
+
14
+ Shape of a well-formed stream:
15
+
16
+ start
17
+ text_start → text_delta* → text_end
18
+ thinking_start → thinking_delta* → thinking_end
19
+ toolcall_start → toolcall_delta* → toolcall_end
20
+ done | error
21
+
22
+ Two invariants the provider layer owes upward, enforced in the adapter:
23
+
24
+ * exactly one `start`
25
+ * exactly one terminal event — `done` or `error` — even if the vendor
26
+ stream dies silently
27
+
28
+ Buying those here, once, means the loop never needs a timeout, a sentinel, or a
29
+ "what if the stream just stops" branch.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ from typing import Annotated, Literal
35
+
36
+ from pydantic import Field
37
+
38
+ from omega_agent.types import AssistantMessage, ToolCall, WireModel
39
+
40
+ # Every streaming event carries `partial`: the whole message as it exists so far.
41
+ # It costs a copy per event and buys stateless consumers — a renderer can ignore
42
+ # the delta entirely and just redraw `partial`.
43
+
44
+
45
+ class AssistantStartEvent(WireModel):
46
+ type: Literal["start"] = "start"
47
+ partial: AssistantMessage
48
+
49
+
50
+ # ------------------------------------------------------------------------ text
51
+
52
+ class TextStartEvent(WireModel):
53
+ type: Literal["text_start"] = "text_start"
54
+ content_index: int
55
+ partial: AssistantMessage
56
+
57
+
58
+ class TextDeltaEvent(WireModel):
59
+ type: Literal["text_delta"] = "text_delta"
60
+ content_index: int
61
+ delta: str
62
+ partial: AssistantMessage
63
+
64
+
65
+ class TextEndEvent(WireModel):
66
+ type: Literal["text_end"] = "text_end"
67
+ content_index: int
68
+ content: str
69
+ partial: AssistantMessage
70
+
71
+
72
+ # -------------------------------------------------------------------- thinking
73
+
74
+ class ThinkingStartEvent(WireModel):
75
+ type: Literal["thinking_start"] = "thinking_start"
76
+ content_index: int
77
+ partial: AssistantMessage
78
+
79
+
80
+ class ThinkingDeltaEvent(WireModel):
81
+ type: Literal["thinking_delta"] = "thinking_delta"
82
+ content_index: int
83
+ delta: str
84
+ partial: AssistantMessage
85
+
86
+
87
+ class ThinkingEndEvent(WireModel):
88
+ type: Literal["thinking_end"] = "thinking_end"
89
+ content_index: int
90
+ content: str
91
+ partial: AssistantMessage
92
+
93
+
94
+ # ------------------------------------------------------------------- tool calls
95
+
96
+ class ToolCallStartEvent(WireModel):
97
+ type: Literal["toolcall_start"] = "toolcall_start"
98
+ content_index: int
99
+ partial: AssistantMessage
100
+
101
+
102
+ class ToolCallDeltaEvent(WireModel):
103
+ type: Literal["toolcall_delta"] = "toolcall_delta"
104
+ content_index: int
105
+ delta: str
106
+ partial: AssistantMessage
107
+
108
+
109
+ class ToolCallEndEvent(WireModel):
110
+ type: Literal["toolcall_end"] = "toolcall_end"
111
+ content_index: int
112
+ tool_call: ToolCall
113
+ partial: AssistantMessage
114
+
115
+
116
+ # -------------------------------------------------------------------- terminal
117
+
118
+ DoneReason = Literal["stop", "length", "toolUse"]
119
+ ErrorReason = Literal["aborted", "error"]
120
+
121
+
122
+ class AssistantDoneEvent(WireModel):
123
+ type: Literal["done"] = "done"
124
+ reason: DoneReason
125
+ message: AssistantMessage
126
+
127
+
128
+ class AssistantErrorEvent(WireModel):
129
+ """A failure, delivered as data rather than raised.
130
+
131
+ A stream that emitted 500 tokens and *then* failed has two things to report.
132
+ An exception carries only the failure and discards the tokens; this carries
133
+ both, because `error` holds the message accumulated so far.
134
+ """
135
+
136
+ type: Literal["error"] = "error"
137
+ reason: ErrorReason
138
+ error: AssistantMessage
139
+
140
+
141
+ AssistantMessageEvent = Annotated[
142
+ AssistantStartEvent
143
+ | TextStartEvent
144
+ | TextDeltaEvent
145
+ | TextEndEvent
146
+ | ThinkingStartEvent
147
+ | ThinkingDeltaEvent
148
+ | ThinkingEndEvent
149
+ | ToolCallStartEvent
150
+ | ToolCallDeltaEvent
151
+ | ToolCallEndEvent
152
+ | AssistantDoneEvent
153
+ | AssistantErrorEvent,
154
+ Field(discriminator="type"),
155
+ ]
156
+
157
+ TERMINAL_EVENT_TYPES = frozenset({"done", "error"})