cowork-dash 0.1.5__tar.gz → 0.1.6__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.
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/CHANGELOG.md +10 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/PKG-INFO +1 -1
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/agent.py +4 -1
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/app.py +61 -15
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/layout.py +2 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/pyproject.toml +1 -1
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/.claude/settings.local.json +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/.gitignore +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/LICENSE +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/MANIFEST.in +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/README.md +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/__init__.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/__main__.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/assets/app.js +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/assets/favicon.ico +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/assets/favicon.svg +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/assets/styles.css +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/canvas.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/cli.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/components.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/config.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/file_utils.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/cowork_dash/tools.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/docs/CLI_USAGE.md +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/docs/dark.png +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/docs/light.png +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/examples/example_agent.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/examples/python_api_example.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/templates/index.html +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/tests/__init__.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/tests/conftest.py +0 -0
- {cowork_dash-0.1.5 → cowork_dash-0.1.6}/tests/test_core.py +0 -0
|
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.6] - 2026-01-25
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Duplicate agent response rendering issue
|
|
12
|
+
- Agent state now resets on page refresh for clean sessions
|
|
13
|
+
- Tool calls indicator disappearing during execution (race condition fix)
|
|
14
|
+
- Missing return value in interrupt handling callback
|
|
15
|
+
- Default agent workspace now uses environment variable or current directory
|
|
16
|
+
|
|
8
17
|
## [0.1.5] - 2026-01-24
|
|
9
18
|
|
|
10
19
|
### Added
|
|
@@ -109,6 +118,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
109
118
|
- Resizable split-pane interface
|
|
110
119
|
- Upload/download functionality for files
|
|
111
120
|
|
|
121
|
+
[0.1.6]: https://github.com/dkedar7/cowork-dash/compare/v0.1.5...v0.1.6
|
|
112
122
|
[0.1.5]: https://github.com/dkedar7/cowork-dash/compare/v0.1.4...v0.1.5
|
|
113
123
|
[0.1.4]: https://github.com/dkedar7/cowork-dash/compare/v0.1.3...v0.1.4
|
|
114
124
|
[0.1.3]: https://github.com/dkedar7/cowork-dash/compare/v0.1.2...v0.1.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cowork-dash
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: AI Agent Web Interface with Filesystem and Canvas Visualization
|
|
5
5
|
Project-URL: Homepage, https://github.com/dkedar7/cowork-dash
|
|
6
6
|
Project-URL: Documentation, https://github.com/dkedar7/cowork-dash/blob/main/README.md
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import uuid
|
|
2
3
|
from deepagents import create_deep_agent
|
|
3
4
|
from langgraph.checkpoint.memory import InMemorySaver
|
|
@@ -92,7 +93,9 @@ Work iteratively like a human using Jupyter:
|
|
|
92
93
|
|
|
93
94
|
The workspace is your sandbox - feel free to create files, organize content, and help users manage their projects."""
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
# Get workspace root from environment variable or default to current directory
|
|
97
|
+
workspace_root = os.getenv("DEEPAGENT_WORKSPACE_ROOT", os.getcwd())
|
|
98
|
+
backend = FilesystemBackend(root_dir=workspace_root, virtual_mode=True)
|
|
96
99
|
|
|
97
100
|
agent = create_deep_agent(
|
|
98
101
|
system_prompt=SYSTEM_PROMPT,
|
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
import json
|
|
5
5
|
import base64
|
|
6
6
|
import re
|
|
7
|
+
import copy
|
|
7
8
|
import shutil
|
|
8
9
|
import platform
|
|
9
10
|
import subprocess
|
|
@@ -850,9 +851,36 @@ def resume_agent_from_interrupt(decision: str, action: str = "approve", action_r
|
|
|
850
851
|
thread.start()
|
|
851
852
|
|
|
852
853
|
def get_agent_state() -> Dict[str, Any]:
|
|
853
|
-
"""Get current agent state (thread-safe).
|
|
854
|
+
"""Get current agent state (thread-safe).
|
|
855
|
+
|
|
856
|
+
Returns a deep copy of mutable collections to prevent race conditions.
|
|
857
|
+
"""
|
|
858
|
+
with _agent_state_lock:
|
|
859
|
+
state = _agent_state.copy()
|
|
860
|
+
# Deep copy mutable collections to prevent race conditions during rendering
|
|
861
|
+
state["tool_calls"] = copy.deepcopy(_agent_state["tool_calls"])
|
|
862
|
+
state["todos"] = copy.deepcopy(_agent_state["todos"])
|
|
863
|
+
state["canvas"] = copy.deepcopy(_agent_state["canvas"])
|
|
864
|
+
return state
|
|
865
|
+
|
|
866
|
+
def reset_agent_state():
|
|
867
|
+
"""Reset agent state for a fresh session (thread-safe).
|
|
868
|
+
|
|
869
|
+
Called on page load to ensure clean state after browser refresh.
|
|
870
|
+
Preserves canvas items loaded from canvas.md.
|
|
871
|
+
"""
|
|
854
872
|
with _agent_state_lock:
|
|
855
|
-
|
|
873
|
+
_agent_state["running"] = False
|
|
874
|
+
_agent_state["thinking"] = ""
|
|
875
|
+
_agent_state["todos"] = []
|
|
876
|
+
_agent_state["tool_calls"] = []
|
|
877
|
+
_agent_state["response"] = ""
|
|
878
|
+
_agent_state["error"] = None
|
|
879
|
+
_agent_state["interrupt"] = None
|
|
880
|
+
_agent_state["start_time"] = None
|
|
881
|
+
_agent_state["stop_requested"] = False
|
|
882
|
+
_agent_state["last_update"] = time.time()
|
|
883
|
+
# Note: canvas is preserved - it's loaded from canvas.md on startup
|
|
856
884
|
|
|
857
885
|
# =============================================================================
|
|
858
886
|
# DASH APP
|
|
@@ -921,15 +949,32 @@ app.layout = create_layout
|
|
|
921
949
|
|
|
922
950
|
# Initial message display
|
|
923
951
|
@app.callback(
|
|
924
|
-
Output("chat-messages", "children"),
|
|
952
|
+
[Output("chat-messages", "children"),
|
|
953
|
+
Output("skip-history-render", "data", allow_duplicate=True),
|
|
954
|
+
Output("session-initialized", "data", allow_duplicate=True)],
|
|
925
955
|
[Input("chat-history", "data")],
|
|
926
|
-
[State("theme-store", "data")
|
|
927
|
-
|
|
956
|
+
[State("theme-store", "data"),
|
|
957
|
+
State("skip-history-render", "data"),
|
|
958
|
+
State("session-initialized", "data")],
|
|
959
|
+
prevent_initial_call="initial_duplicate"
|
|
928
960
|
)
|
|
929
|
-
def display_initial_messages(history, theme):
|
|
930
|
-
"""Display initial welcome message or chat history.
|
|
961
|
+
def display_initial_messages(history, theme, skip_render, session_initialized):
|
|
962
|
+
"""Display initial welcome message or chat history.
|
|
963
|
+
|
|
964
|
+
On first call (page load), resets agent state for a fresh session.
|
|
965
|
+
Skip rendering if skip_render flag is set - this prevents duplicate renders
|
|
966
|
+
when poll_agent_updates already handles the rendering.
|
|
967
|
+
"""
|
|
968
|
+
# Reset agent state on page load (first callback trigger)
|
|
969
|
+
if not session_initialized:
|
|
970
|
+
reset_agent_state()
|
|
971
|
+
|
|
972
|
+
# Skip if flag is set (poll_agent_updates already rendered)
|
|
973
|
+
if skip_render:
|
|
974
|
+
return no_update, False, True # Reset skip flag, mark session initialized
|
|
975
|
+
|
|
931
976
|
if not history:
|
|
932
|
-
return []
|
|
977
|
+
return [], False, True
|
|
933
978
|
|
|
934
979
|
colors = get_colors(theme or "light")
|
|
935
980
|
messages = []
|
|
@@ -946,7 +991,7 @@ def display_initial_messages(history, theme):
|
|
|
946
991
|
todos_block = format_todos_inline(msg["todos"], colors)
|
|
947
992
|
if todos_block:
|
|
948
993
|
messages.append(todos_block)
|
|
949
|
-
return messages
|
|
994
|
+
return messages, False, True
|
|
950
995
|
|
|
951
996
|
# Chat callbacks
|
|
952
997
|
@app.callback(
|
|
@@ -1005,7 +1050,8 @@ def handle_send_immediate(n_clicks, n_submit, message, history, theme, current_w
|
|
|
1005
1050
|
@app.callback(
|
|
1006
1051
|
[Output("chat-messages", "children", allow_duplicate=True),
|
|
1007
1052
|
Output("chat-history", "data", allow_duplicate=True),
|
|
1008
|
-
Output("poll-interval", "disabled", allow_duplicate=True)
|
|
1053
|
+
Output("poll-interval", "disabled", allow_duplicate=True),
|
|
1054
|
+
Output("skip-history-render", "data", allow_duplicate=True)],
|
|
1009
1055
|
Input("poll-interval", "n_intervals"),
|
|
1010
1056
|
[State("chat-history", "data"),
|
|
1011
1057
|
State("pending-message", "data"),
|
|
@@ -1069,7 +1115,7 @@ def poll_agent_updates(n_intervals, history, pending_message, theme):
|
|
|
1069
1115
|
messages.append(interrupt_block)
|
|
1070
1116
|
|
|
1071
1117
|
# Disable polling - wait for user to respond to interrupt
|
|
1072
|
-
return messages, no_update, True
|
|
1118
|
+
return messages, no_update, True, no_update
|
|
1073
1119
|
|
|
1074
1120
|
# Check if agent is done
|
|
1075
1121
|
if not state["running"]:
|
|
@@ -1115,8 +1161,8 @@ def poll_agent_updates(n_intervals, history, pending_message, theme):
|
|
|
1115
1161
|
if todos_block:
|
|
1116
1162
|
final_messages.append(todos_block)
|
|
1117
1163
|
|
|
1118
|
-
# Disable polling
|
|
1119
|
-
return final_messages, history, True
|
|
1164
|
+
# Disable polling, set skip flag to prevent display_initial_messages from re-rendering
|
|
1165
|
+
return final_messages, history, True, True
|
|
1120
1166
|
else:
|
|
1121
1167
|
# Agent still running - show loading with current thinking/tool_calls/todos
|
|
1122
1168
|
messages = render_history_messages(history)
|
|
@@ -1142,8 +1188,8 @@ def poll_agent_updates(n_intervals, history, pending_message, theme):
|
|
|
1142
1188
|
# Add loading indicator
|
|
1143
1189
|
messages.append(format_loading(colors))
|
|
1144
1190
|
|
|
1145
|
-
# Continue polling
|
|
1146
|
-
return messages, no_update, False
|
|
1191
|
+
# Continue polling, no skip flag needed
|
|
1192
|
+
return messages, no_update, False, no_update
|
|
1147
1193
|
|
|
1148
1194
|
|
|
1149
1195
|
# Stop button visibility - show when agent is running
|
|
@@ -37,6 +37,8 @@ def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent
|
|
|
37
37
|
"content": message
|
|
38
38
|
}]),
|
|
39
39
|
dcc.Store(id="pending-message", data=None),
|
|
40
|
+
dcc.Store(id="skip-history-render", data=False), # Flag to skip display_initial_messages render
|
|
41
|
+
dcc.Store(id="session-initialized", data=False), # Flag to track if session has been initialized
|
|
40
42
|
dcc.Store(id="expanded-folders", data=[]),
|
|
41
43
|
dcc.Store(id="file-to-view", data=None),
|
|
42
44
|
dcc.Store(id="file-click-tracker", data={}),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|