@zq-silk/yui 0.15.9 → 0.15.12
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 +8 -4
- package/ARCHITECTURE.zh-CN.md +5 -2
- package/README.md +13 -5
- package/dist/agent/launchEnvironment.js +7 -0
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/cli/commandCatalog.js +64 -16
- package/dist/cli/interactionPolicy.js +7 -3
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/updateOrchestrator.js +24 -1
- package/dist/cli/updatePorts.js +7 -3
- package/dist/cli/upgradeCommand.js +42 -2
- package/dist/cli.js +381 -107
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +339 -4
- package/dist/commands/projectCommands.js +50 -22
- package/dist/commands/releaseCommands.js +18 -0
- package/dist/commands/taskActor.js +25 -0
- package/dist/commands/taskCommands.js +586 -96
- package/dist/commands/taskIntegrationCommands.js +19 -39
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskOverviewCommand.js +4 -3
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +11 -2
- package/dist/commands/taskPublicationVerifyCommand.js +23 -39
- package/dist/commands/taskRemoteDeliveryCommand.js +22 -11
- package/dist/commands/taskRoleRuntimeStatus.js +35 -0
- package/dist/context/runContextPack.js +3 -0
- package/dist/context/taskCatalog.js +187 -0
- package/dist/context/taskContext.js +55 -6
- package/dist/controller/agentHostObservation.js +155 -0
- package/dist/controller/clientRuntime.js +17 -2
- package/dist/controller/controller.js +14 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +519 -25
- package/dist/controller/globalInputDelivery.js +132 -0
- package/dist/controller/jobControl.js +6 -2
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/resourceInventory.js +14 -4
- package/dist/controller/resourceInventoryLinux.js +2 -6
- package/dist/controller/runtime.js +117 -7
- package/dist/controller/runtimeEventInbox.js +32 -3
- package/dist/controller/runtimeEventProcessor.js +26 -6
- package/dist/controller/runtimeHookRunFence.js +75 -19
- package/dist/controller/structuredProviderObservation.js +133 -70
- package/dist/coordination/workMailboxQueue.js +5 -0
- package/dist/execution/workItemExecutionProjection.js +1 -1
- package/dist/executor/agentExecutor.js +64 -4
- package/dist/executor/executorRegistry.js +3 -0
- package/dist/executor/fileRoleLaunchPlanner.js +78 -118
- package/dist/integration/deliveryObligation.js +2 -1
- package/dist/integration/gitIntegrationService.js +329 -386
- package/dist/integration/integrationAttempt.js +30 -4
- package/dist/integration/integrationQueueService.js +7 -7
- package/dist/integration/integrationSourceApplication.js +323 -0
- package/dist/lifecycle/exactRunTerminalization.js +4 -1
- package/dist/message/globalInterrupt.js +33 -0
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/message/inputControlResolution.js +106 -0
- package/dist/message/message.js +367 -0
- package/dist/message/messageContinuation.js +126 -3
- package/dist/message/taskInterrupt.js +34 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/observability/orchestrationMetrics.js +1 -1
- package/dist/release/releaseHandover.js +22 -0
- package/dist/release/releaseWorkflowPorts.js +15 -7
- package/dist/repository/gitWorkspace.js +430 -107
- package/dist/repository/projectMaintenanceLock.js +75 -18
- package/dist/repository/taskWorkspaceCoordinator.js +182 -101
- package/dist/repository/taskWorkspacePreparer.js +205 -72
- package/dist/repository/workItemCandidateSnapshot.js +34 -0
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/resources/resourceDiscovery.js +3 -2
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentHost.js +179 -82
- package/dist/runtime/agentHostCompatibility.js +127 -0
- package/dist/runtime/agentHostProtocol.js +53 -0
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/executionEnvironment.js +0 -19
- package/dist/runtime/launchBroker.js +6 -0
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +6 -2
- package/dist/runtime/taskRuntimeIsolation.js +30 -6
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +5 -3
- package/dist/scheduler/activeRoleRunDelivery.js +12 -0
- package/dist/scheduler/leaderWakeupProcessor.js +5 -0
- package/dist/scheduler/operatorEvent.js +4 -0
- package/dist/scheduler/taskExecutionProjection.js +38 -6
- package/dist/scheduler/taskObservabilityProjection.js +6 -44
- package/dist/scheduler/wakeReason.js +7 -1
- package/dist/scheduler/wakeupQueue.js +2 -0
- package/dist/setup/setupCommand.js +26 -8
- package/dist/storage/homeLayout.js +130 -0
- package/dist/storage/migrations/collapseWorktreeLayout.js +963 -0
- package/dist/storage/migrations/integrationContinuation.js +104 -0
- package/dist/storage/migrations/unifyHomeLayout.js +925 -0
- package/dist/storage/sqliteSchema.js +167 -4
- package/dist/storage/sqliteStore.js +57 -1
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +2 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +95 -2
- package/dist/task/archiveDiagnostics.js +129 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/nextAction.js +44 -11
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/web/assets/client/app.js +147 -17
- package/dist/web/assets/client/components.js +56 -13
- package/dist/web/assets/client/i18n.js +78 -4
- package/dist/web/assets/client/taskSurface.js +108 -1
- package/dist/web/assets/client/view.js +39 -8
- package/dist/web/assets/shell.js +29 -0
- package/dist/web/assets/styles/layout.js +8 -1
- package/dist/web/assets/styles/widgets.js +12 -0
- package/dist/web/webServer.js +131 -4
- package/dist/web/webSnapshot.js +16 -6
- package/dist/web/webTaskSurface.js +222 -5
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +111 -35
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +30 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +23 -3
- package/docs/managed-turn-and-session-runtime.md +47 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +40 -0
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- package/docs/release-workflow.md +39 -0
- package/docs/release-workflow.zh-CN.md +29 -0
- package/docs/sqlite-control-plane-design.md +223 -1
- package/docs/task-delivery.md +133 -13
- package/docs/task-delivery.zh-CN.md +99 -10
- package/docs/task-discovery.md +102 -0
- package/docs/task-discovery.zh-CN.md +86 -0
- package/docs/testing/verification-levels.md +40 -0
- package/docs/testing/verification-levels.zh-CN.md +23 -0
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/references/execution.md +154 -51
- package/skills/yui-leader/references/integration.md +52 -2
- package/skills/yui-operator/SKILL.md +19 -3
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +42 -0
- package/skills/yui-runtime/references/publication.md +42 -0
- package/skills/yui-runtime/references/recovery.md +24 -0
package/ARCHITECTURE.md
CHANGED
|
@@ -169,8 +169,9 @@ Leader management is limited to its Task; executable code still requires exact
|
|
|
169
169
|
Operator-issued grants. Plugin validation is not a security certification.
|
|
170
170
|
|
|
171
171
|
CLI contributions and controlled Web panels are projections of the Registry.
|
|
172
|
-
Web is loopback-only and Controller-owned
|
|
173
|
-
|
|
172
|
+
Web is loopback-only and Controller-owned. Its authenticated local-user controls
|
|
173
|
+
share CLI domain operations; query panels remain read-only and cannot borrow
|
|
174
|
+
that user authority for mutations or plugin management. See [Plugin SDK](docs/plugin-sdk.md) and
|
|
174
175
|
[capabilities and resources](docs/architecture/capabilities-and-resources.md).
|
|
175
176
|
|
|
176
177
|
## Persistence, completion and operation
|
|
@@ -181,8 +182,11 @@ Homes; malformed state is diagnosed, not automatically repaired.
|
|
|
181
182
|
|
|
182
183
|
Completion freezes the delivery result and checks applicable acceptance,
|
|
183
184
|
integration and review contracts. It is distinct from publication, verified
|
|
184
|
-
remote merge, physical quiescence and archive.
|
|
185
|
-
and clean removable managed resources
|
|
185
|
+
remote merge, physical quiescence and archive. Ordinary archive requires settled
|
|
186
|
+
work and clean removable managed resources. Explicitly authorized force archive
|
|
187
|
+
can retain unresolved evidence and unsafe resources without claiming delivery or
|
|
188
|
+
quiescence. Both preserve Task history and cannot reopen.
|
|
189
|
+
See [Task delivery and archive](docs/task-delivery.md#archive).
|
|
186
190
|
|
|
187
191
|
Runtime health and cost are observations, not semantic verdicts. The Agent reads
|
|
188
192
|
exact faults and current intent to choose retry, repair or abandonment. Yui does
|
package/ARCHITECTURE.zh-CN.md
CHANGED
|
@@ -132,7 +132,8 @@ Registry 暴露 context、消息、artifact、环境、插件以及部分 Task/J
|
|
|
132
132
|
由 Operator 签发的 grant。插件验证不是安全认证。
|
|
133
133
|
|
|
134
134
|
CLI 贡献和受控的 Web 面板是 Registry 的投影。Web 仅本地回环、由 Controller 拥有;浏览器
|
|
135
|
-
|
|
135
|
+
凭据认证本地用户控制,这些控制复用 CLI 领域操作;查询面板仍只读,不能借用该用户权限
|
|
136
|
+
执行修改或插件管理。参见[插件 SDK](docs/plugin-sdk.zh-CN.md)和
|
|
136
137
|
[能力与资源](docs/architecture/capabilities-and-resources.zh-CN.md)。
|
|
137
138
|
|
|
138
139
|
## 持久化、完成与运维
|
|
@@ -141,7 +142,9 @@ Home 有一条只追加的存储迁移链。普通运行时只接受当前记录
|
|
|
141
142
|
有效 Home;畸形状态被诊断,而不是自动修复。
|
|
142
143
|
|
|
143
144
|
完成会冻结交付结果,并检查适用的验收、集成和审查合同。它区别于发布、已验证的远程合并、
|
|
144
|
-
|
|
145
|
+
物理静止和归档。普通归档要求工作已了结、受管资源干净可移除。明确授权的 force 归档
|
|
146
|
+
可以保留未解决证据与不安全资源,但不宣称交付或物理静止。两者都保留 Task 历史且不可
|
|
147
|
+
重开。参见[Task 交付与归档](docs/task-delivery.zh-CN.md#归档)。
|
|
145
148
|
|
|
146
149
|
运行时健康度和成本是观察,而不是语义判定。Agent 读取精确的故障和当前意图,以选择重试、
|
|
147
150
|
修复或放弃。Yui 不会自动创建救援 Worker 或选择另一个模型。
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ not from terminal windows you juggle or details you have to remember.
|
|
|
30
30
|
- **Bring your own Agent** — Codex CLI, Claude Code CLI and ACP peers run behind
|
|
31
31
|
one boundary and stay replaceable without losing the Task.
|
|
32
32
|
- **Local-first and private** — everything runs on your machine for one trusted
|
|
33
|
-
user;
|
|
33
|
+
user; Web is loopback-only, with read-only views and authenticated user controls.
|
|
34
34
|
- **Isolated by default** — repository work happens in managed Git worktrees;
|
|
35
35
|
the stable checkout stays read-only.
|
|
36
36
|
|
|
@@ -137,7 +137,11 @@ when necessary. A failed process does not erase the Task, and an uncertain
|
|
|
137
137
|
submission is not silently repeated.
|
|
138
138
|
|
|
139
139
|
For a visual overview, run `yui web` in another terminal. The local Web view
|
|
140
|
-
shows the same tasks and pending questions
|
|
140
|
+
shows the same tasks and pending questions and lets you send messages, answer
|
|
141
|
+
questions and explicitly queue, steer or interrupt Task input. These authenticated
|
|
142
|
+
Task controls use the same operations as the CLI; Web is not a separate task system.
|
|
143
|
+
See [Web permissions](docs/architecture/capabilities-and-resources.md#cli-and-web)
|
|
144
|
+
and [input timing](docs/managed-turn-and-session-runtime.md#input-timing-queue-steer-and-interrupt).
|
|
141
145
|
|
|
142
146
|
## Architecture
|
|
143
147
|
|
|
@@ -243,7 +247,7 @@ the Controller are what touch the store:
|
|
|
243
247
|
it moves work and records facts — it never judges an answer
|
|
244
248
|
|
|
245
249
|
Agents work in Projects: read-only checkout + isolated worktrees.
|
|
246
|
-
Web
|
|
250
|
+
Web (yui web): loopback views + authenticated user controls.
|
|
247
251
|
```
|
|
248
252
|
|
|
249
253
|
### Layered design
|
|
@@ -253,7 +257,7 @@ never a fixed workflow:
|
|
|
253
257
|
|
|
254
258
|
```text
|
|
255
259
|
Experience — how you interact
|
|
256
|
-
CLI (Operator) · Web (loopback,
|
|
260
|
+
CLI (Operator) · Web (loopback, authenticated) · native Agent sessions
|
|
257
261
|
collect input · show facts · confirm actions · invoke capabilities
|
|
258
262
|
▼
|
|
259
263
|
Intelligence — who decides
|
|
@@ -290,9 +294,13 @@ extra states:
|
|
|
290
294
|
WorkItem open ─▶ accepted ─▶ retired
|
|
291
295
|
|
|
292
296
|
Draft holds planning only; activation adopts a delivery workspace.
|
|
293
|
-
|
|
297
|
+
Ordinary archive needs settled work and clean worktrees; it cannot reopen.
|
|
294
298
|
```
|
|
295
299
|
|
|
300
|
+
Explicitly authorized force archive can retain unresolved evidence and unsafe
|
|
301
|
+
resources; it does not prove delivery or permission to delete them.
|
|
302
|
+
See the [archive contract](docs/task-delivery.md#archive).
|
|
303
|
+
|
|
296
304
|
## Design principles
|
|
297
305
|
|
|
298
306
|
### Agents make decisions; Yui makes work durable
|
|
@@ -122,6 +122,13 @@ export function operationalAgentEnvironment(adapterId, source) {
|
|
|
122
122
|
|| `${dirname(process.execPath)}:/usr/local/bin:/usr/bin:/bin`,
|
|
123
123
|
HOME: source.HOME || homedir(),
|
|
124
124
|
TERM: usableInteractiveTerminal(source.TERM),
|
|
125
|
+
// Base fallback only. Every managed Task launch has its isolation
|
|
126
|
+
// environment (TMPDIR = the Home-side runtime `roots.temporary`) assigned
|
|
127
|
+
// OVER this by the launch planner, so this branch is reached only by
|
|
128
|
+
// non-isolated global/ad-hoc launches with no managed runtime. Those run in
|
|
129
|
+
// the operator's own context, where their inherited TMPDIR (or the system
|
|
130
|
+
// temp) is the correct scratch — forcing it under control Home would
|
|
131
|
+
// misroute user scratch into Yui-managed storage.
|
|
125
132
|
TMPDIR: source.TMPDIR || tmpdir()
|
|
126
133
|
};
|
|
127
134
|
}
|
|
@@ -2,6 +2,7 @@ import { validateEffectiveLaunchSnapshot } from "../executor/effectiveLaunch.js"
|
|
|
2
2
|
import { validateTaskRecordReference } from "../task/taskRecordReference.js";
|
|
3
3
|
import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
|
|
4
4
|
import { createRunInput, createRunInputEnvelope, validateRunInput } from "../context/runInputContract.js";
|
|
5
|
+
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
5
6
|
import { boundedRunFailureDiagnostic, MAX_RUN_FAILURE_DIAGNOSTIC_BYTES, MAX_RUN_RESULT_OUTPUT_BYTES } from "../domain/agentResultTransport.js";
|
|
6
7
|
export { boundedRunFailureDiagnostic, MAX_RUN_FAILURE_DIAGNOSTIC_BYTES, MAX_RUN_RESULT_OUTPUT_BYTES, transportAgentResult } from "../domain/agentResultTransport.js";
|
|
7
8
|
/** Record lifecycle and observed delivery are separate read-only facts. */
|
|
@@ -17,6 +18,8 @@ export function runExecutionObservation(run, binding, events = []) {
|
|
|
17
18
|
return {
|
|
18
19
|
recordStatus: run.status,
|
|
19
20
|
delivery,
|
|
21
|
+
retry: binding?.retry?.previousRunId === run.id || binding?.retry?.successorRunId === run.id
|
|
22
|
+
? providerRetryProjection(binding) : null,
|
|
20
23
|
...(current === undefined ? {} : {
|
|
21
24
|
attemptId: current.attemptId, observedAt: current.updatedAt,
|
|
22
25
|
...(current.nativeTurnId === undefined ? {} : { nativeTurnId: current.nativeTurnId })
|
|
@@ -215,6 +215,11 @@ const globalSessionChildren = [
|
|
|
215
215
|
summary: "Reconcile durable Session owners with native sessions.",
|
|
216
216
|
usage: "yui session reconcile [--report] [--cleanup]",
|
|
217
217
|
options: ["--report", "--cleanup"]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: "retry",
|
|
221
|
+
summary: "Inspect or control bounded Provider recovery for this Global Session.",
|
|
222
|
+
usage: "yui session retry <role> [show|cancel|disable|enable]"
|
|
218
223
|
}
|
|
219
224
|
];
|
|
220
225
|
const profileChildren = [
|
|
@@ -373,9 +378,9 @@ const taskChildren = [
|
|
|
373
378
|
},
|
|
374
379
|
{
|
|
375
380
|
name: "list",
|
|
376
|
-
summary: "List
|
|
377
|
-
usage: "yui task list [--all] [--verbose]",
|
|
378
|
-
options: ["--all", "--verbose"]
|
|
381
|
+
summary: "List Task overviews or a bounded discovery catalog.",
|
|
382
|
+
usage: "yui task list [--all] [--verbose] | --view compact [--all] [--status <status>] [--project <id>] [--search <text>] [--attention <category>] [--limit <1..100>] [--cursor <cursor>]",
|
|
383
|
+
options: ["--all", "--verbose", "--view", "--status", "--project", "--search", "--attention", "--limit", "--cursor"]
|
|
379
384
|
},
|
|
380
385
|
{ name: "show", summary: "Show a Task.", usage: "yui task show <id>" },
|
|
381
386
|
{
|
|
@@ -406,10 +411,16 @@ const taskChildren = [
|
|
|
406
411
|
usage: "yui task remote-delivery <task> [--json]",
|
|
407
412
|
options: ["--json"]
|
|
408
413
|
},
|
|
414
|
+
{
|
|
415
|
+
name: "archive-preflight",
|
|
416
|
+
summary: "Inspect current archive and exact-owner cleanup blockers without changing state or authorizing removal.",
|
|
417
|
+
usage: "yui task archive-preflight <id> (--integrated|--abandon) [--force] [--json]",
|
|
418
|
+
options: ["--integrated", "--abandon", "--force", "--json"]
|
|
419
|
+
},
|
|
409
420
|
{
|
|
410
421
|
name: "archive",
|
|
411
|
-
summary: "Archive a Task
|
|
412
|
-
usage: "yui task archive <id> (--integrated [--force]
|
|
422
|
+
summary: "Archive a terminal Task; explicit --force commits despite delivery/cleanup warnings, retaining unsafe resources.",
|
|
423
|
+
usage: "yui task archive <id> (--integrated|--abandon) [--force]",
|
|
413
424
|
options: ["--integrated", "--abandon", "--force"]
|
|
414
425
|
},
|
|
415
426
|
{ name: "reconcile", summary: "Run one immediate Controller reconciliation.", usage: "yui task reconcile <id>" },
|
|
@@ -435,7 +446,7 @@ const taskChildren = [
|
|
|
435
446
|
{
|
|
436
447
|
name: "message",
|
|
437
448
|
summary: "Manage durable Task messages.",
|
|
438
|
-
sections: [{ id: "manage", title: "Commands", entries: ["send", "handoff", "list", "show", "update", "retire"] }],
|
|
449
|
+
sections: [{ id: "manage", title: "Commands", entries: ["send", "queue", "steer", "handoff", "list", "show", "update", "retire"] }],
|
|
439
450
|
children: [
|
|
440
451
|
{
|
|
441
452
|
name: "handoff",
|
|
@@ -454,6 +465,20 @@ const taskChildren = [
|
|
|
454
465
|
},
|
|
455
466
|
fileOptions: ["--body-file"]
|
|
456
467
|
},
|
|
468
|
+
{
|
|
469
|
+
name: "queue",
|
|
470
|
+
summary: "Queue an input for delivery at the recipient's next legal opportunity (idempotent by request id).",
|
|
471
|
+
usage: "yui task message queue <id> (<body>|--body-file <path|->) --request-id <id> [--to <role> --work-item <id>|--review-round <id>]",
|
|
472
|
+
options: ["--body-file", "--request-id", "--to", "--work-item", "--review-round"],
|
|
473
|
+
fileOptions: ["--body-file"]
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: "steer",
|
|
477
|
+
summary: "Steer only a Role's exact current native Turn; saved and reported without fallback when unsupported.",
|
|
478
|
+
usage: "yui task message steer <id> (<body>|--body-file <path|->) --request-id <id> --expected-target <turn> --to <role> [--work-item <id>|--review-round <id>]",
|
|
479
|
+
options: ["--body-file", "--request-id", "--expected-target", "--to", "--work-item", "--review-round"],
|
|
480
|
+
fileOptions: ["--body-file"]
|
|
481
|
+
},
|
|
457
482
|
{
|
|
458
483
|
name: "list",
|
|
459
484
|
summary: "List Task messages.",
|
|
@@ -592,17 +617,29 @@ const taskChildren = [
|
|
|
592
617
|
{
|
|
593
618
|
name: "publication",
|
|
594
619
|
summary: "Create or update external PR/MR publication evidence for a Task.",
|
|
595
|
-
sections: [{ id: "manage", title: "Commands", entries: ["upsert", "verify", "list", "show"] }],
|
|
620
|
+
sections: [{ id: "manage", title: "Commands", entries: ["upsert", "diff", "adopt", "verify", "list", "show"] }],
|
|
596
621
|
children: [
|
|
597
622
|
{
|
|
598
623
|
name: "upsert",
|
|
599
624
|
summary: "Create or immutably update an external PR/MR and its publication state.",
|
|
600
|
-
usage: "yui task publication upsert <task> --project <project> --provider <github|gitlab> --repository <owner/name> --kind <pull-request|merge-request> --id <external-id> [--url <url>] [--title <text>] [--source-branch <branch>] [--target-branch <branch>] [--local-commit <sha>] [--remote-commit <sha>] [--state <open|merged|closed>] [--reported|--verified] [--evidence <text>] [--merged-at <iso-timestamp>]",
|
|
601
|
-
options: ["--project", "--provider", "--repository", "--kind", "--id", "--url", "--title", "--source-branch", "--target-branch", "--local-commit", "--remote-commit", "--state", "--reported", "--verified", "--evidence", "--merged-at"]
|
|
625
|
+
usage: "yui task publication upsert <task> --project <project> --provider <github|gitlab> --repository <owner/name> --kind <pull-request|merge-request> --id <external-id> [--url <url>] [--title <text>] [--source-branch <branch>] [--target-branch <branch>] [--local-commit <sha>] [--head-commit <sha>] [--remote-commit <sha>] [--state <open|merged|closed>] [--reported|--verified] [--evidence <text>] [--merged-at <iso-timestamp>]",
|
|
626
|
+
options: ["--project", "--provider", "--repository", "--kind", "--id", "--url", "--title", "--source-branch", "--target-branch", "--local-commit", "--head-commit", "--remote-commit", "--state", "--reported", "--verified", "--evidence", "--merged-at"]
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
name: "diff",
|
|
630
|
+
summary: "Read a completed Task's fixed acceptance-to-publication candidate diff using local Git only.",
|
|
631
|
+
usage: "yui task publication diff <task>/<publication> [--integration <id>]",
|
|
632
|
+
options: ["--integration"]
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
name: "adopt",
|
|
636
|
+
summary: "Explicitly accept the reviewed publication candidate as covering the original completion.",
|
|
637
|
+
usage: "yui task publication adopt <task>/<publication> --reviewed-diff <sha256> --acceptance <text> [--integration <id>]",
|
|
638
|
+
options: ["--reviewed-diff", "--acceptance", "--integration"]
|
|
602
639
|
},
|
|
603
640
|
{
|
|
604
641
|
name: "verify",
|
|
605
|
-
summary: "
|
|
642
|
+
summary: "Observe the current PR/MR head and merge through its provider; record verification separately from Task coverage.",
|
|
606
643
|
usage: "yui task publication verify (<task>/<publication-id> | <task> <publication-id>)"
|
|
607
644
|
},
|
|
608
645
|
{
|
|
@@ -622,7 +659,7 @@ const taskChildren = [
|
|
|
622
659
|
summary: "Manage Roles within a Task.",
|
|
623
660
|
sections: [{ id: "manage", title: "Commands", entries: [
|
|
624
661
|
"add", "list", "status", "show", "update", "remove", "bind", "unbind",
|
|
625
|
-
"session", "view", "takeover", "release"
|
|
662
|
+
"session", "interrupt", "view", "takeover", "release"
|
|
626
663
|
] }],
|
|
627
664
|
children: [
|
|
628
665
|
{
|
|
@@ -654,8 +691,13 @@ const taskChildren = [
|
|
|
654
691
|
name: "session",
|
|
655
692
|
summary: "Inspect, stop or explicitly select a new Task Role Session.",
|
|
656
693
|
executable: true,
|
|
657
|
-
sections: [{ id: "manage", title: "Commands", entries: ["inspect", "stop", "new"] }],
|
|
694
|
+
sections: [{ id: "manage", title: "Commands", entries: ["inspect", "retry", "stop", "new"] }],
|
|
658
695
|
children: [
|
|
696
|
+
{
|
|
697
|
+
name: "retry",
|
|
698
|
+
summary: "Inspect or control Provider recovery without stopping an admitted Turn.",
|
|
699
|
+
usage: "yui task role session retry <task> <role> [show|cancel|disable|enable]"
|
|
700
|
+
},
|
|
659
701
|
{
|
|
660
702
|
name: "inspect",
|
|
661
703
|
summary: "Read the current Session, Host process, and AgentRun facts.",
|
|
@@ -680,6 +722,12 @@ const taskChildren = [
|
|
|
680
722
|
summary: "Attach read-only to an independent Provider presentation surface.",
|
|
681
723
|
usage: "yui task role view <task> <role>"
|
|
682
724
|
},
|
|
725
|
+
{
|
|
726
|
+
name: "interrupt",
|
|
727
|
+
summary: "Interrupt a Role's exact current native Turn via native cancel; optionally deliver a saved Message once after a proven terminal.",
|
|
728
|
+
usage: "yui task role interrupt <task> <role> --expected-target <turn> [--then-message <task/message>] [--request-id <id>]",
|
|
729
|
+
options: ["--expected-target", "--then-message", "--request-id"]
|
|
730
|
+
},
|
|
683
731
|
{
|
|
684
732
|
name: "takeover",
|
|
685
733
|
summary: "Enter the PTY input gateway for an independent Provider process.",
|
|
@@ -706,7 +754,7 @@ const taskChildren = [
|
|
|
706
754
|
children: [
|
|
707
755
|
{
|
|
708
756
|
name: "create",
|
|
709
|
-
summary: "Create a work
|
|
757
|
+
summary: "Create a separately managed result; a coherent Task may use zero WorkItems. Omit --role for Leader-direct execution; --role (including leader) selects a managed AgentRun executor. Isolate writable direct work before editing, then submit, integrate and accept its Candidate.",
|
|
710
758
|
usage: "yui task work create <task> <title> [--project <project> ...] [--base-ref <project>=<ref> ...] [--objective <text>] [--accept <criterion> ...] [--after <work> ...] [--role <name>]",
|
|
711
759
|
options: ["--project", "--base-ref", "--objective", "--accept", "--after", "--role"]
|
|
712
760
|
},
|
|
@@ -922,7 +970,7 @@ const taskChildren = [
|
|
|
922
970
|
},
|
|
923
971
|
{
|
|
924
972
|
name: "abort",
|
|
925
|
-
summary: "Abandon
|
|
973
|
+
summary: "Abandon an unadvanced Integration while preserving evidence; reconcile an already-applied target.",
|
|
926
974
|
usage: "yui task integration abort <task>/<integration> --reason <text>",
|
|
927
975
|
options: ["--reason"]
|
|
928
976
|
},
|
|
@@ -1461,7 +1509,7 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1461
1509
|
sections: [
|
|
1462
1510
|
{ id: "global", title: "Global Role sessions", entries: ["context", "enter", "record", "replace"] },
|
|
1463
1511
|
{ id: "maintenance", title: "Maintenance", entries: ["stop"] },
|
|
1464
|
-
{ id: "recovery", title: "Recovery", entries: ["reconcile"] }
|
|
1512
|
+
{ id: "recovery", title: "Recovery", entries: ["reconcile", "retry"] }
|
|
1465
1513
|
],
|
|
1466
1514
|
children: globalSessionChildren
|
|
1467
1515
|
},
|
|
@@ -1469,7 +1517,7 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1469
1517
|
name: "task",
|
|
1470
1518
|
summary: "Manage Tasks, WorkItems, AgentRuns, and integration.",
|
|
1471
1519
|
sections: [
|
|
1472
|
-
{ id: "lifecycle", title: "Lifecycle", entries: ["create", "project", "base", "update", "activate", "activation", "execution", "complete", "cancel", "reopen", "retire", "list", "show", "context", "next-action", "remote-delivery", "archive", "replace", "reconcile", "upstream", "artifact"] },
|
|
1520
|
+
{ id: "lifecycle", title: "Lifecycle", entries: ["create", "project", "base", "update", "activate", "activation", "execution", "complete", "cancel", "reopen", "retire", "list", "show", "context", "next-action", "remote-delivery", "archive-preflight", "archive", "replace", "reconcile", "upstream", "artifact"] },
|
|
1473
1521
|
{ id: "collaboration", title: "Collaboration", entries: ["message", "input", "grant", "workflow", "publication", "work", "run", "review", "integration", "role", "overlap", "change-set"] },
|
|
1474
1522
|
{ id: "knowledge", title: "Task Knowledge", entries: ["brief", "decision", "milestone", "event", "continuation", "wake"] }
|
|
1475
1523
|
],
|
|
@@ -229,6 +229,10 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
229
229
|
...taskTarget("remote-delivery"),
|
|
230
230
|
trailingOptions: { "--json": "flag" }
|
|
231
231
|
},
|
|
232
|
+
{
|
|
233
|
+
...taskTarget("archive-preflight"),
|
|
234
|
+
trailingOptions: { "--integrated": "flag", "--abandon": "flag", "--force": "flag", "--json": "flag" }
|
|
235
|
+
},
|
|
232
236
|
{
|
|
233
237
|
...taskTarget("archive", 2, ["completed", "cancelled"]),
|
|
234
238
|
trailingOptions: { "--integrated": "flag", "--abandon": "flag", "--force": "flag" },
|
|
@@ -574,9 +578,9 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
574
578
|
entity: "integration-attempt",
|
|
575
579
|
provider: "integration-attempts",
|
|
576
580
|
actionTarget: true,
|
|
577
|
-
...(command === "continue" ? { statuses: ["blocked", "validating"] } : {}),
|
|
578
|
-
...(command === "resolve" ? { statuses: ["blocked"] } : {}),
|
|
579
|
-
...(command === "abort" ? { statuses: ["running", "blocked"] } : {})
|
|
581
|
+
...(command === "continue" ? { statuses: ["running", "conflicted", "blocked", "validating"] } : {}),
|
|
582
|
+
...(command === "resolve" ? { statuses: ["blocked", "conflicted"] } : {}),
|
|
583
|
+
...(command === "abort" ? { statuses: ["running", "conflicted", "blocked", "validating"] } : {})
|
|
580
584
|
}],
|
|
581
585
|
...(command === "resolve"
|
|
582
586
|
? { trailingOptions: { "--option": "value", "--rationale": "value" } }
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
export function taskDiagnosticTarget(args) {
|
|
4
4
|
if (args[0] !== "task")
|
|
5
5
|
return undefined;
|
|
6
|
-
if (["show", "context", "next-action"].includes(args[1] ?? "")) {
|
|
6
|
+
if (["show", "context", "next-action", "archive-preflight"].includes(args[1] ?? "")) {
|
|
7
7
|
return args[1] === "context" && ["inspect", "delta"].includes(args[2] ?? "")
|
|
8
8
|
? args[3] : args[2];
|
|
9
9
|
}
|
|
@@ -101,7 +101,30 @@ function runStagedUpdate(ports, staged, home) {
|
|
|
101
101
|
const captured = captureControllerLifecycle(ports, staged.version, home);
|
|
102
102
|
if ("outcome" in captured)
|
|
103
103
|
return captured;
|
|
104
|
-
|
|
104
|
+
// An older Controller can finish a launch between preflight and drain.
|
|
105
|
+
// Recheck after its exact stop, before changing the install or storage.
|
|
106
|
+
let fencedPreflight;
|
|
107
|
+
try {
|
|
108
|
+
fencedPreflight = ports.preflight(staged, home);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return restoreControllerOrReport(ports, home, captured.lifecycle, {
|
|
112
|
+
outcome: "aborted", phase: "preflight",
|
|
113
|
+
message: `Quiesced compatibility preflight failed: ${messageOf(error)}`,
|
|
114
|
+
action: "The install and storage are unchanged; inspect the compatibility failure before retrying.",
|
|
115
|
+
recoverable: true, version: staged.version
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (fencedPreflight.status === "blocked") {
|
|
119
|
+
return restoreControllerOrReport(ports, home, captured.lifecycle, {
|
|
120
|
+
outcome: "aborted", phase: "preflight",
|
|
121
|
+
message: fencedPreflight.message, action: fencedPreflight.action,
|
|
122
|
+
recoverable: true, version: staged.version,
|
|
123
|
+
...(fencedPreflight.blockers === undefined ? {} : { blockers: fencedPreflight.blockers }),
|
|
124
|
+
...(fencedPreflight.sceneUnchanged === true ? { sceneUnchanged: true } : {})
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return activateAndVerify(ports, staged, home, captured.lifecycle, fencedPreflight);
|
|
105
128
|
}
|
|
106
129
|
finally {
|
|
107
130
|
releaseHandover?.();
|
package/dist/cli/updatePorts.js
CHANGED
|
@@ -25,14 +25,15 @@
|
|
|
25
25
|
* mismatch fails closed.
|
|
26
26
|
*/
|
|
27
27
|
import { spawnSync } from "node:child_process";
|
|
28
|
-
import { accessSync, constants, existsSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
29
|
-
import { tmpdir } from "node:os";
|
|
28
|
+
import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
30
29
|
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
31
30
|
import { fileURLToPath } from "node:url";
|
|
32
31
|
import { runtimeError } from "../errors/cliError.js";
|
|
33
32
|
import { isConcreteVersion } from "../domain/validation.js";
|
|
34
33
|
import { STORAGE_DOCTOR_CHECK_NAMES } from "../doctor/doctor.js";
|
|
35
34
|
import { acquireHandoverLock } from "../release/runtimeRelease.js";
|
|
35
|
+
import { updateStagingRoot } from "../storage/homeLayout.js";
|
|
36
|
+
import { resolveYuiHome } from "../storage/taskStore.js";
|
|
36
37
|
const PACKAGE_NAME = "@zq-silk/yui";
|
|
37
38
|
const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
|
|
38
39
|
function resolveExecutable(command, environmentPath) {
|
|
@@ -64,7 +65,7 @@ function failedSpawnResult(message) {
|
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
/** Build the real ports. `spawn` is injectable so tests avoid real installs. */
|
|
67
|
-
export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
|
|
68
|
+
export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot = updateStagingRoot(resolveYuiHome(environment))) {
|
|
68
69
|
// The production adapter must not let a later PATH change select a
|
|
69
70
|
// different npm during staging, activation, or recovery. Test doubles are
|
|
70
71
|
// intentionally left untouched so deterministic tests can dispatch on the
|
|
@@ -105,6 +106,9 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
|
|
|
105
106
|
throw runtimeError(`Refusing to stage a non-concrete version (${String(version)}): only an exact `
|
|
106
107
|
+ "major.minor.patch version can be pinned for an update.");
|
|
107
108
|
}
|
|
109
|
+
// The Home staging parent may not exist yet on a Home that has never
|
|
110
|
+
// run an update; create it (private) before minting a throwaway prefix.
|
|
111
|
+
mkdirSync(stagingRoot, { recursive: true, mode: 0o700 });
|
|
108
112
|
const stagingPath = mkdtempSync(join(stagingRoot, "yui-update-stage-"));
|
|
109
113
|
let ownsStaging = true;
|
|
110
114
|
try {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** `yui upgrade` plans or applies the supported linear storage migration chain. */
|
|
2
2
|
import { runStorageUpgrade } from "../storage/upgrade/upgradeOrchestrator.js";
|
|
3
|
-
import { ensureFileTaskController, stopFileTaskController } from "../controller/clientRuntime.js";
|
|
3
|
+
import { ensureFileTaskController, ensureFileTaskControllerIdentity, stopFileTaskController } from "../controller/clientRuntime.js";
|
|
4
|
+
import { callController, ControllerClientError } from "../core/controllerClient.js";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
4
6
|
import { runtimeError, usageError } from "../errors/cliError.js";
|
|
5
7
|
import { acquireHandoverLock, isForeignHandoverLockHeld, isHandoverLockHeld } from "../release/runtimeRelease.js";
|
|
6
8
|
/**
|
|
@@ -43,12 +45,29 @@ async function runInteractiveUpgrade(home, environment) {
|
|
|
43
45
|
const handover = acquireHandoverLock(home);
|
|
44
46
|
let controllerWasRunning = false;
|
|
45
47
|
try {
|
|
48
|
+
const previous = await captureUpgradeController(home);
|
|
46
49
|
const stopped = await stopFileTaskController(home, {
|
|
47
50
|
environment,
|
|
48
|
-
handoverOwnerPid: process.pid
|
|
51
|
+
handoverOwnerPid: process.pid,
|
|
52
|
+
...(previous === undefined ? {} : { expectedPid: previous.pid })
|
|
49
53
|
});
|
|
50
54
|
controllerWasRunning = stopped.stopped;
|
|
51
55
|
const result = await runStorageUpgrade({ home, mode: "execute" });
|
|
56
|
+
if (controllerWasRunning && result.outcome === "blocked") {
|
|
57
|
+
if (previous === undefined) {
|
|
58
|
+
throw runtimeError("Upgrade was blocked without changing storage; the stopped Controller identity is unknown. Keep the Home quiesced.");
|
|
59
|
+
}
|
|
60
|
+
await ensureFileTaskControllerIdentity(home, previous.identity, {
|
|
61
|
+
environment, handoverOwnerPid: process.pid,
|
|
62
|
+
spawnController: (_home, launchEnvironment) => {
|
|
63
|
+
const child = spawn(previous.identity.executablePath, [...previous.identity.args], {
|
|
64
|
+
env: launchEnvironment, detached: true, stdio: "ignore"
|
|
65
|
+
});
|
|
66
|
+
child.unref();
|
|
67
|
+
return child.pid;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
52
71
|
if (controllerWasRunning
|
|
53
72
|
&& (result.outcome === "upgraded" || result.outcome === "already-current")) {
|
|
54
73
|
try {
|
|
@@ -70,6 +89,27 @@ async function runInteractiveUpgrade(home, environment) {
|
|
|
70
89
|
handover.release();
|
|
71
90
|
}
|
|
72
91
|
}
|
|
92
|
+
async function captureUpgradeController(home) {
|
|
93
|
+
let value;
|
|
94
|
+
try {
|
|
95
|
+
value = await callController(home, "controller.identity", {});
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof ControllerClientError && error.code === "CONTROLLER_NOT_RUNNING")
|
|
99
|
+
return undefined;
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
const identity = value;
|
|
103
|
+
if (!Number.isSafeInteger(identity.pid) || identity.pid < 1
|
|
104
|
+
|| typeof identity.executablePath !== "string" || !identity.executablePath
|
|
105
|
+
|| !Array.isArray(identity.args) || identity.args.some(arg => typeof arg !== "string")
|
|
106
|
+
|| typeof identity.version !== "string" || !identity.version) {
|
|
107
|
+
throw runtimeError("Cannot capture the exact Controller for a reversible upgrade preflight.");
|
|
108
|
+
}
|
|
109
|
+
return { pid: identity.pid, identity: {
|
|
110
|
+
executablePath: identity.executablePath, args: identity.args, version: identity.version
|
|
111
|
+
} };
|
|
112
|
+
}
|
|
73
113
|
async function runUpdateOwnedUpgrade(home, environment) {
|
|
74
114
|
const ownerText = environment.YUI_UPDATE_HANDOVER_OWNER_PID;
|
|
75
115
|
const ownerPid = ownerText === undefined ? Number.NaN : Number(ownerText);
|