cowork-dash 0.1.3__tar.gz → 0.1.4__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 (30) hide show
  1. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/CHANGELOG.md +9 -0
  2. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/PKG-INFO +1 -1
  3. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/agent.py +1 -0
  4. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/app.py +43 -31
  5. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/config.py +3 -3
  6. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/pyproject.toml +1 -1
  7. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/.claude/settings.local.json +0 -0
  8. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/.gitignore +0 -0
  9. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/LICENSE +0 -0
  10. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/MANIFEST.in +0 -0
  11. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/README.md +0 -0
  12. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/__init__.py +0 -0
  13. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/__main__.py +0 -0
  14. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/assets/app.js +0 -0
  15. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/assets/favicon.svg +0 -0
  16. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/assets/styles.css +0 -0
  17. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/canvas.py +0 -0
  18. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/cli.py +0 -0
  19. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/components.py +0 -0
  20. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/file_utils.py +0 -0
  21. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/layout.py +0 -0
  22. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/cowork_dash/tools.py +0 -0
  23. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/docs/CLI_USAGE.md +0 -0
  24. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/examples/example_agent.py +0 -0
  25. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/examples/python_api_example.py +0 -0
  26. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/requirements.txt +0 -0
  27. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/templates/index.html +0 -0
  28. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/tests/__init__.py +0 -0
  29. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/tests/conftest.py +0 -0
  30. {cowork_dash-0.1.3 → cowork_dash-0.1.4}/tests/test_core.py +0 -0
@@ -5,6 +5,14 @@ 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.4] - 2026-01-20
9
+
10
+ ### Added
11
+ - App title and subtitle can be set dynamically from agent `name` and `description` attributes
12
+
13
+ ### Fixed
14
+ - Tool call error detection now uses precise patterns to avoid false positives (e.g., reading files about errors no longer marks tool as failed)
15
+
8
16
  ## [0.1.3] - 2026-01-20
9
17
 
10
18
  ### Added
@@ -83,6 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
83
91
  - Resizable split-pane interface
84
92
  - Upload/download functionality for files
85
93
 
94
+ [0.1.4]: https://github.com/dkedar7/cowork-dash/compare/v0.1.3...v0.1.4
86
95
  [0.1.3]: https://github.com/dkedar7/cowork-dash/compare/v0.1.2...v0.1.3
87
96
  [0.1.2]: https://github.com/dkedar7/cowork-dash/compare/v0.1.1...v0.1.2
88
97
  [0.1.1]: https://github.com/dkedar7/cowork-dash/compare/v0.1.0...v0.1.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cowork-dash
3
- Version: 0.1.3
3
+ Version: 0.1.4
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
@@ -96,6 +96,7 @@ backend = FilesystemBackend(root_dir=str("./"), virtual_mode=True)
96
96
 
97
97
  agent = create_deep_agent(
98
98
  system_prompt=SYSTEM_PROMPT,
99
+ name="Cowork Dash",
99
100
  backend=backend,
100
101
  tools=[
101
102
  add_to_canvas,
@@ -124,37 +124,38 @@ def load_agent_from_spec(agent_spec: str):
124
124
  """
125
125
  Load agent from specification string.
126
126
 
127
- Supports two formats:
127
+ Supports two formats (both use colon separator):
128
128
  1. File path format: "path/to/file.py:object_name"
129
- 2. Module format: "mypackage.module.submodule.object_name"
129
+ 2. Module format: "mypackage.module.submodule:object_name"
130
130
 
131
131
  Args:
132
132
  agent_spec: String like "agent.py:agent", "my_agents.py:custom_agent",
133
- or "mypackage.agents.my_agent"
133
+ or "mypackage.agents:my_agent"
134
134
 
135
135
  Returns:
136
136
  tuple: (agent_object, error_message)
137
137
  """
138
138
  try:
139
- # Determine format: file path (contains ":") vs module path (no ":" and no ".py")
140
- if ":" in agent_spec:
141
- # File path format: "path/to/file.py:object_name"
142
- return _load_agent_from_file(agent_spec)
143
- elif agent_spec.endswith(".py"):
144
- # Looks like a file path without object name
145
- return None, f"Invalid agent spec '{agent_spec}'. File path format requires object name: 'path/to/file.py:object_name'"
139
+ # Both formats use colon separator
140
+ if ":" not in agent_spec:
141
+ return None, f"Invalid agent spec '{agent_spec}'. Expected format: 'path/to/file.py:object' or 'module.path:object'"
142
+
143
+ left_part, object_name = agent_spec.rsplit(":", 1)
144
+
145
+ # Determine if it's a file path or module path
146
+ # File paths end with .py or contain path separators
147
+ if left_part.endswith(".py") or "/" in left_part or "\\" in left_part:
148
+ return _load_agent_from_file(left_part, object_name)
146
149
  else:
147
- # Module format: "mypackage.module.object_name"
148
- return _load_agent_from_module(agent_spec)
150
+ return _load_agent_from_module(left_part, object_name)
149
151
 
150
152
  except Exception as e:
151
153
  return None, f"Failed to load agent from {agent_spec}: {e}"
152
154
 
153
155
 
154
- def _load_agent_from_file(agent_spec: str):
156
+ def _load_agent_from_file(file_path_str: str, object_name: str):
155
157
  """Load agent from file path format: 'path/to/file.py:object_name'"""
156
- file_path, object_name = agent_spec.rsplit(":", 1)
157
- file_path = Path(file_path).resolve()
158
+ file_path = Path(file_path_str).resolve()
158
159
 
159
160
  if not file_path.exists():
160
161
  return None, f"Agent file not found: {file_path}"
@@ -176,15 +177,8 @@ def _load_agent_from_file(agent_spec: str):
176
177
  return agent, None
177
178
 
178
179
 
179
- def _load_agent_from_module(agent_spec: str):
180
- """Load agent from module format: 'mypackage.module.object_name'"""
181
- parts = agent_spec.rsplit(".", 1)
182
-
183
- if len(parts) < 2:
184
- return None, f"Invalid module spec '{agent_spec}'. Expected format: 'module.object_name' or 'package.module.object_name'"
185
-
186
- module_path, object_name = parts
187
-
180
+ def _load_agent_from_module(module_path: str, object_name: str):
181
+ """Load agent from module format: 'mypackage.module:object_name'"""
188
182
  try:
189
183
  # Import the module
190
184
  module = importlib.import_module(module_path)
@@ -388,13 +382,27 @@ def _run_agent_stream(message: str, resume_data: Dict = None):
388
382
  # Update tool call status when we get the result
389
383
  tool_call_id = getattr(last_msg, 'tool_call_id', None)
390
384
  if tool_call_id:
391
- # Determine status based on content
385
+ # Determine status - check message status attribute first
392
386
  content = last_msg.content
393
387
  status = "success"
394
- if isinstance(content, str) and ("error" in content.lower() or "Error:" in content):
388
+
389
+ # Check if ToolMessage has explicit status (e.g., from LangGraph)
390
+ msg_status = getattr(last_msg, 'status', None)
391
+ if msg_status == 'error':
395
392
  status = "error"
393
+ # Check for dict with explicit error field
396
394
  elif isinstance(content, dict) and content.get("error"):
397
395
  status = "error"
396
+ # Check for common error patterns at the START of the message
397
+ # (not just anywhere, to avoid false positives)
398
+ elif isinstance(content, str):
399
+ content_lower = content.lower().strip()
400
+ # Only mark as error if it starts with error indicators
401
+ if (content_lower.startswith("error:") or
402
+ content_lower.startswith("failed:") or
403
+ content_lower.startswith("exception:") or
404
+ content_lower.startswith("traceback")):
405
+ status = "error"
398
406
 
399
407
  # Truncate result for display
400
408
  result_display = str(content)
@@ -800,10 +808,14 @@ app.index_string = '''<!DOCTYPE html>
800
808
 
801
809
  def create_layout():
802
810
  """Create the app layout with current configuration."""
811
+ # Use agent's name/description if available, otherwise fall back to config
812
+ title = getattr(agent, 'name', None) or APP_TITLE
813
+ subtitle = getattr(agent, 'description', None) or APP_SUBTITLE
814
+
803
815
  return create_layout_component(
804
816
  workspace_root=WORKSPACE_ROOT,
805
- app_title=APP_TITLE,
806
- app_subtitle=APP_SUBTITLE,
817
+ app_title=title,
818
+ app_subtitle=subtitle,
807
819
  colors=COLORS,
808
820
  styles=STYLES,
809
821
  agent=agent
@@ -1662,9 +1674,9 @@ def run_app(
1662
1674
  agent_instance (object, optional): Agent object instance (Python API only)
1663
1675
  workspace (str, optional): Workspace directory path
1664
1676
  agent_spec (str, optional): Agent specification (overrides agent_instance).
1665
- Supports two formats:
1677
+ Supports two formats (both use colon separator):
1666
1678
  - File path: "path/to/file.py:object_name"
1667
- - Module path: "mypackage.module.object_name"
1679
+ - Module path: "mypackage.module:object_name"
1668
1680
  port (int, optional): Port number
1669
1681
  host (str, optional): Host to bind to
1670
1682
  debug (bool, optional): Debug mode
@@ -1685,7 +1697,7 @@ def run_app(
1685
1697
  >>> run_app(agent_spec="my_agent.py:agent", port=8080)
1686
1698
 
1687
1699
  >>> # Using agent spec (module format)
1688
- >>> run_app(agent_spec="mypackage.agents.my_agent", port=8080)
1700
+ >>> run_app(agent_spec="mypackage.agents:my_agent", port=8080)
1689
1701
 
1690
1702
  >>> # Without agent (manual mode)
1691
1703
  >>> run_app(workspace="~/my-workspace", debug=True)
@@ -53,16 +53,16 @@ def get_config(key: str, default=None, type_cast=None):
53
53
  _workspace_path = get_config("workspace_root", default="./")
54
54
  WORKSPACE_ROOT = Path(_workspace_path).resolve() if _workspace_path else Path("./").resolve()
55
55
 
56
- # Agent specification - supports two formats:
56
+ # Agent specification - supports two formats (both use colon separator):
57
57
  # 1. File path: "path/to/file.py:object_name"
58
- # 2. Module path: "mypackage.module.object_name"
58
+ # 2. Module path: "mypackage.module:object_name"
59
59
  # Environment variable: DEEPAGENT_SPEC (or DEEPAGENT_AGENT_SPEC for backwards compatibility)
60
60
  # CLI argument: --agent
61
61
  # Default: package's built-in agent
62
62
  # Examples:
63
63
  # - "my_agents.py:agent" (file in current directory)
64
64
  # - "/path/to/agent.py:my_agent" (absolute path)
65
- # - "mypackage.agents.my_agent" (installed Python module)
65
+ # - "mypackage.agents:my_agent" (installed Python module)
66
66
  _default_agent = str(Path(__file__).parent / "agent.py") + ":agent"
67
67
  AGENT_SPEC = get_config("spec", default=None) or get_config("agent_spec", default=None) or _default_agent
68
68
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cowork-dash"
7
- version = "0.1.3"
7
+ version = "0.1.4"
8
8
  description = "AI Agent Web Interface with Filesystem and Canvas Visualization"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes
File without changes
File without changes