specsmith 0.3.0.dev136__py3-none-any.whl → 0.3.0.dev139__py3-none-any.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.
- specsmith/agent/runner.py +70 -17
- specsmith/cli.py +9 -23
- {specsmith-0.3.0.dev136.dist-info → specsmith-0.3.0.dev139.dist-info}/METADATA +1 -1
- {specsmith-0.3.0.dev136.dist-info → specsmith-0.3.0.dev139.dist-info}/RECORD +8 -8
- {specsmith-0.3.0.dev136.dist-info → specsmith-0.3.0.dev139.dist-info}/WHEEL +0 -0
- {specsmith-0.3.0.dev136.dist-info → specsmith-0.3.0.dev139.dist-info}/entry_points.txt +0 -0
- {specsmith-0.3.0.dev136.dist-info → specsmith-0.3.0.dev139.dist-info}/licenses/LICENSE +0 -0
- {specsmith-0.3.0.dev136.dist-info → specsmith-0.3.0.dev139.dist-info}/top_level.txt +0 -0
specsmith/agent/runner.py
CHANGED
|
@@ -184,6 +184,7 @@ class AgentRunner:
|
|
|
184
184
|
max_tool_iterations: int = 10,
|
|
185
185
|
optimize: bool = False,
|
|
186
186
|
optimization_config: OptimizationConfig | None = None,
|
|
187
|
+
json_events: bool = False,
|
|
187
188
|
) -> None:
|
|
188
189
|
self.project_dir = str(Path(project_dir).resolve())
|
|
189
190
|
self._provider_name = provider_name
|
|
@@ -191,6 +192,7 @@ class AgentRunner:
|
|
|
191
192
|
self._tier = tier
|
|
192
193
|
self._stream = stream
|
|
193
194
|
self._max_iterations = max_tool_iterations
|
|
195
|
+
self._json_events = json_events
|
|
194
196
|
|
|
195
197
|
self._provider: Any = None
|
|
196
198
|
self._state = SessionState(project_dir=self.project_dir)
|
|
@@ -291,7 +293,10 @@ class AgentRunner:
|
|
|
291
293
|
except Exception as e: # noqa: BLE001
|
|
292
294
|
error_msg = f"[Provider error] {e}"
|
|
293
295
|
if not silent:
|
|
294
|
-
self.
|
|
296
|
+
if self._json_events:
|
|
297
|
+
self._emit_event(type="error", message=error_msg)
|
|
298
|
+
else:
|
|
299
|
+
self._print(error_msg)
|
|
295
300
|
return error_msg
|
|
296
301
|
|
|
297
302
|
# Update credit tracking
|
|
@@ -299,6 +304,15 @@ class AgentRunner:
|
|
|
299
304
|
self._state.total_output_tokens += response.output_tokens
|
|
300
305
|
self._state.total_cost_usd += response.estimated_cost_usd
|
|
301
306
|
|
|
307
|
+
# Emit token update event
|
|
308
|
+
if self._json_events and not silent:
|
|
309
|
+
self._emit_event(
|
|
310
|
+
type="tokens",
|
|
311
|
+
in_tokens=self._state.total_input_tokens,
|
|
312
|
+
out_tokens=self._state.total_output_tokens,
|
|
313
|
+
cost_usd=self._state.total_cost_usd,
|
|
314
|
+
)
|
|
315
|
+
|
|
302
316
|
if response.content:
|
|
303
317
|
final_response = response.content
|
|
304
318
|
|
|
@@ -330,6 +344,14 @@ class AgentRunner:
|
|
|
330
344
|
)
|
|
331
345
|
)
|
|
332
346
|
|
|
347
|
+
# Emit turn-done event
|
|
348
|
+
if self._json_events and not silent:
|
|
349
|
+
self._emit_event(
|
|
350
|
+
type="turn_done",
|
|
351
|
+
total_tokens=self._state.session_tokens,
|
|
352
|
+
cost_usd=self._state.total_cost_usd,
|
|
353
|
+
)
|
|
354
|
+
|
|
333
355
|
return final_response
|
|
334
356
|
|
|
335
357
|
def _call_provider(self, messages: list[Message], silent: bool = False) -> CompletionResponse:
|
|
@@ -362,7 +384,7 @@ class AgentRunner:
|
|
|
362
384
|
messages = hint.messages
|
|
363
385
|
tools = hint.tools
|
|
364
386
|
|
|
365
|
-
use_stream = self._stream and not silent and not tools
|
|
387
|
+
use_stream = self._stream and not silent and not tools and not self._json_events
|
|
366
388
|
if use_stream:
|
|
367
389
|
accumulated = ""
|
|
368
390
|
for token in provider.stream(messages, tools=None):
|
|
@@ -375,7 +397,10 @@ class AgentRunner:
|
|
|
375
397
|
else:
|
|
376
398
|
response = cast(CompletionResponse, provider.complete(messages, tools=tools))
|
|
377
399
|
if not silent and response.content:
|
|
378
|
-
self.
|
|
400
|
+
if self._json_events:
|
|
401
|
+
self._emit_event(type="llm_chunk", text=response.content)
|
|
402
|
+
else:
|
|
403
|
+
self._print(response.content)
|
|
379
404
|
|
|
380
405
|
# ── Optimization post-call ───────────────────────────────────────────
|
|
381
406
|
if self._optimizer is not None and hint is not None:
|
|
@@ -405,7 +430,10 @@ class AgentRunner:
|
|
|
405
430
|
inputs = tc.get("input", {})
|
|
406
431
|
|
|
407
432
|
if not silent:
|
|
408
|
-
self.
|
|
433
|
+
if self._json_events:
|
|
434
|
+
self._emit_event(type="tool_started", name=name, args=inputs)
|
|
435
|
+
else:
|
|
436
|
+
self._print(f"\n[Tool: {name}]")
|
|
409
437
|
|
|
410
438
|
# Fire pre_tool hooks
|
|
411
439
|
pre_ctx = HookContext(
|
|
@@ -451,9 +479,12 @@ class AgentRunner:
|
|
|
451
479
|
elapsed = time.time() * 1000 - start_ms
|
|
452
480
|
|
|
453
481
|
if not silent:
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
482
|
+
if self._json_events:
|
|
483
|
+
self._emit_event(type="tool_finished", name=name, result=output, is_error=error)
|
|
484
|
+
else:
|
|
485
|
+
# Truncate long outputs for display
|
|
486
|
+
display = output[:500] + "..." if len(output) > 500 else output
|
|
487
|
+
self._print(f" → {display}")
|
|
457
488
|
|
|
458
489
|
tr = ToolResult(
|
|
459
490
|
tool_name=name,
|
|
@@ -554,13 +585,23 @@ class AgentRunner:
|
|
|
554
585
|
def _print_banner(self) -> None:
|
|
555
586
|
provider = getattr(self._provider, "provider_name", "?")
|
|
556
587
|
model = getattr(self._provider, "model", "?")
|
|
557
|
-
self.
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
588
|
+
if self._json_events:
|
|
589
|
+
self._emit_event(
|
|
590
|
+
type="ready",
|
|
591
|
+
provider=provider,
|
|
592
|
+
model=model,
|
|
593
|
+
project_dir=self.project_dir,
|
|
594
|
+
tools=len(self._tools),
|
|
595
|
+
skills=len(self._skills),
|
|
596
|
+
)
|
|
597
|
+
else:
|
|
598
|
+
self._print(
|
|
599
|
+
f"\n[specsmith agent — AEE-integrated]"
|
|
600
|
+
f"\n Project: {self.project_dir}"
|
|
601
|
+
f"\n Provider: {provider}/{model}"
|
|
602
|
+
f"\n Tools: {len(self._tools)} | Skills: {len(self._skills)}"
|
|
603
|
+
f"\n Type /help for commands, exit to quit\n"
|
|
604
|
+
)
|
|
564
605
|
|
|
565
606
|
def _print_status(self) -> None:
|
|
566
607
|
self._print(
|
|
@@ -594,10 +635,22 @@ class AgentRunner:
|
|
|
594
635
|
def _prompt(self) -> str:
|
|
595
636
|
"""Read a line of input from stdin."""
|
|
596
637
|
try:
|
|
597
|
-
|
|
638
|
+
# In json_events mode suppress the prompt string so stdout stays pure JSON
|
|
639
|
+
return input("" if self._json_events else "\n> ")
|
|
598
640
|
except EOFError:
|
|
599
641
|
raise
|
|
600
642
|
|
|
601
643
|
def _print(self, text: str = "", end: str = "\n", flush: bool = False) -> None:
|
|
602
|
-
"""Print to stdout."""
|
|
603
|
-
|
|
644
|
+
"""Print to stdout; in json_events mode emit a system event instead."""
|
|
645
|
+
if self._json_events:
|
|
646
|
+
if text.strip():
|
|
647
|
+
self._emit_event(type="system", message=text.strip())
|
|
648
|
+
else:
|
|
649
|
+
print(text, end=end, flush=flush)
|
|
650
|
+
|
|
651
|
+
def _emit_event(self, **kwargs: Any) -> None:
|
|
652
|
+
"""Emit a newline-delimited JSON event to stdout (json_events mode only)."""
|
|
653
|
+
import json
|
|
654
|
+
import sys
|
|
655
|
+
|
|
656
|
+
print(json.dumps(kwargs), flush=True, file=sys.stdout) # noqa: T201
|
specsmith/cli.py
CHANGED
|
@@ -1809,29 +1809,6 @@ def plugin_list() -> None:
|
|
|
1809
1809
|
console.print(f"\n {len(plugins)} plugin(s)")
|
|
1810
1810
|
|
|
1811
1811
|
|
|
1812
|
-
# ---------------------------------------------------------------------------
|
|
1813
|
-
# Serve (API for React dashboard)
|
|
1814
|
-
# ---------------------------------------------------------------------------
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
@main.command()
|
|
1818
|
-
@click.option("--port", default=8910, help="Port to serve on.")
|
|
1819
|
-
def serve(port: int) -> None:
|
|
1820
|
-
"""Start local API server for the web dashboard."""
|
|
1821
|
-
console.print(f"[bold]Starting specsmith API server on port {port}...[/bold]")
|
|
1822
|
-
console.print("[yellow]Not yet implemented. See issue #14.[/yellow]")
|
|
1823
|
-
console.print(
|
|
1824
|
-
"\nPlanned endpoints:\n"
|
|
1825
|
-
" GET /api/projects \u2014 list governed projects\n"
|
|
1826
|
-
" POST /api/projects/init \u2014 scaffold new project\n"
|
|
1827
|
-
" POST /api/projects/import \u2014 import existing project\n"
|
|
1828
|
-
" GET /api/projects/:id/audit \u2014 run audit\n"
|
|
1829
|
-
" GET /api/projects/:id/export \u2014 generate report\n"
|
|
1830
|
-
" GET /api/types \u2014 list all 30 project types\n"
|
|
1831
|
-
" GET /api/tools/:type \u2014 tool registry for a type"
|
|
1832
|
-
)
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
1812
|
# ---------------------------------------------------------------------------
|
|
1836
1813
|
# Process execution and abort
|
|
1837
1814
|
# ---------------------------------------------------------------------------
|
|
@@ -1955,6 +1932,13 @@ def abort_cmd(pid: int | None, abort_all_flag: bool, project_dir: str) -> None:
|
|
|
1955
1932
|
default=False,
|
|
1956
1933
|
help="Enable token optimization (caching, routing, context trim, tool filtering).",
|
|
1957
1934
|
)
|
|
1935
|
+
@click.option(
|
|
1936
|
+
"--json-events",
|
|
1937
|
+
"json_events",
|
|
1938
|
+
is_flag=True,
|
|
1939
|
+
default=False,
|
|
1940
|
+
help="Emit structured JSONL events to stdout (used by IDE clients like the VS Code extension).",
|
|
1941
|
+
)
|
|
1958
1942
|
def run_cmd(
|
|
1959
1943
|
project_dir: str,
|
|
1960
1944
|
task: str,
|
|
@@ -1963,6 +1947,7 @@ def run_cmd(
|
|
|
1963
1947
|
tier: str,
|
|
1964
1948
|
no_stream: bool,
|
|
1965
1949
|
optimize: bool,
|
|
1950
|
+
json_events: bool,
|
|
1966
1951
|
) -> None:
|
|
1967
1952
|
"""Start the AEE-integrated agentic client REPL.
|
|
1968
1953
|
|
|
@@ -1994,6 +1979,7 @@ def run_cmd(
|
|
|
1994
1979
|
tier=tier_map[tier],
|
|
1995
1980
|
stream=not no_stream,
|
|
1996
1981
|
optimize=optimize,
|
|
1982
|
+
json_events=json_events,
|
|
1997
1983
|
)
|
|
1998
1984
|
if task:
|
|
1999
1985
|
result = runner.run_task(task)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.3.0.
|
|
3
|
+
Version: 0.3.0.dev139
|
|
4
4
|
Summary: Applied Epistemic Engineering toolkit — forge epistemically-governed scaffolds, stress-test belief systems, and run AEE pipelines.
|
|
5
5
|
Author: BitConcepts
|
|
6
6
|
License-Expression: MIT
|
|
@@ -12,7 +12,7 @@ specsmith/__main__.py,sha256=OEU30g2x5ATD1bzoo7iwwj6tST_41sgGnwPIK5dF6ao,189
|
|
|
12
12
|
specsmith/architect.py,sha256=7lOy8PXCYMAmt6TmH25oMTvPTWke_ZC2ekjNjcuD9Zg,5439
|
|
13
13
|
specsmith/auditor.py,sha256=KikmeKRdADfIAyq--zdzfn2uiZQQ-CKuHWj5WNqB5i0,23477
|
|
14
14
|
specsmith/auth.py,sha256=MUvKbP7OdyH2hIYAuDS4sB-2U7xY-8zayAP5bwzLUYU,8452
|
|
15
|
-
specsmith/cli.py,sha256=
|
|
15
|
+
specsmith/cli.py,sha256=F-EoUB6eC4fLRlrzIeI3xp-nGfJ2XHKbmd94CO2UH6Q,112383
|
|
16
16
|
specsmith/compressor.py,sha256=IqMF6WZMbqyA0KLQmBxRyM6xZF0g34-9lQxWcYz3tFs,4448
|
|
17
17
|
specsmith/config.py,sha256=7qUer8YiV_PqR68-Yrwd1ZLpSBtai-K9r7N_B6BgQyw,11243
|
|
18
18
|
specsmith/credit_analyzer.py,sha256=InobNXNzgM6dafYopKPJ7Vmod0M6wjv-TsXbxi36SbY,7052
|
|
@@ -41,7 +41,7 @@ specsmith/agent/__init__.py,sha256=BeUjS1HNKV9Ks9sTBq1Ju1CaL0TGnxuGKVTrJ-LlMXQ,1
|
|
|
41
41
|
specsmith/agent/core.py,sha256=RkctGfIxhutVjHhXGJISxIXGP29Tt_plmmwWcf2RVBQ,5968
|
|
42
42
|
specsmith/agent/hooks.py,sha256=GV6x_zOhF_xRCz5ipAqHH2eMTwi8MdwY1IV08m9hUto,7646
|
|
43
43
|
specsmith/agent/optimizer.py,sha256=RA51-yMxw4GOxkC7Ip8Tbrjmsx_USJyASkShZ1SYd7c,33598
|
|
44
|
-
specsmith/agent/runner.py,sha256=
|
|
44
|
+
specsmith/agent/runner.py,sha256=KQZ54FxDpExasw-YQks-D3zxs6nUET9dLQ_Dsm-PBZo,24844
|
|
45
45
|
specsmith/agent/skills.py,sha256=vGs4DkOgZwFi6PiwzDyrr8GmfhwuAReLaiO_V5LhNNg,6375
|
|
46
46
|
specsmith/agent/tools.py,sha256=yaOcZ_rhwFVvhuW-KmQDEz_ODX-2ONgXo1Z_mCvhPpQ,25036
|
|
47
47
|
specsmith/agent/profiles/epistemic-auditor.md,sha256=4MCwBbmauL9YdSPR6PQLxcCVM-JH_wvYUMzJIlQ_84E,2824
|
|
@@ -132,9 +132,9 @@ specsmith/vcs/base.py,sha256=IzyC3xtO8npaNbGZoAzgQyc9iSKleJFnp4GA0otTJfU,4098
|
|
|
132
132
|
specsmith/vcs/bitbucket.py,sha256=U5cGqpybuPngjpu5GDM8aTZPm9bvZf1099OSBwU_Lro,5014
|
|
133
133
|
specsmith/vcs/github.py,sha256=OVTLs9egmM_Smrxm4-Lo525Pi59x0PDMOOwqNNiM7lA,11995
|
|
134
134
|
specsmith/vcs/gitlab.py,sha256=fDYYoDBic0KBnD_QWYf_K3GmdZOEartLXhzNXf4D4-0,5136
|
|
135
|
-
specsmith-0.3.0.
|
|
136
|
-
specsmith-0.3.0.
|
|
137
|
-
specsmith-0.3.0.
|
|
138
|
-
specsmith-0.3.0.
|
|
139
|
-
specsmith-0.3.0.
|
|
140
|
-
specsmith-0.3.0.
|
|
135
|
+
specsmith-0.3.0.dev139.dist-info/licenses/LICENSE,sha256=jCLkf20ZMVU47ykbfIxw-5eukhnaeIsxSflw07hVNlY,1074
|
|
136
|
+
specsmith-0.3.0.dev139.dist-info/METADATA,sha256=KnB2tUASyVviARwHBy1SFIXWV-v0c_gcALYlkQsnwsU,14116
|
|
137
|
+
specsmith-0.3.0.dev139.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
138
|
+
specsmith-0.3.0.dev139.dist-info/entry_points.txt,sha256=X_zb8_KvBONFGgkQYKIbW1UtWd3Ck7E11l3SyHO7omA,49
|
|
139
|
+
specsmith-0.3.0.dev139.dist-info/top_level.txt,sha256=wu2UpLt3wnh0TwSBmoox7kOMoGfzD6mUs22ihAcEx4E,20
|
|
140
|
+
specsmith-0.3.0.dev139.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|