@amaster.ai/employee-runtime-connector 0.1.0-beta.20 → 0.1.0-beta.22
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 +8 -0
- package/dist/amaster-runtime-daemon.mjs +371 -90
- package/dist/amaster-runtime.mjs +31 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,14 @@ The source of truth lives under this package's `src/` directory. The package bui
|
|
|
32
32
|
|
|
33
33
|
Runtime code belongs in the container image. Persist only connector state, the result outbox, and workspaces under the configured state directory.
|
|
34
34
|
|
|
35
|
+
## Mutation attestation
|
|
36
|
+
|
|
37
|
+
The daemon reports its connector contract, exact package/build, platform/architecture, and discovered executor versions on every heartbeat. The server compares those facts with `AMASTER_RUNTIME_RECOMMENDED_VERSION` and `AMASTER_RUNTIME_RECOMMENDED_BUILD_COMMIT`, persists a short-lived content-bound attestation, and correlates Runtime V2 commands to that proof.
|
|
38
|
+
|
|
39
|
+
`AMASTER_RUNTIME_ATTESTATION_MODE` defaults to `shadow`. Set it to `enforce` only after the expected version and build marker are configured and shadow diagnostics are clean; enforce mode refuses to create or lease Runtime V2 mutation commands without a current exact attestation. `AMASTER_RUNTIME_ATTESTATION_TTL_SECONDS` defaults to `300` and accepts `60`–`3600`. Invalid mode or TTL values fail server startup visibly.
|
|
40
|
+
|
|
41
|
+
Threat model: these facts are self-reported by a connector authenticated with its existing connector credential. There is no hardware-backed signature, remote attestation, or independent trust root. The proof detects accidental version/schema/executor drift by an honest connector and correlates commands to the observed facts; it does not prove that a compromised or malicious connector is running the claimed code. Keep the default `shadow` mode observational. Do not treat this mechanism as a production security boundary or enable `enforce` until credential-epoch identity, transactional create/lease revalidation, batch-local rejection, and recovery from honest drift have passed review.
|
|
42
|
+
|
|
35
43
|
The AMaster remote stack derives its runtime image from the Pi base image with
|
|
36
44
|
`docker/Dockerfile.amaster-employee-pi-cli-runtime`. The build installs one exact
|
|
37
45
|
connector package version under `/opt/pi-cli-runtime` and sets
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// AMaster Employee runtime connector daemon bundle.
|
|
3
3
|
|
|
4
4
|
// src/amaster-runtime-daemon.mjs
|
|
5
|
-
import { createHash as
|
|
6
|
-
import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as existsSync9, lstatSync as
|
|
5
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
6
|
+
import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as existsSync9, lstatSync as lstatSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync6, realpathSync as realpathSync3, renameSync as renameSync3, rmSync as rmSync5, statSync as statSync7, symlinkSync as symlinkSync2, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
7
7
|
import { arch as arch3, homedir as homedir3, hostname as hostname2, platform as platform3 } from "node:os";
|
|
8
|
-
import { basename as basename6, delimiter as delimiter2, dirname as dirname6, extname as extname2, isAbsolute as
|
|
8
|
+
import { basename as basename6, delimiter as delimiter2, dirname as dirname6, extname as extname2, isAbsolute as isAbsolute6, join as join10, relative as relative6, resolve as resolve8 } from "node:path";
|
|
9
9
|
import { spawn, spawnSync as spawnSync5 } from "node:child_process";
|
|
10
10
|
|
|
11
11
|
// src/amaster-runtime-daemon/common.mjs
|
|
@@ -2367,6 +2367,8 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2367
2367
|
const governedReads = governedReadSection(context);
|
|
2368
2368
|
const hasTask = Boolean(readString(input.taskMarkdown));
|
|
2369
2369
|
const taskText = readString(input.taskMarkdown) ?? "";
|
|
2370
|
+
const continuationSummary = continuationText(context);
|
|
2371
|
+
const includeTask = mode === "cold" || !continuationSummary && asRecord(input.nativeSession).mode !== "governed_action_approval";
|
|
2370
2372
|
const wakeBodies = commentBodies(context);
|
|
2371
2373
|
const commentsDuplicatedByTask = hasTask && wakeBodies.length > 0 && wakeBodies.every((body) => taskText.includes(body));
|
|
2372
2374
|
const commentsSelected = mode !== "cold" || !commentsDuplicatedByTask;
|
|
@@ -2378,9 +2380,9 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2378
2380
|
const rawSections = [
|
|
2379
2381
|
{ name: "runtime_rules", title: "", priority: 100, sourceRef: `command:${input.commandId}`, content: fixedRules(input, !hasTask) },
|
|
2380
2382
|
{ name: "approval_continuation", title: "Approved Runtime Action Continuation", priority: 99, sourceRef: `run:${input.runId ?? "unknown"}`, content: approvalContinuationText(input), truncationReason: mode === "continuation" ? null : "mode_selection" },
|
|
2381
|
-
{ name: "continuation_summary", title: "Continuation Summary", priority: 97, sourceRef: readString(asRecord(context.paperclipContinuationSummary).key) ?? `issue:${input.issueId ?? "unknown"}`, observedAt: readString(asRecord(context.paperclipContinuationSummary).updatedAt), originalContent:
|
|
2383
|
+
{ name: "continuation_summary", title: "Continuation Summary", priority: 97, sourceRef: readString(asRecord(context.paperclipContinuationSummary).key) ?? `issue:${input.issueId ?? "unknown"}`, observedAt: readString(asRecord(context.paperclipContinuationSummary).updatedAt), originalContent: continuationSummary, content: mode === "cold" ? "" : continuationSummary, truncationReason: mode === "cold" ? "mode_selection" : null },
|
|
2382
2384
|
{ name: "wake_comments", title: "Wake Comment Delta", priority: 95, sourceRef: commentRefs(context).map((id) => `comment:${id}`), originalContent: readString(input.comments) ?? "", content: commentsSelected ? readString(input.comments) ?? "" : "", truncationReason: commentsSelected ? null : "duplicate_task_context" },
|
|
2383
|
-
{ name: "task", title: "Task Context", priority: 90, sourceRef: `issue:${input.issueId ?? "unknown"}`, originalContent: taskText, content:
|
|
2385
|
+
{ name: "task", title: "Task Context", priority: 90, sourceRef: `issue:${input.issueId ?? "unknown"}`, originalContent: taskText, content: includeTask ? taskText : "", truncationReason: includeTask ? null : "mode_selection" },
|
|
2384
2386
|
{ name: "governed_reads", title: "Governed External Reads", priority: 88, sourceRef: governedReads.provenance.map((entry) => entry.sourceRef), observedAt: governedReads.provenance.map((entry) => entry.observedAt), freshness: governedReads.provenance.map((entry) => entry.freshness), scope: governedReads.provenance.map((entry) => entry.scope), content: governedReads.content },
|
|
2385
2387
|
{ name: "agent_instructions", title: "Agent Instructions", priority: 85, sourceRef: "agent_instructions_bundle", content: readString(input.agentInstructions) ?? "" },
|
|
2386
2388
|
{ name: "attachments", title: "Materialized Inputs", priority: 80, sourceRef: "materialized_attachments", content: readString(input.attachmentsText) ?? "" },
|
|
@@ -2433,7 +2435,7 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2433
2435
|
}
|
|
2434
2436
|
|
|
2435
2437
|
// src/amaster-runtime-daemon/config-state.mjs
|
|
2436
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2438
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2437
2439
|
import { homedir as homedir2, hostname } from "node:os";
|
|
2438
2440
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
2439
2441
|
|
|
@@ -2557,16 +2559,53 @@ function runtimeHome(env) {
|
|
|
2557
2559
|
function stateFilePath(env) {
|
|
2558
2560
|
return env.AMASTER_DAEMON_STATE_FILE ? expandHomePath(String(env.AMASTER_DAEMON_STATE_FILE)) : join4(runtimeHome(env), "runtime-connector-state.json");
|
|
2559
2561
|
}
|
|
2562
|
+
function corruptStatePath(path) {
|
|
2563
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
2564
|
+
const base = `${path}.corrupt-${timestamp}Z`;
|
|
2565
|
+
let candidate = base;
|
|
2566
|
+
for (let suffix = 1; existsSync3(candidate); suffix += 1) {
|
|
2567
|
+
candidate = `${base}.${suffix}`;
|
|
2568
|
+
}
|
|
2569
|
+
return candidate;
|
|
2570
|
+
}
|
|
2560
2571
|
function readState(env) {
|
|
2561
2572
|
const path = stateFilePath(env);
|
|
2562
2573
|
if (!existsSync3(path)) return {};
|
|
2563
|
-
|
|
2574
|
+
try {
|
|
2575
|
+
const state = JSON.parse(readFileSync3(path, "utf8"));
|
|
2576
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) {
|
|
2577
|
+
throw new TypeError("runtime connector state must be a JSON object");
|
|
2578
|
+
}
|
|
2579
|
+
return state;
|
|
2580
|
+
} catch {
|
|
2581
|
+
const quarantinePath = corruptStatePath(path);
|
|
2582
|
+
try {
|
|
2583
|
+
renameSync(path, quarantinePath);
|
|
2584
|
+
process.stderr.write(`AMaster runtime state was invalid and quarantined at ${quarantinePath}
|
|
2585
|
+
`);
|
|
2586
|
+
} catch (error) {
|
|
2587
|
+
process.stderr.write(
|
|
2588
|
+
`AMaster runtime state was invalid but could not be quarantined (${error?.code ?? "unknown error"})
|
|
2589
|
+
`
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
return {};
|
|
2593
|
+
}
|
|
2564
2594
|
}
|
|
2565
2595
|
function writeState(env, state) {
|
|
2566
2596
|
const path = stateFilePath(env);
|
|
2567
2597
|
mkdirSync3(dirname3(path), { recursive: true });
|
|
2568
|
-
|
|
2569
|
-
|
|
2598
|
+
const temporaryPath = `${path}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
2599
|
+
try {
|
|
2600
|
+
writeFileSync3(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
2601
|
+
`, {
|
|
2602
|
+
flag: "wx",
|
|
2603
|
+
mode: 384
|
|
2604
|
+
});
|
|
2605
|
+
renameSync(temporaryPath, path);
|
|
2606
|
+
} finally {
|
|
2607
|
+
rmSync3(temporaryPath, { force: true });
|
|
2608
|
+
}
|
|
2570
2609
|
}
|
|
2571
2610
|
function buildConfig(env = process.env, flags = {}) {
|
|
2572
2611
|
const serverUrl = String(flags.serverUrl ?? env.AMASTER_EMPLOYEE_SERVER_URL ?? "http://127.0.0.1:3100").replace(/\/+$/, "");
|
|
@@ -3151,6 +3190,13 @@ function shouldPreserveExecutorJsonlForTranscript(executorKind, event) {
|
|
|
3151
3190
|
var CODEX_TRANSIENT_UPSTREAM_RE = /(?:we(?:'|’)re\s+currently\s+experiencing\s+high\s+demand|temporary\s+errors|rate[-\s]?limit(?:ed)?|too\s+many\s+requests|\b429\b|server\s+overloaded|service\s+unavailable|try\s+again\s+later)/i;
|
|
3152
3191
|
var CODEX_REMOTE_COMPACTION_RE = /remote\s+compact\s+task/i;
|
|
3153
3192
|
var CODEX_USAGE_LIMIT_RE = /you(?:'|’)ve hit your usage limit for .+\.\s+switch to another model now,\s+or try again at\s+([^.!\n]+)(?:[.!]|\n|$)/i;
|
|
3193
|
+
function approvedMcpInvocationSucceeded(results, invocationId) {
|
|
3194
|
+
const approvedInvocationId = readString(invocationId);
|
|
3195
|
+
return Boolean(approvedInvocationId && (Array.isArray(results) ? results : []).some((rawResult) => {
|
|
3196
|
+
const result2 = asRecord(rawResult);
|
|
3197
|
+
return readString(result2.invocationId) === approvedInvocationId && readString(result2.status) === "succeeded" && !["rejected", "blocked"].includes(readString(result2.providerStatus) ?? "");
|
|
3198
|
+
}));
|
|
3199
|
+
}
|
|
3154
3200
|
function parseCodexJsonl(stdout) {
|
|
3155
3201
|
let sessionId = null;
|
|
3156
3202
|
let summary = "";
|
|
@@ -3171,7 +3217,19 @@ function parseCodexJsonl(stdout) {
|
|
|
3171
3217
|
summary = readString(item.text) ?? summary;
|
|
3172
3218
|
}
|
|
3173
3219
|
if (item.type === "mcp_tool_call") {
|
|
3174
|
-
const
|
|
3220
|
+
const result2 = asRecord(item.result);
|
|
3221
|
+
let structuredContent = asRecord(result2.structuredContent);
|
|
3222
|
+
if (Object.keys(structuredContent).length === 0 && Array.isArray(result2.content)) {
|
|
3223
|
+
for (const part of result2.content) {
|
|
3224
|
+
const text = readString(asRecord(part).text);
|
|
3225
|
+
if (!text) continue;
|
|
3226
|
+
try {
|
|
3227
|
+
structuredContent = asRecord(JSON.parse(text));
|
|
3228
|
+
} catch {
|
|
3229
|
+
}
|
|
3230
|
+
if (Object.keys(structuredContent).length > 0) break;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3175
3233
|
const status = readString(structuredContent.status);
|
|
3176
3234
|
if (status) {
|
|
3177
3235
|
const invocationId = readString(structuredContent.invocationId);
|
|
@@ -3536,7 +3594,21 @@ function piMcpToolResults(event) {
|
|
|
3536
3594
|
const status = readString(structuredContent.status);
|
|
3537
3595
|
if (!status) continue;
|
|
3538
3596
|
const invocationId = readString(structuredContent.invocationId);
|
|
3539
|
-
|
|
3597
|
+
const providerContent = asRecord(structuredContent.content);
|
|
3598
|
+
const providerStatus = readString(providerContent.status);
|
|
3599
|
+
const effectResult = asRecord(asRecord(providerContent.result).effectResult);
|
|
3600
|
+
const intentId = readString(effectResult.artifactIntentId);
|
|
3601
|
+
const manifestId = readString(effectResult.manifestId);
|
|
3602
|
+
const sourceRelativePath = readString(effectResult.sourceRelativePath);
|
|
3603
|
+
const sha256 = readString(effectResult.sha256);
|
|
3604
|
+
const byteSize = readNumber(effectResult.byteSize, 0);
|
|
3605
|
+
const artifactIntent = providerContent.status === "pending_reconcile" && intentId && manifestId && sourceRelativePath && /^[a-f0-9]{64}$/.test(sha256 ?? "") && Number.isSafeInteger(byteSize) && byteSize > 0 ? { intentId, manifestId, sourceRelativePath, sha256, byteSize } : null;
|
|
3606
|
+
results.push({
|
|
3607
|
+
...invocationId ? { invocationId } : {},
|
|
3608
|
+
status,
|
|
3609
|
+
...providerStatus ? { providerStatus } : {},
|
|
3610
|
+
...artifactIntent ? { artifactIntent } : {}
|
|
3611
|
+
});
|
|
3540
3612
|
}
|
|
3541
3613
|
return results;
|
|
3542
3614
|
}
|
|
@@ -3705,6 +3777,26 @@ async function postRuntimeConnectorJson(config, path, payload) {
|
|
|
3705
3777
|
}
|
|
3706
3778
|
return body;
|
|
3707
3779
|
}
|
|
3780
|
+
async function postRuntimeConnectorBytes(config, path, body, headers = {}) {
|
|
3781
|
+
if (!Buffer.isBuffer(body)) throw new TypeError("Runtime connector byte upload body must be a Buffer");
|
|
3782
|
+
const res = await fetch(`${config.serverUrl}${path}`, {
|
|
3783
|
+
method: "POST",
|
|
3784
|
+
headers: {
|
|
3785
|
+
...headers,
|
|
3786
|
+
"content-type": "application/octet-stream",
|
|
3787
|
+
...buildRuntimeConnectorAuthHeaders(config)
|
|
3788
|
+
},
|
|
3789
|
+
body
|
|
3790
|
+
});
|
|
3791
|
+
const text = await res.text();
|
|
3792
|
+
const response = text ? JSON.parse(text) : null;
|
|
3793
|
+
if (!res.ok) {
|
|
3794
|
+
const error = new Error(`POST ${path} returned HTTP ${res.status}: ${text}`);
|
|
3795
|
+
error.httpStatus = res.status;
|
|
3796
|
+
throw error;
|
|
3797
|
+
}
|
|
3798
|
+
return response;
|
|
3799
|
+
}
|
|
3708
3800
|
async function bestEffortPostRuntimeConnectorJson(config, path, payload, options = {}) {
|
|
3709
3801
|
try {
|
|
3710
3802
|
return await postRuntimeConnectorJson(config, path, payload);
|
|
@@ -3722,13 +3814,73 @@ async function bestEffortPostRuntimeConnectorJson(config, path, payload, options
|
|
|
3722
3814
|
var postJson = postRuntimeConnectorJson;
|
|
3723
3815
|
var bestEffortPostJson = bestEffortPostRuntimeConnectorJson;
|
|
3724
3816
|
|
|
3725
|
-
// src/amaster-runtime-daemon/
|
|
3817
|
+
// src/amaster-runtime-daemon/runtime-artifact-upload.mjs
|
|
3726
3818
|
import { createHash as createHash3 } from "node:crypto";
|
|
3727
|
-
import {
|
|
3728
|
-
import {
|
|
3819
|
+
import { lstatSync as lstatSync3, readFileSync as readFileSync4, realpathSync } from "node:fs";
|
|
3820
|
+
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3 } from "node:path";
|
|
3821
|
+
var SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
3822
|
+
function requiredString(value, name) {
|
|
3823
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`Runtime Artifact ${name} is required`);
|
|
3824
|
+
return value.trim();
|
|
3825
|
+
}
|
|
3826
|
+
function ownedRelativePath(value) {
|
|
3827
|
+
const normalized = requiredString(value, "sourceRelativePath").replaceAll("\\", "/");
|
|
3828
|
+
if (normalized.startsWith("/") || /^[a-z]:\//i.test(normalized) || normalized.split("/").some((segment) => !segment || segment === "." || segment === "..")) throw new Error(`Runtime Artifact source path is not owned: ${normalized}`);
|
|
3829
|
+
return normalized;
|
|
3830
|
+
}
|
|
3831
|
+
function pathWithin(candidate, root) {
|
|
3832
|
+
const rel = relative3(root, candidate);
|
|
3833
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
3834
|
+
}
|
|
3835
|
+
function prepareRuntimeArtifactUploads(cwd, mcpToolResults) {
|
|
3836
|
+
const root = realpathSync(resolve3(cwd));
|
|
3837
|
+
const uploads = /* @__PURE__ */ new Map();
|
|
3838
|
+
for (const result2 of Array.isArray(mcpToolResults) ? mcpToolResults : []) {
|
|
3839
|
+
const intent = result2 && typeof result2 === "object" && !Array.isArray(result2) ? result2.artifactIntent : null;
|
|
3840
|
+
if (!intent || typeof intent !== "object" || Array.isArray(intent)) continue;
|
|
3841
|
+
const intentId = requiredString(intent.intentId, "intentId");
|
|
3842
|
+
const manifestId = requiredString(intent.manifestId, "manifestId");
|
|
3843
|
+
const sourceRelativePath = ownedRelativePath(intent.sourceRelativePath);
|
|
3844
|
+
const expectedSha256 = requiredString(intent.sha256, "sha256");
|
|
3845
|
+
const expectedByteSize = intent.byteSize;
|
|
3846
|
+
if (!SHA256_PATTERN.test(expectedSha256)) throw new Error(`Runtime Artifact ${intentId} has an invalid SHA-256`);
|
|
3847
|
+
if (!Number.isSafeInteger(expectedByteSize) || expectedByteSize <= 0) {
|
|
3848
|
+
throw new Error(`Runtime Artifact ${intentId} has an invalid byte size`);
|
|
3849
|
+
}
|
|
3850
|
+
const existing = uploads.get(intentId);
|
|
3851
|
+
if (existing) {
|
|
3852
|
+
if (existing.manifestId !== manifestId || existing.sourceRelativePath !== sourceRelativePath || existing.sha256 !== expectedSha256 || existing.byteSize !== expectedByteSize) throw new Error(`Runtime Artifact ${intentId} was emitted with conflicting ownership metadata`);
|
|
3853
|
+
continue;
|
|
3854
|
+
}
|
|
3855
|
+
const sourcePath = resolve3(root, sourceRelativePath);
|
|
3856
|
+
const stat = lstatSync3(sourcePath);
|
|
3857
|
+
if (!stat.isFile() || stat.isSymbolicLink() || !pathWithin(realpathSync(sourcePath), root)) {
|
|
3858
|
+
throw new Error(`Runtime Artifact ${intentId} source is not an owned regular file`);
|
|
3859
|
+
}
|
|
3860
|
+
const body = readFileSync4(sourcePath);
|
|
3861
|
+
const actualSha256 = createHash3("sha256").update(body).digest("hex");
|
|
3862
|
+
if (body.length !== expectedByteSize || actualSha256 !== expectedSha256) {
|
|
3863
|
+
throw new Error(`Runtime Artifact ${intentId} bytes do not match the governed ownership manifest`);
|
|
3864
|
+
}
|
|
3865
|
+
uploads.set(intentId, {
|
|
3866
|
+
intentId,
|
|
3867
|
+
manifestId,
|
|
3868
|
+
sourceRelativePath,
|
|
3869
|
+
sha256: expectedSha256,
|
|
3870
|
+
byteSize: expectedByteSize,
|
|
3871
|
+
body
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
return [...uploads.values()];
|
|
3875
|
+
}
|
|
3876
|
+
|
|
3877
|
+
// src/amaster-runtime-daemon/workspace-guard.mjs
|
|
3878
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3879
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
|
|
3880
|
+
import { basename as basename4, join as join6, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve4 } from "node:path";
|
|
3729
3881
|
|
|
3730
3882
|
// src/amaster-runtime-daemon/workspace-manifest.mjs
|
|
3731
|
-
import { existsSync as existsSync4, readFileSync as
|
|
3883
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3732
3884
|
import { basename as basename3, dirname as dirname4, join as join5 } from "node:path";
|
|
3733
3885
|
var WORKSPACE_MANIFEST_FILENAME = ".amaster-runtime.json";
|
|
3734
3886
|
function nowIso() {
|
|
@@ -3743,7 +3895,7 @@ function workspaceManifestPath(workspaceOrCwd) {
|
|
|
3743
3895
|
function readWorkspaceManifest(manifestPath) {
|
|
3744
3896
|
if (!manifestPath || !existsSync4(manifestPath)) return null;
|
|
3745
3897
|
try {
|
|
3746
|
-
const parsed = JSON.parse(
|
|
3898
|
+
const parsed = JSON.parse(readFileSync5(manifestPath, "utf8"));
|
|
3747
3899
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
3748
3900
|
} catch {
|
|
3749
3901
|
return null;
|
|
@@ -3757,9 +3909,9 @@ function writeWorkspaceManifest(manifestPath, manifest) {
|
|
|
3757
3909
|
try {
|
|
3758
3910
|
writeFileSync4(tempPath, `${JSON.stringify(manifest, null, 2)}
|
|
3759
3911
|
`, { mode: 384 });
|
|
3760
|
-
|
|
3912
|
+
renameSync2(tempPath, manifestPath);
|
|
3761
3913
|
} catch (err) {
|
|
3762
|
-
|
|
3914
|
+
rmSync4(tempPath, { force: true });
|
|
3763
3915
|
throw err;
|
|
3764
3916
|
}
|
|
3765
3917
|
return manifest;
|
|
@@ -3807,23 +3959,23 @@ function updateWorkspaceManifest(workspaceOrManifestPath, patch = {}) {
|
|
|
3807
3959
|
}
|
|
3808
3960
|
|
|
3809
3961
|
// src/amaster-runtime-daemon/workspace-guard.mjs
|
|
3810
|
-
function
|
|
3811
|
-
const rel =
|
|
3812
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
3962
|
+
function pathWithin2(candidate, root) {
|
|
3963
|
+
const rel = relative4(root, candidate);
|
|
3964
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute4(rel);
|
|
3813
3965
|
}
|
|
3814
3966
|
function resolveWorkspaceCwd(config, command) {
|
|
3815
3967
|
const payload = asRecord(command.payload);
|
|
3816
3968
|
const requested = readString(payload.workspacePath);
|
|
3817
3969
|
const fallback = config.workspaceBindings[0] ?? process.cwd();
|
|
3818
|
-
const cwd =
|
|
3970
|
+
const cwd = realpathSync2(resolve4(expandHomePath(requested ?? fallback)));
|
|
3819
3971
|
const allowlist = config.workspaceBindings.flatMap((entry) => {
|
|
3820
3972
|
try {
|
|
3821
|
-
return [
|
|
3973
|
+
return [realpathSync2(resolve4(expandHomePath(entry)))];
|
|
3822
3974
|
} catch {
|
|
3823
3975
|
return [];
|
|
3824
3976
|
}
|
|
3825
3977
|
});
|
|
3826
|
-
const allowed = allowlist.some((entry) =>
|
|
3978
|
+
const allowed = allowlist.some((entry) => pathWithin2(cwd, entry));
|
|
3827
3979
|
if (!allowed) {
|
|
3828
3980
|
throw new Error(`Workspace path is outside AMASTER_WORKSPACE_ALLOWLIST: ${cwd}`);
|
|
3829
3981
|
}
|
|
@@ -3833,7 +3985,7 @@ function resolveWorkspaceCwd(config, command) {
|
|
|
3833
3985
|
return cwd;
|
|
3834
3986
|
}
|
|
3835
3987
|
function shortHash(value, length = 12) {
|
|
3836
|
-
return
|
|
3988
|
+
return createHash4("sha256").update(String(value)).digest("hex").slice(0, length);
|
|
3837
3989
|
}
|
|
3838
3990
|
function safeSegment(value, fallback) {
|
|
3839
3991
|
const raw = String(value ?? "").trim();
|
|
@@ -3857,9 +4009,9 @@ function workspaceLabel(sourceWorkspacePath, payload) {
|
|
|
3857
4009
|
return readString(payload.workspaceName) ?? readString(payload.projectName) ?? readString(context.projectName) ?? basename4(sourceWorkspacePath) ?? "workspace";
|
|
3858
4010
|
}
|
|
3859
4011
|
function workspacesRoot(config) {
|
|
3860
|
-
const root =
|
|
4012
|
+
const root = resolve4(expandHomePath(config.runtimeWorkspacesRoot ?? "~/.amaster-employee/workspaces"));
|
|
3861
4013
|
mkdirSync4(root, { recursive: true });
|
|
3862
|
-
return
|
|
4014
|
+
return realpathSync2(root);
|
|
3863
4015
|
}
|
|
3864
4016
|
function resolveExecutionWorkspace(config, command, opts = {}) {
|
|
3865
4017
|
const sourceWorkspacePath = resolveWorkspaceCwd(config, command);
|
|
@@ -3897,7 +4049,7 @@ function resolveExecutionWorkspace(config, command, opts = {}) {
|
|
|
3897
4049
|
|
|
3898
4050
|
// src/amaster-runtime-daemon/workspace-gc.mjs
|
|
3899
4051
|
import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
3900
|
-
import { join as join7, resolve as
|
|
4052
|
+
import { join as join7, resolve as resolve5 } from "node:path";
|
|
3901
4053
|
function readIsoTime(value) {
|
|
3902
4054
|
if (typeof value !== "string" || !value.trim()) return null;
|
|
3903
4055
|
const time = new Date(value).getTime();
|
|
@@ -3979,7 +4131,7 @@ function summarizeWorkdir(path, manifest, nowMs) {
|
|
|
3979
4131
|
};
|
|
3980
4132
|
}
|
|
3981
4133
|
function planManagedWorkspaceGcDryRun(input) {
|
|
3982
|
-
const root =
|
|
4134
|
+
const root = resolve5(String(input.root ?? ""));
|
|
3983
4135
|
const now = input.now instanceof Date ? input.now : /* @__PURE__ */ new Date();
|
|
3984
4136
|
const nowMs = now.getTime();
|
|
3985
4137
|
const ttlHours = Math.max(1, Number(input.ttlHours ?? 72));
|
|
@@ -4025,7 +4177,7 @@ function planManagedWorkspaceGcDryRun(input) {
|
|
|
4025
4177
|
|
|
4026
4178
|
// src/amaster-runtime-daemon/runtime-status-summary.mjs
|
|
4027
4179
|
import { existsSync as existsSync7, readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
|
|
4028
|
-
import { dirname as dirname5, join as join8, resolve as
|
|
4180
|
+
import { dirname as dirname5, join as join8, resolve as resolve6 } from "node:path";
|
|
4029
4181
|
function runtimeStatusDirectoryEntries(path) {
|
|
4030
4182
|
try {
|
|
4031
4183
|
return readdirSync4(path, { withFileTypes: true });
|
|
@@ -4117,7 +4269,7 @@ function countRuntimeStatusJsonEntries(dir) {
|
|
|
4117
4269
|
}
|
|
4118
4270
|
function summarizeRuntimeLocalState(input) {
|
|
4119
4271
|
const config = input.config ?? {};
|
|
4120
|
-
const root =
|
|
4272
|
+
const root = resolve6(String(config.AMASTER_RUNTIME_WORKSPACES_ROOT ?? config.runtimeWorkspacesRoot ?? input.runtimeWorkspacesRoot ?? ""));
|
|
4121
4273
|
const now = input.now instanceof Date ? input.now : /* @__PURE__ */ new Date();
|
|
4122
4274
|
const workdirs = walkRuntimeStatusManagedWorkdirs(root);
|
|
4123
4275
|
let managedWorkdirBytes = 0;
|
|
@@ -4193,9 +4345,9 @@ function summarizeAmasterRuntimeVersionDrift(input = {}) {
|
|
|
4193
4345
|
|
|
4194
4346
|
// src/amaster-runtime-daemon/workspace-status.mjs
|
|
4195
4347
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
4196
|
-
import { createHash as
|
|
4197
|
-
import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as
|
|
4198
|
-
import { basename as basename5, extname, isAbsolute as
|
|
4348
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
4349
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync6, statSync as statSync6 } from "node:fs";
|
|
4350
|
+
import { basename as basename5, extname, isAbsolute as isAbsolute5, join as join9, relative as relative5, resolve as resolve7 } from "node:path";
|
|
4199
4351
|
var WORKSPACE_RUNTIME_SERVICES_FILENAME = ".amaster-runtime-services.json";
|
|
4200
4352
|
var ARTIFACT_EXTENSIONS = /* @__PURE__ */ new Map([
|
|
4201
4353
|
[".md", "markdown"],
|
|
@@ -4226,9 +4378,12 @@ var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
4226
4378
|
"build",
|
|
4227
4379
|
"coverage",
|
|
4228
4380
|
".turbo",
|
|
4229
|
-
".cache"
|
|
4381
|
+
".cache",
|
|
4382
|
+
".venv",
|
|
4383
|
+
"venv"
|
|
4230
4384
|
]);
|
|
4231
4385
|
var SECRET_PATH_PATTERN = /(^|\/)(\.env($|[._-])|.*\.(pem|key|p12|pfx)$|.*(secret|token|credential|password|authorization|cookie).*)/i;
|
|
4386
|
+
var RUNTIME_INSTRUCTION_FILENAMES = /* @__PURE__ */ new Set(["AGENTS.md", "SOUL.md"]);
|
|
4232
4387
|
var MAX_CANDIDATES = 200;
|
|
4233
4388
|
var MAX_SCAN_ENTRIES = 5e3;
|
|
4234
4389
|
var MAX_HASH_BYTES = 50 * 1024 * 1024;
|
|
@@ -4237,16 +4392,17 @@ var SAFE_RUNTIME_SERVICE_STATUSES = /* @__PURE__ */ new Set(["starting", "runnin
|
|
|
4237
4392
|
var SAFE_RUNTIME_SERVICE_HEALTH_STATUSES = /* @__PURE__ */ new Set(["unknown", "healthy", "unhealthy"]);
|
|
4238
4393
|
var SAFE_RUNTIME_SERVICE_LIFECYCLES = /* @__PURE__ */ new Set(["shared", "ephemeral"]);
|
|
4239
4394
|
function statusPathWithin(candidate, root) {
|
|
4240
|
-
const rel =
|
|
4241
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
4395
|
+
const rel = relative5(root, candidate);
|
|
4396
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute5(rel);
|
|
4242
4397
|
}
|
|
4243
4398
|
function normalizeRelativePath(root, filePath) {
|
|
4244
|
-
return
|
|
4399
|
+
return relative5(root, filePath).split(/[\\/]+/).join("/");
|
|
4245
4400
|
}
|
|
4246
4401
|
function isSafeRelativePath(value) {
|
|
4247
4402
|
const text = String(value ?? "").trim().split(/[\\/]+/).join("/");
|
|
4248
4403
|
if (!text || text.startsWith("../") || text === ".." || text.startsWith("/")) return false;
|
|
4249
|
-
|
|
4404
|
+
const segments = text.split("/").filter(Boolean);
|
|
4405
|
+
return !SECRET_PATH_PATTERN.test(text) && !RUNTIME_INSTRUCTION_FILENAMES.has(basename5(text)) && !segments.some((segment) => segment === ".venv" || segment === "venv");
|
|
4250
4406
|
}
|
|
4251
4407
|
function gitStatusPath(line) {
|
|
4252
4408
|
if (line.startsWith("?? ")) return line.slice(3);
|
|
@@ -4260,7 +4416,7 @@ function sanitizeTrackedChange(line) {
|
|
|
4260
4416
|
return isSafeRelativePath(path) ? line : null;
|
|
4261
4417
|
}
|
|
4262
4418
|
function sha256File(filePath) {
|
|
4263
|
-
return
|
|
4419
|
+
return createHash5("sha256").update(readFileSync6(filePath)).digest("hex");
|
|
4264
4420
|
}
|
|
4265
4421
|
function artifactHashCacheKey(relativePath, stat) {
|
|
4266
4422
|
return `${relativePath}\0${stat.size}\0${stat.mtimeMs}`;
|
|
@@ -4288,7 +4444,7 @@ function artifactSha256(filePath, relativePath, stat, opts = {}) {
|
|
|
4288
4444
|
return hash;
|
|
4289
4445
|
}
|
|
4290
4446
|
function scanArtifactCandidates(cwd, opts = {}) {
|
|
4291
|
-
const root =
|
|
4447
|
+
const root = resolve7(cwd);
|
|
4292
4448
|
const maxCandidates = opts.maxCandidates ?? MAX_CANDIDATES;
|
|
4293
4449
|
const maxEntries = opts.maxEntries ?? MAX_SCAN_ENTRIES;
|
|
4294
4450
|
const candidates = [];
|
|
@@ -4328,7 +4484,7 @@ function scanArtifactCandidates(cwd, opts = {}) {
|
|
|
4328
4484
|
} catch {
|
|
4329
4485
|
continue;
|
|
4330
4486
|
}
|
|
4331
|
-
if (!statusPathWithin(
|
|
4487
|
+
if (!statusPathWithin(resolve7(fullPath), root) || stat.size > MAX_HASH_BYTES) continue;
|
|
4332
4488
|
candidates.push({
|
|
4333
4489
|
relativePath,
|
|
4334
4490
|
name: basename5(fullPath),
|
|
@@ -4388,10 +4544,10 @@ function sanitizeRuntimeService(entry) {
|
|
|
4388
4544
|
};
|
|
4389
4545
|
}
|
|
4390
4546
|
function readRuntimeServicesSnapshot(cwd) {
|
|
4391
|
-
const snapshotPath = join9(
|
|
4547
|
+
const snapshotPath = join9(resolve7(cwd), WORKSPACE_RUNTIME_SERVICES_FILENAME);
|
|
4392
4548
|
if (!existsSync8(snapshotPath)) return [];
|
|
4393
4549
|
try {
|
|
4394
|
-
const parsed = JSON.parse(
|
|
4550
|
+
const parsed = JSON.parse(readFileSync6(snapshotPath, "utf8"));
|
|
4395
4551
|
const rawServices = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.services) ? parsed.services : [];
|
|
4396
4552
|
return rawServices.map((entry) => sanitizeRuntimeService(entry)).filter((entry) => entry !== null).slice(0, 50);
|
|
4397
4553
|
} catch {
|
|
@@ -4462,7 +4618,8 @@ function readWorkspaceStatus(cwd, opts = {}) {
|
|
|
4462
4618
|
}
|
|
4463
4619
|
|
|
4464
4620
|
// src/amaster-runtime-daemon.mjs
|
|
4465
|
-
var CONNECTOR_VERSION = "0.1.0-beta.
|
|
4621
|
+
var CONNECTOR_VERSION = "0.1.0-beta.22";
|
|
4622
|
+
var CONNECTOR_CONTRACT_VERSION = "2026-06-04.v1";
|
|
4466
4623
|
var MAX_CHECKPOINT_BYTES = 20 * 1024 * 1024;
|
|
4467
4624
|
var CHECKPOINT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4468
4625
|
var PROMPT_AGENT_INSTRUCTION_FILE_ORDER = ["AGENTS.md", "SOUL.md"];
|
|
@@ -4575,11 +4732,11 @@ function piExtraArgsDiagnostics(value) {
|
|
|
4575
4732
|
}
|
|
4576
4733
|
function safeExpandPath(value) {
|
|
4577
4734
|
const text = readString(value);
|
|
4578
|
-
return text ?
|
|
4735
|
+
return text ? resolve8(expandHomePath(text)) : null;
|
|
4579
4736
|
}
|
|
4580
4737
|
function safeJsonObjectFromFile(filePath) {
|
|
4581
4738
|
try {
|
|
4582
|
-
const parsed = JSON.parse(
|
|
4739
|
+
const parsed = JSON.parse(readFileSync7(filePath, "utf8"));
|
|
4583
4740
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
4584
4741
|
} catch {
|
|
4585
4742
|
return null;
|
|
@@ -4814,6 +4971,11 @@ function buildRegisterPayload(config) {
|
|
|
4814
4971
|
networkDomains: config.networkDomains,
|
|
4815
4972
|
executors: config.executors,
|
|
4816
4973
|
capabilities: config.capabilities,
|
|
4974
|
+
contractVersion: CONNECTOR_CONTRACT_VERSION,
|
|
4975
|
+
connectorVersion: CONNECTOR_VERSION,
|
|
4976
|
+
...buildCommit ? { buildCommit } : {},
|
|
4977
|
+
platform: process.platform,
|
|
4978
|
+
arch: process.arch,
|
|
4817
4979
|
metadata: {
|
|
4818
4980
|
daemon: "amaster-runtime-daemon.mjs",
|
|
4819
4981
|
connectorVersion: CONNECTOR_VERSION,
|
|
@@ -4856,6 +5018,9 @@ function buildHeartbeatPayload(config, options = {}) {
|
|
|
4856
5018
|
capabilities: config.capabilities,
|
|
4857
5019
|
connectorVersion: CONNECTOR_VERSION,
|
|
4858
5020
|
...buildCommit ? { buildCommit } : {},
|
|
5021
|
+
contractVersion: CONNECTOR_CONTRACT_VERSION,
|
|
5022
|
+
platform: process.platform,
|
|
5023
|
+
arch: process.arch,
|
|
4859
5024
|
runtimeStatus: {
|
|
4860
5025
|
daemon: "running",
|
|
4861
5026
|
pid: process.pid,
|
|
@@ -5395,7 +5560,7 @@ function commandExecutorEnv(command) {
|
|
|
5395
5560
|
}
|
|
5396
5561
|
function readJsonFile(filePath) {
|
|
5397
5562
|
try {
|
|
5398
|
-
const parsed = JSON.parse(
|
|
5563
|
+
const parsed = JSON.parse(readFileSync7(filePath, "utf8"));
|
|
5399
5564
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
5400
5565
|
} catch {
|
|
5401
5566
|
return {};
|
|
@@ -5406,7 +5571,7 @@ function writeJsonFileAtomic(filePath, value) {
|
|
|
5406
5571
|
const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
5407
5572
|
writeFileSync5(tmpPath, `${JSON.stringify(value, null, 2)}
|
|
5408
5573
|
`, { mode: 384 });
|
|
5409
|
-
|
|
5574
|
+
renameSync3(tmpPath, filePath);
|
|
5410
5575
|
}
|
|
5411
5576
|
function isPlainRecord(value) {
|
|
5412
5577
|
return value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -5565,12 +5730,12 @@ function safeAgentInstructionMaterializationTarget(workspace, filePath) {
|
|
|
5565
5730
|
const raw = readString(filePath);
|
|
5566
5731
|
if (!raw || raw.includes("\0")) return null;
|
|
5567
5732
|
const normalized = raw.replace(/\\/g, "/");
|
|
5568
|
-
if (
|
|
5733
|
+
if (isAbsolute6(normalized)) return null;
|
|
5569
5734
|
const segments = normalized.split("/").filter(Boolean);
|
|
5570
5735
|
if (segments.length === 0 || segments.some((segment) => segment === "." || segment === "..")) return null;
|
|
5571
5736
|
const relativePath = segments.join("/");
|
|
5572
|
-
const targetPath =
|
|
5573
|
-
if (!
|
|
5737
|
+
const targetPath = resolve8(workspace.cwd, relativePath);
|
|
5738
|
+
if (!pathWithin2(targetPath, workspace.cwd)) return null;
|
|
5574
5739
|
return { relativePath, targetPath };
|
|
5575
5740
|
}
|
|
5576
5741
|
async function materializeAgentInstructionsBundle(config, command, workspace) {
|
|
@@ -5653,14 +5818,14 @@ function safeCompanyPiHomeSegment(companyId) {
|
|
|
5653
5818
|
if (!raw) return null;
|
|
5654
5819
|
if (/^[A-Za-z0-9][A-Za-z0-9._-]{0,119}$/.test(raw)) return raw;
|
|
5655
5820
|
const normalized = raw.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "").slice(0, 80);
|
|
5656
|
-
const hash =
|
|
5821
|
+
const hash = createHash6("sha256").update(raw).digest("hex").slice(0, 12);
|
|
5657
5822
|
return normalized ? `${normalized}-${hash}` : `company-${hash}`;
|
|
5658
5823
|
}
|
|
5659
5824
|
function companyPiHomeRoot(baseEnv) {
|
|
5660
5825
|
const explicitRoot = readString(baseEnv.AMASTER_COMPANY_PI_HOME_ROOT);
|
|
5661
|
-
if (explicitRoot) return
|
|
5826
|
+
if (explicitRoot) return resolve8(expandHomePath(explicitRoot));
|
|
5662
5827
|
const configuredPiHome = readString(baseEnv.PI_AGENT_HOME) ?? readString(baseEnv.PI_CODING_AGENT_DIR);
|
|
5663
|
-
if (configuredPiHome) return join10(dirname6(
|
|
5828
|
+
if (configuredPiHome) return join10(dirname6(resolve8(expandHomePath(configuredPiHome))), "companies");
|
|
5664
5829
|
return join10(homedir3(), ".amaster-employee", "companies");
|
|
5665
5830
|
}
|
|
5666
5831
|
function companyPiAgentHome(baseEnv, companyId) {
|
|
@@ -5725,9 +5890,9 @@ function resolveNativeSessionRequest(command, workspace) {
|
|
|
5725
5890
|
let cwdMatched = false;
|
|
5726
5891
|
if (requestedCwd && sourceWorkspacePath) {
|
|
5727
5892
|
try {
|
|
5728
|
-
cwdMatched =
|
|
5893
|
+
cwdMatched = realpathSync3(requestedCwd) === realpathSync3(sourceWorkspacePath);
|
|
5729
5894
|
} catch {
|
|
5730
|
-
cwdMatched =
|
|
5895
|
+
cwdMatched = resolve8(requestedCwd) === resolve8(sourceWorkspacePath);
|
|
5731
5896
|
}
|
|
5732
5897
|
}
|
|
5733
5898
|
const used = Boolean(enabled && requested && sessionId && requestedCwd && cwdMatched);
|
|
@@ -6105,6 +6270,11 @@ function createLiveOutputLogger(config, command, executorKind, protectedValues =
|
|
|
6105
6270
|
}
|
|
6106
6271
|
};
|
|
6107
6272
|
}
|
|
6273
|
+
function isRuntimeMetadataArtifactPath(value) {
|
|
6274
|
+
const normalized = String(value ?? "").replaceAll("\\", "/").replace(/^\.\//, "");
|
|
6275
|
+
const name = basename6(normalized);
|
|
6276
|
+
return name === WORKSPACE_MANIFEST_FILENAME || name === WORKSPACE_RUNTIME_SERVICES_FILENAME;
|
|
6277
|
+
}
|
|
6108
6278
|
function sampleProcessGroupRssBytes(processGroupId) {
|
|
6109
6279
|
if (process.platform === "win32" || processGroupId === null) return null;
|
|
6110
6280
|
const now = Date.now();
|
|
@@ -6130,15 +6300,15 @@ function sampleProcessGroupRssBytes(processGroupId) {
|
|
|
6130
6300
|
}
|
|
6131
6301
|
function realOrResolvedPath(value) {
|
|
6132
6302
|
try {
|
|
6133
|
-
return
|
|
6303
|
+
return realpathSync3(value);
|
|
6134
6304
|
} catch {
|
|
6135
|
-
return
|
|
6305
|
+
return resolve8(value);
|
|
6136
6306
|
}
|
|
6137
6307
|
}
|
|
6138
6308
|
function processCwdForPid(pid) {
|
|
6139
6309
|
if (process.platform === "linux") {
|
|
6140
6310
|
try {
|
|
6141
|
-
return
|
|
6311
|
+
return realpathSync3(`/proc/${pid}/cwd`);
|
|
6142
6312
|
} catch {
|
|
6143
6313
|
return null;
|
|
6144
6314
|
}
|
|
@@ -6229,7 +6399,7 @@ function listWorkspaceResidentProcesses(cwd, processGroupId, options = {}) {
|
|
|
6229
6399
|
if (processGroupId !== null && pgid === processGroupId) continue;
|
|
6230
6400
|
const commandMatches = commandLine.includes(cwd) || commandLine.includes(normalizedCwd);
|
|
6231
6401
|
const processCwd = commandMatches ? null : options.processCwdsByPid instanceof Map ? options.processCwdsByPid.get(pid) ?? null : processCwdForPid(pid);
|
|
6232
|
-
const cwdMatches = processCwd ?
|
|
6402
|
+
const cwdMatches = processCwd ? pathWithin2(processCwd, normalizedCwd) : false;
|
|
6233
6403
|
if (!commandMatches && !cwdMatches) continue;
|
|
6234
6404
|
rows.push({ pid, pgid, command: commandLine.slice(0, 300) });
|
|
6235
6405
|
}
|
|
@@ -6308,8 +6478,8 @@ function manifestMatchesActiveRuntimeRef(manifest, refs, workdir = null) {
|
|
|
6308
6478
|
}
|
|
6309
6479
|
function buildOrphanReaperSample(root, workdir, manifest, residents) {
|
|
6310
6480
|
const relativeWorkdir = (() => {
|
|
6311
|
-
const value =
|
|
6312
|
-
return value && !value.startsWith("..") && !
|
|
6481
|
+
const value = relative6(root, workdir);
|
|
6482
|
+
return value && !value.startsWith("..") && !isAbsolute6(value) ? value : basename6(workdir);
|
|
6313
6483
|
})();
|
|
6314
6484
|
return {
|
|
6315
6485
|
workdir: relativeWorkdir,
|
|
@@ -6613,6 +6783,53 @@ async function ingestWorkspaceStatus(config, command, cwd) {
|
|
|
6613
6783
|
runId: commandRunId(command) ?? void 0,
|
|
6614
6784
|
...status
|
|
6615
6785
|
});
|
|
6786
|
+
return status;
|
|
6787
|
+
}
|
|
6788
|
+
async function ingestRuntimeArtifacts(config, command, cwd, mcpToolResults) {
|
|
6789
|
+
const connectorId = requireConnectorId(config);
|
|
6790
|
+
const runId = commandRunId(command);
|
|
6791
|
+
if (!runId) throw new Error("Runtime Artifact ingest requires a correlated runId");
|
|
6792
|
+
const uploads = prepareRuntimeArtifactUploads(cwd, mcpToolResults);
|
|
6793
|
+
const receipts = [];
|
|
6794
|
+
for (const upload of uploads) {
|
|
6795
|
+
const path = `/api/amaster/runtime-connectors/${connectorId}/artifact-intents/${encodeURIComponent(upload.intentId)}/ingest`;
|
|
6796
|
+
try {
|
|
6797
|
+
const receipt = asRecord(await postRuntimeConnectorBytes(config, path, upload.body, {
|
|
6798
|
+
"x-amaster-command-id": command.commandId,
|
|
6799
|
+
"x-amaster-run-id": runId,
|
|
6800
|
+
"x-amaster-artifact-manifest-id": upload.manifestId,
|
|
6801
|
+
"x-amaster-artifact-source-path": upload.sourceRelativePath
|
|
6802
|
+
}));
|
|
6803
|
+
if (readString(receipt.intentId) !== upload.intentId || readString(receipt.status) !== "finalized") {
|
|
6804
|
+
throw new Error(`Runtime Artifact ${upload.intentId} was not finalized: ${JSON.stringify(receipt)}`);
|
|
6805
|
+
}
|
|
6806
|
+
receipts.push(receipt);
|
|
6807
|
+
await ingestLog(config, command, "system", "info", `Finalized Runtime Artifact ${upload.sourceRelativePath}`, {
|
|
6808
|
+
presentationKind: "runtime_artifact_ingest",
|
|
6809
|
+
intentId: upload.intentId,
|
|
6810
|
+
manifestId: upload.manifestId,
|
|
6811
|
+
sourceRelativePath: upload.sourceRelativePath,
|
|
6812
|
+
sha256: upload.sha256,
|
|
6813
|
+
byteSize: upload.byteSize,
|
|
6814
|
+
attachmentId: readString(receipt.attachmentId),
|
|
6815
|
+
workProductId: readString(receipt.workProductId),
|
|
6816
|
+
status: readString(receipt.status)
|
|
6817
|
+
});
|
|
6818
|
+
} catch (err) {
|
|
6819
|
+
const message = `Runtime Artifact ${upload.intentId} ingest failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
6820
|
+
await ingestLog(config, command, "system", "error", message, {
|
|
6821
|
+
presentationKind: "runtime_artifact_ingest",
|
|
6822
|
+
intentId: upload.intentId,
|
|
6823
|
+
manifestId: upload.manifestId,
|
|
6824
|
+
sourceRelativePath: upload.sourceRelativePath,
|
|
6825
|
+
sha256: upload.sha256,
|
|
6826
|
+
byteSize: upload.byteSize,
|
|
6827
|
+
status: "failed"
|
|
6828
|
+
});
|
|
6829
|
+
throw new Error(message);
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
return receipts;
|
|
6616
6833
|
}
|
|
6617
6834
|
function resultOutboxActiveRunSnapshot(command) {
|
|
6618
6835
|
const commandId = readString(command.commandId) ?? readString(command.id);
|
|
@@ -6664,7 +6881,7 @@ async function completeCommand(config, command, status, result2, error) {
|
|
|
6664
6881
|
}
|
|
6665
6882
|
function resultOutboxDir(config) {
|
|
6666
6883
|
const explicit = readString(process.env.AMASTER_RESULT_OUTBOX_DIR);
|
|
6667
|
-
if (explicit) return
|
|
6884
|
+
if (explicit) return resolve8(expandHomePath(explicit));
|
|
6668
6885
|
return join10(dirname6(stateFilePath(process.env)), "result-outbox");
|
|
6669
6886
|
}
|
|
6670
6887
|
function resultOutboxInvalidDir(config) {
|
|
@@ -6689,7 +6906,7 @@ function moveResultOutboxEntryToInvalid(config, file, fullPath, reason, detail,
|
|
|
6689
6906
|
const invalidPath = join10(invalidDir, file);
|
|
6690
6907
|
if (original === void 0) {
|
|
6691
6908
|
try {
|
|
6692
|
-
|
|
6909
|
+
renameSync3(fullPath, invalidPath);
|
|
6693
6910
|
} catch {
|
|
6694
6911
|
copyFileSync2(fullPath, invalidPath);
|
|
6695
6912
|
unlinkSync(fullPath);
|
|
@@ -6709,7 +6926,7 @@ function moveResultOutboxEntryToInvalid(config, file, fullPath, reason, detail,
|
|
|
6709
6926
|
function readValidResultOutboxEntryOrQuarantine(config, file, fullPath) {
|
|
6710
6927
|
let entry;
|
|
6711
6928
|
try {
|
|
6712
|
-
entry = JSON.parse(
|
|
6929
|
+
entry = JSON.parse(readFileSync7(fullPath, "utf8"));
|
|
6713
6930
|
} catch (err) {
|
|
6714
6931
|
const message = err instanceof Error ? err.message : String(err);
|
|
6715
6932
|
moveResultOutboxEntryToInvalid(config, file, fullPath, "malformed_result_outbox_json", message);
|
|
@@ -6987,7 +7204,7 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
6987
7204
|
const body = await runtimeApiBuffer(runtimeAuth, contentPath);
|
|
6988
7205
|
writeFileSync5(targetPath, body);
|
|
6989
7206
|
const attachmentId = readString(attachment.id);
|
|
6990
|
-
const actualSha256 =
|
|
7207
|
+
const actualSha256 = createHash6("sha256").update(body).digest("hex");
|
|
6991
7208
|
const lineageCandidates = lineageCandidatesByAttachmentId.get(attachmentId) ?? [];
|
|
6992
7209
|
const lineage = selectAttachmentLineage(lineageCandidates, actualSha256);
|
|
6993
7210
|
if (lineageCandidates.length > 0 && !lineage) {
|
|
@@ -7010,7 +7227,7 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
7010
7227
|
id: attachmentId,
|
|
7011
7228
|
name: readString(attachment.originalFilename) ?? filename,
|
|
7012
7229
|
path: targetPath,
|
|
7013
|
-
relativePath:
|
|
7230
|
+
relativePath: relative6(workspace.cwd, targetPath),
|
|
7014
7231
|
contentType: readString(attachment.contentType),
|
|
7015
7232
|
byteSize: body.byteLength,
|
|
7016
7233
|
contentPath,
|
|
@@ -7060,7 +7277,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
7060
7277
|
throw new Error("artifact_input_manifest_invalid: expected version 1 entries array");
|
|
7061
7278
|
}
|
|
7062
7279
|
const targetRoot = join10(workspace.cwd, "input-artifacts");
|
|
7063
|
-
|
|
7280
|
+
rmSync5(targetRoot, { recursive: true, force: true });
|
|
7064
7281
|
mkdirSync5(targetRoot, { recursive: true });
|
|
7065
7282
|
const usedPaths = /* @__PURE__ */ new Set();
|
|
7066
7283
|
const materialized = [];
|
|
@@ -7079,7 +7296,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
7079
7296
|
throw new Error(`artifact_input_manifest_invalid: entry ${index} contentPath does not match attachmentId`);
|
|
7080
7297
|
}
|
|
7081
7298
|
const body = await runtimeApiBuffer(runtimeAuth, contentPath);
|
|
7082
|
-
const actualSha256 =
|
|
7299
|
+
const actualSha256 = createHash6("sha256").update(body).digest("hex");
|
|
7083
7300
|
if (body.byteLength !== byteSize || actualSha256 !== sha256) {
|
|
7084
7301
|
throw new Error(
|
|
7085
7302
|
`artifact_input_integrity_mismatch: workProductId=${workProductId} expectedBytes=${byteSize} actualBytes=${body.byteLength} expectedSha256=${sha256} actualSha256=${actualSha256}`
|
|
@@ -7136,53 +7353,56 @@ function issueCheckpointDir(workspace) {
|
|
|
7136
7353
|
async function clearIssueCheckpoint(config, command, workspace, reason) {
|
|
7137
7354
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
7138
7355
|
if (!existsSync9(checkpointDir)) return false;
|
|
7139
|
-
|
|
7356
|
+
rmSync5(checkpointDir, { recursive: true, force: true });
|
|
7140
7357
|
await ingestLog(config, command, "system", "info", "Cleared issue continuation checkpoint", { reason });
|
|
7141
7358
|
return true;
|
|
7142
7359
|
}
|
|
7143
7360
|
function safeCheckpointRelativePath(rawPath) {
|
|
7144
7361
|
const raw = String(rawPath ?? "").trim();
|
|
7145
|
-
if (
|
|
7362
|
+
if (isAbsolute6(raw) || /^[A-Za-z]:[\\/]/.test(raw)) return null;
|
|
7146
7363
|
const normalized = raw.split(/[\\/]+/).filter(Boolean).join("/");
|
|
7147
7364
|
if (!normalized || normalized.startsWith("../") || normalized.split("/").some((segment) => segment.startsWith("."))) return null;
|
|
7148
7365
|
if (normalized.startsWith("input-attachments/") || isRuntimeMetadataArtifactPath(normalized)) return null;
|
|
7149
7366
|
return normalized;
|
|
7150
7367
|
}
|
|
7368
|
+
function hashFileSha256(filePath) {
|
|
7369
|
+
return createHash6("sha256").update(readFileSync7(filePath)).digest("hex");
|
|
7370
|
+
}
|
|
7151
7371
|
async function materializeIssueCheckpoint(config, command, workspace) {
|
|
7152
7372
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
7153
7373
|
const manifestPath = join10(checkpointDir, "manifest.json");
|
|
7154
7374
|
if (!existsSync9(manifestPath)) return [];
|
|
7155
7375
|
let manifest;
|
|
7156
7376
|
try {
|
|
7157
|
-
manifest = asRecord(JSON.parse(
|
|
7377
|
+
manifest = asRecord(JSON.parse(readFileSync7(manifestPath, "utf8")));
|
|
7158
7378
|
} catch (err) {
|
|
7159
|
-
|
|
7379
|
+
rmSync5(checkpointDir, { recursive: true, force: true });
|
|
7160
7380
|
throw new Error(`invalid issue checkpoint manifest: ${err instanceof Error ? err.message : String(err)}`);
|
|
7161
7381
|
}
|
|
7162
7382
|
const expiresAt = Date.parse(readString(manifest.expiresAt) ?? "");
|
|
7163
7383
|
if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || readString(manifest.issueId) !== commandIssueId(command)) {
|
|
7164
|
-
|
|
7384
|
+
rmSync5(checkpointDir, { recursive: true, force: true });
|
|
7165
7385
|
return [];
|
|
7166
7386
|
}
|
|
7167
7387
|
try {
|
|
7168
7388
|
const files = Array.isArray(manifest.files) ? manifest.files : [];
|
|
7169
7389
|
if (files.length > 20) throw new Error("issue checkpoint manifest exceeds the limit of 20 files");
|
|
7170
|
-
const filesRoot =
|
|
7171
|
-
const workspaceRoot =
|
|
7390
|
+
const filesRoot = realpathSync3(join10(checkpointDir, "files"));
|
|
7391
|
+
const workspaceRoot = realpathSync3(workspace.cwd);
|
|
7172
7392
|
const validated = [];
|
|
7173
7393
|
let totalBytes = 0;
|
|
7174
7394
|
for (const rawFile of files) {
|
|
7175
7395
|
const file = asRecord(rawFile);
|
|
7176
7396
|
const relativePath = safeCheckpointRelativePath(readString(file.path));
|
|
7177
7397
|
if (!relativePath) throw new Error("issue checkpoint manifest contains an invalid file path");
|
|
7178
|
-
const sourceCandidate =
|
|
7179
|
-
const target =
|
|
7180
|
-
if (!
|
|
7398
|
+
const sourceCandidate = resolve8(filesRoot, relativePath);
|
|
7399
|
+
const target = resolve8(workspaceRoot, relativePath);
|
|
7400
|
+
if (!pathWithin2(sourceCandidate, filesRoot) || !pathWithin2(target, workspaceRoot)) {
|
|
7181
7401
|
throw new Error(`issue checkpoint path escapes its workspace: ${relativePath}`);
|
|
7182
7402
|
}
|
|
7183
7403
|
if (!existsSync9(sourceCandidate)) throw new Error(`issue checkpoint file is missing: ${relativePath}`);
|
|
7184
|
-
const source =
|
|
7185
|
-
if (!
|
|
7404
|
+
const source = realpathSync3(sourceCandidate);
|
|
7405
|
+
if (!pathWithin2(source, filesRoot) || !statSync7(source).isFile()) {
|
|
7186
7406
|
throw new Error(`issue checkpoint source escapes its storage root: ${relativePath}`);
|
|
7187
7407
|
}
|
|
7188
7408
|
const expectedSha256 = readString(file.sha256);
|
|
@@ -7200,14 +7420,14 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
7200
7420
|
const materialized = [];
|
|
7201
7421
|
for (const { relativePath, source, target } of validated) {
|
|
7202
7422
|
try {
|
|
7203
|
-
|
|
7423
|
+
lstatSync4(target);
|
|
7204
7424
|
continue;
|
|
7205
7425
|
} catch (err) {
|
|
7206
7426
|
if (err?.code !== "ENOENT") throw err;
|
|
7207
7427
|
}
|
|
7208
7428
|
mkdirSync5(dirname6(target), { recursive: true });
|
|
7209
|
-
const targetParent =
|
|
7210
|
-
if (!
|
|
7429
|
+
const targetParent = realpathSync3(dirname6(target));
|
|
7430
|
+
if (!pathWithin2(targetParent, workspaceRoot)) {
|
|
7211
7431
|
throw new Error(`issue checkpoint target escapes its workspace: ${relativePath}`);
|
|
7212
7432
|
}
|
|
7213
7433
|
copyFileSync2(source, target);
|
|
@@ -7232,6 +7452,49 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
7232
7452
|
throw restoreErr;
|
|
7233
7453
|
}
|
|
7234
7454
|
}
|
|
7455
|
+
async function saveIssueCheckpoint(config, command, workspace, candidates) {
|
|
7456
|
+
const checkpointDir = issueCheckpointDir(workspace);
|
|
7457
|
+
const filesDir = join10(checkpointDir, "files");
|
|
7458
|
+
rmSync5(checkpointDir, { recursive: true, force: true });
|
|
7459
|
+
mkdirSync5(filesDir, { recursive: true });
|
|
7460
|
+
const files = [];
|
|
7461
|
+
let totalBytes = 0;
|
|
7462
|
+
const workspaceRoot = realpathSync3(workspace.cwd);
|
|
7463
|
+
for (const candidate of candidates.slice(0, 20)) {
|
|
7464
|
+
const relativePath = safeCheckpointRelativePath(candidate.rawPath);
|
|
7465
|
+
const source = readString(candidate.filePath);
|
|
7466
|
+
if (!relativePath || !source || !existsSync9(source) || !statSync7(source).isFile()) continue;
|
|
7467
|
+
const ownedSource = realpathSync3(source);
|
|
7468
|
+
if (!pathWithin2(ownedSource, workspaceRoot)) {
|
|
7469
|
+
throw new Error(`issue checkpoint source escapes its execution workspace: ${relativePath}`);
|
|
7470
|
+
}
|
|
7471
|
+
const byteSize = statSync7(ownedSource).size;
|
|
7472
|
+
if (byteSize <= 0 || totalBytes + byteSize > MAX_CHECKPOINT_BYTES) continue;
|
|
7473
|
+
const target = resolve8(filesDir, relativePath);
|
|
7474
|
+
if (!pathWithin2(target, filesDir)) continue;
|
|
7475
|
+
mkdirSync5(dirname6(target), { recursive: true });
|
|
7476
|
+
copyFileSync2(ownedSource, target);
|
|
7477
|
+
totalBytes += byteSize;
|
|
7478
|
+
files.push({ path: relativePath, byteSize, sha256: hashFileSha256(ownedSource) });
|
|
7479
|
+
}
|
|
7480
|
+
if (files.length === 0) {
|
|
7481
|
+
rmSync5(checkpointDir, { recursive: true, force: true });
|
|
7482
|
+
return null;
|
|
7483
|
+
}
|
|
7484
|
+
const manifest = {
|
|
7485
|
+
version: 1,
|
|
7486
|
+
issueId: commandIssueId(command),
|
|
7487
|
+
sourceRunId: commandRunId(command),
|
|
7488
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7489
|
+
expiresAt: new Date(Date.now() + CHECKPOINT_TTL_MS).toISOString(),
|
|
7490
|
+
totalBytes,
|
|
7491
|
+
files
|
|
7492
|
+
};
|
|
7493
|
+
writeFileSync5(join10(checkpointDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
7494
|
+
`);
|
|
7495
|
+
await ingestLog(config, command, "system", "warn", `Saved ${files.length} required deliverable(s) for continuation recovery`, manifest);
|
|
7496
|
+
return manifest;
|
|
7497
|
+
}
|
|
7235
7498
|
function piOutputUsageMetadataMissing(parsed) {
|
|
7236
7499
|
const usage = parsed?.usage ?? {};
|
|
7237
7500
|
const totalTokens = Number(usage.inputTokens ?? 0) + Number(usage.cachedInputTokens ?? 0) + Number(usage.outputTokens ?? 0);
|
|
@@ -7284,6 +7547,7 @@ async function executeRunCommand(config, command) {
|
|
|
7284
7547
|
error: err instanceof Error ? err.message : String(err)
|
|
7285
7548
|
});
|
|
7286
7549
|
}
|
|
7550
|
+
await ingestWorkspaceStatus(config, command, cwd);
|
|
7287
7551
|
const promptCompilation = buildCommandPrompt(command, workspace, materializedAttachments, {
|
|
7288
7552
|
executorKind: executor.kind,
|
|
7289
7553
|
artifactVerifierCommands: config.artifactVerifierCommands
|
|
@@ -7429,7 +7693,7 @@ async function executeRunCommand(config, command) {
|
|
|
7429
7693
|
}
|
|
7430
7694
|
);
|
|
7431
7695
|
await ingestCost(config, command, executor, parsed);
|
|
7432
|
-
await ingestWorkspaceStatus(config, command, cwd);
|
|
7696
|
+
const workspaceStatus = await ingestWorkspaceStatus(config, command, cwd);
|
|
7433
7697
|
let nativeSessionRollout = null;
|
|
7434
7698
|
let nativeSessionRolloutError = null;
|
|
7435
7699
|
let nativeSessionRolloutCleanup = null;
|
|
@@ -7438,6 +7702,7 @@ async function executeRunCommand(config, command) {
|
|
|
7438
7702
|
...Array.isArray(parsed.mcpToolResults) ? parsed.mcpToolResults.map(asRecord) : [],
|
|
7439
7703
|
...liveOutputLogger.mcpToolResults().map(asRecord)
|
|
7440
7704
|
];
|
|
7705
|
+
const runtimeArtifacts = await ingestRuntimeArtifacts(config, command, cwd, mcpToolResults);
|
|
7441
7706
|
const shouldPreserveNativeSession = managedMcpProfile && parsed.sessionId && (execution.exitCode === 0 || execution.completionOutputType === "approval_required") && !execution.timedOut && execution.cancelled !== true && !execution.spawnError && ["codex", "pi"].includes(executor.kind) && mcpToolResults.some((result3) => readString(result3.status) === "approval_required");
|
|
7442
7707
|
if (shouldPreserveNativeSession) {
|
|
7443
7708
|
try {
|
|
@@ -7446,7 +7711,20 @@ async function executeRunCommand(config, command) {
|
|
|
7446
7711
|
sessionId: parsed.sessionId,
|
|
7447
7712
|
sourceWorkspacePath: workspace.sourceWorkspacePath
|
|
7448
7713
|
});
|
|
7449
|
-
|
|
7714
|
+
const checkpoint = await saveIssueCheckpoint(
|
|
7715
|
+
config,
|
|
7716
|
+
command,
|
|
7717
|
+
workspace,
|
|
7718
|
+
workspaceStatus.artifacts.map((artifact) => ({
|
|
7719
|
+
rawPath: artifact.relativePath,
|
|
7720
|
+
filePath: resolve8(cwd, artifact.relativePath)
|
|
7721
|
+
}))
|
|
7722
|
+
);
|
|
7723
|
+
nativeSessionRollout = {
|
|
7724
|
+
status: preservedRollout.status,
|
|
7725
|
+
sessionId: preservedRollout.sessionId,
|
|
7726
|
+
...checkpoint ? { checkpoint: { fileCount: checkpoint.files.length, totalBytes: checkpoint.totalBytes } } : {}
|
|
7727
|
+
};
|
|
7450
7728
|
await ingestLog(config, command, "system", "info", `Preserved ${executor.kind} session rollout for an approved action continuation`, {
|
|
7451
7729
|
presentationKind: "managed_mcp_session_rollout",
|
|
7452
7730
|
status: nativeSessionRollout.status,
|
|
@@ -7461,8 +7739,10 @@ async function executeRunCommand(config, command) {
|
|
|
7461
7739
|
});
|
|
7462
7740
|
}
|
|
7463
7741
|
}
|
|
7464
|
-
const
|
|
7465
|
-
|
|
7742
|
+
const approvedContinuationSucceeded = nativeSessionRequest.mode === "governed_action_approval" && approvedMcpInvocationSucceeded(mcpToolResults, nativeSessionRequest.invocationId);
|
|
7743
|
+
if (approvedContinuationSucceeded) {
|
|
7744
|
+
await clearIssueCheckpoint(config, command, workspace, "approved_action_succeeded");
|
|
7745
|
+
}
|
|
7466
7746
|
if (managedMcpProfile && executor.kind === "codex" && approvedContinuationSucceeded) {
|
|
7467
7747
|
try {
|
|
7468
7748
|
nativeSessionRolloutCleanup = cleanupRestoredManagedCodexSessionRollout(managedMcpProfile);
|
|
@@ -7537,6 +7817,7 @@ async function executeRunCommand(config, command) {
|
|
|
7537
7817
|
summary: parsed.summary || (succeeded ? "Executor completed without a text summary." : ""),
|
|
7538
7818
|
usage: parsed.usage,
|
|
7539
7819
|
...costUsage ? { costUsage } : {},
|
|
7820
|
+
...runtimeArtifacts.length > 0 ? { runtimeArtifacts } : {},
|
|
7540
7821
|
outputTelemetry,
|
|
7541
7822
|
contextManifest,
|
|
7542
7823
|
stdout: truncateText(execution.stdout, 8e3),
|
|
@@ -7802,7 +8083,7 @@ async function runLoop(config) {
|
|
|
7802
8083
|
${message}
|
|
7803
8084
|
`);
|
|
7804
8085
|
}
|
|
7805
|
-
await new Promise((
|
|
8086
|
+
await new Promise((resolve9) => setTimeout(resolve9, config.pollIntervalSeconds * 1e3));
|
|
7806
8087
|
}
|
|
7807
8088
|
}
|
|
7808
8089
|
function help() {
|
package/dist/amaster-runtime.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { spawn, spawnSync } from "node:child_process";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { homedir, hostname } from "node:os";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
|
-
const CONNECTOR_VERSION = "0.1.0-beta.
|
|
8
|
+
const CONNECTOR_VERSION = "0.1.0-beta.22";
|
|
9
9
|
|
|
10
10
|
const CAPABILITIES = [
|
|
11
11
|
"remote_registration",
|
|
@@ -277,8 +277,26 @@ function readState(config) {
|
|
|
277
277
|
const statePath = config.AMASTER_DAEMON_STATE_FILE || join(configDir, "runtime-connector-state.json");
|
|
278
278
|
if (!existsSync(statePath)) return {};
|
|
279
279
|
try {
|
|
280
|
-
|
|
280
|
+
const state = JSON.parse(readFileSync(statePath, "utf8"));
|
|
281
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) {
|
|
282
|
+
throw new TypeError("runtime connector state must be a JSON object");
|
|
283
|
+
}
|
|
284
|
+
return state;
|
|
281
285
|
} catch {
|
|
286
|
+
const timestamp = new Date().toISOString().replace(/\D/g, "").slice(0, 14);
|
|
287
|
+
const base = `${statePath}.corrupt-${timestamp}Z`;
|
|
288
|
+
let quarantinePath = base;
|
|
289
|
+
for (let suffix = 1; existsSync(quarantinePath); suffix += 1) {
|
|
290
|
+
quarantinePath = `${base}.${suffix}`;
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
renameSync(statePath, quarantinePath);
|
|
294
|
+
process.stderr.write(`AMaster runtime state was invalid and quarantined at ${quarantinePath}\n`);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
process.stderr.write(
|
|
297
|
+
`AMaster runtime state was invalid but could not be quarantined (${error?.code ?? "unknown error"})\n`,
|
|
298
|
+
);
|
|
299
|
+
}
|
|
282
300
|
return {};
|
|
283
301
|
}
|
|
284
302
|
}
|
|
@@ -286,7 +304,16 @@ function readState(config) {
|
|
|
286
304
|
function writeState(config, state) {
|
|
287
305
|
const statePath = withDefaults(config).AMASTER_DAEMON_STATE_FILE || join(configDir, "runtime-connector-state.json");
|
|
288
306
|
mkdirSync(dirname(statePath), { recursive: true });
|
|
289
|
-
|
|
307
|
+
const temporaryPath = `${statePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
308
|
+
try {
|
|
309
|
+
writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}\n`, {
|
|
310
|
+
flag: "wx",
|
|
311
|
+
mode: 0o600,
|
|
312
|
+
});
|
|
313
|
+
renameSync(temporaryPath, statePath);
|
|
314
|
+
} finally {
|
|
315
|
+
rmSync(temporaryPath, { force: true });
|
|
316
|
+
}
|
|
290
317
|
}
|
|
291
318
|
|
|
292
319
|
function readPidInfo() {
|