@zq-silk/yui 0.16.0 → 0.99.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/ARCHITECTURE.md +12 -0
- package/ARCHITECTURE.zh-CN.md +8 -0
- package/README.md +3 -0
- package/dist/agent/managedRuntimeEnvironment.js +0 -4
- package/dist/agentRun/agentRun.js +7 -17
- package/dist/cli/agentConfigurationPicker.js +24 -21
- package/dist/cli/commandCatalog.js +18 -10
- package/dist/cli/roleWizard.js +41 -30
- package/dist/cli/updateCommand.js +35 -13
- package/dist/cli/updateOrchestrator.js +73 -33
- package/dist/cli/updatePorts.js +51 -20
- package/dist/cli.js +35 -26
- package/dist/commands/controllerCommands.js +1 -1
- package/dist/commands/executionAuditCommands.js +2 -2
- package/dist/commands/projectCommands.js +1 -1
- package/dist/commands/releaseCommands.js +9 -38
- package/dist/commands/taskCommands.js +84 -81
- package/dist/commands/taskCompletionGate.js +0 -117
- package/dist/commands/taskRoleRuntimeStatus.js +1 -1
- package/dist/commands/taskUpstreamCommands.js +93 -54
- package/dist/context/runContextPack.js +30 -54
- package/dist/context/runInputContract.js +9 -0
- package/dist/context/sessionBootstrapManifest.js +11 -19
- package/dist/controller/agentHostObservation.js +4 -2
- package/dist/controller/controller.js +8 -6
- package/dist/controller/fileSchedulerStoreAdapter.js +85 -18
- package/dist/controller/globalInputDelivery.js +28 -7
- package/dist/controller/globalRuntimeAttention.js +34 -0
- package/dist/controller/operatorNotification.js +31 -0
- package/dist/controller/providerRetryAdmission.js +3 -1
- package/dist/controller/providerRetryDelivery.js +10 -3
- package/dist/controller/runtime.js +1 -27
- package/dist/controller/sessionOwnerReconciliation.js +42 -24
- package/dist/controller/structuredProviderObservation.js +27 -0
- package/dist/controller/updateReconciliation.js +72 -20
- package/dist/doctor/doctor.js +2 -1
- package/dist/executor/agentAdapter.js +28 -96
- package/dist/executor/agentConfigurationCatalog.js +13 -68
- package/dist/executor/agentConfigurationFields.js +120 -0
- package/dist/executor/agentConfigurationProbe.js +55 -80
- package/dist/executor/agentExecutor.js +5 -2
- package/dist/executor/executorRegistry.js +0 -9
- package/dist/executor/fileRoleLaunchPlanner.js +12 -11
- package/dist/interaction/operatorPresentation.js +2 -1
- package/dist/message/messageContinuation.js +8 -9
- package/dist/observability/executionAudit.js +1 -10
- package/dist/observability/runtimeIdentity.js +0 -23
- package/dist/output/agentConfigurationPresentation.js +4 -2
- package/dist/release/releaseHandover.js +2 -2
- package/dist/release/releaseIdempotencyStore.js +0 -23
- package/dist/release/releaseWorkflowPorts.js +8 -4
- package/dist/repository/gitWorkspace.js +66 -19
- package/dist/repository/taskWorkspacePreparer.js +75 -68
- package/dist/resources/resourceRegistry.js +90 -44
- package/dist/resources/sqliteResourceRegistry.js +3 -5
- package/dist/runtime/acpSession.js +21 -5
- package/dist/runtime/agentEndpoint.js +4 -1
- package/dist/runtime/agentHost.js +22 -62
- package/dist/runtime/agentHostCleanup.js +85 -0
- package/dist/runtime/agentHostProtocol.js +1 -1
- package/dist/runtime/builtinAgentDrivers.js +46 -119
- package/dist/runtime/builtinTranscriptObserver.js +10 -6
- package/dist/runtime/codexAppServerRuntime.js +55 -89
- package/dist/runtime/jsonLineChannel.js +35 -7
- package/dist/runtime/providerContinuationReconciliationService.js +42 -3
- package/dist/runtime/providerRetry.js +15 -0
- package/dist/runtime/providerRuntimeReconciler.js +12 -2
- package/dist/runtime/structuredProviderHost.js +106 -81
- package/dist/scheduler/activeRoleRunDelivery.js +5 -1
- package/dist/scheduler/operatorInputNotificationProcessor.js +7 -17
- package/dist/storage/upgrade/upgradeOrchestrator.js +0 -10
- package/dist/task/nextAction.js +33 -23
- package/dist/task/task.js +2 -1
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app.js +72 -3
- package/dist/web/assets/client/components.js +2 -1
- package/dist/web/assets/client/i18n.js +2 -0
- package/dist/web/assets/client/taskSummary.js +345 -0
- package/dist/web/assets/client/taskSurface.js +44 -26
- package/dist/web/assets/client/view.js +33 -0
- package/dist/web/assets/shell.js +1 -0
- package/dist/web/assets/styles/cards.js +21 -0
- package/dist/web/webServer.js +39 -5
- package/dist/web/webSessions.js +165 -0
- package/dist/web/webSnapshot.js +23 -0
- package/dist/web/webTaskSurface.js +23 -0
- package/docs/managed-turn-and-session-runtime.md +31 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +23 -0
- package/docs/observability/README.md +47 -0
- package/docs/observability/README.zh-CN.md +37 -0
- package/docs/project-refresh.md +9 -0
- package/docs/project-refresh.zh-CN.md +8 -0
- package/docs/provider-retry.md +35 -0
- package/docs/release-workflow.md +128 -20
- package/docs/release-workflow.zh-CN.md +107 -14
- package/docs/roles-and-configuration.md +30 -0
- package/docs/roles-and-configuration.zh-CN.md +20 -0
- package/docs/task-delivery.md +109 -0
- package/docs/task-delivery.zh-CN.md +80 -0
- package/docs/task-discovery.md +9 -0
- package/docs/task-discovery.zh-CN.md +6 -0
- package/docs/testing/verification-levels.md +33 -12
- package/docs/testing/verification-levels.zh-CN.md +28 -8
- package/i18n/README.zh-CN.md +2 -0
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +11 -0
- package/skills/yui-leader/references/execution.md +22 -0
- package/skills/yui-leader/references/planning.md +6 -2
- package/skills/yui-operator/SKILL.md +45 -13
- package/skills/yui-operator/references/task-delivery.md +99 -0
- package/skills/yui-runtime/SKILL.md +5 -3
- package/skills/yui-runtime/references/publication.md +56 -5
- package/skills/yui-runtime/references/recovery.md +12 -2
- package/dist/runtime/agentHostCompatibility.js +0 -127
package/ARCHITECTURE.md
CHANGED
|
@@ -67,6 +67,18 @@ AgentHost serializes submission through AgentEndpoint. The Provider binding
|
|
|
67
67
|
records actual acceptance and native correlation. A notification can settle
|
|
68
68
|
on acceptance without requiring a final execution report.
|
|
69
69
|
|
|
70
|
+
Operator attention has a separate, local handoff boundary: one transaction
|
|
71
|
+
saves the exact batch as a Global system Message and consumes only that mailbox
|
|
72
|
+
claim. `queued` (with the Message id) means durable queue ownership, not Provider
|
|
73
|
+
acceptance or Agent processing. Later events form a new batch without repeating
|
|
74
|
+
the handed-off refs. Global Messages alone own subsequent native delivery,
|
|
75
|
+
including rejection, unknown acceptance and Session-target mismatch; none causes
|
|
76
|
+
automatic reissuance under a different key. A pass can mark multiple definitively
|
|
77
|
+
unsent stale-target entries undelivered before reaching successor input, but never
|
|
78
|
+
bypass an uncertain attempt or an interrupt-then reservation. Original Messages
|
|
79
|
+
and source Task/Input/Event records remain inspectable. Notifications ask Agents
|
|
80
|
+
to reread facts; they do not authorize replaying prior operations.
|
|
81
|
+
|
|
70
82
|
Busy with proven non-acceptance preserves the input for a subsequent attempt.
|
|
71
83
|
Transport submission alone does not prove acceptance. Unknown effects remain
|
|
72
84
|
visible and fenced: no blind resend or inferred success. Explicit replacement
|
package/ARCHITECTURE.zh-CN.md
CHANGED
|
@@ -53,6 +53,14 @@ AgentRun 记录一次明确请求的执行,带有冻结的 Context 和生效
|
|
|
53
53
|
mailbox 批次;AgentHost 通过 AgentEndpoint 串行化提交。Provider 绑定记录实际接受和原生
|
|
54
54
|
关联。一条通知可以在接受时结算,而不要求最终执行报告。
|
|
55
55
|
|
|
56
|
+
Operator attention 有独立的本地交接边界:同一事务把精确批次保存为 Global 系统 Message,
|
|
57
|
+
并且只消费该 mailbox claim。`queued`(带 Message id)表示持久队列已接管,不代表 Provider
|
|
58
|
+
接受或 Agent 已处理。后到事件形成新批次,不重复已经交出的 refs。后续原生投递仅由
|
|
59
|
+
Global Message 负责,包括拒绝、接受结果未知和 Session 目标失效;这些结果都不会自动换键
|
|
60
|
+
重发。一轮可将多个已明确未提交且目标失效的条目标为未投递,再处理后继输入,但不能越过
|
|
61
|
+
不确定尝试或 interrupt-then 预约。原始 Message 和来源 Task/Input/Event 仍可查阅。通知
|
|
62
|
+
只要求 Agent 重读事实,不授予重放先前操作的权限。
|
|
63
|
+
|
|
56
64
|
忙碌且已证明未接受会为后续尝试保留输入。仅有传输提交不证明接受。未知效果保持可见且被
|
|
57
65
|
围栏隔离:不盲目重发,也不推断成功。显式替换先解决实际的原生执行,再丢弃其工程占用。
|
|
58
66
|
在一个已认领批次期间到达的输入,留待下一个批次。读取 Context 不消费投递。
|
package/README.md
CHANGED
|
@@ -140,6 +140,9 @@ For a visual overview, run `yui web` in another terminal. The local Web view
|
|
|
140
140
|
shows the same tasks and pending questions and lets you send messages, answer
|
|
141
141
|
questions and explicitly queue, steer or interrupt Task input. These authenticated
|
|
142
142
|
Task controls use the same operations as the CLI; Web is not a separate task system.
|
|
143
|
+
The detail prioritizes user input, native Session activity, Task progress and key
|
|
144
|
+
conclusions; fixed file results and delivery evidence open on demand. See
|
|
145
|
+
[attention and progress](docs/observability/README.md#web-attention-and-progress).
|
|
143
146
|
See [Web permissions](docs/architecture/capabilities-and-resources.md#cli-and-web)
|
|
144
147
|
and [input timing](docs/managed-turn-and-session-runtime.md#input-timing-queue-steer-and-interrupt).
|
|
145
148
|
|
|
@@ -19,10 +19,6 @@ export const YUI_MANAGED_RUNTIME_ENVIRONMENT_NAMES = Object.freeze([
|
|
|
19
19
|
"YUI_RUN_ID",
|
|
20
20
|
"YUI_NATIVE_SESSION_ROOT",
|
|
21
21
|
"YUI_NATIVE_SESSION_ID",
|
|
22
|
-
// Retired: Yui no longer publishes a frozen control-plane descriptor into a
|
|
23
|
-
// managed process. Sessions launched by an earlier release still carry it, so
|
|
24
|
-
// it stays in this sanitize list.
|
|
25
|
-
"YUI_CONTROL_PLANE_DESCRIPTOR",
|
|
26
22
|
"YUI_TASK_RUNTIME_ISOLATION_DESCRIPTOR",
|
|
27
23
|
"YUI_TASK_RUNTIME_SERVICE_NAMESPACE",
|
|
28
24
|
"YUI_SESSION_TITLE",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { validateEffectiveLaunchSnapshot } from "../executor/effectiveLaunch.js";
|
|
2
2
|
import { validateTaskRecordReference } from "../task/taskRecordReference.js";
|
|
3
3
|
import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createRunInputEnvelope, requireRunContextSnapshotRef, validateRunInput } from "../context/runInputContract.js";
|
|
5
5
|
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
6
6
|
import { boundedRunFailureDiagnostic, MAX_RUN_FAILURE_DIAGNOSTIC_BYTES, MAX_RUN_RESULT_OUTPUT_BYTES } from "../domain/agentResultTransport.js";
|
|
7
7
|
export { boundedRunFailureDiagnostic, MAX_RUN_FAILURE_DIAGNOSTIC_BYTES, MAX_RUN_RESULT_OUTPUT_BYTES, transportAgentResult } from "../domain/agentResultTransport.js";
|
|
@@ -32,6 +32,9 @@ export function createRun(id, taskId, roleName, mode, input, now, context) {
|
|
|
32
32
|
}
|
|
33
33
|
const timestamp = now.toISOString();
|
|
34
34
|
const normalizedInput = validateRunInput(input);
|
|
35
|
+
const snapshot = requireRunContextSnapshotRef(normalizedInput);
|
|
36
|
+
if (snapshot.taskId !== taskId)
|
|
37
|
+
throw new Error("AgentRun Context Snapshot belongs to another Task.");
|
|
35
38
|
return {
|
|
36
39
|
schemaVersion: 5,
|
|
37
40
|
id: requireSafeIdentity(id, "AgentRun id"),
|
|
@@ -85,22 +88,6 @@ export function runPurposeAdmitsTaskState(purpose, task) {
|
|
|
85
88
|
? task.status === "draft" || task.status === "active"
|
|
86
89
|
: task.status === "active";
|
|
87
90
|
}
|
|
88
|
-
/** Binds a freshly created, not-yet-persisted Turn to its frozen Context. */
|
|
89
|
-
export function withRunContextSnapshot(run, snapshot, deltaRefIds = []) {
|
|
90
|
-
const initial = run.inputs[0];
|
|
91
|
-
if (run.status !== "active" || initial.input.contextSnapshotRef !== undefined) {
|
|
92
|
-
throw new Error(`Cannot replace the AgentRun Context Snapshot: ${run.id}.`);
|
|
93
|
-
}
|
|
94
|
-
const input = createRunInput({
|
|
95
|
-
...initial.input,
|
|
96
|
-
contextSnapshotRef: snapshot,
|
|
97
|
-
deltaRefIds
|
|
98
|
-
});
|
|
99
|
-
return validateRun(Object.freeze({
|
|
100
|
-
...run,
|
|
101
|
-
inputs: [runInputRecord(input, 1, initial.submittedAt), ...run.inputs.slice(1)]
|
|
102
|
-
}));
|
|
103
|
-
}
|
|
104
91
|
export function appendRunInput(run, input, now) {
|
|
105
92
|
validateRun(run);
|
|
106
93
|
if (run.status !== "active")
|
|
@@ -118,6 +105,7 @@ export function appendRunInput(run, input, now) {
|
|
|
118
105
|
/** Derives Provider-visible identity from the AgentRun, the sole semantic owner. */
|
|
119
106
|
export function runInputEnvelope(run, sequence = 1) {
|
|
120
107
|
validateRun(run);
|
|
108
|
+
requireRunContextSnapshotRef(run.inputs[0].input);
|
|
121
109
|
const record = run.inputs[sequence - 1];
|
|
122
110
|
if (record === undefined)
|
|
123
111
|
throw new Error(`AgentRun input does not exist: ${run.id}/${sequence}.`);
|
|
@@ -295,6 +283,8 @@ export function validateRun(run) {
|
|
|
295
283
|
throw new Error("Execution AgentRun cannot carry Review effective provenance.");
|
|
296
284
|
}
|
|
297
285
|
for (const record of run.inputs) {
|
|
286
|
+
// Structural validation keeps historical records and observed subsequent
|
|
287
|
+
// inputs readable. Creation/submission require the initial frozen Snapshot.
|
|
298
288
|
createRunInputEnvelope(runEnvelopeContext(run), record.input);
|
|
299
289
|
}
|
|
300
290
|
if (!["active", "completed", "failed"].includes(run.status)) {
|
|
@@ -30,12 +30,11 @@ export async function selectAgentEffort(resolved, io, input) {
|
|
|
30
30
|
}
|
|
31
31
|
/** Select provider-native permission settings in the adapter's canonical shape. */
|
|
32
32
|
export async function selectAgentPermission(resolved, io, current) {
|
|
33
|
+
renderResolutionNotice(resolved, io);
|
|
33
34
|
const strategyField = configurationField(resolved.catalog, "permission.strategy");
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
{ value: "configured", label: "configured" }
|
|
38
|
-
], current.strategy);
|
|
35
|
+
if (strategyField?.reason !== undefined)
|
|
36
|
+
io.write(`${strategyField.reason}\n`);
|
|
37
|
+
const strategyChoices = uniqueChoices(strategyField?.available === false ? [] : strategyField?.choices ?? [], current.strategy);
|
|
39
38
|
const strategy = await choose("Select permission strategy", strategyChoices.map(({ value, label, description }) => ({
|
|
40
39
|
value,
|
|
41
40
|
label,
|
|
@@ -67,6 +66,10 @@ export async function selectAgentPermission(resolved, io, current) {
|
|
|
67
66
|
delete permission.approval;
|
|
68
67
|
else
|
|
69
68
|
permission.approval = approval.value;
|
|
69
|
+
if (permission.sandbox === undefined && permission.approval === undefined) {
|
|
70
|
+
io.write("Configured permission requires an explicit native option; configuration unchanged.\n");
|
|
71
|
+
return { kind: "cancelled" };
|
|
72
|
+
}
|
|
70
73
|
return {
|
|
71
74
|
kind: "selected",
|
|
72
75
|
permission: permission
|
|
@@ -98,6 +101,11 @@ export async function selectAgentPermission(resolved, io, current) {
|
|
|
98
101
|
delete permission.mode;
|
|
99
102
|
else
|
|
100
103
|
permission.mode = mode.value;
|
|
104
|
+
if (permission.mode === undefined && permission.allowedTools === undefined
|
|
105
|
+
&& permission.disallowedTools === undefined) {
|
|
106
|
+
io.write("Configured permission requires an explicit native option; configuration unchanged.\n");
|
|
107
|
+
return { kind: "cancelled" };
|
|
108
|
+
}
|
|
101
109
|
return {
|
|
102
110
|
kind: "selected",
|
|
103
111
|
permission: permission
|
|
@@ -107,12 +115,13 @@ export function renderAgentConfigurationResolutionNotice(resolved) {
|
|
|
107
115
|
const lines = [];
|
|
108
116
|
if (resolved.source === "cache") {
|
|
109
117
|
lines.push((resolved.failure === undefined ? "" : `! Runtime capability request failed (${resolved.failure.message}). `)
|
|
110
|
-
+ `Showing cached options from ${resolved.fetchedAt ?? "an earlier request"}; they may be stale.`);
|
|
118
|
+
+ `Showing identity-matched cached options from ${resolved.fetchedAt ?? "an earlier request"}; they may be stale.`);
|
|
111
119
|
}
|
|
112
120
|
else if (resolved.source === "fallback") {
|
|
113
121
|
lines.push(`! Runtime capability request failed (${resolved.failure?.message ?? "unknown failure"}). `
|
|
114
|
-
+ "No matching cache is available; only
|
|
122
|
+
+ "No matching cache is available; only declared static adapter contracts and explicit custom values can be offered.");
|
|
115
123
|
}
|
|
124
|
+
lines.push(`Metadata source: ${resolved.source}; last probe attempted ${resolved.attemptedAt}${resolved.fetchedAt === undefined ? "" : `; fetched ${resolved.fetchedAt}`}. Source describes the query, not native confirmation of every field.`);
|
|
116
125
|
lines.push(...resolved.catalog.warnings.map((warning) => `! Agent catalog warning: ${warning}`));
|
|
117
126
|
return lines.length === 0 ? "" : `${lines.join("\n")}\n`;
|
|
118
127
|
}
|
|
@@ -215,29 +224,23 @@ function observedEfforts(models) {
|
|
|
215
224
|
}
|
|
216
225
|
async function selectPermissionField(resolved, io, key, current, defaultValue) {
|
|
217
226
|
const field = configurationField(resolved.catalog, key);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const choices = uniqueChoices(field === undefined
|
|
224
|
-
? fallback.map((value) => ({ value, label: value }))
|
|
225
|
-
: field.choices, current);
|
|
226
|
-
const includeOmit = defaultValue === OMIT || current !== undefined;
|
|
227
|
+
if (field?.reason !== undefined)
|
|
228
|
+
io.write(`${key}: ${field.reason}\n`);
|
|
229
|
+
if (field === undefined)
|
|
230
|
+
io.write(`${key}: no catalog field was reported; no choices inferred.\n`);
|
|
231
|
+
const choices = uniqueChoices(field?.available === false ? [] : field?.choices ?? [], current);
|
|
227
232
|
const pickerChoices = [
|
|
228
|
-
|
|
229
|
-
? [{ value: OMIT, label: "Omit", detail: "Do not pass this provider option" }]
|
|
230
|
-
: []),
|
|
233
|
+
{ value: OMIT, label: "Omit", detail: "Do not pass this provider option" },
|
|
231
234
|
...choices.map(({ value, label, description }) => ({
|
|
232
235
|
value,
|
|
233
236
|
label,
|
|
234
237
|
detail: description ?? value
|
|
235
238
|
})),
|
|
236
239
|
...(field?.allowCustom === true
|
|
237
|
-
? [{ value: CUSTOM, label: "Custom…", detail: "
|
|
240
|
+
? [{ value: CUSTOM, label: "Custom…", detail: "Explicit value; native acceptance unverified" }]
|
|
238
241
|
: [])
|
|
239
242
|
];
|
|
240
|
-
const selected = await choose(`Select ${key}`, pickerChoices, io, current ??
|
|
243
|
+
const selected = await choose(`Select ${key}`, pickerChoices, io, current ?? (choices.some(choice => choice.value === defaultValue) ? defaultValue : OMIT), key);
|
|
241
244
|
if (selected === undefined)
|
|
242
245
|
return { kind: "cancelled" };
|
|
243
246
|
if (selected === OMIT)
|
|
@@ -344,7 +344,7 @@ const taskChildren = [
|
|
|
344
344
|
},
|
|
345
345
|
{
|
|
346
346
|
name: "complete",
|
|
347
|
-
summary: "Complete
|
|
347
|
+
summary: "Complete locally, or prepare/dispatch the configured final Review and remain active. --refresh-remote only fetches freshness observations; it never rebases, changes HEAD or starts Integration checks.",
|
|
348
348
|
usage: "yui task complete <id> (--summary <text>|--summary-file <path|->) [--artifact-ref <artifact-id|turn:id|url> ...] [--refresh-remote] [--accept-published-tree <publication-id>]",
|
|
349
349
|
options: ["--summary", "--summary-file", "--artifact-ref", "--refresh-remote", "--accept-published-tree"],
|
|
350
350
|
fileOptions: ["--summary-file"]
|
|
@@ -356,7 +356,7 @@ const taskChildren = [
|
|
|
356
356
|
children: [
|
|
357
357
|
{
|
|
358
358
|
name: "status",
|
|
359
|
-
summary: "
|
|
359
|
+
summary: "Observe Task Project bases against local refs; --refresh fetches remote objects without changing HEAD, starting checks or creating Integration/Review.",
|
|
360
360
|
usage: "yui task base status <task> [--refresh]",
|
|
361
361
|
options: ["--refresh"]
|
|
362
362
|
}
|
|
@@ -402,7 +402,7 @@ const taskChildren = [
|
|
|
402
402
|
},
|
|
403
403
|
{
|
|
404
404
|
name: "next-action",
|
|
405
|
-
summary: "
|
|
405
|
+
summary: "Read Task facts, mechanical prerequisites and legal alternatives; semantic choices remain with the Leader.",
|
|
406
406
|
usage: "yui task next-action <task> [--json]",
|
|
407
407
|
options: ["--json"]
|
|
408
408
|
},
|
|
@@ -432,7 +432,7 @@ const taskChildren = [
|
|
|
432
432
|
children: [
|
|
433
433
|
{
|
|
434
434
|
name: "integrate",
|
|
435
|
-
summary: "
|
|
435
|
+
summary: "Explicitly rebase Task changes onto the remote development head, run configured/additional checks (possibly as Jobs), then advance HEAD through Integration CAS. Returns exact attempts and partial results; does not request Review or complete the Task.",
|
|
436
436
|
usage: "yui task upstream integrate <task> (--latest|--project <project>) [--check <command> ...] [--rerun-checks]",
|
|
437
437
|
options: ["--latest", "--project", "--check", "--rerun-checks"]
|
|
438
438
|
}
|
|
@@ -893,7 +893,7 @@ const taskChildren = [
|
|
|
893
893
|
{
|
|
894
894
|
name: "expand",
|
|
895
895
|
summary: "Expand one authorized AgentRun context reference.",
|
|
896
|
-
usage: "yui task run context expand <task>/<run> <ref-id>
|
|
896
|
+
usage: "yui task run context expand <task>/<run> <ref-id> --store <store> [--mode full]",
|
|
897
897
|
options: ["--store", "--mode"]
|
|
898
898
|
},
|
|
899
899
|
{
|
|
@@ -916,7 +916,7 @@ const taskChildren = [
|
|
|
916
916
|
name: "retire",
|
|
917
917
|
summary: "Retire an incorrect historical AgentRun without deleting its audit record.",
|
|
918
918
|
usage: "yui task run retire <task>/<run> --reason <text> [--expected-progress-at <timestamp>] [--agent-id <id>] [--adapter-id <id>] [--native-session-id <id>]",
|
|
919
|
-
options: ["--reason", "--expected-progress-at", "--
|
|
919
|
+
options: ["--reason", "--expected-progress-at", "--agent-id", "--adapter-id", "--native-session-id"]
|
|
920
920
|
}
|
|
921
921
|
]
|
|
922
922
|
},
|
|
@@ -1142,7 +1142,12 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1142
1142
|
},
|
|
1143
1143
|
{ name: "help", summary: "Show root or scoped command help.", usage: "yui help [command ...]", commandPathArguments: true },
|
|
1144
1144
|
{ name: "version", summary: "Print the installed Yui version." },
|
|
1145
|
-
{
|
|
1145
|
+
{
|
|
1146
|
+
name: "update",
|
|
1147
|
+
summary: "Install the latest or an exact published Yui version after storage preflight.",
|
|
1148
|
+
usage: "yui update [--version <exact-version>]",
|
|
1149
|
+
options: ["--version"]
|
|
1150
|
+
},
|
|
1146
1151
|
{
|
|
1147
1152
|
name: "upgrade",
|
|
1148
1153
|
summary: "Plan or apply supported storage migrations for this Home.",
|
|
@@ -1379,8 +1384,8 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1379
1384
|
{
|
|
1380
1385
|
name: "clone",
|
|
1381
1386
|
summary: "Clone and bind a Project after user confirmation.",
|
|
1382
|
-
usage: "yui project clone <name> <remote> [--alias <name> ...] [--stable <ref>] [--development <ref>]",
|
|
1383
|
-
options: ["--alias", "--stable", "--development"]
|
|
1387
|
+
usage: "yui project clone <name> <remote> [--alias <name> ...] [--stable <ref>] [--development <ref>] [--external]",
|
|
1388
|
+
options: ["--alias", "--stable", "--development", "--external"]
|
|
1384
1389
|
},
|
|
1385
1390
|
{
|
|
1386
1391
|
name: "refresh",
|
|
@@ -1722,8 +1727,11 @@ export function validateCommandCatalog(root) {
|
|
|
1722
1727
|
}
|
|
1723
1728
|
const options = new Set();
|
|
1724
1729
|
for (const option of [...node.options, ...node.hiddenOptions]) {
|
|
1725
|
-
|
|
1730
|
+
// --version is an alias only as the entire root invocation. Scoped
|
|
1731
|
+
// commands may own a version selector; help aliases remain reserved.
|
|
1732
|
+
if (reservedAliases.has(option) && option !== "--version") {
|
|
1726
1733
|
throw new Error(`Reserved alias token is not allowed: ${[...node.path, option].join(" ")}`);
|
|
1734
|
+
}
|
|
1727
1735
|
if (options.has(option) || immediate.has(option))
|
|
1728
1736
|
throw new Error(`Duplicate command token: ${[...node.path, option].join(" ")}`);
|
|
1729
1737
|
options.add(option);
|
package/dist/cli/roleWizard.js
CHANGED
|
@@ -2,7 +2,7 @@ import { renderTable } from "../output/table.js";
|
|
|
2
2
|
import { displayExecutionComponent } from "../agent/executionComponents.js";
|
|
3
3
|
import { isAgentAdapterId } from "../agent/adapterCatalog.js";
|
|
4
4
|
import { defaultRoleAgentConfig } from "../executor/agentAdapter.js";
|
|
5
|
-
import { selectAgentEffort, selectAgentModelAndEffort } from "./agentConfigurationPicker.js";
|
|
5
|
+
import { selectAgentEffort, selectAgentModelAndEffort, renderAgentConfigurationResolutionNotice } from "./agentConfigurationPicker.js";
|
|
6
6
|
const TEXT_COLUMN = { header: "Selection", minWidth: 12, maxWidth: 34 };
|
|
7
7
|
export async function resolveRoleWizardArguments(commandArgs, ports, io) {
|
|
8
8
|
const args = [...commandArgs];
|
|
@@ -231,6 +231,8 @@ async function configureNewAgentField(args, agent, ports, io) {
|
|
|
231
231
|
args: selection.effort === undefined ? args : [...args, "--effort", selection.effort]
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
|
+
if (resolved !== undefined)
|
|
235
|
+
io.write(renderAgentConfigurationResolutionNotice(resolved));
|
|
234
236
|
if (selected.value === "permission-strategy") {
|
|
235
237
|
const strategy = await promptAgentFieldValue(selected, io);
|
|
236
238
|
if (strategy === undefined || strategy.length === 0)
|
|
@@ -407,6 +409,8 @@ async function updateAgentBindingSettings(args, role, binding, ports, io) {
|
|
|
407
409
|
]
|
|
408
410
|
};
|
|
409
411
|
}
|
|
412
|
+
if (resolved !== undefined)
|
|
413
|
+
io.write(renderAgentConfigurationResolutionNotice(resolved));
|
|
410
414
|
if (selectedField.value === "permission-strategy") {
|
|
411
415
|
const strategy = await promptAgentFieldValue(selectedField, io);
|
|
412
416
|
if (strategy === undefined || strategy.length === 0)
|
|
@@ -446,23 +450,17 @@ function agentFields(binding, catalog) {
|
|
|
446
450
|
return [
|
|
447
451
|
agentField("model", "Model", config.model, "--model", ["--clear-model"]),
|
|
448
452
|
agentField("effort", "Effort", config.effort, "--effort", ["--clear-effort"]),
|
|
449
|
-
agentField("permission-strategy", "Permission strategy", permission.strategy, "--permission-strategy", ["--permission-strategy", "default"],
|
|
453
|
+
agentField("permission-strategy", "Permission strategy", permission.strategy, "--permission-strategy", ["--permission-strategy", "default"], catalogField(catalog, "permission.strategy")),
|
|
450
454
|
...(binding.adapterId === "codex" && permission.strategy === "configured" ? [
|
|
451
|
-
agentField("sandbox", "Sandbox", permission.sandbox, "--sandbox", ["--permission-strategy", "default"],
|
|
452
|
-
|
|
453
|
-
])),
|
|
454
|
-
agentField("approval", "Approval", permission.approval, "--approval", ["--permission-strategy", "default"], catalogChoices(catalog, "permission.approval", [
|
|
455
|
-
"untrusted", "on-request", "never"
|
|
456
|
-
]))
|
|
455
|
+
agentField("sandbox", "Sandbox", permission.sandbox, "--sandbox", ["--permission-strategy", "default"], catalogField(catalog, "permission.sandbox")),
|
|
456
|
+
agentField("approval", "Approval", permission.approval, "--approval", ["--permission-strategy", "default"], catalogField(catalog, "permission.approval"))
|
|
457
457
|
] : []),
|
|
458
458
|
...(binding.adapterId === "codex" ? [
|
|
459
|
-
agentField("search", "Web search", config.search, "--search", ["--clear-search"],
|
|
460
|
-
...catalogChoices(catalog, "search", ["true"])
|
|
461
|
-
])
|
|
459
|
+
agentField("search", "Web search", config.search, "--search", ["--clear-search"], catalogField(catalog, "search"))
|
|
462
460
|
] : []),
|
|
463
461
|
...((binding.adapterId === "claude" || binding.adapterId === "acp")
|
|
464
462
|
&& permission.strategy === "configured" ? [
|
|
465
|
-
agentField("permission-mode", "Permission mode", permission.mode, "--permission-mode", ["--permission-strategy", "default"],
|
|
463
|
+
agentField("permission-mode", "Permission mode", permission.mode, "--permission-mode", ["--permission-strategy", "default"], catalogField(catalog, "permission.mode"))
|
|
466
464
|
] : [])
|
|
467
465
|
];
|
|
468
466
|
}
|
|
@@ -479,9 +477,8 @@ async function loadAgentCatalog(ports, binding, cwd) {
|
|
|
479
477
|
? value
|
|
480
478
|
: undefined;
|
|
481
479
|
}
|
|
482
|
-
function
|
|
483
|
-
|
|
484
|
-
return field === undefined ? [...fallback] : field.choices.map(({ value }) => value);
|
|
480
|
+
function catalogField(catalog, key) {
|
|
481
|
+
return catalog?.fields.find((candidate) => candidate.key === key);
|
|
485
482
|
}
|
|
486
483
|
function appendModelEffortPatch(args, selection, update) {
|
|
487
484
|
return [
|
|
@@ -494,8 +491,19 @@ function appendModelEffortPatch(args, selection, update) {
|
|
|
494
491
|
: ["--effort", selection.effort])
|
|
495
492
|
];
|
|
496
493
|
}
|
|
497
|
-
function agentField(value, label, current, set, clear,
|
|
498
|
-
|
|
494
|
+
function agentField(value, label, current, set, clear, capability) {
|
|
495
|
+
const reported = capability?.available === false ? [] : capability?.choices.map(choice => choice.value) ?? [];
|
|
496
|
+
const retained = (typeof current === "string" || typeof current === "boolean")
|
|
497
|
+
&& !reported.includes(String(current)) ? [String(current)] : [];
|
|
498
|
+
return {
|
|
499
|
+
value, label, current: display(current), set, clear,
|
|
500
|
+
choices: [...reported, ...retained], allowCustom: capability?.allowCustom === true,
|
|
501
|
+
reason: [
|
|
502
|
+
capability?.reason ?? (capability === undefined
|
|
503
|
+
? "No catalog field was reported; no choices inferred." : ""),
|
|
504
|
+
...(retained.length === 0 ? [] : ["Current value is retained but not reported by this catalog."])
|
|
505
|
+
].filter(Boolean).join(" ")
|
|
506
|
+
};
|
|
499
507
|
}
|
|
500
508
|
async function configuredPermissionArgs(binding, catalog, strategy, io) {
|
|
501
509
|
if (strategy !== "configured")
|
|
@@ -509,12 +517,8 @@ async function configuredPermissionArgs(binding, catalog, strategy, io) {
|
|
|
509
517
|
return undefined;
|
|
510
518
|
const option = field === "sandbox" ? "--sandbox" : "--approval";
|
|
511
519
|
const value = await promptAgentFieldValue(agentField(field, field === "sandbox" ? "Sandbox" : "Approval", undefined, option, ["--permission-strategy", "default"], field === "sandbox"
|
|
512
|
-
?
|
|
513
|
-
|
|
514
|
-
])
|
|
515
|
-
: catalogChoices(catalog, "permission.approval", [
|
|
516
|
-
"untrusted", "on-request", "never"
|
|
517
|
-
])), io);
|
|
520
|
+
? catalogField(catalog, "permission.sandbox")
|
|
521
|
+
: catalogField(catalog, "permission.approval")), io);
|
|
518
522
|
return value === undefined
|
|
519
523
|
? undefined
|
|
520
524
|
: ["--permission-strategy", "configured", option, value];
|
|
@@ -523,9 +527,9 @@ async function configuredPermissionArgs(binding, catalog, strategy, io) {
|
|
|
523
527
|
// An ACP Session's configured permission is one mode the Agent enumerated,
|
|
524
528
|
// so there is nothing to choose between: ask for the mode directly rather
|
|
525
529
|
// than offering tool rules the protocol has no place for. The choices come
|
|
526
|
-
// from the
|
|
527
|
-
//
|
|
528
|
-
const value = await promptAgentFieldValue(agentField("permission-mode", "Permission mode", undefined, "--permission-mode", ["--permission-strategy", "default"],
|
|
530
|
+
// from the catalog. An explicitly open field permits a custom mode before
|
|
531
|
+
// Session creation; it is checked against the native Session at launch.
|
|
532
|
+
const value = await promptAgentFieldValue(agentField("permission-mode", "Permission mode", undefined, "--permission-mode", ["--permission-strategy", "default"], catalogField(catalog, "permission.mode")), io);
|
|
529
533
|
return value === undefined || value.length === 0
|
|
530
534
|
? undefined
|
|
531
535
|
: ["--permission-strategy", "configured", "--permission-mode", value];
|
|
@@ -541,16 +545,23 @@ async function configuredPermissionArgs(binding, catalog, strategy, io) {
|
|
|
541
545
|
? "--permission-mode"
|
|
542
546
|
: field === "allowed" ? "--allowed-tool" : "--disallowed-tool";
|
|
543
547
|
const value = field === "mode"
|
|
544
|
-
? await promptAgentFieldValue(agentField("permission-mode", "Permission mode", undefined, option, ["--permission-strategy", "default"],
|
|
548
|
+
? await promptAgentFieldValue(agentField("permission-mode", "Permission mode", undefined, option, ["--permission-strategy", "default"], catalogField(catalog, "permission.mode")), io)
|
|
545
549
|
: (await io.question(`${field === "allowed" ? "Allowed" : "Disallowed"} tool: `))?.trim();
|
|
546
550
|
return value === undefined || value.length === 0
|
|
547
551
|
? undefined
|
|
548
552
|
: ["--permission-strategy", "configured", option, value];
|
|
549
553
|
}
|
|
550
554
|
async function promptAgentFieldValue(field, io) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
555
|
+
if (field.reason)
|
|
556
|
+
io.write(`${field.label}: ${field.reason}\n`);
|
|
557
|
+
if (field.choices === undefined)
|
|
558
|
+
return (await io.question(`${field.label}: `))?.trim();
|
|
559
|
+
const custom = "\0yui:custom";
|
|
560
|
+
const selected = await choose(`Set ${field.label}`, [
|
|
561
|
+
...field.choices.map(choice => ({ value: choice, cells: [choice] })),
|
|
562
|
+
...(field.allowCustom ? [{ value: custom, cells: ["Custom value (native acceptance unverified)"] }] : [])
|
|
563
|
+
], [TEXT_COLUMN], io, field.choices.includes(field.current) ? field.current : field.choices[0], "value");
|
|
564
|
+
return selected === custom ? (await io.question(`Custom ${field.label}: `))?.trim() : selected;
|
|
554
565
|
}
|
|
555
566
|
async function selectActiveAgent(role, ports, io) {
|
|
556
567
|
const configured = await configuredAgents(ports);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { resolveYuiHome } from "../storage/taskStore.js";
|
|
2
2
|
import { createUpdatePorts } from "./updatePorts.js";
|
|
3
3
|
import { runUpdate } from "./updateOrchestrator.js";
|
|
4
|
+
import { isConcreteVersion } from "../domain/validation.js";
|
|
5
|
+
import { usageError } from "../errors/cliError.js";
|
|
4
6
|
/**
|
|
5
7
|
* Run `yui update` as a side-by-side, recoverable orchestration.
|
|
6
8
|
*
|
|
@@ -11,15 +13,24 @@ import { runUpdate } from "./updateOrchestrator.js";
|
|
|
11
13
|
*
|
|
12
14
|
* Returns a process exit code: 0 on success or already-current, 5 on abort.
|
|
13
15
|
*/
|
|
14
|
-
export function runUpdateCommand(environment = process.env, spawn, write = (text) => process.stdout.write(text), ports) {
|
|
16
|
+
export function runUpdateCommand(args = [], environment = process.env, spawn, write = (text) => process.stdout.write(text), ports) {
|
|
17
|
+
const version = parseUpdateVersion(args);
|
|
15
18
|
const home = resolveYuiHome(environment);
|
|
16
19
|
const resolvedPorts = ports ?? createUpdatePorts(environment, spawn);
|
|
17
|
-
const result = runUpdate(resolvedPorts, { home });
|
|
20
|
+
const result = runUpdate(resolvedPorts, { home, version });
|
|
18
21
|
write(`${renderUpdateResult(result)}\n`);
|
|
19
22
|
if (result.outcome === "aborted")
|
|
20
23
|
return 5;
|
|
21
24
|
return 0;
|
|
22
25
|
}
|
|
26
|
+
function parseUpdateVersion(args) {
|
|
27
|
+
if (args.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
if (args.length === 2 && args[0] === "--version" && isConcreteVersion(args[1])) {
|
|
30
|
+
return args[1].trim();
|
|
31
|
+
}
|
|
32
|
+
throw usageError("Update usage: yui update [--version <exact-version>]");
|
|
33
|
+
}
|
|
23
34
|
/** Render an {@link UpdateResult} as concise, CLI-style text. */
|
|
24
35
|
export function renderUpdateResult(result) {
|
|
25
36
|
const rendered = (() => {
|
|
@@ -33,14 +44,18 @@ export function renderUpdateResult(result) {
|
|
|
33
44
|
case "aborted":
|
|
34
45
|
return [
|
|
35
46
|
`Update aborted during ${result.phase}: ${result.message}`,
|
|
36
|
-
result.
|
|
37
|
-
||
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
result.controllerOwnershipUnknown === true || result.controllerRestore?.outcome === "unknown"
|
|
48
|
+
|| result.controllerReconciliation?.attempts.some(attempt => attempt.outcome === "unknown")
|
|
49
|
+
|| result.controllerReconciliation?.observationError !== undefined
|
|
50
|
+
? "Controller resource effects are uncertain; do not assume the Home is quiesced or usable."
|
|
51
|
+
: result.phase === "migrate-storage"
|
|
52
|
+
|| (result.phase === "post-verify" && result.recoverable)
|
|
53
|
+
? "The target binary is installed; the Home remains quiesced pending successful verification."
|
|
54
|
+
: result.recoverable
|
|
55
|
+
? "The current install and Home remain usable."
|
|
56
|
+
: result.phase === "activate-binary"
|
|
57
|
+
? "The Home was unchanged, but binary health is unknown; do not assume the current install is usable."
|
|
58
|
+
: "Manual recovery is required (see below).",
|
|
44
59
|
...(result.backupPath === undefined
|
|
45
60
|
? []
|
|
46
61
|
: [`Storage backup: ${result.backupPath}`]),
|
|
@@ -48,7 +63,14 @@ export function renderUpdateResult(result) {
|
|
|
48
63
|
].join("\n");
|
|
49
64
|
}
|
|
50
65
|
})();
|
|
51
|
-
return
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
return [
|
|
67
|
+
rendered,
|
|
68
|
+
...(result.controllerReconciliation === undefined ? [] : [
|
|
69
|
+
`Controller reconciliation: ${JSON.stringify(result.controllerReconciliation)}`
|
|
70
|
+
]),
|
|
71
|
+
...(result.controllerRestore === undefined ? [] : [
|
|
72
|
+
`Controller restore: ${JSON.stringify(result.controllerRestore)}`
|
|
73
|
+
]),
|
|
74
|
+
...(result.cleanupWarning === undefined ? [] : [`Warning: ${result.cleanupWarning}`])
|
|
75
|
+
].join("\n");
|
|
54
76
|
}
|