@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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
/** Read the existing source owner once and freeze its exact Candidate evidence. */
|
|
3
|
+
export async function snapshotWorkItemCandidate(store, preparer, taskId, workItemId, taskFinalReviewContract) {
|
|
4
|
+
const item = store.getWorkItem(taskId, workItemId);
|
|
5
|
+
if (item === null)
|
|
6
|
+
throw usageError(`Work Item not found: ${taskId}/${workItemId}.`);
|
|
7
|
+
const workspace = store.getWorkItemWorkspace(taskId, workItemId);
|
|
8
|
+
if (workspace !== null) {
|
|
9
|
+
return { candidateGitSnapshot: await preparer.snapshotCandidateWorkspace(workspace) };
|
|
10
|
+
}
|
|
11
|
+
// Read-only results freeze their summary/artifact evidence, not Git output.
|
|
12
|
+
if (item.writeProjectIds.length === 0)
|
|
13
|
+
return {};
|
|
14
|
+
if (taskFinalReviewContract === undefined) {
|
|
15
|
+
throw usageError(`Work Item ${taskId}/${workItemId} has no managed Candidate workspace. `
|
|
16
|
+
+ `Use yui task work isolate ${taskId}/${workItemId} before implementing writable work.`);
|
|
17
|
+
}
|
|
18
|
+
// Preserve the existing exact Task-final metadata-only Candidate contract.
|
|
19
|
+
// It is not the ordinary route for a new independently accepted code result.
|
|
20
|
+
const main = store.getTaskWorkspace(taskId);
|
|
21
|
+
if (main === null)
|
|
22
|
+
return {};
|
|
23
|
+
if (main.owner.type !== "task") {
|
|
24
|
+
throw usageError(`Task has no authoritative main workspace: ${taskId}.`);
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
return {
|
|
28
|
+
directTaskMainSnapshot: await preparer.snapshotDirectTaskMain(main, item.writeProjectIds)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw usageError(error instanceof Error ? error.message : String(error));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { dirname, join, relative, isAbsolute } from "node:path";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3
|
+
import { managedWorkspaceKey, managedWorktreeName } from "../worktree/managedWorkspace.js";
|
|
4
|
+
import { WorkItemChangeSetManager } from "../workspace/workItemChangeSetManager.js";
|
|
5
|
+
import { cleanupCheckFromError } from "../workspace/cleanupInspection.js";
|
|
6
|
+
import { projectTaskRemoteDeliveryFromStore } from "../commands/taskRemoteDeliveryCommand.js";
|
|
7
|
+
import { taskWorkspaceRefSegment } from "./taskWorkspaceIdentity.js";
|
|
8
|
+
import { integrationWorktreeIdentity, worktreeIdentity } from "./gitWorkspace.js";
|
|
9
|
+
/** Current owner facts + the same Git inspection used immediately before removal.
|
|
10
|
+
* No preparation, locks, Git refresh, runtime stop, DB writes or repair.
|
|
11
|
+
*/
|
|
12
|
+
export async function inspectWorkspaceCleanup(store, git, workspace, disposition, forceArchive = false) {
|
|
13
|
+
const owner = workspace.owner;
|
|
14
|
+
const task = store.getTask(owner.taskId);
|
|
15
|
+
if (task === null)
|
|
16
|
+
throw new Error(`Task not found: ${owner.taskId}.`);
|
|
17
|
+
const resource = managedWorkspaceKey(owner);
|
|
18
|
+
const sources = [resource];
|
|
19
|
+
const actions = workspaceCleanupActions(workspace);
|
|
20
|
+
const checks = [];
|
|
21
|
+
const add = (reason, detail, expected, observed) => checks.push({ resource, reason, status: "blocked", detail, expected, observed, sources, actions });
|
|
22
|
+
if (!isDeepStrictEqual(store.getManagedWorkspace(owner), workspace)) {
|
|
23
|
+
add("workspace-identity-mismatch", "Current workspace ownership differs from the inspected record.", "same managed workspace", "changed or missing");
|
|
24
|
+
return checks;
|
|
25
|
+
}
|
|
26
|
+
if (task.workspaceIdentity === undefined && workspace.entries.some(e => e.access === "write")) {
|
|
27
|
+
add("workspace-metadata-missing", "Task has no durable workspace identity; do not infer one from paths.", "recorded Task workspace identity", null);
|
|
28
|
+
return checks;
|
|
29
|
+
}
|
|
30
|
+
let roleName = managedWorktreeName(owner);
|
|
31
|
+
switch (owner.type) {
|
|
32
|
+
case "work-item": {
|
|
33
|
+
const item = store.getWorkItem(task.id, owner.workItemId);
|
|
34
|
+
if (item === null || !["accepted", "retired"].includes(item.status)) {
|
|
35
|
+
add("owner-unsettled", "WorkItem must be terminal before cleanup.", ["accepted", "retired"], item?.status ?? null);
|
|
36
|
+
}
|
|
37
|
+
if (item?.workspaceDisposition !== undefined && item.workspaceDisposition !== disposition) {
|
|
38
|
+
add("workspace-disposition-mismatch", "WorkItem cleanup disposition differs from the recorded decision.", item.workspaceDisposition, disposition);
|
|
39
|
+
}
|
|
40
|
+
if (item?.status === "accepted" && disposition === "integrated") {
|
|
41
|
+
const inspection = await new WorkItemChangeSetManager(store).inspectIntegrated(task.id, item.id);
|
|
42
|
+
checks.push(...inspection.checks);
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case "review-round": {
|
|
47
|
+
const round = store.getReviewRound(task.id, owner.reviewRoundId);
|
|
48
|
+
if (round === null || !["completed", "failed"].includes(round.status)) {
|
|
49
|
+
add("owner-unsettled", "ReviewRound must be terminal before cleanup.", ["completed", "failed"], round?.status ?? null);
|
|
50
|
+
}
|
|
51
|
+
if (round?.workspace === undefined || !isDeepStrictEqual(round.workspace, workspace)) {
|
|
52
|
+
add("workspace-metadata-mismatch", "ReviewRound and managed workspace records diverge.", "same workspace in ReviewRound and registry", "different or missing mirror");
|
|
53
|
+
}
|
|
54
|
+
if (round?.scope === "task")
|
|
55
|
+
roleName = `reviewer-${round.reviewerRoleName}`;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "execution-lane": {
|
|
59
|
+
const group = owner.purpose === "execution"
|
|
60
|
+
? store.getWorkItem(task.id, owner.workItemId)?.executionGroups.find(g => g.id === owner.executionGroupId)
|
|
61
|
+
: store.getReviewRound(task.id, owner.reviewRoundId)?.executionGroup;
|
|
62
|
+
const lane = group?.id === owner.executionGroupId
|
|
63
|
+
? group.lanes.find(l => l.id === owner.executionLaneId) : undefined;
|
|
64
|
+
if (lane === undefined || lane.disposition === "open") {
|
|
65
|
+
add("owner-unsettled", "Execution Lane is not terminally resolved.", "settled lane", lane?.disposition ?? null);
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "integration-attempt": {
|
|
70
|
+
const attempt = store.getIntegrationAttempt(task.id, owner.integrationAttemptId);
|
|
71
|
+
if (attempt === null || ["running", "blocked", "conflicted", "validating"].includes(attempt.status)) {
|
|
72
|
+
add("owner-unsettled", "IntegrationAttempt must be terminal before cleanup.", "terminal attempt", attempt?.status ?? null);
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case "task":
|
|
77
|
+
if (!["completed", "cancelled", "archived"].includes(task.status)) {
|
|
78
|
+
add("task-not-terminal", "Task must be completed or retired before archive cleanup.", ["completed", "cancelled", "archived"], task.status);
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
const delivery = owner.type === "task" && (disposition === "integrated" || task.status === "archived" || forceArchive)
|
|
83
|
+
? await readArchiveDelivery(store, git, task, forceArchive) : undefined;
|
|
84
|
+
if (delivery !== undefined && (!delivery.allMerged || !delivery.allVerified)) {
|
|
85
|
+
add("delivery-coverage", "Task main has commits without verified remote coverage; cleanup does not establish delivery.", { allMerged: true, allVerified: true }, { allMerged: delivery.allMerged, allVerified: delivery.allVerified });
|
|
86
|
+
}
|
|
87
|
+
for (const entry of workspace.entries.filter(e => e.access === "write")) {
|
|
88
|
+
const entryResource = `${resource}/${entry.projectId}`;
|
|
89
|
+
const entryChecks = [];
|
|
90
|
+
const check = (reason, detail, expected, observed) => entryChecks.push({ resource: entryResource, reason, status: "blocked", detail, expected, observed, sources, actions });
|
|
91
|
+
// Integration's durable root is its single Project worktree (not the
|
|
92
|
+
// multi-Project owner container used by the other workspace owners).
|
|
93
|
+
const container = owner.type === "integration-attempt" ? dirname(workspace.root) : workspace.root;
|
|
94
|
+
const expectedPath = join(container, entry.directory);
|
|
95
|
+
if (entry.path !== expectedPath) {
|
|
96
|
+
check("workspace-path-mismatch", "Recorded path is not the current owner-root/Project path; relocation is not proven.", { ownerRelativePath: entry.directory }, { ownerRelativePath: safeRelativePath(container, entry.path) });
|
|
97
|
+
checks.push(...entryChecks);
|
|
98
|
+
continue; // Never inspect a foreign/mismatched path.
|
|
99
|
+
}
|
|
100
|
+
const taskSegment = taskWorkspaceRefSegment(task);
|
|
101
|
+
const expectedBranch = owner.type === "integration-attempt"
|
|
102
|
+
? integrationWorktreeIdentity(taskSegment, owner.integrationAttemptId).branch
|
|
103
|
+
: worktreeIdentity(taskSegment, roleName).branch;
|
|
104
|
+
if (entry.branch !== expectedBranch) {
|
|
105
|
+
check("workspace-identity-mismatch", "Recorded branch does not match the exact owner.", expectedBranch, entry.branch);
|
|
106
|
+
checks.push(...entryChecks);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const main = store.getTaskWorkspace(task.id)?.entries.find(e => e.projectId === entry.projectId);
|
|
111
|
+
if (main === undefined && owner.type !== "task") {
|
|
112
|
+
check("workspace-metadata-missing", "Task main Git repository reference is unavailable.", "Task main Project entry", null);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const state = owner.type === "task"
|
|
116
|
+
? await git.inspectTaskClone({ path: entry.path, container: workspace.root, directory: entry.directory,
|
|
117
|
+
taskSegment, branch: entry.branch })
|
|
118
|
+
: await git.inspectWorktree({ repositoryPath: main.path, container, directory: entry.directory,
|
|
119
|
+
taskSegment, roleName, expectedBranch });
|
|
120
|
+
if (state === "dirty")
|
|
121
|
+
check("dirty-worktree", "Uncommitted or untracked changes must be preserved.", "clean", "dirty");
|
|
122
|
+
if (owner.type === "task" && state !== "missing" && delivery !== undefined) {
|
|
123
|
+
const covered = delivery.projects.find(p => p.projectId === entry.projectId);
|
|
124
|
+
const expected = covered?.expectedLocalCommit;
|
|
125
|
+
const observed = (await git.inspect(entry.path, "HEAD")).baseCommit;
|
|
126
|
+
if (expected != null && expected !== observed && covered?.deliveryLocalCommit !== observed) {
|
|
127
|
+
check("head-mismatch", "Task main HEAD differs from the accepted head and its covered publication candidate.", { acceptedCommit: expected, deliveryLocalCommit: covered?.deliveryLocalCommit ?? null }, observed);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
entryChecks.push(...cleanupCheckFromError(error, entryResource, sources, actions));
|
|
134
|
+
}
|
|
135
|
+
checks.push(...entryChecks);
|
|
136
|
+
}
|
|
137
|
+
return checks;
|
|
138
|
+
}
|
|
139
|
+
/** Reuse Publication's projection, supplying live heads only where its existing
|
|
140
|
+
* contract asks for them (active/cancelled). Completed/archived heads stay frozen.
|
|
141
|
+
* Never prepare a workspace or substitute an old binding's commit for a failed read.
|
|
142
|
+
*/
|
|
143
|
+
export async function readArchiveDelivery(store, git, task, forceArchive = false) {
|
|
144
|
+
const projects = [];
|
|
145
|
+
// Force admission preserves its existing store-only evidence; it never
|
|
146
|
+
// freezes new delivery heads merely because an inspection observed them.
|
|
147
|
+
if (!forceArchive && (task.status === "active" || task.status === "cancelled") && task.workspaceIdentity !== undefined) {
|
|
148
|
+
const main = store.getTaskWorkspace(task.id);
|
|
149
|
+
if (main?.owner.type === "task" && main.owner.taskId === task.id) {
|
|
150
|
+
const branch = worktreeIdentity(taskWorkspaceRefSegment(task), "main").branch;
|
|
151
|
+
for (const entry of main.entries.filter(e => e.access === "write")) {
|
|
152
|
+
if (entry.path !== join(main.root, entry.directory) || entry.branch !== branch)
|
|
153
|
+
continue;
|
|
154
|
+
try {
|
|
155
|
+
const head = await git.inspect(entry.path, "HEAD");
|
|
156
|
+
if (head.root === entry.path && await git.headRef(entry.path) === branch) {
|
|
157
|
+
projects.push({ projectId: entry.projectId, commit: head.baseCommit });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch { /* Failed reads remain unavailable in the existing projection. */ }
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return projectTaskRemoteDeliveryFromStore(store, task, { projects });
|
|
165
|
+
}
|
|
166
|
+
export function workspaceCleanupActions(workspace) {
|
|
167
|
+
const owner = workspace.owner;
|
|
168
|
+
switch (owner.type) {
|
|
169
|
+
case "task": return [`yui task remote-delivery ${owner.taskId}`, `yui task show ${owner.taskId}`];
|
|
170
|
+
case "work-item": return [`yui task work show ${owner.taskId}/${owner.workItemId}`,
|
|
171
|
+
`yui task work cleanup ${owner.taskId}/${owner.workItemId} --integrated|--abandon`];
|
|
172
|
+
case "review-round": return [`yui task work review show ${owner.taskId}/${owner.reviewRoundId}`,
|
|
173
|
+
`yui task work review cleanup ${owner.taskId}/${owner.reviewRoundId}`];
|
|
174
|
+
case "integration-attempt": return [`yui task integration show ${owner.taskId}/${owner.integrationAttemptId}`,
|
|
175
|
+
`yui task integration cleanup ${owner.taskId}/${owner.integrationAttemptId}`];
|
|
176
|
+
case "execution-lane": return owner.purpose === "execution"
|
|
177
|
+
? [`yui task work show ${owner.taskId}/${owner.workItemId}`,
|
|
178
|
+
`yui task work cleanup ${owner.taskId}/${owner.workItemId} --integrated|--abandon`]
|
|
179
|
+
: [`yui task work review show ${owner.taskId}/${owner.reviewRoundId}`,
|
|
180
|
+
`yui task work review cleanup ${owner.taskId}/${owner.reviewRoundId}`];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/** Only expose paths inside this already-authorized owner, never foreign paths. */
|
|
184
|
+
export function safeRelativePath(root, path) {
|
|
185
|
+
const value = relative(root, path);
|
|
186
|
+
return value === ".." || value.startsWith("../") || isAbsolute(value) ? "[outside owner root]" : value || ".";
|
|
187
|
+
}
|
|
@@ -11,6 +11,7 @@ import { existsSync, lstatSync, readFileSync, readdirSync, statSync } from "node
|
|
|
11
11
|
import { basename, join, resolve } from "node:path";
|
|
12
12
|
import { promisify } from "node:util";
|
|
13
13
|
import { managedProjectPath } from "../repository/project.js";
|
|
14
|
+
import { managedRuntimeRoot } from "../storage/homeLayout.js";
|
|
14
15
|
import { isResourceQuarantinePath } from "./resourceRegistry.js";
|
|
15
16
|
import { createResourceRecord, isReleaseNamespacePath, isTerminalTaskStatus } from "./resourceTypes.js";
|
|
16
17
|
const executeFile = promisify(execFile);
|
|
@@ -164,8 +165,8 @@ export async function discoverResources(input) {
|
|
|
164
165
|
function discoverRuntimeArtifacts(home, input) {
|
|
165
166
|
const discovered = [];
|
|
166
167
|
const now = input.now;
|
|
167
|
-
// 3a. Task runtime isolation roots: <home
|
|
168
|
-
const runtimeRoot =
|
|
168
|
+
// 3a. Task runtime isolation roots: <home>/runtime/task-runtimes/<taskId>/<owner>/<launch>/
|
|
169
|
+
const runtimeRoot = managedRuntimeRoot(home);
|
|
169
170
|
if (existsSync(runtimeRoot)) {
|
|
170
171
|
for (const taskEntry of safeReaddir(runtimeRoot)) {
|
|
171
172
|
if (!taskEntry.isDirectory())
|
|
@@ -21,7 +21,7 @@ export const AGENT_ERROR_CATEGORIES = Object.freeze([
|
|
|
21
21
|
]);
|
|
22
22
|
export function standardAgentError(input) {
|
|
23
23
|
const classification = input.classification ?? UNKNOWN_AGENT_ERROR_CLASSIFICATION;
|
|
24
|
-
const retryAfterMs = input.retryAfterMs;
|
|
24
|
+
const retryAfterMs = input.retryAfterMs ?? classification.retryAfterMs;
|
|
25
25
|
if (retryAfterMs !== undefined
|
|
26
26
|
&& (!Number.isSafeInteger(retryAfterMs) || retryAfterMs < 0)) {
|
|
27
27
|
throw new Error("Agent error retryAfterMs must be a non-negative safe integer.");
|
|
@@ -42,7 +42,8 @@ export function standardAgentError(input) {
|
|
|
42
42
|
sessionDisposition: input.sessionDisposition
|
|
43
43
|
?? classification.sessionDisposition
|
|
44
44
|
?? "unknown",
|
|
45
|
-
...(retryAfterMs === undefined ? {} : { retryAfterMs })
|
|
45
|
+
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
46
|
+
...(classification.retryable === undefined ? {} : { retryable: classification.retryable })
|
|
46
47
|
});
|
|
47
48
|
}
|
|
48
49
|
export const UNKNOWN_AGENT_ERROR_CLASSIFICATION = Object.freeze({
|
|
@@ -177,7 +178,8 @@ export function isStandardAgentError(value) {
|
|
|
177
178
|
&& ["accepted", "not-accepted", "unknown"].includes(error.inputDisposition ?? "")
|
|
178
179
|
&& ["recoverable", "unrecoverable", "unknown"].includes(error.sessionDisposition ?? "")
|
|
179
180
|
&& (error.retryAfterMs === undefined
|
|
180
|
-
|| (Number.isSafeInteger(error.retryAfterMs) && error.retryAfterMs >= 0))
|
|
181
|
+
|| (Number.isSafeInteger(error.retryAfterMs) && error.retryAfterMs >= 0))
|
|
182
|
+
&& (error.retryable === undefined || typeof error.retryable === "boolean");
|
|
181
183
|
}
|
|
182
184
|
/**
|
|
183
185
|
* Upper bound for one persisted raw payload. A Provider stack trace or a
|