@zq-silk/yui 0.8.1 → 0.8.3
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 +27 -28
- package/README.md +57 -46
- package/dist/cli/commandCatalog.js +57 -17
- package/dist/cli/interactionPolicy.js +4 -10
- package/dist/cli/invocationRouter.js +2 -1
- package/dist/cli.js +106 -27
- package/dist/commands/taskCommands.js +458 -77
- package/dist/commands/taskCompletionGate.js +152 -0
- package/dist/context/runContextPack.js +19 -4
- package/dist/context/sessionBootstrapManifest.js +1 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +389 -70
- package/dist/controller/resourceInventory.js +9 -5
- package/dist/controller/runtime.js +80 -7
- package/dist/controller/runtimeLaunchCoordinator.js +18 -78
- package/dist/controller/structuredProviderObservation.js +273 -0
- package/dist/executor/agentAdapter.js +40 -0
- package/dist/executor/agentExecutor.js +31 -7
- package/dist/executor/executorRegistry.js +11 -49
- package/dist/executor/fileRoleLaunchPlanner.js +115 -37
- package/dist/lifecycle/canonicalLifecycleEvent.js +5 -2
- package/dist/repository/gitWorkspace.js +7 -4
- package/dist/repository/taskBaseFreshness.js +4 -2
- package/dist/run/agentRun.js +4 -4
- package/dist/runtime/agentHost.js +767 -158
- package/dist/runtime/builtinAgentDrivers.js +1 -5
- package/dist/runtime/codexAppServerRuntime.js +67 -60
- package/dist/runtime/exactControlPlane.js +7 -2
- package/dist/runtime/index.js +6 -2
- package/dist/runtime/launchBroker.js +30 -8
- package/dist/runtime/providerAuthorityFence.js +24 -0
- package/dist/runtime/providerControl.js +63 -0
- package/dist/runtime/providerRecoveryDecision.js +55 -0
- package/dist/runtime/providerRuntimeIdentity.js +269 -19
- package/dist/runtime/runtimeBinding.js +20 -11
- package/dist/runtime/structuredProviderHost.js +476 -0
- package/dist/runtime/tmuxAdapters.js +143 -42
- package/dist/scheduler/activeRoleRunDelivery.js +206 -120
- package/dist/scheduler/leaderWakeupProcessor.js +141 -16
- package/dist/scheduler/wakeReason.js +1 -0
- package/dist/storage/migration/productionRegistry.js +111 -0
- package/dist/storage/sqliteStore.js +2 -0
- package/dist/storage/taskStore.js +3 -1
- package/dist/task/completionReadiness.js +43 -0
- package/dist/task/nextAction.js +6 -4
- package/dist/task/publicationReference.js +1 -0
- package/dist/tmux/tmuxManager.js +1 -1
- package/dist/workItem/workItem.js +12 -0
- package/dist/workspace/workItemChangeSetManager.js +2 -1
- package/i18n/README.zh-CN.md +11 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +8 -3
- package/skills/yui-runtime/SKILL.md +7 -2
package/ARCHITECTURE.md
CHANGED
|
@@ -201,34 +201,33 @@ context.
|
|
|
201
201
|
|
|
202
202
|
## Runtime ownership
|
|
203
203
|
|
|
204
|
-
tmux owns Agent
|
|
205
|
-
mailbox delivery, wakeups, Role liveness,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
is
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
the
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
bindings.
|
|
204
|
+
tmux owns the persistent Agent Host lifetime and observable output. The
|
|
205
|
+
Controller owns mailbox delivery, wakeups, Role liveness, recovery decisions,
|
|
206
|
+
and a durable single-writer authority epoch. The Agent Host owns one structured
|
|
207
|
+
Provider child process and mirrors its output to the pane. Managed prompts are
|
|
208
|
+
never terminal bytes: both Controller and human takeover input become fenced
|
|
209
|
+
Provider-native Turn requests.
|
|
210
|
+
|
|
211
|
+
Run, Conversation, Activation, and Turn identities are independent. A
|
|
212
|
+
Conversation can span Runs and Provider processes; one Activation identifies
|
|
213
|
+
one live process; one Turn identifies one pre-recorded input attempt. A live
|
|
214
|
+
Activation is retained when a later Run reuses the Conversation. Process exit
|
|
215
|
+
ends that Activation, and a resumed process receives a new Activation and a
|
|
216
|
+
higher authority epoch.
|
|
217
|
+
|
|
218
|
+
Task observation uses `task role view`. Explicit takeover requires a live
|
|
219
|
+
managed Run, atomically transfers authority to a human holder, synchronizes the
|
|
220
|
+
same fence to the Host, and only then exposes the PTY input gateway. Detach
|
|
221
|
+
releases authority; `task role release` is an idempotent repair path even after
|
|
222
|
+
the Run has ended. Global interactive entry remains a native session-lifecycle
|
|
223
|
+
operation outside this managed Provider contract.
|
|
224
|
+
|
|
225
|
+
Codex uses a persistent App Server JSON-RPC transport. Claude uses a persistent
|
|
226
|
+
stream-json transport with exact user-message replay acknowledgement. In both
|
|
227
|
+
cases, Yui records Turn intent before writing, accepts only exact Provider
|
|
228
|
+
evidence, and maps an uncertain write to `delivery-unknown` without automatic
|
|
229
|
+
resubmission. Conversation replacement requires exact missing evidence and no
|
|
230
|
+
unsettled input, Turn, Activation, or writer authority.
|
|
232
231
|
|
|
233
232
|
Role desired revisions and Run/Session effective snapshots keep configuration
|
|
234
233
|
history explicit. Resume compares the complete effective snapshot and
|
package/README.md
CHANGED
|
@@ -659,55 +659,68 @@ yui task complete <task-id> --summary-file delivery.txt --refresh-remote
|
|
|
659
659
|
yui task reopen <task-id>
|
|
660
660
|
```
|
|
661
661
|
|
|
662
|
-
|
|
662
|
+
When a verified squash-merge Publication records a remote commit that is
|
|
663
|
+
ancestry-divergent from the unchanged, frozen Task-final Review head, a user or
|
|
664
|
+
global Operator may explicitly authorize completion against its identical Git
|
|
665
|
+
tree:
|
|
666
|
+
|
|
667
|
+
```sh
|
|
668
|
+
yui task complete <task-id> --summary-file delivery.txt \
|
|
669
|
+
--accept-published-tree <publication-id>
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
This does not weaken normal freshness checks. Yui requires the current,
|
|
673
|
+
unsuperseded Publication to be merged and verified, its local commit to equal
|
|
674
|
+
the reviewed physical Task head, its remote commit to be ancestry-divergent,
|
|
675
|
+
and both commits to resolve to the same exact tree. `--refresh-remote` fetches
|
|
676
|
+
the remote object graph before resolving that Publication commit. For a Task
|
|
677
|
+
governed by a durable exact final-review contract, the user/Operator command
|
|
678
|
+
persists the exact authorization tuple and wakes the Task Leader; only that
|
|
679
|
+
contract-capable Leader may consume it and complete the Task. Tasks without
|
|
680
|
+
that contract retain the one-step explicit completion path. The Task event
|
|
681
|
+
audit records the authorization and, on completion, the accepted Project,
|
|
682
|
+
Publication, ReviewRound, both commits, and tree.
|
|
683
|
+
|
|
684
|
+
Completed Tasks reject messages, dispatch, Provider authority changes, retry, and late yields until explicitly reopened, while retaining Task main for inspection or integration. Every isolated WorkItem worktree must be explicitly cleaned as integrated or abandoned before archive; that cleanup also removes its managed branch. Archive requires `--integrated` or `--abandon` to state the Task main outcome and is allowed only after Task main is clean. It removes managed worktrees but retains Task and WorkItem records. The Task main branch is retained as a recovery artifact instead of being silently deleted.
|
|
663
685
|
Task lifecycle completion/selection only suggests valid source states: Draft for activate, active for complete, and completed for reopen.
|
|
664
686
|
|
|
665
687
|
## Sessions and tmux
|
|
666
688
|
|
|
667
|
-
|
|
668
|
-
and
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
tmux fixes a pane's history capacity when that pane is created. Roles created
|
|
695
|
-
before this limit was configured keep their earlier capacity; Yui warns on
|
|
696
|
-
Terminal attach and in Web so the user can exit and re-enter that Role once to
|
|
697
|
-
create a 100,000-line pane while retaining the native Agent conversation.
|
|
698
|
-
|
|
699
|
-
Global interactive entry remains writable when no writer exists and
|
|
700
|
-
automatically downgrades to read-only when another writer is present; global
|
|
701
|
-
Web keeps one writer per tmux session. Task Web is always read-only. Task CLI
|
|
702
|
-
entry is read-only unless `--read-write` is requested, preventing observation
|
|
703
|
-
from changing Agent execution.
|
|
689
|
+
Managed Task Agents use the [hybrid Provider runtime](docs/provider-runtime.md).
|
|
690
|
+
The Controller and Agent Host send Provider-native structured requests; tmux
|
|
691
|
+
and PTY keep the Host alive, show output, and provide an explicit human input
|
|
692
|
+
gateway. Managed prompts are never delivered as terminal bytes.
|
|
693
|
+
|
|
694
|
+
Codex uses a persistent App Server JSON-RPC process. Claude Code uses a
|
|
695
|
+
persistent stream-json process with exact user-message replay acknowledgement.
|
|
696
|
+
Both follow the same Conversation, Activation, Turn, and authority-epoch
|
|
697
|
+
contract. A timeout or uncertain write becomes `delivery-unknown` and is never
|
|
698
|
+
automatically retried.
|
|
699
|
+
|
|
700
|
+
Task Role observation and takeover are explicit:
|
|
701
|
+
|
|
702
|
+
```sh
|
|
703
|
+
yui task role view <task-id> <role>
|
|
704
|
+
yui task role takeover <task-id> <role>
|
|
705
|
+
yui task role release <task-id> <role>
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
`view` is read-only. `takeover` transfers the durable writer authority to the
|
|
709
|
+
human before enabling the Host PTY gateway and requires a live managed Run;
|
|
710
|
+
detach returns it to the Controller. `release` remains available without a live
|
|
711
|
+
Run and idempotently repairs a stranded or partially synchronized takeover.
|
|
712
|
+
Authority cannot transfer while a Provider Turn or input delivery is unsettled.
|
|
713
|
+
|
|
714
|
+
Global Operator and global Role sessions remain native interactive CLIs:
|
|
704
715
|
|
|
705
716
|
```sh
|
|
706
717
|
yui session enter <global-role>
|
|
707
|
-
yui task enter <task-id> [role] [--read-only | --read-write]
|
|
708
|
-
yui task role enter <task-id> <role> [--read-only | --read-write]
|
|
709
718
|
```
|
|
710
719
|
|
|
720
|
+
tmux fixes a pane's history capacity when that pane is created. Existing panes
|
|
721
|
+
retain their configured capacity; managed runtime output remains observable in
|
|
722
|
+
the Agent Host pane without becoming lifecycle or acknowledgement evidence.
|
|
723
|
+
|
|
711
724
|
Each Role, including a Task-bound Worker instance, can bind multiple configured Agents, has one active Agent, and keeps
|
|
712
725
|
a separate native session per Agent binding. Operator narrows this to at most
|
|
713
726
|
one Agent per adapter—for example, one Codex and one Claude—so its bindings are
|
|
@@ -729,12 +742,10 @@ snapshot instead of applying desired drift as a hot change.
|
|
|
729
742
|
|
|
730
743
|
Use `yui config role unbind <global-role> <agent-id>` or `yui task role unbind <task-id> <role> <agent-id>` to retire a dormant binding. The active binding and any non-stopped native session are rejected; a stopped session record is removed atomically with the binding.
|
|
731
744
|
|
|
732
|
-
Claude session IDs are preallocated at launch.
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
Agent Driver Hook ingress for both CLIs. Global interactive Codex sessions may
|
|
737
|
-
still use its structured `notify` callback for conversation presentation.
|
|
745
|
+
Claude session IDs are preallocated at launch. Codex discovers its native
|
|
746
|
+
thread identity from App Server responses. Managed Task Runs use structured
|
|
747
|
+
Provider observations for both CLIs. Global interactive Codex sessions may
|
|
748
|
+
still use its `notify` callback for conversation presentation.
|
|
738
749
|
|
|
739
750
|
Automated lifecycle and delivery decisions use structured Hook payloads,
|
|
740
751
|
persisted identities, usage snapshots, tmux process state, receipts, and pane
|
|
@@ -277,8 +277,8 @@ const taskChildren = [
|
|
|
277
277
|
{
|
|
278
278
|
name: "complete",
|
|
279
279
|
summary: "Complete an active Task and stop automatic wakeups.",
|
|
280
|
-
usage: "yui task complete <id> (--summary <text>|--summary-file <path|->) [--refresh-remote]",
|
|
281
|
-
options: ["--summary", "--summary-file", "--refresh-remote"],
|
|
280
|
+
usage: "yui task complete <id> (--summary <text>|--summary-file <path|->) [--refresh-remote] [--accept-published-tree <publication-id>]",
|
|
281
|
+
options: ["--summary", "--summary-file", "--refresh-remote", "--accept-published-tree"],
|
|
282
282
|
fileOptions: ["--summary-file"]
|
|
283
283
|
},
|
|
284
284
|
{
|
|
@@ -513,7 +513,8 @@ const taskChildren = [
|
|
|
513
513
|
name: "role",
|
|
514
514
|
summary: "Manage Roles within a Task.",
|
|
515
515
|
sections: [{ id: "manage", title: "Commands", entries: [
|
|
516
|
-
"add", "list", "status", "show", "update", "remove", "bind", "unbind", "reset",
|
|
516
|
+
"add", "list", "status", "show", "update", "remove", "bind", "unbind", "reset",
|
|
517
|
+
"view", "takeover", "release"
|
|
517
518
|
] }],
|
|
518
519
|
children: [
|
|
519
520
|
{
|
|
@@ -548,10 +549,19 @@ const taskChildren = [
|
|
|
548
549
|
options: ["--reason"]
|
|
549
550
|
},
|
|
550
551
|
{
|
|
551
|
-
name: "
|
|
552
|
-
summary: "Attach to
|
|
553
|
-
usage: "yui task role
|
|
554
|
-
|
|
552
|
+
name: "view",
|
|
553
|
+
summary: "Attach read-only to a managed Provider presentation surface.",
|
|
554
|
+
usage: "yui task role view <task> <role>"
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
name: "takeover",
|
|
558
|
+
summary: "Acquire Provider writer authority and enter the PTY input gateway.",
|
|
559
|
+
usage: "yui task role takeover <task> <role>"
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
name: "release",
|
|
563
|
+
summary: "Return stranded human Provider authority to the Controller.",
|
|
564
|
+
usage: "yui task role release <task> <role>"
|
|
555
565
|
}
|
|
556
566
|
]
|
|
557
567
|
},
|
|
@@ -675,7 +685,7 @@ const taskChildren = [
|
|
|
675
685
|
{
|
|
676
686
|
name: "run",
|
|
677
687
|
summary: "Inspect and control Task Role Agent Runs.",
|
|
678
|
-
sections: [{ id: "manage", title: "Commands", entries: ["list", "show", "retry", "settle", "recover", "yield", "checkpoint"] }],
|
|
688
|
+
sections: [{ id: "manage", title: "Commands", entries: ["list", "show", "retry", "settle", "recover", "yield", "context", "checkpoint"] }],
|
|
679
689
|
children: [
|
|
680
690
|
{ name: "list", summary: "List Runs for a work item.", usage: "yui task run list <task>/<work>" },
|
|
681
691
|
{
|
|
@@ -706,6 +716,28 @@ const taskChildren = [
|
|
|
706
716
|
options: ["--summary", "--summary-file"],
|
|
707
717
|
fileOptions: ["--summary-file"]
|
|
708
718
|
},
|
|
719
|
+
{
|
|
720
|
+
name: "context",
|
|
721
|
+
summary: "Load the exact authorized Run context.",
|
|
722
|
+
usage: "yui task run context <task>/<run> [--json]",
|
|
723
|
+
executable: true,
|
|
724
|
+
hidden: true,
|
|
725
|
+
sections: [{ id: "load", title: "Commands", entries: ["expand", "delta"] }],
|
|
726
|
+
children: [
|
|
727
|
+
{
|
|
728
|
+
name: "expand",
|
|
729
|
+
summary: "Expand one authorized Run context reference.",
|
|
730
|
+
usage: "yui task run context expand <task>/<run> <ref-id> [--store <store>] [--mode full]",
|
|
731
|
+
options: ["--store", "--mode"]
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
name: "delta",
|
|
735
|
+
summary: "Load authorized Run context changes after a cursor.",
|
|
736
|
+
usage: "yui task run context delta <task>/<run> --after <cursor>",
|
|
737
|
+
options: ["--after"]
|
|
738
|
+
}
|
|
739
|
+
]
|
|
740
|
+
},
|
|
709
741
|
{
|
|
710
742
|
name: "checkpoint",
|
|
711
743
|
summary: "Record durable progress for a long-running Agent Run.",
|
|
@@ -719,7 +751,7 @@ const taskChildren = [
|
|
|
719
751
|
{
|
|
720
752
|
name: "review",
|
|
721
753
|
summary: "Control Task-final ReviewRounds.",
|
|
722
|
-
sections: [{ id: "manage", title: "Commands", entries: ["request", "group", "retry", "finding"] }],
|
|
754
|
+
sections: [{ id: "manage", title: "Commands", entries: ["request", "force-fresh", "group", "retry", "finding"] }],
|
|
723
755
|
children: [
|
|
724
756
|
{
|
|
725
757
|
name: "request",
|
|
@@ -727,6 +759,11 @@ const taskChildren = [
|
|
|
727
759
|
usage: "yui task review request <task> --role <global-role> [--strategy fixed:<count>|adaptive:<max>] [--lane-role <role> ...] [--delta-recheck]",
|
|
728
760
|
options: ["--role", "--strategy", "--lane-role", "--delta-recheck"]
|
|
729
761
|
},
|
|
762
|
+
{
|
|
763
|
+
name: "force-fresh",
|
|
764
|
+
summary: "Replace one exact non-semantic failed Task-final Review with a distinct full Round.",
|
|
765
|
+
usage: "yui task review force-fresh <task>/<review-round>"
|
|
766
|
+
},
|
|
730
767
|
{
|
|
731
768
|
name: "group",
|
|
732
769
|
summary: "Resolve a Reviewer ExecutionGroup after its Lanes finish.",
|
|
@@ -940,12 +977,6 @@ const taskChildren = [
|
|
|
940
977
|
{ name: "show", summary: "Show one ChangeSet.", usage: "yui task change-set show <task>/<change-set>" }
|
|
941
978
|
]
|
|
942
979
|
},
|
|
943
|
-
{
|
|
944
|
-
name: "enter",
|
|
945
|
-
summary: "Attach to an existing Task Role, defaulting to Leader and read-only.",
|
|
946
|
-
usage: "yui task enter <task> [role] [--read-only | --read-write]",
|
|
947
|
-
options: ["--read-only", "--read-write"]
|
|
948
|
-
}
|
|
949
980
|
];
|
|
950
981
|
export const ROOT_COMMAND = buildNode({
|
|
951
982
|
name: "yui",
|
|
@@ -1319,7 +1350,7 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1319
1350
|
summary: "Manage Tasks, WorkItems, Agent Runs, and integration.",
|
|
1320
1351
|
sections: [
|
|
1321
1352
|
{ id: "lifecycle", title: "Lifecycle", entries: ["create", "project", "base", "update", "activate", "complete", "reopen", "retire", "list", "show", "context", "next-action", "archive", "rebuild", "history", "replace", "reconcile"] },
|
|
1322
|
-
{ id: "collaboration", title: "Collaboration", entries: ["message", "input", "grant", "workflow", "publication", "work", "run", "review", "integration", "role", "
|
|
1353
|
+
{ id: "collaboration", title: "Collaboration", entries: ["message", "input", "grant", "workflow", "publication", "work", "run", "review", "integration", "role", "overlap", "change-set"] },
|
|
1323
1354
|
{ id: "knowledge", title: "Task Knowledge", entries: ["brief", "decision", "milestone", "event", "continuation", "wake"] }
|
|
1324
1355
|
],
|
|
1325
1356
|
children: taskChildren
|
|
@@ -1359,8 +1390,17 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1359
1390
|
name: "internal",
|
|
1360
1391
|
summary: "Internal Yui callbacks.",
|
|
1361
1392
|
hidden: true,
|
|
1362
|
-
sections: [{
|
|
1393
|
+
sections: [{
|
|
1394
|
+
id: "callbacks",
|
|
1395
|
+
title: "Callbacks",
|
|
1396
|
+
entries: ["session-notify", "runtime-hook", "agent-host"]
|
|
1397
|
+
}],
|
|
1363
1398
|
children: [
|
|
1399
|
+
{
|
|
1400
|
+
name: "agent-host",
|
|
1401
|
+
summary: "Run the persistent structured Provider host.",
|
|
1402
|
+
usage: "yui internal agent-host <launch-id> <ticket>"
|
|
1403
|
+
},
|
|
1364
1404
|
{
|
|
1365
1405
|
name: "session-notify",
|
|
1366
1406
|
summary: "Record a structured native session notification.",
|
|
@@ -361,8 +361,8 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
361
361
|
trailingOptions: { "--reason": "value" },
|
|
362
362
|
confirmation: { action: "Reset Task Role Session", targetArgumentIndex: 4 }
|
|
363
363
|
},
|
|
364
|
-
{
|
|
365
|
-
commandPath: ["task", "role",
|
|
364
|
+
...["view", "takeover", "release"].map((command) => ({
|
|
365
|
+
commandPath: ["task", "role", command],
|
|
366
366
|
selectors: [
|
|
367
367
|
{ argumentIndex: 3, entity: "task", provider: "tasks", actionTarget: true },
|
|
368
368
|
{
|
|
@@ -372,9 +372,8 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
372
372
|
dependsOn: 3,
|
|
373
373
|
actionTarget: true
|
|
374
374
|
}
|
|
375
|
-
]
|
|
376
|
-
|
|
377
|
-
},
|
|
375
|
+
]
|
|
376
|
+
})),
|
|
378
377
|
{
|
|
379
378
|
commandPath: ["task", "work", "create"],
|
|
380
379
|
selectors: [
|
|
@@ -534,11 +533,6 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
534
533
|
? { trailingOptions: { "--reason": "value" } }
|
|
535
534
|
: {})
|
|
536
535
|
})),
|
|
537
|
-
{
|
|
538
|
-
commandPath: ["task", "enter"],
|
|
539
|
-
selectors: [{ argumentIndex: 2, entity: "task", provider: "tasks", actionTarget: true }],
|
|
540
|
-
trailingOptions: { "--read-only": "flag", "--read-write": "flag" }
|
|
541
|
-
},
|
|
542
536
|
{
|
|
543
537
|
commandPath: ["jobs", "retry"],
|
|
544
538
|
selectors: [{ argumentIndex: 2, entity: "job", provider: "jobs", actionTarget: true }]
|
|
@@ -32,7 +32,8 @@ function resolveExecutionPath(args) {
|
|
|
32
32
|
const child = findChild(node, args[index] ?? "");
|
|
33
33
|
const internalExecutable = child !== undefined && ((node === ROOT_COMMAND && child.name === "internal")
|
|
34
34
|
|| (node.path.join(" ") === "yui config completion" && child.name === "candidates")
|
|
35
|
-
|| (node.path.join(" ") === "yui task run"
|
|
35
|
+
|| (node.path.join(" ") === "yui task run"
|
|
36
|
+
&& (child.name === "checkpoint" || child.name === "context"))
|
|
36
37
|
|| (node.path.join(" ") === "yui controller"
|
|
37
38
|
&& (child.name === "identity" || child.name === "live-identity")));
|
|
38
39
|
if (child === undefined || (child.hidden && !internalExecutable)) {
|
package/dist/cli.js
CHANGED
|
@@ -36,7 +36,7 @@ import { runResourcesCommand } from "./commands/resourcesCommands.js";
|
|
|
36
36
|
import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
|
|
37
37
|
import { runProjectCommand } from "./commands/projectCommands.js";
|
|
38
38
|
import { runProfileCommand } from "./commands/profileCommands.js";
|
|
39
|
-
import { dispatchPreparedReviewRound, failPendingReviewRound,
|
|
39
|
+
import { dispatchPreparedReviewRound, failPendingReviewRound, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
|
|
40
40
|
import { taskActor } from "./commands/taskActor.js";
|
|
41
41
|
import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
|
|
42
42
|
import { runTaskChangeSetCommand } from "./commands/taskChangeSetCommands.js";
|
|
@@ -48,7 +48,7 @@ import { createUpdatePorts } from "./cli/updatePorts.js";
|
|
|
48
48
|
import { createReleaseWorkflowPorts } from "./release/releaseWorkflowPorts.js";
|
|
49
49
|
import { readRuntimeIdentity } from "./release/runtimeRelease.js";
|
|
50
50
|
import { renderReleaseActivateResult, renderReleaseInstallResult, renderReleaseList, runReleaseActivate, runReleaseInstall, runReleaseList } from "./commands/releaseCommands.js";
|
|
51
|
-
import { reconcileTaskRemoteBaselines } from "./commands/taskCompletionGate.js";
|
|
51
|
+
import { reconcileTaskRemoteBaselines, verifyTaskCompletionPublishedTree } from "./commands/taskCompletionGate.js";
|
|
52
52
|
import { runTaskBaseStatusCommand } from "./commands/taskBaseCommands.js";
|
|
53
53
|
import { assertTaskBaseFreshnessForCompletion, inspectTaskBaseFreshness } from "./repository/taskBaseFreshness.js";
|
|
54
54
|
import { FileCompletionManager, resolveCliIdentity } from "./completion/fileCompletionManager.js";
|
|
@@ -61,9 +61,9 @@ import { runSessionNotifyCommand } from "./controller/sessionNotify.js";
|
|
|
61
61
|
import { openSchedulerTelemetry } from "./telemetry/telemetryWiring.js";
|
|
62
62
|
import { runRuntimeObservationHookCommand } from "./controller/runtimeObservationHook.js";
|
|
63
63
|
import { buildDoctorReport, renderDoctor, runDoctorCommand } from "./doctor/doctor.js";
|
|
64
|
-
import { agentNotFound, CliError, usageError } from "./errors/cliError.js";
|
|
64
|
+
import { agentNotFound, CliError, runtimeError, usageError } from "./errors/cliError.js";
|
|
65
65
|
import { FileRoleLaunchPlanner } from "./executor/fileRoleLaunchPlanner.js";
|
|
66
|
-
import { runAgentHost } from "./runtime/agentHost.js";
|
|
66
|
+
import { AGENT_HOST_CONTROL_PROTOCOL, runAgentHost, sendAgentHostAuthorityControl } from "./runtime/agentHost.js";
|
|
67
67
|
import { TaskWorkspaceCoordinator, WorkspaceCleanupBlockedError } from "./repository/taskWorkspaceCoordinator.js";
|
|
68
68
|
import { FileTaskWorkspacePreparer, ReviewRoundWorkspaceEvidenceError } from "./repository/taskWorkspacePreparer.js";
|
|
69
69
|
import { inspectStorageSchema } from "./storage/storageSchema.js";
|
|
@@ -944,6 +944,7 @@ export async function main() {
|
|
|
944
944
|
}
|
|
945
945
|
}
|
|
946
946
|
let completionSummary;
|
|
947
|
+
let completionPublishedTreeProof;
|
|
947
948
|
if (resolved[1] === "base" && resolved[2] === "status") {
|
|
948
949
|
const result = await runTaskBaseStatusCommand(resolved.slice(3), store);
|
|
949
950
|
emit(result.output, false, result.data);
|
|
@@ -958,12 +959,26 @@ export async function main() {
|
|
|
958
959
|
...(taskFinalReviewContract === undefined
|
|
959
960
|
? {}
|
|
960
961
|
: { taskFinalReviewContract })
|
|
961
|
-
});
|
|
962
|
+
}, completionRequest);
|
|
962
963
|
if (!completion.completed && !completion.activeTaskReview) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
964
|
+
// An explicit refresh must fetch the remote object graph before the
|
|
965
|
+
// Publication proof resolves its exact commit. Without the flag the
|
|
966
|
+
// command remains offline and preserves the existing proof-first path.
|
|
967
|
+
const refreshedFreshness = refreshRemote
|
|
968
|
+
? await inspectTaskBaseFreshness(resolved[2], store, { refresh: true })
|
|
969
|
+
: undefined;
|
|
970
|
+
if (completionRequest.acceptedPublishedTreePublicationId !== undefined) {
|
|
971
|
+
completionPublishedTreeProof = await verifyTaskCompletionPublishedTree(completionRequest.taskId, completionRequest.acceptedPublishedTreePublicationId, store);
|
|
972
|
+
}
|
|
973
|
+
const freshness = refreshedFreshness
|
|
974
|
+
?? await inspectTaskBaseFreshness(resolved[2], store);
|
|
975
|
+
for (const warning of assertTaskBaseFreshnessForCompletion(freshness, {
|
|
976
|
+
...(completionPublishedTreeProof === undefined
|
|
977
|
+
? {}
|
|
978
|
+
: {
|
|
979
|
+
acceptedPublishedTreeProjectId: completionPublishedTreeProof.projectId
|
|
980
|
+
})
|
|
981
|
+
})) {
|
|
967
982
|
process.stderr.write(`Warning: ${warning}\n`);
|
|
968
983
|
}
|
|
969
984
|
// Keep completion offline by default. An explicit refresh is the only
|
|
@@ -1002,6 +1017,9 @@ export async function main() {
|
|
|
1002
1017
|
? {}
|
|
1003
1018
|
: { taskFinalReviewContract }),
|
|
1004
1019
|
...(completionSummary === undefined ? {} : { completionSummary }),
|
|
1020
|
+
...(completionPublishedTreeProof === undefined
|
|
1021
|
+
? {}
|
|
1022
|
+
: { completionPublishedTreeProof }),
|
|
1005
1023
|
...(workItemIntegrationProof === undefined ? {} : { workItemIntegrationProof }),
|
|
1006
1024
|
...(candidateGitSnapshot === undefined ? {} : { candidateGitSnapshot }),
|
|
1007
1025
|
...(candidateMaterialization === undefined
|
|
@@ -1148,30 +1166,82 @@ export async function main() {
|
|
|
1148
1166
|
: { command: result.data, ...reviewData });
|
|
1149
1167
|
return;
|
|
1150
1168
|
}
|
|
1151
|
-
if (
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1169
|
+
if (jsonOutput) {
|
|
1170
|
+
throw usageError("Task Role view/takeover requires an interactive terminal.");
|
|
1171
|
+
}
|
|
1172
|
+
if (result.kind === "view") {
|
|
1173
|
+
if (result.output !== undefined)
|
|
1174
|
+
emit(result.output);
|
|
1175
|
+
tmux.attachRole(result.taskId, result.roleName, "read-only");
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
const syncAuthority = async (authorityResult) => {
|
|
1179
|
+
let control;
|
|
1180
|
+
try {
|
|
1181
|
+
control = await sendAgentHostAuthorityControl({
|
|
1182
|
+
home,
|
|
1183
|
+
scope: "task",
|
|
1184
|
+
taskId: authorityResult.taskId,
|
|
1185
|
+
roleName: authorityResult.roleName,
|
|
1186
|
+
control: {
|
|
1187
|
+
protocol: AGENT_HOST_CONTROL_PROTOCOL,
|
|
1188
|
+
type: "set-authority",
|
|
1189
|
+
nativeSessionId: authorityResult.nativeSessionId,
|
|
1190
|
+
authority: authorityResult.authority
|
|
1162
1191
|
}
|
|
1163
|
-
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
catch (error) {
|
|
1195
|
+
throw runtimeError(`Agent Host authority synchronization failed at epoch ${authorityResult.authority.epoch}: `
|
|
1196
|
+
+ `${error instanceof Error ? error.message : String(error)}. `
|
|
1197
|
+
+ `Durable authority is ${authorityResult.authority.owner}-owned; retry `
|
|
1198
|
+
+ `'yui task role release ${authorityResult.taskId} ${authorityResult.roleName}' `
|
|
1199
|
+
+ "to reconcile the Host.");
|
|
1200
|
+
}
|
|
1201
|
+
if (control.outcome !== "accepted"
|
|
1202
|
+
|| control.snapshot.nativeSessionId !== authorityResult.nativeSessionId
|
|
1203
|
+
|| control.snapshot.authorityEpoch !== authorityResult.authority.epoch
|
|
1204
|
+
|| control.snapshot.authorityOwner !== authorityResult.authority.owner
|
|
1205
|
+
|| control.snapshot.authorityHolderId !== authorityResult.authority.holderId) {
|
|
1206
|
+
throw runtimeError(`Agent Host did not accept Provider authority epoch ${authorityResult.authority.epoch}: `
|
|
1207
|
+
+ (control.snapshot.detail ?? control.outcome)
|
|
1208
|
+
+ `. Durable authority is ${authorityResult.authority.owner}-owned; `
|
|
1209
|
+
+ "retry 'yui task role release "
|
|
1210
|
+
+ `${authorityResult.taskId} ${authorityResult.roleName}' to reconcile the Host.`);
|
|
1211
|
+
}
|
|
1212
|
+
return control;
|
|
1213
|
+
};
|
|
1214
|
+
await syncAuthority(result);
|
|
1215
|
+
emit(result.output);
|
|
1216
|
+
if (result.action === "release") {
|
|
1217
|
+
runtime.notifyMailboxChanged({
|
|
1218
|
+
kind: "role",
|
|
1219
|
+
taskId: result.taskId,
|
|
1220
|
+
roleName: result.roleName
|
|
1164
1221
|
});
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
process.stdout.write("Provider input is now routed through the Agent Host PTY gateway. "
|
|
1225
|
+
+ "Use tmux detach (Ctrl-b d) to return authority to the Controller.\n");
|
|
1226
|
+
try {
|
|
1227
|
+
tmux.attachRole(result.taskId, result.roleName, "read-write");
|
|
1165
1228
|
}
|
|
1166
1229
|
finally {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1230
|
+
const currentTask = store.getTask(result.taskId);
|
|
1231
|
+
// Completing or retiring the Task from inside the takeover Turn owns
|
|
1232
|
+
// Provider shutdown and clears the live binding. Do not turn that
|
|
1233
|
+
// successful terminal transition into a failing best-effort release.
|
|
1234
|
+
if (currentTask?.status === "active") {
|
|
1235
|
+
const released = runTaskCommand(["role", "release", result.taskId, result.roleName], store, { runtime, environment: process.env, yuiHome: home });
|
|
1236
|
+
if (released.kind !== "authority" || released.action !== "release") {
|
|
1237
|
+
throw runtimeError("Provider authority release returned an invalid result.");
|
|
1238
|
+
}
|
|
1239
|
+
await syncAuthority(released);
|
|
1240
|
+
emit(released.output);
|
|
1171
1241
|
runtime.notifyMailboxChanged({
|
|
1172
1242
|
kind: "role",
|
|
1173
|
-
taskId:
|
|
1174
|
-
roleName:
|
|
1243
|
+
taskId: released.taskId,
|
|
1244
|
+
roleName: released.roleName
|
|
1175
1245
|
});
|
|
1176
1246
|
}
|
|
1177
1247
|
}
|
|
@@ -1621,6 +1691,15 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
|
|
|
1621
1691
|
taskId = reference.taskId;
|
|
1622
1692
|
}
|
|
1623
1693
|
}
|
|
1694
|
+
else if (args[1] === "review"
|
|
1695
|
+
&& args[2] === "force-fresh"
|
|
1696
|
+
&& args[3] !== undefined) {
|
|
1697
|
+
const reference = cliTaskRecordReference(args[3], "reviewRound", environment);
|
|
1698
|
+
const round = store.getReviewRound(reference.taskId, reference.localId);
|
|
1699
|
+
if (round !== null && (round.scope ?? "work-item") === "task") {
|
|
1700
|
+
taskId = reference.taskId;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1624
1703
|
else if (args[1] === "work"
|
|
1625
1704
|
&& args[2] === "review"
|
|
1626
1705
|
&& args[3] === "retry"
|