@zq-silk/yui 2.5.0 → 2.6.0
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.
- package/dist/cli/commandCatalog.js +3 -2
- package/dist/commands/taskCommands.js +8 -3
- package/dist/commands/taskContextCommand.js +3 -2
- package/dist/context/taskContext.js +7 -4
- package/dist/kernel/builtinCapabilities.js +2 -1
- package/dist/runtime/builtinAgentErrorMappers.js +9 -5
- package/dist/runtime/providerRetry.js +38 -0
- package/dist/runtime/taskSessions.js +240 -0
- package/dist/scheduler/taskExecutionProjection.js +78 -6
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app/taskView.js +1 -0
- package/dist/web/assets/client/domain/records.js +70 -2
- package/dist/web/assets/client/domain/runs.js +3 -2
- package/dist/web/assets/client/domain/vocab.js +1 -1
- package/dist/web/assets/client/i18n/en.js +17 -1
- package/dist/web/assets/client/i18n/zh-CN.js +17 -1
- package/dist/web/assets/client/lib/api.js +4 -1
- package/dist/web/assets/client/lib/theme.js +31 -10
- package/dist/web/assets/client/views/task/actions.js +38 -21
- package/dist/web/assets/client/views/task/observation.js +12 -3
- package/dist/web/assets/client/views/task/overview.js +7 -40
- package/dist/web/assets/client/views/task/runtime.js +71 -34
- package/dist/web/assets/shared/icons.js +2 -0
- package/dist/web/assets/shell/index.js +1 -0
- package/dist/web/assets/shell/settings.js +1 -0
- package/dist/web/assets/styles/views/task.js +14 -0
- package/dist/web/webServer.js +12 -0
- package/dist/web/webSessions.js +2 -165
- package/dist/web/webSnapshot.js +9 -5
- package/dist/web/webTaskActivity.js +54 -0
- package/dist/web/webTaskSurface.js +1 -0
- package/docs/observability/README.md +25 -1
- package/docs/observability/README.zh-CN.md +18 -1
- package/docs/provider-retry.md +11 -0
- package/docs/release-workflow.md +27 -0
- package/docs/release-workflow.zh-CN.md +22 -0
- package/package.json +1 -1
- package/skills/yui-runtime/references/recovery.md +9 -0
|
@@ -5,6 +5,7 @@ export const SETTINGS_HTML = `<!doctype html>
|
|
|
5
5
|
<head>
|
|
6
6
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
7
|
<meta name="yui-web-token" content="__YUI_WEB_TOKEN__">
|
|
8
|
+
<script src="/assets/theme.js"></script>
|
|
8
9
|
<link rel="icon" href="data:,"><title>Yui · Settings</title>
|
|
9
10
|
${STYLESHEETS.map(s => `<link rel="stylesheet" href="/assets/css/${s.name}.css">`).join("\n")}
|
|
10
11
|
</head>
|
|
@@ -53,6 +53,20 @@ export const TASK_STYLES = `
|
|
|
53
53
|
.next-line .icon{color:var(--accent)}
|
|
54
54
|
.next-line strong{color:var(--ink)}
|
|
55
55
|
|
|
56
|
+
/* Next step: a compact action bar — prompt left, actions right; the foot
|
|
57
|
+
(condition + receipt) is one quiet line under a hairline. */
|
|
58
|
+
.next-card>.card-body{gap:0;padding:14px 16px 14px 20px}
|
|
59
|
+
.next-row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px 20px}
|
|
60
|
+
.next-lead{display:grid;gap:3px;flex:1 1 280px;min-width:0}
|
|
61
|
+
.next-kicker{color:var(--ink-4);font-size:var(--fs-2xs);font-weight:600;letter-spacing:.08em;text-transform:uppercase}
|
|
62
|
+
.next-prompt{color:var(--ink-2);font-size:var(--fs-sm);line-height:1.5}
|
|
63
|
+
.next-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px;flex:none}
|
|
64
|
+
.next-actions .btn{height:34px;padding:0 14px}
|
|
65
|
+
.next-foot{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:4px 16px;margin-top:12px;padding-top:10px;border-top:1px dashed var(--line)}
|
|
66
|
+
.next-hint{flex:1 1 320px;min-width:0;color:var(--ink-4);font-size:var(--fs-xs);line-height:1.5}
|
|
67
|
+
.next-foot .receipt:empty{display:none}
|
|
68
|
+
@media(max-width:560px){.next-actions{flex:1 1 100%}.next-actions .btn{flex:1 1 0}}
|
|
69
|
+
|
|
56
70
|
/* Needs you / input requests */
|
|
57
71
|
.needs-card{border-color:var(--warn);box-shadow:0 0 0 3px var(--warn-soft)}
|
|
58
72
|
.needs-card .card-icon{background:var(--warn-soft);color:var(--warn)}
|
package/dist/web/webServer.js
CHANGED
|
@@ -5,6 +5,7 @@ import { CliError, usageError } from "../errors/cliError.js";
|
|
|
5
5
|
import { parseTaskCatalogOptions } from "../context/taskCatalog.js";
|
|
6
6
|
import { parseTaskSearchOptions, searchTaskBodies } from "../context/taskSearch.js";
|
|
7
7
|
import { readTaskUsage } from "../runtime/taskUsageQuery.js";
|
|
8
|
+
import { readWebTaskActivity } from "./webTaskActivity.js";
|
|
8
9
|
import { WebRequestRejected } from "./webMutation.js";
|
|
9
10
|
import { SETTINGS_HTML } from "./assets/shell/settings.js";
|
|
10
11
|
import { TASK_SUBMISSION_INTENTS } from "../message/message.js";
|
|
@@ -484,6 +485,7 @@ async function handleHttpRequest(request, response, store, dependencies, token,
|
|
|
484
485
|
throw new WebRequestRejected("Invalid list limit.");
|
|
485
486
|
value = dependencies.surface.list(taskId, {
|
|
486
487
|
store: query.get("store") ?? "",
|
|
488
|
+
...(query.has("view") ? { view: query.get("view") } : {}),
|
|
487
489
|
...(query.has("status") ? { status: query.get("status") } : {}),
|
|
488
490
|
...(query.has("cursor") ? { cursor: query.get("cursor") } : {}),
|
|
489
491
|
...(limit === null ? {} : { limit: Number(limit) })
|
|
@@ -552,6 +554,16 @@ async function handleHttpRequest(request, response, store, dependencies, token,
|
|
|
552
554
|
: buildWebTaskCatalog(store, options);
|
|
553
555
|
sendJson(response, 200, snapshot, method === "HEAD");
|
|
554
556
|
}
|
|
557
|
+
else if (/^\/api\/tasks\/[^/]+\/activity$/u.test(pathname)) {
|
|
558
|
+
const taskId = decodeURIComponent(pathname.split("/")[3]);
|
|
559
|
+
const query = new URL(request.url, "http://localhost").searchParams;
|
|
560
|
+
if ([...query.keys()].some(key => !["limit", "cursor"].includes(key)))
|
|
561
|
+
throw usageError("Invalid activity option.");
|
|
562
|
+
sendJson(response, 200, readWebTaskActivity(store, taskId, {
|
|
563
|
+
...(query.has("limit") ? { limit: Number(query.get("limit")) } : {}),
|
|
564
|
+
...(query.has("cursor") ? { cursor: query.get("cursor") } : {})
|
|
565
|
+
}, now()), method === "HEAD");
|
|
566
|
+
}
|
|
555
567
|
else if (/^\/api\/tasks\/[^/]+\/usage$/u.test(pathname)) {
|
|
556
568
|
const taskId = decodeURIComponent(pathname.split("/")[3]);
|
|
557
569
|
const query = new URL(request.url, "http://localhost").searchParams;
|
package/dist/web/webSessions.js
CHANGED
|
@@ -1,165 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { projectRuntimeTaskEvents, classifyRuntimeHealth } from "../runtime/runtimeProjection.js";
|
|
4
|
-
import { DEFAULT_RUNTIME_HEALTH_POLICY } from "../runtime/runtimeHealthPolicy.js";
|
|
5
|
-
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
6
|
-
/** Read-only presentation of the selected native connections, never an AgentRun
|
|
7
|
-
* count. Foreground facts are fenced to the exact input; retained continuations
|
|
8
|
-
* stay attached to their original input and are not counted as extra Sessions.
|
|
9
|
-
* No live probe, timers, persistence or recovery actions are introduced here. */
|
|
10
|
-
export function projectWebSessions(input) {
|
|
11
|
-
const policy = input.policy ?? DEFAULT_RUNTIME_HEALTH_POLICY;
|
|
12
|
-
const sessions = [];
|
|
13
|
-
const seen = new Map();
|
|
14
|
-
const observations = input.events.map(event => ({ event, observation: runtimeObservationFromTaskEvent(event) }))
|
|
15
|
-
.filter(entry => entry.observation !== null);
|
|
16
|
-
for (const set of input.sessionSets) {
|
|
17
|
-
const session = set.sessions[set.activeAgentId];
|
|
18
|
-
const binding = set.providerBinding;
|
|
19
|
-
const conversation = binding?.conversations.find(c => c.epoch === binding.currentConversationEpoch && c.status === "current");
|
|
20
|
-
const nativeSessionId = session?.nativeSessionId ?? conversation?.conversationId ?? null;
|
|
21
|
-
const identity = JSON.stringify([binding?.providerNamespace ?? session?.adapterId,
|
|
22
|
-
binding?.accountScope ?? set.activeAgentId, nativeSessionId ?? set.owner.roleName]);
|
|
23
|
-
const prior = seen.get(identity);
|
|
24
|
-
if (prior !== undefined) {
|
|
25
|
-
if (sessions[prior].roleName !== set.owner.roleName)
|
|
26
|
-
sessions[prior] = {
|
|
27
|
-
...sessions[prior], group: "unknown", reason: "Multiple Roles name this native Session; inspect ownership."
|
|
28
|
-
};
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
seen.set(identity, sessions.length);
|
|
32
|
-
const turn = binding?.run;
|
|
33
|
-
const row = {
|
|
34
|
-
taskId: input.taskId, roleName: set.owner.roleName, agentId: set.activeAgentId,
|
|
35
|
-
nativeSessionId, attemptId: turn?.attemptId ?? null, nativeTurnId: turn?.nativeTurnId ?? null,
|
|
36
|
-
runId: turn?.runId ?? null, group: "unknown", reason: "Current native input is not observable.",
|
|
37
|
-
layer: null, lastActivityAt: null, semanticProgressAt: input.semanticProgressAt ?? null,
|
|
38
|
-
sourceUpdatedAt: turn?.updatedAt ?? session?.updatedAt ?? set.updatedAt,
|
|
39
|
-
waitingReason: null, operations: [], background: []
|
|
40
|
-
};
|
|
41
|
-
if (!session || !binding || !conversation || conversation.conversationId !== session.nativeSessionId) {
|
|
42
|
-
sessions.push(row);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
let driverId;
|
|
46
|
-
try {
|
|
47
|
-
driverId = builtinDriverIdForAdapter(session.adapterId);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
sessions.push(row);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
const fence = { taskId: input.taskId, roleName: set.owner.roleName,
|
|
54
|
-
agentId: session.agentId, driverId, nativeSessionId: session.nativeSessionId };
|
|
55
|
-
const sessionFacts = observations.filter(({ observation }) => Object.entries(fence).every(([key, value]) => observation.fence[key] === value));
|
|
56
|
-
// Each Run's continuation fold has its own authority. Foreground operations
|
|
57
|
-
// from older inputs must not make the current input look active.
|
|
58
|
-
const groups = new Map();
|
|
59
|
-
for (const { event, observation } of sessionFacts) {
|
|
60
|
-
const runId = observation.fence.runId;
|
|
61
|
-
const group = groups.get(runId) ?? [];
|
|
62
|
-
group.push(event);
|
|
63
|
-
groups.set(runId, group);
|
|
64
|
-
}
|
|
65
|
-
const background = [...groups].flatMap(([runId, events]) => {
|
|
66
|
-
const projection = projectRuntimeTaskEvents({ ...fence, ...(runId ? { runId } : {}) }, session.createdAt, events);
|
|
67
|
-
const children = Object.entries(projection.continuations)
|
|
68
|
-
.filter(([, c]) => c.execution !== "quiescent" || c.identityConflict)
|
|
69
|
-
.map(([id, c]) => ({ id: `${runId ?? "native"}/${id}`, execution: c.execution }));
|
|
70
|
-
return [...children, ...Object.entries(projection.operations)
|
|
71
|
-
.filter(([, operation]) => operation.kind === "subagent")
|
|
72
|
-
.map(([id]) => ({ id: `${runId ?? "native"}/operation:${id}`, execution: "unsettled" }))];
|
|
73
|
-
});
|
|
74
|
-
if (!turn) {
|
|
75
|
-
sessions.push({ ...row, background, ...(background.length ? {
|
|
76
|
-
group: "background", reason: "Retained background execution is not settled."
|
|
77
|
-
} : {}) });
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const exactEvents = sessionFacts.filter(({ observation }) => {
|
|
81
|
-
const observed = observation;
|
|
82
|
-
const f = observed.fence;
|
|
83
|
-
if (f.continuationId !== undefined || f.runId !== turn.runId)
|
|
84
|
-
return false;
|
|
85
|
-
if (f.receiptId === undefined && f.nativeTurnId === undefined
|
|
86
|
-
&& ["host.observed", "session.ended", "session.failed", "observer.health"].includes(observed.kind)) {
|
|
87
|
-
return observed.receivedAt >= turn.submittedAt;
|
|
88
|
-
}
|
|
89
|
-
if (f.receiptId !== undefined && f.receiptId !== turn.attemptId)
|
|
90
|
-
return false;
|
|
91
|
-
if (f.nativeTurnId !== undefined && f.nativeTurnId !== turn.nativeTurnId)
|
|
92
|
-
return false;
|
|
93
|
-
return f.receiptId === turn.attemptId
|
|
94
|
-
|| (turn.nativeTurnId !== undefined && f.nativeTurnId === turn.nativeTurnId);
|
|
95
|
-
}).map(entry => entry.event);
|
|
96
|
-
const projection = projectRuntimeTaskEvents({
|
|
97
|
-
...fence, ...(turn.runId ? { runId: turn.runId } : {}),
|
|
98
|
-
receiptId: turn.attemptId, ...(turn.nativeTurnId ? { nativeTurnId: turn.nativeTurnId } : {})
|
|
99
|
-
}, turn.submittedAt, exactEvents);
|
|
100
|
-
const health = classifyRuntimeHealth({ projection, now: input.now, policy,
|
|
101
|
-
semanticProgressAt: input.semanticProgressAt ?? turn.submittedAt });
|
|
102
|
-
const retry = providerRetryProjection(binding);
|
|
103
|
-
let group;
|
|
104
|
-
let reason = health.reason;
|
|
105
|
-
if (turn.status === "delivery-unknown") {
|
|
106
|
-
group = "unknown";
|
|
107
|
-
reason = "Original input delivery is unknown; do not replay it.";
|
|
108
|
-
}
|
|
109
|
-
else if (session.status === "ended") {
|
|
110
|
-
group = background.length ? "background" : "stopped";
|
|
111
|
-
reason = "The selected Session ended; Task acceptance and retained background execution are separate.";
|
|
112
|
-
}
|
|
113
|
-
else if (retry?.status === "waiting") {
|
|
114
|
-
group = "waiting";
|
|
115
|
-
reason = `Controller-owned Provider retry waiting (${retry.attempts}/${retry.limit}); next eligible ${retry.nextEligibleAt}. Existing work is preserved.`;
|
|
116
|
-
}
|
|
117
|
-
else if (["failed", "cancelled", "rejected"].includes(turn.status)) {
|
|
118
|
-
group = background.length ? "background" : "stopped";
|
|
119
|
-
reason = turn.terminalReason ?? "The selected native execution stopped; Task acceptance is separate.";
|
|
120
|
-
}
|
|
121
|
-
else if (turn.status === "completed") {
|
|
122
|
-
group = background.length ? "background" : "idle";
|
|
123
|
-
reason = background.length ? "Main Turn ended; background execution remains unsettled."
|
|
124
|
-
: "The native Turn ended; this is not Task completion.";
|
|
125
|
-
}
|
|
126
|
-
else if (turn.status !== "accepted") {
|
|
127
|
-
group = "waiting";
|
|
128
|
-
reason = "Waiting for native input acceptance.";
|
|
129
|
-
}
|
|
130
|
-
else if (projection.run === "waiting") {
|
|
131
|
-
group = "waiting";
|
|
132
|
-
}
|
|
133
|
-
else if (["broken", "stopped"].includes(health.layer)) {
|
|
134
|
-
group = "stopped";
|
|
135
|
-
}
|
|
136
|
-
else if (["completed", "failed", "cancelled"].includes(projection.run)) {
|
|
137
|
-
group = "unknown";
|
|
138
|
-
reason = "Native terminal and current input binding disagree; inspect the exact original input.";
|
|
139
|
-
}
|
|
140
|
-
else if (projection.observer.status === "unavailable") {
|
|
141
|
-
group = "unknown";
|
|
142
|
-
}
|
|
143
|
-
else if (health.layer === "diagnostic-needed") {
|
|
144
|
-
group = "diagnostic";
|
|
145
|
-
}
|
|
146
|
-
else if (health.runtimeIdleMs < policy.quietAfterMs && projection.activity.kind !== "provider") {
|
|
147
|
-
group = "active";
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
group = "quiet";
|
|
151
|
-
reason = "Accepted input has no recent structured activity; silence is not a proven deadlock.";
|
|
152
|
-
}
|
|
153
|
-
sessions.push({ ...row, group, reason, layer: health.layer, background,
|
|
154
|
-
lastActivityAt: projection.lastRuntimeActivityAt ?? null,
|
|
155
|
-
waitingReason: projection.waitingReason ?? null, operations: health.activeOperations });
|
|
156
|
-
}
|
|
157
|
-
const counts = {
|
|
158
|
-
active: 0, waiting: 0, quiet: 0, diagnostic: 0, unknown: 0, stopped: 0, idle: 0, background: 0
|
|
159
|
-
};
|
|
160
|
-
for (const session of sessions)
|
|
161
|
-
counts[session.group]++;
|
|
162
|
-
return { scope: "task-selected-native-sessions", taskId: input.taskId,
|
|
163
|
-
readAt: input.now.toISOString(), counts, sessions,
|
|
164
|
-
coverage: "Recorded selected Sessions only; not a live process or historical resource inventory." };
|
|
165
|
-
}
|
|
1
|
+
// CLI and Web share the same read-only native observation fold.
|
|
2
|
+
export { projectTaskSessions as projectWebSessions } from "../runtime/taskSessions.js";
|
package/dist/web/webSnapshot.js
CHANGED
|
@@ -96,14 +96,14 @@ export function buildWebTaskDetail(store, taskId, now = new Date(), compact = fa
|
|
|
96
96
|
&& effectiveLaunch.sourceDesiredRevision !== role.launchRevision
|
|
97
97
|
};
|
|
98
98
|
});
|
|
99
|
-
const execution = buildTaskExecutionProjection(reader, taskId, now);
|
|
100
|
-
if (execution === null)
|
|
101
|
-
return null;
|
|
102
99
|
const brief = reader.getTaskBrief(taskId);
|
|
103
100
|
const roleSessionSets = reader.listRoleSessionSets(taskId);
|
|
104
101
|
const sessions = projectWebSessions({ taskId, sessionSets: roleSessionSets, events, now,
|
|
105
102
|
...(brief?.updatedAt === undefined ? {} : { semanticProgressAt: brief.updatedAt }),
|
|
106
103
|
policy: resolveRuntimeHealth(reader.getConfig().runtimeHealth) });
|
|
104
|
+
const execution = buildTaskExecutionProjection(reader, taskId, now, sessions);
|
|
105
|
+
if (execution === null)
|
|
106
|
+
return null;
|
|
107
107
|
if (compact) {
|
|
108
108
|
// Observation is not semantic progress. No Task/Message/Run bodies,
|
|
109
109
|
// Role history or validation logs ride the default polling response.
|
|
@@ -115,15 +115,19 @@ export function buildWebTaskDetail(store, taskId, now = new Date(), compact = fa
|
|
|
115
115
|
monitoring: execution.monitoring, failClosed: execution.failClosed,
|
|
116
116
|
activeRuns: execution.activeRuns.slice(0, 16).map(run => ({ id: run.id })),
|
|
117
117
|
activeRunCount: execution.activeRuns.length,
|
|
118
|
+
activity: execution.activity,
|
|
118
119
|
attention: execution.attention.slice(0, 8).map(item => ({ ...item, summary: clip(item.summary) })),
|
|
119
120
|
blockers: execution.blockers.slice(0, 8).map(item => ({ ...item, summary: clip(item.summary) }))
|
|
120
121
|
},
|
|
122
|
+
roleCount: roles.length,
|
|
121
123
|
roles: roles.slice(0, 16).map(role => ({
|
|
122
124
|
name: role.name, status: role.status, runtimeSession: role.runtimeSession
|
|
123
125
|
})),
|
|
124
|
-
sessions: { ...sessions,
|
|
126
|
+
sessions: { ...sessions, total: sessions.sessions.length, coverageStatus: "partial",
|
|
127
|
+
sessions: sessions.sessions.slice(0, 16).map(session => ({
|
|
125
128
|
...session, reason: session.reason.slice(0, 1200),
|
|
126
|
-
|
|
129
|
+
operationCount: session.operations.length, backgroundCount: session.background.length,
|
|
130
|
+
operations: [], background: []
|
|
127
131
|
})) },
|
|
128
132
|
observability: {
|
|
129
133
|
cost: execution.observability.cost,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { taskNotFound } from "../errors/cliError.js";
|
|
2
|
+
import { recordPage } from "../output/boundedRead.js";
|
|
3
|
+
import { resolveRuntimeHealth } from "../config/yuiConfig.js";
|
|
4
|
+
import { projectTaskSessions } from "../runtime/taskSessions.js";
|
|
5
|
+
/** Native observations are not durable execution records. Flatten only the
|
|
6
|
+
* unfinished, recorded facts so one bounded page cannot hide a large child
|
|
7
|
+
* collection inside a single Session. Detail remains in the native Session. */
|
|
8
|
+
export function nativeActivityRows(projection) {
|
|
9
|
+
return projection.sessions.flatMap(session => {
|
|
10
|
+
const identity = {
|
|
11
|
+
roleName: session.roleName, agentId: session.agentId,
|
|
12
|
+
driverId: session.driverId,
|
|
13
|
+
nativeSessionId: session.nativeSessionId, runId: session.runId,
|
|
14
|
+
sourceUpdatedAt: session.sourceUpdatedAt
|
|
15
|
+
};
|
|
16
|
+
const key = JSON.stringify([session.roleName, session.agentId, session.driverId, session.nativeSessionId]);
|
|
17
|
+
return [
|
|
18
|
+
...(["idle", "stopped"].includes(session.group) ? [] : [{
|
|
19
|
+
...identity, id: key, kind: "session", status: session.group,
|
|
20
|
+
summary: session.reason.slice(0, 1200), waitingReason: session.waitingReason,
|
|
21
|
+
lastActivityAt: session.lastActivityAt
|
|
22
|
+
}]),
|
|
23
|
+
...session.operations.filter(operation => !operation.startsWith("subagent:")).map(operation => ({
|
|
24
|
+
...identity, id: `${key}/operation/${operation}`, kind: "operation",
|
|
25
|
+
status: "unsettled", summary: operation.slice(0, 1200)
|
|
26
|
+
})),
|
|
27
|
+
...session.background.map(child => ({
|
|
28
|
+
...identity, runId: child.runId, id: `${key}/background/${child.id}`, kind: "background",
|
|
29
|
+
status: child.execution, summary: child.id.slice(0, 1200)
|
|
30
|
+
}))
|
|
31
|
+
];
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function readWebTaskActivity(store, taskId, options, now = new Date()) {
|
|
35
|
+
return store.readTransaction(reader => {
|
|
36
|
+
if (reader.getTask(taskId) === null)
|
|
37
|
+
throw taskNotFound(taskId);
|
|
38
|
+
const projection = projectTaskSessions({
|
|
39
|
+
taskId, sessionSets: reader.listRoleSessionSets(taskId), events: reader.listEvents(taskId), now,
|
|
40
|
+
policy: resolveRuntimeHealth(reader.getConfig().runtimeHealth)
|
|
41
|
+
});
|
|
42
|
+
const rows = nativeActivityRows(projection);
|
|
43
|
+
return {
|
|
44
|
+
...recordPage(rows, `task:${taskId}/native-activity`, options),
|
|
45
|
+
readAt: now.toISOString(), coverage: "partial",
|
|
46
|
+
scope: projection.coverage,
|
|
47
|
+
counts: {
|
|
48
|
+
sessions: rows.filter(row => row.kind === "session").length,
|
|
49
|
+
operations: rows.filter(row => row.kind === "operation").length,
|
|
50
|
+
background: rows.filter(row => row.kind === "background").length
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -349,6 +349,7 @@ export function createWebTaskSurface(store, options = {}, observations = [], hos
|
|
|
349
349
|
// The same paged family discovery as `task context list`: summaries and
|
|
350
350
|
// exact references, never bodies; continuations keep their filters.
|
|
351
351
|
list: (taskId, input) => listTaskContext(store, taskId, input.store, {
|
|
352
|
+
...(input.view === undefined ? {} : { view: input.view }),
|
|
352
353
|
...(input.status === undefined ? {} : { status: input.status }),
|
|
353
354
|
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
354
355
|
...(input.limit === undefined ? {} : { limit: input.limit })
|
|
@@ -108,8 +108,32 @@ All routes use the local Web token and Task boundary.
|
|
|
108
108
|
`GET /api/tasks/<id>/artifacts` lists a fixed repository revision;
|
|
109
109
|
`?path=<relative-path>&commit=<full-commit>` reads its exact text file.
|
|
110
110
|
`GET /api/tasks/<id>/evidence` reads original check/Review/workspace records.
|
|
111
|
-
`GET /api/tasks/<id>/list?store=<family>[&status&cursor&limit]` returns the same
|
|
111
|
+
`GET /api/tasks/<id>/list?store=<family>[&view=current|all&status&cursor&limit]` returns the same
|
|
112
112
|
paged summaries as `task context list`.
|
|
113
|
+
|
|
114
|
+
`task context list <task> --store <family> --view current` uses the same
|
|
115
|
+
membership as the compact Context counts: active runs, pending/running reviews,
|
|
116
|
+
open InputRequests and queued/running/unknown Jobs. The default `all` view
|
|
117
|
+
preserves history. Each continuation binds the filtered collection and rejects
|
|
118
|
+
changed sources; refresh instead of mixing versions. Runtime lists expose
|
|
119
|
+
loaded/total counts, continuation, explicit refresh and read failures. Exact
|
|
120
|
+
record bodies remain on demand.
|
|
121
|
+
|
|
122
|
+
`GET /api/tasks/<id>/activity[?cursor=...&limit=20]` is a token-authenticated,
|
|
123
|
+
read-only page of recorded native Session, operation and background facts.
|
|
124
|
+
It uses the shared runtime projection, including retained, exactly fenced
|
|
125
|
+
children after Session replacement; it never probes processes or Providers.
|
|
126
|
+
Pages contain at most 100 items and 32 KiB, with separate category counts,
|
|
127
|
+
read time and explicit partial coverage. Compact observations carry full
|
|
128
|
+
counts with bounded Session samples, not nested child lists. Native facts and
|
|
129
|
+
AgentRuns can describe the same execution; their counts are not additive.
|
|
130
|
+
Unknown, unloaded and unavailable observations must not mean no activity.
|
|
131
|
+
|
|
132
|
+
The execution summary includes Job and native facts alongside open inputs and
|
|
133
|
+
AgentRuns. `waiting-on-jobs` is a derived display/next-action state, not a Task
|
|
134
|
+
lifecycle value. Waiting for a user keeps parallel work visible; unknown Job
|
|
135
|
+
outcomes or native activity remain attention facts, never completion or replay
|
|
136
|
+
authority.
|
|
113
137
|
These reads do not start a runtime, fetch a remote, write business state, require
|
|
114
138
|
a migration, or introduce a new persistent protocol.
|
|
115
139
|
|
|
@@ -89,8 +89,25 @@ skipped/缺失检查不算通过。工作区所有权不证明可清理;既
|
|
|
89
89
|
`GET /api/tasks/<id>/artifacts` 列出固定仓库版本;
|
|
90
90
|
`?path=<relative-path>&commit=<full-commit>` 读取该版本文本;
|
|
91
91
|
`GET /api/tasks/<id>/evidence` 读取原检查/Review/工作区记录;
|
|
92
|
-
`GET /api/tasks/<id>/list?store=<family>[&status&cursor&limit]` 返回与
|
|
92
|
+
`GET /api/tasks/<id>/list?store=<family>[&view=current|all&status&cursor&limit]` 返回与
|
|
93
93
|
`task context list` 相同的分页摘要。
|
|
94
|
+
|
|
95
|
+
`task context list <task> --store <family> --view current` 与紧凑 Context
|
|
96
|
+
计数使用同一筛选:active Run、pending/running Review、open InputRequest,
|
|
97
|
+
以及 queued/running/unknown Job。默认 `all` 保留历史。分页游标绑定筛选后的
|
|
98
|
+
集合,来源变化时明确拒绝继续拼接;应刷新重读。运行页展示已加载/总数、继续
|
|
99
|
+
加载、刷新及读取失败,记录原文仍按需展开。
|
|
100
|
+
|
|
101
|
+
`GET /api/tasks/<id>/activity[?cursor=...&limit=20]` 是经过 token 鉴权的只读
|
|
102
|
+
原生会话、operation 和后台活动分页。复用共享运行投影,覆盖会话更换后身份
|
|
103
|
+
明确的保留子活动,不探测系统进程或 Provider。每页最多 100 项、32 KiB,
|
|
104
|
+
包含分类计数、读取时间和明确的 partial 覆盖范围。紧凑观测返回完整计数及
|
|
105
|
+
有限的会话样本,不携带嵌套子活动清单。原生事实与 AgentRun 可能指向同一
|
|
106
|
+
执行,计数不可相加;未知、未加载和观测不可用都不能表示没有活动。
|
|
107
|
+
|
|
108
|
+
执行摘要同时考虑 Job、原生事实、用户输入和 AgentRun。`waiting-on-jobs`
|
|
109
|
+
只是派生展示/下一步状态,不改变 Task 生命周期。等待用户不隐藏并行工作;
|
|
110
|
+
未知 Job 结果和原生活动保留为关注事实,不能据此判定完成或授权重放。
|
|
94
111
|
读取不启动运行时、不访问远端、不写业务状态、不引入迁移或新持久协议。
|
|
95
112
|
|
|
96
113
|
## 执行审计
|
package/docs/provider-retry.md
CHANGED
|
@@ -21,6 +21,10 @@ next input. Agents still own semantic recovery and acceptance.
|
|
|
21
21
|
- An explicit manual retry of the same Run retains its recovery lineage,
|
|
22
22
|
automatic count and original deadline. It is not charged as an automatic
|
|
23
23
|
attempt, but a further failure cannot replenish automatic allowance.
|
|
24
|
+
If no automatic chain exists, it can still use the current binding's exact
|
|
25
|
+
accepted failed Turn and original input. The error need not have qualified
|
|
26
|
+
for automation; manual intent does not waive Session, authority, latest-Turn
|
|
27
|
+
or background-quiescence checks.
|
|
24
28
|
|
|
25
29
|
The records live on the existing Provider binding, with indexed pending-record
|
|
26
30
|
reads and the existing Controller deadline timer. There is no retry Task status,
|
|
@@ -56,6 +60,13 @@ failure and earlier chain references. A changed error that still satisfies the
|
|
|
56
60
|
same safe retry rule continues within the original budget. Loss of that rule,
|
|
57
61
|
authority or budget stops automatic submission.
|
|
58
62
|
|
|
63
|
+
Codex's structured `codexErrorInfo: "serverOverloaded"` qualifies as temporary
|
|
64
|
+
capacity failure. Capacity prose alone does not grant replay eligibility.
|
|
65
|
+
Native nonterminal error/retry notifications do not create a Yui retry; only
|
|
66
|
+
the exact failed terminal does. A missing retry record therefore does not
|
|
67
|
+
prove budget exhaustion or an unrecoverable Session: inspect the original
|
|
68
|
+
error and current Turn before choosing explicit recovery.
|
|
69
|
+
|
|
59
70
|
Task errors retain their original `runtime.agent-error` evidence and notify the
|
|
60
71
|
Leader, or the Operator when the affected Role is the Leader. Global failures
|
|
61
72
|
retain record-only Messages in their own Role inbox, including intermediate
|
package/docs/release-workflow.md
CHANGED
|
@@ -51,6 +51,33 @@ merely to avoid a version decision. Package versions remain distinct from protoc
|
|
|
51
51
|
and persistent-storage contracts: their compatibility checks and required migrations
|
|
52
52
|
are unchanged, and the storage rules below do not authorize a package release.
|
|
53
53
|
|
|
54
|
+
### 2.6.0 compatibility and migration notes
|
|
55
|
+
|
|
56
|
+
This minor includes Task111's paged current activity and parallel execution
|
|
57
|
+
summary. Task runtime lists can discover all current Roles, Runs, Reviews,
|
|
58
|
+
Jobs and open InputRequests beyond the compact samples; exact record details
|
|
59
|
+
remain on demand. `task context list --view current` selects current membership,
|
|
60
|
+
while the default `all` retains history. Refresh a changed collection instead
|
|
61
|
+
of reusing its stale pagination cursor.
|
|
62
|
+
|
|
63
|
+
Execution consumers must handle the additional derived `waiting-on-jobs`
|
|
64
|
+
status and `wait-for-jobs` action, and the parallel `activity` categories.
|
|
65
|
+
These are not Task lifecycle states or new scheduling authority. Waiting for
|
|
66
|
+
user input no longer hides concurrent work. Unknown outcomes remain unknown
|
|
67
|
+
and never authorize replay.
|
|
68
|
+
|
|
69
|
+
Compact native observations now return category totals and bounded Session
|
|
70
|
+
samples with `operationCount`/`backgroundCount`, not embedded child lists.
|
|
71
|
+
Consumers needing those rows must page `/api/tasks/<id>/activity`.
|
|
72
|
+
Coverage is explicitly partial: selected native Sessions and retained,
|
|
73
|
+
exactly fenced child observations, not a live process inventory. Native facts
|
|
74
|
+
may describe an AgentRun and must not be added to it as separate executors.
|
|
75
|
+
|
|
76
|
+
No storage migration is added; storage remains **1.6**. Existing acceptance
|
|
77
|
+
evidence uses deterministic tests and isolated browser fixtures, not real-model
|
|
78
|
+
validation. Publishing this package does not upgrade a global CLI, migrate a
|
|
79
|
+
Home or restart a Controller.
|
|
80
|
+
|
|
54
81
|
### 2.5.0 compatibility and migration notes
|
|
55
82
|
|
|
56
83
|
This minor includes Task110's notification and context-loading improvements.
|
|
@@ -36,6 +36,28 @@ Agent 选择一个预先声明的计划,设施从持久状态驱动该计划
|
|
|
36
36
|
不能将不兼容冒称为兼容,也不为回避版本选择新增历史兼容机制。包版本与协议、
|
|
37
37
|
持久存储契约仍然独立:兼容性检查和必要迁移要求不变,下文存储规则不授予发版权限。
|
|
38
38
|
|
|
39
|
+
### 2.6.0 兼容性及迁移说明
|
|
40
|
+
|
|
41
|
+
本 minor 包含 Task111 的当前活动分页与并行执行摘要。任务运行列表可发现超出
|
|
42
|
+
紧凑样本的全部当前 Role、Run、Review、Job 及 open InputRequest,精确记录
|
|
43
|
+
仍按需展开。`task context list --view current` 筛选当前事项,默认 `all`
|
|
44
|
+
保留历史。集合变化后须刷新,不可继续使用旧分页游标拼接结果。
|
|
45
|
+
|
|
46
|
+
执行摘要的消费者须支持新增的派生状态 `waiting-on-jobs`、动作
|
|
47
|
+
`wait-for-jobs` 及并行 `activity` 分类。这些不是 Task 生命周期状态,
|
|
48
|
+
也不增加调度权限。等待用户输入不再隐藏并行工作;未知结果仍为未知,
|
|
49
|
+
不能据此重放执行。
|
|
50
|
+
|
|
51
|
+
紧凑原生观测现在返回分类总数及有界会话样本,使用
|
|
52
|
+
`operationCount`/`backgroundCount`,不再内嵌子活动列表。需要这些记录的
|
|
53
|
+
消费者须分页读取 `/api/tasks/<id>/activity`。覆盖范围明确为 partial:
|
|
54
|
+
选中的原生会话及身份明确的保留子活动观测,不是实时进程清单。原生事实
|
|
55
|
+
可能描述同一个 AgentRun,不可相加为独立执行者数量。
|
|
56
|
+
|
|
57
|
+
没有新增存储迁移,存储版本仍为 **1.6**。既有验收证据来自确定性测试和
|
|
58
|
+
隔离浏览器夹具,不代表真实模型验证。发布此包不升级全局 CLI、不迁移 Home,
|
|
59
|
+
也不重启 Controller。
|
|
60
|
+
|
|
39
61
|
### 2.5.0 兼容性及迁移说明
|
|
40
62
|
|
|
41
63
|
本 minor 包含 Task110 的通知和上下文加载优化。正常与恢复通知共用有界事实、
|
package/package.json
CHANGED
|
@@ -93,6 +93,15 @@ Assignment from memory. A new Host process need not mean a new native Session.
|
|
|
93
93
|
Preserve frozen Review candidates and selected synthesis sources on retry.
|
|
94
94
|
An infrastructure failure is not a defect in the business result.
|
|
95
95
|
|
|
96
|
+
Native internal retries are not failed terminals: observe without submitting
|
|
97
|
+
another input. A missing automatic retry record is not evidence of exhausted
|
|
98
|
+
budget or a damaged Session. An explicit `task run retry <task>/<run>` can
|
|
99
|
+
continue from the current exact failed Turn even without an earlier automatic
|
|
100
|
+
chain. Codex `systemError` alone does not require replacement; the normal
|
|
101
|
+
submission path checks that the latest Turn is that failed Turn and background
|
|
102
|
+
execution is empty. Missing or changed proof is a refusal, not permission to
|
|
103
|
+
replay, change models or silently replace the Session.
|
|
104
|
+
|
|
96
105
|
## Replace a Session deliberately
|
|
97
106
|
|
|
98
107
|
An authorized Leader or Operator may request:
|