@zq-silk/yui 2.2.0 → 2.4.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/README.md +19 -3
- package/dist/artifacts/artifactCapability.js +39 -10
- package/dist/artifacts/imageMetadata.js +120 -0
- package/dist/artifacts/managedGit.js +7 -4
- package/dist/artifacts/taskArtifactRepository.js +6 -0
- package/dist/cli/commandCatalog.js +25 -11
- package/dist/cli/interactionPolicy.js +4 -0
- package/dist/cli/invocationAuthority.js +1 -1
- package/dist/commands/executionAuditCommands.js +5 -0
- package/dist/commands/globalRoleCommands.js +41 -3
- package/dist/commands/projectCommands.js +89 -94
- package/dist/commands/roleSkillValidation.js +4 -8
- package/dist/commands/taskCommands.js +30 -9
- package/dist/commands/taskIntegrationCommands.js +5 -16
- package/dist/commands/taskUsageCommand.js +38 -0
- package/dist/context/roleSessionContext.js +18 -2
- package/dist/context/runContextPack.js +5 -1
- package/dist/context/sessionBootstrapManifest.js +10 -1
- package/dist/context/skillCatalog.js +108 -0
- package/dist/context/skillPackage.js +161 -0
- package/dist/context/taskContext.js +3 -2
- package/dist/context/taskSearch.js +127 -0
- package/dist/controlPlaneCli.js +51 -39
- package/dist/controller/clientRuntime.js +3 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +32 -6
- package/dist/controller/jobControl.js +2 -0
- package/dist/controller/nativeHostInput.js +37 -0
- package/dist/controller/runtime.js +93 -2
- package/dist/controller/runtimeEventInbox.js +2 -2
- package/dist/controller/runtimeEventProcessor.js +3 -3
- package/dist/doctor/doctor.js +10 -0
- package/dist/execution/reviewMainRun.js +1 -0
- package/dist/execution/workItemMainRun.js +1 -0
- package/dist/executor/codexConfigConflict.js +6 -4
- package/dist/executor/effectiveLaunch.js +24 -1
- package/dist/executor/fileRoleLaunchPlanner.js +13 -5
- package/dist/integration/gitIntegrationService.js +15 -79
- package/dist/integration/integrationSourceApplication.js +4 -2
- package/dist/job/httpPreview.js +61 -0
- package/dist/job/jobCleanupInspection.js +67 -0
- package/dist/job/jobRunner.js +12 -2
- package/dist/kernel/builtinCapabilities.js +3 -1
- package/dist/message/message.js +15 -1
- package/dist/output/rolePresentation.js +4 -1
- package/dist/repository/executionLaneGitSnapshot.js +2 -1
- package/dist/repository/gitCompatibility.js +62 -0
- package/dist/repository/gitWorkspace.js +86 -80
- package/dist/repository/gitWorktreeInventory.js +121 -0
- package/dist/repository/project.js +74 -25
- package/dist/repository/taskWorkspaceCoordinator.js +72 -145
- package/dist/repository/taskWorkspacePreparer.js +39 -43
- package/dist/repository/workspaceCleanupInspection.js +88 -7
- package/dist/resources/resourceDiscovery.js +2 -2
- package/dist/resources/resourceGc.js +7 -4
- package/dist/resources/resourceRegistrar.js +18 -11
- package/dist/runtime/agentEndpoint.js +8 -0
- package/dist/runtime/agentHost.js +170 -3
- package/dist/runtime/codexAppServerRuntime.js +1 -1
- package/dist/runtime/codexNativeAccess.js +175 -0
- package/dist/runtime/controlledCodexTui.js +116 -0
- package/dist/runtime/integrationRuntimeIsolation.js +21 -0
- package/dist/runtime/publicReply.js +23 -0
- package/dist/runtime/runtimeObservation.js +48 -0
- package/dist/runtime/structuredProviderHost.js +16 -7
- package/dist/runtime/taskCostMetrics.js +133 -0
- package/dist/runtime/taskRuntimeIsolation.js +91 -21
- package/dist/runtime/taskUsageMetrics.js +11 -2
- package/dist/runtime/taskUsageQuery.js +38 -0
- package/dist/scheduler/taskExecutionProjection.js +2 -0
- package/dist/scheduler/taskObservabilityProjection.js +9 -1
- package/dist/storage/homeLayout.js +13 -0
- package/dist/storage/sqliteSchema.js +44 -0
- package/dist/storage/sqliteStore.js +53 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskSearch.js +35 -0
- package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
- package/dist/task/archiveDiagnostics.js +26 -8
- package/dist/task/archivePreflight.js +26 -9
- package/dist/task/leaderArchive.js +9 -15
- package/dist/task/ordinaryArchive.js +42 -0
- package/dist/tmux/tmuxManager.js +8 -8
- package/dist/web/assets/assetManifest.js +11 -51
- package/dist/web/assets/client/app/catalog.js +215 -0
- package/dist/web/assets/client/app/main.js +148 -0
- package/dist/web/assets/client/app/selection.js +139 -0
- package/dist/web/assets/client/app/settings.js +326 -0
- package/dist/web/assets/client/app/taskView.js +280 -0
- package/dist/web/assets/client/domain/context.js +96 -0
- package/dist/web/assets/client/domain/records.js +126 -0
- package/dist/web/assets/client/domain/runs.js +119 -0
- package/dist/web/assets/client/domain/taskForms.js +197 -0
- package/dist/web/assets/client/domain/vocab.js +69 -0
- package/dist/web/assets/client/domain/work.js +116 -0
- package/dist/web/assets/client/i18n/en.js +900 -0
- package/dist/web/assets/client/i18n/zh-CN.js +898 -0
- package/dist/web/assets/client/index.js +114 -0
- package/dist/web/assets/client/layout/resizer.js +37 -0
- package/dist/web/assets/client/layout/router.js +35 -0
- package/dist/web/assets/client/layout/shortcuts.js +45 -0
- package/dist/web/assets/client/layout/sizing.js +95 -0
- package/dist/web/assets/client/layout/workspace.js +247 -0
- package/dist/web/assets/client/{api.js → lib/api.js} +28 -4
- package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
- package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
- package/dist/web/assets/client/lib/i18n.js +54 -0
- package/dist/web/assets/client/lib/markdown.js +167 -0
- package/dist/web/assets/client/lib/prefs.js +28 -0
- package/dist/web/assets/client/lib/theme.js +68 -0
- package/dist/web/assets/client/ui/containers.js +101 -0
- package/dist/web/assets/client/ui/controls.js +51 -0
- package/dist/web/assets/client/ui/forms.js +48 -0
- package/dist/web/assets/client/ui/metrics.js +137 -0
- package/dist/web/assets/client/ui/primitives.js +92 -0
- package/dist/web/assets/client/ui/text.js +33 -0
- package/dist/web/assets/client/ui/toast.js +12 -0
- package/dist/web/assets/client/views/bodySearch.js +172 -0
- package/dist/web/assets/client/views/createTask.js +85 -0
- package/dist/web/assets/client/views/dock/conversation.js +612 -0
- package/dist/web/assets/client/views/dock/discussion.js +173 -0
- package/dist/web/assets/client/views/dock/preview.js +129 -0
- package/dist/web/assets/client/views/dock/terminal.js +199 -0
- package/dist/web/assets/client/views/globalInput.js +67 -0
- package/dist/web/assets/client/{overview.js → views/overview.js} +62 -56
- package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
- package/dist/web/assets/client/views/skills.js +152 -0
- package/dist/web/assets/client/views/task/actions.js +36 -0
- package/dist/web/assets/client/views/task/delivery.js +185 -0
- package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
- package/dist/web/assets/client/views/task/files.js +272 -0
- package/dist/web/assets/client/views/task/observation.js +153 -0
- package/dist/web/assets/client/views/task/overview.js +161 -0
- package/dist/web/assets/client/views/task/page.js +131 -0
- package/dist/web/assets/client/views/task/records.js +163 -0
- package/dist/web/assets/client/views/task/runtime.js +130 -0
- package/dist/web/assets/client/views/task/timeline.js +190 -0
- package/dist/web/assets/shared/geometry.js +19 -0
- package/dist/web/assets/{icons.js → shared/icons.js} +4 -0
- package/dist/web/assets/shared/taskTabs.js +8 -0
- package/dist/web/assets/shared/themes.js +93 -0
- package/dist/web/assets/shell/dialogs.js +63 -0
- package/dist/web/assets/shell/dock.js +40 -0
- package/dist/web/assets/shell/index.js +40 -0
- package/dist/web/assets/shell/settings.js +34 -0
- package/dist/web/assets/shell/sidebar.js +45 -0
- package/dist/web/assets/styles/index.js +41 -0
- package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +15 -13
- package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
- package/dist/web/assets/styles/markdown.js +22 -3
- package/dist/web/assets/styles/tokens.js +10 -63
- package/dist/web/assets/styles/ui/controls.js +76 -0
- package/dist/web/assets/styles/ui/forms.js +46 -0
- package/dist/web/assets/styles/ui/surfaces.js +122 -0
- package/dist/web/assets/styles/views/delivery.js +29 -0
- package/dist/web/assets/styles/views/dock.js +71 -0
- package/dist/web/assets/styles/views/page.js +32 -0
- package/dist/web/assets/styles/views/records.js +70 -0
- package/dist/web/assets/styles/views/settings.js +26 -0
- package/dist/web/assets/styles/views/sidebar.js +74 -0
- package/dist/web/assets/styles/views/task.js +80 -0
- package/dist/web/assets/styles/views/terminal.js +38 -0
- package/dist/web/taskPreviews.js +205 -0
- package/dist/web/tmuxWebTerminal.js +16 -9
- package/dist/web/webConversation.js +373 -0
- package/dist/web/webMaterials.js +155 -0
- package/dist/web/webServer.js +306 -18
- package/dist/web/webSettings.js +331 -0
- package/dist/web/webSkills.js +86 -0
- package/dist/web/webSnapshot.js +40 -8
- package/dist/web/webTaskSurface.js +114 -9
- package/dist/workspace/gitChangeSetCapture.js +3 -1
- package/docs/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
- package/docs/observability/README.md +13 -3
- package/docs/observability/README.zh-CN.md +10 -3
- package/docs/release-workflow.md +53 -0
- package/docs/release-workflow.zh-CN.md +40 -0
- package/docs/storage-baseline.md +38 -1
- package/docs/storage-baseline.zh-CN.md +22 -0
- package/docs/task-delivery.md +36 -8
- package/docs/task-delivery.zh-CN.md +24 -7
- package/docs/task-discovery.md +28 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +4 -3
- package/package.json +1 -1
- package/skills/yui-leader/references/execution.md +2 -1
- package/skills/yui-leader/references/integration.md +20 -0
- package/skills/yui-operator/SKILL.md +4 -0
- package/skills/yui-reviewer/SKILL.md +3 -0
- package/skills/yui-runtime/SKILL.md +4 -0
- package/skills/yui-runtime/references/publication.md +20 -4
- package/skills/yui-worker/SKILL.md +3 -0
- package/dist/web/assets/client/app.js +0 -804
- package/dist/web/assets/client/components.js +0 -418
- package/dist/web/assets/client/detail.js +0 -333
- package/dist/web/assets/client/dock.js +0 -225
- package/dist/web/assets/client/evidence.js +0 -172
- package/dist/web/assets/client/forms.js +0 -230
- package/dist/web/assets/client/i18n.js +0 -1161
- package/dist/web/assets/client/markdown.js +0 -114
- package/dist/web/assets/client/records.js +0 -63
- package/dist/web/assets/client/sections.js +0 -313
- package/dist/web/assets/client/theme.js +0 -71
- package/dist/web/assets/shell.js +0 -161
- package/dist/web/assets/styles/components.js +0 -169
- package/dist/web/assets/styles/views.js +0 -292
|
@@ -35,7 +35,7 @@ import { classifyRuntimeProcessExit, validateRuntimeProcessExitObservation } fro
|
|
|
35
35
|
import { providerContinuationKey } from "../runtime/providerContinuation.js";
|
|
36
36
|
import { acceptProviderTurn, beginProviderTurn, cancelQuiescentProviderInput, clearProviderGoal, createProviderRuntimeBinding, currentProviderConversation, managedProviderTurnId, settleProviderTurn, settleProviderTurnSubmission, supersedeProviderConversation, transferProviderAuthority, updateProviderConversationRecoverability, updateProviderGoal } from "../runtime/providerRuntimeIdentity.js";
|
|
37
37
|
import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
|
|
38
|
-
import { RUNTIME_OBSERVATION_TASK_EVENT, createRuntimeObservation,
|
|
38
|
+
import { RUNTIME_OBSERVATION_TASK_EVENT, createRuntimeObservation, isRuntimeUsageEvidence, runtimeObservationFenceMatches, runtimeObservationFromTaskEvent, runtimeObservationRunFenceMatches, runtimeObservationTaskEventPayload } from "../runtime/runtimeObservation.js";
|
|
39
39
|
import { recordLeaderFailure } from "../scheduler/leaderFailure.js";
|
|
40
40
|
import { enqueueSupervisorEvent, recordLeaderAttentionRequired, routeRoleEvent } from "../scheduler/operatorEvent.js";
|
|
41
41
|
import { RUN_DIAGNOSTIC_FINISHED_EVENT, RUN_PROGRESS_EVENT, RUN_RECOVERED_EVENT, RUN_STALLED_EVENT, foldRunProgressFacts, isRoleRunStalled, latestRunDurableProgressAt, latestRunEventTime, latestStallEvidenceKey } from "../scheduler/roleRunStall.js";
|
|
@@ -1192,6 +1192,16 @@ export class FileSchedulerStoreAdapter {
|
|
|
1192
1192
|
throw new AgentHostProviderTurnFenceError("Agent Host Provider Turn carries a stale durable writer fence. Release and reacquire Provider authority before retrying input.");
|
|
1193
1193
|
}
|
|
1194
1194
|
const currentRun = binding.run;
|
|
1195
|
+
if (input.roleName === "leader" && input.attemptId.startsWith(`notification:${input.taskId}/`)) {
|
|
1196
|
+
const wakeId = input.attemptId.split("/")[1];
|
|
1197
|
+
const wake = store.listTaskWakes(input.taskId).find(w => w.id === wakeId);
|
|
1198
|
+
const ids = new Set(wake?.refs?.filter(r => r.type === "message").map(r => r.id));
|
|
1199
|
+
if (store.listMessages(input.taskId).some(m => ids.has(m.id)
|
|
1200
|
+
&& m.inputControl?.expectedSessionId !== undefined
|
|
1201
|
+
&& m.inputControl.expectedSessionId !== input.nativeSessionId)) {
|
|
1202
|
+
throw new AgentHostProviderTurnFenceError("Notification input belongs to the selected old Session; do not retarget.");
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1195
1205
|
if (session.effective.executionEnvironment !== undefined) {
|
|
1196
1206
|
assertExecutionEnvironmentCurrent(store, input.taskId, session.effective.executionEnvironment);
|
|
1197
1207
|
}
|
|
@@ -1248,7 +1258,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1248
1258
|
// before the Provider write; provider-visible userMessage items alone
|
|
1249
1259
|
// are NOT proof of human authorship (managed prompts use those too).
|
|
1250
1260
|
// This shares the exact writer/session admission and its transaction.
|
|
1251
|
-
if (input.authorityOwner === "human" && input.roleName === "leader"
|
|
1261
|
+
if ((input.authorityOwner === "human" || input.attemptId.startsWith(`human:${input.holderId}:`)) && input.roleName === "leader"
|
|
1252
1262
|
&& input.runId === undefined && input.boundedText !== undefined) {
|
|
1253
1263
|
const prior = store.listEvents(input.taskId).find(event => event.type === "message.native-user-input" && event.payload.attemptId === input.attemptId);
|
|
1254
1264
|
if (prior !== undefined) {
|
|
@@ -1770,7 +1780,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1770
1780
|
directive: `Plan Task ${taskId} with the user. Persist requirements and decisions. Request activation explicitly; planning grants no delivery authority.`,
|
|
1771
1781
|
contextSnapshotRef: contextSnapshotRef(snapshot),
|
|
1772
1782
|
deltaRefIds: [] }), now, {
|
|
1773
|
-
purpose: "planning", effective: resolveEffectiveLaunch({ role, purpose: "planning" })
|
|
1783
|
+
purpose: "planning", effective: resolveEffectiveLaunch({ store, role, purpose: "planning" })
|
|
1774
1784
|
});
|
|
1775
1785
|
store.saveRun(run);
|
|
1776
1786
|
store.saveActiveRun(run);
|
|
@@ -1849,6 +1859,18 @@ export class FileSchedulerStoreAdapter {
|
|
|
1849
1859
|
const sessions = store.getTaskRoleSessionSet(taskId, "leader");
|
|
1850
1860
|
const session = sessions?.sessions[sessions.activeAgentId];
|
|
1851
1861
|
const obsoleteIds = new Set();
|
|
1862
|
+
for (const message of store.listMessages(taskId)) {
|
|
1863
|
+
if (!pendingMessageIds.has(message.id) || message.inputControl?.expectedSessionId === undefined)
|
|
1864
|
+
continue;
|
|
1865
|
+
if (message.inputControl.expectedSessionId !== session?.nativeSessionId
|
|
1866
|
+
|| message.continuation?.notDeliveredReason !== undefined) {
|
|
1867
|
+
obsoleteIds.add(message.id);
|
|
1868
|
+
if (message.continuation?.notDeliveredReason === undefined)
|
|
1869
|
+
store.updateMessage(taskId, {
|
|
1870
|
+
...message, continuation: { notDeliveredReason: "selected-native-session-changed" }
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1852
1874
|
for (const message of claimedMessages) {
|
|
1853
1875
|
const claim = message.interruptThen;
|
|
1854
1876
|
if (claim.notDeliveredReason !== undefined
|
|
@@ -3136,6 +3158,7 @@ function mapRole(store, role) {
|
|
|
3136
3158
|
// Active AgentRun delivery separately and exclusively uses turn.effective.
|
|
3137
3159
|
const effective = reopened || (liveSession !== null && liveSession.agentId !== role.activeAgentId)
|
|
3138
3160
|
? resolveEffectiveLaunch({
|
|
3161
|
+
store,
|
|
3139
3162
|
role,
|
|
3140
3163
|
purpose,
|
|
3141
3164
|
...(workspace === undefined ? {} : { workspace }),
|
|
@@ -3144,6 +3167,7 @@ function mapRole(store, role) {
|
|
|
3144
3167
|
: liveSession !== null && workspace?.owner.type === "task"
|
|
3145
3168
|
? effectiveLaunchWithTaskMainWorkspace(liveSession.effective, workspace)
|
|
3146
3169
|
: liveSession?.effective ?? resolveEffectiveLaunch({
|
|
3170
|
+
store,
|
|
3147
3171
|
role,
|
|
3148
3172
|
purpose,
|
|
3149
3173
|
...(workspace === undefined ? {} : { workspace }),
|
|
@@ -3199,6 +3223,8 @@ function taskSessionEffective(store, taskId, roleName, agentId, existing) {
|
|
|
3199
3223
|
return effective;
|
|
3200
3224
|
}
|
|
3201
3225
|
function globalSessionEffective(role, existing) {
|
|
3226
|
+
// A native hook without a captured launch is not proof of loaded resources.
|
|
3227
|
+
// Actual controlled launches supply their frozen effective snapshot explicitly.
|
|
3202
3228
|
return existing?.effective ?? resolveEffectiveLaunch({ role, purpose: "execution" });
|
|
3203
3229
|
}
|
|
3204
3230
|
function mapSession(session) {
|
|
@@ -3292,10 +3318,10 @@ function compactedRuntimeObservationIds(events, incoming) {
|
|
|
3292
3318
|
});
|
|
3293
3319
|
const remove = ({ observation }) => {
|
|
3294
3320
|
if (incoming.kind === "activity.observed") {
|
|
3295
|
-
if (
|
|
3321
|
+
if (isRuntimeUsageEvidence(incoming))
|
|
3296
3322
|
return false;
|
|
3297
3323
|
return observation.kind === "activity.observed"
|
|
3298
|
-
&& !
|
|
3324
|
+
&& !isRuntimeUsageEvidence(observation);
|
|
3299
3325
|
}
|
|
3300
3326
|
if (incoming.kind === "operation.started") {
|
|
3301
3327
|
return (observation.kind === "operation.started"
|
|
@@ -3549,7 +3575,7 @@ function usageSnapshotIsSuperseded(events, incoming) {
|
|
|
3549
3575
|
&& observationIsStrictlyNewer(observation, incoming));
|
|
3550
3576
|
}
|
|
3551
3577
|
function hasPersistedRuntimeObservation(events, incoming) {
|
|
3552
|
-
const usageIdentity =
|
|
3578
|
+
const usageIdentity = isRuntimeUsageEvidence(incoming)
|
|
3553
3579
|
? incoming.eventId
|
|
3554
3580
|
: undefined;
|
|
3555
3581
|
return events.some((event) => (event.type === RUNTIME_OBSERVATION_TASK_EVENT
|
|
@@ -20,6 +20,7 @@ import { CliError } from "../errors/cliError.js";
|
|
|
20
20
|
import { assertJobAssignmentScope, JobAssignmentScopeError } from "../job/jobAssignmentScope.js";
|
|
21
21
|
import { assertContextRecordReadable } from "../context/taskContext.js";
|
|
22
22
|
import { assertTaskProjectWriteAuthority } from "../task/taskAuthority.js";
|
|
23
|
+
import { validateHttpPreview } from "../job/httpPreview.js";
|
|
23
24
|
export function createDurableJobControl(store) {
|
|
24
25
|
const authority = createJobCallAuthority(store);
|
|
25
26
|
return {
|
|
@@ -32,6 +33,7 @@ export function createDurableJobControl(store) {
|
|
|
32
33
|
const context = authority.authenticate(Object.freeze({ ...params.caller }), params.taskId);
|
|
33
34
|
requireJobAssignment(tx, params, params.caller);
|
|
34
35
|
assertNonSecretJobInput(params);
|
|
36
|
+
validateHttpPreview(params.env, params.steps);
|
|
35
37
|
const baseKey = durableJobIdempotencyKey({
|
|
36
38
|
owner: params.owner,
|
|
37
39
|
projectId: params.projectId,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { applyTaskInputControl } from "../commands/taskCommands.js";
|
|
2
|
+
import { applyGlobalInputControl } from "../commands/globalRoleCommands.js";
|
|
3
|
+
/** The Host terminal is a human ingress, like the authenticated local Web
|
|
4
|
+
* surface. Fence that ingress before reusing the existing Message/control
|
|
5
|
+
* transaction; the Host performs the one Provider write after this returns. */
|
|
6
|
+
export function prepareNativeHostSteer(store, input) {
|
|
7
|
+
return store.transaction(tx => {
|
|
8
|
+
const set = input.taskId === undefined ? tx.getGlobalRoleSessionSet(input.roleName)
|
|
9
|
+
: tx.getTaskRoleSessionSet(input.taskId, input.roleName);
|
|
10
|
+
const session = set?.sessions[input.agentId], binding = set?.providerBinding;
|
|
11
|
+
if (!session || session.status !== "active" || session.adapterId !== "codex"
|
|
12
|
+
|| set?.activeAgentId !== input.agentId || session.nativeSessionId !== input.nativeSessionId
|
|
13
|
+
|| binding?.run?.status !== "accepted" || binding.run.nativeTurnId !== input.nativeTurnId
|
|
14
|
+
|| binding.authority.owner !== input.authorityOwner || binding.authority.epoch !== input.authorityEpoch
|
|
15
|
+
|| binding.authority.holderId !== input.holderId
|
|
16
|
+
|| (input.taskId !== undefined && input.roleName !== "leader")) {
|
|
17
|
+
throw new Error("Native steer carries a stale or unsupported Session/Turn/writer fence.");
|
|
18
|
+
}
|
|
19
|
+
if (!input.boundedText.trim() || input.boundedText.includes("\0") || Buffer.byteLength(input.boundedText) > 12000) {
|
|
20
|
+
throw new Error("Native steer exceeds its text bound.");
|
|
21
|
+
}
|
|
22
|
+
const control = { action: "steer", body: input.boundedText, to: input.roleName,
|
|
23
|
+
requestId: input.attemptId, expectedTarget: input.nativeTurnId, expectedSessionId: input.nativeSessionId };
|
|
24
|
+
if (input.taskId === undefined) {
|
|
25
|
+
applyGlobalInputControl(input.roleName, control, tx, { env: {} });
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
applyTaskInputControl(input.taskId, { ...control, to: "leader" }, tx, { environment: {}, now: () => input.now });
|
|
29
|
+
}
|
|
30
|
+
const messages = input.taskId === undefined ? tx.listGlobalRoleMessages(input.roleName) : tx.listMessages(input.taskId);
|
|
31
|
+
const message = messages.find(message => message.inputControl?.requestId === input.attemptId);
|
|
32
|
+
if (message?.control?.outcome !== "pending") {
|
|
33
|
+
throw new Error("Native steer is already settled or unconfirmed; do not resubmit.");
|
|
34
|
+
}
|
|
35
|
+
return { attemptId: message.control.receiptId, boundedText: input.boundedText };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { prepareNativeHostSteer } from "./nativeHostInput.js";
|
|
2
3
|
import { archiveLeaderTask } from "../task/leaderArchive.js";
|
|
3
4
|
import { TaskWorkspaceCoordinator } from "../repository/taskWorkspaceCoordinator.js";
|
|
4
5
|
import { AGENT_OPERATIONAL_ENVIRONMENT_NAMES, NATIVE_AGENT_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, YUI_MANAGED_RUNTIME_ENVIRONMENT_NAMES } from "../agent/launchEnvironment.js";
|
|
@@ -33,8 +34,15 @@ import { openSchedulerTelemetry } from "../telemetry/telemetryWiring.js";
|
|
|
33
34
|
import { NodeCommandExecutor } from "../tmux/commandExecutor.js";
|
|
34
35
|
import { TmuxManager, yuiTmuxServerName } from "../tmux/tmuxManager.js";
|
|
35
36
|
import { createControllerWeb } from "../web/controllerWeb.js";
|
|
37
|
+
import { createTaskPreviews } from "../web/taskPreviews.js";
|
|
36
38
|
import { TmuxWebTerminalService } from "../web/tmuxWebTerminal.js";
|
|
37
39
|
import { createWebTaskSurface } from "../web/webTaskSurface.js";
|
|
40
|
+
import { createWebConversationSurface } from "../web/webConversation.js";
|
|
41
|
+
import { WebRequestRejected } from "../web/webMutation.js";
|
|
42
|
+
import { inspectAgentHost, sendAgentHostNativeControl, AGENT_HOST_CONTROL_PROTOCOL } from "../runtime/agentHost.js";
|
|
43
|
+
import { archiveOrdinaryTask } from "../task/ordinaryArchive.js";
|
|
44
|
+
import { createWebSettings } from "../web/webSettings.js";
|
|
45
|
+
import { createWebSkills } from "../web/webSkills.js";
|
|
38
46
|
import { isTaskOwnedWorkspace, sameManagedWorkspaceIdentity } from "../worktree/managedWorkspace.js";
|
|
39
47
|
import { AgentRuntimeObserver } from "./agentRuntimeObserver.js";
|
|
40
48
|
import { createCapabilityDispatcher } from "./capabilityBridge.js";
|
|
@@ -378,10 +386,64 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
378
386
|
runningRuntime?.signal(`task:${target.taskId}`);
|
|
379
387
|
},
|
|
380
388
|
reconcileTask: (taskId) => runningRuntime?.signal(`task:${taskId}`)
|
|
381
|
-
}, yuiHome: home }
|
|
389
|
+
}, yuiHome: home }, [], undefined, {
|
|
390
|
+
archive: async (taskId) => {
|
|
391
|
+
if (!(workspacePreparer instanceof FileTaskWorkspacePreparer))
|
|
392
|
+
throw new Error("Archive workspace owner unavailable.");
|
|
393
|
+
return archiveOrdinaryTask(new TaskWorkspaceCoordinator(store, workspacePreparer, webWorkflow), taskId, { environment: {}, yuiHome: home });
|
|
394
|
+
}
|
|
395
|
+
});
|
|
382
396
|
const surfaces = new SurfaceContributions(kernel.capabilities.registry);
|
|
397
|
+
const validateNativeWrite = (request) => {
|
|
398
|
+
const sessions = request.scope === "task" ? store.getTaskRoleSessionSet(request.taskId, request.roleName)
|
|
399
|
+
: store.getGlobalRoleSessionSet(request.roleName);
|
|
400
|
+
const selected = sessions?.sessions[sessions.activeAgentId];
|
|
401
|
+
if (!request.nativeSessionId || selected?.nativeSessionId !== request.nativeSessionId
|
|
402
|
+
|| selected.status !== "active"
|
|
403
|
+
|| (request.scope === "task" && (request.roleName !== "leader"
|
|
404
|
+
|| selected.adapterId !== "codex" || !sessions?.providerBinding
|
|
405
|
+
|| !["active", "draft"].includes(store.getTask(request.taskId)?.status ?? "")
|
|
406
|
+
|| store.getTask(request.taskId)?.executionGate.state !== "enabled"))) {
|
|
407
|
+
throw new Error("Native input requires the selected current controlled Codex Operator/Leader.");
|
|
408
|
+
}
|
|
409
|
+
};
|
|
383
410
|
const web = createControllerWeb(store, {
|
|
411
|
+
previews: () => createTaskPreviews(store, home),
|
|
412
|
+
skills: createWebSkills(store),
|
|
413
|
+
settings: createWebSettings(store, {
|
|
414
|
+
environment: options.environment ?? process.env, catalogs,
|
|
415
|
+
refreshConfiguration: () => {
|
|
416
|
+
if (!runningRuntime)
|
|
417
|
+
throw new Error("Controller runtime unavailable.");
|
|
418
|
+
runningRuntime.reloadReconciliationInterval();
|
|
419
|
+
}
|
|
420
|
+
}),
|
|
384
421
|
surface: webSurface,
|
|
422
|
+
conversation: createWebConversationSurface(store, webSurface, (owner, id) => planner.planNativeControl(owner, id), undefined, (owner) => inspectAgentHost({ home, ...owner }), {
|
|
423
|
+
hasWriter: owner => tmux.hasWritableClient(owner.scope === "task" ? owner.taskId : "operator", owner.scope === "task" ? owner.roleName : undefined),
|
|
424
|
+
respond: async (owner, id, requestId, nativeTurnId, result) => {
|
|
425
|
+
const receipt = await sendAgentHostNativeControl({
|
|
426
|
+
home, ...owner, control: { protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "native-respond",
|
|
427
|
+
nativeSessionId: id, requestId, nativeTurnId, result }
|
|
428
|
+
});
|
|
429
|
+
if (receipt.outcome !== "accepted")
|
|
430
|
+
throw new Error(receipt.snapshot.detail ?? "Native answer was not confirmed.");
|
|
431
|
+
return { outcome: receipt.outcome };
|
|
432
|
+
},
|
|
433
|
+
setModel: async (owner, id, model) => {
|
|
434
|
+
const result = await sendAgentHostNativeControl({
|
|
435
|
+
home, ...owner, control: { protocol: AGENT_HOST_CONTROL_PROTOCOL,
|
|
436
|
+
type: "native-model", nativeSessionId: id, model }
|
|
437
|
+
});
|
|
438
|
+
if (result.outcome !== "accepted") {
|
|
439
|
+
const detail = result.snapshot.detail ?? "Model adoption is unconfirmed.";
|
|
440
|
+
if (result.failure?.inputDisposition === "not-accepted")
|
|
441
|
+
throw new WebRequestRejected(detail);
|
|
442
|
+
throw new Error(detail);
|
|
443
|
+
}
|
|
444
|
+
return { outcome: result.outcome, runConfiguration: result.snapshot.runConfiguration };
|
|
445
|
+
}
|
|
446
|
+
}, catalogs),
|
|
385
447
|
answerInput: async ({ taskId, inputId, answer }) => webSurface.answer(taskId, inputId, answer),
|
|
386
448
|
panels: {
|
|
387
449
|
list: (taskId) => surfaces.listPanels(kernel.capabilities.authenticateWebQuery(taskId)),
|
|
@@ -390,6 +452,26 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
390
452
|
terminal: new TmuxWebTerminalService({
|
|
391
453
|
yuiHome: home, tmuxBin: resolveTmuxBin(store.getConfig().tmuxBin), tmux,
|
|
392
454
|
prepareGlobalRole: (roleName) => webWorkflow.prepareGlobalRoleEnter(roleName),
|
|
455
|
+
prepareNative: async (request) => {
|
|
456
|
+
if (!request.nativeSessionId)
|
|
457
|
+
return false;
|
|
458
|
+
const sessions = request.scope === "task" ? store.getTaskRoleSessionSet(request.taskId, request.roleName)
|
|
459
|
+
: store.getGlobalRoleSessionSet(request.roleName);
|
|
460
|
+
const selected = sessions?.sessions[sessions.activeAgentId];
|
|
461
|
+
if (request.scope === "task" && (request.roleName !== "leader" || selected?.adapterId !== "codex"))
|
|
462
|
+
return false;
|
|
463
|
+
validateNativeWrite(request);
|
|
464
|
+
// Preserve ordinary global native access on other adapters and the
|
|
465
|
+
// existing uncontrolled Codex TUI. No lifecycle operation on a switch.
|
|
466
|
+
if (selected?.adapterId !== "codex" || !sessions?.providerBinding)
|
|
467
|
+
return request.scope === "global";
|
|
468
|
+
const result = await sendAgentHostNativeControl({ home, ...request,
|
|
469
|
+
control: { protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "native-open", nativeSessionId: request.nativeSessionId } });
|
|
470
|
+
if (result.outcome !== "accepted" || !result.snapshot.nativeTui)
|
|
471
|
+
throw new Error("Native TUI could not be opened.");
|
|
472
|
+
return true;
|
|
473
|
+
},
|
|
474
|
+
validateWrite: validateNativeWrite,
|
|
393
475
|
environment: options.environment ?? process.env, onError: options.onError
|
|
394
476
|
})
|
|
395
477
|
});
|
|
@@ -612,6 +694,12 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
612
694
|
throw applicationError("INVALID_PARAMS", error instanceof Error ? error.message : String(error));
|
|
613
695
|
}
|
|
614
696
|
}
|
|
697
|
+
if (method === "runtime.native-steer-prepare") {
|
|
698
|
+
const value = providerTurnControlParams(params);
|
|
699
|
+
return prepareNativeHostSteer(store, { ...value,
|
|
700
|
+
nativeTurnId: requiredParam(params.nativeTurnId),
|
|
701
|
+
boundedText: requiredParam(value.boundedText) });
|
|
702
|
+
}
|
|
615
703
|
if (method === "runtime.provider-turn-begin") {
|
|
616
704
|
const value = providerTurnControlParams(params);
|
|
617
705
|
try {
|
|
@@ -927,6 +1015,7 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
927
1015
|
function resolveRuntimeDesiredEffective(store, request, role) {
|
|
928
1016
|
if (request.scope === "global") {
|
|
929
1017
|
return resolveEffectiveLaunch({
|
|
1018
|
+
store,
|
|
930
1019
|
role: role,
|
|
931
1020
|
purpose: "execution"
|
|
932
1021
|
});
|
|
@@ -940,6 +1029,7 @@ function resolveRuntimeDesiredEffective(store, request, role) {
|
|
|
940
1029
|
?? store.getTaskWorkspace(request.taskId)
|
|
941
1030
|
?? undefined;
|
|
942
1031
|
return resolveEffectiveLaunch({
|
|
1032
|
+
store,
|
|
943
1033
|
role: taskRole,
|
|
944
1034
|
purpose: store.getTask(request.taskId)?.status === "draft" && request.roleName === "leader"
|
|
945
1035
|
? "planning" : "execution",
|
|
@@ -1018,7 +1108,7 @@ function assertRuntimeLaunchRequestCurrent(store, request) {
|
|
|
1018
1108
|
}
|
|
1019
1109
|
function currentDesiredEffective(store, request, role) {
|
|
1020
1110
|
if (request.owner.scope === "global") {
|
|
1021
|
-
return resolveEffectiveLaunch({ role: role, purpose: "execution" });
|
|
1111
|
+
return resolveEffectiveLaunch({ store, role: role, purpose: "execution" });
|
|
1022
1112
|
}
|
|
1023
1113
|
const item = store.listWorkItems(request.owner.taskId).find((candidate) => (candidate.assignee === request.owner.roleName
|
|
1024
1114
|
&& !["accepted", "retired"].includes(candidate.status))) ?? null;
|
|
@@ -1028,6 +1118,7 @@ function currentDesiredEffective(store, request, role) {
|
|
|
1028
1118
|
?? store.getTaskWorkspace(request.owner.taskId)
|
|
1029
1119
|
?? undefined;
|
|
1030
1120
|
return resolveEffectiveLaunch({
|
|
1121
|
+
store,
|
|
1031
1122
|
role: role,
|
|
1032
1123
|
purpose: store.getTask(request.owner.taskId)?.status === "draft" && request.owner.roleName === "leader"
|
|
1033
1124
|
? "planning" : "execution",
|
|
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
3
|
import { closeSync, constants, existsSync, fchmodSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { createRuntimeObservation,
|
|
5
|
+
import { createRuntimeObservation, isRuntimeUsageEvidence } from "../runtime/runtimeObservation.js";
|
|
6
6
|
import { boundedRunFailureDiagnostic, transportAgentResult } from "../domain/agentResultTransport.js";
|
|
7
7
|
import { readAgentHostObservationSource } from "../runtime/agentHostProtocol.js";
|
|
8
8
|
export const MAX_RUNTIME_EVENT_FILE_BYTES = 16 * 1024 * 1024;
|
|
@@ -174,7 +174,7 @@ export class FileRuntimeEventInbox {
|
|
|
174
174
|
publish(event) {
|
|
175
175
|
this.hooks.afterAdmission?.();
|
|
176
176
|
if (event.type === "runtime-observation"
|
|
177
|
-
&&
|
|
177
|
+
&& isRuntimeUsageEvidence(event.observation)) {
|
|
178
178
|
const existing = this.list().find((candidate) => (candidate.type === "runtime-observation"
|
|
179
179
|
&& candidate.taskId === event.taskId
|
|
180
180
|
&& candidate.observation.eventId === event.observation.eventId));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isRuntimeUsageEvidence } from "../runtime/runtimeObservation.js";
|
|
2
2
|
const DEFAULT_MAX_RUNTIME_EVENTS_PER_DRAIN = 64;
|
|
3
3
|
/** Folds immutable Hook facts in one bounded transaction before acknowledging them. */
|
|
4
4
|
export class FileRuntimeEventProcessor {
|
|
@@ -299,7 +299,7 @@ export function coalesceRuntimeProgress(events, instrumentation = {}) {
|
|
|
299
299
|
}
|
|
300
300
|
const representedByIndex = new Map();
|
|
301
301
|
for (const indices of indicesByStream.values()) {
|
|
302
|
-
const hasUsage =
|
|
302
|
+
const hasUsage = isRuntimeUsageEvidence(segment[indices[0]].observation);
|
|
303
303
|
// Usage facts and incomplete boundaries are authoritative history for
|
|
304
304
|
// read-only Session token projection, so ingress never coalesces them.
|
|
305
305
|
const retainedPositions = hasUsage ? indices.map((_, position) => position) : [];
|
|
@@ -403,7 +403,7 @@ function progressStreamKey(event) {
|
|
|
403
403
|
fence.nativeTurnId ?? null,
|
|
404
404
|
event.host ?? null,
|
|
405
405
|
payload.activity,
|
|
406
|
-
|
|
406
|
+
isRuntimeUsageEvidence(event.observation) ? "usage" : "signal"
|
|
407
407
|
]);
|
|
408
408
|
}
|
|
409
409
|
function isRuntimeActivityEvent(event) {
|
package/dist/doctor/doctor.js
CHANGED
|
@@ -2,6 +2,8 @@ import { accessSync, constants, existsSync, lstatSync, realpathSync } from "node
|
|
|
2
2
|
import { isAbsolute, join } from "node:path";
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
4
|
import { inspectPty } from "./ptyProbe.js";
|
|
5
|
+
import { gitVersionSupport } from "../repository/gitCompatibility.js";
|
|
6
|
+
import { resolveExecutable } from "../external/pinnedCommandRunner.js";
|
|
5
7
|
import { configuredAgentToDefinition, resolveAgentEnvironment } from "../agent/agent.js";
|
|
6
8
|
import { operationalAgentEnvironment } from "../agent/launchEnvironment.js";
|
|
7
9
|
import { resolveTmuxBin } from "../config/yuiConfig.js";
|
|
@@ -684,6 +686,14 @@ function checkReviewerDispatch(_policy, role, binding, agent, launch, command, c
|
|
|
684
686
|
function checkExecutable(name, command, args, executor) {
|
|
685
687
|
try {
|
|
686
688
|
const output = firstLine(executor.run(command, args));
|
|
689
|
+
if (name === "git") {
|
|
690
|
+
const support = gitVersionSupport(output);
|
|
691
|
+
return {
|
|
692
|
+
name, status: support.supported ? "ok" : "invalid",
|
|
693
|
+
detail: `${resolveExecutable(command, process.env.PATH) ?? command}: ${support.detail}`
|
|
694
|
+
+ (support.supported ? "" : " Select a supported Git in PATH and rerun doctor; no upgrade was performed.")
|
|
695
|
+
};
|
|
696
|
+
}
|
|
687
697
|
return {
|
|
688
698
|
name,
|
|
689
699
|
status: "ok",
|
|
@@ -65,6 +65,7 @@ export function dispatchReviewSynthesis(store, taskId, reviewRoundId, sourceRunI
|
|
|
65
65
|
throw new Error(`Review main workspace is missing: ${taskId}/${round.id}.`);
|
|
66
66
|
}
|
|
67
67
|
const effective = resolveEffectiveLaunch({
|
|
68
|
+
store,
|
|
68
69
|
role,
|
|
69
70
|
purpose: "review",
|
|
70
71
|
workspace,
|
|
@@ -78,6 +78,7 @@ export function dispatchWorkItemSynthesis(store, taskId, workItemId, sourceRunId
|
|
|
78
78
|
throw new Error(`WorkItem main workspace does not match its approved scope: ${item.id}.`);
|
|
79
79
|
}
|
|
80
80
|
const effective = resolveEffectiveLaunch({
|
|
81
|
+
store,
|
|
81
82
|
role,
|
|
82
83
|
purpose: "execution",
|
|
83
84
|
workspace,
|
|
@@ -196,13 +196,15 @@ function exactTrust(directory, configured) {
|
|
|
196
196
|
return undefined;
|
|
197
197
|
}
|
|
198
198
|
function resolveRepositoryRootForTrust(workspace) {
|
|
199
|
-
const result = spawnSync("git", ["-C", workspace, "rev-parse", "--
|
|
199
|
+
const result = spawnSync("git", ["-C", workspace, "rev-parse", "--git-common-dir"], { encoding: "utf8", timeout: 1_000, windowsHide: true });
|
|
200
200
|
if (result.status !== 0 || result.error !== undefined)
|
|
201
201
|
return undefined;
|
|
202
|
-
|
|
203
|
-
if (!isAbsolute(commonDirectory) || commonDirectory.includes("\0"))
|
|
202
|
+
if (!result.stdout.endsWith("\n"))
|
|
204
203
|
return undefined;
|
|
205
|
-
const
|
|
204
|
+
const commonDirectory = result.stdout.slice(0, -1);
|
|
205
|
+
if (commonDirectory.length === 0 || commonDirectory.includes("\0"))
|
|
206
|
+
return undefined;
|
|
207
|
+
const normalized = resolve(workspace, commonDirectory);
|
|
206
208
|
return basename(normalized) === ".git" ? dirname(normalized) : undefined;
|
|
207
209
|
}
|
|
208
210
|
function inspectProjectDiscovery(contents) {
|
|
@@ -2,7 +2,8 @@ import { isDeepStrictEqual } from "node:util";
|
|
|
2
2
|
import { validateExecutionEnvironmentSnapshot } from "../resources/projectResource.js";
|
|
3
3
|
import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
|
|
4
4
|
import { resolveAgentAdapter } from "./agentAdapter.js";
|
|
5
|
-
import { roleSessionKind } from "../context/roleSessionContext.js";
|
|
5
|
+
import { compileRoleSessionContext, roleSessionKind } from "../context/roleSessionContext.js";
|
|
6
|
+
import { validateSkillPackageRef } from "../context/skillPackage.js";
|
|
6
7
|
import { isAgentAdapterId } from "../agent/adapterCatalog.js";
|
|
7
8
|
import { adapterIdForExecutionComponent, isAgentExecutionComponentId, resolveAgentExecutionComponent } from "../agent/executionComponents.js";
|
|
8
9
|
import { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, sessionManifestCompatibilityDigest } from "../context/sessionProtocolIdentity.js";
|
|
@@ -15,6 +16,15 @@ export function resolveEffectiveLaunch(input) {
|
|
|
15
16
|
const workspace = snapshotWorkspace(input.role.workspace, input.workspace);
|
|
16
17
|
const writeProjectIds = effectiveWriteProjects(input, workspace);
|
|
17
18
|
const config = resolveAgentAdapter(binding.adapterId).canonicalizeConfig(clone(binding.config));
|
|
19
|
+
const home = input.store?.rootDirectory() ?? input.yuiHome;
|
|
20
|
+
const sessions = "taskId" in input.role
|
|
21
|
+
? input.store?.getTaskRoleSessionSet(input.role.taskId, input.role.name)
|
|
22
|
+
: input.store?.getGlobalRoleSessionSet(input.role.name);
|
|
23
|
+
const existing = sessions?.sessions[binding.agentId];
|
|
24
|
+
const continuing = existing?.status === "active";
|
|
25
|
+
const skillPackages = continuing ? existing.effective.skillPackages
|
|
26
|
+
: home === undefined ? undefined
|
|
27
|
+
: compileRoleSessionContext(home, input.role, "taskId" in input.role ? { scope: "task", taskId: input.role.taskId } : { scope: "global" }, { purpose: input.purpose }).skills.map(skill => skill.package);
|
|
18
28
|
return snapshotFromConfig({
|
|
19
29
|
executionAuthority: input.executionAuthority ?? (input.purpose === "planning" ? "planning" : "delivery"),
|
|
20
30
|
sourceDesiredRevision: input.role.launchRevision,
|
|
@@ -28,6 +38,7 @@ export function resolveEffectiveLaunch(input) {
|
|
|
28
38
|
? { executionEnvironment: input.role.executionEnvironment }
|
|
29
39
|
: {}),
|
|
30
40
|
context: snapshotContext(input.role),
|
|
41
|
+
...(skillPackages === undefined ? {} : { skillPackages }),
|
|
31
42
|
contextProtocolVersion: SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION,
|
|
32
43
|
sessionManifestCompatibilityDigest: sessionManifestCompatibilityDigest(input.role.name, roleSessionKind(input.role, "taskId" in input.role
|
|
33
44
|
? { scope: "task", taskId: input.role.taskId }
|
|
@@ -216,6 +227,17 @@ export function validateEffectiveLaunchSnapshot(snapshot) {
|
|
|
216
227
|
throw new Error("Effective launch Session Manifest compatibility digest is invalid.");
|
|
217
228
|
}
|
|
218
229
|
validateWorkspace(snapshot.workspace);
|
|
230
|
+
if (snapshot.skillPackages !== undefined) {
|
|
231
|
+
if (!Array.isArray(snapshot.skillPackages))
|
|
232
|
+
throw new Error("Effective Skill packages must be an array.");
|
|
233
|
+
const ids = new Set();
|
|
234
|
+
for (const ref of snapshot.skillPackages) {
|
|
235
|
+
validateSkillPackageRef(ref);
|
|
236
|
+
if (ids.has(ref.id))
|
|
237
|
+
throw new Error("Effective Skill packages must be unique.");
|
|
238
|
+
ids.add(ref.id);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
219
241
|
if (snapshot.executionEnvironment !== undefined) {
|
|
220
242
|
validateExecutionEnvironmentSnapshot(snapshot.executionEnvironment);
|
|
221
243
|
if (snapshot.executionEnvironment.access === "read") {
|
|
@@ -299,6 +321,7 @@ function snapshotFromConfig(input) {
|
|
|
299
321
|
? {}
|
|
300
322
|
: { executionEnvironment: clone(input.executionEnvironment) }),
|
|
301
323
|
context: cloneContext(input.context),
|
|
324
|
+
...(input.skillPackages === undefined ? {} : { skillPackages: clone(input.skillPackages) }),
|
|
302
325
|
contextProtocolVersion: input.contextProtocolVersion,
|
|
303
326
|
sessionManifestCompatibilityDigest: input.sessionManifestCompatibilityDigest,
|
|
304
327
|
...review
|
|
@@ -70,11 +70,13 @@ export class FileRoleLaunchPlanner {
|
|
|
70
70
|
}
|
|
71
71
|
/** Recovery addresses the recorded Agent/Session, independent of delivery
|
|
72
72
|
* admission, a missing worktree, or the old Endpoint code generation. */
|
|
73
|
-
planNativeControl(owner) {
|
|
73
|
+
planNativeControl(owner, nativeSessionId) {
|
|
74
74
|
const set = owner.scope === "task"
|
|
75
75
|
? this.store.getTaskRoleSessionSet(owner.taskId, owner.roleName)
|
|
76
76
|
: this.store.getGlobalRoleSessionSet(owner.roleName);
|
|
77
|
-
const session = roleSessionControlTarget(set)
|
|
77
|
+
const session = nativeSessionId === undefined ? roleSessionControlTarget(set)
|
|
78
|
+
: [...Object.values(set?.sessions ?? {}), ...Object.values(set?.history ?? {})]
|
|
79
|
+
.find(entry => entry.nativeSessionId === nativeSessionId);
|
|
78
80
|
if (session?.adapterId !== "codex")
|
|
79
81
|
throw new Error("Native metadata control requires a recorded Codex Session.");
|
|
80
82
|
const configured = this.store.getConfiguredAgent(session.agentId);
|
|
@@ -229,7 +231,7 @@ export class FileRoleLaunchPlanner {
|
|
|
229
231
|
throw new Error("Global Role has an unmanaged live Session. Stop that exact Session before opening controlled input; it will not be silently replaced.");
|
|
230
232
|
}
|
|
231
233
|
const resolvedEffective = activeLiveRoleAgentSession(sessionSet)?.effective
|
|
232
|
-
?? resolveEffectiveLaunch({ role, purpose: "execution" });
|
|
234
|
+
?? resolveEffectiveLaunch({ store: this.store, role, purpose: "execution" });
|
|
233
235
|
if (input.effective !== undefined
|
|
234
236
|
&& !isDeepStrictEqual(resolvedEffective, input.effective)) {
|
|
235
237
|
throw new Error(`Global Role launch effective snapshot changed: ${role.name}.`);
|
|
@@ -335,7 +337,12 @@ export class FileRoleLaunchPlanner {
|
|
|
335
337
|
Object.assign(launchEnvironment, runtimeIsolation === undefined
|
|
336
338
|
? {}
|
|
337
339
|
: taskRuntimeIsolationEnvironment(runtimeIsolation));
|
|
338
|
-
const
|
|
340
|
+
const skillPackages = input.mode === "resume"
|
|
341
|
+
? existingSession?.effective.skillPackages : effective.skillPackages;
|
|
342
|
+
if (skillPackages === undefined) {
|
|
343
|
+
throw new Error("This launch has no frozen Skill package evidence (pre-1.5 or externally recorded Session). Create a new Session from current Role configuration; historical Skill versions cannot be reconstructed.");
|
|
344
|
+
}
|
|
345
|
+
const baseSessionContext = compileRoleSessionContext(this.home, launchRole, owner, { ...sessionPolicy, skillPackages });
|
|
339
346
|
const bootstrap = materializeSessionBootstrap({
|
|
340
347
|
yuiHome: this.home,
|
|
341
348
|
role: launchRole,
|
|
@@ -686,7 +693,7 @@ function resolveTaskRoleEffectiveLaunch(store, role, purpose = "execution") {
|
|
|
686
693
|
if (purpose === "planning") {
|
|
687
694
|
// Planning has no WorkItem assignment and no managed workspace, so it
|
|
688
695
|
// resolves the Role's own configured workspace with no writable Project.
|
|
689
|
-
return resolveEffectiveLaunch({ role, purpose: "planning" });
|
|
696
|
+
return resolveEffectiveLaunch({ store, role, purpose: "planning" });
|
|
690
697
|
}
|
|
691
698
|
const item = store.listWorkItems(role.taskId).find((candidate) => (candidate.assignee === role.name
|
|
692
699
|
&& !["accepted", "retired"].includes(candidate.status))) ?? null;
|
|
@@ -696,6 +703,7 @@ function resolveTaskRoleEffectiveLaunch(store, role, purpose = "execution") {
|
|
|
696
703
|
?? store.getTaskWorkspace(role.taskId)
|
|
697
704
|
?? undefined;
|
|
698
705
|
return resolveEffectiveLaunch({
|
|
706
|
+
store,
|
|
699
707
|
role,
|
|
700
708
|
purpose: "execution",
|
|
701
709
|
...(workspace === undefined ? {} : { workspace }),
|