@amaster.ai/employee-runtime-connector 0.1.0-beta.12 → 0.1.0-beta.14

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.
@@ -2335,7 +2335,7 @@ import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync
2335
2335
  import { homedir, hostname } from "node:os";
2336
2336
  import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
2337
2337
  import { spawn, spawnSync } from "node:child_process";
2338
- const CONNECTOR_VERSION = "0.1.0-beta.12";
2338
+ const CONNECTOR_VERSION = "0.1.0-beta.14";
2339
2339
  const MAX_INFERRED_ARTIFACT_UPLOADS = 20;
2340
2340
  const MAX_CHECKPOINT_BYTES = 20 * 1024 * 1024;
2341
2341
  const CHECKPOINT_TTL_MS = 24 * 60 * 60 * 1000;
@@ -3562,7 +3562,7 @@ function runtimeActionsInstruction(options = {}) {
3562
3562
  "If direct AMaster API requests are unavailable from the executor sandbox, do not stop at blocked. Use the runtime action bridge for the unapplied mutations.",
3563
3563
  "Use runtime actions as a continuation patch, not as a full replay. If the wake reason or latest comments say a confirmation was accepted, rejected, or that no more child tasks/interactions should be created, do not recreate those earlier actions. Add the final result comment and update the parent task instead.",
3564
3564
  "On an accepted request_confirmation or request_checkbox_confirmation continuation, any new confirmation must include payload.target.type and a non-empty payload.target.key. Missing decision identity fields fail before mutation with accepted_confirmation_decision_target_required. The server reuses identical content for the same target identity and rejects changed prompt, options, or labels until the action uses the genuinely new target revision.",
3565
- "Every request_confirmation and request_checkbox_confirmation should bind the concrete decision to payload.target using {type:\"custom\", key, label, revisionId} or the applicable issue_document target. Keep target.key stable for the same decision purpose and change target.revisionId only when the target content changes. Replace example target values with task-specific values; when no business revision exists, use the current run id as revisionId instead of omitting target.",
3565
+ "Every request_confirmation and request_checkbox_confirmation should bind the concrete decision to payload.target using {type:\"custom\", key, label, revisionId} or the applicable issue_document target. Keep target.key stable for the same decision purpose and change target.revisionId only when the target content changes. Replace example target values with task-specific values. For a custom target with no business revision, use the current run id as revisionId. For an existing issue_document target, use only the exact current revision from task context and never invent a revision id. When the same runtime action batch first upserts the target issue document, omit target.revisionId; the connector binds the created revision before posting the confirmation.",
3566
3566
  "If the wake reason is issue_children_completed, treat the completed child tasks as already resolved blockers. Do not recreate blocker child tasks, do not repeat the first-run blocker plan, and do not create another review path unless new human input is truly required. Summarize the completed child work and use update_parent status done when no human review is needed.",
3567
3567
  "Use update_parent status done when no human review is needed. Prefer creating a pending review path such as request_confirmation, request_checkbox_confirmation, ask_user_questions, or suggest_tasks before status in_review; if a bare in_review update is emitted, AMaster will create a fallback request_confirmation before PATCH.",
3568
3568
  "For create_child_task, do not create ownerless executable todo tasks. Set assigneeAgentId only when the target agent id came from the current AMaster task context or an agent_hire action in this same batch. Do not invent agent ids, role ids, or ids from another company. If you are unsure, omit assigneeAgentId and assigneeUserId; AMaster will assign the current executing agent for todo/in_progress children. Use backlog only when a task is intentionally unassigned.",
@@ -7555,7 +7555,7 @@ function assertRuntimeConfirmationTarget(target) {
7555
7555
  key.length <= 64 && /^[a-z0-9][a-z0-9_-]*$/.test(key),
7556
7556
  "confirmation issue_document target key must use lowercase letters, numbers, _ or -",
7557
7557
  );
7558
- assertRuntimeActionValid(revisionId && uuidPattern.test(revisionId), "confirmation issue_document target revisionId must be a UUID");
7558
+ assertRuntimeActionValid(!revisionId || uuidPattern.test(revisionId), "confirmation issue_document target revisionId must be a UUID");
7559
7559
  for (const field of ["issueId", "documentId"]) {
7560
7560
  const value = readString(record[field]);
7561
7561
  assertRuntimeActionValid(!value || uuidPattern.test(value), `confirmation issue_document target ${field} must be a UUID`);
@@ -7930,6 +7930,33 @@ function preflightRuntimeActionBatch(actions, options = {}) {
7930
7930
  }
7931
7931
  documentActionIndexByKey.set(key, actionIndex);
7932
7932
  }
7933
+ for (const action of orderedActions) {
7934
+ const fields = runtimeActionBusinessFields(action);
7935
+ if (readRuntimeActionType(fields) !== "create_interaction") continue;
7936
+ const kind = inferCreateInteractionKind(fields);
7937
+ if (!["request_confirmation", "request_checkbox_confirmation"].includes(kind ?? "")) continue;
7938
+ const target = asRecord(asRecord(fields.payload).target);
7939
+ if (readString(target.type) !== "issue_document") continue;
7940
+ const key = readString(target.key);
7941
+ if (!key) continue;
7942
+ const actionIndex = actionIndexByObject.get(action);
7943
+ const sameBatchDocumentActionIndex = documentActionIndexByKey.get(key);
7944
+ const sameBatchDocumentUpsert = Number.isInteger(sameBatchDocumentActionIndex);
7945
+ const revisionId = readString(target.revisionId);
7946
+ if (sameBatchDocumentUpsert && !revisionId && sameBatchDocumentActionIndex < actionIndex) continue;
7947
+ if (!sameBatchDocumentUpsert && revisionId) continue;
7948
+ const err = runtimeActionValidationError(
7949
+ sameBatchDocumentUpsert
7950
+ ? revisionId
7951
+ ? `same-batch issue document confirmation for ${key} must omit revisionId so the connector can bind the created revision`
7952
+ : `same-batch issue document confirmation for ${key} requires an earlier upsert_document action so the connector can bind the created revision before applying the interaction`
7953
+ : `issue document confirmation for ${key} requires revisionId when the document is not upserted in the same batch`,
7954
+ );
7955
+ err.runtimeActionType = "create_interaction";
7956
+ err.runtimeActionKind = kind;
7957
+ err.runtimeActionFieldPath = `actions[${actionIndex}].payload.target.revisionId`;
7958
+ throw err;
7959
+ }
7933
7960
  assertRuntimeActionsAllowedByContinuationScope(orderedActions, options.continuationScope);
7934
7961
  assertNoAmbiguousApprovalGatedSideEffects(orderedActions);
7935
7962
  const preflightContext = {
@@ -7983,11 +8010,9 @@ function preflightRuntimeActionBatch(actions, options = {}) {
7983
8010
 
7984
8011
  async function preflightRuntimeDocumentRevisions(runtimeAuth, issueId, actions) {
7985
8012
  const baseRevisionIdsByKey = new Map();
7986
- for (const [actionIndex, action] of actions.entries()) {
7987
- if (readRuntimeActionType(action) !== "upsert_document") continue;
7988
- const fields = runtimeActionBusinessFields(action);
7989
- const key = readBoundedActionString(fields, "key", 64);
7990
- if (!key) continue;
8013
+ const existingDocumentsByKey = new Map();
8014
+ const readExistingDocument = async (key) => {
8015
+ if (existingDocumentsByKey.has(key)) return existingDocumentsByKey.get(key);
7991
8016
  let existingDocument = null;
7992
8017
  try {
7993
8018
  existingDocument = asRecord(await runtimeApiJson(
@@ -7998,6 +8023,15 @@ async function preflightRuntimeDocumentRevisions(runtimeAuth, issueId, actions)
7998
8023
  } catch (err) {
7999
8024
  if (err?.httpStatus !== 404) throw err;
8000
8025
  }
8026
+ existingDocumentsByKey.set(key, existingDocument);
8027
+ return existingDocument;
8028
+ };
8029
+ for (const [actionIndex, action] of actions.entries()) {
8030
+ if (readRuntimeActionType(action) !== "upsert_document") continue;
8031
+ const fields = runtimeActionBusinessFields(action);
8032
+ const key = readBoundedActionString(fields, "key", 64);
8033
+ if (!key) continue;
8034
+ const existingDocument = await readExistingDocument(key);
8001
8035
 
8002
8036
  const explicitBaseRevisionId = readString(fields.baseRevisionId);
8003
8037
  const currentRevisionId = readString(existingDocument?.latestRevisionId);
@@ -8028,6 +8062,29 @@ async function preflightRuntimeDocumentRevisions(runtimeAuth, issueId, actions)
8028
8062
  }
8029
8063
  baseRevisionIdsByKey.set(key, currentRevisionId);
8030
8064
  }
8065
+ for (const [actionIndex, action] of actions.entries()) {
8066
+ const fields = runtimeActionBusinessFields(action);
8067
+ if (readRuntimeActionType(fields) !== "create_interaction") continue;
8068
+ const kind = inferCreateInteractionKind(fields);
8069
+ if (!["request_confirmation", "request_checkbox_confirmation"].includes(kind ?? "")) continue;
8070
+ const target = asRecord(asRecord(fields.payload).target);
8071
+ if (readString(target.type) !== "issue_document") continue;
8072
+ const key = readString(target.key);
8073
+ if (!key || baseRevisionIdsByKey.has(key)) continue;
8074
+ const existingDocument = await readExistingDocument(key);
8075
+ const currentRevisionId = readString(existingDocument?.latestRevisionId);
8076
+ const targetRevisionId = readString(target.revisionId);
8077
+ if (currentRevisionId && currentRevisionId === targetRevisionId) continue;
8078
+ const err = runtimeActionValidationError(
8079
+ currentRevisionId
8080
+ ? `issue document confirmation target ${key} is stale; current revision is ${currentRevisionId}`
8081
+ : `issue document confirmation target ${key} does not exist`,
8082
+ );
8083
+ err.runtimeActionType = "create_interaction";
8084
+ err.runtimeActionKind = kind;
8085
+ err.runtimeActionFieldPath = `actions[${actionIndex}].payload.target.revisionId`;
8086
+ throw err;
8087
+ }
8031
8088
  return baseRevisionIdsByKey;
8032
8089
  }
8033
8090
 
@@ -8434,10 +8491,23 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
8434
8491
  if (!["request_confirmation", "request_checkbox_confirmation", "ask_user_questions", "suggest_tasks"].includes(kind)) {
8435
8492
  throw new Error(`create_interaction action does not support kind: ${kind}`);
8436
8493
  }
8437
- const payload = normalizeCreateInteractionPayload(fields, kind);
8494
+ let payload = normalizeCreateInteractionPayload(fields, kind);
8438
8495
  if (Object.keys(payload).length === 0) {
8439
8496
  throw new Error("create_interaction action requires payload");
8440
8497
  }
8498
+ const interactionTarget = asRecord(payload.target);
8499
+ if (
8500
+ ["request_confirmation", "request_checkbox_confirmation"].includes(kind) &&
8501
+ readString(interactionTarget.type) === "issue_document" &&
8502
+ !readString(interactionTarget.revisionId)
8503
+ ) {
8504
+ const key = readString(interactionTarget.key);
8505
+ const boundTarget = key ? actionContext.issueDocumentTargetsByKey.get(key) : null;
8506
+ if (!boundTarget) {
8507
+ throw new Error(`issue document confirmation target ${String(key)} was not created earlier in the runtime action batch`);
8508
+ }
8509
+ payload = { ...payload, target: boundTarget };
8510
+ }
8441
8511
  const defaultContinuationPolicy = "wake_assignee";
8442
8512
  const createBody = {
8443
8513
  kind,
@@ -8492,13 +8562,30 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
8492
8562
  ?? null,
8493
8563
  },
8494
8564
  ));
8565
+ const latestRevisionId = readString(document.latestRevisionId);
8566
+ const latestRevisionNumber = readNumber(document.latestRevisionNumber, null) ?? undefined;
8567
+ const documentId = readString(document.id);
8568
+ if (actionContext.issueDocumentConfirmationKeysNeedingBinding.has(key)) {
8569
+ if (!documentId || !latestRevisionId || !latestRevisionNumber) {
8570
+ throw new Error(`upsert_document ${key} response did not expose the created document revision`);
8571
+ }
8572
+ actionContext.issueDocumentTargetsByKey.set(key, {
8573
+ type: "issue_document",
8574
+ issueId,
8575
+ documentId,
8576
+ key,
8577
+ revisionId: latestRevisionId,
8578
+ revisionNumber: latestRevisionNumber,
8579
+ });
8580
+ }
8495
8581
  return {
8496
8582
  type,
8497
8583
  ...(originalType && originalType !== type ? { originalType } : {}),
8498
8584
  issueId,
8499
8585
  key,
8500
- documentId: readString(document.id),
8501
- revisionNumber: typeof document.latestRevisionNumber === "number" ? document.latestRevisionNumber : undefined,
8586
+ documentId,
8587
+ revisionId: latestRevisionId ?? undefined,
8588
+ revisionNumber: latestRevisionNumber,
8502
8589
  };
8503
8590
  }
8504
8591
 
@@ -9694,6 +9781,16 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
9694
9781
  const results = [];
9695
9782
  const skipped = [];
9696
9783
  const actionLedgerEntries = [];
9784
+ const issueDocumentConfirmationKeysNeedingBinding = new Set(actions.flatMap((action) => {
9785
+ const fields = runtimeActionBusinessFields(action);
9786
+ if (readRuntimeActionType(fields) !== "create_interaction") return [];
9787
+ const kind = inferCreateInteractionKind(fields);
9788
+ if (!["request_confirmation", "request_checkbox_confirmation"].includes(kind ?? "")) return [];
9789
+ const target = asRecord(asRecord(fields.payload).target);
9790
+ if (readString(target.type) !== "issue_document" || readString(target.revisionId)) return [];
9791
+ const key = readString(target.key);
9792
+ return key ? [key] : [];
9793
+ }));
9697
9794
  const actionContext = {
9698
9795
  rootIssueId: issueId,
9699
9796
  cwd,
@@ -9706,6 +9803,8 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
9706
9803
  acceptedConfirmationContinuation,
9707
9804
  answeredQuestionContinuation,
9708
9805
  documentBaseRevisionIdsByKey,
9806
+ issueDocumentConfirmationKeysNeedingBinding,
9807
+ issueDocumentTargetsByKey: new Map(),
9709
9808
  finalCommentBodies: [],
9710
9809
  };
9711
9810
  for (const [actionIndex, action] of actions.entries()) {
@@ -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.12";
8
+ const CONNECTOR_VERSION = "0.1.0-beta.14";
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.12",
3
+ "version": "0.1.0-beta.14",
4
4
  "description": "AMaster Employee runtime connector CLI and daemon",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,6 +15,8 @@
15
15
  "scripts": {
16
16
  "build": "node scripts/build.mjs",
17
17
  "clean": "node scripts/clean.mjs",
18
+ "phase0b:live": "node probes/runtime-v2-phase0b/live-probe.mjs",
19
+ "phase0b:test": "node --test probes/runtime-v2-phase0b/*.test.mjs",
18
20
  "typecheck": "node --check scripts/build.mjs && node --check scripts/bundle.mjs && node --check src/amaster-runtime.mjs && node --check src/amaster-runtime-daemon.mjs",
19
21
  "test": "pnpm run build && node --test test/*.test.mjs src/amaster-runtime-daemon/*.test.mjs",
20
22
  "prepack": "pnpm run test"