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.
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/PKG-INFO +2 -2
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/pyproject.toml +2 -2
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/prompt.py +3 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/session.py +17 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/PKG-INFO +2 -2
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/SOURCES.txt +2 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/requires.txt +1 -1
- optio_opencode-0.2.0/tests/test_agent_sender_opencode.py +29 -0
- optio_opencode-0.2.0/tests/test_resume_sentence_opencode.py +7 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_seed.py +21 -10
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/README.md +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/setup.cfg +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/__init__.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/host_actions.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/seed_manifest.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/snapshots.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode/types.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/dependency_links.txt +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/top_level.txt +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_actions.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_local.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_primitives_local.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_primitives_remote.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_remote_resume.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_host_resume.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_prompt.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_purge_seed.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_sanity.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_seed_config.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_blob_hooks.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_hooks.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_local.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_remote.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_session_resume.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_smart_install.py +0 -0
- {optio_opencode-0.1.9 → optio_opencode-0.2.0}/tests/test_snapshots.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
@@ -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"
|
|
@@ -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
|
|
312
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{optio_opencode-0.1.9 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|