jac-coder 0.2.3__tar.gz → 0.2.5__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 (126) hide show
  1. {jac_coder-0.2.3 → jac_coder-0.2.5}/PKG-INFO +2 -1
  2. {jac_coder-0.2.3 → jac_coder-0.2.5}/README.md +8 -6
  3. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/__init__.jac +1 -1
  4. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/api.impl.jac +81 -35
  5. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/api.jac +30 -7
  6. jac_coder-0.2.5/jac_coder/cli.impl.jac +730 -0
  7. jac_coder-0.2.5/jac_coder/cli.jac +186 -0
  8. jac_coder-0.2.5/jac_coder/cli_entry.py +27 -0
  9. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/core/nodes.impl.jac +29 -9
  10. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/core/nodes.jac +57 -6
  11. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/core/walkers.impl.jac +34 -43
  12. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/infra/config.impl.jac +7 -0
  13. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/infra/config.jac +4 -0
  14. jac_coder-0.2.5/jac_coder/infra/events.jac +268 -0
  15. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/lib/coder.impl.jac +323 -31
  16. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/lib/coder.jac +8 -0
  17. jac_coder-0.2.5/jac_coder/runtime/compaction.jac +301 -0
  18. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/permission.impl.jac +1 -1
  19. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/prompt.jac +6 -4
  20. jac_coder-0.2.5/jac_coder/runtime/skills.impl.jac +133 -0
  21. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/server.jac +27 -2
  22. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/__init__.jac +4 -4
  23. jac_coder-0.2.5/jac_coder/tool/meta/clarify_intent.impl.jac +110 -0
  24. jac_coder-0.2.5/jac_coder/tool/meta/clarify_intent.jac +52 -0
  25. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/delegation.impl.jac +37 -4
  26. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/delegation.jac +1 -0
  27. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/read/filesystem.impl.jac +77 -11
  28. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/read/filesystem.jac +4 -0
  29. jac_coder-0.2.5/jac_coder/tool/read/jac_analyzer.impl.jac +331 -0
  30. jac_coder-0.2.5/jac_coder/tool/read/jac_analyzer.jac +22 -0
  31. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/run/guarded.impl.jac +46 -2
  32. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/run/guarded.jac +11 -1
  33. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/run/jac_tools.impl.jac +38 -19
  34. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/write/checked.impl.jac +33 -1
  35. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/write/checked.jac +8 -1
  36. jac_coder-0.2.5/jac_coder/util/banner.jac +75 -0
  37. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/util/colors.jac +3 -1
  38. jac_coder-0.2.5/jac_coder/util/md_render.jac +65 -0
  39. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/util/tool_output.impl.jac +13 -6
  40. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/util/tool_output.jac +1 -1
  41. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder.egg-info/PKG-INFO +2 -1
  42. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder.egg-info/SOURCES.txt +8 -22
  43. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder.egg-info/requires.txt +1 -0
  44. {jac_coder-0.2.3 → jac_coder-0.2.5}/pyproject.toml +2 -4
  45. jac_coder-0.2.3/jac_coder/cli_entry.py +0 -25
  46. jac_coder-0.2.3/jac_coder/runtime/skills.impl.jac +0 -231
  47. jac_coder-0.2.3/jac_coder/skills/ROADMAP.md +0 -179
  48. jac_coder-0.2.3/jac_coder/skills/jac-by-llm/SKILL.md +0 -59
  49. jac_coder-0.2.3/jac_coder/skills/jac-cl-auth/SKILL.md +0 -134
  50. jac_coder-0.2.3/jac_coder/skills/jac-cl-components/SKILL.md +0 -139
  51. jac_coder-0.2.3/jac_coder/skills/jac-cl-organization/SKILL.md +0 -110
  52. jac_coder-0.2.3/jac_coder/skills/jac-cl-routing/SKILL.md +0 -63
  53. jac_coder-0.2.3/jac_coder/skills/jac-cl-styling/SKILL.md +0 -51
  54. jac_coder-0.2.3/jac_coder/skills/jac-core-cheatsheet/SKILL.md +0 -86
  55. jac_coder-0.2.3/jac_coder/skills/jac-fullstack-patterns/SKILL.md +0 -45
  56. jac_coder-0.2.3/jac_coder/skills/jac-has-fields/SKILL.md +0 -32
  57. jac_coder-0.2.3/jac_coder/skills/jac-impl-files/SKILL.md +0 -58
  58. jac_coder-0.2.3/jac_coder/skills/jac-node-edge-patterns/SKILL.md +0 -65
  59. jac_coder-0.2.3/jac_coder/skills/jac-npm-packages/SKILL.md +0 -96
  60. jac_coder-0.2.3/jac_coder/skills/jac-scaffold/SKILL.md +0 -70
  61. jac_coder-0.2.3/jac_coder/skills/jac-shadcn-components/SKILL.md +0 -340
  62. jac_coder-0.2.3/jac_coder/skills/jac-sv-auth/SKILL.md +0 -43
  63. jac_coder-0.2.3/jac_coder/skills/jac-sv-endpoints/SKILL.md +0 -71
  64. jac_coder-0.2.3/jac_coder/skills/jac-sv-persistence/SKILL.md +0 -104
  65. jac_coder-0.2.3/jac_coder/skills/jac-types/SKILL.md +0 -100
  66. jac_coder-0.2.3/jac_coder/skills/jac-walker-patterns/SKILL.md +0 -54
  67. jac_coder-0.2.3/jac_coder/tool/meta/question.impl.jac +0 -35
  68. jac_coder-0.2.3/jac_coder/tool/meta/question.jac +0 -7
  69. jac_coder-0.2.3/jac_coder/tool/read/jac_analyzer.impl.jac +0 -593
  70. jac_coder-0.2.3/jac_coder/tool/read/jac_analyzer.jac +0 -21
  71. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/__init__.py +0 -0
  72. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/core/__init__.jac +0 -0
  73. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/core/walkers.jac +0 -0
  74. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/infra/__init__.jac +0 -0
  75. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/infra/kv.jac +0 -0
  76. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/infra/mcp_manager.impl.jac +0 -0
  77. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/infra/mcp_manager.jac +0 -0
  78. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/lib/__init__.jac +0 -0
  79. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/__init__.jac +0 -0
  80. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/context.impl.jac +0 -0
  81. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/context.jac +0 -0
  82. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/cost_tracker.impl.jac +0 -0
  83. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/cost_tracker.jac +0 -0
  84. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/events.jac +0 -0
  85. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/file_logger.jac +0 -0
  86. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/memory.impl.jac +0 -0
  87. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/memory.jac +0 -0
  88. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/permission.jac +0 -0
  89. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/prompt.impl.jac +0 -0
  90. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/runtime/skills.jac +0 -0
  91. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/serve_entry.jac +0 -0
  92. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/git.impl.jac +0 -0
  93. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/git.jac +0 -0
  94. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/mcp.impl.jac +0 -0
  95. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/mcp.jac +0 -0
  96. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/__init__.jac +0 -0
  97. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/task.impl.jac +0 -0
  98. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/task.jac +0 -0
  99. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/think.impl.jac +0 -0
  100. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/think.jac +0 -0
  101. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/todo.impl.jac +0 -0
  102. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/todo.jac +0 -0
  103. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/validate.impl.jac +0 -0
  104. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/meta/validate.jac +0 -0
  105. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/net/__init__.jac +0 -0
  106. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/net/preview.impl.jac +0 -0
  107. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/net/preview.jac +0 -0
  108. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/net/web.impl.jac +0 -0
  109. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/net/web.jac +0 -0
  110. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/read/__init__.jac +0 -0
  111. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/read/load_jac_skill.impl.jac +0 -0
  112. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/read/load_jac_skill.jac +0 -0
  113. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/read/search.impl.jac +0 -0
  114. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/read/search.jac +0 -0
  115. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/run/__init__.jac +0 -0
  116. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/run/jac_tools.jac +0 -0
  117. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/run/shell.impl.jac +0 -0
  118. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/run/shell.jac +0 -0
  119. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/tool/write/__init__.jac +0 -0
  120. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/util/__init__.jac +0 -0
  121. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/util/sandbox.impl.jac +0 -0
  122. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder/util/sandbox.jac +0 -0
  123. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder.egg-info/dependency_links.txt +0 -0
  124. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder.egg-info/entry_points.txt +0 -0
  125. {jac_coder-0.2.3 → jac_coder-0.2.5}/jac_coder.egg-info/top_level.txt +0 -0
  126. {jac_coder-0.2.3 → jac_coder-0.2.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jac-coder
3
- Version: 0.2.3
3
+ Version: 0.2.5
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
@@ -8,3 +8,4 @@ Requires-Dist: jaclang
8
8
  Requires-Dist: byllm
9
9
  Requires-Dist: mcp>=1.0.0
10
10
  Requires-Dist: jac-mcp
11
+ Requires-Dist: rich>=13.0
@@ -1,6 +1,8 @@
1
1
  # JacCoder
2
2
 
3
- ![JacCoder CLI](assets/cli.png)
3
+ <p align="center">
4
+ <img width="520" height="226" alt="image" src="https://github.com/user-attachments/assets/f245c84e-b88c-433a-99b8-a3609dcbe691" />
5
+ </p>
4
6
 
5
7
  AI coding agent for the Jaseci stack, built entirely in [Jac](https://github.com/jaseci-labs/jaseci) using Object-Spatial Programming. Features an orchestrator-worker architecture with compiler-level Jac Intelligence, self-correcting code writes, and in-process SubAgent delegation.
6
8
 
@@ -40,13 +42,13 @@ Dependencies (auto-installed): `jaclang`, `byllm`, `mcp>=1.0.0`, `jac-mcp`, `pyt
40
42
  export OPENAI_API_KEY="sk-..."
41
43
 
42
44
  # Interactive REPL
43
- jac cli.jac
45
+ jac-coder
44
46
 
45
47
  # Single prompt (non-interactive)
46
- jac cli.jac run "build a hello world jac app at /tmp/myapp"
48
+ jac-coder run "build a hello world jac app at /tmp/myapp"
47
49
 
48
50
  # Resume a session
49
- jac cli.jac session <id-prefix>
51
+ jac-coder session <id-prefix>
50
52
  ```
51
53
 
52
54
  ## Architecture
@@ -89,7 +91,7 @@ MainAgent has 27 tools, grouped by domain:
89
91
  | Web | `web_fetch`, `web_search` | HTTP fetch + DuckDuckGo search |
90
92
  | Browser | `browser_open`, `browser_do`, `browser_state`, `browser_validate`, `browser_close` | Visual QA via agent-browser; `browser_validate` is the primary pass/fail check |
91
93
  | Delegate | `spawn_agent` | In-process WorkerAgent / ExplorerAgent walker |
92
- | Interact | `ask_question`, `update_todos` | User prompt + multi-step task tracking |
94
+ | Interact | `clarify_intent`, `update_todos` | Structured clarifying questions (blocking, multi-mode) + multi-step task tracking |
93
95
  | MCP | `mcp_call` | Call any tool from a connected MCP server |
94
96
 
95
97
  ## Public API
@@ -144,7 +146,7 @@ jac-code/
144
146
  │ │ ├── skills.jac # SKILL.md registry + listing injection
145
147
  │ │ └── cost_tracker.jac # Opt-in token + USD cost tracking
146
148
  │ ├── tool/ # 28 tools organized by domain
147
- │ │ ├── meta/ # think, spawn_agent, update_todos, ask_question, validate
149
+ │ │ ├── meta/ # think, spawn_agent, update_todos, clarify_intent, validate
148
150
  │ │ ├── read/ # filesystem, search, jac_analyzer, load_jac_skill
149
151
  │ │ ├── write/ # checked (write_code/edit_code), scaffold
150
152
  │ │ ├── run/ # shell, guarded, jac_tools
@@ -1,4 +1,4 @@
1
- """jac-coder — AI coding agent for Jac.
1
+ """jac-coder — AI coding agent for Jaseci.
2
2
 
3
3
  Top-level re-exports. Existing CLI / stdio-server consumers continue
4
4
  to import from jac_coder.api; this file exposes the library-mode
@@ -150,6 +150,7 @@ impl get_session(session_id: str) -> dict {
150
150
  "status": s.status,
151
151
  "directory": s.directory,
152
152
  "chat_history": s.chat_history,
153
+ "clarification_history": s.clarification_history,
153
154
  "created_at": s.created_at,
154
155
  "updated_at": s.updated_at
155
156
  };
@@ -257,15 +258,24 @@ impl chat(
257
258
  # role=system dicts to session.chat_history without them persisting stale.
258
259
  prefix_parts: list[str] = [];
259
260
 
260
- if session.project_summary {
261
- is_progress_file = session.project_summary.lstrip().startswith("#");
262
- label = (
263
- "Project progress (.jaccoder/progress.md)"
264
- if is_progress_file
265
- else "Project context"
261
+ # Tell the LLM the actual working directory — without this, relative
262
+ # paths are unresolvable and the model guesses (e.g. "/home/user").
263
+ if work_dir {
264
+ prefix_parts.append(
265
+ f"Working directory: {work_dir}\n"
266
+ "Default to this directory: resolve relative paths and deictic "
267
+ "references (e.g. 'here', 'this folder', '.', './sub') against it. "
268
+ "Use absolute paths for anything outside it."
266
269
  );
270
+ }
271
+
272
+ if session.project_summary {
267
273
  prefix_parts.append(
268
- f"[INTERNAL — do NOT present this to the user unless they ask about the project. This is your background knowledge for making informed decisions.]\n{label}:\n{session.project_summary}"
274
+ "[INTERNAL — background context from .jaccoder/progress.md. "
275
+ "Do NOT acknowledge, summarize, or volunteer this to the user "
276
+ "unless they explicitly ask about the project. Use it only to "
277
+ "inform your decisions on real work prompts.]\n"
278
+ f"Project progress (.jaccoder/progress.md):\n{session.project_summary}"
269
279
  );
270
280
  }
271
281
  if session.active_files {
@@ -373,13 +383,18 @@ impl chat(
373
383
  } else {
374
384
  session.pending_errors = [];
375
385
  }
376
- if files_modified and session.directory {
377
- memory = find_or_create_memory(session.directory);
378
- if memory {
379
- update_memory_from_session(
380
- memory, files_modified, response_text[:500]
381
- );
382
- session.project_summary = memory.summarize();
386
+ if session.directory {
387
+ _progress_path = os.path.join(session.directory, ".jaccoder", "progress.md");
388
+ if os.path.isfile(_progress_path) {
389
+ try {
390
+ with open(_progress_path, "r", encoding="utf-8") as _pf {
391
+ _pc = _pf.read();
392
+ }
393
+ if len(_pc) > 4000 {
394
+ _pc = _pc[:4000] + "\n\n[... truncated]";
395
+ }
396
+ session.project_summary = _pc;
397
+ } except Exception { }
383
398
  }
384
399
  }
385
400
 
@@ -443,21 +458,19 @@ def _ensure_memory(session: Session, work_dir: str) -> None {
443
458
  if not work_dir or session.project_summary {
444
459
  return;
445
460
  }
446
- memory = find_or_create_memory(work_dir);
447
- if not memory {
461
+ progress_path = os.path.join(work_dir, ".jaccoder", "progress.md");
462
+ if not os.path.isfile(progress_path) {
448
463
  return;
449
464
  }
450
- has_project = (
451
- os.path.exists(os.path.join(work_dir, "jac.toml"))
452
- or os.path.exists(os.path.join(work_dir, "main.jac"))
453
- );
454
- if not memory.architecture and not memory.scan_attempted and has_project {
455
- _init_memory(memory, work_dir);
456
- }
457
- summary = memory.summarize();
458
- if summary {
459
- session.project_summary = summary;
460
- }
465
+ try {
466
+ with open(progress_path, "r", encoding="utf-8") as f {
467
+ content = f.read();
468
+ }
469
+ if len(content) > 4000 {
470
+ content = content[:4000] + "\n\n[... truncated]";
471
+ }
472
+ session.project_summary = content;
473
+ } except Exception { }
461
474
  }
462
475
 
463
476
 
@@ -484,14 +497,19 @@ def _post_process(session: Session, response_obj: Any) -> None {
484
497
  session.pending_errors = [];
485
498
  }
486
499
 
487
- # Update memory
488
- if response_obj.files_modified and session.directory {
489
- memory = find_or_create_memory(session.directory);
490
- if memory {
491
- update_memory_from_session(
492
- memory, response_obj.files_modified, response_obj.content[:500]
493
- );
494
- session.project_summary = memory.summarize();
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 { }
495
513
  }
496
514
  }
497
515
 
@@ -591,6 +609,34 @@ impl abort_session(session_id: str) -> None {
591
609
  }
592
610
  }
593
611
 
612
+ """Receive the user's decision from a clarification prompt and unblock the agent waiting for it.
613
+ - selected_id: the ID of the option the user selected, if applicable
614
+ - custom_text: any custom text input the user provided, if applicable
615
+ - skipped: whether the user skipped the clarification
616
+ """
617
+ impl clarification_response(
618
+ session_id: str,
619
+ request_id: str,
620
+ selected_id: str = "",
621
+ custom_text: str = "",
622
+ skipped: bool = False
623
+ ) -> dict {
624
+ if not session_id {
625
+ return {"ok": False, "error": "session_id required"};
626
+ }
627
+ _clarification_decisions[session_id] = {
628
+ "request_id": request_id,
629
+ "selected_id": selected_id,
630
+ "custom_text": custom_text,
631
+ "skipped": skipped
632
+ };
633
+ gate = _clarification_events.get(session_id);
634
+ if gate {
635
+ gate.set();
636
+ }
637
+ return {"ok": True};
638
+ }
639
+
594
640
 
595
641
  """Build MCP tools context message for the LLM system prompt.
596
642
 
@@ -13,7 +13,12 @@ import from jac_coder.util.tool_output { tool_end }
13
13
  import from jac_coder.runtime.permission { permission_engine }
14
14
  import from jac_coder.util.sandbox { set_sandbox_root, set_browser_exec }
15
15
  import from jac_coder.runtime.context { build_context, ContextConfig }
16
- import from jac_coder.core.walkers { new_session, ensure_main_agent, _consume_llm_stream, _find_session }
16
+ import from jac_coder.core.walkers {
17
+ new_session,
18
+ ensure_main_agent,
19
+ _consume_llm_stream,
20
+ _find_session
21
+ }
17
22
  import from jac_coder.infra.mcp_manager {
18
23
  mcp_add_server,
19
24
  mcp_disconnect_server,
@@ -29,8 +34,17 @@ import from jac_coder.runtime.memory {
29
34
  update_memory_from_session
30
35
  }
31
36
  import from jac_coder.core.nodes { Session, MainAgent }
32
- import from jac_coder.runtime.cost_tracker { get_summary as _ct_get_summary, is_cost_tracking_enabled as _ct_enabled }
33
- import from jac_coder.tool.run.guarded { _set_chat_session, _clear_chat_session, _chat_contexts }
37
+ import from jac_coder.runtime.cost_tracker {
38
+ get_summary as _ct_get_summary,
39
+ is_cost_tracking_enabled as _ct_enabled
40
+ }
41
+ import from jac_coder.tool.run.guarded {
42
+ _set_chat_session,
43
+ _clear_chat_session,
44
+ _chat_contexts,
45
+ _clarification_events,
46
+ _clarification_decisions
47
+ }
34
48
  import from jac_coder.runtime.events {
35
49
  register_event_callback,
36
50
  clear_event_callbacks,
@@ -47,7 +61,7 @@ import from jac_coder.runtime.events {
47
61
  def initialize(mode: str = "web") -> None;
48
62
 
49
63
  """Remove root edges whose target no longer resolves (e.g. after a class-path rename)."""
50
- def _prune_dangling_root_edges() -> int;
64
+ def _prune_dangling_root_edges -> int;
51
65
 
52
66
  """Create a new session."""
53
67
  def create_session(directory: str, title: str = "", agent: str = "main") -> dict;
@@ -65,7 +79,7 @@ def chat(
65
79
  env_overrides: dict = {}
66
80
  ) -> dict;
67
81
 
68
- def list_sessions() -> list;
82
+ def list_sessions -> list;
69
83
 
70
84
  def get_session(session_id: str) -> dict;
71
85
 
@@ -76,7 +90,7 @@ def close_session(session_id: str) -> dict;
76
90
  def api_set_model(model: str) -> dict;
77
91
 
78
92
  """Get current model info."""
79
- def api_get_model() -> dict;
93
+ def api_get_model -> dict;
80
94
 
81
95
  """Check if local model runtime and model weights are ready for a given alias."""
82
96
  def api_check_local_setup(alias: str) -> dict;
@@ -95,7 +109,16 @@ def api_mcp_reconnect(name: str) -> dict;
95
109
  def api_mcp_delete(name: str) -> dict;
96
110
 
97
111
  """List all configured MCP servers."""
98
- def api_mcp_list() -> list;
112
+ def api_mcp_list -> list;
99
113
 
100
114
  """Append an abort note to a session's chat_history (called synchronously from stop handler)."""
101
115
  def abort_session(session_id: str) -> None;
116
+
117
+ """Accept a user's clarification response and unblock the waiting agent thread."""
118
+ def clarification_response(
119
+ session_id: str,
120
+ request_id: str,
121
+ selected_id: str = "",
122
+ custom_text: str = "",
123
+ skipped: bool = False
124
+ ) -> dict;