@zq-silk/yui 1.0.1 → 1.1.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/README.md +27 -9
- package/dist/cli/commandCatalog.js +7 -5
- package/dist/cli/invocationAuthority.js +4 -0
- package/dist/cli.js +130 -5
- package/dist/commands/releaseCommands.js +33 -24
- package/dist/commands/taskCommands.js +16 -25
- package/dist/commands/taskFactCommands.js +6 -2
- package/dist/controller/clientRuntime.js +7 -2
- package/dist/controller/domainIdentity.js +7 -7
- package/dist/controller/handoverCandidate.js +2 -5
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/resourceCleanupLinux.js +35 -19
- package/dist/controller/resourceInventoryLinux.js +104 -7
- package/dist/controller/runtime.js +4 -13
- package/dist/controller/runtimeLaunchCoordinator.js +2 -4
- package/dist/controller/sessionOwnerReconciliation.js +40 -16
- package/dist/core/controllerClient.js +44 -16
- package/dist/core/controllerEndpoint.js +4 -3
- package/dist/core/controllerProcessIdentity.js +54 -0
- package/dist/core/controllerServer.js +16 -11
- package/dist/core/fileLockOwner.js +53 -12
- package/dist/doctor/doctor.js +230 -12
- package/dist/execution/workItemMainRun.js +1 -1
- package/dist/executor/agentAdapter.js +0 -8
- package/dist/executor/agentExecutor.js +4 -1
- package/dist/executor/fileRoleLaunchPlanner.js +3 -22
- package/dist/message/messageContinuation.js +1 -1
- package/dist/release/releaseHandover.js +77 -25
- package/dist/release/runtimeRelease.js +7 -7
- package/dist/repository/gitWorkspace.js +9 -8
- package/dist/repository/taskWorkspacePreparer.js +15 -57
- package/dist/resources/liveReferences.js +64 -7
- package/dist/runtime/agentEndpointIdentity.js +4 -1
- package/dist/runtime/agentHost.js +8 -9
- package/dist/runtime/codexInteractiveHost.js +1 -1
- package/dist/runtime/codexSharedDaemon.js +211 -0
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/jsonLineChannel.js +6 -1
- package/dist/runtime/localStartup.js +65 -0
- package/dist/runtime/native/darwin-arm64/claude-process-owner +0 -0
- package/dist/runtime/native/darwin-arm64/process-identity +0 -0
- package/dist/runtime/native/darwin-x64/claude-process-owner +0 -0
- package/dist/runtime/native/darwin-x64/process-identity +0 -0
- package/dist/runtime/{claude-process-owner → native/linux-x64/claude-process-owner} +0 -0
- package/dist/runtime/nativeExecutable.js +10 -0
- package/dist/runtime/runtimeCoherence.js +10 -3
- package/dist/runtime/sessionOwnerIdentity.js +19 -0
- package/dist/runtime/sessionTerminationGuard.js +10 -3
- package/dist/runtime/structuredProviderHost.js +51 -26
- package/dist/scheduler/activeRoleRunDelivery.js +2 -11
- package/dist/scheduler/leaderWakeupProcessor.js +1 -2
- package/dist/scheduler/ports.js +1 -8
- package/dist/scheduler/taskExecutionProjection.js +1 -6
- package/dist/storage/sqliteSchema.js +15 -3
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/taskCatalog.js +10 -2
- package/dist/storage/upgrade/taskMainWorkspaceMigration.js +83 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -6
- package/dist/task/task.js +0 -12
- package/dist/tmux/tmuxManager.js +8 -2
- package/dist/tmux/tmuxSocketEndpoint.js +23 -1
- package/dist/web/assets/client/app.js +131 -11
- package/dist/web/assets/client/i18n.js +18 -0
- package/dist/web/assets/client/taskSummary.js +7 -2
- package/dist/web/assets/client/taskSurface.js +54 -5
- package/dist/web/assets/client/view.js +7 -2
- package/dist/web/assets/shell.js +21 -11
- package/dist/web/assets/styles/layout.js +33 -6
- package/dist/web/assets/styles/responsive.js +18 -5
- package/dist/web/assets/styles/tokens.js +1 -1
- package/dist/web/assets/styles/widgets.js +5 -0
- package/docs/provider-runtime.md +7 -0
- package/docs/provider-runtime.zh-CN.md +5 -0
- package/docs/release-workflow.md +7 -1
- package/docs/task-delivery.md +5 -4
- package/docs/testing/verification-levels.md +9 -7
- package/i18n/README.zh-CN.md +19 -6
- package/package.json +5 -6
- package/skills/yui-leader/SKILL.md +8 -0
- package/skills/yui-leader/references/execution.md +4 -1
|
@@ -18,9 +18,10 @@ export function renderTaskSurface(container, data, t, locale, actions) {
|
|
|
18
18
|
const summary = node("div", "section-body");
|
|
19
19
|
summary.append(node("span", "detail-kicker", task.id), node("h2", "detail-title", task.title), pill(t, "status", task.status));
|
|
20
20
|
renderTaskSummary(summary, data, t, locale, actions, recordCard);
|
|
21
|
-
const discuss = node("
|
|
21
|
+
const discuss = node(actions.conversationHost ? "section" : "details",
|
|
22
|
+
actions.conversationHost ? "conversation-compose record-card" : "record-card");
|
|
22
23
|
discuss.dataset.viewKey = "discussion";
|
|
23
|
-
discuss.append(node("summary", "", say("Discuss this Task", "讨论此任务")));
|
|
24
|
+
discuss.append(node(actions.conversationHost ? "h3" : "summary", "", say("Discuss this Task", "讨论此任务")));
|
|
24
25
|
const conversation = node("button", "record-open", say("View Leader Session (read-only)", "查看 Leader Session(只读)"));
|
|
25
26
|
conversation.type = "button";
|
|
26
27
|
// A Draft's planning Session is a real Leader Session, so it is viewable once it
|
|
@@ -96,7 +97,43 @@ export function renderTaskSurface(container, data, t, locale, actions) {
|
|
|
96
97
|
}
|
|
97
98
|
});
|
|
98
99
|
discuss.append(chat);
|
|
99
|
-
|
|
100
|
+
if (actions.conversationHost) {
|
|
101
|
+
const feed = node("div", "conversation-feed");
|
|
102
|
+
feed.setAttribute("aria-label", say("Recorded Task messages", "已记录的 Task 消息"));
|
|
103
|
+
const messages = records("task-message");
|
|
104
|
+
messages.forEach((entry) => {
|
|
105
|
+
if (entry.omitted) {
|
|
106
|
+
feed.append(recordCard(entry));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const value = entry.value;
|
|
110
|
+
const item = node("article", "conversation-item");
|
|
111
|
+
if (value.author?.type === "user") item.classList.add("is-user");
|
|
112
|
+
const head = node("div", "conversation-item-head");
|
|
113
|
+
const author = value.author?.roleName || t("author." + (value.author?.type || "system"));
|
|
114
|
+
head.append(node("strong", "", author));
|
|
115
|
+
if (value.createdAt) {
|
|
116
|
+
const when = node("time", "", new Intl.DateTimeFormat(locale, {
|
|
117
|
+
month: "short", day: "numeric", hour: "2-digit", minute: "2-digit"
|
|
118
|
+
}).format(new Date(value.createdAt)));
|
|
119
|
+
when.dateTime = value.createdAt;
|
|
120
|
+
head.append(when);
|
|
121
|
+
}
|
|
122
|
+
item.append(head, richText(null, value.body || "", t));
|
|
123
|
+
feed.append(item);
|
|
124
|
+
});
|
|
125
|
+
if (!messages.length) feed.append(node("p", "muted", core.omitted.records
|
|
126
|
+
? say("Compact read omits some messages. Open History & messages for source references.",
|
|
127
|
+
"紧凑读取省略了部分消息;可在「历史与消息」查看来源引用。")
|
|
128
|
+
: say("No recorded Task messages yet.", "尚无已记录的 Task 消息。")));
|
|
129
|
+
if (core.omitted.records && messages.length) feed.append(node("p", "muted",
|
|
130
|
+
say("Compact read may omit older messages; see History & messages for source references.",
|
|
131
|
+
"紧凑读取可能省略较早消息;可在「历史与消息」查看来源引用。")));
|
|
132
|
+
actions.conversationHost.replaceChildren(feed, discuss);
|
|
133
|
+
actions.conversationHost.dataset.taskId = task.id;
|
|
134
|
+
} else {
|
|
135
|
+
summary.append(discuss);
|
|
136
|
+
}
|
|
100
137
|
// decision-3 three-action input control (queue / steer / interrupt). This is
|
|
101
138
|
// the same application-layer path the CLI drives; the Web surface adds no
|
|
102
139
|
// fourth action and no auto-fallback. Steer/interrupt name an exact current
|
|
@@ -378,10 +415,14 @@ export function renderTaskSurface(container, data, t, locale, actions) {
|
|
|
378
415
|
scaffold.append(anchorSection(id, null, folded));
|
|
379
416
|
return body;
|
|
380
417
|
}
|
|
381
|
-
const
|
|
418
|
+
const working = task.status === "active" || task.status === "draft";
|
|
419
|
+
const focus = section("detail-focus", working
|
|
420
|
+
? say("Goal and current focus", "目标与当前关注")
|
|
421
|
+
: say("Goal and last working focus", "目标与结束前的工作重点"), records("task-brief"));
|
|
382
422
|
const brief = values("task-brief")[0];
|
|
383
423
|
if (brief) {
|
|
384
|
-
focus.prepend(richText(
|
|
424
|
+
focus.prepend(richText(working
|
|
425
|
+
? t("detail.focus") : say("Last working focus", "结束前的工作重点"), brief.currentFocus, t));
|
|
385
426
|
if (brief.boundaries.length) focus.append(richText(say("Boundaries", "边界"), brief.boundaries.join("\\n"), t));
|
|
386
427
|
}
|
|
387
428
|
section("detail-work", say("Responsibilities and acceptance", "工作责任与验收"), records("work-item"));
|
|
@@ -430,6 +471,14 @@ export function renderTaskSurface(container, data, t, locale, actions) {
|
|
|
430
471
|
execution.append(raw);
|
|
431
472
|
scaffold.append(anchorSection("detail-exec", sectionHead(t("detail.execution")), execution));
|
|
432
473
|
section("detail-operations", say("Original operation facts (no automatic retry)", "原始操作事实(不自动重试)"), records("job"));
|
|
474
|
+
// Four reading groups; the source sections keep their stable deep-link IDs.
|
|
475
|
+
const readingOrder = ["detail-top", "detail-focus", "detail-work", "detail-results",
|
|
476
|
+
"detail-exec", "detail-reviews", "detail-roles", "detail-history",
|
|
477
|
+
"detail-messages", "detail-operations"];
|
|
478
|
+
readingOrder.forEach((id) => {
|
|
479
|
+
const target = scaffold.querySelector("#" + id);
|
|
480
|
+
if (target) scaffold.append(target);
|
|
481
|
+
});
|
|
433
482
|
const refs = node("details", "record-card");
|
|
434
483
|
refs.append(node("summary", "", say("Context cursor and omitted references", "Context 游标与省略引用")));
|
|
435
484
|
refs.append(node("pre", "surface-json", JSON.stringify({
|
|
@@ -478,11 +478,16 @@ export function renderTaskDetail(detail, data, t, locale, actions) {
|
|
|
478
478
|
const focusBody = node("div", "section-body");
|
|
479
479
|
if (data.brief) {
|
|
480
480
|
const focusCard = node("article", "record-card");
|
|
481
|
-
|
|
481
|
+
const working = task.status === "active" || task.status === "draft";
|
|
482
|
+
focusCard.append(richText(working
|
|
483
|
+
? t("detail.focus") : (locale.startsWith("zh") ? "结束前的工作重点" : "Last working focus"),
|
|
484
|
+
data.brief.currentFocus || data.brief.objective, t));
|
|
482
485
|
if (data.brief.technicalApproach) {
|
|
483
486
|
focusCard.append(richText(t("detail.technicalApproach"), data.brief.technicalApproach, t, { muted: true }));
|
|
484
487
|
}
|
|
485
|
-
if (data.brief.leaderSummary) focusCard.append(richText(
|
|
488
|
+
if (data.brief.leaderSummary) focusCard.append(richText(working
|
|
489
|
+
? t("detail.leaderSummary") : (locale.startsWith("zh") ? "结束前的进展摘要" : "Last working report"),
|
|
490
|
+
data.brief.leaderSummary, t, { muted: true }));
|
|
486
491
|
focusBody.append(focusCard);
|
|
487
492
|
} else {
|
|
488
493
|
focusBody.append(emptyRow(t, "empty.brief"));
|
package/dist/web/assets/shell.js
CHANGED
|
@@ -75,30 +75,40 @@ export const DASHBOARD_HTML = `<!doctype html>
|
|
|
75
75
|
</div>
|
|
76
76
|
</div>
|
|
77
77
|
<div class="topbar-actions">
|
|
78
|
+
<button id="conversation-toggle" class="operator-open" type="button" aria-controls="conversation-panel" aria-expanded="false" data-i18n="conversation.show" hidden>Task discussion</button>
|
|
78
79
|
<div class="clock">
|
|
79
80
|
<span data-i18n="sync.label">Last sync</span>
|
|
80
81
|
<time id="last-sync">—</time>
|
|
81
82
|
</div>
|
|
82
|
-
<button id="operator-terminal" class="operator-open" type="button">
|
|
83
|
+
<button id="operator-terminal" class="operator-open" type="button" aria-label="Operator session" data-i18n-aria-label="actions.operator">
|
|
83
84
|
<span class="operator-title" data-i18n="actions.operator">Operator session</span>
|
|
84
85
|
<span class="operator-shortcuts" aria-hidden="true"><kbd>O</kbd></span>
|
|
85
86
|
</button>
|
|
86
|
-
<button id="refresh" class="refresh" type="button" title="Refresh tasks"><span data-i18n="actions.refresh">Refresh</span> <kbd>R</kbd></button>
|
|
87
|
+
<button id="refresh" class="refresh" type="button" title="Refresh tasks" aria-label="Refresh" data-i18n-aria-label="actions.refresh"><span data-i18n="actions.refresh">Refresh</span> <kbd>R</kbd></button>
|
|
87
88
|
</div>
|
|
88
89
|
</header>
|
|
89
90
|
<nav id="detail-tabs" class="detail-tabs" aria-label="Task sections" data-i18n-aria-label="tabs.label" hidden>
|
|
90
|
-
<button class="tab" type="button" data-target="detail-top" data-i18n="tabs.summary">
|
|
91
|
-
<button class="tab" type="button" data-target="detail-
|
|
92
|
-
<button class="tab" type="button" data-target="detail-
|
|
93
|
-
<button class="tab" type="button" data-target="detail-
|
|
94
|
-
<button class="tab" type="button" data-target="detail-exec" data-i18n="tabs.exec">AgentRuns</button>
|
|
95
|
-
<button class="tab" type="button" data-target="detail-reviews" data-i18n="tabs.reviews">Reviews</button>
|
|
96
|
-
<button class="tab" type="button" data-target="detail-roles" data-i18n="tabs.roles">Roles</button>
|
|
97
|
-
<button class="tab" type="button" data-target="detail-history" data-i18n="tabs.history">History</button>
|
|
98
|
-
<button class="tab" type="button" data-target="detail-messages" data-i18n="tabs.messages">Messages</button>
|
|
91
|
+
<button class="tab" type="button" data-target="detail-top" data-i18n="tabs.summary">Overview</button>
|
|
92
|
+
<button class="tab" type="button" data-target="detail-work" data-i18n="tabs.workResults">Work & results</button>
|
|
93
|
+
<button class="tab" type="button" data-target="detail-exec" data-i18n="tabs.executionReviews">Execution & reviews</button>
|
|
94
|
+
<button class="tab" type="button" data-target="detail-history" data-i18n="tabs.historyMessages">History & messages</button>
|
|
99
95
|
</nav>
|
|
100
96
|
<main id="detail" class="detail" aria-labelledby="page-title" tabindex="-1"></main>
|
|
101
97
|
</div>
|
|
98
|
+
<div id="pane-divider" class="pane-divider" role="separator" tabindex="0" aria-label="Resize task discussion" aria-orientation="vertical" aria-valuemin="280" aria-valuemax="640" aria-valuenow="400" data-i18n-aria-label="conversation.resize" hidden><span aria-hidden="true"></span></div>
|
|
99
|
+
<aside id="conversation-panel" class="conversation-panel" aria-labelledby="conversation-title" hidden>
|
|
100
|
+
<header class="conversation-head">
|
|
101
|
+
<div>
|
|
102
|
+
<span class="conversation-eyebrow" data-i18n="conversation.source">Recorded Task messages · not a Session transcript</span>
|
|
103
|
+
<h2 id="conversation-title" data-i18n="conversation.title">Task discussion</h2>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="conversation-head-actions">
|
|
106
|
+
<button id="conversation-swap" class="pane-action" type="button" title="Move discussion to the other side" data-i18n-aria-label="conversation.swap" aria-label="Move discussion to the other side">⇄</button>
|
|
107
|
+
<button id="conversation-close" class="pane-action" type="button" title="Hide discussion" data-i18n-aria-label="conversation.hide" aria-label="Hide discussion">×</button>
|
|
108
|
+
</div>
|
|
109
|
+
</header>
|
|
110
|
+
<div id="conversation-content" class="conversation-content task-surface"></div>
|
|
111
|
+
</aside>
|
|
102
112
|
<dialog id="global-input-dialog" class="global-input-dialog" aria-labelledby="global-input-title">
|
|
103
113
|
<h2 id="global-input-title">Global Role input · 全局 Role 消息</h2>
|
|
104
114
|
<p class="muted">Queue is the default. A cancel request does not prove the Turn stopped.<br>默认排队;取消请求不代表执行已停止。</p>
|
|
@@ -6,10 +6,13 @@ export const LAYOUT_STYLES = `
|
|
|
6
6
|
*{box-sizing:border-box}
|
|
7
7
|
html{min-width:320px;font-family:var(--font-body)}
|
|
8
8
|
body{margin:0;min-width:320px;height:100vh;overflow:hidden;font-size:14px}
|
|
9
|
-
.app-shell{display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr) 0;grid-template-rows:100vh;height:100vh;transition:grid-template-columns var(--motion-slow) var(--ease)}
|
|
10
|
-
body.
|
|
9
|
+
.app-shell{display:grid;grid-template-areas:"sidebar main divider pane";grid-template-columns:var(--sidebar-w) minmax(0,1fr) 0 0;grid-template-rows:100vh;height:100vh;transition:grid-template-columns var(--motion-slow) var(--ease)}
|
|
10
|
+
body.conversation-active .app-shell{grid-template-columns:var(--sidebar-w) minmax(0,1fr) 8px var(--pane-w)}
|
|
11
|
+
body.conversation-active.pane-left .app-shell{grid-template-areas:"sidebar pane divider main";grid-template-columns:var(--sidebar-w) var(--pane-w) 8px minmax(0,1fr)}
|
|
12
|
+
body.terminal-active .app-shell,body.terminal-active.pane-left .app-shell{grid-template-areas:"sidebar main divider pane";grid-template-columns:var(--sidebar-w) minmax(0,1fr) 0 var(--terminal-w)}
|
|
13
|
+
body.pane-resizing .app-shell{transition:none}
|
|
11
14
|
/* Sidebar = persistent work index */
|
|
12
|
-
.sidebar{display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;padding:16px 14px 14px}
|
|
15
|
+
.sidebar{grid-area:sidebar;display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;padding:16px 14px 14px}
|
|
13
16
|
.sidebar-brand{display:flex;gap:10px;align-items:center;flex:none;margin-bottom:14px}
|
|
14
17
|
.brand-text{display:grid;gap:3px;min-width:0;flex:1}
|
|
15
18
|
.sidebar .search{flex:none;margin-bottom:10px}
|
|
@@ -27,7 +30,7 @@ body.terminal-active .app-shell{grid-template-columns:var(--sidebar-w) minmax(0,
|
|
|
27
30
|
.catalog-pager button:disabled{opacity:.45;cursor:default}
|
|
28
31
|
#catalog-attention-reset{grid-column:1/-1}
|
|
29
32
|
/* Main column = top bar + optional section nav + reading surface */
|
|
30
|
-
.main-col{display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;overflow-y:auto;scrollbar-gutter:stable}
|
|
33
|
+
.main-col{grid-area:main;display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;overflow-y:auto;scrollbar-gutter:stable}
|
|
31
34
|
.topbar{position:sticky;top:0;z-index:30;flex:none;display:flex;align-items:center;justify-content:space-between;gap:18px;padding:11px max(var(--page-space),calc((100% - 1100px) / 2));background:color-mix(in srgb,var(--bg) 92%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-bottom:1px solid var(--border)}
|
|
32
35
|
.topbar-leading{display:flex;align-items:center;gap:14px;min-width:0}
|
|
33
36
|
.breadcrumb{display:flex;align-items:baseline;gap:8px;min-width:0}
|
|
@@ -43,8 +46,32 @@ body.detail-active .detail-tabs{display:flex}
|
|
|
43
46
|
.detail{flex:1;min-height:0;padding:16px max(var(--page-space),calc((100% - 1100px) / 2)) 72px;min-width:0}
|
|
44
47
|
.detail:focus-visible{outline:none}
|
|
45
48
|
.anchor{scroll-margin-top:calc(var(--topbar-h,44px) + var(--tabs-h,38px))}
|
|
46
|
-
/*
|
|
47
|
-
.
|
|
49
|
+
/* Adjustable discussion panel; the separator belongs to the task view, not Task state. */
|
|
50
|
+
.pane-divider{grid-area:divider;align-self:stretch;width:8px;min-width:0;cursor:col-resize;position:relative;touch-action:none;z-index:31}
|
|
51
|
+
.pane-divider[hidden]{display:none}
|
|
52
|
+
.pane-divider span{position:absolute;inset:0 3px;background:var(--border-strong);transition:background var(--motion-fast)}
|
|
53
|
+
.pane-divider:hover span,.pane-divider:focus-visible span,body.pane-resizing .pane-divider span{background:var(--accent)}
|
|
54
|
+
.pane-divider:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
|
|
55
|
+
.conversation-panel{grid-area:pane;min-width:0;height:100vh;min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
56
|
+
.conversation-panel[hidden]{display:none}
|
|
57
|
+
.conversation-head{flex:none;min-height:72px;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:13px 16px}
|
|
58
|
+
.conversation-head>div:first-child{min-width:0}
|
|
59
|
+
.conversation-eyebrow{display:block;font-size:10px;line-height:1.4}
|
|
60
|
+
.conversation-head h2{margin:4px 0 0;font-size:16px;font-family:var(--font-display);line-height:1.25}
|
|
61
|
+
.conversation-head-actions{display:flex;gap:4px;align-items:center}
|
|
62
|
+
.conversation-content{display:flex;flex:1;flex-direction:column;min-height:0;padding:14px;gap:14px}
|
|
63
|
+
.conversation-feed{display:flex;flex:1;flex-direction:column;gap:10px;min-height:0;overflow-y:auto;scrollbar-gutter:stable;padding:2px 2px 12px}
|
|
64
|
+
.conversation-item{flex:none;padding:11px 12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-2);min-width:0;overflow-wrap:anywhere}
|
|
65
|
+
.conversation-item.is-user{border-color:var(--accent-line);background:var(--accent-soft)}
|
|
66
|
+
.conversation-item-head{display:flex;justify-content:space-between;gap:10px;color:var(--muted);font-size:11px;margin-bottom:8px}
|
|
67
|
+
.conversation-item-head time{white-space:nowrap}
|
|
68
|
+
.conversation-item .rich-text{font-size:13px}
|
|
69
|
+
.conversation-compose{flex:none;max-height:55%;overflow-y:auto}
|
|
70
|
+
.conversation-compose h3{margin:2px 0;font-size:13px}
|
|
71
|
+
.conversation-compose form{display:grid;gap:9px}
|
|
72
|
+
.conversation-compose textarea{min-height:76px}
|
|
73
|
+
/* Native Session occupies the pane temporarily; it is not the Task message feed. */
|
|
74
|
+
.terminal-panel{grid-area:pane;min-width:0;height:100vh;display:flex;flex-direction:column;overflow:hidden}
|
|
48
75
|
.terminal-panel[hidden]{display:none}
|
|
49
76
|
.terminal-head{flex:none;height:62px;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:10px 14px}
|
|
50
77
|
.terminal-head>div{display:flex;align-items:center;gap:12px;min-width:0}
|
|
@@ -11,8 +11,7 @@ export const RESPONSIVE_STYLES = `
|
|
|
11
11
|
@media(max-width:900px){
|
|
12
12
|
/* Master-detail: sidebar list by default; selecting a task swaps to detail */
|
|
13
13
|
body{overflow:auto}
|
|
14
|
-
.app-shell{grid-template-columns:1fr;height:auto;min-height:100vh}
|
|
15
|
-
body.terminal-active .app-shell{grid-template-columns:1fr}
|
|
14
|
+
.app-shell,body.conversation-active .app-shell,body.conversation-active.pane-left .app-shell,body.terminal-active .app-shell,body.terminal-active.pane-left .app-shell{grid-template-areas:"sidebar" "main";grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto;height:auto;min-height:100vh}
|
|
16
15
|
.sidebar{height:auto;min-height:100vh;border-right:0;border-bottom:1px solid var(--border)}
|
|
17
16
|
.task-list{overflow:visible}
|
|
18
17
|
.main-col{display:none;height:auto;min-height:100vh}
|
|
@@ -20,10 +19,16 @@ export const RESPONSIVE_STYLES = `
|
|
|
20
19
|
body.detail-active .main-col{display:flex}
|
|
21
20
|
body.detail-active .detail-back{display:grid}
|
|
22
21
|
.detail{overflow:visible}
|
|
22
|
+
.pane-divider{display:none}
|
|
23
|
+
.conversation-panel{position:fixed;inset:0;z-index:70;height:100dvh;border-left:0}
|
|
24
|
+
.conversation-feed{min-height:0}
|
|
25
|
+
.conversation-head-actions #conversation-swap{display:none}
|
|
26
|
+
body.conversation-active .main-col{display:none}
|
|
27
|
+
body.conversation-active .conversation-panel{display:flex}
|
|
23
28
|
.overview-duo{grid-template-columns:1fr}
|
|
24
29
|
.observability-metrics{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
25
30
|
/* Terminal panel overlays full-screen instead of occupying a grid column */
|
|
26
|
-
.terminal-panel{position:fixed;inset:0;z-index:
|
|
31
|
+
.terminal-panel{position:fixed;inset:0;z-index:80;height:100dvh;border-left:0}
|
|
27
32
|
}
|
|
28
33
|
@media(max-width:620px){
|
|
29
34
|
.command-rail{grid-template-columns:repeat(2,1fr)}
|
|
@@ -31,8 +36,16 @@ export const RESPONSIVE_STYLES = `
|
|
|
31
36
|
.metric-value{font-size:26px}
|
|
32
37
|
/* Keep the topbar on one line: clock and key hints are expendable */
|
|
33
38
|
.clock{display:none}
|
|
34
|
-
.
|
|
35
|
-
.topbar{flex
|
|
39
|
+
.topbar{flex-wrap:nowrap;gap:8px;padding:10px 14px}
|
|
40
|
+
.topbar-leading{flex:1;gap:8px}
|
|
41
|
+
.breadcrumb .crumb,.breadcrumb .crumb-sep{display:none}
|
|
42
|
+
.topbar-actions{gap:6px}
|
|
43
|
+
#conversation-toggle,#operator-terminal,#refresh{width:38px;height:38px;min-width:38px;justify-content:center;padding:0}
|
|
44
|
+
#conversation-toggle{font-size:0}
|
|
45
|
+
#conversation-toggle::before{content:"▤";font-size:19px}
|
|
46
|
+
#operator-terminal .operator-title,#operator-terminal .operator-shortcuts,#refresh span,#refresh kbd{display:none}
|
|
47
|
+
#operator-terminal::before{content:">_";font-family:var(--font-mono);font-size:15px}
|
|
48
|
+
#refresh::before{content:"↻";font-size:20px}
|
|
36
49
|
.detail-tabs{mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent)}
|
|
37
50
|
.record-cols{grid-template-columns:1fr}
|
|
38
51
|
.observability-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}
|
|
@@ -35,7 +35,7 @@ export const TOKEN_STYLES = `
|
|
|
35
35
|
--font-mono:"JetBrains Mono","SFMono-Regular",Consolas,monospace;
|
|
36
36
|
--font-display:"Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
|
|
37
37
|
--font-body:"Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
|
|
38
|
-
--page-space:clamp(18px,2.6vw,40px);--sidebar-w:clamp(232px,18vw,288px);--terminal-w:clamp(420px,34vw,640px);
|
|
38
|
+
--page-space:clamp(18px,2.6vw,40px);--sidebar-w:clamp(232px,18vw,288px);--terminal-w:clamp(420px,34vw,640px);--pane-w:clamp(300px,29vw,500px);
|
|
39
39
|
--motion-fast:150ms;--motion-slow:320ms;--ease:cubic-bezier(.16,.84,.44,1);
|
|
40
40
|
}
|
|
41
41
|
:root,[data-theme="control-room"]{
|
|
@@ -142,6 +142,11 @@ kbd{background:var(--bg-3);border:1px solid var(--border);border-bottom-width:2p
|
|
|
142
142
|
.metric.is-warning .metric-value{color:var(--warning)}
|
|
143
143
|
/* Terminal panel */
|
|
144
144
|
.terminal-panel{color:#e8eef6;background:#080b11;border-left:1px solid #263244;box-shadow:var(--shadow-pop)}
|
|
145
|
+
.conversation-panel{color:var(--text);background:var(--bg-1);border-left:1px solid var(--border);box-shadow:var(--shadow-card)}
|
|
146
|
+
.conversation-head{border-bottom:1px solid var(--border);background:var(--bg-2)}
|
|
147
|
+
.conversation-eyebrow{color:var(--muted)}
|
|
148
|
+
.pane-action{display:grid;place-items:center;width:34px;height:34px;padding:0;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-1);color:var(--text);font:inherit;font-size:20px;cursor:pointer}
|
|
149
|
+
.pane-action:hover,.pane-action:focus-visible{border-color:var(--accent);color:var(--accent)}
|
|
145
150
|
.terminal-head{background:#0c111a;border-bottom:1px solid #263244}
|
|
146
151
|
.terminal-head h2{font-family:var(--font-display);font-size:15px;color:#e8eef6}
|
|
147
152
|
.terminal-close{display:grid!important;color:#8b99ad;background:#111826;border-color:#263244}
|
package/docs/provider-runtime.md
CHANGED
|
@@ -143,6 +143,13 @@ Failure to drain remains a live ownership dependency, not false quiescence.
|
|
|
143
143
|
This is OS process custody, not a Task workflow or a claim about unrelated
|
|
144
144
|
external services.
|
|
145
145
|
|
|
146
|
+
macOS has no child subreaper. The owner stops descendants it can still prove
|
|
147
|
+
through ancestry or the original process group. If the Claude root forks,
|
|
148
|
+
`setsid` and reparenting can hide a child from both checks; the owner therefore
|
|
149
|
+
reports cleanup as unconfirmed instead of returning a successful exit. This
|
|
150
|
+
also covers ordinary forked tools whose exit cannot be proven after the root
|
|
151
|
+
is gone. A detached child may still need explicit inspection and termination.
|
|
152
|
+
|
|
146
153
|
The dedicated process's PID/start identity is retained as engineering control
|
|
147
154
|
data independently of AgentHost. Recovery never needs the old Host's in-memory
|
|
148
155
|
connection. For Codex, a separate metadata/control client can inspect and stop
|
|
@@ -117,6 +117,11 @@ Task Session 终止首先取消其确切的未结算输入,并在向 Host 发
|
|
|
117
117
|
一个活的所有权依赖,而不是虚假静止。这是 OS 进程托管,不是 Task 工作流,也不是关于
|
|
118
118
|
无关外部服务的断言。
|
|
119
119
|
|
|
120
|
+
macOS 没有子进程 subreaper。owner 会停止仍能通过父子关系或原进程组确认的后代。
|
|
121
|
+
若 Claude 根进程曾 fork,子进程再执行 `setsid` 并被重新托管,就可能避开这两种检查;
|
|
122
|
+
此时 owner 会报告清理未确认,而不会返回成功;普通工具进程曾 fork、但根进程退出后
|
|
123
|
+
无法证明它们都已结束时,也适用这一保守结果。脱离的子进程可能仍需单独检查和终止。
|
|
124
|
+
|
|
120
125
|
专用进程的 PID/start 身份作为工程控制数据独立于 AgentHost 保留。恢复从不需要旧 Host
|
|
121
126
|
的内存连接。对 Codex,一个单独的元数据/控制客户端可以在不提交另一个 prompt 的情况下
|
|
122
127
|
检查并停止已记录的原生执行;未知接受可以在实际原生静止之后被放弃,而不编造原生 Turn
|
package/docs/release-workflow.md
CHANGED
|
@@ -45,10 +45,16 @@ protection and valid exact-identity caches remain normal runtime behavior.
|
|
|
45
45
|
|
|
46
46
|
Stable releases use npm `latest`; prereleases use `next`, never the stable
|
|
47
47
|
default. The release workflow builds one runtime archive, verifies it across
|
|
48
|
-
supported Node versions, publishes that exact archive to npm, then creates the
|
|
48
|
+
supported Node versions on Linux x64, macOS x64 and macOS arm64, publishes that exact archive to npm, then creates the
|
|
49
49
|
matching GitHub Release. Every release carries the runtime archive, checksum and
|
|
50
50
|
provenance. A stable tag becomes GitHub latest; a prerelease does not.
|
|
51
51
|
|
|
52
|
+
Platform jobs compile Yui's native helpers from the same source commit.
|
|
53
|
+
The archive includes every supported helper under `dist/runtime/native/`;
|
|
54
|
+
runtime selection uses the exact OS and architecture, without install scripts
|
|
55
|
+
or downloading executable code on first use. Dependencies such as SQLite
|
|
56
|
+
remain ordinary npm dependencies installed for the consumer's platform.
|
|
57
|
+
|
|
52
58
|
The GitHub Release job uploads only missing assets to a draft, downloads and
|
|
53
59
|
compares every asset against the tested artifact, and never overwrites a
|
|
54
60
|
published asset. A retry accepts only byte-identical existing evidence. If npm
|
package/docs/task-delivery.md
CHANGED
|
@@ -35,9 +35,10 @@ isolation and acceptance checks remain enforced independently.
|
|
|
35
35
|
|
|
36
36
|
Stable Project checkouts are read-only references. Task main is a logical
|
|
37
37
|
multi-Project root with independent per-Project Git clones; WorkItem, Review and
|
|
38
|
-
Integration worktrees belong to those Task repositories.
|
|
39
|
-
|
|
40
|
-
additional-directory mechanism
|
|
38
|
+
Integration worktrees belong to those Task repositories. By default, a Task's native cwd is
|
|
39
|
+
its managed root with zero, one, or multiple Projects. The native
|
|
40
|
+
additional-directory mechanism exposes each bound Project. Binding another
|
|
41
|
+
Project while the Task is active does not move the Task cwd.
|
|
41
42
|
|
|
42
43
|
An isolated WorkItem has independent worktrees for writable Projects and
|
|
43
44
|
Task-main context for the others. Write scope is explicit and can only be
|
|
@@ -367,7 +368,7 @@ This adds neither a retry worker nor another persistent ownership protocol.
|
|
|
367
368
|
Current Resource records are read strictly: required safety fields, enum values,
|
|
368
369
|
and every active reference must be valid. A malformed record or mismatched
|
|
369
370
|
SQLite/payload identity is reported without supplying defaults, dropping refs,
|
|
370
|
-
or rewriting stored evidence. This enforces the current storage 1.
|
|
371
|
+
or rewriting stored evidence. This enforces the current storage 1.1 record contract.
|
|
371
372
|
|
|
372
373
|
Failed preparation compensates only its unadopted resources. Standalone Task
|
|
373
374
|
clones use exact clone identity/cleanliness checks before direct deletion;
|
|
@@ -50,13 +50,12 @@ is required.
|
|
|
50
50
|
## Permanent core smoke
|
|
51
51
|
|
|
52
52
|
`npm test` and `npm run test:core` build the checkout and run the maintained suite.
|
|
53
|
-
The stable suite owns
|
|
54
|
-
|
|
55
|
-
compatibility test.
|
|
53
|
+
The stable suite owns current storage 1.1 and its declared 1.0 minor
|
|
54
|
+
transition. It contains no undeclared historical format compatibility path.
|
|
56
55
|
|
|
57
56
|
Current coverage includes:
|
|
58
57
|
|
|
59
|
-
1. Fresh storage 1.
|
|
58
|
+
1. Fresh storage 1.1, exact schema/record validation, no initialization over
|
|
60
59
|
unknown data, and rejection of old integer formats without mutation.
|
|
61
60
|
2. Exact-version staging, mismatched-target refusal, same-major contiguous
|
|
62
61
|
minor preflight, explicit maintenance-owner identity across handover, and
|
|
@@ -99,8 +98,9 @@ do not add prose-matching tests or claim model validation from static checks.
|
|
|
99
98
|
|
|
100
99
|
## CI and release
|
|
101
100
|
|
|
102
|
-
`ci.yml`
|
|
103
|
-
on every PR, without another
|
|
101
|
+
`ci.yml` runs core plus one assembled-package normal-path smoke per supported
|
|
102
|
+
platform (Linux x64, Mac Intel and Apple Silicon) on every PR, without another
|
|
103
|
+
lint or broad regression suite.
|
|
104
104
|
`node scripts/smoke-runtime-package.mjs --assembled .release-stage` exercises
|
|
105
105
|
the actual CLI/Controller/Host/SQLite and isolated tmux, replacing only the
|
|
106
106
|
external Provider with a deterministic fixture. It covers setup, durable input
|
|
@@ -111,7 +111,9 @@ and grouped viewers without affecting a similarly named neighboring session.
|
|
|
111
111
|
The fixture owns a fresh Home and its PATH, installs cleanup before setup,
|
|
112
112
|
and never calls an installed model Agent.
|
|
113
113
|
|
|
114
|
-
`publish.yml`
|
|
114
|
+
`publish.yml` builds one archive containing all three native targets and runs
|
|
115
|
+
the same smoke on each platform and supported Node version against that exact
|
|
116
|
+
freshly installed package through
|
|
115
117
|
`YUI_INSTALLED_ROOT`, adding actual npm-bin, dependency, supported Node version,
|
|
116
118
|
artifact and provenance boundaries. This validates runtime integration, not
|
|
117
119
|
real-model behavior. Pure contract and safety tests remain in `test/core`;
|
package/i18n/README.zh-CN.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/zhangqian-silk/yui/actions/workflows/ci.yml)
|
|
6
6
|
[](../LICENSE)
|
|
7
7
|

|
|
8
|
-

|
|
8
|
+

|
|
9
9
|
[](#参与开发)
|
|
10
10
|
|
|
11
11
|
让 Agent 持续推进你的任务,而不只是回答一轮对话。
|
|
@@ -30,16 +30,21 @@ Yui 是面向编程 Agent 的本地控制面。你只需用自然语言把目标
|
|
|
30
30
|
Web 仅本地回环,包含只读视图和经认证的用户控制。
|
|
31
31
|
- **默认隔离** —— 仓库改动发生在受管 Git worktree 中,稳定 checkout 保持只读。
|
|
32
32
|
|
|
33
|
-
> **状态:** 1.
|
|
33
|
+
> **状态:** 1.1.0,当前存储版本为 1.1,已声明从 1.0 基线升级。默认更新只执行同一存储
|
|
34
34
|
> 主版本内已声明的小版本升级;其他存储身份会在不修改 Home 的前提下被拒绝。
|
|
35
35
|
|
|
36
36
|
[快速开始](#快速开始) · [通过对话管理工作](#通过对话管理工作) · [架构](#架构) · [设计原则](#设计原则)
|
|
37
37
|
|
|
38
38
|
## 快速开始
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
npm 包包含 Linux x64、Mac Intel 和 Apple Silicon 的 Yui 预编译程序。
|
|
41
|
+
同一份包在三个平台验证后发布,运行时只选择对应平台的程序。
|
|
42
|
+
|
|
43
|
+
需要 Linux x64 / glibc 或 macOS(x64 或 Apple Silicon)、Git、tmux,以及
|
|
44
|
+
Node.js `^20.17.0`、`^22.9.0` 或 `^24.0.0`。macOS 上可用
|
|
45
|
+
`brew install tmux` 安装 tmux。最简单的方式是先安装 Codex CLI 或 Claude
|
|
46
|
+
Code CLI,并确保它已经可以使用你自己的账号正常工作。Yui 负责协调 Agent,
|
|
47
|
+
不提供模型访问额度。
|
|
43
48
|
|
|
44
49
|
Yui 传递 Claude 的认证环境并保留原生配置目录,由 Claude 自身按本地配置
|
|
45
50
|
选择 API key 或登录方式。更换 Session 不会重置登录或初始化记录。首次运行时,
|
|
@@ -64,10 +69,18 @@ yui setup
|
|
|
64
69
|
> 我已经安装了 Yui。请在交互式终端中帮我执行 `yui setup`,选择可用的
|
|
65
70
|
> Agent,并用 `yui doctor` 检查结果。遇到账号或需要我决定的配置时问我。
|
|
66
71
|
|
|
67
|
-
Setup 会配置与你对话的 Operator 和默认任务 Leader,并启动本地 Controller
|
|
72
|
+
Setup 会配置与你对话的 Operator 和默认任务 Leader,并启动本地 Controller,
|
|
73
|
+
以及这些活跃 Role 需要的 Codex 共享 daemon。
|
|
68
74
|
先用这两个角色即可开始,Worker、Reviewer 可以之后再配置。如果 Agent
|
|
69
75
|
没有操作交互式终端的能力,就自己运行 setup;它只负责初始配置。
|
|
70
76
|
|
|
77
|
+
之后启动时运行 `yui start`。它会按需启动 Controller;只有活跃 Role 使用
|
|
78
|
+
Codex 时才会启动 Codex 共享 daemon。在 macOS 上,如果安装了 Codex App,
|
|
79
|
+
它还会配置 App 下次启动时连接该 daemon。先运行 `yui start` 再打开 App;
|
|
80
|
+
如果 App 已经打开,按 Yui 的提示退出并重新打开。`yui doctor` 可以检查
|
|
81
|
+
实时连接。`yui controller start` 只启动 Controller。Linux 不需要 App 配置;
|
|
82
|
+
没有活跃 Codex Role 时不会启动 Codex daemon。
|
|
83
|
+
|
|
71
84
|
### 3. 开始对话
|
|
72
85
|
|
|
73
86
|
```sh
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zq-silk/yui",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Local control plane for long-running native agent CLI sessions backed by tmux.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -22,13 +22,12 @@
|
|
|
22
22
|
"node": "^20.17.0 || ^22.9.0 || ^24.0.0"
|
|
23
23
|
},
|
|
24
24
|
"os": [
|
|
25
|
-
"linux"
|
|
25
|
+
"linux",
|
|
26
|
+
"darwin"
|
|
26
27
|
],
|
|
27
28
|
"cpu": [
|
|
28
|
-
"x64"
|
|
29
|
-
|
|
30
|
-
"libc": [
|
|
31
|
-
"glibc"
|
|
29
|
+
"x64",
|
|
30
|
+
"arm64"
|
|
32
31
|
],
|
|
33
32
|
"keywords": [
|
|
34
33
|
"yui",
|
|
@@ -69,6 +69,14 @@ The DB Brief is the current summary, not a full plan or a history of messages.
|
|
|
69
69
|
After meaningful progress, use `task brief update` with only intended fields;
|
|
70
70
|
read `task event list` before deliberately restoring an older value. Keep
|
|
71
71
|
references to substantial plans, prototypes and reports in that summary.
|
|
72
|
+
When a delivered correction resolves a recorded blocker or changes the next
|
|
73
|
+
action, update the Brief's focus and Leader summary while the Task is still
|
|
74
|
+
open. Before completing a Task, reread its Brief: if it still describes a
|
|
75
|
+
resolved blocker, superseded plan, or waiting state, correct those fields
|
|
76
|
+
before `task complete`. The completion summary is the terminal conclusion;
|
|
77
|
+
do not copy it into Brief merely to create a second completion record. Brief
|
|
78
|
+
maintenance is not a new completion gate, and routine unchanged steps do not
|
|
79
|
+
need an update.
|
|
72
80
|
Decisions contain the actual decision, reason and necessary boundaries, not
|
|
73
81
|
the entire proposal. Label recommendations as recommendations, not user
|
|
74
82
|
decisions.
|
|
@@ -409,7 +409,10 @@ Before ending authorized execution:
|
|
|
409
409
|
with `yui task run show`, read each original result in full, and make the
|
|
410
410
|
next decision.
|
|
411
411
|
2. Persist actual WorkItem lifecycle and material Brief, Decision, Milestone,
|
|
412
|
-
Message, or Knowledge changes.
|
|
412
|
+
Message, or Knowledge changes. If an accepted result or authorized
|
|
413
|
+
correction has cleared a blocker still stated as current in Brief, update
|
|
414
|
+
that working summary before completing or yielding. Keep the original
|
|
415
|
+
failure and correction in their durable records.
|
|
413
416
|
3. Choose one truthful outcome: continue through an owned native child, complete
|
|
414
417
|
the Task, create a justified InputRequest, or leave the active Task waiting
|
|
415
418
|
for a real durable event.
|