@agentsdock/server 1.0.11-beta.2 → 1.0.12-beta.1

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.
@@ -2369,17 +2369,20 @@ class CodexAppServerClient:
2369
2369
  validated["turn"] = _protocol_turn(method, result.get("turn"))
2370
2370
  return validated
2371
2371
 
2372
- async def run_thread_shell_command(self, thread_id: str, command: str) -> None:
2372
+ async def run_thread_shell_command(
2373
+ self, thread_id: str, command: str, *,
2374
+ before_send: Callable[[], bool] | None = None,
2375
+ ) -> None:
2373
2376
  thread_id = _require_nonempty_string(thread_id, "thread_id")
2374
2377
  command = _require_nonempty_string(command, "command")
2375
2378
  method = "thread/shellCommand"
2376
- _protocol_empty_object(
2377
- method,
2378
- await self.request(
2379
- method,
2380
- {"threadId": thread_id, "command": command},
2381
- ),
2382
- )
2379
+ params = {"threadId": thread_id, "command": command}
2380
+ if before_send is None:
2381
+ result = await self.request(method, params)
2382
+ else:
2383
+ await self.start()
2384
+ result = await self._request_connected(method, params, before_send=before_send)
2385
+ _protocol_empty_object(method, result)
2383
2386
 
2384
2387
  async def list_background_terminals(
2385
2388
  self,
@@ -3011,8 +3014,14 @@ class CodexAppServerManager:
3011
3014
  delivery=delivery,
3012
3015
  )
3013
3016
 
3014
- async def run_thread_shell_command(self, thread_id: str, command: str) -> None:
3015
- await self.client.run_thread_shell_command(thread_id, command)
3017
+ async def run_thread_shell_command(
3018
+ self, thread_id: str, command: str, *,
3019
+ before_send: Callable[[], bool] | None = None,
3020
+ ) -> None:
3021
+ if before_send is None:
3022
+ await self.client.run_thread_shell_command(thread_id, command)
3023
+ else:
3024
+ await self.client.run_thread_shell_command(thread_id, command, before_send=before_send)
3016
3025
 
3017
3026
  async def list_background_terminals(
3018
3027
  self,
@@ -23,7 +23,7 @@ SIDE_INSTRUCTIONS = """Answer questions and explore in this separate side chat w
23
23
 
24
24
  Treat inherited messages, tasks, plans, tool calls and approvals as reference material, not current instructions or permission. Follow only requests made in this side chat; do not resume unfinished parent work.
25
25
 
26
- Use the thread's existing permissions and available tools, including external tools, to read or search files and run checks that leave repo-tracked files unchanged. Do not create, contact or control subagents.
26
+ Use the thread's existing permissions and available tools, including external tools and subagents. Delegate side-chat work only to subagents created in this side chat; inherited parent agents belong to the main conversation.
27
27
 
28
28
  Change files, git state, configuration, permissions or other workspace state only when explicitly requested here. Request escalation only when such an explicit mutation requires it. Keep authorized changes limited to the request and preserve ongoing parent work."""
29
29
 
@@ -68,8 +68,6 @@ def side_chat_config(parent_config: dict | None = None) -> dict:
68
68
  if isinstance(servers, dict):
69
69
  servers.pop(name, None)
70
70
  config.update({
71
- "features.multi_agent": False, "features.multi_agent_v2": False,
72
- "agents.enabled": False,
73
71
  f"{prefix}.enabled": False, f"{prefix}.required": False,
74
72
  # Codex validates transport shape even for a disabled server. Never
75
73
  # copy the parent's authenticated helper URL/headers into this child.
@@ -182,6 +180,9 @@ class NativeCodexSideChat:
182
180
  self._progress = None
183
181
  self._lock = asyncio.Lock()
184
182
  self._closed = False
183
+ self._thread_parents: dict[str, str] = {}
184
+ self._owned_threads: set[str] = set()
185
+ self._work_revision = 0
185
186
 
186
187
  @property
187
188
  def closed(self) -> bool:
@@ -193,12 +194,88 @@ class NativeCodexSideChat:
193
194
  return None
194
195
  return deepcopy(list(self._progress.items.values()))
195
196
 
197
+ def owns_thread(self, thread_id: str) -> bool:
198
+ """Only the side fork and provider-verified descendants own approvals."""
199
+ return bool(not self._closed and self.thread_id and thread_id != self.parent_thread_id
200
+ and (thread_id == self.thread_id or thread_id in self._owned_threads))
201
+
202
+ def _remember_thread(self, thread):
203
+ if not isinstance(thread, dict):
204
+ return
205
+ thread_id = thread.get("id")
206
+ source = thread.get("source")
207
+ subagent = source.get("subAgent") if isinstance(source, dict) else None
208
+ spawn = subagent.get("thread_spawn") if isinstance(subagent, dict) else None
209
+ parent = spawn.get("parent_thread_id") if isinstance(spawn, dict) else None
210
+ if isinstance(thread_id, str) and isinstance(parent, str) and parent:
211
+ self._thread_parents[thread_id] = parent
212
+
213
+ def _remember_notification(self, notification):
214
+ if notification.get("method") in {"thread/started", "thread/closed", "thread/status/changed", "turn/started", "turn/completed"}:
215
+ self._work_revision += 1
216
+ if notification.get("method") == "thread/started":
217
+ self._remember_thread((notification.get("params") or {}).get("thread"))
218
+
219
+ def background_work_scope(self):
220
+ client = self._client
221
+ if self._closed or client is None or not client.ready or not self.thread_id:
222
+ return None
223
+ return (self, client, client.generation, self.thread_id, self._work_revision)
224
+
225
+ async def has_background_work(self):
226
+ scope = self.background_work_scope()
227
+ if scope is None:
228
+ return False
229
+ _owner, client, _generation, thread_id, _revision = scope
230
+ descendants = await client.list_descendant_threads(thread_id)
231
+ if self.background_work_scope() != scope:
232
+ raise SideQuestionError(409, "Side chat changed during background inspection")
233
+ # This native relationship-filtered API sees descendants even when
234
+ # app-server omitted their thread/started notification.
235
+ for child in descendants:
236
+ child_id = child.get("id")
237
+ status = child.get("status")
238
+ kind = status.get("type") if isinstance(status, dict) else None
239
+ if child_id in {thread_id, self.parent_thread_id} or not isinstance(child_id, str):
240
+ raise SideQuestionError(502, "Codex returned an invalid side descendant")
241
+ if kind == "active":
242
+ return True
243
+ if kind not in {"idle", "notLoaded"}:
244
+ raise SideQuestionError(502, "Codex side descendant activity is unknown")
245
+ return False
246
+
247
+ async def _verify_owned_thread(self, thread_id: str) -> bool:
248
+ if self.owns_thread(thread_id):
249
+ return True
250
+ if not self.thread_id or self._client is None or self._closed:
251
+ return False
252
+ path: set[str] = set()
253
+ current = thread_id
254
+ while current and current not in path and current != self.parent_thread_id:
255
+ if self.owns_thread(current):
256
+ self._owned_threads.update(path)
257
+ return True
258
+ path.add(current)
259
+ if current not in self._thread_parents:
260
+ try:
261
+ thread = await self._client.read_thread(current, include_turns=False)
262
+ except CodexAppServerError:
263
+ return False
264
+ if self._closed or not isinstance(thread, dict) or thread.get("id") != current:
265
+ return False
266
+ self._remember_thread(thread)
267
+ current = self._thread_parents.get(current, "")
268
+ return False
269
+
196
270
  async def _handle_server_request(self, request_id, method, params):
197
- # The callback can present approvals through the existing UI, but only
198
- # for this exact child. Never route a parent's inherited tool request.
271
+ # Provider lineage permits side-chat descendants without granting a
272
+ # parent's inherited tool request or unrelated thread any authority.
199
273
  if (self.server_request_handler is not None and not self._closed
200
- and self._active is not None and self.thread_id is not None
201
- and params.get("threadId") == self.thread_id):
274
+ and self.thread_id is not None
275
+ and isinstance(params.get("threadId"), str)
276
+ and await self._verify_owned_thread(params["threadId"])
277
+ and not self._closed
278
+ and (self._active is not None or params["threadId"] != self.thread_id)):
202
279
  return await self.server_request_handler(request_id, method, params)
203
280
  return await decline_server_request(request_id, method, params)
204
281
 
@@ -241,6 +318,7 @@ class NativeCodexSideChat:
241
318
  on_process_started=lambda _pid, _group: self._process_started.set(),
242
319
  **client_options,
243
320
  )
321
+ self._client.add_notification_handler(self._remember_notification)
244
322
  await self._client.start()
245
323
  if self._closed:
246
324
  raise SideQuestionError(409, "Side chat was closed; open a new side chat")
@@ -18,6 +18,7 @@ draining; this module deliberately contains no process lifecycle state.
18
18
 
19
19
  from __future__ import annotations
20
20
 
21
+ import hashlib
21
22
  import json
22
23
  import re
23
24
  from typing import Any, Iterator
@@ -31,6 +32,13 @@ CURSOR_SESSION_IDENTIFIER_RE = re.compile(
31
32
  r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$"
32
33
  )
33
34
  CURSOR_CALL_ID_MAX_CHARS = 240
35
+ # Cursor 2026.10.22 emits this compound correlation key even for a normal
36
+ # file-read tool. LF is part of the opaque ID, not a second stream event.
37
+ _CURSOR_UUID_PATTERN = r"[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}"
38
+ _CURSOR_COMPOSITE_CALL_ID_RE = re.compile(
39
+ rf"call-{_CURSOR_UUID_PATTERN}-[0-9]+\nfc_{_CURSOR_UUID_PATTERN}_[0-9]+"
40
+ )
41
+ _CURSOR_ENCODED_CALL_ID_PREFIX = "cursor-call-sha256:"
34
42
  CURSOR_LIFECYCLE_VALUE_MAX = 2_147_483_647
35
43
  CURSOR_ANSI_ESCAPE_RE = re.compile(r"\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1b\\))")
36
44
  CURSOR_TOOL_SUCCESS_OUTCOMES = (
@@ -90,7 +98,13 @@ def canonical_cursor_session_id(value: Any) -> str | None:
90
98
 
91
99
 
92
100
  def canonical_cursor_call_id(value: Any) -> str:
93
- """Validate the opaque correlation key used by Cursor tool events."""
101
+ """Bound an opaque tool key and encode the observed compound CLI format.
102
+
103
+ Keep the complete key in the digest: stripping LF, keeping only one half,
104
+ or assigning a fresh ID per event could merge distinct tools or orphan a
105
+ completion. The encoded namespace is reserved so literal provider IDs
106
+ cannot collide with it. Arbitrary control characters remain invalid.
107
+ """
94
108
 
95
109
  if not isinstance(value, str):
96
110
  raise CursorEventParseError("tool_call call_id must be a string")
@@ -98,11 +112,17 @@ def canonical_cursor_call_id(value: Any) -> str:
98
112
  not value
99
113
  or value != value.strip()
100
114
  or len(value) > CURSOR_CALL_ID_MAX_CHARS
101
- or any(ord(character) < 32 or ord(character) == 127 for character in value)
102
115
  ):
103
116
  raise CursorEventParseError(
104
117
  "tool_call call_id must be a bounded nonempty identifier"
105
118
  )
119
+ composite = _CURSOR_COMPOSITE_CALL_ID_RE.fullmatch(value) is not None
120
+ if not composite and any(ord(character) < 32 or ord(character) == 127 for character in value):
121
+ raise CursorEventParseError(
122
+ "tool_call call_id must be a bounded nonempty identifier"
123
+ )
124
+ if composite or value.startswith(_CURSOR_ENCODED_CALL_ID_PREFIX):
125
+ return _CURSOR_ENCODED_CALL_ID_PREFIX + hashlib.sha256(value.encode("utf-8", "surrogatepass")).hexdigest()
106
126
  return value
107
127
 
108
128
 
package/server/install.sh CHANGED
@@ -1387,6 +1387,13 @@ print_token_for_copy() {
1387
1387
  local copy_reply=""
1388
1388
  local clipboard_command=()
1389
1389
  printf '\nAccess token (%s):\n%s\n\n' "$INSTANCE_NAME" "$access_token"
1390
+ # Render locally, only in an interactive terminal. No credentials in argv.
1391
+ local pairing_script="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/npm/agentsdock/pair.cjs"
1392
+ if [[ -f "$pairing_script" ]] && command -v node >/dev/null 2>&1; then
1393
+ node "$pairing_script" "$INSTANCE_NAME" || true
1394
+ else
1395
+ printf 'Show a connection QR with: agentsdock pair %s\n' "$INSTANCE_NAME"
1396
+ fi
1390
1397
  # A remote clipboard is not the user's local clipboard. Do not emit OSC 52
1391
1398
  # sequences or attempt to modify a remote desktop's clipboard over SSH.
1392
1399
  if [[ -n "${SSH_CONNECTION:-}${SSH_CLIENT:-}${SSH_TTY:-}" ]]; then
@@ -1839,7 +1846,7 @@ if [[ "$TEAM_HUB_MODE" == "host" && "$TEAM_HUB_OPERATION_PENDING" != "true" \
1839
1846
  fi
1840
1847
  fi
1841
1848
 
1842
- RELEASE_FILES=(activation_transaction.py execution_activation.py execution_legacy_runner.py execution_preparation.py update_preparation.py update_handoff.py update_recovery.py execution_update_status.py execution_uninstall.py execution_recovery.py execution_recovery_status.py execution_http.py execution_durability.py execution_control.py execution_install.py execution_maintenance.py execution_manage.py execution_ownership.py execution_service.py execution_transport.py agent_server.py local_session_ownership.py cursor_history.py workspace_git.py team_hub_host.py secure_peer_runtime.py team_mail_runtime.py team_mail_websocket.py team_mail_grants.py secure_peer_delivery.py agentsdock_jobs.py agentsdock_chats.py chat_mailbox.py agentsdock_emergency.py agentsdock_publish.py agentsdock_mail.py agentsdock_team.py provider_commands.py provider_usage.py claude_sdk_client.py claude_model_catalog.py claude_goals.py claude_background_reconciliation.py codex_app_server.py codex_auth.py codex_provider.py codex_response_stream.py provider_connections.py cursor_api_key.py side_questions.py title_generation.py codex_side_question.py codex_side_chat_progress.py claude_side_question.py cursor_agent_client.py cursor_provider_mcp.py opencode_agent_client.py gemini_acp_client.py gemini_history.py gemini_content.py gemini_import.py gemini_runtime.py gemini_runner.py gemini_permissions.py gemini_api.py cursor_process_guard.py transcript_reader.py claude_history_repair.py claude_history_provenance.py codex_history_repair.py public_chat_shares.py public_chat_transcript.py public_chat_share_routes.py interactive_chat_shares.py interactive_chat_share_routes.py interactive_chat_share_web.py interactive_chat_projection.py interactive_chat_runtime.py interactive_chat_native.py shared_chat_videos.py shared_chat_video_stream.py interactive_chat_controls.py install.sh uninstall.sh instances.sh server_instances.py update_runner.py pyproject.toml uv.lock VERSION release-public-key.pem LICENSE NOTICE)
1849
+ RELEASE_FILES=(activation_transaction.py execution_activation.py execution_legacy_runner.py execution_preparation.py update_preparation.py update_handoff.py update_recovery.py execution_update_status.py execution_uninstall.py execution_recovery.py execution_recovery_status.py execution_http.py execution_durability.py execution_control.py execution_install.py execution_maintenance.py execution_manage.py execution_ownership.py execution_service.py execution_transport.py agent_server.py local_session_ownership.py cursor_history.py workspace_git.py team_hub_host.py secure_peer_runtime.py team_mail_runtime.py team_mail_websocket.py team_mail_grants.py secure_peer_delivery.py agentsdock_jobs.py agentsdock_chats.py chat_mailbox.py agentsdock_emergency.py agentsdock_notify.py agentsdock_publish.py agentsdock_mail.py agentsdock_team.py provider_commands.py provider_usage.py session_unread.py claude_sdk_client.py claude_model_catalog.py claude_goals.py claude_background_reconciliation.py codex_app_server.py codex_auth.py codex_provider.py codex_response_stream.py provider_connections.py cursor_api_key.py side_questions.py title_generation.py codex_side_question.py codex_side_chat_progress.py claude_side_question.py cursor_agent_client.py cursor_provider_mcp.py opencode_agent_client.py gemini_acp_client.py gemini_history.py gemini_content.py gemini_import.py gemini_runtime.py gemini_runner.py gemini_permissions.py gemini_api.py cursor_process_guard.py transcript_reader.py claude_history_repair.py claude_history_provenance.py codex_history_repair.py public_chat_shares.py public_chat_transcript.py public_chat_share_routes.py interactive_chat_shares.py interactive_chat_share_routes.py interactive_chat_share_web.py interactive_chat_projection.py interactive_chat_runtime.py interactive_chat_native.py shared_chat_videos.py shared_chat_analytics_web.py shared_chat_video_stream.py interactive_chat_controls.py install.sh uninstall.sh instances.sh server_instances.py npm/agentsdock/pair.cjs npm/agentsdock/qr-renderer.cjs npm/agentsdock/QR-NOTICES.txt update_runner.py pyproject.toml uv.lock VERSION release-public-key.pem LICENSE NOTICE)
1843
1850
  RELEASE_DIRECTORIES=(agentsdock_team_hub)
1844
1851
  TEAM_HUB_RELEASE_FILES=(
1845
1852
  __init__.py
@@ -1880,6 +1887,12 @@ TEAM_HUB_RELEASE_FILES=(
1880
1887
  migrations/0023_team_message_search.sql
1881
1888
  )
1882
1889
 
1890
+ for directory in npm npm/agentsdock; do
1891
+ if [[ ! -d "$SOURCE_DIR/$directory" || -L "$SOURCE_DIR/$directory" ]]; then
1892
+ echo "$directory is missing beside install.sh or is not a real directory." >&2
1893
+ exit 1
1894
+ fi
1895
+ done
1883
1896
  for name in "${RELEASE_FILES[@]}"; do
1884
1897
  if [[ ! -f "$SOURCE_DIR/$name" || -L "$SOURCE_DIR/$name" ]]; then
1885
1898
  echo "$name is missing beside install.sh or is not a regular release file." >&2
@@ -3740,11 +3753,13 @@ validate_staged_release_runtime() (
3740
3753
  "$STAGE_DIR/agentsdock_chats.py" \
3741
3754
  "$STAGE_DIR/chat_mailbox.py" \
3742
3755
  "$STAGE_DIR/agentsdock_emergency.py" \
3756
+ "$STAGE_DIR/agentsdock_notify.py" \
3743
3757
  "$STAGE_DIR/agentsdock_publish.py" \
3744
3758
  "$STAGE_DIR/agentsdock_mail.py" \
3745
3759
  "$STAGE_DIR/agentsdock_team.py" \
3746
3760
  "$STAGE_DIR/provider_commands.py" \
3747
3761
  "$STAGE_DIR/provider_usage.py" \
3762
+ "$STAGE_DIR/session_unread.py" \
3748
3763
  "$STAGE_DIR/claude_sdk_client.py" \
3749
3764
  "$STAGE_DIR/claude_model_catalog.py" \
3750
3765
  "$STAGE_DIR/claude_goals.py" \
@@ -3785,12 +3800,13 @@ validate_staged_release_runtime() (
3785
3800
  "$STAGE_DIR/interactive_chat_runtime.py" \
3786
3801
  "$STAGE_DIR/interactive_chat_native.py" \
3787
3802
  "$STAGE_DIR/shared_chat_videos.py" \
3803
+ "$STAGE_DIR/shared_chat_analytics_web.py" \
3788
3804
  "$STAGE_DIR/shared_chat_video_stream.py" \
3789
3805
  "$STAGE_DIR/interactive_chat_controls.py" \
3790
3806
  "$STAGE_DIR/update_runner.py"
3791
3807
  "$STAGE_DIR/.venv/bin/python" -m py_compile "$STAGE_DIR/server_instances.py"
3792
3808
  "$STAGE_DIR/.venv/bin/python" -m compileall -q "$STAGE_DIR/agentsdock_team_hub"
3793
- PYTHONPATH="$STAGE_DIR" "$STAGE_DIR/.venv/bin/python" -c 'import execution_activation, execution_preparation, update_preparation, update_handoff, update_recovery, execution_update_status, execution_uninstall, execution_recovery, execution_recovery_status, execution_http, execution_durability, execution_control, execution_install, execution_maintenance, execution_manage, execution_ownership, execution_service, execution_transport; import workspace_git, claude_model_catalog, server_instances, local_session_ownership, cursor_history; import codex_auth, codex_provider, codex_response_stream, provider_connections, cursor_api_key, side_questions, title_generation, codex_side_question, codex_side_chat_progress, claude_side_question, agentsdock_team_hub, cursor_agent_client, cursor_provider_mcp, opencode_agent_client, gemini_acp_client, gemini_history, gemini_content, gemini_import, gemini_runtime, gemini_runner, gemini_permissions, gemini_api, cursor_process_guard, secure_peer_delivery, secure_peer_runtime, team_mail_runtime, team_mail_websocket, team_mail_grants, team_hub_host, agentsdock_mail, agentsdock_team, transcript_reader, claude_history_repair, claude_history_provenance, claude_goals, claude_background_reconciliation, codex_history_repair, public_chat_shares, public_chat_transcript, public_chat_share_routes, interactive_chat_shares, interactive_chat_share_routes, interactive_chat_share_web, interactive_chat_projection, interactive_chat_runtime, interactive_chat_native, shared_chat_videos, shared_chat_video_stream, interactive_chat_controls, chat_mailbox, provider_commands, provider_usage; from agentsdock_team_hub import secure_peer, secure_peer_hub' >/dev/null
3809
+ PYTHONPATH="$STAGE_DIR" "$STAGE_DIR/.venv/bin/python" -c 'import execution_activation, execution_preparation, update_preparation, update_handoff, update_recovery, execution_update_status, execution_uninstall, execution_recovery, execution_recovery_status, execution_http, execution_durability, execution_control, execution_install, execution_maintenance, execution_manage, execution_ownership, execution_service, execution_transport; import workspace_git, claude_model_catalog, server_instances, local_session_ownership, cursor_history; import codex_auth, codex_provider, codex_response_stream, provider_connections, cursor_api_key, side_questions, title_generation, codex_side_question, codex_side_chat_progress, claude_side_question, agentsdock_team_hub, cursor_agent_client, cursor_provider_mcp, opencode_agent_client, gemini_acp_client, gemini_history, gemini_content, gemini_import, gemini_runtime, gemini_runner, gemini_permissions, gemini_api, cursor_process_guard, secure_peer_delivery, secure_peer_runtime, team_mail_runtime, team_mail_websocket, team_mail_grants, team_hub_host, agentsdock_mail, agentsdock_team, transcript_reader, claude_history_repair, claude_history_provenance, claude_goals, claude_background_reconciliation, codex_history_repair, public_chat_shares, public_chat_transcript, public_chat_share_routes, interactive_chat_shares, interactive_chat_share_routes, interactive_chat_share_web, interactive_chat_projection, interactive_chat_runtime, interactive_chat_native, shared_chat_videos, shared_chat_analytics_web, shared_chat_video_stream, interactive_chat_controls, chat_mailbox, provider_commands, provider_usage; from agentsdock_team_hub import secure_peer, secure_peer_hub' >/dev/null
3794
3810
  )
3795
3811
 
3796
3812
  abort_unclaimed_team_hub_reactivation() {
@@ -4108,6 +4124,7 @@ if [[ "$stage_identity" != *$'\n'* \
4108
4124
  exit 1
4109
4125
  fi
4110
4126
  for name in "${RELEASE_FILES[@]}"; do
4127
+ mkdir -p "$(dirname -- "$STAGE_DIR/$name")"
4111
4128
  install -m 644 "$SOURCE_DIR/$name" "$STAGE_DIR/$name"
4112
4129
  done
4113
4130
  mkdir -p "$STAGE_DIR/agentsdock_team_hub/migrations"
@@ -4117,7 +4134,7 @@ for name in "${TEAM_HUB_RELEASE_FILES[@]}"; do
4117
4134
  "$SOURCE_DIR/agentsdock_team_hub/$name" \
4118
4135
  "$STAGE_DIR/agentsdock_team_hub/$name"
4119
4136
  done
4120
- chmod 755 "$STAGE_DIR/agent_server.py" "$STAGE_DIR/agentsdock_jobs.py" "$STAGE_DIR/agentsdock_chats.py" "$STAGE_DIR/agentsdock_emergency.py" "$STAGE_DIR/agentsdock_publish.py" "$STAGE_DIR/agentsdock_mail.py" "$STAGE_DIR/agentsdock_team.py" "$STAGE_DIR/install.sh" "$STAGE_DIR/uninstall.sh" "$STAGE_DIR/update_runner.py"
4137
+ chmod 755 "$STAGE_DIR/agent_server.py" "$STAGE_DIR/agentsdock_jobs.py" "$STAGE_DIR/agentsdock_chats.py" "$STAGE_DIR/agentsdock_emergency.py" "$STAGE_DIR/agentsdock_notify.py" "$STAGE_DIR/agentsdock_publish.py" "$STAGE_DIR/agentsdock_mail.py" "$STAGE_DIR/agentsdock_team.py" "$STAGE_DIR/install.sh" "$STAGE_DIR/uninstall.sh" "$STAGE_DIR/update_runner.py"
4121
4138
  chmod 755 "$STAGE_DIR/instances.sh"
4122
4139
 
4123
4140
  if [[ "$PREPARE_ONLY" == "true" ]]; then
@@ -47,7 +47,7 @@ HTTP_COOKIE = "AgentsDock-Chat"
47
47
  HEADERS = {
48
48
  "Cache-Control": "no-store", "Referrer-Policy": "no-referrer", "X-Content-Type-Options": "nosniff",
49
49
  "X-Robots-Tag": "noindex, nofollow, noarchive", "Cross-Origin-Resource-Policy": "same-origin",
50
- "Content-Security-Policy": "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self' data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
50
+ "Content-Security-Policy": "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://api.mixpanel.com; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self' data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
51
51
  }
52
52
 
53
53