optio-opencode 0.2.2__tar.gz → 0.2.4__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 (50) hide show
  1. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/PKG-INFO +15 -3
  2. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/README.md +12 -0
  3. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/pyproject.toml +3 -3
  4. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/prompt.py +3 -3
  5. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/session.py +7 -1
  6. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/types.py +28 -1
  7. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode.egg-info/PKG-INFO +15 -3
  8. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode.egg-info/requires.txt +2 -2
  9. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_conversation_ui_model.py +1 -0
  10. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_session_local.py +37 -0
  11. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/setup.cfg +0 -0
  12. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/__init__.py +0 -0
  13. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/conversation.py +0 -0
  14. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/cred_watcher.py +0 -0
  15. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/host_actions.py +0 -0
  16. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/seed_manifest.py +0 -0
  17. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/snapshots.py +0 -0
  18. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode/verify.py +0 -0
  19. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode.egg-info/SOURCES.txt +0 -0
  20. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode.egg-info/dependency_links.txt +0 -0
  21. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/src/optio_opencode.egg-info/top_level.txt +0 -0
  22. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_agent_sender_opencode.py +0 -0
  23. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_conversation_config.py +0 -0
  24. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_conversation_driver.py +0 -0
  25. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_conversation_session.py +0 -0
  26. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_conversation_ui_session.py +0 -0
  27. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_cred_watcher.py +0 -0
  28. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_file_download.py +0 -0
  29. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_file_upload.py +0 -0
  30. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_host_actions.py +0 -0
  31. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_host_local.py +0 -0
  32. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_host_primitives_local.py +0 -0
  33. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_host_primitives_remote.py +0 -0
  34. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_host_remote_resume.py +0 -0
  35. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_host_resume.py +0 -0
  36. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_prompt.py +0 -0
  37. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_purge_seed.py +0 -0
  38. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_resume_sentence_opencode.py +0 -0
  39. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_sanity.py +0 -0
  40. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_seed_config.py +0 -0
  41. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_session_blob_hooks.py +0 -0
  42. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_session_hooks.py +0 -0
  43. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_session_remote.py +0 -0
  44. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_session_resume.py +0 -0
  45. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_session_seed.py +0 -0
  46. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_session_seed_saveback.py +0 -0
  47. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_smart_install.py +0 -0
  48. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_snapshots.py +0 -0
  49. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_types.py +0 -0
  50. {optio_opencode-0.2.2 → optio_opencode-0.2.4}/tests/test_verify_seed.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optio-opencode
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Run opencode web as an optio task; local subprocess or remote via SSH.
5
5
  Author-email: Kristof Csillag <kristof.csillag@deai-labs.com>
6
6
  License-Expression: Apache-2.0
@@ -20,9 +20,9 @@ Classifier: Topic :: Software Development :: Code Generators
20
20
  Classifier: Framework :: AsyncIO
21
21
  Requires-Python: >=3.11
22
22
  Description-Content-Type: text/markdown
23
- Requires-Dist: optio-core<0.3,>=0.2
23
+ Requires-Dist: optio-core<0.4,>=0.3
24
24
  Requires-Dist: optio-host<0.3,>=0.2
25
- Requires-Dist: optio-agents<0.3,>=0.2
25
+ Requires-Dist: optio-agents<0.4,>=0.3
26
26
  Requires-Dist: asyncssh>=2.14
27
27
  Requires-Dist: aiohttp>=3.9
28
28
  Provides-Extra: dev
@@ -81,6 +81,18 @@ task = create_opencode_task(config)
81
81
 
82
82
  Set `ssh=None` for local subprocess mode.
83
83
 
84
+ ## Messages
85
+
86
+ - `use_client_messages` (bool, default `False`) — enable the `CLIENT_MESSAGE:`
87
+ log keyword: the agent can push `{keyword, data}` messages to the browser
88
+ session that launched the task (surfaced via optio-ui's `onClientMessage`).
89
+ - `on_caller_message` (async callback, default `None`) — enable the
90
+ `CALLER_MESSAGE:` log keyword: the agent can push `{keyword, data}` messages
91
+ to your application. Signature `(hook_ctx, keyword, data) -> str | None`;
92
+ a non-None return is sent back to the agent as feedback. Keywords that are
93
+ not enabled are absent from both the parser and the agent-facing protocol
94
+ documentation.
95
+
84
96
  ## License
85
97
 
86
98
  Apache-2.0.
@@ -50,6 +50,18 @@ task = create_opencode_task(config)
50
50
 
51
51
  Set `ssh=None` for local subprocess mode.
52
52
 
53
+ ## Messages
54
+
55
+ - `use_client_messages` (bool, default `False`) — enable the `CLIENT_MESSAGE:`
56
+ log keyword: the agent can push `{keyword, data}` messages to the browser
57
+ session that launched the task (surfaced via optio-ui's `onClientMessage`).
58
+ - `on_caller_message` (async callback, default `None`) — enable the
59
+ `CALLER_MESSAGE:` log keyword: the agent can push `{keyword, data}` messages
60
+ to your application. Signature `(hook_ctx, keyword, data) -> str | None`;
61
+ a non-None return is sent back to the agent as feedback. Keywords that are
62
+ not enabled are absent from both the parser and the agent-facing protocol
63
+ documentation.
64
+
53
65
  ## License
54
66
 
55
67
  Apache-2.0.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "optio-opencode"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description = "Run opencode web as an optio task; local subprocess or remote via SSH."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -26,11 +26,11 @@ classifiers = [
26
26
  "Framework :: AsyncIO",
27
27
  ]
28
28
  dependencies = [
29
- "optio-core>=0.2,<0.3",
29
+ "optio-core>=0.3,<0.4",
30
30
  # 0.1.1 introduces the bind_addr kwarg on Host.establish_tunnel —
31
31
  # required for OPTIO_WIDGET_TUNNEL_BIND to work.
32
32
  "optio-host>=0.2,<0.3",
33
- "optio-agents>=0.2,<0.3",
33
+ "optio-agents>=0.3,<0.4",
34
34
  "asyncssh>=2.14",
35
35
  "aiohttp>=3.9",
36
36
  ]
@@ -8,10 +8,10 @@ addressed. The consumer's own task description is then appended verbatim.
8
8
 
9
9
 
10
10
  from optio_agents.prompt import downloadables_block
11
- from optio_agents.protocol import build_log_channel_prompt
11
+ from optio_agents.protocol import ProtocolFeatures, build_log_channel_prompt
12
12
 
13
13
 
14
- _OPENCODE_INTRO = """# Coordination protocol with the host (optio-opencode)
14
+ _OPENCODE_INTRO = """# Coordination protocol with the host harness
15
15
 
16
16
  You are running inside a coordination harness. Follow these conventions
17
17
  throughout the session.
@@ -163,7 +163,7 @@ def compose_agents_md(
163
163
  )
164
164
  if host_protocol:
165
165
  if documentation is None:
166
- documentation = build_log_channel_prompt("suppress")
166
+ documentation = build_log_channel_prompt(ProtocolFeatures(browser="suppress"))
167
167
  base_prompt_pre = _OPENCODE_INTRO + documentation
168
168
  else:
169
169
  # Conversation mode without the optio.log channel: no keyword docs,
@@ -84,6 +84,7 @@ def conversation_widget_data(config: "OpencodeTaskConfig", *, session_id: str, d
84
84
  "sessionID": session_id,
85
85
  "directory": directory,
86
86
  "toolVerbosity": config.tool_verbosity,
87
+ "thinkingVerbosity": config.thinking_verbosity,
87
88
  "showModelSelector": config.show_model_selector,
88
89
  "defaultModel": config.default_model,
89
90
  "showFileUpload": config.show_file_upload,
@@ -97,7 +98,11 @@ async def run_opencode_session(ctx: ProcessContext, config: OpencodeTaskConfig)
97
98
  """Execute function body for one optio-opencode task instance."""
98
99
  # --- per-task filesystem layout ---------------------------------------
99
100
  host: Host = _build_host(config, ctx.process_id)
100
- protocol = get_protocol(browser="suppress")
101
+ protocol = get_protocol(
102
+ browser="suppress",
103
+ client_messages=config.use_client_messages,
104
+ caller_messages=config.on_caller_message is not None,
105
+ )
101
106
 
102
107
  instructions = config.consumer_instructions
103
108
  omit_task_framing = False
@@ -502,6 +507,7 @@ async def run_opencode_session(ctx: ProcessContext, config: OpencodeTaskConfig)
502
507
  body=_opencode_body,
503
508
  prepare=_prepare,
504
509
  on_deliverable=config.on_deliverable,
510
+ on_caller_message=config.on_caller_message,
505
511
  after_execute=config.after_execute,
506
512
  protocol=protocol,
507
513
  agent_sender=_agent_sender,
@@ -9,7 +9,11 @@ is owned by ``optio-host``. This module re-exports them so existing
9
9
  from dataclasses import dataclass, field
10
10
  from typing import Any, Awaitable, Callable, Literal
11
11
 
12
- from optio_agents.protocol.session import DeliverableCallback, HookCallback
12
+ from optio_agents.protocol.session import (
13
+ CallerMessageCallback,
14
+ DeliverableCallback,
15
+ HookCallback,
16
+ )
13
17
  from optio_host.types import SSHConfig
14
18
 
15
19
 
@@ -25,15 +29,22 @@ SeedProvider = Callable[[str], Awaitable[str]]
25
29
  ConversationMode = Literal["iframe", "conversation"]
26
30
  ToolVerbosity = Literal["silent", "description-only", "verbose"]
27
31
  _VALID_TOOL_VERBOSITY = {"silent", "description-only", "verbose"}
32
+ # Visibility of reasoning/thinking traces in the conversation widget. Task-level,
33
+ # mirrors ToolVerbosity; the UI never decides. (opencode reasoning is not yet wired
34
+ # to a distinct thinking row, but the option ships for cross-engine parity.)
35
+ ThinkingVerbosity = Literal["hidden", "visible"]
36
+ _VALID_THINKING_VERBOSITY = {"hidden", "visible"}
28
37
 
29
38
 
30
39
  __all__ = [
40
+ "CallerMessageCallback",
31
41
  "DeliverableCallback",
32
42
  "HookCallback",
33
43
  "SSHConfig",
34
44
  "SeedProvider",
35
45
  "ConversationMode",
36
46
  "ToolVerbosity",
47
+ "ThinkingVerbosity",
37
48
  "OpencodeTaskConfig",
38
49
  ]
39
50
 
@@ -52,6 +63,14 @@ class OpencodeTaskConfig:
52
63
  opencode_config: dict[str, Any] = field(default_factory=dict)
53
64
  ssh: SSHConfig | None = None
54
65
  on_deliverable: DeliverableCallback | None = None
66
+ # Enable the CLIENT_MESSAGE keyword: agent-pushed messages routed to the
67
+ # originating browser session's frontend (stored as sessionEvents,
68
+ # surfaced via optio-ui's onClientMessage). Off by default.
69
+ use_client_messages: bool = False
70
+ # Enable the CALLER_MESSAGE keyword: agent-pushed messages routed to this
71
+ # callback in the embedding application. A non-None return value is sent
72
+ # back to the agent as feedback. Off (None) by default.
73
+ on_caller_message: CallerMessageCallback | None = None
55
74
  install_if_missing: bool = True
56
75
  # Override for the optio-owned opencode binary **cache** directory (where
57
76
  # the opencode binary is installed/cached on the worker). ``None``
@@ -113,6 +132,9 @@ class OpencodeTaskConfig:
113
132
  # Rendering hint forwarded to the widget via widgetData; only affects
114
133
  # conversation_ui rendering.
115
134
  tool_verbosity: ToolVerbosity = "description-only"
135
+ # Whether the conversation widget shows the agent's reasoning/thinking traces.
136
+ # Default hidden — thinking is noisy; opt in per task.
137
+ thinking_verbosity: ThinkingVerbosity = "hidden"
116
138
  # Default model for a fresh conversation session, "providerID/modelID".
117
139
  # Forwarded to the widget, which applies it once at the start of a non-
118
140
  # resumed session (history empty) and only if present in the live model
@@ -180,3 +202,8 @@ class OpencodeTaskConfig:
180
202
  f"OpencodeTaskConfig.tool_verbosity={self.tool_verbosity!r} "
181
203
  f"is not one of {sorted(_VALID_TOOL_VERBOSITY)}"
182
204
  )
205
+ if self.thinking_verbosity not in _VALID_THINKING_VERBOSITY:
206
+ raise ValueError(
207
+ f"OpencodeTaskConfig.thinking_verbosity={self.thinking_verbosity!r} "
208
+ f"is not one of {sorted(_VALID_THINKING_VERBOSITY)}"
209
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optio-opencode
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Run opencode web as an optio task; local subprocess or remote via SSH.
5
5
  Author-email: Kristof Csillag <kristof.csillag@deai-labs.com>
6
6
  License-Expression: Apache-2.0
@@ -20,9 +20,9 @@ Classifier: Topic :: Software Development :: Code Generators
20
20
  Classifier: Framework :: AsyncIO
21
21
  Requires-Python: >=3.11
22
22
  Description-Content-Type: text/markdown
23
- Requires-Dist: optio-core<0.3,>=0.2
23
+ Requires-Dist: optio-core<0.4,>=0.3
24
24
  Requires-Dist: optio-host<0.3,>=0.2
25
- Requires-Dist: optio-agents<0.3,>=0.2
25
+ Requires-Dist: optio-agents<0.4,>=0.3
26
26
  Requires-Dist: asyncssh>=2.14
27
27
  Requires-Dist: aiohttp>=3.9
28
28
  Provides-Extra: dev
@@ -81,6 +81,18 @@ task = create_opencode_task(config)
81
81
 
82
82
  Set `ssh=None` for local subprocess mode.
83
83
 
84
+ ## Messages
85
+
86
+ - `use_client_messages` (bool, default `False`) — enable the `CLIENT_MESSAGE:`
87
+ log keyword: the agent can push `{keyword, data}` messages to the browser
88
+ session that launched the task (surfaced via optio-ui's `onClientMessage`).
89
+ - `on_caller_message` (async callback, default `None`) — enable the
90
+ `CALLER_MESSAGE:` log keyword: the agent can push `{keyword, data}` messages
91
+ to your application. Signature `(hook_ctx, keyword, data) -> str | None`;
92
+ a non-None return is sent back to the agent as feedback. Keywords that are
93
+ not enabled are absent from both the parser and the agent-facing protocol
94
+ documentation.
95
+
84
96
  ## License
85
97
 
86
98
  Apache-2.0.
@@ -1,6 +1,6 @@
1
- optio-core<0.3,>=0.2
1
+ optio-core<0.4,>=0.3
2
2
  optio-host<0.3,>=0.2
3
- optio-agents<0.3,>=0.2
3
+ optio-agents<0.4,>=0.3
4
4
  asyncssh>=2.14
5
5
  aiohttp>=3.9
6
6
 
@@ -59,6 +59,7 @@ def test_widget_data_carries_model_fields():
59
59
  "sessionID": "s1",
60
60
  "directory": "/wd",
61
61
  "toolVerbosity": "verbose",
62
+ "thinkingVerbosity": "hidden",
62
63
  "showModelSelector": True,
63
64
  "defaultModel": "opencode/big-pickle",
64
65
  "showFileUpload": False,
@@ -315,6 +315,43 @@ async def test_widget_upstream_and_data_are_set(ctx_and_captures, _supply_scenar
315
315
  assert decoded.startswith("/") and "optio-opencode" in decoded
316
316
 
317
317
 
318
+ async def test_caller_message_reaches_callback(ctx_and_captures, _supply_scenario):
319
+ ctx, _cap, _ = ctx_and_captures
320
+ _supply_scenario["name"] = "caller_message"
321
+
322
+ received: list[tuple[str, object]] = []
323
+ async def on_caller(hook_ctx, keyword, data):
324
+ received.append((keyword, data))
325
+ return "pong"
326
+
327
+ cfg = OpencodeTaskConfig(
328
+ consumer_instructions="(scenario: caller_message)",
329
+ on_caller_message=on_caller,
330
+ )
331
+ await run_opencode_session(ctx, cfg)
332
+
333
+ assert received == [("ping", {"n": 1})]
334
+
335
+
336
+ async def test_client_message_stored_as_session_event(
337
+ ctx_and_captures, _supply_scenario, mongo_db,
338
+ ):
339
+ ctx, _cap, _ = ctx_and_captures
340
+ _supply_scenario["name"] = "client_message"
341
+
342
+ cfg = OpencodeTaskConfig(
343
+ consumer_instructions="(scenario: client_message)",
344
+ use_client_messages=True,
345
+ )
346
+ await run_opencode_session(ctx, cfg)
347
+
348
+ doc = await mongo_db["test_processes"].find_one({"processId": "p"})
349
+ events = [e for e in doc.get("sessionEvents", []) if e["type"] == "client"]
350
+ assert len(events) == 1
351
+ assert events[0]["keyword"] == "notify"
352
+ assert events[0]["data"] == {"msg": "hi"}
353
+
354
+
318
355
  # ---- resume log -----------------------------------------------------------
319
356
 
320
357
 
File without changes