@agentsdock/server 1.0.7-beta.5 → 1.0.8

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 (45) hide show
  1. package/README.md +20 -6
  2. package/package.json +1 -1
  3. package/server/VERSION +1 -1
  4. package/server/agent_server.py +5283 -698
  5. package/server/agentsdock_chats.py +2 -1
  6. package/server/chat_mailbox.py +23 -6
  7. package/server/claude_history_repair.py +45 -4
  8. package/server/claude_model_catalog.py +200 -0
  9. package/server/claude_sdk_client.py +63 -24
  10. package/server/claude_side_question.py +1 -8
  11. package/server/codex_app_server.py +90 -30
  12. package/server/codex_auth.py +119 -0
  13. package/server/codex_history_repair.py +79 -14
  14. package/server/codex_provider.py +160 -77
  15. package/server/codex_side_question.py +154 -57
  16. package/server/cursor_agent_client.py +27 -1
  17. package/server/cursor_api_key.py +64 -0
  18. package/server/cursor_history.py +219 -0
  19. package/server/cursor_provider_mcp.py +290 -0
  20. package/server/execution_durability.py +10 -6
  21. package/server/execution_preparation.py +6 -2
  22. package/server/install.sh +282 -78
  23. package/server/instances.sh +15 -0
  24. package/server/interactive_chat_native.py +4 -3
  25. package/server/interactive_chat_projection.py +6 -19
  26. package/server/interactive_chat_share_routes.py +71 -45
  27. package/server/interactive_chat_share_web.py +2 -2
  28. package/server/interactive_chat_shares.py +6 -11
  29. package/server/local_session_ownership.py +232 -0
  30. package/server/opencode_agent_client.py +685 -0
  31. package/server/provider_commands.py +579 -0
  32. package/server/provider_connections.py +665 -0
  33. package/server/provider_usage.py +241 -0
  34. package/server/public_chat_shares.py +60 -62
  35. package/server/public_chat_transcript.py +9 -31
  36. package/server/pyproject.toml +1 -0
  37. package/server/server_instances.py +931 -0
  38. package/server/shared_chat_video_stream.py +21 -2
  39. package/server/shared_chat_videos.py +54 -24
  40. package/server/side_questions.py +493 -10
  41. package/server/title_generation.py +4 -1
  42. package/server/uninstall.sh +95 -17
  43. package/server/update_preparation.py +4 -1
  44. package/server/update_runner.py +16 -0
  45. package/server/uv.lock +3 -1
@@ -1,6 +1,29 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
+ UNINSTALL_SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
5
+ # Bare uninstall previews ALL instances and requires their exact names. Keep
6
+ # legacy explicit --yes automation default-scoped; --all opts into bulk removal.
7
+ if (($# == 0)); then
8
+ exec bash "$UNINSTALL_SCRIPT_DIR/instances.sh" remove --all
9
+ fi
10
+ for uninstall_option in "$@"; do
11
+ if [[ "$uninstall_option" == "--all" || "$uninstall_option" == "--instance" ]]; then
12
+ exec bash "$UNINSTALL_SCRIPT_DIR/instances.sh" remove "$@"
13
+ fi
14
+ done
15
+ INSTANCE_NAME="${AGENTS_SERVER_INSTANCE:-default}"
16
+ MANAGED_REMOVAL="false"
17
+ if [[ "$1" == "--managed-instance" ]]; then
18
+ MANAGED_REMOVAL="true"
19
+ INSTANCE_NAME="${2:?missing instance name}"
20
+ shift 2
21
+ fi
22
+ if [[ ! "$INSTANCE_NAME" =~ ^[a-z][a-z0-9-]{0,31}$ ]]; then
23
+ echo "Invalid instance name." >&2
24
+ exit 2
25
+ fi
26
+
4
27
  INSTALL_ROOT="${AGENTS_SERVER_INSTALL_DIR:-$HOME/.local/share/agents-server}"
5
28
  CONFIG_ROOT="${AGENTS_SERVER_CONFIG_DIR:-$HOME/.config/agents-server}"
6
29
  LEGACY_STATE_ROOT="$HOME/.zenithbot-agent"
@@ -14,10 +37,29 @@ else
14
37
  STATE_ROOT="$HOME/.agentsdock"
15
38
  fi
16
39
  SERVICE_NAME="agents-server"
40
+ LABEL="com.agentsdock.server"
41
+ INSTANCE_LOG_DIR="$HOME/Library/Logs/AgentsServer"
42
+ if [[ "$INSTANCE_NAME" != "default" ]]; then
43
+ instance_bindings="$(bash "$UNINSTALL_SCRIPT_DIR/instances.sh" _bindings "$INSTANCE_NAME")" || exit 2
44
+ eval "$instance_bindings"
45
+ fi
17
46
  OS_NAME="$(uname -s)"
18
47
 
19
48
  ASSUME_YES="false"
20
49
  PURGE_STATE="false"
50
+ MANAGED_RELEASE_NAME="false"
51
+ UNINSTALL_RED=""
52
+ UNINSTALL_GREEN=""
53
+ UNINSTALL_RESET=""
54
+ if [[ -t 1 && "${TERM:-}" != "dumb" && -z "${NO_COLOR+x}" ]]; then
55
+ UNINSTALL_RED=$'\033[31m'
56
+ UNINSTALL_GREEN=$'\033[32m'
57
+ UNINSTALL_RESET=$'\033[0m'
58
+ fi
59
+
60
+ uninstall_status() {
61
+ printf '%s%s%s\n' "$1" "$2" "$UNINSTALL_RESET"
62
+ }
21
63
 
22
64
  normalize_managed_path() {
23
65
  local label="$1"
@@ -97,6 +139,12 @@ usage() {
97
139
  cat <<USAGE
98
140
  Usage: ./uninstall.sh [--yes] [--purge-state]
99
141
 
142
+ With no arguments: show all current-user instances and confirm their exact names.
143
+ --instance NAME Remove only NAME (preview + confirmation).
144
+ --all Remove all instances (preview + confirmation).
145
+ --all --exclude default Keep the original/default server untouched.
146
+ Legacy --yes without --all remains default-scoped for compatibility.
147
+
100
148
  Stops and removes the AgentsServer user service, versioned release runtime,
101
149
  and generated configuration (including the access token). Chat history, jobs,
102
150
  files, secure-peer credentials, and terminals under $STATE_ROOT are preserved
@@ -117,11 +165,21 @@ while (($#)); do
117
165
  case "$1" in
118
166
  --yes) ASSUME_YES="true"; shift ;;
119
167
  --purge-state) PURGE_STATE="true"; shift ;;
168
+ --managed-release-name) MANAGED_RELEASE_NAME="true"; shift ;;
120
169
  -h|--help) usage; exit 0 ;;
121
170
  *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;;
122
171
  esac
123
172
  done
124
173
 
174
+ # This flag only controls progress text. The manager separately archives state
175
+ # after a successful service removal, using the user's affirmative choice.
176
+ if [[ "$MANAGED_RELEASE_NAME" == "true" \
177
+ && ( "$MANAGED_REMOVAL" != "true" || "$INSTANCE_NAME" == "default" \
178
+ || "$ASSUME_YES" != "true" || "$PURGE_STATE" == "true" ) ]]; then
179
+ echo "--managed-release-name is only for a confirmed named-instance manager removal." >&2
180
+ exit 2
181
+ fi
182
+
125
183
  # The split lifecycle validates its installed layout, obtains an exact idle
126
184
  # execution hold, and owns the shared lock through both native service stops
127
185
  # and filesystem removal. Keep legacy uninstall unchanged for old layouts.
@@ -243,14 +301,15 @@ else
243
301
  echo "Aborted; nothing was changed." >&2
244
302
  exit 1
245
303
  fi
304
+ echo
246
305
 
247
306
  case "$OS_NAME" in
248
307
  Darwin)
249
- LABEL="com.agentsdock.server"
308
+ LABEL="${LABEL:-com.agentsdock.server}"
250
309
  PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
251
310
  SERVICE_TARGET="gui/$(id -u)/$LABEL"
252
311
  if launchctl print "$SERVICE_TARGET" >/dev/null 2>&1; then
253
- echo "Stopping $LABEL"
312
+ uninstall_status "$UNINSTALL_RED" "Stopping $LABEL"
254
313
  if ! launchctl bootout "$SERVICE_TARGET" >/dev/null 2>&1 && \
255
314
  launchctl print "$SERVICE_TARGET" >/dev/null 2>&1; then
256
315
  echo "Could not stop $LABEL; no files were removed." >&2
@@ -270,12 +329,12 @@ case "$OS_NAME" in
270
329
  fi
271
330
  fi
272
331
  if [[ -f "$PLIST" ]]; then
273
- echo "Removing $PLIST"
332
+ uninstall_status "$UNINSTALL_RED" "Removing $PLIST"
274
333
  rm -f "$PLIST"
275
334
  fi
276
- LOG_DIR="$HOME/Library/Logs/AgentsServer"
335
+ LOG_DIR="$INSTANCE_LOG_DIR"
277
336
  if [[ -d "$LOG_DIR" ]]; then
278
- echo "Removing $LOG_DIR"
337
+ uninstall_status "$UNINSTALL_RED" "Removing $LOG_DIR"
279
338
  rm -rf "$LOG_DIR"
280
339
  fi
281
340
  ;;
@@ -286,7 +345,7 @@ case "$OS_NAME" in
286
345
  exit 1
287
346
  fi
288
347
  if [[ -f "$SERVICE_FILE" ]] || systemctl --user is-active --quiet "$SERVICE_NAME.service"; then
289
- echo "Stopping $SERVICE_NAME.service"
348
+ uninstall_status "$UNINSTALL_RED" "Stopping $SERVICE_NAME.service"
290
349
  if ! systemctl --user disable --now "$SERVICE_NAME.service" >/dev/null 2>&1 && \
291
350
  systemctl --user is-active --quiet "$SERVICE_NAME.service"; then
292
351
  echo "Could not stop $SERVICE_NAME.service; no files were removed." >&2
@@ -306,7 +365,7 @@ case "$OS_NAME" in
306
365
  fi
307
366
  fi
308
367
  if [[ -f "$SERVICE_FILE" ]]; then
309
- echo "Removing $SERVICE_FILE"
368
+ uninstall_status "$UNINSTALL_RED" "Removing $SERVICE_FILE"
310
369
  rm -f "$SERVICE_FILE"
311
370
  systemctl --user daemon-reload || true
312
371
  fi
@@ -318,7 +377,7 @@ case "$OS_NAME" in
318
377
  esac
319
378
 
320
379
  if [[ -d "$INSTALL_ROOT" ]]; then
321
- echo "Removing release runtime at $INSTALL_ROOT"
380
+ uninstall_status "$UNINSTALL_RED" "Removing release runtime at $INSTALL_ROOT"
322
381
  shopt -s dotglob nullglob
323
382
  for install_entry in "$INSTALL_ROOT"/*; do
324
383
  [[ "$install_entry" == "$INSTALL_LOCK_DIR" ]] || rm -rf -- "$install_entry"
@@ -327,20 +386,30 @@ if [[ -d "$INSTALL_ROOT" ]]; then
327
386
  fi
328
387
 
329
388
  if [[ -d "$CONFIG_ROOT" ]]; then
330
- echo "Removing configuration at $CONFIG_ROOT"
389
+ uninstall_status "$UNINSTALL_RED" "Removing configuration at $CONFIG_ROOT"
331
390
  rm -rf "$CONFIG_ROOT"
332
391
  fi
333
392
 
334
393
  if [[ "$PURGE_STATE" == "true" ]]; then
335
394
  if [[ -e "$STATE_ROOT" || -L "$LEGACY_STATE_ROOT" ]]; then
336
- [[ ! -L "$LEGACY_STATE_ROOT" ]] || rm -f "$LEGACY_STATE_ROOT"
395
+ if [[ "$INSTANCE_NAME" == "default" ]]; then
396
+ [[ ! -L "$LEGACY_STATE_ROOT" ]] || rm -f "$LEGACY_STATE_ROOT"
397
+ fi
337
398
  [[ ! -e "$STATE_ROOT" ]] || rm -rf "$STATE_ROOT"
338
- echo "Deleted $STATE_ROOT"
399
+ uninstall_status "$UNINSTALL_RED" "Deleted $STATE_ROOT"
400
+ fi
401
+ elif [[ -e "$STATE_ROOT" && "$MANAGED_RELEASE_NAME" != "true" ]]; then
402
+ echo
403
+ uninstall_status "$UNINSTALL_GREEN" "Preserved chat history, jobs, files, tokens, and secure-peer credentials at $STATE_ROOT."
404
+ echo
405
+ if [[ "$INSTANCE_NAME" == "default" ]]; then
406
+ echo "Re-running ./install.sh will pick ordinary AgentsServer state back up. Pass --purge-state to also delete it."
407
+ else
408
+ echo "To reuse this named history, run ./install.sh --instance $INSTANCE_NAME --port PORT (choose an available port)."
409
+ echo "Do not use bare ./install.sh for this instance; it targets the default server. Pass --purge-state to also delete history."
339
410
  fi
340
- elif [[ -e "$STATE_ROOT" ]]; then
341
- echo "Preserved chat history, jobs, files, tokens, and secure-peer credentials at $STATE_ROOT."
342
- echo "Re-running ./install.sh will pick ordinary AgentsServer state back up. Pass --purge-state to also delete it."
343
411
  if [[ -e "$STATE_ROOT/team-hub/team-hub.sqlite3" || -L "$STATE_ROOT/team-hub/team-hub.sqlite3" ]]; then
412
+ echo
344
413
  echo "Preserved Team Hub state is not auto-reactivated in this beta; use a signed managed recovery or support-assisted restoration."
345
414
  fi
346
415
  fi
@@ -354,6 +423,15 @@ if [[ -d "$INSTALL_ROOT" ]] && ! rmdir "$INSTALL_ROOT"; then
354
423
  exit 1
355
424
  fi
356
425
 
357
- echo "AgentsServer service, release runtime, and configuration removed."
358
- echo "Note: any persistent chat terminals (tmux sessions named zd_*) keep running"
359
- echo "independently and are not touched by this script. List them with: tmux ls"
426
+ echo
427
+ if [[ "$INSTANCE_NAME" == "default" ]]; then
428
+ echo "Note: any persistent chat terminals (tmux sessions named zd_*) keep running"
429
+ echo "independently and are not touched by this script. List them with: tmux ls"
430
+ else
431
+ echo "Note: persistent chat terminals for $INSTANCE_NAME keep running independently and are not touched by this script."
432
+ echo "List only this instance's terminals with: tmux -L agents-server-$INSTANCE_NAME ls"
433
+ fi
434
+ if [[ "$MANAGED_REMOVAL" != "true" ]]; then
435
+ echo
436
+ uninstall_status "$UNINSTALL_GREEN" "Successful!"
437
+ fi
@@ -217,7 +217,10 @@ def run_preparation(*, status_path: Path, root: Path, public_key: Path, preparat
217
217
  archive.write_bytes(data)
218
218
  source = updates.safe_extract(archive, work / "extracted",
219
219
  npm_manifest=manifest if manifest.get("schema") == 2 else None)
220
- if not (source / "execution_preparation.py").is_file():
220
+ instance_arguments = updates.instance_installer_arguments(source)
221
+ if instance_arguments or not (source / "execution_preparation.py").is_file():
222
+ # Named instances use their isolated legacy service, not the
223
+ # default-only split gateway preparation/activation protocol.
221
224
  # Older signed releases retain their established installer.
222
225
  prepared = {"mode": "legacy", "preparation_id": preparation_id, "envelope": envelope}
223
226
  else:
@@ -335,6 +335,21 @@ def installer_environment() -> dict[str, str]:
335
335
  return environment
336
336
 
337
337
 
338
+ def instance_installer_arguments(source: Path) -> list[str]:
339
+ """Never let a named server run an old default-only installer."""
340
+ from server_instances import instance_name
341
+ name = instance_name(os.environ.get("AGENTS_SERVER_INSTANCE", "default"))
342
+ if name == "default":
343
+ return [] # Preserve older signed default installers.
344
+ helper = source / "server_instances.py"
345
+ installer = source / "install.sh"
346
+ if (not helper.is_file() or helper.is_symlink()
347
+ or "INSTANCE_PROTOCOL = 1" not in helper.read_text()
348
+ or '--instance)' not in installer.read_text()):
349
+ raise RuntimeError("This release does not support named instances; the existing server was not replaced.")
350
+ return ["--instance", name]
351
+
352
+
338
353
  def run_installer(
339
354
  command: list[str],
340
355
  *,
@@ -1549,6 +1564,7 @@ def run_update(args: argparse.Namespace) -> None:
1549
1564
  command.extend(["--execution-handoff-file", handoff_file])
1550
1565
  if manifest.get("schema") == 2 or prepared_update is not None:
1551
1566
  command.extend(["--expected-api-contract", str(manifest["api_contract_version"])])
1567
+ command.extend(instance_installer_arguments(source))
1552
1568
  if expected_team_hub_id is not None:
1553
1569
  command.extend(
1554
1570
  [
package/server/uv.lock CHANGED
@@ -30,6 +30,7 @@ dependencies = [
30
30
  { name = "croniter" },
31
31
  { name = "cryptography" },
32
32
  { name = "fastapi" },
33
+ { name = "httpx" },
33
34
  { name = "pydantic" },
34
35
  { name = "python-dateutil" },
35
36
  { name = "python-multipart" },
@@ -44,6 +45,7 @@ requires-dist = [
44
45
  { name = "croniter", specifier = ">=6,<7" },
45
46
  { name = "cryptography", specifier = ">=50.0.1,<51" },
46
47
  { name = "fastapi", specifier = ">=0.115,<1" },
48
+ { name = "httpx", specifier = ">=0.28.1,<1" },
47
49
  { name = "pydantic", specifier = ">=2.10,<3" },
48
50
  { name = "python-dateutil", specifier = ">=2.9,<3" },
49
51
  { name = "python-multipart", specifier = ">=0.0.20,<1" },
@@ -326,7 +328,7 @@ name = "exceptiongroup"
326
328
  version = "1.3.1"
327
329
  source = { registry = "https://pypi.org/simple" }
328
330
  dependencies = [
329
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
331
+ { name = "typing-extensions" },
330
332
  ]
331
333
  sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
332
334
  wheels = [