optio-opencode 0.1.9__tar.gz → 0.2.0__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 (37) hide show
  1. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/PKG-INFO +2 -2
  2. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/pyproject.toml +2 -2
  3. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/prompt.py +3 -0
  4. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/session.py +17 -0
  5. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/PKG-INFO +2 -2
  6. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/SOURCES.txt +2 -0
  7. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/requires.txt +1 -1
  8. optio_opencode-0.2.0/tests/test_agent_sender_opencode.py +29 -0
  9. optio_opencode-0.2.0/tests/test_resume_sentence_opencode.py +7 -0
  10. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_seed.py +21 -10
  11. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/README.md +0 -0
  12. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/setup.cfg +0 -0
  13. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/__init__.py +0 -0
  14. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/host_actions.py +0 -0
  15. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/seed_manifest.py +0 -0
  16. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/snapshots.py +0 -0
  17. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/types.py +0 -0
  18. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/dependency_links.txt +0 -0
  19. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/top_level.txt +0 -0
  20. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_actions.py +0 -0
  21. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_local.py +0 -0
  22. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_primitives_local.py +0 -0
  23. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_primitives_remote.py +0 -0
  24. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_remote_resume.py +0 -0
  25. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_resume.py +0 -0
  26. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_prompt.py +0 -0
  27. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_purge_seed.py +0 -0
  28. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_sanity.py +0 -0
  29. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_seed_config.py +0 -0
  30. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_blob_hooks.py +0 -0
  31. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_hooks.py +0 -0
  32. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_local.py +0 -0
  33. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_remote.py +0 -0
  34. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_resume.py +0 -0
  35. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_smart_install.py +0 -0
  36. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_snapshots.py +0 -0
  37. {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optio-opencode
3
- Version: 0.1.9
3
+ Version: 0.2.0
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
@@ -22,7 +22,7 @@ Requires-Python: >=3.11
22
22
  Description-Content-Type: text/markdown
23
23
  Requires-Dist: optio-core<0.3,>=0.2
24
24
  Requires-Dist: optio-host<0.3,>=0.2
25
- Requires-Dist: optio-agents<0.2,>=0.1
25
+ Requires-Dist: optio-agents<0.3,>=0.2
26
26
  Requires-Dist: asyncssh>=2.14
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: pytest>=8.0; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "optio-opencode"
7
- version = "0.1.9"
7
+ version = "0.2.0"
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"
@@ -30,7 +30,7 @@ dependencies = [
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.1,<0.2",
33
+ "optio-agents>=0.2,<0.3",
34
34
  "asyncssh>=2.14",
35
35
  ]
36
36
 
@@ -81,6 +81,9 @@ the situation as a resume:
81
81
 
82
82
  If a resume slips past unnoticed, a failing tool call is the
83
83
  next-best signal — re-check `./resume.log` then.
84
+
85
+ You may also be notified of a resume by a `System:` message on your input
86
+ channel; when you see one, follow the `resume.log` procedure above.
84
87
  """
85
88
 
86
89
 
@@ -31,6 +31,7 @@ from optio_core.context import ProcessContext
31
31
  from optio_core.models import BasicAuth, TaskInstance
32
32
 
33
33
  from optio_agents import HookContext
34
+ from optio_agents import RESUME_NOTICE, SYSTEM_MESSAGE_PREFIX
34
35
  from optio_host.host import Host, LocalHost, ProcessHandle, RemoteHost
35
36
  from optio_host.paths import task_dir
36
37
  from optio_agents.protocol.session import _SessionFailed, run_log_protocol_session
@@ -327,6 +328,13 @@ async def run_opencode_session(ctx: ProcessContext, config: OpencodeTaskConfig)
327
328
  await _post_opencode_prompt(
328
329
  worker_port, password, session_id, AUTO_START_PROMPT,
329
330
  )
331
+ elif resuming and config.supports_resume:
332
+ # Push notification: make the resumed agent NOTICE the resume
333
+ # promptly (resume.log remains the pull-based source of truth).
334
+ await _post_opencode_prompt(
335
+ worker_port, password, session_id,
336
+ f"{SYSTEM_MESSAGE_PREFIX}{RESUME_NOTICE}",
337
+ )
330
338
 
331
339
  # --- await opencode subprocess exit -----------------------------
332
340
  # The protocol driver runs this body alongside the tail dispatcher
@@ -343,6 +351,14 @@ async def run_opencode_session(ctx: ProcessContext, config: OpencodeTaskConfig)
343
351
  # --- run the protocol session -----------------------------------------
344
352
  # host.connect() already happened up-front (before install + resume).
345
353
  session_error: BaseException | None = None
354
+
355
+ async def _agent_sender(message: str) -> None:
356
+ # worker_port / session_id are set by _opencode_body at launch;
357
+ # password is established at function scope. _post_opencode_prompt
358
+ # raises on a non-2xx / unreachable worker, which
359
+ # send_to_agent converts to False.
360
+ await _post_opencode_prompt(worker_port, password, session_id, message)
361
+
346
362
  try:
347
363
  # before_execute is wired manually inside _opencode_body (after
348
364
  # install, before launch) per opencode's documented timing.
@@ -355,6 +371,7 @@ async def run_opencode_session(ctx: ProcessContext, config: OpencodeTaskConfig)
355
371
  on_deliverable=config.on_deliverable,
356
372
  after_execute=config.after_execute,
357
373
  protocol=protocol,
374
+ agent_sender=_agent_sender,
358
375
  )
359
376
  except _SessionFailed as fail:
360
377
  session_error = fail
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optio-opencode
3
- Version: 0.1.9
3
+ Version: 0.2.0
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
@@ -22,7 +22,7 @@ Requires-Python: >=3.11
22
22
  Description-Content-Type: text/markdown
23
23
  Requires-Dist: optio-core<0.3,>=0.2
24
24
  Requires-Dist: optio-host<0.3,>=0.2
25
- Requires-Dist: optio-agents<0.2,>=0.1
25
+ Requires-Dist: optio-agents<0.3,>=0.2
26
26
  Requires-Dist: asyncssh>=2.14
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: pytest>=8.0; extra == "dev"
@@ -12,6 +12,7 @@ src/optio_opencode.egg-info/SOURCES.txt
12
12
  src/optio_opencode.egg-info/dependency_links.txt
13
13
  src/optio_opencode.egg-info/requires.txt
14
14
  src/optio_opencode.egg-info/top_level.txt
15
+ tests/test_agent_sender_opencode.py
15
16
  tests/test_host_actions.py
16
17
  tests/test_host_local.py
17
18
  tests/test_host_primitives_local.py
@@ -20,6 +21,7 @@ tests/test_host_remote_resume.py
20
21
  tests/test_host_resume.py
21
22
  tests/test_prompt.py
22
23
  tests/test_purge_seed.py
24
+ tests/test_resume_sentence_opencode.py
23
25
  tests/test_sanity.py
24
26
  tests/test_seed_config.py
25
27
  tests/test_session_blob_hooks.py
@@ -1,6 +1,6 @@
1
1
  optio-core<0.3,>=0.2
2
2
  optio-host<0.3,>=0.2
3
- optio-agents<0.2,>=0.1
3
+ optio-agents<0.3,>=0.2
4
4
  asyncssh>=2.14
5
5
 
6
6
  [dev]
@@ -0,0 +1,29 @@
1
+ import pytest
2
+
3
+ import optio_opencode.session as S
4
+ from optio_agents import RESUME_NOTICE, SYSTEM_MESSAGE_PREFIX
5
+
6
+
7
+ @pytest.mark.asyncio
8
+ async def test_post_prompt_signature_used_by_sender(monkeypatch):
9
+ """The opencode sender forwards (port, password, session_id, message) to
10
+ _post_opencode_prompt verbatim. Mirrors the closure built in
11
+ run_opencode_session."""
12
+ calls = []
13
+
14
+ async def fake_post(port, password, session_id, message):
15
+ calls.append((port, password, session_id, message))
16
+
17
+ monkeypatch.setattr(S, "_post_opencode_prompt", fake_post)
18
+
19
+ worker_port, password, session_id = 4321, "pw", "sess-1"
20
+
21
+ async def _agent_sender(message: str) -> None:
22
+ await S._post_opencode_prompt(worker_port, password, session_id, message)
23
+
24
+ await _agent_sender("hello")
25
+ assert calls == [(4321, "pw", "sess-1", "hello")]
26
+
27
+
28
+ def test_resume_notice_string():
29
+ assert f"{SYSTEM_MESSAGE_PREFIX}{RESUME_NOTICE}" == "System: you have been resumed"
@@ -0,0 +1,7 @@
1
+ from optio_opencode.prompt import _render_resume_section
2
+
3
+
4
+ def test_resume_section_mentions_system_notification():
5
+ section = _render_resume_section(None)
6
+ assert "`System:` message" in section
7
+ assert "resume.log" in section
@@ -308,9 +308,10 @@ async def test_second_session_consumes_seed(
308
308
  async def test_auto_start_posts_on_fresh_and_not_on_resume(
309
309
  mongo_db, task_root, _supply_scenario, monkeypatch,
310
310
  ):
311
- """auto_start=True POSTs the kickoff prompt on a fresh launch and is
312
- suppressed on resume (the restored session already carries its
313
- conversation)."""
311
+ """auto_start=True POSTs the kickoff prompt on a fresh launch; on resume
312
+ the kickoff is suppressed (the restored session already carries its
313
+ conversation) but a System: resume notice is POSTed so the agent notices
314
+ the resume."""
314
315
  import optio_opencode.session as session_mod
315
316
 
316
317
  _supply_scenario["name"] = "happy"
@@ -335,19 +336,29 @@ async def test_auto_start_posts_on_fresh_and_not_on_resume(
335
336
  # fall back to a fresh launch and POST the kickoff prompt again.
336
337
  before_execute=_plant_env,
337
338
  ))
338
- assert len(posts) == 1
339
- posted_session_id, posted_message = posts[0]
340
- assert posted_message == session_mod.AUTO_START_PROMPT
341
- assert posted_session_id # a real (pre-created) session id
342
-
343
- # resume the same process must NOT POST again
339
+ # The 'happy' scenario also emits a DELIVERABLE, which the mandatory
340
+ # acknowledgment POSTs as a "deliverable ...: accepted" ack via the same
341
+ # _post_opencode_prompt. This test is about the kickoff / resume-notice
342
+ # posts, so filter those out.
343
+ notice = f"{session_mod.SYSTEM_MESSAGE_PREFIX}{session_mod.RESUME_NOTICE}"
344
+ kickoffs = [m for _sid, m in posts if m == session_mod.AUTO_START_PROMPT]
345
+ notices = [m for _sid, m in posts if m == notice]
346
+ assert kickoffs == [session_mod.AUTO_START_PROMPT] # kickoff posted exactly once
347
+ assert notices == [] # no resume notice on fresh
348
+ assert all(sid for sid, _m in posts) # real (pre-created) session ids
349
+
350
+ # resume the same process → the kickoff is suppressed, but a System:
351
+ # resume notice is POSTed so the agent notices the resume.
344
352
  ctx_resume = await _make_ctx(mongo_db, pid, resume=True)
345
353
  await run_opencode_session(ctx_resume, OpencodeTaskConfig(
346
354
  consumer_instructions="(scenario: happy)",
347
355
  auto_start=True,
348
356
  before_execute=_plant_env,
349
357
  ))
350
- assert len(posts) == 1, posts
358
+ kickoffs = [m for _sid, m in posts if m == session_mod.AUTO_START_PROMPT]
359
+ notices = [m for _sid, m in posts if m == notice]
360
+ assert kickoffs == [session_mod.AUTO_START_PROMPT] # no NEW kickoff on resume
361
+ assert notices == [notice] # resume notice posted once
351
362
 
352
363
 
353
364
  def test_post_opencode_prompt_uses_prompt_async_parts_body(monkeypatch):
File without changes
File without changes