@opengeni/runtime 2.5.3-canary.0 → 2.5.3-canary.2
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/dist/{chunk-SD6MDONS.js → chunk-3EXLW2IW.js} +150 -34
- package/dist/chunk-3EXLW2IW.js.map +1 -0
- package/dist/{chunk-VVEGJPDR.js → chunk-46BFUGFL.js} +317 -27
- package/dist/chunk-46BFUGFL.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/operational-instructions.d.ts +1 -1
- package/dist/sandbox/index.js +1 -1
- package/dist/sandbox/routing/backend-resolver.d.ts +1 -0
- package/dist/sandbox/selfhosted/file-transfer.d.ts +20 -0
- package/dist/sandbox/selfhosted/session.d.ts +7 -0
- package/dist/workspace-skills.d.ts +4 -0
- package/dist/workspace-tool-gateway.js +2 -2
- package/package.json +11 -11
- package/src/bundled_default_skills/document-parsing/SKILL.md +36 -0
- package/src/bundled_site_skills/opengeni-sites/SKILL.md +1 -1
- package/src/lazy-tool-transport.ts +2 -0
- package/src/operational-instructions.ts +1 -1
- package/src/runtime-skills.ts +7 -2
- package/src/sandbox/routing/backend-resolver.ts +2 -0
- package/src/sandbox/selfhosted/control-rpc.ts +22 -0
- package/src/sandbox/selfhosted/fault-rendering.ts +9 -0
- package/src/sandbox/selfhosted/file-transfer.ts +160 -0
- package/src/sandbox/selfhosted/op-testing.ts +3 -0
- package/src/sandbox/selfhosted/session.ts +194 -5
- package/src/workspace-skills.ts +171 -31
- package/dist/chunk-SD6MDONS.js.map +0 -1
- package/dist/chunk-VVEGJPDR.js.map +0 -1
|
@@ -15993,6 +15993,9 @@ function agentErrorToControlError(err, controlRequestId = null) {
|
|
|
15993
15993
|
}
|
|
15994
15994
|
}
|
|
15995
15995
|
function payloadTooLargeMessage(detail) {
|
|
15996
|
+
if (detail.direction === "request") {
|
|
15997
|
+
return "The request exceeds the machine link's per-message size limit and was not sent. The machine is not offline. Use a supported bounded transfer for large file content.";
|
|
15998
|
+
}
|
|
15996
15999
|
const encoded = detail.encoded_bytes;
|
|
15997
16000
|
const max = detail.max_payload;
|
|
15998
16001
|
const sizes = encoded && max ? `The result was ${encoded} bytes, over the machine link's ${max}-byte per-message limit. ` : "The result was larger than the machine link can deliver in a single message. ";
|
|
@@ -16105,6 +16108,18 @@ var NatsControlRpc = class {
|
|
|
16105
16108
|
} catch (err) {
|
|
16106
16109
|
const denied = natsAuthorizationFailure(err);
|
|
16107
16110
|
if (denied) throw denied;
|
|
16111
|
+
if (typeof err === "object" && err !== null && "code" in err && err.code === "MAX_PAYLOAD_EXCEEDED") {
|
|
16112
|
+
return {
|
|
16113
|
+
requestId: req.requestId,
|
|
16114
|
+
error: {
|
|
16115
|
+
code: ErrorCode.ERROR_CODE_PAYLOAD_TOO_LARGE,
|
|
16116
|
+
message: "outbound control request exceeds the transport payload limit",
|
|
16117
|
+
retryable: false,
|
|
16118
|
+
detail: { direction: "request", encoded_bytes: String(payload.byteLength) }
|
|
16119
|
+
},
|
|
16120
|
+
result: void 0
|
|
16121
|
+
};
|
|
16122
|
+
}
|
|
16108
16123
|
if (isNoRespondersError(err)) {
|
|
16109
16124
|
return offlineControlResponse(req.requestId, true);
|
|
16110
16125
|
}
|
|
@@ -16185,6 +16200,15 @@ function assemble(error, f) {
|
|
|
16185
16200
|
function renderSelfhostedFault(error) {
|
|
16186
16201
|
const detail = error.detail;
|
|
16187
16202
|
if (error.payloadTooLarge || error.code === ErrorCode2.ERROR_CODE_PAYLOAD_TOO_LARGE) {
|
|
16203
|
+
if (detail.direction === "request") {
|
|
16204
|
+
return assemble(error, {
|
|
16205
|
+
headline: "the request was too large to send",
|
|
16206
|
+
happened: "the request was not sent because it exceeded the per-message size limit",
|
|
16207
|
+
layer: "the machine link's outbound request size limit, not machine availability",
|
|
16208
|
+
preserved: "the rejected request did not execute on the machine",
|
|
16209
|
+
tryNext: "use a supported bounded transfer; retrying the unchanged request will not help"
|
|
16210
|
+
});
|
|
16211
|
+
}
|
|
16188
16212
|
if (detail.failure_code === "OP_OVERFLOW") {
|
|
16189
16213
|
const retained = detail.retained_bytes ? `after retaining ${detail.retained_bytes} bytes of output` : "when its output exceeded what the machine can retain for delivery";
|
|
16190
16214
|
return assemble(error, {
|
|
@@ -25876,6 +25900,120 @@ import {
|
|
|
25876
25900
|
} from "@opengeni/agent-proto";
|
|
25877
25901
|
import { DESKTOP_STREAM_PORT as DESKTOP_STREAM_PORT4 } from "@opengeni/contracts";
|
|
25878
25902
|
|
|
25903
|
+
// src/sandbox/selfhosted/file-transfer.ts
|
|
25904
|
+
import { blake3 } from "@noble/hashes/blake3";
|
|
25905
|
+
import { bytesToHex } from "@noble/hashes/utils";
|
|
25906
|
+
import { OpState } from "@opengeni/agent-proto";
|
|
25907
|
+
var SELFHOSTED_FILE_CHUNK_BYTES = 256 * 1024;
|
|
25908
|
+
function fileContentDigest(content) {
|
|
25909
|
+
return bytesToHex(blake3(content));
|
|
25910
|
+
}
|
|
25911
|
+
var FileTransferOutcomeError = class extends Error {
|
|
25912
|
+
constructor(operationId, phase, cause, observationError) {
|
|
25913
|
+
const detail = cause instanceof Error ? cause.message : "unknown failure";
|
|
25914
|
+
const observation = observationError instanceof Error ? ` Outcome check: ${observationError.message}` : "";
|
|
25915
|
+
super(
|
|
25916
|
+
`File transfer ${operationId} ${phase} failed: ${detail}.${observation} No mutation was replayed; inspect the destination before another edit.`,
|
|
25917
|
+
{ cause }
|
|
25918
|
+
);
|
|
25919
|
+
this.operationId = operationId;
|
|
25920
|
+
this.phase = phase;
|
|
25921
|
+
this.observationError = observationError;
|
|
25922
|
+
this.name = "FileTransferOutcomeError";
|
|
25923
|
+
}
|
|
25924
|
+
retryable = false;
|
|
25925
|
+
};
|
|
25926
|
+
async function transferEditorFile(input) {
|
|
25927
|
+
const opId = `fsw-${crypto.randomUUID()}`;
|
|
25928
|
+
const digest = fileContentDigest(input.content);
|
|
25929
|
+
const checked = (reply) => {
|
|
25930
|
+
if (reply.error) throw agentErrorToControlError(reply.error, reply.requestId);
|
|
25931
|
+
if (!reply.result) throw new Error("The machine omitted the file-transfer result.");
|
|
25932
|
+
return reply.result;
|
|
25933
|
+
};
|
|
25934
|
+
const send = async (requestId2, op) => {
|
|
25935
|
+
const reply = await input.request(requestId2, op);
|
|
25936
|
+
if (reply.requestId !== requestId2)
|
|
25937
|
+
throw new Error("The machine returned a mismatched file-transfer response identity.");
|
|
25938
|
+
return checked(reply);
|
|
25939
|
+
};
|
|
25940
|
+
const isVerified = (observed) => !(observed.$case !== "opStatus" || observed.opStatus.opId !== opId || observed.opStatus.state !== OpState.OP_STATE_COMPLETE || observed.opStatus.exit?.exitCode !== 0 || observed.opStatus.exit.cancelled || observed.opStatus.exit.timedOut || observed.opStatus.exit.failureCode || observed.opStatus.exit.digests.content !== digest || observed.opStatus.exit.totals.content !== String(input.content.byteLength));
|
|
25941
|
+
const verifyOutcome = async (cancelRunning = false) => {
|
|
25942
|
+
const observed = await send(crypto.randomUUID(), { $case: "opQuery", opQuery: { opId } });
|
|
25943
|
+
if (isVerified(observed)) return;
|
|
25944
|
+
if (cancelRunning && observed.$case === "opStatus" && observed.opStatus.opId === opId && observed.opStatus.state === OpState.OP_STATE_RUNNING) {
|
|
25945
|
+
const cancelled = await send(crypto.randomUUID(), { $case: "opCancel", opCancel: { opId } });
|
|
25946
|
+
if (isVerified(cancelled)) return;
|
|
25947
|
+
if (cancelled.$case === "opStatus" && cancelled.opStatus.opId === opId && cancelled.opStatus.state === OpState.OP_STATE_COMPLETE && cancelled.opStatus.exit?.cancelled) {
|
|
25948
|
+
throw new Error("Cancellation of the abandoned transfer's private staging was verified.");
|
|
25949
|
+
}
|
|
25950
|
+
}
|
|
25951
|
+
throw new Error(
|
|
25952
|
+
"The file-transfer outcome is not verified; the operation was not replayed. Inspect the destination before another edit."
|
|
25953
|
+
);
|
|
25954
|
+
};
|
|
25955
|
+
let phase = "begin";
|
|
25956
|
+
try {
|
|
25957
|
+
const begun = await send(opId, {
|
|
25958
|
+
$case: "opStart",
|
|
25959
|
+
opStart: {
|
|
25960
|
+
op: {
|
|
25961
|
+
$case: "fsWrite",
|
|
25962
|
+
fsWrite: {
|
|
25963
|
+
path: input.path,
|
|
25964
|
+
createParents: true,
|
|
25965
|
+
mode: 0,
|
|
25966
|
+
expectedBaseDigest: input.baseContent === void 0 ? "" : fileContentDigest(input.baseContent),
|
|
25967
|
+
expectedAbsent: input.baseContent === void 0,
|
|
25968
|
+
contentDigest: digest,
|
|
25969
|
+
contentSize: String(input.content.byteLength)
|
|
25970
|
+
}
|
|
25971
|
+
},
|
|
25972
|
+
windowBytes: "0",
|
|
25973
|
+
deadlineMs: "0",
|
|
25974
|
+
originId: ""
|
|
25975
|
+
}
|
|
25976
|
+
});
|
|
25977
|
+
if (begun.$case !== "opStart" || !begun.opStart.accepted || begun.opStart.status?.opId !== opId) {
|
|
25978
|
+
throw new Error("The machine did not accept the exact file-transfer operation.");
|
|
25979
|
+
}
|
|
25980
|
+
phase = "upload";
|
|
25981
|
+
let offset = 0;
|
|
25982
|
+
let seq = 0;
|
|
25983
|
+
do {
|
|
25984
|
+
const end = Math.min(input.content.byteLength, offset + SELFHOSTED_FILE_CHUNK_BYTES);
|
|
25985
|
+
const reply = await send(crypto.randomUUID(), {
|
|
25986
|
+
$case: "writeChunk",
|
|
25987
|
+
writeChunk: {
|
|
25988
|
+
opId,
|
|
25989
|
+
seq: String(seq),
|
|
25990
|
+
offset: String(offset),
|
|
25991
|
+
bytes: input.content.subarray(offset, end),
|
|
25992
|
+
last: end === input.content.byteLength
|
|
25993
|
+
}
|
|
25994
|
+
});
|
|
25995
|
+
if (reply.$case !== "writeChunk" || reply.writeChunk.seq !== String(seq)) {
|
|
25996
|
+
throw new Error(
|
|
25997
|
+
"The machine returned a mismatched file-transfer acknowledgment; no chunk was replayed."
|
|
25998
|
+
);
|
|
25999
|
+
}
|
|
26000
|
+
offset = end;
|
|
26001
|
+
seq += 1;
|
|
26002
|
+
} while (offset < input.content.byteLength);
|
|
26003
|
+
phase = "verify";
|
|
26004
|
+
await verifyOutcome();
|
|
26005
|
+
} catch (error) {
|
|
26006
|
+
if (phase === "verify") throw new FileTransferOutcomeError(opId, phase, error);
|
|
26007
|
+
try {
|
|
26008
|
+
await verifyOutcome(true);
|
|
26009
|
+
return { recovered: true };
|
|
26010
|
+
} catch (observationError) {
|
|
26011
|
+
throw new FileTransferOutcomeError(opId, phase, error, observationError);
|
|
26012
|
+
}
|
|
26013
|
+
}
|
|
26014
|
+
return { recovered: false };
|
|
26015
|
+
}
|
|
26016
|
+
|
|
25879
26017
|
// src/sandbox/selfhosted/retry-policy.ts
|
|
25880
26018
|
var SELFHOSTED_IDEMPOTENT_READONLY_OPS = /* @__PURE__ */ new Set([
|
|
25881
26019
|
"ping",
|
|
@@ -25929,15 +26067,15 @@ var defaultSelfhostedRetryClock = {
|
|
|
25929
26067
|
};
|
|
25930
26068
|
|
|
25931
26069
|
// src/sandbox/selfhosted/op-stream.ts
|
|
25932
|
-
import { blake3 } from "@noble/hashes/blake3";
|
|
25933
|
-
import { bytesToHex } from "@noble/hashes/utils";
|
|
26070
|
+
import { blake3 as blake32 } from "@noble/hashes/blake3";
|
|
26071
|
+
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
|
|
25934
26072
|
import {
|
|
25935
26073
|
ErrorCode as ErrorCode3,
|
|
25936
26074
|
OpAck,
|
|
25937
26075
|
OpChannel,
|
|
25938
26076
|
OpFrame,
|
|
25939
26077
|
OpLostReason,
|
|
25940
|
-
OpState
|
|
26078
|
+
OpState as OpState2
|
|
25941
26079
|
} from "@opengeni/agent-proto";
|
|
25942
26080
|
|
|
25943
26081
|
// src/sandbox/selfhosted/op-transport.ts
|
|
@@ -26260,7 +26398,7 @@ var OpConsumer = class {
|
|
|
26260
26398
|
};
|
|
26261
26399
|
outputFrames = [];
|
|
26262
26400
|
outputDecoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
|
26263
|
-
hashes = { stdout:
|
|
26401
|
+
hashes = { stdout: blake32.create(), stderr: blake32.create() };
|
|
26264
26402
|
totals = { stdout: 0n, stderr: 0n };
|
|
26265
26403
|
captureOnly = false;
|
|
26266
26404
|
receivedPayloadBytes = 0n;
|
|
@@ -26429,7 +26567,7 @@ var OpConsumer = class {
|
|
|
26429
26567
|
if (result.$case !== "opStatus") {
|
|
26430
26568
|
throw protocolError(`op-stream cancel: unexpected result ${result.$case}`);
|
|
26431
26569
|
}
|
|
26432
|
-
if (result.opStatus.state ===
|
|
26570
|
+
if (result.opStatus.state === OpState2.OP_STATE_LOST) {
|
|
26433
26571
|
throw lostToControlError(result.opStatus);
|
|
26434
26572
|
}
|
|
26435
26573
|
await this.attach(0);
|
|
@@ -26498,7 +26636,7 @@ var OpConsumer = class {
|
|
|
26498
26636
|
if (!result.opStart.accepted) {
|
|
26499
26637
|
throw this.refusedStart(status);
|
|
26500
26638
|
}
|
|
26501
|
-
if (status?.state ===
|
|
26639
|
+
if (status?.state === OpState2.OP_STATE_LOST) {
|
|
26502
26640
|
throw lostToControlError(status);
|
|
26503
26641
|
}
|
|
26504
26642
|
return drainingRetries + blipRetries;
|
|
@@ -26541,7 +26679,7 @@ var OpConsumer = class {
|
|
|
26541
26679
|
detail: { cancelled: "true" }
|
|
26542
26680
|
});
|
|
26543
26681
|
}
|
|
26544
|
-
if (status?.state ===
|
|
26682
|
+
if (status?.state === OpState2.OP_STATE_LOST) {
|
|
26545
26683
|
return lostToControlError(status);
|
|
26546
26684
|
}
|
|
26547
26685
|
return protocolError("op-stream start: the runner refused the op without a typed reason");
|
|
@@ -26589,7 +26727,7 @@ var OpConsumer = class {
|
|
|
26589
26727
|
if (result.$case !== "opStatus") {
|
|
26590
26728
|
throw protocolError(`op-stream attach: unexpected result ${result.$case}`);
|
|
26591
26729
|
}
|
|
26592
|
-
if (result.opStatus.state ===
|
|
26730
|
+
if (result.opStatus.state === OpState2.OP_STATE_LOST) {
|
|
26593
26731
|
throw lostToControlError(result.opStatus);
|
|
26594
26732
|
}
|
|
26595
26733
|
return result.opStatus;
|
|
@@ -26779,7 +26917,7 @@ var OpConsumer = class {
|
|
|
26779
26917
|
throw protocolError(`op-stream query: unexpected result ${result.$case}`);
|
|
26780
26918
|
}
|
|
26781
26919
|
const status = result.opStatus;
|
|
26782
|
-
if (status.state ===
|
|
26920
|
+
if (status.state === OpState2.OP_STATE_LOST) {
|
|
26783
26921
|
reject(lostToControlError(status));
|
|
26784
26922
|
return;
|
|
26785
26923
|
}
|
|
@@ -26819,8 +26957,8 @@ var OpConsumer = class {
|
|
|
26819
26957
|
}
|
|
26820
26958
|
const declaredDigest = exit.digests[channel];
|
|
26821
26959
|
if (declaredDigest) {
|
|
26822
|
-
const digest =
|
|
26823
|
-
this.captureOnly ? this.hashes[channel].clone().digest() :
|
|
26960
|
+
const digest = bytesToHex2(
|
|
26961
|
+
this.captureOnly ? this.hashes[channel].clone().digest() : blake32(bytes)
|
|
26824
26962
|
);
|
|
26825
26963
|
if (digest !== declaredDigest) {
|
|
26826
26964
|
throw protocolError(
|
|
@@ -27000,6 +27138,7 @@ var SelfhostedSession = class {
|
|
|
27000
27138
|
resourcePolicy;
|
|
27001
27139
|
resourcePolicySupported;
|
|
27002
27140
|
operationCpuQuotaSupported;
|
|
27141
|
+
transactionalFsWriteSupported;
|
|
27003
27142
|
defaultOpStream;
|
|
27004
27143
|
connectionInstanceId;
|
|
27005
27144
|
resolveOperationAdmission;
|
|
@@ -27059,6 +27198,7 @@ var SelfhostedSession = class {
|
|
|
27059
27198
|
this.resourcePolicy = normalizeOperationResourcePolicy(deps.operationResourcePolicy);
|
|
27060
27199
|
this.resourcePolicySupported = deps.operationResourcePolicySupported === true;
|
|
27061
27200
|
this.operationCpuQuotaSupported = deps.operationCpuQuotaSupported === true;
|
|
27201
|
+
this.transactionalFsWriteSupported = deps.transactionalFsWriteSupported === true;
|
|
27062
27202
|
this.defaultOpStream = deps.opStream;
|
|
27063
27203
|
this.connectionInstanceId = deps.connectionInstanceId;
|
|
27064
27204
|
this.resolveOperationAdmission = deps.resolveOperationAdmission;
|
|
@@ -27070,7 +27210,8 @@ var SelfhostedSession = class {
|
|
|
27070
27210
|
connectionInstanceId: deps.connectionInstanceId,
|
|
27071
27211
|
subject: this.subject,
|
|
27072
27212
|
resourcePolicy: this.resourcePolicy,
|
|
27073
|
-
opStream: deps.opStream
|
|
27213
|
+
opStream: deps.opStream,
|
|
27214
|
+
transactionalFsWriteSupported: this.transactionalFsWriteSupported
|
|
27074
27215
|
}) : void 0;
|
|
27075
27216
|
this.state = {
|
|
27076
27217
|
agentId: deps.agentId,
|
|
@@ -27130,7 +27271,8 @@ var SelfhostedSession = class {
|
|
|
27130
27271
|
connectionInstanceId: this.connectionInstanceId,
|
|
27131
27272
|
subject: this.subject,
|
|
27132
27273
|
resourcePolicy: commandPolicy ? this.resourcePolicy : void 0,
|
|
27133
|
-
opStream: this.defaultOpStream
|
|
27274
|
+
opStream: this.defaultOpStream,
|
|
27275
|
+
transactionalFsWriteSupported: this.transactionalFsWriteSupported
|
|
27134
27276
|
};
|
|
27135
27277
|
}
|
|
27136
27278
|
const resolved = await this.resolveOperationAdmission();
|
|
@@ -27163,7 +27305,8 @@ var SelfhostedSession = class {
|
|
|
27163
27305
|
resolved.connectionInstanceId
|
|
27164
27306
|
),
|
|
27165
27307
|
resourcePolicy: commandPolicy ? resourcePolicy : void 0,
|
|
27166
|
-
opStream: resolved.opStream
|
|
27308
|
+
opStream: resolved.opStream,
|
|
27309
|
+
transactionalFsWriteSupported: resolved.transactionalFsWriteSupported === true
|
|
27167
27310
|
};
|
|
27168
27311
|
}
|
|
27169
27312
|
async admitCommand() {
|
|
@@ -27306,7 +27449,7 @@ var SelfhostedSession = class {
|
|
|
27306
27449
|
machineId: this.agentId,
|
|
27307
27450
|
faultClass: selfhostedFaultClass(error),
|
|
27308
27451
|
// Known only on a PAYLOAD_TOO_LARGE fault (the agent's encoded_bytes detail).
|
|
27309
|
-
...Number.isFinite(encoded) ? { replyBytes: encoded } : {}
|
|
27452
|
+
...Number.isFinite(encoded) && error.detail.direction !== "request" ? { replyBytes: encoded } : {}
|
|
27310
27453
|
});
|
|
27311
27454
|
if (error.draining && drainingRetries > 0) {
|
|
27312
27455
|
throw drainingExhaustedError(error, drainingRetries);
|
|
@@ -27671,8 +27814,145 @@ var SelfhostedSession = class {
|
|
|
27671
27814
|
}
|
|
27672
27815
|
const pathExists3 = (path) => this.pathExists(path, runAs);
|
|
27673
27816
|
const readText2 = async (path) => decoder.decode(await this.readFile({ path, ...runAs ? { runAs } : {} }));
|
|
27674
|
-
const writeText = async (path, content) => {
|
|
27675
|
-
|
|
27817
|
+
const writeText = async (path, content, base) => {
|
|
27818
|
+
const bytes = encoder.encode(content);
|
|
27819
|
+
if (bytes.byteLength <= SELFHOSTED_FILE_CHUNK_BYTES) {
|
|
27820
|
+
await this.writeFile({ path, content, createParents: true });
|
|
27821
|
+
return;
|
|
27822
|
+
}
|
|
27823
|
+
const initial = await this.admitOperation(false);
|
|
27824
|
+
if (!initial.transactionalFsWriteSupported) {
|
|
27825
|
+
await this.writeFile({ path, content, createParents: true });
|
|
27826
|
+
return;
|
|
27827
|
+
}
|
|
27828
|
+
if (runAs) {
|
|
27829
|
+
throw new SelfhostedControlError({
|
|
27830
|
+
code: ErrorCode4.ERROR_CODE_UNSUPPORTED,
|
|
27831
|
+
message: "Transactional file editing does not support runAs impersonation; no file was written.",
|
|
27832
|
+
reason: null,
|
|
27833
|
+
retryable: false
|
|
27834
|
+
});
|
|
27835
|
+
}
|
|
27836
|
+
const requestTransfer = async (requestId2, op) => {
|
|
27837
|
+
const startedAt = Date.now();
|
|
27838
|
+
try {
|
|
27839
|
+
const current = await this.admitOperation(false);
|
|
27840
|
+
if (!current.transactionalFsWriteSupported || current.subject !== initial.subject || current.connectionInstanceId !== initial.connectionInstanceId) {
|
|
27841
|
+
throw new Error(
|
|
27842
|
+
"The authorized machine connection changed during the file transfer; the operation was not replayed."
|
|
27843
|
+
);
|
|
27844
|
+
}
|
|
27845
|
+
const response = await this.controlRpc.request(
|
|
27846
|
+
current.subject,
|
|
27847
|
+
{
|
|
27848
|
+
requestId: requestId2,
|
|
27849
|
+
epoch: this.epoch,
|
|
27850
|
+
op,
|
|
27851
|
+
resourcePolicy: void 0
|
|
27852
|
+
},
|
|
27853
|
+
{ timeoutMs: this.timeoutMs }
|
|
27854
|
+
);
|
|
27855
|
+
if (response.requestId !== requestId2)
|
|
27856
|
+
throw new Error("The machine returned a mismatched file-transfer response identity.");
|
|
27857
|
+
const error = response.error ? agentErrorToControlError(response.error, requestId2) : void 0;
|
|
27858
|
+
this.emitOp({
|
|
27859
|
+
op: op.$case,
|
|
27860
|
+
outcome: error || !response.result ? "failed" : "ok",
|
|
27861
|
+
healed: false,
|
|
27862
|
+
retries: 0,
|
|
27863
|
+
durationMs: Date.now() - startedAt,
|
|
27864
|
+
machineId: this.agentId,
|
|
27865
|
+
...error ? { code: error.code, reason: error.reason, faultClass: selfhostedFaultClass(error) } : {}
|
|
27866
|
+
});
|
|
27867
|
+
return response;
|
|
27868
|
+
} catch (error) {
|
|
27869
|
+
this.emitOp({
|
|
27870
|
+
op: op.$case,
|
|
27871
|
+
outcome: "failed",
|
|
27872
|
+
healed: false,
|
|
27873
|
+
retries: 0,
|
|
27874
|
+
durationMs: Date.now() - startedAt,
|
|
27875
|
+
machineId: this.agentId,
|
|
27876
|
+
...error instanceof SelfhostedControlError ? { code: error.code, reason: error.reason, faultClass: selfhostedFaultClass(error) } : {}
|
|
27877
|
+
});
|
|
27878
|
+
throw error;
|
|
27879
|
+
}
|
|
27880
|
+
};
|
|
27881
|
+
const resolvedBase = typeof base === "function" ? await base() : base;
|
|
27882
|
+
const transferStartedAt = Date.now();
|
|
27883
|
+
try {
|
|
27884
|
+
const result = await transferEditorFile({
|
|
27885
|
+
path: resolveConnectedMachinePath(this.workspaceRoot, path),
|
|
27886
|
+
content: bytes,
|
|
27887
|
+
...resolvedBase === void 0 ? {} : { baseContent: encoder.encode(resolvedBase) },
|
|
27888
|
+
request: requestTransfer
|
|
27889
|
+
});
|
|
27890
|
+
this.emitOp({
|
|
27891
|
+
op: "fsWrite",
|
|
27892
|
+
outcome: "ok",
|
|
27893
|
+
healed: result.recovered,
|
|
27894
|
+
retries: 0,
|
|
27895
|
+
durationMs: Date.now() - transferStartedAt,
|
|
27896
|
+
machineId: this.agentId
|
|
27897
|
+
});
|
|
27898
|
+
} catch (error) {
|
|
27899
|
+
this.emitOp({
|
|
27900
|
+
op: "fsWrite",
|
|
27901
|
+
outcome: "failed",
|
|
27902
|
+
healed: false,
|
|
27903
|
+
retries: 0,
|
|
27904
|
+
durationMs: Date.now() - transferStartedAt,
|
|
27905
|
+
machineId: this.agentId
|
|
27906
|
+
});
|
|
27907
|
+
throw error;
|
|
27908
|
+
}
|
|
27909
|
+
return async (source, sourceBase) => {
|
|
27910
|
+
try {
|
|
27911
|
+
const sourcePath = resolveConnectedMachinePath(this.workspaceRoot, source);
|
|
27912
|
+
const sourceBytes = [];
|
|
27913
|
+
let offset = 0;
|
|
27914
|
+
let total = Infinity;
|
|
27915
|
+
while (offset < total) {
|
|
27916
|
+
const reply2 = await requestTransfer(crypto.randomUUID(), {
|
|
27917
|
+
$case: "fsRead",
|
|
27918
|
+
fsRead: {
|
|
27919
|
+
path: sourcePath,
|
|
27920
|
+
offset: String(offset),
|
|
27921
|
+
length: String(SELFHOSTED_FILE_CHUNK_BYTES)
|
|
27922
|
+
}
|
|
27923
|
+
});
|
|
27924
|
+
if (reply2.error) throw agentErrorToControlError(reply2.error);
|
|
27925
|
+
if (reply2.result?.$case !== "fsRead")
|
|
27926
|
+
throw new Error("Source verification response missing");
|
|
27927
|
+
const part = reply2.result.fsRead;
|
|
27928
|
+
total = safeWireSize(part.totalSize, "source file size");
|
|
27929
|
+
if (!part.content.length && offset < total)
|
|
27930
|
+
throw new Error("Source verification was incomplete");
|
|
27931
|
+
sourceBytes.push(part.content);
|
|
27932
|
+
offset += part.content.length;
|
|
27933
|
+
}
|
|
27934
|
+
const actual = new Uint8Array(offset);
|
|
27935
|
+
let cursor = 0;
|
|
27936
|
+
for (const chunk of sourceBytes) {
|
|
27937
|
+
actual.set(chunk, cursor);
|
|
27938
|
+
cursor += chunk.length;
|
|
27939
|
+
}
|
|
27940
|
+
if (fileContentDigest(actual) !== fileContentDigest(encoder.encode(sourceBase)))
|
|
27941
|
+
throw new Error("Source changed before move cleanup");
|
|
27942
|
+
const reply = await requestTransfer(crypto.randomUUID(), {
|
|
27943
|
+
$case: "fsRemove",
|
|
27944
|
+
fsRemove: { path: sourcePath, recursive: false }
|
|
27945
|
+
});
|
|
27946
|
+
if (reply.error) throw agentErrorToControlError(reply.error);
|
|
27947
|
+
if (reply.result?.$case !== "fsRemove")
|
|
27948
|
+
throw new Error("Source cleanup response missing");
|
|
27949
|
+
} catch (cause) {
|
|
27950
|
+
throw new Error(
|
|
27951
|
+
"The destination edit was verified, but source cleanup was not verified. The move was not replayed; inspect both paths.",
|
|
27952
|
+
{ cause }
|
|
27953
|
+
);
|
|
27954
|
+
}
|
|
27955
|
+
};
|
|
27676
27956
|
};
|
|
27677
27957
|
const deletePath = async (path) => {
|
|
27678
27958
|
const result = await this.call({
|
|
@@ -27698,9 +27978,11 @@ var SelfhostedSession = class {
|
|
|
27698
27978
|
const current = await readText2(operation.path);
|
|
27699
27979
|
const next = applyDiff2(current, operation.diff);
|
|
27700
27980
|
const destination = operation.moveTo ?? operation.path;
|
|
27701
|
-
await
|
|
27981
|
+
const destinationBase = destination === operation.path ? current : async () => await pathExists3(destination) ? await readText2(destination) : void 0;
|
|
27982
|
+
const cleanup = await writeText(destination, next, destinationBase);
|
|
27702
27983
|
if (operation.moveTo && destination !== operation.path) {
|
|
27703
|
-
await
|
|
27984
|
+
if (cleanup) await cleanup(operation.path, current);
|
|
27985
|
+
else await deletePath(operation.path);
|
|
27704
27986
|
}
|
|
27705
27987
|
return {};
|
|
27706
27988
|
},
|
|
@@ -27984,6 +28266,7 @@ var SelfhostedSandboxClient = class {
|
|
|
27984
28266
|
operationResourcePolicy;
|
|
27985
28267
|
operationResourcePolicySupported;
|
|
27986
28268
|
operationCpuQuotaSupported;
|
|
28269
|
+
transactionalFsWriteSupported;
|
|
27987
28270
|
resolveOperationAdmission;
|
|
27988
28271
|
adoptBackgroundCommand;
|
|
27989
28272
|
settleBackgroundCommand;
|
|
@@ -28006,6 +28289,7 @@ var SelfhostedSandboxClient = class {
|
|
|
28006
28289
|
this.operationResourcePolicy = opts.operationResourcePolicy;
|
|
28007
28290
|
this.operationResourcePolicySupported = opts.operationResourcePolicySupported;
|
|
28008
28291
|
this.operationCpuQuotaSupported = opts.operationCpuQuotaSupported;
|
|
28292
|
+
this.transactionalFsWriteSupported = opts.transactionalFsWriteSupported;
|
|
28009
28293
|
this.resolveOperationAdmission = opts.resolveOperationAdmission;
|
|
28010
28294
|
this.adoptBackgroundCommand = opts.adoptBackgroundCommand;
|
|
28011
28295
|
this.settleBackgroundCommand = opts.settleBackgroundCommand;
|
|
@@ -28039,6 +28323,7 @@ var SelfhostedSandboxClient = class {
|
|
|
28039
28323
|
operationResourcePolicySupported: this.operationResourcePolicySupported
|
|
28040
28324
|
} : {},
|
|
28041
28325
|
...this.operationCpuQuotaSupported !== void 0 ? { operationCpuQuotaSupported: this.operationCpuQuotaSupported } : {},
|
|
28326
|
+
transactionalFsWriteSupported: this.transactionalFsWriteSupported === true,
|
|
28042
28327
|
...this.resolveOperationAdmission !== void 0 ? { resolveOperationAdmission: this.resolveOperationAdmission } : {},
|
|
28043
28328
|
...this.adoptBackgroundCommand !== void 0 ? { adoptBackgroundCommand: this.adoptBackgroundCommand } : {},
|
|
28044
28329
|
...this.settleBackgroundCommand !== void 0 ? { settleBackgroundCommand: this.settleBackgroundCommand } : {},
|
|
@@ -28109,6 +28394,7 @@ async function buildSelfhostedBackendSession(deps) {
|
|
|
28109
28394
|
operationResourcePolicySupported: deps.operationResourcePolicySupported
|
|
28110
28395
|
} : {},
|
|
28111
28396
|
...deps.operationCpuQuotaSupported !== void 0 ? { operationCpuQuotaSupported: deps.operationCpuQuotaSupported } : {},
|
|
28397
|
+
transactionalFsWriteSupported: deps.transactionalFsWriteSupported === true,
|
|
28112
28398
|
...deps.resolveOperationAdmission !== void 0 ? { resolveOperationAdmission: deps.resolveOperationAdmission } : {},
|
|
28113
28399
|
...deps.adoptBackgroundCommand !== void 0 ? { adoptBackgroundCommand: deps.adoptBackgroundCommand } : {},
|
|
28114
28400
|
...deps.settleBackgroundCommand !== void 0 ? { settleBackgroundCommand: deps.settleBackgroundCommand } : {},
|
|
@@ -35006,15 +35292,15 @@ function concat(a, b) {
|
|
|
35006
35292
|
}
|
|
35007
35293
|
|
|
35008
35294
|
// src/sandbox/selfhosted/op-testing.ts
|
|
35009
|
-
import { blake3 as
|
|
35010
|
-
import { bytesToHex as
|
|
35295
|
+
import { blake3 as blake33 } from "@noble/hashes/blake3";
|
|
35296
|
+
import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils";
|
|
35011
35297
|
import {
|
|
35012
35298
|
ErrorCode as ErrorCode6,
|
|
35013
35299
|
OpAck as OpAck2,
|
|
35014
35300
|
OpChannel as OpChannel2,
|
|
35015
35301
|
OpFrame as OpFrame2,
|
|
35016
35302
|
OpLostReason as OpLostReason2,
|
|
35017
|
-
OpState as
|
|
35303
|
+
OpState as OpState3
|
|
35018
35304
|
} from "@opengeni/agent-proto";
|
|
35019
35305
|
var InMemoryOpStreamTransport = class {
|
|
35020
35306
|
available = true;
|
|
@@ -35307,8 +35593,9 @@ var FakeOpRunner = class {
|
|
|
35307
35593
|
const complete = !run2.script.live || run2.liveEmitted;
|
|
35308
35594
|
return {
|
|
35309
35595
|
opId,
|
|
35310
|
-
state: complete ?
|
|
35596
|
+
state: complete ? OpState3.OP_STATE_COMPLETE : OpState3.OP_STATE_RUNNING,
|
|
35311
35597
|
nextSeq: complete ? (run2.exitSeq + 1n).toString() : "1",
|
|
35598
|
+
writeOffset: "0",
|
|
35312
35599
|
exit: complete ? run2.exit : void 0,
|
|
35313
35600
|
lostReason: OpLostReason2.OP_LOST_REASON_UNSPECIFIED
|
|
35314
35601
|
};
|
|
@@ -35342,7 +35629,7 @@ var FakeOpRunner = class {
|
|
|
35342
35629
|
const totals = {};
|
|
35343
35630
|
for (const channel of ["stdout", "stderr"]) {
|
|
35344
35631
|
const joined = concat2(channelBytes[channel]);
|
|
35345
|
-
digests[channel] =
|
|
35632
|
+
digests[channel] = bytesToHex3(blake33(joined));
|
|
35346
35633
|
totals[channel] = String(joined.byteLength);
|
|
35347
35634
|
}
|
|
35348
35635
|
const exit = {
|
|
@@ -35448,7 +35735,8 @@ function statusOnly(requestId2, status) {
|
|
|
35448
35735
|
function lostStatus(opId) {
|
|
35449
35736
|
return {
|
|
35450
35737
|
opId,
|
|
35451
|
-
state:
|
|
35738
|
+
state: OpState3.OP_STATE_LOST,
|
|
35739
|
+
writeOffset: "0",
|
|
35452
35740
|
nextSeq: "0",
|
|
35453
35741
|
exit: void 0,
|
|
35454
35742
|
lostReason: OpLostReason2.OP_LOST_REASON_EVICTED
|
|
@@ -35457,7 +35745,8 @@ function lostStatus(opId) {
|
|
|
35457
35745
|
function cancelledStatus(opId) {
|
|
35458
35746
|
return {
|
|
35459
35747
|
opId,
|
|
35460
|
-
state:
|
|
35748
|
+
state: OpState3.OP_STATE_COMPLETE,
|
|
35749
|
+
writeOffset: "0",
|
|
35461
35750
|
nextSeq: "1",
|
|
35462
35751
|
exit: {
|
|
35463
35752
|
exitCode: -1,
|
|
@@ -35577,6 +35866,7 @@ function makeActiveBackendResolver(deps) {
|
|
|
35577
35866
|
operationResourcePolicy: connection.operationResourcePolicy,
|
|
35578
35867
|
operationResourcePolicySupported: connection.operationResourcePolicySupported,
|
|
35579
35868
|
operationCpuQuotaSupported: connection.operationCpuQuotaSupported,
|
|
35869
|
+
transactionalFsWriteSupported: connection.transactionalFsWriteSupported === true,
|
|
35580
35870
|
// The routing proxy caches this session by sandbox+epoch, but command
|
|
35581
35871
|
// policy and runner authority are mutable. Re-read one coherent DB
|
|
35582
35872
|
// snapshot only when a new exec/Git is admitted.
|
|
@@ -37003,4 +37293,4 @@ export {
|
|
|
37003
37293
|
collectSandboxEnvironment2 as collectSandboxEnvironment,
|
|
37004
37294
|
parseExposedPorts2 as parseExposedPorts
|
|
37005
37295
|
};
|
|
37006
|
-
//# sourceMappingURL=chunk-
|
|
37296
|
+
//# sourceMappingURL=chunk-46BFUGFL.js.map
|