jac-coder 0.2.7__tar.gz → 0.2.9__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 (102) hide show
  1. {jac_coder-0.2.7 → jac_coder-0.2.9}/PKG-INFO +5 -3
  2. {jac_coder-0.2.7 → jac_coder-0.2.9}/README.md +1 -3
  3. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/api.impl.jac +12 -64
  4. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/api.jac +1 -9
  5. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/cli.jac +2 -7
  6. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/core/nodes.impl.jac +18 -63
  7. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/core/nodes.jac +209 -54
  8. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/core/walkers.impl.jac +43 -1
  9. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/core/walkers.jac +2 -10
  10. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/infra/config.jac +1 -2
  11. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/infra/events.jac +15 -3
  12. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/infra/kv.jac +10 -1
  13. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/infra/mcp_manager.jac +0 -1
  14. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/lib/coder.impl.jac +1 -1
  15. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/lib/coder.jac +1 -1
  16. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/context.impl.jac +2 -2
  17. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/cost_tracker.impl.jac +0 -2
  18. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/file_logger.jac +0 -6
  19. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/permission.impl.jac +1 -2
  20. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/prompt.impl.jac +5 -0
  21. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/prompt.jac +16 -1
  22. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/skills.impl.jac +21 -0
  23. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/skills.jac +3 -3
  24. jac_coder-0.2.9/jac_coder/runtime/struggle_report.jac +326 -0
  25. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/server.jac +1 -1
  26. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/__init__.jac +0 -1
  27. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/git.impl.jac +0 -2
  28. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/delegation.impl.jac +21 -13
  29. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/delegation.jac +2 -2
  30. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/net/preview.impl.jac +12 -2
  31. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/net/preview.jac +1 -1
  32. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/filesystem.impl.jac +0 -2
  33. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/filesystem.jac +0 -1
  34. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/jac_analyzer.impl.jac +0 -108
  35. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/jac_analyzer.jac +0 -4
  36. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/run/shell.jac +0 -1
  37. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/write/checked.impl.jac +0 -3
  38. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/util/tool_output.impl.jac +0 -2
  39. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder.egg-info/PKG-INFO +5 -3
  40. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder.egg-info/SOURCES.txt +1 -6
  41. jac_coder-0.2.9/jac_coder.egg-info/requires.txt +8 -0
  42. {jac_coder-0.2.7 → jac_coder-0.2.9}/pyproject.toml +5 -3
  43. jac_coder-0.2.7/jac_coder/runtime/memory.impl.jac +0 -247
  44. jac_coder-0.2.7/jac_coder/runtime/memory.jac +0 -21
  45. jac_coder-0.2.7/jac_coder/tool/meta/task.impl.jac +0 -12
  46. jac_coder-0.2.7/jac_coder/tool/meta/task.jac +0 -9
  47. jac_coder-0.2.7/jac_coder/tool/meta/validate.impl.jac +0 -254
  48. jac_coder-0.2.7/jac_coder/tool/meta/validate.jac +0 -11
  49. jac_coder-0.2.7/jac_coder.egg-info/requires.txt +0 -6
  50. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/__init__.jac +0 -0
  51. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/__init__.py +0 -0
  52. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/cli.impl.jac +0 -0
  53. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/cli_entry.py +0 -0
  54. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/core/__init__.jac +0 -0
  55. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/infra/__init__.jac +0 -0
  56. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/infra/config.impl.jac +0 -0
  57. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/infra/mcp_manager.impl.jac +0 -0
  58. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/lib/__init__.jac +0 -0
  59. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/__init__.jac +0 -0
  60. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/compaction.jac +0 -0
  61. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/context.jac +0 -0
  62. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/cost_tracker.jac +0 -0
  63. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/events.jac +0 -0
  64. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/runtime/permission.jac +0 -0
  65. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/serve_entry.jac +0 -0
  66. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/git.jac +0 -0
  67. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/mcp.impl.jac +0 -0
  68. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/mcp.jac +0 -0
  69. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/__init__.jac +0 -0
  70. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/clarify_intent.impl.jac +0 -0
  71. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/clarify_intent.jac +0 -0
  72. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/think.impl.jac +0 -0
  73. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/think.jac +0 -0
  74. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/todo.impl.jac +0 -0
  75. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/meta/todo.jac +0 -0
  76. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/net/__init__.jac +0 -0
  77. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/net/web.impl.jac +0 -0
  78. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/net/web.jac +0 -0
  79. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/__init__.jac +0 -0
  80. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/load_jac_skill.impl.jac +0 -0
  81. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/load_jac_skill.jac +0 -0
  82. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/search.impl.jac +0 -0
  83. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/read/search.jac +0 -0
  84. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/run/__init__.jac +0 -0
  85. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/run/guarded.impl.jac +0 -0
  86. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/run/guarded.jac +0 -0
  87. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/run/jac_tools.impl.jac +0 -0
  88. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/run/jac_tools.jac +0 -0
  89. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/run/shell.impl.jac +0 -0
  90. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/write/__init__.jac +0 -0
  91. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/tool/write/checked.jac +0 -0
  92. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/util/__init__.jac +0 -0
  93. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/util/banner.jac +0 -0
  94. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/util/colors.jac +0 -0
  95. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/util/md_render.jac +0 -0
  96. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/util/sandbox.impl.jac +0 -0
  97. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/util/sandbox.jac +0 -0
  98. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder/util/tool_output.jac +0 -0
  99. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder.egg-info/dependency_links.txt +0 -0
  100. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder.egg-info/entry_points.txt +0 -0
  101. {jac_coder-0.2.7 → jac_coder-0.2.9}/jac_coder.egg-info/top_level.txt +0 -0
  102. {jac_coder-0.2.7 → jac_coder-0.2.9}/setup.cfg +0 -0
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jac-coder
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: AI coding agent backend for Jac, powered by jac-byllm
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: python-dotenv>=1.0.0
7
- Requires-Dist: jaclang
8
- Requires-Dist: byllm
9
7
  Requires-Dist: mcp>=1.0.0
10
8
  Requires-Dist: jac-mcp
11
9
  Requires-Dist: rich>=13.0
10
+ Requires-Dist: litellm<=1.82.6,>=1.70.0
11
+ Requires-Dist: httpx>=0.27.0
12
+ Requires-Dist: loguru<0.8.0,>=0.7.2
13
+ Requires-Dist: pillow<13.0.0,>=12.0.0
@@ -64,7 +64,6 @@ Root → Session → MainAgent
64
64
  - **WorkerAgent (walker)** — in-process SubAgent with 14 tools (can write/edit/run). For known changes.
65
65
  - **ExplorerAgent (walker)** — in-process SubAgent with 10 tools (read-only + web search). For root-cause investigation.
66
66
  - **Session (node)** — persistent chat state, history, active files, errors, mode hint.
67
- - **ProjectMemory (node)** — AST-derived codebase knowledge (nodes, walkers, edges, imports). Backed by `.jaccoder/progress.md` as primary source of truth.
68
67
  - **McpRegistry (node)** — persisted MCP server configs.
69
68
 
70
69
  See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full picture.
@@ -130,7 +129,7 @@ jac-code/
130
129
  │ ├── api.jac # Public API — the only module external apps import
131
130
  │ ├── server.jac # JSON-RPC stdio server for the VS Code extension
132
131
  │ ├── core/
133
- │ │ ├── nodes.jac # MainAgent, WorkerAgent, ExplorerAgent, Session, ProjectMemory
132
+ │ │ ├── nodes.jac # MainAgent, WorkerAgent, ExplorerAgent, Session
134
133
  │ │ └── walkers.jac # interact, new_session, list_sessions, etc.
135
134
  │ ├── lib/
136
135
  │ │ └── coder.jac # JacCoder library-mode class (stateless + service modes)
@@ -140,7 +139,6 @@ jac-code/
140
139
  │ ├── runtime/
141
140
  │ │ ├── context.jac # Tiered compaction with LLM-summary fallback
142
141
  │ │ ├── events.jac # Event bus, doom-loop detection, abort signal
143
- │ │ ├── memory.jac # AST-first ProjectMemory (LLM fallback)
144
142
  │ │ ├── permission.jac # Permission rule engine (allow / ask / deny)
145
143
  │ │ ├── prompt.jac # Mode-aware dynamic prompt assembly
146
144
  │ │ ├── skills.jac # SKILL.md registry + listing injection
@@ -197,7 +197,6 @@ impl chat(
197
197
  return {"error": "Session is stale after a server update. Please close this session and open a new one.", "agent": "error"};
198
198
  }
199
199
 
200
- # Override directory if provided
201
200
  if directory {
202
201
  session.directory = directory;
203
202
  }
@@ -230,7 +229,6 @@ impl chat(
230
229
  # whether to auto-allow, prompt, or deny write/edit/bash operations.
231
230
  _set_chat_session(session_id, edit_mode);
232
231
 
233
- # Setup
234
232
  clear_abort();
235
233
  reset_steps();
236
234
  start_turn();
@@ -242,7 +240,6 @@ impl chat(
242
240
  }
243
241
 
244
242
  try {
245
- # Memory initialization
246
243
  _ensure_memory(session, work_dir);
247
244
 
248
245
  session.updated_at = datetime.now().isoformat();
@@ -339,7 +336,7 @@ impl chat(
339
336
  user_image = None;
340
337
  if images and len(images) > 0 {
341
338
  try {
342
- import from byllm.lib { Image }
339
+ import from jaclang.byllm.lib { Image }
343
340
  ;
344
341
  first_img = images[0];
345
342
  data_url = first_img.get("data_url", "") if isinstance(first_img, dict) else str(first_img);
@@ -358,6 +355,7 @@ impl chat(
358
355
 
359
356
  response_text = stream_result["content"];
360
357
  tools_used = stream_result["tools_used"];
358
+ tool_records = stream_result.get("tool_records", []);
361
359
 
362
360
  # Finalize turn — get accumulated file/error data from tool internals
363
361
  tool_end();
@@ -366,7 +364,6 @@ impl chat(
366
364
  files_modified = summary.get("files_modified", []);
367
365
  has_errors = summary.get("errors", 0) > 0;
368
366
 
369
- # Update session state
370
367
  if files_modified {
371
368
  for f in files_modified {
372
369
  if f not in session.active_files {
@@ -421,6 +418,14 @@ impl chat(
421
418
  import from jac_coder.runtime.prompt { infer_mode_hint as _infer_hint }
422
419
  session.last_mode_hint = _infer_hint(tools_used, files_modified);
423
420
 
421
+ # Guaranteed end-of-turn struggle reflection (work turns only).
422
+ if not is_abort_requested() {
423
+ import from jac_coder.runtime.struggle_report { reflect_and_log }
424
+ reflect_and_log(
425
+ main_agent, tool_records, files_modified, has_errors, response_text
426
+ );
427
+ }
428
+
424
429
  return {
425
430
  "response": response_text,
426
431
  "agent": "main",
@@ -474,63 +479,6 @@ def _ensure_memory(session: Session, work_dir: str) -> None {
474
479
  }
475
480
 
476
481
 
477
- def _post_process(session: Session, response_obj: Any) -> None {
478
- # Track files
479
- if response_obj.files_modified {
480
- for f in response_obj.files_modified {
481
- if f not in session.active_files {
482
- session.active_files.append(f);
483
- }
484
- }
485
- if len(session.active_files) > 10 {
486
- session.active_files = session.active_files[-10:];
487
- }
488
- }
489
-
490
- # Track errors
491
- if response_obj.has_errors {
492
- files_str = ", ".join(response_obj.files_modified)
493
- if response_obj.files_modified
494
- else "unknown";
495
- session.pending_errors = [f"Errors in previous turn (files: {files_str})"];
496
- } else {
497
- session.pending_errors = [];
498
- }
499
-
500
- # Refresh project_summary from progress.md (agent may have updated it).
501
- if session.directory {
502
- progress_path = os.path.join(session.directory, ".jaccoder", "progress.md");
503
- if os.path.isfile(progress_path) {
504
- try {
505
- with open(progress_path, "r", encoding="utf-8") as f {
506
- pc = f.read();
507
- }
508
- if len(pc) > 4000 {
509
- pc = pc[:4000] + "\n\n[... truncated]";
510
- }
511
- session.project_summary = pc;
512
- } except Exception { }
513
- }
514
- }
515
-
516
- # Persist response
517
- record: dict = {
518
- "role": "assistant",
519
- "content": response_obj.content,
520
- "agent": "main"
521
- };
522
- if response_obj.tools_used {
523
- record["tools_used"] = response_obj.tools_used;
524
- }
525
- if response_obj.files_modified {
526
- record["files_modified"] = response_obj.files_modified;
527
- }
528
- session.chat_history.append(record);
529
- session.last_agent = "main";
530
- session.updated_at = datetime.now().isoformat();
531
- }
532
-
533
-
534
482
  # ---------------------------------------------------------------------------
535
483
  # Model management API
536
484
  # ---------------------------------------------------------------------------
@@ -552,8 +500,8 @@ impl api_check_local_setup(alias: str) -> dict {
552
500
  model: bool = False;
553
501
  valid_alias: bool = False;
554
502
  try {
555
- import from byllm.local_runtime { LOCAL_MODELS }
556
- import from byllm.model_cache { is_downloaded }
503
+ import from jaclang.byllm.local_runtime { LOCAL_MODELS }
504
+ import from jaclang.byllm.model_cache { is_downloaded }
557
505
  spec = LOCAL_MODELS.get(alias);
558
506
  if spec is not None {
559
507
  valid_alias = True;
@@ -11,8 +11,7 @@ import from jac_coder.infra.config {
11
11
  }
12
12
  import from jac_coder.util.tool_output { tool_end }
13
13
  import from jac_coder.runtime.permission { permission_engine }
14
- import from jac_coder.util.sandbox { set_sandbox_root, set_browser_exec }
15
- import from jac_coder.runtime.context { build_context, ContextConfig }
14
+ import from jac_coder.util.sandbox { set_sandbox_root }
16
15
  import from jac_coder.core.walkers {
17
16
  new_session,
18
17
  ensure_main_agent,
@@ -28,11 +27,6 @@ import from jac_coder.infra.mcp_manager {
28
27
  mcp_get_tools,
29
28
  mcp_register_builtin
30
29
  }
31
- import from jac_coder.runtime.memory {
32
- find_or_create_memory,
33
- _init_memory,
34
- update_memory_from_session
35
- }
36
30
  import from jac_coder.core.nodes { Session, MainAgent }
37
31
  import from jac_coder.runtime.cost_tracker {
38
32
  get_summary as _ct_get_summary,
@@ -41,7 +35,6 @@ import from jac_coder.runtime.cost_tracker {
41
35
  import from jac_coder.tool.run.guarded {
42
36
  _set_chat_session,
43
37
  _clear_chat_session,
44
- _chat_contexts,
45
38
  _clarification_events,
46
39
  _clarification_decisions,
47
40
  _CLRFY_KEY_PREFIX
@@ -52,7 +45,6 @@ import from jac_coder.runtime.events {
52
45
  reset_steps,
53
46
  start_turn,
54
47
  emit_turn_summary,
55
- emit_event,
56
48
  is_abort_requested,
57
49
  clear_abort
58
50
  }
@@ -2,19 +2,15 @@
2
2
 
3
3
  import os;
4
4
  import sys;
5
- import from datetime { datetime }
6
5
  import from jac_coder.core.nodes { Session, MainAgent }
7
6
  import from jac_coder.core.walkers {
8
7
  new_session,
9
- list_sessions,
10
- get_session,
11
- close_session,
12
8
  interact,
13
9
  ensure_main_agent
14
10
  }
15
- import from jac_coder.infra.config { llm, model_name, get_config, set_model, get_model }
11
+ import from jac_coder.infra.config { model_name, get_config, set_model, get_model }
16
12
  import from jac_coder.runtime.permission { permission_engine }
17
- import from jac_coder.runtime.events { register_event_callback, get_agent_prefix }
13
+ import from jac_coder.runtime.events { register_event_callback }
18
14
  import from jac_coder.runtime.file_logger { init_file_logger, file_log_event, close_file_logger }
19
15
  import from jac_coder.tool.meta.todo { get_todos }
20
16
  import from jac_coder.util.colors {
@@ -26,7 +22,6 @@ import from jac_coder.util.colors {
26
22
  GREEN,
27
23
  YELLOW,
28
24
  RED,
29
- MAGENTA,
30
25
  WHITE,
31
26
  AGENT_COLORS
32
27
  }
@@ -14,68 +14,6 @@ impl Session.postinit{
14
14
  }
15
15
 
16
16
 
17
- # ---------------------------------------------------------------------------
18
- # ProjectMemory
19
- # ---------------------------------------------------------------------------
20
- sem ProjectMemory.profile_project = """Analyze a Jac project and extract structured memory.
21
- Given the file tree and key file contents, return:
22
- - architecture: 1-2 sentences describing the project purpose, tech stack, and node/walker structure
23
- - file_map: dict mapping up to 15 important file paths (relative) to 1-line descriptions of their role
24
- - conventions: list of coding conventions observed (naming style, file organization, patterns)
25
- Be concise and factual. This context will help future coding sessions start smart.""";
26
-
27
- sem ProjectMemory.collect_learnings = """Extract learnings from a completed coding session.
28
- Given the files modified and a session summary, return only NEW information:
29
- - new_files: dict of newly created or significantly changed file paths to 1-line descriptions
30
- - new_conventions: new coding patterns or conventions established (empty list if none)
31
- - new_decisions: architectural or design decisions made (empty list if none)
32
- - known_issues: bugs or issues discovered but not yet fixed (empty list if none)
33
- Be selective — only include genuinely new information.""";
34
-
35
- sem ProjectScan.architecture = "1-2 sentences: project purpose, tech stack, and overall node/walker structure";
36
- sem ProjectScan.file_map = "dict mapping relative file paths to 1-line role descriptions (max 15 files)";
37
- sem ProjectScan.conventions = "list of coding conventions: naming style, file organization, patterns used";
38
-
39
- sem SessionLearnings.new_files = "dict of new/modified file paths to 1-line descriptions of their content";
40
- sem SessionLearnings.new_conventions = "new coding conventions established this session (empty list if none)";
41
- sem SessionLearnings.new_decisions = "architectural decisions made this session (empty list if none)";
42
- sem SessionLearnings.known_issues = "bugs or issues discovered but not yet fixed (empty list if none)";
43
-
44
- impl ProjectMemory.summarize() -> str {
45
- parts = [];
46
- if self.architecture {
47
- parts.append("Architecture: " + self.architecture);
48
- }
49
- if self.node_details {
50
- parts.append(self.node_details);
51
- }
52
- if self.walker_details {
53
- parts.append(self.walker_details);
54
- }
55
- if self.graph_topology {
56
- parts.append("Graph: " + self.graph_topology);
57
- }
58
- if self.import_map {
59
- parts.append(self.import_map);
60
- }
61
- if self.file_map {
62
- items = list(self.file_map.items())[:10];
63
- file_lines = [f" {k}: {v}" for (k, v) in items];
64
- parts.append("Key files:\n" + "\n".join(file_lines));
65
- }
66
- if self.conventions {
67
- parts.append("Conventions: " + ", ".join(self.conventions[:5]));
68
- }
69
- if self.past_decisions {
70
- parts.append("Past decisions: " + "; ".join(self.past_decisions[:3]));
71
- }
72
- if self.known_issues {
73
- parts.append("Known issues: " + "; ".join(self.known_issues[:3]));
74
- }
75
- return "\n".join(parts);
76
- }
77
-
78
-
79
17
  # ---------------------------------------------------------------------------
80
18
  # MainAgent — the orchestrator
81
19
  # ---------------------------------------------------------------------------
@@ -87,10 +25,14 @@ sem MainAgent.respond = "Respond to the user's coding request.";
87
25
  # ---------------------------------------------------------------------------
88
26
  # SubAgent walkers — sem annotations and entry ability implementations
89
27
  # ---------------------------------------------------------------------------
90
- sem WorkerAgent.do_work = "Execute the delegated coding task.";
28
+ sem WorkerAgent.do_work = "Execute the delegated coding task as a pure implementer. When .jaccoder/ui_spec.json exists, treat it as a binding UI contract — never change style, theme, base_color, nav_pattern, or pages (those belong to UIDesignerAgent). If a mechanical implementation need arises for a component not in primitive_components (e.g. a spinner), add it to primitive_components in ui_spec.json first, then run jac add --shadcn. Never make freestyle design decisions.";
91
29
 
92
30
  sem ExplorerAgent.do_work = "Investigate the codebase and report findings.";
93
31
 
32
+ sem UIDesignerAgent = "UI design authority — produces or updates .jaccoder/ui_spec.json, the living design contract for the application.";
33
+ sem UIDesignerAgent.do_work = "Write or update .jaccoder/ui_spec.json. Create mode (no UISpec in task): analyse project from scratch and produce a full spec. Update mode (current UISpec JSON provided in task): apply the user's change request to only the affected fields, preserve everything else.";
34
+ sem UIDesignerAgent.task = "Input context in one of two forms. Create: project path + directory listing + jac.toml + user request. Update: project path + current UISpec JSON + user request.";
35
+
94
36
 
95
37
  """Consume a SubAgent's by llm() stream, forward events, and return results."""
96
38
  def _consume_subagent_stream(event_stream: Any, prefix: str, track_files: bool = False) -> dict {
@@ -134,6 +76,11 @@ def _consume_subagent_stream(event_stream: Any, prefix: str, track_files: bool =
134
76
  }
135
77
  } elif event.event_type == "steps_done" {
136
78
  emit_event("llm_steps_done", dict(event.data));
79
+ } elif event.event_type == "stream_reset" {
80
+ # byllm retried a transient mid-stream drop; drop the partial subagent
81
+ # reply so the regenerated text isn't doubled.
82
+ content = "";
83
+ emit_event("llm_stream_reset", dict(event.data));
137
84
  } elif event.event_type == "chunk" {
138
85
  content += event.data.get("content", "");
139
86
  } elif event.event_type == "usage" {
@@ -169,3 +116,11 @@ impl ExplorerAgent.execute with MainAgent entry {
169
116
  self.result_files = [];
170
117
  self.result_errors = list(result["errors"]);
171
118
  }
119
+
120
+
121
+ impl UIDesignerAgent.execute with MainAgent entry {
122
+ result = _consume_subagent_stream(
123
+ self.do_work(task_str=self.task), "sub:designer"
124
+ );
125
+ self.result_content = str(result["content"]);
126
+ }