pycodex-ws 0.5.0__tar.gz → 0.5.2__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.
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/PKG-INFO +42 -3
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/README.md +40 -1
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_ws/feishu_card.py +2 -2
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_ws/feishu_link.py +1 -1
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pyproject.toml +2 -2
- pycodex_ws-0.5.2/workspace_server/WORKSPACE_API.md +257 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/app.py +423 -47
- pycodex_ws-0.5.2/workspace_server/content_views.css +1 -0
- pycodex_ws-0.5.2/workspace_server/content_views.mjs +32 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/customization.py +1 -1
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/composer.mjs +28 -2
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/content_views.css +33 -3
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/content_views.mjs +73 -3
- pycodex_ws-0.5.2/workspace_server/frontend/prompt_history.mjs +85 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/CONTRIBUTING.md +157 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/README.md +27 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/consecutive-tool-groups/README.md +118 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/consecutive-tool-groups/preview-mobile.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/consecutive-tool-groups/preview.html +48 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/consecutive-tool-groups/preview.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/consecutive-tool-groups/source/fixture.mjs +11 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/consecutive-tool-groups/source/tool-groups.css +11 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/consecutive-tool-groups/source/tool-groups.js +111 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/consecutive-tool-groups/source/verify.mjs +61 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/exec-status/README.md +85 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/exec-status/preview-mobile.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/exec-status/preview.html +33 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/exec-status/preview.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/exec-status/source/custom.css +5 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/exec-status/source/custom.js +75 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/exec-status/source/fixture.mjs +18 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/exec-status/source/verify.mjs +80 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/reasoning-display/README.md +88 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/reasoning-display/preview-mobile.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/reasoning-display/preview.html +34 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/reasoning-display/preview.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/reasoning-display/source/custom.css +6 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/reasoning-display/source/custom.js +95 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/reasoning-display/source/fixture.mjs +26 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/reasoning-display/source/verify.mjs +80 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/README.md +126 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/preview-mobile.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/preview.html +57 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/preview.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/source/custom.css +46 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/source/custom.js +49 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/source/fixture.mjs +25 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/source/session-tips.js +165 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/session-tips/source/verify.mjs +71 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/subagent-status/README.md +87 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/subagent-status/preview-mobile.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/subagent-status/preview.html +33 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/subagent-status/preview.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/subagent-status/source/custom.css +8 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/subagent-status/source/custom.js +92 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/subagent-status/source/fixture.mjs +22 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/subagent-status/source/verify.mjs +80 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/tool-calls/README.md +92 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/tool-calls/preview-mobile.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/tool-calls/preview.html +34 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/tool-calls/preview.png +0 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/tool-calls/source/custom.css +6 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/tool-calls/source/custom.js +112 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/tool-calls/source/fixture.mjs +25 -0
- pycodex_ws-0.5.2/workspace_server/optional_features/tool-calls/source/verify.mjs +80 -0
- pycodex_ws-0.5.2/workspace_server/session_api.py +142 -0
- pycodex_ws-0.5.2/workspace_server/sharing.py +93 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/workspace.html +580 -49
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/workspace_views.py +19 -6
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/workspaces.py +23 -2
- pycodex_ws-0.5.0/workspace_server/content_views.css +0 -1
- pycodex_ws-0.5.0/workspace_server/content_views.mjs +0 -31
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/.gitignore +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/LICENSE +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_router/__init__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_router/__main__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_router/browser.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_router/client.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_router/index.html +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_router/protocol.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_router/server.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_ws/__init__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/pycodex_ws/feishu_oauth.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/__init__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/__main__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/agent_access.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/content_views.LICENSE.txt +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/editor.css +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/editor.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/.gitignore +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/build.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/editor.css +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/editor.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/editor_context.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/package-lock.json +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/package.json +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/touch_selection.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/frontend/view_labels.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/owned_process.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/runtime_info.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.2}/workspace_server/workspaces.html +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pycodex-ws
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: Browser workspace and optional Feishu integration for python-codex
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.7
|
|
7
7
|
Requires-Dist: pillow>=9.0.0
|
|
8
|
-
Requires-Dist: python-codex==0.5.
|
|
8
|
+
Requires-Dist: python-codex==0.5.2
|
|
9
9
|
Requires-Dist: websockets<16,>=11
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
|
|
@@ -52,6 +52,12 @@ running sessions and unread replies. `/cd <directory>` changes the current
|
|
|
52
52
|
session's working directory while idle; `/model`, `/resume`, `/compact` and
|
|
53
53
|
other core session commands are also available.
|
|
54
54
|
|
|
55
|
+
Consecutive model reconnection diagnostics share one collapsed `Model retries`
|
|
56
|
+
row per sequence. The summary shows the number of recorded retries, the latest
|
|
57
|
+
attempt and its HTTP status when available. Expand it to read the original errors
|
|
58
|
+
in a scrollable log. New retries update the same row and preserve its open state;
|
|
59
|
+
refreshing starts collapsed. Other model diagnostics and final errors stay visible.
|
|
60
|
+
|
|
55
61
|
Click Markdown links to open files and comparisons, for example
|
|
56
62
|
`[source](app.py:42)` or `[changes](diff:HEAD:app.py)`. In a code or diff view,
|
|
57
63
|
select text and choose **Insert into chat** to add editable context to the draft.
|
|
@@ -67,7 +73,8 @@ distributed separately as a VSIX.
|
|
|
67
73
|
|
|
68
74
|
## Customize the interface
|
|
69
75
|
|
|
70
|
-
Click the gear button
|
|
76
|
+
Click the gear button and choose **Frontend customization** to start a
|
|
77
|
+
customization conversation with the Agent.
|
|
71
78
|
Custom files live in the server user's `~/.pycodex/frontend/`:
|
|
72
79
|
|
|
73
80
|
- `custom.css` and `custom.js`: optional styles and an ES module, loaded automatically.
|
|
@@ -78,6 +85,38 @@ Refresh to apply changes and keep the selected conversation. Open the workspace
|
|
|
78
85
|
with `?builtin=1` to bypass all customizations; click “Re-enable customizations”
|
|
79
86
|
to return. Changes apply to all workspaces for that OS user.
|
|
80
87
|
See the [customization contract](../../docs/RUNTIME.md#frontend-customization).
|
|
88
|
+
For custom interfaces, the [resource API](../../docs/WORKSPACE_API.md) exposes
|
|
89
|
+
state, model catalogs, asynchronous command results, paginated history, event
|
|
90
|
+
replay and sub-agent/background-work inventories. A `mount(host)` export in
|
|
91
|
+
`custom.js` can access the built-in page's session, draft and view controls.
|
|
92
|
+
|
|
93
|
+
Ask the Agent to show the [feature catalog](workspace_server/optional_features/README.md)
|
|
94
|
+
in the view area, then choose features by name or ID in chat. The Agent adapts
|
|
95
|
+
their source into your frontend. To share a local change, ask it to publish a
|
|
96
|
+
feature PR on GitHub; the [contribution guide](workspace_server/optional_features/CONTRIBUTING.md)
|
|
97
|
+
specifies the source, documentation, validation and PR format.
|
|
98
|
+
|
|
99
|
+
## Share a workspace
|
|
100
|
+
|
|
101
|
+
Start the workspace server with an administrator password (`--password`), then
|
|
102
|
+
choose **Settings → Share** in a workspace. Set a different share password and
|
|
103
|
+
send the displayed workspace link and password to the recipient.
|
|
104
|
+
|
|
105
|
+
The existing login page accepts either the administrator password or the share
|
|
106
|
+
password for the workspace being opened. Visitors can use all its conversations,
|
|
107
|
+
Canvas, files, APIs and WebSocket connections, but cannot access workspace
|
|
108
|
+
management, another workspace or sharing settings. The settings button and
|
|
109
|
+
management navigation are hidden for visitors.
|
|
110
|
+
|
|
111
|
+
Passwords, verification values and visitor tokens are kept only in process
|
|
112
|
+
memory. Changing the password or stopping sharing invalidates existing visitor
|
|
113
|
+
logins and disconnects their WebSockets. Restarting the process clears all
|
|
114
|
+
shares. Already accepted Agent work continues.
|
|
115
|
+
|
|
116
|
+
This restricts web access; Agent tools retain their existing file and execution
|
|
117
|
+
permissions. An unprotected server must configure its administrator password
|
|
118
|
+
before enabling sharing. Router authentication is unchanged: a link through a
|
|
119
|
+
router still requires its normal login.
|
|
81
120
|
|
|
82
121
|
## Storage
|
|
83
122
|
|
|
@@ -41,6 +41,12 @@ running sessions and unread replies. `/cd <directory>` changes the current
|
|
|
41
41
|
session's working directory while idle; `/model`, `/resume`, `/compact` and
|
|
42
42
|
other core session commands are also available.
|
|
43
43
|
|
|
44
|
+
Consecutive model reconnection diagnostics share one collapsed `Model retries`
|
|
45
|
+
row per sequence. The summary shows the number of recorded retries, the latest
|
|
46
|
+
attempt and its HTTP status when available. Expand it to read the original errors
|
|
47
|
+
in a scrollable log. New retries update the same row and preserve its open state;
|
|
48
|
+
refreshing starts collapsed. Other model diagnostics and final errors stay visible.
|
|
49
|
+
|
|
44
50
|
Click Markdown links to open files and comparisons, for example
|
|
45
51
|
`[source](app.py:42)` or `[changes](diff:HEAD:app.py)`. In a code or diff view,
|
|
46
52
|
select text and choose **Insert into chat** to add editable context to the draft.
|
|
@@ -56,7 +62,8 @@ distributed separately as a VSIX.
|
|
|
56
62
|
|
|
57
63
|
## Customize the interface
|
|
58
64
|
|
|
59
|
-
Click the gear button
|
|
65
|
+
Click the gear button and choose **Frontend customization** to start a
|
|
66
|
+
customization conversation with the Agent.
|
|
60
67
|
Custom files live in the server user's `~/.pycodex/frontend/`:
|
|
61
68
|
|
|
62
69
|
- `custom.css` and `custom.js`: optional styles and an ES module, loaded automatically.
|
|
@@ -67,6 +74,38 @@ Refresh to apply changes and keep the selected conversation. Open the workspace
|
|
|
67
74
|
with `?builtin=1` to bypass all customizations; click “Re-enable customizations”
|
|
68
75
|
to return. Changes apply to all workspaces for that OS user.
|
|
69
76
|
See the [customization contract](../../docs/RUNTIME.md#frontend-customization).
|
|
77
|
+
For custom interfaces, the [resource API](../../docs/WORKSPACE_API.md) exposes
|
|
78
|
+
state, model catalogs, asynchronous command results, paginated history, event
|
|
79
|
+
replay and sub-agent/background-work inventories. A `mount(host)` export in
|
|
80
|
+
`custom.js` can access the built-in page's session, draft and view controls.
|
|
81
|
+
|
|
82
|
+
Ask the Agent to show the [feature catalog](workspace_server/optional_features/README.md)
|
|
83
|
+
in the view area, then choose features by name or ID in chat. The Agent adapts
|
|
84
|
+
their source into your frontend. To share a local change, ask it to publish a
|
|
85
|
+
feature PR on GitHub; the [contribution guide](workspace_server/optional_features/CONTRIBUTING.md)
|
|
86
|
+
specifies the source, documentation, validation and PR format.
|
|
87
|
+
|
|
88
|
+
## Share a workspace
|
|
89
|
+
|
|
90
|
+
Start the workspace server with an administrator password (`--password`), then
|
|
91
|
+
choose **Settings → Share** in a workspace. Set a different share password and
|
|
92
|
+
send the displayed workspace link and password to the recipient.
|
|
93
|
+
|
|
94
|
+
The existing login page accepts either the administrator password or the share
|
|
95
|
+
password for the workspace being opened. Visitors can use all its conversations,
|
|
96
|
+
Canvas, files, APIs and WebSocket connections, but cannot access workspace
|
|
97
|
+
management, another workspace or sharing settings. The settings button and
|
|
98
|
+
management navigation are hidden for visitors.
|
|
99
|
+
|
|
100
|
+
Passwords, verification values and visitor tokens are kept only in process
|
|
101
|
+
memory. Changing the password or stopping sharing invalidates existing visitor
|
|
102
|
+
logins and disconnects their WebSockets. Restarting the process clears all
|
|
103
|
+
shares. Already accepted Agent work continues.
|
|
104
|
+
|
|
105
|
+
This restricts web access; Agent tools retain their existing file and execution
|
|
106
|
+
permissions. An unprotected server must configure its administrator password
|
|
107
|
+
before enabling sharing. Router authentication is unchanged: a link through a
|
|
108
|
+
router still requires its normal login.
|
|
70
109
|
|
|
71
110
|
## Storage
|
|
72
111
|
|
|
@@ -357,9 +357,9 @@ class PycodexCard:
|
|
|
357
357
|
or getattr(action_payload, "input_value", None)
|
|
358
358
|
or value.get("input_value")
|
|
359
359
|
or ""
|
|
360
|
-
)
|
|
360
|
+
)
|
|
361
361
|
if not prompt and form_values:
|
|
362
|
-
prompt = str(next(iter(form_values.values())))
|
|
362
|
+
prompt = str(next(iter(form_values.values())))
|
|
363
363
|
action = str(
|
|
364
364
|
value.get("action") or getattr(action_payload, "name", None) or "send"
|
|
365
365
|
).strip()
|
|
@@ -105,7 +105,7 @@ class PycodexRuntimeLink:
|
|
|
105
105
|
"content": "Unsupported action: {0}".format(action.get("action")),
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
prompt = str(action.get("prompt") or "")
|
|
108
|
+
prompt = str(action.get("prompt") or "")
|
|
109
109
|
future = asyncio.run_coroutine_threadsafe(
|
|
110
110
|
self.runtime.submit_input(
|
|
111
111
|
prompt, sender=str(action.get("sender") or "feishu")
|
|
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pycodex-ws"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
description = "Browser workspace and optional Feishu integration for python-codex"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.7"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"python-codex==0.5.
|
|
12
|
+
"python-codex==0.5.2",
|
|
13
13
|
"pillow>=9.0.0",
|
|
14
14
|
"websockets>=11,<16",
|
|
15
15
|
]
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# Workspace resource API
|
|
2
|
+
|
|
3
|
+
This reference ships with `pycodex-ws`, beside `workspace.html`. The interface
|
|
4
|
+
customization prompt includes its absolute path so the Agent can read the
|
|
5
|
+
installed API contract without a repository checkout.
|
|
6
|
+
|
|
7
|
+
Custom frontends use the same authenticated backend as the built-in workspace.
|
|
8
|
+
Paths below are workspace-relative: use `api/...` from its page, or prefix with
|
|
9
|
+
`/w/{workspace_id}/` on a multi-workspace server. Existing endpoints remain
|
|
10
|
+
available. Resource responses include `api_version: 1` and use
|
|
11
|
+
`Cache-Control: no-store`.
|
|
12
|
+
|
|
13
|
+
## Workspace sharing
|
|
14
|
+
|
|
15
|
+
`GET api/share`, `PUT api/share` and `DELETE api/share` require the server's
|
|
16
|
+
administrator identity. PUT takes `{"password": "..."}`; the password must be
|
|
17
|
+
nonempty and differ from the configured workspace server administrator password.
|
|
18
|
+
Sharing is unavailable when that administrator password is unset.
|
|
19
|
+
|
|
20
|
+
The response is `{enabled, available, url}`. `url` is the workspace's existing
|
|
21
|
+
page path including any mount prefix. GET never returns a password or token.
|
|
22
|
+
DELETE disables sharing; PUT rotates its password and visitor grant.
|
|
23
|
+
These endpoints require a JSON object for PUT and return `Cache-Control: no-store`.
|
|
24
|
+
|
|
25
|
+
`POST /login?next=<workspace-path>` reuses the normal login form. The administrator
|
|
26
|
+
password grants administrator access; a matching share password grants access
|
|
27
|
+
only to that workspace. The server checks the cookie's grant on every HTTP and
|
|
28
|
+
WebSocket request. Visitor login redirects are restricted to the granted
|
|
29
|
+
workspace. Passwords and tokens have no persistence, and password changes,
|
|
30
|
+
revocation or process restart invalidate old grants. Revocation closes existing
|
|
31
|
+
visitor WebSockets without cancelling accepted Agent work.
|
|
32
|
+
|
|
33
|
+
The rendered page has `data-access-role="admin"` or `"visitor"` on its body.
|
|
34
|
+
Custom frontends can use `__ACCESS_ROLE__` to hide management controls; authorization
|
|
35
|
+
is enforced on the server regardless of UI visibility. Sharing does not reduce
|
|
36
|
+
Agent execution permissions, and does not replace Router authentication.
|
|
37
|
+
|
|
38
|
+
## Creating sessions
|
|
39
|
+
|
|
40
|
+
`GET api/sessions` returns `{sessions, profiles, default_profile}`. `profiles`
|
|
41
|
+
contains only names from the server's Codex config; no provider configuration
|
|
42
|
+
or credentials are returned. The catalog is loaded when the workspace is built.
|
|
43
|
+
`default_profile` is the launch `--profile`, or `null` for the base configuration.
|
|
44
|
+
Each session list entry includes its effective `profile` name or `null`.
|
|
45
|
+
|
|
46
|
+
`POST api/sessions` accepts `{title?, prompt?, profile?}`. Title and prompt are
|
|
47
|
+
strings and default to empty. Profile is a string name; omission or `null` uses
|
|
48
|
+
the server default. An unknown name returns HTTP 400; an invalid field type
|
|
49
|
+
returns HTTP 422. An empty body still creates a default session.
|
|
50
|
+
The selected profile configures both the model and Agent instructions; CLI
|
|
51
|
+
overrides still apply. A nonempty prompt is submitted through Runtime.
|
|
52
|
+
The response contains `ok`, `session_id`, `sessions`, and `snapshot`, plus the
|
|
53
|
+
submission receipt when a prompt was supplied.
|
|
54
|
+
|
|
55
|
+
The saved tab stores the selected name, including an explicit `null` for the
|
|
56
|
+
base configuration. Restart resolves that name against the current config,
|
|
57
|
+
even when the launch default changed. Older tabs without a profile use the
|
|
58
|
+
launch default. A missing saved profile fails restoration. Existing sessions
|
|
59
|
+
keep their configuration; this API does not switch profiles in a running tab.
|
|
60
|
+
|
|
61
|
+
## PDF previews
|
|
62
|
+
|
|
63
|
+
`POST api/views/open` with `{"href": "report.pdf"}` opens a local PDF using the
|
|
64
|
+
normal file-view path resolution. Its document has `media_type: "application/pdf"`
|
|
65
|
+
and source metadata, without text or embedded file bytes.
|
|
66
|
+
`GET/HEAD api/views/{id}/pdf` streams that view's current file with
|
|
67
|
+
`Content-Type: application/pdf` and inline disposition; `?download=true`
|
|
68
|
+
selects attachment disposition. These routes require workspace authentication.
|
|
69
|
+
Closing the view removes access through that ID. They accept no file-path input.
|
|
70
|
+
PDF files do not use the text/image snapshot size limit; invalid headers are
|
|
71
|
+
rejected. `revision` in the iframe URL changes when file metadata changes.
|
|
72
|
+
|
|
73
|
+
## State and resources
|
|
74
|
+
|
|
75
|
+
| Method and path | Response fields |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `GET api/sessions/{id}/state` | `state`, `cursor` |
|
|
78
|
+
| `GET api/sessions/{id}/models` | `model`, `models` |
|
|
79
|
+
| `GET api/sessions/{id}/history?limit=100&before=...` | `source`, `items`, `next_cursor` |
|
|
80
|
+
| `GET api/sessions/{id}/agents` | `agents` |
|
|
81
|
+
| `GET api/sessions/{id}/background-work` | `background_work` |
|
|
82
|
+
| `POST api/sessions/{id}/commands` | `submission_id`, `submission` |
|
|
83
|
+
| `GET api/sessions/{id}/submissions/{submission_id}` | `submission` |
|
|
84
|
+
|
|
85
|
+
`id` is the tab ID from `GET api/sessions`. It remains stable when `/fork` or
|
|
86
|
+
`/resume` changes the Agent identity. `state.agent_session_id` identifies the
|
|
87
|
+
current Agent/rollout session.
|
|
88
|
+
|
|
89
|
+
State includes:
|
|
90
|
+
|
|
91
|
+
- `model: {name, reasoning_effort}`, `title`, and `cwd`;
|
|
92
|
+
- `busy`, `running`, `closed`, and `accepts_input`;
|
|
93
|
+
- `active_turn`: IDs, user texts, accumulated assistant text and model errors,
|
|
94
|
+
or `null`; completed history is excluded;
|
|
95
|
+
- `queued_inputs`: `submission_id`, `turn_id`, `queue` (`steer` or `enqueue`)
|
|
96
|
+
and `prompts`, in execution order; merged steer inputs share one ID;
|
|
97
|
+
- `commands`: currently queued/running commands with their submission IDs;
|
|
98
|
+
- `input_request`, `plan`, and `background_work_count`;
|
|
99
|
+
- `context_window`, `max_context_window`, `auto_compact_token_limit`,
|
|
100
|
+
and the latest `usage_tokens`.
|
|
101
|
+
|
|
102
|
+
`running` describes Agent execution; `busy` additionally includes queued turns
|
|
103
|
+
and draining. Command status is separate, so a custom command can run while the
|
|
104
|
+
Agent is idle. State/model queries do not add control messages or invoke a model
|
|
105
|
+
completion.
|
|
106
|
+
Workspace model clients implement `ModelControl`, including an explicit
|
|
107
|
+
`reasoning_effort` (possibly `None`) and `list_models()`. The core Agent's
|
|
108
|
+
minimal `ModelClient` protocol is unchanged.
|
|
109
|
+
|
|
110
|
+
`agents` is a flattened descendant tree with `agent_id`, `parent_agent_id`,
|
|
111
|
+
`nickname`, and the existing sub-agent `status` value. The root is identified
|
|
112
|
+
by `state.agent_session_id`. Descendant IDs are assigned by their manager and
|
|
113
|
+
remain stable across that child's fork/resume. Add `?agent_id=...` to state,
|
|
114
|
+
models, history, agents, background-work or commands to target an owned child.
|
|
115
|
+
|
|
116
|
+
Background records have `tool` and `kind`. Exec records include `session_id`,
|
|
117
|
+
`pid`, `tty`, `status` (`running` or `exited`) and `exit_code`. They omit shell
|
|
118
|
+
arguments, environment and process output. Entries disappear when the exec
|
|
119
|
+
manager releases them; this is an inventory, not an execution archive.
|
|
120
|
+
Clock records include `enabled`, `period_m`, `status` (`disabled`, `paused`,
|
|
121
|
+
or `armed`) and timezone-aware `next_fire_at` (`null` when not armed).
|
|
122
|
+
|
|
123
|
+
## Commands and results
|
|
124
|
+
|
|
125
|
+
Commands use a name without the slash and an optional string argument:
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
const response = await fetch(`api/sessions/${sessionId}/commands`, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: {"Content-Type": "application/json"},
|
|
131
|
+
body: JSON.stringify({command: "effort", argument: "high"}),
|
|
132
|
+
});
|
|
133
|
+
if (!response.ok) throw new Error((await response.json()).detail);
|
|
134
|
+
const {submission_id} = await response.json();
|
|
135
|
+
const result = await fetch(
|
|
136
|
+
`api/sessions/${sessionId}/submissions/${submission_id}`,
|
|
137
|
+
).then(response => response.json());
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Accepted requests return HTTP **202**, including fast commands. Records contain
|
|
141
|
+
`submission_id`, `agent_id`, `kind`, `status`, `result`, and `error`; commands
|
|
142
|
+
also have `command`. Status progresses through `queued`, `running`, then
|
|
143
|
+
`completed` or `failed`. Failures have `error: {type, message}`. Query until
|
|
144
|
+
terminal or consume WS `submission` notifications. A 202 receipt does not imply
|
|
145
|
+
execution succeeded.
|
|
146
|
+
|
|
147
|
+
Names come from Runtime's command registry, including workspace commands.
|
|
148
|
+
`queue` is excluded here: send `/queue <message>` through the message endpoint.
|
|
149
|
+
Operations retain their idle/admission requirements. Runtime owns serialization
|
|
150
|
+
and draining. HTTP request completion or disconnect does not cancel an accepted
|
|
151
|
+
command. This adds no turn cancellation, queue editing or arbitrary tool execution.
|
|
152
|
+
|
|
153
|
+
Regular workspace message receipts are also queryable. Their successful result
|
|
154
|
+
contains `turn_id`, `output_text`, and `iterations`. Command events observed
|
|
155
|
+
from other frontends are retained as well. Retention is **all pending submissions
|
|
156
|
+
plus the last 500 completed submissions** in the workspace session's memory,
|
|
157
|
+
shared across its observed agents. Restart/deletion clears this store;
|
|
158
|
+
expired/unknown IDs return 404. Submission lookup does not need `agent_id`.
|
|
159
|
+
|
|
160
|
+
Malformed input returns 422, missing resources return 404, and admission rejected
|
|
161
|
+
because the Agent is shutting down returns 409. Execution failures after
|
|
162
|
+
admission appear in the submission record.
|
|
163
|
+
|
|
164
|
+
## History pagination
|
|
165
|
+
|
|
166
|
+
`limit` is between 1 and 200. The initial page contains the newest records;
|
|
167
|
+
each page is chronological. Pass `next_cursor` as `before` for older records;
|
|
168
|
+
stop at `next_cursor: null`. Cursors are opaque.
|
|
169
|
+
|
|
170
|
+
Items contain `index`, `type`, and `payload`. A `response_item` carries the
|
|
171
|
+
serialized conversation item, including tool calls/results and available
|
|
172
|
+
reasoning records. This does not decrypt reasoning or reconstruct unrecorded
|
|
173
|
+
output. There is currently no dedicated live reasoning-delta event; the display
|
|
174
|
+
snapshot's `thinking` field contains assistant text and is not a reasoning API.
|
|
175
|
+
A `compacted` item contains its summary in `payload.message`; it does
|
|
176
|
+
not duplicate the checkpoint's replacement history.
|
|
177
|
+
|
|
178
|
+
Recorded sessions use `source: "rollout"` and retain pre-compaction records.
|
|
179
|
+
Session metadata and duplicate display events are excluded. Reading uses bounded
|
|
180
|
+
page memory and scans the rollout up to the requested record; no separate
|
|
181
|
+
database/index is maintained. File I/O runs off the session loop.
|
|
182
|
+
|
|
183
|
+
Unrecorded sessions and forks before their first write use
|
|
184
|
+
`source: "active_history"`; only current in-memory history is available.
|
|
185
|
+
Appending preserves older cursors. Compaction, a changed session/source, or
|
|
186
|
+
the transition from unwritten to recorded history can invalidate a cursor;
|
|
187
|
+
HTTP 409 directs the client to restart pagination. Sources are append-only;
|
|
188
|
+
external rollout edits are not a supported pagination workflow.
|
|
189
|
+
|
|
190
|
+
## WebSocket state and replay
|
|
191
|
+
|
|
192
|
+
Connect to `ws/session?session_id=...`, optionally with `cursor=...` and
|
|
193
|
+
`agent_id=...`. Existing authentication and same-origin checks apply.
|
|
194
|
+
|
|
195
|
+
`hello` retains `snapshot` and `events` and adds `api_version`, `state`, `cursor`,
|
|
196
|
+
and `reset_required`. Subsequent events include a cursor and lightweight state.
|
|
197
|
+
The legacy display snapshot remains on its existing event types.
|
|
198
|
+
|
|
199
|
+
- Without a cursor, hello contains the last 200 cached events.
|
|
200
|
+
- A valid cursor replays events strictly after it, up to the 500-event window.
|
|
201
|
+
- An expired cursor, different stream or future sequence returns
|
|
202
|
+
`reset_required: true`, no replay events and a fresh snapshot/cursor.
|
|
203
|
+
- Invalid cursor syntax produces a WS error and closes the connection.
|
|
204
|
+
|
|
205
|
+
Hello's state is authoritative at its cursor; older events fill the timeline
|
|
206
|
+
and should not overwrite that state. Process live events in cursor order and
|
|
207
|
+
save the last processed cursor for reconnecting. State snapshots, cursor
|
|
208
|
+
allocation, hello subscription and publication share a lock across worker/server
|
|
209
|
+
threads.
|
|
210
|
+
|
|
211
|
+
The stream includes structured Runtime/tool/model events, `runtime_updated`
|
|
212
|
+
notifications for plan, queue, command and resource changes, and `submission`
|
|
213
|
+
notifications containing result records. Agent/background change notifications
|
|
214
|
+
tell clients to re-query the inventory.
|
|
215
|
+
|
|
216
|
+
Events are memory-only and retained by count. Replay guarantees apply inside
|
|
217
|
+
that window; a rollout is not a persistent timing/retry/WS log. Child streams
|
|
218
|
+
are created when first observed and have separate cursors, excluding earlier
|
|
219
|
+
events. Their WS connection is read-only: `ping` works; `send`/`answer` is rejected.
|
|
220
|
+
Observing a child does not enable interactive requests or rollout recording.
|
|
221
|
+
|
|
222
|
+
## Built-in page extension host
|
|
223
|
+
|
|
224
|
+
An optional `mount(host)` export in `~/.pycodex/frontend/custom.js` is called
|
|
225
|
+
once when the built-in page initializes. Side-effect-only modules still load
|
|
226
|
+
normally. Initialization failures are shown in the page. `?builtin=1` disables
|
|
227
|
+
loading and mounting. A complete HTML replacement owns its initialization.
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
export function mount(host) {
|
|
231
|
+
const update = async sessionId => {
|
|
232
|
+
if (!sessionId) return;
|
|
233
|
+
const {state} = await host.request(`api/sessions/${sessionId}/state`);
|
|
234
|
+
if (host.getActiveSessionId() === sessionId) {
|
|
235
|
+
document.body.dataset.agentBusy = String(state.busy);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
update(host.getActiveSessionId()).catch(console.error);
|
|
239
|
+
host.onSessionChanged(id => update(id).catch(console.error));
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The frozen host has `apiVersion: 1`:
|
|
244
|
+
|
|
245
|
+
| Method | Contract |
|
|
246
|
+
| --- | --- |
|
|
247
|
+
| `getActiveSessionId()` | Current tab ID; initially may be empty |
|
|
248
|
+
| `selectSession(id)` | Switch to an existing tab; returns a promise |
|
|
249
|
+
| `onSessionChanged(handler)` | Subscribe; returns an unsubscribe function |
|
|
250
|
+
| `getDraft()` | Structured composer draft, including context parts |
|
|
251
|
+
| `setDraft(draft)` | Replace current draft; `draft.parts` must be an array |
|
|
252
|
+
| `openView(href, srcPath)` | Open a file/Git/workspace link for the current session |
|
|
253
|
+
| `request(path, options)` | Fetch workspace-relative JSON; HTTP failures reject |
|
|
254
|
+
|
|
255
|
+
Tab-change callbacks run after the synchronous draft/view switch. Check the
|
|
256
|
+
current session when applying asynchronous results. Host state is browser-local;
|
|
257
|
+
this does not add cross-device draft storage.
|