forgexa-cli 1.62.5__tar.gz → 1.63.0__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 (29) hide show
  1. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/PKG-INFO +1 -1
  2. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/__init__.py +1 -1
  3. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/agent_core.py +19 -12
  4. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/daemon.py +31 -23
  5. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli.egg-info/PKG-INFO +1 -1
  6. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/pyproject.toml +1 -1
  7. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/README.md +0 -0
  8. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/_build_config.py +0 -0
  9. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/_local_bind.py +0 -0
  10. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/autoupgrade.py +0 -0
  11. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/main.py +0 -0
  12. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/py.typed +0 -0
  13. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/runtime_evidence.py +0 -0
  14. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli/zcode_protocol.py +0 -0
  15. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli.egg-info/SOURCES.txt +0 -0
  16. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli.egg-info/dependency_links.txt +0 -0
  17. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli.egg-info/entry_points.txt +0 -0
  18. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli.egg-info/requires.txt +0 -0
  19. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/forgexa_cli.egg-info/top_level.txt +0 -0
  20. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/setup.cfg +0 -0
  21. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_auth_and_runtime_commands.py +0 -0
  22. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_autoupgrade.py +0 -0
  23. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_check_command.py +0 -0
  24. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_expiry_warnings_and_revoke.py +0 -0
  25. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_local_bind_commands.py +0 -0
  26. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_runtime_credentials.py +0 -0
  27. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_session_credentials.py +0 -0
  28. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_silent_install.py +0 -0
  29. {forgexa_cli-1.62.5 → forgexa_cli-1.63.0}/tests/test_upgrade_observability.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.62.5
3
+ Version: 1.63.0
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,2 +1,2 @@
1
1
  """forgexa-cli — Forgexa command-line client."""
2
- __version__ = "1.62.5"
2
+ __version__ = "1.63.0"
@@ -1309,12 +1309,14 @@ def _kill_proc(proc: asyncio.subprocess.Process) -> None:
1309
1309
  (npm, yarn, ssh, git, etc.) spawned by the agent stay alive, keep pipes
1310
1310
  open, and exhaust system resources. ``os.killpg`` sends SIGKILL to the
1311
1311
  whole process group, reliably cleaning up all descendants.
1312
+ Owned agents start a new session, so their PID remains the process-group
1313
+ ID even after the leader exits.
1312
1314
  """
1313
1315
  try:
1314
1316
  if sys.platform != "win32":
1315
1317
  import signal as _signal
1316
1318
  try:
1317
- os.killpg(os.getpgid(proc.pid), _signal.SIGKILL)
1319
+ os.killpg(proc.pid, _signal.SIGKILL)
1318
1320
  except (ProcessLookupError, PermissionError, OSError):
1319
1321
  pass
1320
1322
  else:
@@ -1322,6 +1324,7 @@ def _kill_proc(proc: asyncio.subprocess.Process) -> None:
1322
1324
  _subprocess.run(
1323
1325
  ["taskkill", "/F", "/T", "/PID", str(proc.pid)],
1324
1326
  capture_output=True,
1327
+ timeout=5,
1325
1328
  **_agent_process_group_kwargs(),
1326
1329
  )
1327
1330
  except Exception:
@@ -2462,7 +2465,7 @@ def deepseek_harness_build_info(binary: str) -> dict:
2462
2465
  if result.returncode or not re.fullmatch(r"\d+\.\d+\.\d+", node_version) or parsed is None or not ((parsed[0] == 22 and parsed >= (22, 19, 0)) or parsed[0] >= 24):
2463
2466
  raise RuntimeError("DeepSeek Harness requires Node 22.19+ or Node 24+")
2464
2467
  digest = hashlib.sha256()
2465
- digest.update(f"{sys.platform}:{platform.machine()}:{platform.release()}:{node_version}:harness-policy-3".encode())
2468
+ digest.update(f"{sys.platform}:{platform.machine()}:{platform.release()}:{node_version}:harness-policy-4".encode())
2466
2469
  extensions = [manifest.parent for manifest in (roots[0] / "node_modules" / "@deepseek-ai").glob("dsh*/package.json")]
2467
2470
  for root in sorted(set([*roots, *extensions])):
2468
2471
  files = [root / "package.json"] + sorted((root / "lib").rglob("*.js"))
@@ -2592,10 +2595,15 @@ def deepseek_harness_overlay(*, cwd: Path, credentials_path: Path, provider: str
2592
2595
  "watch": False,
2593
2596
  }},
2594
2597
  {"id": "tool-skill", "disabled": False},
2598
+ {"id": "tool-subagent", "disabled": False, "config": {
2599
+ "provider": "spawn", "enableRunInBackground": False,
2600
+ "backgroundMode": "one-shot", "maxDepth": 1,
2601
+ "modelSelectionSettings": False,
2602
+ }},
2595
2603
  ]
2596
2604
  disabled = (
2597
2605
  "session-telemetry-otel", "hmr", "plugin-manager", "tool-plugin-manager",
2598
- "llm-pi-ai", "tool-subagent", "tool-subagent-fork",
2606
+ "llm-pi-ai", "tool-subagent-fork",
2599
2607
  "tool-subagent-control", "tool-subagent-list-agents", "tool-workflow",
2600
2608
  "goal-round-driver", "command-goal", "tool-goal", "command-feedback", "tool-web",
2601
2609
  )
@@ -2862,7 +2870,7 @@ def mimo_base_env(env: dict[str, str]) -> dict[str, str]:
2862
2870
  "git * reset --hard*": "deny",
2863
2871
  },
2864
2872
  "external_directory": "deny",
2865
- "task": "deny",
2873
+ "task": "allow",
2866
2874
  "skill": {"*": "allow", "compose:*": "deny"},
2867
2875
  "webfetch": "deny",
2868
2876
  "websearch": "deny",
@@ -3168,9 +3176,9 @@ def zcode_build_verification_issue(binary: str | Path, env: dict[str, str] | Non
3168
3176
  return None
3169
3177
 
3170
3178
 
3171
- ZCODE_ALLOWED_TOOLS = ("Read", "Glob", "Grep", "Edit", "Write", "Skill")
3179
+ ZCODE_ALLOWED_TOOLS = ("Read", "Glob", "Grep", "Edit", "Write", "Skill", "Agent", "Task")
3172
3180
  ZCODE_DENIED_TOOLS = (
3173
- "Bash", "MCP", "Plugin", "Task", "WebFetch", "WebSearch",
3181
+ "Bash", "MCP", "Plugin", "WebFetch", "WebSearch",
3174
3182
  )
3175
3183
 
3176
3184
 
@@ -3247,10 +3255,8 @@ def agent_execution_verification_issue(agent_id: str, binary: str) -> str | None
3247
3255
  return None
3248
3256
 
3249
3257
 
3250
- _FORGEXA_CODING_AGENT_TOOLS = ("Read", "Glob", "Grep", "Edit", "Write", "Bash", "Skill")
3258
+ _FORGEXA_CODING_AGENT_TOOLS = ("Read", "Glob", "Grep", "Edit", "Write", "Bash", "Skill", "Agent", "Task")
3251
3259
  _FORGEXA_CODING_AGENT_DENIED_TOOLS = (
3252
- "Agent",
3253
- "Task",
3254
3260
  "SlashCommand",
3255
3261
  "WebFetch",
3256
3262
  "WebSearch",
@@ -3655,9 +3661,10 @@ def build_copilot_command(
3655
3661
 
3656
3662
  ``prompt_via_stdin`` omits ``-p`` so the caller pipes the prompt into
3657
3663
  stdin instead - verified live on CLI 1.0.86, where piped stdin is a full
3658
- non-interactive prompt source. The host uses it when the argv form would
3659
- exceed the platform command-line limit (inlined attachments can push the
3660
- prompt past Windows' 32k-char CreateProcess ceiling).
3664
+ non-interactive prompt source. The daemon uses it for native launchers
3665
+ to avoid single-argument and total command-line limits; Windows batch
3666
+ wrappers retain task-file redirection. The host uses it when the argv
3667
+ form would exceed the platform command-line limit.
3661
3668
  """
3662
3669
  cmd = [
3663
3670
  binary,
@@ -1196,7 +1196,7 @@ except (ImportError, ModuleNotFoundError):
1196
1196
  # DAEMON_VERSION is the protocol/logic version of the daemon code.
1197
1197
  # Kept in sync with pyproject.toml version via bump-version.sh.
1198
1198
  # CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
1199
- DAEMON_VERSION = "1.62.5"
1199
+ DAEMON_VERSION = "1.63.0"
1200
1200
 
1201
1201
 
1202
1202
  def _detect_client_type() -> str:
@@ -6525,10 +6525,11 @@ class ProcessManager:
6525
6525
  _last_activity_at: list[float] = [time.monotonic()]
6526
6526
 
6527
6527
  # Write prompt and close stdin so the agent knows input is done.
6528
- if stdin_input and proc.stdin:
6528
+ if proc.stdin:
6529
6529
  try:
6530
- proc.stdin.write(stdin_input)
6531
- await proc.stdin.drain()
6530
+ if stdin_input:
6531
+ proc.stdin.write(stdin_input)
6532
+ await proc.stdin.drain()
6532
6533
  except Exception:
6533
6534
  pass
6534
6535
  proc.stdin.close()
@@ -7668,7 +7669,8 @@ class ProcessManager:
7668
7669
  explicitly configured supported token environment variable.
7669
7670
 
7670
7671
  Flags:
7671
- -p / --prompt Non-interactive prompt (exits after completion).
7672
+ stdin Full non-interactive prompt, avoiding argv limits.
7673
+ -p / --prompt Short task-file instruction for Windows wrappers.
7672
7674
  --output-format json JSONL stream of session events.
7673
7675
  --allow-all Grant all tool + path + URL permissions required
7674
7676
  for autonomous file-editing tasks.
@@ -7768,11 +7770,13 @@ class ProcessManager:
7768
7770
  _effective_prompt, _task_file = _redirect_prompt_through_task_file(
7769
7771
  cwd, prompt, task_id, "Copilot",
7770
7772
  )
7773
+ _stdin_input = None if _task_file is not None else prompt.encode("utf-8")
7771
7774
 
7772
7775
  cmd = agent_core.build_copilot_command(
7773
7776
  agent.command,
7774
7777
  cwd=cwd,
7775
7778
  prompt=_effective_prompt,
7779
+ prompt_via_stdin=_stdin_input is not None,
7776
7780
  model=model_override,
7777
7781
  reasoning=reasoning if not _retry_without_effort else None,
7778
7782
  disable_builtin_mcps=disable_builtin_mcps,
@@ -7786,9 +7790,20 @@ class ProcessManager:
7786
7790
  # reproduce failures manually and diagnose CLI version incompatibilities.
7787
7791
  _cmd_display = " ".join(
7788
7792
  f'"{a}"' if " " in a else a
7789
- for a in cmd[:-1] # omit the -p value (potentially huge/sensitive)
7790
- ) + ' -p "<prompt>"'
7791
- logger.debug("Copilot invocation for task %s: %s", task_id, _cmd_display)
7793
+ for a in (cmd if _stdin_input is not None else cmd[:-1])
7794
+ )
7795
+ if _stdin_input is None:
7796
+ _cmd_display += ' "<prompt>"'
7797
+ logger.debug(
7798
+ "Copilot invocation for task %s: %s (prompt via %s)",
7799
+ task_id, _cmd_display, "stdin" if _stdin_input is not None else "task file",
7800
+ )
7801
+ logger.info(
7802
+ "Copilot task %s prompt transport: %s (%d UTF-8 bytes)",
7803
+ task_id,
7804
+ "stdin" if _stdin_input is not None else "task file",
7805
+ len(_stdin_input) if _stdin_input is not None else len(prompt.encode("utf-8")),
7806
+ )
7792
7807
 
7793
7808
  # On Windows, CREATE_NO_WINDOW ensures GetConsoleWindow() returns NULL for
7794
7809
  # the child process, even when the parent (daemon or Tauri app) has a console
@@ -7796,9 +7811,9 @@ class ProcessManager:
7796
7811
  # check via GetConsoleWindow(): if a console window is visible, it uses only
7797
7812
  # the first line of -p as a brief context note and reports "No task was
7798
7813
  # provided" for the rest of the prompt — silently discarding the full task.
7799
- # stdin=DEVNULL (below) covers the primary check (GetConsoleMode on stdin);
7814
+ # A pipe or DEVNULL covers the primary check (GetConsoleMode on stdin);
7800
7815
  # CREATE_NO_WINDOW covers this secondary check so Copilot always sees a fully
7801
- # headless environment and processes -p as the complete task specification.
7816
+ # headless environment and processes the complete task specification.
7802
7817
  proc: asyncio.subprocess.Process | None = None
7803
7818
 
7804
7819
  async def _terminate_copilot_process(
@@ -7817,18 +7832,11 @@ class ProcessManager:
7817
7832
  *cmd,
7818
7833
  stdout=asyncio.subprocess.PIPE,
7819
7834
  stderr=asyncio.subprocess.PIPE,
7820
- # stdin=DEVNULL is critical on Windows.
7821
- # Without it, the child process inherits the parent daemon's stdin.
7822
- # On Windows, the daemon often has a real console handle on stdin
7823
- # (e.g. from a CMD/PowerShell window or the Desktop app's console).
7824
- # Copilot CLI detects this via GetConsoleMode(GetStdHandle(STD_INPUT))
7825
- # and enters interactive/session-check mode, ignoring -p as a task:
7826
- # it checks the session todo table (empty), reports "no tracked work
7827
- # items", and waits for user input instead of executing the task.
7828
- # On Linux/macOS (daemon as service), stdin is typically /dev/null so
7829
- # the bug doesn't manifest. Explicit DEVNULL ensures non-interactive
7830
- # mode on all platforms, matching the intended headless operation.
7831
- stdin=asyncio.subprocess.DEVNULL,
7835
+ stdin=(
7836
+ asyncio.subprocess.PIPE
7837
+ if _stdin_input is not None
7838
+ else asyncio.subprocess.DEVNULL
7839
+ ),
7832
7840
  cwd=str(cwd),
7833
7841
  env=env,
7834
7842
  limit=100 * 1024 * 1024,
@@ -7836,7 +7844,7 @@ class ProcessManager:
7836
7844
  )
7837
7845
  self.active_processes[task_id] = proc
7838
7846
  stdout, stderr, returncode = await self._stream_process(
7839
- proc, None, timeout, task_id, on_chunk,
7847
+ proc, _stdin_input, timeout, task_id, on_chunk,
7840
7848
  workspace_path=cwd,
7841
7849
  stream_stderr=stream_stderr,
7842
7850
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.62.5
3
+ Version: 1.63.0
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "forgexa-cli"
3
- version = "1.62.5"
3
+ version = "1.63.0"
4
4
  description = "Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform"
5
5
  requires-python = ">=3.9"
6
6
  license = "MIT"
File without changes
File without changes