@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
|
@@ -18,6 +18,7 @@ import { closeAgentHostEndpoints } from "./agentHostCleanup.js";
|
|
|
18
18
|
import { readAgentRunConfigurationObservation, unknownAgentRunConfiguration } from "./agentRunConfiguration.js";
|
|
19
19
|
import { CodexPreSubmissionError } from "./codexAppServerRuntime.js";
|
|
20
20
|
import { runCodexInteractiveHost } from "./codexInteractiveHost.js";
|
|
21
|
+
import { openControlledCodexTui } from "./controlledCodexTui.js";
|
|
21
22
|
import { validateAgentHostLaunchPayload } from "./launchBroker.js";
|
|
22
23
|
import { validateRuntimeProcessExitObservation } from "./processExitObservation.js";
|
|
23
24
|
import { persistRuntimeProcessExitObservation, replayRuntimeProcessExitOutbox } from "./processExitOutbox.js";
|
|
@@ -108,6 +109,10 @@ export async function runAgentHost(input) {
|
|
|
108
109
|
let snapshot = hostSnapshot("idle");
|
|
109
110
|
let dispatchTail = Promise.resolve();
|
|
110
111
|
let promptHuman = () => { };
|
|
112
|
+
let humanConsole;
|
|
113
|
+
let nativeTui;
|
|
114
|
+
let nativeOpening;
|
|
115
|
+
let modelChanging = false;
|
|
111
116
|
const recentSteerAttempts = new Map();
|
|
112
117
|
await replayExitOutbox(input.home);
|
|
113
118
|
const updateSnapshot = (next) => {
|
|
@@ -444,6 +449,8 @@ export async function runAgentHost(input) {
|
|
|
444
449
|
&& providerSession.adapterId === "codex"
|
|
445
450
|
&& !hostStopRequested;
|
|
446
451
|
if (reconnectableCodexClient) {
|
|
452
|
+
nativeTui?.close();
|
|
453
|
+
nativeTui = undefined;
|
|
447
454
|
session = undefined;
|
|
448
455
|
if (codexClientAttachedAt !== undefined
|
|
449
456
|
&& Date.now() - codexClientAttachedAt >= CODEX_CLIENT_STABLE_MS) {
|
|
@@ -1028,7 +1035,128 @@ export async function runAgentHost(input) {
|
|
|
1028
1035
|
promptHuman();
|
|
1029
1036
|
return snapshot;
|
|
1030
1037
|
};
|
|
1031
|
-
const
|
|
1038
|
+
const nativeMutation = async (expected, method, params) => {
|
|
1039
|
+
if (session !== expected || params.threadId !== expected.nativeSessionId || !authority
|
|
1040
|
+
|| (sessionPayload?.environment.YUI_SESSION_SCOPE === "task" && sessionPayload.environment.YUI_ROLE !== "leader")) {
|
|
1041
|
+
throw new Error("Native input targets a replaced or unsupported Session.");
|
|
1042
|
+
}
|
|
1043
|
+
if (method === "turn/interrupt") {
|
|
1044
|
+
if (!activeRunAttemptId || params.turnId !== activeNativeTurnId)
|
|
1045
|
+
throw new Error("Native stop targets a different Turn.");
|
|
1046
|
+
const cancelled = await expected.cancel(activeRunAttemptId);
|
|
1047
|
+
if (cancelled.status !== "requested")
|
|
1048
|
+
throw new Error(`Native stop is ${cancelled.status}; do not replay.`);
|
|
1049
|
+
return {};
|
|
1050
|
+
}
|
|
1051
|
+
if (!Array.isArray(params.input) || params.input.length === 0
|
|
1052
|
+
|| params.input.some(part => !part || typeof part !== "object"
|
|
1053
|
+
|| part.type !== "text" || typeof part.text !== "string")) {
|
|
1054
|
+
throw new Error("Managed native access currently supports text input only.");
|
|
1055
|
+
}
|
|
1056
|
+
const boundedText = params.input.map(part => part.text).join("\n");
|
|
1057
|
+
if (!boundedText.trim() || boundedText.includes("\0") || Buffer.byteLength(boundedText) > 12000)
|
|
1058
|
+
throw new Error("Native input exceeds its text bound.");
|
|
1059
|
+
const run = { attemptId: `human:${authority.holderId}:${randomUUID()}`, boundedText };
|
|
1060
|
+
if (method === "turn/steer") {
|
|
1061
|
+
const turnId = activeNativeTurnId;
|
|
1062
|
+
if (params.expectedTurnId !== turnId || !turnId)
|
|
1063
|
+
throw new Error("Native steer targets a different Turn.");
|
|
1064
|
+
if (!sessionPayload)
|
|
1065
|
+
throw new Error("Native Session launch context is unavailable.");
|
|
1066
|
+
// Persist human intent and the existing steer receipt before Provider
|
|
1067
|
+
// write. A fresh human turn id is not an additional-input binding.
|
|
1068
|
+
const prepared = await callControllerIdempotently(input.home, "runtime.native-steer-prepare", {
|
|
1069
|
+
...hostRunControlParams(sessionPayload, expected.nativeSessionId, authority, run.attemptId),
|
|
1070
|
+
nativeTurnId: turnId, boundedText
|
|
1071
|
+
});
|
|
1072
|
+
const result = await handleControl({ protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "steer-turn",
|
|
1073
|
+
nativeSessionId: expected.nativeSessionId, nativeTurnId: turnId, authority, run: prepared });
|
|
1074
|
+
if (result.outcome !== "accepted")
|
|
1075
|
+
throw new Error("Native steer acceptance is unconfirmed; do not resubmit.");
|
|
1076
|
+
return { turnId };
|
|
1077
|
+
}
|
|
1078
|
+
const result = await handleControl({ protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "submit-turn",
|
|
1079
|
+
nativeSessionId: expected.nativeSessionId, authority, run });
|
|
1080
|
+
const accepted = result.snapshot;
|
|
1081
|
+
if (result.outcome !== "accepted" || accepted.inputAcceptance !== "provider" || !accepted.nativeTurnId) {
|
|
1082
|
+
throw new Error("Native acceptance is unconfirmed; do not resubmit.");
|
|
1083
|
+
}
|
|
1084
|
+
return { turn: { id: accepted.nativeTurnId, items: [], status: "inProgress", error: null } };
|
|
1085
|
+
};
|
|
1086
|
+
const handleControl = async (request) => {
|
|
1087
|
+
if (request.type === "native-model") {
|
|
1088
|
+
return enqueueSerialized(async () => {
|
|
1089
|
+
const current = session;
|
|
1090
|
+
if (!current?.nativeAccess || current.nativeSessionId !== request.nativeSessionId
|
|
1091
|
+
|| !sessionPayload || activeNativeTurnId || authority?.owner !== "controller"
|
|
1092
|
+
|| (sessionPayload.environment.YUI_SESSION_SCOPE === "task" && sessionPayload.environment.YUI_ROLE !== "leader")
|
|
1093
|
+
|| (sessionPayload.environment.YUI_SESSION_SCOPE === "global" && sessionPayload.environment.YUI_ROLE !== "operator")) {
|
|
1094
|
+
throw new Error("Model switching requires the idle current controlled Codex Session, without a native terminal writer.");
|
|
1095
|
+
}
|
|
1096
|
+
if (nativeTui || nativeOpening) {
|
|
1097
|
+
throw new Error("Exit the native TUI before changing the Session model. Closing a browser terminal viewer only detaches it; the Host-owned TUI remains open.");
|
|
1098
|
+
}
|
|
1099
|
+
modelChanging = true;
|
|
1100
|
+
try {
|
|
1101
|
+
await assertHostExecutionEnvironment(input.home, sessionPayload, current.nativeSessionId);
|
|
1102
|
+
try {
|
|
1103
|
+
await current.nativeAccess.setModel(request.model);
|
|
1104
|
+
}
|
|
1105
|
+
catch (error) {
|
|
1106
|
+
if (error instanceof CodexPreSubmissionError)
|
|
1107
|
+
throw error;
|
|
1108
|
+
throw new ProviderDeliveryUnknownError(`Native model adoption is unconfirmed: ${errorText(error)}`, `model:${current.nativeSessionId}`, { cause: error });
|
|
1109
|
+
}
|
|
1110
|
+
return controlResult("accepted", { ...snapshot, runConfiguration: current.runConfiguration });
|
|
1111
|
+
}
|
|
1112
|
+
finally {
|
|
1113
|
+
modelChanging = false;
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
if (request.type === "native-open" || request.type === "native-respond") {
|
|
1118
|
+
const current = session;
|
|
1119
|
+
if (!current?.nativeAccess || current.nativeSessionId !== request.nativeSessionId
|
|
1120
|
+
|| !sessionPayload || (sessionPayload.environment.YUI_SESSION_SCOPE === "task"
|
|
1121
|
+
&& sessionPayload.environment.YUI_ROLE !== "leader"))
|
|
1122
|
+
throw new Error("Native access requires the current controlled Codex Operator/Leader.");
|
|
1123
|
+
if (request.type === "native-respond") {
|
|
1124
|
+
await assertHostExecutionEnvironment(input.home, sessionPayload, current.nativeSessionId);
|
|
1125
|
+
await current.nativeAccess.respond(request.requestId, request.nativeSessionId, request.nativeTurnId ?? null, request.result);
|
|
1126
|
+
}
|
|
1127
|
+
else if (!nativeTui) {
|
|
1128
|
+
if (modelChanging)
|
|
1129
|
+
throw new Error("Wait for model adoption before attaching native input.");
|
|
1130
|
+
if (!process.stdin.isTTY)
|
|
1131
|
+
throw new Error("Native TUI requires the Host's owned terminal.");
|
|
1132
|
+
humanConsole?.close();
|
|
1133
|
+
humanConsole = undefined;
|
|
1134
|
+
nativeOpening ??= openControlledCodexTui(sessionPayload, current.nativeSessionId, current.nativeAccess, {
|
|
1135
|
+
mutate: (method, params) => nativeMutation(current, method, params),
|
|
1136
|
+
respond: async (requestId, nativeTurnId, result) => {
|
|
1137
|
+
await handleControl({ protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "native-respond",
|
|
1138
|
+
nativeSessionId: current.nativeSessionId, requestId, nativeTurnId, result });
|
|
1139
|
+
},
|
|
1140
|
+
onExit: () => {
|
|
1141
|
+
nativeTui = undefined;
|
|
1142
|
+
process.stdout.write("\r\nNative TUI detached. Reattach the terminal to reopen this same Thread; the Agent Host remains running.\r\n");
|
|
1143
|
+
},
|
|
1144
|
+
onError: error => process.stderr.write(`Native TUI: ${errorText(error)}\n`)
|
|
1145
|
+
});
|
|
1146
|
+
try {
|
|
1147
|
+
nativeTui = await nativeOpening;
|
|
1148
|
+
}
|
|
1149
|
+
finally {
|
|
1150
|
+
nativeOpening = undefined;
|
|
1151
|
+
}
|
|
1152
|
+
if (session !== current) {
|
|
1153
|
+
nativeTui.close();
|
|
1154
|
+
nativeTui = undefined;
|
|
1155
|
+
throw new Error("Session changed while opening native access.");
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return controlResult("accepted", { ...snapshot, nativeTui: !!nativeTui });
|
|
1159
|
+
}
|
|
1032
1160
|
if (request.type === "status") {
|
|
1033
1161
|
// Read the Agent's configuration at answer time, not from the stored
|
|
1034
1162
|
// snapshot. The Agent may have changed it since the last state
|
|
@@ -1036,10 +1164,20 @@ export async function runAgentHost(input) {
|
|
|
1036
1164
|
// current reading rather than the one that was true at launch. Nothing is
|
|
1037
1165
|
// sent to the Agent to obtain it: this reads what the Session has already
|
|
1038
1166
|
// been told.
|
|
1167
|
+
let requestBytes = 0;
|
|
1168
|
+
const nativeRequests = session?.nativeAccess?.requests.filter(request => {
|
|
1169
|
+
requestBytes += Buffer.byteLength(JSON.stringify(request));
|
|
1170
|
+
return requestBytes <= 8000;
|
|
1171
|
+
}).slice(0, 8);
|
|
1039
1172
|
return controlResult("status", session === undefined
|
|
1040
1173
|
? snapshot
|
|
1041
1174
|
: validateSnapshot({
|
|
1042
1175
|
...snapshot,
|
|
1176
|
+
nativeTui: !!nativeTui,
|
|
1177
|
+
...(nativeRequests === undefined ? {} : { nativeRequests,
|
|
1178
|
+
nativeRequestsOmitted: nativeRequests.length !== session.nativeAccess.requests.length }),
|
|
1179
|
+
...(session.publicReply === undefined ? {} : { publicReply: session.publicReply }),
|
|
1180
|
+
...(session.adapterId === "codex" ? { publicReplyObservation: "supported" } : {}),
|
|
1043
1181
|
runConfiguration: session.runConfiguration
|
|
1044
1182
|
}));
|
|
1045
1183
|
}
|
|
@@ -1138,6 +1276,8 @@ export async function runAgentHost(input) {
|
|
|
1138
1276
|
const accepted = await enqueueSerialized(async () => setAuthority(request));
|
|
1139
1277
|
return controlResult("accepted", accepted);
|
|
1140
1278
|
}
|
|
1279
|
+
if (request.type !== "launch")
|
|
1280
|
+
throw new Error("Unsupported Host request.");
|
|
1141
1281
|
const redeemed = await redeem(input.home, request.ticket);
|
|
1142
1282
|
if (activeRunPayload !== undefined
|
|
1143
1283
|
|| ["starting", "ready", "settling", "delivery-unknown"].includes(snapshot.state)) {
|
|
@@ -1149,8 +1289,9 @@ export async function runAgentHost(input) {
|
|
|
1149
1289
|
}
|
|
1150
1290
|
const accepted = await enqueueDispatch(redeemed);
|
|
1151
1291
|
return controlResult("accepted", accepted);
|
|
1152
|
-
}
|
|
1153
|
-
const
|
|
1292
|
+
};
|
|
1293
|
+
const control = await openAgentHostControl(input.home, payload, () => snapshot, handleControl);
|
|
1294
|
+
humanConsole = process.stdin.isTTY
|
|
1154
1295
|
? createInterface({ input: process.stdin, output: process.stdout, terminal: true })
|
|
1155
1296
|
: undefined;
|
|
1156
1297
|
promptHuman = () => {
|
|
@@ -1231,6 +1372,7 @@ export async function runAgentHost(input) {
|
|
|
1231
1372
|
if (forceKillTimer !== undefined)
|
|
1232
1373
|
clearTimeout(forceKillTimer);
|
|
1233
1374
|
humanConsole?.close();
|
|
1375
|
+
nativeTui?.close();
|
|
1234
1376
|
await closeAgentHostEndpoints({
|
|
1235
1377
|
owner: endpointOwner, session, lease: endpointLease, adapterId: endpointAdapterId,
|
|
1236
1378
|
attemptId: activeRunAttemptId ?? lastTerminal?.attemptId,
|
|
@@ -1309,6 +1451,9 @@ export async function sendAgentHostAuthorityControl(input) {
|
|
|
1309
1451
|
export async function sendAgentHostCancelControl(input) {
|
|
1310
1452
|
return await sendAgentHostControl(input);
|
|
1311
1453
|
}
|
|
1454
|
+
export async function sendAgentHostNativeControl(input) {
|
|
1455
|
+
return sendAgentHostControl(input);
|
|
1456
|
+
}
|
|
1312
1457
|
export async function inspectAgentHost(input) {
|
|
1313
1458
|
const result = await sendAgentHostControl({
|
|
1314
1459
|
...input,
|
|
@@ -1550,6 +1695,12 @@ function boundControlResponse(result) {
|
|
|
1550
1695
|
};
|
|
1551
1696
|
if (withinControlBound(result))
|
|
1552
1697
|
return result;
|
|
1698
|
+
if (result.snapshot.publicReply !== undefined) {
|
|
1699
|
+
const { publicReply: _omitted, ...snapshot } = result.snapshot;
|
|
1700
|
+
result = { ...result, snapshot };
|
|
1701
|
+
if (withinControlBound(result))
|
|
1702
|
+
return result;
|
|
1703
|
+
}
|
|
1553
1704
|
const clip = (text, chars) => {
|
|
1554
1705
|
if (text.length <= chars)
|
|
1555
1706
|
return text;
|
|
@@ -1613,6 +1764,22 @@ function validateControl(control) {
|
|
|
1613
1764
|
}
|
|
1614
1765
|
if (control.type === "status")
|
|
1615
1766
|
return Object.freeze({ ...control });
|
|
1767
|
+
if (control.type === "native-open" || control.type === "native-respond" || control.type === "native-model") {
|
|
1768
|
+
validateIdentity(control.nativeSessionId, "native Session id");
|
|
1769
|
+
if (control.type === "native-model" && (typeof control.model !== "string"
|
|
1770
|
+
|| !control.model.trim() || control.model.length > 200 || control.model.includes("\0"))) {
|
|
1771
|
+
throw new Error("Invalid native model.");
|
|
1772
|
+
}
|
|
1773
|
+
if (control.type === "native-respond") {
|
|
1774
|
+
if (control.nativeTurnId !== null)
|
|
1775
|
+
validateIdentity(control.nativeTurnId, "native Turn id");
|
|
1776
|
+
if ((typeof control.requestId !== "string" && typeof control.requestId !== "number")
|
|
1777
|
+
|| control.result === null || typeof control.result !== "object" || Array.isArray(control.result)) {
|
|
1778
|
+
throw new Error("Invalid native request response.");
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
return Object.freeze({ ...control });
|
|
1782
|
+
}
|
|
1616
1783
|
if (control.type === "cancel") {
|
|
1617
1784
|
if (control.nativeOnly !== undefined && typeof control.nativeOnly !== "boolean") {
|
|
1618
1785
|
throw new Error("Native-only cancellation flag is invalid.");
|
|
@@ -42,7 +42,7 @@ export class CodexAppServerRuntime {
|
|
|
42
42
|
cwd: text(input.cwd, "Codex thread cwd"),
|
|
43
43
|
...threadOptions(input)
|
|
44
44
|
});
|
|
45
|
-
return { conversationId: threadId(result) };
|
|
45
|
+
return { conversationId: threadId(result), raw: result };
|
|
46
46
|
}
|
|
47
47
|
async resumeConversation(conversationId, options = {}) {
|
|
48
48
|
const id = text(conversationId, "Codex thread id");
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { unknownAgentRunConfiguration } from "./agentRunConfiguration.js";
|
|
3
|
+
import { CodexPreSubmissionError } from "./codexAppServerRuntime.js";
|
|
4
|
+
const READS = new Set([
|
|
5
|
+
"config/read", "configRequirements/read", "model/list", "collaborationMode/list",
|
|
6
|
+
"experimentalFeature/list", "skills/list", "hooks/list", "mcpServerStatus/list", "account/read",
|
|
7
|
+
"account/rateLimits/read", "modelProvider/capabilities/read", "permissionProfile/list",
|
|
8
|
+
"thread/read", "thread/turns/list", "thread/items/list", "thread/goal/get"
|
|
9
|
+
]);
|
|
10
|
+
const REQUESTS = new Set([
|
|
11
|
+
"item/commandExecution/requestApproval", "item/fileChange/requestApproval",
|
|
12
|
+
"item/tool/requestUserInput", "item/permissions/requestApproval", "mcpServer/elicitation/request"
|
|
13
|
+
]);
|
|
14
|
+
/** A view of one existing connection, never another Thread or durable input queue.
|
|
15
|
+
* Both the native TUI and Web answer the original server request on this transport. */
|
|
16
|
+
export function createCodexNativeAccess(threadId, initialized, channel, openingMessages = [], initialConfiguration) {
|
|
17
|
+
const pending = new Map();
|
|
18
|
+
const listeners = new Set();
|
|
19
|
+
let configuration = initialConfiguration;
|
|
20
|
+
let observedAt = new Date().toISOString();
|
|
21
|
+
const observed = () => typeof configuration?.model !== "string"
|
|
22
|
+
? unknownAgentRunConfiguration("No confirmed native model response. Reconnect to restore the fixed launch configuration.")
|
|
23
|
+
: {
|
|
24
|
+
status: "observed", observedAt,
|
|
25
|
+
handshake: { status: "unsupported", reason: "Model reported by Codex thread/start or thread/resume; not a capability handshake." },
|
|
26
|
+
requested: [],
|
|
27
|
+
axes: [{ key: "model", category: "model", current: { status: "observed", value: configuration.model }, offered: [] }]
|
|
28
|
+
};
|
|
29
|
+
const observe = (message) => {
|
|
30
|
+
const params = message.params;
|
|
31
|
+
if (params?.threadId !== threadId)
|
|
32
|
+
return;
|
|
33
|
+
if ((typeof message.id === "string" || typeof message.id === "number")
|
|
34
|
+
&& typeof message.method === "string" && REQUESTS.has(message.method)
|
|
35
|
+
&& (typeof params.turnId === "string"
|
|
36
|
+
|| (message.method === "mcpServer/elicitation/request" && params.turnId == null))) {
|
|
37
|
+
pending.set(message.id, { id: message.id, method: message.method,
|
|
38
|
+
threadId, turnId: typeof params.turnId === "string" ? params.turnId : null, params });
|
|
39
|
+
}
|
|
40
|
+
if (message.method === "serverRequest/resolved")
|
|
41
|
+
pending.delete(params.requestId);
|
|
42
|
+
if (message.method === "turn/completed") {
|
|
43
|
+
const turn = params.turn;
|
|
44
|
+
for (const [id, request] of pending)
|
|
45
|
+
if (request.turnId === turn?.id)
|
|
46
|
+
pending.delete(id);
|
|
47
|
+
}
|
|
48
|
+
for (const listener of listeners)
|
|
49
|
+
listener(message);
|
|
50
|
+
};
|
|
51
|
+
for (const message of openingMessages)
|
|
52
|
+
observe(message);
|
|
53
|
+
channel.onMessage(observe);
|
|
54
|
+
return {
|
|
55
|
+
initialized,
|
|
56
|
+
get attached() { return listeners.size > 0; },
|
|
57
|
+
get requests() { return [...pending.values()]; },
|
|
58
|
+
get runConfiguration() { return observed(); },
|
|
59
|
+
async setModel(model) {
|
|
60
|
+
try {
|
|
61
|
+
if (!model.trim() || model.length > 200 || model.includes("\0"))
|
|
62
|
+
throw new Error("Invalid model.");
|
|
63
|
+
const read = await channel.request("thread/read", { threadId, includeTurns: false });
|
|
64
|
+
const thread = read.thread;
|
|
65
|
+
if (thread?.id !== threadId || thread.status?.type !== "idle") {
|
|
66
|
+
throw new Error("Model switching requires a confirmed idle Thread.");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
throw new CodexPreSubmissionError(error instanceof Error ? error.message : "Native idle state is unavailable.");
|
|
71
|
+
}
|
|
72
|
+
// No fork/start, account, permission or Role-default mutation. Do not
|
|
73
|
+
// preserve an old observation if the native mutation becomes uncertain.
|
|
74
|
+
configuration = undefined;
|
|
75
|
+
const result = await channel.request("thread/resume", { threadId, model, excludeTurns: true });
|
|
76
|
+
if (result.thread?.id !== threadId || result.model !== model) {
|
|
77
|
+
throw new Error("Native model adoption was not confirmed; inspect before another change.");
|
|
78
|
+
}
|
|
79
|
+
configuration = result;
|
|
80
|
+
observedAt = new Date().toISOString();
|
|
81
|
+
},
|
|
82
|
+
async read(method, params) {
|
|
83
|
+
// TUI startup attaches to the already opened Thread without resuming it
|
|
84
|
+
// with new options or changing the Host's configured execution boundary.
|
|
85
|
+
if (method === "thread/resume") {
|
|
86
|
+
if (params.threadId !== threadId)
|
|
87
|
+
throw new Error("Select the existing Session; new/forked Threads are not supported here.");
|
|
88
|
+
const page = params.initialTurnsPage;
|
|
89
|
+
configuration = await channel.request("thread/resume", { threadId,
|
|
90
|
+
...(typeof params.excludeTurns === "boolean" ? { excludeTurns: params.excludeTurns } : {}),
|
|
91
|
+
...(page ? { initialTurnsPage: { ...page, limit: Math.min(40, typeof page.limit === "number" && page.limit > 0 ? page.limit : 40) } } : {}) });
|
|
92
|
+
observedAt = new Date().toISOString();
|
|
93
|
+
return configuration;
|
|
94
|
+
}
|
|
95
|
+
if (!READS.has(method))
|
|
96
|
+
throw new Error(`Native operation is not available in managed access: ${method}.`);
|
|
97
|
+
if (method.startsWith("thread/") && params.threadId !== threadId) {
|
|
98
|
+
throw new Error("Native read targets a different Thread.");
|
|
99
|
+
}
|
|
100
|
+
// Native TUI owns its hydration protocol (legacy TUI can explicitly ask
|
|
101
|
+
// for full turns). Web history remains independently bounded/paginated.
|
|
102
|
+
return channel.request(method, method === "thread/turns/list" || method === "thread/items/list"
|
|
103
|
+
? { ...params, limit: Math.min(40, typeof params.limit === "number" && params.limit > 0 ? params.limit : 40) }
|
|
104
|
+
: params);
|
|
105
|
+
},
|
|
106
|
+
validateTurn(params) {
|
|
107
|
+
if (!configuration)
|
|
108
|
+
throw new Error("Native TUI has not attached to the existing Thread.");
|
|
109
|
+
for (const [key, source] of Object.entries({
|
|
110
|
+
model: "model", cwd: "cwd", runtimeWorkspaceRoots: "runtimeWorkspaceRoots",
|
|
111
|
+
approvalPolicy: "approvalPolicy", approvalsReviewer: "approvalsReviewer",
|
|
112
|
+
effort: "reasoningEffort", collaborationMode: "collaborationMode"
|
|
113
|
+
})) {
|
|
114
|
+
if (params[key] != null && !isDeepStrictEqual(params[key], configuration[source])) {
|
|
115
|
+
throw new Error(`Native ${key} differs from the managed Session; change it through Yui configuration.`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const key of ["sandboxPolicy", "permissions", "environments", "toolOutput", "additionalContext", "outputSchema", "disabledPluginIds", "serviceTierForTurn", "summary", "personality", "cyberAccessProgram"]) {
|
|
119
|
+
if (params[key] != null)
|
|
120
|
+
throw new Error(`Native ${key} overrides are not supported in managed access.`);
|
|
121
|
+
}
|
|
122
|
+
if (params.serviceTier != null && params.serviceTier !== (configuration.serviceTier ?? "default")) {
|
|
123
|
+
throw new Error("Native service tier differs from the managed Session.");
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
async respond(id, expectedThread, turnId, result) {
|
|
127
|
+
const request = pending.get(id);
|
|
128
|
+
if (!request || request.threadId !== expectedThread || request.turnId !== turnId) {
|
|
129
|
+
throw new Error("Native request is no longer pending for this exact Thread and Turn.");
|
|
130
|
+
}
|
|
131
|
+
validateNativeResponse(request, result);
|
|
132
|
+
// Claim before awaiting transport. A lost send acknowledgement is unknown,
|
|
133
|
+
// not permission for another entry to repeat a possibly applied answer.
|
|
134
|
+
pending.delete(id);
|
|
135
|
+
await channel.send({ id, result });
|
|
136
|
+
},
|
|
137
|
+
events(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; }
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function validateNativeResponse(request, result) {
|
|
141
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
142
|
+
throw new Error("Missing native response.");
|
|
143
|
+
if (request.method === "mcpServer/elicitation/request") {
|
|
144
|
+
if (!["accept", "decline", "cancel"].includes(String(result.action))
|
|
145
|
+
|| (result.action !== "accept" && result.content != null)) {
|
|
146
|
+
throw new Error("Invalid native MCP elicitation response.");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (request.method === "item/tool/requestUserInput") {
|
|
150
|
+
const answers = result.answers;
|
|
151
|
+
const questions = request.params.questions;
|
|
152
|
+
if (!answers || typeof answers !== "object" || Array.isArray(answers) || !Array.isArray(questions)) {
|
|
153
|
+
throw new Error("Native question requires answers keyed by question id.");
|
|
154
|
+
}
|
|
155
|
+
const ids = questions.map(q => q.id);
|
|
156
|
+
if (Object.keys(answers).some(id => !ids.includes(id))
|
|
157
|
+
|| ids.some(id => {
|
|
158
|
+
if (typeof id !== "string")
|
|
159
|
+
return true;
|
|
160
|
+
const answer = answers[id];
|
|
161
|
+
return !Array.isArray(answer?.answers) || answer.answers.some(value => typeof value !== "string");
|
|
162
|
+
})) {
|
|
163
|
+
throw new Error("Native answers do not match the original questions.");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else if (request.method === "item/permissions/requestApproval") {
|
|
167
|
+
// Web currently exposes decline only; richer native grants remain the TUI's
|
|
168
|
+
// protocol responsibility. Never infer broad grants from an accept button.
|
|
169
|
+
if (!result.permissions || typeof result.permissions !== "object")
|
|
170
|
+
throw new Error("Missing native permissions response.");
|
|
171
|
+
}
|
|
172
|
+
else if (!["accept", "acceptForSession", "decline", "cancel"].includes(String(result.decision))) {
|
|
173
|
+
throw new Error("Invalid native approval decision.");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { once } from "node:events";
|
|
4
|
+
import WebSocket, { WebSocketServer } from "ws";
|
|
5
|
+
import { localCodexTuiEnvironment } from "./codexInteractiveHost.js";
|
|
6
|
+
/** The TUI is a view of the Host-owned connection. It cannot launch, fork,
|
|
7
|
+
* reconfigure, or submit outside the Host's existing input admission. */
|
|
8
|
+
export async function openControlledCodexTui(payload, threadId, access, input) {
|
|
9
|
+
const baseArgs = JSON.parse(payload.environment.YUI_AGENT_BASE_ARGS ?? "[]");
|
|
10
|
+
if (!Array.isArray(baseArgs) || baseArgs.some(arg => typeof arg !== "string"))
|
|
11
|
+
throw new Error("Invalid Codex base arguments.");
|
|
12
|
+
const token = randomBytes(32).toString("hex");
|
|
13
|
+
const server = new WebSocketServer({
|
|
14
|
+
host: "127.0.0.1", port: 0, maxPayload: 1024 * 1024, perMessageDeflate: false,
|
|
15
|
+
verifyClient: (info) => info.req.headers.authorization === `Bearer ${token}`
|
|
16
|
+
});
|
|
17
|
+
let socket;
|
|
18
|
+
let closed = false;
|
|
19
|
+
let markReady;
|
|
20
|
+
let failReady;
|
|
21
|
+
const ready = new Promise((resolve, reject) => { markReady = resolve; failReady = reject; });
|
|
22
|
+
void ready.catch(() => { });
|
|
23
|
+
const unsubscribe = access.events(message => {
|
|
24
|
+
if (socket?.readyState === WebSocket.OPEN)
|
|
25
|
+
socket.send(JSON.stringify(message));
|
|
26
|
+
});
|
|
27
|
+
server.on("connection", client => {
|
|
28
|
+
if (socket || closed) {
|
|
29
|
+
client.close(1008, "TUI already attached.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
socket = client;
|
|
33
|
+
client.on("error", input.onError);
|
|
34
|
+
client.on("message", (bytes, binary) => {
|
|
35
|
+
void (async () => {
|
|
36
|
+
if (binary)
|
|
37
|
+
throw new Error("Expected native JSON request.");
|
|
38
|
+
const message = JSON.parse(bytes.toString());
|
|
39
|
+
const id = message.id;
|
|
40
|
+
if (typeof id !== "number" && typeof id !== "string")
|
|
41
|
+
return; // notifications
|
|
42
|
+
try {
|
|
43
|
+
if (message.method === undefined) {
|
|
44
|
+
const request = access.requests.find(request => request.id === id);
|
|
45
|
+
if (!request)
|
|
46
|
+
throw new Error("Native request is no longer pending.");
|
|
47
|
+
await input.respond(id, request.turnId, message.result);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const method = String(message.method), params = (message.params ?? {});
|
|
51
|
+
if (method === "turn/start")
|
|
52
|
+
access.validateTurn(params);
|
|
53
|
+
const result = method === "initialize" ? access.initialized
|
|
54
|
+
: ["turn/start", "turn/steer", "turn/interrupt"].includes(method) ? await input.mutate(method, params)
|
|
55
|
+
: await access.read(method, params);
|
|
56
|
+
if (client.readyState === WebSocket.OPEN)
|
|
57
|
+
client.send(JSON.stringify({ id, result }));
|
|
58
|
+
if (method === "thread/resume") {
|
|
59
|
+
markReady();
|
|
60
|
+
for (const request of access.requests)
|
|
61
|
+
client.send(JSON.stringify({
|
|
62
|
+
id: request.id, method: request.method, params: request.params
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (client.readyState === WebSocket.OPEN)
|
|
68
|
+
client.send(JSON.stringify({
|
|
69
|
+
id, error: { code: -32600, message: error instanceof Error ? error.message : String(error) }
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
})().catch(input.onError);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
await once(server, "listening");
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
unsubscribe();
|
|
80
|
+
server.close();
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
const address = server.address();
|
|
84
|
+
if (!address || typeof address === "string")
|
|
85
|
+
throw new Error("Native TUI relay did not bind.");
|
|
86
|
+
const child = spawn(payload.command, [...baseArgs,
|
|
87
|
+
"--remote", `ws://127.0.0.1:${address.port}`, "--remote-auth-token-env", "YUI_CODEX_REMOTE_AUTH_TOKEN",
|
|
88
|
+
"resume", threadId], {
|
|
89
|
+
cwd: payload.cwd, stdio: "inherit",
|
|
90
|
+
env: { ...localCodexTuiEnvironment(payload.environment), YUI_CODEX_REMOTE_AUTH_TOKEN: token }
|
|
91
|
+
});
|
|
92
|
+
const close = () => {
|
|
93
|
+
if (closed)
|
|
94
|
+
return;
|
|
95
|
+
closed = true;
|
|
96
|
+
unsubscribe();
|
|
97
|
+
socket?.terminate();
|
|
98
|
+
server.close();
|
|
99
|
+
child.kill("SIGTERM");
|
|
100
|
+
};
|
|
101
|
+
server.on("error", input.onError);
|
|
102
|
+
child.on("error", error => { failReady(error); input.onError(error); close(); input.onExit(); });
|
|
103
|
+
child.once("exit", () => { failReady(new Error("Native TUI exited before attachment.")); close(); input.onExit(); });
|
|
104
|
+
const deadline = setTimeout(() => failReady(new Error("Native TUI did not attach to the selected Thread.")), 10_000);
|
|
105
|
+
try {
|
|
106
|
+
await ready;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
close();
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
clearTimeout(deadline);
|
|
114
|
+
}
|
|
115
|
+
return { close };
|
|
116
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { controllerSocketPath } from "../core/controllerEndpoint.js";
|
|
3
|
+
import { managedIntegrationRuntimeRoot } from "../storage/homeLayout.js";
|
|
4
|
+
import { yuiTmuxServerName } from "../tmux/tmuxManager.js";
|
|
5
|
+
import { FileTaskRuntimeIsolation } from "./taskRuntimeIsolation.js";
|
|
6
|
+
/** One identity/layout for Integration execution and exact-owner cleanup. */
|
|
7
|
+
export function defaultIntegrationRuntimeIsolation(home, homeId) {
|
|
8
|
+
const controlHome = resolve(home);
|
|
9
|
+
const runtimeRoot = managedIntegrationRuntimeRoot(controlHome);
|
|
10
|
+
return new FileTaskRuntimeIsolation({
|
|
11
|
+
runtimeRoot,
|
|
12
|
+
pathLayout: "compact",
|
|
13
|
+
controlPlane: {
|
|
14
|
+
yuiHome: controlHome,
|
|
15
|
+
managedRuntimeRoot: runtimeRoot,
|
|
16
|
+
controllerSocketPath: controllerSocketPath(homeId),
|
|
17
|
+
tmuxNamespace: yuiTmuxServerName(controlHome),
|
|
18
|
+
globalInstallPaths: [process.execPath]
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function foldPublicReply(previous, nativeSessionId, method, params) {
|
|
2
|
+
if (params.threadId !== nativeSessionId)
|
|
3
|
+
return previous;
|
|
4
|
+
if (method === "turn/started")
|
|
5
|
+
return undefined;
|
|
6
|
+
if (typeof params.turnId !== "string")
|
|
7
|
+
return previous;
|
|
8
|
+
if (method === "item/agentMessage/delta" && typeof params.itemId === "string" && typeof params.delta === "string") {
|
|
9
|
+
const same = previous?.id === params.itemId && previous.turnId === params.turnId;
|
|
10
|
+
const text = (same ? previous.text : "") + params.delta;
|
|
11
|
+
return { nativeSessionId, turnId: params.turnId, id: params.itemId,
|
|
12
|
+
text: text.slice(0, 6000), truncated: text.length > 6000 || !!(same && previous.truncated) };
|
|
13
|
+
}
|
|
14
|
+
if ((method === "item/started" || method === "item/completed")
|
|
15
|
+
&& params.item !== null && typeof params.item === "object") {
|
|
16
|
+
const item = params.item;
|
|
17
|
+
if (item.type === "agentMessage" && typeof item.id === "string" && typeof item.text === "string") {
|
|
18
|
+
return { nativeSessionId, turnId: params.turnId, id: item.id,
|
|
19
|
+
text: item.text.slice(0, 6000), truncated: item.text.length > 6000 };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return previous;
|
|
23
|
+
}
|