@zq-silk/yui 0.15.12 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +3 -2
- package/ARCHITECTURE.zh-CN.md +3 -2
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +57 -41
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +7 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1280 -1243
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/globalRoleCommands.js +53 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +1 -10
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +271 -710
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +17 -57
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +3 -3
- package/dist/commands/taskPublicationCommands.js +1 -1
- package/dist/commands/taskPublicationVerifyCommand.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +2 -3
- package/dist/context/taskCatalog.js +3 -5
- package/dist/context/taskContext.js +139 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +9 -22
- package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryDelivery.js +7 -7
- package/dist/controller/runtime.js +51 -44
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +60 -84
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +1 -9
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/projectMaintenanceLock.js +23 -64
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +13 -22
- package/dist/repository/taskWorkspacePreparer.js +29 -25
- package/dist/repository/workspaceCleanupInspection.js +5 -5
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +15 -15
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +4 -41
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +7 -7
- package/dist/scheduler/leaderWakeupProcessor.js +19 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +25 -33
- package/dist/scheduler/taskObservabilityProjection.js +0 -1
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +142 -1
- package/dist/storage/sqliteStore.js +156 -162
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +15 -7
- package/dist/storage/taskStore.js +3 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/taskSurface.js +4 -4
- package/dist/web/webServer.js +4 -6
- package/dist/web/webSnapshot.js +3 -83
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/docs/architecture/capabilities-and-resources.md +14 -2
- package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
- package/docs/managed-turn-and-session-runtime.md +83 -10
- package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
- package/docs/plugin-sdk.md +4 -2
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +91 -5
- package/docs/task-delivery.zh-CN.md +72 -4
- package/docs/task-discovery.md +10 -11
- package/docs/task-discovery.zh-CN.md +8 -9
- package/docs/testing/verification-levels.md +145 -9
- package/docs/testing/verification-levels.zh-CN.md +103 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +15 -10
- package/skills/yui-runtime/SKILL.md +6 -6
- package/skills/yui-runtime/references/recovery.md +47 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -377
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The deterministic double for every external system (GitHub, npm, Home,
|
|
3
|
-
* Controller). No real network, process, or filesystem side effect: the
|
|
4
|
-
* engine's at-most-once and recovery guarantees are proven against this.
|
|
5
|
-
*/
|
|
6
|
-
export function createFakeReleasePorts(scripts) {
|
|
7
|
-
const counters = new Map();
|
|
8
|
-
for (const stepId of Object.keys(scripts)) {
|
|
9
|
-
counters.set(stepId, { execute: 0, query: 0, keys: [] });
|
|
10
|
-
}
|
|
11
|
-
const tally = (stepId) => {
|
|
12
|
-
let calls = counters.get(stepId);
|
|
13
|
-
if (calls === undefined) {
|
|
14
|
-
calls = { execute: 0, query: 0, keys: [] };
|
|
15
|
-
counters.set(stepId, calls);
|
|
16
|
-
}
|
|
17
|
-
return calls;
|
|
18
|
-
};
|
|
19
|
-
const next = (queue, stepId, kind, consumed) => {
|
|
20
|
-
if (queue === undefined || consumed >= queue.length) {
|
|
21
|
-
throw new Error(`Fake release ports: no more scripted ${kind} outcomes for step ${stepId}.`);
|
|
22
|
-
}
|
|
23
|
-
const entry = queue[consumed];
|
|
24
|
-
return typeof entry === "function" ? entry() : entry;
|
|
25
|
-
};
|
|
26
|
-
return {
|
|
27
|
-
async executeStep(input) {
|
|
28
|
-
const calls = tally(input.step.id);
|
|
29
|
-
const effect = next(scripts[input.step.id]?.execute, input.step.id, "execute", calls.execute);
|
|
30
|
-
calls.execute += 1;
|
|
31
|
-
calls.keys.push(input.idempotencyKey);
|
|
32
|
-
return effect;
|
|
33
|
-
},
|
|
34
|
-
async queryStepEffect(input) {
|
|
35
|
-
const calls = tally(input.step.id);
|
|
36
|
-
const query = next(scripts[input.step.id]?.query, input.step.id, "query", calls.query);
|
|
37
|
-
calls.query += 1;
|
|
38
|
-
return query;
|
|
39
|
-
},
|
|
40
|
-
calls(stepId) {
|
|
41
|
-
const calls = tally(stepId);
|
|
42
|
-
return { execute: calls.execute, query: calls.query, keys: Object.freeze([...calls.keys]) };
|
|
43
|
-
},
|
|
44
|
-
exhausted() {
|
|
45
|
-
for (const [stepId, script] of Object.entries(scripts)) {
|
|
46
|
-
const calls = counters.get(stepId);
|
|
47
|
-
if ((script.execute?.length ?? 0) !== calls.execute)
|
|
48
|
-
return false;
|
|
49
|
-
if ((script.query?.length ?? 0) !== calls.query)
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
3
|
-
import { createSessionOwnerIdentity, sessionOwnerProcessKey } from "./sessionOwnerIdentity.js";
|
|
4
|
-
/**
|
|
5
|
-
* Durable, enumerable physical owner records for runtime Sessions.
|
|
6
|
-
*
|
|
7
|
-
* One JSON file per process identity under `<home>/runtime/session-owners/`. The
|
|
8
|
-
* directory is runtime state (like the tmux socket), not aggregate domain
|
|
9
|
-
* state, so it needs no schema migration; it survives a Controller restart and
|
|
10
|
-
* stays enumerable after the durable Session map or history is cleared, which
|
|
11
|
-
* is exactly the gap the audit proved (durable pointers empty, physical
|
|
12
|
-
* processes still live).
|
|
13
|
-
*/
|
|
14
|
-
export class FileSessionOwnerRegistry {
|
|
15
|
-
#directory;
|
|
16
|
-
constructor(home) {
|
|
17
|
-
this.#directory = join(resolve(home), "runtime", "session-owners");
|
|
18
|
-
}
|
|
19
|
-
get directory() {
|
|
20
|
-
return this.#directory;
|
|
21
|
-
}
|
|
22
|
-
/** Atomically records (or replaces) one Session's owner identity. */
|
|
23
|
-
record(identity) {
|
|
24
|
-
mkdirSync(this.#directory, { recursive: true, mode: 0o700 });
|
|
25
|
-
const target = this.#path(sessionOwnerProcessKey(identity));
|
|
26
|
-
const temporary = `${target}.tmp-${process.pid}`;
|
|
27
|
-
writeFileSync(temporary, JSON.stringify(identity, null, 2) + "\n", { mode: 0o600 });
|
|
28
|
-
renameSync(temporary, target);
|
|
29
|
-
}
|
|
30
|
-
get(processKey) {
|
|
31
|
-
if (!isSafeProcessKey(processKey))
|
|
32
|
-
return null;
|
|
33
|
-
try {
|
|
34
|
-
return parseOwnerRecord(readFileSync(this.#path(processKey), "utf8"));
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
list() {
|
|
41
|
-
let entries;
|
|
42
|
-
try {
|
|
43
|
-
entries = readdirSync(this.#directory, { encoding: "utf8" });
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
return [];
|
|
47
|
-
}
|
|
48
|
-
const records = [];
|
|
49
|
-
for (const entry of entries) {
|
|
50
|
-
if (!entry.endsWith(".json"))
|
|
51
|
-
continue;
|
|
52
|
-
try {
|
|
53
|
-
records.push(parseOwnerRecord(readFileSync(join(this.#directory, entry), "utf8")));
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// A malformed record must not hide the exact candidates beside it.
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
records.sort((left, right) => left.recordedAt.localeCompare(right.recordedAt));
|
|
60
|
-
return records;
|
|
61
|
-
}
|
|
62
|
-
listForOwner(owner) {
|
|
63
|
-
return this.list().filter((record) => (record.owner.scope === owner.scope
|
|
64
|
-
&& (owner.scope === "global"
|
|
65
|
-
|| record.owner.taskId === owner.taskId)
|
|
66
|
-
&& record.owner.roleName === owner.roleName));
|
|
67
|
-
}
|
|
68
|
-
/** Removes a record whose physical resources were proven absent. */
|
|
69
|
-
remove(processKey) {
|
|
70
|
-
if (!isSafeProcessKey(processKey))
|
|
71
|
-
return;
|
|
72
|
-
try {
|
|
73
|
-
rmSync(this.#path(processKey), { force: true });
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
// A stale record is harmless: reconciliation re-verifies physical
|
|
77
|
-
// identity before acting on it.
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
exists() {
|
|
81
|
-
return existsSync(this.#directory);
|
|
82
|
-
}
|
|
83
|
-
#path(processKey) {
|
|
84
|
-
if (!isSafeProcessKey(processKey))
|
|
85
|
-
throw new Error("Invalid process key.");
|
|
86
|
-
return join(this.#directory, `${processKey}.json`);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function isSafeProcessKey(processKey) {
|
|
90
|
-
return /^[1-9][0-9]*-[0-9]+$/u.test(processKey);
|
|
91
|
-
}
|
|
92
|
-
function parseOwnerRecord(raw) {
|
|
93
|
-
const parsed = JSON.parse(raw);
|
|
94
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
95
|
-
throw new Error("Session owner record is invalid.");
|
|
96
|
-
}
|
|
97
|
-
const record = parsed;
|
|
98
|
-
const owner = record.owner;
|
|
99
|
-
const tmux = record.tmux;
|
|
100
|
-
const providerRoot = record.providerRoot;
|
|
101
|
-
if (!["launch-env", "pane-pid", "owned-child"].includes(String(providerRoot.attribution))) {
|
|
102
|
-
throw new Error("Session owner process attribution is invalid.");
|
|
103
|
-
}
|
|
104
|
-
return createSessionOwnerIdentity({
|
|
105
|
-
owner: {
|
|
106
|
-
scope: owner.scope === "global" ? "global" : "task",
|
|
107
|
-
...(owner.taskId === undefined ? {} : { taskId: String(owner.taskId) }),
|
|
108
|
-
roleName: String(owner.roleName)
|
|
109
|
-
},
|
|
110
|
-
agentId: String(record.agentId),
|
|
111
|
-
adapterId: String(record.adapterId),
|
|
112
|
-
...(record.nativeSessionId === undefined
|
|
113
|
-
? {}
|
|
114
|
-
: { nativeSessionId: String(record.nativeSessionId) }),
|
|
115
|
-
tmux: {
|
|
116
|
-
serverName: String(tmux.serverName),
|
|
117
|
-
socketPath: String(tmux.socketPath),
|
|
118
|
-
sessionName: String(tmux.sessionName),
|
|
119
|
-
windowName: String(tmux.windowName),
|
|
120
|
-
...(tmux.panePid === undefined ? {} : { panePid: Number(tmux.panePid) })
|
|
121
|
-
},
|
|
122
|
-
providerRoot: {
|
|
123
|
-
pid: Number(providerRoot.pid),
|
|
124
|
-
startIdentity: String(providerRoot.startIdentity),
|
|
125
|
-
...(providerRoot.processGroupId === undefined
|
|
126
|
-
? {}
|
|
127
|
-
: { processGroupId: Number(providerRoot.processGroupId) }),
|
|
128
|
-
...(providerRoot.processSessionId === undefined
|
|
129
|
-
? {}
|
|
130
|
-
: { processSessionId: Number(providerRoot.processSessionId) }),
|
|
131
|
-
attribution: providerRoot.attribution === "pane-pid" ? "pane-pid"
|
|
132
|
-
: providerRoot.attribution === "owned-child" ? "owned-child" : "launch-env"
|
|
133
|
-
},
|
|
134
|
-
...(record.runtimeRoot === undefined ? {} : { runtimeRoot: String(record.runtimeRoot) }),
|
|
135
|
-
recordedAt: new Date(String(record.recordedAt))
|
|
136
|
-
});
|
|
137
|
-
}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { isCompletedTaskReviewEvidenceFromRuns } from "../review/reviewAcceptance.js";
|
|
2
|
-
const OPEN_WORK_ITEM_STATUSES = new Set(["open"]);
|
|
3
|
-
export function detectDeliveryDuplicates(facts, intent) {
|
|
4
|
-
switch (intent.kind) {
|
|
5
|
-
case "create-work-item":
|
|
6
|
-
return detectWorkItemDuplicates(facts, intent.scope);
|
|
7
|
-
case "integration-start":
|
|
8
|
-
return detectIntegrationDuplicates(facts, intent);
|
|
9
|
-
case "review-request":
|
|
10
|
-
return detectReviewDuplicates(facts, intent);
|
|
11
|
-
case "complete-task":
|
|
12
|
-
return detectCompleteDuplicates(facts);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Apply the configured mode. `display` never interferes; `warn` reports every
|
|
17
|
-
* match as a warning; `enforce` hard-blocks on exact evidence and warns on
|
|
18
|
-
* suspected duplicates.
|
|
19
|
-
*/
|
|
20
|
-
export function evaluateDeliveryGuard(duplicates, mode) {
|
|
21
|
-
if (mode === "display" || duplicates.length === 0) {
|
|
22
|
-
return { blocked: null, warnings: [] };
|
|
23
|
-
}
|
|
24
|
-
const exact = duplicates.filter((duplicate) => duplicate.severity === "exact");
|
|
25
|
-
const suspected = duplicates.filter((duplicate) => duplicate.severity === "suspected");
|
|
26
|
-
if (mode === "enforce" && exact.length > 0) {
|
|
27
|
-
return { blocked: exact[0], warnings: suspected };
|
|
28
|
-
}
|
|
29
|
-
return { blocked: null, warnings: [...exact, ...suspected] };
|
|
30
|
-
}
|
|
31
|
-
export function formatDeliveryDuplicate(duplicate) {
|
|
32
|
-
const refs = duplicate.refs.map((ref) => `${ref.kind} ${ref.id}`).join(", ");
|
|
33
|
-
const reuse = duplicate.reuseCommand === undefined
|
|
34
|
-
? ""
|
|
35
|
-
: ` Existing proof: ${duplicate.reuseCommand}`;
|
|
36
|
-
return `${duplicate.severity === "exact" ? "Exact duplicate" : "Suspected duplicate"}: ${duplicate.reason} (${refs}).${reuse}`;
|
|
37
|
-
}
|
|
38
|
-
function detectWorkItemDuplicates(facts, scope) {
|
|
39
|
-
const wanted = normalizeScope(scope);
|
|
40
|
-
const duplicates = [];
|
|
41
|
-
for (const item of facts.workItems) {
|
|
42
|
-
const existing = normalizeScope({
|
|
43
|
-
title: item.title,
|
|
44
|
-
objective: item.objective,
|
|
45
|
-
acceptance: item.acceptance,
|
|
46
|
-
writeProjectIds: item.writeProjectIds
|
|
47
|
-
});
|
|
48
|
-
const sameScope = existing.title === wanted.title
|
|
49
|
-
&& existing.objective === wanted.objective
|
|
50
|
-
&& existing.acceptance === wanted.acceptance
|
|
51
|
-
&& existing.writeProjectIds === wanted.writeProjectIds;
|
|
52
|
-
const ref = { kind: "work-item", id: item.id };
|
|
53
|
-
if (sameScope && OPEN_WORK_ITEM_STATUSES.has(item.status)) {
|
|
54
|
-
duplicates.push({
|
|
55
|
-
severity: "exact",
|
|
56
|
-
reason: `Work Item ${item.id} is already open with the identical scope`,
|
|
57
|
-
refs: [ref],
|
|
58
|
-
reuseCommand: `yui task work show ${facts.task.id}/${item.id}`
|
|
59
|
-
});
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (sameScope && item.status === "accepted") {
|
|
63
|
-
duplicates.push({
|
|
64
|
-
severity: "suspected",
|
|
65
|
-
reason: `Work Item ${item.id} already delivered the identical scope; re-creating it may be a duplicate successor`,
|
|
66
|
-
refs: [ref],
|
|
67
|
-
reuseCommand: `yui task work show ${facts.task.id}/${item.id}`
|
|
68
|
-
});
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (!sameScope
|
|
72
|
-
&& OPEN_WORK_ITEM_STATUSES.has(item.status)
|
|
73
|
-
&& existing.writeProjectIds === wanted.writeProjectIds
|
|
74
|
-
&& wanted.writeProjectIds.length > 0
|
|
75
|
-
&& overlap(existing.acceptance, wanted.acceptance)) {
|
|
76
|
-
duplicates.push({
|
|
77
|
-
severity: "suspected",
|
|
78
|
-
reason: `Open Work Item ${item.id} shares the same Project scope and acceptance lines`,
|
|
79
|
-
refs: [ref]
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return duplicates;
|
|
84
|
-
}
|
|
85
|
-
function detectIntegrationDuplicates(facts, intent) {
|
|
86
|
-
const duplicates = [];
|
|
87
|
-
for (const attempt of facts.integrations) {
|
|
88
|
-
if (attempt.projectId !== intent.projectId)
|
|
89
|
-
continue;
|
|
90
|
-
const sameSource = attempt.source.kind === "work-item"
|
|
91
|
-
&& attempt.source.workItemId === intent.workItemId
|
|
92
|
-
&& attempt.source.resultCommit === intent.resultCommit;
|
|
93
|
-
const ref = { kind: "integration-attempt", id: attempt.id };
|
|
94
|
-
if (sameSource && attempt.status === "committed") {
|
|
95
|
-
duplicates.push({
|
|
96
|
-
severity: "exact",
|
|
97
|
-
reason: `Integration ${attempt.id} already committed this exact WorkItem result`,
|
|
98
|
-
refs: [ref],
|
|
99
|
-
reuseCommand: `yui task integration show ${facts.task.id}/${attempt.id}`
|
|
100
|
-
});
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
if (sameSource
|
|
104
|
-
&& (attempt.status === "running" || attempt.status === "validating")) {
|
|
105
|
-
duplicates.push({
|
|
106
|
-
severity: "suspected",
|
|
107
|
-
reason: `Integration ${attempt.id} is already ${attempt.status} for this WorkItem result`,
|
|
108
|
-
refs: [ref],
|
|
109
|
-
reuseCommand: `yui task integration show ${facts.task.id}/${attempt.id}`
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return duplicates;
|
|
114
|
-
}
|
|
115
|
-
function detectReviewDuplicates(facts, intent) {
|
|
116
|
-
const wanted = new Set(intent.taskCandidateCommits.map((commit) => commit.toLowerCase()));
|
|
117
|
-
const duplicates = [];
|
|
118
|
-
for (const round of facts.reviewRounds) {
|
|
119
|
-
if ((round.scope ?? "work-item") !== "task")
|
|
120
|
-
continue;
|
|
121
|
-
if (round.reviewerRoleName !== intent.reviewerRoleName)
|
|
122
|
-
continue;
|
|
123
|
-
const commits = new Set((round.taskCandidate?.projects ?? []).map((project) => project.commit.toLowerCase()));
|
|
124
|
-
const sameCandidate = commits.size === wanted.size
|
|
125
|
-
&& commits.size > 0
|
|
126
|
-
&& [...wanted].every((commit) => commits.has(commit));
|
|
127
|
-
if (!sameCandidate)
|
|
128
|
-
continue;
|
|
129
|
-
const ref = { kind: "review-round", id: round.id };
|
|
130
|
-
if (isCompletedTaskReviewEvidenceFromRuns(round, facts.reviewOutcomeEvidence?.runs ?? [])) {
|
|
131
|
-
duplicates.push({
|
|
132
|
-
severity: "exact",
|
|
133
|
-
reason: `Task-final Review ${round.id} already attests this exact head`,
|
|
134
|
-
refs: [ref]
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
else if (round.status === "pending" || round.status === "running") {
|
|
138
|
-
duplicates.push({
|
|
139
|
-
severity: "suspected",
|
|
140
|
-
reason: `Task-final Review ${round.id} is already ${round.status} for this exact head`,
|
|
141
|
-
refs: [ref]
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return duplicates;
|
|
146
|
-
}
|
|
147
|
-
function detectCompleteDuplicates(facts) {
|
|
148
|
-
if (facts.task.status === "completed" || facts.task.status === "archived") {
|
|
149
|
-
return [{
|
|
150
|
-
severity: "exact",
|
|
151
|
-
reason: `Task ${facts.task.id} is already ${facts.task.status}`,
|
|
152
|
-
refs: [{ kind: "task", id: facts.task.id }]
|
|
153
|
-
}];
|
|
154
|
-
}
|
|
155
|
-
return [];
|
|
156
|
-
}
|
|
157
|
-
function normalizeScope(scope) {
|
|
158
|
-
return {
|
|
159
|
-
title: scope.title.trim().toLowerCase(),
|
|
160
|
-
objective: scope.objective.trim().toLowerCase(),
|
|
161
|
-
acceptance: [...scope.acceptance]
|
|
162
|
-
.map((line) => line.trim().toLowerCase())
|
|
163
|
-
.filter((line) => line.length > 0)
|
|
164
|
-
.sort()
|
|
165
|
-
.join("\n"),
|
|
166
|
-
writeProjectIds: [...scope.writeProjectIds].map((id) => id.trim()).sort().join(",")
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
function overlap(left, right) {
|
|
170
|
-
const rightLines = new Set(right.split("\n"));
|
|
171
|
-
return left.split("\n").some((line) => line.length > 0 && rightLines.has(line));
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Default number of consecutive completed Leader turns that must produce no
|
|
175
|
-
* durable delivery change before the budget is exhausted.
|
|
176
|
-
*/
|
|
177
|
-
export const DEFAULT_SEMANTIC_BUDGET_RUNS = 3;
|
|
178
|
-
/**
|
|
179
|
-
* Evaluate the semantic-progress budget from existing records only. The
|
|
180
|
-
* budget is exhausted when the last `runs` Leader AgentRuns all completed and no
|
|
181
|
-
* WorkItem/ChangeSet/Integration/Review record changed at or after the first
|
|
182
|
-
* of those AgentRuns. Active execution (any AgentRun) never exhausts the budget: a
|
|
183
|
-
* slow but progressing Worker or Leader is never interrupted.
|
|
184
|
-
*/
|
|
185
|
-
export function evaluateSemanticBudget(facts, runs = DEFAULT_SEMANTIC_BUDGET_RUNS) {
|
|
186
|
-
if (facts.activeRuns.length > 0) {
|
|
187
|
-
return {
|
|
188
|
-
exhausted: false,
|
|
189
|
-
reason: "Active execution is in flight; the budget never interrupts a progressing AgentRun.",
|
|
190
|
-
evidence: facts.activeRuns.map((run) => run.id)
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
const recentLeaderRuns = facts.leaderRuns
|
|
194
|
-
.filter((run) => run.status === "completed")
|
|
195
|
-
.slice(-runs);
|
|
196
|
-
if (recentLeaderRuns.length < runs) {
|
|
197
|
-
return {
|
|
198
|
-
exhausted: false,
|
|
199
|
-
reason: `Fewer than ${runs} consecutive completed Leader runs.`,
|
|
200
|
-
evidence: recentLeaderRuns.map((run) => run.id)
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
const firstStartedAt = Math.min(...recentLeaderRuns.map((run) => Date.parse(run.createdAt)));
|
|
204
|
-
const changed = latestDeliveryChangeAt(facts);
|
|
205
|
-
if (changed >= firstStartedAt) {
|
|
206
|
-
return {
|
|
207
|
-
exhausted: false,
|
|
208
|
-
reason: "A delivery record changed during the recent Leader turns.",
|
|
209
|
-
evidence: recentLeaderRuns.map((run) => run.id)
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
return {
|
|
213
|
-
exhausted: true,
|
|
214
|
-
reason: `${runs} consecutive Leader runs produced no durable delivery change; record a diagnosis and wait for new facts instead of creating more records.`,
|
|
215
|
-
evidence: recentLeaderRuns.map((run) => run.id)
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
function latestDeliveryChangeAt(facts) {
|
|
219
|
-
const timestamps = [
|
|
220
|
-
...facts.workItems.map((item) => Date.parse(item.updatedAt)),
|
|
221
|
-
...facts.changeSets.map((changeSet) => Date.parse(changeSet.createdAt)),
|
|
222
|
-
...facts.integrations.map((attempt) => Math.max(Date.parse(attempt.updatedAt), Date.parse(attempt.endedAt ?? attempt.updatedAt))),
|
|
223
|
-
...facts.reviewRounds.map((round) => Math.max(Date.parse(round.createdAt), Date.parse(round.endedAt ?? round.createdAt)))
|
|
224
|
-
];
|
|
225
|
-
return timestamps.length === 0 ? 0 : Math.max(...timestamps);
|
|
226
|
-
}
|
|
File without changes
|