agentic-workspace 1.0.0rc3__py3-none-win_amd64.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.
@@ -0,0 +1,8 @@
1
+ """The installed Python projection of the native Agentic Workspace authority."""
2
+
3
+ from importlib.metadata import version
4
+
5
+ from ._binding import DecisionContractError, answer_carried, invoke, invoke_carried, resources, select_reference, start
6
+
7
+ __version__ = version("agentic-workspace")
8
+ __all__ = ["DecisionContractError", "answer_carried", "invoke", "invoke_carried", "resources", "select_reference", "start"]
@@ -0,0 +1,64 @@
1
+ """Private installed transport; ordinary meaning belongs to the paired Rust core."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import subprocess
6
+ from collections.abc import Mapping
7
+ from typing import Any
8
+
9
+ from .native_core import core_binary as native_core_binary
10
+
11
+
12
+ class DecisionContractError(ValueError):
13
+ """Raised when the shared core rejects a source-decision request."""
14
+
15
+ def resources(context: Mapping[str, Any]) -> dict[str, Any]:
16
+ """Bounded Rust-owned resource proposals and effects; no host policy reducer."""
17
+ return _request({"resources": context})
18
+
19
+ def start(context: Mapping[str, Any]) -> dict[str, Any]:
20
+ """Consume repository sources through the native public owner boundary."""
21
+ return _request({"start": context})
22
+
23
+ def invoke(context: Mapping[str, Any]) -> dict[str, Any]:
24
+ """Submit an exact public invocation to the same native owner boundary."""
25
+ return _request({"invoke": context})
26
+
27
+ def select_reference(context: Mapping[str, Any], reference: str, **material: Any) -> dict[str, Any]:
28
+ """Forward an exact reference and optional bounded answer to Rust.
29
+
30
+ Context is copied, never retained as hidden session state. Rust alone
31
+ validates the answer and current owner identity.
32
+ """
33
+ if set(material) - {"answer"}:
34
+ raise TypeError("select_reference accepts only answer material")
35
+ return start({**context, "reference": reference, **material})
36
+
37
+ def answer_carried(carriage: Mapping[str, Any], reference: str, answer: Any) -> dict[str, Any]:
38
+ """Carry exact owner material; Rust binds only the returned bounded answer."""
39
+ return _request({"start": {"request": carriage, "reference": reference, "answer": answer, "projection": "carried"}})
40
+
41
+ def invoke_carried(carriage: Mapping[str, Any], reference: str) -> dict[str, Any]:
42
+ """Execute one exact carried action, with native execution-time admission."""
43
+ return _request({"invoke": {"invocation": carriage, "reference": reference, "projection": "carried"}})
44
+
45
+ def _request(payload: Mapping[str, Any]) -> dict[str, Any]:
46
+ try:
47
+ binary = native_core_binary()
48
+ except (OSError, RuntimeError) as error:
49
+ raise DecisionContractError(str(error)) from error
50
+ completed = subprocess.run(
51
+ [str(binary)],
52
+ input=json.dumps(payload, separators=(",", ":")),
53
+ text=True,
54
+ encoding="utf-8",
55
+ capture_output=True,
56
+ check=False,
57
+ )
58
+ if completed.returncode != 0:
59
+ try:
60
+ message = json.loads(completed.stderr)["error"]["message"]
61
+ except (KeyError, TypeError, json.JSONDecodeError):
62
+ message = completed.stderr.strip() or f"shared core exited with status {completed.returncode}"
63
+ raise DecisionContractError(str(message))
64
+ return dict(json.loads(completed.stdout))
@@ -0,0 +1 @@
1
+ {"kind": "agentic-workspace/native-python-artifact/v1", "package_version": "1.0.0rc3", "rust_host": "x86_64-pc-windows-msvc", "rust_target": "x86_64-pc-windows-msvc", "rust_toolchain": {"channel": "1.98.1", "declaration_sha256": "887f9be066a15585a2c583578e84b0fcb541126d81546276bad3d2ff00d61167", "release": "1.98.1", "commit_hash": "48a229ceaefd4985c50990b14116b6d856af0985", "commit_date": "2026-09-01", "host": "x86_64-pc-windows-msvc", "llvm_version": "22.1.8"}, "source_head": "85272df02be42707f6abd21cb120e25fffcd85f7", "source_dirty": false, "sha256": "5c436e86923fb8947bee4e7f1c3dbe478b9257a834aab9e2a95248bb113fcf67", "cli_sha256": "4792983d514835db22b8beb8cf9c4162c92bd9ef5fa65ad70eed8a1aeb100be9"}
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ import sys
5
+
6
+ from agentic_workspace.native_core import cli_binary
7
+
8
+
9
+ def _run_cli(argv: list[str] | None = None) -> int:
10
+ """Optional Python entry point; the paired Rust executable owns the command."""
11
+ args = list(sys.argv[1:] if argv is None else argv)
12
+ try:
13
+ return subprocess.call([str(cli_binary()), *args])
14
+ except (OSError, RuntimeError) as exc:
15
+ print(str(exc), file=sys.stderr)
16
+ return 2
17
+
18
+
19
+ main = _run_cli
20
+
21
+ __all__ = ["main"]
@@ -0,0 +1,483 @@
1
+ """Codex protocol I/O only; no Assignment policy, packet admission or repository semantics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import os
8
+ import queue
9
+ import shutil
10
+ import signal
11
+ import subprocess
12
+ import tempfile
13
+ import threading
14
+ import time
15
+ from contextlib import contextmanager
16
+ from contextvars import ContextVar
17
+ from pathlib import Path
18
+ from typing import Any, Callable, Iterator
19
+
20
+ _discovery_scope: ContextVar[dict[tuple[str, str], dict[str, Any]] | None] = ContextVar("native-discovery-scope", default=None)
21
+
22
+
23
+ @contextmanager
24
+ def discovery_scope() -> Iterator[None]:
25
+ """Share one current snapshot within a bounded source-owner evaluation."""
26
+ token = _discovery_scope.set({})
27
+ try:
28
+ yield
29
+ finally:
30
+ _discovery_scope.reset(token)
31
+
32
+
33
+ def digest(value: Any) -> str:
34
+ return "sha256:" + hashlib.sha256(json.dumps(value, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
35
+
36
+
37
+ def _write(path: Path, value: Any) -> None:
38
+ path.parent.mkdir(parents=True, exist_ok=True)
39
+ temporary = path.with_name(f".{path.name}.{os.getpid()}.tmp")
40
+ temporary.write_text(json.dumps(value, sort_keys=True), encoding="utf-8")
41
+ temporary.replace(path)
42
+
43
+
44
+ class ProviderError(ValueError):
45
+ def __init__(self, message: str, *, metrics: dict[str, Any] | None = None):
46
+ super().__init__(message)
47
+ self.metrics = metrics or {}
48
+
49
+
50
+ class CodexConnection:
51
+ """One provider process. Only the provider owns conversation persistence."""
52
+
53
+ def __init__(self, executable: str, *, timeout: float = 30, environment: dict[str, str] | None = None):
54
+ self.timeout = timeout
55
+ self.process = subprocess.Popen(
56
+ [executable, "app-server", "--stdio"],
57
+ stdin=subprocess.PIPE,
58
+ stdout=subprocess.PIPE,
59
+ stderr=subprocess.DEVNULL,
60
+ text=True,
61
+ encoding="utf-8",
62
+ env={**os.environ, **environment} if environment is not None else None,
63
+ start_new_session=os.name != "nt",
64
+ creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0,
65
+ )
66
+ self.messages: queue.Queue[dict[str, Any]] = queue.Queue()
67
+ self.number = 0
68
+ self.events: list[dict[str, Any]] = []
69
+ threading.Thread(target=self._read, daemon=True).start()
70
+ try:
71
+ self.call("initialize", {"clientInfo": {"name": "aw-native-transport", "version": "1"}})
72
+ self.send({"method": "initialized"})
73
+ except Exception:
74
+ self.close()
75
+ raise
76
+
77
+ def _read(self) -> None:
78
+ assert self.process.stdout is not None
79
+ for line in self.process.stdout:
80
+ try:
81
+ value = json.loads(line)
82
+ except ValueError:
83
+ continue
84
+ # Drop reasoning, deltas, commands and history at the adapter ingress.
85
+ if "id" in value or value.get("method") in {"item/completed", "turn/completed", "thread/tokenUsage/updated", "error"}:
86
+ if value.get("method") == "item/completed" and value.get("params", {}).get("item", {}).get("type") != "agentMessage":
87
+ continue
88
+ self.messages.put(value)
89
+ self.messages.put({"adapter_closed": True})
90
+
91
+ def send(self, payload: dict[str, Any]) -> None:
92
+ assert self.process.stdin is not None
93
+ self.process.stdin.write(json.dumps(payload) + "\n")
94
+ self.process.stdin.flush()
95
+
96
+ def next(self, timeout: float | None = None) -> dict[str, Any]:
97
+ try:
98
+ item = self.messages.get(timeout=self.timeout if timeout is None else timeout)
99
+ except queue.Empty as error:
100
+ raise ProviderError("provider-response-timeout") from error
101
+ if item.get("adapter_closed"):
102
+ raise ProviderError("provider-process-closed")
103
+ # This adapter does not admit interactive approvals or tool execution
104
+ # outside its fixed read-only sandbox. Deny provider requests explicitly.
105
+ if "id" in item and "method" in item:
106
+ self.send({"id": item["id"], "error": {"code": -32601, "message": "Unsupported by bounded read-only adapter"}})
107
+ return self.next(timeout)
108
+ return item
109
+
110
+ def call(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
111
+ self.number += 1
112
+ number = self.number
113
+ self.send({"id": number, "method": method, "params": params})
114
+ deadline = time.monotonic() + self.timeout
115
+ while time.monotonic() < deadline:
116
+ message = self.next(max(0.01, deadline - time.monotonic()))
117
+ if message.get("id") == number:
118
+ if "error" in message:
119
+ if (
120
+ method == "thread/archive"
121
+ and message["error"].get("message") == f"no rollout found for thread id {params.get('threadId')}"
122
+ ):
123
+ raise ProviderError("native-active-thread-unavailable")
124
+ if (
125
+ method in {"thread/resume", "thread/fork"}
126
+ and message["error"].get("message") == f"no rollout found for thread id {params.get('threadId')}"
127
+ ):
128
+ raise ProviderError("native-continuation-unavailable")
129
+ # No prompt or provider payload is retained in an error.
130
+ raise ProviderError(f"provider-operation-rejected:{method}:{message['error'].get('code')}")
131
+ return message["result"]
132
+ self.events.append(message)
133
+ raise ProviderError("provider-response-timeout")
134
+
135
+ def close(self) -> None:
136
+ if self.process.stdin is not None and not self.process.stdin.closed:
137
+ self.process.stdin.close()
138
+ try:
139
+ self.process.wait(timeout=5)
140
+ except subprocess.TimeoutExpired:
141
+ if os.name == "nt":
142
+ # The installed CLI may be a cmd/node launcher. Terminating
143
+ # only that wrapper does not prove its worker has stopped.
144
+ killed = subprocess.run(
145
+ ["taskkill", "/PID", str(self.process.pid), "/T", "/F"],
146
+ capture_output=True,
147
+ timeout=10,
148
+ creationflags=subprocess.CREATE_NO_WINDOW,
149
+ check=False,
150
+ )
151
+ if killed.returncode != 0:
152
+ raise ProviderError("native-process-tree-release-unconfirmed")
153
+ self.process.wait(timeout=5)
154
+ else:
155
+ try:
156
+ os.killpg(self.process.pid, signal.SIGKILL)
157
+ except ProcessLookupError:
158
+ pass
159
+ self.process.wait(timeout=5)
160
+
161
+
162
+ def discover(
163
+ root: Path, *, executable: str = "codex", refresh: bool = False, now: float | None = None, persist: bool = True
164
+ ) -> dict[str, Any]:
165
+ """Local version signal plus bounded remote/account capability refresh."""
166
+ clock = time.time() if now is None else now
167
+ scope = _discovery_scope.get()
168
+ key = (str(root.resolve()), executable)
169
+ scoped = scope.get(key, {}) if scope is not None else {}
170
+ if not refresh and clock < scoped.get("expires_at", 0) and not scoped.get("invalidated"):
171
+ return scoped
172
+ resolved = shutil.which(executable)
173
+ if not resolved:
174
+ raise ProviderError("native-adapter-executable-unavailable")
175
+ version = subprocess.run([resolved, "--version"], capture_output=True, text=True, timeout=10, check=True).stdout.strip()
176
+ adapter_revision = hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
177
+ identity = digest({"adapter": "codex-app-server/v1", "implementation": adapter_revision, "executable": resolved, "version": version})
178
+ path = root / ".agentic-workspace/local/transport-capabilities" / f"{identity[7:]}.json"
179
+ try:
180
+ cached = json.loads(path.read_text(encoding="utf-8"))
181
+ except (OSError, ValueError):
182
+ cached = {}
183
+ if not refresh and cached.get("identity") == identity and clock < cached.get("expires_at", 0) and not cached.get("invalidated"):
184
+ if scope is not None:
185
+ scope[key] = cached
186
+ return cached
187
+ # Discover actual installed protocol properties. In particular excludeTurns
188
+ # is required: AW never requests the provider's persisted conversation.
189
+ with tempfile.TemporaryDirectory(prefix="aw-provider-schema-") as directory:
190
+ subprocess.run([resolved, "app-server", "generate-json-schema", "--out", directory], capture_output=True, timeout=20, check=True)
191
+ schemas = {}
192
+ for name in (
193
+ "ThreadStartParams",
194
+ "ThreadResumeParams",
195
+ "ThreadForkParams",
196
+ "TurnStartParams",
197
+ "ThreadArchiveParams",
198
+ "ConfigReadParams",
199
+ ):
200
+ schema_path = Path(directory) / "v2" / f"{name}.json"
201
+ if schema_path.is_file():
202
+ schemas[name] = json.loads(schema_path.read_text())["properties"]
203
+ turn_supported = {"threadId", "input", "outputSchema", "model", "approvalPolicy"} <= schemas.get("TurnStartParams", {}).keys()
204
+ thread_fields = {"cwd", "model", "sandbox", "approvalPolicy"}
205
+ modes = ["fresh"] if turn_supported and thread_fields <= schemas.get("ThreadStartParams", {}).keys() else []
206
+ for mode, name in (("resume", "ThreadResumeParams"), ("fork", "ThreadForkParams")):
207
+ if turn_supported and (thread_fields | {"excludeTurns", "threadId"}) <= schemas.get(name, {}).keys():
208
+ modes.append(mode)
209
+ if "resume" in modes:
210
+ modes.append("restart")
211
+ connection = CodexConnection(resolved)
212
+ try:
213
+ result = connection.call("model/list", {"limit": 100})
214
+ effective_effort = None
215
+ effective_settings_known = {"cwd", "includeLayers"} <= schemas.get("ConfigReadParams", {}).keys()
216
+ if effective_settings_known:
217
+ effective = connection.call("config/read", {"includeLayers": False, "cwd": str(root)})
218
+ effective_effort = effective.get("config", {}).get("model_reasoning_effort")
219
+ finally:
220
+ connection.close()
221
+ if result.get("nextCursor"):
222
+ raise ProviderError("provider-catalog-exceeds-bounded-discovery")
223
+ catalog = [
224
+ {
225
+ "model": m["model"],
226
+ "reasoning_efforts": [item["reasoningEffort"] for item in m.get("supportedReasoningEfforts", [])],
227
+ "default_reasoning_effort": m.get("defaultReasoningEffort"),
228
+ }
229
+ for m in result["data"]
230
+ if not m.get("hidden")
231
+ ]
232
+ facts = {
233
+ "adapter": "codex-app-server/v1",
234
+ "implementation_revision": adapter_revision,
235
+ "executable": resolved,
236
+ "version": version,
237
+ "modes": modes,
238
+ "models": catalog,
239
+ "effective_reasoning_effort": effective_effort,
240
+ "effective_settings_known": effective_settings_known,
241
+ "protocol_revision": digest(schemas),
242
+ "parameters": ["model", "timeout_seconds"]
243
+ + (["reasoning_effort"] if "effort" in schemas.get("TurnStartParams", {}) else [])
244
+ + (["ephemeral"] if "ephemeral" in schemas.get("ThreadStartParams", {}) else []),
245
+ "ephemeral_modes": [
246
+ mode
247
+ for mode, name in (("fresh", "ThreadStartParams"), ("fork", "ThreadForkParams"))
248
+ if mode in modes and "ephemeral" in schemas.get(name, {})
249
+ ],
250
+ "archive_supported": "threadId" in schemas.get("ThreadArchiveParams", {}),
251
+ }
252
+ snapshot = {
253
+ **facts,
254
+ "identity": identity,
255
+ "revision": digest(facts),
256
+ "observed_at": clock,
257
+ "expires_at": clock + 900,
258
+ "live_worker_continuity": False,
259
+ "exclusive_writer": True,
260
+ "process_lifetime": "one-dispatch",
261
+ "conversation_lifetime": "provider-owned",
262
+ "source": "installed-protocol-and-model-list",
263
+ }
264
+ if persist:
265
+ _write(path, snapshot)
266
+ if scope is not None:
267
+ scope[key] = snapshot
268
+ return snapshot
269
+
270
+
271
+ def validate_selection(snapshot: dict[str, Any], selection: dict[str, Any], *, now: float | None = None) -> None:
272
+ clock = time.time() if now is None else now
273
+ if snapshot.get("invalidated") or clock >= snapshot["expires_at"] or selection.get("capability_revision") != snapshot["revision"]:
274
+ raise ProviderError("native-capability-not-current")
275
+ if set(selection) - {"capability_revision", "mode", "reference", "parameters", "semantic_scope", "lineage_revision"}:
276
+ raise ProviderError("unsupported-native-selection-field")
277
+ mode = selection.get("mode")
278
+ if mode not in snapshot["modes"]:
279
+ raise ProviderError("native-continuity-unavailable")
280
+ if mode != "fresh" and not selection.get("reference"):
281
+ raise ProviderError("native-continuation-reference-required")
282
+ if mode == "fresh" and selection.get("reference"):
283
+ raise ProviderError("fresh-cannot-inherit-reference")
284
+ validate_parameters(snapshot, mode, selection.get("parameters", {}))
285
+
286
+
287
+ def validate_parameters(snapshot: dict[str, Any], mode: str, parameters: dict[str, Any]) -> None:
288
+ if set(parameters) - set(snapshot.get("parameters", ["model"])):
289
+ raise ProviderError("native-parameter-unsupported")
290
+ if "ephemeral" in parameters and not isinstance(parameters["ephemeral"], bool):
291
+ raise ProviderError("native-parameter-unsupported")
292
+ if parameters.get("ephemeral") and mode not in snapshot.get("ephemeral_modes", []):
293
+ raise ProviderError("native-ephemeral-continuity-unavailable")
294
+ model = next((m for m in snapshot["models"] if m["model"] == parameters.get("model")), None)
295
+ if model is None:
296
+ raise ProviderError("native-model-unavailable")
297
+ if "reasoning_effort" in parameters and parameters["reasoning_effort"] not in model["reasoning_efforts"]:
298
+ raise ProviderError("native-parameter-unsupported")
299
+ if "timeout_seconds" in parameters:
300
+ timeout = parameters["timeout_seconds"]
301
+ if not isinstance(timeout, int) or isinstance(timeout, bool) or not 1 <= timeout <= 1800:
302
+ raise ProviderError("native-parameter-unsupported")
303
+
304
+
305
+ @contextmanager
306
+ def exclusive_lineage(reference: str) -> Iterator[None]:
307
+ """Cross-worktree OS lock; released on process death, never guessed from TTL."""
308
+ directory = Path(tempfile.gettempdir()) / "agentic-workspace-provider-locks"
309
+ directory.mkdir(parents=True, exist_ok=True)
310
+ path = directory / (hashlib.sha256(("codex:" + reference).encode()).hexdigest() + ".lock")
311
+ with path.open("a+b") as lock:
312
+ try:
313
+ if path.stat().st_size == 0:
314
+ lock.write(b"0")
315
+ lock.flush()
316
+ lock.seek(0)
317
+ if os.name == "nt":
318
+ import msvcrt
319
+
320
+ msvcrt.locking(lock.fileno(), msvcrt.LK_NBLCK, 1)
321
+ else:
322
+ import fcntl
323
+
324
+ fcntl.flock(lock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
325
+ except OSError as error:
326
+ raise ProviderError("native-lineage-exclusive-writer-busy") from error
327
+ try:
328
+ yield
329
+ finally:
330
+ lock.seek(0)
331
+ if os.name == "nt":
332
+ import msvcrt
333
+
334
+ msvcrt.locking(lock.fileno(), msvcrt.LK_UNLCK, 1)
335
+ else:
336
+ import fcntl
337
+
338
+ fcntl.flock(lock.fileno(), fcntl.LOCK_UN)
339
+
340
+
341
+ def execute(
342
+ root: Path,
343
+ snapshot: dict[str, Any],
344
+ selection: dict[str, Any],
345
+ prompt: str,
346
+ schema: dict[str, Any],
347
+ *,
348
+ timeout: int = 1800,
349
+ on_thread: Callable[[str], None] | None = None,
350
+ worker_environment: dict[str, str] | None = None,
351
+ on_closed: Callable[[], None] | None = None,
352
+ on_history: Callable[[bool], None] | None = None,
353
+ ) -> dict[str, Any]:
354
+ """Run an exact native topology; missing provider state never becomes fresh."""
355
+ validate_selection(snapshot, selection)
356
+ reference = selection.get("reference")
357
+ mode = selection["mode"]
358
+ started = time.monotonic()
359
+ metrics: dict[str, Any] = {"kind": "agentic-workspace/assignment-transport-metrics/v1"}
360
+ usage_current = True
361
+ deadline = None
362
+ with exclusive_lineage(reference or "fresh:" + os.urandom(16).hex()):
363
+ connection = (
364
+ CodexConnection(snapshot["executable"], environment=worker_environment)
365
+ if worker_environment is not None
366
+ else CodexConnection(snapshot["executable"])
367
+ )
368
+ try:
369
+ method = {"fresh": "thread/start", "resume": "thread/resume", "restart": "thread/resume", "fork": "thread/fork"}[mode]
370
+ params = {"cwd": str(root), "model": selection["parameters"]["model"], "sandbox": "read-only", "approvalPolicy": "never"}
371
+ ephemeral = selection["parameters"].get("ephemeral", False)
372
+ if mode in {"fresh", "fork"} and "ephemeral" in selection["parameters"]:
373
+ params["ephemeral"] = ephemeral
374
+ if reference:
375
+ params.update({"threadId": reference, "excludeTurns": True})
376
+ result = connection.call(method, params)
377
+ thread = result["thread"]
378
+ actual = thread["id"]
379
+ if mode in {"resume", "restart"} and actual != reference:
380
+ raise ProviderError("provider-continuation-identity-mismatch")
381
+ if mode == "fork" and actual == reference:
382
+ raise ProviderError("provider-fork-lineage-not-independent")
383
+ if on_thread is not None:
384
+ on_thread(actual)
385
+ if on_history is not None and isinstance(thread.get("ephemeral"), bool):
386
+ on_history(thread["ephemeral"])
387
+ if ephemeral and thread.get("ephemeral") is not True:
388
+ raise ProviderError("provider-ephemeral-guarantee-not-enforced")
389
+ if not ephemeral and thread.get("ephemeral") is True:
390
+ raise ProviderError("provider-persistence-guarantee-not-enforced")
391
+ if "ephemeral" in selection["parameters"] and thread.get("ephemeral") is not ephemeral:
392
+ raise ProviderError("provider-history-guarantee-unconfirmed")
393
+ turn_params = {
394
+ "threadId": actual,
395
+ "input": [{"type": "text", "text": prompt}],
396
+ "outputSchema": schema,
397
+ "model": selection["parameters"]["model"],
398
+ "approvalPolicy": "never",
399
+ }
400
+ if selection["parameters"].get("reasoning_effort"):
401
+ turn_params["effort"] = selection["parameters"]["reasoning_effort"]
402
+ turn = connection.call("turn/start", turn_params)["turn"]["id"]
403
+ output = ""
404
+ deadline = time.monotonic() + timeout
405
+ while time.monotonic() < deadline:
406
+ event = connection.events.pop(0) if connection.events else connection.next(max(0.01, deadline - time.monotonic()))
407
+ payload = event.get("params", {})
408
+ if payload.get("threadId") != actual:
409
+ continue
410
+ if event.get("method") == "thread/tokenUsage/updated" and payload.get("turnId") == turn and mode == "fresh":
411
+ # `last` covers one model response, not the entire tool loop.
412
+ # Fresh has a known zero lineage baseline. Reused contexts
413
+ # lack a proven pre-turn baseline, so their total is unknown.
414
+ usage = payload.get("tokenUsage", {}).get("total", {})
415
+ observed: dict[str, int] = {}
416
+ for source, target in (
417
+ ("inputTokens", "effective_input_tokens"),
418
+ ("cachedInputTokens", "cached_input_tokens"),
419
+ ("outputTokens", "output_tokens"),
420
+ ):
421
+ value = usage.get(source)
422
+ if isinstance(value, int) and not isinstance(value, bool) and value >= 0:
423
+ observed[target] = value
424
+ fields = ("effective_input_tokens", "cached_input_tokens", "output_tokens")
425
+ if (
426
+ len(observed) != len(fields)
427
+ or any(observed.get(field, -1) < metrics.get(field, 0) for field in fields)
428
+ or observed.get("cached_input_tokens", 0) > observed.get("effective_input_tokens", 0)
429
+ ):
430
+ usage_current = False
431
+ if usage_current:
432
+ metrics.update(observed)
433
+ else:
434
+ # A provider counter reset/estimate is not a cheaper run.
435
+ for field in fields:
436
+ metrics.pop(field, None)
437
+ if payload.get("turnId") == turn and event.get("method") == "item/completed":
438
+ output = payload["item"].get("text", "")
439
+ if event.get("method") == "turn/completed" and payload.get("turn", {}).get("id") == turn:
440
+ if payload["turn"].get("status") != "completed":
441
+ raise ProviderError("provider-turn-failed")
442
+ return {
443
+ "returned_work": json.loads(output),
444
+ "metrics": metrics,
445
+ "elapsed_ms": round((time.monotonic() - started) * 1000),
446
+ "continuation": {
447
+ "reference": actual,
448
+ "mode": mode,
449
+ "capability_revision": snapshot["revision"],
450
+ "parameters": selection["parameters"],
451
+ "live": False,
452
+ "exclusive": True,
453
+ "ephemeral": ephemeral,
454
+ },
455
+ "raw_transcript_stored": False,
456
+ }
457
+ raise ProviderError("provider-turn-timeout")
458
+ except (ProviderError, ValueError, OSError) as error:
459
+ reason = str(error) if isinstance(error, ProviderError) else "native-return-or-control-failed"
460
+ if reason == "provider-response-timeout" and deadline is not None and time.monotonic() >= deadline:
461
+ reason = "provider-turn-timeout"
462
+ raise ProviderError(reason, metrics=metrics) from error
463
+ finally:
464
+ connection.close()
465
+ if on_closed is not None:
466
+ on_closed()
467
+
468
+
469
+ def archive_reference(snapshot: dict[str, Any], reference: str) -> str:
470
+ """Reversible cleanup of a caller-owned terminal worker; never delete state."""
471
+ if not snapshot.get("archive_supported"):
472
+ raise ProviderError("native-archive-unavailable")
473
+ connection = CodexConnection(snapshot["executable"])
474
+ try:
475
+ try:
476
+ connection.call("thread/archive", {"threadId": reference})
477
+ except ProviderError as error:
478
+ if str(error) != "native-active-thread-unavailable":
479
+ raise
480
+ return "already-absent"
481
+ return "archived"
482
+ finally:
483
+ connection.close()
@@ -0,0 +1,63 @@
1
+ """Resolve verified package artifacts, a prepared source checkout, or an explicit core."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import os
8
+ from importlib.metadata import version
9
+ from pathlib import Path
10
+
11
+
12
+ def _verify(path: Path, key: str) -> None:
13
+ packaged = Path(__file__).with_name("_native")
14
+ manifest_path = packaged / "artifact.json"
15
+ if packaged.is_dir():
16
+ if not manifest_path.is_file():
17
+ raise RuntimeError("packaged native artifact manifest missing")
18
+ manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
19
+ if manifest["package_version"] != version("agentic-workspace"):
20
+ raise RuntimeError("packaged native artifact version mismatch")
21
+ if hashlib.sha256(path.read_bytes()).hexdigest() != manifest[key]:
22
+ raise RuntimeError("packaged native artifact digest mismatch")
23
+
24
+
25
+ def core_binary() -> Path:
26
+ name = "agentic-workspace-core.exe" if os.name == "nt" else "agentic-workspace-core"
27
+ configured = os.environ.get("AGENTIC_WORKSPACE_CORE_BINARY")
28
+ packaged = Path(__file__).with_name("_native")
29
+ path = Path(configured) if configured else packaged / name
30
+ # Only this module's source tree can supply an implicit development build.
31
+ # Never search cwd/PATH or fall back from a present packaged distribution.
32
+ root = Path(__file__).resolve().parents[2]
33
+ if (
34
+ not configured
35
+ and not packaged.exists()
36
+ and Path(__file__).resolve().parent.parent == root / "src"
37
+ and (root / "Cargo.lock").is_file()
38
+ and (root / "crates/agentic-workspace-core/Cargo.toml").is_file()
39
+ and (root / "crates/agentic-workspace-cli/Cargo.toml").is_file()
40
+ ):
41
+ directory = root / "target/debug"
42
+ path = directory / name
43
+ cli = directory / ("agentic-workspace.exe" if os.name == "nt" else "agentic-workspace")
44
+ if not path.is_file() or not cli.is_file():
45
+ raise RuntimeError(
46
+ f"source-checkout native binary pair is unavailable at {directory}; "
47
+ "run cargo build --locked --workspace --bins from this checkout; "
48
+ "for a custom target directory explicitly set AGENTIC_WORKSPACE_CORE_BINARY to its paired core"
49
+ )
50
+ if not path.is_file():
51
+ raise RuntimeError(
52
+ "shared Agentic Workspace core is unavailable; install the native package or explicitly set AGENTIC_WORKSPACE_CORE_BINARY for development"
53
+ )
54
+ _verify(path, "sha256")
55
+ return path
56
+
57
+
58
+ def cli_binary() -> Path:
59
+ path = core_binary().with_name("agentic-workspace.exe" if os.name == "nt" else "agentic-workspace")
60
+ if not path.is_file():
61
+ raise RuntimeError("paired native Agentic Workspace CLI is unavailable")
62
+ _verify(path, "cli_sha256")
63
+ return path
@@ -0,0 +1,113 @@
1
+ """Thin Codex app-server bridge for the native owner's sealed worker protocol.
2
+
3
+ Core owns packet validation, return identity, attempts and integration. This
4
+ adapter owns only provider discovery and one fresh read-only host turn.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import json
11
+ import subprocess
12
+ import sys
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from agentic_workspace import codex_provider as native_transport
17
+ from agentic_workspace.native_core import core_binary
18
+
19
+
20
+ def assignment_packet(context: dict[str, Any]) -> dict[str, Any]:
21
+ """Forward packet admission to the paired core; never interpret it in Python."""
22
+ result = subprocess.run(
23
+ [str(core_binary())],
24
+ input=json.dumps({"assignment_packet": context}),
25
+ text=True,
26
+ encoding="utf-8",
27
+ capture_output=True,
28
+ check=False,
29
+ )
30
+ if result.returncode:
31
+ raise ValueError(result.stderr.strip())
32
+ return json.loads(result.stdout)
33
+
34
+
35
+ def capability(root: Path, parameters: dict[str, Any]) -> dict[str, Any]:
36
+ """Fresh adapter facts only: no worker turn, session or retained observation."""
37
+ facts: dict[str, Any] = {
38
+ "kind": "agentic-workspace/host-transport-capability/v1",
39
+ "parameters": parameters,
40
+ "adapter_revision": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
41
+ }
42
+ try:
43
+ snapshot = native_transport.discover(root, refresh=True, persist=False)
44
+ facts["provider_revision"] = snapshot["revision"]
45
+ native_transport.validate_selection(
46
+ snapshot, {"mode": "fresh", "parameters": parameters, "capability_revision": snapshot["revision"]}
47
+ )
48
+ facts.update(status="available", reason="current-protocol-and-model", result_classes=["read-only", "unapplied-patch"])
49
+ except native_transport.ProviderError as error:
50
+ reason = str(error)
51
+ # Only settled adapter facts establish unavailability. Discovery errors,
52
+ # quota, timeouts and unknown protocol remain unresolved, never local fit.
53
+ unavailable = reason in {
54
+ "native-adapter-executable-unavailable",
55
+ "native-model-unavailable",
56
+ "native-continuity-unavailable",
57
+ "native-parameter-unsupported",
58
+ "native-ephemeral-continuity-unavailable",
59
+ }
60
+ facts.update(status="unavailable" if unavailable else "unknown", reason=reason, result_classes=[])
61
+ except (OSError, ValueError, KeyError, subprocess.SubprocessError):
62
+ facts.update(status="unknown", reason="host-capability-discovery-failed", result_classes=[])
63
+ facts["revision"] = native_transport.digest(facts)
64
+ return facts
65
+
66
+
67
+ def dispatch(root: Path, packet: dict[str, Any]) -> dict[str, Any]:
68
+ entry = assignment_packet({"action": "entry", "packet": packet})
69
+ configuration = packet["assignment_identity"]["current_assignment"]["selected"]["configuration"]
70
+ adapter = configuration["execution"]["adapter"]
71
+ if adapter.get("adapter") != "codex-app-server/v1":
72
+ raise ValueError("unsupported sealed host adapter")
73
+ parameters = adapter["parameters"]
74
+ if configuration["execution"].get("host_capability") != capability(root, parameters):
75
+ raise ValueError("host capability changed; resolve current Assignment before dispatch")
76
+ snapshot = native_transport.discover(root, refresh=True)
77
+ if snapshot["revision"] != configuration["execution"]["host_capability"]["provider_revision"]:
78
+ raise ValueError("host capability changed; resolve current Assignment before dispatch")
79
+ selection = {"mode": "fresh", "parameters": parameters, "capability_revision": snapshot["revision"]}
80
+ native_transport.validate_selection(snapshot, selection)
81
+ # Expand the bounded captured capsule mechanically, never from the filesystem.
82
+ prompt = json.dumps({"worker": entry, "captured_inputs": packet["assignment_identity"]["input_capsule"]})
83
+ schema = {
84
+ "type": "object",
85
+ "additionalProperties": False,
86
+ "properties": {
87
+ "summary": {"type": "string"},
88
+ "changed_paths": {"type": "array", "items": {"type": "string"}},
89
+ "patch": {"type": "string"},
90
+ "stop_conditions_hit": {"type": "array", "items": {"type": "string"}},
91
+ "result_delivery": {"const": "unapplied-patch"},
92
+ },
93
+ "required": ["summary", "changed_paths", "patch", "stop_conditions_hit", "result_delivery"],
94
+ }
95
+ result = native_transport.execute(root, snapshot, selection, prompt, schema, timeout=adapter["timeout_seconds"])
96
+ returned = assignment_packet({"action": "return", "packet": packet, "material": result["returned_work"]})
97
+ return next(
98
+ request["arguments"]["returned"]
99
+ for request in returned["reentry"]["request"]
100
+ if request["request_kind"] in {"assignment/observe-readonly-return/v1", "assignment/observe-patch-return/v1"}
101
+ )
102
+
103
+
104
+ def main() -> None:
105
+ if sys.argv[1:] == ["--aw-capability"]:
106
+ print(json.dumps(capability(Path.cwd(), json.load(sys.stdin))))
107
+ return
108
+ packet = json.load(sys.stdin)
109
+ print(json.dumps(dispatch(Path.cwd(), packet)))
110
+
111
+
112
+ if __name__ == "__main__":
113
+ main()
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.5
2
+ Name: agentic-workspace
3
+ Version: 1.0.0rc3
4
+ Summary: Repo-native continuity and execution layer for agents, combining workspace routing, active planning, and durable memory.
5
+ Project-URL: Homepage, https://github.com/rickardvh/agentic-workspace
6
+ Project-URL: Repository, https://github.com/rickardvh/agentic-workspace
7
+ Project-URL: Issues, https://github.com/rickardvh/agentic-workspace/issues
8
+ Project-URL: Support, https://github.com/rickardvh/agentic-workspace/issues
9
+ Project-URL: Documentation, https://github.com/rickardvh/agentic-workspace/blob/master/docs/agentic-workspace-install.md
10
+ Author: Rickard von Haugwitz
11
+ Maintainer: Rickard von Haugwitz
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: agents,developer-tools,memory,planning,repository,workflow
15
+ Classifier: Development Status :: 3 - Alpha
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Software Development :: Build Tools
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Agentic Workspace
27
+
28
+ **Persistent operating context and dynamic control for coding agents.**
29
+
30
+ Agentic Workspace (AW) helps coding agents enter a repository with the right guidance, continue unfinished work across sessions, and preserve useful lessons without turning every task into a workflow.
31
+
32
+ It builds on repository instructions and skills with persistent, source-owned context and a small Rust-backed tool surface for exact current information and bounded operations. Keep your existing agent, editor, source tree, tests, and review process; AW connects them to the operating context that matters for the task at hand.
33
+
34
+ [Get started](#get-started) · [Everyday use](docs/everyday-use.md) · [Documentation](docs/index.md) · [Releases](https://github.com/rickardvh/agentic-workspace/releases)
35
+
36
+ ## Why AW?
37
+
38
+ A repository instruction file can explain how to work in a project. By itself, it cannot tell a new session where a multi-step task stopped, which retained lesson is relevant now, whether earlier verification still supports a claim, or which deeper procedure is worth loading for this change.
39
+
40
+ As work spans sessions and agents, that context has to come from somewhere. Without a deliberate home, it tends to disappear into chat, duplicate into prose, or be reconstructed from source, issues, and history.
41
+
42
+ AW keeps only the operating context whose availability can materially change agent behavior, then makes the relevant part cheap to reach:
43
+
44
+ | Need | What AW provides |
45
+ | --- | --- |
46
+ | Find the right guidance | Task-relevant instructions, source references, and reusable skills. |
47
+ | Continue interrupted work | Retained outcomes, constraints, accepted progress, blockers, and next actions. |
48
+ | Know what to verify | Relevant checking procedures, recorded evidence, and visible gaps. |
49
+ | Hand work to another agent | Bounded assignments with explicit context, constraints, and return expectations. |
50
+ | Avoid repeated rediscovery | Useful lessons and corrections retained with the appropriate owner. |
51
+
52
+ The goal is less repeated explanation, searching, handoff reconstruction, and repair—not a larger prompt or a new workflow to manage.
53
+
54
+ **Small tasks stay small.** A typo fix does not need Planning, Memory, Verification, delegation, or another artifact merely because those capabilities are available.
55
+
56
+ ## What using it looks like
57
+
58
+ Imagine an API change that spans two sessions:
59
+
60
+ > Add pagination to the users API without breaking existing clients.
61
+
62
+ In a repository with API guidance and verification procedures configured, the agent can use AW to find the relevant contract, load a useful implementation procedure, and identify the checks expected for the change. It still reads the source, reasons about the design, and implements with its ordinary tools.
63
+
64
+ If work stops partway through, Planning can preserve the intended outcome, accepted progress, unresolved questions, and next action.
65
+
66
+ In a later session:
67
+
68
+ > Continue the pagination work.
69
+
70
+ The next agent can recover that continuation rather than reconstructing the previous conversation. Changed assumptions and missing evidence remain things to check; a recorded result is not automatically fresh proof.
71
+
72
+ The same principle applies to a handoff: preserve enough for the receiving agent to do bounded work without copying the entire parent session. Returned work still needs appropriate integration and verification.
73
+
74
+ [See everyday examples →](docs/everyday-use.md)
75
+
76
+ ## How it works
77
+
78
+ **Skills teach procedure. Repository instructions and configuration set policy. Domain owners hold current state and evidence. Tools provide exact current information and controlled operations. The agent supplies judgment.**
79
+
80
+ A small repository entry point leads the agent to AW's canonical `workspace-startup` skill. That skill explains how to reach relevant sources, ask the runtime for current facts when they matter, and load specialized procedures only when useful.
81
+
82
+ The underlying model is deliberately small:
83
+
84
+ ```text
85
+ Find the relevant context → Do the work → Update what matters
86
+ ```
87
+
88
+ Internally, AW resolves a compact operating contract, exposes supported actions, and reconciles their consequences afterward. That machinery is a substrate for the agent, not a phase machine the user has to operate around every task.
89
+
90
+ Source code, documentation, tests, decisions, and other canonical repository material remain in their existing homes. AW routes to those sources rather than importing the repository into a second knowledge system.
91
+
92
+ [Product model](docs/package/overview.md) · [Architecture](docs/architecture.md)
93
+
94
+ ## Get started
95
+
96
+ Use the [installation and adoption guide](docs/agentic-workspace-install.md) for the release class you intend to run. Stable support, release candidates, and previews have different evidence and support boundaries; exact install commands and platform claims belong to the selected release and its receipts rather than this README.
97
+
98
+ For support-bearing installs, follow the [generated install projection](docs/reference/support-bearing-install.md) to the selected release's `distribution-install-readiness.json` receipt and its exact installation command.
99
+
100
+ ### Adopt a repository
101
+
102
+ Installing the runtime and adopting a repository are separate operations. After installing the selected artifact, run ordinary `start` against the target Git repository:
103
+
104
+ ```bash
105
+ agentic-workspace start --target . \
106
+ --task "Inspect this repository" \
107
+ --format json
108
+ ```
109
+
110
+ If the repository is not yet adopted and the selected artifact includes the current adoption owner, Configuration returns the exact repository-adoption request. Follow that request and execute only the returned authorized action. Adoption establishes the small package-owned host footprint and managed `AGENTS.md` fence; it does **not** invent repository policy, choose optional modules, or create Planning, Memory, or Verification state.
111
+
112
+ Historical `init`/setup command families are not the v1 adoption model.
113
+
114
+ ### In an adopted repository
115
+
116
+ The repository's small entry point leads to the canonical `workspace-startup` skill. Agent hosts with supported native skill discovery can expose the same skill directly.
117
+
118
+ For an initial check, ask your agent:
119
+
120
+ > Use this repository's Agentic Workspace setup to identify the guidance and checks relevant to an API change. Do not modify anything yet.
121
+
122
+ For direct inspection, use the same `start` boundary with the actual task. After adoption, continue giving your agent ordinary work; the canonical skill teaches it when AW is useful, so you should not need to run a manual command sequence around every change.
123
+
124
+ [Installation and adoption](docs/agentic-workspace-install.md) · [Everyday use](docs/everyday-use.md) · [CLI reference](docs/reference/cli-catalogue.md)
125
+
126
+ ## Your repository, your rules
127
+
128
+ AW is designed to fit around the project rather than reorganize it.
129
+
130
+ A simplified host repository looks like this:
131
+
132
+ ```text
133
+ your-repository/
134
+ ├── AGENTS.md # Small managed entry point inside a repo-owned file
135
+ ├── src/, docs/, tests/ # Existing project contents
136
+ └── .agentic-workspace/ # Package integration plus optional owner state
137
+ ```
138
+
139
+ The current public adoption footprint is Configuration-owned and deliberately small. Package-managed skills, ownership/read-profile metadata, provenance, and adoption identity stay distinct from repo-owned configuration, module-owned state, local data, and promoted output.
140
+
141
+ You can express a repository-wide correction in ordinary language:
142
+
143
+ > For this repository, read the API contract before changing public response fields.
144
+
145
+ Or make the scope explicitly local:
146
+
147
+ > Only on this machine, use this executable path when invoking AW.
148
+
149
+ The agent can route the change to the appropriate instruction or configuration owner and verify whether it was retained. A promise in chat is not a saved rule, and a local preference should not silently become shared repository policy.
150
+
151
+ Reusable methods belong in skills. Binding rules belong in instructions or configuration. Current work and evidence stay with the capabilities responsible for them.
152
+
153
+ The same Configuration-owned footprint is used for adoption, refresh, and removal. De-adoption removes only authenticated package-owned integration, preserves repo-owned configuration and domain state, and refuses to erase edited or unowned content merely because it sits under an AW path.
154
+
155
+ [Configuration reference](docs/reference/workspace-config.md) · [Repository footprint](docs/reference/installed-surface-catalogue.md)
156
+
157
+ ## Add structure where it helps
158
+
159
+ AW includes optional first-party capabilities for recurring coordination costs:
160
+
161
+ **Planning** preserves execution continuity: intended outcome, constraints, progress, blockers, and next action.
162
+
163
+ **Memory** retains useful observations and lessons that would otherwise be expensive to rediscover. Advice can be reconsidered when its dependencies change; a note is not automatically policy.
164
+
165
+ **Verification** makes checking procedures, evidence, and known gaps reusable. Passing a check supports the claim it actually tests, not every claim about the finished work.
166
+
167
+ These are peer capabilities, not mandatory workflow stages. Repositories can use what repays its cost and leave unrelated capabilities out of the current task's context.
168
+
169
+ Repository-specific rules and procedures do not require a new module. Modules are for independently reusable capabilities with their own domain state or operations.
170
+
171
+ [Modules and extensions →](docs/package/modules.md)
172
+
173
+ ## Works with agents rather than replacing them
174
+
175
+ Agentic Workspace is not an autonomous coding agent. It gives the agent already working in the repository better operating context and safer repository-native control.
176
+
177
+ An agent with runtime access can query current owner state and use supported operations. A repository-only reviewer can follow the same canonical skill and generated read profile to recover relevant recorded intent, constraints, progress, and advice, while live machine state, fresh proof, and effect permission remain unknown.
178
+
179
+ The ordinary deterministic product semantics live in a shared Rust core. Native, Python, TypeScript, and JSON-facing surfaces project or bind that authority rather than defining separate workflow behavior. The host project's implementation language does not need to match AW's runtime implementation.
180
+
181
+ Host integrations still differ. Provider independence does not mean every agent host discovers skills identically or every model follows repository guidance perfectly; current support and evidence remain release- and environment-bound.
182
+
183
+ [Evidence and support →](docs/evidence-and-support.md)
184
+
185
+ ## Trust and support
186
+
187
+ **AW is not a sandbox.** Repository-configured commands run with the caller's filesystem and credential authority. Review the repository and its execution routes before allowing them to run. Credentials belong in the host or environment's credential facilities, not checked-in AW state.
188
+
189
+ AW's operation boundaries and verification support do not replace human judgment, independent review, or existing security controls.
190
+
191
+ Exact package identities, installation commands, runtime versions, operating-system support, and prerelease/stable status are deliberately kept in release-bound or generated owners instead of copied into this landing page.
192
+
193
+ [Installation and adoption](docs/agentic-workspace-install.md) · [Threat model](docs/security/threat-model.md) · [Evidence and support](docs/evidence-and-support.md)
194
+
195
+ ## Learn more
196
+
197
+ **Use AW:** [Installation and adoption](docs/agentic-workspace-install.md) · [Everyday use](docs/everyday-use.md) · [Configuration](docs/reference/workspace-config.md)
198
+
199
+ **Understand AW:** [Product overview](docs/package/overview.md) · [Architecture](docs/architecture.md) · [Design principles](docs/design-principles.md) · [Modules](docs/package/modules.md)
200
+
201
+ **Look up details:** [CLI reference](docs/reference/cli-catalogue.md) · [Installed surfaces](docs/reference/installed-surface-catalogue.md) · [Full documentation](docs/index.md)
202
+
203
+ ## Contributing
204
+
205
+ For changes to Agentic Workspace itself, start with the [contributor playbook](docs/maintainer/contributor-playbook.md). Use the repository's [issue templates](https://github.com/rickardvh/agentic-workspace/issues/new/choose) to report a problem or propose a change.
206
+
207
+ Contributions should make useful work easier to complete, continue, or verify while keeping unnecessary context, framework surface, and repository residue low.
208
+
209
+ ## License
210
+
211
+ [MIT](LICENSE).
@@ -0,0 +1,14 @@
1
+ agentic_workspace/cli.py,sha256=Cv-6qEhAGBdAqapqhCVvaJprnKMHWO7o69qPzLP08w4,520
2
+ agentic_workspace/codex_provider.py,sha256=RiP5C1yHkoaXqz_e1UL66sN1oVqrAzxAsR6vmGd_l8I,23003
3
+ agentic_workspace/native_core.py,sha256=bvOZGQOdXDqMI4814WESC0dn9EPzdOzGnC01D8onpDI,2815
4
+ agentic_workspace/sealed_codex_transport.py,sha256=n8rs15OXA4-3MTQtq3hnb7XETZosCPPzjUTKWaHMVBM,5192
5
+ agentic_workspace/__init__.py,sha256=REtU4aBp-goYOOX9z8_QroPiDO7S86rfInsibeSUNs8,410
6
+ agentic_workspace/_binding.py,sha256=gfyyjByLQHKoksg04FcH5w7iSCEBMb4gUCqdQaTFTOQ,2797
7
+ agentic_workspace/_native/agentic-workspace-core.exe,sha256=XENuhpI_uJR77k5_HD2-R4uSV6g0qrniqVJIuxE_z2c,18485760
8
+ agentic_workspace/_native/agentic-workspace.exe,sha256=R5KYPVFINdsiuL64z5xBYskr2e9fplrXDu2KGusQC-k,501248
9
+ agentic_workspace/_native/artifact.json,sha256=jvQ0JFr2mnmnvXfUfo-RvX9k1ADiLU1-tcGECcaLB4M,708
10
+ agentic_workspace-1.0.0rc3.dist-info/METADATA,sha256=m1wGW__UD5ZusXiPG9IA7sASXGb4JP9zASxF3GNkJYQ,13793
11
+ agentic_workspace-1.0.0rc3.dist-info/WHEEL,sha256=OA-gEgWbLnh0Tf6JrLOMFR4vOWY4QbBMSZuN6osy-Q0,94
12
+ agentic_workspace-1.0.0rc3.dist-info/entry_points.txt,sha256=3M_M5tF8MeVr98DcZBMvXFmOmXyrBrpKvdibWQ_lY6g,65
13
+ agentic_workspace-1.0.0rc3.dist-info/licenses/LICENSE,sha256=PE4wqbGPdD5Z1Y158xpSdRFzpEXdacvSzwJf7AD7PTQ,1077
14
+ agentic_workspace-1.0.0rc3.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-win_amd64
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ agentic-workspace = agentic_workspace.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rickard von Haugwitz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.