@amaster.ai/employee-runtime-connector 0.1.0-beta.43 → 0.1.0-beta.45

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 CHANGED
@@ -32,6 +32,18 @@ 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
+ ## Pi terminal cleanup outcome
36
+
37
+ Pi execution uses three separate evidence layers:
38
+
39
+ 1. a valid successful `agent_end` and output/usage contract;
40
+ 2. a durable, inspectable Runtime Action receipt or finalized Runtime Artifact;
41
+ 3. process cleanup disposition.
42
+
43
+ An exact process-kill `EPERM` error emitted only after the first two layers have completed may be isolated as a failed `cleanupDisposition` warning without changing the command's successful business result. Both accepted shapes require `kill` and `EPERM`; generic filesystem/process permission text such as `EPERM: operation not permitted, unlink ...` is a business error. A `runtime_action.status` readback is evidence only when its call/plan ref matches an earlier submit/commit receipt in the same transcript. The diagnostic and durable evidence reference remain in the result. Text-only or action-only-without-assistant-output streams, standalone governed reads, non-effect Runtime Action tools, rejected or pending effects, pre-terminal errors, provider failures, timeout, cancellation, resource limits, any signal, live residue, and uncertain ownership remain failures.
44
+
45
+ This isolation does not schedule a retry or a second business continuation. Command result delivery and the result outbox remain the sole idempotency boundary.
46
+
35
47
  ## Mutation attestation
36
48
 
37
49
  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.
@@ -2558,6 +2558,7 @@ function isTerminalResultOutboxStatus(status) {
2558
2558
 
2559
2559
  // src/amaster-runtime-daemon/prompt-compiler.mjs
2560
2560
  var DEFAULT_PROMPT_BUDGET_CHARS = 32e3;
2561
+ var DEADLINE_POSTURE_GUARD = "Named-window:skip_this_window_and_continue; no lower-quality/approval-bypass/fabrication/whole-task-stop; deadline_posture_receipt=targetMilestoneRef,posture,onMiss,taskContinuation,next owner/action; not Server proof";
2561
2562
  var MIN_PROMPT_BUDGET_CHARS = 8192;
2562
2563
  var CONTINUATION_WAKE_PATTERN = /(continuation|continued|retry|approved|liveness|resume|max_turn)/i;
2563
2564
  var RECOVERY_WAKE_REASONS = /* @__PURE__ */ new Set([
@@ -2670,9 +2671,10 @@ function governedReadSection(context) {
2670
2671
  function fixedRules(input, includeIssueLine) {
2671
2672
  return [
2672
2673
  "## AMaster Runtime Connector Task",
2673
- "You are executing a task dispatched by MirrorX from the central control plane.",
2674
- "Work only inside the declared workspace. Make concrete progress and finish with a concise result summary.",
2674
+ "MirrorX task.",
2675
+ "Use only the declared workspace; make concrete progress and report concisely.",
2675
2676
  "Before changing the task status to done, audit every explicit requirement in the task against the final evidence. A successful tool or document write proves delivery, not acceptance: inspect the delivered content for required sections, diagrams, tables, and factual constraints. Do not leave stale in-progress wording such as \u201Ccurrent run\u201D in a terminal deliverable; rewrite it to the final observed state. Do not list finalization itself as remaining or next work in a terminal deliverable. If any requirement is missing or cannot be verified, do not mark done. To request review, use create_interaction with kind request_confirmation and payload.resolutionMode review; never call update_parent with status in_review. Otherwise keep the issue todo or blocked with the exact gap and next owner.",
2677
+ DEADLINE_POSTURE_GUARD,
2676
2678
  "Do not install operating-system or user-global packages, and do not use host package managers such as brew, apt, yum, or global pip/npm installs. Use tools already available or workspace-local dependencies or virtual environments. If a required renderer or evaluator is unavailable, keep the source artifact, record the exact verification gap, and do not mutate the host.",
2677
2679
  `- command id: ${input.commandId}`,
2678
2680
  `- run id: ${input.runId ?? "unknown"}`,
@@ -3690,7 +3692,17 @@ var PI_PROVIDER_AUTH_RE = /(?:(?:\b401\b|\b403\b)[^\n]*(?:unauthorized|forbidden
3690
3692
  var PI_PROVIDER_QUOTA_EXHAUSTED_RE = /(?:\binsufficient[_\s-]?user[_\s-]?quota\b|河狸币余额不足|\b402\b[^\n]*(?:余额不足|payment\s+required))/i;
3691
3693
  var PI_PROVIDER_TRANSIENT_RE = /(?:\b(?:429|5\d{2})\b|rate[-\s]?limit(?:ed)?|too\s+many\s+requests|billing\s+admission\s+failed|service\s+unavailable|upstream[^\n]*(?:unavailable|failed|timeout)|connect(?:ion)?[^\n]*refused|temporar(?:y|ily)[^\n]*(?:unavailable|failed)|try\s+again\s+later)/i;
3692
3694
  var PI_PROVIDER_RETRY_AFTER_SECONDS_RE = /retry[-\s]?after\s*[:=]?\s*(\d{1,6})\s*(?:seconds?|secs?|s)\b/i;
3695
+ var PI_TERMINAL_CLEANUP_PERMISSION_RE = /(?:\bkill\b[^\n]*\bEPERM\b|\bEPERM\b[^\n]*\bkill\b)/i;
3693
3696
  var MAX_PI_PROVIDER_RETRY_AFTER_SECONDS = 7 * 24 * 60 * 60;
3697
+ var PI_DURABLE_RUNTIME_ACTION_WRITE_TOOLS = /* @__PURE__ */ new Set([
3698
+ "runtime_action.submit",
3699
+ "runtime_action.commit"
3700
+ ]);
3701
+ var PI_RUNTIME_ACTION_WRITE_RECEIPT_STATUSES = /* @__PURE__ */ new Set(["accepted", "pending_reconcile"]);
3702
+ var PI_TERMINAL_RUNTIME_ACTION_STATUSES = /* @__PURE__ */ new Set([
3703
+ "completed",
3704
+ "succeeded"
3705
+ ]);
3694
3706
  function approvedMcpInvocationSucceeded(results, invocationId) {
3695
3707
  const approvedInvocationId = readString(invocationId);
3696
3708
  return Boolean(approvedInvocationId && (Array.isArray(results) ? results : []).some((rawResult) => {
@@ -3704,18 +3716,81 @@ function governedMcpToolResult(structuredContent) {
3704
3716
  const invocationId = readString(structuredContent.invocationId);
3705
3717
  const providerContent = asRecord(structuredContent.content);
3706
3718
  const providerStatus = readString(providerContent.status);
3707
- const effectResult = asRecord(asRecord(providerContent.result).effectResult);
3719
+ const providerResult = asRecord(providerContent.result);
3720
+ const effectResult = asRecord(providerResult.effectResult);
3708
3721
  const intentId = readString(effectResult.artifactIntentId);
3709
3722
  const manifestId = readString(effectResult.manifestId);
3710
3723
  const sourceRelativePath = readString(effectResult.sourceRelativePath);
3711
3724
  const sha256 = readString(effectResult.sha256);
3712
3725
  const byteSize = readNumber(effectResult.byteSize, 0);
3713
3726
  const artifactIntent = providerStatus === "pending_reconcile" && intentId && manifestId && sourceRelativePath && /^[a-f0-9]{64}$/.test(sha256 ?? "") && Number.isSafeInteger(byteSize) && byteSize > 0 ? { intentId, manifestId, sourceRelativePath, sha256, byteSize } : null;
3727
+ const runtimeActionToolName = readString(providerContent.toolName);
3728
+ const runtimeAction = runtimeActionToolName?.startsWith("runtime_action.") ? {
3729
+ toolName: runtimeActionToolName,
3730
+ ...readString(providerResult.callId) ? { callId: readString(providerResult.callId) } : {},
3731
+ ...readString(providerResult.planId) ? { planId: readString(providerResult.planId) } : {},
3732
+ ...readString(providerResult.status) ? { resultStatus: readString(providerResult.status) } : {}
3733
+ } : null;
3714
3734
  return {
3715
3735
  ...invocationId ? { invocationId } : {},
3716
3736
  status,
3717
3737
  ...providerStatus ? { providerStatus } : {},
3718
- ...artifactIntent ? { artifactIntent } : {}
3738
+ ...artifactIntent ? { artifactIntent } : {},
3739
+ ...runtimeAction ? { runtimeAction } : {}
3740
+ };
3741
+ }
3742
+ function durablePiRuntimeActionEvidence(results) {
3743
+ const normalizedResults = (Array.isArray(results) ? results : []).map(asRecord);
3744
+ const writeCallIds = /* @__PURE__ */ new Set();
3745
+ const writePlanIds = /* @__PURE__ */ new Set();
3746
+ for (const result2 of normalizedResults) {
3747
+ const runtimeAction = asRecord(result2.runtimeAction);
3748
+ const toolName = readString(runtimeAction.toolName);
3749
+ const callId = readString(runtimeAction.callId);
3750
+ const planId = readString(runtimeAction.planId);
3751
+ const resultStatus = readString(runtimeAction.resultStatus);
3752
+ const status = readString(result2.status);
3753
+ const providerStatus = readString(result2.providerStatus);
3754
+ if (status === "succeeded" && providerStatus === "accepted" && toolName && resultStatus && PI_TERMINAL_RUNTIME_ACTION_STATUSES.has(resultStatus)) {
3755
+ const isLinkedStatus = toolName === "runtime_action.status" && (callId && writeCallIds.has(callId) || planId && writePlanIds.has(planId));
3756
+ const isDurableWrite = toolName === "runtime_action.submit" ? Boolean(callId) : toolName === "runtime_action.commit" && Boolean(planId || callId);
3757
+ if (isLinkedStatus || isDurableWrite) {
3758
+ return {
3759
+ kind: "runtime_action",
3760
+ ...readString(result2.invocationId) ? { invocationId: readString(result2.invocationId) } : {},
3761
+ toolName,
3762
+ ...callId ? { callId } : {},
3763
+ ...planId ? { planId } : {}
3764
+ };
3765
+ }
3766
+ }
3767
+ if (status !== "succeeded" || !PI_DURABLE_RUNTIME_ACTION_WRITE_TOOLS.has(toolName ?? "") || !PI_RUNTIME_ACTION_WRITE_RECEIPT_STATUSES.has(providerStatus ?? "")) continue;
3768
+ if (callId) writeCallIds.add(callId);
3769
+ if (planId) writePlanIds.add(planId);
3770
+ }
3771
+ return null;
3772
+ }
3773
+ function finalizedPiRuntimeArtifactEvidence(runtimeArtifacts) {
3774
+ const artifact = (Array.isArray(runtimeArtifacts) ? runtimeArtifacts : []).map(asRecord).find((entry) => readString(entry.status) === "finalized");
3775
+ if (!artifact) return null;
3776
+ const intentId = readString(artifact.intentId) ?? readString(artifact.artifactIntentId);
3777
+ return {
3778
+ kind: "runtime_artifact",
3779
+ ...intentId ? { intentId } : {}
3780
+ };
3781
+ }
3782
+ function classifyPiTerminalCleanupDisposition(parsed, runtimeArtifacts) {
3783
+ const diagnostics = Array.isArray(parsed?.cleanupDiagnostics) ? parsed.cleanupDiagnostics.map(asRecord) : [];
3784
+ if (parsed?.terminalEventType !== "agent_end" || readString(parsed?.stopReason) || parsed?.hasAssistantOutput !== true || diagnostics.length === 0 || diagnostics.some((diagnostic) => readString(diagnostic.phase) !== "post_terminal" || readString(diagnostic.code) !== "pi_terminal_cleanup_permission_denied") || readNumber(parsed?.nonCleanupErrorCount, 0) > 0) return null;
3785
+ const durableEvidence = durablePiRuntimeActionEvidence(parsed?.mcpToolResults) ?? finalizedPiRuntimeArtifactEvidence(runtimeArtifacts);
3786
+ if (!durableEvidence) return null;
3787
+ return {
3788
+ status: "failed",
3789
+ phase: "post_terminal",
3790
+ errorCode: "pi_terminal_cleanup_permission_denied",
3791
+ isolatedFromBusinessResult: true,
3792
+ durableEvidence,
3793
+ diagnostics
3719
3794
  };
3720
3795
  }
3721
3796
  function codexMcpToolResults(event) {
@@ -4159,8 +4234,11 @@ function parsePiJsonl(stdout) {
4159
4234
  let terminalEventType = null;
4160
4235
  let stopReason = null;
4161
4236
  let hasAssistantOutput = false;
4237
+ let nonCleanupErrorCount = 0;
4238
+ let nonCleanupErrorMessage = null;
4162
4239
  const messages = [];
4163
4240
  const mcpToolResults = [];
4241
+ const cleanupDiagnostics = [];
4164
4242
  const usage = { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 };
4165
4243
  for (const rawLine of String(stdout ?? "").split(/\r?\n/)) {
4166
4244
  const event = parseJsonLine(rawLine.trim());
@@ -4191,12 +4269,30 @@ function parsePiJsonl(stdout) {
4191
4269
  if (event.type === "turn_end") sawTurnEnd = true;
4192
4270
  if (event.type === "turn_end" || event.type === "agent_end") terminalEventType = event.type;
4193
4271
  stopReason = readString(event.stopReason ?? event.stop_reason) ?? piNestedMessageStopReason(event) ?? stopReason;
4194
- errorMessage = piStopReasonErrorText(event) ?? piNestedMessageErrorText(event) ?? errorMessage;
4272
+ const terminalError = piStopReasonErrorText(event) ?? piNestedMessageErrorText(event);
4273
+ if (terminalError) {
4274
+ nonCleanupErrorCount += 1;
4275
+ nonCleanupErrorMessage = terminalError;
4276
+ errorMessage = terminalError;
4277
+ }
4195
4278
  hasAssistantOutput = maybeCapturePiMessage(event, messages, usage) || hasAssistantOutput;
4196
4279
  continue;
4197
4280
  }
4198
4281
  if (event.type === "error") {
4199
- errorMessage = readString(event.message) ?? errorMessage;
4282
+ const eventError = readString(event.message);
4283
+ if (!eventError) continue;
4284
+ if (PI_TERMINAL_CLEANUP_PERMISSION_RE.test(eventError)) {
4285
+ cleanupDiagnostics.push({
4286
+ code: "pi_terminal_cleanup_permission_denied",
4287
+ message: eventError,
4288
+ phase: terminalEventType === "agent_end" ? "post_terminal" : "pre_terminal"
4289
+ });
4290
+ errorMessage = nonCleanupErrorMessage ?? eventError;
4291
+ } else {
4292
+ nonCleanupErrorCount += 1;
4293
+ nonCleanupErrorMessage = eventError;
4294
+ errorMessage = eventError;
4295
+ }
4200
4296
  }
4201
4297
  }
4202
4298
  return {
@@ -4208,6 +4304,8 @@ function parsePiJsonl(stdout) {
4208
4304
  stopReason,
4209
4305
  hasAssistantOutput,
4210
4306
  errorMessage,
4307
+ ...nonCleanupErrorCount > 0 ? { nonCleanupErrorCount } : {},
4308
+ ...cleanupDiagnostics.length > 0 ? { cleanupDiagnostics } : {},
4211
4309
  ...mcpToolResults.length > 0 ? { mcpToolResults } : {}
4212
4310
  };
4213
4311
  }
@@ -4953,7 +5051,79 @@ function normalizeRuntimeVersionText(value) {
4953
5051
  const trimmed = value.trim();
4954
5052
  return trimmed ? trimmed : null;
4955
5053
  }
4956
- function versionPairDiffers(current, recommended) {
5054
+ var EXACT_SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
5055
+ function isExactSemverVersion(value) {
5056
+ const normalized = normalizeRuntimeVersionText(value);
5057
+ const match = normalized?.match(EXACT_SEMVER_PATTERN);
5058
+ if (!match) return false;
5059
+ const prerelease = match[4]?.split(".") ?? [];
5060
+ return !prerelease.some(
5061
+ (identifier) => /^\d+$/.test(identifier) && identifier.length > 1 && identifier.startsWith("0")
5062
+ );
5063
+ }
5064
+ function compareNumericSemverIdentifiers(left, right) {
5065
+ if (left.length !== right.length) return left.length < right.length ? -1 : 1;
5066
+ if (left === right) return 0;
5067
+ return left < right ? -1 : 1;
5068
+ }
5069
+ function compareExactSemverVersions(current, recommended) {
5070
+ const normalizedCurrent = normalizeRuntimeVersionText(current);
5071
+ const normalizedRecommended = normalizeRuntimeVersionText(recommended);
5072
+ if (!normalizedCurrent || !normalizedRecommended || !isExactSemverVersion(normalizedCurrent) || !isExactSemverVersion(normalizedRecommended)) {
5073
+ return null;
5074
+ }
5075
+ const currentMatch = normalizedCurrent.match(EXACT_SEMVER_PATTERN);
5076
+ const recommendedMatch = normalizedRecommended.match(EXACT_SEMVER_PATTERN);
5077
+ if (!currentMatch || !recommendedMatch) return null;
5078
+ for (const index of [1, 2, 3]) {
5079
+ const comparison = compareNumericSemverIdentifiers(
5080
+ currentMatch[index],
5081
+ recommendedMatch[index]
5082
+ );
5083
+ if (comparison !== 0) return comparison;
5084
+ }
5085
+ const currentPrerelease = currentMatch[4]?.split(".");
5086
+ const recommendedPrerelease = recommendedMatch[4]?.split(".");
5087
+ if (!currentPrerelease && !recommendedPrerelease) return 0;
5088
+ if (!currentPrerelease) return 1;
5089
+ if (!recommendedPrerelease) return -1;
5090
+ const identifierCount = Math.max(
5091
+ currentPrerelease.length,
5092
+ recommendedPrerelease.length
5093
+ );
5094
+ for (let index = 0; index < identifierCount; index += 1) {
5095
+ const currentIdentifier = currentPrerelease[index];
5096
+ const recommendedIdentifier = recommendedPrerelease[index];
5097
+ if (currentIdentifier === void 0) return -1;
5098
+ if (recommendedIdentifier === void 0) return 1;
5099
+ if (currentIdentifier === recommendedIdentifier) continue;
5100
+ const currentIsNumeric = /^\d+$/.test(currentIdentifier);
5101
+ const recommendedIsNumeric = /^\d+$/.test(recommendedIdentifier);
5102
+ if (currentIsNumeric && recommendedIsNumeric) {
5103
+ return compareNumericSemverIdentifiers(
5104
+ currentIdentifier,
5105
+ recommendedIdentifier
5106
+ );
5107
+ }
5108
+ if (currentIsNumeric !== recommendedIsNumeric) {
5109
+ return currentIsNumeric ? -1 : 1;
5110
+ }
5111
+ return currentIdentifier < recommendedIdentifier ? -1 : 1;
5112
+ }
5113
+ return 0;
5114
+ }
5115
+ function versionPairRequiresUpgrade(current, recommended) {
5116
+ const normalizedCurrent = normalizeRuntimeVersionText(current);
5117
+ const normalizedRecommended = normalizeRuntimeVersionText(recommended);
5118
+ if (!normalizedCurrent || !normalizedRecommended) return false;
5119
+ const comparison = compareExactSemverVersions(
5120
+ normalizedCurrent,
5121
+ normalizedRecommended
5122
+ );
5123
+ if (comparison !== null) return comparison < 0;
5124
+ return false;
5125
+ }
5126
+ function normalizedTextPairDiffers(current, recommended) {
4957
5127
  const normalizedCurrent = normalizeRuntimeVersionText(current);
4958
5128
  const normalizedRecommended = normalizeRuntimeVersionText(recommended);
4959
5129
  if (!normalizedCurrent || !normalizedRecommended) return false;
@@ -4962,10 +5132,10 @@ function versionPairDiffers(current, recommended) {
4962
5132
  function summarizeAmasterRuntimeVersionDrift(input = {}) {
4963
5133
  const versionMismatches = [];
4964
5134
  const bundleMismatches = [];
4965
- if (versionPairDiffers(input.connectorVersion, input.recommendedConnectorVersion)) {
5135
+ if (versionPairRequiresUpgrade(input.connectorVersion, input.recommendedConnectorVersion)) {
4966
5136
  versionMismatches.push("connector package version differs from the recommended package");
4967
5137
  }
4968
- if (versionPairDiffers(input.buildCommit, input.recommendedBuildCommit)) {
5138
+ if (normalizedTextPairDiffers(input.buildCommit, input.recommendedBuildCommit)) {
4969
5139
  bundleMismatches.push("build commit differs from the deployed connector package");
4970
5140
  }
4971
5141
  const recommendedConnectorVersion = normalizeRuntimeVersionText(input.recommendedConnectorVersion);
@@ -6061,7 +6231,7 @@ function readWorkspaceStatus(cwd, opts = {}) {
6061
6231
  }
6062
6232
 
6063
6233
  // src/amaster-runtime-daemon.mjs
6064
- var CONNECTOR_VERSION = "0.1.0-beta.43";
6234
+ var CONNECTOR_VERSION = "0.1.0-beta.45";
6065
6235
  var CONNECTOR_CONTRACT_VERSION = "2026-06-04.v1";
6066
6236
  var MAX_CHECKPOINT_BYTES = 20 * 1024 * 1024;
6067
6237
  var CHECKPOINT_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -6339,7 +6509,10 @@ function piCapabilitySourcesDiagnostics() {
6339
6509
  const piAgentHome = configuredPiAgentHome ?? piCodingAgentDir;
6340
6510
  const piAgentHomeSource = configuredPiAgentHome ? "PI_AGENT_HOME" : piCodingAgentDirSource;
6341
6511
  const userSkillsPath = piAgentHome ? join13(piAgentHome, "skills") : null;
6342
- const marketplaceSkillsPath = safeExpandPath(process.env.PI_AGENT_MARKETPLACE_SKILLS_DIR) ?? (piAgentHome ? join13(piAgentHome, "marketplace", "skills") : null);
6512
+ const configuredMarketplaceSkillsPath = safeExpandPath(
6513
+ process.env.PI_AGENT_MARKETPLACE_SKILLS_DIR
6514
+ );
6515
+ const marketplaceSkillsPath = configuredMarketplaceSkillsPath ?? (piAgentHome ? join13(piAgentHome, "marketplace", "skills") : null);
6343
6516
  const builtinSkillsPath = safeExpandPath(process.env.PI_AGENT_BUILTIN_SKILLS_DIR) ?? safeExpandPath(process.env.AMASTER_BUILTIN_SKILLS);
6344
6517
  const mcpConfigPath = safeExpandPath(process.env.PI_AGENT_MCP_SERVERS_FILE) ?? (piAgentHome ? join13(piAgentHome, "mcp.json") : null);
6345
6518
  const settingsConfigPath = piCodingAgentDir ? join13(piCodingAgentDir, "settings.json") : null;
@@ -6347,7 +6520,7 @@ function piCapabilitySourcesDiagnostics() {
6347
6520
  safeSkillRootSummary("user", `${piAgentHomeSource}/skills`, userSkillsPath),
6348
6521
  safeSkillRootSummary(
6349
6522
  "marketplace",
6350
- readString(process.env.PI_AGENT_MARKETPLACE_SKILLS_DIR) ? "PI_AGENT_MARKETPLACE_SKILLS_DIR" : `${piAgentHomeSource}/marketplace/skills`,
6523
+ configuredMarketplaceSkillsPath ? "PI_AGENT_MARKETPLACE_SKILLS_DIR" : `${piAgentHomeSource}/marketplace/skills`,
6351
6524
  marketplaceSkillsPath
6352
6525
  ),
6353
6526
  safeSkillRootSummary(
@@ -6357,7 +6530,9 @@ function piCapabilitySourcesDiagnostics() {
6357
6530
  )
6358
6531
  ];
6359
6532
  const visibleSkillCount = skillRoots.reduce((sum, root) => sum + readNumber(root.skillCount, 0), 0);
6360
- const missingRootKinds = skillRoots.filter((root) => root.configured === true && root.available !== true).map((root) => root.kind);
6533
+ const missingRootKinds = skillRoots.filter(
6534
+ (root) => root.configured === true && root.available !== true && (root.kind !== "marketplace" || configuredMarketplaceSkillsPath)
6535
+ ).map((root) => root.kind);
6361
6536
  const mcpConfig = safeJsonConfigSummary(
6362
6537
  readString(process.env.PI_AGENT_MCP_SERVERS_FILE) ? "PI_AGENT_MCP_SERVERS_FILE" : `${piAgentHomeSource}/mcp.json`,
6363
6538
  mcpConfigPath,
@@ -9611,18 +9786,33 @@ async function executeRunCommand(config, command) {
9611
9786
  const memoryLimitError = hasMemoryLimit ? `${executor.kind === "pi" ? "Pi Agent" : "Executor"} memory limit exceeded: RSS ${readNumber(memoryLimit.rssBytes, 0)} bytes exceeded ${readNumber(memoryLimit.limitBytes, config.executorMaxRssMb * 1024 * 1024)} bytes` : null;
9612
9787
  const piUsageDiagnostic = executor.kind === "pi" && !hasOutputFlood && piOutputUsageMetadataMissing(parsed) ? "Pi Agent exited without usage metadata" : null;
9613
9788
  const completionOutputStopped = executor.kind === "pi" && piCompletionOutputStopped(parsed, execution);
9614
- const piInvalidOutputError = executor.kind === "pi" ? piOutputValidationError(parsed, {
9615
- allowMissingTurnEnd: completionOutputStopped,
9789
+ const cleanupDisposition = executor.kind === "pi" && !cancelled && !hasOutputFlood && !hasMemoryLimit && execution.timedOut !== true && !execution.spawnError && execution.signal === null ? classifyPiTerminalCleanupDisposition(parsed, runtimeArtifacts) : null;
9790
+ if (cleanupDisposition) {
9791
+ await ingestLog(
9792
+ config,
9793
+ command,
9794
+ "system",
9795
+ "warn",
9796
+ "Pi terminal result was preserved after a post-terminal cleanup permission failure",
9797
+ {
9798
+ presentationKind: "pi_terminal_cleanup",
9799
+ cleanupDisposition
9800
+ }
9801
+ );
9802
+ }
9803
+ const parsedForValidation = cleanupDisposition ? { ...parsed, errorMessage: null } : parsed;
9804
+ const piInvalidOutputError = executor.kind === "pi" ? piOutputValidationError(parsedForValidation, {
9805
+ allowMissingTurnEnd: completionOutputStopped || Boolean(cleanupDisposition),
9616
9806
  allowMissingAssistantOutput: execution.completionOutputType === "approval_required"
9617
9807
  }) : null;
9618
- const piProviderFailure = executor.kind === "pi" && !cancelled && !hasOutputFlood && !hasMemoryLimit && execution.timedOut !== true && !execution.spawnError ? classifyPiProviderError(parsed) : null;
9619
- const parsedErrorMessage = outputFloodError ?? memoryLimitError ?? piInvalidOutputError ?? nativeSessionRolloutError ?? nativeSessionRolloutCleanupError ?? parsed.errorMessage;
9808
+ const piProviderFailure = executor.kind === "pi" && !cancelled && !hasOutputFlood && !hasMemoryLimit && execution.timedOut !== true && !execution.spawnError ? classifyPiProviderError(parsedForValidation) : null;
9809
+ const parsedErrorMessage = outputFloodError ?? memoryLimitError ?? piInvalidOutputError ?? nativeSessionRolloutError ?? nativeSessionRolloutCleanupError ?? parsedForValidation.errorMessage;
9620
9810
  const codexTransientFailure = executor.kind === "codex" && (execution.exitCode ?? 0) !== 0 ? classifyCodexTransientUpstreamError({
9621
9811
  stdout: execution.stdout,
9622
9812
  stderr: execution.stderr,
9623
9813
  errorMessage: parsedErrorMessage
9624
9814
  }) : null;
9625
- const succeeded = !cancelled && !hasOutputFlood && !hasMemoryLimit && (execution.exitCode === 0 || completionOutputStopped) && !execution.timedOut && !execution.spawnError && !parsedErrorMessage;
9815
+ const succeeded = !cancelled && !hasOutputFlood && !hasMemoryLimit && (execution.exitCode === 0 || completionOutputStopped || Boolean(cleanupDisposition)) && !execution.timedOut && !execution.spawnError && !parsedErrorMessage;
9626
9816
  const resultStderr = filterExecutionStderrForResult(executor.kind, execution.stderr);
9627
9817
  const error = execution.timedOut ? `Executor timed out after ${config.executorTimeoutSeconds}s` : cancelled ? "Executor cancelled by AMaster control plane" : execution.spawnError ?? parsedErrorMessage ?? (succeeded ? null : `Executor exited with code ${execution.exitCode ?? "unknown"}`);
9628
9818
  const costUsage = parsedCostUsage(parsed.usage);
@@ -9655,6 +9845,7 @@ async function executeRunCommand(config, command) {
9655
9845
  timedOut: execution.timedOut,
9656
9846
  ...execution.outputDrainForcedClosed ? { outputDrainForcedClosed: true } : {},
9657
9847
  ...readString(execution.completionOutputType) ? { completionOutputType: readString(execution.completionOutputType) } : {},
9848
+ ...cleanupDisposition ? { cleanupDisposition } : {},
9658
9849
  ...cancelled ? { cancelledByControlPlane: true } : {},
9659
9850
  ...invocation.nativeSession ? { nativeSession: invocation.nativeSession } : {},
9660
9851
  ...nativeSessionRollout ? { nativeSessionRollout } : {},
@@ -5,7 +5,7 @@ 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.43";
8
+ const CONNECTOR_VERSION = "0.1.0-beta.45";
9
9
 
10
10
  const CAPABILITIES = [
11
11
  "remote_registration",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amaster.ai/employee-runtime-connector",
3
- "version": "0.1.0-beta.43",
3
+ "version": "0.1.0-beta.45",
4
4
  "description": "MirrorX runtime connector CLI and daemon",
5
5
  "license": "MIT",
6
6
  "type": "module",