ms-tau-sdk 1.2.8.dev14__tar.gz → 1.2.9.dev16__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.
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/CHANGELOG.md +15 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/PKG-INFO +6 -2
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/README.md +5 -1
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/proxy.py +24 -1
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/static/app.js +250 -7
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/static/board.css +7 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/static/index.html +40 -3
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/pyproject.toml +1 -1
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/agent_skills/tau_local_development/SKILL.md +21 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/agent_skills/tau_project_customization/SKILL.md +7 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/api/a2a.py +18 -19
- ms_tau_sdk-1.2.9.dev16/src/ms_tau_sdk/api/inspection.py +122 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/api/models.py +11 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/app.py +2 -1
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/errors.py +15 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/manager.py +665 -3
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/.gitignore +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/app.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/cli.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/config.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/env_file.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/logs.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/state.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/static/BULMA-LICENSE.txt +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/static/bulma.min.css +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/agent_skills/tau_a2a_runtime_adapter/SKILL.md +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/agent_skills/tau_repository_integration/SKILL.md +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/api/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/api/chat.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/api/dependencies.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/api/health.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/api/sessions.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/application.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/backend/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/backend/auth.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/backend/client.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/backend/local.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/backend/mcp.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/backend/models.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/backend/routes.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/cli.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/logging.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/protocols/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/protocols/a2a_message.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/protocols/a2a_roles.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/protocols/assistant_ui.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/protocols/strict_json.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/providers/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/providers/definitions.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/providers/factory.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/resources/SYSTEM.md +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/resources/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/resources/loader.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/resources/prompts/review-code-repository.md +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/deployment_health.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/events.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/extensions.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/failures.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/observability.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/provenance.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/session.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/snapshots.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/runtime/task_context.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/sessions/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/sessions/storage.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/settings.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/skills.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/tools/__init__.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/tools/mainsequence_mcp.py +0 -0
- {ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/src/ms_tau_sdk/tools/task_control.py +0 -0
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.9 — 2026-09-25
|
|
4
|
+
|
|
5
|
+
- Fixed managed A2A Task creation and continuation to send dictionary-valued message extensions
|
|
6
|
+
to Django. Missing extensions now become `{}`; unsupported shapes return HTTP 400 before a
|
|
7
|
+
backend Task mutation, instead of surfacing Django validation as HTTP 502. (Issue #40.)
|
|
8
|
+
- Added Tau Board's Agent view for readable effective Agent Card inspection, source-grouped tool
|
|
9
|
+
catalog inspection, project extension diagnostics, and bounded registered-entry source viewing.
|
|
10
|
+
- Added an explicit local project-tool workbench with schema-generated inputs, authoritative
|
|
11
|
+
validation, short-lived single-use confirmation, exact loaded-tool execution, streamed bounded
|
|
12
|
+
results, timeout and cancellation. It does not call the model or write conversation/Task history;
|
|
13
|
+
project code still has its normal real side effects.
|
|
14
|
+
- Added creation and completion/status timing to the A2A Task view. Completion uses the standard
|
|
15
|
+
A2A `task.status.timestamp`; creation is joined from Tau's existing local SQLite Task state, so
|
|
16
|
+
the wire contract remains standard.
|
|
17
|
+
|
|
3
18
|
## 1.2.8 — 2026-09-24
|
|
4
19
|
|
|
5
20
|
- Added independent `TAU_EXCLUDE_BASE_TOOLS` and `TAU_EXCLUDE_MAINSEQUENCE_MCP` process settings.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ms-tau-sdk
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.9.dev16
|
|
4
4
|
Summary: Workspace-bound Tau application primitives for Main Sequence projects
|
|
5
5
|
Project-URL: Changelog, https://github.com/mainsequence-sdk/ms-tau-sdk/blob/development/CHANGELOG.md
|
|
6
6
|
Project-URL: Documentation, https://github.com/mainsequence-sdk/ms-tau-sdk/tree/development/docs
|
|
@@ -163,7 +163,11 @@ Tau Board ships in the same SDK wheel. The `tau-board` extra declares its tested
|
|
|
163
163
|
bounds; plain SDK installations never start the board.
|
|
164
164
|
|
|
165
165
|
Open `http://127.0.0.1:8788` to chat, send A2A Messages and Tasks, inspect local SQLite state,
|
|
166
|
-
|
|
166
|
+
inspect the effective Agent Card and tool catalog, validate and explicitly run project extension
|
|
167
|
+
tools, and read logs. The A2A view combines the standard current-status timestamp with Tau's local
|
|
168
|
+
creation timestamp. Tool tests call the exact loaded tool without a model turn or history entry,
|
|
169
|
+
but execute real project code with its normal side effects. The board serves packaged Bulma CSS
|
|
170
|
+
with no frontend build or CDN. It can select
|
|
167
171
|
another loopback Tau endpoint and local state directory in its Connect view. Its Settings tab shows
|
|
168
172
|
the board process's safe environment values and edits named non-secret variables in a local `.env`
|
|
169
173
|
file; Tau or the board must restart before saved startup values take effect.
|
|
@@ -132,7 +132,11 @@ Tau Board ships in the same SDK wheel. The `tau-board` extra declares its tested
|
|
|
132
132
|
bounds; plain SDK installations never start the board.
|
|
133
133
|
|
|
134
134
|
Open `http://127.0.0.1:8788` to chat, send A2A Messages and Tasks, inspect local SQLite state,
|
|
135
|
-
|
|
135
|
+
inspect the effective Agent Card and tool catalog, validate and explicitly run project extension
|
|
136
|
+
tools, and read logs. The A2A view combines the standard current-status timestamp with Tau's local
|
|
137
|
+
creation timestamp. Tool tests call the exact loaded tool without a model turn or history entry,
|
|
138
|
+
but execute real project code with its normal side effects. The board serves packaged Bulma CSS
|
|
139
|
+
with no frontend build or CDN. It can select
|
|
136
140
|
another loopback Tau endpoint and local state directory in its Connect view. Its Settings tab shows
|
|
137
141
|
the board process's safe environment values and edits named non-secret variables in a local `.env`
|
|
138
142
|
file; Tau or the board must restart before saved startup values take effect.
|
{ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/proxy.py
RENAMED
|
@@ -27,6 +27,24 @@ ALLOWED: tuple[tuple[str, re.Pattern[str]], ...] = (
|
|
|
27
27
|
("GET", re.compile(r"/api/a2a/v1/tasks/[A-Za-z0-9._-]+:subscribe")),
|
|
28
28
|
("POST", re.compile(r"/api/a2a/v1/tasks/[A-Za-z0-9._-]+:cancel")),
|
|
29
29
|
("GET", re.compile(r"/api/a2a/v1/extendedAgentCard")),
|
|
30
|
+
(
|
|
31
|
+
"GET",
|
|
32
|
+
re.compile(r"/api/local/v1/sessions/[A-Za-z0-9._-]+/agent-inspection"),
|
|
33
|
+
),
|
|
34
|
+
(
|
|
35
|
+
"GET",
|
|
36
|
+
re.compile(r"/api/local/v1/sessions/[A-Za-z0-9._-]+/extension-sources/[a-f0-9]{24}"),
|
|
37
|
+
),
|
|
38
|
+
(
|
|
39
|
+
"POST",
|
|
40
|
+
re.compile(
|
|
41
|
+
r"/api/local/v1/sessions/[A-Za-z0-9._-]+/tools/[A-Za-z0-9._-]+:(?:validate|test)"
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
(
|
|
45
|
+
"POST",
|
|
46
|
+
re.compile(r"/api/local/v1/sessions/[A-Za-z0-9._-]+/tool-tests/[A-Za-z0-9-]+:cancel"),
|
|
47
|
+
),
|
|
30
48
|
)
|
|
31
49
|
|
|
32
50
|
|
|
@@ -82,7 +100,12 @@ async def forward(
|
|
|
82
100
|
raise TauUnavailable("Tau endpoint redirected a board request")
|
|
83
101
|
response_headers = {
|
|
84
102
|
name: upstream.headers[name]
|
|
85
|
-
for name in (
|
|
103
|
+
for name in (
|
|
104
|
+
"x-agent-session-uid",
|
|
105
|
+
"x-tau-tool-test-uid",
|
|
106
|
+
"cache-control",
|
|
107
|
+
"x-vercel-ai-ui-message-stream",
|
|
108
|
+
)
|
|
86
109
|
if name in upstream.headers
|
|
87
110
|
}
|
|
88
111
|
media_type = upstream.headers.get("content-type", "application/json").split(";", 1)[0]
|
{ms_tau_sdk-1.2.8.dev14 → ms_tau_sdk-1.2.9.dev16}/packages/tau-board/src/ms_tau_board/static/app.js
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const $ = (id) => document.getElementById(id);
|
|
2
|
-
const views = ["connect", "chat", "a2a", "tasks", "state", "logs", "settings"];
|
|
2
|
+
const views = ["connect", "agent", "chat", "a2a", "tasks", "state", "logs", "settings"];
|
|
3
3
|
const tables = ["sessions", "entries", "a2a_tasks", "a2a_task_messages", "a2a_task_outputs", "a2a_task_attempts", "a2a_task_events", "snapshots"];
|
|
4
4
|
let config = { profiles: [], selectedProfile: "" };
|
|
5
5
|
let activeView = "connect";
|
|
@@ -20,6 +20,12 @@ let logOffset = 0;
|
|
|
20
20
|
let logRecords = [];
|
|
21
21
|
let logHasMore = false;
|
|
22
22
|
let requestedLogSession = "";
|
|
23
|
+
let agentInspection = null;
|
|
24
|
+
let selectedTool = null;
|
|
25
|
+
let toolConfirmation = "";
|
|
26
|
+
let currentToolTest = "";
|
|
27
|
+
let toolTestAbort = null;
|
|
28
|
+
const a2aTaskState = new Map();
|
|
23
29
|
const catalogs = new Map();
|
|
24
30
|
const desiredSelection = { chat: null, a2a: null };
|
|
25
31
|
|
|
@@ -37,7 +43,11 @@ function notice(message, kind = "is-info") {
|
|
|
37
43
|
}
|
|
38
44
|
function dismissNotice() { $("notice").className = "notification is-hidden"; }
|
|
39
45
|
function errorMessage(data, status) {
|
|
40
|
-
if (data && typeof data === "object")
|
|
46
|
+
if (data && typeof data === "object") {
|
|
47
|
+
const value = data.error || data.message || data.detail;
|
|
48
|
+
if (value && typeof value === "object") return value.message || JSON.stringify(value);
|
|
49
|
+
return value || `HTTP ${status}`;
|
|
50
|
+
}
|
|
41
51
|
return String(data || `HTTP ${status}`);
|
|
42
52
|
}
|
|
43
53
|
async function json(url, options = {}) {
|
|
@@ -62,7 +72,7 @@ function populateSelect(select, values, selected) {
|
|
|
62
72
|
select.value = selected;
|
|
63
73
|
}
|
|
64
74
|
function renderProfiles() {
|
|
65
|
-
for (const id of ["connect-select", "chat-profile", "a2a-profile", "tasks-profile", "log-profile"]) {
|
|
75
|
+
for (const id of ["connect-select", "agent-profile", "chat-profile", "a2a-profile", "tasks-profile", "log-profile"]) {
|
|
66
76
|
const select = $(id);
|
|
67
77
|
const selected = id === "connect-select" ? config.selectedProfile : (select.value || config.selectedProfile);
|
|
68
78
|
clear(select);
|
|
@@ -214,11 +224,191 @@ function showView(view) {
|
|
|
214
224
|
document.querySelector(`[data-nav="${item}"]`).classList.toggle("is-active", item === activeView);
|
|
215
225
|
}
|
|
216
226
|
if (activeView === "a2a") refreshTasks().catch(report);
|
|
227
|
+
if (activeView === "agent") refreshAgentSessions().catch(report);
|
|
217
228
|
if (activeView === "tasks") refreshTaskExplorer().catch(report);
|
|
218
229
|
if (activeView === "state") refreshState().catch(report);
|
|
219
230
|
if (activeView === "logs") refreshLogChoices().then(() => refreshLogs()).catch(report);
|
|
220
231
|
if (activeView === "settings") refreshSettings().catch(report);
|
|
221
232
|
}
|
|
233
|
+
function agentHeaders(extra = {}) { return { "X-Tau-Board-Profile": $("agent-profile").value, ...extra }; }
|
|
234
|
+
function agentSession() { return $("agent-session").value; }
|
|
235
|
+
function invalidateToolConfirmation() {
|
|
236
|
+
toolConfirmation = "";
|
|
237
|
+
$("tool-run").disabled = true;
|
|
238
|
+
clear($("tool-test-status"));
|
|
239
|
+
}
|
|
240
|
+
async function refreshAgentSessions() {
|
|
241
|
+
const previous = agentSession() || $("chat-session").value.trim() || $("a2a-context").value.trim();
|
|
242
|
+
const result = await json("/api/board/sessions", { headers: agentHeaders() });
|
|
243
|
+
const select = $("agent-session"); clear(select);
|
|
244
|
+
select.append(option("", "Select a loaded local session"));
|
|
245
|
+
for (const session of result.sessions || []) select.append(option(session.uid, `${session.uid} · ${session.model || "unknown model"}`));
|
|
246
|
+
select.value = previous;
|
|
247
|
+
if (!select.value && select.options.length === 2) select.selectedIndex = 1;
|
|
248
|
+
if (select.value) await inspectAgent();
|
|
249
|
+
}
|
|
250
|
+
function renderAgentCard(card) {
|
|
251
|
+
const box = $("agent-card"); clear(box);
|
|
252
|
+
if (!card) { box.append(node("p", "No Agent Card was returned.", "help")); $("agent-card-json").textContent = "null"; return; }
|
|
253
|
+
for (const [label, value] of [["Name", card.name], ["Description", card.description], ["Version", card.version], ["URL", card.url]]) detailLine(box, label, value);
|
|
254
|
+
const capabilities = card.capabilities || {};
|
|
255
|
+
detailLine(box, "Capabilities", Object.entries(capabilities).filter(([, value]) => typeof value === "boolean" && value).map(([name]) => name).join(", ") || "None declared");
|
|
256
|
+
detailLine(box, "Protocol extensions", (capabilities.extensions || []).map((item) => item.uri || "unnamed").join(", ") || "None");
|
|
257
|
+
detailLine(box, "Agent Card skills", (card.skills || []).map((item) => item.name || item.id || "unnamed").join(", ") || "None");
|
|
258
|
+
$("agent-card-json").textContent = JSON.stringify(card, null, 2);
|
|
259
|
+
}
|
|
260
|
+
function renderExtensions(items, diagnostics) {
|
|
261
|
+
const box = $("agent-extensions"); clear(box);
|
|
262
|
+
for (const item of items || []) {
|
|
263
|
+
const card = node("div", "", "extension-card");
|
|
264
|
+
card.append(node("strong", item.name), node("span", ` ${item.origin}`, "tag is-light ml-2"));
|
|
265
|
+
card.append(node("p", item.entryPath || "Source is not exposed", "mono"));
|
|
266
|
+
card.append(node("p", `Tools: ${(item.tools || []).join(", ") || "none"}`, "help")); box.append(card);
|
|
267
|
+
}
|
|
268
|
+
for (const item of diagnostics || []) {
|
|
269
|
+
const card = node("div", "", "extension-card has-text-danger");
|
|
270
|
+
card.textContent = `${item.severity} · ${item.name} · ${item.errorType}${item.path ? ` · ${item.path}` : ""}`; box.append(card);
|
|
271
|
+
}
|
|
272
|
+
if (!(items || []).length && !(diagnostics || []).length) box.append(node("p", "No project extensions or diagnostics.", "help"));
|
|
273
|
+
}
|
|
274
|
+
function renderTools(tools) {
|
|
275
|
+
const box = $("agent-tools"); clear(box);
|
|
276
|
+
const labels = { project_extension: "Project extensions", tau_coding: "Tau coding", mainsequence_mcp: "Main Sequence MCP", protocol: "A2A Task controls", user_extension: "User extensions", built_in_extension: "Tau extensions", explicit_extension: "Explicit extensions", unknown_extension: "Other extensions" };
|
|
277
|
+
const groups = new Map();
|
|
278
|
+
for (const tool of tools || []) {
|
|
279
|
+
if (!groups.has(tool.category)) groups.set(tool.category, []);
|
|
280
|
+
groups.get(tool.category).push(tool);
|
|
281
|
+
}
|
|
282
|
+
const order = ["project_extension", "tau_coding", "mainsequence_mcp", "protocol", ...groups.keys()];
|
|
283
|
+
for (const category of [...new Set(order)]) {
|
|
284
|
+
if (!groups.has(category)) continue;
|
|
285
|
+
box.append(node("p", labels[category] || category, "tool-group"));
|
|
286
|
+
for (const tool of groups.get(category)) {
|
|
287
|
+
const button = node("button", "", "button is-light"); button.type = "button";
|
|
288
|
+
button.append(node("strong", tool.label || tool.name), node("span", `\n${tool.name}`, "mono help"));
|
|
289
|
+
button.addEventListener("click", () => selectTool(tool, button)); box.append(button);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (!(tools || []).length) box.append(node("p", "This session has no executable tools.", "help"));
|
|
293
|
+
}
|
|
294
|
+
function defaultFieldValue(schema) {
|
|
295
|
+
if (Object.hasOwn(schema, "default")) return schema.default;
|
|
296
|
+
if (Array.isArray(schema.examples) && schema.examples.length) return schema.examples[0];
|
|
297
|
+
return "";
|
|
298
|
+
}
|
|
299
|
+
function renderToolForm(schema) {
|
|
300
|
+
const form = $("tool-form"); clear(form);
|
|
301
|
+
const required = new Set(schema.required || []);
|
|
302
|
+
for (const [name, field] of Object.entries(schema.properties || {})) {
|
|
303
|
+
const wrapper = node("div", "", "field tool-field");
|
|
304
|
+
wrapper.append(node("label", `${name}${required.has(name) ? " *" : ""}`, "label"));
|
|
305
|
+
let input;
|
|
306
|
+
if (Array.isArray(field.enum)) {
|
|
307
|
+
const control = node("div", "", "select is-fullwidth"); input = node("select");
|
|
308
|
+
if (!required.has(name)) input.append(option("", "Not set"));
|
|
309
|
+
for (const value of field.enum) input.append(option(JSON.stringify(value), String(value)));
|
|
310
|
+
control.append(input); wrapper.append(control); input.dataset.encoding = "json";
|
|
311
|
+
} else if (field.type === "boolean") {
|
|
312
|
+
const control = node("div", "", "select is-fullwidth"); input = node("select");
|
|
313
|
+
if (!required.has(name)) input.append(option("", "Not set"));
|
|
314
|
+
input.append(option("true", "True"), option("false", "False")); control.append(input); wrapper.append(control); input.dataset.encoding = "json";
|
|
315
|
+
} else if (field.type === "object" || field.type === "array") {
|
|
316
|
+
input = node("textarea", "", "textarea mono"); input.rows = 3; input.dataset.encoding = "json"; wrapper.append(input);
|
|
317
|
+
} else {
|
|
318
|
+
input = node("input", "", "input"); input.type = field.type === "number" || field.type === "integer" ? "number" : "text";
|
|
319
|
+
if (field.type === "integer") input.step = "1"; input.dataset.encoding = field.type || "string"; wrapper.append(input);
|
|
320
|
+
}
|
|
321
|
+
input.dataset.toolField = name; input.dataset.required = required.has(name) ? "true" : "false";
|
|
322
|
+
const initial = defaultFieldValue(field);
|
|
323
|
+
if (initial !== "") input.value = input.dataset.encoding === "json" ? JSON.stringify(initial) : String(initial);
|
|
324
|
+
if (field.description) wrapper.append(node("p", field.description, "help"));
|
|
325
|
+
input.addEventListener("input", () => { invalidateToolConfirmation(); try { $("tool-arguments").value = JSON.stringify(formArguments(), null, 2); } catch (_) { /* raw editor remains available */ } });
|
|
326
|
+
form.append(wrapper);
|
|
327
|
+
}
|
|
328
|
+
if (!Object.keys(schema.properties || {}).length) form.append(node("p", "This tool has no declared fields. Use the JSON editor for additional properties.", "help mb-3"));
|
|
329
|
+
try { $("tool-arguments").value = JSON.stringify(formArguments(), null, 2); } catch (_) { $("tool-arguments").value = "{}"; }
|
|
330
|
+
}
|
|
331
|
+
function formArguments() {
|
|
332
|
+
const result = {};
|
|
333
|
+
for (const input of document.querySelectorAll("[data-tool-field]")) {
|
|
334
|
+
if (input.value === "" && input.dataset.required !== "true") continue;
|
|
335
|
+
let value = input.value;
|
|
336
|
+
if (input.dataset.encoding === "json") value = JSON.parse(value || "null");
|
|
337
|
+
else if (input.dataset.encoding === "number") value = Number(value);
|
|
338
|
+
else if (input.dataset.encoding === "integer") value = Number.parseInt(value, 10);
|
|
339
|
+
result[input.dataset.toolField] = value;
|
|
340
|
+
}
|
|
341
|
+
return result;
|
|
342
|
+
}
|
|
343
|
+
function selectTool(tool, button) {
|
|
344
|
+
selectedTool = tool; invalidateToolConfirmation(); currentToolTest = "";
|
|
345
|
+
for (const item of $("agent-tools").querySelectorAll("button")) item.classList.toggle("is-selected", item === button);
|
|
346
|
+
$("tool-title").textContent = tool.label || tool.name;
|
|
347
|
+
$("tool-description").textContent = tool.description || "No description.";
|
|
348
|
+
const meta = $("tool-meta"); clear(meta);
|
|
349
|
+
for (const value of [tool.name, tool.category, tool.executionMode, tool.extension]) if (value) meta.append(node("span", value, "tag is-light"));
|
|
350
|
+
$("tool-schema").textContent = JSON.stringify(tool.parameters, null, 2);
|
|
351
|
+
$("tool-source").disabled = !tool.sourceUid; $("tool-source-output").classList.add("is-hidden");
|
|
352
|
+
renderToolForm(tool.parameters || { type: "object", properties: {} });
|
|
353
|
+
$("tool-validate").disabled = !tool.testable;
|
|
354
|
+
$("tool-confirm").checked = false; $("tool-cancel").disabled = true;
|
|
355
|
+
$("tool-output").textContent = tool.testable ? "Fill the arguments, then validate before running." : (tool.testingReason || "This tool is inspect-only.");
|
|
356
|
+
}
|
|
357
|
+
async function inspectAgent() {
|
|
358
|
+
const session = agentSession();
|
|
359
|
+
if (!session) throw new Error("Select a local session to inspect.");
|
|
360
|
+
agentInspection = await json(`/tau/api/local/v1/sessions/${encodeURIComponent(session)}/agent-inspection`, { headers: agentHeaders() });
|
|
361
|
+
if (!agentInspection.available) {
|
|
362
|
+
clear($("agent-tools")); clear($("agent-card")); clear($("agent-extensions"));
|
|
363
|
+
$("agent-overview").textContent = "Session is not loaded. Send a Chat or A2A message, then inspect again."; return;
|
|
364
|
+
}
|
|
365
|
+
const overview = $("agent-overview"); clear(overview);
|
|
366
|
+
for (const [label, value] of [["Agent", agentInspection.agentUid], ["Session", agentInspection.sessionUid], ["Provider", agentInspection.model?.provider], ["Model", agentInspection.model?.model], ["Catalog", agentInspection.catalogDigest], ["State", agentInspection.busy ? "Busy" : "Idle"]]) detailLine(overview, label, value);
|
|
367
|
+
renderAgentCard(agentInspection.agentCard); renderExtensions(agentInspection.extensions, agentInspection.diagnostics); renderTools(agentInspection.tools);
|
|
368
|
+
}
|
|
369
|
+
async function viewToolSource() {
|
|
370
|
+
if (!selectedTool?.sourceUid) return;
|
|
371
|
+
const result = await json(`/tau/api/local/v1/sessions/${encodeURIComponent(agentSession())}/extension-sources/${selectedTool.sourceUid}`, { headers: agentHeaders() });
|
|
372
|
+
const output = $("tool-source-output"); output.textContent = `${result.path}\n\n${result.content}`; output.classList.remove("is-hidden");
|
|
373
|
+
}
|
|
374
|
+
function rawToolArguments() {
|
|
375
|
+
const value = JSON.parse($("tool-arguments").value || "{}");
|
|
376
|
+
if (!value || Array.isArray(value) || typeof value !== "object") throw new Error("Tool arguments must be a JSON object.");
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
async function validateTool() {
|
|
380
|
+
if (!selectedTool?.testable) throw new Error("Select a testable project tool.");
|
|
381
|
+
const body = { catalogDigest: agentInspection.catalogDigest, arguments: rawToolArguments() };
|
|
382
|
+
const result = await json(`/tau/api/local/v1/sessions/${encodeURIComponent(agentSession())}/tools/${encodeURIComponent(selectedTool.name)}:validate`, { method: "POST", headers: agentHeaders({ "Content-Type": "application/json" }), body: JSON.stringify(body) });
|
|
383
|
+
toolConfirmation = result.confirmation; $("tool-arguments").value = JSON.stringify(result.arguments, null, 2);
|
|
384
|
+
const status = $("tool-test-status"); clear(status); status.append(node("span", `Validated · confirmation expires in ${result.expiresInSeconds}s`, "tag is-success is-light"));
|
|
385
|
+
$("tool-run").disabled = !$("tool-confirm").checked;
|
|
386
|
+
}
|
|
387
|
+
async function runTool() {
|
|
388
|
+
if (!toolConfirmation || !$("tool-confirm").checked) throw new Error("Validate the arguments and confirm the side-effect warning first.");
|
|
389
|
+
const body = { catalogDigest: agentInspection.catalogDigest, arguments: rawToolArguments(), confirmation: toolConfirmation };
|
|
390
|
+
toolConfirmation = ""; $("tool-run").disabled = true; $("tool-validate").disabled = true; $("tool-output").textContent = "Starting tool…";
|
|
391
|
+
toolTestAbort = new AbortController();
|
|
392
|
+
try {
|
|
393
|
+
const response = await fetch(`/tau/api/local/v1/sessions/${encodeURIComponent(agentSession())}/tools/${encodeURIComponent(selectedTool.name)}:test`, { method: "POST", headers: agentHeaders({ "Content-Type": "application/json" }), body: JSON.stringify(body), signal: toolTestAbort.signal });
|
|
394
|
+
if (!response.ok) throw new Error(errorMessage(await response.json().catch(() => ({})), response.status));
|
|
395
|
+
currentToolTest = response.headers.get("X-Tau-Tool-Test-Uid") || ""; $("tool-cancel").disabled = !currentToolTest;
|
|
396
|
+
await readSSE(response, (event) => {
|
|
397
|
+
if (event.type === "started") $("tool-output").textContent = `Running ${event.toolName}…`;
|
|
398
|
+
if (event.type === "update") $("tool-output").textContent += `\n\nUpdate:\n${JSON.stringify(event.result, null, 2)}`;
|
|
399
|
+
if (event.type === "completed") $("tool-output").textContent = `${event.truncated ? "TRUNCATED\n" : ""}${JSON.stringify(event.result, null, 2)}`;
|
|
400
|
+
if (event.type === "error") $("tool-output").textContent = `${event.errorType || event.error}: ${event.message}`;
|
|
401
|
+
if (event.type === "cancelled") $("tool-output").textContent += "\n\nCancelled.";
|
|
402
|
+
if (event.type === "finished") { const status = $("tool-test-status"); clear(status); status.append(node("span", event.outcome, `tag ${event.outcome === "completed" ? "is-success" : "is-warning"} is-light`)); }
|
|
403
|
+
});
|
|
404
|
+
} finally {
|
|
405
|
+
currentToolTest = ""; toolTestAbort = null; $("tool-cancel").disabled = true; $("tool-validate").disabled = !selectedTool?.testable; $("tool-confirm").checked = false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function cancelTool() {
|
|
409
|
+
if (!currentToolTest) return;
|
|
410
|
+
await json(`/tau/api/local/v1/sessions/${encodeURIComponent(agentSession())}/tool-tests/${encodeURIComponent(currentToolTest)}:cancel`, { method: "POST", headers: agentHeaders({ "Content-Type": "application/json" }), body: "{}" });
|
|
411
|
+
}
|
|
222
412
|
function report(error) { notice(error.message || String(error), "is-danger"); }
|
|
223
413
|
function chatLine(who, type = "agent") {
|
|
224
414
|
const item = node("div", "", `chat-line ${type}`);
|
|
@@ -319,11 +509,37 @@ function taskText(task) {
|
|
|
319
509
|
const parts = (task.artifacts || []).flatMap((artifact) => artifact.parts || []);
|
|
320
510
|
return parts.map((part) => part.text ?? (part.data ? JSON.stringify(part.data, null, 2) : "")).filter(Boolean).join("\n");
|
|
321
511
|
}
|
|
512
|
+
const terminalTaskStates = new Set([
|
|
513
|
+
"TASK_STATE_COMPLETED", "TASK_STATE_FAILED", "TASK_STATE_CANCELED", "TASK_STATE_REJECTED",
|
|
514
|
+
]);
|
|
515
|
+
function taskTiming(task) {
|
|
516
|
+
const local = a2aTaskState.get(task.id) || {};
|
|
517
|
+
const statusTime = task.status?.timestamp || local.status_timestamp || "";
|
|
518
|
+
const state = task.status?.state || "TASK_STATE_UNKNOWN";
|
|
519
|
+
return {
|
|
520
|
+
created: local.created_at || "",
|
|
521
|
+
statusTime,
|
|
522
|
+
terminal: terminalTaskStates.has(state),
|
|
523
|
+
completed: state === "TASK_STATE_COMPLETED",
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
function displayTime(value) {
|
|
527
|
+
if (!value) return "—";
|
|
528
|
+
const parsed = new Date(value);
|
|
529
|
+
return Number.isNaN(parsed.valueOf()) ? value : parsed.toLocaleString();
|
|
530
|
+
}
|
|
531
|
+
function timedCell(value, label = "") {
|
|
532
|
+
const cell = node("td", label ? `${label}: ${displayTime(value)}` : displayTime(value));
|
|
533
|
+
if (value) { cell.classList.add("mono"); cell.title = value; }
|
|
534
|
+
return cell;
|
|
535
|
+
}
|
|
322
536
|
function displayTask(task) {
|
|
323
537
|
if (!task) return;
|
|
324
538
|
$("a2a-task-id").value = task.id || "";
|
|
325
539
|
$("a2a-context").value = task.contextId || "";
|
|
326
|
-
|
|
540
|
+
const timing = taskTiming(task);
|
|
541
|
+
const statusLabel = timing.completed ? "Completed" : (timing.terminal ? "Terminal status" : "Status recorded");
|
|
542
|
+
$("task-output").textContent = `${task.id} · ${task.status?.state || "unknown"}\nCreated: ${displayTime(timing.created)}\n${statusLabel}: ${displayTime(timing.statusTime)}\n\n${taskText(task)}`;
|
|
327
543
|
}
|
|
328
544
|
function handleA2AFrame(frame) {
|
|
329
545
|
const value = frame.result || frame;
|
|
@@ -392,11 +608,26 @@ async function cancelTask(id) {
|
|
|
392
608
|
async function refreshTasks() {
|
|
393
609
|
const contextId = $("a2a-context").value.trim();
|
|
394
610
|
const url = "/tau/api/a2a/v1/tasks" + (contextId ? `?contextId=${encodeURIComponent(contextId)}` : "");
|
|
395
|
-
const
|
|
611
|
+
const headers = tauHeaders("a2a");
|
|
612
|
+
const localUrl = "/api/board/tasks" + (contextId ? `?sessionUid=${encodeURIComponent(contextId)}` : "");
|
|
613
|
+
const [value, localValue] = await Promise.all([
|
|
614
|
+
json(url, { headers }),
|
|
615
|
+
json(localUrl, { headers }),
|
|
616
|
+
]);
|
|
617
|
+
a2aTaskState.clear();
|
|
618
|
+
for (const task of localValue.tasks || []) a2aTaskState.set(task.task_id, task);
|
|
396
619
|
const body = $("task-list"); clear(body);
|
|
397
620
|
for (const task of (value.tasks || []).slice(-100).reverse()) {
|
|
398
621
|
const row = node("tr");
|
|
399
|
-
|
|
622
|
+
const timing = taskTiming(task);
|
|
623
|
+
const statusLabel = timing.completed ? "Completed" : (timing.terminal ? "Terminal" : "Status");
|
|
624
|
+
row.append(
|
|
625
|
+
node("td", task.id, "mono"),
|
|
626
|
+
node("td", task.contextId || "", "mono"),
|
|
627
|
+
node("td", task.status?.state || ""),
|
|
628
|
+
timedCell(timing.created),
|
|
629
|
+
timedCell(timing.statusTime, statusLabel),
|
|
630
|
+
);
|
|
400
631
|
const actions = node("td");
|
|
401
632
|
for (const [label, action] of [["Open", () => getTask(task.id)], ["Inspect", () => { $("tasks-profile").value = $("a2a-profile").value; selectedTaskId = task.id; showView("tasks"); }], ["Follow", () => subscribeTask(task.id)], ["Cancel", () => cancelTask(task.id)], ["Continue", () => {
|
|
402
633
|
$("a2a-kind").value = "continue"; $("a2a-task-id").value = task.id; $("a2a-context").value = task.contextId || ""; $("a2a-input").focus();
|
|
@@ -407,7 +638,10 @@ async function refreshTasks() {
|
|
|
407
638
|
}
|
|
408
639
|
row.append(actions); body.append(row);
|
|
409
640
|
}
|
|
410
|
-
if (!(value.tasks || []).length) { const row = node("tr"); const cell = node("td", "No Tasks in this context yet."); cell.colSpan =
|
|
641
|
+
if (!(value.tasks || []).length) { const row = node("tr"); const cell = node("td", "No Tasks in this context yet."); cell.colSpan = 6; row.append(cell); body.append(row); }
|
|
642
|
+
const currentId = $("a2a-task-id").value.trim();
|
|
643
|
+
const current = (value.tasks || []).find((task) => task.id === currentId);
|
|
644
|
+
if (current) displayTask(current);
|
|
411
645
|
}
|
|
412
646
|
async function refreshState() {
|
|
413
647
|
const result = await json("/api/board/state");
|
|
@@ -674,6 +908,15 @@ async function start() {
|
|
|
674
908
|
bind("save-profile", "click", saveProfile);
|
|
675
909
|
bind("delete-profile", "click", deleteProfile);
|
|
676
910
|
bind("check-connection", "click", checkConnection);
|
|
911
|
+
bind("agent-profile", "change", refreshAgentSessions);
|
|
912
|
+
bind("agent-session", "change", inspectAgent);
|
|
913
|
+
bind("agent-refresh", "click", inspectAgent);
|
|
914
|
+
bind("tool-source", "click", viewToolSource);
|
|
915
|
+
bind("tool-validate", "click", validateTool);
|
|
916
|
+
bind("tool-run", "click", runTool);
|
|
917
|
+
bind("tool-cancel", "click", cancelTool);
|
|
918
|
+
bind("tool-confirm", "change", () => { $("tool-run").disabled = !toolConfirmation || !$("tool-confirm").checked; });
|
|
919
|
+
$("tool-arguments").addEventListener("input", invalidateToolConfirmation);
|
|
677
920
|
for (const kind of ["chat", "a2a"]) {
|
|
678
921
|
bind(`${kind}-profile`, "change", () => loadCatalog(kind));
|
|
679
922
|
for (const field of ["provider", "model", "thinking"]) bind(`${kind}-${field}`, "change", () => selectionChanged(kind, field));
|
|
@@ -32,6 +32,13 @@ td { max-width: 24rem; overflow-wrap: anywhere; font-size: .82rem; }
|
|
|
32
32
|
.task-picker { max-height: 65vh; overflow: auto; }
|
|
33
33
|
.task-picker button { display: block; width: 100%; text-align: left; white-space: normal; height: auto; margin: .35rem 0; overflow-wrap: anywhere; }
|
|
34
34
|
.task-picker button.is-selected { border-color: var(--bulma-primary); background: var(--bulma-scheme-main-ter); color: var(--bulma-text); font-weight: 700; }
|
|
35
|
+
.tool-picker { max-height: 72vh; overflow: auto; }
|
|
36
|
+
.tool-group { margin: 1rem 0 .4rem; color: var(--bulma-text-weak); font-size: .7rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; }
|
|
37
|
+
.tool-picker button { display: block; width: 100%; height: auto; margin-bottom: .4rem; padding: .65rem; text-align: left; white-space: normal; overflow-wrap: anywhere; }
|
|
38
|
+
.tool-picker button.is-selected { border-color: var(--bulma-primary); background: var(--bulma-primary-soft); }
|
|
39
|
+
.source-panel { max-height: 30rem; overflow: auto; margin-top: .75rem; padding: .75rem; border: 1px solid var(--bulma-border); border-radius: .4rem; background: var(--bulma-scheme-main-bis); white-space: pre-wrap; overflow-wrap: anywhere; font-size: .78rem; }
|
|
40
|
+
.tool-field { margin-bottom: .8rem; }
|
|
41
|
+
.extension-card { border-bottom: 1px solid var(--bulma-border-weak); padding: .55rem 0; font-size: .82rem; overflow-wrap: anywhere; }
|
|
35
42
|
#tasks-detail { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1rem; }
|
|
36
43
|
.settings-table td { vertical-align: middle; }
|
|
37
44
|
.settings-table td:first-child { min-width: 15rem; }
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Tau Board</title>
|
|
7
7
|
<link rel="stylesheet" href="/assets/bulma.min.css">
|
|
8
|
-
<link rel="stylesheet" href="/assets/board.css?v=
|
|
9
|
-
<script type="module" src="/assets/app.js?v=
|
|
8
|
+
<link rel="stylesheet" href="/assets/board.css?v=3">
|
|
9
|
+
<script type="module" src="/assets/app.js?v=3"></script>
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<header class="board-header">
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
<nav class="tabs is-boxed" aria-label="Board views">
|
|
23
23
|
<ul>
|
|
24
24
|
<li class="is-active" data-nav="connect"><a href="#connect">Connect</a></li>
|
|
25
|
+
<li data-nav="agent"><a href="#agent">Agent</a></li>
|
|
25
26
|
<li data-nav="chat"><a href="#chat">Chat</a></li>
|
|
26
27
|
<li data-nav="a2a"><a href="#a2a">A2A</a></li>
|
|
27
28
|
<li data-nav="tasks"><a href="#tasks">Tasks</a></li>
|
|
@@ -59,6 +60,42 @@
|
|
|
59
60
|
</div>
|
|
60
61
|
</section>
|
|
61
62
|
|
|
63
|
+
<section id="view-agent" class="board-view is-hidden">
|
|
64
|
+
<div class="columns is-variable is-4">
|
|
65
|
+
<div class="column is-one-quarter"><div class="box">
|
|
66
|
+
<h2 class="title is-5">Loaded agent</h2>
|
|
67
|
+
<div class="field"><label class="label" for="agent-profile">Endpoint profile</label><div class="select is-fullwidth"><select id="agent-profile"></select></div></div>
|
|
68
|
+
<div class="field"><label class="label" for="agent-session">Local session</label><div class="select is-fullwidth"><select id="agent-session"><option value="">No session selected</option></select></div><p class="help">Inspection never loads a session. Start or resume it through Chat or A2A first.</p></div>
|
|
69
|
+
<button class="button is-primary is-fullwidth" id="agent-refresh">Inspect agent</button>
|
|
70
|
+
<div id="agent-overview" class="detail-list mt-4">Select an already loaded session.</div>
|
|
71
|
+
<details class="mt-4"><summary class="has-text-weight-semibold">Effective Agent Card</summary><div id="agent-card" class="detail-list mt-3"></div><pre id="agent-card-json" class="source-panel"></pre></details>
|
|
72
|
+
<details class="mt-4"><summary class="has-text-weight-semibold">Extensions and diagnostics</summary><div id="agent-extensions" class="mt-3"></div></details>
|
|
73
|
+
</div></div>
|
|
74
|
+
<div class="column is-one-quarter"><div class="box">
|
|
75
|
+
<h2 class="title is-5">Effective tools</h2>
|
|
76
|
+
<p class="help mb-3">The exact catalog exposed to the selected session, grouped by runtime ownership.</p>
|
|
77
|
+
<div id="agent-tools" class="tool-picker"></div>
|
|
78
|
+
</div></div>
|
|
79
|
+
<div class="column"><div class="box">
|
|
80
|
+
<h2 id="tool-title" class="title is-5">Tool details</h2>
|
|
81
|
+
<div id="tool-meta" class="tags"></div>
|
|
82
|
+
<p id="tool-description" class="mb-4">Select a tool to inspect its contract.</p>
|
|
83
|
+
<details open><summary class="has-text-weight-semibold">Argument schema</summary><pre id="tool-schema" class="source-panel"></pre></details>
|
|
84
|
+
<div class="buttons mt-3"><button class="button is-small" id="tool-source" disabled>View project source</button></div>
|
|
85
|
+
<pre id="tool-source-output" class="source-panel is-hidden"></pre>
|
|
86
|
+
<hr>
|
|
87
|
+
<h3 class="title is-6">Project tool workbench</h3>
|
|
88
|
+
<div class="notification is-warning is-light">This invokes real project Python code with Tau's filesystem, network, environment, and credential access. It is not a sandbox or rollback.</div>
|
|
89
|
+
<div id="tool-form"></div>
|
|
90
|
+
<div class="field"><label class="label" for="tool-arguments">Canonical JSON arguments</label><textarea class="textarea mono" id="tool-arguments" rows="8" spellcheck="false">{}</textarea></div>
|
|
91
|
+
<div class="buttons"><button class="button" id="tool-validate" disabled>Validate</button><button class="button is-danger" id="tool-run" disabled>Run tool</button><button class="button is-warning is-light" id="tool-cancel" disabled>Cancel</button></div>
|
|
92
|
+
<label class="checkbox mb-4"><input type="checkbox" id="tool-confirm"> I understand this executes real code and may cause side effects.</label>
|
|
93
|
+
<div id="tool-test-status" class="tags"></div>
|
|
94
|
+
<pre id="tool-output" class="output-panel">No tool test has run.</pre>
|
|
95
|
+
</div></div>
|
|
96
|
+
</div>
|
|
97
|
+
</section>
|
|
98
|
+
|
|
62
99
|
<section id="view-chat" class="board-view is-hidden">
|
|
63
100
|
<div class="columns is-variable is-5">
|
|
64
101
|
<div class="column is-one-third"><div class="box">
|
|
@@ -93,7 +130,7 @@
|
|
|
93
130
|
</div></div><div class="column"><div class="box">
|
|
94
131
|
<div class="level"><div class="level-left"><h2 class="title is-5">Tasks</h2></div><div class="level-right"><button class="button is-small" id="refresh-tasks">Refresh</button></div></div>
|
|
95
132
|
<div id="task-output" class="output-panel" aria-live="polite">Choose an action to begin.</div>
|
|
96
|
-
<div class="table-container"><table class="table is-fullwidth is-striped is-hoverable"><thead><tr><th>Task</th><th>Context</th><th>Status</th><th></th></tr></thead><tbody id="task-list"></tbody></table></div>
|
|
133
|
+
<div class="table-container"><table class="table is-fullwidth is-striped is-hoverable"><thead><tr><th>Task</th><th>Context</th><th>Status</th><th>Created</th><th>Completed / status time</th><th></th></tr></thead><tbody id="task-list"></tbody></table></div>
|
|
97
134
|
</div></div></div>
|
|
98
135
|
</section>
|
|
99
136
|
|
|
@@ -102,6 +102,27 @@ identity:
|
|
|
102
102
|
These are the only A2A-adjacent capability boundaries; public local Message and Task requests must not return
|
|
103
103
|
`local_mode_capability_unsupported`.
|
|
104
104
|
|
|
105
|
+
Each standard A2A Task carries the time its current state was recorded in
|
|
106
|
+
`task.status.timestamp`. For `TASK_STATE_COMPLETED`, that is the completion time. The A2A Task
|
|
107
|
+
object has no standard top-level creation timestamp; Tau persists `created_at` in local SQLite,
|
|
108
|
+
and Tau Board joins that local row when it displays the A2A Task list. Do not invent extra A2A
|
|
109
|
+
wire fields for Board presentation.
|
|
110
|
+
|
|
111
|
+
## Inspect and test with Tau Board
|
|
112
|
+
|
|
113
|
+
Install `ms-tau-sdk[tau-board]`, run `tau-board` as a separate process, and connect it to the
|
|
114
|
+
loopback local-mode Tau endpoint. The Agent tab requires an already loaded session: first send a
|
|
115
|
+
Chat or A2A request, then select that session. It shows the effective Agent Card, loaded tools by
|
|
116
|
+
source, project extension diagnostics, and registered project extension entry source.
|
|
117
|
+
|
|
118
|
+
Only project-extension tools with an object JSON Schema are runnable in the workbench. Select the
|
|
119
|
+
tool, fill its generated form or raw JSON, validate the exact canonical arguments, acknowledge
|
|
120
|
+
the side-effect warning, then run it. Validation never calls the tool. Run invokes the exact
|
|
121
|
+
loaded tool without a model call and without adding chat entries, A2A Tasks, or artifacts. The
|
|
122
|
+
tool still has the Tau process's real filesystem, network, environment, and credential access;
|
|
123
|
+
there is no automatic rollback. A busy session, stale catalog, expired confirmation, non-project
|
|
124
|
+
tool, oversized output, timeout, or cancellation must fail explicitly.
|
|
125
|
+
|
|
105
126
|
## Outbound A2A through Main Sequence MCP
|
|
106
127
|
|
|
107
128
|
Keep the projected `a2a.send_message` tool available. A local agent may send a Message or create a
|
|
@@ -93,3 +93,10 @@ Verify each project-owned capability through its observable interface and focuse
|
|
|
93
93
|
that documented tool names and schemas match the loaded catalog, lifecycle hooks clean up their
|
|
94
94
|
resources, and health diagnostics report extension load errors without exposing prompt content or
|
|
95
95
|
secrets.
|
|
96
|
+
|
|
97
|
+
For interactive local verification, start Tau in local mode, load a session through Chat or A2A,
|
|
98
|
+
then open Tau Board's Agent tab. Inspect the effective tool category, JSON Schema, extension entry
|
|
99
|
+
source, and diagnostics. For a project-owned tool, fill the generated form or raw JSON, validate
|
|
100
|
+
the canonical arguments, explicitly acknowledge the side-effect warning, and run it. This invokes
|
|
101
|
+
the exact loaded tool without a model turn or conversation/Task-history entry. It is not a sandbox:
|
|
102
|
+
filesystem, network, credential, and external-system side effects are the project's responsibility.
|
|
@@ -737,6 +737,20 @@ async def _collect_validated_turn(
|
|
|
737
737
|
raise AssertionError("strict JSON repair loop did not terminate")
|
|
738
738
|
|
|
739
739
|
|
|
740
|
+
def _backend_task_message(message: dict[str, Any]) -> dict[str, Any]:
|
|
741
|
+
extensions = message.get("extensions", {})
|
|
742
|
+
if not isinstance(extensions, dict):
|
|
743
|
+
raise HTTPException(status_code=400, detail="message.extensions must be an object")
|
|
744
|
+
return {
|
|
745
|
+
"message_id": message["messageId"],
|
|
746
|
+
"role": "user",
|
|
747
|
+
"parts": message["parts"],
|
|
748
|
+
"metadata": message.get("metadata", {}),
|
|
749
|
+
"extensions": extensions,
|
|
750
|
+
"reference_task_ids": message.get("referenceTaskIds", []),
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
|
|
740
754
|
async def _create_backend_task(
|
|
741
755
|
client: MainSequenceClient,
|
|
742
756
|
*,
|
|
@@ -745,6 +759,7 @@ async def _create_backend_task(
|
|
|
745
759
|
output_contract: StrictJsonContract | None = None,
|
|
746
760
|
provenance: TurnProvenance | None = None,
|
|
747
761
|
) -> AgentTaskCreateResult:
|
|
762
|
+
initial_message = _backend_task_message(message)
|
|
748
763
|
context_id = str(message["contextId"])
|
|
749
764
|
session = await client.get_session(context_id)
|
|
750
765
|
if not session.agent_uid:
|
|
@@ -755,14 +770,7 @@ async def _create_backend_task(
|
|
|
755
770
|
"agent_session_uid": context_id,
|
|
756
771
|
"context_id": context_id,
|
|
757
772
|
"task_id": task_id,
|
|
758
|
-
"initial_message":
|
|
759
|
-
"message_id": message["messageId"],
|
|
760
|
-
"role": "user",
|
|
761
|
-
"parts": message["parts"],
|
|
762
|
-
"metadata": message.get("metadata", {}),
|
|
763
|
-
"extensions": message.get("extensions", []),
|
|
764
|
-
"reference_task_ids": message.get("referenceTaskIds", []),
|
|
765
|
-
},
|
|
773
|
+
"initial_message": initial_message,
|
|
766
774
|
"metadata": {
|
|
767
775
|
"transport": "a2a",
|
|
768
776
|
"execution": {
|
|
@@ -1887,18 +1895,9 @@ async def message_send(
|
|
|
1887
1895
|
status_code=400,
|
|
1888
1896
|
detail="A Task continuation requires configuration.responseKind 'task'",
|
|
1889
1897
|
)
|
|
1898
|
+
continued_message = _backend_task_message(message)
|
|
1890
1899
|
existing_task = await client.get_task_by_protocol_id(continuation_task_id)
|
|
1891
|
-
task = await client.continue_task(
|
|
1892
|
-
existing_task.uid,
|
|
1893
|
-
{
|
|
1894
|
-
"message_id": message["messageId"],
|
|
1895
|
-
"role": "user",
|
|
1896
|
-
"parts": message["parts"],
|
|
1897
|
-
"metadata": message.get("metadata", {}),
|
|
1898
|
-
"extensions": message.get("extensions", []),
|
|
1899
|
-
"reference_task_ids": message.get("referenceTaskIds", []),
|
|
1900
|
-
},
|
|
1901
|
-
)
|
|
1900
|
+
task = await client.continue_task(existing_task.uid, continued_message)
|
|
1902
1901
|
await _schedule_task_accelerator(
|
|
1903
1902
|
client,
|
|
1904
1903
|
manager,
|