optio-opencode 0.1.8__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.8 → optio_opencode-0.2.0}/PKG-INFO +2 -2
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/pyproject.toml +2 -2
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode/prompt.py +3 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode/session.py +35 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/PKG-INFO +2 -2
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/SOURCES.txt +2 -0
- {optio_opencode-0.1.8 → 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.8 → optio_opencode-0.2.0}/tests/test_session_blob_hooks.py +5 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_session_local.py +19 -1
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_session_resume.py +44 -2
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_session_seed.py +26 -10
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/README.md +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/setup.cfg +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode/__init__.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode/host_actions.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode/seed_manifest.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode/snapshots.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode/types.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/dependency_links.txt +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/src/optio_opencode.egg-info/top_level.txt +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_host_actions.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_host_local.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_host_primitives_local.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_host_primitives_remote.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_host_remote_resume.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_host_resume.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_prompt.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_purge_seed.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_sanity.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_seed_config.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_session_hooks.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_session_remote.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_smart_install.py +0 -0
- {optio_opencode-0.1.8 → optio_opencode-0.2.0}/tests/test_snapshots.py +0 -0
- {optio_opencode-0.1.8 → 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
|
|
@@ -483,6 +500,24 @@ async def _capture_snapshot(
|
|
|
483
500
|
opencode_executable: str = "opencode",
|
|
484
501
|
session_blob_encrypt: "Callable[[bytes], bytes] | None" = None,
|
|
485
502
|
) -> None:
|
|
503
|
+
# Defense-in-depth (guard #2): refuse to capture a resumable snapshot
|
|
504
|
+
# unless opencode's auth.json exists and is non-empty on the host. A
|
|
505
|
+
# credential-less workdir is degenerate — restoring it would relaunch
|
|
506
|
+
# opencode with no auth, so marking it resumable is worse than useless.
|
|
507
|
+
# (Live-reach is already covered by the session_id is not None gate at
|
|
508
|
+
# the call site; this covers the bad/empty-seed edge.)
|
|
509
|
+
workdir = host.workdir.rstrip("/")
|
|
510
|
+
chk = await host.run_command(
|
|
511
|
+
f"test -s {shlex.quote(workdir)}/home/.local/share/opencode/auth.json "
|
|
512
|
+
f"&& echo OK || true"
|
|
513
|
+
)
|
|
514
|
+
if "OK" not in chk.stdout:
|
|
515
|
+
_LOG.warning(
|
|
516
|
+
"snapshot capture skipped: opencode auth.json absent/empty; "
|
|
517
|
+
"refusing to mark resumable"
|
|
518
|
+
)
|
|
519
|
+
return
|
|
520
|
+
|
|
486
521
|
session_json = await host_actions.opencode_export(
|
|
487
522
|
host, opencode_db, session_id,
|
|
488
523
|
opencode_executable=opencode_executable,
|
|
@@ -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"
|
|
@@ -99,6 +99,11 @@ async def test_capture_writes_through_session_blob_encrypt(monkeypatch):
|
|
|
99
99
|
yield b"workdir-bytes"
|
|
100
100
|
fake_host = MagicMock()
|
|
101
101
|
fake_host.archive_workdir = _fake_archive
|
|
102
|
+
# Satisfy the snapshot-capture defense-in-depth guard: it runs a
|
|
103
|
+
# `test -s .../auth.json && echo OK` probe on the host and refuses to
|
|
104
|
+
# capture unless the output contains "OK".
|
|
105
|
+
fake_host.workdir = "/work"
|
|
106
|
+
fake_host.run_command = AsyncMock(return_value=MagicMock(stdout="OK\n"))
|
|
102
107
|
|
|
103
108
|
await _capture_snapshot(
|
|
104
109
|
fake_ctx, fake_host,
|
|
@@ -92,6 +92,21 @@ def _config(scenario: str, deliverable_cb=None, raises: bool = False) -> Opencod
|
|
|
92
92
|
)
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
async def _plant_auth_json(hook_ctx) -> None:
|
|
96
|
+
"""before_execute hook: plant a non-empty opencode auth.json in the workdir.
|
|
97
|
+
|
|
98
|
+
The snapshot-capture defense-in-depth guard refuses to mark a session
|
|
99
|
+
resumable unless ``home/.local/share/opencode/auth.json`` exists and is
|
|
100
|
+
non-empty on the host, so capture-expecting tests must plant credentials
|
|
101
|
+
the same way a real seeded launch would.
|
|
102
|
+
"""
|
|
103
|
+
await hook_ctx.run_on_host(
|
|
104
|
+
"mkdir -p home/.local/share/opencode && "
|
|
105
|
+
"printf '{\"anthropic\": {\"type\": \"api\", \"key\": \"sk-test\"}}' "
|
|
106
|
+
"> home/.local/share/opencode/auth.json"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
95
110
|
@pytest.fixture(autouse=True)
|
|
96
111
|
def _supply_scenario(monkeypatch):
|
|
97
112
|
"""Substitute fake_opencode.py for the real opencode binary.
|
|
@@ -596,11 +611,14 @@ async def test_session_local_supports_resume_true_captures_snapshot(
|
|
|
596
611
|
ctx_and_captures, _supply_scenario, tmp_workdir,
|
|
597
612
|
):
|
|
598
613
|
"""With supports_resume=True (default), a snapshot IS captured."""
|
|
614
|
+
import dataclasses
|
|
599
615
|
from optio_opencode.snapshots import SESSION_SNAPSHOT_COLLECTION_SUFFIX
|
|
600
616
|
ctx, _, _ = ctx_and_captures
|
|
601
617
|
_supply_scenario["name"] = "happy"
|
|
602
618
|
|
|
603
|
-
|
|
619
|
+
# Plant auth.json so the snapshot-capture defense-in-depth guard permits
|
|
620
|
+
# capture; without credentials it would refuse to mark resumable.
|
|
621
|
+
cfg = dataclasses.replace(_config("happy"), before_execute=_plant_auth_json)
|
|
604
622
|
await run_opencode_session(ctx, cfg)
|
|
605
623
|
|
|
606
624
|
coll_name = f"{ctx._prefix}{SESSION_SNAPSHOT_COLLECTION_SUFFIX}"
|
|
@@ -121,9 +121,30 @@ async def _make_ctx(mongo_db, process_id: str, *, resume: bool):
|
|
|
121
121
|
return ctx, proc["_id"]
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
async def
|
|
124
|
+
async def _plant_auth_json(hook_ctx) -> None:
|
|
125
|
+
"""before_execute hook: plant a non-empty opencode auth.json in the workdir.
|
|
126
|
+
|
|
127
|
+
The snapshot-capture defense-in-depth guard refuses to mark a session
|
|
128
|
+
resumable unless ``home/.local/share/opencode/auth.json`` exists and is
|
|
129
|
+
non-empty on the host. Capture-expecting tests must therefore plant a
|
|
130
|
+
credentials file the same way a real seeded launch would, mirroring how
|
|
131
|
+
claudecode tests plant ``.credentials.json``.
|
|
132
|
+
"""
|
|
133
|
+
await hook_ctx.run_on_host(
|
|
134
|
+
"mkdir -p home/.local/share/opencode && "
|
|
135
|
+
"printf '{\"anthropic\": {\"type\": \"api\", \"key\": \"sk-test\"}}' "
|
|
136
|
+
"> home/.local/share/opencode/auth.json"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
async def _run_one_cycle(
|
|
141
|
+
mongo_db, process_id: str, resume: bool, *, plant_auth: bool = True,
|
|
142
|
+
) -> None:
|
|
125
143
|
ctx, _ = await _make_ctx(mongo_db, process_id, resume=resume)
|
|
126
|
-
cfg = OpencodeTaskConfig(
|
|
144
|
+
cfg = OpencodeTaskConfig(
|
|
145
|
+
consumer_instructions=f"(scenario: happy {process_id})",
|
|
146
|
+
before_execute=_plant_auth_json if plant_auth else None,
|
|
147
|
+
)
|
|
127
148
|
await run_opencode_session(ctx, cfg)
|
|
128
149
|
|
|
129
150
|
|
|
@@ -142,6 +163,27 @@ async def test_terminal_flow_captures_snapshot_and_wipes_workdir(mongo_db, task_
|
|
|
142
163
|
assert not wd.exists() or not any(wd.iterdir())
|
|
143
164
|
|
|
144
165
|
|
|
166
|
+
async def test_no_auth_json_refuses_to_capture_snapshot(mongo_db, task_root):
|
|
167
|
+
"""Defense-in-depth: a normal launch (session_id created) but with NO
|
|
168
|
+
opencode auth.json on the host must NOT capture a snapshot or mark the
|
|
169
|
+
process resumable. Guards against marking a degenerate, credential-less
|
|
170
|
+
workdir as resumable.
|
|
171
|
+
"""
|
|
172
|
+
pid = "oc_no_auth_1"
|
|
173
|
+
await _run_one_cycle(mongo_db, pid, resume=False, plant_auth=False)
|
|
174
|
+
|
|
175
|
+
snap = await load_latest_snapshot(mongo_db, prefix="test", process_id=pid)
|
|
176
|
+
assert snap is None
|
|
177
|
+
|
|
178
|
+
count = await mongo_db[f"test{SESSION_SNAPSHOT_COLLECTION_SUFFIX}"].count_documents(
|
|
179
|
+
{"processId": pid}
|
|
180
|
+
)
|
|
181
|
+
assert count == 0
|
|
182
|
+
|
|
183
|
+
proc = await mongo_db["test_processes"].find_one({"processId": pid})
|
|
184
|
+
assert proc.get("hasSavedState") is not True
|
|
185
|
+
|
|
186
|
+
|
|
145
187
|
async def test_resume_creates_second_snapshot(mongo_db, task_root):
|
|
146
188
|
pid = "oc_resume_1"
|
|
147
189
|
await _run_one_cycle(mongo_db, pid, resume=False)
|
|
@@ -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"
|
|
@@ -330,19 +331,34 @@ async def test_auto_start_posts_on_fresh_and_not_on_resume(
|
|
|
330
331
|
await run_opencode_session(ctx_fresh, OpencodeTaskConfig(
|
|
331
332
|
consumer_instructions="(scenario: happy)",
|
|
332
333
|
auto_start=True,
|
|
334
|
+
# Plant auth.json so the snapshot-capture defense-in-depth guard does
|
|
335
|
+
# not refuse to mark this resumable; otherwise the resume leg would
|
|
336
|
+
# fall back to a fresh launch and POST the kickoff prompt again.
|
|
337
|
+
before_execute=_plant_env,
|
|
333
338
|
))
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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.
|
|
340
352
|
ctx_resume = await _make_ctx(mongo_db, pid, resume=True)
|
|
341
353
|
await run_opencode_session(ctx_resume, OpencodeTaskConfig(
|
|
342
354
|
consumer_instructions="(scenario: happy)",
|
|
343
355
|
auto_start=True,
|
|
356
|
+
before_execute=_plant_env,
|
|
344
357
|
))
|
|
345
|
-
|
|
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
|
|
346
362
|
|
|
347
363
|
|
|
348
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.8 → 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
|