@zq-silk/yui 0.13.4 → 0.13.6
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 +13 -13
- package/README.md +19 -20
- package/dist/cli/commandCatalog.js +22 -21
- package/dist/cli/interactionPolicy.js +0 -14
- package/dist/cli.js +42 -0
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/taskCommands.js +60 -326
- package/dist/commands/taskContextCommand.js +3 -14
- package/dist/commands/taskExecutionCommands.js +254 -0
- package/dist/commands/taskNextActionCommand.js +1 -3
- package/dist/commands/taskOverviewCommand.js +9 -2
- package/dist/commands/taskRoleRuntimeStatus.js +2 -25
- package/dist/controller/agentRuntimeObserver.js +4 -2
- package/dist/controller/clientRuntime.js +45 -2
- package/dist/controller/controller.js +6 -3
- package/dist/controller/fileSchedulerStoreAdapter.js +59 -188
- package/dist/controller/jobControl.js +3 -2
- package/dist/controller/runtime.js +6 -33
- package/dist/controller/runtimeEventProcessor.js +8 -4
- package/dist/controller/runtimeHookRunFence.js +4 -10
- package/dist/execution/executionHealth.js +8 -16
- package/dist/executor/agentAdapter.js +3 -8
- package/dist/executor/agentExecutor.js +13 -14
- package/dist/executor/fileRoleLaunchPlanner.js +10 -26
- package/dist/lifecycle/exactRunTerminalization.js +24 -322
- package/dist/repository/taskWorkspaceCoordinator.js +0 -9
- package/dist/runtime/agentHost.js +22 -83
- package/dist/runtime/builtinAgentDrivers.js +1 -1
- package/dist/runtime/exactControlPlane.js +15 -9
- package/dist/runtime/launchBroker.js +1 -11
- package/dist/runtime/providerContinuationReconciliationService.js +1 -1
- package/dist/runtime/providerRecoveryDecision.js +1 -1
- package/dist/runtime/providerRuntimeIdentity.js +25 -15
- package/dist/runtime/structuredProviderHost.js +0 -57
- package/dist/scheduler/activeRoleRunDelivery.js +1 -19
- package/dist/scheduler/leaderWakeupProcessor.js +12 -63
- package/dist/scheduler/ports.js +3 -2
- package/dist/scheduler/roleRunLiveness.js +4 -1
- package/dist/scheduler/roleRunStall.js +0 -2
- package/dist/scheduler/taskExecutionProjection.js +18 -1
- package/dist/scheduler/wakeupQueue.js +2 -1
- package/dist/storage/migration/productionRegistry.js +65 -0
- package/dist/storage/sqliteStore.js +10 -2
- package/dist/storage/taskStore.js +11 -3
- package/dist/task/completionReadiness.js +0 -67
- package/dist/task/nextAction.js +16 -32
- package/dist/task/task.js +38 -3
- package/dist/web/assets/client/i18n.js +0 -4
- package/dist/web/assets/client/view.js +0 -18
- package/dist/web/webSnapshot.js +7 -13
- package/i18n/README.zh-CN.md +4 -4
- package/package.json +1 -1
- package/dist/run/recoveryProjection.js +0 -252
- package/dist/runtime/conversationSwitch.js +0 -277
package/ARCHITECTURE.md
CHANGED
|
@@ -254,21 +254,21 @@ Yui's current attachment, not exclusive ownership of the Provider thread. One
|
|
|
254
254
|
Turn identifies one provider-native execution. Yui's authority epoch fences
|
|
255
255
|
only Yui's own submissions and retries.
|
|
256
256
|
|
|
257
|
-
Codex Task threads
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
session-lifecycle operation outside the Task delivery contract.
|
|
263
|
-
|
|
264
|
-
Codex uses `app-server
|
|
265
|
-
|
|
257
|
+
Codex Task threads remain ordinary native sessions and can be inspected in
|
|
258
|
+
Desktop. While their managed Role runtime is live, writes go through Yui's
|
|
259
|
+
view/takeover boundary; stop Task execution before resuming the thread from a
|
|
260
|
+
different client. If Yui observes a pre-existing active Turn during resume, it
|
|
261
|
+
keeps its pending Run/message until that Turn settles. Global interactive entry
|
|
262
|
+
remains a native session-lifecycle operation outside the Task delivery contract.
|
|
263
|
+
|
|
264
|
+
Codex uses a Yui-owned direct `app-server` child to create or resume a normal
|
|
265
|
+
thread. The process loads the selected native config profile; Role model,
|
|
266
|
+
effort, permission, workspace, and shell settings are passed at
|
|
266
267
|
`thread/start`/`thread/resume`, while the ordinary Task message points to the
|
|
267
268
|
Session Manifest and matching Role Skill. Yui does not write either to global
|
|
268
|
-
Codex config
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
Profile supplies the per-Role behavior, Skills, model, and effort instead.
|
|
269
|
+
Codex config. The Agent Host owns the Provider process group, so Task execution
|
|
270
|
+
stop can end the complete runtime and start can rebuild it without depending on
|
|
271
|
+
the shared Codex daemon.
|
|
272
272
|
Claude uses a persistent stream-json
|
|
273
273
|
transport with exact user-message replay acknowledgement. In both cases, Yui
|
|
274
274
|
records Turn intent before writing, accepts only exact Provider evidence, and
|
package/README.md
CHANGED
|
@@ -549,18 +549,18 @@ yui operator new
|
|
|
549
549
|
yui operator enter
|
|
550
550
|
```
|
|
551
551
|
|
|
552
|
-
|
|
553
|
-
|
|
552
|
+
If current execution cannot be settled normally, fence the Task and restart
|
|
553
|
+
from its durable progress:
|
|
554
554
|
|
|
555
555
|
```sh
|
|
556
|
-
yui task
|
|
556
|
+
yui task execution stop <task-id> --force --reason "<why execution must be fenced>"
|
|
557
|
+
yui task execution start <task-id>
|
|
557
558
|
```
|
|
558
559
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
messages, reviews, and delivery history remain durable.
|
|
560
|
+
`stop` terminates disposable Runs and Sessions while preserving WorkItems,
|
|
561
|
+
repository changes, Messages, reviews, and other Task progress. `start` admits
|
|
562
|
+
one new Leader attempt from those durable records; it does not recover an old
|
|
563
|
+
Agent conversation.
|
|
564
564
|
|
|
565
565
|
Without `--task`, `operator submit` creates a new Draft. Drafts accept planning changes but must be activated before Agent execution.
|
|
566
566
|
Operator resolves every request against the Project catalog and existing Task
|
|
@@ -875,13 +875,13 @@ Provider conversations remain ordinary user conversations. Yui adds the Role
|
|
|
875
875
|
Skill and Session Manifest pointer, then sends Task work through provider-native
|
|
876
876
|
structured requests. Managed prompts are never delivered as terminal bytes.
|
|
877
877
|
|
|
878
|
-
Codex uses `app-server
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
878
|
+
Codex uses a direct, Yui-owned `app-server` child to create or resume a normal
|
|
879
|
+
thread. The thread remains visible in Codex, while Yui's view/takeover boundary
|
|
880
|
+
is the supported way to interact with a live managed Role. Task execution stop
|
|
881
|
+
terminates the Agent Host and its Provider process group; start creates a fresh
|
|
882
|
+
owned runtime without depending on the shared Codex daemon. If the child
|
|
883
|
+
disconnects, the Host may start a bounded replacement process and reconcile the
|
|
884
|
+
exact owned Turn from native history.
|
|
885
885
|
Claude Code keeps its independent stream-json process with exact
|
|
886
886
|
user-message replay acknowledgement. A timeout or uncertain write becomes
|
|
887
887
|
`delivery-unknown` and is never automatically retried.
|
|
@@ -894,10 +894,9 @@ yui task role takeover <task-id> <role>
|
|
|
894
894
|
yui task role release <task-id> <role>
|
|
895
895
|
```
|
|
896
896
|
|
|
897
|
-
For
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
process.
|
|
897
|
+
For a live managed Role, including Codex, these commands are the supported
|
|
898
|
+
human-control boundary. Its ordinary native thread remains inspectable in
|
|
899
|
+
Codex; stop Task execution before resuming it from a different client.
|
|
901
900
|
|
|
902
901
|
Global Operator and global Role sessions remain native interactive CLIs:
|
|
903
902
|
|
|
@@ -962,7 +961,7 @@ movement cannot conceal a workflow that is not advancing.
|
|
|
962
961
|
|
|
963
962
|
Stable Role context never creates a separate bootstrap Turn. Task execution Runs use the generic Leader or Worker Skill, while review Runs use the generic Reviewer Skill based on durable Run purpose rather than a configured Role name. The provider either carries the Skill through a safe additive native context channel or points to it from the ordinary Task delivery. These Yui-owned Role Skills define portable orchestration only. Project Skills remain ordinary versioned files in the Project and are discovered, selected, and loaded by the Agent through its native project mechanism; Yui does not scan, parse, copy, or inject them.
|
|
964
963
|
|
|
965
|
-
Managed Codex keeps the user's native developer instructions unchanged. The ordinary Task message includes a compact absolute Session Manifest pointer, and the manifest identifies the matching Yui-owned Role Skill for Codex to read on demand.
|
|
964
|
+
Managed Codex keeps the user's native developer instructions unchanged. The ordinary Task message includes a compact absolute Session Manifest pointer, and the manifest identifies the matching Yui-owned Role Skill for Codex to read on demand. Its Yui-owned App Server process loads any selected Codex native config profile; model, effort, permission, workspace, and shell settings are supplied to `thread/start` or `thread/resume`, and the underlying Codex config file is never mutated. App Server notifications are the managed thread's lifecycle authority; Yui installs no managed Codex Hook and does not claim `notify`. Interactive Codex Sessions may still use Yui's structured `notify` callback, and Doctor reports any effective configuration conflict. `skills.config` is not misused because it only enables or disables already-discovered Skills. Claude receives the same Yui-owned Role Skill content from a private `0600` managed context file rather than a large or sensitive argv value; retries and resumes reuse the purpose-specific Role path. Non-Operator global Roles stay neutral and receive no Task orchestration Skill. Operator therefore opens at an empty native composer, so the user's text remains its first user message. Leader wakeups and Worker or Reviewer Run assignments remain real mailbox-delivered work messages.
|
|
966
965
|
|
|
967
966
|
## Controller and failure handling
|
|
968
967
|
|
|
@@ -282,6 +282,24 @@ const taskChildren = [
|
|
|
282
282
|
}
|
|
283
283
|
},
|
|
284
284
|
{ name: "activate", summary: "Activate a Draft Task.", usage: "yui task activate <id>" },
|
|
285
|
+
{
|
|
286
|
+
name: "execution",
|
|
287
|
+
summary: "Fence or resume all execution for a Task.",
|
|
288
|
+
sections: [{ id: "manage", title: "Commands", entries: ["stop", "start"] }],
|
|
289
|
+
children: [
|
|
290
|
+
{
|
|
291
|
+
name: "stop",
|
|
292
|
+
summary: "Stop all Task execution while preserving durable progress.",
|
|
293
|
+
usage: "yui task execution stop <task> --force --reason <text>",
|
|
294
|
+
options: ["--force", "--reason"]
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: "start",
|
|
298
|
+
summary: "Resume the Leader from durable Task progress.",
|
|
299
|
+
usage: "yui task execution start <task>"
|
|
300
|
+
}
|
|
301
|
+
]
|
|
302
|
+
},
|
|
285
303
|
{
|
|
286
304
|
name: "complete",
|
|
287
305
|
summary: "Complete an active Task and stop automatic wakeups.",
|
|
@@ -540,7 +558,7 @@ const taskChildren = [
|
|
|
540
558
|
name: "role",
|
|
541
559
|
summary: "Manage Roles within a Task.",
|
|
542
560
|
sections: [{ id: "manage", title: "Commands", entries: [
|
|
543
|
-
"add", "list", "status", "show", "update", "remove", "bind", "unbind",
|
|
561
|
+
"add", "list", "status", "show", "update", "remove", "bind", "unbind",
|
|
544
562
|
"view", "takeover", "release"
|
|
545
563
|
] }],
|
|
546
564
|
children: [
|
|
@@ -569,17 +587,6 @@ const taskChildren = [
|
|
|
569
587
|
{ name: "remove", summary: "Remove a Task Role.", usage: "yui task role remove <task> <role>" },
|
|
570
588
|
{ name: "bind", summary: "Bind and activate an Agent for a Task Role.", usage: "yui task role bind <task> <role> <agent-id>" },
|
|
571
589
|
{ name: "unbind", summary: "Unbind a dormant Agent from a Task Role.", usage: "yui task role unbind <task> <role> <agent-id>" },
|
|
572
|
-
{
|
|
573
|
-
name: "session",
|
|
574
|
-
summary: "Manage the Provider Conversation bound to a Task Role.",
|
|
575
|
-
sections: [{ id: "manage", title: "Commands", entries: ["switch"] }],
|
|
576
|
-
children: [{
|
|
577
|
-
name: "switch",
|
|
578
|
-
summary: "Request a fresh Conversation after the current one reaches a safe boundary.",
|
|
579
|
-
usage: "yui task role session switch <task> <role> --reason <text>",
|
|
580
|
-
options: ["--reason"]
|
|
581
|
-
}]
|
|
582
|
-
},
|
|
583
590
|
{
|
|
584
591
|
name: "view",
|
|
585
592
|
summary: "Attach read-only to an independent Provider presentation surface.",
|
|
@@ -720,12 +727,12 @@ const taskChildren = [
|
|
|
720
727
|
{
|
|
721
728
|
name: "run",
|
|
722
729
|
summary: "Inspect and control Task Role Agent Runs.",
|
|
723
|
-
sections: [{ id: "manage", title: "Commands", entries: ["list", "show", "retry", "settle", "
|
|
730
|
+
sections: [{ id: "manage", title: "Commands", entries: ["list", "show", "retry", "settle", "yield", "context", "checkpoint", "retire"] }],
|
|
724
731
|
children: [
|
|
725
732
|
{ name: "list", summary: "List Runs for a work item.", usage: "yui task run list <task>/<work>" },
|
|
726
733
|
{
|
|
727
734
|
name: "show",
|
|
728
|
-
summary: "Show one Run
|
|
735
|
+
summary: "Show one Run and its retained audit evidence.",
|
|
729
736
|
usage: "yui task run show <task>/<run> [--json]"
|
|
730
737
|
},
|
|
731
738
|
{
|
|
@@ -738,12 +745,6 @@ const taskChildren = [
|
|
|
738
745
|
summary: "Close an obsolete stranded final Review Run without requesting a retry Round.",
|
|
739
746
|
usage: "yui task run settle <task>/<run>"
|
|
740
747
|
},
|
|
741
|
-
{
|
|
742
|
-
name: "recover",
|
|
743
|
-
summary: "Record one exact Leader-controlled Run recovery decision.",
|
|
744
|
-
usage: "yui task run recover <task>/<run> --action <diagnose|retry|terminate> (--expected-progress-at <timestamp>|--from-next-action <fingerprint>) --provider-acceptance <accepted|rejected|ambiguous> --reason <text>",
|
|
745
|
-
options: ["--action", "--expected-progress-at", "--progress-at", "--from-next-action", "--provider-acceptance", "--reason", "--role", "--agent-id", "--adapter-id", "--native-session-id", "--launch-id"]
|
|
746
|
-
},
|
|
747
748
|
{
|
|
748
749
|
name: "yield",
|
|
749
750
|
summary: "Complete an active Run and wake the Leader.",
|
|
@@ -1432,7 +1433,7 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1432
1433
|
name: "task",
|
|
1433
1434
|
summary: "Manage Tasks, WorkItems, Agent Runs, and integration.",
|
|
1434
1435
|
sections: [
|
|
1435
|
-
{ id: "lifecycle", title: "Lifecycle", entries: ["create", "project", "base", "update", "activate", "complete", "reopen", "retire", "list", "show", "context", "next-action", "archive", "rebuild", "history", "replace", "reconcile", "upstream"] },
|
|
1436
|
+
{ id: "lifecycle", title: "Lifecycle", entries: ["create", "project", "base", "update", "activate", "execution", "complete", "reopen", "retire", "list", "show", "context", "next-action", "archive", "rebuild", "history", "replace", "reconcile", "upstream"] },
|
|
1436
1437
|
{ id: "collaboration", title: "Collaboration", entries: ["message", "input", "grant", "workflow", "publication", "work", "run", "review", "integration", "role", "overlap", "change-set"] },
|
|
1437
1438
|
{ id: "knowledge", title: "Task Knowledge", entries: ["brief", "decision", "milestone", "event", "continuation", "wake"] }
|
|
1438
1439
|
],
|
|
@@ -346,20 +346,6 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
346
346
|
{ argumentIndex: 5, entity: "agent", provider: "configured-agents", actionTarget: false }
|
|
347
347
|
]
|
|
348
348
|
})),
|
|
349
|
-
{
|
|
350
|
-
commandPath: ["task", "role", "session", "switch"],
|
|
351
|
-
selectors: [
|
|
352
|
-
{ argumentIndex: 4, entity: "task", provider: "tasks", actionTarget: true, statuses: ["active"] },
|
|
353
|
-
{
|
|
354
|
-
argumentIndex: 5,
|
|
355
|
-
entity: "task-role",
|
|
356
|
-
provider: "task-roles",
|
|
357
|
-
dependsOn: 4,
|
|
358
|
-
actionTarget: true
|
|
359
|
-
}
|
|
360
|
-
],
|
|
361
|
-
trailingOptions: { "--reason": "value" }
|
|
362
|
-
},
|
|
363
349
|
...["view", "takeover", "release"].map((command) => ({
|
|
364
350
|
commandPath: ["task", "role", command],
|
|
365
351
|
selectors: [
|
package/dist/cli.js
CHANGED
|
@@ -39,6 +39,7 @@ import { runProjectCommand } from "./commands/projectCommands.js";
|
|
|
39
39
|
import { runProfileCommand } from "./commands/profileCommands.js";
|
|
40
40
|
import { dispatchPreparedReviewRound, failPendingReviewRound, preserveReviewRoundWorkspace, parseTaskCompletionRequest, parseTaskFinalReviewContractRebindRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, resolvedExecutionStageRetryGroup, validateTaskArchiveRequest } from "./commands/taskCommands.js";
|
|
41
41
|
import { taskActor } from "./commands/taskActor.js";
|
|
42
|
+
import { parseTaskExecutionStartRequest, parseTaskExecutionStopRequest, finalizeStoppedTaskExecution, startTaskExecutionCommand, stopTaskExecutionCommand } from "./commands/taskExecutionCommands.js";
|
|
42
43
|
import { isCurrentGlobalOperator } from "./commands/taskInputCommands.js";
|
|
43
44
|
import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
|
|
44
45
|
import { runTaskChangeSetCommand } from "./commands/taskChangeSetCommands.js";
|
|
@@ -808,6 +809,47 @@ export async function main() {
|
|
|
808
809
|
return;
|
|
809
810
|
}
|
|
810
811
|
if (resolved[0] === "task") {
|
|
812
|
+
if (resolved[1] === "execution") {
|
|
813
|
+
if (resolved[2] === "stop") {
|
|
814
|
+
const request = parseTaskExecutionStopRequest(resolved.slice(3));
|
|
815
|
+
const result = stopTaskExecutionCommand(request, store, { environment: process.env });
|
|
816
|
+
try {
|
|
817
|
+
await ensureFileTaskController(home, { environment: process.env });
|
|
818
|
+
await runtime.stopTaskDurableJobs(result.taskId);
|
|
819
|
+
await runtime.stopTaskRoleSessions(result.taskId, result.roleNames);
|
|
820
|
+
await runtime.assertTaskPhysicalResourcesReleased(result.taskId);
|
|
821
|
+
finalizeStoppedTaskExecution(result.taskId, store);
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
825
|
+
throw runtimeError(`Task execution is stopped and durable progress is preserved, but physical runtime cleanup failed: ${message}`);
|
|
826
|
+
}
|
|
827
|
+
emit(result.output, false, result);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
if (resolved[2] === "start") {
|
|
831
|
+
const taskId = parseTaskExecutionStartRequest(resolved.slice(3));
|
|
832
|
+
const task = store.getTask(taskId);
|
|
833
|
+
if (task === null)
|
|
834
|
+
throw usageError(`Task not found: ${taskId}.`);
|
|
835
|
+
// Reject managed Task callers before inspecting or starting runtime resources.
|
|
836
|
+
if (taskActor(process.env, taskId) === "leader") {
|
|
837
|
+
throw usageError("Task execution stop/start requires the global Operator or a human user.");
|
|
838
|
+
}
|
|
839
|
+
if (task.executionGate.state === "stopped") {
|
|
840
|
+
await runtime.assertTaskPhysicalResourcesReleased(taskId);
|
|
841
|
+
}
|
|
842
|
+
await ensureFileTaskController(home, { environment: process.env });
|
|
843
|
+
const result = startTaskExecutionCommand(taskId, store, { environment: process.env });
|
|
844
|
+
// Idempotent start is also a reliable kick: if an earlier caller
|
|
845
|
+
// committed the gate but lost its Controller acknowledgement, retrying
|
|
846
|
+
// start re-signals the same durable wake without creating another one.
|
|
847
|
+
await runtime.notifyMailboxChanged?.({ kind: "role", taskId, roleName: "leader" });
|
|
848
|
+
emit(result.output, false, result);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
throw usageError("Task execution usage: yui task execution <stop|start> ...");
|
|
852
|
+
}
|
|
811
853
|
if (resolved[1] === "integration") {
|
|
812
854
|
const result = await runTaskIntegrationCommand(resolved.slice(2), store, home, {
|
|
813
855
|
environment: process.env,
|
|
@@ -139,7 +139,7 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
139
139
|
}
|
|
140
140
|
if (report.sessions.status === "ok" && report.sessions.data !== undefined) {
|
|
141
141
|
const sessions = report.sessions.data;
|
|
142
|
-
lines.push("", `Sessions: ${sessions.generations} generations · ${sessions.broken} broken · ${sessions.stopped} stopped · ${sessions.other} other`, `Resets: ${sessions.resets} ·
|
|
142
|
+
lines.push("", `Sessions: ${sessions.generations} generations · ${sessions.broken} broken · ${sessions.stopped} stopped · ${sessions.other} other`, `Resets: ${sessions.resets} · Historical conversation switches ${sessions.conversationSwitches}`
|
|
143
143
|
+ ` · lifecycle events ${sessions.lifecycleEvents} · stop failures ${sessions.stopFailures}`, `Terminal by Run relation: ${sessions.terminalByRunRelation.postRunYielded} post-run-yielded`
|
|
144
144
|
+ ` · ${sessions.terminalByRunRelation.runFailed} run-failed`
|
|
145
145
|
+ ` · ${sessions.terminalByRunRelation.activeRun} active-run`
|