autoforge-agent 0.4.0__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. autoforge_agent-0.4.2/MANIFEST.in +10 -0
  2. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/PKG-INFO +2 -1
  3. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/cli.py +6 -2
  4. autoforge_agent-0.4.2/autoforge/web/static/index.html +406 -0
  5. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/webtools.py +12 -7
  6. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge_agent.egg-info/PKG-INFO +2 -1
  7. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge_agent.egg-info/SOURCES.txt +16 -0
  8. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge_agent.egg-info/requires.txt +1 -0
  9. autoforge_agent-0.4.2/probes/FINDINGS.md +261 -0
  10. autoforge_agent-0.4.2/probes/probe_forge_latency.py +102 -0
  11. autoforge_agent-0.4.2/probes/probe_gateway_context_503.py +60 -0
  12. autoforge_agent-0.4.2/probes/probe_gateway_empty.py +83 -0
  13. autoforge_agent-0.4.2/probes/probe_gateway_max_tokens.py +122 -0
  14. autoforge_agent-0.4.2/probes/probe_gateway_models.py +134 -0
  15. autoforge_agent-0.4.2/probes/probe_gateway_thinking.py +134 -0
  16. autoforge_agent-0.4.2/probes/probe_generator_reply.py +69 -0
  17. autoforge_agent-0.4.2/probes/probe_model_latency.py +66 -0
  18. autoforge_agent-0.4.2/probes/raw_generator_reply.txt +23 -0
  19. autoforge_agent-0.4.2/probes/raw_generator_reply_clean.txt +16 -0
  20. autoforge_agent-0.4.2/pyproject.toml +38 -0
  21. autoforge_agent-0.4.2/tests/_terminal.py +127 -0
  22. autoforge_agent-0.4.2/tests/conftest.py +36 -0
  23. autoforge_agent-0.4.2/tests/mcp_server_fixture.py +176 -0
  24. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_setup.py +15 -0
  25. autoforge_agent-0.4.0/pyproject.toml +0 -24
  26. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/README.md +0 -0
  27. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/__init__.py +0 -0
  28. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/__main__.py +0 -0
  29. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/agent.py +0 -0
  30. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/autonomy/__init__.py +0 -0
  31. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/autonomy/confirm.py +0 -0
  32. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/autonomy/policy.py +0 -0
  33. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/autonomy/roles.py +0 -0
  34. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/autonomy/selfmod.py +0 -0
  35. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/autonomy/spawn.py +0 -0
  36. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/autonomy/topology.py +0 -0
  37. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/browser.py +0 -0
  38. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/configfile.py +0 -0
  39. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/core/__init__.py +0 -0
  40. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/core/agent.py +0 -0
  41. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/core/compaction.py +0 -0
  42. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/core/lineedit.py +0 -0
  43. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/core/llm.py +0 -0
  44. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/core/message.py +0 -0
  45. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/core/steering.py +0 -0
  46. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/cpu/__init__.py +0 -0
  47. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/cpu/kernel.py +0 -0
  48. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/cpu/ops.py +0 -0
  49. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/cpu/probe.py +0 -0
  50. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/cpu/safety.py +0 -0
  51. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/cpu/tune.py +0 -0
  52. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/ecosystem.py +0 -0
  53. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/__init__.py +0 -0
  54. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/adversary.py +0 -0
  55. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/evolution.py +0 -0
  56. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/fuzzer.py +0 -0
  57. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/generator.py +0 -0
  58. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/invariance.py +0 -0
  59. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/metacog.py +0 -0
  60. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/pipeline.py +0 -0
  61. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/sandbox.py +0 -0
  62. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/validity.py +0 -0
  63. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/forge/verifier.py +0 -0
  64. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/gpu/__init__.py +0 -0
  65. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/gpu/backend_triton.py +0 -0
  66. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/gpu/bench.py +0 -0
  67. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/gpu/kernel.py +0 -0
  68. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/gpu/ops.py +0 -0
  69. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/gpu/probe.py +0 -0
  70. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/gpu/safety.py +0 -0
  71. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/gpu/units.py +0 -0
  72. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/mcp.py +0 -0
  73. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/measure.py +0 -0
  74. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/modes.py +0 -0
  75. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/notify.py +0 -0
  76. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/route/__init__.py +0 -0
  77. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/route/router.py +0 -0
  78. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/schedule.py +0 -0
  79. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/setup_wizard.py +0 -0
  80. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/skills.py +0 -0
  81. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/store.py +0 -0
  82. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/timing.py +0 -0
  83. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/tools/__init__.py +0 -0
  84. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/tools/composition.py +0 -0
  85. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/tools/registry.py +0 -0
  86. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/tools/spec.py +0 -0
  87. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/vision.py +0 -0
  88. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/web/__init__.py +0 -0
  89. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge/web/server.py +0 -0
  90. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge_agent.egg-info/dependency_links.txt +0 -0
  91. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge_agent.egg-info/entry_points.txt +0 -0
  92. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/autoforge_agent.egg-info/top_level.txt +0 -0
  93. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/setup.cfg +0 -0
  94. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_autoforge.py +0 -0
  95. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_browser.py +0 -0
  96. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_cli.py +0 -0
  97. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_compaction.py +0 -0
  98. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_compaction_wiring.py +0 -0
  99. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_cpu.py +0 -0
  100. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_ecosystem.py +0 -0
  101. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_execution_samples.py +0 -0
  102. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_forge_failure_feedback.py +0 -0
  103. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_generator_coercion.py +0 -0
  104. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_host_facts.py +0 -0
  105. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_invariance.py +0 -0
  106. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_json_repair.py +0 -0
  107. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_lineedit.py +0 -0
  108. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_live_progress.py +0 -0
  109. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_llm_client.py +0 -0
  110. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_llm_response_shape.py +0 -0
  111. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_llm_retry.py +0 -0
  112. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_mcp.py +0 -0
  113. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_mcp_http.py +0 -0
  114. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_memory_injection.py +0 -0
  115. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_modes.py +0 -0
  116. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_notify.py +0 -0
  117. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_policy_gates.py +0 -0
  118. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_reach.py +0 -0
  119. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_reasoning_budget.py +0 -0
  120. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_roles.py +0 -0
  121. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_schedule.py +0 -0
  122. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_self_knowledge.py +0 -0
  123. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_skills.py +0 -0
  124. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_steering.py +0 -0
  125. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_topology.py +0 -0
  126. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_v02.py +0 -0
  127. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_validity.py +0 -0
  128. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_vision.py +0 -0
  129. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_web.py +0 -0
  130. {autoforge_agent-0.4.0 → autoforge_agent-0.4.2}/tests/test_web_live.py +0 -0
@@ -0,0 +1,10 @@
1
+ # The sdist ships the test suite (setuptools picks up tests/test*.py on its own),
2
+ # so it has to ship everything that suite imports or reads:
3
+ # - conftest.py and the shared fakes the terminal and MCP tests drive
4
+ # - probes/: the captured generator replies test_json_repair reads by path
5
+ # Without this the tarball carried 39 test modules that could not be collected,
6
+ # plus two that failed on a missing fixture. Found by unpacking the release and
7
+ # running it -- which is now a job in ci.yml, and was briefly a job in
8
+ # tool-market's reproduce workflow before it moved to the repo that can fix it.
9
+ recursive-include tests *.py
10
+ recursive-include probes *.txt *.md *.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autoforge-agent
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Agent framework with verifiable self-made tools (the forge pipeline).
5
5
  Author: Zhang Yangyi
6
6
  License: MIT
@@ -11,6 +11,7 @@ Requires-Dist: requests>=2.28
11
11
  Provides-Extra: dev
12
12
  Requires-Dist: pytest>=7; extra == "dev"
13
13
  Requires-Dist: pytest-xdist; extra == "dev"
14
+ Requires-Dist: pyyaml; extra == "dev"
14
15
 
15
16
  # autoforge
16
17
 
@@ -39,7 +39,7 @@ from dataclasses import replace
39
39
  from pathlib import Path
40
40
  from typing import Any
41
41
 
42
- from . import configfile, setup_wizard
42
+ from . import __version__, configfile, setup_wizard
43
43
  from .agent import ForgeAgent
44
44
  from .autonomy.policy import (CONFIRM_REQUIRED, FULL_FREEDOM, SUPERVISED,
45
45
  AutonomyPolicy)
@@ -1020,7 +1020,11 @@ def build_parser() -> argparse.ArgumentParser:
1020
1020
  p.add_argument("--policy", choices=list(POLICIES),
1021
1021
  help="autonomy preset: full (default) or supervised "
1022
1022
  "(AUTOFORGE_POLICY)")
1023
- p.add_argument("--version", action="version", version="autoforge 0.4.0")
1023
+ # Read, not written down. This said "autoforge 0.4.0" as a literal, which is
1024
+ # the one place a user actually reads the number -- so it was the one place
1025
+ # that would go on reporting the old release after a bump, printing a
1026
+ # version the installed package did not have.
1027
+ p.add_argument("--version", action="version", version=f"autoforge {__version__}")
1024
1028
 
1025
1029
  sub = p.add_subparsers(dest="command")
1026
1030
  sub.add_parser("chat", help="talk to the agent in a REPL")
@@ -0,0 +1,406 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>autoforge harness</title>
7
+ <style>
8
+ :root{
9
+ --bg:#0b0d10; --panel:#12151a; --panel2:#171b21; --line:#22272f;
10
+ --fg:#d8dee6; --dim:#7c8794; --faint:#4d5560;
11
+ --blue:#4c9aff; --cyan:#37d3c4; --amber:#e3b341; --green:#4ec97e;
12
+ --orange:#f0883e; --red:#f25f5c; --purple:#a98bf0; --pink:#e879b9;
13
+ --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
14
+ }
15
+ *{box-sizing:border-box}
16
+ html,body{height:100%;margin:0}
17
+ body{
18
+ background:var(--bg);color:var(--fg);
19
+ font:14px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
20
+ display:flex;flex-direction:column;overflow:hidden;
21
+ }
22
+ button,input,select,textarea{font:inherit;color:inherit}
23
+ button{background:var(--panel2);border:1px solid var(--line);border-radius:6px;
24
+ padding:5px 11px;cursor:pointer;transition:.12s}
25
+ button:hover{border-color:#39414d;background:#1d222a}
26
+ button.primary{background:#1d4ed8;border-color:#2b5fe0;color:#fff}
27
+ button.primary:hover{background:#2559e8}
28
+ button.ghost{background:transparent}
29
+ button:disabled{opacity:.45;cursor:not-allowed}
30
+
31
+ /* ---- top bar ---- */
32
+ header{display:flex;align-items:center;gap:13px;padding:9px 15px;
33
+ border-bottom:1px solid var(--line);background:var(--panel);flex:none}
34
+ .brand{font-weight:650;letter-spacing:-.2px}
35
+ .brand span{color:var(--faint);font-weight:400}
36
+ .badge{font:11px/1 var(--mono);color:var(--dim);background:var(--panel2);
37
+ border:1px solid var(--line);border-radius:20px;padding:4px 9px;white-space:nowrap}
38
+ .spacer{flex:1}
39
+ select{background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:5px 8px}
40
+
41
+ /* ---- layout ---- */
42
+ main{flex:1;display:flex;min-height:0}
43
+ aside{width:225px;flex:none;border-right:1px solid var(--line);
44
+ background:var(--panel);display:flex;flex-direction:column;min-height:0}
45
+ aside h3{margin:0;padding:10px 13px 7px;font-size:11px;letter-spacing:.09em;
46
+ text-transform:uppercase;color:var(--faint);font-weight:600}
47
+ .list{overflow-y:auto;flex:1;padding:0 8px 10px}
48
+ .sess{padding:8px 10px;border-radius:6px;cursor:pointer;margin-bottom:3px;
49
+ border:1px solid transparent}
50
+ .sess:hover{background:var(--panel2)}
51
+ .sess.on{background:#182231;border-color:#24354d}
52
+ .sess .t{font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
53
+ .sess .m{font:10.5px/1.4 var(--mono);color:var(--faint);margin-top:2px}
54
+
55
+ section.chat{flex:1;display:flex;flex-direction:column;min-width:0;min-height:0}
56
+
57
+ /* ---- transcript ---- */
58
+ #log{flex:1;overflow-y:auto;padding:18px 22px;display:flex;
59
+ flex-direction:column;gap:11px}
60
+ .msg{max-width:min(860px,92%);border-radius:9px;padding:10px 14px;
61
+ border:1px solid var(--line);background:var(--panel)}
62
+ .msg .who{font:10.5px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;
63
+ color:var(--faint);margin-bottom:6px}
64
+ .msg.user{align-self:flex-end;background:#16202e;border-color:#22344a}
65
+ .msg.assistant{align-self:flex-start;border-color:#1f3b34}
66
+ .msg.assistant .who{color:var(--cyan)}
67
+ .msg.user .who{color:var(--blue)}
68
+ .msg.system{align-self:center;background:transparent;border-style:dashed;
69
+ color:var(--dim);font-size:12.5px;max-width:80%}
70
+ .msg.error{border-color:#5a2b2b;background:#1d1416}
71
+ .msg.error .who{color:var(--red)}
72
+ .msg pre{margin:6px 0 0;white-space:pre-wrap;word-break:break-word;
73
+ font:12.5px/1.55 var(--mono);background:#0d1014;border:1px solid var(--line);
74
+ border-radius:6px;padding:9px 11px;overflow-x:auto}
75
+ .msg .body{white-space:pre-wrap;word-break:break-word}
76
+ .empty{color:var(--faint);text-align:center;margin:auto;max-width:430px;line-height:1.9}
77
+ .empty code{font:12.5px var(--mono);color:var(--cyan);background:#0d1014;
78
+ border:1px solid var(--line);border-radius:4px;padding:2px 6px}
79
+
80
+ /* ---- checks table ---- */
81
+ table.checks{width:100%;border-collapse:collapse;margin-top:8px;font-size:12.5px}
82
+ table.checks td{padding:3px 7px;border-bottom:1px solid var(--line);
83
+ vertical-align:top}
84
+ table.checks td.k{font:11.5px var(--mono);color:var(--dim);white-space:nowrap;width:1%}
85
+ .pass{color:var(--green)}.fail{color:var(--red)}
86
+
87
+ /* ---- composer ---- */
88
+ .composer{flex:none;border-top:1px solid var(--line);background:var(--panel);
89
+ padding:11px 15px}
90
+ .row{display:flex;gap:9px;align-items:flex-end}
91
+ textarea{flex:1;resize:none;background:var(--panel2);border:1px solid var(--line);
92
+ border-radius:8px;padding:10px 12px;min-height:44px;max-height:190px;
93
+ font-family:inherit;outline:none}
94
+ textarea:focus{border-color:#31506f}
95
+ .hint{font:11px/1.6 var(--mono);color:var(--faint);margin-top:6px}
96
+ .hint b{color:var(--dim);font-weight:500}
97
+
98
+ /* ---- trace panel ---- */
99
+ aside.trace{width:335px;border-right:none;border-left:1px solid var(--line)}
100
+ .chips{display:flex;flex-wrap:wrap;gap:5px;padding:0 11px 9px}
101
+ .chip{font:10.5px/1 var(--mono);border:1px solid var(--line);border-radius:20px;
102
+ padding:4px 8px;cursor:pointer;color:var(--dim);background:transparent}
103
+ .chip.on{color:#0b0d10;border-color:transparent}
104
+ .tlist{overflow-y:auto;flex:1;padding:0 11px 14px}
105
+ .te{border-left:2px solid var(--line);padding:5px 0 6px 9px;margin-bottom:5px}
106
+ .te .h{font:10px/1.5 var(--mono);color:var(--faint);display:flex;gap:7px}
107
+ .te .x{font:12px/1.5 var(--mono);word-break:break-word;white-space:pre-wrap}
108
+ .te[data-s=user]{border-color:var(--blue)}
109
+ .te[data-s=assistant]{border-color:var(--cyan)}
110
+ .te[data-s=system]{border-color:var(--faint)}
111
+ .te[data-s=error]{border-color:var(--red)}
112
+ .te[data-s=tool_call]{border-color:var(--amber)}
113
+ .te[data-s=tool_result]{border-color:var(--green)}
114
+ .te[data-s=forge]{border-color:var(--orange)}
115
+ .te[data-s=subagent]{border-color:var(--pink)}
116
+ .te[data-s=selfmod]{border-color:var(--purple)}
117
+ .tools{padding:0 11px 12px;max-height:190px;overflow-y:auto}
118
+ .tool{display:flex;justify-content:space-between;gap:8px;font:11.5px var(--mono);
119
+ padding:3px 0;border-bottom:1px solid #191d23}
120
+ .tool .st{color:var(--faint)}
121
+ .live{width:6px;height:6px;border-radius:50%;background:var(--faint);display:inline-block}
122
+ .live.on{background:var(--green);box-shadow:0 0 7px var(--green)}
123
+ .hidden{display:none!important}
124
+ ::-webkit-scrollbar{width:9px;height:9px}
125
+ ::-webkit-scrollbar-thumb{background:#2a313a;border-radius:5px}
126
+ ::-webkit-scrollbar-track{background:transparent}
127
+ </style>
128
+ </head>
129
+ <body>
130
+
131
+ <header>
132
+ <div class="brand">autoforge <span>harness</span></div>
133
+ <span class="badge" id="bModel">…</span>
134
+ <span class="badge" id="bBase"></span>
135
+ <select id="mode" title="runtime mode">
136
+ <option value="standard">standard — full forging agent</option>
137
+ <option value="minimal">minimal — two-tool coding agent</option>
138
+ </select>
139
+ <button class="primary" id="bNew">+ New session</button>
140
+ <div class="spacer"></div>
141
+ <span class="live" id="live" title="stream"></span>
142
+ <button class="ghost" id="bTrace">Trace</button>
143
+ <button class="ghost" id="bTools">Tools</button>
144
+ </header>
145
+
146
+ <main>
147
+ <aside>
148
+ <h3>Sessions</h3>
149
+ <div class="list" id="sessions"></div>
150
+ </aside>
151
+
152
+ <section class="chat">
153
+ <div id="log">
154
+ <div class="empty">
155
+ <div style="font-size:15px;color:var(--fg);margin-bottom:10px">Give the agent a need. It builds the tool.</div>
156
+ Try <code>I keep needing to normalise ISBN-10 and ISBN-13 identifiers</code><br>
157
+ then ask it to use the tool it just made.<br><br>
158
+ Everything the model sees shows up in the <b>Trace</b> panel, by source.
159
+ </div>
160
+ </div>
161
+ <div class="composer">
162
+ <div class="row">
163
+ <textarea id="input" rows="1"
164
+ placeholder="Tell the agent what you need… (⏎ send · ⇧⏎ newline)"></textarea>
165
+ <button class="primary" id="bSend">Send</button>
166
+ <button id="bForge" title="run the forge pipeline directly on this need">Forge</button>
167
+ </div>
168
+ <div class="hint"><b>Forge</b> runs the 5-check gate: execution · robustness · adversarial · trigger · negative</div>
169
+ </div>
170
+ </section>
171
+
172
+ <aside class="trace">
173
+ <h3>Trajectory <span id="tCount" style="color:var(--faint)"></span></h3>
174
+ <div class="chips" id="chips"></div>
175
+ <div class="tlist" id="trace"></div>
176
+ <h3>Tools</h3>
177
+ <div class="tools" id="tools"></div>
178
+ </aside>
179
+ </main>
180
+
181
+ <script>
182
+ const SOURCES = ["user","assistant","tool_call","tool_result","forge","system","error","subagent","selfmod","reasoning"];
183
+ const COLOR = {user:"--blue",assistant:"--cyan",tool_call:"--amber",tool_result:"--green",
184
+ forge:"--orange",system:"--faint",error:"--red",subagent:"--pink",selfmod:"--purple",
185
+ reasoning:"--purple"};
186
+
187
+ let current = null; // session id
188
+ let events = []; // events of the current session
189
+ let es = null; // EventSource
190
+ let hidden = new Set(); // source filter
191
+ const $ = (id) => document.getElementById(id);
192
+
193
+ // ---------------------------------------------------------------- api
194
+ async function api(path, opts) {
195
+ const res = await fetch(path, {
196
+ headers: {"Content-Type": "application/json"},
197
+ ...opts,
198
+ });
199
+ if (!res.ok) {
200
+ let detail = res.statusText;
201
+ try { detail = (await res.json()).error || detail; } catch (e) {}
202
+ throw new Error(detail);
203
+ }
204
+ return res.status === 204 ? null : res.json();
205
+ }
206
+
207
+ // ---------------------------------------------------------------- state
208
+ async function loadState() {
209
+ const s = await api("/api/state");
210
+ $("bModel").textContent = s.model || "?";
211
+ $("bBase").textContent = (s.base || "").replace(/^https?:\/\//, "");
212
+ $("bBase").title = s.base || "";
213
+ $("mode").value = s.default_mode || "standard";
214
+ document.title = `autoforge harness — ${s.model || ""}`;
215
+ }
216
+
217
+ async function loadSessions() {
218
+ const {sessions} = await api("/api/sessions");
219
+ const box = $("sessions");
220
+ if (!sessions.length) {
221
+ box.innerHTML = `<div style="color:var(--faint);font:11.5px var(--mono);padding:4px 6px">none yet</div>`;
222
+ return;
223
+ }
224
+ box.innerHTML = "";
225
+ for (const s of sessions) {
226
+ const el = document.createElement("div");
227
+ el.className = "sess" + (s.id === current ? " on" : "");
228
+ const when = new Date(s.updated * 1000).toLocaleTimeString();
229
+ el.innerHTML = `<div class="t"></div><div class="m">${s.mode} · ${s.events}ev · ${when}${s.busy ? " · ●" : ""}</div>`;
230
+ el.querySelector(".t").textContent = s.title;
231
+ el.onclick = () => openSession(s.id);
232
+ box.appendChild(el);
233
+ }
234
+ }
235
+
236
+ async function loadTools() {
237
+ try {
238
+ const t = await api("/api/tools");
239
+ const box = $("tools");
240
+ const list = t.tools || [];
241
+ if (!list.length) {
242
+ box.innerHTML = `<div style="color:var(--faint);font:11.5px var(--mono)">none yet — forge one</div>`;
243
+ return;
244
+ }
245
+ box.innerHTML = list.map(x =>
246
+ `<div class="tool"><span>${esc(x.name)}</span><span class="st">${esc(x.state || "")}</span></div>`
247
+ ).join("");
248
+ } catch (e) {}
249
+ }
250
+
251
+ // ---------------------------------------------------------------- sessions
252
+ async function newSession() {
253
+ const mode = $("mode").value;
254
+ const s = await api("/api/sessions", {method: "POST", body: JSON.stringify({mode})});
255
+ await loadSessions();
256
+ openSession(s.id);
257
+ }
258
+
259
+ async function openSession(id) {
260
+ if (es) { es.close(); es = null; }
261
+ current = id; events = [];
262
+ hidden = new Set(); renderChips();
263
+ $("log").innerHTML = "";
264
+ $("trace").innerHTML = "";
265
+ const s = await api(`/api/sessions/${id}`);
266
+ for (const ev of s.events) ingest(ev);
267
+ renderLog();
268
+ subscribe(id, events.length ? events[events.length - 1].seq : 0);
269
+ loadSessions();
270
+ $("input").focus();
271
+ }
272
+
273
+ function subscribe(id, after) {
274
+ if (es) es.close();
275
+ es = new EventSource(`/api/sessions/${id}/stream?after=${after}`);
276
+ es.onopen = () => $("live").classList.add("on");
277
+ es.onerror = () => { $("live").classList.remove("on"); };
278
+ es.onmessage = (m) => {
279
+ let ev; try { ev = JSON.parse(m.data); } catch (e) { return; }
280
+ if (ev.seq <= (events.at(-1)?.seq || 0)) return;
281
+ ingest(ev); renderLog(); loadSessions();
282
+ if (ev.source === "forge" || ev.source === "tool_result") loadTools();
283
+ };
284
+ }
285
+
286
+ // ---------------------------------------------------------------- render
287
+ function esc(s) { return String(s ?? "").replace(/[&<>"]/g, c =>
288
+ ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c])); }
289
+
290
+ function ingest(ev) {
291
+ events.push(ev);
292
+ if (ev.source === "forge" && ev.checks) forges.push(ev);
293
+ const el = document.createElement("div");
294
+ el.className = "te";
295
+ el.dataset.s = ev.source;
296
+ el.dataset.seq = ev.seq;
297
+ if (hidden.has(ev.source)) el.classList.add("hidden");
298
+ const at = new Date(ev.t * 1000).toLocaleTimeString();
299
+ el.innerHTML = `<div class="h"><span style="color:var(${COLOR[ev.source] || "--dim"})">${esc(ev.source)}</span><span>${at}</span><span>#${ev.seq}</span></div><div class="x"></div>`;
300
+ el.querySelector(".x").textContent = ev.text || "";
301
+ $("trace").appendChild(el);
302
+ $("trace").scrollTop = $("trace").scrollHeight;
303
+ $("tCount").textContent = `(${events.length})`;
304
+ }
305
+
306
+ const forges = [];
307
+
308
+ function renderLog() {
309
+ const log = $("log");
310
+ const chatty = events.filter(e => ["user","assistant","system","error","forge"].includes(e.source));
311
+ if (!chatty.length) return;
312
+ log.innerHTML = "";
313
+ for (const ev of chatty) log.appendChild(bubble(ev));
314
+ log.scrollTop = log.scrollHeight;
315
+ }
316
+
317
+ function bubble(ev) {
318
+ const el = document.createElement("div");
319
+ const who = {user:"you", assistant:"agent", system:"system", error:"error", forge:"forge"}[ev.source] || ev.source;
320
+ el.className = "msg " + (ev.source === "error" ? "error" : ev.source);
321
+ el.innerHTML = `<div class="who">${esc(who)}</div><div class="body"></div>`;
322
+ el.querySelector(".body").textContent = ev.text || "";
323
+ if (ev.checks && ev.checks.length) {
324
+ const rows = ev.checks.map(c =>
325
+ `<tr><td class="k">r${c.round}</td><td class="k">${esc(c.name)}</td>
326
+ <td class="${c.passed ? "pass" : "fail"}">${c.passed ? "PASS" : "FAIL"}</td>
327
+ <td>${esc(c.detail)}</td></tr>`).join("");
328
+ el.insertAdjacentHTML("beforeend", `<table class="checks">${rows}</table>`);
329
+ }
330
+ if (ev.source === "error" && ev.detail) {
331
+ el.insertAdjacentHTML("beforeend", `<pre>${esc(ev.detail)}</pre>`);
332
+ }
333
+ return el;
334
+ }
335
+
336
+ function renderChips() {
337
+ const box = $("chips");
338
+ box.innerHTML = "";
339
+ for (const s of SOURCES) {
340
+ const c = document.createElement("span");
341
+ c.className = "chip" + (hidden.has(s) ? "" : " on");
342
+ c.textContent = s;
343
+ if (!hidden.has(s)) c.style.background = `var(${COLOR[s]})`;
344
+ c.onclick = () => {
345
+ if (hidden.has(s)) hidden.delete(s); else hidden.add(s);
346
+ renderChips();
347
+ document.querySelectorAll(".te").forEach(el => {
348
+ el.classList.toggle("hidden", hidden.has(el.dataset.s));
349
+ });
350
+ };
351
+ box.appendChild(c);
352
+ }
353
+ }
354
+
355
+ // ---------------------------------------------------------------- actions
356
+ async function send(kind) {
357
+ if (!current) { await newSession(); }
358
+ const box = $("input");
359
+ const text = box.value.trim();
360
+ if (!text) return;
361
+ box.value = ""; box.style.height = "auto";
362
+ $("bSend").disabled = $("bForge").disabled = true;
363
+ try {
364
+ await api(`/api/sessions/${current}/${kind}`,
365
+ {method: "POST", body: JSON.stringify(kind === "send" ? {text} : {need: text})});
366
+ } catch (e) {
367
+ ingest({seq: (events.at(-1)?.seq || 0) + 1, t: Date.now() / 1000, source: "error", text: e.message});
368
+ renderLog();
369
+ } finally {
370
+ $("bSend").disabled = $("bForge").disabled = false;
371
+ setTimeout(() => { $("bSend").disabled = $("bForge").disabled = false; }, 400);
372
+ box.focus();
373
+ }
374
+ }
375
+
376
+ // ---------------------------------------------------------------- wiring
377
+ $("bNew").onclick = newSession;
378
+ $("bSend").onclick = () => send("send");
379
+ $("bForge").onclick = () => send("forge");
380
+ $("bTrace").onclick = () => document.querySelector("aside.trace").classList.toggle("hidden");
381
+ $("bTools").onclick = () => { loadTools(); $("tools").scrollIntoView(); };
382
+
383
+ const input = $("input");
384
+ input.addEventListener("keydown", (e) => {
385
+ if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send("send"); }
386
+ });
387
+ input.addEventListener("input", () => {
388
+ input.style.height = "auto";
389
+ input.style.height = Math.min(input.scrollHeight, 190) + "px";
390
+ });
391
+
392
+ (async function boot() {
393
+ try {
394
+ await loadState();
395
+ await loadSessions();
396
+ await loadTools();
397
+ } catch (e) {
398
+ ingesting = null;
399
+ console.error(e);
400
+ }
401
+ setInterval(loadSessions, 5000);
402
+ input.focus();
403
+ })();
404
+ </script>
405
+ </body>
406
+ </html>
@@ -84,20 +84,25 @@ __all__ = [
84
84
  ]
85
85
 
86
86
  #: What this program calls itself when a server asks. Read from the installed
87
- #: distribution so it cannot drift from `pyproject.toml`, with a literal
88
- #: fallback for the source-checkout case where nothing is installed. The
89
- #: distribution is `autoforge-agent` (the bare name `autoforge` on the index is
90
- #: somebody else's package), so looking up `autoforge` here would not raise --
91
- #: it would find nothing installed and quietly serve a stale version forever.
87
+ #: distribution so it cannot drift from `pyproject.toml`, with a fallback for the
88
+ #: source-checkout case where nothing is installed. The distribution is
89
+ #: `autoforge-agent` (the bare name `autoforge` on the index is somebody else's
90
+ #: package), so looking up `autoforge` here would not raise -- it would find
91
+ #: nothing installed and quietly serve a stale version forever.
92
+ #:
93
+ #: The fallback is deliberately not a plausible version. It used to be the
94
+ #: release number, which is worse than useless: on release day the fallback and
95
+ #: the real answer are the same string, so a broken lookup looks exactly like a
96
+ #: working one -- and a test that compares the two cannot tell them apart.
92
97
  try:
93
98
  from importlib.metadata import PackageNotFoundError as _NotFound
94
99
  from importlib.metadata import version as _pkg_version
95
100
  try:
96
101
  _VERSION = _pkg_version("autoforge-agent")
97
102
  except _NotFound:
98
- _VERSION = "0.4.0"
103
+ _VERSION = "0.0.0+unknown"
99
104
  except ImportError: # pragma: no cover
100
- _VERSION = "0.4.0"
105
+ _VERSION = "0.0.0+unknown"
101
106
 
102
107
  #: Per-page budget for what the model sees. Generous because this spends
103
108
  #: context rather than API dollars, and a page cut to 5k is a page whose
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autoforge-agent
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Agent framework with verifiable self-made tools (the forge pipeline).
5
5
  Author: Zhang Yangyi
6
6
  License: MIT
@@ -11,6 +11,7 @@ Requires-Dist: requests>=2.28
11
11
  Provides-Extra: dev
12
12
  Requires-Dist: pytest>=7; extra == "dev"
13
13
  Requires-Dist: pytest-xdist; extra == "dev"
14
+ Requires-Dist: pyyaml; extra == "dev"
14
15
 
15
16
  # autoforge
16
17
 
@@ -1,3 +1,4 @@
1
+ MANIFEST.in
1
2
  README.md
2
3
  pyproject.toml
3
4
  autoforge/__init__.py
@@ -65,12 +66,27 @@ autoforge/tools/registry.py
65
66
  autoforge/tools/spec.py
66
67
  autoforge/web/__init__.py
67
68
  autoforge/web/server.py
69
+ autoforge/web/static/index.html
68
70
  autoforge_agent.egg-info/PKG-INFO
69
71
  autoforge_agent.egg-info/SOURCES.txt
70
72
  autoforge_agent.egg-info/dependency_links.txt
71
73
  autoforge_agent.egg-info/entry_points.txt
72
74
  autoforge_agent.egg-info/requires.txt
73
75
  autoforge_agent.egg-info/top_level.txt
76
+ probes/FINDINGS.md
77
+ probes/probe_forge_latency.py
78
+ probes/probe_gateway_context_503.py
79
+ probes/probe_gateway_empty.py
80
+ probes/probe_gateway_max_tokens.py
81
+ probes/probe_gateway_models.py
82
+ probes/probe_gateway_thinking.py
83
+ probes/probe_generator_reply.py
84
+ probes/probe_model_latency.py
85
+ probes/raw_generator_reply.txt
86
+ probes/raw_generator_reply_clean.txt
87
+ tests/_terminal.py
88
+ tests/conftest.py
89
+ tests/mcp_server_fixture.py
74
90
  tests/test_autoforge.py
75
91
  tests/test_browser.py
76
92
  tests/test_cli.py
@@ -3,3 +3,4 @@ requests>=2.28
3
3
  [dev]
4
4
  pytest>=7
5
5
  pytest-xdist
6
+ pyyaml